@gaia-ai/core 0.6.0 → 0.6.2
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 +2 -2
- package/dist/src/cli/config-source.d.ts +47 -0
- package/dist/src/cli/config-source.js +197 -0
- package/dist/src/cli/gaia-dir.d.ts +48 -3
- package/dist/src/cli/gaia-dir.js +100 -10
- package/dist/src/cli/load-gaia-config.d.ts +5 -0
- package/dist/src/cli/load-gaia-config.js +10 -1
- package/dist/src/core/exec.d.ts +1 -1
- package/dist/src/core/exec.js +1 -1
- package/dist/src/index.d.ts +4 -11
- package/dist/src/index.js +18 -7
- package/dist/src/plugins/discover-addons.d.ts +19 -8
- package/dist/src/plugins/discover-addons.js +13 -3
- package/dist/src/plugins/preset.d.ts +30 -12
- package/dist/src/plugins/preset.js +10 -0
- package/package.json +4 -5
- package/dist/src/plugins/agent/agent.d.ts +0 -61
- package/dist/src/plugins/agent/agent.js +0 -11
- package/dist/src/plugins/auth/basic.d.ts +0 -12
- package/dist/src/plugins/auth/basic.js +0 -37
- package/dist/src/plugins/builtins-preset.d.ts +0 -5
- package/dist/src/plugins/builtins-preset.js +0 -32
- package/dist/src/plugins/executor/executor.d.ts +0 -104
- package/dist/src/plugins/executor/executor.js +0 -1
- package/dist/src/plugins/plugins.d.ts +0 -60
- package/dist/src/plugins/plugins.js +0 -42
- package/dist/src/plugins/registry-exports.d.ts +0 -6
- package/dist/src/plugins/registry-exports.js +0 -6
- package/dist/src/plugins/remote/drupal.d.ts +0 -40
- package/dist/src/plugins/remote/drupal.js +0 -393
- package/dist/src/plugins/remote/fake.d.ts +0 -113
- package/dist/src/plugins/remote/fake.js +0 -247
- package/dist/src/plugins/remote/remote.d.ts +0 -203
- package/dist/src/plugins/remote/remote.js +0 -1
- package/dist/src/plugins/workspace/fake.d.ts +0 -6
- package/dist/src/plugins/workspace/fake.js +0 -16
- package/dist/src/plugins/workspace/git.d.ts +0 -37
- package/dist/src/plugins/workspace/git.js +0 -89
- package/dist/src/plugins/workspace/instructions.d.ts +0 -6
- package/dist/src/plugins/workspace/instructions.js +0 -16
- package/dist/src/plugins/workspace/workspace.d.ts +0 -35
- package/dist/src/plugins/workspace/workspace.js +0 -1
- package/dist/src/plugins-index.d.ts +0 -1
- package/dist/src/plugins-index.js +0 -1
- package/dist/src/types.d.ts +0 -65
- package/dist/src/types.js +0 -1
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
const ACTIVE = ['claimed', 'running'];
|
|
2
|
-
export class FakeGaiaRemote {
|
|
3
|
-
calls = {
|
|
4
|
-
markRunning: [],
|
|
5
|
-
markFailed: [],
|
|
6
|
-
finalizeRun: [],
|
|
7
|
-
closeTicket: [],
|
|
8
|
-
markCleanedUp: [],
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Override for reconcile tests: when set, `fetchActiveRuns` returns this
|
|
12
|
-
* list verbatim instead of deriving it from the internal runs map.
|
|
13
|
-
*/
|
|
14
|
-
activeRuns = null;
|
|
15
|
-
runs = new Map();
|
|
16
|
-
queue = [];
|
|
17
|
-
tickets;
|
|
18
|
-
conductorStatus = 'online';
|
|
19
|
-
/** Stable counter for assigning numeric ids to unseeded runs. */
|
|
20
|
-
runIdCounter = 0;
|
|
21
|
-
constructor(seed = {}) {
|
|
22
|
-
this.tickets = seed.tickets ?? {};
|
|
23
|
-
for (const r of seed.runs ?? []) {
|
|
24
|
-
const handler = r.handler ?? 'code';
|
|
25
|
-
// Use the seeded id when provided; otherwise assign a deterministic counter.
|
|
26
|
-
const runId = r.id ?? ++this.runIdCounter;
|
|
27
|
-
this.runs.set(r.runUuid, {
|
|
28
|
-
runUuid: r.runUuid,
|
|
29
|
-
runId,
|
|
30
|
-
ticketUuid: r.ticketUuid,
|
|
31
|
-
handler,
|
|
32
|
-
state: r.state ?? r.stateAtStart,
|
|
33
|
-
stateAtStart: r.stateAtStart,
|
|
34
|
-
...(r.worktreePath !== undefined
|
|
35
|
-
? { worktreePath: r.worktreePath }
|
|
36
|
-
: {}),
|
|
37
|
-
...(r.agent !== undefined ? { agent: r.agent } : {}),
|
|
38
|
-
...(r.closed !== undefined ? { closed: r.closed } : {}),
|
|
39
|
-
});
|
|
40
|
-
this.queue.push({
|
|
41
|
-
runUuid: r.runUuid,
|
|
42
|
-
runId,
|
|
43
|
-
ticketUuid: r.ticketUuid,
|
|
44
|
-
stateAtStart: r.stateAtStart,
|
|
45
|
-
handler,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async registerConductor(_reg) {
|
|
50
|
-
this.conductorStatus = 'online';
|
|
51
|
-
return 'fake-conductor-uuid';
|
|
52
|
-
}
|
|
53
|
-
async heartbeat() {
|
|
54
|
-
// Mirrors the server: a heartbeat always brings the conductor online.
|
|
55
|
-
this.conductorStatus = 'online';
|
|
56
|
-
return this.conductorStatus;
|
|
57
|
-
}
|
|
58
|
-
async getConductorStatus(_conductorId) {
|
|
59
|
-
return this.conductorStatus;
|
|
60
|
-
}
|
|
61
|
-
async setConductorStatus(_conductorId, status) {
|
|
62
|
-
this.conductorStatus = status;
|
|
63
|
-
}
|
|
64
|
-
async listConductors(_owner) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
async activeRunCount(_conductorId) {
|
|
68
|
-
return this.internalActiveRuns().length;
|
|
69
|
-
}
|
|
70
|
-
async fetchActiveRuns(_conductorId) {
|
|
71
|
-
if (this.activeRuns !== null) {
|
|
72
|
-
return this.activeRuns;
|
|
73
|
-
}
|
|
74
|
-
return this.internalActiveRuns().map((r) => {
|
|
75
|
-
const ticket = this.tickets[r.ticketUuid];
|
|
76
|
-
const identifier = ticket?.identifier ?? '';
|
|
77
|
-
const branchName = ticket?.branchName ??
|
|
78
|
-
(identifier ? `gaia/${identifier.toLowerCase()}` : '');
|
|
79
|
-
return {
|
|
80
|
-
runUuid: r.runUuid,
|
|
81
|
-
ticketUuid: r.ticketUuid,
|
|
82
|
-
ticketIdentifier: identifier,
|
|
83
|
-
branchName,
|
|
84
|
-
state: r.state,
|
|
85
|
-
stateAtStart: r.stateAtStart,
|
|
86
|
-
worktreePath: r.worktreePath ?? '',
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
async claimNext(_claim) {
|
|
91
|
-
return this.queue.shift() ?? null;
|
|
92
|
-
}
|
|
93
|
-
async getTicket(ticketUuid) {
|
|
94
|
-
const t = this.tickets[ticketUuid];
|
|
95
|
-
if (!t) {
|
|
96
|
-
throw new Error(`fake remote: ticket ${ticketUuid} not seeded`);
|
|
97
|
-
}
|
|
98
|
-
const branchName = t.branchName ?? `gaia/${t.identifier.toLowerCase()}`;
|
|
99
|
-
return {
|
|
100
|
-
uuid: ticketUuid,
|
|
101
|
-
identifier: t.identifier,
|
|
102
|
-
title: t.title,
|
|
103
|
-
state: t.state,
|
|
104
|
-
branchName,
|
|
105
|
-
...(t.baseBranch ? { baseBranch: t.baseBranch } : {}),
|
|
106
|
-
...(t.effectiveEnvVars ? { effectiveEnvVars: t.effectiveEnvVars } : {}),
|
|
107
|
-
...(t.url ? { issueUrl: t.url } : {}),
|
|
108
|
-
labels: t.labels ?? [],
|
|
109
|
-
environments: t.environments ?? [],
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
async getRunWorktree(runUuid) {
|
|
113
|
-
return this.runs.get(runUuid)?.worktreePath ?? '';
|
|
114
|
-
}
|
|
115
|
-
async getRunTicketIdentifier(runUuid) {
|
|
116
|
-
const ticketUuid = this.runs.get(runUuid)?.ticketUuid;
|
|
117
|
-
if (!ticketUuid) {
|
|
118
|
-
return '';
|
|
119
|
-
}
|
|
120
|
-
return this.tickets[ticketUuid]?.identifier ?? '';
|
|
121
|
-
}
|
|
122
|
-
async getRunTicketBranchName(runUuid) {
|
|
123
|
-
const ticketUuid = this.runs.get(runUuid)?.ticketUuid;
|
|
124
|
-
if (!ticketUuid) {
|
|
125
|
-
return '';
|
|
126
|
-
}
|
|
127
|
-
const ticket = this.tickets[ticketUuid];
|
|
128
|
-
if (!ticket) {
|
|
129
|
-
return '';
|
|
130
|
-
}
|
|
131
|
-
return ticket.branchName ?? `gaia/${ticket.identifier.toLowerCase()}`;
|
|
132
|
-
}
|
|
133
|
-
async markRunning(runUuid, attrs) {
|
|
134
|
-
this.calls.markRunning.push({
|
|
135
|
-
runUuid,
|
|
136
|
-
...(attrs
|
|
137
|
-
? {
|
|
138
|
-
attrs: {
|
|
139
|
-
...(attrs.worktree_path
|
|
140
|
-
? { worktree_path: attrs.worktree_path }
|
|
141
|
-
: {}),
|
|
142
|
-
...(attrs.agent ? { agent: attrs.agent } : {}),
|
|
143
|
-
},
|
|
144
|
-
}
|
|
145
|
-
: {}),
|
|
146
|
-
});
|
|
147
|
-
const run = this.runs.get(runUuid);
|
|
148
|
-
if (run) {
|
|
149
|
-
run.state = 'running';
|
|
150
|
-
if (attrs?.worktree_path) {
|
|
151
|
-
run.worktreePath = attrs.worktree_path;
|
|
152
|
-
}
|
|
153
|
-
if (attrs?.agent) {
|
|
154
|
-
run.agent = attrs.agent;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async markFailed(runUuid, errorLog) {
|
|
159
|
-
this.calls.markFailed.push({ runUuid, errorLog });
|
|
160
|
-
const run = this.runs.get(runUuid);
|
|
161
|
-
if (run) {
|
|
162
|
-
run.state = 'failed';
|
|
163
|
-
run.closed = true;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
async fetchFinalizableRuns(_conductorId) {
|
|
167
|
-
return [...this.runs.values()]
|
|
168
|
-
.filter((r) => r.state === 'done' && !r.closed)
|
|
169
|
-
.map((r) => ({
|
|
170
|
-
runUuid: r.runUuid,
|
|
171
|
-
runId: r.runId,
|
|
172
|
-
worktreePath: r.worktreePath ?? '',
|
|
173
|
-
agent: r.agent ?? '',
|
|
174
|
-
}));
|
|
175
|
-
}
|
|
176
|
-
async finalizeRun(runUuid, log, metrics) {
|
|
177
|
-
this.calls.finalizeRun.push({
|
|
178
|
-
runUuid,
|
|
179
|
-
log,
|
|
180
|
-
...(metrics ? { metrics } : {}),
|
|
181
|
-
});
|
|
182
|
-
const run = this.runs.get(runUuid);
|
|
183
|
-
if (run) {
|
|
184
|
-
run.closed = true;
|
|
185
|
-
if (log)
|
|
186
|
-
run.log = log;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async fetchUncleanedTickets(conductorId) {
|
|
190
|
-
// Scoped to the querying conductor (GAIA-121). A seed without an explicit
|
|
191
|
-
// `conductorId` defaults to the querying conductor (the common reaper-test
|
|
192
|
-
// case); an explicit value scopes it, and `''` models an unassigned ticket.
|
|
193
|
-
return Object.entries(this.tickets)
|
|
194
|
-
.filter(([, t]) => (t.state === 'done' || t.closed) &&
|
|
195
|
-
!t.cleanedUp &&
|
|
196
|
-
(t.conductorId ?? conductorId) === conductorId)
|
|
197
|
-
.map(([ticketUuid, t]) => ({
|
|
198
|
-
ticketUuid,
|
|
199
|
-
branchName: t.branchName ?? `gaia/${t.identifier.toLowerCase()}`,
|
|
200
|
-
worktreePath: this.latestRunWorktree(ticketUuid),
|
|
201
|
-
state: t.state,
|
|
202
|
-
closed: t.closed ?? false,
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
|
-
async closeTicket(uuid) {
|
|
206
|
-
this.calls.closeTicket.push(uuid);
|
|
207
|
-
const t = this.tickets[uuid];
|
|
208
|
-
if (t) {
|
|
209
|
-
t.closed = true;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async markTicketCleanedUp(uuid) {
|
|
213
|
-
this.calls.markCleanedUp.push(uuid);
|
|
214
|
-
const t = this.tickets[uuid];
|
|
215
|
-
if (t) {
|
|
216
|
-
t.cleanedUp = true;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
async resolveTicketByIdentifier(_project, identifier) {
|
|
220
|
-
const hit = Object.entries(this.tickets).find(([, t]) => t.identifier === identifier);
|
|
221
|
-
return hit ? { uuid: hit[0], title: hit[1].title ?? '' } : null;
|
|
222
|
-
}
|
|
223
|
-
/** Worktree path of the ticket's most recently seeded run, or '' when none. */
|
|
224
|
-
latestRunWorktree(ticketUuid) {
|
|
225
|
-
let worktree = '';
|
|
226
|
-
for (const r of this.runs.values()) {
|
|
227
|
-
if (r.ticketUuid === ticketUuid && r.worktreePath) {
|
|
228
|
-
worktree = r.worktreePath;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return worktree;
|
|
232
|
-
}
|
|
233
|
-
internalActiveRuns() {
|
|
234
|
-
return [...this.runs.values()].filter((r) => ACTIVE.includes(r.state));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
export function fakeRemote(seed = {}) {
|
|
238
|
-
const remote = new FakeGaiaRemote(seed);
|
|
239
|
-
return {
|
|
240
|
-
kind: 'remote',
|
|
241
|
-
id: 'fake',
|
|
242
|
-
requiredModules: [],
|
|
243
|
-
async createRemote(_config) {
|
|
244
|
-
return remote;
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
export interface ConductorRegistration {
|
|
2
|
-
id: string;
|
|
3
|
-
project: string;
|
|
4
|
-
/** Empty = serve all claimable states in the project (GAIA-207). */
|
|
5
|
-
states: string[];
|
|
6
|
-
workspace: string;
|
|
7
|
-
label: string;
|
|
8
|
-
max_parallel: number;
|
|
9
|
-
}
|
|
10
|
-
export interface ClaimOptions {
|
|
11
|
-
leaseSeconds: number;
|
|
12
|
-
conductorId: string;
|
|
13
|
-
}
|
|
14
|
-
export interface ClaimedRun {
|
|
15
|
-
runUuid: string;
|
|
16
|
-
runId: number;
|
|
17
|
-
ticketUuid: string;
|
|
18
|
-
stateAtStart: string;
|
|
19
|
-
handler: string;
|
|
20
|
-
}
|
|
21
|
-
export interface Ticket {
|
|
22
|
-
uuid: string;
|
|
23
|
-
identifier: string;
|
|
24
|
-
title: string;
|
|
25
|
-
state: string;
|
|
26
|
-
branchName: string;
|
|
27
|
-
/** Branch the ticket's work is based on (computed server-side): parent branch, project default, or 'main'. */
|
|
28
|
-
baseBranch?: string;
|
|
29
|
-
/**
|
|
30
|
-
* Effective environment variables (GAIA-99), resolved server-side along the
|
|
31
|
-
* parent_id chain (child wins) as canonical `.env`-style `KEY=value` lines.
|
|
32
|
-
* The conductor parses this, strips reserved keys, overlays the core GAIA_*
|
|
33
|
-
* vars, and injects the result into the agent run env AND the workspace hooks.
|
|
34
|
-
* Omitted when the ticket (and its ancestors) set none. Values may be secret.
|
|
35
|
-
*/
|
|
36
|
-
effectiveEnvVars?: string;
|
|
37
|
-
issueUrl?: string;
|
|
38
|
-
/** Ticket label term names (gaia_labels vocab), sideloaded for agent selection. Empty when none. */
|
|
39
|
-
labels: string[];
|
|
40
|
-
/** Ticket environments (gaia_environment refs), each as name + tier, sideloaded for agent selection. Empty when none. */
|
|
41
|
-
environments: {
|
|
42
|
-
name: string;
|
|
43
|
-
tier: string;
|
|
44
|
-
}[];
|
|
45
|
-
}
|
|
46
|
-
export interface ActiveRun {
|
|
47
|
-
runUuid: string;
|
|
48
|
-
ticketUuid: string;
|
|
49
|
-
ticketIdentifier: string;
|
|
50
|
-
branchName: string;
|
|
51
|
-
state: string;
|
|
52
|
-
stateAtStart: string;
|
|
53
|
-
/** Absolute path of the per-run git worktree, or '' when unset. */
|
|
54
|
-
worktreePath: string;
|
|
55
|
-
}
|
|
56
|
-
export interface ConductorStatus {
|
|
57
|
-
id: string;
|
|
58
|
-
project: string;
|
|
59
|
-
label: string;
|
|
60
|
-
status: string;
|
|
61
|
-
lastSeen: number;
|
|
62
|
-
load: number;
|
|
63
|
-
}
|
|
64
|
-
export interface FinalizableRun {
|
|
65
|
-
runUuid: string;
|
|
66
|
-
/** Numeric run id (the `#<runId>` tab-label token); scopes the run's tab-close at finalise (GAIA-183). */
|
|
67
|
-
runId: number;
|
|
68
|
-
/** Absolute path of the per-run git worktree, or '' when unset. */
|
|
69
|
-
worktreePath: string;
|
|
70
|
-
/** Id of the agent that ran (GAIA-144); routes footprint parsing at finalize. '' / undefined when unset. */
|
|
71
|
-
agent?: string;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Per-run effort footprint the conductor writes onto `gaia_run` at close
|
|
75
|
-
* (GAIA-132). Six integer metrics, all parsed from the agent transcript —
|
|
76
|
-
* including `duration_s`, the run's wall-clock length derived from the
|
|
77
|
-
* transcript's first→last timestamps (GAIA-151), not a re-read `started_at`.
|
|
78
|
-
*/
|
|
79
|
-
export interface RunMetrics {
|
|
80
|
-
tokens: number;
|
|
81
|
-
duration_s: number;
|
|
82
|
-
agent_turns: number;
|
|
83
|
-
tool_calls: number;
|
|
84
|
-
user_prompts: number;
|
|
85
|
-
user_prompt_words: number;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* A finished ticket whose worktree has not yet been torn down — the
|
|
89
|
-
* conductor's teardown work list, driven by the durable `cleaned_up` flag
|
|
90
|
-
* (GAIA-89). "Finished" = reached `done` OR already `closed`; `cleaned_up=0`
|
|
91
|
-
* means the herdr worktree still needs reclaiming. The reconciliation is NOT
|
|
92
|
-
* scoped to a conductor's machine_id, so it also surfaces tickets whose
|
|
93
|
-
* conductor was down at `done` or whose `conductor_id` was reassigned.
|
|
94
|
-
*/
|
|
95
|
-
export interface UncleanTicket {
|
|
96
|
-
ticketUuid: string;
|
|
97
|
-
/** The ticket's git branch — the key herdr resolves the worktree by. */
|
|
98
|
-
branchName: string;
|
|
99
|
-
/**
|
|
100
|
-
* Absolute path of the ticket's worktree (from its latest run's
|
|
101
|
-
* `worktree_path`), or '' when unresolved. The cwd the teardown runs in.
|
|
102
|
-
*/
|
|
103
|
-
worktreePath: string;
|
|
104
|
-
/** Workflow state (e.g. `coding`, `done`). */
|
|
105
|
-
state: string;
|
|
106
|
-
/** Whether the ticket's lifecycle has already been closed out. */
|
|
107
|
-
closed: boolean;
|
|
108
|
-
}
|
|
109
|
-
/** Extra conductor-writable gaia_run attributes (besides state/lease). */
|
|
110
|
-
export interface RunWriteAttributes {
|
|
111
|
-
/** Absolute path of the per-run git worktree (set by the conductor). */
|
|
112
|
-
worktree_path?: string;
|
|
113
|
-
/** Id of the agent the conductor chose for this run (GAIA-144), for footprint routing. */
|
|
114
|
-
agent?: string;
|
|
115
|
-
}
|
|
116
|
-
export interface GaiaRemote {
|
|
117
|
-
/** Upserts by machine_id; sets status=online. Returns the Drupal entity uuid. */
|
|
118
|
-
registerConductor(reg: ConductorRegistration): Promise<string>;
|
|
119
|
-
/**
|
|
120
|
-
* Records a heartbeat server-side: upserts by machine_id (recreating a
|
|
121
|
-
* vanished registration), refreshes last_seen/lease/load, and brings the
|
|
122
|
-
* conductor online. Keyed on machine_id, so it never 404s — the self-heal
|
|
123
|
-
* lives on the server, not the client. Returns the resulting status
|
|
124
|
-
* (`online` | `offline`).
|
|
125
|
-
*/
|
|
126
|
-
heartbeat(reg: ConductorRegistration, currentLoad: number, leaseSeconds?: number): Promise<string>;
|
|
127
|
-
getConductorStatus(conductorId: string): Promise<string | null>;
|
|
128
|
-
setConductorStatus(conductorId: string, status: 'offline' | 'online'): Promise<void>;
|
|
129
|
-
listConductors(owner?: 'me'): Promise<ConductorStatus[]>;
|
|
130
|
-
activeRunCount(conductorId: string): Promise<number>;
|
|
131
|
-
fetchActiveRuns(conductorId: string): Promise<ActiveRun[]>;
|
|
132
|
-
claimNext(claim: ClaimOptions): Promise<ClaimedRun | null>;
|
|
133
|
-
getTicket(ticketUuid: string): Promise<Ticket>;
|
|
134
|
-
/** Returns the run's worktree_path, or '' when unset. */
|
|
135
|
-
getRunWorktree(runUuid: string): Promise<string>;
|
|
136
|
-
/**
|
|
137
|
-
* Returns the identifier of the run's ticket, or '' when unresolved. Lets the
|
|
138
|
-
* release path derive the tab matching ref (the ticket identifier) from a run
|
|
139
|
-
* uuid alone.
|
|
140
|
-
*/
|
|
141
|
-
getRunTicketIdentifier(runUuid: string): Promise<string>;
|
|
142
|
-
/**
|
|
143
|
-
* Returns the branch_name of the run's ticket, or '' when unresolved. Used
|
|
144
|
-
* by dispatch for the per-(branch,state) herdr tab model.
|
|
145
|
-
*/
|
|
146
|
-
getRunTicketBranchName(runUuid: string): Promise<string>;
|
|
147
|
-
markRunning(runUuid: string, attrs?: RunWriteAttributes): Promise<void>;
|
|
148
|
-
/**
|
|
149
|
-
* Terminalise a run to the `failed` state on a dispatch/setup error (GAIA-149).
|
|
150
|
-
* PATCHes state=failed, records the failure detail in `error_log`, and closes
|
|
151
|
-
* the run (closed + closed_date). `failed` is a terminal sibling of `expired`
|
|
152
|
-
* (dispatch error vs lease lapse), so the run stops counting toward capacity
|
|
153
|
-
* and its ticket is no longer blocked by the single-active-run gate — instead
|
|
154
|
-
* of the old "leave it `claimed` to expire after ~300s" silent stall. The
|
|
155
|
-
* failure is visible immediately and its cause is recorded.
|
|
156
|
-
*/
|
|
157
|
-
markFailed(runUuid: string, errorLog: string): Promise<void>;
|
|
158
|
-
/**
|
|
159
|
-
* Runs this conductor owns that are state=done but not yet closed — the
|
|
160
|
-
* conductor's one-shot finalisation work list.
|
|
161
|
-
*/
|
|
162
|
-
fetchFinalizableRuns(conductorId: string): Promise<FinalizableRun[]>;
|
|
163
|
-
/**
|
|
164
|
-
* Finalise a run: set closed + closed_date, (when non-empty) log, and (when
|
|
165
|
-
* given) the per-run footprint metrics (GAIA-132). No state change — the run
|
|
166
|
-
* is already done.
|
|
167
|
-
*/
|
|
168
|
-
finalizeRun(uuid: string, log: string, metrics?: RunMetrics): Promise<void>;
|
|
169
|
-
/**
|
|
170
|
-
* Finished tickets (state=done OR closed) whose worktree is not yet torn
|
|
171
|
-
* down (cleaned_up=0) — the conductor's teardown work list (GAIA-89). Driven
|
|
172
|
-
* by the durable `cleaned_up` flag AND scoped to the reaping conductor
|
|
173
|
-
* (GAIA-121): only tickets assigned to `conductorId` are loaded, so every
|
|
174
|
-
* ticket on the list is unambiguously this conductor's — a missing worktree
|
|
175
|
-
* means "already torn down on this host", not "belongs to another host". The
|
|
176
|
-
* same query backs the live tick and the standalone `gaia conductor reap`.
|
|
177
|
-
* Empty once every finished ticket this conductor owns is cleaned.
|
|
178
|
-
*/
|
|
179
|
-
fetchUncleanedTickets(conductorId: string): Promise<UncleanTicket[]>;
|
|
180
|
-
/**
|
|
181
|
-
* Close a ticket: set closed=true + closed_date. Ticket-lifecycle only, and
|
|
182
|
-
* decoupled from teardown — a done ticket is closed even if its worktree
|
|
183
|
-
* teardown later fails. No state change (the ticket is already done).
|
|
184
|
-
*/
|
|
185
|
-
closeTicket(uuid: string): Promise<void>;
|
|
186
|
-
/**
|
|
187
|
-
* Mark a ticket's worktree torn down: set cleaned_up=true so it drops off the
|
|
188
|
-
* {@link fetchUncleanedTickets} work list. Written ONLY after a verified
|
|
189
|
-
* teardown, so a teardown miss leaves cleaned_up=0 and the next reconciliation
|
|
190
|
-
* retries — one miss never orphans the workspace, and a re-run on an
|
|
191
|
-
* already-cleaned ticket is a no-op (it is no longer on the list).
|
|
192
|
-
*/
|
|
193
|
-
markTicketCleanedUp(uuid: string): Promise<void>;
|
|
194
|
-
/**
|
|
195
|
-
* Resolve a ticket identifier (e.g. "GAIA-134") to its uuid+title within a
|
|
196
|
-
* project, or null when no such ticket exists. Used by the
|
|
197
|
-
* `gaia deployment tickets` helper to turn commit-message identifiers into tickets.
|
|
198
|
-
*/
|
|
199
|
-
resolveTicketByIdentifier(project: string, identifier: string): Promise<{
|
|
200
|
-
uuid: string;
|
|
201
|
-
title: string;
|
|
202
|
-
} | null>;
|
|
203
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { WorkspacePlugin } from '../plugins.js';
|
|
2
|
-
import type { EnsuredWorkspace, GaiaWorkspace } from './workspace.js';
|
|
3
|
-
export declare class FakeWorkspace implements GaiaWorkspace {
|
|
4
|
-
ensure(identifier: string, _title?: string): Promise<EnsuredWorkspace>;
|
|
5
|
-
}
|
|
6
|
-
export declare function fakeWorkspace(): WorkspacePlugin;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export class FakeWorkspace {
|
|
2
|
-
async ensure(identifier, _title) {
|
|
3
|
-
return { path: `/fake/${identifier}`, instructions: null, created: true };
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
export function fakeWorkspace() {
|
|
7
|
-
const workspace = new FakeWorkspace();
|
|
8
|
-
return {
|
|
9
|
-
kind: 'workspace',
|
|
10
|
-
id: 'fake',
|
|
11
|
-
requiredModules: [],
|
|
12
|
-
async createWorkspace(_config) {
|
|
13
|
-
return workspace;
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { WorkspacePlugin } from '../plugins.js';
|
|
2
|
-
import type { EnsuredWorkspace, GaiaWorkspace } from './workspace.js';
|
|
3
|
-
/** Default branch template: `<prefix><key>-<title-slug>` (readable). */
|
|
4
|
-
export declare const DEFAULT_BRANCH_TEMPLATE = "{branchPrefix}{key}-{titleSlug}";
|
|
5
|
-
export type GitRunner = (args: string[], cwd: string) => Promise<void>;
|
|
6
|
-
export interface GitWorkspaceOptions {
|
|
7
|
-
/** Main clone (a git repo): config source + worktree base. */
|
|
8
|
-
root: string;
|
|
9
|
-
/** Where per-ticket worktrees live. Default: `${root}-worktrees`. */
|
|
10
|
-
worktreesRoot?: string;
|
|
11
|
-
/** Branch name prefix per ticket. Default: 'gaia/'. */
|
|
12
|
-
branchPrefix?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Branch name template. Vars: {branchPrefix}, {key} (sanitized identifier),
|
|
15
|
-
* {identifier}, {titleSlug}. Default: `{branchPrefix}{key}-{titleSlug}`.
|
|
16
|
-
*/
|
|
17
|
-
branchTemplate?: string;
|
|
18
|
-
runGit?: GitRunner;
|
|
19
|
-
}
|
|
20
|
-
export declare class GitWorkspace implements GaiaWorkspace {
|
|
21
|
-
private readonly runGit;
|
|
22
|
-
private readonly root;
|
|
23
|
-
private readonly worktreesRoot;
|
|
24
|
-
private readonly branchPrefix;
|
|
25
|
-
private readonly branchTemplate;
|
|
26
|
-
constructor(options: GitWorkspaceOptions);
|
|
27
|
-
/** Render the branch name for a ticket from the configured template. */
|
|
28
|
-
branchFor(identifier: string, title?: string): string;
|
|
29
|
-
keyFor(identifier: string): string;
|
|
30
|
-
private pathFor;
|
|
31
|
-
ensure(identifier: string, title?: string, _baseRef?: string): Promise<EnsuredWorkspace>;
|
|
32
|
-
remove(identifier: string): Promise<void>;
|
|
33
|
-
}
|
|
34
|
-
export declare function gitWorkspace(opts?: {
|
|
35
|
-
branchPrefix?: string;
|
|
36
|
-
branchTemplate?: string;
|
|
37
|
-
}): WorkspacePlugin;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { dirname, join, resolve } from 'node:path';
|
|
3
|
-
import { exec } from '../../core/exec.js';
|
|
4
|
-
import { slugify } from '../../core/slug.js';
|
|
5
|
-
import { loadInstructions } from './instructions.js';
|
|
6
|
-
/** Default branch template: `<prefix><key>-<title-slug>` (readable). */
|
|
7
|
-
export const DEFAULT_BRANCH_TEMPLATE = '{branchPrefix}{key}-{titleSlug}';
|
|
8
|
-
function renderBranchTemplate(template, vars) {
|
|
9
|
-
const rendered = template.replace(/\{(\w+)\}/g, (whole, key) => {
|
|
10
|
-
const value = vars[key];
|
|
11
|
-
return value === undefined ? whole : value;
|
|
12
|
-
});
|
|
13
|
-
// An empty title slug leaves a dangling separator (e.g. `gaia/GL-9-`) —
|
|
14
|
-
// trim trailing branch separators so the branch stays a valid, tidy ref.
|
|
15
|
-
return rendered.replace(/[-_./]+$/g, '');
|
|
16
|
-
}
|
|
17
|
-
const defaultGitRunner = async (args, cwd) => {
|
|
18
|
-
await exec('git', args, { cwd });
|
|
19
|
-
};
|
|
20
|
-
export class GitWorkspace {
|
|
21
|
-
runGit;
|
|
22
|
-
root;
|
|
23
|
-
worktreesRoot;
|
|
24
|
-
branchPrefix;
|
|
25
|
-
branchTemplate;
|
|
26
|
-
constructor(options) {
|
|
27
|
-
this.runGit = options.runGit ?? defaultGitRunner;
|
|
28
|
-
this.root = resolve(options.root);
|
|
29
|
-
this.worktreesRoot = options.worktreesRoot
|
|
30
|
-
? resolve(options.worktreesRoot)
|
|
31
|
-
: `${this.root}-worktrees`;
|
|
32
|
-
this.branchPrefix = options.branchPrefix ?? 'gaia/';
|
|
33
|
-
this.branchTemplate = options.branchTemplate ?? DEFAULT_BRANCH_TEMPLATE;
|
|
34
|
-
}
|
|
35
|
-
/** Render the branch name for a ticket from the configured template. */
|
|
36
|
-
branchFor(identifier, title) {
|
|
37
|
-
return renderBranchTemplate(this.branchTemplate, {
|
|
38
|
-
branchPrefix: this.branchPrefix,
|
|
39
|
-
key: this.keyFor(identifier),
|
|
40
|
-
identifier,
|
|
41
|
-
titleSlug: slugify(title ?? ''),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
keyFor(identifier) {
|
|
45
|
-
return identifier.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
46
|
-
}
|
|
47
|
-
pathFor(key) {
|
|
48
|
-
return resolve(join(this.worktreesRoot, key));
|
|
49
|
-
}
|
|
50
|
-
async ensure(identifier, title, _baseRef) {
|
|
51
|
-
const key = this.keyFor(identifier);
|
|
52
|
-
// Reject empty and dot-leading keys: a dot-leading key would produce an
|
|
53
|
-
// invalid git ref (e.g. `gaia/.hidden`) and covers '.'/'..' too.
|
|
54
|
-
if (key === '' || key.startsWith('.')) {
|
|
55
|
-
throw new Error(`invalid workspace identifier: ${identifier}`);
|
|
56
|
-
}
|
|
57
|
-
const path = this.pathFor(key);
|
|
58
|
-
if (existsSync(path)) {
|
|
59
|
-
return { path, instructions: loadInstructions(path), created: false };
|
|
60
|
-
}
|
|
61
|
-
const branch = this.branchFor(identifier, title);
|
|
62
|
-
await this.runGit(['-C', this.root, 'worktree', 'add', '--force', '-B', branch, path], this.root);
|
|
63
|
-
// The `after_create` hook is run by the executor (GAIA-84), not here — the
|
|
64
|
-
// workspace only reports that it created a fresh worktree.
|
|
65
|
-
return { path, instructions: loadInstructions(path), created: true };
|
|
66
|
-
}
|
|
67
|
-
async remove(identifier) {
|
|
68
|
-
const key = this.keyFor(identifier);
|
|
69
|
-
const path = this.pathFor(key);
|
|
70
|
-
await this.runGit(['-C', this.root, 'worktree', 'remove', '--force', path], this.root);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
export function gitWorkspace(opts = {}) {
|
|
74
|
-
return {
|
|
75
|
-
kind: 'workspace',
|
|
76
|
-
id: 'git',
|
|
77
|
-
requiredModules: [],
|
|
78
|
-
async createWorkspace(config) {
|
|
79
|
-
const root = config.config_path
|
|
80
|
-
? dirname(config.config_path)
|
|
81
|
-
: process.cwd();
|
|
82
|
-
return new GitWorkspace({
|
|
83
|
-
root,
|
|
84
|
-
...(opts.branchPrefix ? { branchPrefix: opts.branchPrefix } : {}),
|
|
85
|
-
...(opts.branchTemplate ? { branchTemplate: opts.branchTemplate } : {}),
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
/** Loads ./WORKFLOW.md from the workspace, hashing its content. */
|
|
5
|
-
export function loadInstructions(workspacePath) {
|
|
6
|
-
const path = resolve(workspacePath, 'WORKFLOW.md');
|
|
7
|
-
if (!existsSync(path)) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
const text = readFileSync(path, 'utf8');
|
|
11
|
-
return {
|
|
12
|
-
path,
|
|
13
|
-
sha256: createHash('sha256').update(text).digest('hex'),
|
|
14
|
-
text,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export interface EnsuredWorkspace {
|
|
2
|
-
path: string;
|
|
3
|
-
instructions: {
|
|
4
|
-
path: string;
|
|
5
|
-
sha256: string;
|
|
6
|
-
text: string;
|
|
7
|
-
} | null;
|
|
8
|
-
/**
|
|
9
|
-
* Whether this call CREATED the worktree (true) or reused an existing one
|
|
10
|
-
* (false). The core runs the `after_create` lifecycle hook only on a fresh
|
|
11
|
-
* worktree, so it needs to know which happened. Lifecycle hooks themselves
|
|
12
|
-
* are no longer a workspace concern (GAIA-84): the executor owns and runs
|
|
13
|
-
* them best-effort.
|
|
14
|
-
*/
|
|
15
|
-
created: boolean;
|
|
16
|
-
}
|
|
17
|
-
export interface GaiaWorkspace {
|
|
18
|
-
/**
|
|
19
|
-
* Ensure a per-ticket worktree exists. `branch`, when given, is the branch
|
|
20
|
-
* name the dispatcher computed for the ticket. The git plugin treats it as a
|
|
21
|
-
* readable title to slug into its branch template; the herdr plugin uses it
|
|
22
|
-
* verbatim as the worktree branch. The worktree directory key/path stays
|
|
23
|
-
* identifier- or branch-derived so reuse is stable across runs.
|
|
24
|
-
*
|
|
25
|
-
* `baseRef`, when given, overrides the base the new branch is created from
|
|
26
|
-
* (e.g. `origin/<base_branch>` so a sub-ticket stacks on its parent); if it
|
|
27
|
-
* does not resolve on the remote the implementation falls back to its default
|
|
28
|
-
* base — never a hard error.
|
|
29
|
-
*
|
|
30
|
-
* Reports `created` so the caller can run the `after_create` hook only on a
|
|
31
|
-
* fresh worktree. Lifecycle-hook invocation is NOT a workspace responsibility
|
|
32
|
-
* anymore — the executor owns all hooks (GAIA-84).
|
|
33
|
-
*/
|
|
34
|
-
ensure(identifier: string, branch?: string, baseRef?: string): Promise<EnsuredWorkspace>;
|
|
35
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './plugins/registry-exports.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './plugins/registry-exports.js';
|