@aeolus-fleet/trierarch 0.0.0-stage → 0.17.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.
Files changed (89) hide show
  1. package/README.md +76 -2
  2. package/dist/adapters/claude-code.d.ts +23 -0
  3. package/dist/adapters/claude-code.js +52 -0
  4. package/dist/adapters/claude-code.js.map +1 -0
  5. package/dist/adapters/files.d.ts +23 -0
  6. package/dist/adapters/files.js +69 -0
  7. package/dist/adapters/files.js.map +1 -0
  8. package/dist/adapters/flags.d.ts +11 -0
  9. package/dist/adapters/flags.js +28 -0
  10. package/dist/adapters/flags.js.map +1 -0
  11. package/dist/adapters/git-workspace.d.ts +12 -0
  12. package/dist/adapters/git-workspace.js +64 -0
  13. package/dist/adapters/git-workspace.js.map +1 -0
  14. package/dist/adapters/json-state.d.ts +10 -0
  15. package/dist/adapters/json-state.js +70 -0
  16. package/dist/adapters/json-state.js.map +1 -0
  17. package/dist/adapters/paths.d.ts +19 -0
  18. package/dist/adapters/paths.js +14 -0
  19. package/dist/adapters/paths.js.map +1 -0
  20. package/dist/adapters/plugin.d.ts +10 -0
  21. package/dist/adapters/plugin.js +32 -0
  22. package/dist/adapters/plugin.js.map +1 -0
  23. package/dist/adapters/rest-fleet.d.ts +34 -0
  24. package/dist/adapters/rest-fleet.js +108 -0
  25. package/dist/adapters/rest-fleet.js.map +1 -0
  26. package/dist/adapters/run-command.d.ts +13 -0
  27. package/dist/adapters/run-command.js +27 -0
  28. package/dist/adapters/run-command.js.map +1 -0
  29. package/dist/adapters/tmux.d.ts +25 -0
  30. package/dist/adapters/tmux.js +51 -0
  31. package/dist/adapters/tmux.js.map +1 -0
  32. package/dist/adapters/version.d.ts +6 -0
  33. package/dist/adapters/version.js +12 -0
  34. package/dist/adapters/version.js.map +1 -0
  35. package/dist/bin/aeolus-trierarch.d.ts +2 -0
  36. package/dist/bin/aeolus-trierarch.js +10 -0
  37. package/dist/bin/aeolus-trierarch.js.map +1 -0
  38. package/dist/cli/config-check.d.ts +3 -0
  39. package/dist/cli/config-check.js +8 -0
  40. package/dist/cli/config-check.js.map +1 -0
  41. package/dist/cli/init.d.ts +18 -0
  42. package/dist/cli/init.js +28 -0
  43. package/dist/cli/init.js.map +1 -0
  44. package/dist/cli/install.d.ts +34 -0
  45. package/dist/cli/install.js +109 -0
  46. package/dist/cli/install.js.map +1 -0
  47. package/dist/cli/main.d.ts +8 -0
  48. package/dist/cli/main.js +75 -0
  49. package/dist/cli/main.js.map +1 -0
  50. package/dist/cli/run.d.ts +19 -0
  51. package/dist/cli/run.js +51 -0
  52. package/dist/cli/run.js.map +1 -0
  53. package/dist/core/answers.d.ts +4 -0
  54. package/dist/core/answers.js +27 -0
  55. package/dist/core/answers.js.map +1 -0
  56. package/dist/core/check-want.d.ts +32 -0
  57. package/dist/core/check-want.js +80 -0
  58. package/dist/core/check-want.js.map +1 -0
  59. package/dist/core/entry.d.ts +73 -0
  60. package/dist/core/entry.js +15 -0
  61. package/dist/core/entry.js.map +1 -0
  62. package/dist/core/handle-delivery.d.ts +19 -0
  63. package/dist/core/handle-delivery.js +141 -0
  64. package/dist/core/handle-delivery.js.map +1 -0
  65. package/dist/core/ports.d.ts +137 -0
  66. package/dist/core/ports.js +2 -0
  67. package/dist/core/ports.js.map +1 -0
  68. package/dist/core/reconciler.d.ts +67 -0
  69. package/dist/core/reconciler.js +101 -0
  70. package/dist/core/reconciler.js.map +1 -0
  71. package/dist/core/restart-policy.d.ts +19 -0
  72. package/dist/core/restart-policy.js +25 -0
  73. package/dist/core/restart-policy.js.map +1 -0
  74. package/dist/core/run-pass.d.ts +20 -0
  75. package/dist/core/run-pass.js +171 -0
  76. package/dist/core/run-pass.js.map +1 -0
  77. package/dist/core/shared/clock.d.ts +4 -0
  78. package/dist/core/shared/clock.js +2 -0
  79. package/dist/core/shared/clock.js.map +1 -0
  80. package/dist/core/shared/result.d.ts +16 -0
  81. package/dist/core/shared/result.js +7 -0
  82. package/dist/core/shared/result.js.map +1 -0
  83. package/dist/core/uninstall.d.ts +13 -0
  84. package/dist/core/uninstall.js +11 -0
  85. package/dist/core/uninstall.js.map +1 -0
  86. package/dist/index.d.ts +6 -0
  87. package/dist/index.js +6 -0
  88. package/dist/index.js.map +1 -0
  89. package/package.json +37 -4
package/README.md CHANGED
@@ -1,3 +1,77 @@
1
- # Temporary Holding Version
1
+ # @aeolus-fleet/trierarch
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Keeps the ships on its wanted list crewed on the machine it runs on: it starts, restarts, wakes and stops their sessions. It crews ships with `fleet:crew`; it never commissions or retires them. What a trierarch does is in [docs/trierarch.md](../../docs/trierarch.md); how this package is built is in [docs/architecture.md](../../docs/architecture.md#the-trierarch). Optional, like squadrons.
4
+
5
+ ## Install
6
+
7
+ On the machine that will run the sessions (macOS or Linux), with Node 26, `git`, `tmux`, Claude Code and the aeolus plugin for Claude Code installed:
8
+
9
+ ```sh
10
+ npm install --global @aeolus-fleet/trierarch
11
+ ```
12
+
13
+ 1. In the console, commission the trierarch's own ship: type `trierarch`, with **Crew ships** (`fleet:crew`). Copy its ship id and secret from the starting prompt.
14
+ 2. Register it and write a configuration:
15
+
16
+ ```sh
17
+ aeolus-trierarch init https://fleet.example.com shp_... aeolus_sk_v1_...
18
+ ```
19
+
20
+ This keeps the trierarch's own crew token in `~/.aeolus/trierarch/crew-token`, readable by you only, and never writes the secret. It writes `~/.aeolus/trierarch/config.json` when there is none yet, with `config.schema.json` beside it so an editor checks it.
21
+ 3. Edit the configuration (below), then check it:
22
+
23
+ ```sh
24
+ aeolus-trierarch config check
25
+ ```
26
+
27
+ It prints the flags each harness launches with, and each option value's flags.
28
+ 4. Keep it running:
29
+
30
+ ```sh
31
+ aeolus-trierarch install
32
+ ```
33
+
34
+ On macOS this loads a launchd agent (`~/Library/LaunchAgents/dev.aeolus-fleet.trierarch.plist`) that starts the trierarch at login and again whenever it stops. On Linux it enables a systemd user unit (`aeolus-trierarch.service`). With `--no-load` it only writes the file. Logs go to `~/.aeolus/trierarch/logs/`. To run it in the foreground instead: `aeolus-trierarch run`.
35
+
36
+ ## Configure
37
+
38
+ `~/.aeolus/trierarch/config.json` (or the file `--config` or `AEOLUS_TRIERARCH_CONFIG` names):
39
+
40
+ ```json
41
+ {
42
+ "$schema": "./config.schema.json",
43
+ "caps": { "ships": 4, "running": 2 },
44
+ "repositories": { "aeolus-fleet": { "path": "/Users/thomas/Projects/aeolus-fleet" } },
45
+ "folders": { "notes": { "path": "/Users/thomas/notes" } },
46
+ "harnesses": {
47
+ "claude-code": {
48
+ "flags": ["--remote-control"],
49
+ "options": {
50
+ "model": { "values": { "opus": ["--model", "claude-opus-5-5"], "sonnet": ["--model", "claude-sonnet-5-5"] }, "default": "opus" }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ - **caps**: how many ships it keeps on its list, and how many sessions run at once.
58
+ - **repositories**: by name. A want for a worktree of one gets a git worktree under `~/.aeolus/trierarch/worktrees/<repository>/<ship>`, detached at the want's ref or the repository's HEAD. `worktreeRoot` moves that root.
59
+ - **folders**: by name, used as they are, one ship per folder, never removed.
60
+ - **harnesses**: the flags every launch gets, and named options. A want picks option values by name; it never adds a flag, and messages never carry paths or flags. There is no policy on which flags: put `--dangerously-skip-permissions` in `flags` if you want it. The trierarch never adds a flag by itself.
61
+
62
+ The trierarch finds the aeolus plugin in Claude Code's plugin cache (the newest version). Set `AEOLUS_PLUGIN_ROOT` and `AEOLUS_PLUGIN_DATA` to point it elsewhere. `aeolus-trierarch install` carries these, `AEOLUS_TRIERARCH_CONFIG` and `PATH` into the service as they are set where it runs.
63
+
64
+ ## What is here
65
+
66
+ - **Core** (`src/core`), with no framework, adapter or clock of its own:
67
+ - the wanted list;
68
+ - a pure Reconciler, carried out by the run pass;
69
+ - the restart policy: 5 s, 30 s, 2 min, then 10 min, with a budget of 5 restarts an hour;
70
+ - the ports.
71
+ - **Adapters** (`src/adapters`):
72
+ - the fleet over REST;
73
+ - tmux, on its own server (`tmux -L aeolus-trierarch`, sessions named `trierarch-<ship id>` and kept on exit). Attach to one with `tmux -L aeolus-trierarch attach -t trierarch-<ship id>`;
74
+ - git worktrees and folders;
75
+ - Claude Code, through the aeolus plugin's `aeolus-identity.sh`;
76
+ - the JSON state store.
77
+ - **The command** (`src/cli`): `init`, `config check`, `run`, `install`.
@@ -0,0 +1,23 @@
1
+ import type { TrierarchConfiguration } from '@aeolus-fleet/common';
2
+ import type { HarnessPort } from '../core/ports.js';
3
+ import type { Tmux } from './tmux.js';
4
+ /**
5
+ * Claude Code as a harness (docs/architecture.md, "First adapters"): `claude`
6
+ * in the folder, `--continue` on a restart, `/aeolus:wake` typed to wake an
7
+ * idle session. A session's identity is written and read through the aeolus
8
+ * plugin's own `aeolus-identity.sh`, never a copy of how the plugin names its
9
+ * files.
10
+ */
11
+ /** The aeolus plugin as Claude Code installed it: its scripts and its data folder. */
12
+ export interface AeolusPlugin {
13
+ readonly root: string;
14
+ readonly data: string;
15
+ }
16
+ export declare const WAKE_PROMPT = "/aeolus:wake";
17
+ export declare function createClaudeCodeHarness(options: {
18
+ configuration: TrierarchConfiguration;
19
+ plugin: AeolusPlugin;
20
+ sessions: Pick<Tmux, 'start' | 'type'>;
21
+ /** The program to run; `claude` unless a test runs another. */
22
+ command?: string;
23
+ }): HarnessPort;
@@ -0,0 +1,52 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { effectiveFlags } from './flags.js';
3
+ import { runCommand } from './run-command.js';
4
+ export const WAKE_PROMPT = '/aeolus:wake';
5
+ export function createClaudeCodeHarness(options) {
6
+ const { configuration, plugin, sessions } = options;
7
+ const identity = async (folder, args) => runCommand('bash', { args: [`${plugin.root}/scripts/aeolus-identity.sh`, ...args], env: { AEOLUS_FOLDER: folder, AEOLUS_DATA: plugin.data } });
8
+ return {
9
+ prepareIdentity: async ({ folder, identity: crew }) => {
10
+ const written = await identity(folder, [
11
+ 'write',
12
+ '--wake-by',
13
+ 'trierarch',
14
+ crew.fleetUrl,
15
+ crew.shipId,
16
+ crew.shipName,
17
+ crew.crewToken,
18
+ ...(crew.squadron === undefined ? [] : [crew.squadron]),
19
+ ]);
20
+ if (written.status !== 0) {
21
+ throw new Error(`aeolus-identity.sh write failed: ${written.stderr.trim()}`);
22
+ }
23
+ },
24
+ crewTokenOf: async (folder) => {
25
+ const path = (await identity(folder, ['path'])).stdout.trim();
26
+ const text = await readFile(path, 'utf8').catch(() => '');
27
+ return /^crewToken=(.+)$/m.exec(text)?.[1];
28
+ },
29
+ removeIdentity: async (folder) => {
30
+ await identity(folder, ['delete']);
31
+ },
32
+ launch: async ({ shipId, folder, harness, options: picked, isFirstStart, firstPrompt }) => {
33
+ const settings = configuration.harnesses[harness];
34
+ if (settings === undefined) {
35
+ throw new Error(`The configuration has no harness ${harness}`);
36
+ }
37
+ const prompt = isFirstStart && firstPrompt !== undefined ? firstPrompt : WAKE_PROMPT;
38
+ // The prompt goes first: a flag with an optional value, such as `--remote-control [name]`, would take it.
39
+ const command = [options.command ?? 'claude', prompt, ...effectiveFlags(settings, picked), ...(isFirstStart ? [] : ['--continue'])];
40
+ await sessions.start({ shipId, folder, command });
41
+ },
42
+ turnOf: async (folder) => {
43
+ const turn = await identity(folder, ['turn']);
44
+ const [state] = turn.stdout.split(' ');
45
+ return turn.status === 0 && (state === 'busy' || state === 'idle') ? state : 'unknown';
46
+ },
47
+ wake: async ({ shipId }) => {
48
+ await sessions.type({ shipId, text: WAKE_PROMPT });
49
+ },
50
+ };
51
+ }
52
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/adapters/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type TrierarchConfiguration } from '@aeolus-fleet/common';
2
+ /**
3
+ * The trierarch's own files: its configuration, written by the operator, and
4
+ * its crew file, written by `init`: the fleet's URL and the trierarch's own
5
+ * crew token, readable by its user only.
6
+ */
7
+ /** A file the trierarch cannot use, with why, for the operator to read. */
8
+ export declare class TrierarchFileError extends Error {
9
+ }
10
+ /** The configuration a fresh `init` writes: no repository or folder yet, Claude Code with no flag. The trierarch adds no flag by itself. */
11
+ export declare function initialConfiguration(): TrierarchConfiguration & {
12
+ $schema: string;
13
+ };
14
+ /** The configuration's JSON Schema, written beside it so an editor checks it. */
15
+ export declare function configurationJsonSchema(): Record<string, unknown>;
16
+ export declare function loadConfiguration(path: string): Promise<TrierarchConfiguration>;
17
+ /** The fleet and the trierarch's own crew token. */
18
+ export interface CrewFile {
19
+ readonly fleetUrl: string;
20
+ readonly crewToken: string;
21
+ }
22
+ export declare function writeCrewFile(path: string, crew: CrewFile): Promise<void>;
23
+ export declare function readCrewFile(path: string): Promise<CrewFile>;
@@ -0,0 +1,69 @@
1
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { trierarchConfigurationSchema } from '@aeolus-fleet/common';
4
+ import { z } from 'zod';
5
+ /**
6
+ * The trierarch's own files: its configuration, written by the operator, and
7
+ * its crew file, written by `init`: the fleet's URL and the trierarch's own
8
+ * crew token, readable by its user only.
9
+ */
10
+ /** A file the trierarch cannot use, with why, for the operator to read. */
11
+ export class TrierarchFileError extends Error {
12
+ }
13
+ /** The configuration a fresh `init` writes: no repository or folder yet, Claude Code with no flag. The trierarch adds no flag by itself. */
14
+ export function initialConfiguration() {
15
+ return {
16
+ $schema: './config.schema.json',
17
+ caps: { ships: 4, running: 2 },
18
+ repositories: {},
19
+ folders: {},
20
+ harnesses: { 'claude-code': { flags: [], options: {} } },
21
+ };
22
+ }
23
+ /** The configuration's JSON Schema, written beside it so an editor checks it. */
24
+ export function configurationJsonSchema() {
25
+ return z.toJSONSchema(trierarchConfigurationSchema, { io: 'input' });
26
+ }
27
+ export async function loadConfiguration(path) {
28
+ let text;
29
+ try {
30
+ text = await readFile(path, 'utf8');
31
+ }
32
+ catch {
33
+ throw new TrierarchFileError(`No configuration at ${path}: run aeolus-trierarch init first`);
34
+ }
35
+ let json;
36
+ try {
37
+ json = JSON.parse(text);
38
+ }
39
+ catch (error) {
40
+ throw new TrierarchFileError(`The configuration at ${path} is no JSON: ${error instanceof Error ? error.message : String(error)}`);
41
+ }
42
+ const parsed = trierarchConfigurationSchema.safeParse(json);
43
+ if (!parsed.success) {
44
+ throw new TrierarchFileError(`The configuration at ${path} does not fit:\n${z.prettifyError(parsed.error)}`);
45
+ }
46
+ return parsed.data;
47
+ }
48
+ export async function writeCrewFile(path, crew) {
49
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
50
+ await writeFile(path, `fleetUrl=${crew.fleetUrl}\ncrewToken=${crew.crewToken}\n`, { mode: 0o600 });
51
+ await chmod(path, 0o600);
52
+ }
53
+ export async function readCrewFile(path) {
54
+ let text;
55
+ try {
56
+ text = await readFile(path, 'utf8');
57
+ }
58
+ catch {
59
+ throw new TrierarchFileError(`No crew token at ${path}: run aeolus-trierarch init first`);
60
+ }
61
+ const field = (name) => new RegExp(`^${name}=(.+)$`, 'm').exec(text)?.[1];
62
+ const fleetUrl = field('fleetUrl');
63
+ const crewToken = field('crewToken');
64
+ if (fleetUrl === undefined || crewToken === undefined) {
65
+ throw new TrierarchFileError(`The crew file at ${path} lacks its fleetUrl or crewToken line: run aeolus-trierarch init again`);
66
+ }
67
+ return { fleetUrl, crewToken };
68
+ }
69
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/adapters/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { TrierarchConfiguration } from '@aeolus-fleet/common';
2
+ type HarnessConfiguration = TrierarchConfiguration['harnesses'][string];
3
+ /**
4
+ * The flags a launch gets: the harness's own, then each option's value as the
5
+ * want picked it, or its default. A want picks names only; the flags come
6
+ * from the configuration alone.
7
+ */
8
+ export declare function effectiveFlags(harness: HarnessConfiguration, options: Readonly<Record<string, string>>): string[];
9
+ /** What `config check` prints: per harness, the flags a want with no options launches with, and each option's values. */
10
+ export declare function describeFlags(configuration: TrierarchConfiguration): string;
11
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The flags a launch gets: the harness's own, then each option's value as the
3
+ * want picked it, or its default. A want picks names only; the flags come
4
+ * from the configuration alone.
5
+ */
6
+ export function effectiveFlags(harness, options) {
7
+ const picked = Object.entries(harness.options).flatMap(([name, option]) => {
8
+ const value = options[name] ?? option.default;
9
+ return value === undefined ? [] : (option.values[value] ?? []);
10
+ });
11
+ return [...harness.flags, ...picked];
12
+ }
13
+ /** What `config check` prints: per harness, the flags a want with no options launches with, and each option's values. */
14
+ export function describeFlags(configuration) {
15
+ return Object.entries(configuration.harnesses)
16
+ .map(([name, harness]) => {
17
+ const lines = [`${name}: ${[...effectiveFlags(harness, {})].join(' ') || '(no flags)'}`];
18
+ for (const [option, settings] of Object.entries(harness.options)) {
19
+ for (const [value, flags] of Object.entries(settings.values)) {
20
+ const isDefault = settings.default === value;
21
+ lines.push(` ${option}=${value}${isDefault ? ' (default)' : ''}: ${flags.join(' ') || '(no flags)'}`);
22
+ }
23
+ }
24
+ return lines.join('\n');
25
+ })
26
+ .join('\n');
27
+ }
28
+ //# sourceMappingURL=flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/adapters/flags.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAA6B,EAAE,OAAyC;IACrG,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;QACxE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC;QAC9C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,yHAAyH;AACzH,MAAM,UAAU,aAAa,CAAC,aAAqC;IACjE,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC;SAC3C,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QACzF,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACjE,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC;gBAC7C,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;YACzG,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { TrierarchConfiguration } from '@aeolus-fleet/common';
2
+ import type { WorkspacePort } from '../core/ports.js';
3
+ /**
4
+ * Workspaces (docs/architecture.md, "First adapters"): a git worktree of a
5
+ * configured repository under the worktree root, one folder per ship
6
+ * (`<root>/<repository>/<ship>`), detached at the want's ref or the
7
+ * repository's HEAD; or a configured folder used as it is.
8
+ */
9
+ export declare function createGitWorkspace(options: {
10
+ configuration: TrierarchConfiguration;
11
+ root: string;
12
+ }): WorkspacePort;
@@ -0,0 +1,64 @@
1
+ import { mkdir, readdir, stat } from 'node:fs/promises';
2
+ import { join, relative, sep } from 'node:path';
3
+ import { runCommand } from './run-command.js';
4
+ /**
5
+ * Workspaces (docs/architecture.md, "First adapters"): a git worktree of a
6
+ * configured repository under the worktree root, one folder per ship
7
+ * (`<root>/<repository>/<ship>`), detached at the want's ref or the
8
+ * repository's HEAD; or a configured folder used as it is.
9
+ */
10
+ export function createGitWorkspace(options) {
11
+ const { configuration, root } = options;
12
+ const git = async (args) => {
13
+ const result = await runCommand('git', { args });
14
+ if (result.status !== 0) {
15
+ throw new Error(`git ${args.join(' ')} failed: ${result.stderr.trim()}`);
16
+ }
17
+ return result.stdout;
18
+ };
19
+ const exists = async (path) => (await stat(path).catch(() => undefined)) !== undefined;
20
+ const repositoryOf = (folder) => {
21
+ const [repository] = relative(root, folder).split(sep);
22
+ return repository === undefined ? undefined : configuration.repositories[repository]?.path;
23
+ };
24
+ return {
25
+ prepare: async ({ shipName, workspace }) => {
26
+ if (workspace.kind === 'folder') {
27
+ const folder = configuration.folders[workspace.name]?.path;
28
+ if (folder === undefined) {
29
+ throw new Error(`The configuration has no folder ${workspace.name}`);
30
+ }
31
+ return { folder };
32
+ }
33
+ const repository = configuration.repositories[workspace.repository]?.path;
34
+ if (repository === undefined) {
35
+ throw new Error(`The configuration has no repository ${workspace.repository}`);
36
+ }
37
+ const folder = join(root, workspace.repository, shipName);
38
+ // A worktree a stop mid-crew left is used as it is.
39
+ if (!(await exists(folder))) {
40
+ await mkdir(join(root, workspace.repository), { recursive: true });
41
+ await git(['-C', repository, 'worktree', 'add', '--detach', folder, ...(workspace.ref === undefined ? [] : [workspace.ref])]);
42
+ }
43
+ return { folder };
44
+ },
45
+ isClean: async (folder) => (await git(['-C', folder, 'status', '--porcelain'])).trim() === '',
46
+ remove: async (folder) => {
47
+ const repository = repositoryOf(folder);
48
+ if (repository === undefined) {
49
+ throw new Error(`${folder} is no worktree under ${root}: the trierarch removes only what it made`);
50
+ }
51
+ await git(['-C', repository, 'worktree', 'remove', '--force', folder]);
52
+ },
53
+ worktrees: async () => {
54
+ const found = [];
55
+ for (const repository of await readdir(root).catch(() => [])) {
56
+ for (const ship of await readdir(join(root, repository)).catch(() => [])) {
57
+ found.push({ path: join(root, repository, ship) });
58
+ }
59
+ }
60
+ return found;
61
+ },
62
+ };
63
+ }
64
+ //# sourceMappingURL=git-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-workspace.js","sourceRoot":"","sources":["../../src/adapters/git-workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAKhD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgE;IACjG,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,GAAG,GAAG,KAAK,EAAE,IAAuB,EAAmB,EAAE;QAC7D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,SAAS,CAAC;IACjH,MAAM,YAAY,GAAG,CAAC,MAAc,EAAsB,EAAE;QAC1D,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvD,OAAO,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;IAC7F,CAAC,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE;YACzC,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;gBAC3D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,CAAC,mCAAmC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvE,CAAC;gBACD,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,CAAC;YACD,MAAM,UAAU,GAAG,aAAa,CAAC,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;YAC1E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,uCAAuC,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC;YACjF,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC1D,oDAAoD;YACpD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAC5B,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACnE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChI,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;QAC7F,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACvB,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,yBAAyB,IAAI,2CAA2C,CAAC,CAAC;YACrG,CAAC;YACD,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,MAAM,KAAK,GAAuB,EAAE,CAAC;YACrC,KAAK,MAAM,UAAU,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC7D,KAAK,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;oBACzE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { StatePort } from '../core/ports.js';
2
+ /**
3
+ * The state store: `~/.aeolus/trierarch/state.json`, written whole and
4
+ * atomically (a temporary file, then a rename), so a stop mid-write leaves the
5
+ * last state. It holds no secret. Only the most recent applied messages are
6
+ * kept, enough for the fleet to deliver one again.
7
+ */
8
+ /** How many applied messages the store keeps, newest last. */
9
+ export declare const APPLIED_CAP = 1000;
10
+ export declare function createJsonState(path: string): StatePort;
@@ -0,0 +1,70 @@
1
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { idSchema, TRIERARCH_ENTRY_STATES, trierarchWorkspaceSchema } from '@aeolus-fleet/common';
4
+ import { z } from 'zod';
5
+ import { EMPTY_STATE } from '../core/entry.js';
6
+ /**
7
+ * The state store: `~/.aeolus/trierarch/state.json`, written whole and
8
+ * atomically (a temporary file, then a rename), so a stop mid-write leaves the
9
+ * last state. It holds no secret. Only the most recent applied messages are
10
+ * kept, enough for the fleet to deliver one again.
11
+ */
12
+ /** How many applied messages the store keeps, newest last. */
13
+ export const APPLIED_CAP = 1000;
14
+ const outgoingSchema = z.object({
15
+ to: idSchema('ship'),
16
+ inReplyTo: z.string().optional(),
17
+ name: z.string(),
18
+ payload: z.record(z.string(), z.unknown()),
19
+ idempotencyKey: z.string(),
20
+ });
21
+ const stateSchema = z.object({
22
+ entries: z.record(z.string(), z.object({
23
+ shipId: idSchema('ship'),
24
+ harness: z.string(),
25
+ workspace: trierarchWorkspaceSchema,
26
+ squadron: z.string().optional(),
27
+ firstPrompt: z.string().optional(),
28
+ options: z.record(z.string(), z.string()),
29
+ requester: idSchema('ship'),
30
+ state: z.enum(TRIERARCH_ENTRY_STATES),
31
+ since: z.string(),
32
+ exits: z.array(z.string()),
33
+ restartAt: z.string().optional(),
34
+ folder: z.string().optional(),
35
+ hasStarted: z.boolean(),
36
+ release: z.object({ messageId: z.string(), sender: idSchema('ship'), isForced: z.boolean() }).optional(),
37
+ wake: z.object({ waiting: z.int().nonnegative(), isPending: z.boolean() }),
38
+ })),
39
+ applied: z.record(z.string(), z.array(outgoingSchema)),
40
+ kept: z.array(z.object({ shipId: idSchema('ship'), path: z.string() })),
41
+ orphans: z.array(z.string()),
42
+ });
43
+ function isMissing(error) {
44
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
45
+ }
46
+ export function createJsonState(path) {
47
+ return {
48
+ load: async () => {
49
+ let text;
50
+ try {
51
+ text = await readFile(path, 'utf8');
52
+ }
53
+ catch (error) {
54
+ if (isMissing(error)) {
55
+ return EMPTY_STATE;
56
+ }
57
+ throw error;
58
+ }
59
+ return stateSchema.parse(JSON.parse(text));
60
+ },
61
+ save: async (state) => {
62
+ const applied = Object.fromEntries(Object.entries(state.applied).slice(-APPLIED_CAP));
63
+ await mkdir(dirname(path), { recursive: true });
64
+ const temporary = `${path}.tmp`;
65
+ await writeFile(temporary, `${JSON.stringify({ ...state, applied }, null, 2)}\n`, { mode: 0o600 });
66
+ await rename(temporary, path);
67
+ },
68
+ };
69
+ }
70
+ //# sourceMappingURL=json-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-state.js","sourceRoot":"","sources":["../../src/adapters/json-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Where the trierarch keeps its files (docs/architecture.md, "Files on the
3
+ * machine"): everything under `~/.aeolus/trierarch/`. The configuration's
4
+ * path can be moved with `--config` or AEOLUS_TRIERARCH_CONFIG, and the
5
+ * worktree root by the configuration.
6
+ */
7
+ export interface TrierarchPaths {
8
+ readonly home: string;
9
+ readonly config: string;
10
+ readonly configSchema: string;
11
+ readonly crewToken: string;
12
+ readonly state: string;
13
+ readonly logs: string;
14
+ readonly worktrees: string;
15
+ }
16
+ export declare function trierarchPaths(at: {
17
+ homeDirectory: string;
18
+ config?: string;
19
+ }): TrierarchPaths;
@@ -0,0 +1,14 @@
1
+ import { join } from 'node:path';
2
+ export function trierarchPaths(at) {
3
+ const home = join(at.homeDirectory, '.aeolus', 'trierarch');
4
+ return {
5
+ home,
6
+ config: at.config ?? join(home, 'config.json'),
7
+ configSchema: join(home, 'config.schema.json'),
8
+ crewToken: join(home, 'crew-token'),
9
+ state: join(home, 'state.json'),
10
+ logs: join(home, 'logs'),
11
+ worktrees: join(home, 'worktrees'),
12
+ };
13
+ }
14
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/adapters/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkBjC,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,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QAC/B,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { AeolusPlugin } from './claude-code.js';
2
+ /**
3
+ * Where Claude Code installed the aeolus plugin: AEOLUS_PLUGIN_ROOT and
4
+ * AEOLUS_PLUGIN_DATA when set, else the newest version in Claude Code's
5
+ * plugin cache and the plugin's data folder.
6
+ */
7
+ export declare function findAeolusPlugin(at: {
8
+ homeDirectory: string;
9
+ env: Readonly<Record<string, string | undefined>>;
10
+ }): Promise<AeolusPlugin>;
@@ -0,0 +1,32 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Where Claude Code installed the aeolus plugin: AEOLUS_PLUGIN_ROOT and
5
+ * AEOLUS_PLUGIN_DATA when set, else the newest version in Claude Code's
6
+ * plugin cache and the plugin's data folder.
7
+ */
8
+ export async function findAeolusPlugin(at) {
9
+ const plugins = join(at.homeDirectory, '.claude', 'plugins');
10
+ const data = at.env.AEOLUS_PLUGIN_DATA ?? join(plugins, 'data', 'aeolus-aeolus-fleet');
11
+ if (at.env.AEOLUS_PLUGIN_ROOT !== undefined) {
12
+ return { root: at.env.AEOLUS_PLUGIN_ROOT, data };
13
+ }
14
+ const cache = join(plugins, 'cache', 'aeolus-fleet', 'aeolus');
15
+ const versions = (await readdir(cache).catch(() => [])).filter((name) => /^\d+\.\d+\.\d+$/.test(name)).sort(byVersion);
16
+ const newest = versions.at(-1);
17
+ if (newest === undefined) {
18
+ throw new Error(`The aeolus plugin is not installed for Claude Code (nothing in ${cache}): install it, or set AEOLUS_PLUGIN_ROOT`);
19
+ }
20
+ return { root: join(cache, newest), data };
21
+ }
22
+ function byVersion(left, right) {
23
+ const [a, b] = [left, right].map((version) => version.split('.').map(Number));
24
+ for (let index = 0; index < 3; index += 1) {
25
+ const difference = (a?.[index] ?? 0) - (b?.[index] ?? 0);
26
+ if (difference !== 0) {
27
+ return difference;
28
+ }
29
+ }
30
+ return 0;
31
+ }
32
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/adapters/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAgF;IACrH,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACvF,IAAI,EAAE,CAAC,GAAG,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvH,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,kEAAkE,KAAK,0CAA0C,CAAC,CAAC;IACrI,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,KAAa;IAC5C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9E,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type ShipId } from '@aeolus-fleet/common';
2
+ import type { Delivery, FleetPort } from '../core/ports.js';
3
+ /**
4
+ * The fleet over its REST API (`/api/v1`), as any ship may call it: the
5
+ * trierarch's own crew (receive, ack, send) with its crew token, its
6
+ * `fleet:crew` calls (ship, getStartingPrompt, release), and a session's
7
+ * register, inbox and report. A refusal the trierarch does not expect throws
8
+ * with the fleet's code and message.
9
+ */
10
+ /** What the trierarch states as model and harness: it is software, not a model, as squadrons is (#157). */
11
+ export declare const TRIERARCH_SELF: {
12
+ readonly model: `@aeolus-fleet/trierarch@${string}`;
13
+ readonly harness: "aeolus-trierarch";
14
+ };
15
+ /** A refusal from the fleet: its code and message. */
16
+ export declare class FleetRefusal extends Error {
17
+ readonly code: string;
18
+ constructor(code: string, message: string);
19
+ }
20
+ export interface RestFleet extends FleetPort {
21
+ /** The deliveries waiting for the trierarch's own ship. */
22
+ receive(): Promise<Delivery[]>;
23
+ /** Registers a ship with its secret, as the trierarch itself at init: its crew token. */
24
+ registerSelf(crew: {
25
+ shipId: ShipId;
26
+ secret: string;
27
+ }): Promise<{
28
+ crewToken: string;
29
+ }>;
30
+ }
31
+ export declare function createRestFleet(options: {
32
+ fleetUrl: string;
33
+ crewToken: string;
34
+ }): RestFleet;