dsh-edge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +183 -0
- package/README.zh.md +183 -0
- package/THIRD_PARTY_NOTICES.md +199 -0
- package/dist/_headers +3 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/index-C-1AiF3k.js +112 -0
- package/dist/assets/index-CSGf6Qzd.css +1 -0
- package/dist/assets/langs/c-BIGW1oBm.js +2 -0
- package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
- package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
- package/dist/assets/langs/css-CLj8gQPS.js +2 -0
- package/dist/assets/langs/go-C27-OAKa.js +2 -0
- package/dist/assets/langs/html-CfGypltT.js +2 -0
- package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
- package/dist/assets/langs/java-CylS5w8V.js +2 -0
- package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
- package/dist/assets/langs/less-B1dDrJ26.js +2 -0
- package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
- package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
- package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
- package/dist/assets/langs/php-D-uCvoST.js +2 -0
- package/dist/assets/langs/python-B6aJPvgy.js +2 -0
- package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
- package/dist/assets/langs/rust-B1yitclQ.js +2 -0
- package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
- package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
- package/dist/assets/langs/swift-D82vCrfD.js +2 -0
- package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
- package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
- package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
- package/dist/assets/vendor-Cjbwl5VI.js +411 -0
- package/dist/assets/vendor-CjyC-hUb.css +1 -0
- package/dist/favicon.svg +8 -0
- package/dist/index.html +35 -0
- package/dist/manifest.webmanifest +16 -0
- package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
- package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
- package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
- package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
- package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
- package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
- package/package.json +97 -0
- package/scripts/assemble-web.mjs +230 -0
- package/scripts/bundle-size.d.mts +2 -0
- package/scripts/bundle-size.mjs +26 -0
- package/scripts/bundle.mjs +53 -0
- package/scripts/cli.d.mts +39 -0
- package/scripts/cli.mjs +496 -0
- package/scripts/install.d.mts +149 -0
- package/scripts/install.mjs +989 -0
- package/scripts/legal-files.mjs +24 -0
- package/scripts/verify-packed.mjs +32 -0
- package/scripts/wrangler-config.d.mts +18 -0
- package/scripts/wrangler-config.mjs +84 -0
- package/src/agent.ts +119 -0
- package/src/auth.ts +352 -0
- package/src/deepseek.ts +231 -0
- package/src/deployment.ts +147 -0
- package/src/direct-shell-core-empty.ts +10 -0
- package/src/direct-shell-protocol.ts +5 -0
- package/src/direct-shell.ts +341 -0
- package/src/do-session-persistence.ts +1064 -0
- package/src/edge-api.ts +796 -0
- package/src/edge-credentials.ts +56 -0
- package/src/edge-remotes.ts +41 -0
- package/src/edge-workspace-store.ts +339 -0
- package/src/http.ts +212 -0
- package/src/index.ts +196 -0
- package/src/instance.ts +1007 -0
- package/src/isolated-direct-shell-unavailable.ts +11 -0
- package/src/protocol.ts +39 -0
- package/src/release.ts +9 -0
- package/src/session-store.ts +1110 -0
- package/src/sse.ts +86 -0
- package/src/web-search.ts +38 -0
- package/src/workspace.ts +270 -0
- package/wrangler.jsonc +56 -0
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as prompt from '@clack/prompts'
|
|
4
|
+
import { realpathSync, writeSync } from 'node:fs'
|
|
5
|
+
import { Writable } from 'node:stream'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import edgePackage from '../package.json' with { type: 'json' }
|
|
8
|
+
import {
|
|
9
|
+
executeWrangler,
|
|
10
|
+
InstallCancelledError,
|
|
11
|
+
InstallerOutputError,
|
|
12
|
+
installEdge,
|
|
13
|
+
} from './install.mjs'
|
|
14
|
+
|
|
15
|
+
const INTERRUPT_EXIT_CODES = new Map([
|
|
16
|
+
['SIGHUP', 129],
|
|
17
|
+
['SIGINT', 130],
|
|
18
|
+
['SIGTERM', 143],
|
|
19
|
+
])
|
|
20
|
+
const OUTPUT_DRAIN_TIMEOUT_MS = 1_000
|
|
21
|
+
|
|
22
|
+
export class InstallInterruptedError extends InstallCancelledError {
|
|
23
|
+
constructor(signal) {
|
|
24
|
+
super()
|
|
25
|
+
this.exitCode = INTERRUPT_EXIT_CODES.get(signal)
|
|
26
|
+
this.signal = signal
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseCommand(args) {
|
|
31
|
+
if (args.length === 1 && (args[0] === 'install' || args[0] === 'upgrade')) return args[0]
|
|
32
|
+
if (args.length === 0 || (args.length === 1 && ['--help', '-h'].includes(args[0]))) return 'help'
|
|
33
|
+
if (args.length === 1 && ['--version', '-v'].includes(args[0])) return 'version'
|
|
34
|
+
throw new Error('Usage: dsh-edge <install|upgrade>')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createInstallerUi(
|
|
38
|
+
clack = prompt,
|
|
39
|
+
signal,
|
|
40
|
+
writeDescriptor = writeSync,
|
|
41
|
+
output,
|
|
42
|
+
writeRecovery,
|
|
43
|
+
command = 'install',
|
|
44
|
+
) {
|
|
45
|
+
const withOutput = options => output === undefined ? options : { ...options, output }
|
|
46
|
+
const log = (writer, message) => output === undefined
|
|
47
|
+
? writer(message)
|
|
48
|
+
: writer(message, { output })
|
|
49
|
+
const note = (message, title) => output === undefined
|
|
50
|
+
? clack.note(message, title)
|
|
51
|
+
: clack.note(message, title, { output })
|
|
52
|
+
return {
|
|
53
|
+
intro: message => log(clack.intro, message),
|
|
54
|
+
step: message => log(clack.log.step, message),
|
|
55
|
+
async selectRuntime() {
|
|
56
|
+
return await requireAnswer(await clack.select(withOutput({
|
|
57
|
+
message: 'Choose a runtime',
|
|
58
|
+
signal,
|
|
59
|
+
initialValue: 'direct',
|
|
60
|
+
options: [
|
|
61
|
+
{
|
|
62
|
+
value: 'direct',
|
|
63
|
+
label: 'Free — Direct Shell',
|
|
64
|
+
hint: 'recommended; runs on Workers Free',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
value: 'isolated',
|
|
68
|
+
label: 'Isolated — Dynamic Worker',
|
|
69
|
+
hint: 'requires Workers Paid (starting at $5/month)',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
})))
|
|
73
|
+
},
|
|
74
|
+
async selectAccount(choices) {
|
|
75
|
+
return await requireAnswer(await clack.select(withOutput({
|
|
76
|
+
message: 'Choose a Cloudflare account',
|
|
77
|
+
options: choices,
|
|
78
|
+
signal,
|
|
79
|
+
})))
|
|
80
|
+
},
|
|
81
|
+
async workerName(initialValue, validate) {
|
|
82
|
+
return await requireAnswer(await clack.text(withOutput({
|
|
83
|
+
message: 'Worker name',
|
|
84
|
+
initialValue,
|
|
85
|
+
signal,
|
|
86
|
+
validate,
|
|
87
|
+
})))
|
|
88
|
+
},
|
|
89
|
+
async workerConflict(workerName) {
|
|
90
|
+
return await requireAnswer(await clack.select(withOutput({
|
|
91
|
+
message: `${workerName} already exists`,
|
|
92
|
+
signal,
|
|
93
|
+
options: [
|
|
94
|
+
{ value: 'rename', label: 'Choose another name' },
|
|
95
|
+
{ value: 'update', label: 'Update this Worker', hint: 'keeps its Durable Object data' },
|
|
96
|
+
{ value: 'cancel', label: 'Cancel' },
|
|
97
|
+
],
|
|
98
|
+
})))
|
|
99
|
+
},
|
|
100
|
+
async selectOwnerSecretMode() {
|
|
101
|
+
return await requireAnswer(await clack.select(withOutput({
|
|
102
|
+
message: 'Set the owner access key',
|
|
103
|
+
initialValue: 'generate',
|
|
104
|
+
signal,
|
|
105
|
+
options: [
|
|
106
|
+
{ value: 'generate', label: 'Generate a secure key', hint: 'recommended' },
|
|
107
|
+
{ value: 'custom', label: 'Enter my own key' },
|
|
108
|
+
],
|
|
109
|
+
})))
|
|
110
|
+
},
|
|
111
|
+
async ownerSecret(validate) {
|
|
112
|
+
return await requireAnswer(await clack.password(withOutput({
|
|
113
|
+
message: 'Owner access key',
|
|
114
|
+
mask: '•',
|
|
115
|
+
signal,
|
|
116
|
+
validate,
|
|
117
|
+
})))
|
|
118
|
+
},
|
|
119
|
+
async deepSeekKey(validate) {
|
|
120
|
+
return await requireAnswer(await clack.password(withOutput({
|
|
121
|
+
message: 'DeepSeek API key',
|
|
122
|
+
mask: '•',
|
|
123
|
+
signal,
|
|
124
|
+
validate,
|
|
125
|
+
})))
|
|
126
|
+
},
|
|
127
|
+
async confirm(summary) {
|
|
128
|
+
note([
|
|
129
|
+
`Runtime: ${summary.modeLabel}`,
|
|
130
|
+
`Account: ${summary.accountLabel}`,
|
|
131
|
+
`Worker: ${summary.workerName}`,
|
|
132
|
+
`Cost: ${summary.paid ? 'Workers Paid is required' : 'Works on Workers Free'}`,
|
|
133
|
+
...(command === 'upgrade' ? ['Existing Durable Object data is preserved.', 'The entered secrets replace the active values.'] : []),
|
|
134
|
+
].join('\n'), command === 'upgrade' ? 'Upgrade summary' : 'Installation summary')
|
|
135
|
+
return await requireAnswer(await clack.confirm(withOutput({
|
|
136
|
+
message: command === 'upgrade' ? 'Upgrade this instance?' : 'Install this instance?',
|
|
137
|
+
initialValue: true,
|
|
138
|
+
signal,
|
|
139
|
+
})))
|
|
140
|
+
},
|
|
141
|
+
async acceptTemporaryTerms() {
|
|
142
|
+
note([
|
|
143
|
+
'Cloudflare Terms of Service: https://www.cloudflare.com/terms/',
|
|
144
|
+
'Cloudflare Privacy Policy: https://www.cloudflare.com/privacypolicy/',
|
|
145
|
+
].join('\n'), 'Temporary account terms')
|
|
146
|
+
return await requireAnswer(await clack.confirm(withOutput({
|
|
147
|
+
message: 'Accept these terms and create a temporary Cloudflare account?',
|
|
148
|
+
initialValue: false,
|
|
149
|
+
signal,
|
|
150
|
+
})))
|
|
151
|
+
},
|
|
152
|
+
cleanupFailure(message) {
|
|
153
|
+
log(clack.log.warn, message)
|
|
154
|
+
},
|
|
155
|
+
recovery(result) {
|
|
156
|
+
note(recoveryLines(result).join('\n'), 'Worker uploaded — recovery details')
|
|
157
|
+
},
|
|
158
|
+
outputFailureRecovery(result, failedStream) {
|
|
159
|
+
const lines = [
|
|
160
|
+
'',
|
|
161
|
+
'Worker uploaded — recovery details',
|
|
162
|
+
...recoveryLines(result),
|
|
163
|
+
'',
|
|
164
|
+
]
|
|
165
|
+
if (writeRecovery !== undefined) {
|
|
166
|
+
return writeRecovery(failedStream, lines.join('\n'))
|
|
167
|
+
}
|
|
168
|
+
return writeAlternate(writeDescriptor, failedStream, lines)
|
|
169
|
+
},
|
|
170
|
+
success(result) {
|
|
171
|
+
const lines = [
|
|
172
|
+
`URL: ${result.publicUrl}`,
|
|
173
|
+
`Owner access key: ${result.ownerSecret}`,
|
|
174
|
+
]
|
|
175
|
+
if (result.claimUrl !== undefined) {
|
|
176
|
+
lines.push(
|
|
177
|
+
`Claim URL: ${result.claimUrl}`,
|
|
178
|
+
'',
|
|
179
|
+
'Next steps:',
|
|
180
|
+
'1. Claim this temporary account within 60 minutes to keep the Worker and its data.',
|
|
181
|
+
'2. Open the URL above.',
|
|
182
|
+
'3. Enter the owner access key when prompted.',
|
|
183
|
+
)
|
|
184
|
+
} else {
|
|
185
|
+
lines.push(
|
|
186
|
+
'',
|
|
187
|
+
'Next steps:',
|
|
188
|
+
'1. Open the URL above.',
|
|
189
|
+
'2. Enter the owner access key when prompted.',
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
note(lines.join('\n'), command === 'upgrade' ? 'Worker upgraded' : 'Worker uploaded')
|
|
193
|
+
log(clack.outro, command === 'upgrade' ? 'Upgrade handoff complete.' : 'Installation handoff complete.')
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function recoveryLines(result) {
|
|
199
|
+
const lines = [
|
|
200
|
+
`Worker: ${result.workerName}`,
|
|
201
|
+
`Active owner access key: ${result.ownerSecret}`,
|
|
202
|
+
]
|
|
203
|
+
if (result.publicUrl !== undefined) lines.unshift(`URL: ${result.publicUrl}`)
|
|
204
|
+
if (result.claimUrl !== undefined) lines.push(`Claim URL: ${result.claimUrl}`)
|
|
205
|
+
lines.push('Save this key. Wrangler reported a successful upload, but the installer could not complete its handoff.')
|
|
206
|
+
return lines
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function writeAlternate(writeDescriptor, failedStream, lines) {
|
|
210
|
+
const descriptor = failedStream === 'stdout' ? 2 : 1
|
|
211
|
+
try {
|
|
212
|
+
writeDescriptor(descriptor, lines.join('\n'))
|
|
213
|
+
return true
|
|
214
|
+
} catch {
|
|
215
|
+
// The alternate process stream is unavailable too; credential cleanup must continue.
|
|
216
|
+
return false
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function requireAnswer(value) {
|
|
221
|
+
if (prompt.isCancel(value)) throw new InstallCancelledError()
|
|
222
|
+
return value
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export async function runInstaller({
|
|
226
|
+
command = 'install',
|
|
227
|
+
install,
|
|
228
|
+
installEdgeImpl = installEdge,
|
|
229
|
+
installerUi,
|
|
230
|
+
outputDrainTimeoutMs = OUTPUT_DRAIN_TIMEOUT_MS,
|
|
231
|
+
uiFactory = (signal, output, writeRecovery) => createInstallerUi(
|
|
232
|
+
prompt,
|
|
233
|
+
signal,
|
|
234
|
+
writeSync,
|
|
235
|
+
output,
|
|
236
|
+
writeRecovery,
|
|
237
|
+
command,
|
|
238
|
+
),
|
|
239
|
+
runtimeProcess = process,
|
|
240
|
+
stderr = process.stderr,
|
|
241
|
+
stdout = process.stdout,
|
|
242
|
+
wranglerRunner = executeWrangler,
|
|
243
|
+
} = {}) {
|
|
244
|
+
const controller = new AbortController()
|
|
245
|
+
let interruption
|
|
246
|
+
let outputFailure
|
|
247
|
+
let notifyOutputFailure
|
|
248
|
+
const outputFailureDetected = new Promise(resolve => {
|
|
249
|
+
notifyOutputFailure = resolve
|
|
250
|
+
})
|
|
251
|
+
let recoveryStream
|
|
252
|
+
const failOutput = (stream, error) => {
|
|
253
|
+
if (outputFailure !== undefined) return
|
|
254
|
+
outputFailure = new InstallerOutputError(stream, error)
|
|
255
|
+
recoveryStream ??= stream
|
|
256
|
+
notifyOutputFailure()
|
|
257
|
+
controller.abort(outputFailure)
|
|
258
|
+
}
|
|
259
|
+
const stdoutBoundary = createInstallerOutput(stdout, error => failOutput('stdout', error))
|
|
260
|
+
const stderrBoundary = createInstallerOutput(stderr, error => failOutput('stderr', error))
|
|
261
|
+
const cancelBlockedOutput = () => {
|
|
262
|
+
const interruptError = interruption ?? controller.signal.reason
|
|
263
|
+
if (!(interruptError instanceof InstallInterruptedError)) return
|
|
264
|
+
const stdoutBlocked = stdoutBoundary.cancel()
|
|
265
|
+
const stderrBlocked = stderrBoundary.cancel()
|
|
266
|
+
recoveryStream ??= stderrBlocked && !stdoutBlocked ? 'stderr' : 'stdout'
|
|
267
|
+
interruptError.outputFailureStream = recoveryStream
|
|
268
|
+
}
|
|
269
|
+
controller.signal.addEventListener('abort', cancelBlockedOutput)
|
|
270
|
+
const installOperation = install ?? (options => installEdgeImpl({
|
|
271
|
+
...options,
|
|
272
|
+
runWrangler: (args, runOptions) => wranglerRunner(args, {
|
|
273
|
+
...runOptions,
|
|
274
|
+
stderrDestination: stderrBoundary.output,
|
|
275
|
+
stdoutDestination: stdoutBoundary.output,
|
|
276
|
+
}),
|
|
277
|
+
}))
|
|
278
|
+
const writeRecovery = (failedStream, value) => {
|
|
279
|
+
const alternateBoundary = failedStream === 'stdout' ? stderrBoundary : stdoutBoundary
|
|
280
|
+
return alternateBoundary.write(value)
|
|
281
|
+
}
|
|
282
|
+
const baseUi = installerUi
|
|
283
|
+
?? uiFactory(controller.signal, stdoutBoundary.output, writeRecovery)
|
|
284
|
+
let lastRecovery
|
|
285
|
+
let alternateRecoveryAttempted = false
|
|
286
|
+
let alternateRecoveryPending
|
|
287
|
+
const deliverAlternateRecovery = (result, stream) => {
|
|
288
|
+
if (alternateRecoveryAttempted) return
|
|
289
|
+
alternateRecoveryAttempted = true
|
|
290
|
+
try {
|
|
291
|
+
alternateRecoveryPending = Promise.resolve(baseUi.outputFailureRecovery(result, stream))
|
|
292
|
+
.then(delivered => delivered !== false, () => false)
|
|
293
|
+
} catch {
|
|
294
|
+
alternateRecoveryPending = Promise.resolve()
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const ui = {
|
|
298
|
+
...baseUi,
|
|
299
|
+
recovery(result) {
|
|
300
|
+
lastRecovery = result
|
|
301
|
+
if (recoveryStream === undefined) baseUi.recovery(result)
|
|
302
|
+
else deliverAlternateRecovery(result, recoveryStream)
|
|
303
|
+
},
|
|
304
|
+
outputFailureRecovery(result, stream) {
|
|
305
|
+
lastRecovery = result
|
|
306
|
+
recoveryStream ??= stream
|
|
307
|
+
deliverAlternateRecovery(result, stream)
|
|
308
|
+
},
|
|
309
|
+
success(result) {
|
|
310
|
+
lastRecovery = result
|
|
311
|
+
if (recoveryStream === undefined) baseUi.success(result)
|
|
312
|
+
else deliverAlternateRecovery(result, recoveryStream)
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
const handlers = new Map([...INTERRUPT_EXIT_CODES].map(([signal]) => [
|
|
316
|
+
signal,
|
|
317
|
+
() => {
|
|
318
|
+
interruption ??= new InstallInterruptedError(signal)
|
|
319
|
+
if (controller.signal.aborted) cancelBlockedOutput()
|
|
320
|
+
else controller.abort(interruption)
|
|
321
|
+
},
|
|
322
|
+
]))
|
|
323
|
+
for (const [signal, handler] of handlers) runtimeProcess.on(signal, handler)
|
|
324
|
+
let result
|
|
325
|
+
let installError
|
|
326
|
+
try {
|
|
327
|
+
try {
|
|
328
|
+
result = await installOperation({ command, ui, signal: controller.signal })
|
|
329
|
+
} catch (error) {
|
|
330
|
+
installError = error
|
|
331
|
+
}
|
|
332
|
+
if (outputFailure === undefined) {
|
|
333
|
+
await Promise.race([
|
|
334
|
+
Promise.all([stdoutBoundary.settled(), stderrBoundary.settled()]),
|
|
335
|
+
outputFailureDetected,
|
|
336
|
+
])
|
|
337
|
+
}
|
|
338
|
+
if (recoveryStream !== undefined && lastRecovery !== undefined) {
|
|
339
|
+
deliverAlternateRecovery(lastRecovery, recoveryStream)
|
|
340
|
+
}
|
|
341
|
+
const outputsSettled = Promise.all([stdoutBoundary.settled(), stderrBoundary.settled()])
|
|
342
|
+
if (outputFailure !== undefined) {
|
|
343
|
+
let timeout
|
|
344
|
+
const drainTimedOut = new Promise(resolve => {
|
|
345
|
+
timeout = setTimeout(() => resolve(true), outputDrainTimeoutMs)
|
|
346
|
+
})
|
|
347
|
+
try {
|
|
348
|
+
const timedOut = await Promise.race([
|
|
349
|
+
outputsSettled.then(() => false),
|
|
350
|
+
drainTimedOut,
|
|
351
|
+
])
|
|
352
|
+
if (timedOut) {
|
|
353
|
+
stdoutBoundary.cancel()
|
|
354
|
+
stderrBoundary.cancel()
|
|
355
|
+
await outputsSettled
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
clearTimeout(timeout)
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
await outputsSettled
|
|
362
|
+
}
|
|
363
|
+
await alternateRecoveryPending
|
|
364
|
+
const abortError = interruption ?? controller.signal.reason
|
|
365
|
+
if (abortError instanceof InstallInterruptedError) throw abortError
|
|
366
|
+
if (outputFailure !== undefined && abortError === outputFailure) throw outputFailure
|
|
367
|
+
if (installError !== undefined) throw installError
|
|
368
|
+
if (abortError instanceof Error) throw abortError
|
|
369
|
+
if (controller.signal.aborted) throw new Error('Installation aborted without an Error reason.')
|
|
370
|
+
return result
|
|
371
|
+
} finally {
|
|
372
|
+
for (const [signal, handler] of handlers) runtimeProcess.removeListener(signal, handler)
|
|
373
|
+
controller.signal.removeEventListener('abort', cancelBlockedOutput)
|
|
374
|
+
stdoutBoundary.dispose()
|
|
375
|
+
stderrBoundary.dispose()
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function createInstallerOutput(destination, onFailure) {
|
|
380
|
+
let activeWrite
|
|
381
|
+
let failed = false
|
|
382
|
+
let pendingWrites = 0
|
|
383
|
+
const settleWaiters = new Set()
|
|
384
|
+
const settle = () => {
|
|
385
|
+
if (pendingWrites !== 0 || output.writableLength !== 0) return
|
|
386
|
+
for (const resolve of settleWaiters) resolve()
|
|
387
|
+
settleWaiters.clear()
|
|
388
|
+
}
|
|
389
|
+
const fail = (error) => {
|
|
390
|
+
if (failed) return
|
|
391
|
+
failed = true
|
|
392
|
+
onFailure(error instanceof Error ? error : new Error(String(error)))
|
|
393
|
+
activeWrite?.()
|
|
394
|
+
}
|
|
395
|
+
const closed = () => fail(new Error('Output destination closed during installation.'))
|
|
396
|
+
const output = new Writable({
|
|
397
|
+
write(chunk, encoding, callback) {
|
|
398
|
+
if (failed) {
|
|
399
|
+
callback()
|
|
400
|
+
queueMicrotask(settle)
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
pendingWrites += 1
|
|
404
|
+
let completed = false
|
|
405
|
+
const written = (error) => {
|
|
406
|
+
if (completed) return
|
|
407
|
+
completed = true
|
|
408
|
+
activeWrite = undefined
|
|
409
|
+
if (error !== undefined && error !== null) fail(error)
|
|
410
|
+
pendingWrites = Math.max(0, pendingWrites - 1)
|
|
411
|
+
callback()
|
|
412
|
+
queueMicrotask(settle)
|
|
413
|
+
}
|
|
414
|
+
activeWrite = written
|
|
415
|
+
try {
|
|
416
|
+
destination.write(chunk, encoding, written)
|
|
417
|
+
} catch (error) {
|
|
418
|
+
written(error)
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
Object.defineProperties(output, {
|
|
423
|
+
columns: { get: () => destination.columns },
|
|
424
|
+
isTTY: { get: () => destination.isTTY },
|
|
425
|
+
rows: { get: () => destination.rows },
|
|
426
|
+
})
|
|
427
|
+
destination.on('error', fail)
|
|
428
|
+
destination.on('close', closed)
|
|
429
|
+
return {
|
|
430
|
+
output,
|
|
431
|
+
write(value) {
|
|
432
|
+
return new Promise(resolve => {
|
|
433
|
+
if (failed) {
|
|
434
|
+
resolve(false)
|
|
435
|
+
return
|
|
436
|
+
}
|
|
437
|
+
output.write(value, () => resolve(!failed))
|
|
438
|
+
})
|
|
439
|
+
},
|
|
440
|
+
cancel() {
|
|
441
|
+
const blocked = pendingWrites !== 0 || output.writableLength !== 0
|
|
442
|
+
if (!blocked) return false
|
|
443
|
+
failed = true
|
|
444
|
+
destination.destroy()
|
|
445
|
+
activeWrite?.()
|
|
446
|
+
return true
|
|
447
|
+
},
|
|
448
|
+
settled() {
|
|
449
|
+
if (pendingWrites === 0 && output.writableLength === 0) return Promise.resolve()
|
|
450
|
+
return new Promise(resolve => settleWaiters.add(resolve))
|
|
451
|
+
},
|
|
452
|
+
dispose() {
|
|
453
|
+
destination.removeListener('error', fail)
|
|
454
|
+
destination.removeListener('close', closed)
|
|
455
|
+
},
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function main() {
|
|
460
|
+
const command = parseCommand(process.argv.slice(2))
|
|
461
|
+
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')
|
|
463
|
+
return
|
|
464
|
+
}
|
|
465
|
+
if (command === 'version') {
|
|
466
|
+
process.stdout.write(`${edgePackage.version}\n`)
|
|
467
|
+
return
|
|
468
|
+
}
|
|
469
|
+
await runInstaller({ command })
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (process.argv[1] !== undefined
|
|
473
|
+
&& realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
474
|
+
main().catch((error) => {
|
|
475
|
+
if (error instanceof InstallCancelledError) {
|
|
476
|
+
if (error instanceof InstallInterruptedError) {
|
|
477
|
+
writeAlternate(
|
|
478
|
+
writeSync,
|
|
479
|
+
error.outputFailureStream ?? 'stdout',
|
|
480
|
+
['', `Installation interrupted by ${error.signal}.`, ''],
|
|
481
|
+
)
|
|
482
|
+
process.exitCode = error.exitCode
|
|
483
|
+
} else {
|
|
484
|
+
prompt.cancel(error.message)
|
|
485
|
+
}
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
if (error instanceof InstallerOutputError) {
|
|
489
|
+
writeAlternate(writeSync, error.stream, ['', `Installation failed: ${error.message}`, ''])
|
|
490
|
+
process.exitCode = 1
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
prompt.cancel(error instanceof Error ? error.message : String(error))
|
|
494
|
+
process.exitCode = 1
|
|
495
|
+
})
|
|
496
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export type RuntimeMode = 'direct' | 'isolated'
|
|
2
|
+
export type InstallerCommand = 'install' | 'upgrade'
|
|
3
|
+
|
|
4
|
+
export interface CloudflareAccount {
|
|
5
|
+
id: string
|
|
6
|
+
name: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CommandResult {
|
|
10
|
+
interrupted?: boolean
|
|
11
|
+
outputFailure?: InstallerOutputError
|
|
12
|
+
status: number | null
|
|
13
|
+
stdout: string
|
|
14
|
+
stderr: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InstallerUi {
|
|
18
|
+
intro(message: string): void
|
|
19
|
+
step(message: string): void
|
|
20
|
+
selectRuntime(): Promise<RuntimeMode>
|
|
21
|
+
selectAccount(choices: Array<{ value: string; label: string; hint?: string }>): Promise<string>
|
|
22
|
+
workerName(initialValue: string, validate: (value: string) => string | undefined): Promise<string>
|
|
23
|
+
workerConflict(workerName: string): Promise<'rename' | 'update' | 'cancel'>
|
|
24
|
+
selectOwnerSecretMode(): Promise<'generate' | 'custom'>
|
|
25
|
+
ownerSecret(validate: (value: string) => string | undefined): Promise<string>
|
|
26
|
+
deepSeekKey(validate: (value: string) => string | undefined): Promise<string>
|
|
27
|
+
confirm(summary: {
|
|
28
|
+
mode: RuntimeMode
|
|
29
|
+
modeLabel: string
|
|
30
|
+
accountLabel: string
|
|
31
|
+
workerName: string
|
|
32
|
+
paid: boolean
|
|
33
|
+
}): Promise<boolean>
|
|
34
|
+
acceptTemporaryTerms(): Promise<boolean>
|
|
35
|
+
cleanupFailure(message: string): void
|
|
36
|
+
recovery(result: InstallRecovery): void
|
|
37
|
+
outputFailureRecovery(
|
|
38
|
+
result: InstallRecovery,
|
|
39
|
+
failedStream: 'stderr' | 'stdout',
|
|
40
|
+
): boolean | Promise<boolean> | void
|
|
41
|
+
success(result: InstallResult): void
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface InstallRecovery {
|
|
45
|
+
claimUrl?: string
|
|
46
|
+
ownerSecret: string
|
|
47
|
+
publicUrl?: string
|
|
48
|
+
workerName: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface InstallResult {
|
|
52
|
+
publicUrl: string
|
|
53
|
+
versionId?: string
|
|
54
|
+
account?: CloudflareAccount
|
|
55
|
+
claimUrl?: string
|
|
56
|
+
deploymentId: string
|
|
57
|
+
mode: RuntimeMode
|
|
58
|
+
ownerSecret: string
|
|
59
|
+
temporary: boolean
|
|
60
|
+
workerName: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const DEPLOYMENT_ID_DEFINE: string
|
|
64
|
+
export const DEFAULT_WORKER_NAME: string
|
|
65
|
+
export const LOGIN_PROFILE: string
|
|
66
|
+
export const RUNTIME_MODES: Readonly<Record<RuntimeMode, Readonly<{
|
|
67
|
+
environment: string
|
|
68
|
+
expectedShell: string
|
|
69
|
+
label: string
|
|
70
|
+
}>>>
|
|
71
|
+
export class InstallCancelledError extends Error {}
|
|
72
|
+
export class InstallerOutputError extends Error {
|
|
73
|
+
readonly stream: 'stderr' | 'stdout'
|
|
74
|
+
}
|
|
75
|
+
export function accountChoices(mode: RuntimeMode, accounts: CloudflareAccount[], command?: InstallerCommand): Array<{
|
|
76
|
+
value: string
|
|
77
|
+
label: string
|
|
78
|
+
hint?: string
|
|
79
|
+
}>
|
|
80
|
+
export function parseWhoami(source: string): { accounts: CloudflareAccount[]; email?: string }
|
|
81
|
+
export function validateWorkerName(value: string): string | undefined
|
|
82
|
+
export function validateOwnerSecret(value: string): string | undefined
|
|
83
|
+
export function validateDeepSeekKey(value: string): string | undefined
|
|
84
|
+
export function generateOwnerSecret(): string
|
|
85
|
+
export function wranglerEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
|
|
86
|
+
export function unauthenticatedEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
|
|
87
|
+
export function wranglerDeployArgs(options: {
|
|
88
|
+
deploymentId: string
|
|
89
|
+
mode: RuntimeMode
|
|
90
|
+
workerName: string
|
|
91
|
+
secretsFile: string
|
|
92
|
+
configFile: string
|
|
93
|
+
profile?: string
|
|
94
|
+
temporary?: boolean
|
|
95
|
+
}): string[]
|
|
96
|
+
export function parseDeploymentOutput(source: string): { publicUrl: string; versionId?: string }
|
|
97
|
+
export function parseClaimUrl(source: string): string | undefined
|
|
98
|
+
export function parseWorkerExistence(result: CommandResult): boolean
|
|
99
|
+
export function truncateUtf8Tail(value: string, maxBytes: number): string
|
|
100
|
+
export function createOutputForwarder(
|
|
101
|
+
source: NodeJS.ReadableStream,
|
|
102
|
+
destination: NodeJS.WritableStream,
|
|
103
|
+
onFailure: (error: Error) => void,
|
|
104
|
+
): {
|
|
105
|
+
write(chunk: string): void
|
|
106
|
+
settled(): Promise<void>
|
|
107
|
+
cancel(): void
|
|
108
|
+
dispose(): void
|
|
109
|
+
}
|
|
110
|
+
export function createTerminalSanitizer(): {
|
|
111
|
+
push(chunk: string): string
|
|
112
|
+
}
|
|
113
|
+
export function resolveWranglerClose(options: {
|
|
114
|
+
outputFailure?: InstallerOutputError
|
|
115
|
+
processError?: unknown
|
|
116
|
+
signal?: AbortSignal
|
|
117
|
+
status: number | null
|
|
118
|
+
stderr: string
|
|
119
|
+
stdout: string
|
|
120
|
+
}): CommandResult
|
|
121
|
+
export function installEdge(options: {
|
|
122
|
+
command?: InstallerCommand
|
|
123
|
+
ui: InstallerUi
|
|
124
|
+
runWrangler?: (args: string[], options?: {
|
|
125
|
+
environment?: NodeJS.ProcessEnv
|
|
126
|
+
interactive?: boolean
|
|
127
|
+
capture?: boolean
|
|
128
|
+
signal?: AbortSignal
|
|
129
|
+
}) => Promise<CommandResult>
|
|
130
|
+
deploymentId?: string
|
|
131
|
+
environment?: NodeJS.ProcessEnv
|
|
132
|
+
createTemporaryDirectory?: () => Promise<string>
|
|
133
|
+
removePath?: typeof import('node:fs/promises').rm
|
|
134
|
+
signal?: AbortSignal
|
|
135
|
+
}): Promise<InstallResult>
|
|
136
|
+
export function wranglerProcessInvocation(args: string[], options?: {
|
|
137
|
+
nodeExecutable?: string
|
|
138
|
+
wranglerCli?: string
|
|
139
|
+
}): { command: string; args: string[] }
|
|
140
|
+
export function executeWrangler(args: string[], options?: {
|
|
141
|
+
environment?: NodeJS.ProcessEnv
|
|
142
|
+
interactive?: boolean
|
|
143
|
+
capture?: boolean
|
|
144
|
+
forceKillAfterDelay?: number
|
|
145
|
+
invocation?: { command: string; args: string[] }
|
|
146
|
+
signal?: AbortSignal
|
|
147
|
+
stderrDestination?: NodeJS.WritableStream
|
|
148
|
+
stdoutDestination?: NodeJS.WritableStream
|
|
149
|
+
}): Promise<CommandResult>
|