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.
- package/LICENSE +1 -1
- package/README.i18n.yaml +2 -2
- package/README.md +6 -4
- package/README.zh.md +6 -4
- package/THIRD_PARTY_NOTICES.md +42 -14
- package/dist/index.html +1 -1
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +7 -7
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +61 -61
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +14 -14
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +287 -287
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +10 -10
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +11 -11
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +15 -15
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +8 -8
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +27 -27
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +8 -8
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +31 -31
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +23 -23
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +19 -19
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +29 -29
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +7 -7
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +64 -64
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +183 -183
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +44 -44
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +24 -24
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +79 -79
- package/dist/plugins/{@deepseek-ai/dsh-client-ui-edge → dsh-edge-client-ui}/client.js +12 -12
- package/package.json +26 -23
- package/scripts/assemble-web.mjs +1 -1
- package/scripts/bundle.mjs +41 -8
- package/scripts/cli.d.mts +1 -0
- package/scripts/cli.mjs +37 -6
- package/scripts/install.d.mts +6 -6
- package/scripts/install.mjs +36 -19
- package/scripts/legal-files.mjs +65 -7
- package/scripts/smoke-installed.mjs +47 -0
- package/scripts/verify-packed.d.mts +24 -0
- package/scripts/verify-packed.mjs +85 -23
- package/scripts/wrangler-config.d.mts +19 -2
- package/scripts/wrangler-config.mjs +68 -27
- package/worker/direct/index.js +2347 -0
- package/worker/isolated/index.js +44491 -0
- package/src/agent.ts +0 -119
- package/src/auth.ts +0 -352
- package/src/deepseek.ts +0 -231
- package/src/deployment.ts +0 -147
- package/src/direct-shell-core-empty.ts +0 -10
- package/src/direct-shell-protocol.ts +0 -5
- package/src/direct-shell.ts +0 -341
- package/src/do-session-persistence.ts +0 -1064
- package/src/edge-api.ts +0 -796
- package/src/edge-credentials.ts +0 -56
- package/src/edge-remotes.ts +0 -41
- package/src/edge-workspace-store.ts +0 -339
- package/src/http.ts +0 -212
- package/src/index.ts +0 -196
- package/src/instance.ts +0 -1007
- package/src/isolated-direct-shell-unavailable.ts +0 -11
- package/src/protocol.ts +0 -39
- package/src/release.ts +0 -9
- package/src/session-store.ts +0 -1110
- package/src/sse.ts +0 -86
- package/src/web-search.ts +0 -38
- 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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
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-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
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-
|
|
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"
|
package/scripts/assemble-web.mjs
CHANGED
|
@@ -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 = ['
|
|
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.
|
package/scripts/bundle.mjs
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
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.
|
|
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
|
|
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' ? '
|
|
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
|
|
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
|
|
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
|
package/scripts/install.d.mts
CHANGED
|
@@ -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
|
-
|
|
142
|
-
|
|
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[] }
|
package/scripts/install.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomBytes
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 =
|
|
581
|
+
stdoutForwarder = forwardOutput && child.stdout
|
|
580
582
|
? createOutputForwarder(child.stdout, stdoutDestination, error => failOutput('stdout', error))
|
|
581
583
|
: undefined
|
|
582
|
-
stderrForwarder =
|
|
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 =
|
|
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 =
|
|
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
|
|
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.'
|
package/scripts/legal-files.mjs
CHANGED
|
@@ -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
|
|
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
|
|
15
|
-
const {
|
|
16
|
-
if (
|
|
17
|
-
throw new Error(`${name}
|
|
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
|
|
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
|
+
}
|