@algosuite/vo-mcp 0.2.0-beta.26 → 0.2.0-beta.27
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/agent-auth-probe-cli.mjs +105 -8
- package/dist/runner-cli.js +508 -98
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
|
@@ -177,27 +177,46 @@ function getAnthropicKey({ EntryCtor = defaultEntryCtor() } = {}) {
|
|
|
177
177
|
}
|
|
178
178
|
var PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
|
|
179
179
|
var CLAUDE_PREFER_LOGIN_ENV = "VO_RUNNER_CLAUDE_PREFER_LOGIN";
|
|
180
|
+
var PREFER_KEY_ENV = "VO_RUNNER_PREFER_KEY";
|
|
181
|
+
var CLAUDE_PREFER_KEY_ENV = "VO_RUNNER_CLAUDE_PREFER_KEY";
|
|
180
182
|
function isTruthyFlag(v) {
|
|
181
183
|
const s = String(v ?? "").trim().toLowerCase();
|
|
182
184
|
return s === "1" || s === "true" || s === "yes" || s === "on";
|
|
183
185
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
+
function wantsLogin(env) {
|
|
187
|
+
return isTruthyFlag(env[CLAUDE_PREFER_LOGIN_ENV]) || isTruthyFlag(env[PREFER_LOGIN_ENV]);
|
|
188
|
+
}
|
|
189
|
+
function wantsKey(env) {
|
|
190
|
+
return isTruthyFlag(env[CLAUDE_PREFER_KEY_ENV]) || isTruthyFlag(env[PREFER_KEY_ENV]);
|
|
191
|
+
}
|
|
192
|
+
function withAnthropicKey(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
193
|
+
if (wantsLogin(baseEnv)) {
|
|
186
194
|
const next = { ...baseEnv };
|
|
187
195
|
delete next.ANTHROPIC_API_KEY;
|
|
188
196
|
return next;
|
|
189
197
|
}
|
|
190
198
|
if (baseEnv.ANTHROPIC_API_KEY) return { ...baseEnv };
|
|
191
199
|
const key = getKey();
|
|
192
|
-
|
|
200
|
+
if (!key) return { ...baseEnv };
|
|
201
|
+
if (!wantsKey(baseEnv) && probeLogin() === true) return { ...baseEnv };
|
|
202
|
+
return { ...baseEnv, ANTHROPIC_API_KEY: key };
|
|
203
|
+
}
|
|
204
|
+
function claudeCostBasis(env = process.env) {
|
|
205
|
+
return String(env.ANTHROPIC_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
|
|
193
206
|
}
|
|
194
|
-
function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey } = {}) {
|
|
195
|
-
if (
|
|
207
|
+
function describeAnthropicAuthSource(baseEnv = {}, { getKey = getAnthropicKey, probeLogin = probeClaudeLoginState } = {}) {
|
|
208
|
+
if (wantsLogin(baseEnv)) {
|
|
196
209
|
return "claude auth login (VO_RUNNER_PREFER_LOGIN set \u2014 any API key ignored)";
|
|
197
210
|
}
|
|
198
211
|
if (baseEnv.ANTHROPIC_API_KEY) return "ANTHROPIC_API_KEY from environment";
|
|
199
|
-
if (getKey()) return "
|
|
200
|
-
|
|
212
|
+
if (!getKey()) return "claude auth login session (no API key set)";
|
|
213
|
+
if (wantsKey(baseEnv)) {
|
|
214
|
+
return "ANTHROPIC_API_KEY from OS keychain (VO_RUNNER_PREFER_KEY set \u2014 subscription ignored)";
|
|
215
|
+
}
|
|
216
|
+
if (probeLogin() === true) {
|
|
217
|
+
return "claude auth login session (subscription beats the stored keychain key)";
|
|
218
|
+
}
|
|
219
|
+
return "ANTHROPIC_API_KEY from OS keychain";
|
|
201
220
|
}
|
|
202
221
|
function probeClaudeLoginState({
|
|
203
222
|
spawn: spawn2 = spawnSync2,
|
|
@@ -375,6 +394,36 @@ function parseClaudeStreamEvent(line) {
|
|
|
375
394
|
return event.type === "result" ? buildResultEvent(event) : null;
|
|
376
395
|
}
|
|
377
396
|
|
|
397
|
+
// ../../scripts/virtual-office/code-runner/agent-auth-tier.mjs
|
|
398
|
+
var AUTH_TIER_SUBSCRIPTION = "subscription";
|
|
399
|
+
var AUTH_TIER_API_KEY = "api_key";
|
|
400
|
+
var AUTH_TIER_LOCAL = "local";
|
|
401
|
+
var AUTH_TIER_UNKNOWN = "unknown";
|
|
402
|
+
var AUTH_TIERS = Object.freeze([
|
|
403
|
+
AUTH_TIER_SUBSCRIPTION,
|
|
404
|
+
AUTH_TIER_API_KEY,
|
|
405
|
+
AUTH_TIER_LOCAL,
|
|
406
|
+
AUTH_TIER_UNKNOWN
|
|
407
|
+
]);
|
|
408
|
+
var COST_BASIS_TO_TIER = Object.freeze({
|
|
409
|
+
subscription_api_equivalent: AUTH_TIER_SUBSCRIPTION,
|
|
410
|
+
vendor_billed: AUTH_TIER_API_KEY,
|
|
411
|
+
local_zero: AUTH_TIER_LOCAL
|
|
412
|
+
});
|
|
413
|
+
function authTierFromCostBasis(costBasis) {
|
|
414
|
+
return COST_BASIS_TO_TIER[String(costBasis ?? "")] ?? AUTH_TIER_UNKNOWN;
|
|
415
|
+
}
|
|
416
|
+
function safeAuthTier(compute) {
|
|
417
|
+
try {
|
|
418
|
+
return normalizeAuthTier(compute());
|
|
419
|
+
} catch {
|
|
420
|
+
return AUTH_TIER_UNKNOWN;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function normalizeAuthTier(value) {
|
|
424
|
+
return AUTH_TIERS.includes(value) ? value : AUTH_TIER_UNKNOWN;
|
|
425
|
+
}
|
|
426
|
+
|
|
378
427
|
// ../../scripts/virtual-office/code-runner/cli-version-floor.mjs
|
|
379
428
|
var MIN_CLAUDE_CLI_VERSION = "2.1.218";
|
|
380
429
|
var SECURITY_RATIONALE = "Claude Code 2.1.211/2.1.213 fixed a PreToolUse-hook bypass on unsandboxed Bash (our destructive-fs/git/cloud tripwires DO NOT FIRE on older CLIs) and worktree-subagents mutating the main checkout. 2.1.218 fixed Windows paths with a lowercase-\\u segment (e.g. ...\\utils\\, ...\\ui\\) being corrupted into CJK in tool inputs, making those files silently inaccessible \u2014 the fleet is Windows and 1,376 tracked files sit under utils/ alone. Update: npm install -g @anthropic-ai/claude-code (or the native installer).";
|
|
@@ -440,9 +489,22 @@ function isTimeout(probe) {
|
|
|
440
489
|
function notFound(probe) {
|
|
441
490
|
return errorCode(probe?.error) === "ENOENT";
|
|
442
491
|
}
|
|
492
|
+
function resolveClaudeAuthTier({
|
|
493
|
+
env = process.env,
|
|
494
|
+
loggedIn = null,
|
|
495
|
+
getStoredKey = getAnthropicKey
|
|
496
|
+
} = {}) {
|
|
497
|
+
return safeAuthTier(() => {
|
|
498
|
+
const spawnEnv = withAnthropicKey(env, { getKey: getStoredKey, probeLogin: () => loggedIn });
|
|
499
|
+
const tier = authTierFromCostBasis(claudeCostBasis(spawnEnv));
|
|
500
|
+
if (tier === AUTH_TIER_SUBSCRIPTION && loggedIn !== true) return AUTH_TIER_UNKNOWN;
|
|
501
|
+
return tier;
|
|
502
|
+
});
|
|
503
|
+
}
|
|
443
504
|
async function checkClaudeAuth({
|
|
444
505
|
spawnVersion = spawnClaudeSync,
|
|
445
506
|
probeLogin = probeClaudeLoginState,
|
|
507
|
+
getStoredKey = getAnthropicKey,
|
|
446
508
|
env = process.env
|
|
447
509
|
} = {}) {
|
|
448
510
|
try {
|
|
@@ -492,6 +554,10 @@ async function checkClaudeAuth({
|
|
|
492
554
|
return {
|
|
493
555
|
installed: true,
|
|
494
556
|
authenticated: true,
|
|
557
|
+
// Dispatch-time billing signal, carried on the same probe that already
|
|
558
|
+
// paid for the login read. Never sent for a non-authenticated result:
|
|
559
|
+
// there is no tier without a working credential.
|
|
560
|
+
authTier: resolveClaudeAuthTier({ env, loggedIn, getStoredKey }),
|
|
495
561
|
message: loggedIn === true ? "claude CLI installed and logged in (claude auth status)" : "claude binary found (login state unknown \u2014 auth check is best-effort)"
|
|
496
562
|
};
|
|
497
563
|
} catch (error) {
|
|
@@ -536,7 +602,7 @@ var ClaudeRunner = class {
|
|
|
536
602
|
return withAnthropicKey(env);
|
|
537
603
|
}
|
|
538
604
|
costBasis(env = process.env) {
|
|
539
|
-
return
|
|
605
|
+
return claudeCostBasis(env);
|
|
540
606
|
}
|
|
541
607
|
/** Describe which Anthropic auth source the spawn will use (for runner logs). */
|
|
542
608
|
describeAuth(env = process.env) {
|
|
@@ -811,6 +877,19 @@ var CodexRunner = class {
|
|
|
811
877
|
costBasis(env = process.env) {
|
|
812
878
|
return String(env.OPENAI_API_KEY || env.CODEX_API_KEY || "").trim() ? "vendor_billed" : "subscription_api_equivalent";
|
|
813
879
|
}
|
|
880
|
+
/**
|
|
881
|
+
* Dispatch-time billing tier for the NEXT codex spawn, from the SAME facts
|
|
882
|
+
* checkAuth() already gathered — no extra subprocess. applyAuthEnv() is a
|
|
883
|
+
* keychain read in this process (@napi-rs/keyring), not a spawn.
|
|
884
|
+
*
|
|
885
|
+
* Codex is the agent where this signal was already computed and then thrown
|
|
886
|
+
* away: checkAuth() distinguished "API key available (no persisted ChatGPT
|
|
887
|
+
* login)" from a real login, but only inside a `message` string that the
|
|
888
|
+
* heartbeat schema strips before storage. This gives that fact a typed home.
|
|
889
|
+
*/
|
|
890
|
+
authTier(env = this.env) {
|
|
891
|
+
return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env))));
|
|
892
|
+
}
|
|
814
893
|
/** Best-effort binary + persisted-login probe. Never throws or spends tokens. */
|
|
815
894
|
async checkAuth() {
|
|
816
895
|
try {
|
|
@@ -844,6 +923,7 @@ ${login.stderr || ""}`.trim();
|
|
|
844
923
|
installed: true,
|
|
845
924
|
authenticated: true,
|
|
846
925
|
...versionField,
|
|
926
|
+
authTier: this.authTier(),
|
|
847
927
|
message: "codex API key available (no persisted ChatGPT login)"
|
|
848
928
|
};
|
|
849
929
|
}
|
|
@@ -858,6 +938,7 @@ ${login.stderr || ""}`.trim();
|
|
|
858
938
|
installed: true,
|
|
859
939
|
authenticated: true,
|
|
860
940
|
...versionField,
|
|
941
|
+
authTier: this.authTier(),
|
|
861
942
|
message: output || "codex login status succeeded"
|
|
862
943
|
};
|
|
863
944
|
} catch (err) {
|
|
@@ -960,6 +1041,16 @@ var CursorRunner = class {
|
|
|
960
1041
|
costBasis(env = process.env) {
|
|
961
1042
|
return env.CURSOR_API_KEY ? "vendor_billed" : "unknown";
|
|
962
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Dispatch-time billing tier. Inherits costBasis()'s deliberate refusal to
|
|
1046
|
+
* guess: a prior interactive `cursor-agent login` has undocumented
|
|
1047
|
+
* subscription semantics, so it reports 'unknown' rather than claiming a
|
|
1048
|
+
* flat-cost seat the runner cannot actually prove. Keychain read only — the
|
|
1049
|
+
* heartbeat never pays for a subprocess to answer this.
|
|
1050
|
+
*/
|
|
1051
|
+
authTier(env = process.env) {
|
|
1052
|
+
return safeAuthTier(() => authTierFromCostBasis(this.costBasis(this.applyAuthEnv(env))));
|
|
1053
|
+
}
|
|
963
1054
|
/** Best-effort: is `cursor-agent` on PATH? Never throws. */
|
|
964
1055
|
async checkAuth() {
|
|
965
1056
|
try {
|
|
@@ -985,6 +1076,7 @@ var CursorRunner = class {
|
|
|
985
1076
|
return {
|
|
986
1077
|
installed: true,
|
|
987
1078
|
authenticated: true,
|
|
1079
|
+
authTier: this.authTier(),
|
|
988
1080
|
message: "cursor-agent found (EXPERIMENTAL: headless mode may need a TTY; auth check is best-effort)"
|
|
989
1081
|
};
|
|
990
1082
|
} catch (err) {
|
|
@@ -1121,6 +1213,10 @@ var LocalModelRunner = class {
|
|
|
1121
1213
|
costBasis() {
|
|
1122
1214
|
return "local_zero";
|
|
1123
1215
|
}
|
|
1216
|
+
/** Dispatch-time billing tier: local inference is never billed by a vendor. */
|
|
1217
|
+
authTier() {
|
|
1218
|
+
return safeAuthTier(() => authTierFromCostBasis(this.costBasis()));
|
|
1219
|
+
}
|
|
1124
1220
|
describeAuth(env = process.env) {
|
|
1125
1221
|
const authEnv = this.applyAuthEnv(env);
|
|
1126
1222
|
const provider = resolveLocalProvider(this.env);
|
|
@@ -1178,6 +1274,7 @@ var LocalModelRunner = class {
|
|
|
1178
1274
|
return {
|
|
1179
1275
|
installed: true,
|
|
1180
1276
|
authenticated: true,
|
|
1277
|
+
authTier: this.authTier(),
|
|
1181
1278
|
message: `${provider} reachable; model "${model}" configured (local-only, no cloud spend)`
|
|
1182
1279
|
};
|
|
1183
1280
|
}
|