@suzuke/agend 1.0.0 → 1.1.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 (71) hide show
  1. package/README.md +50 -528
  2. package/README.zh-TW.md +49 -474
  3. package/dist/channel/adapters/discord.js +1 -1
  4. package/dist/channel/adapters/discord.js.map +1 -1
  5. package/dist/channel/factory.js +2 -2
  6. package/dist/channel/factory.js.map +1 -1
  7. package/dist/channel/mcp-tools.js +3 -3
  8. package/dist/channel/mcp-tools.js.map +1 -1
  9. package/dist/cli.js +1 -1
  10. package/dist/cli.js.map +1 -1
  11. package/dist/daemon.d.ts +14 -2
  12. package/dist/daemon.js +119 -46
  13. package/dist/daemon.js.map +1 -1
  14. package/dist/export-import.js +2 -2
  15. package/dist/export-import.js.map +1 -1
  16. package/dist/fleet-manager.d.ts +1 -0
  17. package/dist/fleet-manager.js +15 -9
  18. package/dist/fleet-manager.js.map +1 -1
  19. package/dist/setup-wizard.js +2 -2
  20. package/dist/setup-wizard.js.map +1 -1
  21. package/dist/tmux-control.d.ts +49 -0
  22. package/dist/tmux-control.js +184 -0
  23. package/dist/tmux-control.js.map +1 -0
  24. package/dist/tmux-manager.js +3 -2
  25. package/dist/tmux-manager.js.map +1 -1
  26. package/package.json +3 -5
  27. package/dist/approval/approval-server.d.ts +0 -30
  28. package/dist/approval/approval-server.js +0 -156
  29. package/dist/approval/approval-server.js.map +0 -1
  30. package/dist/approval/tmux-prompt-detector.d.ts +0 -34
  31. package/dist/approval/tmux-prompt-detector.js +0 -264
  32. package/dist/approval/tmux-prompt-detector.js.map +0 -1
  33. package/dist/backend/approval-strategy.d.ts +0 -14
  34. package/dist/backend/approval-strategy.js +0 -2
  35. package/dist/backend/approval-strategy.js.map +0 -1
  36. package/dist/backend/hook-based-approval.d.ts +0 -20
  37. package/dist/backend/hook-based-approval.js +0 -41
  38. package/dist/backend/hook-based-approval.js.map +0 -1
  39. package/dist/container-manager.d.ts +0 -24
  40. package/dist/container-manager.js +0 -148
  41. package/dist/container-manager.js.map +0 -1
  42. package/dist/db.d.ts +0 -10
  43. package/dist/db.js +0 -43
  44. package/dist/db.js.map +0 -1
  45. package/dist/install-recorder.d.ts +0 -30
  46. package/dist/install-recorder.js +0 -159
  47. package/dist/install-recorder.js.map +0 -1
  48. package/dist/meeting/orchestrator.d.ts +0 -30
  49. package/dist/meeting/orchestrator.js +0 -355
  50. package/dist/meeting/orchestrator.js.map +0 -1
  51. package/dist/meeting/prompt-builder.d.ts +0 -12
  52. package/dist/meeting/prompt-builder.js +0 -96
  53. package/dist/meeting/prompt-builder.js.map +0 -1
  54. package/dist/meeting/role-assigner.d.ts +0 -2
  55. package/dist/meeting/role-assigner.js +0 -25
  56. package/dist/meeting/role-assigner.js.map +0 -1
  57. package/dist/meeting/types.d.ts +0 -21
  58. package/dist/meeting/types.js +0 -2
  59. package/dist/meeting/types.js.map +0 -1
  60. package/dist/meeting-manager.d.ts +0 -10
  61. package/dist/meeting-manager.js +0 -38
  62. package/dist/meeting-manager.js.map +0 -1
  63. package/dist/memory-layer.d.ts +0 -13
  64. package/dist/memory-layer.js +0 -44
  65. package/dist/memory-layer.js.map +0 -1
  66. package/dist/plugin/agend/.mcp.json +0 -9
  67. package/dist/plugin/ccd-channel/.claude-plugin/plugin.json +0 -5
  68. package/dist/plugin/ccd-channel/.mcp.json +0 -9
  69. package/dist/process-manager.d.ts +0 -31
  70. package/dist/process-manager.js +0 -264
  71. package/dist/process-manager.js.map +0 -1
@@ -1,264 +0,0 @@
1
- import { readFileSync, statSync, writeFileSync, existsSync } from "node:fs";
2
- import { join } from "node:path";
3
- /** Strip ANSI escape codes from terminal output */
4
- function stripAnsi(text) {
5
- // eslint-disable-next-line no-control-regex
6
- return text.replace(/\x1b\[\d*C/g, " ") // cursor forward → space
7
- .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") // other CSI sequences
8
- .replace(/\x1b\][^\x07]*\x07/g, "") // OSC sequences
9
- .replace(/\x1b[()][0-9A-B]/g, "") // charset switches
10
- .replace(/[\x00-\x08\x0e-\x1f]/g, ""); // misc control chars
11
- }
12
- /** Detect whether text contains a Claude Code interactive prompt */
13
- export function detectInteractivePrompt(text) {
14
- const clean = stripAnsi(text);
15
- // All Claude Code interactive prompts have numbered options like "1." or "❯ 1."
16
- // combined with "Esc to cancel" or "Enter to confirm"
17
- const hasNumberedOption = /[❯>]?\s*1\.\s/.test(clean);
18
- const hasPromptChrome = /Esc to cancel|Enter to confirm/.test(clean);
19
- return hasNumberedOption && hasPromptChrome;
20
- }
21
- /** Classify a detected prompt to determine handling strategy */
22
- export function classifyPrompt(text) {
23
- const clean = stripAnsi(text);
24
- // Permission / tool use: "Do you want to proceed?" with Yes/No
25
- if (/Do you want to proceed/i.test(clean) && /\bYes\b/.test(clean) && /\bNo\b/.test(clean)) {
26
- return "permission";
27
- }
28
- // Settings error: has "Settings Error" or "Continue without these settings"
29
- if (/Settings Error/i.test(clean) || /Continue without these settings/i.test(clean)) {
30
- return "settings_error";
31
- }
32
- // Dev channels: "I am using this for local development"
33
- if (/I am using this for local development/i.test(clean)) {
34
- return "dev_channels";
35
- }
36
- // MCP trust: "New MCP server found" or "Use this and all future"
37
- if (/New MCP server found/i.test(clean) || /Use this and all future/i.test(clean)) {
38
- return "mcp_trust";
39
- }
40
- // File creation: "Do you want to create"
41
- if (/Do you want to create/i.test(clean)) {
42
- return "file_creation";
43
- }
44
- return "unknown";
45
- }
46
- // ── Kept for backwards compat with tests ─────────────────────────────────────
47
- export function detectPermissionPrompt(text) {
48
- const clean = stripAnsi(text);
49
- return /1\.\s*Yes\b/.test(clean) && /3\.\s*No\b/.test(clean);
50
- }
51
- /**
52
- * Extract tool name from Claude Code permission prompt text.
53
- * Returns the permission-format tool name (e.g. "mcp__puppeteer__puppeteer_navigate").
54
- */
55
- export function extractToolPattern(text) {
56
- const clean = stripAnsi(text);
57
- // "don't" may appear as don't, don.t, or dont (apostrophe stripped by terminal)
58
- const m = clean.match(/don.?t ask again for\s+(.+?)\s+commands?\s+in\b/i);
59
- if (!m)
60
- return null;
61
- const display = m[1].trim();
62
- // MCP tool: "server - tool_name" → "mcp__server__tool_name"
63
- const mcpMatch = display.match(/^(\S+)\s*-\s*(\S+)$/);
64
- if (mcpMatch) {
65
- return `mcp__${mcpMatch[1]}__${mcpMatch[2]}`;
66
- }
67
- // Built-in tool: "Bash" → "Bash(*)"
68
- return `${display}(*)`;
69
- }
70
- /** Build a clean prompt message for Telegram display */
71
- export function formatPromptForDisplay(text) {
72
- const clean = stripAnsi(text)
73
- .replace(/\r/g, "")
74
- .replace(/\n{3,}/g, "\n\n")
75
- .trim();
76
- // Tool use prompt: extract tool name and args
77
- const toolMatch = clean.match(/(\S+\s*-\s*\S+)\s*\(([^)]*)\)\s*\(MCP\)/i)
78
- ?? clean.match(/(\S+)\s*\(([^)]*)\)/);
79
- if (toolMatch) {
80
- const tool = toolMatch[1].trim();
81
- const args = toolMatch[2].trim();
82
- const truncatedArgs = args.length > 200 ? args.slice(0, 200) + "…" : args;
83
- return `⚠️ ${tool}\n\`\`\`\n${truncatedArgs}\n\`\`\``;
84
- }
85
- // Fallback: cleaned text, truncated
86
- const truncated = clean.length > 500 ? clean.slice(0, 500) + "…" : clean;
87
- return `⚠️ Prompt\n${truncated}`;
88
- }
89
- // ── Persistent tool allowlist ────────────────────────────────────────────────
90
- const ALLOWLIST_FILE = "tool-allowlist.json";
91
- export function loadToolAllowlist(instanceDir) {
92
- const p = join(instanceDir, ALLOWLIST_FILE);
93
- if (!existsSync(p))
94
- return [];
95
- try {
96
- const data = JSON.parse(readFileSync(p, "utf8"));
97
- return Array.isArray(data) ? data : [];
98
- }
99
- catch {
100
- return [];
101
- }
102
- }
103
- export function saveToolToAllowlist(instanceDir, pattern) {
104
- const list = loadToolAllowlist(instanceDir);
105
- if (!list.includes(pattern)) {
106
- list.push(pattern);
107
- writeFileSync(join(instanceDir, ALLOWLIST_FILE), JSON.stringify(list, null, 2));
108
- }
109
- }
110
- // ── Prompt handler helpers ───────────────────────────────────────────────────
111
- /**
112
- * Select option N in a Claude Code interactive menu.
113
- * Option 1 is pre-selected (❯), so:
114
- * option 1 → Enter
115
- * option 2 → Down + Enter
116
- * option 3 → Down + Down + Enter
117
- */
118
- async function selectOption(tmux, option) {
119
- for (let i = 1; i < option; i++) {
120
- await tmux.sendSpecialKey("Down");
121
- }
122
- await tmux.sendSpecialKey("Enter");
123
- }
124
- async function pressEscape(tmux) {
125
- await tmux.sendSpecialKey("Escape");
126
- }
127
- // ── Main detector ────────────────────────────────────────────────────────────
128
- export class TmuxPromptDetector {
129
- outputLogPath;
130
- tmux;
131
- approvalFn;
132
- logger;
133
- instanceDir;
134
- pollTimer = null;
135
- byteOffset = 0;
136
- pendingApproval = false;
137
- constructor(outputLogPath, tmux, approvalFn, logger, instanceDir) {
138
- this.outputLogPath = outputLogPath;
139
- this.tmux = tmux;
140
- this.approvalFn = approvalFn;
141
- this.logger = logger;
142
- this.instanceDir = instanceDir;
143
- }
144
- startPolling(intervalMs = 2000) {
145
- if (this.pollTimer !== null)
146
- return;
147
- // Skip existing content — only detect prompts written after we start
148
- try {
149
- this.byteOffset = statSync(this.outputLogPath).size;
150
- }
151
- catch { /* file may not exist yet */ }
152
- this.pollTimer = setInterval(async () => {
153
- // Read new content from log file
154
- let newContent;
155
- try {
156
- const stat = statSync(this.outputLogPath);
157
- const fileSize = stat.size;
158
- if (fileSize <= this.byteOffset)
159
- return;
160
- const buf = Buffer.alloc(fileSize - this.byteOffset);
161
- const fd = await import("node:fs").then(fs => fs.openSync(this.outputLogPath, "r"));
162
- const { readSync, closeSync } = await import("node:fs");
163
- const bytesRead = readSync(fd, buf, 0, buf.length, this.byteOffset);
164
- closeSync(fd);
165
- if (bytesRead <= 0)
166
- return;
167
- newContent = buf.subarray(0, bytesRead).toString("utf8");
168
- this.byteOffset += bytesRead;
169
- }
170
- catch {
171
- // File may not exist yet (ENOENT); silently ignore
172
- return;
173
- }
174
- // Detect and handle prompts
175
- try {
176
- if (!detectInteractivePrompt(newContent) || this.pendingApproval)
177
- return;
178
- const promptType = classifyPrompt(newContent);
179
- this.logger.info({ promptType }, "TmuxPromptDetector: interactive prompt detected");
180
- switch (promptType) {
181
- case "dev_channels":
182
- case "mcp_trust":
183
- // Auto-confirm: option 1 is already selected
184
- await selectOption(this.tmux, 1);
185
- this.logger.info({ promptType }, "TmuxPromptDetector: auto-confirmed");
186
- break;
187
- case "settings_error":
188
- // "Continue without these settings" is option 2
189
- await selectOption(this.tmux, 2);
190
- this.logger.info("TmuxPromptDetector: auto-continued past settings error");
191
- break;
192
- case "file_creation":
193
- // Auto-deny file creation prompts (SKILL.md etc.)
194
- await pressEscape(this.tmux);
195
- this.logger.info("TmuxPromptDetector: auto-denied file creation");
196
- break;
197
- case "permission":
198
- // Forward to user via Telegram
199
- this.pendingApproval = true;
200
- try {
201
- const toolPattern = extractToolPattern(newContent);
202
- const cleanPrompt = formatPromptForDisplay(newContent);
203
- const result = await this.approvalFn(cleanPrompt);
204
- this.logger.info({ decision: result.decision }, "TmuxPromptDetector: user responded");
205
- if (result.decision === "always_allow") {
206
- await selectOption(this.tmux, 2); // "Yes, and don't ask again"
207
- if (toolPattern && this.instanceDir) {
208
- saveToolToAllowlist(this.instanceDir, toolPattern);
209
- this.logger.info({ toolPattern }, "TmuxPromptDetector: added to allowlist");
210
- }
211
- }
212
- else if (result.decision === "approve") {
213
- await selectOption(this.tmux, 1); // "Yes"
214
- }
215
- else {
216
- await selectOption(this.tmux, 3); // "No"
217
- }
218
- }
219
- catch (err) {
220
- this.logger.warn("TmuxPromptDetector: approval error, denying", err);
221
- await pressEscape(this.tmux);
222
- }
223
- finally {
224
- this.pendingApproval = false;
225
- }
226
- break;
227
- case "unknown":
228
- default:
229
- // Forward unknown prompts to user too
230
- this.pendingApproval = true;
231
- try {
232
- const cleanPrompt = formatPromptForDisplay(newContent);
233
- const result = await this.approvalFn(cleanPrompt);
234
- this.logger.info({ decision: result.decision }, "TmuxPromptDetector: user responded to unknown prompt");
235
- if (result.decision === "deny") {
236
- await pressEscape(this.tmux);
237
- }
238
- else {
239
- await selectOption(this.tmux, 1);
240
- }
241
- }
242
- catch (err) {
243
- this.logger.error({ err }, "Unknown prompt approval error");
244
- await pressEscape(this.tmux);
245
- }
246
- finally {
247
- this.pendingApproval = false;
248
- }
249
- break;
250
- }
251
- }
252
- catch (err) {
253
- this.logger.error({ err }, "Prompt detection error");
254
- }
255
- }, intervalMs);
256
- }
257
- stop() {
258
- if (this.pollTimer !== null) {
259
- clearInterval(this.pollTimer);
260
- this.pollTimer = null;
261
- }
262
- }
263
- }
264
- //# sourceMappingURL=tmux-prompt-detector.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tmux-prompt-detector.js","sourceRoot":"","sources":["../../src/approval/tmux-prompt-detector.ts"],"names":[],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- export interface ApprovalStrategy {
2
- /**
3
- * Return hook definitions to merge into CLI settings.
4
- * Hook-based: returns { hooks: { PreToolUse: [...] } }
5
- * Shell-wrapper: returns {} (no hooks needed)
6
- */
7
- setup(port: number): {
8
- hooks?: Record<string, unknown>;
9
- };
10
- /** Start the approval service. Returns the actual port. */
11
- start(): Promise<number>;
12
- /** Stop the approval service */
13
- stop(): Promise<void>;
14
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=approval-strategy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"approval-strategy.js","sourceRoot":"","sources":["../../src/backend/approval-strategy.ts"],"names":[],"mappings":""}
@@ -1,20 +0,0 @@
1
- import type { ApprovalStrategy } from "./approval-strategy.js";
2
- import type { MessageBus } from "../channel/message-bus.js";
3
- import type { IpcServer } from "../channel/ipc-bridge.js";
4
- export interface HookBasedApprovalOptions {
5
- messageBus: MessageBus;
6
- port: number;
7
- ipcServer?: IpcServer | null;
8
- topicMode?: boolean;
9
- instanceName?: string;
10
- }
11
- export declare class HookBasedApproval implements ApprovalStrategy {
12
- private opts;
13
- private server;
14
- constructor(opts: HookBasedApprovalOptions);
15
- setup(port: number): {
16
- hooks: Record<string, unknown>;
17
- };
18
- start(): Promise<number>;
19
- stop(): Promise<void>;
20
- }
@@ -1,41 +0,0 @@
1
- import { ApprovalServer } from "../approval/approval-server.js";
2
- export class HookBasedApproval {
3
- opts;
4
- server;
5
- constructor(opts) {
6
- this.opts = opts;
7
- this.server = new ApprovalServer({
8
- messageBus: opts.messageBus,
9
- port: opts.port,
10
- ipcServer: opts.ipcServer,
11
- topicMode: opts.topicMode,
12
- instanceName: opts.instanceName,
13
- });
14
- }
15
- setup(port) {
16
- const token = this.server.getToken();
17
- return {
18
- hooks: {
19
- PreToolUse: [
20
- {
21
- matcher: "Bash",
22
- hooks: [
23
- {
24
- type: "command",
25
- command: `curl -s -X POST http://127.0.0.1:${port}/approve -H 'Content-Type: application/json' -H 'Authorization: Bearer ${token}' -d @- --max-time 130 --connect-timeout 1 || echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"approval server unreachable"}}'`,
26
- timeout: 135000,
27
- },
28
- ],
29
- },
30
- ],
31
- },
32
- };
33
- }
34
- async start() {
35
- return this.server.start();
36
- }
37
- async stop() {
38
- return this.server.stop();
39
- }
40
- }
41
- //# sourceMappingURL=hook-based-approval.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hook-based-approval.js","sourceRoot":"","sources":["../../src/backend/hook-based-approval.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAYhE,MAAM,OAAO,iBAAiB;IAGR;IAFZ,MAAM,CAAiB;IAE/B,YAAoB,IAA8B;QAA9B,SAAI,GAAJ,IAAI,CAA0B;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC;YAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO;YACL,KAAK,EAAE;gBACL,UAAU,EAAE;oBACV;wBACE,OAAO,EAAE,MAAM;wBACf,KAAK,EAAE;4BACL;gCACE,IAAI,EAAE,SAAS;gCACf,OAAO,EAAE,oCAAoC,IAAI,0EAA0E,KAAK,iMAAiM;gCACjU,OAAO,EAAE,MAAM;6BAChB;yBACF;qBACF;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;CACF"}
@@ -1,24 +0,0 @@
1
- import { type PendingPackages } from "./install-recorder.js";
2
- /**
3
- * Generate Dockerfile RUN lines from pending packages.
4
- */
5
- export declare function generateDockerfilePatch(pending: PendingPackages): string;
6
- export interface ContainerOptions {
7
- projectRoots: string[];
8
- dataDir: string;
9
- ccdInstallDir: string;
10
- extraMounts: string[];
11
- memory?: string;
12
- cpus?: string;
13
- network?: string;
14
- }
15
- export declare class ContainerManager {
16
- isRunning(): Promise<boolean>;
17
- ensureRunning(opts: ContainerOptions): Promise<void>;
18
- destroy(): Promise<void>;
19
- shouldAutoBake(recordPath: string): boolean;
20
- autoBake(recordPath: string, dockerfilePath: string): Promise<{
21
- success: boolean;
22
- packages: PendingPackages;
23
- }>;
24
- }
@@ -1,148 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { readFileSync, writeFileSync } from "node:fs";
3
- import { homedir, tmpdir } from "node:os";
4
- import { dirname, resolve as resolvePath } from "node:path";
5
- import { readPendingPackages, clearPendingPackages } from "./install-recorder.js";
6
- function exec(cmd, args) {
7
- return new Promise((resolve, reject) => {
8
- execFile(cmd, args, (err, stdout, stderr) => {
9
- if (err)
10
- reject(err);
11
- else
12
- resolve({ stdout: stdout, stderr: stderr });
13
- });
14
- });
15
- }
16
- const CONTAINER_NAME = "ccd-shared";
17
- const IMAGE_NAME = "ccd-sandbox:latest";
18
- const BAKE_THRESHOLD_COUNT = 3;
19
- const BAKE_THRESHOLD_HOURS = 24;
20
- /**
21
- * Generate Dockerfile RUN lines from pending packages.
22
- */
23
- export function generateDockerfilePatch(pending) {
24
- const lines = [];
25
- const date = new Date().toISOString().slice(0, 10);
26
- lines.push(`# Auto-baked from Claude's install history (${date})`);
27
- if (pending.apt.length > 0) {
28
- lines.push(`RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends ${pending.apt.join(" ")} && sudo rm -rf /var/lib/apt/lists/*`);
29
- }
30
- if (pending.pip.length > 0) {
31
- lines.push(`RUN pip3 install --break-system-packages ${pending.pip.join(" ")}`);
32
- }
33
- if (pending.cargo.length > 0) {
34
- lines.push(`RUN cargo install ${pending.cargo.join(" ")}`);
35
- }
36
- if (pending.npm.length > 0) {
37
- lines.push(`RUN npm install -g ${pending.npm.join(" ")}`);
38
- }
39
- return lines.join("\n") + "\n";
40
- }
41
- export class ContainerManager {
42
- async isRunning() {
43
- const { stdout } = await exec("docker", ["ps", "-q", "-f", `name=${CONTAINER_NAME}`]);
44
- return stdout.trim().length > 0;
45
- }
46
- async ensureRunning(opts) {
47
- if (await this.isRunning())
48
- return;
49
- const home = homedir();
50
- const network = opts.network ?? "none";
51
- const args = [
52
- "run", "-d",
53
- "--name", CONTAINER_NAME,
54
- "--restart", "unless-stopped",
55
- "--label", "ccd=shared",
56
- ];
57
- // M3: Network isolation (--add-host is incompatible with --network none)
58
- args.push("--network", network);
59
- if (network !== "none") {
60
- args.push("--add-host", "host.docker.internal:host-gateway");
61
- }
62
- // M2: Resource limits
63
- args.push("--memory", opts.memory ?? "4g", "--cpus", opts.cpus ?? "2");
64
- for (const raw of opts.projectRoots) {
65
- const root = raw.startsWith("~") ? raw.replace("~", home) : raw;
66
- args.push("-v", `${root}:${root}`);
67
- }
68
- args.push("-v", `${home}/.claude:${home}/.claude:ro`);
69
- args.push("-v", `${opts.dataDir}:${opts.dataDir}`);
70
- args.push("-v", `${opts.ccdInstallDir}:${opts.ccdInstallDir}:ro`);
71
- // Mount host tmpdir so Claude Code's cwd-tracking temp files work inside the container
72
- const tmp = tmpdir();
73
- args.push("-v", `${tmp}:${tmp}`);
74
- // C6: Validate extra_mounts against allowed directories
75
- for (const mount of opts.extraMounts) {
76
- const hostPath = mount.split(":")[0];
77
- const resolved = resolvePath(hostPath);
78
- if (resolved.includes("..")) {
79
- throw new Error(`Extra mount "${mount}" contains path traversal`);
80
- }
81
- const allowed = opts.projectRoots.some(r => resolved.startsWith(r)) || resolved.startsWith(home);
82
- if (!allowed) {
83
- throw new Error(`Extra mount "${mount}" is outside allowed directories (project roots and $HOME)`);
84
- }
85
- args.push("-v", mount);
86
- }
87
- args.push(IMAGE_NAME, "tail", "-f", "/dev/null");
88
- // M4: Fix TOCTOU race – another process may have started the container
89
- try {
90
- await exec("docker", args);
91
- }
92
- catch (err) {
93
- if (await this.isRunning())
94
- return;
95
- throw err;
96
- }
97
- }
98
- async destroy() {
99
- try {
100
- await exec("docker", ["rm", "-f", CONTAINER_NAME]);
101
- }
102
- catch {
103
- // Container might not exist
104
- }
105
- }
106
- shouldAutoBake(recordPath) {
107
- const pending = readPendingPackages(recordPath);
108
- if (pending.count === 0)
109
- return false;
110
- if (pending.count >= BAKE_THRESHOLD_COUNT)
111
- return true;
112
- if (pending.oldestTs) {
113
- const hoursAgo = (Date.now() - pending.oldestTs.getTime()) / (1000 * 60 * 60);
114
- if (hoursAgo >= BAKE_THRESHOLD_HOURS)
115
- return true;
116
- }
117
- return false;
118
- }
119
- async autoBake(recordPath, dockerfilePath) {
120
- const pending = readPendingPackages(recordPath);
121
- if (pending.count === 0)
122
- return { success: true, packages: pending };
123
- const patch = generateDockerfilePatch(pending);
124
- // Save original Dockerfile for rollback on build failure
125
- const original = readFileSync(dockerfilePath, "utf-8");
126
- writeFileSync(dockerfilePath, original + "\n" + patch);
127
- // Rebuild image
128
- try {
129
- await exec("docker", [
130
- "build",
131
- "-f", dockerfilePath,
132
- "-t", IMAGE_NAME,
133
- dirname(dockerfilePath),
134
- ]);
135
- }
136
- catch (err) {
137
- // Rollback Dockerfile on build failure
138
- writeFileSync(dockerfilePath, original);
139
- return { success: false, packages: pending };
140
- }
141
- // Destroy old container so it restarts with new image
142
- await this.destroy();
143
- // Clear pending packages
144
- clearPendingPackages(recordPath);
145
- return { success: true, packages: pending };
146
- }
147
- }
148
- //# sourceMappingURL=container-manager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"container-manager.js","sourceRoot":"","sources":["../src/container-manager.ts"],"names":[],"mappings":"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"}
package/dist/db.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { MemoryBackupRow } from "./types.js";
2
- export declare class MemoryDb {
3
- private db;
4
- constructor(dbPath: string);
5
- insertBackup(filePath: string, content: string, chatId: string | null): void;
6
- getAll(): MemoryBackupRow[];
7
- getByFilePath(filePath: string): MemoryBackupRow[];
8
- pruneOldBackups(keepPerFile?: number): void;
9
- close(): void;
10
- }
package/dist/db.js DELETED
@@ -1,43 +0,0 @@
1
- import Database from "better-sqlite3";
2
- export class MemoryDb {
3
- db;
4
- constructor(dbPath) {
5
- this.db = new Database(dbPath);
6
- this.db.pragma("journal_mode = WAL");
7
- this.db.exec(`
8
- CREATE TABLE IF NOT EXISTS memory_backups (
9
- id INTEGER PRIMARY KEY AUTOINCREMENT,
10
- file_path TEXT NOT NULL,
11
- content TEXT NOT NULL,
12
- chat_id TEXT,
13
- backed_up_at TEXT NOT NULL DEFAULT (datetime('now'))
14
- )
15
- `);
16
- }
17
- insertBackup(filePath, content, chatId) {
18
- this.db
19
- .prepare("INSERT INTO memory_backups (file_path, content, chat_id) VALUES (?, ?, ?)")
20
- .run(filePath, content, chatId);
21
- }
22
- getAll() {
23
- return this.db
24
- .prepare("SELECT * FROM memory_backups ORDER BY backed_up_at DESC")
25
- .all();
26
- }
27
- getByFilePath(filePath) {
28
- return this.db
29
- .prepare("SELECT * FROM memory_backups WHERE file_path = ? ORDER BY backed_up_at DESC")
30
- .all(filePath);
31
- }
32
- pruneOldBackups(keepPerFile = 10) {
33
- const files = this.db.prepare("SELECT DISTINCT file_path FROM memory_backups").all();
34
- const deleteStmt = this.db.prepare("DELETE FROM memory_backups WHERE file_path = ? AND id NOT IN (SELECT id FROM memory_backups WHERE file_path = ? ORDER BY backed_up_at DESC LIMIT ?)");
35
- for (const { file_path } of files) {
36
- deleteStmt.run(file_path, file_path, keepPerFile);
37
- }
38
- }
39
- close() {
40
- this.db.close();
41
- }
42
- }
43
- //# sourceMappingURL=db.js.map
package/dist/db.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,MAAM,OAAO,QAAQ;IACX,EAAE,CAAoB;IAE9B,YAAY,MAAc;QACxB,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;KAQZ,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,QAAgB,EAAE,OAAe,EAAE,MAAqB;QACnE,IAAI,CAAC,EAAE;aACJ,OAAO,CAAC,2EAA2E,CAAC;aACpF,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,yDAAyD,CAAC;aAClE,GAAG,EAAuB,CAAC;IAChC,CAAC;IAED,aAAa,CAAC,QAAgB;QAC5B,OAAO,IAAI,CAAC,EAAE;aACX,OAAO,CAAC,6EAA6E,CAAC;aACtF,GAAG,CAAC,QAAQ,CAAsB,CAAC;IACxC,CAAC;IAED,eAAe,CAAC,WAAW,GAAG,EAAE;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,EAA6B,CAAC;QAChH,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAChC,qJAAqJ,CACtJ,CAAC;QACF,KAAK,MAAM,EAAE,SAAS,EAAE,IAAI,KAAK,EAAE,CAAC;YAClC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}
@@ -1,30 +0,0 @@
1
- export interface InstallCommand {
2
- type: "pip" | "apt" | "cargo" | "npm";
3
- packages: string[];
4
- }
5
- /**
6
- * Parse a shell command and detect if it's a package install command.
7
- * For multiline / chained commands (&&), each segment is checked.
8
- * Returns the first matching install command, or null.
9
- */
10
- export declare function parseInstallCommand(command: string): InstallCommand | null;
11
- /**
12
- * Record an install command to a file, deduplicating against existing entries.
13
- */
14
- export declare function recordInstall(filePath: string, install: InstallCommand): void;
15
- export interface PendingPackages {
16
- apt: string[];
17
- pip: string[];
18
- cargo: string[];
19
- npm: string[];
20
- count: number;
21
- oldestTs: Date | null;
22
- }
23
- /**
24
- * Read pending packages from the record file.
25
- */
26
- export declare function readPendingPackages(filePath: string): PendingPackages;
27
- /**
28
- * Clear all pending packages by truncating the file.
29
- */
30
- export declare function clearPendingPackages(filePath: string): void;