dsh-edge 0.1.0 → 0.1.3

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 (64) hide show
  1. package/LICENSE +1 -1
  2. package/README.i18n.yaml +2 -2
  3. package/README.md +6 -4
  4. package/README.zh.md +6 -4
  5. package/THIRD_PARTY_NOTICES.md +42 -14
  6. package/dist/index.html +1 -1
  7. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +7 -7
  8. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +61 -61
  9. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +14 -14
  10. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +287 -287
  11. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +10 -10
  12. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +11 -11
  13. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +15 -15
  14. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +8 -8
  15. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +27 -27
  16. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +8 -8
  17. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +31 -31
  18. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +23 -23
  19. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +19 -19
  20. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +29 -29
  21. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +7 -7
  22. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +64 -64
  23. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +183 -183
  24. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +44 -44
  25. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +24 -24
  26. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +79 -79
  27. package/dist/plugins/{@deepseek-ai/dsh-client-ui-edge → dsh-edge-client-ui}/client.js +12 -12
  28. package/package.json +26 -23
  29. package/scripts/assemble-web.mjs +1 -1
  30. package/scripts/bundle.mjs +41 -8
  31. package/scripts/cli.d.mts +1 -0
  32. package/scripts/cli.mjs +37 -6
  33. package/scripts/install.d.mts +6 -6
  34. package/scripts/install.mjs +36 -19
  35. package/scripts/legal-files.mjs +65 -7
  36. package/scripts/smoke-installed.mjs +47 -0
  37. package/scripts/verify-packed.d.mts +24 -0
  38. package/scripts/verify-packed.mjs +85 -23
  39. package/scripts/wrangler-config.d.mts +19 -2
  40. package/scripts/wrangler-config.mjs +68 -27
  41. package/worker/direct/index.js +2347 -0
  42. package/worker/isolated/index.js +44491 -0
  43. package/src/agent.ts +0 -119
  44. package/src/auth.ts +0 -352
  45. package/src/deepseek.ts +0 -231
  46. package/src/deployment.ts +0 -147
  47. package/src/direct-shell-core-empty.ts +0 -10
  48. package/src/direct-shell-protocol.ts +0 -5
  49. package/src/direct-shell.ts +0 -341
  50. package/src/do-session-persistence.ts +0 -1064
  51. package/src/edge-api.ts +0 -796
  52. package/src/edge-credentials.ts +0 -56
  53. package/src/edge-remotes.ts +0 -41
  54. package/src/edge-workspace-store.ts +0 -339
  55. package/src/http.ts +0 -212
  56. package/src/index.ts +0 -196
  57. package/src/instance.ts +0 -1007
  58. package/src/isolated-direct-shell-unavailable.ts +0 -11
  59. package/src/protocol.ts +0 -39
  60. package/src/release.ts +0 -9
  61. package/src/session-store.ts +0 -1110
  62. package/src/sse.ts +0 -86
  63. package/src/web-search.ts +0 -38
  64. package/src/workspace.ts +0 -270
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "dsh-edge",
3
- "version": "0.1.0",
4
- "description": "Install and run DeepSeek Harness on Cloudflare Workers",
3
+ "version": "0.1.3",
4
+ "description": "Run DeepSeek Harness on Cloudflare Workers as an independent community project",
5
+ "author": "pawaca",
5
6
  "license": "MIT",
6
7
  "type": "module",
7
8
  "bin": {
@@ -9,7 +10,7 @@
9
10
  },
10
11
  "files": [
11
12
  "dist",
12
- "src",
13
+ "worker",
13
14
  "scripts/*.d.mts",
14
15
  "scripts/*.mjs",
15
16
  "wrangler.jsonc",
@@ -44,52 +45,54 @@
44
45
  },
45
46
  "dependencies": {
46
47
  "@clack/prompts": "1.7.0",
47
- "@cloudflare/computer": "0.2.0",
48
- "just-bash": "3.3.0",
49
48
  "execa": "^10.0.0",
50
49
  "jsonc-parser": "^3.3.1",
51
- "wrangler": "4.123.0",
50
+ "wrangler": "4.123.0"
51
+ },
52
+ "devDependencies": {
53
+ "@cloudflare/computer": "0.2.0",
54
+ "@cloudflare/workers-types": "5.20260812.1",
55
+ "just-bash": "3.3.0",
56
+ "playwright": "^1.49.0",
57
+ "typescript": "^6.0.3",
58
+ "vitest": "^4.1.8",
59
+ "ws": "^8.21.0",
52
60
  "@deepseek-ai/dsh-brand": "0.1.0-rc.7",
53
61
  "@deepseek-ai/cordis": "4.0.1",
54
- "@deepseek-ai/dsh-anonymous-user-id": "0.1.0-rc.7",
55
- "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.7",
62
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.7",
63
+ "@deepseek-ai/dsh-agent-loop": "0.1.0-rc.7",
56
64
  "@deepseek-ai/dsh-credentials": "0.1.0-rc.7",
65
+ "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.7",
66
+ "@deepseek-ai/dsh-anonymous-user-id": "0.1.0-rc.7",
57
67
  "@deepseek-ai/dsh-launch-environment": "0.1.0-rc.7",
58
- "@deepseek-ai/dsh-agent-loop": "0.1.0-rc.7",
59
68
  "@deepseek-ai/dsh-llm": "0.1.0-rc.7",
60
- "@deepseek-ai/dsh-agent": "0.1.0-rc.7",
61
69
  "@deepseek-ai/dsh-llm-deepseek": "0.1.0-rc.7",
62
70
  "@deepseek-ai/dsh-session": "0.1.0-rc.7",
63
71
  "@deepseek-ai/dsh-session-persistence": "0.1.0-rc.7",
64
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.7",
65
72
  "@deepseek-ai/dsh-session-query": "0.1.0-rc.7",
66
- "@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.0-rc.7",
67
73
  "@deepseek-ai/dsh-settings": "0.1.0-rc.7",
68
- "@deepseek-ai/dsh-tools": "0.1.0-rc.7",
69
- "@deepseek-ai/dsh-tool-web": "0.1.0-rc.7",
74
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.7",
70
75
  "@deepseek-ai/dsh-session-title": "0.1.0-rc.7",
76
+ "@deepseek-ai/dsh-tool-web": "0.1.0-rc.7",
77
+ "@deepseek-ai/dsh-tool-call-timeout-policy": "0.1.0-rc.7",
78
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.7",
71
79
  "@deepseek-ai/dsh-web": "0.1.0-rc.7",
72
80
  "@deepseek-ai/dsh-web-search-deepseek": "0.1.0-rc.7"
73
81
  },
74
- "devDependencies": {
75
- "@cloudflare/workers-types": "5.20260812.1",
76
- "playwright": "^1.49.0",
77
- "typescript": "^6.0.3",
78
- "vitest": "^4.1.8",
79
- "ws": "^8.21.0"
80
- },
81
82
  "scripts": {
82
83
  "bundle:direct": "pnpm run web:build && node scripts/bundle.mjs direct",
83
84
  "bundle:isolated": "pnpm run web:build && node scripts/bundle.mjs isolated",
85
+ "bundle:workers": "node scripts/bundle.mjs direct && node scripts/bundle.mjs isolated",
84
86
  "dev": "pnpm run web:build && wrangler dev --env=\"\"",
85
87
  "dev:isolated": "pnpm run web:build && wrangler dev --env isolated",
86
88
  "dsh-edge": "node scripts/cli.mjs",
87
89
  "example:install": "node examples/install-keyless.mjs",
88
- "install:cloudflare": "pnpm run web:build && node scripts/cli.mjs install",
90
+ "install:cloudflare": "pnpm run web:build && pnpm run bundle:workers && node scripts/cli.mjs install",
91
+ "legal:write": "node scripts/legal-files.mjs write",
89
92
  "pack:verify": "node scripts/verify-packed.mjs",
90
93
  "web:assemble": "node scripts/assemble-web.mjs",
91
94
  "web:build": "pnpm --workspace-root run build:lib:host && pnpm --workspace-root run build:lib:client && pnpm --workspace-root run build:web && pnpm run web:assemble",
92
- "test": "vitest run --root ../.. apps/dsh-edge/tests/agent.spec.ts apps/dsh-edge/tests/auth.spec.ts apps/dsh-edge/tests/cli.spec.ts apps/dsh-edge/tests/deployment-health.spec.ts apps/dsh-edge/tests/deployment.spec.ts apps/dsh-edge/tests/direct-shell.spec.ts apps/dsh-edge/tests/do-session-persistence.spec.ts apps/dsh-edge/tests/edge-api.spec.ts apps/dsh-edge/tests/edge-credentials.spec.ts apps/dsh-edge/tests/edge-remotes.spec.ts apps/dsh-edge/tests/installer.spec.ts apps/dsh-edge/tests/sse.spec.ts apps/dsh-edge/tests/web-search.spec.ts apps/dsh-edge/tests/workspace.spec.ts",
95
+ "test": "vitest run --root ../.. apps/dsh-edge/tests/agent.spec.ts apps/dsh-edge/tests/auth.spec.ts apps/dsh-edge/tests/cli.spec.ts apps/dsh-edge/tests/deployment-health.spec.ts apps/dsh-edge/tests/deployment.spec.ts apps/dsh-edge/tests/direct-shell.spec.ts apps/dsh-edge/tests/do-session-persistence.spec.ts apps/dsh-edge/tests/edge-api.spec.ts apps/dsh-edge/tests/edge-credentials.spec.ts apps/dsh-edge/tests/edge-remotes.spec.ts apps/dsh-edge/tests/installer.spec.ts apps/dsh-edge/tests/sse.spec.ts apps/dsh-edge/tests/verify-packed.spec.ts apps/dsh-edge/tests/web-search.spec.ts apps/dsh-edge/tests/workspace.spec.ts",
93
96
  "test:integration": "pnpm run web:build && node tests/session.integration.mjs",
94
97
  "test:snapshot": "vitest run --config vitest.snapshot.config.ts",
95
98
  "typecheck": "tsc --noEmit"
@@ -23,7 +23,7 @@ const deploymentPatches = [
23
23
  ]
24
24
  // Deployment-owned browser features use the same client-plugin contract but
25
25
  // intentionally stay out of upstream's generic Web composition.
26
- const edgeClientPackages = ['@deepseek-ai/dsh-client-ui-edge']
26
+ const edgeClientPackages = ['dsh-edge-client-ui']
27
27
 
28
28
  // HMR is useful only when the upstream Node host also runs its bundle watcher.
29
29
  // Edge deploys immutable, content-addressed static assets instead.
@@ -1,11 +1,15 @@
1
1
  import { createRequire } from 'node:module'
2
- import { mkdtemp, rm } from 'node:fs/promises'
2
+ import { lstat, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
6
6
  import { execa } from 'execa'
7
+ import edgePackage from '../package.json' with { type: 'json' }
7
8
  import { requireGzipBudget } from './bundle-size.mjs'
8
- import { writeModeWranglerConfig } from './wrangler-config.mjs'
9
+ import {
10
+ workerArtifactPath,
11
+ writeSourceModeWranglerConfig,
12
+ } from './wrangler-config.mjs'
9
13
 
10
14
  const DIRECT_GZIP_BUDGET_BYTES = 900 * 1024
11
15
  const appDirectory = fileURLToPath(new URL('..', import.meta.url))
@@ -21,12 +25,14 @@ if (mode !== 'direct' && mode !== 'isolated') {
21
25
  try {
22
26
  const configFile = join(directory, 'wrangler.json')
23
27
  const outputDirectory = join(directory, 'worker')
24
- await writeModeWranglerConfig(mode, configFile)
28
+ await writeSourceModeWranglerConfig(mode, configFile)
25
29
  const result = await execa(process.execPath, [
26
30
  wranglerCli,
27
31
  'deploy',
28
32
  '--config', configFile,
29
33
  '--env', mode === 'direct' ? '' : 'isolated',
34
+ '--define',
35
+ `__DSH_EDGE_DEPLOYMENT_ID__:${JSON.stringify(`dsh-edge@${edgePackage.version}/${mode}`)}`,
30
36
  '--dry-run',
31
37
  '--outdir', outputDirectory,
32
38
  ], {
@@ -38,11 +44,14 @@ if (mode !== 'direct' && mode !== 'isolated') {
38
44
  process.stdout.write(output.endsWith('\n') ? output : `${output}\n`)
39
45
  if (result.exitCode !== 0) {
40
46
  process.exitCode = result.exitCode
41
- } else if (mode === 'direct') {
42
- const bytes = requireGzipBudget(output, DIRECT_GZIP_BUDGET_BYTES)
43
- process.stdout.write(
44
- `Direct Worker gzip budget: ${bytes}/${DIRECT_GZIP_BUDGET_BYTES} bytes.\n`,
45
- )
47
+ } else {
48
+ if (mode === 'direct') {
49
+ const bytes = requireGzipBudget(output, DIRECT_GZIP_BUDGET_BYTES)
50
+ process.stdout.write(
51
+ `Direct Worker gzip budget: ${bytes}/${DIRECT_GZIP_BUDGET_BYTES} bytes.\n`,
52
+ )
53
+ }
54
+ await publishArtifact(mode, join(outputDirectory, 'index.js'))
46
55
  }
47
56
  } catch (error) {
48
57
  process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
@@ -51,3 +60,27 @@ if (mode !== 'direct' && mode !== 'isolated') {
51
60
  await rm(directory, { recursive: true, force: true })
52
61
  }
53
62
  }
63
+
64
+ async function publishArtifact(runtimeMode, source) {
65
+ const destination = workerArtifactPath(runtimeMode)
66
+ const destinationDirectory = fileURLToPath(new URL(`../worker/${runtimeMode}`, import.meta.url))
67
+ await removeGeneratedDirectory(destinationDirectory)
68
+ await mkdir(destinationDirectory, { recursive: true, mode: 0o755 })
69
+ const bundle = (await readFile(source, 'utf8'))
70
+ .replace(/\n\/\/# sourceMappingURL=index\.js\.map\s*$/u, '\n')
71
+ await writeFile(destination, bundle, { encoding: 'utf8', mode: 0o644, flag: 'wx' })
72
+ process.stdout.write(`Wrote ${runtimeMode} Worker artifact to ${destination}.\n`)
73
+ }
74
+
75
+ async function removeGeneratedDirectory(directory) {
76
+ try {
77
+ const entry = await lstat(directory)
78
+ if (!entry.isDirectory() || entry.isSymbolicLink()) {
79
+ throw new Error(`Refusing to replace non-directory Worker artifact path: ${directory}`)
80
+ }
81
+ } catch (error) {
82
+ if (error?.code === 'ENOENT') return
83
+ throw error
84
+ }
85
+ await rm(directory, { recursive: true })
86
+ }
package/scripts/cli.d.mts CHANGED
@@ -35,5 +35,6 @@ export function runInstaller(options?: {
35
35
  }
36
36
  stderr?: import('node:stream').Writable
37
37
  stdout?: import('node:stream').Writable
38
+ verbose?: boolean
38
39
  wranglerRunner?: typeof import('./install.mjs').executeWrangler
39
40
  }): Promise<unknown>
package/scripts/cli.mjs CHANGED
@@ -28,10 +28,14 @@ export class InstallInterruptedError extends InstallCancelledError {
28
28
  }
29
29
 
30
30
  export function parseCommand(args) {
31
- if (args.length === 1 && (args[0] === 'install' || args[0] === 'upgrade')) return args[0]
31
+ if (args.filter(arg => arg === '--verbose').length > 1) throw usageError()
32
+ const operation = args.filter(arg => arg !== '--verbose')
33
+ if (operation.length === 1 && (operation[0] === 'install' || operation[0] === 'upgrade')) {
34
+ return operation[0]
35
+ }
32
36
  if (args.length === 0 || (args.length === 1 && ['--help', '-h'].includes(args[0]))) return 'help'
33
37
  if (args.length === 1 && ['--version', '-v'].includes(args[0])) return 'version'
34
- throw new Error('Usage: dsh-edge <install|upgrade>')
38
+ throw usageError()
35
39
  }
36
40
 
37
41
  export function createInstallerUi(
@@ -49,6 +53,7 @@ export function createInstallerUi(
49
53
  const note = (message, title) => output === undefined
50
54
  ? clack.note(message, title)
51
55
  : clack.note(message, title, { output })
56
+ let deploymentSpinner
52
57
  return {
53
58
  intro: message => log(clack.intro, message),
54
59
  step: message => log(clack.log.step, message),
@@ -149,6 +154,25 @@ export function createInstallerUi(
149
154
  signal,
150
155
  })))
151
156
  },
157
+ deploymentStart(message) {
158
+ deploymentSpinner = clack.spinner(withOutput({ indicator: 'timer', signal }))
159
+ deploymentSpinner.start(message)
160
+ },
161
+ deploymentFinish(succeeded) {
162
+ if (deploymentSpinner === undefined) return
163
+ if (succeeded) deploymentSpinner.stop('Cloudflare accepted the Worker upload.')
164
+ else deploymentSpinner.error('Cloudflare did not accept the Worker upload.')
165
+ deploymentSpinner = undefined
166
+ },
167
+ failedDeployment(result) {
168
+ note([
169
+ `Worker: ${result.workerName}`,
170
+ 'Status: the Worker was not installed.',
171
+ `Temporary account claim URL: ${result.claimUrl}`,
172
+ '',
173
+ 'You may claim the temporary account within 60 minutes, then retry the installation.',
174
+ ].join('\n'), 'Installation did not complete')
175
+ },
152
176
  cleanupFailure(message) {
153
177
  log(clack.log.warn, message)
154
178
  },
@@ -189,7 +213,7 @@ export function createInstallerUi(
189
213
  '2. Enter the owner access key when prompted.',
190
214
  )
191
215
  }
192
- note(lines.join('\n'), command === 'upgrade' ? 'Worker upgraded' : 'Worker uploaded')
216
+ note(lines.join('\n'), command === 'upgrade' ? 'dsh-edge upgraded' : 'dsh-edge installed')
193
217
  log(clack.outro, command === 'upgrade' ? 'Upgrade handoff complete.' : 'Installation handoff complete.')
194
218
  },
195
219
  }
@@ -224,6 +248,7 @@ function requireAnswer(value) {
224
248
 
225
249
  export async function runInstaller({
226
250
  command = 'install',
251
+ verbose = false,
227
252
  install,
228
253
  installEdgeImpl = installEdge,
229
254
  installerUi,
@@ -271,6 +296,7 @@ export async function runInstaller({
271
296
  ...options,
272
297
  runWrangler: (args, runOptions) => wranglerRunner(args, {
273
298
  ...runOptions,
299
+ forwardOutput: verbose || (runOptions?.forwardOutput ?? runOptions?.interactive ?? false),
274
300
  stderrDestination: stderrBoundary.output,
275
301
  stdoutDestination: stdoutBoundary.output,
276
302
  }),
@@ -457,16 +483,21 @@ function createInstallerOutput(destination, onFailure) {
457
483
  }
458
484
 
459
485
  async function main() {
460
- const command = parseCommand(process.argv.slice(2))
486
+ const args = process.argv.slice(2)
487
+ const command = parseCommand(args)
461
488
  if (command === 'help') {
462
- process.stdout.write('Usage: dsh-edge <install|upgrade>\n\nCommands:\n install Create or update an instance\n upgrade Upgrade an existing instance without deleting its data\n')
489
+ process.stdout.write('Usage: dsh-edge <install|upgrade> [--verbose]\n\nCommands:\n install Create or update an instance\n upgrade Upgrade an existing instance without deleting its data\n\nOptions:\n --verbose Show Wrangler deployment output\n')
463
490
  return
464
491
  }
465
492
  if (command === 'version') {
466
493
  process.stdout.write(`${edgePackage.version}\n`)
467
494
  return
468
495
  }
469
- await runInstaller({ command })
496
+ await runInstaller({ command, verbose: args.includes('--verbose') })
497
+ }
498
+
499
+ function usageError() {
500
+ return new Error('Usage: dsh-edge <install|upgrade> [--verbose]')
470
501
  }
471
502
 
472
503
  if (process.argv[1] !== undefined
@@ -32,6 +32,9 @@ export interface InstallerUi {
32
32
  paid: boolean
33
33
  }): Promise<boolean>
34
34
  acceptTemporaryTerms(): Promise<boolean>
35
+ deploymentStart?(message: string): void
36
+ deploymentFinish?(succeeded: boolean): void
37
+ failedDeployment?(result: { claimUrl: string; workerName: string }): void
35
38
  cleanupFailure(message: string): void
36
39
  recovery(result: InstallRecovery): void
37
40
  outputFailureRecovery(
@@ -53,14 +56,12 @@ export interface InstallResult {
53
56
  versionId?: string
54
57
  account?: CloudflareAccount
55
58
  claimUrl?: string
56
- deploymentId: string
57
59
  mode: RuntimeMode
58
60
  ownerSecret: string
59
61
  temporary: boolean
60
62
  workerName: string
61
63
  }
62
64
 
63
- export const DEPLOYMENT_ID_DEFINE: string
64
65
  export const DEFAULT_WORKER_NAME: string
65
66
  export const LOGIN_PROFILE: string
66
67
  export const RUNTIME_MODES: Readonly<Record<RuntimeMode, Readonly<{
@@ -85,7 +86,6 @@ export function generateOwnerSecret(): string
85
86
  export function wranglerEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
86
87
  export function unauthenticatedEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
87
88
  export function wranglerDeployArgs(options: {
88
- deploymentId: string
89
89
  mode: RuntimeMode
90
90
  workerName: string
91
91
  secretsFile: string
@@ -127,7 +127,6 @@ export function installEdge(options: {
127
127
  capture?: boolean
128
128
  signal?: AbortSignal
129
129
  }) => Promise<CommandResult>
130
- deploymentId?: string
131
130
  environment?: NodeJS.ProcessEnv
132
131
  createTemporaryDirectory?: () => Promise<string>
133
132
  removePath?: typeof import('node:fs/promises').rm
@@ -138,8 +137,9 @@ export function wranglerProcessInvocation(args: string[], options?: {
138
137
  wranglerCli?: string
139
138
  }): { command: string; args: string[] }
140
139
  export function executeWrangler(args: string[], options?: {
141
- environment?: NodeJS.ProcessEnv
142
- interactive?: boolean
140
+ environment?: NodeJS.ProcessEnv
141
+ interactive?: boolean
142
+ forwardOutput?: boolean
143
143
  capture?: boolean
144
144
  forceKillAfterDelay?: number
145
145
  invocation?: { command: string; args: string[] }
@@ -1,4 +1,4 @@
1
- import { randomBytes, randomUUID } from 'node:crypto'
1
+ import { randomBytes } from 'node:crypto'
2
2
  import { execFile } from 'node:child_process'
3
3
  import { chmod, mkdtemp, open, readFile, rm, writeFile } from 'node:fs/promises'
4
4
  import { createRequire } from 'node:module'
@@ -7,9 +7,8 @@ import { join } from 'node:path'
7
7
  import { setTimeout as sleep } from 'node:timers/promises'
8
8
  import { fileURLToPath } from 'node:url'
9
9
  import { execa } from 'execa'
10
- import { writeModeWranglerConfig } from './wrangler-config.mjs'
10
+ import { writePrebuiltModeWranglerConfig } from './wrangler-config.mjs'
11
11
 
12
- export const DEPLOYMENT_ID_DEFINE = '__DSH_EDGE_DEPLOYMENT_ID__'
13
12
  export const DEFAULT_WORKER_NAME = 'dsh-edge'
14
13
  export const LOGIN_PROFILE = 'dsh-edge-install'
15
14
  export const RUNTIME_MODES = Object.freeze({
@@ -207,7 +206,6 @@ export function unauthenticatedEnvironment(environment = process.env) {
207
206
 
208
207
  /** Build an exact, secret-free Wrangler deployment command. */
209
208
  export function wranglerDeployArgs({
210
- deploymentId,
211
209
  mode,
212
210
  workerName,
213
211
  secretsFile,
@@ -227,8 +225,6 @@ export function wranglerDeployArgs({
227
225
  workerName,
228
226
  '--config',
229
227
  configFile,
230
- '--define',
231
- `${DEPLOYMENT_ID_DEFINE}:${JSON.stringify(deploymentId)}`,
232
228
  '--secrets-file',
233
229
  secretsFile,
234
230
  ]
@@ -286,7 +282,6 @@ export async function installEdge({
286
282
  command = 'install',
287
283
  ui,
288
284
  runWrangler = executeWrangler,
289
- deploymentId = randomUUID(),
290
285
  environment = process.env,
291
286
  createTemporaryDirectory = createPrivateTemporaryDirectory,
292
287
  removePath = rm,
@@ -391,13 +386,16 @@ export async function installEdge({
391
386
  const secretsFile = join(temporaryDirectory, 'secrets.json')
392
387
  const configFile = join(temporaryDirectory, 'wrangler.json')
393
388
  const outputFile = join(temporaryDirectory, 'wrangler-output.ndjson')
394
- await writeModeWranglerConfig(mode, configFile)
389
+ await writePrebuiltModeWranglerConfig(mode, configFile)
395
390
  await writeFile(secretsFile, JSON.stringify({
396
391
  DEEPSEEK_API_KEY: deepSeekKey,
397
392
  DSH_EDGE_ACCESS_KEY: ownerSecret,
398
393
  }), { encoding: 'utf8', mode: 0o600, flag: 'wx' })
399
394
 
400
- ui.step(command === 'upgrade' ? 'Upgrading the Worker…' : 'Installing the Worker…')
395
+ const deploymentMessage = command === 'upgrade'
396
+ ? 'Uploading the tested Worker release…'
397
+ : 'Installing the tested Worker release…'
398
+ ui.deploymentStart?.(deploymentMessage)
401
399
  const commandEnvironment = temporary
402
400
  ? unauthenticatedEnvironment(environment)
403
401
  : accountEnvironment(profileEnvironment ?? environment, account.id)
@@ -406,10 +404,10 @@ export async function installEdge({
406
404
  commandEnvironment.WRANGLER_OUTPUT_FILE_PATH = outputFile
407
405
  commandEnvironment.FORCE_COLOR = '0'
408
406
  let deployResult
407
+ let deploySucceeded = false
409
408
  let credentialCleanupError
410
409
  try {
411
410
  deployResult = await runWrangler(wranglerDeployArgs({
412
- deploymentId,
413
411
  mode,
414
412
  workerName,
415
413
  secretsFile,
@@ -419,26 +417,30 @@ export async function installEdge({
419
417
  }), {
420
418
  environment: commandEnvironment,
421
419
  interactive: true,
420
+ forwardOutput: false,
422
421
  capture: true,
423
422
  signal,
424
423
  })
424
+ deploySucceeded = deployResult.status === 0
425
425
  } finally {
426
+ ui.deploymentFinish?.(deploySucceeded)
426
427
  try {
427
428
  await removePath(secretsFile, { force: true })
428
429
  } catch (error) {
429
430
  credentialCleanupError = error
430
431
  }
431
432
  }
433
+ const claimUrl = temporary
434
+ ? parseClaimUrl(`${deployResult.stdout}\n${deployResult.stderr}`)
435
+ : undefined
432
436
  if (deployResult.status !== 0) {
437
+ if (claimUrl !== undefined) ui.failedDeployment?.({ claimUrl, workerName })
433
438
  const cleanupDetail = credentialCleanupError === undefined
434
439
  ? ''
435
440
  : `\nTemporary credential cleanup also failed: ${describeError(credentialCleanupError)}`
436
441
  throw new Error(`${formatDeployFailure(mode, deployResult)}${cleanupDetail}`)
437
442
  }
438
443
 
439
- const claimUrl = temporary
440
- ? parseClaimUrl(`${deployResult.stdout}\n${deployResult.stderr}`)
441
- : undefined
442
444
  const recoverUploadedWorker = () => {
443
445
  const recovery = { claimUrl, ownerSecret, workerName }
444
446
  if (deployResult.outputFailure !== undefined) {
@@ -483,7 +485,6 @@ export async function installEdge({
483
485
  ...deployment,
484
486
  account,
485
487
  claimUrl,
486
- deploymentId,
487
488
  mode,
488
489
  ownerSecret,
489
490
  temporary,
@@ -535,6 +536,7 @@ export function wranglerProcessInvocation(args, {
535
536
  export async function executeWrangler(args, {
536
537
  environment = wranglerEnvironment(),
537
538
  interactive = false,
539
+ forwardOutput = interactive,
538
540
  capture = true,
539
541
  forceKillAfterDelay = 2_000,
540
542
  invocation = wranglerProcessInvocation(args),
@@ -576,21 +578,21 @@ export async function executeWrangler(args, {
576
578
  }
577
579
  const stdoutTerminal = createTerminalSanitizer()
578
580
  const stderrTerminal = createTerminalSanitizer()
579
- stdoutForwarder = interactive && child.stdout
581
+ stdoutForwarder = forwardOutput && child.stdout
580
582
  ? createOutputForwarder(child.stdout, stdoutDestination, error => failOutput('stdout', error))
581
583
  : undefined
582
- stderrForwarder = interactive && child.stderr
584
+ stderrForwarder = forwardOutput && child.stderr
583
585
  ? createOutputForwarder(child.stderr, stderrDestination, error => failOutput('stderr', error))
584
586
  : undefined
585
587
  child.stdout?.setEncoding('utf8')
586
588
  child.stderr?.setEncoding('utf8')
587
589
  child.stdout?.on('data', (chunk) => {
588
- const terminalChunk = interactive ? stdoutTerminal.push(chunk) : ''
590
+ const terminalChunk = forwardOutput ? stdoutTerminal.push(chunk) : ''
589
591
  if (terminalChunk !== '') stdoutForwarder?.write(terminalChunk)
590
592
  if (capture) stdout = appendBounded(stdout, chunk)
591
593
  })
592
594
  child.stderr?.on('data', (chunk) => {
593
- const terminalChunk = interactive ? stderrTerminal.push(chunk) : ''
595
+ const terminalChunk = forwardOutput ? stderrTerminal.push(chunk) : ''
594
596
  if (terminalChunk !== '') stderrForwarder?.write(terminalChunk)
595
597
  if (capture) stderr = appendBounded(stderr, chunk)
596
598
  })
@@ -937,7 +939,22 @@ function describeError(error) {
937
939
  }
938
940
 
939
941
  function formatDeployFailure(mode, result) {
940
- const failure = commandFailure('Cloudflare installation failed', result)
942
+ const diagnostic = stripAnsi(result.stderr || result.stdout).trim()
943
+ const code = /\[code:\s*(\d+)\]/u.exec(diagnostic)?.[1]
944
+ let detail
945
+ if (code === '10021') {
946
+ detail = 'Cloudflare rejected the Worker module during validation (code 10021).'
947
+ } else if (code === '10027') {
948
+ detail = 'Cloudflare rejected the Worker because it exceeds the account size limit (code 10027).'
949
+ } else {
950
+ const firstError = diagnostic.split(/\r?\n/u)
951
+ .map(line => line.trim())
952
+ .find(line => line !== '' && !line.startsWith('(node:'))
953
+ detail = firstError === undefined
954
+ ? 'Cloudflare did not accept the Worker upload.'
955
+ : `Cloudflare did not accept the Worker upload${code === undefined ? '' : ` (code ${code})`}: ${firstError}`
956
+ }
957
+ const failure = `${detail} Run the command again with --verbose to inspect Wrangler output.`
941
958
  if (mode !== 'isolated') return failure
942
959
  return `${failure}\nThe isolated runtime requires the Workers Paid plan (starting at $5/month). `
943
960
  + 'Enable Workers Paid for this account or install the Free direct runtime.'
@@ -1,7 +1,8 @@
1
- import { readFileSync } from 'node:fs'
1
+ import { readFileSync, writeFileSync } from 'node:fs'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
- const legalFiles = ['LICENSE', 'THIRD_PARTY_NOTICES.md']
4
+ const upstreamCopyright = 'Copyright (c) 2026 DeepSeek'
5
+ const edgeCopyright = 'Copyright (c) 2026 pawaca'
5
6
 
6
7
  function paths(name) {
7
8
  return {
@@ -10,15 +11,72 @@ function paths(name) {
10
11
  }
11
12
  }
12
13
 
14
+ function renderLicense() {
15
+ const upstream = readFileSync(paths('LICENSE').source, 'utf8')
16
+ if (!upstream.includes(upstreamCopyright)) {
17
+ throw new Error('repository-root LICENSE no longer contains the expected DeepSeek copyright')
18
+ }
19
+ return upstream.replace(upstreamCopyright, edgeCopyright)
20
+ }
21
+
22
+ function renderNotices() {
23
+ const upstreamLicense = readFileSync(paths('LICENSE').source, 'utf8').trim()
24
+ const repositoryNotices = readFileSync(paths('THIRD_PARTY_NOTICES.md').source, 'utf8')
25
+ const inventoryStart = repositoryNotices.indexOf('## Vendored source')
26
+ if (inventoryStart < 0) {
27
+ throw new Error('repository-root THIRD_PARTY_NOTICES.md has no dependency inventory')
28
+ }
29
+ const upstreamInventory = repositoryNotices
30
+ .slice(inventoryStart)
31
+ .replace(/^## /gmu, '### ')
32
+ .trim()
33
+
34
+ return `<!-- Generated by apps/dsh-edge/scripts/legal-files.mjs — do not edit by hand.
35
+ Run \`pnpm --filter dsh-edge run legal:write\` to regenerate. -->
36
+
37
+ # Third-Party Notices
38
+
39
+ \`dsh-edge\` is an independent community project and is not affiliated with or endorsed by DeepSeek.
40
+
41
+ ## DeepSeek Harness
42
+
43
+ \`dsh-edge\` includes and adapts DeepSeek Harness. DeepSeek Harness retains its upstream copyright and MIT license:
44
+
45
+ \`\`\`text
46
+ ${upstreamLicense}
47
+ \`\`\`
48
+
49
+ ## DeepSeek Harness dependency notices
50
+
51
+ The following inventory is reproduced from the DeepSeek Harness source distribution. Each project remains under its own license; nothing in this file changes those terms.
52
+
53
+ ${upstreamInventory}
54
+ `
55
+ }
56
+
57
+ function expectedFiles() {
58
+ return new Map([
59
+ ['LICENSE', renderLicense()],
60
+ ['THIRD_PARTY_NOTICES.md', renderNotices()],
61
+ ])
62
+ }
63
+
13
64
  function verify() {
14
- for (const name of legalFiles) {
15
- const { source, target } = paths(name)
16
- if (!readFileSync(target).equals(readFileSync(source))) {
17
- throw new Error(`${name} differs from the repository-root authority`)
65
+ for (const [name, expected] of expectedFiles()) {
66
+ const { target } = paths(name)
67
+ if (readFileSync(target, 'utf8') !== expected) {
68
+ throw new Error(`${name} is stale; run \`pnpm --filter dsh-edge run legal:write\``)
18
69
  }
19
70
  }
20
71
  }
21
72
 
73
+ function write() {
74
+ for (const [name, content] of expectedFiles()) {
75
+ writeFileSync(paths(name).target, content)
76
+ }
77
+ }
78
+
22
79
  const action = process.argv[2]
23
80
  if (action === 'verify') verify()
24
- else throw new Error('Usage: node scripts/legal-files.mjs verify')
81
+ else if (action === 'write') write()
82
+ else throw new Error('Usage: node scripts/legal-files.mjs <verify|write>')
@@ -0,0 +1,47 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdtemp, rm, stat } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { unstable_dev } from 'wrangler'
6
+ import edgePackage from '../package.json' with { type: 'json' }
7
+ import {
8
+ workerArtifactPath,
9
+ writePrebuiltModeWranglerConfig,
10
+ } from './wrangler-config.mjs'
11
+
12
+ const ACCESS_KEY = 'packed-installer-owner-key-32-bytes'
13
+ const directory = await mkdtemp(join(tmpdir(), 'dsh-edge-installed-smoke-'))
14
+ let worker
15
+
16
+ try {
17
+ for (const mode of ['direct', 'isolated']) {
18
+ const artifact = workerArtifactPath(mode)
19
+ assert.equal((await stat(artifact)).isFile(), true, `missing ${mode} Worker artifact`)
20
+ }
21
+
22
+ const configFile = join(directory, 'wrangler.json')
23
+ await writePrebuiltModeWranglerConfig('direct', configFile)
24
+ worker = await unstable_dev(workerArtifactPath('direct'), {
25
+ config: configFile,
26
+ vars: {
27
+ DEEPSEEK_API_KEY: 'packed-installer-test-key',
28
+ DSH_EDGE_ACCESS_KEY: ACCESS_KEY,
29
+ },
30
+ logLevel: 'error',
31
+ experimental: {
32
+ disableExperimentalWarning: true,
33
+ showInteractiveDevSession: false,
34
+ watch: false,
35
+ },
36
+ })
37
+ const response = await worker.fetch('http://dsh-edge.test/api/health')
38
+ assert.equal(response.status, 200)
39
+ const health = await response.json()
40
+ assert.equal(health.service, 'dsh-edge')
41
+ assert.equal(health.shell, 'just-bash-direct')
42
+ assert.equal(health.deploymentId, `dsh-edge@${edgePackage.version}/direct`)
43
+ process.stdout.write('Installed direct Worker artifact started successfully.\n')
44
+ } finally {
45
+ await worker?.stop()
46
+ await rm(directory, { recursive: true, force: true })
47
+ }