@exagone313/dsh-podman 0.2.0-rc.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/README.zh.md +14 -2
- package/cordis.patch.yml +58 -0
- package/dist/approval-reasons.d.ts +3 -4
- package/dist/approval-reasons.js +40 -49
- package/dist/approval-reasons.test.js +43 -15
- package/dist/approval.js +27 -10
- package/dist/approval.test.js +165 -34
- package/dist/card-route.d.ts +3 -2
- package/dist/card-route.js +154 -42
- package/dist/card-route.test.js +85 -43
- package/dist/card-test-support.js +39 -66
- package/dist/client/ContainerCard.d.ts +1 -1
- package/dist/client/ContainerCard.js +92 -50
- package/dist/client/card-protocol.d.ts +6 -3
- package/dist/client/container-card-caches.js +11 -6
- package/dist/client/container-card-controller.d.ts +16 -18
- package/dist/client/container-card-controller.js +92 -40
- package/dist/client/container-card-create-modal.d.ts +2 -2
- package/dist/client/container-card-create-modal.js +13 -5
- package/dist/client/container-card-default-env.d.ts +10 -0
- package/dist/client/container-card-default-env.js +69 -0
- package/dist/client/container-card-directory.d.ts +2 -2
- package/dist/client/container-card-directory.js +12 -4
- package/dist/client/container-card-editors.d.ts +3 -3
- package/dist/client/container-card-editors.js +104 -32
- package/dist/client/container-card-images.d.ts +4 -4
- package/dist/client/container-card-images.js +19 -6
- package/dist/client/container-card-paths.d.ts +3 -3
- package/dist/client/container-card-paths.js +18 -11
- package/dist/client/container-card-row.d.ts +10 -6
- package/dist/client/container-card-row.js +82 -13
- package/dist/client/container-card-secrets.d.ts +3 -3
- package/dist/client/container-card-secrets.js +8 -6
- package/dist/client/container-card-shared.d.ts +4 -17
- package/dist/client/container-card-shared.js +9 -25
- package/dist/client/container-card-styles.d.ts +2 -8
- package/dist/client/container-card-styles.js +14 -52
- package/dist/client/container-card-volumes.d.ts +2 -2
- package/dist/client/container-card-volumes.js +9 -5
- package/dist/client/container-card-workspace.d.ts +8 -6
- package/dist/client/container-card-workspace.js +9 -3
- package/dist/client/directory-picker.js +2 -1
- package/dist/client/index.js +13141 -2036
- package/dist/client/locales.d.ts +6 -1
- package/dist/client/locales.js +106 -38
- package/dist/client/podman-terminal-guide.d.ts +11 -0
- package/dist/client/podman-terminal-guide.js +208 -0
- package/dist/client/podman-terminal-title.d.ts +7 -0
- package/dist/client/podman-terminal-title.js +30 -0
- package/dist/client/podman-terminal.d.ts +26 -0
- package/dist/client/podman-terminal.js +412 -0
- package/dist/client/read-only-approval.d.ts +2 -2
- package/dist/client/read-only-approval.js +13 -3
- package/dist/client/slot-contract.d.ts +0 -11
- package/dist/client/terminal-preference.d.ts +26 -0
- package/dist/client/terminal-preference.js +67 -0
- package/dist/client/terminal-protocol.d.ts +85 -0
- package/dist/client/terminal-protocol.js +14 -0
- package/dist/client/terminal-tab.d.ts +60 -0
- package/dist/client/terminal-tab.js +35 -0
- package/dist/client/terminal-targets.d.ts +33 -0
- package/dist/client/terminal-targets.js +65 -0
- package/dist/client/terminal-titles.d.ts +23 -0
- package/dist/client/terminal-titles.js +52 -0
- package/dist/client/terminal-transport.d.ts +63 -0
- package/dist/client/terminal-transport.js +207 -0
- package/dist/client/tool-views.js +215 -49
- package/dist/container-env.d.ts +11 -0
- package/dist/container-env.js +96 -0
- package/dist/container-env.test.d.ts +1 -0
- package/dist/container-env.test.js +93 -0
- package/dist/containers.test.js +196 -18
- package/dist/daemons.test.js +18 -4
- package/dist/dsh-version.d.ts +1 -0
- package/dist/dsh-version.js +46 -0
- package/dist/env-rows.d.ts +14 -0
- package/dist/env-rows.js +52 -0
- package/dist/env-rows.test.d.ts +1 -0
- package/dist/env-rows.test.js +43 -0
- package/dist/fs-provider.d.ts +1 -0
- package/dist/fs-provider.js +26 -12
- package/dist/fs-provider.test.js +51 -10
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +2 -2
- package/dist/grpc/proto/dshctl/v1/control.proto +11 -1
- package/dist/grpc/proto/dshguest/v1/guest.proto +5 -0
- package/dist/grpc/runtime-client.d.ts +3 -1
- package/dist/grpc/runtime-client.js +90 -17
- package/dist/guest-rpc.d.ts +15 -19
- package/dist/guest-rpc.js +289 -70
- package/dist/guest-rpc.test.d.ts +1 -0
- package/dist/guest-rpc.test.js +241 -0
- package/dist/guest-terminal.d.ts +30 -0
- package/dist/guest-terminal.js +196 -0
- package/dist/images.test.js +10 -3
- package/dist/index.d.ts +11 -16
- package/dist/index.js +42 -36
- package/dist/locales.test.d.ts +1 -0
- package/dist/locales.test.js +34 -0
- package/dist/misc.test.js +38 -10
- package/dist/mount-enums.js +2 -1
- package/dist/mount-input.d.ts +2 -0
- package/dist/mount-input.js +38 -22
- package/dist/mounts.test.js +81 -11
- package/dist/naming.test.d.ts +1 -0
- package/dist/naming.test.js +28 -0
- package/dist/output-reader.js +36 -4
- package/dist/package-deps.test.d.ts +1 -0
- package/dist/package-deps.test.js +49 -0
- package/dist/paths.test.js +4 -2
- package/dist/plugin-meta.test.d.ts +1 -0
- package/dist/plugin-meta.test.js +29 -0
- package/dist/project-path.js +33 -7
- package/dist/project-path.test.js +14 -0
- package/dist/prompts.d.ts +0 -4
- package/dist/prompts.js +15 -84
- package/dist/read-only-shell.js +3 -1
- package/dist/read-only-shell.test.js +80 -25
- package/dist/runtime-client.test.d.ts +1 -0
- package/dist/runtime-client.test.js +53 -0
- package/dist/secrets.test.js +21 -6
- package/dist/settings-commands.test.js +61 -14
- package/dist/settings-create.test.js +32 -10
- package/dist/settings-default-env.test.d.ts +1 -0
- package/dist/settings-default-env.test.js +175 -0
- package/dist/settings-mounts.test.js +46 -7
- package/dist/settings-schema.d.ts +6 -11
- package/dist/settings-schema.js +4 -8
- package/dist/settings-schema.test.d.ts +1 -0
- package/dist/settings-schema.test.js +35 -0
- package/dist/settings-workspaces.test.js +38 -63
- package/dist/spill-store.d.ts +1 -0
- package/dist/spill-store.js +17 -4
- package/dist/spill-store.test.js +31 -8
- package/dist/subprocess.d.ts +4 -0
- package/dist/subprocess.js +148 -162
- package/dist/subprocess.test.js +118 -8
- package/dist/terminal-preference.test.d.ts +1 -0
- package/dist/terminal-preference.test.js +62 -0
- package/dist/terminal-route.d.ts +10 -0
- package/dist/terminal-route.js +270 -0
- package/dist/terminal-route.test.d.ts +1 -0
- package/dist/terminal-route.test.js +265 -0
- package/dist/terminal-sessions.d.ts +69 -0
- package/dist/terminal-sessions.js +252 -0
- package/dist/terminal-shells.d.ts +22 -0
- package/dist/terminal-shells.js +99 -0
- package/dist/terminal-tab.test.d.ts +1 -0
- package/dist/terminal-tab.test.js +58 -0
- package/dist/terminal-targets.test.d.ts +1 -0
- package/dist/terminal-targets.test.js +43 -0
- package/dist/terminal-titles.test.d.ts +1 -0
- package/dist/terminal-titles.test.js +36 -0
- package/dist/test-support.d.ts +35 -5
- package/dist/test-support.js +91 -29
- package/dist/tool-defs.js +19 -4
- package/dist/tool-handlers.js +80 -28
- package/dist/tool-params.js +19 -5
- package/dist/version-compat.d.ts +4 -0
- package/dist/version-compat.js +29 -0
- package/dist/version-compat.test.d.ts +1 -0
- package/dist/version-compat.test.js +27 -0
- package/dist/volumes.test.js +1 -1
- package/dist/workspace-binding.d.ts +19 -10
- package/dist/workspace-binding.js +252 -78
- package/dist/workspace-binding.test.js +202 -27
- package/docs/architecture.md +71 -20
- package/docs/architecture.zh.md +62 -12
- package/docs/configuration.md +61 -20
- package/docs/configuration.zh.md +53 -19
- package/docs/development-prompt.md +72 -0
- package/docs/development-prompt.zh.md +69 -0
- package/docs/development.md +184 -29
- package/docs/development.zh.md +165 -26
- package/docs/install-dsh-and-dsh-podman.md +24 -21
- package/docs/install-dsh-and-dsh-podman.zh.md +20 -21
- package/docs/uninstall.md +83 -0
- package/docs/uninstall.zh.md +79 -0
- package/docs/update.md +120 -0
- package/docs/update.zh.md +114 -0
- package/docs/usage.md +108 -61
- package/docs/usage.zh.md +76 -38
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +58 -25
- package/quadlet/dsh-podman-orchestrator.container +46 -0
- package/quadlet/dsh.container +35 -0
- package/LICENSE.pkg +0 -17103
- package/dist/preferences.d.ts +0 -6
- package/dist/preferences.js +0 -27
package/dist/approval.test.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
// SPDX-License-Identifier: MIT
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
|
-
import {
|
|
7
|
-
import { tmpdir } from "node:os";
|
|
8
|
-
import { join } from "node:path";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
9
7
|
import { presetExec, sessionExec, testReadSession } from "./test-support.js";
|
|
10
|
-
import {
|
|
8
|
+
import { approvalDecision, isSandboxEscalation, preExecutePolicy, READ_ONLY_TOOLS, SANDBOX_ESCALATION_REASON_PREFIX, summarizeArgs, TOOLS, } from "./index.js";
|
|
9
|
+
// The bundle patch that declares the Podman operator preset.
|
|
10
|
+
function podmanOpsPatch() {
|
|
11
|
+
return readFileSync(new URL("../cordis.patch.yml", import.meta.url), "utf8");
|
|
12
|
+
}
|
|
11
13
|
test("isSandboxEscalation claims only the harness's sandbox escalation", () => {
|
|
12
14
|
assert.equal(SANDBOX_ESCALATION_REASON_PREFIX, "escalate sandbox to ");
|
|
13
15
|
assert.equal(isSandboxEscalation({
|
|
@@ -130,7 +132,19 @@ test("summarizeArgs renders the approval reason for each gated tool", () => {
|
|
|
130
132
|
assert.equal(summarizeArgs("image_build", {
|
|
131
133
|
imageId: "dev",
|
|
132
134
|
parent: "archlinux",
|
|
133
|
-
packages: [
|
|
135
|
+
packages: [
|
|
136
|
+
"git",
|
|
137
|
+
"curl",
|
|
138
|
+
"tmux",
|
|
139
|
+
"vim",
|
|
140
|
+
"zsh",
|
|
141
|
+
"openssh",
|
|
142
|
+
"jq",
|
|
143
|
+
"ripgrep",
|
|
144
|
+
"make",
|
|
145
|
+
"cc",
|
|
146
|
+
"go",
|
|
147
|
+
],
|
|
134
148
|
}), 'Build image "dev" from "archlinux" with packages: git, curl, tmux, vim, zsh, openssh, jq, ripgrep, +3 more.');
|
|
135
149
|
assert.equal(summarizeArgs("image_rebuild", { imageId: "valkey" }), 'Rebuild image "valkey".');
|
|
136
150
|
assert.equal(summarizeArgs("image_rebuild_all", {}), "Rebuild all images.");
|
|
@@ -247,7 +261,12 @@ test("read-only permission allows get/list tools and denies the rest", async ()
|
|
|
247
261
|
assert.equal(delegated, true, `${name} must be allowed under read-only`);
|
|
248
262
|
assert.equal(result.kind, "allow");
|
|
249
263
|
}
|
|
250
|
-
for (const name of [
|
|
264
|
+
for (const name of [
|
|
265
|
+
"image_build",
|
|
266
|
+
"container_start",
|
|
267
|
+
"container_remove",
|
|
268
|
+
"volume_remove",
|
|
269
|
+
]) {
|
|
251
270
|
const result = (await preExecutePolicy(sessionExec(name), () => Promise.resolve({ kind: "allow" }), undefined, undefined, testReadSession));
|
|
252
271
|
assert.equal(result.kind, "deny", `${name} must be denied under read-only`);
|
|
253
272
|
assert.ok(result.reason.includes("read-only"), `${name} deny reason`);
|
|
@@ -270,7 +289,9 @@ test("read-only denies the built-in file and shell tools", async () => {
|
|
|
270
289
|
}
|
|
271
290
|
});
|
|
272
291
|
test("workspace-write and full access delegate the built-in file and shell tools", async () => {
|
|
273
|
-
for (const facts of [{ mode: "workspace-write", policy: "ask" }, {
|
|
292
|
+
for (const facts of [{ mode: "workspace-write", policy: "ask" }, {
|
|
293
|
+
policy: "never",
|
|
294
|
+
}]) {
|
|
274
295
|
for (const name of ["write", "edit", "bash", "pwsh"]) {
|
|
275
296
|
let delegated = false;
|
|
276
297
|
const result = (await preExecutePolicy(sessionExec(name, facts), () => {
|
|
@@ -322,10 +343,18 @@ test("read-only wins over a never approval policy", async () => {
|
|
|
322
343
|
});
|
|
323
344
|
test("podman-ops preset asks for its approval-gated tools only", async () => {
|
|
324
345
|
const workspaceWrite = { mode: "workspace-write", policy: "ask" };
|
|
325
|
-
const asked = (await preExecutePolicy(presetExec("container_bash", "podman-ops", {
|
|
346
|
+
const asked = (await preExecutePolicy(presetExec("container_bash", "podman-ops", {
|
|
347
|
+
container: "valkey-ctr",
|
|
348
|
+
command: "valkey-cli ping",
|
|
349
|
+
}, workspaceWrite), () => Promise.resolve({ kind: "allow" }), undefined, undefined, testReadSession));
|
|
326
350
|
assert.equal(asked.kind, "ask");
|
|
327
351
|
assert.equal(asked.reason, 'Run a shell command in container "valkey-ctr": valkey-cli ping');
|
|
328
|
-
const daemon = (await preExecutePolicy(presetExec("daemon_start", "podman-ops", {
|
|
352
|
+
const daemon = (await preExecutePolicy(presetExec("daemon_start", "podman-ops", {
|
|
353
|
+
container: "valkey-ctr",
|
|
354
|
+
name: "v1",
|
|
355
|
+
argv: ["valkey-server"],
|
|
356
|
+
uid: 1001,
|
|
357
|
+
}, workspaceWrite), () => Promise.resolve({ kind: "allow" }), undefined, undefined, testReadSession));
|
|
329
358
|
assert.equal(daemon.kind, "ask");
|
|
330
359
|
assert.equal(daemon.reason, 'Start daemon "v1" in container "valkey-ctr": valkey-server (uid 1001)');
|
|
331
360
|
});
|
|
@@ -350,10 +379,17 @@ test("podman-ops keeps open tools ungated and respects permissions", async () =>
|
|
|
350
379
|
}, undefined, undefined, testReadSession));
|
|
351
380
|
assert.equal(delegated, true, "image_list must stay open in podman-ops");
|
|
352
381
|
assert.equal(open.kind, "allow");
|
|
353
|
-
const denied = (await preExecutePolicy(presetExec("container_bash", "podman-ops", {
|
|
382
|
+
const denied = (await preExecutePolicy(presetExec("container_bash", "podman-ops", {
|
|
383
|
+
container: "c",
|
|
384
|
+
command: "ls",
|
|
385
|
+
}, {
|
|
386
|
+
mode: "read-only",
|
|
387
|
+
}), () => Promise.resolve({ kind: "allow" }), undefined, undefined, testReadSession));
|
|
354
388
|
assert.equal(denied.kind, "deny", "read-only wins over podman-ops approval");
|
|
355
389
|
let fullDelegated = false;
|
|
356
|
-
const full = (await preExecutePolicy(presetExec("daemon_start", "podman-ops", { container: "c", argv: ["x"] }, {
|
|
390
|
+
const full = (await preExecutePolicy(presetExec("daemon_start", "podman-ops", { container: "c", argv: ["x"] }, {
|
|
391
|
+
policy: "never",
|
|
392
|
+
}), () => {
|
|
357
393
|
fullDelegated = true;
|
|
358
394
|
return Promise.resolve({ kind: "allow" });
|
|
359
395
|
}, undefined, undefined, testReadSession));
|
|
@@ -361,12 +397,45 @@ test("podman-ops keeps open tools ungated and respects permissions", async () =>
|
|
|
361
397
|
assert.equal(full.kind, "allow");
|
|
362
398
|
});
|
|
363
399
|
test("summarizeArgs renders reasons for the podman-ops gated tools", () => {
|
|
364
|
-
assert.equal(summarizeArgs("container_exec", {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
400
|
+
assert.equal(summarizeArgs("container_exec", {
|
|
401
|
+
container: "c",
|
|
402
|
+
argv: [
|
|
403
|
+
"python",
|
|
404
|
+
"run.py",
|
|
405
|
+
"--x",
|
|
406
|
+
"1",
|
|
407
|
+
"--y",
|
|
408
|
+
"2",
|
|
409
|
+
"--z",
|
|
410
|
+
"3",
|
|
411
|
+
"--w",
|
|
412
|
+
"4",
|
|
413
|
+
"--v",
|
|
414
|
+
"5",
|
|
415
|
+
],
|
|
416
|
+
}), 'Run a command in container "c": python run.py --x 1 --y 2 --z 3 …');
|
|
417
|
+
assert.equal(summarizeArgs("container_write", {
|
|
418
|
+
container: "c",
|
|
419
|
+
file_path: "/etc/valkey/valkey.conf",
|
|
420
|
+
}), 'Write "/etc/valkey/valkey.conf" in container "c".');
|
|
421
|
+
assert.equal(summarizeArgs("container_edit", {
|
|
422
|
+
container: "c",
|
|
423
|
+
file_path: "/etc/valkey/valkey.conf",
|
|
424
|
+
}), 'Edit "/etc/valkey/valkey.conf" in container "c".');
|
|
425
|
+
assert.equal(summarizeArgs("container_bash", {
|
|
426
|
+
container: "c",
|
|
427
|
+
command: "ping -c 1 8.8.8.8",
|
|
428
|
+
}), 'Run a shell command in container "c": ping -c 1 8.8.8.8');
|
|
429
|
+
assert.equal(summarizeArgs("container_bash", {
|
|
430
|
+
container: "c",
|
|
431
|
+
command: "ls",
|
|
432
|
+
workdir: "/srv",
|
|
433
|
+
}), 'Run a shell command in container "c" (cwd "/srv"): ls');
|
|
434
|
+
assert.equal(summarizeArgs("container_exec", {
|
|
435
|
+
container: "c",
|
|
436
|
+
argv: ["ls"],
|
|
437
|
+
workdir: "/srv",
|
|
438
|
+
}), 'Run a command in container "c" (cwd "/srv"): ls');
|
|
370
439
|
});
|
|
371
440
|
test("summarizeArgs names the process identity and groups", () => {
|
|
372
441
|
assert.equal(summarizeArgs("container_bash", {
|
|
@@ -376,7 +445,11 @@ test("summarizeArgs names the process identity and groups", () => {
|
|
|
376
445
|
gid: 1000,
|
|
377
446
|
groups: [3000, 4000],
|
|
378
447
|
}), 'Run a shell command in container "c": id (uid 1000, gid 1000, groups 3000, 4000)');
|
|
379
|
-
assert.equal(summarizeArgs("container_exec", {
|
|
448
|
+
assert.equal(summarizeArgs("container_exec", {
|
|
449
|
+
container: "c",
|
|
450
|
+
argv: ["id"],
|
|
451
|
+
gid: 2000,
|
|
452
|
+
}), 'Run a command in container "c": id (gid 2000)');
|
|
380
453
|
assert.equal(summarizeArgs("daemon_start", {
|
|
381
454
|
container: "c",
|
|
382
455
|
name: "web",
|
|
@@ -385,9 +458,14 @@ test("summarizeArgs names the process identity and groups", () => {
|
|
|
385
458
|
groups: [3000],
|
|
386
459
|
}), 'Start daemon "web" in container "c": nginx (uid 1000, groups 3000)');
|
|
387
460
|
// An empty groups list is not worth naming.
|
|
388
|
-
assert.equal(summarizeArgs("container_exec", {
|
|
461
|
+
assert.equal(summarizeArgs("container_exec", {
|
|
462
|
+
container: "c",
|
|
463
|
+
argv: ["id"],
|
|
464
|
+
groups: [],
|
|
465
|
+
}), 'Run a command in container "c": id');
|
|
389
466
|
});
|
|
390
467
|
test("Podman-ops preset content covers the recent tools", () => {
|
|
468
|
+
const patch = podmanOpsPatch();
|
|
391
469
|
for (const tool of [
|
|
392
470
|
"image_rebuild_all",
|
|
393
471
|
"secret_list",
|
|
@@ -396,24 +474,26 @@ test("Podman-ops preset content covers the recent tools", () => {
|
|
|
396
474
|
"container_secret_add",
|
|
397
475
|
"container_secret_remove",
|
|
398
476
|
]) {
|
|
399
|
-
assert.ok(
|
|
477
|
+
assert.ok(patch.includes(tool), `podman-ops composition must mention ${tool}`);
|
|
400
478
|
}
|
|
401
|
-
assert.
|
|
479
|
+
assert.match(patch, /id: podman-ops/, "the preset row must carry the policy's preset id");
|
|
480
|
+
assert.ok(patch.includes("secrets"), "podman-ops metadata must mention secrets");
|
|
402
481
|
// The persona plugin takes its prose as `prefix` (a required field); `text`
|
|
403
482
|
// is not part of its schema and would make the preset fail to load.
|
|
404
|
-
assert.match(
|
|
405
|
-
assert.doesNotMatch(
|
|
483
|
+
assert.match(patch, /prefix:/);
|
|
484
|
+
assert.doesNotMatch(patch, /\btext:/);
|
|
406
485
|
});
|
|
407
|
-
test("Podman-ops preset
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
486
|
+
test("Podman-ops preset declares its composition as plugin rows", () => {
|
|
487
|
+
const patch = podmanOpsPatch();
|
|
488
|
+
for (const plugin of [
|
|
489
|
+
"@deepseek-ai/dsh-persona",
|
|
490
|
+
"@deepseek-ai/dsh-tool-ask-user",
|
|
491
|
+
"@deepseek-ai/dsh-tool-todo",
|
|
492
|
+
"@deepseek-ai/dsh-tool-web",
|
|
493
|
+
]) {
|
|
494
|
+
assert.ok(patch.includes(plugin), `podman-ops composition must declare ${plugin}`);
|
|
495
|
+
}
|
|
496
|
+
assert.ok(patch.includes("@deepseek-ai/dsh-agent-preset"), "the preset must be declared through the agent-preset row");
|
|
417
497
|
});
|
|
418
498
|
test("summarizeArgs includes PATH additions for container start/recreate", () => {
|
|
419
499
|
assert.equal(summarizeArgs("container_start", { container: "web", paths: ["/opt/bin"] }), 'Start container "web" with PATH additions: "/opt/bin".');
|
|
@@ -443,7 +523,18 @@ test("summarizeArgs includes env keys for container start/recreate", () => {
|
|
|
443
523
|
}), 'Start container "web" from image "localhost/dsh-podman/nginx:latest" with mounts: project "team" (read-only) with env: A, B, C.');
|
|
444
524
|
assert.equal(summarizeArgs("container_start", {
|
|
445
525
|
container: "web",
|
|
446
|
-
env: {
|
|
526
|
+
env: {
|
|
527
|
+
A: "1",
|
|
528
|
+
B: "2",
|
|
529
|
+
C: "3",
|
|
530
|
+
D: "4",
|
|
531
|
+
E: "5",
|
|
532
|
+
F: "6",
|
|
533
|
+
G: "7",
|
|
534
|
+
H: "8",
|
|
535
|
+
I: "9",
|
|
536
|
+
J: "10",
|
|
537
|
+
},
|
|
447
538
|
}), 'Start container "web" with env: A, B, C, D, E, F, G, H, +2 more.');
|
|
448
539
|
assert.equal(summarizeArgs("container_recreate", {
|
|
449
540
|
container: "c",
|
|
@@ -460,3 +551,43 @@ test("approval prompts name the resolved path", () => {
|
|
|
460
551
|
assert.equal(summarizeArgs("container_write", { container: "c", file_path: "notes.md" }), 'Write "notes.md" in container "c".');
|
|
461
552
|
assert.equal(summarizeArgs("container_write", { container: "c", file_path: "../x" }, "/projects/team"), 'Write "../x" in container "c".');
|
|
462
553
|
});
|
|
554
|
+
test("summarizeArgs includes secret env names for container start/recreate", () => {
|
|
555
|
+
assert.equal(summarizeArgs("container_start", {
|
|
556
|
+
container: "web",
|
|
557
|
+
secretEnv: { TOKEN: "api-token" },
|
|
558
|
+
}), 'Start container "web" with secret env: TOKEN.');
|
|
559
|
+
assert.equal(summarizeArgs("container_recreate", {
|
|
560
|
+
container: "c",
|
|
561
|
+
env: { A: "1" },
|
|
562
|
+
secretEnv: { TOKEN: "api-token" },
|
|
563
|
+
}), 'Recreate container "c" with env: A with secret env: TOKEN.');
|
|
564
|
+
// An empty map is not an attachment and is not named.
|
|
565
|
+
assert.equal(summarizeArgs("container_start", { container: "web", secretEnv: {} }), 'Start container "web".');
|
|
566
|
+
});
|
|
567
|
+
test("container_start asks for approval when it attaches secrets", () => {
|
|
568
|
+
// Plain env, paths, and a bare start stay prompt-free by design: the caller
|
|
569
|
+
// already knows those values, unlike a secret the container injects.
|
|
570
|
+
assert.equal(approvalDecision("container_start", { container: "web" }), undefined);
|
|
571
|
+
assert.equal(approvalDecision("container_start", {
|
|
572
|
+
container: "web",
|
|
573
|
+
paths: ["/opt/bin"],
|
|
574
|
+
}), undefined);
|
|
575
|
+
assert.equal(approvalDecision("container_start", { container: "web", env: { A: "1" } }), undefined);
|
|
576
|
+
assert.equal(approvalDecision("container_start", { container: "web", secretEnv: {} }), undefined);
|
|
577
|
+
// Mounts and secret attachments both change what the container can reach.
|
|
578
|
+
assert.equal(approvalDecision("container_start", {
|
|
579
|
+
container: "web",
|
|
580
|
+
mounts: [{ project: "team" }],
|
|
581
|
+
})?.kind, "ask");
|
|
582
|
+
const withSecret = approvalDecision("container_start", {
|
|
583
|
+
container: "web",
|
|
584
|
+
secretEnv: { TOKEN: "api-token" },
|
|
585
|
+
});
|
|
586
|
+
assert.equal(withSecret?.kind, "ask");
|
|
587
|
+
assert.match(withSecret?.reason ?? "", /secret env: TOKEN/);
|
|
588
|
+
});
|
|
589
|
+
test("the bundle patch disables the built-in terminal UI", () => {
|
|
590
|
+
// The Podman terminal replaces the built-in one; its client row must stay off
|
|
591
|
+
// so the guide offers a single terminal card and Ctrl+` is free.
|
|
592
|
+
assert.match(podmanOpsPatch(), /- id: ui-sidebar-terminal\n\s+disabled: true/);
|
|
593
|
+
});
|
package/dist/card-route.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CardSnapshot } from "./client/card-protocol.js";
|
|
2
|
+
import { type ReasonLocale } from "./approval-reasons.js";
|
|
2
3
|
import { type WorkspaceResolver } from "./workspace-binding.js";
|
|
3
4
|
/** Build the live snapshot the card renders. */
|
|
4
5
|
export declare function cardSnapshot(resolver: WorkspaceResolver, workspaceRegistry: any): Promise<CardSnapshot>;
|
|
@@ -6,10 +7,10 @@ export declare function cardSnapshot(resolver: WorkspaceResolver, workspaceRegis
|
|
|
6
7
|
* Run one card command, returning the notice to show (usually empty). `ctx` is
|
|
7
8
|
* the settings-injected context, read only for the session locale.
|
|
8
9
|
*/
|
|
9
|
-
export declare function runCommand(
|
|
10
|
+
export declare function runCommand(_ctx: any, resolver: WorkspaceResolver, workspaceRegistry: any, raw: Record<string, unknown>, readLocale?: () => ReasonLocale): Promise<string>;
|
|
10
11
|
/**
|
|
11
12
|
* Register the card's authenticated fetch route. It is served below the
|
|
12
13
|
* harness API path, so the carrier applies its Host/Origin fence and browser
|
|
13
14
|
* authentication before the handler runs.
|
|
14
15
|
*/
|
|
15
|
-
export declare function registerCardRoute(ctx: any, resolver: WorkspaceResolver, workspaceRegistry?: any): void;
|
|
16
|
+
export declare function registerCardRoute(ctx: any, resolver: WorkspaceResolver, workspaceRegistry?: any, readLocale?: () => ReasonLocale): void;
|