agl 41.0.0 → 41.0.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.
- package/changelog.md +7 -0
- package/dist_serve/bundle.js +1 -1
- package/dist_serve/bundle.js.map +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.controller.js +2 -2
- package/dist_ts/classes.mcpreexec.js +4 -4
- package/dist_ts/classes.workercallers.d.ts +2 -0
- package/dist_ts/classes.workercallers.js +11 -8
- package/dist_ts/functions.workerprocessproof.d.ts +12 -3
- package/dist_ts/functions.workerprocessproof.js +17 -6
- package/package.json +1 -1
- package/readme.md +8 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.controller.ts +1 -1
- package/ts/classes.mcpreexec.ts +3 -3
- package/ts/classes.workercallers.ts +12 -7
- package/ts/functions.workerprocessproof.ts +17 -3
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -75,6 +75,8 @@ type TReproof = { kind: 'rebound' } | { kind: 'refused'; reason: string } | { ki
|
|
|
75
75
|
|
|
76
76
|
export interface IControllerWorkerCallerOptions {
|
|
77
77
|
controllerGeneration: string;
|
|
78
|
+
/** The port this controller is configured to serve: a bridge an upgrade re-executed in place names it (`aglMcpReexecArguments`). */
|
|
79
|
+
controllerPort: number;
|
|
78
80
|
registry: ControllerMcpCallerRegistry;
|
|
79
81
|
readMaster(): Promise<IConfigurationMaster>;
|
|
80
82
|
captureCodexOrigin(launch: Extract<TFleetWorkerCallerLaunch, { harnessId: 'codex' }>): Promise<IWorkerCodexCallerOrigin>;
|
|
@@ -103,8 +105,10 @@ const pending = (): IWorkerCallerEnrollmentResponse => ({ callerPending: true, b
|
|
|
103
105
|
const transientRefusalMs = 5_000;
|
|
104
106
|
const maximumRefusals = 64;
|
|
105
107
|
const maximumLoggedReasons = 4096;
|
|
108
|
+
/** A message as a clause of a pending reason, without its final period: the log line ends the sentence. */
|
|
109
|
+
const clause = (messageArg: string): string => messageArg.replace(/\.+$/u, '');
|
|
106
110
|
const proofReason = (errorArg: unknown): string => errorArg instanceof WorkerCallerProofError
|
|
107
|
-
? errorArg.message : `a process could not be read (${(errorArg as NodeJS.ErrnoException).code ?? 'unknown'})`;
|
|
111
|
+
? clause(errorArg.message) : `a process could not be read (${(errorArg as NodeJS.ErrnoException).code ?? 'unknown'})`;
|
|
108
112
|
const assertIdentifier = (valueArg: string): void => {
|
|
109
113
|
if (typeof valueArg !== 'string' || !/^[A-Za-z0-9_.:-]{1,256}$/u.test(valueArg)) throw new Error('Invalid worker launch identifier.');
|
|
110
114
|
};
|
|
@@ -377,19 +381,20 @@ export class ControllerWorkerCallers implements IFleetWorkerCallerHooks {
|
|
|
377
381
|
private async reprove(recordArg: IWorkerLaunch, bridgePidArg: number, connectionArg?: IWorkerCallerConnection): Promise<TReproof> {
|
|
378
382
|
const observe = this.options.observeClaudeJob;
|
|
379
383
|
if (!observe) return { kind: 'refused', reason: 'the process its launch is bound to is gone, and no fleet proves the job\'s next process' };
|
|
384
|
+
const port = this.options.controllerPort;
|
|
380
385
|
let bridge: IWorkerProcessSnapshot, command = recordArg.command!;
|
|
381
|
-
try { bridge = await this.proof.readBridge(connectionArg, bridgePidArg, command); } catch (errorArg) {
|
|
386
|
+
try { bridge = await this.proof.readBridge(connectionArg, bridgePidArg, command, port); } catch (errorArg) {
|
|
382
387
|
if (!isWorkerCallerProofRefusal(errorArg)) throw errorArg;
|
|
383
388
|
// An upgrade re-executes a bridge in place onto the build the controller runs now: it runs the command this
|
|
384
389
|
// controller resolves from its own configured `agl` entry, never one the bridge names.
|
|
385
390
|
let current: IWorkerBridgeCommand;
|
|
386
391
|
try { current = await this.currentCommand(); } catch (resolveArg) {
|
|
387
|
-
return { kind: 'refused', reason: `${proofReason(errorArg)} (the approved bridge command cannot be resolved now: ${resolveArg instanceof Error ? resolveArg.message : 'unknown failure'})` };
|
|
392
|
+
return { kind: 'refused', reason: `${proofReason(errorArg)} (the approved bridge command cannot be resolved now: ${resolveArg instanceof Error ? clause(resolveArg.message) : 'unknown failure'})` };
|
|
388
393
|
}
|
|
389
394
|
if (plugins.util.isDeepStrictEqual(current, command)) return { kind: 'refused', reason: proofReason(errorArg) };
|
|
390
|
-
try { bridge = await this.proof.readBridge(connectionArg, bridgePidArg, current); } catch (currentArg) {
|
|
395
|
+
try { bridge = await this.proof.readBridge(connectionArg, bridgePidArg, current, port); } catch (currentArg) {
|
|
391
396
|
if (!isWorkerCallerProofRefusal(currentArg)) throw currentArg;
|
|
392
|
-
return { kind: 'refused', reason: proofReason(errorArg) };
|
|
397
|
+
return { kind: 'refused', reason: `recorded bridge command: ${proofReason(errorArg)}; current bridge command: ${proofReason(currentArg)}` };
|
|
393
398
|
}
|
|
394
399
|
command = current;
|
|
395
400
|
}
|
|
@@ -433,7 +438,7 @@ export class ControllerWorkerCallers implements IFleetWorkerCallerHooks {
|
|
|
433
438
|
if (!isWorkerCallerProofRefusal(errorArg)) throw errorArg;
|
|
434
439
|
return refuse('the process of its Claude job is not the approved Claude Code executable', false);
|
|
435
440
|
}
|
|
436
|
-
try { await this.proof.verifyBridge(connectionArg, bridge.pid, command, parent); } catch (errorArg) {
|
|
441
|
+
try { await this.proof.verifyBridge(connectionArg, bridge.pid, command, parent, port); } catch (errorArg) {
|
|
437
442
|
if (!isWorkerCallerProofRefusal(errorArg)) throw errorArg;
|
|
438
443
|
return refuse('its bridge is not proved to be a direct child of the process of its Claude job', true);
|
|
439
444
|
}
|
|
@@ -678,7 +683,7 @@ export class ControllerWorkerCallers implements IFleetWorkerCallerHooks {
|
|
|
678
683
|
private async safeSnapshot(recordArg: IWorkerLaunch, bridgePidArg: number, connectionArg?: IWorkerCallerConnection): Promise<IWorkerProcessSnapshot> {
|
|
679
684
|
if (recordArg.state !== 'bound' || !recordArg.command || !recordArg.parent || this.closed
|
|
680
685
|
|| (recordArg.codex && !await recordArg.codex.isCurrent())) throw new WorkerCallerProofError('Worker caller proof is unavailable.');
|
|
681
|
-
return this.proof.verifyBridge(connectionArg, bridgePidArg, recordArg.command, recordArg.parent);
|
|
686
|
+
return this.proof.verifyBridge(connectionArg, bridgePidArg, recordArg.command, recordArg.parent, this.options.controllerPort);
|
|
682
687
|
}
|
|
683
688
|
|
|
684
689
|
private async prove(recordArg: IWorkerLaunch, bridgePidArg: number, connectionArg?: IWorkerCallerConnection,
|
|
@@ -142,15 +142,26 @@ const processOwnsLoopbackConnection = async (connectionArg: IWorkerCallerConnect
|
|
|
142
142
|
return matches.length === 1 && await processOwnsInode(pidArg, matches[0][9]);
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* The arguments after its `cli.js` entry that an upgrade re-executes an `agl mcp` bridge of the controller on
|
|
147
|
+
* `controllerPortArg` with (`AglMcpReexecCoordinator`): the port pins the next image to its handover record's.
|
|
148
|
+
* A bridge proof accepts exactly these besides the approved command's own.
|
|
149
|
+
*/
|
|
150
|
+
export const aglMcpReexecArguments = (controllerPortArg: number): string[] => ['mcp', '--port', String(controllerPortArg)];
|
|
151
|
+
|
|
145
152
|
/**
|
|
146
153
|
* The bridge process `bridgePidArg`, proved to hold the caller's end of the direct loopback connection and to run the
|
|
147
154
|
* approved command, whatever its parent. The command is the one recorded when the launch was prepared: an upgrade
|
|
148
|
-
* installs AGL under another path, and a command whose files are gone is refused as such.
|
|
155
|
+
* installs AGL under another path, and a command whose files are gone is refused as such. Its arguments are the
|
|
156
|
+
* approved ones or, once an upgrade re-executed the bridge in place, `aglMcpReexecArguments` for `controllerPortArg`,
|
|
157
|
+
* the port this controller is configured with, never one the bridge names: the connection proof already shows that
|
|
158
|
+
* the bridge talks to this controller.
|
|
149
159
|
*/
|
|
150
160
|
export const readWorkerBridge = async (
|
|
151
161
|
connectionArg: IWorkerCallerConnection | undefined,
|
|
152
162
|
bridgePidArg: number,
|
|
153
163
|
commandArg: IWorkerBridgeCommand,
|
|
164
|
+
controllerPortArg: number,
|
|
154
165
|
): Promise<IWorkerProcessSnapshot> => {
|
|
155
166
|
for (const file of [commandArg.executable, commandArg.entry]) {
|
|
156
167
|
let resolved: string;
|
|
@@ -165,8 +176,10 @@ export const readWorkerBridge = async (
|
|
|
165
176
|
}
|
|
166
177
|
const bridge = await readWorkerProcessSnapshot(bridgePidArg);
|
|
167
178
|
const entry = await plugins.fs.promises.realpath(bridge.arguments[1] ?? '');
|
|
179
|
+
const argumentsTail = bridge.arguments.slice(2);
|
|
168
180
|
if (bridge.executable !== commandArg.executable || entry !== commandArg.entry
|
|
169
|
-
|| !plugins.util.isDeepStrictEqual(
|
|
181
|
+
|| (!plugins.util.isDeepStrictEqual(argumentsTail, commandArg.arguments)
|
|
182
|
+
&& !plugins.util.isDeepStrictEqual(argumentsTail, aglMcpReexecArguments(controllerPortArg)))) {
|
|
170
183
|
throw new WorkerCallerProofError('Worker bridge does not match the approved launch.');
|
|
171
184
|
}
|
|
172
185
|
return bridge;
|
|
@@ -177,8 +190,9 @@ export const verifyWorkerBridge = async (
|
|
|
177
190
|
bridgePidArg: number,
|
|
178
191
|
commandArg: IWorkerBridgeCommand,
|
|
179
192
|
parentArg: IWorkerProcessSnapshot,
|
|
193
|
+
controllerPortArg: number,
|
|
180
194
|
): Promise<IWorkerProcessSnapshot> => {
|
|
181
|
-
const bridge = await readWorkerBridge(connectionArg, bridgePidArg, commandArg);
|
|
195
|
+
const bridge = await readWorkerBridge(connectionArg, bridgePidArg, commandArg, controllerPortArg);
|
|
182
196
|
if (bridge.parentPid !== parentArg.pid) throw new WorkerCallerProofError('Worker bridge does not match the approved launch.');
|
|
183
197
|
if (!workerSnapshotsEqual(parentArg, await readWorkerProcessSnapshot(parentArg.pid))
|
|
184
198
|
|| !workerSnapshotsEqual(bridge, await readWorkerProcessSnapshot(bridge.pid))) {
|