@odla-ai/cli 0.46.12 → 0.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -1783,6 +1783,7 @@ var init_surface = __esm({
1783
1783
  watch: {}
1784
1784
  },
1785
1785
  provision: {},
1786
+ promotion: { plan: {}, inspect: {}, status: {}, apply: {}, rollback: {}, next: {}, record: {}, cancel: {} },
1786
1787
  runbook: {
1787
1788
  ask: {},
1788
1789
  search: {},
@@ -4270,9 +4271,9 @@ function canonicalValue(value2) {
4270
4271
  }
4271
4272
  if (Array.isArray(value2)) return value2.map(canonicalValue);
4272
4273
  if (value2 && typeof value2 === "object") {
4273
- const record11 = value2;
4274
+ const record12 = value2;
4274
4275
  return Object.fromEntries(
4275
- Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, canonicalValue(record11[key])])
4276
+ Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, canonicalValue(record12[key])])
4276
4277
  );
4277
4278
  }
4278
4279
  throw new TypeError("canonical JSON rejects unsupported values");
@@ -6878,17 +6879,17 @@ var init_cli_project = __esm({
6878
6879
  }
6879
6880
  });
6880
6881
 
6881
- // ../harness/dist/chunk-I43KTCJ2.js
6882
+ // ../harness/dist/chunk-U324RQ4N.js
6882
6883
  var HARNESS_PROTOCOL_VERSION;
6883
- var init_chunk_I43KTCJ2 = __esm({
6884
- "../harness/dist/chunk-I43KTCJ2.js"() {
6884
+ var init_chunk_U324RQ4N = __esm({
6885
+ "../harness/dist/chunk-U324RQ4N.js"() {
6885
6886
  "use strict";
6886
6887
  init_cjs_shims();
6887
6888
  HARNESS_PROTOCOL_VERSION = 1;
6888
6889
  }
6889
6890
  });
6890
6891
 
6891
- // ../harness/dist/chunk-HDIR4MM5.js
6892
+ // ../harness/dist/chunk-5LRYJKUI.js
6892
6893
  function assertPinnedImage(image) {
6893
6894
  if (!DIGEST_IMAGE.test(image)) throw new TypeError("container image must be pinned by sha256 digest");
6894
6895
  }
@@ -7032,8 +7033,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
7032
7033
  const maxFiles = options.maxFiles ?? 2e4;
7033
7034
  const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
7034
7035
  const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
7035
- const entries = inventory.flatMap((record11) => {
7036
- const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record11);
7036
+ const entries = inventory.flatMap((record12) => {
7037
+ const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record12);
7037
7038
  return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
7038
7039
  });
7039
7040
  if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
@@ -7237,8 +7238,8 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
7237
7238
  }
7238
7239
  }
7239
7240
  var import_child_process, import_fs, import_promises2, import_path, import_process, import_promises3, import_os, import_path2, import_child_process2, import_path3, import_promises4, import_os2, import_path4, import_child_process3, DIGEST_IMAGE, SKIP_WORKSPACE_DIRS, SECRET_WORKSPACE_FILE;
7240
- var init_chunk_HDIR4MM5 = __esm({
7241
- "../harness/dist/chunk-HDIR4MM5.js"() {
7241
+ var init_chunk_5LRYJKUI = __esm({
7242
+ "../harness/dist/chunk-5LRYJKUI.js"() {
7242
7243
  "use strict";
7243
7244
  init_cjs_shims();
7244
7245
  import_child_process = require("child_process");
@@ -7268,6 +7269,171 @@ var init_chunk_HDIR4MM5 = __esm({
7268
7269
  }
7269
7270
  });
7270
7271
 
7272
+ // ../harness/dist/chunk-FAN2R3GW.js
7273
+ function patchStats(value2) {
7274
+ if (typeof value2 !== "string") return {};
7275
+ let additions = 0;
7276
+ let deletions = 0;
7277
+ for (const line2 of value2.slice(0, 262144).split("\n")) {
7278
+ if (line2.startsWith("+++") || line2.startsWith("---")) continue;
7279
+ if (line2.startsWith("+")) additions += 1;
7280
+ else if (line2.startsWith("-")) deletions += 1;
7281
+ }
7282
+ return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
7283
+ }
7284
+ function searchResults(value2) {
7285
+ if (typeof value2 !== "string") return void 0;
7286
+ const results = value2.split("\n").flatMap((line2) => {
7287
+ const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
7288
+ if (!match) return [];
7289
+ const lineNumber = Number(match[2]);
7290
+ const itemText = text3(match[3], 240);
7291
+ if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
7292
+ return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
7293
+ }).slice(0, 5);
7294
+ return results.length ? results : void 0;
7295
+ }
7296
+ function codeToolRequestPresentation(request3) {
7297
+ const input = request3.input;
7298
+ if (request3.tool === "sandbox.read") {
7299
+ const path = text3(input.path, 1024);
7300
+ if (!path) return void 0;
7301
+ const startLine = integer(input.startLine);
7302
+ const endLine = integer(input.endLine);
7303
+ return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
7304
+ }
7305
+ if (request3.tool === "sandbox.list") {
7306
+ const scope = text3(input.prefix, 1024);
7307
+ return { kind: "list", ...scope ? { scope } : {} };
7308
+ }
7309
+ if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
7310
+ const query = text3(input.query, 512);
7311
+ const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
7312
+ if (request3.tool === "sandbox.search" && !query) return void 0;
7313
+ return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
7314
+ }
7315
+ if (request3.tool === "sandbox.apply_patch") {
7316
+ return { kind: "patch", ...patchStats(input.patch) };
7317
+ }
7318
+ const recipeId = text3(input.recipeId, 120);
7319
+ return recipeId ? { kind: "recipe", recipeId } : void 0;
7320
+ }
7321
+ function codeToolResultPresentation(request3, response2) {
7322
+ const started = codeToolRequestPresentation(request3);
7323
+ if (!started || !response2.ok) return started;
7324
+ const details = record5(response2.details);
7325
+ if (started.kind === "read") {
7326
+ return {
7327
+ ...started,
7328
+ ...integer(details?.startLine) ? { startLine: integer(details?.startLine) } : {},
7329
+ ...integer(details?.endLine) ? { endLine: integer(details?.endLine) } : {},
7330
+ ...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
7331
+ };
7332
+ }
7333
+ if (started.kind === "list") {
7334
+ 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);
7335
+ return {
7336
+ ...started,
7337
+ ...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
7338
+ ...listed.length ? { paths: listed } : {}
7339
+ };
7340
+ }
7341
+ if (started.kind === "query") {
7342
+ const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
7343
+ const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
7344
+ return {
7345
+ ...started,
7346
+ ...integer(details?.count) !== void 0 ? { count: integer(details?.count) } : {},
7347
+ ...results ? { results } : {},
7348
+ ...resultExcerpt ? { excerpt: resultExcerpt } : {}
7349
+ };
7350
+ }
7351
+ if (started.kind === "patch") {
7352
+ return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
7353
+ }
7354
+ const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
7355
+ return {
7356
+ ...started,
7357
+ ...integer(details?.exitCode) !== void 0 ? { exitCode: integer(details?.exitCode) } : {},
7358
+ ...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
7359
+ ...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
7360
+ ...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
7361
+ };
7362
+ }
7363
+ function toolFailureReason(response2) {
7364
+ if (response2.ok) return void 0;
7365
+ const supplied = response2.details?.failureReason;
7366
+ const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
7367
+ return reason.slice(0, MAX_FAILURE_REASON);
7368
+ }
7369
+ function observedBroker(input) {
7370
+ const now = input.now ?? Date.now;
7371
+ return {
7372
+ execute: async (context, request3) => {
7373
+ const startedAt = now();
7374
+ const operationId = input.operationIdFor(request3.requestId);
7375
+ const startedPresentation = codeToolRequestPresentation(request3);
7376
+ await input.emit({
7377
+ type: "tool",
7378
+ phase: "started",
7379
+ tool: request3.tool,
7380
+ operationId,
7381
+ ...startedPresentation ? { presentation: startedPresentation } : {}
7382
+ });
7383
+ const response2 = await input.broker.execute(context, request3);
7384
+ const completedPresentation = codeToolResultPresentation(request3, response2);
7385
+ const failureReason = toolFailureReason(response2);
7386
+ await input.emit({
7387
+ type: "tool",
7388
+ phase: "completed",
7389
+ tool: request3.tool,
7390
+ ok: response2.ok,
7391
+ durationMs: now() - startedAt,
7392
+ operationId,
7393
+ ...failureReason ? { failureReason } : {},
7394
+ ...completedPresentation ? { presentation: completedPresentation } : {}
7395
+ });
7396
+ return response2;
7397
+ }
7398
+ };
7399
+ }
7400
+ var text3, integer, record5, excerpt, paths, MAX_FAILURE_REASON, DEFAULT_FAILURE_REASON;
7401
+ var init_chunk_FAN2R3GW = __esm({
7402
+ "../harness/dist/chunk-FAN2R3GW.js"() {
7403
+ "use strict";
7404
+ init_cjs_shims();
7405
+ text3 = (value2, maximum) => {
7406
+ if (typeof value2 !== "string") return void 0;
7407
+ const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
7408
+ return bounded ? bounded.slice(0, maximum) : void 0;
7409
+ };
7410
+ integer = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
7411
+ record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
7412
+ excerpt = (value2, tail = false) => {
7413
+ if (typeof value2 !== "string") return void 0;
7414
+ const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
7415
+ const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
7416
+ if (!source) return void 0;
7417
+ const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
7418
+ const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
7419
+ return text3(selected.join("\n"), 2400);
7420
+ };
7421
+ paths = (value2) => {
7422
+ if (!Array.isArray(value2)) return void 0;
7423
+ const items = value2.flatMap((item) => {
7424
+ const path = text3(item, 1024);
7425
+ return path ? [path] : [];
7426
+ }).slice(0, 12);
7427
+ return items.length ? items : void 0;
7428
+ };
7429
+ MAX_FAILURE_REASON = 240;
7430
+ DEFAULT_FAILURE_REASON = {
7431
+ "tool denied by CaMeL policy": "tool denied by CaMeL policy",
7432
+ "review sessions are read-only": "review session is read-only; workspace changes are not permitted"
7433
+ };
7434
+ }
7435
+ });
7436
+
7271
7437
  // ../camel/dist/chunk-7FHPOQVP.js
7272
7438
  var CamelError;
7273
7439
  var init_chunk_7FHPOQVP = __esm({
@@ -7314,8 +7480,8 @@ function normalize(value2) {
7314
7480
  if (Array.isArray(value2)) return value2.map(normalize);
7315
7481
  if (value2 instanceof Uint8Array) return { $bytes: [...value2] };
7316
7482
  if (typeof value2 === "object") {
7317
- const record11 = value2;
7318
- return Object.fromEntries(Object.keys(record11).filter((key) => record11[key] !== void 0).sort().map((key) => [key, normalize(record11[key])]));
7483
+ const record12 = value2;
7484
+ return Object.fromEntries(Object.keys(record12).filter((key) => record12[key] !== void 0).sort().map((key) => [key, normalize(record12[key])]));
7319
7485
  }
7320
7486
  throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
7321
7487
  }
@@ -8236,7 +8402,7 @@ var init_code2 = __esm({
8236
8402
  }
8237
8403
  });
8238
8404
 
8239
- // ../harness/dist/chunk-4EPJJMFG.js
8405
+ // ../harness/dist/chunk-Q4NL5XO3.js
8240
8406
  async function digestStagedWorkspace(root, limits) {
8241
8407
  const files = [];
8242
8408
  const walk = async (directory) => {
@@ -8294,7 +8460,7 @@ function createCodeRuntimeControlClient(options) {
8294
8460
  }
8295
8461
  const value2 = await response2.json().catch(() => null);
8296
8462
  if (!response2.ok) {
8297
- const problem = record5(record5(value2)?.error);
8463
+ const problem = record6(record6(value2)?.error);
8298
8464
  throw new CodeRuntimeControlError(
8299
8465
  typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
8300
8466
  response2.status,
@@ -8315,12 +8481,12 @@ function createCodeRuntimeControlClient(options) {
8315
8481
  await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
8316
8482
  ),
8317
8483
  infer: async (sessionId, inference) => {
8318
- const value2 = record5(await call4(
8484
+ const value2 = record6(await call4(
8319
8485
  `/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
8320
8486
  inference,
8321
8487
  modelRequestTimeoutMs
8322
8488
  ));
8323
- if (!value2 || value2.requestId !== inference.requestId || !record5(value2.response) || !record5(value2.receipt)) {
8489
+ if (!value2 || value2.requestId !== inference.requestId || !record6(value2.response) || !record6(value2.receipt)) {
8324
8490
  throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
8325
8491
  }
8326
8492
  return value2;
@@ -8451,12 +8617,12 @@ function validateHeartbeat(version, capabilities) {
8451
8617
  }
8452
8618
  }
8453
8619
  function parseSnapshot(value2) {
8454
- const root = record5(value2);
8455
- const host = record5(root?.host);
8620
+ const root = record6(value2);
8621
+ const host = record6(root?.host);
8456
8622
  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");
8457
8623
  const bindingIds = /* @__PURE__ */ new Set();
8458
8624
  const bindings = root.bindings.map((item) => {
8459
- const binding = record5(item);
8625
+ const binding = record6(item);
8460
8626
  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)) {
8461
8627
  throw invalid("binding");
8462
8628
  }
@@ -8466,10 +8632,10 @@ function parseSnapshot(value2) {
8466
8632
  const commandIds = /* @__PURE__ */ new Set();
8467
8633
  const commandSequences = /* @__PURE__ */ new Set();
8468
8634
  const commands = root.commands.map((item) => {
8469
- const command = record5(item);
8635
+ const command = record6(item);
8470
8636
  const binding = bindings.find((candidate) => candidate.bindingId === command?.bindingId);
8471
8637
  const sequenceKey = `${String(command?.instanceId)}:${String(command?.sequence)}`;
8472
- 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)) || !record5(command.payload) || !Number.isSafeInteger(command.createdAt)) throw invalid("command");
8638
+ 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");
8473
8639
  commandIds.add(command.commandId);
8474
8640
  commandSequences.add(sequenceKey);
8475
8641
  return command;
@@ -8478,10 +8644,10 @@ function parseSnapshot(value2) {
8478
8644
  }
8479
8645
  async function parseSource(value2) {
8480
8646
  const repositoryLimits = { maximumFiles: 1e5, maximumBytes: 80 * 1024 * 1024 };
8481
- const snapshot = record5(record5(value2)?.snapshot);
8647
+ const snapshot = record6(record6(value2)?.snapshot);
8482
8648
  if (!snapshot || typeof snapshot.repository !== "string" || typeof snapshot.commitSha !== "string" || typeof snapshot.treeDigest !== "string" || !Array.isArray(snapshot.files)) throw invalid("source");
8483
8649
  const files = snapshot.files.map((value22) => {
8484
- const file = record5(value22);
8650
+ const file = record6(value22);
8485
8651
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
8486
8652
  return { path: file.path, content: file.content };
8487
8653
  });
@@ -8490,11 +8656,11 @@ async function parseSource(value2) {
8490
8656
  const aliases = /* @__PURE__ */ new Set();
8491
8657
  const references = [];
8492
8658
  for (const item of referencesValue) {
8493
- const reference = record5(item);
8659
+ const reference = record6(item);
8494
8660
  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");
8495
8661
  aliases.add(reference.alias);
8496
8662
  const referenceFiles = reference.files.map((entry) => {
8497
- const file = record5(entry);
8663
+ const file = record6(entry);
8498
8664
  if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
8499
8665
  return { path: file.path, content: file.content };
8500
8666
  });
@@ -8509,31 +8675,31 @@ async function parseSource(value2) {
8509
8675
  return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
8510
8676
  }
8511
8677
  function parseReview(value2) {
8512
- const review = record5(record5(value2)?.review);
8513
- 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");
8678
+ const review = record6(record6(value2)?.review);
8679
+ 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");
8514
8680
  return review;
8515
8681
  }
8516
8682
  function parseCandidate(value2) {
8517
- const candidate = record5(record5(value2)?.candidate);
8683
+ const candidate = record6(record6(value2)?.candidate);
8518
8684
  if (!candidate || typeof candidate.candidateId !== "string" || !/^ccand_[0-9a-f]{32}$/.test(candidate.candidateId) || !["submitted", "approved", "published", "failed"].includes(String(candidate.status))) {
8519
8685
  throw invalid("candidate");
8520
8686
  }
8521
8687
  return { candidateId: candidate.candidateId, status: candidate.status };
8522
8688
  }
8523
8689
  function parseCollaborationSkills(value2) {
8524
- const items = record5(value2)?.skills;
8690
+ const items = record6(value2)?.skills;
8525
8691
  if (!Array.isArray(items) || items.length > 16) throw invalid("collaboration skills");
8526
8692
  const skillNames2 = /* @__PURE__ */ new Set();
8527
8693
  const toolNames = /* @__PURE__ */ new Set();
8528
8694
  return items.map((item) => {
8529
- const skill = record5(item);
8695
+ const skill = record6(item);
8530
8696
  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) {
8531
8697
  throw invalid("collaboration skill");
8532
8698
  }
8533
8699
  skillNames2.add(skill.name);
8534
8700
  const tools = skill.tools.map((candidate) => {
8535
- const tool = record5(candidate);
8536
- const inputSchema = record5(tool?.inputSchema);
8701
+ const tool = record6(candidate);
8702
+ const inputSchema = record6(tool?.inputSchema);
8537
8703
  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") {
8538
8704
  throw invalid("collaboration tool");
8539
8705
  }
@@ -8558,12 +8724,12 @@ function parseCollaborationSkills(value2) {
8558
8724
  }
8559
8725
  function validateCollaborationToolRequest(value2) {
8560
8726
  validCommandId(value2.commandId);
8561
- if (typeof value2.toolCallId !== "string" || value2.toolCallId.length > 256 || !/^[^\s\u0000-\u001f\u007f]+$/.test(value2.toolCallId) || !validManifestName(value2.skill) || !validManifestName(value2.tool) || !record5(value2.input) || jsonBytes(value2.input) > 128e3) {
8727
+ 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) {
8562
8728
  throw new TypeError("invalid Code collaboration tool request");
8563
8729
  }
8564
8730
  }
8565
8731
  function parseCollaborationToolOutput(value2) {
8566
- const output = record5(record5(value2)?.output);
8732
+ const output = record6(record6(value2)?.output);
8567
8733
  if (!output || output.isError !== void 0 && typeof output.isError !== "boolean") {
8568
8734
  throw invalid("collaboration tool");
8569
8735
  }
@@ -8572,7 +8738,7 @@ function parseCollaborationToolOutput(value2) {
8572
8738
  return { content: output.content, ...output.isError === true ? { isError: true } : {} };
8573
8739
  }
8574
8740
  if (!Array.isArray(output.content) || output.content.length > 64 || jsonBytes(output.content) > 1e6 || !output.content.every((block2) => {
8575
- const item = record5(block2);
8741
+ const item = record6(block2);
8576
8742
  return item && ["text", "image", "audio", "document", "tool_use", "tool_result", "thinking"].includes(String(item.type));
8577
8743
  })) throw invalid("collaboration tool");
8578
8744
  return {
@@ -8606,10 +8772,41 @@ function jsonBytes(value2) {
8606
8772
  }
8607
8773
  }
8608
8774
  function stripPatchEnvelope(patch2) {
8609
- if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return patch2;
8775
+ if (!/^\*\*\* (?:Begin|End) Patch\s*$/m.test(patch2)) return applyPatchDialectToDiff(patch2);
8610
8776
  const kept = patch2.split("\n").filter((line2) => !/^\*\*\* (?:Begin|End) Patch\s*$/.test(line2));
8611
8777
  const stripped = kept.join("\n");
8612
- return /^diff --git /m.test(stripped) ? stripped : patch2;
8778
+ if (/^diff --git /m.test(stripped)) return stripped;
8779
+ const translated = applyPatchDialectToDiff(stripped);
8780
+ return translated === stripped ? patch2 : translated;
8781
+ }
8782
+ function applyPatchDialectToDiff(patch2) {
8783
+ if (!/^\*\*\* (?:Update|Add|Delete) File: /m.test(patch2)) return patch2;
8784
+ const out = [];
8785
+ let open = false;
8786
+ for (const line2 of patch2.split("\n")) {
8787
+ const file = /^\*\*\* (Update|Add|Delete) File: (.+?)\s*$/.exec(line2);
8788
+ if (file) {
8789
+ const [, verb, raw] = file;
8790
+ const path = raw.trim();
8791
+ if (!PATH.test(path)) return patch2;
8792
+ out.push(`diff --git a/${path} b/${path}`);
8793
+ if (verb === "Add") out.push("new file mode 100644", "--- /dev/null", `+++ b/${path}`);
8794
+ else if (verb === "Delete") out.push(`--- a/${path}`, "+++ /dev/null");
8795
+ else out.push(`--- a/${path}`, `+++ b/${path}`);
8796
+ open = true;
8797
+ continue;
8798
+ }
8799
+ if (/^\*\*\* /.test(line2)) continue;
8800
+ if (!open) continue;
8801
+ if (/^@@/.test(line2)) {
8802
+ out.push("@@ -1 +1 @@");
8803
+ continue;
8804
+ }
8805
+ out.push(line2);
8806
+ }
8807
+ if (!open) return patch2;
8808
+ return `${out.join("\n").replace(/\n+$/, "")}
8809
+ `;
8613
8810
  }
8614
8811
  function validateCodePatch(rawPatch, maxBytes) {
8615
8812
  const patch2 = stripPatchEnvelope(rawPatch);
@@ -8662,17 +8859,20 @@ function resolveCodePath(workspaceDir, path) {
8662
8859
  if (target !== root && !target.startsWith(`${root}${import_path6.sep}`)) throw new TypeError("path escapes the staged workspace");
8663
8860
  return target;
8664
8861
  }
8862
+ function hasContextFreeHunk(patch2) {
8863
+ const bodies = patch2.split(/^@@.*$/m).slice(1);
8864
+ return bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
8865
+ }
8665
8866
  function describePatchFailure(patch2, detail) {
8666
8867
  const hunks = patch2.split("\n").filter((line2) => line2.startsWith("@@"));
8667
- const bodies = patch2.split(/^@@.*$/m).slice(1);
8668
- const contextless = bodies.some((body) => !body.split("\n").some((line2) => line2.startsWith(" ") && line2.trim().length > 0));
8669
- const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
8868
+ const hint = hunks.length > 0 && hasContextFreeHunk(patch2) ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
8670
8869
  return `patch did not apply: ${detail}${hint}`;
8671
8870
  }
8672
8871
  async function applyCodePatch(workspaceDir, rawPatch, paths2) {
8673
8872
  const patch2 = stripPatchEnvelope(rawPatch);
8674
- await gitApply(workspaceDir, patch2, true);
8675
- await gitApply(workspaceDir, patch2, false);
8873
+ const zero = hasContextFreeHunk(patch2);
8874
+ await gitApply(workspaceDir, patch2, true, zero);
8875
+ await gitApply(workspaceDir, patch2, false, zero);
8676
8876
  for (const path of paths2) {
8677
8877
  try {
8678
8878
  const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
@@ -8684,9 +8884,16 @@ async function applyCodePatch(workspaceDir, rawPatch, paths2) {
8684
8884
  }
8685
8885
  }
8686
8886
  }
8687
- function gitApply(cwd, patch2, check) {
8887
+ function gitApply(cwd, patch2, check, unidiffZero = false) {
8688
8888
  return new Promise((accept, reject) => {
8689
- const args = ["apply", "--recount", "--whitespace=nowarn", ...check ? ["--check"] : [], "-"];
8889
+ const args = [
8890
+ "apply",
8891
+ "--recount",
8892
+ ...unidiffZero ? ["--unidiff-zero"] : [],
8893
+ "--whitespace=nowarn",
8894
+ ...check ? ["--check"] : [],
8895
+ "-"
8896
+ ];
8690
8897
  const child = (0, import_child_process4.spawn)("git", args, {
8691
8898
  cwd,
8692
8899
  shell: false,
@@ -8695,8 +8902,8 @@ function gitApply(cwd, patch2, check) {
8695
8902
  });
8696
8903
  let stderr2 = "";
8697
8904
  child.stderr.setEncoding("utf8");
8698
- child.stderr.on("data", (text22) => {
8699
- if (stderr2.length < 4e3) stderr2 += text22.slice(0, 4e3);
8905
+ child.stderr.on("data", (text4) => {
8906
+ if (stderr2.length < 4e3) stderr2 += text4.slice(0, 4e3);
8700
8907
  });
8701
8908
  child.once("error", reject);
8702
8909
  child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
@@ -8934,7 +9141,7 @@ function validate2(input) {
8934
9141
  maximumFiles: policy.maximumFiles ?? 2e4,
8935
9142
  maximumBytes: policy.maximumBytes ?? 512 * 1024 * 1024
8936
9143
  };
8937
- if ([...result.testPathPrefixes, ...result.testPathSuffixes].some((rule) => !RULE.test(rule)) || !integer(result.maximumChangedTests, 0, 1e4) || !integer(result.maximumPatchBytes, 1, 4 * 1024 * 1024) || !integer(result.maximumFiles, 1, 1e5) || !integer(result.maximumBytes, 1, 2 * 1024 * 1024 * 1024)) {
9144
+ 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)) {
8938
9145
  throw new TypeError("clean verification policy exceeds its bounds");
8939
9146
  }
8940
9147
  return result;
@@ -8985,7 +9192,7 @@ function hashFile(path) {
8985
9192
  });
8986
9193
  }
8987
9194
  function checkedResult(result, maximumOutputBytes) {
8988
- if (!integer(result.exitCode, 0, 255) || !integer(result.durationMs, 0, 30 * 6e4) || typeof result.stdout !== "string" || typeof result.stderr !== "string" || typeof result.outputLimitExceeded !== "boolean" || typeof result.timedOut !== "boolean") {
9195
+ 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") {
8989
9196
  throw new TypeError("recipe executor returned an invalid result");
8990
9197
  }
8991
9198
  const bytes = Buffer.byteLength(result.stdout) + Buffer.byteLength(result.stderr);
@@ -9035,7 +9242,7 @@ function digestJson(value2) {
9035
9242
  function digestBytes(value2) {
9036
9243
  return `sha256:${(0, import_crypto3.createHash)("sha256").update(value2).digest("hex")}`;
9037
9244
  }
9038
- function integer(value2, minimum, maximum) {
9245
+ function integer2(value2, minimum, maximum) {
9039
9246
  return Number.isSafeInteger(value2) && value2 >= minimum && value2 <= maximum;
9040
9247
  }
9041
9248
  async function prepareRuntimeCheckpoint(input) {
@@ -9062,9 +9269,9 @@ async function prepareRuntimeCheckpoint(input) {
9062
9269
  if (evidence.receipt.outcome === "passed") {
9063
9270
  verification = evidence.receipt;
9064
9271
  review = await input.review(patch2, verification);
9065
- note = review.verdict === "approved" ? "Clean verification and independent review passed" : "Clean verification passed; independent review rejected the candidate";
9272
+ note = describeReview(review);
9066
9273
  } else {
9067
- note = `Clean verification failed: ${evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed").map((recipe2) => `${recipe2.recipeId}=${recipe2.status}`).join(", ")}`;
9274
+ note = describeGateFailure(evidence);
9068
9275
  }
9069
9276
  } catch (cause) {
9070
9277
  note = `Candidate verification or review failed closed: ${message(cause)}`;
@@ -9089,6 +9296,23 @@ async function prepareRuntimeCheckpoint(input) {
9089
9296
  });
9090
9297
  return { checkpoint, verification, review, note };
9091
9298
  }
9299
+ function describeReview(review) {
9300
+ const findings = review.findings.map((finding) => ` - [${finding.severity}] ${finding.detail}`).join("\n");
9301
+ 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.`;
9302
+ return [head, review.summary, findings].filter(Boolean).join("\n").slice(0, 4e3);
9303
+ }
9304
+ function describeGateFailure(evidence) {
9305
+ const failed = evidence.receipt.recipes.filter((recipe2) => recipe2.status !== "passed");
9306
+ const detail = failed.map((recipe2) => {
9307
+ const log = evidence.logs.find((entry) => entry.recipeId === recipe2.recipeId);
9308
+ const output = `${log?.stdout ?? ""}
9309
+ ${log?.stderr ?? ""}`.trim();
9310
+ return `${recipe2.recipeId}=${recipe2.status}${output ? `
9311
+ ${output.slice(0, 1500)}` : ""}`;
9312
+ }).join("\n\n");
9313
+ return `Clean verification failed. Fix this and checkpoint again:
9314
+ ${detail}`.slice(0, 4e3);
9315
+ }
9092
9316
  function codeCommandMetadata(payload, resume) {
9093
9317
  const trusted = record22(payload.trustedBase);
9094
9318
  const role = payload.role;
@@ -9969,7 +10193,9 @@ function optionalInteger(value2) {
9969
10193
  return value2;
9970
10194
  }
9971
10195
  function response(request3, ok, content2, details) {
9972
- return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
10196
+ if (ok) return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
10197
+ const failureReason = typeof details?.failureReason === "string" && details.failureReason || DEFAULT_FAILURE_REASON2[content2] || content2 || "tool request failed; inspect the tool input and workspace state";
10198
+ return { requestId: request3.requestId, ok, content: content2, details: { ...details, failureReason } };
9973
10199
  }
9974
10200
  function workspaceGraphs(workspaceDir, paths2) {
9975
10201
  const existing = cache.get(workspaceDir);
@@ -10533,96 +10759,6 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
10533
10759
  const bounded = attributed.type === "message" ? { ...attributed, body: attributed.body.trim().slice(0, 2e4) || `${attributed.actor} event` } : attributed;
10534
10760
  await control.appendSessionEvent(command.sessionId, eventId, bounded);
10535
10761
  }
10536
- function patchStats(value2) {
10537
- if (typeof value2 !== "string") return {};
10538
- let additions = 0;
10539
- let deletions = 0;
10540
- for (const line2 of value2.slice(0, 262144).split("\n")) {
10541
- if (line2.startsWith("+++") || line2.startsWith("---")) continue;
10542
- if (line2.startsWith("+")) additions += 1;
10543
- else if (line2.startsWith("-")) deletions += 1;
10544
- }
10545
- return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
10546
- }
10547
- function searchResults(value2) {
10548
- if (typeof value2 !== "string") return void 0;
10549
- const results = value2.split("\n").flatMap((line2) => {
10550
- const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
10551
- if (!match) return [];
10552
- const lineNumber = Number(match[2]);
10553
- const itemText = text3(match[3], 240);
10554
- if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
10555
- return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
10556
- }).slice(0, 5);
10557
- return results.length ? results : void 0;
10558
- }
10559
- function codeToolRequestPresentation(request3) {
10560
- const input = request3.input;
10561
- if (request3.tool === "sandbox.read") {
10562
- const path = text3(input.path, 1024);
10563
- if (!path) return void 0;
10564
- const startLine = integer2(input.startLine);
10565
- const endLine = integer2(input.endLine);
10566
- return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
10567
- }
10568
- if (request3.tool === "sandbox.list") {
10569
- const scope = text3(input.prefix, 1024);
10570
- return { kind: "list", ...scope ? { scope } : {} };
10571
- }
10572
- if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
10573
- const query = text3(input.query, 512);
10574
- const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
10575
- if (request3.tool === "sandbox.search" && !query) return void 0;
10576
- return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
10577
- }
10578
- if (request3.tool === "sandbox.apply_patch") {
10579
- return { kind: "patch", ...patchStats(input.patch) };
10580
- }
10581
- const recipeId = text3(input.recipeId, 120);
10582
- return recipeId ? { kind: "recipe", recipeId } : void 0;
10583
- }
10584
- function codeToolResultPresentation(request3, response2) {
10585
- const started = codeToolRequestPresentation(request3);
10586
- if (!started || !response2.ok) return started;
10587
- const details = record32(response2.details);
10588
- if (started.kind === "read") {
10589
- return {
10590
- ...started,
10591
- ...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
10592
- ...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
10593
- ...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
10594
- };
10595
- }
10596
- if (started.kind === "list") {
10597
- 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);
10598
- return {
10599
- ...started,
10600
- ...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
10601
- ...listed.length ? { paths: listed } : {}
10602
- };
10603
- }
10604
- if (started.kind === "query") {
10605
- const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
10606
- const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
10607
- return {
10608
- ...started,
10609
- ...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
10610
- ...results ? { results } : {},
10611
- ...resultExcerpt ? { excerpt: resultExcerpt } : {}
10612
- };
10613
- }
10614
- if (started.kind === "patch") {
10615
- return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
10616
- }
10617
- const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
10618
- return {
10619
- ...started,
10620
- ...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
10621
- ...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
10622
- ...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
10623
- ...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
10624
- };
10625
- }
10626
10762
  function codeRuntimeAcknowledgementGate(signal) {
10627
10763
  let settle;
10628
10764
  let settled = false;
@@ -10689,13 +10825,14 @@ function observeCodeRuntimeSessionSkills(command, skills, emit4) {
10689
10825
  })
10690
10826
  }));
10691
10827
  }
10692
- var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_ai5, import_child_process6, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record5, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, SOURCE_MAX_FILES, SOURCE_MAX_BYTES, SOURCE_SET_MAX_BYTES, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, MAX_NATIVE_ARG_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, text3, integer2, record32, excerpt, paths, TheseusRuntimeEngine;
10693
- var init_chunk_4EPJJMFG = __esm({
10694
- "../harness/dist/chunk-4EPJJMFG.js"() {
10828
+ var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_ai5, import_child_process6, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record6, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, SOURCE_MAX_FILES, SOURCE_MAX_BYTES, SOURCE_SET_MAX_BYTES, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, MAX_NATIVE_ARG_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, DEFAULT_FAILURE_REASON2, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, TheseusRuntimeEngine;
10829
+ var init_chunk_Q4NL5XO3 = __esm({
10830
+ "../harness/dist/chunk-Q4NL5XO3.js"() {
10695
10831
  "use strict";
10696
10832
  init_cjs_shims();
10697
- init_chunk_HDIR4MM5();
10698
- init_chunk_I43KTCJ2();
10833
+ init_chunk_FAN2R3GW();
10834
+ init_chunk_5LRYJKUI();
10835
+ init_chunk_U324RQ4N();
10699
10836
  import_crypto = require("crypto");
10700
10837
  import_promises5 = require("fs/promises");
10701
10838
  import_path5 = require("path");
@@ -10777,7 +10914,7 @@ var init_chunk_4EPJJMFG = __esm({
10777
10914
  code;
10778
10915
  name = "CodeRuntimeControlError";
10779
10916
  };
10780
- record5 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
10917
+ record6 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
10781
10918
  invalid = (part) => new CodeRuntimeControlError(`invalid Code runtime ${part} response`, 502, "invalid_response");
10782
10919
  RESERVED = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
10783
10920
  SECRET = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
@@ -10930,6 +11067,10 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
10930
11067
  recipeId: "selector",
10931
11068
  sourceDigest: "payload"
10932
11069
  });
11070
+ DEFAULT_FAILURE_REASON2 = {
11071
+ "tool denied by CaMeL policy": "tool denied by CaMeL policy",
11072
+ "review sessions are read-only": "review session is read-only; workspace changes are not permitted"
11073
+ };
10933
11074
  cache = /* @__PURE__ */ new Map();
10934
11075
  shortId = (id2) => id2.slice(id2.indexOf(":") + 1);
10935
11076
  GRAPH_TOOLS = /* @__PURE__ */ new Set([
@@ -10942,30 +11083,6 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
10942
11083
  POSITIVE = (value2) => Number.isFinite(value2) && Number(value2) > 0 ? Number(value2) : void 0;
10943
11084
  digestRuntimeValue = (value2) => `sha256:${(0, import_crypto4.createHash)("sha256").update(value2).digest("hex")}`;
10944
11085
  runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
10945
- text3 = (value2, maximum) => {
10946
- if (typeof value2 !== "string") return void 0;
10947
- const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
10948
- return bounded ? bounded.slice(0, maximum) : void 0;
10949
- };
10950
- integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
10951
- record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
10952
- excerpt = (value2, tail = false) => {
10953
- if (typeof value2 !== "string") return void 0;
10954
- const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
10955
- const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
10956
- if (!source) return void 0;
10957
- const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
10958
- const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
10959
- return text3(selected.join("\n"), 2400);
10960
- };
10961
- paths = (value2) => {
10962
- if (!Array.isArray(value2)) return void 0;
10963
- const items = value2.flatMap((item) => {
10964
- const path = text3(item, 1024);
10965
- return path ? [path] : [];
10966
- }).slice(0, 12);
10967
- return items.length ? items : void 0;
10968
- };
10969
11086
  TheseusRuntimeEngine = class {
10970
11087
  constructor(options) {
10971
11088
  this.options = options;
@@ -11201,36 +11318,11 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
11201
11318
  }
11202
11319
  /** Report every brokered effect as it starts and finishes. */
11203
11320
  #observed(command, active, broker) {
11204
- return {
11205
- execute: async (context, request3) => {
11206
- const startedAt = Date.now();
11207
- const operationId = digestRuntimeValue(`${command.commandId}:${request3.requestId}`);
11208
- const startedPresentation = codeToolRequestPresentation(request3);
11209
- await this.#event(
11210
- command,
11211
- {
11212
- type: "tool",
11213
- phase: "started",
11214
- tool: request3.tool,
11215
- operationId,
11216
- ...startedPresentation ? { presentation: startedPresentation } : {}
11217
- },
11218
- active.conversationRefs
11219
- ).catch(() => void 0);
11220
- const response2 = await broker.execute(context, request3);
11221
- const completedPresentation = codeToolResultPresentation(request3, response2);
11222
- await this.#event(command, {
11223
- type: "tool",
11224
- phase: "completed",
11225
- tool: request3.tool,
11226
- ok: response2.ok,
11227
- durationMs: Date.now() - startedAt,
11228
- operationId,
11229
- ...completedPresentation ? { presentation: completedPresentation } : {}
11230
- }, active.conversationRefs).catch(() => void 0);
11231
- return response2;
11232
- }
11233
- };
11321
+ return observedBroker({
11322
+ broker,
11323
+ operationIdFor: (requestId) => digestRuntimeValue(`${command.commandId}:${requestId}`),
11324
+ emit: (event) => this.#event(command, event, active.conversationRefs).catch(() => void 0)
11325
+ });
11234
11326
  }
11235
11327
  async #checkpoint(command) {
11236
11328
  const active = this.#active.get(command.sessionId);
@@ -11267,8 +11359,8 @@ var init_node = __esm({
11267
11359
  "../harness/dist/node.js"() {
11268
11360
  "use strict";
11269
11361
  init_cjs_shims();
11270
- init_chunk_4EPJJMFG();
11271
- init_chunk_HDIR4MM5();
11362
+ init_chunk_Q4NL5XO3();
11363
+ init_chunk_5LRYJKUI();
11272
11364
  MEASURED_PREMIUM = Object.freeze({
11273
11365
  /** 3 racers vs pure depth at equal budget: 21,044 / 7,936. */
11274
11366
  racePerRacer: 0.55,
@@ -11510,6 +11602,19 @@ var init_code_runtime_config = __esm({
11510
11602
  init_cjs_shims();
11511
11603
  CODE_NODE_IMAGE = "node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd";
11512
11604
  CODE_BUILD_RECIPES = Object.freeze([{
11605
+ id: "odla-code-gates",
11606
+ image: CODE_NODE_IMAGE,
11607
+ // Selects the repository's fast, no-capability gates from the same manifest
11608
+ // CI and `npm run preflight` read, so a new gate is picked up without a
11609
+ // second list to keep in sync. The container has no shell: this is argv.
11610
+ command: ["node", "scripts/recipe-gates.mjs"],
11611
+ timeoutMs: 18e4,
11612
+ maxOutputBytes: 1024 * 1024,
11613
+ cpus: 1,
11614
+ // check:secrets holds ~2,500 source files in memory at once.
11615
+ memory: "1g",
11616
+ pids: 128
11617
+ }, {
11513
11618
  id: "odla-code-contracts",
11514
11619
  image: CODE_NODE_IMAGE,
11515
11620
  command: [
@@ -11691,20 +11796,20 @@ async function runCodeRuntime(input) {
11691
11796
  }
11692
11797
  }
11693
11798
  function parseConnection(value2, appId, appEnv) {
11694
- const root = record6(value2);
11695
- const host = record6(root?.host);
11696
- const offer = record6(root?.offer);
11697
- const binding = record6(root?.binding);
11799
+ const root = record7(value2);
11800
+ const host = record7(root?.host);
11801
+ const offer = record7(root?.offer);
11802
+ const binding = record7(root?.binding);
11698
11803
  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)) {
11699
11804
  throw new Error("connect Code host returned an invalid response");
11700
11805
  }
11701
11806
  return root;
11702
11807
  }
11703
11808
  function apiFailure(action2, status, value2) {
11704
- const message2 = record6(record6(value2)?.error)?.message;
11809
+ const message2 = record7(record7(value2)?.error)?.message;
11705
11810
  return `${action2} failed (${status})${typeof message2 === "string" ? `: ${message2}` : ""}`;
11706
11811
  }
11707
- function record6(value2) {
11812
+ function record7(value2) {
11708
11813
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
11709
11814
  }
11710
11815
  var import_node_fs18, import_node_os5, import_node_path18;
@@ -12299,6 +12404,14 @@ Usage:
12299
12404
  odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
12300
12405
  odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
12301
12406
  odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
12407
+ odla-ai promotion plan --file <promotion-plan.json> [--env dev] [--json]
12408
+ odla-ai promotion inspect <plan-id> [--env dev] [--json]
12409
+ odla-ai promotion status <plan-id> [--env dev] [--json]
12410
+ odla-ai promotion apply <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
12411
+ odla-ai promotion rollback <plan-id> --approval <approval-id> [--idempotency-key <key>] [--env dev] [--json]
12412
+ odla-ai promotion next <plan-id> <operation-id> [--env dev] [--json]
12413
+ odla-ai promotion record <plan-id> <operation-id> <step-id> --file <step-receipt.json> [--env dev] [--json]
12414
+ odla-ai promotion cancel <plan-id> <operation-id> [--env dev] [--json]
12302
12415
  odla-ai operations get <operation-id> [--json]
12303
12416
  odla-ai operations wait <operation-id> [--interval <seconds>] [--timeout <seconds>] [--json]
12304
12417
  odla-ai calendar status [--env dev] [--email <odla-account>] [--json]
@@ -12467,6 +12580,11 @@ Commands:
12467
12580
  init Create a generic odla.config.mjs plus starter schema/rules files.
12468
12581
  doctor Validate and summarize the project config without network calls.
12469
12582
  config Diff Registry intent, freeze a CAS-bound plan, and conditionally apply its safe actions.
12583
+ promotion Plan and inspect exact selective-release bundles without target
12584
+ mutation, then queue and resume a human-approved apply or rollback
12585
+ from an enrolled device. Each target step is leased and receipt-
12586
+ journaled; approval remains bound to the immutable plan digest,
12587
+ target set, action, and expiry, so every mismatch fails closed.
12470
12588
  operations Inspect or wait on one exact, durable config-operation receipt.
12471
12589
  calendar Inspect, connect, or disconnect the live Google booking connection.
12472
12590
  app Archive (suspend, data retained), restore, export, import, or
@@ -13376,17 +13494,17 @@ function collectEntityFields(entity, parsed, allowClear) {
13376
13494
  if (entity === "task" && fields.column === "ready") fields.column = "todo";
13377
13495
  return fields;
13378
13496
  }
13379
- function statusCol(entity, record11) {
13380
- if (entity === "bug") return `${record11.status ?? ""}/${record11.severity ?? ""}`;
13497
+ function statusCol(entity, record12) {
13498
+ if (entity === "bug") return `${record12.status ?? ""}/${record12.severity ?? ""}`;
13381
13499
  if (entity === "task") {
13382
- const state2 = record11.column === "todo" ? "ready" : String(record11.column ?? "");
13383
- return record11.revision ? `${state2}; r${record11.revision}` : state2;
13500
+ const state2 = record12.column === "todo" ? "ready" : String(record12.column ?? "");
13501
+ return record12.revision ? `${state2}; r${record12.revision}` : state2;
13384
13502
  }
13385
- return String(record11.status ?? "");
13503
+ return String(record12.status ?? "");
13386
13504
  }
13387
- function referenceMarkup(entity, record11) {
13388
- const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
13389
- return `@[${label}](pm:${entity}/${record11.id})`;
13505
+ function referenceMarkup(entity, record12) {
13506
+ const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
13507
+ return `@[${label}](pm:${entity}/${record12.id})`;
13390
13508
  }
13391
13509
  function studioRecordUrl(ctx, entity, id2) {
13392
13510
  return new URL(
@@ -13394,13 +13512,13 @@ function studioRecordUrl(ctx, entity, id2) {
13394
13512
  ctx.platformUrl
13395
13513
  ).href;
13396
13514
  }
13397
- function studioRecordLink(ctx, entity, record11) {
13398
- const label = (record11.title?.trim() || `${entity} ${record11.id}`).replaceAll("]", ")");
13399
- return `[${label}](${studioRecordUrl(ctx, entity, record11.id)})`;
13515
+ function studioRecordLink(ctx, entity, record12) {
13516
+ const label = (record12.title?.trim() || `${entity} ${record12.id}`).replaceAll("]", ")");
13517
+ return `[${label}](${studioRecordUrl(ctx, entity, record12.id)})`;
13400
13518
  }
13401
- function printRecord(ctx, entity, record11) {
13519
+ function printRecord(ctx, entity, record12) {
13402
13520
  ctx.out.log(
13403
- `${record11.id} [${statusCol(entity, record11)}] ${record11.appId} ${studioRecordLink(ctx, entity, record11)}`
13521
+ `${record12.id} [${statusCol(entity, record12)}] ${record12.appId} ${studioRecordLink(ctx, entity, record12)}`
13404
13522
  );
13405
13523
  }
13406
13524
  function emit2(ctx, value2, human) {
@@ -13504,8 +13622,8 @@ async function pmAdd(ctx, entity, parsed) {
13504
13622
  input,
13505
13623
  mutationId: writeMutationId2(parsed)
13506
13624
  });
13507
- const record11 = { id: res.id, appId, title: String(input.title) };
13508
- emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record11)}`));
13625
+ const record12 = { id: res.id, appId, title: String(input.title) };
13626
+ emit2(ctx, res, () => ctx.out.log(`created ${entity}: ${studioRecordLink(ctx, entity, record12)}`));
13509
13627
  }
13510
13628
  function looksLikeUuidPrefix(value2) {
13511
13629
  if (!UUID_PREFIX.test(value2) || !/[0-9a-f]/i.test(value2)) return false;
@@ -13523,7 +13641,7 @@ async function resolvePmId(ctx, entity, supplied) {
13523
13641
  "GET",
13524
13642
  `/${entity}?limit=${limit}&offset=${offset}`
13525
13643
  );
13526
- matches.push(...page2.records.map((record11) => record11.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
13644
+ matches.push(...page2.records.map((record12) => record12.id).filter((id2) => id2.toLowerCase().startsWith(prefix)));
13527
13645
  offset += page2.records.length;
13528
13646
  if (matches.length > 1 || page2.records.length === 0 || offset >= page2.total) break;
13529
13647
  }
@@ -13537,17 +13655,17 @@ async function resolvePmId(ctx, entity, supplied) {
13537
13655
  }
13538
13656
  async function pmGet(ctx, entity, id2) {
13539
13657
  const resolved = await resolvePmId(ctx, entity, id2);
13540
- const { record: record11 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
13541
- emit2(ctx, record11, () => printRecord(ctx, entity, record11));
13658
+ const { record: record12 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(resolved)}`);
13659
+ emit2(ctx, record12, () => printRecord(ctx, entity, record12));
13542
13660
  }
13543
13661
  async function pmReference(ctx, entity, id2) {
13544
- const { record: record11 } = await pmRequest(
13662
+ const { record: record12 } = await pmRequest(
13545
13663
  ctx,
13546
13664
  "GET",
13547
13665
  `/${entity}/${encodeURIComponent(id2)}`
13548
13666
  );
13549
- const markup = referenceMarkup(entity, record11);
13550
- emit2(ctx, { kind: `pm:${entity}`, id: record11.id, label: record11.title ?? "", markup }, () => {
13667
+ const markup = referenceMarkup(entity, record12);
13668
+ emit2(ctx, { kind: `pm:${entity}`, id: record12.id, label: record12.title ?? "", markup }, () => {
13551
13669
  ctx.out.log(markup);
13552
13670
  });
13553
13671
  }
@@ -13626,16 +13744,16 @@ var init_pm_actions = __esm({
13626
13744
  });
13627
13745
 
13628
13746
  // src/pm-lean.ts
13629
- function leanRecord(entity, record11) {
13747
+ function leanRecord(entity, record12) {
13630
13748
  const out = {};
13631
13749
  for (const key of [...COMMON, ...PER_ENTITY[entity]]) {
13632
- const value2 = record11[key];
13750
+ const value2 = record12[key];
13633
13751
  if (value2 !== void 0 && value2 !== null) out[key] = value2;
13634
13752
  }
13635
13753
  return out;
13636
13754
  }
13637
13755
  function leanRecords(entity, records, verbose) {
13638
- return verbose ? records : records.map((record11) => leanRecord(entity, record11));
13756
+ return verbose ? records : records.map((record12) => leanRecord(entity, record12));
13639
13757
  }
13640
13758
  var COMMON, PER_ENTITY;
13641
13759
  var init_pm_lean = __esm({
@@ -13687,8 +13805,8 @@ async function pmNext(ctx, parsed) {
13687
13805
  // One request for both live columns; the split below is over open work only.
13688
13806
  listFiltered(ctx, "task", appId, projectId, { column: `${READY_COLUMN},doing` })
13689
13807
  ]);
13690
- const ready = tasks.filter((record11) => record11.column === READY_COLUMN);
13691
- const doing = tasks.filter((record11) => record11.column === "doing");
13808
+ const ready = tasks.filter((record12) => record12.column === READY_COLUMN);
13809
+ const doing = tasks.filter((record12) => record12.column === "doing");
13692
13810
  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}`;
13693
13811
  const monitor = `odla-ai pm watch --app ${appId} --jsonl`;
13694
13812
  const result = {
@@ -13709,7 +13827,7 @@ async function pmNext(ctx, parsed) {
13709
13827
  ]) {
13710
13828
  ctx.out.log(`${label}:`);
13711
13829
  if (!records.length) ctx.out.log("- (none)");
13712
- else for (const record11 of records) printRecord(ctx, entity, record11);
13830
+ else for (const record12 of records) printRecord(ctx, entity, record12);
13713
13831
  }
13714
13832
  ctx.out.log(`next: ${guidance}`);
13715
13833
  ctx.out.log(`monitor: ${monitor}`);
@@ -13748,7 +13866,7 @@ async function pmHandoff(ctx, parsed) {
13748
13866
  ]) {
13749
13867
  ctx.out.log(`${label}:`);
13750
13868
  if (!records.length) ctx.out.log("- (none)");
13751
- else for (const record11 of records) printRecord(ctx, entity, record11);
13869
+ else for (const record12 of records) printRecord(ctx, entity, record12);
13752
13870
  }
13753
13871
  ctx.out.log(`monitor: ${monitor}`);
13754
13872
  });
@@ -13849,14 +13967,14 @@ var init_pm_start = __esm({
13849
13967
 
13850
13968
  // src/pm-links.ts
13851
13969
  async function pmLink(ctx, entity, id2) {
13852
- const { record: record11 } = await pmRequest(
13970
+ const { record: record12 } = await pmRequest(
13853
13971
  ctx,
13854
13972
  "GET",
13855
13973
  `/${entity}/${encodeURIComponent(id2)}`
13856
13974
  );
13857
- const url = studioRecordUrl(ctx, entity, record11.id);
13858
- const markdown = studioRecordLink(ctx, entity, record11);
13859
- emit2(ctx, { kind: entity, id: record11.id, label: record11.title ?? "", url, markdown }, () => {
13975
+ const url = studioRecordUrl(ctx, entity, record12.id);
13976
+ const markdown = studioRecordLink(ctx, entity, record12);
13977
+ emit2(ctx, { kind: entity, id: record12.id, label: record12.title ?? "", url, markdown }, () => {
13860
13978
  ctx.out.log(markdown);
13861
13979
  });
13862
13980
  }
@@ -14011,16 +14129,16 @@ async function page(ctx, appId, cursor) {
14011
14129
  }
14012
14130
  return data;
14013
14131
  }
14014
- function recordState(record11) {
14015
- if (record11.column) return record11.column === "todo" ? "ready" : record11.column;
14016
- return String(record11.status ?? "");
14132
+ function recordState(record12) {
14133
+ if (record12.column) return record12.column === "todo" ? "ready" : record12.column;
14134
+ return String(record12.status ?? "");
14017
14135
  }
14018
14136
  function eventRecord(event) {
14019
14137
  return event.payload.payload;
14020
14138
  }
14021
14139
  function eventLabel(event) {
14022
- const record11 = eventRecord(event);
14023
- if (record11) return String(record11.title ?? event.payload.entityId);
14140
+ const record12 = eventRecord(event);
14141
+ if (record12) return String(record12.title ?? event.payload.entityId);
14024
14142
  const body = event.payload.message?.body?.replace(/\s+/g, " ").trim();
14025
14143
  return body || event.payload.entityId;
14026
14144
  }
@@ -14028,10 +14146,10 @@ function report3(ctx, parsed, result) {
14028
14146
  if (ctx.json) ctx.out.log(JSON.stringify(result, null, 2));
14029
14147
  else if (parsed.options.jsonl !== true && result.found) {
14030
14148
  for (const event of result.events ?? []) {
14031
- const record11 = eventRecord(event);
14032
- const state2 = record11 ? recordState(record11) : "comment";
14149
+ const record12 = eventRecord(event);
14150
+ const state2 = record12 ? recordState(record12) : "comment";
14033
14151
  ctx.out.log(
14034
- `${event.id} ${event.type} ${state2}${record11?.revision ? `; r${record11.revision}` : ""} ${eventLabel(event)}`
14152
+ `${event.id} ${event.type} ${state2}${record12?.revision ? `; r${record12.revision}` : ""} ${eventLabel(event)}`
14035
14153
  );
14036
14154
  }
14037
14155
  }
@@ -14105,8 +14223,8 @@ async function pmWatch(ctx, parsed) {
14105
14223
  }
14106
14224
  firstSuccess = false;
14107
14225
  const matching = current.events.filter((event) => {
14108
- const record11 = eventRecord(event);
14109
- const state2 = record11 ? recordState(record11).toLowerCase() : "";
14226
+ const record12 = eventRecord(event);
14227
+ const state2 = record12 ? recordState(record12).toLowerCase() : "";
14110
14228
  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);
14111
14229
  });
14112
14230
  for (const event of matching) {
@@ -14594,17 +14712,17 @@ async function platformStatus(parsed, deps) {
14594
14712
  }
14595
14713
  }
14596
14714
  function isPlatformStatus(value2) {
14597
- if (!record7(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
14598
- if (!record7(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
14599
- if (!record7(value2.catalog) || !record7(value2.summary)) return false;
14715
+ if (!record8(value2) || value2.schemaVersion !== "odla.platform-status/v1") return false;
14716
+ if (!record8(value2.verdict) || !Array.isArray(value2.verdict.reasons)) return false;
14717
+ if (!record8(value2.catalog) || !record8(value2.summary)) return false;
14600
14718
  return Array.isArray(value2.services) && Array.isArray(value2.nextActions);
14601
14719
  }
14602
14720
  function apiMessage(value2) {
14603
- if (!record7(value2)) return "request failed";
14604
- const error = record7(value2.error) ? value2.error : value2;
14721
+ if (!record8(value2)) return "request failed";
14722
+ const error = record8(value2.error) ? value2.error : value2;
14605
14723
  return typeof error.message === "string" ? error.message : typeof error.code === "string" ? error.code : "request failed";
14606
14724
  }
14607
- function record7(value2) {
14725
+ function record8(value2) {
14608
14726
  return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
14609
14727
  }
14610
14728
  var init_platform_command = __esm({
@@ -14656,7 +14774,7 @@ function statusVerdict(reads) {
14656
14774
  severity: "degraded"
14657
14775
  });
14658
14776
  }
14659
- const performance = record8(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
14777
+ const performance = record9(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
14660
14778
  if (performance?.status === "unavailable") {
14661
14779
  reasons.push({
14662
14780
  source: "liveSync",
@@ -14737,7 +14855,7 @@ function statusVerdict(reads) {
14737
14855
  reasons
14738
14856
  };
14739
14857
  }
14740
- function record8(value2) {
14858
+ function record9(value2) {
14741
14859
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
14742
14860
  }
14743
14861
  function numeric2(value2) {
@@ -14771,7 +14889,7 @@ function printO11yStatus(status, out) {
14771
14889
  out.log(
14772
14890
  `o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
14773
14891
  );
14774
- const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record9) : [];
14892
+ const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record10) : [];
14775
14893
  const requests = routes.reduce(
14776
14894
  (total, row) => total + numeric3(row.requests),
14777
14895
  0
@@ -14783,39 +14901,39 @@ function printO11yStatus(status, out) {
14783
14901
  out.log(
14784
14902
  `application ${status.application.httpStatus} ${requests} requests ${errors} errors`
14785
14903
  );
14786
- const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record9) : [];
14904
+ const versions = Array.isArray(status.applicationVersions.body.rows) ? status.applicationVersions.body.rows.filter(record10) : [];
14787
14905
  out.log(
14788
14906
  `application-versions ${status.applicationVersions.httpStatus} ${versions.length ? versions.slice(0, 5).map(
14789
14907
  (row) => `${String(row.value || "(unattributed)")}:${numeric3(row.requests)}`
14790
14908
  ).join(", ") : "none observed"}`
14791
14909
  );
14792
14910
  out.log(liveSyncLine(status.liveSync));
14793
- const canaryDurations = record9(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
14911
+ const canaryDurations = record10(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
14794
14912
  out.log(
14795
14913
  `canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
14796
14914
  );
14797
- const collectorIngest = record9(status.collector.body.ingest) ? status.collector.body.ingest : {};
14798
- const collectorStorage = record9(collectorIngest.storage) ? collectorIngest.storage : {};
14915
+ const collectorIngest = record10(status.collector.body.ingest) ? status.collector.body.ingest : {};
14916
+ const collectorStorage = record10(collectorIngest.storage) ? collectorIngest.storage : {};
14799
14917
  out.log(
14800
14918
  `collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
14801
14919
  );
14802
- const providerMetrics = record9(status.provider.body.metrics) ? status.provider.body.metrics : {};
14803
- const providerCapacity = record9(status.provider.body.capacity) ? status.provider.body.capacity : {};
14804
- const workerMemory = record9(providerCapacity.memory) ? providerCapacity.memory : {};
14920
+ const providerMetrics = record10(status.provider.body.metrics) ? status.provider.body.metrics : {};
14921
+ const providerCapacity = record10(status.provider.body.capacity) ? status.provider.body.capacity : {};
14922
+ const workerMemory = record10(providerCapacity.memory) ? providerCapacity.memory : {};
14805
14923
  out.log(
14806
14924
  `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`
14807
14925
  );
14808
14926
  for (const line2 of providerCapacityLines(status.providerCapacity)) {
14809
14927
  out.log(line2);
14810
14928
  }
14811
- const coverage = record9(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
14812
- const coverageCounts = record9(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
14813
- const coverageBudget = record9(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
14929
+ const coverage = record10(status.providerReconciliation.body.comparison) ? status.providerReconciliation.body.comparison : {};
14930
+ const coverageCounts = record10(status.providerReconciliation.body.counts) ? status.providerReconciliation.body.counts : {};
14931
+ const coverageBudget = record10(status.providerReconciliation.body.budget) ? status.providerReconciliation.body.budget : {};
14814
14932
  out.log(
14815
14933
  `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`
14816
14934
  );
14817
14935
  const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
14818
- const providerFreshness = record9(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
14936
+ const providerFreshness = record10(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
14819
14937
  out.log(
14820
14938
  `cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
14821
14939
  );
@@ -14824,17 +14942,17 @@ function printO11yStatus(status, out) {
14824
14942
  );
14825
14943
  }
14826
14944
  function providerCapacityLines(read3) {
14827
- const resources = record9(read3.body.resources) ? read3.body.resources : {};
14828
- const durableObjects = record9(resources.durableObjects) ? resources.durableObjects : {};
14829
- const periodic = record9(durableObjects.periodic) ? durableObjects.periodic : {};
14830
- const storage = record9(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
14831
- const d1 = record9(resources.d1) ? resources.d1 : {};
14832
- const d1Activity = record9(d1.activity) ? d1.activity : {};
14833
- const d1Storage = record9(d1.storage) ? d1.storage : {};
14834
- const d1Latency = record9(d1Activity.latency) ? d1Activity.latency : {};
14835
- const r2 = record9(resources.r2) ? resources.r2 : {};
14836
- const r2Operations = record9(r2.operations) ? r2.operations : {};
14837
- const r2Storage = record9(r2.storage) ? r2.storage : {};
14945
+ const resources = record10(read3.body.resources) ? read3.body.resources : {};
14946
+ const durableObjects = record10(resources.durableObjects) ? resources.durableObjects : {};
14947
+ const periodic = record10(durableObjects.periodic) ? durableObjects.periodic : {};
14948
+ const storage = record10(durableObjects.sqliteStorage) ? durableObjects.sqliteStorage : {};
14949
+ const d1 = record10(resources.d1) ? resources.d1 : {};
14950
+ const d1Activity = record10(d1.activity) ? d1.activity : {};
14951
+ const d1Storage = record10(d1.storage) ? d1.storage : {};
14952
+ const d1Latency = record10(d1Activity.latency) ? d1Activity.latency : {};
14953
+ const r2 = record10(resources.r2) ? resources.r2 : {};
14954
+ const r2Operations = record10(r2.operations) ? r2.operations : {};
14955
+ const r2Storage = record10(r2.storage) ? r2.storage : {};
14838
14956
  const status = String(
14839
14957
  read3.body.status ?? read3.body.error ?? "unavailable"
14840
14958
  );
@@ -14845,11 +14963,11 @@ function providerCapacityLines(read3) {
14845
14963
  ];
14846
14964
  }
14847
14965
  function liveSyncLine(read3) {
14848
- const performance = record9(read3.body.performance) ? read3.body.performance : {};
14849
- const commitToSend = record9(performance.commitToSend) ? performance.commitToSend : {};
14966
+ const performance = record10(read3.body.performance) ? read3.body.performance : {};
14967
+ const commitToSend = record10(performance.commitToSend) ? performance.commitToSend : {};
14850
14968
  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`;
14851
14969
  }
14852
- function record9(value2) {
14970
+ function record10(value2) {
14853
14971
  return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
14854
14972
  }
14855
14973
  function numeric3(value2) {
@@ -15197,7 +15315,7 @@ async function monitorCommand(parsed, deps = {}) {
15197
15315
  if (action2 === "plan" || action2 === "apply") {
15198
15316
  const desired = monitoringWireConfig(context.cfg, env);
15199
15317
  const live = await request2(`${base}?env=${encodeURIComponent(env)}`, { headers }, doFetch);
15200
- const currentRevision = record10(live.config) ? string(live.config.revision) : null;
15318
+ const currentRevision = record11(live.config) ? string(live.config.revision) : null;
15201
15319
  const changed = currentRevision !== desired.revision;
15202
15320
  const plan = {
15203
15321
  schemaVersion: 1,
@@ -15242,7 +15360,7 @@ async function monitorCommand(parsed, deps = {}) {
15242
15360
  headers
15243
15361
  }, doFetch);
15244
15362
  emit3(result2, jsonOutput, out, () => {
15245
- const run = record10(result2.run) ? result2.run : {};
15363
+ const run = record11(result2.run) ? result2.run : {};
15246
15364
  out.log(`monitor run ${appId}/${env}/${probeId}: ${string(run.outcome) ?? "unknown"}${run.failure_code ? ` (${String(run.failure_code)})` : ""}`);
15247
15365
  });
15248
15366
  return;
@@ -15267,12 +15385,12 @@ async function request2(url, init, doFetch) {
15267
15385
  let body = {};
15268
15386
  try {
15269
15387
  const parsed = text4 ? JSON.parse(text4) : {};
15270
- body = record10(parsed) ? parsed : { value: parsed };
15388
+ body = record11(parsed) ? parsed : { value: parsed };
15271
15389
  } catch {
15272
15390
  body = { message: text4.slice(0, 500) };
15273
15391
  }
15274
15392
  if (!response2.ok) {
15275
- const error = record10(body.error) ? body.error : body;
15393
+ const error = record11(body.error) ? body.error : body;
15276
15394
  throw new Error(string(error.message) ?? string(error.code) ?? `monitor request failed (${response2.status})`);
15277
15395
  }
15278
15396
  return body;
@@ -15280,7 +15398,7 @@ async function request2(url, init, doFetch) {
15280
15398
  function printRead(action2, appId, env, result, out) {
15281
15399
  if (action2 === "status") {
15282
15400
  out.log(`monitor status ${appId}/${env}: ${String(result.overall ?? (result.configured === false ? "not configured" : "unknown"))}`);
15283
- const slos = Array.isArray(result.slos) ? result.slos.filter(record10) : [];
15401
+ const slos = Array.isArray(result.slos) ? result.slos.filter(record11) : [];
15284
15402
  for (const slo of slos) out.log(`slo ${String(slo.id)} ${String(slo.state)} ${percent(slo.observed)} observed ${percent(slo.budgetRemaining)} budget remaining`);
15285
15403
  const incidents = Array.isArray(result.openIncidents) ? result.openIncidents.length : 0;
15286
15404
  const gaps = Array.isArray(result.monitoringGaps) ? result.monitoringGaps.length : 0;
@@ -15289,20 +15407,20 @@ function printRead(action2, appId, env, result, out) {
15289
15407
  return;
15290
15408
  }
15291
15409
  if (action2 === "incidents") {
15292
- const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record10) : [];
15410
+ const incidents = Array.isArray(result.incidents) ? result.incidents.filter(record11) : [];
15293
15411
  out.log(`monitor incidents ${appId}/${env}: ${incidents.length}`);
15294
15412
  for (const incident2 of incidents) out.log(`${String(incident2.state)} ${String(incident2.kind)} ${String(incident2.slo_id)} ${new Date(Number(incident2.opened_at)).toISOString()}`);
15295
15413
  return;
15296
15414
  }
15297
15415
  out.log(`monitor report ${appId}/${env}: ${String(result.period)} ${String(result.overall)}`);
15298
- const probes = Array.isArray(result.probes) ? result.probes.filter(record10) : [];
15416
+ const probes = Array.isArray(result.probes) ? result.probes.filter(record11) : [];
15299
15417
  for (const probe of probes) out.log(`probe ${String(probe.id)} ${Number(probe.good)} good ${Number(probe.bad)} bad ${Number(probe.unknown)} unknown`);
15300
15418
  }
15301
15419
  function emit3(value2, json, out, human) {
15302
15420
  if (json) out.log(JSON.stringify(value2, null, 2));
15303
15421
  else human();
15304
15422
  }
15305
- function record10(value2) {
15423
+ function record11(value2) {
15306
15424
  return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
15307
15425
  }
15308
15426
  function string(value2) {
@@ -15870,6 +15988,139 @@ var init_provision = __esm({
15870
15988
  }
15871
15989
  });
15872
15990
 
15991
+ // src/security-command-context.ts
15992
+ async function hostedSecurityContext(parsed, dependencies) {
15993
+ const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
15994
+ const cfg = await loadProjectConfig(configPath);
15995
+ const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
15996
+ if (!env || !cfg.envs.includes(env)) {
15997
+ throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
15998
+ }
15999
+ const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
16000
+ if (platformAudience(cfg.platformUrl) !== platform) {
16001
+ throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
16002
+ }
16003
+ const doFetch = dependencies.fetch ?? fetch;
16004
+ const stdout = dependencies.stdout ?? console;
16005
+ const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
16006
+ const token = await getDeveloperToken(
16007
+ cfg,
16008
+ { configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
16009
+ doFetch,
16010
+ stdout,
16011
+ { optionalProjectCapabilities: ["app.manage"] }
16012
+ );
16013
+ return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
16014
+ }
16015
+ function requiredSecurityPositional(parsed, index, label) {
16016
+ const value2 = parsed.positionals[index];
16017
+ if (!value2) throw new Error(`${label} is required`);
16018
+ return value2;
16019
+ }
16020
+ function securityProfile(value2) {
16021
+ if (value2 === void 0) return void 0;
16022
+ if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
16023
+ throw new Error("--profile must be odla, cloudflare-app, or generic");
16024
+ }
16025
+ var init_security_command_context = __esm({
16026
+ "src/security-command-context.ts"() {
16027
+ "use strict";
16028
+ init_cjs_shims();
16029
+ init_argv();
16030
+ init_config();
16031
+ init_token();
16032
+ }
16033
+ });
16034
+
16035
+ // src/promotion-command.ts
16036
+ async function promotionCommand(parsed, dependencies) {
16037
+ const action2 = parsed.positionals[1];
16038
+ if (!action2 || !["plan", "inspect", "status", "apply", "rollback", "next", "record", "cancel"].includes(action2)) rejectWord(["promotion"], action2);
16039
+ assertArgs(parsed, ["config", "env", "platform", "email", "open", "json", "file", "approval", "idempotency-key"], 5);
16040
+ const context = await hostedSecurityContext(parsed, dependencies);
16041
+ const client = (0, import_apps14.createAppsClient)({
16042
+ token: context.token,
16043
+ fetcher: { fetch: (request3, init) => {
16044
+ const internal = new URL(typeof request3 === "string" ? request3 : request3.url);
16045
+ return context.fetch(`${context.platform}${internal.pathname}${internal.search}`, init);
16046
+ } }
16047
+ });
16048
+ const json = parsed.options.json === true;
16049
+ if (action2 === "plan") {
16050
+ if (parsed.positionals[2]) throw new Error("promotion plan takes --file, not a positional plan id");
16051
+ const file = stringOpt(parsed.options.file);
16052
+ if (!file) throw new Error("--file <promotion-plan.json> is required");
16053
+ const source = JSON.parse(await (0, import_promises12.readFile)(file, "utf8"));
16054
+ const plan2 = await client.createPromotionReleasePlan(context.appId, source);
16055
+ render(plan2, json, context.stdout);
16056
+ return;
16057
+ }
16058
+ const planId = requiredSecurityPositional(parsed, 2, "plan id");
16059
+ if (action2 === "inspect") {
16060
+ if (parsed.positionals[3]) throw new Error("promotion inspect takes only a plan id");
16061
+ const plan2 = await client.getPromotionReleasePlan(context.appId, planId);
16062
+ if (!plan2) throw new Error(`promotion release ${planId} was not found`);
16063
+ render(plan2, json, context.stdout);
16064
+ return;
16065
+ }
16066
+ if (action2 === "status") {
16067
+ if (parsed.positionals[3]) throw new Error("promotion status takes only a plan id");
16068
+ const status = await client.getPromotionReleaseStatus(context.appId, planId);
16069
+ if (!status) throw new Error(`promotion release ${planId} was not found`);
16070
+ render(status, json, context.stdout);
16071
+ return;
16072
+ }
16073
+ if (action2 === "next" || action2 === "cancel" || action2 === "record") {
16074
+ const operationId = requiredSecurityPositional(parsed, 3, "operation id");
16075
+ if (action2 === "next") {
16076
+ if (parsed.positionals[4]) throw new Error("promotion next takes only plan and operation ids");
16077
+ render(await client.claimPromotionReleaseWork(context.appId, planId, operationId), json, context.stdout);
16078
+ return;
16079
+ }
16080
+ if (action2 === "cancel") {
16081
+ if (parsed.positionals[4]) throw new Error("promotion cancel takes only plan and operation ids");
16082
+ render(await client.cancelPromotionReleaseOperation(context.appId, planId, operationId), json, context.stdout);
16083
+ return;
16084
+ }
16085
+ const stepId = requiredSecurityPositional(parsed, 4, "step id");
16086
+ const file = stringOpt(parsed.options.file);
16087
+ if (!file) throw new Error("--file <step-receipt.json> is required");
16088
+ const receipt = JSON.parse(await (0, import_promises12.readFile)(file, "utf8"));
16089
+ render(await client.recordPromotionReleaseWork(context.appId, planId, operationId, stepId, receipt), json, context.stdout);
16090
+ return;
16091
+ }
16092
+ const approvalId = stringOpt(parsed.options.approval);
16093
+ if (parsed.positionals[3]) throw new Error(`promotion ${action2} takes only a plan id`);
16094
+ if (!approvalId) throw new Error(`--approval <approval-id> is required for promotion ${action2}`);
16095
+ const plan = await client.getPromotionReleasePlan(context.appId, planId);
16096
+ if (!plan) throw new Error(`promotion release ${planId} was not found`);
16097
+ if (plan.state !== "reviewable") throw new Error(`promotion release is ${plan.state}: ${plan.blockers.join("; ")}`);
16098
+ const operationAction = action2;
16099
+ const operation = await client.requestPromotionReleaseOperation(context.appId, planId, operationAction, {
16100
+ approvalId,
16101
+ planDigest: plan.planDigest,
16102
+ targetSetDigest: plan.targetSetDigest,
16103
+ idempotencyKey: stringOpt(parsed.options["idempotency-key"]) ?? `${operationAction}:${planId}`
16104
+ });
16105
+ render(operation, json, context.stdout);
16106
+ }
16107
+ var import_promises12, import_apps14, render;
16108
+ var init_promotion_command = __esm({
16109
+ "src/promotion-command.ts"() {
16110
+ "use strict";
16111
+ init_cjs_shims();
16112
+ import_promises12 = require("fs/promises");
16113
+ import_apps14 = require("@odla-ai/apps");
16114
+ init_argv();
16115
+ init_security_command_context();
16116
+ init_surface();
16117
+ render = (value2, json, out) => {
16118
+ if (json) out.log(JSON.stringify(value2));
16119
+ else out.log(JSON.stringify(value2, null, 2));
16120
+ };
16121
+ }
16122
+ });
16123
+
15873
16124
  // src/record.ts
15874
16125
  function recordInvocation(parsed) {
15875
16126
  const file = import_node_process19.default.env.ODLA_CLI_RECORD;
@@ -15901,7 +16152,7 @@ function advisoryCollectingFetch(inner, sink) {
15901
16152
  return (async (input, init) => {
15902
16153
  const response2 = await inner(input, init);
15903
16154
  try {
15904
- sink.push(...(0, import_apps14.parseAdvisories)(response2));
16155
+ sink.push(...(0, import_apps15.parseAdvisories)(response2));
15905
16156
  } catch {
15906
16157
  }
15907
16158
  return response2;
@@ -15920,15 +16171,15 @@ function renderAdvisories(out, advisories, env = process.env) {
15920
16171
  const key = `${advisory.code}:${advisory.message}`;
15921
16172
  if (seen.has(key)) continue;
15922
16173
  seen.add(key);
15923
- out.error((0, import_apps14.formatAdvisory)(advisory));
16174
+ out.error((0, import_apps15.formatAdvisory)(advisory));
15924
16175
  }
15925
16176
  }
15926
- var import_apps14, superseded;
16177
+ var import_apps15, superseded;
15927
16178
  var init_advisory_output = __esm({
15928
16179
  "src/advisory-output.ts"() {
15929
16180
  "use strict";
15930
16181
  init_cjs_shims();
15931
- import_apps14 = require("@odla-ai/apps");
16182
+ import_apps15 = require("@odla-ai/apps");
15932
16183
  superseded = /* @__PURE__ */ new Set();
15933
16184
  }
15934
16185
  });
@@ -16187,7 +16438,7 @@ async function bySlug(ctx, slug) {
16187
16438
  "GET",
16188
16439
  `/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
16189
16440
  );
16190
- const filtered = page2.records.find((record11) => record11.slug === slug);
16441
+ const filtered = page2.records.find((record12) => record12.slug === slug);
16191
16442
  if (filtered) return filtered;
16192
16443
  const limit = 100;
16193
16444
  for (let offset = 0; ; offset += limit) {
@@ -16196,7 +16447,7 @@ async function bySlug(ctx, slug) {
16196
16447
  "GET",
16197
16448
  `/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
16198
16449
  );
16199
- const found = fallback.records.find((record11) => record11.slug === slug);
16450
+ const found = fallback.records.find((record12) => record12.slug === slug);
16200
16451
  if (found) return found;
16201
16452
  if (!fallback.records.length || offset + fallback.records.length >= fallback.total) break;
16202
16453
  }
@@ -17081,50 +17332,6 @@ var init_runbook_command = __esm({
17081
17332
  }
17082
17333
  });
17083
17334
 
17084
- // src/security-command-context.ts
17085
- async function hostedSecurityContext(parsed, dependencies) {
17086
- const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
17087
- const cfg = await loadProjectConfig(configPath);
17088
- const env = stringOpt(parsed.options.env) ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
17089
- if (!env || !cfg.envs.includes(env)) {
17090
- throw new Error(`env "${env ?? ""}" is not declared in ${configPath}`);
17091
- }
17092
- const platform = platformAudience(stringOpt(parsed.options.platform) ?? cfg.platformUrl);
17093
- if (platformAudience(cfg.platformUrl) !== platform) {
17094
- throw new Error("--platform cannot reuse a project developer token from another platform; update odla.config.mjs and authenticate there");
17095
- }
17096
- const doFetch = dependencies.fetch ?? fetch;
17097
- const stdout = dependencies.stdout ?? console;
17098
- const open = parsed.options.open === false ? false : parsed.options.open === true ? true : void 0;
17099
- const token = await getDeveloperToken(
17100
- cfg,
17101
- { configPath, email: stringOpt(parsed.options.email), open, openApprovalUrl: dependencies.openUrl },
17102
- doFetch,
17103
- stdout,
17104
- { optionalProjectCapabilities: ["app.manage"] }
17105
- );
17106
- return { platform, token, appId: cfg.app.id, env, fetch: doFetch, stdout };
17107
- }
17108
- function requiredSecurityPositional(parsed, index, label) {
17109
- const value2 = parsed.positionals[index];
17110
- if (!value2) throw new Error(`${label} is required`);
17111
- return value2;
17112
- }
17113
- function securityProfile(value2) {
17114
- if (value2 === void 0) return void 0;
17115
- if (value2 === "odla" || value2 === "cloudflare-app" || value2 === "generic") return value2;
17116
- throw new Error("--profile must be odla, cloudflare-app, or generic");
17117
- }
17118
- var init_security_command_context = __esm({
17119
- "src/security-command-context.ts"() {
17120
- "use strict";
17121
- init_cjs_shims();
17122
- init_argv();
17123
- init_config();
17124
- init_token();
17125
- }
17126
- });
17127
-
17128
17335
  // src/security-command-output.ts
17129
17336
  function printHostedSecurityPlan(out, plan, appId) {
17130
17337
  out.log(`Hosted security plan for ${appId}/${plan.env}: ${plan.ready ? "ready" : "not ready"}`);
@@ -17916,6 +18123,10 @@ async function dispatchCli(argv2, dependencies) {
17916
18123
  await securityCommand(parsed, runtime);
17917
18124
  return;
17918
18125
  }
18126
+ if (command === "promotion") {
18127
+ await promotionCommand(parsed, runtime);
18128
+ return;
18129
+ }
17919
18130
  if (command === "brand") {
17920
18131
  await brandCommand(parsed, runtime);
17921
18132
  return;
@@ -18045,6 +18256,7 @@ var init_cli = __esm({
18045
18256
  init_o11y_command();
18046
18257
  init_monitor_command();
18047
18258
  init_provision();
18259
+ init_promotion_command();
18048
18260
  init_record();
18049
18261
  init_advisory_output();
18050
18262
  init_cached_credential();