@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.
- package/README.md +50 -528
- package/README.zh-TW.md +49 -474
- package/dist/channel/adapters/discord.js +1 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/factory.js +2 -2
- package/dist/channel/factory.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +14 -2
- package/dist/daemon.js +119 -46
- package/dist/daemon.js.map +1 -1
- package/dist/export-import.js +2 -2
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-manager.d.ts +1 -0
- package/dist/fleet-manager.js +15 -9
- package/dist/fleet-manager.js.map +1 -1
- package/dist/setup-wizard.js +2 -2
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tmux-control.d.ts +49 -0
- package/dist/tmux-control.js +184 -0
- package/dist/tmux-control.js.map +1 -0
- package/dist/tmux-manager.js +3 -2
- package/dist/tmux-manager.js.map +1 -1
- package/package.json +3 -5
- package/dist/approval/approval-server.d.ts +0 -30
- package/dist/approval/approval-server.js +0 -156
- package/dist/approval/approval-server.js.map +0 -1
- package/dist/approval/tmux-prompt-detector.d.ts +0 -34
- package/dist/approval/tmux-prompt-detector.js +0 -264
- package/dist/approval/tmux-prompt-detector.js.map +0 -1
- package/dist/backend/approval-strategy.d.ts +0 -14
- package/dist/backend/approval-strategy.js +0 -2
- package/dist/backend/approval-strategy.js.map +0 -1
- package/dist/backend/hook-based-approval.d.ts +0 -20
- package/dist/backend/hook-based-approval.js +0 -41
- package/dist/backend/hook-based-approval.js.map +0 -1
- package/dist/container-manager.d.ts +0 -24
- package/dist/container-manager.js +0 -148
- package/dist/container-manager.js.map +0 -1
- package/dist/db.d.ts +0 -10
- package/dist/db.js +0 -43
- package/dist/db.js.map +0 -1
- package/dist/install-recorder.d.ts +0 -30
- package/dist/install-recorder.js +0 -159
- package/dist/install-recorder.js.map +0 -1
- package/dist/meeting/orchestrator.d.ts +0 -30
- package/dist/meeting/orchestrator.js +0 -355
- package/dist/meeting/orchestrator.js.map +0 -1
- package/dist/meeting/prompt-builder.d.ts +0 -12
- package/dist/meeting/prompt-builder.js +0 -96
- package/dist/meeting/prompt-builder.js.map +0 -1
- package/dist/meeting/role-assigner.d.ts +0 -2
- package/dist/meeting/role-assigner.js +0 -25
- package/dist/meeting/role-assigner.js.map +0 -1
- package/dist/meeting/types.d.ts +0 -21
- package/dist/meeting/types.js +0 -2
- package/dist/meeting/types.js.map +0 -1
- package/dist/meeting-manager.d.ts +0 -10
- package/dist/meeting-manager.js +0 -38
- package/dist/meeting-manager.js.map +0 -1
- package/dist/memory-layer.d.ts +0 -13
- package/dist/memory-layer.js +0 -44
- package/dist/memory-layer.js.map +0 -1
- package/dist/plugin/agend/.mcp.json +0 -9
- package/dist/plugin/ccd-channel/.claude-plugin/plugin.json +0 -5
- package/dist/plugin/ccd-channel/.mcp.json +0 -9
- package/dist/process-manager.d.ts +0 -31
- package/dist/process-manager.js +0 -264
- 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 +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;
|