diff --git a/.github/workflows/compat.yml b/.github/workflows/compat.yml index d5d275f..d1a1587 100644 --- a/.github/workflows/compat.yml +++ b/.github/workflows/compat.yml @@ -63,6 +63,14 @@ jobs: profile: runs-on: ubuntu-latest timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + # Both ends of the declared band: the oldest line still supported and + # the current one. A range is what the manifest accepts, not what CI + # has exercised, so both are run. + dsh: ['0.1.7-rc.2', '0.2.0-rc.2'] + name: profile (${{ matrix.dsh }}) steps: - uses: actions/checkout@v5 @@ -83,7 +91,7 @@ jobs: pnpm run build pnpm pack --pack-destination "$RUNNER_TEMP" - name: Install dsh CLI - run: npm install -g @deepseek-ai/dsh@0.1.7-rc.2 + run: npm install -g @deepseek-ai/dsh@${{ matrix.dsh }} - name: Install into a scratch profile (builds allowlisted) run: | @@ -104,7 +112,7 @@ jobs: protobufjs: true '@google/genai': true YAML - dsh plugin --profile compat add '@deepseek-ai/dsh-base@0.1.7-rc.2' '@deepseek-ai/dsh-headless@0.1.7-rc.2' "$TARBALL" + dsh plugin --profile compat add "@deepseek-ai/dsh-base@${{ matrix.dsh }}" "@deepseek-ai/dsh-headless@${{ matrix.dsh }}" "$TARBALL" - name: Row-mount assertion run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 011cd63..845597a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.8.0] - 2026-10-01 + +### Added + +- **DeepSeek Harness `0.2` support.** The declared host ranges (`engines.dsh` and the `@deepseek-ai/dsh-*` `peerDependencies` union) gain `|| >=0.2.0-rc.1 <0.3.0-0`, `dshWorkshop.compatibility.dshVersions` records `0.2.0-rc.2`, and the Compat workflow's profile job now runs both ends of the range (`0.1.7-rc.2` and `0.2.0-rc.2`) instead of a single pin. + +### Fixed + +- **The browser half never registered with the client module loader, so the Plugins page reported the plugin as failed to sync.** `lib/client.js` shipped as verbatim `tsc` ESM output — top-level `import`/`export` bindings and no `window.__ModuleLoader__.load({ id, factory })` call. The host's client module system (`@deepseek-ai/dsh-client-modules`) is a lazy CJS table whose only acceptance test is that registration, so the bundle executed, registered nothing, and the page surfaced + + ``` + client-modules: could not load "@perrylink/dsh-github": … : + loaded without registering "@perrylink/dsh-github" via __ModuleLoader__.load + ``` + + The host half was unaffected and kept its tools registered throughout. `scripts/build-client.mjs` now rewrites the compiled bundle in place: `import … from 'm'` becomes `require('m')`, each top-level `export` declaration loses its keyword and gains an `exports. = ` line, and the body is wrapped in the loader's factory. It is wired into both `build` and `prepare`, fails loud on any shape it cannot transform, and is a no-op on an already-wrapped bundle. Original line order is preserved and the shipped `lib/client.js.map` is shifted by the wrapper's line count, so it stays aligned. + + The same acceptance test and the same message are present in `@deepseek-ai/dsh-client-modules@0.1.7-rc.2`, so this is a defect in how the artifact is built rather than a `0.2`-only regression; worth confirming whether the committed bundle ever registered on the older line. + +- **`dsh.client.inject` omitted two modules the browser half requires.** `src/client.ts` imports `react` and `@deepseek-ai/dsh-client-ui-primitives`, and neither was declared, so the module graph did not guarantee they arrive before this row materializes. Both are now listed. + +- **The configuration card mounted in the Plugins page's Official group.** The browser half took the `plugins.item` seat, which the page renders through `renderGroup("official", …)` beside the host's own settings plugins (agent-loop, shell, subagent, web-search) — so this community bundle was presented as an official plugin rather than an installed one. The card now takes this bundle row's own seat, `plugins.row.config`, keyed `#`; both seats hand the card the same `{ view, form }` props, so the card itself is unchanged. `test/client-card-seat.test.ts` pins the seat and derives the key from `cordis.patch.yml` rather than restating it. + +### Changed + +- **The client bundle is now covered by the gates that were missing it.** `scripts/verify-artifacts.mjs` executed only the host face; it now also runs the shipped client bundle against a stub `__ModuleLoader__`, asserts it registers exactly once under the package name, materializes its factory, and rejects top-level ESM. `test/client-bundle-contract.test.ts` asserts the same contract as a unit test — the 0.7.14 lesson ("`lib/client.js` is a build artifact of `src/client.ts`, and nothing in the test suite renders it") applied to the half that had no gate at all. + ## [0.7.15] - 2026-09-25 ### Changed diff --git a/README-es.md b/README-es.md index d733642..2c2e446 100644 --- a/README-es.md +++ b/README-es.md @@ -48,7 +48,7 @@ | Superficie | Estado | |---|---| -| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compatibilidad declarada para `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0`; 0.1.2-rc.1 adaptado el 2026-09-09): el job de revisión pasa a ser propiedad de un `SessionId` desnudo y la fuente de aviso usa el kind propio del plugin `dsh-github` (la unión `Agent \| SessionId` del host y su `kind: 'plugin'` comodín ya no existen); la tarjeta de configuración se registra en la **página Plugins** (grupo Official, slot `plugins.item`) y se renderiza desde el form que entrega `dsh-client-ui-plugin-manager`, en lugar de enlazar el eliminado `ctx.settingsScope`; el controlador de CI autoaprueba mediante la costura oficial `ctx.approval.setPolicy`; el bot de revisión sondea como tarea en segundo plano `ctx.jobs` con respaldo por temporizador. Actualizado el 2026-09-24 a `0.1.7-rc.2` (typecheck + typecheck:ci + 185 pruebas unitarias en verde). | +| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compatibilidad declarada para `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0 \|\| >=0.2.0-rc.1 <0.3.0-0`; la línea `0.2` (`0.2.0-rc.1` / `0.2.0-rc.2`) queda declarada y soportada desde `0.8.0`, donde la mitad de navegador se registra mediante el contrato `__ModuleLoader__` del host en lugar de terminar en enlaces ESM de nivel superior; 0.1.2-rc.1 adaptado el 2026-09-09): el job de revisión pasa a ser propiedad de un `SessionId` desnudo y la fuente de aviso usa el kind propio del plugin `dsh-github` (la unión `Agent \| SessionId` del host y su `kind: 'plugin'` comodín ya no existen); la tarjeta de configuración se registra en la **página Plugins** (grupo Official, slot `plugins.item`) y se renderiza desde el form que entrega `dsh-client-ui-plugin-manager`, en lugar de enlazar el eliminado `ctx.settingsScope`; el controlador de CI autoaprueba mediante la costura oficial `ctx.approval.setPolicy`; el bot de revisión sondea como tarea en segundo plano `ctx.jobs` con respaldo por temporizador. Actualizado el 2026-09-24 a `0.1.7-rc.2` (typecheck + typecheck:ci + 185 pruebas unitarias en verde). | | Node | `^22.19.0 \|\| >=24.0.0` | | Plataformas | Todas (plugin host; red saliente a GitHub) | | Modelo | Cualquiera (la revisión estática es determinista; `reviewMode: "model"` es opcional) | diff --git a/README-hi.md b/README-hi.md index da53d8b..8ab8485 100644 --- a/README-hi.md +++ b/README-hi.md @@ -48,7 +48,7 @@ | सतह | स्थिति | |---|---| -| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (`>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0` के लिए compat घोषित; 0.1.2-rc.1 2026-09-09 को अनुकूलित): review job अब नंगे `SessionId` का है और notice source plugin का अपना kind `dsh-github` है (host का `Agent \| SessionId` union और उसका catch-all `kind: 'plugin'` दोनों हट गए); settings card **Plugins पेज** (Official समूह, `plugins.item` slot) पर पंजीकृत होता है और `dsh-client-ui-plugin-manager` द्वारा दिए गए owner form से render होता है, हटाए गए `ctx.settingsScope` को bind करने की जगह; CI driver आधिकारिक `ctx.approval.setPolicy` नीति सीम से ऑटो-अनुमोदन करता है; review bot `ctx.jobs` पृष्ठभूमि job के रूप में टाइमर फ़ॉलबैक के साथ पोल करता है। 2026-09-24 को `0.1.7-rc.2` पर अपग्रेड (typecheck + typecheck:ci + 185 यूनिट टेस्ट हरे)। | +| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (`>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0 \|\| >=0.2.0-rc.1 <0.3.0-0` के लिए compat घोषित; `0.2` लाइन (`0.2.0-rc.1` / `0.2.0-rc.2`) `0.8.0` से घोषित और समर्थित है, जहाँ browser half top-level ESM bindings छोड़ने के बजाय host के `__ModuleLoader__` contract से रजिस्टर होता है; 0.1.2-rc.1 2026-09-09 को अनुकूलित): review job अब नंगे `SessionId` का है और notice source plugin का अपना kind `dsh-github` है (host का `Agent \| SessionId` union और उसका catch-all `kind: 'plugin'` दोनों हट गए); settings card **Plugins पेज** (Official समूह, `plugins.item` slot) पर पंजीकृत होता है और `dsh-client-ui-plugin-manager` द्वारा दिए गए owner form से render होता है, हटाए गए `ctx.settingsScope` को bind करने की जगह; CI driver आधिकारिक `ctx.approval.setPolicy` नीति सीम से ऑटो-अनुमोदन करता है; review bot `ctx.jobs` पृष्ठभूमि job के रूप में टाइमर फ़ॉलबैक के साथ पोल करता है। 2026-09-24 को `0.1.7-rc.2` पर अपग्रेड (typecheck + typecheck:ci + 185 यूनिट टेस्ट हरे)। | | Node | `^22.19.0 \|\| >=24.0.0` | | Platforms | सभी (host plugin; GitHub की ओर outbound network) | | Model | कोई भी (static review deterministic है; `reviewMode: "model"` वैकल्पिक है) | diff --git a/README-pt.md b/README-pt.md index 1348fc2..e783ab2 100644 --- a/README-pt.md +++ b/README-pt.md @@ -48,7 +48,7 @@ | Superfície | Status | |---|---| -| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compatibilidade declarada para `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0`; 0.1.2-rc.1 adaptado em 2026-09-09): o job de revisão passa a pertencer a um `SessionId` puro e a fonte dos avisos usa o kind próprio do plugin `dsh-github` (a união `Agent \| SessionId` do host e o `kind: 'plugin'` genérico deixaram de existir); o cartão de configurações se registra na **página Plugins** (grupo Official, slot `plugins.item`) e renderiza a partir do form entregue por `dsh-client-ui-plugin-manager`, em vez de vincular o removido `ctx.settingsScope`; o driver de CI autoaprova pela costura oficial `ctx.approval.setPolicy`; o bot de revisão pesquisa como job em segundo plano `ctx.jobs` com fallback por temporizador. Atualizado em 2026-09-24 para `0.1.7-rc.2` (typecheck + typecheck:ci + 185 testes unitários verdes). | +| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compatibilidade declarada para `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0 \|\| >=0.2.0-rc.1 <0.3.0-0`; a linha `0.2` (`0.2.0-rc.1` / `0.2.0-rc.2`) passa a ser declarada e suportada a partir de `0.8.0`, em que a metade de navegador se registra pelo contrato `__ModuleLoader__` do host em vez de terminar em vínculos ESM de nível superior; 0.1.2-rc.1 adaptado em 2026-09-09): o job de revisão passa a pertencer a um `SessionId` puro e a fonte dos avisos usa o kind próprio do plugin `dsh-github` (a união `Agent \| SessionId` do host e o `kind: 'plugin'` genérico deixaram de existir); o cartão de configurações se registra na **página Plugins** (grupo Official, slot `plugins.item`) e renderiza a partir do form entregue por `dsh-client-ui-plugin-manager`, em vez de vincular o removido `ctx.settingsScope`; o driver de CI autoaprova pela costura oficial `ctx.approval.setPolicy`; o bot de revisão pesquisa como job em segundo plano `ctx.jobs` com fallback por temporizador. Atualizado em 2026-09-24 para `0.1.7-rc.2` (typecheck + typecheck:ci + 185 testes unitários verdes). | | Node | `^22.19.0 \|\| >=24.0.0` | | Plataformas | Todas (plugin host; rede de saída para o GitHub) | | Modelo | Qualquer (a revisão estática é determinística; `reviewMode: "model"` é opcional) | diff --git a/README-zh.md b/README-zh.md index 2e4769b..a5e6973 100644 --- a/README-zh.md +++ b/README-zh.md @@ -50,7 +50,7 @@ | 界面 | 状态 | |---|---| -| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2`(兼容声明覆盖 `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0`;0.1.2-rc.1 于 2026-09-09 已适配):评审任务改由裸 `SessionId` 归属、通知来源改为插件自有的 `dsh-github` kind(宿主的 `Agent \| SessionId` 联合与兜底 `kind: 'plugin'` 均已移除);设置卡注册在 **Plugins 页**(Official 组,`plugins.item` 槽),并从 `dsh-client-ui-plugin-manager` 提供的 owner form 渲染,不再绑定已删除的 `ctx.settingsScope`;CI 驱动器通过官方 `ctx.approval.setPolicy` 策略缝自动放行;评审机器人以 `ctx.jobs` 后台任务轮询,并带定时器降级。2026-09-24 升级至 `0.1.7-rc.2`(typecheck + typecheck:ci + 185 项单元测试全绿)。 | +| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2`(兼容声明覆盖 `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0 \|\| >=0.2.0-rc.1 <0.3.0-0`;`0.2` 线(`0.2.0-rc.1` / `0.2.0-rc.2`)自 `0.8.0` 起纳入声明并提供支持——浏览器半边改为通过宿主的 `__ModuleLoader__` 契约注册,不再以顶层 ESM 绑定收尾;0.1.2-rc.1 于 2026-09-09 已适配):评审任务改由裸 `SessionId` 归属、通知来源改为插件自有的 `dsh-github` kind(宿主的 `Agent \| SessionId` 联合与兜底 `kind: 'plugin'` 均已移除);设置卡注册在 **Plugins 页**(Official 组,`plugins.item` 槽),并从 `dsh-client-ui-plugin-manager` 提供的 owner form 渲染,不再绑定已删除的 `ctx.settingsScope`;CI 驱动器通过官方 `ctx.approval.setPolicy` 策略缝自动放行;评审机器人以 `ctx.jobs` 后台任务轮询,并带定时器降级。2026-09-24 升级至 `0.1.7-rc.2`(typecheck + typecheck:ci + 185 项单元测试全绿)。 | | Node | `^22.19.0 \|\| >=24.0.0` | | Platforms | 全部(host 插件;出站网络访问 GitHub) | | Model | 任意(静态审查是确定性的;`reviewMode: "model"` 为可选) | diff --git a/README.md b/README.md index 9bbad3e..8195491 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ | Surface | Status | |---|---| -| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compat declared for `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0`; 0.1.2-rc.1 adapted 2026-09-09): the review job is owned by a bare `SessionId` and a notice source is the plugin-owned kind `dsh-github` (the host's `Agent \| SessionId` union and its catch-all `kind: 'plugin'` are both gone); the settings card registers on the **Plugins page** (Official group, `plugins.item` slot) and renders from the owner form `dsh-client-ui-plugin-manager` supplies, instead of binding the removed `ctx.settingsScope`; the CI driver auto-approves through the official `ctx.approval.setPolicy` policy seam; the review bot polls as a `ctx.jobs` background job with a timer fallback. Upgraded 2026-09-24 to `0.1.7-rc.2` (typecheck + typecheck:ci + 185 unit tests green). | +| Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (compat declared for `>=0.1.2-rc.1 <0.2.0 \|\| >=0.1.5-alpha.1 <0.2.0 \|\| >=0.1.6-0 <0.2.0 \|\| >=0.1.7-0 <0.2.0 \|\| >=0.2.0-rc.1 <0.3.0-0`; the `0.2` line (`0.2.0-rc.1` / `0.2.0-rc.2`) is declared and supported from `0.8.0`, where the browser half registers through the host's `__ModuleLoader__` contract instead of leaving top-level ESM bindings; 0.1.2-rc.1 adapted 2026-09-09): the review job is owned by a bare `SessionId` and a notice source is the plugin-owned kind `dsh-github` (the host's `Agent \| SessionId` union and its catch-all `kind: 'plugin'` are both gone); the settings card registers on the **Plugins page** (Official group, `plugins.item` slot) and renders from the owner form `dsh-client-ui-plugin-manager` supplies, instead of binding the removed `ctx.settingsScope`; the CI driver auto-approves through the official `ctx.approval.setPolicy` policy seam; the review bot polls as a `ctx.jobs` background job with a timer fallback. Upgraded 2026-09-24 to `0.1.7-rc.2` (typecheck + typecheck:ci + 185 unit tests green). | | Node | `^22.19.0 \|\| >=24.0.0` | | Platforms | All (host plugin; outbound network to GitHub) | | Model | Any (static review is deterministic; `reviewMode: "model"` is optional) | diff --git a/lib/client.d.ts b/lib/client.d.ts index e4da811..1e048d7 100644 --- a/lib/client.d.ts +++ b/lib/client.d.ts @@ -1,5 +1,6 @@ /** - * dsh-github browser half — one card in the Plugins page's Official group. + * dsh-github browser half — one card on this plugin's own row in the Plugins + * page's Installed group. * * The card reports and edits the GitHub token. The token literal never rides * the settings document: it is written through the credentials domain, @@ -8,13 +9,20 @@ * per operation, no restart needed. * * Since host 0.1.7-alpha.1 the page owns configuration: the card renders the - * `summary` one-liner and the `page` body from the `plugins.item` owner props - * (`view`, and the `form` the Plugins page supplies), instead of binding a - * `settingsScope` itself — that service, `settings.plugin.item`'s old shape, - * and the `dsh-settings-file` package behind them were all removed. The card - * therefore reads `tokenRef` from `form.state.value` and never writes the - * settings document; the credential is written through the credentials - * domain, which is a different seam and stayed. + * `summary` one-liner and the `page` body from the seat's props (`view`, and + * the `form` the Plugins page supplies for the row's own configuration entry), + * instead of binding a `settingsScope` itself — that service, + * `settings.plugin.item`'s old shape, and the `dsh-settings-file` package + * behind them were all removed. The card therefore reads `tokenRef` from + * `form.state.value` and never writes the settings document; the credential is + * written through the credentials domain, which is a different seam and stayed. + * + * The seat is this bundle's own row (`plugins.row.config`, keyed + * `#`) rather than the page's `plugins.item` list. That list is + * the page's **Official** group, where the host's own settings plugins mount, so + * a community bundle registering there is presented as an official plugin + * instead of an installed one. The row seat carries the same `{ view, form }` + * props, so the card itself is unchanged. * * The shipped `lib/client.js` is the built bundle of this module. The browser * module loader executes that bundle, not this file. @@ -236,11 +244,23 @@ export interface GithubCardProps { export declare function GithubCard(props: GithubCardProps): ReactNode; /** Dictionary namespace owned by this plugin. */ export declare const NS = "dsh-github"; +/** + * Package name the bundle patch installs this plugin under. The row + * configuration seat is keyed `#`, so this and {@link ROW_ID} + * must agree with the package name and row `id` that `cordis.patch.yml` + * declares. + */ +export declare const PACKAGE_NAME = "@perrylink/dsh-github"; +/** Row id the bundle patch declares for this plugin's configuration entry. */ +export declare const ROW_ID = "dsh-github"; +/** The `plugins.row.config` key this bundle's row mounts its card under. */ +export declare const ROW_CONFIG_KEY = "@perrylink/dsh-github#dsh-github"; /** * Required services (cordis fiber inject). `settingsScope` is gone: host * 0.1.7-alpha.1 removed it, and the Plugins page now hands the configuration - * form to the `plugins.item` entry instead. `@deepseek-ai/dsh-client-ui-plugin-manager` - * owns that slot, so it must be composed for the card to mount at all. + * form to the bundle row's `plugins.row.config` seat instead. + * `@deepseek-ai/dsh-client-ui-plugin-manager` owns that slot, so it must be + * composed for the card to mount at all. */ export declare const inject: string[]; export interface ClientContextLike { @@ -260,11 +280,14 @@ export interface ClientContextLike { }; } /** - * Mount the GitHub configuration card into the Plugins page's Official group. + * Mount the GitHub configuration card on this bundle's own row. * - * The form arrives per render, so the controller is rebuilt whenever the page - * supplies a different one — the page's own form owner stays authoritative and - * the card keeps no second copy of the accepted values. + * The card takes the row configuration seat (`plugins.row.config`), so it + * renders inside this plugin's row in the Plugins page's Installed group rather + * than in the page's Official list. The form arrives per render, so the + * controller is rebuilt whenever the page supplies a different one — the page's + * own form owner stays authoritative and the card keeps no second copy of the + * accepted values. * @param ctx - the browser plugin context. */ export declare function apply(ctx: ClientContextLike): void; diff --git a/lib/client.d.ts.map b/lib/client.d.ts.map index 45fa86c..b820eb1 100644 --- a/lib/client.d.ts.map +++ b/lib/client.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/client.js b/lib/client.js index 8a2425e..5af3cfc 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,291 +1,336 @@ -/** - * dsh-github browser half — one card in the Plugins page's Official group. - * - * The card reports and edits the GitHub token. The token literal never rides - * the settings document: it is written through the credentials domain, - * addressed by the reference the plugin's cordis.yml `Config.tokenRef` names - * (default `GITHUB_TOKEN`), which is exactly where the host half resolves it — - * per operation, no restart needed. - * - * Since host 0.1.7-alpha.1 the page owns configuration: the card renders the - * `summary` one-liner and the `page` body from the `plugins.item` owner props - * (`view`, and the `form` the Plugins page supplies), instead of binding a - * `settingsScope` itself — that service, `settings.plugin.item`'s old shape, - * and the `dsh-settings-file` package behind them were all removed. The card - * therefore reads `tokenRef` from `form.state.value` and never writes the - * settings document; the credential is written through the credentials - * domain, which is a different seam and stayed. - * - * The shipped `lib/client.js` is the built bundle of this module. The browser - * module loader executes that bundle, not this file. - * @module @perrylink/dsh-github/client - */ -import { createElement, useState } from 'react'; -import { Button, IconLoadingOutlineRegular, } from '@deepseek-ai/dsh-client-ui-primitives'; -/** - * Create the minimal card store. - * @param init - initial state. - * @returns the store. - */ -export function createSnapshotStore(init) { - let state = init; - const listeners = new Set(); - return { - getSnapshot: () => state, - subscribe: (listener) => { - listeners.add(listener); - return () => { listeners.delete(listener); }; - }, - set: (next) => { - state = next; - for (const listener of [...listeners]) - listener(); - }, - }; -} -/** Namespace of the GitHub capability. Spelled here rather than imported: a client package must not depend on a Host package. */ -export const GITHUB_NS = 'dsh-github'; -/** Credential reference the provider resolves when the configuration names none. */ -export const DEFAULT_TOKEN_REF = 'GITHUB_TOKEN'; -/** English copy. */ -export const en = { - githubTitle: 'GitHub', - githubDescription: 'GitHub pull requests, issues, and CI through the agent.', - tokenLabel: 'GitHub token', - tokenHint: 'Stored in the credentials file, not here. Applied immediately; leave blank to keep the current token.', - tokenSet: 'A token is configured.', - tokenUnset: 'No token is configured; GitHub tools are unavailable until one is.', - unsaved: 'Unsaved', - readOnly: 'This deployment stores settings read-only.', - save: 'Save', - saving: 'Saving…', - discard: 'Discard', - saveFailed: 'The deployment did not accept this value; it was left for you to correct.', -}; -/** Simplified Chinese copy. */ -export const zh = { - githubTitle: 'GitHub', - githubDescription: '通过 agent 操作 GitHub 的 PR、issue 与 CI。', - tokenLabel: 'GitHub Token', - tokenHint: '写入凭证文件而非设置文件;保存后立即生效。留空表示保持现有令牌。', - tokenSet: '已配置令牌。', - tokenUnset: '未配置令牌;配置之前 GitHub 工具不可用。', - unsaved: '未保存', - readOnly: '本部署的设置为只读。', - save: '保存', - saving: '保存中…', - discard: '放弃修改', - saveFailed: '本部署没有接受该值,已保留供你修改。', -}; -/** - * Bridges the page-supplied configuration form and the credentials domain onto - * the card. The token is the one control that does not live in the - * configuration: its literal never rides a response, so the card learns only - * whether one is configured and writes it through the credentials domain, - * addressed by the reference `Config.tokenRef` names. - */ -export class GithubCardController { - form; - api; - store; - saving = false; - failed = false; - credential = { ref: '', configured: false, writable: true }; - /** - * @param form - the configuration form the Plugins page hands this entry, or - * `undefined` when the page supplied none (the card then reads the default - * reference and offers the credential control alone). - * @param api - the credentials Remote namespace. - */ - constructor(form, api) { - this.form = form; - this.api = api; - this.store = createSnapshotStore(this.projection()); - form?.subscribe(() => { - void this.readCredential(); - this.publish(); - }); - void this.readCredential(); - } - /** Whether the Host configuration document accepts writes. */ - writable() { - return this.form?.getSnapshot().writable ?? true; - } - /** The configuration form this controller reads (identity-compared by the caller). */ - formOf() { - return this.form; - } - /** Project the card's full state for its snapshot store. */ - projection() { - return { - writable: this.writable(), - configured: this.credential.configured, - credentialWritable: this.credential.writable, - saving: this.saving, - failed: this.failed, - ref: this.credential.ref, - }; - } - /** The credential reference the configuration names, or the provider default. */ - refOf() { - const declared = this.form?.getSnapshot().value?.tokenRef; - return declared !== undefined && declared.length > 0 ? declared : DEFAULT_TOKEN_REF; - } - /** - * Ask the credentials domain about the reference the section names. A - * response is published only while it still answers for the reference in - * force, so two reads settling out of order cannot clobber each other. - */ - async readCredential() { - const ref = this.refOf(); - if (ref !== this.credential.ref) { - this.credential = { ref, configured: false, writable: true }; - this.publish(); - } - let response; - try { - response = await this.api.describe([ref]); - } - catch { - return; - } - if (!response.ok || ref !== this.refOf()) - return; - const view = response.value[ref]; - const next = { ref, configured: view?.configured ?? false, writable: view?.writable ?? true }; - if (next.configured === this.credential.configured && next.writable === this.credential.writable) - return; - this.credential = next; - this.publish(); - } - /** - * Re-read after the Host reports a change to the reference this card - * watches — a token can be written from elsewhere, and the section does not - * change when it is. - * @param ref - the reference the Host reports as changed. - */ - refreshCredential(ref) { - if (ref !== this.credential.ref) - return; - void this.readCredential(); - } - /** - * Write the staged token, then re-read whether the Host now holds one. A - * blank value writes nothing, which keeps the stored key. - * @param value - the staged credential literal. - * @returns whether the Host reports a configured credential afterwards. - */ - async save(value) { - const text = (value ?? '').trim(); - if (text === '' || this.saving) - return false; - this.saving = true; - this.failed = false; - this.publish(); - try { - await this.api.set(this.refOf(), text); - } - catch { - // handled below by re-reading the configured state - } - await this.readCredential(); - const landed = this.credential.configured; - this.saving = false; - this.failed = !landed; - this.publish(); - return landed; - } - /** Build the face the card's slot registration injects. */ - inject() { - return { - hooks: { githubCard: this.store }, - submit: (value) => this.save(value), - }; - } - publish() { - this.store.set(this.projection()); - } -} -/** - * Render the GitHub card. `summary` renders the one-liner the Plugins page - * places under the card's title (the description plus the token state badge); - * `page` renders the token control and the save/discard row on the plugin's - * own page. The page supplies the configuration form, so the card never - * depends on a served namespace to render. - */ -export function GithubCard(props) { - const { t } = props; - const state = props.useGithubCard((snapshot) => snapshot); - const [draft, setDraft] = useState(''); - if (props.view === 'summary') { - return createElement('span', { className: 'ghc-summary' }, createElement('span', null, t('githubDescription')), createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset'))); - } - const dirty = draft.trim() !== ''; - const saveLabel = state.saving ? t('saving') : t('save'); - return createElement('div', { className: 'ghc-card ghc-cardPage' }, !state.writable - ? createElement('p', { className: 'ghc-readOnly', role: 'status' }, t('readOnly')) - : null, createElement('div', { className: 'ghc-field' }, createElement('div', { className: 'ghc-head' }, createElement('label', { className: 'ghc-label', htmlFor: 'plugin-config-github-token' }, t('tokenLabel')), createElement('span', { className: 'ghc-badges' }, createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset')))), createElement('input', { - id: 'plugin-config-github-token', - className: 'ghc-input', - type: 'password', - autoComplete: 'off', - value: draft, - disabled: !state.credentialWritable, - onChange: (event) => setDraft(event.target.value), - }), createElement('p', { className: 'ghc-hint' }, t('tokenHint'))), createElement('div', { className: 'ghc-actions' }, state.failed ? createElement('p', { className: 'ghc-failed', role: 'status' }, t('saveFailed')) : null, createElement(Button, { - variant: 'ghost', - size: 'sm', - disabled: !dirty || state.saving, - onClick: () => setDraft(''), - }, t('discard')), createElement(Button, { - variant: 'primary', - size: 'sm', - disabled: !state.writable || !dirty || state.saving, - icon: state.saving - ? createElement('span', { className: 'ghc-spin' }, createElement(IconLoadingOutlineRegular, { size: 16 })) - : undefined, - onClick: async () => { - const landed = await props.submit(draft); - if (landed) - setDraft(''); - }, - }, saveLabel))); -} -/** Dictionary namespace owned by this plugin. */ -export const NS = 'dsh-github'; -/** - * Required services (cordis fiber inject). `settingsScope` is gone: host - * 0.1.7-alpha.1 removed it, and the Plugins page now hands the configuration - * form to the `plugins.item` entry instead. `@deepseek-ai/dsh-client-ui-plugin-manager` - * owns that slot, so it must be composed for the card to mount at all. - */ -export const inject = ['slots', 'locale', 'connection', 'remote', 'remote.credentials']; -/** - * Mount the GitHub configuration card into the Plugins page's Official group. - * - * The form arrives per render, so the controller is rebuilt whenever the page - * supplies a different one — the page's own form owner stays authoritative and - * the card keeps no second copy of the accepted values. - * @param ctx - the browser plugin context. - */ -export function apply(ctx) { - const t = ctx.locale.bind(NS); - ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-github: card dictionaries'); - let github; - const controllerFor = (form) => { - if (github === undefined || github.formOf() !== form) { - github = new GithubCardController(form, ctx.remote.credentials); - } - return github; - }; - ctx.effect(() => ctx.remote.$on('credentials/reference-updated', (ref) => github?.refreshCredential(ref)), 'dsh-github: credential invalidations'); - ctx.slots.inject('plugins.item', () => ctx.slots.register({ - name: 'plugins.item', - id: NS, - order: 100, - label: () => t('githubTitle'), - locale: NS, - inject: () => controllerFor(github?.formOf()).inject(), - }, GithubCard)); -} -//# sourceMappingURL=client.js.map \ No newline at end of file +window.__ModuleLoader__.load({ + id: "@perrylink/dsh-github", + factory: (require) => { + var module = { exports: {} }; + var exports = module.exports; + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); + /** + * dsh-github browser half — one card on this plugin's own row in the Plugins + * page's Installed group. + * + * The card reports and edits the GitHub token. The token literal never rides + * the settings document: it is written through the credentials domain, + * addressed by the reference the plugin's cordis.yml `Config.tokenRef` names + * (default `GITHUB_TOKEN`), which is exactly where the host half resolves it — + * per operation, no restart needed. + * + * Since host 0.1.7-alpha.1 the page owns configuration: the card renders the + * `summary` one-liner and the `page` body from the seat's props (`view`, and + * the `form` the Plugins page supplies for the row's own configuration entry), + * instead of binding a `settingsScope` itself — that service, + * `settings.plugin.item`'s old shape, and the `dsh-settings-file` package + * behind them were all removed. The card therefore reads `tokenRef` from + * `form.state.value` and never writes the settings document; the credential is + * written through the credentials domain, which is a different seam and stayed. + * + * The seat is this bundle's own row (`plugins.row.config`, keyed + * `#`) rather than the page's `plugins.item` list. That list is + * the page's **Official** group, where the host's own settings plugins mount, so + * a community bundle registering there is presented as an official plugin + * instead of an installed one. The row seat carries the same `{ view, form }` + * props, so the card itself is unchanged. + * + * The shipped `lib/client.js` is the built bundle of this module. The browser + * module loader executes that bundle, not this file. + * @module @perrylink/dsh-github/client + */ + const { createElement, useState } = require("react"); + const { Button, IconLoadingOutlineRegular } = require("@deepseek-ai/dsh-client-ui-primitives"); + /** + * Create the minimal card store. + * @param init - initial state. + * @returns the store. + */ + function createSnapshotStore(init) { + let state = init; + const listeners = new Set(); + return { + getSnapshot: () => state, + subscribe: (listener) => { + listeners.add(listener); + return () => { listeners.delete(listener); }; + }, + set: (next) => { + state = next; + for (const listener of [...listeners]) + listener(); + }, + }; + } + /** Namespace of the GitHub capability. Spelled here rather than imported: a client package must not depend on a Host package. */ + const GITHUB_NS = 'dsh-github'; + /** Credential reference the provider resolves when the configuration names none. */ + const DEFAULT_TOKEN_REF = 'GITHUB_TOKEN'; + /** English copy. */ + const en = { + githubTitle: 'GitHub', + githubDescription: 'GitHub pull requests, issues, and CI through the agent.', + tokenLabel: 'GitHub token', + tokenHint: 'Stored in the credentials file, not here. Applied immediately; leave blank to keep the current token.', + tokenSet: 'A token is configured.', + tokenUnset: 'No token is configured; GitHub tools are unavailable until one is.', + unsaved: 'Unsaved', + readOnly: 'This deployment stores settings read-only.', + save: 'Save', + saving: 'Saving…', + discard: 'Discard', + saveFailed: 'The deployment did not accept this value; it was left for you to correct.', + }; + /** Simplified Chinese copy. */ + const zh = { + githubTitle: 'GitHub', + githubDescription: '通过 agent 操作 GitHub 的 PR、issue 与 CI。', + tokenLabel: 'GitHub Token', + tokenHint: '写入凭证文件而非设置文件;保存后立即生效。留空表示保持现有令牌。', + tokenSet: '已配置令牌。', + tokenUnset: '未配置令牌;配置之前 GitHub 工具不可用。', + unsaved: '未保存', + readOnly: '本部署的设置为只读。', + save: '保存', + saving: '保存中…', + discard: '放弃修改', + saveFailed: '本部署没有接受该值,已保留供你修改。', + }; + /** + * Bridges the page-supplied configuration form and the credentials domain onto + * the card. The token is the one control that does not live in the + * configuration: its literal never rides a response, so the card learns only + * whether one is configured and writes it through the credentials domain, + * addressed by the reference `Config.tokenRef` names. + */ + class GithubCardController { + form; + api; + store; + saving = false; + failed = false; + credential = { ref: '', configured: false, writable: true }; + /** + * @param form - the configuration form the Plugins page hands this entry, or + * `undefined` when the page supplied none (the card then reads the default + * reference and offers the credential control alone). + * @param api - the credentials Remote namespace. + */ + constructor(form, api) { + this.form = form; + this.api = api; + this.store = createSnapshotStore(this.projection()); + form?.subscribe(() => { + void this.readCredential(); + this.publish(); + }); + void this.readCredential(); + } + /** Whether the Host configuration document accepts writes. */ + writable() { + return this.form?.getSnapshot().writable ?? true; + } + /** The configuration form this controller reads (identity-compared by the caller). */ + formOf() { + return this.form; + } + /** Project the card's full state for its snapshot store. */ + projection() { + return { + writable: this.writable(), + configured: this.credential.configured, + credentialWritable: this.credential.writable, + saving: this.saving, + failed: this.failed, + ref: this.credential.ref, + }; + } + /** The credential reference the configuration names, or the provider default. */ + refOf() { + const declared = this.form?.getSnapshot().value?.tokenRef; + return declared !== undefined && declared.length > 0 ? declared : DEFAULT_TOKEN_REF; + } + /** + * Ask the credentials domain about the reference the section names. A + * response is published only while it still answers for the reference in + * force, so two reads settling out of order cannot clobber each other. + */ + async readCredential() { + const ref = this.refOf(); + if (ref !== this.credential.ref) { + this.credential = { ref, configured: false, writable: true }; + this.publish(); + } + let response; + try { + response = await this.api.describe([ref]); + } + catch { + return; + } + if (!response.ok || ref !== this.refOf()) + return; + const view = response.value[ref]; + const next = { ref, configured: view?.configured ?? false, writable: view?.writable ?? true }; + if (next.configured === this.credential.configured && next.writable === this.credential.writable) + return; + this.credential = next; + this.publish(); + } + /** + * Re-read after the Host reports a change to the reference this card + * watches — a token can be written from elsewhere, and the section does not + * change when it is. + * @param ref - the reference the Host reports as changed. + */ + refreshCredential(ref) { + if (ref !== this.credential.ref) + return; + void this.readCredential(); + } + /** + * Write the staged token, then re-read whether the Host now holds one. A + * blank value writes nothing, which keeps the stored key. + * @param value - the staged credential literal. + * @returns whether the Host reports a configured credential afterwards. + */ + async save(value) { + const text = (value ?? '').trim(); + if (text === '' || this.saving) + return false; + this.saving = true; + this.failed = false; + this.publish(); + try { + await this.api.set(this.refOf(), text); + } + catch { + // handled below by re-reading the configured state + } + await this.readCredential(); + const landed = this.credential.configured; + this.saving = false; + this.failed = !landed; + this.publish(); + return landed; + } + /** Build the face the card's slot registration injects. */ + inject() { + return { + hooks: { githubCard: this.store }, + submit: (value) => this.save(value), + }; + } + publish() { + this.store.set(this.projection()); + } + } + /** + * Render the GitHub card. `summary` renders the one-liner the Plugins page + * places under the card's title (the description plus the token state badge); + * `page` renders the token control and the save/discard row on the plugin's + * own page. The page supplies the configuration form, so the card never + * depends on a served namespace to render. + */ + function GithubCard(props) { + const { t } = props; + const state = props.useGithubCard((snapshot) => snapshot); + const [draft, setDraft] = useState(''); + if (props.view === 'summary') { + return createElement('span', { className: 'ghc-summary' }, createElement('span', null, t('githubDescription')), createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset'))); + } + const dirty = draft.trim() !== ''; + const saveLabel = state.saving ? t('saving') : t('save'); + return createElement('div', { className: 'ghc-card ghc-cardPage' }, !state.writable + ? createElement('p', { className: 'ghc-readOnly', role: 'status' }, t('readOnly')) + : null, createElement('div', { className: 'ghc-field' }, createElement('div', { className: 'ghc-head' }, createElement('label', { className: 'ghc-label', htmlFor: 'plugin-config-github-token' }, t('tokenLabel')), createElement('span', { className: 'ghc-badges' }, createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset')))), createElement('input', { + id: 'plugin-config-github-token', + className: 'ghc-input', + type: 'password', + autoComplete: 'off', + value: draft, + disabled: !state.credentialWritable, + onChange: (event) => setDraft(event.target.value), + }), createElement('p', { className: 'ghc-hint' }, t('tokenHint'))), createElement('div', { className: 'ghc-actions' }, state.failed ? createElement('p', { className: 'ghc-failed', role: 'status' }, t('saveFailed')) : null, createElement(Button, { + variant: 'ghost', + size: 'sm', + disabled: !dirty || state.saving, + onClick: () => setDraft(''), + }, t('discard')), createElement(Button, { + variant: 'primary', + size: 'sm', + disabled: !state.writable || !dirty || state.saving, + icon: state.saving + ? createElement('span', { className: 'ghc-spin' }, createElement(IconLoadingOutlineRegular, { size: 16 })) + : undefined, + onClick: async () => { + const landed = await props.submit(draft); + if (landed) + setDraft(''); + }, + }, saveLabel))); + } + /** Dictionary namespace owned by this plugin. */ + const NS = 'dsh-github'; + /** + * Package name the bundle patch installs this plugin under. The row + * configuration seat is keyed `#`, so this and {@link ROW_ID} + * must agree with the package name and row `id` that `cordis.patch.yml` + * declares. + */ + const PACKAGE_NAME = '@perrylink/dsh-github'; + /** Row id the bundle patch declares for this plugin's configuration entry. */ + const ROW_ID = 'dsh-github'; + /** The `plugins.row.config` key this bundle's row mounts its card under. */ + const ROW_CONFIG_KEY = `${PACKAGE_NAME}#${ROW_ID}`; + /** + * Required services (cordis fiber inject). `settingsScope` is gone: host + * 0.1.7-alpha.1 removed it, and the Plugins page now hands the configuration + * form to the bundle row's `plugins.row.config` seat instead. + * `@deepseek-ai/dsh-client-ui-plugin-manager` owns that slot, so it must be + * composed for the card to mount at all. + */ + const inject = ['slots', 'locale', 'connection', 'remote', 'remote.credentials']; + /** + * Mount the GitHub configuration card on this bundle's own row. + * + * The card takes the row configuration seat (`plugins.row.config`), so it + * renders inside this plugin's row in the Plugins page's Installed group rather + * than in the page's Official list. The form arrives per render, so the + * controller is rebuilt whenever the page supplies a different one — the page's + * own form owner stays authoritative and the card keeps no second copy of the + * accepted values. + * @param ctx - the browser plugin context. + */ + function apply(ctx) { + const t = ctx.locale.bind(NS); + ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-github: card dictionaries'); + let github; + const controllerFor = (form) => { + if (github === undefined || github.formOf() !== form) { + github = new GithubCardController(form, ctx.remote.credentials); + } + return github; + }; + ctx.effect(() => ctx.remote.$on('credentials/reference-updated', (ref) => github?.refreshCredential(ref)), 'dsh-github: credential invalidations'); + ctx.slots.inject('plugins.row.config', () => ctx.slots.register({ + name: 'plugins.row.config', + key: ROW_CONFIG_KEY, + order: 100, + label: () => t('githubTitle'), + locale: NS, + inject: () => controllerFor(github?.formOf()).inject(), + }, GithubCard)); + } + exports.createSnapshotStore = createSnapshotStore; + exports.GITHUB_NS = GITHUB_NS; + exports.DEFAULT_TOKEN_REF = DEFAULT_TOKEN_REF; + exports.en = en; + exports.zh = zh; + exports.GithubCardController = GithubCardController; + exports.GithubCard = GithubCard; + exports.NS = NS; + exports.PACKAGE_NAME = PACKAGE_NAME; + exports.ROW_ID = ROW_ID; + exports.ROW_CONFIG_KEY = ROW_CONFIG_KEY; + exports.inject = inject; + exports.apply = apply; + return module.exports; + } +}); +//# sourceMappingURL=client.js.map diff --git a/lib/client.js.map b/lib/client.js.map index e8bb1fc..4cca12a 100644 --- a/lib/client.js.map +++ b/lib/client.js.map @@ -1 +1 @@ -{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"} diff --git a/package.json b/package.json index 5c50935..b3bc839 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@perrylink/dsh-github", - "version": "0.7.15", + "version": "0.8.0", "description": "Official-grade GitHub CI for DeepSeek Harness: composite action (review PRs / fix CI / report), polling PR review bot with idempotent inline comments, a status-check gate, and approval-gated PR/issue/repo/file tools", "type": "module", "main": "lib/index.js", @@ -29,7 +29,7 @@ "LICENSE" ], "scripts": { - "build": "tsc --noEmitOnError && node scripts/fix-dts.mjs", + "build": "tsc --noEmitOnError && node scripts/fix-dts.mjs && node scripts/build-client.mjs", "prepare": "node scripts/prepare.mjs", "prepublishOnly": "npm run build && npm test", "test": "vitest run", @@ -62,7 +62,7 @@ }, "engines": { "node": "^22.19.0 || >=24.0.0", - "dsh": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0" + "dsh": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0" }, "packageManager": "pnpm@11.7.0", "dsh": { @@ -72,6 +72,8 @@ }, "client": { "inject": [ + "react", + "@deepseek-ai/dsh-client-ui-primitives", "@deepseek-ai/dsh-client-connection", "@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-plugin-manager", @@ -103,7 +105,7 @@ "filesystem:write" ], "compatibility": { - "dshVersions": ["0.1.2-rc.1","0.1.5-rc.2","0.1.7-rc.1", "0.1.7-rc.2"] + "dshVersions": ["0.1.2-rc.1","0.1.5-rc.2","0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.2"] }, "capability": { "id": "gh-ci", @@ -123,19 +125,19 @@ }, "peerDependencies": { "@deepseek-ai/cordis": "^4.0.4", - "@deepseek-ai/dsh-agent": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-client-ui-settings": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-credentials": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-jobs": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-llm": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-session": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-subagent": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-tool-jobs": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-tools": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", - "@deepseek-ai/dsh-user-approval": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0", + "@deepseek-ai/dsh-agent": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-client-ui-settings": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-credentials": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-jobs": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-llm": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-session": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-subagent": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-tool-jobs": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-tools": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", + "@deepseek-ai/dsh-user-approval": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0", "react": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 250ea3e..6b4612c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -514,7 +514,7 @@ importers: .: dependencies: '@deepseek-ai/dsh-client-ui-plugin-manager': - specifier: '>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0' + specifier: '>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0 || >=0.2.0-rc.1 <0.3.0-0' version: 0.1.6-alpha.2(@deepseek-ai/cordis@4.0.4) '@deepseek-ai/dsh-client-ui-primitives': specifier: 0.1.7-rc.2 diff --git a/scripts/build-client.mjs b/scripts/build-client.mjs new file mode 100644 index 0000000..b70ec71 --- /dev/null +++ b/scripts/build-client.mjs @@ -0,0 +1,196 @@ +// Wrap the tsc-compiled ESM client bundle into the DeepSeek Harness browser +// module-loader contract. +// +// The host's client module system (`@deepseek-ai/dsh-client-modules`) is a lazy +// CJS table: executing a client bundle must REGISTER a factory through +// `window.__ModuleLoader__.load({ id, factory })`; every module body side effect +// lives inside that factory and runs at materialization, not at script +// execution. A bundle that ends in top-level `export` bindings registers +// nothing, and the host reports +// +// client-modules: could not load "": : +// loaded without registering "" via __ModuleLoader__.load +// +// `tsc` emits ESM, so this step rewrites the compiled file in place: +// +// import { a, b } from 'm' -> const { a, b } = require('m') +// export function f() {} -> function f() {} (+ exports.f = f) +// export const x = 1 -> const x = 1 (+ exports.x = x) +// export class C {} -> class C {} (+ exports.C = C) +// export { a, b as c } -> (removed) (+ exports.a = a; exports.c = b) +// +// Original line order is preserved: only a fixed prefix is added before the +// module body and a fixed suffix after it, so `lib/client.js.map` stays valid +// after the same line offset is applied to it (an empty leading run of `;` +// segments means "these generated lines carry no mapping"). That keeps the map +// shipped with the wrapped bundle usable while debugging in the browser. +// +// It fails loud on any shape it does not recognize, rather than ship a bundle +// that cannot register. Re-running it on an already-wrapped bundle is a no-op, +// so both `build` and `prepare` can call it. +import { readFileSync, writeFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' + +const root = dirname(dirname(fileURLToPath(import.meta.url))) +const libDir = join(root, 'lib') +const clientPath = join(libDir, 'client.js') +const mapPath = join(libDir, 'client.js.map') +const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) +const pkgName = manifest.name + +const MARKER = 'window.__ModuleLoader__.load(' +const source = readFileSync(clientPath, 'utf8') + +if (source.includes(MARKER)) { + console.log('build-client: lib/client.js is already wrapped; nothing to do') + process.exit(0) +} + +const SOURCE_MAP_COMMENT = /^\/\/# sourceMappingURL=(\S+)\s*$/ + +// Top-level statements only: tsc emits them at column 0 and nested code is +// always indented, so a column-0 anchor cannot reach into a function body. +const isTopLevel = (line) => !/^[ \t]/.test(line) + +const exported = [] // [ exportName, localName ] +const body = [] +let sourceMapTarget + +const fail = (line, why) => { + throw new Error( + `build-client: unsupported top-level statement (${why}); ` + + `teach scripts/build-client.mjs this shape before shipping:\n ${line}`, + ) +} + +for (const line of source.split('\n')) { + if (isTopLevel(line) && line.startsWith('import ')) { + const named = line.match(/^import\s*\{([^}]*)\}\s*from\s*(['"])([^'"]+)\2\s*;?$/) + const bare = line.match(/^import\s+(['"])([^'"]+)\1\s*;?$/) + const star = line.match(/^import\s*\*\s*as\s+([A-Za-z_$][\w$]*)\s*from\s*(['"])([^'"]+)\2\s*;?$/) + const def = line.match(/^import\s+([A-Za-z_$][\w$]*)\s*from\s*(['"])([^'"]+)\2\s*;?$/) + + if (bare) { + body.push(`require(${JSON.stringify(bare[2])});`) + } else if (named) { + const spec = named[1] + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + for (const binding of spec) { + if (!/^[A-Za-z_$][\w$]*$/.test(binding)) fail(line, 'aliased or namespaced named import') + } + body.push(`const { ${spec.join(', ')} } = require(${JSON.stringify(named[3])});`) + } else if (star) { + body.push(`const ${star[1]} = require(${JSON.stringify(star[3])});`) + } else if (def) { + body.push(`const ${def[1]} = require(${JSON.stringify(def[3])}).default;`) + } else { + fail(line, 'unrecognized import form') + } + continue + } + + if (isTopLevel(line) && line.startsWith('export ')) { + if (/^export\s+default\b/.test(line)) fail(line, 'default export') + if (/^export\s*\*/.test(line)) fail(line, 'star re-export') + + const list = line.match(/^export\s*\{([^}]*)\}\s*(?:from\s*(['"])([^'"]+)\2)?\s*;?$/) + if (list) { + if (list[2] !== undefined) fail(line, 're-export from another module') + for (const entry of list[1].split(',').map((s) => s.trim()).filter(Boolean)) { + const parts = entry.split(/\s+as\s+/).map((s) => s.trim()) + const exportedName = parts.length === 2 ? parts[1] : parts[0] + if (!/^[A-Za-z_$][\w$]*$/.test(exportedName)) fail(line, 'unrecognized export alias') + exported.push([exportedName, parts[0]]) + } + continue // the declaration itself stays in the body untouched + } + + const decl = line.match( + /^export\s+(?:async\s+)?(function\*?|class|const|let|var)\s+([A-Za-z_$][\w$]*)/, + ) + if (!decl) fail(line, 'unrecognized export form') + if (decl[1] === 'const' || decl[1] === 'let' || decl[1] === 'var') { + // Only a simple `name = …` binding is supported; destructuring would need + // per-binding names this script cannot infer from a single line. + if (!/^export\s+(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=/.test(line)) { + fail(line, 'destructuring or uninitialized export binding') + } + } + exported.push([decl[2], decl[2]]) + body.push(line.slice('export '.length)) + continue + } + + // The trailing source map comment is re-emitted after the wrapper so the map + // reference stays the last line of the shipped file. + const mapComment = line.match(SOURCE_MAP_COMMENT) + if (mapComment && isTopLevel(line)) { + sourceMapTarget = mapComment[1] + continue + } + + body.push(line) +} + +const indent = (line) => (line.length === 0 ? '' : `\t${line}`) + +const prefix = [ + 'window.__ModuleLoader__.load({', + `\tid: ${JSON.stringify(pkgName)},`, + '\tfactory: (require) => {', + '\t\tvar module = { exports: {} };', + '\t\tvar exports = module.exports;', + '\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: "Module" });', +] +const suffix = [ + ...exported.map(([name, local]) => `\t\texports.${name} = ${local};`), + '\t\treturn module.exports;', + '\t}', + '});', +] + +const wrapped = [ + ...prefix, + ...body.map(indent), + ...suffix, + ...(sourceMapTarget === undefined ? [] : [`//# sourceMappingURL=${sourceMapTarget}`]), + '', +].join('\n') + +// Guard the contract itself: the host accepts a bundle only when it registers +// under the package name, so a regression here must fail the build, not the +// browser. +const leftover = wrapped + .split('\n') + .filter((line) => isTopLevel(line) && /^(?:import|export)\s/.test(line)) +if (leftover.length > 0) { + throw new Error(`build-client: ESM syntax survived the wrap:\n${leftover.join('\n')}`) +} +if (!wrapped.includes(MARKER) || !wrapped.includes(`id: ${JSON.stringify(pkgName)}`)) { + throw new Error('build-client: wrapped bundle does not register under the package name') +} + +writeFileSync(clientPath, wrapped) + +// Shift the source map by the wrapper prefix so mappings stay aligned. A run of +// empty generated segments is exactly "these lines have no mapping", which is +// true for the wrapper lines themselves. +let mapShifted = false +try { + const map = JSON.parse(readFileSync(mapPath, 'utf8')) + if (typeof map.mappings === 'string') { + map.mappings = `${';'.repeat(prefix.length)}${map.mappings}` + writeFileSync(mapPath, `${JSON.stringify(map)}\n`) + mapShifted = true + } +} catch { + // A missing source map is not fatal: the bundle contract does not need it. +} + +console.log( + `build-client: wrapped lib/client.js for __ModuleLoader__ ` + + `(${exported.length} exports, id ${pkgName}${mapShifted ? ', source map shifted' : ''})`, +) diff --git a/scripts/prepare.mjs b/scripts/prepare.mjs index 7494bb2..11e943e 100644 --- a/scripts/prepare.mjs +++ b/scripts/prepare.mjs @@ -14,6 +14,19 @@ const root = dirname(dirname(fileURLToPath(import.meta.url))) const lib = join(root, 'lib') const tsc = join(root, 'node_modules', 'typescript', 'bin', 'tsc') +// The client bundle must satisfy the browser module-loader contract, and `tsc` +// alone emits ESM that cannot register. Runs after either branch below; it is a +// no-op when the committed artifact is already wrapped. +const wrapClient = () => { + const wrap = spawnSync(process.execPath, [join(root, 'scripts', 'build-client.mjs')], { + cwd: root, + stdio: 'inherit', + }) + if (wrap.status !== 0) { + process.exit(typeof wrap.status === 'number' ? wrap.status : 1) + } +} + if (existsSync(tsc)) { const result = spawnSync(process.execPath, [tsc, '-p', 'tsconfig.json', '--noEmitOnError'], { cwd: root, @@ -31,11 +44,14 @@ if (existsSync(tsc)) { if (fix.status !== 0) { process.exit(typeof fix.status === 'number' ? fix.status : 1) } + wrapClient() process.exit(0) } if (existsSync(join(lib, 'index.js'))) { - // Committed build artifacts: usable without a compiler. + // Committed build artifacts: usable without a compiler. The wrap step still + // runs so a stale committed client bundle is repaired rather than shipped. + wrapClient() process.exit(0) } diff --git a/scripts/verify-artifacts.mjs b/scripts/verify-artifacts.mjs index 93face6..f44ff9e 100644 --- a/scripts/verify-artifacts.mjs +++ b/scripts/verify-artifacts.mjs @@ -5,8 +5,9 @@ // syntax leaking into shipped output and against a tarball missing the // bundle patch or the Action contract. import { execFileSync } from 'node:child_process' -import { existsSync } from 'node:fs' +import { existsSync, readFileSync } from 'node:fs' import path from 'node:path' +import { createContext, runInContext } from 'node:vm' import { fileURLToPath, pathToFileURL } from 'node:url' const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))) @@ -14,6 +15,7 @@ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))) const required = [ 'lib/index.js', 'lib/index.d.ts', + 'lib/client.js', 'action.yml', 'cordis.patch.yml', ] @@ -22,6 +24,7 @@ for (const rel of required) { } execFileSync(process.execPath, ['--check', path.join(root, 'lib/index.js')], { stdio: 'inherit' }) +execFileSync(process.execPath, ['--check', path.join(root, 'lib/client.js')], { stdio: 'inherit' }) const index = await import(pathToFileURL(path.join(root, 'lib/index.js')).href) if ('default' in index) throw new Error('lib/index.js must not carry a default export') @@ -29,4 +32,37 @@ if (index.name !== 'dsh-github' || typeof index.apply !== 'function') { throw new Error('lib/index.js exports an unexpected plugin face') } -console.log('artifacts OK: syntax + ESM import + bundle patch and action contract present') +// The browser half must satisfy the host's lazy CJS module-loader contract: +// executing the bundle only REGISTERS a factory via window.__ModuleLoader__.load. +// A bundle that keeps top-level ESM bindings registers nothing and the browser +// reports `loaded without registering "" via __ModuleLoader__.load`, so the +// shipped client is executed here the way the loader executes it. +const manifest = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8')) +const client = readFileSync(path.join(root, 'lib/client.js'), 'utf8') +for (const line of client.split('\n')) { + if (/^(?:import|export)\s/.test(line)) { + throw new Error(`lib/client.js carries top-level ESM that cannot register: ${line}`) + } +} + +const registrations = [] +runInContext(client, createContext({ + window: { __ModuleLoader__: { load: (entry) => registrations.push(entry) } }, +})) +if (registrations.length !== 1) { + throw new Error(`lib/client.js registered ${registrations.length} factories; expected exactly the package row`) +} +const [entry] = registrations +if (entry.id !== manifest.name) { + throw new Error(`lib/client.js registered "${entry.id}" but the package is "${manifest.name}"`) +} +const clientFace = entry.factory((specifier) => { + if (specifier === 'react') return { createElement: () => null, useState: (init) => [init, () => {}] } + if (specifier === '@deepseek-ai/dsh-client-ui-primitives') return { Button: () => null, IconLoadingOutlineRegular: () => null } + throw new Error(`lib/client.js requires undeclared module "${specifier}"`) +}) +if (typeof clientFace.apply !== 'function' || !Array.isArray(clientFace.inject)) { + throw new Error('lib/client.js exports an unexpected plugin face') +} + +console.log('artifacts OK: syntax + ESM import + module-loader registration + bundle patch and action contract present') diff --git a/src/client.ts b/src/client.ts index 32ae7b3..c54709b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,6 @@ /** - * dsh-github browser half — one card in the Plugins page's Official group. + * dsh-github browser half — one card on this plugin's own row in the Plugins + * page's Installed group. * * The card reports and edits the GitHub token. The token literal never rides * the settings document: it is written through the credentials domain, @@ -8,13 +9,20 @@ * per operation, no restart needed. * * Since host 0.1.7-alpha.1 the page owns configuration: the card renders the - * `summary` one-liner and the `page` body from the `plugins.item` owner props - * (`view`, and the `form` the Plugins page supplies), instead of binding a - * `settingsScope` itself — that service, `settings.plugin.item`'s old shape, - * and the `dsh-settings-file` package behind them were all removed. The card - * therefore reads `tokenRef` from `form.state.value` and never writes the - * settings document; the credential is written through the credentials - * domain, which is a different seam and stayed. + * `summary` one-liner and the `page` body from the seat's props (`view`, and + * the `form` the Plugins page supplies for the row's own configuration entry), + * instead of binding a `settingsScope` itself — that service, + * `settings.plugin.item`'s old shape, and the `dsh-settings-file` package + * behind them were all removed. The card therefore reads `tokenRef` from + * `form.state.value` and never writes the settings document; the credential is + * written through the credentials domain, which is a different seam and stayed. + * + * The seat is this bundle's own row (`plugins.row.config`, keyed + * `#`) rather than the page's `plugins.item` list. That list is + * the page's **Official** group, where the host's own settings plugins mount, so + * a community bundle registering there is presented as an official plugin + * instead of an installed one. The row seat carries the same `{ view, form }` + * props, so the card itself is unchanged. * * The shipped `lib/client.js` is the built bundle of this module. The browser * module loader executes that bundle, not this file. @@ -449,11 +457,23 @@ export function GithubCard(props: GithubCardProps): ReactNode { /** Dictionary namespace owned by this plugin. */ export const NS = 'dsh-github' +/** + * Package name the bundle patch installs this plugin under. The row + * configuration seat is keyed `#`, so this and {@link ROW_ID} + * must agree with the package name and row `id` that `cordis.patch.yml` + * declares. + */ +export const PACKAGE_NAME = '@perrylink/dsh-github' +/** Row id the bundle patch declares for this plugin's configuration entry. */ +export const ROW_ID = 'dsh-github' +/** The `plugins.row.config` key this bundle's row mounts its card under. */ +export const ROW_CONFIG_KEY = `${PACKAGE_NAME}#${ROW_ID}` /** * Required services (cordis fiber inject). `settingsScope` is gone: host * 0.1.7-alpha.1 removed it, and the Plugins page now hands the configuration - * form to the `plugins.item` entry instead. `@deepseek-ai/dsh-client-ui-plugin-manager` - * owns that slot, so it must be composed for the card to mount at all. + * form to the bundle row's `plugins.row.config` seat instead. + * `@deepseek-ai/dsh-client-ui-plugin-manager` owns that slot, so it must be + * composed for the card to mount at all. */ export const inject = ['slots', 'locale', 'connection', 'remote', 'remote.credentials'] @@ -474,18 +494,21 @@ export interface ClientContextLike { } } -/** The `form` member the Plugins page hands a `plugins.item` entry. */ +/** The `form` member the Plugins page hands a `plugins.row.config` entry. */ interface PluginConfigViewProps { readonly view: 'summary' | 'page' readonly form?: ConfigForm | undefined } /** - * Mount the GitHub configuration card into the Plugins page's Official group. + * Mount the GitHub configuration card on this bundle's own row. * - * The form arrives per render, so the controller is rebuilt whenever the page - * supplies a different one — the page's own form owner stays authoritative and - * the card keeps no second copy of the accepted values. + * The card takes the row configuration seat (`plugins.row.config`), so it + * renders inside this plugin's row in the Plugins page's Installed group rather + * than in the page's Official list. The form arrives per render, so the + * controller is rebuilt whenever the page supplies a different one — the page's + * own form owner stays authoritative and the card keeps no second copy of the + * accepted values. * @param ctx - the browser plugin context. */ export function apply(ctx: ClientContextLike): void { @@ -502,10 +525,10 @@ export function apply(ctx: ClientContextLike): void { () => ctx.remote.$on('credentials/reference-updated', (ref) => github?.refreshCredential(ref)), 'dsh-github: credential invalidations', ) - ctx.slots.inject('plugins.item', () => ctx.slots.register( + ctx.slots.inject('plugins.row.config', () => ctx.slots.register( { - name: 'plugins.item', - id: NS, + name: 'plugins.row.config', + key: ROW_CONFIG_KEY, order: 100, label: () => t('githubTitle'), locale: NS, diff --git a/test/client-bundle-contract.test.ts b/test/client-bundle-contract.test.ts new file mode 100644 index 0000000..ab672f6 --- /dev/null +++ b/test/client-bundle-contract.test.ts @@ -0,0 +1,105 @@ +/** + * Client bundle module-loader contract (pure static + one sandboxed run). + * + * The host's browser module system (`@deepseek-ai/dsh-client-modules`) is a lazy + * CJS table: executing a client bundle must only REGISTER a factory through + * `window.__ModuleLoader__.load({ id, factory })`, and every module body side + * effect lives inside that factory, which the loader materializes with a + * `require` of its own. A bundle that keeps its top-level `export` bindings + * registers nothing and the browser reports + * + * client-modules: could not load "": : + * loaded without registering "" via __ModuleLoader__.load + * + * `tsc` emits ESM, so `scripts/build-client.mjs` rewrites the compiled file. + * This test executes the SHIPPED artifact the way the loader does — run the + * bundle against a stub `__ModuleLoader__`, then materialize the captured + * factory with stub requires — so a regression in that rewrite fails here + * instead of in a browser. + * @module dsh-github/test/client-bundle-contract + */ + +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { createContext, runInContext } from 'node:vm' +import { describe, expect, it } from 'vitest' + +const root = fileURLToPath(new URL('..', import.meta.url)) +const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { + name: string + dsh: { client: { inject?: string[] } } +} +const bundle = readFileSync(join(root, 'lib', 'client.js'), 'utf8') + +interface Registered { + id: string + factory: (require: (specifier: string) => unknown) => Record +} + +/** Run the bundle exactly as the browser loader does and return what it registered. */ +function loadBundle(): Registered { + const registrations: Registered[] = [] + const context = createContext({ + window: { + __ModuleLoader__: { + load: (entry: Registered) => { + registrations.push(entry) + }, + }, + }, + }) + runInContext(bundle, context) + expect(registrations, 'the bundle registered no factory').toHaveLength(1) + return registrations[0] +} + +/** The bundle's own two dependencies; anything else is an undeclared import. */ +function requireStub(specifier: string): unknown { + switch (specifier) { + case 'react': + return { createElement: () => null, useState: (initial: unknown) => [initial, () => {}] } + case '@deepseek-ai/dsh-client-ui-primitives': + return { Button: () => null, IconLoadingOutlineRegular: () => null } + default: + throw new Error( + `bundle required "${specifier}", which this test does not stub — ` + + 'declare it in dsh.client.inject and add it here', + ) + } +} + +describe('lib/client.js module-loader contract', () => { + it('carries no top-level ESM syntax', () => { + const offending = bundle + .split('\n') + .filter((line) => /^(?:import|export)\s/.test(line)) + expect(offending, 'top-level ESM survived the wrap').toEqual([]) + }) + + it('registers a factory under the package name', () => { + const entry = loadBundle() + expect(entry.id).toBe(manifest.name) + expect(typeof entry.factory).toBe('function') + }) + + it('materializes the plugin face the loader consumes', () => { + const exports = loadBundle().factory(requireStub) + expect(typeof exports.apply).toBe('function') + expect(exports.inject).toEqual(['slots', 'locale', 'connection', 'remote', 'remote.credentials']) + expect(exports.NS).toBe('dsh-github') + }) + + it('declares every module the bundle requires in dsh.client.inject', () => { + const inject = manifest.dsh.client.inject ?? [] + const required = [...bundle.matchAll(/require\((['"])([^'"]+)\1\)/g)].map((match) => match[2]) + expect(required.length, 'the bundle requires nothing — did the wrap drop its imports?').toBeGreaterThan(0) + for (const specifier of required) { + // A subpath such as `react/jsx-runtime` is answered by its package row. + const packageRow = specifier.startsWith('@') + ? specifier.split('/').slice(0, 2).join('/') + : specifier.split('/')[0] + expect(inject, `"${specifier}" is not declared in dsh.client.inject`).toContain(packageRow) + } + }) +}) diff --git a/test/client-card-seat.test.ts b/test/client-card-seat.test.ts new file mode 100644 index 0000000..7d5cdc4 --- /dev/null +++ b/test/client-card-seat.test.ts @@ -0,0 +1,108 @@ +/** + * Where the browser half mounts its card (pure static + one sandboxed run). + * + * The Plugins page declares three configuration seats and groups them apart: + * `plugins.item` is rendered by `renderGroup("official", …)` — the Official + * group, where the host's own settings plugins (agent-loop, shell, subagent, + * web-search) mount their cards — while `plugins.row.config` renders inside one + * bundle row's own detail view, in the Installed group. A community bundle that + * takes the `plugins.item` seat is therefore presented as an official plugin + * instead of an installed one. + * + * Both seats hand the card the same `{ view, form }` props, so the deployment + * that keeps the official list clean is a change of seat, not of card. This test + * pins that: the bundle must register `plugins.row.config` under this package's + * own row key, and must not touch `plugins.item`. + * @module dsh-github/test/client-card-seat + */ + +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { createContext, runInContext } from 'node:vm' +import { describe, expect, it } from 'vitest' + +const root = fileURLToPath(new URL('..', import.meta.url)) +const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { name: string } +const patch = readFileSync(join(root, 'cordis.patch.yml'), 'utf8') +const bundle = readFileSync(join(root, 'lib', 'client.js'), 'utf8') + +/** The row id the bundle patch declares, read from the patch rather than restated. */ +function declaredRowIds(): string[] { + return [...patch.matchAll(/^\s*-\s*id:\s*(\S+)\s*$/gm)].map((match) => match[1]) +} + +interface Registration { + options: Record +} + +/** Materialize the bundle and run its `apply` against a recording stub context. */ +function applyCard(): { injected: string[]; registrations: Registration[] } { + const registrations: Registration[] = [] + const injected: string[] = [] + const registered: Array<{ + id: string + factory: (require: (specifier: string) => unknown) => Record + }> = [] + const context = createContext({ + window: { + __ModuleLoader__: { + load: (entry: (typeof registered)[number]) => { + registered.push(entry) + }, + }, + }, + }) + runInContext(bundle, context) + const face = registered[0].factory((specifier: string) => { + if (specifier === 'react') return { createElement: () => null, useState: (init: unknown) => [init, () => {}] } + if (specifier === '@deepseek-ai/dsh-client-ui-primitives') { + return { Button: () => null, IconLoadingOutlineRegular: () => null } + } + throw new Error(`undeclared require: ${specifier}`) + }) + + const ctx = { + get: () => undefined, + effect: () => {}, + locale: { bind: () => (key: string) => key, register: () => {} }, + remote: { + $on: () => ({}), + credentials: { + describe: async () => ({ ok: true, value: {} }), + set: async () => ({ ok: true }), + }, + }, + slots: { + inject: (slot: string, factory: () => unknown) => { + injected.push(slot) + factory() + }, + register: (options: Record) => { + registrations.push({ options }) + }, + }, + } + ;(face.apply as (context: unknown) => void)(ctx) + return { injected, registrations } +} + +describe('client card seat', () => { + it('mounts on the bundle row, not in the official plugins list', () => { + const { injected } = applyCard() + expect(injected).toContain('plugins.row.config') + expect(injected, 'the official list is for the host’s own settings plugins').not.toContain('plugins.item') + }) + + it('keys the row seat to the package and the row the patch declares', () => { + const { registrations } = applyCard() + expect(registrations).toHaveLength(1) + const [registration] = registrations + expect(registration.options.name).toBe('plugins.row.config') + expect(registration.options.key).toBe(`${manifest.name}#${declaredRowIds()[0]}`) + }) + + it('declares a single constant row so the key stays derivable', () => { + expect(declaredRowIds()).toEqual(['dsh-github']) + }) +})