@oxagen/cli 2.1.4 → 2.1.5-22
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/oxagen.mjs +938 -191
- package/package.json +1 -1
package/oxagen.mjs
CHANGED
|
@@ -5776,7 +5776,7 @@ var init_wire = __esm({
|
|
|
5776
5776
|
harness: "stella",
|
|
5777
5777
|
provider: "anthropic",
|
|
5778
5778
|
prefix: "/stella/anthropic",
|
|
5779
|
-
brokerable:
|
|
5779
|
+
brokerable: true,
|
|
5780
5780
|
baseUrlFile: "$STELLA_HOME/stella.toml"
|
|
5781
5781
|
}
|
|
5782
5782
|
];
|
|
@@ -7149,6 +7149,248 @@ var init_tools = __esm({
|
|
|
7149
7149
|
}
|
|
7150
7150
|
});
|
|
7151
7151
|
|
|
7152
|
+
// ../../packages/tacho/src/claude-code/github-text.ts
|
|
7153
|
+
function mentionsOxagenApp(text5) {
|
|
7154
|
+
return OXAGEN_APP_MENTION.test(text5);
|
|
7155
|
+
}
|
|
7156
|
+
function ghCommands(command2) {
|
|
7157
|
+
const commands = [];
|
|
7158
|
+
for (const piece of splitCommandList(command2)) {
|
|
7159
|
+
const words2 = leadingWords(piece);
|
|
7160
|
+
let i2 = 0;
|
|
7161
|
+
while (i2 < words2.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[i2] ?? ""))
|
|
7162
|
+
i2 += 1;
|
|
7163
|
+
if (isGh(words2[i2])) commands.push(words2.slice(i2));
|
|
7164
|
+
}
|
|
7165
|
+
return commands;
|
|
7166
|
+
}
|
|
7167
|
+
function leadingWords(piece) {
|
|
7168
|
+
const words2 = [];
|
|
7169
|
+
let current = "";
|
|
7170
|
+
let started = false;
|
|
7171
|
+
let quote;
|
|
7172
|
+
for (let i2 = 0; i2 < piece.length; i2 += 1) {
|
|
7173
|
+
const char = piece[i2];
|
|
7174
|
+
const substitution = quote !== "'" && (char === "`" || char === "$" && piece[i2 + 1] === "(");
|
|
7175
|
+
if (substitution) return words2;
|
|
7176
|
+
if (quote !== void 0) {
|
|
7177
|
+
if (char === quote) quote = void 0;
|
|
7178
|
+
else current += char;
|
|
7179
|
+
continue;
|
|
7180
|
+
}
|
|
7181
|
+
if (char === '"' || char === "'") {
|
|
7182
|
+
quote = char;
|
|
7183
|
+
started = true;
|
|
7184
|
+
continue;
|
|
7185
|
+
}
|
|
7186
|
+
if (char === "\\" && piece[i2 + 1] !== void 0) {
|
|
7187
|
+
current += piece[i2 + 1];
|
|
7188
|
+
started = true;
|
|
7189
|
+
i2 += 1;
|
|
7190
|
+
continue;
|
|
7191
|
+
}
|
|
7192
|
+
if ("|&()<>".includes(char)) break;
|
|
7193
|
+
if (/\s/.test(char)) {
|
|
7194
|
+
if (started) words2.push(current);
|
|
7195
|
+
current = "";
|
|
7196
|
+
started = false;
|
|
7197
|
+
continue;
|
|
7198
|
+
}
|
|
7199
|
+
current += char;
|
|
7200
|
+
started = true;
|
|
7201
|
+
}
|
|
7202
|
+
if (started && quote === void 0) words2.push(current);
|
|
7203
|
+
return words2;
|
|
7204
|
+
}
|
|
7205
|
+
function ghSubcommand(words2) {
|
|
7206
|
+
const found = [];
|
|
7207
|
+
for (let j = 1; j < words2.length && found.length < 2; j += 1) {
|
|
7208
|
+
const word = words2[j];
|
|
7209
|
+
if (GH_VALUE_FLAGS.has(word)) j += 1;
|
|
7210
|
+
else if (!word.startsWith("-")) found.push(word);
|
|
7211
|
+
}
|
|
7212
|
+
return [found[0], found[1]];
|
|
7213
|
+
}
|
|
7214
|
+
function ghApiCall(words2) {
|
|
7215
|
+
let method;
|
|
7216
|
+
let body2 = false;
|
|
7217
|
+
let path2;
|
|
7218
|
+
const start = words2.indexOf("api");
|
|
7219
|
+
for (let j = start + 1; j < words2.length; j += 1) {
|
|
7220
|
+
const word = words2[j];
|
|
7221
|
+
const eq = word.indexOf("=");
|
|
7222
|
+
const flag2 = word.startsWith("--") && eq > 0 ? word.slice(0, eq) : word;
|
|
7223
|
+
const inline = flag2 !== word ? word.slice(eq + 1) : void 0;
|
|
7224
|
+
if (/^-X[A-Za-z]+$/.test(word)) {
|
|
7225
|
+
method = word.slice(2);
|
|
7226
|
+
continue;
|
|
7227
|
+
}
|
|
7228
|
+
if (/^-[fF]./.test(word)) {
|
|
7229
|
+
body2 = true;
|
|
7230
|
+
continue;
|
|
7231
|
+
}
|
|
7232
|
+
if (API_VALUE_FLAGS.has(flag2)) {
|
|
7233
|
+
const value = inline ?? words2[j + 1];
|
|
7234
|
+
if (inline === void 0) j += 1;
|
|
7235
|
+
if (flag2 === "-X" || flag2 === "--method") method = value;
|
|
7236
|
+
if (API_BODY_FLAGS.has(flag2)) body2 = true;
|
|
7237
|
+
continue;
|
|
7238
|
+
}
|
|
7239
|
+
if (word.startsWith("-")) continue;
|
|
7240
|
+
path2 ??= word;
|
|
7241
|
+
}
|
|
7242
|
+
return {
|
|
7243
|
+
method: (method ?? (body2 ? "POST" : "GET")).toUpperCase(),
|
|
7244
|
+
...path2 !== void 0 ? { path: path2 } : {}
|
|
7245
|
+
};
|
|
7246
|
+
}
|
|
7247
|
+
function hasBody(words2) {
|
|
7248
|
+
return words2.some(
|
|
7249
|
+
(word) => word === "-b" || word === "--body" || word === "-F" || word === "--body-file" || word.startsWith("--body=") || word.startsWith("--body-file=")
|
|
7250
|
+
);
|
|
7251
|
+
}
|
|
7252
|
+
function shellWritesComment(command2) {
|
|
7253
|
+
return ghCommands(command2).some((words2) => {
|
|
7254
|
+
const [noun, verb] = ghSubcommand(words2);
|
|
7255
|
+
if ((noun === "pr" || noun === "issue") && verb === "comment")
|
|
7256
|
+
return !words2.includes("--edit-last");
|
|
7257
|
+
if (noun === "pr" && verb === "review") return hasBody(words2);
|
|
7258
|
+
if (noun === "api") {
|
|
7259
|
+
const call2 = ghApiCall(words2);
|
|
7260
|
+
const path2 = call2.path?.split("?", 1)[0];
|
|
7261
|
+
return call2.method === "POST" && path2 !== void 0 && COMMENT_API_PATH.test(path2);
|
|
7262
|
+
}
|
|
7263
|
+
return false;
|
|
7264
|
+
});
|
|
7265
|
+
}
|
|
7266
|
+
function endsWithAny(tool, suffixes) {
|
|
7267
|
+
const name = nameWords(tool).join("_");
|
|
7268
|
+
return suffixes.some(
|
|
7269
|
+
(suffix) => name === suffix || name.endsWith(`_${suffix}`)
|
|
7270
|
+
);
|
|
7271
|
+
}
|
|
7272
|
+
function mcpWritesComment(tool, input) {
|
|
7273
|
+
if (endsWithAny(tool, [
|
|
7274
|
+
"add_issue_comment",
|
|
7275
|
+
"add_reply_to_pull_request_comment",
|
|
7276
|
+
"create_pull_request_review"
|
|
7277
|
+
]))
|
|
7278
|
+
return true;
|
|
7279
|
+
if (endsWithAny(tool, ["pull_request_review_write"])) {
|
|
7280
|
+
const body2 = input["body"];
|
|
7281
|
+
return (input["method"] === "create" || input["method"] === "submit_pending") && typeof body2 === "string" && body2.trim().length > 0;
|
|
7282
|
+
}
|
|
7283
|
+
return false;
|
|
7284
|
+
}
|
|
7285
|
+
function commentAttrs(toolName3, input, response) {
|
|
7286
|
+
const fields = typeof input === "object" && input !== null ? input : {};
|
|
7287
|
+
let writes = false;
|
|
7288
|
+
if (SHELL_TOOLS.has(toolName3)) {
|
|
7289
|
+
const command2 = fields["command"];
|
|
7290
|
+
writes = typeof command2 === "string" && shellWritesComment(command2);
|
|
7291
|
+
} else {
|
|
7292
|
+
const tool = mcpToolName(toolName3)?.tool;
|
|
7293
|
+
writes = tool !== void 0 && mcpWritesComment(tool, fields);
|
|
7294
|
+
}
|
|
7295
|
+
if (!writes) return {};
|
|
7296
|
+
for (const text5 of responseTexts(response)) {
|
|
7297
|
+
const match = COMMENT_URL.exec(text5);
|
|
7298
|
+
if (match !== null)
|
|
7299
|
+
return { "comment.url": match[0], "comment.action": "created" };
|
|
7300
|
+
}
|
|
7301
|
+
return {};
|
|
7302
|
+
}
|
|
7303
|
+
function shellWritesGithubText(command2) {
|
|
7304
|
+
return ghCommands(command2).some((words2) => {
|
|
7305
|
+
const [noun, verb] = ghSubcommand(words2);
|
|
7306
|
+
if (noun === "api") return ghApiCall(words2).method !== "GET";
|
|
7307
|
+
if (noun === void 0 || verb === void 0) return false;
|
|
7308
|
+
return GH_TEXT_VERBS[noun]?.has(verb) === true;
|
|
7309
|
+
});
|
|
7310
|
+
}
|
|
7311
|
+
function mcpWritesGithubText(tool) {
|
|
7312
|
+
return nameWords(tool).some((word) => GITHUB_TEXT_WORDS.has(word)) && mcpToolMutates(tool);
|
|
7313
|
+
}
|
|
7314
|
+
function strings(value, out = [], depth = 0) {
|
|
7315
|
+
if (depth > 8) return out;
|
|
7316
|
+
if (typeof value === "string") out.push(value);
|
|
7317
|
+
else if (Array.isArray(value))
|
|
7318
|
+
for (const item of value) strings(item, out, depth + 1);
|
|
7319
|
+
else if (typeof value === "object" && value !== null)
|
|
7320
|
+
for (const item of Object.values(value)) strings(item, out, depth + 1);
|
|
7321
|
+
return out;
|
|
7322
|
+
}
|
|
7323
|
+
function oxagenMentionRefusal(toolName3, shellLine2, input) {
|
|
7324
|
+
if (shellLine2 !== void 0) {
|
|
7325
|
+
return shellWritesGithubText(shellLine2) && mentionsOxagenApp(shellLine2) ? OXAGEN_MENTION_REFUSAL : void 0;
|
|
7326
|
+
}
|
|
7327
|
+
const tool = mcpToolName(toolName3)?.tool;
|
|
7328
|
+
if (tool === void 0 || !mcpWritesGithubText(tool)) return void 0;
|
|
7329
|
+
return strings(input ?? {}).some(mentionsOxagenApp) ? OXAGEN_MENTION_REFUSAL : void 0;
|
|
7330
|
+
}
|
|
7331
|
+
var OXAGEN_APP_MENTION, OXAGEN_MENTION_REFUSAL, GH_VALUE_FLAGS, API_VALUE_FLAGS, API_BODY_FLAGS, COMMENT_API_PATH, SHELL_TOOLS, COMMENT_URL, GH_TEXT_VERBS, GITHUB_TEXT_WORDS;
|
|
7332
|
+
var init_github_text = __esm({
|
|
7333
|
+
"../../packages/tacho/src/claude-code/github-text.ts"() {
|
|
7334
|
+
"use strict";
|
|
7335
|
+
init_tools();
|
|
7336
|
+
OXAGEN_APP_MENTION = /(^|[^\w@/.-])@oxagen(-[a-z0-9-]+)?(\[bot\])?\b/i;
|
|
7337
|
+
OXAGEN_MENTION_REFUSAL = "This agent may not mention the Oxagen App in GitHub text, because a mention asks Oxagen to start or steer an agent. Remove the mention and run the call again.";
|
|
7338
|
+
GH_VALUE_FLAGS = /* @__PURE__ */ new Set(["-R", "--repo"]);
|
|
7339
|
+
API_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
7340
|
+
"-X",
|
|
7341
|
+
"--method",
|
|
7342
|
+
"-H",
|
|
7343
|
+
"--header",
|
|
7344
|
+
"-f",
|
|
7345
|
+
"--raw-field",
|
|
7346
|
+
"-F",
|
|
7347
|
+
"--field",
|
|
7348
|
+
"--input",
|
|
7349
|
+
"-q",
|
|
7350
|
+
"--jq",
|
|
7351
|
+
"-t",
|
|
7352
|
+
"--template",
|
|
7353
|
+
"--hostname",
|
|
7354
|
+
"--cache",
|
|
7355
|
+
"-p",
|
|
7356
|
+
"--preview",
|
|
7357
|
+
"-R",
|
|
7358
|
+
"--repo"
|
|
7359
|
+
]);
|
|
7360
|
+
API_BODY_FLAGS = /* @__PURE__ */ new Set([
|
|
7361
|
+
"-f",
|
|
7362
|
+
"--raw-field",
|
|
7363
|
+
"-F",
|
|
7364
|
+
"--field",
|
|
7365
|
+
"--input"
|
|
7366
|
+
]);
|
|
7367
|
+
COMMENT_API_PATH = /\/(issues\/\d+\/comments|pulls\/\d+\/comments|pulls\/\d+\/comments\/\d+\/replies|pulls\/\d+\/reviews)\/?$/;
|
|
7368
|
+
SHELL_TOOLS = /* @__PURE__ */ new Set([
|
|
7369
|
+
"Bash",
|
|
7370
|
+
"Shell",
|
|
7371
|
+
"bash",
|
|
7372
|
+
"shell",
|
|
7373
|
+
"local_shell",
|
|
7374
|
+
"exec_command"
|
|
7375
|
+
]);
|
|
7376
|
+
COMMENT_URL = /https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/(?:pull|issues)\/\d+#(?:issuecomment-\d+|discussion_r\d+|pullrequestreview-\d+)/;
|
|
7377
|
+
GH_TEXT_VERBS = {
|
|
7378
|
+
pr: /* @__PURE__ */ new Set(["create", "edit", "comment", "review", "close", "merge"]),
|
|
7379
|
+
issue: /* @__PURE__ */ new Set(["create", "edit", "comment", "close", "reopen"])
|
|
7380
|
+
};
|
|
7381
|
+
GITHUB_TEXT_WORDS = /* @__PURE__ */ new Set([
|
|
7382
|
+
"issue",
|
|
7383
|
+
"issues",
|
|
7384
|
+
"pull",
|
|
7385
|
+
"comment",
|
|
7386
|
+
"comments",
|
|
7387
|
+
"review",
|
|
7388
|
+
"reviews",
|
|
7389
|
+
"discussion"
|
|
7390
|
+
]);
|
|
7391
|
+
}
|
|
7392
|
+
});
|
|
7393
|
+
|
|
7152
7394
|
// ../../packages/tacho/src/policy/builtins.ts
|
|
7153
7395
|
function isCedarHarness(harness) {
|
|
7154
7396
|
return CEDAR_HARNESSES.includes(harness);
|
|
@@ -7877,7 +8119,7 @@ function pathMatches(spec, path2, context) {
|
|
|
7877
8119
|
);
|
|
7878
8120
|
}
|
|
7879
8121
|
function isShellTool(toolName3) {
|
|
7880
|
-
return
|
|
8122
|
+
return SHELL_TOOLS2.has(toolName3);
|
|
7881
8123
|
}
|
|
7882
8124
|
function compiledGlob(glob) {
|
|
7883
8125
|
let regex = globCache.get(glob);
|
|
@@ -8046,6 +8288,12 @@ function evaluatePreToolUse(input) {
|
|
|
8046
8288
|
{ source: "human" }
|
|
8047
8289
|
);
|
|
8048
8290
|
}
|
|
8291
|
+
const mention = oxagenMentionRefusal(
|
|
8292
|
+
toolName3,
|
|
8293
|
+
isShellTool(toolName3) ? shellLineOf(toolInput ?? {}) : void 0,
|
|
8294
|
+
toolInput
|
|
8295
|
+
);
|
|
8296
|
+
if (mention !== void 0) return deny2("oxagen_app_mention", mention);
|
|
8049
8297
|
if (!input.bundleVerified) {
|
|
8050
8298
|
if (readOnly) {
|
|
8051
8299
|
return {
|
|
@@ -8293,11 +8541,12 @@ function decideWithCedar(input) {
|
|
|
8293
8541
|
};
|
|
8294
8542
|
}
|
|
8295
8543
|
}
|
|
8296
|
-
var SHELL_OPERATORS, SHELL_LEADING_WORDS, ARGV_SHELLS, WINDOWS_PATH,
|
|
8544
|
+
var SHELL_OPERATORS, SHELL_LEADING_WORDS, ARGV_SHELLS, WINDOWS_PATH, SHELL_TOOLS2, DECLARATION_SYNONYMS, GLOB_CACHE_LIMIT, globCache, parsedRuleCache, WRITE_EFFECTS;
|
|
8297
8545
|
var init_bundle = __esm({
|
|
8298
8546
|
"../../packages/tacho/src/host/bundle.ts"() {
|
|
8299
8547
|
"use strict";
|
|
8300
8548
|
init_digest();
|
|
8549
|
+
init_github_text();
|
|
8301
8550
|
init_tools();
|
|
8302
8551
|
init_builtins();
|
|
8303
8552
|
init_hook();
|
|
@@ -8320,7 +8569,7 @@ var init_bundle = __esm({
|
|
|
8320
8569
|
]);
|
|
8321
8570
|
ARGV_SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash"]);
|
|
8322
8571
|
WINDOWS_PATH = /^(?:[A-Za-z]:[\\/]|\\\\)/;
|
|
8323
|
-
|
|
8572
|
+
SHELL_TOOLS2 = /* @__PURE__ */ new Set([
|
|
8324
8573
|
"Bash",
|
|
8325
8574
|
"Shell",
|
|
8326
8575
|
"bash",
|
|
@@ -11907,7 +12156,8 @@ function normalizeHook(raw, env, options) {
|
|
|
11907
12156
|
const tool = input.tool_name ?? "";
|
|
11908
12157
|
const issued = {
|
|
11909
12158
|
...issueAttrs(tool, input.tool_input, input.tool_response),
|
|
11910
|
-
...releaseAttrs(tool, input.tool_input)
|
|
12159
|
+
...releaseAttrs(tool, input.tool_input),
|
|
12160
|
+
...commentAttrs(tool, input.tool_input, input.tool_response)
|
|
11911
12161
|
};
|
|
11912
12162
|
const named = {
|
|
11913
12163
|
...facts["effect_kind"] === "pr_open" ? pullRequestAttrs(input.tool_response) : {},
|
|
@@ -12203,6 +12453,7 @@ var init_hooks = __esm({
|
|
|
12203
12453
|
init_wire();
|
|
12204
12454
|
init_context();
|
|
12205
12455
|
init_payload_members();
|
|
12456
|
+
init_github_text();
|
|
12206
12457
|
init_tools();
|
|
12207
12458
|
textMember = external_exports.preprocess((value) => {
|
|
12208
12459
|
if (typeof value === "number" || typeof value === "boolean")
|
|
@@ -15458,7 +15709,7 @@ var init_tool_call_dedupe = __esm({
|
|
|
15458
15709
|
* sighting and registers nothing. `owner` is the subagent whose chain the
|
|
15459
15710
|
* row seals on.
|
|
15460
15711
|
*/
|
|
15461
|
-
judge(toolUseId, source,
|
|
15712
|
+
judge(toolUseId, source, hasBody2, owner) {
|
|
15462
15713
|
if (toolUseId === void 0 || toolUseId.length === 0)
|
|
15463
15714
|
return { verdict: { kind: "first" }, commit: () => {
|
|
15464
15715
|
} };
|
|
@@ -15467,13 +15718,13 @@ var init_tool_call_dedupe = __esm({
|
|
|
15467
15718
|
const commit = () => {
|
|
15468
15719
|
const entry2 = seen ?? { sources: [], body: false };
|
|
15469
15720
|
if (!entry2.sources.includes(source)) entry2.sources.push(source);
|
|
15470
|
-
entry2.body = entry2.body ||
|
|
15721
|
+
entry2.body = entry2.body || hasBody2;
|
|
15471
15722
|
if (owner !== void 0) entry2.owner ??= owner;
|
|
15472
15723
|
this.entries.set(toolUseId, entry2);
|
|
15473
15724
|
this.trim();
|
|
15474
15725
|
};
|
|
15475
15726
|
if (first === void 0) return { verdict: { kind: "first" }, commit };
|
|
15476
|
-
if (
|
|
15727
|
+
if (hasBody2 && seen?.body !== true)
|
|
15477
15728
|
return { verdict: { kind: "body", of: first }, commit };
|
|
15478
15729
|
return { verdict: { kind: "repeat" }, commit };
|
|
15479
15730
|
}
|
|
@@ -16305,7 +16556,7 @@ var init_otel = __esm({
|
|
|
16305
16556
|
function rec2(value) {
|
|
16306
16557
|
return isRecord2(value) ? value : void 0;
|
|
16307
16558
|
}
|
|
16308
|
-
function
|
|
16559
|
+
function strings2(value) {
|
|
16309
16560
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
|
|
16310
16561
|
}
|
|
16311
16562
|
function inventoryFromInit(record) {
|
|
@@ -16317,15 +16568,15 @@ function inventoryFromInit(record) {
|
|
|
16317
16568
|
const set = (key, value) => {
|
|
16318
16569
|
if (value !== void 0) out[key] = value;
|
|
16319
16570
|
};
|
|
16320
|
-
set("tools_available",
|
|
16571
|
+
set("tools_available", strings2(init["tools"]));
|
|
16321
16572
|
set("mcp_servers", init["mcp_servers"]);
|
|
16322
|
-
set("agents_available",
|
|
16323
|
-
set("skills_available",
|
|
16324
|
-
set("slash_commands",
|
|
16573
|
+
set("agents_available", strings2(init["agents"]));
|
|
16574
|
+
set("skills_available", strings2(init["skills"]));
|
|
16575
|
+
set("slash_commands", strings2(init["slash_commands"]));
|
|
16325
16576
|
set("plugins", init["plugins"]);
|
|
16326
16577
|
set("plugin_errors", init["plugin_errors"]);
|
|
16327
16578
|
set("mcp_server_errors", init["mcp_server_errors"]);
|
|
16328
|
-
set("harness_capabilities",
|
|
16579
|
+
set("harness_capabilities", strings2(init["capabilities"]));
|
|
16329
16580
|
set("memory_paths", init["memory_paths"]);
|
|
16330
16581
|
set("analytics_disabled", bool2(init["analytics_disabled"]));
|
|
16331
16582
|
set("product_feedback_disabled", bool2(init["product_feedback_disabled"]));
|
|
@@ -17828,12 +18079,12 @@ var init_recorder = __esm({
|
|
|
17828
18079
|
* sighting could not, and it is stamped so a reader counts one call. See
|
|
17829
18080
|
* `tool-call-dedupe.ts` and ADR-140.
|
|
17830
18081
|
*/
|
|
17831
|
-
toolCallSighting(body2, source,
|
|
18082
|
+
toolCallSighting(body2, source, hasBody2) {
|
|
17832
18083
|
const toolUseId = typeof body2["tool_use_id"] === "string" ? body2["tool_use_id"] : void 0;
|
|
17833
18084
|
const { verdict, commit } = this.toolCallLedger.judge(
|
|
17834
18085
|
toolUseId,
|
|
17835
18086
|
source,
|
|
17836
|
-
|
|
18087
|
+
hasBody2,
|
|
17837
18088
|
this.options.parent?.subagentId
|
|
17838
18089
|
);
|
|
17839
18090
|
if (verdict.kind === "repeat") return { attrs: void 0, commit };
|
|
@@ -22629,6 +22880,15 @@ import {
|
|
|
22629
22880
|
function anthropicCredentialKind(secret, fallback) {
|
|
22630
22881
|
return secret.trim().startsWith(CLAUDE_OAUTH_TOKEN_PREFIX) ? "oauth" : fallback;
|
|
22631
22882
|
}
|
|
22883
|
+
function harnessOf(entry2) {
|
|
22884
|
+
return entry2.harness ?? defaultHarnessForProvider(entry2.provider);
|
|
22885
|
+
}
|
|
22886
|
+
function sealingContext(provider, harness) {
|
|
22887
|
+
return harness === defaultHarnessForProvider(provider) ? `${CREDENTIAL_STORE_SCHEMA}:${provider}` : `${CREDENTIAL_STORE_SCHEMA}:${provider}:${harness}`;
|
|
22888
|
+
}
|
|
22889
|
+
function matches(entry2, provider, harness) {
|
|
22890
|
+
return entry2.provider === provider && harnessOf(entry2) === harness;
|
|
22891
|
+
}
|
|
22632
22892
|
function readKey(path2) {
|
|
22633
22893
|
try {
|
|
22634
22894
|
const hex = readFileSync7(path2, "utf8").trim();
|
|
@@ -22703,6 +22963,7 @@ function credentialPrefix(secret) {
|
|
|
22703
22963
|
function custodyOf(entry2) {
|
|
22704
22964
|
return {
|
|
22705
22965
|
provider: entry2.provider,
|
|
22966
|
+
harness: harnessOf(entry2),
|
|
22706
22967
|
kind: entry2.kind,
|
|
22707
22968
|
source: entry2.source,
|
|
22708
22969
|
taken_at: entry2.taken_at,
|
|
@@ -22768,49 +23029,56 @@ function openCredentialStore(paths) {
|
|
|
22768
23029
|
`
|
|
22769
23030
|
);
|
|
22770
23031
|
}
|
|
23032
|
+
const ownerOf = (provider, harness) => harness ?? defaultHarnessForProvider(provider);
|
|
22771
23033
|
return {
|
|
22772
|
-
has: (provider
|
|
22773
|
-
|
|
22774
|
-
|
|
23034
|
+
has: (provider, harness) => {
|
|
23035
|
+
const owner = ownerOf(provider, harness);
|
|
23036
|
+
return readAll().some((e) => matches(e, provider, owner));
|
|
23037
|
+
},
|
|
23038
|
+
read: (provider, harness) => {
|
|
23039
|
+
const owner = ownerOf(provider, harness);
|
|
23040
|
+
const entry2 = readAll().find((e) => matches(e, provider, owner));
|
|
22775
23041
|
if (entry2 === void 0) return void 0;
|
|
22776
23042
|
return {
|
|
22777
23043
|
kind: entry2.kind,
|
|
22778
23044
|
secret: open(
|
|
22779
23045
|
keyForRead(),
|
|
22780
23046
|
entry2.sealed,
|
|
22781
|
-
|
|
23047
|
+
sealingContext(provider, owner)
|
|
22782
23048
|
)
|
|
22783
23049
|
};
|
|
22784
23050
|
},
|
|
22785
|
-
take: (provider, { kind, secret }, source, now) => {
|
|
23051
|
+
take: (provider, { kind, secret }, source, now, harness) => {
|
|
22786
23052
|
if (secret.trim().length === 0)
|
|
22787
23053
|
throw new Error("refusing to take an empty credential into custody");
|
|
23054
|
+
const owner = ownerOf(provider, harness);
|
|
22788
23055
|
const entry2 = {
|
|
22789
23056
|
provider,
|
|
23057
|
+
harness: owner,
|
|
22790
23058
|
kind,
|
|
22791
23059
|
source,
|
|
22792
23060
|
taken_at: new Date(now).toISOString(),
|
|
22793
23061
|
// A ChatGPT login is JSON, whose head says nothing; it is named.
|
|
22794
23062
|
prefix: kind === "chatgpt" ? "login" : credentialPrefix(secret),
|
|
22795
|
-
sealed: seal(
|
|
22796
|
-
keyForWrite(),
|
|
22797
|
-
secret,
|
|
22798
|
-
`${CREDENTIAL_STORE_SCHEMA}:${provider}`
|
|
22799
|
-
)
|
|
23063
|
+
sealed: seal(keyForWrite(), secret, sealingContext(provider, owner))
|
|
22800
23064
|
};
|
|
22801
|
-
writeAll2([
|
|
23065
|
+
writeAll2([
|
|
23066
|
+
...readAll().filter((e) => !matches(e, provider, owner)),
|
|
23067
|
+
entry2
|
|
23068
|
+
]);
|
|
22802
23069
|
return custodyOf(entry2);
|
|
22803
23070
|
},
|
|
22804
|
-
release: (provider) => {
|
|
23071
|
+
release: (provider, harness) => {
|
|
23072
|
+
const owner = ownerOf(provider, harness);
|
|
22805
23073
|
const entries2 = readAll();
|
|
22806
|
-
const entry2 = entries2.find((e) => e
|
|
23074
|
+
const entry2 = entries2.find((e) => matches(e, provider, owner));
|
|
22807
23075
|
if (entry2 === void 0) return void 0;
|
|
22808
23076
|
const secret = open(
|
|
22809
23077
|
keyForRead(),
|
|
22810
23078
|
entry2.sealed,
|
|
22811
|
-
|
|
23079
|
+
sealingContext(provider, owner)
|
|
22812
23080
|
);
|
|
22813
|
-
writeAll2(entries2.filter((e) => e
|
|
23081
|
+
writeAll2(entries2.filter((e) => !matches(e, provider, owner)));
|
|
22814
23082
|
return { kind: entry2.kind, secret };
|
|
22815
23083
|
},
|
|
22816
23084
|
status: () => readAll().map(custodyOf),
|
|
@@ -22837,6 +23105,7 @@ var init_credential_store = __esm({
|
|
|
22837
23105
|
"../../packages/tacho/src/host/credential-store.ts"() {
|
|
22838
23106
|
"use strict";
|
|
22839
23107
|
init_zod();
|
|
23108
|
+
init_wire();
|
|
22840
23109
|
init_fs();
|
|
22841
23110
|
init_run_token();
|
|
22842
23111
|
CREDENTIAL_STORE_SCHEMA = "tacho.credentials.v1";
|
|
@@ -22845,6 +23114,8 @@ var init_credential_store = __esm({
|
|
|
22845
23114
|
"claude-code:settings.env",
|
|
22846
23115
|
/** `OPENAI_API_KEY`, or the ChatGPT login's `tokens`, in Codex's `auth.json`. */
|
|
22847
23116
|
"codex:auth.json",
|
|
23117
|
+
/** `providers.anthropic.api_key` in Stella's `stella.toml` or legacy `settings.json`. */
|
|
23118
|
+
"stella:settings",
|
|
22848
23119
|
/** A ChatGPT login the gateway refreshed, replacing the one it took. */
|
|
22849
23120
|
"chatgpt:refresh",
|
|
22850
23121
|
/**
|
|
@@ -22879,6 +23150,12 @@ var init_credential_store = __esm({
|
|
|
22879
23150
|
}).strict();
|
|
22880
23151
|
entrySchema = external_exports.object({
|
|
22881
23152
|
provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
|
|
23153
|
+
/**
|
|
23154
|
+
* The harness the secret belongs to. Absent on a row written before
|
|
23155
|
+
* custody was keyed by harness; such a row is the provider's default
|
|
23156
|
+
* harness's (`harnessOf`).
|
|
23157
|
+
*/
|
|
23158
|
+
harness: external_exports.enum(BROKERABLE_HARNESSES).optional(),
|
|
22882
23159
|
kind: external_exports.enum(CREDENTIAL_KINDS),
|
|
22883
23160
|
source: external_exports.enum(CREDENTIAL_SOURCES),
|
|
22884
23161
|
taken_at: external_exports.string(),
|
|
@@ -23120,6 +23397,12 @@ function findTopLevelKey(lines, pattern) {
|
|
|
23120
23397
|
return -1;
|
|
23121
23398
|
}
|
|
23122
23399
|
function findStellaBaseUrl(lines) {
|
|
23400
|
+
return findStellaKey(lines, BASE_URL_LINE);
|
|
23401
|
+
}
|
|
23402
|
+
function findStellaApiKey(lines) {
|
|
23403
|
+
return findStellaKey(lines, API_KEY_LINE);
|
|
23404
|
+
}
|
|
23405
|
+
function findStellaKey(lines, keyLine) {
|
|
23123
23406
|
let header2 = -1;
|
|
23124
23407
|
let key = -1;
|
|
23125
23408
|
for (const line of tomlKeyLines(lines)) {
|
|
@@ -23140,11 +23423,11 @@ function findStellaBaseUrl(lines) {
|
|
|
23140
23423
|
if (/^\s*["']?anthropic["']?\s*[.=]/.test(text5))
|
|
23141
23424
|
return { kind: "conflict" };
|
|
23142
23425
|
} else if (line.table === STELLA_TABLE && key < 0) {
|
|
23143
|
-
if (
|
|
23426
|
+
if (keyLine.test(text5)) key = line.index;
|
|
23144
23427
|
}
|
|
23145
23428
|
}
|
|
23146
23429
|
if (header2 < 0) return { kind: "absent" };
|
|
23147
|
-
const rest = key >= 0 ?
|
|
23430
|
+
const rest = key >= 0 ? keyLine.exec(lines[key]?.text ?? "")?.[1] ?? "" : "";
|
|
23148
23431
|
return {
|
|
23149
23432
|
kind: "table",
|
|
23150
23433
|
header: header2,
|
|
@@ -23164,12 +23447,14 @@ function stellaTableIsEmpty(lines, header2) {
|
|
|
23164
23447
|
}
|
|
23165
23448
|
return true;
|
|
23166
23449
|
}
|
|
23167
|
-
var STELLA_TABLE, BASE_URL_LINE;
|
|
23450
|
+
var STELLA_TABLE, BASE_URL_LINE, STELLA_API_KEY_LINE, API_KEY_LINE;
|
|
23168
23451
|
var init_model_base_url_toml = __esm({
|
|
23169
23452
|
"../../packages/tacho/src/host/model-base-url-toml.ts"() {
|
|
23170
23453
|
"use strict";
|
|
23171
23454
|
STELLA_TABLE = "providers.anthropic";
|
|
23172
23455
|
BASE_URL_LINE = /^\s*(?:base_url|"base_url"|'base_url')\s*=\s*(.*)$/;
|
|
23456
|
+
STELLA_API_KEY_LINE = /^(\s*(?:api_key|"api_key"|'api_key')\s*=\s*)("(?:[^"\\]|\\.)*"|'[^']*')(.*)$/;
|
|
23457
|
+
API_KEY_LINE = /^\s*(?:api_key|"api_key"|'api_key')\s*=\s*(.*)$/;
|
|
23173
23458
|
}
|
|
23174
23459
|
});
|
|
23175
23460
|
|
|
@@ -23865,6 +24150,11 @@ function helperCommandFor(tachoCommand) {
|
|
|
23865
24150
|
return `${tachoCommand} credential issue --harness claude-code`;
|
|
23866
24151
|
}
|
|
23867
24152
|
function fileFor2(harness, homes) {
|
|
24153
|
+
if (harness === "stella")
|
|
24154
|
+
return modelBaseUrlFile("stella", {
|
|
24155
|
+
home: homes.home,
|
|
24156
|
+
...homes.stellaHome !== void 0 ? { stellaHome: homes.stellaHome } : {}
|
|
24157
|
+
});
|
|
23868
24158
|
return harness === "claude-code" ? join15(
|
|
23869
24159
|
homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
|
|
23870
24160
|
"settings.json"
|
|
@@ -23917,6 +24207,14 @@ function readSidecar2(path2, harness) {
|
|
|
23917
24207
|
) : []
|
|
23918
24208
|
};
|
|
23919
24209
|
if (harness === "codex") return { ...common, harness };
|
|
24210
|
+
if (harness === "stella") {
|
|
24211
|
+
const quote = parsed2["quote"];
|
|
24212
|
+
return {
|
|
24213
|
+
...common,
|
|
24214
|
+
harness,
|
|
24215
|
+
quote: quote === "basic" || quote === "literal" ? quote : null
|
|
24216
|
+
};
|
|
24217
|
+
}
|
|
23920
24218
|
const helper = parsed2["previous_helper"];
|
|
23921
24219
|
return {
|
|
23922
24220
|
...common,
|
|
@@ -23994,6 +24292,23 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
|
|
|
23994
24292
|
);
|
|
23995
24293
|
shadow = managed.shadow;
|
|
23996
24294
|
if (managed.unreadable !== void 0) unreadable.push(managed.unreadable);
|
|
24295
|
+
} else if (harness === "stella") {
|
|
24296
|
+
let at2 = { kind: "absent" };
|
|
24297
|
+
try {
|
|
24298
|
+
at2 = stellaKeyIn(file, text5);
|
|
24299
|
+
} catch (error) {
|
|
24300
|
+
unreadable.push(error instanceof Error ? error.message : String(error));
|
|
24301
|
+
}
|
|
24302
|
+
if (at2.kind === "value") {
|
|
24303
|
+
brokered = looksLikeRunToken(at2.value);
|
|
24304
|
+
if (brokered) {
|
|
24305
|
+
const claims = peekRunTokenClaims(at2.value);
|
|
24306
|
+
if (claims !== void 0)
|
|
24307
|
+
tokenExpiresAt = new Date(claims.exp).toISOString();
|
|
24308
|
+
} else if (why === void 0 && anthropicCredentialKind(at2.value, "api_key") !== "api_key")
|
|
24309
|
+
why = "subscription_login";
|
|
24310
|
+
} else if (why === void 0)
|
|
24311
|
+
why = at2.kind === "unsupported" ? "unsupported_shape" : "no_key";
|
|
23997
24312
|
} else {
|
|
23998
24313
|
const auth = parseForReport(text5, file, unreadable);
|
|
23999
24314
|
const key = codexTokenMember(auth);
|
|
@@ -24164,6 +24479,230 @@ function applyCodex(options, taken) {
|
|
|
24164
24479
|
writeFileAtomicPreserving(file, next);
|
|
24165
24480
|
return { changed: true, text: next };
|
|
24166
24481
|
}
|
|
24482
|
+
function isJsonPath(file) {
|
|
24483
|
+
return file.endsWith(".json");
|
|
24484
|
+
}
|
|
24485
|
+
function objectMember(parent, key) {
|
|
24486
|
+
const value = parent?.[key];
|
|
24487
|
+
if (value === void 0) return void 0;
|
|
24488
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
24489
|
+
}
|
|
24490
|
+
function stellaKeyIn(file, text5) {
|
|
24491
|
+
if (isJsonPath(file)) {
|
|
24492
|
+
const settings = parseJsonObject(text5, file);
|
|
24493
|
+
const providers = objectMember(settings, "providers");
|
|
24494
|
+
if (providers === null) return { kind: "unsupported" };
|
|
24495
|
+
const anthropic = objectMember(providers, "anthropic");
|
|
24496
|
+
if (anthropic === null) return { kind: "unsupported" };
|
|
24497
|
+
const value2 = anthropic?.["api_key"];
|
|
24498
|
+
if (value2 === void 0 || value2 === null || value2 === "")
|
|
24499
|
+
return { kind: "absent" };
|
|
24500
|
+
if (typeof value2 !== "string") return { kind: "unsupported" };
|
|
24501
|
+
return { kind: "value", value: value2, quote: null };
|
|
24502
|
+
}
|
|
24503
|
+
const lines = splitLines(text5);
|
|
24504
|
+
const found = findStellaApiKey(lines);
|
|
24505
|
+
if (found.kind === "conflict") return { kind: "unsupported" };
|
|
24506
|
+
if (found.kind === "absent" || found.key < 0) return { kind: "absent" };
|
|
24507
|
+
const match = STELLA_API_KEY_LINE.exec(lines[found.key]?.text ?? "");
|
|
24508
|
+
const quoted3 = match?.[2];
|
|
24509
|
+
const value = quoted3 !== void 0 ? tomlStringValue(quoted3) : null;
|
|
24510
|
+
if (quoted3 === void 0 || value === null) return { kind: "unsupported" };
|
|
24511
|
+
if (value.length === 0) return { kind: "absent" };
|
|
24512
|
+
return {
|
|
24513
|
+
kind: "value",
|
|
24514
|
+
value,
|
|
24515
|
+
quote: quoted3.startsWith("'") ? "literal" : "basic"
|
|
24516
|
+
};
|
|
24517
|
+
}
|
|
24518
|
+
function tomlString(value, quote) {
|
|
24519
|
+
return quote === "literal" && !/['\r\n]/.test(value) ? `'${value}'` : JSON.stringify(value);
|
|
24520
|
+
}
|
|
24521
|
+
function withStellaKey(file, text5, value, quote) {
|
|
24522
|
+
if (isJsonPath(file)) {
|
|
24523
|
+
const settings = parseJsonObject(text5, file);
|
|
24524
|
+
const providers = objectMember(settings, "providers");
|
|
24525
|
+
const anthropic = objectMember(providers ?? void 0, "anthropic");
|
|
24526
|
+
if (providers === null || anthropic === null) return void 0;
|
|
24527
|
+
settings["providers"] = {
|
|
24528
|
+
...providers ?? {},
|
|
24529
|
+
anthropic: { ...anthropic ?? {}, api_key: value }
|
|
24530
|
+
};
|
|
24531
|
+
return serializeJsonLike(text5, settings);
|
|
24532
|
+
}
|
|
24533
|
+
const lines = splitLines(text5 ?? "");
|
|
24534
|
+
const eol = (text5 ?? "").includes("\r\n") ? "\r\n" : "\n";
|
|
24535
|
+
const found = findStellaApiKey(lines);
|
|
24536
|
+
if (found.kind === "conflict") return void 0;
|
|
24537
|
+
const line = `api_key = ${tomlString(value, quote)}`;
|
|
24538
|
+
if (found.kind === "table" && found.key >= 0) {
|
|
24539
|
+
const at3 = lines[found.key];
|
|
24540
|
+
const match = STELLA_API_KEY_LINE.exec(at3.text);
|
|
24541
|
+
if (match === null) return void 0;
|
|
24542
|
+
at3.text = `${match[1] ?? ""}${tomlString(value, quote)}${match[3] ?? ""}`;
|
|
24543
|
+
return joinLines(lines);
|
|
24544
|
+
}
|
|
24545
|
+
if (found.kind === "table") {
|
|
24546
|
+
const header2 = lines[found.header];
|
|
24547
|
+
const after = header2.eol;
|
|
24548
|
+
if (header2.eol === "") header2.eol = eol;
|
|
24549
|
+
lines.splice(found.header + 1, 0, { text: line, eol: after });
|
|
24550
|
+
return joinLines(lines);
|
|
24551
|
+
}
|
|
24552
|
+
const marker = lines.findIndex((l) => STELLA_BLOCK_MARKER2.test(l.text));
|
|
24553
|
+
const at2 = marker >= 0 ? marker : lines.length;
|
|
24554
|
+
const before = lines[at2 - 1];
|
|
24555
|
+
const added = [];
|
|
24556
|
+
if (before !== void 0) {
|
|
24557
|
+
if (before.eol === "") before.eol = eol;
|
|
24558
|
+
if (before.text.trim().length > 0) added.push({ text: "", eol });
|
|
24559
|
+
}
|
|
24560
|
+
added.push({ text: "[providers.anthropic]", eol }, { text: line, eol });
|
|
24561
|
+
lines.splice(at2, 0, ...added);
|
|
24562
|
+
return joinLines(lines);
|
|
24563
|
+
}
|
|
24564
|
+
function withoutStellaKey(file, text5) {
|
|
24565
|
+
if (isJsonPath(file)) {
|
|
24566
|
+
const settings = parseJsonObject(text5, file);
|
|
24567
|
+
const anthropic = objectMember(
|
|
24568
|
+
objectMember(settings, "providers") ?? void 0,
|
|
24569
|
+
"anthropic"
|
|
24570
|
+
);
|
|
24571
|
+
if (anthropic === null || anthropic === void 0) return void 0;
|
|
24572
|
+
delete anthropic["api_key"];
|
|
24573
|
+
return serializeJsonLike(text5, settings);
|
|
24574
|
+
}
|
|
24575
|
+
const lines = splitLines(text5);
|
|
24576
|
+
const found = findStellaApiKey(lines);
|
|
24577
|
+
if (found.kind !== "table" || found.key < 0) return void 0;
|
|
24578
|
+
lines.splice(found.key, 1);
|
|
24579
|
+
return joinLines(lines);
|
|
24580
|
+
}
|
|
24581
|
+
function applyStella(options, taken) {
|
|
24582
|
+
const file = fileFor2("stella", options);
|
|
24583
|
+
const backup = sidecarFor2(file);
|
|
24584
|
+
const text5 = readTextIfExists(file);
|
|
24585
|
+
if (text5 === void 0) return { changed: false, text: text5, reason: "no_file" };
|
|
24586
|
+
const read2 = readSidecar2(backup, "stella");
|
|
24587
|
+
const existing = read2?.harness === "stella" ? read2 : void 0;
|
|
24588
|
+
const at2 = stellaKeyIn(file, text5);
|
|
24589
|
+
if (at2.kind === "unsupported")
|
|
24590
|
+
return { changed: false, text: text5, reason: "unsupported_shape" };
|
|
24591
|
+
if (at2.kind === "absent") return { changed: false, text: text5, reason: "no_key" };
|
|
24592
|
+
const current = at2.value;
|
|
24593
|
+
const ours = looksLikeRunToken(current);
|
|
24594
|
+
if (!ours) {
|
|
24595
|
+
if (anthropicCredentialKind(current, "api_key") !== "api_key")
|
|
24596
|
+
return { changed: false, text: text5, reason: "subscription_login" };
|
|
24597
|
+
taken.push({
|
|
24598
|
+
harness: "stella",
|
|
24599
|
+
provider: "anthropic",
|
|
24600
|
+
credential: { kind: "api_key", secret: current },
|
|
24601
|
+
member: STELLA_API_KEY_MEMBER
|
|
24602
|
+
});
|
|
24603
|
+
}
|
|
24604
|
+
const token = options.staticTokens?.stella;
|
|
24605
|
+
if (token === void 0) {
|
|
24606
|
+
if (!ours) taken.pop();
|
|
24607
|
+
return { changed: false, text: text5, reason: "no_token" };
|
|
24608
|
+
}
|
|
24609
|
+
if (current === token && existing !== void 0)
|
|
24610
|
+
return { changed: false, text: text5 };
|
|
24611
|
+
const next = withStellaKey(file, text5, token, at2.quote);
|
|
24612
|
+
if (next === void 0) {
|
|
24613
|
+
if (!ours) taken.pop();
|
|
24614
|
+
return { changed: false, text: text5, reason: "unsupported_shape" };
|
|
24615
|
+
}
|
|
24616
|
+
const took = ours ? [] : [{ member: STELLA_API_KEY_MEMBER, kind: "api_key" }];
|
|
24617
|
+
const sidecar = existing ? {
|
|
24618
|
+
...existing,
|
|
24619
|
+
written_sha256: sha256Hex(next),
|
|
24620
|
+
// A key the person put back after enrolling sets the quote again.
|
|
24621
|
+
...ours ? {} : { quote: at2.quote },
|
|
24622
|
+
taken: [
|
|
24623
|
+
...existing.taken,
|
|
24624
|
+
...took.filter(
|
|
24625
|
+
(t) => !existing.taken.some((e) => e.member === t.member)
|
|
24626
|
+
)
|
|
24627
|
+
]
|
|
24628
|
+
} : {
|
|
24629
|
+
schema: SIDECAR_SCHEMA2,
|
|
24630
|
+
harness: "stella",
|
|
24631
|
+
existed: true,
|
|
24632
|
+
written_sha256: sha256Hex(next),
|
|
24633
|
+
quote: at2.quote,
|
|
24634
|
+
taken: took
|
|
24635
|
+
};
|
|
24636
|
+
writeSidecar(backup, sidecar);
|
|
24637
|
+
writeFileAtomicPreserving(file, next);
|
|
24638
|
+
return { changed: true, text: next };
|
|
24639
|
+
}
|
|
24640
|
+
function restoreStella(options, released, sidecar) {
|
|
24641
|
+
const file = fileFor2("stella", options);
|
|
24642
|
+
const backup = sidecarFor2(file);
|
|
24643
|
+
const text5 = readTextIfExists(file);
|
|
24644
|
+
const dropSidecar = () => {
|
|
24645
|
+
if (existsSync7(backup)) unlinkSync6(backup);
|
|
24646
|
+
};
|
|
24647
|
+
const quote = sidecar?.quote ?? null;
|
|
24648
|
+
if (text5 === void 0) {
|
|
24649
|
+
if (released !== void 0) {
|
|
24650
|
+
const written2 = withStellaKey(file, void 0, released.secret, quote);
|
|
24651
|
+
if (written2 !== void 0) {
|
|
24652
|
+
writeFileAtomicPreserving(file, written2);
|
|
24653
|
+
dropSidecar();
|
|
24654
|
+
return { changed: true, text: written2, secretRestored: true };
|
|
24655
|
+
}
|
|
24656
|
+
}
|
|
24657
|
+
dropSidecar();
|
|
24658
|
+
return { changed: false, text: text5, secretRestored: false };
|
|
24659
|
+
}
|
|
24660
|
+
const at2 = stellaKeyIn(file, text5);
|
|
24661
|
+
if (at2.kind === "value" && !looksLikeRunToken(at2.value)) {
|
|
24662
|
+
dropSidecar();
|
|
24663
|
+
return {
|
|
24664
|
+
changed: false,
|
|
24665
|
+
text: text5,
|
|
24666
|
+
secretRestored: false,
|
|
24667
|
+
reason: "foreign_key_present"
|
|
24668
|
+
};
|
|
24669
|
+
}
|
|
24670
|
+
if (at2.kind === "unsupported") {
|
|
24671
|
+
dropSidecar();
|
|
24672
|
+
return {
|
|
24673
|
+
changed: false,
|
|
24674
|
+
text: text5,
|
|
24675
|
+
secretRestored: false,
|
|
24676
|
+
reason: "unsupported_shape"
|
|
24677
|
+
};
|
|
24678
|
+
}
|
|
24679
|
+
const next = released !== void 0 ? withStellaKey(
|
|
24680
|
+
file,
|
|
24681
|
+
text5,
|
|
24682
|
+
released.secret,
|
|
24683
|
+
quote ?? (at2.kind === "value" ? at2.quote : null)
|
|
24684
|
+
) : at2.kind === "value" ? withoutStellaKey(file, text5) : text5;
|
|
24685
|
+
if (next === void 0) {
|
|
24686
|
+
dropSidecar();
|
|
24687
|
+
return {
|
|
24688
|
+
changed: false,
|
|
24689
|
+
text: text5,
|
|
24690
|
+
secretRestored: false,
|
|
24691
|
+
reason: "unsupported_shape"
|
|
24692
|
+
};
|
|
24693
|
+
}
|
|
24694
|
+
if (next === text5) {
|
|
24695
|
+
dropSidecar();
|
|
24696
|
+
return { changed: false, text: text5, secretRestored: false };
|
|
24697
|
+
}
|
|
24698
|
+
writeFileAtomicPreserving(file, next);
|
|
24699
|
+
dropSidecar();
|
|
24700
|
+
return {
|
|
24701
|
+
changed: true,
|
|
24702
|
+
text: next,
|
|
24703
|
+
secretRestored: released !== void 0
|
|
24704
|
+
};
|
|
24705
|
+
}
|
|
24167
24706
|
function restoreClaude(options, released, sidecar) {
|
|
24168
24707
|
const file = fileFor2("claude-code", options);
|
|
24169
24708
|
const backup = sidecarFor2(file);
|
|
@@ -24291,14 +24830,14 @@ async function applyModelCredentials(options, internals = {}) {
|
|
|
24291
24830
|
outcomeOf(harness, options, "symlink"),
|
|
24292
24831
|
internals
|
|
24293
24832
|
);
|
|
24294
|
-
const outcome = harness === "claude-code" ? applyClaude(options, taken) : applyCodex(options, taken);
|
|
24833
|
+
const outcome = harness === "claude-code" ? applyClaude(options, taken) : harness === "stella" ? applyStella(options, taken) : applyCodex(options, taken);
|
|
24295
24834
|
return describe4(harness, options, outcome, internals);
|
|
24296
24835
|
});
|
|
24297
24836
|
return { harnesses, taken };
|
|
24298
24837
|
}
|
|
24299
24838
|
async function restoreModelCredentials(options, restore = {}, internals = {}) {
|
|
24300
24839
|
const harnesses = uniqueHarnesses(options.harnesses).map((harness) => {
|
|
24301
|
-
const released = restore.secrets?.[
|
|
24840
|
+
const released = restore.secrets?.[harness];
|
|
24302
24841
|
const { sidecar, unreadable } = sidecarOrWhy2(
|
|
24303
24842
|
sidecarFor2(fileFor2(harness, options)),
|
|
24304
24843
|
harness
|
|
@@ -24307,6 +24846,10 @@ async function restoreModelCredentials(options, restore = {}, internals = {}) {
|
|
|
24307
24846
|
options,
|
|
24308
24847
|
released,
|
|
24309
24848
|
sidecar?.harness === "claude-code" ? sidecar : void 0
|
|
24849
|
+
) : harness === "stella" ? restoreStella(
|
|
24850
|
+
options,
|
|
24851
|
+
released,
|
|
24852
|
+
sidecar?.harness === "stella" ? sidecar : void 0
|
|
24310
24853
|
) : restoreCodex(
|
|
24311
24854
|
options,
|
|
24312
24855
|
released,
|
|
@@ -24326,6 +24869,17 @@ async function peekModelCredentials(options) {
|
|
|
24326
24869
|
const file = fileFor2(harness, options);
|
|
24327
24870
|
const text5 = readTextIfExists(file);
|
|
24328
24871
|
if (text5 === void 0) continue;
|
|
24872
|
+
if (harness === "stella") {
|
|
24873
|
+
const at2 = stellaKeyIn(file, text5);
|
|
24874
|
+
if (at2.kind === "value" && !looksLikeRunToken(at2.value) && anthropicCredentialKind(at2.value, "api_key") === "api_key")
|
|
24875
|
+
taken.push({
|
|
24876
|
+
harness,
|
|
24877
|
+
provider: "anthropic",
|
|
24878
|
+
credential: { kind: "api_key", secret: at2.value },
|
|
24879
|
+
member: STELLA_API_KEY_MEMBER
|
|
24880
|
+
});
|
|
24881
|
+
continue;
|
|
24882
|
+
}
|
|
24329
24883
|
const document = parseJsonObject(text5, file);
|
|
24330
24884
|
if (harness === "claude-code") {
|
|
24331
24885
|
const env = envOf(document) ?? {};
|
|
@@ -24377,6 +24931,10 @@ function hasOrphanedModelCredential(harness, home, dirs = {}) {
|
|
|
24377
24931
|
const text5 = readTextIfExists(file);
|
|
24378
24932
|
if (text5 === void 0) return false;
|
|
24379
24933
|
try {
|
|
24934
|
+
if (harness === "stella") {
|
|
24935
|
+
const at2 = stellaKeyIn(file, text5);
|
|
24936
|
+
return at2.kind === "value" && looksLikeRunToken(at2.value);
|
|
24937
|
+
}
|
|
24380
24938
|
const document = parseJsonObject(text5, file);
|
|
24381
24939
|
if (harness === "claude-code") return isTachoHelper(document[HELPER_KEY]);
|
|
24382
24940
|
const key = codexTokenMember(document);
|
|
@@ -24390,14 +24948,14 @@ function staticTokenRenewWindowMs(host, now) {
|
|
|
24390
24948
|
if (!Number.isFinite(notAfter)) return STATIC_TOKEN_RENEW_WINDOW_MS;
|
|
24391
24949
|
return Math.min(STATIC_TOKEN_RENEW_WINDOW_MS, (notAfter - now) / 2);
|
|
24392
24950
|
}
|
|
24393
|
-
function staticTokenStillGood(token, host, keyPath2, now) {
|
|
24951
|
+
function staticTokenStillGood(token, host, keyPath2, now, provider = "openai") {
|
|
24394
24952
|
if (token === void 0 || !looksLikeRunToken(token)) return false;
|
|
24395
24953
|
const key = readRunTokenKey(keyPath2);
|
|
24396
24954
|
if (key === void 0) return false;
|
|
24397
24955
|
const verdict = verifyRunToken(token, {
|
|
24398
24956
|
key,
|
|
24399
24957
|
host: host.host_enrollment_id,
|
|
24400
|
-
provider
|
|
24958
|
+
provider,
|
|
24401
24959
|
now
|
|
24402
24960
|
});
|
|
24403
24961
|
return verdict.ok && verdict.claims.exp - now > staticTokenRenewWindowMs(host, now);
|
|
@@ -24434,7 +24992,19 @@ function readCodexApiKeyMember(home, dirs = {}) {
|
|
|
24434
24992
|
return void 0;
|
|
24435
24993
|
}
|
|
24436
24994
|
}
|
|
24437
|
-
|
|
24995
|
+
function readStaticTokenMember(harness, home, dirs = {}) {
|
|
24996
|
+
if (harness === "codex") return readCodexApiKeyMember(home, dirs);
|
|
24997
|
+
const file = fileFor2("stella", { ...dirs, home });
|
|
24998
|
+
try {
|
|
24999
|
+
const text5 = readTextIfExists(file);
|
|
25000
|
+
if (text5 === void 0) return void 0;
|
|
25001
|
+
const at2 = stellaKeyIn(file, text5);
|
|
25002
|
+
return at2.kind === "value" ? at2.value : void 0;
|
|
25003
|
+
} catch {
|
|
25004
|
+
return void 0;
|
|
25005
|
+
}
|
|
25006
|
+
}
|
|
25007
|
+
var HARNESS_PROVIDER, STATIC_TOKEN_HARNESSES, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CLAUDE_OAUTH_TOKEN, CLAUDE_MEMBERS, CODEX_KEY2, CODEX_TOKENS, CODEX_LAST_REFRESH, CHATGPT_REFRESH_HELD, HELPER_MARK, STELLA_API_KEY_MEMBER, STELLA_BLOCK_MARKER2, STATIC_TOKEN_RENEW_WINDOW_MS, CODEX_LAST_REFRESH_RESTATE_MS;
|
|
24438
25008
|
var init_model_credential = __esm({
|
|
24439
25009
|
"../../packages/tacho/src/host/model-credential.ts"() {
|
|
24440
25010
|
"use strict";
|
|
@@ -24443,9 +25013,13 @@ var init_model_credential = __esm({
|
|
|
24443
25013
|
init_wire();
|
|
24444
25014
|
init_credential_store();
|
|
24445
25015
|
init_model_base_url();
|
|
25016
|
+
init_model_base_url_toml();
|
|
24446
25017
|
init_paths();
|
|
24447
25018
|
init_run_token();
|
|
24448
25019
|
HARNESS_PROVIDER = BROKERABLE_HARNESS_PROVIDER;
|
|
25020
|
+
STATIC_TOKEN_HARNESSES = BROKERABLE_HARNESSES.filter(
|
|
25021
|
+
(harness) => harness !== "claude-code"
|
|
25022
|
+
);
|
|
24449
25023
|
SIDECAR_SCHEMA2 = "oxagen.model-credential.v1";
|
|
24450
25024
|
HELPER_KEY = "apiKeyHelper";
|
|
24451
25025
|
CLAUDE_API_KEY = "ANTHROPIC_API_KEY";
|
|
@@ -24461,6 +25035,8 @@ var init_model_credential = __esm({
|
|
|
24461
25035
|
CODEX_LAST_REFRESH = "last_refresh";
|
|
24462
25036
|
CHATGPT_REFRESH_HELD = "held-by-oxagen-gateway";
|
|
24463
25037
|
HELPER_MARK = /\bcredential issue --harness claude-code\s*$/;
|
|
25038
|
+
STELLA_API_KEY_MEMBER = "providers.anthropic.api_key";
|
|
25039
|
+
STELLA_BLOCK_MARKER2 = /^# >>> tacho enrollment /;
|
|
24464
25040
|
STATIC_TOKEN_RENEW_WINDOW_MS = 7 * 24 * 60 * 6e4;
|
|
24465
25041
|
CODEX_LAST_REFRESH_RESTATE_MS = 5 * 24 * 60 * 6e4;
|
|
24466
25042
|
}
|
|
@@ -24472,7 +25048,8 @@ import { dirname as dirname8 } from "node:path";
|
|
|
24472
25048
|
function harnessDirsOf(deps) {
|
|
24473
25049
|
return {
|
|
24474
25050
|
claudeConfigDir: dirname8(deps.paths.claudeSettings),
|
|
24475
|
-
codexHome: dirname8(deps.paths.codexHooks)
|
|
25051
|
+
codexHome: dirname8(deps.paths.codexHooks),
|
|
25052
|
+
stellaHome: dirname8(deps.paths.stellaToml)
|
|
24476
25053
|
};
|
|
24477
25054
|
}
|
|
24478
25055
|
function parseCredentialMode(value) {
|
|
@@ -24548,6 +25125,7 @@ async function credentialStatus(options, deps) {
|
|
|
24548
25125
|
].flatMap(
|
|
24549
25126
|
(agent) => (agent.credentialStore?.status() ?? []).map((c) => ({
|
|
24550
25127
|
provider: c.provider,
|
|
25128
|
+
harness: c.harness,
|
|
24551
25129
|
kind: c.kind,
|
|
24552
25130
|
source: c.source,
|
|
24553
25131
|
taken_at: c.taken_at,
|
|
@@ -24561,6 +25139,7 @@ async function credentialStatus(options, deps) {
|
|
|
24561
25139
|
const harnesses = [...new Set(enrolled)].filter(isCredentialHarness);
|
|
24562
25140
|
const state = deps.modelCredentials !== void 0 && harnesses.length > 0 ? (await deps.modelCredentials.read({
|
|
24563
25141
|
home: deps.home,
|
|
25142
|
+
stellaHome: dirname8(deps.paths.stellaToml),
|
|
24564
25143
|
harnesses,
|
|
24565
25144
|
helperCommand: deps.runtime.credentialHelperCommand
|
|
24566
25145
|
})).harnesses : [];
|
|
@@ -24575,7 +25154,7 @@ async function credentialStatus(options, deps) {
|
|
|
24575
25154
|
);
|
|
24576
25155
|
for (const entry2 of custody)
|
|
24577
25156
|
deps.out(
|
|
24578
|
-
`Custody ${entry2.provider}: ${entry2.kind} ${entry2.prefix} from ${entry2.source}, taken ${entry2.taken_at}`
|
|
25157
|
+
`Custody ${entry2.provider}${entry2.harness === defaultHarnessForProvider(entry2.provider) ? "" : ` (${TACHO_HARNESS_LABELS[entry2.harness]})`}: ${entry2.kind} ${entry2.prefix} from ${entry2.source}, taken ${entry2.taken_at}`
|
|
24579
25158
|
);
|
|
24580
25159
|
for (const entry2 of state) deps.out(` ${describeHarness(entry2)}`);
|
|
24581
25160
|
return report;
|
|
@@ -24585,6 +25164,22 @@ function describeHarness(entry2) {
|
|
|
24585
25164
|
if (entry2.brokered) {
|
|
24586
25165
|
return entry2.shadowedBy !== void 0 ? `${label2}: points at the gateway's run tokens, but ${entry2.shadowedBy.file} sets apiKeyHelper, and managed settings win, so it does not` : `${label2}: holds a run token; the gateway supplies the ${HARNESS_PROVIDER[entry2.harness]} credential`;
|
|
24587
25166
|
}
|
|
25167
|
+
if (entry2.harness === "stella") {
|
|
25168
|
+
switch (entry2.reason) {
|
|
25169
|
+
case "no_key":
|
|
25170
|
+
return `${label2}: ${entry2.file} holds no providers.anthropic.api_key, so Stella sends the key it reads from ANTHROPIC_API_KEY or its credentials file, and that key crosses the proxy as its own. Put the key in ${entry2.file} and run \`oxagen agent enroll\` to broker it`;
|
|
25171
|
+
case "subscription_login":
|
|
25172
|
+
return `${label2}: ${entry2.file} holds a Claude subscription token in providers.anthropic.api_key, and Stella is brokered only on an API key; it crosses the proxy as its own`;
|
|
25173
|
+
case "unsupported_shape":
|
|
25174
|
+
return `${label2}: ${entry2.file} defines providers.anthropic in a shape a line edit cannot change safely, so it was left alone; its own key crosses the proxy`;
|
|
25175
|
+
case "symlink":
|
|
25176
|
+
return `${label2}: ${entry2.file} is a symbolic link, so it was left alone; its own key crosses the proxy`;
|
|
25177
|
+
case "no_file":
|
|
25178
|
+
return `${label2}: no ${entry2.file} yet; its own key crosses the proxy`;
|
|
25179
|
+
default:
|
|
25180
|
+
return `${label2}: holds its own key, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
|
|
25181
|
+
}
|
|
25182
|
+
}
|
|
24588
25183
|
switch (entry2.reason) {
|
|
24589
25184
|
case "subscription_login":
|
|
24590
25185
|
return `${label2}: signed in with a ChatGPT login that crosses the proxy as its own (run \`oxagen credential login --harness codex\` to put it in the gateway's custody)`;
|
|
@@ -24598,6 +25193,16 @@ function describeHarness(entry2) {
|
|
|
24598
25193
|
return entry2.harness === "claude-code" ? `${label2}: holds its own credential, which crosses the proxy (run \`oxagen credential login\` to put your Claude subscription in the gateway's custody)` : `${label2}: holds its own credential, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
|
|
24599
25194
|
}
|
|
24600
25195
|
}
|
|
25196
|
+
function stellaEnvKeyWarning(entries2, env) {
|
|
25197
|
+
const stella = entries2.find((entry2) => entry2.harness === "stella");
|
|
25198
|
+
if (stella?.brokered !== true) return void 0;
|
|
25199
|
+
if ((env[STELLA_ANTHROPIC_KEY_ENV]?.trim() ?? "").length === 0)
|
|
25200
|
+
return void 0;
|
|
25201
|
+
return `${STELLA_ANTHROPIC_KEY_ENV} is set in this environment. Stella reads it ahead of the run token in ${stella.file}, so an interactive Stella sends that key and the gateway refuses it as foreign_credential. Unset ${STELLA_ANTHROPIC_KEY_ENV} in your shell profile; the gateway supplies Stella's key. Runs Oxagen dispatches are not affected`;
|
|
25202
|
+
}
|
|
25203
|
+
function fileSourceOf(harness) {
|
|
25204
|
+
return harness === "claude-code" ? "claude-code:settings.env" : harness === "stella" ? "stella:settings" : "codex:auth.json";
|
|
25205
|
+
}
|
|
24601
25206
|
async function brokerCredentials(host, harnesses, deps) {
|
|
24602
25207
|
const warnings = [];
|
|
24603
25208
|
const store = deps.credentialStore;
|
|
@@ -24607,21 +25212,26 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24607
25212
|
return { harnesses: [], taken: [], warnings };
|
|
24608
25213
|
const helperCommand = deps.runtime.credentialHelperCommand;
|
|
24609
25214
|
const offered = /* @__PURE__ */ new Map();
|
|
25215
|
+
const stellaHome = dirname8(deps.paths.stellaToml);
|
|
24610
25216
|
for (const taken of await contract.peek({
|
|
24611
25217
|
home: deps.home,
|
|
25218
|
+
stellaHome,
|
|
24612
25219
|
harnesses: targets,
|
|
24613
25220
|
helperCommand
|
|
24614
25221
|
})) {
|
|
24615
|
-
offered.set(taken.
|
|
25222
|
+
offered.set(taken.harness, {
|
|
25223
|
+
provider: taken.provider,
|
|
24616
25224
|
credential: taken.credential,
|
|
24617
|
-
source: taken.harness
|
|
25225
|
+
source: fileSourceOf(taken.harness)
|
|
24618
25226
|
});
|
|
24619
25227
|
}
|
|
24620
25228
|
for (const harness of targets) {
|
|
25229
|
+
if (harness === "stella") continue;
|
|
24621
25230
|
const provider = HARNESS_PROVIDER[harness];
|
|
24622
25231
|
const fromEnv = deps.env[brokerEnvVar(provider)];
|
|
24623
|
-
if (!offered.has(
|
|
24624
|
-
offered.set(
|
|
25232
|
+
if (!offered.has(harness) && typeof fromEnv === "string" && fromEnv.trim().length > 0)
|
|
25233
|
+
offered.set(harness, {
|
|
25234
|
+
provider,
|
|
24625
25235
|
credential: {
|
|
24626
25236
|
kind: provider === "anthropic" ? anthropicCredentialKind(fromEnv, "api_key") : "bearer",
|
|
24627
25237
|
secret: fromEnv.trim()
|
|
@@ -24629,8 +25239,9 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24629
25239
|
source: "enroll:env"
|
|
24630
25240
|
});
|
|
24631
25241
|
const oauth = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
|
|
24632
|
-
if (
|
|
24633
|
-
offered.set(
|
|
25242
|
+
if (harness === "claude-code" && !offered.has(harness) && oauth !== void 0 && isSubscriptionToken(oauth))
|
|
25243
|
+
offered.set(harness, {
|
|
25244
|
+
provider,
|
|
24634
25245
|
credential: { kind: "oauth", secret: oauth },
|
|
24635
25246
|
source: "enroll:env"
|
|
24636
25247
|
});
|
|
@@ -24639,52 +25250,70 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24639
25250
|
const staticTokens = {};
|
|
24640
25251
|
let state;
|
|
24641
25252
|
try {
|
|
24642
|
-
for (const [
|
|
24643
|
-
store.take(
|
|
24644
|
-
|
|
25253
|
+
for (const [harness, offer] of offered) {
|
|
25254
|
+
store.take(
|
|
25255
|
+
offer.provider,
|
|
25256
|
+
offer.credential,
|
|
25257
|
+
offer.source,
|
|
25258
|
+
deps.now(),
|
|
25259
|
+
harness
|
|
25260
|
+
);
|
|
25261
|
+
takenNow.push({ provider: offer.provider, harness });
|
|
24645
25262
|
}
|
|
24646
|
-
|
|
24647
|
-
|
|
24648
|
-
|
|
24649
|
-
|
|
24650
|
-
|
|
24651
|
-
|
|
24652
|
-
|
|
24653
|
-
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
25263
|
+
for (const harness of STATIC_TOKEN_HARNESSES) {
|
|
25264
|
+
if (!targets.includes(harness)) continue;
|
|
25265
|
+
const provider = HARNESS_PROVIDER[harness];
|
|
25266
|
+
if (!store.has(provider, harness)) continue;
|
|
25267
|
+
const current = readStaticTokenMember(
|
|
25268
|
+
harness,
|
|
25269
|
+
deps.home,
|
|
25270
|
+
harnessDirsOf(deps)
|
|
25271
|
+
);
|
|
25272
|
+
if (staticTokenStillGood(
|
|
25273
|
+
current,
|
|
25274
|
+
host,
|
|
25275
|
+
deps.paths.runTokenKey,
|
|
25276
|
+
deps.now(),
|
|
25277
|
+
provider
|
|
25278
|
+
))
|
|
25279
|
+
continue;
|
|
25280
|
+
const issued = await issueStatic(harness, deps);
|
|
25281
|
+
if (issued.token !== void 0) staticTokens[harness] = issued.token;
|
|
25282
|
+
else
|
|
25283
|
+
warnings.push(
|
|
25284
|
+
`${TACHO_HARNESS_LABELS[harness]} keeps its own credential: ${issued.detail}`
|
|
25285
|
+
);
|
|
24661
25286
|
}
|
|
24662
25287
|
state = await contract.apply({
|
|
24663
25288
|
home: deps.home,
|
|
25289
|
+
stellaHome,
|
|
24664
25290
|
harnesses: targets,
|
|
24665
25291
|
helperCommand,
|
|
24666
25292
|
staticTokens
|
|
24667
25293
|
});
|
|
24668
25294
|
} catch (error) {
|
|
24669
|
-
for (const provider of takenNow)
|
|
25295
|
+
for (const { provider, harness } of takenNow)
|
|
25296
|
+
store.release(provider, harness);
|
|
24670
25297
|
throw error;
|
|
24671
25298
|
}
|
|
24672
25299
|
for (const taken of state.taken) {
|
|
24673
|
-
if (offered.has(taken.
|
|
25300
|
+
if (offered.has(taken.harness)) continue;
|
|
24674
25301
|
store.take(
|
|
24675
25302
|
taken.provider,
|
|
24676
25303
|
taken.credential,
|
|
24677
|
-
taken.harness
|
|
24678
|
-
deps.now()
|
|
25304
|
+
fileSourceOf(taken.harness),
|
|
25305
|
+
deps.now(),
|
|
25306
|
+
taken.harness
|
|
24679
25307
|
);
|
|
24680
|
-
takenNow.push(taken.provider);
|
|
25308
|
+
takenNow.push({ provider: taken.provider, harness: taken.harness });
|
|
24681
25309
|
}
|
|
24682
25310
|
for (const entry2 of state.harnesses) {
|
|
24683
|
-
const
|
|
25311
|
+
const harness = entry2.harness;
|
|
25312
|
+
const provider = HARNESS_PROVIDER[harness];
|
|
24684
25313
|
if (entry2.brokered) {
|
|
24685
|
-
if (!store.has(provider)) {
|
|
25314
|
+
if (!store.has(provider, harness)) {
|
|
24686
25315
|
await contract.restore(
|
|
24687
|
-
{ home: deps.home, harnesses: [
|
|
25316
|
+
{ home: deps.home, stellaHome, harnesses: [harness], helperCommand },
|
|
24688
25317
|
{}
|
|
24689
25318
|
);
|
|
24690
25319
|
const index = state.harnesses.indexOf(entry2);
|
|
@@ -24695,19 +25324,19 @@ async function brokerCredentials(host, harnesses, deps) {
|
|
|
24695
25324
|
reason: "no_token"
|
|
24696
25325
|
};
|
|
24697
25326
|
warnings.push(
|
|
24698
|
-
|
|
25327
|
+
harness === "claude-code" ? `${TACHO_HARNESS_LABELS[harness]} keeps its own login: the gateway holds no Anthropic credential yet. Run \`oxagen credential login\` to sign in with your Claude subscription, or put a key in ${entry2.file} and enroll again` : harness === "stella" ? `${TACHO_HARNESS_LABELS[harness]} keeps its own key: the gateway holds no Anthropic key of Stella's. Put the key in providers.anthropic.api_key in ${entry2.file} and enroll again` : `${TACHO_HARNESS_LABELS[harness]} keeps its own login: no ${provider} key was found in ${entry2.file} or ${brokerEnvVar(provider)}, so there is nothing for the gateway to take into custody`
|
|
24699
25328
|
);
|
|
24700
25329
|
}
|
|
24701
25330
|
continue;
|
|
24702
25331
|
}
|
|
24703
|
-
|
|
24704
|
-
|
|
24705
|
-
const
|
|
24706
|
-
|
|
24707
|
-
const offer = offered.get(
|
|
25332
|
+
const at2 = takenNow.findIndex((t) => t.harness === harness);
|
|
25333
|
+
if (at2 >= 0 && store.has(provider, harness)) {
|
|
25334
|
+
const released = store.release(provider, harness);
|
|
25335
|
+
takenNow.splice(at2, 1);
|
|
25336
|
+
const offer = offered.get(harness);
|
|
24708
25337
|
if (offer?.source === "enroll:env" || released === void 0) continue;
|
|
24709
25338
|
warnings.push(
|
|
24710
|
-
`${TACHO_HARNESS_LABELS[
|
|
25339
|
+
`${TACHO_HARNESS_LABELS[harness]} keeps its own credential (${entry2.reason ?? "not brokered"}), so its ${provider} key was not taken into custody`
|
|
24711
25340
|
);
|
|
24712
25341
|
}
|
|
24713
25342
|
}
|
|
@@ -24754,10 +25383,10 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
|
|
|
24754
25383
|
let fromLogin = false;
|
|
24755
25384
|
try {
|
|
24756
25385
|
fromLogin = store?.status().some(
|
|
24757
|
-
(c) => c.provider === provider && c.source === "claude:setup-token"
|
|
25386
|
+
(c) => c.provider === provider && c.harness === harness && c.source === "claude:setup-token"
|
|
24758
25387
|
) === true;
|
|
24759
|
-
if (fromLogin) store?.release(provider);
|
|
24760
|
-
else released = store?.read(provider);
|
|
25388
|
+
if (fromLogin) store?.release(provider, harness);
|
|
25389
|
+
else released = store?.read(provider, harness);
|
|
24761
25390
|
} catch (error) {
|
|
24762
25391
|
unreadable = error instanceof Error ? error.message : String(error);
|
|
24763
25392
|
}
|
|
@@ -24774,18 +25403,23 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
|
|
|
24774
25403
|
);
|
|
24775
25404
|
}
|
|
24776
25405
|
const state = await contract.restore(
|
|
24777
|
-
{
|
|
24778
|
-
|
|
25406
|
+
{
|
|
25407
|
+
home: deps.home,
|
|
25408
|
+
stellaHome: dirname8(deps.paths.stellaToml),
|
|
25409
|
+
harnesses: [harness],
|
|
25410
|
+
helperCommand
|
|
25411
|
+
},
|
|
25412
|
+
released !== void 0 ? { secrets: { [harness]: released } } : {}
|
|
24779
25413
|
);
|
|
24780
25414
|
for (const entry2 of state.harnesses) {
|
|
24781
25415
|
if (entry2.changed) restored.push(entry2.file);
|
|
24782
25416
|
if (released === void 0) continue;
|
|
24783
25417
|
if (entry2.secretRestored === true) {
|
|
24784
|
-
store?.release(provider);
|
|
25418
|
+
store?.release(provider, harness);
|
|
24785
25419
|
continue;
|
|
24786
25420
|
}
|
|
24787
25421
|
if (mode === "unenroll") {
|
|
24788
|
-
store?.release(provider);
|
|
25422
|
+
store?.release(provider, harness);
|
|
24789
25423
|
warnings.push(
|
|
24790
25424
|
`${entry2.file} already holds a ${provider} credential of its own, so the older one the gateway held was not written back and is discarded with the store`
|
|
24791
25425
|
);
|
|
@@ -24989,7 +25623,7 @@ async function credentialLogin(options, deps) {
|
|
|
24989
25623
|
detail: outcome.harnesses.some((h) => h.brokered) ? "Claude Code now holds a run token, and the gateway sends your subscription with every call" : "The subscription is in custody. Run `oxagen agent enroll` to route Claude Code through the gateway"
|
|
24990
25624
|
};
|
|
24991
25625
|
}
|
|
24992
|
-
var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, CLAUDE_OAUTH_TOKEN_ENV;
|
|
25626
|
+
var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, STELLA_ANTHROPIC_KEY_ENV, CLAUDE_OAUTH_TOKEN_ENV;
|
|
24993
25627
|
var init_credential = __esm({
|
|
24994
25628
|
"../../packages/tacho/src/cli/credential.ts"() {
|
|
24995
25629
|
"use strict";
|
|
@@ -25007,6 +25641,7 @@ var init_credential = __esm({
|
|
|
25007
25641
|
"brokered",
|
|
25008
25642
|
"passthrough"
|
|
25009
25643
|
];
|
|
25644
|
+
STELLA_ANTHROPIC_KEY_ENV = "ANTHROPIC_API_KEY";
|
|
25010
25645
|
CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
|
|
25011
25646
|
}
|
|
25012
25647
|
});
|
|
@@ -25777,7 +26412,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
|
|
|
25777
26412
|
import { dirname as dirname9, resolve as resolve3 } from "node:path";
|
|
25778
26413
|
import { fileURLToPath } from "node:url";
|
|
25779
26414
|
function readVersion() {
|
|
25780
|
-
if (true) return "2.1.
|
|
26415
|
+
if (true) return "2.1.5-22";
|
|
25781
26416
|
try {
|
|
25782
26417
|
const here = dirname9(fileURLToPath(import.meta.url));
|
|
25783
26418
|
const pkg = JSON.parse(
|
|
@@ -26168,7 +26803,8 @@ function defaultCliDeps(overrides = {}, managed = {}) {
|
|
|
26168
26803
|
const harnessFiles = new HarnessFiles(paths.dir);
|
|
26169
26804
|
const harnessDirs = {
|
|
26170
26805
|
claudeConfigDir: dirname10(paths.claudeSettings),
|
|
26171
|
-
codexHome: dirname10(paths.codexHooks)
|
|
26806
|
+
codexHome: dirname10(paths.codexHooks),
|
|
26807
|
+
stellaHome: dirname10(paths.stellaToml)
|
|
26172
26808
|
};
|
|
26173
26809
|
const daemonGet = async (path2, options) => {
|
|
26174
26810
|
const host = readHostFile(paths.hostFile);
|
|
@@ -30163,6 +30799,7 @@ async function agentStatus(deps) {
|
|
|
30163
30799
|
return void 0;
|
|
30164
30800
|
const state = await deps.modelCredentials.read({
|
|
30165
30801
|
home: deps.home,
|
|
30802
|
+
stellaHome: dirname11(deps.paths.stellaToml),
|
|
30166
30803
|
harnesses: routedHarnesses,
|
|
30167
30804
|
helperCommand: deps.runtime.credentialHelperCommand
|
|
30168
30805
|
});
|
|
@@ -30224,6 +30861,7 @@ async function agentStatus(deps) {
|
|
|
30224
30861
|
try {
|
|
30225
30862
|
credentialCustody = deps.credentialStore.status().map((c) => ({
|
|
30226
30863
|
provider: c.provider,
|
|
30864
|
+
harness: c.harness,
|
|
30227
30865
|
kind: c.kind,
|
|
30228
30866
|
source: c.source,
|
|
30229
30867
|
taken_at: c.taken_at
|
|
@@ -30232,6 +30870,9 @@ async function agentStatus(deps) {
|
|
|
30232
30870
|
problems.push(error instanceof Error ? error.message : String(error));
|
|
30233
30871
|
}
|
|
30234
30872
|
}
|
|
30873
|
+
const credentialWarnings = [];
|
|
30874
|
+
const envKey = stellaEnvKeyWarning(modelCredentials ?? [], deps.env);
|
|
30875
|
+
if (envKey !== void 0) credentialWarnings.push(envKey);
|
|
30235
30876
|
const tiers = observedTiers(daemon);
|
|
30236
30877
|
const retired = host.revoked_at !== null;
|
|
30237
30878
|
const shipping = retired ? void 0 : shippingHealth(daemon, walStats, deps.now());
|
|
@@ -30242,6 +30883,7 @@ async function agentStatus(deps) {
|
|
|
30242
30883
|
...gateway !== void 0 ? { gateway } : {},
|
|
30243
30884
|
...modelBaseUrls !== void 0 ? { modelBaseUrls } : {},
|
|
30244
30885
|
...modelCredentials !== void 0 ? { modelCredentials } : {},
|
|
30886
|
+
...credentialWarnings.length > 0 ? { credentialWarnings } : {},
|
|
30245
30887
|
...credentialCustody !== void 0 ? { credentialCustody } : {},
|
|
30246
30888
|
...Object.keys(tiers).length > 0 ? { tiers } : {},
|
|
30247
30889
|
host: {
|
|
@@ -30391,6 +31033,8 @@ function printStatus(report, deps) {
|
|
|
30391
31033
|
}
|
|
30392
31034
|
for (const entry2 of modelCredentials ?? [])
|
|
30393
31035
|
deps.out(`Credential ${describeHarness(entry2)}`);
|
|
31036
|
+
for (const warning of report.credentialWarnings ?? [])
|
|
31037
|
+
deps.out(`Warning ${warning}`);
|
|
30394
31038
|
for (const harness of h.harnesses)
|
|
30395
31039
|
deps.out(
|
|
30396
31040
|
`Tier ${harness}: ${tiers[harness] ?? "no run since the collector started"}`
|
|
@@ -32248,7 +32892,6 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
|
|
|
32248
32892
|
if (unhooked.includes(harness)) return false;
|
|
32249
32893
|
const file = modelBaseUrlFile(harness, {
|
|
32250
32894
|
home: deps.home,
|
|
32251
|
-
stellaHome,
|
|
32252
32895
|
...harnessDirsOf(deps)
|
|
32253
32896
|
});
|
|
32254
32897
|
let linked2 = false;
|
|
@@ -32308,9 +32951,9 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
|
|
|
32308
32951
|
const outcome = await brokerCredentials(host, reaching, deps);
|
|
32309
32952
|
for (const entry2 of outcome.harnesses)
|
|
32310
32953
|
deps.out(` ${describeHarness(entry2)}`);
|
|
32311
|
-
for (const provider of outcome.taken)
|
|
32954
|
+
for (const { provider, harness } of outcome.taken)
|
|
32312
32955
|
deps.out(
|
|
32313
|
-
` ${provider} credential taken into the gateway's custody (${deps.paths.credentials})`
|
|
32956
|
+
` ${harness === "stella" ? "Stella's " : ""}${provider} credential taken into the gateway's custody (${deps.paths.credentials})`
|
|
32314
32957
|
);
|
|
32315
32958
|
warnings.push(...outcome.warnings);
|
|
32316
32959
|
const unrouted = writable2.filter((h) => !reaching.includes(h));
|
|
@@ -34515,14 +35158,20 @@ function headlessArgs(harness, prompt) {
|
|
|
34515
35158
|
return ["run", prompt];
|
|
34516
35159
|
}
|
|
34517
35160
|
}
|
|
34518
|
-
function headlessEnv(harness) {
|
|
34519
|
-
|
|
35161
|
+
function headlessEnv(harness, stellaRunToken) {
|
|
35162
|
+
if (harness !== "stella") return {};
|
|
35163
|
+
return {
|
|
35164
|
+
STELLA_TRUST_PROJECT: "1",
|
|
35165
|
+
...stellaRunToken !== void 0 ? { ANTHROPIC_API_KEY: stellaRunToken } : {}
|
|
35166
|
+
};
|
|
34520
35167
|
}
|
|
35168
|
+
var DISPATCHED_STELLA_TOKEN_TTL_MS;
|
|
34521
35169
|
var init_work_order_start = __esm({
|
|
34522
35170
|
"../../packages/tacho/src/host/work-order-start.ts"() {
|
|
34523
35171
|
"use strict";
|
|
34524
35172
|
init_control_client();
|
|
34525
35173
|
init_wire();
|
|
35174
|
+
DISPATCHED_STELLA_TOKEN_TTL_MS = 24 * 60 * 6e4;
|
|
34526
35175
|
}
|
|
34527
35176
|
});
|
|
34528
35177
|
|
|
@@ -34899,6 +35548,24 @@ async function workClaim(criterionId, options, deps) {
|
|
|
34899
35548
|
);
|
|
34900
35549
|
return 2;
|
|
34901
35550
|
}
|
|
35551
|
+
if (options.workOrder === void 0 !== (options.item === void 0)) {
|
|
35552
|
+
deps.err(
|
|
35553
|
+
"Give --work-order and --item together, as the first prompt lists them for each item of a batch."
|
|
35554
|
+
);
|
|
35555
|
+
return 2;
|
|
35556
|
+
}
|
|
35557
|
+
if (options.workOrder !== void 0 && workOrderIdOf(options.workOrder) === void 0) {
|
|
35558
|
+
deps.err(
|
|
35559
|
+
`${JSON.stringify(options.workOrder)} is not a work order id. A work order id starts with wo_, as the first prompt lists it.`
|
|
35560
|
+
);
|
|
35561
|
+
return 2;
|
|
35562
|
+
}
|
|
35563
|
+
if (options.item !== void 0 && !/^wi_[0-9A-Za-z]+$/.test(options.item)) {
|
|
35564
|
+
deps.err(
|
|
35565
|
+
`${JSON.stringify(options.item)} is not a work item id. A work item id starts with wi_, as the first prompt lists it.`
|
|
35566
|
+
);
|
|
35567
|
+
return 2;
|
|
35568
|
+
}
|
|
34902
35569
|
const workOrderId = workOrderIdOf(deps.env[WORK_ORDER_ENV]);
|
|
34903
35570
|
if (workOrderId === void 0) {
|
|
34904
35571
|
deps.err(
|
|
@@ -34925,7 +35592,9 @@ async function workClaim(criterionId, options, deps) {
|
|
|
34925
35592
|
);
|
|
34926
35593
|
return 1;
|
|
34927
35594
|
}
|
|
34928
|
-
const { agent
|
|
35595
|
+
const { agent } = found;
|
|
35596
|
+
const item = options.item ?? found.item;
|
|
35597
|
+
const claimOrder = options.workOrder ?? workOrderId;
|
|
34929
35598
|
if (item === void 0) {
|
|
34930
35599
|
deps.err(
|
|
34931
35600
|
`The run of ${workOrderId} on this machine does not record its work item, because an older oxagen started it. Claims work in the next run that \`oxagen work start\` starts.`
|
|
@@ -34944,7 +35613,7 @@ async function workClaim(criterionId, options, deps) {
|
|
|
34944
35613
|
try {
|
|
34945
35614
|
answer = await client.claimWorkCriterion({
|
|
34946
35615
|
item_id: item,
|
|
34947
|
-
work_order_id:
|
|
35616
|
+
work_order_id: claimOrder,
|
|
34948
35617
|
criterion_id: criterionId,
|
|
34949
35618
|
head_sha: head2,
|
|
34950
35619
|
text: text5
|
|
@@ -34962,7 +35631,7 @@ async function workClaim(criterionId, options, deps) {
|
|
|
34962
35631
|
);
|
|
34963
35632
|
return 1;
|
|
34964
35633
|
}
|
|
34965
|
-
deps.out(claimAnswerMessage(answer, criterionId,
|
|
35634
|
+
deps.out(claimAnswerMessage(answer, criterionId, claimOrder, head2));
|
|
34966
35635
|
return 0;
|
|
34967
35636
|
}
|
|
34968
35637
|
var NOT_ENROLLED;
|
|
@@ -34989,6 +35658,7 @@ __export(cli_exports, {
|
|
|
34989
35658
|
CURSOR_COVERAGE_NOTE: () => CURSOR_COVERAGE_NOTE,
|
|
34990
35659
|
MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
|
|
34991
35660
|
SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
|
|
35661
|
+
STELLA_ANTHROPIC_KEY_ENV: () => STELLA_ANTHROPIC_KEY_ENV,
|
|
34992
35662
|
acquireSubscriptionToken: () => acquireSubscriptionToken,
|
|
34993
35663
|
addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
|
|
34994
35664
|
agentNameFromCommand: () => agentNameFromCommand,
|
|
@@ -35052,6 +35722,7 @@ __export(cli_exports, {
|
|
|
35052
35722
|
spawnAgent: () => spawnAgent,
|
|
35053
35723
|
stableExecutablePath: () => stableExecutablePath,
|
|
35054
35724
|
status: () => status,
|
|
35725
|
+
stellaEnvKeyWarning: () => stellaEnvKeyWarning,
|
|
35055
35726
|
stopGateway: () => stopGateway,
|
|
35056
35727
|
streamDaemon: () => streamDaemon,
|
|
35057
35728
|
stripEnrollmentHooks: () => stripEnrollmentHooks,
|
|
@@ -40627,6 +41298,69 @@ var init_runner = __esm({
|
|
|
40627
41298
|
}
|
|
40628
41299
|
});
|
|
40629
41300
|
|
|
41301
|
+
// ../../packages/tacho/src/collector/static-token-renewal.ts
|
|
41302
|
+
async function renewStaticToken(harness, deps) {
|
|
41303
|
+
const provider = HARNESS_PROVIDER[harness];
|
|
41304
|
+
const label2 = TACHO_HARNESS_LABELS[harness];
|
|
41305
|
+
if (deps.host.host_status !== "active" || !deps.inCustody(provider, harness))
|
|
41306
|
+
return "skipped";
|
|
41307
|
+
const dirs = deps.stellaHome !== void 0 ? { stellaHome: deps.stellaHome } : {};
|
|
41308
|
+
let brokered = false;
|
|
41309
|
+
try {
|
|
41310
|
+
brokered = (await readModelCredentialState({
|
|
41311
|
+
home: deps.home,
|
|
41312
|
+
...dirs,
|
|
41313
|
+
harnesses: [harness]
|
|
41314
|
+
})).harnesses[0]?.brokered === true;
|
|
41315
|
+
} catch (error) {
|
|
41316
|
+
deps.log(
|
|
41317
|
+
`static token renewal: cannot read ${label2}'s credential file: ${error instanceof Error ? error.message : String(error)}`
|
|
41318
|
+
);
|
|
41319
|
+
return "failed";
|
|
41320
|
+
}
|
|
41321
|
+
if (!brokered) return "skipped";
|
|
41322
|
+
const current = readStaticTokenMember(harness, deps.home, dirs);
|
|
41323
|
+
if (staticTokenStillGood(
|
|
41324
|
+
current,
|
|
41325
|
+
deps.host,
|
|
41326
|
+
deps.runTokenKeyPath,
|
|
41327
|
+
deps.now(),
|
|
41328
|
+
provider
|
|
41329
|
+
))
|
|
41330
|
+
return "still_good";
|
|
41331
|
+
const issued = deps.issue?.({ harness, placement: "static" });
|
|
41332
|
+
if (issued === void 0 || issued.status !== 200) {
|
|
41333
|
+
deps.log(
|
|
41334
|
+
`static token renewal: not issued for ${label2} (${issued?.body.error ?? "no issuer"})`
|
|
41335
|
+
);
|
|
41336
|
+
return "not_issued";
|
|
41337
|
+
}
|
|
41338
|
+
try {
|
|
41339
|
+
await applyModelCredentials({
|
|
41340
|
+
home: deps.home,
|
|
41341
|
+
...dirs,
|
|
41342
|
+
harnesses: [harness],
|
|
41343
|
+
staticTokens: { [harness]: issued.body.token }
|
|
41344
|
+
});
|
|
41345
|
+
deps.log(
|
|
41346
|
+
`renewed ${label2}'s static run token ${issued.body.token_id}, expires ${issued.body.expires_at}`
|
|
41347
|
+
);
|
|
41348
|
+
return "renewed";
|
|
41349
|
+
} catch (error) {
|
|
41350
|
+
deps.log(
|
|
41351
|
+
`static token renewal: could not write ${label2}'s credential file: ${error instanceof Error ? error.message : String(error)}`
|
|
41352
|
+
);
|
|
41353
|
+
return "failed";
|
|
41354
|
+
}
|
|
41355
|
+
}
|
|
41356
|
+
var init_static_token_renewal = __esm({
|
|
41357
|
+
"../../packages/tacho/src/collector/static-token-renewal.ts"() {
|
|
41358
|
+
"use strict";
|
|
41359
|
+
init_model_credential();
|
|
41360
|
+
init_wire();
|
|
41361
|
+
}
|
|
41362
|
+
});
|
|
41363
|
+
|
|
40630
41364
|
// ../../packages/tacho/src/host/chatgpt-refresh.ts
|
|
40631
41365
|
function jwtExpiryMs(token) {
|
|
40632
41366
|
const payload = token.split(".")[1];
|
|
@@ -41532,6 +42266,20 @@ function createWorkOrderAutostart(deps) {
|
|
|
41532
42266
|
}
|
|
41533
42267
|
return searchPath;
|
|
41534
42268
|
};
|
|
42269
|
+
const stellaRunToken = (id2) => {
|
|
42270
|
+
const answer = deps.issueRunToken?.({
|
|
42271
|
+
harness: "stella",
|
|
42272
|
+
placement: "static",
|
|
42273
|
+
ttl_ms: DISPATCHED_STELLA_TOKEN_TTL_MS
|
|
42274
|
+
});
|
|
42275
|
+
if (answer === void 0) return void 0;
|
|
42276
|
+
if (answer.status === 200) return answer.body.token;
|
|
42277
|
+
if (answer.body.code !== "credential_unavailable")
|
|
42278
|
+
log(
|
|
42279
|
+
`work order ${id2}: no run token for Stella (${answer.body.error}); it starts on its own key`
|
|
42280
|
+
);
|
|
42281
|
+
return void 0;
|
|
42282
|
+
};
|
|
41535
42283
|
const lookup = deps.findCheckout ?? ((repository) => findCheckout(paths, repository, deps.host()));
|
|
41536
42284
|
const retry = /* @__PURE__ */ new Map();
|
|
41537
42285
|
const cleanups = /* @__PURE__ */ new Set();
|
|
@@ -41778,7 +42526,10 @@ function createWorkOrderAutostart(deps) {
|
|
|
41778
42526
|
env: {
|
|
41779
42527
|
...deps.env,
|
|
41780
42528
|
PATH: pathForHarness(),
|
|
41781
|
-
...headlessEnv(
|
|
42529
|
+
...headlessEnv(
|
|
42530
|
+
harness,
|
|
42531
|
+
harness === "stella" ? stellaRunToken(id2) : void 0
|
|
42532
|
+
),
|
|
41782
42533
|
[WORK_ORDER_ENV]: id2
|
|
41783
42534
|
},
|
|
41784
42535
|
logPath: workspace.log
|
|
@@ -46001,10 +46752,10 @@ function createGithubProxy(deps) {
|
|
|
46001
46752
|
error: "GitHub custody is not configured for this repository and harness"
|
|
46002
46753
|
}
|
|
46003
46754
|
};
|
|
46004
|
-
const
|
|
46755
|
+
const matches2 = deps.registry.live().filter(
|
|
46005
46756
|
(s2) => !s2.pendingTerminal && s2.cwd && resolve13(s2.cwd) === resolve13(input.cwd) && deps.registry.agentOf(s2).harness === input.harness
|
|
46006
46757
|
);
|
|
46007
|
-
if (
|
|
46758
|
+
if (matches2.length !== 1)
|
|
46008
46759
|
return {
|
|
46009
46760
|
status: 409,
|
|
46010
46761
|
body: {
|
|
@@ -46021,7 +46772,7 @@ function createGithubProxy(deps) {
|
|
|
46021
46772
|
return grant(
|
|
46022
46773
|
{
|
|
46023
46774
|
repository: input.repository,
|
|
46024
|
-
session:
|
|
46775
|
+
session: matches2[0].recorder.sessionUuid
|
|
46025
46776
|
},
|
|
46026
46777
|
host
|
|
46027
46778
|
);
|
|
@@ -50028,11 +50779,11 @@ function providerForHarness(harness) {
|
|
|
50028
50779
|
function issueRunToken(input, deps) {
|
|
50029
50780
|
const harness = typeof input.harness === "string" ? input.harness : "";
|
|
50030
50781
|
const provider = providerForHarness(harness);
|
|
50031
|
-
if (provider === void 0)
|
|
50782
|
+
if (provider === void 0 || !isBrokerableHarness(harness))
|
|
50032
50783
|
return {
|
|
50033
50784
|
status: 400,
|
|
50034
50785
|
body: {
|
|
50035
|
-
error:
|
|
50786
|
+
error: `run tokens are issued for ${BROKERABLE_HARNESSES.join(", ")}, the harnesses whose model calls the gateway routes`,
|
|
50036
50787
|
code: "harness_not_brokered"
|
|
50037
50788
|
}
|
|
50038
50789
|
};
|
|
@@ -50057,7 +50808,7 @@ function issueRunToken(input, deps) {
|
|
|
50057
50808
|
};
|
|
50058
50809
|
let held;
|
|
50059
50810
|
try {
|
|
50060
|
-
held = deps.store.read(provider);
|
|
50811
|
+
held = deps.store.read(provider, harness);
|
|
50061
50812
|
} catch (error) {
|
|
50062
50813
|
deps.log(
|
|
50063
50814
|
`credential issuer: cannot read custody: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -50074,7 +50825,7 @@ function issueRunToken(input, deps) {
|
|
|
50074
50825
|
return {
|
|
50075
50826
|
status: 403,
|
|
50076
50827
|
body: {
|
|
50077
|
-
error: `the gateway holds no ${provider} credential in custody, so a run token would buy nothing; run \`oxagen agent enroll\` again`,
|
|
50828
|
+
error: `the gateway holds no ${provider} credential in custody${harness === "stella" ? " for Stella" : ""}, so a run token would buy nothing; run \`oxagen agent enroll\` again`,
|
|
50078
50829
|
code: "credential_unavailable"
|
|
50079
50830
|
}
|
|
50080
50831
|
};
|
|
@@ -51503,6 +52254,9 @@ function createModelProxy(deps) {
|
|
|
51503
52254
|
function harnessFor(provider) {
|
|
51504
52255
|
return defaultHarnessForProvider(provider);
|
|
51505
52256
|
}
|
|
52257
|
+
function custodyHarnessFor(route) {
|
|
52258
|
+
return route.harness !== void 0 && isBrokerableHarness(route.harness) ? route.harness : defaultHarnessForProvider(route.provider);
|
|
52259
|
+
}
|
|
51506
52260
|
function correlate(req, route, body2) {
|
|
51507
52261
|
const harness = route.harness ?? harnessFor(route.provider);
|
|
51508
52262
|
const explicit = header(req, TACHO_MODEL_SESSION_HEADER);
|
|
@@ -51622,7 +52376,8 @@ function createModelProxy(deps) {
|
|
|
51622
52376
|
function resolveCredential(req, route) {
|
|
51623
52377
|
const broker = deps.credentials;
|
|
51624
52378
|
const presented = presentedCredential(req);
|
|
51625
|
-
const
|
|
52379
|
+
const owner = custodyHarnessFor(route);
|
|
52380
|
+
const brokered = broker !== void 0 && broker.brokered(route.provider, owner) && route.upstream === "chatgpt" === (broker.kind?.(route.provider, owner) === "chatgpt");
|
|
51626
52381
|
if (broker === void 0 || !brokered) {
|
|
51627
52382
|
if (presented !== void 0 && looksLikeRunToken(presented.value)) {
|
|
51628
52383
|
const claims = peekRunTokenClaims(presented.value);
|
|
@@ -51666,7 +52421,17 @@ function createModelProxy(deps) {
|
|
|
51666
52421
|
}
|
|
51667
52422
|
};
|
|
51668
52423
|
}
|
|
51669
|
-
|
|
52424
|
+
if ((owner === "stella" || verdict.claims.harness === "stella") && verdict.claims.harness !== owner) {
|
|
52425
|
+
return {
|
|
52426
|
+
basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
|
|
52427
|
+
claims: verdict.claims,
|
|
52428
|
+
refusal: {
|
|
52429
|
+
code: "run_token_mismatch",
|
|
52430
|
+
message: CREDENTIAL_MESSAGES.run_token_mismatch
|
|
52431
|
+
}
|
|
52432
|
+
};
|
|
52433
|
+
}
|
|
52434
|
+
const held2 = broker.custody(route.provider, owner);
|
|
51670
52435
|
if (held2 === void 0) {
|
|
51671
52436
|
return {
|
|
51672
52437
|
basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
|
|
@@ -53032,7 +53797,7 @@ var init_model_proxy = __esm({
|
|
|
53032
53797
|
run_token_expired: "The run token this call carried has expired. The harness fetches a new one from the Oxagen gateway on its next attempt.",
|
|
53033
53798
|
run_token_mismatch: "The run token this call carried was issued for another host or another model provider. Run `oxagen agent enroll` again on this machine so the harness fetches run tokens from this host's gateway.",
|
|
53034
53799
|
run_token_required: "This machine brokers model credentials through the Oxagen gateway, and this call carried none. Run `oxagen agent enroll` again to point the harness at the gateway's run tokens.",
|
|
53035
|
-
foreign_credential: "This machine brokers model credentials through the Oxagen gateway, and this call brought its own. Unset the provider's API key (ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY) in the shell and in the env block of any .claude/settings.json or .claude/settings.local.json the harness reads; the gateway supplies the credential.",
|
|
53800
|
+
foreign_credential: "This machine brokers model credentials through the Oxagen gateway, and this call brought its own. Unset the provider's API key (ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY) in the shell and in the env block of any .claude/settings.json or .claude/settings.local.json the harness reads; the gateway supplies the credential. Stella reads ANTHROPIC_API_KEY from the shell ahead of its stella.toml, so unset it there too.",
|
|
53036
53801
|
credential_unavailable: "The run token is valid, but the Oxagen gateway holds no credential for this model provider. Run `oxagen agent enroll` again, or `oxagen credential status` to see what is in custody."
|
|
53037
53802
|
};
|
|
53038
53803
|
}
|
|
@@ -54720,10 +55485,11 @@ function cloudGatewayCredentialStore(disk, runToken) {
|
|
|
54720
55485
|
kind: provider === "anthropic" ? "api_key" : "bearer",
|
|
54721
55486
|
secret: runToken
|
|
54722
55487
|
});
|
|
55488
|
+
const ours = (provider, harness) => harness === void 0 || harness === defaultHarnessForProvider(provider);
|
|
54723
55489
|
return {
|
|
54724
55490
|
...disk,
|
|
54725
|
-
has: () =>
|
|
54726
|
-
read: (provider) => held(provider),
|
|
55491
|
+
has: (provider, harness) => ours(provider, harness),
|
|
55492
|
+
read: (provider, harness) => ours(provider, harness) ? held(provider) : void 0,
|
|
54727
55493
|
take: () => {
|
|
54728
55494
|
throw new Error(
|
|
54729
55495
|
"A sandbox's local gateway holds no model credential. The cloud gateway attaches the operator's key."
|
|
@@ -55091,9 +55857,9 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55091
55857
|
) : diskCredentialStore;
|
|
55092
55858
|
const runTokenKey = () => readRunTokenKey(paths.runTokenKey) ?? loadOrCreateRunTokenKey(paths.runTokenKey).key;
|
|
55093
55859
|
runTokenKey();
|
|
55094
|
-
function inCustody(provider) {
|
|
55860
|
+
function inCustody(provider, harness) {
|
|
55095
55861
|
try {
|
|
55096
|
-
return credentialStore.has(provider);
|
|
55862
|
+
return harness !== void 0 ? credentialStore.has(provider, harness) : credentialStore.status().some((c) => c.provider === provider) || credentialStore.has(provider);
|
|
55097
55863
|
} catch (error) {
|
|
55098
55864
|
log(
|
|
55099
55865
|
`credential store unreadable: ${error instanceof Error ? error.message : String(error)}; check ${paths.credentials} and ${paths.credentialsKey}, then run \`oxagen credential status\``
|
|
@@ -55187,6 +55953,11 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
55187
55953
|
env: options.workOrders?.env ?? process.env,
|
|
55188
55954
|
now,
|
|
55189
55955
|
log,
|
|
55956
|
+
// Bound late: `api` is built below, and a start runs only after a poll.
|
|
55957
|
+
issueRunToken: (input) => api.issueRunToken?.(input) ?? {
|
|
55958
|
+
status: 403,
|
|
55959
|
+
body: { error: "no issuer", code: "credential_unavailable" }
|
|
55960
|
+
},
|
|
55190
55961
|
...options.workOrders?.spawn !== void 0 ? { spawn: options.workOrders.spawn } : {},
|
|
55191
55962
|
...options.workOrders?.isAlive !== void 0 ? { isAlive: options.workOrders.isAlive } : {},
|
|
55192
55963
|
...options.workOrders?.findCheckout !== void 0 ? { findCheckout: options.workOrders.findCheckout } : {}
|
|
@@ -56958,17 +57729,19 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
56958
57729
|
...options.beforeForward !== void 0 ? { beforeForward: options.beforeForward } : {},
|
|
56959
57730
|
...options.pauseHold,
|
|
56960
57731
|
credentials: {
|
|
56961
|
-
brokered: (provider) => inCustody(provider),
|
|
56962
|
-
kind: (provider) => {
|
|
57732
|
+
brokered: (provider, harness) => inCustody(provider, harness),
|
|
57733
|
+
kind: (provider, harness) => {
|
|
56963
57734
|
try {
|
|
56964
|
-
return credentialStore.status().find(
|
|
57735
|
+
return credentialStore.status().find(
|
|
57736
|
+
(entry2) => entry2.provider === provider && entry2.harness === harness
|
|
57737
|
+
)?.kind;
|
|
56965
57738
|
} catch {
|
|
56966
57739
|
return void 0;
|
|
56967
57740
|
}
|
|
56968
57741
|
},
|
|
56969
|
-
custody: (provider) => {
|
|
57742
|
+
custody: (provider, harness) => {
|
|
56970
57743
|
try {
|
|
56971
|
-
return credentialStore.read(provider);
|
|
57744
|
+
return credentialStore.read(provider, harness);
|
|
56972
57745
|
} catch (error) {
|
|
56973
57746
|
log(
|
|
56974
57747
|
`credential store unreadable: ${error instanceof Error ? error.message : String(error)}; check ${paths.credentials} and ${paths.credentialsKey}, then run \`oxagen credential status\``
|
|
@@ -57669,8 +58442,11 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
57669
58442
|
let lastStaticRenewalAt = 0;
|
|
57670
58443
|
let lastLoginRefreshAt = 0;
|
|
57671
58444
|
async function renewStaticTokens() {
|
|
57672
|
-
|
|
57673
|
-
|
|
58445
|
+
const harnesses = STATIC_TOKEN_HARNESSES.filter(
|
|
58446
|
+
(h) => host.harnesses.includes(h)
|
|
58447
|
+
);
|
|
58448
|
+
if (harnesses.length === 0) return;
|
|
58449
|
+
if (harnesses.includes("codex") && now() - lastLoginRefreshAt >= CHATGPT_LOGIN_CHECK_MS) {
|
|
57674
58450
|
lastLoginRefreshAt = now();
|
|
57675
58451
|
try {
|
|
57676
58452
|
const refreshed = await refreshChatGptCustody(
|
|
@@ -57692,44 +58468,17 @@ async function initializeDaemon(options, server, port, ready) {
|
|
|
57692
58468
|
}
|
|
57693
58469
|
if (now() - lastStaticRenewalAt < STATIC_TOKEN_RENEWAL_CHECK_MS) return;
|
|
57694
58470
|
lastStaticRenewalAt = now();
|
|
57695
|
-
|
|
57696
|
-
|
|
57697
|
-
|
|
57698
|
-
|
|
57699
|
-
|
|
57700
|
-
|
|
57701
|
-
|
|
57702
|
-
|
|
57703
|
-
|
|
57704
|
-
|
|
57705
|
-
}
|
|
57706
|
-
if (state === void 0 || !state.brokered) return;
|
|
57707
|
-
const current = readCodexApiKeyMember(home);
|
|
57708
|
-
if (staticTokenStillGood(current, host, paths.runTokenKey, now())) return;
|
|
57709
|
-
const issued = api.issueRunToken?.({
|
|
57710
|
-
harness: "codex",
|
|
57711
|
-
placement: "static"
|
|
57712
|
-
});
|
|
57713
|
-
if (issued === void 0 || issued.status !== 200) {
|
|
57714
|
-
log(
|
|
57715
|
-
`static token renewal: not issued (${issued?.body.error ?? "no issuer"})`
|
|
57716
|
-
);
|
|
57717
|
-
return;
|
|
57718
|
-
}
|
|
57719
|
-
try {
|
|
57720
|
-
await applyModelCredentials({
|
|
57721
|
-
home,
|
|
57722
|
-
harnesses: ["codex"],
|
|
57723
|
-
staticTokens: { codex: issued.body.token }
|
|
58471
|
+
for (const harness of harnesses)
|
|
58472
|
+
await renewStaticToken(harness, {
|
|
58473
|
+
home: options.home ?? homedir7(),
|
|
58474
|
+
stellaHome: dirname24(paths.stellaToml),
|
|
58475
|
+
host,
|
|
58476
|
+
runTokenKeyPath: paths.runTokenKey,
|
|
58477
|
+
now,
|
|
58478
|
+
inCustody,
|
|
58479
|
+
issue: api.issueRunToken,
|
|
58480
|
+
log
|
|
57724
58481
|
});
|
|
57725
|
-
log(
|
|
57726
|
-
`renewed Codex's static run token ${issued.body.token_id}, expires ${issued.body.expires_at}`
|
|
57727
|
-
);
|
|
57728
|
-
} catch (error) {
|
|
57729
|
-
log(
|
|
57730
|
-
`static token renewal: could not write Codex's auth file: ${error instanceof Error ? error.message : String(error)}`
|
|
57731
|
-
);
|
|
57732
|
-
}
|
|
57733
58482
|
}
|
|
57734
58483
|
async function tick() {
|
|
57735
58484
|
await controlTick();
|
|
@@ -57929,6 +58678,7 @@ var init_daemon = __esm({
|
|
|
57929
58678
|
init_claude_code_mcp_writer();
|
|
57930
58679
|
init_model_base_url();
|
|
57931
58680
|
init_model_credential();
|
|
58681
|
+
init_static_token_renewal();
|
|
57932
58682
|
init_chatgpt_refresh();
|
|
57933
58683
|
init_run_token();
|
|
57934
58684
|
init_process_scan();
|
|
@@ -60294,7 +61044,7 @@ var init_package = __esm({
|
|
|
60294
61044
|
"package.json"() {
|
|
60295
61045
|
package_default = {
|
|
60296
61046
|
name: "@oxagen/cli",
|
|
60297
|
-
version: "2.1.
|
|
61047
|
+
version: "2.1.5-22",
|
|
60298
61048
|
description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
|
|
60299
61049
|
type: "module",
|
|
60300
61050
|
main: "./dist/index.js",
|
|
@@ -66533,13 +67283,13 @@ function ruleIssues(rule, value) {
|
|
|
66533
67283
|
}
|
|
66534
67284
|
}
|
|
66535
67285
|
function testJson(test) {
|
|
66536
|
-
const
|
|
67286
|
+
const matches2 = {
|
|
66537
67287
|
properties: {
|
|
66538
67288
|
[test.field]: { const: "is" in test ? test.is : test.isNot }
|
|
66539
67289
|
},
|
|
66540
67290
|
required: [test.field]
|
|
66541
67291
|
};
|
|
66542
|
-
return "is" in test ?
|
|
67292
|
+
return "is" in test ? matches2 : { not: matches2 };
|
|
66543
67293
|
}
|
|
66544
67294
|
function nested(path2, leaf) {
|
|
66545
67295
|
return path2.reduceRight(
|
|
@@ -77639,10 +78389,10 @@ function verifyRunExport(files2) {
|
|
|
77639
78389
|
const shipped = parseJson2(redactionsText, "redactions", checks);
|
|
77640
78390
|
if (shipped !== null) {
|
|
77641
78391
|
const recomputed = summarizeRunExportRedactions(frames);
|
|
77642
|
-
const
|
|
78392
|
+
const matches2 = digestJcs(recomputed) === digestJcs(shipped);
|
|
77643
78393
|
hold(
|
|
77644
78394
|
"redactions",
|
|
77645
|
-
|
|
78395
|
+
matches2,
|
|
77646
78396
|
`${recomputed.redacted_total} redaction(s) summarised`,
|
|
77647
78397
|
"redactions.json does not match what the frames record"
|
|
77648
78398
|
);
|
|
@@ -79282,7 +80032,7 @@ var init_run_shared = __esm({
|
|
|
79282
80032
|
});
|
|
79283
80033
|
|
|
79284
80034
|
// ../../packages/oxagen/src/contracts/run.list.ts
|
|
79285
|
-
var runPublicIdSchema, frameSeqSchema, runSourceSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS,
|
|
80035
|
+
var runPublicIdSchema, frameSeqSchema, runSourceSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS, HOST_POLL_WINDOW_MS, RUN_PULL_REQUEST_MAX, runPullRequestSchema, runDiffSchema, runAttributionSchema, RUN_PULL_REQUEST_FILTERS, RUN_PAUSE_STATES, runPauseSchema, RUN_REPLAY_FILTERS, RUN_SORT_KEYS, RUN_LIST_TOTAL_BOUND, RUN_LABEL_MAX, runItemSchema, runList;
|
|
79286
80036
|
var init_run_list = __esm({
|
|
79287
80037
|
"../../packages/oxagen/src/contracts/run.list.ts"() {
|
|
79288
80038
|
"use strict";
|
|
@@ -79368,7 +80118,6 @@ var init_run_list = __esm({
|
|
|
79368
80118
|
"host_offline",
|
|
79369
80119
|
"daemon_chain"
|
|
79370
80120
|
];
|
|
79371
|
-
STEER_BLOCKS = ["no_prompt_carrier"];
|
|
79372
80121
|
HOST_POLL_WINDOW_MS = 5 * 6e4;
|
|
79373
80122
|
RUN_PULL_REQUEST_MAX = 10;
|
|
79374
80123
|
runPullRequestSchema = external_exports.object({
|
|
@@ -79628,13 +80377,6 @@ var init_run_list = __esm({
|
|
|
79628
80377
|
* resume, steer or cancel on it. Absent reads as false.
|
|
79629
80378
|
*/
|
|
79630
80379
|
inApp: external_exports.boolean().optional(),
|
|
79631
|
-
/**
|
|
79632
|
-
* Why a steer or message cannot reach this run when the other commands
|
|
79633
|
-
* can, or null when it can. `dispatch_command` refuses on the same rule
|
|
79634
|
-
* (`steerBlockOf`). Null for a ledger run. Optional, read like
|
|
79635
|
-
* `commandBlock`: absent means not known.
|
|
79636
|
-
*/
|
|
79637
|
-
steerBlock: external_exports.enum(STEER_BLOCKS).nullable().optional(),
|
|
79638
80380
|
/**
|
|
79639
80381
|
* The model the run ended on, falling back to the one it started on; null
|
|
79640
80382
|
* when the store recorded no model, which is every ledger run.
|
|
@@ -81095,7 +81837,7 @@ function opposite(a, b3) {
|
|
|
81095
81837
|
}
|
|
81096
81838
|
function similarStatements(entries2) {
|
|
81097
81839
|
const bySize = entries2.map(compared).sort((a, b3) => a.set.size - b3.set.size);
|
|
81098
|
-
const
|
|
81840
|
+
const matches2 = [];
|
|
81099
81841
|
for (let i2 = 0; i2 < bySize.length; i2 += 1) {
|
|
81100
81842
|
const a = bySize[i2];
|
|
81101
81843
|
for (let j = i2 + 1; j < bySize.length; j += 1) {
|
|
@@ -81103,14 +81845,14 @@ function similarStatements(entries2) {
|
|
|
81103
81845
|
if (a.normalized !== b3.normalized && a.set.size < b3.set.size * NEAR_DUPLICATE_SIMILARITY)
|
|
81104
81846
|
break;
|
|
81105
81847
|
if (!alike(a, b3)) continue;
|
|
81106
|
-
|
|
81848
|
+
matches2.push({
|
|
81107
81849
|
a: a.entry,
|
|
81108
81850
|
b: b3.entry,
|
|
81109
81851
|
opposite: opposite(a.entry, b3.entry)
|
|
81110
81852
|
});
|
|
81111
81853
|
}
|
|
81112
81854
|
}
|
|
81113
|
-
return
|
|
81855
|
+
return matches2;
|
|
81114
81856
|
}
|
|
81115
81857
|
function active(lineage, kind, effect, file, statement) {
|
|
81116
81858
|
return { lineage, kind, effect, file, statement };
|
|
@@ -89802,17 +90544,17 @@ function registryLaunch({
|
|
|
89802
90544
|
]
|
|
89803
90545
|
};
|
|
89804
90546
|
}
|
|
89805
|
-
const
|
|
90547
|
+
const matches2 = (entry2.server.packages ?? []).filter(
|
|
89806
90548
|
(candidate) => candidate.registryType === type
|
|
89807
90549
|
);
|
|
89808
|
-
const [pkg] =
|
|
89809
|
-
if (pkg === void 0 ||
|
|
90550
|
+
const [pkg] = matches2;
|
|
90551
|
+
if (pkg === void 0 || matches2.length > 1) {
|
|
89810
90552
|
return {
|
|
89811
90553
|
ok: false,
|
|
89812
90554
|
problems: [
|
|
89813
90555
|
{
|
|
89814
90556
|
field: "source.registry_type",
|
|
89815
|
-
message: pkg === void 0 ? `the entry lists no ${type} package` : `the entry lists ${
|
|
90557
|
+
message: pkg === void 0 ? `the entry lists no ${type} package` : `the entry lists ${matches2.length} ${type} packages, so source.registry_type cannot pick one`
|
|
89816
90558
|
}
|
|
89817
90559
|
]
|
|
89818
90560
|
};
|
|
@@ -95833,13 +96575,13 @@ function timestampFromJson(timestamp2, json2) {
|
|
|
95833
96575
|
if (typeof json2 !== "string") {
|
|
95834
96576
|
throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: ${formatVal(json2)}`);
|
|
95835
96577
|
}
|
|
95836
|
-
const
|
|
95837
|
-
if (!
|
|
96578
|
+
const matches2 = json2.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]{1,9}))?(?:Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
|
|
96579
|
+
if (!matches2) {
|
|
95838
96580
|
throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: invalid RFC 3339 string`);
|
|
95839
96581
|
}
|
|
95840
96582
|
const ms = Date.parse(
|
|
95841
96583
|
// biome-ignore format: want this to read well
|
|
95842
|
-
|
|
96584
|
+
matches2[1] + "-" + matches2[2] + "-" + matches2[3] + "T" + matches2[4] + ":" + matches2[5] + ":" + matches2[6] + (matches2[8] ? matches2[8] : "Z")
|
|
95843
96585
|
);
|
|
95844
96586
|
if (Number.isNaN(ms)) {
|
|
95845
96587
|
throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: invalid RFC 3339 string`);
|
|
@@ -95849,8 +96591,8 @@ function timestampFromJson(timestamp2, json2) {
|
|
|
95849
96591
|
}
|
|
95850
96592
|
timestamp2.seconds = protoInt64.parse(ms / 1e3);
|
|
95851
96593
|
timestamp2.nanos = 0;
|
|
95852
|
-
if (
|
|
95853
|
-
timestamp2.nanos = parseInt("1" +
|
|
96594
|
+
if (matches2[7]) {
|
|
96595
|
+
timestamp2.nanos = parseInt("1" + matches2[7] + "0".repeat(9 - matches2[7].length)) - 1e9;
|
|
95854
96596
|
}
|
|
95855
96597
|
}
|
|
95856
96598
|
function durationFromJson(duration, json2) {
|
|
@@ -106166,18 +106908,18 @@ function lintRegistry(folder, report) {
|
|
|
106166
106908
|
function lintPackage(source, entry2, type, report) {
|
|
106167
106909
|
const other = otherChoice(source, entry2, type);
|
|
106168
106910
|
const packages = entry2.server.packages ?? [];
|
|
106169
|
-
const
|
|
106911
|
+
const matches2 = packages.filter(
|
|
106170
106912
|
(candidate) => candidate.registryType === type
|
|
106171
106913
|
);
|
|
106172
|
-
const [pkg] =
|
|
106173
|
-
if (pkg === void 0 ||
|
|
106914
|
+
const [pkg] = matches2;
|
|
106915
|
+
if (pkg === void 0 || matches2.length > 1) {
|
|
106174
106916
|
const bundle2 = packages.some(
|
|
106175
106917
|
(candidate) => candidate.registryType === "mcpb"
|
|
106176
106918
|
);
|
|
106177
106919
|
report("package_cannot_run", {
|
|
106178
106920
|
tool: void 0,
|
|
106179
106921
|
field: "source.registry_type",
|
|
106180
|
-
message: pkg === void 0 ? `The catalog entry lists no ${type} package${bundle2 ? ", and the local gateway cannot run its mcpb bundle" : ""}, so nothing could start the server.` : `The catalog entry lists ${
|
|
106922
|
+
message: pkg === void 0 ? `The catalog entry lists no ${type} package${bundle2 ? ", and the local gateway cannot run its mcpb bundle" : ""}, so nothing could start the server.` : `The catalog entry lists ${matches2.length} ${type} packages, so source.registry_type cannot pick one.`,
|
|
106181
106923
|
fix: other
|
|
106182
106924
|
});
|
|
106183
106925
|
return;
|
|
@@ -138560,10 +139302,15 @@ function buildProgram() {
|
|
|
138560
139302
|
});
|
|
138561
139303
|
work.command("claim").description(
|
|
138562
139304
|
"From inside a work order's run, claim that the pushed head commit meets one criterion of the brief. A person still decides."
|
|
138563
|
-
).argument("<criterion>", "The criterion's id from the brief, such as c1").requiredOption("--text <text>", "How the commit meets the criterion").
|
|
138564
|
-
|
|
138565
|
-
|
|
138566
|
-
|
|
139305
|
+
).argument("<criterion>", "The criterion's id from the brief, such as c1").requiredOption("--text <text>", "How the commit meets the criterion").option(
|
|
139306
|
+
"--work-order <id>",
|
|
139307
|
+
"The work order of a batch item, as the first prompt lists it"
|
|
139308
|
+
).option("--item <id>", "The work item of that work order").action(
|
|
139309
|
+
async (criterionId, opts) => {
|
|
139310
|
+
const { handleWorkClaim: handleWorkClaim2 } = await Promise.resolve().then(() => (init_tacho(), tacho_exports));
|
|
139311
|
+
process.exitCode = await handleWorkClaim2(criterionId, opts);
|
|
139312
|
+
}
|
|
139313
|
+
);
|
|
138567
139314
|
const env = program2.command("env").description("Manage workspace environments");
|
|
138568
139315
|
env.command("list").description("List environments in the active workspace").option("--json", "Output JSON").action(async (opts) => {
|
|
138569
139316
|
const { handleEnvList: handleEnvList2 } = await Promise.resolve().then(() => (init_env2(), env_exports));
|