agent-shim 8.6.0 → 8.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +277 -209
- package/dist/index.cjs +2021 -1904
- package/dist/index.mjs +2008 -1900
- package/dist/types/index.d.ts +263 -60
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3323,25 +3323,19 @@ function realConnectEffects() {
|
|
|
3323
3323
|
}
|
|
3324
3324
|
};
|
|
3325
3325
|
},
|
|
3326
|
-
connectTlsUpstream: async (host, port, clientAlpn) =>
|
|
3327
|
-
const
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
}
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
candidate.once("error", (error) => {
|
|
3336
|
-
if ("code" in error && error.code === "EADDRINUSE") {
|
|
3337
|
-
settle(dial(upstreamLocalPort()));
|
|
3338
|
-
return;
|
|
3339
|
-
}
|
|
3340
|
-
reject(error);
|
|
3326
|
+
connectTlsUpstream: async (host, port, clientAlpn) => {
|
|
3327
|
+
const dialled = await reservedDial({ host, port });
|
|
3328
|
+
if (dialled.error !== null) {
|
|
3329
|
+
throw dialled.error;
|
|
3330
|
+
}
|
|
3331
|
+
return await new Promise((resolve, reject) => {
|
|
3332
|
+
const secure = tls.connect({ socket: dialled.socket, servername: host, ...clientAlpn === void 0 ? {} : { ALPNProtocols: [clientAlpn] } });
|
|
3333
|
+
secure.once("secureConnect", () => {
|
|
3334
|
+
resolve(secure);
|
|
3341
3335
|
});
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
}
|
|
3336
|
+
secure.once("error", reject);
|
|
3337
|
+
});
|
|
3338
|
+
},
|
|
3345
3339
|
connectTcp: async (host, port) => await new Promise((resolve, reject) => {
|
|
3346
3340
|
const socket = net2.connect({ host, port });
|
|
3347
3341
|
socket.once("connect", () => {
|
|
@@ -4903,547 +4897,380 @@ function listUsageSnapshots(fs12, snapshotsDir) {
|
|
|
4903
4897
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
4904
4898
|
}
|
|
4905
4899
|
|
|
4906
|
-
// src/
|
|
4907
|
-
var
|
|
4908
|
-
var
|
|
4909
|
-
var
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
const { paths, ports } = params;
|
|
4917
|
-
const deadline = ports.now() + HEADROOM_START_TIMEOUT_MS;
|
|
4918
|
-
let spawnedSupervisor = false;
|
|
4919
|
-
let awaitingAllowlist = false;
|
|
4920
|
-
for (; ; ) {
|
|
4921
|
-
const state = readHeadroomState(ports.fs, paths.headroomStateFile);
|
|
4922
|
-
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
4923
|
-
const daemonUp = state.socketPath !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
4924
|
-
if (daemonUp && state.socketPath !== void 0) {
|
|
4925
|
-
const required = params.requiredAllowlistHash?.();
|
|
4926
|
-
if (required === void 0 || state.allowlistHash === required) {
|
|
4927
|
-
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
4928
|
-
return { socketPath: state.socketPath };
|
|
4929
|
-
}
|
|
4930
|
-
const holders = sessionsForSupervisor(ports.fs, paths.headroomSessionsDir, state.supervisorPid).filter((session) => ports.isRunning(session.pid));
|
|
4931
|
-
if (holders.length > 0) {
|
|
4932
|
-
throw new HeadroomStartError(
|
|
4933
|
-
`the running headroom daemon was started before this launch's front door address and would refuse every request this launch sends through it. It restarts once the launches using it end (pid ${holders.map((session) => String(session.pid)).join(", ")}); end them and launch again, or launch with --no-headroom.`
|
|
4934
|
-
);
|
|
4935
|
-
}
|
|
4936
|
-
awaitingAllowlist = true;
|
|
4937
|
-
}
|
|
4938
|
-
if (state.lastError !== void 0) {
|
|
4939
|
-
throw new HeadroomStartError(
|
|
4940
|
-
`the headroom daemon reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.headroomLogPath})`
|
|
4941
|
-
);
|
|
4942
|
-
}
|
|
4943
|
-
} else if (!spawnedSupervisor) {
|
|
4944
|
-
ports.fs.mkdirp(paths.headroomDir);
|
|
4945
|
-
const created = ports.fs.writeFileExclusive(
|
|
4946
|
-
paths.headroomLockFile,
|
|
4947
|
-
`${JSON.stringify({ pid: params.launcherPid, at: ports.now() })}
|
|
4948
|
-
`
|
|
4949
|
-
);
|
|
4950
|
-
if (created) {
|
|
4951
|
-
ports.spawnSupervisor(paths);
|
|
4952
|
-
spawnedSupervisor = true;
|
|
4953
|
-
} else {
|
|
4954
|
-
const lock = readStartLock(ports.fs, paths.headroomLockFile);
|
|
4955
|
-
if (lock === void 0 || !ports.isRunning(lock.pid)) {
|
|
4956
|
-
ports.fs.removeRecursive(paths.headroomLockFile);
|
|
4957
|
-
continue;
|
|
4958
|
-
}
|
|
4959
|
-
}
|
|
4960
|
-
}
|
|
4961
|
-
if (ports.now() >= deadline) {
|
|
4962
|
-
const lastError = state?.lastError;
|
|
4963
|
-
throw new HeadroomStartError(
|
|
4964
|
-
`the headroom daemon did not become ready within ${String(HEADROOM_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (awaitingAllowlist ? " (the running daemon's allowlist does not admit this launch's front door, and it did not restart)" : "") + `. Daemon log: ${paths.headroomLogPath}`
|
|
4965
|
-
);
|
|
4966
|
-
}
|
|
4967
|
-
ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
|
|
4900
|
+
// src/usage/preflight.ts
|
|
4901
|
+
var MS_PER_SECOND2 = 1e3;
|
|
4902
|
+
var SECONDS_PER_MINUTE = 60;
|
|
4903
|
+
var MINUTES_PER_HOUR = 60;
|
|
4904
|
+
var HOURS_PER_DAY = 24;
|
|
4905
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
4906
|
+
function formatAge(ms) {
|
|
4907
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
4908
|
+
if (minutes < MINUTES_PER_HOUR) {
|
|
4909
|
+
return `${String(Math.max(minutes, 0))}m`;
|
|
4968
4910
|
}
|
|
4911
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
4912
|
+
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
4969
4913
|
}
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4914
|
+
function effectiveWindow(window, nowMs) {
|
|
4915
|
+
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
4916
|
+
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
4917
|
+
return { reset: true, utilization: 0 };
|
|
4918
|
+
}
|
|
4974
4919
|
return {
|
|
4975
|
-
|
|
4976
|
-
...
|
|
4977
|
-
...
|
|
4978
|
-
...
|
|
4979
|
-
...config.interceptToolResults === void 0 ? {} : { interceptToolResults: config.interceptToolResults },
|
|
4980
|
-
...config.readMaturation === void 0 ? {} : { readMaturation: config.readMaturation }
|
|
4920
|
+
reset: false,
|
|
4921
|
+
...window.utilization === void 0 ? {} : { utilization: window.utilization },
|
|
4922
|
+
...resetsAtMs === void 0 ? {} : { resetsAtMs },
|
|
4923
|
+
...window.status === void 0 ? {} : { status: window.status }
|
|
4981
4924
|
};
|
|
4982
4925
|
}
|
|
4983
|
-
function
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4926
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
4927
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
4928
|
+
const unified = rateLimit?.unified;
|
|
4929
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
4930
|
+
return [];
|
|
4931
|
+
}
|
|
4932
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
4933
|
+
const windows = [
|
|
4934
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
4935
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
4991
4936
|
];
|
|
4992
|
-
}
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4937
|
+
return windows.flatMap(({ name, window }) => {
|
|
4938
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
4939
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
4940
|
+
return [];
|
|
4941
|
+
}
|
|
4942
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
4943
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
4944
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
4945
|
+
});
|
|
4998
4946
|
}
|
|
4999
4947
|
|
|
5000
|
-
// src/
|
|
5001
|
-
import
|
|
5002
|
-
var
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
}
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
4948
|
+
// src/usage/plan.ts
|
|
4949
|
+
import { z as z6 } from "zod";
|
|
4950
|
+
var PlanClassSchema = z6.union([
|
|
4951
|
+
z6.strictObject({
|
|
4952
|
+
kind: z6.literal("subscription"),
|
|
4953
|
+
/** The plan's size as a multiple of the smallest subscription tier (Max 5x is 5, Max 20x is 20). */
|
|
4954
|
+
capacity: z6.number().positive(),
|
|
4955
|
+
/** False when the tier string named no multiplier and no tier this project knows, so `capacity` is the neutral 1 rather than a reading. */
|
|
4956
|
+
recognised: z6.boolean(),
|
|
4957
|
+
/** The tier string the classification rests on, when the account had one. */
|
|
4958
|
+
tier: z6.string().optional()
|
|
4959
|
+
}),
|
|
4960
|
+
z6.strictObject({
|
|
4961
|
+
kind: z6.literal("pay-per-use"),
|
|
4962
|
+
tier: z6.string().optional()
|
|
4963
|
+
})
|
|
4964
|
+
]);
|
|
4965
|
+
var TIER_MULTIPLIER = /(?:^|_)(\d+)x$/;
|
|
4966
|
+
var ZERO_TIER = /(?:^|_)zero$/;
|
|
4967
|
+
var BASE_TIER = /(?:^|_)(?:pro|free)$/;
|
|
4968
|
+
function planOf(account) {
|
|
4969
|
+
const tier = account?.organizationRateLimitTier ?? account?.userRateLimitTier;
|
|
4970
|
+
const tierField = tier === void 0 ? {} : { tier };
|
|
4971
|
+
if (tier === void 0) {
|
|
4972
|
+
return { kind: "subscription", capacity: 1, recognised: false };
|
|
5024
4973
|
}
|
|
5025
|
-
if (
|
|
5026
|
-
return
|
|
4974
|
+
if (ZERO_TIER.test(tier)) {
|
|
4975
|
+
return { kind: "pay-per-use", ...tierField };
|
|
5027
4976
|
}
|
|
5028
|
-
|
|
5029
|
-
|
|
4977
|
+
const multiplier = TIER_MULTIPLIER.exec(tier)?.[1];
|
|
4978
|
+
if (multiplier !== void 0) {
|
|
4979
|
+
return { kind: "subscription", capacity: Number(multiplier), recognised: true, ...tierField };
|
|
5030
4980
|
}
|
|
5031
|
-
return
|
|
4981
|
+
return { kind: "subscription", capacity: 1, recognised: BASE_TIER.test(tier), ...tierField };
|
|
5032
4982
|
}
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
const length = Buffer.byteLength(socketPath, "utf8");
|
|
5040
|
-
if (length > UNIX_SOCKET_PATH_MAX_BYTES) {
|
|
5041
|
-
return {
|
|
5042
|
-
refused: `the headroom socket path ${socketPath} is ${String(length)} bytes, longer than the ${String(UNIX_SOCKET_PATH_MAX_BYTES)} a unix socket path can hold; set AGENT_SHIM_HOME to a shorter directory`
|
|
5043
|
-
};
|
|
5044
|
-
}
|
|
5045
|
-
if (ports.lstat(paths.headroomSocketDir)?.kind === "symlink") {
|
|
5046
|
-
return { refused: `the headroom socket directory ${paths.headroomSocketDir} is a symlink; it must be the real directory` };
|
|
4983
|
+
|
|
4984
|
+
// src/usage/rateLimit.ts
|
|
4985
|
+
var MS_PER_SECOND3 = 1e3;
|
|
4986
|
+
function epochSecondsToIso(value) {
|
|
4987
|
+
if (value === void 0) {
|
|
4988
|
+
return void 0;
|
|
5047
4989
|
}
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
return refusal === void 0 ? { socketPath } : { refused: refusal };
|
|
4990
|
+
const seconds = Number(value);
|
|
4991
|
+
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
5051
4992
|
}
|
|
5052
|
-
function
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
const pid = HEADROOM_SOCKET_NAME.exec(name)?.[1];
|
|
5056
|
-
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
5057
|
-
continue;
|
|
5058
|
-
}
|
|
5059
|
-
fs12.removeRecursive(path11.join(paths.headroomSocketDir, name));
|
|
5060
|
-
removed.push(name);
|
|
4993
|
+
function nonNegativeNumber(value) {
|
|
4994
|
+
if (value === void 0 || value.trim() === "") {
|
|
4995
|
+
return void 0;
|
|
5061
4996
|
}
|
|
5062
|
-
|
|
4997
|
+
const parsed = Number(value);
|
|
4998
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
5063
4999
|
}
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
const match = GIT_SPEC_RE.exec(spec);
|
|
5072
|
-
if (match === null) {
|
|
5073
|
-
return false;
|
|
5000
|
+
var UNIFIED_PREFIX = "anthropic-ratelimit-unified-";
|
|
5001
|
+
function unifiedWindow(headers, window) {
|
|
5002
|
+
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
5003
|
+
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
5004
|
+
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
5005
|
+
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
5006
|
+
return void 0;
|
|
5074
5007
|
}
|
|
5075
|
-
|
|
5076
|
-
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
5077
|
-
}
|
|
5078
|
-
function pinnedGitCommit(spec) {
|
|
5079
|
-
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
5080
|
-
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
5008
|
+
return { ...utilization === void 0 ? {} : { utilization }, ...resetsAt === void 0 ? {} : { resetsAt }, ...status === void 0 ? {} : { status } };
|
|
5081
5009
|
}
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5010
|
+
function parseUnifiedRateLimit(headers) {
|
|
5011
|
+
if (!Object.keys(headers).some((name) => name.startsWith(UNIFIED_PREFIX))) {
|
|
5012
|
+
return void 0;
|
|
5013
|
+
}
|
|
5014
|
+
const status = headers[`${UNIFIED_PREFIX}status`];
|
|
5015
|
+
const fiveHour = unifiedWindow(headers, "5h");
|
|
5016
|
+
const sevenDay = unifiedWindow(headers, "7d");
|
|
5017
|
+
const representativeClaim = headers[`${UNIFIED_PREFIX}representative-claim`];
|
|
5018
|
+
const resetAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}reset`]);
|
|
5019
|
+
const overageStatus = headers[`${UNIFIED_PREFIX}overage-status`];
|
|
5085
5020
|
return {
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5021
|
+
...status === void 0 ? {} : { status },
|
|
5022
|
+
...fiveHour === void 0 ? {} : { fiveHour },
|
|
5023
|
+
...sevenDay === void 0 ? {} : { sevenDay },
|
|
5024
|
+
...representativeClaim === void 0 ? {} : { representativeClaim },
|
|
5025
|
+
...resetAt === void 0 ? {} : { resetAt },
|
|
5026
|
+
...overageStatus === void 0 ? {} : { overageStatus }
|
|
5089
5027
|
};
|
|
5090
5028
|
}
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
var
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5029
|
+
|
|
5030
|
+
// src/usage/scan.ts
|
|
5031
|
+
import { z as z7 } from "zod";
|
|
5032
|
+
var AnthropicUsageSchema = z7.looseObject({
|
|
5033
|
+
input_tokens: z7.number().int().nonnegative().nullish(),
|
|
5034
|
+
output_tokens: z7.number().int().nonnegative().nullish(),
|
|
5035
|
+
cache_creation_input_tokens: z7.number().int().nonnegative().nullish(),
|
|
5036
|
+
cache_read_input_tokens: z7.number().int().nonnegative().nullish()
|
|
5037
|
+
});
|
|
5038
|
+
var ErrorObjectSchema = z7.looseObject({
|
|
5039
|
+
type: z7.string().optional(),
|
|
5040
|
+
code: z7.union([z7.string(), z7.number()]).optional(),
|
|
5041
|
+
message: z7.string().optional()
|
|
5042
|
+
});
|
|
5043
|
+
|
|
5044
|
+
// src/usage/middleware.ts
|
|
5045
|
+
var ANTHROPIC_PROVIDER = "anthropic";
|
|
5046
|
+
|
|
5047
|
+
// src/usage/pick.ts
|
|
5048
|
+
var MS_PER_SECOND4 = 1e3;
|
|
5049
|
+
var MS_PER_HOUR = 36e5;
|
|
5050
|
+
var HOURS_PER_DAY2 = 24;
|
|
5051
|
+
var FIVE_HOURS = 5;
|
|
5052
|
+
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
5053
|
+
var PERCENT = 100;
|
|
5054
|
+
var SEVEN_DAYS = 7;
|
|
5055
|
+
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
5056
|
+
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
5057
|
+
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
5058
|
+
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
5059
|
+
function unifiedOf(state) {
|
|
5060
|
+
return state?.rateLimit?.unified;
|
|
5099
5061
|
}
|
|
5100
|
-
function
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
const specifier = /==|>=|<=|!=|>|<|~=/.exec(source);
|
|
5105
|
-
if (specifier === null) {
|
|
5106
|
-
return true;
|
|
5107
|
-
}
|
|
5108
|
-
const installed = parseVersion(version);
|
|
5109
|
-
const wanted = parseVersion(source.slice(specifier.index + specifier[0].length));
|
|
5110
|
-
if (installed === void 0 || wanted === void 0) {
|
|
5111
|
-
return true;
|
|
5062
|
+
function limitBlockedUntil(state, nowMs) {
|
|
5063
|
+
const limit = state?.lastLimit;
|
|
5064
|
+
if (state === void 0 || limit === void 0) {
|
|
5065
|
+
return void 0;
|
|
5112
5066
|
}
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
case "==":
|
|
5116
|
-
return cmp === 0;
|
|
5117
|
-
case "!=":
|
|
5118
|
-
return cmp !== 0;
|
|
5119
|
-
case ">=":
|
|
5120
|
-
return cmp >= 0;
|
|
5121
|
-
case "<=":
|
|
5122
|
-
return cmp <= 0;
|
|
5123
|
-
case ">":
|
|
5124
|
-
return cmp > 0;
|
|
5125
|
-
case "<":
|
|
5126
|
-
return cmp < 0;
|
|
5127
|
-
case "~=":
|
|
5128
|
-
return compareVersions(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
5129
|
-
default:
|
|
5130
|
-
return true;
|
|
5067
|
+
if (Date.parse(state.lastRequestAt) > Date.parse(limit.observedAt)) {
|
|
5068
|
+
return void 0;
|
|
5131
5069
|
}
|
|
5070
|
+
const until = limit.resetAt === void 0 ? limit.retryAfterSeconds === void 0 ? void 0 : Date.parse(limit.observedAt) + limit.retryAfterSeconds * MS_PER_SECOND4 : Date.parse(limit.resetAt);
|
|
5071
|
+
return until !== void 0 && until > nowMs ? until : void 0;
|
|
5132
5072
|
}
|
|
5133
|
-
function
|
|
5134
|
-
const
|
|
5135
|
-
|
|
5073
|
+
function windowBlockedUntil(windows) {
|
|
5074
|
+
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
5075
|
+
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
5076
|
+
}
|
|
5077
|
+
function fiveHourBurn(records) {
|
|
5078
|
+
const points = [];
|
|
5079
|
+
for (const record of records) {
|
|
5080
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.fiveHour;
|
|
5081
|
+
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
5082
|
+
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
const latest = points.at(-1)?.resetsAt;
|
|
5086
|
+
const current = points.filter((entry) => entry.resetsAt === latest).map((entry) => entry.point);
|
|
5087
|
+
const first = current.at(0);
|
|
5088
|
+
const last = current.at(-1);
|
|
5089
|
+
if (first === void 0 || last === void 0 || last.atMs <= first.atMs || last.utilization <= first.utilization) {
|
|
5136
5090
|
return void 0;
|
|
5137
5091
|
}
|
|
5138
|
-
return
|
|
5092
|
+
return (last.utilization - first.utilization) / (last.atMs - first.atMs);
|
|
5139
5093
|
}
|
|
5140
|
-
function
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
}
|
|
5094
|
+
function percent(fraction) {
|
|
5095
|
+
return `${String(Math.round(fraction * PERCENT))}%`;
|
|
5096
|
+
}
|
|
5097
|
+
function describeWindow(label, window, effective, nowMs) {
|
|
5098
|
+
if (effective.reset) {
|
|
5099
|
+
return `${label} window has reset`;
|
|
5147
5100
|
}
|
|
5148
|
-
|
|
5101
|
+
const used = effective.utilization === void 0 ? "usage unknown" : `${percent(effective.utilization)} used`;
|
|
5102
|
+
const resets = effective.resetsAtMs === void 0 ? "" : `, resets in ${formatAge(effective.resetsAtMs - nowMs)}`;
|
|
5103
|
+
return `${label} ${used}${resets}${window.status === "allowed_warning" ? " (nearly used)" : ""}`;
|
|
5149
5104
|
}
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
const
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5105
|
+
function capacityOf(plan) {
|
|
5106
|
+
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
5107
|
+
}
|
|
5108
|
+
function assess(member, nowMs) {
|
|
5109
|
+
const plan = planOf(member.account);
|
|
5110
|
+
const planReasons = plan.kind === "pay-per-use" ? ["billed by use (no plan allowance)"] : plan.recognised ? [`${String(plan.capacity)}x plan`] : [`plan tier ${plan.tier === void 0 ? "not known" : `"${plan.tier}" not recognised`}, counted as 1x`];
|
|
5111
|
+
const base = { identity: member.identity, plan };
|
|
5112
|
+
if (member.readError !== void 0) {
|
|
5113
|
+
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
5158
5114
|
}
|
|
5159
|
-
|
|
5160
|
-
const
|
|
5161
|
-
|
|
5162
|
-
return
|
|
5163
|
-
}
|
|
5164
|
-
const
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
const versionOk = version2 !== void 0 && versionSatisfies(version2, config.source);
|
|
5181
|
-
const pinned = pinnedGitCommit(config.source);
|
|
5182
|
-
const commitOk = pinned === void 0 || ports.installedCommit() === pinned;
|
|
5183
|
-
if (versionOk && commitOk && (installedSource === void 0 || installedSource === config.source)) {
|
|
5184
|
-
installedSource = config.source;
|
|
5185
|
-
return true;
|
|
5186
|
-
}
|
|
5187
|
-
ports.log(`agent-shim headroom supervisor: installing headroom from ${config.source}`);
|
|
5188
|
-
const result = ports.install(config.source);
|
|
5189
|
-
if (!result.ok) {
|
|
5190
|
-
return false;
|
|
5115
|
+
const state = member.snapshot?.providers[ANTHROPIC_PROVIDER];
|
|
5116
|
+
const unified = unifiedOf(state);
|
|
5117
|
+
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
5118
|
+
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
5119
|
+
}
|
|
5120
|
+
const observedAgo = state.rateLimit === void 0 ? "" : ` (last seen ${formatAge(nowMs - Date.parse(state.rateLimit.observedAt))} ago)`;
|
|
5121
|
+
const fiveHourWindow = unified.fiveHour;
|
|
5122
|
+
const sevenDayWindow = unified.sevenDay;
|
|
5123
|
+
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
5124
|
+
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
5125
|
+
const windowReasons = [
|
|
5126
|
+
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
5127
|
+
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
5128
|
+
];
|
|
5129
|
+
const blockedUntil = Math.max(
|
|
5130
|
+
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
5131
|
+
limitBlockedUntil(state, nowMs) ?? 0
|
|
5132
|
+
);
|
|
5133
|
+
if (blockedUntil > nowMs) {
|
|
5134
|
+
if (unified.overageStatus === "allowed") {
|
|
5135
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
5191
5136
|
}
|
|
5192
|
-
|
|
5193
|
-
return true;
|
|
5194
|
-
};
|
|
5195
|
-
const socket = prepareHeadroomSocket(fs12, ports.socketTrust, paths, ports.ownPid);
|
|
5196
|
-
if (socket.refused !== void 0) {
|
|
5197
|
-
return fail(socket.refused);
|
|
5137
|
+
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
5198
5138
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
if (!ensureInstalled()) {
|
|
5202
|
-
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5139
|
+
if (plan.kind === "pay-per-use" || fiveHourWindow === void 0 && sevenDayWindow === void 0) {
|
|
5140
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [...plan.kind === "pay-per-use" ? planReasons : ["no plan windows reported, so usage bills as extra usage"]] } };
|
|
5203
5141
|
}
|
|
5204
|
-
const
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
const orphanPid = previousState?.headroomPid;
|
|
5208
|
-
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
5209
|
-
ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
5210
|
-
ports.stopProcess(orphanPid);
|
|
5142
|
+
const basis = seven?.utilization !== void 0 ? { window: seven, windowMs: SEVEN_DAY_WINDOW_MS } : five?.utilization !== void 0 ? { window: five, windowMs: FIVE_HOUR_WINDOW_MS } : void 0;
|
|
5143
|
+
if (basis?.window.utilization === void 0) {
|
|
5144
|
+
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
5211
5145
|
}
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5146
|
+
const remaining = Math.max(0, 1 - basis.window.utilization);
|
|
5147
|
+
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5148
|
+
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
5149
|
+
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
5150
|
+
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5151
|
+
const burn = fiveHourBurn(member.records);
|
|
5152
|
+
return {
|
|
5153
|
+
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
5154
|
+
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
5155
|
+
...burn === void 0 ? {} : { burn }
|
|
5156
|
+
};
|
|
5157
|
+
}
|
|
5158
|
+
function rankPool(input) {
|
|
5159
|
+
const { members, nowMs } = input;
|
|
5160
|
+
const assessments = members.map((member) => assess(member, nowMs));
|
|
5161
|
+
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
5162
|
+
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
5163
|
+
const candidates = assessments.map((assessment) => {
|
|
5164
|
+
const { candidate, fiveHour, burn } = assessment;
|
|
5165
|
+
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
5166
|
+
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
5167
|
+
return { ...candidate, feasible: true };
|
|
5222
5168
|
}
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
const allowlist = allowlistOf(ports);
|
|
5227
|
-
const allowlistHash = hashAllowlist(allowlist);
|
|
5228
|
-
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
5229
|
-
const ownsNow = ownsState();
|
|
5230
|
-
if (!ownsNow && !superseded) {
|
|
5231
|
-
ports.log("agent-shim headroom supervisor: another supervisor now owns the state file; serving this daemon's own sessions until they end");
|
|
5169
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
5170
|
+
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
5171
|
+
return { ...candidate, feasible: true };
|
|
5232
5172
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
|
|
5249
|
-
}
|
|
5250
|
-
if (installedSource !== config.source && !ensureInstalled()) {
|
|
5251
|
-
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5252
|
-
}
|
|
5253
|
-
const pid = ports.spawnHeadroom(socketPath, allowlist, config.settings);
|
|
5254
|
-
const ready = await waitUntilReady(ports, socketPath);
|
|
5255
|
-
if (ready) {
|
|
5256
|
-
consecutiveFailures = 0;
|
|
5257
|
-
headroomPid = pid;
|
|
5258
|
-
runningHash = allowlistHash;
|
|
5259
|
-
runningSettingsHash = settingsHash;
|
|
5260
|
-
const recorded = writeOwnState({
|
|
5261
|
-
supervisorPid: ports.ownPid,
|
|
5262
|
-
headroomPid: pid,
|
|
5263
|
-
socketPath,
|
|
5264
|
-
version,
|
|
5265
|
-
allowlistHash,
|
|
5266
|
-
settingsHash,
|
|
5267
|
-
installedSource: config.source
|
|
5268
|
-
});
|
|
5269
|
-
if (recorded) {
|
|
5270
|
-
fs12.removeRecursive(paths.headroomLockFile);
|
|
5271
|
-
}
|
|
5272
|
-
idleSince = void 0;
|
|
5273
|
-
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on unix socket ${socketPath}`);
|
|
5274
|
-
} else {
|
|
5275
|
-
ports.stopProcess(pid);
|
|
5276
|
-
consecutiveFailures += 1;
|
|
5277
|
-
ports.log(
|
|
5278
|
-
`agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
5279
|
-
);
|
|
5280
|
-
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
5281
|
-
continue;
|
|
5282
|
-
}
|
|
5283
|
-
} else {
|
|
5284
|
-
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
5285
|
-
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
5286
|
-
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
5287
|
-
ports.stopProcess(headroomPid);
|
|
5288
|
-
headroomPid = void 0;
|
|
5289
|
-
runningHash = void 0;
|
|
5290
|
-
runningSettingsHash = void 0;
|
|
5291
|
-
continue;
|
|
5292
|
-
}
|
|
5293
|
-
}
|
|
5294
|
-
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
5295
|
-
if (sessions.length === 0) {
|
|
5296
|
-
idleSince ??= ports.now();
|
|
5297
|
-
const idleMs = ports.now() - idleSince;
|
|
5298
|
-
if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
|
|
5299
|
-
ports.log(
|
|
5300
|
-
`agent-shim headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
5301
|
-
);
|
|
5302
|
-
ports.stopProcess(headroomPid);
|
|
5303
|
-
writeOwnState({ version });
|
|
5304
|
-
return 0;
|
|
5305
|
-
}
|
|
5306
|
-
} else {
|
|
5307
|
-
idleSince = void 0;
|
|
5308
|
-
}
|
|
5309
|
-
}
|
|
5310
|
-
await ports.sleep(HEADROOM_POLL_MS);
|
|
5311
|
-
}
|
|
5312
|
-
}
|
|
5313
|
-
function currentHeadroomAllowlist(fs12, paths) {
|
|
5314
|
-
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
5315
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
5316
|
-
}
|
|
5317
|
-
function allowlistOf(ports) {
|
|
5318
|
-
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
5319
|
-
}
|
|
5320
|
-
async function waitUntilReady(ports, socketPath) {
|
|
5321
|
-
const deadline = ports.now() + HEADROOM_READY_TIMEOUT_MS;
|
|
5322
|
-
for (; ; ) {
|
|
5323
|
-
if (await ports.ready(socketPath)) {
|
|
5324
|
-
return true;
|
|
5325
|
-
}
|
|
5326
|
-
if (ports.now() >= deadline) {
|
|
5327
|
-
return false;
|
|
5328
|
-
}
|
|
5329
|
-
await ports.sleep(HEADROOM_POLL_MS);
|
|
5330
|
-
}
|
|
5331
|
-
}
|
|
5332
|
-
|
|
5333
|
-
// src/launcher/guard.ts
|
|
5334
|
-
var AMBIENT_CREDENTIAL_VARS = [
|
|
5335
|
-
"ANTHROPIC_API_KEY",
|
|
5336
|
-
"ANTHROPIC_AUTH_TOKEN",
|
|
5337
|
-
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
5338
|
-
"CLAUDE_CODE_USE_BEDROCK",
|
|
5339
|
-
"CLAUDE_CODE_USE_VERTEX",
|
|
5340
|
-
"CLAUDE_CODE_USE_FOUNDRY"
|
|
5341
|
-
];
|
|
5342
|
-
function detectAmbientCredential(env, injected) {
|
|
5343
|
-
for (const variable of AMBIENT_CREDENTIAL_VARS) {
|
|
5344
|
-
const value = env[variable];
|
|
5345
|
-
if (value === void 0 || value === "") {
|
|
5346
|
-
continue;
|
|
5347
|
-
}
|
|
5348
|
-
if (injected?.variable === variable && value === injected.token) {
|
|
5349
|
-
continue;
|
|
5173
|
+
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
5174
|
+
});
|
|
5175
|
+
const ordered = [...candidates].sort(
|
|
5176
|
+
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
5177
|
+
);
|
|
5178
|
+
const sticky = input.sticky;
|
|
5179
|
+
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
5180
|
+
const stickyCandidate = ordered[stickyIndex];
|
|
5181
|
+
if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
|
|
5182
|
+
const ageMs = nowMs - Date.parse(sticky.at);
|
|
5183
|
+
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
5184
|
+
if (input.resuming || warm) {
|
|
5185
|
+
const why = warm ? `used here ${formatAge(ageMs)} ago, prompt cache still warm` : "resuming a conversation started on this account";
|
|
5186
|
+
ordered.splice(stickyIndex, 1);
|
|
5187
|
+
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
5350
5188
|
}
|
|
5351
|
-
return { variable };
|
|
5352
|
-
}
|
|
5353
|
-
return void 0;
|
|
5354
|
-
}
|
|
5355
|
-
function formatAmbientCredentialGuardMessage(variable, identityName) {
|
|
5356
|
-
const identitySetCommand = identityName === void 0 ? "agent-shim identity set <name> --allow-ambient-credential" : `agent-shim identity set ${identityName} --allow-ambient-credential`;
|
|
5357
|
-
return [
|
|
5358
|
-
`error: ${variable} is set in the environment. This identity's isolated`,
|
|
5359
|
-
"credential would be bypassed \u2014 every identity authenticates as this same key",
|
|
5360
|
-
"while it's set. Unset it, or if this is deliberate, opt in per-launch with",
|
|
5361
|
-
"AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL=1, or persistently for this identity with",
|
|
5362
|
-
`\`${identitySetCommand}\`.`
|
|
5363
|
-
].join("\n");
|
|
5364
|
-
}
|
|
5365
|
-
function evaluateAmbientCredentialGuard(params) {
|
|
5366
|
-
const detected = detectAmbientCredential(params.env, params.injectedCredential);
|
|
5367
|
-
if (detected === void 0) {
|
|
5368
|
-
return { ok: true };
|
|
5369
|
-
}
|
|
5370
|
-
if (params.allowAmbientCredentialOverride || params.allowAmbientCredential || params.providerSelected === true) {
|
|
5371
|
-
return { ok: true };
|
|
5372
5189
|
}
|
|
5190
|
+
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
5191
|
+
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
5192
|
+
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
5373
5193
|
return {
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
message: formatAmbientCredentialGuardMessage(detected.variable, params.identityName)
|
|
5194
|
+
candidates: ordered,
|
|
5195
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
5377
5196
|
};
|
|
5378
5197
|
}
|
|
5379
5198
|
|
|
5380
|
-
// src/
|
|
5381
|
-
import
|
|
5382
|
-
import
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5199
|
+
// src/usage/poolPick.ts
|
|
5200
|
+
import path11 from "node:path";
|
|
5201
|
+
import { z as z8 } from "zod";
|
|
5202
|
+
|
|
5203
|
+
// src/usage/store.ts
|
|
5204
|
+
var DAY_MS2 = 864e5;
|
|
5205
|
+
var DAYS_IN_WEEK = 7;
|
|
5206
|
+
var USAGE_RETENTION_MS = DAYS_IN_WEEK * DAY_MS2;
|
|
5207
|
+
|
|
5208
|
+
// src/usage/poolPick.ts
|
|
5209
|
+
var SCHEMA_VERSION = 1;
|
|
5210
|
+
var PickEntrySchema = z8.strictObject({ identity: z8.string().min(1), at: z8.iso.datetime() });
|
|
5211
|
+
var PicksFileSchema = z8.strictObject({
|
|
5212
|
+
schemaVersion: z8.literal(SCHEMA_VERSION),
|
|
5213
|
+
picks: z8.record(z8.string().min(1), PickEntrySchema)
|
|
5214
|
+
});
|
|
5215
|
+
function errorMessage(error) {
|
|
5216
|
+
return error instanceof Error ? error.message : String(error);
|
|
5392
5217
|
}
|
|
5393
|
-
function
|
|
5394
|
-
|
|
5218
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5219
|
+
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5220
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5221
|
+
return identities.map((identity) => {
|
|
5222
|
+
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5223
|
+
try {
|
|
5224
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5225
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5226
|
+
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5227
|
+
} catch (error) {
|
|
5228
|
+
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
5229
|
+
return { identity, records, readError: errorMessage(error) };
|
|
5230
|
+
}
|
|
5231
|
+
throw error;
|
|
5232
|
+
}
|
|
5233
|
+
});
|
|
5395
5234
|
}
|
|
5396
|
-
function
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5235
|
+
function rankPoolFromStore(params) {
|
|
5236
|
+
return rankPool({
|
|
5237
|
+
members: loadPoolMembers(params.fs, params.paths, params.identities, params.nowMs),
|
|
5238
|
+
nowMs: params.nowMs,
|
|
5239
|
+
resuming: params.resuming,
|
|
5240
|
+
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5241
|
+
});
|
|
5242
|
+
}
|
|
5243
|
+
function readPicksFile(fs12, file) {
|
|
5244
|
+
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5245
|
+
const raw = fs12.readFileUtf8(file);
|
|
5246
|
+
if (raw === void 0) {
|
|
5247
|
+
return { file: empty };
|
|
5248
|
+
}
|
|
5249
|
+
let parsed;
|
|
5250
|
+
try {
|
|
5251
|
+
parsed = JSON.parse(raw);
|
|
5252
|
+
} catch {
|
|
5253
|
+
return { file: empty, problem: `${file} is not valid JSON` };
|
|
5400
5254
|
}
|
|
5401
|
-
|
|
5255
|
+
const result = PicksFileSchema.safeParse(parsed);
|
|
5256
|
+
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5402
5257
|
}
|
|
5403
|
-
function
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
configProfilesDir: path12.join(root, "config-profiles"),
|
|
5408
|
-
providersDir: path12.join(root, "providers"),
|
|
5409
|
-
directoryRulesFile: path12.join(root, "directory-rules.json"),
|
|
5410
|
-
activeIdentityFile: path12.join(root, "active-identity"),
|
|
5411
|
-
globalConfigFile: path12.join(root, "config.json"),
|
|
5412
|
-
categoriesLocalFile: path12.join(root, "categories.local.json"),
|
|
5413
|
-
claudeShimFile: path12.join(root, "claude-shim.json"),
|
|
5414
|
-
headroomDir: path12.join(root, "headroom"),
|
|
5415
|
-
headroomStateFile: path12.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
5416
|
-
headroomSocketDir: path12.join(root, "headroom", "run"),
|
|
5417
|
-
headroomLockFile: path12.join(root, "headroom", "start.lock"),
|
|
5418
|
-
headroomSessionsDir: path12.join(root, "headroom", "sessions"),
|
|
5419
|
-
logsDir: path12.join(root, "logs"),
|
|
5420
|
-
headroomLogPath: path12.join(root, "logs", "headroom.log"),
|
|
5421
|
-
frontdoorDir: path12.join(root, "frontdoor"),
|
|
5422
|
-
frontdoorCaDir: path12.join(root, "frontdoor", "ca"),
|
|
5423
|
-
frontdoorCaCertFile: path12.join(root, "frontdoor", "ca", "ca.pem"),
|
|
5424
|
-
frontdoorCaKeyFile: path12.join(root, "frontdoor", "ca", "ca.key"),
|
|
5425
|
-
frontdoorCaBundlesDir: path12.join(root, "frontdoor", "ca", "bundles"),
|
|
5426
|
-
frontdoorStateFile: path12.join(root, "frontdoor", "state.json"),
|
|
5427
|
-
frontdoorLockFile: path12.join(root, "frontdoor", "start.lock"),
|
|
5428
|
-
frontdoorControlTokenFile: path12.join(root, "frontdoor", "control-token"),
|
|
5429
|
-
frontdoorSessionsDir: path12.join(root, "frontdoor", "sessions"),
|
|
5430
|
-
frontdoorLogPath: path12.join(root, "logs", "frontdoor.log"),
|
|
5431
|
-
usageDir: path12.join(root, "usage"),
|
|
5432
|
-
usageLogDir: path12.join(root, "usage", "log"),
|
|
5433
|
-
usageSnapshotsDir: path12.join(root, "usage", "snapshots"),
|
|
5434
|
-
usagePicksFile: path12.join(root, "usage", "picks.json")
|
|
5435
|
-
};
|
|
5258
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5259
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5260
|
+
const entry = read.file.picks[directory];
|
|
5261
|
+
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5436
5262
|
}
|
|
5437
|
-
function
|
|
5438
|
-
|
|
5263
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5264
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5265
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5266
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5267
|
+
fs12.mkdirPrivate(path11.dirname(picksFile));
|
|
5268
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5269
|
+
`);
|
|
5439
5270
|
}
|
|
5440
5271
|
|
|
5441
|
-
// src/identityStore.ts
|
|
5442
|
-
import fs5 from "node:fs";
|
|
5443
|
-
import path15 from "node:path";
|
|
5444
|
-
|
|
5445
5272
|
// src/launcher/identity.ts
|
|
5446
|
-
import
|
|
5273
|
+
import path12 from "node:path";
|
|
5447
5274
|
function poolNameOf(selector) {
|
|
5448
5275
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
5449
5276
|
}
|
|
@@ -5492,1445 +5319,1775 @@ function decideConfigProfile(params) {
|
|
|
5492
5319
|
return { source: "none" };
|
|
5493
5320
|
}
|
|
5494
5321
|
function loadIdentity(identitiesDir, name, fs12) {
|
|
5495
|
-
return loadConfigFile(
|
|
5322
|
+
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
5496
5323
|
}
|
|
5497
5324
|
|
|
5498
|
-
// src/
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
var
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
this.name = "ProfileNotFoundError";
|
|
5506
|
-
}
|
|
5507
|
-
profileName;
|
|
5508
|
-
};
|
|
5509
|
-
var ProfileAlreadyExistsError = class extends CliError {
|
|
5510
|
-
constructor(profileName) {
|
|
5511
|
-
super(`A configuration profile named "${profileName}" already exists.`);
|
|
5512
|
-
this.profileName = profileName;
|
|
5513
|
-
this.name = "ProfileAlreadyExistsError";
|
|
5514
|
-
}
|
|
5515
|
-
profileName;
|
|
5516
|
-
};
|
|
5517
|
-
var InvalidCategoryNameError = class extends CliError {
|
|
5518
|
-
constructor(categoryName) {
|
|
5519
|
-
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
5520
|
-
this.categoryName = categoryName;
|
|
5521
|
-
this.name = "InvalidCategoryNameError";
|
|
5522
|
-
}
|
|
5523
|
-
categoryName;
|
|
5524
|
-
};
|
|
5525
|
-
function profileJsonPath(paths, name) {
|
|
5526
|
-
return path14.join(paths.configProfilesDir, `${name}.json`);
|
|
5325
|
+
// src/launcher/pool.ts
|
|
5326
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5327
|
+
var TERMINATOR = "--";
|
|
5328
|
+
var REASONS_LOGGED = 3;
|
|
5329
|
+
function splitMembers(pool, paths, fs12) {
|
|
5330
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5331
|
+
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5527
5332
|
}
|
|
5528
|
-
function
|
|
5529
|
-
|
|
5333
|
+
function isResuming(passthrough) {
|
|
5334
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5335
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5530
5336
|
}
|
|
5531
|
-
function
|
|
5532
|
-
|
|
5533
|
-
|
|
5337
|
+
function resolvePoolLaunch(params) {
|
|
5338
|
+
const { poolName, log } = params;
|
|
5339
|
+
const pool = params.pools?.[poolName];
|
|
5340
|
+
if (pool === void 0) {
|
|
5341
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5534
5342
|
}
|
|
5535
|
-
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
}
|
|
5539
|
-
function createProfile(paths, name, extendsList, description) {
|
|
5540
|
-
if (profileExists(paths, name)) {
|
|
5541
|
-
throw new ProfileAlreadyExistsError(name);
|
|
5343
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5344
|
+
for (const name of missing) {
|
|
5345
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5542
5346
|
}
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
...description === void 0 ? {} : { description },
|
|
5546
|
-
...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
|
|
5547
|
-
});
|
|
5548
|
-
if (!parsed.success) {
|
|
5549
|
-
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
5347
|
+
if (identities.length === 0) {
|
|
5348
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5550
5349
|
}
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
if (!fs4.existsSync(paths.configProfilesDir)) {
|
|
5556
|
-
return [];
|
|
5350
|
+
const resuming = isResuming(params.passthrough);
|
|
5351
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5352
|
+
if (stickyRead.problem !== void 0) {
|
|
5353
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5557
5354
|
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
const
|
|
5562
|
-
if (
|
|
5563
|
-
|
|
5355
|
+
for (; ; ) {
|
|
5356
|
+
const nowMs = params.now();
|
|
5357
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5358
|
+
const { pick, earliestReturn } = ranking;
|
|
5359
|
+
if (pick !== void 0) {
|
|
5360
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5361
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5564
5362
|
}
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
}
|
|
5568
|
-
function globalConfigPath(paths) {
|
|
5569
|
-
return paths.globalConfigFile;
|
|
5570
|
-
}
|
|
5571
|
-
function readGlobalConfig(paths) {
|
|
5572
|
-
return readJson(globalConfigPath(paths), GlobalConfigSchema);
|
|
5573
|
-
}
|
|
5574
|
-
function setGlobalDefaultProfile(paths, name) {
|
|
5575
|
-
return applyPatch(
|
|
5576
|
-
globalConfigPath(paths),
|
|
5577
|
-
GlobalConfigSchema,
|
|
5578
|
-
{ defaultConfigProfile: name },
|
|
5579
|
-
{ defaults: {} }
|
|
5580
|
-
);
|
|
5581
|
-
}
|
|
5582
|
-
function validateCategoryNames(patch) {
|
|
5583
|
-
for (const key of Object.keys(patch)) {
|
|
5584
|
-
if (!isOverridableCategory(key)) {
|
|
5585
|
-
throw new InvalidCategoryNameError(key);
|
|
5363
|
+
if (earliestReturn === void 0) {
|
|
5364
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5586
5365
|
}
|
|
5366
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5367
|
+
if (!params.wait) {
|
|
5368
|
+
return {
|
|
5369
|
+
ok: false,
|
|
5370
|
+
message: `agent-shim: every member of pool "${poolName}" is refused right now; the earliest, ${earliestReturn.identity}, returns at ${until} (in ${formatAge(earliestReturn.atMs - nowMs)}). Pass --wait to sleep until then.`
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5374
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5587
5375
|
}
|
|
5588
5376
|
}
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
|
|
5602
|
-
}
|
|
5603
|
-
function setProfileLaunchFlags(paths, name, patch) {
|
|
5604
|
-
requireProfileExists(paths, name);
|
|
5605
|
-
const existing = readProfile(paths, name) ?? {};
|
|
5606
|
-
const mergedLaunch = { ...existing.launch, ...patch };
|
|
5607
|
-
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
5608
|
-
// Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
|
|
5609
|
-
launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
|
|
5610
|
-
});
|
|
5377
|
+
|
|
5378
|
+
// src/poolPickReport.ts
|
|
5379
|
+
function candidateView(candidate) {
|
|
5380
|
+
return {
|
|
5381
|
+
identity: candidate.identity,
|
|
5382
|
+
class: candidate.class,
|
|
5383
|
+
...candidate.score === void 0 ? {} : { score: candidate.score },
|
|
5384
|
+
feasible: candidate.feasible,
|
|
5385
|
+
...candidate.blockedUntilMs === void 0 ? {} : { blockedUntil: new Date(candidate.blockedUntilMs).toISOString() },
|
|
5386
|
+
plan: candidate.plan,
|
|
5387
|
+
reasons: [...candidate.reasons]
|
|
5388
|
+
};
|
|
5611
5389
|
}
|
|
5612
|
-
function
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5390
|
+
function collectPoolPick(params) {
|
|
5391
|
+
const { present, missing } = splitMembers(params.pool, params.paths, params.fs);
|
|
5392
|
+
const sticky = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.directory);
|
|
5393
|
+
const ranking = rankPoolFromStore({
|
|
5394
|
+
fs: params.usageFs,
|
|
5395
|
+
paths: params.paths,
|
|
5396
|
+
identities: present,
|
|
5397
|
+
nowMs: params.nowMs,
|
|
5398
|
+
resuming: false,
|
|
5399
|
+
...sticky.sticky === void 0 ? {} : { sticky: sticky.sticky }
|
|
5617
5400
|
});
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5401
|
+
return {
|
|
5402
|
+
pool: params.poolName,
|
|
5403
|
+
directory: params.directory,
|
|
5404
|
+
...ranking.pick === void 0 ? {} : { pick: ranking.pick.identity },
|
|
5405
|
+
candidates: ranking.candidates.map(candidateView),
|
|
5406
|
+
missing: [...missing],
|
|
5407
|
+
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
5408
|
+
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
5409
|
+
};
|
|
5622
5410
|
}
|
|
5623
5411
|
|
|
5624
|
-
// src/
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
const pool = readPools(paths)[name];
|
|
5654
|
-
if (pool === void 0) {
|
|
5655
|
-
throw new PoolNotFoundError(name);
|
|
5656
|
-
}
|
|
5657
|
-
return pool;
|
|
5658
|
-
}
|
|
5659
|
-
function writePools(paths, pools) {
|
|
5660
|
-
applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
|
|
5661
|
-
}
|
|
5662
|
-
function addPool(paths, name, identities) {
|
|
5663
|
-
if (!PoolNameSchema.safeParse(name).success) {
|
|
5664
|
-
throw new InvalidPoolNameError(name);
|
|
5665
|
-
}
|
|
5666
|
-
const pools = readPools(paths);
|
|
5667
|
-
if (pools[name] !== void 0) {
|
|
5668
|
-
throw new PoolAlreadyExistsError(name);
|
|
5669
|
-
}
|
|
5670
|
-
const pool = { identities: [...identities] };
|
|
5671
|
-
writePools(paths, { ...pools, [name]: pool });
|
|
5672
|
-
return pool;
|
|
5673
|
-
}
|
|
5674
|
-
function setPool(paths, name, identities) {
|
|
5675
|
-
requirePool(paths, name);
|
|
5676
|
-
const pool = { identities: [...identities] };
|
|
5677
|
-
writePools(paths, { ...readPools(paths), [name]: pool });
|
|
5678
|
-
return pool;
|
|
5679
|
-
}
|
|
5680
|
-
function removePool(paths, name) {
|
|
5681
|
-
requirePool(paths, name);
|
|
5682
|
-
const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
|
|
5683
|
-
writePools(paths, remaining);
|
|
5684
|
-
}
|
|
5412
|
+
// src/usage/pickReportSchema.ts
|
|
5413
|
+
import { z as z9 } from "zod";
|
|
5414
|
+
var PoolPickCandidateViewSchema = z9.strictObject({
|
|
5415
|
+
identity: z9.string(),
|
|
5416
|
+
/** In pick order: `scored` has usable quota data, `unknown` has none, `pay-per-use` bills by use, `ineligible` is refused right now. */
|
|
5417
|
+
class: z9.enum(["scored", "unknown", "pay-per-use", "ineligible"]),
|
|
5418
|
+
/** Plan-size-weighted remaining quota per hour until its reset; present for `scored` only. */
|
|
5419
|
+
score: z9.number().optional(),
|
|
5420
|
+
/** False when the five-hour window would run dry, at the observed pace, before it resets. */
|
|
5421
|
+
feasible: z9.boolean(),
|
|
5422
|
+
/** When an `ineligible` member can be used again, as an ISO instant. */
|
|
5423
|
+
blockedUntil: z9.iso.datetime().optional(),
|
|
5424
|
+
plan: PlanClassSchema,
|
|
5425
|
+
/** The facts behind the class and score, in the order they matter. */
|
|
5426
|
+
reasons: z9.array(z9.string())
|
|
5427
|
+
});
|
|
5428
|
+
var PoolPickReportSchema = z9.strictObject({
|
|
5429
|
+
pool: z9.string(),
|
|
5430
|
+
directory: z9.string(),
|
|
5431
|
+
/** The member a launch would run as, absent when every member is refused. */
|
|
5432
|
+
pick: z9.string().optional(),
|
|
5433
|
+
candidates: z9.array(PoolPickCandidateViewSchema),
|
|
5434
|
+
/** Members that name an identity that does not exist; a launch skips them. */
|
|
5435
|
+
missing: z9.array(z9.string()),
|
|
5436
|
+
/** When nothing can be picked, the soonest any member returns. */
|
|
5437
|
+
earliestReturn: z9.strictObject({ identity: z9.string(), at: z9.iso.datetime() }).optional(),
|
|
5438
|
+
/** Why the last-pick record could not be read, when that is the case. */
|
|
5439
|
+
stickyProblem: z9.string().optional()
|
|
5440
|
+
});
|
|
5685
5441
|
|
|
5686
|
-
// src/
|
|
5687
|
-
var
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5442
|
+
// src/headroom/ensure.ts
|
|
5443
|
+
var HEADROOM_START_TIMEOUT_MS = 12e4;
|
|
5444
|
+
var HEADROOM_LAUNCHER_POLL_MS = 250;
|
|
5445
|
+
var HeadroomStartError = class extends CliError {
|
|
5446
|
+
constructor(message) {
|
|
5447
|
+
super(message);
|
|
5448
|
+
this.name = "HeadroomStartError";
|
|
5692
5449
|
}
|
|
5693
|
-
name;
|
|
5694
5450
|
};
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5451
|
+
function ensureHeadroom(params) {
|
|
5452
|
+
const { paths, ports } = params;
|
|
5453
|
+
const deadline = ports.now() + HEADROOM_START_TIMEOUT_MS;
|
|
5454
|
+
let spawnedSupervisor = false;
|
|
5455
|
+
let awaitingAllowlist = false;
|
|
5456
|
+
for (; ; ) {
|
|
5457
|
+
const state = readHeadroomState(ports.fs, paths.headroomStateFile);
|
|
5458
|
+
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
5459
|
+
const daemonUp = state.socketPath !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
5460
|
+
if (daemonUp && state.socketPath !== void 0) {
|
|
5461
|
+
const required = params.requiredAllowlistHash?.();
|
|
5462
|
+
if (required === void 0 || state.allowlistHash === required) {
|
|
5463
|
+
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
5464
|
+
return { socketPath: state.socketPath };
|
|
5465
|
+
}
|
|
5466
|
+
const holders = sessionsForSupervisor(ports.fs, paths.headroomSessionsDir, state.supervisorPid).filter((session) => ports.isRunning(session.pid));
|
|
5467
|
+
if (holders.length > 0) {
|
|
5468
|
+
throw new HeadroomStartError(
|
|
5469
|
+
`the running headroom daemon was started before this launch's front door address and would refuse every request this launch sends through it. It restarts once the launches using it end (pid ${holders.map((session) => String(session.pid)).join(", ")}); end them and launch again, or launch with --no-headroom.`
|
|
5470
|
+
);
|
|
5471
|
+
}
|
|
5472
|
+
awaitingAllowlist = true;
|
|
5473
|
+
}
|
|
5474
|
+
if (state.lastError !== void 0) {
|
|
5475
|
+
throw new HeadroomStartError(
|
|
5476
|
+
`the headroom daemon reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.headroomLogPath})`
|
|
5477
|
+
);
|
|
5478
|
+
}
|
|
5479
|
+
} else if (!spawnedSupervisor) {
|
|
5480
|
+
ports.fs.mkdirp(paths.headroomDir);
|
|
5481
|
+
const created = ports.fs.writeFileExclusive(
|
|
5482
|
+
paths.headroomLockFile,
|
|
5483
|
+
`${JSON.stringify({ pid: params.launcherPid, at: ports.now() })}
|
|
5484
|
+
`
|
|
5485
|
+
);
|
|
5486
|
+
if (created) {
|
|
5487
|
+
ports.spawnSupervisor(paths);
|
|
5488
|
+
spawnedSupervisor = true;
|
|
5489
|
+
} else {
|
|
5490
|
+
const lock = readStartLock(ports.fs, paths.headroomLockFile);
|
|
5491
|
+
if (lock === void 0 || !ports.isRunning(lock.pid)) {
|
|
5492
|
+
ports.fs.removeRecursive(paths.headroomLockFile);
|
|
5493
|
+
continue;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
if (ports.now() >= deadline) {
|
|
5498
|
+
const lastError = state?.lastError;
|
|
5499
|
+
throw new HeadroomStartError(
|
|
5500
|
+
`the headroom daemon did not become ready within ${String(HEADROOM_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (awaitingAllowlist ? " (the running daemon's allowlist does not admit this launch's front door, and it did not restart)" : "") + `. Daemon log: ${paths.headroomLogPath}`
|
|
5501
|
+
);
|
|
5502
|
+
}
|
|
5503
|
+
ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
|
|
5700
5504
|
}
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5507
|
+
// src/headroom/settings.ts
|
|
5508
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
5509
|
+
function settingsOf(config) {
|
|
5510
|
+
return {
|
|
5511
|
+
...config.mode === void 0 ? {} : { mode: config.mode },
|
|
5512
|
+
...config.targetRatio === void 0 ? {} : { targetRatio: config.targetRatio },
|
|
5513
|
+
...config.ccr === void 0 ? {} : { ccr: config.ccr },
|
|
5514
|
+
...config.rolloutChannel === void 0 ? {} : { rolloutChannel: config.rolloutChannel },
|
|
5515
|
+
...config.interceptToolResults === void 0 ? {} : { interceptToolResults: config.interceptToolResults },
|
|
5516
|
+
...config.readMaturation === void 0 ? {} : { readMaturation: config.readMaturation }
|
|
5517
|
+
};
|
|
5518
|
+
}
|
|
5519
|
+
function settingsArgs(settings) {
|
|
5520
|
+
return [
|
|
5521
|
+
...settings.mode === void 0 ? [] : ["--mode", settings.mode],
|
|
5522
|
+
...settings.targetRatio === void 0 ? [] : ["--target-ratio", String(settings.targetRatio)],
|
|
5523
|
+
...settings.ccr === "lossless" ? ["--lossless"] : [],
|
|
5524
|
+
...settings.ccr === "none" ? ["--no-ccr"] : [],
|
|
5525
|
+
...settings.interceptToolResults === true ? ["--intercept-tool-results"] : [],
|
|
5526
|
+
...settings.readMaturation === true ? ["--read-maturation"] : []
|
|
5527
|
+
];
|
|
5528
|
+
}
|
|
5529
|
+
function settingsEnv(settings) {
|
|
5530
|
+
return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
|
|
5531
|
+
}
|
|
5532
|
+
function hashSettings(settings) {
|
|
5533
|
+
return createHash4("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
|
|
5534
|
+
}
|
|
5535
|
+
|
|
5536
|
+
// src/headroom/socket.ts
|
|
5537
|
+
import path13 from "node:path";
|
|
5538
|
+
var GROUP_OR_OTHER_BITS = 63;
|
|
5539
|
+
var OCTAL_RADIX = 8;
|
|
5540
|
+
var MODE_DIGITS = 4;
|
|
5541
|
+
var HEADROOM_SOCKET_SUFFIX = ".sock";
|
|
5542
|
+
var HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
|
|
5543
|
+
var UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
5544
|
+
var PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
|
|
5545
|
+
function headroomSocketPath(paths, supervisorPid) {
|
|
5546
|
+
return path13.join(paths.headroomSocketDir, `${String(supervisorPid)}${HEADROOM_SOCKET_SUFFIX}`);
|
|
5547
|
+
}
|
|
5548
|
+
function platformRefusal(platform) {
|
|
5549
|
+
return PLATFORMS_WITHOUT_UNIX_SOCKETS.includes(platform) ? `headroom routing needs a unix domain socket, which headroom cannot serve on ${platform}; agent-shim never falls back to a TCP port for this hop` : void 0;
|
|
5550
|
+
}
|
|
5551
|
+
function ownerOnlyRefusal(label, target, stat, expected, uid) {
|
|
5552
|
+
if (stat === void 0) {
|
|
5553
|
+
return `the headroom ${label} ${target} does not exist`;
|
|
5710
5554
|
}
|
|
5711
|
-
|
|
5712
|
-
};
|
|
5713
|
-
|
|
5714
|
-
|
|
5555
|
+
if (stat.kind === "symlink") {
|
|
5556
|
+
return `the headroom ${label} ${target} is a symlink; it must be the real ${expected === "dir" ? "directory" : "socket"}`;
|
|
5557
|
+
}
|
|
5558
|
+
if (stat.kind !== expected) {
|
|
5559
|
+
return `the headroom ${label} ${target} is not a ${expected === "dir" ? "directory" : "socket"}`;
|
|
5560
|
+
}
|
|
5561
|
+
if (stat.uid !== uid) {
|
|
5562
|
+
return `the headroom ${label} ${target} is owned by uid ${String(stat.uid)}, not by this user (uid ${String(uid)})`;
|
|
5563
|
+
}
|
|
5564
|
+
if ((stat.mode & GROUP_OR_OTHER_BITS) !== 0) {
|
|
5565
|
+
return `the headroom ${label} ${target} has mode ${stat.mode.toString(OCTAL_RADIX).padStart(MODE_DIGITS, "0")}, which lets other users reach it; it must be accessible to its owner only`;
|
|
5566
|
+
}
|
|
5567
|
+
return void 0;
|
|
5715
5568
|
}
|
|
5716
|
-
function
|
|
5717
|
-
|
|
5569
|
+
function prepareHeadroomSocket(fs12, ports, paths, supervisorPid) {
|
|
5570
|
+
const unsupported = platformRefusal(ports.platform);
|
|
5571
|
+
if (unsupported !== void 0) {
|
|
5572
|
+
return { refused: unsupported };
|
|
5573
|
+
}
|
|
5574
|
+
const socketPath = headroomSocketPath(paths, supervisorPid);
|
|
5575
|
+
const length = Buffer.byteLength(socketPath, "utf8");
|
|
5576
|
+
if (length > UNIX_SOCKET_PATH_MAX_BYTES) {
|
|
5577
|
+
return {
|
|
5578
|
+
refused: `the headroom socket path ${socketPath} is ${String(length)} bytes, longer than the ${String(UNIX_SOCKET_PATH_MAX_BYTES)} a unix socket path can hold; set AGENT_SHIM_HOME to a shorter directory`
|
|
5579
|
+
};
|
|
5580
|
+
}
|
|
5581
|
+
if (ports.lstat(paths.headroomSocketDir)?.kind === "symlink") {
|
|
5582
|
+
return { refused: `the headroom socket directory ${paths.headroomSocketDir} is a symlink; it must be the real directory` };
|
|
5583
|
+
}
|
|
5584
|
+
fs12.mkdirPrivate(paths.headroomSocketDir);
|
|
5585
|
+
const refusal = ownerOnlyRefusal("socket directory", paths.headroomSocketDir, ports.lstat(paths.headroomSocketDir), "dir", ports.currentUid());
|
|
5586
|
+
return refusal === void 0 ? { socketPath } : { refused: refusal };
|
|
5718
5587
|
}
|
|
5719
|
-
function
|
|
5720
|
-
|
|
5588
|
+
function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
|
|
5589
|
+
const removed = [];
|
|
5590
|
+
for (const name of fs12.readdir(paths.headroomSocketDir)) {
|
|
5591
|
+
const pid = HEADROOM_SOCKET_NAME.exec(name)?.[1];
|
|
5592
|
+
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
5593
|
+
continue;
|
|
5594
|
+
}
|
|
5595
|
+
fs12.removeRecursive(path13.join(paths.headroomSocketDir, name));
|
|
5596
|
+
removed.push(name);
|
|
5597
|
+
}
|
|
5598
|
+
return removed;
|
|
5721
5599
|
}
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5600
|
+
|
|
5601
|
+
// src/headroom/source.ts
|
|
5602
|
+
import { z as z10 } from "zod";
|
|
5603
|
+
var GIT_SPEC_RE = /git\+[^@\s]+(?:@([^\s#]+))?/;
|
|
5604
|
+
var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
5605
|
+
var DirectUrlSchema = z10.object({ vcs_info: z10.object({ commit_id: z10.string() }) });
|
|
5606
|
+
function isMovingGitSource(spec) {
|
|
5607
|
+
const match = GIT_SPEC_RE.exec(spec);
|
|
5608
|
+
if (match === null) {
|
|
5609
|
+
return false;
|
|
5725
5610
|
}
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5728
|
-
|
|
5611
|
+
const ref = match[1];
|
|
5612
|
+
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
5613
|
+
}
|
|
5614
|
+
function pinnedGitCommit(spec) {
|
|
5615
|
+
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
5616
|
+
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5619
|
+
// src/headroom/supervisor.ts
|
|
5620
|
+
function resolveSupervisorConfig(configured) {
|
|
5621
|
+
return {
|
|
5622
|
+
source: configured.source ?? HEADROOM_DEFAULT_SOURCE,
|
|
5623
|
+
idleShutdownMinutes: configured.idleShutdownMinutes ?? HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES,
|
|
5624
|
+
settings: settingsOf(configured)
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
var HEADROOM_BACKOFF_BASE_MS = 500;
|
|
5628
|
+
var HEADROOM_BACKOFF_CAP_MS = 3e4;
|
|
5629
|
+
var HEADROOM_START_RETRY_BUDGET = 5;
|
|
5630
|
+
var HEADROOM_READY_TIMEOUT_MS = 15e3;
|
|
5631
|
+
var HEADROOM_POLL_MS = 1e3;
|
|
5632
|
+
var MS_PER_MINUTE2 = 6e4;
|
|
5633
|
+
function backoffForAttempt(attempt) {
|
|
5634
|
+
return Math.min(HEADROOM_BACKOFF_CAP_MS, HEADROOM_BACKOFF_BASE_MS * 2 ** (attempt - 1));
|
|
5635
|
+
}
|
|
5636
|
+
function versionSatisfies(version, source) {
|
|
5637
|
+
if (version === void 0) {
|
|
5638
|
+
return false;
|
|
5639
|
+
}
|
|
5640
|
+
const specifier = /==|>=|<=|!=|>|<|~=/.exec(source);
|
|
5641
|
+
if (specifier === null) {
|
|
5642
|
+
return true;
|
|
5643
|
+
}
|
|
5644
|
+
const installed = parseVersion(version);
|
|
5645
|
+
const wanted = parseVersion(source.slice(specifier.index + specifier[0].length));
|
|
5646
|
+
if (installed === void 0 || wanted === void 0) {
|
|
5647
|
+
return true;
|
|
5648
|
+
}
|
|
5649
|
+
const cmp = compareVersions(installed, wanted);
|
|
5650
|
+
switch (specifier[0]) {
|
|
5651
|
+
case "==":
|
|
5652
|
+
return cmp === 0;
|
|
5653
|
+
case "!=":
|
|
5654
|
+
return cmp !== 0;
|
|
5655
|
+
case ">=":
|
|
5656
|
+
return cmp >= 0;
|
|
5657
|
+
case "<=":
|
|
5658
|
+
return cmp <= 0;
|
|
5659
|
+
case ">":
|
|
5660
|
+
return cmp > 0;
|
|
5661
|
+
case "<":
|
|
5662
|
+
return cmp < 0;
|
|
5663
|
+
case "~=":
|
|
5664
|
+
return compareVersions(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
5665
|
+
default:
|
|
5666
|
+
return true;
|
|
5729
5667
|
}
|
|
5730
|
-
writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
|
|
5731
|
-
return parsed.data;
|
|
5732
5668
|
}
|
|
5733
|
-
function
|
|
5734
|
-
const
|
|
5735
|
-
if (
|
|
5736
|
-
|
|
5737
|
-
}
|
|
5738
|
-
|
|
5669
|
+
function parseVersion(input) {
|
|
5670
|
+
const match = /(\d+)(\.\d+)*/.exec(input);
|
|
5671
|
+
if (match === null) {
|
|
5672
|
+
return void 0;
|
|
5673
|
+
}
|
|
5674
|
+
return match[0].split(".").map((part) => Number.parseInt(part, 10));
|
|
5675
|
+
}
|
|
5676
|
+
function compareVersions(a, b) {
|
|
5677
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
5678
|
+
const left = a[index] ?? 0;
|
|
5679
|
+
const right = b[index] ?? 0;
|
|
5680
|
+
if (left !== right) {
|
|
5681
|
+
return left - right;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
return 0;
|
|
5685
|
+
}
|
|
5686
|
+
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
5687
|
+
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
5688
|
+
const { fs: fs12, paths } = ports;
|
|
5689
|
+
let config = initialConfig;
|
|
5690
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
5691
|
+
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
5692
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
5693
|
+
return 0;
|
|
5694
|
+
}
|
|
5695
|
+
let installedSource = previousState?.installedSource;
|
|
5696
|
+
const ownsState = () => {
|
|
5697
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
5698
|
+
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
5699
|
+
};
|
|
5700
|
+
const writeOwnState = (state) => {
|
|
5701
|
+
if (!ownsState()) {
|
|
5702
|
+
return false;
|
|
5703
|
+
}
|
|
5704
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
5705
|
+
return true;
|
|
5706
|
+
};
|
|
5707
|
+
const fail = (message) => {
|
|
5708
|
+
if (writeOwnState({ lastError: message })) {
|
|
5709
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
5710
|
+
}
|
|
5711
|
+
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
5712
|
+
return 1;
|
|
5713
|
+
};
|
|
5714
|
+
const ensureInstalled = () => {
|
|
5715
|
+
const version2 = ports.headroomVersion();
|
|
5716
|
+
const versionOk = version2 !== void 0 && versionSatisfies(version2, config.source);
|
|
5717
|
+
const pinned = pinnedGitCommit(config.source);
|
|
5718
|
+
const commitOk = pinned === void 0 || ports.installedCommit() === pinned;
|
|
5719
|
+
if (versionOk && commitOk && (installedSource === void 0 || installedSource === config.source)) {
|
|
5720
|
+
installedSource = config.source;
|
|
5721
|
+
return true;
|
|
5722
|
+
}
|
|
5723
|
+
ports.log(`agent-shim headroom supervisor: installing headroom from ${config.source}`);
|
|
5724
|
+
const result = ports.install(config.source);
|
|
5725
|
+
if (!result.ok) {
|
|
5726
|
+
return false;
|
|
5727
|
+
}
|
|
5728
|
+
installedSource = config.source;
|
|
5729
|
+
return true;
|
|
5730
|
+
};
|
|
5731
|
+
const socket = prepareHeadroomSocket(fs12, ports.socketTrust, paths, ports.ownPid);
|
|
5732
|
+
if (socket.refused !== void 0) {
|
|
5733
|
+
return fail(socket.refused);
|
|
5734
|
+
}
|
|
5735
|
+
const { socketPath } = socket;
|
|
5736
|
+
pruneStaleHeadroomSockets(fs12, paths, ports.isRunning);
|
|
5737
|
+
if (!ensureInstalled()) {
|
|
5738
|
+
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5739
|
+
}
|
|
5740
|
+
const version = ports.headroomVersion() ?? "unknown";
|
|
5741
|
+
writeOwnState({ supervisorPid: ports.ownPid, version, installedSource });
|
|
5742
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
5743
|
+
const orphanPid = previousState?.headroomPid;
|
|
5744
|
+
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
5745
|
+
ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
5746
|
+
ports.stopProcess(orphanPid);
|
|
5747
|
+
}
|
|
5748
|
+
let headroomPid;
|
|
5749
|
+
let runningHash;
|
|
5750
|
+
let runningSettingsHash;
|
|
5751
|
+
let consecutiveFailures = 0;
|
|
5752
|
+
let idleSince;
|
|
5753
|
+
let superseded = false;
|
|
5754
|
+
let ticks = 0;
|
|
5755
|
+
for (; ; ) {
|
|
5756
|
+
if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
|
|
5757
|
+
return HEADROOM_SUPERVISOR_STILL_RUNNING;
|
|
5758
|
+
}
|
|
5759
|
+
ticks += 1;
|
|
5760
|
+
config = ports.readConfig();
|
|
5761
|
+
const settingsHash = hashSettings(config.settings);
|
|
5762
|
+
const allowlist = allowlistOf(ports);
|
|
5763
|
+
const allowlistHash = hashAllowlist(allowlist);
|
|
5764
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
5765
|
+
const ownsNow = ownsState();
|
|
5766
|
+
if (!ownsNow && !superseded) {
|
|
5767
|
+
ports.log("agent-shim headroom supervisor: another supervisor now owns the state file; serving this daemon's own sessions until they end");
|
|
5768
|
+
}
|
|
5769
|
+
superseded = !ownsNow;
|
|
5770
|
+
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
5771
|
+
if (headroomPid === void 0 || crashed) {
|
|
5772
|
+
if (crashed) {
|
|
5773
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(headroomPid)} died`);
|
|
5774
|
+
headroomPid = void 0;
|
|
5775
|
+
runningHash = void 0;
|
|
5776
|
+
runningSettingsHash = void 0;
|
|
5777
|
+
writeOwnState({
|
|
5778
|
+
supervisorPid: ports.ownPid,
|
|
5779
|
+
version,
|
|
5780
|
+
installedSource
|
|
5781
|
+
});
|
|
5782
|
+
}
|
|
5783
|
+
if (consecutiveFailures >= HEADROOM_START_RETRY_BUDGET) {
|
|
5784
|
+
return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
|
|
5785
|
+
}
|
|
5786
|
+
if (installedSource !== config.source && !ensureInstalled()) {
|
|
5787
|
+
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5788
|
+
}
|
|
5789
|
+
const pid = ports.spawnHeadroom(socketPath, allowlist, config.settings);
|
|
5790
|
+
const ready = await waitUntilReady(ports, socketPath);
|
|
5791
|
+
if (ready) {
|
|
5792
|
+
consecutiveFailures = 0;
|
|
5793
|
+
headroomPid = pid;
|
|
5794
|
+
runningHash = allowlistHash;
|
|
5795
|
+
runningSettingsHash = settingsHash;
|
|
5796
|
+
const recorded = writeOwnState({
|
|
5797
|
+
supervisorPid: ports.ownPid,
|
|
5798
|
+
headroomPid: pid,
|
|
5799
|
+
socketPath,
|
|
5800
|
+
version,
|
|
5801
|
+
allowlistHash,
|
|
5802
|
+
settingsHash,
|
|
5803
|
+
installedSource: config.source
|
|
5804
|
+
});
|
|
5805
|
+
if (recorded) {
|
|
5806
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
5807
|
+
}
|
|
5808
|
+
idleSince = void 0;
|
|
5809
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on unix socket ${socketPath}`);
|
|
5810
|
+
} else {
|
|
5811
|
+
ports.stopProcess(pid);
|
|
5812
|
+
consecutiveFailures += 1;
|
|
5813
|
+
ports.log(
|
|
5814
|
+
`agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
5815
|
+
);
|
|
5816
|
+
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
5817
|
+
continue;
|
|
5818
|
+
}
|
|
5819
|
+
} else {
|
|
5820
|
+
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
5821
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
5822
|
+
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
5823
|
+
ports.stopProcess(headroomPid);
|
|
5824
|
+
headroomPid = void 0;
|
|
5825
|
+
runningHash = void 0;
|
|
5826
|
+
runningSettingsHash = void 0;
|
|
5827
|
+
continue;
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
5831
|
+
if (sessions.length === 0) {
|
|
5832
|
+
idleSince ??= ports.now();
|
|
5833
|
+
const idleMs = ports.now() - idleSince;
|
|
5834
|
+
if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
|
|
5835
|
+
ports.log(
|
|
5836
|
+
`agent-shim headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
5837
|
+
);
|
|
5838
|
+
ports.stopProcess(headroomPid);
|
|
5839
|
+
writeOwnState({ version });
|
|
5840
|
+
return 0;
|
|
5841
|
+
}
|
|
5842
|
+
} else {
|
|
5843
|
+
idleSince = void 0;
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
5739
5847
|
}
|
|
5740
|
-
writeTextAtomic(paths.activeIdentityFile, `${name}
|
|
5741
|
-
`);
|
|
5742
5848
|
}
|
|
5743
|
-
function
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
}
|
|
5747
|
-
const raw = fs5.readFileSync(paths.activeIdentityFile, "utf8").trim();
|
|
5748
|
-
return raw === "" ? void 0 : raw;
|
|
5849
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
5850
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
5851
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
5749
5852
|
}
|
|
5750
|
-
function
|
|
5751
|
-
return
|
|
5853
|
+
function allowlistOf(ports) {
|
|
5854
|
+
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
5752
5855
|
}
|
|
5753
|
-
function
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
return { problem: error.message.replace(/\s*\n\s*/g, " ") };
|
|
5856
|
+
async function waitUntilReady(ports, socketPath) {
|
|
5857
|
+
const deadline = ports.now() + HEADROOM_READY_TIMEOUT_MS;
|
|
5858
|
+
for (; ; ) {
|
|
5859
|
+
if (await ports.ready(socketPath)) {
|
|
5860
|
+
return true;
|
|
5759
5861
|
}
|
|
5760
|
-
|
|
5862
|
+
if (ports.now() >= deadline) {
|
|
5863
|
+
return false;
|
|
5864
|
+
}
|
|
5865
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
5761
5866
|
}
|
|
5762
5867
|
}
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5868
|
+
|
|
5869
|
+
// src/launcher/guard.ts
|
|
5870
|
+
var AMBIENT_CREDENTIAL_VARS = [
|
|
5871
|
+
"ANTHROPIC_API_KEY",
|
|
5872
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
5873
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
5874
|
+
"CLAUDE_CODE_USE_BEDROCK",
|
|
5875
|
+
"CLAUDE_CODE_USE_VERTEX",
|
|
5876
|
+
"CLAUDE_CODE_USE_FOUNDRY"
|
|
5877
|
+
];
|
|
5878
|
+
function detectAmbientCredential(env, injected) {
|
|
5879
|
+
for (const variable of AMBIENT_CREDENTIAL_VARS) {
|
|
5880
|
+
const value = env[variable];
|
|
5881
|
+
if (value === void 0 || value === "") {
|
|
5773
5882
|
continue;
|
|
5774
5883
|
}
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
}
|
|
5780
|
-
function setDefaultConfigProfile(paths, identityName, profileName) {
|
|
5781
|
-
if (!identityExists(paths, identityName)) {
|
|
5782
|
-
throw new IdentityNotFoundError(identityName);
|
|
5884
|
+
if (injected?.variable === variable && value === injected.token) {
|
|
5885
|
+
continue;
|
|
5886
|
+
}
|
|
5887
|
+
return { variable };
|
|
5783
5888
|
}
|
|
5784
|
-
return
|
|
5785
|
-
defaultConfigProfile: profileName
|
|
5786
|
-
});
|
|
5889
|
+
return void 0;
|
|
5787
5890
|
}
|
|
5788
|
-
function
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5891
|
+
function formatAmbientCredentialGuardMessage(variable, identityName) {
|
|
5892
|
+
const identitySetCommand = identityName === void 0 ? "agent-shim identity set <name> --allow-ambient-credential" : `agent-shim identity set ${identityName} --allow-ambient-credential`;
|
|
5893
|
+
return [
|
|
5894
|
+
`error: ${variable} is set in the environment. This identity's isolated`,
|
|
5895
|
+
"credential would be bypassed \u2014 every identity authenticates as this same key",
|
|
5896
|
+
"while it's set. Unset it, or if this is deliberate, opt in per-launch with",
|
|
5897
|
+
"AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL=1, or persistently for this identity with",
|
|
5898
|
+
`\`${identitySetCommand}\`.`
|
|
5899
|
+
].join("\n");
|
|
5795
5900
|
}
|
|
5796
|
-
function
|
|
5797
|
-
const
|
|
5798
|
-
if (
|
|
5799
|
-
|
|
5800
|
-
}
|
|
5801
|
-
if (change === false) {
|
|
5802
|
-
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
|
|
5901
|
+
function evaluateAmbientCredentialGuard(params) {
|
|
5902
|
+
const detected = detectAmbientCredential(params.env, params.injectedCredential);
|
|
5903
|
+
if (detected === void 0) {
|
|
5904
|
+
return { ok: true };
|
|
5803
5905
|
}
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
|
|
5906
|
+
if (params.allowAmbientCredentialOverride || params.allowAmbientCredential || params.providerSelected === true) {
|
|
5907
|
+
return { ok: true };
|
|
5807
5908
|
}
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
}
|
|
5813
|
-
}
|
|
5814
|
-
function belongsToIdentity(entry, name) {
|
|
5815
|
-
return entry === name || entry === path15.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
5909
|
+
return {
|
|
5910
|
+
ok: false,
|
|
5911
|
+
variable: detected.variable,
|
|
5912
|
+
message: formatAmbientCredentialGuardMessage(detected.variable, params.identityName)
|
|
5913
|
+
};
|
|
5816
5914
|
}
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
if (readActiveIdentity(paths) === name) {
|
|
5827
|
-
fs5.rmSync(paths.activeIdentityFile);
|
|
5915
|
+
|
|
5916
|
+
// src/paths.ts
|
|
5917
|
+
import fs3 from "node:fs";
|
|
5918
|
+
import os from "node:os";
|
|
5919
|
+
import path14 from "node:path";
|
|
5920
|
+
function resolveAgentShimHome(env = process.env, home = os.homedir(), isDirectory = isExistingDirectory) {
|
|
5921
|
+
const fromEnv = env.AGENT_SHIM_HOME;
|
|
5922
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
5923
|
+
return fromEnv;
|
|
5828
5924
|
}
|
|
5925
|
+
const current = path14.join(home, HOME_DIRNAME);
|
|
5926
|
+
const legacy = path14.join(home, LEGACY_HOME_DIRNAME);
|
|
5927
|
+
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
5829
5928
|
}
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
import fs6 from "node:fs";
|
|
5833
|
-
import path16 from "node:path";
|
|
5834
|
-
|
|
5835
|
-
// src/credentialCache.ts
|
|
5836
|
-
import { z as z7 } from "zod";
|
|
5837
|
-
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
5838
|
-
var CachedCredentialSchema = z7.strictObject({
|
|
5839
|
-
token: z7.string().min(1),
|
|
5840
|
-
fetchedAt: z7.number().int().nonnegative(),
|
|
5841
|
-
source: CredentialSourceSchema
|
|
5842
|
-
});
|
|
5843
|
-
function effectiveStore(cache, platform) {
|
|
5844
|
-
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
5929
|
+
function isExistingDirectory(candidate) {
|
|
5930
|
+
return fs3.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
5845
5931
|
}
|
|
5846
|
-
function
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
5851
|
-
if (multiplier === void 0) {
|
|
5852
|
-
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
5932
|
+
function resolveClaudeHome() {
|
|
5933
|
+
const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
|
|
5934
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
5935
|
+
return fromEnv;
|
|
5853
5936
|
}
|
|
5854
|
-
return
|
|
5937
|
+
return path14.join(os.homedir(), ".claude");
|
|
5855
5938
|
}
|
|
5856
|
-
function
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5939
|
+
function buildLayoutPaths(root) {
|
|
5940
|
+
return {
|
|
5941
|
+
root,
|
|
5942
|
+
identitiesDir: path14.join(root, "identities"),
|
|
5943
|
+
configProfilesDir: path14.join(root, "config-profiles"),
|
|
5944
|
+
providersDir: path14.join(root, "providers"),
|
|
5945
|
+
directoryRulesFile: path14.join(root, "directory-rules.json"),
|
|
5946
|
+
activeIdentityFile: path14.join(root, "active-identity"),
|
|
5947
|
+
globalConfigFile: path14.join(root, "config.json"),
|
|
5948
|
+
categoriesLocalFile: path14.join(root, "categories.local.json"),
|
|
5949
|
+
claudeShimFile: path14.join(root, "claude-shim.json"),
|
|
5950
|
+
headroomDir: path14.join(root, "headroom"),
|
|
5951
|
+
headroomStateFile: path14.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
5952
|
+
headroomSocketDir: path14.join(root, "headroom", "run"),
|
|
5953
|
+
headroomLockFile: path14.join(root, "headroom", "start.lock"),
|
|
5954
|
+
headroomSessionsDir: path14.join(root, "headroom", "sessions"),
|
|
5955
|
+
logsDir: path14.join(root, "logs"),
|
|
5956
|
+
headroomLogPath: path14.join(root, "logs", "headroom.log"),
|
|
5957
|
+
frontdoorDir: path14.join(root, "frontdoor"),
|
|
5958
|
+
frontdoorCaDir: path14.join(root, "frontdoor", "ca"),
|
|
5959
|
+
frontdoorCaCertFile: path14.join(root, "frontdoor", "ca", "ca.pem"),
|
|
5960
|
+
frontdoorCaKeyFile: path14.join(root, "frontdoor", "ca", "ca.key"),
|
|
5961
|
+
frontdoorCaBundlesDir: path14.join(root, "frontdoor", "ca", "bundles"),
|
|
5962
|
+
frontdoorStateFile: path14.join(root, "frontdoor", "state.json"),
|
|
5963
|
+
frontdoorLockFile: path14.join(root, "frontdoor", "start.lock"),
|
|
5964
|
+
frontdoorControlTokenFile: path14.join(root, "frontdoor", "control-token"),
|
|
5965
|
+
frontdoorSessionsDir: path14.join(root, "frontdoor", "sessions"),
|
|
5966
|
+
frontdoorLogPath: path14.join(root, "logs", "frontdoor.log"),
|
|
5967
|
+
usageDir: path14.join(root, "usage"),
|
|
5968
|
+
usageLogDir: path14.join(root, "usage", "log"),
|
|
5969
|
+
usageSnapshotsDir: path14.join(root, "usage", "snapshots"),
|
|
5970
|
+
usagePicksFile: path14.join(root, "usage", "picks.json")
|
|
5971
|
+
};
|
|
5972
|
+
}
|
|
5973
|
+
function resolveLayoutPaths() {
|
|
5974
|
+
return buildLayoutPaths(resolveAgentShimHome());
|
|
5862
5975
|
}
|
|
5863
5976
|
|
|
5864
|
-
// src/
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
return ["op", "read", source.op];
|
|
5977
|
+
// src/identityStore.ts
|
|
5978
|
+
import fs5 from "node:fs";
|
|
5979
|
+
import path16 from "node:path";
|
|
5980
|
+
|
|
5981
|
+
// src/configProfilesStore.ts
|
|
5982
|
+
import fs4 from "node:fs";
|
|
5983
|
+
import path15 from "node:path";
|
|
5984
|
+
var ProfileNotFoundError = class extends CliError {
|
|
5985
|
+
constructor(profileName) {
|
|
5986
|
+
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
5987
|
+
this.profileName = profileName;
|
|
5988
|
+
this.name = "ProfileNotFoundError";
|
|
5877
5989
|
}
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5990
|
+
profileName;
|
|
5991
|
+
};
|
|
5992
|
+
var ProfileAlreadyExistsError = class extends CliError {
|
|
5993
|
+
constructor(profileName) {
|
|
5994
|
+
super(`A configuration profile named "${profileName}" already exists.`);
|
|
5995
|
+
this.profileName = profileName;
|
|
5996
|
+
this.name = "ProfileAlreadyExistsError";
|
|
5884
5997
|
}
|
|
5885
|
-
|
|
5886
|
-
|
|
5998
|
+
profileName;
|
|
5999
|
+
};
|
|
6000
|
+
var InvalidCategoryNameError = class extends CliError {
|
|
6001
|
+
constructor(categoryName) {
|
|
6002
|
+
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
6003
|
+
this.categoryName = categoryName;
|
|
6004
|
+
this.name = "InvalidCategoryNameError";
|
|
5887
6005
|
}
|
|
5888
|
-
|
|
6006
|
+
categoryName;
|
|
6007
|
+
};
|
|
6008
|
+
function profileJsonPath(paths, name) {
|
|
6009
|
+
return path15.join(paths.configProfilesDir, `${name}.json`);
|
|
5889
6010
|
}
|
|
5890
|
-
function
|
|
5891
|
-
|
|
5892
|
-
|
|
6011
|
+
function profileExists(paths, name) {
|
|
6012
|
+
return fs4.existsSync(profileJsonPath(paths, name));
|
|
6013
|
+
}
|
|
6014
|
+
function requireProfileExists(paths, name) {
|
|
6015
|
+
if (!profileExists(paths, name)) {
|
|
6016
|
+
throw new ProfileNotFoundError(name);
|
|
5893
6017
|
}
|
|
5894
|
-
|
|
5895
|
-
|
|
6018
|
+
}
|
|
6019
|
+
function readProfile(paths, name) {
|
|
6020
|
+
return readJson(profileJsonPath(paths, name), ConfigProfileSchema);
|
|
6021
|
+
}
|
|
6022
|
+
function createProfile(paths, name, extendsList, description) {
|
|
6023
|
+
if (profileExists(paths, name)) {
|
|
6024
|
+
throw new ProfileAlreadyExistsError(name);
|
|
5896
6025
|
}
|
|
5897
|
-
|
|
5898
|
-
|
|
6026
|
+
const filePath = profileJsonPath(paths, name);
|
|
6027
|
+
const parsed = ConfigProfileSchema.safeParse({
|
|
6028
|
+
...description === void 0 ? {} : { description },
|
|
6029
|
+
...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
|
|
6030
|
+
});
|
|
6031
|
+
if (!parsed.success) {
|
|
6032
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
5899
6033
|
}
|
|
5900
|
-
|
|
5901
|
-
|
|
6034
|
+
writeJsonAtomic(filePath, parsed.data);
|
|
6035
|
+
return parsed.data;
|
|
6036
|
+
}
|
|
6037
|
+
function listProfiles(paths) {
|
|
6038
|
+
if (!fs4.existsSync(paths.configProfilesDir)) {
|
|
6039
|
+
return [];
|
|
5902
6040
|
}
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
6041
|
+
const names = fs4.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort();
|
|
6042
|
+
const result = [];
|
|
6043
|
+
for (const name of names) {
|
|
6044
|
+
const profile = readProfile(paths, name);
|
|
6045
|
+
if (profile !== void 0) {
|
|
6046
|
+
result.push({ name, profile });
|
|
6047
|
+
}
|
|
5909
6048
|
}
|
|
5910
|
-
return
|
|
6049
|
+
return result;
|
|
5911
6050
|
}
|
|
5912
|
-
function
|
|
5913
|
-
return
|
|
5914
|
-
target: credential.target ?? "bearer",
|
|
5915
|
-
sources: credential.sources.map(summariseSource),
|
|
5916
|
-
...credential.cache === void 0 ? {} : { cache: credential.cache }
|
|
5917
|
-
};
|
|
6051
|
+
function globalConfigPath(paths) {
|
|
6052
|
+
return paths.globalConfigFile;
|
|
5918
6053
|
}
|
|
5919
|
-
function
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
6054
|
+
function readGlobalConfig(paths) {
|
|
6055
|
+
return readJson(globalConfigPath(paths), GlobalConfigSchema);
|
|
6056
|
+
}
|
|
6057
|
+
function setGlobalDefaultProfile(paths, name) {
|
|
6058
|
+
return applyPatch(
|
|
6059
|
+
globalConfigPath(paths),
|
|
6060
|
+
GlobalConfigSchema,
|
|
6061
|
+
{ defaultConfigProfile: name },
|
|
6062
|
+
{ defaults: {} }
|
|
6063
|
+
);
|
|
6064
|
+
}
|
|
6065
|
+
function validateCategoryNames(patch) {
|
|
6066
|
+
for (const key of Object.keys(patch)) {
|
|
6067
|
+
if (!isOverridableCategory(key)) {
|
|
6068
|
+
throw new InvalidCategoryNameError(key);
|
|
6069
|
+
}
|
|
5935
6070
|
}
|
|
5936
6071
|
}
|
|
5937
|
-
function
|
|
5938
|
-
|
|
6072
|
+
function setProfileCategories(paths, name, patch) {
|
|
6073
|
+
requireProfileExists(paths, name);
|
|
6074
|
+
const expandedPatch = expandAllCategoryKey(patch);
|
|
6075
|
+
validateCategoryNames(expandedPatch);
|
|
6076
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6077
|
+
const mergedCategories = { ...existing.categories, ...expandedPatch };
|
|
6078
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { categories: mergedCategories });
|
|
5939
6079
|
}
|
|
5940
|
-
function
|
|
5941
|
-
|
|
5942
|
-
|
|
6080
|
+
function setProfileEntries(paths, name, patch) {
|
|
6081
|
+
requireProfileExists(paths, name);
|
|
6082
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6083
|
+
const mergedEntries = { ...existing.entries, ...patch };
|
|
6084
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
|
|
5943
6085
|
}
|
|
5944
|
-
function
|
|
5945
|
-
|
|
6086
|
+
function setProfileLaunchFlags(paths, name, patch) {
|
|
6087
|
+
requireProfileExists(paths, name);
|
|
6088
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6089
|
+
const mergedLaunch = { ...existing.launch, ...patch };
|
|
6090
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
6091
|
+
// Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
|
|
6092
|
+
launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
|
|
6093
|
+
});
|
|
5946
6094
|
}
|
|
5947
|
-
function
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
}
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
);
|
|
5966
|
-
}
|
|
5967
|
-
const token2 = read.content.trim();
|
|
5968
|
-
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
6095
|
+
function setProfileMetadata(paths, name, patch) {
|
|
6096
|
+
requireProfileExists(paths, name);
|
|
6097
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
6098
|
+
...patch.extends === void 0 ? {} : { extends: patch.extends.length === 0 ? void 0 : [...patch.extends] },
|
|
6099
|
+
...patch.description === void 0 ? {} : { description: patch.description === false ? void 0 : patch.description }
|
|
6100
|
+
});
|
|
6101
|
+
}
|
|
6102
|
+
function removeProfile(paths, name) {
|
|
6103
|
+
requireProfileExists(paths, name);
|
|
6104
|
+
fs4.rmSync(profileJsonPath(paths, name));
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
// src/poolStore.ts
|
|
6108
|
+
var PoolNotFoundError = class extends CliError {
|
|
6109
|
+
constructor(poolName) {
|
|
6110
|
+
super(`No pool named "${poolName}". Run \`agent-shim pool add ${poolName} --identity <name>...\` first.`);
|
|
6111
|
+
this.poolName = poolName;
|
|
6112
|
+
this.name = "PoolNotFoundError";
|
|
5969
6113
|
}
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
6114
|
+
poolName;
|
|
6115
|
+
};
|
|
6116
|
+
var PoolAlreadyExistsError = class extends CliError {
|
|
6117
|
+
constructor(poolName) {
|
|
6118
|
+
super(`A pool named "${poolName}" already exists. Use \`agent-shim pool set ${poolName}\` to change its members.`);
|
|
6119
|
+
this.poolName = poolName;
|
|
6120
|
+
this.name = "PoolAlreadyExistsError";
|
|
5974
6121
|
}
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
6122
|
+
poolName;
|
|
6123
|
+
};
|
|
6124
|
+
var InvalidPoolNameError = class extends CliError {
|
|
6125
|
+
constructor(attemptedName) {
|
|
6126
|
+
super(`"${attemptedName}" is not a valid pool name: it must start with a letter or number and may then contain letters, numbers, dots, hyphens and underscores.`);
|
|
6127
|
+
this.attemptedName = attemptedName;
|
|
6128
|
+
this.name = "InvalidPoolNameError";
|
|
5979
6129
|
}
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
6130
|
+
attemptedName;
|
|
6131
|
+
};
|
|
6132
|
+
function readPools(paths) {
|
|
6133
|
+
return readGlobalConfig(paths)?.pools ?? {};
|
|
6134
|
+
}
|
|
6135
|
+
function requirePool(paths, name) {
|
|
6136
|
+
const pool = readPools(paths)[name];
|
|
6137
|
+
if (pool === void 0) {
|
|
6138
|
+
throw new PoolNotFoundError(name);
|
|
5984
6139
|
}
|
|
5985
|
-
|
|
5986
|
-
return token === "" ? none("printed no token") : { ok: true, token };
|
|
6140
|
+
return pool;
|
|
5987
6141
|
}
|
|
5988
|
-
function
|
|
5989
|
-
|
|
5990
|
-
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
6142
|
+
function writePools(paths, pools) {
|
|
6143
|
+
applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
|
|
5991
6144
|
}
|
|
5992
|
-
function
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
5996
|
-
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
5997
|
-
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
5998
|
-
const entry = cache.port.read(store, params.subject);
|
|
5999
|
-
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
6000
|
-
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
6001
|
-
}
|
|
6145
|
+
function addPool(paths, name, identities) {
|
|
6146
|
+
if (!PoolNameSchema.safeParse(name).success) {
|
|
6147
|
+
throw new InvalidPoolNameError(name);
|
|
6002
6148
|
}
|
|
6003
|
-
const
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
const outcome = trySource(source, params);
|
|
6007
|
-
if (outcome.ok) {
|
|
6008
|
-
if (cache !== void 0 && store !== void 0) {
|
|
6009
|
-
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
6010
|
-
}
|
|
6011
|
-
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
6012
|
-
}
|
|
6013
|
-
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
6014
|
-
attempts.push(line);
|
|
6015
|
-
if (outcome.warn) {
|
|
6016
|
-
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
6017
|
-
}
|
|
6149
|
+
const pools = readPools(paths);
|
|
6150
|
+
if (pools[name] !== void 0) {
|
|
6151
|
+
throw new PoolAlreadyExistsError(name);
|
|
6018
6152
|
}
|
|
6019
|
-
const
|
|
6020
|
-
|
|
6153
|
+
const pool = { identities: [...identities] };
|
|
6154
|
+
writePools(paths, { ...pools, [name]: pool });
|
|
6155
|
+
return pool;
|
|
6021
6156
|
}
|
|
6022
|
-
function
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
};
|
|
6157
|
+
function setPool(paths, name, identities) {
|
|
6158
|
+
requirePool(paths, name);
|
|
6159
|
+
const pool = { identities: [...identities] };
|
|
6160
|
+
writePools(paths, { ...readPools(paths), [name]: pool });
|
|
6161
|
+
return pool;
|
|
6028
6162
|
}
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6163
|
+
function removePool(paths, name) {
|
|
6164
|
+
requirePool(paths, name);
|
|
6165
|
+
const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
|
|
6166
|
+
writePools(paths, remaining);
|
|
6167
|
+
}
|
|
6168
|
+
|
|
6169
|
+
// src/identityStore.ts
|
|
6170
|
+
var IdentityNotFoundError = class extends CliError {
|
|
6032
6171
|
constructor(name) {
|
|
6033
|
-
super(`No
|
|
6172
|
+
super(`No identity named "${name}". Run \`agent-shim identity add ${name}\` first.`);
|
|
6034
6173
|
this.name = name;
|
|
6035
|
-
this.name = "
|
|
6174
|
+
this.name = "IdentityNotFoundError";
|
|
6036
6175
|
}
|
|
6037
6176
|
name;
|
|
6038
6177
|
};
|
|
6039
|
-
var
|
|
6040
|
-
constructor(
|
|
6041
|
-
super(`
|
|
6042
|
-
this.
|
|
6043
|
-
this.name = "
|
|
6178
|
+
var IdentityAlreadyExistsError = class extends CliError {
|
|
6179
|
+
constructor(identityName) {
|
|
6180
|
+
super(`An identity named "${identityName}" already exists.`);
|
|
6181
|
+
this.identityName = identityName;
|
|
6182
|
+
this.name = "IdentityAlreadyExistsError";
|
|
6044
6183
|
}
|
|
6045
|
-
|
|
6184
|
+
identityName;
|
|
6046
6185
|
};
|
|
6047
|
-
var
|
|
6186
|
+
var InvalidIdentityNameError = class extends CliError {
|
|
6048
6187
|
constructor(attemptedName) {
|
|
6049
6188
|
super(
|
|
6050
|
-
`"${attemptedName}" is not a valid
|
|
6189
|
+
`"${attemptedName}" is not a valid identity name \u2014 identity names must start with a letter or number and may then contain letters, numbers, dots, hyphens, underscores, and at signs.`
|
|
6051
6190
|
);
|
|
6052
6191
|
this.attemptedName = attemptedName;
|
|
6053
|
-
this.name = "
|
|
6192
|
+
this.name = "InvalidIdentityNameError";
|
|
6054
6193
|
}
|
|
6055
6194
|
attemptedName;
|
|
6056
6195
|
};
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
return path16.join(paths.providersDir, `${name}.json`);
|
|
6060
|
-
}
|
|
6061
|
-
function providerExists(paths, name) {
|
|
6062
|
-
return fs6.existsSync(providerJsonPath(paths, name));
|
|
6196
|
+
function identityJsonPath(paths, name) {
|
|
6197
|
+
return path16.join(paths.identitiesDir, name, "identity.json");
|
|
6063
6198
|
}
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
6067
|
-
function isRecord3(value) {
|
|
6068
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6199
|
+
function identityExists(paths, name) {
|
|
6200
|
+
return fs5.existsSync(identityJsonPath(paths, name));
|
|
6069
6201
|
}
|
|
6070
|
-
function
|
|
6071
|
-
|
|
6072
|
-
return void 0;
|
|
6073
|
-
}
|
|
6074
|
-
const env = isRecord3(raw.env) ? raw.env : void 0;
|
|
6075
|
-
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
6076
|
-
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
6077
|
-
if (fields.length === 0) {
|
|
6078
|
-
return void 0;
|
|
6079
|
-
}
|
|
6080
|
-
const sources = [];
|
|
6081
|
-
if (typeof raw.tokenEnv === "string") {
|
|
6082
|
-
sources.push({ env: raw.tokenEnv });
|
|
6083
|
-
}
|
|
6084
|
-
if (Array.isArray(raw.tokenCommand)) {
|
|
6085
|
-
sources.push({ command: raw.tokenCommand });
|
|
6086
|
-
}
|
|
6087
|
-
if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
|
|
6088
|
-
sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
|
|
6089
|
-
}
|
|
6090
|
-
const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
|
|
6091
|
-
const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
|
|
6092
|
-
const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
|
|
6093
|
-
return {
|
|
6094
|
-
fields,
|
|
6095
|
-
replacement: {
|
|
6096
|
-
...rest,
|
|
6097
|
-
credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
|
|
6098
|
-
...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
|
|
6099
|
-
}
|
|
6100
|
-
};
|
|
6202
|
+
function readIdentity(paths, name) {
|
|
6203
|
+
return readJson(identityJsonPath(paths, name), IdentitySchema);
|
|
6101
6204
|
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
`${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
|
|
6106
|
-
` + JSON.stringify(conversion.replacement, null, 2)
|
|
6107
|
-
);
|
|
6108
|
-
this.filePath = filePath;
|
|
6109
|
-
this.conversion = conversion;
|
|
6110
|
-
this.name = "LegacyProviderFileError";
|
|
6111
|
-
}
|
|
6112
|
-
filePath;
|
|
6113
|
-
conversion;
|
|
6114
|
-
};
|
|
6115
|
-
function parseProviderFile(filePath, raw) {
|
|
6116
|
-
const conversion = legacyProviderConversion(raw);
|
|
6117
|
-
if (conversion !== void 0) {
|
|
6118
|
-
throw new LegacyProviderFileError(filePath, conversion);
|
|
6205
|
+
function addIdentity(paths, name) {
|
|
6206
|
+
if (identityExists(paths, name)) {
|
|
6207
|
+
throw new IdentityAlreadyExistsError(name);
|
|
6119
6208
|
}
|
|
6120
|
-
const parsed =
|
|
6209
|
+
const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
|
|
6121
6210
|
if (!parsed.success) {
|
|
6122
|
-
throw new
|
|
6211
|
+
throw new InvalidIdentityNameError(name);
|
|
6123
6212
|
}
|
|
6213
|
+
writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
|
|
6124
6214
|
return parsed.data;
|
|
6125
6215
|
}
|
|
6126
|
-
function
|
|
6127
|
-
const
|
|
6128
|
-
if (
|
|
6216
|
+
function useIdentity(paths, name) {
|
|
6217
|
+
const pool = poolNameOf(name);
|
|
6218
|
+
if (pool !== void 0) {
|
|
6219
|
+
requirePool(paths, pool);
|
|
6220
|
+
} else if (!identityExists(paths, name)) {
|
|
6221
|
+
throw new IdentityNotFoundError(name);
|
|
6222
|
+
}
|
|
6223
|
+
writeTextAtomic(paths.activeIdentityFile, `${name}
|
|
6224
|
+
`);
|
|
6225
|
+
}
|
|
6226
|
+
function readActiveIdentity(paths) {
|
|
6227
|
+
if (!fs5.existsSync(paths.activeIdentityFile)) {
|
|
6129
6228
|
return void 0;
|
|
6130
6229
|
}
|
|
6131
|
-
const raw =
|
|
6132
|
-
return
|
|
6230
|
+
const raw = fs5.readFileSync(paths.activeIdentityFile, "utf8").trim();
|
|
6231
|
+
return raw === "" ? void 0 : raw;
|
|
6133
6232
|
}
|
|
6134
|
-
function
|
|
6135
|
-
|
|
6136
|
-
const raw = port.readConfigFile(filePath);
|
|
6137
|
-
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
6233
|
+
function isIdentityDirectoryName(name) {
|
|
6234
|
+
return !name.startsWith(".");
|
|
6138
6235
|
}
|
|
6139
|
-
function
|
|
6140
|
-
|
|
6141
|
-
(
|
|
6142
|
-
|
|
6143
|
-
|
|
6236
|
+
function readIdentityForListing(paths, name) {
|
|
6237
|
+
try {
|
|
6238
|
+
return readIdentity(paths, name);
|
|
6239
|
+
} catch (error) {
|
|
6240
|
+
if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
|
|
6241
|
+
return { problem: error.message.replace(/\s*\n\s*/g, " ") };
|
|
6242
|
+
}
|
|
6243
|
+
throw error;
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
function listIdentities(paths) {
|
|
6247
|
+
if (!fs5.existsSync(paths.identitiesDir)) {
|
|
6248
|
+
return [];
|
|
6249
|
+
}
|
|
6250
|
+
const active = readActiveIdentity(paths);
|
|
6251
|
+
const names = fs5.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort();
|
|
6144
6252
|
const result = [];
|
|
6145
6253
|
for (const name of names) {
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
provider = readProvider(paths, name);
|
|
6149
|
-
} catch (error) {
|
|
6150
|
-
if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
|
|
6151
|
-
throw error;
|
|
6152
|
-
}
|
|
6153
|
-
console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
|
|
6254
|
+
const read = readIdentityForListing(paths, name);
|
|
6255
|
+
if (read === void 0) {
|
|
6154
6256
|
continue;
|
|
6155
6257
|
}
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
}
|
|
6258
|
+
const isActive = name === active;
|
|
6259
|
+
result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
|
|
6159
6260
|
}
|
|
6160
6261
|
return result;
|
|
6161
6262
|
}
|
|
6162
|
-
function
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
entries = readdir(providersDir);
|
|
6166
|
-
} catch {
|
|
6167
|
-
return [];
|
|
6168
|
-
}
|
|
6169
|
-
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
|
|
6170
|
-
}
|
|
6171
|
-
function describeProviderEndpoint(provider) {
|
|
6172
|
-
return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
|
|
6173
|
-
}
|
|
6174
|
-
var ProviderKindMismatchError = class extends UsageError {
|
|
6175
|
-
constructor(message) {
|
|
6176
|
-
super(message);
|
|
6177
|
-
this.name = "ProviderKindMismatchError";
|
|
6263
|
+
function setDefaultConfigProfile(paths, identityName, profileName) {
|
|
6264
|
+
if (!identityExists(paths, identityName)) {
|
|
6265
|
+
throw new IdentityNotFoundError(identityName);
|
|
6178
6266
|
}
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6267
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6268
|
+
defaultConfigProfile: profileName
|
|
6269
|
+
});
|
|
6182
6270
|
}
|
|
6183
|
-
function
|
|
6184
|
-
if (
|
|
6185
|
-
throw new
|
|
6186
|
-
}
|
|
6187
|
-
if (!PROVIDER_NAME_RE.test(name)) {
|
|
6188
|
-
throw new InvalidProviderNameError(name);
|
|
6189
|
-
}
|
|
6190
|
-
const kind = input.kind ?? "http";
|
|
6191
|
-
if (kind === "codex" && input.baseUrl !== void 0) {
|
|
6192
|
-
throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
|
|
6193
|
-
}
|
|
6194
|
-
if (kind === "http" && input.baseUrl === void 0) {
|
|
6195
|
-
throw new ProviderKindMismatchError("An http provider needs --base-url.");
|
|
6196
|
-
}
|
|
6197
|
-
if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
|
|
6198
|
-
throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
|
|
6199
|
-
}
|
|
6200
|
-
const shared = {
|
|
6201
|
-
displayName: input.displayName,
|
|
6202
|
-
credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
|
|
6203
|
-
...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
|
|
6204
|
-
};
|
|
6205
|
-
const parsed = ProviderSchema.safeParse(
|
|
6206
|
-
kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
|
|
6207
|
-
);
|
|
6208
|
-
if (!parsed.success) {
|
|
6209
|
-
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6271
|
+
function setAllowAmbientCredential(paths, identityName, allow) {
|
|
6272
|
+
if (!identityExists(paths, identityName)) {
|
|
6273
|
+
throw new IdentityNotFoundError(identityName);
|
|
6210
6274
|
}
|
|
6211
|
-
|
|
6212
|
-
|
|
6275
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6276
|
+
allowAmbientCredential: allow
|
|
6277
|
+
});
|
|
6213
6278
|
}
|
|
6214
|
-
function
|
|
6215
|
-
const existing =
|
|
6279
|
+
function setIdentityCredential(paths, identityName, change) {
|
|
6280
|
+
const existing = readIdentity(paths, identityName);
|
|
6216
6281
|
if (existing === void 0) {
|
|
6217
|
-
throw new
|
|
6218
|
-
}
|
|
6219
|
-
const unset = new Set(input.unsetEnv);
|
|
6220
|
-
const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
|
|
6221
|
-
const target = input.target ?? existing.credential.target;
|
|
6222
|
-
const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
|
|
6223
|
-
if (isCodexProvider(existing) && input.baseUrl !== void 0) {
|
|
6224
|
-
throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
|
|
6225
|
-
}
|
|
6226
|
-
if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
|
|
6227
|
-
throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
|
|
6228
|
-
}
|
|
6229
|
-
const shared = {
|
|
6230
|
-
...input.displayName === void 0 ? {} : { displayName: input.displayName },
|
|
6231
|
-
credential: {
|
|
6232
|
-
sources: input.sources ?? existing.credential.sources,
|
|
6233
|
-
...target === void 0 ? {} : { target },
|
|
6234
|
-
...cache === void 0 ? {} : { cache }
|
|
6235
|
-
},
|
|
6236
|
-
env: Object.keys(env).length === 0 ? void 0 : env
|
|
6237
|
-
};
|
|
6238
|
-
let candidate;
|
|
6239
|
-
if (isCodexProvider(existing)) {
|
|
6240
|
-
const codex = {
|
|
6241
|
-
...existing.codex,
|
|
6242
|
-
...input.codex,
|
|
6243
|
-
...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
|
|
6244
|
-
};
|
|
6245
|
-
candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
|
|
6246
|
-
} else {
|
|
6247
|
-
candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
|
|
6282
|
+
throw new IdentityNotFoundError(identityName);
|
|
6248
6283
|
}
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6284
|
+
if (change === false) {
|
|
6285
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
|
|
6252
6286
|
}
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
}
|
|
6256
|
-
function removeProvider(paths, name) {
|
|
6257
|
-
if (!providerExists(paths, name)) {
|
|
6258
|
-
throw new ProviderNotFoundError(name);
|
|
6287
|
+
const sources = change.sources ?? existing.credential?.sources;
|
|
6288
|
+
if (sources === void 0) {
|
|
6289
|
+
throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
|
|
6259
6290
|
}
|
|
6260
|
-
|
|
6291
|
+
const target = change.target ?? existing.credential?.target;
|
|
6292
|
+
const cache = change.cache === void 0 ? existing.credential?.cache : change.cache === false ? void 0 : change.cache;
|
|
6293
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6294
|
+
credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
|
|
6295
|
+
});
|
|
6261
6296
|
}
|
|
6262
|
-
function
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6297
|
+
function belongsToIdentity(entry, name) {
|
|
6298
|
+
return entry === name || entry === path16.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
6299
|
+
}
|
|
6300
|
+
function removeIdentity(paths, name) {
|
|
6301
|
+
if (!identityExists(paths, name)) {
|
|
6302
|
+
throw new IdentityNotFoundError(name);
|
|
6303
|
+
}
|
|
6304
|
+
for (const entry of fs5.readdirSync(paths.identitiesDir)) {
|
|
6305
|
+
if (belongsToIdentity(entry, name)) {
|
|
6306
|
+
fs5.rmSync(path16.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
6267
6307
|
}
|
|
6268
6308
|
}
|
|
6269
|
-
|
|
6309
|
+
if (readActiveIdentity(paths) === name) {
|
|
6310
|
+
fs5.rmSync(paths.activeIdentityFile);
|
|
6311
|
+
}
|
|
6270
6312
|
}
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6313
|
+
|
|
6314
|
+
// src/providersStore.ts
|
|
6315
|
+
import fs6 from "node:fs";
|
|
6316
|
+
import path17 from "node:path";
|
|
6317
|
+
|
|
6318
|
+
// src/credentialCache.ts
|
|
6319
|
+
import { z as z11 } from "zod";
|
|
6320
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
6321
|
+
var CachedCredentialSchema = z11.strictObject({
|
|
6322
|
+
token: z11.string().min(1),
|
|
6323
|
+
fetchedAt: z11.number().int().nonnegative(),
|
|
6324
|
+
source: CredentialSourceSchema
|
|
6325
|
+
});
|
|
6326
|
+
function effectiveStore(cache, platform) {
|
|
6327
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
6328
|
+
}
|
|
6329
|
+
function ttlMs(cache) {
|
|
6330
|
+
if (cache.ttl === void 0) {
|
|
6275
6331
|
return void 0;
|
|
6276
6332
|
}
|
|
6277
|
-
const
|
|
6278
|
-
if (
|
|
6279
|
-
|
|
6280
|
-
return {
|
|
6281
|
-
ok: false,
|
|
6282
|
-
status: 1,
|
|
6283
|
-
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
6284
|
-
};
|
|
6333
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
6334
|
+
if (multiplier === void 0) {
|
|
6335
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
6285
6336
|
}
|
|
6286
|
-
|
|
6287
|
-
|
|
6337
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
6338
|
+
}
|
|
6339
|
+
function isFresh(entry, cache, now) {
|
|
6340
|
+
const limit = ttlMs(cache);
|
|
6341
|
+
if (entry.fetchedAt > now) {
|
|
6342
|
+
return false;
|
|
6288
6343
|
}
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6344
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
6345
|
+
}
|
|
6346
|
+
function cacheAgeMs(entry, now) {
|
|
6347
|
+
return Math.max(0, now - entry.fetchedAt);
|
|
6348
|
+
}
|
|
6349
|
+
function describeCachedCredential(params) {
|
|
6350
|
+
const store = effectiveStore(params.cache, params.platform);
|
|
6351
|
+
let entry;
|
|
6352
|
+
try {
|
|
6353
|
+
entry = params.port.read(store, params.owner);
|
|
6354
|
+
} catch (error) {
|
|
6355
|
+
return { store, status: "unreadable", reason: error instanceof Error ? error.message : String(error) };
|
|
6292
6356
|
}
|
|
6293
|
-
|
|
6357
|
+
if (entry === void 0) {
|
|
6358
|
+
return { store, status: "empty" };
|
|
6359
|
+
}
|
|
6360
|
+
const limit = ttlMs(params.cache);
|
|
6361
|
+
const ageMs = cacheAgeMs(entry, params.nowMs);
|
|
6362
|
+
return {
|
|
6363
|
+
store,
|
|
6364
|
+
status: isFresh(entry, params.cache, params.nowMs) ? "fresh" : "expired",
|
|
6365
|
+
ageMs,
|
|
6366
|
+
...limit === void 0 ? {} : { expiresInMs: limit - ageMs }
|
|
6367
|
+
};
|
|
6294
6368
|
}
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
constructor(rulePath) {
|
|
6299
|
-
super(`No directory rule found for path "${rulePath}".`);
|
|
6300
|
-
this.rulePath = rulePath;
|
|
6301
|
-
this.name = "DirectoryRuleNotFoundError";
|
|
6369
|
+
function formatCachedCredentialState(state) {
|
|
6370
|
+
if (state.status === "empty") {
|
|
6371
|
+
return `no cached entry in the ${state.store} store yet`;
|
|
6302
6372
|
}
|
|
6303
|
-
|
|
6304
|
-
}
|
|
6305
|
-
var DirectoryRuleMissingTargetError = class extends CliError {
|
|
6306
|
-
constructor() {
|
|
6307
|
-
super("A directory rule must set at least one of --config-profile or --identity.");
|
|
6308
|
-
this.name = "DirectoryRuleMissingTargetError";
|
|
6373
|
+
if (state.status === "unreadable") {
|
|
6374
|
+
return `the ${state.store} store cannot be read here: ${state.reason}`;
|
|
6309
6375
|
}
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
constructor(rulePath) {
|
|
6313
|
-
super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
|
|
6314
|
-
this.rulePath = rulePath;
|
|
6315
|
-
this.name = "DirectoryRuleAlreadyExistsError";
|
|
6376
|
+
if (state.status === "expired" && (state.expiresInMs === void 0 || state.expiresInMs > 0)) {
|
|
6377
|
+
return "cached with a timestamp in the future, treated as expired";
|
|
6316
6378
|
}
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
|
|
6321
|
-
}
|
|
6322
|
-
function writeDirectoryRules(paths, rules) {
|
|
6323
|
-
const parsed = DirectoryRulesSchema.safeParse(rules);
|
|
6324
|
-
if (!parsed.success) {
|
|
6325
|
-
throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
|
|
6379
|
+
const age = `cached ${formatAge(state.ageMs)} ago`;
|
|
6380
|
+
if (state.expiresInMs === void 0) {
|
|
6381
|
+
return `${age}, no expiry`;
|
|
6326
6382
|
}
|
|
6327
|
-
|
|
6383
|
+
return state.status === "fresh" ? `${age}, expires in ${formatAge(state.expiresInMs)}` : `${age}, expired ${formatAge(-state.expiresInMs)} ago`;
|
|
6328
6384
|
}
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6385
|
+
|
|
6386
|
+
// src/credential.ts
|
|
6387
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
6388
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
6389
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
6390
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
6391
|
+
var PERMISSION_BITS = 511;
|
|
6392
|
+
var OCTAL_RADIX2 = 8;
|
|
6393
|
+
function commandArgv(source) {
|
|
6394
|
+
if ("command" in source) {
|
|
6395
|
+
return source.command;
|
|
6335
6396
|
}
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
throw new DirectoryRuleAlreadyExistsError(rulePath);
|
|
6397
|
+
if ("op" in source) {
|
|
6398
|
+
return ["op", "read", source.op];
|
|
6339
6399
|
}
|
|
6340
|
-
const
|
|
6341
|
-
|
|
6342
|
-
return created;
|
|
6400
|
+
const account = source.keychain.account;
|
|
6401
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
6343
6402
|
}
|
|
6344
|
-
function
|
|
6345
|
-
|
|
6403
|
+
function isInteractiveSource(source, env) {
|
|
6404
|
+
if ("command" in source || "keychain" in source) {
|
|
6405
|
+
return source.interactive ?? false;
|
|
6406
|
+
}
|
|
6407
|
+
if ("op" in source) {
|
|
6408
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
6409
|
+
}
|
|
6410
|
+
return false;
|
|
6346
6411
|
}
|
|
6347
|
-
function
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
const existing = index === -1 ? void 0 : current.rules[index];
|
|
6351
|
-
if (existing === void 0) {
|
|
6352
|
-
throw new DirectoryRuleNotFoundError(rulePath);
|
|
6412
|
+
function summariseSource(source) {
|
|
6413
|
+
if ("env" in source) {
|
|
6414
|
+
return { kind: "env", variable: source.env };
|
|
6353
6415
|
}
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
|
|
6357
|
-
const updated = {
|
|
6358
|
-
...rest,
|
|
6359
|
-
...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
|
|
6360
|
-
...nextIdentity === void 0 ? {} : { identity: nextIdentity }
|
|
6361
|
-
};
|
|
6362
|
-
if (!ruleHasEffect(updated)) {
|
|
6363
|
-
throw new DirectoryRuleMissingTargetError();
|
|
6416
|
+
if ("file" in source) {
|
|
6417
|
+
return { kind: "file", path: source.file };
|
|
6364
6418
|
}
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6419
|
+
if ("command" in source) {
|
|
6420
|
+
return { kind: "command", program: source.command[0] };
|
|
6421
|
+
}
|
|
6422
|
+
if ("op" in source) {
|
|
6423
|
+
return { kind: "op", reference: source.op };
|
|
6424
|
+
}
|
|
6425
|
+
if ("keychain" in source) {
|
|
6426
|
+
return {
|
|
6427
|
+
kind: "keychain",
|
|
6428
|
+
service: source.keychain.service,
|
|
6429
|
+
...source.keychain.account === void 0 ? {} : { account: source.keychain.account }
|
|
6430
|
+
};
|
|
6431
|
+
}
|
|
6432
|
+
return { kind: "literal" };
|
|
6369
6433
|
}
|
|
6370
|
-
function
|
|
6434
|
+
function summariseCredential(credential) {
|
|
6371
6435
|
return {
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
...
|
|
6436
|
+
target: credential.target ?? "bearer",
|
|
6437
|
+
sources: credential.sources.map(summariseSource),
|
|
6438
|
+
...credential.cache === void 0 ? {} : { cache: credential.cache }
|
|
6375
6439
|
};
|
|
6376
6440
|
}
|
|
6377
|
-
function
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6441
|
+
function formatSourceSummary(summary) {
|
|
6442
|
+
switch (summary.kind) {
|
|
6443
|
+
case "env":
|
|
6444
|
+
return `env ${summary.variable}`;
|
|
6445
|
+
case "file":
|
|
6446
|
+
return `file ${summary.path}`;
|
|
6447
|
+
case "command":
|
|
6448
|
+
return `command ${summary.program}`;
|
|
6449
|
+
case "op":
|
|
6450
|
+
return `op ${summary.reference}`;
|
|
6451
|
+
case "keychain":
|
|
6452
|
+
return `keychain ${summary.service}${summary.account === void 0 ? "" : ` (account ${summary.account})`}`;
|
|
6453
|
+
case "literal":
|
|
6454
|
+
return "literal (non-secret placeholder)";
|
|
6455
|
+
default:
|
|
6456
|
+
return summary;
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
function describeSource(source) {
|
|
6460
|
+
return formatSourceSummary(summariseSource(source));
|
|
6461
|
+
}
|
|
6462
|
+
function formatCredentialSummary(summary) {
|
|
6463
|
+
const cache = summary.cache === void 0 ? "" : `, cached ${summary.cache.ttl === void 0 ? "with no expiry" : `for ${summary.cache.ttl}`}${summary.cache.store === void 0 ? "" : ` in the ${summary.cache.store} store`}`;
|
|
6464
|
+
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
6465
|
+
}
|
|
6466
|
+
function describeCredential(credential) {
|
|
6467
|
+
return formatCredentialSummary(summariseCredential(credential));
|
|
6468
|
+
}
|
|
6469
|
+
function trySource(source, params) {
|
|
6470
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
6471
|
+
if ("env" in source) {
|
|
6472
|
+
const value = params.env[source.env];
|
|
6473
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
6474
|
+
}
|
|
6475
|
+
if ("literal" in source) {
|
|
6476
|
+
return { ok: true, token: source.literal };
|
|
6477
|
+
}
|
|
6478
|
+
if ("file" in source) {
|
|
6479
|
+
const read = params.port.readSecretFile(source.file);
|
|
6480
|
+
if (!read.found) {
|
|
6481
|
+
return none("does not exist");
|
|
6482
|
+
}
|
|
6483
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
6484
|
+
return none(
|
|
6485
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX2)}); run \`chmod 600 ${source.file}\``,
|
|
6486
|
+
true
|
|
6487
|
+
);
|
|
6488
|
+
}
|
|
6489
|
+
const token2 = read.content.trim();
|
|
6490
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
6491
|
+
}
|
|
6492
|
+
const argv = commandArgv(source);
|
|
6493
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
6494
|
+
if (interactive && !params.port.personPresent()) {
|
|
6495
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
6382
6496
|
}
|
|
6383
|
-
|
|
6384
|
-
}
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
import os4 from "node:os";
|
|
6388
|
-
|
|
6389
|
-
// src/launcher/cascade.ts
|
|
6390
|
-
import path18 from "node:path";
|
|
6391
|
-
|
|
6392
|
-
// src/pathNorm.ts
|
|
6393
|
-
import path17 from "node:path";
|
|
6394
|
-
function expandTilde(value, home) {
|
|
6395
|
-
if (value === "~") {
|
|
6396
|
-
return home;
|
|
6497
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
6498
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
6499
|
+
if (result.timedOut) {
|
|
6500
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
6397
6501
|
}
|
|
6398
|
-
if (
|
|
6399
|
-
|
|
6502
|
+
if (result.status !== 0) {
|
|
6503
|
+
const stderr = result.stderr.trim();
|
|
6504
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
6505
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
6400
6506
|
}
|
|
6401
|
-
|
|
6507
|
+
const token = result.stdout.trim();
|
|
6508
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
6402
6509
|
}
|
|
6403
|
-
function
|
|
6404
|
-
|
|
6510
|
+
function warmCommandFor(subject) {
|
|
6511
|
+
const [kind, name] = subject.split(" ");
|
|
6512
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
6405
6513
|
}
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
6416
|
-
if (loaded === void 0) {
|
|
6417
|
-
return void 0;
|
|
6418
|
-
}
|
|
6419
|
-
return { name, profile: loaded.config, entryOrder: loaded.entryOrder, filepath: loaded.filepath };
|
|
6420
|
-
};
|
|
6421
|
-
const limit = globalConfig?.config.walkUpLimit;
|
|
6422
|
-
const dirs = walkDirectoryAncestors(params.cwd, {
|
|
6423
|
-
home: params.home,
|
|
6424
|
-
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
6425
|
-
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
6426
|
-
});
|
|
6427
|
-
const levels = [];
|
|
6428
|
-
for (const dir of dirs) {
|
|
6429
|
-
const portable = loadConfigFile(path18.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path18.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6430
|
-
const portableLocal = loadConfigFile(path18.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path18.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6431
|
-
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
6432
|
-
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
6433
|
-
return {
|
|
6434
|
-
rule,
|
|
6435
|
-
...entryOrder === void 0 ? {} : { entryOrder },
|
|
6436
|
-
filepath: params.paths.directoryRulesFile
|
|
6437
|
-
};
|
|
6438
|
-
});
|
|
6439
|
-
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
6440
|
-
continue;
|
|
6514
|
+
function resolveCredential(params) {
|
|
6515
|
+
const target = params.credential.target ?? "bearer";
|
|
6516
|
+
const cacheBlock = params.credential.cache;
|
|
6517
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
6518
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
6519
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
6520
|
+
const entry = cache.port.read(store, params.subject);
|
|
6521
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
6522
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
6441
6523
|
}
|
|
6442
|
-
levels.push({
|
|
6443
|
-
dir,
|
|
6444
|
-
...portable === void 0 ? {} : { portable: { config: portable.config, entryOrder: portable.entryOrder, filepath: portable.filepath } },
|
|
6445
|
-
...rules.length === 0 ? {} : { rules },
|
|
6446
|
-
...portableLocal === void 0 ? {} : { portableLocal: { config: portableLocal.config, entryOrder: portableLocal.entryOrder, filepath: portableLocal.filepath } }
|
|
6447
|
-
});
|
|
6448
6524
|
}
|
|
6449
|
-
const
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
};
|
|
6457
|
-
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
6458
|
-
}
|
|
6459
|
-
function readDirectorySelections(loaded) {
|
|
6460
|
-
let identity;
|
|
6461
|
-
let configProfile;
|
|
6462
|
-
for (const level of loaded.input.levels ?? []) {
|
|
6463
|
-
for (const source of [level.portable?.config, ...(level.rules ?? []).map((entry) => entry.rule), level.portableLocal?.config]) {
|
|
6464
|
-
if (source === void 0) {
|
|
6465
|
-
continue;
|
|
6466
|
-
}
|
|
6467
|
-
if (source.identity !== void 0) {
|
|
6468
|
-
identity = source.identity;
|
|
6469
|
-
}
|
|
6470
|
-
if (source.configProfile !== void 0) {
|
|
6471
|
-
configProfile = source.configProfile;
|
|
6525
|
+
const attempts = [];
|
|
6526
|
+
const warnings = [];
|
|
6527
|
+
for (const source of params.credential.sources) {
|
|
6528
|
+
const outcome = trySource(source, params);
|
|
6529
|
+
if (outcome.ok) {
|
|
6530
|
+
if (cache !== void 0 && store !== void 0) {
|
|
6531
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
6472
6532
|
}
|
|
6533
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
6534
|
+
}
|
|
6535
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
6536
|
+
attempts.push(line);
|
|
6537
|
+
if (outcome.warn) {
|
|
6538
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
6473
6539
|
}
|
|
6474
6540
|
}
|
|
6475
|
-
|
|
6476
|
-
}
|
|
6477
|
-
|
|
6478
|
-
// src/usage/preflight.ts
|
|
6479
|
-
var MS_PER_SECOND2 = 1e3;
|
|
6480
|
-
var SECONDS_PER_MINUTE = 60;
|
|
6481
|
-
var MINUTES_PER_HOUR = 60;
|
|
6482
|
-
var HOURS_PER_DAY = 24;
|
|
6483
|
-
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
6484
|
-
function formatAge(ms) {
|
|
6485
|
-
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
6486
|
-
if (minutes < MINUTES_PER_HOUR) {
|
|
6487
|
-
return `${String(Math.max(minutes, 0))}m`;
|
|
6488
|
-
}
|
|
6489
|
-
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
6490
|
-
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
6541
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
6542
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
6491
6543
|
}
|
|
6492
|
-
function
|
|
6493
|
-
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
6494
|
-
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
6495
|
-
return { reset: true, utilization: 0 };
|
|
6496
|
-
}
|
|
6544
|
+
function credentialVariables(target, token) {
|
|
6497
6545
|
return {
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
...window.status === void 0 ? {} : { status: window.status }
|
|
6546
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
6547
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
6548
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
6502
6549
|
};
|
|
6503
6550
|
}
|
|
6504
|
-
function quotaWarnings(snapshot, provider, nowMs) {
|
|
6505
|
-
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
6506
|
-
const unified = rateLimit?.unified;
|
|
6507
|
-
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
6508
|
-
return [];
|
|
6509
|
-
}
|
|
6510
|
-
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
6511
|
-
const windows = [
|
|
6512
|
-
{ name: "five-hour", window: unified.fiveHour },
|
|
6513
|
-
{ name: "seven-day", window: unified.sevenDay }
|
|
6514
|
-
];
|
|
6515
|
-
return windows.flatMap(({ name, window }) => {
|
|
6516
|
-
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
6517
|
-
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
6518
|
-
return [];
|
|
6519
|
-
}
|
|
6520
|
-
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
6521
|
-
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
6522
|
-
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
6523
|
-
});
|
|
6524
|
-
}
|
|
6525
|
-
|
|
6526
|
-
// src/usage/poolPick.ts
|
|
6527
|
-
import path19 from "node:path";
|
|
6528
|
-
import { z as z9 } from "zod";
|
|
6529
6551
|
|
|
6530
|
-
// src/
|
|
6531
|
-
var
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
const tierField = tier === void 0 ? {} : { tier };
|
|
6537
|
-
if (tier === void 0) {
|
|
6538
|
-
return { kind: "subscription", capacity: 1, recognised: false };
|
|
6552
|
+
// src/providersStore.ts
|
|
6553
|
+
var ProviderNotFoundError = class extends CliError {
|
|
6554
|
+
constructor(name) {
|
|
6555
|
+
super(`No provider named "${name}". Run \`agent-shim provider add ${name}\` first.`);
|
|
6556
|
+
this.name = name;
|
|
6557
|
+
this.name = "ProviderNotFoundError";
|
|
6539
6558
|
}
|
|
6540
|
-
|
|
6541
|
-
|
|
6559
|
+
name;
|
|
6560
|
+
};
|
|
6561
|
+
var ProviderAlreadyExistsError = class extends CliError {
|
|
6562
|
+
constructor(providerName) {
|
|
6563
|
+
super(`A provider named "${providerName}" already exists.`);
|
|
6564
|
+
this.providerName = providerName;
|
|
6565
|
+
this.name = "ProviderAlreadyExistsError";
|
|
6542
6566
|
}
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6567
|
+
providerName;
|
|
6568
|
+
};
|
|
6569
|
+
var InvalidProviderNameError = class extends CliError {
|
|
6570
|
+
constructor(attemptedName) {
|
|
6571
|
+
super(
|
|
6572
|
+
`"${attemptedName}" is not a valid provider name. Provider names must start with a letter or number and may then contain letters, numbers, dots, hyphens, underscores, and at signs.`
|
|
6573
|
+
);
|
|
6574
|
+
this.attemptedName = attemptedName;
|
|
6575
|
+
this.name = "InvalidProviderNameError";
|
|
6546
6576
|
}
|
|
6547
|
-
|
|
6577
|
+
attemptedName;
|
|
6578
|
+
};
|
|
6579
|
+
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
6580
|
+
function providerJsonPath(paths, name) {
|
|
6581
|
+
return path17.join(paths.providersDir, `${name}.json`);
|
|
6548
6582
|
}
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
var MS_PER_SECOND3 = 1e3;
|
|
6552
|
-
function epochSecondsToIso(value) {
|
|
6553
|
-
if (value === void 0) {
|
|
6554
|
-
return void 0;
|
|
6555
|
-
}
|
|
6556
|
-
const seconds = Number(value);
|
|
6557
|
-
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
6583
|
+
function providerExists(paths, name) {
|
|
6584
|
+
return fs6.existsSync(providerJsonPath(paths, name));
|
|
6558
6585
|
}
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
6586
|
+
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
6587
|
+
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
6588
|
+
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
6589
|
+
function isRecord3(value) {
|
|
6590
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6565
6591
|
}
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
6569
|
-
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
6570
|
-
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
6571
|
-
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
6592
|
+
function legacyProviderConversion(raw) {
|
|
6593
|
+
if (!isRecord3(raw) || "credential" in raw) {
|
|
6572
6594
|
return void 0;
|
|
6573
6595
|
}
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
if (
|
|
6596
|
+
const env = isRecord3(raw.env) ? raw.env : void 0;
|
|
6597
|
+
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
6598
|
+
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
6599
|
+
if (fields.length === 0) {
|
|
6578
6600
|
return void 0;
|
|
6579
6601
|
}
|
|
6580
|
-
const
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6602
|
+
const sources = [];
|
|
6603
|
+
if (typeof raw.tokenEnv === "string") {
|
|
6604
|
+
sources.push({ env: raw.tokenEnv });
|
|
6605
|
+
}
|
|
6606
|
+
if (Array.isArray(raw.tokenCommand)) {
|
|
6607
|
+
sources.push({ command: raw.tokenCommand });
|
|
6608
|
+
}
|
|
6609
|
+
if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
|
|
6610
|
+
sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
|
|
6611
|
+
}
|
|
6612
|
+
const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
|
|
6613
|
+
const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
|
|
6614
|
+
const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
|
|
6586
6615
|
return {
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6616
|
+
fields,
|
|
6617
|
+
replacement: {
|
|
6618
|
+
...rest,
|
|
6619
|
+
credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
|
|
6620
|
+
...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
|
|
6621
|
+
}
|
|
6593
6622
|
};
|
|
6594
6623
|
}
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
var ErrorObjectSchema = z8.looseObject({
|
|
6605
|
-
type: z8.string().optional(),
|
|
6606
|
-
code: z8.union([z8.string(), z8.number()]).optional(),
|
|
6607
|
-
message: z8.string().optional()
|
|
6608
|
-
});
|
|
6609
|
-
|
|
6610
|
-
// src/usage/middleware.ts
|
|
6611
|
-
var ANTHROPIC_PROVIDER = "anthropic";
|
|
6612
|
-
|
|
6613
|
-
// src/usage/pick.ts
|
|
6614
|
-
var MS_PER_SECOND4 = 1e3;
|
|
6615
|
-
var MS_PER_HOUR = 36e5;
|
|
6616
|
-
var HOURS_PER_DAY2 = 24;
|
|
6617
|
-
var FIVE_HOURS = 5;
|
|
6618
|
-
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
6619
|
-
var PERCENT = 100;
|
|
6620
|
-
var SEVEN_DAYS = 7;
|
|
6621
|
-
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
6622
|
-
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
6623
|
-
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
6624
|
-
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
6625
|
-
function unifiedOf(state) {
|
|
6626
|
-
return state?.rateLimit?.unified;
|
|
6627
|
-
}
|
|
6628
|
-
function limitBlockedUntil(state, nowMs) {
|
|
6629
|
-
const limit = state?.lastLimit;
|
|
6630
|
-
if (state === void 0 || limit === void 0) {
|
|
6631
|
-
return void 0;
|
|
6624
|
+
var LegacyProviderFileError = class extends CliError {
|
|
6625
|
+
constructor(filePath, conversion) {
|
|
6626
|
+
super(
|
|
6627
|
+
`${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
|
|
6628
|
+
` + JSON.stringify(conversion.replacement, null, 2)
|
|
6629
|
+
);
|
|
6630
|
+
this.filePath = filePath;
|
|
6631
|
+
this.conversion = conversion;
|
|
6632
|
+
this.name = "LegacyProviderFileError";
|
|
6632
6633
|
}
|
|
6633
|
-
|
|
6634
|
+
filePath;
|
|
6635
|
+
conversion;
|
|
6636
|
+
};
|
|
6637
|
+
function parseProviderFile(filePath, raw) {
|
|
6638
|
+
const conversion = legacyProviderConversion(raw);
|
|
6639
|
+
if (conversion !== void 0) {
|
|
6640
|
+
throw new LegacyProviderFileError(filePath, conversion);
|
|
6641
|
+
}
|
|
6642
|
+
const parsed = ProviderSchema.safeParse(raw);
|
|
6643
|
+
if (!parsed.success) {
|
|
6644
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
6645
|
+
}
|
|
6646
|
+
return parsed.data;
|
|
6647
|
+
}
|
|
6648
|
+
function readProvider(paths, name) {
|
|
6649
|
+
const filePath = providerJsonPath(paths, name);
|
|
6650
|
+
if (!fs6.existsSync(filePath)) {
|
|
6634
6651
|
return void 0;
|
|
6635
6652
|
}
|
|
6636
|
-
const
|
|
6637
|
-
return
|
|
6653
|
+
const raw = JSON.parse(fs6.readFileSync(filePath, "utf8"));
|
|
6654
|
+
return parseProviderFile(filePath, raw);
|
|
6638
6655
|
}
|
|
6639
|
-
function
|
|
6640
|
-
const
|
|
6641
|
-
|
|
6656
|
+
function loadProvider(providersDir, name, port) {
|
|
6657
|
+
const filePath = path17.join(providersDir, `${name}.json`);
|
|
6658
|
+
const raw = port.readConfigFile(filePath);
|
|
6659
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
6642
6660
|
}
|
|
6643
|
-
function
|
|
6644
|
-
const
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6661
|
+
function listProviders(paths) {
|
|
6662
|
+
const names = listProviderNames(
|
|
6663
|
+
(dir) => fs6.readdirSync(dir),
|
|
6664
|
+
paths.providersDir
|
|
6665
|
+
);
|
|
6666
|
+
const result = [];
|
|
6667
|
+
for (const name of names) {
|
|
6668
|
+
let provider;
|
|
6669
|
+
try {
|
|
6670
|
+
provider = readProvider(paths, name);
|
|
6671
|
+
} catch (error) {
|
|
6672
|
+
if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
|
|
6673
|
+
throw error;
|
|
6674
|
+
}
|
|
6675
|
+
console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
|
|
6676
|
+
continue;
|
|
6677
|
+
}
|
|
6678
|
+
if (provider !== void 0) {
|
|
6679
|
+
result.push({ name, provider });
|
|
6649
6680
|
}
|
|
6650
6681
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6682
|
+
return result;
|
|
6683
|
+
}
|
|
6684
|
+
function listProviderNames(readdir, providersDir) {
|
|
6685
|
+
let entries;
|
|
6686
|
+
try {
|
|
6687
|
+
entries = readdir(providersDir);
|
|
6688
|
+
} catch {
|
|
6689
|
+
return [];
|
|
6657
6690
|
}
|
|
6658
|
-
return (
|
|
6691
|
+
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
|
|
6659
6692
|
}
|
|
6660
|
-
function
|
|
6661
|
-
return
|
|
6693
|
+
function describeProviderEndpoint(provider) {
|
|
6694
|
+
return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
|
|
6662
6695
|
}
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6696
|
+
var ProviderKindMismatchError = class extends UsageError {
|
|
6697
|
+
constructor(message) {
|
|
6698
|
+
super(message);
|
|
6699
|
+
this.name = "ProviderKindMismatchError";
|
|
6666
6700
|
}
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
return
|
|
6670
|
-
}
|
|
6671
|
-
function capacityOf(plan) {
|
|
6672
|
-
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
6701
|
+
};
|
|
6702
|
+
function isEmptyCodexConfig(config) {
|
|
6703
|
+
return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
|
|
6673
6704
|
}
|
|
6674
|
-
function
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
const base = { identity: member.identity, plan };
|
|
6678
|
-
if (member.readError !== void 0) {
|
|
6679
|
-
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
6705
|
+
function addProvider(paths, name, input) {
|
|
6706
|
+
if (providerExists(paths, name)) {
|
|
6707
|
+
throw new ProviderAlreadyExistsError(name);
|
|
6680
6708
|
}
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
6684
|
-
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
6709
|
+
if (!PROVIDER_NAME_RE.test(name)) {
|
|
6710
|
+
throw new InvalidProviderNameError(name);
|
|
6685
6711
|
}
|
|
6686
|
-
const
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
6690
|
-
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
6691
|
-
const windowReasons = [
|
|
6692
|
-
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
6693
|
-
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
6694
|
-
];
|
|
6695
|
-
const blockedUntil = Math.max(
|
|
6696
|
-
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
6697
|
-
limitBlockedUntil(state, nowMs) ?? 0
|
|
6698
|
-
);
|
|
6699
|
-
if (blockedUntil > nowMs) {
|
|
6700
|
-
if (unified.overageStatus === "allowed") {
|
|
6701
|
-
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
6702
|
-
}
|
|
6703
|
-
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
6712
|
+
const kind = input.kind ?? "http";
|
|
6713
|
+
if (kind === "codex" && input.baseUrl !== void 0) {
|
|
6714
|
+
throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
|
|
6704
6715
|
}
|
|
6705
|
-
if (
|
|
6706
|
-
|
|
6716
|
+
if (kind === "http" && input.baseUrl === void 0) {
|
|
6717
|
+
throw new ProviderKindMismatchError("An http provider needs --base-url.");
|
|
6707
6718
|
}
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
6719
|
+
if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
|
|
6720
|
+
throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
|
|
6711
6721
|
}
|
|
6712
|
-
const
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6717
|
-
const burn = fiveHourBurn(member.records);
|
|
6718
|
-
return {
|
|
6719
|
-
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
6720
|
-
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
6721
|
-
...burn === void 0 ? {} : { burn }
|
|
6722
|
+
const shared = {
|
|
6723
|
+
displayName: input.displayName,
|
|
6724
|
+
credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
|
|
6725
|
+
...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
|
|
6722
6726
|
};
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
const { members, nowMs } = input;
|
|
6726
|
-
const assessments = members.map((member) => assess(member, nowMs));
|
|
6727
|
-
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
6728
|
-
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
6729
|
-
const candidates = assessments.map((assessment) => {
|
|
6730
|
-
const { candidate, fiveHour, burn } = assessment;
|
|
6731
|
-
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
6732
|
-
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
6733
|
-
return { ...candidate, feasible: true };
|
|
6734
|
-
}
|
|
6735
|
-
const runsDryInMs = fiveHour.remaining / pace;
|
|
6736
|
-
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
6737
|
-
return { ...candidate, feasible: true };
|
|
6738
|
-
}
|
|
6739
|
-
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
6740
|
-
});
|
|
6741
|
-
const ordered = [...candidates].sort(
|
|
6742
|
-
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
6727
|
+
const parsed = ProviderSchema.safeParse(
|
|
6728
|
+
kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
|
|
6743
6729
|
);
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6730
|
+
if (!parsed.success) {
|
|
6731
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6732
|
+
}
|
|
6733
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
6734
|
+
return parsed.data;
|
|
6735
|
+
}
|
|
6736
|
+
function updateProvider(paths, name, input) {
|
|
6737
|
+
const existing = readProvider(paths, name);
|
|
6738
|
+
if (existing === void 0) {
|
|
6739
|
+
throw new ProviderNotFoundError(name);
|
|
6740
|
+
}
|
|
6741
|
+
const unset = new Set(input.unsetEnv);
|
|
6742
|
+
const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
|
|
6743
|
+
const target = input.target ?? existing.credential.target;
|
|
6744
|
+
const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
|
|
6745
|
+
if (isCodexProvider(existing) && input.baseUrl !== void 0) {
|
|
6746
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
|
|
6755
6747
|
}
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6748
|
+
if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
|
|
6749
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
|
|
6750
|
+
}
|
|
6751
|
+
const shared = {
|
|
6752
|
+
...input.displayName === void 0 ? {} : { displayName: input.displayName },
|
|
6753
|
+
credential: {
|
|
6754
|
+
sources: input.sources ?? existing.credential.sources,
|
|
6755
|
+
...target === void 0 ? {} : { target },
|
|
6756
|
+
...cache === void 0 ? {} : { cache }
|
|
6757
|
+
},
|
|
6758
|
+
env: Object.keys(env).length === 0 ? void 0 : env
|
|
6762
6759
|
};
|
|
6760
|
+
let candidate;
|
|
6761
|
+
if (isCodexProvider(existing)) {
|
|
6762
|
+
const codex = {
|
|
6763
|
+
...existing.codex,
|
|
6764
|
+
...input.codex,
|
|
6765
|
+
...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
|
|
6766
|
+
};
|
|
6767
|
+
candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
|
|
6768
|
+
} else {
|
|
6769
|
+
candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
|
|
6770
|
+
}
|
|
6771
|
+
const parsed = ProviderSchema.safeParse(candidate);
|
|
6772
|
+
if (!parsed.success) {
|
|
6773
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6774
|
+
}
|
|
6775
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
6776
|
+
return parsed.data;
|
|
6763
6777
|
}
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
// src/usage/poolPick.ts
|
|
6771
|
-
var SCHEMA_VERSION = 1;
|
|
6772
|
-
var PickEntrySchema = z9.strictObject({ identity: z9.string().min(1), at: z9.iso.datetime() });
|
|
6773
|
-
var PicksFileSchema = z9.strictObject({
|
|
6774
|
-
schemaVersion: z9.literal(SCHEMA_VERSION),
|
|
6775
|
-
picks: z9.record(z9.string().min(1), PickEntrySchema)
|
|
6776
|
-
});
|
|
6777
|
-
function errorMessage(error) {
|
|
6778
|
-
return error instanceof Error ? error.message : String(error);
|
|
6778
|
+
function removeProvider(paths, name) {
|
|
6779
|
+
if (!providerExists(paths, name)) {
|
|
6780
|
+
throw new ProviderNotFoundError(name);
|
|
6781
|
+
}
|
|
6782
|
+
fs6.rmSync(providerJsonPath(paths, name));
|
|
6779
6783
|
}
|
|
6780
|
-
function
|
|
6781
|
-
|
|
6782
|
-
const
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
try {
|
|
6786
|
-
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
6787
|
-
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
6788
|
-
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
6789
|
-
} catch (error) {
|
|
6790
|
-
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
6791
|
-
return { identity, records, readError: errorMessage(error) };
|
|
6792
|
-
}
|
|
6793
|
-
throw error;
|
|
6784
|
+
function cascadeProviderName(cascade) {
|
|
6785
|
+
let name;
|
|
6786
|
+
for (const layer of assembleCascade(cascade).layers) {
|
|
6787
|
+
if (layer.launch?.provider !== void 0) {
|
|
6788
|
+
name = layer.launch.provider;
|
|
6794
6789
|
}
|
|
6795
|
-
}
|
|
6790
|
+
}
|
|
6791
|
+
return name;
|
|
6796
6792
|
}
|
|
6797
|
-
function
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6793
|
+
function resolveProvider(params) {
|
|
6794
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
6795
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
6796
|
+
if (name === void 0) {
|
|
6797
|
+
return void 0;
|
|
6798
|
+
}
|
|
6799
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
6800
|
+
if (definition === void 0) {
|
|
6801
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
6802
|
+
return {
|
|
6803
|
+
ok: false,
|
|
6804
|
+
status: 1,
|
|
6805
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
6806
|
+
};
|
|
6807
|
+
}
|
|
6808
|
+
if (params.credentials === void 0) {
|
|
6809
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
6810
|
+
}
|
|
6811
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
6812
|
+
if (!resolution.ok) {
|
|
6813
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
6814
|
+
}
|
|
6815
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
6804
6816
|
}
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6817
|
+
|
|
6818
|
+
// src/directoryRulesStore.ts
|
|
6819
|
+
var DirectoryRuleNotFoundError = class extends CliError {
|
|
6820
|
+
constructor(rulePath) {
|
|
6821
|
+
super(`No directory rule found for path "${rulePath}".`);
|
|
6822
|
+
this.rulePath = rulePath;
|
|
6823
|
+
this.name = "DirectoryRuleNotFoundError";
|
|
6810
6824
|
}
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6825
|
+
rulePath;
|
|
6826
|
+
};
|
|
6827
|
+
var DirectoryRuleMissingTargetError = class extends CliError {
|
|
6828
|
+
constructor() {
|
|
6829
|
+
super("A directory rule must set at least one of --config-profile or --identity.");
|
|
6830
|
+
this.name = "DirectoryRuleMissingTargetError";
|
|
6816
6831
|
}
|
|
6817
|
-
|
|
6818
|
-
|
|
6832
|
+
};
|
|
6833
|
+
var DirectoryRuleAlreadyExistsError = class extends CliError {
|
|
6834
|
+
constructor(rulePath) {
|
|
6835
|
+
super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
|
|
6836
|
+
this.rulePath = rulePath;
|
|
6837
|
+
this.name = "DirectoryRuleAlreadyExistsError";
|
|
6838
|
+
}
|
|
6839
|
+
rulePath;
|
|
6840
|
+
};
|
|
6841
|
+
function readDirectoryRules(paths) {
|
|
6842
|
+
return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
|
|
6819
6843
|
}
|
|
6820
|
-
function
|
|
6821
|
-
const
|
|
6822
|
-
|
|
6823
|
-
|
|
6844
|
+
function writeDirectoryRules(paths, rules) {
|
|
6845
|
+
const parsed = DirectoryRulesSchema.safeParse(rules);
|
|
6846
|
+
if (!parsed.success) {
|
|
6847
|
+
throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
|
|
6848
|
+
}
|
|
6849
|
+
writeJsonAtomic(paths.directoryRulesFile, parsed.data);
|
|
6824
6850
|
}
|
|
6825
|
-
function
|
|
6826
|
-
|
|
6827
|
-
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
6828
|
-
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
6829
|
-
fs12.mkdirPrivate(path19.dirname(picksFile));
|
|
6830
|
-
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
6831
|
-
`);
|
|
6851
|
+
function listDirectoryRules(paths) {
|
|
6852
|
+
return readDirectoryRules(paths).rules;
|
|
6832
6853
|
}
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6854
|
+
function addDirectoryRule(paths, rulePath, options) {
|
|
6855
|
+
if (options.configProfile === void 0 && options.identity === void 0) {
|
|
6856
|
+
throw new DirectoryRuleMissingTargetError();
|
|
6857
|
+
}
|
|
6858
|
+
const current = readDirectoryRules(paths);
|
|
6859
|
+
if (current.rules.some((rule) => rule.path === rulePath)) {
|
|
6860
|
+
throw new DirectoryRuleAlreadyExistsError(rulePath);
|
|
6861
|
+
}
|
|
6862
|
+
const created = buildNewRule(rulePath, options);
|
|
6863
|
+
writeDirectoryRules(paths, { ...current, rules: [...current.rules, created] });
|
|
6864
|
+
return created;
|
|
6841
6865
|
}
|
|
6842
|
-
function
|
|
6843
|
-
|
|
6844
|
-
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
6866
|
+
function ruleHasEffect(rule) {
|
|
6867
|
+
return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
|
|
6845
6868
|
}
|
|
6846
|
-
function
|
|
6847
|
-
const
|
|
6848
|
-
const
|
|
6849
|
-
|
|
6850
|
-
|
|
6869
|
+
function updateDirectoryRule(paths, rulePath, options) {
|
|
6870
|
+
const current = readDirectoryRules(paths);
|
|
6871
|
+
const index = current.rules.findIndex((rule) => rule.path === rulePath);
|
|
6872
|
+
const existing = index === -1 ? void 0 : current.rules[index];
|
|
6873
|
+
if (existing === void 0) {
|
|
6874
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
6851
6875
|
}
|
|
6852
|
-
const {
|
|
6853
|
-
|
|
6854
|
-
|
|
6876
|
+
const { configProfile, identity, ...rest } = existing;
|
|
6877
|
+
const nextConfigProfile = options.configProfile === void 0 ? configProfile : options.configProfile === false ? void 0 : options.configProfile;
|
|
6878
|
+
const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
|
|
6879
|
+
const updated = {
|
|
6880
|
+
...rest,
|
|
6881
|
+
...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
|
|
6882
|
+
...nextIdentity === void 0 ? {} : { identity: nextIdentity }
|
|
6883
|
+
};
|
|
6884
|
+
if (!ruleHasEffect(updated)) {
|
|
6885
|
+
throw new DirectoryRuleMissingTargetError();
|
|
6855
6886
|
}
|
|
6856
|
-
|
|
6857
|
-
|
|
6887
|
+
const nextRules = [...current.rules];
|
|
6888
|
+
nextRules[index] = updated;
|
|
6889
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
6890
|
+
return updated;
|
|
6891
|
+
}
|
|
6892
|
+
function buildNewRule(rulePath, options) {
|
|
6893
|
+
return {
|
|
6894
|
+
path: rulePath,
|
|
6895
|
+
...options.configProfile !== void 0 ? { configProfile: options.configProfile } : {},
|
|
6896
|
+
...options.identity !== void 0 ? { identity: options.identity } : {}
|
|
6897
|
+
};
|
|
6898
|
+
}
|
|
6899
|
+
function removeDirectoryRule(paths, rulePath) {
|
|
6900
|
+
const current = readDirectoryRules(paths);
|
|
6901
|
+
const nextRules = current.rules.filter((rule) => rule.path !== rulePath);
|
|
6902
|
+
if (nextRules.length === current.rules.length) {
|
|
6903
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
6858
6904
|
}
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6905
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
6906
|
+
}
|
|
6907
|
+
|
|
6908
|
+
// src/checkReport.ts
|
|
6909
|
+
import os4 from "node:os";
|
|
6910
|
+
|
|
6911
|
+
// src/launcher/cascade.ts
|
|
6912
|
+
import path19 from "node:path";
|
|
6913
|
+
|
|
6914
|
+
// src/pathNorm.ts
|
|
6915
|
+
import path18 from "node:path";
|
|
6916
|
+
function expandTilde(value, home) {
|
|
6917
|
+
if (value === "~") {
|
|
6918
|
+
return home;
|
|
6863
6919
|
}
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6920
|
+
if (value.startsWith("~/")) {
|
|
6921
|
+
return path18.join(home, value.slice(2));
|
|
6922
|
+
}
|
|
6923
|
+
return value;
|
|
6924
|
+
}
|
|
6925
|
+
function normaliseRulePath(rulePath, home) {
|
|
6926
|
+
return path18.resolve(expandTilde(rulePath, home));
|
|
6927
|
+
}
|
|
6928
|
+
|
|
6929
|
+
// src/launcher/cascade.ts
|
|
6930
|
+
var PORTABLE_CONFIG_FILENAME = ".agent-shim.json";
|
|
6931
|
+
var PORTABLE_LOCAL_CONFIG_FILENAME = ".agent-shim.local.json";
|
|
6932
|
+
function loadCascadeInput(params) {
|
|
6933
|
+
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
6934
|
+
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
6935
|
+
const loadProfile = (name) => {
|
|
6936
|
+
const filepath = path19.join(params.paths.configProfilesDir, `${name}.json`);
|
|
6937
|
+
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
6938
|
+
if (loaded === void 0) {
|
|
6939
|
+
return void 0;
|
|
6874
6940
|
}
|
|
6875
|
-
|
|
6876
|
-
|
|
6941
|
+
return { name, profile: loaded.config, entryOrder: loaded.entryOrder, filepath: loaded.filepath };
|
|
6942
|
+
};
|
|
6943
|
+
const limit = globalConfig?.config.walkUpLimit;
|
|
6944
|
+
const dirs = walkDirectoryAncestors(params.cwd, {
|
|
6945
|
+
home: params.home,
|
|
6946
|
+
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
6947
|
+
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
6948
|
+
});
|
|
6949
|
+
const levels = [];
|
|
6950
|
+
for (const dir of dirs) {
|
|
6951
|
+
const portable = loadConfigFile(path19.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path19.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6952
|
+
const portableLocal = loadConfigFile(path19.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path19.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6953
|
+
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
6954
|
+
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
6877
6955
|
return {
|
|
6878
|
-
|
|
6879
|
-
|
|
6956
|
+
rule,
|
|
6957
|
+
...entryOrder === void 0 ? {} : { entryOrder },
|
|
6958
|
+
filepath: params.paths.directoryRulesFile
|
|
6880
6959
|
};
|
|
6960
|
+
});
|
|
6961
|
+
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
6962
|
+
continue;
|
|
6963
|
+
}
|
|
6964
|
+
levels.push({
|
|
6965
|
+
dir,
|
|
6966
|
+
...portable === void 0 ? {} : { portable: { config: portable.config, entryOrder: portable.entryOrder, filepath: portable.filepath } },
|
|
6967
|
+
...rules.length === 0 ? {} : { rules },
|
|
6968
|
+
...portableLocal === void 0 ? {} : { portableLocal: { config: portableLocal.config, entryOrder: portableLocal.entryOrder, filepath: portableLocal.filepath } }
|
|
6969
|
+
});
|
|
6970
|
+
}
|
|
6971
|
+
const input = {
|
|
6972
|
+
home: params.home,
|
|
6973
|
+
...globalConfig === void 0 ? {} : { globalConfig: { config: globalConfig.config, entryOrder: globalConfig.entryOrder, filepath: globalConfig.filepath } },
|
|
6974
|
+
...params.baseConfigProfile === void 0 ? {} : { baseConfigProfile: params.baseConfigProfile },
|
|
6975
|
+
loadProfile,
|
|
6976
|
+
levels,
|
|
6977
|
+
...params.cliOverride === void 0 ? {} : { cliOverride: params.cliOverride }
|
|
6978
|
+
};
|
|
6979
|
+
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
6980
|
+
}
|
|
6981
|
+
function readDirectorySelections(loaded) {
|
|
6982
|
+
let identity;
|
|
6983
|
+
let configProfile;
|
|
6984
|
+
for (const level of loaded.input.levels ?? []) {
|
|
6985
|
+
for (const source of [level.portable?.config, ...(level.rules ?? []).map((entry) => entry.rule), level.portableLocal?.config]) {
|
|
6986
|
+
if (source === void 0) {
|
|
6987
|
+
continue;
|
|
6988
|
+
}
|
|
6989
|
+
if (source.identity !== void 0) {
|
|
6990
|
+
identity = source.identity;
|
|
6991
|
+
}
|
|
6992
|
+
if (source.configProfile !== void 0) {
|
|
6993
|
+
configProfile = source.configProfile;
|
|
6994
|
+
}
|
|
6881
6995
|
}
|
|
6882
|
-
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
6883
|
-
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
6884
6996
|
}
|
|
6997
|
+
return { ...identity === void 0 ? {} : { identity }, ...configProfile === void 0 ? {} : { configProfile } };
|
|
6885
6998
|
}
|
|
6886
6999
|
|
|
6887
|
-
// src/
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
reasons: candidate.reasons
|
|
6897
|
-
};
|
|
7000
|
+
// src/realCredentialCache.ts
|
|
7001
|
+
import { spawnSync } from "node:child_process";
|
|
7002
|
+
import fs7 from "node:fs";
|
|
7003
|
+
import path20 from "node:path";
|
|
7004
|
+
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7005
|
+
var CACHE_DIR_MODE = 448;
|
|
7006
|
+
var CACHE_FILE_MODE = 384;
|
|
7007
|
+
function cacheFileNameFor(owner) {
|
|
7008
|
+
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
6898
7009
|
}
|
|
6899
|
-
function
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
7010
|
+
function parseEntry(raw) {
|
|
7011
|
+
let json;
|
|
7012
|
+
try {
|
|
7013
|
+
json = JSON.parse(raw);
|
|
7014
|
+
} catch {
|
|
7015
|
+
return void 0;
|
|
7016
|
+
}
|
|
7017
|
+
const parsed = CachedCredentialSchema.safeParse(json);
|
|
7018
|
+
return parsed.success ? parsed.data : void 0;
|
|
7019
|
+
}
|
|
7020
|
+
function requireKeychain() {
|
|
7021
|
+
if (process.platform !== "darwin") {
|
|
7022
|
+
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
function security(args, stdin) {
|
|
7026
|
+
const result = spawnSync("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
7027
|
+
return { status: result.status, stdout: result.stdout };
|
|
7028
|
+
}
|
|
7029
|
+
function createRealCredentialCache(root) {
|
|
7030
|
+
const dir = path20.join(root, "credential-cache");
|
|
7031
|
+
const filePath = (owner) => path20.join(dir, cacheFileNameFor(owner));
|
|
6910
7032
|
return {
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
7033
|
+
read(store, owner) {
|
|
7034
|
+
if (store === "file") {
|
|
7035
|
+
try {
|
|
7036
|
+
return parseEntry(fs7.readFileSync(filePath(owner), "utf8"));
|
|
7037
|
+
} catch {
|
|
7038
|
+
return void 0;
|
|
7039
|
+
}
|
|
7040
|
+
}
|
|
7041
|
+
requireKeychain();
|
|
7042
|
+
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
7043
|
+
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
7044
|
+
},
|
|
7045
|
+
write(store, owner, entry) {
|
|
7046
|
+
const serialised = JSON.stringify(entry);
|
|
7047
|
+
if (store === "file") {
|
|
7048
|
+
fs7.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
7049
|
+
fs7.chmodSync(dir, CACHE_DIR_MODE);
|
|
7050
|
+
const target = filePath(owner);
|
|
7051
|
+
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
7052
|
+
fs7.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
7053
|
+
fs7.renameSync(temp, target);
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
requireKeychain();
|
|
7057
|
+
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
7058
|
+
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
7059
|
+
`);
|
|
7060
|
+
if (result.status !== 0) {
|
|
7061
|
+
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
7062
|
+
}
|
|
7063
|
+
},
|
|
7064
|
+
remove(store, owner) {
|
|
7065
|
+
if (store === "file") {
|
|
7066
|
+
fs7.rmSync(filePath(owner), { force: true });
|
|
7067
|
+
return;
|
|
7068
|
+
}
|
|
7069
|
+
requireKeychain();
|
|
7070
|
+
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
7071
|
+
}
|
|
6918
7072
|
};
|
|
6919
7073
|
}
|
|
7074
|
+
function realCredentialCacheEnv(paths) {
|
|
7075
|
+
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
7076
|
+
}
|
|
6920
7077
|
|
|
6921
7078
|
// src/realPorts.ts
|
|
6922
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
7079
|
+
import { spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
6923
7080
|
import { createHash as createHash5 } from "node:crypto";
|
|
6924
|
-
import
|
|
7081
|
+
import fs8 from "node:fs";
|
|
6925
7082
|
import os3 from "node:os";
|
|
6926
|
-
import
|
|
7083
|
+
import path22 from "node:path";
|
|
6927
7084
|
import { isSea } from "node:sea";
|
|
6928
7085
|
|
|
6929
7086
|
// src/versionDiscovery.ts
|
|
6930
7087
|
import os2 from "node:os";
|
|
6931
|
-
import
|
|
7088
|
+
import path21 from "node:path";
|
|
6932
7089
|
function defaultVersionsDir() {
|
|
6933
|
-
return
|
|
7090
|
+
return path21.join(os2.homedir(), ".local", "share", "claude", "versions");
|
|
6934
7091
|
}
|
|
6935
7092
|
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
6936
7093
|
function isNumericDottedVersion(name) {
|
|
@@ -6972,7 +7129,7 @@ function discoverClaudeBinary(options) {
|
|
|
6972
7129
|
const highestVersion = pickHighestVersion(entries);
|
|
6973
7130
|
if (highestVersion !== void 0) {
|
|
6974
7131
|
return {
|
|
6975
|
-
path:
|
|
7132
|
+
path: path21.join(versionsDir, highestVersion),
|
|
6976
7133
|
source: "versions-dir",
|
|
6977
7134
|
version: highestVersion
|
|
6978
7135
|
};
|
|
@@ -6995,7 +7152,7 @@ function isEnoent3(error) {
|
|
|
6995
7152
|
var realFsPort = {
|
|
6996
7153
|
readFileUtf8(filePath) {
|
|
6997
7154
|
try {
|
|
6998
|
-
return
|
|
7155
|
+
return fs8.readFileSync(filePath, "utf8");
|
|
6999
7156
|
} catch (error) {
|
|
7000
7157
|
if (isEnoent3(error)) {
|
|
7001
7158
|
return void 0;
|
|
@@ -7006,7 +7163,7 @@ var realFsPort = {
|
|
|
7006
7163
|
readConfigFile: cosmiconfigReader(),
|
|
7007
7164
|
readdir(dirPath) {
|
|
7008
7165
|
try {
|
|
7009
|
-
return
|
|
7166
|
+
return fs8.readdirSync(dirPath);
|
|
7010
7167
|
} catch (error) {
|
|
7011
7168
|
if (isEnoent3(error)) {
|
|
7012
7169
|
return [];
|
|
@@ -7025,7 +7182,7 @@ var realFarmFs = {
|
|
|
7025
7182
|
lstat(filePath) {
|
|
7026
7183
|
let stat;
|
|
7027
7184
|
try {
|
|
7028
|
-
stat =
|
|
7185
|
+
stat = fs8.lstatSync(filePath);
|
|
7029
7186
|
} catch (error) {
|
|
7030
7187
|
if (isEnoent3(error)) {
|
|
7031
7188
|
return void 0;
|
|
@@ -7037,7 +7194,7 @@ var realFarmFs = {
|
|
|
7037
7194
|
},
|
|
7038
7195
|
readdir(dirPath) {
|
|
7039
7196
|
try {
|
|
7040
|
-
return
|
|
7197
|
+
return fs8.readdirSync(dirPath);
|
|
7041
7198
|
} catch (error) {
|
|
7042
7199
|
if (isEnoent3(error) || isErrorWithCode(error, "ENOTDIR")) {
|
|
7043
7200
|
return [];
|
|
@@ -7046,23 +7203,23 @@ var realFarmFs = {
|
|
|
7046
7203
|
}
|
|
7047
7204
|
},
|
|
7048
7205
|
mkdirp(dirPath) {
|
|
7049
|
-
|
|
7206
|
+
fs8.mkdirSync(dirPath, { recursive: true });
|
|
7050
7207
|
},
|
|
7051
7208
|
symlink(target, linkPath) {
|
|
7052
|
-
|
|
7209
|
+
fs8.symlinkSync(target, linkPath);
|
|
7053
7210
|
},
|
|
7054
7211
|
rename(from, to) {
|
|
7055
|
-
|
|
7212
|
+
fs8.renameSync(from, to);
|
|
7056
7213
|
},
|
|
7057
7214
|
removeRecursive(targetPath) {
|
|
7058
|
-
|
|
7215
|
+
fs8.rmSync(targetPath, { recursive: true, force: true });
|
|
7059
7216
|
},
|
|
7060
7217
|
copyRecursive(from, to) {
|
|
7061
|
-
|
|
7218
|
+
fs8.cpSync(from, to, { recursive: true, force: true, verbatimSymlinks: true });
|
|
7062
7219
|
},
|
|
7063
7220
|
readFileUtf8(filePath) {
|
|
7064
7221
|
try {
|
|
7065
|
-
return
|
|
7222
|
+
return fs8.readFileSync(filePath, "utf8");
|
|
7066
7223
|
} catch (error) {
|
|
7067
7224
|
if (isEnoent3(error)) {
|
|
7068
7225
|
return void 0;
|
|
@@ -7071,36 +7228,36 @@ var realFarmFs = {
|
|
|
7071
7228
|
}
|
|
7072
7229
|
},
|
|
7073
7230
|
writeFileUtf8(filePath, contents) {
|
|
7074
|
-
|
|
7231
|
+
fs8.writeFileSync(filePath, contents, "utf8");
|
|
7075
7232
|
},
|
|
7076
7233
|
mkdirPrivate(dirPath) {
|
|
7077
|
-
|
|
7078
|
-
|
|
7234
|
+
fs8.mkdirSync(dirPath, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
|
|
7235
|
+
fs8.chmodSync(dirPath, OWNER_ONLY_DIR_MODE);
|
|
7079
7236
|
},
|
|
7080
7237
|
writeFilePrivate(filePath, contents) {
|
|
7081
7238
|
const temp = `${filePath}.${String(process.pid)}.tmp`;
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7239
|
+
fs8.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
|
|
7240
|
+
fs8.chmodSync(temp, OWNER_ONLY_FILE_MODE);
|
|
7241
|
+
fs8.renameSync(temp, filePath);
|
|
7085
7242
|
},
|
|
7086
7243
|
appendFilePrivate(filePath, contents) {
|
|
7087
|
-
const descriptor =
|
|
7244
|
+
const descriptor = fs8.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
7088
7245
|
try {
|
|
7089
|
-
if ((
|
|
7090
|
-
|
|
7246
|
+
if ((fs8.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
|
|
7247
|
+
fs8.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
7091
7248
|
}
|
|
7092
7249
|
const bytes = Buffer.from(contents, "utf8");
|
|
7093
7250
|
let written = 0;
|
|
7094
7251
|
while (written < bytes.length) {
|
|
7095
|
-
written +=
|
|
7252
|
+
written += fs8.writeSync(descriptor, bytes, written, bytes.length - written);
|
|
7096
7253
|
}
|
|
7097
7254
|
} finally {
|
|
7098
|
-
|
|
7255
|
+
fs8.closeSync(descriptor);
|
|
7099
7256
|
}
|
|
7100
7257
|
},
|
|
7101
7258
|
writeFileExclusive(filePath, contents) {
|
|
7102
7259
|
try {
|
|
7103
|
-
|
|
7260
|
+
fs8.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
|
|
7104
7261
|
return true;
|
|
7105
7262
|
} catch (error) {
|
|
7106
7263
|
if (isErrorWithCode(error, "EEXIST")) {
|
|
@@ -7111,7 +7268,7 @@ var realFarmFs = {
|
|
|
7111
7268
|
},
|
|
7112
7269
|
hashFile(filePath) {
|
|
7113
7270
|
try {
|
|
7114
|
-
return createHash5("sha256").update(
|
|
7271
|
+
return createHash5("sha256").update(fs8.readFileSync(filePath)).digest("hex");
|
|
7115
7272
|
} catch (error) {
|
|
7116
7273
|
if (isEnoent3(error) || isErrorWithCode(error, "EISDIR")) {
|
|
7117
7274
|
return void 0;
|
|
@@ -7133,7 +7290,7 @@ function realIsProcessAlive(pid) {
|
|
|
7133
7290
|
}
|
|
7134
7291
|
}
|
|
7135
7292
|
function realIsProcessZombie(pid) {
|
|
7136
|
-
const result =
|
|
7293
|
+
const result = spawnSync2("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
|
|
7137
7294
|
return result.status === 0 && result.stdout.trim().startsWith("Z");
|
|
7138
7295
|
}
|
|
7139
7296
|
function realIsProcessRunning(pid) {
|
|
@@ -7141,17 +7298,17 @@ function realIsProcessRunning(pid) {
|
|
|
7141
7298
|
}
|
|
7142
7299
|
var realRunPort = {
|
|
7143
7300
|
run(command, args) {
|
|
7144
|
-
const result =
|
|
7301
|
+
const result = spawnSync2(command, [...args], { encoding: "utf8" });
|
|
7145
7302
|
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
7146
7303
|
}
|
|
7147
7304
|
};
|
|
7148
7305
|
function expandHome2(filePath) {
|
|
7149
|
-
return filePath.startsWith("~/") ?
|
|
7306
|
+
return filePath.startsWith("~/") ? path22.join(os3.homedir(), filePath.slice(1)) : filePath;
|
|
7150
7307
|
}
|
|
7151
7308
|
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
7152
7309
|
function hasDesktopSession() {
|
|
7153
7310
|
if (process.platform === "darwin") {
|
|
7154
|
-
const result =
|
|
7311
|
+
const result = spawnSync2("launchctl", ["managername"], { encoding: "utf8" });
|
|
7155
7312
|
return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
|
|
7156
7313
|
}
|
|
7157
7314
|
if (process.platform === "win32") {
|
|
@@ -7165,8 +7322,8 @@ var realCredentialPort = {
|
|
|
7165
7322
|
let content;
|
|
7166
7323
|
let stat;
|
|
7167
7324
|
try {
|
|
7168
|
-
stat =
|
|
7169
|
-
content =
|
|
7325
|
+
stat = fs8.statSync(resolved);
|
|
7326
|
+
content = fs8.readFileSync(resolved, "utf8");
|
|
7170
7327
|
} catch (error) {
|
|
7171
7328
|
if (isEnoent3(error)) {
|
|
7172
7329
|
return { found: false };
|
|
@@ -7177,7 +7334,7 @@ var realCredentialPort = {
|
|
|
7177
7334
|
},
|
|
7178
7335
|
runCommand(argv, options) {
|
|
7179
7336
|
const [command, ...args] = argv;
|
|
7180
|
-
const result =
|
|
7337
|
+
const result = spawnSync2(command, args, {
|
|
7181
7338
|
encoding: "utf8",
|
|
7182
7339
|
timeout: options.timeoutMs,
|
|
7183
7340
|
stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
|
|
@@ -7219,8 +7376,8 @@ function spawnDaemonThrough(executable) {
|
|
|
7219
7376
|
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
7220
7377
|
}
|
|
7221
7378
|
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
7222
|
-
|
|
7223
|
-
const logFd =
|
|
7379
|
+
fs8.mkdirSync(path22.dirname(logPath), { recursive: true });
|
|
7380
|
+
const logFd = fs8.openSync(logPath, "a");
|
|
7224
7381
|
try {
|
|
7225
7382
|
const child = spawn(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
7226
7383
|
child.unref();
|
|
@@ -7229,7 +7386,7 @@ function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
|
7229
7386
|
}
|
|
7230
7387
|
return child.pid;
|
|
7231
7388
|
} finally {
|
|
7232
|
-
|
|
7389
|
+
fs8.closeSync(logFd);
|
|
7233
7390
|
}
|
|
7234
7391
|
}
|
|
7235
7392
|
function spawnHeadroomSupervisor(paths, spawnDaemon) {
|
|
@@ -7247,7 +7404,7 @@ var realHeadroomSocketTrust = {
|
|
|
7247
7404
|
lstat: (target) => {
|
|
7248
7405
|
let stat;
|
|
7249
7406
|
try {
|
|
7250
|
-
stat =
|
|
7407
|
+
stat = fs8.lstatSync(target);
|
|
7251
7408
|
} catch (error) {
|
|
7252
7409
|
if (isEnoent3(error)) {
|
|
7253
7410
|
return void 0;
|
|
@@ -7293,7 +7450,7 @@ var EXECUTE_BITS_MASK = 73;
|
|
|
7293
7450
|
function listVersionsDir(dir) {
|
|
7294
7451
|
let entries;
|
|
7295
7452
|
try {
|
|
7296
|
-
entries =
|
|
7453
|
+
entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
7297
7454
|
} catch (error) {
|
|
7298
7455
|
if (isEnoent3(error)) {
|
|
7299
7456
|
return [];
|
|
@@ -7301,8 +7458,8 @@ function listVersionsDir(dir) {
|
|
|
7301
7458
|
throw error;
|
|
7302
7459
|
}
|
|
7303
7460
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
7304
|
-
const fullPath =
|
|
7305
|
-
const stat =
|
|
7461
|
+
const fullPath = path22.join(dir, entry.name);
|
|
7462
|
+
const stat = fs8.statSync(fullPath);
|
|
7306
7463
|
return {
|
|
7307
7464
|
name: entry.name,
|
|
7308
7465
|
isFile: true,
|
|
@@ -7316,7 +7473,7 @@ function windowsPathExtensions(pathext) {
|
|
|
7316
7473
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
7317
7474
|
}
|
|
7318
7475
|
function resolveExecutableCandidate(dir, name, env) {
|
|
7319
|
-
const candidate =
|
|
7476
|
+
const candidate = path22.join(dir, name);
|
|
7320
7477
|
if (env.platform === "win32") {
|
|
7321
7478
|
const extensions = windowsPathExtensions(env.pathext);
|
|
7322
7479
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -7336,7 +7493,7 @@ function resolveExecutableCandidate(dir, name, env) {
|
|
|
7336
7493
|
}
|
|
7337
7494
|
function realStatFileMode(candidate) {
|
|
7338
7495
|
try {
|
|
7339
|
-
const stat =
|
|
7496
|
+
const stat = fs8.statSync(candidate);
|
|
7340
7497
|
return stat.isFile() ? stat.mode : void 0;
|
|
7341
7498
|
} catch (error) {
|
|
7342
7499
|
if (isEnoent3(error)) {
|
|
@@ -7365,13 +7522,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
7365
7522
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
7366
7523
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
7367
7524
|
}
|
|
7368
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
7369
|
-
const candidateDir =
|
|
7370
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
7525
|
+
const candidate = env.argv1 === void 0 ? env.execPath : path22.resolve(env.cwd, env.argv1);
|
|
7526
|
+
const candidateDir = path22.dirname(candidate);
|
|
7527
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => path22.resolve(dir) === candidateDir);
|
|
7371
7528
|
if (candidateDirOnPath) {
|
|
7372
7529
|
return candidate;
|
|
7373
7530
|
}
|
|
7374
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
7531
|
+
const viaPath = searchPathForExecutable(env.pathDirs, path22.basename(candidate), env.findExecutableInDir);
|
|
7375
7532
|
return viaPath ?? candidate;
|
|
7376
7533
|
}
|
|
7377
7534
|
function realOwnExecutablePath() {
|
|
@@ -7379,7 +7536,7 @@ function realOwnExecutablePath() {
|
|
|
7379
7536
|
argv1: process.argv[1],
|
|
7380
7537
|
execPath: process.execPath,
|
|
7381
7538
|
cwd: process.cwd(),
|
|
7382
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
7539
|
+
pathDirs: (process.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== ""),
|
|
7383
7540
|
findExecutableInDir
|
|
7384
7541
|
});
|
|
7385
7542
|
}
|
|
@@ -7392,15 +7549,15 @@ function realContentSourcePath() {
|
|
|
7392
7549
|
function realResolveClaudeBinary(isOwnBinary) {
|
|
7393
7550
|
return () => discoverClaudeBinary({
|
|
7394
7551
|
listVersionsDir,
|
|
7395
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
7552
|
+
pathDirs: (process.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== ""),
|
|
7396
7553
|
findExecutableInDir,
|
|
7397
7554
|
isOwnBinary
|
|
7398
7555
|
});
|
|
7399
7556
|
}
|
|
7400
7557
|
|
|
7401
7558
|
// src/checkReport.ts
|
|
7402
|
-
import
|
|
7403
|
-
import { z as
|
|
7559
|
+
import path23 from "node:path";
|
|
7560
|
+
import { z as z12 } from "zod";
|
|
7404
7561
|
|
|
7405
7562
|
// src/cli/bool.ts
|
|
7406
7563
|
function parseBoolWord(input) {
|
|
@@ -7515,9 +7672,9 @@ function lookupKeychainService(run, farmRoot) {
|
|
|
7515
7672
|
note: serviceName === void 0 ? `A Keychain entry was found for account "${farmRoot}" but its service name could not be parsed from security's output.` : `Keychain service name for this identity: "${serviceName}".`
|
|
7516
7673
|
};
|
|
7517
7674
|
}
|
|
7518
|
-
var SettingsSecretsShapeSchema =
|
|
7519
|
-
env:
|
|
7520
|
-
hooks:
|
|
7675
|
+
var SettingsSecretsShapeSchema = z12.looseObject({
|
|
7676
|
+
env: z12.record(z12.string(), z12.unknown()).optional(),
|
|
7677
|
+
hooks: z12.record(z12.string(), z12.array(z12.unknown())).optional()
|
|
7521
7678
|
});
|
|
7522
7679
|
function hookCommandCountOf(group) {
|
|
7523
7680
|
if (typeof group !== "object" || group === null) {
|
|
@@ -7569,14 +7726,23 @@ function inspectSettingsExposure(params) {
|
|
|
7569
7726
|
}
|
|
7570
7727
|
return reports;
|
|
7571
7728
|
}
|
|
7572
|
-
function
|
|
7729
|
+
function cachedState(cacheEnv, owner, block) {
|
|
7730
|
+
if (cacheEnv === void 0 || block.cache === void 0) {
|
|
7731
|
+
return void 0;
|
|
7732
|
+
}
|
|
7733
|
+
return describeCachedCredential({ port: cacheEnv.port, platform: cacheEnv.platform, owner, cache: block.cache, nowMs: cacheEnv.now() });
|
|
7734
|
+
}
|
|
7735
|
+
function buildCredentialReport(provider, identity, identityName, cacheEnv) {
|
|
7573
7736
|
const identityCredential = identity?.credential === void 0 ? void 0 : summariseCredential(identity.credential);
|
|
7737
|
+
const identityCached = identity?.credential === void 0 || identityName === void 0 ? void 0 : cachedState(cacheEnv, `identity ${identityName}`, identity.credential);
|
|
7738
|
+
const providerCached = provider?.definition === void 0 ? void 0 : cachedState(cacheEnv, `provider ${provider.name}`, provider.definition.credential);
|
|
7574
7739
|
return {
|
|
7575
7740
|
applies: provider !== void 0 ? "provider" : identityCredential !== void 0 ? "identity" : "stored-login",
|
|
7576
7741
|
...provider === void 0 ? {} : {
|
|
7577
|
-
provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential) } : { name: provider.name, problem: provider.problem }
|
|
7742
|
+
provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential), ...providerCached === void 0 ? {} : { cached: providerCached } } : { name: provider.name, problem: provider.problem }
|
|
7578
7743
|
},
|
|
7579
|
-
...identityCredential === void 0 ? {} : { identity: identityCredential }
|
|
7744
|
+
...identityCredential === void 0 ? {} : { identity: identityCredential },
|
|
7745
|
+
...identityCached === void 0 ? {} : { identityCached }
|
|
7580
7746
|
};
|
|
7581
7747
|
}
|
|
7582
7748
|
function runCheck(params) {
|
|
@@ -7615,7 +7781,7 @@ function runCheck(params) {
|
|
|
7615
7781
|
ambientCredential,
|
|
7616
7782
|
...keychain === void 0 ? {} : { keychain },
|
|
7617
7783
|
settingsExposure,
|
|
7618
|
-
credential: buildCredentialReport(params.provider, params.identity)
|
|
7784
|
+
credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache)
|
|
7619
7785
|
};
|
|
7620
7786
|
}
|
|
7621
7787
|
function formatCheckReport(report) {
|
|
@@ -7653,12 +7819,12 @@ function formatCheckReport(report) {
|
|
|
7653
7819
|
const { credential } = report;
|
|
7654
7820
|
if (credential.provider !== void 0) {
|
|
7655
7821
|
lines.push(
|
|
7656
|
-
` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}`
|
|
7822
|
+
` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}${credential.provider.cached === void 0 ? "" : `; ${formatCachedCredentialState(credential.provider.cached)}`}`
|
|
7657
7823
|
);
|
|
7658
7824
|
}
|
|
7659
7825
|
if (credential.identity !== void 0) {
|
|
7660
7826
|
lines.push(
|
|
7661
|
-
` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
|
|
7827
|
+
` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.identityCached === void 0 ? "" : `; ${formatCachedCredentialState(credential.identityCached)}`}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
|
|
7662
7828
|
);
|
|
7663
7829
|
}
|
|
7664
7830
|
if (credential.applies === "stored-login") {
|
|
@@ -7767,10 +7933,10 @@ function collectCheckReport(params) {
|
|
|
7767
7933
|
}).input;
|
|
7768
7934
|
const providerName = cascadeProviderName(cascade);
|
|
7769
7935
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
7770
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
7936
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : path23.join(paths.identitiesDir, identityDecision.name);
|
|
7771
7937
|
const settingsFiles = {
|
|
7772
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
7773
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
7938
|
+
"settings.json": realFsPort.readFileUtf8(path23.join(claudeHome, "settings.json")),
|
|
7939
|
+
"settings.local.json": realFsPort.readFileUtf8(path23.join(claudeHome, "settings.local.json"))
|
|
7774
7940
|
};
|
|
7775
7941
|
const report = runCheck({
|
|
7776
7942
|
cwd,
|
|
@@ -7791,6 +7957,7 @@ function collectCheckReport(params) {
|
|
|
7791
7957
|
...loadedIdentity === void 0 ? {} : { identity: loadedIdentity.config },
|
|
7792
7958
|
settingsFiles,
|
|
7793
7959
|
run: realRunPort,
|
|
7960
|
+
credentialCache: realCredentialCacheEnv(paths),
|
|
7794
7961
|
...farmRoot === void 0 ? {} : { farmRoot },
|
|
7795
7962
|
platform: process.platform,
|
|
7796
7963
|
...provider === void 0 ? {} : { provider }
|
|
@@ -7799,23 +7966,23 @@ function collectCheckReport(params) {
|
|
|
7799
7966
|
}
|
|
7800
7967
|
|
|
7801
7968
|
// src/claudeShim.ts
|
|
7802
|
-
import
|
|
7803
|
-
import
|
|
7804
|
-
import { z as
|
|
7805
|
-
var ClaudeShimStateSchema =
|
|
7806
|
-
targetPath:
|
|
7807
|
-
method:
|
|
7808
|
-
installedAtMs:
|
|
7969
|
+
import fs9 from "node:fs";
|
|
7970
|
+
import path24 from "node:path";
|
|
7971
|
+
import { z as z13 } from "zod";
|
|
7972
|
+
var ClaudeShimStateSchema = z13.strictObject({
|
|
7973
|
+
targetPath: z13.string().min(1),
|
|
7974
|
+
method: z13.enum(["hardlink", "copy"]),
|
|
7975
|
+
installedAtMs: z13.number()
|
|
7809
7976
|
});
|
|
7810
7977
|
function commandFilename(ownExecutablePath, commandName) {
|
|
7811
7978
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
7812
7979
|
}
|
|
7813
7980
|
function resolveOwnBinaryCheck(paths, contentSourcePath) {
|
|
7814
7981
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
7815
|
-
const recordedTarget = state === void 0 ? void 0 :
|
|
7982
|
+
const recordedTarget = state === void 0 ? void 0 : path24.resolve(state.targetPath);
|
|
7816
7983
|
let ownStat;
|
|
7817
7984
|
return (candidate) => {
|
|
7818
|
-
if (recordedTarget !== void 0 &&
|
|
7985
|
+
if (recordedTarget !== void 0 && path24.resolve(candidate) === recordedTarget) {
|
|
7819
7986
|
return true;
|
|
7820
7987
|
}
|
|
7821
7988
|
ownStat ??= statOrUndefined(contentSourcePath);
|
|
@@ -7829,7 +7996,7 @@ function isEnoent4(error) {
|
|
|
7829
7996
|
}
|
|
7830
7997
|
function statOrUndefined(target) {
|
|
7831
7998
|
try {
|
|
7832
|
-
return
|
|
7999
|
+
return fs9.statSync(target);
|
|
7833
8000
|
} catch (error) {
|
|
7834
8001
|
if (isEnoent4(error)) {
|
|
7835
8002
|
return void 0;
|
|
@@ -7837,22 +8004,22 @@ function statOrUndefined(target) {
|
|
|
7837
8004
|
throw error;
|
|
7838
8005
|
}
|
|
7839
8006
|
}
|
|
7840
|
-
var nodeLinkFs = { link:
|
|
8007
|
+
var nodeLinkFs = { link: fs9.linkSync, copyFile: fs9.copyFileSync, chmod: fs9.chmodSync };
|
|
7841
8008
|
function findPathShadow(params) {
|
|
7842
|
-
const resolvedTargetDir =
|
|
8009
|
+
const resolvedTargetDir = path24.resolve(params.targetDir);
|
|
7843
8010
|
for (const dir of params.pathDirs) {
|
|
7844
8011
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
7845
8012
|
if (found === void 0) {
|
|
7846
8013
|
continue;
|
|
7847
8014
|
}
|
|
7848
|
-
return
|
|
8015
|
+
return path24.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
7849
8016
|
}
|
|
7850
|
-
return params.pathDirs.some((dir) =>
|
|
8017
|
+
return params.pathDirs.some((dir) => path24.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
7851
8018
|
}
|
|
7852
8019
|
|
|
7853
8020
|
// src/doctorReport.ts
|
|
7854
|
-
import
|
|
7855
|
-
import
|
|
8021
|
+
import fs10 from "node:fs";
|
|
8022
|
+
import path25 from "node:path";
|
|
7856
8023
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
7857
8024
|
if (status.status !== "shadowed") {
|
|
7858
8025
|
return status;
|
|
@@ -7876,7 +8043,7 @@ function validateJson(schema, input) {
|
|
|
7876
8043
|
return { ok: true, data: result.data };
|
|
7877
8044
|
}
|
|
7878
8045
|
function pushPathResolution(push, resolution) {
|
|
7879
|
-
const ownDir =
|
|
8046
|
+
const ownDir = path25.dirname(resolution.ownExecutablePath);
|
|
7880
8047
|
switch (resolution.agentShim.status) {
|
|
7881
8048
|
case "ok":
|
|
7882
8049
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -7918,7 +8085,14 @@ function pushPathResolution(push, resolution) {
|
|
|
7918
8085
|
break;
|
|
7919
8086
|
}
|
|
7920
8087
|
}
|
|
7921
|
-
function
|
|
8088
|
+
function cacheClause(cacheEnv, owner, block) {
|
|
8089
|
+
if (cacheEnv === void 0 || block.cache === void 0) {
|
|
8090
|
+
return { text: "", unreadable: false };
|
|
8091
|
+
}
|
|
8092
|
+
const state = describeCachedCredential({ port: cacheEnv.port, platform: cacheEnv.platform, owner, cache: block.cache, nowMs: cacheEnv.now() });
|
|
8093
|
+
return { text: `; ${formatCachedCredentialState(state)}`, unreadable: state.status === "unreadable" };
|
|
8094
|
+
}
|
|
8095
|
+
function pushProvider(push, entry, cacheEnv) {
|
|
7922
8096
|
if (entry.raw === void 0) {
|
|
7923
8097
|
push("provider", "fail", `${entry.path} is missing.`, entry.name);
|
|
7924
8098
|
return;
|
|
@@ -7940,18 +8114,19 @@ function pushProvider(push, entry) {
|
|
|
7940
8114
|
push("provider", "fail", new ConfigValidationError(entry.path, validated.error.issues).message, entry.name);
|
|
7941
8115
|
return;
|
|
7942
8116
|
}
|
|
7943
|
-
|
|
8117
|
+
const cache = cacheClause(cacheEnv, `provider ${entry.name}`, validated.data.credential);
|
|
8118
|
+
push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(validated.data)}, credential ${describeCredential(validated.data.credential)})${cache.text}.`, entry.name);
|
|
7944
8119
|
}
|
|
7945
8120
|
function pushLegacyName(push, params) {
|
|
7946
8121
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
7947
|
-
if (legacyVariables.length === 0 &&
|
|
8122
|
+
if (legacyVariables.length === 0 && path25.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
7948
8123
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
7949
8124
|
return;
|
|
7950
8125
|
}
|
|
7951
8126
|
for (const name of legacyVariables) {
|
|
7952
8127
|
push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
|
|
7953
8128
|
}
|
|
7954
|
-
if (
|
|
8129
|
+
if (path25.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
7955
8130
|
push(
|
|
7956
8131
|
"legacy-name",
|
|
7957
8132
|
"warn",
|
|
@@ -8033,10 +8208,11 @@ function runDoctor(params) {
|
|
|
8033
8208
|
);
|
|
8034
8209
|
} else {
|
|
8035
8210
|
const credential = validated.data.credential;
|
|
8211
|
+
const cache = credential === void 0 ? { text: "", unreadable: false } : cacheClause(params.credentialCache, `identity ${entry.name}`, credential);
|
|
8036
8212
|
push(
|
|
8037
8213
|
"identity",
|
|
8038
|
-
"pass",
|
|
8039
|
-
`${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}.`,
|
|
8214
|
+
cache.unreadable ? "fail" : "pass",
|
|
8215
|
+
`${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}${cache.text}.`,
|
|
8040
8216
|
entry.name
|
|
8041
8217
|
);
|
|
8042
8218
|
}
|
|
@@ -8056,7 +8232,7 @@ function runDoctor(params) {
|
|
|
8056
8232
|
return poolName === void 0 ? validIdentityNames.has(selector) : poolName in pools;
|
|
8057
8233
|
};
|
|
8058
8234
|
for (const entry of params.providers) {
|
|
8059
|
-
pushProvider(push, entry);
|
|
8235
|
+
pushProvider(push, entry, params.credentialCache);
|
|
8060
8236
|
}
|
|
8061
8237
|
if (params.platform !== "darwin" || params.run === void 0) {
|
|
8062
8238
|
push("keychain", "pass", "Skipped (not macOS).");
|
|
@@ -8250,27 +8426,27 @@ function formatDoctorReport(report) {
|
|
|
8250
8426
|
}
|
|
8251
8427
|
function realpathOrSelf(target) {
|
|
8252
8428
|
try {
|
|
8253
|
-
return
|
|
8429
|
+
return fs10.realpathSync(target);
|
|
8254
8430
|
} catch {
|
|
8255
8431
|
return target;
|
|
8256
8432
|
}
|
|
8257
8433
|
}
|
|
8258
8434
|
function collectDoctorReport(params) {
|
|
8259
8435
|
const { paths } = params;
|
|
8260
|
-
const identityNames =
|
|
8436
|
+
const identityNames = fs10.existsSync(paths.identitiesDir) ? fs10.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
8261
8437
|
const identities = identityNames.map((name) => {
|
|
8262
|
-
const farmRoot =
|
|
8263
|
-
const identityPath =
|
|
8438
|
+
const farmRoot = path25.join(paths.identitiesDir, name);
|
|
8439
|
+
const identityPath = path25.join(farmRoot, "identity.json");
|
|
8264
8440
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
8265
8441
|
});
|
|
8266
|
-
const profileNames =
|
|
8442
|
+
const profileNames = fs10.existsSync(paths.configProfilesDir) ? fs10.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
8267
8443
|
const configProfiles = profileNames.map((name) => {
|
|
8268
|
-
const profilePath =
|
|
8444
|
+
const profilePath = path25.join(paths.configProfilesDir, `${name}.json`);
|
|
8269
8445
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
8270
8446
|
});
|
|
8271
|
-
const providerNames =
|
|
8447
|
+
const providerNames = fs10.existsSync(paths.providersDir) ? fs10.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
8272
8448
|
const providers = providerNames.map((name) => {
|
|
8273
|
-
const providerPath =
|
|
8449
|
+
const providerPath = path25.join(paths.providersDir, `${name}.json`);
|
|
8274
8450
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
8275
8451
|
});
|
|
8276
8452
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -8282,12 +8458,12 @@ function collectDoctorReport(params) {
|
|
|
8282
8458
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
8283
8459
|
}
|
|
8284
8460
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
8285
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
8461
|
+
const pathDirs = (params.env.PATH ?? "").split(path25.delimiter).filter((dir) => dir !== "");
|
|
8286
8462
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
8287
8463
|
findPathShadow({
|
|
8288
8464
|
pathDirs,
|
|
8289
|
-
targetDir:
|
|
8290
|
-
targetFilename:
|
|
8465
|
+
targetDir: path25.dirname(shimState.targetPath),
|
|
8466
|
+
targetFilename: path25.basename(shimState.targetPath),
|
|
8291
8467
|
findExecutableInDir
|
|
8292
8468
|
}),
|
|
8293
8469
|
shimState.targetPath,
|
|
@@ -8295,6 +8471,7 @@ function collectDoctorReport(params) {
|
|
|
8295
8471
|
);
|
|
8296
8472
|
const report = runDoctor({
|
|
8297
8473
|
env: params.env,
|
|
8474
|
+
credentialCache: realCredentialCacheEnv(paths),
|
|
8298
8475
|
identities,
|
|
8299
8476
|
configProfiles,
|
|
8300
8477
|
providers,
|
|
@@ -8303,14 +8480,14 @@ function collectDoctorReport(params) {
|
|
|
8303
8480
|
categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
|
|
8304
8481
|
activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
|
|
8305
8482
|
binaryDiscovery,
|
|
8306
|
-
claudeShim: { state: shimState, targetExists: shimState !== void 0 &&
|
|
8483
|
+
claudeShim: { state: shimState, targetExists: shimState !== void 0 && fs10.existsSync(shimState.targetPath) },
|
|
8307
8484
|
rootPath: paths.root,
|
|
8308
8485
|
pathResolution: {
|
|
8309
8486
|
ownExecutablePath,
|
|
8310
8487
|
agentShim: refinePathShadow(
|
|
8311
8488
|
findPathShadow({
|
|
8312
8489
|
pathDirs,
|
|
8313
|
-
targetDir:
|
|
8490
|
+
targetDir: path25.dirname(ownExecutablePath),
|
|
8314
8491
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
8315
8492
|
findExecutableInDir
|
|
8316
8493
|
}),
|
|
@@ -8331,7 +8508,7 @@ function collectDoctorReport(params) {
|
|
|
8331
8508
|
|
|
8332
8509
|
// src/launcher.ts
|
|
8333
8510
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8334
|
-
import
|
|
8511
|
+
import path27 from "node:path";
|
|
8335
8512
|
|
|
8336
8513
|
// src/launcher/argv.ts
|
|
8337
8514
|
var ConflictingIdentityError = class extends UsageError {
|
|
@@ -8583,7 +8760,7 @@ function buildCliOverride(params) {
|
|
|
8583
8760
|
}
|
|
8584
8761
|
|
|
8585
8762
|
// src/launcher/flags.ts
|
|
8586
|
-
import
|
|
8763
|
+
import path26 from "node:path";
|
|
8587
8764
|
|
|
8588
8765
|
// src/headroom/headers.ts
|
|
8589
8766
|
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
@@ -8653,7 +8830,7 @@ function buildEnv(params) {
|
|
|
8653
8830
|
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
8654
8831
|
let providerCustomHeaders;
|
|
8655
8832
|
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
8656
|
-
env.CLAUDE_CONFIG_DIR =
|
|
8833
|
+
env.CLAUDE_CONFIG_DIR = path26.join(params.identitiesDir, params.resolvedIdentityName);
|
|
8657
8834
|
}
|
|
8658
8835
|
if (params.provider !== void 0) {
|
|
8659
8836
|
const providerEnv = params.provider.definition.env ?? {};
|
|
@@ -8814,7 +8991,7 @@ function prepareLaunch(params) {
|
|
|
8814
8991
|
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
8815
8992
|
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
8816
8993
|
});
|
|
8817
|
-
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(
|
|
8994
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path27.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
8818
8995
|
log.error(
|
|
8819
8996
|
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
8820
8997
|
);
|
|
@@ -8987,84 +9164,6 @@ function prepareLaunch(params) {
|
|
|
8987
9164
|
import os5 from "node:os";
|
|
8988
9165
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
8989
9166
|
|
|
8990
|
-
// src/realCredentialCache.ts
|
|
8991
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8992
|
-
import fs10 from "node:fs";
|
|
8993
|
-
import path27 from "node:path";
|
|
8994
|
-
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
8995
|
-
var CACHE_DIR_MODE = 448;
|
|
8996
|
-
var CACHE_FILE_MODE = 384;
|
|
8997
|
-
function cacheFileNameFor(owner) {
|
|
8998
|
-
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
8999
|
-
}
|
|
9000
|
-
function parseEntry(raw) {
|
|
9001
|
-
let json;
|
|
9002
|
-
try {
|
|
9003
|
-
json = JSON.parse(raw);
|
|
9004
|
-
} catch {
|
|
9005
|
-
return void 0;
|
|
9006
|
-
}
|
|
9007
|
-
const parsed = CachedCredentialSchema.safeParse(json);
|
|
9008
|
-
return parsed.success ? parsed.data : void 0;
|
|
9009
|
-
}
|
|
9010
|
-
function requireKeychain() {
|
|
9011
|
-
if (process.platform !== "darwin") {
|
|
9012
|
-
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
9013
|
-
}
|
|
9014
|
-
}
|
|
9015
|
-
function security(args, stdin) {
|
|
9016
|
-
const result = spawnSync2("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
9017
|
-
return { status: result.status, stdout: result.stdout };
|
|
9018
|
-
}
|
|
9019
|
-
function createRealCredentialCache(root) {
|
|
9020
|
-
const dir = path27.join(root, "credential-cache");
|
|
9021
|
-
const filePath = (owner) => path27.join(dir, cacheFileNameFor(owner));
|
|
9022
|
-
return {
|
|
9023
|
-
read(store, owner) {
|
|
9024
|
-
if (store === "file") {
|
|
9025
|
-
try {
|
|
9026
|
-
return parseEntry(fs10.readFileSync(filePath(owner), "utf8"));
|
|
9027
|
-
} catch {
|
|
9028
|
-
return void 0;
|
|
9029
|
-
}
|
|
9030
|
-
}
|
|
9031
|
-
requireKeychain();
|
|
9032
|
-
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
9033
|
-
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
9034
|
-
},
|
|
9035
|
-
write(store, owner, entry) {
|
|
9036
|
-
const serialised = JSON.stringify(entry);
|
|
9037
|
-
if (store === "file") {
|
|
9038
|
-
fs10.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
9039
|
-
fs10.chmodSync(dir, CACHE_DIR_MODE);
|
|
9040
|
-
const target = filePath(owner);
|
|
9041
|
-
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
9042
|
-
fs10.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
9043
|
-
fs10.renameSync(temp, target);
|
|
9044
|
-
return;
|
|
9045
|
-
}
|
|
9046
|
-
requireKeychain();
|
|
9047
|
-
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
9048
|
-
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
9049
|
-
`);
|
|
9050
|
-
if (result.status !== 0) {
|
|
9051
|
-
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
9052
|
-
}
|
|
9053
|
-
},
|
|
9054
|
-
remove(store, owner) {
|
|
9055
|
-
if (store === "file") {
|
|
9056
|
-
fs10.rmSync(filePath(owner), { force: true });
|
|
9057
|
-
return;
|
|
9058
|
-
}
|
|
9059
|
-
requireKeychain();
|
|
9060
|
-
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
9061
|
-
}
|
|
9062
|
-
};
|
|
9063
|
-
}
|
|
9064
|
-
function realCredentialCacheEnv(paths) {
|
|
9065
|
-
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
9066
|
-
}
|
|
9067
|
-
|
|
9068
9167
|
// src/frontdoor/realFrontDoorPort.ts
|
|
9069
9168
|
import fs11 from "node:fs";
|
|
9070
9169
|
|
|
@@ -9328,8 +9427,11 @@ export {
|
|
|
9328
9427
|
InvalidProviderNameError,
|
|
9329
9428
|
LaunchRefusedError,
|
|
9330
9429
|
LegacyProviderFileError,
|
|
9430
|
+
PROMPT_CACHE_TTL_MS,
|
|
9431
|
+
PlanClassSchema,
|
|
9331
9432
|
PoolNameSchema,
|
|
9332
9433
|
PoolNotFoundError,
|
|
9434
|
+
PoolPickReportSchema,
|
|
9333
9435
|
PoolSchema,
|
|
9334
9436
|
PortableConfigSchema,
|
|
9335
9437
|
ProfileAlreadyExistsError,
|
|
@@ -9366,16 +9468,19 @@ export {
|
|
|
9366
9468
|
checkReportToJson,
|
|
9367
9469
|
collectCheckReport,
|
|
9368
9470
|
collectDoctorReport,
|
|
9471
|
+
collectPoolPick,
|
|
9369
9472
|
createLeafCache,
|
|
9370
9473
|
createProfile,
|
|
9371
9474
|
createRcControlHandler,
|
|
9372
9475
|
createRcSessionTracker,
|
|
9373
9476
|
describeProviderEndpoint,
|
|
9374
9477
|
detectAmbientCredential,
|
|
9478
|
+
effectiveWindow,
|
|
9375
9479
|
ensureCa,
|
|
9376
9480
|
ensureFrontDoor,
|
|
9377
9481
|
ensureHeadroom,
|
|
9378
9482
|
evaluateAmbientCredentialGuard,
|
|
9483
|
+
evaluateWhen,
|
|
9379
9484
|
formatAmbientCredentialGuardMessage,
|
|
9380
9485
|
formatCheckReport,
|
|
9381
9486
|
formatDoctorReport,
|
|
@@ -9393,13 +9498,16 @@ export {
|
|
|
9393
9498
|
listProfiles,
|
|
9394
9499
|
listProviders,
|
|
9395
9500
|
listUsageSnapshots,
|
|
9501
|
+
matchBranch,
|
|
9396
9502
|
mintLeaf,
|
|
9397
9503
|
observingRoutedRoute,
|
|
9398
9504
|
parseConnectTarget,
|
|
9505
|
+
planOf,
|
|
9399
9506
|
prepareClaudeLaunch,
|
|
9400
9507
|
prepareLaunch,
|
|
9401
9508
|
profileExists,
|
|
9402
9509
|
providerExists,
|
|
9510
|
+
rankPool,
|
|
9403
9511
|
rcEventWriteResultFromAnswer,
|
|
9404
9512
|
rcObserverAsPassthrough,
|
|
9405
9513
|
readActiveIdentity,
|