@odla-ai/cli 0.37.1 → 0.38.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 +199 -62
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-XFIFFYNH.js → chunk-HETCZVFB.js} +183 -56
- package/dist/chunk-HETCZVFB.js.map +1 -0
- package/dist/{cli-OH6PCYZ3.js → cli-LGYDXY5R.js} +2 -2
- package/dist/index.cjs +182 -55
- 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-LGYDXY5R.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -174,7 +174,7 @@ function absoluteEntryPath(entryPath) {
|
|
|
174
174
|
|
|
175
175
|
// src/bin.ts
|
|
176
176
|
var argv = process.argv.slice(2);
|
|
177
|
-
requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-
|
|
177
|
+
requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-LGYDXY5R.js")).runCli()).catch((err) => {
|
|
178
178
|
console.error(redactSecrets(`odla-ai: ${err instanceof Error ? err.message : String(err)}`));
|
|
179
179
|
process.exitCode = exitCodeFor(err);
|
|
180
180
|
});
|
|
@@ -1023,6 +1023,66 @@ function addOption(options, name, value2) {
|
|
|
1023
1023
|
else options[name] = [String(current), String(value2)];
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
+
// src/admin-spend.ts
|
|
1027
|
+
async function call(ctx, method, scope) {
|
|
1028
|
+
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
1029
|
+
const response2 = await ctx.doFetch(url, {
|
|
1030
|
+
method,
|
|
1031
|
+
headers: { authorization: `Bearer ${ctx.token}` }
|
|
1032
|
+
});
|
|
1033
|
+
const body = await response2.json().catch(() => ({}));
|
|
1034
|
+
if (!response2.ok) {
|
|
1035
|
+
const detail = body.error?.message ?? body.error?.code ?? `registry returned ${response2.status}`;
|
|
1036
|
+
throw new Error(`spend ${method} failed: ${detail} (${response2.status})`);
|
|
1037
|
+
}
|
|
1038
|
+
return body;
|
|
1039
|
+
}
|
|
1040
|
+
var money = (value2) => `$${value2.toFixed(2)}`;
|
|
1041
|
+
async function spendShow(ctx, scope) {
|
|
1042
|
+
const view = await call(ctx, "GET", scope);
|
|
1043
|
+
if (ctx.json) return ctx.out.log(JSON.stringify(view, null, 2));
|
|
1044
|
+
ctx.out.log(`scope: ${view.scope}`);
|
|
1045
|
+
ctx.out.log(`day: ${view.day} (UTC)`);
|
|
1046
|
+
ctx.out.log(
|
|
1047
|
+
`spent: ${money(view.spentUsd)} of ${view.capUsd > 0 ? money(view.capUsd) : "(no cap set)"}`
|
|
1048
|
+
);
|
|
1049
|
+
ctx.out.log(`calls: ${view.calls}`);
|
|
1050
|
+
if (view.unpricedCalls > 0) {
|
|
1051
|
+
ctx.out.log(
|
|
1052
|
+
`unpriced: ${view.unpricedCalls} call(s) had no price, so "spent" is a floor, not the total.`
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
if (!view.latched) return ctx.out.log("status: running");
|
|
1056
|
+
ctx.out.log(
|
|
1057
|
+
`status: HALTED \u2014 reached ${money(view.latched.capUsd)} on ${view.latched.day} (${view.latched.reason}), at ${money(view.latched.costUsd)}.`
|
|
1058
|
+
);
|
|
1059
|
+
ctx.out.log("A new day does not clear this. Resume with:");
|
|
1060
|
+
ctx.out.log(` odla-ai admin spend reset ${view.scope}`);
|
|
1061
|
+
}
|
|
1062
|
+
async function spendReset(ctx, scope) {
|
|
1063
|
+
const result = await call(ctx, "DELETE", scope);
|
|
1064
|
+
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
1065
|
+
ctx.out.log(result.message);
|
|
1066
|
+
if (result.stillOverCap) {
|
|
1067
|
+
ctx.out.log(
|
|
1068
|
+
` today: ${money(result.spentUsd)} against a ${money(result.capUsd)} cap.`
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
async function adminSpend(parsed, ctx) {
|
|
1073
|
+
const action2 = parsed.positionals[2];
|
|
1074
|
+
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1075
|
+
if (action2 !== "show" && action2 !== "reset") {
|
|
1076
|
+
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1077
|
+
}
|
|
1078
|
+
if (!scope) {
|
|
1079
|
+
throw new Error(
|
|
1080
|
+
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
return action2 === "show" ? spendShow(ctx, scope) : spendReset(ctx, scope);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1026
1086
|
// src/operator-context.ts
|
|
1027
1087
|
import { existsSync as existsSync6 } from "fs";
|
|
1028
1088
|
import { join as join5, resolve as resolve4 } from "path";
|
|
@@ -1711,7 +1771,11 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1711
1771
|
const appEnvironment = clean2(process11.env.ODLA_APP_ID);
|
|
1712
1772
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1713
1773
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1714
|
-
if (appValue)
|
|
1774
|
+
if (appValue) {
|
|
1775
|
+
for (const id2 of options.allowAppList ? appValue.split(",") : [appValue]) {
|
|
1776
|
+
assertOperatorName(id2.trim(), "app");
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1715
1779
|
if (options.requireApp && !appValue) {
|
|
1716
1780
|
throw new Error(
|
|
1717
1781
|
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
@@ -1802,6 +1866,31 @@ var SET_OPTIONS = [
|
|
|
1802
1866
|
async function adminCommand(parsed, deps = {}) {
|
|
1803
1867
|
const area = parsed.positionals[1];
|
|
1804
1868
|
const action2 = parsed.positionals[2];
|
|
1869
|
+
if (area === "spend") {
|
|
1870
|
+
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
1871
|
+
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
1872
|
+
const out = deps.stdout ?? console;
|
|
1873
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1874
|
+
const token = await getDeveloperToken(
|
|
1875
|
+
context2.cfg,
|
|
1876
|
+
{
|
|
1877
|
+
configPath: context2.cfg.configPath,
|
|
1878
|
+
token: stringOpt(parsed.options.token),
|
|
1879
|
+
email: stringOpt(parsed.options.email),
|
|
1880
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1881
|
+
openApprovalUrl: deps.openUrl
|
|
1882
|
+
},
|
|
1883
|
+
doFetch,
|
|
1884
|
+
out
|
|
1885
|
+
);
|
|
1886
|
+
return adminSpend(parsed, {
|
|
1887
|
+
platformUrl: context2.platform.value,
|
|
1888
|
+
token,
|
|
1889
|
+
doFetch,
|
|
1890
|
+
json: parsed.options.json === true,
|
|
1891
|
+
out
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1805
1894
|
const credentialSet = action2 === "credential" && parsed.positionals[3] === "set";
|
|
1806
1895
|
const credentials = action2 === "credentials";
|
|
1807
1896
|
const models = action2 === "models";
|
|
@@ -1905,7 +1994,8 @@ async function fetchIdentity(platformUrl, token, doFetch) {
|
|
|
1905
1994
|
email,
|
|
1906
1995
|
admin: body.admin === true,
|
|
1907
1996
|
machine,
|
|
1908
|
-
scopes
|
|
1997
|
+
scopes,
|
|
1998
|
+
projects: Array.isArray(body.projects) ? body.projects.map(String) : null
|
|
1909
1999
|
};
|
|
1910
2000
|
}
|
|
1911
2001
|
function credentialLabel(identity) {
|
|
@@ -1975,6 +2065,13 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
1975
2065
|
out.log(`credential id: ${identity.credential.id}`);
|
|
1976
2066
|
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
1977
2067
|
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
2068
|
+
if (identity.projects === null) {
|
|
2069
|
+
out.log("projects: (this registry does not report project grants)");
|
|
2070
|
+
} else if (identity.projects.length) {
|
|
2071
|
+
out.log(`projects: ${identity.projects.join(", ")}`);
|
|
2072
|
+
} else {
|
|
2073
|
+
out.log("projects: (none \u2014 every pm and discuss call will be refused)");
|
|
2074
|
+
}
|
|
1978
2075
|
if (!identity.admin) {
|
|
1979
2076
|
if (identity.scopes.includes("platform:runbook:write")) {
|
|
1980
2077
|
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
@@ -5477,10 +5574,10 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
5477
5574
|
import { cpus, hostname, totalmem } from "os";
|
|
5478
5575
|
import { resolve as resolve11 } from "path";
|
|
5479
5576
|
|
|
5480
|
-
// ../harness/dist/chunk-
|
|
5577
|
+
// ../harness/dist/chunk-LNQNFGQC.js
|
|
5481
5578
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
5482
5579
|
|
|
5483
|
-
// ../harness/dist/chunk-
|
|
5580
|
+
// ../harness/dist/chunk-K76I2TCQ.js
|
|
5484
5581
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5485
5582
|
import { constants } from "fs";
|
|
5486
5583
|
import { access } from "fs/promises";
|
|
@@ -5853,7 +5950,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
5853
5950
|
}
|
|
5854
5951
|
}
|
|
5855
5952
|
|
|
5856
|
-
// ../harness/dist/chunk-
|
|
5953
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
5857
5954
|
import { createHash as createHash3 } from "crypto";
|
|
5858
5955
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
5859
5956
|
import { relative as relative4, resolve as resolve10 } from "path";
|
|
@@ -6190,7 +6287,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6190
6287
|
}
|
|
6191
6288
|
}
|
|
6192
6289
|
|
|
6193
|
-
// ../harness/dist/chunk-
|
|
6290
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6194
6291
|
import { spawn as spawn4 } from "child_process";
|
|
6195
6292
|
import { lstat as lstat2 } from "fs/promises";
|
|
6196
6293
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -6495,7 +6592,7 @@ function looksLikeDestination(value2) {
|
|
|
6495
6592
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text3) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text3);
|
|
6496
6593
|
}
|
|
6497
6594
|
|
|
6498
|
-
// ../harness/dist/chunk-
|
|
6595
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6499
6596
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
6500
6597
|
import { readFile as readFile3 } from "fs/promises";
|
|
6501
6598
|
import { join as join33 } from "path";
|
|
@@ -6763,7 +6860,7 @@ async function buildCodeGraph(input) {
|
|
|
6763
6860
|
return builder.build();
|
|
6764
6861
|
}
|
|
6765
6862
|
|
|
6766
|
-
// ../harness/dist/chunk-
|
|
6863
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6767
6864
|
import { createHash as createHash32 } from "crypto";
|
|
6768
6865
|
async function digestStagedWorkspace(root, limits) {
|
|
6769
6866
|
const files = [];
|
|
@@ -6883,7 +6980,7 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6883
6980
|
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
6884
6981
|
}
|
|
6885
6982
|
const request3 = options.fetch ?? fetch;
|
|
6886
|
-
const
|
|
6983
|
+
const call4 = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
6887
6984
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
6888
6985
|
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
6889
6986
|
let response2;
|
|
@@ -6913,17 +7010,17 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6913
7010
|
return {
|
|
6914
7011
|
heartbeat: async (version, capabilities) => {
|
|
6915
7012
|
validateHeartbeat(version, capabilities);
|
|
6916
|
-
return parseSnapshot(await
|
|
7013
|
+
return parseSnapshot(await call4("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
6917
7014
|
},
|
|
6918
7015
|
acknowledge: async (commandId, result) => {
|
|
6919
7016
|
if (!/^ccmd_[0-9a-f]{32}$/.test(commandId)) throw new TypeError("invalid Code runtime command id");
|
|
6920
|
-
await
|
|
7017
|
+
await call4(`/registry/code/runtime/commands/${commandId}/ack`, result);
|
|
6921
7018
|
},
|
|
6922
7019
|
source: async (sessionId) => parseSource(
|
|
6923
|
-
await
|
|
7020
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
6924
7021
|
),
|
|
6925
7022
|
infer: async (sessionId, inference) => {
|
|
6926
|
-
const value2 = record5(await
|
|
7023
|
+
const value2 = record5(await call4(
|
|
6927
7024
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
6928
7025
|
inference,
|
|
6929
7026
|
modelRequestTimeoutMs
|
|
@@ -6934,11 +7031,11 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6934
7031
|
return value2;
|
|
6935
7032
|
},
|
|
6936
7033
|
review: async (sessionId, review) => parseReview(
|
|
6937
|
-
await
|
|
7034
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
6938
7035
|
),
|
|
6939
7036
|
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
6940
7037
|
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
6941
|
-
return parseCandidate(await
|
|
7038
|
+
return parseCandidate(await call4(
|
|
6942
7039
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
6943
7040
|
{ checkpointId, verification }
|
|
6944
7041
|
));
|
|
@@ -6948,21 +7045,21 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6948
7045
|
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
6949
7046
|
throw new TypeError("invalid Code session event");
|
|
6950
7047
|
}
|
|
6951
|
-
await
|
|
7048
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
6952
7049
|
},
|
|
6953
7050
|
recallMemories: async (sessionId, subjects, limit) => {
|
|
6954
|
-
const response2 = await
|
|
7051
|
+
const response2 = await call4(
|
|
6955
7052
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
6956
7053
|
{ subjects: [...subjects], limit }
|
|
6957
7054
|
);
|
|
6958
7055
|
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
6959
7056
|
},
|
|
6960
7057
|
rememberMemory: async (sessionId, memory) => {
|
|
6961
|
-
await
|
|
7058
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
6962
7059
|
},
|
|
6963
7060
|
reportSessionFailure: async (sessionId, message2) => {
|
|
6964
7061
|
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
6965
|
-
await
|
|
7062
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
6966
7063
|
}
|
|
6967
7064
|
};
|
|
6968
7065
|
}
|
|
@@ -7851,7 +7948,7 @@ var SYSTEM_PROMPT_FOR = {
|
|
|
7851
7948
|
};
|
|
7852
7949
|
function codeSkill(opts) {
|
|
7853
7950
|
let seq = 0;
|
|
7854
|
-
const
|
|
7951
|
+
const call4 = async (tool, input, signal) => {
|
|
7855
7952
|
const startedAt = Date.now();
|
|
7856
7953
|
const response2 = await opts.broker.execute(
|
|
7857
7954
|
{ lease: opts.lease, workspaceDir: opts.workspaceDir, signal },
|
|
@@ -7873,7 +7970,7 @@ function codeSkill(opts) {
|
|
|
7873
7970
|
},
|
|
7874
7971
|
additionalProperties: false
|
|
7875
7972
|
},
|
|
7876
|
-
handler: (input, ctx) =>
|
|
7973
|
+
handler: (input, ctx) => call4("sandbox.read", input, ctx.signal)
|
|
7877
7974
|
};
|
|
7878
7975
|
const applyPatch = {
|
|
7879
7976
|
name: "odla_apply_git_diff",
|
|
@@ -7884,7 +7981,7 @@ function codeSkill(opts) {
|
|
|
7884
7981
|
properties: { patch: { type: "string", minLength: 1, maxLength: 262144 } },
|
|
7885
7982
|
additionalProperties: false
|
|
7886
7983
|
},
|
|
7887
|
-
handler: (input, ctx) =>
|
|
7984
|
+
handler: (input, ctx) => call4("sandbox.apply_patch", input, ctx.signal)
|
|
7888
7985
|
};
|
|
7889
7986
|
const runRecipe = {
|
|
7890
7987
|
name: "odla_run_recipe",
|
|
@@ -7895,7 +7992,7 @@ function codeSkill(opts) {
|
|
|
7895
7992
|
properties: { recipeId: { type: "string", minLength: 1, maxLength: 120, pattern: "^[a-zA-Z0-9._:-]+$" } },
|
|
7896
7993
|
additionalProperties: false
|
|
7897
7994
|
},
|
|
7898
|
-
handler: (input, ctx) =>
|
|
7995
|
+
handler: (input, ctx) => call4("sandbox.run_recipe", input, ctx.signal)
|
|
7899
7996
|
};
|
|
7900
7997
|
const listFiles2 = {
|
|
7901
7998
|
name: "odla_list",
|
|
@@ -7908,7 +8005,7 @@ function codeSkill(opts) {
|
|
|
7908
8005
|
},
|
|
7909
8006
|
additionalProperties: false
|
|
7910
8007
|
},
|
|
7911
|
-
handler: (input, ctx) =>
|
|
8008
|
+
handler: (input, ctx) => call4("sandbox.list", input, ctx.signal)
|
|
7912
8009
|
};
|
|
7913
8010
|
const searchFiles = {
|
|
7914
8011
|
name: "odla_search",
|
|
@@ -7924,7 +8021,7 @@ function codeSkill(opts) {
|
|
|
7924
8021
|
},
|
|
7925
8022
|
additionalProperties: false
|
|
7926
8023
|
},
|
|
7927
|
-
handler: (input, ctx) =>
|
|
8024
|
+
handler: (input, ctx) => call4("sandbox.search", input, ctx.signal)
|
|
7928
8025
|
};
|
|
7929
8026
|
const graphTool = (name, tool, description, required) => ({
|
|
7930
8027
|
name,
|
|
@@ -7935,7 +8032,7 @@ function codeSkill(opts) {
|
|
|
7935
8032
|
properties: { query: { type: "string", maxLength: 512 } },
|
|
7936
8033
|
additionalProperties: false
|
|
7937
8034
|
},
|
|
7938
|
-
handler: (input, ctx) =>
|
|
8035
|
+
handler: (input, ctx) => call4(tool, input, ctx.signal)
|
|
7939
8036
|
});
|
|
7940
8037
|
const orientation = [
|
|
7941
8038
|
graphTool(
|
|
@@ -7974,9 +8071,9 @@ async function runCodeAgent(options) {
|
|
|
7974
8071
|
lease: options.lease,
|
|
7975
8072
|
workspaceDir: options.workspaceDir,
|
|
7976
8073
|
surface,
|
|
7977
|
-
onToolCall: (
|
|
7978
|
-
toolCalls.push(
|
|
7979
|
-
options.onToolCall?.(
|
|
8074
|
+
onToolCall: (call4) => {
|
|
8075
|
+
toolCalls.push(call4);
|
|
8076
|
+
options.onToolCall?.(call4);
|
|
7980
8077
|
}
|
|
7981
8078
|
});
|
|
7982
8079
|
const compaction = options.compaction === void 0 ? keepRecentExchanges({ whenInputTokensExceed: 12e4, keep: 3 }) : options.compaction;
|
|
@@ -8061,6 +8158,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8061
8158
|
call: request3.call
|
|
8062
8159
|
});
|
|
8063
8160
|
state2.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
8161
|
+
const { costUsd } = response2.receipt;
|
|
8162
|
+
if (costUsd === void 0) state2.costKnown = false;
|
|
8163
|
+
else state2.costUsd += costUsd;
|
|
8064
8164
|
await input.event({
|
|
8065
8165
|
type: "usage",
|
|
8066
8166
|
provider: response2.receipt.provider,
|
|
@@ -8070,7 +8170,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8070
8170
|
durationMs: Date.now() - startedAt,
|
|
8071
8171
|
interactionId: command.commandId,
|
|
8072
8172
|
interactionTokens: state2.tokens,
|
|
8073
|
-
interactionMaxTokens: metadata2.maxTokensPerInteraction
|
|
8173
|
+
interactionMaxTokens: metadata2.maxTokensPerInteraction,
|
|
8174
|
+
...costUsd === void 0 ? {} : { costUsd },
|
|
8175
|
+
...state2.costKnown ? { interactionCostUsd: state2.costUsd } : {}
|
|
8074
8176
|
}).catch(() => void 0);
|
|
8075
8177
|
return {
|
|
8076
8178
|
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
@@ -8914,10 +9016,16 @@ async function startGoalPursuit(input) {
|
|
|
8914
9016
|
attempt: async ({ prompt }) => {
|
|
8915
9017
|
const result = await input.attempt(prompt);
|
|
8916
9018
|
return {
|
|
8917
|
-
//
|
|
8918
|
-
//
|
|
8919
|
-
//
|
|
8920
|
-
|
|
9019
|
+
// What the attempt actually spent, so the runner's token_budget and
|
|
9020
|
+
// cost_budget checks can be reached. This used to be a hardcoded 0 with
|
|
9021
|
+
// no cost at all, which made maxTokens and maxUsd unreachable while
|
|
9022
|
+
// callers reasonably read them as hard ceilings.
|
|
9023
|
+
//
|
|
9024
|
+
// costUsd is omitted rather than zeroed when any call in the attempt
|
|
9025
|
+
// was unpriced: the runner only enforces a cost budget while the cost
|
|
9026
|
+
// is known, and a zero would make it enforce against a lie.
|
|
9027
|
+
tokens: result.tokens ?? 0,
|
|
9028
|
+
...result.costUsd === void 0 ? {} : { costUsd: result.costUsd },
|
|
8921
9029
|
...result.status === "failed" ? { error: result.error ?? "attempt failed" } : {}
|
|
8922
9030
|
};
|
|
8923
9031
|
},
|
|
@@ -9131,7 +9239,7 @@ var CodePiRuntimeEngine = class {
|
|
|
9131
9239
|
recipeAuthorization: this.options.recipeAuthorization
|
|
9132
9240
|
}, lease, metadata2.role));
|
|
9133
9241
|
const startedAt = Date.now();
|
|
9134
|
-
const interaction = { tokens: 0, noticeEmitted: false };
|
|
9242
|
+
const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
|
|
9135
9243
|
const inference = createCodeRuntimeInference({
|
|
9136
9244
|
command,
|
|
9137
9245
|
metadata: metadata2,
|
|
@@ -9169,7 +9277,11 @@ var CodePiRuntimeEngine = class {
|
|
|
9169
9277
|
await this.#diagnostic(command, active, detail);
|
|
9170
9278
|
await this.#failure(command, active, detail);
|
|
9171
9279
|
}
|
|
9172
|
-
return
|
|
9280
|
+
return {
|
|
9281
|
+
...result,
|
|
9282
|
+
tokens: interaction.tokens,
|
|
9283
|
+
...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
|
|
9284
|
+
};
|
|
9173
9285
|
}
|
|
9174
9286
|
/** Report every brokered effect as it starts and finishes. */
|
|
9175
9287
|
#observed(command, active, broker) {
|
|
@@ -10278,6 +10390,8 @@ Usage:
|
|
|
10278
10390
|
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
10279
10391
|
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
10280
10392
|
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
10393
|
+
odla-ai admin spend show <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
10394
|
+
odla-ai admin spend reset <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
10281
10395
|
odla-ai security github connect [--repo owner/name] [--env dev] [continue in Studio; human session required]
|
|
10282
10396
|
odla-ai security github disconnect --source <id> [--env dev] [continue in Studio; human session required]
|
|
10283
10397
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -10370,6 +10484,9 @@ Commands:
|
|
|
10370
10484
|
human session connects one); "code grant request|list|approve|revoke" then
|
|
10371
10485
|
governs unattended access: an agent may request, only a human may approve.
|
|
10372
10486
|
admin Manage platform-funded AI routing/credentials/usage with narrow device grants.
|
|
10487
|
+
"admin spend" reads one app's daily inference spend and resumes it
|
|
10488
|
+
after a cap halts it. The cap is per UTC day; exhausting it LATCHES,
|
|
10489
|
+
and a new day does not clear the latch \u2014 resuming is deliberate.
|
|
10373
10490
|
security Connect GitHub sources and run commit-pinned hosted reviews, or scan a local snapshot.
|
|
10374
10491
|
pm Project management (via @odla-ai/pm): Products contain Projects;
|
|
10375
10492
|
projects contain goals, kanban tasks, decisions, and bugs. Use
|
|
@@ -10377,6 +10494,12 @@ Commands:
|
|
|
10377
10494
|
pass --app/--project explicitly. Same device-grant auth as "app".
|
|
10378
10495
|
Status changes and comments post to each item's @odla-ai/chat
|
|
10379
10496
|
discussion thread.
|
|
10497
|
+
NOTE: "--column ready" is OWNER-ONLY. Creating a task in Ready
|
|
10498
|
+
approves its complete execution contract, so it needs pm.plan,
|
|
10499
|
+
which no device enrollment or handshake approval can grant. An
|
|
10500
|
+
agent proposes in Backlog (the default); a human owner or a
|
|
10501
|
+
pm.plan agent promotes. This is deliberate, not a permission
|
|
10502
|
+
gap \u2014 it was reported as one.
|
|
10380
10503
|
bug Intent-first alias for PM bugs. "bug report" writes to
|
|
10381
10504
|
odla PM; odla product defects do not belong in GitHub Issues.
|
|
10382
10505
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
@@ -13491,7 +13614,11 @@ async function revoke(parsed, deps, cfg, doFetch, out, json) {
|
|
|
13491
13614
|
if (json) out.log(JSON.stringify({ deviceId, revoked: true }, null, 2));
|
|
13492
13615
|
}
|
|
13493
13616
|
async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app:device:enroll") {
|
|
13494
|
-
const { credentials } = await resolveOperatorContext(parsed, {
|
|
13617
|
+
const { credentials } = await resolveOperatorContext(parsed, {
|
|
13618
|
+
allowMissingConfig: true,
|
|
13619
|
+
// A device is granted the apps named in ONE approval, so --app is a list here.
|
|
13620
|
+
allowAppList: true
|
|
13621
|
+
});
|
|
13495
13622
|
const scopedTokenFile = credentials.scopedTokenFile;
|
|
13496
13623
|
return getScopedPlatformToken({
|
|
13497
13624
|
platform: cfg.platformUrl,
|
|
@@ -13514,7 +13641,7 @@ function defaultDeviceName() {
|
|
|
13514
13641
|
// src/runbook-actions.ts
|
|
13515
13642
|
import { readFileSync as readFileSync10 } from "fs";
|
|
13516
13643
|
var PLATFORM_SCOPE = "$platform";
|
|
13517
|
-
async function
|
|
13644
|
+
async function call2(ctx, method, path, body) {
|
|
13518
13645
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
13519
13646
|
method,
|
|
13520
13647
|
headers: {
|
|
@@ -13536,7 +13663,7 @@ async function call(ctx, method, path, body) {
|
|
|
13536
13663
|
throw new Error(message2);
|
|
13537
13664
|
}
|
|
13538
13665
|
async function bySlug(ctx, slug) {
|
|
13539
|
-
const page2 = await
|
|
13666
|
+
const page2 = await call2(
|
|
13540
13667
|
ctx,
|
|
13541
13668
|
"GET",
|
|
13542
13669
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
@@ -13545,7 +13672,7 @@ async function bySlug(ctx, slug) {
|
|
|
13545
13672
|
if (filtered) return filtered;
|
|
13546
13673
|
const limit = 100;
|
|
13547
13674
|
for (let offset = 0; ; offset += limit) {
|
|
13548
|
-
const fallback = await
|
|
13675
|
+
const fallback = await call2(
|
|
13549
13676
|
ctx,
|
|
13550
13677
|
"GET",
|
|
13551
13678
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
@@ -13566,7 +13693,7 @@ async function runbookList(ctx, all, query) {
|
|
|
13566
13693
|
const params = new URLSearchParams();
|
|
13567
13694
|
if (!all) params.set("app", ctx.appId);
|
|
13568
13695
|
if (query) params.set("q", query);
|
|
13569
|
-
const page2 = await
|
|
13696
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
13570
13697
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
13571
13698
|
if (!page2.records.length) return ctx.out.log("(no runbooks)");
|
|
13572
13699
|
ctx.out.log(["SLUG", "STATUS", "V", "SCOPE", "UPDATED", "TITLE"].join(" "));
|
|
@@ -13583,7 +13710,7 @@ async function runbookGet(ctx, slug) {
|
|
|
13583
13710
|
ctx.out.log(runbook.body);
|
|
13584
13711
|
}
|
|
13585
13712
|
async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
13586
|
-
const created = await
|
|
13713
|
+
const created = await call2(ctx, "POST", "/runbook", {
|
|
13587
13714
|
appId: ctx.appId,
|
|
13588
13715
|
input: { slug, title, body, ...summary ? { summary } : {}, ...requires ? { requires } : {} }
|
|
13589
13716
|
});
|
|
@@ -13591,7 +13718,7 @@ async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
|
13591
13718
|
}
|
|
13592
13719
|
async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
13593
13720
|
const runbook = await bySlug(ctx, slug);
|
|
13594
|
-
const result = await
|
|
13721
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13595
13722
|
// An empty --requires clears the declaration; omitting the flag leaves
|
|
13596
13723
|
// whatever is there, so an ordinary body edit never drops it.
|
|
13597
13724
|
patch: {
|
|
@@ -13606,7 +13733,7 @@ async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
|
13606
13733
|
}
|
|
13607
13734
|
async function runbookStatus(ctx, slug, status) {
|
|
13608
13735
|
const runbook = await bySlug(ctx, slug);
|
|
13609
|
-
const result = await
|
|
13736
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13610
13737
|
patch: { status }
|
|
13611
13738
|
});
|
|
13612
13739
|
ctx.out.log(ctx.json ? JSON.stringify(result, null, 2) : `${slug} \u2192 ${status}`);
|
|
@@ -13615,7 +13742,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
13615
13742
|
if (visibility !== "operator" && visibility !== "admin")
|
|
13616
13743
|
throw new Error(`visibility must be "operator" or "admin", got "${visibility}"`);
|
|
13617
13744
|
const runbook = await bySlug(ctx, slug);
|
|
13618
|
-
const result = await
|
|
13745
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13619
13746
|
patch: { visibility }
|
|
13620
13747
|
});
|
|
13621
13748
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
@@ -13625,7 +13752,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
13625
13752
|
}
|
|
13626
13753
|
async function runbookHistory(ctx, slug) {
|
|
13627
13754
|
const runbook = await bySlug(ctx, slug);
|
|
13628
|
-
const page2 = await
|
|
13755
|
+
const page2 = await call2(ctx, "GET", `/runbook/${encodeURIComponent(runbook.id)}/revisions`);
|
|
13629
13756
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
13630
13757
|
ctx.out.log(`${slug} is at v${runbook.version}`);
|
|
13631
13758
|
if (!page2.records.length) return ctx.out.log("(no earlier versions)");
|
|
@@ -13635,7 +13762,7 @@ async function runbookHistory(ctx, slug) {
|
|
|
13635
13762
|
}
|
|
13636
13763
|
async function runbookRevert(ctx, slug, version) {
|
|
13637
13764
|
const runbook = await bySlug(ctx, slug);
|
|
13638
|
-
const result = await
|
|
13765
|
+
const result = await call2(
|
|
13639
13766
|
ctx,
|
|
13640
13767
|
"POST",
|
|
13641
13768
|
`/runbook/${encodeURIComponent(runbook.id)}/revert`,
|
|
@@ -13646,7 +13773,7 @@ async function runbookRevert(ctx, slug, version) {
|
|
|
13646
13773
|
}
|
|
13647
13774
|
async function runbookRemove(ctx, slug) {
|
|
13648
13775
|
const runbook = await bySlug(ctx, slug);
|
|
13649
|
-
await
|
|
13776
|
+
await call2(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
13650
13777
|
ctx.out.log(`removed ${slug}`);
|
|
13651
13778
|
}
|
|
13652
13779
|
|
|
@@ -13707,7 +13834,7 @@ ${counts.created} created, ${counts.updated} updated, ${counts.unchanged} unchan
|
|
|
13707
13834
|
async function upsert(ctx, r, visibility) {
|
|
13708
13835
|
const found = await bySlug(ctx, r.slug).catch(() => null);
|
|
13709
13836
|
if (!found) {
|
|
13710
|
-
await
|
|
13837
|
+
await call2(ctx, "POST", "/runbook", {
|
|
13711
13838
|
appId: ctx.appId,
|
|
13712
13839
|
input: {
|
|
13713
13840
|
slug: r.slug,
|
|
@@ -13726,7 +13853,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
13726
13853
|
ctx.out.log(` = ${r.slug} (already current at v${found.version})`);
|
|
13727
13854
|
return "unchanged";
|
|
13728
13855
|
}
|
|
13729
|
-
const result = await
|
|
13856
|
+
const result = await call2(
|
|
13730
13857
|
ctx,
|
|
13731
13858
|
"PATCH",
|
|
13732
13859
|
`/runbook/${encodeURIComponent(found.id)}`,
|
|
@@ -13954,7 +14081,7 @@ async function assessImpact(ctx, surfaces, all, limit) {
|
|
|
13954
14081
|
for (const surface of surfaces) {
|
|
13955
14082
|
const params = new URLSearchParams({ q: surface.query, limit: String(limit) });
|
|
13956
14083
|
if (!all) params.set("app", ctx.appId);
|
|
13957
|
-
const result = await
|
|
14084
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
13958
14085
|
const runbooks = result.outcome === "ranked" ? foldHits(result.hits) : result.candidates.map((c) => ({ ...c, version: 0, sections: [] }));
|
|
13959
14086
|
out.push({ surface, runbooks });
|
|
13960
14087
|
}
|
|
@@ -14037,7 +14164,7 @@ function lintRunbook(runbook, installed) {
|
|
|
14037
14164
|
async function runbookLint(ctx, all) {
|
|
14038
14165
|
const params = new URLSearchParams();
|
|
14039
14166
|
if (!all) params.set("app", ctx.appId);
|
|
14040
|
-
const page2 = await
|
|
14167
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
14041
14168
|
const installed = { "@odla-ai/cli": cliVersion() };
|
|
14042
14169
|
const findings = page2.records.flatMap((runbook) => lintRunbook(runbook, installed));
|
|
14043
14170
|
if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page2.records.length, findings }, null, 2));
|
|
@@ -14062,7 +14189,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
14062
14189
|
const params = new URLSearchParams({ q: query });
|
|
14063
14190
|
if (!all) params.set("app", ctx.appId);
|
|
14064
14191
|
if (limit) params.set("limit", String(limit));
|
|
14065
|
-
const result = await
|
|
14192
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
14066
14193
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
14067
14194
|
if (result.outcome === "empty-corpus") return ctx.out.log("no runbooks are available to search");
|
|
14068
14195
|
if (result.outcome === "no-match") return ctx.out.log(`no runbook mentions "${query}"`);
|
|
@@ -14081,7 +14208,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
14081
14208
|
}
|
|
14082
14209
|
var JSDOC_POINTER = "Runbooks are procedure. For what an export does \u2014 arguments, defaults, errors \u2014 read its JSDoc: the installed package's .d.ts, or its page under https://odla.ai/docs.";
|
|
14083
14210
|
async function runbookAsk(ctx, question, all) {
|
|
14084
|
-
const result = await
|
|
14211
|
+
const result = await call2(ctx, "POST", "/runbook/ask", {
|
|
14085
14212
|
question,
|
|
14086
14213
|
...all ? {} : { app: ctx.appId }
|
|
14087
14214
|
});
|
|
@@ -14111,7 +14238,7 @@ async function runbookAsk(ctx, question, all) {
|
|
|
14111
14238
|
}
|
|
14112
14239
|
async function runbookComment(ctx, slug, body) {
|
|
14113
14240
|
const found = await bySlug(ctx, slug);
|
|
14114
|
-
await
|
|
14241
|
+
await call2(ctx, "POST", `/runbook/${encodeURIComponent(found.id)}/comments`, { body });
|
|
14115
14242
|
ctx.out.log(`commented on ${slug} (v${found.version})`);
|
|
14116
14243
|
}
|
|
14117
14244
|
|
|
@@ -15281,4 +15408,4 @@ export {
|
|
|
15281
15408
|
isTerminalHostedSecurityStatus,
|
|
15282
15409
|
runCli
|
|
15283
15410
|
};
|
|
15284
|
-
//# sourceMappingURL=chunk-
|
|
15411
|
+
//# sourceMappingURL=chunk-HETCZVFB.js.map
|