Commit Graph
5 Commits
Author SHA1 Message Date
ytahdnand钉萁 df5470e430 feat(web-shell): Add workspace file browser and previews (#13878)
* feat(web-shell): Add workspace file browser and previews

* fix(web-shell): Restore source preview CI fixtures and notices

* fix(web-shell): Address file pane review blockers

* fix(web-shell): Resolve review regressions and strengthen file checks

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-13 09:38:47 +00:00
易良andQwen-Coder 7837c6c200 fix(web-shell): verify wildcard export targets against the packed file list (#12364)
* fix(web-shell): verify wildcard export targets against the packed file list

verify-publish-artifacts.mjs statted every `exports` value as a literal
path, so a legal subpath pattern ("./*": "./dist/*") was reported as
`missing ./dist/*` and prepublishOnly refused a publishable package.

Pattern entries now skip the meaningless literal stat and are matched
against the npm pack file list instead: at least one packed file must
match the pattern, so a manifest pointing at nothing shippable still
fails closed. Literal entries keep the existing checks unchanged.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmua2h4ec9e

* fix(web-shell): gate wildcard export branch on the exports key, not the value

Node gives `*` pattern meaning only when the exports key carries it, so
deciding "subpath pattern" from the exports value silently certified a
manifest the base verifier refused: `{ "./foo": "./dist/*.js" }` resolves
to a literal path and every consumer hits ERR_MODULE_NOT_FOUND. Thread the
key through the flatten and gate the wildcard branch on
`key.includes('*') && entry.includes('*')` (two-sided, so a pattern key
with a literal target still gets the relative-import chunk scan), with an
explicit seen-set keyed on key+target text since `[key, value]` pairs
never dedupe by identity.

Tests: pin the regression with a literal-key/wildcard-value case asserting
`missing ./dist/*.js` (red before, green now, red again if the key check
is removed), and make the wildcard fails-closed fixture discriminating by
writing `dist/absent/sdk.js` — on disk but unpacked, so swapping the
packed-list membership check for a filesystem-existence oracle no longer
survives. Both mutants verified killed against this suite.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmua8wlpr9r

* fix(web-shell): match Node star cardinality in wildcard exports gate

R2-1: the wildcard gate tested whether `*` is present on both sides, but
Node honours a pattern key only when it carries exactly one `*`, and
substitutes that single capture into every `*` in the target. The gate now
requires exactly one `*` in the key, and the target regex captures on the
first `*` and back-references it for every later `*`. Witness fixtures
{"./*/*": "./dist/*"}, {"./*": "./dist/*/*"}, {"./*": "./dist/*/*.d.ts"}
and {"./*": "./dist/*/*.js"} all flipped exit 0 -> 1 (base refused all
four), while a packed dist/a/a.js still satisfies {"./*": "./dist/*/*.js"}
(exit 0), so the gate is not a blanket refusal of multi-star targets.

R2-2: the dedup comment now states the invariant the key half protects
(which branch a pair takes depends on the key, so two keys sharing one
target must both be checked), pinned by a fixture advertising one target
through both a pattern key and a literal key.

R2-3: a pattern key with a literal target keeps the literal chunk scan,
pinned by a fixture whose packed entry imports an unpacked nested chunk.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmuafc326a5

* fix(web-shell): require subpath keys and valid targets in the exports gate

The wildcard pattern branch counted the stars on the exports key but not
Node's other conditions for honouring a subpath pattern, and built its
regex from the normalized target:

- a `*`-bearing key that is not `./`-prefixed (`"dist/*"`, `"*"`) took
  the pattern branch and was certified publishable, where Node rejects
  the whole manifest with ERR_INVALID_PACKAGE_CONFIG for every specifier,
  the root import included; the base verifier refused it.
- a target that is not `./`-relative, or that carries a `.`/`..` segment,
  was matched after `join` normalized those segments away, so
  `./dist/../dist/*.js` was certified while Node throws
  ERR_INVALID_PACKAGE_TARGET for every subpath through that key.
- the positional `\1` emitted for every non-first star glued onto a
  following digit: `./dist/*/*1.js` compiled `\11` as the Annex B octal
  escape U+0009, leaving a pattern no packed path can satisfy and
  refusing a manifest Node resolves fine. A named capture cannot absorb
  the digit.

Also pins that the `*` capture may cross a `/`, which nothing in the
suite observed: narrowing it to `([^/]*)` left all twelve existing cases
green while refusing wildcard families whose only packed members are
nested.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmub7ww4cbn

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-22 03:56:13 +00:00
dvd233 733f58b6fc fix(web-shell): isolate npm pack diagnostics (#12367) 2026-09-22 02:42:12 +00:00
易良andQwen-Coder 46f6aebdc3 fix(web-shell): check publish artifacts against the npm pack list (#12343)
The `prepublishOnly` guard tested every export target and relative chunk
with `existsSync` against the working tree, so a file the build emitted but
`files` never published passed. `dist/*.js` does not cross a `/`, which makes
the fail-open case exactly the one the script's own comment warns about: an
entry point at `./dist/nested/index.js`, or a chunk in a subdirectory, is on
disk, is announced to consumers, and is absent from the tarball. A published
version cannot be replaced, so the guard has to answer for what npm ships.

Ask npm: `npm pack --dry-run --json --ignore-scripts` lists the paths that
would actually be packed. Membership is decided against that list, after
reducing both sides to the package-root-relative posix form npm reports
(no leading `./`). The existing `missing` / `which was not built` messages
are kept, and the packed-list problems are added alongside them.

If the pack list cannot be obtained the run reports it as a problem and
refuses the publish rather than skipping the membership checks.

Tests copy the real script into a throwaway package root and drive it the
way `prepublishOnly` does: the two fail-open shapes above now exit 1, an
export target that was never built still exits 1, and a fixture with the
shape the published package actually ships still exits 0.

Fixes #12310


Patrol-Run: qwen-issue-patrol/jmu9nh0028l

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-20 12:04:46 +00:00
易良 9e6d058b41 fix(web-shell): make the published package resolvable for consumers (#12188)
* fix(web-shell): make the published package resolvable for consumers

Three packaging defects were invisible until after `npm publish`, where a
version can no longer be corrected in place:

- Three UI components imported sibling primitives through the repo-only `@/`
  alias, and `tsc` copied that alias verbatim into the emitted declarations, so
  consumers could not resolve the field, alert-dialog and toggle-group types.
  Those imports are now relative and the declaration tree carries no `@/`.
- `fzf`, `@tanstack/react-virtual` and `@xterm/addon-fit` were declared as
  runtime dependencies although the library bundles inline them and no emitted
  declaration mentions them; they move to devDependencies. The packages the
  declarations do reference — `@xterm/xterm`, `@tanstack/react-table` and
  `@modelcontextprotocol/ext-apps` — stay declared.
- A failed type step leaves the shared dist without declarations while the
  exports map still announces them. A `prepublishOnly` hook now refuses to pack
  when an exports target is missing or a declaration imports through `@/`.

* fix(web-shell): keep bundled deps declared for the extension notices

Demoting `fzf`, `@tanstack/react-virtual` and `@xterm/addon-fit` to
`devDependencies` expires the companion's NOTICES.txt freshness check: that
file is generated from the companion's production dependency graph, and the
extension bundles this package's inlined code while shipping the notices in
the VSIX. Both `dist/extension.cjs` and `dist/webview.js` contain that code,
so regenerating would delete 79 lines of MIT notices for software that still
reaches users.

The three packages therefore stay in `dependencies`, and the lockfiles match
the base commit again. `npm run generate:notices` is a no-op at 659
dependencies.

* fix(web-shell): guard shared chunks and drop the last alias sibling import

`dialog.tsx` was the only component still importing a sibling primitive
through the repo's `@/` alias. Its button import is elided from the emitted
declarations today, so nothing leaks yet, but it is the same alias in the same
position and would leak the moment the button reaches a public type. All four
sibling imports are now relative.

The publish guard checked that `exports` targets exist on disk, which says
nothing about the chunks the bundles import by relative path: `dist/index.js`
and `dist/daemon-react-sdk.js` both pull in a hash-named chunk at the `dist`
root, published only because the file list globs `dist/*.js`. A chunk emitted
into a subdirectory would be announced by an entry point but never packed, so
the guard now refuses to pack when an entry's relative import was not built.
2026-09-19 09:57:54 +00:00