@corenel/cli 0.4.7 → 0.4.9
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/main.js +184 -5
- package/package.json +5 -5
package/dist/main.js
CHANGED
|
@@ -13488,7 +13488,7 @@ var require_filters = __commonJS({
|
|
|
13488
13488
|
function striptags(input, preserveLinebreaks) {
|
|
13489
13489
|
input = normalize(input, "");
|
|
13490
13490
|
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
|
|
13491
|
-
var trimmedInput =
|
|
13491
|
+
var trimmedInput = trim2(input.replace(tags, ""));
|
|
13492
13492
|
var res = "";
|
|
13493
13493
|
if (preserveLinebreaks) {
|
|
13494
13494
|
res = trimmedInput.replace(/^ +| +$/gm, "").replace(/ +/g, " ").replace(/(\r\n)/g, "\n").replace(/\n\n\n+/g, "\n\n");
|
|
@@ -13506,10 +13506,10 @@ var require_filters = __commonJS({
|
|
|
13506
13506
|
return r.copySafeness(str, words.join(" "));
|
|
13507
13507
|
}
|
|
13508
13508
|
_exports.title = title;
|
|
13509
|
-
function
|
|
13509
|
+
function trim2(str) {
|
|
13510
13510
|
return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ""));
|
|
13511
13511
|
}
|
|
13512
|
-
_exports.trim =
|
|
13512
|
+
_exports.trim = trim2;
|
|
13513
13513
|
function truncate2(input, length, killwords, end) {
|
|
13514
13514
|
var orig = input;
|
|
13515
13515
|
input = normalize(input, "");
|
|
@@ -22722,6 +22722,26 @@ function selfDirectionBlock(def) {
|
|
|
22722
22722
|
return readConfigSyncRendered(SELF_DIRECTION_BLOCK, { consequence });
|
|
22723
22723
|
}
|
|
22724
22724
|
|
|
22725
|
+
// ../crew/triggerMatch.ts
|
|
22726
|
+
var RUN_STATUS_VALUES = ["ok", "aborted", "budget", "stalled", "policy", "stuck", "max_turns", "error"];
|
|
22727
|
+
var RUN_STATUS_ALIASES = [
|
|
22728
|
+
["stop", "ok"],
|
|
22729
|
+
["tool_calls", "ok"],
|
|
22730
|
+
["function_call", "ok"],
|
|
22731
|
+
["end_turn", "ok"],
|
|
22732
|
+
["stop_sequence", "ok"],
|
|
22733
|
+
["tool_use", "ok"],
|
|
22734
|
+
["length", "stuck"],
|
|
22735
|
+
["max_tokens", "stuck"],
|
|
22736
|
+
["refusal", "policy"],
|
|
22737
|
+
["content_filter", "policy"],
|
|
22738
|
+
["interrupted", "aborted"]
|
|
22739
|
+
];
|
|
22740
|
+
var RUN_STATUS_BY_RAW = new Map([
|
|
22741
|
+
...RUN_STATUS_VALUES.map((v) => [v, v]),
|
|
22742
|
+
...RUN_STATUS_ALIASES
|
|
22743
|
+
]);
|
|
22744
|
+
|
|
22725
22745
|
// ../crew/memory.ts
|
|
22726
22746
|
import { makeFactStore } from "@corenel/harness/memory/store";
|
|
22727
22747
|
function agentMemoryRoot(crewRoot, agentName) {
|
|
@@ -23081,6 +23101,12 @@ function helpText(version2) {
|
|
|
23081
23101
|
" --allow-mutations allow write/shell/other mutating tools (run/run-agent refuse them",
|
|
23082
23102
|
" outright without this; interactively it skips the per-command",
|
|
23083
23103
|
" prompt instead). --allow-shell still works as an alias.",
|
|
23104
|
+
" --max-usd <n> stop `corenel run` once it has spent this much. Needs a model the",
|
|
23105
|
+
" catalog prices -- refused up front rather than metering $0 and",
|
|
23106
|
+
" never firing",
|
|
23107
|
+
" --max-tokens <n> stop `corenel run` after this many tokens",
|
|
23108
|
+
" --max-ms <n> stop `corenel run` after this long",
|
|
23109
|
+
" --on-budget <what> what a passed ceiling does: stop (the default) or warn",
|
|
23084
23110
|
" --no-tui plain line-by-line mode (automatic when output is piped)",
|
|
23085
23111
|
" --tui force the full terminal face, and fail if it cannot start",
|
|
23086
23112
|
" --resume [id] reopen the last conversation, or the one named",
|
|
@@ -23103,6 +23129,9 @@ function helpText(version2) {
|
|
|
23103
23129
|
" unresolvable --policy, or an unknown command",
|
|
23104
23130
|
" 3 the policy asked to park an unattended action, but a one-shot run",
|
|
23105
23131
|
" has no one to park it with -- refused before the turn started",
|
|
23132
|
+
" 4 a --max-usd/--max-tokens/--max-ms ceiling stopped the run. Distinct from",
|
|
23133
|
+
' 1 so the script that set the cap can tell "I capped this" apart from',
|
|
23134
|
+
' "the run broke"',
|
|
23106
23135
|
""
|
|
23107
23136
|
].join("\n");
|
|
23108
23137
|
}
|
|
@@ -23953,6 +23982,104 @@ function currentFaceEnv(argv) {
|
|
|
23953
23982
|
};
|
|
23954
23983
|
}
|
|
23955
23984
|
|
|
23985
|
+
// src/budgetFlags.ts
|
|
23986
|
+
var BudgetFlagError = class extends Error {
|
|
23987
|
+
constructor(flagName, detail) {
|
|
23988
|
+
super(`${flagName} ${detail}`);
|
|
23989
|
+
this.flagName = flagName;
|
|
23990
|
+
this.name = "BudgetFlagError";
|
|
23991
|
+
}
|
|
23992
|
+
};
|
|
23993
|
+
var CEILINGS = [
|
|
23994
|
+
{ name: "max-usd", key: "maxUsd", unit: "an amount in dollars, e.g. --max-usd 2.50" },
|
|
23995
|
+
{ name: "max-tokens", key: "maxTokens", unit: "a token count, e.g. --max-tokens 100000" },
|
|
23996
|
+
{ name: "max-ms", key: "maxMs", unit: "a duration in milliseconds, e.g. --max-ms 600000" }
|
|
23997
|
+
];
|
|
23998
|
+
function ceiling(argv, name, unit) {
|
|
23999
|
+
if (!hasValueFlag(argv, name)) return void 0;
|
|
24000
|
+
const raw = flag(argv, name, "");
|
|
24001
|
+
const n = Number(raw);
|
|
24002
|
+
if (!Number.isFinite(n)) throw new BudgetFlagError(`--${name}`, `needs ${unit} -- got "${raw}"`);
|
|
24003
|
+
if (n <= 0) throw new BudgetFlagError(`--${name}`, `must be greater than zero -- got "${raw}"`);
|
|
24004
|
+
return n;
|
|
24005
|
+
}
|
|
24006
|
+
var ACTIONS = ["stop", "warn"];
|
|
24007
|
+
function breachAction(argv) {
|
|
24008
|
+
if (!hasValueFlag(argv, "on-budget")) return void 0;
|
|
24009
|
+
const raw = flag(argv, "on-budget", "");
|
|
24010
|
+
if (raw === "ask") {
|
|
24011
|
+
throw new BudgetFlagError("--on-budget", 'cannot be "ask": a one-shot run has nobody to ask. Use stop or warn.');
|
|
24012
|
+
}
|
|
24013
|
+
if (raw === "degrade") {
|
|
24014
|
+
throw new BudgetFlagError("--on-budget", 'cannot be "degrade": `run` has no cheaper-model picker. Use stop or warn.');
|
|
24015
|
+
}
|
|
24016
|
+
if (!ACTIONS.includes(raw)) {
|
|
24017
|
+
throw new BudgetFlagError("--on-budget", `must be stop or warn -- got "${raw}"`);
|
|
24018
|
+
}
|
|
24019
|
+
return raw;
|
|
24020
|
+
}
|
|
24021
|
+
function budgetConstraintsFrom(argv) {
|
|
24022
|
+
const limits = {};
|
|
24023
|
+
for (const c of CEILINGS) {
|
|
24024
|
+
const v = ceiling(argv, c.name, c.unit);
|
|
24025
|
+
if (v !== void 0) limits[c.key] = v;
|
|
24026
|
+
}
|
|
24027
|
+
const action = breachAction(argv);
|
|
24028
|
+
const hasLimit = Object.keys(limits).length > 0;
|
|
24029
|
+
if (!hasLimit) {
|
|
24030
|
+
if (action !== void 0) {
|
|
24031
|
+
throw new BudgetFlagError("--on-budget", "needs a budget to act on -- add --max-usd, --max-tokens or --max-ms.");
|
|
24032
|
+
}
|
|
24033
|
+
return void 0;
|
|
24034
|
+
}
|
|
24035
|
+
return { ...limits, onBreach: action ?? "stop" };
|
|
24036
|
+
}
|
|
24037
|
+
|
|
24038
|
+
// src/runBudget.ts
|
|
24039
|
+
import { BudgetMeter as BudgetMeter2 } from "@corenel/harness/core/budget";
|
|
24040
|
+
import { priceUsageWith, pricedForUsd } from "@corenel/harness/core/pricing";
|
|
24041
|
+
var UnpriceableBudgetError = class extends Error {
|
|
24042
|
+
constructor(model) {
|
|
24043
|
+
super(`--max-usd cannot be enforced: no price is known for model "${model}", so spend would always read $0. Use --max-tokens or --max-ms instead, or run a model the catalog prices.`);
|
|
24044
|
+
this.model = model;
|
|
24045
|
+
this.name = "UnpriceableBudgetError";
|
|
24046
|
+
}
|
|
24047
|
+
};
|
|
24048
|
+
function makeRunBudget(deps) {
|
|
24049
|
+
const { constraints, rates, model, onStop, onWarn } = deps;
|
|
24050
|
+
if (constraints.maxUsd != null && !pricedForUsd(rates)) throw new UnpriceableBudgetError(model);
|
|
24051
|
+
const meter = new BudgetMeter2(constraints, deps.now ?? (() => Date.now()));
|
|
24052
|
+
let fired = false;
|
|
24053
|
+
return {
|
|
24054
|
+
onUsage(usage) {
|
|
24055
|
+
if (fired) return;
|
|
24056
|
+
meter.add({
|
|
24057
|
+
inTok: usage.promptTokens,
|
|
24058
|
+
outTok: usage.completionTokens,
|
|
24059
|
+
// `?? 0` is safe ONLY because an unenforceable usd cap was refused
|
|
24060
|
+
// above: with no maxUsd, cost is irrelevant to every other ceiling.
|
|
24061
|
+
cost: priceUsageWith(usage, rates) ?? 0
|
|
24062
|
+
});
|
|
24063
|
+
const breach = meter.breach();
|
|
24064
|
+
if (!breach) return;
|
|
24065
|
+
fired = true;
|
|
24066
|
+
const message = describeBreach(breach);
|
|
24067
|
+
if (constraints.onBreach === "warn") onWarn(message);
|
|
24068
|
+
else onStop(message);
|
|
24069
|
+
}
|
|
24070
|
+
};
|
|
24071
|
+
}
|
|
24072
|
+
function describeBreach(b) {
|
|
24073
|
+
return `budget ${b.kind} ${trim(b.spent)} > ${trim(b.limit)}`;
|
|
24074
|
+
}
|
|
24075
|
+
function trim(v) {
|
|
24076
|
+
if (Number.isInteger(v)) return String(v);
|
|
24077
|
+
return v.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
24078
|
+
}
|
|
24079
|
+
|
|
24080
|
+
// src/cli.ts
|
|
24081
|
+
import { hydratePrices } from "@corenel/harness/core/pricing";
|
|
24082
|
+
|
|
23956
24083
|
// src/runOutcome.ts
|
|
23957
24084
|
function isFailedTurn(outcome) {
|
|
23958
24085
|
if (outcome.text !== "") return false;
|
|
@@ -24130,6 +24257,7 @@ async function resolvePolicy(opts) {
|
|
|
24130
24257
|
|
|
24131
24258
|
// src/cli.ts
|
|
24132
24259
|
var EXIT_POLICY_PARK_REFUSED = 3;
|
|
24260
|
+
var EXIT_BUDGET_EXCEEDED = 4;
|
|
24133
24261
|
async function agentSetup(argv) {
|
|
24134
24262
|
setGatewayBase2(await apiBase(flag(argv, "base", "")));
|
|
24135
24263
|
const token = await nodeAuthToken().getToken();
|
|
@@ -24182,12 +24310,13 @@ async function agentSetup(argv) {
|
|
|
24182
24310
|
async function run(argv) {
|
|
24183
24311
|
const prompt = promptOf(argv);
|
|
24184
24312
|
if (!prompt) {
|
|
24185
|
-
process.stderr.write('usage: corenel run "<prompt>" [--model M] [--base URL] [--session ID] [--allow-mutations]\n');
|
|
24313
|
+
process.stderr.write('usage: corenel run "<prompt>" [--model M] [--base URL] [--session ID] [--allow-mutations]\n [--max-usd N] [--max-tokens N] [--max-ms N] [--on-budget stop|warn]\n');
|
|
24186
24314
|
process.exit(2);
|
|
24187
24315
|
}
|
|
24316
|
+
const budget = budgetConstraintsFrom(argv);
|
|
24188
24317
|
const session = flag(argv, "session", "");
|
|
24189
24318
|
if (session) {
|
|
24190
|
-
if (hasAllowMutationsFlag(argv) || hasValueFlag(argv, "model") || hasValueFlag(argv, "base")) {
|
|
24319
|
+
if (hasAllowMutationsFlag(argv) || hasValueFlag(argv, "model") || hasValueFlag(argv, "base") || budget) {
|
|
24191
24320
|
process.stderr.write("corenel: note: --session ignores every other flag on this line -- the sandboxed run uses its own defaults.\n");
|
|
24192
24321
|
}
|
|
24193
24322
|
const ext = findExtension("sessions");
|
|
@@ -24202,6 +24331,26 @@ async function run(argv) {
|
|
|
24202
24331
|
}
|
|
24203
24332
|
async function runOnce() {
|
|
24204
24333
|
const ac = new AbortController();
|
|
24334
|
+
let budgetStopped = false;
|
|
24335
|
+
let meter = null;
|
|
24336
|
+
if (budget) {
|
|
24337
|
+
const rates = budget.maxUsd != null ? (await hydratePrices(await apiBase(flag(argv, "base", "")), nodeAuthToken().getToken)).get(model) ?? null : null;
|
|
24338
|
+
meter = makeRunBudget({
|
|
24339
|
+
constraints: budget,
|
|
24340
|
+
rates,
|
|
24341
|
+
model,
|
|
24342
|
+
onStop: (m) => {
|
|
24343
|
+
budgetStopped = true;
|
|
24344
|
+
process.stderr.write(`corenel: ${m} -- stopping the run.
|
|
24345
|
+
`);
|
|
24346
|
+
ac.abort();
|
|
24347
|
+
},
|
|
24348
|
+
onWarn: (m) => {
|
|
24349
|
+
process.stderr.write(`corenel: ${m} -- continuing, because --on-budget is warn.
|
|
24350
|
+
`);
|
|
24351
|
+
}
|
|
24352
|
+
});
|
|
24353
|
+
}
|
|
24205
24354
|
const allowMutations = hasAllowMutationsFlag(argv);
|
|
24206
24355
|
const unattended = policy.unattended ?? "deny";
|
|
24207
24356
|
const unattendedReasons = [];
|
|
@@ -24240,10 +24389,17 @@ async function run(argv) {
|
|
|
24240
24389
|
spawn: { tools, client, defaultModel: model, permission }
|
|
24241
24390
|
}),
|
|
24242
24391
|
signal: ac.signal,
|
|
24392
|
+
// The meter is fed from the SAME event stream, one `usage` per model call.
|
|
24243
24393
|
onEvent: (e) => {
|
|
24244
24394
|
if (e.type === "error") lastError = e.message;
|
|
24395
|
+
else if (e.type === "usage") meter?.onUsage(e.usage);
|
|
24245
24396
|
}
|
|
24246
24397
|
});
|
|
24398
|
+
if (budgetStopped) {
|
|
24399
|
+
process.stderr.write("corenel: the run stopped at its budget.\n");
|
|
24400
|
+
process.exitCode = EXIT_BUDGET_EXCEEDED;
|
|
24401
|
+
return;
|
|
24402
|
+
}
|
|
24247
24403
|
if (isFailedTurn({ stopReason: result.stopReason, text: result.text, errorEmitted: lastError !== "" })) {
|
|
24248
24404
|
process.stderr.write(`corenel: ${friendlyError(lastError || `the run ended without an answer (${result.stopReason})`)}
|
|
24249
24405
|
`);
|
|
@@ -24263,6 +24419,12 @@ async function interactive(argv) {
|
|
|
24263
24419
|
async function runInteractive() {
|
|
24264
24420
|
const approveAll = hasAllowMutationsFlag(argv);
|
|
24265
24421
|
const cwd = process.cwd();
|
|
24422
|
+
let modelRates = null;
|
|
24423
|
+
const priceBase = await apiBase(flag(argv, "base", ""));
|
|
24424
|
+
void hydratePrices(priceBase, nodeAuthToken().getToken).then((table) => {
|
|
24425
|
+
modelRates = table.get(model) ?? null;
|
|
24426
|
+
}).catch(() => {
|
|
24427
|
+
});
|
|
24266
24428
|
const files = new NodeFileService(cwd);
|
|
24267
24429
|
const persistence = createSessionPersistence(files);
|
|
24268
24430
|
const toolDefs = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
@@ -24296,6 +24458,7 @@ async function interactive(argv) {
|
|
|
24296
24458
|
process.exitCode = await runTui({
|
|
24297
24459
|
cwd,
|
|
24298
24460
|
model,
|
|
24461
|
+
rates: () => modelRates,
|
|
24299
24462
|
system: systemSource,
|
|
24300
24463
|
client,
|
|
24301
24464
|
tools,
|
|
@@ -24330,6 +24493,7 @@ async function interactive(argv) {
|
|
|
24330
24493
|
process.exitCode = await runRepl({
|
|
24331
24494
|
client,
|
|
24332
24495
|
model,
|
|
24496
|
+
rates: () => modelRates,
|
|
24333
24497
|
system: systemSource,
|
|
24334
24498
|
tools,
|
|
24335
24499
|
// The readline face attaches its own PermissionService too (repl.ts) and
|
|
@@ -24452,6 +24616,20 @@ Logged in. Token saved to ${authTokenPath()}
|
|
|
24452
24616
|
process.exitCode = 2;
|
|
24453
24617
|
return;
|
|
24454
24618
|
}
|
|
24619
|
+
if (e instanceof BudgetFlagError) {
|
|
24620
|
+
process.stderr.write(`corenel: ${e.message}
|
|
24621
|
+
|
|
24622
|
+
`);
|
|
24623
|
+
process.stderr.write(helpText(version()));
|
|
24624
|
+
process.exitCode = 2;
|
|
24625
|
+
return;
|
|
24626
|
+
}
|
|
24627
|
+
if (e instanceof UnpriceableBudgetError) {
|
|
24628
|
+
process.stderr.write(`corenel: ${e.message}
|
|
24629
|
+
`);
|
|
24630
|
+
process.exitCode = 2;
|
|
24631
|
+
return;
|
|
24632
|
+
}
|
|
24455
24633
|
if (e instanceof InvalidApiBaseError) {
|
|
24456
24634
|
process.stderr.write(`corenel: ${e.message}
|
|
24457
24635
|
`);
|
|
@@ -24483,5 +24661,6 @@ main().catch((e) => {
|
|
|
24483
24661
|
bail.unref();
|
|
24484
24662
|
});
|
|
24485
24663
|
export {
|
|
24664
|
+
EXIT_BUDGET_EXCEEDED,
|
|
24486
24665
|
EXIT_POLICY_PARK_REFUSED
|
|
24487
24666
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corenel/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Corenel CLI — runs the harness in node, in-proc, no transport. The headless proof the kernel is host-agnostic. (corenel run/ask/chat/login + start --sidecar to follow.)",
|
|
6
6
|
"bin": {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"openai": "^4.77.0",
|
|
11
11
|
"ws": "^8.18.0",
|
|
12
|
-
"@corenel/
|
|
13
|
-
"@corenel/protocol": "0.7.
|
|
14
|
-
"@corenel/
|
|
12
|
+
"@corenel/term": "0.2.8",
|
|
13
|
+
"@corenel/protocol": "0.7.3",
|
|
14
|
+
"@corenel/harness": "0.7.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"esbuild": "^0.21.5",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"tsx": "^4.19.2",
|
|
21
21
|
"typescript": "^5.9.3",
|
|
22
22
|
"@corenel/crew": "0.0.0",
|
|
23
|
-
"@corenel/tools-node": "0.3.
|
|
23
|
+
"@corenel/tools-node": "0.3.6"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|