overlord-cli 0.2605101225.0 → 0.2605111015.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
@@ -74,6 +74,8 @@ ovld setup all
74
74
  ovld doctor
75
75
  ```
76
76
 
77
+ For ticket-scoped protocol and launch commands, `ticket_id` values such as `1:899` carry the organization id. The CLI uses that first, then `--organization-id` for UUID compatibility, then stored auth.
78
+
77
79
  ## Requirements
78
80
 
79
81
  - Node.js 20 or newer
@@ -520,11 +520,20 @@ function isLocalDevCli() {
520
520
  return !sourcePath.split(path.sep).includes('node_modules');
521
521
  }
522
522
 
523
+ function parseOrganizationIdHint(value) {
524
+ if (typeof value !== 'string' && typeof value !== 'number') return null;
525
+ const parsed = Number.parseInt(String(value), 10);
526
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
527
+ }
528
+
523
529
  /**
524
530
  * Resolve the Overlord auth session from env vars or shared credentials.
525
531
  * Refreshes OAuth access tokens when possible.
532
+ *
533
+ * @param {{ organizationIdHint?: number | string | null }} [options]
526
534
  */
527
- export async function resolveAuth() {
535
+ export async function resolveAuth(options = {}) {
536
+ const organizationIdHint = parseOrganizationIdHint(options.organizationIdHint);
528
537
  const selectedCredentials = selectStoredCredentials();
529
538
  const creds = selectedCredentials?.credentials ?? migrateLegacyCredentials();
530
539
  const overlordUrlFromEnv = normalizePlatformUrl(process.env.OVERLORD_URL);
@@ -548,7 +557,8 @@ export async function resolveAuth() {
548
557
  typeof process.env.OVERLORD_ORGANIZATION_ID === 'string'
549
558
  ? Number.parseInt(process.env.OVERLORD_ORGANIZATION_ID, 10)
550
559
  : null;
551
- if (!Number.isFinite(envOrganizationId)) {
560
+ const resolvedOrganizationId = organizationIdHint ?? envOrganizationId;
561
+ if (!Number.isFinite(resolvedOrganizationId)) {
552
562
  throw new Error(
553
563
  'OVERLORD_ACCESS_TOKEN requires OVERLORD_ORGANIZATION_ID so protocol requests stay scoped.'
554
564
  );
@@ -559,7 +569,7 @@ export async function resolveAuth() {
559
569
  platformUrl,
560
570
  bearerToken: envAccessToken,
561
571
  localSecret,
562
- organizationId: envOrganizationId,
572
+ organizationId: resolvedOrganizationId,
563
573
  authMode: 'oauth_env'
564
574
  };
565
575
  }
@@ -606,7 +616,7 @@ export async function resolveAuth() {
606
616
  platformUrl,
607
617
  bearerToken: nextCredentials.access_token,
608
618
  localSecret,
609
- organizationId: nextCredentials.organization_id,
619
+ organizationId: organizationIdHint ?? nextCredentials.organization_id,
610
620
  authMode: 'oauth'
611
621
  };
612
622
  }
@@ -72,7 +72,6 @@ const agentIdentifierMap = {
72
72
  };
73
73
 
74
74
  const supportedAgents = ['claude', 'codex', 'cursor', 'gemini', 'opencode'];
75
- const TICKET_ID_REGEX = /^(\d+):\d+$/;
76
75
 
77
76
  function shellQuote(value) {
78
77
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
@@ -112,14 +111,17 @@ function parseOrganizationId(value) {
112
111
  }
113
112
 
114
113
  function organizationIdFromTicketId(ticketId) {
115
- const match = String(ticketId ?? '').trim().match(TICKET_ID_REGEX);
116
- return match ? parseOrganizationId(match[1]) : null;
114
+ const [organizationPart, _ticketSequencePart, ...rest] = String(ticketId ?? '')
115
+ .trim()
116
+ .split(':');
117
+ if (rest.length > 0) return null;
118
+ return parseOrganizationId(organizationPart);
117
119
  }
118
120
 
119
121
  function resolveLaunchOrganizationId(ticketId, optionsOrganizationId, authOrganizationId) {
120
122
  return (
121
- parseOrganizationId(optionsOrganizationId) ??
122
123
  organizationIdFromTicketId(ticketId) ??
124
+ parseOrganizationId(optionsOrganizationId) ??
123
125
  parseOrganizationId(authOrganizationId)
124
126
  );
125
127
  }
@@ -278,8 +280,15 @@ async function runAgent(agent, mode = 'run', options = {}) {
278
280
  process.chdir(options.workingDirectory);
279
281
  }
280
282
 
281
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
282
283
  const launchOrganizationId = resolveLaunchOrganizationId(
284
+ ticketId,
285
+ options.organizationId,
286
+ null
287
+ );
288
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth({
289
+ organizationIdHint: launchOrganizationId
290
+ });
291
+ const resolvedLaunchOrganizationId = resolveLaunchOrganizationId(
283
292
  ticketId,
284
293
  options.organizationId,
285
294
  organizationId
@@ -287,7 +296,7 @@ async function runAgent(agent, mode = 'run', options = {}) {
287
296
  if (options.sshCommand?.trim()) {
288
297
  const remoteCommand = buildRemoteLaunchCommand(agent, {
289
298
  ...options,
290
- organizationId: launchOrganizationId
299
+ organizationId: resolvedLaunchOrganizationId
291
300
  });
292
301
  try {
293
302
  execFileSync('sh', ['-lc', remoteCommand], { stdio: 'inherit', env: process.env });
@@ -303,7 +312,7 @@ async function runAgent(agent, mode = 'run', options = {}) {
303
312
  platformUrl,
304
313
  bearerToken,
305
314
  localSecret,
306
- launchOrganizationId,
315
+ resolvedLaunchOrganizationId,
307
316
  ticketId,
308
317
  agent
309
318
  );
@@ -311,7 +320,9 @@ async function runAgent(agent, mode = 'run', options = {}) {
311
320
  const childEnv = {
312
321
  ...process.env,
313
322
  AGENT_IDENTIFIER: agentIdentifierMap[agent],
314
- ...(launchOrganizationId ? { OVERLORD_ORGANIZATION_ID: String(launchOrganizationId) } : {})
323
+ ...(resolvedLaunchOrganizationId
324
+ ? { OVERLORD_ORGANIZATION_ID: String(resolvedLaunchOrganizationId) }
325
+ : {})
315
326
  };
316
327
  const extraArgs = buildExtraArgs(agent, options);
317
328
 
@@ -7,6 +7,7 @@ import path from 'node:path';
7
7
 
8
8
  import { buildAuthHeaders, getAuthStatus, resolveAuth } from './credentials.mjs';
9
9
 
10
+
10
11
  /**
11
12
  * Parse simple CLI flags: --key value or --key=value
12
13
  * @param {string[]} args
@@ -69,7 +70,7 @@ export function resolveProtocolModelIdentifier(flags = {}) {
69
70
  return envModel || null;
70
71
  }
71
72
 
72
- function resolveProtocolMetadata(flags = {}, base = {}) {
73
+ async function resolveProtocolMetadata(flags = {}, base = {}) {
73
74
  const metadata = { ...base };
74
75
 
75
76
  if (flags['metadata-json']) {
@@ -85,6 +86,11 @@ function resolveProtocolMetadata(flags = {}, base = {}) {
85
86
  metadata.model = modelIdentifier;
86
87
  }
87
88
 
89
+ const snapshot = await resolveSnapshotContext(flags);
90
+ if (snapshot) {
91
+ metadata.snapshot = snapshot;
92
+ }
93
+
88
94
  return metadata;
89
95
  }
90
96
 
@@ -233,6 +239,28 @@ function requireFlag(flags, name, envAlias) {
233
239
  return String(value);
234
240
  }
235
241
 
242
+ function parseOrganizationId(value) {
243
+ if (typeof value !== 'string' && typeof value !== 'number') return null;
244
+ const parsed = Number.parseInt(String(value), 10);
245
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
246
+ }
247
+
248
+ function organizationIdFromTicketId(ticketId) {
249
+ const [organizationPart, _ticketSequencePart, ...rest] = String(ticketId ?? '')
250
+ .trim()
251
+ .split(':');
252
+ if (rest.length > 0) return null;
253
+ return parseOrganizationId(organizationPart);
254
+ }
255
+
256
+ function resolveOrganizationIdHint(flags, ticketId) {
257
+ return organizationIdFromTicketId(ticketId) ?? parseOrganizationId(flags['organization-id']);
258
+ }
259
+
260
+ async function resolveProtocolAuthForFlags(flags, ticketId = '') {
261
+ return resolveAuth({ organizationIdHint: resolveOrganizationIdHint(flags, ticketId) });
262
+ }
263
+
236
264
  function parseJsonFlag(flagName, rawValue) {
237
265
  try {
238
266
  return JSON.parse(String(rawValue));
@@ -318,6 +346,19 @@ async function resolveChangeRationales(flags) {
318
346
  return [];
319
347
  }
320
348
 
349
+ async function resolveSnapshotContext(flags) {
350
+ if (flags['snapshot-file']) {
351
+ return await readJsonFileOrStdin(String(flags['snapshot-file']), '--snapshot-file');
352
+ }
353
+ if (flags['snapshot-json']) {
354
+ return parseJsonFlag('--snapshot-json', flags['snapshot-json']);
355
+ }
356
+ if (process.env.OVERLORD_SNAPSHOT_JSON?.trim()) {
357
+ return parseJsonFlag('--snapshot-json', process.env.OVERLORD_SNAPSHOT_JSON);
358
+ }
359
+ return null;
360
+ }
361
+
321
362
  function normalizeRepoRelativeFilePath(filePath, repoRoot) {
322
363
  if (typeof filePath !== 'string') return null;
323
364
 
@@ -493,7 +534,7 @@ function resolveExternalSessionId(flags) {
493
534
  async function protocolAttach(args) {
494
535
  const flags = parseFlags(args);
495
536
  const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
496
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
537
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
497
538
  const timeoutMs = resolveTimeout(flags);
498
539
 
499
540
  const externalSessionId = resolveExternalSessionId(flags);
@@ -503,7 +544,7 @@ async function protocolAttach(args) {
503
544
  agentIdentifier: resolveProtocolAgentIdentifier(flags),
504
545
  connectionMethod: String(flags.method ?? 'cli'),
505
546
  ...(externalSessionId !== undefined ? { externalSessionId } : {}),
506
- metadata: resolveProtocolMetadata(flags, { cwd: process.cwd() })
547
+ metadata: await resolveProtocolMetadata(flags, { cwd: process.cwd() })
507
548
  };
508
549
 
509
550
  const data = await apiPost(
@@ -539,10 +580,11 @@ async function protocolUpdate(args) {
539
580
  ? readTextFile(String(flags['summary-file']), '--summary-file')
540
581
  : requireFlag(flags, 'summary', undefined);
541
582
 
542
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
583
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
543
584
  const timeoutMs = resolveTimeout(flags);
544
585
  const changeRationales = await resolveChangeRationales(flags);
545
586
  const externalSessionId = resolveExternalSessionId(flags);
587
+ const snapshot = await resolveSnapshotContext(flags);
546
588
 
547
589
  const body = {
548
590
  sessionKey,
@@ -562,6 +604,7 @@ async function protocolUpdate(args) {
562
604
  ...(flags['payload-json']
563
605
  ? { payload: parseJsonFlag('--payload-json', flags['payload-json']) }
564
606
  : {}),
607
+ ...(snapshot ? { snapshot } : {}),
565
608
  ...(changeRationales.length > 0 ? { changeRationales } : {})
566
609
  };
567
610
 
@@ -594,8 +637,9 @@ async function protocolRecordChangeRationales(args) {
594
637
  );
595
638
  }
596
639
 
597
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
640
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
598
641
  const timeoutMs = resolveTimeout(flags);
642
+ const snapshot = await resolveSnapshotContext(flags);
599
643
 
600
644
  const body = {
601
645
  sessionKey,
@@ -606,7 +650,8 @@ async function protocolRecordChangeRationales(args) {
606
650
  : flags.summary
607
651
  ? { summary: String(flags.summary) }
608
652
  : {}),
609
- ...(flags.phase ? { phase: String(flags.phase) } : {})
653
+ ...(flags.phase ? { phase: String(flags.phase) } : {}),
654
+ ...(snapshot ? { snapshot } : {})
610
655
  };
611
656
 
612
657
  const data = await apiPost(
@@ -634,7 +679,7 @@ async function protocolAsk(args) {
634
679
  ? readTextFile(String(flags['question-file']), '--question-file')
635
680
  : requireFlag(flags, 'question', undefined);
636
681
 
637
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
682
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
638
683
  const timeoutMs = resolveTimeout(flags);
639
684
 
640
685
  const body = {
@@ -670,7 +715,7 @@ async function protocolPermissionRequest(args) {
670
715
  ? await readJsonFileOrStdin(String(flags['payload-file']), '--payload-file')
671
716
  : {};
672
717
 
673
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
718
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
674
719
  const timeoutMs = resolveTimeout(flags);
675
720
 
676
721
  const data = await apiPost(
@@ -695,7 +740,7 @@ async function protocolReadContext(args) {
695
740
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
696
741
  if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
697
742
 
698
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
743
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
699
744
  const timeoutMs = resolveTimeout(flags);
700
745
 
701
746
  const body = {
@@ -739,7 +784,7 @@ async function protocolWriteContext(args) {
739
784
  value = String(flags.value);
740
785
  }
741
786
 
742
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
787
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
743
788
  const timeoutMs = resolveTimeout(flags);
744
789
 
745
790
  const body = {
@@ -786,7 +831,7 @@ async function protocolDeliver(args) {
786
831
  ? readTextFile(String(flags['summary-file']), '--summary-file')
787
832
  : requireFlag(flags, 'summary', undefined));
788
833
 
789
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
834
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
790
835
  const timeoutMs = resolveTimeout(flags);
791
836
 
792
837
  let artifacts = deliverPayload?.artifacts ?? [];
@@ -808,6 +853,7 @@ async function protocolDeliver(args) {
808
853
 
809
854
  const changeRationales =
810
855
  deliverPayload?.changeRationales ?? (await resolveChangeRationales(flags));
856
+ const snapshot = (await resolveSnapshotContext(flags)) ?? deliverPayload?.snapshot ?? null;
811
857
  validateDeliverFileChanges(flags, changeRationales);
812
858
 
813
859
  const body = {
@@ -815,6 +861,7 @@ async function protocolDeliver(args) {
815
861
  ticketId,
816
862
  summary,
817
863
  artifacts,
864
+ ...(snapshot ? { snapshot } : {}),
818
865
  ...(changeRationales.length > 0 ? { changeRationales } : {})
819
866
  };
820
867
 
@@ -840,7 +887,7 @@ async function protocolAttachmentList(args) {
840
887
  if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
841
888
  if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
842
889
 
843
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
890
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
844
891
  const timeoutMs = resolveTimeout(flags);
845
892
 
846
893
  const body = {
@@ -869,7 +916,7 @@ async function protocolAttachmentPrepareUpload(args) {
869
916
  const objectiveId = requireFlag(flags, 'objective-id', undefined);
870
917
  const fileName = requireFlag(flags, 'file-name', undefined);
871
918
 
872
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
919
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
873
920
  const timeoutMs = resolveTimeout(flags);
874
921
 
875
922
  const body = {
@@ -906,7 +953,7 @@ async function protocolAttachmentFinalizeUpload(args) {
906
953
  const storagePath = requireFlag(flags, 'storage-path', undefined);
907
954
  const label = requireFlag(flags, 'label', undefined);
908
955
 
909
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
956
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
910
957
  const timeoutMs = resolveTimeout(flags);
911
958
 
912
959
  const body = {
@@ -946,7 +993,7 @@ async function protocolAttachmentGetDownloadUrl(args) {
946
993
  throw new Error('--objective-id is required when using --storage-path');
947
994
  }
948
995
 
949
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
996
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
950
997
  const timeoutMs = resolveTimeout(flags);
951
998
 
952
999
  const body = {
@@ -987,7 +1034,7 @@ async function protocolAttachmentUploadFile(args) {
987
1034
  const fileStats = await stat(filePath);
988
1035
  const fileBytes = await readFile(filePath);
989
1036
 
990
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1037
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
991
1038
  const timeoutMs = resolveTimeout(flags);
992
1039
 
993
1040
  const metadata = flags['metadata-json']
@@ -1049,7 +1096,8 @@ async function protocolAttachmentUploadFile(args) {
1049
1096
 
1050
1097
  async function protocolDiscoverProject(args) {
1051
1098
  const flags = parseFlags(args);
1052
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1099
+ const { platformUrl, bearerToken, localSecret, organizationId } =
1100
+ await resolveProtocolAuthForFlags(flags);
1053
1101
  const timeoutMs = resolveTimeout(flags);
1054
1102
 
1055
1103
  const workingDirectory = String(flags['working-directory'] ?? process.cwd());
@@ -1077,14 +1125,14 @@ async function protocolDiscoverProject(args) {
1077
1125
  async function protocolConnect(args) {
1078
1126
  const flags = parseFlags(args);
1079
1127
  const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
1080
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1128
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
1081
1129
  const timeoutMs = resolveTimeout(flags);
1082
1130
 
1083
1131
  const body = {
1084
1132
  ticketId,
1085
1133
  agentIdentifier: resolveProtocolAgentIdentifier(flags),
1086
1134
  connectionMethod: String(flags.method ?? 'cli'),
1087
- metadata: resolveProtocolMetadata(flags, { cwd: process.cwd() })
1135
+ metadata: await resolveProtocolMetadata(flags, { cwd: process.cwd() })
1088
1136
  };
1089
1137
 
1090
1138
  const data = await apiPost(
@@ -1112,7 +1160,7 @@ async function protocolConnect(args) {
1112
1160
  async function protocolLoadContext(args) {
1113
1161
  const flags = parseFlags(args);
1114
1162
  const ticketId = requireFlag(flags, 'ticket-id', 'TICKET_ID');
1115
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1163
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
1116
1164
  const timeoutMs = resolveTimeout(flags);
1117
1165
 
1118
1166
  const body = { ticketId };
@@ -1136,7 +1184,12 @@ async function protocolLoadContext(args) {
1136
1184
  async function protocolPrompt(args) {
1137
1185
  const flags = parseFlags(args);
1138
1186
  const objective = requireFlag(flags, 'objective', undefined);
1139
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1187
+ const parentTicketId =
1188
+ flags['parent-ticket-id'] !== undefined
1189
+ ? String(flags['parent-ticket-id'] ?? process.env.TICKET_ID ?? '')
1190
+ : '';
1191
+ const { platformUrl, bearerToken, localSecret, organizationId } =
1192
+ await resolveProtocolAuthForFlags(flags, parentTicketId);
1140
1193
  const timeoutMs = resolveTimeout(flags);
1141
1194
  const agentIdentifier = resolveProtocolAgentIdentifier(flags);
1142
1195
  const modelIdentifier = resolveProtocolModelIdentifier(flags);
@@ -1152,7 +1205,7 @@ async function protocolPrompt(args) {
1152
1205
  objective,
1153
1206
  agentIdentifier,
1154
1207
  connectionMethod: String(flags.method ?? 'cli'),
1155
- metadata: resolveProtocolMetadata(flags, { cwd: process.cwd() }),
1208
+ metadata: await resolveProtocolMetadata(flags, { cwd: process.cwd() }),
1156
1209
  ...(flags.title ? { title: String(flags.title) } : {}),
1157
1210
  ...(flags.priority ? { priority: String(flags.priority) } : {}),
1158
1211
  ...(flags['project-id'] ? { projectId: String(flags['project-id']) } : {}),
@@ -1167,9 +1220,7 @@ async function protocolPrompt(args) {
1167
1220
  ...(flags['parent-session-key']
1168
1221
  ? { parentSessionKey: String(flags['parent-session-key']) }
1169
1222
  : {}),
1170
- ...(flags['parent-ticket-id']
1171
- ? { parentTicketId: String(flags['parent-ticket-id'] ?? process.env.TICKET_ID ?? '') }
1172
- : {})
1223
+ ...(parentTicketId ? { parentTicketId } : {})
1173
1224
  };
1174
1225
 
1175
1226
  const data = await apiPost(
@@ -1202,7 +1253,7 @@ async function protocolCreateTicket(args) {
1202
1253
  const flags = parseFlags(args);
1203
1254
  const { sessionKey, ticketId } = resolveSessionFlags(flags);
1204
1255
  const objective = requireFlag(flags, 'objective', undefined);
1205
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1256
+ const { platformUrl, bearerToken, localSecret, organizationId } = await resolveProtocolAuthForFlags(flags, ticketId);
1206
1257
  const timeoutMs = resolveTimeout(flags);
1207
1258
  const agentIdentifier = resolveProtocolAgentIdentifier(flags);
1208
1259
  const modelIdentifier = resolveProtocolModelIdentifier(flags);
@@ -1283,7 +1334,8 @@ async function protocolCreateTicket(args) {
1283
1334
 
1284
1335
  async function protocolSearchTickets(args) {
1285
1336
  const flags = parseFlags(args);
1286
- const { platformUrl, bearerToken, localSecret, organizationId } = await resolveAuth();
1337
+ const { platformUrl, bearerToken, localSecret, organizationId } =
1338
+ await resolveProtocolAuthForFlags(flags);
1287
1339
  const timeoutMs = resolveTimeout(flags);
1288
1340
 
1289
1341
  const statuses = flags.status
@@ -1407,10 +1459,13 @@ Environment fallback:
1407
1459
  Common flags:
1408
1460
  --timeout <ms> Request timeout in milliseconds (default: ${DEFAULT_TIMEOUT_MS})
1409
1461
  --ticket-id <ticket_id> Ticket identifier (e.g. 1:899) when the subcommand operates on an existing ticket
1462
+ --organization-id <id> Legacy organization override for UUID ticket ids; inferred from ticket_id when possible
1410
1463
  --session-key <key> Session key returned by attach/connect/prompt
1411
1464
  --agent <identifier> Agent identifier sent to Overlord (default: AGENT_IDENTIFIER or claude-code)
1412
1465
  --model <identifier> Model identifier to snapshot on executing objectives
1413
1466
  --method <connectionMethod> Connection method sent to Overlord (default: cli)
1467
+ --snapshot-json <json> Snapshot metadata to attach to sessions/file changes
1468
+ --snapshot-file <path|-> Read snapshot metadata JSON from a file or stdin
1414
1469
 
1415
1470
  auth-status:
1416
1471
  Purpose:
@@ -1463,7 +1518,7 @@ load-context:
1463
1518
  Purpose:
1464
1519
  Read ticket details without creating a session
1465
1520
  Required:
1466
- --ticket-id <id>
1521
+ --ticket-id <ticket_id>
1467
1522
 
1468
1523
  search-tickets:
1469
1524
  Purpose:
@@ -1486,7 +1541,7 @@ update:
1486
1541
  Post progress or activity events during execution
1487
1542
  Required:
1488
1543
  --session-key <key>
1489
- --ticket-id <id>
1544
+ --ticket-id <ticket_id>
1490
1545
  --summary <text> or --summary-file <path>
1491
1546
  Optional:
1492
1547
  --phase <status> draft | execute | review | deliver | complete | blocked | cancelled
@@ -1504,7 +1559,7 @@ record-change-rationales:
1504
1559
  Persist structured file-change rationale records without also posting a normal update
1505
1560
  Required:
1506
1561
  --session-key <key>
1507
- --ticket-id <id>
1562
+ --ticket-id <ticket_id>
1508
1563
  --change-rationales-json <json> or --change-rationales-file <path>
1509
1564
  Optional:
1510
1565
  --summary <text> or --summary-file <path>
@@ -1515,7 +1570,7 @@ ask:
1515
1570
  Raise a blocking question for a human reviewer/PM
1516
1571
  Required:
1517
1572
  --session-key <key>
1518
- --ticket-id <id>
1573
+ --ticket-id <ticket_id>
1519
1574
  --question <text> or --question-file <path>
1520
1575
  Optional:
1521
1576
  --phase <status>
@@ -1528,7 +1583,7 @@ permission-request:
1528
1583
  Notify Overlord that the local agent runtime is requesting tool permission.
1529
1584
  This is primarily used by installed permission hooks.
1530
1585
  Required:
1531
- --ticket-id <id>
1586
+ --ticket-id <ticket_id>
1532
1587
  Optional:
1533
1588
  --payload-file <path|-> Hook JSON payload, or stdin when "-"
1534
1589
 
@@ -1537,7 +1592,7 @@ read-context:
1537
1592
  Read persistent shared context written by earlier sessions
1538
1593
  Required:
1539
1594
  --session-key <key>
1540
- --ticket-id <id>
1595
+ --ticket-id <ticket_id>
1541
1596
  Optional:
1542
1597
  --query <text> Filter by key substring
1543
1598
  --limit <n> Max entries to return
@@ -1547,7 +1602,7 @@ write-context:
1547
1602
  Save shared facts for future sessions
1548
1603
  Required:
1549
1604
  --session-key <key>
1550
- --ticket-id <id>
1605
+ --ticket-id <ticket_id>
1551
1606
  --key <name>
1552
1607
  --value <json-or-string> Parsed as JSON first; stored as a string if JSON parsing fails
1553
1608
  Optional:
@@ -1558,7 +1613,7 @@ deliver:
1558
1613
  Conclude the session and submit the final narrative plus artifacts/change rationales
1559
1614
  Required:
1560
1615
  --session-key <key>
1561
- --ticket-id <id>
1616
+ --ticket-id <ticket_id>
1562
1617
  --summary <text> or --summary-file <path>
1563
1618
  or: --payload-file <path|-> containing { summary, artifacts, changeRationales }
1564
1619
  Optional:
@@ -1590,7 +1645,7 @@ prompt:
1590
1645
  --execution-target <t> agent | human
1591
1646
  --delegate <model> Model or delegate identifier that created the ticket
1592
1647
  --parent-session-key <key>
1593
- --parent-ticket-id <id>
1648
+ --parent-ticket-id <ticket_id>
1594
1649
  --agent <identifier>
1595
1650
  --model <identifier>
1596
1651
  --method <connectionMethod>
@@ -1607,7 +1662,7 @@ create:
1607
1662
  --objective <text>
1608
1663
  Optional:
1609
1664
  --session-key <key>
1610
- --ticket-id <id>
1665
+ --ticket-id <ticket_id>
1611
1666
  --working-directory <path> Resolve project by your configured local working directory (default: cwd)
1612
1667
  --project-id <id> Explicit project for standalone draft creation
1613
1668
  --personal Create a private standalone draft without a project
@@ -1628,7 +1683,7 @@ create:
1628
1683
  attachment-list:
1629
1684
  Required:
1630
1685
  --session-key <key>
1631
- --ticket-id <id>
1686
+ --ticket-id <ticket_id>
1632
1687
  Optional:
1633
1688
  --objective-id <id> Filter to a single objective
1634
1689
  Returns:
@@ -1637,7 +1692,7 @@ attachment-list:
1637
1692
  attachment-prepare-upload:
1638
1693
  Required:
1639
1694
  --session-key <key>
1640
- --ticket-id <id>
1695
+ --ticket-id <ticket_id>
1641
1696
  --objective-id <id>
1642
1697
  --file-name <name>
1643
1698
  Optional:
@@ -1649,7 +1704,7 @@ attachment-prepare-upload:
1649
1704
  attachment-finalize-upload:
1650
1705
  Required:
1651
1706
  --session-key <key>
1652
- --ticket-id <id>
1707
+ --ticket-id <ticket_id>
1653
1708
  --objective-id <id>
1654
1709
  --storage-path <path>
1655
1710
  --label <text>
@@ -1661,7 +1716,7 @@ attachment-finalize-upload:
1661
1716
  attachment-download-url:
1662
1717
  Required:
1663
1718
  --session-key <key>
1664
- --ticket-id <id>
1719
+ --ticket-id <ticket_id>
1665
1720
  one of: --attachment-id <id> | --storage-path <path>
1666
1721
  Optional:
1667
1722
  --objective-id <id> Required when using --storage-path
@@ -1670,7 +1725,7 @@ attachment-download-url:
1670
1725
  attachment-upload-file:
1671
1726
  Required:
1672
1727
  --session-key <key>
1673
- --ticket-id <id>
1728
+ --ticket-id <ticket_id>
1674
1729
  --objective-id <id>
1675
1730
  --file <path>
1676
1731
  Optional:
@@ -1684,30 +1739,30 @@ Examples:
1684
1739
  ovld protocol discover-project
1685
1740
  ovld protocol discover-project --working-directory /path/to/repo
1686
1741
  ovld protocol prompt --agent codex --objective "Implement feature X" # auto-resolves project from cwd
1687
- ovld protocol attach --ticket-id abc-123
1688
- ovld protocol attach --ticket-id abc-123 --external-session-id null
1689
- ovld protocol connect --ticket-id abc-123
1690
- ovld protocol load-context --ticket-id abc-123
1742
+ ovld protocol attach --ticket-id 1:899
1743
+ ovld protocol attach --ticket-id 1:899 --external-session-id null
1744
+ ovld protocol connect --ticket-id 1:899
1745
+ ovld protocol load-context --ticket-id 1:899
1691
1746
  ovld protocol search-tickets --query "auth refactor" --status next-up,execute --limit 10
1692
1747
  ovld protocol create --agent codex --objective "Capture follow-up work from this repo"
1693
- ovld protocol create --agent codex --session-key <key> --ticket-id <id> --objective "Capture follow-up work"
1748
+ ovld protocol create --agent codex --session-key <key> --ticket-id <ticket_id> --objective "Capture follow-up work"
1694
1749
  ovld protocol prompt --agent codex --objective "Implement user auth" --priority high
1695
- ovld protocol update --session-key <key> --ticket-id <id> --summary "Did X" --phase execute
1696
- ovld protocol update --session-key <key> --ticket-id <id> --summary-file ./update.txt --event-type user_follow_up
1697
- ovld protocol record-change-rationales --session-key <key> --ticket-id <id> --change-rationales-json '[{"label":"...","file_path":"...","summary":"...","why":"...","impact":"...","hunks":[{"header":"@@ ... @@"}]}]'
1698
- ovld protocol ask --session-key <key> --ticket-id <id> --question-file ./question.txt
1699
- ovld protocol read-context --session-key <key> --ticket-id <id> --query arch --limit 5
1700
- ovld protocol write-context --session-key <key> --ticket-id <id> --key "arch" --value '"monorepo"' --tags repo,agent
1701
- ovld protocol attachment-list --session-key <key> --ticket-id <id>
1702
- ovld protocol attachment-prepare-upload --session-key <key> --ticket-id <id> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
1703
- ovld protocol attachment-upload-file --session-key <key> --ticket-id <id> --objective-id <objective-id> --file ./spec.pdf
1704
- ovld protocol attachment-download-url --session-key <key> --ticket-id <id> --attachment-id <attachment-id>
1705
- ovld protocol deliver --session-key <key> --ticket-id <id> --summary "Done"
1706
- ovld protocol deliver --session-key <key> --ticket-id <id> --summary "Done" --artifacts-file ./artifacts.json
1707
- ovld protocol deliver --session-key <key> --ticket-id <id> --payload-file ./deliver.json
1708
- ovld protocol deliver --session-key <key> --ticket-id <id> --payload-file -
1709
- ovld protocol deliver --session-key <key> --ticket-id <id> --summary "Done" --skip-file-change-check
1710
- ovld protocol deliver --session-key <key> --ticket-id <id> --summary "Done" --timeout 60000
1750
+ ovld protocol update --session-key <key> --ticket-id <ticket_id> --summary "Did X" --phase execute
1751
+ ovld protocol update --session-key <key> --ticket-id <ticket_id> --summary-file ./update.txt --event-type user_follow_up
1752
+ ovld protocol record-change-rationales --session-key <key> --ticket-id <ticket_id> --change-rationales-json '[{"label":"...","file_path":"...","summary":"...","why":"...","impact":"...","hunks":[{"header":"@@ ... @@"}]}]'
1753
+ ovld protocol ask --session-key <key> --ticket-id <ticket_id> --question-file ./question.txt
1754
+ ovld protocol read-context --session-key <key> --ticket-id <ticket_id> --query arch --limit 5
1755
+ ovld protocol write-context --session-key <key> --ticket-id <ticket_id> --key "arch" --value '"monorepo"' --tags repo,agent
1756
+ ovld protocol attachment-list --session-key <key> --ticket-id <ticket_id>
1757
+ ovld protocol attachment-prepare-upload --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file-name spec.pdf --content-type application/pdf
1758
+ ovld protocol attachment-upload-file --session-key <key> --ticket-id <ticket_id> --objective-id <objective-id> --file ./spec.pdf
1759
+ ovld protocol attachment-download-url --session-key <key> --ticket-id <ticket_id> --attachment-id <attachment-id>
1760
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
1761
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
1762
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file ./deliver.json
1763
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
1764
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
1765
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --timeout 60000
1711
1766
  `);
1712
1767
  return;
1713
1768
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2605101225.0",
3
+ "version": "0.2605111015.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",