Skip to content

fix(sdk): support relative artifact download URLs - #9734

Merged
ytahdn merged 1 commit into
QwenLM:mainfrom
ytahdn:codex/fix-web-shell-artifact-download-url
Aug 22, 2026
Merged

fix(sdk): support relative artifact download URLs#9734
ytahdn merged 1 commit into
QwenLM:mainfrom
ytahdn:codex/fix-web-shell-artifact-download-url

Conversation

@ytahdn

@ytahdn ytahdn commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Allows workspace artifact byte downloads to use a relative daemon base URL while preserving the existing behavior for absolute daemon URLs. It also adds regression coverage for both URL forms.

Why it's needed

Web Shell integrations can configure a same-origin relative daemon path such as /daemon. Artifact downloads previously constructed that path with the absolute-only URL constructor, causing Chromium to report Failed to construct 'URL': Invalid URL before the byte request reached the daemon.

Reviewer Test Plan

How to verify

Configure the client with a relative daemon base path and request raw bytes for a workspace artifact. Confirm that the request reaches the default REST transport with the relative path and encoded query parameters. Also confirm that an absolute daemon URL still produces the same absolute request as before.

Evidence (Before & After)

Before: workspace artifact downloads using a relative daemon base path failed before issuing the byte request with Failed to construct 'URL': Invalid URL.

After: relative and absolute daemon base paths both produce valid byte requests, and the SDK unit test file passes all 348 tests.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Node.js 22.14.0; focused SDK unit tests and SDK type checking.

Risk & Scope

  • Main risk or tradeoff: Relative URL handling relies on the browser-compatible default REST transport, matching the Web Shell integration path.
  • Not validated / out of scope: ACP transports with relative base URLs and other workspace file helper methods that still require absolute URLs.
  • Breaking changes / migration notes: None.

Linked Issues

N/A

中文说明

此 PR 的改动

让 workspace artifact 字节下载支持相对 daemon 基础地址,同时保持绝对 daemon 地址的现有行为,并为两种地址形式增加回归测试。

为什么需要

Web Shell 集成可以配置 /daemon 这样的同源相对 daemon 路径。此前 artifact 下载使用只接受绝对地址的 URL 构造器处理该路径,导致 Chromium 在字节请求到达 daemon 前报错:Failed to construct 'URL': Invalid URL

Reviewer 测试计划

验证方式

使用相对 daemon 基础路径配置客户端并读取 workspace artifact 原始字节,确认请求通过默认 REST transport 发出,且相对路径与查询参数编码正确。同时确认绝对 daemon 地址仍生成与此前一致的绝对请求。

修改前后证据

修改前:使用相对 daemon 基础路径下载 workspace artifact 时,在字节请求发出前报错 Failed to construct 'URL': Invalid URL

修改后:相对和绝对 daemon 基础路径均能生成有效的字节请求,SDK 单元测试文件的 348 个测试全部通过。

测试平台

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

Node.js 22.14.0;运行了 SDK 聚焦单元测试与 SDK 类型检查。

风险与范围

  • 主要风险或权衡:相对 URL 处理依赖浏览器兼容的默认 REST transport,与 Web Shell 集成链路一致。
  • 未验证或范围外:使用相对基础地址的 ACP transport,以及仍要求绝对地址的其他 workspace 文件辅助方法。
  • 破坏性变更或迁移说明:无。

关联 Issue

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finished — CI landed green on b44b4dc and the deferred approval was posted. finalize run

Qwen Triage 已完成 —— b44b4dc 的 CI 全绿,延迟审批已提交。查看 finalize 运行

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: this one is real, not theoretical. The top-level readWorkspaceFileBytes built its request with new URL(${baseUrl}/file/bytes), and the Web Shell's default same-origin configuration passes an empty base URL (getDaemonBaseUrl() returns '' when no ?daemon= param is present). new URL() throws on relative input, which is exactly the reported Failed to construct 'URL': Invalid URL — the byte request never leaves the page. Every other endpoint in the client already uses plain string interpolation (which resolves fine against the page origin in the browser), so this method was one of a handful of odd ones out. Verified against the code at the reviewed commit; no linked issue, but the mechanism is mechanically reproducible.

Direction: aligned. Web Shell is under active development (e.g. #9730 landed this week), and same-origin daemon mode is its default configuration — artifact downloads breaking there is a genuine user-facing defect.

Size: not applicable — the change is not in core paths (9 production lines in the SDK daemon client, 24 lines of tests).

Approach: the scope feels right and the shape matches convention — the fix mirrors the workspace-scoped readWorkspaceFileBytes sibling (URLSearchParams + string interpolation), and query-encoding semantics are preserved. One genuine question: the sibling top-level helpers (readWorkspaceFile — used by the same artifact panel for text previews — fileStat, dirList, workspacePathSuggestions, glob, and the upload path) still carry the identical new URL() defect. The PR is upfront about scoping them out, which is fine as a minimal fix, but worth deciding whether a follow-up should sweep the rest.

Risk: no elevated risk signals — no high-risk paths matched.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: 这是真实存在的 bug,不是理论性问题。顶层 readWorkspaceFileBytesnew URL(${baseUrl}/file/bytes) 构造请求,而 Web Shell 默认的同源配置会传入空的基础地址(没有 ?daemon= 参数时 getDaemonBaseUrl() 返回 '')。new URL() 对相对地址会抛错,这正是报告中的 Failed to construct 'URL': Invalid URL——字节请求根本没有发出。客户端里其余所有端点都已经用字符串拼接方式构造请求(在浏览器中会相对页面 origin 正常解析),所以这个方法是少数几个例外之一。已在被审查的提交上核实;虽然没有关联 issue,但该机制可以机械地复现。

方向: 对齐。Web Shell 正在活跃开发(例如本周合入的 #9730),同源 daemon 模式是它的默认配置——在那里 artifact 下载坏掉是真实可见的用户缺陷。

规模: 不适用——改动不在核心路径(SDK daemon 客户端 9 行生产代码,24 行测试)。

方案: 范围合理,形态也符合惯例——修复方式与工作区作用域的 readWorkspaceFileBytes 兄弟方法一致(URLSearchParams + 字符串拼接),查询参数编码语义保持不变。一个真诚的疑问:顶层的兄弟辅助方法(readWorkspaceFile——同一个 artifact 面板用它做文本预览——以及 fileStatdirListworkspacePathSuggestionsglob 和上传路径)仍然带有完全相同的 new URL() 缺陷。PR 已明确说明这些不在本次范围内,作为最小修复这没问题,但值得决定是否用后续 PR 一并清理。

风险: 无升级风险信号——未命中高风险路径。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at b44b4dc7d7d42c8a2392bab846e439c002ac12b8 · re-run with @qwen-code /triage

@ytahdn
ytahdn enabled auto-merge August 22, 2026 15:18
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Code review

Read independently first: given "artifact byte downloads must work with a relative daemon base URL", my proposal would be exactly what this PR does — stop parsing the URL and instead build the query with URLSearchParams and fetch ${baseUrl}/file/bytes?${query}, the same shape the workspace-scoped readWorkspaceFileBytes variant and the other ~100 endpoints in this file already use. The PR matches that one-to-one, adds a regression test, and changes nothing else. No blockers found.

Verified against the reviewed commit:

  • Absolute-URL behavior is byte-for-byte preserved. URLSearchParams serializes identically to URL.searchParams (same form-urlencoded output), and the pre-existing test asserting http://daemon/file/bytes?path=bin.dat&offset=4&maxBytes=2 is untouched and still pins the absolute path. Node-side callers (e.g. the daemon-mcp serve bridge) keep working unchanged.
  • Relative URLs flow through cleanly. fetchWithTimeout and RestSseTransport.fetch pass the URL string to fetch without parsing it, so in the browser /daemon/file/bytes?... resolves against the page origin — matching how the Web Shell dev proxy and same-origin deployments route /file/* to the daemon.
  • Trailing-slash bases are safe — the constructor already normalizes baseUrl via stripTrailingSlashes.
  • The new test asserts the exact relative request URL (/daemon/file/bytes?path=reports%2Freport.pdf&offset=0&maxBytes=2), so the regression is genuinely pinned at the SDK level — remove the fix and the test throws.

Standing non-blocking follow-up (restated from Stage 1): the sibling top-level helpers — readWorkspaceFile (text previews in this same artifact panel), fileStat, dirList, workspacePathSuggestions, glob, and the upload path — still use absolute-only new URL(). The PR scopes them out explicitly; a follow-up sweep would close the rest.

Test evidence (PR's own CI, read via API — PR code never executed in this environment)

The main unit/integration suite (Test (ubuntu-latest, Node 22.x)) is still running at review time; the table below is a snapshot and the finalize job will update it once CI settles. The Security Checks workflow completed green. The macOS/Windows test jobs and CLI integration tests are skipped for this run. No check has failed. The author's PR description reports 348 SDK unit tests passing locally — that is the author's claim, not independently re-run here.

Final CI results for b44b4dc (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Secret scan (TruffleHog) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

Sandboxed verification would settle the remaining gap: @qwen-code /verify — the unit test pins the SDK-level request URL, but that a relative daemon base produces a byte request that actually reaches the daemon end-to-end in a browser same-origin setup (author tested macOS only, via unit tests) is not exercised by the suite; an A/B run against the base build would prove the fix is load-bearing.

中文说明

代码审查

先独立形成方案再对照:给定"让 artifact 字节下载支持相对 daemon 基础地址",我的方案与本 PR 完全一致——不再解析 URL,改用 URLSearchParams 构造查询参数,然后 fetch ${baseUrl}/file/bytes?${query},这正是本文件中工作区作用域 readWorkspaceFileBytes 变体和其余约 100 个端点已经在用的形态。PR 与此一一对应,补了回归测试,没有其他改动。未发现阻塞问题。

已在被审查提交上核实:

  • 绝对地址行为逐字节保持不变。 URLSearchParamsURL.searchParams 序列化结果一致,原有断言 http://daemon/file/bytes?path=bin.dat&offset=4&maxBytes=2 的测试未被改动且仍钉住绝对路径;Node 侧调用方(如 daemon-mcp serve bridge)不受影响。
  • 相对地址可以顺畅传递。 fetchWithTimeoutRestSseTransport.fetch 都不解析 URL 字符串,直接交给 fetch,因此在浏览器中 /daemon/file/bytes?... 会相对页面 origin 解析——与 Web Shell 开发代理及同源部署把 /file/* 路由到 daemon 的方式一致。
  • 带尾斜杠的基础地址安全——构造函数已用 stripTrailingSlashes 归一化 baseUrl
  • 新测试断言了精确的相对请求 URL(/daemon/file/bytes?path=reports%2Freport.pdf&offset=0&maxBytes=2),回归在 SDK 层面确实被钉住——去掉修复该测试即抛错。

既有的非阻塞跟进项(与 Stage 1 相同):顶层兄弟辅助方法——readWorkspaceFile(同一 artifact 面板的文本预览)、fileStatdirListworkspacePathSuggestionsglob 以及上传路径——仍在使用只接受绝对地址的 new URL()。PR 已明确将其排除在范围外;建议后续用一个跟进 PR 一并清理。

测试证据(来自 PR 自身 CI,通过 API 读取——本环境未执行任何 PR 代码)

主单元/集成套件(Test (ubuntu-latest, Node 22.x))在审查时仍在运行;下表为快照,CI 结束后 finalize 任务会更新它。Security Checks 工作流已通过。macOS/Windows 测试任务与 CLI 集成测试本次被跳过。没有失败的检查。作者在 PR 描述中报告本地 348 个 SDK 单元测试全部通过——这是作者的声明,未在此独立复跑。

沙箱验证可以补上剩余缺口:@qwen-code /verify——单元测试钉住了 SDK 层的请求 URL,但"相对 daemon 基础地址在浏览器同源部署下能让字节请求真正到达 daemon"(作者仅在 macOS 上通过单元测试验证)并未被套件覆盖;对 base 构建做一次 A/B 运行可以证明该修复确实关键。

Qwen Code · qwen3.8-max

Reviewed at b44b4dc7d7d42c8a2392bab846e439c002ac12b8 · re-run with @qwen-code /triage

@ytahdn
ytahdn disabled auto-merge August 22, 2026 15:19
@ytahdn
ytahdn enabled auto-merge August 22, 2026 15:19
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal, convention-matching fix for a verified defect; the only non-blocking nit is the sibling helpers that still carry the same defect (author scoped them out deliberately).

Stepping back: this is exactly the kind of PR the gate should wave through quickly. The problem is real and verified in code — the Web Shell's default same-origin mode hands the client an empty base URL, and this one method insisted on parsing it as absolute, killing artifact byte downloads before the request ever left the page. My independent proposal before reading the diff was identical to what landed: adopt the URLSearchParams + interpolation shape the rest of the file already uses. No over-engineering, no drive-by changes, and the new test genuinely pins the behavior (it throws without the fix). Absolute-URL callers get byte-identical requests, so there's no regression surface on the Node side.

Two honest reservations, neither blocking: the sibling top-level helpers (readWorkspaceFile included — that's text previews in this same panel) still throw on relative bases, so the panel isn't fully fixed until a follow-up sweeps them; and the suite pins the SDK request URL but not the browser end-to-end hop, which is what the @qwen-code /verify line in my review comment names.

Verdict: approve. CI is still running (Test (ubuntu-latest, Node 22.x) in flight on the reviewed commit), so approval is deferred until CI lands green on b44b4dc7d7d42c8a2392bab846e439c002ac12b8 — the finalize step will post it pinned to that commit.

中文说明

置信度:4/5 —— 对一个已核实的缺陷给出了干净、最小、符合惯例的修复;唯一非阻塞的小遗憾是兄弟辅助方法仍带有同样的缺陷(作者已有意将其划出范围)。

退一步整体看:这正是审查关卡应该快速放行的 PR。问题真实且已在代码中核实——Web Shell 默认的同源模式会把空的基础地址交给客户端,而这一个方法坚持把它当绝对地址解析,导致 artifact 字节下载在请求发出前就失败。我在读 diff 之前独立形成的方案与最终实现完全一致:采用文件其余部分已在使用的 URLSearchParams + 字符串拼接形态。没有过度设计,没有顺手改动,新测试也真正钉住了行为(去掉修复即抛错)。绝对地址调用方得到逐字节一致的请求,Node 侧没有回归面。

两点坦率的保留意见,均不阻塞:顶层兄弟辅助方法(包括 readWorkspaceFile——也就是同一面板的文本预览)在相对地址下仍会抛错,所以在后续 PR 清理之前该面板并未完全修复;套件钉住的是 SDK 层请求 URL,而不是浏览器端到端的最后一跳,这正是我审查意见中 @qwen-code /verify 那一行指出的。

结论:批准。CI 仍在运行(被审查提交上的 Test (ubuntu-latest, Node 22.x) 尚未完成),因此批准推迟到 CI 在该提交上全绿后执行——finalize 步骤会提交绑定到该提交的批准。

Qwen Code · qwen3.8-max

Reviewed at b44b4dc7d7d42c8a2392bab846e439c002ac12b8 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot 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.

LGTM, looks ready to ship — CI landed green after the review. ✅

@qwen-code-ci-bot qwen-code-ci-bot 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! ✅

中文说明

未发现问题。LGTM!✅

— qwen3.8-max via Qwen Code /review (v0.22.0)

@doudouOUC doudouOUC 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.

⚠️ This run could not certify that any of this diff was reviewed.

Not reviewed: coverage — no plan was given, so this run cannot show that any of the diff was read.

— qwen3.8-max via Qwen Code /review (v0.21.10)

@doudouOUC

Copy link
Copy Markdown
Collaborator

Review: PR #9734 — fix(sdk): support relative artifact download URLs

Verdict: Comment — no Criticals, 1 Suggestion.

Findings

Suggestion — Sibling methods still fail with relative base URLs (packages/sdk-typescript/src/daemon/DaemonClient.ts)

A DaemonClient constructed with a relative baseUrl (e.g., '/daemon') will work for readWorkspaceFileBytes after this fix, but will throw TypeError: Invalid URL when calling readWorkspaceFile, fileStat, dirList, workspacePathSuggestions, or glob — because those 5 sibling methods still use new URL() + searchParams.set().

Suggested fix: Apply the same URLSearchParams + template-literal pattern to the 5 sibling methods in a follow-up PR.

Note: The author explicitly scoped other methods out of this PR ('Not validated / out of scope: ... other workspace file helper methods that still require absolute URLs'). This is a Suggestion for follow-up, not a blocker for this PR.

Coverage

  • 14 review agents examined the full diff (1 chunk, 60 lines, 2 files)
  • No Criticals found
  • 0 inline comments
  • Build: 1 pre-existing failure in audio-capture (Windows Python missing), unrelated to the PR

Reviewed by

14 agents across all dimensions: issue fidelity, correctness, security, reuse, abstraction fit, consistency, performance, test coverage, adversarial audit (attacker, oncall, maintainer), removed-behavior audit, cross-file tracing, and build/test verification.

— qwen3.8-max via Qwen Code /review

@chiga0 chiga0 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 blocking findings.

Checked:

  • Encoding parity: new URLSearchParams({path}) + .toString() produces identical %-encoding to the old url.searchParams.set() path — /%2F, spaces → +. Confirmed against the existing test expectation (path=bin.dat) and the new one (path=reports%2Freport.pdf).
  • Absolute-URL backward compat: the existing test reads raw bytes as base64 payloads (line 563, baseUrl: 'http://daemon') exercises the same code path with an absolute URL and CI passes (Test (ubuntu-latest, Node 22.x): green).
  • New test non-vacuity: with the old code, new URL('/daemon/file/bytes') throws before fetch is called, so the new test would fail with an exception — not a no-op assertion.
  • offset: 0 edge case: 0 !== undefinedquery.set('offset', '0') included; test confirms.
  • Sibling: WorkspaceDaemonClient.readWorkspaceFileBytes (line 6260) already uses URLSearchParams + template literal — pattern is consistent with the broader codebase.
  • Scope of new URL() residue: readWorkspaceFile, fileStat, dirList in DaemonClient still use new URL(). Author explicitly scopes these out in the PR description; no regression introduced.

Ran: CI Test (ubuntu-latest, Node 22.x) — pass; web-shell E2E Smoke (ubuntu-latest, Node 22.x) — pass.

Not covered: macOS/Windows test jobs — SKIPPING (fork-PR pattern, consistent with other PRs in this repo). The changed logic is platform-independent URL string construction; no platform-specific branches introduced.

Reviewed with AI assistance.

@ytahdn
ytahdn added this pull request to the merge queue Aug 22, 2026
Merged via the queue into QwenLM:main with commit 1007bca Aug 22, 2026
99 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.22.2.

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