@odla-ai/cli 0.46.12 → 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 +556 -344
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-Q4BGAUEH.js → chunk-BD5KIG63.js} +519 -324
- package/dist/chunk-BD5KIG63.js.map +1 -0
- package/dist/{cli-5CDFI4AY.js → cli-MXYYOFBR.js} +2 -2
- package/dist/index.cjs +521 -326
- 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-Q4BGAUEH.js.map +0 -1
- /package/dist/{cli-5CDFI4AY.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;
|
|
@@ -10331,36 +10440,11 @@ var TheseusRuntimeEngine = class {
|
|
|
10331
10440
|
}
|
|
10332
10441
|
/** Report every brokered effect as it starts and finishes. */
|
|
10333
10442
|
#observed(command, active, broker) {
|
|
10334
|
-
return {
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
await this.#event(
|
|
10340
|
-
command,
|
|
10341
|
-
{
|
|
10342
|
-
type: "tool",
|
|
10343
|
-
phase: "started",
|
|
10344
|
-
tool: request3.tool,
|
|
10345
|
-
operationId,
|
|
10346
|
-
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
10347
|
-
},
|
|
10348
|
-
active.conversationRefs
|
|
10349
|
-
).catch(() => void 0);
|
|
10350
|
-
const response2 = await broker.execute(context, request3);
|
|
10351
|
-
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
10352
|
-
await this.#event(command, {
|
|
10353
|
-
type: "tool",
|
|
10354
|
-
phase: "completed",
|
|
10355
|
-
tool: request3.tool,
|
|
10356
|
-
ok: response2.ok,
|
|
10357
|
-
durationMs: Date.now() - startedAt,
|
|
10358
|
-
operationId,
|
|
10359
|
-
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
10360
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
10361
|
-
return response2;
|
|
10362
|
-
}
|
|
10363
|
-
};
|
|
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
|
+
});
|
|
10364
10448
|
}
|
|
10365
10449
|
async #checkpoint(command) {
|
|
10366
10450
|
const active = this.#active.get(command.sessionId);
|
|
@@ -10670,6 +10754,19 @@ function digestText(value2) {
|
|
|
10670
10754
|
// src/code-runtime-config.ts
|
|
10671
10755
|
var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
|
|
10672
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
|
+
}, {
|
|
10673
10770
|
id: "odla-code-contracts",
|
|
10674
10771
|
image: CODE_NODE_IMAGE,
|
|
10675
10772
|
command: [
|
|
@@ -10849,20 +10946,20 @@ async function runCodeRuntime(input) {
|
|
|
10849
10946
|
}
|
|
10850
10947
|
}
|
|
10851
10948
|
function parseConnection(value2, appId, appEnv) {
|
|
10852
|
-
const root =
|
|
10853
|
-
const host =
|
|
10854
|
-
const offer =
|
|
10855
|
-
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);
|
|
10856
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)) {
|
|
10857
10954
|
throw new Error("connect Code host returned an invalid response");
|
|
10858
10955
|
}
|
|
10859
10956
|
return root;
|
|
10860
10957
|
}
|
|
10861
10958
|
function apiFailure(action2, status, value2) {
|
|
10862
|
-
const message2 =
|
|
10959
|
+
const message2 = record7(record7(value2)?.error)?.message;
|
|
10863
10960
|
return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
|
|
10864
10961
|
}
|
|
10865
|
-
function
|
|
10962
|
+
function record7(value2) {
|
|
10866
10963
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10867
10964
|
}
|
|
10868
10965
|
|
|
@@ -11368,6 +11465,14 @@ Usage:
|
|
|
11368
11465
|
odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
|
|
11369
11466
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
11370
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]
|
|
11371
11476
|
odla-ai operations get <operation-id> [--json]
|
|
11372
11477
|
odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
|
|
11373
11478
|
odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
|
|
@@ -11534,6 +11639,11 @@ Commands:
|
|
|
11534
11639
|
init Create a generic odla.config.mjs plus starter schema/rules files.
|
|
11535
11640
|
doctor Validate and summarize the project config without network calls.
|
|
11536
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.
|
|
11537
11647
|
operations Inspect or wait on one exact, durable config-operation receipt.
|
|
11538
11648
|
calendar Inspect, connect, or disconnect the live Google booking connection.
|
|
11539
11649
|
app Archive (suspend, data retained), restore, export, import, or
|
|
@@ -12409,17 +12519,17 @@ function collectEntityFields(entity, parsed, allowClear) {
|
|
|
12409
12519
|
if (entity === "task" && fields.column === "ready") fields.column = "todo";
|
|
12410
12520
|
return fields;
|
|
12411
12521
|
}
|
|
12412
|
-
function statusCol(entity,
|
|
12413
|
-
if (entity === "bug") return `${
|
|
12522
|
+
function statusCol(entity, record12) {
|
|
12523
|
+
if (entity === "bug") return `${record12.status ?? ""}/${record12.severity ?? ""}`;
|
|
12414
12524
|
if (entity === "task") {
|
|
12415
|
-
const state2 =
|
|
12416
|
-
return
|
|
12525
|
+
const state2 = record12.column === "todo" ? "ready" : String(record12.column ?? "");
|
|
12526
|
+
return record12.revision ? `${state2}; r${record12.revision}` : state2;
|
|
12417
12527
|
}
|
|
12418
|
-
return String(
|
|
12528
|
+
return String(record12.status ?? "");
|
|
12419
12529
|
}
|
|
12420
|
-
function referenceMarkup(entity,
|
|
12421
|
-
const label = (
|
|
12422
|
-
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})`;
|
|
12423
12533
|
}
|
|
12424
12534
|
var STUDIO_SECTION = {
|
|
12425
12535
|
goal: "goals",
|
|
@@ -12433,13 +12543,13 @@ function studioRecordUrl(ctx, entity, id2) {
|
|
|
12433
12543
|
ctx.platformUrl
|
|
12434
12544
|
).href;
|
|
12435
12545
|
}
|
|
12436
|
-
function studioRecordLink(ctx, entity,
|
|
12437
|
-
const label = (
|
|
12438
|
-
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)})`;
|
|
12439
12549
|
}
|
|
12440
|
-
function printRecord(ctx, entity,
|
|
12550
|
+
function printRecord(ctx, entity, record12) {
|
|
12441
12551
|
ctx.out.log(
|
|
12442
|
-
`${
|
|
12552
|
+
`${record12.id} [${statusCol(entity, record12)}] ${record12.appId} ${studioRecordLink(ctx, entity, record12)}`
|
|
12443
12553
|
);
|
|
12444
12554
|
}
|
|
12445
12555
|
function emit2(ctx, value2, human) {
|
|
@@ -12493,8 +12603,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
12493
12603
|
input,
|
|
12494
12604
|
mutationId: writeMutationId2(parsed)
|
|
12495
12605
|
});
|
|
12496
|
-
const
|
|
12497
|
-
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)}`));
|
|
12498
12608
|
}
|
|
12499
12609
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12500
12610
|
var UUID_PREFIX = /^[0-9a-f-]{8,35}$/i;
|
|
@@ -12514,7 +12624,7 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12514
12624
|
"GET",
|
|
12515
12625
|
`/${entity}?limit=${limit}&offset=${offset}`
|
|
12516
12626
|
);
|
|
12517
|
-
matches.push(...page2.records.map((
|
|
12627
|
+
matches.push(...page2.records.map((record12) => record12.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
|
|
12518
12628
|
offset += page2.records.length;
|
|
12519
12629
|
if (matches.length > 1 || page2.records.length === 0 || offset >= page2.total) break;
|
|
12520
12630
|
}
|
|
@@ -12528,17 +12638,17 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12528
12638
|
}
|
|
12529
12639
|
async function pmGet(ctx, entity, id2) {
|
|
12530
12640
|
const resolved = await resolvePmId(ctx, entity, id2);
|
|
12531
|
-
const { record:
|
|
12532
|
-
emit2(ctx,
|
|
12641
|
+
const { record: record12 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
|
|
12642
|
+
emit2(ctx, record12, () => printRecord(ctx, entity, record12));
|
|
12533
12643
|
}
|
|
12534
12644
|
async function pmReference(ctx, entity, id2) {
|
|
12535
|
-
const { record:
|
|
12645
|
+
const { record: record12 } = await pmRequest(
|
|
12536
12646
|
ctx,
|
|
12537
12647
|
"GET",
|
|
12538
12648
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12539
12649
|
);
|
|
12540
|
-
const markup = referenceMarkup(entity,
|
|
12541
|
-
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 }, () => {
|
|
12542
12652
|
ctx.out.log(markup);
|
|
12543
12653
|
});
|
|
12544
12654
|
}
|
|
@@ -12623,16 +12733,16 @@ var PER_ENTITY = {
|
|
|
12623
12733
|
decision: ["status", "body", "supersedesId"],
|
|
12624
12734
|
bug: ["status", "severity", "description", "goalId", "assigneeId", "decisionId"]
|
|
12625
12735
|
};
|
|
12626
|
-
function leanRecord(entity,
|
|
12736
|
+
function leanRecord(entity, record12) {
|
|
12627
12737
|
const out = {};
|
|
12628
12738
|
for (const key of [...COMMON, ...PER_ENTITY[entity]]) {
|
|
12629
|
-
const value2 =
|
|
12739
|
+
const value2 = record12[key];
|
|
12630
12740
|
if (value2 !== void 0 && value2 !== null) out[key] = value2;
|
|
12631
12741
|
}
|
|
12632
12742
|
return out;
|
|
12633
12743
|
}
|
|
12634
12744
|
function leanRecords(entity, records, verbose) {
|
|
12635
|
-
return verbose ? records : records.map((
|
|
12745
|
+
return verbose ? records : records.map((record12) => leanRecord(entity, record12));
|
|
12636
12746
|
}
|
|
12637
12747
|
|
|
12638
12748
|
// src/pm-intake.ts
|
|
@@ -12665,8 +12775,8 @@ async function pmNext(ctx, parsed) {
|
|
|
12665
12775
|
// One request for both live columns; the split below is over open work only.
|
|
12666
12776
|
listFiltered(ctx, "task", appId, projectId, { column: `${READY_COLUMN},doing` })
|
|
12667
12777
|
]);
|
|
12668
|
-
const ready = tasks.filter((
|
|
12669
|
-
const doing = tasks.filter((
|
|
12778
|
+
const ready = tasks.filter((record12) => record12.column === READY_COLUMN);
|
|
12779
|
+
const doing = tasks.filter((record12) => record12.column === "doing");
|
|
12670
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}`;
|
|
12671
12781
|
const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
|
|
12672
12782
|
const result = {
|
|
@@ -12687,7 +12797,7 @@ async function pmNext(ctx, parsed) {
|
|
|
12687
12797
|
]) {
|
|
12688
12798
|
ctx.out.log(`${label}:`);
|
|
12689
12799
|
if (!records.length) ctx.out.log("- (none)");
|
|
12690
|
-
else for (const
|
|
12800
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12691
12801
|
}
|
|
12692
12802
|
ctx.out.log(`next: ${guidance}`);
|
|
12693
12803
|
ctx.out.log(`monitor: ${monitor}`);
|
|
@@ -12726,7 +12836,7 @@ async function pmHandoff(ctx, parsed) {
|
|
|
12726
12836
|
]) {
|
|
12727
12837
|
ctx.out.log(`${label}:`);
|
|
12728
12838
|
if (!records.length) ctx.out.log("- (none)");
|
|
12729
|
-
else for (const
|
|
12839
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12730
12840
|
}
|
|
12731
12841
|
ctx.out.log(`monitor: ${monitor}`);
|
|
12732
12842
|
});
|
|
@@ -12801,14 +12911,14 @@ async function pmStart(ctx, parsed) {
|
|
|
12801
12911
|
|
|
12802
12912
|
// src/pm-links.ts
|
|
12803
12913
|
async function pmLink(ctx, entity, id2) {
|
|
12804
|
-
const { record:
|
|
12914
|
+
const { record: record12 } = await pmRequest(
|
|
12805
12915
|
ctx,
|
|
12806
12916
|
"GET",
|
|
12807
12917
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12808
12918
|
);
|
|
12809
|
-
const url = studioRecordUrl(ctx, entity,
|
|
12810
|
-
const markdown = studioRecordLink(ctx, entity,
|
|
12811
|
-
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 }, () => {
|
|
12812
12922
|
ctx.out.log(markdown);
|
|
12813
12923
|
});
|
|
12814
12924
|
}
|
|
@@ -12935,16 +13045,16 @@ async function page(ctx, appId, cursor) {
|
|
|
12935
13045
|
}
|
|
12936
13046
|
return data;
|
|
12937
13047
|
}
|
|
12938
|
-
function recordState(
|
|
12939
|
-
if (
|
|
12940
|
-
return String(
|
|
13048
|
+
function recordState(record12) {
|
|
13049
|
+
if (record12.column) return record12.column === "todo" ? "ready" : record12.column;
|
|
13050
|
+
return String(record12.status ?? "");
|
|
12941
13051
|
}
|
|
12942
13052
|
function eventRecord(event) {
|
|
12943
13053
|
return event.payload.payload;
|
|
12944
13054
|
}
|
|
12945
13055
|
function eventLabel(event) {
|
|
12946
|
-
const
|
|
12947
|
-
if (
|
|
13056
|
+
const record12 = eventRecord(event);
|
|
13057
|
+
if (record12) return String(record12.title ?? event.payload.entityId);
|
|
12948
13058
|
const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
|
|
12949
13059
|
return body || event.payload.entityId;
|
|
12950
13060
|
}
|
|
@@ -12952,10 +13062,10 @@ function report3(ctx, parsed, result) {
|
|
|
12952
13062
|
if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
|
|
12953
13063
|
else if (parsed.options.jsonl !== true && result.found) {
|
|
12954
13064
|
for (const event of result.events ?? []) {
|
|
12955
|
-
const
|
|
12956
|
-
const state2 =
|
|
13065
|
+
const record12 = eventRecord(event);
|
|
13066
|
+
const state2 = record12 ? recordState(record12) : "comment";
|
|
12957
13067
|
ctx.out.log(
|
|
12958
|
-
`${event.id} ${event.type} ${state2}${
|
|
13068
|
+
`${event.id} ${event.type} ${state2}${record12?.revision ? `; r${record12.revision}` : ""} ${eventLabel(event)}`
|
|
12959
13069
|
);
|
|
12960
13070
|
}
|
|
12961
13071
|
}
|
|
@@ -13029,8 +13139,8 @@ async function pmWatch(ctx, parsed) {
|
|
|
13029
13139
|
}
|
|
13030
13140
|
firstSuccess = false;
|
|
13031
13141
|
const matching = current.events.filter((event) => {
|
|
13032
|
-
const
|
|
13033
|
-
const state2 =
|
|
13142
|
+
const record12 = eventRecord(event);
|
|
13143
|
+
const state2 = record12 ? recordState(record12).toLowerCase() : "";
|
|
13034
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);
|
|
13035
13145
|
});
|
|
13036
13146
|
for (const event of matching) {
|
|
@@ -13461,17 +13571,17 @@ async function platformStatus(parsed, deps) {
|
|
|
13461
13571
|
}
|
|
13462
13572
|
}
|
|
13463
13573
|
function isPlatformStatus(value2) {
|
|
13464
|
-
if (!
|
|
13465
|
-
if (!
|
|
13466
|
-
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;
|
|
13467
13577
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
13468
13578
|
}
|
|
13469
13579
|
function apiMessage(value2) {
|
|
13470
|
-
if (!
|
|
13471
|
-
const error =
|
|
13580
|
+
if (!record8(value2)) return "request failed";
|
|
13581
|
+
const error = record8(value2.error) ? value2.error : value2;
|
|
13472
13582
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
13473
13583
|
}
|
|
13474
|
-
function
|
|
13584
|
+
function record8(value2) {
|
|
13475
13585
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
13476
13586
|
}
|
|
13477
13587
|
|
|
@@ -13512,7 +13622,7 @@ function statusVerdict(reads) {
|
|
|
13512
13622
|
severity: "degraded"
|
|
13513
13623
|
});
|
|
13514
13624
|
}
|
|
13515
|
-
const performance =
|
|
13625
|
+
const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
13516
13626
|
if (performance?.status === "unavailable") {
|
|
13517
13627
|
reasons.push({
|
|
13518
13628
|
source: "liveSync",
|
|
@@ -13593,7 +13703,7 @@ function statusVerdict(reads) {
|
|
|
13593
13703
|
reasons
|
|
13594
13704
|
};
|
|
13595
13705
|
}
|
|
13596
|
-
function
|
|
13706
|
+
function record9(value2) {
|
|
13597
13707
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13598
13708
|
}
|
|
13599
13709
|
function numeric2(value2) {
|
|
@@ -13621,7 +13731,7 @@ function printO11yStatus(status, out) {
|
|
|
13621
13731
|
out.log(
|
|
13622
13732
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
13623
13733
|
);
|
|
13624
|
-
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) : [];
|
|
13625
13735
|
const requests = routes.reduce(
|
|
13626
13736
|
(total, row) => total + numeric3(row.requests),
|
|
13627
13737
|
0
|
|
@@ -13633,39 +13743,39 @@ function printO11yStatus(status, out) {
|
|
|
13633
13743
|
out.log(
|
|
13634
13744
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
13635
13745
|
);
|
|
13636
|
-
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) : [];
|
|
13637
13747
|
out.log(
|
|
13638
13748
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
13639
13749
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
13640
13750
|
).join(", ") : "none observed"}`
|
|
13641
13751
|
);
|
|
13642
13752
|
out.log(liveSyncLine(status.liveSync));
|
|
13643
|
-
const canaryDurations =
|
|
13753
|
+
const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
13644
13754
|
out.log(
|
|
13645
13755
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
13646
13756
|
);
|
|
13647
|
-
const collectorIngest =
|
|
13648
|
-
const collectorStorage =
|
|
13757
|
+
const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
13758
|
+
const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
13649
13759
|
out.log(
|
|
13650
13760
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
13651
13761
|
);
|
|
13652
|
-
const providerMetrics =
|
|
13653
|
-
const providerCapacity =
|
|
13654
|
-
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 : {};
|
|
13655
13765
|
out.log(
|
|
13656
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`
|
|
13657
13767
|
);
|
|
13658
13768
|
for (const line2 of providerCapacityLines(status.providerCapacity)) {
|
|
13659
13769
|
out.log(line2);
|
|
13660
13770
|
}
|
|
13661
|
-
const coverage =
|
|
13662
|
-
const coverageCounts =
|
|
13663
|
-
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 : {};
|
|
13664
13774
|
out.log(
|
|
13665
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`
|
|
13666
13776
|
);
|
|
13667
13777
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
13668
|
-
const providerFreshness =
|
|
13778
|
+
const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
13669
13779
|
out.log(
|
|
13670
13780
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
13671
13781
|
);
|
|
@@ -13674,17 +13784,17 @@ function printO11yStatus(status, out) {
|
|
|
13674
13784
|
);
|
|
13675
13785
|
}
|
|
13676
13786
|
function providerCapacityLines(read3) {
|
|
13677
|
-
const resources =
|
|
13678
|
-
const durableObjects =
|
|
13679
|
-
const periodic =
|
|
13680
|
-
const storage =
|
|
13681
|
-
const d1 =
|
|
13682
|
-
const d1Activity =
|
|
13683
|
-
const d1Storage =
|
|
13684
|
-
const d1Latency =
|
|
13685
|
-
const r2 =
|
|
13686
|
-
const r2Operations =
|
|
13687
|
-
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 : {};
|
|
13688
13798
|
const status = String(
|
|
13689
13799
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
13690
13800
|
);
|
|
@@ -13695,11 +13805,11 @@ function providerCapacityLines(read3) {
|
|
|
13695
13805
|
];
|
|
13696
13806
|
}
|
|
13697
13807
|
function liveSyncLine(read3) {
|
|
13698
|
-
const performance =
|
|
13699
|
-
const commitToSend =
|
|
13808
|
+
const performance = record10(read3.body.performance) ? read3.body.performance : {};
|
|
13809
|
+
const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
|
|
13700
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`;
|
|
13701
13811
|
}
|
|
13702
|
-
function
|
|
13812
|
+
function record10(value2) {
|
|
13703
13813
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13704
13814
|
}
|
|
13705
13815
|
function numeric3(value2) {
|
|
@@ -14037,7 +14147,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
14037
14147
|
if (action2 === "plan" || action2 === "apply") {
|
|
14038
14148
|
const desired = monitoringWireConfig(context.cfg, env);
|
|
14039
14149
|
const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
|
|
14040
|
-
const currentRevision =
|
|
14150
|
+
const currentRevision = record11(live.config) ? string(live.config.revision) : null;
|
|
14041
14151
|
const changed = currentRevision !== desired.revision;
|
|
14042
14152
|
const plan = {
|
|
14043
14153
|
schemaVersion: 1,
|
|
@@ -14082,7 +14192,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
14082
14192
|
headers
|
|
14083
14193
|
}, doFetch);
|
|
14084
14194
|
emit3(result2, jsonOutput, out, () => {
|
|
14085
|
-
const run =
|
|
14195
|
+
const run = record11(result2.run) ? result2.run : {};
|
|
14086
14196
|
out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
|
|
14087
14197
|
});
|
|
14088
14198
|
return;
|
|
@@ -14107,12 +14217,12 @@ async function request2(url, init, doFetch) {
|
|
|
14107
14217
|
let body = {};
|
|
14108
14218
|
try {
|
|
14109
14219
|
const parsed = text4 ? JSON.parse(text4) : {};
|
|
14110
|
-
body =
|
|
14220
|
+
body = record11(parsed) ? parsed : { value: parsed };
|
|
14111
14221
|
} catch {
|
|
14112
14222
|
body = { message: text4.slice(0, 500) };
|
|
14113
14223
|
}
|
|
14114
14224
|
if (!response2.ok) {
|
|
14115
|
-
const error =
|
|
14225
|
+
const error = record11(body.error) ? body.error : body;
|
|
14116
14226
|
throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
|
|
14117
14227
|
}
|
|
14118
14228
|
return body;
|
|
@@ -14120,7 +14230,7 @@ async function request2(url, init, doFetch) {
|
|
|
14120
14230
|
function printRead(action2, appId, env, result, out) {
|
|
14121
14231
|
if (action2 === "status") {
|
|
14122
14232
|
out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
|
|
14123
|
-
const slos = Array.isArray(result.slos) ? result.slos.filter(
|
|
14233
|
+
const slos = Array.isArray(result.slos) ? result.slos.filter(record11) : [];
|
|
14124
14234
|
for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
|
|
14125
14235
|
const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
|
|
14126
14236
|
const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
|
|
@@ -14129,20 +14239,20 @@ function printRead(action2, appId, env, result, out) {
|
|
|
14129
14239
|
return;
|
|
14130
14240
|
}
|
|
14131
14241
|
if (action2 === "incidents") {
|
|
14132
|
-
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(
|
|
14242
|
+
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record11) : [];
|
|
14133
14243
|
out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
|
|
14134
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()}`);
|
|
14135
14245
|
return;
|
|
14136
14246
|
}
|
|
14137
14247
|
out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
|
|
14138
|
-
const probes = Array.isArray(result.probes) ? result.probes.filter(
|
|
14248
|
+
const probes = Array.isArray(result.probes) ? result.probes.filter(record11) : [];
|
|
14139
14249
|
for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
|
|
14140
14250
|
}
|
|
14141
14251
|
function emit3(value2, json, out, human) {
|
|
14142
14252
|
if (json) out.log(JSON.stringify(value2, null, 2));
|
|
14143
14253
|
else human();
|
|
14144
14254
|
}
|
|
14145
|
-
function
|
|
14255
|
+
function record11(value2) {
|
|
14146
14256
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
14147
14257
|
}
|
|
14148
14258
|
function string(value2) {
|
|
@@ -14633,6 +14743,122 @@ async function provision(options) {
|
|
|
14633
14743
|
}
|
|
14634
14744
|
}
|
|
14635
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
|
+
|
|
14636
14862
|
// src/record.ts
|
|
14637
14863
|
var import_node_fs18 = require("fs");
|
|
14638
14864
|
var import_node_process18 = __toESM(require("process"), 1);
|
|
@@ -14652,12 +14878,12 @@ function recordInvocation(parsed) {
|
|
|
14652
14878
|
}
|
|
14653
14879
|
|
|
14654
14880
|
// src/advisory-output.ts
|
|
14655
|
-
var
|
|
14881
|
+
var import_apps15 = require("@odla-ai/apps");
|
|
14656
14882
|
function advisoryCollectingFetch(inner, sink) {
|
|
14657
14883
|
return (async (input, init) => {
|
|
14658
14884
|
const response2 = await inner(input, init);
|
|
14659
14885
|
try {
|
|
14660
|
-
sink.push(...(0,
|
|
14886
|
+
sink.push(...(0, import_apps15.parseAdvisories)(response2));
|
|
14661
14887
|
} catch {
|
|
14662
14888
|
}
|
|
14663
14889
|
return response2;
|
|
@@ -14677,7 +14903,7 @@ function renderAdvisories(out, advisories, env = process.env) {
|
|
|
14677
14903
|
const key = `${advisory.code}:${advisory.message}`;
|
|
14678
14904
|
if (seen.has(key)) continue;
|
|
14679
14905
|
seen.add(key);
|
|
14680
|
-
out.error((0,
|
|
14906
|
+
out.error((0, import_apps15.formatAdvisory)(advisory));
|
|
14681
14907
|
}
|
|
14682
14908
|
}
|
|
14683
14909
|
|
|
@@ -14964,7 +15190,7 @@ async function bySlug(ctx, slug) {
|
|
|
14964
15190
|
"GET",
|
|
14965
15191
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
14966
15192
|
);
|
|
14967
|
-
const filtered = page2.records.find((
|
|
15193
|
+
const filtered = page2.records.find((record12) => record12.slug === slug);
|
|
14968
15194
|
if (filtered) return filtered;
|
|
14969
15195
|
const limit = 100;
|
|
14970
15196
|
for (let offset = 0; ; offset += limit) {
|
|
@@ -14973,7 +15199,7 @@ async function bySlug(ctx, slug) {
|
|
|
14973
15199
|
"GET",
|
|
14974
15200
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
14975
15201
|
);
|
|
14976
|
-
const found = fallback.records.find((
|
|
15202
|
+
const found = fallback.records.find((record12) => record12.slug === slug);
|
|
14977
15203
|
if (found) return found;
|
|
14978
15204
|
if (!fallback.records.length || offset + fallback.records.length >= fallback.total) break;
|
|
14979
15205
|
}
|
|
@@ -15773,41 +15999,6 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15773
15999
|
}
|
|
15774
16000
|
}
|
|
15775
16001
|
|
|
15776
|
-
// src/security-command-context.ts
|
|
15777
|
-
async function hostedSecurityContext(parsed, dependencies) {
|
|
15778
|
-
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
15779
|
-
const cfg = await loadProjectConfig(configPath);
|
|
15780
|
-
const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
15781
|
-
if (!env || !cfg.envs.includes(env)) {
|
|
15782
|
-
throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
|
|
15783
|
-
}
|
|
15784
|
-
const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
|
|
15785
|
-
if (platformAudience(cfg.platformUrl) !== platform) {
|
|
15786
|
-
throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
|
|
15787
|
-
}
|
|
15788
|
-
const doFetch = dependencies.fetch ?? fetch;
|
|
15789
|
-
const stdout = dependencies.stdout ?? console;
|
|
15790
|
-
const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
|
|
15791
|
-
const token = await getDeveloperToken(
|
|
15792
|
-
cfg,
|
|
15793
|
-
{ configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
|
|
15794
|
-
doFetch,
|
|
15795
|
-
stdout,
|
|
15796
|
-
{ optionalProjectCapabilities: ["app.manage"] }
|
|
15797
|
-
);
|
|
15798
|
-
return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
|
|
15799
|
-
}
|
|
15800
|
-
function requiredSecurityPositional(parsed, index, label) {
|
|
15801
|
-
const value2 = parsed.positionals[index];
|
|
15802
|
-
if (!value2) throw new Error(`${label} is required`);
|
|
15803
|
-
return value2;
|
|
15804
|
-
}
|
|
15805
|
-
function securityProfile(value2) {
|
|
15806
|
-
if (value2 === void 0) return void 0;
|
|
15807
|
-
if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
|
|
15808
|
-
throw new Error("--profile must be odla, cloudflare-app, or generic");
|
|
15809
|
-
}
|
|
15810
|
-
|
|
15811
16002
|
// src/security-command-output.ts
|
|
15812
16003
|
function printHostedSecurityPlan(out, plan, appId) {
|
|
15813
16004
|
out.log(`Hosted security plan for ${appId}/${plan.env}: ${plan.ready ? "ready" : "not ready"}`);
|
|
@@ -16630,6 +16821,10 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16630
16821
|
await securityCommand(parsed, runtime);
|
|
16631
16822
|
return;
|
|
16632
16823
|
}
|
|
16824
|
+
if (command === "promotion") {
|
|
16825
|
+
await promotionCommand(parsed, runtime);
|
|
16826
|
+
return;
|
|
16827
|
+
}
|
|
16633
16828
|
if (command === "brand") {
|
|
16634
16829
|
await brandCommand(parsed, runtime);
|
|
16635
16830
|
return;
|