@aeolus-fleet/trierarch 0.18.0 → 0.19.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/LICENSE +201 -0
- package/README.md +11 -11
- package/dist/adapters/claude-code.d.ts +3 -3
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex.d.ts +3 -3
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/flags.d.ts +2 -2
- package/dist/adapters/flags.js +2 -2
- package/dist/adapters/git-workspace.js +1 -1
- package/dist/adapters/harnesses.d.ts +4 -4
- package/dist/adapters/harnesses.js +2 -2
- package/dist/adapters/harnesses.js.map +1 -1
- package/dist/adapters/json-state.d.ts +6 -4
- package/dist/adapters/json-state.js +21 -21
- package/dist/adapters/json-state.js.map +1 -1
- package/dist/adapters/rest-fleet.d.ts +4 -3
- package/dist/adapters/rest-fleet.js +20 -13
- package/dist/adapters/rest-fleet.js.map +1 -1
- package/dist/adapters/service.js +1 -1
- package/dist/adapters/style.d.ts +3 -3
- package/dist/adapters/style.js +1 -2
- package/dist/adapters/style.js.map +1 -1
- package/dist/cli/config-check.d.ts +3 -3
- package/dist/cli/config-check.js +1 -1
- package/dist/cli/config-check.js.map +1 -1
- package/dist/cli/init.d.ts +2 -2
- package/dist/cli/init.js +1 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/list.d.ts +4 -3
- package/dist/cli/list.js +1 -1
- package/dist/cli/list.js.map +1 -1
- package/dist/cli/main.js +2 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.d.ts +12 -7
- package/dist/cli/run.js +44 -24
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/status.d.ts +3 -2
- package/dist/cli/status.js +5 -4
- package/dist/cli/status.js.map +1 -1
- package/dist/core/check-settings.d.ts +25 -0
- package/dist/core/check-settings.js +52 -0
- package/dist/core/check-settings.js.map +1 -0
- package/dist/core/entry.d.ts +18 -30
- package/dist/core/entry.js +3 -1
- package/dist/core/entry.js.map +1 -1
- package/dist/core/handle-delivery.d.ts +9 -14
- package/dist/core/handle-delivery.js +4 -133
- package/dist/core/handle-delivery.js.map +1 -1
- package/dist/core/options-schema.d.ts +7 -0
- package/dist/core/options-schema.js +10 -0
- package/dist/core/options-schema.js.map +1 -0
- package/dist/core/ports.d.ts +48 -8
- package/dist/core/reconciler.d.ts +32 -12
- package/dist/core/reconciler.js +107 -23
- package/dist/core/reconciler.js.map +1 -1
- package/dist/core/report-self.d.ts +16 -0
- package/dist/core/report-self.js +41 -0
- package/dist/core/report-self.js.map +1 -0
- package/dist/core/run-pass.d.ts +8 -7
- package/dist/core/run-pass.js +113 -69
- package/dist/core/run-pass.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/dist/core/answers.d.ts +0 -4
- package/dist/core/answers.js +0 -32
- package/dist/core/answers.js.map +0 -1
- package/dist/core/check-want.d.ts +0 -32
- package/dist/core/check-want.js +0 -80
- package/dist/core/check-want.js.map +0 -1
package/dist/cli/status.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ENTRY_STATES } from '../core/entry.js';
|
|
2
3
|
import { FleetRefusal } from '../adapters/rest-fleet.js';
|
|
3
4
|
import { PLAIN, stateTone } from '../adapters/style.js';
|
|
4
5
|
export function leaseFrom(whoami) {
|
|
@@ -19,7 +20,7 @@ export async function inspectStatus(at) {
|
|
|
19
20
|
const [service, lease, sessions, state, running] = await Promise.all([at.service.status(), at.lease(), at.processes.list(), at.state.load(), at.running()]);
|
|
20
21
|
// Only the service's own process counts: a file left by an earlier process, or by a `run` outside the service, says nothing of it.
|
|
21
22
|
const runningVersion = service.isRunning && running !== undefined && running.pid === service.pid ? running.version : undefined;
|
|
22
|
-
const entries = {
|
|
23
|
+
const entries = { crewing: 0, running: 0, restarting: 0, crashed: 0, releasing: 0 };
|
|
23
24
|
for (const entry of Object.values(state.entries)) {
|
|
24
25
|
entries[entry.state] += 1;
|
|
25
26
|
}
|
|
@@ -76,7 +77,7 @@ export function describeStatus(report, style = PLAIN) {
|
|
|
76
77
|
line('Version', describeVersion(report, style)),
|
|
77
78
|
line('Service', describeService(report.service, style)),
|
|
78
79
|
line('Fleet', describeFleet(report.fleet, style)),
|
|
79
|
-
line('Caps', `${String(report.caps.ships.used)} of ${String(report.caps.ships.cap)} ships
|
|
80
|
+
line('Caps', `${String(report.caps.ships.used)} of ${String(report.caps.ships.cap)} ships crewed here, ${String(report.caps.running.used)} of ${String(report.caps.running.cap)} sessions running`),
|
|
80
81
|
line('Entries', counted.length === 0 ? 'none' : counted.map(([state, count]) => style.tone(stateToneOf(state), `${state} ${String(count)}`)).join(', ')),
|
|
81
82
|
line('Kept worktrees', report.kept.length === 0 ? 'none' : style.tone('busy', report.kept.map((kept) => `${kept.path} (${kept.shipId})`).join(', '))),
|
|
82
83
|
line('Orphans', report.orphans.length === 0 ? 'none' : style.tone('busy', report.orphans.join(', '))),
|
|
@@ -84,7 +85,7 @@ export function describeStatus(report, style = PLAIN) {
|
|
|
84
85
|
}
|
|
85
86
|
/** The tone of a state counted under entries, as its name is a key of the report. */
|
|
86
87
|
function stateToneOf(state) {
|
|
87
|
-
const parsed =
|
|
88
|
+
const parsed = z.enum(ENTRY_STATES).safeParse(state);
|
|
88
89
|
return parsed.success ? stateTone(parsed.data) : 'quiet';
|
|
89
90
|
}
|
|
90
91
|
//# sourceMappingURL=status.js.map
|
package/dist/cli/status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type CrewSettings, type ShipId, type TrierarchConfiguration } from '@aeolus-fleet/common';
|
|
2
|
+
import type { TrierarchState } from './entry.js';
|
|
3
|
+
import { type Result } from './shared/result.js';
|
|
4
|
+
/** Why the trierarch cannot crew settings: the field at fault, when one is, and a reason argo can read. */
|
|
5
|
+
export interface Refusal {
|
|
6
|
+
readonly field?: string;
|
|
7
|
+
readonly reason: string;
|
|
8
|
+
}
|
|
9
|
+
/** Settings this trierarch can crew: parsed, with each option resolved to its value name. */
|
|
10
|
+
export interface CheckedSettings {
|
|
11
|
+
readonly settings: CrewSettings;
|
|
12
|
+
readonly options: Readonly<Record<string, string>>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks a crew request's settings again before crewing (decision 0027):
|
|
16
|
+
* the crew settings schema, then what this trierarch's configuration offers
|
|
17
|
+
* (harness, repository or folder, each option and its value), and one folder
|
|
18
|
+
* per ship. The trierarch plugin checked them already; this machine's
|
|
19
|
+
* configuration may have changed since.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkSettings(raw: unknown, context: {
|
|
22
|
+
shipId: ShipId;
|
|
23
|
+
configuration: TrierarchConfiguration;
|
|
24
|
+
state: TrierarchState;
|
|
25
|
+
}): Result<CheckedSettings, Refusal>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { crewSettingsSchema } from '@aeolus-fleet/common';
|
|
2
|
+
import { err, ok } from './shared/result.js';
|
|
3
|
+
/** The field an issue names, as `options.model`, when it names one. */
|
|
4
|
+
function fieldOf(path) {
|
|
5
|
+
return path.length === 0 ? {} : { field: path.map(String).join('.') };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Checks a crew request's settings again before crewing (decision 0027):
|
|
9
|
+
* the crew settings schema, then what this trierarch's configuration offers
|
|
10
|
+
* (harness, repository or folder, each option and its value), and one folder
|
|
11
|
+
* per ship. The trierarch plugin checked them already; this machine's
|
|
12
|
+
* configuration may have changed since.
|
|
13
|
+
*/
|
|
14
|
+
export function checkSettings(raw, context) {
|
|
15
|
+
const parsed = crewSettingsSchema.safeParse(raw);
|
|
16
|
+
if (!parsed.success) {
|
|
17
|
+
const [issue] = parsed.error.issues;
|
|
18
|
+
return err({ ...fieldOf(issue?.path ?? []), reason: issue?.message ?? 'The settings do not parse' });
|
|
19
|
+
}
|
|
20
|
+
const settings = parsed.data;
|
|
21
|
+
const { configuration, state, shipId } = context;
|
|
22
|
+
const harness = configuration.harnesses[settings.harness];
|
|
23
|
+
if (harness === undefined) {
|
|
24
|
+
return err({ field: 'harness', reason: `This trierarch offers no harness ${settings.harness}` });
|
|
25
|
+
}
|
|
26
|
+
const { workspace } = settings;
|
|
27
|
+
if (workspace.kind === 'worktree' && !(workspace.repository in configuration.repositories)) {
|
|
28
|
+
return err({ field: 'workspace.repository', reason: `This trierarch offers no repository ${workspace.repository}` });
|
|
29
|
+
}
|
|
30
|
+
if (workspace.kind === 'folder') {
|
|
31
|
+
if (!(workspace.name in configuration.folders)) {
|
|
32
|
+
return err({ field: 'workspace.name', reason: `This trierarch offers no folder ${workspace.name}` });
|
|
33
|
+
}
|
|
34
|
+
const crewing = Object.values(state.entries).find((entry) => entry.shipId !== shipId && entry.workspace.kind === 'folder' && entry.workspace.name === workspace.name);
|
|
35
|
+
if (crewing !== undefined) {
|
|
36
|
+
return err({ field: 'workspace.name', reason: `The folder ${workspace.name} crews ${crewing.shipId} already: one folder crews one ship` });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const options = {};
|
|
40
|
+
for (const [name, value] of Object.entries(settings.options)) {
|
|
41
|
+
const option = harness.options[name];
|
|
42
|
+
if (option === undefined) {
|
|
43
|
+
return err({ field: `options.${name}`, reason: `${settings.harness} has no option ${name}` });
|
|
44
|
+
}
|
|
45
|
+
if (typeof value !== 'string' || !(value in option.values)) {
|
|
46
|
+
return err({ field: `options.${name}`, reason: `${name} must be one of ${Object.keys(option.values).join(', ')}` });
|
|
47
|
+
}
|
|
48
|
+
options[name] = value;
|
|
49
|
+
}
|
|
50
|
+
return ok({ settings, options });
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=check-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-settings.js","sourceRoot":"","sources":["../../src/core/check-settings.ts"],"names":[],"mappings":"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"}
|
package/dist/core/entry.d.ts
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
import type { ShipId,
|
|
1
|
+
import type { ShipId, TrierarchWorkspace } from '@aeolus-fleet/common';
|
|
2
2
|
/**
|
|
3
|
-
* The trierarch's saved state (docs/trierarch.md):
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* secret: a session's crew token lives only in its folder's
|
|
3
|
+
* The trierarch's saved state (docs/trierarch.md): an entry per ship it
|
|
4
|
+
* crews, by ship id, the worktrees it kept, the orphans it found, and the
|
|
5
|
+
* settings versions it told argo it cannot crew. Plain data, saved whole as
|
|
6
|
+
* JSON, never a secret: a session's crew token lives only in its folder's
|
|
7
|
+
* identity file. The crew requests themselves live in the fleet.
|
|
7
8
|
*/
|
|
8
9
|
export interface TrierarchState {
|
|
9
10
|
readonly entries: Readonly<Record<string, Entry>>;
|
|
10
|
-
/** Each applied message's answers, by message id, so a message applied before changes nothing. */
|
|
11
|
-
readonly applied: Readonly<Record<string, readonly Outgoing[]>>;
|
|
12
11
|
readonly kept: readonly KeptWorktree[];
|
|
13
12
|
readonly orphans: readonly string[];
|
|
13
|
+
/** By ship id: the settings version argo was told this trierarch cannot crew, so it is told once. */
|
|
14
|
+
readonly refused: Readonly<Record<string, number>>;
|
|
14
15
|
}
|
|
15
|
-
/**
|
|
16
|
+
/** Where an entry stands: the crew statuses the trierarch writes, and releasing while it ends. */
|
|
17
|
+
export declare const ENTRY_STATES: readonly ["crewing", "running", "restarting", "crashed", "releasing"];
|
|
18
|
+
export type EntryState = (typeof ENTRY_STATES)[number];
|
|
19
|
+
/** One ship the trierarch crews, from the crew request assigned to it, with the settings of the version it crewed. */
|
|
16
20
|
export interface Entry {
|
|
17
21
|
readonly shipId: ShipId;
|
|
18
22
|
/** The ship's name, from the fleet when it is crewed: its session is named after it. */
|
|
19
23
|
readonly shipName?: string;
|
|
24
|
+
/** The settings version this entry crews with: a new one crews the ship again. */
|
|
25
|
+
readonly settingsVersion: number;
|
|
20
26
|
readonly harness: string;
|
|
21
27
|
readonly workspace: TrierarchWorkspace;
|
|
22
28
|
readonly squadron?: string;
|
|
23
29
|
/** Given on the first start only. */
|
|
24
30
|
readonly firstPrompt?: string;
|
|
25
|
-
/** Option name to value name, as the
|
|
31
|
+
/** Option name to value name, as the settings picked them. */
|
|
26
32
|
readonly options: Readonly<Record<string, string>>;
|
|
27
|
-
|
|
28
|
-
readonly requester: ShipId;
|
|
29
|
-
readonly state: TrierarchEntryState;
|
|
33
|
+
readonly state: EntryState;
|
|
30
34
|
/** When the entry took its state, ISO 8601. */
|
|
31
35
|
readonly since: string;
|
|
32
36
|
/** When its session exited within the restart window, oldest first, ISO 8601. */
|
|
@@ -36,37 +40,21 @@ export interface Entry {
|
|
|
36
40
|
/** Where its workspace is, once made. */
|
|
37
41
|
readonly folder?: string;
|
|
38
42
|
readonly hasStarted: boolean;
|
|
39
|
-
/** The release a message asked for, answered once the ship is released. */
|
|
40
|
-
readonly release?: {
|
|
41
|
-
readonly messageId: string;
|
|
42
|
-
readonly sender: ShipId;
|
|
43
|
-
readonly isForced: boolean;
|
|
44
|
-
};
|
|
45
43
|
/** The inbox count last seen, and whether a wake waits for the session to turn idle. */
|
|
46
44
|
readonly wake: {
|
|
47
45
|
readonly waiting: number;
|
|
48
46
|
readonly isPending: boolean;
|
|
49
47
|
};
|
|
50
48
|
}
|
|
51
|
-
/** A worktree kept because it had changes, until a human
|
|
49
|
+
/** A worktree kept because it had changes, until a human clears it. */
|
|
52
50
|
export interface KeptWorktree {
|
|
53
51
|
readonly shipId: ShipId;
|
|
54
52
|
readonly path: string;
|
|
55
53
|
}
|
|
56
|
-
/** A message the trierarch sends: an answer in reply to a command, or a notice. */
|
|
57
|
-
export interface Outgoing {
|
|
58
|
-
readonly to: ShipId;
|
|
59
|
-
readonly inReplyTo?: string;
|
|
60
|
-
/** The protocol name, such as `wanted`: its content type is `trierarchContentType(name)`. */
|
|
61
|
-
readonly name: string;
|
|
62
|
-
readonly payload: Readonly<Record<string, unknown>>;
|
|
63
|
-
/** The same each time this message is sent again, so the fleet stores it once. */
|
|
64
|
-
readonly idempotencyKey: string;
|
|
65
|
-
}
|
|
66
54
|
export declare const EMPTY_STATE: TrierarchState;
|
|
67
55
|
/** The entry in a new state since now. */
|
|
68
56
|
export declare function withState(entry: Entry, change: {
|
|
69
|
-
state:
|
|
57
|
+
state: EntryState;
|
|
70
58
|
now: Date;
|
|
71
59
|
}): Entry;
|
|
72
60
|
/** The state with one entry put in, or replaced. */
|
package/dist/core/entry.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** Where an entry stands: the crew statuses the trierarch writes, and releasing while it ends. */
|
|
2
|
+
export const ENTRY_STATES = ['crewing', 'running', 'restarting', 'crashed', 'releasing'];
|
|
3
|
+
export const EMPTY_STATE = { entries: {}, kept: [], orphans: [], refused: {} };
|
|
2
4
|
/** The entry in a new state since now. */
|
|
3
5
|
export function withState(entry, change) {
|
|
4
6
|
return { ...entry, state: change.state, since: change.now.toISOString() };
|
package/dist/core/entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/core/entry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/core/entry.ts"],"names":[],"mappings":"AAiBA,kGAAkG;AAClG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,CAAU,CAAC;AAsClG,MAAM,CAAC,MAAM,WAAW,GAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAE/F,0CAA0C;AAC1C,MAAM,UAAU,SAAS,CAAC,KAAY,EAAE,MAAwC;IAC9E,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;AAC5E,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,QAAQ,CAAC,KAAqB,EAAE,KAAY;IAC1D,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;AAC5E,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,WAAW,CAAC,KAAqB,EAAE,MAAc;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC;IAClG,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import type { Delivery, FleetPort, Logger
|
|
2
|
-
import type { Clock } from './shared/clock.js';
|
|
1
|
+
import type { Delivery, FleetPort, Logger } from './ports.js';
|
|
3
2
|
/**
|
|
4
|
-
* Use case: a
|
|
5
|
-
*
|
|
6
|
-
* the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* Use case: a message arrives at the trierarch's own ship. Its ship stays a
|
|
4
|
+
* normal ship (docs/trierarch.md): a ping gets pong. A trierarch takes work
|
|
5
|
+
* only through the crew requests assigned to it, never from a message, so
|
|
6
|
+
* any other delivery is acknowledged, logged as not handled, and acted on
|
|
7
|
+
* never: no ship can start or stop a session on the machine by messaging it
|
|
8
|
+
* (#301).
|
|
9
9
|
*/
|
|
10
10
|
export type HandleDelivery = (delivery: Delivery) => Promise<void>;
|
|
11
|
-
export
|
|
11
|
+
export declare function createHandleDelivery(deps: {
|
|
12
12
|
fleet: FleetPort;
|
|
13
|
-
workspace: WorkspacePort;
|
|
14
|
-
state: StatePort;
|
|
15
|
-
setup: TrierarchSetup;
|
|
16
|
-
clock: Clock;
|
|
17
13
|
logger: Logger;
|
|
18
|
-
}
|
|
19
|
-
export declare function createHandleDelivery(deps: HandleDeliveryDeps): HandleDelivery;
|
|
14
|
+
}): HandleDelivery;
|
|
@@ -1,141 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { describedOf, listedOf } from './answers.js';
|
|
4
|
-
import { checkWant, fieldOf, newEntry } from './check-want.js';
|
|
5
|
-
import { putEntry, removeEntry, withState } from './entry.js';
|
|
6
|
-
const CONTENT_TYPE = /^application\/vnd\.aeolus\.trierarch\.(\w+)\+json$/;
|
|
7
|
-
const shipIdOnly = z.looseObject({ shipId: idSchema('ship') });
|
|
1
|
+
import { isPingContentType } from '@aeolus-fleet/common';
|
|
8
2
|
export function createHandleDelivery(deps) {
|
|
9
3
|
return async (delivery) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (before !== undefined) {
|
|
13
|
-
await deps.fleet.ack(delivery.deliveryId);
|
|
14
|
-
await Promise.all(before.map((answer) => deps.fleet.send(answer)));
|
|
4
|
+
if (isPingContentType(delivery.contentType)) {
|
|
5
|
+
await deps.fleet.pong(delivery.deliveryId);
|
|
15
6
|
return;
|
|
16
7
|
}
|
|
17
|
-
const { state: next, answers } = await apply(state, { delivery, deps });
|
|
18
|
-
await deps.state.save({ ...next, applied: { ...next.applied, [delivery.messageId]: answers } });
|
|
19
8
|
await deps.fleet.ack(delivery.deliveryId);
|
|
20
|
-
|
|
21
|
-
await deps.fleet.send(answer);
|
|
22
|
-
}
|
|
9
|
+
deps.logger.warn(`Not handled: ${delivery.contentType} from ${delivery.senderShipId}. A trierarch takes work only through the crew requests assigned to it.`);
|
|
23
10
|
};
|
|
24
11
|
}
|
|
25
|
-
async function apply(state, context) {
|
|
26
|
-
const { delivery, deps } = context;
|
|
27
|
-
const name = CONTENT_TYPE.exec(delivery.contentType)?.[1] ?? '';
|
|
28
|
-
const payload = parsedJson(delivery.payload);
|
|
29
|
-
const answer = answerTo(delivery);
|
|
30
|
-
switch (name) {
|
|
31
|
-
case 'describe':
|
|
32
|
-
return { state, answers: [answer('described', describedOf(deps.setup, state))] };
|
|
33
|
-
case 'list':
|
|
34
|
-
return { state, answers: [answer('listed', listedOf(state))] };
|
|
35
|
-
case 'want':
|
|
36
|
-
return want(state, { payload, context });
|
|
37
|
-
case 'release':
|
|
38
|
-
return release(state, { payload, context });
|
|
39
|
-
default:
|
|
40
|
-
deps.logger.warn(`A message the trierarch does not read: ${delivery.contentType}, from ${delivery.senderShipId}`);
|
|
41
|
-
return { state, answers: [] };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/** The payload as JSON, or undefined when it is none: the schemas then refuse it. */
|
|
45
|
-
function parsedJson(payload) {
|
|
46
|
-
try {
|
|
47
|
-
const parsed = JSON.parse(payload);
|
|
48
|
-
return parsed;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function answerTo(delivery) {
|
|
55
|
-
return (name, payload) => ({
|
|
56
|
-
to: delivery.senderShipId,
|
|
57
|
-
inReplyTo: delivery.messageId,
|
|
58
|
-
name,
|
|
59
|
-
payload,
|
|
60
|
-
idempotencyKey: `trierarch:${delivery.messageId}:${name}`,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
function refused(context, refusal) {
|
|
64
|
-
return answerTo(context.delivery)('refused', {
|
|
65
|
-
shipId: refusal.shipId,
|
|
66
|
-
...(refusal.field !== undefined && { field: refusal.field }),
|
|
67
|
-
reason: refusal.reason,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
/** A command whose ship id does not parse cannot be answered: refused names a ship. */
|
|
71
|
-
function unanswerable(state, context) {
|
|
72
|
-
context.deps.logger.warn(`A command with no ship id the trierarch can answer, from ${context.delivery.senderShipId}`);
|
|
73
|
-
return { state, answers: [] };
|
|
74
|
-
}
|
|
75
|
-
async function want(state, at) {
|
|
76
|
-
const { payload, context } = at;
|
|
77
|
-
const { deps, delivery } = context;
|
|
78
|
-
const target = shipIdOnly.safeParse(payload);
|
|
79
|
-
if (!target.success) {
|
|
80
|
-
return unanswerable(state, context);
|
|
81
|
-
}
|
|
82
|
-
const { shipId } = target.data;
|
|
83
|
-
const existing = state.entries[shipId];
|
|
84
|
-
if (existing !== undefined && existing.state !== 'crashed') {
|
|
85
|
-
return { state, answers: [refused(context, { shipId, field: 'shipId', reason: `${shipId} is on the wanted list already` })] };
|
|
86
|
-
}
|
|
87
|
-
const checked = checkWant(payload, { configuration: deps.setup.configuration, state: removeEntry(state, shipId) });
|
|
88
|
-
if (!checked.isOk) {
|
|
89
|
-
return { state, answers: [refused(context, { shipId, ...checked.error })] };
|
|
90
|
-
}
|
|
91
|
-
const now = deps.clock.now();
|
|
92
|
-
const entry = newEntry(checked.value, { requester: delivery.senderShipId, now });
|
|
93
|
-
const wanted = answerTo(delivery)('wanted', { shipId });
|
|
94
|
-
if (existing !== undefined) {
|
|
95
|
-
// Wanted again after it crashed: its session starts again in its folder, with its lease still held.
|
|
96
|
-
const again = { ...entry, state: 'running', hasStarted: existing.hasStarted, ...(existing.folder !== undefined && { folder: existing.folder }) };
|
|
97
|
-
return { state: putEntry(state, again), answers: [wanted] };
|
|
98
|
-
}
|
|
99
|
-
const ship = await deps.fleet.ship(shipId);
|
|
100
|
-
switch (ship.kind) {
|
|
101
|
-
case 'notFound':
|
|
102
|
-
return { state, answers: [refused(context, { shipId, field: 'shipId', reason: `The fleet has no ship ${shipId}` })] };
|
|
103
|
-
case 'retired':
|
|
104
|
-
return { state, answers: [refused(context, { shipId, field: 'shipId', reason: `${shipId} is retired` })] };
|
|
105
|
-
case 'crewed':
|
|
106
|
-
return { state, answers: [refused(context, { shipId, field: 'shipId', reason: `${ship.name} is crewed by another session` })] };
|
|
107
|
-
case 'awaitingCrew':
|
|
108
|
-
return { state: putEntry(state, entry), answers: [wanted] };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async function release(state, at) {
|
|
112
|
-
const { payload, context } = at;
|
|
113
|
-
const { deps, delivery } = context;
|
|
114
|
-
const parsed = releaseCommandSchema.safeParse(payload);
|
|
115
|
-
if (!parsed.success) {
|
|
116
|
-
const target = shipIdOnly.safeParse(payload);
|
|
117
|
-
if (!target.success) {
|
|
118
|
-
return unanswerable(state, context);
|
|
119
|
-
}
|
|
120
|
-
const [issue] = parsed.error.issues;
|
|
121
|
-
return { state, answers: [refused(context, { shipId: target.data.shipId, ...fieldOf(issue?.path), reason: issue?.message ?? 'The release does not parse' })] };
|
|
122
|
-
}
|
|
123
|
-
const { shipId, force = false } = parsed.data;
|
|
124
|
-
const entry = state.entries[shipId];
|
|
125
|
-
if (entry !== undefined) {
|
|
126
|
-
const releasing = { ...withState(entry, { state: 'releasing', now: deps.clock.now() }), release: { messageId: delivery.messageId, sender: delivery.senderShipId, isForced: force } };
|
|
127
|
-
return { state: putEntry(state, releasing), answers: [] };
|
|
128
|
-
}
|
|
129
|
-
const kept = state.kept.find((each) => each.shipId === shipId);
|
|
130
|
-
if (kept === undefined) {
|
|
131
|
-
return { state, answers: [refused(context, { shipId, field: 'shipId', reason: `${shipId} is not on the wanted list` })] };
|
|
132
|
-
}
|
|
133
|
-
const answer = answerTo(delivery);
|
|
134
|
-
if (!force) {
|
|
135
|
-
return { state, answers: [answer('released', { shipId, workspace: 'kept', path: kept.path })] };
|
|
136
|
-
}
|
|
137
|
-
// A release with force clears a kept worktree.
|
|
138
|
-
await deps.workspace.remove(kept.path);
|
|
139
|
-
return { state: { ...state, kept: state.kept.filter((each) => each !== kept) }, answers: [answer('released', { shipId, workspace: 'removed' })] };
|
|
140
|
-
}
|
|
141
12
|
//# sourceMappingURL=handle-delivery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-delivery.js","sourceRoot":"","sources":["../../src/core/handle-delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"handle-delivery.js","sourceRoot":"","sources":["../../src/core/handle-delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAczD,MAAM,UAAU,oBAAoB,CAAC,IAA0C;IAC7E,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,IAAI,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,QAAQ,CAAC,WAAW,SAAS,QAAQ,CAAC,YAAY,yEAAyE,CAAC,CAAC;IAChK,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TrierarchConfiguration, TrierarchReportDetails } from '@aeolus-fleet/common';
|
|
2
|
+
/**
|
|
3
|
+
* A harness's options as a JSON Schema, as the trierarch's report details
|
|
4
|
+
* give it (docs/trierarch.md, "What a trierarch reports"): each option one of
|
|
5
|
+
* its value names, with its default when it has one.
|
|
6
|
+
*/
|
|
7
|
+
export declare function optionsSchemaOf(harness: TrierarchConfiguration['harnesses'][string]): TrierarchReportDetails['harnesses'][number]['options'];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A harness's options as a JSON Schema, as the trierarch's report details
|
|
3
|
+
* give it (docs/trierarch.md, "What a trierarch reports"): each option one of
|
|
4
|
+
* its value names, with its default when it has one.
|
|
5
|
+
*/
|
|
6
|
+
export function optionsSchemaOf(harness) {
|
|
7
|
+
const properties = Object.fromEntries(Object.entries(harness.options).map(([name, option]) => [name, { enum: Object.keys(option.values), ...(option.default !== undefined && { default: option.default }) }]));
|
|
8
|
+
return { type: 'object', properties, additionalProperties: false };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=options-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options-schema.js","sourceRoot":"","sources":["../../src/core/options-schema.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAoD;IAClF,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CACnC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CACxK,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;AACrE,CAAC"}
|
package/dist/core/ports.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { CrewStatus, ShipId, TrierarchConfiguration, TrierarchReportDetails, TrierarchWorkspace } from '@aeolus-fleet/common';
|
|
2
|
+
import type { TrierarchState } from './entry.js';
|
|
3
3
|
import type { Action } from './reconciler.js';
|
|
4
4
|
/**
|
|
5
5
|
* The trierarch's ports (docs/architecture.md, "The trierarch"). Adapters
|
|
@@ -7,7 +7,7 @@ import type { Action } from './reconciler.js';
|
|
|
7
7
|
* and folders for workspaces, Claude Code for the harness, a JSON file for
|
|
8
8
|
* state. Each throws only for a system failure.
|
|
9
9
|
*/
|
|
10
|
-
/** Where a ship stands in the fleet, as `fleet.ship` answers the trierarch (
|
|
10
|
+
/** Where a ship stands in the fleet, as `fleet.ship` answers the trierarch (crew:run, for a ship assigned to it). */
|
|
11
11
|
export type FleetShipStatus = {
|
|
12
12
|
readonly kind: 'awaitingCrew';
|
|
13
13
|
readonly name: string;
|
|
@@ -34,9 +34,32 @@ export type InboxAnswer = {
|
|
|
34
34
|
} | {
|
|
35
35
|
readonly kind: 'leaseEnded';
|
|
36
36
|
};
|
|
37
|
+
/** A crew request assigned to the trierarch's ship, as `fleet.assignedCrewRequests` answers: its settings unread. */
|
|
38
|
+
export interface AssignedRequest {
|
|
39
|
+
readonly shipId: ShipId;
|
|
40
|
+
readonly settings: unknown;
|
|
41
|
+
readonly settingsVersion: number;
|
|
42
|
+
/** How the trierarch last said the crew stands; releasing once the requester removed it; null until any. */
|
|
43
|
+
readonly status: CrewStatus | null;
|
|
44
|
+
}
|
|
45
|
+
/** A plain-text report to argo, the operator, stored once under its idempotency key. */
|
|
46
|
+
export interface ArgoReport {
|
|
47
|
+
readonly text: string;
|
|
48
|
+
readonly idempotencyKey: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The fleet as the trierarch's own ship calls it, with `crew:run`: it reaches
|
|
52
|
+
* only the ships whose crew requests are assigned to it (decision 0029).
|
|
53
|
+
*/
|
|
37
54
|
export interface FleetPort {
|
|
38
55
|
/** The fleet's URL, written into each session's identity. */
|
|
39
56
|
readonly url: string;
|
|
57
|
+
/** The crew requests assigned to the trierarch's ship, oldest ship first. */
|
|
58
|
+
assignedRequests(): Promise<readonly AssignedRequest[]>;
|
|
59
|
+
/** Says how its crew of an assigned ship stands. */
|
|
60
|
+
writeStatus(shipId: ShipId, status: CrewStatus): Promise<void>;
|
|
61
|
+
/** Confirms it released the ship of a releasing request, which then goes. */
|
|
62
|
+
confirmRelease(shipId: ShipId): Promise<void>;
|
|
40
63
|
ship(shipId: ShipId): Promise<FleetShipStatus>;
|
|
41
64
|
/** A new starting prompt's secret; the trierarch keeps it only until it registers. */
|
|
42
65
|
getStartingPrompt(shipId: ShipId): Promise<{
|
|
@@ -52,7 +75,10 @@ export interface FleetPort {
|
|
|
52
75
|
release(shipId: ShipId): Promise<void>;
|
|
53
76
|
/** Acknowledges a delivery to the trierarch's own ship. */
|
|
54
77
|
ack(deliveryId: string): Promise<void>;
|
|
55
|
-
|
|
78
|
+
/** Answers a ping to the trierarch's own ship. */
|
|
79
|
+
pong(deliveryId: string): Promise<void>;
|
|
80
|
+
/** Sends argo a report, as the trierarch's own ship. */
|
|
81
|
+
reportToArgo(report: ArgoReport): Promise<void>;
|
|
56
82
|
/** A session's inbox, asked with its crew token. */
|
|
57
83
|
inbox(crewToken: string): Promise<InboxAnswer>;
|
|
58
84
|
/** Reports on a ship's behalf, with its session's crew token. */
|
|
@@ -61,6 +87,14 @@ export interface FleetPort {
|
|
|
61
87
|
state: 'blocked' | 'working';
|
|
62
88
|
note: string;
|
|
63
89
|
}): Promise<void>;
|
|
90
|
+
/** Reports as the trierarch's own ship: what it does, and its details. */
|
|
91
|
+
reportSelf(report: SelfReport): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
/** The trierarch's own report: idle or working, how many sessions run, and its details (docs/trierarch.md). */
|
|
94
|
+
export interface SelfReport {
|
|
95
|
+
readonly state: 'idle' | 'working';
|
|
96
|
+
readonly note: string;
|
|
97
|
+
readonly details: TrierarchReportDetails;
|
|
64
98
|
}
|
|
65
99
|
/** What a session's folder identity holds for the trierarch: written through the aeolus plugin, never a copy of it. */
|
|
66
100
|
export interface Identity {
|
|
@@ -81,7 +115,7 @@ export interface HarnessPort {
|
|
|
81
115
|
/** The crew token of the folder's identity, when there is one. */
|
|
82
116
|
crewTokenOf(folder: string): Promise<string | undefined>;
|
|
83
117
|
removeIdentity(folder: string): Promise<void>;
|
|
84
|
-
/** Starts the harness in the folder: the first start with the
|
|
118
|
+
/** Starts the harness in the folder: the first start with the settings' first prompt, later ones continuing. */
|
|
85
119
|
launch(session: {
|
|
86
120
|
shipId: ShipId;
|
|
87
121
|
/** With the workspace, what the session is named after where the harness names one. */
|
|
@@ -137,7 +171,8 @@ export interface LoggedAction {
|
|
|
137
171
|
readonly shipId: ShipId;
|
|
138
172
|
/** Known once the ship was first crewed. */
|
|
139
173
|
readonly shipName?: string;
|
|
140
|
-
|
|
174
|
+
/** What the loop did for the ship: an action it carried out, or leave when another session crews the ship. */
|
|
175
|
+
readonly action: Exclude<Action['kind'], 'argo' | 'status' | 'refuse'> | 'leave';
|
|
141
176
|
readonly outcome: string;
|
|
142
177
|
/** For a failure: what happens next, and where to look. */
|
|
143
178
|
readonly next?: string;
|
|
@@ -146,9 +181,14 @@ export interface Logger {
|
|
|
146
181
|
warn(message: string): void;
|
|
147
182
|
action(logged: LoggedAction): void;
|
|
148
183
|
}
|
|
149
|
-
/**
|
|
184
|
+
/** A flag a harness adapter adds itself, as mechanism, never the operator's: on every launch or on a restart only. */
|
|
185
|
+
export interface AdapterFlag {
|
|
186
|
+
readonly flag: string;
|
|
187
|
+
readonly when: 'always' | 'restart';
|
|
188
|
+
}
|
|
189
|
+
/** The trierarch's configuration, its version, and the flags each harness's adapter adds itself. */
|
|
150
190
|
export interface TrierarchSetup {
|
|
151
191
|
readonly configuration: TrierarchConfiguration;
|
|
152
192
|
readonly version: string;
|
|
153
|
-
readonly adapterFlags: Readonly<Record<string, readonly
|
|
193
|
+
readonly adapterFlags: Readonly<Record<string, readonly AdapterFlag[]>>;
|
|
154
194
|
}
|