@provos/ironcurtain 0.4.1 → 0.5.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 +219 -248
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +144 -6
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/constitution-user-base.md +2 -0
- package/dist/config/first-start.js +77 -12
- package/dist/config/first-start.js.map +1 -1
- package/dist/config/generated/compiled-policy.json +199 -72
- package/dist/config/generated/tool-annotations.json +1018 -36
- package/dist/config/mcp-servers.json +10 -0
- package/dist/config/paths.d.ts +31 -0
- package/dist/config/paths.js +52 -2
- package/dist/config/paths.js.map +1 -1
- package/dist/config/types.d.ts +8 -0
- package/dist/config/user-config.d.ts +21 -3
- package/dist/config/user-config.js +34 -3
- package/dist/config/user-config.js.map +1 -1
- package/dist/docker/adapters/claude-code.js +64 -8
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/agent-adapter.d.ts +18 -1
- package/dist/docker/docker-agent-session.d.ts +15 -8
- package/dist/docker/docker-agent-session.js +61 -88
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +51 -0
- package/dist/docker/docker-infrastructure.js +261 -0
- package/dist/docker/docker-infrastructure.js.map +1 -0
- package/dist/docker/docker-manager.js +3 -0
- package/dist/docker/docker-manager.js.map +1 -1
- package/dist/docker/mitm-proxy.js +4 -0
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +81 -0
- package/dist/docker/oauth-credentials.js +178 -0
- package/dist/docker/oauth-credentials.js.map +1 -0
- package/dist/docker/orientation.js +1 -1
- package/dist/docker/orientation.js.map +1 -1
- package/dist/docker/provider-config.d.ts +3 -0
- package/dist/docker/provider-config.js +28 -0
- package/dist/docker/provider-config.js.map +1 -1
- package/dist/docker/pty-session.d.ts +27 -0
- package/dist/docker/pty-session.js +420 -0
- package/dist/docker/pty-session.js.map +1 -0
- package/dist/docker/pty-types.d.ts +24 -0
- package/dist/docker/pty-types.js +12 -0
- package/dist/docker/pty-types.js.map +1 -0
- package/dist/docker/types.d.ts +6 -0
- package/dist/escalation/escalation-watcher.d.ts +44 -0
- package/dist/escalation/escalation-watcher.js +92 -0
- package/dist/escalation/escalation-watcher.js.map +1 -0
- package/dist/escalation/listener-command.d.ts +10 -0
- package/dist/escalation/listener-command.js +407 -0
- package/dist/escalation/listener-command.js.map +1 -0
- package/dist/escalation/listener-state.d.ts +49 -0
- package/dist/escalation/listener-state.js +104 -0
- package/dist/escalation/listener-state.js.map +1 -0
- package/dist/escalation/session-registry.d.ts +17 -0
- package/dist/escalation/session-registry.js +98 -0
- package/dist/escalation/session-registry.js.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.js +2 -3
- package/dist/logger.js.map +1 -1
- package/dist/pipeline/annotate.js +35 -15
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/constitution-customizer.d.ts +2 -1
- package/dist/pipeline/constitution-customizer.js +36 -3
- package/dist/pipeline/constitution-customizer.js.map +1 -1
- package/dist/pipeline/github-identity.d.ts +38 -0
- package/dist/pipeline/github-identity.js +64 -0
- package/dist/pipeline/github-identity.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.js +34 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -1
- package/dist/sandbox/index.js +12 -3
- package/dist/sandbox/index.js.map +1 -1
- package/dist/servers/fetch-server.js +2 -2
- package/dist/session/base-transport.d.ts +19 -0
- package/dist/session/base-transport.js +36 -0
- package/dist/session/base-transport.js.map +1 -0
- package/dist/session/cli-transport.d.ts +3 -3
- package/dist/session/cli-transport.js +7 -4
- package/dist/session/cli-transport.js.map +1 -1
- package/dist/session/index.js +24 -78
- package/dist/session/index.js.map +1 -1
- package/dist/session/interaction-log.d.ts +13 -0
- package/dist/session/interaction-log.js +28 -0
- package/dist/session/interaction-log.js.map +1 -0
- package/dist/session/preflight.d.ts +3 -0
- package/dist/session/preflight.js +39 -18
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/types.d.ts +2 -0
- package/dist/signal/bot-command.js +5 -0
- package/dist/signal/bot-command.js.map +1 -1
- package/dist/signal/format.d.ts +21 -1
- package/dist/signal/format.js +41 -3
- package/dist/signal/format.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +53 -18
- package/dist/signal/signal-bot-daemon.js +344 -106
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-config.d.ts +4 -0
- package/dist/signal/signal-config.js +2 -0
- package/dist/signal/signal-config.js.map +1 -1
- package/dist/signal/signal-transport.d.ts +13 -7
- package/dist/signal/signal-transport.js +36 -12
- package/dist/signal/signal-transport.js.map +1 -1
- package/dist/trusted-process/audit-log.d.ts +6 -1
- package/dist/trusted-process/audit-log.js +22 -2
- package/dist/trusted-process/audit-log.js.map +1 -1
- package/dist/trusted-process/audit-redactor.d.ts +25 -0
- package/dist/trusted-process/audit-redactor.js +121 -0
- package/dist/trusted-process/audit-redactor.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +1 -1
- package/dist/trusted-process/escalation.js +6 -1
- package/dist/trusted-process/escalation.js.map +1 -1
- package/dist/trusted-process/index.d.ts +2 -1
- package/dist/trusted-process/index.js +24 -6
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/mcp-client-manager.js +14 -2
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-error-utils.d.ts +11 -0
- package/dist/trusted-process/mcp-error-utils.js +50 -0
- package/dist/trusted-process/mcp-error-utils.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +4 -0
- package/dist/trusted-process/mcp-proxy-server.js +14 -4
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/sandbox-integration.d.ts +5 -0
- package/dist/trusted-process/sandbox-integration.js +1 -1
- package/dist/trusted-process/sandbox-integration.js.map +1 -1
- package/dist/trusted-process/server-context.d.ts +21 -0
- package/dist/trusted-process/server-context.js +42 -0
- package/dist/trusted-process/server-context.js.map +1 -0
- package/dist/types/argument-roles.d.ts +5 -2
- package/dist/types/argument-roles.js +25 -2
- package/dist/types/argument-roles.js.map +1 -1
- package/docker/Dockerfile.claude-code +8 -3
- package/docker/entrypoint-claude-code.sh +64 -4
- package/package.json +16 -8
- package/src/config/constitution-user-base.md +2 -0
- package/src/config/generated/compiled-policy.json +199 -72
- package/src/config/generated/tool-annotations.json +1018 -36
- package/src/config/mcp-servers.json +10 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PTY session module -- orchestrates a Docker session where the user's
|
|
3
|
+
* terminal is attached directly to Claude Code's PTY inside the container.
|
|
4
|
+
*
|
|
5
|
+
* Instead of the batch-oriented DockerAgentSession (docker exec per turn),
|
|
6
|
+
* this module provides a native interactive experience by bridging the
|
|
7
|
+
* terminal via a Node.js PTY proxy to a socat-managed PTY inside the container.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* User terminal -> Node.js PTY proxy -> UDS/TCP -> socat (container)
|
|
11
|
+
* -> Claude Code (interactive, with PTY)
|
|
12
|
+
* -> Code Mode Proxy (MCP)
|
|
13
|
+
* -> mcp-proxy-server (PolicyEngine + Audit)
|
|
14
|
+
*/
|
|
15
|
+
import { createConnection } from 'node:net';
|
|
16
|
+
import { execFile } from 'node:child_process';
|
|
17
|
+
import { mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
18
|
+
import { resolve } from 'node:path';
|
|
19
|
+
import chalk from 'chalk';
|
|
20
|
+
import ora from 'ora';
|
|
21
|
+
import { createSessionId } from '../session/types.js';
|
|
22
|
+
import { PTY_SOCK_NAME, DEFAULT_PTY_PORT } from './pty-types.js';
|
|
23
|
+
import { createEscalationWatcher, atomicWriteJsonSync } from '../escalation/escalation-watcher.js';
|
|
24
|
+
import { getSessionDir, getSessionSandboxDir, getSessionEscalationDir, getSessionAuditLogPath, getSessionLogPath, getSessionLlmLogPath, getPtyRegistryDir, } from '../config/paths.js';
|
|
25
|
+
import * as logger from '../logger.js';
|
|
26
|
+
/** Maximum time to wait for the PTY socket to appear (ms). */
|
|
27
|
+
const PTY_READINESS_TIMEOUT_MS = 30_000;
|
|
28
|
+
/** Poll interval when waiting for PTY socket (ms). */
|
|
29
|
+
const PTY_READINESS_POLL_MS = 200;
|
|
30
|
+
/**
|
|
31
|
+
* Runs a PTY session: starts proxies, launches container with PTY-enabled
|
|
32
|
+
* Claude Code, attaches the terminal, and blocks until the session ends.
|
|
33
|
+
*/
|
|
34
|
+
export async function runPtySession(options) {
|
|
35
|
+
const { prepareDockerInfrastructure } = await import('./docker-infrastructure.js');
|
|
36
|
+
const sessionId = createSessionId();
|
|
37
|
+
const sessionDir = getSessionDir(sessionId);
|
|
38
|
+
const sandboxDir = getSessionSandboxDir(sessionId);
|
|
39
|
+
const escalationDir = getSessionEscalationDir(sessionId);
|
|
40
|
+
const auditLogPath = getSessionAuditLogPath(sessionId);
|
|
41
|
+
const socketsDir = resolve(sessionDir, 'sockets');
|
|
42
|
+
mkdirSync(sandboxDir, { recursive: true, mode: 0o700 });
|
|
43
|
+
mkdirSync(escalationDir, { recursive: true, mode: 0o700 });
|
|
44
|
+
mkdirSync(socketsDir, { recursive: true, mode: 0o700 });
|
|
45
|
+
// Set up session logging
|
|
46
|
+
const sessionLogPath = getSessionLogPath(sessionId);
|
|
47
|
+
const llmLogPath = getSessionLlmLogPath(sessionId);
|
|
48
|
+
logger.setup({ logFilePath: sessionLogPath });
|
|
49
|
+
logger.info(`PTY session ${sessionId} starting`);
|
|
50
|
+
logger.info(`Sandbox: ${sandboxDir}`);
|
|
51
|
+
logger.info(`Escalation dir: ${escalationDir}`);
|
|
52
|
+
logger.info(`LLM log: ${llmLogPath}`);
|
|
53
|
+
// Patch config for this session
|
|
54
|
+
const sessionConfig = {
|
|
55
|
+
...options.config,
|
|
56
|
+
allowedDirectory: sandboxDir,
|
|
57
|
+
auditLogPath,
|
|
58
|
+
escalationDir,
|
|
59
|
+
sessionLogPath,
|
|
60
|
+
llmLogPath,
|
|
61
|
+
mcpServers: JSON.parse(JSON.stringify(options.config.mcpServers)),
|
|
62
|
+
};
|
|
63
|
+
const initSpinner = ora({
|
|
64
|
+
text: `Initializing PTY session (${options.mode.agent})...`,
|
|
65
|
+
stream: process.stderr,
|
|
66
|
+
discardStdin: false,
|
|
67
|
+
}).start();
|
|
68
|
+
let containerId = null;
|
|
69
|
+
let sidecarContainerId = null;
|
|
70
|
+
let escalationFileWatcher = null;
|
|
71
|
+
let registrationPath = null;
|
|
72
|
+
let shutdownSpinner = null;
|
|
73
|
+
// Terminal safety: ensure raw mode is restored on any exit
|
|
74
|
+
const restoreTerminal = () => {
|
|
75
|
+
try {
|
|
76
|
+
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
77
|
+
process.stdin.setRawMode(false);
|
|
78
|
+
process.stdin.pause();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* best effort */
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
process.on('exit', restoreTerminal);
|
|
86
|
+
// SIGTERM triggers graceful shutdown by aborting the PTY connection.
|
|
87
|
+
// This causes attachPty() to resolve, which then falls through to the
|
|
88
|
+
// finally block for full async cleanup (containers, proxies, files).
|
|
89
|
+
const shutdownController = new AbortController();
|
|
90
|
+
const handleSigterm = () => {
|
|
91
|
+
shutdownController.abort();
|
|
92
|
+
};
|
|
93
|
+
process.on('SIGTERM', handleSigterm);
|
|
94
|
+
// Infra variables set inside try, used in finally for cleanup
|
|
95
|
+
let proxy = null;
|
|
96
|
+
let mitmProxy = null;
|
|
97
|
+
let docker = null;
|
|
98
|
+
let useTcp = false;
|
|
99
|
+
try {
|
|
100
|
+
const infra = await prepareDockerInfrastructure(sessionConfig, options.mode, sessionDir, sandboxDir, escalationDir, auditLogPath, sessionId);
|
|
101
|
+
({ docker, proxy, mitmProxy, useTcp } = infra);
|
|
102
|
+
const { adapter, fakeKeys, orientationDir, systemPrompt, image, mitmAddr } = infra;
|
|
103
|
+
// Validate adapter supports PTY mode
|
|
104
|
+
if (!adapter.buildPtyCommand) {
|
|
105
|
+
throw new Error(`Agent ${adapter.id} does not support PTY mode.`);
|
|
106
|
+
}
|
|
107
|
+
// Write system prompt to file for shell-injection-safe PTY command
|
|
108
|
+
writeFileSync(resolve(orientationDir, 'system-prompt.txt'), systemPrompt);
|
|
109
|
+
// Determine PTY connection target
|
|
110
|
+
const ptySockPath = useTcp ? undefined : `/run/ironcurtain/${PTY_SOCK_NAME}`;
|
|
111
|
+
const ptyPort = useTcp ? DEFAULT_PTY_PORT : undefined;
|
|
112
|
+
// Build the PTY command
|
|
113
|
+
const ptyCommand = adapter.buildPtyCommand(systemPrompt, ptySockPath, ptyPort);
|
|
114
|
+
// Build container configuration
|
|
115
|
+
const shortId = sessionId.substring(0, 12);
|
|
116
|
+
const { INTERNAL_NETWORK_NAME, INTERNAL_NETWORK_SUBNET, INTERNAL_NETWORK_GATEWAY } = await import('./platform.js');
|
|
117
|
+
const { quote } = await import('shell-quote');
|
|
118
|
+
let env;
|
|
119
|
+
let network;
|
|
120
|
+
let mounts;
|
|
121
|
+
let extraHosts;
|
|
122
|
+
if (useTcp && proxy.port !== undefined && mitmAddr.port !== undefined) {
|
|
123
|
+
// macOS TCP mode
|
|
124
|
+
const mcpPort = proxy.port;
|
|
125
|
+
const mitmPort = mitmAddr.port;
|
|
126
|
+
env = {
|
|
127
|
+
...adapter.buildEnv(sessionConfig, fakeKeys),
|
|
128
|
+
HTTPS_PROXY: `http://host.docker.internal:${mitmPort}`,
|
|
129
|
+
HTTP_PROXY: `http://host.docker.internal:${mitmPort}`,
|
|
130
|
+
};
|
|
131
|
+
await docker.createNetwork(INTERNAL_NETWORK_NAME, {
|
|
132
|
+
internal: true,
|
|
133
|
+
subnet: INTERNAL_NETWORK_SUBNET,
|
|
134
|
+
gateway: INTERNAL_NETWORK_GATEWAY,
|
|
135
|
+
});
|
|
136
|
+
network = INTERNAL_NETWORK_NAME;
|
|
137
|
+
const socatImage = 'alpine/socat';
|
|
138
|
+
if (!(await docker.imageExists(socatImage))) {
|
|
139
|
+
logger.info(`Pulling ${socatImage}...`);
|
|
140
|
+
await docker.pullImage(socatImage);
|
|
141
|
+
}
|
|
142
|
+
// Create socat sidecar with PTY port forwarding
|
|
143
|
+
const sidecarName = `ironcurtain-sidecar-${shortId}`;
|
|
144
|
+
const ptyPortNum = ptyPort ?? DEFAULT_PTY_PORT;
|
|
145
|
+
sidecarContainerId = await docker.create({
|
|
146
|
+
image: socatImage,
|
|
147
|
+
name: sidecarName,
|
|
148
|
+
network: 'bridge',
|
|
149
|
+
mounts: [],
|
|
150
|
+
env: {},
|
|
151
|
+
entrypoint: '/bin/sh',
|
|
152
|
+
command: [
|
|
153
|
+
'-c',
|
|
154
|
+
quote(['socat', `TCP-LISTEN:${mcpPort},fork,reuseaddr`, `TCP:host.docker.internal:${mcpPort}`]) +
|
|
155
|
+
' & ' +
|
|
156
|
+
quote(['socat', `TCP-LISTEN:${mitmPort},fork,reuseaddr`, `TCP:host.docker.internal:${mitmPort}`]) +
|
|
157
|
+
' & ' +
|
|
158
|
+
quote(['socat', `TCP-LISTEN:${ptyPortNum},fork,reuseaddr`, `TCP:host.docker.internal:${ptyPortNum}`]) +
|
|
159
|
+
' & wait',
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
await docker.start(sidecarContainerId);
|
|
163
|
+
await docker.connectNetwork(INTERNAL_NETWORK_NAME, sidecarContainerId);
|
|
164
|
+
const sidecarIp = await docker.getContainerIp(sidecarContainerId, INTERNAL_NETWORK_NAME);
|
|
165
|
+
extraHosts = [`host.docker.internal:${sidecarIp}`];
|
|
166
|
+
mounts = [
|
|
167
|
+
{ source: sandboxDir, target: '/workspace', readonly: false },
|
|
168
|
+
{ source: orientationDir, target: '/etc/ironcurtain', readonly: true },
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Linux UDS mode
|
|
173
|
+
env = {
|
|
174
|
+
...adapter.buildEnv(sessionConfig, fakeKeys),
|
|
175
|
+
HTTPS_PROXY: 'http://127.0.0.1:18080',
|
|
176
|
+
HTTP_PROXY: 'http://127.0.0.1:18080',
|
|
177
|
+
};
|
|
178
|
+
network = 'none';
|
|
179
|
+
mounts = [
|
|
180
|
+
{ source: sandboxDir, target: '/workspace', readonly: false },
|
|
181
|
+
{ source: socketsDir, target: '/run/ironcurtain', readonly: false },
|
|
182
|
+
{ source: orientationDir, target: '/etc/ironcurtain', readonly: true },
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
// Create and start container with PTY command and TTY
|
|
186
|
+
containerId = await docker.create({
|
|
187
|
+
image,
|
|
188
|
+
name: `ironcurtain-pty-${shortId}`,
|
|
189
|
+
network,
|
|
190
|
+
mounts,
|
|
191
|
+
env,
|
|
192
|
+
command: ptyCommand,
|
|
193
|
+
sessionLabel: sessionId,
|
|
194
|
+
resources: { memoryMb: 4096, cpus: 2 },
|
|
195
|
+
extraHosts,
|
|
196
|
+
tty: true,
|
|
197
|
+
});
|
|
198
|
+
await docker.start(containerId);
|
|
199
|
+
logger.info(`PTY container started: ${containerId.substring(0, 12)}`);
|
|
200
|
+
// Write session registration for the escalation listener
|
|
201
|
+
registrationPath = writeRegistration(sessionId, escalationDir, adapter.displayName);
|
|
202
|
+
// Start escalation file watcher (emits BEL to alert user)
|
|
203
|
+
escalationFileWatcher = createEscalationWatcher(escalationDir, {
|
|
204
|
+
onEscalation: () => {
|
|
205
|
+
process.stderr.write('\x07'); // BEL character
|
|
206
|
+
},
|
|
207
|
+
onEscalationExpired: () => { },
|
|
208
|
+
});
|
|
209
|
+
escalationFileWatcher.start();
|
|
210
|
+
// Wait for PTY socket readiness
|
|
211
|
+
const ptyTarget = useTcp
|
|
212
|
+
? { host: 'localhost', port: ptyPort ?? DEFAULT_PTY_PORT }
|
|
213
|
+
: resolve(socketsDir, PTY_SOCK_NAME);
|
|
214
|
+
await waitForPtyReady(ptyTarget);
|
|
215
|
+
initSpinner.succeed(chalk.dim('PTY session ready'));
|
|
216
|
+
process.stderr.write('\n');
|
|
217
|
+
// Attach terminal via Node.js PTY proxy
|
|
218
|
+
const exitCode = await attachPty({
|
|
219
|
+
target: ptyTarget,
|
|
220
|
+
containerId,
|
|
221
|
+
signal: shutdownController.signal,
|
|
222
|
+
});
|
|
223
|
+
// PTY disconnected -- restore terminal and show shutdown progress
|
|
224
|
+
restoreTerminal();
|
|
225
|
+
process.stderr.write('\n');
|
|
226
|
+
shutdownSpinner = ora({
|
|
227
|
+
text: 'Shutting down PTY session...',
|
|
228
|
+
stream: process.stderr,
|
|
229
|
+
discardStdin: false,
|
|
230
|
+
}).start();
|
|
231
|
+
if (exitCode !== 0) {
|
|
232
|
+
process.stderr.write(chalk.yellow(`PTY session exited with code ${exitCode}\n`));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
// Stop spinner if still running (e.g. error during setup)
|
|
237
|
+
if (initSpinner.isSpinning) {
|
|
238
|
+
initSpinner.fail(chalk.red('PTY session failed'));
|
|
239
|
+
}
|
|
240
|
+
restoreTerminal();
|
|
241
|
+
process.off('exit', restoreTerminal);
|
|
242
|
+
process.off('SIGTERM', handleSigterm);
|
|
243
|
+
// Stop escalation watcher
|
|
244
|
+
escalationFileWatcher?.stop();
|
|
245
|
+
// Delete registration file
|
|
246
|
+
if (registrationPath) {
|
|
247
|
+
try {
|
|
248
|
+
unlinkSync(registrationPath);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
/* best effort */
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Stop and remove containers
|
|
255
|
+
if (docker && containerId) {
|
|
256
|
+
await docker.stop(containerId).catch(() => { });
|
|
257
|
+
await docker.remove(containerId).catch(() => { });
|
|
258
|
+
}
|
|
259
|
+
if (docker && sidecarContainerId) {
|
|
260
|
+
await docker.stop(sidecarContainerId).catch(() => { });
|
|
261
|
+
await docker.remove(sidecarContainerId).catch(() => { });
|
|
262
|
+
}
|
|
263
|
+
// Remove internal network if used
|
|
264
|
+
if (docker && useTcp) {
|
|
265
|
+
const { INTERNAL_NETWORK_NAME } = await import('./platform.js');
|
|
266
|
+
await docker.removeNetwork(INTERNAL_NETWORK_NAME).catch(() => { });
|
|
267
|
+
}
|
|
268
|
+
// Stop proxies
|
|
269
|
+
await mitmProxy?.stop().catch(() => { });
|
|
270
|
+
await proxy?.stop().catch(() => { });
|
|
271
|
+
logger.info(`PTY session ${sessionId} ended`);
|
|
272
|
+
logger.teardown();
|
|
273
|
+
// shutdownSpinner is declared inside try but accessible here via closure
|
|
274
|
+
shutdownSpinner?.succeed(chalk.dim('PTY session ended'));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/** Creates a net.Socket connection to the PTY target (UDS or TCP). */
|
|
278
|
+
function connectToTarget(target) {
|
|
279
|
+
if (typeof target === 'string') {
|
|
280
|
+
return createConnection({ path: target });
|
|
281
|
+
}
|
|
282
|
+
return createConnection({ host: target.host, port: target.port });
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Attaches the user's terminal to the container PTY via a Node.js socket.
|
|
286
|
+
* Returns a promise that resolves with 0 on normal close, 1 on error.
|
|
287
|
+
*/
|
|
288
|
+
function attachPty(options) {
|
|
289
|
+
const conn = connectToTarget(options.target);
|
|
290
|
+
const { stdin, stdout } = process;
|
|
291
|
+
return new Promise((resolvePromise) => {
|
|
292
|
+
let resolved = false;
|
|
293
|
+
const settle = (code) => {
|
|
294
|
+
if (resolved)
|
|
295
|
+
return;
|
|
296
|
+
resolved = true;
|
|
297
|
+
resolvePromise(code);
|
|
298
|
+
};
|
|
299
|
+
conn.once('connect', () => {
|
|
300
|
+
// Put terminal in raw mode
|
|
301
|
+
if (stdin.isTTY) {
|
|
302
|
+
stdin.setRawMode(true);
|
|
303
|
+
}
|
|
304
|
+
stdin.resume();
|
|
305
|
+
// SIGWINCH: forward terminal resize to container PTY
|
|
306
|
+
const onResize = () => {
|
|
307
|
+
const { columns, rows } = stdout;
|
|
308
|
+
if (columns && rows) {
|
|
309
|
+
execFile('docker', ['exec', options.containerId, '/etc/ironcurtain/resize-pty.sh', String(columns), String(rows)], { timeout: 5000 }, () => {
|
|
310
|
+
/* best effort */
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
stdout.on('resize', onResize);
|
|
315
|
+
// Send initial size
|
|
316
|
+
onResize();
|
|
317
|
+
// Host -> Container
|
|
318
|
+
// Ctrl-\ (0x1c) is intercepted as an emergency exit since raw mode
|
|
319
|
+
// disables normal signal generation for SIGQUIT.
|
|
320
|
+
const CTRL_BACKSLASH = 0x1c;
|
|
321
|
+
const onData = (data) => {
|
|
322
|
+
if (data.length === 1 && data[0] === CTRL_BACKSLASH) {
|
|
323
|
+
cleanup();
|
|
324
|
+
conn.destroy();
|
|
325
|
+
settle(0);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
conn.write(data);
|
|
329
|
+
};
|
|
330
|
+
stdin.on('data', onData);
|
|
331
|
+
// Container -> Host (untrusted output, displayed directly)
|
|
332
|
+
conn.pipe(stdout);
|
|
333
|
+
// Use function declarations (hoisted) so cleanup and onAbort can
|
|
334
|
+
// reference each other without temporal dead zone issues.
|
|
335
|
+
function cleanup() {
|
|
336
|
+
stdout.removeListener('resize', onResize);
|
|
337
|
+
stdin.removeListener('data', onData);
|
|
338
|
+
conn.unpipe(stdout);
|
|
339
|
+
stdin.pause();
|
|
340
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
341
|
+
}
|
|
342
|
+
function onAbort() {
|
|
343
|
+
cleanup();
|
|
344
|
+
conn.destroy();
|
|
345
|
+
settle(0);
|
|
346
|
+
}
|
|
347
|
+
if (options.signal?.aborted) {
|
|
348
|
+
onAbort();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
352
|
+
conn.once('close', () => {
|
|
353
|
+
cleanup();
|
|
354
|
+
settle(0);
|
|
355
|
+
});
|
|
356
|
+
conn.once('error', () => {
|
|
357
|
+
cleanup();
|
|
358
|
+
settle(1);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
conn.once('error', () => {
|
|
362
|
+
settle(1);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
// --- Readiness polling ---
|
|
367
|
+
/**
|
|
368
|
+
* Waits for the PTY socket/port to become connectable.
|
|
369
|
+
*/
|
|
370
|
+
async function waitForPtyReady(target) {
|
|
371
|
+
const deadline = Date.now() + PTY_READINESS_TIMEOUT_MS;
|
|
372
|
+
while (Date.now() < deadline) {
|
|
373
|
+
const connected = await tryConnect(target);
|
|
374
|
+
if (connected)
|
|
375
|
+
return;
|
|
376
|
+
await new Promise((r) => setTimeout(r, PTY_READINESS_POLL_MS));
|
|
377
|
+
}
|
|
378
|
+
throw new Error(`PTY socket did not become ready within ${PTY_READINESS_TIMEOUT_MS / 1000}s`);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Tries to connect to a target. Returns true if the connection succeeds.
|
|
382
|
+
*/
|
|
383
|
+
function tryConnect(target) {
|
|
384
|
+
return new Promise((resolve) => {
|
|
385
|
+
const conn = connectToTarget(target);
|
|
386
|
+
const timer = setTimeout(() => {
|
|
387
|
+
conn.destroy();
|
|
388
|
+
resolve(false);
|
|
389
|
+
}, 1000);
|
|
390
|
+
conn.on('connect', () => {
|
|
391
|
+
clearTimeout(timer);
|
|
392
|
+
conn.destroy();
|
|
393
|
+
resolve(true);
|
|
394
|
+
});
|
|
395
|
+
conn.on('error', () => {
|
|
396
|
+
clearTimeout(timer);
|
|
397
|
+
resolve(false);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
// --- Registration ---
|
|
402
|
+
/**
|
|
403
|
+
* Writes a PTY session registration file to the registry directory.
|
|
404
|
+
* Returns the absolute path of the registration file.
|
|
405
|
+
*/
|
|
406
|
+
function writeRegistration(sessionId, escalationDir, adapterDisplayName) {
|
|
407
|
+
const registryDir = getPtyRegistryDir();
|
|
408
|
+
mkdirSync(registryDir, { recursive: true, mode: 0o700 });
|
|
409
|
+
const registration = {
|
|
410
|
+
sessionId,
|
|
411
|
+
escalationDir,
|
|
412
|
+
label: `${adapterDisplayName} (interactive)`,
|
|
413
|
+
startedAt: new Date().toISOString(),
|
|
414
|
+
pid: process.pid,
|
|
415
|
+
};
|
|
416
|
+
const registrationPath = resolve(registryDir, `session-${sessionId}.json`);
|
|
417
|
+
atomicWriteJsonSync(registrationPath, registration);
|
|
418
|
+
return registrationPath;
|
|
419
|
+
}
|
|
420
|
+
//# sourceMappingURL=pty-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-session.js","sourceRoot":"","sources":["../../src/docker/pty-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for PTY session management and the escalation listener.
|
|
3
|
+
*/
|
|
4
|
+
/** Registration file written by PTY sessions for the escalation listener. */
|
|
5
|
+
export interface PtySessionRegistration {
|
|
6
|
+
/** Unique session identifier (SessionId). */
|
|
7
|
+
readonly sessionId: string;
|
|
8
|
+
/** Absolute path to the session's escalation directory. */
|
|
9
|
+
readonly escalationDir: string;
|
|
10
|
+
/** Human-readable label for TUI display. */
|
|
11
|
+
readonly label: string;
|
|
12
|
+
/** ISO 8601 timestamp when the session started. */
|
|
13
|
+
readonly startedAt: string;
|
|
14
|
+
/** PID of the ironcurtain process managing this session. */
|
|
15
|
+
readonly pid: number;
|
|
16
|
+
}
|
|
17
|
+
/** Well-known directory for PTY session registration files. */
|
|
18
|
+
export declare const PTY_REGISTRY_DIR_NAME = "pty-registry";
|
|
19
|
+
/** Lock file name for single-instance enforcement. */
|
|
20
|
+
export declare const LISTENER_LOCK_FILE = "escalation-listener.lock";
|
|
21
|
+
/** PTY socket filename (Linux UDS mode). */
|
|
22
|
+
export declare const PTY_SOCK_NAME = "pty.sock";
|
|
23
|
+
/** Default PTY port inside the container (macOS TCP mode only). */
|
|
24
|
+
export declare const DEFAULT_PTY_PORT = 19000;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for PTY session management and the escalation listener.
|
|
3
|
+
*/
|
|
4
|
+
/** Well-known directory for PTY session registration files. */
|
|
5
|
+
export const PTY_REGISTRY_DIR_NAME = 'pty-registry';
|
|
6
|
+
/** Lock file name for single-instance enforcement. */
|
|
7
|
+
export const LISTENER_LOCK_FILE = 'escalation-listener.lock';
|
|
8
|
+
/** PTY socket filename (Linux UDS mode). */
|
|
9
|
+
export const PTY_SOCK_NAME = 'pty.sock';
|
|
10
|
+
/** Default PTY port inside the container (macOS TCP mode only). */
|
|
11
|
+
export const DEFAULT_PTY_PORT = 19000;
|
|
12
|
+
//# sourceMappingURL=pty-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-types.js","sourceRoot":"","sources":["../../src/docker/pty-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAEpD,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAE7D,4CAA4C;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;AAExC,mEAAmE;AACnE,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC"}
|
package/dist/docker/types.d.ts
CHANGED
|
@@ -57,6 +57,12 @@ export interface DockerContainerConfig {
|
|
|
57
57
|
* must NEVER set this field.
|
|
58
58
|
*/
|
|
59
59
|
readonly capAdd?: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* Allocate a pseudo-TTY for the container (-t flag).
|
|
62
|
+
* Required for PTY mode where the container runs an interactive process.
|
|
63
|
+
* Optional. Defaults to false (no TTY allocated).
|
|
64
|
+
*/
|
|
65
|
+
readonly tty?: boolean;
|
|
60
66
|
}
|
|
61
67
|
/** A volume mount for a Docker container. */
|
|
62
68
|
export interface DockerMount {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared escalation watcher for polling escalation directories.
|
|
3
|
+
*
|
|
4
|
+
* Pure data module -- no I/O presentation. Consumers (DockerAgentSession,
|
|
5
|
+
* EscalationListener) handle display. Polls a session's escalation
|
|
6
|
+
* directory for new request files and handles resolution via response files.
|
|
7
|
+
*/
|
|
8
|
+
import type { EscalationRequest } from '../session/types.js';
|
|
9
|
+
/** Events emitted by the escalation watcher. */
|
|
10
|
+
export interface EscalationWatcherEvents {
|
|
11
|
+
/** A new escalation request was detected. */
|
|
12
|
+
onEscalation: (request: EscalationRequest) => void;
|
|
13
|
+
/** A pending escalation expired (proxy timed out and cleaned up files). */
|
|
14
|
+
onEscalationExpired: (escalationId: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface EscalationWatcher {
|
|
17
|
+
/** Start polling the escalation directory. */
|
|
18
|
+
start(): void;
|
|
19
|
+
/** Stop polling. */
|
|
20
|
+
stop(): void;
|
|
21
|
+
/** Returns the currently pending escalation, if any. */
|
|
22
|
+
getPending(): EscalationRequest | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a pending escalation by writing the response file.
|
|
25
|
+
* Returns true if the resolution was accepted (request file still exists),
|
|
26
|
+
* or false if the escalation had already expired.
|
|
27
|
+
*
|
|
28
|
+
* @throws {Error} if no escalation with this ID is pending
|
|
29
|
+
*/
|
|
30
|
+
resolve(escalationId: string, decision: 'approved' | 'denied'): boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface EscalationWatcherOptions {
|
|
33
|
+
/** Poll interval in ms. Default: 300. */
|
|
34
|
+
pollIntervalMs?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Atomically writes a JSON file using write-to-temp-then-rename.
|
|
38
|
+
* Prevents partial reads by other processes polling the same directory.
|
|
39
|
+
*/
|
|
40
|
+
export declare function atomicWriteJsonSync(filePath: string, data: unknown): void;
|
|
41
|
+
/**
|
|
42
|
+
* Creates an escalation watcher for a single session's escalation directory.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createEscalationWatcher(escalationDir: string, events: EscalationWatcherEvents, options?: EscalationWatcherOptions): EscalationWatcher;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared escalation watcher for polling escalation directories.
|
|
3
|
+
*
|
|
4
|
+
* Pure data module -- no I/O presentation. Consumers (DockerAgentSession,
|
|
5
|
+
* EscalationListener) handle display. Polls a session's escalation
|
|
6
|
+
* directory for new request files and handles resolution via response files.
|
|
7
|
+
*/
|
|
8
|
+
import { readdirSync, readFileSync, existsSync, writeFileSync, renameSync } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
const DEFAULT_POLL_INTERVAL_MS = 300;
|
|
11
|
+
/**
|
|
12
|
+
* Atomically writes a JSON file using write-to-temp-then-rename.
|
|
13
|
+
* Prevents partial reads by other processes polling the same directory.
|
|
14
|
+
*/
|
|
15
|
+
export function atomicWriteJsonSync(filePath, data) {
|
|
16
|
+
const tmpPath = `${filePath}.tmp`;
|
|
17
|
+
writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
18
|
+
renameSync(tmpPath, filePath);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates an escalation watcher for a single session's escalation directory.
|
|
22
|
+
*/
|
|
23
|
+
export function createEscalationWatcher(escalationDir, events, options) {
|
|
24
|
+
const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
25
|
+
let pendingEscalation;
|
|
26
|
+
const seenEscalationIds = new Set();
|
|
27
|
+
let pollInterval = null;
|
|
28
|
+
function extractEscalationId(filename) {
|
|
29
|
+
return filename.replace(/^request-/, '').replace(/\.json$/, '');
|
|
30
|
+
}
|
|
31
|
+
function checkEscalationExpiry() {
|
|
32
|
+
if (!pendingEscalation)
|
|
33
|
+
return;
|
|
34
|
+
const escalationId = pendingEscalation.escalationId;
|
|
35
|
+
const requestExists = existsSync(resolve(escalationDir, `request-${escalationId}.json`));
|
|
36
|
+
const responseExists = existsSync(resolve(escalationDir, `response-${escalationId}.json`));
|
|
37
|
+
if (!requestExists && !responseExists) {
|
|
38
|
+
const expiredId = escalationId;
|
|
39
|
+
pendingEscalation = undefined;
|
|
40
|
+
events.onEscalationExpired(expiredId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function pollEscalationDirectory() {
|
|
44
|
+
if (pendingEscalation) {
|
|
45
|
+
checkEscalationExpiry();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const files = readdirSync(escalationDir);
|
|
50
|
+
const requestFile = files.find((f) => f.startsWith('request-') && f.endsWith('.json') && !seenEscalationIds.has(extractEscalationId(f)));
|
|
51
|
+
if (!requestFile)
|
|
52
|
+
return;
|
|
53
|
+
const requestPath = resolve(escalationDir, requestFile);
|
|
54
|
+
const request = JSON.parse(readFileSync(requestPath, 'utf-8'));
|
|
55
|
+
seenEscalationIds.add(request.escalationId);
|
|
56
|
+
pendingEscalation = request;
|
|
57
|
+
events.onEscalation(request);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Directory may not exist yet or be empty
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
start() {
|
|
65
|
+
if (pollInterval)
|
|
66
|
+
return;
|
|
67
|
+
pollInterval = setInterval(pollEscalationDirectory, pollIntervalMs);
|
|
68
|
+
},
|
|
69
|
+
stop() {
|
|
70
|
+
if (pollInterval) {
|
|
71
|
+
clearInterval(pollInterval);
|
|
72
|
+
pollInterval = null;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
getPending() {
|
|
76
|
+
return pendingEscalation;
|
|
77
|
+
},
|
|
78
|
+
resolve(escalationId, decision) {
|
|
79
|
+
if (!pendingEscalation || pendingEscalation.escalationId !== escalationId) {
|
|
80
|
+
throw new Error(`No pending escalation with ID: ${escalationId}`);
|
|
81
|
+
}
|
|
82
|
+
const responsePath = resolve(escalationDir, `response-${escalationId}.json`);
|
|
83
|
+
atomicWriteJsonSync(responsePath, { decision });
|
|
84
|
+
pendingEscalation = undefined;
|
|
85
|
+
// Stale detection: verify the request file still exists after writing the response.
|
|
86
|
+
// If the proxy already timed out and cleaned up, the response was too late.
|
|
87
|
+
const requestStillExists = existsSync(resolve(escalationDir, `request-${escalationId}.json`));
|
|
88
|
+
return requestStillExists;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=escalation-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalation-watcher.js","sourceRoot":"","sources":["../../src/escalation/escalation-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation listener CLI command.
|
|
3
|
+
*
|
|
4
|
+
* Polls the PTY session registry directory for active sessions, watches
|
|
5
|
+
* each session's escalation directory for new requests, and presents
|
|
6
|
+
* a terminal dashboard where the user can approve or deny escalations.
|
|
7
|
+
*
|
|
8
|
+
* Usage: ironcurtain escalation-listener
|
|
9
|
+
*/
|
|
10
|
+
export declare function main(): Promise<void>;
|