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 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
+ }
@@ -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));
@@ -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
- return {
218
- sessionKey: String(flags['session-key'] ?? process.env.SESSION_KEY ?? ''),
219
- ticketId: String(flags['ticket-id'] ?? process.env.TICKET_ID ?? '')
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) throw new Error('--ticket-id is required (or set TICKET_ID)');
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
- ...(flags['objective-id'] ? { objectiveId: String(flags['objective-id']) } : {})
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 = String(flags['working-directory'] ?? process.cwd());
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
- { workingDirectory },
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> --ticket-id <ticket_id>
3019
- ovld protocol attachment-prepare-upload --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
3020
- ovld protocol attachment-upload-file --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file ./spec.pdf
3021
- ovld protocol attachment-download-url --session-key <key> --ticket-id <ticket_id> --attachment-id <attachment-id>
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2605211850.1",
3
+ "version": "0.2605230834.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "homepage": "https://www.ovld.ai/docs/",
6
6
  "type": "module",