@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,407 @@
|
|
|
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
|
+
import { createInterface } from 'node:readline';
|
|
11
|
+
import { existsSync, mkdirSync, writeSync, readFileSync, unlinkSync, openSync, closeSync, constants } from 'node:fs';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { getPtyRegistryDir, getListenerLockPath } from '../config/paths.js';
|
|
14
|
+
import { readActiveRegistrations } from './session-registry.js';
|
|
15
|
+
import { createEscalationWatcher } from './escalation-watcher.js';
|
|
16
|
+
import { createInitialState, addSession, removeSession, addEscalation, resolveEscalation, expireEscalation, } from './listener-state.js';
|
|
17
|
+
/** Poll interval for the session registry directory (ms). */
|
|
18
|
+
const REGISTRY_POLL_INTERVAL_MS = 1000;
|
|
19
|
+
export async function main() {
|
|
20
|
+
// Single-instance enforcement
|
|
21
|
+
const lockPath = getListenerLockPath();
|
|
22
|
+
if (!acquireLock(lockPath)) {
|
|
23
|
+
process.stderr.write(chalk.red('Another escalation listener is already running.\n'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const registryDir = getPtyRegistryDir();
|
|
27
|
+
mkdirSync(registryDir, { recursive: true, mode: 0o700 });
|
|
28
|
+
let state = createInitialState();
|
|
29
|
+
let running = true;
|
|
30
|
+
let lastRenderedContent = '';
|
|
31
|
+
// Set up readline for command input
|
|
32
|
+
const rl = createInterface({
|
|
33
|
+
input: process.stdin,
|
|
34
|
+
output: process.stderr,
|
|
35
|
+
terminal: true,
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Renders the dashboard only if the content has actually changed.
|
|
39
|
+
* Uses cursor repositioning (move to home) instead of screen clear
|
|
40
|
+
* to avoid destroying readline's input buffer.
|
|
41
|
+
*/
|
|
42
|
+
const render = () => {
|
|
43
|
+
const content = buildDashboard(state);
|
|
44
|
+
if (content === lastRenderedContent)
|
|
45
|
+
return;
|
|
46
|
+
lastRenderedContent = content;
|
|
47
|
+
// Move cursor to home position, write content with per-line clearing,
|
|
48
|
+
// then clear any remaining lines below. The per-line \x1b[K (clear to
|
|
49
|
+
// end of line) is needed because shorter new lines would otherwise leave
|
|
50
|
+
// leftover characters from the previous (longer) dashboard render.
|
|
51
|
+
process.stderr.write('\x1b[H\x1b[K' + content.replace(/\n/g, '\x1b[K\n') + '\x1b[J');
|
|
52
|
+
// Force readline to redisplay its prompt + current input buffer
|
|
53
|
+
rl.prompt(true);
|
|
54
|
+
};
|
|
55
|
+
// Poll for new/removed sessions
|
|
56
|
+
const registryPollInterval = setInterval(() => {
|
|
57
|
+
const registrations = readActiveRegistrations(registryDir);
|
|
58
|
+
const currentIds = new Set(registrations.map((r) => r.sessionId));
|
|
59
|
+
const stateIds = new Set(state.sessions.keys());
|
|
60
|
+
let changed = false;
|
|
61
|
+
// Add new sessions
|
|
62
|
+
for (const reg of registrations) {
|
|
63
|
+
if (!stateIds.has(reg.sessionId)) {
|
|
64
|
+
const watcher = createWatcherForSession(reg, (newState) => {
|
|
65
|
+
state = newState;
|
|
66
|
+
render();
|
|
67
|
+
}, () => state);
|
|
68
|
+
state = addSession(state, reg, watcher);
|
|
69
|
+
watcher.start();
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Remove gone sessions
|
|
74
|
+
for (const id of stateIds) {
|
|
75
|
+
if (!currentIds.has(id)) {
|
|
76
|
+
const session = state.sessions.get(id);
|
|
77
|
+
session?.watcher.stop();
|
|
78
|
+
state = removeSession(state, id);
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (changed)
|
|
83
|
+
render();
|
|
84
|
+
}, REGISTRY_POLL_INTERVAL_MS);
|
|
85
|
+
// Initial render: clear screen once, then use repositioning from now on
|
|
86
|
+
rl.setPrompt(' > ');
|
|
87
|
+
process.stderr.write('\x1b[2J');
|
|
88
|
+
render();
|
|
89
|
+
// Handle commands
|
|
90
|
+
rl.on('line', (line) => {
|
|
91
|
+
const trimmed = line.trim();
|
|
92
|
+
if (!trimmed) {
|
|
93
|
+
render();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const result = handleCommand(trimmed, state);
|
|
97
|
+
state = result.state;
|
|
98
|
+
if (result.quit) {
|
|
99
|
+
if (result.message) {
|
|
100
|
+
process.stderr.write(result.message + '\n');
|
|
101
|
+
}
|
|
102
|
+
running = false;
|
|
103
|
+
rl.close();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Force a re-render by invalidating the cache
|
|
107
|
+
lastRenderedContent = '';
|
|
108
|
+
if (result.message) {
|
|
109
|
+
// Render first so the message appears below the dashboard
|
|
110
|
+
render();
|
|
111
|
+
process.stderr.write(result.message + '\n');
|
|
112
|
+
rl.prompt(true);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
render();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
rl.on('close', () => {
|
|
119
|
+
running = false;
|
|
120
|
+
});
|
|
121
|
+
// Wait until quit
|
|
122
|
+
await new Promise((resolve) => {
|
|
123
|
+
const checkInterval = setInterval(() => {
|
|
124
|
+
if (!running) {
|
|
125
|
+
clearInterval(checkInterval);
|
|
126
|
+
resolve();
|
|
127
|
+
}
|
|
128
|
+
}, 100);
|
|
129
|
+
});
|
|
130
|
+
// Cleanup
|
|
131
|
+
clearInterval(registryPollInterval);
|
|
132
|
+
for (const session of state.sessions.values()) {
|
|
133
|
+
session.watcher.stop();
|
|
134
|
+
}
|
|
135
|
+
releaseLock(lockPath);
|
|
136
|
+
}
|
|
137
|
+
// --- Lock management ---
|
|
138
|
+
/**
|
|
139
|
+
* Acquires the listener lock file using O_EXCL for atomicity.
|
|
140
|
+
* Returns true if the lock was acquired, false if another instance holds it.
|
|
141
|
+
*/
|
|
142
|
+
function acquireLock(lockPath) {
|
|
143
|
+
if (existsSync(lockPath)) {
|
|
144
|
+
// Check if the PID in the lock file is still alive
|
|
145
|
+
try {
|
|
146
|
+
const content = readFileSync(lockPath, 'utf-8');
|
|
147
|
+
const pid = parseInt(content.trim(), 10);
|
|
148
|
+
if (!isNaN(pid) && isPidAlive(pid)) {
|
|
149
|
+
return false; // Another instance is running
|
|
150
|
+
}
|
|
151
|
+
// Stale lock -- remove and try again
|
|
152
|
+
unlinkSync(lockPath);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// If we can't read the lock, try to recreate it
|
|
156
|
+
try {
|
|
157
|
+
unlinkSync(lockPath);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const fd = openSync(lockPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
|
|
166
|
+
const content = Buffer.from(String(process.pid));
|
|
167
|
+
writeSync(fd, content);
|
|
168
|
+
closeSync(fd);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function releaseLock(lockPath) {
|
|
176
|
+
try {
|
|
177
|
+
unlinkSync(lockPath);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
/* best effort */
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function isPidAlive(pid) {
|
|
184
|
+
try {
|
|
185
|
+
process.kill(pid, 0);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// --- Watcher creation ---
|
|
193
|
+
function createWatcherForSession(registration, onStateChange, getState) {
|
|
194
|
+
return createEscalationWatcher(registration.escalationDir, {
|
|
195
|
+
onEscalation: (request) => {
|
|
196
|
+
const newState = addEscalation(getState(), registration.sessionId, request);
|
|
197
|
+
onStateChange(newState);
|
|
198
|
+
// BEL character alerts the user
|
|
199
|
+
process.stderr.write('\x07');
|
|
200
|
+
},
|
|
201
|
+
onEscalationExpired: (escalationId) => {
|
|
202
|
+
const newState = expireEscalation(getState(), registration.sessionId, escalationId);
|
|
203
|
+
onStateChange(newState);
|
|
204
|
+
},
|
|
205
|
+
}, { pollIntervalMs: 300 });
|
|
206
|
+
}
|
|
207
|
+
function handleCommand(input, state) {
|
|
208
|
+
const parts = input.split(/\s+/);
|
|
209
|
+
const command = parts[0].toLowerCase();
|
|
210
|
+
switch (command) {
|
|
211
|
+
case '/approve': {
|
|
212
|
+
const arg = parts[1];
|
|
213
|
+
if (!arg)
|
|
214
|
+
return { state, message: chalk.yellow('Usage: /approve <number> or /approve all') };
|
|
215
|
+
if (arg === 'all') {
|
|
216
|
+
return approveOrDenyAll(state, 'approved');
|
|
217
|
+
}
|
|
218
|
+
const num = parseInt(arg, 10);
|
|
219
|
+
if (isNaN(num))
|
|
220
|
+
return { state, message: chalk.yellow('Invalid escalation number') };
|
|
221
|
+
return approveOrDeny(state, num, 'approved');
|
|
222
|
+
}
|
|
223
|
+
case '/deny': {
|
|
224
|
+
const arg = parts[1];
|
|
225
|
+
if (!arg)
|
|
226
|
+
return { state, message: chalk.yellow('Usage: /deny <number> or /deny all') };
|
|
227
|
+
if (arg === 'all') {
|
|
228
|
+
return approveOrDenyAll(state, 'denied');
|
|
229
|
+
}
|
|
230
|
+
const num = parseInt(arg, 10);
|
|
231
|
+
if (isNaN(num))
|
|
232
|
+
return { state, message: chalk.yellow('Invalid escalation number') };
|
|
233
|
+
return approveOrDeny(state, num, 'denied');
|
|
234
|
+
}
|
|
235
|
+
case '/sessions':
|
|
236
|
+
return { state, message: formatSessionDetails(state) };
|
|
237
|
+
case '/quit':
|
|
238
|
+
case '/q':
|
|
239
|
+
return { state, quit: true };
|
|
240
|
+
default:
|
|
241
|
+
return {
|
|
242
|
+
state,
|
|
243
|
+
message: chalk.yellow('Unknown command. Available: /approve <N>, /deny <N>, /approve all, /deny all, /sessions, /quit'),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function approveOrDeny(state, displayNumber, decision) {
|
|
248
|
+
const escalation = state.pendingEscalations.get(displayNumber);
|
|
249
|
+
if (!escalation) {
|
|
250
|
+
return { state, message: chalk.yellow(`No pending escalation #${displayNumber}`) };
|
|
251
|
+
}
|
|
252
|
+
const session = state.sessions.get(escalation.sessionId);
|
|
253
|
+
if (!session) {
|
|
254
|
+
return { state, message: chalk.yellow(`Session for escalation #${displayNumber} no longer active`) };
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const accepted = session.watcher.resolve(escalation.request.escalationId, decision);
|
|
258
|
+
const newState = resolveEscalation(state, displayNumber, decision);
|
|
259
|
+
if (!accepted) {
|
|
260
|
+
return {
|
|
261
|
+
state: newState,
|
|
262
|
+
message: chalk.yellow(`Escalation #${displayNumber} expired (response was too late)`),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const label = decision === 'approved' ? chalk.green('APPROVED') : chalk.red('DENIED');
|
|
266
|
+
return {
|
|
267
|
+
state: newState,
|
|
268
|
+
message: `Escalation #${displayNumber} ${label}`,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
return { state, message: chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function approveOrDenyAll(state, decision) {
|
|
276
|
+
if (state.pendingEscalations.size === 0) {
|
|
277
|
+
return { state, message: chalk.yellow('No pending escalations') };
|
|
278
|
+
}
|
|
279
|
+
let currentState = state;
|
|
280
|
+
const messages = [];
|
|
281
|
+
const nums = [...state.pendingEscalations.keys()].sort((a, b) => a - b);
|
|
282
|
+
for (const num of nums) {
|
|
283
|
+
const result = approveOrDeny(currentState, num, decision);
|
|
284
|
+
currentState = result.state;
|
|
285
|
+
if (result.message)
|
|
286
|
+
messages.push(result.message);
|
|
287
|
+
}
|
|
288
|
+
return { state: currentState, message: messages.join('\n') };
|
|
289
|
+
}
|
|
290
|
+
function formatSessionDetails(state) {
|
|
291
|
+
if (state.sessions.size === 0) {
|
|
292
|
+
return chalk.dim('No active sessions');
|
|
293
|
+
}
|
|
294
|
+
const lines = [];
|
|
295
|
+
for (const session of state.sessions.values()) {
|
|
296
|
+
const reg = session.registration;
|
|
297
|
+
const ago = formatTimeAgo(new Date(reg.startedAt));
|
|
298
|
+
lines.push(` [${session.displayNumber}] ${reg.sessionId.substring(0, 8)} ${reg.label} ${chalk.dim(ago)}`);
|
|
299
|
+
}
|
|
300
|
+
return lines.join('\n');
|
|
301
|
+
}
|
|
302
|
+
// --- Rendering ---
|
|
303
|
+
/**
|
|
304
|
+
* Builds the dashboard content as a string.
|
|
305
|
+
* Pure function — no side effects. The caller handles writing to the terminal
|
|
306
|
+
* and coordinating with readline's prompt.
|
|
307
|
+
*/
|
|
308
|
+
function buildDashboard(state) {
|
|
309
|
+
const lines = [];
|
|
310
|
+
const sessionCount = state.sessions.size;
|
|
311
|
+
const pendingCount = state.pendingEscalations.size;
|
|
312
|
+
// Header
|
|
313
|
+
lines.push(chalk.bold.cyan(' IronCurtain Escalation Listener') +
|
|
314
|
+
chalk.dim(` ${sessionCount} session${sessionCount !== 1 ? 's' : ''} active`));
|
|
315
|
+
lines.push(chalk.dim(' ' + '\u2500'.repeat(60)));
|
|
316
|
+
// Active Sessions
|
|
317
|
+
lines.push('');
|
|
318
|
+
lines.push(chalk.bold(' Active Sessions:'));
|
|
319
|
+
if (sessionCount === 0) {
|
|
320
|
+
lines.push(chalk.dim(' Waiting for PTY sessions...'));
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
for (const session of state.sessions.values()) {
|
|
324
|
+
const reg = session.registration;
|
|
325
|
+
const ago = formatTimeAgo(new Date(reg.startedAt));
|
|
326
|
+
const sessionPending = countPendingForSession(state, reg.sessionId);
|
|
327
|
+
const pendingLabel = sessionPending > 0 ? chalk.yellow(` ${sessionPending} pending`) : chalk.dim(' 0 pending');
|
|
328
|
+
lines.push(` [${chalk.bold(String(session.displayNumber))}] ` +
|
|
329
|
+
`${reg.sessionId.substring(0, 8)} ` +
|
|
330
|
+
`${reg.label.substring(0, 40)} ` +
|
|
331
|
+
chalk.dim(ago) +
|
|
332
|
+
pendingLabel);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Pending Escalations
|
|
336
|
+
lines.push('');
|
|
337
|
+
lines.push(chalk.dim(' ' + '\u2500'.repeat(60)));
|
|
338
|
+
lines.push('');
|
|
339
|
+
lines.push(chalk.bold(' Pending Escalations:') + (pendingCount === 0 ? chalk.dim(' (none)') : ''));
|
|
340
|
+
if (pendingCount > 0) {
|
|
341
|
+
const sorted = [...state.pendingEscalations.values()].sort((a, b) => a.displayNumber - b.displayNumber);
|
|
342
|
+
for (const esc of sorted) {
|
|
343
|
+
lines.push('');
|
|
344
|
+
lines.push(` [${chalk.bold.yellow(String(esc.displayNumber))}] ` +
|
|
345
|
+
`Session #${esc.sessionDisplayNumber} (${esc.sessionId.substring(0, 8)})`);
|
|
346
|
+
lines.push(` Tool: ${chalk.cyan(esc.request.serverName + '/' + esc.request.toolName)}`);
|
|
347
|
+
const argPreview = formatArgPreview(esc.request.arguments);
|
|
348
|
+
if (argPreview) {
|
|
349
|
+
lines.push(` Args: ${argPreview}`);
|
|
350
|
+
}
|
|
351
|
+
lines.push(` Reason: ${esc.request.reason}`);
|
|
352
|
+
if (esc.request.context) {
|
|
353
|
+
for (const [k, v] of Object.entries(esc.request.context)) {
|
|
354
|
+
lines.push(` ${k}: ${chalk.yellow(v)}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// History
|
|
360
|
+
if (state.history.length > 0) {
|
|
361
|
+
lines.push('');
|
|
362
|
+
lines.push(chalk.dim(' ' + '\u2500'.repeat(60)));
|
|
363
|
+
lines.push('');
|
|
364
|
+
lines.push(chalk.bold(' History (last 10):'));
|
|
365
|
+
const recent = state.history.slice(0, 10);
|
|
366
|
+
for (const entry of recent) {
|
|
367
|
+
const time = entry.resolvedAt.toLocaleTimeString();
|
|
368
|
+
const decisionLabel = entry.decision === 'approved' ? chalk.green('APPROVED') : chalk.red('DENIED');
|
|
369
|
+
lines.push(` [${chalk.dim(time)}] ` + `${entry.serverName}/${entry.toolName} ` + decisionLabel);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Command hint (prompt itself is handled by readline)
|
|
373
|
+
lines.push('');
|
|
374
|
+
lines.push(chalk.dim(' ' + '\u2500'.repeat(60)));
|
|
375
|
+
lines.push(chalk.dim(' /approve N | /deny N | /approve all | /deny all | /quit'));
|
|
376
|
+
return lines.join('\n') + '\n';
|
|
377
|
+
}
|
|
378
|
+
// --- Helpers ---
|
|
379
|
+
function countPendingForSession(state, sessionId) {
|
|
380
|
+
let count = 0;
|
|
381
|
+
for (const esc of state.pendingEscalations.values()) {
|
|
382
|
+
if (esc.sessionId === sessionId)
|
|
383
|
+
count++;
|
|
384
|
+
}
|
|
385
|
+
return count;
|
|
386
|
+
}
|
|
387
|
+
function formatTimeAgo(date) {
|
|
388
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
389
|
+
if (seconds < 60)
|
|
390
|
+
return `${seconds}s ago`;
|
|
391
|
+
const minutes = Math.floor(seconds / 60);
|
|
392
|
+
if (minutes < 60)
|
|
393
|
+
return `${minutes}m ago`;
|
|
394
|
+
const hours = Math.floor(minutes / 60);
|
|
395
|
+
return `${hours}h ago`;
|
|
396
|
+
}
|
|
397
|
+
function formatArgPreview(args) {
|
|
398
|
+
const parts = [];
|
|
399
|
+
for (const [key, value] of Object.entries(args)) {
|
|
400
|
+
if (typeof value === 'string') {
|
|
401
|
+
const display = value.length > 60 ? value.substring(0, 57) + '...' : value;
|
|
402
|
+
parts.push(`${key}: ${display}`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return parts.slice(0, 3).join(', ');
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=listener-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener-command.js","sourceRoot":"","sources":["../../src/escalation/listener-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State management for the escalation listener TUI.
|
|
3
|
+
*
|
|
4
|
+
* Immutable state updates -- each mutation returns a new state object.
|
|
5
|
+
* The rendering layer reads state and redraws on change.
|
|
6
|
+
*/
|
|
7
|
+
import type { PtySessionRegistration } from '../docker/pty-types.js';
|
|
8
|
+
import type { EscalationWatcher } from './escalation-watcher.js';
|
|
9
|
+
import type { EscalationRequest } from '../session/types.js';
|
|
10
|
+
export interface ActiveSession {
|
|
11
|
+
readonly registration: PtySessionRegistration;
|
|
12
|
+
readonly watcher: EscalationWatcher;
|
|
13
|
+
/** Sequential display number assigned when the session was first detected. */
|
|
14
|
+
readonly displayNumber: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PendingEscalation {
|
|
17
|
+
readonly sessionId: string;
|
|
18
|
+
readonly sessionDisplayNumber: number;
|
|
19
|
+
readonly request: EscalationRequest;
|
|
20
|
+
/** Monotonically increasing display number for /approve and /deny commands. */
|
|
21
|
+
readonly displayNumber: number;
|
|
22
|
+
readonly receivedAt: Date;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedEscalation {
|
|
25
|
+
readonly sessionId: string;
|
|
26
|
+
readonly toolName: string;
|
|
27
|
+
readonly serverName: string;
|
|
28
|
+
readonly decision: 'approved' | 'denied';
|
|
29
|
+
readonly resolvedAt: Date;
|
|
30
|
+
}
|
|
31
|
+
export interface ListenerState {
|
|
32
|
+
readonly sessions: ReadonlyMap<string, ActiveSession>;
|
|
33
|
+
readonly pendingEscalations: ReadonlyMap<number, PendingEscalation>;
|
|
34
|
+
readonly history: readonly ResolvedEscalation[];
|
|
35
|
+
readonly nextEscalationNumber: number;
|
|
36
|
+
readonly nextSessionNumber: number;
|
|
37
|
+
}
|
|
38
|
+
/** Creates an empty initial state. */
|
|
39
|
+
export declare function createInitialState(): ListenerState;
|
|
40
|
+
/** Adds a new session to the state. */
|
|
41
|
+
export declare function addSession(state: ListenerState, registration: PtySessionRegistration, watcher: EscalationWatcher): ListenerState;
|
|
42
|
+
/** Removes a session and its pending escalations from the state. */
|
|
43
|
+
export declare function removeSession(state: ListenerState, sessionId: string): ListenerState;
|
|
44
|
+
/** Adds a new pending escalation to the state. */
|
|
45
|
+
export declare function addEscalation(state: ListenerState, sessionId: string, request: EscalationRequest): ListenerState;
|
|
46
|
+
/** Resolves a pending escalation and moves it to history. */
|
|
47
|
+
export declare function resolveEscalation(state: ListenerState, displayNumber: number, decision: 'approved' | 'denied'): ListenerState;
|
|
48
|
+
/** Removes a pending escalation that expired. */
|
|
49
|
+
export declare function expireEscalation(state: ListenerState, sessionId: string, escalationId: string): ListenerState;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State management for the escalation listener TUI.
|
|
3
|
+
*
|
|
4
|
+
* Immutable state updates -- each mutation returns a new state object.
|
|
5
|
+
* The rendering layer reads state and redraws on change.
|
|
6
|
+
*/
|
|
7
|
+
/** Maximum number of resolved escalations to keep in history. */
|
|
8
|
+
const MAX_HISTORY = 20;
|
|
9
|
+
/** Creates an empty initial state. */
|
|
10
|
+
export function createInitialState() {
|
|
11
|
+
return {
|
|
12
|
+
sessions: new Map(),
|
|
13
|
+
pendingEscalations: new Map(),
|
|
14
|
+
history: [],
|
|
15
|
+
nextEscalationNumber: 1,
|
|
16
|
+
nextSessionNumber: 1,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Adds a new session to the state. */
|
|
20
|
+
export function addSession(state, registration, watcher) {
|
|
21
|
+
const sessions = new Map(state.sessions);
|
|
22
|
+
sessions.set(registration.sessionId, {
|
|
23
|
+
registration,
|
|
24
|
+
watcher,
|
|
25
|
+
displayNumber: state.nextSessionNumber,
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
...state,
|
|
29
|
+
sessions,
|
|
30
|
+
nextSessionNumber: state.nextSessionNumber + 1,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Removes a session and its pending escalations from the state. */
|
|
34
|
+
export function removeSession(state, sessionId) {
|
|
35
|
+
const sessions = new Map(state.sessions);
|
|
36
|
+
sessions.delete(sessionId);
|
|
37
|
+
// Remove any pending escalations for this session
|
|
38
|
+
const pendingEscalations = new Map(state.pendingEscalations);
|
|
39
|
+
for (const [num, esc] of pendingEscalations) {
|
|
40
|
+
if (esc.sessionId === sessionId) {
|
|
41
|
+
pendingEscalations.delete(num);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { ...state, sessions, pendingEscalations };
|
|
45
|
+
}
|
|
46
|
+
/** Adds a new pending escalation to the state. */
|
|
47
|
+
export function addEscalation(state, sessionId, request) {
|
|
48
|
+
const session = state.sessions.get(sessionId);
|
|
49
|
+
if (!session)
|
|
50
|
+
return state;
|
|
51
|
+
const displayNumber = state.nextEscalationNumber;
|
|
52
|
+
const pendingEscalations = new Map(state.pendingEscalations);
|
|
53
|
+
pendingEscalations.set(displayNumber, {
|
|
54
|
+
sessionId,
|
|
55
|
+
sessionDisplayNumber: session.displayNumber,
|
|
56
|
+
request,
|
|
57
|
+
displayNumber,
|
|
58
|
+
receivedAt: new Date(),
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
...state,
|
|
62
|
+
pendingEscalations,
|
|
63
|
+
nextEscalationNumber: displayNumber + 1,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Resolves a pending escalation and moves it to history. */
|
|
67
|
+
export function resolveEscalation(state, displayNumber, decision) {
|
|
68
|
+
const escalation = state.pendingEscalations.get(displayNumber);
|
|
69
|
+
if (!escalation)
|
|
70
|
+
return state;
|
|
71
|
+
const pendingEscalations = new Map(state.pendingEscalations);
|
|
72
|
+
pendingEscalations.delete(displayNumber);
|
|
73
|
+
const resolved = {
|
|
74
|
+
sessionId: escalation.sessionId,
|
|
75
|
+
toolName: escalation.request.toolName,
|
|
76
|
+
serverName: escalation.request.serverName,
|
|
77
|
+
decision,
|
|
78
|
+
resolvedAt: new Date(),
|
|
79
|
+
};
|
|
80
|
+
// Prepend to history and trim to MAX_HISTORY
|
|
81
|
+
const history = [resolved, ...state.history].slice(0, MAX_HISTORY);
|
|
82
|
+
return { ...state, pendingEscalations, history };
|
|
83
|
+
}
|
|
84
|
+
/** Removes a pending escalation that expired. */
|
|
85
|
+
export function expireEscalation(state, sessionId, escalationId) {
|
|
86
|
+
const pendingEscalations = new Map(state.pendingEscalations);
|
|
87
|
+
for (const [num, esc] of pendingEscalations) {
|
|
88
|
+
if (esc.sessionId === sessionId && esc.request.escalationId === escalationId) {
|
|
89
|
+
pendingEscalations.delete(num);
|
|
90
|
+
// Add to history as expired
|
|
91
|
+
const resolved = {
|
|
92
|
+
sessionId: esc.sessionId,
|
|
93
|
+
toolName: esc.request.toolName,
|
|
94
|
+
serverName: esc.request.serverName,
|
|
95
|
+
decision: 'denied',
|
|
96
|
+
resolvedAt: new Date(),
|
|
97
|
+
};
|
|
98
|
+
const history = [resolved, ...state.history].slice(0, MAX_HISTORY);
|
|
99
|
+
return { ...state, pendingEscalations, history };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return state;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=listener-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listener-state.js","sourceRoot":"","sources":["../../src/escalation/listener-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsCH,iEAAiE;AACjE,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,sCAAsC;AACtC,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,kBAAkB,EAAE,IAAI,GAAG,EAAE;QAC7B,OAAO,EAAE,EAAE;QACX,oBAAoB,EAAE,CAAC;QACvB,iBAAiB,EAAE,CAAC;KACrB,CAAC;AACJ,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,UAAU,CACxB,KAAoB,EACpB,YAAoC,EACpC,OAA0B;IAE1B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE;QACnC,YAAY;QACZ,OAAO;QACP,aAAa,EAAE,KAAK,CAAC,iBAAiB;KACvC,CAAC,CAAC;IACH,OAAO;QACL,GAAG,KAAK;QACR,QAAQ;QACR,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,GAAG,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,SAAiB;IACnE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAE3B,kDAAkD;IAClD,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7D,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,kBAAkB,EAAE,CAAC;QAC5C,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAChC,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;AACpD,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,SAAiB,EAAE,OAA0B;IAC/F,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,MAAM,aAAa,GAAG,KAAK,CAAC,oBAAoB,CAAC;IACjD,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7D,kBAAkB,CAAC,GAAG,CAAC,aAAa,EAAE;QACpC,SAAS;QACT,oBAAoB,EAAE,OAAO,CAAC,aAAa;QAC3C,OAAO;QACP,aAAa;QACb,UAAU,EAAE,IAAI,IAAI,EAAE;KACvB,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,KAAK;QACR,kBAAkB;QAClB,oBAAoB,EAAE,aAAa,GAAG,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,iBAAiB,CAC/B,KAAoB,EACpB,aAAqB,EACrB,QAA+B;IAE/B,MAAM,UAAU,GAAG,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7D,kBAAkB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAEzC,MAAM,QAAQ,GAAuB;QACnC,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ;QACrC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,UAAU;QACzC,QAAQ;QACR,UAAU,EAAE,IAAI,IAAI,EAAE;KACvB,CAAC;IAEF,6CAA6C;IAC7C,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAEnE,OAAO,EAAE,GAAG,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC;AACnD,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB,CAAC,KAAoB,EAAE,SAAiB,EAAE,YAAoB;IAC5F,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAE7D,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,kBAAkB,EAAE,CAAC;QAC5C,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;YAC7E,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAE/B,4BAA4B;YAC5B,MAAM,QAAQ,GAAuB;gBACnC,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ;gBAC9B,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU;gBAClC,QAAQ,EAAE,QAAQ;gBAClB,UAAU,EAAE,IAAI,IAAI,EAAE;aACvB,CAAC;YACF,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;YAEnE,OAAO,EAAE,GAAG,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session registry -- reads and validates PTY session registration files.
|
|
3
|
+
*
|
|
4
|
+
* PTY sessions write registration files to ~/.ironcurtain/pty-registry/
|
|
5
|
+
* when they start, and delete them on shutdown. The escalation listener
|
|
6
|
+
* polls this directory to discover active sessions.
|
|
7
|
+
*
|
|
8
|
+
* Stale session detection: if a registration file exists but the PID
|
|
9
|
+
* is no longer alive, the session crashed without cleanup. The registry
|
|
10
|
+
* removes stale registrations.
|
|
11
|
+
*/
|
|
12
|
+
import type { PtySessionRegistration } from '../docker/pty-types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Reads all valid, non-stale session registrations from the registry directory.
|
|
15
|
+
* Removes stale registrations (PID no longer alive) as a side effect.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readActiveRegistrations(registryDir: string): PtySessionRegistration[];
|