@provos/ironcurtain 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +219 -248
  2. package/dist/cli.js +10 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/config-command.js +144 -6
  5. package/dist/config/config-command.js.map +1 -1
  6. package/dist/config/constitution-user-base.md +2 -0
  7. package/dist/config/first-start.js +77 -12
  8. package/dist/config/first-start.js.map +1 -1
  9. package/dist/config/generated/compiled-policy.json +199 -72
  10. package/dist/config/generated/tool-annotations.json +1018 -36
  11. package/dist/config/mcp-servers.json +10 -0
  12. package/dist/config/paths.d.ts +31 -0
  13. package/dist/config/paths.js +52 -2
  14. package/dist/config/paths.js.map +1 -1
  15. package/dist/config/types.d.ts +8 -0
  16. package/dist/config/user-config.d.ts +21 -3
  17. package/dist/config/user-config.js +34 -3
  18. package/dist/config/user-config.js.map +1 -1
  19. package/dist/docker/adapters/claude-code.js +104 -23
  20. package/dist/docker/adapters/claude-code.js.map +1 -1
  21. package/dist/docker/agent-adapter.d.ts +24 -1
  22. package/dist/docker/agent-adapter.js +6 -1
  23. package/dist/docker/agent-adapter.js.map +1 -1
  24. package/dist/docker/docker-agent-session.d.ts +16 -9
  25. package/dist/docker/docker-agent-session.js +64 -90
  26. package/dist/docker/docker-agent-session.js.map +1 -1
  27. package/dist/docker/docker-infrastructure.d.ts +51 -0
  28. package/dist/docker/docker-infrastructure.js +261 -0
  29. package/dist/docker/docker-infrastructure.js.map +1 -0
  30. package/dist/docker/docker-manager.js +3 -0
  31. package/dist/docker/docker-manager.js.map +1 -1
  32. package/dist/docker/mitm-proxy.js +4 -0
  33. package/dist/docker/mitm-proxy.js.map +1 -1
  34. package/dist/docker/oauth-credentials.d.ts +81 -0
  35. package/dist/docker/oauth-credentials.js +178 -0
  36. package/dist/docker/oauth-credentials.js.map +1 -0
  37. package/dist/docker/orientation.d.ts +1 -1
  38. package/dist/docker/orientation.js +3 -2
  39. package/dist/docker/orientation.js.map +1 -1
  40. package/dist/docker/provider-config.d.ts +3 -0
  41. package/dist/docker/provider-config.js +28 -0
  42. package/dist/docker/provider-config.js.map +1 -1
  43. package/dist/docker/pty-session.d.ts +27 -0
  44. package/dist/docker/pty-session.js +510 -0
  45. package/dist/docker/pty-session.js.map +1 -0
  46. package/dist/docker/pty-types.d.ts +24 -0
  47. package/dist/docker/pty-types.js +12 -0
  48. package/dist/docker/pty-types.js.map +1 -0
  49. package/dist/docker/types.d.ts +6 -0
  50. package/dist/escalation/escalation-watcher.d.ts +44 -0
  51. package/dist/escalation/escalation-watcher.js +92 -0
  52. package/dist/escalation/escalation-watcher.js.map +1 -0
  53. package/dist/escalation/listener-command.d.ts +10 -0
  54. package/dist/escalation/listener-command.js +407 -0
  55. package/dist/escalation/listener-command.js.map +1 -0
  56. package/dist/escalation/listener-state.d.ts +49 -0
  57. package/dist/escalation/listener-state.js +104 -0
  58. package/dist/escalation/listener-state.js.map +1 -0
  59. package/dist/escalation/session-registry.d.ts +17 -0
  60. package/dist/escalation/session-registry.js +98 -0
  61. package/dist/escalation/session-registry.js.map +1 -0
  62. package/dist/index.js +15 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/logger.js +2 -3
  65. package/dist/logger.js.map +1 -1
  66. package/dist/pipeline/annotate.js +35 -15
  67. package/dist/pipeline/annotate.js.map +1 -1
  68. package/dist/pipeline/constitution-customizer.d.ts +2 -1
  69. package/dist/pipeline/constitution-customizer.js +36 -3
  70. package/dist/pipeline/constitution-customizer.js.map +1 -1
  71. package/dist/pipeline/github-identity.d.ts +38 -0
  72. package/dist/pipeline/github-identity.js +64 -0
  73. package/dist/pipeline/github-identity.js.map +1 -0
  74. package/dist/pipeline/handwritten-scenarios.js +34 -0
  75. package/dist/pipeline/handwritten-scenarios.js.map +1 -1
  76. package/dist/sandbox/index.js +14 -3
  77. package/dist/sandbox/index.js.map +1 -1
  78. package/dist/servers/fetch-server.js +2 -2
  79. package/dist/session/base-transport.d.ts +19 -0
  80. package/dist/session/base-transport.js +36 -0
  81. package/dist/session/base-transport.js.map +1 -0
  82. package/dist/session/cli-transport.d.ts +3 -3
  83. package/dist/session/cli-transport.js +7 -4
  84. package/dist/session/cli-transport.js.map +1 -1
  85. package/dist/session/index.js +24 -78
  86. package/dist/session/index.js.map +1 -1
  87. package/dist/session/interaction-log.d.ts +13 -0
  88. package/dist/session/interaction-log.js +28 -0
  89. package/dist/session/interaction-log.js.map +1 -0
  90. package/dist/session/preflight.d.ts +3 -0
  91. package/dist/session/preflight.js +39 -18
  92. package/dist/session/preflight.js.map +1 -1
  93. package/dist/session/types.d.ts +2 -0
  94. package/dist/signal/bot-command.js +5 -0
  95. package/dist/signal/bot-command.js.map +1 -1
  96. package/dist/signal/format.d.ts +21 -1
  97. package/dist/signal/format.js +41 -3
  98. package/dist/signal/format.js.map +1 -1
  99. package/dist/signal/signal-bot-daemon.d.ts +53 -18
  100. package/dist/signal/signal-bot-daemon.js +344 -106
  101. package/dist/signal/signal-bot-daemon.js.map +1 -1
  102. package/dist/signal/signal-config.d.ts +4 -0
  103. package/dist/signal/signal-config.js +2 -0
  104. package/dist/signal/signal-config.js.map +1 -1
  105. package/dist/signal/signal-transport.d.ts +13 -7
  106. package/dist/signal/signal-transport.js +36 -12
  107. package/dist/signal/signal-transport.js.map +1 -1
  108. package/dist/trusted-process/audit-log.d.ts +6 -1
  109. package/dist/trusted-process/audit-log.js +22 -2
  110. package/dist/trusted-process/audit-log.js.map +1 -1
  111. package/dist/trusted-process/audit-redactor.d.ts +25 -0
  112. package/dist/trusted-process/audit-redactor.js +121 -0
  113. package/dist/trusted-process/audit-redactor.js.map +1 -0
  114. package/dist/trusted-process/escalation.d.ts +1 -1
  115. package/dist/trusted-process/escalation.js +6 -1
  116. package/dist/trusted-process/escalation.js.map +1 -1
  117. package/dist/trusted-process/index.d.ts +2 -1
  118. package/dist/trusted-process/index.js +47 -6
  119. package/dist/trusted-process/index.js.map +1 -1
  120. package/dist/trusted-process/mcp-client-manager.js +14 -2
  121. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  122. package/dist/trusted-process/mcp-error-utils.d.ts +11 -0
  123. package/dist/trusted-process/mcp-error-utils.js +50 -0
  124. package/dist/trusted-process/mcp-error-utils.js.map +1 -0
  125. package/dist/trusted-process/mcp-proxy-server.d.ts +4 -2
  126. package/dist/trusted-process/mcp-proxy-server.js +56 -13
  127. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  128. package/dist/trusted-process/path-utils.d.ts +15 -0
  129. package/dist/trusted-process/path-utils.js +38 -0
  130. package/dist/trusted-process/path-utils.js.map +1 -1
  131. package/dist/trusted-process/sandbox-integration.d.ts +5 -0
  132. package/dist/trusted-process/sandbox-integration.js +1 -1
  133. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  134. package/dist/trusted-process/server-context.d.ts +21 -0
  135. package/dist/trusted-process/server-context.js +42 -0
  136. package/dist/trusted-process/server-context.js.map +1 -0
  137. package/dist/types/argument-roles.d.ts +5 -2
  138. package/dist/types/argument-roles.js +25 -2
  139. package/dist/types/argument-roles.js.map +1 -1
  140. package/docker/Dockerfile.claude-code +8 -3
  141. package/docker/entrypoint-claude-code.sh +64 -4
  142. package/package.json +16 -8
  143. package/src/config/constitution-user-base.md +2 -0
  144. package/src/config/generated/compiled-policy.json +199 -72
  145. package/src/config/generated/tool-annotations.json +1018 -36
  146. package/src/config/mcp-servers.json +10 -0
@@ -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>;
@@ -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;