dsh-edge 0.18.0-alpha.1 → 0.18.0-alpha.2

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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority.
3
3
  # After editing either side, update both and re-record every pair with:
4
4
  # pnpm run doc-pairs -- --write
5
- README.md: e046e77635f642aa9be7b40c205295bd78e799bc
6
- README.zh.md: 23286dce38430d2ab6449d4daa2219ed5cc32b3c
5
+ README.md: d8b101ff65ead8c0e4a991600e353c5d53472149
6
+ README.zh.md: b03fadb37a72b70ed06bca9b6ebb99d3af90b914
package/README.md CHANGED
@@ -152,7 +152,7 @@ This reference separates code that runs natively in Workers, code adapted at an
152
152
  | Directory picker | `ctx.directoryPicker` seam with the `-native` OS chooser or the `-browse` filesystem backend, chosen at boot by `dsh-host-directory-picker-auto`, plus the Web browse dialog | Reused with an Edge browse backend | Install the upstream seam and pin the upstream `dsh-client-ui-directory-picker-browse` dialog in the Web roster; Edge answers the `browse` capability from the `/workspace` Computer VFS one level per call, rooted at `/workspace` with the upstream hidden, bound, and failure vocabulary, so the upstream `DirectoryPickerController` serves `directoryPicker/list` and `createDirectory` and refuses the native `pick`. |
153
153
  | Conversation file links | `session/openWorkspacePath` hands a clicked path to the Host desktop opener through `dsh-native-command`; `session/canOpenWorkspacePath` gates the affordance | Adapted at the upstream `SessionControllerInternals` seam plus a Web download fallback | Compose `SessionController` with Edge internals so the native probe answers false and an open attempt fails with a readable reason instead of a `child_process` error. The V3 Web client opens file links in its right-sidebar preview. Edge composes the upstream `workspaceFiles` controller over request-scoped VFS reads, including bounded text/byte windows and metadata subscriptions. The legacy `ctx.remote.session.openWorkspacePath` fallback and owner-authenticated `GET /api/workspace/file` download remain available. |
154
154
  | Existing Web UI | Runtime-loaded shell and `dsh.client` plugin graph | Reused with generic composition fallbacks | Assemble the upstream shell and supported upstream client bundles as Worker assets. Shared slot-occupancy rules hide actions whose provider is absent; Cloudflare serves ordinary assets directly, while `/`, `/login`, and `/api/*` enter the Worker for owner access control. The assembled asset policy prevents every direct or SPA-fallback shell alias from being framed. |
155
- | Other tools | Web Search, filesystem editor tools, MCP, skills, workflows, jobs, and subagents | Search, file, goal, skill, and MCP tools ported; workflow and run_code ported for the isolated build | Reuse upstream DeepSeek Web Search with its 30-second tool-call timeout. The upstream `workflow` tool is offered only by the Dynamic Worker runtime (the isolated, Paid-plan build); the Direct build does not register it. Each run loads its script into its own Dynamic Worker with no outbound network and a 30-second Worker Loader `cpuMs` limit (enforced by the Cloudflare runtime, not by local development), because workerd forbids in-process `eval`/`new Function`/`node:vm`; `agent()`, `phase()`, and `log()` cross an RPC bridge to the Durable Object, which enforces the caps: at most 4 children run at once (below the Workers six-connection limit) and one run starts at most 100 children to bound child-session row writes. The upstream `run_code` tool (`dsh-tools` PTC mode, enabled as `both` alongside the native tools) is offered by the same runtime: each program is type-stripped with sucrase and runs in its own Dynamic Worker with a 30-second `cpuMs` limit and no outbound network; its `tools.*` calls cross an RPC bridge where the Durable Object caps them at 200 per run (each is a nested tool dispatch with its own session events), 256 KiB of arguments, and 2 MiB per result, and nested calls stay approval-gated. File tools (read/write/edit/read_image) adapted via `EdgeFileSystem` backed by Computer VFS. Goal tools (`ToolGoal`) composed directly with `GoalService` as upstream cordis plugins. Skill tool via `SkillRegistry` + `EdgeSkillProvider` (DO KV-backed); owner CRUD at `GET/PUT/DELETE /api/skills`. MCP client (`dsh-mcp-client`) installed; Streamable HTTP transport only (stdio unavailable on Workers). Configure servers in Settings → DSH Edge → MCP Servers; tools appear as `mcp__<serverName>__<tool>`. Add the remaining tools individually against Worker-compatible capabilities; do not advertise unavailable host behavior. |
155
+ | Other tools | Web Search, filesystem editor tools, MCP, skills, workflows, jobs, and subagents | Search, file, goal, skill, and MCP tools ported; workflow and run_code ported for the isolated build | Reuse upstream DeepSeek Web Search with its 30-second tool-call timeout. The upstream `workflow` tool is offered only by the Dynamic Worker runtime (the isolated, Paid-plan build); the Direct build does not register it. Each run loads its script into its own Dynamic Worker with no outbound network and a 30-second Worker Loader `cpuMs` limit (enforced by the Cloudflare runtime, not by local development), because workerd forbids in-process `eval`/`new Function`/`node:vm`; `agent()`, `phase()`, and `log()` cross an RPC bridge to the Durable Object, which enforces the caps: at most 4 children run at once (below the Workers six-connection limit) and one run starts at most 100 children to bound child-session row writes. The upstream `run_code` tool (`dsh-tools` PTC mode) is offered by the same runtime, but only to sessions started in the upstream `ptc` agent preset (PTC mode), which isolated deployments offer under its upstream id so the Web client shows its built-in bilingual name. That preset presents every tool through the generated SDK and, as upstream does, does not offer `workflow`, because `run_code` is its only orchestration surface. The default `dsh-edge` preset presents tools natively and keeps `workflow`: each program is type-stripped with sucrase and runs in its own Dynamic Worker with a 30-second `cpuMs` limit and no outbound network; its `tools.*` calls cross an RPC bridge where the Durable Object caps them at 200 per run (each is a nested tool dispatch with its own session events), 256 KiB of arguments, and 2 MiB per result, and nested calls stay approval-gated. File tools (read/write/edit/read_image) adapted via `EdgeFileSystem` backed by Computer VFS. Goal tools (`ToolGoal`) composed directly with `GoalService` as upstream cordis plugins. Skill tool via `SkillRegistry` + `EdgeSkillProvider` (DO KV-backed); owner CRUD at `GET/PUT/DELETE /api/skills`. MCP client (`dsh-mcp-client`) installed; Streamable HTTP transport only (stdio unavailable on Workers). Configure servers in Settings → DSH Edge → MCP Servers; tools appear as `mcp__<serverName>__<tool>`. Add the remaining tools individually against Worker-compatible capabilities; do not advertise unavailable host behavior. |
156
156
  | Attachments | Local attachment storage, upstream image references, composer, gallery, lightbox, and provider conversion | Adapted at the native storage seam | Reuse upstream `AttachmentStore`, admission, protocol, authorization, UI, and DeepSeek conversion unchanged. Store immutable PNG/JPEG bytes under their SHA-256 identities in a 64 MiB, 512 KiB-chunked DO backend for new and pre-attachment deployments, or in private R2 for deployments already pinned to it; session events retain only upstream refs. The first backend is pinned per owner instance so claiming or upgrading cannot strand existing references. |
157
157
  | Goal tracking | `GoalService` with create/edit/pause/resume/complete/clear mutations and GoalBar UI | Reused | Install upstream `GoalService` and `ToolGoal` as cordis plugins (direct composition). Browser GoalBar mutations route through `TypertGatewayService` at `/api/goals/<method>`. `SessionProjectionCache` persists goal state across DO restart. |
158
158
  | Context compaction | Token metering, automatic compaction, and session title generation | Reused | Install upstream cordis plugins directly. Context compaction, token metering, tool-result pruning, and automatic session title generation run unchanged. |
@@ -281,7 +281,7 @@ The installer asks in this order: account, Worker name, then what to change. Eve
281
281
  - **An existing name is an update.** The installer reads what the Worker can do from its bindings and offers **Update it** (the default), **Update and change what it can do**, **Use another name**, or **Cancel**. "Update it" is the confirmation: it deploys the new release with the same capabilities and keeps conversations, files, the owner access key, and the DeepSeek key. It never renames the Worker for you or asks for secrets again.
282
282
  - **A new name asks what the agent should do.** The choices are cumulative, and the price comes second:
283
283
  - `Research and write` (the direct mode, the default): search the web, read pages, draft docs, and connect your tools through MCP. It runs on Workers Free.
284
- - `+ Analyze data and split big jobs` (the isolated mode): adds `run_code` for scripts over your data and `workflow` for parallel subagents. It requires Workers Paid (from $5/month).
284
+ - `+ Analyze data and split big jobs` (the isolated mode): adds `workflow` for parallel subagents, and `run_code` for scripts over your data in sessions started in the PTC mode agent preset (which, like upstream, does not offer `workflow`). It requires Workers Paid (from $5/month).
285
285
  - `+ Work on code projects` (the container mode): adds a Linux container for git, npm, and python. Commands start in the isolated shell; routing sends a command to the container only when a program it starts is outside the lightweight shell's verified set, the command cannot be parsed confidently, or the agent sets `linux: true`. At most two container commands run at once. It requires Workers Paid plus container time while it runs.
286
286
  - **Changing capabilities.** The capability list marks the current choice. Choosing less asks a second confirmation that names what is removed and defaults to No. Leaving the container mode keeps the Worker's Container application as the rollback target and prints the command that removes it; run it after you sign in and the new version works, to stop Container billing.
287
287
  - **One confirmation.** The summary lists what the instance can do, its cost, the account, the Worker, image storage, and the defaults below. For a temporary account, confirming also accepts Cloudflare's Terms of Service and Privacy Policy.
package/README.zh.md CHANGED
@@ -152,7 +152,7 @@ curl -b /tmp/dsh-edge-cookie -N -X POST -H 'content-type: application/json' \
152
152
  | Directory picker | `ctx.directoryPicker` seam,由 `dsh-host-directory-picker-auto` 在启动时选择 `-native` OS 选择器或 `-browse` 文件系统 backend,加 Web browse 对话框 | 复用并提供 Edge browse backend | 安装上游 seam,并把上游 `dsh-client-ui-directory-picker-browse` 对话框固定进 Web roster;Edge 从 `/workspace` Computer VFS 按每次一层回答 `browse` capability,以 `/workspace` 为根并沿用上游的 hidden、bound 与失败词汇,因此上游 `DirectoryPickerController` 提供 `directoryPicker/list` 与 `createDirectory`,并拒绝原生 `pick`。 |
153
153
  | 对话文件链接 | `session/openWorkspacePath` 经 `dsh-native-command` 把点击的路径交给宿主桌面打开器;`session/canOpenWorkspacePath` 决定是否展示该入口 | 在上游 `SessionControllerInternals` seam 适配,加 Web 端下载回退 | 用 Edge internals 组合 `SessionController`,让原生探测返回 false、打开尝试以可读原因失败而不是 `child_process` 错误。V3 Web 客户端在右侧栏预览文件链接。Edge 将上游 `workspaceFiles` 控制器接入请求作用域内的 VFS 读取,支持有界文本/字节分页和元数据订阅。旧版 `ctx.remote.session.openWorkspacePath` 回退以及经 owner 鉴权的 `GET /api/workspace/file` 下载仍然可用。 |
154
154
  | Existing Web UI | 运行时加载的 shell 和 `dsh.client` 插件 graph | 复用并采用通用 composition fallback | 把上游 shell 和受支持的上游客户端包组装成 Worker 静态资源;共享的 slot occupancy 规则会隐藏缺少 provider 的 action。Cloudflare 直接提供普通资源,`/`、`/login` 与 `/api/*` 则进入 Worker 执行 owner access control。组装后的 asset policy 会阻止所有直接或 SPA-fallback shell alias 被嵌入 frame。 |
155
- | Other tools | Web Search、filesystem editor tools、MCP、skills、workflows、jobs 和 subagents | Search、文件、goal、skill 和 MCP 工具已移植;workflow 与 run_code 已在 isolated 构建中移植 | 复用上游 DeepSeek Web Search 及其 30 秒 tool-call timeout。上游 `workflow` 工具仅由 Dynamic Worker 运行时提供(isolated、Paid 计划构建);Direct 构建不注册该工具。由于 workerd 禁止在进程内使用 `eval`/`new Function`/`node:vm`,每次运行都会把脚本加载到独立的 Dynamic Worker 中执行,该 Worker 无出站网络,并受 30 秒 Worker Loader `cpuMs` 上限约束(由 Cloudflare 运行时强制执行,本地开发环境不执行);`agent()`、`phase()`、`log()` 经 RPC 桥回到 Durable Object,由其执行各项上限:同时最多运行 4 个子 agent(低于 Workers 六连接上限),单次运行最多启动 100 个子 agent,以限制子会话的行写入。上游 `run_code` 工具(`dsh-tools` PTC 模式,以 `both` 与原生工具并存)由同一运行时提供:每个程序先用 sucrase 去除类型,再在独立 Dynamic Worker 中运行,受 30 秒 `cpuMs` 上限约束且无出站网络;其中的 `tools.*` 调用经 RPC 桥回到 Durable Object,由其限制为每次运行最多 200 次(每次都是一次写入 session 事件的嵌套工具派发)、参数最多 256 KiB、单次结果最多 2 MiB,嵌套调用仍走审批。文件工具(read/write/edit/read_image)通过 `EdgeFileSystem` 适配 Computer VFS。Goal 工具(`ToolGoal`)作为上游 cordis 插件直接与 `GoalService` 组合。Skill 工具通过 `SkillRegistry` + `EdgeSkillProvider`(DO KV 存储);owner CRUD 接口 `GET/PUT/DELETE /api/skills`。MCP client(`dsh-mcp-client`)已安装;仅支持 Streamable HTTP transport(Workers 上不可用 stdio)。在 Settings → DSH Edge → MCP Servers 中配置服务器;工具名格式为 `mcp__<serverName>__<tool>`。逐个针对 Worker-compatible capabilities 增加其余工具,不宣称不可用的 host 行为。 |
155
+ | Other tools | Web Search、filesystem editor tools、MCP、skills、workflows、jobs 和 subagents | Search、文件、goal、skill 和 MCP 工具已移植;workflow 与 run_code 已在 isolated 构建中移植 | 复用上游 DeepSeek Web Search 及其 30 秒 tool-call timeout。上游 `workflow` 工具仅由 Dynamic Worker 运行时提供(isolated、Paid 计划构建);Direct 构建不注册该工具。由于 workerd 禁止在进程内使用 `eval`/`new Function`/`node:vm`,每次运行都会把脚本加载到独立的 Dynamic Worker 中执行,该 Worker 无出站网络,并受 30 秒 Worker Loader `cpuMs` 上限约束(由 Cloudflare 运行时强制执行,本地开发环境不执行);`agent()`、`phase()`、`log()` 经 RPC 桥回到 Durable Object,由其执行各项上限:同时最多运行 4 个子 agent(低于 Workers 六连接上限),单次运行最多启动 100 个子 agent,以限制子会话的行写入。上游 `run_code` 工具(`dsh-tools` PTC 模式)由同一运行时提供,但只提供给以上游 `ptc` agent 预设(PTC 模式)开始的会话。isolated 部署沿用上游 id 提供该预设,因此 Web 客户端显示其内置的双语名称。该预设通过生成的 SDK 呈现全部工具,并与上游一样不提供 `workflow`,因为 `run_code` 是它唯一的编排方式。默认的 `dsh-edge` 预设以原生方式呈现工具,并保留 `workflow`:每个程序先用 sucrase 去除类型,再在独立 Dynamic Worker 中运行,受 30 秒 `cpuMs` 上限约束且无出站网络;其中的 `tools.*` 调用经 RPC 桥回到 Durable Object,由其限制为每次运行最多 200 次(每次都是一次写入 session 事件的嵌套工具派发)、参数最多 256 KiB、单次结果最多 2 MiB,嵌套调用仍走审批。文件工具(read/write/edit/read_image)通过 `EdgeFileSystem` 适配 Computer VFS。Goal 工具(`ToolGoal`)作为上游 cordis 插件直接与 `GoalService` 组合。Skill 工具通过 `SkillRegistry` + `EdgeSkillProvider`(DO KV 存储);owner CRUD 接口 `GET/PUT/DELETE /api/skills`。MCP client(`dsh-mcp-client`)已安装;仅支持 Streamable HTTP transport(Workers 上不可用 stdio)。在 Settings → DSH Edge → MCP Servers 中配置服务器;工具名格式为 `mcp__<serverName>__<tool>`。逐个针对 Worker-compatible capabilities 增加其余工具,不宣称不可用的 host 行为。 |
156
156
  | Attachments | 本地 attachment storage、上游 image reference、composer、gallery、lightbox 与 provider conversion | 在原生 storage seam 上适配 | 原样复用上游 `AttachmentStore`、admission、协议、授权、UI 与 DeepSeek conversion。PNG/JPEG 不可变字节按 SHA-256 identity 存入新部署与 pre-attachment 部署使用的 64 MiB、按 512 KiB 分块的 DO backend,或存入已固定使用私有 R2 的部署的 R2;session event 只保留上游 ref。每个 owner instance 首次选择的 backend 会被固定,认领或升级不会让既有引用失联。 |
157
157
  | Goal tracking | `GoalService` 提供 create/edit/pause/resume/complete/clear mutation 与 GoalBar UI | 复用 | 作为 cordis 插件安装上游 `GoalService` 和 `ToolGoal`(直接组合)。浏览器 GoalBar mutation 通过 `TypertGatewayService` 路由到 `/api/goals/<method>`。`SessionProjectionCache` 在 DO 重启后持久化 goal 状态。 |
158
158
  | Context compaction | Token 计量、自动压缩和 session 标题生成 | 复用 | 直接安装上游 cordis 插件。上下文压缩、token 计量、工具结果修剪和自动 session 标题生成原样运行。 |
@@ -281,7 +281,7 @@ npx dsh-edge upgrade
281
281
  - **已存在的名称就是更新。** 安装器从 Worker 的 binding 读出它当前能做什么,提供 **Update it**(默认)、**Update and change what it can do**、**Use another name** 和 **Cancel**。选 "Update it" 即是确认:以相同能力部署新版本,并保留对话、文件、owner access key 与 DeepSeek key。安装器不会替你改名,也不会再次询问 secret。
282
282
  - **新名称会询问 agent 要做什么。** 选项逐级包含,价格放在第二位:
283
283
  - `Research and write`(direct 模式,默认):联网搜索、读网页、写文档,并通过 MCP 连接你的工具。可在 Workers Free 上运行。
284
- - `+ Analyze data and split big jobs`(isolated 模式):增加 `run_code`,用脚本处理你的数据;增加 `workflow`,把任务交给并行子 agent。需要 Workers Paid(每月 5 美元起)。
284
+ - `+ Analyze data and split big jobs`(isolated 模式):增加 `workflow`,把任务交给并行子 agent;在以 PTC 模式 agent 预设开始的会话中增加 `run_code`,用脚本处理你的数据(与上游一样,该预设不提供 `workflow`)。需要 Workers Paid(每月 5 美元起)。
285
285
  - `+ Work on code projects`(container 模式):增加用于 git、npm、python 的 Linux 容器。命令先在隔离 shell 中执行;只有当命令启动的程序不在轻量 shell 已验证的清单内、命令无法被可靠解析,或 agent 设置了 `linux: true` 时,才分流到容器。容器内最多同时运行两条命令。需要 Workers Paid,外加容器运行时长。
286
286
  - **调整能力。** 能力列表会标出当前选择。选择更少的能力时会二次确认,列出将被移除的内容,默认 No。离开 container 模式后,安装器会保留该 Worker 的 Container application 作为回退目标,并打印删除它的命令;登录确认新版本正常后运行该命令,即可停止 Container 计费。
287
287
  - **唯一一次确认。** 摘要列出实例能做什么、费用、账户、Worker、图片存储以及下面的默认值。使用临时账户时,确认即同时接受 Cloudflare 服务条款与隐私政策。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-edge",
3
- "version": "0.18.0-alpha.1",
3
+ "version": "0.18.0-alpha.2",
4
4
  "description": "Your DeepSeek Harness, anywhere — deploy a persistent personal coding agent to Cloudflare Workers in one command",
5
5
  "author": "pawaca",
6
6
  "license": "MIT",
package/scripts/cli.mjs CHANGED
@@ -212,11 +212,11 @@ export function createInstallerUi(
212
212
  ? [['Kept', KEPT_ON_UPDATE]]
213
213
  : [
214
214
  ['Owner key', 'generated and shown when installation finishes'],
215
- ['DeepSeek', 'add your API key later in Settings → Models'],
215
+ ['DeepSeek', 'add your API key when the web app asks, or later in Settings → Models'],
216
216
  ]),
217
217
  ]),
218
218
  ...(summary.mode === 'container'
219
- ? ['', 'The container sleeps after 10 idle minutes.', CONTAINER_ROLLOUT_NOTE]
219
+ ? ['', 'The container sleeps after 10 idle minutes by default; change it in Settings → DSH Edge.', CONTAINER_ROLLOUT_NOTE]
220
220
  : []),
221
221
  ...(summary.temporary
222
222
  ? [
@@ -298,7 +298,7 @@ export function createInstallerUi(
298
298
  newKey
299
299
  ? 'Enter the owner access key when prompted.'
300
300
  : 'Sign in with your existing owner access key.',
301
- ...(result.updated ? [] : ['Add your DeepSeek API key in Settings → Models.']),
301
+ ...(result.updated ? [] : ['Add your DeepSeek API key when the web app asks (or later in Settings → Models).']),
302
302
  ...(newKey ? ['Save the owner access key; you need it to sign in.'] : []),
303
303
  ]
304
304
  note([
@@ -391,8 +391,26 @@ export function parseWorkerExistence(result) {
391
391
  throw new Error(commandFailure('Could not check whether the Worker already exists', result))
392
392
  }
393
393
 
394
- function staleContainerCleanupCommand(workerName) {
395
- return `npx wrangler containers list, then npx wrangler containers delete <id> for ${containerApplicationName(workerName)}`
394
+ /**
395
+ * The command that removes a Worker's Container application after it leaves
396
+ * Container mode. Listing is read-only; when it fails or finds nothing, the
397
+ * owner is told how to find the id instead.
398
+ */
399
+ async function staleContainerCleanupCommand({ runWrangler, environment, profile, workerName, signal }) {
400
+ const name = containerApplicationName(workerName)
401
+ const manual = `npx wrangler containers list, then npx wrangler containers delete <id> for ${name}`
402
+ try {
403
+ const listed = await runWrangler(['containers', 'list', '--json', ...profileArgs(profile)], { environment, signal })
404
+ if (listed.status !== 0) return manual
405
+ const applications = JSON.parse(listed.stdout)
406
+ const ids = Array.isArray(applications)
407
+ ? applications.filter(application => application?.name === name).map(application => application.id)
408
+ : []
409
+ if (ids.length === 0 || ids.some(id => typeof id !== 'string' || !/^[0-9a-f-]{36}$/u.test(id))) return manual
410
+ return ids.map(id => ['npx wrangler containers delete', id, ...profileArgs(profile)].join(' ')).join(' && ')
411
+ } catch {
412
+ return manual
413
+ }
396
414
  }
397
415
 
398
416
  /**
@@ -787,9 +805,11 @@ export async function installEdge({
787
805
  // read back) cannot verify the replacement's runtime. The owner removes it
788
806
  // after signing in; its files live in the Durable Object, not the container.
789
807
  if (existing?.mode === 'container' && mode !== 'container') {
808
+ const command = await staleContainerCleanupCommand({
809
+ runWrangler, environment: commandEnvironment, profile, workerName, signal,
810
+ })
790
811
  ui.cleanupFailure(`The ${containerApplicationName(workerName)} Container application was kept for rollback. `
791
- + `Once you have signed in and the new version works, remove it to stop Container billing: ${
792
- staleContainerCleanupCommand(workerName)}.`)
812
+ + `Once you have signed in and the new version works, remove it to stop Container billing: ${command}`)
793
813
  }
794
814
  } catch (error) {
795
815
  primaryError = signal?.aborted ? abortReason(signal, 'Installation interrupted.') : error