@odla-ai/cli 0.25.23 → 0.26.1
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 +32 -2
- package/dist/bin.cjs +485 -165
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-JJ42NV7X.js → chunk-ZAZBUURZ.js} +485 -165
- package/dist/chunk-ZAZBUURZ.js.map +1 -0
- package/dist/index.cjs +485 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -12
- package/dist/index.d.ts +14 -12
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/references/sdks.md +5 -3
- package/skills/odla-migrate/references/phase-2b-calendar.md +13 -8
- package/dist/chunk-JJ42NV7X.js.map +0 -1
|
@@ -115,10 +115,10 @@ function isManagedDevVar(line) {
|
|
|
115
115
|
const match = line.match(/^\s*(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/);
|
|
116
116
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
117
117
|
}
|
|
118
|
-
function writePrivateText(path,
|
|
118
|
+
function writePrivateText(path, text2) {
|
|
119
119
|
mkdirSync(dirname(path), { recursive: true });
|
|
120
120
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
121
|
-
writeFileSync(temporary,
|
|
121
|
+
writeFileSync(temporary, text2, { mode: 384 });
|
|
122
122
|
chmodSync(temporary, 384);
|
|
123
123
|
renameSync(temporary, path);
|
|
124
124
|
}
|
|
@@ -439,6 +439,7 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
439
439
|
}
|
|
440
440
|
var SCOPE_PURPOSE = {
|
|
441
441
|
"platform:status:read": "read the platform fleet health and deployment snapshot",
|
|
442
|
+
"platform:chat:credential:write": "rotate the built-in Discussion responder credential",
|
|
442
443
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
443
444
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
444
445
|
"platform:runbook:write": "add or edit odla's operational runbooks",
|
|
@@ -538,6 +539,14 @@ async function readSecretStream(kind, stream = process6.stdin) {
|
|
|
538
539
|
return value2;
|
|
539
540
|
}
|
|
540
541
|
|
|
542
|
+
// src/principal-presentation.ts
|
|
543
|
+
function unresolvedPrincipalLabel(credentialKind2, principalId) {
|
|
544
|
+
const kind = typeof credentialKind2 === "string" ? credentialKind2.trim() : "";
|
|
545
|
+
const id = typeof principalId === "string" ? principalId.trim() : "";
|
|
546
|
+
const audit = kind && id ? `${kind}:${id}` : kind || id;
|
|
547
|
+
return `Unknown principal${audit ? ` [${audit}]` : ""}`;
|
|
548
|
+
}
|
|
549
|
+
|
|
541
550
|
// src/admin-ai-audit.ts
|
|
542
551
|
function adminAiAuditQuery(filters) {
|
|
543
552
|
if (filters.limit === void 0) return "";
|
|
@@ -567,17 +576,17 @@ async function readAdminAiAudit(request2) {
|
|
|
567
576
|
String(event.changeKind ?? ""),
|
|
568
577
|
String(event.purpose ?? event.provider ?? ""),
|
|
569
578
|
route2,
|
|
570
|
-
|
|
579
|
+
unresolvedPrincipalLabel(event.actorType, event.actorId)
|
|
571
580
|
].join(" "));
|
|
572
581
|
}
|
|
573
582
|
}
|
|
574
583
|
async function responseBody(response2) {
|
|
575
|
-
const
|
|
576
|
-
if (!
|
|
584
|
+
const text2 = await response2.text();
|
|
585
|
+
if (!text2) return {};
|
|
577
586
|
try {
|
|
578
|
-
return JSON.parse(
|
|
587
|
+
return JSON.parse(text2);
|
|
579
588
|
} catch {
|
|
580
|
-
return { message:
|
|
589
|
+
return { message: text2.slice(0, 300) };
|
|
581
590
|
}
|
|
582
591
|
}
|
|
583
592
|
function apiError(status, body) {
|
|
@@ -644,7 +653,7 @@ when app/env run actor purpose/role route / policy tokens cost status`);
|
|
|
644
653
|
timestamp2(event.created_at),
|
|
645
654
|
`${String(event.app_id ?? "")}/${String(event.env ?? "")}`,
|
|
646
655
|
String(event.run_id ?? ""),
|
|
647
|
-
|
|
656
|
+
unresolvedPrincipalLabel(event.actor_type, event.actor_id),
|
|
648
657
|
`${String(event.purpose ?? "")}/${String(event.role ?? "")}`,
|
|
649
658
|
`${String(event.provider ?? "")}/${String(event.model ?? "")}@v${String(event.policy_version ?? "unknown")}`,
|
|
650
659
|
String(input + output),
|
|
@@ -665,12 +674,12 @@ function timestamp2(value2) {
|
|
|
665
674
|
return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
|
|
666
675
|
}
|
|
667
676
|
async function responseBody2(res) {
|
|
668
|
-
const
|
|
669
|
-
if (!
|
|
677
|
+
const text2 = await res.text();
|
|
678
|
+
if (!text2) return {};
|
|
670
679
|
try {
|
|
671
|
-
return JSON.parse(
|
|
680
|
+
return JSON.parse(text2);
|
|
672
681
|
} catch {
|
|
673
|
-
return { message:
|
|
682
|
+
return { message: text2.slice(0, 300) };
|
|
674
683
|
}
|
|
675
684
|
}
|
|
676
685
|
function apiError2(action2, status, body) {
|
|
@@ -846,12 +855,12 @@ function catalogModels(body) {
|
|
|
846
855
|
return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
|
|
847
856
|
}
|
|
848
857
|
async function responseBody3(res) {
|
|
849
|
-
const
|
|
850
|
-
if (!
|
|
858
|
+
const text2 = await res.text();
|
|
859
|
+
if (!text2) return {};
|
|
851
860
|
try {
|
|
852
|
-
return JSON.parse(
|
|
861
|
+
return JSON.parse(text2);
|
|
853
862
|
} catch {
|
|
854
|
-
return { message:
|
|
863
|
+
return { message: text2.slice(0, 300) };
|
|
855
864
|
}
|
|
856
865
|
}
|
|
857
866
|
function apiError3(action2, status, body) {
|
|
@@ -1828,9 +1837,9 @@ function canonicalValue(value2) {
|
|
|
1828
1837
|
}
|
|
1829
1838
|
if (Array.isArray(value2)) return value2.map(canonicalValue);
|
|
1830
1839
|
if (value2 && typeof value2 === "object") {
|
|
1831
|
-
const
|
|
1840
|
+
const record11 = value2;
|
|
1832
1841
|
return Object.fromEntries(
|
|
1833
|
-
Object.keys(
|
|
1842
|
+
Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, canonicalValue(record11[key])])
|
|
1834
1843
|
);
|
|
1835
1844
|
}
|
|
1836
1845
|
throw new TypeError("canonical JSON rejects unsupported values");
|
|
@@ -2414,7 +2423,7 @@ function compareOptionalEnvironmentState(env, desired, observed, desiredSource,
|
|
|
2414
2423
|
}
|
|
2415
2424
|
|
|
2416
2425
|
// src/config-reconcile-command.ts
|
|
2417
|
-
import { createAppsClient as createAppsClient2 } from "@odla-ai/apps";
|
|
2426
|
+
import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
|
|
2418
2427
|
import { join as join4 } from "path";
|
|
2419
2428
|
async function configDiff(options) {
|
|
2420
2429
|
const reconciliation = await inspectConfig(options);
|
|
@@ -2561,22 +2570,26 @@ function planNextActions(reconciliation, configPath) {
|
|
|
2561
2570
|
}
|
|
2562
2571
|
}
|
|
2563
2572
|
if (reconciliation.actions.some((action2) => action2.applySupport === "studio")) {
|
|
2564
|
-
const { appId, platformUrl } = reconciliation.scope;
|
|
2565
2573
|
next.push({
|
|
2566
2574
|
code: "review_destructive",
|
|
2567
|
-
command:
|
|
2575
|
+
command: studioSettingsUrl(reconciliation),
|
|
2568
2576
|
description: "Review service disablement in the owning Studio scope; this plan will not apply it."
|
|
2569
2577
|
});
|
|
2570
2578
|
}
|
|
2571
2579
|
if (!reconciliation.actions.length && reconciliation.summary.unmanaged) {
|
|
2572
2580
|
next.push({
|
|
2573
2581
|
code: "declare_or_accept_runtime_state",
|
|
2574
|
-
command:
|
|
2582
|
+
command: studioSettingsUrl(reconciliation),
|
|
2575
2583
|
description: "Declare the live value in project config or keep it as an explicit runtime-managed setting."
|
|
2576
2584
|
});
|
|
2577
2585
|
}
|
|
2578
2586
|
return next;
|
|
2579
2587
|
}
|
|
2588
|
+
function studioSettingsUrl(reconciliation) {
|
|
2589
|
+
const environment2 = reconciliation.actions.map((action2) => action2.env).concat(reconciliation.scope.environments).find((candidate) => candidate === "dev" || candidate === "prod") ?? "dev";
|
|
2590
|
+
const origin = reconciliation.scope.platformUrl.replace(/\/$/, "");
|
|
2591
|
+
return `${origin}${studioAppSettingsPath(reconciliation.scope.appId, environment2, "environment")}`;
|
|
2592
|
+
}
|
|
2580
2593
|
function quoteArg2(value2) {
|
|
2581
2594
|
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
2582
2595
|
}
|
|
@@ -2616,15 +2629,15 @@ function readWranglerConfig(path) {
|
|
|
2616
2629
|
return null;
|
|
2617
2630
|
}
|
|
2618
2631
|
}
|
|
2619
|
-
function stripJsonComments(
|
|
2632
|
+
function stripJsonComments(text2) {
|
|
2620
2633
|
let result = "";
|
|
2621
2634
|
let inString = false;
|
|
2622
|
-
for (let i = 0; i <
|
|
2623
|
-
const ch =
|
|
2635
|
+
for (let i = 0; i < text2.length; i++) {
|
|
2636
|
+
const ch = text2[i];
|
|
2624
2637
|
if (inString) {
|
|
2625
2638
|
result += ch;
|
|
2626
2639
|
if (ch === "\\") {
|
|
2627
|
-
result +=
|
|
2640
|
+
result += text2[i + 1] ?? "";
|
|
2628
2641
|
i++;
|
|
2629
2642
|
} else if (ch === '"') {
|
|
2630
2643
|
inString = false;
|
|
@@ -2636,14 +2649,14 @@ function stripJsonComments(text) {
|
|
|
2636
2649
|
result += ch;
|
|
2637
2650
|
continue;
|
|
2638
2651
|
}
|
|
2639
|
-
if (ch === "/" &&
|
|
2640
|
-
while (i <
|
|
2652
|
+
if (ch === "/" && text2[i + 1] === "/") {
|
|
2653
|
+
while (i < text2.length && text2[i] !== "\n") i++;
|
|
2641
2654
|
result += "\n";
|
|
2642
2655
|
continue;
|
|
2643
2656
|
}
|
|
2644
|
-
if (ch === "/" &&
|
|
2657
|
+
if (ch === "/" && text2[i + 1] === "*") {
|
|
2645
2658
|
i += 2;
|
|
2646
|
-
while (i <
|
|
2659
|
+
while (i < text2.length && !(text2[i] === "*" && text2[i + 1] === "/")) i++;
|
|
2647
2660
|
i++;
|
|
2648
2661
|
continue;
|
|
2649
2662
|
}
|
|
@@ -2660,7 +2673,14 @@ async function wranglerLoggedIn(run, cwd) {
|
|
|
2660
2673
|
}
|
|
2661
2674
|
}
|
|
2662
2675
|
function wranglerPutSecret(run, opts) {
|
|
2663
|
-
const args = [
|
|
2676
|
+
const args = [
|
|
2677
|
+
"wrangler",
|
|
2678
|
+
"secret",
|
|
2679
|
+
"put",
|
|
2680
|
+
opts.name,
|
|
2681
|
+
...opts.env ? ["--env", opts.env] : [],
|
|
2682
|
+
...opts.configPath ? ["--config", opts.configPath] : []
|
|
2683
|
+
];
|
|
2664
2684
|
return run("npx", args, { input: opts.value, cwd: opts.cwd });
|
|
2665
2685
|
}
|
|
2666
2686
|
|
|
@@ -3019,9 +3039,9 @@ function initProject(options) {
|
|
|
3019
3039
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
3020
3040
|
out.log("updated .gitignore for local odla credentials");
|
|
3021
3041
|
}
|
|
3022
|
-
function writeIfMissing(path,
|
|
3042
|
+
function writeIfMissing(path, text2) {
|
|
3023
3043
|
if (existsSync6(path)) return;
|
|
3024
|
-
writeFileSync2(path,
|
|
3044
|
+
writeFileSync2(path, text2);
|
|
3025
3045
|
}
|
|
3026
3046
|
function configTemplate(input) {
|
|
3027
3047
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -3229,13 +3249,13 @@ async function secretsSetClerkKey(options) {
|
|
|
3229
3249
|
body: JSON.stringify({ value: value2 })
|
|
3230
3250
|
});
|
|
3231
3251
|
if (!res.ok) {
|
|
3232
|
-
const
|
|
3233
|
-
throw new Error(`store Clerk secret key failed (${res.status}): ${
|
|
3252
|
+
const text2 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
|
|
3253
|
+
throw new Error(`store Clerk secret key failed (${res.status}): ${text2 || "request failed"}`);
|
|
3234
3254
|
}
|
|
3235
3255
|
out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
|
|
3236
3256
|
}
|
|
3237
|
-
function scrubValue(
|
|
3238
|
-
return redactSecrets(
|
|
3257
|
+
function scrubValue(text2, value2) {
|
|
3258
|
+
return redactSecrets(text2).split(value2).join("[value redacted]");
|
|
3239
3259
|
}
|
|
3240
3260
|
async function resolveVaultWrite(options) {
|
|
3241
3261
|
const out = options.stdout ?? console;
|
|
@@ -4218,8 +4238,8 @@ async function runContainerAttempt(options) {
|
|
|
4218
4238
|
let stopped = false;
|
|
4219
4239
|
let exited = false;
|
|
4220
4240
|
child.stderr.setEncoding("utf8");
|
|
4221
|
-
child.stderr.on("data", (
|
|
4222
|
-
if (stderr.length < 64 * 1024) stderr +=
|
|
4241
|
+
child.stderr.on("data", (text2) => {
|
|
4242
|
+
if (stderr.length < 64 * 1024) stderr += text2.slice(0, 64 * 1024 - stderr.length);
|
|
4223
4243
|
});
|
|
4224
4244
|
const stop = (reason) => {
|
|
4225
4245
|
if (stopped || exited) return;
|
|
@@ -4377,8 +4397,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
4377
4397
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
4378
4398
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
4379
4399
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
4380
|
-
const entries = inventory.flatMap((
|
|
4381
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
4400
|
+
const entries = inventory.flatMap((record11) => {
|
|
4401
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record11);
|
|
4382
4402
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
4383
4403
|
});
|
|
4384
4404
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -4626,8 +4646,8 @@ function normalize(value2) {
|
|
|
4626
4646
|
if (Array.isArray(value2)) return value2.map(normalize);
|
|
4627
4647
|
if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
|
|
4628
4648
|
if (typeof value2 === "object") {
|
|
4629
|
-
const
|
|
4630
|
-
return Object.fromEntries(Object.keys(
|
|
4649
|
+
const record11 = value2;
|
|
4650
|
+
return Object.fromEntries(Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, normalize(record11[key])]));
|
|
4631
4651
|
}
|
|
4632
4652
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
4633
4653
|
}
|
|
@@ -5051,8 +5071,8 @@ function boundedInteger(value2, spec) {
|
|
|
5051
5071
|
}
|
|
5052
5072
|
function boundedNumber(value2, spec) {
|
|
5053
5073
|
if (spec.kind !== "finite_number" || typeof value2 !== "number" || !Number.isFinite(value2) || value2 < spec.minimum || value2 > spec.maximum) throw new CamelError("conversion_rejected", "Finite-number conversion rejected the structured value.");
|
|
5054
|
-
const
|
|
5055
|
-
if (/e/i.test(
|
|
5074
|
+
const text2 = String(value2);
|
|
5075
|
+
if (/e/i.test(text2) || (text2.split(".")[1]?.length ?? 0) > spec.maximumDecimalPlaces) throw new CamelError("conversion_rejected", "Finite-number conversion rejected a non-canonical decimal.");
|
|
5056
5076
|
return value2;
|
|
5057
5077
|
}
|
|
5058
5078
|
function enumMember(value2, spec) {
|
|
@@ -5208,8 +5228,8 @@ function validateUnsafeSelector(path, value2, tool) {
|
|
|
5208
5228
|
return void 0;
|
|
5209
5229
|
}
|
|
5210
5230
|
function looksLikeDestination(value2) {
|
|
5211
|
-
const
|
|
5212
|
-
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(
|
|
5231
|
+
const text2 = value2.trim();
|
|
5232
|
+
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text2) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text2);
|
|
5213
5233
|
}
|
|
5214
5234
|
|
|
5215
5235
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
@@ -5578,8 +5598,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
5578
5598
|
});
|
|
5579
5599
|
let stderr = "";
|
|
5580
5600
|
child.stderr.setEncoding("utf8");
|
|
5581
|
-
child.stderr.on("data", (
|
|
5582
|
-
if (stderr.length < 4e3) stderr +=
|
|
5601
|
+
child.stderr.on("data", (text2) => {
|
|
5602
|
+
if (stderr.length < 4e3) stderr += text2.slice(0, 4e3);
|
|
5583
5603
|
});
|
|
5584
5604
|
child.once("error", reject);
|
|
5585
5605
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(`patch did not apply: ${stderr.trim().slice(0, 500)}`)));
|
|
@@ -6750,10 +6770,10 @@ var CodePiRuntimeEngine = class {
|
|
|
6750
6770
|
task: lease.task,
|
|
6751
6771
|
limits: this.options.limits,
|
|
6752
6772
|
signal: active.abort.signal,
|
|
6753
|
-
onStderr: (
|
|
6773
|
+
onStderr: (text2) => this.#event(command, {
|
|
6754
6774
|
type: "message",
|
|
6755
6775
|
actor: "system",
|
|
6756
|
-
body:
|
|
6776
|
+
body: text2.slice(0, 4e3)
|
|
6757
6777
|
}, active.conversationRefs),
|
|
6758
6778
|
onMessage: async (output) => {
|
|
6759
6779
|
if (output.type === "inference.request") {
|
|
@@ -7516,7 +7536,10 @@ var COMMAND_SURFACE = {
|
|
|
7516
7536
|
init: {},
|
|
7517
7537
|
o11y: { status: {} },
|
|
7518
7538
|
operations: { get: {}, wait: {} },
|
|
7519
|
-
platform: {
|
|
7539
|
+
platform: {
|
|
7540
|
+
status: {},
|
|
7541
|
+
"chat-credentials": { rotate: {} }
|
|
7542
|
+
},
|
|
7520
7543
|
pm: {
|
|
7521
7544
|
...PM_ENTITIES,
|
|
7522
7545
|
handoff: {}
|
|
@@ -8413,8 +8436,8 @@ async function appImport(options) {
|
|
|
8413
8436
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
8414
8437
|
const doFetch = options.fetch ?? fetch;
|
|
8415
8438
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
8416
|
-
const
|
|
8417
|
-
const { format, sources } = parseImport(
|
|
8439
|
+
const text2 = options.file === "-" ? (options.readStdin ?? (() => readFileSync9(0, "utf8")))() : readFileSync9(options.file, "utf8");
|
|
8440
|
+
const { format, sources } = parseImport(text2, options.ns);
|
|
8418
8441
|
if (format === "namespace-map" && options.ns) {
|
|
8419
8442
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
8420
8443
|
}
|
|
@@ -8505,7 +8528,10 @@ function report(options, owners, headline) {
|
|
|
8505
8528
|
if (headline) out.log(headline);
|
|
8506
8529
|
out.log(`owners (${owners.length}):`);
|
|
8507
8530
|
for (const o of owners) {
|
|
8508
|
-
|
|
8531
|
+
const name = o.email?.trim() || "Unnamed member";
|
|
8532
|
+
out.log(
|
|
8533
|
+
` ${o.primary ? "\u2605" : "\xB7"} ${name} [${o.ownerId}]${o.primary ? " (primary)" : ""}`
|
|
8534
|
+
);
|
|
8509
8535
|
}
|
|
8510
8536
|
}
|
|
8511
8537
|
async function ownersList(options) {
|
|
@@ -9251,7 +9277,8 @@ Usage:
|
|
|
9251
9277
|
odla-ai doctor [--config odla.config.mjs]
|
|
9252
9278
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
9253
9279
|
odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
|
|
9254
|
-
odla-ai operations
|
|
9280
|
+
odla-ai operations get <operation-id> [--json]
|
|
9281
|
+
odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
|
|
9255
9282
|
odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
|
|
9256
9283
|
odla-ai calendar calendars [--env dev] [--email <odla-account>] [--json]
|
|
9257
9284
|
odla-ai calendar connect [--env dev] [--email <odla-account>] [--no-open] [--yes]
|
|
@@ -9302,6 +9329,7 @@ Usage:
|
|
|
9302
9329
|
odla-ai context remove <name> --yes [--json]
|
|
9303
9330
|
odla-ai o11y status [--app <id>] [--context <name>] [--platform https://odla.ai] [--env prod] [--minutes 60] [--json]
|
|
9304
9331
|
odla-ai platform status [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
9332
|
+
odla-ai platform chat-credentials rotate [--context <name>] [--email <odla-account>] [--wrangler-config <path>] [--expected-version <id>] [--json] --yes
|
|
9305
9333
|
odla-ai whoami [--context <name>] [--platform https://odla.ai] [--json]
|
|
9306
9334
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
9307
9335
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
@@ -9365,9 +9393,9 @@ Commands:
|
|
|
9365
9393
|
does and what it guarantees is JSDoc, rendered per package at
|
|
9366
9394
|
https://odla.ai/docs and shipped in the installed .d.ts. Answering
|
|
9367
9395
|
a question usually needs both.
|
|
9368
|
-
whoami Report
|
|
9369
|
-
|
|
9370
|
-
|
|
9396
|
+
whoami Report the actual principal name, handle and kind, its manager,
|
|
9397
|
+
credential kind, accountable owner, and platform-admin status.
|
|
9398
|
+
A manager relationship never supplies authority by itself.
|
|
9371
9399
|
context Explain selected config, platform, app, environment, and
|
|
9372
9400
|
developer-token provenance without printing credentials or
|
|
9373
9401
|
starting a device handshake. Operator work can run outside a
|
|
@@ -9487,6 +9515,58 @@ Safety:
|
|
|
9487
9515
|
`);
|
|
9488
9516
|
}
|
|
9489
9517
|
|
|
9518
|
+
// src/discuss-principals.ts
|
|
9519
|
+
function mergeDiscussPrincipals(target, source) {
|
|
9520
|
+
Object.assign(target.authors, source.authors ?? {});
|
|
9521
|
+
Object.assign(target.principals, source.principals ?? {});
|
|
9522
|
+
}
|
|
9523
|
+
var withAt = (handle) => handle.startsWith("@") ? handle : `@${handle}`;
|
|
9524
|
+
function discussPrincipalLabel(principalId, authorKind, projection) {
|
|
9525
|
+
const profile = projection.principals?.[principalId];
|
|
9526
|
+
if (profile) {
|
|
9527
|
+
const identity = `${profile.displayName} (${withAt(profile.handle)})`;
|
|
9528
|
+
if (profile.kind === "agent") {
|
|
9529
|
+
return profile.managerDisplayName ? `${identity} \u2014 agent managed by ${profile.managerDisplayName}` : `${identity} \u2014 agent`;
|
|
9530
|
+
}
|
|
9531
|
+
return profile.kind === "service" ? `${identity} \u2014 service` : identity;
|
|
9532
|
+
}
|
|
9533
|
+
const legacyName = projection.authors?.[principalId]?.trim();
|
|
9534
|
+
if (legacyName) {
|
|
9535
|
+
return authorKind === "bot" ? `${legacyName} \u2014 agent` : legacyName;
|
|
9536
|
+
}
|
|
9537
|
+
return authorKind === "bot" ? "Unnamed agent" : "Unnamed member";
|
|
9538
|
+
}
|
|
9539
|
+
|
|
9540
|
+
// src/discuss-read-render.ts
|
|
9541
|
+
function discussBodyWithRefs(post) {
|
|
9542
|
+
if (!post.refs || post.refs.length === 0) return post.body;
|
|
9543
|
+
let out = "";
|
|
9544
|
+
let cursor = 0;
|
|
9545
|
+
for (const ref of [...post.refs].sort((a, b) => a.start - b.start)) {
|
|
9546
|
+
if (ref.start < cursor || ref.end > post.body.length) continue;
|
|
9547
|
+
out += post.body.slice(cursor, ref.start) + `@[${ref.label}](${ref.kind}/${ref.id})`;
|
|
9548
|
+
cursor = ref.end;
|
|
9549
|
+
}
|
|
9550
|
+
return out + post.body.slice(cursor);
|
|
9551
|
+
}
|
|
9552
|
+
function renderDiscussRead(ctx, topic, posts, projection) {
|
|
9553
|
+
const status = topic.resolved ? "resolved" : "open";
|
|
9554
|
+
ctx.out.log(`${topic.subject} [${status}] ${topic.appId ?? ""}`);
|
|
9555
|
+
for (const post of posts) {
|
|
9556
|
+
const who = discussPrincipalLabel(
|
|
9557
|
+
post.authorId,
|
|
9558
|
+
post.authorKind,
|
|
9559
|
+
projection
|
|
9560
|
+
);
|
|
9561
|
+
ctx.out.log(`
|
|
9562
|
+
\u2014 ${who}`);
|
|
9563
|
+
ctx.out.log(discussBodyWithRefs(post));
|
|
9564
|
+
for (const file of post.attachments ?? []) {
|
|
9565
|
+
ctx.out.log(` [attachment] ${file.name} (${file.size} bytes)`);
|
|
9566
|
+
}
|
|
9567
|
+
}
|
|
9568
|
+
}
|
|
9569
|
+
|
|
9490
9570
|
// src/discuss-actions.ts
|
|
9491
9571
|
var writeMutationId = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
9492
9572
|
async function request(ctx, method, path, body) {
|
|
@@ -9505,17 +9585,6 @@ function emit(ctx, value2, human) {
|
|
|
9505
9585
|
else human();
|
|
9506
9586
|
}
|
|
9507
9587
|
var state = (topic) => topic.resolved ? "resolved" : "open";
|
|
9508
|
-
function bodyWithRefs(post) {
|
|
9509
|
-
if (!post.refs || post.refs.length === 0) return post.body;
|
|
9510
|
-
let out = "";
|
|
9511
|
-
let cursor = 0;
|
|
9512
|
-
for (const ref of [...post.refs].sort((a, b) => a.start - b.start)) {
|
|
9513
|
-
if (ref.start < cursor || ref.end > post.body.length) continue;
|
|
9514
|
-
out += post.body.slice(cursor, ref.start) + `@[${ref.label}](${ref.kind}/${ref.id})`;
|
|
9515
|
-
cursor = ref.end;
|
|
9516
|
-
}
|
|
9517
|
-
return out + post.body.slice(cursor);
|
|
9518
|
-
}
|
|
9519
9588
|
function content(parsed) {
|
|
9520
9589
|
const markup = stringOpt(parsed.options.markup);
|
|
9521
9590
|
if (markup) return { markup };
|
|
@@ -9570,11 +9639,19 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9570
9639
|
offset: requestedOffset ?? "0"
|
|
9571
9640
|
});
|
|
9572
9641
|
const page = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
|
|
9573
|
-
emit(
|
|
9642
|
+
emit(
|
|
9643
|
+
ctx,
|
|
9644
|
+
page,
|
|
9645
|
+
() => renderDiscussRead(ctx, page.topic, page.posts, page)
|
|
9646
|
+
);
|
|
9574
9647
|
return;
|
|
9575
9648
|
}
|
|
9576
9649
|
for (let scan = 0; scan < 3; scan++) {
|
|
9577
9650
|
const posts = /* @__PURE__ */ new Map();
|
|
9651
|
+
const projection = {
|
|
9652
|
+
authors: {},
|
|
9653
|
+
principals: {}
|
|
9654
|
+
};
|
|
9578
9655
|
let topic = null;
|
|
9579
9656
|
let offset = 0;
|
|
9580
9657
|
for (; ; ) {
|
|
@@ -9585,6 +9662,7 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9585
9662
|
);
|
|
9586
9663
|
topic = page.topic;
|
|
9587
9664
|
for (const post of page.posts) posts.set(post.id, post);
|
|
9665
|
+
mergeDiscussPrincipals(projection, page);
|
|
9588
9666
|
if (posts.size > 1e4) throw new Error("discuss read failed: conversation exceeds 10000 posts");
|
|
9589
9667
|
if (!page.page?.hasMore) break;
|
|
9590
9668
|
if (page.page.nextOffset === null || page.page.nextOffset <= offset) {
|
|
@@ -9597,24 +9675,18 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9597
9675
|
);
|
|
9598
9676
|
const expected = Number.isInteger(topic.replyCount) ? topic.replyCount + 1 : ordered.length;
|
|
9599
9677
|
if (ordered.length === expected) {
|
|
9600
|
-
const result = { topic, posts: ordered };
|
|
9601
|
-
emit(
|
|
9678
|
+
const result = { topic, posts: ordered, ...projection };
|
|
9679
|
+
emit(
|
|
9680
|
+
ctx,
|
|
9681
|
+
result,
|
|
9682
|
+
() => renderDiscussRead(ctx, result.topic, result.posts, result)
|
|
9683
|
+
);
|
|
9602
9684
|
return;
|
|
9603
9685
|
}
|
|
9604
9686
|
if (offset === 0) throw new Error("discuss read failed: registry did not provide forward post pages");
|
|
9605
9687
|
}
|
|
9606
9688
|
throw new Error("discuss read failed: conversation changed during every complete-read attempt");
|
|
9607
9689
|
}
|
|
9608
|
-
function renderRead(ctx, topic, posts) {
|
|
9609
|
-
ctx.out.log(`${topic.subject} [${state(topic)}] ${topic.appId ?? ""}`);
|
|
9610
|
-
for (const post of posts) {
|
|
9611
|
-
const who = post.authorKind === "bot" ? `${post.authorId} (agent)` : post.authorId;
|
|
9612
|
-
ctx.out.log(`
|
|
9613
|
-
\u2014 ${who}`);
|
|
9614
|
-
ctx.out.log(bodyWithRefs(post));
|
|
9615
|
-
for (const file of post.attachments ?? []) ctx.out.log(` [attachment] ${file.name} (${file.size} bytes)`);
|
|
9616
|
-
}
|
|
9617
|
-
}
|
|
9618
9690
|
async function discussPost(ctx, parsed) {
|
|
9619
9691
|
const appId = stringOpt(parsed.options.app) ?? ctx.appId;
|
|
9620
9692
|
if (!appId) throw new Error("discuss post needs --app <appId>");
|
|
@@ -9839,6 +9911,8 @@ async function discussWatch(ctx, topicId, parsed) {
|
|
|
9839
9911
|
found: true,
|
|
9840
9912
|
cursor,
|
|
9841
9913
|
events: matching,
|
|
9914
|
+
...page.authors ? { authors: page.authors } : {},
|
|
9915
|
+
...page.principals ? { principals: page.principals } : {},
|
|
9842
9916
|
...posts && posts.length > 0 ? { posts } : {},
|
|
9843
9917
|
...topics && topics.length > 0 ? { topics } : {}
|
|
9844
9918
|
});
|
|
@@ -9871,7 +9945,11 @@ function report2(ctx, parsed, result) {
|
|
|
9871
9945
|
ctx.out.log(JSON.stringify(result, null, 2));
|
|
9872
9946
|
} else if (parsed.options.jsonl !== true && result.found) {
|
|
9873
9947
|
for (const post of result.posts ?? []) {
|
|
9874
|
-
const who =
|
|
9948
|
+
const who = discussPrincipalLabel(
|
|
9949
|
+
post.authorId,
|
|
9950
|
+
post.authorKind,
|
|
9951
|
+
result
|
|
9952
|
+
);
|
|
9875
9953
|
ctx.out.log(`\u2014 ${who}
|
|
9876
9954
|
${post.body}`);
|
|
9877
9955
|
}
|
|
@@ -10016,8 +10094,8 @@ function collectFields(parsed, allowClear) {
|
|
|
10016
10094
|
if (allowClear) out[spec.key] = null;
|
|
10017
10095
|
continue;
|
|
10018
10096
|
}
|
|
10019
|
-
const
|
|
10020
|
-
out[spec.key] = spec.num ? Number(
|
|
10097
|
+
const text2 = stringOpt(value2);
|
|
10098
|
+
out[spec.key] = spec.num ? Number(text2) : text2;
|
|
10021
10099
|
}
|
|
10022
10100
|
return out;
|
|
10023
10101
|
}
|
|
@@ -10084,8 +10162,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
10084
10162
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
10085
10163
|
}
|
|
10086
10164
|
async function pmGet(ctx, entity, id) {
|
|
10087
|
-
const { record:
|
|
10088
|
-
emit2(ctx,
|
|
10165
|
+
const { record: record11 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
10166
|
+
emit2(ctx, record11, () => printRecord(ctx, entity, record11));
|
|
10089
10167
|
}
|
|
10090
10168
|
async function pmSet(ctx, entity, id, parsed) {
|
|
10091
10169
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
@@ -10130,9 +10208,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
10130
10208
|
]);
|
|
10131
10209
|
const handoff = {
|
|
10132
10210
|
appId,
|
|
10133
|
-
unmetGoals: goals.filter((
|
|
10134
|
-
activeTasks: tasks.filter((
|
|
10135
|
-
openBugs: bugs.filter((
|
|
10211
|
+
unmetGoals: goals.filter((record11) => record11.status !== "met"),
|
|
10212
|
+
activeTasks: tasks.filter((record11) => record11.column !== "done"),
|
|
10213
|
+
openBugs: bugs.filter((record11) => record11.status !== "fixed" && record11.status !== "wontfix")
|
|
10136
10214
|
};
|
|
10137
10215
|
const result = {
|
|
10138
10216
|
...handoff,
|
|
@@ -10151,10 +10229,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
10151
10229
|
]) {
|
|
10152
10230
|
ctx.out.log(`${label}:`);
|
|
10153
10231
|
if (!records.length) ctx.out.log("- (none)");
|
|
10154
|
-
else for (const
|
|
10232
|
+
else for (const record11 of records) printRecord(
|
|
10155
10233
|
ctx,
|
|
10156
10234
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
10157
|
-
|
|
10235
|
+
record11
|
|
10158
10236
|
);
|
|
10159
10237
|
}
|
|
10160
10238
|
});
|
|
@@ -10345,19 +10423,180 @@ function age(input) {
|
|
|
10345
10423
|
return `${Math.round(input / 6e4)}m`;
|
|
10346
10424
|
}
|
|
10347
10425
|
|
|
10426
|
+
// src/platform-chat-credential-command.ts
|
|
10427
|
+
import process12 from "process";
|
|
10428
|
+
async function rotatePlatformChatCredential(parsed, deps) {
|
|
10429
|
+
assertArgs(
|
|
10430
|
+
parsed,
|
|
10431
|
+
[
|
|
10432
|
+
"config",
|
|
10433
|
+
"context",
|
|
10434
|
+
"platform",
|
|
10435
|
+
"token",
|
|
10436
|
+
"email",
|
|
10437
|
+
"open",
|
|
10438
|
+
"json",
|
|
10439
|
+
"yes",
|
|
10440
|
+
"wrangler-config",
|
|
10441
|
+
"expected-version"
|
|
10442
|
+
],
|
|
10443
|
+
3
|
|
10444
|
+
);
|
|
10445
|
+
if (parsed.options.yes !== true) {
|
|
10446
|
+
throw new Error(
|
|
10447
|
+
"platform chat-credentials rotate changes the production chat secret; pass --yes"
|
|
10448
|
+
);
|
|
10449
|
+
}
|
|
10450
|
+
const context = await resolveOperatorContext(parsed, {
|
|
10451
|
+
allowMissingConfig: true
|
|
10452
|
+
});
|
|
10453
|
+
const platform = context.platform.value;
|
|
10454
|
+
const doFetch = deps.fetch ?? fetch;
|
|
10455
|
+
const out = deps.stdout ?? console;
|
|
10456
|
+
const run = deps.runner ?? defaultRunner;
|
|
10457
|
+
const cwd = process12.cwd();
|
|
10458
|
+
const wranglerConfig = stringOpt(parsed.options["wrangler-config"]) ?? "packages/chat-agent/wrangler.jsonc";
|
|
10459
|
+
if (!await wranglerLoggedIn(run, cwd)) {
|
|
10460
|
+
throw new Error(
|
|
10461
|
+
'Wrangler is not authenticated; run "npx wrangler login" and retry'
|
|
10462
|
+
);
|
|
10463
|
+
}
|
|
10464
|
+
const priorHealth = await doFetch(`${platform}/health/services/chat`);
|
|
10465
|
+
const priorVersion = priorHealth.headers.get("x-odla-worker-version-id");
|
|
10466
|
+
await priorHealth.body?.cancel().catch(() => {
|
|
10467
|
+
});
|
|
10468
|
+
if (!priorVersion) {
|
|
10469
|
+
throw new Error(
|
|
10470
|
+
"chat health did not identify its current Worker version; refusing to rotate"
|
|
10471
|
+
);
|
|
10472
|
+
}
|
|
10473
|
+
const expectedVersion = stringOpt(parsed.options["expected-version"]);
|
|
10474
|
+
if (expectedVersion && priorVersion !== expectedVersion) {
|
|
10475
|
+
throw new Error(
|
|
10476
|
+
`chat health answered from version ${priorVersion}; expected ${expectedVersion}`
|
|
10477
|
+
);
|
|
10478
|
+
}
|
|
10479
|
+
const token = await resolveAdminPlatformToken({
|
|
10480
|
+
platform,
|
|
10481
|
+
scope: "platform:chat:credential:write",
|
|
10482
|
+
token: stringOpt(parsed.options.token),
|
|
10483
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
10484
|
+
email: stringOpt(parsed.options.email),
|
|
10485
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10486
|
+
fetch: doFetch,
|
|
10487
|
+
stdout: out,
|
|
10488
|
+
openApprovalUrl: deps.openUrl,
|
|
10489
|
+
label: "odla CLI (rotate built-in Discussion responder)"
|
|
10490
|
+
});
|
|
10491
|
+
const mintedResponse = await doFetch(
|
|
10492
|
+
`${platform}/registry/platform/chat-credentials/rotate`,
|
|
10493
|
+
{
|
|
10494
|
+
method: "POST",
|
|
10495
|
+
headers: { authorization: `Bearer ${token}` }
|
|
10496
|
+
}
|
|
10497
|
+
);
|
|
10498
|
+
const minted = await mintedResponse.json().catch(() => null);
|
|
10499
|
+
if (!mintedResponse.ok) {
|
|
10500
|
+
throw new Error(
|
|
10501
|
+
`mint Discussion credential failed (HTTP ${mintedResponse.status}): ${apiMessage(minted)}`
|
|
10502
|
+
);
|
|
10503
|
+
}
|
|
10504
|
+
if (!isPlatformChatCredential(minted)) {
|
|
10505
|
+
throw new Error(
|
|
10506
|
+
"platform returned an invalid odla.platform-chat-credential/v1 envelope"
|
|
10507
|
+
);
|
|
10508
|
+
}
|
|
10509
|
+
const put = await wranglerPutSecret(run, {
|
|
10510
|
+
name: "ODLA_BOT_TOKENS",
|
|
10511
|
+
value: JSON.stringify({
|
|
10512
|
+
[minted.appId]: { [minted.principalId]: minted.key }
|
|
10513
|
+
}),
|
|
10514
|
+
configPath: wranglerConfig,
|
|
10515
|
+
cwd
|
|
10516
|
+
});
|
|
10517
|
+
if (put.code !== 0) {
|
|
10518
|
+
throw new Error(
|
|
10519
|
+
"Wrangler could not replace ODLA_BOT_TOKENS; the new scoped key was not activated"
|
|
10520
|
+
);
|
|
10521
|
+
}
|
|
10522
|
+
const version = await waitForRotatedChatHealth({
|
|
10523
|
+
fetch: doFetch,
|
|
10524
|
+
platform,
|
|
10525
|
+
priorVersion,
|
|
10526
|
+
wait: deps.pollWait
|
|
10527
|
+
});
|
|
10528
|
+
const result = {
|
|
10529
|
+
schemaVersion: "odla.platform-chat-credential-rotation/v1",
|
|
10530
|
+
appId: minted.appId,
|
|
10531
|
+
appIncarnation: minted.appIncarnation,
|
|
10532
|
+
principalId: minted.principalId,
|
|
10533
|
+
health: { ok: true, service: "odla-chat-agent", version }
|
|
10534
|
+
};
|
|
10535
|
+
if (parsed.options.json === true) {
|
|
10536
|
+
out.log(JSON.stringify(result, null, 2));
|
|
10537
|
+
} else {
|
|
10538
|
+
out.log(
|
|
10539
|
+
`rotated ${result.appId} ${result.principalId} ${result.health.version}`
|
|
10540
|
+
);
|
|
10541
|
+
}
|
|
10542
|
+
}
|
|
10543
|
+
async function waitForRotatedChatHealth(opts) {
|
|
10544
|
+
const wait2 = opts.wait ?? ((milliseconds) => new Promise((resolve12) => setTimeout(resolve12, milliseconds)));
|
|
10545
|
+
let lastStatus = 0;
|
|
10546
|
+
let lastVersion = null;
|
|
10547
|
+
let lastError = "private_service_unready";
|
|
10548
|
+
for (let attempt = 0; attempt < 60; attempt++) {
|
|
10549
|
+
const response2 = await opts.fetch(
|
|
10550
|
+
`${opts.platform}/health/services/chat`
|
|
10551
|
+
);
|
|
10552
|
+
const body = await response2.json().catch(() => null);
|
|
10553
|
+
const version = response2.headers.get("x-odla-worker-version-id");
|
|
10554
|
+
if (response2.ok && record7(body) && body.ok === true && body.service === "odla-chat-agent" && version && version !== opts.priorVersion) {
|
|
10555
|
+
return version;
|
|
10556
|
+
}
|
|
10557
|
+
lastStatus = response2.status;
|
|
10558
|
+
lastVersion = version;
|
|
10559
|
+
lastError = record7(body) && typeof body.error === "string" ? body.error : "private_service_unready";
|
|
10560
|
+
if (attempt < 59) await wait2(5e3);
|
|
10561
|
+
}
|
|
10562
|
+
throw new Error(
|
|
10563
|
+
`chat health did not converge on the rotated Worker deployment (HTTP ${lastStatus}, version ${lastVersion ?? "unknown"}, ${lastError})`
|
|
10564
|
+
);
|
|
10565
|
+
}
|
|
10566
|
+
function isPlatformChatCredential(value2) {
|
|
10567
|
+
return record7(value2) && value2.schemaVersion === "odla.platform-chat-credential/v1" && value2.appId === "odla-pm" && typeof value2.appIncarnation === "string" && value2.appIncarnation.length > 0 && value2.principalId === "agent_odla" && typeof value2.key === "string" && value2.key.startsWith("odla_sk_");
|
|
10568
|
+
}
|
|
10569
|
+
function apiMessage(value2) {
|
|
10570
|
+
if (!record7(value2)) return "request failed";
|
|
10571
|
+
const error = record7(value2.error) ? value2.error : value2;
|
|
10572
|
+
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
10573
|
+
}
|
|
10574
|
+
function record7(value2) {
|
|
10575
|
+
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
10576
|
+
}
|
|
10577
|
+
|
|
10348
10578
|
// src/platform-command.ts
|
|
10349
10579
|
async function platformCommand(parsed, deps = {}) {
|
|
10580
|
+
const action2 = parsed.positionals[1];
|
|
10581
|
+
if (action2 === "status") {
|
|
10582
|
+
return platformStatus(parsed, deps);
|
|
10583
|
+
}
|
|
10584
|
+
if (action2 === "chat-credentials" && parsed.positionals[2] === "rotate") {
|
|
10585
|
+
return rotatePlatformChatCredential(parsed, deps);
|
|
10586
|
+
}
|
|
10587
|
+
throw new Error(
|
|
10588
|
+
`unknown platform action "${[
|
|
10589
|
+
action2,
|
|
10590
|
+
parsed.positionals[2]
|
|
10591
|
+
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
10592
|
+
);
|
|
10593
|
+
}
|
|
10594
|
+
async function platformStatus(parsed, deps) {
|
|
10350
10595
|
assertArgs(
|
|
10351
10596
|
parsed,
|
|
10352
10597
|
["config", "context", "platform", "token", "email", "open", "json"],
|
|
10353
10598
|
2
|
|
10354
10599
|
);
|
|
10355
|
-
const action2 = parsed.positionals[1];
|
|
10356
|
-
if (action2 !== "status") {
|
|
10357
|
-
throw new Error(
|
|
10358
|
-
`unknown platform action "${action2 ?? ""}". Try "odla-ai platform status --json".`
|
|
10359
|
-
);
|
|
10360
|
-
}
|
|
10361
10600
|
const context = await resolveOperatorContext(parsed, {
|
|
10362
10601
|
allowMissingConfig: true
|
|
10363
10602
|
});
|
|
@@ -10382,7 +10621,7 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
10382
10621
|
const body = await response2.json().catch(() => null);
|
|
10383
10622
|
if (!response2.ok) {
|
|
10384
10623
|
throw new Error(
|
|
10385
|
-
`read platform status failed (HTTP ${response2.status}): ${
|
|
10624
|
+
`read platform status failed (HTTP ${response2.status}): ${apiMessage2(body)}`
|
|
10386
10625
|
);
|
|
10387
10626
|
}
|
|
10388
10627
|
if (!isPlatformStatus(body)) {
|
|
@@ -10395,17 +10634,17 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
10395
10634
|
}
|
|
10396
10635
|
}
|
|
10397
10636
|
function isPlatformStatus(value2) {
|
|
10398
|
-
if (!
|
|
10399
|
-
if (!
|
|
10400
|
-
if (!
|
|
10637
|
+
if (!record8(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
|
|
10638
|
+
if (!record8(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
|
|
10639
|
+
if (!record8(value2.catalog) || !record8(value2.summary)) return false;
|
|
10401
10640
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
10402
10641
|
}
|
|
10403
|
-
function
|
|
10404
|
-
if (!
|
|
10405
|
-
const error =
|
|
10642
|
+
function apiMessage2(value2) {
|
|
10643
|
+
if (!record8(value2)) return "request failed";
|
|
10644
|
+
const error = record8(value2.error) ? value2.error : value2;
|
|
10406
10645
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
10407
10646
|
}
|
|
10408
|
-
function
|
|
10647
|
+
function record8(value2) {
|
|
10409
10648
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
10410
10649
|
}
|
|
10411
10650
|
|
|
@@ -10446,7 +10685,7 @@ function statusVerdict(reads) {
|
|
|
10446
10685
|
severity: "degraded"
|
|
10447
10686
|
});
|
|
10448
10687
|
}
|
|
10449
|
-
const performance =
|
|
10688
|
+
const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
10450
10689
|
if (performance?.status === "unavailable") {
|
|
10451
10690
|
reasons.push({
|
|
10452
10691
|
source: "liveSync",
|
|
@@ -10527,7 +10766,7 @@ function statusVerdict(reads) {
|
|
|
10527
10766
|
reasons
|
|
10528
10767
|
};
|
|
10529
10768
|
}
|
|
10530
|
-
function
|
|
10769
|
+
function record9(value2) {
|
|
10531
10770
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10532
10771
|
}
|
|
10533
10772
|
function numeric2(value2) {
|
|
@@ -10555,7 +10794,7 @@ function printO11yStatus(status, out) {
|
|
|
10555
10794
|
out.log(
|
|
10556
10795
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
10557
10796
|
);
|
|
10558
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
10797
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record10) : [];
|
|
10559
10798
|
const requests = routes.reduce(
|
|
10560
10799
|
(total, row) => total + numeric3(row.requests),
|
|
10561
10800
|
0
|
|
@@ -10567,39 +10806,39 @@ function printO11yStatus(status, out) {
|
|
|
10567
10806
|
out.log(
|
|
10568
10807
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
10569
10808
|
);
|
|
10570
|
-
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(
|
|
10809
|
+
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record10) : [];
|
|
10571
10810
|
out.log(
|
|
10572
10811
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
10573
10812
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
10574
10813
|
).join(", ") : "none observed"}`
|
|
10575
10814
|
);
|
|
10576
10815
|
out.log(liveSyncLine(status.liveSync));
|
|
10577
|
-
const canaryDurations =
|
|
10816
|
+
const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
10578
10817
|
out.log(
|
|
10579
10818
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
10580
10819
|
);
|
|
10581
|
-
const collectorIngest =
|
|
10582
|
-
const collectorStorage =
|
|
10820
|
+
const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
10821
|
+
const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
10583
10822
|
out.log(
|
|
10584
10823
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
10585
10824
|
);
|
|
10586
|
-
const providerMetrics =
|
|
10587
|
-
const providerCapacity =
|
|
10588
|
-
const workerMemory =
|
|
10825
|
+
const providerMetrics = record10(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
10826
|
+
const providerCapacity = record10(status.provider.body.capacity) ? status.provider.body.capacity : {};
|
|
10827
|
+
const workerMemory = record10(providerCapacity.memory) ? providerCapacity.memory : {};
|
|
10589
10828
|
out.log(
|
|
10590
10829
|
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric3(providerMetrics.requests)} invocations ${numeric3(providerMetrics.errors)} runtime errors ${optionalBytes(workerMemory.headroomBytes)} isolate memory headroom`
|
|
10591
10830
|
);
|
|
10592
10831
|
for (const line of providerCapacityLines(status.providerCapacity)) {
|
|
10593
10832
|
out.log(line);
|
|
10594
10833
|
}
|
|
10595
|
-
const coverage =
|
|
10596
|
-
const coverageCounts =
|
|
10597
|
-
const coverageBudget =
|
|
10834
|
+
const coverage = record10(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
|
|
10835
|
+
const coverageCounts = record10(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
|
|
10836
|
+
const coverageBudget = record10(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
|
|
10598
10837
|
out.log(
|
|
10599
10838
|
`request-coverage ${status.providerReconciliation.httpStatus} ${String(status.providerReconciliation.body.status ?? status.providerReconciliation.body.error ?? "unavailable")} ${optionalPercent(coverage.applicationCoverage)} application/provider ${numeric3(coverageCounts.applicationRequests)}/${numeric3(coverageCounts.providerRequests)} requests \xB1${optionalPercent(coverageBudget.maxRelativeError)} budget`
|
|
10600
10839
|
);
|
|
10601
10840
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
10602
|
-
const providerFreshness =
|
|
10841
|
+
const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
10603
10842
|
out.log(
|
|
10604
10843
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
10605
10844
|
);
|
|
@@ -10608,17 +10847,17 @@ function printO11yStatus(status, out) {
|
|
|
10608
10847
|
);
|
|
10609
10848
|
}
|
|
10610
10849
|
function providerCapacityLines(read3) {
|
|
10611
|
-
const resources =
|
|
10612
|
-
const durableObjects =
|
|
10613
|
-
const periodic =
|
|
10614
|
-
const storage =
|
|
10615
|
-
const d1 =
|
|
10616
|
-
const d1Activity =
|
|
10617
|
-
const d1Storage =
|
|
10618
|
-
const d1Latency =
|
|
10619
|
-
const r2 =
|
|
10620
|
-
const r2Operations =
|
|
10621
|
-
const r2Storage =
|
|
10850
|
+
const resources = record10(read3.body.resources) ? read3.body.resources : {};
|
|
10851
|
+
const durableObjects = record10(resources.durableObjects) ? resources.durableObjects : {};
|
|
10852
|
+
const periodic = record10(durableObjects.periodic) ? durableObjects.periodic : {};
|
|
10853
|
+
const storage = record10(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
|
|
10854
|
+
const d1 = record10(resources.d1) ? resources.d1 : {};
|
|
10855
|
+
const d1Activity = record10(d1.activity) ? d1.activity : {};
|
|
10856
|
+
const d1Storage = record10(d1.storage) ? d1.storage : {};
|
|
10857
|
+
const d1Latency = record10(d1Activity.latency) ? d1Activity.latency : {};
|
|
10858
|
+
const r2 = record10(resources.r2) ? resources.r2 : {};
|
|
10859
|
+
const r2Operations = record10(r2.operations) ? r2.operations : {};
|
|
10860
|
+
const r2Storage = record10(r2.storage) ? r2.storage : {};
|
|
10622
10861
|
const status = String(
|
|
10623
10862
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
10624
10863
|
);
|
|
@@ -10629,11 +10868,11 @@ function providerCapacityLines(read3) {
|
|
|
10629
10868
|
];
|
|
10630
10869
|
}
|
|
10631
10870
|
function liveSyncLine(read3) {
|
|
10632
|
-
const performance =
|
|
10633
|
-
const commitToSend =
|
|
10871
|
+
const performance = record10(read3.body.performance) ? read3.body.performance : {};
|
|
10872
|
+
const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
|
|
10634
10873
|
return `live-sync ${read3.httpStatus} ${String(read3.body.status ?? read3.body.error ?? "unavailable")} ${numeric3(read3.body.activeConnections)} active ${optionalNumeric(commitToSend.p95)} commit-to-send p95 ${numeric3(performance.sendFailures)} send failures`;
|
|
10635
10874
|
}
|
|
10636
|
-
function
|
|
10875
|
+
function record10(value2) {
|
|
10637
10876
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
10638
10877
|
}
|
|
10639
10878
|
function numeric3(value2) {
|
|
@@ -10802,14 +11041,14 @@ function statusMinutes(value2) {
|
|
|
10802
11041
|
}
|
|
10803
11042
|
async function read2(url, headers, doFetch) {
|
|
10804
11043
|
const response2 = await doFetch(url, { headers });
|
|
10805
|
-
const
|
|
11044
|
+
const text2 = await response2.text();
|
|
10806
11045
|
let body = {};
|
|
10807
|
-
if (
|
|
11046
|
+
if (text2) {
|
|
10808
11047
|
try {
|
|
10809
|
-
const value2 = JSON.parse(
|
|
11048
|
+
const value2 = JSON.parse(text2);
|
|
10810
11049
|
body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
|
|
10811
11050
|
} catch {
|
|
10812
|
-
body = { message:
|
|
11051
|
+
body = { message: text2.slice(0, 300) };
|
|
10813
11052
|
}
|
|
10814
11053
|
}
|
|
10815
11054
|
return { httpStatus: response2.status, body };
|
|
@@ -10817,9 +11056,9 @@ async function read2(url, headers, doFetch) {
|
|
|
10817
11056
|
|
|
10818
11057
|
// src/record.ts
|
|
10819
11058
|
import { appendFileSync } from "fs";
|
|
10820
|
-
import
|
|
11059
|
+
import process13 from "process";
|
|
10821
11060
|
function recordInvocation(parsed) {
|
|
10822
|
-
const file =
|
|
11061
|
+
const file = process13.env.ODLA_CLI_RECORD;
|
|
10823
11062
|
if (!file) return;
|
|
10824
11063
|
try {
|
|
10825
11064
|
const entry = {
|
|
@@ -10949,12 +11188,17 @@ async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
|
10949
11188
|
});
|
|
10950
11189
|
ctx.out.log(ctx.json ? JSON.stringify(created, null, 2) : `created ${slug} (${created.id})`);
|
|
10951
11190
|
}
|
|
10952
|
-
async function runbookEdit(ctx, slug, body, note, requires) {
|
|
11191
|
+
async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
10953
11192
|
const runbook = await bySlug(ctx, slug);
|
|
10954
11193
|
const result = await call(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
10955
11194
|
// An empty --requires clears the declaration; omitting the flag leaves
|
|
10956
11195
|
// whatever is there, so an ordinary body edit never drops it.
|
|
10957
|
-
patch: {
|
|
11196
|
+
patch: {
|
|
11197
|
+
body,
|
|
11198
|
+
expectedVersion: expectedVersion ?? runbook.version,
|
|
11199
|
+
...note ? { note } : {},
|
|
11200
|
+
...requires === void 0 ? {} : { requires: requires || null }
|
|
11201
|
+
}
|
|
10958
11202
|
});
|
|
10959
11203
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
10960
11204
|
ctx.out.log(`${slug} \u2192 v${result.record?.version ?? runbook.version + 1}`);
|
|
@@ -11008,12 +11252,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
11008
11252
|
// src/runbook-import.ts
|
|
11009
11253
|
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
11010
11254
|
import { basename as basename2, join as join13 } from "path";
|
|
11011
|
-
function parseRunbook(
|
|
11012
|
-
let rest =
|
|
11255
|
+
function parseRunbook(text2, slug) {
|
|
11256
|
+
let rest = text2;
|
|
11013
11257
|
const meta = {};
|
|
11014
|
-
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(
|
|
11258
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text2);
|
|
11015
11259
|
if (fm) {
|
|
11016
|
-
rest =
|
|
11260
|
+
rest = text2.slice(fm[0].length);
|
|
11017
11261
|
for (const line of fm[1].split(/\r?\n/)) {
|
|
11018
11262
|
const pair = /^(\w+)\s*:\s*(.+)$/.exec(line.trim());
|
|
11019
11263
|
if (!pair) continue;
|
|
@@ -11094,6 +11338,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
11094
11338
|
patch: {
|
|
11095
11339
|
title: r.title,
|
|
11096
11340
|
body: r.body,
|
|
11341
|
+
expectedVersion: found.version,
|
|
11097
11342
|
...r.summary ? { summary: r.summary } : {},
|
|
11098
11343
|
...r.tags ? { tags: r.tags } : {},
|
|
11099
11344
|
note: `imported from ${r.file}`
|
|
@@ -11485,9 +11730,9 @@ import { spawnSync } from "child_process";
|
|
|
11485
11730
|
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
11486
11731
|
import { tmpdir as tmpdir4 } from "os";
|
|
11487
11732
|
import { join as join15 } from "path";
|
|
11488
|
-
import
|
|
11733
|
+
import process14 from "process";
|
|
11489
11734
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11490
|
-
function resolveEditor(env =
|
|
11735
|
+
function resolveEditor(env = process14.env) {
|
|
11491
11736
|
for (const name of EDITOR_ENV) {
|
|
11492
11737
|
const value2 = env[name];
|
|
11493
11738
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11501,8 +11746,8 @@ function defaultRun(command, path) {
|
|
|
11501
11746
|
return result.status ?? 0;
|
|
11502
11747
|
}
|
|
11503
11748
|
function editText(initial, slug, deps = {}) {
|
|
11504
|
-
const env = deps.env ??
|
|
11505
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11749
|
+
const env = deps.env ?? process14.env;
|
|
11750
|
+
const interactive = deps.interactive ?? (() => Boolean(process14.stdin.isTTY));
|
|
11506
11751
|
const editor = resolveEditor(env);
|
|
11507
11752
|
if (!editor)
|
|
11508
11753
|
throw new Error(
|
|
@@ -11534,27 +11779,90 @@ async function editRunbook(ctx, slug, deps = {}) {
|
|
|
11534
11779
|
const found = page.records[0];
|
|
11535
11780
|
if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
|
|
11536
11781
|
ctx.out.log(`opening ${slug} v${found.version} in your editor\u2026`);
|
|
11537
|
-
|
|
11782
|
+
const body = await editText(found.body, slug, deps);
|
|
11783
|
+
return body === null ? null : { body, expectedVersion: found.version };
|
|
11538
11784
|
}
|
|
11539
11785
|
|
|
11540
11786
|
// src/whoami-command.ts
|
|
11787
|
+
var text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
11788
|
+
function principalKind(value2, machine) {
|
|
11789
|
+
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
11790
|
+
}
|
|
11791
|
+
function credentialKind(value2, machine, scopes) {
|
|
11792
|
+
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
11793
|
+
if (machine) return "machine";
|
|
11794
|
+
if (scopes.length) return "device";
|
|
11795
|
+
return "unknown";
|
|
11796
|
+
}
|
|
11797
|
+
function managerOf(value2) {
|
|
11798
|
+
if (!value2 || typeof value2 !== "object") return null;
|
|
11799
|
+
const row = value2;
|
|
11800
|
+
const principalId = text(row.principalId);
|
|
11801
|
+
if (!principalId) return null;
|
|
11802
|
+
return {
|
|
11803
|
+
principalId,
|
|
11804
|
+
displayName: text(row.displayName) ?? "Unnamed member",
|
|
11805
|
+
handle: text(row.handle) ?? ""
|
|
11806
|
+
};
|
|
11807
|
+
}
|
|
11808
|
+
function unnamedPrincipal(kind) {
|
|
11809
|
+
if (kind === "agent") return "Unnamed agent";
|
|
11810
|
+
if (kind === "service") return "Unnamed service";
|
|
11811
|
+
return "Unnamed member";
|
|
11812
|
+
}
|
|
11541
11813
|
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
11542
11814
|
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
11543
11815
|
headers: { authorization: `Bearer ${token}` }
|
|
11544
11816
|
});
|
|
11545
11817
|
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
11546
11818
|
const body = await res.json();
|
|
11819
|
+
const developerId = text(body.developerId) ?? "";
|
|
11820
|
+
const machine = body.machine === true;
|
|
11821
|
+
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
11822
|
+
const principalId = text(body.principalId) ?? developerId;
|
|
11823
|
+
const email = text(body.email);
|
|
11824
|
+
const kind = principalKind(body.principalKind, machine);
|
|
11825
|
+
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
11826
|
+
const handle = text(body.handle) ?? "";
|
|
11827
|
+
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
11547
11828
|
return {
|
|
11548
|
-
developerId
|
|
11549
|
-
|
|
11829
|
+
developerId,
|
|
11830
|
+
principalId,
|
|
11831
|
+
principalKind: kind,
|
|
11832
|
+
displayName,
|
|
11833
|
+
handle,
|
|
11834
|
+
manager: managerOf(body.manager),
|
|
11835
|
+
credential: {
|
|
11836
|
+
id: text(credential2.id),
|
|
11837
|
+
kind: credentialKind(credential2.kind, machine, scopes)
|
|
11838
|
+
},
|
|
11839
|
+
email,
|
|
11550
11840
|
admin: body.admin === true,
|
|
11551
|
-
machine
|
|
11552
|
-
scopes
|
|
11841
|
+
machine,
|
|
11842
|
+
scopes
|
|
11553
11843
|
};
|
|
11554
11844
|
}
|
|
11555
|
-
function
|
|
11556
|
-
if (identity.machine) return "machine (platform admin secret)";
|
|
11557
|
-
|
|
11845
|
+
function credentialLabel(identity) {
|
|
11846
|
+
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
11847
|
+
if (identity.credential.kind === "device")
|
|
11848
|
+
return identity.scopes.length ? "device (scoped)" : "device";
|
|
11849
|
+
if (identity.credential.kind === "clerk") return "clerk";
|
|
11850
|
+
return "unknown (legacy server)";
|
|
11851
|
+
}
|
|
11852
|
+
function namedPrincipal(identity) {
|
|
11853
|
+
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
11854
|
+
}
|
|
11855
|
+
function namedManager(manager) {
|
|
11856
|
+
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
11857
|
+
}
|
|
11858
|
+
function accountableOwner(identity) {
|
|
11859
|
+
if (identity.principalKind === "agent" && identity.manager) {
|
|
11860
|
+
return namedManager(identity.manager);
|
|
11861
|
+
}
|
|
11862
|
+
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
11863
|
+
return namedPrincipal(identity);
|
|
11864
|
+
}
|
|
11865
|
+
return identity.email ?? "Unnamed member";
|
|
11558
11866
|
}
|
|
11559
11867
|
async function whoamiCommand(parsed, deps = {}) {
|
|
11560
11868
|
assertArgs(
|
|
@@ -11585,9 +11893,19 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
11585
11893
|
return;
|
|
11586
11894
|
}
|
|
11587
11895
|
out.log(`platform: ${cfg.platformUrl}`);
|
|
11588
|
-
out.log(`
|
|
11896
|
+
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
11897
|
+
out.log(`principal id: ${identity.principalId}`);
|
|
11898
|
+
out.log(`kind: ${identity.principalKind}`);
|
|
11899
|
+
if (identity.principalKind === "agent")
|
|
11900
|
+
out.log(
|
|
11901
|
+
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
11902
|
+
);
|
|
11903
|
+
out.log(`owner: ${accountableOwner(identity)}`);
|
|
11904
|
+
out.log(`owner id: ${identity.developerId}`);
|
|
11589
11905
|
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
11590
|
-
out.log(`credential: ${
|
|
11906
|
+
out.log(`credential: ${credentialLabel(identity)}`);
|
|
11907
|
+
if (identity.credential.id)
|
|
11908
|
+
out.log(`credential id: ${identity.credential.id}`);
|
|
11591
11909
|
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
11592
11910
|
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
11593
11911
|
if (!identity.admin) {
|
|
@@ -11736,14 +12054,16 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
11736
12054
|
const name = requireSlug(slug, "edit");
|
|
11737
12055
|
const file = stringOpt(parsed.options.file);
|
|
11738
12056
|
const inline = stringOpt(parsed.options.body);
|
|
11739
|
-
const
|
|
11740
|
-
if (
|
|
12057
|
+
const edited = file === void 0 && inline === void 0 ? await editRunbook(ctx, name) : readBody(file, inline);
|
|
12058
|
+
if (edited === null) return ctx.out.log(`${name} unchanged; nothing written`);
|
|
12059
|
+
const body = typeof edited === "string" ? edited : edited.body;
|
|
11741
12060
|
return runbookEdit(
|
|
11742
12061
|
ctx,
|
|
11743
12062
|
name,
|
|
11744
12063
|
body,
|
|
11745
12064
|
stringOpt(parsed.options.note),
|
|
11746
|
-
parsed.options.requires === void 0 ? void 0 : stringOpt(parsed.options.requires) ?? ""
|
|
12065
|
+
parsed.options.requires === void 0 ? void 0 : stringOpt(parsed.options.requires) ?? "",
|
|
12066
|
+
typeof edited === "string" ? void 0 : edited.expectedVersion
|
|
11747
12067
|
);
|
|
11748
12068
|
}
|
|
11749
12069
|
case "import": {
|
|
@@ -12414,4 +12734,4 @@ export {
|
|
|
12414
12734
|
exitCodeFor,
|
|
12415
12735
|
runCli
|
|
12416
12736
|
};
|
|
12417
|
-
//# sourceMappingURL=chunk-
|
|
12737
|
+
//# sourceMappingURL=chunk-ZAZBUURZ.js.map
|