@odla-ai/cli 0.43.1 → 0.45.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/dist/bin.cjs +671 -552
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +12 -14
- package/dist/bin.js.map +1 -1
- package/dist/chunk-5MEO3BVF.js +282 -0
- package/dist/chunk-5MEO3BVF.js.map +1 -0
- package/dist/{chunk-T4OPSRME.js → chunk-JHB6352A.js} +429 -471
- package/dist/chunk-JHB6352A.js.map +1 -0
- package/dist/cli-2FTL245Y.js +12 -0
- package/dist/index.cjs +341 -256
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-T4OPSRME.js.map +0 -1
- package/dist/chunk-UKLSRQ5J.js +0 -129
- package/dist/chunk-UKLSRQ5J.js.map +0 -1
- package/dist/cli-3JOB4OUV.js +0 -12
- /package/dist/{cli-3JOB4OUV.js.map → cli-2FTL245Y.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1234,10 +1234,17 @@ function parseArgv(argv) {
|
|
|
1234
1234
|
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
1235
1235
|
const allowed = new Set(allowedOptions2);
|
|
1236
1236
|
for (const name of Object.keys(parsed.options)) {
|
|
1237
|
-
if (
|
|
1237
|
+
if (allowed.has(name)) continue;
|
|
1238
|
+
const accepts = [...allowed].sort().map((option) => `--${option}`).join(" ");
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`unknown option "--${name}"` + (accepts ? ` \u2014 this command accepts: ${accepts}` : " \u2014 this command takes no options")
|
|
1241
|
+
);
|
|
1238
1242
|
}
|
|
1239
1243
|
if (parsed.positionals.length > maxPositionals) {
|
|
1240
|
-
|
|
1244
|
+
const taken = parsed.positionals.slice(0, maxPositionals).join(" ");
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`unexpected argument "${parsed.positionals[maxPositionals]}" \u2014 ` + (maxPositionals ? `"odla-ai ${taken}" takes no further arguments` : "this command takes no arguments")
|
|
1247
|
+
);
|
|
1241
1248
|
}
|
|
1242
1249
|
}
|
|
1243
1250
|
function requiredString(value2, name) {
|
|
@@ -1275,6 +1282,198 @@ function addOption(options, name, value2) {
|
|
|
1275
1282
|
else options[name] = [String(current), String(value2)];
|
|
1276
1283
|
}
|
|
1277
1284
|
|
|
1285
|
+
// src/surface.ts
|
|
1286
|
+
var PM_ACTIONS = {
|
|
1287
|
+
list: {},
|
|
1288
|
+
add: {},
|
|
1289
|
+
create: {},
|
|
1290
|
+
get: {},
|
|
1291
|
+
set: {},
|
|
1292
|
+
update: {},
|
|
1293
|
+
status: {},
|
|
1294
|
+
move: {},
|
|
1295
|
+
done: {},
|
|
1296
|
+
comment: {},
|
|
1297
|
+
comments: {},
|
|
1298
|
+
history: {},
|
|
1299
|
+
link: {},
|
|
1300
|
+
ref: {},
|
|
1301
|
+
rm: {},
|
|
1302
|
+
delete: {}
|
|
1303
|
+
};
|
|
1304
|
+
var PM_TASK_ACTIONS = {
|
|
1305
|
+
...PM_ACTIONS,
|
|
1306
|
+
ready: {},
|
|
1307
|
+
claim: {},
|
|
1308
|
+
release: {}
|
|
1309
|
+
};
|
|
1310
|
+
var PM_ENTITIES = {
|
|
1311
|
+
...Object.fromEntries(
|
|
1312
|
+
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
1313
|
+
),
|
|
1314
|
+
task: PM_TASK_ACTIONS,
|
|
1315
|
+
kanban: PM_TASK_ACTIONS
|
|
1316
|
+
};
|
|
1317
|
+
var COMMAND_SURFACE = {
|
|
1318
|
+
agent: { jobs: {}, retry: {} },
|
|
1319
|
+
ai: { models: {} },
|
|
1320
|
+
admin: {
|
|
1321
|
+
ai: {
|
|
1322
|
+
show: {},
|
|
1323
|
+
set: {},
|
|
1324
|
+
credentials: {},
|
|
1325
|
+
models: {},
|
|
1326
|
+
usage: {},
|
|
1327
|
+
audit: {},
|
|
1328
|
+
credential: { set: {} }
|
|
1329
|
+
},
|
|
1330
|
+
spend: { show: {}, reset: {} }
|
|
1331
|
+
},
|
|
1332
|
+
app: {
|
|
1333
|
+
archive: {},
|
|
1334
|
+
restore: {},
|
|
1335
|
+
export: {},
|
|
1336
|
+
import: {},
|
|
1337
|
+
rename: {},
|
|
1338
|
+
"refresh-sandbox": {},
|
|
1339
|
+
"go-live": {},
|
|
1340
|
+
promote: {},
|
|
1341
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
1342
|
+
},
|
|
1343
|
+
auth: { login: {} },
|
|
1344
|
+
brand: { design: { unpack: {} } },
|
|
1345
|
+
bug: { create: {}, list: {}, report: {} },
|
|
1346
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
1347
|
+
capabilities: {},
|
|
1348
|
+
code: {
|
|
1349
|
+
connect: {},
|
|
1350
|
+
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
1351
|
+
repository: { show: {}, list: {}, bind: {} }
|
|
1352
|
+
},
|
|
1353
|
+
config: { diff: {}, plan: {}, apply: {} },
|
|
1354
|
+
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
1355
|
+
credentials: { list: {}, revoke: {} },
|
|
1356
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
1357
|
+
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
1358
|
+
discuss: {
|
|
1359
|
+
groups: {},
|
|
1360
|
+
list: {},
|
|
1361
|
+
topics: {},
|
|
1362
|
+
read: {},
|
|
1363
|
+
post: {},
|
|
1364
|
+
reply: {},
|
|
1365
|
+
resolve: {},
|
|
1366
|
+
who: {},
|
|
1367
|
+
watch: {}
|
|
1368
|
+
},
|
|
1369
|
+
doctor: {},
|
|
1370
|
+
help: {},
|
|
1371
|
+
init: {},
|
|
1372
|
+
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
1373
|
+
o11y: { status: {} },
|
|
1374
|
+
operations: { get: {}, wait: {} },
|
|
1375
|
+
platform: {
|
|
1376
|
+
status: {}
|
|
1377
|
+
},
|
|
1378
|
+
pm: {
|
|
1379
|
+
...PM_ENTITIES,
|
|
1380
|
+
project: { list: {}, add: {}, create: {}, use: {} },
|
|
1381
|
+
handoff: {},
|
|
1382
|
+
next: {},
|
|
1383
|
+
start: {},
|
|
1384
|
+
watch: {}
|
|
1385
|
+
},
|
|
1386
|
+
provision: {},
|
|
1387
|
+
runbook: {
|
|
1388
|
+
ask: {},
|
|
1389
|
+
search: {},
|
|
1390
|
+
impact: {},
|
|
1391
|
+
list: {},
|
|
1392
|
+
get: {},
|
|
1393
|
+
cat: {},
|
|
1394
|
+
new: {},
|
|
1395
|
+
edit: {},
|
|
1396
|
+
comment: {},
|
|
1397
|
+
import: {},
|
|
1398
|
+
visibility: {},
|
|
1399
|
+
publish: {},
|
|
1400
|
+
archive: {},
|
|
1401
|
+
history: {},
|
|
1402
|
+
revert: {},
|
|
1403
|
+
rm: {},
|
|
1404
|
+
lint: {}
|
|
1405
|
+
},
|
|
1406
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
1407
|
+
security: {
|
|
1408
|
+
plan: {},
|
|
1409
|
+
sources: {},
|
|
1410
|
+
run: {},
|
|
1411
|
+
status: {},
|
|
1412
|
+
report: {},
|
|
1413
|
+
github: { connect: {}, disconnect: {} }
|
|
1414
|
+
},
|
|
1415
|
+
setup: {},
|
|
1416
|
+
skill: { install: {} },
|
|
1417
|
+
smoke: {},
|
|
1418
|
+
version: {},
|
|
1419
|
+
whoami: {}
|
|
1420
|
+
};
|
|
1421
|
+
function acceptedAfter(path) {
|
|
1422
|
+
let node = COMMAND_SURFACE;
|
|
1423
|
+
for (const word of path) {
|
|
1424
|
+
node = node?.[word];
|
|
1425
|
+
if (!node) return [];
|
|
1426
|
+
}
|
|
1427
|
+
return Object.keys(node).sort();
|
|
1428
|
+
}
|
|
1429
|
+
function validateInvocation(words2) {
|
|
1430
|
+
let node = COMMAND_SURFACE;
|
|
1431
|
+
const walked = [];
|
|
1432
|
+
for (const word of words2) {
|
|
1433
|
+
if (Object.keys(node).length === 0) return null;
|
|
1434
|
+
const next = node[word];
|
|
1435
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
1436
|
+
walked.push(word);
|
|
1437
|
+
node = next;
|
|
1438
|
+
}
|
|
1439
|
+
return null;
|
|
1440
|
+
}
|
|
1441
|
+
function describeProblem(problem) {
|
|
1442
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
1443
|
+
if (!problem.accepted.length) return `"${problem.word}" is not accepted ${where}. Run "odla-ai help"`;
|
|
1444
|
+
if (!problem.word) return `"odla-ai ${problem.validPrefix}" needs one of: ${problem.accepted.join(", ")}`;
|
|
1445
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
1446
|
+
}
|
|
1447
|
+
function rejectWord(path, word, note) {
|
|
1448
|
+
const sentence = describeProblem({
|
|
1449
|
+
validPrefix: path.join(" "),
|
|
1450
|
+
word: word ?? "",
|
|
1451
|
+
accepted: acceptedAfter(path)
|
|
1452
|
+
});
|
|
1453
|
+
throw new Error(note ? `${sentence}. ${note}` : sentence);
|
|
1454
|
+
}
|
|
1455
|
+
function invocationPath(words2) {
|
|
1456
|
+
let node = COMMAND_SURFACE;
|
|
1457
|
+
const path = [];
|
|
1458
|
+
for (const word of words2) {
|
|
1459
|
+
const next = node[word];
|
|
1460
|
+
if (!next) break;
|
|
1461
|
+
path.push(word);
|
|
1462
|
+
node = next;
|
|
1463
|
+
if (Object.keys(node).length === 0) break;
|
|
1464
|
+
}
|
|
1465
|
+
return path;
|
|
1466
|
+
}
|
|
1467
|
+
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
1468
|
+
const paths2 = [];
|
|
1469
|
+
for (const [word, child] of Object.entries(node)) {
|
|
1470
|
+
const path = [...prefix, word];
|
|
1471
|
+
paths2.push(path);
|
|
1472
|
+
paths2.push(...surfacePaths(child, path));
|
|
1473
|
+
}
|
|
1474
|
+
return paths2;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1278
1477
|
// src/admin-spend.ts
|
|
1279
1478
|
async function call(ctx, method, scope) {
|
|
1280
1479
|
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
@@ -1324,9 +1523,7 @@ async function spendReset(ctx, scope) {
|
|
|
1324
1523
|
async function adminSpend(parsed, ctx) {
|
|
1325
1524
|
const action2 = parsed.positionals[2];
|
|
1326
1525
|
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1327
|
-
if (action2 !== "show" && action2 !== "reset")
|
|
1328
|
-
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1329
|
-
}
|
|
1526
|
+
if (action2 !== "show" && action2 !== "reset") rejectWord(["admin", "spend"], action2);
|
|
1330
1527
|
if (!scope) {
|
|
1331
1528
|
throw new Error(
|
|
1332
1529
|
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
@@ -2125,6 +2322,11 @@ var SET_OPTIONS = [
|
|
|
2125
2322
|
async function adminCommand(parsed, deps = {}) {
|
|
2126
2323
|
const area = parsed.positionals[1];
|
|
2127
2324
|
const action2 = parsed.positionals[2];
|
|
2325
|
+
if (area !== "ai" && area !== "spend") rejectWord(["admin"], area);
|
|
2326
|
+
if (!acceptedAfter(["admin", area]).includes(action2 ?? "")) rejectWord(["admin", area], action2);
|
|
2327
|
+
if (action2 === "credential" && !acceptedAfter(["admin", "ai", "credential"]).includes(parsed.positionals[3] ?? "")) {
|
|
2328
|
+
rejectWord(["admin", "ai", "credential"], parsed.positionals[3]);
|
|
2329
|
+
}
|
|
2128
2330
|
if (area === "spend") {
|
|
2129
2331
|
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
2130
2332
|
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2150,14 +2352,11 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2150
2352
|
out
|
|
2151
2353
|
});
|
|
2152
2354
|
}
|
|
2153
|
-
const credentialSet = action2 === "credential"
|
|
2355
|
+
const credentialSet = action2 === "credential";
|
|
2154
2356
|
const credentials = action2 === "credentials";
|
|
2155
2357
|
const models = action2 === "models";
|
|
2156
2358
|
const usage = action2 === "usage";
|
|
2157
2359
|
const audit = action2 === "audit";
|
|
2158
|
-
if (area !== "ai" || action2 !== "show" && action2 !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
2159
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
2160
|
-
}
|
|
2161
2360
|
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;
|
|
2162
2361
|
assertArgs(parsed, allowed, credentialSet ? 5 : action2 === "set" ? 4 : 3);
|
|
2163
2362
|
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2371,9 +2570,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2371
2570
|
"json"
|
|
2372
2571
|
], 2);
|
|
2373
2572
|
const action2 = parsed.positionals[1] ?? "login";
|
|
2374
|
-
if (action2 !== "login")
|
|
2375
|
-
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
2376
|
-
}
|
|
2573
|
+
if (action2 !== "login") rejectWord(["auth"], action2);
|
|
2377
2574
|
const context = await resolveOperatorContext(parsed, {
|
|
2378
2575
|
allowMissingConfig: true,
|
|
2379
2576
|
requireApp: true
|
|
@@ -2446,9 +2643,7 @@ function bothTenants(cfg) {
|
|
|
2446
2643
|
// src/agent-command.ts
|
|
2447
2644
|
async function agentCommand(parsed, deps = {}) {
|
|
2448
2645
|
const action2 = parsed.positionals[1];
|
|
2449
|
-
if (action2 !== "jobs" && action2 !== "retry")
|
|
2450
|
-
throw new Error(`unknown agent action "${action2 ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
2451
|
-
}
|
|
2646
|
+
if (action2 !== "jobs" && action2 !== "retry") rejectWord(["agent"], action2);
|
|
2452
2647
|
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token"], action2 === "jobs" ? 2 : 3);
|
|
2453
2648
|
if (action2 === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
2454
2649
|
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
@@ -2619,9 +2814,7 @@ async function appOwnersCommand(parsed, dependencies = {}) {
|
|
|
2619
2814
|
await (sub === "add" ? ownersAdd(email, options) : ownersRemove(email, options));
|
|
2620
2815
|
return;
|
|
2621
2816
|
}
|
|
2622
|
-
|
|
2623
|
-
`unknown app owners subcommand "${sub}". Try "odla-ai app owners list", "odla-ai app owners add <email>", or "odla-ai app owners remove <email>".`
|
|
2624
|
-
);
|
|
2817
|
+
rejectWord(["app", "owners"], sub);
|
|
2625
2818
|
}
|
|
2626
2819
|
|
|
2627
2820
|
// src/app-rename.ts
|
|
@@ -2727,8 +2920,10 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2727
2920
|
return;
|
|
2728
2921
|
}
|
|
2729
2922
|
if (sub !== "archive" && sub !== "restore") {
|
|
2730
|
-
|
|
2731
|
-
|
|
2923
|
+
rejectWord(
|
|
2924
|
+
["app"],
|
|
2925
|
+
sub,
|
|
2926
|
+
"Permanent deletion has no CLI: it requires a signed-in owner in Studio."
|
|
2732
2927
|
);
|
|
2733
2928
|
}
|
|
2734
2929
|
assertArgs(parsed, ["config", "token", "email", "yes", "json"], 2);
|
|
@@ -2889,7 +3084,8 @@ async function brandCommand(parsed, deps) {
|
|
|
2889
3084
|
await designUnpack(parsed, deps);
|
|
2890
3085
|
return;
|
|
2891
3086
|
}
|
|
2892
|
-
|
|
3087
|
+
if (subject !== "design") rejectWord(["brand"], subject);
|
|
3088
|
+
rejectWord(["brand", "design"], action2, USAGE);
|
|
2893
3089
|
}
|
|
2894
3090
|
|
|
2895
3091
|
// src/calendar-errors.ts
|
|
@@ -5750,9 +5946,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5750
5946
|
return;
|
|
5751
5947
|
}
|
|
5752
5948
|
if (sub !== "push") {
|
|
5753
|
-
|
|
5754
|
-
`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".`
|
|
5755
|
-
);
|
|
5949
|
+
rejectWord(["secrets"], sub);
|
|
5756
5950
|
}
|
|
5757
5951
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
5758
5952
|
await secretsPush({
|
|
@@ -5765,7 +5959,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5765
5959
|
async function projectCommand(command, parsed, deps) {
|
|
5766
5960
|
if (command === "ai") {
|
|
5767
5961
|
const sub = parsed.positionals[1];
|
|
5768
|
-
if (sub !== "models")
|
|
5962
|
+
if (sub !== "models") rejectWord(["ai"], sub);
|
|
5769
5963
|
assertArgs(parsed, ["config", "env", "provider", "json"], 2);
|
|
5770
5964
|
await aiModels({
|
|
5771
5965
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -5779,9 +5973,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5779
5973
|
}
|
|
5780
5974
|
if (command === "config") {
|
|
5781
5975
|
const sub = parsed.positionals[1];
|
|
5782
|
-
if (sub !== "diff" && sub !== "plan" && sub !== "apply")
|
|
5783
|
-
throw new Error(`unknown config subcommand "${sub ?? ""}". Try "odla-ai config diff --json".`);
|
|
5784
|
-
}
|
|
5976
|
+
if (sub !== "diff" && sub !== "plan" && sub !== "apply") rejectWord(["config"], sub);
|
|
5785
5977
|
assertArgs(
|
|
5786
5978
|
parsed,
|
|
5787
5979
|
sub === "apply" ? ["config", "plan", "idempotency-key", "token", "email", "open", "json"] : ["config", "token", "email", "open", "json"],
|
|
@@ -5809,7 +6001,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5809
6001
|
if (command === "operations") {
|
|
5810
6002
|
const sub = parsed.positionals[1];
|
|
5811
6003
|
if (sub !== "get" && sub !== "wait") {
|
|
5812
|
-
|
|
6004
|
+
rejectWord(["operations"], sub);
|
|
5813
6005
|
}
|
|
5814
6006
|
assertArgs(
|
|
5815
6007
|
parsed,
|
|
@@ -5883,7 +6075,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5883
6075
|
}
|
|
5884
6076
|
if (command === "skill") {
|
|
5885
6077
|
const sub = parsed.positionals[1];
|
|
5886
|
-
if (sub !== "install")
|
|
6078
|
+
if (sub !== "install") rejectWord(["skill"], sub);
|
|
5887
6079
|
install(parsed, 2, deps);
|
|
5888
6080
|
return true;
|
|
5889
6081
|
}
|
|
@@ -10441,9 +10633,7 @@ function grantsUrl(cfg, suffix = "") {
|
|
|
10441
10633
|
async function codeGrantCommand(parsed, deps = {}) {
|
|
10442
10634
|
const action2 = parsed.positionals[2];
|
|
10443
10635
|
if (!isAction(action2)) {
|
|
10444
|
-
|
|
10445
|
-
`unknown code grant action "${action2 ?? ""}". Try "odla-ai code grant list --env dev".`
|
|
10446
|
-
);
|
|
10636
|
+
rejectWord(["code", "grant"], action2);
|
|
10447
10637
|
}
|
|
10448
10638
|
const decides = action2 === "approve" || action2 === "revoke";
|
|
10449
10639
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open"], decides ? 4 : 3);
|
|
@@ -10563,9 +10753,7 @@ function repositoryUrl(cfg, suffix = "") {
|
|
|
10563
10753
|
async function codeRepositoryCommand(parsed, deps = {}) {
|
|
10564
10754
|
const action2 = parsed.positionals[2];
|
|
10565
10755
|
if (!isAction2(action2)) {
|
|
10566
|
-
|
|
10567
|
-
`unknown code repository action "${action2 ?? ""}". Try "odla-ai code repository show --env dev".`
|
|
10568
|
-
);
|
|
10756
|
+
rejectWord(["code", "repository"], action2);
|
|
10569
10757
|
}
|
|
10570
10758
|
assertArgs(parsed, ["config", "env", "repo", "json", "token", "email", "open"], 3);
|
|
10571
10759
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
@@ -10640,9 +10828,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10640
10828
|
});
|
|
10641
10829
|
}
|
|
10642
10830
|
if (sub !== "connect") {
|
|
10643
|
-
|
|
10644
|
-
`unknown code subcommand "${sub ?? ""}". Try "odla-ai code connect --env dev" or "odla-ai code grant list --env dev".`
|
|
10645
|
-
);
|
|
10831
|
+
rejectWord(["code"], sub);
|
|
10646
10832
|
}
|
|
10647
10833
|
assertArgs(parsed, [
|
|
10648
10834
|
"config",
|
|
@@ -10779,9 +10965,7 @@ async function contextCommand(parsed, deps = {}) {
|
|
|
10779
10965
|
return;
|
|
10780
10966
|
}
|
|
10781
10967
|
if (action2 !== "show") {
|
|
10782
|
-
|
|
10783
|
-
`unknown context action "${action2 ?? ""}". Try show|list|save|remove.`
|
|
10784
|
-
);
|
|
10968
|
+
rejectWord(["context"], action2);
|
|
10785
10969
|
}
|
|
10786
10970
|
const context = await resolveOperatorContext(parsed, {
|
|
10787
10971
|
allowMissingConfig: true,
|
|
@@ -10837,9 +11021,7 @@ async function responseError(response2) {
|
|
|
10837
11021
|
}
|
|
10838
11022
|
async function credentialCommand(parsed, deps = {}) {
|
|
10839
11023
|
const action2 = parsed.positionals[1] ?? "list";
|
|
10840
|
-
if (action2 !== "list" && action2 !== "revoke")
|
|
10841
|
-
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
10842
|
-
}
|
|
11024
|
+
if (action2 !== "list" && action2 !== "revoke") rejectWord(["credentials"], action2);
|
|
10843
11025
|
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
10844
11026
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
10845
11027
|
const doFetch = deps.fetch ?? fetch;
|
|
@@ -11248,9 +11430,17 @@ Safety:
|
|
|
11248
11430
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
11249
11431
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
11250
11432
|
continues provisioning with the approved replacement credential.
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11433
|
+
Every command says so on STDERR when this CLI is older than the one npm
|
|
11434
|
+
serves, naming the repair for how this executable was launched \u2014 rebuild the
|
|
11435
|
+
worktree, npm i the dependency, or npx the scoped package. The answer is read
|
|
11436
|
+
from a cache under ~/.odla refreshed in the background at most every three
|
|
11437
|
+
hours, so no invocation waits on the registry and stdout is never touched.
|
|
11438
|
+
Set ODLA_CLI_UPDATE_CHECK=0 to switch it off.
|
|
11439
|
+
Before a non-dry-run provision, the executable additionally checks npm LIVE
|
|
11440
|
+
rather than trusting that cache: a security-sensitive grant shape must not
|
|
11441
|
+
ride on a three-hour-old answer. A confirmed stale client stops with a safe
|
|
11442
|
+
npx rerun command; a workspace-linked client also identifies the worktree
|
|
11443
|
+
that must be updated.
|
|
11254
11444
|
Run Code from a GitHub checkout already connected to an app in Studio; an
|
|
11255
11445
|
odla.config.mjs may select the app explicitly but is not required. With an
|
|
11256
11446
|
enrolled code.session device, the Studio repository selection authorizes the
|
|
@@ -11841,7 +12031,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11841
12031
|
assertArgs(parsed, ALLOWED, 3);
|
|
11842
12032
|
const action2 = parsed.positionals[1];
|
|
11843
12033
|
const id2 = parsed.positionals[2];
|
|
11844
|
-
if (!
|
|
12034
|
+
if (!acceptedAfter(["discuss"]).includes(action2 ?? "")) rejectWord(["discuss"], action2);
|
|
11845
12035
|
const ctx = await buildContext(parsed, deps);
|
|
11846
12036
|
switch (action2) {
|
|
11847
12037
|
case "groups":
|
|
@@ -11865,7 +12055,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11865
12055
|
return;
|
|
11866
12056
|
}
|
|
11867
12057
|
default:
|
|
11868
|
-
|
|
12058
|
+
rejectWord(["discuss"], action2);
|
|
11869
12059
|
}
|
|
11870
12060
|
}
|
|
11871
12061
|
|
|
@@ -12775,7 +12965,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12775
12965
|
assertArgs(parsed, COMMON_OPTIONS, 4);
|
|
12776
12966
|
return pmProjectUse(await buildContext2(parsed, deps), requireId2(parsed.positionals[3], action3));
|
|
12777
12967
|
}
|
|
12778
|
-
|
|
12968
|
+
rejectWord(["pm", "project"], action3);
|
|
12779
12969
|
}
|
|
12780
12970
|
if (word === "next") {
|
|
12781
12971
|
assertArgs(parsed, [...COMMON_OPTIONS, "app", "project", "verbose"], 2);
|
|
@@ -12806,10 +12996,10 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12806
12996
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
12807
12997
|
}
|
|
12808
12998
|
const entity = ALIASES[word];
|
|
12809
|
-
if (!entity)
|
|
12999
|
+
if (!entity) rejectWord(["pm"], word);
|
|
12810
13000
|
const requestedAction = parsed.positionals[2] ?? "list";
|
|
12811
13001
|
const action2 = canonicalAction(requestedAction);
|
|
12812
|
-
if (!action2)
|
|
13002
|
+
if (!action2) rejectWord(["pm", word], requestedAction);
|
|
12813
13003
|
assertArgs(parsed, allowedOptions(entity, action2), 4);
|
|
12814
13004
|
if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
|
|
12815
13005
|
throw new Error(`pm ${action2} is only valid for tasks`);
|
|
@@ -12895,12 +13085,7 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
12895
13085
|
if (action2 === "status") {
|
|
12896
13086
|
return platformStatus(parsed, deps);
|
|
12897
13087
|
}
|
|
12898
|
-
|
|
12899
|
-
`unknown platform action "${[
|
|
12900
|
-
action2,
|
|
12901
|
-
parsed.positionals[2]
|
|
12902
|
-
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
12903
|
-
);
|
|
13088
|
+
rejectWord(["platform"], action2);
|
|
12904
13089
|
}
|
|
12905
13090
|
async function platformStatus(parsed, deps) {
|
|
12906
13091
|
assertArgs(
|
|
@@ -13237,9 +13422,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
13237
13422
|
);
|
|
13238
13423
|
const action2 = parsed.positionals[1];
|
|
13239
13424
|
if (action2 !== "status") {
|
|
13240
|
-
|
|
13241
|
-
`unknown o11y action "${action2 ?? ""}". Try "odla-ai o11y status --json".`
|
|
13242
|
-
);
|
|
13425
|
+
rejectWord(["o11y"], action2);
|
|
13243
13426
|
}
|
|
13244
13427
|
const minutes = statusMinutes(
|
|
13245
13428
|
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
@@ -13492,9 +13675,7 @@ var OPTIONS = [
|
|
|
13492
13675
|
async function monitorCommand(parsed, deps = {}) {
|
|
13493
13676
|
assertArgs(parsed, OPTIONS, 3);
|
|
13494
13677
|
const action2 = parsed.positionals[1] ?? "status";
|
|
13495
|
-
if (!["
|
|
13496
|
-
throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
|
|
13497
|
-
}
|
|
13678
|
+
if (!acceptedAfter(["monitor"]).includes(action2)) rejectWord(["monitor"], action2);
|
|
13498
13679
|
const context = await resolveOperatorContext(parsed, {
|
|
13499
13680
|
allowMissingConfig: action2 !== "plan" && action2 !== "apply",
|
|
13500
13681
|
requireApp: true
|
|
@@ -14124,187 +14305,6 @@ async function provision(options) {
|
|
|
14124
14305
|
// src/record.ts
|
|
14125
14306
|
var import_node_fs18 = require("fs");
|
|
14126
14307
|
var import_node_process18 = __toESM(require("process"), 1);
|
|
14127
|
-
|
|
14128
|
-
// src/surface.ts
|
|
14129
|
-
var PM_ACTIONS = {
|
|
14130
|
-
list: {},
|
|
14131
|
-
add: {},
|
|
14132
|
-
create: {},
|
|
14133
|
-
get: {},
|
|
14134
|
-
set: {},
|
|
14135
|
-
update: {},
|
|
14136
|
-
status: {},
|
|
14137
|
-
move: {},
|
|
14138
|
-
done: {},
|
|
14139
|
-
comment: {},
|
|
14140
|
-
comments: {},
|
|
14141
|
-
ref: {},
|
|
14142
|
-
rm: {},
|
|
14143
|
-
delete: {}
|
|
14144
|
-
};
|
|
14145
|
-
var PM_TASK_ACTIONS = {
|
|
14146
|
-
...PM_ACTIONS,
|
|
14147
|
-
ready: {},
|
|
14148
|
-
claim: {},
|
|
14149
|
-
release: {}
|
|
14150
|
-
};
|
|
14151
|
-
var PM_ENTITIES = {
|
|
14152
|
-
...Object.fromEntries(
|
|
14153
|
-
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
14154
|
-
),
|
|
14155
|
-
task: PM_TASK_ACTIONS,
|
|
14156
|
-
kanban: PM_TASK_ACTIONS
|
|
14157
|
-
};
|
|
14158
|
-
var COMMAND_SURFACE = {
|
|
14159
|
-
agent: { jobs: {}, retry: {} },
|
|
14160
|
-
ai: { models: {} },
|
|
14161
|
-
admin: {
|
|
14162
|
-
ai: {
|
|
14163
|
-
show: {},
|
|
14164
|
-
set: {},
|
|
14165
|
-
credentials: {},
|
|
14166
|
-
models: {},
|
|
14167
|
-
usage: {},
|
|
14168
|
-
audit: {},
|
|
14169
|
-
credential: { set: {} }
|
|
14170
|
-
}
|
|
14171
|
-
},
|
|
14172
|
-
app: {
|
|
14173
|
-
archive: {},
|
|
14174
|
-
restore: {},
|
|
14175
|
-
export: {},
|
|
14176
|
-
import: {},
|
|
14177
|
-
rename: {},
|
|
14178
|
-
"refresh-sandbox": {},
|
|
14179
|
-
"go-live": {},
|
|
14180
|
-
promote: {},
|
|
14181
|
-
owners: { list: {}, add: {}, remove: {} }
|
|
14182
|
-
},
|
|
14183
|
-
auth: { login: {} },
|
|
14184
|
-
brand: { design: { unpack: {} } },
|
|
14185
|
-
bug: { create: {}, list: {}, report: {} },
|
|
14186
|
-
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
14187
|
-
capabilities: {},
|
|
14188
|
-
code: {
|
|
14189
|
-
connect: {},
|
|
14190
|
-
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
14191
|
-
repository: { show: {}, list: {}, bind: {} }
|
|
14192
|
-
},
|
|
14193
|
-
config: { diff: {}, plan: {}, apply: {} },
|
|
14194
|
-
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
14195
|
-
credentials: { list: {}, revoke: {} },
|
|
14196
|
-
device: { enroll: {}, list: {}, revoke: {} },
|
|
14197
|
-
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
14198
|
-
discuss: {
|
|
14199
|
-
groups: {},
|
|
14200
|
-
list: {},
|
|
14201
|
-
topics: {},
|
|
14202
|
-
read: {},
|
|
14203
|
-
post: {},
|
|
14204
|
-
reply: {},
|
|
14205
|
-
resolve: {},
|
|
14206
|
-
who: {},
|
|
14207
|
-
watch: {}
|
|
14208
|
-
},
|
|
14209
|
-
doctor: {},
|
|
14210
|
-
help: {},
|
|
14211
|
-
init: {},
|
|
14212
|
-
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
14213
|
-
o11y: { status: {} },
|
|
14214
|
-
operations: { get: {}, wait: {} },
|
|
14215
|
-
platform: {
|
|
14216
|
-
status: {}
|
|
14217
|
-
},
|
|
14218
|
-
pm: {
|
|
14219
|
-
...PM_ENTITIES,
|
|
14220
|
-
project: { list: {}, add: {}, create: {}, use: {} },
|
|
14221
|
-
handoff: {},
|
|
14222
|
-
next: {},
|
|
14223
|
-
start: {},
|
|
14224
|
-
watch: {}
|
|
14225
|
-
},
|
|
14226
|
-
provision: {},
|
|
14227
|
-
runbook: {
|
|
14228
|
-
ask: {},
|
|
14229
|
-
search: {},
|
|
14230
|
-
impact: {},
|
|
14231
|
-
list: {},
|
|
14232
|
-
get: {},
|
|
14233
|
-
cat: {},
|
|
14234
|
-
new: {},
|
|
14235
|
-
edit: {},
|
|
14236
|
-
comment: {},
|
|
14237
|
-
import: {},
|
|
14238
|
-
visibility: {},
|
|
14239
|
-
publish: {},
|
|
14240
|
-
archive: {},
|
|
14241
|
-
history: {},
|
|
14242
|
-
revert: {},
|
|
14243
|
-
rm: {},
|
|
14244
|
-
lint: {}
|
|
14245
|
-
},
|
|
14246
|
-
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
14247
|
-
security: {
|
|
14248
|
-
plan: {},
|
|
14249
|
-
sources: {},
|
|
14250
|
-
run: {},
|
|
14251
|
-
status: {},
|
|
14252
|
-
report: {},
|
|
14253
|
-
github: { connect: {}, disconnect: {} }
|
|
14254
|
-
},
|
|
14255
|
-
setup: {},
|
|
14256
|
-
skill: { install: {} },
|
|
14257
|
-
smoke: {},
|
|
14258
|
-
version: {},
|
|
14259
|
-
whoami: {}
|
|
14260
|
-
};
|
|
14261
|
-
function acceptedAfter(path) {
|
|
14262
|
-
let node = COMMAND_SURFACE;
|
|
14263
|
-
for (const word of path) {
|
|
14264
|
-
node = node?.[word];
|
|
14265
|
-
if (!node) return [];
|
|
14266
|
-
}
|
|
14267
|
-
return Object.keys(node).sort();
|
|
14268
|
-
}
|
|
14269
|
-
function validateInvocation(words2) {
|
|
14270
|
-
let node = COMMAND_SURFACE;
|
|
14271
|
-
const walked = [];
|
|
14272
|
-
for (const word of words2) {
|
|
14273
|
-
if (Object.keys(node).length === 0) return null;
|
|
14274
|
-
const next = node[word];
|
|
14275
|
-
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
14276
|
-
walked.push(word);
|
|
14277
|
-
node = next;
|
|
14278
|
-
}
|
|
14279
|
-
return null;
|
|
14280
|
-
}
|
|
14281
|
-
function describeProblem(problem) {
|
|
14282
|
-
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
14283
|
-
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
14284
|
-
}
|
|
14285
|
-
function invocationPath(words2) {
|
|
14286
|
-
let node = COMMAND_SURFACE;
|
|
14287
|
-
const path = [];
|
|
14288
|
-
for (const word of words2) {
|
|
14289
|
-
const next = node[word];
|
|
14290
|
-
if (!next) break;
|
|
14291
|
-
path.push(word);
|
|
14292
|
-
node = next;
|
|
14293
|
-
if (Object.keys(node).length === 0) break;
|
|
14294
|
-
}
|
|
14295
|
-
return path;
|
|
14296
|
-
}
|
|
14297
|
-
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
14298
|
-
const paths2 = [];
|
|
14299
|
-
for (const [word, child] of Object.entries(node)) {
|
|
14300
|
-
const path = [...prefix, word];
|
|
14301
|
-
paths2.push(path);
|
|
14302
|
-
paths2.push(...surfacePaths(child, path));
|
|
14303
|
-
}
|
|
14304
|
-
return paths2;
|
|
14305
|
-
}
|
|
14306
|
-
|
|
14307
|
-
// src/record.ts
|
|
14308
14308
|
function recordInvocation(parsed) {
|
|
14309
14309
|
const file = import_node_process18.default.env.ODLA_CLI_RECORD;
|
|
14310
14310
|
if (!file) return;
|
|
@@ -14393,6 +14393,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14393
14393
|
"wait"
|
|
14394
14394
|
], 3);
|
|
14395
14395
|
const action2 = parsed.positionals[1] ?? "";
|
|
14396
|
+
if (!acceptedAfter(["device"]).includes(action2)) rejectWord(["device"], action2);
|
|
14396
14397
|
const out = deps.stdout ?? console;
|
|
14397
14398
|
const doFetch = deps.fetch ?? fetch;
|
|
14398
14399
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
@@ -14400,7 +14401,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14400
14401
|
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
14401
14402
|
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
14402
14403
|
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
14403
|
-
|
|
14404
|
+
rejectWord(["device"], action2);
|
|
14404
14405
|
}
|
|
14405
14406
|
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14406
14407
|
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
@@ -15347,6 +15348,7 @@ async function buildContext3(parsed, deps, action2) {
|
|
|
15347
15348
|
async function runbookCommand(parsed, deps = {}) {
|
|
15348
15349
|
const action2 = parsed.positionals[1] ?? "list";
|
|
15349
15350
|
assertArgs(parsed, ALLOWED2, action2 === "ask" || action2 === "search" ? 64 : 4);
|
|
15351
|
+
if (!acceptedAfter(["runbook"]).includes(action2)) rejectWord(["runbook"], action2);
|
|
15350
15352
|
const ctx = await buildContext3(parsed, deps, action2);
|
|
15351
15353
|
const slug = parsed.positionals[2];
|
|
15352
15354
|
switch (action2) {
|
|
@@ -15436,7 +15438,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15436
15438
|
case "rm":
|
|
15437
15439
|
return runbookRemove(ctx, requireSlug(slug, "rm"));
|
|
15438
15440
|
default:
|
|
15439
|
-
|
|
15441
|
+
rejectWord(["runbook"], action2);
|
|
15440
15442
|
}
|
|
15441
15443
|
}
|
|
15442
15444
|
|
|
@@ -16050,9 +16052,7 @@ async function securityCommand(parsed, dependencies) {
|
|
|
16050
16052
|
else printHostedReport(context.stdout, report5);
|
|
16051
16053
|
return;
|
|
16052
16054
|
}
|
|
16053
|
-
if (sub !== "run")
|
|
16054
|
-
throw new Error('unknown security command. Try "odla-ai security plan", "security sources", or "security run".');
|
|
16055
|
-
}
|
|
16055
|
+
if (sub !== "run") rejectWord(["security"], sub);
|
|
16056
16056
|
const sourceId = stringOpt(parsed.options.source);
|
|
16057
16057
|
if (sourceId) await runSourceSecurityCommand(parsed, dependencies, sourceId);
|
|
16058
16058
|
else await runLocalSecurityCommand(parsed, dependencies);
|
|
@@ -16069,9 +16069,7 @@ async function githubSecurityCommand(parsed, dependencies) {
|
|
|
16069
16069
|
stringOpt(parsed.options.env)
|
|
16070
16070
|
);
|
|
16071
16071
|
}
|
|
16072
|
-
if (action2 !== "connect")
|
|
16073
|
-
throw new Error('unknown security github command. Try "odla-ai security github connect".');
|
|
16074
|
-
}
|
|
16072
|
+
if (action2 !== "connect") rejectWord(["security", "github"], action2);
|
|
16075
16073
|
assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
|
|
16076
16074
|
await requireStudioHuman(
|
|
16077
16075
|
stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -16118,6 +16116,87 @@ async function securityStatus(parsed, dependencies) {
|
|
|
16118
16116
|
}
|
|
16119
16117
|
}
|
|
16120
16118
|
|
|
16119
|
+
// src/update-notice.ts
|
|
16120
|
+
var import_node_child_process8 = require("child_process");
|
|
16121
|
+
var import_node_fs24 = require("fs");
|
|
16122
|
+
var import_node_path23 = require("path");
|
|
16123
|
+
var import_node_process21 = __toESM(require("process"), 1);
|
|
16124
|
+
var REGISTRY_URL = "https://registry.npmjs.org/@odla-ai%2fcli/latest";
|
|
16125
|
+
var INTERVAL_MS = 3 * 60 * 60 * 1e3;
|
|
16126
|
+
var VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
16127
|
+
var refreshScheduled = false;
|
|
16128
|
+
function updateCacheFile(env) {
|
|
16129
|
+
return odlaHomePath(["cli-update.json"], env);
|
|
16130
|
+
}
|
|
16131
|
+
function readUpdateCache(path) {
|
|
16132
|
+
try {
|
|
16133
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(path, "utf8"));
|
|
16134
|
+
if (typeof parsed.latest !== "string" || !VERSION.test(parsed.latest)) return null;
|
|
16135
|
+
if (typeof parsed.checkedAt !== "number") return null;
|
|
16136
|
+
return { latest: parsed.latest, checkedAt: parsed.checkedAt };
|
|
16137
|
+
} catch {
|
|
16138
|
+
return null;
|
|
16139
|
+
}
|
|
16140
|
+
}
|
|
16141
|
+
function updateNotice(options = {}) {
|
|
16142
|
+
const env = options.env ?? import_node_process21.default.env;
|
|
16143
|
+
if (env.ODLA_CLI_UPDATE_CHECK === "0") return null;
|
|
16144
|
+
const current = options.currentVersion ?? cliVersion();
|
|
16145
|
+
if (!VERSION.test(current)) return null;
|
|
16146
|
+
const path = updateCacheFile(env);
|
|
16147
|
+
const cache2 = readUpdateCache(path);
|
|
16148
|
+
const now = options.now ?? Date.now();
|
|
16149
|
+
if ((!cache2 || now - cache2.checkedAt > INTERVAL_MS) && !refreshScheduled) {
|
|
16150
|
+
refreshScheduled = true;
|
|
16151
|
+
(options.refresh ?? spawnRefresh)(path, env.ODLA_CLI_REGISTRY_URL ?? REGISTRY_URL);
|
|
16152
|
+
}
|
|
16153
|
+
if (!cache2 || compareVersions(current, cache2.latest) >= 0) return null;
|
|
16154
|
+
return renderNotice(current, cache2.latest, options.entryPath ?? import_node_process21.default.argv[1]);
|
|
16155
|
+
}
|
|
16156
|
+
function renderNotice(current, latest, entryPath) {
|
|
16157
|
+
const resolved = resolvedEntryPath(entryPath);
|
|
16158
|
+
const behindMajor = Number(latest.split(".")[0]) > Number(current.split(".")[0]);
|
|
16159
|
+
const repair = isWorkspaceCli(resolved) ? `this is the workspace build at ${resolved} \u2014 rebase that worktree and rebuild it` : resolved.includes("/_npx/") ? `run npx --yes @odla-ai/cli@${latest} <command>` : `run npm i @odla-ai/cli@${latest}`;
|
|
16160
|
+
const severity = behindMajor ? "a MAJOR version behind, so commands and flags this version has may no longer exist" : "behind";
|
|
16161
|
+
return `odla-ai: ${current} is ${severity}; npm serves ${latest}. To update, ${repair}.`;
|
|
16162
|
+
}
|
|
16163
|
+
function spawnRefresh(cachePath, registryUrl) {
|
|
16164
|
+
const script = `
|
|
16165
|
+
const {mkdirSync,writeFileSync}=require("node:fs");
|
|
16166
|
+
const {dirname}=require("node:path");
|
|
16167
|
+
const [path,url]=process.argv.slice(1);
|
|
16168
|
+
const done=setTimeout(()=>process.exit(0),5000); done.unref();
|
|
16169
|
+
fetch(url,{headers:{accept:"application/vnd.npm.install-v1+json"}})
|
|
16170
|
+
.then(r=>r.ok?r.json():null)
|
|
16171
|
+
.then(b=>{
|
|
16172
|
+
const v=b&&b.version;
|
|
16173
|
+
if (typeof v!=="string"||!/^\\d+\\.\\d+\\.\\d+/.test(v)) return;
|
|
16174
|
+
mkdirSync(dirname(path),{recursive:true});
|
|
16175
|
+
writeFileSync(path,JSON.stringify({latest:v,checkedAt:Date.now()}));
|
|
16176
|
+
})
|
|
16177
|
+
.catch(()=>{});
|
|
16178
|
+
`;
|
|
16179
|
+
try {
|
|
16180
|
+
(0, import_node_child_process8.spawn)(import_node_process21.default.execPath, ["-e", script, cachePath, registryUrl], {
|
|
16181
|
+
detached: true,
|
|
16182
|
+
stdio: "ignore"
|
|
16183
|
+
}).unref();
|
|
16184
|
+
} catch {
|
|
16185
|
+
}
|
|
16186
|
+
}
|
|
16187
|
+
function resolvedEntryPath(entryPath) {
|
|
16188
|
+
if (!entryPath) return "unknown executable";
|
|
16189
|
+
try {
|
|
16190
|
+
return (0, import_node_fs24.realpathSync)(entryPath);
|
|
16191
|
+
} catch {
|
|
16192
|
+
return entryPath;
|
|
16193
|
+
}
|
|
16194
|
+
}
|
|
16195
|
+
function isWorkspaceCli(entryPath) {
|
|
16196
|
+
const normalized = entryPath.replaceAll("\\", "/");
|
|
16197
|
+
return /\/packages\/cli\/(dist|bin)\//.test(normalized) && !normalized.includes("/node_modules/");
|
|
16198
|
+
}
|
|
16199
|
+
|
|
16121
16200
|
// src/exit-code.ts
|
|
16122
16201
|
function exitCodeFor(err) {
|
|
16123
16202
|
const code = err?.code;
|
|
@@ -16145,6 +16224,11 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
16145
16224
|
throw error;
|
|
16146
16225
|
} finally {
|
|
16147
16226
|
renderAdvisories(out, advisories);
|
|
16227
|
+
try {
|
|
16228
|
+
const behind = updateNotice();
|
|
16229
|
+
if (behind) out.error(behind);
|
|
16230
|
+
} catch {
|
|
16231
|
+
}
|
|
16148
16232
|
}
|
|
16149
16233
|
}
|
|
16150
16234
|
async function dispatchCli(argv, dependencies) {
|
|
@@ -16225,6 +16309,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16225
16309
|
}
|
|
16226
16310
|
if (command === "bug") {
|
|
16227
16311
|
const action2 = parsed.positionals[1] ?? "list";
|
|
16312
|
+
if (!acceptedAfter(["bug"]).includes(action2)) rejectWord(["bug"], action2);
|
|
16228
16313
|
const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
16229
16314
|
await pmCommand({
|
|
16230
16315
|
...parsed,
|
|
@@ -16253,7 +16338,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16253
16338
|
return;
|
|
16254
16339
|
}
|
|
16255
16340
|
if (await projectCommand(command, parsed, runtime)) return;
|
|
16256
|
-
|
|
16341
|
+
rejectWord([], command);
|
|
16257
16342
|
}
|
|
16258
16343
|
async function provisionCommand(parsed, dependencies) {
|
|
16259
16344
|
assertArgs(parsed, [
|
|
@@ -16297,7 +16382,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
16297
16382
|
async function calendarCommand(parsed, dependencies) {
|
|
16298
16383
|
const sub = parsed.positionals[1];
|
|
16299
16384
|
if (sub !== "status" && sub !== "calendars" && sub !== "connect" && sub !== "disconnect") {
|
|
16300
|
-
|
|
16385
|
+
rejectWord(["calendar"], sub);
|
|
16301
16386
|
}
|
|
16302
16387
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open", "yes"], 2);
|
|
16303
16388
|
if (sub !== "status" && sub !== "calendars" && parsed.options.json !== void 0) throw new Error(`--json is supported only by calendar status/calendars`);
|