@odla-ai/cli 0.46.11 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/bin.cjs +615 -347
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-J6GIGX6T.js → chunk-BD5KIG63.js} +578 -327
- package/dist/chunk-BD5KIG63.js.map +1 -0
- package/dist/{cli-BI26ENP4.js → cli-MXYYOFBR.js} +2 -2
- package/dist/index.cjs +580 -329
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/skills/odla/references/sdks.md +5 -0
- package/dist/chunk-J6GIGX6T.js.map +0 -1
- /package/dist/{cli-BI26ENP4.js.map → cli-MXYYOFBR.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1385,6 +1385,7 @@ var COMMAND_SURFACE = {
|
|
|
1385
1385
|
watch: {}
|
|
1386
1386
|
},
|
|
1387
1387
|
provision: {},
|
|
1388
|
+
promotion: { plan: {}, inspect: {}, status: {}, apply: {}, rollback: {}, next: {}, record: {}, cancel: {} },
|
|
1388
1389
|
runbook: {
|
|
1389
1390
|
ask: {},
|
|
1390
1391
|
search: {},
|
|
@@ -3733,9 +3734,9 @@ function canonicalValue(value2) {
|
|
|
3733
3734
|
}
|
|
3734
3735
|
if (Array.isArray(value2)) return value2.map(canonicalValue);
|
|
3735
3736
|
if (value2 && typeof value2 === "object") {
|
|
3736
|
-
const
|
|
3737
|
+
const record12 = value2;
|
|
3737
3738
|
return Object.fromEntries(
|
|
3738
|
-
Object.keys(
|
|
3739
|
+
Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, canonicalValue(record12[key])])
|
|
3739
3740
|
);
|
|
3740
3741
|
}
|
|
3741
3742
|
throw new TypeError("canonical JSON rejects unsupported values");
|
|
@@ -6119,10 +6120,10 @@ var import_node_fs16 = require("fs");
|
|
|
6119
6120
|
var import_node_os5 = require("os");
|
|
6120
6121
|
var import_node_path16 = require("path");
|
|
6121
6122
|
|
|
6122
|
-
// ../harness/dist/chunk-
|
|
6123
|
+
// ../harness/dist/chunk-U324RQ4N.js
|
|
6123
6124
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
6124
6125
|
|
|
6125
|
-
// ../harness/dist/chunk-
|
|
6126
|
+
// ../harness/dist/chunk-5LRYJKUI.js
|
|
6126
6127
|
var import_child_process = require("child_process");
|
|
6127
6128
|
var import_fs = require("fs");
|
|
6128
6129
|
var import_promises2 = require("fs/promises");
|
|
@@ -6290,8 +6291,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
6290
6291
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
6291
6292
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
6292
6293
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
6293
|
-
const entries = inventory.flatMap((
|
|
6294
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
6294
|
+
const entries = inventory.flatMap((record12) => {
|
|
6295
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record12);
|
|
6295
6296
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
6296
6297
|
});
|
|
6297
6298
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -6495,7 +6496,165 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
6495
6496
|
}
|
|
6496
6497
|
}
|
|
6497
6498
|
|
|
6498
|
-
// ../harness/dist/chunk-
|
|
6499
|
+
// ../harness/dist/chunk-FAN2R3GW.js
|
|
6500
|
+
var text3 = (value2, maximum) => {
|
|
6501
|
+
if (typeof value2 !== "string") return void 0;
|
|
6502
|
+
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
6503
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
6504
|
+
};
|
|
6505
|
+
var integer = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
6506
|
+
var record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
6507
|
+
var excerpt = (value2, tail = false) => {
|
|
6508
|
+
if (typeof value2 !== "string") return void 0;
|
|
6509
|
+
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
6510
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
6511
|
+
if (!source) return void 0;
|
|
6512
|
+
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
6513
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
6514
|
+
return text3(selected.join("\n"), 2400);
|
|
6515
|
+
};
|
|
6516
|
+
var paths = (value2) => {
|
|
6517
|
+
if (!Array.isArray(value2)) return void 0;
|
|
6518
|
+
const items = value2.flatMap((item) => {
|
|
6519
|
+
const path = text3(item, 1024);
|
|
6520
|
+
return path ? [path] : [];
|
|
6521
|
+
}).slice(0, 12);
|
|
6522
|
+
return items.length ? items : void 0;
|
|
6523
|
+
};
|
|
6524
|
+
function patchStats(value2) {
|
|
6525
|
+
if (typeof value2 !== "string") return {};
|
|
6526
|
+
let additions = 0;
|
|
6527
|
+
let deletions = 0;
|
|
6528
|
+
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
6529
|
+
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
6530
|
+
if (line2.startsWith("+")) additions += 1;
|
|
6531
|
+
else if (line2.startsWith("-")) deletions += 1;
|
|
6532
|
+
}
|
|
6533
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
6534
|
+
}
|
|
6535
|
+
function searchResults(value2) {
|
|
6536
|
+
if (typeof value2 !== "string") return void 0;
|
|
6537
|
+
const results = value2.split("\n").flatMap((line2) => {
|
|
6538
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
6539
|
+
if (!match) return [];
|
|
6540
|
+
const lineNumber = Number(match[2]);
|
|
6541
|
+
const itemText = text3(match[3], 240);
|
|
6542
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
6543
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
6544
|
+
}).slice(0, 5);
|
|
6545
|
+
return results.length ? results : void 0;
|
|
6546
|
+
}
|
|
6547
|
+
function codeToolRequestPresentation(request3) {
|
|
6548
|
+
const input = request3.input;
|
|
6549
|
+
if (request3.tool === "sandbox.read") {
|
|
6550
|
+
const path = text3(input.path, 1024);
|
|
6551
|
+
if (!path) return void 0;
|
|
6552
|
+
const startLine = integer(input.startLine);
|
|
6553
|
+
const endLine = integer(input.endLine);
|
|
6554
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
6555
|
+
}
|
|
6556
|
+
if (request3.tool === "sandbox.list") {
|
|
6557
|
+
const scope = text3(input.prefix, 1024);
|
|
6558
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
6559
|
+
}
|
|
6560
|
+
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
6561
|
+
const query = text3(input.query, 512);
|
|
6562
|
+
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
6563
|
+
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
6564
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
6565
|
+
}
|
|
6566
|
+
if (request3.tool === "sandbox.apply_patch") {
|
|
6567
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
6568
|
+
}
|
|
6569
|
+
const recipeId = text3(input.recipeId, 120);
|
|
6570
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
6571
|
+
}
|
|
6572
|
+
function codeToolResultPresentation(request3, response2) {
|
|
6573
|
+
const started = codeToolRequestPresentation(request3);
|
|
6574
|
+
if (!started || !response2.ok) return started;
|
|
6575
|
+
const details = record5(response2.details);
|
|
6576
|
+
if (started.kind === "read") {
|
|
6577
|
+
return {
|
|
6578
|
+
...started,
|
|
6579
|
+
...integer(details?.startLine) ? { startLine: integer(details?.startLine) } : {},
|
|
6580
|
+
...integer(details?.endLine) ? { endLine: integer(details?.endLine) } : {},
|
|
6581
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
6582
|
+
};
|
|
6583
|
+
}
|
|
6584
|
+
if (started.kind === "list") {
|
|
6585
|
+
const listed = response2.content.split("\n").filter((line2) => line2 && !line2.startsWith("\u2026") && !line2.startsWith("Workspace ")).map((line2) => text3(line2, 1024)).filter((line2) => Boolean(line2)).slice(0, 8);
|
|
6586
|
+
return {
|
|
6587
|
+
...started,
|
|
6588
|
+
...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
|
|
6589
|
+
...listed.length ? { paths: listed } : {}
|
|
6590
|
+
};
|
|
6591
|
+
}
|
|
6592
|
+
if (started.kind === "query") {
|
|
6593
|
+
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
6594
|
+
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
6595
|
+
return {
|
|
6596
|
+
...started,
|
|
6597
|
+
...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
|
|
6598
|
+
...results ? { results } : {},
|
|
6599
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
6600
|
+
};
|
|
6601
|
+
}
|
|
6602
|
+
if (started.kind === "patch") {
|
|
6603
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
6604
|
+
}
|
|
6605
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
6606
|
+
return {
|
|
6607
|
+
...started,
|
|
6608
|
+
...integer(details?.exitCode) !== void 0 ? { exitCode: integer(details?.exitCode) } : {},
|
|
6609
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
6610
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
6611
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
6612
|
+
};
|
|
6613
|
+
}
|
|
6614
|
+
var MAX_FAILURE_REASON = 240;
|
|
6615
|
+
function toolFailureReason(response2) {
|
|
6616
|
+
if (response2.ok) return void 0;
|
|
6617
|
+
const supplied = response2.details?.failureReason;
|
|
6618
|
+
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
6619
|
+
return reason.slice(0, MAX_FAILURE_REASON);
|
|
6620
|
+
}
|
|
6621
|
+
var DEFAULT_FAILURE_REASON = {
|
|
6622
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
6623
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
6624
|
+
};
|
|
6625
|
+
function observedBroker(input) {
|
|
6626
|
+
const now = input.now ?? Date.now;
|
|
6627
|
+
return {
|
|
6628
|
+
execute: async (context, request3) => {
|
|
6629
|
+
const startedAt = now();
|
|
6630
|
+
const operationId = input.operationIdFor(request3.requestId);
|
|
6631
|
+
const startedPresentation = codeToolRequestPresentation(request3);
|
|
6632
|
+
await input.emit({
|
|
6633
|
+
type: "tool",
|
|
6634
|
+
phase: "started",
|
|
6635
|
+
tool: request3.tool,
|
|
6636
|
+
operationId,
|
|
6637
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
6638
|
+
});
|
|
6639
|
+
const response2 = await input.broker.execute(context, request3);
|
|
6640
|
+
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
6641
|
+
const failureReason = toolFailureReason(response2);
|
|
6642
|
+
await input.emit({
|
|
6643
|
+
type: "tool",
|
|
6644
|
+
phase: "completed",
|
|
6645
|
+
tool: request3.tool,
|
|
6646
|
+
ok: response2.ok,
|
|
6647
|
+
durationMs: now() - startedAt,
|
|
6648
|
+
operationId,
|
|
6649
|
+
...failureReason ? { failureReason } : {},
|
|
6650
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
6651
|
+
});
|
|
6652
|
+
return response2;
|
|
6653
|
+
}
|
|
6654
|
+
};
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
6499
6658
|
var import_crypto = require("crypto");
|
|
6500
6659
|
var import_promises5 = require("fs/promises");
|
|
6501
6660
|
var import_path5 = require("path");
|
|
@@ -6539,8 +6698,8 @@ function normalize(value2) {
|
|
|
6539
6698
|
if (Array.isArray(value2)) return value2.map(normalize);
|
|
6540
6699
|
if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
|
|
6541
6700
|
if (typeof value2 === "object") {
|
|
6542
|
-
const
|
|
6543
|
-
return Object.fromEntries(Object.keys(
|
|
6701
|
+
const record12 = value2;
|
|
6702
|
+
return Object.fromEntries(Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, normalize(record12[key])]));
|
|
6544
6703
|
}
|
|
6545
6704
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
6546
6705
|
}
|
|
@@ -6832,7 +6991,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6832
6991
|
}
|
|
6833
6992
|
}
|
|
6834
6993
|
|
|
6835
|
-
// ../harness/dist/chunk-
|
|
6994
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
6836
6995
|
var import_child_process4 = require("child_process");
|
|
6837
6996
|
var import_promises6 = require("fs/promises");
|
|
6838
6997
|
var import_path6 = require("path");
|
|
@@ -7136,7 +7295,7 @@ function looksLikeDestination(value2) {
|
|
|
7136
7295
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text4) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text4);
|
|
7137
7296
|
}
|
|
7138
7297
|
|
|
7139
|
-
// ../harness/dist/chunk-
|
|
7298
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
7140
7299
|
var import_promises10 = require("fs/promises");
|
|
7141
7300
|
var import_promises11 = require("fs/promises");
|
|
7142
7301
|
var import_path10 = require("path");
|
|
@@ -7404,7 +7563,7 @@ async function buildCodeGraph(input) {
|
|
|
7404
7563
|
return builder.build();
|
|
7405
7564
|
}
|
|
7406
7565
|
|
|
7407
|
-
// ../harness/dist/chunk-
|
|
7566
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
7408
7567
|
var import_crypto4 = require("crypto");
|
|
7409
7568
|
async function digestStagedWorkspace(root, limits) {
|
|
7410
7569
|
const files = [];
|
|
@@ -7463,7 +7622,7 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7463
7622
|
}
|
|
7464
7623
|
const value2 = await response2.json().catch(() => null);
|
|
7465
7624
|
if (!response2.ok) {
|
|
7466
|
-
const problem =
|
|
7625
|
+
const problem = record6(record6(value2)?.error);
|
|
7467
7626
|
throw new CodeRuntimeControlError(
|
|
7468
7627
|
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
7469
7628
|
response2.status,
|
|
@@ -7484,12 +7643,12 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7484
7643
|
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
7485
7644
|
),
|
|
7486
7645
|
infer: async (sessionId, inference) => {
|
|
7487
|
-
const value2 =
|
|
7646
|
+
const value2 = record6(await call4(
|
|
7488
7647
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
7489
7648
|
inference,
|
|
7490
7649
|
modelRequestTimeoutMs
|
|
7491
7650
|
));
|
|
7492
|
-
if (!value2 || value2.requestId !== inference.requestId || !
|
|
7651
|
+
if (!value2 || value2.requestId !== inference.requestId || !record6(value2.response) || !record6(value2.receipt)) {
|
|
7493
7652
|
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
7494
7653
|
}
|
|
7495
7654
|
return value2;
|
|
@@ -7669,12 +7828,12 @@ function validateHeartbeat(version, capabilities) {
|
|
|
7669
7828
|
}
|
|
7670
7829
|
}
|
|
7671
7830
|
function parseSnapshot(value2) {
|
|
7672
|
-
const root =
|
|
7673
|
-
const host =
|
|
7831
|
+
const root = record6(value2);
|
|
7832
|
+
const host = record6(root?.host);
|
|
7674
7833
|
if (!host || typeof host.hostId !== "string" || typeof host.runtimeVersion !== "string" || !Number.isSafeInteger(host.lastSeenAt) || host.revokedAt !== null || !Array.isArray(root?.bindings) || root.bindings.length > 1024 || !Array.isArray(root?.commands) || root.commands.length > 64) throw invalid("heartbeat");
|
|
7675
7834
|
const bindingIds = /* @__PURE__ */ new Set();
|
|
7676
7835
|
const bindings = root.bindings.map((item) => {
|
|
7677
|
-
const binding =
|
|
7836
|
+
const binding = record6(item);
|
|
7678
7837
|
if (!binding || typeof binding.bindingId !== "string" || typeof binding.appId !== "string" || binding.env !== "dev" && binding.env !== "prod" || typeof binding.offerId !== "string" || binding.hostId !== host.hostId || !Number.isSafeInteger(binding.generation) || Number(binding.generation) < 1 || binding.revokedAt !== null || bindingIds.has(binding.bindingId)) {
|
|
7679
7838
|
throw invalid("binding");
|
|
7680
7839
|
}
|
|
@@ -7684,10 +7843,10 @@ function parseSnapshot(value2) {
|
|
|
7684
7843
|
const commandIds = /* @__PURE__ */ new Set();
|
|
7685
7844
|
const commandSequences = /* @__PURE__ */ new Set();
|
|
7686
7845
|
const commands = root.commands.map((item) => {
|
|
7687
|
-
const command =
|
|
7846
|
+
const command = record6(item);
|
|
7688
7847
|
const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
|
|
7689
7848
|
const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
|
|
7690
|
-
if (!command || typeof command.commandId !== "string" || !/^ccmd_[0-9a-f]{32}$/.test(command.commandId) || typeof command.instanceId !== "string" || typeof command.sessionId !== "string" || !/^csess_[0-9a-f]{32}$/.test(command.sessionId) || typeof command.appId !== "string" || command.env !== "dev" && command.env !== "prod" || command.hostId !== host.hostId || !binding || binding.appId !== command.appId || binding.generation !== command.bindingGeneration || !Number.isSafeInteger(command.sequence) || Number(command.sequence) < 1 || commandIds.has(command.commandId) || commandSequences.has(sequenceKey) || !["start", "prompt", "checkpoint_stop", "resume"].includes(String(command.kind)) || !
|
|
7849
|
+
if (!command || typeof command.commandId !== "string" || !/^ccmd_[0-9a-f]{32}$/.test(command.commandId) || typeof command.instanceId !== "string" || typeof command.sessionId !== "string" || !/^csess_[0-9a-f]{32}$/.test(command.sessionId) || typeof command.appId !== "string" || command.env !== "dev" && command.env !== "prod" || command.hostId !== host.hostId || !binding || binding.appId !== command.appId || binding.generation !== command.bindingGeneration || !Number.isSafeInteger(command.sequence) || Number(command.sequence) < 1 || commandIds.has(command.commandId) || commandSequences.has(sequenceKey) || !["start", "prompt", "checkpoint_stop", "resume"].includes(String(command.kind)) || !record6(command.payload) || !Number.isSafeInteger(command.createdAt)) throw invalid("command");
|
|
7691
7850
|
commandIds.add(command.commandId);
|
|
7692
7851
|
commandSequences.add(sequenceKey);
|
|
7693
7852
|
return command;
|
|
@@ -7696,10 +7855,10 @@ function parseSnapshot(value2) {
|
|
|
7696
7855
|
}
|
|
7697
7856
|
async function parseSource(value2) {
|
|
7698
7857
|
const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
|
|
7699
|
-
const snapshot =
|
|
7858
|
+
const snapshot = record6(record6(value2)?.snapshot);
|
|
7700
7859
|
if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
|
|
7701
7860
|
const files = snapshot.files.map((value22) => {
|
|
7702
|
-
const file =
|
|
7861
|
+
const file = record6(value22);
|
|
7703
7862
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
|
|
7704
7863
|
return { path: file.path, content: file.content };
|
|
7705
7864
|
});
|
|
@@ -7708,11 +7867,11 @@ async function parseSource(value2) {
|
|
|
7708
7867
|
const aliases = /* @__PURE__ */ new Set();
|
|
7709
7868
|
const references = [];
|
|
7710
7869
|
for (const item of referencesValue) {
|
|
7711
|
-
const reference =
|
|
7870
|
+
const reference = record6(item);
|
|
7712
7871
|
if (!reference || typeof reference.alias !== "string" || !/^[a-z][a-z0-9-]{0,39}$/.test(reference.alias) || aliases.has(reference.alias) || reference.alias === "primary" || typeof reference.repository !== "string" || typeof reference.commitSha !== "string" || typeof reference.treeDigest !== "string" || !Array.isArray(reference.files)) throw invalid("reference source");
|
|
7713
7872
|
aliases.add(reference.alias);
|
|
7714
7873
|
const referenceFiles = reference.files.map((entry) => {
|
|
7715
|
-
const file =
|
|
7874
|
+
const file = record6(entry);
|
|
7716
7875
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
|
|
7717
7876
|
return { path: file.path, content: file.content };
|
|
7718
7877
|
});
|
|
@@ -7727,31 +7886,31 @@ async function parseSource(value2) {
|
|
|
7727
7886
|
return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
|
|
7728
7887
|
}
|
|
7729
7888
|
function parseReview(value2) {
|
|
7730
|
-
const review =
|
|
7731
|
-
if (!review || !["approved", "rejected"].includes(String(review.verdict)) || typeof review.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(review.reviewDigest) || typeof review.provider !== "string" || !review.provider || typeof review.model !== "string" || !review.model || !Number.isSafeInteger(review.policyVersion) || Number(review.policyVersion) < 1) throw invalid("review");
|
|
7889
|
+
const review = record6(record6(value2)?.review);
|
|
7890
|
+
if (!review || !["approved", "rejected"].includes(String(review.verdict)) || typeof review.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(review.reviewDigest) || typeof review.provider !== "string" || !review.provider || typeof review.model !== "string" || !review.model || !Number.isSafeInteger(review.policyVersion) || Number(review.policyVersion) < 1 || !Number.isSafeInteger(review.score) || Number(review.score) < 0 || Number(review.score) > 100 || typeof review.summary !== "string" || !Array.isArray(review.findings) || review.findings.length > 12) throw invalid("review");
|
|
7732
7891
|
return review;
|
|
7733
7892
|
}
|
|
7734
7893
|
function parseCandidate(value2) {
|
|
7735
|
-
const candidate =
|
|
7894
|
+
const candidate = record6(record6(value2)?.candidate);
|
|
7736
7895
|
if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
|
|
7737
7896
|
throw invalid("candidate");
|
|
7738
7897
|
}
|
|
7739
7898
|
return { candidateId: candidate.candidateId, status: candidate.status };
|
|
7740
7899
|
}
|
|
7741
7900
|
function parseCollaborationSkills(value2) {
|
|
7742
|
-
const items =
|
|
7901
|
+
const items = record6(value2)?.skills;
|
|
7743
7902
|
if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
|
|
7744
7903
|
const skillNames2 = /* @__PURE__ */ new Set();
|
|
7745
7904
|
const toolNames = /* @__PURE__ */ new Set();
|
|
7746
7905
|
return items.map((item) => {
|
|
7747
|
-
const skill =
|
|
7906
|
+
const skill = record6(item);
|
|
7748
7907
|
if (!skill || !validManifestName(skill.name) || skillNames2.has(skill.name) || skill.instructions !== void 0 && (typeof skill.instructions !== "string" || utf8Bytes(skill.instructions) > 32e3) || !Array.isArray(skill.tools) || !skill.tools.length || skill.tools.length > 128) {
|
|
7749
7908
|
throw invalid("collaboration skill");
|
|
7750
7909
|
}
|
|
7751
7910
|
skillNames2.add(skill.name);
|
|
7752
7911
|
const tools = skill.tools.map((candidate) => {
|
|
7753
|
-
const tool =
|
|
7754
|
-
const inputSchema =
|
|
7912
|
+
const tool = record6(candidate);
|
|
7913
|
+
const inputSchema = record6(tool?.inputSchema);
|
|
7755
7914
|
if (!tool || !validManifestName(tool.name) || toolNames.has(tool.name) || typeof tool.description !== "string" || utf8Bytes(tool.description) > 8e3 || !inputSchema || jsonBytes(inputSchema) > 64e3 || tool.concurrency !== void 0 && tool.concurrency !== "parallel") {
|
|
7756
7915
|
throw invalid("collaboration tool");
|
|
7757
7916
|
}
|
|
@@ -7776,12 +7935,12 @@ function parseCollaborationSkills(value2) {
|
|
|
7776
7935
|
}
|
|
7777
7936
|
function validateCollaborationToolRequest(value2) {
|
|
7778
7937
|
validCommandId(value2.commandId);
|
|
7779
|
-
if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !
|
|
7938
|
+
if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !record6(value2.input) || jsonBytes(value2.input) > 128e3) {
|
|
7780
7939
|
throw new TypeError("invalid Code collaboration tool request");
|
|
7781
7940
|
}
|
|
7782
7941
|
}
|
|
7783
7942
|
function parseCollaborationToolOutput(value2) {
|
|
7784
|
-
const output =
|
|
7943
|
+
const output = record6(record6(value2)?.output);
|
|
7785
7944
|
if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
|
|
7786
7945
|
throw invalid("collaboration tool");
|
|
7787
7946
|
}
|
|
@@ -7790,7 +7949,7 @@ function parseCollaborationToolOutput(value2) {
|
|
|
7790
7949
|
return { content: output.content, ...output.isError === true ? { isError: true } : {} };
|
|
7791
7950
|
}
|
|
7792
7951
|
if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block2) => {
|
|
7793
|
-
const item =
|
|
7952
|
+
const item = record6(block2);
|
|
7794
7953
|
return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
|
|
7795
7954
|
})) throw invalid("collaboration tool");
|
|
7796
7955
|
return {
|
|
@@ -7823,17 +7982,48 @@ function jsonBytes(value2) {
|
|
|
7823
7982
|
return Number.POSITIVE_INFINITY;
|
|
7824
7983
|
}
|
|
7825
7984
|
}
|
|
7826
|
-
var
|
|
7985
|
+
var record6 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
7827
7986
|
var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
|
|
7828
7987
|
var RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
7829
7988
|
var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
7830
7989
|
var PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
7831
7990
|
var FORBIDDEN = /^(?:GIT binary patch|Binary files |rename (?:from|to) |copy (?:from|to) |similarity index |old mode |new mode |deleted file mode 160000|new file mode 160000)/m;
|
|
7832
7991
|
function stripPatchEnvelope(patch2) {
|
|
7833
|
-
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
|
|
7992
|
+
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
|
|
7834
7993
|
const kept = patch2.split("\n").filter((line2) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line2));
|
|
7835
7994
|
const stripped = kept.join("\n");
|
|
7836
|
-
|
|
7995
|
+
if (/^diff --git /m.test(stripped)) return stripped;
|
|
7996
|
+
const translated = applyPatchDialectToDiff(stripped);
|
|
7997
|
+
return translated === stripped ? patch2 : translated;
|
|
7998
|
+
}
|
|
7999
|
+
function applyPatchDialectToDiff(patch2) {
|
|
8000
|
+
if (!/^\*\*\* (?:Update|Add|Delete) File: /m.test(patch2)) return patch2;
|
|
8001
|
+
const out = [];
|
|
8002
|
+
let open = false;
|
|
8003
|
+
for (const line2 of patch2.split("\n")) {
|
|
8004
|
+
const file = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/.exec(line2);
|
|
8005
|
+
if (file) {
|
|
8006
|
+
const [, verb, raw] = file;
|
|
8007
|
+
const path = raw.trim();
|
|
8008
|
+
if (!PATH.test(path)) return patch2;
|
|
8009
|
+
out.push(`diff --git a/${path} b/${path}`);
|
|
8010
|
+
if (verb === "Add") out.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
|
|
8011
|
+
else if (verb === "Delete") out.push(`--- a/${path}`, "+++ /dev/null");
|
|
8012
|
+
else out.push(`--- a/${path}`, `+++ b/${path}`);
|
|
8013
|
+
open = true;
|
|
8014
|
+
continue;
|
|
8015
|
+
}
|
|
8016
|
+
if (/^\*\*\* /.test(line2)) continue;
|
|
8017
|
+
if (!open) continue;
|
|
8018
|
+
if (/^@@/.test(line2)) {
|
|
8019
|
+
out.push("@@ -1 +1 @@");
|
|
8020
|
+
continue;
|
|
8021
|
+
}
|
|
8022
|
+
out.push(line2);
|
|
8023
|
+
}
|
|
8024
|
+
if (!open) return patch2;
|
|
8025
|
+
return `${out.join("\n").replace(/\n+$/, "")}
|
|
8026
|
+
`;
|
|
7837
8027
|
}
|
|
7838
8028
|
function validateCodePatch(rawPatch, maxBytes) {
|
|
7839
8029
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
@@ -7886,17 +8076,20 @@ function resolveCodePath(workspaceDir, path) {
|
|
|
7886
8076
|
if (target !== root && !target.startsWith(`${root}${import_path6.sep}`)) throw new TypeError("path escapes the staged workspace");
|
|
7887
8077
|
return target;
|
|
7888
8078
|
}
|
|
8079
|
+
function hasContextFreeHunk(patch2) {
|
|
8080
|
+
const bodies = patch2.split(/^@@.*$/m).slice(1);
|
|
8081
|
+
return bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
|
|
8082
|
+
}
|
|
7889
8083
|
function describePatchFailure(patch2, detail) {
|
|
7890
8084
|
const hunks = patch2.split("\n").filter((line2) => line2.startsWith("@@"));
|
|
7891
|
-
const
|
|
7892
|
-
const contextless = bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
|
|
7893
|
-
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
8085
|
+
const hint = hunks.length > 0 && hasContextFreeHunk(patch2) ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
7894
8086
|
return `patch did not apply: ${detail}${hint}`;
|
|
7895
8087
|
}
|
|
7896
8088
|
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
7897
8089
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
7898
|
-
|
|
7899
|
-
await gitApply(workspaceDir, patch2,
|
|
8090
|
+
const zero = hasContextFreeHunk(patch2);
|
|
8091
|
+
await gitApply(workspaceDir, patch2, true, zero);
|
|
8092
|
+
await gitApply(workspaceDir, patch2, false, zero);
|
|
7900
8093
|
for (const path of paths2) {
|
|
7901
8094
|
try {
|
|
7902
8095
|
const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
|
|
@@ -7908,9 +8101,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
|
7908
8101
|
}
|
|
7909
8102
|
}
|
|
7910
8103
|
}
|
|
7911
|
-
function gitApply(cwd, patch2, check) {
|
|
8104
|
+
function gitApply(cwd, patch2, check, unidiffZero = false) {
|
|
7912
8105
|
return new Promise((accept, reject) => {
|
|
7913
|
-
const args = [
|
|
8106
|
+
const args = [
|
|
8107
|
+
"apply",
|
|
8108
|
+
"--recount",
|
|
8109
|
+
...unidiffZero ? ["--unidiff-zero"] : [],
|
|
8110
|
+
"--whitespace=nowarn",
|
|
8111
|
+
...check ? ["--check"] : [],
|
|
8112
|
+
"-"
|
|
8113
|
+
];
|
|
7914
8114
|
const child = (0, import_child_process4.spawn)("git", args, {
|
|
7915
8115
|
cwd,
|
|
7916
8116
|
shell: false,
|
|
@@ -7919,8 +8119,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
7919
8119
|
});
|
|
7920
8120
|
let stderr2 = "";
|
|
7921
8121
|
child.stderr.setEncoding("utf8");
|
|
7922
|
-
child.stderr.on("data", (
|
|
7923
|
-
if (stderr2.length < 4e3) stderr2 +=
|
|
8122
|
+
child.stderr.on("data", (text4) => {
|
|
8123
|
+
if (stderr2.length < 4e3) stderr2 += text4.slice(0, 4e3);
|
|
7924
8124
|
});
|
|
7925
8125
|
child.once("error", reject);
|
|
7926
8126
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
|
|
@@ -8166,7 +8366,7 @@ function validate2(input) {
|
|
|
8166
8366
|
maximumFiles: policy.maximumFiles ?? 2e4,
|
|
8167
8367
|
maximumBytes: policy.maximumBytes ?? 512 * 1024 * 1024
|
|
8168
8368
|
};
|
|
8169
|
-
if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !
|
|
8369
|
+
if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !integer2(result.maximumChangedTests, 0, 1e4) || !integer2(result.maximumPatchBytes, 1, 4 * 1024 * 1024) || !integer2(result.maximumFiles, 1, 1e5) || !integer2(result.maximumBytes, 1, 2 * 1024 * 1024 * 1024)) {
|
|
8170
8370
|
throw new TypeError("clean verification policy exceeds its bounds");
|
|
8171
8371
|
}
|
|
8172
8372
|
return result;
|
|
@@ -8217,7 +8417,7 @@ function hashFile(path) {
|
|
|
8217
8417
|
});
|
|
8218
8418
|
}
|
|
8219
8419
|
function checkedResult(result, maximumOutputBytes) {
|
|
8220
|
-
if (!
|
|
8420
|
+
if (!integer2(result.exitCode, 0, 255) || !integer2(result.durationMs, 0, 30 * 6e4) || typeof result.stdout !== "string" || typeof result.stderr !== "string" || typeof result.outputLimitExceeded !== "boolean" || typeof result.timedOut !== "boolean") {
|
|
8221
8421
|
throw new TypeError("recipe executor returned an invalid result");
|
|
8222
8422
|
}
|
|
8223
8423
|
const bytes = Buffer.byteLength(result.stdout) + Buffer.byteLength(result.stderr);
|
|
@@ -8267,7 +8467,7 @@ function digestJson(value2) {
|
|
|
8267
8467
|
function digestBytes(value2) {
|
|
8268
8468
|
return `sha256:${(0, import_crypto3.createHash)("sha256").update(value2).digest("hex")}`;
|
|
8269
8469
|
}
|
|
8270
|
-
function
|
|
8470
|
+
function integer2(value2, minimum, maximum) {
|
|
8271
8471
|
return Number.isSafeInteger(value2) && value2 >= minimum && value2 <= maximum;
|
|
8272
8472
|
}
|
|
8273
8473
|
async function prepareRuntimeCheckpoint(input) {
|
|
@@ -8294,9 +8494,9 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
8294
8494
|
if (evidence.receipt.outcome === "passed") {
|
|
8295
8495
|
verification = evidence.receipt;
|
|
8296
8496
|
review = await input.review(patch2, verification);
|
|
8297
|
-
note = review
|
|
8497
|
+
note = describeReview(review);
|
|
8298
8498
|
} else {
|
|
8299
|
-
note =
|
|
8499
|
+
note = describeGateFailure(evidence);
|
|
8300
8500
|
}
|
|
8301
8501
|
} catch (cause) {
|
|
8302
8502
|
note = `Candidate verification or review failed closed: ${message(cause)}`;
|
|
@@ -8321,6 +8521,23 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
8321
8521
|
});
|
|
8322
8522
|
return { checkpoint, verification, review, note };
|
|
8323
8523
|
}
|
|
8524
|
+
function describeReview(review) {
|
|
8525
|
+
const findings = review.findings.map((finding) => ` - [${finding.severity}] ${finding.detail}`).join("\n");
|
|
8526
|
+
const head = review.verdict === "approved" ? `Clean verification and independent review passed (score ${review.score}/100).` : `Clean verification passed; independent review REJECTED this candidate (score ${review.score}/100). Address the findings and checkpoint again.`;
|
|
8527
|
+
return [head, review.summary, findings].filter(Boolean).join("\n").slice(0, 4e3);
|
|
8528
|
+
}
|
|
8529
|
+
function describeGateFailure(evidence) {
|
|
8530
|
+
const failed = evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed");
|
|
8531
|
+
const detail = failed.map((recipe2) => {
|
|
8532
|
+
const log = evidence.logs.find((entry) => entry.recipeId === recipe2.recipeId);
|
|
8533
|
+
const output = `${log?.stdout ?? ""}
|
|
8534
|
+
${log?.stderr ?? ""}`.trim();
|
|
8535
|
+
return `${recipe2.recipeId}=${recipe2.status}${output ? `
|
|
8536
|
+
${output.slice(0, 1500)}` : ""}`;
|
|
8537
|
+
}).join("\n\n");
|
|
8538
|
+
return `Clean verification failed. Fix this and checkpoint again:
|
|
8539
|
+
${detail}`.slice(0, 4e3);
|
|
8540
|
+
}
|
|
8324
8541
|
var message = (value2) => (value2 instanceof Error ? value2.message : String(value2)).slice(0, 500);
|
|
8325
8542
|
var CodeRuntimeCheckpointManager = class {
|
|
8326
8543
|
constructor(options) {
|
|
@@ -9339,8 +9556,14 @@ function optionalInteger(value2) {
|
|
|
9339
9556
|
if (!Number.isSafeInteger(value2) || value2 < 1) throw new TypeError("line bounds must be positive integers");
|
|
9340
9557
|
return value2;
|
|
9341
9558
|
}
|
|
9559
|
+
var DEFAULT_FAILURE_REASON2 = {
|
|
9560
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
9561
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
9562
|
+
};
|
|
9342
9563
|
function response(request3, ok, content2, details) {
|
|
9343
|
-
return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
9564
|
+
if (ok) return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
9565
|
+
const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content2] || content2 || "tool request failed; inspect the tool input and workspace state";
|
|
9566
|
+
return { requestId: request3.requestId, ok, content: content2, details: { ...details, failureReason } };
|
|
9344
9567
|
}
|
|
9345
9568
|
var cache = /* @__PURE__ */ new Map();
|
|
9346
9569
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
@@ -9916,120 +10139,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
9916
10139
|
}
|
|
9917
10140
|
var digestRuntimeValue = (value2) => `sha256:${(0, import_crypto4.createHash)("sha256").update(value2).digest("hex")}`;
|
|
9918
10141
|
var runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
|
|
9919
|
-
var text3 = (value2, maximum) => {
|
|
9920
|
-
if (typeof value2 !== "string") return void 0;
|
|
9921
|
-
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
9922
|
-
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
9923
|
-
};
|
|
9924
|
-
var integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
9925
|
-
var record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
9926
|
-
var excerpt = (value2, tail = false) => {
|
|
9927
|
-
if (typeof value2 !== "string") return void 0;
|
|
9928
|
-
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
9929
|
-
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
9930
|
-
if (!source) return void 0;
|
|
9931
|
-
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
9932
|
-
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
9933
|
-
return text3(selected.join("\n"), 2400);
|
|
9934
|
-
};
|
|
9935
|
-
var paths = (value2) => {
|
|
9936
|
-
if (!Array.isArray(value2)) return void 0;
|
|
9937
|
-
const items = value2.flatMap((item) => {
|
|
9938
|
-
const path = text3(item, 1024);
|
|
9939
|
-
return path ? [path] : [];
|
|
9940
|
-
}).slice(0, 12);
|
|
9941
|
-
return items.length ? items : void 0;
|
|
9942
|
-
};
|
|
9943
|
-
function patchStats(value2) {
|
|
9944
|
-
if (typeof value2 !== "string") return {};
|
|
9945
|
-
let additions = 0;
|
|
9946
|
-
let deletions = 0;
|
|
9947
|
-
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
9948
|
-
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
9949
|
-
if (line2.startsWith("+")) additions += 1;
|
|
9950
|
-
else if (line2.startsWith("-")) deletions += 1;
|
|
9951
|
-
}
|
|
9952
|
-
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
9953
|
-
}
|
|
9954
|
-
function searchResults(value2) {
|
|
9955
|
-
if (typeof value2 !== "string") return void 0;
|
|
9956
|
-
const results = value2.split("\n").flatMap((line2) => {
|
|
9957
|
-
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
9958
|
-
if (!match) return [];
|
|
9959
|
-
const lineNumber = Number(match[2]);
|
|
9960
|
-
const itemText = text3(match[3], 240);
|
|
9961
|
-
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
9962
|
-
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
9963
|
-
}).slice(0, 5);
|
|
9964
|
-
return results.length ? results : void 0;
|
|
9965
|
-
}
|
|
9966
|
-
function codeToolRequestPresentation(request3) {
|
|
9967
|
-
const input = request3.input;
|
|
9968
|
-
if (request3.tool === "sandbox.read") {
|
|
9969
|
-
const path = text3(input.path, 1024);
|
|
9970
|
-
if (!path) return void 0;
|
|
9971
|
-
const startLine = integer2(input.startLine);
|
|
9972
|
-
const endLine = integer2(input.endLine);
|
|
9973
|
-
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
9974
|
-
}
|
|
9975
|
-
if (request3.tool === "sandbox.list") {
|
|
9976
|
-
const scope = text3(input.prefix, 1024);
|
|
9977
|
-
return { kind: "list", ...scope ? { scope } : {} };
|
|
9978
|
-
}
|
|
9979
|
-
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
9980
|
-
const query = text3(input.query, 512);
|
|
9981
|
-
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
9982
|
-
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
9983
|
-
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
9984
|
-
}
|
|
9985
|
-
if (request3.tool === "sandbox.apply_patch") {
|
|
9986
|
-
return { kind: "patch", ...patchStats(input.patch) };
|
|
9987
|
-
}
|
|
9988
|
-
const recipeId = text3(input.recipeId, 120);
|
|
9989
|
-
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
9990
|
-
}
|
|
9991
|
-
function codeToolResultPresentation(request3, response2) {
|
|
9992
|
-
const started = codeToolRequestPresentation(request3);
|
|
9993
|
-
if (!started || !response2.ok) return started;
|
|
9994
|
-
const details = record32(response2.details);
|
|
9995
|
-
if (started.kind === "read") {
|
|
9996
|
-
return {
|
|
9997
|
-
...started,
|
|
9998
|
-
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
9999
|
-
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
10000
|
-
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
10001
|
-
};
|
|
10002
|
-
}
|
|
10003
|
-
if (started.kind === "list") {
|
|
10004
|
-
const listed = response2.content.split("\n").filter((line2) => line2 && !line2.startsWith("\u2026") && !line2.startsWith("Workspace ")).map((line2) => text3(line2, 1024)).filter((line2) => Boolean(line2)).slice(0, 8);
|
|
10005
|
-
return {
|
|
10006
|
-
...started,
|
|
10007
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
10008
|
-
...listed.length ? { paths: listed } : {}
|
|
10009
|
-
};
|
|
10010
|
-
}
|
|
10011
|
-
if (started.kind === "query") {
|
|
10012
|
-
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
10013
|
-
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
10014
|
-
return {
|
|
10015
|
-
...started,
|
|
10016
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
10017
|
-
...results ? { results } : {},
|
|
10018
|
-
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
10019
|
-
};
|
|
10020
|
-
}
|
|
10021
|
-
if (started.kind === "patch") {
|
|
10022
|
-
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
10023
|
-
}
|
|
10024
|
-
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
10025
|
-
return {
|
|
10026
|
-
...started,
|
|
10027
|
-
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
10028
|
-
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
10029
|
-
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
10030
|
-
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
10031
|
-
};
|
|
10032
|
-
}
|
|
10033
10142
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
10034
10143
|
let settle;
|
|
10035
10144
|
let settled = false;
|
|
@@ -10047,6 +10156,55 @@ function codeRuntimeAcknowledgementGate(signal) {
|
|
|
10047
10156
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
10048
10157
|
return { ready, release };
|
|
10049
10158
|
}
|
|
10159
|
+
function observeCodeRuntimeSessionSkills(command, skills, emit4) {
|
|
10160
|
+
return skills.map((skill) => ({
|
|
10161
|
+
...skill,
|
|
10162
|
+
tools: skill.tools.map((tool) => {
|
|
10163
|
+
if (!tool.handler) return tool;
|
|
10164
|
+
const handler = tool.handler;
|
|
10165
|
+
return {
|
|
10166
|
+
...tool,
|
|
10167
|
+
handler: async (input, context) => {
|
|
10168
|
+
const startedAt = Date.now();
|
|
10169
|
+
const operationId = digestRuntimeValue(
|
|
10170
|
+
`${command.commandId}:${skill.name}:${tool.name}:${context.toolCallId ?? "missing"}`
|
|
10171
|
+
);
|
|
10172
|
+
await emit4({
|
|
10173
|
+
type: "collaboration",
|
|
10174
|
+
phase: "started",
|
|
10175
|
+
skill: skill.name,
|
|
10176
|
+
tool: tool.name,
|
|
10177
|
+
operationId
|
|
10178
|
+
}).catch(() => void 0);
|
|
10179
|
+
try {
|
|
10180
|
+
const output = await handler(input, context);
|
|
10181
|
+
await emit4({
|
|
10182
|
+
type: "collaboration",
|
|
10183
|
+
phase: "completed",
|
|
10184
|
+
skill: skill.name,
|
|
10185
|
+
tool: tool.name,
|
|
10186
|
+
operationId,
|
|
10187
|
+
ok: output.isError !== true,
|
|
10188
|
+
durationMs: Date.now() - startedAt
|
|
10189
|
+
}).catch(() => void 0);
|
|
10190
|
+
return output;
|
|
10191
|
+
} catch (cause) {
|
|
10192
|
+
await emit4({
|
|
10193
|
+
type: "collaboration",
|
|
10194
|
+
phase: "completed",
|
|
10195
|
+
skill: skill.name,
|
|
10196
|
+
tool: tool.name,
|
|
10197
|
+
operationId,
|
|
10198
|
+
ok: false,
|
|
10199
|
+
durationMs: Date.now() - startedAt
|
|
10200
|
+
}).catch(() => void 0);
|
|
10201
|
+
throw cause;
|
|
10202
|
+
}
|
|
10203
|
+
}
|
|
10204
|
+
};
|
|
10205
|
+
})
|
|
10206
|
+
}));
|
|
10207
|
+
}
|
|
10050
10208
|
var TheseusRuntimeEngine = class {
|
|
10051
10209
|
constructor(options) {
|
|
10052
10210
|
this.options = options;
|
|
@@ -10243,7 +10401,7 @@ var TheseusRuntimeEngine = class {
|
|
|
10243
10401
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
10244
10402
|
});
|
|
10245
10403
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
10246
|
-
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
10404
|
+
const extraSkills = observeCodeRuntimeSessionSkills(command, await sessionSkillsFor(this.options, command), (event) => this.#event(command, event, active.conversationRefs));
|
|
10247
10405
|
const result = await this.#attempt({
|
|
10248
10406
|
inference,
|
|
10249
10407
|
broker,
|
|
@@ -10282,36 +10440,11 @@ var TheseusRuntimeEngine = class {
|
|
|
10282
10440
|
}
|
|
10283
10441
|
/** Report every brokered effect as it starts and finishes. */
|
|
10284
10442
|
#observed(command, active, broker) {
|
|
10285
|
-
return {
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
await this.#event(
|
|
10291
|
-
command,
|
|
10292
|
-
{
|
|
10293
|
-
type: "tool",
|
|
10294
|
-
phase: "started",
|
|
10295
|
-
tool: request3.tool,
|
|
10296
|
-
operationId,
|
|
10297
|
-
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
10298
|
-
},
|
|
10299
|
-
active.conversationRefs
|
|
10300
|
-
).catch(() => void 0);
|
|
10301
|
-
const response2 = await broker.execute(context, request3);
|
|
10302
|
-
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
10303
|
-
await this.#event(command, {
|
|
10304
|
-
type: "tool",
|
|
10305
|
-
phase: "completed",
|
|
10306
|
-
tool: request3.tool,
|
|
10307
|
-
ok: response2.ok,
|
|
10308
|
-
durationMs: Date.now() - startedAt,
|
|
10309
|
-
operationId,
|
|
10310
|
-
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
10311
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
10312
|
-
return response2;
|
|
10313
|
-
}
|
|
10314
|
-
};
|
|
10443
|
+
return observedBroker({
|
|
10444
|
+
broker,
|
|
10445
|
+
operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
|
|
10446
|
+
emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
|
|
10447
|
+
});
|
|
10315
10448
|
}
|
|
10316
10449
|
async #checkpoint(command) {
|
|
10317
10450
|
const active = this.#active.get(command.sessionId);
|
|
@@ -10621,6 +10754,19 @@ function digestText(value2) {
|
|
|
10621
10754
|
// src/code-runtime-config.ts
|
|
10622
10755
|
var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
|
|
10623
10756
|
var CODE_BUILD_RECIPES = Object.freeze([{
|
|
10757
|
+
id: "odla-code-gates",
|
|
10758
|
+
image: CODE_NODE_IMAGE,
|
|
10759
|
+
// Selects the repository's fast, no-capability gates from the same manifest
|
|
10760
|
+
// CI and `npm run preflight` read, so a new gate is picked up without a
|
|
10761
|
+
// second list to keep in sync. The container has no shell: this is argv.
|
|
10762
|
+
command: ["node", "scripts/recipe-gates.mjs"],
|
|
10763
|
+
timeoutMs: 18e4,
|
|
10764
|
+
maxOutputBytes: 1024 * 1024,
|
|
10765
|
+
cpus: 1,
|
|
10766
|
+
// check:secrets holds ~2,500 source files in memory at once.
|
|
10767
|
+
memory: "1g",
|
|
10768
|
+
pids: 128
|
|
10769
|
+
}, {
|
|
10624
10770
|
id: "odla-code-contracts",
|
|
10625
10771
|
image: CODE_NODE_IMAGE,
|
|
10626
10772
|
command: [
|
|
@@ -10800,20 +10946,20 @@ async function runCodeRuntime(input) {
|
|
|
10800
10946
|
}
|
|
10801
10947
|
}
|
|
10802
10948
|
function parseConnection(value2, appId, appEnv) {
|
|
10803
|
-
const root =
|
|
10804
|
-
const host =
|
|
10805
|
-
const offer =
|
|
10806
|
-
const binding =
|
|
10949
|
+
const root = record7(value2);
|
|
10950
|
+
const host = record7(root?.host);
|
|
10951
|
+
const offer = record7(root?.offer);
|
|
10952
|
+
const binding = record7(root?.binding);
|
|
10807
10953
|
if (!root || typeof root.token !== "string" || !/^odla_code_host_[0-9a-f]{64}$/.test(root.token) || typeof root.resumed !== "boolean" || !host || !/^chost_[0-9a-f]{32}$/.test(String(host.hostId)) || typeof host.name !== "string" || !offer || !Number.isSafeInteger(offer.slots) || !binding || typeof binding.appId !== "string" || !binding.appId || appId && binding.appId !== appId || binding.env !== appEnv || !Number.isSafeInteger(binding.generation)) {
|
|
10808
10954
|
throw new Error("connect Code host returned an invalid response");
|
|
10809
10955
|
}
|
|
10810
10956
|
return root;
|
|
10811
10957
|
}
|
|
10812
10958
|
function apiFailure(action2, status, value2) {
|
|
10813
|
-
const message2 =
|
|
10959
|
+
const message2 = record7(record7(value2)?.error)?.message;
|
|
10814
10960
|
return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
|
|
10815
10961
|
}
|
|
10816
|
-
function
|
|
10962
|
+
function record7(value2) {
|
|
10817
10963
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10818
10964
|
}
|
|
10819
10965
|
|
|
@@ -11319,6 +11465,14 @@ Usage:
|
|
|
11319
11465
|
odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
|
|
11320
11466
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
11321
11467
|
odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
|
|
11468
|
+
odla-ai promotion plan --file <promotion-plan.json> [--env dev] [--json]
|
|
11469
|
+
odla-ai promotion inspect <plan-id> [--env dev] [--json]
|
|
11470
|
+
odla-ai promotion status <plan-id> [--env dev] [--json]
|
|
11471
|
+
odla-ai promotion apply <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
|
|
11472
|
+
odla-ai promotion rollback <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
|
|
11473
|
+
odla-ai promotion next <plan-id> <operation-id> [--env dev] [--json]
|
|
11474
|
+
odla-ai promotion record <plan-id> <operation-id> <step-id> --file <step-receipt.json> [--env dev] [--json]
|
|
11475
|
+
odla-ai promotion cancel <plan-id> <operation-id> [--env dev] [--json]
|
|
11322
11476
|
odla-ai operations get <operation-id> [--json]
|
|
11323
11477
|
odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
|
|
11324
11478
|
odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
|
|
@@ -11485,6 +11639,11 @@ Commands:
|
|
|
11485
11639
|
init Create a generic odla.config.mjs plus starter schema/rules files.
|
|
11486
11640
|
doctor Validate and summarize the project config without network calls.
|
|
11487
11641
|
config Diff Registry intent, freeze a CAS-bound plan, and conditionally apply its safe actions.
|
|
11642
|
+
promotion Plan and inspect exact selective-release bundles without target
|
|
11643
|
+
mutation, then queue and resume a human-approved apply or rollback
|
|
11644
|
+
from an enrolled device. Each target step is leased and receipt-
|
|
11645
|
+
journaled; approval remains bound to the immutable plan digest,
|
|
11646
|
+
target set, action, and expiry, so every mismatch fails closed.
|
|
11488
11647
|
operations Inspect or wait on one exact, durable config-operation receipt.
|
|
11489
11648
|
calendar Inspect, connect, or disconnect the live Google booking connection.
|
|
11490
11649
|
app Archive (suspend, data retained), restore, export, import, or
|
|
@@ -12360,17 +12519,17 @@ function collectEntityFields(entity, parsed, allowClear) {
|
|
|
12360
12519
|
if (entity === "task" && fields.column === "ready") fields.column = "todo";
|
|
12361
12520
|
return fields;
|
|
12362
12521
|
}
|
|
12363
|
-
function statusCol(entity,
|
|
12364
|
-
if (entity === "bug") return `${
|
|
12522
|
+
function statusCol(entity, record12) {
|
|
12523
|
+
if (entity === "bug") return `${record12.status ?? ""}/${record12.severity ?? ""}`;
|
|
12365
12524
|
if (entity === "task") {
|
|
12366
|
-
const state2 =
|
|
12367
|
-
return
|
|
12525
|
+
const state2 = record12.column === "todo" ? "ready" : String(record12.column ?? "");
|
|
12526
|
+
return record12.revision ? `${state2}; r${record12.revision}` : state2;
|
|
12368
12527
|
}
|
|
12369
|
-
return String(
|
|
12528
|
+
return String(record12.status ?? "");
|
|
12370
12529
|
}
|
|
12371
|
-
function referenceMarkup(entity,
|
|
12372
|
-
const label = (
|
|
12373
|
-
return `@[${label}](pm:${entity}/${
|
|
12530
|
+
function referenceMarkup(entity, record12) {
|
|
12531
|
+
const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
|
|
12532
|
+
return `@[${label}](pm:${entity}/${record12.id})`;
|
|
12374
12533
|
}
|
|
12375
12534
|
var STUDIO_SECTION = {
|
|
12376
12535
|
goal: "goals",
|
|
@@ -12384,13 +12543,13 @@ function studioRecordUrl(ctx, entity, id2) {
|
|
|
12384
12543
|
ctx.platformUrl
|
|
12385
12544
|
).href;
|
|
12386
12545
|
}
|
|
12387
|
-
function studioRecordLink(ctx, entity,
|
|
12388
|
-
const label = (
|
|
12389
|
-
return `[${label}](${studioRecordUrl(ctx, entity,
|
|
12546
|
+
function studioRecordLink(ctx, entity, record12) {
|
|
12547
|
+
const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
|
|
12548
|
+
return `[${label}](${studioRecordUrl(ctx, entity, record12.id)})`;
|
|
12390
12549
|
}
|
|
12391
|
-
function printRecord(ctx, entity,
|
|
12550
|
+
function printRecord(ctx, entity, record12) {
|
|
12392
12551
|
ctx.out.log(
|
|
12393
|
-
`${
|
|
12552
|
+
`${record12.id} [${statusCol(entity, record12)}] ${record12.appId} ${studioRecordLink(ctx, entity, record12)}`
|
|
12394
12553
|
);
|
|
12395
12554
|
}
|
|
12396
12555
|
function emit2(ctx, value2, human) {
|
|
@@ -12444,8 +12603,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
12444
12603
|
input,
|
|
12445
12604
|
mutationId: writeMutationId2(parsed)
|
|
12446
12605
|
});
|
|
12447
|
-
const
|
|
12448
|
-
emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity,
|
|
12606
|
+
const record12 = { id: res.id, appId, title: String(input.title) };
|
|
12607
|
+
emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record12)}`));
|
|
12449
12608
|
}
|
|
12450
12609
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12451
12610
|
var UUID_PREFIX = /^[0-9a-f-]{8,35}$/i;
|
|
@@ -12465,7 +12624,7 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12465
12624
|
"GET",
|
|
12466
12625
|
`/${entity}?limit=${limit}&offset=${offset}`
|
|
12467
12626
|
);
|
|
12468
|
-
matches.push(...page2.records.map((
|
|
12627
|
+
matches.push(...page2.records.map((record12) => record12.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
|
|
12469
12628
|
offset += page2.records.length;
|
|
12470
12629
|
if (matches.length > 1 || page2.records.length === 0 || offset >= page2.total) break;
|
|
12471
12630
|
}
|
|
@@ -12479,17 +12638,17 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12479
12638
|
}
|
|
12480
12639
|
async function pmGet(ctx, entity, id2) {
|
|
12481
12640
|
const resolved = await resolvePmId(ctx, entity, id2);
|
|
12482
|
-
const { record:
|
|
12483
|
-
emit2(ctx,
|
|
12641
|
+
const { record: record12 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
|
|
12642
|
+
emit2(ctx, record12, () => printRecord(ctx, entity, record12));
|
|
12484
12643
|
}
|
|
12485
12644
|
async function pmReference(ctx, entity, id2) {
|
|
12486
|
-
const { record:
|
|
12645
|
+
const { record: record12 } = await pmRequest(
|
|
12487
12646
|
ctx,
|
|
12488
12647
|
"GET",
|
|
12489
12648
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12490
12649
|
);
|
|
12491
|
-
const markup = referenceMarkup(entity,
|
|
12492
|
-
emit2(ctx, { kind: `pm:${entity}`, id:
|
|
12650
|
+
const markup = referenceMarkup(entity, record12);
|
|
12651
|
+
emit2(ctx, { kind: `pm:${entity}`, id: record12.id, label: record12.title ?? "", markup }, () => {
|
|
12493
12652
|
ctx.out.log(markup);
|
|
12494
12653
|
});
|
|
12495
12654
|
}
|
|
@@ -12574,16 +12733,16 @@ var PER_ENTITY = {
|
|
|
12574
12733
|
decision: ["status", "body", "supersedesId"],
|
|
12575
12734
|
bug: ["status", "severity", "description", "goalId", "assigneeId", "decisionId"]
|
|
12576
12735
|
};
|
|
12577
|
-
function leanRecord(entity,
|
|
12736
|
+
function leanRecord(entity, record12) {
|
|
12578
12737
|
const out = {};
|
|
12579
12738
|
for (const key of [...COMMON, ...PER_ENTITY[entity]]) {
|
|
12580
|
-
const value2 =
|
|
12739
|
+
const value2 = record12[key];
|
|
12581
12740
|
if (value2 !== void 0 && value2 !== null) out[key] = value2;
|
|
12582
12741
|
}
|
|
12583
12742
|
return out;
|
|
12584
12743
|
}
|
|
12585
12744
|
function leanRecords(entity, records, verbose) {
|
|
12586
|
-
return verbose ? records : records.map((
|
|
12745
|
+
return verbose ? records : records.map((record12) => leanRecord(entity, record12));
|
|
12587
12746
|
}
|
|
12588
12747
|
|
|
12589
12748
|
// src/pm-intake.ts
|
|
@@ -12616,16 +12775,18 @@ async function pmNext(ctx, parsed) {
|
|
|
12616
12775
|
// One request for both live columns; the split below is over open work only.
|
|
12617
12776
|
listFiltered(ctx, "task", appId, projectId, { column: `${READY_COLUMN},doing` })
|
|
12618
12777
|
]);
|
|
12619
|
-
const ready = tasks.filter((
|
|
12620
|
-
const doing = tasks.filter((
|
|
12778
|
+
const ready = tasks.filter((record12) => record12.column === READY_COLUMN);
|
|
12779
|
+
const doing = tasks.filter((record12) => record12.column === "doing");
|
|
12621
12780
|
const guidance = !goals.length ? "discuss alignment with the user before creating or claiming project work" : !ready.length ? 'refine a linked Backlog task and mark it Ready with "pm task ready <id>"' : `claim the top Ready task in one call: odla-ai pm start --app ${appId}`;
|
|
12781
|
+
const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
|
|
12622
12782
|
const result = {
|
|
12623
12783
|
appId,
|
|
12624
12784
|
projectId,
|
|
12625
12785
|
openGoals: leanRecords("goal", goals, verbose),
|
|
12626
12786
|
doing: leanRecords("task", doing, verbose),
|
|
12627
12787
|
ready: leanRecords("task", ready, verbose),
|
|
12628
|
-
next: guidance
|
|
12788
|
+
next: guidance,
|
|
12789
|
+
monitor
|
|
12629
12790
|
};
|
|
12630
12791
|
emit2(ctx, result, () => {
|
|
12631
12792
|
ctx.out.log(`${appId}: goal-aligned work intake (read only)`);
|
|
@@ -12636,9 +12797,10 @@ async function pmNext(ctx, parsed) {
|
|
|
12636
12797
|
]) {
|
|
12637
12798
|
ctx.out.log(`${label}:`);
|
|
12638
12799
|
if (!records.length) ctx.out.log("- (none)");
|
|
12639
|
-
else for (const
|
|
12800
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12640
12801
|
}
|
|
12641
12802
|
ctx.out.log(`next: ${guidance}`);
|
|
12803
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12642
12804
|
});
|
|
12643
12805
|
}
|
|
12644
12806
|
async function pmHandoff(ctx, parsed) {
|
|
@@ -12650,17 +12812,20 @@ async function pmHandoff(ctx, parsed) {
|
|
|
12650
12812
|
listFiltered(ctx, "bug", appId, projectId, { status: OPEN_BUG_STATUSES })
|
|
12651
12813
|
]);
|
|
12652
12814
|
const clean4 = !goals.length && !tasks.length && !bugs.length;
|
|
12815
|
+
const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
|
|
12653
12816
|
const result = {
|
|
12654
12817
|
appId,
|
|
12655
12818
|
projectId,
|
|
12656
12819
|
unmetGoals: leanRecords("goal", goals, verbose),
|
|
12657
12820
|
activeTasks: leanRecords("task", tasks, verbose),
|
|
12658
12821
|
openBugs: leanRecords("bug", bugs, verbose),
|
|
12659
|
-
clean: clean4
|
|
12822
|
+
clean: clean4,
|
|
12823
|
+
monitor
|
|
12660
12824
|
};
|
|
12661
12825
|
emit2(ctx, result, () => {
|
|
12662
12826
|
if (clean4) {
|
|
12663
12827
|
ctx.out.log(`${appId}: no unresolved PM work`);
|
|
12828
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12664
12829
|
return;
|
|
12665
12830
|
}
|
|
12666
12831
|
ctx.out.log(`${appId}: authoritative PM handoff`);
|
|
@@ -12671,8 +12836,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
12671
12836
|
]) {
|
|
12672
12837
|
ctx.out.log(`${label}:`);
|
|
12673
12838
|
if (!records.length) ctx.out.log("- (none)");
|
|
12674
|
-
else for (const
|
|
12839
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12675
12840
|
}
|
|
12841
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12676
12842
|
});
|
|
12677
12843
|
}
|
|
12678
12844
|
|
|
@@ -12745,14 +12911,14 @@ async function pmStart(ctx, parsed) {
|
|
|
12745
12911
|
|
|
12746
12912
|
// src/pm-links.ts
|
|
12747
12913
|
async function pmLink(ctx, entity, id2) {
|
|
12748
|
-
const { record:
|
|
12914
|
+
const { record: record12 } = await pmRequest(
|
|
12749
12915
|
ctx,
|
|
12750
12916
|
"GET",
|
|
12751
12917
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12752
12918
|
);
|
|
12753
|
-
const url = studioRecordUrl(ctx, entity,
|
|
12754
|
-
const markdown = studioRecordLink(ctx, entity,
|
|
12755
|
-
emit2(ctx, { kind: entity, id:
|
|
12919
|
+
const url = studioRecordUrl(ctx, entity, record12.id);
|
|
12920
|
+
const markdown = studioRecordLink(ctx, entity, record12);
|
|
12921
|
+
emit2(ctx, { kind: entity, id: record12.id, label: record12.title ?? "", url, markdown }, () => {
|
|
12756
12922
|
ctx.out.log(markdown);
|
|
12757
12923
|
});
|
|
12758
12924
|
}
|
|
@@ -12879,16 +13045,16 @@ async function page(ctx, appId, cursor) {
|
|
|
12879
13045
|
}
|
|
12880
13046
|
return data;
|
|
12881
13047
|
}
|
|
12882
|
-
function recordState(
|
|
12883
|
-
if (
|
|
12884
|
-
return String(
|
|
13048
|
+
function recordState(record12) {
|
|
13049
|
+
if (record12.column) return record12.column === "todo" ? "ready" : record12.column;
|
|
13050
|
+
return String(record12.status ?? "");
|
|
12885
13051
|
}
|
|
12886
13052
|
function eventRecord(event) {
|
|
12887
13053
|
return event.payload.payload;
|
|
12888
13054
|
}
|
|
12889
13055
|
function eventLabel(event) {
|
|
12890
|
-
const
|
|
12891
|
-
if (
|
|
13056
|
+
const record12 = eventRecord(event);
|
|
13057
|
+
if (record12) return String(record12.title ?? event.payload.entityId);
|
|
12892
13058
|
const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
|
|
12893
13059
|
return body || event.payload.entityId;
|
|
12894
13060
|
}
|
|
@@ -12896,10 +13062,10 @@ function report3(ctx, parsed, result) {
|
|
|
12896
13062
|
if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
|
|
12897
13063
|
else if (parsed.options.jsonl !== true && result.found) {
|
|
12898
13064
|
for (const event of result.events ?? []) {
|
|
12899
|
-
const
|
|
12900
|
-
const state2 =
|
|
13065
|
+
const record12 = eventRecord(event);
|
|
13066
|
+
const state2 = record12 ? recordState(record12) : "comment";
|
|
12901
13067
|
ctx.out.log(
|
|
12902
|
-
`${event.id} ${event.type} ${state2}${
|
|
13068
|
+
`${event.id} ${event.type} ${state2}${record12?.revision ? `; r${record12.revision}` : ""} ${eventLabel(event)}`
|
|
12903
13069
|
);
|
|
12904
13070
|
}
|
|
12905
13071
|
}
|
|
@@ -12973,8 +13139,8 @@ async function pmWatch(ctx, parsed) {
|
|
|
12973
13139
|
}
|
|
12974
13140
|
firstSuccess = false;
|
|
12975
13141
|
const matching = current.events.filter((event) => {
|
|
12976
|
-
const
|
|
12977
|
-
const state2 =
|
|
13142
|
+
const record12 = eventRecord(event);
|
|
13143
|
+
const state2 = record12 ? recordState(record12).toLowerCase() : "";
|
|
12978
13144
|
return (!entity || event.payload.entityKind === entity) && (!action2 || event.payload.action === action2) && (!wantedState || state2 === wantedState || wantedState === "todo" && state2 === "ready") && (!by || event.actor.id === by) && (!self || event.actor.id !== self);
|
|
12979
13145
|
});
|
|
12980
13146
|
for (const event of matching) {
|
|
@@ -13405,17 +13571,17 @@ async function platformStatus(parsed, deps) {
|
|
|
13405
13571
|
}
|
|
13406
13572
|
}
|
|
13407
13573
|
function isPlatformStatus(value2) {
|
|
13408
|
-
if (!
|
|
13409
|
-
if (!
|
|
13410
|
-
if (!
|
|
13574
|
+
if (!record8(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
|
|
13575
|
+
if (!record8(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
|
|
13576
|
+
if (!record8(value2.catalog) || !record8(value2.summary)) return false;
|
|
13411
13577
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
13412
13578
|
}
|
|
13413
13579
|
function apiMessage(value2) {
|
|
13414
|
-
if (!
|
|
13415
|
-
const error =
|
|
13580
|
+
if (!record8(value2)) return "request failed";
|
|
13581
|
+
const error = record8(value2.error) ? value2.error : value2;
|
|
13416
13582
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
13417
13583
|
}
|
|
13418
|
-
function
|
|
13584
|
+
function record8(value2) {
|
|
13419
13585
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
13420
13586
|
}
|
|
13421
13587
|
|
|
@@ -13456,7 +13622,7 @@ function statusVerdict(reads) {
|
|
|
13456
13622
|
severity: "degraded"
|
|
13457
13623
|
});
|
|
13458
13624
|
}
|
|
13459
|
-
const performance =
|
|
13625
|
+
const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
13460
13626
|
if (performance?.status === "unavailable") {
|
|
13461
13627
|
reasons.push({
|
|
13462
13628
|
source: "liveSync",
|
|
@@ -13537,7 +13703,7 @@ function statusVerdict(reads) {
|
|
|
13537
13703
|
reasons
|
|
13538
13704
|
};
|
|
13539
13705
|
}
|
|
13540
|
-
function
|
|
13706
|
+
function record9(value2) {
|
|
13541
13707
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13542
13708
|
}
|
|
13543
13709
|
function numeric2(value2) {
|
|
@@ -13565,7 +13731,7 @@ function printO11yStatus(status, out) {
|
|
|
13565
13731
|
out.log(
|
|
13566
13732
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
13567
13733
|
);
|
|
13568
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
13734
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record10) : [];
|
|
13569
13735
|
const requests = routes.reduce(
|
|
13570
13736
|
(total, row) => total + numeric3(row.requests),
|
|
13571
13737
|
0
|
|
@@ -13577,39 +13743,39 @@ function printO11yStatus(status, out) {
|
|
|
13577
13743
|
out.log(
|
|
13578
13744
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
13579
13745
|
);
|
|
13580
|
-
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(
|
|
13746
|
+
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record10) : [];
|
|
13581
13747
|
out.log(
|
|
13582
13748
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
13583
13749
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
13584
13750
|
).join(", ") : "none observed"}`
|
|
13585
13751
|
);
|
|
13586
13752
|
out.log(liveSyncLine(status.liveSync));
|
|
13587
|
-
const canaryDurations =
|
|
13753
|
+
const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
13588
13754
|
out.log(
|
|
13589
13755
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
13590
13756
|
);
|
|
13591
|
-
const collectorIngest =
|
|
13592
|
-
const collectorStorage =
|
|
13757
|
+
const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
13758
|
+
const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
13593
13759
|
out.log(
|
|
13594
13760
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
13595
13761
|
);
|
|
13596
|
-
const providerMetrics =
|
|
13597
|
-
const providerCapacity =
|
|
13598
|
-
const workerMemory =
|
|
13762
|
+
const providerMetrics = record10(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
13763
|
+
const providerCapacity = record10(status.provider.body.capacity) ? status.provider.body.capacity : {};
|
|
13764
|
+
const workerMemory = record10(providerCapacity.memory) ? providerCapacity.memory : {};
|
|
13599
13765
|
out.log(
|
|
13600
13766
|
`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`
|
|
13601
13767
|
);
|
|
13602
13768
|
for (const line2 of providerCapacityLines(status.providerCapacity)) {
|
|
13603
13769
|
out.log(line2);
|
|
13604
13770
|
}
|
|
13605
|
-
const coverage =
|
|
13606
|
-
const coverageCounts =
|
|
13607
|
-
const coverageBudget =
|
|
13771
|
+
const coverage = record10(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
|
|
13772
|
+
const coverageCounts = record10(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
|
|
13773
|
+
const coverageBudget = record10(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
|
|
13608
13774
|
out.log(
|
|
13609
13775
|
`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`
|
|
13610
13776
|
);
|
|
13611
13777
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
13612
|
-
const providerFreshness =
|
|
13778
|
+
const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
13613
13779
|
out.log(
|
|
13614
13780
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
13615
13781
|
);
|
|
@@ -13618,17 +13784,17 @@ function printO11yStatus(status, out) {
|
|
|
13618
13784
|
);
|
|
13619
13785
|
}
|
|
13620
13786
|
function providerCapacityLines(read3) {
|
|
13621
|
-
const resources =
|
|
13622
|
-
const durableObjects =
|
|
13623
|
-
const periodic =
|
|
13624
|
-
const storage =
|
|
13625
|
-
const d1 =
|
|
13626
|
-
const d1Activity =
|
|
13627
|
-
const d1Storage =
|
|
13628
|
-
const d1Latency =
|
|
13629
|
-
const r2 =
|
|
13630
|
-
const r2Operations =
|
|
13631
|
-
const r2Storage =
|
|
13787
|
+
const resources = record10(read3.body.resources) ? read3.body.resources : {};
|
|
13788
|
+
const durableObjects = record10(resources.durableObjects) ? resources.durableObjects : {};
|
|
13789
|
+
const periodic = record10(durableObjects.periodic) ? durableObjects.periodic : {};
|
|
13790
|
+
const storage = record10(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
|
|
13791
|
+
const d1 = record10(resources.d1) ? resources.d1 : {};
|
|
13792
|
+
const d1Activity = record10(d1.activity) ? d1.activity : {};
|
|
13793
|
+
const d1Storage = record10(d1.storage) ? d1.storage : {};
|
|
13794
|
+
const d1Latency = record10(d1Activity.latency) ? d1Activity.latency : {};
|
|
13795
|
+
const r2 = record10(resources.r2) ? resources.r2 : {};
|
|
13796
|
+
const r2Operations = record10(r2.operations) ? r2.operations : {};
|
|
13797
|
+
const r2Storage = record10(r2.storage) ? r2.storage : {};
|
|
13632
13798
|
const status = String(
|
|
13633
13799
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
13634
13800
|
);
|
|
@@ -13639,11 +13805,11 @@ function providerCapacityLines(read3) {
|
|
|
13639
13805
|
];
|
|
13640
13806
|
}
|
|
13641
13807
|
function liveSyncLine(read3) {
|
|
13642
|
-
const performance =
|
|
13643
|
-
const commitToSend =
|
|
13808
|
+
const performance = record10(read3.body.performance) ? read3.body.performance : {};
|
|
13809
|
+
const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
|
|
13644
13810
|
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`;
|
|
13645
13811
|
}
|
|
13646
|
-
function
|
|
13812
|
+
function record10(value2) {
|
|
13647
13813
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13648
13814
|
}
|
|
13649
13815
|
function numeric3(value2) {
|
|
@@ -13981,7 +14147,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
13981
14147
|
if (action2 === "plan" || action2 === "apply") {
|
|
13982
14148
|
const desired = monitoringWireConfig(context.cfg, env);
|
|
13983
14149
|
const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
|
|
13984
|
-
const currentRevision =
|
|
14150
|
+
const currentRevision = record11(live.config) ? string(live.config.revision) : null;
|
|
13985
14151
|
const changed = currentRevision !== desired.revision;
|
|
13986
14152
|
const plan = {
|
|
13987
14153
|
schemaVersion: 1,
|
|
@@ -14026,7 +14192,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
14026
14192
|
headers
|
|
14027
14193
|
}, doFetch);
|
|
14028
14194
|
emit3(result2, jsonOutput, out, () => {
|
|
14029
|
-
const run =
|
|
14195
|
+
const run = record11(result2.run) ? result2.run : {};
|
|
14030
14196
|
out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
|
|
14031
14197
|
});
|
|
14032
14198
|
return;
|
|
@@ -14051,12 +14217,12 @@ async function request2(url, init, doFetch) {
|
|
|
14051
14217
|
let body = {};
|
|
14052
14218
|
try {
|
|
14053
14219
|
const parsed = text4 ? JSON.parse(text4) : {};
|
|
14054
|
-
body =
|
|
14220
|
+
body = record11(parsed) ? parsed : { value: parsed };
|
|
14055
14221
|
} catch {
|
|
14056
14222
|
body = { message: text4.slice(0, 500) };
|
|
14057
14223
|
}
|
|
14058
14224
|
if (!response2.ok) {
|
|
14059
|
-
const error =
|
|
14225
|
+
const error = record11(body.error) ? body.error : body;
|
|
14060
14226
|
throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
|
|
14061
14227
|
}
|
|
14062
14228
|
return body;
|
|
@@ -14064,7 +14230,7 @@ async function request2(url, init, doFetch) {
|
|
|
14064
14230
|
function printRead(action2, appId, env, result, out) {
|
|
14065
14231
|
if (action2 === "status") {
|
|
14066
14232
|
out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
|
|
14067
|
-
const slos = Array.isArray(result.slos) ? result.slos.filter(
|
|
14233
|
+
const slos = Array.isArray(result.slos) ? result.slos.filter(record11) : [];
|
|
14068
14234
|
for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
|
|
14069
14235
|
const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
|
|
14070
14236
|
const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
|
|
@@ -14073,20 +14239,20 @@ function printRead(action2, appId, env, result, out) {
|
|
|
14073
14239
|
return;
|
|
14074
14240
|
}
|
|
14075
14241
|
if (action2 === "incidents") {
|
|
14076
|
-
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(
|
|
14242
|
+
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record11) : [];
|
|
14077
14243
|
out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
|
|
14078
14244
|
for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
|
|
14079
14245
|
return;
|
|
14080
14246
|
}
|
|
14081
14247
|
out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
|
|
14082
|
-
const probes = Array.isArray(result.probes) ? result.probes.filter(
|
|
14248
|
+
const probes = Array.isArray(result.probes) ? result.probes.filter(record11) : [];
|
|
14083
14249
|
for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
|
|
14084
14250
|
}
|
|
14085
14251
|
function emit3(value2, json, out, human) {
|
|
14086
14252
|
if (json) out.log(JSON.stringify(value2, null, 2));
|
|
14087
14253
|
else human();
|
|
14088
14254
|
}
|
|
14089
|
-
function
|
|
14255
|
+
function record11(value2) {
|
|
14090
14256
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
14091
14257
|
}
|
|
14092
14258
|
function string(value2) {
|
|
@@ -14577,6 +14743,122 @@ async function provision(options) {
|
|
|
14577
14743
|
}
|
|
14578
14744
|
}
|
|
14579
14745
|
|
|
14746
|
+
// src/promotion-command.ts
|
|
14747
|
+
var import_promises12 = require("fs/promises");
|
|
14748
|
+
var import_apps14 = require("@odla-ai/apps");
|
|
14749
|
+
|
|
14750
|
+
// src/security-command-context.ts
|
|
14751
|
+
async function hostedSecurityContext(parsed, dependencies) {
|
|
14752
|
+
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
14753
|
+
const cfg = await loadProjectConfig(configPath);
|
|
14754
|
+
const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
14755
|
+
if (!env || !cfg.envs.includes(env)) {
|
|
14756
|
+
throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
|
|
14757
|
+
}
|
|
14758
|
+
const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
|
|
14759
|
+
if (platformAudience(cfg.platformUrl) !== platform) {
|
|
14760
|
+
throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
|
|
14761
|
+
}
|
|
14762
|
+
const doFetch = dependencies.fetch ?? fetch;
|
|
14763
|
+
const stdout = dependencies.stdout ?? console;
|
|
14764
|
+
const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
|
|
14765
|
+
const token = await getDeveloperToken(
|
|
14766
|
+
cfg,
|
|
14767
|
+
{ configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
|
|
14768
|
+
doFetch,
|
|
14769
|
+
stdout,
|
|
14770
|
+
{ optionalProjectCapabilities: ["app.manage"] }
|
|
14771
|
+
);
|
|
14772
|
+
return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
|
|
14773
|
+
}
|
|
14774
|
+
function requiredSecurityPositional(parsed, index, label) {
|
|
14775
|
+
const value2 = parsed.positionals[index];
|
|
14776
|
+
if (!value2) throw new Error(`${label} is required`);
|
|
14777
|
+
return value2;
|
|
14778
|
+
}
|
|
14779
|
+
function securityProfile(value2) {
|
|
14780
|
+
if (value2 === void 0) return void 0;
|
|
14781
|
+
if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
|
|
14782
|
+
throw new Error("--profile must be odla, cloudflare-app, or generic");
|
|
14783
|
+
}
|
|
14784
|
+
|
|
14785
|
+
// src/promotion-command.ts
|
|
14786
|
+
var render = (value2, json, out) => {
|
|
14787
|
+
if (json) out.log(JSON.stringify(value2));
|
|
14788
|
+
else out.log(JSON.stringify(value2, null, 2));
|
|
14789
|
+
};
|
|
14790
|
+
async function promotionCommand(parsed, dependencies) {
|
|
14791
|
+
const action2 = parsed.positionals[1];
|
|
14792
|
+
if (!action2 || !["plan", "inspect", "status", "apply", "rollback", "next", "record", "cancel"].includes(action2)) rejectWord(["promotion"], action2);
|
|
14793
|
+
assertArgs(parsed, ["config", "env", "platform", "email", "open", "json", "file", "approval", "idempotency-key"], 5);
|
|
14794
|
+
const context = await hostedSecurityContext(parsed, dependencies);
|
|
14795
|
+
const client = (0, import_apps14.createAppsClient)({
|
|
14796
|
+
token: context.token,
|
|
14797
|
+
fetcher: { fetch: (request3, init) => {
|
|
14798
|
+
const internal = new URL(typeof request3 === "string" ? request3 : request3.url);
|
|
14799
|
+
return context.fetch(`${context.platform}${internal.pathname}${internal.search}`, init);
|
|
14800
|
+
} }
|
|
14801
|
+
});
|
|
14802
|
+
const json = parsed.options.json === true;
|
|
14803
|
+
if (action2 === "plan") {
|
|
14804
|
+
if (parsed.positionals[2]) throw new Error("promotion plan takes --file, not a positional plan id");
|
|
14805
|
+
const file = stringOpt(parsed.options.file);
|
|
14806
|
+
if (!file) throw new Error("--file <promotion-plan.json> is required");
|
|
14807
|
+
const source = JSON.parse(await (0, import_promises12.readFile)(file, "utf8"));
|
|
14808
|
+
const plan2 = await client.createPromotionReleasePlan(context.appId, source);
|
|
14809
|
+
render(plan2, json, context.stdout);
|
|
14810
|
+
return;
|
|
14811
|
+
}
|
|
14812
|
+
const planId = requiredSecurityPositional(parsed, 2, "plan id");
|
|
14813
|
+
if (action2 === "inspect") {
|
|
14814
|
+
if (parsed.positionals[3]) throw new Error("promotion inspect takes only a plan id");
|
|
14815
|
+
const plan2 = await client.getPromotionReleasePlan(context.appId, planId);
|
|
14816
|
+
if (!plan2) throw new Error(`promotion release ${planId} was not found`);
|
|
14817
|
+
render(plan2, json, context.stdout);
|
|
14818
|
+
return;
|
|
14819
|
+
}
|
|
14820
|
+
if (action2 === "status") {
|
|
14821
|
+
if (parsed.positionals[3]) throw new Error("promotion status takes only a plan id");
|
|
14822
|
+
const status = await client.getPromotionReleaseStatus(context.appId, planId);
|
|
14823
|
+
if (!status) throw new Error(`promotion release ${planId} was not found`);
|
|
14824
|
+
render(status, json, context.stdout);
|
|
14825
|
+
return;
|
|
14826
|
+
}
|
|
14827
|
+
if (action2 === "next" || action2 === "cancel" || action2 === "record") {
|
|
14828
|
+
const operationId = requiredSecurityPositional(parsed, 3, "operation id");
|
|
14829
|
+
if (action2 === "next") {
|
|
14830
|
+
if (parsed.positionals[4]) throw new Error("promotion next takes only plan and operation ids");
|
|
14831
|
+
render(await client.claimPromotionReleaseWork(context.appId, planId, operationId), json, context.stdout);
|
|
14832
|
+
return;
|
|
14833
|
+
}
|
|
14834
|
+
if (action2 === "cancel") {
|
|
14835
|
+
if (parsed.positionals[4]) throw new Error("promotion cancel takes only plan and operation ids");
|
|
14836
|
+
render(await client.cancelPromotionReleaseOperation(context.appId, planId, operationId), json, context.stdout);
|
|
14837
|
+
return;
|
|
14838
|
+
}
|
|
14839
|
+
const stepId = requiredSecurityPositional(parsed, 4, "step id");
|
|
14840
|
+
const file = stringOpt(parsed.options.file);
|
|
14841
|
+
if (!file) throw new Error("--file <step-receipt.json> is required");
|
|
14842
|
+
const receipt = JSON.parse(await (0, import_promises12.readFile)(file, "utf8"));
|
|
14843
|
+
render(await client.recordPromotionReleaseWork(context.appId, planId, operationId, stepId, receipt), json, context.stdout);
|
|
14844
|
+
return;
|
|
14845
|
+
}
|
|
14846
|
+
const approvalId = stringOpt(parsed.options.approval);
|
|
14847
|
+
if (parsed.positionals[3]) throw new Error(`promotion ${action2} takes only a plan id`);
|
|
14848
|
+
if (!approvalId) throw new Error(`--approval <approval-id> is required for promotion ${action2}`);
|
|
14849
|
+
const plan = await client.getPromotionReleasePlan(context.appId, planId);
|
|
14850
|
+
if (!plan) throw new Error(`promotion release ${planId} was not found`);
|
|
14851
|
+
if (plan.state !== "reviewable") throw new Error(`promotion release is ${plan.state}: ${plan.blockers.join("; ")}`);
|
|
14852
|
+
const operationAction = action2;
|
|
14853
|
+
const operation = await client.requestPromotionReleaseOperation(context.appId, planId, operationAction, {
|
|
14854
|
+
approvalId,
|
|
14855
|
+
planDigest: plan.planDigest,
|
|
14856
|
+
targetSetDigest: plan.targetSetDigest,
|
|
14857
|
+
idempotencyKey: stringOpt(parsed.options["idempotency-key"]) ?? `${operationAction}:${planId}`
|
|
14858
|
+
});
|
|
14859
|
+
render(operation, json, context.stdout);
|
|
14860
|
+
}
|
|
14861
|
+
|
|
14580
14862
|
// src/record.ts
|
|
14581
14863
|
var import_node_fs18 = require("fs");
|
|
14582
14864
|
var import_node_process18 = __toESM(require("process"), 1);
|
|
@@ -14596,12 +14878,12 @@ function recordInvocation(parsed) {
|
|
|
14596
14878
|
}
|
|
14597
14879
|
|
|
14598
14880
|
// src/advisory-output.ts
|
|
14599
|
-
var
|
|
14881
|
+
var import_apps15 = require("@odla-ai/apps");
|
|
14600
14882
|
function advisoryCollectingFetch(inner, sink) {
|
|
14601
14883
|
return (async (input, init) => {
|
|
14602
14884
|
const response2 = await inner(input, init);
|
|
14603
14885
|
try {
|
|
14604
|
-
sink.push(...(0,
|
|
14886
|
+
sink.push(...(0, import_apps15.parseAdvisories)(response2));
|
|
14605
14887
|
} catch {
|
|
14606
14888
|
}
|
|
14607
14889
|
return response2;
|
|
@@ -14621,7 +14903,7 @@ function renderAdvisories(out, advisories, env = process.env) {
|
|
|
14621
14903
|
const key = `${advisory.code}:${advisory.message}`;
|
|
14622
14904
|
if (seen.has(key)) continue;
|
|
14623
14905
|
seen.add(key);
|
|
14624
|
-
out.error((0,
|
|
14906
|
+
out.error((0, import_apps15.formatAdvisory)(advisory));
|
|
14625
14907
|
}
|
|
14626
14908
|
}
|
|
14627
14909
|
|
|
@@ -14908,7 +15190,7 @@ async function bySlug(ctx, slug) {
|
|
|
14908
15190
|
"GET",
|
|
14909
15191
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
14910
15192
|
);
|
|
14911
|
-
const filtered = page2.records.find((
|
|
15193
|
+
const filtered = page2.records.find((record12) => record12.slug === slug);
|
|
14912
15194
|
if (filtered) return filtered;
|
|
14913
15195
|
const limit = 100;
|
|
14914
15196
|
for (let offset = 0; ; offset += limit) {
|
|
@@ -14917,7 +15199,7 @@ async function bySlug(ctx, slug) {
|
|
|
14917
15199
|
"GET",
|
|
14918
15200
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
14919
15201
|
);
|
|
14920
|
-
const found = fallback.records.find((
|
|
15202
|
+
const found = fallback.records.find((record12) => record12.slug === slug);
|
|
14921
15203
|
if (found) return found;
|
|
14922
15204
|
if (!fallback.records.length || offset + fallback.records.length >= fallback.total) break;
|
|
14923
15205
|
}
|
|
@@ -15717,41 +15999,6 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15717
15999
|
}
|
|
15718
16000
|
}
|
|
15719
16001
|
|
|
15720
|
-
// src/security-command-context.ts
|
|
15721
|
-
async function hostedSecurityContext(parsed, dependencies) {
|
|
15722
|
-
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
15723
|
-
const cfg = await loadProjectConfig(configPath);
|
|
15724
|
-
const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
15725
|
-
if (!env || !cfg.envs.includes(env)) {
|
|
15726
|
-
throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
|
|
15727
|
-
}
|
|
15728
|
-
const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
|
|
15729
|
-
if (platformAudience(cfg.platformUrl) !== platform) {
|
|
15730
|
-
throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
|
|
15731
|
-
}
|
|
15732
|
-
const doFetch = dependencies.fetch ?? fetch;
|
|
15733
|
-
const stdout = dependencies.stdout ?? console;
|
|
15734
|
-
const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
|
|
15735
|
-
const token = await getDeveloperToken(
|
|
15736
|
-
cfg,
|
|
15737
|
-
{ configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
|
|
15738
|
-
doFetch,
|
|
15739
|
-
stdout,
|
|
15740
|
-
{ optionalProjectCapabilities: ["app.manage"] }
|
|
15741
|
-
);
|
|
15742
|
-
return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
|
|
15743
|
-
}
|
|
15744
|
-
function requiredSecurityPositional(parsed, index, label) {
|
|
15745
|
-
const value2 = parsed.positionals[index];
|
|
15746
|
-
if (!value2) throw new Error(`${label} is required`);
|
|
15747
|
-
return value2;
|
|
15748
|
-
}
|
|
15749
|
-
function securityProfile(value2) {
|
|
15750
|
-
if (value2 === void 0) return void 0;
|
|
15751
|
-
if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
|
|
15752
|
-
throw new Error("--profile must be odla, cloudflare-app, or generic");
|
|
15753
|
-
}
|
|
15754
|
-
|
|
15755
16002
|
// src/security-command-output.ts
|
|
15756
16003
|
function printHostedSecurityPlan(out, plan, appId) {
|
|
15757
16004
|
out.log(`Hosted security plan for ${appId}/${plan.env}: ${plan.ready ? "ready" : "not ready"}`);
|
|
@@ -16574,6 +16821,10 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16574
16821
|
await securityCommand(parsed, runtime);
|
|
16575
16822
|
return;
|
|
16576
16823
|
}
|
|
16824
|
+
if (command === "promotion") {
|
|
16825
|
+
await promotionCommand(parsed, runtime);
|
|
16826
|
+
return;
|
|
16827
|
+
}
|
|
16577
16828
|
if (command === "brand") {
|
|
16578
16829
|
await brandCommand(parsed, runtime);
|
|
16579
16830
|
return;
|