@exagone313/dsh-podman 0.2.0-rc.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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/README.zh.md +41 -0
  4. package/cordis.patch.yml +17 -0
  5. package/dist/approval-reasons.d.ts +129 -0
  6. package/dist/approval-reasons.js +253 -0
  7. package/dist/approval-reasons.test.d.ts +1 -0
  8. package/dist/approval-reasons.test.js +115 -0
  9. package/dist/approval.d.ts +29 -0
  10. package/dist/approval.js +412 -0
  11. package/dist/approval.test.d.ts +1 -0
  12. package/dist/approval.test.js +462 -0
  13. package/dist/card-route.d.ts +15 -0
  14. package/dist/card-route.js +478 -0
  15. package/dist/card-route.test.d.ts +1 -0
  16. package/dist/card-route.test.js +181 -0
  17. package/dist/card-test-support.d.ts +15 -0
  18. package/dist/card-test-support.js +160 -0
  19. package/dist/client/ContainerCard.d.ts +6 -0
  20. package/dist/client/ContainerCard.js +79 -0
  21. package/dist/client/card-client.d.ts +6 -0
  22. package/dist/client/card-client.js +47 -0
  23. package/dist/client/card-protocol.d.ts +101 -0
  24. package/dist/client/card-protocol.js +10 -0
  25. package/dist/client/container-card-caches.d.ts +10 -0
  26. package/dist/client/container-card-caches.js +31 -0
  27. package/dist/client/container-card-controller.d.ts +107 -0
  28. package/dist/client/container-card-controller.js +207 -0
  29. package/dist/client/container-card-create-modal.d.ts +24 -0
  30. package/dist/client/container-card-create-modal.js +109 -0
  31. package/dist/client/container-card-directory-styles.d.ts +5 -0
  32. package/dist/client/container-card-directory-styles.js +246 -0
  33. package/dist/client/container-card-directory.d.ts +12 -0
  34. package/dist/client/container-card-directory.js +144 -0
  35. package/dist/client/container-card-editors.d.ts +30 -0
  36. package/dist/client/container-card-editors.js +116 -0
  37. package/dist/client/container-card-images.d.ts +37 -0
  38. package/dist/client/container-card-images.js +35 -0
  39. package/dist/client/container-card-paths.d.ts +20 -0
  40. package/dist/client/container-card-paths.js +88 -0
  41. package/dist/client/container-card-row.d.ts +30 -0
  42. package/dist/client/container-card-row.js +54 -0
  43. package/dist/client/container-card-secrets.d.ts +21 -0
  44. package/dist/client/container-card-secrets.js +71 -0
  45. package/dist/client/container-card-shared.d.ts +55 -0
  46. package/dist/client/container-card-shared.js +152 -0
  47. package/dist/client/container-card-styles.d.ts +21 -0
  48. package/dist/client/container-card-styles.js +143 -0
  49. package/dist/client/container-card-volumes.d.ts +12 -0
  50. package/dist/client/container-card-volumes.js +37 -0
  51. package/dist/client/container-card-workspace.d.ts +31 -0
  52. package/dist/client/container-card-workspace.js +24 -0
  53. package/dist/client/directory-picker.d.ts +12 -0
  54. package/dist/client/directory-picker.js +30 -0
  55. package/dist/client/index.d.ts +5 -0
  56. package/dist/client/index.js +4419 -0
  57. package/dist/client/locales.d.ts +9 -0
  58. package/dist/client/locales.js +370 -0
  59. package/dist/client/read-only-approval.d.ts +8 -0
  60. package/dist/client/read-only-approval.js +63 -0
  61. package/dist/client/slot-contract.d.ts +12 -0
  62. package/dist/client/slot-contract.js +4 -0
  63. package/dist/client/terminal-styles.d.ts +5 -0
  64. package/dist/client/terminal-styles.js +31 -0
  65. package/dist/client/tool-views.d.ts +8 -0
  66. package/dist/client/tool-views.js +241 -0
  67. package/dist/containers.test.d.ts +1 -0
  68. package/dist/containers.test.js +443 -0
  69. package/dist/daemons.test.d.ts +1 -0
  70. package/dist/daemons.test.js +153 -0
  71. package/dist/fs-provider.d.ts +23 -0
  72. package/dist/fs-provider.js +184 -0
  73. package/dist/fs-provider.test.d.ts +1 -0
  74. package/dist/fs-provider.test.js +260 -0
  75. package/dist/generated/version.d.ts +2 -0
  76. package/dist/generated/version.js +6 -0
  77. package/dist/grpc/proto/dshctl/v1/control.proto +133 -0
  78. package/dist/grpc/proto/dshguest/v1/guest.proto +79 -0
  79. package/dist/grpc/runtime-client.d.ts +5 -0
  80. package/dist/grpc/runtime-client.js +43 -0
  81. package/dist/guest-rpc.d.ts +62 -0
  82. package/dist/guest-rpc.js +391 -0
  83. package/dist/images.test.d.ts +1 -0
  84. package/dist/images.test.js +79 -0
  85. package/dist/index.d.ts +22 -0
  86. package/dist/index.js +140 -0
  87. package/dist/misc.test.d.ts +1 -0
  88. package/dist/misc.test.js +141 -0
  89. package/dist/mount-enums.d.ts +22 -0
  90. package/dist/mount-enums.js +81 -0
  91. package/dist/mount-input.d.ts +14 -0
  92. package/dist/mount-input.js +64 -0
  93. package/dist/mounts.test.d.ts +1 -0
  94. package/dist/mounts.test.js +408 -0
  95. package/dist/output-reader.d.ts +34 -0
  96. package/dist/output-reader.js +87 -0
  97. package/dist/paths.test.d.ts +1 -0
  98. package/dist/paths.test.js +74 -0
  99. package/dist/preferences.d.ts +6 -0
  100. package/dist/preferences.js +27 -0
  101. package/dist/project-path.d.ts +6 -0
  102. package/dist/project-path.js +73 -0
  103. package/dist/project-path.test.d.ts +1 -0
  104. package/dist/project-path.test.js +52 -0
  105. package/dist/prompts.d.ts +13 -0
  106. package/dist/prompts.js +149 -0
  107. package/dist/public.d.ts +4 -0
  108. package/dist/public.js +63 -0
  109. package/dist/read-only-shell.d.ts +14 -0
  110. package/dist/read-only-shell.js +150 -0
  111. package/dist/read-only-shell.test.d.ts +1 -0
  112. package/dist/read-only-shell.test.js +127 -0
  113. package/dist/secrets.test.d.ts +1 -0
  114. package/dist/secrets.test.js +181 -0
  115. package/dist/settings-commands.test.d.ts +1 -0
  116. package/dist/settings-commands.test.js +549 -0
  117. package/dist/settings-create.test.d.ts +1 -0
  118. package/dist/settings-create.test.js +394 -0
  119. package/dist/settings-mounts.test.d.ts +1 -0
  120. package/dist/settings-mounts.test.js +467 -0
  121. package/dist/settings-paths.test.d.ts +1 -0
  122. package/dist/settings-paths.test.js +65 -0
  123. package/dist/settings-schema.d.ts +14 -0
  124. package/dist/settings-schema.js +14 -0
  125. package/dist/settings-workspaces.test.d.ts +1 -0
  126. package/dist/settings-workspaces.test.js +265 -0
  127. package/dist/spill-store.d.ts +17 -0
  128. package/dist/spill-store.js +54 -0
  129. package/dist/spill-store.test.d.ts +1 -0
  130. package/dist/spill-store.test.js +59 -0
  131. package/dist/subprocess.d.ts +9 -0
  132. package/dist/subprocess.js +369 -0
  133. package/dist/subprocess.test.d.ts +1 -0
  134. package/dist/subprocess.test.js +234 -0
  135. package/dist/test-support.d.ts +328 -0
  136. package/dist/test-support.js +437 -0
  137. package/dist/tool-defs.d.ts +26 -0
  138. package/dist/tool-defs.js +188 -0
  139. package/dist/tool-handlers.d.ts +2 -0
  140. package/dist/tool-handlers.js +546 -0
  141. package/dist/tool-params.d.ts +803 -0
  142. package/dist/tool-params.js +494 -0
  143. package/dist/tool-schemas.d.ts +2 -0
  144. package/dist/tool-schemas.js +5 -0
  145. package/dist/tool-views.d.ts +2 -0
  146. package/dist/tool-views.js +56 -0
  147. package/dist/views.test.d.ts +1 -0
  148. package/dist/views.test.js +23 -0
  149. package/dist/volumes.test.d.ts +1 -0
  150. package/dist/volumes.test.js +40 -0
  151. package/dist/workspace-binding.d.ts +40 -0
  152. package/dist/workspace-binding.js +275 -0
  153. package/dist/workspace-binding.test.d.ts +1 -0
  154. package/dist/workspace-binding.test.js +337 -0
  155. package/docs/architecture.md +173 -0
  156. package/docs/architecture.zh.md +129 -0
  157. package/docs/configuration.md +160 -0
  158. package/docs/configuration.zh.md +147 -0
  159. package/docs/development.md +142 -0
  160. package/docs/development.zh.md +135 -0
  161. package/docs/install-dsh-and-dsh-podman.md +165 -0
  162. package/docs/install-dsh-and-dsh-podman.zh.md +145 -0
  163. package/docs/usage.md +393 -0
  164. package/docs/usage.zh.md +315 -0
  165. package/package.json +119 -0
@@ -0,0 +1,315 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
3
+
4
+ SPDX-License-Identifier: MIT
5
+ -->
6
+
7
+ # 使用
8
+
9
+ 本页面记录了面向模型的工具、设置卡片以及镜像模型。参见[架构](architecture.zh.md)了解各部分如何组合在一起。
10
+
11
+ ## 模型上下文
12
+
13
+ Harness 会添加一个提示词区段,指明其自身的磁盘检出目录,以便模型检查或扩展 DSH
14
+ 本身。该检出目录位于宿主机上,无法从工作区容器访问,因此 dsh-podman
15
+ 会从组装后的系统提示词中移除该区段;模型不会被告知一个具有误导性的路径。
16
+
17
+ dsh-podman 还会添加自己的提示词区段,说明内置的 shell
18
+ 与文件系统工具(`bash`、`read`、`write`、`edit`、`glob`、`grep`)在工作区的默认容器内运行,而非宿主机,并说明它们与
19
+ `container_*` 工具的关系。
20
+
21
+ ## 镜像模型
22
+
23
+ dsh-podman 将容器运行的镜像组织为三个层级:
24
+
25
+ - **原始镜像**是从互联网拉取的公共上游镜像,例如
26
+ `docker.io/library/ubuntu:latest`。基础镜像仓库固定了它们的完整引用;它们仅用作构建基础镜像时的
27
+ `FROM`,从不被直接引用。
28
+ - **基础镜像**是 dsh-podman
29
+ 提供的固定内置集合:`archlinux`(pacman)、`ubuntu`(apt)和
30
+ `alpine`(apk)。每个基础镜像由其原始镜像、dsh-podman
31
+ 拥有的默认软件包列表及其软件包管理器定义。默认情况下它们从原始镜像**本地构建**(安装默认软件包);当
32
+ `DSH_PODMAN_BASE_IMAGE_PREFIX` 指向某个镜像仓库(任何不以 `localhost/`
33
+ 开头的名称)时,它们改为被**拉取**。即使尚未构建/拉取,基础镜像也会列在设置 UI
34
+ 中,可从卡片重建或拉取,并且它们的短名称是保留的——它们不能被覆盖构建、重建或作为自定义镜像移除。
35
+ - **自定义镜像**是从**父镜像**——基础镜像或另一个自定义镜像——创建的用户构建镜像,继承其软件包管理器并在此基础上添加额外软件包。它们通过短名称引用(例如
36
+ `valkey`)。
37
+
38
+ 镜像引用(`imageId`、`parent`、`image`)**只能是短名称**(没有镜像仓库前缀,没有
39
+ `:tag`)。
40
+
41
+ ## 工具
42
+
43
+ 插件注册了以下面向模型的工具。标记 `✱`
44
+ 的工具需要审批(有些仅在特定参数下需要——已在其所在行注明)。容器工具作用于当前工作区的**逻辑容器名称**(`"default"`
45
+ 选择工作区的默认容器)。
46
+
47
+ ### 审批
48
+
49
+ 审批由插件自身通过 `tools/pre-execute`
50
+ 策略执行,该策略读取会话生效的权限旋钮(沙箱模式 + 审批策略):
51
+
52
+ - **Read Only** — 只有 get/list
53
+ 类工具可以运行(`image_list`、`image_get`、`container_list`、`container_read`、`container_glob`、`container_grep`、`container_mount_list`、`volume_list`、`secret_list`、`daemon_list`、`daemon_logs`);其他所有插件工具都被拒绝。当目标容器中所有带模式的挂载(项目与卷;tmpfs
54
+ 与机密不带模式)都已是 `read_only` 时,内置的文件与 shell
55
+ 工具(`write`、`edit`、`bash`,以及 Windows 上的
56
+ `pwsh`)及其容器对应工具(`container_bash`、`container_exec`、`container_write`、`container_edit`、`daemon_start`)可以运行——本应约束它们的
57
+ harness 沙箱在容器内被绕过。当某个读写挂载会阻止这些工具时,插件会通过 DSH
58
+ 的审批服务询问,提示中列出将被重新挂载为 `read_only`
59
+ 的挂载与保持不变的挂载,然后以只读挂载列表重建容器再运行该工具;拒绝该提示会拒绝此次调用。`read`、`glob`
60
+ 和 `grep` 始终可运行。
61
+ - **Workspace Write** — 带 `✱` 的工具通过 DSH
62
+ 的审批服务询问(调用会显示标准审批提示,当没有可用的审批通道时被拒绝);`container_start`
63
+ 仅在传入 `mounts` 时询问。
64
+ - **Full access** — 工具运行时不显示审批提示。
65
+
66
+ harness 的 `sandbox_permissions` 参数(例如 `bash`
67
+ 上的一次性沙箱放宽)会被接受但在此处没有效果:沙箱在容器内被绕过,因此插件会直接授予该升权而不询问。
68
+
69
+ 审批提示的原因是一句话,说明操作及其涉及的对象,并对每个标识符加引号——例如
70
+ `Add mount to container "web": volume "data" (read-only)`。它涵盖容器镜像、每个挂载的类型与目标路径、环境变量的键,以及解析后的文件路径。它会跟随界面语言:浏览器客户端将当前语言记录到插件设置中,并以持久化的语言偏好作为回退(两者都未设置时为英文)。策略拒绝——只读沙箱,或项目挂载上指定了目标路径——也会使用相同的语言。设置卡片操作是直接的控制调用,不进行门控。
71
+
72
+ `container_start`、`container_recreate` 和 `container_bash` 接受应用于容器(或
73
+ bash 进程)的 `env` 映射;`container_exec` 和 `daemon_start` 已经接受 `env`,而
74
+ `daemon_restart` 复用守护进程存储的环境。`container_start` 和
75
+ `container_recreate` 还接受 `secretEnv` 映射(环境变量名 →
76
+ 机密短名称),将现有机密附加到容器的环境——参见[机密](#机密)。在
77
+ `container_start` 和 `container_recreate` 上,省略 `env`(或
78
+ `secretEnv`)会保留容器存储的映射,而提供映射会完全替换它。环境变量不被视为机密,因此审批原因和
79
+ `container_list` 显示变量的**键**。以 `DSH_PODMAN`
80
+ 开头的键被保留并被拒绝,因为编排器将该命名空间用于 guest-agent 接线。
81
+
82
+ `container_bash` 和 `container_exec` 使用与内置 `bash`
83
+ 相同的命令可见环境运行:harness 托管的 `DSH_*`
84
+ 事实(`DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`、`DSH_WEB_URL`)及其非交互式终端覆盖项(`NO_COLOR`、`TERM=dumb`、`PAGER=cat`、`GIT_PAGER=cat`)。调用方的
85
+ `env` 条目会覆盖终端覆盖项,但无法取代托管的 `DSH_*` 事实。
86
+
87
+ ### 镜像
88
+
89
+ | 工具 | 参数 | 描述 |
90
+ | --------------------- | ------------------------------- | ------------------------------------------------------------------------------------ |
91
+ | `image_build` ✱ | `imageId`, `parent`, `packages` | 从基础或自定义父镜像及软件包列表构建新的自定义镜像 |
92
+ | `image_get` | `imageId` | 单个镜像的详细信息 |
93
+ | `image_list` | — | 列出已构建的工作区镜像,基础镜像在前 |
94
+ | `image_rebuild_all` ✱ | — | 确保每个基础镜像,然后按依赖顺序重建每个自定义镜像,跳过重建失败的任何镜像及其依赖项 |
95
+ | `image_rebuild` ✱ | `imageId` | 原地重建现有的自定义镜像 |
96
+ | `image_remove` ✱ | `imageId` | 移除已构建的镜像;当工作区或容器仍引用它时拒绝 |
97
+
98
+ ### 容器
99
+
100
+ | 工具 | 参数 | 描述 |
101
+ | ---------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
102
+ | `container_bash` | `container`, `command`, `description`, optional `workdir`, `timeoutMs`, `env`, `uid`, `gid`, `groups` | 运行 shell 命令 |
103
+ | `container_edit` | `container`, `file_path`, `old_string`, `new_string`, optional `replace_all` | 编辑文件 |
104
+ | `container_exec` | `container`, `argv`, `description`, optional `workdir`, `timeoutMs`, `env`, `uid`, `gid`, `groups` | 运行程序 |
105
+ | `container_glob` | `container`, `pattern`, optional `path` | 列出匹配模式的文件 |
106
+ | `container_grep` | `container`, `pattern`, optional `path`, `include` | 按正则表达式搜索文件 |
107
+ | `container_list` | — | 列出当前工作区的容器 |
108
+ | `container_read` | `container`, `file_path`, optional `offset`, `limit` | 读取文件 |
109
+ | `container_recreate` ✱ | `container`, optional `image`, `mounts`, `env`, `secretEnv`, `paths` | 重建容器,省略 `image` 时保留其当前镜像,可选地使用新的项目挂载、环境或 PATH 附加项 |
110
+ | `container_remove` ✱ | `container` | 移除容器(先优雅地停止其守护进程) |
111
+ | `container_start` ✱ | `container`, optional `image`, `mounts`, `env`, `secretEnv`, `paths` | 启动容器(省略 `image` 时使用默认镜像);仅在传入 `mounts` 时需要审批 |
112
+ | `container_write` | `container`, `file_path`, `content` | 写入文件 |
113
+
114
+ `container_bash`、`container_exec`、`container_read`、`container_write`、
115
+ `container_edit`、`container_glob` 和 `container_grep` 的参数与 harness 内置的
116
+ `bash`/`read`/`write`/`edit`/`glob`/`grep` 工具保持一致(外加 `container`
117
+ 目标),因此同一套参数可直接用于指定的容器。`container_read`、`container_write`
118
+ 和 `container_edit`
119
+ 还通过内置文件工具所用的同一文件系统提供者解析目标,因此行为一致:二进制文件会以相同的错误被拒绝,而先读后写保护(拒绝覆盖会话中未读取过的文件)对它们的作用与内置
120
+ `write` 和 `edit` 完全相同——`container_read` 会像内置 `read`
121
+ 一样,为其读取的路径满足该保护。已被读取但随后被删除的路径视为新文件:写入会重新创建它,而不会报告版本过期。插件还为它的每个工具注册了专用的
122
+ UI 行(图标、标题、摘要和结果正文),因此它们会像内置工具一样渲染,而不是显示为
123
+ 通用的 `Tool call` 行。
124
+
125
+ 路径和工作目录可以是绝对路径或相对路径。相对路径会相对于会话的工作目录解析,项目也正是挂载在容器中的该目录下。`container_read`、`container_write`
126
+ 和 `container_edit` 的 `file_path` 不能包含 `..`
127
+ 片段;工作目录可以包含,因为命令不受限于 projects 根目录。
128
+
129
+ 未指定工作目录时,`container_bash`、`container_exec` 和 `daemon_start`
130
+ 会在会话的工作目录中运行(与 harness 的 `bash`
131
+ 工具一致),`container_glob`/`container_grep`
132
+ 默认也在其中搜索。该目录必须已挂载到容器中——否则将使用 guest agent
133
+ 自身的工作目录。显式的 `workdir`(或 `path`)始终优先。
134
+
135
+ `container_bash` 和 `container_exec` 接受可选的 `uid`、`gid` 和 `groups`
136
+ 以其他用户身份运行命令。这些值只能是数字:容器的 `/etc/passwd` 和 `/etc/group`
137
+ 位于只读根文件系统上,因此名称无法解析。`groups`
138
+ 会替换进程的整个附加组集合,而应用任何身份都要求 guest agent 以 root
139
+ 运行——它正是如此。`HOME` 不受管理,因此以其他 uid 运行的命令会继承容器的
140
+ `HOME`(只读的 `/root`);请通过 `env` 将其指向可写目录。
141
+
142
+ 文件工具(`container_read`、`container_write`、`container_edit`)可以访问
143
+ 工作区的挂载:projects 根目录下的项目目录,以及任何 `volume` 和 `tmpfs`
144
+ 挂载在其绝对目标路径上的内容。`secret` 挂载不会通过文件 API
145
+ 暴露——机密值只能由容器内运行的进程读取。
146
+
147
+ ### 挂载与卷
148
+
149
+ | 工具 | 参数 | 描述 |
150
+ | -------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
151
+ | `container_mount_add` ✱ | `container`, optional `kind`, `project`, `destination`, `mode`, `volume`, `secret` | 添加挂载;`kind` 为 `project`(默认)、`tmpfs`、`volume` 或 `secret` |
152
+ | `container_mount_list` | `container` | 列出容器的挂载 |
153
+ | `container_mount_remove` ✱ | `container`, optional `kind`, `project`, `volume`, `destination`, `secret` | 移除挂载;通过 `kind` 及其标识字段指定(见下文) |
154
+ | `container_mount_update` ✱ | `container`, `mode`, optional `kind`, `project`, `volume`, `destination` | 更改项目或卷挂载的模式;标识方式同移除(见下文) |
155
+ | `volume_create` | `name` | 创建受管理的命名卷 |
156
+ | `volume_list` | — | 列出受管理的命名卷(短名称) |
157
+ | `volume_remove` ✱ | `name` | 移除受管理的命名卷;当容器仍在挂载它时拒绝 |
158
+
159
+ `project` 挂载将 projects 根目录下的一个路径绑定到容器(`team`,或项目内的目录
160
+ `team/src`);`tmpfs` 挂载可写的内存文件系统,`volume` 挂载 podman
161
+ 命名卷(首次使用时自动创建)——两者都位于任意绝对容器路径,绝不位于 projects
162
+ 根目录、`/tmp` 或其他保留路径之下。`secret`
163
+ 挂载将受管理的机密作为只读文件暴露在绝对容器路径(参见[机密](#机密))。
164
+
165
+ `project` 挂载不接受 `destination`:项目目录始终挂载在 projects
166
+ 根目录下与之对应的路径上。`destination` 仅适用于 `tmpfs`、`volume` 和 `secret`
167
+ 挂载。
168
+
169
+ 在设置卡片中,添加挂载与创建容器对话框的项目路径字段带有 **浏览…**
170
+ 按钮。它会打开一个目录选择器,通过 dsh 自身的主机侧目录列表(也就是 dsh
171
+ 工作区目录选择所用的同一服务)读取 projects
172
+ 根目录,而不经过编排器或容器。其布局与 dsh
173
+ 自带的目录浏览器一致:双栏、带箭头图标的面包屑、文件夹图标以及隐藏项开关。该对话框被限制在
174
+ projects 根目录内,生成的路径以该根目录为基准存储。
175
+
176
+ 移除挂载时,通过 `kind` 及该挂载自身的标识字段指定:项目挂载用
177
+ `project`,命名卷用 `volume`,机密用 `secret`,`tmpfs` 挂载用
178
+ `destination`。若某个标识字段匹配到多个挂载,请求会被拒绝,因此当同一卷或机密被挂载到多个位置时,还需一并传入
179
+ `destination`。`container_mount_list` 会报告准确的值。`kind` 可省略——会从
180
+ `secret` 或 `volume` 推断,否则为 `project`——但 `tmpfs`
181
+ 例外,它没有可推断的名称,必须显式指定。
182
+
183
+ `mode` 为 `read_only` 或 `read_write`,默认为
184
+ `read_only`,这样添加挂载永远不会授予未被请求的写权限。`tmpfs` 挂载始终为
185
+ `read_write`,`secret` 挂载不带 mode。工作区自身的项目挂载以 `read_write`
186
+ 创建;当会话不应修改项目时,用 `container_mount_update` 将其重新挂载为
187
+ `read_only`。
188
+
189
+ 更改挂载的模式应使用 `container_mount_update`,而不是
190
+ `container_mount_add`:重新添加已存在的挂载会被拒绝,而不会静默更改它。它的标识方式与
191
+ `container_mount_remove`
192
+ 完全相同(若某个标识字段匹配到多个挂载,请求会被拒绝),必须提供
193
+ `mode`,且仅适用于 `project` 和 `volume` 挂载——`tmpfs` 始终为
194
+ `read_write`,`secret` 挂载不带 mode。将模式更改为挂载已有的模式会被拒绝。
195
+
196
+ **命名容器**只携带创建它时指定的挂载:项目目录不会被自动挂载。**默认容器**
197
+ 始终保留其工作区项目挂载,并且无法移除——但可以用 `container_mount_update`
198
+ 将其重新挂载为 `read_only`。
199
+
200
+ 修改容器的挂载(`container_mount_add`/`container_mount_remove`/`container_mount_update`)或其机密环境变量(`container_secret_add`/`container_secret_remove`)会**重建**容器:其正在运行的进程(包括守护进程)会被终止。绑定挂载的卷中的数据会保留;`tmpfs`
201
+ 的内容不会。
202
+
203
+ ### PATH 附加项
204
+
205
+ | 工具 | 参数 | 描述 |
206
+ | ------------------------- | -------------------- | -------------------------------------------------------- |
207
+ | `container_path_set` ✱ | `container`, `paths` | 替换整个有序列表,第一项优先级最高;空列表表示清除 |
208
+ | `container_path_add` ✱ | `container`, `path` | 在最前面添加一个目录;已存在的条目会被移到最前 |
209
+ | `container_path_remove` ✱ | `container`, `path` | 移除一个已添加的目录;属于容器默认 PATH 的目录不会被移除 |
210
+
211
+ 每个条目都必须是绝对、词法干净的目录,且不含 `:`
212
+ 或换行符。该列表会被添加到容器自身 `PATH`(镜像的 `PATH`,即运行中的 guest agent
213
+ 所见)之前,作用于 agent 启动的每个命令:内置的 `bash`/`read`/`write`/`edit`
214
+ 工具、`container_bash`、`container_exec`、终端,以及之后启动的守护进程。容器不会被重建——运行中的
215
+ guest agent 会立即收到新列表,因此已在运行的守护进程仍使用旧的
216
+ `PATH`。容器被重建后会恢复持久化的列表。该列表也可以在创建、启动或重建容器时设置:通过设置模态框,或
217
+ `container_start` 和 `container_recreate` 的 `paths` 参数。
218
+
219
+ ### 机密
220
+
221
+ | 工具 | 参数 | 描述 |
222
+ | --------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
223
+ | `container_secret_add` ✱ | `container`, `env`, `secret` | 将机密作为环境变量附加到容器 |
224
+ | `container_secret_remove` ✱ | `container`, `env` | 从容器的环境变量中分离机密环境变量 |
225
+ | `secret_create` | `name`, optional `length`, `charset` | 创建具有**编排器生成的随机**值的机密(`length` 默认 32;`charset` 为 `alphanumeric` \| `hex` \| `base64url`) |
226
+ | `secret_list` | — | 列出受管理的机密(短名称) |
227
+ | `secret_remove` ✱ | `name` | 移除受管理的机密;当容器挂载它或将其作为环境变量附加时拒绝 |
228
+
229
+ 机密存储在 podman 的 `DSH_PODMAN_SECRET_PREFIX`(默认 `dsh-podman-`)下;工具和
230
+ UI 使用短名称。`secret_create` 的值由服务端用 `crypto/rand`
231
+ 生成,且**永不暴露**——没有读取工具。机密可以附加到容器上,既可以作为**挂载**(`container_mount_add kind="secret"` +
232
+ `secret` + `destination`,只读,位于绝不位于 projects
233
+ 根目录之下的绝对路径),也可以作为**环境变量**(`container_secret_add`;环境变量名不得以
234
+ `DSH_PODMAN`
235
+ 开头)。设置卡片可以用用户输入的内容**覆盖**机密(只写),但绝不读取它。
236
+
237
+ 挂载到某个路径的机密以 root 所有的**文件**(而非目录)形式创建,权限为除 root
238
+ 外一律拒绝,因此只有容器的默认(root)用户可以读取它;以其他 `uid`
239
+ 启动的守护进程无法读取挂载的机密。作为环境变量附加的机密会被 agent
240
+ 启动的每个进程继承,除非守护进程以 `inheritEnv=false`
241
+ 启动(参见[守护进程](#守护进程))。
242
+
243
+ ### 守护进程
244
+
245
+ | 工具 | 参数 | 描述 |
246
+ | ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
247
+ | `daemon_list` | `container` | 列出守护进程(包括其有效 `uid`/`gid` 和 `groups`) |
248
+ | `daemon_logs` | `container`, `name`, optional `tailBytes` | 查看守护进程 stdout/stderr 的尾部 |
249
+ | `daemon_restart` | `container`, `name` | 使用相同的命令、环境、用户重启守护进程 |
250
+ | `daemon_start` | `container`, `name`, `argv`, optional `cwd`, `env`, `inheritEnv`, `uid`, `gid`, `groups` | 启动后台守护进程;可选的 `uid`/`gid`/`groups` 以其他用户身份运行它 |
251
+ | `daemon_stop` | `container`, `name`, optional `signal` | 停止守护进程 |
252
+
253
+ 守护进程默认以容器用户身份运行。当只设置 `uid` 时,`gid`
254
+ 默认为相同值;两者都未设置时,守护进程在没有任何 uid/gid
255
+ 覆盖的情况下运行。`groups` 会替换进程的整个附加组集合。`daemon_list`
256
+ 报告每个守护进程的有效 `uid`/`gid` 和附加组 `groups`。
257
+ 使用已存在的名称启动守护进程时,会先停止该守护进程(如果它仍在运行)并将其替换;
258
+ 已停止的守护进程仍会列出,以便其日志仍可读取。守护进程位于容器的 guest agent
259
+ 中,不会在容器重建后保留(参见[挂载与卷](#挂载与卷))。
260
+
261
+ 守护进程默认继承容器的环境——即 guest agent 拥有的所有变量(包括用
262
+ `container_secret_add` 附加的环境机密),但会去掉保留的 `DSH_PODMAN`
263
+ 命名空间。传入 `inheritEnv=false`
264
+ 可将其隔离启动:此时它只接收(来自容器的)`PATH` 和 `HOME` 以及自身的
265
+ `env`,因此容器环境变量和机密环境变量都不可见。`daemon_restart`
266
+ 会沿用守护进程启动时的模式。
267
+
268
+ ## 容器管理 UI
269
+
270
+ 插件附带一个浏览器端,在 dsh **Settings → Plugins**
271
+ 页面注册一个卡片。该卡片列出编排器创建的 guest
272
+ 容器和已构建的镜像。没有容器的工作区会得到一个 **Create container**
273
+ 按钮,打开一个配置模态框——镜像、环境、挂载(project、tmpfs、volume 和
274
+ secret)、PATH 附加项以及机密环境变量——而已有容器的工作区通过同一个模态框提供
275
+ **Add container**
276
+ 按钮,用于添加额外的命名容器,模态框标题与打开它的按钮一致。在该模态框中,每个挂载的模式是一个下拉框(read-only/read-write),因此可以在创建时就把工作区项目挂载设为只读;tmpfs
277
+ 和 secret 挂载的模式是固定的(分别为 read-write 和
278
+ read-only),显示为不可编辑的下拉框。容器行显示其环境和机密环境变量及其挂载,允许编辑环境变量和添加/移除挂载(每次移除都会确认),以及将命名机密附加/分离到容器的环境变量;每行还提供
279
+ **Remove**、**Recreate**(相同镜像)和 **Recreate with
280
+ image**。每个工作区行还提供 **移除 Pod**,它会移除该工作区的
281
+ Pod、其所有容器以及编排器对应的记录(卷、机密和项目数据会保留);移除工作区的最后一个容器也会一并移除其
282
+ Pod,因此不会留下空的 Pod。每次打开 Plugins
283
+ 页面时卡片都会重新读取实时状态,其头部还有一个 **Reload this view**
284
+ 按钮。镜像部分可以重建单个镜像或按依赖顺序**重建全部**;**Build image**
285
+ 打开一个弹窗,包含 image-id/base-image 表单和用于软件包列表的 chip
286
+ 输入框(输入名称并按空格/逗号,或粘贴列表,以添加可移除的
287
+ chips)。卷和机密以独立的可展开行列出,每行都有自己的操作,**Create volume** /
288
+ **Create secret**
289
+ 打开弹窗表单(机密表单接受可选的长度;机密的值可以被覆盖,但绝不读取)。卡片操作是直接的控制调用,不受审批门控。
290
+
291
+ **软件包缓存**区段会报告每个已配置构建缓存(`DSH_PODMAN_HOST_PACMAN_CACHE`、`DSH_PODMAN_HOST_APT_CACHE`、`DSH_PODMAN_HOST_APK_CACHE`)的大小,并提供两个清理操作:**保留最新版本**会移除每个软件包除最新版本之外的所有缓存文件(连同其签名),**全部移除**会清空缓存。两者都是安全的——缓存的软件包只会被重新下载——且都不会在镜像构建进行时运行。
292
+
293
+ ## Podman 操作员模式
294
+
295
+ 插件附带一个名为 _Podman operator mode_(id `podman-ops`)的 **agent
296
+ 预设**。每次加载时,它都会将预设(重新)写入 harness
297
+ 的用户预设根目录(`~/.dsh/.agent-presets/podman-ops/`),覆盖任何本地副本,使发布的内容保持权威。它出现在会话的
298
+ agent 预设选择器中,紧挨着内置预设。
299
+
300
+ 该预设将聚焦 Podman 的人格与内置任务工具(`ask_user_question`、`todo_write`)和
301
+ `web_search`(web fetch 已禁用)组合在一起。它不挂载主机 shell、主机文件系统或
302
+ coding-agent 行(subagents、workflows、skills、goal、plan
303
+ mode、jobs)。插件自身的工具是全局的,全部保持可用,分为:
304
+
305
+ - **直接:**
306
+ `image_list`、`image_get`、`container_list`、`container_read`、`container_glob`、`container_grep`、`container_mount_list`、`volume_list`、`secret_list`、`secret_create`、`daemon_list`、`daemon_logs`、`daemon_stop`、`daemon_restart`、`container_start`(仅在传入
307
+ `mounts` 时询问)。
308
+ - **需审批**(通常的 `✱`
309
+ 工具):`image_build`、`image_rebuild`、`image_rebuild_all`、`image_remove`、`container_recreate`、`container_remove`、`container_mount_add`、`container_mount_remove`、`container_mount_update`、`container_path_set`、`container_path_add`、`container_path_remove`、`volume_remove`、`secret_remove`、`container_secret_add`、`container_secret_remove`。
310
+ - **仅在此预设中需审批:**
311
+ `container_bash`、`container_exec`、`container_write`、`container_edit`、`daemon_start`——因此一旦用户批准,agent
312
+ 就可以运行命令、编辑容器文件或启动守护进程,而无需这些工具在其他预设中询问。
313
+
314
+ 上述审批策略中的权限旋钮仍然适用(Read Only 只允许直接的 read/list 工具;Full
315
+ access 跳过每个提示)。
package/package.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "@exagone313/dsh-podman",
3
+ "version": "0.2.0-rc.2",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Exagone313/dsh-podman.git"
8
+ },
9
+ "type": "module",
10
+ "main": "./dist/index.js",
11
+ "exports": {
12
+ ".": "./dist/index.js",
13
+ "./client": {
14
+ "types": "./dist/client/index.d.ts",
15
+ "default": "./dist/client/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "dsh": {
20
+ "bundle": {
21
+ "patch": "./cordis.patch.yml"
22
+ },
23
+ "client": {
24
+ "inject": [
25
+ "@deepseek-ai/dsh-client-locale",
26
+ "@deepseek-ai/dsh-client-ui-renderer",
27
+ "@deepseek-ai/dsh-client-ui-settings",
28
+ "@deepseek-ai/dsh-client-ui-tool"
29
+ ],
30
+ "platform": "web"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "docs",
36
+ "cordis.patch.yml",
37
+ "README.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "node scripts/generate-version.mjs && tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/copy-proto.mjs && node scripts/build-client-bundle.mjs",
41
+ "typecheck": "node scripts/generate-version.mjs && tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
42
+ "test": "node --test dist/*.test.js",
43
+ "bump-version": "node scripts/bump-version.mjs"
44
+ },
45
+ "dependencies": {
46
+ "@grpc/grpc-js": "^1.14.4",
47
+ "@grpc/proto-loader": "^0.8.1"
48
+ },
49
+ "devDependencies": {
50
+ "@deepseek-ai/cordis": "^4.0.2",
51
+ "@deepseek-ai/dsh-api-remotes": "0.1.5-rc.2",
52
+ "@deepseek-ai/dsh-api-workspace-controller": "0.1.5-rc.2",
53
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
54
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
55
+ "@deepseek-ai/dsh-client-ui-approval": "0.1.5-rc.2",
56
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2",
57
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
58
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
59
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
60
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
61
+ "@deepseek-ai/dsh-client-ui-tool": "^0.1.5-rc.2",
62
+ "@deepseek-ai/schemastery": "^3.18.2",
63
+ "@types/node": "^22.0.0",
64
+ "@types/react": "^18.3.31",
65
+ "esbuild": "^0.28.2",
66
+ "typescript": "^7.0.2"
67
+ },
68
+ "engines": {
69
+ "node": ">=22"
70
+ },
71
+ "packageManager": "pnpm@12.4.1",
72
+ "publishConfig": {
73
+ "access": "public"
74
+ },
75
+ "peerDependencies": {
76
+ "@deepseek-ai/cordis": "^4.0.2",
77
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
78
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
79
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
80
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
81
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
82
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
83
+ "@deepseek-ai/dsh-client-ui-tool": "^0.1.5-rc.2",
84
+ "@deepseek-ai/schemastery": "*",
85
+ "react": "^18.2.0"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "@deepseek-ai/cordis": {
89
+ "optional": true
90
+ },
91
+ "@deepseek-ai/dsh-client-locale": {
92
+ "optional": true
93
+ },
94
+ "@deepseek-ai/dsh-client-store": {
95
+ "optional": true
96
+ },
97
+ "@deepseek-ai/dsh-client-ui-primitives": {
98
+ "optional": true
99
+ },
100
+ "@deepseek-ai/dsh-client-ui-renderer": {
101
+ "optional": true
102
+ },
103
+ "@deepseek-ai/dsh-client-ui-settings": {
104
+ "optional": true
105
+ },
106
+ "@deepseek-ai/dsh-client-ui-slots": {
107
+ "optional": true
108
+ },
109
+ "@deepseek-ai/dsh-client-ui-tool": {
110
+ "optional": true
111
+ },
112
+ "@deepseek-ai/schemastery": {
113
+ "optional": true
114
+ },
115
+ "react": {
116
+ "optional": true
117
+ }
118
+ }
119
+ }