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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +183 -0
  4. package/README.zh.md +183 -0
  5. package/THIRD_PARTY_NOTICES.md +199 -0
  6. package/dist/_headers +3 -0
  7. package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. package/dist/assets/index-C-1AiF3k.js +112 -0
  67. package/dist/assets/index-CSGf6Qzd.css +1 -0
  68. package/dist/assets/langs/c-BIGW1oBm.js +2 -0
  69. package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
  70. package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
  71. package/dist/assets/langs/css-CLj8gQPS.js +2 -0
  72. package/dist/assets/langs/go-C27-OAKa.js +2 -0
  73. package/dist/assets/langs/html-CfGypltT.js +2 -0
  74. package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
  75. package/dist/assets/langs/java-CylS5w8V.js +2 -0
  76. package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
  77. package/dist/assets/langs/less-B1dDrJ26.js +2 -0
  78. package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
  79. package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
  80. package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
  81. package/dist/assets/langs/php-D-uCvoST.js +2 -0
  82. package/dist/assets/langs/python-B6aJPvgy.js +2 -0
  83. package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
  84. package/dist/assets/langs/rust-B1yitclQ.js +2 -0
  85. package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
  86. package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
  87. package/dist/assets/langs/swift-D82vCrfD.js +2 -0
  88. package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
  89. package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
  90. package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
  91. package/dist/assets/vendor-Cjbwl5VI.js +411 -0
  92. package/dist/assets/vendor-CjyC-hUb.css +1 -0
  93. package/dist/favicon.svg +8 -0
  94. package/dist/index.html +35 -0
  95. package/dist/manifest.webmanifest +16 -0
  96. package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
  97. package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
  98. package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
  99. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
  100. package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
  101. package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
  102. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
  103. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
  104. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
  105. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
  106. package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
  107. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
  108. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
  109. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
  110. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
  111. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
  112. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
  113. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
  114. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
  115. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
  116. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
  117. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
  118. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
  119. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
  120. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
  121. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
  122. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
  123. package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
  124. package/package.json +97 -0
  125. package/scripts/assemble-web.mjs +230 -0
  126. package/scripts/bundle-size.d.mts +2 -0
  127. package/scripts/bundle-size.mjs +26 -0
  128. package/scripts/bundle.mjs +53 -0
  129. package/scripts/cli.d.mts +39 -0
  130. package/scripts/cli.mjs +496 -0
  131. package/scripts/install.d.mts +149 -0
  132. package/scripts/install.mjs +989 -0
  133. package/scripts/legal-files.mjs +24 -0
  134. package/scripts/verify-packed.mjs +32 -0
  135. package/scripts/wrangler-config.d.mts +18 -0
  136. package/scripts/wrangler-config.mjs +84 -0
  137. package/src/agent.ts +119 -0
  138. package/src/auth.ts +352 -0
  139. package/src/deepseek.ts +231 -0
  140. package/src/deployment.ts +147 -0
  141. package/src/direct-shell-core-empty.ts +10 -0
  142. package/src/direct-shell-protocol.ts +5 -0
  143. package/src/direct-shell.ts +341 -0
  144. package/src/do-session-persistence.ts +1064 -0
  145. package/src/edge-api.ts +796 -0
  146. package/src/edge-credentials.ts +56 -0
  147. package/src/edge-remotes.ts +41 -0
  148. package/src/edge-workspace-store.ts +339 -0
  149. package/src/http.ts +212 -0
  150. package/src/index.ts +196 -0
  151. package/src/instance.ts +1007 -0
  152. package/src/isolated-direct-shell-unavailable.ts +11 -0
  153. package/src/protocol.ts +39 -0
  154. package/src/release.ts +9 -0
  155. package/src/session-store.ts +1110 -0
  156. package/src/sse.ts +86 -0
  157. package/src/web-search.ts +38 -0
  158. package/src/workspace.ts +270 -0
  159. package/wrangler.jsonc +56 -0
@@ -0,0 +1,341 @@
1
+ /** In-process just-bash backend for the Cloudflare Computer workspace runtime. */
2
+
3
+ import type { BackendHandle, WorkspaceBackend } from '@cloudflare/computer'
4
+ import {
5
+ WorkspaceFsAdapter,
6
+ defineArtifactsCommand,
7
+ defineAssetsCommand,
8
+ defineGitCommand,
9
+ type WorkspaceFs,
10
+ } from '@cloudflare/computer/backends/worker-shell'
11
+ import {
12
+ Bash,
13
+ type BashExecResult,
14
+ type IFileSystem,
15
+ } from 'just-bash/browser'
16
+ import {
17
+ DIRECT_SHELL_OUTPUT_TRUNCATED,
18
+ EDGE_SHELL_OUTPUT_LIMIT_BYTES,
19
+ } from './direct-shell-protocol.ts'
20
+
21
+ const DEFAULT_BACKEND_ID = 'worker-shell'
22
+ const DEFAULT_CWD = '/workspace'
23
+ type DirectBackendHost = Parameters<WorkspaceBackend['connect']>[0]
24
+ type WorkspaceSyncRpc = BackendHandle['rpc']['sync']
25
+ type ShellExec = BackendHandle['rpc']['shell']['exec']
26
+ type ShellExecInput = Parameters<ShellExec>[0]
27
+ type ShellExecEnvelope = Awaited<ReturnType<ShellExec>>
28
+ type ShellExecEvent = ShellExecEnvelope['events'] extends ReadableStream<infer Event>
29
+ ? Event
30
+ : never
31
+
32
+ interface DirectBashExecOptions {
33
+ cwd?: string
34
+ env?: Record<string, string>
35
+ stdin?: string
36
+ stdinKind?: 'bytes'
37
+ signal?: AbortSignal
38
+ }
39
+
40
+ interface DirectExecution {
41
+ controller: AbortController
42
+ done: Promise<void>
43
+ }
44
+
45
+ interface DirectBashOutcome {
46
+ result: BashExecResult
47
+ terminalResult?: typeof DIRECT_SHELL_OUTPUT_TRUNCATED
48
+ }
49
+
50
+ /** Options for the free in-process shell backend. */
51
+ export interface DirectShellBackendOptions {
52
+ id?: string
53
+ }
54
+
55
+ /** Run just-bash in the owning Durable Object against Computer's local VFS. */
56
+ export class DirectShellBackend implements WorkspaceBackend {
57
+ readonly type = 'direct-shell'
58
+ readonly id: string
59
+
60
+ constructor(options: DirectShellBackendOptions = {}) {
61
+ this.id = options.id ?? DEFAULT_BACKEND_ID
62
+ }
63
+
64
+ /** Connect one workspace-local shell runtime. */
65
+ connect(host: DirectBackendHost): Promise<BackendHandle> {
66
+ const runtime = new DirectShellRuntime(host)
67
+ return Promise.resolve({
68
+ rpc: {
69
+ sync: disabledSyncRpc(),
70
+ shell: {
71
+ exec: input => runtime.exec(input),
72
+ getExec: input => runtime.getExec(input.id),
73
+ killExec: input => runtime.killExec(input.id, input.signal),
74
+ disposeExec: input => runtime.disposeExec(input.id),
75
+ },
76
+ },
77
+ sync: 'none',
78
+ close: () => runtime.close(),
79
+ })
80
+ }
81
+ }
82
+
83
+ class DirectShellRuntime {
84
+ private readonly fs: WorkspaceFsAdapter
85
+ private readonly executions = new Map<string, DirectExecution>()
86
+ private closed = false
87
+
88
+ constructor(private readonly host: DirectBackendHost) {
89
+ this.fs = new WorkspaceFsAdapter(workspaceFs(host))
90
+ }
91
+
92
+ exec(input: ShellExecInput): Promise<ShellExecEnvelope> {
93
+ if (this.closed) throw directShellError('ECLOSED', 'direct shell backend is closed')
94
+ const id = input.id ?? crypto.randomUUID()
95
+ if (this.executions.has(id)) {
96
+ throw directShellError('EEXEC_BUSY', `execution ${id} is running`)
97
+ }
98
+
99
+ const controller = new AbortController()
100
+ const result = Promise.resolve().then(() => this.run(input, controller.signal))
101
+ const done = result.then(() => undefined, () => undefined)
102
+ this.executions.set(id, { controller, done })
103
+ return Promise.resolve({
104
+ id,
105
+ events: executionEvents(
106
+ id,
107
+ result,
108
+ () => { this.deleteExecution(id, controller) },
109
+ () => this.disposeExec(id),
110
+ ),
111
+ })
112
+ }
113
+
114
+ getExec(id: string): Promise<ShellExecEnvelope> {
115
+ throw directShellError('ENOENT', `no such exec: ${id}`)
116
+ }
117
+
118
+ killExec(id: string, signal = 'SIGTERM'): Promise<void> {
119
+ this.executions.get(id)?.controller.abort(
120
+ directShellError('EEXEC_CANCELLED', `execution cancelled with ${signal}`),
121
+ )
122
+ return Promise.resolve()
123
+ }
124
+
125
+ async disposeExec(id: string): Promise<void> {
126
+ const execution = this.executions.get(id)
127
+ if (execution === undefined) return
128
+ execution.controller.abort(
129
+ directShellError('EEXEC_DISPOSED', `execution disposed: ${id}`),
130
+ )
131
+ await execution.done
132
+ this.deleteExecution(id, execution.controller)
133
+ }
134
+
135
+ async close(): Promise<void> {
136
+ if (this.closed) return
137
+ this.closed = true
138
+ const active = [...this.executions.values()]
139
+ for (const execution of active) {
140
+ execution.controller.abort(directShellError('ECLOSED', 'direct shell backend is closing'))
141
+ }
142
+ await Promise.allSettled(active.map(execution => execution.done))
143
+ this.executions.clear()
144
+ }
145
+
146
+ private async run(input: ShellExecInput, signal: AbortSignal): Promise<DirectBashOutcome> {
147
+ const timeoutLimit = input.timeoutMs !== undefined && input.timeoutMs > 0
148
+ ? { maxExecutionTimeMs: input.timeoutMs }
149
+ : {}
150
+ const bash = new Bash({
151
+ // Computer and just-bash accept the same runtime encodings, but their
152
+ // interfaces spell the optional UTF-8 alias set differently.
153
+ fs: this.fs as unknown as IFileSystem,
154
+ cwd: input.cwd ?? DEFAULT_CWD,
155
+ ...(input.env === undefined ? {} : { env: input.env }),
156
+ customCommands: [
157
+ defineGitCommand(this.host),
158
+ defineAssetsCommand({}),
159
+ defineArtifactsCommand(this.host),
160
+ ],
161
+ defenseInDepth: { enabled: 'auto' },
162
+ executionLimitProfile: 'hardened',
163
+ executionLimits: {
164
+ maxOutputSize: EDGE_SHELL_OUTPUT_LIMIT_BYTES,
165
+ ...timeoutLimit,
166
+ },
167
+ })
168
+ const directBash = bash as unknown as {
169
+ exec(command: string, options: DirectBashExecOptions): Promise<BashExecResult>
170
+ }
171
+ try {
172
+ const result = await directBash.exec(input.source, {
173
+ cwd: input.cwd ?? DEFAULT_CWD,
174
+ ...(input.env === undefined ? {} : { env: input.env }),
175
+ ...(input.stdin === undefined
176
+ ? {}
177
+ : { stdin: latin1FromBytes(input.stdin), stdinKind: 'bytes' as const }),
178
+ signal,
179
+ })
180
+ return {
181
+ result,
182
+ ...(hitDirectOutputLimit(result)
183
+ ? { terminalResult: DIRECT_SHELL_OUTPUT_TRUNCATED }
184
+ : {}),
185
+ }
186
+ } catch (error) {
187
+ const message = error instanceof Error ? error.message : String(error)
188
+ return {
189
+ result: {
190
+ stdout: '',
191
+ stderr: `${message}\n`,
192
+ exitCode: signal.aborted ? 130 : 1,
193
+ env: input.env ?? {},
194
+ },
195
+ }
196
+ }
197
+ }
198
+
199
+ private deleteExecution(id: string, controller: AbortController): void {
200
+ if (this.executions.get(id)?.controller === controller) this.executions.delete(id)
201
+ }
202
+ }
203
+
204
+ function workspaceFs(host: DirectBackendHost): WorkspaceFs {
205
+ const fs = host.fs
206
+ return {
207
+ readFile: fs.readFile.bind(fs),
208
+ exists: async path => await statOrNull(() => fs.stat(path)) !== null,
209
+ stat: path => fs.stat(path),
210
+ statOrNull: path => statOrNull(() => fs.stat(path)),
211
+ lstat: path => fs.lstat(path),
212
+ lstatOrNull: path => statOrNull(() => fs.lstat(path)),
213
+ readdir: path => fs.readdir(path),
214
+ find: (directory, pattern) => fs.find(directory, pattern),
215
+ ls: prefix => fs.ls(prefix),
216
+ grep: (pattern, path, options) => fs.grep(pattern, path, options),
217
+ readlink: path => fs.readlink(path),
218
+ writeFile: (path, content, options) => fs.writeFile(path, content, options),
219
+ mkdir: (path, options) => fs.mkdir(path, options),
220
+ rm: (path, options) => fs.rm(path, options),
221
+ chmod: (path, mode) => fs.chmod(path, mode),
222
+ symlink: (target, path) => fs.symlink(target, path),
223
+ }
224
+ }
225
+
226
+ async function statOrNull<T>(read: () => Promise<T>): Promise<T | null> {
227
+ try {
228
+ return await read()
229
+ } catch (error) {
230
+ if (hasErrorCode(error, 'ENOENT')) return null
231
+ throw error
232
+ }
233
+ }
234
+
235
+ function hasErrorCode(error: unknown, code: string): boolean {
236
+ return typeof error === 'object'
237
+ && error !== null
238
+ && 'code' in error
239
+ && error.code === code
240
+ }
241
+
242
+ function executionEvents(
243
+ id: string,
244
+ result: Promise<DirectBashOutcome>,
245
+ onTerminal: () => void,
246
+ onCancel: () => Promise<void>,
247
+ ): ReadableStream<ShellExecEvent> {
248
+ const encoder = new TextEncoder()
249
+ let events: ShellExecEvent[] | undefined
250
+ return new ReadableStream({
251
+ async pull(controller) {
252
+ try {
253
+ events ??= shellExecEvents(id, encoder, await result)
254
+ const event = events.shift()
255
+ if (event === undefined) return
256
+ controller.enqueue(event)
257
+ if (event.name === 'exit') {
258
+ controller.close()
259
+ onTerminal()
260
+ }
261
+ } catch (error) {
262
+ controller.error(error)
263
+ onTerminal()
264
+ }
265
+ },
266
+ cancel: onCancel,
267
+ }, { highWaterMark: 0 })
268
+ }
269
+
270
+ function shellExecEvents(
271
+ id: string,
272
+ encoder: TextEncoder,
273
+ outcome: DirectBashOutcome,
274
+ ): ShellExecEvent[] {
275
+ const { result } = outcome
276
+ const events: ShellExecEvent[] = []
277
+ let seq = 0
278
+ if (result.stdout.length > 0) {
279
+ events.push({ id, seq: ++seq, name: 'stdout', value: encoder.encode(result.stdout) })
280
+ }
281
+ if (result.stderr.length > 0) {
282
+ events.push({ id, seq: ++seq, name: 'stderr', value: encoder.encode(result.stderr) })
283
+ }
284
+ events.push({
285
+ id,
286
+ seq: ++seq,
287
+ name: 'exit',
288
+ code: result.exitCode,
289
+ ...(outcome.terminalResult === undefined ? {} : { result: outcome.terminalResult }),
290
+ })
291
+ return events
292
+ }
293
+
294
+ function hitDirectOutputLimit(result: BashExecResult): boolean {
295
+ if (result.exitCode !== 126) return false
296
+ const accountedBytes = result.internalOutputAccounting?.stderr
297
+ if (accountedBytes === undefined) return false
298
+ const stderr = new TextEncoder().encode(result.stderr)
299
+ if (accountedBytes < 0 || accountedBytes > stderr.byteLength) return false
300
+ const interpreterDiagnostic = new TextDecoder().decode(stderr.subarray(accountedBytes))
301
+ return [
302
+ `limit exceeded (${EDGE_SHELL_OUTPUT_LIMIT_BYTES} bytes)`,
303
+ `total output size exceeded (>${EDGE_SHELL_OUTPUT_LIMIT_BYTES} bytes), `
304
+ + 'increase executionLimits.maxOutputSize',
305
+ ].some(diagnostic => interpreterDiagnostic.includes(diagnostic))
306
+ }
307
+
308
+ function latin1FromBytes(bytes: Uint8Array): string {
309
+ let result = ''
310
+ for (const byte of bytes) result += String.fromCharCode(byte)
311
+ return result
312
+ }
313
+
314
+ function directShellError(code: string, message: string): Error & { code: string } {
315
+ const error = new Error(message) as Error & { code: string }
316
+ error.name = 'DirectShellError'
317
+ error.code = code
318
+ return error
319
+ }
320
+
321
+ function disabledSyncRpc(): WorkspaceSyncRpc {
322
+ const refuse = (name: string): never => {
323
+ throw directShellError(
324
+ 'ENOSYS',
325
+ `DirectShellBackend: sync.${name} must not be called because sync is disabled`,
326
+ )
327
+ }
328
+ return {
329
+ push: () => refuse('push'),
330
+ fetchChanges: () => refuse('fetchChanges'),
331
+ watermarks: () => refuse('watermarks'),
332
+ readEntry: () => refuse('readEntry'),
333
+ hasObjects: () => refuse('hasObjects'),
334
+ fetchObjects: () => new ReadableStream({
335
+ start(controller) {
336
+ controller.error(directShellError('ENOSYS', 'DirectShellBackend: sync is disabled'))
337
+ },
338
+ }),
339
+ pushObjects: () => refuse('pushObjects'),
340
+ }
341
+ }