@odla-ai/cli 0.25.23 → 0.26.2
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 +13 -2
- package/dist/bin.cjs +270 -108
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-JJ42NV7X.js → chunk-MBZXWPZP.js} +272 -110
- package/dist/chunk-MBZXWPZP.js.map +1 -0
- package/dist/index.cjs +270 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -11
- package/dist/index.d.ts +13 -11
- 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
|
}
|
|
@@ -538,6 +538,14 @@ async function readSecretStream(kind, stream = process6.stdin) {
|
|
|
538
538
|
return value2;
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
// src/principal-presentation.ts
|
|
542
|
+
function unresolvedPrincipalLabel(credentialKind2, principalId) {
|
|
543
|
+
const kind = typeof credentialKind2 === "string" ? credentialKind2.trim() : "";
|
|
544
|
+
const id = typeof principalId === "string" ? principalId.trim() : "";
|
|
545
|
+
const audit = kind && id ? `${kind}:${id}` : kind || id;
|
|
546
|
+
return `Unknown principal${audit ? ` [${audit}]` : ""}`;
|
|
547
|
+
}
|
|
548
|
+
|
|
541
549
|
// src/admin-ai-audit.ts
|
|
542
550
|
function adminAiAuditQuery(filters) {
|
|
543
551
|
if (filters.limit === void 0) return "";
|
|
@@ -567,17 +575,17 @@ async function readAdminAiAudit(request2) {
|
|
|
567
575
|
String(event.changeKind ?? ""),
|
|
568
576
|
String(event.purpose ?? event.provider ?? ""),
|
|
569
577
|
route2,
|
|
570
|
-
|
|
578
|
+
unresolvedPrincipalLabel(event.actorType, event.actorId)
|
|
571
579
|
].join(" "));
|
|
572
580
|
}
|
|
573
581
|
}
|
|
574
582
|
async function responseBody(response2) {
|
|
575
|
-
const
|
|
576
|
-
if (!
|
|
583
|
+
const text2 = await response2.text();
|
|
584
|
+
if (!text2) return {};
|
|
577
585
|
try {
|
|
578
|
-
return JSON.parse(
|
|
586
|
+
return JSON.parse(text2);
|
|
579
587
|
} catch {
|
|
580
|
-
return { message:
|
|
588
|
+
return { message: text2.slice(0, 300) };
|
|
581
589
|
}
|
|
582
590
|
}
|
|
583
591
|
function apiError(status, body) {
|
|
@@ -644,7 +652,7 @@ when app/env run actor purpose/role route / policy tokens cost status`);
|
|
|
644
652
|
timestamp2(event.created_at),
|
|
645
653
|
`${String(event.app_id ?? "")}/${String(event.env ?? "")}`,
|
|
646
654
|
String(event.run_id ?? ""),
|
|
647
|
-
|
|
655
|
+
unresolvedPrincipalLabel(event.actor_type, event.actor_id),
|
|
648
656
|
`${String(event.purpose ?? "")}/${String(event.role ?? "")}`,
|
|
649
657
|
`${String(event.provider ?? "")}/${String(event.model ?? "")}@v${String(event.policy_version ?? "unknown")}`,
|
|
650
658
|
String(input + output),
|
|
@@ -665,12 +673,12 @@ function timestamp2(value2) {
|
|
|
665
673
|
return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
|
|
666
674
|
}
|
|
667
675
|
async function responseBody2(res) {
|
|
668
|
-
const
|
|
669
|
-
if (!
|
|
676
|
+
const text2 = await res.text();
|
|
677
|
+
if (!text2) return {};
|
|
670
678
|
try {
|
|
671
|
-
return JSON.parse(
|
|
679
|
+
return JSON.parse(text2);
|
|
672
680
|
} catch {
|
|
673
|
-
return { message:
|
|
681
|
+
return { message: text2.slice(0, 300) };
|
|
674
682
|
}
|
|
675
683
|
}
|
|
676
684
|
function apiError2(action2, status, body) {
|
|
@@ -846,12 +854,12 @@ function catalogModels(body) {
|
|
|
846
854
|
return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
|
|
847
855
|
}
|
|
848
856
|
async function responseBody3(res) {
|
|
849
|
-
const
|
|
850
|
-
if (!
|
|
857
|
+
const text2 = await res.text();
|
|
858
|
+
if (!text2) return {};
|
|
851
859
|
try {
|
|
852
|
-
return JSON.parse(
|
|
860
|
+
return JSON.parse(text2);
|
|
853
861
|
} catch {
|
|
854
|
-
return { message:
|
|
862
|
+
return { message: text2.slice(0, 300) };
|
|
855
863
|
}
|
|
856
864
|
}
|
|
857
865
|
function apiError3(action2, status, body) {
|
|
@@ -2414,7 +2422,7 @@ function compareOptionalEnvironmentState(env, desired, observed, desiredSource,
|
|
|
2414
2422
|
}
|
|
2415
2423
|
|
|
2416
2424
|
// src/config-reconcile-command.ts
|
|
2417
|
-
import { createAppsClient as createAppsClient2 } from "@odla-ai/apps";
|
|
2425
|
+
import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
|
|
2418
2426
|
import { join as join4 } from "path";
|
|
2419
2427
|
async function configDiff(options) {
|
|
2420
2428
|
const reconciliation = await inspectConfig(options);
|
|
@@ -2561,22 +2569,26 @@ function planNextActions(reconciliation, configPath) {
|
|
|
2561
2569
|
}
|
|
2562
2570
|
}
|
|
2563
2571
|
if (reconciliation.actions.some((action2) => action2.applySupport === "studio")) {
|
|
2564
|
-
const { appId, platformUrl } = reconciliation.scope;
|
|
2565
2572
|
next.push({
|
|
2566
2573
|
code: "review_destructive",
|
|
2567
|
-
command:
|
|
2574
|
+
command: studioSettingsUrl(reconciliation),
|
|
2568
2575
|
description: "Review service disablement in the owning Studio scope; this plan will not apply it."
|
|
2569
2576
|
});
|
|
2570
2577
|
}
|
|
2571
2578
|
if (!reconciliation.actions.length && reconciliation.summary.unmanaged) {
|
|
2572
2579
|
next.push({
|
|
2573
2580
|
code: "declare_or_accept_runtime_state",
|
|
2574
|
-
command:
|
|
2581
|
+
command: studioSettingsUrl(reconciliation),
|
|
2575
2582
|
description: "Declare the live value in project config or keep it as an explicit runtime-managed setting."
|
|
2576
2583
|
});
|
|
2577
2584
|
}
|
|
2578
2585
|
return next;
|
|
2579
2586
|
}
|
|
2587
|
+
function studioSettingsUrl(reconciliation) {
|
|
2588
|
+
const environment2 = reconciliation.actions.map((action2) => action2.env).concat(reconciliation.scope.environments).find((candidate) => candidate === "dev" || candidate === "prod") ?? "dev";
|
|
2589
|
+
const origin = reconciliation.scope.platformUrl.replace(/\/$/, "");
|
|
2590
|
+
return `${origin}${studioAppSettingsPath(reconciliation.scope.appId, environment2, "environment")}`;
|
|
2591
|
+
}
|
|
2580
2592
|
function quoteArg2(value2) {
|
|
2581
2593
|
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
2582
2594
|
}
|
|
@@ -2616,15 +2628,15 @@ function readWranglerConfig(path) {
|
|
|
2616
2628
|
return null;
|
|
2617
2629
|
}
|
|
2618
2630
|
}
|
|
2619
|
-
function stripJsonComments(
|
|
2631
|
+
function stripJsonComments(text2) {
|
|
2620
2632
|
let result = "";
|
|
2621
2633
|
let inString = false;
|
|
2622
|
-
for (let i = 0; i <
|
|
2623
|
-
const ch =
|
|
2634
|
+
for (let i = 0; i < text2.length; i++) {
|
|
2635
|
+
const ch = text2[i];
|
|
2624
2636
|
if (inString) {
|
|
2625
2637
|
result += ch;
|
|
2626
2638
|
if (ch === "\\") {
|
|
2627
|
-
result +=
|
|
2639
|
+
result += text2[i + 1] ?? "";
|
|
2628
2640
|
i++;
|
|
2629
2641
|
} else if (ch === '"') {
|
|
2630
2642
|
inString = false;
|
|
@@ -2636,14 +2648,14 @@ function stripJsonComments(text) {
|
|
|
2636
2648
|
result += ch;
|
|
2637
2649
|
continue;
|
|
2638
2650
|
}
|
|
2639
|
-
if (ch === "/" &&
|
|
2640
|
-
while (i <
|
|
2651
|
+
if (ch === "/" && text2[i + 1] === "/") {
|
|
2652
|
+
while (i < text2.length && text2[i] !== "\n") i++;
|
|
2641
2653
|
result += "\n";
|
|
2642
2654
|
continue;
|
|
2643
2655
|
}
|
|
2644
|
-
if (ch === "/" &&
|
|
2656
|
+
if (ch === "/" && text2[i + 1] === "*") {
|
|
2645
2657
|
i += 2;
|
|
2646
|
-
while (i <
|
|
2658
|
+
while (i < text2.length && !(text2[i] === "*" && text2[i + 1] === "/")) i++;
|
|
2647
2659
|
i++;
|
|
2648
2660
|
continue;
|
|
2649
2661
|
}
|
|
@@ -2660,7 +2672,14 @@ async function wranglerLoggedIn(run, cwd) {
|
|
|
2660
2672
|
}
|
|
2661
2673
|
}
|
|
2662
2674
|
function wranglerPutSecret(run, opts) {
|
|
2663
|
-
const args = [
|
|
2675
|
+
const args = [
|
|
2676
|
+
"wrangler",
|
|
2677
|
+
"secret",
|
|
2678
|
+
"put",
|
|
2679
|
+
opts.name,
|
|
2680
|
+
...opts.env ? ["--env", opts.env] : [],
|
|
2681
|
+
...opts.configPath ? ["--config", opts.configPath] : []
|
|
2682
|
+
];
|
|
2664
2683
|
return run("npx", args, { input: opts.value, cwd: opts.cwd });
|
|
2665
2684
|
}
|
|
2666
2685
|
|
|
@@ -3019,9 +3038,9 @@ function initProject(options) {
|
|
|
3019
3038
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
3020
3039
|
out.log("updated .gitignore for local odla credentials");
|
|
3021
3040
|
}
|
|
3022
|
-
function writeIfMissing(path,
|
|
3041
|
+
function writeIfMissing(path, text2) {
|
|
3023
3042
|
if (existsSync6(path)) return;
|
|
3024
|
-
writeFileSync2(path,
|
|
3043
|
+
writeFileSync2(path, text2);
|
|
3025
3044
|
}
|
|
3026
3045
|
function configTemplate(input) {
|
|
3027
3046
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -3229,13 +3248,13 @@ async function secretsSetClerkKey(options) {
|
|
|
3229
3248
|
body: JSON.stringify({ value: value2 })
|
|
3230
3249
|
});
|
|
3231
3250
|
if (!res.ok) {
|
|
3232
|
-
const
|
|
3233
|
-
throw new Error(`store Clerk secret key failed (${res.status}): ${
|
|
3251
|
+
const text2 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
|
|
3252
|
+
throw new Error(`store Clerk secret key failed (${res.status}): ${text2 || "request failed"}`);
|
|
3234
3253
|
}
|
|
3235
3254
|
out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
|
|
3236
3255
|
}
|
|
3237
|
-
function scrubValue(
|
|
3238
|
-
return redactSecrets(
|
|
3256
|
+
function scrubValue(text2, value2) {
|
|
3257
|
+
return redactSecrets(text2).split(value2).join("[value redacted]");
|
|
3239
3258
|
}
|
|
3240
3259
|
async function resolveVaultWrite(options) {
|
|
3241
3260
|
const out = options.stdout ?? console;
|
|
@@ -4218,8 +4237,8 @@ async function runContainerAttempt(options) {
|
|
|
4218
4237
|
let stopped = false;
|
|
4219
4238
|
let exited = false;
|
|
4220
4239
|
child.stderr.setEncoding("utf8");
|
|
4221
|
-
child.stderr.on("data", (
|
|
4222
|
-
if (stderr.length < 64 * 1024) stderr +=
|
|
4240
|
+
child.stderr.on("data", (text2) => {
|
|
4241
|
+
if (stderr.length < 64 * 1024) stderr += text2.slice(0, 64 * 1024 - stderr.length);
|
|
4223
4242
|
});
|
|
4224
4243
|
const stop = (reason) => {
|
|
4225
4244
|
if (stopped || exited) return;
|
|
@@ -5051,8 +5070,8 @@ function boundedInteger(value2, spec) {
|
|
|
5051
5070
|
}
|
|
5052
5071
|
function boundedNumber(value2, spec) {
|
|
5053
5072
|
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(
|
|
5073
|
+
const text2 = String(value2);
|
|
5074
|
+
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
5075
|
return value2;
|
|
5057
5076
|
}
|
|
5058
5077
|
function enumMember(value2, spec) {
|
|
@@ -5208,8 +5227,8 @@ function validateUnsafeSelector(path, value2, tool) {
|
|
|
5208
5227
|
return void 0;
|
|
5209
5228
|
}
|
|
5210
5229
|
function looksLikeDestination(value2) {
|
|
5211
|
-
const
|
|
5212
|
-
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(
|
|
5230
|
+
const text2 = value2.trim();
|
|
5231
|
+
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text2) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text2);
|
|
5213
5232
|
}
|
|
5214
5233
|
|
|
5215
5234
|
// ../harness/dist/chunk-GMVZ4LZH.js
|
|
@@ -5578,8 +5597,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
5578
5597
|
});
|
|
5579
5598
|
let stderr = "";
|
|
5580
5599
|
child.stderr.setEncoding("utf8");
|
|
5581
|
-
child.stderr.on("data", (
|
|
5582
|
-
if (stderr.length < 4e3) stderr +=
|
|
5600
|
+
child.stderr.on("data", (text2) => {
|
|
5601
|
+
if (stderr.length < 4e3) stderr += text2.slice(0, 4e3);
|
|
5583
5602
|
});
|
|
5584
5603
|
child.once("error", reject);
|
|
5585
5604
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(`patch did not apply: ${stderr.trim().slice(0, 500)}`)));
|
|
@@ -6750,10 +6769,10 @@ var CodePiRuntimeEngine = class {
|
|
|
6750
6769
|
task: lease.task,
|
|
6751
6770
|
limits: this.options.limits,
|
|
6752
6771
|
signal: active.abort.signal,
|
|
6753
|
-
onStderr: (
|
|
6772
|
+
onStderr: (text2) => this.#event(command, {
|
|
6754
6773
|
type: "message",
|
|
6755
6774
|
actor: "system",
|
|
6756
|
-
body:
|
|
6775
|
+
body: text2.slice(0, 4e3)
|
|
6757
6776
|
}, active.conversationRefs),
|
|
6758
6777
|
onMessage: async (output) => {
|
|
6759
6778
|
if (output.type === "inference.request") {
|
|
@@ -7516,7 +7535,9 @@ var COMMAND_SURFACE = {
|
|
|
7516
7535
|
init: {},
|
|
7517
7536
|
o11y: { status: {} },
|
|
7518
7537
|
operations: { get: {}, wait: {} },
|
|
7519
|
-
platform: {
|
|
7538
|
+
platform: {
|
|
7539
|
+
status: {}
|
|
7540
|
+
},
|
|
7520
7541
|
pm: {
|
|
7521
7542
|
...PM_ENTITIES,
|
|
7522
7543
|
handoff: {}
|
|
@@ -8413,8 +8434,8 @@ async function appImport(options) {
|
|
|
8413
8434
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
8414
8435
|
const doFetch = options.fetch ?? fetch;
|
|
8415
8436
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
8416
|
-
const
|
|
8417
|
-
const { format, sources } = parseImport(
|
|
8437
|
+
const text2 = options.file === "-" ? (options.readStdin ?? (() => readFileSync9(0, "utf8")))() : readFileSync9(options.file, "utf8");
|
|
8438
|
+
const { format, sources } = parseImport(text2, options.ns);
|
|
8418
8439
|
if (format === "namespace-map" && options.ns) {
|
|
8419
8440
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
8420
8441
|
}
|
|
@@ -8505,7 +8526,10 @@ function report(options, owners, headline) {
|
|
|
8505
8526
|
if (headline) out.log(headline);
|
|
8506
8527
|
out.log(`owners (${owners.length}):`);
|
|
8507
8528
|
for (const o of owners) {
|
|
8508
|
-
|
|
8529
|
+
const name = o.email?.trim() || "Unnamed member";
|
|
8530
|
+
out.log(
|
|
8531
|
+
` ${o.primary ? "\u2605" : "\xB7"} ${name} [${o.ownerId}]${o.primary ? " (primary)" : ""}`
|
|
8532
|
+
);
|
|
8509
8533
|
}
|
|
8510
8534
|
}
|
|
8511
8535
|
async function ownersList(options) {
|
|
@@ -9251,7 +9275,8 @@ Usage:
|
|
|
9251
9275
|
odla-ai doctor [--config odla.config.mjs]
|
|
9252
9276
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
9253
9277
|
odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
|
|
9254
|
-
odla-ai operations
|
|
9278
|
+
odla-ai operations get <operation-id> [--json]
|
|
9279
|
+
odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
|
|
9255
9280
|
odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
|
|
9256
9281
|
odla-ai calendar calendars [--env dev] [--email <odla-account>] [--json]
|
|
9257
9282
|
odla-ai calendar connect [--env dev] [--email <odla-account>] [--no-open] [--yes]
|
|
@@ -9365,9 +9390,9 @@ Commands:
|
|
|
9365
9390
|
does and what it guarantees is JSDoc, rendered per package at
|
|
9366
9391
|
https://odla.ai/docs and shipped in the installed .d.ts. Answering
|
|
9367
9392
|
a question usually needs both.
|
|
9368
|
-
whoami Report
|
|
9369
|
-
|
|
9370
|
-
|
|
9393
|
+
whoami Report the actual principal name, handle and kind, its manager,
|
|
9394
|
+
credential kind, accountable owner, and platform-admin status.
|
|
9395
|
+
A manager relationship never supplies authority by itself.
|
|
9371
9396
|
context Explain selected config, platform, app, environment, and
|
|
9372
9397
|
developer-token provenance without printing credentials or
|
|
9373
9398
|
starting a device handshake. Operator work can run outside a
|
|
@@ -9487,6 +9512,58 @@ Safety:
|
|
|
9487
9512
|
`);
|
|
9488
9513
|
}
|
|
9489
9514
|
|
|
9515
|
+
// src/discuss-principals.ts
|
|
9516
|
+
function mergeDiscussPrincipals(target, source) {
|
|
9517
|
+
Object.assign(target.authors, source.authors ?? {});
|
|
9518
|
+
Object.assign(target.principals, source.principals ?? {});
|
|
9519
|
+
}
|
|
9520
|
+
var withAt = (handle) => handle.startsWith("@") ? handle : `@${handle}`;
|
|
9521
|
+
function discussPrincipalLabel(principalId, authorKind, projection) {
|
|
9522
|
+
const profile = projection.principals?.[principalId];
|
|
9523
|
+
if (profile) {
|
|
9524
|
+
const identity = `${profile.displayName} (${withAt(profile.handle)})`;
|
|
9525
|
+
if (profile.kind === "agent") {
|
|
9526
|
+
return profile.managerDisplayName ? `${identity} \u2014 agent managed by ${profile.managerDisplayName}` : `${identity} \u2014 agent`;
|
|
9527
|
+
}
|
|
9528
|
+
return profile.kind === "service" ? `${identity} \u2014 service` : identity;
|
|
9529
|
+
}
|
|
9530
|
+
const legacyName = projection.authors?.[principalId]?.trim();
|
|
9531
|
+
if (legacyName) {
|
|
9532
|
+
return authorKind === "bot" ? `${legacyName} \u2014 agent` : legacyName;
|
|
9533
|
+
}
|
|
9534
|
+
return authorKind === "bot" ? "Unnamed agent" : "Unnamed member";
|
|
9535
|
+
}
|
|
9536
|
+
|
|
9537
|
+
// src/discuss-read-render.ts
|
|
9538
|
+
function discussBodyWithRefs(post) {
|
|
9539
|
+
if (!post.refs || post.refs.length === 0) return post.body;
|
|
9540
|
+
let out = "";
|
|
9541
|
+
let cursor = 0;
|
|
9542
|
+
for (const ref of [...post.refs].sort((a, b) => a.start - b.start)) {
|
|
9543
|
+
if (ref.start < cursor || ref.end > post.body.length) continue;
|
|
9544
|
+
out += post.body.slice(cursor, ref.start) + `@[${ref.label}](${ref.kind}/${ref.id})`;
|
|
9545
|
+
cursor = ref.end;
|
|
9546
|
+
}
|
|
9547
|
+
return out + post.body.slice(cursor);
|
|
9548
|
+
}
|
|
9549
|
+
function renderDiscussRead(ctx, topic, posts, projection) {
|
|
9550
|
+
const status = topic.resolved ? "resolved" : "open";
|
|
9551
|
+
ctx.out.log(`${topic.subject} [${status}] ${topic.appId ?? ""}`);
|
|
9552
|
+
for (const post of posts) {
|
|
9553
|
+
const who = discussPrincipalLabel(
|
|
9554
|
+
post.authorId,
|
|
9555
|
+
post.authorKind,
|
|
9556
|
+
projection
|
|
9557
|
+
);
|
|
9558
|
+
ctx.out.log(`
|
|
9559
|
+
\u2014 ${who}`);
|
|
9560
|
+
ctx.out.log(discussBodyWithRefs(post));
|
|
9561
|
+
for (const file of post.attachments ?? []) {
|
|
9562
|
+
ctx.out.log(` [attachment] ${file.name} (${file.size} bytes)`);
|
|
9563
|
+
}
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
|
|
9490
9567
|
// src/discuss-actions.ts
|
|
9491
9568
|
var writeMutationId = (parsed) => stringOpt(parsed.options["mutation-id"]) ?? crypto.randomUUID();
|
|
9492
9569
|
async function request(ctx, method, path, body) {
|
|
@@ -9505,17 +9582,6 @@ function emit(ctx, value2, human) {
|
|
|
9505
9582
|
else human();
|
|
9506
9583
|
}
|
|
9507
9584
|
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
9585
|
function content(parsed) {
|
|
9520
9586
|
const markup = stringOpt(parsed.options.markup);
|
|
9521
9587
|
if (markup) return { markup };
|
|
@@ -9570,11 +9636,19 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9570
9636
|
offset: requestedOffset ?? "0"
|
|
9571
9637
|
});
|
|
9572
9638
|
const page = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
|
|
9573
|
-
emit(
|
|
9639
|
+
emit(
|
|
9640
|
+
ctx,
|
|
9641
|
+
page,
|
|
9642
|
+
() => renderDiscussRead(ctx, page.topic, page.posts, page)
|
|
9643
|
+
);
|
|
9574
9644
|
return;
|
|
9575
9645
|
}
|
|
9576
9646
|
for (let scan = 0; scan < 3; scan++) {
|
|
9577
9647
|
const posts = /* @__PURE__ */ new Map();
|
|
9648
|
+
const projection = {
|
|
9649
|
+
authors: {},
|
|
9650
|
+
principals: {}
|
|
9651
|
+
};
|
|
9578
9652
|
let topic = null;
|
|
9579
9653
|
let offset = 0;
|
|
9580
9654
|
for (; ; ) {
|
|
@@ -9585,6 +9659,7 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9585
9659
|
);
|
|
9586
9660
|
topic = page.topic;
|
|
9587
9661
|
for (const post of page.posts) posts.set(post.id, post);
|
|
9662
|
+
mergeDiscussPrincipals(projection, page);
|
|
9588
9663
|
if (posts.size > 1e4) throw new Error("discuss read failed: conversation exceeds 10000 posts");
|
|
9589
9664
|
if (!page.page?.hasMore) break;
|
|
9590
9665
|
if (page.page.nextOffset === null || page.page.nextOffset <= offset) {
|
|
@@ -9597,24 +9672,18 @@ async function discussRead(ctx, id, parsed) {
|
|
|
9597
9672
|
);
|
|
9598
9673
|
const expected = Number.isInteger(topic.replyCount) ? topic.replyCount + 1 : ordered.length;
|
|
9599
9674
|
if (ordered.length === expected) {
|
|
9600
|
-
const result = { topic, posts: ordered };
|
|
9601
|
-
emit(
|
|
9675
|
+
const result = { topic, posts: ordered, ...projection };
|
|
9676
|
+
emit(
|
|
9677
|
+
ctx,
|
|
9678
|
+
result,
|
|
9679
|
+
() => renderDiscussRead(ctx, result.topic, result.posts, result)
|
|
9680
|
+
);
|
|
9602
9681
|
return;
|
|
9603
9682
|
}
|
|
9604
9683
|
if (offset === 0) throw new Error("discuss read failed: registry did not provide forward post pages");
|
|
9605
9684
|
}
|
|
9606
9685
|
throw new Error("discuss read failed: conversation changed during every complete-read attempt");
|
|
9607
9686
|
}
|
|
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
9687
|
async function discussPost(ctx, parsed) {
|
|
9619
9688
|
const appId = stringOpt(parsed.options.app) ?? ctx.appId;
|
|
9620
9689
|
if (!appId) throw new Error("discuss post needs --app <appId>");
|
|
@@ -9839,6 +9908,8 @@ async function discussWatch(ctx, topicId, parsed) {
|
|
|
9839
9908
|
found: true,
|
|
9840
9909
|
cursor,
|
|
9841
9910
|
events: matching,
|
|
9911
|
+
...page.authors ? { authors: page.authors } : {},
|
|
9912
|
+
...page.principals ? { principals: page.principals } : {},
|
|
9842
9913
|
...posts && posts.length > 0 ? { posts } : {},
|
|
9843
9914
|
...topics && topics.length > 0 ? { topics } : {}
|
|
9844
9915
|
});
|
|
@@ -9871,7 +9942,11 @@ function report2(ctx, parsed, result) {
|
|
|
9871
9942
|
ctx.out.log(JSON.stringify(result, null, 2));
|
|
9872
9943
|
} else if (parsed.options.jsonl !== true && result.found) {
|
|
9873
9944
|
for (const post of result.posts ?? []) {
|
|
9874
|
-
const who =
|
|
9945
|
+
const who = discussPrincipalLabel(
|
|
9946
|
+
post.authorId,
|
|
9947
|
+
post.authorKind,
|
|
9948
|
+
result
|
|
9949
|
+
);
|
|
9875
9950
|
ctx.out.log(`\u2014 ${who}
|
|
9876
9951
|
${post.body}`);
|
|
9877
9952
|
}
|
|
@@ -10016,8 +10091,8 @@ function collectFields(parsed, allowClear) {
|
|
|
10016
10091
|
if (allowClear) out[spec.key] = null;
|
|
10017
10092
|
continue;
|
|
10018
10093
|
}
|
|
10019
|
-
const
|
|
10020
|
-
out[spec.key] = spec.num ? Number(
|
|
10094
|
+
const text2 = stringOpt(value2);
|
|
10095
|
+
out[spec.key] = spec.num ? Number(text2) : text2;
|
|
10021
10096
|
}
|
|
10022
10097
|
return out;
|
|
10023
10098
|
}
|
|
@@ -10347,17 +10422,23 @@ function age(input) {
|
|
|
10347
10422
|
|
|
10348
10423
|
// src/platform-command.ts
|
|
10349
10424
|
async function platformCommand(parsed, deps = {}) {
|
|
10425
|
+
const action2 = parsed.positionals[1];
|
|
10426
|
+
if (action2 === "status") {
|
|
10427
|
+
return platformStatus(parsed, deps);
|
|
10428
|
+
}
|
|
10429
|
+
throw new Error(
|
|
10430
|
+
`unknown platform action "${[
|
|
10431
|
+
action2,
|
|
10432
|
+
parsed.positionals[2]
|
|
10433
|
+
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
10434
|
+
);
|
|
10435
|
+
}
|
|
10436
|
+
async function platformStatus(parsed, deps) {
|
|
10350
10437
|
assertArgs(
|
|
10351
10438
|
parsed,
|
|
10352
10439
|
["config", "context", "platform", "token", "email", "open", "json"],
|
|
10353
10440
|
2
|
|
10354
10441
|
);
|
|
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
10442
|
const context = await resolveOperatorContext(parsed, {
|
|
10362
10443
|
allowMissingConfig: true
|
|
10363
10444
|
});
|
|
@@ -10802,14 +10883,14 @@ function statusMinutes(value2) {
|
|
|
10802
10883
|
}
|
|
10803
10884
|
async function read2(url, headers, doFetch) {
|
|
10804
10885
|
const response2 = await doFetch(url, { headers });
|
|
10805
|
-
const
|
|
10886
|
+
const text2 = await response2.text();
|
|
10806
10887
|
let body = {};
|
|
10807
|
-
if (
|
|
10888
|
+
if (text2) {
|
|
10808
10889
|
try {
|
|
10809
|
-
const value2 = JSON.parse(
|
|
10890
|
+
const value2 = JSON.parse(text2);
|
|
10810
10891
|
body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
|
|
10811
10892
|
} catch {
|
|
10812
|
-
body = { message:
|
|
10893
|
+
body = { message: text2.slice(0, 300) };
|
|
10813
10894
|
}
|
|
10814
10895
|
}
|
|
10815
10896
|
return { httpStatus: response2.status, body };
|
|
@@ -10949,12 +11030,17 @@ async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
|
10949
11030
|
});
|
|
10950
11031
|
ctx.out.log(ctx.json ? JSON.stringify(created, null, 2) : `created ${slug} (${created.id})`);
|
|
10951
11032
|
}
|
|
10952
|
-
async function runbookEdit(ctx, slug, body, note, requires) {
|
|
11033
|
+
async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
10953
11034
|
const runbook = await bySlug(ctx, slug);
|
|
10954
11035
|
const result = await call(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
10955
11036
|
// An empty --requires clears the declaration; omitting the flag leaves
|
|
10956
11037
|
// whatever is there, so an ordinary body edit never drops it.
|
|
10957
|
-
patch: {
|
|
11038
|
+
patch: {
|
|
11039
|
+
body,
|
|
11040
|
+
expectedVersion: expectedVersion ?? runbook.version,
|
|
11041
|
+
...note ? { note } : {},
|
|
11042
|
+
...requires === void 0 ? {} : { requires: requires || null }
|
|
11043
|
+
}
|
|
10958
11044
|
});
|
|
10959
11045
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
10960
11046
|
ctx.out.log(`${slug} \u2192 v${result.record?.version ?? runbook.version + 1}`);
|
|
@@ -11008,12 +11094,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
11008
11094
|
// src/runbook-import.ts
|
|
11009
11095
|
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
11010
11096
|
import { basename as basename2, join as join13 } from "path";
|
|
11011
|
-
function parseRunbook(
|
|
11012
|
-
let rest =
|
|
11097
|
+
function parseRunbook(text2, slug) {
|
|
11098
|
+
let rest = text2;
|
|
11013
11099
|
const meta = {};
|
|
11014
|
-
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(
|
|
11100
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text2);
|
|
11015
11101
|
if (fm) {
|
|
11016
|
-
rest =
|
|
11102
|
+
rest = text2.slice(fm[0].length);
|
|
11017
11103
|
for (const line of fm[1].split(/\r?\n/)) {
|
|
11018
11104
|
const pair = /^(\w+)\s*:\s*(.+)$/.exec(line.trim());
|
|
11019
11105
|
if (!pair) continue;
|
|
@@ -11094,6 +11180,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
11094
11180
|
patch: {
|
|
11095
11181
|
title: r.title,
|
|
11096
11182
|
body: r.body,
|
|
11183
|
+
expectedVersion: found.version,
|
|
11097
11184
|
...r.summary ? { summary: r.summary } : {},
|
|
11098
11185
|
...r.tags ? { tags: r.tags } : {},
|
|
11099
11186
|
note: `imported from ${r.file}`
|
|
@@ -11534,27 +11621,90 @@ async function editRunbook(ctx, slug, deps = {}) {
|
|
|
11534
11621
|
const found = page.records[0];
|
|
11535
11622
|
if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
|
|
11536
11623
|
ctx.out.log(`opening ${slug} v${found.version} in your editor\u2026`);
|
|
11537
|
-
|
|
11624
|
+
const body = await editText(found.body, slug, deps);
|
|
11625
|
+
return body === null ? null : { body, expectedVersion: found.version };
|
|
11538
11626
|
}
|
|
11539
11627
|
|
|
11540
11628
|
// src/whoami-command.ts
|
|
11629
|
+
var text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
11630
|
+
function principalKind(value2, machine) {
|
|
11631
|
+
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
11632
|
+
}
|
|
11633
|
+
function credentialKind(value2, machine, scopes) {
|
|
11634
|
+
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
11635
|
+
if (machine) return "machine";
|
|
11636
|
+
if (scopes.length) return "device";
|
|
11637
|
+
return "unknown";
|
|
11638
|
+
}
|
|
11639
|
+
function managerOf(value2) {
|
|
11640
|
+
if (!value2 || typeof value2 !== "object") return null;
|
|
11641
|
+
const row = value2;
|
|
11642
|
+
const principalId = text(row.principalId);
|
|
11643
|
+
if (!principalId) return null;
|
|
11644
|
+
return {
|
|
11645
|
+
principalId,
|
|
11646
|
+
displayName: text(row.displayName) ?? "Unnamed member",
|
|
11647
|
+
handle: text(row.handle) ?? ""
|
|
11648
|
+
};
|
|
11649
|
+
}
|
|
11650
|
+
function unnamedPrincipal(kind) {
|
|
11651
|
+
if (kind === "agent") return "Unnamed agent";
|
|
11652
|
+
if (kind === "service") return "Unnamed service";
|
|
11653
|
+
return "Unnamed member";
|
|
11654
|
+
}
|
|
11541
11655
|
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
11542
11656
|
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
11543
11657
|
headers: { authorization: `Bearer ${token}` }
|
|
11544
11658
|
});
|
|
11545
11659
|
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
11546
11660
|
const body = await res.json();
|
|
11661
|
+
const developerId = text(body.developerId) ?? "";
|
|
11662
|
+
const machine = body.machine === true;
|
|
11663
|
+
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
11664
|
+
const principalId = text(body.principalId) ?? developerId;
|
|
11665
|
+
const email = text(body.email);
|
|
11666
|
+
const kind = principalKind(body.principalKind, machine);
|
|
11667
|
+
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
11668
|
+
const handle = text(body.handle) ?? "";
|
|
11669
|
+
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
11547
11670
|
return {
|
|
11548
|
-
developerId
|
|
11549
|
-
|
|
11671
|
+
developerId,
|
|
11672
|
+
principalId,
|
|
11673
|
+
principalKind: kind,
|
|
11674
|
+
displayName,
|
|
11675
|
+
handle,
|
|
11676
|
+
manager: managerOf(body.manager),
|
|
11677
|
+
credential: {
|
|
11678
|
+
id: text(credential2.id),
|
|
11679
|
+
kind: credentialKind(credential2.kind, machine, scopes)
|
|
11680
|
+
},
|
|
11681
|
+
email,
|
|
11550
11682
|
admin: body.admin === true,
|
|
11551
|
-
machine
|
|
11552
|
-
scopes
|
|
11683
|
+
machine,
|
|
11684
|
+
scopes
|
|
11553
11685
|
};
|
|
11554
11686
|
}
|
|
11555
|
-
function
|
|
11556
|
-
if (identity.machine) return "machine (platform admin secret)";
|
|
11557
|
-
|
|
11687
|
+
function credentialLabel(identity) {
|
|
11688
|
+
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
11689
|
+
if (identity.credential.kind === "device")
|
|
11690
|
+
return identity.scopes.length ? "device (scoped)" : "device";
|
|
11691
|
+
if (identity.credential.kind === "clerk") return "clerk";
|
|
11692
|
+
return "unknown (legacy server)";
|
|
11693
|
+
}
|
|
11694
|
+
function namedPrincipal(identity) {
|
|
11695
|
+
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
11696
|
+
}
|
|
11697
|
+
function namedManager(manager) {
|
|
11698
|
+
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
11699
|
+
}
|
|
11700
|
+
function accountableOwner(identity) {
|
|
11701
|
+
if (identity.principalKind === "agent" && identity.manager) {
|
|
11702
|
+
return namedManager(identity.manager);
|
|
11703
|
+
}
|
|
11704
|
+
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
11705
|
+
return namedPrincipal(identity);
|
|
11706
|
+
}
|
|
11707
|
+
return identity.email ?? "Unnamed member";
|
|
11558
11708
|
}
|
|
11559
11709
|
async function whoamiCommand(parsed, deps = {}) {
|
|
11560
11710
|
assertArgs(
|
|
@@ -11585,9 +11735,19 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
11585
11735
|
return;
|
|
11586
11736
|
}
|
|
11587
11737
|
out.log(`platform: ${cfg.platformUrl}`);
|
|
11588
|
-
out.log(`
|
|
11738
|
+
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
11739
|
+
out.log(`principal id: ${identity.principalId}`);
|
|
11740
|
+
out.log(`kind: ${identity.principalKind}`);
|
|
11741
|
+
if (identity.principalKind === "agent")
|
|
11742
|
+
out.log(
|
|
11743
|
+
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
11744
|
+
);
|
|
11745
|
+
out.log(`owner: ${accountableOwner(identity)}`);
|
|
11746
|
+
out.log(`owner id: ${identity.developerId}`);
|
|
11589
11747
|
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
11590
|
-
out.log(`credential: ${
|
|
11748
|
+
out.log(`credential: ${credentialLabel(identity)}`);
|
|
11749
|
+
if (identity.credential.id)
|
|
11750
|
+
out.log(`credential id: ${identity.credential.id}`);
|
|
11591
11751
|
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
11592
11752
|
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
11593
11753
|
if (!identity.admin) {
|
|
@@ -11736,14 +11896,16 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
11736
11896
|
const name = requireSlug(slug, "edit");
|
|
11737
11897
|
const file = stringOpt(parsed.options.file);
|
|
11738
11898
|
const inline = stringOpt(parsed.options.body);
|
|
11739
|
-
const
|
|
11740
|
-
if (
|
|
11899
|
+
const edited = file === void 0 && inline === void 0 ? await editRunbook(ctx, name) : readBody(file, inline);
|
|
11900
|
+
if (edited === null) return ctx.out.log(`${name} unchanged; nothing written`);
|
|
11901
|
+
const body = typeof edited === "string" ? edited : edited.body;
|
|
11741
11902
|
return runbookEdit(
|
|
11742
11903
|
ctx,
|
|
11743
11904
|
name,
|
|
11744
11905
|
body,
|
|
11745
11906
|
stringOpt(parsed.options.note),
|
|
11746
|
-
parsed.options.requires === void 0 ? void 0 : stringOpt(parsed.options.requires) ?? ""
|
|
11907
|
+
parsed.options.requires === void 0 ? void 0 : stringOpt(parsed.options.requires) ?? "",
|
|
11908
|
+
typeof edited === "string" ? void 0 : edited.expectedVersion
|
|
11747
11909
|
);
|
|
11748
11910
|
}
|
|
11749
11911
|
case "import": {
|
|
@@ -12414,4 +12576,4 @@ export {
|
|
|
12414
12576
|
exitCodeFor,
|
|
12415
12577
|
runCli
|
|
12416
12578
|
};
|
|
12417
|
-
//# sourceMappingURL=chunk-
|
|
12579
|
+
//# sourceMappingURL=chunk-MBZXWPZP.js.map
|