@odla-ai/cli 0.37.0 → 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 +202 -88
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-OWTIDSL5.js → chunk-DKOJBWRJ.js} +186 -82
- package/dist/chunk-DKOJBWRJ.js.map +1 -0
- package/dist/{cli-QR44X5IB.js → cli-DZKEPGBZ.js} +2 -2
- package/dist/index.cjs +185 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-OWTIDSL5.js.map +0 -1
- /package/dist/{cli-QR44X5IB.js.map → cli-DZKEPGBZ.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-DZKEPGBZ.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";
|
|
@@ -1802,6 +1862,31 @@ var SET_OPTIONS = [
|
|
|
1802
1862
|
async function adminCommand(parsed, deps = {}) {
|
|
1803
1863
|
const area = parsed.positionals[1];
|
|
1804
1864
|
const action2 = parsed.positionals[2];
|
|
1865
|
+
if (area === "spend") {
|
|
1866
|
+
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
1867
|
+
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
1868
|
+
const out = deps.stdout ?? console;
|
|
1869
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1870
|
+
const token = await getDeveloperToken(
|
|
1871
|
+
context2.cfg,
|
|
1872
|
+
{
|
|
1873
|
+
configPath: context2.cfg.configPath,
|
|
1874
|
+
token: stringOpt(parsed.options.token),
|
|
1875
|
+
email: stringOpt(parsed.options.email),
|
|
1876
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1877
|
+
openApprovalUrl: deps.openUrl
|
|
1878
|
+
},
|
|
1879
|
+
doFetch,
|
|
1880
|
+
out
|
|
1881
|
+
);
|
|
1882
|
+
return adminSpend(parsed, {
|
|
1883
|
+
platformUrl: context2.platform.value,
|
|
1884
|
+
token,
|
|
1885
|
+
doFetch,
|
|
1886
|
+
json: parsed.options.json === true,
|
|
1887
|
+
out
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1805
1890
|
const credentialSet = action2 === "credential" && parsed.positionals[3] === "set";
|
|
1806
1891
|
const credentials = action2 === "credentials";
|
|
1807
1892
|
const models = action2 === "models";
|
|
@@ -1905,7 +1990,8 @@ async function fetchIdentity(platformUrl, token, doFetch) {
|
|
|
1905
1990
|
email,
|
|
1906
1991
|
admin: body.admin === true,
|
|
1907
1992
|
machine,
|
|
1908
|
-
scopes
|
|
1993
|
+
scopes,
|
|
1994
|
+
projects: Array.isArray(body.projects) ? body.projects.map(String) : null
|
|
1909
1995
|
};
|
|
1910
1996
|
}
|
|
1911
1997
|
function credentialLabel(identity) {
|
|
@@ -1975,6 +2061,13 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
1975
2061
|
out.log(`credential id: ${identity.credential.id}`);
|
|
1976
2062
|
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
1977
2063
|
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
2064
|
+
if (identity.projects === null) {
|
|
2065
|
+
out.log("projects: (this registry does not report project grants)");
|
|
2066
|
+
} else if (identity.projects.length) {
|
|
2067
|
+
out.log(`projects: ${identity.projects.join(", ")}`);
|
|
2068
|
+
} else {
|
|
2069
|
+
out.log("projects: (none \u2014 every pm and discuss call will be refused)");
|
|
2070
|
+
}
|
|
1978
2071
|
if (!identity.admin) {
|
|
1979
2072
|
if (identity.scopes.includes("platform:runbook:write")) {
|
|
1980
2073
|
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
@@ -5477,10 +5570,10 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
5477
5570
|
import { cpus, hostname, totalmem } from "os";
|
|
5478
5571
|
import { resolve as resolve11 } from "path";
|
|
5479
5572
|
|
|
5480
|
-
// ../harness/dist/chunk-
|
|
5573
|
+
// ../harness/dist/chunk-LNQNFGQC.js
|
|
5481
5574
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
5482
5575
|
|
|
5483
|
-
// ../harness/dist/chunk-
|
|
5576
|
+
// ../harness/dist/chunk-K76I2TCQ.js
|
|
5484
5577
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5485
5578
|
import { constants } from "fs";
|
|
5486
5579
|
import { access } from "fs/promises";
|
|
@@ -5853,7 +5946,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
5853
5946
|
}
|
|
5854
5947
|
}
|
|
5855
5948
|
|
|
5856
|
-
// ../harness/dist/chunk-
|
|
5949
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
5857
5950
|
import { createHash as createHash3 } from "crypto";
|
|
5858
5951
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
5859
5952
|
import { relative as relative4, resolve as resolve10 } from "path";
|
|
@@ -6190,7 +6283,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6190
6283
|
}
|
|
6191
6284
|
}
|
|
6192
6285
|
|
|
6193
|
-
// ../harness/dist/chunk-
|
|
6286
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6194
6287
|
import { spawn as spawn4 } from "child_process";
|
|
6195
6288
|
import { lstat as lstat2 } from "fs/promises";
|
|
6196
6289
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -6495,7 +6588,7 @@ function looksLikeDestination(value2) {
|
|
|
6495
6588
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text3) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text3);
|
|
6496
6589
|
}
|
|
6497
6590
|
|
|
6498
|
-
// ../harness/dist/chunk-
|
|
6591
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6499
6592
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
6500
6593
|
import { readFile as readFile3 } from "fs/promises";
|
|
6501
6594
|
import { join as join33 } from "path";
|
|
@@ -6763,7 +6856,7 @@ async function buildCodeGraph(input) {
|
|
|
6763
6856
|
return builder.build();
|
|
6764
6857
|
}
|
|
6765
6858
|
|
|
6766
|
-
// ../harness/dist/chunk-
|
|
6859
|
+
// ../harness/dist/chunk-UVGZHNLW.js
|
|
6767
6860
|
import { createHash as createHash32 } from "crypto";
|
|
6768
6861
|
async function digestStagedWorkspace(root, limits) {
|
|
6769
6862
|
const files = [];
|
|
@@ -6883,7 +6976,7 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6883
6976
|
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
6884
6977
|
}
|
|
6885
6978
|
const request3 = options.fetch ?? fetch;
|
|
6886
|
-
const
|
|
6979
|
+
const call4 = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
6887
6980
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
6888
6981
|
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
6889
6982
|
let response2;
|
|
@@ -6913,17 +7006,17 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6913
7006
|
return {
|
|
6914
7007
|
heartbeat: async (version, capabilities) => {
|
|
6915
7008
|
validateHeartbeat(version, capabilities);
|
|
6916
|
-
return parseSnapshot(await
|
|
7009
|
+
return parseSnapshot(await call4("/registry/code/runtime/heartbeat", { runtimeVersion: version, capabilities }));
|
|
6917
7010
|
},
|
|
6918
7011
|
acknowledge: async (commandId, result) => {
|
|
6919
7012
|
if (!/^ccmd_[0-9a-f]{32}$/.test(commandId)) throw new TypeError("invalid Code runtime command id");
|
|
6920
|
-
await
|
|
7013
|
+
await call4(`/registry/code/runtime/commands/${commandId}/ack`, result);
|
|
6921
7014
|
},
|
|
6922
7015
|
source: async (sessionId) => parseSource(
|
|
6923
|
-
await
|
|
7016
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
6924
7017
|
),
|
|
6925
7018
|
infer: async (sessionId, inference) => {
|
|
6926
|
-
const value2 = record5(await
|
|
7019
|
+
const value2 = record5(await call4(
|
|
6927
7020
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
6928
7021
|
inference,
|
|
6929
7022
|
modelRequestTimeoutMs
|
|
@@ -6934,11 +7027,11 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6934
7027
|
return value2;
|
|
6935
7028
|
},
|
|
6936
7029
|
review: async (sessionId, review) => parseReview(
|
|
6937
|
-
await
|
|
7030
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
6938
7031
|
),
|
|
6939
7032
|
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
6940
7033
|
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
6941
|
-
return parseCandidate(await
|
|
7034
|
+
return parseCandidate(await call4(
|
|
6942
7035
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/candidates`,
|
|
6943
7036
|
{ checkpointId, verification }
|
|
6944
7037
|
));
|
|
@@ -6948,21 +7041,21 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6948
7041
|
if (!/^[A-Za-z0-9._:-]{1,120}$/.test(eventId) || !event || typeof event !== "object" || new TextEncoder().encode(serialized).byteLength > 24e3) {
|
|
6949
7042
|
throw new TypeError("invalid Code session event");
|
|
6950
7043
|
}
|
|
6951
|
-
await
|
|
7044
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
6952
7045
|
},
|
|
6953
7046
|
recallMemories: async (sessionId, subjects, limit) => {
|
|
6954
|
-
const response2 = await
|
|
7047
|
+
const response2 = await call4(
|
|
6955
7048
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
6956
7049
|
{ subjects: [...subjects], limit }
|
|
6957
7050
|
);
|
|
6958
7051
|
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
6959
7052
|
},
|
|
6960
7053
|
rememberMemory: async (sessionId, memory) => {
|
|
6961
|
-
await
|
|
7054
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
6962
7055
|
},
|
|
6963
7056
|
reportSessionFailure: async (sessionId, message2) => {
|
|
6964
7057
|
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
6965
|
-
await
|
|
7058
|
+
await call4(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
6966
7059
|
}
|
|
6967
7060
|
};
|
|
6968
7061
|
}
|
|
@@ -7851,7 +7944,7 @@ var SYSTEM_PROMPT_FOR = {
|
|
|
7851
7944
|
};
|
|
7852
7945
|
function codeSkill(opts) {
|
|
7853
7946
|
let seq = 0;
|
|
7854
|
-
const
|
|
7947
|
+
const call4 = async (tool, input, signal) => {
|
|
7855
7948
|
const startedAt = Date.now();
|
|
7856
7949
|
const response2 = await opts.broker.execute(
|
|
7857
7950
|
{ lease: opts.lease, workspaceDir: opts.workspaceDir, signal },
|
|
@@ -7873,7 +7966,7 @@ function codeSkill(opts) {
|
|
|
7873
7966
|
},
|
|
7874
7967
|
additionalProperties: false
|
|
7875
7968
|
},
|
|
7876
|
-
handler: (input, ctx) =>
|
|
7969
|
+
handler: (input, ctx) => call4("sandbox.read", input, ctx.signal)
|
|
7877
7970
|
};
|
|
7878
7971
|
const applyPatch = {
|
|
7879
7972
|
name: "odla_apply_git_diff",
|
|
@@ -7884,7 +7977,7 @@ function codeSkill(opts) {
|
|
|
7884
7977
|
properties: { patch: { type: "string", minLength: 1, maxLength: 262144 } },
|
|
7885
7978
|
additionalProperties: false
|
|
7886
7979
|
},
|
|
7887
|
-
handler: (input, ctx) =>
|
|
7980
|
+
handler: (input, ctx) => call4("sandbox.apply_patch", input, ctx.signal)
|
|
7888
7981
|
};
|
|
7889
7982
|
const runRecipe = {
|
|
7890
7983
|
name: "odla_run_recipe",
|
|
@@ -7895,7 +7988,7 @@ function codeSkill(opts) {
|
|
|
7895
7988
|
properties: { recipeId: { type: "string", minLength: 1, maxLength: 120, pattern: "^[a-zA-Z0-9._:-]+$" } },
|
|
7896
7989
|
additionalProperties: false
|
|
7897
7990
|
},
|
|
7898
|
-
handler: (input, ctx) =>
|
|
7991
|
+
handler: (input, ctx) => call4("sandbox.run_recipe", input, ctx.signal)
|
|
7899
7992
|
};
|
|
7900
7993
|
const listFiles2 = {
|
|
7901
7994
|
name: "odla_list",
|
|
@@ -7908,7 +8001,7 @@ function codeSkill(opts) {
|
|
|
7908
8001
|
},
|
|
7909
8002
|
additionalProperties: false
|
|
7910
8003
|
},
|
|
7911
|
-
handler: (input, ctx) =>
|
|
8004
|
+
handler: (input, ctx) => call4("sandbox.list", input, ctx.signal)
|
|
7912
8005
|
};
|
|
7913
8006
|
const searchFiles = {
|
|
7914
8007
|
name: "odla_search",
|
|
@@ -7924,7 +8017,7 @@ function codeSkill(opts) {
|
|
|
7924
8017
|
},
|
|
7925
8018
|
additionalProperties: false
|
|
7926
8019
|
},
|
|
7927
|
-
handler: (input, ctx) =>
|
|
8020
|
+
handler: (input, ctx) => call4("sandbox.search", input, ctx.signal)
|
|
7928
8021
|
};
|
|
7929
8022
|
const graphTool = (name, tool, description, required) => ({
|
|
7930
8023
|
name,
|
|
@@ -7935,7 +8028,7 @@ function codeSkill(opts) {
|
|
|
7935
8028
|
properties: { query: { type: "string", maxLength: 512 } },
|
|
7936
8029
|
additionalProperties: false
|
|
7937
8030
|
},
|
|
7938
|
-
handler: (input, ctx) =>
|
|
8031
|
+
handler: (input, ctx) => call4(tool, input, ctx.signal)
|
|
7939
8032
|
});
|
|
7940
8033
|
const orientation = [
|
|
7941
8034
|
graphTool(
|
|
@@ -7974,9 +8067,9 @@ async function runCodeAgent(options) {
|
|
|
7974
8067
|
lease: options.lease,
|
|
7975
8068
|
workspaceDir: options.workspaceDir,
|
|
7976
8069
|
surface,
|
|
7977
|
-
onToolCall: (
|
|
7978
|
-
toolCalls.push(
|
|
7979
|
-
options.onToolCall?.(
|
|
8070
|
+
onToolCall: (call4) => {
|
|
8071
|
+
toolCalls.push(call4);
|
|
8072
|
+
options.onToolCall?.(call4);
|
|
7980
8073
|
}
|
|
7981
8074
|
});
|
|
7982
8075
|
const compaction = options.compaction === void 0 ? keepRecentExchanges({ whenInputTokensExceed: 12e4, keep: 3 }) : options.compaction;
|
|
@@ -8061,6 +8154,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8061
8154
|
call: request3.call
|
|
8062
8155
|
});
|
|
8063
8156
|
state2.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
8157
|
+
const { costUsd } = response2.receipt;
|
|
8158
|
+
if (costUsd === void 0) state2.costKnown = false;
|
|
8159
|
+
else state2.costUsd += costUsd;
|
|
8064
8160
|
await input.event({
|
|
8065
8161
|
type: "usage",
|
|
8066
8162
|
provider: response2.receipt.provider,
|
|
@@ -8070,7 +8166,9 @@ async function handleCodeRuntimeInference(input) {
|
|
|
8070
8166
|
durationMs: Date.now() - startedAt,
|
|
8071
8167
|
interactionId: command.commandId,
|
|
8072
8168
|
interactionTokens: state2.tokens,
|
|
8073
|
-
interactionMaxTokens: metadata2.maxTokensPerInteraction
|
|
8169
|
+
interactionMaxTokens: metadata2.maxTokensPerInteraction,
|
|
8170
|
+
...costUsd === void 0 ? {} : { costUsd },
|
|
8171
|
+
...state2.costKnown ? { interactionCostUsd: state2.costUsd } : {}
|
|
8074
8172
|
}).catch(() => void 0);
|
|
8075
8173
|
return {
|
|
8076
8174
|
protocolVersion: HARNESS_PROTOCOL_VERSION,
|
|
@@ -8914,10 +9012,16 @@ async function startGoalPursuit(input) {
|
|
|
8914
9012
|
attempt: async ({ prompt }) => {
|
|
8915
9013
|
const result = await input.attempt(prompt);
|
|
8916
9014
|
return {
|
|
8917
|
-
//
|
|
8918
|
-
//
|
|
8919
|
-
//
|
|
8920
|
-
|
|
9015
|
+
// What the attempt actually spent, so the runner's token_budget and
|
|
9016
|
+
// cost_budget checks can be reached. This used to be a hardcoded 0 with
|
|
9017
|
+
// no cost at all, which made maxTokens and maxUsd unreachable while
|
|
9018
|
+
// callers reasonably read them as hard ceilings.
|
|
9019
|
+
//
|
|
9020
|
+
// costUsd is omitted rather than zeroed when any call in the attempt
|
|
9021
|
+
// was unpriced: the runner only enforces a cost budget while the cost
|
|
9022
|
+
// is known, and a zero would make it enforce against a lie.
|
|
9023
|
+
tokens: result.tokens ?? 0,
|
|
9024
|
+
...result.costUsd === void 0 ? {} : { costUsd: result.costUsd },
|
|
8921
9025
|
...result.status === "failed" ? { error: result.error ?? "attempt failed" } : {}
|
|
8922
9026
|
};
|
|
8923
9027
|
},
|
|
@@ -9131,7 +9235,7 @@ var CodePiRuntimeEngine = class {
|
|
|
9131
9235
|
recipeAuthorization: this.options.recipeAuthorization
|
|
9132
9236
|
}, lease, metadata2.role));
|
|
9133
9237
|
const startedAt = Date.now();
|
|
9134
|
-
const interaction = { tokens: 0, noticeEmitted: false };
|
|
9238
|
+
const interaction = { tokens: 0, noticeEmitted: false, costUsd: 0, costKnown: true };
|
|
9135
9239
|
const inference = createCodeRuntimeInference({
|
|
9136
9240
|
command,
|
|
9137
9241
|
metadata: metadata2,
|
|
@@ -9169,7 +9273,11 @@ var CodePiRuntimeEngine = class {
|
|
|
9169
9273
|
await this.#diagnostic(command, active, detail);
|
|
9170
9274
|
await this.#failure(command, active, detail);
|
|
9171
9275
|
}
|
|
9172
|
-
return
|
|
9276
|
+
return {
|
|
9277
|
+
...result,
|
|
9278
|
+
tokens: interaction.tokens,
|
|
9279
|
+
...interaction.costKnown ? { costUsd: interaction.costUsd } : {}
|
|
9280
|
+
};
|
|
9173
9281
|
}
|
|
9174
9282
|
/** Report every brokered effect as it starts and finishes. */
|
|
9175
9283
|
#observed(command, active, broker) {
|
|
@@ -10278,6 +10386,8 @@ Usage:
|
|
|
10278
10386
|
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
10279
10387
|
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
10280
10388
|
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
10389
|
+
odla-ai admin spend show <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
10390
|
+
odla-ai admin spend reset <app:<id>:<incarnation>> [--context <name>] [--json]
|
|
10281
10391
|
odla-ai security github connect [--repo owner/name] [--env dev] [continue in Studio; human session required]
|
|
10282
10392
|
odla-ai security github disconnect --source <id> [--env dev] [continue in Studio; human session required]
|
|
10283
10393
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -10370,6 +10480,9 @@ Commands:
|
|
|
10370
10480
|
human session connects one); "code grant request|list|approve|revoke" then
|
|
10371
10481
|
governs unattended access: an agent may request, only a human may approve.
|
|
10372
10482
|
admin Manage platform-funded AI routing/credentials/usage with narrow device grants.
|
|
10483
|
+
"admin spend" reads one app's daily inference spend and resumes it
|
|
10484
|
+
after a cap halts it. The cap is per UTC day; exhausting it LATCHES,
|
|
10485
|
+
and a new day does not clear the latch \u2014 resuming is deliberate.
|
|
10373
10486
|
security Connect GitHub sources and run commit-pinned hosted reviews, or scan a local snapshot.
|
|
10374
10487
|
pm Project management (via @odla-ai/pm): Products contain Projects;
|
|
10375
10488
|
projects contain goals, kanban tasks, decisions, and bugs. Use
|
|
@@ -10377,6 +10490,12 @@ Commands:
|
|
|
10377
10490
|
pass --app/--project explicitly. Same device-grant auth as "app".
|
|
10378
10491
|
Status changes and comments post to each item's @odla-ai/chat
|
|
10379
10492
|
discussion thread.
|
|
10493
|
+
NOTE: "--column ready" is OWNER-ONLY. Creating a task in Ready
|
|
10494
|
+
approves its complete execution contract, so it needs pm.plan,
|
|
10495
|
+
which no device enrollment or handshake approval can grant. An
|
|
10496
|
+
agent proposes in Backlog (the default); a human owner or a
|
|
10497
|
+
pm.plan agent promotes. This is deliberate, not a permission
|
|
10498
|
+
gap \u2014 it was reported as one.
|
|
10380
10499
|
bug Intent-first alias for PM bugs. "bug report" writes to
|
|
10381
10500
|
odla PM; odla product defects do not belong in GitHub Issues.
|
|
10382
10501
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
@@ -12757,9 +12876,15 @@ async function provisionEnvCredentials(opts) {
|
|
|
12757
12876
|
if (o11yToken) {
|
|
12758
12877
|
opts.stdout.log(`${opts.env}: reusing local o11y ingest token`);
|
|
12759
12878
|
} else {
|
|
12760
|
-
o11yToken = await issueO11yToken(opts);
|
|
12761
|
-
|
|
12762
|
-
|
|
12879
|
+
o11yToken = await issueO11yToken(opts) ?? void 0;
|
|
12880
|
+
if (o11yToken) {
|
|
12881
|
+
credentials = save(opts, credentials, tenantId, { ...dbKey ? { dbKey } : {}, o11yToken });
|
|
12882
|
+
opts.stdout.log(`${opts.env}: ${opts.rotateO11y ? "rotated" : "issued"} o11y ingest token`);
|
|
12883
|
+
} else {
|
|
12884
|
+
opts.stdout.log(
|
|
12885
|
+
`${opts.env}: o11y token already exists and its shown-once value is not on this machine \u2014 leaving it alone (a co-owner provisioned this env). Nothing was rotated, and this env's running Worker keeps its token. To replace it deliberately \u2014 which invalidates the token on whatever is deployed \u2014 run "odla-ai provision --rotate-o11y-token --push-secrets".`
|
|
12886
|
+
);
|
|
12887
|
+
}
|
|
12763
12888
|
}
|
|
12764
12889
|
}
|
|
12765
12890
|
return save(opts, credentials, tenantId, {
|
|
@@ -12813,11 +12938,7 @@ async function issueO11yToken(opts) {
|
|
|
12813
12938
|
`${opts.cfg.platformUrl}/o11y/${encodeURIComponent(opts.cfg.app.id)}/token${suffix}?env=${encodeURIComponent(opts.env)}`,
|
|
12814
12939
|
{ method: "POST", headers: { authorization: `Bearer ${opts.developerToken}` } }
|
|
12815
12940
|
);
|
|
12816
|
-
if (res.status === 409 && !opts.rotateO11y)
|
|
12817
|
-
throw new Error(
|
|
12818
|
-
`o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --push-secrets" to install a separate runtime credential without rotating siblings`
|
|
12819
|
-
);
|
|
12820
|
-
}
|
|
12941
|
+
if (res.status === 409 && !opts.rotateO11y) return null;
|
|
12821
12942
|
if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText7(res)}`);
|
|
12822
12943
|
const body = await res.json();
|
|
12823
12944
|
if (!body.token) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) returned no token`);
|
|
@@ -13512,7 +13633,7 @@ function defaultDeviceName() {
|
|
|
13512
13633
|
// src/runbook-actions.ts
|
|
13513
13634
|
import { readFileSync as readFileSync10 } from "fs";
|
|
13514
13635
|
var PLATFORM_SCOPE = "$platform";
|
|
13515
|
-
async function
|
|
13636
|
+
async function call2(ctx, method, path, body) {
|
|
13516
13637
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
13517
13638
|
method,
|
|
13518
13639
|
headers: {
|
|
@@ -13534,7 +13655,7 @@ async function call(ctx, method, path, body) {
|
|
|
13534
13655
|
throw new Error(message2);
|
|
13535
13656
|
}
|
|
13536
13657
|
async function bySlug(ctx, slug) {
|
|
13537
|
-
const page2 = await
|
|
13658
|
+
const page2 = await call2(
|
|
13538
13659
|
ctx,
|
|
13539
13660
|
"GET",
|
|
13540
13661
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
@@ -13543,7 +13664,7 @@ async function bySlug(ctx, slug) {
|
|
|
13543
13664
|
if (filtered) return filtered;
|
|
13544
13665
|
const limit = 100;
|
|
13545
13666
|
for (let offset = 0; ; offset += limit) {
|
|
13546
|
-
const fallback = await
|
|
13667
|
+
const fallback = await call2(
|
|
13547
13668
|
ctx,
|
|
13548
13669
|
"GET",
|
|
13549
13670
|
`/runbook?app=${encodeURIComponent(ctx.appId)}&limit=${limit}&offset=${offset}`
|
|
@@ -13564,7 +13685,7 @@ async function runbookList(ctx, all, query) {
|
|
|
13564
13685
|
const params = new URLSearchParams();
|
|
13565
13686
|
if (!all) params.set("app", ctx.appId);
|
|
13566
13687
|
if (query) params.set("q", query);
|
|
13567
|
-
const page2 = await
|
|
13688
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
13568
13689
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
13569
13690
|
if (!page2.records.length) return ctx.out.log("(no runbooks)");
|
|
13570
13691
|
ctx.out.log(["SLUG", "STATUS", "V", "SCOPE", "UPDATED", "TITLE"].join(" "));
|
|
@@ -13581,7 +13702,7 @@ async function runbookGet(ctx, slug) {
|
|
|
13581
13702
|
ctx.out.log(runbook.body);
|
|
13582
13703
|
}
|
|
13583
13704
|
async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
13584
|
-
const created = await
|
|
13705
|
+
const created = await call2(ctx, "POST", "/runbook", {
|
|
13585
13706
|
appId: ctx.appId,
|
|
13586
13707
|
input: { slug, title, body, ...summary ? { summary } : {}, ...requires ? { requires } : {} }
|
|
13587
13708
|
});
|
|
@@ -13589,7 +13710,7 @@ async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
|
13589
13710
|
}
|
|
13590
13711
|
async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
13591
13712
|
const runbook = await bySlug(ctx, slug);
|
|
13592
|
-
const result = await
|
|
13713
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13593
13714
|
// An empty --requires clears the declaration; omitting the flag leaves
|
|
13594
13715
|
// whatever is there, so an ordinary body edit never drops it.
|
|
13595
13716
|
patch: {
|
|
@@ -13604,7 +13725,7 @@ async function runbookEdit(ctx, slug, body, note, requires, expectedVersion) {
|
|
|
13604
13725
|
}
|
|
13605
13726
|
async function runbookStatus(ctx, slug, status) {
|
|
13606
13727
|
const runbook = await bySlug(ctx, slug);
|
|
13607
|
-
const result = await
|
|
13728
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13608
13729
|
patch: { status }
|
|
13609
13730
|
});
|
|
13610
13731
|
ctx.out.log(ctx.json ? JSON.stringify(result, null, 2) : `${slug} \u2192 ${status}`);
|
|
@@ -13613,7 +13734,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
13613
13734
|
if (visibility !== "operator" && visibility !== "admin")
|
|
13614
13735
|
throw new Error(`visibility must be "operator" or "admin", got "${visibility}"`);
|
|
13615
13736
|
const runbook = await bySlug(ctx, slug);
|
|
13616
|
-
const result = await
|
|
13737
|
+
const result = await call2(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
13617
13738
|
patch: { visibility }
|
|
13618
13739
|
});
|
|
13619
13740
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
@@ -13623,7 +13744,7 @@ async function runbookVisibility(ctx, slug, visibility) {
|
|
|
13623
13744
|
}
|
|
13624
13745
|
async function runbookHistory(ctx, slug) {
|
|
13625
13746
|
const runbook = await bySlug(ctx, slug);
|
|
13626
|
-
const page2 = await
|
|
13747
|
+
const page2 = await call2(ctx, "GET", `/runbook/${encodeURIComponent(runbook.id)}/revisions`);
|
|
13627
13748
|
if (ctx.json) return ctx.out.log(JSON.stringify(page2, null, 2));
|
|
13628
13749
|
ctx.out.log(`${slug} is at v${runbook.version}`);
|
|
13629
13750
|
if (!page2.records.length) return ctx.out.log("(no earlier versions)");
|
|
@@ -13633,7 +13754,7 @@ async function runbookHistory(ctx, slug) {
|
|
|
13633
13754
|
}
|
|
13634
13755
|
async function runbookRevert(ctx, slug, version) {
|
|
13635
13756
|
const runbook = await bySlug(ctx, slug);
|
|
13636
|
-
const result = await
|
|
13757
|
+
const result = await call2(
|
|
13637
13758
|
ctx,
|
|
13638
13759
|
"POST",
|
|
13639
13760
|
`/runbook/${encodeURIComponent(runbook.id)}/revert`,
|
|
@@ -13644,7 +13765,7 @@ async function runbookRevert(ctx, slug, version) {
|
|
|
13644
13765
|
}
|
|
13645
13766
|
async function runbookRemove(ctx, slug) {
|
|
13646
13767
|
const runbook = await bySlug(ctx, slug);
|
|
13647
|
-
await
|
|
13768
|
+
await call2(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
13648
13769
|
ctx.out.log(`removed ${slug}`);
|
|
13649
13770
|
}
|
|
13650
13771
|
|
|
@@ -13703,14 +13824,9 @@ ${counts.created} created, ${counts.updated} updated, ${counts.unchanged} unchan
|
|
|
13703
13824
|
);
|
|
13704
13825
|
}
|
|
13705
13826
|
async function upsert(ctx, r, visibility) {
|
|
13706
|
-
const
|
|
13707
|
-
ctx,
|
|
13708
|
-
"GET",
|
|
13709
|
-
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(r.slug)}&limit=1`
|
|
13710
|
-
);
|
|
13711
|
-
const found = page2.records[0];
|
|
13827
|
+
const found = await bySlug(ctx, r.slug).catch(() => null);
|
|
13712
13828
|
if (!found) {
|
|
13713
|
-
await
|
|
13829
|
+
await call2(ctx, "POST", "/runbook", {
|
|
13714
13830
|
appId: ctx.appId,
|
|
13715
13831
|
input: {
|
|
13716
13832
|
slug: r.slug,
|
|
@@ -13729,7 +13845,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
13729
13845
|
ctx.out.log(` = ${r.slug} (already current at v${found.version})`);
|
|
13730
13846
|
return "unchanged";
|
|
13731
13847
|
}
|
|
13732
|
-
const result = await
|
|
13848
|
+
const result = await call2(
|
|
13733
13849
|
ctx,
|
|
13734
13850
|
"PATCH",
|
|
13735
13851
|
`/runbook/${encodeURIComponent(found.id)}`,
|
|
@@ -13957,7 +14073,7 @@ async function assessImpact(ctx, surfaces, all, limit) {
|
|
|
13957
14073
|
for (const surface of surfaces) {
|
|
13958
14074
|
const params = new URLSearchParams({ q: surface.query, limit: String(limit) });
|
|
13959
14075
|
if (!all) params.set("app", ctx.appId);
|
|
13960
|
-
const result = await
|
|
14076
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
13961
14077
|
const runbooks = result.outcome === "ranked" ? foldHits(result.hits) : result.candidates.map((c) => ({ ...c, version: 0, sections: [] }));
|
|
13962
14078
|
out.push({ surface, runbooks });
|
|
13963
14079
|
}
|
|
@@ -14040,7 +14156,7 @@ function lintRunbook(runbook, installed) {
|
|
|
14040
14156
|
async function runbookLint(ctx, all) {
|
|
14041
14157
|
const params = new URLSearchParams();
|
|
14042
14158
|
if (!all) params.set("app", ctx.appId);
|
|
14043
|
-
const page2 = await
|
|
14159
|
+
const page2 = await call2(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
14044
14160
|
const installed = { "@odla-ai/cli": cliVersion() };
|
|
14045
14161
|
const findings = page2.records.flatMap((runbook) => lintRunbook(runbook, installed));
|
|
14046
14162
|
if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page2.records.length, findings }, null, 2));
|
|
@@ -14065,7 +14181,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
14065
14181
|
const params = new URLSearchParams({ q: query });
|
|
14066
14182
|
if (!all) params.set("app", ctx.appId);
|
|
14067
14183
|
if (limit) params.set("limit", String(limit));
|
|
14068
|
-
const result = await
|
|
14184
|
+
const result = await call2(ctx, "GET", `/runbook/search?${params}`);
|
|
14069
14185
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
14070
14186
|
if (result.outcome === "empty-corpus") return ctx.out.log("no runbooks are available to search");
|
|
14071
14187
|
if (result.outcome === "no-match") return ctx.out.log(`no runbook mentions "${query}"`);
|
|
@@ -14084,7 +14200,7 @@ async function runbookSearch(ctx, query, all, limit) {
|
|
|
14084
14200
|
}
|
|
14085
14201
|
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.";
|
|
14086
14202
|
async function runbookAsk(ctx, question, all) {
|
|
14087
|
-
const result = await
|
|
14203
|
+
const result = await call2(ctx, "POST", "/runbook/ask", {
|
|
14088
14204
|
question,
|
|
14089
14205
|
...all ? {} : { app: ctx.appId }
|
|
14090
14206
|
});
|
|
@@ -14113,14 +14229,8 @@ async function runbookAsk(ctx, question, all) {
|
|
|
14113
14229
|
ctx.out.log(JSDOC_POINTER);
|
|
14114
14230
|
}
|
|
14115
14231
|
async function runbookComment(ctx, slug, body) {
|
|
14116
|
-
const
|
|
14117
|
-
|
|
14118
|
-
"GET",
|
|
14119
|
-
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
14120
|
-
);
|
|
14121
|
-
const found = page2.records[0];
|
|
14122
|
-
if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
|
|
14123
|
-
await call(ctx, "POST", `/runbook/${encodeURIComponent(found.id)}/comments`, { body });
|
|
14232
|
+
const found = await bySlug(ctx, slug);
|
|
14233
|
+
await call2(ctx, "POST", `/runbook/${encodeURIComponent(found.id)}/comments`, { body });
|
|
14124
14234
|
ctx.out.log(`commented on ${slug} (v${found.version})`);
|
|
14125
14235
|
}
|
|
14126
14236
|
|
|
@@ -14170,13 +14280,7 @@ var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
|
14170
14280
|
|
|
14171
14281
|
// src/runbook-edit-flow.ts
|
|
14172
14282
|
async function editRunbook(ctx, slug, deps = {}) {
|
|
14173
|
-
const
|
|
14174
|
-
ctx,
|
|
14175
|
-
"GET",
|
|
14176
|
-
`/runbook?app=${encodeURIComponent(ctx.appId)}&slug=${encodeURIComponent(slug)}&limit=1`
|
|
14177
|
-
);
|
|
14178
|
-
const found = page2.records[0];
|
|
14179
|
-
if (!found) throw new Error(`no runbook "${slug}" in ${ctx.appId}`);
|
|
14283
|
+
const found = await bySlug(ctx, slug);
|
|
14180
14284
|
ctx.out.log(`opening ${slug} v${found.version} in your editor\u2026`);
|
|
14181
14285
|
const body = await editText(found.body, slug, deps);
|
|
14182
14286
|
return body === null ? null : { body, expectedVersion: found.version };
|
|
@@ -15296,4 +15400,4 @@ export {
|
|
|
15296
15400
|
isTerminalHostedSecurityStatus,
|
|
15297
15401
|
runCli
|
|
15298
15402
|
};
|
|
15299
|
-
//# sourceMappingURL=chunk-
|
|
15403
|
+
//# sourceMappingURL=chunk-DKOJBWRJ.js.map
|