@odla-ai/cli 0.25.2 → 0.25.6
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 +31 -15
- package/REQUIREMENTS.md +5 -5
- package/dist/bin.cjs +379 -82
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +3 -2
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-MX6FSJUN.js → chunk-MWLZO2EZ.js} +379 -82
- package/dist/chunk-MWLZO2EZ.js.map +1 -0
- package/dist/index.cjs +378 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-MX6FSJUN.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1935,6 +1935,37 @@ function redactSecrets(value) {
|
|
|
1935
1935
|
for (const [pattern, replacement] of REPLACEMENTS) result = result.replace(pattern, replacement);
|
|
1936
1936
|
return result;
|
|
1937
1937
|
}
|
|
1938
|
+
function redactingOutput(output) {
|
|
1939
|
+
const redactArgs = (args) => args.map((value) => redactOutputValue(value, /* @__PURE__ */ new WeakMap()));
|
|
1940
|
+
return {
|
|
1941
|
+
log: (...args) => output.log(...redactArgs(args)),
|
|
1942
|
+
error: (...args) => output.error(...redactArgs(args))
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
function redactOutputValue(value, seen) {
|
|
1946
|
+
if (typeof value === "string") return redactSecrets(value);
|
|
1947
|
+
if (value instanceof Error) {
|
|
1948
|
+
const redacted = new Error(redactSecrets(value.message));
|
|
1949
|
+
redacted.name = value.name;
|
|
1950
|
+
if (value.stack) redacted.stack = redactSecrets(value.stack);
|
|
1951
|
+
return redacted;
|
|
1952
|
+
}
|
|
1953
|
+
if (!value || typeof value !== "object") return value;
|
|
1954
|
+
const prior = seen.get(value);
|
|
1955
|
+
if (prior) return prior;
|
|
1956
|
+
if (Array.isArray(value)) {
|
|
1957
|
+
const next2 = [];
|
|
1958
|
+
seen.set(value, next2);
|
|
1959
|
+
for (const item of value) next2.push(redactOutputValue(item, seen));
|
|
1960
|
+
return next2;
|
|
1961
|
+
}
|
|
1962
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1963
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
1964
|
+
const next = {};
|
|
1965
|
+
seen.set(value, next);
|
|
1966
|
+
for (const [key, item] of Object.entries(value)) next[key] = redactOutputValue(item, seen);
|
|
1967
|
+
return next;
|
|
1968
|
+
}
|
|
1938
1969
|
function looksSecret(value) {
|
|
1939
1970
|
return redactSecrets(value) !== value || value.includes("-----BEGIN");
|
|
1940
1971
|
}
|
|
@@ -2339,6 +2370,7 @@ var CAPABILITIES = {
|
|
|
2339
2370
|
"compose declared app-capability schema/rules, create guarded seeds when absent, and configure platform AI, auth, and deployment links",
|
|
2340
2371
|
"apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)",
|
|
2341
2372
|
"validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
|
|
2373
|
+
"read one versioned o11y status envelope spanning application RED, current live-sync freshness/load, the protected commit-to-visible canary, collector ingest/scheduler trust, provider-owned runtime metrics, and a bounded machine verdict",
|
|
2342
2374
|
"run app-attributed hosted security discovery and independent validation without provider keys",
|
|
2343
2375
|
"connect/revoke source-read-only GitHub sources and drive commit-pinned hosted security jobs without PATs or provider keys",
|
|
2344
2376
|
"let admins manage system AI policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"
|
|
@@ -2359,7 +2391,7 @@ var CAPABILITIES = {
|
|
|
2359
2391
|
"approve GitHub App repository access separately from redacted-snippet disclosure"
|
|
2360
2392
|
],
|
|
2361
2393
|
studio: [
|
|
2362
|
-
"view telemetry and environment state",
|
|
2394
|
+
"view application telemetry, reconciled live-sync load/freshness, commit-to-visible canary health, provider-owned Worker runtime metrics, and environment state",
|
|
2363
2395
|
"let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them",
|
|
2364
2396
|
"review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens",
|
|
2365
2397
|
"perform manual credential recovery \u2014 for the primary owner or any co-owner \u2014 when the CLI's local shown-once copy is unavailable",
|
|
@@ -3351,9 +3383,17 @@ async function smoke(options) {
|
|
|
3351
3383
|
throw new Error(`local credentials are for app "${credentials.appId}", but config app is "${cfg.app.id}"`);
|
|
3352
3384
|
}
|
|
3353
3385
|
const entry = credentials.envs[env];
|
|
3354
|
-
if (!entry?.tenantId
|
|
3386
|
+
if (!entry?.tenantId) {
|
|
3387
|
+
throw new Error(`local credentials have no tenant for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
3388
|
+
}
|
|
3389
|
+
const hasDb = cfg.services.includes("db");
|
|
3390
|
+
const hasO11y = cfg.services.includes("o11y");
|
|
3391
|
+
if (hasDb && !entry.dbKey) {
|
|
3355
3392
|
throw new Error(`local credentials have no db key for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
3356
3393
|
}
|
|
3394
|
+
if (hasO11y && !entry.o11yToken) {
|
|
3395
|
+
throw new Error(`local credentials have no o11y token for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
3396
|
+
}
|
|
3357
3397
|
const doFetch = options.fetch ?? fetch;
|
|
3358
3398
|
out.log(`smoke: ${cfg.app.id}/${env}`);
|
|
3359
3399
|
out.log(` tenant: ${entry.tenantId}`);
|
|
@@ -3366,6 +3406,7 @@ async function smoke(options) {
|
|
|
3366
3406
|
}
|
|
3367
3407
|
out.log(` ai: ${provider}`);
|
|
3368
3408
|
}
|
|
3409
|
+
if (hasO11y) out.log(` o11y: credentials present`);
|
|
3369
3410
|
if (cfg.services.includes("calendar")) {
|
|
3370
3411
|
const token = await getDeveloperToken(
|
|
3371
3412
|
cfg,
|
|
@@ -3384,26 +3425,30 @@ async function smoke(options) {
|
|
|
3384
3425
|
out.log(` calendar: ${status.status}, bookable (booking \u2192 ${status.bookingCalendarId ?? "primary"})`);
|
|
3385
3426
|
}
|
|
3386
3427
|
const database = await resolveDatabaseConfig(cfg);
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3428
|
+
if (hasDb) {
|
|
3429
|
+
const expectedSchema = database.schema;
|
|
3430
|
+
const expectedEntities = serializedEntities(expectedSchema);
|
|
3431
|
+
const liveSchemaPayload = await getJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(entry.tenantId)}/schema`, entry.dbKey);
|
|
3432
|
+
const liveSchema = liveSchemaPayload.schema ?? liveSchemaPayload;
|
|
3433
|
+
const liveEntities = serializedEntities(liveSchema);
|
|
3434
|
+
if (expectedEntities.length) {
|
|
3435
|
+
const missing = expectedEntities.filter((entity) => !liveEntities.includes(entity));
|
|
3436
|
+
if (missing.length) throw new Error(`live schema is missing expected entities: ${missing.join(", ")}`);
|
|
3437
|
+
}
|
|
3438
|
+
out.log(` schema: ${liveEntities.length} entities`);
|
|
3439
|
+
const aggregateEntity = expectedEntities[0] ?? liveEntities[0];
|
|
3440
|
+
if (aggregateEntity) {
|
|
3441
|
+
const aggregate = await postJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(entry.tenantId)}/aggregate`, entry.dbKey, {
|
|
3442
|
+
ns: aggregateEntity,
|
|
3443
|
+
aggregate: { count: true }
|
|
3444
|
+
});
|
|
3445
|
+
const count = aggregate.aggregate?.count;
|
|
3446
|
+
out.log(` aggregate: ${aggregateEntity}.count=${String(count ?? "ok")}`);
|
|
3447
|
+
} else {
|
|
3448
|
+
out.log(` aggregate: skipped (schema has no entities)`);
|
|
3449
|
+
}
|
|
3405
3450
|
} else {
|
|
3406
|
-
out.log(`
|
|
3451
|
+
out.log(` db: skipped (not enabled)`);
|
|
3407
3452
|
}
|
|
3408
3453
|
const probes = database.integrations.flatMap(
|
|
3409
3454
|
(integration) => (integration.probes ?? []).map((probe) => ({ integration: integration.id, probe }))
|
|
@@ -3456,7 +3501,7 @@ function publicConfigUrl(platformUrl, appId, env) {
|
|
|
3456
3501
|
}
|
|
3457
3502
|
async function safeText3(res) {
|
|
3458
3503
|
try {
|
|
3459
|
-
return (await res.text()).slice(0, 500);
|
|
3504
|
+
return redactSecrets((await res.text()).slice(0, 500));
|
|
3460
3505
|
} catch {
|
|
3461
3506
|
return "";
|
|
3462
3507
|
}
|
|
@@ -3961,8 +4006,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
3961
4006
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
3962
4007
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
3963
4008
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
3964
|
-
const entries = inventory.flatMap((
|
|
3965
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
4009
|
+
const entries = inventory.flatMap((record6) => {
|
|
4010
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record6);
|
|
3966
4011
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
3967
4012
|
});
|
|
3968
4013
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -4210,8 +4255,8 @@ function normalize(value) {
|
|
|
4210
4255
|
if (Array.isArray(value)) return value.map(normalize);
|
|
4211
4256
|
if (value instanceof Uint8Array) return { $bytes: [...value] };
|
|
4212
4257
|
if (typeof value === "object") {
|
|
4213
|
-
const
|
|
4214
|
-
return Object.fromEntries(Object.keys(
|
|
4258
|
+
const record6 = value;
|
|
4259
|
+
return Object.fromEntries(Object.keys(record6).filter((key) => record6[key] !== void 0).sort().map((key) => [key, normalize(record6[key])]));
|
|
4215
4260
|
}
|
|
4216
4261
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
4217
4262
|
}
|
|
@@ -6456,8 +6501,8 @@ function cliVersion() {
|
|
|
6456
6501
|
const pkg = JSON.parse((0, import_node_fs11.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
|
|
6457
6502
|
return pkg.version ?? "unknown";
|
|
6458
6503
|
}
|
|
6459
|
-
function printHelp() {
|
|
6460
|
-
|
|
6504
|
+
function printHelp(output = console) {
|
|
6505
|
+
output.log(`odla-ai
|
|
6461
6506
|
|
|
6462
6507
|
Start here:
|
|
6463
6508
|
odla-ai runbook ask "<question>" The current procedure, from odla's own
|
|
@@ -6489,22 +6534,23 @@ Usage:
|
|
|
6489
6534
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
6490
6535
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
6491
6536
|
odla-ai pm <goal|task|decision|bug> list [--app <id>] [--status <s>] [--severity <s>] [--column <c>] [--q <text>] [--json]
|
|
6492
|
-
odla-ai pm <goal|task|decision|bug> add --app <id> --title <t> [--severity high|--column doing|--goal <id>|--description <text>|--body <text>|--proof <text>]
|
|
6537
|
+
odla-ai pm <goal|task|decision|bug> add --app <id> --title <t> [--severity high|--column doing|--goal <id>|--description <text>|--body <text>|--proof <text>] [--mutation-id <id>]
|
|
6493
6538
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
6494
|
-
odla-ai pm <goal|task|decision|bug> set <id> [--status <s>|--column <c>|--assignee <id>|--no-assignee|--goal <id>|--decision <id>]
|
|
6495
|
-
odla-ai pm <goal|task|decision|bug> done <id> [--decision <accepted-decision-id>]
|
|
6496
|
-
odla-ai pm <goal|task|decision|bug> comment <id> --body "..."
|
|
6539
|
+
odla-ai pm <goal|task|decision|bug> set <id> [--status <s>|--column <c>|--assignee <id>|--no-assignee|--goal <id>|--decision <id>] [--mutation-id <id>]
|
|
6540
|
+
odla-ai pm <goal|task|decision|bug> done <id> [--decision <accepted-decision-id>] [--mutation-id <id>]
|
|
6541
|
+
odla-ai pm <goal|task|decision|bug> comment <id> --body "..." [--mutation-id <id>]
|
|
6497
6542
|
odla-ai pm <goal|task|decision|bug> comments <id> [--json]
|
|
6498
6543
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
6499
6544
|
odla-ai pm handoff --app <id> [--json]
|
|
6500
6545
|
odla-ai discuss groups [--json]
|
|
6501
6546
|
odla-ai discuss list [--app <id>] [--q <text>] [--state open|resolved|all] [--json]
|
|
6502
6547
|
odla-ai discuss read <topic> [--json]
|
|
6503
|
-
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"]
|
|
6504
|
-
odla-ai discuss reply <topic> --body "..." [--markup "..."]
|
|
6505
|
-
odla-ai discuss resolve <topic> [--reopen]
|
|
6548
|
+
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"] [--mutation-id <id>]
|
|
6549
|
+
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
6550
|
+
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
6506
6551
|
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
6507
6552
|
odla-ai discuss watch [<topic>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json]
|
|
6553
|
+
odla-ai o11y status [--app <id>] [--env prod] [--minutes 60] [--json]
|
|
6508
6554
|
odla-ai whoami [--json]
|
|
6509
6555
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
6510
6556
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
@@ -6616,6 +6662,11 @@ Commands:
|
|
|
6616
6662
|
(not 1) when it times out with nothing new, so a script can tell
|
|
6617
6663
|
"no answer yet" from a failure and just rerun. Use "who" to look
|
|
6618
6664
|
up the exact @[Label](kind/id) markup for a mention.
|
|
6665
|
+
o11y Read one stable status envelope for application RED, current
|
|
6666
|
+
live-sync load/freshness, the protected commit-to-visible
|
|
6667
|
+
canary, collector ingest/scheduler trust, Cloudflare-owned
|
|
6668
|
+
runtime metrics, and a machine verdict.
|
|
6669
|
+
--json keeps auth progress on stderr for unattended agents.
|
|
6619
6670
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
6620
6671
|
smoke Verify credentials, public-config, composed schema, db aggregate, and integration probes.
|
|
6621
6672
|
skill Same installer; --agent accepts all, claude, codex, cursor,
|
|
@@ -7263,6 +7314,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
7263
7314
|
}
|
|
7264
7315
|
|
|
7265
7316
|
// src/discuss-actions.ts
|
|
7317
|
+
var writeMutationId = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
7266
7318
|
async function request(ctx, method, path, body) {
|
|
7267
7319
|
const res = await ctx.doFetch(`${ctx.platformUrl}/registry/discuss${path}`, {
|
|
7268
7320
|
method,
|
|
@@ -7359,7 +7411,8 @@ async function discussPost(ctx, parsed) {
|
|
|
7359
7411
|
const created = await request(ctx, "POST", "/topics", {
|
|
7360
7412
|
appId,
|
|
7361
7413
|
subject,
|
|
7362
|
-
...content(parsed)
|
|
7414
|
+
...content(parsed),
|
|
7415
|
+
mutationId: writeMutationId(parsed)
|
|
7363
7416
|
});
|
|
7364
7417
|
emit(ctx, created, () => ctx.out.log(`opened topic ${created.id}`));
|
|
7365
7418
|
}
|
|
@@ -7368,16 +7421,16 @@ async function discussReply(ctx, id, parsed) {
|
|
|
7368
7421
|
ctx,
|
|
7369
7422
|
"POST",
|
|
7370
7423
|
`/topics/${encodeURIComponent(id)}/replies`,
|
|
7371
|
-
content(parsed)
|
|
7424
|
+
{ ...content(parsed), mutationId: writeMutationId(parsed) }
|
|
7372
7425
|
);
|
|
7373
7426
|
emit(ctx, created, () => ctx.out.log(`replied ${created.id}`));
|
|
7374
7427
|
}
|
|
7375
|
-
async function discussResolve(ctx, id, resolved) {
|
|
7428
|
+
async function discussResolve(ctx, id, resolved, parsed) {
|
|
7376
7429
|
const result = await request(
|
|
7377
7430
|
ctx,
|
|
7378
7431
|
"PATCH",
|
|
7379
7432
|
`/topics/${encodeURIComponent(id)}`,
|
|
7380
|
-
{ resolved }
|
|
7433
|
+
{ resolved, mutationId: writeMutationId(parsed) }
|
|
7381
7434
|
);
|
|
7382
7435
|
emit(ctx, result, () => ctx.out.log(`${resolved ? "resolved" : "reopened"} ${result.id}`));
|
|
7383
7436
|
}
|
|
@@ -7490,7 +7543,8 @@ var ALLOWED = [
|
|
|
7490
7543
|
"by",
|
|
7491
7544
|
"self",
|
|
7492
7545
|
"interval",
|
|
7493
|
-
"timeout"
|
|
7546
|
+
"timeout",
|
|
7547
|
+
"mutation-id"
|
|
7494
7548
|
];
|
|
7495
7549
|
function requireId(id, action) {
|
|
7496
7550
|
if (!id) throw new Error(`"discuss ${action}" needs a topic id`);
|
|
@@ -7532,7 +7586,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
7532
7586
|
case "reply":
|
|
7533
7587
|
return discussReply(ctx, requireId(id, "reply"), parsed);
|
|
7534
7588
|
case "resolve":
|
|
7535
|
-
return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true);
|
|
7589
|
+
return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true, parsed);
|
|
7536
7590
|
case "who":
|
|
7537
7591
|
return discussWho(ctx, parsed);
|
|
7538
7592
|
case "watch": {
|
|
@@ -7546,6 +7600,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
7546
7600
|
}
|
|
7547
7601
|
|
|
7548
7602
|
// src/pm-actions.ts
|
|
7603
|
+
var writeMutationId2 = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
7549
7604
|
var FIELD_MAP = {
|
|
7550
7605
|
title: { key: "title" },
|
|
7551
7606
|
status: { key: "status" },
|
|
@@ -7647,25 +7702,35 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
7647
7702
|
if (!input.title) throw new Error("pm add needs --title <title>");
|
|
7648
7703
|
if (entity === "bug" && !input.description)
|
|
7649
7704
|
throw new Error("pm bug add needs --description <context> (or --body <context>)");
|
|
7650
|
-
const res = await pmRequest(ctx, "POST", `/${entity}`, {
|
|
7705
|
+
const res = await pmRequest(ctx, "POST", `/${entity}`, {
|
|
7706
|
+
appId,
|
|
7707
|
+
input,
|
|
7708
|
+
mutationId: writeMutationId2(parsed)
|
|
7709
|
+
});
|
|
7651
7710
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
7652
7711
|
}
|
|
7653
7712
|
async function pmGet(ctx, entity, id) {
|
|
7654
|
-
const { record:
|
|
7655
|
-
emit2(ctx,
|
|
7713
|
+
const { record: record6 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
7714
|
+
emit2(ctx, record6, () => printRecord(ctx, entity, record6));
|
|
7656
7715
|
}
|
|
7657
7716
|
async function pmSet(ctx, entity, id, parsed) {
|
|
7658
7717
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
7659
7718
|
if (Object.keys(patch2).length === 0)
|
|
7660
7719
|
throw new Error("pm set needs at least one field flag (e.g. --status doing, --assignee me, --no-assignee)");
|
|
7661
|
-
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
7720
|
+
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
7721
|
+
patch: patch2,
|
|
7722
|
+
mutationId: writeMutationId2(parsed)
|
|
7723
|
+
});
|
|
7662
7724
|
emit2(ctx, res, () => ctx.out.log(res.record ? `${res.record.id} [${statusCol(entity, res.record)}] ${res.record.appId} ${res.record.title ?? ""}` : `updated ${entity} ${id}`));
|
|
7663
7725
|
}
|
|
7664
7726
|
async function pmDone(ctx, entity, id, parsed) {
|
|
7665
7727
|
const decisionId = stringOpt(parsed.options.decision);
|
|
7666
7728
|
if (decisionId && entity !== "bug") throw new Error("--decision is only valid when completing a bug");
|
|
7667
7729
|
const patch2 = { ...DONE[entity], ...decisionId ? { decisionId } : {} };
|
|
7668
|
-
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
7730
|
+
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
7731
|
+
patch: patch2,
|
|
7732
|
+
mutationId: writeMutationId2(parsed)
|
|
7733
|
+
});
|
|
7669
7734
|
emit2(ctx, res, () => ctx.out.log(`${entity} ${id} \u2192 done`));
|
|
7670
7735
|
}
|
|
7671
7736
|
async function allRecords(ctx, entity, appId) {
|
|
@@ -7691,9 +7756,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
7691
7756
|
]);
|
|
7692
7757
|
const handoff = {
|
|
7693
7758
|
appId,
|
|
7694
|
-
unmetGoals: goals.filter((
|
|
7695
|
-
activeTasks: tasks.filter((
|
|
7696
|
-
openBugs: bugs.filter((
|
|
7759
|
+
unmetGoals: goals.filter((record6) => record6.status !== "met"),
|
|
7760
|
+
activeTasks: tasks.filter((record6) => record6.column !== "done"),
|
|
7761
|
+
openBugs: bugs.filter((record6) => record6.status !== "fixed" && record6.status !== "wontfix")
|
|
7697
7762
|
};
|
|
7698
7763
|
const result = {
|
|
7699
7764
|
...handoff,
|
|
@@ -7712,10 +7777,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
7712
7777
|
]) {
|
|
7713
7778
|
ctx.out.log(`${label}:`);
|
|
7714
7779
|
if (!records.length) ctx.out.log("- (none)");
|
|
7715
|
-
else for (const
|
|
7780
|
+
else for (const record6 of records) printRecord(
|
|
7716
7781
|
ctx,
|
|
7717
7782
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
7718
|
-
|
|
7783
|
+
record6
|
|
7719
7784
|
);
|
|
7720
7785
|
}
|
|
7721
7786
|
});
|
|
@@ -7723,7 +7788,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
7723
7788
|
async function pmComment(ctx, entity, id, parsed) {
|
|
7724
7789
|
const body = stringOpt(parsed.options.body);
|
|
7725
7790
|
if (!body) throw new Error('pm comment needs --body "..."');
|
|
7726
|
-
await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
|
|
7791
|
+
await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
|
|
7792
|
+
body,
|
|
7793
|
+
mutationId: writeMutationId2(parsed)
|
|
7794
|
+
});
|
|
7727
7795
|
ctx.out.log(`commented on ${entity} ${id}`);
|
|
7728
7796
|
}
|
|
7729
7797
|
async function pmComments(ctx, entity, id) {
|
|
@@ -7773,7 +7841,8 @@ var ALLOWED2 = [
|
|
|
7773
7841
|
"decision",
|
|
7774
7842
|
"limit",
|
|
7775
7843
|
"offset",
|
|
7776
|
-
"q"
|
|
7844
|
+
"q",
|
|
7845
|
+
"mutation-id"
|
|
7777
7846
|
];
|
|
7778
7847
|
function requireId2(id, action) {
|
|
7779
7848
|
if (!id) throw new Error(`"pm ... ${action}" needs an item id`);
|
|
@@ -7830,6 +7899,234 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
7830
7899
|
}
|
|
7831
7900
|
}
|
|
7832
7901
|
|
|
7902
|
+
// src/o11y-verdict.ts
|
|
7903
|
+
function statusVerdict(reads) {
|
|
7904
|
+
const reasons = [];
|
|
7905
|
+
sourceHttp(reasons, "application", reads.application);
|
|
7906
|
+
sourceHttp(reasons, "liveSync", reads.liveSync);
|
|
7907
|
+
sourceHttp(reasons, "canary", reads.canary);
|
|
7908
|
+
sourceHttp(reasons, "collector", reads.collector);
|
|
7909
|
+
sourceHttp(reasons, "provider", reads.provider);
|
|
7910
|
+
const liveStatus = String(reads.liveSync.body.status ?? "");
|
|
7911
|
+
if (liveStatus === "partial") {
|
|
7912
|
+
reasons.push({
|
|
7913
|
+
source: "liveSync",
|
|
7914
|
+
code: "partial_reconciliation",
|
|
7915
|
+
severity: "degraded"
|
|
7916
|
+
});
|
|
7917
|
+
} else if (liveStatus === "stale" || liveStatus === "unavailable") {
|
|
7918
|
+
reasons.push({
|
|
7919
|
+
source: "liveSync",
|
|
7920
|
+
code: `reconciliation_${liveStatus}`,
|
|
7921
|
+
severity: "degraded"
|
|
7922
|
+
});
|
|
7923
|
+
}
|
|
7924
|
+
const canaryStatus = String(reads.canary.body.status ?? "");
|
|
7925
|
+
if (canaryStatus === "failing") {
|
|
7926
|
+
reasons.push({
|
|
7927
|
+
source: "canary",
|
|
7928
|
+
code: String(reads.canary.body.errorCode ?? "journey_failed"),
|
|
7929
|
+
severity: "unhealthy"
|
|
7930
|
+
});
|
|
7931
|
+
} else if (canaryStatus === "stale") {
|
|
7932
|
+
reasons.push({
|
|
7933
|
+
source: "canary",
|
|
7934
|
+
code: "journey_stale",
|
|
7935
|
+
severity: "unhealthy"
|
|
7936
|
+
});
|
|
7937
|
+
} else if (canaryStatus === "pending" || canaryStatus === "not_configured") {
|
|
7938
|
+
reasons.push({
|
|
7939
|
+
source: "canary",
|
|
7940
|
+
code: `journey_${canaryStatus}`,
|
|
7941
|
+
severity: "degraded"
|
|
7942
|
+
});
|
|
7943
|
+
}
|
|
7944
|
+
const collectorStatus = String(reads.collector.body.status ?? "");
|
|
7945
|
+
if (collectorStatus === "unhealthy") {
|
|
7946
|
+
reasons.push({
|
|
7947
|
+
source: "collector",
|
|
7948
|
+
code: collectorReason(reads.collector, "collector_unhealthy"),
|
|
7949
|
+
severity: "unhealthy"
|
|
7950
|
+
});
|
|
7951
|
+
} else if (collectorStatus === "degraded") {
|
|
7952
|
+
reasons.push({
|
|
7953
|
+
source: "collector",
|
|
7954
|
+
code: collectorReason(reads.collector, "collector_degraded"),
|
|
7955
|
+
severity: "degraded"
|
|
7956
|
+
});
|
|
7957
|
+
}
|
|
7958
|
+
const providerStatus = String(reads.provider.body.status ?? "");
|
|
7959
|
+
if (providerStatus && providerStatus !== "observed") {
|
|
7960
|
+
reasons.push({
|
|
7961
|
+
source: "provider",
|
|
7962
|
+
code: `provider_${providerStatus}`,
|
|
7963
|
+
severity: "degraded"
|
|
7964
|
+
});
|
|
7965
|
+
}
|
|
7966
|
+
return {
|
|
7967
|
+
status: reasons.some((reason) => reason.severity === "unhealthy") ? "unhealthy" : reasons.length > 0 ? "degraded" : "healthy",
|
|
7968
|
+
reasons
|
|
7969
|
+
};
|
|
7970
|
+
}
|
|
7971
|
+
function collectorReason(read3, fallback) {
|
|
7972
|
+
const reasons = read3.body.reasons;
|
|
7973
|
+
if (!Array.isArray(reasons)) return fallback;
|
|
7974
|
+
const first = reasons.find(
|
|
7975
|
+
(reason) => Boolean(reason) && typeof reason === "object" && !Array.isArray(reason)
|
|
7976
|
+
);
|
|
7977
|
+
return typeof first?.code === "string" && first.code ? first.code : fallback;
|
|
7978
|
+
}
|
|
7979
|
+
function sourceHttp(reasons, source, read3) {
|
|
7980
|
+
if (read3.httpStatus >= 200 && read3.httpStatus < 300) return;
|
|
7981
|
+
reasons.push({
|
|
7982
|
+
source,
|
|
7983
|
+
code: `http_${read3.httpStatus}`,
|
|
7984
|
+
severity: read3.httpStatus >= 500 && read3.httpStatus !== 501 ? "unhealthy" : "degraded"
|
|
7985
|
+
});
|
|
7986
|
+
}
|
|
7987
|
+
|
|
7988
|
+
// src/o11y-command.ts
|
|
7989
|
+
async function o11yCommand(parsed, deps = {}) {
|
|
7990
|
+
assertArgs(
|
|
7991
|
+
parsed,
|
|
7992
|
+
["config", "token", "email", "json", "app", "env", "minutes"],
|
|
7993
|
+
2
|
|
7994
|
+
);
|
|
7995
|
+
const action = parsed.positionals[1];
|
|
7996
|
+
if (action !== "status") {
|
|
7997
|
+
throw new Error(
|
|
7998
|
+
`unknown o11y action "${action ?? ""}". Try "odla-ai o11y status --json".`
|
|
7999
|
+
);
|
|
8000
|
+
}
|
|
8001
|
+
const minutes = statusMinutes(
|
|
8002
|
+
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
8003
|
+
);
|
|
8004
|
+
const cfg = await loadProjectConfig(
|
|
8005
|
+
stringOpt(parsed.options.config) ?? "odla.config.mjs"
|
|
8006
|
+
);
|
|
8007
|
+
const doFetch = deps.fetch ?? fetch;
|
|
8008
|
+
const out = deps.stdout ?? console;
|
|
8009
|
+
const token = await getDeveloperToken(
|
|
8010
|
+
cfg,
|
|
8011
|
+
{
|
|
8012
|
+
configPath: cfg.configPath,
|
|
8013
|
+
token: stringOpt(parsed.options.token),
|
|
8014
|
+
email: stringOpt(parsed.options.email),
|
|
8015
|
+
open: false
|
|
8016
|
+
},
|
|
8017
|
+
doFetch,
|
|
8018
|
+
out
|
|
8019
|
+
);
|
|
8020
|
+
const appId = stringOpt(parsed.options.app)?.trim() || cfg.app.id;
|
|
8021
|
+
const env = stringOpt(parsed.options.env)?.trim() || "prod";
|
|
8022
|
+
const base = `${cfg.platformUrl}/o11y/${encodeURIComponent(appId)}`;
|
|
8023
|
+
const query = new URLSearchParams({ env, minutes: String(minutes) });
|
|
8024
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
8025
|
+
const [application, liveSync, canary, collector, provider] = await Promise.all([
|
|
8026
|
+
read2(`${base}/metrics/red?${query}`, headers, doFetch),
|
|
8027
|
+
read2(
|
|
8028
|
+
`${base}/live-sync/health?${new URLSearchParams({ env })}`,
|
|
8029
|
+
headers,
|
|
8030
|
+
doFetch
|
|
8031
|
+
),
|
|
8032
|
+
read2(
|
|
8033
|
+
`${base}/live-sync/canary?${new URLSearchParams({ env })}`,
|
|
8034
|
+
headers,
|
|
8035
|
+
doFetch
|
|
8036
|
+
),
|
|
8037
|
+
read2(`${base}/self-health?${query}`, headers, doFetch),
|
|
8038
|
+
read2(`${base}/provider/cloudflare?${query}`, headers, doFetch)
|
|
8039
|
+
]);
|
|
8040
|
+
const verdict = statusVerdict({
|
|
8041
|
+
application,
|
|
8042
|
+
liveSync,
|
|
8043
|
+
canary,
|
|
8044
|
+
collector,
|
|
8045
|
+
provider
|
|
8046
|
+
});
|
|
8047
|
+
const status = {
|
|
8048
|
+
schemaVersion: 3,
|
|
8049
|
+
scope: { appId, env, minutes },
|
|
8050
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8051
|
+
verdict,
|
|
8052
|
+
application,
|
|
8053
|
+
liveSync,
|
|
8054
|
+
canary,
|
|
8055
|
+
collector,
|
|
8056
|
+
provider
|
|
8057
|
+
};
|
|
8058
|
+
if (parsed.options.json === true) {
|
|
8059
|
+
out.log(JSON.stringify(status, null, 2));
|
|
8060
|
+
return;
|
|
8061
|
+
}
|
|
8062
|
+
printStatus2(status, out);
|
|
8063
|
+
}
|
|
8064
|
+
function statusMinutes(value) {
|
|
8065
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 7 * 24 * 60) {
|
|
8066
|
+
throw new Error("--minutes must be an integer from 1 to 10080");
|
|
8067
|
+
}
|
|
8068
|
+
return value;
|
|
8069
|
+
}
|
|
8070
|
+
async function read2(url, headers, doFetch) {
|
|
8071
|
+
const response2 = await doFetch(url, { headers });
|
|
8072
|
+
const text = await response2.text();
|
|
8073
|
+
let body = {};
|
|
8074
|
+
if (text) {
|
|
8075
|
+
try {
|
|
8076
|
+
const value = JSON.parse(text);
|
|
8077
|
+
body = value && typeof value === "object" && !Array.isArray(value) ? value : { value };
|
|
8078
|
+
} catch {
|
|
8079
|
+
body = { message: text.slice(0, 300) };
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
8082
|
+
return { httpStatus: response2.status, body };
|
|
8083
|
+
}
|
|
8084
|
+
function printStatus2(status, out) {
|
|
8085
|
+
out.log(
|
|
8086
|
+
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
8087
|
+
);
|
|
8088
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record5) : [];
|
|
8089
|
+
const requests = routes.reduce(
|
|
8090
|
+
(total, row) => total + numeric2(row.requests),
|
|
8091
|
+
0
|
|
8092
|
+
);
|
|
8093
|
+
const errors = routes.reduce(
|
|
8094
|
+
(total, row) => total + numeric2(row.errors),
|
|
8095
|
+
0
|
|
8096
|
+
);
|
|
8097
|
+
out.log(
|
|
8098
|
+
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
8099
|
+
);
|
|
8100
|
+
out.log(
|
|
8101
|
+
`live-sync ${status.liveSync.httpStatus} ${String(status.liveSync.body.status ?? status.liveSync.body.error ?? "unavailable")} ${numeric2(status.liveSync.body.activeConnections)} active`
|
|
8102
|
+
);
|
|
8103
|
+
const canaryDurations = record5(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
8104
|
+
out.log(
|
|
8105
|
+
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
8106
|
+
);
|
|
8107
|
+
const collectorIngest = record5(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
8108
|
+
const collectorStorage = record5(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
8109
|
+
out.log(
|
|
8110
|
+
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric2(collectorStorage.affectedPoints)} affected points`
|
|
8111
|
+
);
|
|
8112
|
+
const providerMetrics = record5(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
8113
|
+
out.log(
|
|
8114
|
+
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric2(providerMetrics.requests)} invocations ${numeric2(providerMetrics.errors)} runtime errors`
|
|
8115
|
+
);
|
|
8116
|
+
out.log(
|
|
8117
|
+
`verdict ${status.verdict.status} ${status.verdict.reasons.map((reason) => `${reason.source}:${reason.code}`).join(", ") || "all required signals healthy"}`
|
|
8118
|
+
);
|
|
8119
|
+
}
|
|
8120
|
+
function record5(value) {
|
|
8121
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8122
|
+
}
|
|
8123
|
+
function numeric2(value) {
|
|
8124
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8125
|
+
}
|
|
8126
|
+
function optionalNumeric(value) {
|
|
8127
|
+
return typeof value === "number" && Number.isFinite(value) ? `${value} ms` : "\u2014";
|
|
8128
|
+
}
|
|
8129
|
+
|
|
7833
8130
|
// src/provision.ts
|
|
7834
8131
|
var import_apps5 = require("@odla-ai/apps");
|
|
7835
8132
|
var import_ai3 = require("@odla-ai/ai");
|
|
@@ -8007,15 +8304,6 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
8007
8304
|
}
|
|
8008
8305
|
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
|
|
8009
8306
|
}
|
|
8010
|
-
async function readRegistryApp(cfg, token, doFetch) {
|
|
8011
|
-
const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
|
|
8012
|
-
headers: { authorization: `Bearer ${token}` }
|
|
8013
|
-
});
|
|
8014
|
-
if (res.status === 404) return null;
|
|
8015
|
-
if (!res.ok) return null;
|
|
8016
|
-
const json = await res.json();
|
|
8017
|
-
return json.app ?? null;
|
|
8018
|
-
}
|
|
8019
8307
|
async function postJson3(doFetch, url, bearer, body) {
|
|
8020
8308
|
const res = await doFetch(url, {
|
|
8021
8309
|
method: "POST",
|
|
@@ -8114,7 +8402,7 @@ async function provision(options) {
|
|
|
8114
8402
|
const doFetch = options.fetch ?? fetch;
|
|
8115
8403
|
const token = await getDeveloperToken(cfg, options, doFetch, out);
|
|
8116
8404
|
const apps = (0, import_apps5.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
|
|
8117
|
-
const existing = await
|
|
8405
|
+
const existing = await apps.resolveApp(cfg.app.id);
|
|
8118
8406
|
if (existing) {
|
|
8119
8407
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
8120
8408
|
} else {
|
|
@@ -8305,6 +8593,7 @@ var COMMAND_SURFACE = {
|
|
|
8305
8593
|
doctor: {},
|
|
8306
8594
|
help: {},
|
|
8307
8595
|
init: {},
|
|
8596
|
+
o11y: { status: {} },
|
|
8308
8597
|
pm: {
|
|
8309
8598
|
...PM_ENTITIES,
|
|
8310
8599
|
handoff: {}
|
|
@@ -8814,7 +9103,7 @@ var SOURCE2 = /\.(ts|tsx|js|jsx|mts|cts)$/;
|
|
|
8814
9103
|
function gitRunner(cwd) {
|
|
8815
9104
|
return (args) => (0, import_node_child_process7.execFileSync)("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
|
|
8816
9105
|
}
|
|
8817
|
-
function collectDiff(runGit, base,
|
|
9106
|
+
function collectDiff(runGit, base, read3) {
|
|
8818
9107
|
let merged = "";
|
|
8819
9108
|
try {
|
|
8820
9109
|
merged += runGit(["diff", "--unified=3", `${base}...HEAD`]);
|
|
@@ -8827,9 +9116,9 @@ function collectDiff(runGit, base, read2) {
|
|
|
8827
9116
|
merged += runGit(["diff", "--unified=3", "HEAD"]);
|
|
8828
9117
|
} catch {
|
|
8829
9118
|
}
|
|
8830
|
-
return merged + untrackedDiff(runGit,
|
|
9119
|
+
return merged + untrackedDiff(runGit, read3);
|
|
8831
9120
|
}
|
|
8832
|
-
function untrackedDiff(runGit,
|
|
9121
|
+
function untrackedDiff(runGit, read3) {
|
|
8833
9122
|
let listed = "";
|
|
8834
9123
|
try {
|
|
8835
9124
|
listed = runGit(["ls-files", "--others", "--exclude-standard"]);
|
|
@@ -8845,7 +9134,7 @@ function untrackedDiff(runGit, read2) {
|
|
|
8845
9134
|
if (!SOURCE2.test(path)) continue;
|
|
8846
9135
|
let body;
|
|
8847
9136
|
try {
|
|
8848
|
-
body =
|
|
9137
|
+
body = read3(path);
|
|
8849
9138
|
} catch {
|
|
8850
9139
|
continue;
|
|
8851
9140
|
}
|
|
@@ -8927,8 +9216,8 @@ function report3(ctx, impacts) {
|
|
|
8927
9216
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
8928
9217
|
const cwd = deps.cwd ?? process.cwd();
|
|
8929
9218
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
8930
|
-
const
|
|
8931
|
-
const surfaces = changedSurfaces(collectDiff(runGit, options.base,
|
|
9219
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs16.readFileSync)((0, import_node_path12.join)(cwd, path), "utf8"));
|
|
9220
|
+
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
8932
9221
|
if (!surfaces.length) {
|
|
8933
9222
|
return ctx.out.log(
|
|
8934
9223
|
ctx.json ? JSON.stringify({ base: options.base, impacts: [] }, null, 2) : `no changes against ${options.base}`
|
|
@@ -10062,33 +10351,37 @@ function exitCodeFor(err) {
|
|
|
10062
10351
|
return code === "handshake_pending" || code === "watch_timeout" ? 75 : 1;
|
|
10063
10352
|
}
|
|
10064
10353
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
10354
|
+
const runtime = {
|
|
10355
|
+
...dependencies,
|
|
10356
|
+
stdout: redactingOutput(dependencies.stdout ?? console)
|
|
10357
|
+
};
|
|
10065
10358
|
const parsed = parseArgv(argv);
|
|
10066
10359
|
recordInvocation(parsed);
|
|
10067
10360
|
const command = parsed.positionals[0] ?? "help";
|
|
10068
10361
|
if (command === "version" || command === "-v" || parsed.options.version === true) {
|
|
10069
10362
|
assertArgs(parsed, ["version"], 1);
|
|
10070
|
-
|
|
10363
|
+
runtime.stdout.log(cliVersion());
|
|
10071
10364
|
return;
|
|
10072
10365
|
}
|
|
10073
10366
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
10074
10367
|
assertArgs(parsed, ["help"], 1);
|
|
10075
|
-
printHelp();
|
|
10368
|
+
printHelp(runtime.stdout);
|
|
10076
10369
|
return;
|
|
10077
10370
|
}
|
|
10078
10371
|
if (command === "whoami") {
|
|
10079
|
-
await whoamiCommand(parsed,
|
|
10372
|
+
await whoamiCommand(parsed, runtime);
|
|
10080
10373
|
return;
|
|
10081
10374
|
}
|
|
10082
10375
|
if (command === "runbook") {
|
|
10083
|
-
await runbookCommand(parsed,
|
|
10376
|
+
await runbookCommand(parsed, runtime);
|
|
10084
10377
|
return;
|
|
10085
10378
|
}
|
|
10086
10379
|
if (command === "calendar") {
|
|
10087
|
-
await calendarCommand(parsed,
|
|
10380
|
+
await calendarCommand(parsed, runtime);
|
|
10088
10381
|
return;
|
|
10089
10382
|
}
|
|
10090
10383
|
if (command === "code") {
|
|
10091
|
-
await codeCommand(parsed,
|
|
10384
|
+
await codeCommand(parsed, runtime);
|
|
10092
10385
|
return;
|
|
10093
10386
|
}
|
|
10094
10387
|
if (command === "admin") {
|
|
@@ -10096,26 +10389,30 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
10096
10389
|
return;
|
|
10097
10390
|
}
|
|
10098
10391
|
if (command === "app") {
|
|
10099
|
-
await appCommand(parsed,
|
|
10392
|
+
await appCommand(parsed, runtime);
|
|
10100
10393
|
return;
|
|
10101
10394
|
}
|
|
10102
10395
|
if (command === "security") {
|
|
10103
|
-
await securityCommand(parsed,
|
|
10396
|
+
await securityCommand(parsed, runtime);
|
|
10104
10397
|
return;
|
|
10105
10398
|
}
|
|
10106
10399
|
if (command === "pm") {
|
|
10107
|
-
await pmCommand(parsed,
|
|
10400
|
+
await pmCommand(parsed, runtime);
|
|
10108
10401
|
return;
|
|
10109
10402
|
}
|
|
10110
10403
|
if (command === "discuss") {
|
|
10111
|
-
await discussCommand(parsed,
|
|
10404
|
+
await discussCommand(parsed, runtime);
|
|
10405
|
+
return;
|
|
10406
|
+
}
|
|
10407
|
+
if (command === "o11y") {
|
|
10408
|
+
await o11yCommand(parsed, runtime);
|
|
10112
10409
|
return;
|
|
10113
10410
|
}
|
|
10114
10411
|
if (command === "provision") {
|
|
10115
|
-
await provisionCommand(parsed,
|
|
10412
|
+
await provisionCommand(parsed, runtime);
|
|
10116
10413
|
return;
|
|
10117
10414
|
}
|
|
10118
|
-
if (await projectCommand(command, parsed,
|
|
10415
|
+
if (await projectCommand(command, parsed, runtime)) return;
|
|
10119
10416
|
throw new Error(`unknown command "${command}". Run "odla-ai help".`);
|
|
10120
10417
|
}
|
|
10121
10418
|
async function provisionCommand(parsed, dependencies) {
|