@provos/ironcurtain 0.6.0 → 0.7.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 +21 -2
- package/dist/cli-help.d.ts +63 -0
- package/dist/cli-help.js +102 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.js +73 -56
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +136 -2
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/generated/tool-annotations.json +487 -338
- package/dist/config/index.d.ts +21 -1
- package/dist/config/index.js +12 -4
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-servers.json +2 -8
- package/dist/config/paths.d.ts +30 -0
- package/dist/config/paths.js +58 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/tool-description-hints.json +15 -0
- package/dist/config/types.d.ts +7 -0
- package/dist/config/user-config.d.ts +21 -0
- package/dist/config/user-config.js +8 -0
- package/dist/config/user-config.js.map +1 -1
- package/dist/cron/compile-task-policy.d.ts +17 -0
- package/dist/cron/compile-task-policy.js +39 -0
- package/dist/cron/compile-task-policy.js.map +1 -0
- package/dist/cron/cron-scheduler.d.ts +65 -0
- package/dist/cron/cron-scheduler.js +184 -0
- package/dist/cron/cron-scheduler.js.map +1 -0
- package/dist/cron/file-lock.d.ts +41 -0
- package/dist/cron/file-lock.js +156 -0
- package/dist/cron/file-lock.js.map +1 -0
- package/dist/cron/format-utils.d.ts +18 -0
- package/dist/cron/format-utils.js +145 -0
- package/dist/cron/format-utils.js.map +1 -0
- package/dist/cron/git-sync.d.ts +22 -0
- package/dist/cron/git-sync.js +121 -0
- package/dist/cron/git-sync.js.map +1 -0
- package/dist/cron/headless-transport.d.ts +27 -0
- package/dist/cron/headless-transport.js +43 -0
- package/dist/cron/headless-transport.js.map +1 -0
- package/dist/cron/job-commands.d.ts +39 -0
- package/dist/cron/job-commands.js +522 -0
- package/dist/cron/job-commands.js.map +1 -0
- package/dist/cron/job-store.d.ts +35 -0
- package/dist/cron/job-store.js +111 -0
- package/dist/cron/job-store.js.map +1 -0
- package/dist/cron/types.d.ts +127 -0
- package/dist/cron/types.js +25 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/daemon/control-socket.d.ts +127 -0
- package/dist/daemon/control-socket.js +261 -0
- package/dist/daemon/control-socket.js.map +1 -0
- package/dist/daemon/daemon-command.d.ts +13 -0
- package/dist/daemon/daemon-command.js +230 -0
- package/dist/daemon/daemon-command.js.map +1 -0
- package/dist/daemon/ironcurtain-daemon.d.ts +90 -0
- package/dist/daemon/ironcurtain-daemon.js +548 -0
- package/dist/daemon/ironcurtain-daemon.js.map +1 -0
- package/dist/docker/adapters/claude-code.js +7 -47
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/adapters/goose.d.ts +58 -0
- package/dist/docker/adapters/goose.js +250 -0
- package/dist/docker/adapters/goose.js.map +1 -0
- package/dist/docker/adapters/shared-scripts.d.ts +29 -0
- package/dist/docker/adapters/shared-scripts.js +80 -0
- package/dist/docker/adapters/shared-scripts.js.map +1 -0
- package/dist/docker/agent-adapter.d.ts +12 -0
- package/dist/docker/agent-adapter.js.map +1 -1
- package/dist/docker/agent-registry.d.ts +6 -1
- package/dist/docker/agent-registry.js +10 -1
- package/dist/docker/agent-registry.js.map +1 -1
- package/dist/docker/docker-agent-session.d.ts +2 -25
- package/dist/docker/docker-agent-session.js +7 -106
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +8 -0
- package/dist/docker/docker-infrastructure.js +12 -11
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/pty-session.js +7 -4
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/escalation/escalation-watcher.d.ts +2 -0
- package/dist/escalation/escalation-watcher.js +3 -0
- package/dist/escalation/escalation-watcher.js.map +1 -1
- package/dist/escalation/session-registry.js +1 -14
- package/dist/escalation/session-registry.js.map +1 -1
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/dist/mux/mux-app.js +11 -6
- package/dist/mux/mux-app.js.map +1 -1
- package/dist/mux/mux-command.js +20 -9
- package/dist/mux/mux-command.js.map +1 -1
- package/dist/mux/mux-input-handler.d.ts +5 -0
- package/dist/mux/mux-input-handler.js +64 -9
- package/dist/mux/mux-input-handler.js.map +1 -1
- package/dist/mux/mux-renderer.d.ts +20 -0
- package/dist/mux/mux-renderer.js +127 -35
- package/dist/mux/mux-renderer.js.map +1 -1
- package/dist/mux/mux-splash.js +2 -1
- package/dist/mux/mux-splash.js.map +1 -1
- package/dist/mux/paste-interceptor.d.ts +22 -0
- package/dist/mux/paste-interceptor.js +143 -0
- package/dist/mux/paste-interceptor.js.map +1 -0
- package/dist/mux/pty-bridge.js +1 -1
- package/dist/mux/pty-bridge.js.map +1 -1
- package/dist/mux/types.d.ts +8 -1
- package/dist/mux/types.js +17 -5
- package/dist/mux/types.js.map +1 -1
- package/dist/pipeline/annotate.js +2 -1
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/compile.d.ts +4 -34
- package/dist/pipeline/compile.js +29 -568
- package/dist/pipeline/compile.js.map +1 -1
- package/dist/pipeline/constitution-compiler.js +1 -1
- package/dist/pipeline/mcp-connections.d.ts +16 -0
- package/dist/pipeline/mcp-connections.js +61 -0
- package/dist/pipeline/mcp-connections.js.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +86 -0
- package/dist/pipeline/pipeline-runner.js +554 -0
- package/dist/pipeline/pipeline-runner.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +29 -2
- package/dist/pipeline/pipeline-shared.js +68 -12
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/refresh-lists.js +38 -12
- package/dist/pipeline/refresh-lists.js.map +1 -1
- package/dist/pipeline/scenario-generator.js +2 -1
- package/dist/pipeline/scenario-generator.js.map +1 -1
- package/dist/pipeline/tool-annotator.d.ts +3 -3
- package/dist/pipeline/tool-annotator.js +96 -4
- package/dist/pipeline/tool-annotator.js.map +1 -1
- package/dist/pipeline/types.d.ts +52 -0
- package/dist/sandbox/index.js +36 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/session/agent-session.d.ts +3 -0
- package/dist/session/agent-session.js +10 -1
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/index.js +54 -5
- package/dist/session/index.js.map +1 -1
- package/dist/session/preflight.js +26 -6
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/prompts.d.ts +14 -0
- package/dist/session/prompts.js +39 -2
- package/dist/session/prompts.js.map +1 -1
- package/dist/session/session-manager.d.ts +105 -0
- package/dist/session/session-manager.js +134 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +27 -0
- package/dist/session/workspace-validation.d.ts +2 -0
- package/dist/session/workspace-validation.js +6 -5
- package/dist/session/workspace-validation.js.map +1 -1
- package/dist/signal/bot-command.d.ts +2 -0
- package/dist/signal/bot-command.js +8 -0
- package/dist/signal/bot-command.js.map +1 -1
- package/dist/signal/format.d.ts +27 -0
- package/dist/signal/format.js +59 -0
- package/dist/signal/format.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +27 -25
- package/dist/signal/signal-bot-daemon.js +145 -130
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-container.js +63 -19
- package/dist/signal/signal-container.js.map +1 -1
- package/dist/signal/signal-transport.js +3 -8
- package/dist/signal/signal-transport.js.map +1 -1
- package/dist/trusted-process/domain-utils.d.ts +38 -4
- package/dist/trusted-process/domain-utils.js +118 -11
- package/dist/trusted-process/domain-utils.js.map +1 -1
- package/dist/trusted-process/index.js +6 -2
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/mcp-client-manager.js +11 -5
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-proxy-server.d.ts +2 -0
- package/dist/trusted-process/mcp-proxy-server.js +59 -16
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/permissive-output-validator.d.ts +23 -0
- package/dist/trusted-process/permissive-output-validator.js +10 -0
- package/dist/trusted-process/permissive-output-validator.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +27 -4
- package/dist/trusted-process/policy-engine.js +152 -57
- package/dist/trusted-process/policy-engine.js.map +1 -1
- package/dist/trusted-process/tool-description-hints.d.ts +19 -0
- package/dist/trusted-process/tool-description-hints.js +53 -0
- package/dist/trusted-process/tool-description-hints.js.map +1 -0
- package/dist/types/argument-roles.d.ts +33 -1
- package/dist/types/argument-roles.js +129 -1
- package/dist/types/argument-roles.js.map +1 -1
- package/docker/Dockerfile.goose +34 -0
- package/docker/entrypoint-goose.sh +30 -0
- package/package.json +1 -1
- package/src/config/generated/tool-annotations.json +487 -338
- package/src/config/mcp-servers.json +2 -8
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IronCurtainDaemon -- Unified daemon managing both Signal-initiated
|
|
3
|
+
* interactive sessions and cron-scheduled headless sessions.
|
|
4
|
+
*
|
|
5
|
+
* Lifecycle:
|
|
6
|
+
* 1. start() -- loads jobs, starts Signal (if configured), schedules jobs
|
|
7
|
+
* 2. Runs indefinitely, processing Signal messages and cron triggers
|
|
8
|
+
* 3. shutdown() -- unschedules all jobs, ends all sessions, disconnects
|
|
9
|
+
*
|
|
10
|
+
* Both Signal and cron sessions are managed through a shared SessionManager
|
|
11
|
+
* instance. This enables unified escalation routing: `approve #N` from
|
|
12
|
+
* Signal works for both Signal-initiated and cron-initiated sessions.
|
|
13
|
+
*/
|
|
14
|
+
import { mkdirSync, readFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { syncGitRepo } from '../cron/git-sync.js';
|
|
16
|
+
import { resolve } from 'node:path';
|
|
17
|
+
import { createSession } from '../session/index.js';
|
|
18
|
+
import { loadConfig } from '../config/index.js';
|
|
19
|
+
import { loadUserConfig } from '../config/user-config.js';
|
|
20
|
+
import { getJobWorkspaceDir, getJobGeneratedDir, getJobDir } from '../config/paths.js';
|
|
21
|
+
import { SessionManager } from '../session/session-manager.js';
|
|
22
|
+
import { HeadlessTransport } from '../cron/headless-transport.js';
|
|
23
|
+
import { buildCronSystemPromptAugmentation } from '../session/prompts.js';
|
|
24
|
+
import { createCronScheduler } from '../cron/cron-scheduler.js';
|
|
25
|
+
import { loadAllJobs, loadJob, saveJob, deleteJob, saveRunRecord, loadRecentRuns } from '../cron/job-store.js';
|
|
26
|
+
import { compileTaskPolicy } from '../cron/compile-task-policy.js';
|
|
27
|
+
import { CRON_BUDGET_DEFAULTS } from '../cron/types.js';
|
|
28
|
+
import { BudgetExhaustedError } from '../session/errors.js';
|
|
29
|
+
import { validateWorkspacePath } from '../session/workspace-validation.js';
|
|
30
|
+
import * as logger from '../logger.js';
|
|
31
|
+
import { getDaemonLogPath } from '../config/paths.js';
|
|
32
|
+
import { ControlSocketServer } from './control-socket.js';
|
|
33
|
+
/**
|
|
34
|
+
* Builds an IronCurtainConfig patched for cron session budget defaults.
|
|
35
|
+
*/
|
|
36
|
+
function buildCronSessionConfig(globalConfig, job) {
|
|
37
|
+
const cronBudget = {
|
|
38
|
+
...CRON_BUDGET_DEFAULTS,
|
|
39
|
+
...(job.budgetOverrides ?? {}),
|
|
40
|
+
};
|
|
41
|
+
const patchedUserConfig = {
|
|
42
|
+
...globalConfig.userConfig,
|
|
43
|
+
resourceBudget: {
|
|
44
|
+
...globalConfig.userConfig.resourceBudget,
|
|
45
|
+
...cronBudget,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
...globalConfig,
|
|
50
|
+
userConfig: patchedUserConfig,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export class IronCurtainDaemon {
|
|
54
|
+
mode;
|
|
55
|
+
noSignal;
|
|
56
|
+
sessionManager = new SessionManager();
|
|
57
|
+
scheduler;
|
|
58
|
+
/** Time the daemon was started (for uptime calculation). */
|
|
59
|
+
startTime = null;
|
|
60
|
+
/** Tracks which jobs are currently running (jobId -> session label). */
|
|
61
|
+
activeJobRuns = new Map();
|
|
62
|
+
/** Control socket server for CLI communication. */
|
|
63
|
+
controlSocket = null;
|
|
64
|
+
/** Signal daemon instance (null if Signal not configured). */
|
|
65
|
+
signalDaemon = null;
|
|
66
|
+
/** Resolve to exit the daemon. */
|
|
67
|
+
exitResolve = null;
|
|
68
|
+
constructor(options) {
|
|
69
|
+
this.mode = options.mode;
|
|
70
|
+
this.noSignal = options.noSignal ?? false;
|
|
71
|
+
this.scheduler = createCronScheduler();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Starts the daemon. Loads all enabled jobs, schedules them,
|
|
75
|
+
* optionally starts the Signal transport.
|
|
76
|
+
*/
|
|
77
|
+
async start() {
|
|
78
|
+
this.startTime = new Date();
|
|
79
|
+
// Set up file-based logger (redirects console.* to log file)
|
|
80
|
+
logger.setup({ logFilePath: getDaemonLogPath('daemon') });
|
|
81
|
+
// Start control socket for CLI communication
|
|
82
|
+
await this.startControlSocket();
|
|
83
|
+
// Load and schedule all enabled jobs
|
|
84
|
+
const jobs = loadAllJobs();
|
|
85
|
+
const enabledJobs = jobs.filter((j) => j.enabled);
|
|
86
|
+
logger.info(`[Daemon] Loaded ${jobs.length} job(s), ${enabledJobs.length} enabled`);
|
|
87
|
+
for (const job of enabledJobs) {
|
|
88
|
+
try {
|
|
89
|
+
this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
|
|
90
|
+
const nextRun = this.scheduler.getNextRun(job.id);
|
|
91
|
+
logger.info(`[Daemon] Scheduled job ${job.id}: ${job.schedule} (next: ${nextRun?.toISOString() ?? 'unknown'})`);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
logger.warn(`[Daemon] Failed to schedule job ${job.id}: ${String(err)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Start Signal daemon if configured and not disabled
|
|
98
|
+
if (!this.noSignal) {
|
|
99
|
+
try {
|
|
100
|
+
await this.startSignalDaemon();
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger.warn(`[Daemon] Signal transport not available: ${err instanceof Error ? err.message : String(err)}`);
|
|
104
|
+
logger.info('[Daemon] Continuing in cron-only mode');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
process.stderr.write('IronCurtain daemon started.\n');
|
|
108
|
+
if (this.controlSocket) {
|
|
109
|
+
process.stderr.write(' Control socket: listening\n');
|
|
110
|
+
}
|
|
111
|
+
if (enabledJobs.length > 0) {
|
|
112
|
+
process.stderr.write(` Scheduled jobs: ${enabledJobs.length}\n`);
|
|
113
|
+
}
|
|
114
|
+
if (this.signalDaemon) {
|
|
115
|
+
process.stderr.write(' Signal transport: connected\n');
|
|
116
|
+
}
|
|
117
|
+
// Block until shutdown
|
|
118
|
+
await new Promise((resolve) => {
|
|
119
|
+
this.exitResolve = resolve;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/** Graceful shutdown. */
|
|
123
|
+
async shutdown() {
|
|
124
|
+
logger.info('[Daemon] Shutting down...');
|
|
125
|
+
// Stop control socket first (reject new CLI commands)
|
|
126
|
+
if (this.controlSocket) {
|
|
127
|
+
try {
|
|
128
|
+
await this.controlSocket.stop();
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
logger.warn(`[Daemon] Error stopping control socket: ${String(err)}`);
|
|
132
|
+
}
|
|
133
|
+
this.controlSocket = null;
|
|
134
|
+
}
|
|
135
|
+
// Unschedule all jobs
|
|
136
|
+
this.scheduler.unscheduleAll();
|
|
137
|
+
// Shutdown Signal daemon first (ends Signal sessions via shared SessionManager)
|
|
138
|
+
if (this.signalDaemon) {
|
|
139
|
+
try {
|
|
140
|
+
await this.signalDaemon.shutdown();
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
logger.warn(`[Daemon] Error shutting down Signal: ${String(err)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// End remaining sessions (cron sessions and any Signal stragglers)
|
|
147
|
+
const remaining = this.sessionManager.all();
|
|
148
|
+
await Promise.allSettled(remaining.map(async (s) => {
|
|
149
|
+
try {
|
|
150
|
+
await this.sessionManager.end(s.label);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
logger.warn(`[Daemon] Error ending session #${s.label}: ${String(err)}`);
|
|
154
|
+
}
|
|
155
|
+
}));
|
|
156
|
+
// Teardown logger last (restores console.* to original behavior)
|
|
157
|
+
logger.teardown();
|
|
158
|
+
this.exitResolve?.();
|
|
159
|
+
}
|
|
160
|
+
// -----------------------------------------------------------------------
|
|
161
|
+
// Job management (called by CLI subcommands)
|
|
162
|
+
// -----------------------------------------------------------------------
|
|
163
|
+
/**
|
|
164
|
+
* Adds a new job. Runs the full policy compilation pipeline
|
|
165
|
+
* before confirming. Schedules the job if enabled.
|
|
166
|
+
*/
|
|
167
|
+
async addJob(job) {
|
|
168
|
+
// Validate custom workspace before proceeding
|
|
169
|
+
if (job.workspace) {
|
|
170
|
+
const globalConfig = loadConfig();
|
|
171
|
+
validateWorkspacePath(job.workspace, globalConfig.protectedPaths);
|
|
172
|
+
}
|
|
173
|
+
// Ensure workspace exists
|
|
174
|
+
const workspace = job.workspace ?? getJobWorkspaceDir(job.id);
|
|
175
|
+
mkdirSync(workspace, { recursive: true });
|
|
176
|
+
// Clone git repo if specified
|
|
177
|
+
if (job.gitRepo) {
|
|
178
|
+
logger.info(`[Daemon] Cloning repo for job ${job.id}...`);
|
|
179
|
+
syncGitRepo(job.gitRepo, workspace);
|
|
180
|
+
logger.info(`[Daemon] Repo cloned for job ${job.id}`);
|
|
181
|
+
}
|
|
182
|
+
// Compile per-job policy
|
|
183
|
+
logger.info(`[Daemon] Compiling task policy for job ${job.id}...`);
|
|
184
|
+
await compileTaskPolicy(job.taskConstitution, getJobDir(job.id));
|
|
185
|
+
// Save job definition
|
|
186
|
+
await saveJob(job);
|
|
187
|
+
// Schedule if enabled
|
|
188
|
+
if (job.enabled) {
|
|
189
|
+
this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
|
|
190
|
+
const nextRun = this.scheduler.getNextRun(job.id);
|
|
191
|
+
logger.info(`[Daemon] Job ${job.id} scheduled. Next run: ${nextRun?.toISOString() ?? 'unknown'}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Removes a job. Stops any active run, unschedules, deletes files. */
|
|
195
|
+
async removeJob(jobId) {
|
|
196
|
+
// Stop active run if any
|
|
197
|
+
const activeLabel = this.activeJobRuns.get(jobId);
|
|
198
|
+
if (activeLabel !== undefined) {
|
|
199
|
+
await this.sessionManager.end(activeLabel);
|
|
200
|
+
this.activeJobRuns.delete(jobId);
|
|
201
|
+
}
|
|
202
|
+
this.scheduler.unschedule(jobId);
|
|
203
|
+
deleteJob(jobId);
|
|
204
|
+
logger.info(`[Daemon] Removed job ${jobId}`);
|
|
205
|
+
}
|
|
206
|
+
/** Enables a previously disabled job. Schedules it. */
|
|
207
|
+
async enableJob(jobId) {
|
|
208
|
+
const job = loadJob(jobId);
|
|
209
|
+
if (!job)
|
|
210
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
211
|
+
const updated = { ...job, enabled: true };
|
|
212
|
+
await saveJob(updated);
|
|
213
|
+
this.scheduler.schedule(updated, (j) => this.onJobTrigger(j));
|
|
214
|
+
logger.info(`[Daemon] Enabled job ${jobId}`);
|
|
215
|
+
}
|
|
216
|
+
/** Disables a job. Unschedules it but preserves files. */
|
|
217
|
+
async disableJob(jobId) {
|
|
218
|
+
const job = loadJob(jobId);
|
|
219
|
+
if (!job)
|
|
220
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
221
|
+
const updated = { ...job, enabled: false };
|
|
222
|
+
await saveJob(updated);
|
|
223
|
+
this.scheduler.unschedule(jobId);
|
|
224
|
+
logger.info(`[Daemon] Disabled job ${jobId}`);
|
|
225
|
+
}
|
|
226
|
+
/** Reloads a job definition from disk and reschedules it. */
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- must be async to satisfy ControlRequestHandler interface
|
|
228
|
+
async reloadJob(jobId) {
|
|
229
|
+
const job = loadJob(jobId);
|
|
230
|
+
if (!job)
|
|
231
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
232
|
+
this.scheduler.unschedule(jobId);
|
|
233
|
+
if (job.enabled) {
|
|
234
|
+
this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
|
|
235
|
+
const nextRun = this.scheduler.getNextRun(jobId);
|
|
236
|
+
logger.info(`[Daemon] Reloaded job ${jobId}: rescheduled (next: ${nextRun?.toISOString() ?? 'unknown'})`);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
logger.info(`[Daemon] Reloaded job ${jobId}: disabled, not scheduled`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** Re-runs policy compilation for an existing job. */
|
|
243
|
+
async recompileJob(jobId) {
|
|
244
|
+
const job = loadJob(jobId);
|
|
245
|
+
if (!job)
|
|
246
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
247
|
+
await compileTaskPolicy(job.taskConstitution, getJobDir(jobId));
|
|
248
|
+
logger.info(`[Daemon] Recompiled policy for job ${jobId}`);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Manually triggers a job run (for testing).
|
|
252
|
+
* Does not affect the cron schedule.
|
|
253
|
+
*/
|
|
254
|
+
async runJobNow(jobId) {
|
|
255
|
+
const job = loadJob(jobId);
|
|
256
|
+
if (!job)
|
|
257
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
258
|
+
return this.executeJob(job);
|
|
259
|
+
}
|
|
260
|
+
/** Lists all jobs with their schedules and last run status. */
|
|
261
|
+
listJobs() {
|
|
262
|
+
const jobs = loadAllJobs();
|
|
263
|
+
return jobs.map((job) => ({
|
|
264
|
+
job,
|
|
265
|
+
nextRun: this.scheduler.getNextRun(job.id),
|
|
266
|
+
lastRun: loadRecentRuns(job.id, 1)[0],
|
|
267
|
+
isRunning: this.activeJobRuns.has(job.id),
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
/** Returns a status snapshot for the control socket. */
|
|
271
|
+
getStatus() {
|
|
272
|
+
const jobs = loadAllJobs();
|
|
273
|
+
const enabledJobs = jobs.filter((j) => j.enabled);
|
|
274
|
+
// Find the earliest next fire time across all scheduled jobs
|
|
275
|
+
let nextFireTime = null;
|
|
276
|
+
for (const job of enabledJobs) {
|
|
277
|
+
const nextRun = this.scheduler.getNextRun(job.id);
|
|
278
|
+
if (nextRun && (nextFireTime === null || nextRun < nextFireTime)) {
|
|
279
|
+
nextFireTime = nextRun;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const uptimeSeconds = this.startTime ? (Date.now() - this.startTime.getTime()) / 1000 : 0;
|
|
283
|
+
return {
|
|
284
|
+
uptimeSeconds,
|
|
285
|
+
jobs: {
|
|
286
|
+
total: jobs.length,
|
|
287
|
+
enabled: enabledJobs.length,
|
|
288
|
+
running: this.activeJobRuns.size,
|
|
289
|
+
},
|
|
290
|
+
signalConnected: this.signalDaemon !== null,
|
|
291
|
+
nextFireTime,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// -----------------------------------------------------------------------
|
|
295
|
+
// Job execution
|
|
296
|
+
// -----------------------------------------------------------------------
|
|
297
|
+
/**
|
|
298
|
+
* Called when a scheduled job fires.
|
|
299
|
+
* Checks for overlap, creates a session, and runs the job.
|
|
300
|
+
*/
|
|
301
|
+
async onJobTrigger(job) {
|
|
302
|
+
// Check overlap
|
|
303
|
+
if (this.activeJobRuns.has(job.id)) {
|
|
304
|
+
const msg = `[Daemon] Skipping job ${job.id}: previous run still active (session #${this.activeJobRuns.get(job.id)})`;
|
|
305
|
+
logger.warn(msg);
|
|
306
|
+
this.signalDaemon
|
|
307
|
+
?.sendSignalMessage(`Skipped job "${job.name}": previous run still in progress.`)
|
|
308
|
+
.catch(() => { });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
await this.executeJob(job);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
logger.warn(`[Daemon] Job ${job.id} failed: ${String(err)}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Executes a job: creates a session with the job's workspace and
|
|
320
|
+
* per-job policy, runs it to completion, and records the outcome.
|
|
321
|
+
*/
|
|
322
|
+
async executeJob(job) {
|
|
323
|
+
const startedAt = new Date().toISOString();
|
|
324
|
+
const workspace = job.workspace ?? getJobWorkspaceDir(job.id);
|
|
325
|
+
const globalConfig = loadConfig();
|
|
326
|
+
// Validate custom workspace paths against sandbox containment rules.
|
|
327
|
+
// Default job workspace dirs are safe (under ~/.ironcurtain/jobs/), but
|
|
328
|
+
// user-provided workspace fields in job.json could be arbitrary paths.
|
|
329
|
+
if (job.workspace) {
|
|
330
|
+
try {
|
|
331
|
+
validateWorkspacePath(job.workspace, globalConfig.protectedPaths);
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
335
|
+
logger.warn(`[Daemon] Job ${job.id} has invalid workspace: ${message}`);
|
|
336
|
+
const record = {
|
|
337
|
+
startedAt,
|
|
338
|
+
completedAt: new Date().toISOString(),
|
|
339
|
+
outcome: { kind: 'error', message: `Invalid workspace: ${message}` },
|
|
340
|
+
budget: { totalTokens: 0, stepCount: 0, elapsedSeconds: 0, estimatedCostUsd: 0 },
|
|
341
|
+
summary: null,
|
|
342
|
+
escalationsEncountered: 0,
|
|
343
|
+
escalationsApproved: 0,
|
|
344
|
+
discardedChanges: null,
|
|
345
|
+
};
|
|
346
|
+
saveRunRecord(job.id, record);
|
|
347
|
+
return record;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Sync git repo if configured
|
|
351
|
+
let discardedChanges = null;
|
|
352
|
+
if (job.gitRepo) {
|
|
353
|
+
logger.info(`[Daemon] Syncing repo for job ${job.id}...`);
|
|
354
|
+
discardedChanges = syncGitRepo(job.gitRepo, workspace);
|
|
355
|
+
if (discardedChanges) {
|
|
356
|
+
logger.info(`[Daemon] Discarded local changes in job ${job.id}:\n${discardedChanges}`);
|
|
357
|
+
}
|
|
358
|
+
logger.info(`[Daemon] Repo synced for job ${job.id}`);
|
|
359
|
+
}
|
|
360
|
+
const patchedConfig = buildCronSessionConfig(globalConfig, job);
|
|
361
|
+
const jobGeneratedDir = getJobGeneratedDir(job.id);
|
|
362
|
+
// Build system prompt augmentation
|
|
363
|
+
const augmentation = buildCronSystemPromptAugmentation({
|
|
364
|
+
taskDescription: job.taskDescription,
|
|
365
|
+
workspacePath: workspace,
|
|
366
|
+
});
|
|
367
|
+
// Create the headless transport
|
|
368
|
+
const transport = new HeadlessTransport({ taskMessage: job.taskDescription });
|
|
369
|
+
// Track escalation counts for this run
|
|
370
|
+
let escalationsEncountered = 0;
|
|
371
|
+
let escalationsApproved = 0;
|
|
372
|
+
// Create the session with per-job policy and escalation handler.
|
|
373
|
+
// Auto-approver is disabled: cron sessions have no interactive user
|
|
374
|
+
// context, so intent-matching against a user message is meaningless.
|
|
375
|
+
const session = await createSession({
|
|
376
|
+
mode: this.mode,
|
|
377
|
+
config: patchedConfig,
|
|
378
|
+
workspacePath: workspace,
|
|
379
|
+
policyDir: jobGeneratedDir,
|
|
380
|
+
systemPromptAugmentation: augmentation,
|
|
381
|
+
disableAutoApprove: true,
|
|
382
|
+
onEscalation: (request) => {
|
|
383
|
+
escalationsEncountered++;
|
|
384
|
+
this.handleCronEscalation(request, job);
|
|
385
|
+
},
|
|
386
|
+
onEscalationResolved: (_id, decision) => {
|
|
387
|
+
if (decision === 'approved')
|
|
388
|
+
escalationsApproved++;
|
|
389
|
+
const label = this.activeJobRuns.get(job.id);
|
|
390
|
+
if (label !== undefined) {
|
|
391
|
+
this.sessionManager.clearPendingEscalation(label);
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
onEscalationExpired: () => {
|
|
395
|
+
const label = this.activeJobRuns.get(job.id);
|
|
396
|
+
if (label !== undefined) {
|
|
397
|
+
this.sessionManager.clearPendingEscalation(label);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
// Register in session manager
|
|
402
|
+
const source = { kind: 'cron', jobId: job.id, jobName: job.name };
|
|
403
|
+
const label = this.sessionManager.register(session, transport, source);
|
|
404
|
+
this.activeJobRuns.set(job.id, label);
|
|
405
|
+
logger.info(`[Daemon] Started job ${job.id} as session #${label}`);
|
|
406
|
+
let outcome;
|
|
407
|
+
try {
|
|
408
|
+
await transport.run(session);
|
|
409
|
+
outcome = { kind: 'success' };
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
if (err instanceof BudgetExhaustedError) {
|
|
413
|
+
outcome = { kind: 'budget_exhausted', dimension: err.dimension };
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
outcome = { kind: 'error', message: err instanceof Error ? err.message : String(err) };
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Build run record
|
|
420
|
+
const completedAt = new Date().toISOString();
|
|
421
|
+
const budgetStatus = session.getBudgetStatus();
|
|
422
|
+
const summary = this.readLastRunSummary(workspace);
|
|
423
|
+
const record = {
|
|
424
|
+
startedAt,
|
|
425
|
+
completedAt,
|
|
426
|
+
outcome,
|
|
427
|
+
budget: {
|
|
428
|
+
totalTokens: budgetStatus.totalTokens,
|
|
429
|
+
stepCount: budgetStatus.stepCount,
|
|
430
|
+
elapsedSeconds: budgetStatus.elapsedSeconds,
|
|
431
|
+
estimatedCostUsd: budgetStatus.estimatedCostUsd,
|
|
432
|
+
},
|
|
433
|
+
summary,
|
|
434
|
+
escalationsEncountered,
|
|
435
|
+
escalationsApproved,
|
|
436
|
+
discardedChanges,
|
|
437
|
+
};
|
|
438
|
+
// Save run record
|
|
439
|
+
saveRunRecord(job.id, record);
|
|
440
|
+
// Notify via Signal if configured
|
|
441
|
+
if (job.notifyOnCompletion && this.signalDaemon) {
|
|
442
|
+
const outcomeStr = outcome.kind === 'success'
|
|
443
|
+
? 'Completed'
|
|
444
|
+
: outcome.kind === 'budget_exhausted'
|
|
445
|
+
? `Budget exhausted: ${outcome.dimension}`
|
|
446
|
+
: `Error: ${outcome.message}`;
|
|
447
|
+
const notify = `[cron: ${job.name}] ${outcomeStr} (${budgetStatus.elapsedSeconds.toFixed(0)}s, $${budgetStatus.estimatedCostUsd.toFixed(2)})`;
|
|
448
|
+
const notifyMsg = summary ? `${notify}\n${summary.slice(0, 500)}` : notify;
|
|
449
|
+
this.signalDaemon.sendSignalMessage(notifyMsg).catch(() => { });
|
|
450
|
+
}
|
|
451
|
+
// Cleanup
|
|
452
|
+
this.activeJobRuns.delete(job.id);
|
|
453
|
+
await this.sessionManager.end(label);
|
|
454
|
+
logger.info(`[Daemon] Job ${job.id} completed: ${outcome.kind}`);
|
|
455
|
+
return record;
|
|
456
|
+
}
|
|
457
|
+
// -----------------------------------------------------------------------
|
|
458
|
+
// Escalation handling for cron sessions
|
|
459
|
+
// -----------------------------------------------------------------------
|
|
460
|
+
handleCronEscalation(request, job) {
|
|
461
|
+
const label = this.activeJobRuns.get(job.id);
|
|
462
|
+
if (label === undefined)
|
|
463
|
+
return;
|
|
464
|
+
this.sessionManager.setPendingEscalation(label, request.escalationId);
|
|
465
|
+
if (job.notifyOnEscalation && this.signalDaemon) {
|
|
466
|
+
const banner = [
|
|
467
|
+
`[#${label} cron: ${job.name}] Escalation: ${request.serverName}/${request.toolName}`,
|
|
468
|
+
`Reason: "${request.reason}"`,
|
|
469
|
+
`Reply: approve #${label} / deny #${label}`,
|
|
470
|
+
].join('\n');
|
|
471
|
+
this.signalDaemon.sendSignalMessage(banner).catch(() => { });
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
// Auto-deny when Signal is not configured
|
|
475
|
+
const managed = this.sessionManager.get(label);
|
|
476
|
+
managed?.session.resolveEscalation(request.escalationId, 'denied').catch(() => { });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// -----------------------------------------------------------------------
|
|
480
|
+
// Signal integration
|
|
481
|
+
// -----------------------------------------------------------------------
|
|
482
|
+
async startSignalDaemon() {
|
|
483
|
+
const { resolveSignalConfig } = await import('../signal/signal-config.js');
|
|
484
|
+
const { SignalBotDaemon } = await import('../signal/signal-bot-daemon.js');
|
|
485
|
+
const { createSignalContainerManager } = await import('../signal/signal-container.js');
|
|
486
|
+
const { createDockerManager } = await import('../docker/docker-manager.js');
|
|
487
|
+
const userConfig = loadUserConfig();
|
|
488
|
+
const signalConfig = resolveSignalConfig(userConfig);
|
|
489
|
+
if (!signalConfig) {
|
|
490
|
+
throw new Error('Signal is not configured. Run: ironcurtain setup-signal');
|
|
491
|
+
}
|
|
492
|
+
process.stderr.write('Starting Signal transport...\n');
|
|
493
|
+
const docker = createDockerManager();
|
|
494
|
+
const containerManager = createSignalContainerManager(docker, signalConfig.container);
|
|
495
|
+
const signalDaemon = new SignalBotDaemon({
|
|
496
|
+
config: signalConfig,
|
|
497
|
+
containerManager,
|
|
498
|
+
mode: this.mode,
|
|
499
|
+
sessionManager: this.sessionManager,
|
|
500
|
+
});
|
|
501
|
+
// Await the connection phase (starts Docker, health check, WebSocket).
|
|
502
|
+
// Throws if the container won't start or health check times out.
|
|
503
|
+
await signalDaemon.connect();
|
|
504
|
+
this.signalDaemon = signalDaemon;
|
|
505
|
+
// Run the blocking event loop in the background (blocks until shutdown)
|
|
506
|
+
signalDaemon.run().catch((err) => {
|
|
507
|
+
logger.warn(`[Daemon] Signal daemon exited: ${String(err)}`);
|
|
508
|
+
this.signalDaemon = null;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
// -----------------------------------------------------------------------
|
|
512
|
+
// Helpers
|
|
513
|
+
// -----------------------------------------------------------------------
|
|
514
|
+
async startControlSocket() {
|
|
515
|
+
const handler = {
|
|
516
|
+
getStatus: () => this.getStatus(),
|
|
517
|
+
addJob: (job) => this.addJob(job),
|
|
518
|
+
removeJob: (jobId) => this.removeJob(jobId),
|
|
519
|
+
enableJob: (jobId) => this.enableJob(jobId),
|
|
520
|
+
disableJob: (jobId) => this.disableJob(jobId),
|
|
521
|
+
recompileJob: (jobId) => this.recompileJob(jobId),
|
|
522
|
+
reloadJob: (jobId) => this.reloadJob(jobId),
|
|
523
|
+
runJobNow: (jobId) => this.runJobNow(jobId),
|
|
524
|
+
listJobs: () => this.listJobs(),
|
|
525
|
+
};
|
|
526
|
+
this.controlSocket = new ControlSocketServer(handler);
|
|
527
|
+
try {
|
|
528
|
+
await this.controlSocket.start();
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
logger.warn(`[Daemon] Failed to start control socket: ${err instanceof Error ? err.message : String(err)}`);
|
|
532
|
+
this.controlSocket = null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
readLastRunSummary(workspace) {
|
|
536
|
+
const summaryPath = resolve(workspace, 'last-run.md');
|
|
537
|
+
if (!existsSync(summaryPath))
|
|
538
|
+
return null;
|
|
539
|
+
try {
|
|
540
|
+
const content = readFileSync(summaryPath, 'utf-8');
|
|
541
|
+
return content.length > 2000 ? content.slice(0, 2000) + '...' : content;
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
//# sourceMappingURL=ironcurtain-daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ironcurtain-daemon.js","sourceRoot":"","sources":["../../src/daemon/ironcurtain-daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { anthropicProvider, claudePlatformProvider, anthropicOAuthProvider, claudePlatformOAuthProvider, } from '../provider-config.js';
|
|
15
15
|
import { buildSystemPrompt } from '../../session/prompts.js';
|
|
16
|
+
import { buildResizePtyScript, buildCheckPtySizeScript, buildNetworkSection, buildPolicySection, buildAttributionSection, } from './shared-scripts.js';
|
|
16
17
|
const CLAUDE_CODE_IMAGE = 'ironcurtain-claude-code:latest';
|
|
17
18
|
function buildDockerEnvironmentPrompt(context) {
|
|
18
19
|
return `## Docker Environment
|
|
@@ -37,9 +38,7 @@ tools for all subsequent file operations on the cloned/written files.
|
|
|
37
38
|
When cloning repos, use ${context.workspaceDir} as the target directory
|
|
38
39
|
(e.g. \`${context.workspaceDir}/repo-name\`).
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
The container has NO direct internet access. All HTTP requests and
|
|
42
|
-
git operations MUST go through the sandbox tools via \`execute_code\`.
|
|
41
|
+
${buildNetworkSection('the sandbox tools via `execute_code`')}
|
|
43
42
|
|
|
44
43
|
IMPORTANT: Your built-in server-side web search tool (WebSearch) is DISABLED
|
|
45
44
|
and will NOT work — it is stripped by the security proxy. You MUST use the
|
|
@@ -51,15 +50,9 @@ To search the web:
|
|
|
51
50
|
To fetch a URL:
|
|
52
51
|
\`const page = tools.fetch_http_fetch({ url: "https://example.com" });\`
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
Every tool call through \`execute_code\` is evaluated against security policy rules:
|
|
56
|
-
- **Allowed**: proceeds automatically
|
|
57
|
-
- **Denied**: blocked -- do NOT retry denied operations
|
|
58
|
-
- **Escalated**: requires human approval -- you will receive the result once approved
|
|
53
|
+
${buildPolicySection('tool call through `execute_code`')}
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
When adding attribution lines (e.g. Co-Authored-By, "Generated with"), include
|
|
62
|
-
"running under IronCurtain" alongside the tool name.
|
|
55
|
+
${buildAttributionSection()}
|
|
63
56
|
`;
|
|
64
57
|
}
|
|
65
58
|
export const claudeCodeAdapter = {
|
|
@@ -102,42 +95,9 @@ exec claude --dangerously-skip-permissions \\
|
|
|
102
95
|
--mcp-config /etc/ironcurtain/claude-mcp-config.json \\
|
|
103
96
|
--append-system-prompt "$IRONCURTAIN_SYSTEM_PROMPT"
|
|
104
97
|
`;
|
|
105
|
-
// Helper
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// the initial size from env vars, so no fallback is needed.
|
|
109
|
-
// Diagnostic output goes to stderr (captured by host-side logger).
|
|
110
|
-
const resizeScript = `#!/bin/bash
|
|
111
|
-
# Called from the host via: docker exec <cid> /etc/ironcurtain/resize-pty.sh <cols> <rows>
|
|
112
|
-
COLS=$1
|
|
113
|
-
ROWS=$2
|
|
114
|
-
|
|
115
|
-
CLAUDE_PID=$(pgrep -x claude | head -1)
|
|
116
|
-
if [ -z "$CLAUDE_PID" ]; then
|
|
117
|
-
echo "no-claude" >&2
|
|
118
|
-
exit 0
|
|
119
|
-
fi
|
|
120
|
-
|
|
121
|
-
PTS=$(readlink /proc/$CLAUDE_PID/fd/0 2>/dev/null)
|
|
122
|
-
if [ -z "$PTS" ] || ! [ -e "$PTS" ]; then
|
|
123
|
-
echo "no-pty pid=$CLAUDE_PID pts=$PTS" >&2
|
|
124
|
-
exit 0
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
stty -F "$PTS" cols "$COLS" rows "$ROWS" 2>/dev/null
|
|
128
|
-
RC=$?
|
|
129
|
-
kill -WINCH "$CLAUDE_PID" 2>/dev/null
|
|
130
|
-
echo "ok pid=$CLAUDE_PID pts=$PTS stty=$RC \${COLS}x\${ROWS}" >&2
|
|
131
|
-
`;
|
|
132
|
-
// Helper script to report the current PTY size for host-side verification.
|
|
133
|
-
const checkSizeScript = `#!/bin/bash
|
|
134
|
-
# Returns "rows cols" of the container PTY
|
|
135
|
-
CLAUDE_PID=$(pgrep -x claude | head -1)
|
|
136
|
-
if [ -z "$CLAUDE_PID" ]; then echo "0 0"; exit 0; fi
|
|
137
|
-
PTS=$(readlink /proc/$CLAUDE_PID/fd/0 2>/dev/null)
|
|
138
|
-
if [ -z "$PTS" ] || ! [ -e "$PTS" ]; then echo "0 0"; exit 0; fi
|
|
139
|
-
stty -F "$PTS" size 2>/dev/null || echo "0 0"
|
|
140
|
-
`;
|
|
98
|
+
// Helper scripts for PTY resize — use shared generators parameterized by process name.
|
|
99
|
+
const resizeScript = buildResizePtyScript('claude');
|
|
100
|
+
const checkSizeScript = buildCheckPtySizeScript('claude');
|
|
141
101
|
return [
|
|
142
102
|
{ path: 'start-claude.sh', content: startScript, mode: 0o755 },
|
|
143
103
|
{ path: 'resize-pty.sh', content: resizeScript, mode: 0o755 },
|