Skip to content

docs(serve): v0.16-alpha known limits + SDK QWEN_SERVER_TOKEN env fallback (PR 27)#4473

Merged
doudouOUC merged 2 commits into
daemon_mode_b_mainfrom
f5-pr27-alpha-docs-sdk-env-fallback
May 24, 2026
Merged

docs(serve): v0.16-alpha known limits + SDK QWEN_SERVER_TOKEN env fallback (PR 27)#4473
doudouOUC merged 2 commits into
daemon_mode_b_mainfrom
f5-pr27-alpha-docs-sdk-env-fallback

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

Summary

First PR in the F5 release chain (PR 27 → PR 28 → PR 30a → PR 31) per the 2026-05-24 v0.16-alpha scope freeze in #4175. v0.16-alpha targets text-only chat / coding with local-only deployment.

Two changes bundled:

1. SDK DaemonClient env fallback (~50 LOC + 4 tests)

DaemonClient constructor now falls back to QWEN_SERVER_TOKEN when opts.token is absent — closes the asymmetry where the daemon side already honors this env var (--token CLI flag fallback, in main since PR 15) but the SDK forced clients to thread the value through every construction.

This is the entire ergonomic replacement for PR 29's SDK env/file fallback. PR 29's other features (auto-gen daemon token, instance-path keying, stale cleanup) remain deferred to v0.16.x per the scope freeze — all are DX improvements over the boot-time security gate already shipped in PR 15.

Properties:

  • Browser-safe via globalThis.process indirection (the SDK is imported by @qwen-code/webui; literal process.env access would explode at module load on browser bundles)
  • Whitespace stripped (matches daemon-side trim convention — handy for export QWEN_SERVER_TOKEN="$(cat token.txt)" where cat adds a trailing newline)
  • Empty / whitespace-only treated as unset (a stale export QWEN_SERVER_TOKEN="" won't accidentally send Authorization: Bearer with no token)
  • Resolved at construction, not per-request (later process.env mutations don't affect already-built clients)
  • Explicit opts.token wins over env (existing API behavior preserved)

2. v0.16-alpha known limits + scope docs (~120 LOC markdown)

  • docs/users/qwen-serve.md: new top-of-file alpha banner + a comprehensive ## v0.16-alpha known limits section enumerating the deferred surface (text-only ✅ vs multimodal ❌, local launchers ✅ vs containerized ❌, BYO-token ✅ vs auto-gen ❌, etc.). Each entry links to the v0.16.x or v0.17 destination per the scope freeze.
  • docs/developers/examples/daemon-client-quickstart.md: documents the SDK env fallback in both the Hello-daemon intro snippet and the Authentication section. Shows the recommended "export + no-token-arg" pattern for local dev.

Files changed

File Change
packages/sdk-typescript/src/daemon/DaemonClient.ts New private readTokenFromEnv() helper + constructor fallback (~50 LOC)
packages/sdk-typescript/test/unit/DaemonClient.test.ts 4 new tests in bearer auth describe + defensive snapshot on the existing "omits Authorization" test
docs/users/qwen-serve.md Alpha banner + v0.16-alpha known limits section
docs/developers/examples/daemon-client-quickstart.md SDK env fallback documented in 2 places

Test plan

  • npx tsc --noEmit -p packages/sdk-typescript/tsconfig.json — clean
  • npm test --workspace=@qwen-code/sdk -- DaemonClient125/125 pass (121 existing + 4 new)
  • npm test --workspace=@qwen-code/sdk -- daemon-public-surface4/4 pass; constructor signature + exported types unchanged (no breaking change)
  • eslint --max-warnings 0 clean on the 2 touched .ts files
  • Pre-commit prettier ran cleanly across all 4 files

Backward compatibility

  • DaemonClient constructor signature unchanged. opts.token still wins when explicitly passed.
  • Existing tests (attaches Authorization: Bearer when token is set + omits Authorization when no token) keep passing. The latter got a defensive snapshot/restore around it so an inherited test-runner export of QWEN_SERVER_TOKEN can't turn it into a false positive.
  • Browser-safe bundle invariant maintained — globalThis.process access pattern matches existing browser-safe code in the SDK.
  • @qwen-code/sdk still ships at 0.1.7 in this PR; the minor bump to 0.2.0 (with this fallback as one of the motivating additions) lands in PR 28's npm publish scaffolding.

Follow-ups (out of scope)

  • PR 28: bumps @qwen-code/sdk to 0.2.0 + ships the npm publish scaffolding for the v0.16-alpha cut
  • PR 30a: systemd / launchd / nohup launch templates referencing the BYO-token guide added here
  • PR 29 deferred features: re-evaluate when remote / multi-daemon / enterprise pilot becomes the target

🤖 Generated with Qwen Code

…lback (PR 27)

First PR in the F5 release chain (PR 27 → 28 → 30a → 31) per the
2026-05-24 v0.16-alpha scope freeze in #4175 (text-only chat / coding
+ local-only deployment).

## SDK ergonomic micro-change (~50 LOC + 4 tests)

`DaemonClient` constructor falls back to `QWEN_SERVER_TOKEN` env
var when `opts.token` is absent — closes the asymmetry where the
daemon side already honors this var (--token CLI flag fallback,
already in main since PR 15) but the SDK forced clients to thread
it through every construction.

Properties:
- Browser-safe via `globalThis.process` indirection (the SDK is
  imported by @qwen-code/webui; literal process.env access would
  explode at module load on browser bundles)
- Whitespace stripped (matches daemon-side trim — handy for
  `export QWEN_SERVER_TOKEN=\"\$(cat token.txt)\"` where cat adds a
  trailing newline)
- Empty / whitespace-only treated as unset (a stale
  `export QWEN_SERVER_TOKEN=\"\"` won't accidentally send
  Authorization: Bearer with no token)
- Resolved at construction, not lazily per-request (later
  process.env mutations don't affect already-built clients)
- Explicit opts.token wins over env

Tests: 4 new in DaemonClient.test.ts `bearer auth` describe
covering env fallback / explicit-wins / empty-treated-unset /
whitespace-stripped. Plus a defensive snapshot/restore on the
existing 'omits Authorization when no token' test so an
inherited test-runner export of QWEN_SERVER_TOKEN doesn't turn
that assertion into a false positive.

This SDK fallback is the entire ergonomic replacement for PR 29's
SDK env/file fallback. PR 29's other features (auto-gen daemon
token, instance-path keying, stale cleanup) remain deferred to
v0.16.x — all are DX improvements over the boot-time security gate
already shipped in PR 15.

## v0.16-alpha docs (~120 LOC markdown)

- docs/users/qwen-serve.md: new "v0.16-alpha known limits" section
  enumerating product surface (text-only ✅, multimodal ❌),
  deployment surface (local launchers ✅, containerized ❌, multi-
  daemon ❌, BYO-token ✅), and hardening posture (boot security
  gate ✅, mutation gate ✅, MCP guardrails ✅, prompt absolute
  deadline ⏸️, rate limiting ⏸️, --max-body-size ⏸️). Adds an
  alpha banner at the top of the file.

- docs/developers/examples/daemon-client-quickstart.md:
  documents the SDK env fallback in both the Hello-daemon
  intro and the Authentication section, with the "export +
  no-token-arg" recommended path called out for local dev.

Verification: 125/125 DaemonClient.test.ts pass (121 existing +
4 new); 4/4 daemon-public-surface.test.ts pass (constructor
signature unchanged); tsc clean on packages/sdk-typescript;
eslint --max-warnings 0 clean on touched .ts files.

Part of #4175.
Copilot AI review requested due to automatic review settings May 24, 2026 02:05
@github-actions

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR implements two key changes for the v0.16-alpha release: (1) SDK DaemonClient now falls back to QWEN_SERVER_TOKEN environment variable when no explicit token is provided, and (2) comprehensive documentation of v0.16-alpha known limits. The implementation is well-executed with thoughtful defensive measures, thorough test coverage, and excellent documentation.

🔍 General Feedback

  • Strong defensive programming: The readTokenFromEnv() function handles browser safety, whitespace trimming, and empty-value detection with clear rationale
  • Excellent test coverage: 4 new tests cover the core functionality plus a defensive snapshot/restore for existing tests
  • Outstanding documentation: Both user-facing and developer docs are updated with comprehensive details about the feature and alpha limitations
  • Browser-safe design: The globalThis.process indirection shows thoughtful consideration of the SDK's dual runtime environment
  • Symmetry with daemon: Matching the daemon-side --token CLI fallback behavior creates a consistent developer experience

🎯 Specific Feedback

🟢 Medium

  • File: packages/sdk-typescript/src/daemon/DaemonClient.ts:129-172 - The readTokenFromEnv() function's JSDoc comment is quite detailed (40+ lines). Consider extracting some of this rationale into the PR description or a separate design doc reference to keep the code comment more concise. The inline comments are valuable but the full context might be better suited for external documentation.

  • File: packages/sdk-typescript/test/unit/DaemonClient.test.ts:504-518 - The defensive snapshot/restore pattern for QWEN_SERVER_TOKEN in the existing "omits Authorization" test is excellent. However, consider extracting this pattern into a reusable test helper (e.g., withCleanEnvVar(varName, fn)) if similar patterns appear elsewhere in the test suite to avoid duplication.

🔵 Low

  • File: packages/sdk-typescript/src/daemon/DaemonClient.ts:156 - The try/catch around env access is very defensive. Consider adding a brief comment explaining what scenario this protects against (e.g., "Defensive: some restricted environments may throw on property access").

  • File: docs/users/qwen-serve.md:19-44 - The known limits section is comprehensive and valuable. Consider adding a "Last updated" date or version reference since this is explicitly tied to v0.16-alpha, making it easier to track when these limits are addressed in future releases.

  • File: docs/developers/examples/daemon-client-quickstart.md:242-250 - The documentation mentions "Browser bundles (e.g. via @qwen-code/webui) get undefined cleanly" - consider linking to the webui package or adding a brief note about how browser bundling handles the globalThis.process check for developers unfamiliar with this pattern.

✅ Highlights

  • Exceptional documentation quality: The v0.16-alpha known limits section is a model for transparent, actionable documentation. Each deferred feature is clearly marked with ✅/❌ status and linked to follow-up work
  • Thoughtful browser safety: The globalThis.process indirection demonstrates excellent cross-platform consideration for an SDK that runs in both Node.js and browser environments
  • Comprehensive test strategy: Tests cover not just the happy path but also edge cases (whitespace handling, empty values, explicit token precedence) and include defensive measures against test runner environment pollution
  • Clear versioning strategy: The PR description clearly articulates what's in scope for v0.16-alpha vs. deferred to v0.16.x/v0.17, with explicit links to the scope freeze discussion (proposal(serve): Mode B feature-priority roadmap toward v0.16 production-ready #4175)
  • Backward compatibility maintained: Explicit opts.token still wins, constructor signature unchanged, existing tests pass - a textbook example of non-breaking feature addition

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds v0.16-alpha “known limits” documentation for qwen serve and introduces an SDK ergonomics change where DaemonClient can automatically pick up QWEN_SERVER_TOKEN from the environment when opts.token is not provided.

Changes:

  • SDK: DaemonClient constructor falls back to QWEN_SERVER_TOKEN (browser-safe via globalThis.process, trims whitespace, treats empty as unset).
  • Tests: expands DaemonClient bearer-auth coverage with env-fallback cases and hardens the “no token” test against inherited environment state.
  • Docs: adds a v0.16-alpha banner + “known limits” section to qwen serve docs and documents the SDK env fallback in the daemon client quickstart.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/sdk-typescript/src/daemon/DaemonClient.ts Adds readTokenFromEnv() and constructor fallback to QWEN_SERVER_TOKEN.
packages/sdk-typescript/test/unit/DaemonClient.test.ts Adds env-fallback tests and isolates env state for the “no token” assertion.
docs/users/qwen-serve.md Adds v0.16-alpha banner and a “known limits” section for deferred scope.
docs/developers/examples/daemon-client-quickstart.md Documents env fallback usage patterns in the SDK quickstart and auth section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/sdk-typescript/src/daemon/DaemonClient.ts Outdated
Comment thread docs/developers/examples/daemon-client-quickstart.md Outdated

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅ — qwen3.7-max via Qwen Code /review

T1 [copilot DaemonClient.ts:144 — stale line refs in readTokenFromEnv
  JSDoc]: removed `runQwenServe.ts:175` (token resolution actually
  lives at line 302-318 today, would drift again on next refactor)
  and `docs/users/qwen-serve.md:173`. Replaced with stable
  symbol/section references ("runQwenServe token-resolution path";
  "qwen-serve user guide CLI flags section").

T2 [copilot daemon-client-quickstart.md:33 — `~/.qwen/server-token`
  implies built-in path that doesn't exist]: PR 27 explicitly defers
  token auto-generation + file-store fallback (PR 29 deferred features).
  The example incorrectly suggested a standard file location.
  Replaced with two explicit user-managed alternatives:
  - `openssl rand -hex 32` one-shot
  - `cat ./my-token-file` user-managed file

Both threads were accurate suggestions caught at the right time
(zero behavior change; pure docstring/example accuracy).

Verification: 125/125 DaemonClient tests pass; tsc + eslint clean
on touched files.
@doudouOUC doudouOUC requested a review from chiga0 May 24, 2026 11:01

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅ — qwen3.7-max via Qwen Code /review

@doudouOUC doudouOUC merged commit 63803de into daemon_mode_b_main May 24, 2026
6 checks passed
@doudouOUC doudouOUC deleted the f5-pr27-alpha-docs-sdk-env-fallback branch May 24, 2026 15:57
doudouOUC added a commit that referenced this pull request May 27, 2026
Squashed feature work from daemon_mode_b_main branch, rebased onto
latest main to establish proper merge-base and clean PR diff.

Original commits:
- perf(core): F2 cleanup PR A — R9/W11/W12/R10 (post-merge follow-ups) (#4411)
- refactor(acp-bridge): F1 test split — lift bridge.test.ts (6861 LOC) to acp-bridge (#4445)
- fix(core): F2 cleanup PR B — self-heal observability (W133-a + W134) (#4460)
- feat(sdk/daemon-ui): unified completeness follow-up to #4328 (#4353)
- docs(serve): v0.16-alpha known limits + SDK QWEN_SERVER_TOKEN env fallback (PR 27) (#4473)
- docs(deploy): local launch templates for v0.16-alpha (PR 30a) (#4483)
- feat(daemon+sdk): cross-client real-time sync completeness (#4484)
- feat(serve): add POST /session/:id/recap (#4504)
- feat(daemon): add voterClientId to permission_resolved (A4) (#4539)
- feat(serve): --allow-origin <pattern> CORS allowlist (T2.4 #4514) (#4527)
- feat(daemon): in-session model switch reaches the bus (A1) (#4546)
- feat(serve): prompt absolute deadline + SSE writer idle timeout (#4514 T2.9) (#4530)
- Feat/daemon react cli (#4380)
doudouOUC added a commit that referenced this pull request May 27, 2026
…lback (PR 27) (#4473)

* docs(serve): v0.16-alpha known limits + SDK QWEN_SERVER_TOKEN env fallback (PR 27)

First PR in the F5 release chain (PR 27 → 28 → 30a → 31) per the
2026-05-24 v0.16-alpha scope freeze in #4175 (text-only chat / coding
+ local-only deployment).

## SDK ergonomic micro-change (~50 LOC + 4 tests)

`DaemonClient` constructor falls back to `QWEN_SERVER_TOKEN` env
var when `opts.token` is absent — closes the asymmetry where the
daemon side already honors this var (--token CLI flag fallback,
already in main since PR 15) but the SDK forced clients to thread
it through every construction.

Properties:
- Browser-safe via `globalThis.process` indirection (the SDK is
  imported by @qwen-code/webui; literal process.env access would
  explode at module load on browser bundles)
- Whitespace stripped (matches daemon-side trim — handy for
  `export QWEN_SERVER_TOKEN=\"\$(cat token.txt)\"` where cat adds a
  trailing newline)
- Empty / whitespace-only treated as unset (a stale
  `export QWEN_SERVER_TOKEN=\"\"` won't accidentally send
  Authorization: Bearer with no token)
- Resolved at construction, not lazily per-request (later
  process.env mutations don't affect already-built clients)
- Explicit opts.token wins over env

Tests: 4 new in DaemonClient.test.ts `bearer auth` describe
covering env fallback / explicit-wins / empty-treated-unset /
whitespace-stripped. Plus a defensive snapshot/restore on the
existing 'omits Authorization when no token' test so an
inherited test-runner export of QWEN_SERVER_TOKEN doesn't turn
that assertion into a false positive.

This SDK fallback is the entire ergonomic replacement for PR 29's
SDK env/file fallback. PR 29's other features (auto-gen daemon
token, instance-path keying, stale cleanup) remain deferred to
v0.16.x — all are DX improvements over the boot-time security gate
already shipped in PR 15.

## v0.16-alpha docs (~120 LOC markdown)

- docs/users/qwen-serve.md: new "v0.16-alpha known limits" section
  enumerating product surface (text-only ✅, multimodal ❌),
  deployment surface (local launchers ✅, containerized ❌, multi-
  daemon ❌, BYO-token ✅), and hardening posture (boot security
  gate ✅, mutation gate ✅, MCP guardrails ✅, prompt absolute
  deadline ⏸️, rate limiting ⏸️, --max-body-size ⏸️). Adds an
  alpha banner at the top of the file.

- docs/developers/examples/daemon-client-quickstart.md:
  documents the SDK env fallback in both the Hello-daemon
  intro and the Authentication section, with the "export +
  no-token-arg" recommended path called out for local dev.

Verification: 125/125 DaemonClient.test.ts pass (121 existing +
4 new); 4/4 daemon-public-surface.test.ts pass (constructor
signature unchanged); tsc clean on packages/sdk-typescript;
eslint --max-warnings 0 clean on touched .ts files.

Part of #4175.

* fix(sdk): #4473 round 1 fold-in — 2 copilot doc threads adopted

T1 [copilot DaemonClient.ts:144 — stale line refs in readTokenFromEnv
  JSDoc]: removed `runQwenServe.ts:175` (token resolution actually
  lives at line 302-318 today, would drift again on next refactor)
  and `docs/users/qwen-serve.md:173`. Replaced with stable
  symbol/section references ("runQwenServe token-resolution path";
  "qwen-serve user guide CLI flags section").

T2 [copilot daemon-client-quickstart.md:33 — `~/.qwen/server-token`
  implies built-in path that doesn't exist]: PR 27 explicitly defers
  token auto-generation + file-store fallback (PR 29 deferred features).
  The example incorrectly suggested a standard file location.
  Replaced with two explicit user-managed alternatives:
  - `openssl rand -hex 32` one-shot
  - `cat ./my-token-file` user-managed file

Both threads were accurate suggestions caught at the right time
(zero behavior change; pure docstring/example accuracy).

Verification: 125/125 DaemonClient tests pass; tsc + eslint clean
on touched files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants