@aeolus-fleet/trierarch 0.0.0-stage → 0.17.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/README.md +94 -2
- package/dist/adapters/claude-code-setup.d.ts +18 -0
- package/dist/adapters/claude-code-setup.js +51 -0
- package/dist/adapters/claude-code-setup.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +23 -0
- package/dist/adapters/claude-code.js +52 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/files.d.ts +26 -0
- package/dist/adapters/files.js +70 -0
- package/dist/adapters/files.js.map +1 -0
- package/dist/adapters/flags.d.ts +11 -0
- package/dist/adapters/flags.js +28 -0
- package/dist/adapters/flags.js.map +1 -0
- package/dist/adapters/git-workspace.d.ts +12 -0
- package/dist/adapters/git-workspace.js +64 -0
- package/dist/adapters/git-workspace.js.map +1 -0
- package/dist/adapters/json-state.d.ts +10 -0
- package/dist/adapters/json-state.js +70 -0
- package/dist/adapters/json-state.js.map +1 -0
- package/dist/adapters/paths.d.ts +19 -0
- package/dist/adapters/paths.js +14 -0
- package/dist/adapters/paths.js.map +1 -0
- package/dist/adapters/plugin.d.ts +10 -0
- package/dist/adapters/plugin.js +32 -0
- package/dist/adapters/plugin.js.map +1 -0
- package/dist/adapters/rest-fleet.d.ts +39 -0
- package/dist/adapters/rest-fleet.js +116 -0
- package/dist/adapters/rest-fleet.js.map +1 -0
- package/dist/adapters/run-command.d.ts +13 -0
- package/dist/adapters/run-command.js +27 -0
- package/dist/adapters/run-command.js.map +1 -0
- package/dist/adapters/service.d.ts +51 -0
- package/dist/adapters/service.js +213 -0
- package/dist/adapters/service.js.map +1 -0
- package/dist/adapters/tmux.d.ts +25 -0
- package/dist/adapters/tmux.js +51 -0
- package/dist/adapters/tmux.js.map +1 -0
- package/dist/adapters/version.d.ts +6 -0
- package/dist/adapters/version.js +12 -0
- package/dist/adapters/version.js.map +1 -0
- package/dist/bin/aeolus-trierarch.d.ts +2 -0
- package/dist/bin/aeolus-trierarch.js +12 -0
- package/dist/bin/aeolus-trierarch.js.map +1 -0
- package/dist/cli/config-check.d.ts +12 -0
- package/dist/cli/config-check.js +15 -0
- package/dist/cli/config-check.js.map +1 -0
- package/dist/cli/init.d.ts +43 -0
- package/dist/cli/init.js +222 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/list.d.ts +16 -0
- package/dist/cli/list.js +32 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/logs.d.ts +16 -0
- package/dist/cli/logs.js +51 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/main.d.ts +8 -0
- package/dist/cli/main.js +222 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/prompter.d.ts +25 -0
- package/dist/cli/prompter.js +95 -0
- package/dist/cli/prompter.js.map +1 -0
- package/dist/cli/run.d.ts +19 -0
- package/dist/cli/run.js +51 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/status.d.ts +44 -0
- package/dist/cli/status.js +65 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/uninstall.d.ts +19 -0
- package/dist/cli/uninstall.js +16 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/core/answers.d.ts +4 -0
- package/dist/core/answers.js +27 -0
- package/dist/core/answers.js.map +1 -0
- package/dist/core/check-want.d.ts +32 -0
- package/dist/core/check-want.js +80 -0
- package/dist/core/check-want.js.map +1 -0
- package/dist/core/entry.d.ts +73 -0
- package/dist/core/entry.js +15 -0
- package/dist/core/entry.js.map +1 -0
- package/dist/core/handle-delivery.d.ts +19 -0
- package/dist/core/handle-delivery.js +141 -0
- package/dist/core/handle-delivery.js.map +1 -0
- package/dist/core/ports.d.ts +137 -0
- package/dist/core/ports.js +2 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/reconciler.d.ts +67 -0
- package/dist/core/reconciler.js +101 -0
- package/dist/core/reconciler.js.map +1 -0
- package/dist/core/restart-policy.d.ts +19 -0
- package/dist/core/restart-policy.js +25 -0
- package/dist/core/restart-policy.js.map +1 -0
- package/dist/core/run-pass.d.ts +20 -0
- package/dist/core/run-pass.js +171 -0
- package/dist/core/run-pass.js.map +1 -0
- package/dist/core/shared/clock.d.ts +4 -0
- package/dist/core/shared/clock.js +2 -0
- package/dist/core/shared/clock.js.map +1 -0
- package/dist/core/shared/result.d.ts +16 -0
- package/dist/core/shared/result.js +7 -0
- package/dist/core/shared/result.js.map +1 -0
- package/dist/core/uninstall.d.ts +13 -0
- package/dist/core/uninstall.js +11 -0
- package/dist/core/uninstall.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -4
package/README.md
CHANGED
|
@@ -1,3 +1,95 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @aeolus-fleet/trierarch
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
That gives the command `aeolus-trierarch`.
|
|
14
|
+
|
|
15
|
+
1. In the console, commission the trierarch's own ship: type `trierarch`, with **Crew ships** (`fleet:crew`). Keep its starting prompt open: it shows the ship id and the secret.
|
|
16
|
+
2. Set up the machine:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
aeolus-trierarch init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
It asks for what it needs, in a short flow:
|
|
23
|
+
- the fleet URL, the ship id and the secret. The secret is not shown as you type it, so it stays out of your shell history and any transcript. It registers the ship, keeps its crew token in `~/.aeolus/trierarch/crew-token` (readable by you only) and writes the secret nowhere;
|
|
24
|
+
- whether Claude Code launches with `--dangerously-skip-permissions` and with `--remote-control` (both no unless you say yes);
|
|
25
|
+
- the repositories it may make worktrees of, by name and path;
|
|
26
|
+
- the caps: how many ships on its list, how many sessions at once.
|
|
27
|
+
|
|
28
|
+
It writes `~/.aeolus/trierarch/config.json`, with `config.schema.json` beside it so an editor checks it. It answers Claude Code's one-time questions ahead, so a session nobody watches never waits on one: it trusts the worktree root in `~/.claude.json`, which covers every worktree under it, and each configured folder (on every run, so a folder added since is trusted too), and, when the sessions skip permissions, offers to accept bypass permissions mode in `~/.claude/settings.json`. Then it offers to install and start the service: a launchd agent on macOS (`~/Library/LaunchAgents/dev.aeolus-fleet.trierarch.plist`), a systemd user unit on Linux (`aeolus-trierarch.service`), started at login and again whenever it stops.
|
|
29
|
+
|
|
30
|
+
Without a terminal, pass everything as flags and take every default with `--yes`: `aeolus-trierarch init --fleet-url https://fleet.example.com --ship-id shp_... --secret aeolus_sk_v1_... --yes`. Prefer the prompt: a secret in a flag lands in your shell history.
|
|
31
|
+
|
|
32
|
+
Run `aeolus-trierarch init` again on a machine set up already to change the configuration. It says so and never registers again; to register another ship, remove `~/.aeolus/trierarch/crew-token` first.
|
|
33
|
+
|
|
34
|
+
## Use
|
|
35
|
+
|
|
36
|
+
| Command | What it does |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `aeolus-trierarch status` | The service (running, pid, since), the fleet and the trierarch's own lease, the caps in use, the entries by state, kept worktrees and orphans |
|
|
39
|
+
| `aeolus-trierarch list` | The wanted entries: ship, state, harness, workspace, since, restarts |
|
|
40
|
+
| `aeolus-trierarch logs [--lines <n>] [--follow]` | The last lines of `~/.aeolus/trierarch/logs/trierarch.log`, and with `--follow` each new one |
|
|
41
|
+
| `aeolus-trierarch config check` | Checks the configuration and gives the flags each harness launches with, and each option value's flags |
|
|
42
|
+
| `aeolus-trierarch start`, `stop`, `restart` | The service. A stopped one starts again at the next login; restart it after you change the configuration |
|
|
43
|
+
| `aeolus-trierarch install [--no-load]` | Installs the service; with `--no-load` it only writes its file |
|
|
44
|
+
| `aeolus-trierarch uninstall` | Removes the service and stops every session. It deletes nothing else: it lists the worktrees it leaves, and keeps its own files under `~/.aeolus/trierarch/` |
|
|
45
|
+
| `aeolus-trierarch run` | What the service runs: keeps the wanted ships crewed until stopped |
|
|
46
|
+
|
|
47
|
+
Every command takes `--json`, so a ship can read what it says, and `--config <path>`. `status`, `list` and `logs` read what is on the machine and send no message; `status` asks the fleet `whoami` once, for the lease.
|
|
48
|
+
|
|
49
|
+
To stop the trierarch: `aeolus-trierarch stop`, or `aeolus-trierarch uninstall` to keep it from starting at login. Each session runs in tmux on the trierarch's own server: attach to one with `tmux -L aeolus-trierarch attach -t trierarch-<ship id>`.
|
|
50
|
+
|
|
51
|
+
## Configure
|
|
52
|
+
|
|
53
|
+
`~/.aeolus/trierarch/config.json` (or the file `--config` or `AEOLUS_TRIERARCH_CONFIG` names):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"$schema": "./config.schema.json",
|
|
58
|
+
"caps": { "ships": 4, "running": 2 },
|
|
59
|
+
"repositories": { "aeolus-fleet": { "path": "/Users/thomas/Projects/aeolus-fleet" } },
|
|
60
|
+
"folders": { "notes": { "path": "/Users/thomas/notes" } },
|
|
61
|
+
"harnesses": {
|
|
62
|
+
"claude-code": {
|
|
63
|
+
"flags": ["--remote-control"],
|
|
64
|
+
"options": {
|
|
65
|
+
"model": { "values": { "opus": ["--model", "claude-opus-5-5"], "sonnet": ["--model", "claude-sonnet-5-5"] }, "default": "opus" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **caps**: how many ships it keeps on its list, and how many sessions run at once.
|
|
73
|
+
- **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.
|
|
74
|
+
- **folders**: by name, used as they are, one ship per folder, never removed.
|
|
75
|
+
- **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.
|
|
76
|
+
|
|
77
|
+
`aeolus-trierarch init` writes caps, repositories and Claude Code's flags; edit the file for folders, options and `worktreeRoot`, then `aeolus-trierarch config check` and `aeolus-trierarch restart`.
|
|
78
|
+
|
|
79
|
+
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.
|
|
80
|
+
|
|
81
|
+
## What is here
|
|
82
|
+
|
|
83
|
+
- **Core** (`src/core`), with no framework, adapter or clock of its own:
|
|
84
|
+
- the wanted list;
|
|
85
|
+
- a pure Reconciler, carried out by the run pass;
|
|
86
|
+
- the restart policy: 5 s, 30 s, 2 min, then 10 min, with a budget of 5 restarts an hour;
|
|
87
|
+
- the ports.
|
|
88
|
+
- **Adapters** (`src/adapters`):
|
|
89
|
+
- the fleet over REST;
|
|
90
|
+
- 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>`;
|
|
91
|
+
- git worktrees and folders;
|
|
92
|
+
- Claude Code, through the aeolus plugin's `aeolus-identity.sh`;
|
|
93
|
+
- the JSON state store.
|
|
94
|
+
- **The command** (`src/cli`): `init`, `status`, `list`, `logs`, `config check`, `start`, `stop`, `restart`, `install`, `uninstall`, `run`.
|
|
95
|
+
- **Adapters for the setup**: the service (launchd or systemd), and Claude Code's own files for its one-time questions.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code's one-time questions, answered ahead so a session nobody
|
|
3
|
+
* watches never waits on one. Claude Code asks whether to trust a folder on
|
|
4
|
+
* its first start there, even with `--dangerously-skip-permissions`; trusting
|
|
5
|
+
* a folder covers every folder under it, so trusting the worktree root covers
|
|
6
|
+
* every worktree. It asks once per user whether to accept bypass permissions
|
|
7
|
+
* mode. Both answers live in Claude Code's own files, which this changes key
|
|
8
|
+
* by key, keeping everything else and the file's mode.
|
|
9
|
+
*/
|
|
10
|
+
export interface ClaudeCodeSetup {
|
|
11
|
+
trust(folder: string): Promise<void>;
|
|
12
|
+
isTrusted(folder: string): Promise<boolean>;
|
|
13
|
+
acceptSkipPermissions(): Promise<void>;
|
|
14
|
+
isSkipPermissionsAccepted(): Promise<boolean>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createClaudeCodeSetup(at: {
|
|
17
|
+
homeDirectory: string;
|
|
18
|
+
}): ClaudeCodeSetup;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { TrierarchFileError } from './files.js';
|
|
5
|
+
const jsonObjectSchema = z.record(z.string(), z.unknown());
|
|
6
|
+
/** A file only its user reads, as Claude Code keeps ~/.claude.json. */
|
|
7
|
+
const PRIVATE_MODE = 0o600;
|
|
8
|
+
async function readJsonObject(path) {
|
|
9
|
+
let text;
|
|
10
|
+
try {
|
|
11
|
+
text = await readFile(path, 'utf8');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
return jsonObjectSchema.parse(JSON.parse(text));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new TrierarchFileError(`${path} is no JSON object: fix it by hand, then run this again`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function writeJsonObject(path, value) {
|
|
24
|
+
const mode = (await stat(path).catch(() => undefined))?.mode ?? PRIVATE_MODE;
|
|
25
|
+
await mkdir(dirname(path), { recursive: true });
|
|
26
|
+
const temporary = `${path}.aeolus-trierarch.tmp`;
|
|
27
|
+
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: mode & 0o777 });
|
|
28
|
+
await rename(temporary, path);
|
|
29
|
+
}
|
|
30
|
+
export function createClaudeCodeSetup(at) {
|
|
31
|
+
const state = join(at.homeDirectory, '.claude.json');
|
|
32
|
+
const settings = join(at.homeDirectory, '.claude', 'settings.json');
|
|
33
|
+
const projectsOf = (claude) => jsonObjectSchema.safeParse(claude.projects).data ?? {};
|
|
34
|
+
return {
|
|
35
|
+
trust: async (folder) => {
|
|
36
|
+
const claude = await readJsonObject(state);
|
|
37
|
+
const projects = projectsOf(claude);
|
|
38
|
+
const project = jsonObjectSchema.safeParse(projects[folder]).data ?? {};
|
|
39
|
+
await writeJsonObject(state, { ...claude, projects: { ...projects, [folder]: { ...project, hasTrustDialogAccepted: true } } });
|
|
40
|
+
},
|
|
41
|
+
isTrusted: async (folder) => {
|
|
42
|
+
const project = jsonObjectSchema.safeParse(projectsOf(await readJsonObject(state))[folder]).data;
|
|
43
|
+
return project?.hasTrustDialogAccepted === true;
|
|
44
|
+
},
|
|
45
|
+
acceptSkipPermissions: async () => {
|
|
46
|
+
await writeJsonObject(settings, { ...(await readJsonObject(settings)), skipDangerousModePermissionPrompt: true });
|
|
47
|
+
},
|
|
48
|
+
isSkipPermissionsAccepted: async () => (await readJsonObject(settings)).skipDangerousModePermissionPrompt === true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=claude-code-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-setup.js","sourceRoot":"","sources":["../../src/adapters/claude-code-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,26 @@
|
|
|
1
|
+
import { type ShipId, 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, the trierarch's own ship and its crew token. A crew file written before the ship id was kept lacks it. */
|
|
18
|
+
export interface CrewFile {
|
|
19
|
+
readonly fleetUrl: string;
|
|
20
|
+
readonly shipId?: ShipId;
|
|
21
|
+
readonly crewToken: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function writeCrewFile(path: string, crew: CrewFile & {
|
|
24
|
+
shipId: ShipId;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
export declare function readCrewFile(path: string): Promise<CrewFile>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { idSchema, 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}\nshipId=${crew.shipId}\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
|
+
const shipId = idSchema('ship').safeParse(field('shipId'));
|
|
68
|
+
return { fleetUrl, ...(shipId.success && { shipId: shipId.data }), crewToken };
|
|
69
|
+
}
|
|
70
|
+
//# 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":"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"}
|
|
@@ -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"}
|