@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.
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/README.zh.md +41 -0
- package/cordis.patch.yml +17 -0
- package/dist/approval-reasons.d.ts +129 -0
- package/dist/approval-reasons.js +253 -0
- package/dist/approval-reasons.test.d.ts +1 -0
- package/dist/approval-reasons.test.js +115 -0
- package/dist/approval.d.ts +29 -0
- package/dist/approval.js +412 -0
- package/dist/approval.test.d.ts +1 -0
- package/dist/approval.test.js +462 -0
- package/dist/card-route.d.ts +15 -0
- package/dist/card-route.js +478 -0
- package/dist/card-route.test.d.ts +1 -0
- package/dist/card-route.test.js +181 -0
- package/dist/card-test-support.d.ts +15 -0
- package/dist/card-test-support.js +160 -0
- package/dist/client/ContainerCard.d.ts +6 -0
- package/dist/client/ContainerCard.js +79 -0
- package/dist/client/card-client.d.ts +6 -0
- package/dist/client/card-client.js +47 -0
- package/dist/client/card-protocol.d.ts +101 -0
- package/dist/client/card-protocol.js +10 -0
- package/dist/client/container-card-caches.d.ts +10 -0
- package/dist/client/container-card-caches.js +31 -0
- package/dist/client/container-card-controller.d.ts +107 -0
- package/dist/client/container-card-controller.js +207 -0
- package/dist/client/container-card-create-modal.d.ts +24 -0
- package/dist/client/container-card-create-modal.js +109 -0
- package/dist/client/container-card-directory-styles.d.ts +5 -0
- package/dist/client/container-card-directory-styles.js +246 -0
- package/dist/client/container-card-directory.d.ts +12 -0
- package/dist/client/container-card-directory.js +144 -0
- package/dist/client/container-card-editors.d.ts +30 -0
- package/dist/client/container-card-editors.js +116 -0
- package/dist/client/container-card-images.d.ts +37 -0
- package/dist/client/container-card-images.js +35 -0
- package/dist/client/container-card-paths.d.ts +20 -0
- package/dist/client/container-card-paths.js +88 -0
- package/dist/client/container-card-row.d.ts +30 -0
- package/dist/client/container-card-row.js +54 -0
- package/dist/client/container-card-secrets.d.ts +21 -0
- package/dist/client/container-card-secrets.js +71 -0
- package/dist/client/container-card-shared.d.ts +55 -0
- package/dist/client/container-card-shared.js +152 -0
- package/dist/client/container-card-styles.d.ts +21 -0
- package/dist/client/container-card-styles.js +143 -0
- package/dist/client/container-card-volumes.d.ts +12 -0
- package/dist/client/container-card-volumes.js +37 -0
- package/dist/client/container-card-workspace.d.ts +31 -0
- package/dist/client/container-card-workspace.js +24 -0
- package/dist/client/directory-picker.d.ts +12 -0
- package/dist/client/directory-picker.js +30 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +4419 -0
- package/dist/client/locales.d.ts +9 -0
- package/dist/client/locales.js +370 -0
- package/dist/client/read-only-approval.d.ts +8 -0
- package/dist/client/read-only-approval.js +63 -0
- package/dist/client/slot-contract.d.ts +12 -0
- package/dist/client/slot-contract.js +4 -0
- package/dist/client/terminal-styles.d.ts +5 -0
- package/dist/client/terminal-styles.js +31 -0
- package/dist/client/tool-views.d.ts +8 -0
- package/dist/client/tool-views.js +241 -0
- package/dist/containers.test.d.ts +1 -0
- package/dist/containers.test.js +443 -0
- package/dist/daemons.test.d.ts +1 -0
- package/dist/daemons.test.js +153 -0
- package/dist/fs-provider.d.ts +23 -0
- package/dist/fs-provider.js +184 -0
- package/dist/fs-provider.test.d.ts +1 -0
- package/dist/fs-provider.test.js +260 -0
- package/dist/generated/version.d.ts +2 -0
- package/dist/generated/version.js +6 -0
- package/dist/grpc/proto/dshctl/v1/control.proto +133 -0
- package/dist/grpc/proto/dshguest/v1/guest.proto +79 -0
- package/dist/grpc/runtime-client.d.ts +5 -0
- package/dist/grpc/runtime-client.js +43 -0
- package/dist/guest-rpc.d.ts +62 -0
- package/dist/guest-rpc.js +391 -0
- package/dist/images.test.d.ts +1 -0
- package/dist/images.test.js +79 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +140 -0
- package/dist/misc.test.d.ts +1 -0
- package/dist/misc.test.js +141 -0
- package/dist/mount-enums.d.ts +22 -0
- package/dist/mount-enums.js +81 -0
- package/dist/mount-input.d.ts +14 -0
- package/dist/mount-input.js +64 -0
- package/dist/mounts.test.d.ts +1 -0
- package/dist/mounts.test.js +408 -0
- package/dist/output-reader.d.ts +34 -0
- package/dist/output-reader.js +87 -0
- package/dist/paths.test.d.ts +1 -0
- package/dist/paths.test.js +74 -0
- package/dist/preferences.d.ts +6 -0
- package/dist/preferences.js +27 -0
- package/dist/project-path.d.ts +6 -0
- package/dist/project-path.js +73 -0
- package/dist/project-path.test.d.ts +1 -0
- package/dist/project-path.test.js +52 -0
- package/dist/prompts.d.ts +13 -0
- package/dist/prompts.js +149 -0
- package/dist/public.d.ts +4 -0
- package/dist/public.js +63 -0
- package/dist/read-only-shell.d.ts +14 -0
- package/dist/read-only-shell.js +150 -0
- package/dist/read-only-shell.test.d.ts +1 -0
- package/dist/read-only-shell.test.js +127 -0
- package/dist/secrets.test.d.ts +1 -0
- package/dist/secrets.test.js +181 -0
- package/dist/settings-commands.test.d.ts +1 -0
- package/dist/settings-commands.test.js +549 -0
- package/dist/settings-create.test.d.ts +1 -0
- package/dist/settings-create.test.js +394 -0
- package/dist/settings-mounts.test.d.ts +1 -0
- package/dist/settings-mounts.test.js +467 -0
- package/dist/settings-paths.test.d.ts +1 -0
- package/dist/settings-paths.test.js +65 -0
- package/dist/settings-schema.d.ts +14 -0
- package/dist/settings-schema.js +14 -0
- package/dist/settings-workspaces.test.d.ts +1 -0
- package/dist/settings-workspaces.test.js +265 -0
- package/dist/spill-store.d.ts +17 -0
- package/dist/spill-store.js +54 -0
- package/dist/spill-store.test.d.ts +1 -0
- package/dist/spill-store.test.js +59 -0
- package/dist/subprocess.d.ts +9 -0
- package/dist/subprocess.js +369 -0
- package/dist/subprocess.test.d.ts +1 -0
- package/dist/subprocess.test.js +234 -0
- package/dist/test-support.d.ts +328 -0
- package/dist/test-support.js +437 -0
- package/dist/tool-defs.d.ts +26 -0
- package/dist/tool-defs.js +188 -0
- package/dist/tool-handlers.d.ts +2 -0
- package/dist/tool-handlers.js +546 -0
- package/dist/tool-params.d.ts +803 -0
- package/dist/tool-params.js +494 -0
- package/dist/tool-schemas.d.ts +2 -0
- package/dist/tool-schemas.js +5 -0
- package/dist/tool-views.d.ts +2 -0
- package/dist/tool-views.js +56 -0
- package/dist/views.test.d.ts +1 -0
- package/dist/views.test.js +23 -0
- package/dist/volumes.test.d.ts +1 -0
- package/dist/volumes.test.js +40 -0
- package/dist/workspace-binding.d.ts +40 -0
- package/dist/workspace-binding.js +275 -0
- package/dist/workspace-binding.test.d.ts +1 -0
- package/dist/workspace-binding.test.js +337 -0
- package/docs/architecture.md +173 -0
- package/docs/architecture.zh.md +129 -0
- package/docs/configuration.md +160 -0
- package/docs/configuration.zh.md +147 -0
- package/docs/development.md +142 -0
- package/docs/development.zh.md +135 -0
- package/docs/install-dsh-and-dsh-podman.md +165 -0
- package/docs/install-dsh-and-dsh-podman.zh.md +145 -0
- package/docs/usage.md +393 -0
- package/docs/usage.zh.md +315 -0
- package/package.json +119 -0
package/docs/usage.md
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Usage
|
|
8
|
+
|
|
9
|
+
This page documents the model-facing tools, the settings card, and the image
|
|
10
|
+
model. See [Architecture](architecture.md) for how the pieces fit together.
|
|
11
|
+
|
|
12
|
+
## Model context
|
|
13
|
+
|
|
14
|
+
The harness adds a prompt section naming its own on-disk checkout so the model
|
|
15
|
+
can inspect or extend DSH itself. That checkout lives on the host and is not
|
|
16
|
+
reachable from the workspace container, so dsh-podman removes the section from
|
|
17
|
+
the assembled system prompt; the model is never told a misleading path.
|
|
18
|
+
|
|
19
|
+
dsh-podman also adds its own prompt section clarifying that the built-in shell
|
|
20
|
+
and filesystem tools (`bash`, `read`, `write`, `edit`, `glob`, `grep`) run
|
|
21
|
+
inside the workspace's default container rather than on the host, and how they
|
|
22
|
+
relate to the `container_*` tools.
|
|
23
|
+
|
|
24
|
+
## Image model
|
|
25
|
+
|
|
26
|
+
dsh-podman organizes the images its containers run into three tiers:
|
|
27
|
+
|
|
28
|
+
- **Primitive images** are public upstream images pulled from the internet, e.g.
|
|
29
|
+
`docker.io/library/ubuntu:latest`. The base registry pins their full
|
|
30
|
+
references; they serve only as the `FROM` when building a base image and are
|
|
31
|
+
never referenced directly.
|
|
32
|
+
- **Base images** are the fixed, built-in set provided by dsh-podman:
|
|
33
|
+
`archlinux` (pacman), `ubuntu` (apt), and `alpine` (apk). Each is defined by
|
|
34
|
+
its primitive, a dsh-podman-owned default package list, and its package
|
|
35
|
+
manager. By default they are **built locally** from the primitive (installing
|
|
36
|
+
the default packages); when `DSH_PODMAN_BASE_IMAGE_PREFIX` points at a
|
|
37
|
+
registry (anything not starting with `localhost/`), they are **pulled**
|
|
38
|
+
instead. Base images are listed in the settings UI even when not yet
|
|
39
|
+
built/pulled, are rebuilt or pulled from the card, and their short names are
|
|
40
|
+
reserved — they cannot be built over, rebuilt, or removed as custom images.
|
|
41
|
+
- **Custom images** are user-built images created from a **parent** — a base
|
|
42
|
+
image or another custom image — inheriting its package manager and adding
|
|
43
|
+
extra packages on top. They are referenced by their short name (e.g.
|
|
44
|
+
`valkey`).
|
|
45
|
+
|
|
46
|
+
Image references (`imageId`, `parent`, `image`) are **short names only** (no
|
|
47
|
+
registry prefix, no `:tag`).
|
|
48
|
+
|
|
49
|
+
## Tools
|
|
50
|
+
|
|
51
|
+
The plugin registers the following model-facing tools. Tools marked `✱` require
|
|
52
|
+
approval (some only under certain parameters — noted in their row). Container
|
|
53
|
+
tools operate on a **logical container name** of the current workspace
|
|
54
|
+
(`"default"` selects the workspace's default container).
|
|
55
|
+
|
|
56
|
+
### Approval
|
|
57
|
+
|
|
58
|
+
Approval is enforced by the plugin itself through a `tools/pre-execute` policy
|
|
59
|
+
that reads the session's effective permission knobs (sandbox mode + approval
|
|
60
|
+
policy):
|
|
61
|
+
|
|
62
|
+
- **Read Only** — only the get/list tools run (`image_list`, `image_get`,
|
|
63
|
+
`container_list`, `container_read`, `container_glob`, `container_grep`,
|
|
64
|
+
`container_mount_list`, `volume_list`, `secret_list`, `daemon_list`,
|
|
65
|
+
`daemon_logs`); every other plugin tool is denied. The built-in file and shell
|
|
66
|
+
tools (`write`, `edit`, `bash`, and `pwsh` on Windows) and their container
|
|
67
|
+
counterparts (`container_bash`, `container_exec`, `container_write`,
|
|
68
|
+
`container_edit`, `daemon_start`) run when every mount of the target container
|
|
69
|
+
that carries a mode (project and volume; tmpfs and secrets do not) is already
|
|
70
|
+
`read_only` — the harness sandbox that would confine them is bypassed inside
|
|
71
|
+
the container. When a read-write mount would block one of them, the plugin
|
|
72
|
+
asks through DSH's approval service with a prompt listing the mounts it would
|
|
73
|
+
remount `read_only` and those it keeps, then recreates the container with the
|
|
74
|
+
read-only list before running the tool; a rejected prompt denies the call.
|
|
75
|
+
`read`, `glob`, and `grep` always run.
|
|
76
|
+
- **Workspace Write** — the `✱` tools ask through DSH's approval service (the
|
|
77
|
+
call shows the standard approval prompt and is denied when no approval channel
|
|
78
|
+
is available); `container_start` asks only when `mounts` is passed.
|
|
79
|
+
- **Full access** — tools run without approval prompts.
|
|
80
|
+
|
|
81
|
+
The harness's `sandbox_permissions` argument (a one-shot sandbox widening, e.g.
|
|
82
|
+
on `bash`) is accepted but has no effect here: the sandbox is bypassed inside
|
|
83
|
+
the container, so the plugin grants the escalation without prompting.
|
|
84
|
+
|
|
85
|
+
The prompt's reason is a full sentence naming the action and the objects it
|
|
86
|
+
touches, quoting every identifier — for example
|
|
87
|
+
`Add mount to container "web": volume "data" (read-only)`. It covers the
|
|
88
|
+
container image, each mount's kind and destination, the environment variable
|
|
89
|
+
keys, and the resolved file path. It follows the UI language: the browser client
|
|
90
|
+
records the active locale in the plugin settings, with the durable locale
|
|
91
|
+
preference as the fallback (English when neither is set). A policy denial — a
|
|
92
|
+
read-only sandbox, or a destination on a project mount — is reported in the same
|
|
93
|
+
language. The settings-card actions are direct control calls and are not gated.
|
|
94
|
+
|
|
95
|
+
`container_start`, `container_recreate`, and `container_bash` accept an `env`
|
|
96
|
+
map applied to the container (or the bash process); `container_exec` and
|
|
97
|
+
`daemon_start` already accept `env`, and `daemon_restart` reuses a daemon's
|
|
98
|
+
stored environment. `container_start` and `container_recreate` also accept a
|
|
99
|
+
`secretEnv` map (env var name → secret short name) that attaches existing
|
|
100
|
+
secrets to the container's environment — see [Secrets](#secrets). On
|
|
101
|
+
`container_start` and `container_recreate`, an omitted `env` (or `secretEnv`)
|
|
102
|
+
keeps the container's stored map, while a provided map replaces it entirely.
|
|
103
|
+
Environment variables are not treated as secrets, so the approval reason and
|
|
104
|
+
`container_list` show the variable **keys**. Keys starting with `DSH_PODMAN` are
|
|
105
|
+
reserved and rejected, since the orchestrator uses that namespace for
|
|
106
|
+
guest-agent wiring.
|
|
107
|
+
|
|
108
|
+
`container_bash` and `container_exec` run with the same command-visible
|
|
109
|
+
environment as the built-in `bash`: the harness's managed `DSH_*` facts
|
|
110
|
+
(`DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`, `DSH_WEB_URL`) and its
|
|
111
|
+
non-interactive terminal overrides (`NO_COLOR`, `TERM=dumb`, `PAGER=cat`,
|
|
112
|
+
`GIT_PAGER=cat`). A caller's `env` entry beats an override but cannot displace a
|
|
113
|
+
managed `DSH_*` fact.
|
|
114
|
+
|
|
115
|
+
### Images
|
|
116
|
+
|
|
117
|
+
| Tool | Params | Description |
|
|
118
|
+
| --------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
119
|
+
| `image_build` ✱ | `imageId`, `parent`, `packages` | Build a new custom image from a base or custom parent and package list |
|
|
120
|
+
| `image_get` | `imageId` | Details for one image |
|
|
121
|
+
| `image_list` | — | List the built workspace images, base images first |
|
|
122
|
+
| `image_rebuild_all` ✱ | — | Ensure every base, then rebuild every custom image in dependency order, skipping any image whose rebuild fails and its dependents |
|
|
123
|
+
| `image_rebuild` ✱ | `imageId` | Rebuild an existing custom image in place |
|
|
124
|
+
| `image_remove` ✱ | `imageId` | Remove a built image; refused while a workspace or container still references it |
|
|
125
|
+
|
|
126
|
+
### Containers
|
|
127
|
+
|
|
128
|
+
| Tool | Params | Description |
|
|
129
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
130
|
+
| `container_bash` | `container`, `command`, `description`, optional `workdir`, `timeoutMs`, `env`, `uid`, `gid`, `groups` | Run a shell command |
|
|
131
|
+
| `container_edit` | `container`, `file_path`, `old_string`, `new_string`, optional `replace_all` | Edit a file |
|
|
132
|
+
| `container_exec` | `container`, `argv`, `description`, optional `workdir`, `timeoutMs`, `env`, `uid`, `gid`, `groups` | Run a program |
|
|
133
|
+
| `container_glob` | `container`, `pattern`, optional `path` | List files matching a pattern |
|
|
134
|
+
| `container_grep` | `container`, `pattern`, optional `path`, `include` | Search files for a regex |
|
|
135
|
+
| `container_list` | — | List the containers of the current workspace |
|
|
136
|
+
| `container_read` | `container`, `file_path`, optional `offset`, `limit` | Read a file |
|
|
137
|
+
| `container_recreate` ✱ | `container`, optional `image`, `mounts`, `env`, `secretEnv`, `paths` | Recreate a container, keeping its current image when `image` is omitted, optionally with new project mounts, environment, or PATH additions |
|
|
138
|
+
| `container_remove` ✱ | `container` | Remove a container (stops its daemons gracefully first) |
|
|
139
|
+
| `container_start` ✱ | `container`, optional `image`, `mounts`, `env`, `secretEnv`, `paths` | Start a container (default image when `image` is omitted); approval required only when `mounts` is passed |
|
|
140
|
+
| `container_write` | `container`, `file_path`, `content` | Write a file |
|
|
141
|
+
|
|
142
|
+
The `container_bash`, `container_exec`, `container_read`, `container_write`,
|
|
143
|
+
`container_edit`, `container_glob` and `container_grep` arguments mirror the
|
|
144
|
+
harness's built-in `bash`/`read`/`write`/`edit`/`glob`/`grep` tools (plus the
|
|
145
|
+
`container` target), so the same vocabulary works against a chosen container.
|
|
146
|
+
`container_read`, `container_write` and `container_edit` also resolve their
|
|
147
|
+
target through the same filesystem provider as the built-in file tools, so they
|
|
148
|
+
share their behavior: binary files are refused with the same error, and the
|
|
149
|
+
read-before-write guard (refusing to overwrite a file that was not read in the
|
|
150
|
+
session) applies to them exactly as it does to the built-in `write` and `edit` —
|
|
151
|
+
a `container_read` satisfies that guard for the path it read, just like the
|
|
152
|
+
built-in `read`. A path that was read and has since been removed is a new file:
|
|
153
|
+
writing it creates it again instead of reporting a stale version. The plugin
|
|
154
|
+
also registers a dedicated UI row for every one of its tools (icon, title,
|
|
155
|
+
summary, and result body), so they render like the built-in tools rather than as
|
|
156
|
+
a generic `Tool call` row.
|
|
157
|
+
|
|
158
|
+
Paths and working directories may be absolute or relative. A relative value is
|
|
159
|
+
resolved against the session's working directory, which is also where the
|
|
160
|
+
project is mounted inside the container. A `file_path` on `container_read`,
|
|
161
|
+
`container_write` and `container_edit` may not contain a `..` segment; working
|
|
162
|
+
directories may, since commands are not confined to the projects root.
|
|
163
|
+
|
|
164
|
+
When no working directory is given, `container_bash`, `container_exec` and
|
|
165
|
+
`daemon_start` run in the session's working directory (like the harness's `bash`
|
|
166
|
+
tool), and `container_glob`/`container_grep` search it by default. That
|
|
167
|
+
directory must be mounted in the container — otherwise the guest agent's own
|
|
168
|
+
working directory is used. An explicit `workdir` (or `path`) takes precedence.
|
|
169
|
+
|
|
170
|
+
`container_bash` and `container_exec` accept an optional `uid`, `gid` and
|
|
171
|
+
`groups` to run the command as another user. The values are numeric only: the
|
|
172
|
+
container's `/etc/passwd` and `/etc/group` live on the read-only rootfs, so
|
|
173
|
+
names never resolve. `groups` replaces the process's whole supplementary set,
|
|
174
|
+
and applying any identity requires the guest agent to run as root, which it
|
|
175
|
+
does. `HOME` is not managed, so a command run as another uid inherits the
|
|
176
|
+
container's `HOME` (the read-only `/root`); pass `env` to point it at a writable
|
|
177
|
+
directory.
|
|
178
|
+
|
|
179
|
+
The file tools (`container_read`, `container_write`, `container_edit`) can reach
|
|
180
|
+
the workspace's mounts: the project directory under the projects root, as well
|
|
181
|
+
as any `volume` and `tmpfs` mounts at their absolute destinations. `secret`
|
|
182
|
+
mounts are not exposed through the file API — the secret value is only readable
|
|
183
|
+
by processes running inside the container.
|
|
184
|
+
|
|
185
|
+
### Mounts and volumes
|
|
186
|
+
|
|
187
|
+
| Tool | Params | Description |
|
|
188
|
+
| -------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
189
|
+
| `container_mount_add` ✱ | `container`, optional `kind`, `project`, `destination`, `mode`, `volume`, `secret` | Add a mount; `kind` is `project` (default), `tmpfs`, `volume`, or `secret` |
|
|
190
|
+
| `container_mount_list` | `container` | List the container's mounts |
|
|
191
|
+
| `container_mount_remove` ✱ | `container`, optional `kind`, `project`, `volume`, `destination`, `secret` | Remove a mount; identify it by `kind` plus its handle (see below) |
|
|
192
|
+
| `container_mount_update` ✱ | `container`, `mode`, optional `kind`, `project`, `volume`, `destination` | Change a project or volume mount's mode; identify it like removal (below) |
|
|
193
|
+
| `volume_create` | `name` | Create a managed named volume |
|
|
194
|
+
| `volume_list` | — | List the managed named volumes (short names) |
|
|
195
|
+
| `volume_remove` ✱ | `name` | Remove a managed named volume; refused while a container still mounts it |
|
|
196
|
+
|
|
197
|
+
A `project` mount binds a path under the projects root (`team`, or `team/src`
|
|
198
|
+
for a directory inside it); `tmpfs` mounts a writable in-memory filesystem and
|
|
199
|
+
`volume` mounts a podman named volume (auto-created on first use) — both at an
|
|
200
|
+
arbitrary absolute container path, never under the projects root, `/tmp`, or
|
|
201
|
+
another reserved path. A `secret` mount exposes a managed secret as a read-only
|
|
202
|
+
file at an absolute container path (see [Secrets](#secrets)).
|
|
203
|
+
|
|
204
|
+
Project mounts do not take a `destination`: a project directory is always
|
|
205
|
+
mounted at its mirrored path under the projects root. `destination` applies only
|
|
206
|
+
to `tmpfs`, `volume` and `secret` mounts.
|
|
207
|
+
|
|
208
|
+
In the settings card, the project path field of the add-mount and
|
|
209
|
+
create-container dialogs has a **Browse…** button. It opens a directory picker
|
|
210
|
+
that reads the projects root through dsh's own host-side directory listing — the
|
|
211
|
+
same service behind dsh's workspace directory selection — not through the
|
|
212
|
+
orchestrator or a container. Its layout follows dsh's own directory browser: two
|
|
213
|
+
columns, chevron breadcrumbs, folder icons, and a hidden-entry toggle. The
|
|
214
|
+
dialog is confined to the projects root, and the path it produces is stored
|
|
215
|
+
relative to that root.
|
|
216
|
+
|
|
217
|
+
Removing a mount names it by `kind` plus that mount's own handle: `project` for
|
|
218
|
+
a project mount, `volume` for a named volume, `secret` for a secret, and
|
|
219
|
+
`destination` for a `tmpfs` mount. A handle that matches more than one mount is
|
|
220
|
+
rejected, so pass `destination` as well when a volume or secret is mounted more
|
|
221
|
+
than once. `container_mount_list` reports the exact values. `kind` is optional —
|
|
222
|
+
inferred from `secret` or `volume`, otherwise `project` — except for `tmpfs`,
|
|
223
|
+
which has no name to infer from and must be named explicitly.
|
|
224
|
+
|
|
225
|
+
`mode` is `read_only` or `read_write`, and defaults to `read_only` so that
|
|
226
|
+
adding a mount never grants write access that was not asked for. `tmpfs` mounts
|
|
227
|
+
are always `read_write`, and `secret` mounts take no mode. The workspace's own
|
|
228
|
+
project mount is created `read_write`; remount it `read_only` with
|
|
229
|
+
`container_mount_update` when a session should not modify the project.
|
|
230
|
+
|
|
231
|
+
Changing a mount's mode is `container_mount_update`, not `container_mount_add`:
|
|
232
|
+
re-adding a mount that already exists is rejected rather than silently changing
|
|
233
|
+
it. It identifies the mount exactly like `container_mount_remove` (a handle that
|
|
234
|
+
matches more than one mount is rejected), requires `mode`, and applies only to
|
|
235
|
+
`project` and `volume` mounts — `tmpfs` is always `read_write` and `secret`
|
|
236
|
+
mounts carry no mode. A mode change to the mode the mount already has is
|
|
237
|
+
rejected.
|
|
238
|
+
|
|
239
|
+
A **named container** carries exactly the mounts it was created with: the
|
|
240
|
+
workspace project directory is not mounted automatically. The **default
|
|
241
|
+
container** always keeps its workspace project mount, which cannot be removed —
|
|
242
|
+
but it can be remounted `read_only` with `container_mount_update`.
|
|
243
|
+
|
|
244
|
+
Mutating a container's mounts
|
|
245
|
+
(`container_mount_add`/`container_mount_remove`/`container_mount_update`) or its
|
|
246
|
+
secret environment variables (`container_secret_add`/`container_secret_remove`)
|
|
247
|
+
**recreates** the container: its running processes, including daemons, are
|
|
248
|
+
terminated. Data in bind-mounted volumes persists; `tmpfs` contents do not.
|
|
249
|
+
|
|
250
|
+
### PATH additions
|
|
251
|
+
|
|
252
|
+
| Tool | Params | Description |
|
|
253
|
+
| ------------------------- | -------------------- | ------------------------------------------------------------------------------- |
|
|
254
|
+
| `container_path_set` ✱ | `container`, `paths` | Replace the whole ordered list, first entry highest priority; empty clears it |
|
|
255
|
+
| `container_path_add` ✱ | `container`, `path` | Prepend one directory; an existing entry moves to the front |
|
|
256
|
+
| `container_path_remove` ✱ | `container`, `path` | Remove one added directory; a directory of the container's default PATH is kept |
|
|
257
|
+
|
|
258
|
+
Every entry must be an absolute, lexically clean directory with no `:` or
|
|
259
|
+
newline. The list is prepended to the container's own `PATH` (the image's, as
|
|
260
|
+
the running guest agent sees it) for every command the agent starts: the
|
|
261
|
+
built-in `bash`/`read`/`write`/`edit` tools, `container_bash`, `container_exec`,
|
|
262
|
+
the terminal, and daemons started afterwards. Nothing is recreated — the running
|
|
263
|
+
guest agent receives the new list immediately, so daemons already running keep
|
|
264
|
+
their old `PATH`. A recreated container restores the persisted list. The list
|
|
265
|
+
can also be set when a container is created, started, or recreated, through the
|
|
266
|
+
settings modal or the `paths` argument of `container_start` and
|
|
267
|
+
`container_recreate`.
|
|
268
|
+
|
|
269
|
+
### Secrets
|
|
270
|
+
|
|
271
|
+
| Tool | Params | Description |
|
|
272
|
+
| --------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
273
|
+
| `container_secret_add` ✱ | `container`, `env`, `secret` | Attach a secret to a container as an environment variable |
|
|
274
|
+
| `container_secret_remove` ✱ | `container`, `env` | Detach a secret environment variable from a container |
|
|
275
|
+
| `secret_create` | `name`, optional `length`, `charset` | Create a secret with an **orchestrator-generated random** value (`length` default 32; `charset` `alphanumeric` \| `hex` \| `base64url`) |
|
|
276
|
+
| `secret_list` | — | List the managed secrets (short names) |
|
|
277
|
+
| `secret_remove` ✱ | `name` | Remove a managed secret; refused while a container mounts it or attaches it as an environment variable |
|
|
278
|
+
|
|
279
|
+
Secrets are stored in podman under `DSH_PODMAN_SECRET_PREFIX` (default
|
|
280
|
+
`dsh-podman-`); the tools and UI use short names. `secret_create` values are
|
|
281
|
+
generated server-side with `crypto/rand` and **never exposed** — there is no
|
|
282
|
+
read tool. A secret can be attached to a container either as a **mount**
|
|
283
|
+
(`container_mount_add kind="secret"` + `secret` + `destination`, read-only, at
|
|
284
|
+
an absolute path never under the projects root) or as an **environment
|
|
285
|
+
variable** (`container_secret_add`; the env var name must not start with
|
|
286
|
+
`DSH_PODMAN`). The settings card can **overwrite** a secret with user-typed
|
|
287
|
+
content (write-only) but never reads it.
|
|
288
|
+
|
|
289
|
+
A secret mounted at a path is created as a root-owned **file** (not a directory)
|
|
290
|
+
with permissions that deny everyone but root, so only the container's default
|
|
291
|
+
(root) user can read it; a daemon started with a different `uid` cannot read a
|
|
292
|
+
mounted secret. A secret attached as an environment variable is inherited by
|
|
293
|
+
every process the agent starts unless the daemon is started with
|
|
294
|
+
`inheritEnv=false` (see [Daemons](#daemons)).
|
|
295
|
+
|
|
296
|
+
### Daemons
|
|
297
|
+
|
|
298
|
+
| Tool | Params | Description |
|
|
299
|
+
| ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
300
|
+
| `daemon_list` | `container` | List the daemons (including their effective `uid`/`gid` and `groups`) |
|
|
301
|
+
| `daemon_logs` | `container`, `name`, optional `tailBytes` | Tail a daemon's stdout/stderr |
|
|
302
|
+
| `daemon_restart` | `container`, `name` | Restart a daemon with the same command, environment, and user |
|
|
303
|
+
| `daemon_start` | `container`, `name`, `argv`, optional `cwd`, `env`, `inheritEnv`, `uid`, `gid`, `groups` | Start a background daemon; optional `uid`/`gid`/`groups` run it as another user |
|
|
304
|
+
| `daemon_stop` | `container`, `name`, optional `signal` | Stop a daemon |
|
|
305
|
+
|
|
306
|
+
Daemons run as the container user by default. When only `uid` is set, `gid`
|
|
307
|
+
defaults to the same value; when neither is set, the daemon runs without any
|
|
308
|
+
uid/gid override. `groups` replaces the process's whole supplementary set.
|
|
309
|
+
`daemon_list` reports the effective `uid`/`gid` and supplementary `groups` of
|
|
310
|
+
each daemon. Starting a daemon with a name that already exists stops that daemon
|
|
311
|
+
first (when it is still running) and replaces it; stopped daemons stay listed so
|
|
312
|
+
their logs remain readable. Daemons live in the container's guest agent and do
|
|
313
|
+
not survive a container recreate (see
|
|
314
|
+
[Mounts and volumes](#mounts-and-volumes)).
|
|
315
|
+
|
|
316
|
+
A daemon inherits the container's environment by default — every variable the
|
|
317
|
+
guest agent has, including environment secrets attached with
|
|
318
|
+
`container_secret_add` — minus the reserved `DSH_PODMAN` namespace. Pass
|
|
319
|
+
`inheritEnv=false` to start it isolated: it then receives only `PATH` and `HOME`
|
|
320
|
+
(from the container) plus its own `env`, so container and secret environment
|
|
321
|
+
variables are not visible. `daemon_restart` replays the mode the daemon was
|
|
322
|
+
started with.
|
|
323
|
+
|
|
324
|
+
## Container management UI
|
|
325
|
+
|
|
326
|
+
The plugin ships a browser half that registers a card in the dsh **Settings →
|
|
327
|
+
Plugins** page. The card lists the orchestrator-created guest containers and the
|
|
328
|
+
built images. A workspace with no container gets a **Create container** button
|
|
329
|
+
that opens a configuration modal — image, environment, mounts (project, tmpfs,
|
|
330
|
+
volume, and secret), PATH additions, and secret environment variables — and
|
|
331
|
+
workspaces that already have containers offer an **Add container** button for
|
|
332
|
+
additional, named containers through the same modal, which is titled after the
|
|
333
|
+
button that opened it. In that modal each mount's mode is a dropdown
|
|
334
|
+
(read-only/read-write), so the workspace project mount can be created read-only
|
|
335
|
+
in one step; tmpfs and secret mounts are fixed (read-write and read-only
|
|
336
|
+
respectively) and show a disabled dropdown. Container rows show their
|
|
337
|
+
environment and secret-environment variables and their mounts, let you edit
|
|
338
|
+
environment variables and add/remove mounts (each removal is confirmed), and
|
|
339
|
+
attach/detach named secrets to a container's environment variables; each row
|
|
340
|
+
also offers **Remove**, **Recreate** (same image), and **Recreate with image**.
|
|
341
|
+
Every workspace row also offers **Remove pod**, which removes the workspace's
|
|
342
|
+
pod, all of its containers, and the orchestrator's record for it (volumes,
|
|
343
|
+
secrets, and project data are kept); removing a workspace's last container
|
|
344
|
+
removes its pod as well, so an empty pod is never left behind. The card re-reads
|
|
345
|
+
the live state whenever the Plugins page is opened, and its header has a
|
|
346
|
+
**Reload this view** button. The images section can rebuild a single image or
|
|
347
|
+
**rebuild all** in dependency order; **Build image** opens a popup with an
|
|
348
|
+
image-id/base-image form and a chip input for the package list (type a name and
|
|
349
|
+
press space/comma, or paste a list, to add removable chips). Volumes and secrets
|
|
350
|
+
are listed as individual expandable rows, each with its own actions, and
|
|
351
|
+
**Create volume** / **Create secret** open popup forms (the secret form takes an
|
|
352
|
+
optional length; a secret's value can be overwritten, never read). Card actions
|
|
353
|
+
are direct control calls and are not approval-gated.
|
|
354
|
+
|
|
355
|
+
A **Package caches** section reports the size of every configured build cache
|
|
356
|
+
(`DSH_PODMAN_HOST_PACMAN_CACHE`, `DSH_PODMAN_HOST_APT_CACHE`,
|
|
357
|
+
`DSH_PODMAN_HOST_APK_CACHE`) and offers two cleanup actions: **Keep latest
|
|
358
|
+
versions** removes every cached package file except the newest of each package
|
|
359
|
+
(and the signature it carried), and **Remove all** empties the caches. Both are
|
|
360
|
+
safe — a cached package is only ever re-downloaded — and neither runs while an
|
|
361
|
+
image build is in progress.
|
|
362
|
+
|
|
363
|
+
## Podman operator mode
|
|
364
|
+
|
|
365
|
+
The plugin ships an **agent preset** named _Podman operator mode_ (id
|
|
366
|
+
`podman-ops`). On every load it (re)writes the preset into the harness's
|
|
367
|
+
user-presets root (`~/.dsh/.agent-presets/podman-ops/`), overwriting any local
|
|
368
|
+
copy so the shipped content stays authoritative. It appears in the session's
|
|
369
|
+
agent-preset picker next to the shipped presets.
|
|
370
|
+
|
|
371
|
+
The preset composes a Podman-focused persona with the built-in task tools
|
|
372
|
+
(`ask_user_question`, `todo_write`) and `web_search` (web fetch disabled). It
|
|
373
|
+
does not mount the host shell, host filesystem, or the coding-agent rows
|
|
374
|
+
(subagents, workflows, skills, goal, plan mode, jobs). The plugin's own tools
|
|
375
|
+
are global and all remain available, split as:
|
|
376
|
+
|
|
377
|
+
- **Direct:** `image_list`, `image_get`, `container_list`, `container_read`,
|
|
378
|
+
`container_glob`, `container_grep`, `container_mount_list`, `volume_list`,
|
|
379
|
+
`secret_list`, `secret_create`, `daemon_list`, `daemon_logs`, `daemon_stop`,
|
|
380
|
+
`daemon_restart`, `container_start` (asks only when `mounts` is passed).
|
|
381
|
+
- **Approval-gated** (the usual `✱` tools): `image_build`, `image_rebuild`,
|
|
382
|
+
`image_rebuild_all`, `image_remove`, `container_recreate`, `container_remove`,
|
|
383
|
+
`container_mount_add`, `container_mount_remove`, `container_mount_update`,
|
|
384
|
+
`container_path_set`, `container_path_add`, `container_path_remove`,
|
|
385
|
+
`volume_remove`, `secret_remove`, `container_secret_add`,
|
|
386
|
+
`container_secret_remove`.
|
|
387
|
+
- **Approval-gated only in this preset:** `container_bash`, `container_exec`,
|
|
388
|
+
`container_write`, `container_edit`, `daemon_start` — so the agent can run
|
|
389
|
+
commands, edit container files, or start daemons once the user approves,
|
|
390
|
+
without those tools asking in other presets.
|
|
391
|
+
|
|
392
|
+
The permission knobs in the approval policy above still apply (Read Only allows
|
|
393
|
+
only the direct read/list tools; Full access skips every prompt).
|