dsh-git-worktree 0.1.1 → 0.2.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 (133) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +21 -21
  3. package/README.md +115 -37
  4. package/README.zh.md +115 -37
  5. package/cordis.patch.yml +7 -7
  6. package/lib/adapters/registry.js +40 -0
  7. package/lib/adapters/registry.js.map +1 -1
  8. package/lib/adapters/safe-file.js +34 -4
  9. package/lib/adapters/safe-file.js.map +1 -1
  10. package/lib/client/WorktreeCreateRow.d.ts +7 -0
  11. package/lib/client/WorktreeCreateRow.js +26 -0
  12. package/lib/client/WorktreeCreateRow.js.map +1 -0
  13. package/lib/client/WorktreeReviewRow.d.ts +11 -0
  14. package/lib/client/WorktreeReviewRow.js +67 -0
  15. package/lib/client/WorktreeReviewRow.js.map +1 -0
  16. package/lib/client/actions.d.ts +86 -0
  17. package/lib/client/actions.js +35 -0
  18. package/lib/client/actions.js.map +1 -0
  19. package/lib/client/console-remote/adapter.d.ts +3 -0
  20. package/lib/client/console-remote/adapter.js +59 -0
  21. package/lib/client/console-remote/adapter.js.map +1 -0
  22. package/lib/client/console-remote/index.d.ts +18 -0
  23. package/lib/client/console-remote/index.js +17 -0
  24. package/lib/client/console-remote/index.js.map +1 -0
  25. package/lib/client/index.d.ts +17 -0
  26. package/lib/client/index.js +44 -0
  27. package/lib/client/index.js.map +1 -0
  28. package/lib/client/model.d.ts +68 -0
  29. package/lib/client/model.js +127 -0
  30. package/lib/client/model.js.map +1 -0
  31. package/lib/client/pre-session/PreSessionWorktreeToggle.d.ts +16 -0
  32. package/lib/client/pre-session/PreSessionWorktreeToggle.js +128 -0
  33. package/lib/client/pre-session/PreSessionWorktreeToggle.js.map +1 -0
  34. package/lib/client/pre-session/controller.d.ts +42 -0
  35. package/lib/client/pre-session/controller.js +149 -0
  36. package/lib/client/pre-session/controller.js.map +1 -0
  37. package/lib/client/pre-session/index.d.ts +15 -0
  38. package/lib/client/pre-session/index.js +16 -0
  39. package/lib/client/pre-session/index.js.map +1 -0
  40. package/lib/client/review-console/ReviewActions.d.ts +15 -0
  41. package/lib/client/review-console/ReviewActions.js +219 -0
  42. package/lib/client/review-console/ReviewActions.js.map +1 -0
  43. package/lib/client/review-console/WorktreeReviewPanel.d.ts +20 -0
  44. package/lib/client/review-console/WorktreeReviewPanel.js +75 -0
  45. package/lib/client/review-console/WorktreeReviewPanel.js.map +1 -0
  46. package/lib/client/review-console/index.d.ts +8 -0
  47. package/lib/client/review-console/index.js +22 -0
  48. package/lib/client/review-console/index.js.map +1 -0
  49. package/lib/client/review-console/review-console.styles.d.ts +1 -0
  50. package/lib/client/review-console/review-console.styles.js +81 -0
  51. package/lib/client/review-console/review-console.styles.js.map +1 -0
  52. package/lib/client/review-console/status-events.d.ts +3 -0
  53. package/lib/client/review-console/status-events.js +6 -0
  54. package/lib/client/review-console/status-events.js.map +1 -0
  55. package/lib/client/styles.d.ts +1 -0
  56. package/lib/client/styles.js +78 -0
  57. package/lib/client/styles.js.map +1 -0
  58. package/lib/client/target-console/TargetStatusAction.d.ts +7 -0
  59. package/lib/client/target-console/TargetStatusAction.js +46 -0
  60. package/lib/client/target-console/TargetStatusAction.js.map +1 -0
  61. package/lib/client/target-console/WorktreeConsoleView.d.ts +9 -0
  62. package/lib/client/target-console/WorktreeConsoleView.js +362 -0
  63. package/lib/client/target-console/WorktreeConsoleView.js.map +1 -0
  64. package/lib/client/target-console/WorktreeReviewStatus.d.ts +9 -0
  65. package/lib/client/target-console/WorktreeReviewStatus.js +91 -0
  66. package/lib/client/target-console/WorktreeReviewStatus.js.map +1 -0
  67. package/lib/client/target-console/index.d.ts +14 -0
  68. package/lib/client/target-console/index.js +23 -0
  69. package/lib/client/target-console/index.js.map +1 -0
  70. package/lib/client/target-console/target-console.styles.d.ts +1 -0
  71. package/lib/client/target-console/target-console.styles.js +130 -0
  72. package/lib/client/target-console/target-console.styles.js.map +1 -0
  73. package/lib/client.js +6813 -0
  74. package/lib/commands.d.ts +2 -4
  75. package/lib/commands.js +54 -28
  76. package/lib/commands.js.map +1 -1
  77. package/lib/console-contract.d.ts +236 -0
  78. package/lib/console-contract.js +86 -0
  79. package/lib/console-contract.js.map +1 -0
  80. package/lib/console-host/control-plane.d.ts +29 -0
  81. package/lib/console-host/control-plane.js +443 -0
  82. package/lib/console-host/control-plane.js.map +1 -0
  83. package/lib/console-host/errors.d.ts +6 -0
  84. package/lib/console-host/errors.js +31 -0
  85. package/lib/console-host/errors.js.map +1 -0
  86. package/lib/console-host/projection.d.ts +12 -0
  87. package/lib/console-host/projection.js +131 -0
  88. package/lib/console-host/projection.js.map +1 -0
  89. package/lib/console-host/review-diff.d.ts +18 -0
  90. package/lib/console-host/review-diff.js +176 -0
  91. package/lib/console-host/review-diff.js.map +1 -0
  92. package/lib/console-host/service.d.ts +24 -0
  93. package/lib/console-host/service.js +131 -0
  94. package/lib/console-host/service.js.map +1 -0
  95. package/lib/console-remote/descriptors.d.ts +3 -0
  96. package/lib/console-remote/descriptors.js +96 -0
  97. package/lib/console-remote/descriptors.js.map +1 -0
  98. package/lib/console-remote/remote.d.ts +27 -0
  99. package/lib/console-remote/remote.js +4 -0
  100. package/lib/console-remote/remote.js.map +1 -0
  101. package/lib/console-remote/schemas.d.ts +29 -0
  102. package/lib/console-remote/schemas.js +143 -0
  103. package/lib/console-remote/schemas.js.map +1 -0
  104. package/lib/console-remote/typert.d.ts +3 -0
  105. package/lib/console-remote/typert.js +14 -0
  106. package/lib/console-remote/typert.js.map +1 -0
  107. package/lib/index.d.ts +22 -6
  108. package/lib/index.js +28 -10
  109. package/lib/index.js.map +1 -1
  110. package/lib/managed-worktree-path.d.ts +14 -13
  111. package/lib/managed-worktree-path.js +32 -22
  112. package/lib/managed-worktree-path.js.map +1 -1
  113. package/lib/ports.d.ts +37 -1
  114. package/lib/session-checkout-apply.d.ts +54 -1
  115. package/lib/session-checkout-apply.js +318 -28
  116. package/lib/session-checkout-apply.js.map +1 -1
  117. package/lib/session-checkout-module.js +853 -33
  118. package/lib/session-checkout-module.js.map +1 -1
  119. package/lib/session-target-context.d.ts +6 -0
  120. package/lib/session-target-context.js +16 -0
  121. package/lib/session-target-context.js.map +1 -0
  122. package/lib/tools.d.ts +5 -6
  123. package/lib/tools.js +43 -233
  124. package/lib/tools.js.map +1 -1
  125. package/lib/types.d.ts +57 -11
  126. package/lib/types.js +6 -6
  127. package/lib/types.js.map +1 -1
  128. package/package.json +116 -67
  129. package/scripts/check-publish.mjs +193 -0
  130. package/scripts/clean-lib.mjs +7 -0
  131. package/scripts/dev-dsh-lib.mjs +453 -0
  132. package/scripts/dev-dsh.mjs +89 -0
  133. package/scripts/dsh-source-runner.mjs +14 -0
package/package.json CHANGED
@@ -1,67 +1,116 @@
1
- {
2
- "name": "dsh-git-worktree",
3
- "version": "0.1.1",
4
- "description": "Domi-grade git worktree isolation and delivery for DeepSeek Harness: permanent worktrees, ready-for-review / apply / discard / finish lifecycle, conflict handling, and safe cleanup.",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./manager": {
14
- "types": "./lib/manager.d.ts",
15
- "default": "./lib/manager.js"
16
- },
17
- "./package.json": "./package.json",
18
- "./cordis.patch.yml": "./cordis.patch.yml"
19
- },
20
- "files": [
21
- "lib",
22
- "cordis.patch.yml"
23
- ],
24
- "license": "MIT",
25
- "dsh": {
26
- "bundle": {
27
- "patch": "./cordis.patch.yml"
28
- }
29
- },
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/wloops/dsh-git-worktree.git"
33
- },
34
- "bugs": {
35
- "url": "https://github.com/wloops/dsh-git-worktree/issues"
36
- },
37
- "homepage": "https://github.com/wloops/dsh-git-worktree#readme",
38
- "engines": {
39
- "node": ">=20"
40
- },
41
- "scripts": {
42
- "build": "tsc",
43
- "typecheck": "tsc --noEmit",
44
- "test": "vitest run",
45
- "prepublishOnly": "pnpm run build && pnpm run test"
46
- },
47
- "dependencies": {
48
- "@deepseek-ai/cordis": "4.0.1",
49
- "@deepseek-ai/dsh-commands": "0.1.0-rc.6",
50
- "@deepseek-ai/dsh-subprocess": "0.1.0-rc.6",
51
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
52
- "@deepseek-ai/schemastery": "3.18.1"
53
- },
54
- "devDependencies": {
55
- "@types/node": "^22.0.0",
56
- "typescript": "^5.6.0",
57
- "vitest": "^2.1.0"
58
- },
59
- "keywords": [
60
- "deepseek-harness",
61
- "dsh",
62
- "cordis",
63
- "plugin",
64
- "worktree",
65
- "git"
66
- ]
67
- }
1
+ {
2
+ "name": "dsh-git-worktree",
3
+ "version": "0.2.0",
4
+ "description": "Experimental Git worktree Session Targets for DeepSeek Harness with real isolated sessions, review ToolViews, human-confirmed delivery, and safe cleanup.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./client": {
14
+ "types": "./lib/client/console-remote/index.d.ts",
15
+ "default": "./lib/client.js"
16
+ },
17
+ "./typert": {
18
+ "types": "./lib/console-remote/typert.d.ts",
19
+ "default": "./lib/console-remote/typert.js"
20
+ },
21
+ "./remote": {
22
+ "types": "./lib/console-remote/remote.d.ts",
23
+ "default": "./lib/console-remote/remote.js"
24
+ },
25
+ "./console-contract": {
26
+ "types": "./lib/console-contract.d.ts",
27
+ "default": "./lib/console-contract.js"
28
+ },
29
+ "./package.json": "./package.json",
30
+ "./cordis.patch.yml": "./cordis.patch.yml"
31
+ },
32
+ "files": [
33
+ "lib",
34
+ "scripts",
35
+ "CHANGELOG.md",
36
+ "cordis.patch.yml"
37
+ ],
38
+ "license": "MIT",
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": "./cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "inject": [
45
+ "@deepseek-ai/dsh-client-runtime",
46
+ "@deepseek-ai/dsh-client-ui-conversation",
47
+ "@deepseek-ai/dsh-client-ui-tool",
48
+ "@deepseek-ai/dsh-api-gateway"
49
+ ],
50
+ "platform": "web"
51
+ }
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/wloops/dsh-git-worktree.git"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/wloops/dsh-git-worktree/issues"
59
+ },
60
+ "homepage": "https://github.com/wloops/dsh-git-worktree#readme",
61
+ "engines": {
62
+ "node": ">=20"
63
+ },
64
+ "scripts": {
65
+ "build": "node scripts/clean-lib.mjs && tsc && tsdown",
66
+ "typecheck": "tsc --noEmit",
67
+ "test": "vitest run",
68
+ "prepare": "pnpm run build",
69
+ "check:publish": "node scripts/check-publish.mjs",
70
+ "check:publish:release": "node scripts/check-publish.mjs --require-clean",
71
+ "dev:dsh": "node scripts/dev-dsh.mjs run",
72
+ "dev:dsh:install": "node scripts/dev-dsh.mjs install",
73
+ "dev:dsh:smoke": "node scripts/dev-dsh.mjs smoke",
74
+ "dev:dsh:remove": "node scripts/dev-dsh.mjs remove",
75
+ "prepublishOnly": "pnpm run build && pnpm run check:publish:release && pnpm run test"
76
+ },
77
+ "dependencies": {
78
+ "@deepseek-ai/cordis": "4.0.1",
79
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
80
+ "@deepseek-ai/dsh-commands": "0.1.0-rc.6",
81
+ "@deepseek-ai/dsh-session": "0.1.0-rc.6",
82
+ "@deepseek-ai/dsh-subprocess": "0.1.0-rc.6",
83
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
84
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
85
+ "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6",
86
+ "@deepseek-ai/dsh-typert-registry": "0.1.0-rc.6",
87
+ "@deepseek-ai/schemastery": "3.18.1",
88
+ "zod": "4.4.3"
89
+ },
90
+ "peerDependencies": {
91
+ "react": "^18.2.0"
92
+ },
93
+ "devDependencies": {
94
+ "@deepseek-ai/dsh-api-gateway": "0.1.0-rc.6",
95
+ "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
96
+ "@deepseek-ai/dsh-typert-loader": "0.1.0-rc.6",
97
+ "@testing-library/react": "^16.3.2",
98
+ "@types/node": "^22.0.0",
99
+ "@types/react": "~18.3.31",
100
+ "@types/react-dom": "~18.3.7",
101
+ "jsdom": "^24.1.3",
102
+ "react": "^18.3.1",
103
+ "react-dom": "^18.3.1",
104
+ "tsdown": "^0.17.2",
105
+ "typescript": "^5.6.0",
106
+ "vitest": "^2.1.0"
107
+ },
108
+ "keywords": [
109
+ "deepseek-harness",
110
+ "dsh",
111
+ "cordis",
112
+ "plugin",
113
+ "worktree",
114
+ "git"
115
+ ]
116
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Pre-publish integrity gate. It validates every declared export, the Host
3
+ * mount patch, and the browser closure bundle instead of checking only the
4
+ * root entry. Set CHECK_PUBLISH_REQUIRE_CLEAN=1 in release CI to additionally
5
+ * require a clean Git checkout.
6
+ */
7
+ import { execSync } from 'node:child_process'
8
+ import { createRequire } from 'node:module'
9
+ import { existsSync, readFileSync } from 'node:fs'
10
+ import { dirname, relative, resolve, sep } from 'node:path'
11
+ import { fileURLToPath, pathToFileURL } from 'node:url'
12
+ import vm from 'node:vm'
13
+
14
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
15
+ const require = createRequire(import.meta.url)
16
+ const fail = (message) => { console.error(`check-publish: FAIL: ${message}`); process.exit(1) }
17
+ const ok = (message) => console.log(`check-publish: ok: ${message}`)
18
+ const manifest = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'))
19
+
20
+ function exportTargets(value, label) {
21
+ if (typeof value === 'string') return [{ condition: 'default', target: value }]
22
+ if (!value || typeof value !== 'object' || Array.isArray(value)) fail(`${label} must be a path or condition map`)
23
+ return Object.entries(value).flatMap(([condition, nested]) => {
24
+ if (typeof nested === 'string') return [{ condition, target: nested }]
25
+ return exportTargets(nested, `${label}.${condition}`)
26
+ })
27
+ }
28
+
29
+ function includedByFiles(target) {
30
+ const normalized = target.replace(/^\.\//, '').replaceAll('\\', '/')
31
+ if (normalized === 'package.json') return true
32
+ return (manifest.files ?? []).some((entry) => {
33
+ const normalizedEntry = String(entry).replace(/^\.\//, '').replaceAll('\\', '/').replace(/\/$/, '')
34
+ return normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)
35
+ })
36
+ }
37
+
38
+ // 1. Host bundle declaration and patch.
39
+ const patchRel = manifest.dsh?.bundle?.patch
40
+ if (typeof patchRel !== 'string' || patchRel.length === 0) {
41
+ fail('package.json lacks "dsh".bundle.patch — dsh plugin add would install a plain dependency without mounting it')
42
+ }
43
+ const patchPath = resolve(root, patchRel)
44
+ if (!existsSync(patchPath)) fail(`declared patch file ${patchRel} does not exist`)
45
+ if (!includedByFiles(patchRel)) fail(`${patchRel} is not covered by package.json "files"`)
46
+ const patch = readFileSync(patchPath, 'utf8')
47
+ if (!patch.includes('- insert:') || !patch.includes(`name: ${manifest.name}`)) {
48
+ fail(`${patchRel} must insert one plugin row named ${manifest.name}`)
49
+ }
50
+ ok(`Host mount patch ${patchRel}`)
51
+
52
+ // 2. Every package export must exist and be included in the tarball.
53
+ const exportsMap = manifest.exports
54
+ if (!exportsMap || typeof exportsMap !== 'object' || Array.isArray(exportsMap)) fail('package.json lacks an exports map')
55
+ let exportCount = 0
56
+ for (const [subpath, value] of Object.entries(exportsMap)) {
57
+ for (const { condition, target } of exportTargets(value, `exports[${JSON.stringify(subpath)}]`)) {
58
+ exportCount += 1
59
+ if (!target.startsWith('./')) fail(`export ${subpath} (${condition}) must be package-relative: ${target}`)
60
+ const absolute = resolve(root, target)
61
+ const inside = relative(root, absolute)
62
+ if (inside.startsWith(`..${sep}`) || inside === '..') fail(`export ${subpath} escapes the package: ${target}`)
63
+ if (!existsSync(absolute)) fail(`export ${subpath} (${condition}) points to missing ${target} — run build first`)
64
+ if (!includedByFiles(target)) fail(`export ${subpath} (${condition}) target ${target} is not covered by package.json "files"`)
65
+ }
66
+ }
67
+ ok(`${exportCount} export targets exist and are publishable`)
68
+
69
+ for (const stale of [
70
+ 'lib/client/review-console/DiffViewer.js',
71
+ 'lib/client/review-console/useReviewDiff.js',
72
+ ]) {
73
+ if (existsSync(resolve(root, stale))) fail(`removed acceptance UI survived as stale build output: ${stale}`)
74
+ }
75
+ ok('removed Diff UI has no stale publish artifact')
76
+
77
+ // 3. Manual strict Typert artifacts: one descriptor identity feeds Host Loader and Client mount.
78
+ if (manifest.dependencies?.['@deepseek-ai/dsh-typert-generator'] || manifest.devDependencies?.['@deepseek-ai/dsh-typert-generator']) {
79
+ fail('manual contribution package must not depend on @deepseek-ai/dsh-typert-generator')
80
+ }
81
+ const typertExport = exportsMap['./typert']?.default
82
+ const remoteExport = exportsMap['./remote']?.default
83
+ if (typeof typertExport !== 'string' || typeof remoteExport !== 'string') {
84
+ fail('package must export both ./typert and ./remote artifacts')
85
+ }
86
+ let hostContribution
87
+ let remoteContribution
88
+ try {
89
+ hostContribution = (await import(pathToFileURL(resolve(root, typertExport)).href)).TYPERT
90
+ remoteContribution = (await import(pathToFileURL(resolve(root, remoteExport)).href)).default
91
+ } catch (error) {
92
+ fail(`Typert artifacts failed to import: ${error instanceof Error ? error.stack ?? error.message : String(error)}`)
93
+ }
94
+ if (hostContribution?.package !== manifest.name || hostContribution.face !== 'host') fail('./typert has invalid package/face identity')
95
+ if (remoteContribution?.package !== manifest.name) fail('./remote has invalid package identity')
96
+ if (hostContribution.invocations !== remoteContribution.descriptors) fail('./typert and ./remote must share one descriptor array instance')
97
+ const expectedRemoteMethods = ['current', 'list', 'create', 'inspect', 'reviewDiff', 'preflight', 'preview', 'rollbackPreview', 'discard', 'finalize', 'finalizePreview', 'setRetention', 'retryCleanup']
98
+ if (JSON.stringify(hostContribution.invocations.map(value => value.method)) !== JSON.stringify(expectedRemoteMethods)) {
99
+ fail(`manual Remote methods differ from the required surface: ${hostContribution.invocations.map(value => value.method).join(', ')}`)
100
+ }
101
+ for (const descriptor of hostContribution.invocations) {
102
+ if (descriptor.namespace !== 'gitWorktree' || descriptor.service !== 'gitWorktree') fail(`invalid Remote identity for ${descriptor.method}`)
103
+ if (descriptor.result?.mode !== 'strict' || !descriptor.result.schema?._zod) fail(`Remote ${descriptor.method} result lacks a Zod v4 strict codec`)
104
+ for (const parameter of descriptor.parameters ?? []) {
105
+ if (parameter.codec?.mode !== 'strict' || !parameter.codec.schema?._zod) fail(`Remote ${descriptor.method}/${parameter.wire} lacks a Zod v4 strict codec`)
106
+ }
107
+ }
108
+ ok(`manual strict ./typert + ./remote contribution (${expectedRemoteMethods.length} methods)`)
109
+
110
+ // 4. Host entry metadata required by the Cordis loader.
111
+ const entryRel = exportsMap['.']?.default ?? manifest.main
112
+ if (typeof entryRel !== 'string') fail('exports["."].default is not a path')
113
+ const entry = readFileSync(resolve(root, entryRel), 'utf8')
114
+ if (!/export\s+function\s+apply\b/.test(entry)) fail(`${entryRel} does not export function apply`)
115
+ if (!/export\s+const\s+inject\b/.test(entry)) fail(`${entryRel} does not export const inject`)
116
+ ok(`${entryRel} exports Host apply + inject`)
117
+
118
+ // 5. Browser ToolView declaration and executable ModuleLoader closure.
119
+ const client = manifest.dsh?.client
120
+ if (!client || client.platform !== 'web' || !Array.isArray(client.inject) || client.inject.length === 0) {
121
+ fail('package.json must declare dsh.client { platform: "web", inject: [...] }')
122
+ }
123
+ const requiredClientPackages = [
124
+ '@deepseek-ai/dsh-client-runtime',
125
+ '@deepseek-ai/dsh-client-ui-conversation',
126
+ '@deepseek-ai/dsh-api-gateway',
127
+ ]
128
+ const missingClientPackages = requiredClientPackages.filter(name => !client.inject.includes(name))
129
+ if (missingClientPackages.length > 0) {
130
+ fail(`package.json dsh.client.inject is missing: ${missingClientPackages.join(', ')}`)
131
+ }
132
+ const clientRel = exportsMap['./client']?.default
133
+ if (typeof clientRel !== 'string') fail('exports["./client"].default is required for dsh.client')
134
+ const clientSource = readFileSync(resolve(root, clientRel), 'utf8')
135
+ const browserRequires = [...clientSource.matchAll(/require\(["']([^"']+)["']\)/gu)].map(match => match[1])
136
+ const allowedBrowserRequires = new Set([
137
+ 'react',
138
+ 'react/jsx-runtime',
139
+ '@deepseek-ai/dsh-client-ui-primitives',
140
+ ])
141
+ const unsupportedBrowserRequires = browserRequires.filter(specifier => !allowedBrowserRequires.has(specifier))
142
+ if (unsupportedBrowserRequires.length > 0) {
143
+ fail(`${clientRel} requires modules absent from the browser ModuleLoader table: ${[...new Set(unsupportedBrowserRequires)].join(', ')}`)
144
+ }
145
+ const sandbox = {}
146
+ sandbox.window = sandbox
147
+ sandbox.globalThis = sandbox
148
+ let handoff
149
+ sandbox.__ModuleLoader__ = {
150
+ load(value) {
151
+ if (handoff !== undefined) fail('client bundle registered more than one ModuleLoader handoff')
152
+ handoff = value
153
+ },
154
+ }
155
+ try {
156
+ vm.runInNewContext(clientSource, sandbox, { filename: clientRel })
157
+ } catch (error) {
158
+ fail(`client bundle script failed before ModuleLoader registration: ${error instanceof Error ? error.message : String(error)}`)
159
+ }
160
+ if (!handoff || handoff.id !== manifest.name || typeof handoff.factory !== 'function') {
161
+ fail(`client bundle must call window.__ModuleLoader__.load with id ${JSON.stringify(manifest.name)} and a factory`)
162
+ }
163
+ const browserRequire = (specifier) => {
164
+ if (specifier === '@deepseek-ai/dsh-client-ui-primitives') return { Modal: () => null }
165
+ return require(specifier)
166
+ }
167
+ let clientExports
168
+ try {
169
+ clientExports = handoff.factory(browserRequire)
170
+ } catch (error) {
171
+ fail(`client bundle factory failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`)
172
+ }
173
+ if (!clientExports || typeof clientExports.apply !== 'function' || !Array.isArray(clientExports.inject)) {
174
+ fail('client bundle factory did not return apply + inject')
175
+ }
176
+ if (!clientExports.inject.includes('remote')) fail('client bundle must inject the official Remote service before $mount')
177
+ if (!clientExports.inject.includes('conversation')) fail('client bundle must inject the public conversation service for pre-session draft transfer')
178
+ ok(`${clientRel} registers ${manifest.name} through the browser ModuleLoader contract`)
179
+
180
+ // 6. Release CI and npm's prepublish lifecycle require committed source identity;
181
+ // local review builds intentionally run against a dirty managed Worktree.
182
+ const requireClean = process.env.CHECK_PUBLISH_REQUIRE_CLEAN === '1'
183
+ || process.argv.includes('--require-clean')
184
+ if (requireClean) {
185
+ let dirty = ''
186
+ try { dirty = execSync('git status --porcelain --untracked-files=all', { cwd: root, encoding: 'utf8' }) } catch {}
187
+ if (dirty.trim() !== '') fail(`working tree differs from HEAD:\n${dirty}`)
188
+ ok('working tree matches HEAD')
189
+ } else {
190
+ ok('Git cleanliness check deferred (set CHECK_PUBLISH_REQUIRE_CLEAN=1 for release CI)')
191
+ }
192
+
193
+ console.log('check-publish: all checks passed')
@@ -0,0 +1,7 @@
1
+ import { rmSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ // TypeScript does not delete outputs for removed source files. Always rebuild
5
+ // the publishable tree from an empty lib/ directory so stale UI modules cannot
6
+ // survive into a tarball.
7
+ rmSync(fileURLToPath(new URL('../lib/', import.meta.url)), { recursive: true, force: true })