@aeolus-fleet/trierarch 0.32.2 → 0.33.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 +11 -11
- package/dist/adapters/caller-check.d.ts +14 -0
- package/dist/adapters/caller-check.js +30 -0
- package/dist/adapters/caller-check.js.map +1 -1
- package/dist/adapters/claude-code.d.ts +26 -6
- package/dist/adapters/claude-code.js +54 -33
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/clone-workspace.js +10 -1
- package/dist/adapters/clone-workspace.js.map +1 -1
- package/dist/adapters/codex.d.ts +47 -16
- package/dist/adapters/codex.js +82 -45
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/container.d.ts +4 -2
- package/dist/adapters/container.js +24 -17
- package/dist/adapters/container.js.map +1 -1
- package/dist/adapters/files.d.ts +6 -0
- package/dist/adapters/files.js +16 -0
- package/dist/adapters/files.js.map +1 -1
- package/dist/adapters/git-workspace.js +21 -4
- package/dist/adapters/git-workspace.js.map +1 -1
- package/dist/adapters/harnesses.d.ts +4 -3
- package/dist/adapters/harnesses.js +6 -7
- package/dist/adapters/harnesses.js.map +1 -1
- package/dist/adapters/hold-protocol.d.ts +50 -0
- package/dist/adapters/hold-protocol.js +58 -0
- package/dist/adapters/hold-protocol.js.map +1 -0
- package/dist/adapters/hold-runtime.d.ts +36 -0
- package/dist/adapters/hold-runtime.js +146 -0
- package/dist/adapters/hold-runtime.js.map +1 -0
- package/dist/adapters/hold.d.ts +41 -0
- package/dist/adapters/hold.js +310 -0
- package/dist/adapters/hold.js.map +1 -0
- package/dist/adapters/paths.d.ts +2 -0
- package/dist/adapters/paths.js +1 -0
- package/dist/adapters/paths.js.map +1 -1
- package/dist/adapters/runtime-adapters.d.ts +14 -1
- package/dist/adapters/runtime-adapters.js +14 -2
- package/dist/adapters/runtime-adapters.js.map +1 -1
- package/dist/adapters/stop-turn.d.ts +5 -5
- package/dist/adapters/stop-turn.js +6 -6
- package/dist/adapters/stop-turn.js.map +1 -1
- package/dist/adapters/tmux.d.ts +7 -30
- package/dist/adapters/tmux.js +9 -107
- package/dist/adapters/tmux.js.map +1 -1
- package/dist/bin/aeolus-trierarch.js +16 -7
- package/dist/bin/aeolus-trierarch.js.map +1 -1
- package/dist/cli/config-check.js +1 -1
- package/dist/cli/config-check.js.map +1 -1
- package/dist/cli/hold.d.ts +7 -0
- package/dist/cli/hold.js +60 -0
- package/dist/cli/hold.js.map +1 -0
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.js +17 -11
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/main.js +7 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.js +1 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/upgrade.d.ts +6 -1
- package/dist/cli/upgrade.js +10 -2
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/core/check-session-runtime.d.ts +5 -3
- package/dist/core/check-session-runtime.js +13 -14
- package/dist/core/check-session-runtime.js.map +1 -1
- package/dist/core/ports.d.ts +18 -12
- package/dist/core/reconciler.js +20 -5
- package/dist/core/reconciler.js.map +1 -1
- package/dist/core/remote-control.d.ts +15 -0
- package/dist/core/remote-control.js +30 -0
- package/dist/core/remote-control.js.map +1 -0
- package/package.json +1 -1
- package/dist/adapters/codex-screen.d.ts +0 -1
- package/dist/adapters/codex-screen.js +0 -19
- package/dist/adapters/codex-screen.js.map +0 -1
- package/dist/adapters/codex-session-log.d.ts +0 -8
- package/dist/adapters/codex-session-log.js +0 -45
- package/dist/adapters/codex-session-log.js.map +0 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createWriteStream } from 'node:fs';
|
|
3
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { createServer } from 'node:net';
|
|
5
|
+
import { createInterface } from 'node:readline';
|
|
6
|
+
import { codexThreadStartedSchema, codexTurnCommand } from './codex.js';
|
|
7
|
+
import { holdRequestSchema, parsedLine, REFUSED } from './hold-protocol.js';
|
|
8
|
+
/**
|
|
9
|
+
* The hold (headless H8 to H12): a small process per session, started outside
|
|
10
|
+
* the trierarch's service so it outlives it, the only holder of its harness's
|
|
11
|
+
* stdin and stdout. It starts the harness as its child, appends every line the
|
|
12
|
+
* harness writes to the session's events, keeps the harness's session id, and
|
|
13
|
+
* answers the trierarch alone on its socket: send a line, interrupt the turn,
|
|
14
|
+
* say its state. When the harness ends, the hold ends; when the hold is
|
|
15
|
+
* stopped, the harness ends with it. A hold that dies without the chance to
|
|
16
|
+
* stop it, as SIGKILL leaves it, still ends its harness: a guard, a process of
|
|
17
|
+
* its own, holds a pipe from the hold and ends each harness's group once that
|
|
18
|
+
* pipe closes, which the hold's death does.
|
|
19
|
+
*
|
|
20
|
+
* Claude Code's turn is stopped with the interrupt control request on its
|
|
21
|
+
* stdin, which keeps the process (H12, Thomas, 25 Oct: "Headless option 2").
|
|
22
|
+
* When Claude Code refuses it, SIGINT stops the turn and the process, and the
|
|
23
|
+
* hold starts it again resuming its session, so the trierarch sees no exit.
|
|
24
|
+
*
|
|
25
|
+
* Codex runs one `codex exec` per turn (H11): the start command is the first
|
|
26
|
+
* turn, and each line sent is the prompt of a `codex exec resume` of the
|
|
27
|
+
* thread the first named, started once the last turn's process has ended.
|
|
28
|
+
* Between turns no Codex process runs and the hold stays; its turn is stopped
|
|
29
|
+
* with SIGINT, which ends the process (H12).
|
|
30
|
+
*/
|
|
31
|
+
/** The longest the hold waits for an interrupted turn to end before the forced line goes in anyway (timings-and-limits.md). */
|
|
32
|
+
export const INTERRUPT_WAIT_MS = 10_000;
|
|
33
|
+
/** The flag that resumes a Claude Code session by its id, and the one that continues the folder's last, which a resume replaces. */
|
|
34
|
+
const RESUME = '--resume';
|
|
35
|
+
const CONTINUE = '--continue';
|
|
36
|
+
/** How long the guard gives a harness's group to end after SIGTERM before SIGKILL. */
|
|
37
|
+
const GUARD_GRACE_MS = 2_000;
|
|
38
|
+
/**
|
|
39
|
+
* The guard, run by Node: each stdin line is the group of a harness the hold
|
|
40
|
+
* started; when stdin closes, every such group is ended. It is plain
|
|
41
|
+
* JavaScript so it needs nothing the hold was started with.
|
|
42
|
+
*/
|
|
43
|
+
const GUARD = `
|
|
44
|
+
const groups = new Set();
|
|
45
|
+
const end = (signal) => {
|
|
46
|
+
for (const group of groups) {
|
|
47
|
+
try { process.kill(-group, signal); } catch {}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const lines = require('node:readline').createInterface({ input: process.stdin });
|
|
51
|
+
lines.on('line', (line) => { groups.add(Number(line)); });
|
|
52
|
+
lines.on('close', () => {
|
|
53
|
+
end('SIGTERM');
|
|
54
|
+
setTimeout(() => { end('SIGKILL'); }, ${String(GUARD_GRACE_MS)});
|
|
55
|
+
});
|
|
56
|
+
`;
|
|
57
|
+
/** The session id a Claude Code event names, when it names one. */
|
|
58
|
+
function sessionIdOf(event) {
|
|
59
|
+
const id = Reflect.get(event, 'session_id');
|
|
60
|
+
return typeof id === 'string' ? id : undefined;
|
|
61
|
+
}
|
|
62
|
+
/** Runs the hold until its harness ends; answers the harness's exit code. */
|
|
63
|
+
export async function runHold(options) {
|
|
64
|
+
const { files, folder, harness, environment, version } = options;
|
|
65
|
+
await mkdir(files.folder, { recursive: true, mode: 0o700 });
|
|
66
|
+
await rm(files.socket, { force: true });
|
|
67
|
+
await writeFile(files.pid, String(process.pid));
|
|
68
|
+
const events = createWriteStream(files.events, { flags: 'a', mode: 0o600 });
|
|
69
|
+
let sessionId;
|
|
70
|
+
let isBusy = false;
|
|
71
|
+
let isRestarting = false;
|
|
72
|
+
let isStopping = false;
|
|
73
|
+
const isCodex = harness === 'codex';
|
|
74
|
+
let controls = 0;
|
|
75
|
+
const waiters = new Set();
|
|
76
|
+
const tell = (event) => {
|
|
77
|
+
for (const waiter of [...waiters]) {
|
|
78
|
+
waiter(event);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
let resolveEnd = () => undefined;
|
|
82
|
+
const ended = new Promise((resolve) => {
|
|
83
|
+
resolveEnd = resolve;
|
|
84
|
+
});
|
|
85
|
+
// A line is acted on once it is written, so whoever a result or a control response answers finds it in the events.
|
|
86
|
+
const onLine = (line) => {
|
|
87
|
+
events.write(`${line}\n`, () => {
|
|
88
|
+
onWritten(line);
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const onWritten = (line) => {
|
|
92
|
+
const event = parsedLine(line);
|
|
93
|
+
if (typeof event !== 'object' || event === null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const type = Reflect.get(event, 'type');
|
|
97
|
+
const id = isCodex ? codexThreadStartedSchema.safeParse(event).data?.thread_id : sessionIdOf(event);
|
|
98
|
+
if (id !== undefined && id !== sessionId) {
|
|
99
|
+
sessionId = id;
|
|
100
|
+
void writeFile(files.sessionId, id);
|
|
101
|
+
}
|
|
102
|
+
if (type === 'result') {
|
|
103
|
+
isBusy = false;
|
|
104
|
+
tell({ type: 'result' });
|
|
105
|
+
}
|
|
106
|
+
const response = type === 'control_response' ? Reflect.get(event, 'response') : undefined;
|
|
107
|
+
if (typeof response === 'object' && response !== null) {
|
|
108
|
+
const requestId = Reflect.get(response, 'request_id');
|
|
109
|
+
tell({ type: 'control', requestId: typeof requestId === 'string' ? requestId : '', isRefused: Reflect.get(response, 'subtype') === 'error' });
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// A session of its own, so a signal to the hold's group does not end the guard before it ends the harness.
|
|
113
|
+
const guard = spawn(process.execPath, ['-e', GUARD], { env: {}, stdio: ['pipe', 'ignore', 'ignore'], detached: true });
|
|
114
|
+
guard.stdin.on('error', () => undefined);
|
|
115
|
+
guard.unref();
|
|
116
|
+
/** Kills the harness's process group: the harness and every process it started. */
|
|
117
|
+
const killGroup = (running) => {
|
|
118
|
+
try {
|
|
119
|
+
process.kill(-(running.pid ?? 0), 'SIGKILL');
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Its group is gone already.
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const startHarness = (command) => {
|
|
126
|
+
const [program = '', ...args] = command;
|
|
127
|
+
// A process group of its own: a stop ends the harness and every process it started.
|
|
128
|
+
const started = spawn(program, args, { cwd: folder, env: { ...environment }, stdio: ['pipe', 'pipe', 'ignore'], detached: true });
|
|
129
|
+
if (started.pid !== undefined) {
|
|
130
|
+
guard.stdin.write(`${String(started.pid)}\n`);
|
|
131
|
+
}
|
|
132
|
+
started.stdin.on('error', () => undefined);
|
|
133
|
+
// codex exec reads a stdin that is no terminal to its end before its turn: it gets none.
|
|
134
|
+
if (isCodex) {
|
|
135
|
+
started.stdin.end();
|
|
136
|
+
}
|
|
137
|
+
createInterface({ input: started.stdout }).on('line', onLine);
|
|
138
|
+
started.on('error', () => {
|
|
139
|
+
resolveEnd(1);
|
|
140
|
+
});
|
|
141
|
+
started.on('exit', (code) => {
|
|
142
|
+
if (isCodex && child === started) {
|
|
143
|
+
isBusy = false;
|
|
144
|
+
child = undefined;
|
|
145
|
+
}
|
|
146
|
+
tell({ type: 'exit' });
|
|
147
|
+
if (isStopping || (!isRestarting && !isCodex)) {
|
|
148
|
+
resolveEnd(code ?? 1);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return started;
|
|
152
|
+
};
|
|
153
|
+
let child = startHarness(options.command);
|
|
154
|
+
if (isCodex) {
|
|
155
|
+
isBusy = true;
|
|
156
|
+
}
|
|
157
|
+
/** Waits until the waiter is told what it waits for, or the time is up. */
|
|
158
|
+
const waitFor = (isIt, ms) => new Promise((resolve) => {
|
|
159
|
+
const waiter = (event) => {
|
|
160
|
+
if (isIt(event)) {
|
|
161
|
+
finish(event);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const timer = setTimeout(() => {
|
|
165
|
+
finish(undefined);
|
|
166
|
+
}, ms);
|
|
167
|
+
const finish = (event) => {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
waiters.delete(waiter);
|
|
170
|
+
resolve(event);
|
|
171
|
+
};
|
|
172
|
+
waiters.add(waiter);
|
|
173
|
+
});
|
|
174
|
+
/** Claude Code started again, resuming its session, after SIGINT ended it (H12). */
|
|
175
|
+
const resumed = () => {
|
|
176
|
+
const [program = '', ...flags] = options.command.filter((word) => word !== CONTINUE);
|
|
177
|
+
return sessionId === undefined ? [program, ...flags, CONTINUE] : [program, ...flags, RESUME, sessionId];
|
|
178
|
+
};
|
|
179
|
+
/** Ends Codex's running turn: SIGINT, then its process group killed when it has not ended within the wait (H12). */
|
|
180
|
+
const endCodexTurn = async (signal) => {
|
|
181
|
+
const running = child;
|
|
182
|
+
if (running === undefined) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const exited = waitFor((event) => event.type === 'exit', INTERRUPT_WAIT_MS);
|
|
186
|
+
if (signal === 'SIGINT') {
|
|
187
|
+
running.kill('SIGINT');
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
killGroup(running);
|
|
191
|
+
}
|
|
192
|
+
if ((await exited) === undefined && child === running) {
|
|
193
|
+
killGroup(running);
|
|
194
|
+
await waitFor((event) => event.type === 'exit', INTERRUPT_WAIT_MS);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
/** Codex's next turn, once the last turn's process has ended: a codex exec resume of the session's thread (H11). */
|
|
198
|
+
const startCodexTurn = async (line) => {
|
|
199
|
+
if (sessionId === undefined) {
|
|
200
|
+
return { isOk: false, error: 'Codex named no thread to resume yet' };
|
|
201
|
+
}
|
|
202
|
+
if (child !== undefined) {
|
|
203
|
+
const running = child;
|
|
204
|
+
if ((await waitFor((event) => event.type === 'exit', INTERRUPT_WAIT_MS)) === undefined && child === running) {
|
|
205
|
+
await endCodexTurn('SIGKILL');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
isBusy = true;
|
|
209
|
+
child = startHarness(codexTurnCommand({ start: options.command, threadId: sessionId, line }));
|
|
210
|
+
return { isOk: true };
|
|
211
|
+
};
|
|
212
|
+
/** The lines in the order they came: one waits for the one before, so two never start a codex exec resume on one thread. */
|
|
213
|
+
let lastCodexSend = Promise.resolve();
|
|
214
|
+
const sendCodex = (line) => {
|
|
215
|
+
const sent = lastCodexSend.then(() => startCodexTurn(line));
|
|
216
|
+
// A failed send must not hold back the lines after it.
|
|
217
|
+
lastCodexSend = sent.catch(() => undefined);
|
|
218
|
+
return sent;
|
|
219
|
+
};
|
|
220
|
+
const interrupt = async () => {
|
|
221
|
+
if (!isBusy) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (isCodex) {
|
|
225
|
+
await endCodexTurn('SIGINT');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const current = child;
|
|
229
|
+
if (current === undefined) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const until = Date.now() + INTERRUPT_WAIT_MS;
|
|
233
|
+
if (harness === 'claude-code') {
|
|
234
|
+
controls += 1;
|
|
235
|
+
const requestId = `aeolus-interrupt-${String(controls)}`;
|
|
236
|
+
const turnEnd = waitFor((event) => event.type === 'result' || event.type === 'exit', INTERRUPT_WAIT_MS);
|
|
237
|
+
const answer = waitFor((event) => event.type === 'control' && event.requestId === requestId, INTERRUPT_WAIT_MS);
|
|
238
|
+
current.stdin.write(`${JSON.stringify({ type: 'control_request', request_id: requestId, request: { subtype: 'interrupt' } })}\n`);
|
|
239
|
+
const answered = await Promise.race([answer, turnEnd]);
|
|
240
|
+
if (answered?.type !== 'control' || !answered.isRefused) {
|
|
241
|
+
await turnEnd;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// SIGINT ends the turn, and Claude Code's process with it: started again resuming its session, no exit for the trierarch to see.
|
|
246
|
+
isRestarting = harness === 'claude-code';
|
|
247
|
+
const exited = waitFor((event) => event.type === 'exit', Math.max(0, until - Date.now()));
|
|
248
|
+
current.kill('SIGINT');
|
|
249
|
+
await exited;
|
|
250
|
+
if (isRestarting) {
|
|
251
|
+
isBusy = false;
|
|
252
|
+
child = startHarness(resumed());
|
|
253
|
+
isRestarting = false;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const answer = async (request) => {
|
|
257
|
+
switch (request.call) {
|
|
258
|
+
case 'send':
|
|
259
|
+
if (isCodex) {
|
|
260
|
+
return sendCodex(request.line);
|
|
261
|
+
}
|
|
262
|
+
isBusy = true;
|
|
263
|
+
child?.stdin.write(`${request.line}\n`);
|
|
264
|
+
return { isOk: true };
|
|
265
|
+
case 'interrupt':
|
|
266
|
+
await interrupt();
|
|
267
|
+
return { isOk: true };
|
|
268
|
+
case 'state':
|
|
269
|
+
return { isOk: true, state: { status: 'running', version, ...(sessionId !== undefined && { sessionId }) } };
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const server = createServer((connection) => {
|
|
273
|
+
connection.on('error', () => undefined);
|
|
274
|
+
void (async () => {
|
|
275
|
+
if (!(await options.mayCall(connection))) {
|
|
276
|
+
connection.end(`${JSON.stringify({ isOk: false, error: REFUSED })}\n`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const lines = createInterface({ input: connection });
|
|
280
|
+
for await (const line of lines) {
|
|
281
|
+
const request = holdRequestSchema.safeParse(parsedLine(line));
|
|
282
|
+
const answered = request.success ? await answer(request.data) : { isOk: false, error: 'not a hold call' };
|
|
283
|
+
connection.end(`${JSON.stringify(answered)}\n`);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
})();
|
|
287
|
+
});
|
|
288
|
+
await new Promise((resolve) => server.listen(files.socket, resolve));
|
|
289
|
+
const stop = () => {
|
|
290
|
+
isStopping = true;
|
|
291
|
+
if (child === undefined) {
|
|
292
|
+
resolveEnd(0);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
process.kill(-(child.pid ?? 0), 'SIGTERM');
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Its group is gone already.
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
process.once('SIGTERM', stop);
|
|
303
|
+
process.once('SIGHUP', stop);
|
|
304
|
+
const code = await ended;
|
|
305
|
+
server.close();
|
|
306
|
+
await rm(files.socket, { force: true });
|
|
307
|
+
await new Promise((resolve) => events.end(resolve));
|
|
308
|
+
return code;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=hold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hold.js","sourceRoot":"","sources":["../../src/adapters/hold.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters/paths.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface TrierarchPaths {
|
|
|
20
20
|
readonly worktrees: string;
|
|
21
21
|
/** The homes of the ships whose sessions run in containers, one folder per ship (decision 0039). */
|
|
22
22
|
readonly homes: string;
|
|
23
|
+
/** Each session's hold files, in a folder of its own (headless H8). */
|
|
24
|
+
readonly sessions: string;
|
|
23
25
|
/** Each crewed ship's socket, in a folder of its own: its session's fleet calls come over it (decision 0026). */
|
|
24
26
|
readonly sockets: string;
|
|
25
27
|
/** Each crewed ship's read-only checkouts, in a folder of its own (#1182). */
|
package/dist/adapters/paths.js
CHANGED
|
@@ -13,6 +13,7 @@ export function trierarchPaths(at) {
|
|
|
13
13
|
logs: join(home, 'logs'),
|
|
14
14
|
worktrees: join(home, 'worktrees'),
|
|
15
15
|
homes: join(home, 'homes'),
|
|
16
|
+
sessions: join(home, 'sessions'),
|
|
16
17
|
sockets: join(home, 'sockets'),
|
|
17
18
|
checkouts: join(home, 'checkouts'),
|
|
18
19
|
trustedRepositories: join(home, 'trusted-repositories.json'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/adapters/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/adapters/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAqCjC,MAAM,UAAU,cAAc,CAAC,EAA8C;IAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAC5D,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QAC9C,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC;QAC9C,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QACnC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QACrC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QAC/B,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;QACrC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QACnC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;QAClC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;QAC1B,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;QAChC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QAC9B,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;QAClC,mBAAmB,EAAE,IAAI,CAAC,IAAI,EAAE,2BAA2B,CAAC;QAC5D,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,MAAc;IACpD,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -3,6 +3,17 @@ import type { SessionRuntime, WorkspacePort } from '../core/ports.js';
|
|
|
3
3
|
import type { SessionRoots } from './caller-check.js';
|
|
4
4
|
import type { TrustedClones } from './trusted-clones.js';
|
|
5
5
|
import type { TrierarchPaths } from './paths.js';
|
|
6
|
+
/**
|
|
7
|
+
* The sessions on the host (headless H4, H8): one hold per ship, with the
|
|
8
|
+
* sessions an older trierarch started in tmux listed beside them until each
|
|
9
|
+
* is replaced (H19).
|
|
10
|
+
*/
|
|
11
|
+
export declare function hostSessions(at: {
|
|
12
|
+
paths: TrierarchPaths;
|
|
13
|
+
version: string;
|
|
14
|
+
launch?: readonly string[];
|
|
15
|
+
program?: readonly string[];
|
|
16
|
+
}): SessionRuntime & SessionRoots;
|
|
6
17
|
/** What runs the sessions, where their workspaces go, and, in containers, where each ship's home is kept. */
|
|
7
18
|
export interface RuntimeAdapters {
|
|
8
19
|
readonly sessions: SessionRuntime & SessionRoots;
|
|
@@ -15,7 +26,7 @@ export interface RuntimeAdapters {
|
|
|
15
26
|
}
|
|
16
27
|
/**
|
|
17
28
|
* The adapters the configuration's session runtime picks (decision 0039): on
|
|
18
|
-
* the host,
|
|
29
|
+
* the host, a hold per ship and a worktree per ship (headless H4); in containers, a container per ship
|
|
19
30
|
* from the image of the trierarch's own version, with the configured binary
|
|
20
31
|
* and caps and the machine's model token, a clone per ship in place of the
|
|
21
32
|
* worktree, by its real path, and a home of each ship's own.
|
|
@@ -28,4 +39,6 @@ export declare function runtimeAdaptersOf(at: {
|
|
|
28
39
|
managedSettings: string;
|
|
29
40
|
/** The trusted repositories a workspace clones first when the machine does not have them (aeolus-fleet#384). */
|
|
30
41
|
clones?: TrustedClones;
|
|
42
|
+
/** The trierarch program that starts each hold; the one running now unless a test runs another. */
|
|
43
|
+
program?: readonly string[];
|
|
31
44
|
}): RuntimeAdapters;
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { realpath } from 'node:fs/promises';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
2
3
|
import { createCloneWorkspace } from './clone-workspace.js';
|
|
3
4
|
import { createContainerRuntime } from './container.js';
|
|
4
5
|
import { createGitWorkspace } from './git-workspace.js';
|
|
6
|
+
import { createHoldRuntime } from './hold-runtime.js';
|
|
5
7
|
import { createTmux } from './tmux.js';
|
|
6
8
|
/** The session image `release.yml` publishes with each version (decision 0011, 0039). */
|
|
7
9
|
const SESSION_IMAGE = 'ghcr.io/thomashendrickx/aeolus-session';
|
|
10
|
+
/** The trierarch program as it runs now, which starts each hold: `hold` and its words follow. */
|
|
11
|
+
const TRIERARCH_PROGRAM = [process.execPath, fileURLToPath(new URL('../bin/aeolus-trierarch.js', import.meta.url))];
|
|
12
|
+
/**
|
|
13
|
+
* The sessions on the host (headless H4, H8): one hold per ship, with the
|
|
14
|
+
* sessions an older trierarch started in tmux listed beside them until each
|
|
15
|
+
* is replaced (H19).
|
|
16
|
+
*/
|
|
17
|
+
export function hostSessions(at) {
|
|
18
|
+
return createHoldRuntime({ sessions: at.paths.sessions, program: at.program ?? TRIERARCH_PROGRAM, version: at.version, platform: process.platform, launch: at.launch ?? [], legacy: createTmux() });
|
|
19
|
+
}
|
|
8
20
|
/**
|
|
9
21
|
* The adapters the configuration's session runtime picks (decision 0039): on
|
|
10
|
-
* the host,
|
|
22
|
+
* the host, a hold per ship and a worktree per ship (headless H4); in containers, a container per ship
|
|
11
23
|
* from the image of the trierarch's own version, with the configured binary
|
|
12
24
|
* and caps and the machine's model token, a clone per ship in place of the
|
|
13
25
|
* worktree, by its real path, and a home of each ship's own.
|
|
@@ -17,7 +29,7 @@ export function runtimeAdaptersOf(at) {
|
|
|
17
29
|
const root = configuration.worktreeRoot ?? paths.worktrees;
|
|
18
30
|
const { runtime } = configuration;
|
|
19
31
|
if (runtime?.kind !== 'container') {
|
|
20
|
-
return { sessions:
|
|
32
|
+
return { sessions: hostSessions({ paths, version, launch, ...(at.program !== undefined && { program: at.program }) }), workspace: createGitWorkspace({ configuration, root, ...(trusted !== undefined && { clones: trusted }) }) };
|
|
21
33
|
}
|
|
22
34
|
const clones = createCloneWorkspace({ configuration, root, ...(trusted !== undefined && { clones: trusted }) });
|
|
23
35
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-adapters.js","sourceRoot":"","sources":["../../src/adapters/runtime-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-adapters.js","sourceRoot":"","sources":["../../src/adapters/runtime-adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { ShipId } from '@aeolus-fleet/common';
|
|
2
|
-
import type { SessionRuntime } from '../core/ports.js';
|
|
3
1
|
/**
|
|
4
|
-
* Stops a
|
|
2
|
+
* Stops a busy turn in a terminal for a forced message (decision 0040), as a
|
|
5
3
|
* person does with Escape, then waits for the screen to settle: once it holds
|
|
6
4
|
* still between two reads, the harness takes a new prompt, so the line typed
|
|
7
5
|
* next starts the forced turn instead of joining the stopped one.
|
|
8
6
|
*/
|
|
9
|
-
export declare function stopTurn(
|
|
10
|
-
|
|
7
|
+
export declare function stopTurn(session: {
|
|
8
|
+
escape: () => Promise<void>;
|
|
9
|
+
screen: () => Promise<string>;
|
|
10
|
+
}, at?: {
|
|
11
11
|
pollMs?: number;
|
|
12
12
|
mostMs?: number;
|
|
13
13
|
}): Promise<void>;
|
|
@@ -4,19 +4,19 @@ const SETTLE_POLL_MS = 250;
|
|
|
4
4
|
/** The longest the screen may take to settle: a screen that keeps changing never holds the pass longer. */
|
|
5
5
|
const SETTLE_MOST_MS = 5000;
|
|
6
6
|
/**
|
|
7
|
-
* Stops a
|
|
7
|
+
* Stops a busy turn in a terminal for a forced message (decision 0040), as a
|
|
8
8
|
* person does with Escape, then waits for the screen to settle: once it holds
|
|
9
9
|
* still between two reads, the harness takes a new prompt, so the line typed
|
|
10
10
|
* next starts the forced turn instead of joining the stopped one.
|
|
11
11
|
*/
|
|
12
|
-
export async function stopTurn(
|
|
13
|
-
const {
|
|
14
|
-
await
|
|
12
|
+
export async function stopTurn(session, at = {}) {
|
|
13
|
+
const { pollMs = SETTLE_POLL_MS, mostMs = SETTLE_MOST_MS } = at;
|
|
14
|
+
await session.escape();
|
|
15
15
|
const until = Date.now() + mostMs;
|
|
16
|
-
let shown = await
|
|
16
|
+
let shown = await session.screen();
|
|
17
17
|
while (Date.now() < until) {
|
|
18
18
|
await wait(pollMs);
|
|
19
|
-
const now = await
|
|
19
|
+
const now = await session.screen();
|
|
20
20
|
if (now === shown) {
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop-turn.js","sourceRoot":"","sources":["../../src/adapters/stop-turn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,IAAI,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"stop-turn.js","sourceRoot":"","sources":["../../src/adapters/stop-turn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE1D,oEAAoE;AACpE,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,2GAA2G;AAC3G,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAuE,EAAE,KAA2C,EAAE;IACnJ,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,EAAE,CAAC;IAChE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;IAClC,IAAI,KAAK,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IACnC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;QACnC,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,KAAK,GAAG,GAAG,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/adapters/tmux.d.ts
CHANGED
|
@@ -1,36 +1,13 @@
|
|
|
1
1
|
import type { SessionRuntime } from '../core/ports.js';
|
|
2
|
-
import type { SessionRoots } from './caller-check.js';
|
|
3
2
|
export declare const TMUX_SERVER = "aeolus-trierarch";
|
|
4
|
-
/** What the tmux command that may start the server runs through: a systemd scope of its own under a systemd unit, which sets INVOCATION_ID. */
|
|
5
|
-
export declare function sessionsLaunch(at: {
|
|
6
|
-
platform: string;
|
|
7
|
-
env: Readonly<Record<string, string | undefined>>;
|
|
8
|
-
}): readonly string[];
|
|
9
3
|
/**
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* the trierarch stops. Under a systemd unit it starts in a scope of its own
|
|
17
|
-
* (`systemd-run --scope`), so it is not left over in the service's control
|
|
18
|
-
* group on a restart (#537), and the unit's stop leaves it alone (#479).
|
|
19
|
-
*
|
|
20
|
-
* A tmux server takes its global environment from the process that starts
|
|
21
|
-
* it, and every session gets it. Anything else may start the server on the
|
|
22
|
-
* trierarch's socket, such as a ship's session after a `tmux kill-server`
|
|
23
|
-
* (#881), so before each start the server's global environment is set back
|
|
24
|
-
* to the trierarch's own: a variable the trierarch has not is removed, one it
|
|
25
|
-
* has with another value is set to its own (#883).
|
|
26
|
-
*
|
|
27
|
-
* A session's command runs without the `TMUX` and `TMUX_PANE` tmux gives
|
|
28
|
-
* its pane: they name the trierarch's server, so a bare `tmux kill-server` in
|
|
29
|
-
* a session, such as a test script's, would end every ship's session (#880).
|
|
30
|
-
* A bare tmux in a session reaches its user's default server instead.
|
|
4
|
+
* The sessions a trierarch before headless sessions started on the host
|
|
5
|
+
* (headless H4, H19): one tmux session per ship on its own tmux server
|
|
6
|
+
* (`tmux -L aeolus-trierarch`), named after its ship id. The trierarch no
|
|
7
|
+
* longer starts any; it lists them, as of another version, so each is
|
|
8
|
+
* replaced by a hold once its turn is idle, and stops them. Without a server
|
|
9
|
+
* running there are none.
|
|
31
10
|
*/
|
|
32
11
|
export declare function createTmux(options?: {
|
|
33
12
|
server?: string;
|
|
34
|
-
|
|
35
|
-
environment?: Readonly<Record<string, string | undefined>>;
|
|
36
|
-
}): SessionRuntime & SessionRoots;
|
|
13
|
+
}): Pick<SessionRuntime, 'list' | 'stop'>;
|