@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
|
@@ -1156,6 +1156,37 @@ function redactSecrets(value) {
|
|
|
1156
1156
|
for (const [pattern, replacement] of REPLACEMENTS) result = result.replace(pattern, replacement);
|
|
1157
1157
|
return result;
|
|
1158
1158
|
}
|
|
1159
|
+
function redactingOutput(output) {
|
|
1160
|
+
const redactArgs = (args) => args.map((value) => redactOutputValue(value, /* @__PURE__ */ new WeakMap()));
|
|
1161
|
+
return {
|
|
1162
|
+
log: (...args) => output.log(...redactArgs(args)),
|
|
1163
|
+
error: (...args) => output.error(...redactArgs(args))
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
function redactOutputValue(value, seen) {
|
|
1167
|
+
if (typeof value === "string") return redactSecrets(value);
|
|
1168
|
+
if (value instanceof Error) {
|
|
1169
|
+
const redacted = new Error(redactSecrets(value.message));
|
|
1170
|
+
redacted.name = value.name;
|
|
1171
|
+
if (value.stack) redacted.stack = redactSecrets(value.stack);
|
|
1172
|
+
return redacted;
|
|
1173
|
+
}
|
|
1174
|
+
if (!value || typeof value !== "object") return value;
|
|
1175
|
+
const prior = seen.get(value);
|
|
1176
|
+
if (prior) return prior;
|
|
1177
|
+
if (Array.isArray(value)) {
|
|
1178
|
+
const next2 = [];
|
|
1179
|
+
seen.set(value, next2);
|
|
1180
|
+
for (const item of value) next2.push(redactOutputValue(item, seen));
|
|
1181
|
+
return next2;
|
|
1182
|
+
}
|
|
1183
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1184
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
1185
|
+
const next = {};
|
|
1186
|
+
seen.set(value, next);
|
|
1187
|
+
for (const [key, item] of Object.entries(value)) next[key] = redactOutputValue(item, seen);
|
|
1188
|
+
return next;
|
|
1189
|
+
}
|
|
1159
1190
|
function looksSecret(value) {
|
|
1160
1191
|
return redactSecrets(value) !== value || value.includes("-----BEGIN");
|
|
1161
1192
|
}
|
|
@@ -1585,6 +1616,7 @@ var CAPABILITIES = {
|
|
|
1585
1616
|
"compose declared app-capability schema/rules, create guarded seeds when absent, and configure platform AI, auth, and deployment links",
|
|
1586
1617
|
"apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)",
|
|
1587
1618
|
"validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
|
|
1619
|
+
"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",
|
|
1588
1620
|
"run app-attributed hosted security discovery and independent validation without provider keys",
|
|
1589
1621
|
"connect/revoke source-read-only GitHub sources and drive commit-pinned hosted security jobs without PATs or provider keys",
|
|
1590
1622
|
"let admins manage system AI policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"
|
|
@@ -1605,7 +1637,7 @@ var CAPABILITIES = {
|
|
|
1605
1637
|
"approve GitHub App repository access separately from redacted-snippet disclosure"
|
|
1606
1638
|
],
|
|
1607
1639
|
studio: [
|
|
1608
|
-
"view telemetry and environment state",
|
|
1640
|
+
"view application telemetry, reconciled live-sync load/freshness, commit-to-visible canary health, provider-owned Worker runtime metrics, and environment state",
|
|
1609
1641
|
"let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them",
|
|
1610
1642
|
"review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens",
|
|
1611
1643
|
"perform manual credential recovery \u2014 for the primary owner or any co-owner \u2014 when the CLI's local shown-once copy is unavailable",
|
|
@@ -2578,9 +2610,17 @@ async function smoke(options) {
|
|
|
2578
2610
|
throw new Error(`local credentials are for app "${credentials.appId}", but config app is "${cfg.app.id}"`);
|
|
2579
2611
|
}
|
|
2580
2612
|
const entry = credentials.envs[env];
|
|
2581
|
-
if (!entry?.tenantId
|
|
2613
|
+
if (!entry?.tenantId) {
|
|
2614
|
+
throw new Error(`local credentials have no tenant for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
2615
|
+
}
|
|
2616
|
+
const hasDb = cfg.services.includes("db");
|
|
2617
|
+
const hasO11y = cfg.services.includes("o11y");
|
|
2618
|
+
if (hasDb && !entry.dbKey) {
|
|
2582
2619
|
throw new Error(`local credentials have no db key for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
2583
2620
|
}
|
|
2621
|
+
if (hasO11y && !entry.o11yToken) {
|
|
2622
|
+
throw new Error(`local credentials have no o11y token for env "${env}". Run "odla-ai provision --write-dev-vars".`);
|
|
2623
|
+
}
|
|
2584
2624
|
const doFetch = options.fetch ?? fetch;
|
|
2585
2625
|
out.log(`smoke: ${cfg.app.id}/${env}`);
|
|
2586
2626
|
out.log(` tenant: ${entry.tenantId}`);
|
|
@@ -2593,6 +2633,7 @@ async function smoke(options) {
|
|
|
2593
2633
|
}
|
|
2594
2634
|
out.log(` ai: ${provider}`);
|
|
2595
2635
|
}
|
|
2636
|
+
if (hasO11y) out.log(` o11y: credentials present`);
|
|
2596
2637
|
if (cfg.services.includes("calendar")) {
|
|
2597
2638
|
const token = await getDeveloperToken(
|
|
2598
2639
|
cfg,
|
|
@@ -2611,26 +2652,30 @@ async function smoke(options) {
|
|
|
2611
2652
|
out.log(` calendar: ${status.status}, bookable (booking \u2192 ${status.bookingCalendarId ?? "primary"})`);
|
|
2612
2653
|
}
|
|
2613
2654
|
const database = await resolveDatabaseConfig(cfg);
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2655
|
+
if (hasDb) {
|
|
2656
|
+
const expectedSchema = database.schema;
|
|
2657
|
+
const expectedEntities = serializedEntities(expectedSchema);
|
|
2658
|
+
const liveSchemaPayload = await getJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(entry.tenantId)}/schema`, entry.dbKey);
|
|
2659
|
+
const liveSchema = liveSchemaPayload.schema ?? liveSchemaPayload;
|
|
2660
|
+
const liveEntities = serializedEntities(liveSchema);
|
|
2661
|
+
if (expectedEntities.length) {
|
|
2662
|
+
const missing = expectedEntities.filter((entity) => !liveEntities.includes(entity));
|
|
2663
|
+
if (missing.length) throw new Error(`live schema is missing expected entities: ${missing.join(", ")}`);
|
|
2664
|
+
}
|
|
2665
|
+
out.log(` schema: ${liveEntities.length} entities`);
|
|
2666
|
+
const aggregateEntity = expectedEntities[0] ?? liveEntities[0];
|
|
2667
|
+
if (aggregateEntity) {
|
|
2668
|
+
const aggregate = await postJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(entry.tenantId)}/aggregate`, entry.dbKey, {
|
|
2669
|
+
ns: aggregateEntity,
|
|
2670
|
+
aggregate: { count: true }
|
|
2671
|
+
});
|
|
2672
|
+
const count = aggregate.aggregate?.count;
|
|
2673
|
+
out.log(` aggregate: ${aggregateEntity}.count=${String(count ?? "ok")}`);
|
|
2674
|
+
} else {
|
|
2675
|
+
out.log(` aggregate: skipped (schema has no entities)`);
|
|
2676
|
+
}
|
|
2632
2677
|
} else {
|
|
2633
|
-
out.log(`
|
|
2678
|
+
out.log(` db: skipped (not enabled)`);
|
|
2634
2679
|
}
|
|
2635
2680
|
const probes = database.integrations.flatMap(
|
|
2636
2681
|
(integration) => (integration.probes ?? []).map((probe) => ({ integration: integration.id, probe }))
|
|
@@ -2683,7 +2728,7 @@ function publicConfigUrl(platformUrl, appId, env) {
|
|
|
2683
2728
|
}
|
|
2684
2729
|
async function safeText3(res) {
|
|
2685
2730
|
try {
|
|
2686
|
-
return (await res.text()).slice(0, 500);
|
|
2731
|
+
return redactSecrets((await res.text()).slice(0, 500));
|
|
2687
2732
|
} catch {
|
|
2688
2733
|
return "";
|
|
2689
2734
|
}
|
|
@@ -3406,8 +3451,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
3406
3451
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
3407
3452
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
3408
3453
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
3409
|
-
const entries = inventory.flatMap((
|
|
3410
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
3454
|
+
const entries = inventory.flatMap((record6) => {
|
|
3455
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record6);
|
|
3411
3456
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
3412
3457
|
});
|
|
3413
3458
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -3655,8 +3700,8 @@ function normalize(value) {
|
|
|
3655
3700
|
if (Array.isArray(value)) return value.map(normalize);
|
|
3656
3701
|
if (value instanceof Uint8Array) return { $bytes: [...value] };
|
|
3657
3702
|
if (typeof value === "object") {
|
|
3658
|
-
const
|
|
3659
|
-
return Object.fromEntries(Object.keys(
|
|
3703
|
+
const record6 = value;
|
|
3704
|
+
return Object.fromEntries(Object.keys(record6).filter((key) => record6[key] !== void 0).sort().map((key) => [key, normalize(record6[key])]));
|
|
3660
3705
|
}
|
|
3661
3706
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
3662
3707
|
}
|
|
@@ -5901,8 +5946,8 @@ function cliVersion() {
|
|
|
5901
5946
|
const pkg = JSON.parse(readFileSync6(new URL("../package.json", import.meta.url), "utf8"));
|
|
5902
5947
|
return pkg.version ?? "unknown";
|
|
5903
5948
|
}
|
|
5904
|
-
function printHelp() {
|
|
5905
|
-
|
|
5949
|
+
function printHelp(output = console) {
|
|
5950
|
+
output.log(`odla-ai
|
|
5906
5951
|
|
|
5907
5952
|
Start here:
|
|
5908
5953
|
odla-ai runbook ask "<question>" The current procedure, from odla's own
|
|
@@ -5934,22 +5979,23 @@ Usage:
|
|
|
5934
5979
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
5935
5980
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
5936
5981
|
odla-ai pm <goal|task|decision|bug> list [--app <id>] [--status <s>] [--severity <s>] [--column <c>] [--q <text>] [--json]
|
|
5937
|
-
odla-ai pm <goal|task|decision|bug> add --app <id> --title <t> [--severity high|--column doing|--goal <id>|--description <text>|--body <text>|--proof <text>]
|
|
5982
|
+
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>]
|
|
5938
5983
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
5939
|
-
odla-ai pm <goal|task|decision|bug> set <id> [--status <s>|--column <c>|--assignee <id>|--no-assignee|--goal <id>|--decision <id>]
|
|
5940
|
-
odla-ai pm <goal|task|decision|bug> done <id> [--decision <accepted-decision-id>]
|
|
5941
|
-
odla-ai pm <goal|task|decision|bug> comment <id> --body "..."
|
|
5984
|
+
odla-ai pm <goal|task|decision|bug> set <id> [--status <s>|--column <c>|--assignee <id>|--no-assignee|--goal <id>|--decision <id>] [--mutation-id <id>]
|
|
5985
|
+
odla-ai pm <goal|task|decision|bug> done <id> [--decision <accepted-decision-id>] [--mutation-id <id>]
|
|
5986
|
+
odla-ai pm <goal|task|decision|bug> comment <id> --body "..." [--mutation-id <id>]
|
|
5942
5987
|
odla-ai pm <goal|task|decision|bug> comments <id> [--json]
|
|
5943
5988
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
5944
5989
|
odla-ai pm handoff --app <id> [--json]
|
|
5945
5990
|
odla-ai discuss groups [--json]
|
|
5946
5991
|
odla-ai discuss list [--app <id>] [--q <text>] [--state open|resolved|all] [--json]
|
|
5947
5992
|
odla-ai discuss read <topic> [--json]
|
|
5948
|
-
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"]
|
|
5949
|
-
odla-ai discuss reply <topic> --body "..." [--markup "..."]
|
|
5950
|
-
odla-ai discuss resolve <topic> [--reopen]
|
|
5993
|
+
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"] [--mutation-id <id>]
|
|
5994
|
+
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
5995
|
+
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
5951
5996
|
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
5952
5997
|
odla-ai discuss watch [<topic>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json]
|
|
5998
|
+
odla-ai o11y status [--app <id>] [--env prod] [--minutes 60] [--json]
|
|
5953
5999
|
odla-ai whoami [--json]
|
|
5954
6000
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
5955
6001
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
@@ -6061,6 +6107,11 @@ Commands:
|
|
|
6061
6107
|
(not 1) when it times out with nothing new, so a script can tell
|
|
6062
6108
|
"no answer yet" from a failure and just rerun. Use "who" to look
|
|
6063
6109
|
up the exact @[Label](kind/id) markup for a mention.
|
|
6110
|
+
o11y Read one stable status envelope for application RED, current
|
|
6111
|
+
live-sync load/freshness, the protected commit-to-visible
|
|
6112
|
+
canary, collector ingest/scheduler trust, Cloudflare-owned
|
|
6113
|
+
runtime metrics, and a machine verdict.
|
|
6114
|
+
--json keeps auth progress on stderr for unattended agents.
|
|
6064
6115
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
6065
6116
|
smoke Verify credentials, public-config, composed schema, db aggregate, and integration probes.
|
|
6066
6117
|
skill Same installer; --agent accepts all, claude, codex, cursor,
|
|
@@ -6521,15 +6572,6 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
6521
6572
|
}
|
|
6522
6573
|
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
|
|
6523
6574
|
}
|
|
6524
|
-
async function readRegistryApp(cfg, token, doFetch) {
|
|
6525
|
-
const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
|
|
6526
|
-
headers: { authorization: `Bearer ${token}` }
|
|
6527
|
-
});
|
|
6528
|
-
if (res.status === 404) return null;
|
|
6529
|
-
if (!res.ok) return null;
|
|
6530
|
-
const json = await res.json();
|
|
6531
|
-
return json.app ?? null;
|
|
6532
|
-
}
|
|
6533
6575
|
async function postJson3(doFetch, url, bearer, body) {
|
|
6534
6576
|
const res = await doFetch(url, {
|
|
6535
6577
|
method: "POST",
|
|
@@ -6628,7 +6670,7 @@ async function provision(options) {
|
|
|
6628
6670
|
const doFetch = options.fetch ?? fetch;
|
|
6629
6671
|
const token = await getDeveloperToken(cfg, options, doFetch, out);
|
|
6630
6672
|
const apps = createAppsClient({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
|
|
6631
|
-
const existing = await
|
|
6673
|
+
const existing = await apps.resolveApp(cfg.app.id);
|
|
6632
6674
|
if (existing) {
|
|
6633
6675
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
6634
6676
|
} else {
|
|
@@ -6815,6 +6857,7 @@ var COMMAND_SURFACE = {
|
|
|
6815
6857
|
doctor: {},
|
|
6816
6858
|
help: {},
|
|
6817
6859
|
init: {},
|
|
6860
|
+
o11y: { status: {} },
|
|
6818
6861
|
pm: {
|
|
6819
6862
|
...PM_ENTITIES,
|
|
6820
6863
|
handoff: {}
|
|
@@ -8009,6 +8052,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
8009
8052
|
}
|
|
8010
8053
|
|
|
8011
8054
|
// src/discuss-actions.ts
|
|
8055
|
+
var writeMutationId = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
8012
8056
|
async function request(ctx, method, path, body) {
|
|
8013
8057
|
const res = await ctx.doFetch(`${ctx.platformUrl}/registry/discuss${path}`, {
|
|
8014
8058
|
method,
|
|
@@ -8105,7 +8149,8 @@ async function discussPost(ctx, parsed) {
|
|
|
8105
8149
|
const created = await request(ctx, "POST", "/topics", {
|
|
8106
8150
|
appId,
|
|
8107
8151
|
subject,
|
|
8108
|
-
...content(parsed)
|
|
8152
|
+
...content(parsed),
|
|
8153
|
+
mutationId: writeMutationId(parsed)
|
|
8109
8154
|
});
|
|
8110
8155
|
emit(ctx, created, () => ctx.out.log(`opened topic ${created.id}`));
|
|
8111
8156
|
}
|
|
@@ -8114,16 +8159,16 @@ async function discussReply(ctx, id, parsed) {
|
|
|
8114
8159
|
ctx,
|
|
8115
8160
|
"POST",
|
|
8116
8161
|
`/topics/${encodeURIComponent(id)}/replies`,
|
|
8117
|
-
content(parsed)
|
|
8162
|
+
{ ...content(parsed), mutationId: writeMutationId(parsed) }
|
|
8118
8163
|
);
|
|
8119
8164
|
emit(ctx, created, () => ctx.out.log(`replied ${created.id}`));
|
|
8120
8165
|
}
|
|
8121
|
-
async function discussResolve(ctx, id, resolved) {
|
|
8166
|
+
async function discussResolve(ctx, id, resolved, parsed) {
|
|
8122
8167
|
const result = await request(
|
|
8123
8168
|
ctx,
|
|
8124
8169
|
"PATCH",
|
|
8125
8170
|
`/topics/${encodeURIComponent(id)}`,
|
|
8126
|
-
{ resolved }
|
|
8171
|
+
{ resolved, mutationId: writeMutationId(parsed) }
|
|
8127
8172
|
);
|
|
8128
8173
|
emit(ctx, result, () => ctx.out.log(`${resolved ? "resolved" : "reopened"} ${result.id}`));
|
|
8129
8174
|
}
|
|
@@ -8236,7 +8281,8 @@ var ALLOWED = [
|
|
|
8236
8281
|
"by",
|
|
8237
8282
|
"self",
|
|
8238
8283
|
"interval",
|
|
8239
|
-
"timeout"
|
|
8284
|
+
"timeout",
|
|
8285
|
+
"mutation-id"
|
|
8240
8286
|
];
|
|
8241
8287
|
function requireId(id, action) {
|
|
8242
8288
|
if (!id) throw new Error(`"discuss ${action}" needs a topic id`);
|
|
@@ -8278,7 +8324,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
8278
8324
|
case "reply":
|
|
8279
8325
|
return discussReply(ctx, requireId(id, "reply"), parsed);
|
|
8280
8326
|
case "resolve":
|
|
8281
|
-
return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true);
|
|
8327
|
+
return discussResolve(ctx, requireId(id, "resolve"), parsed.options.reopen !== true, parsed);
|
|
8282
8328
|
case "who":
|
|
8283
8329
|
return discussWho(ctx, parsed);
|
|
8284
8330
|
case "watch": {
|
|
@@ -8292,6 +8338,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
8292
8338
|
}
|
|
8293
8339
|
|
|
8294
8340
|
// src/pm-actions.ts
|
|
8341
|
+
var writeMutationId2 = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
8295
8342
|
var FIELD_MAP = {
|
|
8296
8343
|
title: { key: "title" },
|
|
8297
8344
|
status: { key: "status" },
|
|
@@ -8393,25 +8440,35 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
8393
8440
|
if (!input.title) throw new Error("pm add needs --title <title>");
|
|
8394
8441
|
if (entity === "bug" && !input.description)
|
|
8395
8442
|
throw new Error("pm bug add needs --description <context> (or --body <context>)");
|
|
8396
|
-
const res = await pmRequest(ctx, "POST", `/${entity}`, {
|
|
8443
|
+
const res = await pmRequest(ctx, "POST", `/${entity}`, {
|
|
8444
|
+
appId,
|
|
8445
|
+
input,
|
|
8446
|
+
mutationId: writeMutationId2(parsed)
|
|
8447
|
+
});
|
|
8397
8448
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
8398
8449
|
}
|
|
8399
8450
|
async function pmGet(ctx, entity, id) {
|
|
8400
|
-
const { record:
|
|
8401
|
-
emit2(ctx,
|
|
8451
|
+
const { record: record6 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
8452
|
+
emit2(ctx, record6, () => printRecord(ctx, entity, record6));
|
|
8402
8453
|
}
|
|
8403
8454
|
async function pmSet(ctx, entity, id, parsed) {
|
|
8404
8455
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
8405
8456
|
if (Object.keys(patch2).length === 0)
|
|
8406
8457
|
throw new Error("pm set needs at least one field flag (e.g. --status doing, --assignee me, --no-assignee)");
|
|
8407
|
-
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
8458
|
+
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
8459
|
+
patch: patch2,
|
|
8460
|
+
mutationId: writeMutationId2(parsed)
|
|
8461
|
+
});
|
|
8408
8462
|
emit2(ctx, res, () => ctx.out.log(res.record ? `${res.record.id} [${statusCol(entity, res.record)}] ${res.record.appId} ${res.record.title ?? ""}` : `updated ${entity} ${id}`));
|
|
8409
8463
|
}
|
|
8410
8464
|
async function pmDone(ctx, entity, id, parsed) {
|
|
8411
8465
|
const decisionId = stringOpt(parsed.options.decision);
|
|
8412
8466
|
if (decisionId && entity !== "bug") throw new Error("--decision is only valid when completing a bug");
|
|
8413
8467
|
const patch2 = { ...DONE[entity], ...decisionId ? { decisionId } : {} };
|
|
8414
|
-
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
8468
|
+
const res = await pmRequest(ctx, "PATCH", `/${entity}/${encodeURIComponent(id)}`, {
|
|
8469
|
+
patch: patch2,
|
|
8470
|
+
mutationId: writeMutationId2(parsed)
|
|
8471
|
+
});
|
|
8415
8472
|
emit2(ctx, res, () => ctx.out.log(`${entity} ${id} \u2192 done`));
|
|
8416
8473
|
}
|
|
8417
8474
|
async function allRecords(ctx, entity, appId) {
|
|
@@ -8437,9 +8494,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
8437
8494
|
]);
|
|
8438
8495
|
const handoff = {
|
|
8439
8496
|
appId,
|
|
8440
|
-
unmetGoals: goals.filter((
|
|
8441
|
-
activeTasks: tasks.filter((
|
|
8442
|
-
openBugs: bugs.filter((
|
|
8497
|
+
unmetGoals: goals.filter((record6) => record6.status !== "met"),
|
|
8498
|
+
activeTasks: tasks.filter((record6) => record6.column !== "done"),
|
|
8499
|
+
openBugs: bugs.filter((record6) => record6.status !== "fixed" && record6.status !== "wontfix")
|
|
8443
8500
|
};
|
|
8444
8501
|
const result = {
|
|
8445
8502
|
...handoff,
|
|
@@ -8458,10 +8515,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
8458
8515
|
]) {
|
|
8459
8516
|
ctx.out.log(`${label}:`);
|
|
8460
8517
|
if (!records.length) ctx.out.log("- (none)");
|
|
8461
|
-
else for (const
|
|
8518
|
+
else for (const record6 of records) printRecord(
|
|
8462
8519
|
ctx,
|
|
8463
8520
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
8464
|
-
|
|
8521
|
+
record6
|
|
8465
8522
|
);
|
|
8466
8523
|
}
|
|
8467
8524
|
});
|
|
@@ -8469,7 +8526,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
8469
8526
|
async function pmComment(ctx, entity, id, parsed) {
|
|
8470
8527
|
const body = stringOpt(parsed.options.body);
|
|
8471
8528
|
if (!body) throw new Error('pm comment needs --body "..."');
|
|
8472
|
-
await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
|
|
8529
|
+
await pmRequest(ctx, "POST", `/${entity}/${encodeURIComponent(id)}/comments`, {
|
|
8530
|
+
body,
|
|
8531
|
+
mutationId: writeMutationId2(parsed)
|
|
8532
|
+
});
|
|
8473
8533
|
ctx.out.log(`commented on ${entity} ${id}`);
|
|
8474
8534
|
}
|
|
8475
8535
|
async function pmComments(ctx, entity, id) {
|
|
@@ -8519,7 +8579,8 @@ var ALLOWED2 = [
|
|
|
8519
8579
|
"decision",
|
|
8520
8580
|
"limit",
|
|
8521
8581
|
"offset",
|
|
8522
|
-
"q"
|
|
8582
|
+
"q",
|
|
8583
|
+
"mutation-id"
|
|
8523
8584
|
];
|
|
8524
8585
|
function requireId2(id, action) {
|
|
8525
8586
|
if (!id) throw new Error(`"pm ... ${action}" needs an item id`);
|
|
@@ -8576,6 +8637,234 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
8576
8637
|
}
|
|
8577
8638
|
}
|
|
8578
8639
|
|
|
8640
|
+
// src/o11y-verdict.ts
|
|
8641
|
+
function statusVerdict(reads) {
|
|
8642
|
+
const reasons = [];
|
|
8643
|
+
sourceHttp(reasons, "application", reads.application);
|
|
8644
|
+
sourceHttp(reasons, "liveSync", reads.liveSync);
|
|
8645
|
+
sourceHttp(reasons, "canary", reads.canary);
|
|
8646
|
+
sourceHttp(reasons, "collector", reads.collector);
|
|
8647
|
+
sourceHttp(reasons, "provider", reads.provider);
|
|
8648
|
+
const liveStatus = String(reads.liveSync.body.status ?? "");
|
|
8649
|
+
if (liveStatus === "partial") {
|
|
8650
|
+
reasons.push({
|
|
8651
|
+
source: "liveSync",
|
|
8652
|
+
code: "partial_reconciliation",
|
|
8653
|
+
severity: "degraded"
|
|
8654
|
+
});
|
|
8655
|
+
} else if (liveStatus === "stale" || liveStatus === "unavailable") {
|
|
8656
|
+
reasons.push({
|
|
8657
|
+
source: "liveSync",
|
|
8658
|
+
code: `reconciliation_${liveStatus}`,
|
|
8659
|
+
severity: "degraded"
|
|
8660
|
+
});
|
|
8661
|
+
}
|
|
8662
|
+
const canaryStatus = String(reads.canary.body.status ?? "");
|
|
8663
|
+
if (canaryStatus === "failing") {
|
|
8664
|
+
reasons.push({
|
|
8665
|
+
source: "canary",
|
|
8666
|
+
code: String(reads.canary.body.errorCode ?? "journey_failed"),
|
|
8667
|
+
severity: "unhealthy"
|
|
8668
|
+
});
|
|
8669
|
+
} else if (canaryStatus === "stale") {
|
|
8670
|
+
reasons.push({
|
|
8671
|
+
source: "canary",
|
|
8672
|
+
code: "journey_stale",
|
|
8673
|
+
severity: "unhealthy"
|
|
8674
|
+
});
|
|
8675
|
+
} else if (canaryStatus === "pending" || canaryStatus === "not_configured") {
|
|
8676
|
+
reasons.push({
|
|
8677
|
+
source: "canary",
|
|
8678
|
+
code: `journey_${canaryStatus}`,
|
|
8679
|
+
severity: "degraded"
|
|
8680
|
+
});
|
|
8681
|
+
}
|
|
8682
|
+
const collectorStatus = String(reads.collector.body.status ?? "");
|
|
8683
|
+
if (collectorStatus === "unhealthy") {
|
|
8684
|
+
reasons.push({
|
|
8685
|
+
source: "collector",
|
|
8686
|
+
code: collectorReason(reads.collector, "collector_unhealthy"),
|
|
8687
|
+
severity: "unhealthy"
|
|
8688
|
+
});
|
|
8689
|
+
} else if (collectorStatus === "degraded") {
|
|
8690
|
+
reasons.push({
|
|
8691
|
+
source: "collector",
|
|
8692
|
+
code: collectorReason(reads.collector, "collector_degraded"),
|
|
8693
|
+
severity: "degraded"
|
|
8694
|
+
});
|
|
8695
|
+
}
|
|
8696
|
+
const providerStatus = String(reads.provider.body.status ?? "");
|
|
8697
|
+
if (providerStatus && providerStatus !== "observed") {
|
|
8698
|
+
reasons.push({
|
|
8699
|
+
source: "provider",
|
|
8700
|
+
code: `provider_${providerStatus}`,
|
|
8701
|
+
severity: "degraded"
|
|
8702
|
+
});
|
|
8703
|
+
}
|
|
8704
|
+
return {
|
|
8705
|
+
status: reasons.some((reason) => reason.severity === "unhealthy") ? "unhealthy" : reasons.length > 0 ? "degraded" : "healthy",
|
|
8706
|
+
reasons
|
|
8707
|
+
};
|
|
8708
|
+
}
|
|
8709
|
+
function collectorReason(read3, fallback) {
|
|
8710
|
+
const reasons = read3.body.reasons;
|
|
8711
|
+
if (!Array.isArray(reasons)) return fallback;
|
|
8712
|
+
const first = reasons.find(
|
|
8713
|
+
(reason) => Boolean(reason) && typeof reason === "object" && !Array.isArray(reason)
|
|
8714
|
+
);
|
|
8715
|
+
return typeof first?.code === "string" && first.code ? first.code : fallback;
|
|
8716
|
+
}
|
|
8717
|
+
function sourceHttp(reasons, source, read3) {
|
|
8718
|
+
if (read3.httpStatus >= 200 && read3.httpStatus < 300) return;
|
|
8719
|
+
reasons.push({
|
|
8720
|
+
source,
|
|
8721
|
+
code: `http_${read3.httpStatus}`,
|
|
8722
|
+
severity: read3.httpStatus >= 500 && read3.httpStatus !== 501 ? "unhealthy" : "degraded"
|
|
8723
|
+
});
|
|
8724
|
+
}
|
|
8725
|
+
|
|
8726
|
+
// src/o11y-command.ts
|
|
8727
|
+
async function o11yCommand(parsed, deps = {}) {
|
|
8728
|
+
assertArgs(
|
|
8729
|
+
parsed,
|
|
8730
|
+
["config", "token", "email", "json", "app", "env", "minutes"],
|
|
8731
|
+
2
|
|
8732
|
+
);
|
|
8733
|
+
const action = parsed.positionals[1];
|
|
8734
|
+
if (action !== "status") {
|
|
8735
|
+
throw new Error(
|
|
8736
|
+
`unknown o11y action "${action ?? ""}". Try "odla-ai o11y status --json".`
|
|
8737
|
+
);
|
|
8738
|
+
}
|
|
8739
|
+
const minutes = statusMinutes(
|
|
8740
|
+
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
8741
|
+
);
|
|
8742
|
+
const cfg = await loadProjectConfig(
|
|
8743
|
+
stringOpt(parsed.options.config) ?? "odla.config.mjs"
|
|
8744
|
+
);
|
|
8745
|
+
const doFetch = deps.fetch ?? fetch;
|
|
8746
|
+
const out = deps.stdout ?? console;
|
|
8747
|
+
const token = await getDeveloperToken(
|
|
8748
|
+
cfg,
|
|
8749
|
+
{
|
|
8750
|
+
configPath: cfg.configPath,
|
|
8751
|
+
token: stringOpt(parsed.options.token),
|
|
8752
|
+
email: stringOpt(parsed.options.email),
|
|
8753
|
+
open: false
|
|
8754
|
+
},
|
|
8755
|
+
doFetch,
|
|
8756
|
+
out
|
|
8757
|
+
);
|
|
8758
|
+
const appId = stringOpt(parsed.options.app)?.trim() || cfg.app.id;
|
|
8759
|
+
const env = stringOpt(parsed.options.env)?.trim() || "prod";
|
|
8760
|
+
const base = `${cfg.platformUrl}/o11y/${encodeURIComponent(appId)}`;
|
|
8761
|
+
const query = new URLSearchParams({ env, minutes: String(minutes) });
|
|
8762
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
8763
|
+
const [application, liveSync, canary, collector, provider] = await Promise.all([
|
|
8764
|
+
read2(`${base}/metrics/red?${query}`, headers, doFetch),
|
|
8765
|
+
read2(
|
|
8766
|
+
`${base}/live-sync/health?${new URLSearchParams({ env })}`,
|
|
8767
|
+
headers,
|
|
8768
|
+
doFetch
|
|
8769
|
+
),
|
|
8770
|
+
read2(
|
|
8771
|
+
`${base}/live-sync/canary?${new URLSearchParams({ env })}`,
|
|
8772
|
+
headers,
|
|
8773
|
+
doFetch
|
|
8774
|
+
),
|
|
8775
|
+
read2(`${base}/self-health?${query}`, headers, doFetch),
|
|
8776
|
+
read2(`${base}/provider/cloudflare?${query}`, headers, doFetch)
|
|
8777
|
+
]);
|
|
8778
|
+
const verdict = statusVerdict({
|
|
8779
|
+
application,
|
|
8780
|
+
liveSync,
|
|
8781
|
+
canary,
|
|
8782
|
+
collector,
|
|
8783
|
+
provider
|
|
8784
|
+
});
|
|
8785
|
+
const status = {
|
|
8786
|
+
schemaVersion: 3,
|
|
8787
|
+
scope: { appId, env, minutes },
|
|
8788
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8789
|
+
verdict,
|
|
8790
|
+
application,
|
|
8791
|
+
liveSync,
|
|
8792
|
+
canary,
|
|
8793
|
+
collector,
|
|
8794
|
+
provider
|
|
8795
|
+
};
|
|
8796
|
+
if (parsed.options.json === true) {
|
|
8797
|
+
out.log(JSON.stringify(status, null, 2));
|
|
8798
|
+
return;
|
|
8799
|
+
}
|
|
8800
|
+
printStatus2(status, out);
|
|
8801
|
+
}
|
|
8802
|
+
function statusMinutes(value) {
|
|
8803
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 7 * 24 * 60) {
|
|
8804
|
+
throw new Error("--minutes must be an integer from 1 to 10080");
|
|
8805
|
+
}
|
|
8806
|
+
return value;
|
|
8807
|
+
}
|
|
8808
|
+
async function read2(url, headers, doFetch) {
|
|
8809
|
+
const response2 = await doFetch(url, { headers });
|
|
8810
|
+
const text = await response2.text();
|
|
8811
|
+
let body = {};
|
|
8812
|
+
if (text) {
|
|
8813
|
+
try {
|
|
8814
|
+
const value = JSON.parse(text);
|
|
8815
|
+
body = value && typeof value === "object" && !Array.isArray(value) ? value : { value };
|
|
8816
|
+
} catch {
|
|
8817
|
+
body = { message: text.slice(0, 300) };
|
|
8818
|
+
}
|
|
8819
|
+
}
|
|
8820
|
+
return { httpStatus: response2.status, body };
|
|
8821
|
+
}
|
|
8822
|
+
function printStatus2(status, out) {
|
|
8823
|
+
out.log(
|
|
8824
|
+
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
8825
|
+
);
|
|
8826
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record5) : [];
|
|
8827
|
+
const requests = routes.reduce(
|
|
8828
|
+
(total, row) => total + numeric2(row.requests),
|
|
8829
|
+
0
|
|
8830
|
+
);
|
|
8831
|
+
const errors = routes.reduce(
|
|
8832
|
+
(total, row) => total + numeric2(row.errors),
|
|
8833
|
+
0
|
|
8834
|
+
);
|
|
8835
|
+
out.log(
|
|
8836
|
+
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
8837
|
+
);
|
|
8838
|
+
out.log(
|
|
8839
|
+
`live-sync ${status.liveSync.httpStatus} ${String(status.liveSync.body.status ?? status.liveSync.body.error ?? "unavailable")} ${numeric2(status.liveSync.body.activeConnections)} active`
|
|
8840
|
+
);
|
|
8841
|
+
const canaryDurations = record5(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
8842
|
+
out.log(
|
|
8843
|
+
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
8844
|
+
);
|
|
8845
|
+
const collectorIngest = record5(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
8846
|
+
const collectorStorage = record5(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
8847
|
+
out.log(
|
|
8848
|
+
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric2(collectorStorage.affectedPoints)} affected points`
|
|
8849
|
+
);
|
|
8850
|
+
const providerMetrics = record5(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
8851
|
+
out.log(
|
|
8852
|
+
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric2(providerMetrics.requests)} invocations ${numeric2(providerMetrics.errors)} runtime errors`
|
|
8853
|
+
);
|
|
8854
|
+
out.log(
|
|
8855
|
+
`verdict ${status.verdict.status} ${status.verdict.reasons.map((reason) => `${reason.source}:${reason.code}`).join(", ") || "all required signals healthy"}`
|
|
8856
|
+
);
|
|
8857
|
+
}
|
|
8858
|
+
function record5(value) {
|
|
8859
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8860
|
+
}
|
|
8861
|
+
function numeric2(value) {
|
|
8862
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8863
|
+
}
|
|
8864
|
+
function optionalNumeric(value) {
|
|
8865
|
+
return typeof value === "number" && Number.isFinite(value) ? `${value} ms` : "\u2014";
|
|
8866
|
+
}
|
|
8867
|
+
|
|
8579
8868
|
// src/record.ts
|
|
8580
8869
|
import { appendFileSync } from "fs";
|
|
8581
8870
|
import process9 from "process";
|
|
@@ -9002,7 +9291,7 @@ var SOURCE2 = /\.(ts|tsx|js|jsx|mts|cts)$/;
|
|
|
9002
9291
|
function gitRunner(cwd) {
|
|
9003
9292
|
return (args) => execFileSync2("git", args, { cwd, encoding: "utf8", maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
|
|
9004
9293
|
}
|
|
9005
|
-
function collectDiff(runGit, base,
|
|
9294
|
+
function collectDiff(runGit, base, read3) {
|
|
9006
9295
|
let merged = "";
|
|
9007
9296
|
try {
|
|
9008
9297
|
merged += runGit(["diff", "--unified=3", `${base}...HEAD`]);
|
|
@@ -9015,9 +9304,9 @@ function collectDiff(runGit, base, read2) {
|
|
|
9015
9304
|
merged += runGit(["diff", "--unified=3", "HEAD"]);
|
|
9016
9305
|
} catch {
|
|
9017
9306
|
}
|
|
9018
|
-
return merged + untrackedDiff(runGit,
|
|
9307
|
+
return merged + untrackedDiff(runGit, read3);
|
|
9019
9308
|
}
|
|
9020
|
-
function untrackedDiff(runGit,
|
|
9309
|
+
function untrackedDiff(runGit, read3) {
|
|
9021
9310
|
let listed = "";
|
|
9022
9311
|
try {
|
|
9023
9312
|
listed = runGit(["ls-files", "--others", "--exclude-standard"]);
|
|
@@ -9033,7 +9322,7 @@ function untrackedDiff(runGit, read2) {
|
|
|
9033
9322
|
if (!SOURCE2.test(path)) continue;
|
|
9034
9323
|
let body;
|
|
9035
9324
|
try {
|
|
9036
|
-
body =
|
|
9325
|
+
body = read3(path);
|
|
9037
9326
|
} catch {
|
|
9038
9327
|
continue;
|
|
9039
9328
|
}
|
|
@@ -9115,8 +9404,8 @@ function report3(ctx, impacts) {
|
|
|
9115
9404
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
9116
9405
|
const cwd = deps.cwd ?? process.cwd();
|
|
9117
9406
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
9118
|
-
const
|
|
9119
|
-
const surfaces = changedSurfaces(collectDiff(runGit, options.base,
|
|
9407
|
+
const read3 = deps.readRepoFile ?? ((path) => readFileSync10(join10(cwd, path), "utf8"));
|
|
9408
|
+
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
9120
9409
|
if (!surfaces.length) {
|
|
9121
9410
|
return ctx.out.log(
|
|
9122
9411
|
ctx.json ? JSON.stringify({ base: options.base, impacts: [] }, null, 2) : `no changes against ${options.base}`
|
|
@@ -9981,33 +10270,37 @@ function exitCodeFor(err) {
|
|
|
9981
10270
|
return code === "handshake_pending" || code === "watch_timeout" ? 75 : 1;
|
|
9982
10271
|
}
|
|
9983
10272
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
10273
|
+
const runtime = {
|
|
10274
|
+
...dependencies,
|
|
10275
|
+
stdout: redactingOutput(dependencies.stdout ?? console)
|
|
10276
|
+
};
|
|
9984
10277
|
const parsed = parseArgv(argv);
|
|
9985
10278
|
recordInvocation(parsed);
|
|
9986
10279
|
const command = parsed.positionals[0] ?? "help";
|
|
9987
10280
|
if (command === "version" || command === "-v" || parsed.options.version === true) {
|
|
9988
10281
|
assertArgs(parsed, ["version"], 1);
|
|
9989
|
-
|
|
10282
|
+
runtime.stdout.log(cliVersion());
|
|
9990
10283
|
return;
|
|
9991
10284
|
}
|
|
9992
10285
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
9993
10286
|
assertArgs(parsed, ["help"], 1);
|
|
9994
|
-
printHelp();
|
|
10287
|
+
printHelp(runtime.stdout);
|
|
9995
10288
|
return;
|
|
9996
10289
|
}
|
|
9997
10290
|
if (command === "whoami") {
|
|
9998
|
-
await whoamiCommand(parsed,
|
|
10291
|
+
await whoamiCommand(parsed, runtime);
|
|
9999
10292
|
return;
|
|
10000
10293
|
}
|
|
10001
10294
|
if (command === "runbook") {
|
|
10002
|
-
await runbookCommand(parsed,
|
|
10295
|
+
await runbookCommand(parsed, runtime);
|
|
10003
10296
|
return;
|
|
10004
10297
|
}
|
|
10005
10298
|
if (command === "calendar") {
|
|
10006
|
-
await calendarCommand(parsed,
|
|
10299
|
+
await calendarCommand(parsed, runtime);
|
|
10007
10300
|
return;
|
|
10008
10301
|
}
|
|
10009
10302
|
if (command === "code") {
|
|
10010
|
-
await codeCommand(parsed,
|
|
10303
|
+
await codeCommand(parsed, runtime);
|
|
10011
10304
|
return;
|
|
10012
10305
|
}
|
|
10013
10306
|
if (command === "admin") {
|
|
@@ -10015,26 +10308,30 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
10015
10308
|
return;
|
|
10016
10309
|
}
|
|
10017
10310
|
if (command === "app") {
|
|
10018
|
-
await appCommand(parsed,
|
|
10311
|
+
await appCommand(parsed, runtime);
|
|
10019
10312
|
return;
|
|
10020
10313
|
}
|
|
10021
10314
|
if (command === "security") {
|
|
10022
|
-
await securityCommand(parsed,
|
|
10315
|
+
await securityCommand(parsed, runtime);
|
|
10023
10316
|
return;
|
|
10024
10317
|
}
|
|
10025
10318
|
if (command === "pm") {
|
|
10026
|
-
await pmCommand(parsed,
|
|
10319
|
+
await pmCommand(parsed, runtime);
|
|
10027
10320
|
return;
|
|
10028
10321
|
}
|
|
10029
10322
|
if (command === "discuss") {
|
|
10030
|
-
await discussCommand(parsed,
|
|
10323
|
+
await discussCommand(parsed, runtime);
|
|
10324
|
+
return;
|
|
10325
|
+
}
|
|
10326
|
+
if (command === "o11y") {
|
|
10327
|
+
await o11yCommand(parsed, runtime);
|
|
10031
10328
|
return;
|
|
10032
10329
|
}
|
|
10033
10330
|
if (command === "provision") {
|
|
10034
|
-
await provisionCommand(parsed,
|
|
10331
|
+
await provisionCommand(parsed, runtime);
|
|
10035
10332
|
return;
|
|
10036
10333
|
}
|
|
10037
|
-
if (await projectCommand(command, parsed,
|
|
10334
|
+
if (await projectCommand(command, parsed, runtime)) return;
|
|
10038
10335
|
throw new Error(`unknown command "${command}". Run "odla-ai help".`);
|
|
10039
10336
|
}
|
|
10040
10337
|
async function provisionCommand(parsed, dependencies) {
|
|
@@ -10149,4 +10446,4 @@ export {
|
|
|
10149
10446
|
exitCodeFor,
|
|
10150
10447
|
runCli
|
|
10151
10448
|
};
|
|
10152
|
-
//# sourceMappingURL=chunk-
|
|
10449
|
+
//# sourceMappingURL=chunk-MWLZO2EZ.js.map
|