@odla-ai/cli 0.37.1 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +189 -60
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-XFIFFYNH.js → chunk-DKOJBWRJ.js} +173 -54
- package/dist/chunk-DKOJBWRJ.js.map +1 -0
- package/dist/{cli-OH6PCYZ3.js → cli-DZKEPGBZ.js} +2 -2
- package/dist/index.cjs +172 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-XFIFFYNH.js.map +0 -1
- /package/dist/{cli-OH6PCYZ3.js.map → cli-DZKEPGBZ.js.map} +0 -0
package/dist/bin.cjs
CHANGED
|
@@ -1322,6 +1322,74 @@ var init_argv = __esm({
|
|
|
1322
1322
|
}
|
|
1323
1323
|
});
|
|
1324
1324
|
|
|
1325
|
+
// src/admin-spend.ts
|
|
1326
|
+
async function call(ctx, method, scope) {
|
|
1327
|
+
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
1328
|
+
const response2 = await ctx.doFetch(url, {
|
|
1329
|
+
method,
|
|
1330
|
+
headers: { authorization: `Bearer ${ctx.token}` }
|
|
1331
|
+
});
|
|
1332
|
+
const body = await response2.json().catch(() => ({}));
|
|
1333
|
+
if (!response2.ok) {
|
|
1334
|
+
const detail = body.error?.message ?? body.error?.code ?? `registry returned ${response2.status}`;
|
|
1335
|
+
throw new Error(`spend ${method} failed: ${detail} (${response2.status})`);
|
|
1336
|
+
}
|
|
1337
|
+
return body;
|
|
1338
|
+
}
|
|
1339
|
+
async function spendShow(ctx, scope) {
|
|
1340
|
+
const view = await call(ctx, "GET", scope);
|
|
1341
|
+
if (ctx.json) return ctx.out.log(JSON.stringify(view, null, 2));
|
|
1342
|
+
ctx.out.log(`scope: ${view.scope}`);
|
|
1343
|
+
ctx.out.log(`day: ${view.day} (UTC)`);
|
|
1344
|
+
ctx.out.log(
|
|
1345
|
+
`spent: ${money(view.spentUsd)} of ${view.capUsd > 0 ? money(view.capUsd) : "(no cap set)"}`
|
|
1346
|
+
);
|
|
1347
|
+
ctx.out.log(`calls: ${view.calls}`);
|
|
1348
|
+
if (view.unpricedCalls > 0) {
|
|
1349
|
+
ctx.out.log(
|
|
1350
|
+
`unpriced: ${view.unpricedCalls} call(s) had no price, so "spent" is a floor, not the total.`
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
if (!view.latched) return ctx.out.log("status: running");
|
|
1354
|
+
ctx.out.log(
|
|
1355
|
+
`status: HALTED \u2014 reached ${money(view.latched.capUsd)} on ${view.latched.day} (${view.latched.reason}), at ${money(view.latched.costUsd)}.`
|
|
1356
|
+
);
|
|
1357
|
+
ctx.out.log("A new day does not clear this. Resume with:");
|
|
1358
|
+
ctx.out.log(` odla-ai admin spend reset ${view.scope}`);
|
|
1359
|
+
}
|
|
1360
|
+
async function spendReset(ctx, scope) {
|
|
1361
|
+
const result = await call(ctx, "DELETE", scope);
|
|
1362
|
+
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
1363
|
+
ctx.out.log(result.message);
|
|
1364
|
+
if (result.stillOverCap) {
|
|
1365
|
+
ctx.out.log(
|
|
1366
|
+
` today: ${money(result.spentUsd)} against a ${money(result.capUsd)} cap.`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
async function adminSpend(parsed, ctx) {
|
|
1371
|
+
const action2 = parsed.positionals[2];
|
|
1372
|
+
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1373
|
+
if (action2 !== "show" && action2 !== "reset") {
|
|
1374
|
+
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1375
|
+
}
|
|
1376
|
+
if (!scope) {
|
|
1377
|
+
throw new Error(
|
|
1378
|
+
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
return action2 === "show" ? spendShow(ctx, scope) : spendReset(ctx, scope);
|
|
1382
|
+
}
|
|
1383
|
+
var money;
|
|
1384
|
+
var init_admin_spend = __esm({
|
|
1385
|
+
"src/admin-spend.ts"() {
|
|
1386
|
+
"use strict";
|
|
1387
|
+
init_cjs_shims();
|
|
1388
|
+
init_argv();
|
|
1389
|
+
money = (value2) => `$${value2.toFixed(2)}`;
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1325
1393
|
// src/ai-config-validation.ts
|
|
1326
1394
|
function validateAiConfig(cfg, path) {
|
|
1327
1395
|
if (cfg.ai === void 0) return;
|
|
@@ -2141,6 +2209,31 @@ var init_operator_context = __esm({
|
|
|
2141
2209
|
async function adminCommand(parsed, deps = {}) {
|
|
2142
2210
|
const area = parsed.positionals[1];
|
|
2143
2211
|
const action2 = parsed.positionals[2];
|
|
2212
|
+
if (area === "spend") {
|
|
2213
|
+
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
2214
|
+
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
2215
|
+
const out = deps.stdout ?? console;
|
|
2216
|
+
const doFetch = deps.fetch ?? fetch;
|
|
2217
|
+
const token = await getDeveloperToken(
|
|
2218
|
+
context2.cfg,
|
|
2219
|
+
{
|
|
2220
|
+
configPath: context2.cfg.configPath,
|
|
2221
|
+
token: stringOpt(parsed.options.token),
|
|
2222
|
+
email: stringOpt(parsed.options.email),
|
|
2223
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
2224
|
+
openApprovalUrl: deps.openUrl
|
|
2225
|
+
},
|
|
2226
|
+
doFetch,
|
|
2227
|
+
out
|
|
2228
|
+
);
|
|
2229
|
+
return adminSpend(parsed, {
|
|
2230
|
+
platformUrl: context2.platform.value,
|
|
2231
|
+
token,
|
|
2232
|
+
doFetch,
|
|
2233
|
+
json: parsed.options.json === true,
|
|
2234
|
+
out
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2144
2237
|
const credentialSet = action2 === "credential" && parsed.positionals[3] === "set";
|
|
2145
2238
|
const credentials = action2 === "credentials";
|
|
2146
2239
|
const models = action2 === "models";
|
|
@@ -2190,6 +2283,8 @@ var init_admin_command = __esm({
|
|
|
2190
2283
|
"use strict";
|
|
2191
2284
|
init_cjs_shims();
|
|
2192
2285
|
init_admin_ai();
|
|
2286
|
+
init_admin_spend();
|
|
2287
|
+
init_token();
|
|
2193
2288
|
init_argv();
|
|
2194
2289
|
init_operator_context();
|
|
2195
2290
|
CONTEXT_OPTIONS = ["platform", "config", "context", "token", "open", "email"];
|
|
@@ -2265,7 +2360,8 @@ async function fetchIdentity(platformUrl, token, doFetch) {
|
|
|
2265
2360
|
email,
|
|
2266
2361
|
admin: body.admin === true,
|
|
2267
2362
|
machine,
|
|
2268
|
-
scopes
|
|
2363
|
+
scopes,
|
|
2364
|
+
projects: Array.isArray(body.projects) ? body.projects.map(String) : null
|
|
2269
2365
|
};
|
|
2270
2366
|
}
|
|
2271
2367
|
function credentialLabel(identity) {
|
|
@@ -2335,6 +2431,13 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
2335
2431
|
out.log(`credential id: ${identity.credential.id}`);
|
|
2336
2432
|
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
2337
2433
|
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
2434
|
+
if (identity.projects === null) {
|
|
2435
|
+
out.log("projects: (this registry does not report project grants)");
|
|
2436
|
+
} else if (identity.projects.length) {
|
|
2437
|
+
out.log(`projects: ${identity.projects.join(", ")}`);
|
|
2438
|
+
} else {
|
|
2439
|
+
out.log("projects: (none \u2014 every pm and discuss call will be refused)");
|
|
2440
|
+
}
|
|
2338
2441
|
if (!identity.admin) {
|
|
2339
2442
|
if (identity.scopes.includes("platform:runbook:write")) {
|
|
2340
2443
|
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
@@ -6211,17 +6314,17 @@ var init_cli_project = __esm({
|
|
|
6211
6314
|
}
|
|
6212
6315
|
});
|
|
6213
6316
|
|
|
6214
|
-
// ../harness/dist/chunk-
|
|
6317
|
+
// ../harness/dist/chunk-LNQNFGQC.js
|
|
6215
6318
|
var HARNESS_PROTOCOL_VERSION;
|
|
6216
|
-
var
|
|
6217
|
-
"../harness/dist/chunk-
|
|
6319
|
+
var init_chunk_LNQNFGQC = __esm({
|
|
6320
|
+
"../harness/dist/chunk-LNQNFGQC.js"() {
|
|
6218
6321
|
"use strict";
|
|
6219
6322
|
init_cjs_shims();
|
|
6220
6323
|
HARNESS_PROTOCOL_VERSION = 1;
|
|
6221
6324
|
}
|
|
6222
6325
|
});
|
|
6223
6326
|
|
|
6224
|
-
// ../harness/dist/chunk-
|
|
6327
|
+
// ../harness/dist/chunk-K76I2TCQ.js
|
|
6225
6328
|
function assertPinnedImage(image) {
|
|
6226
6329
|
if (!DIGEST_IMAGE.test(image)) throw new TypeError("container image must be pinned by sha256 digest");
|
|
6227
6330
|
}
|
|
@@ -6570,8 +6673,8 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
6570
6673
|
}
|
|
6571
6674
|
}
|
|
6572
6675
|
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;
|
|
6573
|
-
var
|
|
6574
|
-
"../harness/dist/chunk-
|
|
6676
|
+
var init_chunk_K76I2TCQ = __esm({
|
|
6677
|
+
"../harness/dist/chunk-K76I2TCQ.js"() {
|
|
6575
6678
|
"use strict";
|
|
6576
6679
|
init_cjs_shims();
|
|
6577
6680
|
import_child_process = require("child_process");
|
|
@@ -7569,7 +7672,7 @@ var init_code2 = __esm({
|
|
|
7569
7672
|
}
|
|
7570
7673
|
});
|
|
7571
7674
|
|
|
7572
|
-
// ../harness/dist/chunk-
|
|
7675
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
7573
7676
|
async function digestStagedWorkspace(root, limits) {
|
|
7574
7677
|
const files = [];
|
|
7575
7678
|
const walk = async (directory) => {
|
|
@@ -7647,7 +7750,7 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7647
7750
|
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
7648
7751
|
}
|
|
7649
7752
|
const request3 = options.fetch ?? fetch;
|
|
7650
|
-
const
|
|
7753
|
+
const call4 = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
7651
7754
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
7652
7755
|
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
7653
7756
|
let response2;
|
|
@@ -7677,17 +7780,17 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7677
7780
|
return {
|
|
7678
7781
|
heartbeat: async (version, capabilities) => {
|
|
7679
7782
|
validateHeartbeat(version, capabilities);
|
|
7680
|
-
return parseSnapshot(await
|
|
7783
|
+
return parseSnapshot(await call4("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
7681
7784
|
},
|
|
7682
7785
|
acknowledge: async (commandId, result) => {
|
|
7683
7786
|
if (!/^ccmd_[0-9a-f]{32}$/.test(commandId)) throw new TypeError("invalid Code runtime command id");
|
|
7684
|
-
await
|
|
7787
|
+
await call4(`/registry/code/runtime/commands/${commandId}/ack`, result);
|
|
7685
7788
|
},
|
|
7686
7789
|
source: async (sessionId) => parseSource(
|
|
7687
|
-
await
|
|
7790
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
7688
7791
|
),
|
|
7689
7792
|
infer: async (sessionId, inference) => {
|
|
7690
|
-
const value2 = record5(await
|
|
7793
|
+
const value2 = record5(await call4(
|
|
7691
7794
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
7692
7795
|
inference,
|
|
7693
7796
|
modelRequestTimeoutMs
|
|
@@ -7698,11 +7801,11 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7698
7801
|
return value2;
|
|
7699
7802
|
},
|
|
7700
7803
|
review: async (sessionId, review) => parseReview(
|
|
7701
|
-
await
|
|
7804
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
7702
7805
|
),
|
|
7703
7806
|
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
7704
7807
|
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
7705
|
-
return parseCandidate(await
|
|
7808
|
+
return parseCandidate(await call4(
|
|
7706
7809
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
7707
7810
|
{ checkpointId, verification }
|
|
7708
7811
|
));
|
|
@@ -7712,21 +7815,21 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7712
7815
|
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
7713
7816
|
throw new TypeError("invalid Code session event");
|
|
7714
7817
|
}
|
|
7715
|
-
await
|
|
7818
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
7716
7819
|
},
|
|
7717
7820
|
recallMemories: async (sessionId, subjects, limit) => {
|
|
7718
|
-
const response2 = await
|
|
7821
|
+
const response2 = await call4(
|
|
7719
7822
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
7720
7823
|
{ subjects: [...subjects], limit }
|
|
7721
7824
|
);
|
|
7722
7825
|
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
7723
7826
|
},
|
|
7724
7827
|
rememberMemory: async (sessionId, memory) => {
|
|
7725
|
-
await
|
|
7828
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
7726
7829
|
},
|
|
7727
7830
|
reportSessionFailure: async (sessionId, message2) => {
|
|
7728
7831
|
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
7729
|
-
await
|
|
7832
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
7730
7833
|
}
|
|
7731
7834
|
};
|
|
7732
7835
|
}
|
|
@@ -8512,7 +8615,7 @@ async function materializeCommandWorkspace(input) {
|
|
|
8512
8615
|
}
|
|
8513
8616
|
function codeSkill(opts) {
|
|
8514
8617
|
let seq = 0;
|
|
8515
|
-
const
|
|
8618
|
+
const call4 = async (tool, input, signal) => {
|
|
8516
8619
|
const startedAt = Date.now();
|
|
8517
8620
|
const response2 = await opts.broker.execute(
|
|
8518
8621
|
{ lease: opts.lease, workspaceDir: opts.workspaceDir, signal },
|
|
@@ -8534,7 +8637,7 @@ function codeSkill(opts) {
|
|
|
8534
8637
|
},
|
|
8535
8638
|
additionalProperties: false
|
|
8536
8639
|
},
|
|
8537
|
-
handler: (input, ctx) =>
|
|
8640
|
+
handler: (input, ctx) => call4("sandbox.read", input, ctx.signal)
|
|
8538
8641
|
};
|
|
8539
8642
|
const applyPatch = {
|
|
8540
8643
|
name: "odla_apply_git_diff",
|
|
@@ -8545,7 +8648,7 @@ function codeSkill(opts) {
|
|
|
8545
8648
|
properties: { patch: { type: "string", minLength: 1, maxLength: 262144 } },
|
|
8546
8649
|
additionalProperties: false
|
|
8547
8650
|
},
|
|
8548
|
-
handler: (input, ctx) =>
|
|
8651
|
+
handler: (input, ctx) => call4("sandbox.apply_patch", input, ctx.signal)
|
|
8549
8652
|
};
|
|
8550
8653
|
const runRecipe = {
|
|
8551
8654
|
name: "odla_run_recipe",
|
|
@@ -8556,7 +8659,7 @@ function codeSkill(opts) {
|
|
|
8556
8659
|
properties: { recipeId: { type: "string", minLength: 1, maxLength: 120, pattern: "^[a-zA-Z0-9._:-]+$" } },
|
|
8557
8660
|
additionalProperties: false
|
|
8558
8661
|
},
|
|
8559
|
-
handler: (input, ctx) =>
|
|
8662
|
+
handler: (input, ctx) => call4("sandbox.run_recipe", input, ctx.signal)
|
|
8560
8663
|
};
|
|
8561
8664
|
const listFiles2 = {
|
|
8562
8665
|
name: "odla_list",
|
|
@@ -8569,7 +8672,7 @@ function codeSkill(opts) {
|
|
|
8569
8672
|
},
|
|
8570
8673
|
additionalProperties: false
|
|
8571
8674
|
},
|
|
8572
|
-
handler: (input, ctx) =>
|
|
8675
|
+
handler: (input, ctx) => call4("sandbox.list", input, ctx.signal)
|
|
8573
8676
|
};
|
|
8574
8677
|
const searchFiles = {
|
|
8575
8678
|
name: "odla_search",
|
|
@@ -8585,7 +8688,7 @@ function codeSkill(opts) {
|
|
|
8585
8688
|
},
|
|
8586
8689
|
additionalProperties: false
|
|
8587
8690
|
},
|
|
8588
|
-
handler: (input, ctx) =>
|
|
8691
|
+
handler: (input, ctx) => call4("sandbox.search", input, ctx.signal)
|
|
8589
8692
|
};
|
|
8590
8693
|
const graphTool = (name, tool, description, required) => ({
|
|
8591
8694
|
name,
|
|
@@ -8596,7 +8699,7 @@ function codeSkill(opts) {
|
|
|
8596
8699
|
properties: { query: { type: "string", maxLength: 512 } },
|
|
8597
8700
|
additionalProperties: false
|
|
8598
8701
|
},
|
|
8599
|
-
handler: (input, ctx) =>
|
|
8702
|
+
handler: (input, ctx) => call4(tool, input, ctx.signal)
|
|
8600
8703
|
});
|
|
8601
8704
|
const orientation = [
|
|
8602
8705
|
graphTool(
|
|
@@ -8635,9 +8738,9 @@ async function runCodeAgent(options) {
|
|
|
8635
8738
|
lease: options.lease,
|
|
8636
8739
|
workspaceDir: options.workspaceDir,
|
|
8637
8740
|
surface,
|
|
8638
|
-
onToolCall: (
|
|
8639
|
-
toolCalls.push(
|
|
8640
|
-
options.onToolCall?.(
|
|
8741
|
+
onToolCall: (call4) => {
|
|
8742
|
+
toolCalls.push(call4);
|
|
8743
|
+
options.onToolCall?.(call4);
|
|
8641
8744
|
}
|
|
8642
8745
|
});
|
|
8643
8746
|
const compaction = options.compaction === void 0 ? (0, import_ai4.keepRecentExchanges)({ whenInputTokensExceed: 12e4, keep: 3 }) : options.compaction;
|
|
@@ -8722,6 +8825,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8722
8825
|
call: request3.call
|
|
8723
8826
|
});
|
|
8724
8827
|
state2.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
8828
|
+
const { costUsd } = response2.receipt;
|
|
8829
|
+
if (costUsd === void 0) state2.costKnown = false;
|
|
8830
|
+
else state2.costUsd += costUsd;
|
|
8725
8831
|
await input.event({
|
|
8726
8832
|
type: "usage",
|
|
8727
8833
|
provider: response2.receipt.provider,
|
|
@@ -8731,7 +8837,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8731
8837
|
durationMs: Date.now() - startedAt,
|
|
8732
8838
|
interactionId: command.commandId,
|
|
8733
8839
|
interactionTokens: state2.tokens,
|
|
8734
|
-
interactionMaxTokens: metadata2.maxTokensPerInteraction
|
|
8840
|
+
interactionMaxTokens: metadata2.maxTokensPerInteraction,
|
|
8841
|
+
...costUsd === void 0 ? {} : { costUsd },
|
|
8842
|
+
...state2.costKnown ? { interactionCostUsd: state2.costUsd } : {}
|
|
8735
8843
|
}).catch(() => void 0);
|
|
8736
8844
|
return {
|
|
8737
8845
|
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
@@ -9522,10 +9630,16 @@ async function startGoalPursuit(input) {
|
|
|
9522
9630
|
attempt: async ({ prompt }) => {
|
|
9523
9631
|
const result = await input.attempt(prompt);
|
|
9524
9632
|
return {
|
|
9525
|
-
//
|
|
9526
|
-
//
|
|
9527
|
-
//
|
|
9528
|
-
|
|
9633
|
+
// What the attempt actually spent, so the runner's token_budget and
|
|
9634
|
+
// cost_budget checks can be reached. This used to be a hardcoded 0 with
|
|
9635
|
+
// no cost at all, which made maxTokens and maxUsd unreachable while
|
|
9636
|
+
// callers reasonably read them as hard ceilings.
|
|
9637
|
+
//
|
|
9638
|
+
// costUsd is omitted rather than zeroed when any call in the attempt
|
|
9639
|
+
// was unpriced: the runner only enforces a cost budget while the cost
|
|
9640
|
+
// is known, and a zero would make it enforce against a lie.
|
|
9641
|
+
tokens: result.tokens ?? 0,
|
|
9642
|
+
...result.costUsd === void 0 ? {} : { costUsd: result.costUsd },
|
|
9529
9643
|
...result.status === "failed" ? { error: result.error ?? "attempt failed" } : {}
|
|
9530
9644
|
};
|
|
9531
9645
|
},
|
|
@@ -9554,12 +9668,12 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
9554
9668
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
9555
9669
|
}
|
|
9556
9670
|
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_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, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
|
|
9557
|
-
var
|
|
9558
|
-
"../harness/dist/chunk-
|
|
9671
|
+
var init_chunk_UVGZHNLW = __esm({
|
|
9672
|
+
"../harness/dist/chunk-UVGZHNLW.js"() {
|
|
9559
9673
|
"use strict";
|
|
9560
9674
|
init_cjs_shims();
|
|
9561
|
-
|
|
9562
|
-
|
|
9675
|
+
init_chunk_K76I2TCQ();
|
|
9676
|
+
init_chunk_LNQNFGQC();
|
|
9563
9677
|
import_crypto = require("crypto");
|
|
9564
9678
|
import_promises5 = require("fs/promises");
|
|
9565
9679
|
import_path5 = require("path");
|
|
@@ -9973,7 +10087,7 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
9973
10087
|
recipeAuthorization: this.options.recipeAuthorization
|
|
9974
10088
|
}, lease, metadata2.role));
|
|
9975
10089
|
const startedAt = Date.now();
|
|
9976
|
-
const interaction = { tokens: 0, noticeEmitted: false };
|
|
10090
|
+
const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
|
|
9977
10091
|
const inference = createCodeRuntimeInference({
|
|
9978
10092
|
command,
|
|
9979
10093
|
metadata: metadata2,
|
|
@@ -10011,7 +10125,11 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
10011
10125
|
await this.#diagnostic(command, active, detail);
|
|
10012
10126
|
await this.#failure(command, active, detail);
|
|
10013
10127
|
}
|
|
10014
|
-
return
|
|
10128
|
+
return {
|
|
10129
|
+
...result,
|
|
10130
|
+
tokens: interaction.tokens,
|
|
10131
|
+
...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
|
|
10132
|
+
};
|
|
10015
10133
|
}
|
|
10016
10134
|
/** Report every brokered effect as it starts and finishes. */
|
|
10017
10135
|
#observed(command, active, broker) {
|
|
@@ -10070,8 +10188,8 @@ var init_node = __esm({
|
|
|
10070
10188
|
"../harness/dist/node.js"() {
|
|
10071
10189
|
"use strict";
|
|
10072
10190
|
init_cjs_shims();
|
|
10073
|
-
|
|
10074
|
-
|
|
10191
|
+
init_chunk_UVGZHNLW();
|
|
10192
|
+
init_chunk_K76I2TCQ();
|
|
10075
10193
|
MEASURED_PREMIUM = Object.freeze({
|
|
10076
10194
|
/** 3 racers vs pure depth at equal budget: 21,044 / 7,936. */
|
|
10077
10195
|
racePerRacer: 0.55,
|
|
@@ -11175,6 +11293,8 @@ Usage:
|
|
|
11175
11293
|
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
11176
11294
|
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
11177
11295
|
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
11296
|
+
odla-ai admin spend show <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
11297
|
+
odla-ai admin spend reset <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
11178
11298
|
odla-ai security github connect [--repo owner/name] [--env dev] [continue in Studio; human session required]
|
|
11179
11299
|
odla-ai security github disconnect --source <id> [--env dev] [continue in Studio; human session required]
|
|
11180
11300
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -11269,6 +11389,9 @@ Commands:
|
|
|
11269
11389
|
human session connects one); "code grant request|list|approve|revoke" then
|
|
11270
11390
|
governs unattended access: an agent may request, only a human may approve.
|
|
11271
11391
|
admin Manage platform-funded AI routing/credentials/usage with narrow device grants.
|
|
11392
|
+
"admin spend" reads one app's daily inference spend and resumes it
|
|
11393
|
+
after a cap halts it. The cap is per UTC day; exhausting it LATCHES,
|
|
11394
|
+
and a new day does not clear the latch \u2014 resuming is deliberate.
|
|
11272
11395
|
security Connect GitHub sources and run commit-pinned hosted reviews, or scan a local snapshot.
|
|
11273
11396
|
pm Project management (via @odla-ai/pm): Products contain Projects;
|
|
11274
11397
|
projects contain goals, kanban tasks, decisions, and bugs. Use
|
|
@@ -11276,6 +11399,12 @@ Commands:
|
|
|
11276
11399
|
pass --app/--project explicitly. Same device-grant auth as "app".
|
|
11277
11400
|
Status changes and comments post to each item's @odla-ai/chat
|
|
11278
11401
|
discussion thread.
|
|
11402
|
+
NOTE: "--column ready" is OWNER-ONLY. Creating a task in Ready
|
|
11403
|
+
approves its complete execution contract, so it needs pm.plan,
|
|
11404
|
+
which no device enrollment or handshake approval can grant. An
|
|
11405
|
+
agent proposes in Backlog (the default); a human owner or a
|
|
11406
|
+
pm.plan agent promotes. This is deliberate, not a permission
|
|
11407
|
+
gap \u2014 it was reported as one.
|
|
11279
11408
|
bug Intent-first alias for PM bugs. "bug report" writes to
|
|
11280
11409
|
odla PM; odla product defects do not belong in GitHub Issues.
|
|
11281
11410
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
@@ -14700,7 +14829,7 @@ var init_device_command = __esm({
|
|
|
14700
14829
|
});
|
|
14701
14830
|
|
|
14702
14831
|
// src/runbook-actions.ts
|
|
14703
|
-
async function
|
|
14832
|
+
async function call2(ctx, method, path, body) {
|
|
14704
14833
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
14705
14834
|
method,
|
|
14706
14835
|
headers: {
|
|
@@ -14722,7 +14851,7 @@ async function call(ctx, method, path, body) {
|
|
|
14722
14851
|
throw new Error(message2);
|
|
14723
14852
|
}
|
|
14724
14853
|
async function bySlug(ctx, slug) {
|
|
14725
|
-
const page2 = await
|
|
14854
|
+
const page2 = await call2(
|
|
14726
14855
|
ctx,
|
|
14727
14856
|
"GET",
|
|
14728
14857
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
@@ -14731,7 +14860,7 @@ async function bySlug(ctx, slug) {
|
|
|
14731
14860
|
if (filtered) return filtered;
|
|
14732
14861
|
const limit = 100;
|
|
14733
14862
|
for (let offset = 0; ; offset += limit) {
|
|
14734
|
-
const fallback = await
|
|
14863
|
+
const fallback = await call2(
|
|
14735
14864
|
ctx,
|
|
14736
14865
|
"GET",
|
|
14737
14866
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
@@ -14751,7 +14880,7 @@ async function runbookList(ctx, all, query) {
|
|
|
14751
14880
|
const params = new URLSearchParams();
|
|
14752
14881
|
if (!all) params.set("app", ctx.appId);
|
|
14753
14882
|
if (query) params.set("q", query);
|
|
14754
|
-
const page2 = await
|
|
14883
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
14755
14884
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
14756
14885
|
if (!page2.records.length) return ctx.out.log("(no runbooks)");
|
|
14757
14886
|
ctx.out.log(["SLUG", "STATUS", "V", "SCOPE", "UPDATED", "TITLE"].join(" "));
|
|
@@ -14768,7 +14897,7 @@ async function runbookGet(ctx, slug) {
|
|
|
14768
14897
|
ctx.out.log(runbook.body);
|
|
14769
14898
|
}
|
|
14770
14899
|
async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
14771
|
-
const created = await
|
|
14900
|
+
const created = await call2(ctx, "POST", "/runbook", {
|
|
14772
14901
|
appId: ctx.appId,
|
|
14773
14902
|
input: { slug, title, body, ...summary ? { summary } : {}, ...requires ? { requires } : {} }
|
|
14774
14903
|
});
|
|
@@ -14776,7 +14905,7 @@ async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
|
14776
14905
|
}
|
|
14777
14906
|
async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
14778
14907
|
const runbook = await bySlug(ctx, slug);
|
|
14779
|
-
const result = await
|
|
14908
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
14780
14909
|
// An empty --requires clears the declaration; omitting the flag leaves
|
|
14781
14910
|
// whatever is there, so an ordinary body edit never drops it.
|
|
14782
14911
|
patch: {
|
|
@@ -14791,7 +14920,7 @@ async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
|
14791
14920
|
}
|
|
14792
14921
|
async function runbookStatus(ctx, slug, status) {
|
|
14793
14922
|
const runbook = await bySlug(ctx, slug);
|
|
14794
|
-
const result = await
|
|
14923
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
14795
14924
|
patch: { status }
|
|
14796
14925
|
});
|
|
14797
14926
|
ctx.out.log(ctx.json ? JSON.stringify(result, null, 2) : `${slug} \u2192 ${status}`);
|
|
@@ -14800,7 +14929,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
14800
14929
|
if (visibility !== "operator" && visibility !== "admin")
|
|
14801
14930
|
throw new Error(`visibility must be "operator" or "admin", got "${visibility}"`);
|
|
14802
14931
|
const runbook = await bySlug(ctx, slug);
|
|
14803
|
-
const result = await
|
|
14932
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
14804
14933
|
patch: { visibility }
|
|
14805
14934
|
});
|
|
14806
14935
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
@@ -14810,7 +14939,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
14810
14939
|
}
|
|
14811
14940
|
async function runbookHistory(ctx, slug) {
|
|
14812
14941
|
const runbook = await bySlug(ctx, slug);
|
|
14813
|
-
const page2 = await
|
|
14942
|
+
const page2 = await call2(ctx, "GET", `/runbook/${encodeURIComponent(runbook.id)}/revisions`);
|
|
14814
14943
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
14815
14944
|
ctx.out.log(`${slug} is at v${runbook.version}`);
|
|
14816
14945
|
if (!page2.records.length) return ctx.out.log("(no earlier versions)");
|
|
@@ -14820,7 +14949,7 @@ async function runbookHistory(ctx, slug) {
|
|
|
14820
14949
|
}
|
|
14821
14950
|
async function runbookRevert(ctx, slug, version) {
|
|
14822
14951
|
const runbook = await bySlug(ctx, slug);
|
|
14823
|
-
const result = await
|
|
14952
|
+
const result = await call2(
|
|
14824
14953
|
ctx,
|
|
14825
14954
|
"POST",
|
|
14826
14955
|
`/runbook/${encodeURIComponent(runbook.id)}/revert`,
|
|
@@ -14831,7 +14960,7 @@ async function runbookRevert(ctx, slug, version) {
|
|
|
14831
14960
|
}
|
|
14832
14961
|
async function runbookRemove(ctx, slug) {
|
|
14833
14962
|
const runbook = await bySlug(ctx, slug);
|
|
14834
|
-
await
|
|
14963
|
+
await call2(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
14835
14964
|
ctx.out.log(`removed ${slug}`);
|
|
14836
14965
|
}
|
|
14837
14966
|
var import_node_fs21, PLATFORM_SCOPE, stamp;
|
|
@@ -14902,7 +15031,7 @@ ${counts.created} created, ${counts.updated} updated, ${counts.unchanged} unchan
|
|
|
14902
15031
|
async function upsert(ctx, r, visibility) {
|
|
14903
15032
|
const found = await bySlug(ctx, r.slug).catch(() => null);
|
|
14904
15033
|
if (!found) {
|
|
14905
|
-
await
|
|
15034
|
+
await call2(ctx, "POST", "/runbook", {
|
|
14906
15035
|
appId: ctx.appId,
|
|
14907
15036
|
input: {
|
|
14908
15037
|
slug: r.slug,
|
|
@@ -14921,7 +15050,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
14921
15050
|
ctx.out.log(` = ${r.slug} (already current at v${found.version})`);
|
|
14922
15051
|
return "unchanged";
|
|
14923
15052
|
}
|
|
14924
|
-
const result = await
|
|
15053
|
+
const result = await call2(
|
|
14925
15054
|
ctx,
|
|
14926
15055
|
"PATCH",
|
|
14927
15056
|
`/runbook/${encodeURIComponent(found.id)}`,
|
|
@@ -15160,7 +15289,7 @@ async function assessImpact(ctx, surfaces, all, limit) {
|
|
|
15160
15289
|
for (const surface of surfaces) {
|
|
15161
15290
|
const params = new URLSearchParams({ q: surface.query, limit: String(limit) });
|
|
15162
15291
|
if (!all) params.set("app", ctx.appId);
|
|
15163
|
-
const result = await
|
|
15292
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
15164
15293
|
const runbooks = result.outcome === "ranked" ? foldHits(result.hits) : result.candidates.map((c) => ({ ...c, version: 0, sections: [] }));
|
|
15165
15294
|
out.push({ surface, runbooks });
|
|
15166
15295
|
}
|
|
@@ -15256,7 +15385,7 @@ function lintRunbook(runbook, installed) {
|
|
|
15256
15385
|
async function runbookLint(ctx, all) {
|
|
15257
15386
|
const params = new URLSearchParams();
|
|
15258
15387
|
if (!all) params.set("app", ctx.appId);
|
|
15259
|
-
const page2 = await
|
|
15388
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
15260
15389
|
const installed = { "@odla-ai/cli": cliVersion() };
|
|
15261
15390
|
const findings = page2.records.flatMap((runbook) => lintRunbook(runbook, installed));
|
|
15262
15391
|
if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page2.records.length, findings }, null, 2));
|
|
@@ -15291,7 +15420,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
15291
15420
|
const params = new URLSearchParams({ q: query });
|
|
15292
15421
|
if (!all) params.set("app", ctx.appId);
|
|
15293
15422
|
if (limit) params.set("limit", String(limit));
|
|
15294
|
-
const result = await
|
|
15423
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
15295
15424
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
15296
15425
|
if (result.outcome === "empty-corpus") return ctx.out.log("no runbooks are available to search");
|
|
15297
15426
|
if (result.outcome === "no-match") return ctx.out.log(`no runbook mentions "${query}"`);
|
|
@@ -15309,7 +15438,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
15309
15438
|
}
|
|
15310
15439
|
}
|
|
15311
15440
|
async function runbookAsk(ctx, question, all) {
|
|
15312
|
-
const result = await
|
|
15441
|
+
const result = await call2(ctx, "POST", "/runbook/ask", {
|
|
15313
15442
|
question,
|
|
15314
15443
|
...all ? {} : { app: ctx.appId }
|
|
15315
15444
|
});
|
|
@@ -15339,7 +15468,7 @@ async function runbookAsk(ctx, question, all) {
|
|
|
15339
15468
|
}
|
|
15340
15469
|
async function runbookComment(ctx, slug, body) {
|
|
15341
15470
|
const found = await bySlug(ctx, slug);
|
|
15342
|
-
await
|
|
15471
|
+
await call2(ctx, "POST", `/runbook/${encodeURIComponent(found.id)}/comments`, { body });
|
|
15343
15472
|
ctx.out.log(`commented on ${slug} (v${found.version})`);
|
|
15344
15473
|
}
|
|
15345
15474
|
var JSDOC_POINTER;
|