aicodeman 1.3.5 → 1.4.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.
Files changed (106) hide show
  1. package/dist/docker-export.d.ts +107 -0
  2. package/dist/docker-export.d.ts.map +1 -0
  3. package/dist/docker-export.js +335 -0
  4. package/dist/docker-export.js.map +1 -0
  5. package/dist/docker-hosts.d.ts +184 -0
  6. package/dist/docker-hosts.d.ts.map +1 -0
  7. package/dist/docker-hosts.js +506 -0
  8. package/dist/docker-hosts.js.map +1 -0
  9. package/dist/mux-interface.d.ts +7 -1
  10. package/dist/mux-interface.d.ts.map +1 -1
  11. package/dist/session.d.ts +9 -2
  12. package/dist/session.d.ts.map +1 -1
  13. package/dist/session.js +48 -4
  14. package/dist/session.js.map +1 -1
  15. package/dist/tmux-manager.d.ts +50 -1
  16. package/dist/tmux-manager.d.ts.map +1 -1
  17. package/dist/tmux-manager.js +214 -7
  18. package/dist/tmux-manager.js.map +1 -1
  19. package/dist/types/api.d.ts +9 -1
  20. package/dist/types/api.d.ts.map +1 -1
  21. package/dist/types/api.js.map +1 -1
  22. package/dist/types/session.d.ts +102 -0
  23. package/dist/types/session.d.ts.map +1 -1
  24. package/dist/types/session.js.map +1 -1
  25. package/dist/web/network-auth-policy.d.ts +9 -0
  26. package/dist/web/network-auth-policy.d.ts.map +1 -1
  27. package/dist/web/network-auth-policy.js +12 -0
  28. package/dist/web/network-auth-policy.js.map +1 -1
  29. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  30. package/dist/web/public/{app.6890f3d0.js → app.07e61fcf.js} +1 -1
  31. package/dist/web/public/app.07e61fcf.js.br +0 -0
  32. package/dist/web/public/app.07e61fcf.js.gz +0 -0
  33. package/dist/web/public/{constants.5b18ed64.js → constants.503109fa.js} +3 -0
  34. package/dist/web/public/constants.503109fa.js.br +0 -0
  35. package/dist/web/public/{constants.5b18ed64.js.gz → constants.503109fa.js.gz} +0 -0
  36. package/dist/web/public/cron-ui.js.gz +0 -0
  37. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  38. package/dist/web/public/index.html +139 -10
  39. package/dist/web/public/index.html.br +0 -0
  40. package/dist/web/public/index.html.gz +0 -0
  41. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  42. package/dist/web/public/keyboard-accessory.3f91acef.js.gz +0 -0
  43. package/dist/web/public/mobile-handlers.b5a667ef.js.gz +0 -0
  44. package/dist/web/public/mobile.0644e3ac.css.gz +0 -0
  45. package/dist/web/public/notification-manager.ca9dc40d.js.gz +0 -0
  46. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  47. package/dist/web/public/panels-ui.ca03853f.js.gz +0 -0
  48. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  49. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  50. package/dist/web/public/respawn-ui.2d249da9.js.gz +0 -0
  51. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  52. package/dist/web/public/session-ui.9da1b71f.js +58 -0
  53. package/dist/web/public/session-ui.9da1b71f.js.br +0 -0
  54. package/dist/web/public/session-ui.9da1b71f.js.gz +0 -0
  55. package/dist/web/public/settings-ui.4acbe789.js +67 -0
  56. package/dist/web/public/settings-ui.4acbe789.js.br +0 -0
  57. package/dist/web/public/settings-ui.4acbe789.js.gz +0 -0
  58. package/dist/web/public/{styles.3d78fd0d.css → styles.546ddae4.css} +1 -1
  59. package/dist/web/public/styles.546ddae4.css.br +0 -0
  60. package/dist/web/public/styles.546ddae4.css.gz +0 -0
  61. package/dist/web/public/subagent-windows.69b60c35.js.gz +0 -0
  62. package/dist/web/public/sw.js.gz +0 -0
  63. package/dist/web/public/terminal-ui.efda6083.js.gz +0 -0
  64. package/dist/web/public/ultracode-panel.js.gz +0 -0
  65. package/dist/web/public/ultracode-windows.js.gz +0 -0
  66. package/dist/web/public/upload.html.gz +0 -0
  67. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  68. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  69. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  70. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  71. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  72. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  73. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  74. package/dist/web/public/vendor/xterm.css.gz +0 -0
  75. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  76. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  77. package/dist/web/routes/case-routes.d.ts.map +1 -1
  78. package/dist/web/routes/case-routes.js +372 -3
  79. package/dist/web/routes/case-routes.js.map +1 -1
  80. package/dist/web/routes/session-routes.d.ts.map +1 -1
  81. package/dist/web/routes/session-routes.js +67 -4
  82. package/dist/web/routes/session-routes.js.map +1 -1
  83. package/dist/web/schemas.d.ts +71 -1
  84. package/dist/web/schemas.d.ts.map +1 -1
  85. package/dist/web/schemas.js +155 -0
  86. package/dist/web/schemas.js.map +1 -1
  87. package/dist/web/server.d.ts +11 -0
  88. package/dist/web/server.d.ts.map +1 -1
  89. package/dist/web/server.js +81 -1
  90. package/dist/web/server.js.map +1 -1
  91. package/dist/web/sse-events.d.ts +9 -0
  92. package/dist/web/sse-events.d.ts.map +1 -1
  93. package/dist/web/sse-events.js +11 -0
  94. package/dist/web/sse-events.js.map +1 -1
  95. package/package.json +1 -1
  96. package/dist/web/public/app.6890f3d0.js.br +0 -0
  97. package/dist/web/public/app.6890f3d0.js.gz +0 -0
  98. package/dist/web/public/constants.5b18ed64.js.br +0 -0
  99. package/dist/web/public/session-ui.c9094fe9.js +0 -49
  100. package/dist/web/public/session-ui.c9094fe9.js.br +0 -0
  101. package/dist/web/public/session-ui.c9094fe9.js.gz +0 -0
  102. package/dist/web/public/settings-ui.9538a0c1.js +0 -67
  103. package/dist/web/public/settings-ui.9538a0c1.js.br +0 -0
  104. package/dist/web/public/settings-ui.9538a0c1.js.gz +0 -0
  105. package/dist/web/public/styles.3d78fd0d.css.br +0 -0
  106. package/dist/web/public/styles.3d78fd0d.css.gz +0 -0
@@ -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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"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @fileoverview Docker cases: storage, pure command-arg builders, and daemon probes.
3
+ *
4
+ * Docker mode is a LOCATION OVERLAY on cases (not a 6th SessionMode), the direct
5
+ * analog of the remote-SSH feature in `remote-hosts.ts`. Instead of a local tmux
6
+ * pane running `ssh host` into a durable remote tmux server, a local tmux pane
7
+ * runs `docker exec -it` into a durable IN-CONTAINER tmux server. The container is
8
+ * scoped to the CASE (`codeman-case-<name>`), so multiple sessions can `docker
9
+ * exec` into the same long-lived container.
10
+ *
11
+ * This module mirrors `remote-hosts.ts`:
12
+ * - JSON storage for hosts (`docker-hosts.json`) and cases (`docker-cases.json`)
13
+ * - `toSessionDocker()` (mirror of `toSessionRemote`)
14
+ * - `buildDockerBaseArgs()` / `buildDockerCreateArgs()` (mirror of `buildSshConnectionArgs`)
15
+ * - `checkDockerAvailable()` / `checkDockerTmuxAvailable()` (mirror of `checkRemoteTmuxAvailable`)
16
+ *
17
+ * The launch/kill command orchestration (`buildDockerLaunchCommand`,
18
+ * `buildDockerKillCommand`, `dockerTmuxSessionName`) lives in `tmux-manager.ts`,
19
+ * mirroring where `buildRemoteLaunchCommand` lives.
20
+ *
21
+ * @module docker-hosts
22
+ */
23
+ import type { DockerCase, DockerEngine, DockerHost, DockerResourceLimits, SessionDocker, SessionMode } from './types.js';
24
+ /** Locally-built base image (see scripts/build-agent-image.mjs). */
25
+ export declare const DEFAULT_AGENT_IMAGE = "codeman/agent:base";
26
+ /** HOME inside the base image (the `agent` user). Cred mounts + hook-secret land under it. */
27
+ export declare const CONTAINER_HOME = "/home/agent";
28
+ /** Sensible resource defaults (all overridable per host). */
29
+ export declare const DEFAULT_DOCKER_RESOURCES: DockerResourceLimits;
30
+ export declare function dockerHostsPath(configDir: string): string;
31
+ export declare function dockerCasesPath(configDir: string): string;
32
+ export declare function readDockerHosts(configDir: string): Promise<DockerHost[]>;
33
+ export declare function writeDockerHosts(configDir: string, hosts: DockerHost[]): Promise<void>;
34
+ export declare function readDockerCases(configDir: string): Promise<DockerCase[]>;
35
+ export declare function writeDockerCases(configDir: string, cases: DockerCase[]): Promise<void>;
36
+ /** Per-case container name. Mirrors how remote derives a stable name from the case. */
37
+ export declare function dockerContainerName(caseName: string): string;
38
+ /** Default pane command per CLI mode (mirror of defaultRemoteCommandForMode). */
39
+ export declare function defaultDockerCommandForMode(mode: SessionMode): string;
40
+ /** `container:/workdir` display string (mirror of remoteDisplayPath's `user@host:path`). */
41
+ export declare function dockerDisplayPath(docker: Pick<SessionDocker, 'containerName' | 'containerWorkdir'> | {
42
+ container: string;
43
+ path: string;
44
+ }): string;
45
+ /**
46
+ * The host-callback gateway alias is ENGINE-SPECIFIC: Docker exposes the host as
47
+ * `host.docker.internal`, Podman as `host.containers.internal`. Both are added to
48
+ * the host-guard allowlist so a mixed fleet keeps working.
49
+ */
50
+ export declare function hostGatewayAlias(engine: DockerEngine): string;
51
+ /**
52
+ * Rewrite the server's own `CODEMAN_API_URL` to a container-reachable one by
53
+ * swapping ONLY the hostname for the engine's host-gateway alias, preserving
54
+ * scheme AND port (prod is HTTPS on 3000, so hardcoding http://…:3000 breaks
55
+ * every hook). Falls back to `https://<alias>:3000` when the input is absent or
56
+ * unparseable.
57
+ */
58
+ export declare function containerApiUrl(processApiUrl: string | undefined, engine: DockerEngine): string;
59
+ /**
60
+ * Stable hash of the drift-relevant `docker create` inputs, stored on the
61
+ * container as the `codeman.confighash` label. On launch, a mismatch between the
62
+ * desired hash and the running container's label triggers the recreate-on-drift
63
+ * prompt (host config edits actually take effect).
64
+ */
65
+ export declare function dockerConfigHash(docker: Pick<SessionDocker, 'engine' | 'image' | 'containerWorkdir' | 'network' | 'networkName' | 'resources' | 'gpus' | 'mountCredentials' | 'extraCreateArgs'>): string;
66
+ /**
67
+ * Build the flattened per-session Docker metadata from a host profile + a case,
68
+ * resolving every default (mirror of toSessionRemote). The `configHash` is
69
+ * computed last over the resolved values.
70
+ */
71
+ export declare function toSessionDocker(host: DockerHost, dockerCase: DockerCase): SessionDocker;
72
+ /**
73
+ * POSIX single-quote shell-escaping (end-quote, escaped-quote, restart-quote).
74
+ * Mirror of the helper in remote-hosts.ts / tmux-manager.ts. Every dynamic value
75
+ * interpolated into the outer `bash -c "..."` launch layer is escaped through
76
+ * this so a path with spaces stays a single shell token. Operator-entered fields
77
+ * are ALSO schema-rejected for `$`/backtick (NO_SHELL_META) as defense in depth.
78
+ */
79
+ export declare function shellescape(str: string): string;
80
+ /** A resolved bind mount (source existence already checked by the caller). */
81
+ export interface DockerMount {
82
+ src: string;
83
+ dst: string;
84
+ readonly?: boolean;
85
+ }
86
+ /**
87
+ * Resolved, IO-free context for buildDockerCreateArgs. The caller (tmux-manager)
88
+ * resolves the environment-dependent bits (host uid, existing cred mounts, the
89
+ * derived api url, Desktop detection) so this builder stays pure and unit-testable.
90
+ */
91
+ export interface DockerCreateContext {
92
+ docker: SessionDocker;
93
+ /** Codeman session id (only the first 8 chars are used, for the codeman.session label). */
94
+ sessionId: string;
95
+ /** CODEMAN_INSTANCE ('' for prod) — scopes the boot reaper so a beta never reaps prod. */
96
+ instance: string;
97
+ /** Pre-resolved uid/userns tokens: ['--user','1000:0'] | ['--userns','keep-id'] | []. */
98
+ userArgs: string[];
99
+ /** Existing host credential bind mounts (convenient mode). Empty in sealed mode. */
100
+ credentialMounts: DockerMount[];
101
+ /** Extra bind mounts (e.g. the read-only hook-secret file). */
102
+ extraMounts: DockerMount[];
103
+ /** Create-time env (NON-secret, committed-safe): HOME, TERM, COLORTERM, CODEMAN_API_URL, CODEMAN_HOOK_SECRET_FILE. */
104
+ envCreate: Record<string, string>;
105
+ /** Whether to add `--add-host <alias>:host-gateway` (skipped on Docker Desktop, where the alias is native). */
106
+ addHostGateway: boolean;
107
+ /** Engine host-gateway alias (host.docker.internal / host.containers.internal). */
108
+ gatewayAlias: string;
109
+ }
110
+ /**
111
+ * Engine prefix tokens shared by every docker invocation (mirror of
112
+ * buildSshConnectionArgs). Returns e.g. ['docker'] or ['podman','--context','ctx'].
113
+ */
114
+ export declare function buildDockerBaseArgs(docker: Pick<SessionDocker, 'engine' | 'context' | 'daemonHost'>): string[];
115
+ /**
116
+ * Build the `docker create` token list (from `create` through the `sleep
117
+ * infinity` CMD) for a long-lived, hardened, per-case container. PURE: every
118
+ * dynamic value is shellescaped; the caller joins with spaces into the launch
119
+ * string. Security invariants baked in: --cap-drop ALL, --security-opt
120
+ * no-new-privileges, --pids-limit, --memory==--memory-swap, --init,
121
+ * --pull=never, --restart no, NEVER --privileged, NEVER the docker socket.
122
+ */
123
+ export declare function buildDockerCreateArgs(ctx: DockerCreateContext): string[];
124
+ /**
125
+ * Resolve which host credential dirs/files EXIST and map them to their container
126
+ * HOME location. Only-existing avoids docker auto-creating root-owned empty dirs
127
+ * in the user's home. `~/.claude` also carries the transcripts (bind-mounted so
128
+ * host watchers + `--resume` see them) and is therefore mounted read-WRITE.
129
+ */
130
+ export declare function resolveCredentialMounts(home?: string): DockerMount[];
131
+ export interface DockerAvailability {
132
+ ok: boolean;
133
+ engine: DockerEngine;
134
+ rootless: boolean;
135
+ isDesktop: boolean;
136
+ cgroupV2: boolean;
137
+ /** Best-effort: are --memory/--cpus/--pids-limit actually enforced on this engine? */
138
+ capsEnforced: boolean;
139
+ error?: string;
140
+ }
141
+ /**
142
+ * Probe the container engine: server up, cgroup version, rootless, Desktop, and
143
+ * whether resource caps are enforceable. Auto-detects docker then podman when no
144
+ * engine is given. No-op canned value under VITEST.
145
+ */
146
+ export declare function checkDockerAvailable(engine?: DockerEngine): Promise<DockerAvailability>;
147
+ /** Is the base image present locally? (never triggers an auto-pull). */
148
+ export declare function checkDockerImagePresent(engine: DockerEngine, image: string): Promise<boolean>;
149
+ export interface DockerTmuxCheckResult {
150
+ ok: boolean;
151
+ tmuxPath?: string;
152
+ /** Distinguishes "image missing" (build it) from "tmux missing in image" (rebuild it). */
153
+ imageMissing?: boolean;
154
+ error?: string;
155
+ }
156
+ /**
157
+ * Verify the base image is present AND contains tmux (a HARD prerequisite: the
158
+ * in-container tmux is what makes reconnect durable). Never triggers a pull
159
+ * (`--pull=never`). No-op under VITEST. Mirror of checkRemoteTmuxAvailable.
160
+ */
161
+ export declare function checkDockerTmuxAvailable(docker: Pick<SessionDocker, 'engine' | 'image'>): Promise<DockerTmuxCheckResult>;
162
+ /**
163
+ * Resolve the host's IP on the default docker bridge (the address a container
164
+ * reaches as `host.docker.internal`), so the server can bind a hooks-only listener
165
+ * there and in-container hooks can call back. Defaults to the conventional
166
+ * 172.17.0.1 when the inspect fails but docker is up; null when docker is absent.
167
+ * No-op canned value under VITEST.
168
+ */
169
+ export declare function detectDockerBridgeGateway(engine?: DockerEngine): Promise<string | null>;
170
+ /**
171
+ * Instance-scoped boot reaper: `docker rm -f` any MANAGED container that belongs
172
+ * to THIS instance (by the `codeman.instance` label) but whose case is no longer
173
+ * in `docker-cases.json`. The instance scoping is what stops a beta from reaping
174
+ * prod's containers (the cross-instance hazard). No-op under VITEST. Best-effort.
175
+ */
176
+ export declare function reapOrphanedDockerContainers(configDir: string, instance: string, engine?: DockerEngine): Promise<string[]>;
177
+ /**
178
+ * Read the IN-CONTAINER Claude CLI version (`docker exec <container> claude
179
+ * --version`). Feeds Session.cliVersion for docker sessions (the LOCAL claude
180
+ * would report the wrong version and disable trackpad wheel-forwarding, #154).
181
+ * Returns undefined on any failure. No-op under VITEST.
182
+ */
183
+ export declare function probeDockerCliVersion(docker: Pick<SessionDocker, 'engine' | 'containerName'>, mode: SessionMode): Promise<string | undefined>;
184
+ //# sourceMappingURL=docker-hosts.d.ts.map