dsh-advisor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +7 -0
  3. package/README.md +303 -0
  4. package/README.zh.md +166 -0
  5. package/cordis.patch.yml +6 -0
  6. package/lib/advisor-runtime.d.ts +242 -0
  7. package/lib/advisor-runtime.js +662 -0
  8. package/lib/advisor-runtime.js.map +1 -0
  9. package/lib/client/advisor-card.d.ts +90 -0
  10. package/lib/client/advisor-store.d.ts +310 -0
  11. package/lib/client/index.d.ts +39 -0
  12. package/lib/client/locales.d.ts +40 -0
  13. package/lib/client.d.ts +1 -0
  14. package/lib/client.js +840 -0
  15. package/lib/commands.d.ts +136 -0
  16. package/lib/commands.js +185 -0
  17. package/lib/commands.js.map +1 -0
  18. package/lib/config.d.ts +74 -0
  19. package/lib/config.js +93 -0
  20. package/lib/config.js.map +1 -0
  21. package/lib/delivery.d.ts +129 -0
  22. package/lib/delivery.js +169 -0
  23. package/lib/delivery.js.map +1 -0
  24. package/lib/emission-guard.d.ts +99 -0
  25. package/lib/emission-guard.js +155 -0
  26. package/lib/emission-guard.js.map +1 -0
  27. package/lib/gateway.d.ts +116 -0
  28. package/lib/gateway.js +214 -0
  29. package/lib/gateway.js.map +1 -0
  30. package/lib/index.d.ts +48 -0
  31. package/lib/index.js +485 -0
  32. package/lib/index.js.map +1 -0
  33. package/lib/kinds.d.ts +38 -0
  34. package/lib/kinds.js +24 -0
  35. package/lib/kinds.js.map +1 -0
  36. package/lib/prompts.d.ts +22 -0
  37. package/lib/prompts.js +38 -0
  38. package/lib/prompts.js.map +1 -0
  39. package/lib/settings.d.ts +96 -0
  40. package/lib/settings.js +141 -0
  41. package/lib/settings.js.map +1 -0
  42. package/lib/transcript.d.ts +257 -0
  43. package/lib/transcript.js +530 -0
  44. package/lib/transcript.js.map +1 -0
  45. package/package.json +90 -0
  46. package/scripts/build-client.mjs +268 -0
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Client bundle build (plan dsh-advisor-settings-n2, task 2 — KD-S5): emits
4
+ * the closure-factory CJS artifact the dsh web loader consumes —
5
+ * `window.__ModuleLoader__.load({ id: 'dsh-advisor', factory: (require) => {
6
+ * … return module.exports; } })`. Externals resolve through the loader module
7
+ * table — the frozen `CLIENT_EXTERNALS` (platform seed entries + the
8
+ * documented `@deepseek-ai/dsh-client-runtime/client` exemption); everything
9
+ * else inlines. The web shell's ClientModuleHostService serves the artifact at
10
+ * `/plugins/dsh-advisor/client.js` and executes it as a CLASSIC <script>, so
11
+ * the emitted text must contain NO `import.meta` and no top-level ESM
12
+ * statements (either is a parse-time SyntaxError).
13
+ *
14
+ * A purity gate (esbuild onResolve) rejects any non-external, non-inline-safe
15
+ * `@deepseek-ai/*` VALUE import — type-only imports are erased by esbuild's
16
+ * TS loader before resolution and never reach the gate; cross-plugin
17
+ * collaboration goes through cordis services. The in-script contract
18
+ * assertions then re-check the artifact (requires ⊆ CLIENT_EXTERNALS, no
19
+ * `import.meta`, no ESM statements).
20
+ *
21
+ * Build tool: esbuild (explicit devDependency — the repo is pnpm/node; this
22
+ * is the node-port of mstar's bun `build-client-bundle.ts`). CSS Modules are
23
+ * now inlined (mirror of the dsh tsdown preset's dsh-css-modules-inline):
24
+ * `*.module.css` side-effect imports compile through lightningcss
25
+ * ([hash]_[local], minified) and emit a guarded `<style data-plugin>`
26
+ * injection stub into the bundle — the deferred-styling extension point is
27
+ * closed (plan Review Gate Summary, qc1 S-2).
28
+ *
29
+ * Declarations: runs `tsc -p tsconfig.client.json` (emitDeclarationOnly) into
30
+ * `lib/client/`, then writes the flat re-export `lib/client.d.ts`
31
+ * (`export * from './client/index.js'`) that `exports["./client"].types`
32
+ * points at — same shape as the mstar client bundle.
33
+ */
34
+
35
+ import { build } from 'esbuild'
36
+ import { transform } from 'lightningcss'
37
+ import { createRequire } from 'node:module'
38
+ import { spawnSync } from 'node:child_process'
39
+ import { readFileSync, rmSync, writeFileSync } from 'node:fs'
40
+ import { basename, join } from 'node:path'
41
+
42
+ const require = createRequire(import.meta.url)
43
+
44
+ const ID = 'dsh-advisor'
45
+ const ENTRY = 'src/client/index.ts'
46
+ const OUT_FILE = 'lib/client.js'
47
+
48
+ /** Loader module table (KD-S5): platform seed entries plus the documented runtime/client exemption. */
49
+ export const CLIENT_EXTERNALS = [
50
+ 'react',
51
+ 'react/jsx-runtime',
52
+ 'react-dom',
53
+ 'react-dom/client',
54
+ '@deepseek-ai/cordis',
55
+ '@deepseek-ai/dsh-client-ui-slots',
56
+ '@deepseek-ai/dsh-client-web-react',
57
+ '@deepseek-ai/dsh-client-ui-primitives',
58
+ '@deepseek-ai/dsh-client-schema-form',
59
+ '@deepseek-ai/dsh-client-runtime/client',
60
+ ]
61
+
62
+ /** Virtual-id wrapper keeping module CSS away from esbuild's own css pipeline (mirror of dsh tsdown.client.ts dsh-css-modules-inline). */
63
+ const CSS_VIRTUAL_PREFIX = '\0dsh-css:'
64
+ const CSS_VIRTUAL_SUFFIX = '.mjs'
65
+ /** Namespace esbuild requires on non-file paths returned from onResolve. */
66
+ const CSS_NAMESPACE = 'dsh-css-modules'
67
+
68
+ /** Wire/type layers with no shared runtime identity that may inline (tsdown.client.ts mirror). */
69
+ const INLINE_SAFE = /^@deepseek-ai\/dsh-(host-apiproxy|session|llm|tools|brand)(\/|$)/
70
+ /** Generated descriptor/codec contribution with no shared runtime identity. */
71
+ const GENERATED_REMOTE = /^@deepseek-ai\/dsh-[a-z0-9]+(?:-[a-z0-9]+)*\/remote$/
72
+
73
+ const result = await build({
74
+ entryPoints: [ENTRY],
75
+ outfile: OUT_FILE,
76
+ bundle: true,
77
+ format: 'cjs',
78
+ platform: 'browser',
79
+ target: 'es2020',
80
+ // Automatic JSX runtime (T2 review Critical-1): the CLASSIC transform emits
81
+ // a free `React.createElement` global that the loader module table does not
82
+ // provide -> ReferenceError on first render. The automatic runtime emits
83
+ // `require("react/jsx-runtime")` instead, which IS a frozen CLIENT_EXTERNALS
84
+ // entry (below) and the loader answers it — same as dsh-private's bundles.
85
+ jsx: 'automatic',
86
+ // Externals resolve through the loader module table (the injected require);
87
+ // a require() the table cannot answer is a guaranteed runtime throw, so the
88
+ // rule is the table list itself: no opinion for table entries, bundle
89
+ // everything else (no peer auto-externalization).
90
+ external: [...CLIENT_EXTERNALS],
91
+ // zustand-style deps read process.env.NODE_ENV and probe
92
+ // import.meta.env.MODE; the loader executes the bundle as a classic script
93
+ // where a literal `import.meta` is a SyntaxError. Defining the full
94
+ // `import.meta.env` object erases every reference. Both keys honor the
95
+ // build's NODE_ENV so a dev build keeps dev-branch semantics; artifacts
96
+ // default to production.
97
+ define: {
98
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV ?? 'production'),
99
+ 'import.meta.env.MODE': JSON.stringify(process.env.NODE_ENV ?? 'production'),
100
+ 'import.meta.env': JSON.stringify({ MODE: process.env.NODE_ENV ?? 'production' }),
101
+ },
102
+ // Closure-factory handoff (KD-S5): `module`/`exports` are declared inside
103
+ // the factory body; the factory returns that surface to the loader.
104
+ banner: {
105
+ js: `window.__ModuleLoader__.load({ id: ${JSON.stringify(ID)}, factory: (require) => {\nvar module = { exports: {} }; var exports = module.exports;`,
106
+ },
107
+ footer: {
108
+ js: 'return module.exports; } });',
109
+ },
110
+ plugins: [{
111
+ // Bundle purity gate (build-time mirror of the value-import boundary):
112
+ // platform seed entries stay external, inline-safe wire layers inline,
113
+ // and every other @deepseek-ai value import is a build error — a
114
+ // cross-plugin value import either inlines a duplicate runtime instance
115
+ // or requires a specifier the frozen module table cannot answer.
116
+ name: 'dsh-client-bundle-purity',
117
+ setup(build) {
118
+ build.onResolve({ filter: /^@deepseek-ai\// }, (args) => {
119
+ if (CLIENT_EXTERNALS.includes(args.path)) return undefined // platform module: external wins
120
+ if (INLINE_SAFE.test(args.path) || GENERATED_REMOTE.test(args.path)) return undefined // wire contribution: inline is the point
121
+ throw new Error(
122
+ `client bundle purity: "${args.path}" is not a platform module (CLIENT_EXTERNALS), an inline-safe wire layer, or a generated /remote contribution — `
123
+ + 'cross-plugin value imports are forbidden; collaborate through cordis services (type-only imports are erased and never reach this gate)',
124
+ )
125
+ })
126
+ },
127
+ }, {
128
+ // CSS Modules inline injection (dsh tsdown.client.ts dsh-css-modules-inline
129
+ // mirror): side-effect `*.module.css` imports compile through lightningcss
130
+ // ([hash]_[local], minified) and the module exports the hashed class map.
131
+ // The emitted stub injects one guarded `<style data-plugin>` per module
132
+ // file at factory execution; the web shell's loader cleans up plugin-owned
133
+ // tags by `style[data-plugin=<id>]` + per-module `data-plugin-css`.
134
+ name: 'dsh-css-modules-inline',
135
+ setup(build) {
136
+ build.onResolve({ filter: /\.module\.css$/ }, (args) => {
137
+ // Absolute physical path, wrapped in the virtual id (suffix keeps esbuild off its own CSS pipeline).
138
+ return { path: CSS_VIRTUAL_PREFIX + join(args.resolveDir, args.path) + CSS_VIRTUAL_SUFFIX, namespace: CSS_NAMESPACE }
139
+ })
140
+ build.onLoad({ filter: /^\0dsh-css:/, namespace: CSS_NAMESPACE }, (args) => {
141
+ const fileId = args.path.slice(CSS_VIRTUAL_PREFIX.length, -CSS_VIRTUAL_SUFFIX.length)
142
+ const source = readFileSync(fileId)
143
+ const { code, exports: cssExports } = transform({
144
+ filename: fileId,
145
+ code: source,
146
+ cssModules: { pattern: '[hash]_[local]' },
147
+ minify: true,
148
+ })
149
+ const classMap = {}
150
+ // Deterministic emit (F-2, QC consolidated): sort the export entries by
151
+ // local name so the class-map JSON literal's key order is byte-stable
152
+ // across consecutive builds (lightningcss insertion order is
153
+ // nondeterministic → M7/M9 bundle non-idempotency).
154
+ for (const [local, exp] of Object.entries(cssExports ?? {}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) classMap[local] = exp.name
155
+ // One <style data-plugin> per module file; idempotent under re-evaluation.
156
+ // The emitted stub is the EXACT mirror of the dsh tsdown preset's
157
+ // dsh-css-modules-inline load() output: the guard is only the
158
+ // `typeof document` + `data-plugin-css` presence check, and the class
159
+ // map rides the default export as a JSON literal. The advisor card
160
+ // imports that default binding and consumes its classes in JSX, so
161
+ // the export — and the whole stub — survives bundling unchanged.
162
+ //
163
+ // tagId selector-safety (F-5, QC consolidated): the guard builds
164
+ // `style[data-plugin-css=<JSON.stringify(tagId)>]` via JS escaping,
165
+ // NOT CSS escaping — so tagId (`dsh-advisor/<basename>`) must stay
166
+ // CSS-attribute-selector-safe: no `"`, no `\`, no `]`. CSS-module
167
+ // basenames satisfy this by construction (plain [A-Za-z0-9_.-] file
168
+ // names), which is why the virtual id is allowed to carry one.
169
+ const contents = [
170
+ `const css = ${JSON.stringify(code.toString())};`,
171
+ `const tagId = ${JSON.stringify(`${ID}/${basename(fileId)}`)};`,
172
+ `if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css=' + JSON.stringify(tagId) + ']') === null) {`,
173
+ ` const tag = document.createElement('style');`,
174
+ ` tag.dataset.plugin = ${JSON.stringify(ID)};`,
175
+ ` tag.dataset.pluginCss = tagId;`,
176
+ ` tag.textContent = css;`,
177
+ ` document.head.appendChild(tag);`,
178
+ `}`,
179
+ `export default ${JSON.stringify(classMap)};`,
180
+ ].join('\n')
181
+ // F-3 (QC consolidated): declare the physical css file as a watch
182
+ // dependency (mirror of the dsh tsdown preset's addWatchFile(fileId)),
183
+ // so a watch-mode build rebuilds when the module css changes.
184
+ return { loader: 'js', contents, watchFiles: [fileId] }
185
+ })
186
+ },
187
+ }],
188
+ })
189
+
190
+ if (result.errors.length > 0) {
191
+ throw new Error(`client bundle build failed:\n${result.errors.map((e) => e.text).join('\n')}`)
192
+ }
193
+
194
+ // Inline bundle-contract assertions (KD-S5): the emitted text must carry the
195
+ // closure-factory load handoff, must not VALUE-import `@deepseek-ai/*` outside
196
+ // the frozen externals table, and must contain NO `import.meta` / ESM
197
+ // statements — the web loader executes this file as a classic <script>.
198
+ //
199
+ // F-1 (QC consolidated): esbuild stamps the virtual CSS-module id — including
200
+ // a raw NUL byte and the builder's absolute path — into an output comment
201
+ // (`// dsh-css-modules:\0dsh-css:<abs path>.mjs`). Strip those comment lines
202
+ // so the shipped/served artifact carries neither the NUL byte nor a local
203
+ // machine path (the regex matches the observed esbuild 0.28 comment form;
204
+ // the \x00 is the raw byte, [^\n]* the rest of the line, gm spans all of them).
205
+ const bundleText = readFileSync(OUT_FILE, 'utf8')
206
+ .replace(/^\/\/ dsh-css-modules:\x00[^\n]*\n/gm, '')
207
+ // The strip must land in the artifact itself, not just the in-memory text.
208
+ writeFileSync(OUT_FILE, bundleText)
209
+ if (!bundleText.includes('window.__ModuleLoader__.load(') || !bundleText.includes(JSON.stringify(ID))) {
210
+ throw new Error('client bundle contract: the closure-factory load handoff with the plugin id is missing')
211
+ }
212
+ for (const match of bundleText.matchAll(/require\(\s*["'](@deepseek-ai\/[^"']+)["']\s*\)/g)) {
213
+ const specifier = match[1]
214
+ if (!CLIENT_EXTERNALS.includes(specifier)) {
215
+ throw new Error(`client bundle contract: "${specifier}" VALUE import survived the purity gate`)
216
+ }
217
+ }
218
+ if (bundleText.includes('import.meta') || /(^|\n)\s*(import|export)\s/.test(bundleText)) {
219
+ throw new Error('client bundle contract: emitted bundle contains import.meta / ESM statements — the classic-script loader would fail to parse it')
220
+ }
221
+ // F-1 regression (QC consolidated): the artifact must never ship a raw NUL
222
+ // byte or the builder's absolute machine path — both leaked through esbuild's
223
+ // virtual-module comment (`// dsh-css-modules:\0…`) and stripped above.
224
+ if (bundleText.includes('\u0000')) {
225
+ throw new Error('client bundle contract: emitted bundle contains a NUL byte — esbuild virtual-module comment not stripped')
226
+ }
227
+ if (bundleText.includes('/Users/')) {
228
+ throw new Error('client bundle contract: emitted bundle leaks a builder machine path ("/Users/")')
229
+ }
230
+ // CSS-modules inline wiring: the bundle must carry the guarded <style
231
+ // data-plugin> injection stub and the tagId of the advisor card module
232
+ // (the loader cleans up plugin-owned tags by `style[data-plugin=<id>]` +
233
+ // `data-plugin-css`; without this wiring the card renders unstyled).
234
+ for (const fragment of [
235
+ 'data-plugin',
236
+ 'document.head.appendChild',
237
+ 'dsh-advisor/advisor-card.module.css',
238
+ ]) {
239
+ if (!bundleText.includes(fragment)) {
240
+ throw new Error(`client bundle contract: CSS-modules inline wiring missing — "${fragment}" not in the emitted bundle`)
241
+ }
242
+ }
243
+ // Quote-agnostic: esbuild's printer normalizes string quotes, so accept both.
244
+ if (!/document\.createElement\(['"]style['"]\)/.test(bundleText)) {
245
+ throw new Error('client bundle contract: CSS-modules inline wiring missing — document.createElement("style") not in the emitted bundle')
246
+ }
247
+ // F-2 (QC consolidated): pin the attribution the loader cleanup keys on —
248
+ // the web shell removes plugin-owned tags by `style[data-plugin=<id>]`, so the
249
+ // stub must actually assign tag.dataset.plugin (not just carry the literal
250
+ // "data-plugin" string). Quote/whitespace-normalized: match the assignment.
251
+ if (!/tag\.dataset\.plugin\s*=/.test(bundleText)) {
252
+ throw new Error('client bundle contract: CSS-modules inline wiring missing — tag.dataset.plugin attribution (loader cleanup key) not in the emitted bundle')
253
+ }
254
+
255
+ // Declarations for `exports["./client"].types`: tsc emits the client .d.ts
256
+ // tree into lib/client/ (emitDeclarationOnly), then we write the flat
257
+ // re-export so the locked export path stays stable regardless of the internal
258
+ // layout (same shape as the mstar client bundle).
259
+ const tscBin = require.resolve('typescript/bin/tsc')
260
+ rmSync('lib/client', { recursive: true, force: true })
261
+ const tsc = spawnSync(process.execPath, [tscBin, '-p', 'tsconfig.client.json'], { stdio: 'inherit' })
262
+ if (tsc.status !== 0) {
263
+ throw new Error(`client bundle declarations failed (tsc -p tsconfig.client.json, exit ${String(tsc.status)})`)
264
+ }
265
+ const clientDts = join('lib', 'client.d.ts')
266
+ writeFileSync(clientDts, `export * from './client/index.js'\n`)
267
+
268
+ console.log(`build-client: ${ENTRY} -> ${OUT_FILE} (closure-factory CJS) + ${clientDts}`)