@odla-ai/cli 0.43.1 → 0.44.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.
@@ -1147,10 +1147,17 @@ function parseArgv(argv) {
1147
1147
  function assertArgs(parsed, allowedOptions2, maxPositionals) {
1148
1148
  const allowed = new Set(allowedOptions2);
1149
1149
  for (const name of Object.keys(parsed.options)) {
1150
- if (!allowed.has(name)) throw new Error(`unknown option "--${name}"; run "odla-ai help" for supported options`);
1150
+ if (allowed.has(name)) continue;
1151
+ const accepts = [...allowed].sort().map((option) => `--${option}`).join(" ");
1152
+ throw new Error(
1153
+ `unknown option "--${name}"` + (accepts ? ` \u2014 this command accepts: ${accepts}` : " \u2014 this command takes no options")
1154
+ );
1151
1155
  }
1152
1156
  if (parsed.positionals.length > maxPositionals) {
1153
- throw new Error(`unexpected argument "${parsed.positionals[maxPositionals]}"; run "odla-ai help"`);
1157
+ const taken = parsed.positionals.slice(0, maxPositionals).join(" ");
1158
+ throw new Error(
1159
+ `unexpected argument "${parsed.positionals[maxPositionals]}" \u2014 ` + (maxPositionals ? `"odla-ai ${taken}" takes no further arguments` : "this command takes no arguments")
1160
+ );
1154
1161
  }
1155
1162
  }
1156
1163
  function requiredString(value2, name) {
@@ -1188,6 +1195,198 @@ function addOption(options, name, value2) {
1188
1195
  else options[name] = [String(current), String(value2)];
1189
1196
  }
1190
1197
 
1198
+ // src/surface.ts
1199
+ var PM_ACTIONS = {
1200
+ list: {},
1201
+ add: {},
1202
+ create: {},
1203
+ get: {},
1204
+ set: {},
1205
+ update: {},
1206
+ status: {},
1207
+ move: {},
1208
+ done: {},
1209
+ comment: {},
1210
+ comments: {},
1211
+ history: {},
1212
+ link: {},
1213
+ ref: {},
1214
+ rm: {},
1215
+ delete: {}
1216
+ };
1217
+ var PM_TASK_ACTIONS = {
1218
+ ...PM_ACTIONS,
1219
+ ready: {},
1220
+ claim: {},
1221
+ release: {}
1222
+ };
1223
+ var PM_ENTITIES = {
1224
+ ...Object.fromEntries(
1225
+ ["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
1226
+ ),
1227
+ task: PM_TASK_ACTIONS,
1228
+ kanban: PM_TASK_ACTIONS
1229
+ };
1230
+ var COMMAND_SURFACE = {
1231
+ agent: { jobs: {}, retry: {} },
1232
+ ai: { models: {} },
1233
+ admin: {
1234
+ ai: {
1235
+ show: {},
1236
+ set: {},
1237
+ credentials: {},
1238
+ models: {},
1239
+ usage: {},
1240
+ audit: {},
1241
+ credential: { set: {} }
1242
+ },
1243
+ spend: { show: {}, reset: {} }
1244
+ },
1245
+ app: {
1246
+ archive: {},
1247
+ restore: {},
1248
+ export: {},
1249
+ import: {},
1250
+ rename: {},
1251
+ "refresh-sandbox": {},
1252
+ "go-live": {},
1253
+ promote: {},
1254
+ owners: { list: {}, add: {}, remove: {} }
1255
+ },
1256
+ auth: { login: {} },
1257
+ brand: { design: { unpack: {} } },
1258
+ bug: { create: {}, list: {}, report: {} },
1259
+ calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
1260
+ capabilities: {},
1261
+ code: {
1262
+ connect: {},
1263
+ grant: { request: {}, list: {}, approve: {}, revoke: {} },
1264
+ repository: { show: {}, list: {}, bind: {} }
1265
+ },
1266
+ config: { diff: {}, plan: {}, apply: {} },
1267
+ context: { show: {}, list: {}, save: {}, remove: {} },
1268
+ credentials: { list: {}, revoke: {} },
1269
+ device: { enroll: {}, list: {}, revoke: {} },
1270
+ // `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
1271
+ discuss: {
1272
+ groups: {},
1273
+ list: {},
1274
+ topics: {},
1275
+ read: {},
1276
+ post: {},
1277
+ reply: {},
1278
+ resolve: {},
1279
+ who: {},
1280
+ watch: {}
1281
+ },
1282
+ doctor: {},
1283
+ help: {},
1284
+ init: {},
1285
+ monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
1286
+ o11y: { status: {} },
1287
+ operations: { get: {}, wait: {} },
1288
+ platform: {
1289
+ status: {}
1290
+ },
1291
+ pm: {
1292
+ ...PM_ENTITIES,
1293
+ project: { list: {}, add: {}, create: {}, use: {} },
1294
+ handoff: {},
1295
+ next: {},
1296
+ start: {},
1297
+ watch: {}
1298
+ },
1299
+ provision: {},
1300
+ runbook: {
1301
+ ask: {},
1302
+ search: {},
1303
+ impact: {},
1304
+ list: {},
1305
+ get: {},
1306
+ cat: {},
1307
+ new: {},
1308
+ edit: {},
1309
+ comment: {},
1310
+ import: {},
1311
+ visibility: {},
1312
+ publish: {},
1313
+ archive: {},
1314
+ history: {},
1315
+ revert: {},
1316
+ rm: {},
1317
+ lint: {}
1318
+ },
1319
+ secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
1320
+ security: {
1321
+ plan: {},
1322
+ sources: {},
1323
+ run: {},
1324
+ status: {},
1325
+ report: {},
1326
+ github: { connect: {}, disconnect: {} }
1327
+ },
1328
+ setup: {},
1329
+ skill: { install: {} },
1330
+ smoke: {},
1331
+ version: {},
1332
+ whoami: {}
1333
+ };
1334
+ function acceptedAfter(path) {
1335
+ let node = COMMAND_SURFACE;
1336
+ for (const word of path) {
1337
+ node = node?.[word];
1338
+ if (!node) return [];
1339
+ }
1340
+ return Object.keys(node).sort();
1341
+ }
1342
+ function validateInvocation(words2) {
1343
+ let node = COMMAND_SURFACE;
1344
+ const walked = [];
1345
+ for (const word of words2) {
1346
+ if (Object.keys(node).length === 0) return null;
1347
+ const next = node[word];
1348
+ if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
1349
+ walked.push(word);
1350
+ node = next;
1351
+ }
1352
+ return null;
1353
+ }
1354
+ function describeProblem(problem) {
1355
+ const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
1356
+ if (!problem.accepted.length) return `"${problem.word}" is not accepted ${where}. Run "odla-ai help"`;
1357
+ if (!problem.word) return `"odla-ai ${problem.validPrefix}" needs one of: ${problem.accepted.join(", ")}`;
1358
+ return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
1359
+ }
1360
+ function rejectWord(path, word, note) {
1361
+ const sentence = describeProblem({
1362
+ validPrefix: path.join(" "),
1363
+ word: word ?? "",
1364
+ accepted: acceptedAfter(path)
1365
+ });
1366
+ throw new Error(note ? `${sentence}. ${note}` : sentence);
1367
+ }
1368
+ function invocationPath(words2) {
1369
+ let node = COMMAND_SURFACE;
1370
+ const path = [];
1371
+ for (const word of words2) {
1372
+ const next = node[word];
1373
+ if (!next) break;
1374
+ path.push(word);
1375
+ node = next;
1376
+ if (Object.keys(node).length === 0) break;
1377
+ }
1378
+ return path;
1379
+ }
1380
+ function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
1381
+ const paths2 = [];
1382
+ for (const [word, child] of Object.entries(node)) {
1383
+ const path = [...prefix, word];
1384
+ paths2.push(path);
1385
+ paths2.push(...surfacePaths(child, path));
1386
+ }
1387
+ return paths2;
1388
+ }
1389
+
1191
1390
  // src/admin-spend.ts
1192
1391
  async function call(ctx, method, scope) {
1193
1392
  const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
@@ -1237,9 +1436,7 @@ async function spendReset(ctx, scope) {
1237
1436
  async function adminSpend(parsed, ctx) {
1238
1437
  const action2 = parsed.positionals[2];
1239
1438
  const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
1240
- if (action2 !== "show" && action2 !== "reset") {
1241
- throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
1242
- }
1439
+ if (action2 !== "show" && action2 !== "reset") rejectWord(["admin", "spend"], action2);
1243
1440
  if (!scope) {
1244
1441
  throw new Error(
1245
1442
  `"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
@@ -2038,6 +2235,11 @@ var SET_OPTIONS = [
2038
2235
  async function adminCommand(parsed, deps = {}) {
2039
2236
  const area = parsed.positionals[1];
2040
2237
  const action2 = parsed.positionals[2];
2238
+ if (area !== "ai" && area !== "spend") rejectWord(["admin"], area);
2239
+ if (!acceptedAfter(["admin", area]).includes(action2 ?? "")) rejectWord(["admin", area], action2);
2240
+ if (action2 === "credential" && !acceptedAfter(["admin", "ai", "credential"]).includes(parsed.positionals[3] ?? "")) {
2241
+ rejectWord(["admin", "ai", "credential"], parsed.positionals[3]);
2242
+ }
2041
2243
  if (area === "spend") {
2042
2244
  assertArgs(parsed, JSON_OPTIONS, 4);
2043
2245
  const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
@@ -2063,14 +2265,11 @@ async function adminCommand(parsed, deps = {}) {
2063
2265
  out
2064
2266
  });
2065
2267
  }
2066
- const credentialSet = action2 === "credential" && parsed.positionals[3] === "set";
2268
+ const credentialSet = action2 === "credential";
2067
2269
  const credentials = action2 === "credentials";
2068
2270
  const models = action2 === "models";
2069
2271
  const usage = action2 === "usage";
2070
2272
  const audit = action2 === "audit";
2071
- if (area !== "ai" || action2 !== "show" && action2 !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
2072
- throw new Error('unknown admin command. Try "odla-ai admin ai show".');
2073
- }
2074
2273
  const allowed = credentialSet ? [...CONTEXT_OPTIONS, "from-env", "stdin"] : action2 === "set" ? SET_OPTIONS : models ? [...JSON_OPTIONS, "provider"] : usage ? [...JSON_OPTIONS, "app-id", "env", "run-id", "limit"] : audit ? [...JSON_OPTIONS, "limit"] : JSON_OPTIONS;
2075
2274
  assertArgs(parsed, allowed, credentialSet ? 5 : action2 === "set" ? 4 : 3);
2076
2275
  const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
@@ -2284,9 +2483,7 @@ async function authCommand(parsed, deps = {}) {
2284
2483
  "json"
2285
2484
  ], 2);
2286
2485
  const action2 = parsed.positionals[1] ?? "login";
2287
- if (action2 !== "login") {
2288
- throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
2289
- }
2486
+ if (action2 !== "login") rejectWord(["auth"], action2);
2290
2487
  const context = await resolveOperatorContext(parsed, {
2291
2488
  allowMissingConfig: true,
2292
2489
  requireApp: true
@@ -2359,9 +2556,7 @@ function bothTenants(cfg) {
2359
2556
  // src/agent-command.ts
2360
2557
  async function agentCommand(parsed, deps = {}) {
2361
2558
  const action2 = parsed.positionals[1];
2362
- if (action2 !== "jobs" && action2 !== "retry") {
2363
- throw new Error(`unknown agent action "${action2 ?? ""}". Try "odla-ai agent jobs --json".`);
2364
- }
2559
+ if (action2 !== "jobs" && action2 !== "retry") rejectWord(["agent"], action2);
2365
2560
  assertArgs(parsed, ["config", "env", "state", "limit", "json", "token"], action2 === "jobs" ? 2 : 3);
2366
2561
  if (action2 === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
2367
2562
  throw new Error('--state and --limit are supported only by "agent jobs"');
@@ -2536,9 +2731,7 @@ async function appOwnersCommand(parsed, dependencies = {}) {
2536
2731
  await (sub === "add" ? ownersAdd(email, options) : ownersRemove(email, options));
2537
2732
  return;
2538
2733
  }
2539
- throw new Error(
2540
- `unknown app owners subcommand "${sub}". Try "odla-ai app owners list", "odla-ai app owners add <email>", or "odla-ai app owners remove <email>".`
2541
- );
2734
+ rejectWord(["app", "owners"], sub);
2542
2735
  }
2543
2736
 
2544
2737
  // src/app-rename.ts
@@ -2644,8 +2837,10 @@ async function appCommand(parsed, dependencies = {}) {
2644
2837
  return;
2645
2838
  }
2646
2839
  if (sub !== "archive" && sub !== "restore") {
2647
- throw new Error(
2648
- `unknown app subcommand "${sub ?? ""}". Try "odla-ai app archive --yes", "odla-ai app restore", "odla-ai app export", "odla-ai app import <file>", "odla-ai app refresh-sandbox", "odla-ai app go-live", "odla-ai app promote", "odla-ai app rename <name>", or "odla-ai app owners <list|add|remove>". (Permanent deletion has no CLI: it requires a signed-in owner in Studio.)`
2840
+ rejectWord(
2841
+ ["app"],
2842
+ sub,
2843
+ "Permanent deletion has no CLI: it requires a signed-in owner in Studio."
2649
2844
  );
2650
2845
  }
2651
2846
  assertArgs(parsed, ["config", "token", "email", "yes", "json"], 2);
@@ -2811,7 +3006,8 @@ async function brandCommand(parsed, deps) {
2811
3006
  await designUnpack(parsed, deps);
2812
3007
  return;
2813
3008
  }
2814
- throw new Error(USAGE);
3009
+ if (subject !== "design") rejectWord(["brand"], subject);
3010
+ rejectWord(["brand", "design"], action2, USAGE);
2815
3011
  }
2816
3012
 
2817
3013
  // src/calendar-errors.ts
@@ -5620,9 +5816,7 @@ async function secretsCommand(parsed, deps) {
5620
5816
  return;
5621
5817
  }
5622
5818
  if (sub !== "push") {
5623
- throw new Error(
5624
- `unknown secrets subcommand "${sub ?? ""}". Try "odla-ai secrets push --env dev", "odla-ai secrets status --env dev", "odla-ai secrets set <name> --env dev --stdin", or "odla-ai secrets set-clerk-key --env dev --stdin".`
5625
- );
5819
+ rejectWord(["secrets"], sub);
5626
5820
  }
5627
5821
  assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
5628
5822
  await secretsPush({
@@ -5635,7 +5829,7 @@ async function secretsCommand(parsed, deps) {
5635
5829
  async function projectCommand(command, parsed, deps) {
5636
5830
  if (command === "ai") {
5637
5831
  const sub = parsed.positionals[1];
5638
- if (sub !== "models") throw new Error(`unknown ai subcommand "${sub ?? ""}". Try "odla-ai ai models --env dev".`);
5832
+ if (sub !== "models") rejectWord(["ai"], sub);
5639
5833
  assertArgs(parsed, ["config", "env", "provider", "json"], 2);
5640
5834
  await aiModels({
5641
5835
  configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
@@ -5649,9 +5843,7 @@ async function projectCommand(command, parsed, deps) {
5649
5843
  }
5650
5844
  if (command === "config") {
5651
5845
  const sub = parsed.positionals[1];
5652
- if (sub !== "diff" && sub !== "plan" && sub !== "apply") {
5653
- throw new Error(`unknown config subcommand "${sub ?? ""}". Try "odla-ai config diff --json".`);
5654
- }
5846
+ if (sub !== "diff" && sub !== "plan" && sub !== "apply") rejectWord(["config"], sub);
5655
5847
  assertArgs(
5656
5848
  parsed,
5657
5849
  sub === "apply" ? ["config", "plan", "idempotency-key", "token", "email", "open", "json"] : ["config", "token", "email", "open", "json"],
@@ -5679,7 +5871,7 @@ async function projectCommand(command, parsed, deps) {
5679
5871
  if (command === "operations") {
5680
5872
  const sub = parsed.positionals[1];
5681
5873
  if (sub !== "get" && sub !== "wait") {
5682
- throw new Error(`unknown operations subcommand "${sub ?? ""}". Try "odla-ai operations get <operation-id> --json".`);
5874
+ rejectWord(["operations"], sub);
5683
5875
  }
5684
5876
  assertArgs(
5685
5877
  parsed,
@@ -5753,7 +5945,7 @@ async function projectCommand(command, parsed, deps) {
5753
5945
  }
5754
5946
  if (command === "skill") {
5755
5947
  const sub = parsed.positionals[1];
5756
- if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
5948
+ if (sub !== "install") rejectWord(["skill"], sub);
5757
5949
  install(parsed, 2, deps);
5758
5950
  return true;
5759
5951
  }
@@ -10314,9 +10506,7 @@ function grantsUrl(cfg, suffix = "") {
10314
10506
  async function codeGrantCommand(parsed, deps = {}) {
10315
10507
  const action2 = parsed.positionals[2];
10316
10508
  if (!isAction(action2)) {
10317
- throw new Error(
10318
- `unknown code grant action "${action2 ?? ""}". Try "odla-ai code grant list --env dev".`
10319
- );
10509
+ rejectWord(["code", "grant"], action2);
10320
10510
  }
10321
10511
  const decides = action2 === "approve" || action2 === "revoke";
10322
10512
  assertArgs(parsed, ["config", "env", "json", "token", "email", "open"], decides ? 4 : 3);
@@ -10436,9 +10626,7 @@ function repositoryUrl(cfg, suffix = "") {
10436
10626
  async function codeRepositoryCommand(parsed, deps = {}) {
10437
10627
  const action2 = parsed.positionals[2];
10438
10628
  if (!isAction2(action2)) {
10439
- throw new Error(
10440
- `unknown code repository action "${action2 ?? ""}". Try "odla-ai code repository show --env dev".`
10441
- );
10629
+ rejectWord(["code", "repository"], action2);
10442
10630
  }
10443
10631
  assertArgs(parsed, ["config", "env", "repo", "json", "token", "email", "open"], 3);
10444
10632
  const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
@@ -10513,9 +10701,7 @@ async function codeCommand(parsed, dependencies) {
10513
10701
  });
10514
10702
  }
10515
10703
  if (sub !== "connect") {
10516
- throw new Error(
10517
- `unknown code subcommand "${sub ?? ""}". Try "odla-ai code connect --env dev" or "odla-ai code grant list --env dev".`
10518
- );
10704
+ rejectWord(["code"], sub);
10519
10705
  }
10520
10706
  assertArgs(parsed, [
10521
10707
  "config",
@@ -10652,9 +10838,7 @@ async function contextCommand(parsed, deps = {}) {
10652
10838
  return;
10653
10839
  }
10654
10840
  if (action2 !== "show") {
10655
- throw new Error(
10656
- `unknown context action "${action2 ?? ""}". Try show|list|save|remove.`
10657
- );
10841
+ rejectWord(["context"], action2);
10658
10842
  }
10659
10843
  const context = await resolveOperatorContext(parsed, {
10660
10844
  allowMissingConfig: true,
@@ -10710,9 +10894,7 @@ async function responseError(response2) {
10710
10894
  }
10711
10895
  async function credentialCommand(parsed, deps = {}) {
10712
10896
  const action2 = parsed.positionals[1] ?? "list";
10713
- if (action2 !== "list" && action2 !== "revoke") {
10714
- throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
10715
- }
10897
+ if (action2 !== "list" && action2 !== "revoke") rejectWord(["credentials"], action2);
10716
10898
  assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
10717
10899
  const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
10718
10900
  const doFetch = deps.fetch ?? fetch;
@@ -11714,7 +11896,7 @@ async function discussCommand(parsed, deps = {}) {
11714
11896
  assertArgs(parsed, ALLOWED, 3);
11715
11897
  const action2 = parsed.positionals[1];
11716
11898
  const id2 = parsed.positionals[2];
11717
- if (!action2) throw new Error('"discuss" needs an action. Run "odla-ai help".');
11899
+ if (!acceptedAfter(["discuss"]).includes(action2 ?? "")) rejectWord(["discuss"], action2);
11718
11900
  const ctx = await buildContext(parsed, deps);
11719
11901
  switch (action2) {
11720
11902
  case "groups":
@@ -11738,7 +11920,7 @@ async function discussCommand(parsed, deps = {}) {
11738
11920
  return;
11739
11921
  }
11740
11922
  default:
11741
- throw new Error(`unknown discuss action "${action2}". Run "odla-ai help".`);
11923
+ rejectWord(["discuss"], action2);
11742
11924
  }
11743
11925
  }
11744
11926
 
@@ -12648,7 +12830,7 @@ async function pmCommand(parsed, deps = {}) {
12648
12830
  assertArgs(parsed, COMMON_OPTIONS, 4);
12649
12831
  return pmProjectUse(await buildContext2(parsed, deps), requireId2(parsed.positionals[3], action3));
12650
12832
  }
12651
- throw new Error(`unknown pm project action "${action3}". Try list|add|use.`);
12833
+ rejectWord(["pm", "project"], action3);
12652
12834
  }
12653
12835
  if (word === "next") {
12654
12836
  assertArgs(parsed, [...COMMON_OPTIONS, "app", "project", "verbose"], 2);
@@ -12679,10 +12861,10 @@ async function pmCommand(parsed, deps = {}) {
12679
12861
  return pmHandoff(await buildContext2(parsed, deps), parsed);
12680
12862
  }
12681
12863
  const entity = ALIASES[word];
12682
- if (!entity) throw new Error(`unknown pm entity "${word}". Try "odla-ai pm start" to claim Ready work, or "odla-ai pm bug list" (goal|task|decision|bug).`);
12864
+ if (!entity) rejectWord(["pm"], word);
12683
12865
  const requestedAction = parsed.positionals[2] ?? "list";
12684
12866
  const action2 = canonicalAction(requestedAction);
12685
- if (!action2) throw new Error(`unknown pm action "${requestedAction}". Try list|add|get|set|done|link|ref|comment|comments|history|rm.`);
12867
+ if (!action2) rejectWord(["pm", word], requestedAction);
12686
12868
  assertArgs(parsed, allowedOptions(entity, action2), 4);
12687
12869
  if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
12688
12870
  throw new Error(`pm ${action2} is only valid for tasks`);
@@ -12768,12 +12950,7 @@ async function platformCommand(parsed, deps = {}) {
12768
12950
  if (action2 === "status") {
12769
12951
  return platformStatus(parsed, deps);
12770
12952
  }
12771
- throw new Error(
12772
- `unknown platform action "${[
12773
- action2,
12774
- parsed.positionals[2]
12775
- ].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
12776
- );
12953
+ rejectWord(["platform"], action2);
12777
12954
  }
12778
12955
  async function platformStatus(parsed, deps) {
12779
12956
  assertArgs(
@@ -13110,9 +13287,7 @@ async function o11yCommand(parsed, deps = {}) {
13110
13287
  );
13111
13288
  const action2 = parsed.positionals[1];
13112
13289
  if (action2 !== "status") {
13113
- throw new Error(
13114
- `unknown o11y action "${action2 ?? ""}". Try "odla-ai o11y status --json".`
13115
- );
13290
+ rejectWord(["o11y"], action2);
13116
13291
  }
13117
13292
  const minutes = statusMinutes(
13118
13293
  numberOpt(parsed.options.minutes, "--minutes") ?? 60
@@ -13365,9 +13540,7 @@ var OPTIONS = [
13365
13540
  async function monitorCommand(parsed, deps = {}) {
13366
13541
  assertArgs(parsed, OPTIONS, 3);
13367
13542
  const action2 = parsed.positionals[1] ?? "status";
13368
- if (!["plan", "apply", "run", "status", "incidents", "report"].includes(action2)) {
13369
- throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
13370
- }
13543
+ if (!acceptedAfter(["monitor"]).includes(action2)) rejectWord(["monitor"], action2);
13371
13544
  const context = await resolveOperatorContext(parsed, {
13372
13545
  allowMissingConfig: action2 !== "plan" && action2 !== "apply",
13373
13546
  requireApp: true
@@ -13997,187 +14170,6 @@ async function provision(options) {
13997
14170
  // src/record.ts
13998
14171
  import { appendFileSync } from "fs";
13999
14172
  import process19 from "process";
14000
-
14001
- // src/surface.ts
14002
- var PM_ACTIONS = {
14003
- list: {},
14004
- add: {},
14005
- create: {},
14006
- get: {},
14007
- set: {},
14008
- update: {},
14009
- status: {},
14010
- move: {},
14011
- done: {},
14012
- comment: {},
14013
- comments: {},
14014
- ref: {},
14015
- rm: {},
14016
- delete: {}
14017
- };
14018
- var PM_TASK_ACTIONS = {
14019
- ...PM_ACTIONS,
14020
- ready: {},
14021
- claim: {},
14022
- release: {}
14023
- };
14024
- var PM_ENTITIES = {
14025
- ...Object.fromEntries(
14026
- ["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
14027
- ),
14028
- task: PM_TASK_ACTIONS,
14029
- kanban: PM_TASK_ACTIONS
14030
- };
14031
- var COMMAND_SURFACE = {
14032
- agent: { jobs: {}, retry: {} },
14033
- ai: { models: {} },
14034
- admin: {
14035
- ai: {
14036
- show: {},
14037
- set: {},
14038
- credentials: {},
14039
- models: {},
14040
- usage: {},
14041
- audit: {},
14042
- credential: { set: {} }
14043
- }
14044
- },
14045
- app: {
14046
- archive: {},
14047
- restore: {},
14048
- export: {},
14049
- import: {},
14050
- rename: {},
14051
- "refresh-sandbox": {},
14052
- "go-live": {},
14053
- promote: {},
14054
- owners: { list: {}, add: {}, remove: {} }
14055
- },
14056
- auth: { login: {} },
14057
- brand: { design: { unpack: {} } },
14058
- bug: { create: {}, list: {}, report: {} },
14059
- calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
14060
- capabilities: {},
14061
- code: {
14062
- connect: {},
14063
- grant: { request: {}, list: {}, approve: {}, revoke: {} },
14064
- repository: { show: {}, list: {}, bind: {} }
14065
- },
14066
- config: { diff: {}, plan: {}, apply: {} },
14067
- context: { show: {}, list: {}, save: {}, remove: {} },
14068
- credentials: { list: {}, revoke: {} },
14069
- device: { enroll: {}, list: {}, revoke: {} },
14070
- // `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
14071
- discuss: {
14072
- groups: {},
14073
- list: {},
14074
- topics: {},
14075
- read: {},
14076
- post: {},
14077
- reply: {},
14078
- resolve: {},
14079
- who: {},
14080
- watch: {}
14081
- },
14082
- doctor: {},
14083
- help: {},
14084
- init: {},
14085
- monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
14086
- o11y: { status: {} },
14087
- operations: { get: {}, wait: {} },
14088
- platform: {
14089
- status: {}
14090
- },
14091
- pm: {
14092
- ...PM_ENTITIES,
14093
- project: { list: {}, add: {}, create: {}, use: {} },
14094
- handoff: {},
14095
- next: {},
14096
- start: {},
14097
- watch: {}
14098
- },
14099
- provision: {},
14100
- runbook: {
14101
- ask: {},
14102
- search: {},
14103
- impact: {},
14104
- list: {},
14105
- get: {},
14106
- cat: {},
14107
- new: {},
14108
- edit: {},
14109
- comment: {},
14110
- import: {},
14111
- visibility: {},
14112
- publish: {},
14113
- archive: {},
14114
- history: {},
14115
- revert: {},
14116
- rm: {},
14117
- lint: {}
14118
- },
14119
- secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
14120
- security: {
14121
- plan: {},
14122
- sources: {},
14123
- run: {},
14124
- status: {},
14125
- report: {},
14126
- github: { connect: {}, disconnect: {} }
14127
- },
14128
- setup: {},
14129
- skill: { install: {} },
14130
- smoke: {},
14131
- version: {},
14132
- whoami: {}
14133
- };
14134
- function acceptedAfter(path) {
14135
- let node = COMMAND_SURFACE;
14136
- for (const word of path) {
14137
- node = node?.[word];
14138
- if (!node) return [];
14139
- }
14140
- return Object.keys(node).sort();
14141
- }
14142
- function validateInvocation(words2) {
14143
- let node = COMMAND_SURFACE;
14144
- const walked = [];
14145
- for (const word of words2) {
14146
- if (Object.keys(node).length === 0) return null;
14147
- const next = node[word];
14148
- if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
14149
- walked.push(word);
14150
- node = next;
14151
- }
14152
- return null;
14153
- }
14154
- function describeProblem(problem) {
14155
- const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
14156
- return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
14157
- }
14158
- function invocationPath(words2) {
14159
- let node = COMMAND_SURFACE;
14160
- const path = [];
14161
- for (const word of words2) {
14162
- const next = node[word];
14163
- if (!next) break;
14164
- path.push(word);
14165
- node = next;
14166
- if (Object.keys(node).length === 0) break;
14167
- }
14168
- return path;
14169
- }
14170
- function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
14171
- const paths2 = [];
14172
- for (const [word, child] of Object.entries(node)) {
14173
- const path = [...prefix, word];
14174
- paths2.push(path);
14175
- paths2.push(...surfacePaths(child, path));
14176
- }
14177
- return paths2;
14178
- }
14179
-
14180
- // src/record.ts
14181
14173
  function recordInvocation(parsed) {
14182
14174
  const file = process19.env.ODLA_CLI_RECORD;
14183
14175
  if (!file) return;
@@ -14271,6 +14263,7 @@ async function deviceCommand(parsed, deps) {
14271
14263
  "wait"
14272
14264
  ], 3);
14273
14265
  const action2 = parsed.positionals[1] ?? "";
14266
+ if (!acceptedAfter(["device"]).includes(action2)) rejectWord(["device"], action2);
14274
14267
  const out = deps.stdout ?? console;
14275
14268
  const doFetch = deps.fetch ?? fetch;
14276
14269
  const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
@@ -14278,7 +14271,7 @@ async function deviceCommand(parsed, deps) {
14278
14271
  if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
14279
14272
  if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
14280
14273
  if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
14281
- throw new Error('odla-ai device expects "enroll", "list", or "revoke"');
14274
+ rejectWord(["device"], action2);
14282
14275
  }
14283
14276
  async function enroll(parsed, deps, cfg, doFetch, out, json) {
14284
14277
  const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
@@ -15176,6 +15169,7 @@ async function buildContext3(parsed, deps, action2) {
15176
15169
  async function runbookCommand(parsed, deps = {}) {
15177
15170
  const action2 = parsed.positionals[1] ?? "list";
15178
15171
  assertArgs(parsed, ALLOWED2, action2 === "ask" || action2 === "search" ? 64 : 4);
15172
+ if (!acceptedAfter(["runbook"]).includes(action2)) rejectWord(["runbook"], action2);
15179
15173
  const ctx = await buildContext3(parsed, deps, action2);
15180
15174
  const slug = parsed.positionals[2];
15181
15175
  switch (action2) {
@@ -15265,7 +15259,7 @@ async function runbookCommand(parsed, deps = {}) {
15265
15259
  case "rm":
15266
15260
  return runbookRemove(ctx, requireSlug(slug, "rm"));
15267
15261
  default:
15268
- throw new Error(`unknown runbook action "${action2}". Try ${acceptedAfter(["runbook"]).join(", ")}.`);
15262
+ rejectWord(["runbook"], action2);
15269
15263
  }
15270
15264
  }
15271
15265
 
@@ -15886,9 +15880,7 @@ async function securityCommand(parsed, dependencies) {
15886
15880
  else printHostedReport(context.stdout, report5);
15887
15881
  return;
15888
15882
  }
15889
- if (sub !== "run") {
15890
- throw new Error('unknown security command. Try "odla-ai security plan", "security sources", or "security run".');
15891
- }
15883
+ if (sub !== "run") rejectWord(["security"], sub);
15892
15884
  const sourceId = stringOpt(parsed.options.source);
15893
15885
  if (sourceId) await runSourceSecurityCommand(parsed, dependencies, sourceId);
15894
15886
  else await runLocalSecurityCommand(parsed, dependencies);
@@ -15905,9 +15897,7 @@ async function githubSecurityCommand(parsed, dependencies) {
15905
15897
  stringOpt(parsed.options.env)
15906
15898
  );
15907
15899
  }
15908
- if (action2 !== "connect") {
15909
- throw new Error('unknown security github command. Try "odla-ai security github connect".');
15910
- }
15900
+ if (action2 !== "connect") rejectWord(["security", "github"], action2);
15911
15901
  assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
15912
15902
  await requireStudioHuman(
15913
15903
  stringOpt(parsed.options.config) ?? "odla.config.mjs",
@@ -16050,6 +16040,7 @@ async function dispatchCli(argv, dependencies) {
16050
16040
  }
16051
16041
  if (command === "bug") {
16052
16042
  const action2 = parsed.positionals[1] ?? "list";
16043
+ if (!acceptedAfter(["bug"]).includes(action2)) rejectWord(["bug"], action2);
16053
16044
  const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
16054
16045
  await pmCommand({
16055
16046
  ...parsed,
@@ -16078,7 +16069,7 @@ async function dispatchCli(argv, dependencies) {
16078
16069
  return;
16079
16070
  }
16080
16071
  if (await projectCommand(command, parsed, runtime)) return;
16081
- throw new Error(`unknown command "${command}". Run "odla-ai help".`);
16072
+ rejectWord([], command);
16082
16073
  }
16083
16074
  async function provisionCommand(parsed, dependencies) {
16084
16075
  assertArgs(parsed, [
@@ -16122,7 +16113,7 @@ async function provisionCommand(parsed, dependencies) {
16122
16113
  async function calendarCommand(parsed, dependencies) {
16123
16114
  const sub = parsed.positionals[1];
16124
16115
  if (sub !== "status" && sub !== "calendars" && sub !== "connect" && sub !== "disconnect") {
16125
- throw new Error(`unknown calendar subcommand "${sub ?? ""}". Try "odla-ai calendar status --env dev".`);
16116
+ rejectWord(["calendar"], sub);
16126
16117
  }
16127
16118
  assertArgs(parsed, ["config", "env", "json", "token", "email", "open", "yes"], 2);
16128
16119
  if (sub !== "status" && sub !== "calendars" && parsed.options.json !== void 0) throw new Error(`--json is supported only by calendar status/calendars`);
@@ -16149,6 +16140,12 @@ export {
16149
16140
  getScopedPlatformToken,
16150
16141
  SYSTEM_AI_PURPOSES,
16151
16142
  adminAi,
16143
+ COMMAND_SURFACE,
16144
+ acceptedAfter,
16145
+ validateInvocation,
16146
+ describeProblem,
16147
+ invocationPath,
16148
+ surfacePaths,
16152
16149
  calendarServiceConfig,
16153
16150
  calendarBookingPageUrl,
16154
16151
  GOOGLE_CALENDAR_EVENTS_SCOPE,
@@ -16186,12 +16183,6 @@ export {
16186
16183
  monitoringWireConfig,
16187
16184
  monitorCommand,
16188
16185
  provision,
16189
- COMMAND_SURFACE,
16190
- acceptedAfter,
16191
- validateInvocation,
16192
- describeProblem,
16193
- invocationPath,
16194
- surfacePaths,
16195
16186
  runHostedSecurity,
16196
16187
  getHostedSecurityIntent,
16197
16188
  getHostedSecurityPlan,
@@ -16203,4 +16194,4 @@ export {
16203
16194
  isTerminalHostedSecurityStatus,
16204
16195
  runCli
16205
16196
  };
16206
- //# sourceMappingURL=chunk-T4OPSRME.js.map
16197
+ //# sourceMappingURL=chunk-7MUWCSGP.js.map