aicodeman 1.3.5 → 1.4.1
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 +16 -0
- package/dist/docker-export.d.ts +107 -0
- package/dist/docker-export.d.ts.map +1 -0
- package/dist/docker-export.js +335 -0
- package/dist/docker-export.js.map +1 -0
- package/dist/docker-hosts.d.ts +264 -0
- package/dist/docker-hosts.d.ts.map +1 -0
- package/dist/docker-hosts.js +744 -0
- package/dist/docker-hosts.js.map +1 -0
- package/dist/mux-interface.d.ts +7 -1
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/session.d.ts +9 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +48 -4
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +58 -1
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +247 -7
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/api.d.ts +9 -1
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/types/session.d.ts +102 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/web/network-auth-policy.d.ts +9 -0
- package/dist/web/network-auth-policy.d.ts.map +1 -1
- package/dist/web/network-auth-policy.js +12 -0
- package/dist/web/network-auth-policy.js.map +1 -1
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/{app.6890f3d0.js → app.6c4b9893.js} +1 -1
- package/dist/web/public/app.6c4b9893.js.br +0 -0
- package/dist/web/public/app.6c4b9893.js.gz +0 -0
- package/dist/web/public/{constants.5b18ed64.js → constants.48da7f03.js} +7 -0
- package/dist/web/public/constants.48da7f03.js.br +0 -0
- package/dist/web/public/{constants.5b18ed64.js.gz → constants.48da7f03.js.gz} +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +148 -11
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
- package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
- package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
- package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.ca03853f.js → panels-ui.3c9b0465.js} +53 -53
- package/dist/web/public/panels-ui.3c9b0465.js.br +0 -0
- package/dist/web/public/panels-ui.3c9b0465.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.4851c61a.js +58 -0
- package/dist/web/public/session-ui.4851c61a.js.br +0 -0
- package/dist/web/public/session-ui.4851c61a.js.gz +0 -0
- package/dist/web/public/settings-ui.29214cf1.js +67 -0
- package/dist/web/public/settings-ui.29214cf1.js.br +0 -0
- package/dist/web/public/settings-ui.29214cf1.js.gz +0 -0
- package/dist/web/public/{styles.3d78fd0d.css → styles.ff43f80f.css} +1 -1
- package/dist/web/public/styles.ff43f80f.css.br +0 -0
- package/dist/web/public/styles.ff43f80f.css.gz +0 -0
- package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.efda6083.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +422 -3
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +85 -5
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +72 -1
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +158 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +11 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +81 -1
- package/dist/web/server.js.map +1 -1
- package/dist/web/sse-events.d.ts +21 -0
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +23 -0
- package/dist/web/sse-events.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/app.6890f3d0.js.br +0 -0
- package/dist/web/public/app.6890f3d0.js.gz +0 -0
- package/dist/web/public/constants.5b18ed64.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.br +0 -0
- package/dist/web/public/panels-ui.ca03853f.js.gz +0 -0
- package/dist/web/public/session-ui.c9094fe9.js +0 -49
- package/dist/web/public/session-ui.c9094fe9.js.br +0 -0
- package/dist/web/public/session-ui.c9094fe9.js.gz +0 -0
- package/dist/web/public/settings-ui.9538a0c1.js +0 -67
- package/dist/web/public/settings-ui.9538a0c1.js.br +0 -0
- package/dist/web/public/settings-ui.9538a0c1.js.gz +0 -0
- package/dist/web/public/styles.3d78fd0d.css.br +0 -0
- package/dist/web/public/styles.3d78fd0d.css.gz +0 -0
package/README.md
CHANGED
|
@@ -366,6 +366,7 @@ PTY Output → 16ms Server Batch → DEC 2026 Wrap → SSE → Client rAF → xt
|
|
|
366
366
|
|
|
367
367
|
- **Self-update** — git-clone installs under systemd/launchd update in place from **App Settings → Updates**: it detects the latest release, auto-stashes a dirty tree, and streams build progress across the service restart (npm installs report as non-updatable)
|
|
368
368
|
- **Multi-CLI** — run **Claude Code**, **OpenCode**, or **Codex** per session; env-var prefixes auto-gate (`CLAUDE_CODE_*` vs `OPENCODE_*` vs `CODEX_*`). See [`docs/opencode-integration.md`](docs/opencode-integration.md)
|
|
369
|
+
- **Docker sessions** — run a case inside an isolated, hardened container. One checkbox on **Create New** spins up a container with sensible defaults and starts the agent inside it; multiple sessions share one per-case container; export a container + its workspace to a portable `.tar.gz` to move it to another machine. See [`docs/docker-cases.md`](docs/docker-cases.md)
|
|
369
370
|
- **Effort & Ultracode** — set a per-session default effort (`low`–`max`) or enable **ultracode** (dynamic multi-agent workflows). Soft defaults only — switchable anytime with `/effort` in-session. Extended-thinking budget is configurable too
|
|
370
371
|
- **Voice input** — dictate prompts with Deepgram Nova-3 (Web Speech API fallback): toggle recording, auto-silence stop, live level meter (`Ctrl+Shift+V`)
|
|
371
372
|
- **Image input** — paste or drag-and-drop images straight into a session
|
|
@@ -376,6 +377,21 @@ PTY Output → 16ms Server Batch → DEC 2026 Wrap → SSE → Client rAF → xt
|
|
|
376
377
|
|
|
377
378
|
---
|
|
378
379
|
|
|
380
|
+
## Isolated Docker Sessions
|
|
381
|
+
|
|
382
|
+
Run a case inside its own hardened Docker container instead of directly on your host — for security isolation, reproducible toolchains, and one-click portability.
|
|
383
|
+
|
|
384
|
+
- **One click** — on **New Case → Create New**, tick **🐳 Run in an isolated Docker container**. Codeman creates the case folder, spins up a container with default settings, and starts the agent inside it. No host/image/network fields to fill in.
|
|
385
|
+
- **Resource templates** — expand the checkbox for a **Small / Medium / Large / GPU** preset (memory, CPUs, GPU), or set your own. **Disk is elastic** — storage grows as data flows in, no fixed cap.
|
|
386
|
+
- **Shared per-case container** — many sessions can `docker exec` into the same container; killing one session never tears the container out from under the others.
|
|
387
|
+
- **Hardened by default** — non-root, `--cap-drop ALL`, `no-new-privileges`, PID/memory caps, never `--privileged` or the docker socket. Your existing `~/.claude` login is bind-mounted (credentials stay on the host, never captured in exports); a **sealed** profile (no host mounts, network off) is one toggle away.
|
|
388
|
+
- **Move it to another machine** — export a container's whole environment (toolchain + workspace) to a portable `.tar.gz`, `docker load` it on the other side, and import it into a fresh case.
|
|
389
|
+
- **Durable** — reconnect after a restart lands back in the same live agent; a container stop/reboot resumes the conversation from the bind-mounted transcript.
|
|
390
|
+
|
|
391
|
+
Prerequisite: Docker (or Podman) and the base image — build it once with `node scripts/build-agent-image.mjs`. Full guide: [`docs/docker-cases.md`](docs/docker-cases.md).
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
379
395
|
## Remote Access — Cloudflare Tunnel
|
|
380
396
|
|
|
381
397
|
Access Codeman from your phone or any device outside your local network using a free [Cloudflare quick tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) — no port forwarding, no DNS, no static IP required.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Docker case export / import: move a container (toolchain + any
|
|
3
|
+
* in-image changes) PLUS its workspace to another machine as one portable
|
|
4
|
+
* `.codeman-container.tgz`, and restore it.
|
|
5
|
+
*
|
|
6
|
+
* A full-image export = `docker commit` the running container to an image ->
|
|
7
|
+
* `docker save` that image -> tar the bind-mounted workspace -> a manifest, all
|
|
8
|
+
* bundled into one gzip tarball. A workspace-only export skips the image (fast,
|
|
9
|
+
* files-only). Import validates the manifest + per-member checksums, extracts the
|
|
10
|
+
* workspace with a path-traversal guard, `docker load`s the image and RE-TAGS it
|
|
11
|
+
* into a quarantined namespace (never overwriting a local tag), and hands the
|
|
12
|
+
* caller enough to recreate a hardened case on the destination.
|
|
13
|
+
*
|
|
14
|
+
* Safety (all from the design critic): pause the container spanning the workspace
|
|
15
|
+
* tar AND the commit so the two artifacts are mutually consistent; a free-space
|
|
16
|
+
* precheck (a full docker graph wedges EVERY session on the host); `docker rmi`
|
|
17
|
+
* the intermediate image in a finally; sealed containers refuse a full-image
|
|
18
|
+
* export (an in-container login would ride the committed layer); import rejects
|
|
19
|
+
* absolute / `..` tar members and checksum mismatches. Bounded by
|
|
20
|
+
* runWithConversionLimit so N exports cannot fork-bomb the host.
|
|
21
|
+
*
|
|
22
|
+
* @module docker-export
|
|
23
|
+
*/
|
|
24
|
+
import type { DockerEngine, SessionDocker } from './types.js';
|
|
25
|
+
/** Refuse to export when the target filesystem has less than this free (a full graph wedges the daemon). */
|
|
26
|
+
export declare const DOCKER_EXPORT_MIN_FREE_BYTES: number;
|
|
27
|
+
/** Manifest schema version (bump on any breaking field change). */
|
|
28
|
+
export declare const DOCKER_EXPORT_SCHEMA = 1;
|
|
29
|
+
export type DockerExportMode = 'full' | 'workspace';
|
|
30
|
+
export interface DockerExportManifest {
|
|
31
|
+
schemaVersion: number;
|
|
32
|
+
caseName: string;
|
|
33
|
+
mode: DockerExportMode;
|
|
34
|
+
engine: DockerEngine;
|
|
35
|
+
image: string;
|
|
36
|
+
containerWorkdir: string;
|
|
37
|
+
network: string;
|
|
38
|
+
createdAt: number;
|
|
39
|
+
codemanVersion: string;
|
|
40
|
+
mountCredentials: boolean;
|
|
41
|
+
/** True when the bundle provably carries no credentials (convenient-mode workspace, or a full image whose creds were bind-mounted and thus never committed). */
|
|
42
|
+
secretFree: boolean;
|
|
43
|
+
/** sha256 of each bundle member that is present. */
|
|
44
|
+
checksums: {
|
|
45
|
+
image?: string;
|
|
46
|
+
workspace?: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Raw argv prefix for the engine (NO shell escaping — used with spawn). */
|
|
50
|
+
export declare function dockerArgv(docker: Pick<SessionDocker, 'engine' | 'context' | 'daemonHost'>): string[];
|
|
51
|
+
/** Portable bundle filename for a case export. */
|
|
52
|
+
export declare function exportBundleName(caseName: string, timestamp: number, mode: DockerExportMode): string;
|
|
53
|
+
/** Quarantined image tag for an imported bundle (never overwrites a local tag). */
|
|
54
|
+
export declare function importedImageTag(caseName: string, timestamp: number): string;
|
|
55
|
+
/** Intermediate commit tag for a full-image export (unique per export, rmi'd in finally). */
|
|
56
|
+
export declare function exportImageTag(caseName: string, timestamp: number): string;
|
|
57
|
+
/**
|
|
58
|
+
* Reject a tar member path that would escape the extraction root (absolute path
|
|
59
|
+
* or a `..` component). The import-side traversal guard.
|
|
60
|
+
*/
|
|
61
|
+
export declare function isSafeTarMember(member: string): boolean;
|
|
62
|
+
/** Parse the image id/ref from `docker load` output ("Loaded image: x" / "Loaded image ID: sha256:..."). */
|
|
63
|
+
export declare function parseLoadedImageRef(loadOutput: string): string | null;
|
|
64
|
+
export interface ExportResult {
|
|
65
|
+
bundlePath: string;
|
|
66
|
+
manifest: DockerExportManifest;
|
|
67
|
+
sizeBytes: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Export a docker case to a portable bundle. Bounded by runWithConversionLimit.
|
|
71
|
+
* `full` mode commits + saves the image AND tars the workspace; `workspace` mode
|
|
72
|
+
* tars just the workspace. The container is paused across the artifact capture so
|
|
73
|
+
* image and workspace are mutually consistent.
|
|
74
|
+
*/
|
|
75
|
+
export declare function exportDockerCase(params: {
|
|
76
|
+
docker: SessionDocker;
|
|
77
|
+
caseName: string;
|
|
78
|
+
timestamp: number;
|
|
79
|
+
exportsDir: string;
|
|
80
|
+
mode: DockerExportMode;
|
|
81
|
+
codemanVersion: string;
|
|
82
|
+
}): Promise<ExportResult>;
|
|
83
|
+
export interface ImportResult {
|
|
84
|
+
manifest: DockerExportManifest;
|
|
85
|
+
/** Quarantined image ref the destination case should use (full mode only). */
|
|
86
|
+
importedImage?: string;
|
|
87
|
+
/** Directory the workspace was extracted into. */
|
|
88
|
+
workspacePath: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Import a bundle produced by exportDockerCase: validate the manifest + per-member
|
|
92
|
+
* checksums, extract the workspace (traversal-guarded) into destWorkspace, and, in
|
|
93
|
+
* full mode, `docker load` the image and re-tag it into a quarantined namespace.
|
|
94
|
+
*/
|
|
95
|
+
export declare function importDockerBundle(params: {
|
|
96
|
+
bundlePath: string;
|
|
97
|
+
destWorkspace: string;
|
|
98
|
+
engine: DockerEngine;
|
|
99
|
+
timestamp: number;
|
|
100
|
+
}): Promise<ImportResult>;
|
|
101
|
+
/** List export bundles in the exports dir (newest first), with size + mtime. */
|
|
102
|
+
export declare function listDockerExports(exportsDir: string): Promise<Array<{
|
|
103
|
+
name: string;
|
|
104
|
+
sizeBytes: number;
|
|
105
|
+
mtimeMs: number;
|
|
106
|
+
}>>;
|
|
107
|
+
//# sourceMappingURL=docker-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-export.d.ts","sourceRoot":"","sources":["../src/docker-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Docker case export / import: move a container (toolchain + any
|
|
3
|
+
* in-image changes) PLUS its workspace to another machine as one portable
|
|
4
|
+
* `.codeman-container.tgz`, and restore it.
|
|
5
|
+
*
|
|
6
|
+
* A full-image export = `docker commit` the running container to an image ->
|
|
7
|
+
* `docker save` that image -> tar the bind-mounted workspace -> a manifest, all
|
|
8
|
+
* bundled into one gzip tarball. A workspace-only export skips the image (fast,
|
|
9
|
+
* files-only). Import validates the manifest + per-member checksums, extracts the
|
|
10
|
+
* workspace with a path-traversal guard, `docker load`s the image and RE-TAGS it
|
|
11
|
+
* into a quarantined namespace (never overwriting a local tag), and hands the
|
|
12
|
+
* caller enough to recreate a hardened case on the destination.
|
|
13
|
+
*
|
|
14
|
+
* Safety (all from the design critic): pause the container spanning the workspace
|
|
15
|
+
* tar AND the commit so the two artifacts are mutually consistent; a free-space
|
|
16
|
+
* precheck (a full docker graph wedges EVERY session on the host); `docker rmi`
|
|
17
|
+
* the intermediate image in a finally; sealed containers refuse a full-image
|
|
18
|
+
* export (an in-container login would ride the committed layer); import rejects
|
|
19
|
+
* absolute / `..` tar members and checksum mismatches. Bounded by
|
|
20
|
+
* runWithConversionLimit so N exports cannot fork-bomb the host.
|
|
21
|
+
*
|
|
22
|
+
* @module docker-export
|
|
23
|
+
*/
|
|
24
|
+
import { createReadStream, createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
|
25
|
+
import fs from 'node:fs/promises';
|
|
26
|
+
import { join, basename } from 'node:path';
|
|
27
|
+
import { createHash } from 'node:crypto';
|
|
28
|
+
import { spawn } from 'node:child_process';
|
|
29
|
+
import { pipeline } from 'node:stream/promises';
|
|
30
|
+
import { runWithConversionLimit } from './document-conversion-limiter.js';
|
|
31
|
+
const IS_TEST_MODE = !!process.env.VITEST;
|
|
32
|
+
/** Refuse to export when the target filesystem has less than this free (a full graph wedges the daemon). */
|
|
33
|
+
export const DOCKER_EXPORT_MIN_FREE_BYTES = 2 * 1024 * 1024 * 1024; // 2 GiB
|
|
34
|
+
/** Manifest schema version (bump on any breaking field change). */
|
|
35
|
+
export const DOCKER_EXPORT_SCHEMA = 1;
|
|
36
|
+
// ========== Pure helpers (unit-tested) ==========
|
|
37
|
+
/** Raw argv prefix for the engine (NO shell escaping — used with spawn). */
|
|
38
|
+
export function dockerArgv(docker) {
|
|
39
|
+
const argv = [docker.engine === 'podman' ? 'podman' : 'docker'];
|
|
40
|
+
if (docker.context)
|
|
41
|
+
argv.push('--context', docker.context);
|
|
42
|
+
if (docker.daemonHost)
|
|
43
|
+
argv.push('-H', docker.daemonHost);
|
|
44
|
+
return argv;
|
|
45
|
+
}
|
|
46
|
+
/** Portable bundle filename for a case export. */
|
|
47
|
+
export function exportBundleName(caseName, timestamp, mode) {
|
|
48
|
+
const suffix = mode === 'workspace' ? 'workspace' : 'container';
|
|
49
|
+
return `${caseName}-${timestamp}.codeman-${suffix}.tgz`;
|
|
50
|
+
}
|
|
51
|
+
/** Quarantined image tag for an imported bundle (never overwrites a local tag). */
|
|
52
|
+
export function importedImageTag(caseName, timestamp) {
|
|
53
|
+
return `codeman/imported-${caseName}:${timestamp}`;
|
|
54
|
+
}
|
|
55
|
+
/** Intermediate commit tag for a full-image export (unique per export, rmi'd in finally). */
|
|
56
|
+
export function exportImageTag(caseName, timestamp) {
|
|
57
|
+
return `codeman/export-${caseName}:${timestamp}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Reject a tar member path that would escape the extraction root (absolute path
|
|
61
|
+
* or a `..` component). The import-side traversal guard.
|
|
62
|
+
*/
|
|
63
|
+
export function isSafeTarMember(member) {
|
|
64
|
+
const trimmed = member.trim();
|
|
65
|
+
if (!trimmed || trimmed === './')
|
|
66
|
+
return true;
|
|
67
|
+
if (trimmed.startsWith('/'))
|
|
68
|
+
return false;
|
|
69
|
+
// Normalize separators and check each component.
|
|
70
|
+
return !trimmed.split('/').some((part) => part === '..');
|
|
71
|
+
}
|
|
72
|
+
/** Parse the image id/ref from `docker load` output ("Loaded image: x" / "Loaded image ID: sha256:..."). */
|
|
73
|
+
export function parseLoadedImageRef(loadOutput) {
|
|
74
|
+
const idMatch = loadOutput.match(/Loaded image ID:\s*(sha256:[0-9a-f]+)/i);
|
|
75
|
+
if (idMatch)
|
|
76
|
+
return idMatch[1];
|
|
77
|
+
const refMatch = loadOutput.match(/Loaded image:\s*(\S+)/i);
|
|
78
|
+
if (refMatch)
|
|
79
|
+
return refMatch[1];
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// ========== IO helpers ==========
|
|
83
|
+
function run(cmd, args, opts = {}) {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
86
|
+
let stdout = '';
|
|
87
|
+
let stderr = '';
|
|
88
|
+
let timer;
|
|
89
|
+
if (opts.timeout) {
|
|
90
|
+
timer = setTimeout(() => {
|
|
91
|
+
child.kill('SIGKILL');
|
|
92
|
+
reject(new Error(`${cmd} timed out after ${opts.timeout}ms`));
|
|
93
|
+
}, opts.timeout);
|
|
94
|
+
}
|
|
95
|
+
child.stdout.on('data', (d) => (stdout += d));
|
|
96
|
+
child.stderr.on('data', (d) => (stderr += d));
|
|
97
|
+
child.on('error', (err) => {
|
|
98
|
+
if (timer)
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
reject(err);
|
|
101
|
+
});
|
|
102
|
+
child.on('close', (code) => {
|
|
103
|
+
if (timer)
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
if (code === 0)
|
|
106
|
+
resolve({ stdout, stderr });
|
|
107
|
+
else
|
|
108
|
+
reject(new Error(`${cmd} ${args.join(' ')} exited ${code}: ${stderr.trim()}`));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Stream `docker save <tag>` stdout to a raw tar file (no shell, no double-gzip).
|
|
114
|
+
* Uses stream `pipeline` so completion means the write stream is FULLY flushed to
|
|
115
|
+
* disk (a naive child 'close' resolves before the last chunks land, truncating the
|
|
116
|
+
* file — a real bug caught in end-to-end testing), AND waits for a clean exit code.
|
|
117
|
+
*/
|
|
118
|
+
async function saveImageToTar(argv, tag, outPath) {
|
|
119
|
+
const child = spawn(argv[0], [...argv.slice(1), 'save', tag], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
120
|
+
let stderr = '';
|
|
121
|
+
child.stderr.on('data', (d) => (stderr += d));
|
|
122
|
+
const exited = new Promise((resolve, reject) => {
|
|
123
|
+
child.on('error', reject);
|
|
124
|
+
child.on('close', (code) => code === 0 ? resolve() : reject(new Error(`docker save exited ${code}: ${stderr.trim()}`)));
|
|
125
|
+
});
|
|
126
|
+
// pipeline resolves only after the destination has fully flushed.
|
|
127
|
+
await Promise.all([pipeline(child.stdout, createWriteStream(outPath)), exited]);
|
|
128
|
+
}
|
|
129
|
+
async function sha256File(path) {
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const hash = createHash('sha256');
|
|
132
|
+
const stream = createReadStream(path);
|
|
133
|
+
stream.on('data', (d) => hash.update(d));
|
|
134
|
+
stream.on('error', reject);
|
|
135
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function freeBytes(path) {
|
|
139
|
+
try {
|
|
140
|
+
const stat = await fs.statfs(path);
|
|
141
|
+
return Number(stat.bavail) * Number(stat.bsize);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return Number.POSITIVE_INFINITY; // statfs unsupported — don't block
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function isContainerRunning(argv, container) {
|
|
148
|
+
try {
|
|
149
|
+
const { stdout } = await run(argv[0], [...argv.slice(1), 'inspect', '-f', '{{.State.Running}}', container], {
|
|
150
|
+
timeout: 15_000,
|
|
151
|
+
});
|
|
152
|
+
return stdout.trim() === 'true';
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Export a docker case to a portable bundle. Bounded by runWithConversionLimit.
|
|
160
|
+
* `full` mode commits + saves the image AND tars the workspace; `workspace` mode
|
|
161
|
+
* tars just the workspace. The container is paused across the artifact capture so
|
|
162
|
+
* image and workspace are mutually consistent.
|
|
163
|
+
*/
|
|
164
|
+
export async function exportDockerCase(params) {
|
|
165
|
+
const { docker, caseName, timestamp, exportsDir, mode, codemanVersion } = params;
|
|
166
|
+
if (mode === 'full' && !docker.mountCredentials) {
|
|
167
|
+
throw new Error('full-image export is refused for a sealed (mountCredentials:false) container: an in-container login would ride the committed image layer. Use a workspace-only export.');
|
|
168
|
+
}
|
|
169
|
+
if (IS_TEST_MODE) {
|
|
170
|
+
// No real docker/tar under vitest — return a deterministic stub.
|
|
171
|
+
const manifest = {
|
|
172
|
+
schemaVersion: DOCKER_EXPORT_SCHEMA,
|
|
173
|
+
caseName,
|
|
174
|
+
mode,
|
|
175
|
+
engine: docker.engine,
|
|
176
|
+
image: docker.image,
|
|
177
|
+
containerWorkdir: docker.containerWorkdir,
|
|
178
|
+
network: docker.network,
|
|
179
|
+
createdAt: timestamp,
|
|
180
|
+
codemanVersion,
|
|
181
|
+
mountCredentials: docker.mountCredentials,
|
|
182
|
+
secretFree: true,
|
|
183
|
+
checksums: {},
|
|
184
|
+
};
|
|
185
|
+
return { bundlePath: join(exportsDir, exportBundleName(caseName, timestamp, mode)), manifest, sizeBytes: 0 };
|
|
186
|
+
}
|
|
187
|
+
return runWithConversionLimit(async () => {
|
|
188
|
+
if (!existsSync(exportsDir))
|
|
189
|
+
mkdirSync(exportsDir, { recursive: true });
|
|
190
|
+
const free = await freeBytes(exportsDir);
|
|
191
|
+
if (free < DOCKER_EXPORT_MIN_FREE_BYTES) {
|
|
192
|
+
throw new Error(`not enough free space to export (need >= ${Math.round(DOCKER_EXPORT_MIN_FREE_BYTES / 1e9)}GB, have ${Math.round(free / 1e9)}GB). A full docker graph wedges every session on the host.`);
|
|
193
|
+
}
|
|
194
|
+
const argv = dockerArgv(docker);
|
|
195
|
+
const bundlePath = join(exportsDir, exportBundleName(caseName, timestamp, mode));
|
|
196
|
+
const stageDir = join(exportsDir, `.stage-${caseName}-${timestamp}`);
|
|
197
|
+
mkdirSync(stageDir, { recursive: true });
|
|
198
|
+
const wasRunning = await isContainerRunning(argv, docker.containerName);
|
|
199
|
+
let commitTag;
|
|
200
|
+
try {
|
|
201
|
+
if (wasRunning) {
|
|
202
|
+
await run(argv[0], [...argv.slice(1), 'pause', docker.containerName], { timeout: 30_000 }).catch(() => { });
|
|
203
|
+
}
|
|
204
|
+
const checksums = {};
|
|
205
|
+
if (mode === 'full') {
|
|
206
|
+
commitTag = exportImageTag(caseName, timestamp);
|
|
207
|
+
// Blank instance-specific committed env so the image carries no stale host refs.
|
|
208
|
+
await run(argv[0], [
|
|
209
|
+
...argv.slice(1),
|
|
210
|
+
'commit',
|
|
211
|
+
'-c',
|
|
212
|
+
'ENV CODEMAN_API_URL=',
|
|
213
|
+
'-c',
|
|
214
|
+
'ENV CODEMAN_HOOK_SECRET_FILE=',
|
|
215
|
+
docker.containerName,
|
|
216
|
+
commitTag,
|
|
217
|
+
], { timeout: 300_000 });
|
|
218
|
+
const imageTar = join(stageDir, 'image.tar');
|
|
219
|
+
await saveImageToTar(argv, commitTag, imageTar);
|
|
220
|
+
checksums.image = await sha256File(imageTar);
|
|
221
|
+
}
|
|
222
|
+
const workspaceTar = join(stageDir, 'workspace.tar');
|
|
223
|
+
await run('tar', ['-cf', workspaceTar, '-C', docker.hostWorkspacePath, '.'], { timeout: 300_000 });
|
|
224
|
+
checksums.workspace = await sha256File(workspaceTar);
|
|
225
|
+
const manifest = {
|
|
226
|
+
schemaVersion: DOCKER_EXPORT_SCHEMA,
|
|
227
|
+
caseName,
|
|
228
|
+
mode,
|
|
229
|
+
engine: docker.engine,
|
|
230
|
+
image: docker.image,
|
|
231
|
+
containerWorkdir: docker.containerWorkdir,
|
|
232
|
+
network: docker.network,
|
|
233
|
+
createdAt: timestamp,
|
|
234
|
+
codemanVersion,
|
|
235
|
+
mountCredentials: docker.mountCredentials,
|
|
236
|
+
// Convenient mode keeps creds on bind mounts (never committed), so the bundle is secret-free.
|
|
237
|
+
secretFree: docker.mountCredentials,
|
|
238
|
+
checksums,
|
|
239
|
+
};
|
|
240
|
+
await fs.writeFile(join(stageDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
241
|
+
const members = mode === 'full' ? ['manifest.json', 'image.tar', 'workspace.tar'] : ['manifest.json', 'workspace.tar'];
|
|
242
|
+
await run('tar', ['-czf', bundlePath, '-C', stageDir, ...members], { timeout: 300_000 });
|
|
243
|
+
const stat = await fs.stat(bundlePath);
|
|
244
|
+
return { bundlePath, manifest, sizeBytes: stat.size };
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
// Always remove the intermediate image + stage dir, and unpause.
|
|
248
|
+
if (commitTag) {
|
|
249
|
+
await run(argv[0], [...argv.slice(1), 'rmi', commitTag], { timeout: 60_000 }).catch(() => { });
|
|
250
|
+
}
|
|
251
|
+
await fs.rm(stageDir, { recursive: true, force: true }).catch(() => { });
|
|
252
|
+
if (wasRunning) {
|
|
253
|
+
await run(argv[0], [...argv.slice(1), 'unpause', docker.containerName], { timeout: 30_000 }).catch(() => { });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Import a bundle produced by exportDockerCase: validate the manifest + per-member
|
|
260
|
+
* checksums, extract the workspace (traversal-guarded) into destWorkspace, and, in
|
|
261
|
+
* full mode, `docker load` the image and re-tag it into a quarantined namespace.
|
|
262
|
+
*/
|
|
263
|
+
export async function importDockerBundle(params) {
|
|
264
|
+
const { bundlePath, destWorkspace, engine, timestamp } = params;
|
|
265
|
+
const argv = [engine === 'podman' ? 'podman' : 'docker'];
|
|
266
|
+
if (IS_TEST_MODE) {
|
|
267
|
+
const raw = await fs.readFile(bundlePath, 'utf-8').catch(() => '{}');
|
|
268
|
+
return { manifest: JSON.parse(raw), workspacePath: destWorkspace };
|
|
269
|
+
}
|
|
270
|
+
const stageDir = `${destWorkspace}.import-stage-${timestamp}`;
|
|
271
|
+
mkdirSync(stageDir, { recursive: true });
|
|
272
|
+
try {
|
|
273
|
+
await run('tar', ['-xzf', bundlePath, '-C', stageDir], { timeout: 300_000 });
|
|
274
|
+
const manifestRaw = await fs.readFile(join(stageDir, 'manifest.json'), 'utf-8');
|
|
275
|
+
const manifest = JSON.parse(manifestRaw);
|
|
276
|
+
if (manifest.schemaVersion !== DOCKER_EXPORT_SCHEMA) {
|
|
277
|
+
throw new Error(`unsupported export schema version ${manifest.schemaVersion} (expected ${DOCKER_EXPORT_SCHEMA})`);
|
|
278
|
+
}
|
|
279
|
+
// Integrity: verify checksums before trusting any member.
|
|
280
|
+
const workspaceTar = join(stageDir, 'workspace.tar');
|
|
281
|
+
if (manifest.checksums.workspace) {
|
|
282
|
+
const actual = await sha256File(workspaceTar);
|
|
283
|
+
if (actual !== manifest.checksums.workspace)
|
|
284
|
+
throw new Error('workspace checksum mismatch (corrupt or tampered bundle)');
|
|
285
|
+
}
|
|
286
|
+
// Traversal guard: reject absolute / `..` members before extraction.
|
|
287
|
+
const { stdout: memberList } = await run('tar', ['-tf', workspaceTar], { timeout: 60_000 });
|
|
288
|
+
for (const member of memberList.split('\n').filter(Boolean)) {
|
|
289
|
+
if (!isSafeTarMember(member))
|
|
290
|
+
throw new Error(`unsafe path in workspace archive: ${member}`);
|
|
291
|
+
}
|
|
292
|
+
mkdirSync(destWorkspace, { recursive: true });
|
|
293
|
+
await run('tar', ['--no-same-owner', '-xf', workspaceTar, '-C', destWorkspace], { timeout: 300_000 });
|
|
294
|
+
let importedImage;
|
|
295
|
+
if (manifest.mode === 'full') {
|
|
296
|
+
const imageTar = join(stageDir, 'image.tar');
|
|
297
|
+
if (manifest.checksums.image) {
|
|
298
|
+
const actual = await sha256File(imageTar);
|
|
299
|
+
if (actual !== manifest.checksums.image)
|
|
300
|
+
throw new Error('image checksum mismatch (corrupt or tampered bundle)');
|
|
301
|
+
}
|
|
302
|
+
const { stdout } = await run(argv[0], [...argv.slice(1), 'load', '-i', imageTar], { timeout: 300_000 });
|
|
303
|
+
const loadedRef = parseLoadedImageRef(stdout);
|
|
304
|
+
if (!loadedRef)
|
|
305
|
+
throw new Error('could not determine loaded image ref');
|
|
306
|
+
// Quarantine: re-tag by the loaded ref/id, never trusting the bundle's original tag.
|
|
307
|
+
importedImage = importedImageTag(manifest.caseName, timestamp);
|
|
308
|
+
await run(argv[0], [...argv.slice(1), 'tag', loadedRef, importedImage], { timeout: 60_000 });
|
|
309
|
+
}
|
|
310
|
+
return { manifest, importedImage, workspacePath: destWorkspace };
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
await fs.rm(stageDir, { recursive: true, force: true }).catch(() => { });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/** List export bundles in the exports dir (newest first), with size + mtime. */
|
|
317
|
+
export async function listDockerExports(exportsDir) {
|
|
318
|
+
if (!existsSync(exportsDir))
|
|
319
|
+
return [];
|
|
320
|
+
const entries = await fs.readdir(exportsDir).catch(() => []);
|
|
321
|
+
const out = [];
|
|
322
|
+
for (const name of entries) {
|
|
323
|
+
if (!name.endsWith('.tgz'))
|
|
324
|
+
continue;
|
|
325
|
+
try {
|
|
326
|
+
const stat = await fs.stat(join(exportsDir, name));
|
|
327
|
+
out.push({ name: basename(name), sizeBytes: stat.size, mtimeMs: stat.mtimeMs });
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
/* skip */
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return out.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=docker-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-export.js","sourceRoot":"","sources":["../src/docker-export.ts"],"names":[],"mappings":"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"}
|