@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
|
@@ -1252,6 +1252,7 @@ var COMMAND_SURFACE = {
|
|
|
1252
1252
|
watch: {}
|
|
1253
1253
|
},
|
|
1254
1254
|
provision: {},
|
|
1255
|
+
promotion: { plan: {}, inspect: {}, status: {}, apply: {}, rollback: {}, next: {}, record: {}, cancel: {} },
|
|
1255
1256
|
runbook: {
|
|
1256
1257
|
ask: {},
|
|
1257
1258
|
search: {},
|
|
@@ -3557,9 +3558,9 @@ function canonicalValue(value2) {
|
|
|
3557
3558
|
}
|
|
3558
3559
|
if (Array.isArray(value2)) return value2.map(canonicalValue);
|
|
3559
3560
|
if (value2 && typeof value2 === "object") {
|
|
3560
|
-
const
|
|
3561
|
+
const record12 = value2;
|
|
3561
3562
|
return Object.fromEntries(
|
|
3562
|
-
Object.keys(
|
|
3563
|
+
Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, canonicalValue(record12[key])])
|
|
3563
3564
|
);
|
|
3564
3565
|
}
|
|
3565
3566
|
throw new TypeError("canonical JSON rejects unsupported values");
|
|
@@ -5943,10 +5944,10 @@ import { existsSync as existsSync10 } from "fs";
|
|
|
5943
5944
|
import { cpus, hostname, totalmem } from "os";
|
|
5944
5945
|
import { resolve as resolve11 } from "path";
|
|
5945
5946
|
|
|
5946
|
-
// ../harness/dist/chunk-
|
|
5947
|
+
// ../harness/dist/chunk-U324RQ4N.js
|
|
5947
5948
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
5948
5949
|
|
|
5949
|
-
// ../harness/dist/chunk-
|
|
5950
|
+
// ../harness/dist/chunk-5LRYJKUI.js
|
|
5950
5951
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5951
5952
|
import { constants } from "fs";
|
|
5952
5953
|
import { access } from "fs/promises";
|
|
@@ -6114,8 +6115,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
6114
6115
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
6115
6116
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
6116
6117
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
6117
|
-
const entries = inventory.flatMap((
|
|
6118
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
6118
|
+
const entries = inventory.flatMap((record12) => {
|
|
6119
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record12);
|
|
6119
6120
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
6120
6121
|
});
|
|
6121
6122
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -6319,7 +6320,165 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
6319
6320
|
}
|
|
6320
6321
|
}
|
|
6321
6322
|
|
|
6322
|
-
// ../harness/dist/chunk-
|
|
6323
|
+
// ../harness/dist/chunk-FAN2R3GW.js
|
|
6324
|
+
var text3 = (value2, maximum) => {
|
|
6325
|
+
if (typeof value2 !== "string") return void 0;
|
|
6326
|
+
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
6327
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
6328
|
+
};
|
|
6329
|
+
var integer = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
6330
|
+
var record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
6331
|
+
var excerpt = (value2, tail = false) => {
|
|
6332
|
+
if (typeof value2 !== "string") return void 0;
|
|
6333
|
+
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
6334
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
6335
|
+
if (!source) return void 0;
|
|
6336
|
+
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
6337
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
6338
|
+
return text3(selected.join("\n"), 2400);
|
|
6339
|
+
};
|
|
6340
|
+
var paths = (value2) => {
|
|
6341
|
+
if (!Array.isArray(value2)) return void 0;
|
|
6342
|
+
const items = value2.flatMap((item) => {
|
|
6343
|
+
const path = text3(item, 1024);
|
|
6344
|
+
return path ? [path] : [];
|
|
6345
|
+
}).slice(0, 12);
|
|
6346
|
+
return items.length ? items : void 0;
|
|
6347
|
+
};
|
|
6348
|
+
function patchStats(value2) {
|
|
6349
|
+
if (typeof value2 !== "string") return {};
|
|
6350
|
+
let additions = 0;
|
|
6351
|
+
let deletions = 0;
|
|
6352
|
+
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
6353
|
+
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
6354
|
+
if (line2.startsWith("+")) additions += 1;
|
|
6355
|
+
else if (line2.startsWith("-")) deletions += 1;
|
|
6356
|
+
}
|
|
6357
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
6358
|
+
}
|
|
6359
|
+
function searchResults(value2) {
|
|
6360
|
+
if (typeof value2 !== "string") return void 0;
|
|
6361
|
+
const results = value2.split("\n").flatMap((line2) => {
|
|
6362
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
6363
|
+
if (!match) return [];
|
|
6364
|
+
const lineNumber = Number(match[2]);
|
|
6365
|
+
const itemText = text3(match[3], 240);
|
|
6366
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
6367
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
6368
|
+
}).slice(0, 5);
|
|
6369
|
+
return results.length ? results : void 0;
|
|
6370
|
+
}
|
|
6371
|
+
function codeToolRequestPresentation(request3) {
|
|
6372
|
+
const input = request3.input;
|
|
6373
|
+
if (request3.tool === "sandbox.read") {
|
|
6374
|
+
const path = text3(input.path, 1024);
|
|
6375
|
+
if (!path) return void 0;
|
|
6376
|
+
const startLine = integer(input.startLine);
|
|
6377
|
+
const endLine = integer(input.endLine);
|
|
6378
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
6379
|
+
}
|
|
6380
|
+
if (request3.tool === "sandbox.list") {
|
|
6381
|
+
const scope = text3(input.prefix, 1024);
|
|
6382
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
6383
|
+
}
|
|
6384
|
+
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
6385
|
+
const query = text3(input.query, 512);
|
|
6386
|
+
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
6387
|
+
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
6388
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
6389
|
+
}
|
|
6390
|
+
if (request3.tool === "sandbox.apply_patch") {
|
|
6391
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
6392
|
+
}
|
|
6393
|
+
const recipeId = text3(input.recipeId, 120);
|
|
6394
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
6395
|
+
}
|
|
6396
|
+
function codeToolResultPresentation(request3, response2) {
|
|
6397
|
+
const started = codeToolRequestPresentation(request3);
|
|
6398
|
+
if (!started || !response2.ok) return started;
|
|
6399
|
+
const details = record5(response2.details);
|
|
6400
|
+
if (started.kind === "read") {
|
|
6401
|
+
return {
|
|
6402
|
+
...started,
|
|
6403
|
+
...integer(details?.startLine) ? { startLine: integer(details?.startLine) } : {},
|
|
6404
|
+
...integer(details?.endLine) ? { endLine: integer(details?.endLine) } : {},
|
|
6405
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
6406
|
+
};
|
|
6407
|
+
}
|
|
6408
|
+
if (started.kind === "list") {
|
|
6409
|
+
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);
|
|
6410
|
+
return {
|
|
6411
|
+
...started,
|
|
6412
|
+
...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
|
|
6413
|
+
...listed.length ? { paths: listed } : {}
|
|
6414
|
+
};
|
|
6415
|
+
}
|
|
6416
|
+
if (started.kind === "query") {
|
|
6417
|
+
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
6418
|
+
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
6419
|
+
return {
|
|
6420
|
+
...started,
|
|
6421
|
+
...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
|
|
6422
|
+
...results ? { results } : {},
|
|
6423
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
6424
|
+
};
|
|
6425
|
+
}
|
|
6426
|
+
if (started.kind === "patch") {
|
|
6427
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
6428
|
+
}
|
|
6429
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
6430
|
+
return {
|
|
6431
|
+
...started,
|
|
6432
|
+
...integer(details?.exitCode) !== void 0 ? { exitCode: integer(details?.exitCode) } : {},
|
|
6433
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
6434
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
6435
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
6436
|
+
};
|
|
6437
|
+
}
|
|
6438
|
+
var MAX_FAILURE_REASON = 240;
|
|
6439
|
+
function toolFailureReason(response2) {
|
|
6440
|
+
if (response2.ok) return void 0;
|
|
6441
|
+
const supplied = response2.details?.failureReason;
|
|
6442
|
+
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
6443
|
+
return reason.slice(0, MAX_FAILURE_REASON);
|
|
6444
|
+
}
|
|
6445
|
+
var DEFAULT_FAILURE_REASON = {
|
|
6446
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
6447
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
6448
|
+
};
|
|
6449
|
+
function observedBroker(input) {
|
|
6450
|
+
const now = input.now ?? Date.now;
|
|
6451
|
+
return {
|
|
6452
|
+
execute: async (context, request3) => {
|
|
6453
|
+
const startedAt = now();
|
|
6454
|
+
const operationId = input.operationIdFor(request3.requestId);
|
|
6455
|
+
const startedPresentation = codeToolRequestPresentation(request3);
|
|
6456
|
+
await input.emit({
|
|
6457
|
+
type: "tool",
|
|
6458
|
+
phase: "started",
|
|
6459
|
+
tool: request3.tool,
|
|
6460
|
+
operationId,
|
|
6461
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
6462
|
+
});
|
|
6463
|
+
const response2 = await input.broker.execute(context, request3);
|
|
6464
|
+
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
6465
|
+
const failureReason = toolFailureReason(response2);
|
|
6466
|
+
await input.emit({
|
|
6467
|
+
type: "tool",
|
|
6468
|
+
phase: "completed",
|
|
6469
|
+
tool: request3.tool,
|
|
6470
|
+
ok: response2.ok,
|
|
6471
|
+
durationMs: now() - startedAt,
|
|
6472
|
+
operationId,
|
|
6473
|
+
...failureReason ? { failureReason } : {},
|
|
6474
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
6475
|
+
});
|
|
6476
|
+
return response2;
|
|
6477
|
+
}
|
|
6478
|
+
};
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6481
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
6323
6482
|
import { createHash as createHash3 } from "crypto";
|
|
6324
6483
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
6325
6484
|
import { relative as relative4, resolve as resolve10 } from "path";
|
|
@@ -6363,8 +6522,8 @@ function normalize(value2) {
|
|
|
6363
6522
|
if (Array.isArray(value2)) return value2.map(normalize);
|
|
6364
6523
|
if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
|
|
6365
6524
|
if (typeof value2 === "object") {
|
|
6366
|
-
const
|
|
6367
|
-
return Object.fromEntries(Object.keys(
|
|
6525
|
+
const record12 = value2;
|
|
6526
|
+
return Object.fromEntries(Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, normalize(record12[key])]));
|
|
6368
6527
|
}
|
|
6369
6528
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
6370
6529
|
}
|
|
@@ -6656,7 +6815,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6656
6815
|
}
|
|
6657
6816
|
}
|
|
6658
6817
|
|
|
6659
|
-
// ../harness/dist/chunk-
|
|
6818
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
6660
6819
|
import { spawn as spawn4 } from "child_process";
|
|
6661
6820
|
import { lstat as lstat2 } from "fs/promises";
|
|
6662
6821
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -6963,7 +7122,7 @@ function looksLikeDestination(value2) {
|
|
|
6963
7122
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text4) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text4);
|
|
6964
7123
|
}
|
|
6965
7124
|
|
|
6966
|
-
// ../harness/dist/chunk-
|
|
7125
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
6967
7126
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
6968
7127
|
import { readFile as readFile3 } from "fs/promises";
|
|
6969
7128
|
import { join as join33 } from "path";
|
|
@@ -7231,7 +7390,7 @@ async function buildCodeGraph(input) {
|
|
|
7231
7390
|
return builder.build();
|
|
7232
7391
|
}
|
|
7233
7392
|
|
|
7234
|
-
// ../harness/dist/chunk-
|
|
7393
|
+
// ../harness/dist/chunk-Q4NL5XO3.js
|
|
7235
7394
|
import { createHash as createHash32 } from "crypto";
|
|
7236
7395
|
async function digestStagedWorkspace(root, limits) {
|
|
7237
7396
|
const files = [];
|
|
@@ -7290,7 +7449,7 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7290
7449
|
}
|
|
7291
7450
|
const value2 = await response2.json().catch(() => null);
|
|
7292
7451
|
if (!response2.ok) {
|
|
7293
|
-
const problem =
|
|
7452
|
+
const problem = record6(record6(value2)?.error);
|
|
7294
7453
|
throw new CodeRuntimeControlError(
|
|
7295
7454
|
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
7296
7455
|
response2.status,
|
|
@@ -7311,12 +7470,12 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7311
7470
|
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
7312
7471
|
),
|
|
7313
7472
|
infer: async (sessionId, inference) => {
|
|
7314
|
-
const value2 =
|
|
7473
|
+
const value2 = record6(await call4(
|
|
7315
7474
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
7316
7475
|
inference,
|
|
7317
7476
|
modelRequestTimeoutMs
|
|
7318
7477
|
));
|
|
7319
|
-
if (!value2 || value2.requestId !== inference.requestId || !
|
|
7478
|
+
if (!value2 || value2.requestId !== inference.requestId || !record6(value2.response) || !record6(value2.receipt)) {
|
|
7320
7479
|
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
7321
7480
|
}
|
|
7322
7481
|
return value2;
|
|
@@ -7496,12 +7655,12 @@ function validateHeartbeat(version, capabilities) {
|
|
|
7496
7655
|
}
|
|
7497
7656
|
}
|
|
7498
7657
|
function parseSnapshot(value2) {
|
|
7499
|
-
const root =
|
|
7500
|
-
const host =
|
|
7658
|
+
const root = record6(value2);
|
|
7659
|
+
const host = record6(root?.host);
|
|
7501
7660
|
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");
|
|
7502
7661
|
const bindingIds = /* @__PURE__ */ new Set();
|
|
7503
7662
|
const bindings = root.bindings.map((item) => {
|
|
7504
|
-
const binding =
|
|
7663
|
+
const binding = record6(item);
|
|
7505
7664
|
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)) {
|
|
7506
7665
|
throw invalid("binding");
|
|
7507
7666
|
}
|
|
@@ -7511,10 +7670,10 @@ function parseSnapshot(value2) {
|
|
|
7511
7670
|
const commandIds = /* @__PURE__ */ new Set();
|
|
7512
7671
|
const commandSequences = /* @__PURE__ */ new Set();
|
|
7513
7672
|
const commands = root.commands.map((item) => {
|
|
7514
|
-
const command =
|
|
7673
|
+
const command = record6(item);
|
|
7515
7674
|
const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
|
|
7516
7675
|
const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
|
|
7517
|
-
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)) || !
|
|
7676
|
+
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");
|
|
7518
7677
|
commandIds.add(command.commandId);
|
|
7519
7678
|
commandSequences.add(sequenceKey);
|
|
7520
7679
|
return command;
|
|
@@ -7523,10 +7682,10 @@ function parseSnapshot(value2) {
|
|
|
7523
7682
|
}
|
|
7524
7683
|
async function parseSource(value2) {
|
|
7525
7684
|
const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
|
|
7526
|
-
const snapshot =
|
|
7685
|
+
const snapshot = record6(record6(value2)?.snapshot);
|
|
7527
7686
|
if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
|
|
7528
7687
|
const files = snapshot.files.map((value22) => {
|
|
7529
|
-
const file =
|
|
7688
|
+
const file = record6(value22);
|
|
7530
7689
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
|
|
7531
7690
|
return { path: file.path, content: file.content };
|
|
7532
7691
|
});
|
|
@@ -7535,11 +7694,11 @@ async function parseSource(value2) {
|
|
|
7535
7694
|
const aliases = /* @__PURE__ */ new Set();
|
|
7536
7695
|
const references = [];
|
|
7537
7696
|
for (const item of referencesValue) {
|
|
7538
|
-
const reference =
|
|
7697
|
+
const reference = record6(item);
|
|
7539
7698
|
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");
|
|
7540
7699
|
aliases.add(reference.alias);
|
|
7541
7700
|
const referenceFiles = reference.files.map((entry) => {
|
|
7542
|
-
const file =
|
|
7701
|
+
const file = record6(entry);
|
|
7543
7702
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
|
|
7544
7703
|
return { path: file.path, content: file.content };
|
|
7545
7704
|
});
|
|
@@ -7554,31 +7713,31 @@ async function parseSource(value2) {
|
|
|
7554
7713
|
return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
|
|
7555
7714
|
}
|
|
7556
7715
|
function parseReview(value2) {
|
|
7557
|
-
const review =
|
|
7558
|
-
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");
|
|
7716
|
+
const review = record6(record6(value2)?.review);
|
|
7717
|
+
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");
|
|
7559
7718
|
return review;
|
|
7560
7719
|
}
|
|
7561
7720
|
function parseCandidate(value2) {
|
|
7562
|
-
const candidate =
|
|
7721
|
+
const candidate = record6(record6(value2)?.candidate);
|
|
7563
7722
|
if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
|
|
7564
7723
|
throw invalid("candidate");
|
|
7565
7724
|
}
|
|
7566
7725
|
return { candidateId: candidate.candidateId, status: candidate.status };
|
|
7567
7726
|
}
|
|
7568
7727
|
function parseCollaborationSkills(value2) {
|
|
7569
|
-
const items =
|
|
7728
|
+
const items = record6(value2)?.skills;
|
|
7570
7729
|
if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
|
|
7571
7730
|
const skillNames2 = /* @__PURE__ */ new Set();
|
|
7572
7731
|
const toolNames = /* @__PURE__ */ new Set();
|
|
7573
7732
|
return items.map((item) => {
|
|
7574
|
-
const skill =
|
|
7733
|
+
const skill = record6(item);
|
|
7575
7734
|
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) {
|
|
7576
7735
|
throw invalid("collaboration skill");
|
|
7577
7736
|
}
|
|
7578
7737
|
skillNames2.add(skill.name);
|
|
7579
7738
|
const tools = skill.tools.map((candidate) => {
|
|
7580
|
-
const tool =
|
|
7581
|
-
const inputSchema =
|
|
7739
|
+
const tool = record6(candidate);
|
|
7740
|
+
const inputSchema = record6(tool?.inputSchema);
|
|
7582
7741
|
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") {
|
|
7583
7742
|
throw invalid("collaboration tool");
|
|
7584
7743
|
}
|
|
@@ -7603,12 +7762,12 @@ function parseCollaborationSkills(value2) {
|
|
|
7603
7762
|
}
|
|
7604
7763
|
function validateCollaborationToolRequest(value2) {
|
|
7605
7764
|
validCommandId(value2.commandId);
|
|
7606
|
-
if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !
|
|
7765
|
+
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) {
|
|
7607
7766
|
throw new TypeError("invalid Code collaboration tool request");
|
|
7608
7767
|
}
|
|
7609
7768
|
}
|
|
7610
7769
|
function parseCollaborationToolOutput(value2) {
|
|
7611
|
-
const output =
|
|
7770
|
+
const output = record6(record6(value2)?.output);
|
|
7612
7771
|
if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
|
|
7613
7772
|
throw invalid("collaboration tool");
|
|
7614
7773
|
}
|
|
@@ -7617,7 +7776,7 @@ function parseCollaborationToolOutput(value2) {
|
|
|
7617
7776
|
return { content: output.content, ...output.isError === true ? { isError: true } : {} };
|
|
7618
7777
|
}
|
|
7619
7778
|
if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block2) => {
|
|
7620
|
-
const item =
|
|
7779
|
+
const item = record6(block2);
|
|
7621
7780
|
return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
|
|
7622
7781
|
})) throw invalid("collaboration tool");
|
|
7623
7782
|
return {
|
|
@@ -7650,17 +7809,48 @@ function jsonBytes(value2) {
|
|
|
7650
7809
|
return Number.POSITIVE_INFINITY;
|
|
7651
7810
|
}
|
|
7652
7811
|
}
|
|
7653
|
-
var
|
|
7812
|
+
var record6 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
7654
7813
|
var invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
|
|
7655
7814
|
var RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
7656
7815
|
var SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
7657
7816
|
var PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
7658
7817
|
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;
|
|
7659
7818
|
function stripPatchEnvelope(patch2) {
|
|
7660
|
-
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
|
|
7819
|
+
if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
|
|
7661
7820
|
const kept = patch2.split("\n").filter((line2) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line2));
|
|
7662
7821
|
const stripped = kept.join("\n");
|
|
7663
|
-
|
|
7822
|
+
if (/^diff --git /m.test(stripped)) return stripped;
|
|
7823
|
+
const translated = applyPatchDialectToDiff(stripped);
|
|
7824
|
+
return translated === stripped ? patch2 : translated;
|
|
7825
|
+
}
|
|
7826
|
+
function applyPatchDialectToDiff(patch2) {
|
|
7827
|
+
if (!/^\*\*\* (?:Update|Add|Delete) File: /m.test(patch2)) return patch2;
|
|
7828
|
+
const out = [];
|
|
7829
|
+
let open = false;
|
|
7830
|
+
for (const line2 of patch2.split("\n")) {
|
|
7831
|
+
const file = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/.exec(line2);
|
|
7832
|
+
if (file) {
|
|
7833
|
+
const [, verb, raw] = file;
|
|
7834
|
+
const path = raw.trim();
|
|
7835
|
+
if (!PATH.test(path)) return patch2;
|
|
7836
|
+
out.push(`diff --git a/${path} b/${path}`);
|
|
7837
|
+
if (verb === "Add") out.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
|
|
7838
|
+
else if (verb === "Delete") out.push(`--- a/${path}`, "+++ /dev/null");
|
|
7839
|
+
else out.push(`--- a/${path}`, `+++ b/${path}`);
|
|
7840
|
+
open = true;
|
|
7841
|
+
continue;
|
|
7842
|
+
}
|
|
7843
|
+
if (/^\*\*\* /.test(line2)) continue;
|
|
7844
|
+
if (!open) continue;
|
|
7845
|
+
if (/^@@/.test(line2)) {
|
|
7846
|
+
out.push("@@ -1 +1 @@");
|
|
7847
|
+
continue;
|
|
7848
|
+
}
|
|
7849
|
+
out.push(line2);
|
|
7850
|
+
}
|
|
7851
|
+
if (!open) return patch2;
|
|
7852
|
+
return `${out.join("\n").replace(/\n+$/, "")}
|
|
7853
|
+
`;
|
|
7664
7854
|
}
|
|
7665
7855
|
function validateCodePatch(rawPatch, maxBytes) {
|
|
7666
7856
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
@@ -7713,17 +7903,20 @@ function resolveCodePath(workspaceDir, path) {
|
|
|
7713
7903
|
if (target !== root && !target.startsWith(`${root}${sep3}`)) throw new TypeError("path escapes the staged workspace");
|
|
7714
7904
|
return target;
|
|
7715
7905
|
}
|
|
7906
|
+
function hasContextFreeHunk(patch2) {
|
|
7907
|
+
const bodies = patch2.split(/^@@.*$/m).slice(1);
|
|
7908
|
+
return bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
|
|
7909
|
+
}
|
|
7716
7910
|
function describePatchFailure(patch2, detail) {
|
|
7717
7911
|
const hunks = patch2.split("\n").filter((line2) => line2.startsWith("@@"));
|
|
7718
|
-
const
|
|
7719
|
-
const contextless = bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
|
|
7720
|
-
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
7912
|
+
const hint = hunks.length > 0 && hasContextFreeHunk(patch2) ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
7721
7913
|
return `patch did not apply: ${detail}${hint}`;
|
|
7722
7914
|
}
|
|
7723
7915
|
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
7724
7916
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
7725
|
-
|
|
7726
|
-
await gitApply(workspaceDir, patch2,
|
|
7917
|
+
const zero = hasContextFreeHunk(patch2);
|
|
7918
|
+
await gitApply(workspaceDir, patch2, true, zero);
|
|
7919
|
+
await gitApply(workspaceDir, patch2, false, zero);
|
|
7727
7920
|
for (const path of paths2) {
|
|
7728
7921
|
try {
|
|
7729
7922
|
const info = await lstat2(resolveCodePath(workspaceDir, path));
|
|
@@ -7735,9 +7928,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
|
7735
7928
|
}
|
|
7736
7929
|
}
|
|
7737
7930
|
}
|
|
7738
|
-
function gitApply(cwd, patch2, check) {
|
|
7931
|
+
function gitApply(cwd, patch2, check, unidiffZero = false) {
|
|
7739
7932
|
return new Promise((accept, reject) => {
|
|
7740
|
-
const args = [
|
|
7933
|
+
const args = [
|
|
7934
|
+
"apply",
|
|
7935
|
+
"--recount",
|
|
7936
|
+
...unidiffZero ? ["--unidiff-zero"] : [],
|
|
7937
|
+
"--whitespace=nowarn",
|
|
7938
|
+
...check ? ["--check"] : [],
|
|
7939
|
+
"-"
|
|
7940
|
+
];
|
|
7741
7941
|
const child = spawn4("git", args, {
|
|
7742
7942
|
cwd,
|
|
7743
7943
|
shell: false,
|
|
@@ -7746,8 +7946,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
7746
7946
|
});
|
|
7747
7947
|
let stderr2 = "";
|
|
7748
7948
|
child.stderr.setEncoding("utf8");
|
|
7749
|
-
child.stderr.on("data", (
|
|
7750
|
-
if (stderr2.length < 4e3) stderr2 +=
|
|
7949
|
+
child.stderr.on("data", (text4) => {
|
|
7950
|
+
if (stderr2.length < 4e3) stderr2 += text4.slice(0, 4e3);
|
|
7751
7951
|
});
|
|
7752
7952
|
child.once("error", reject);
|
|
7753
7953
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
|
|
@@ -7993,7 +8193,7 @@ function validate2(input) {
|
|
|
7993
8193
|
maximumFiles: policy.maximumFiles ?? 2e4,
|
|
7994
8194
|
maximumBytes: policy.maximumBytes ?? 512 * 1024 * 1024
|
|
7995
8195
|
};
|
|
7996
|
-
if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !
|
|
8196
|
+
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)) {
|
|
7997
8197
|
throw new TypeError("clean verification policy exceeds its bounds");
|
|
7998
8198
|
}
|
|
7999
8199
|
return result;
|
|
@@ -8044,7 +8244,7 @@ function hashFile(path) {
|
|
|
8044
8244
|
});
|
|
8045
8245
|
}
|
|
8046
8246
|
function checkedResult(result, maximumOutputBytes) {
|
|
8047
|
-
if (!
|
|
8247
|
+
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") {
|
|
8048
8248
|
throw new TypeError("recipe executor returned an invalid result");
|
|
8049
8249
|
}
|
|
8050
8250
|
const bytes = Buffer.byteLength(result.stdout) + Buffer.byteLength(result.stderr);
|
|
@@ -8094,7 +8294,7 @@ function digestJson(value2) {
|
|
|
8094
8294
|
function digestBytes(value2) {
|
|
8095
8295
|
return `sha256:${createHash22("sha256").update(value2).digest("hex")}`;
|
|
8096
8296
|
}
|
|
8097
|
-
function
|
|
8297
|
+
function integer2(value2, minimum, maximum) {
|
|
8098
8298
|
return Number.isSafeInteger(value2) && value2 >= minimum && value2 <= maximum;
|
|
8099
8299
|
}
|
|
8100
8300
|
async function prepareRuntimeCheckpoint(input) {
|
|
@@ -8121,9 +8321,9 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
8121
8321
|
if (evidence.receipt.outcome === "passed") {
|
|
8122
8322
|
verification = evidence.receipt;
|
|
8123
8323
|
review = await input.review(patch2, verification);
|
|
8124
|
-
note = review
|
|
8324
|
+
note = describeReview(review);
|
|
8125
8325
|
} else {
|
|
8126
|
-
note =
|
|
8326
|
+
note = describeGateFailure(evidence);
|
|
8127
8327
|
}
|
|
8128
8328
|
} catch (cause) {
|
|
8129
8329
|
note = `Candidate verification or review failed closed: ${message(cause)}`;
|
|
@@ -8148,6 +8348,23 @@ async function prepareRuntimeCheckpoint(input) {
|
|
|
8148
8348
|
});
|
|
8149
8349
|
return { checkpoint, verification, review, note };
|
|
8150
8350
|
}
|
|
8351
|
+
function describeReview(review) {
|
|
8352
|
+
const findings = review.findings.map((finding) => ` - [${finding.severity}] ${finding.detail}`).join("\n");
|
|
8353
|
+
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.`;
|
|
8354
|
+
return [head, review.summary, findings].filter(Boolean).join("\n").slice(0, 4e3);
|
|
8355
|
+
}
|
|
8356
|
+
function describeGateFailure(evidence) {
|
|
8357
|
+
const failed = evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed");
|
|
8358
|
+
const detail = failed.map((recipe2) => {
|
|
8359
|
+
const log = evidence.logs.find((entry) => entry.recipeId === recipe2.recipeId);
|
|
8360
|
+
const output = `${log?.stdout ?? ""}
|
|
8361
|
+
${log?.stderr ?? ""}`.trim();
|
|
8362
|
+
return `${recipe2.recipeId}=${recipe2.status}${output ? `
|
|
8363
|
+
${output.slice(0, 1500)}` : ""}`;
|
|
8364
|
+
}).join("\n\n");
|
|
8365
|
+
return `Clean verification failed. Fix this and checkpoint again:
|
|
8366
|
+
${detail}`.slice(0, 4e3);
|
|
8367
|
+
}
|
|
8151
8368
|
var message = (value2) => (value2 instanceof Error ? value2.message : String(value2)).slice(0, 500);
|
|
8152
8369
|
var CodeRuntimeCheckpointManager = class {
|
|
8153
8370
|
constructor(options) {
|
|
@@ -9166,8 +9383,14 @@ function optionalInteger(value2) {
|
|
|
9166
9383
|
if (!Number.isSafeInteger(value2) || value2 < 1) throw new TypeError("line bounds must be positive integers");
|
|
9167
9384
|
return value2;
|
|
9168
9385
|
}
|
|
9386
|
+
var DEFAULT_FAILURE_REASON2 = {
|
|
9387
|
+
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
9388
|
+
"review sessions are read-only": "review session is read-only; workspace changes are not permitted"
|
|
9389
|
+
};
|
|
9169
9390
|
function response(request3, ok, content2, details) {
|
|
9170
|
-
return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
9391
|
+
if (ok) return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
9392
|
+
const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content2] || content2 || "tool request failed; inspect the tool input and workspace state";
|
|
9393
|
+
return { requestId: request3.requestId, ok, content: content2, details: { ...details, failureReason } };
|
|
9171
9394
|
}
|
|
9172
9395
|
var cache = /* @__PURE__ */ new Map();
|
|
9173
9396
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
@@ -9743,120 +9966,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
9743
9966
|
}
|
|
9744
9967
|
var digestRuntimeValue = (value2) => `sha256:${createHash32("sha256").update(value2).digest("hex")}`;
|
|
9745
9968
|
var runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
|
|
9746
|
-
var text3 = (value2, maximum) => {
|
|
9747
|
-
if (typeof value2 !== "string") return void 0;
|
|
9748
|
-
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
9749
|
-
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
9750
|
-
};
|
|
9751
|
-
var integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
9752
|
-
var record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
9753
|
-
var excerpt = (value2, tail = false) => {
|
|
9754
|
-
if (typeof value2 !== "string") return void 0;
|
|
9755
|
-
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
9756
|
-
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
9757
|
-
if (!source) return void 0;
|
|
9758
|
-
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
9759
|
-
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
9760
|
-
return text3(selected.join("\n"), 2400);
|
|
9761
|
-
};
|
|
9762
|
-
var paths = (value2) => {
|
|
9763
|
-
if (!Array.isArray(value2)) return void 0;
|
|
9764
|
-
const items = value2.flatMap((item) => {
|
|
9765
|
-
const path = text3(item, 1024);
|
|
9766
|
-
return path ? [path] : [];
|
|
9767
|
-
}).slice(0, 12);
|
|
9768
|
-
return items.length ? items : void 0;
|
|
9769
|
-
};
|
|
9770
|
-
function patchStats(value2) {
|
|
9771
|
-
if (typeof value2 !== "string") return {};
|
|
9772
|
-
let additions = 0;
|
|
9773
|
-
let deletions = 0;
|
|
9774
|
-
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
9775
|
-
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
9776
|
-
if (line2.startsWith("+")) additions += 1;
|
|
9777
|
-
else if (line2.startsWith("-")) deletions += 1;
|
|
9778
|
-
}
|
|
9779
|
-
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
9780
|
-
}
|
|
9781
|
-
function searchResults(value2) {
|
|
9782
|
-
if (typeof value2 !== "string") return void 0;
|
|
9783
|
-
const results = value2.split("\n").flatMap((line2) => {
|
|
9784
|
-
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
9785
|
-
if (!match) return [];
|
|
9786
|
-
const lineNumber = Number(match[2]);
|
|
9787
|
-
const itemText = text3(match[3], 240);
|
|
9788
|
-
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
9789
|
-
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
9790
|
-
}).slice(0, 5);
|
|
9791
|
-
return results.length ? results : void 0;
|
|
9792
|
-
}
|
|
9793
|
-
function codeToolRequestPresentation(request3) {
|
|
9794
|
-
const input = request3.input;
|
|
9795
|
-
if (request3.tool === "sandbox.read") {
|
|
9796
|
-
const path = text3(input.path, 1024);
|
|
9797
|
-
if (!path) return void 0;
|
|
9798
|
-
const startLine = integer2(input.startLine);
|
|
9799
|
-
const endLine = integer2(input.endLine);
|
|
9800
|
-
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
9801
|
-
}
|
|
9802
|
-
if (request3.tool === "sandbox.list") {
|
|
9803
|
-
const scope = text3(input.prefix, 1024);
|
|
9804
|
-
return { kind: "list", ...scope ? { scope } : {} };
|
|
9805
|
-
}
|
|
9806
|
-
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
9807
|
-
const query = text3(input.query, 512);
|
|
9808
|
-
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
9809
|
-
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
9810
|
-
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
9811
|
-
}
|
|
9812
|
-
if (request3.tool === "sandbox.apply_patch") {
|
|
9813
|
-
return { kind: "patch", ...patchStats(input.patch) };
|
|
9814
|
-
}
|
|
9815
|
-
const recipeId = text3(input.recipeId, 120);
|
|
9816
|
-
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
9817
|
-
}
|
|
9818
|
-
function codeToolResultPresentation(request3, response2) {
|
|
9819
|
-
const started = codeToolRequestPresentation(request3);
|
|
9820
|
-
if (!started || !response2.ok) return started;
|
|
9821
|
-
const details = record32(response2.details);
|
|
9822
|
-
if (started.kind === "read") {
|
|
9823
|
-
return {
|
|
9824
|
-
...started,
|
|
9825
|
-
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
9826
|
-
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
9827
|
-
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
9828
|
-
};
|
|
9829
|
-
}
|
|
9830
|
-
if (started.kind === "list") {
|
|
9831
|
-
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);
|
|
9832
|
-
return {
|
|
9833
|
-
...started,
|
|
9834
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9835
|
-
...listed.length ? { paths: listed } : {}
|
|
9836
|
-
};
|
|
9837
|
-
}
|
|
9838
|
-
if (started.kind === "query") {
|
|
9839
|
-
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
9840
|
-
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
9841
|
-
return {
|
|
9842
|
-
...started,
|
|
9843
|
-
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9844
|
-
...results ? { results } : {},
|
|
9845
|
-
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
9846
|
-
};
|
|
9847
|
-
}
|
|
9848
|
-
if (started.kind === "patch") {
|
|
9849
|
-
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
9850
|
-
}
|
|
9851
|
-
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
9852
|
-
return {
|
|
9853
|
-
...started,
|
|
9854
|
-
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
9855
|
-
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
9856
|
-
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
9857
|
-
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
9858
|
-
};
|
|
9859
|
-
}
|
|
9860
9969
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
9861
9970
|
let settle;
|
|
9862
9971
|
let settled = false;
|
|
@@ -9874,6 +9983,55 @@ function codeRuntimeAcknowledgementGate(signal) {
|
|
|
9874
9983
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
9875
9984
|
return { ready, release };
|
|
9876
9985
|
}
|
|
9986
|
+
function observeCodeRuntimeSessionSkills(command, skills, emit4) {
|
|
9987
|
+
return skills.map((skill) => ({
|
|
9988
|
+
...skill,
|
|
9989
|
+
tools: skill.tools.map((tool) => {
|
|
9990
|
+
if (!tool.handler) return tool;
|
|
9991
|
+
const handler = tool.handler;
|
|
9992
|
+
return {
|
|
9993
|
+
...tool,
|
|
9994
|
+
handler: async (input, context) => {
|
|
9995
|
+
const startedAt = Date.now();
|
|
9996
|
+
const operationId = digestRuntimeValue(
|
|
9997
|
+
`${command.commandId}:${skill.name}:${tool.name}:${context.toolCallId ?? "missing"}`
|
|
9998
|
+
);
|
|
9999
|
+
await emit4({
|
|
10000
|
+
type: "collaboration",
|
|
10001
|
+
phase: "started",
|
|
10002
|
+
skill: skill.name,
|
|
10003
|
+
tool: tool.name,
|
|
10004
|
+
operationId
|
|
10005
|
+
}).catch(() => void 0);
|
|
10006
|
+
try {
|
|
10007
|
+
const output = await handler(input, context);
|
|
10008
|
+
await emit4({
|
|
10009
|
+
type: "collaboration",
|
|
10010
|
+
phase: "completed",
|
|
10011
|
+
skill: skill.name,
|
|
10012
|
+
tool: tool.name,
|
|
10013
|
+
operationId,
|
|
10014
|
+
ok: output.isError !== true,
|
|
10015
|
+
durationMs: Date.now() - startedAt
|
|
10016
|
+
}).catch(() => void 0);
|
|
10017
|
+
return output;
|
|
10018
|
+
} catch (cause) {
|
|
10019
|
+
await emit4({
|
|
10020
|
+
type: "collaboration",
|
|
10021
|
+
phase: "completed",
|
|
10022
|
+
skill: skill.name,
|
|
10023
|
+
tool: tool.name,
|
|
10024
|
+
operationId,
|
|
10025
|
+
ok: false,
|
|
10026
|
+
durationMs: Date.now() - startedAt
|
|
10027
|
+
}).catch(() => void 0);
|
|
10028
|
+
throw cause;
|
|
10029
|
+
}
|
|
10030
|
+
}
|
|
10031
|
+
};
|
|
10032
|
+
})
|
|
10033
|
+
}));
|
|
10034
|
+
}
|
|
9877
10035
|
var TheseusRuntimeEngine = class {
|
|
9878
10036
|
constructor(options) {
|
|
9879
10037
|
this.options = options;
|
|
@@ -10070,7 +10228,7 @@ var TheseusRuntimeEngine = class {
|
|
|
10070
10228
|
event: (event) => this.#event(command, event, active.conversationRefs)
|
|
10071
10229
|
});
|
|
10072
10230
|
await this.#event(command, { type: "status", status: "running" }, active.conversationRefs);
|
|
10073
|
-
const extraSkills = await sessionSkillsFor(this.options, command);
|
|
10231
|
+
const extraSkills = observeCodeRuntimeSessionSkills(command, await sessionSkillsFor(this.options, command), (event) => this.#event(command, event, active.conversationRefs));
|
|
10074
10232
|
const result = await this.#attempt({
|
|
10075
10233
|
inference,
|
|
10076
10234
|
broker,
|
|
@@ -10109,36 +10267,11 @@ var TheseusRuntimeEngine = class {
|
|
|
10109
10267
|
}
|
|
10110
10268
|
/** Report every brokered effect as it starts and finishes. */
|
|
10111
10269
|
#observed(command, active, broker) {
|
|
10112
|
-
return {
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
await this.#event(
|
|
10118
|
-
command,
|
|
10119
|
-
{
|
|
10120
|
-
type: "tool",
|
|
10121
|
-
phase: "started",
|
|
10122
|
-
tool: request3.tool,
|
|
10123
|
-
operationId,
|
|
10124
|
-
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
10125
|
-
},
|
|
10126
|
-
active.conversationRefs
|
|
10127
|
-
).catch(() => void 0);
|
|
10128
|
-
const response2 = await broker.execute(context, request3);
|
|
10129
|
-
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
10130
|
-
await this.#event(command, {
|
|
10131
|
-
type: "tool",
|
|
10132
|
-
phase: "completed",
|
|
10133
|
-
tool: request3.tool,
|
|
10134
|
-
ok: response2.ok,
|
|
10135
|
-
durationMs: Date.now() - startedAt,
|
|
10136
|
-
operationId,
|
|
10137
|
-
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
10138
|
-
}, active.conversationRefs).catch(() => void 0);
|
|
10139
|
-
return response2;
|
|
10140
|
-
}
|
|
10141
|
-
};
|
|
10270
|
+
return observedBroker({
|
|
10271
|
+
broker,
|
|
10272
|
+
operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
|
|
10273
|
+
emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
|
|
10274
|
+
});
|
|
10142
10275
|
}
|
|
10143
10276
|
async #checkpoint(command) {
|
|
10144
10277
|
const active = this.#active.get(command.sessionId);
|
|
@@ -10448,6 +10581,19 @@ function digestText(value2) {
|
|
|
10448
10581
|
// src/code-runtime-config.ts
|
|
10449
10582
|
var CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
|
|
10450
10583
|
var CODE_BUILD_RECIPES = Object.freeze([{
|
|
10584
|
+
id: "odla-code-gates",
|
|
10585
|
+
image: CODE_NODE_IMAGE,
|
|
10586
|
+
// Selects the repository's fast, no-capability gates from the same manifest
|
|
10587
|
+
// CI and `npm run preflight` read, so a new gate is picked up without a
|
|
10588
|
+
// second list to keep in sync. The container has no shell: this is argv.
|
|
10589
|
+
command: ["node", "scripts/recipe-gates.mjs"],
|
|
10590
|
+
timeoutMs: 18e4,
|
|
10591
|
+
maxOutputBytes: 1024 * 1024,
|
|
10592
|
+
cpus: 1,
|
|
10593
|
+
// check:secrets holds ~2,500 source files in memory at once.
|
|
10594
|
+
memory: "1g",
|
|
10595
|
+
pids: 128
|
|
10596
|
+
}, {
|
|
10451
10597
|
id: "odla-code-contracts",
|
|
10452
10598
|
image: CODE_NODE_IMAGE,
|
|
10453
10599
|
command: [
|
|
@@ -10627,20 +10773,20 @@ async function runCodeRuntime(input) {
|
|
|
10627
10773
|
}
|
|
10628
10774
|
}
|
|
10629
10775
|
function parseConnection(value2, appId, appEnv) {
|
|
10630
|
-
const root =
|
|
10631
|
-
const host =
|
|
10632
|
-
const offer =
|
|
10633
|
-
const binding =
|
|
10776
|
+
const root = record7(value2);
|
|
10777
|
+
const host = record7(root?.host);
|
|
10778
|
+
const offer = record7(root?.offer);
|
|
10779
|
+
const binding = record7(root?.binding);
|
|
10634
10780
|
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)) {
|
|
10635
10781
|
throw new Error("connect Code host returned an invalid response");
|
|
10636
10782
|
}
|
|
10637
10783
|
return root;
|
|
10638
10784
|
}
|
|
10639
10785
|
function apiFailure(action2, status, value2) {
|
|
10640
|
-
const message2 =
|
|
10786
|
+
const message2 = record7(record7(value2)?.error)?.message;
|
|
10641
10787
|
return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
|
|
10642
10788
|
}
|
|
10643
|
-
function
|
|
10789
|
+
function record7(value2) {
|
|
10644
10790
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10645
10791
|
}
|
|
10646
10792
|
|
|
@@ -11146,6 +11292,14 @@ Usage:
|
|
|
11146
11292
|
odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
|
|
11147
11293
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
11148
11294
|
odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
|
|
11295
|
+
odla-ai promotion plan --file <promotion-plan.json> [--env dev] [--json]
|
|
11296
|
+
odla-ai promotion inspect <plan-id> [--env dev] [--json]
|
|
11297
|
+
odla-ai promotion status <plan-id> [--env dev] [--json]
|
|
11298
|
+
odla-ai promotion apply <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
|
|
11299
|
+
odla-ai promotion rollback <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
|
|
11300
|
+
odla-ai promotion next <plan-id> <operation-id> [--env dev] [--json]
|
|
11301
|
+
odla-ai promotion record <plan-id> <operation-id> <step-id> --file <step-receipt.json> [--env dev] [--json]
|
|
11302
|
+
odla-ai promotion cancel <plan-id> <operation-id> [--env dev] [--json]
|
|
11149
11303
|
odla-ai operations get <operation-id> [--json]
|
|
11150
11304
|
odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
|
|
11151
11305
|
odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
|
|
@@ -11312,6 +11466,11 @@ Commands:
|
|
|
11312
11466
|
init Create a generic odla.config.mjs plus starter schema/rules files.
|
|
11313
11467
|
doctor Validate and summarize the project config without network calls.
|
|
11314
11468
|
config Diff Registry intent, freeze a CAS-bound plan, and conditionally apply its safe actions.
|
|
11469
|
+
promotion Plan and inspect exact selective-release bundles without target
|
|
11470
|
+
mutation, then queue and resume a human-approved apply or rollback
|
|
11471
|
+
from an enrolled device. Each target step is leased and receipt-
|
|
11472
|
+
journaled; approval remains bound to the immutable plan digest,
|
|
11473
|
+
target set, action, and expiry, so every mismatch fails closed.
|
|
11315
11474
|
operations Inspect or wait on one exact, durable config-operation receipt.
|
|
11316
11475
|
calendar Inspect, connect, or disconnect the live Google booking connection.
|
|
11317
11476
|
app Archive (suspend, data retained), restore, export, import, or
|
|
@@ -12187,17 +12346,17 @@ function collectEntityFields(entity, parsed, allowClear) {
|
|
|
12187
12346
|
if (entity === "task" && fields.column === "ready") fields.column = "todo";
|
|
12188
12347
|
return fields;
|
|
12189
12348
|
}
|
|
12190
|
-
function statusCol(entity,
|
|
12191
|
-
if (entity === "bug") return `${
|
|
12349
|
+
function statusCol(entity, record12) {
|
|
12350
|
+
if (entity === "bug") return `${record12.status ?? ""}/${record12.severity ?? ""}`;
|
|
12192
12351
|
if (entity === "task") {
|
|
12193
|
-
const state2 =
|
|
12194
|
-
return
|
|
12352
|
+
const state2 = record12.column === "todo" ? "ready" : String(record12.column ?? "");
|
|
12353
|
+
return record12.revision ? `${state2}; r${record12.revision}` : state2;
|
|
12195
12354
|
}
|
|
12196
|
-
return String(
|
|
12355
|
+
return String(record12.status ?? "");
|
|
12197
12356
|
}
|
|
12198
|
-
function referenceMarkup(entity,
|
|
12199
|
-
const label = (
|
|
12200
|
-
return `@[${label}](pm:${entity}/${
|
|
12357
|
+
function referenceMarkup(entity, record12) {
|
|
12358
|
+
const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
|
|
12359
|
+
return `@[${label}](pm:${entity}/${record12.id})`;
|
|
12201
12360
|
}
|
|
12202
12361
|
var STUDIO_SECTION = {
|
|
12203
12362
|
goal: "goals",
|
|
@@ -12211,13 +12370,13 @@ function studioRecordUrl(ctx, entity, id2) {
|
|
|
12211
12370
|
ctx.platformUrl
|
|
12212
12371
|
).href;
|
|
12213
12372
|
}
|
|
12214
|
-
function studioRecordLink(ctx, entity,
|
|
12215
|
-
const label = (
|
|
12216
|
-
return `[${label}](${studioRecordUrl(ctx, entity,
|
|
12373
|
+
function studioRecordLink(ctx, entity, record12) {
|
|
12374
|
+
const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
|
|
12375
|
+
return `[${label}](${studioRecordUrl(ctx, entity, record12.id)})`;
|
|
12217
12376
|
}
|
|
12218
|
-
function printRecord(ctx, entity,
|
|
12377
|
+
function printRecord(ctx, entity, record12) {
|
|
12219
12378
|
ctx.out.log(
|
|
12220
|
-
`${
|
|
12379
|
+
`${record12.id} [${statusCol(entity, record12)}] ${record12.appId} ${studioRecordLink(ctx, entity, record12)}`
|
|
12221
12380
|
);
|
|
12222
12381
|
}
|
|
12223
12382
|
function emit2(ctx, value2, human) {
|
|
@@ -12271,8 +12430,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
12271
12430
|
input,
|
|
12272
12431
|
mutationId: writeMutationId2(parsed)
|
|
12273
12432
|
});
|
|
12274
|
-
const
|
|
12275
|
-
emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity,
|
|
12433
|
+
const record12 = { id: res.id, appId, title: String(input.title) };
|
|
12434
|
+
emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record12)}`));
|
|
12276
12435
|
}
|
|
12277
12436
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12278
12437
|
var UUID_PREFIX = /^[0-9a-f-]{8,35}$/i;
|
|
@@ -12292,7 +12451,7 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12292
12451
|
"GET",
|
|
12293
12452
|
`/${entity}?limit=${limit}&offset=${offset}`
|
|
12294
12453
|
);
|
|
12295
|
-
matches.push(...page2.records.map((
|
|
12454
|
+
matches.push(...page2.records.map((record12) => record12.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
|
|
12296
12455
|
offset += page2.records.length;
|
|
12297
12456
|
if (matches.length > 1 || page2.records.length === 0 || offset >= page2.total) break;
|
|
12298
12457
|
}
|
|
@@ -12306,17 +12465,17 @@ async function resolvePmId(ctx, entity, supplied) {
|
|
|
12306
12465
|
}
|
|
12307
12466
|
async function pmGet(ctx, entity, id2) {
|
|
12308
12467
|
const resolved = await resolvePmId(ctx, entity, id2);
|
|
12309
|
-
const { record:
|
|
12310
|
-
emit2(ctx,
|
|
12468
|
+
const { record: record12 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
|
|
12469
|
+
emit2(ctx, record12, () => printRecord(ctx, entity, record12));
|
|
12311
12470
|
}
|
|
12312
12471
|
async function pmReference(ctx, entity, id2) {
|
|
12313
|
-
const { record:
|
|
12472
|
+
const { record: record12 } = await pmRequest(
|
|
12314
12473
|
ctx,
|
|
12315
12474
|
"GET",
|
|
12316
12475
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12317
12476
|
);
|
|
12318
|
-
const markup = referenceMarkup(entity,
|
|
12319
|
-
emit2(ctx, { kind: `pm:${entity}`, id:
|
|
12477
|
+
const markup = referenceMarkup(entity, record12);
|
|
12478
|
+
emit2(ctx, { kind: `pm:${entity}`, id: record12.id, label: record12.title ?? "", markup }, () => {
|
|
12320
12479
|
ctx.out.log(markup);
|
|
12321
12480
|
});
|
|
12322
12481
|
}
|
|
@@ -12401,16 +12560,16 @@ var PER_ENTITY = {
|
|
|
12401
12560
|
decision: ["status", "body", "supersedesId"],
|
|
12402
12561
|
bug: ["status", "severity", "description", "goalId", "assigneeId", "decisionId"]
|
|
12403
12562
|
};
|
|
12404
|
-
function leanRecord(entity,
|
|
12563
|
+
function leanRecord(entity, record12) {
|
|
12405
12564
|
const out = {};
|
|
12406
12565
|
for (const key of [...COMMON, ...PER_ENTITY[entity]]) {
|
|
12407
|
-
const value2 =
|
|
12566
|
+
const value2 = record12[key];
|
|
12408
12567
|
if (value2 !== void 0 && value2 !== null) out[key] = value2;
|
|
12409
12568
|
}
|
|
12410
12569
|
return out;
|
|
12411
12570
|
}
|
|
12412
12571
|
function leanRecords(entity, records, verbose) {
|
|
12413
|
-
return verbose ? records : records.map((
|
|
12572
|
+
return verbose ? records : records.map((record12) => leanRecord(entity, record12));
|
|
12414
12573
|
}
|
|
12415
12574
|
|
|
12416
12575
|
// src/pm-intake.ts
|
|
@@ -12443,16 +12602,18 @@ async function pmNext(ctx, parsed) {
|
|
|
12443
12602
|
// One request for both live columns; the split below is over open work only.
|
|
12444
12603
|
listFiltered(ctx, "task", appId, projectId, { column: `${READY_COLUMN},doing` })
|
|
12445
12604
|
]);
|
|
12446
|
-
const ready = tasks.filter((
|
|
12447
|
-
const doing = tasks.filter((
|
|
12605
|
+
const ready = tasks.filter((record12) => record12.column === READY_COLUMN);
|
|
12606
|
+
const doing = tasks.filter((record12) => record12.column === "doing");
|
|
12448
12607
|
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}`;
|
|
12608
|
+
const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
|
|
12449
12609
|
const result = {
|
|
12450
12610
|
appId,
|
|
12451
12611
|
projectId,
|
|
12452
12612
|
openGoals: leanRecords("goal", goals, verbose),
|
|
12453
12613
|
doing: leanRecords("task", doing, verbose),
|
|
12454
12614
|
ready: leanRecords("task", ready, verbose),
|
|
12455
|
-
next: guidance
|
|
12615
|
+
next: guidance,
|
|
12616
|
+
monitor
|
|
12456
12617
|
};
|
|
12457
12618
|
emit2(ctx, result, () => {
|
|
12458
12619
|
ctx.out.log(`${appId}: goal-aligned work intake (read only)`);
|
|
@@ -12463,9 +12624,10 @@ async function pmNext(ctx, parsed) {
|
|
|
12463
12624
|
]) {
|
|
12464
12625
|
ctx.out.log(`${label}:`);
|
|
12465
12626
|
if (!records.length) ctx.out.log("- (none)");
|
|
12466
|
-
else for (const
|
|
12627
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12467
12628
|
}
|
|
12468
12629
|
ctx.out.log(`next: ${guidance}`);
|
|
12630
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12469
12631
|
});
|
|
12470
12632
|
}
|
|
12471
12633
|
async function pmHandoff(ctx, parsed) {
|
|
@@ -12477,17 +12639,20 @@ async function pmHandoff(ctx, parsed) {
|
|
|
12477
12639
|
listFiltered(ctx, "bug", appId, projectId, { status: OPEN_BUG_STATUSES })
|
|
12478
12640
|
]);
|
|
12479
12641
|
const clean4 = !goals.length && !tasks.length && !bugs.length;
|
|
12642
|
+
const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
|
|
12480
12643
|
const result = {
|
|
12481
12644
|
appId,
|
|
12482
12645
|
projectId,
|
|
12483
12646
|
unmetGoals: leanRecords("goal", goals, verbose),
|
|
12484
12647
|
activeTasks: leanRecords("task", tasks, verbose),
|
|
12485
12648
|
openBugs: leanRecords("bug", bugs, verbose),
|
|
12486
|
-
clean: clean4
|
|
12649
|
+
clean: clean4,
|
|
12650
|
+
monitor
|
|
12487
12651
|
};
|
|
12488
12652
|
emit2(ctx, result, () => {
|
|
12489
12653
|
if (clean4) {
|
|
12490
12654
|
ctx.out.log(`${appId}: no unresolved PM work`);
|
|
12655
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12491
12656
|
return;
|
|
12492
12657
|
}
|
|
12493
12658
|
ctx.out.log(`${appId}: authoritative PM handoff`);
|
|
@@ -12498,8 +12663,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
12498
12663
|
]) {
|
|
12499
12664
|
ctx.out.log(`${label}:`);
|
|
12500
12665
|
if (!records.length) ctx.out.log("- (none)");
|
|
12501
|
-
else for (const
|
|
12666
|
+
else for (const record12 of records) printRecord(ctx, entity, record12);
|
|
12502
12667
|
}
|
|
12668
|
+
ctx.out.log(`monitor: ${monitor}`);
|
|
12503
12669
|
});
|
|
12504
12670
|
}
|
|
12505
12671
|
|
|
@@ -12572,14 +12738,14 @@ async function pmStart(ctx, parsed) {
|
|
|
12572
12738
|
|
|
12573
12739
|
// src/pm-links.ts
|
|
12574
12740
|
async function pmLink(ctx, entity, id2) {
|
|
12575
|
-
const { record:
|
|
12741
|
+
const { record: record12 } = await pmRequest(
|
|
12576
12742
|
ctx,
|
|
12577
12743
|
"GET",
|
|
12578
12744
|
`/${entity}/${encodeURIComponent(id2)}`
|
|
12579
12745
|
);
|
|
12580
|
-
const url = studioRecordUrl(ctx, entity,
|
|
12581
|
-
const markdown = studioRecordLink(ctx, entity,
|
|
12582
|
-
emit2(ctx, { kind: entity, id:
|
|
12746
|
+
const url = studioRecordUrl(ctx, entity, record12.id);
|
|
12747
|
+
const markdown = studioRecordLink(ctx, entity, record12);
|
|
12748
|
+
emit2(ctx, { kind: entity, id: record12.id, label: record12.title ?? "", url, markdown }, () => {
|
|
12583
12749
|
ctx.out.log(markdown);
|
|
12584
12750
|
});
|
|
12585
12751
|
}
|
|
@@ -12706,16 +12872,16 @@ async function page(ctx, appId, cursor) {
|
|
|
12706
12872
|
}
|
|
12707
12873
|
return data;
|
|
12708
12874
|
}
|
|
12709
|
-
function recordState(
|
|
12710
|
-
if (
|
|
12711
|
-
return String(
|
|
12875
|
+
function recordState(record12) {
|
|
12876
|
+
if (record12.column) return record12.column === "todo" ? "ready" : record12.column;
|
|
12877
|
+
return String(record12.status ?? "");
|
|
12712
12878
|
}
|
|
12713
12879
|
function eventRecord(event) {
|
|
12714
12880
|
return event.payload.payload;
|
|
12715
12881
|
}
|
|
12716
12882
|
function eventLabel(event) {
|
|
12717
|
-
const
|
|
12718
|
-
if (
|
|
12883
|
+
const record12 = eventRecord(event);
|
|
12884
|
+
if (record12) return String(record12.title ?? event.payload.entityId);
|
|
12719
12885
|
const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
|
|
12720
12886
|
return body || event.payload.entityId;
|
|
12721
12887
|
}
|
|
@@ -12723,10 +12889,10 @@ function report3(ctx, parsed, result) {
|
|
|
12723
12889
|
if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
|
|
12724
12890
|
else if (parsed.options.jsonl !== true && result.found) {
|
|
12725
12891
|
for (const event of result.events ?? []) {
|
|
12726
|
-
const
|
|
12727
|
-
const state2 =
|
|
12892
|
+
const record12 = eventRecord(event);
|
|
12893
|
+
const state2 = record12 ? recordState(record12) : "comment";
|
|
12728
12894
|
ctx.out.log(
|
|
12729
|
-
`${event.id} ${event.type} ${state2}${
|
|
12895
|
+
`${event.id} ${event.type} ${state2}${record12?.revision ? `; r${record12.revision}` : ""} ${eventLabel(event)}`
|
|
12730
12896
|
);
|
|
12731
12897
|
}
|
|
12732
12898
|
}
|
|
@@ -12800,8 +12966,8 @@ async function pmWatch(ctx, parsed) {
|
|
|
12800
12966
|
}
|
|
12801
12967
|
firstSuccess = false;
|
|
12802
12968
|
const matching = current.events.filter((event) => {
|
|
12803
|
-
const
|
|
12804
|
-
const state2 =
|
|
12969
|
+
const record12 = eventRecord(event);
|
|
12970
|
+
const state2 = record12 ? recordState(record12).toLowerCase() : "";
|
|
12805
12971
|
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);
|
|
12806
12972
|
});
|
|
12807
12973
|
for (const event of matching) {
|
|
@@ -13232,17 +13398,17 @@ async function platformStatus(parsed, deps) {
|
|
|
13232
13398
|
}
|
|
13233
13399
|
}
|
|
13234
13400
|
function isPlatformStatus(value2) {
|
|
13235
|
-
if (!
|
|
13236
|
-
if (!
|
|
13237
|
-
if (!
|
|
13401
|
+
if (!record8(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
|
|
13402
|
+
if (!record8(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
|
|
13403
|
+
if (!record8(value2.catalog) || !record8(value2.summary)) return false;
|
|
13238
13404
|
return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
|
|
13239
13405
|
}
|
|
13240
13406
|
function apiMessage(value2) {
|
|
13241
|
-
if (!
|
|
13242
|
-
const error =
|
|
13407
|
+
if (!record8(value2)) return "request failed";
|
|
13408
|
+
const error = record8(value2.error) ? value2.error : value2;
|
|
13243
13409
|
return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
|
|
13244
13410
|
}
|
|
13245
|
-
function
|
|
13411
|
+
function record8(value2) {
|
|
13246
13412
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
13247
13413
|
}
|
|
13248
13414
|
|
|
@@ -13283,7 +13449,7 @@ function statusVerdict(reads) {
|
|
|
13283
13449
|
severity: "degraded"
|
|
13284
13450
|
});
|
|
13285
13451
|
}
|
|
13286
|
-
const performance =
|
|
13452
|
+
const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
13287
13453
|
if (performance?.status === "unavailable") {
|
|
13288
13454
|
reasons.push({
|
|
13289
13455
|
source: "liveSync",
|
|
@@ -13364,7 +13530,7 @@ function statusVerdict(reads) {
|
|
|
13364
13530
|
reasons
|
|
13365
13531
|
};
|
|
13366
13532
|
}
|
|
13367
|
-
function
|
|
13533
|
+
function record9(value2) {
|
|
13368
13534
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13369
13535
|
}
|
|
13370
13536
|
function numeric2(value2) {
|
|
@@ -13392,7 +13558,7 @@ function printO11yStatus(status, out) {
|
|
|
13392
13558
|
out.log(
|
|
13393
13559
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
13394
13560
|
);
|
|
13395
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
13561
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record10) : [];
|
|
13396
13562
|
const requests = routes.reduce(
|
|
13397
13563
|
(total, row) => total + numeric3(row.requests),
|
|
13398
13564
|
0
|
|
@@ -13404,39 +13570,39 @@ function printO11yStatus(status, out) {
|
|
|
13404
13570
|
out.log(
|
|
13405
13571
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
13406
13572
|
);
|
|
13407
|
-
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(
|
|
13573
|
+
const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record10) : [];
|
|
13408
13574
|
out.log(
|
|
13409
13575
|
`application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
|
|
13410
13576
|
(row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
|
|
13411
13577
|
).join(", ") : "none observed"}`
|
|
13412
13578
|
);
|
|
13413
13579
|
out.log(liveSyncLine(status.liveSync));
|
|
13414
|
-
const canaryDurations =
|
|
13580
|
+
const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
13415
13581
|
out.log(
|
|
13416
13582
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
13417
13583
|
);
|
|
13418
|
-
const collectorIngest =
|
|
13419
|
-
const collectorStorage =
|
|
13584
|
+
const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
13585
|
+
const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
13420
13586
|
out.log(
|
|
13421
13587
|
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
13422
13588
|
);
|
|
13423
|
-
const providerMetrics =
|
|
13424
|
-
const providerCapacity =
|
|
13425
|
-
const workerMemory =
|
|
13589
|
+
const providerMetrics = record10(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
13590
|
+
const providerCapacity = record10(status.provider.body.capacity) ? status.provider.body.capacity : {};
|
|
13591
|
+
const workerMemory = record10(providerCapacity.memory) ? providerCapacity.memory : {};
|
|
13426
13592
|
out.log(
|
|
13427
13593
|
`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`
|
|
13428
13594
|
);
|
|
13429
13595
|
for (const line2 of providerCapacityLines(status.providerCapacity)) {
|
|
13430
13596
|
out.log(line2);
|
|
13431
13597
|
}
|
|
13432
|
-
const coverage =
|
|
13433
|
-
const coverageCounts =
|
|
13434
|
-
const coverageBudget =
|
|
13598
|
+
const coverage = record10(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
|
|
13599
|
+
const coverageCounts = record10(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
|
|
13600
|
+
const coverageBudget = record10(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
|
|
13435
13601
|
out.log(
|
|
13436
13602
|
`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`
|
|
13437
13603
|
);
|
|
13438
13604
|
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
13439
|
-
const providerFreshness =
|
|
13605
|
+
const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
13440
13606
|
out.log(
|
|
13441
13607
|
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
13442
13608
|
);
|
|
@@ -13445,17 +13611,17 @@ function printO11yStatus(status, out) {
|
|
|
13445
13611
|
);
|
|
13446
13612
|
}
|
|
13447
13613
|
function providerCapacityLines(read3) {
|
|
13448
|
-
const resources =
|
|
13449
|
-
const durableObjects =
|
|
13450
|
-
const periodic =
|
|
13451
|
-
const storage =
|
|
13452
|
-
const d1 =
|
|
13453
|
-
const d1Activity =
|
|
13454
|
-
const d1Storage =
|
|
13455
|
-
const d1Latency =
|
|
13456
|
-
const r2 =
|
|
13457
|
-
const r2Operations =
|
|
13458
|
-
const r2Storage =
|
|
13614
|
+
const resources = record10(read3.body.resources) ? read3.body.resources : {};
|
|
13615
|
+
const durableObjects = record10(resources.durableObjects) ? resources.durableObjects : {};
|
|
13616
|
+
const periodic = record10(durableObjects.periodic) ? durableObjects.periodic : {};
|
|
13617
|
+
const storage = record10(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
|
|
13618
|
+
const d1 = record10(resources.d1) ? resources.d1 : {};
|
|
13619
|
+
const d1Activity = record10(d1.activity) ? d1.activity : {};
|
|
13620
|
+
const d1Storage = record10(d1.storage) ? d1.storage : {};
|
|
13621
|
+
const d1Latency = record10(d1Activity.latency) ? d1Activity.latency : {};
|
|
13622
|
+
const r2 = record10(resources.r2) ? resources.r2 : {};
|
|
13623
|
+
const r2Operations = record10(r2.operations) ? r2.operations : {};
|
|
13624
|
+
const r2Storage = record10(r2.storage) ? r2.storage : {};
|
|
13459
13625
|
const status = String(
|
|
13460
13626
|
read3.body.status ?? read3.body.error ?? "unavailable"
|
|
13461
13627
|
);
|
|
@@ -13466,11 +13632,11 @@ function providerCapacityLines(read3) {
|
|
|
13466
13632
|
];
|
|
13467
13633
|
}
|
|
13468
13634
|
function liveSyncLine(read3) {
|
|
13469
|
-
const performance =
|
|
13470
|
-
const commitToSend =
|
|
13635
|
+
const performance = record10(read3.body.performance) ? read3.body.performance : {};
|
|
13636
|
+
const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
|
|
13471
13637
|
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`;
|
|
13472
13638
|
}
|
|
13473
|
-
function
|
|
13639
|
+
function record10(value2) {
|
|
13474
13640
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
13475
13641
|
}
|
|
13476
13642
|
function numeric3(value2) {
|
|
@@ -13808,7 +13974,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
13808
13974
|
if (action2 === "plan" || action2 === "apply") {
|
|
13809
13975
|
const desired = monitoringWireConfig(context.cfg, env);
|
|
13810
13976
|
const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
|
|
13811
|
-
const currentRevision =
|
|
13977
|
+
const currentRevision = record11(live.config) ? string(live.config.revision) : null;
|
|
13812
13978
|
const changed = currentRevision !== desired.revision;
|
|
13813
13979
|
const plan = {
|
|
13814
13980
|
schemaVersion: 1,
|
|
@@ -13853,7 +14019,7 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
13853
14019
|
headers
|
|
13854
14020
|
}, doFetch);
|
|
13855
14021
|
emit3(result2, jsonOutput, out, () => {
|
|
13856
|
-
const run =
|
|
14022
|
+
const run = record11(result2.run) ? result2.run : {};
|
|
13857
14023
|
out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
|
|
13858
14024
|
});
|
|
13859
14025
|
return;
|
|
@@ -13878,12 +14044,12 @@ async function request2(url, init, doFetch) {
|
|
|
13878
14044
|
let body = {};
|
|
13879
14045
|
try {
|
|
13880
14046
|
const parsed = text4 ? JSON.parse(text4) : {};
|
|
13881
|
-
body =
|
|
14047
|
+
body = record11(parsed) ? parsed : { value: parsed };
|
|
13882
14048
|
} catch {
|
|
13883
14049
|
body = { message: text4.slice(0, 500) };
|
|
13884
14050
|
}
|
|
13885
14051
|
if (!response2.ok) {
|
|
13886
|
-
const error =
|
|
14052
|
+
const error = record11(body.error) ? body.error : body;
|
|
13887
14053
|
throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
|
|
13888
14054
|
}
|
|
13889
14055
|
return body;
|
|
@@ -13891,7 +14057,7 @@ async function request2(url, init, doFetch) {
|
|
|
13891
14057
|
function printRead(action2, appId, env, result, out) {
|
|
13892
14058
|
if (action2 === "status") {
|
|
13893
14059
|
out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
|
|
13894
|
-
const slos = Array.isArray(result.slos) ? result.slos.filter(
|
|
14060
|
+
const slos = Array.isArray(result.slos) ? result.slos.filter(record11) : [];
|
|
13895
14061
|
for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
|
|
13896
14062
|
const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
|
|
13897
14063
|
const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
|
|
@@ -13900,20 +14066,20 @@ function printRead(action2, appId, env, result, out) {
|
|
|
13900
14066
|
return;
|
|
13901
14067
|
}
|
|
13902
14068
|
if (action2 === "incidents") {
|
|
13903
|
-
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(
|
|
14069
|
+
const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record11) : [];
|
|
13904
14070
|
out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
|
|
13905
14071
|
for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
|
|
13906
14072
|
return;
|
|
13907
14073
|
}
|
|
13908
14074
|
out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
|
|
13909
|
-
const probes = Array.isArray(result.probes) ? result.probes.filter(
|
|
14075
|
+
const probes = Array.isArray(result.probes) ? result.probes.filter(record11) : [];
|
|
13910
14076
|
for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
|
|
13911
14077
|
}
|
|
13912
14078
|
function emit3(value2, json, out, human) {
|
|
13913
14079
|
if (json) out.log(JSON.stringify(value2, null, 2));
|
|
13914
14080
|
else human();
|
|
13915
14081
|
}
|
|
13916
|
-
function
|
|
14082
|
+
function record11(value2) {
|
|
13917
14083
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
13918
14084
|
}
|
|
13919
14085
|
function string(value2) {
|
|
@@ -14404,6 +14570,122 @@ async function provision(options) {
|
|
|
14404
14570
|
}
|
|
14405
14571
|
}
|
|
14406
14572
|
|
|
14573
|
+
// src/promotion-command.ts
|
|
14574
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
14575
|
+
import { createAppsClient as createAppsClient4 } from "@odla-ai/apps";
|
|
14576
|
+
|
|
14577
|
+
// src/security-command-context.ts
|
|
14578
|
+
async function hostedSecurityContext(parsed, dependencies) {
|
|
14579
|
+
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
14580
|
+
const cfg = await loadProjectConfig(configPath);
|
|
14581
|
+
const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
14582
|
+
if (!env || !cfg.envs.includes(env)) {
|
|
14583
|
+
throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
|
|
14584
|
+
}
|
|
14585
|
+
const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
|
|
14586
|
+
if (platformAudience(cfg.platformUrl) !== platform) {
|
|
14587
|
+
throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
|
|
14588
|
+
}
|
|
14589
|
+
const doFetch = dependencies.fetch ?? fetch;
|
|
14590
|
+
const stdout = dependencies.stdout ?? console;
|
|
14591
|
+
const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
|
|
14592
|
+
const token = await getDeveloperToken(
|
|
14593
|
+
cfg,
|
|
14594
|
+
{ configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
|
|
14595
|
+
doFetch,
|
|
14596
|
+
stdout,
|
|
14597
|
+
{ optionalProjectCapabilities: ["app.manage"] }
|
|
14598
|
+
);
|
|
14599
|
+
return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
|
|
14600
|
+
}
|
|
14601
|
+
function requiredSecurityPositional(parsed, index, label) {
|
|
14602
|
+
const value2 = parsed.positionals[index];
|
|
14603
|
+
if (!value2) throw new Error(`${label} is required`);
|
|
14604
|
+
return value2;
|
|
14605
|
+
}
|
|
14606
|
+
function securityProfile(value2) {
|
|
14607
|
+
if (value2 === void 0) return void 0;
|
|
14608
|
+
if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
|
|
14609
|
+
throw new Error("--profile must be odla, cloudflare-app, or generic");
|
|
14610
|
+
}
|
|
14611
|
+
|
|
14612
|
+
// src/promotion-command.ts
|
|
14613
|
+
var render = (value2, json, out) => {
|
|
14614
|
+
if (json) out.log(JSON.stringify(value2));
|
|
14615
|
+
else out.log(JSON.stringify(value2, null, 2));
|
|
14616
|
+
};
|
|
14617
|
+
async function promotionCommand(parsed, dependencies) {
|
|
14618
|
+
const action2 = parsed.positionals[1];
|
|
14619
|
+
if (!action2 || !["plan", "inspect", "status", "apply", "rollback", "next", "record", "cancel"].includes(action2)) rejectWord(["promotion"], action2);
|
|
14620
|
+
assertArgs(parsed, ["config", "env", "platform", "email", "open", "json", "file", "approval", "idempotency-key"], 5);
|
|
14621
|
+
const context = await hostedSecurityContext(parsed, dependencies);
|
|
14622
|
+
const client = createAppsClient4({
|
|
14623
|
+
token: context.token,
|
|
14624
|
+
fetcher: { fetch: (request3, init) => {
|
|
14625
|
+
const internal = new URL(typeof request3 === "string" ? request3 : request3.url);
|
|
14626
|
+
return context.fetch(`${context.platform}${internal.pathname}${internal.search}`, init);
|
|
14627
|
+
} }
|
|
14628
|
+
});
|
|
14629
|
+
const json = parsed.options.json === true;
|
|
14630
|
+
if (action2 === "plan") {
|
|
14631
|
+
if (parsed.positionals[2]) throw new Error("promotion plan takes --file, not a positional plan id");
|
|
14632
|
+
const file = stringOpt(parsed.options.file);
|
|
14633
|
+
if (!file) throw new Error("--file <promotion-plan.json> is required");
|
|
14634
|
+
const source = JSON.parse(await readFile5(file, "utf8"));
|
|
14635
|
+
const plan2 = await client.createPromotionReleasePlan(context.appId, source);
|
|
14636
|
+
render(plan2, json, context.stdout);
|
|
14637
|
+
return;
|
|
14638
|
+
}
|
|
14639
|
+
const planId = requiredSecurityPositional(parsed, 2, "plan id");
|
|
14640
|
+
if (action2 === "inspect") {
|
|
14641
|
+
if (parsed.positionals[3]) throw new Error("promotion inspect takes only a plan id");
|
|
14642
|
+
const plan2 = await client.getPromotionReleasePlan(context.appId, planId);
|
|
14643
|
+
if (!plan2) throw new Error(`promotion release ${planId} was not found`);
|
|
14644
|
+
render(plan2, json, context.stdout);
|
|
14645
|
+
return;
|
|
14646
|
+
}
|
|
14647
|
+
if (action2 === "status") {
|
|
14648
|
+
if (parsed.positionals[3]) throw new Error("promotion status takes only a plan id");
|
|
14649
|
+
const status = await client.getPromotionReleaseStatus(context.appId, planId);
|
|
14650
|
+
if (!status) throw new Error(`promotion release ${planId} was not found`);
|
|
14651
|
+
render(status, json, context.stdout);
|
|
14652
|
+
return;
|
|
14653
|
+
}
|
|
14654
|
+
if (action2 === "next" || action2 === "cancel" || action2 === "record") {
|
|
14655
|
+
const operationId = requiredSecurityPositional(parsed, 3, "operation id");
|
|
14656
|
+
if (action2 === "next") {
|
|
14657
|
+
if (parsed.positionals[4]) throw new Error("promotion next takes only plan and operation ids");
|
|
14658
|
+
render(await client.claimPromotionReleaseWork(context.appId, planId, operationId), json, context.stdout);
|
|
14659
|
+
return;
|
|
14660
|
+
}
|
|
14661
|
+
if (action2 === "cancel") {
|
|
14662
|
+
if (parsed.positionals[4]) throw new Error("promotion cancel takes only plan and operation ids");
|
|
14663
|
+
render(await client.cancelPromotionReleaseOperation(context.appId, planId, operationId), json, context.stdout);
|
|
14664
|
+
return;
|
|
14665
|
+
}
|
|
14666
|
+
const stepId = requiredSecurityPositional(parsed, 4, "step id");
|
|
14667
|
+
const file = stringOpt(parsed.options.file);
|
|
14668
|
+
if (!file) throw new Error("--file <step-receipt.json> is required");
|
|
14669
|
+
const receipt = JSON.parse(await readFile5(file, "utf8"));
|
|
14670
|
+
render(await client.recordPromotionReleaseWork(context.appId, planId, operationId, stepId, receipt), json, context.stdout);
|
|
14671
|
+
return;
|
|
14672
|
+
}
|
|
14673
|
+
const approvalId = stringOpt(parsed.options.approval);
|
|
14674
|
+
if (parsed.positionals[3]) throw new Error(`promotion ${action2} takes only a plan id`);
|
|
14675
|
+
if (!approvalId) throw new Error(`--approval <approval-id> is required for promotion ${action2}`);
|
|
14676
|
+
const plan = await client.getPromotionReleasePlan(context.appId, planId);
|
|
14677
|
+
if (!plan) throw new Error(`promotion release ${planId} was not found`);
|
|
14678
|
+
if (plan.state !== "reviewable") throw new Error(`promotion release is ${plan.state}: ${plan.blockers.join("; ")}`);
|
|
14679
|
+
const operationAction = action2;
|
|
14680
|
+
const operation = await client.requestPromotionReleaseOperation(context.appId, planId, operationAction, {
|
|
14681
|
+
approvalId,
|
|
14682
|
+
planDigest: plan.planDigest,
|
|
14683
|
+
targetSetDigest: plan.targetSetDigest,
|
|
14684
|
+
idempotencyKey: stringOpt(parsed.options["idempotency-key"]) ?? `${operationAction}:${planId}`
|
|
14685
|
+
});
|
|
14686
|
+
render(operation, json, context.stdout);
|
|
14687
|
+
}
|
|
14688
|
+
|
|
14407
14689
|
// src/record.ts
|
|
14408
14690
|
import { appendFileSync } from "fs";
|
|
14409
14691
|
import process18 from "process";
|
|
@@ -14691,7 +14973,7 @@ async function bySlug(ctx, slug) {
|
|
|
14691
14973
|
"GET",
|
|
14692
14974
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
14693
14975
|
);
|
|
14694
|
-
const filtered = page2.records.find((
|
|
14976
|
+
const filtered = page2.records.find((record12) => record12.slug === slug);
|
|
14695
14977
|
if (filtered) return filtered;
|
|
14696
14978
|
const limit = 100;
|
|
14697
14979
|
for (let offset = 0; ; offset += limit) {
|
|
@@ -14700,7 +14982,7 @@ async function bySlug(ctx, slug) {
|
|
|
14700
14982
|
"GET",
|
|
14701
14983
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
14702
14984
|
);
|
|
14703
|
-
const found = fallback.records.find((
|
|
14985
|
+
const found = fallback.records.find((record12) => record12.slug === slug);
|
|
14704
14986
|
if (found) return found;
|
|
14705
14987
|
if (!fallback.records.length || offset + fallback.records.length >= fallback.total) break;
|
|
14706
14988
|
}
|
|
@@ -15500,41 +15782,6 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15500
15782
|
}
|
|
15501
15783
|
}
|
|
15502
15784
|
|
|
15503
|
-
// src/security-command-context.ts
|
|
15504
|
-
async function hostedSecurityContext(parsed, dependencies) {
|
|
15505
|
-
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
15506
|
-
const cfg = await loadProjectConfig(configPath);
|
|
15507
|
-
const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
15508
|
-
if (!env || !cfg.envs.includes(env)) {
|
|
15509
|
-
throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
|
|
15510
|
-
}
|
|
15511
|
-
const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
|
|
15512
|
-
if (platformAudience(cfg.platformUrl) !== platform) {
|
|
15513
|
-
throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
|
|
15514
|
-
}
|
|
15515
|
-
const doFetch = dependencies.fetch ?? fetch;
|
|
15516
|
-
const stdout = dependencies.stdout ?? console;
|
|
15517
|
-
const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
|
|
15518
|
-
const token = await getDeveloperToken(
|
|
15519
|
-
cfg,
|
|
15520
|
-
{ configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
|
|
15521
|
-
doFetch,
|
|
15522
|
-
stdout,
|
|
15523
|
-
{ optionalProjectCapabilities: ["app.manage"] }
|
|
15524
|
-
);
|
|
15525
|
-
return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
|
|
15526
|
-
}
|
|
15527
|
-
function requiredSecurityPositional(parsed, index, label) {
|
|
15528
|
-
const value2 = parsed.positionals[index];
|
|
15529
|
-
if (!value2) throw new Error(`${label} is required`);
|
|
15530
|
-
return value2;
|
|
15531
|
-
}
|
|
15532
|
-
function securityProfile(value2) {
|
|
15533
|
-
if (value2 === void 0) return void 0;
|
|
15534
|
-
if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
|
|
15535
|
-
throw new Error("--profile must be odla, cloudflare-app, or generic");
|
|
15536
|
-
}
|
|
15537
|
-
|
|
15538
15785
|
// src/security-command-output.ts
|
|
15539
15786
|
function printHostedSecurityPlan(out, plan, appId) {
|
|
15540
15787
|
out.log(`Hosted security plan for ${appId}/${plan.env}: ${plan.ready ? "ready" : "not ready"}`);
|
|
@@ -16272,6 +16519,10 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16272
16519
|
await securityCommand(parsed, runtime);
|
|
16273
16520
|
return;
|
|
16274
16521
|
}
|
|
16522
|
+
if (command === "promotion") {
|
|
16523
|
+
await promotionCommand(parsed, runtime);
|
|
16524
|
+
return;
|
|
16525
|
+
}
|
|
16275
16526
|
if (command === "brand") {
|
|
16276
16527
|
await brandCommand(parsed, runtime);
|
|
16277
16528
|
return;
|
|
@@ -16436,4 +16687,4 @@ export {
|
|
|
16436
16687
|
isTerminalHostedSecurityStatus,
|
|
16437
16688
|
runCli
|
|
16438
16689
|
};
|
|
16439
|
-
//# sourceMappingURL=chunk-
|
|
16690
|
+
//# sourceMappingURL=chunk-BD5KIG63.js.map
|