overlord-cli 0.2605211850.1 → 0.2605230834.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/bin/_cli/add-cwd.mjs +190 -0
- package/bin/_cli/index.mjs +7 -0
- package/bin/_cli/protocol.mjs +85 -31
- package/package.json +1 -1
- package/plugins/antigravity/scripts/overlord-mcp.mjs +137 -8
- package/plugins/antigravity/skills/overlord-ticket/SKILL.md +14 -11
- package/plugins/claude/skills/overlord-ticket/SKILL.md +24 -1
- package/plugins/claude/skills/overlord-ticket/reference/cli.md +6 -4
- package/plugins/claude/skills/overlord-ticket/reference/context.md +4 -4
- package/plugins/claude/skills/overlord-ticket/reference/mcp.md +3 -2
- package/plugins/cursor/skills/overlord-ticket/SKILL.md +24 -1
- package/plugins/cursor/skills/overlord-ticket/reference/cli.md +6 -4
- package/plugins/cursor/skills/overlord-ticket/reference/context.md +4 -4
- package/plugins/cursor/skills/overlord-ticket/reference/mcp.md +3 -2
- package/plugins/overlord/README.md +1 -0
- package/plugins/overlord/scripts/overlord-mcp.mjs +137 -8
- package/plugins/overlord/skills/overlord-ticket/SKILL.md +24 -1
- package/plugins/overlord/skills/overlord-ticket/reference/cli.md +6 -4
- package/plugins/overlord/skills/overlord-ticket/reference/context.md +4 -4
- package/plugins/overlord/skills/overlord-ticket/reference/mcp.md +3 -2
package/README.md
CHANGED
|
@@ -65,6 +65,8 @@ ovld prompt "Draft a fix for the onboarding flow"
|
|
|
65
65
|
ovld version
|
|
66
66
|
ovld update
|
|
67
67
|
ovld protocol discover-project
|
|
68
|
+
ovld protocol discover-project --project-id <project-id>
|
|
69
|
+
ovld protocol discover-project --working-directory /path/to/repo --device-fingerprint "$OVERLORD_DEVICE_FINGERPRINT"
|
|
68
70
|
ovld protocol attach --ticket-id <ticket_id>
|
|
69
71
|
ovld protocol search-tickets --query "auth refactor" --status next-up,execute
|
|
70
72
|
ovld protocol update --session-key <session-key> --ticket-id <ticket_id> --summary "Working on it" --phase execute
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// `ovld add-cwd` — interactively add the current working directory as a
|
|
4
|
+
// project resource. Lists the user's projects, prompts for a selection,
|
|
5
|
+
// registers cwd as a primary resource for this device, and writes the
|
|
6
|
+
// project entry into the directory's overlord.json file.
|
|
7
|
+
|
|
8
|
+
import os from 'node:os';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import readline from 'node:readline';
|
|
11
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
12
|
+
|
|
13
|
+
import { buildAuthHeaders, resolveAuth } from './credentials.mjs';
|
|
14
|
+
import { upsertLocalOverlordConfig } from './local-config.mjs';
|
|
15
|
+
import { parseNumberedSelection, sortProjects } from './new-ticket.mjs';
|
|
16
|
+
import { readOrCreateDeviceFingerprint } from './runner.mjs';
|
|
17
|
+
|
|
18
|
+
function parseFlags(args) {
|
|
19
|
+
const flags = {};
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
const arg = args[i];
|
|
22
|
+
if (!arg.startsWith('--')) continue;
|
|
23
|
+
const eqIdx = arg.indexOf('=');
|
|
24
|
+
if (eqIdx !== -1) {
|
|
25
|
+
flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const key = arg.slice(2);
|
|
29
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
30
|
+
flags[key] = args[i + 1];
|
|
31
|
+
i++;
|
|
32
|
+
} else {
|
|
33
|
+
flags[key] = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return flags;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function projectLabel(project) {
|
|
40
|
+
const organizationName = String(project.organizationName ?? '').trim();
|
|
41
|
+
return organizationName ? `${project.name} — ${organizationName}` : project.name;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function fetchProjects(platformUrl, bearerToken, localSecret, organizationId) {
|
|
45
|
+
const res = await fetch(`${platformUrl}/api/protocol/projects`, {
|
|
46
|
+
headers: buildAuthHeaders(bearerToken, localSecret, organizationId)
|
|
47
|
+
});
|
|
48
|
+
const data = await res.json().catch(() => ({}));
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Failed to list projects (${res.status}): ${data.error ?? JSON.stringify(data)}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return Array.isArray(data.projects) ? sortProjects(data.projects) : [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function promptForProject(projects, cwd) {
|
|
58
|
+
if (!projects.length) {
|
|
59
|
+
throw new Error('No projects available. Create a project in Overlord first.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const rl = readline.createInterface({ input, output });
|
|
63
|
+
const ask = q =>
|
|
64
|
+
new Promise((resolve, reject) => {
|
|
65
|
+
const onError = error => {
|
|
66
|
+
rl.off('error', onError);
|
|
67
|
+
reject(error);
|
|
68
|
+
};
|
|
69
|
+
rl.once('error', onError);
|
|
70
|
+
rl.question(q, answer => {
|
|
71
|
+
rl.off('error', onError);
|
|
72
|
+
resolve(answer);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
while (true) {
|
|
78
|
+
output.write(`\nCurrent directory:\n ${cwd}\n\nProjects:\n`);
|
|
79
|
+
projects.forEach((p, i) => {
|
|
80
|
+
output.write(` ${i + 1}. ${projectLabel(p)}\n`);
|
|
81
|
+
});
|
|
82
|
+
const answer = await ask(`\nSelect a project to register this directory with (1-${projects.length}, or 'q' to cancel): `);
|
|
83
|
+
if (answer.trim().toLowerCase() === 'q') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const idx = parseNumberedSelection(answer, projects.length);
|
|
87
|
+
if (idx !== null) return projects[idx];
|
|
88
|
+
output.write(`Enter a number between 1 and ${projects.length}.\n`);
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
rl.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function runAddCwdCommand(args) {
|
|
96
|
+
const flags = parseFlags(args);
|
|
97
|
+
|
|
98
|
+
if (flags.help === true || flags.h === true) {
|
|
99
|
+
console.log(`Usage: ovld add-cwd [--directory <path>] [--project-id <id>] [--primary]
|
|
100
|
+
|
|
101
|
+
Registers a directory (defaulting to the current working directory) as a
|
|
102
|
+
project resource for this device, and writes the project entry into the
|
|
103
|
+
directory's overlord.json file.
|
|
104
|
+
|
|
105
|
+
When --project-id is omitted, the command lists your projects and prompts
|
|
106
|
+
you to pick one.
|
|
107
|
+
|
|
108
|
+
By default the registered resource is marked primary for this device. Pass
|
|
109
|
+
--primary=false to skip that behaviour.`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const directoryPath = path.resolve(
|
|
114
|
+
typeof flags.directory === 'string' && flags.directory.trim()
|
|
115
|
+
? flags.directory.trim()
|
|
116
|
+
: process.cwd()
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const isPrimary = !(flags.primary === 'false' || flags.primary === false);
|
|
120
|
+
|
|
121
|
+
const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth({});
|
|
122
|
+
|
|
123
|
+
const projects = await fetchProjects(platformUrl, bearerToken, localSecret, organizationId);
|
|
124
|
+
let project = null;
|
|
125
|
+
if (typeof flags['project-id'] === 'string' && flags['project-id'].trim()) {
|
|
126
|
+
const wanted = flags['project-id'].trim();
|
|
127
|
+
project = projects.find(p => p.id === wanted) ?? null;
|
|
128
|
+
if (!project) {
|
|
129
|
+
console.error(`Error: project ${wanted} not found among your projects.`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
project = await promptForProject(projects, directoryPath);
|
|
134
|
+
if (!project) {
|
|
135
|
+
output.write('Cancelled. No changes made.\n');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const deviceFingerprint = readOrCreateDeviceFingerprint(flags);
|
|
141
|
+
|
|
142
|
+
const body = {
|
|
143
|
+
projectId: project.id,
|
|
144
|
+
directoryPath,
|
|
145
|
+
deviceFingerprint,
|
|
146
|
+
isPrimary,
|
|
147
|
+
deviceHostname: os.hostname(),
|
|
148
|
+
devicePlatform: process.platform
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const res = await fetch(`${platformUrl}/api/protocol/add-project-resource`, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: {
|
|
154
|
+
...buildAuthHeaders(bearerToken, localSecret, organizationId),
|
|
155
|
+
'Content-Type': 'application/json'
|
|
156
|
+
},
|
|
157
|
+
body: JSON.stringify(body)
|
|
158
|
+
});
|
|
159
|
+
const data = await res.json().catch(() => ({}));
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
if (res.status === 409) {
|
|
162
|
+
output.write(
|
|
163
|
+
`\nDirectory is already registered for "${project.name}" on this device. Updating overlord.json only.\n`
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
console.error(
|
|
167
|
+
`Failed to register resource (${res.status}): ${data.error ?? JSON.stringify(data)}`
|
|
168
|
+
);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const result = await upsertLocalOverlordConfig({
|
|
175
|
+
directoryPath,
|
|
176
|
+
project: { id: project.id, name: project.name }
|
|
177
|
+
});
|
|
178
|
+
output.write(`\nWrote ${result.filePath} (${result.action}).\n`);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
output.write(
|
|
181
|
+
`\nWarning: could not update overlord.json: ${err instanceof Error ? err.message : err}\n`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (data.resource?.id) {
|
|
186
|
+
output.write(`Registered resource ${data.resource.id} (primary=${data.resource.isPrimary}).\n`);
|
|
187
|
+
} else if (res.ok) {
|
|
188
|
+
output.write(`Registered ${directoryPath} for project "${project.name}".\n`);
|
|
189
|
+
}
|
|
190
|
+
}
|
package/bin/_cli/index.mjs
CHANGED
|
@@ -32,6 +32,7 @@ Usage:
|
|
|
32
32
|
${primaryCommand} attach [ticketId] [agent] Search tickets and launch an agent (interactive)
|
|
33
33
|
${primaryCommand} create "<objective>" Create a ticket with numbered project selection; supports --agent/--model/--delegate
|
|
34
34
|
${primaryCommand} prompt "<objective>" Create a ticket, then launch an agent on it
|
|
35
|
+
${primaryCommand} add-cwd Register the current directory as a project resource (interactive)
|
|
35
36
|
${primaryCommand} auth <subcommand> Login, logout, repair, or check auth status
|
|
36
37
|
${primaryCommand} tickets <subcommand> Create or list tickets
|
|
37
38
|
${primaryCommand} ticket <subcommand> Work with a single ticket
|
|
@@ -105,6 +106,12 @@ export async function runCli({ primaryCommand }) {
|
|
|
105
106
|
return;
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
if (command === 'add-cwd') {
|
|
110
|
+
const { runAddCwdCommand } = await import('./add-cwd.mjs');
|
|
111
|
+
await runAddCwdCommand(rest);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
108
115
|
// Auth group
|
|
109
116
|
if (command === 'auth') {
|
|
110
117
|
await runAuthCommand(rest[0], rest.slice(1));
|
package/bin/_cli/protocol.mjs
CHANGED
|
@@ -212,12 +212,49 @@ async function uploadToSignedUrl(uploadUrl, bytes, contentType, timeoutMs = DEFA
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Session file persistence — auto-resolve session key between CLI calls
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
function getSessionFilePath() {
|
|
220
|
+
return path.join(os.tmpdir(), `.overlord-session-${Buffer.from(process.cwd()).toString('base64url')}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function persistSession(sessionKey, ticketId) {
|
|
224
|
+
try {
|
|
225
|
+
fs.writeFileSync(
|
|
226
|
+
getSessionFilePath(),
|
|
227
|
+
JSON.stringify({ sessionKey, ticketId, ts: Date.now() }),
|
|
228
|
+
'utf8'
|
|
229
|
+
);
|
|
230
|
+
} catch { /* best-effort */ }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function readPersistedSession() {
|
|
234
|
+
try {
|
|
235
|
+
const raw = fs.readFileSync(getSessionFilePath(), 'utf8');
|
|
236
|
+
const data = JSON.parse(raw);
|
|
237
|
+
if (data.sessionKey && data.ticketId) return data;
|
|
238
|
+
} catch { /* missing or corrupt — ignore */ }
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
215
242
|
/** Read SESSION_KEY and TICKET_ID from env if flags not provided */
|
|
216
243
|
function resolveSessionFlags(flags) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
};
|
|
244
|
+
const sessionKey = String(flags['session-key'] ?? process.env.SESSION_KEY ?? '');
|
|
245
|
+
const ticketId = String(flags['ticket-id'] ?? process.env.TICKET_ID ?? '');
|
|
246
|
+
|
|
247
|
+
if (sessionKey && ticketId) return { sessionKey, ticketId };
|
|
248
|
+
|
|
249
|
+
const persisted = readPersistedSession();
|
|
250
|
+
if (persisted) {
|
|
251
|
+
return {
|
|
252
|
+
sessionKey: sessionKey || persisted.sessionKey,
|
|
253
|
+
ticketId: ticketId || persisted.ticketId
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { sessionKey, ticketId };
|
|
221
258
|
}
|
|
222
259
|
|
|
223
260
|
/** Resolve request timeout from --timeout flag or OVERLORD_TIMEOUT env var. */
|
|
@@ -944,6 +981,7 @@ async function protocolAttach(args) {
|
|
|
944
981
|
console.log(JSON.stringify(data, null, 2));
|
|
945
982
|
|
|
946
983
|
if (sessionKey) {
|
|
984
|
+
persistSession(sessionKey, ticketId);
|
|
947
985
|
// Emit a machine-readable line for easy shell capture:
|
|
948
986
|
// SESSION_KEY=$(ovld protocol attach --ticket-id ... | grep ^SESSION_KEY= | cut -d= -f2)
|
|
949
987
|
process.stderr.write(`\nSESSION_KEY=${sessionKey}\n`);
|
|
@@ -1367,8 +1405,11 @@ async function protocolDeliver(args) {
|
|
|
1367
1405
|
async function protocolAttachmentList(args) {
|
|
1368
1406
|
const flags = parseFlags(args);
|
|
1369
1407
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1408
|
+
const objectiveId = flags['objective-id'] ? String(flags['objective-id']) : '';
|
|
1370
1409
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1371
|
-
if (!ticketId
|
|
1410
|
+
if (!ticketId && !objectiveId) {
|
|
1411
|
+
throw new Error('--objective-id or --ticket-id is required');
|
|
1412
|
+
}
|
|
1372
1413
|
|
|
1373
1414
|
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1374
1415
|
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
@@ -1376,8 +1417,8 @@ async function protocolAttachmentList(args) {
|
|
|
1376
1417
|
|
|
1377
1418
|
const body = {
|
|
1378
1419
|
sessionKey,
|
|
1379
|
-
ticketId,
|
|
1380
|
-
...(
|
|
1420
|
+
...(ticketId ? { ticketId } : {}),
|
|
1421
|
+
...(objectiveId ? { objectiveId } : {})
|
|
1381
1422
|
};
|
|
1382
1423
|
|
|
1383
1424
|
const data = await apiPost(
|
|
@@ -1396,7 +1437,6 @@ async function protocolAttachmentPrepareUpload(args) {
|
|
|
1396
1437
|
const flags = parseFlags(args);
|
|
1397
1438
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1398
1439
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1399
|
-
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1400
1440
|
const objectiveId = requireFlag(flags, 'objective-id', undefined);
|
|
1401
1441
|
const fileName = requireFlag(flags, 'file-name', undefined);
|
|
1402
1442
|
|
|
@@ -1406,7 +1446,7 @@ async function protocolAttachmentPrepareUpload(args) {
|
|
|
1406
1446
|
|
|
1407
1447
|
const body = {
|
|
1408
1448
|
sessionKey,
|
|
1409
|
-
ticketId,
|
|
1449
|
+
...(ticketId ? { ticketId } : {}),
|
|
1410
1450
|
objectiveId,
|
|
1411
1451
|
fileName,
|
|
1412
1452
|
...(flags.label ? { label: String(flags.label) } : {}),
|
|
@@ -1433,7 +1473,6 @@ async function protocolAttachmentFinalizeUpload(args) {
|
|
|
1433
1473
|
const flags = parseFlags(args);
|
|
1434
1474
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1435
1475
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1436
|
-
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1437
1476
|
const objectiveId = requireFlag(flags, 'objective-id', undefined);
|
|
1438
1477
|
const storagePath = requireFlag(flags, 'storage-path', undefined);
|
|
1439
1478
|
const label = requireFlag(flags, 'label', undefined);
|
|
@@ -1444,7 +1483,7 @@ async function protocolAttachmentFinalizeUpload(args) {
|
|
|
1444
1483
|
|
|
1445
1484
|
const body = {
|
|
1446
1485
|
sessionKey,
|
|
1447
|
-
ticketId,
|
|
1486
|
+
...(ticketId ? { ticketId } : {}),
|
|
1448
1487
|
objectiveId,
|
|
1449
1488
|
storagePath,
|
|
1450
1489
|
label,
|
|
@@ -1471,7 +1510,6 @@ async function protocolAttachmentGetDownloadUrl(args) {
|
|
|
1471
1510
|
const flags = parseFlags(args);
|
|
1472
1511
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1473
1512
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1474
|
-
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1475
1513
|
if (!flags['attachment-id'] && !flags['storage-path']) {
|
|
1476
1514
|
throw new Error('--attachment-id or --storage-path is required');
|
|
1477
1515
|
}
|
|
@@ -1485,7 +1523,7 @@ async function protocolAttachmentGetDownloadUrl(args) {
|
|
|
1485
1523
|
|
|
1486
1524
|
const body = {
|
|
1487
1525
|
sessionKey,
|
|
1488
|
-
ticketId,
|
|
1526
|
+
...(ticketId ? { ticketId } : {}),
|
|
1489
1527
|
...(flags['objective-id'] ? { objectiveId: String(flags['objective-id']) } : {}),
|
|
1490
1528
|
...(flags['attachment-id'] ? { attachmentId: String(flags['attachment-id']) } : {}),
|
|
1491
1529
|
...(flags['storage-path'] ? { storagePath: String(flags['storage-path']) } : {}),
|
|
@@ -1508,7 +1546,6 @@ async function protocolAttachmentUploadFile(args) {
|
|
|
1508
1546
|
const flags = parseFlags(args);
|
|
1509
1547
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1510
1548
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1511
|
-
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1512
1549
|
const objectiveId = requireFlag(flags, 'objective-id', undefined);
|
|
1513
1550
|
const filePath = requireFlag(flags, 'file', undefined);
|
|
1514
1551
|
|
|
@@ -1537,7 +1574,7 @@ async function protocolAttachmentUploadFile(args) {
|
|
|
1537
1574
|
'/api/protocol/attachments/prepare-upload',
|
|
1538
1575
|
{
|
|
1539
1576
|
sessionKey,
|
|
1540
|
-
ticketId,
|
|
1577
|
+
...(ticketId ? { ticketId } : {}),
|
|
1541
1578
|
objectiveId,
|
|
1542
1579
|
fileName,
|
|
1543
1580
|
label,
|
|
@@ -1564,7 +1601,7 @@ async function protocolAttachmentUploadFile(args) {
|
|
|
1564
1601
|
'/api/protocol/attachments/finalize-upload',
|
|
1565
1602
|
{
|
|
1566
1603
|
sessionKey,
|
|
1567
|
-
ticketId,
|
|
1604
|
+
...(ticketId ? { ticketId } : {}),
|
|
1568
1605
|
objectiveId,
|
|
1569
1606
|
storagePath,
|
|
1570
1607
|
label,
|
|
@@ -1588,7 +1625,8 @@ async function protocolDiscoverProject(args) {
|
|
|
1588
1625
|
await resolveProtocolAuthForFlags(flags);
|
|
1589
1626
|
const timeoutMs = resolveTimeout(flags);
|
|
1590
1627
|
|
|
1591
|
-
const workingDirectory =
|
|
1628
|
+
const workingDirectory =
|
|
1629
|
+
flags['working-directory'] !== undefined ? String(flags['working-directory']) : process.cwd();
|
|
1592
1630
|
|
|
1593
1631
|
const data = await apiPost(
|
|
1594
1632
|
platformUrl,
|
|
@@ -1596,7 +1634,14 @@ async function protocolDiscoverProject(args) {
|
|
|
1596
1634
|
localSecret,
|
|
1597
1635
|
organizationId,
|
|
1598
1636
|
'/api/protocol/discover-project',
|
|
1599
|
-
{
|
|
1637
|
+
{
|
|
1638
|
+
...(flags['project-id'] ? { projectId: String(flags['project-id']) } : { workingDirectory }),
|
|
1639
|
+
...(flags['device-fingerprint']
|
|
1640
|
+
? { deviceFingerprint: String(flags['device-fingerprint']) }
|
|
1641
|
+
: {}),
|
|
1642
|
+
...(flags['device-hostname'] ? { deviceHostname: String(flags['device-hostname']) } : {}),
|
|
1643
|
+
...(flags['device-platform'] ? { devicePlatform: String(flags['device-platform']) } : {})
|
|
1644
|
+
},
|
|
1600
1645
|
timeoutMs
|
|
1601
1646
|
);
|
|
1602
1647
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -2019,6 +2064,7 @@ async function protocolConnect(args) {
|
|
|
2019
2064
|
console.log(JSON.stringify(data, null, 2));
|
|
2020
2065
|
|
|
2021
2066
|
if (sessionKey) {
|
|
2067
|
+
persistSession(sessionKey, ticketId);
|
|
2022
2068
|
process.stderr.write(`\nSESSION_KEY=${sessionKey}\n`);
|
|
2023
2069
|
}
|
|
2024
2070
|
}
|
|
@@ -2145,6 +2191,7 @@ async function protocolPrompt(args) {
|
|
|
2145
2191
|
console.log(JSON.stringify(data, null, 2));
|
|
2146
2192
|
|
|
2147
2193
|
if (sessionKey) {
|
|
2194
|
+
if (ticketId) persistSession(sessionKey, ticketId);
|
|
2148
2195
|
process.stderr.write(`\nSESSION_KEY=${sessionKey}\n`);
|
|
2149
2196
|
}
|
|
2150
2197
|
if (ticketId) {
|
|
@@ -2493,9 +2540,10 @@ Project discovery:
|
|
|
2493
2540
|
You can also discover the project explicitly:
|
|
2494
2541
|
|
|
2495
2542
|
ovld protocol discover-project
|
|
2543
|
+
ovld protocol discover-project --project-id <project-uuid>
|
|
2496
2544
|
ovld protocol discover-project --working-directory /path/to/repo
|
|
2497
2545
|
|
|
2498
|
-
Use --project-id to override automatic resolution on prompt or ticket creation.
|
|
2546
|
+
Use --project-id to override automatic resolution on discover, prompt, or ticket creation.
|
|
2499
2547
|
Use --personal to create a private ticket without assigning any project.
|
|
2500
2548
|
|
|
2501
2549
|
Subcommands:
|
|
@@ -2536,8 +2584,8 @@ Subcommands:
|
|
|
2536
2584
|
update-project-resource Update a resource directory's path, label, or primary status
|
|
2537
2585
|
|
|
2538
2586
|
Environment fallback:
|
|
2539
|
-
--session-key <- SESSION_KEY
|
|
2540
|
-
--ticket-id <- TICKET_ID (human-readable ticket_id, e.g. 1:899)
|
|
2587
|
+
--session-key <- SESSION_KEY or auto-persisted session from last attach/connect/prompt in this working directory
|
|
2588
|
+
--ticket-id <- TICKET_ID or auto-persisted session (human-readable ticket_id, e.g. 1:899)
|
|
2541
2589
|
auth/host <- OVERLORD_URL, optional OVERLORD_ACCESS_TOKEN + OVERLORD_ORGANIZATION_ID, or shared OAuth credentials from ovld auth/Desktop login
|
|
2542
2590
|
--timeout <- OVERLORD_TIMEOUT
|
|
2543
2591
|
|
|
@@ -2563,7 +2611,11 @@ discover-project:
|
|
|
2563
2611
|
Resolve the Overlord project that corresponds to the current (or given) working directory.
|
|
2564
2612
|
Uses the caller's configured "Local working directory" for matching.
|
|
2565
2613
|
Optional:
|
|
2614
|
+
--project-id <uuid> Resolve this project directly; skips working-directory matching
|
|
2566
2615
|
--working-directory <path> Directory to match (default: current working directory)
|
|
2616
|
+
--device-fingerprint <fp> Prefer resource directories for this registered device
|
|
2617
|
+
--device-hostname <name> Register/update device metadata when fingerprint is provided
|
|
2618
|
+
--device-platform <platform>
|
|
2567
2619
|
Returns:
|
|
2568
2620
|
Project JSON with id, name, organizationId. Prints PROJECT_ID=<id> on stderr.
|
|
2569
2621
|
Notes:
|
|
@@ -2860,19 +2912,20 @@ record-work:
|
|
|
2860
2912
|
attachment-list:
|
|
2861
2913
|
Required:
|
|
2862
2914
|
--session-key <key>
|
|
2863
|
-
--ticket-id <ticket_id>
|
|
2915
|
+
one of: --objective-id <id> | --ticket-id <ticket_id>
|
|
2864
2916
|
Optional:
|
|
2865
|
-
--objective-id <id> Filter to a single objective
|
|
2917
|
+
--objective-id <id> Filter to a single objective (also acts as ticket scope)
|
|
2918
|
+
--ticket-id <ticket_id> List every attachment for a ticket
|
|
2866
2919
|
Returns:
|
|
2867
2920
|
JSON array of { id, label, content_type, file_size, objective_id, storage_path, created_at }
|
|
2868
2921
|
|
|
2869
2922
|
attachment-prepare-upload:
|
|
2870
2923
|
Required:
|
|
2871
2924
|
--session-key <key>
|
|
2872
|
-
--ticket-id <ticket_id>
|
|
2873
2925
|
--objective-id <id>
|
|
2874
2926
|
--file-name <name>
|
|
2875
2927
|
Optional:
|
|
2928
|
+
--ticket-id <ticket_id> Derived from --objective-id when omitted
|
|
2876
2929
|
--label <text>
|
|
2877
2930
|
--content-type <mime>
|
|
2878
2931
|
--file-size <bytes>
|
|
@@ -2881,11 +2934,11 @@ attachment-prepare-upload:
|
|
|
2881
2934
|
attachment-finalize-upload:
|
|
2882
2935
|
Required:
|
|
2883
2936
|
--session-key <key>
|
|
2884
|
-
--ticket-id <ticket_id>
|
|
2885
2937
|
--objective-id <id>
|
|
2886
2938
|
--storage-path <path>
|
|
2887
2939
|
--label <text>
|
|
2888
2940
|
Optional:
|
|
2941
|
+
--ticket-id <ticket_id> Derived from --objective-id when omitted
|
|
2889
2942
|
--content-type <mime>
|
|
2890
2943
|
--file-size <bytes>
|
|
2891
2944
|
--metadata-json <json>
|
|
@@ -2893,19 +2946,19 @@ attachment-finalize-upload:
|
|
|
2893
2946
|
attachment-download-url:
|
|
2894
2947
|
Required:
|
|
2895
2948
|
--session-key <key>
|
|
2896
|
-
--ticket-id <ticket_id>
|
|
2897
2949
|
one of: --attachment-id <id> | --storage-path <path>
|
|
2898
2950
|
Optional:
|
|
2951
|
+
--ticket-id <ticket_id> Derived from --attachment-id / --objective-id when omitted
|
|
2899
2952
|
--objective-id <id> Required when using --storage-path
|
|
2900
2953
|
--expires-in <seconds>
|
|
2901
2954
|
|
|
2902
2955
|
attachment-upload-file:
|
|
2903
2956
|
Required:
|
|
2904
2957
|
--session-key <key>
|
|
2905
|
-
--ticket-id <ticket_id>
|
|
2906
2958
|
--objective-id <id>
|
|
2907
2959
|
--file <path>
|
|
2908
2960
|
Optional:
|
|
2961
|
+
--ticket-id <ticket_id> Derived from --objective-id when omitted
|
|
2909
2962
|
--file-name <name> Defaults to basename of --file
|
|
2910
2963
|
--label <text> Defaults to file name
|
|
2911
2964
|
--content-type <mime> Defaults to application/octet-stream
|
|
@@ -2993,6 +3046,7 @@ fail-execution-launch:
|
|
|
2993
3046
|
Examples:
|
|
2994
3047
|
ovld protocol auth-status
|
|
2995
3048
|
ovld protocol discover-project
|
|
3049
|
+
ovld protocol discover-project --project-id <project-uuid>
|
|
2996
3050
|
ovld protocol discover-project --working-directory /path/to/repo
|
|
2997
3051
|
ovld protocol prompt --agent codex --objective "Implement feature X" # auto-resolves project from cwd
|
|
2998
3052
|
ovld protocol attach --ticket-id 1:899
|
|
@@ -3015,10 +3069,10 @@ Examples:
|
|
|
3015
3069
|
ovld protocol hook-event --hook-type UserPromptSubmit --ticket-id <ticket_id> --prompt "User follow-up" --turn-index 1
|
|
3016
3070
|
ovld protocol read-context --session-key <key> --ticket-id <ticket_id> --query arch --limit 5
|
|
3017
3071
|
ovld protocol write-context --session-key <key> --ticket-id <ticket_id> --key "arch" --value '"monorepo"' --tags repo,agent
|
|
3018
|
-
ovld protocol attachment-list --session-key <key> --
|
|
3019
|
-
ovld protocol attachment-prepare-upload --session-key <key> --
|
|
3020
|
-
ovld protocol attachment-upload-file --session-key <key> --
|
|
3021
|
-
ovld protocol attachment-download-url --session-key <key> --
|
|
3072
|
+
ovld protocol attachment-list --session-key <key> --objective-id <objective-id>
|
|
3073
|
+
ovld protocol attachment-prepare-upload --session-key <key> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
|
|
3074
|
+
ovld protocol attachment-upload-file --session-key <key> --objective-id <objective-id> --file ./spec.pdf
|
|
3075
|
+
ovld protocol attachment-download-url --session-key <key> --attachment-id <attachment-id>
|
|
3022
3076
|
ovld protocol request-execution --ticket-id 1:899 --agent codex --requested-from manual_run
|
|
3023
3077
|
ovld protocol claim-execution --device-fingerprint $OVERLORD_DEVICE_FINGERPRINT
|
|
3024
3078
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
|