agent-shim 8.7.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 +30 -21
- package/dist/index.cjs +1814 -1758
- package/dist/index.mjs +1805 -1758
- package/dist/types/index.d.ts +238 -60
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -63,8 +63,11 @@ __export(index_exports, {
|
|
|
63
63
|
InvalidProviderNameError: () => InvalidProviderNameError,
|
|
64
64
|
LaunchRefusedError: () => LaunchRefusedError,
|
|
65
65
|
LegacyProviderFileError: () => LegacyProviderFileError,
|
|
66
|
+
PROMPT_CACHE_TTL_MS: () => PROMPT_CACHE_TTL_MS,
|
|
67
|
+
PlanClassSchema: () => PlanClassSchema,
|
|
66
68
|
PoolNameSchema: () => PoolNameSchema,
|
|
67
69
|
PoolNotFoundError: () => PoolNotFoundError,
|
|
70
|
+
PoolPickReportSchema: () => PoolPickReportSchema,
|
|
68
71
|
PoolSchema: () => PoolSchema,
|
|
69
72
|
PortableConfigSchema: () => PortableConfigSchema,
|
|
70
73
|
ProfileAlreadyExistsError: () => ProfileAlreadyExistsError,
|
|
@@ -101,16 +104,19 @@ __export(index_exports, {
|
|
|
101
104
|
checkReportToJson: () => checkReportToJson,
|
|
102
105
|
collectCheckReport: () => collectCheckReport,
|
|
103
106
|
collectDoctorReport: () => collectDoctorReport,
|
|
107
|
+
collectPoolPick: () => collectPoolPick,
|
|
104
108
|
createLeafCache: () => createLeafCache,
|
|
105
109
|
createProfile: () => createProfile,
|
|
106
110
|
createRcControlHandler: () => createRcControlHandler,
|
|
107
111
|
createRcSessionTracker: () => createRcSessionTracker,
|
|
108
112
|
describeProviderEndpoint: () => describeProviderEndpoint,
|
|
109
113
|
detectAmbientCredential: () => detectAmbientCredential,
|
|
114
|
+
effectiveWindow: () => effectiveWindow,
|
|
110
115
|
ensureCa: () => ensureCa,
|
|
111
116
|
ensureFrontDoor: () => ensureFrontDoor,
|
|
112
117
|
ensureHeadroom: () => ensureHeadroom,
|
|
113
118
|
evaluateAmbientCredentialGuard: () => evaluateAmbientCredentialGuard,
|
|
119
|
+
evaluateWhen: () => evaluateWhen,
|
|
114
120
|
formatAmbientCredentialGuardMessage: () => formatAmbientCredentialGuardMessage,
|
|
115
121
|
formatCheckReport: () => formatCheckReport,
|
|
116
122
|
formatDoctorReport: () => formatDoctorReport,
|
|
@@ -128,13 +134,16 @@ __export(index_exports, {
|
|
|
128
134
|
listProfiles: () => listProfiles,
|
|
129
135
|
listProviders: () => listProviders,
|
|
130
136
|
listUsageSnapshots: () => listUsageSnapshots,
|
|
137
|
+
matchBranch: () => matchBranch,
|
|
131
138
|
mintLeaf: () => mintLeaf,
|
|
132
139
|
observingRoutedRoute: () => observingRoutedRoute,
|
|
133
140
|
parseConnectTarget: () => parseConnectTarget,
|
|
141
|
+
planOf: () => planOf,
|
|
134
142
|
prepareClaudeLaunch: () => prepareClaudeLaunch,
|
|
135
143
|
prepareLaunch: () => prepareLaunch,
|
|
136
144
|
profileExists: () => profileExists,
|
|
137
145
|
providerExists: () => providerExists,
|
|
146
|
+
rankPool: () => rankPool,
|
|
138
147
|
rcEventWriteResultFromAnswer: () => rcEventWriteResultFromAnswer,
|
|
139
148
|
rcObserverAsPassthrough: () => rcObserverAsPassthrough,
|
|
140
149
|
readActiveIdentity: () => readActiveIdentity,
|
|
@@ -3515,25 +3524,19 @@ function realConnectEffects() {
|
|
|
3515
3524
|
}
|
|
3516
3525
|
};
|
|
3517
3526
|
},
|
|
3518
|
-
connectTlsUpstream: async (host, port, clientAlpn) =>
|
|
3519
|
-
const
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
candidate.once("error", (error) => {
|
|
3528
|
-
if ("code" in error && error.code === "EADDRINUSE") {
|
|
3529
|
-
settle(dial(upstreamLocalPort()));
|
|
3530
|
-
return;
|
|
3531
|
-
}
|
|
3532
|
-
reject(error);
|
|
3527
|
+
connectTlsUpstream: async (host, port, clientAlpn) => {
|
|
3528
|
+
const dialled = await reservedDial({ host, port });
|
|
3529
|
+
if (dialled.error !== null) {
|
|
3530
|
+
throw dialled.error;
|
|
3531
|
+
}
|
|
3532
|
+
return await new Promise((resolve, reject) => {
|
|
3533
|
+
const secure = tls.connect({ socket: dialled.socket, servername: host, ...clientAlpn === void 0 ? {} : { ALPNProtocols: [clientAlpn] } });
|
|
3534
|
+
secure.once("secureConnect", () => {
|
|
3535
|
+
resolve(secure);
|
|
3533
3536
|
});
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
}
|
|
3537
|
+
secure.once("error", reject);
|
|
3538
|
+
});
|
|
3539
|
+
},
|
|
3537
3540
|
connectTcp: async (host, port) => await new Promise((resolve, reject) => {
|
|
3538
3541
|
const socket = net2.connect({ host, port });
|
|
3539
3542
|
socket.once("connect", () => {
|
|
@@ -5095,547 +5098,380 @@ function listUsageSnapshots(fs12, snapshotsDir) {
|
|
|
5095
5098
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
5096
5099
|
}
|
|
5097
5100
|
|
|
5098
|
-
// src/
|
|
5099
|
-
var
|
|
5100
|
-
var
|
|
5101
|
-
var
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
const { paths, ports } = params;
|
|
5109
|
-
const deadline = ports.now() + HEADROOM_START_TIMEOUT_MS;
|
|
5110
|
-
let spawnedSupervisor = false;
|
|
5111
|
-
let awaitingAllowlist = false;
|
|
5112
|
-
for (; ; ) {
|
|
5113
|
-
const state = readHeadroomState(ports.fs, paths.headroomStateFile);
|
|
5114
|
-
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
5115
|
-
const daemonUp = state.socketPath !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
5116
|
-
if (daemonUp && state.socketPath !== void 0) {
|
|
5117
|
-
const required = params.requiredAllowlistHash?.();
|
|
5118
|
-
if (required === void 0 || state.allowlistHash === required) {
|
|
5119
|
-
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
5120
|
-
return { socketPath: state.socketPath };
|
|
5121
|
-
}
|
|
5122
|
-
const holders = sessionsForSupervisor(ports.fs, paths.headroomSessionsDir, state.supervisorPid).filter((session) => ports.isRunning(session.pid));
|
|
5123
|
-
if (holders.length > 0) {
|
|
5124
|
-
throw new HeadroomStartError(
|
|
5125
|
-
`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.`
|
|
5126
|
-
);
|
|
5127
|
-
}
|
|
5128
|
-
awaitingAllowlist = true;
|
|
5129
|
-
}
|
|
5130
|
-
if (state.lastError !== void 0) {
|
|
5131
|
-
throw new HeadroomStartError(
|
|
5132
|
-
`the headroom daemon reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.headroomLogPath})`
|
|
5133
|
-
);
|
|
5134
|
-
}
|
|
5135
|
-
} else if (!spawnedSupervisor) {
|
|
5136
|
-
ports.fs.mkdirp(paths.headroomDir);
|
|
5137
|
-
const created = ports.fs.writeFileExclusive(
|
|
5138
|
-
paths.headroomLockFile,
|
|
5139
|
-
`${JSON.stringify({ pid: params.launcherPid, at: ports.now() })}
|
|
5140
|
-
`
|
|
5141
|
-
);
|
|
5142
|
-
if (created) {
|
|
5143
|
-
ports.spawnSupervisor(paths);
|
|
5144
|
-
spawnedSupervisor = true;
|
|
5145
|
-
} else {
|
|
5146
|
-
const lock = readStartLock(ports.fs, paths.headroomLockFile);
|
|
5147
|
-
if (lock === void 0 || !ports.isRunning(lock.pid)) {
|
|
5148
|
-
ports.fs.removeRecursive(paths.headroomLockFile);
|
|
5149
|
-
continue;
|
|
5150
|
-
}
|
|
5151
|
-
}
|
|
5152
|
-
}
|
|
5153
|
-
if (ports.now() >= deadline) {
|
|
5154
|
-
const lastError = state?.lastError;
|
|
5155
|
-
throw new HeadroomStartError(
|
|
5156
|
-
`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}`
|
|
5157
|
-
);
|
|
5158
|
-
}
|
|
5159
|
-
ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
|
|
5101
|
+
// src/usage/preflight.ts
|
|
5102
|
+
var MS_PER_SECOND2 = 1e3;
|
|
5103
|
+
var SECONDS_PER_MINUTE = 60;
|
|
5104
|
+
var MINUTES_PER_HOUR = 60;
|
|
5105
|
+
var HOURS_PER_DAY = 24;
|
|
5106
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5107
|
+
function formatAge(ms) {
|
|
5108
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5109
|
+
if (minutes < MINUTES_PER_HOUR) {
|
|
5110
|
+
return `${String(Math.max(minutes, 0))}m`;
|
|
5160
5111
|
}
|
|
5112
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
5113
|
+
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
5161
5114
|
}
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5115
|
+
function effectiveWindow(window, nowMs) {
|
|
5116
|
+
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
5117
|
+
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
5118
|
+
return { reset: true, utilization: 0 };
|
|
5119
|
+
}
|
|
5166
5120
|
return {
|
|
5167
|
-
|
|
5168
|
-
...
|
|
5169
|
-
...
|
|
5170
|
-
...
|
|
5171
|
-
...config.interceptToolResults === void 0 ? {} : { interceptToolResults: config.interceptToolResults },
|
|
5172
|
-
...config.readMaturation === void 0 ? {} : { readMaturation: config.readMaturation }
|
|
5121
|
+
reset: false,
|
|
5122
|
+
...window.utilization === void 0 ? {} : { utilization: window.utilization },
|
|
5123
|
+
...resetsAtMs === void 0 ? {} : { resetsAtMs },
|
|
5124
|
+
...window.status === void 0 ? {} : { status: window.status }
|
|
5173
5125
|
};
|
|
5174
5126
|
}
|
|
5175
|
-
function
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5127
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
5128
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
5129
|
+
const unified = rateLimit?.unified;
|
|
5130
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
5131
|
+
return [];
|
|
5132
|
+
}
|
|
5133
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
5134
|
+
const windows = [
|
|
5135
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
5136
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
5183
5137
|
];
|
|
5184
|
-
}
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5138
|
+
return windows.flatMap(({ name, window }) => {
|
|
5139
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
5140
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
5141
|
+
return [];
|
|
5142
|
+
}
|
|
5143
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
5144
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
5145
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
5146
|
+
});
|
|
5190
5147
|
}
|
|
5191
5148
|
|
|
5192
|
-
// src/
|
|
5193
|
-
var
|
|
5194
|
-
var
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
}
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5149
|
+
// src/usage/plan.ts
|
|
5150
|
+
var import_zod6 = require("zod");
|
|
5151
|
+
var PlanClassSchema = import_zod6.z.union([
|
|
5152
|
+
import_zod6.z.strictObject({
|
|
5153
|
+
kind: import_zod6.z.literal("subscription"),
|
|
5154
|
+
/** The plan's size as a multiple of the smallest subscription tier (Max 5x is 5, Max 20x is 20). */
|
|
5155
|
+
capacity: import_zod6.z.number().positive(),
|
|
5156
|
+
/** False when the tier string named no multiplier and no tier this project knows, so `capacity` is the neutral 1 rather than a reading. */
|
|
5157
|
+
recognised: import_zod6.z.boolean(),
|
|
5158
|
+
/** The tier string the classification rests on, when the account had one. */
|
|
5159
|
+
tier: import_zod6.z.string().optional()
|
|
5160
|
+
}),
|
|
5161
|
+
import_zod6.z.strictObject({
|
|
5162
|
+
kind: import_zod6.z.literal("pay-per-use"),
|
|
5163
|
+
tier: import_zod6.z.string().optional()
|
|
5164
|
+
})
|
|
5165
|
+
]);
|
|
5166
|
+
var TIER_MULTIPLIER = /(?:^|_)(\d+)x$/;
|
|
5167
|
+
var ZERO_TIER = /(?:^|_)zero$/;
|
|
5168
|
+
var BASE_TIER = /(?:^|_)(?:pro|free)$/;
|
|
5169
|
+
function planOf(account) {
|
|
5170
|
+
const tier = account?.organizationRateLimitTier ?? account?.userRateLimitTier;
|
|
5171
|
+
const tierField = tier === void 0 ? {} : { tier };
|
|
5172
|
+
if (tier === void 0) {
|
|
5173
|
+
return { kind: "subscription", capacity: 1, recognised: false };
|
|
5216
5174
|
}
|
|
5217
|
-
if (
|
|
5218
|
-
return
|
|
5175
|
+
if (ZERO_TIER.test(tier)) {
|
|
5176
|
+
return { kind: "pay-per-use", ...tierField };
|
|
5219
5177
|
}
|
|
5220
|
-
|
|
5221
|
-
|
|
5178
|
+
const multiplier = TIER_MULTIPLIER.exec(tier)?.[1];
|
|
5179
|
+
if (multiplier !== void 0) {
|
|
5180
|
+
return { kind: "subscription", capacity: Number(multiplier), recognised: true, ...tierField };
|
|
5222
5181
|
}
|
|
5223
|
-
return
|
|
5182
|
+
return { kind: "subscription", capacity: 1, recognised: BASE_TIER.test(tier), ...tierField };
|
|
5224
5183
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
const length = Buffer.byteLength(socketPath, "utf8");
|
|
5232
|
-
if (length > UNIX_SOCKET_PATH_MAX_BYTES) {
|
|
5233
|
-
return {
|
|
5234
|
-
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`
|
|
5235
|
-
};
|
|
5236
|
-
}
|
|
5237
|
-
if (ports.lstat(paths.headroomSocketDir)?.kind === "symlink") {
|
|
5238
|
-
return { refused: `the headroom socket directory ${paths.headroomSocketDir} is a symlink; it must be the real directory` };
|
|
5184
|
+
|
|
5185
|
+
// src/usage/rateLimit.ts
|
|
5186
|
+
var MS_PER_SECOND3 = 1e3;
|
|
5187
|
+
function epochSecondsToIso(value) {
|
|
5188
|
+
if (value === void 0) {
|
|
5189
|
+
return void 0;
|
|
5239
5190
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
return refusal === void 0 ? { socketPath } : { refused: refusal };
|
|
5191
|
+
const seconds = Number(value);
|
|
5192
|
+
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
5243
5193
|
}
|
|
5244
|
-
function
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
const pid = HEADROOM_SOCKET_NAME.exec(name)?.[1];
|
|
5248
|
-
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
5249
|
-
continue;
|
|
5250
|
-
}
|
|
5251
|
-
fs12.removeRecursive(import_node_path11.default.join(paths.headroomSocketDir, name));
|
|
5252
|
-
removed.push(name);
|
|
5194
|
+
function nonNegativeNumber(value) {
|
|
5195
|
+
if (value === void 0 || value.trim() === "") {
|
|
5196
|
+
return void 0;
|
|
5253
5197
|
}
|
|
5254
|
-
|
|
5198
|
+
const parsed = Number(value);
|
|
5199
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
5255
5200
|
}
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
const match = GIT_SPEC_RE.exec(spec);
|
|
5264
|
-
if (match === null) {
|
|
5265
|
-
return false;
|
|
5201
|
+
var UNIFIED_PREFIX = "anthropic-ratelimit-unified-";
|
|
5202
|
+
function unifiedWindow(headers, window) {
|
|
5203
|
+
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
5204
|
+
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
5205
|
+
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
5206
|
+
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
5207
|
+
return void 0;
|
|
5266
5208
|
}
|
|
5267
|
-
|
|
5268
|
-
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
5269
|
-
}
|
|
5270
|
-
function pinnedGitCommit(spec) {
|
|
5271
|
-
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
5272
|
-
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
5209
|
+
return { ...utilization === void 0 ? {} : { utilization }, ...resetsAt === void 0 ? {} : { resetsAt }, ...status === void 0 ? {} : { status } };
|
|
5273
5210
|
}
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5211
|
+
function parseUnifiedRateLimit(headers) {
|
|
5212
|
+
if (!Object.keys(headers).some((name) => name.startsWith(UNIFIED_PREFIX))) {
|
|
5213
|
+
return void 0;
|
|
5214
|
+
}
|
|
5215
|
+
const status = headers[`${UNIFIED_PREFIX}status`];
|
|
5216
|
+
const fiveHour = unifiedWindow(headers, "5h");
|
|
5217
|
+
const sevenDay = unifiedWindow(headers, "7d");
|
|
5218
|
+
const representativeClaim = headers[`${UNIFIED_PREFIX}representative-claim`];
|
|
5219
|
+
const resetAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}reset`]);
|
|
5220
|
+
const overageStatus = headers[`${UNIFIED_PREFIX}overage-status`];
|
|
5277
5221
|
return {
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5222
|
+
...status === void 0 ? {} : { status },
|
|
5223
|
+
...fiveHour === void 0 ? {} : { fiveHour },
|
|
5224
|
+
...sevenDay === void 0 ? {} : { sevenDay },
|
|
5225
|
+
...representativeClaim === void 0 ? {} : { representativeClaim },
|
|
5226
|
+
...resetAt === void 0 ? {} : { resetAt },
|
|
5227
|
+
...overageStatus === void 0 ? {} : { overageStatus }
|
|
5281
5228
|
};
|
|
5282
5229
|
}
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
var
|
|
5286
|
-
var
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5230
|
+
|
|
5231
|
+
// src/usage/scan.ts
|
|
5232
|
+
var import_zod7 = require("zod");
|
|
5233
|
+
var AnthropicUsageSchema = import_zod7.z.looseObject({
|
|
5234
|
+
input_tokens: import_zod7.z.number().int().nonnegative().nullish(),
|
|
5235
|
+
output_tokens: import_zod7.z.number().int().nonnegative().nullish(),
|
|
5236
|
+
cache_creation_input_tokens: import_zod7.z.number().int().nonnegative().nullish(),
|
|
5237
|
+
cache_read_input_tokens: import_zod7.z.number().int().nonnegative().nullish()
|
|
5238
|
+
});
|
|
5239
|
+
var ErrorObjectSchema = import_zod7.z.looseObject({
|
|
5240
|
+
type: import_zod7.z.string().optional(),
|
|
5241
|
+
code: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.number()]).optional(),
|
|
5242
|
+
message: import_zod7.z.string().optional()
|
|
5243
|
+
});
|
|
5244
|
+
|
|
5245
|
+
// src/usage/middleware.ts
|
|
5246
|
+
var ANTHROPIC_PROVIDER = "anthropic";
|
|
5247
|
+
|
|
5248
|
+
// src/usage/pick.ts
|
|
5249
|
+
var MS_PER_SECOND4 = 1e3;
|
|
5250
|
+
var MS_PER_HOUR = 36e5;
|
|
5251
|
+
var HOURS_PER_DAY2 = 24;
|
|
5252
|
+
var FIVE_HOURS = 5;
|
|
5253
|
+
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
5254
|
+
var PERCENT = 100;
|
|
5255
|
+
var SEVEN_DAYS = 7;
|
|
5256
|
+
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
5257
|
+
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
5258
|
+
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
5259
|
+
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
5260
|
+
function unifiedOf(state) {
|
|
5261
|
+
return state?.rateLimit?.unified;
|
|
5291
5262
|
}
|
|
5292
|
-
function
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
const specifier = /==|>=|<=|!=|>|<|~=/.exec(source);
|
|
5297
|
-
if (specifier === null) {
|
|
5298
|
-
return true;
|
|
5299
|
-
}
|
|
5300
|
-
const installed = parseVersion(version);
|
|
5301
|
-
const wanted = parseVersion(source.slice(specifier.index + specifier[0].length));
|
|
5302
|
-
if (installed === void 0 || wanted === void 0) {
|
|
5303
|
-
return true;
|
|
5263
|
+
function limitBlockedUntil(state, nowMs) {
|
|
5264
|
+
const limit = state?.lastLimit;
|
|
5265
|
+
if (state === void 0 || limit === void 0) {
|
|
5266
|
+
return void 0;
|
|
5304
5267
|
}
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
case "==":
|
|
5308
|
-
return cmp === 0;
|
|
5309
|
-
case "!=":
|
|
5310
|
-
return cmp !== 0;
|
|
5311
|
-
case ">=":
|
|
5312
|
-
return cmp >= 0;
|
|
5313
|
-
case "<=":
|
|
5314
|
-
return cmp <= 0;
|
|
5315
|
-
case ">":
|
|
5316
|
-
return cmp > 0;
|
|
5317
|
-
case "<":
|
|
5318
|
-
return cmp < 0;
|
|
5319
|
-
case "~=":
|
|
5320
|
-
return compareVersions(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
5321
|
-
default:
|
|
5322
|
-
return true;
|
|
5268
|
+
if (Date.parse(state.lastRequestAt) > Date.parse(limit.observedAt)) {
|
|
5269
|
+
return void 0;
|
|
5323
5270
|
}
|
|
5271
|
+
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);
|
|
5272
|
+
return until !== void 0 && until > nowMs ? until : void 0;
|
|
5324
5273
|
}
|
|
5325
|
-
function
|
|
5326
|
-
const
|
|
5327
|
-
|
|
5274
|
+
function windowBlockedUntil(windows) {
|
|
5275
|
+
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
5276
|
+
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
5277
|
+
}
|
|
5278
|
+
function fiveHourBurn(records) {
|
|
5279
|
+
const points = [];
|
|
5280
|
+
for (const record of records) {
|
|
5281
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.fiveHour;
|
|
5282
|
+
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
5283
|
+
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
const latest = points.at(-1)?.resetsAt;
|
|
5287
|
+
const current = points.filter((entry) => entry.resetsAt === latest).map((entry) => entry.point);
|
|
5288
|
+
const first = current.at(0);
|
|
5289
|
+
const last = current.at(-1);
|
|
5290
|
+
if (first === void 0 || last === void 0 || last.atMs <= first.atMs || last.utilization <= first.utilization) {
|
|
5328
5291
|
return void 0;
|
|
5329
5292
|
}
|
|
5330
|
-
return
|
|
5293
|
+
return (last.utilization - first.utilization) / (last.atMs - first.atMs);
|
|
5331
5294
|
}
|
|
5332
|
-
function
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
}
|
|
5295
|
+
function percent(fraction) {
|
|
5296
|
+
return `${String(Math.round(fraction * PERCENT))}%`;
|
|
5297
|
+
}
|
|
5298
|
+
function describeWindow(label, window, effective, nowMs) {
|
|
5299
|
+
if (effective.reset) {
|
|
5300
|
+
return `${label} window has reset`;
|
|
5339
5301
|
}
|
|
5340
|
-
|
|
5302
|
+
const used = effective.utilization === void 0 ? "usage unknown" : `${percent(effective.utilization)} used`;
|
|
5303
|
+
const resets = effective.resetsAtMs === void 0 ? "" : `, resets in ${formatAge(effective.resetsAtMs - nowMs)}`;
|
|
5304
|
+
return `${label} ${used}${resets}${window.status === "allowed_warning" ? " (nearly used)" : ""}`;
|
|
5341
5305
|
}
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
const
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5306
|
+
function capacityOf(plan) {
|
|
5307
|
+
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
5308
|
+
}
|
|
5309
|
+
function assess(member, nowMs) {
|
|
5310
|
+
const plan = planOf(member.account);
|
|
5311
|
+
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`];
|
|
5312
|
+
const base = { identity: member.identity, plan };
|
|
5313
|
+
if (member.readError !== void 0) {
|
|
5314
|
+
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
5350
5315
|
}
|
|
5351
|
-
|
|
5352
|
-
const
|
|
5353
|
-
|
|
5354
|
-
return
|
|
5355
|
-
}
|
|
5356
|
-
const
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
const versionOk = version2 !== void 0 && versionSatisfies(version2, config.source);
|
|
5373
|
-
const pinned = pinnedGitCommit(config.source);
|
|
5374
|
-
const commitOk = pinned === void 0 || ports.installedCommit() === pinned;
|
|
5375
|
-
if (versionOk && commitOk && (installedSource === void 0 || installedSource === config.source)) {
|
|
5376
|
-
installedSource = config.source;
|
|
5377
|
-
return true;
|
|
5378
|
-
}
|
|
5379
|
-
ports.log(`agent-shim headroom supervisor: installing headroom from ${config.source}`);
|
|
5380
|
-
const result = ports.install(config.source);
|
|
5381
|
-
if (!result.ok) {
|
|
5382
|
-
return false;
|
|
5316
|
+
const state = member.snapshot?.providers[ANTHROPIC_PROVIDER];
|
|
5317
|
+
const unified = unifiedOf(state);
|
|
5318
|
+
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
5319
|
+
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
5320
|
+
}
|
|
5321
|
+
const observedAgo = state.rateLimit === void 0 ? "" : ` (last seen ${formatAge(nowMs - Date.parse(state.rateLimit.observedAt))} ago)`;
|
|
5322
|
+
const fiveHourWindow = unified.fiveHour;
|
|
5323
|
+
const sevenDayWindow = unified.sevenDay;
|
|
5324
|
+
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
5325
|
+
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
5326
|
+
const windowReasons = [
|
|
5327
|
+
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
5328
|
+
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
5329
|
+
];
|
|
5330
|
+
const blockedUntil = Math.max(
|
|
5331
|
+
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
5332
|
+
limitBlockedUntil(state, nowMs) ?? 0
|
|
5333
|
+
);
|
|
5334
|
+
if (blockedUntil > nowMs) {
|
|
5335
|
+
if (unified.overageStatus === "allowed") {
|
|
5336
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
5383
5337
|
}
|
|
5384
|
-
|
|
5385
|
-
return true;
|
|
5386
|
-
};
|
|
5387
|
-
const socket = prepareHeadroomSocket(fs12, ports.socketTrust, paths, ports.ownPid);
|
|
5388
|
-
if (socket.refused !== void 0) {
|
|
5389
|
-
return fail(socket.refused);
|
|
5338
|
+
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
5390
5339
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
if (!ensureInstalled()) {
|
|
5394
|
-
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5340
|
+
if (plan.kind === "pay-per-use" || fiveHourWindow === void 0 && sevenDayWindow === void 0) {
|
|
5341
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [...plan.kind === "pay-per-use" ? planReasons : ["no plan windows reported, so usage bills as extra usage"]] } };
|
|
5395
5342
|
}
|
|
5396
|
-
const
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
const orphanPid = previousState?.headroomPid;
|
|
5400
|
-
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
5401
|
-
ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
5402
|
-
ports.stopProcess(orphanPid);
|
|
5343
|
+
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;
|
|
5344
|
+
if (basis?.window.utilization === void 0) {
|
|
5345
|
+
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
5403
5346
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5347
|
+
const remaining = Math.max(0, 1 - basis.window.utilization);
|
|
5348
|
+
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5349
|
+
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
5350
|
+
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
5351
|
+
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5352
|
+
const burn = fiveHourBurn(member.records);
|
|
5353
|
+
return {
|
|
5354
|
+
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
5355
|
+
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
5356
|
+
...burn === void 0 ? {} : { burn }
|
|
5357
|
+
};
|
|
5358
|
+
}
|
|
5359
|
+
function rankPool(input) {
|
|
5360
|
+
const { members, nowMs } = input;
|
|
5361
|
+
const assessments = members.map((member) => assess(member, nowMs));
|
|
5362
|
+
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
5363
|
+
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
5364
|
+
const candidates = assessments.map((assessment) => {
|
|
5365
|
+
const { candidate, fiveHour, burn } = assessment;
|
|
5366
|
+
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
5367
|
+
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
5368
|
+
return { ...candidate, feasible: true };
|
|
5414
5369
|
}
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
const allowlist = allowlistOf(ports);
|
|
5419
|
-
const allowlistHash = hashAllowlist(allowlist);
|
|
5420
|
-
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
5421
|
-
const ownsNow = ownsState();
|
|
5422
|
-
if (!ownsNow && !superseded) {
|
|
5423
|
-
ports.log("agent-shim headroom supervisor: another supervisor now owns the state file; serving this daemon's own sessions until they end");
|
|
5370
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
5371
|
+
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
5372
|
+
return { ...candidate, feasible: true };
|
|
5424
5373
|
}
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
|
|
5441
|
-
}
|
|
5442
|
-
if (installedSource !== config.source && !ensureInstalled()) {
|
|
5443
|
-
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5444
|
-
}
|
|
5445
|
-
const pid = ports.spawnHeadroom(socketPath, allowlist, config.settings);
|
|
5446
|
-
const ready = await waitUntilReady(ports, socketPath);
|
|
5447
|
-
if (ready) {
|
|
5448
|
-
consecutiveFailures = 0;
|
|
5449
|
-
headroomPid = pid;
|
|
5450
|
-
runningHash = allowlistHash;
|
|
5451
|
-
runningSettingsHash = settingsHash;
|
|
5452
|
-
const recorded = writeOwnState({
|
|
5453
|
-
supervisorPid: ports.ownPid,
|
|
5454
|
-
headroomPid: pid,
|
|
5455
|
-
socketPath,
|
|
5456
|
-
version,
|
|
5457
|
-
allowlistHash,
|
|
5458
|
-
settingsHash,
|
|
5459
|
-
installedSource: config.source
|
|
5460
|
-
});
|
|
5461
|
-
if (recorded) {
|
|
5462
|
-
fs12.removeRecursive(paths.headroomLockFile);
|
|
5463
|
-
}
|
|
5464
|
-
idleSince = void 0;
|
|
5465
|
-
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on unix socket ${socketPath}`);
|
|
5466
|
-
} else {
|
|
5467
|
-
ports.stopProcess(pid);
|
|
5468
|
-
consecutiveFailures += 1;
|
|
5469
|
-
ports.log(
|
|
5470
|
-
`agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
5471
|
-
);
|
|
5472
|
-
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
5473
|
-
continue;
|
|
5474
|
-
}
|
|
5475
|
-
} else {
|
|
5476
|
-
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
5477
|
-
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
5478
|
-
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
5479
|
-
ports.stopProcess(headroomPid);
|
|
5480
|
-
headroomPid = void 0;
|
|
5481
|
-
runningHash = void 0;
|
|
5482
|
-
runningSettingsHash = void 0;
|
|
5483
|
-
continue;
|
|
5484
|
-
}
|
|
5485
|
-
}
|
|
5486
|
-
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
5487
|
-
if (sessions.length === 0) {
|
|
5488
|
-
idleSince ??= ports.now();
|
|
5489
|
-
const idleMs = ports.now() - idleSince;
|
|
5490
|
-
if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
|
|
5491
|
-
ports.log(
|
|
5492
|
-
`agent-shim headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
5493
|
-
);
|
|
5494
|
-
ports.stopProcess(headroomPid);
|
|
5495
|
-
writeOwnState({ version });
|
|
5496
|
-
return 0;
|
|
5497
|
-
}
|
|
5498
|
-
} else {
|
|
5499
|
-
idleSince = void 0;
|
|
5500
|
-
}
|
|
5501
|
-
}
|
|
5502
|
-
await ports.sleep(HEADROOM_POLL_MS);
|
|
5503
|
-
}
|
|
5504
|
-
}
|
|
5505
|
-
function currentHeadroomAllowlist(fs12, paths) {
|
|
5506
|
-
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
5507
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
5508
|
-
}
|
|
5509
|
-
function allowlistOf(ports) {
|
|
5510
|
-
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
5511
|
-
}
|
|
5512
|
-
async function waitUntilReady(ports, socketPath) {
|
|
5513
|
-
const deadline = ports.now() + HEADROOM_READY_TIMEOUT_MS;
|
|
5514
|
-
for (; ; ) {
|
|
5515
|
-
if (await ports.ready(socketPath)) {
|
|
5516
|
-
return true;
|
|
5517
|
-
}
|
|
5518
|
-
if (ports.now() >= deadline) {
|
|
5519
|
-
return false;
|
|
5520
|
-
}
|
|
5521
|
-
await ports.sleep(HEADROOM_POLL_MS);
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
|
|
5525
|
-
// src/launcher/guard.ts
|
|
5526
|
-
var AMBIENT_CREDENTIAL_VARS = [
|
|
5527
|
-
"ANTHROPIC_API_KEY",
|
|
5528
|
-
"ANTHROPIC_AUTH_TOKEN",
|
|
5529
|
-
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
5530
|
-
"CLAUDE_CODE_USE_BEDROCK",
|
|
5531
|
-
"CLAUDE_CODE_USE_VERTEX",
|
|
5532
|
-
"CLAUDE_CODE_USE_FOUNDRY"
|
|
5533
|
-
];
|
|
5534
|
-
function detectAmbientCredential(env, injected) {
|
|
5535
|
-
for (const variable of AMBIENT_CREDENTIAL_VARS) {
|
|
5536
|
-
const value = env[variable];
|
|
5537
|
-
if (value === void 0 || value === "") {
|
|
5538
|
-
continue;
|
|
5539
|
-
}
|
|
5540
|
-
if (injected?.variable === variable && value === injected.token) {
|
|
5541
|
-
continue;
|
|
5374
|
+
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
5375
|
+
});
|
|
5376
|
+
const ordered = [...candidates].sort(
|
|
5377
|
+
(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)
|
|
5378
|
+
);
|
|
5379
|
+
const sticky = input.sticky;
|
|
5380
|
+
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
5381
|
+
const stickyCandidate = ordered[stickyIndex];
|
|
5382
|
+
if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
|
|
5383
|
+
const ageMs = nowMs - Date.parse(sticky.at);
|
|
5384
|
+
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
5385
|
+
if (input.resuming || warm) {
|
|
5386
|
+
const why = warm ? `used here ${formatAge(ageMs)} ago, prompt cache still warm` : "resuming a conversation started on this account";
|
|
5387
|
+
ordered.splice(stickyIndex, 1);
|
|
5388
|
+
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
5542
5389
|
}
|
|
5543
|
-
return { variable };
|
|
5544
|
-
}
|
|
5545
|
-
return void 0;
|
|
5546
|
-
}
|
|
5547
|
-
function formatAmbientCredentialGuardMessage(variable, identityName) {
|
|
5548
|
-
const identitySetCommand = identityName === void 0 ? "agent-shim identity set <name> --allow-ambient-credential" : `agent-shim identity set ${identityName} --allow-ambient-credential`;
|
|
5549
|
-
return [
|
|
5550
|
-
`error: ${variable} is set in the environment. This identity's isolated`,
|
|
5551
|
-
"credential would be bypassed \u2014 every identity authenticates as this same key",
|
|
5552
|
-
"while it's set. Unset it, or if this is deliberate, opt in per-launch with",
|
|
5553
|
-
"AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL=1, or persistently for this identity with",
|
|
5554
|
-
`\`${identitySetCommand}\`.`
|
|
5555
|
-
].join("\n");
|
|
5556
|
-
}
|
|
5557
|
-
function evaluateAmbientCredentialGuard(params) {
|
|
5558
|
-
const detected = detectAmbientCredential(params.env, params.injectedCredential);
|
|
5559
|
-
if (detected === void 0) {
|
|
5560
|
-
return { ok: true };
|
|
5561
|
-
}
|
|
5562
|
-
if (params.allowAmbientCredentialOverride || params.allowAmbientCredential || params.providerSelected === true) {
|
|
5563
|
-
return { ok: true };
|
|
5564
5390
|
}
|
|
5391
|
+
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
5392
|
+
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
5393
|
+
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
5565
5394
|
return {
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
message: formatAmbientCredentialGuardMessage(detected.variable, params.identityName)
|
|
5395
|
+
candidates: ordered,
|
|
5396
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
5569
5397
|
};
|
|
5570
5398
|
}
|
|
5571
5399
|
|
|
5572
|
-
// src/
|
|
5573
|
-
var
|
|
5574
|
-
var
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5400
|
+
// src/usage/poolPick.ts
|
|
5401
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
5402
|
+
var import_zod8 = require("zod");
|
|
5403
|
+
|
|
5404
|
+
// src/usage/store.ts
|
|
5405
|
+
var DAY_MS2 = 864e5;
|
|
5406
|
+
var DAYS_IN_WEEK = 7;
|
|
5407
|
+
var USAGE_RETENTION_MS = DAYS_IN_WEEK * DAY_MS2;
|
|
5408
|
+
|
|
5409
|
+
// src/usage/poolPick.ts
|
|
5410
|
+
var SCHEMA_VERSION = 1;
|
|
5411
|
+
var PickEntrySchema = import_zod8.z.strictObject({ identity: import_zod8.z.string().min(1), at: import_zod8.z.iso.datetime() });
|
|
5412
|
+
var PicksFileSchema = import_zod8.z.strictObject({
|
|
5413
|
+
schemaVersion: import_zod8.z.literal(SCHEMA_VERSION),
|
|
5414
|
+
picks: import_zod8.z.record(import_zod8.z.string().min(1), PickEntrySchema)
|
|
5415
|
+
});
|
|
5416
|
+
function errorMessage(error) {
|
|
5417
|
+
return error instanceof Error ? error.message : String(error);
|
|
5584
5418
|
}
|
|
5585
|
-
function
|
|
5586
|
-
|
|
5419
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5420
|
+
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5421
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5422
|
+
return identities.map((identity) => {
|
|
5423
|
+
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5424
|
+
try {
|
|
5425
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5426
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5427
|
+
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5428
|
+
} catch (error) {
|
|
5429
|
+
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
5430
|
+
return { identity, records, readError: errorMessage(error) };
|
|
5431
|
+
}
|
|
5432
|
+
throw error;
|
|
5433
|
+
}
|
|
5434
|
+
});
|
|
5587
5435
|
}
|
|
5588
|
-
function
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5436
|
+
function rankPoolFromStore(params) {
|
|
5437
|
+
return rankPool({
|
|
5438
|
+
members: loadPoolMembers(params.fs, params.paths, params.identities, params.nowMs),
|
|
5439
|
+
nowMs: params.nowMs,
|
|
5440
|
+
resuming: params.resuming,
|
|
5441
|
+
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5442
|
+
});
|
|
5443
|
+
}
|
|
5444
|
+
function readPicksFile(fs12, file) {
|
|
5445
|
+
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5446
|
+
const raw = fs12.readFileUtf8(file);
|
|
5447
|
+
if (raw === void 0) {
|
|
5448
|
+
return { file: empty };
|
|
5449
|
+
}
|
|
5450
|
+
let parsed;
|
|
5451
|
+
try {
|
|
5452
|
+
parsed = JSON.parse(raw);
|
|
5453
|
+
} catch {
|
|
5454
|
+
return { file: empty, problem: `${file} is not valid JSON` };
|
|
5592
5455
|
}
|
|
5593
|
-
|
|
5456
|
+
const result = PicksFileSchema.safeParse(parsed);
|
|
5457
|
+
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5594
5458
|
}
|
|
5595
|
-
function
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
configProfilesDir: import_node_path12.default.join(root, "config-profiles"),
|
|
5600
|
-
providersDir: import_node_path12.default.join(root, "providers"),
|
|
5601
|
-
directoryRulesFile: import_node_path12.default.join(root, "directory-rules.json"),
|
|
5602
|
-
activeIdentityFile: import_node_path12.default.join(root, "active-identity"),
|
|
5603
|
-
globalConfigFile: import_node_path12.default.join(root, "config.json"),
|
|
5604
|
-
categoriesLocalFile: import_node_path12.default.join(root, "categories.local.json"),
|
|
5605
|
-
claudeShimFile: import_node_path12.default.join(root, "claude-shim.json"),
|
|
5606
|
-
headroomDir: import_node_path12.default.join(root, "headroom"),
|
|
5607
|
-
headroomStateFile: import_node_path12.default.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
5608
|
-
headroomSocketDir: import_node_path12.default.join(root, "headroom", "run"),
|
|
5609
|
-
headroomLockFile: import_node_path12.default.join(root, "headroom", "start.lock"),
|
|
5610
|
-
headroomSessionsDir: import_node_path12.default.join(root, "headroom", "sessions"),
|
|
5611
|
-
logsDir: import_node_path12.default.join(root, "logs"),
|
|
5612
|
-
headroomLogPath: import_node_path12.default.join(root, "logs", "headroom.log"),
|
|
5613
|
-
frontdoorDir: import_node_path12.default.join(root, "frontdoor"),
|
|
5614
|
-
frontdoorCaDir: import_node_path12.default.join(root, "frontdoor", "ca"),
|
|
5615
|
-
frontdoorCaCertFile: import_node_path12.default.join(root, "frontdoor", "ca", "ca.pem"),
|
|
5616
|
-
frontdoorCaKeyFile: import_node_path12.default.join(root, "frontdoor", "ca", "ca.key"),
|
|
5617
|
-
frontdoorCaBundlesDir: import_node_path12.default.join(root, "frontdoor", "ca", "bundles"),
|
|
5618
|
-
frontdoorStateFile: import_node_path12.default.join(root, "frontdoor", "state.json"),
|
|
5619
|
-
frontdoorLockFile: import_node_path12.default.join(root, "frontdoor", "start.lock"),
|
|
5620
|
-
frontdoorControlTokenFile: import_node_path12.default.join(root, "frontdoor", "control-token"),
|
|
5621
|
-
frontdoorSessionsDir: import_node_path12.default.join(root, "frontdoor", "sessions"),
|
|
5622
|
-
frontdoorLogPath: import_node_path12.default.join(root, "logs", "frontdoor.log"),
|
|
5623
|
-
usageDir: import_node_path12.default.join(root, "usage"),
|
|
5624
|
-
usageLogDir: import_node_path12.default.join(root, "usage", "log"),
|
|
5625
|
-
usageSnapshotsDir: import_node_path12.default.join(root, "usage", "snapshots"),
|
|
5626
|
-
usagePicksFile: import_node_path12.default.join(root, "usage", "picks.json")
|
|
5627
|
-
};
|
|
5459
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5460
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5461
|
+
const entry = read.file.picks[directory];
|
|
5462
|
+
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5628
5463
|
}
|
|
5629
|
-
function
|
|
5630
|
-
|
|
5464
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5465
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5466
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5467
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5468
|
+
fs12.mkdirPrivate(import_node_path11.default.dirname(picksFile));
|
|
5469
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5470
|
+
`);
|
|
5631
5471
|
}
|
|
5632
5472
|
|
|
5633
|
-
// src/identityStore.ts
|
|
5634
|
-
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
5635
|
-
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
5636
|
-
|
|
5637
5473
|
// src/launcher/identity.ts
|
|
5638
|
-
var
|
|
5474
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
5639
5475
|
function poolNameOf(selector) {
|
|
5640
5476
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
5641
5477
|
}
|
|
@@ -5684,1471 +5520,1682 @@ function decideConfigProfile(params) {
|
|
|
5684
5520
|
return { source: "none" };
|
|
5685
5521
|
}
|
|
5686
5522
|
function loadIdentity(identitiesDir, name, fs12) {
|
|
5687
|
-
return loadConfigFile(
|
|
5523
|
+
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
5688
5524
|
}
|
|
5689
5525
|
|
|
5690
|
-
// src/
|
|
5691
|
-
var
|
|
5692
|
-
var
|
|
5693
|
-
var
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
this.name = "ProfileNotFoundError";
|
|
5698
|
-
}
|
|
5699
|
-
profileName;
|
|
5700
|
-
};
|
|
5701
|
-
var ProfileAlreadyExistsError = class extends CliError {
|
|
5702
|
-
constructor(profileName) {
|
|
5703
|
-
super(`A configuration profile named "${profileName}" already exists.`);
|
|
5704
|
-
this.profileName = profileName;
|
|
5705
|
-
this.name = "ProfileAlreadyExistsError";
|
|
5706
|
-
}
|
|
5707
|
-
profileName;
|
|
5708
|
-
};
|
|
5709
|
-
var InvalidCategoryNameError = class extends CliError {
|
|
5710
|
-
constructor(categoryName) {
|
|
5711
|
-
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
5712
|
-
this.categoryName = categoryName;
|
|
5713
|
-
this.name = "InvalidCategoryNameError";
|
|
5714
|
-
}
|
|
5715
|
-
categoryName;
|
|
5716
|
-
};
|
|
5717
|
-
function profileJsonPath(paths, name) {
|
|
5718
|
-
return import_node_path14.default.join(paths.configProfilesDir, `${name}.json`);
|
|
5526
|
+
// src/launcher/pool.ts
|
|
5527
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5528
|
+
var TERMINATOR = "--";
|
|
5529
|
+
var REASONS_LOGGED = 3;
|
|
5530
|
+
function splitMembers(pool, paths, fs12) {
|
|
5531
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5532
|
+
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5719
5533
|
}
|
|
5720
|
-
function
|
|
5721
|
-
|
|
5534
|
+
function isResuming(passthrough) {
|
|
5535
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5536
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5722
5537
|
}
|
|
5723
|
-
function
|
|
5724
|
-
|
|
5725
|
-
|
|
5538
|
+
function resolvePoolLaunch(params) {
|
|
5539
|
+
const { poolName, log } = params;
|
|
5540
|
+
const pool = params.pools?.[poolName];
|
|
5541
|
+
if (pool === void 0) {
|
|
5542
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5726
5543
|
}
|
|
5727
|
-
}
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
}
|
|
5731
|
-
function createProfile(paths, name, extendsList, description) {
|
|
5732
|
-
if (profileExists(paths, name)) {
|
|
5733
|
-
throw new ProfileAlreadyExistsError(name);
|
|
5544
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5545
|
+
for (const name of missing) {
|
|
5546
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5734
5547
|
}
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
...description === void 0 ? {} : { description },
|
|
5738
|
-
...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
|
|
5739
|
-
});
|
|
5740
|
-
if (!parsed.success) {
|
|
5741
|
-
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
5548
|
+
if (identities.length === 0) {
|
|
5549
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5742
5550
|
}
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
if (!import_node_fs3.default.existsSync(paths.configProfilesDir)) {
|
|
5748
|
-
return [];
|
|
5551
|
+
const resuming = isResuming(params.passthrough);
|
|
5552
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5553
|
+
if (stickyRead.problem !== void 0) {
|
|
5554
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5749
5555
|
}
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
const
|
|
5754
|
-
if (
|
|
5755
|
-
|
|
5556
|
+
for (; ; ) {
|
|
5557
|
+
const nowMs = params.now();
|
|
5558
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5559
|
+
const { pick, earliestReturn } = ranking;
|
|
5560
|
+
if (pick !== void 0) {
|
|
5561
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5562
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5756
5563
|
}
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
}
|
|
5760
|
-
function globalConfigPath(paths) {
|
|
5761
|
-
return paths.globalConfigFile;
|
|
5762
|
-
}
|
|
5763
|
-
function readGlobalConfig(paths) {
|
|
5764
|
-
return readJson(globalConfigPath(paths), GlobalConfigSchema);
|
|
5765
|
-
}
|
|
5766
|
-
function setGlobalDefaultProfile(paths, name) {
|
|
5767
|
-
return applyPatch(
|
|
5768
|
-
globalConfigPath(paths),
|
|
5769
|
-
GlobalConfigSchema,
|
|
5770
|
-
{ defaultConfigProfile: name },
|
|
5771
|
-
{ defaults: {} }
|
|
5772
|
-
);
|
|
5773
|
-
}
|
|
5774
|
-
function validateCategoryNames(patch) {
|
|
5775
|
-
for (const key of Object.keys(patch)) {
|
|
5776
|
-
if (!isOverridableCategory(key)) {
|
|
5777
|
-
throw new InvalidCategoryNameError(key);
|
|
5564
|
+
if (earliestReturn === void 0) {
|
|
5565
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5778
5566
|
}
|
|
5567
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5568
|
+
if (!params.wait) {
|
|
5569
|
+
return {
|
|
5570
|
+
ok: false,
|
|
5571
|
+
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.`
|
|
5572
|
+
};
|
|
5573
|
+
}
|
|
5574
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5575
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5779
5576
|
}
|
|
5780
5577
|
}
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
|
|
5794
|
-
}
|
|
5795
|
-
function setProfileLaunchFlags(paths, name, patch) {
|
|
5796
|
-
requireProfileExists(paths, name);
|
|
5797
|
-
const existing = readProfile(paths, name) ?? {};
|
|
5798
|
-
const mergedLaunch = { ...existing.launch, ...patch };
|
|
5799
|
-
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
5800
|
-
// Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
|
|
5801
|
-
launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
|
|
5802
|
-
});
|
|
5578
|
+
|
|
5579
|
+
// src/poolPickReport.ts
|
|
5580
|
+
function candidateView(candidate) {
|
|
5581
|
+
return {
|
|
5582
|
+
identity: candidate.identity,
|
|
5583
|
+
class: candidate.class,
|
|
5584
|
+
...candidate.score === void 0 ? {} : { score: candidate.score },
|
|
5585
|
+
feasible: candidate.feasible,
|
|
5586
|
+
...candidate.blockedUntilMs === void 0 ? {} : { blockedUntil: new Date(candidate.blockedUntilMs).toISOString() },
|
|
5587
|
+
plan: candidate.plan,
|
|
5588
|
+
reasons: [...candidate.reasons]
|
|
5589
|
+
};
|
|
5803
5590
|
}
|
|
5804
|
-
function
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5591
|
+
function collectPoolPick(params) {
|
|
5592
|
+
const { present, missing } = splitMembers(params.pool, params.paths, params.fs);
|
|
5593
|
+
const sticky = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.directory);
|
|
5594
|
+
const ranking = rankPoolFromStore({
|
|
5595
|
+
fs: params.usageFs,
|
|
5596
|
+
paths: params.paths,
|
|
5597
|
+
identities: present,
|
|
5598
|
+
nowMs: params.nowMs,
|
|
5599
|
+
resuming: false,
|
|
5600
|
+
...sticky.sticky === void 0 ? {} : { sticky: sticky.sticky }
|
|
5809
5601
|
});
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5602
|
+
return {
|
|
5603
|
+
pool: params.poolName,
|
|
5604
|
+
directory: params.directory,
|
|
5605
|
+
...ranking.pick === void 0 ? {} : { pick: ranking.pick.identity },
|
|
5606
|
+
candidates: ranking.candidates.map(candidateView),
|
|
5607
|
+
missing: [...missing],
|
|
5608
|
+
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
5609
|
+
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
5610
|
+
};
|
|
5814
5611
|
}
|
|
5815
5612
|
|
|
5816
|
-
// src/
|
|
5817
|
-
var
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
const pool = readPools(paths)[name];
|
|
5846
|
-
if (pool === void 0) {
|
|
5847
|
-
throw new PoolNotFoundError(name);
|
|
5848
|
-
}
|
|
5849
|
-
return pool;
|
|
5850
|
-
}
|
|
5851
|
-
function writePools(paths, pools) {
|
|
5852
|
-
applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
|
|
5853
|
-
}
|
|
5854
|
-
function addPool(paths, name, identities) {
|
|
5855
|
-
if (!PoolNameSchema.safeParse(name).success) {
|
|
5856
|
-
throw new InvalidPoolNameError(name);
|
|
5857
|
-
}
|
|
5858
|
-
const pools = readPools(paths);
|
|
5859
|
-
if (pools[name] !== void 0) {
|
|
5860
|
-
throw new PoolAlreadyExistsError(name);
|
|
5861
|
-
}
|
|
5862
|
-
const pool = { identities: [...identities] };
|
|
5863
|
-
writePools(paths, { ...pools, [name]: pool });
|
|
5864
|
-
return pool;
|
|
5865
|
-
}
|
|
5866
|
-
function setPool(paths, name, identities) {
|
|
5867
|
-
requirePool(paths, name);
|
|
5868
|
-
const pool = { identities: [...identities] };
|
|
5869
|
-
writePools(paths, { ...readPools(paths), [name]: pool });
|
|
5870
|
-
return pool;
|
|
5871
|
-
}
|
|
5872
|
-
function removePool(paths, name) {
|
|
5873
|
-
requirePool(paths, name);
|
|
5874
|
-
const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
|
|
5875
|
-
writePools(paths, remaining);
|
|
5876
|
-
}
|
|
5613
|
+
// src/usage/pickReportSchema.ts
|
|
5614
|
+
var import_zod9 = require("zod");
|
|
5615
|
+
var PoolPickCandidateViewSchema = import_zod9.z.strictObject({
|
|
5616
|
+
identity: import_zod9.z.string(),
|
|
5617
|
+
/** In pick order: `scored` has usable quota data, `unknown` has none, `pay-per-use` bills by use, `ineligible` is refused right now. */
|
|
5618
|
+
class: import_zod9.z.enum(["scored", "unknown", "pay-per-use", "ineligible"]),
|
|
5619
|
+
/** Plan-size-weighted remaining quota per hour until its reset; present for `scored` only. */
|
|
5620
|
+
score: import_zod9.z.number().optional(),
|
|
5621
|
+
/** False when the five-hour window would run dry, at the observed pace, before it resets. */
|
|
5622
|
+
feasible: import_zod9.z.boolean(),
|
|
5623
|
+
/** When an `ineligible` member can be used again, as an ISO instant. */
|
|
5624
|
+
blockedUntil: import_zod9.z.iso.datetime().optional(),
|
|
5625
|
+
plan: PlanClassSchema,
|
|
5626
|
+
/** The facts behind the class and score, in the order they matter. */
|
|
5627
|
+
reasons: import_zod9.z.array(import_zod9.z.string())
|
|
5628
|
+
});
|
|
5629
|
+
var PoolPickReportSchema = import_zod9.z.strictObject({
|
|
5630
|
+
pool: import_zod9.z.string(),
|
|
5631
|
+
directory: import_zod9.z.string(),
|
|
5632
|
+
/** The member a launch would run as, absent when every member is refused. */
|
|
5633
|
+
pick: import_zod9.z.string().optional(),
|
|
5634
|
+
candidates: import_zod9.z.array(PoolPickCandidateViewSchema),
|
|
5635
|
+
/** Members that name an identity that does not exist; a launch skips them. */
|
|
5636
|
+
missing: import_zod9.z.array(import_zod9.z.string()),
|
|
5637
|
+
/** When nothing can be picked, the soonest any member returns. */
|
|
5638
|
+
earliestReturn: import_zod9.z.strictObject({ identity: import_zod9.z.string(), at: import_zod9.z.iso.datetime() }).optional(),
|
|
5639
|
+
/** Why the last-pick record could not be read, when that is the case. */
|
|
5640
|
+
stickyProblem: import_zod9.z.string().optional()
|
|
5641
|
+
});
|
|
5877
5642
|
|
|
5878
|
-
// src/
|
|
5879
|
-
var
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5643
|
+
// src/headroom/ensure.ts
|
|
5644
|
+
var HEADROOM_START_TIMEOUT_MS = 12e4;
|
|
5645
|
+
var HEADROOM_LAUNCHER_POLL_MS = 250;
|
|
5646
|
+
var HeadroomStartError = class extends CliError {
|
|
5647
|
+
constructor(message) {
|
|
5648
|
+
super(message);
|
|
5649
|
+
this.name = "HeadroomStartError";
|
|
5884
5650
|
}
|
|
5885
|
-
name;
|
|
5886
5651
|
};
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5652
|
+
function ensureHeadroom(params) {
|
|
5653
|
+
const { paths, ports } = params;
|
|
5654
|
+
const deadline = ports.now() + HEADROOM_START_TIMEOUT_MS;
|
|
5655
|
+
let spawnedSupervisor = false;
|
|
5656
|
+
let awaitingAllowlist = false;
|
|
5657
|
+
for (; ; ) {
|
|
5658
|
+
const state = readHeadroomState(ports.fs, paths.headroomStateFile);
|
|
5659
|
+
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
5660
|
+
const daemonUp = state.socketPath !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
5661
|
+
if (daemonUp && state.socketPath !== void 0) {
|
|
5662
|
+
const required = params.requiredAllowlistHash?.();
|
|
5663
|
+
if (required === void 0 || state.allowlistHash === required) {
|
|
5664
|
+
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
5665
|
+
return { socketPath: state.socketPath };
|
|
5666
|
+
}
|
|
5667
|
+
const holders = sessionsForSupervisor(ports.fs, paths.headroomSessionsDir, state.supervisorPid).filter((session) => ports.isRunning(session.pid));
|
|
5668
|
+
if (holders.length > 0) {
|
|
5669
|
+
throw new HeadroomStartError(
|
|
5670
|
+
`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.`
|
|
5671
|
+
);
|
|
5672
|
+
}
|
|
5673
|
+
awaitingAllowlist = true;
|
|
5674
|
+
}
|
|
5675
|
+
if (state.lastError !== void 0) {
|
|
5676
|
+
throw new HeadroomStartError(
|
|
5677
|
+
`the headroom daemon reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.headroomLogPath})`
|
|
5678
|
+
);
|
|
5679
|
+
}
|
|
5680
|
+
} else if (!spawnedSupervisor) {
|
|
5681
|
+
ports.fs.mkdirp(paths.headroomDir);
|
|
5682
|
+
const created = ports.fs.writeFileExclusive(
|
|
5683
|
+
paths.headroomLockFile,
|
|
5684
|
+
`${JSON.stringify({ pid: params.launcherPid, at: ports.now() })}
|
|
5685
|
+
`
|
|
5686
|
+
);
|
|
5687
|
+
if (created) {
|
|
5688
|
+
ports.spawnSupervisor(paths);
|
|
5689
|
+
spawnedSupervisor = true;
|
|
5690
|
+
} else {
|
|
5691
|
+
const lock = readStartLock(ports.fs, paths.headroomLockFile);
|
|
5692
|
+
if (lock === void 0 || !ports.isRunning(lock.pid)) {
|
|
5693
|
+
ports.fs.removeRecursive(paths.headroomLockFile);
|
|
5694
|
+
continue;
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
if (ports.now() >= deadline) {
|
|
5699
|
+
const lastError = state?.lastError;
|
|
5700
|
+
throw new HeadroomStartError(
|
|
5701
|
+
`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}`
|
|
5702
|
+
);
|
|
5703
|
+
}
|
|
5704
|
+
ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
|
|
5902
5705
|
}
|
|
5903
|
-
attemptedName;
|
|
5904
|
-
};
|
|
5905
|
-
function identityJsonPath(paths, name) {
|
|
5906
|
-
return import_node_path15.default.join(paths.identitiesDir, name, "identity.json");
|
|
5907
5706
|
}
|
|
5908
|
-
|
|
5909
|
-
|
|
5707
|
+
|
|
5708
|
+
// src/headroom/settings.ts
|
|
5709
|
+
var import_node_crypto8 = require("node:crypto");
|
|
5710
|
+
function settingsOf(config) {
|
|
5711
|
+
return {
|
|
5712
|
+
...config.mode === void 0 ? {} : { mode: config.mode },
|
|
5713
|
+
...config.targetRatio === void 0 ? {} : { targetRatio: config.targetRatio },
|
|
5714
|
+
...config.ccr === void 0 ? {} : { ccr: config.ccr },
|
|
5715
|
+
...config.rolloutChannel === void 0 ? {} : { rolloutChannel: config.rolloutChannel },
|
|
5716
|
+
...config.interceptToolResults === void 0 ? {} : { interceptToolResults: config.interceptToolResults },
|
|
5717
|
+
...config.readMaturation === void 0 ? {} : { readMaturation: config.readMaturation }
|
|
5718
|
+
};
|
|
5910
5719
|
}
|
|
5911
|
-
function
|
|
5912
|
-
return
|
|
5720
|
+
function settingsArgs(settings) {
|
|
5721
|
+
return [
|
|
5722
|
+
...settings.mode === void 0 ? [] : ["--mode", settings.mode],
|
|
5723
|
+
...settings.targetRatio === void 0 ? [] : ["--target-ratio", String(settings.targetRatio)],
|
|
5724
|
+
...settings.ccr === "lossless" ? ["--lossless"] : [],
|
|
5725
|
+
...settings.ccr === "none" ? ["--no-ccr"] : [],
|
|
5726
|
+
...settings.interceptToolResults === true ? ["--intercept-tool-results"] : [],
|
|
5727
|
+
...settings.readMaturation === true ? ["--read-maturation"] : []
|
|
5728
|
+
];
|
|
5913
5729
|
}
|
|
5914
|
-
function
|
|
5915
|
-
|
|
5916
|
-
throw new IdentityAlreadyExistsError(name);
|
|
5917
|
-
}
|
|
5918
|
-
const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
|
|
5919
|
-
if (!parsed.success) {
|
|
5920
|
-
throw new InvalidIdentityNameError(name);
|
|
5921
|
-
}
|
|
5922
|
-
writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
|
|
5923
|
-
return parsed.data;
|
|
5730
|
+
function settingsEnv(settings) {
|
|
5731
|
+
return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
|
|
5924
5732
|
}
|
|
5925
|
-
function
|
|
5926
|
-
|
|
5927
|
-
if (pool !== void 0) {
|
|
5928
|
-
requirePool(paths, pool);
|
|
5929
|
-
} else if (!identityExists(paths, name)) {
|
|
5930
|
-
throw new IdentityNotFoundError(name);
|
|
5931
|
-
}
|
|
5932
|
-
writeTextAtomic(paths.activeIdentityFile, `${name}
|
|
5933
|
-
`);
|
|
5733
|
+
function hashSettings(settings) {
|
|
5734
|
+
return (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
|
|
5934
5735
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5736
|
+
|
|
5737
|
+
// src/headroom/socket.ts
|
|
5738
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
5739
|
+
var GROUP_OR_OTHER_BITS = 63;
|
|
5740
|
+
var OCTAL_RADIX = 8;
|
|
5741
|
+
var MODE_DIGITS = 4;
|
|
5742
|
+
var HEADROOM_SOCKET_SUFFIX = ".sock";
|
|
5743
|
+
var HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
|
|
5744
|
+
var UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
5745
|
+
var PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
|
|
5746
|
+
function headroomSocketPath(paths, supervisorPid) {
|
|
5747
|
+
return import_node_path13.default.join(paths.headroomSocketDir, `${String(supervisorPid)}${HEADROOM_SOCKET_SUFFIX}`);
|
|
5941
5748
|
}
|
|
5942
|
-
function
|
|
5943
|
-
return
|
|
5749
|
+
function platformRefusal(platform) {
|
|
5750
|
+
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;
|
|
5944
5751
|
}
|
|
5945
|
-
function
|
|
5946
|
-
|
|
5947
|
-
return
|
|
5948
|
-
} catch (error) {
|
|
5949
|
-
if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
|
|
5950
|
-
return { problem: error.message.replace(/\s*\n\s*/g, " ") };
|
|
5951
|
-
}
|
|
5952
|
-
throw error;
|
|
5752
|
+
function ownerOnlyRefusal(label, target, stat, expected, uid) {
|
|
5753
|
+
if (stat === void 0) {
|
|
5754
|
+
return `the headroom ${label} ${target} does not exist`;
|
|
5953
5755
|
}
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
if (!import_node_fs4.default.existsSync(paths.identitiesDir)) {
|
|
5957
|
-
return [];
|
|
5756
|
+
if (stat.kind === "symlink") {
|
|
5757
|
+
return `the headroom ${label} ${target} is a symlink; it must be the real ${expected === "dir" ? "directory" : "socket"}`;
|
|
5958
5758
|
}
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
const result = [];
|
|
5962
|
-
for (const name of names) {
|
|
5963
|
-
const read = readIdentityForListing(paths, name);
|
|
5964
|
-
if (read === void 0) {
|
|
5965
|
-
continue;
|
|
5966
|
-
}
|
|
5967
|
-
const isActive = name === active;
|
|
5968
|
-
result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
|
|
5759
|
+
if (stat.kind !== expected) {
|
|
5760
|
+
return `the headroom ${label} ${target} is not a ${expected === "dir" ? "directory" : "socket"}`;
|
|
5969
5761
|
}
|
|
5970
|
-
|
|
5971
|
-
}
|
|
5972
|
-
function setDefaultConfigProfile(paths, identityName, profileName) {
|
|
5973
|
-
if (!identityExists(paths, identityName)) {
|
|
5974
|
-
throw new IdentityNotFoundError(identityName);
|
|
5762
|
+
if (stat.uid !== uid) {
|
|
5763
|
+
return `the headroom ${label} ${target} is owned by uid ${String(stat.uid)}, not by this user (uid ${String(uid)})`;
|
|
5975
5764
|
}
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
});
|
|
5979
|
-
}
|
|
5980
|
-
function setAllowAmbientCredential(paths, identityName, allow) {
|
|
5981
|
-
if (!identityExists(paths, identityName)) {
|
|
5982
|
-
throw new IdentityNotFoundError(identityName);
|
|
5765
|
+
if ((stat.mode & GROUP_OR_OTHER_BITS) !== 0) {
|
|
5766
|
+
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`;
|
|
5983
5767
|
}
|
|
5984
|
-
return
|
|
5985
|
-
allowAmbientCredential: allow
|
|
5986
|
-
});
|
|
5768
|
+
return void 0;
|
|
5987
5769
|
}
|
|
5988
|
-
function
|
|
5989
|
-
const
|
|
5990
|
-
if (
|
|
5991
|
-
|
|
5770
|
+
function prepareHeadroomSocket(fs12, ports, paths, supervisorPid) {
|
|
5771
|
+
const unsupported = platformRefusal(ports.platform);
|
|
5772
|
+
if (unsupported !== void 0) {
|
|
5773
|
+
return { refused: unsupported };
|
|
5992
5774
|
}
|
|
5993
|
-
|
|
5994
|
-
|
|
5775
|
+
const socketPath = headroomSocketPath(paths, supervisorPid);
|
|
5776
|
+
const length = Buffer.byteLength(socketPath, "utf8");
|
|
5777
|
+
if (length > UNIX_SOCKET_PATH_MAX_BYTES) {
|
|
5778
|
+
return {
|
|
5779
|
+
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`
|
|
5780
|
+
};
|
|
5995
5781
|
}
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
|
|
5782
|
+
if (ports.lstat(paths.headroomSocketDir)?.kind === "symlink") {
|
|
5783
|
+
return { refused: `the headroom socket directory ${paths.headroomSocketDir} is a symlink; it must be the real directory` };
|
|
5999
5784
|
}
|
|
6000
|
-
|
|
6001
|
-
const
|
|
6002
|
-
return
|
|
6003
|
-
credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
|
|
6004
|
-
});
|
|
6005
|
-
}
|
|
6006
|
-
function belongsToIdentity(entry, name) {
|
|
6007
|
-
return entry === name || entry === import_node_path15.default.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
5785
|
+
fs12.mkdirPrivate(paths.headroomSocketDir);
|
|
5786
|
+
const refusal = ownerOnlyRefusal("socket directory", paths.headroomSocketDir, ports.lstat(paths.headroomSocketDir), "dir", ports.currentUid());
|
|
5787
|
+
return refusal === void 0 ? { socketPath } : { refused: refusal };
|
|
6008
5788
|
}
|
|
6009
|
-
function
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
import_node_fs4.default.rmSync(import_node_path15.default.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
5789
|
+
function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
|
|
5790
|
+
const removed = [];
|
|
5791
|
+
for (const name of fs12.readdir(paths.headroomSocketDir)) {
|
|
5792
|
+
const pid = HEADROOM_SOCKET_NAME.exec(name)?.[1];
|
|
5793
|
+
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
5794
|
+
continue;
|
|
6016
5795
|
}
|
|
5796
|
+
fs12.removeRecursive(import_node_path13.default.join(paths.headroomSocketDir, name));
|
|
5797
|
+
removed.push(name);
|
|
6017
5798
|
}
|
|
6018
|
-
|
|
6019
|
-
import_node_fs4.default.rmSync(paths.activeIdentityFile);
|
|
6020
|
-
}
|
|
5799
|
+
return removed;
|
|
6021
5800
|
}
|
|
6022
5801
|
|
|
6023
|
-
// src/
|
|
6024
|
-
var
|
|
6025
|
-
var
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
5802
|
+
// src/headroom/source.ts
|
|
5803
|
+
var import_zod10 = require("zod");
|
|
5804
|
+
var GIT_SPEC_RE = /git\+[^@\s]+(?:@([^\s#]+))?/;
|
|
5805
|
+
var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
5806
|
+
var DirectUrlSchema = import_zod10.z.object({ vcs_info: import_zod10.z.object({ commit_id: import_zod10.z.string() }) });
|
|
5807
|
+
function isMovingGitSource(spec) {
|
|
5808
|
+
const match = GIT_SPEC_RE.exec(spec);
|
|
5809
|
+
if (match === null) {
|
|
5810
|
+
return false;
|
|
5811
|
+
}
|
|
5812
|
+
const ref = match[1];
|
|
5813
|
+
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
5814
|
+
}
|
|
5815
|
+
function pinnedGitCommit(spec) {
|
|
5816
|
+
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
5817
|
+
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
5818
|
+
}
|
|
6029
5819
|
|
|
6030
|
-
// src/
|
|
6031
|
-
|
|
6032
|
-
var SECONDS_PER_MINUTE = 60;
|
|
6033
|
-
var MINUTES_PER_HOUR = 60;
|
|
6034
|
-
var HOURS_PER_DAY = 24;
|
|
6035
|
-
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
6036
|
-
function formatAge(ms) {
|
|
6037
|
-
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
6038
|
-
if (minutes < MINUTES_PER_HOUR) {
|
|
6039
|
-
return `${String(Math.max(minutes, 0))}m`;
|
|
6040
|
-
}
|
|
6041
|
-
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
6042
|
-
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
6043
|
-
}
|
|
6044
|
-
function effectiveWindow(window, nowMs) {
|
|
6045
|
-
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
6046
|
-
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
6047
|
-
return { reset: true, utilization: 0 };
|
|
6048
|
-
}
|
|
5820
|
+
// src/headroom/supervisor.ts
|
|
5821
|
+
function resolveSupervisorConfig(configured) {
|
|
6049
5822
|
return {
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
...window.status === void 0 ? {} : { status: window.status }
|
|
5823
|
+
source: configured.source ?? HEADROOM_DEFAULT_SOURCE,
|
|
5824
|
+
idleShutdownMinutes: configured.idleShutdownMinutes ?? HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES,
|
|
5825
|
+
settings: settingsOf(configured)
|
|
6054
5826
|
};
|
|
6055
5827
|
}
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
{ name: "five-hour", window: unified.fiveHour },
|
|
6065
|
-
{ name: "seven-day", window: unified.sevenDay }
|
|
6066
|
-
];
|
|
6067
|
-
return windows.flatMap(({ name, window }) => {
|
|
6068
|
-
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
6069
|
-
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
6070
|
-
return [];
|
|
6071
|
-
}
|
|
6072
|
-
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
6073
|
-
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
6074
|
-
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
6075
|
-
});
|
|
5828
|
+
var HEADROOM_BACKOFF_BASE_MS = 500;
|
|
5829
|
+
var HEADROOM_BACKOFF_CAP_MS = 3e4;
|
|
5830
|
+
var HEADROOM_START_RETRY_BUDGET = 5;
|
|
5831
|
+
var HEADROOM_READY_TIMEOUT_MS = 15e3;
|
|
5832
|
+
var HEADROOM_POLL_MS = 1e3;
|
|
5833
|
+
var MS_PER_MINUTE2 = 6e4;
|
|
5834
|
+
function backoffForAttempt(attempt) {
|
|
5835
|
+
return Math.min(HEADROOM_BACKOFF_CAP_MS, HEADROOM_BACKOFF_BASE_MS * 2 ** (attempt - 1));
|
|
6076
5836
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
}
|
|
6085
|
-
|
|
6086
|
-
|
|
5837
|
+
function versionSatisfies(version, source) {
|
|
5838
|
+
if (version === void 0) {
|
|
5839
|
+
return false;
|
|
5840
|
+
}
|
|
5841
|
+
const specifier = /==|>=|<=|!=|>|<|~=/.exec(source);
|
|
5842
|
+
if (specifier === null) {
|
|
5843
|
+
return true;
|
|
5844
|
+
}
|
|
5845
|
+
const installed = parseVersion(version);
|
|
5846
|
+
const wanted = parseVersion(source.slice(specifier.index + specifier[0].length));
|
|
5847
|
+
if (installed === void 0 || wanted === void 0) {
|
|
5848
|
+
return true;
|
|
5849
|
+
}
|
|
5850
|
+
const cmp = compareVersions(installed, wanted);
|
|
5851
|
+
switch (specifier[0]) {
|
|
5852
|
+
case "==":
|
|
5853
|
+
return cmp === 0;
|
|
5854
|
+
case "!=":
|
|
5855
|
+
return cmp !== 0;
|
|
5856
|
+
case ">=":
|
|
5857
|
+
return cmp >= 0;
|
|
5858
|
+
case "<=":
|
|
5859
|
+
return cmp <= 0;
|
|
5860
|
+
case ">":
|
|
5861
|
+
return cmp > 0;
|
|
5862
|
+
case "<":
|
|
5863
|
+
return cmp < 0;
|
|
5864
|
+
case "~=":
|
|
5865
|
+
return compareVersions(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
5866
|
+
default:
|
|
5867
|
+
return true;
|
|
5868
|
+
}
|
|
6087
5869
|
}
|
|
6088
|
-
function
|
|
6089
|
-
|
|
5870
|
+
function parseVersion(input) {
|
|
5871
|
+
const match = /(\d+)(\.\d+)*/.exec(input);
|
|
5872
|
+
if (match === null) {
|
|
6090
5873
|
return void 0;
|
|
6091
5874
|
}
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
5875
|
+
return match[0].split(".").map((part) => Number.parseInt(part, 10));
|
|
5876
|
+
}
|
|
5877
|
+
function compareVersions(a, b) {
|
|
5878
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
5879
|
+
const left = a[index] ?? 0;
|
|
5880
|
+
const right = b[index] ?? 0;
|
|
5881
|
+
if (left !== right) {
|
|
5882
|
+
return left - right;
|
|
5883
|
+
}
|
|
6095
5884
|
}
|
|
6096
|
-
return
|
|
5885
|
+
return 0;
|
|
6097
5886
|
}
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
5887
|
+
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
5888
|
+
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
5889
|
+
const { fs: fs12, paths } = ports;
|
|
5890
|
+
let config = initialConfig;
|
|
5891
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
5892
|
+
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
5893
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
5894
|
+
return 0;
|
|
5895
|
+
}
|
|
5896
|
+
let installedSource = previousState?.installedSource;
|
|
5897
|
+
const ownsState = () => {
|
|
5898
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
5899
|
+
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
5900
|
+
};
|
|
5901
|
+
const writeOwnState = (state) => {
|
|
5902
|
+
if (!ownsState()) {
|
|
5903
|
+
return false;
|
|
5904
|
+
}
|
|
5905
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
5906
|
+
return true;
|
|
5907
|
+
};
|
|
5908
|
+
const fail = (message) => {
|
|
5909
|
+
if (writeOwnState({ lastError: message })) {
|
|
5910
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
5911
|
+
}
|
|
5912
|
+
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
5913
|
+
return 1;
|
|
5914
|
+
};
|
|
5915
|
+
const ensureInstalled = () => {
|
|
5916
|
+
const version2 = ports.headroomVersion();
|
|
5917
|
+
const versionOk = version2 !== void 0 && versionSatisfies(version2, config.source);
|
|
5918
|
+
const pinned = pinnedGitCommit(config.source);
|
|
5919
|
+
const commitOk = pinned === void 0 || ports.installedCommit() === pinned;
|
|
5920
|
+
if (versionOk && commitOk && (installedSource === void 0 || installedSource === config.source)) {
|
|
5921
|
+
installedSource = config.source;
|
|
5922
|
+
return true;
|
|
5923
|
+
}
|
|
5924
|
+
ports.log(`agent-shim headroom supervisor: installing headroom from ${config.source}`);
|
|
5925
|
+
const result = ports.install(config.source);
|
|
5926
|
+
if (!result.ok) {
|
|
5927
|
+
return false;
|
|
5928
|
+
}
|
|
5929
|
+
installedSource = config.source;
|
|
5930
|
+
return true;
|
|
5931
|
+
};
|
|
5932
|
+
const socket = prepareHeadroomSocket(fs12, ports.socketTrust, paths, ports.ownPid);
|
|
5933
|
+
if (socket.refused !== void 0) {
|
|
5934
|
+
return fail(socket.refused);
|
|
5935
|
+
}
|
|
5936
|
+
const { socketPath } = socket;
|
|
5937
|
+
pruneStaleHeadroomSockets(fs12, paths, ports.isRunning);
|
|
5938
|
+
if (!ensureInstalled()) {
|
|
5939
|
+
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5940
|
+
}
|
|
5941
|
+
const version = ports.headroomVersion() ?? "unknown";
|
|
5942
|
+
writeOwnState({ supervisorPid: ports.ownPid, version, installedSource });
|
|
5943
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
5944
|
+
const orphanPid = previousState?.headroomPid;
|
|
5945
|
+
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
5946
|
+
ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
5947
|
+
ports.stopProcess(orphanPid);
|
|
5948
|
+
}
|
|
5949
|
+
let headroomPid;
|
|
5950
|
+
let runningHash;
|
|
5951
|
+
let runningSettingsHash;
|
|
5952
|
+
let consecutiveFailures = 0;
|
|
5953
|
+
let idleSince;
|
|
5954
|
+
let superseded = false;
|
|
5955
|
+
let ticks = 0;
|
|
5956
|
+
for (; ; ) {
|
|
5957
|
+
if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
|
|
5958
|
+
return HEADROOM_SUPERVISOR_STILL_RUNNING;
|
|
5959
|
+
}
|
|
5960
|
+
ticks += 1;
|
|
5961
|
+
config = ports.readConfig();
|
|
5962
|
+
const settingsHash = hashSettings(config.settings);
|
|
5963
|
+
const allowlist = allowlistOf(ports);
|
|
5964
|
+
const allowlistHash = hashAllowlist(allowlist);
|
|
5965
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
5966
|
+
const ownsNow = ownsState();
|
|
5967
|
+
if (!ownsNow && !superseded) {
|
|
5968
|
+
ports.log("agent-shim headroom supervisor: another supervisor now owns the state file; serving this daemon's own sessions until they end");
|
|
5969
|
+
}
|
|
5970
|
+
superseded = !ownsNow;
|
|
5971
|
+
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
5972
|
+
if (headroomPid === void 0 || crashed) {
|
|
5973
|
+
if (crashed) {
|
|
5974
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(headroomPid)} died`);
|
|
5975
|
+
headroomPid = void 0;
|
|
5976
|
+
runningHash = void 0;
|
|
5977
|
+
runningSettingsHash = void 0;
|
|
5978
|
+
writeOwnState({
|
|
5979
|
+
supervisorPid: ports.ownPid,
|
|
5980
|
+
version,
|
|
5981
|
+
installedSource
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
if (consecutiveFailures >= HEADROOM_START_RETRY_BUDGET) {
|
|
5985
|
+
return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
|
|
5986
|
+
}
|
|
5987
|
+
if (installedSource !== config.source && !ensureInstalled()) {
|
|
5988
|
+
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
5989
|
+
}
|
|
5990
|
+
const pid = ports.spawnHeadroom(socketPath, allowlist, config.settings);
|
|
5991
|
+
const ready = await waitUntilReady(ports, socketPath);
|
|
5992
|
+
if (ready) {
|
|
5993
|
+
consecutiveFailures = 0;
|
|
5994
|
+
headroomPid = pid;
|
|
5995
|
+
runningHash = allowlistHash;
|
|
5996
|
+
runningSettingsHash = settingsHash;
|
|
5997
|
+
const recorded = writeOwnState({
|
|
5998
|
+
supervisorPid: ports.ownPid,
|
|
5999
|
+
headroomPid: pid,
|
|
6000
|
+
socketPath,
|
|
6001
|
+
version,
|
|
6002
|
+
allowlistHash,
|
|
6003
|
+
settingsHash,
|
|
6004
|
+
installedSource: config.source
|
|
6005
|
+
});
|
|
6006
|
+
if (recorded) {
|
|
6007
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
6008
|
+
}
|
|
6009
|
+
idleSince = void 0;
|
|
6010
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on unix socket ${socketPath}`);
|
|
6011
|
+
} else {
|
|
6012
|
+
ports.stopProcess(pid);
|
|
6013
|
+
consecutiveFailures += 1;
|
|
6014
|
+
ports.log(
|
|
6015
|
+
`agent-shim headroom supervisor: headroom did not become ready on unix socket ${socketPath} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
6016
|
+
);
|
|
6017
|
+
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
6018
|
+
continue;
|
|
6019
|
+
}
|
|
6020
|
+
} else {
|
|
6021
|
+
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
6022
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
6023
|
+
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
6024
|
+
ports.stopProcess(headroomPid);
|
|
6025
|
+
headroomPid = void 0;
|
|
6026
|
+
runningHash = void 0;
|
|
6027
|
+
runningSettingsHash = void 0;
|
|
6028
|
+
continue;
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
6032
|
+
if (sessions.length === 0) {
|
|
6033
|
+
idleSince ??= ports.now();
|
|
6034
|
+
const idleMs = ports.now() - idleSince;
|
|
6035
|
+
if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
|
|
6036
|
+
ports.log(
|
|
6037
|
+
`agent-shim headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
6038
|
+
);
|
|
6039
|
+
ports.stopProcess(headroomPid);
|
|
6040
|
+
writeOwnState({ version });
|
|
6041
|
+
return 0;
|
|
6042
|
+
}
|
|
6043
|
+
} else {
|
|
6044
|
+
idleSince = void 0;
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
6102
6048
|
}
|
|
6103
|
-
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
6104
6049
|
}
|
|
6105
|
-
function
|
|
6106
|
-
|
|
6050
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
6051
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
6052
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
6107
6053
|
}
|
|
6108
|
-
function
|
|
6109
|
-
|
|
6110
|
-
let entry;
|
|
6111
|
-
try {
|
|
6112
|
-
entry = params.port.read(store, params.owner);
|
|
6113
|
-
} catch (error) {
|
|
6114
|
-
return { store, status: "unreadable", reason: error instanceof Error ? error.message : String(error) };
|
|
6115
|
-
}
|
|
6116
|
-
if (entry === void 0) {
|
|
6117
|
-
return { store, status: "empty" };
|
|
6118
|
-
}
|
|
6119
|
-
const limit = ttlMs(params.cache);
|
|
6120
|
-
const ageMs = cacheAgeMs(entry, params.nowMs);
|
|
6121
|
-
return {
|
|
6122
|
-
store,
|
|
6123
|
-
status: isFresh(entry, params.cache, params.nowMs) ? "fresh" : "expired",
|
|
6124
|
-
ageMs,
|
|
6125
|
-
...limit === void 0 ? {} : { expiresInMs: limit - ageMs }
|
|
6126
|
-
};
|
|
6054
|
+
function allowlistOf(ports) {
|
|
6055
|
+
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
6127
6056
|
}
|
|
6128
|
-
function
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
const age = `cached ${formatAge(state.ageMs)} ago`;
|
|
6139
|
-
if (state.expiresInMs === void 0) {
|
|
6140
|
-
return `${age}, no expiry`;
|
|
6057
|
+
async function waitUntilReady(ports, socketPath) {
|
|
6058
|
+
const deadline = ports.now() + HEADROOM_READY_TIMEOUT_MS;
|
|
6059
|
+
for (; ; ) {
|
|
6060
|
+
if (await ports.ready(socketPath)) {
|
|
6061
|
+
return true;
|
|
6062
|
+
}
|
|
6063
|
+
if (ports.now() >= deadline) {
|
|
6064
|
+
return false;
|
|
6065
|
+
}
|
|
6066
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
6141
6067
|
}
|
|
6142
|
-
return state.status === "fresh" ? `${age}, expires in ${formatAge(state.expiresInMs)}` : `${age}, expired ${formatAge(-state.expiresInMs)} ago`;
|
|
6143
6068
|
}
|
|
6144
6069
|
|
|
6145
|
-
// src/
|
|
6146
|
-
var
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6070
|
+
// src/launcher/guard.ts
|
|
6071
|
+
var AMBIENT_CREDENTIAL_VARS = [
|
|
6072
|
+
"ANTHROPIC_API_KEY",
|
|
6073
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
6074
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
6075
|
+
"CLAUDE_CODE_USE_BEDROCK",
|
|
6076
|
+
"CLAUDE_CODE_USE_VERTEX",
|
|
6077
|
+
"CLAUDE_CODE_USE_FOUNDRY"
|
|
6078
|
+
];
|
|
6079
|
+
function detectAmbientCredential(env, injected) {
|
|
6080
|
+
for (const variable of AMBIENT_CREDENTIAL_VARS) {
|
|
6081
|
+
const value = env[variable];
|
|
6082
|
+
if (value === void 0 || value === "") {
|
|
6083
|
+
continue;
|
|
6084
|
+
}
|
|
6085
|
+
if (injected?.variable === variable && value === injected.token) {
|
|
6086
|
+
continue;
|
|
6087
|
+
}
|
|
6088
|
+
return { variable };
|
|
6158
6089
|
}
|
|
6159
|
-
|
|
6160
|
-
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
6090
|
+
return void 0;
|
|
6161
6091
|
}
|
|
6162
|
-
function
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6092
|
+
function formatAmbientCredentialGuardMessage(variable, identityName) {
|
|
6093
|
+
const identitySetCommand = identityName === void 0 ? "agent-shim identity set <name> --allow-ambient-credential" : `agent-shim identity set ${identityName} --allow-ambient-credential`;
|
|
6094
|
+
return [
|
|
6095
|
+
`error: ${variable} is set in the environment. This identity's isolated`,
|
|
6096
|
+
"credential would be bypassed \u2014 every identity authenticates as this same key",
|
|
6097
|
+
"while it's set. Unset it, or if this is deliberate, opt in per-launch with",
|
|
6098
|
+
"AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL=1, or persistently for this identity with",
|
|
6099
|
+
`\`${identitySetCommand}\`.`
|
|
6100
|
+
].join("\n");
|
|
6170
6101
|
}
|
|
6171
|
-
function
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
if ("file" in source) {
|
|
6176
|
-
return { kind: "file", path: source.file };
|
|
6177
|
-
}
|
|
6178
|
-
if ("command" in source) {
|
|
6179
|
-
return { kind: "command", program: source.command[0] };
|
|
6180
|
-
}
|
|
6181
|
-
if ("op" in source) {
|
|
6182
|
-
return { kind: "op", reference: source.op };
|
|
6102
|
+
function evaluateAmbientCredentialGuard(params) {
|
|
6103
|
+
const detected = detectAmbientCredential(params.env, params.injectedCredential);
|
|
6104
|
+
if (detected === void 0) {
|
|
6105
|
+
return { ok: true };
|
|
6183
6106
|
}
|
|
6184
|
-
if (
|
|
6185
|
-
return {
|
|
6186
|
-
kind: "keychain",
|
|
6187
|
-
service: source.keychain.service,
|
|
6188
|
-
...source.keychain.account === void 0 ? {} : { account: source.keychain.account }
|
|
6189
|
-
};
|
|
6107
|
+
if (params.allowAmbientCredentialOverride || params.allowAmbientCredential || params.providerSelected === true) {
|
|
6108
|
+
return { ok: true };
|
|
6190
6109
|
}
|
|
6191
|
-
return { kind: "literal" };
|
|
6192
|
-
}
|
|
6193
|
-
function summariseCredential(credential) {
|
|
6194
6110
|
return {
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6111
|
+
ok: false,
|
|
6112
|
+
variable: detected.variable,
|
|
6113
|
+
message: formatAmbientCredentialGuardMessage(detected.variable, params.identityName)
|
|
6198
6114
|
};
|
|
6199
6115
|
}
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
return `op ${summary.reference}`;
|
|
6210
|
-
case "keychain":
|
|
6211
|
-
return `keychain ${summary.service}${summary.account === void 0 ? "" : ` (account ${summary.account})`}`;
|
|
6212
|
-
case "literal":
|
|
6213
|
-
return "literal (non-secret placeholder)";
|
|
6214
|
-
default:
|
|
6215
|
-
return summary;
|
|
6116
|
+
|
|
6117
|
+
// src/paths.ts
|
|
6118
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
6119
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
6120
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
6121
|
+
function resolveAgentShimHome(env = process.env, home = import_node_os.default.homedir(), isDirectory = isExistingDirectory) {
|
|
6122
|
+
const fromEnv = env.AGENT_SHIM_HOME;
|
|
6123
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6124
|
+
return fromEnv;
|
|
6216
6125
|
}
|
|
6126
|
+
const current = import_node_path14.default.join(home, HOME_DIRNAME);
|
|
6127
|
+
const legacy = import_node_path14.default.join(home, LEGACY_HOME_DIRNAME);
|
|
6128
|
+
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
6217
6129
|
}
|
|
6218
|
-
function
|
|
6219
|
-
return
|
|
6130
|
+
function isExistingDirectory(candidate) {
|
|
6131
|
+
return import_node_fs2.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
6220
6132
|
}
|
|
6221
|
-
function
|
|
6222
|
-
const
|
|
6223
|
-
|
|
6133
|
+
function resolveClaudeHome() {
|
|
6134
|
+
const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
|
|
6135
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6136
|
+
return fromEnv;
|
|
6137
|
+
}
|
|
6138
|
+
return import_node_path14.default.join(import_node_os.default.homedir(), ".claude");
|
|
6224
6139
|
}
|
|
6225
|
-
function
|
|
6226
|
-
return
|
|
6140
|
+
function buildLayoutPaths(root) {
|
|
6141
|
+
return {
|
|
6142
|
+
root,
|
|
6143
|
+
identitiesDir: import_node_path14.default.join(root, "identities"),
|
|
6144
|
+
configProfilesDir: import_node_path14.default.join(root, "config-profiles"),
|
|
6145
|
+
providersDir: import_node_path14.default.join(root, "providers"),
|
|
6146
|
+
directoryRulesFile: import_node_path14.default.join(root, "directory-rules.json"),
|
|
6147
|
+
activeIdentityFile: import_node_path14.default.join(root, "active-identity"),
|
|
6148
|
+
globalConfigFile: import_node_path14.default.join(root, "config.json"),
|
|
6149
|
+
categoriesLocalFile: import_node_path14.default.join(root, "categories.local.json"),
|
|
6150
|
+
claudeShimFile: import_node_path14.default.join(root, "claude-shim.json"),
|
|
6151
|
+
headroomDir: import_node_path14.default.join(root, "headroom"),
|
|
6152
|
+
headroomStateFile: import_node_path14.default.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
6153
|
+
headroomSocketDir: import_node_path14.default.join(root, "headroom", "run"),
|
|
6154
|
+
headroomLockFile: import_node_path14.default.join(root, "headroom", "start.lock"),
|
|
6155
|
+
headroomSessionsDir: import_node_path14.default.join(root, "headroom", "sessions"),
|
|
6156
|
+
logsDir: import_node_path14.default.join(root, "logs"),
|
|
6157
|
+
headroomLogPath: import_node_path14.default.join(root, "logs", "headroom.log"),
|
|
6158
|
+
frontdoorDir: import_node_path14.default.join(root, "frontdoor"),
|
|
6159
|
+
frontdoorCaDir: import_node_path14.default.join(root, "frontdoor", "ca"),
|
|
6160
|
+
frontdoorCaCertFile: import_node_path14.default.join(root, "frontdoor", "ca", "ca.pem"),
|
|
6161
|
+
frontdoorCaKeyFile: import_node_path14.default.join(root, "frontdoor", "ca", "ca.key"),
|
|
6162
|
+
frontdoorCaBundlesDir: import_node_path14.default.join(root, "frontdoor", "ca", "bundles"),
|
|
6163
|
+
frontdoorStateFile: import_node_path14.default.join(root, "frontdoor", "state.json"),
|
|
6164
|
+
frontdoorLockFile: import_node_path14.default.join(root, "frontdoor", "start.lock"),
|
|
6165
|
+
frontdoorControlTokenFile: import_node_path14.default.join(root, "frontdoor", "control-token"),
|
|
6166
|
+
frontdoorSessionsDir: import_node_path14.default.join(root, "frontdoor", "sessions"),
|
|
6167
|
+
frontdoorLogPath: import_node_path14.default.join(root, "logs", "frontdoor.log"),
|
|
6168
|
+
usageDir: import_node_path14.default.join(root, "usage"),
|
|
6169
|
+
usageLogDir: import_node_path14.default.join(root, "usage", "log"),
|
|
6170
|
+
usageSnapshotsDir: import_node_path14.default.join(root, "usage", "snapshots"),
|
|
6171
|
+
usagePicksFile: import_node_path14.default.join(root, "usage", "picks.json")
|
|
6172
|
+
};
|
|
6227
6173
|
}
|
|
6228
|
-
function
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
}
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX2)}); run \`chmod 600 ${source.file}\``,
|
|
6245
|
-
true
|
|
6246
|
-
);
|
|
6247
|
-
}
|
|
6248
|
-
const token2 = read.content.trim();
|
|
6249
|
-
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
6174
|
+
function resolveLayoutPaths() {
|
|
6175
|
+
return buildLayoutPaths(resolveAgentShimHome());
|
|
6176
|
+
}
|
|
6177
|
+
|
|
6178
|
+
// src/identityStore.ts
|
|
6179
|
+
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
6180
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
6181
|
+
|
|
6182
|
+
// src/configProfilesStore.ts
|
|
6183
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
6184
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
6185
|
+
var ProfileNotFoundError = class extends CliError {
|
|
6186
|
+
constructor(profileName) {
|
|
6187
|
+
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
6188
|
+
this.profileName = profileName;
|
|
6189
|
+
this.name = "ProfileNotFoundError";
|
|
6250
6190
|
}
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6191
|
+
profileName;
|
|
6192
|
+
};
|
|
6193
|
+
var ProfileAlreadyExistsError = class extends CliError {
|
|
6194
|
+
constructor(profileName) {
|
|
6195
|
+
super(`A configuration profile named "${profileName}" already exists.`);
|
|
6196
|
+
this.profileName = profileName;
|
|
6197
|
+
this.name = "ProfileAlreadyExistsError";
|
|
6255
6198
|
}
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6199
|
+
profileName;
|
|
6200
|
+
};
|
|
6201
|
+
var InvalidCategoryNameError = class extends CliError {
|
|
6202
|
+
constructor(categoryName) {
|
|
6203
|
+
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
6204
|
+
this.categoryName = categoryName;
|
|
6205
|
+
this.name = "InvalidCategoryNameError";
|
|
6260
6206
|
}
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6207
|
+
categoryName;
|
|
6208
|
+
};
|
|
6209
|
+
function profileJsonPath(paths, name) {
|
|
6210
|
+
return import_node_path15.default.join(paths.configProfilesDir, `${name}.json`);
|
|
6211
|
+
}
|
|
6212
|
+
function profileExists(paths, name) {
|
|
6213
|
+
return import_node_fs3.default.existsSync(profileJsonPath(paths, name));
|
|
6214
|
+
}
|
|
6215
|
+
function requireProfileExists(paths, name) {
|
|
6216
|
+
if (!profileExists(paths, name)) {
|
|
6217
|
+
throw new ProfileNotFoundError(name);
|
|
6265
6218
|
}
|
|
6266
|
-
const token = result.stdout.trim();
|
|
6267
|
-
return token === "" ? none("printed no token") : { ok: true, token };
|
|
6268
6219
|
}
|
|
6269
|
-
function
|
|
6270
|
-
|
|
6271
|
-
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
6220
|
+
function readProfile(paths, name) {
|
|
6221
|
+
return readJson(profileJsonPath(paths, name), ConfigProfileSchema);
|
|
6272
6222
|
}
|
|
6273
|
-
function
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
6277
|
-
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
6278
|
-
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
6279
|
-
const entry = cache.port.read(store, params.subject);
|
|
6280
|
-
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
6281
|
-
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
6282
|
-
}
|
|
6223
|
+
function createProfile(paths, name, extendsList, description) {
|
|
6224
|
+
if (profileExists(paths, name)) {
|
|
6225
|
+
throw new ProfileAlreadyExistsError(name);
|
|
6283
6226
|
}
|
|
6284
|
-
const
|
|
6285
|
-
const
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6227
|
+
const filePath = profileJsonPath(paths, name);
|
|
6228
|
+
const parsed = ConfigProfileSchema.safeParse({
|
|
6229
|
+
...description === void 0 ? {} : { description },
|
|
6230
|
+
...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
|
|
6231
|
+
});
|
|
6232
|
+
if (!parsed.success) {
|
|
6233
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
6234
|
+
}
|
|
6235
|
+
writeJsonAtomic(filePath, parsed.data);
|
|
6236
|
+
return parsed.data;
|
|
6237
|
+
}
|
|
6238
|
+
function listProfiles(paths) {
|
|
6239
|
+
if (!import_node_fs3.default.existsSync(paths.configProfilesDir)) {
|
|
6240
|
+
return [];
|
|
6241
|
+
}
|
|
6242
|
+
const names = import_node_fs3.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort();
|
|
6243
|
+
const result = [];
|
|
6244
|
+
for (const name of names) {
|
|
6245
|
+
const profile = readProfile(paths, name);
|
|
6246
|
+
if (profile !== void 0) {
|
|
6247
|
+
result.push({ name, profile });
|
|
6293
6248
|
}
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6249
|
+
}
|
|
6250
|
+
return result;
|
|
6251
|
+
}
|
|
6252
|
+
function globalConfigPath(paths) {
|
|
6253
|
+
return paths.globalConfigFile;
|
|
6254
|
+
}
|
|
6255
|
+
function readGlobalConfig(paths) {
|
|
6256
|
+
return readJson(globalConfigPath(paths), GlobalConfigSchema);
|
|
6257
|
+
}
|
|
6258
|
+
function setGlobalDefaultProfile(paths, name) {
|
|
6259
|
+
return applyPatch(
|
|
6260
|
+
globalConfigPath(paths),
|
|
6261
|
+
GlobalConfigSchema,
|
|
6262
|
+
{ defaultConfigProfile: name },
|
|
6263
|
+
{ defaults: {} }
|
|
6264
|
+
);
|
|
6265
|
+
}
|
|
6266
|
+
function validateCategoryNames(patch) {
|
|
6267
|
+
for (const key of Object.keys(patch)) {
|
|
6268
|
+
if (!isOverridableCategory(key)) {
|
|
6269
|
+
throw new InvalidCategoryNameError(key);
|
|
6298
6270
|
}
|
|
6299
6271
|
}
|
|
6300
|
-
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
6301
|
-
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
6302
6272
|
}
|
|
6303
|
-
function
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
};
|
|
6273
|
+
function setProfileCategories(paths, name, patch) {
|
|
6274
|
+
requireProfileExists(paths, name);
|
|
6275
|
+
const expandedPatch = expandAllCategoryKey(patch);
|
|
6276
|
+
validateCategoryNames(expandedPatch);
|
|
6277
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6278
|
+
const mergedCategories = { ...existing.categories, ...expandedPatch };
|
|
6279
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { categories: mergedCategories });
|
|
6280
|
+
}
|
|
6281
|
+
function setProfileEntries(paths, name, patch) {
|
|
6282
|
+
requireProfileExists(paths, name);
|
|
6283
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6284
|
+
const mergedEntries = { ...existing.entries, ...patch };
|
|
6285
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
|
|
6286
|
+
}
|
|
6287
|
+
function setProfileLaunchFlags(paths, name, patch) {
|
|
6288
|
+
requireProfileExists(paths, name);
|
|
6289
|
+
const existing = readProfile(paths, name) ?? {};
|
|
6290
|
+
const mergedLaunch = { ...existing.launch, ...patch };
|
|
6291
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
6292
|
+
// Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
|
|
6293
|
+
launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
|
|
6294
|
+
});
|
|
6295
|
+
}
|
|
6296
|
+
function setProfileMetadata(paths, name, patch) {
|
|
6297
|
+
requireProfileExists(paths, name);
|
|
6298
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
6299
|
+
...patch.extends === void 0 ? {} : { extends: patch.extends.length === 0 ? void 0 : [...patch.extends] },
|
|
6300
|
+
...patch.description === void 0 ? {} : { description: patch.description === false ? void 0 : patch.description }
|
|
6301
|
+
});
|
|
6302
|
+
}
|
|
6303
|
+
function removeProfile(paths, name) {
|
|
6304
|
+
requireProfileExists(paths, name);
|
|
6305
|
+
import_node_fs3.default.rmSync(profileJsonPath(paths, name));
|
|
6309
6306
|
}
|
|
6310
6307
|
|
|
6311
|
-
// src/
|
|
6312
|
-
var
|
|
6313
|
-
constructor(
|
|
6314
|
-
super(`No
|
|
6315
|
-
this.
|
|
6316
|
-
this.name = "
|
|
6308
|
+
// src/poolStore.ts
|
|
6309
|
+
var PoolNotFoundError = class extends CliError {
|
|
6310
|
+
constructor(poolName) {
|
|
6311
|
+
super(`No pool named "${poolName}". Run \`agent-shim pool add ${poolName} --identity <name>...\` first.`);
|
|
6312
|
+
this.poolName = poolName;
|
|
6313
|
+
this.name = "PoolNotFoundError";
|
|
6317
6314
|
}
|
|
6318
|
-
|
|
6315
|
+
poolName;
|
|
6319
6316
|
};
|
|
6320
|
-
var
|
|
6321
|
-
constructor(
|
|
6322
|
-
super(`A
|
|
6323
|
-
this.
|
|
6324
|
-
this.name = "
|
|
6317
|
+
var PoolAlreadyExistsError = class extends CliError {
|
|
6318
|
+
constructor(poolName) {
|
|
6319
|
+
super(`A pool named "${poolName}" already exists. Use \`agent-shim pool set ${poolName}\` to change its members.`);
|
|
6320
|
+
this.poolName = poolName;
|
|
6321
|
+
this.name = "PoolAlreadyExistsError";
|
|
6325
6322
|
}
|
|
6326
|
-
|
|
6323
|
+
poolName;
|
|
6327
6324
|
};
|
|
6328
|
-
var
|
|
6325
|
+
var InvalidPoolNameError = class extends CliError {
|
|
6329
6326
|
constructor(attemptedName) {
|
|
6330
|
-
super(
|
|
6331
|
-
`"${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.`
|
|
6332
|
-
);
|
|
6327
|
+
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.`);
|
|
6333
6328
|
this.attemptedName = attemptedName;
|
|
6334
|
-
this.name = "
|
|
6329
|
+
this.name = "InvalidPoolNameError";
|
|
6335
6330
|
}
|
|
6336
6331
|
attemptedName;
|
|
6337
6332
|
};
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
return import_node_path16.default.join(paths.providersDir, `${name}.json`);
|
|
6333
|
+
function readPools(paths) {
|
|
6334
|
+
return readGlobalConfig(paths)?.pools ?? {};
|
|
6341
6335
|
}
|
|
6342
|
-
function
|
|
6343
|
-
|
|
6336
|
+
function requirePool(paths, name) {
|
|
6337
|
+
const pool = readPools(paths)[name];
|
|
6338
|
+
if (pool === void 0) {
|
|
6339
|
+
throw new PoolNotFoundError(name);
|
|
6340
|
+
}
|
|
6341
|
+
return pool;
|
|
6344
6342
|
}
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
6348
|
-
function isRecord3(value) {
|
|
6349
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6343
|
+
function writePools(paths, pools) {
|
|
6344
|
+
applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
|
|
6350
6345
|
}
|
|
6351
|
-
function
|
|
6352
|
-
if (!
|
|
6353
|
-
|
|
6354
|
-
}
|
|
6355
|
-
const env = isRecord3(raw.env) ? raw.env : void 0;
|
|
6356
|
-
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
6357
|
-
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
6358
|
-
if (fields.length === 0) {
|
|
6359
|
-
return void 0;
|
|
6346
|
+
function addPool(paths, name, identities) {
|
|
6347
|
+
if (!PoolNameSchema.safeParse(name).success) {
|
|
6348
|
+
throw new InvalidPoolNameError(name);
|
|
6360
6349
|
}
|
|
6361
|
-
const
|
|
6362
|
-
if (
|
|
6363
|
-
|
|
6350
|
+
const pools = readPools(paths);
|
|
6351
|
+
if (pools[name] !== void 0) {
|
|
6352
|
+
throw new PoolAlreadyExistsError(name);
|
|
6364
6353
|
}
|
|
6365
|
-
|
|
6366
|
-
|
|
6354
|
+
const pool = { identities: [...identities] };
|
|
6355
|
+
writePools(paths, { ...pools, [name]: pool });
|
|
6356
|
+
return pool;
|
|
6357
|
+
}
|
|
6358
|
+
function setPool(paths, name, identities) {
|
|
6359
|
+
requirePool(paths, name);
|
|
6360
|
+
const pool = { identities: [...identities] };
|
|
6361
|
+
writePools(paths, { ...readPools(paths), [name]: pool });
|
|
6362
|
+
return pool;
|
|
6363
|
+
}
|
|
6364
|
+
function removePool(paths, name) {
|
|
6365
|
+
requirePool(paths, name);
|
|
6366
|
+
const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
|
|
6367
|
+
writePools(paths, remaining);
|
|
6368
|
+
}
|
|
6369
|
+
|
|
6370
|
+
// src/identityStore.ts
|
|
6371
|
+
var IdentityNotFoundError = class extends CliError {
|
|
6372
|
+
constructor(name) {
|
|
6373
|
+
super(`No identity named "${name}". Run \`agent-shim identity add ${name}\` first.`);
|
|
6374
|
+
this.name = name;
|
|
6375
|
+
this.name = "IdentityNotFoundError";
|
|
6367
6376
|
}
|
|
6368
|
-
|
|
6369
|
-
|
|
6377
|
+
name;
|
|
6378
|
+
};
|
|
6379
|
+
var IdentityAlreadyExistsError = class extends CliError {
|
|
6380
|
+
constructor(identityName) {
|
|
6381
|
+
super(`An identity named "${identityName}" already exists.`);
|
|
6382
|
+
this.identityName = identityName;
|
|
6383
|
+
this.name = "IdentityAlreadyExistsError";
|
|
6370
6384
|
}
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
fields,
|
|
6376
|
-
replacement: {
|
|
6377
|
-
...rest,
|
|
6378
|
-
credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
|
|
6379
|
-
...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
|
|
6380
|
-
}
|
|
6381
|
-
};
|
|
6382
|
-
}
|
|
6383
|
-
var LegacyProviderFileError = class extends CliError {
|
|
6384
|
-
constructor(filePath, conversion) {
|
|
6385
|
+
identityName;
|
|
6386
|
+
};
|
|
6387
|
+
var InvalidIdentityNameError = class extends CliError {
|
|
6388
|
+
constructor(attemptedName) {
|
|
6385
6389
|
super(
|
|
6386
|
-
|
|
6387
|
-
` + JSON.stringify(conversion.replacement, null, 2)
|
|
6390
|
+
`"${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.`
|
|
6388
6391
|
);
|
|
6389
|
-
this.
|
|
6390
|
-
this.
|
|
6391
|
-
this.name = "LegacyProviderFileError";
|
|
6392
|
-
}
|
|
6393
|
-
filePath;
|
|
6394
|
-
conversion;
|
|
6395
|
-
};
|
|
6396
|
-
function parseProviderFile(filePath, raw) {
|
|
6397
|
-
const conversion = legacyProviderConversion(raw);
|
|
6398
|
-
if (conversion !== void 0) {
|
|
6399
|
-
throw new LegacyProviderFileError(filePath, conversion);
|
|
6392
|
+
this.attemptedName = attemptedName;
|
|
6393
|
+
this.name = "InvalidIdentityNameError";
|
|
6400
6394
|
}
|
|
6401
|
-
|
|
6395
|
+
attemptedName;
|
|
6396
|
+
};
|
|
6397
|
+
function identityJsonPath(paths, name) {
|
|
6398
|
+
return import_node_path16.default.join(paths.identitiesDir, name, "identity.json");
|
|
6399
|
+
}
|
|
6400
|
+
function identityExists(paths, name) {
|
|
6401
|
+
return import_node_fs4.default.existsSync(identityJsonPath(paths, name));
|
|
6402
|
+
}
|
|
6403
|
+
function readIdentity(paths, name) {
|
|
6404
|
+
return readJson(identityJsonPath(paths, name), IdentitySchema);
|
|
6405
|
+
}
|
|
6406
|
+
function addIdentity(paths, name) {
|
|
6407
|
+
if (identityExists(paths, name)) {
|
|
6408
|
+
throw new IdentityAlreadyExistsError(name);
|
|
6409
|
+
}
|
|
6410
|
+
const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
|
|
6402
6411
|
if (!parsed.success) {
|
|
6403
|
-
throw new
|
|
6412
|
+
throw new InvalidIdentityNameError(name);
|
|
6404
6413
|
}
|
|
6414
|
+
writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
|
|
6405
6415
|
return parsed.data;
|
|
6406
6416
|
}
|
|
6407
|
-
function
|
|
6408
|
-
const
|
|
6409
|
-
if (
|
|
6417
|
+
function useIdentity(paths, name) {
|
|
6418
|
+
const pool = poolNameOf(name);
|
|
6419
|
+
if (pool !== void 0) {
|
|
6420
|
+
requirePool(paths, pool);
|
|
6421
|
+
} else if (!identityExists(paths, name)) {
|
|
6422
|
+
throw new IdentityNotFoundError(name);
|
|
6423
|
+
}
|
|
6424
|
+
writeTextAtomic(paths.activeIdentityFile, `${name}
|
|
6425
|
+
`);
|
|
6426
|
+
}
|
|
6427
|
+
function readActiveIdentity(paths) {
|
|
6428
|
+
if (!import_node_fs4.default.existsSync(paths.activeIdentityFile)) {
|
|
6410
6429
|
return void 0;
|
|
6411
6430
|
}
|
|
6412
|
-
const raw =
|
|
6413
|
-
return
|
|
6431
|
+
const raw = import_node_fs4.default.readFileSync(paths.activeIdentityFile, "utf8").trim();
|
|
6432
|
+
return raw === "" ? void 0 : raw;
|
|
6414
6433
|
}
|
|
6415
|
-
function
|
|
6416
|
-
|
|
6417
|
-
const raw = port.readConfigFile(filePath);
|
|
6418
|
-
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
6434
|
+
function isIdentityDirectoryName(name) {
|
|
6435
|
+
return !name.startsWith(".");
|
|
6419
6436
|
}
|
|
6420
|
-
function
|
|
6421
|
-
|
|
6422
|
-
(
|
|
6423
|
-
|
|
6424
|
-
|
|
6437
|
+
function readIdentityForListing(paths, name) {
|
|
6438
|
+
try {
|
|
6439
|
+
return readIdentity(paths, name);
|
|
6440
|
+
} catch (error) {
|
|
6441
|
+
if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
|
|
6442
|
+
return { problem: error.message.replace(/\s*\n\s*/g, " ") };
|
|
6443
|
+
}
|
|
6444
|
+
throw error;
|
|
6445
|
+
}
|
|
6446
|
+
}
|
|
6447
|
+
function listIdentities(paths) {
|
|
6448
|
+
if (!import_node_fs4.default.existsSync(paths.identitiesDir)) {
|
|
6449
|
+
return [];
|
|
6450
|
+
}
|
|
6451
|
+
const active = readActiveIdentity(paths);
|
|
6452
|
+
const names = import_node_fs4.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort();
|
|
6425
6453
|
const result = [];
|
|
6426
6454
|
for (const name of names) {
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
provider = readProvider(paths, name);
|
|
6430
|
-
} catch (error) {
|
|
6431
|
-
if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
|
|
6432
|
-
throw error;
|
|
6433
|
-
}
|
|
6434
|
-
console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
|
|
6455
|
+
const read = readIdentityForListing(paths, name);
|
|
6456
|
+
if (read === void 0) {
|
|
6435
6457
|
continue;
|
|
6436
6458
|
}
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
}
|
|
6459
|
+
const isActive = name === active;
|
|
6460
|
+
result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
|
|
6440
6461
|
}
|
|
6441
6462
|
return result;
|
|
6442
6463
|
}
|
|
6443
|
-
function
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
entries = readdir(providersDir);
|
|
6447
|
-
} catch {
|
|
6448
|
-
return [];
|
|
6464
|
+
function setDefaultConfigProfile(paths, identityName, profileName) {
|
|
6465
|
+
if (!identityExists(paths, identityName)) {
|
|
6466
|
+
throw new IdentityNotFoundError(identityName);
|
|
6449
6467
|
}
|
|
6450
|
-
return
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
|
|
6468
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6469
|
+
defaultConfigProfile: profileName
|
|
6470
|
+
});
|
|
6454
6471
|
}
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
this.name = "ProviderKindMismatchError";
|
|
6472
|
+
function setAllowAmbientCredential(paths, identityName, allow) {
|
|
6473
|
+
if (!identityExists(paths, identityName)) {
|
|
6474
|
+
throw new IdentityNotFoundError(identityName);
|
|
6459
6475
|
}
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6476
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6477
|
+
allowAmbientCredential: allow
|
|
6478
|
+
});
|
|
6463
6479
|
}
|
|
6464
|
-
function
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
if (!PROVIDER_NAME_RE.test(name)) {
|
|
6469
|
-
throw new InvalidProviderNameError(name);
|
|
6470
|
-
}
|
|
6471
|
-
const kind = input.kind ?? "http";
|
|
6472
|
-
if (kind === "codex" && input.baseUrl !== void 0) {
|
|
6473
|
-
throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
|
|
6474
|
-
}
|
|
6475
|
-
if (kind === "http" && input.baseUrl === void 0) {
|
|
6476
|
-
throw new ProviderKindMismatchError("An http provider needs --base-url.");
|
|
6480
|
+
function setIdentityCredential(paths, identityName, change) {
|
|
6481
|
+
const existing = readIdentity(paths, identityName);
|
|
6482
|
+
if (existing === void 0) {
|
|
6483
|
+
throw new IdentityNotFoundError(identityName);
|
|
6477
6484
|
}
|
|
6478
|
-
if (
|
|
6479
|
-
|
|
6485
|
+
if (change === false) {
|
|
6486
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
|
|
6480
6487
|
}
|
|
6481
|
-
const
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
|
|
6485
|
-
};
|
|
6486
|
-
const parsed = ProviderSchema.safeParse(
|
|
6487
|
-
kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
|
|
6488
|
-
);
|
|
6489
|
-
if (!parsed.success) {
|
|
6490
|
-
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6488
|
+
const sources = change.sources ?? existing.credential?.sources;
|
|
6489
|
+
if (sources === void 0) {
|
|
6490
|
+
throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
|
|
6491
6491
|
}
|
|
6492
|
-
|
|
6493
|
-
|
|
6492
|
+
const target = change.target ?? existing.credential?.target;
|
|
6493
|
+
const cache = change.cache === void 0 ? existing.credential?.cache : change.cache === false ? void 0 : change.cache;
|
|
6494
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
6495
|
+
credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
|
|
6496
|
+
});
|
|
6494
6497
|
}
|
|
6495
|
-
function
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6498
|
+
function belongsToIdentity(entry, name) {
|
|
6499
|
+
return entry === name || entry === import_node_path16.default.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
6500
|
+
}
|
|
6501
|
+
function removeIdentity(paths, name) {
|
|
6502
|
+
if (!identityExists(paths, name)) {
|
|
6503
|
+
throw new IdentityNotFoundError(name);
|
|
6499
6504
|
}
|
|
6500
|
-
const
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
if (isCodexProvider(existing) && input.baseUrl !== void 0) {
|
|
6505
|
-
throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
|
|
6505
|
+
for (const entry of import_node_fs4.default.readdirSync(paths.identitiesDir)) {
|
|
6506
|
+
if (belongsToIdentity(entry, name)) {
|
|
6507
|
+
import_node_fs4.default.rmSync(import_node_path16.default.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
6508
|
+
}
|
|
6506
6509
|
}
|
|
6507
|
-
if (
|
|
6508
|
-
|
|
6510
|
+
if (readActiveIdentity(paths) === name) {
|
|
6511
|
+
import_node_fs4.default.rmSync(paths.activeIdentityFile);
|
|
6509
6512
|
}
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6513
|
+
}
|
|
6514
|
+
|
|
6515
|
+
// src/providersStore.ts
|
|
6516
|
+
var import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
6517
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
6518
|
+
|
|
6519
|
+
// src/credentialCache.ts
|
|
6520
|
+
var import_zod11 = require("zod");
|
|
6521
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
6522
|
+
var CachedCredentialSchema = import_zod11.z.strictObject({
|
|
6523
|
+
token: import_zod11.z.string().min(1),
|
|
6524
|
+
fetchedAt: import_zod11.z.number().int().nonnegative(),
|
|
6525
|
+
source: CredentialSourceSchema
|
|
6526
|
+
});
|
|
6527
|
+
function effectiveStore(cache, platform) {
|
|
6528
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
6529
|
+
}
|
|
6530
|
+
function ttlMs(cache) {
|
|
6531
|
+
if (cache.ttl === void 0) {
|
|
6532
|
+
return void 0;
|
|
6529
6533
|
}
|
|
6530
|
-
const
|
|
6531
|
-
if (
|
|
6532
|
-
throw new
|
|
6534
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
6535
|
+
if (multiplier === void 0) {
|
|
6536
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
6533
6537
|
}
|
|
6534
|
-
|
|
6535
|
-
return parsed.data;
|
|
6538
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
6536
6539
|
}
|
|
6537
|
-
function
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
+
function isFresh(entry, cache, now) {
|
|
6541
|
+
const limit = ttlMs(cache);
|
|
6542
|
+
if (entry.fetchedAt > now) {
|
|
6543
|
+
return false;
|
|
6540
6544
|
}
|
|
6541
|
-
|
|
6545
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
6542
6546
|
}
|
|
6543
|
-
function
|
|
6544
|
-
|
|
6545
|
-
for (const layer of assembleCascade(cascade).layers) {
|
|
6546
|
-
if (layer.launch?.provider !== void 0) {
|
|
6547
|
-
name = layer.launch.provider;
|
|
6548
|
-
}
|
|
6549
|
-
}
|
|
6550
|
-
return name;
|
|
6547
|
+
function cacheAgeMs(entry, now) {
|
|
6548
|
+
return Math.max(0, now - entry.fetchedAt);
|
|
6551
6549
|
}
|
|
6552
|
-
function
|
|
6553
|
-
const
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6550
|
+
function describeCachedCredential(params) {
|
|
6551
|
+
const store = effectiveStore(params.cache, params.platform);
|
|
6552
|
+
let entry;
|
|
6553
|
+
try {
|
|
6554
|
+
entry = params.port.read(store, params.owner);
|
|
6555
|
+
} catch (error) {
|
|
6556
|
+
return { store, status: "unreadable", reason: error instanceof Error ? error.message : String(error) };
|
|
6557
|
+
}
|
|
6558
|
+
if (entry === void 0) {
|
|
6559
|
+
return { store, status: "empty" };
|
|
6560
|
+
}
|
|
6561
|
+
const limit = ttlMs(params.cache);
|
|
6562
|
+
const ageMs = cacheAgeMs(entry, params.nowMs);
|
|
6563
|
+
return {
|
|
6564
|
+
store,
|
|
6565
|
+
status: isFresh(entry, params.cache, params.nowMs) ? "fresh" : "expired",
|
|
6566
|
+
ageMs,
|
|
6567
|
+
...limit === void 0 ? {} : { expiresInMs: limit - ageMs }
|
|
6568
|
+
};
|
|
6569
|
+
}
|
|
6570
|
+
function formatCachedCredentialState(state) {
|
|
6571
|
+
if (state.status === "empty") {
|
|
6572
|
+
return `no cached entry in the ${state.store} store yet`;
|
|
6557
6573
|
}
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
6561
|
-
return {
|
|
6562
|
-
ok: false,
|
|
6563
|
-
status: 1,
|
|
6564
|
-
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
6565
|
-
};
|
|
6574
|
+
if (state.status === "unreadable") {
|
|
6575
|
+
return `the ${state.store} store cannot be read here: ${state.reason}`;
|
|
6566
6576
|
}
|
|
6567
|
-
if (
|
|
6568
|
-
return
|
|
6577
|
+
if (state.status === "expired" && (state.expiresInMs === void 0 || state.expiresInMs > 0)) {
|
|
6578
|
+
return "cached with a timestamp in the future, treated as expired";
|
|
6569
6579
|
}
|
|
6570
|
-
const
|
|
6571
|
-
if (
|
|
6572
|
-
return {
|
|
6580
|
+
const age = `cached ${formatAge(state.ageMs)} ago`;
|
|
6581
|
+
if (state.expiresInMs === void 0) {
|
|
6582
|
+
return `${age}, no expiry`;
|
|
6573
6583
|
}
|
|
6574
|
-
return
|
|
6584
|
+
return state.status === "fresh" ? `${age}, expires in ${formatAge(state.expiresInMs)}` : `${age}, expired ${formatAge(-state.expiresInMs)} ago`;
|
|
6575
6585
|
}
|
|
6576
6586
|
|
|
6577
|
-
// src/
|
|
6578
|
-
var
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
constructor() {
|
|
6588
|
-
super("A directory rule must set at least one of --config-profile or --identity.");
|
|
6589
|
-
this.name = "DirectoryRuleMissingTargetError";
|
|
6587
|
+
// src/credential.ts
|
|
6588
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
6589
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
6590
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
6591
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
6592
|
+
var PERMISSION_BITS = 511;
|
|
6593
|
+
var OCTAL_RADIX2 = 8;
|
|
6594
|
+
function commandArgv(source) {
|
|
6595
|
+
if ("command" in source) {
|
|
6596
|
+
return source.command;
|
|
6590
6597
|
}
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
constructor(rulePath) {
|
|
6594
|
-
super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
|
|
6595
|
-
this.rulePath = rulePath;
|
|
6596
|
-
this.name = "DirectoryRuleAlreadyExistsError";
|
|
6598
|
+
if ("op" in source) {
|
|
6599
|
+
return ["op", "read", source.op];
|
|
6597
6600
|
}
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
function readDirectoryRules(paths) {
|
|
6601
|
-
return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
|
|
6601
|
+
const account = source.keychain.account;
|
|
6602
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
6602
6603
|
}
|
|
6603
|
-
function
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
|
|
6604
|
+
function isInteractiveSource(source, env) {
|
|
6605
|
+
if ("command" in source || "keychain" in source) {
|
|
6606
|
+
return source.interactive ?? false;
|
|
6607
6607
|
}
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
return
|
|
6608
|
+
if ("op" in source) {
|
|
6609
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
6610
|
+
}
|
|
6611
|
+
return false;
|
|
6612
6612
|
}
|
|
6613
|
-
function
|
|
6614
|
-
if (
|
|
6615
|
-
|
|
6613
|
+
function summariseSource(source) {
|
|
6614
|
+
if ("env" in source) {
|
|
6615
|
+
return { kind: "env", variable: source.env };
|
|
6616
6616
|
}
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
throw new DirectoryRuleAlreadyExistsError(rulePath);
|
|
6617
|
+
if ("file" in source) {
|
|
6618
|
+
return { kind: "file", path: source.file };
|
|
6620
6619
|
}
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
return created;
|
|
6624
|
-
}
|
|
6625
|
-
function ruleHasEffect(rule) {
|
|
6626
|
-
return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
|
|
6627
|
-
}
|
|
6628
|
-
function updateDirectoryRule(paths, rulePath, options) {
|
|
6629
|
-
const current = readDirectoryRules(paths);
|
|
6630
|
-
const index = current.rules.findIndex((rule) => rule.path === rulePath);
|
|
6631
|
-
const existing = index === -1 ? void 0 : current.rules[index];
|
|
6632
|
-
if (existing === void 0) {
|
|
6633
|
-
throw new DirectoryRuleNotFoundError(rulePath);
|
|
6620
|
+
if ("command" in source) {
|
|
6621
|
+
return { kind: "command", program: source.command[0] };
|
|
6634
6622
|
}
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
|
|
6638
|
-
const updated = {
|
|
6639
|
-
...rest,
|
|
6640
|
-
...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
|
|
6641
|
-
...nextIdentity === void 0 ? {} : { identity: nextIdentity }
|
|
6642
|
-
};
|
|
6643
|
-
if (!ruleHasEffect(updated)) {
|
|
6644
|
-
throw new DirectoryRuleMissingTargetError();
|
|
6623
|
+
if ("op" in source) {
|
|
6624
|
+
return { kind: "op", reference: source.op };
|
|
6645
6625
|
}
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6626
|
+
if ("keychain" in source) {
|
|
6627
|
+
return {
|
|
6628
|
+
kind: "keychain",
|
|
6629
|
+
service: source.keychain.service,
|
|
6630
|
+
...source.keychain.account === void 0 ? {} : { account: source.keychain.account }
|
|
6631
|
+
};
|
|
6632
|
+
}
|
|
6633
|
+
return { kind: "literal" };
|
|
6650
6634
|
}
|
|
6651
|
-
function
|
|
6635
|
+
function summariseCredential(credential) {
|
|
6652
6636
|
return {
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
...
|
|
6637
|
+
target: credential.target ?? "bearer",
|
|
6638
|
+
sources: credential.sources.map(summariseSource),
|
|
6639
|
+
...credential.cache === void 0 ? {} : { cache: credential.cache }
|
|
6656
6640
|
};
|
|
6657
6641
|
}
|
|
6658
|
-
function
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6642
|
+
function formatSourceSummary(summary) {
|
|
6643
|
+
switch (summary.kind) {
|
|
6644
|
+
case "env":
|
|
6645
|
+
return `env ${summary.variable}`;
|
|
6646
|
+
case "file":
|
|
6647
|
+
return `file ${summary.path}`;
|
|
6648
|
+
case "command":
|
|
6649
|
+
return `command ${summary.program}`;
|
|
6650
|
+
case "op":
|
|
6651
|
+
return `op ${summary.reference}`;
|
|
6652
|
+
case "keychain":
|
|
6653
|
+
return `keychain ${summary.service}${summary.account === void 0 ? "" : ` (account ${summary.account})`}`;
|
|
6654
|
+
case "literal":
|
|
6655
|
+
return "literal (non-secret placeholder)";
|
|
6656
|
+
default:
|
|
6657
|
+
return summary;
|
|
6663
6658
|
}
|
|
6664
|
-
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
6665
6659
|
}
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
6669
|
-
|
|
6670
|
-
// src/launcher/cascade.ts
|
|
6671
|
-
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
6672
|
-
|
|
6673
|
-
// src/pathNorm.ts
|
|
6674
|
-
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
6675
|
-
function expandTilde(value, home) {
|
|
6676
|
-
if (value === "~") {
|
|
6677
|
-
return home;
|
|
6678
|
-
}
|
|
6679
|
-
if (value.startsWith("~/")) {
|
|
6680
|
-
return import_node_path17.default.join(home, value.slice(2));
|
|
6681
|
-
}
|
|
6682
|
-
return value;
|
|
6660
|
+
function describeSource(source) {
|
|
6661
|
+
return formatSourceSummary(summariseSource(source));
|
|
6683
6662
|
}
|
|
6684
|
-
function
|
|
6685
|
-
|
|
6663
|
+
function formatCredentialSummary(summary) {
|
|
6664
|
+
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`}`;
|
|
6665
|
+
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
6686
6666
|
}
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6667
|
+
function describeCredential(credential) {
|
|
6668
|
+
return formatCredentialSummary(summariseCredential(credential));
|
|
6669
|
+
}
|
|
6670
|
+
function trySource(source, params) {
|
|
6671
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
6672
|
+
if ("env" in source) {
|
|
6673
|
+
const value = params.env[source.env];
|
|
6674
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
6675
|
+
}
|
|
6676
|
+
if ("literal" in source) {
|
|
6677
|
+
return { ok: true, token: source.literal };
|
|
6678
|
+
}
|
|
6679
|
+
if ("file" in source) {
|
|
6680
|
+
const read = params.port.readSecretFile(source.file);
|
|
6681
|
+
if (!read.found) {
|
|
6682
|
+
return none("does not exist");
|
|
6699
6683
|
}
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
6706
|
-
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
6707
|
-
});
|
|
6708
|
-
const levels = [];
|
|
6709
|
-
for (const dir of dirs) {
|
|
6710
|
-
const portable = loadConfigFile(import_node_path18.default.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path18.default.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6711
|
-
const portableLocal = loadConfigFile(import_node_path18.default.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path18.default.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
6712
|
-
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
6713
|
-
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
6714
|
-
return {
|
|
6715
|
-
rule,
|
|
6716
|
-
...entryOrder === void 0 ? {} : { entryOrder },
|
|
6717
|
-
filepath: params.paths.directoryRulesFile
|
|
6718
|
-
};
|
|
6719
|
-
});
|
|
6720
|
-
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
6721
|
-
continue;
|
|
6684
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
6685
|
+
return none(
|
|
6686
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX2)}); run \`chmod 600 ${source.file}\``,
|
|
6687
|
+
true
|
|
6688
|
+
);
|
|
6722
6689
|
}
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6690
|
+
const token2 = read.content.trim();
|
|
6691
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
6692
|
+
}
|
|
6693
|
+
const argv = commandArgv(source);
|
|
6694
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
6695
|
+
if (interactive && !params.port.personPresent()) {
|
|
6696
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
6697
|
+
}
|
|
6698
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
6699
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
6700
|
+
if (result.timedOut) {
|
|
6701
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
6702
|
+
}
|
|
6703
|
+
if (result.status !== 0) {
|
|
6704
|
+
const stderr = result.stderr.trim();
|
|
6705
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
6706
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
6729
6707
|
}
|
|
6730
|
-
const
|
|
6731
|
-
|
|
6732
|
-
...globalConfig === void 0 ? {} : { globalConfig: { config: globalConfig.config, entryOrder: globalConfig.entryOrder, filepath: globalConfig.filepath } },
|
|
6733
|
-
...params.baseConfigProfile === void 0 ? {} : { baseConfigProfile: params.baseConfigProfile },
|
|
6734
|
-
loadProfile,
|
|
6735
|
-
levels,
|
|
6736
|
-
...params.cliOverride === void 0 ? {} : { cliOverride: params.cliOverride }
|
|
6737
|
-
};
|
|
6738
|
-
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
6708
|
+
const token = result.stdout.trim();
|
|
6709
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
6739
6710
|
}
|
|
6740
|
-
function
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6711
|
+
function warmCommandFor(subject) {
|
|
6712
|
+
const [kind, name] = subject.split(" ");
|
|
6713
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
6714
|
+
}
|
|
6715
|
+
function resolveCredential(params) {
|
|
6716
|
+
const target = params.credential.target ?? "bearer";
|
|
6717
|
+
const cacheBlock = params.credential.cache;
|
|
6718
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
6719
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
6720
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
6721
|
+
const entry = cache.port.read(store, params.subject);
|
|
6722
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
6723
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
6724
|
+
}
|
|
6725
|
+
}
|
|
6726
|
+
const attempts = [];
|
|
6727
|
+
const warnings = [];
|
|
6728
|
+
for (const source of params.credential.sources) {
|
|
6729
|
+
const outcome = trySource(source, params);
|
|
6730
|
+
if (outcome.ok) {
|
|
6731
|
+
if (cache !== void 0 && store !== void 0) {
|
|
6732
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
6753
6733
|
}
|
|
6734
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
6735
|
+
}
|
|
6736
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
6737
|
+
attempts.push(line);
|
|
6738
|
+
if (outcome.warn) {
|
|
6739
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
6754
6740
|
}
|
|
6755
6741
|
}
|
|
6756
|
-
|
|
6742
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
6743
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
6744
|
+
}
|
|
6745
|
+
function credentialVariables(target, token) {
|
|
6746
|
+
return {
|
|
6747
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
6748
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
6749
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
6750
|
+
};
|
|
6757
6751
|
}
|
|
6758
6752
|
|
|
6759
|
-
// src/
|
|
6760
|
-
var
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
var ZERO_TIER = /(?:^|_)zero$/;
|
|
6766
|
-
var BASE_TIER = /(?:^|_)(?:pro|free)$/;
|
|
6767
|
-
function planOf(account) {
|
|
6768
|
-
const tier = account?.organizationRateLimitTier ?? account?.userRateLimitTier;
|
|
6769
|
-
const tierField = tier === void 0 ? {} : { tier };
|
|
6770
|
-
if (tier === void 0) {
|
|
6771
|
-
return { kind: "subscription", capacity: 1, recognised: false };
|
|
6753
|
+
// src/providersStore.ts
|
|
6754
|
+
var ProviderNotFoundError = class extends CliError {
|
|
6755
|
+
constructor(name) {
|
|
6756
|
+
super(`No provider named "${name}". Run \`agent-shim provider add ${name}\` first.`);
|
|
6757
|
+
this.name = name;
|
|
6758
|
+
this.name = "ProviderNotFoundError";
|
|
6772
6759
|
}
|
|
6773
|
-
|
|
6774
|
-
|
|
6760
|
+
name;
|
|
6761
|
+
};
|
|
6762
|
+
var ProviderAlreadyExistsError = class extends CliError {
|
|
6763
|
+
constructor(providerName) {
|
|
6764
|
+
super(`A provider named "${providerName}" already exists.`);
|
|
6765
|
+
this.providerName = providerName;
|
|
6766
|
+
this.name = "ProviderAlreadyExistsError";
|
|
6775
6767
|
}
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6768
|
+
providerName;
|
|
6769
|
+
};
|
|
6770
|
+
var InvalidProviderNameError = class extends CliError {
|
|
6771
|
+
constructor(attemptedName) {
|
|
6772
|
+
super(
|
|
6773
|
+
`"${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.`
|
|
6774
|
+
);
|
|
6775
|
+
this.attemptedName = attemptedName;
|
|
6776
|
+
this.name = "InvalidProviderNameError";
|
|
6779
6777
|
}
|
|
6780
|
-
|
|
6778
|
+
attemptedName;
|
|
6779
|
+
};
|
|
6780
|
+
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
6781
|
+
function providerJsonPath(paths, name) {
|
|
6782
|
+
return import_node_path17.default.join(paths.providersDir, `${name}.json`);
|
|
6781
6783
|
}
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
var MS_PER_SECOND3 = 1e3;
|
|
6785
|
-
function epochSecondsToIso(value) {
|
|
6786
|
-
if (value === void 0) {
|
|
6787
|
-
return void 0;
|
|
6788
|
-
}
|
|
6789
|
-
const seconds = Number(value);
|
|
6790
|
-
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
6784
|
+
function providerExists(paths, name) {
|
|
6785
|
+
return import_node_fs5.default.existsSync(providerJsonPath(paths, name));
|
|
6791
6786
|
}
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
6787
|
+
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
6788
|
+
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
6789
|
+
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
6790
|
+
function isRecord3(value) {
|
|
6791
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6798
6792
|
}
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
6802
|
-
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
6803
|
-
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
6804
|
-
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
6793
|
+
function legacyProviderConversion(raw) {
|
|
6794
|
+
if (!isRecord3(raw) || "credential" in raw) {
|
|
6805
6795
|
return void 0;
|
|
6806
6796
|
}
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
if (
|
|
6797
|
+
const env = isRecord3(raw.env) ? raw.env : void 0;
|
|
6798
|
+
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
6799
|
+
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
6800
|
+
if (fields.length === 0) {
|
|
6811
6801
|
return void 0;
|
|
6812
6802
|
}
|
|
6813
|
-
const
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6803
|
+
const sources = [];
|
|
6804
|
+
if (typeof raw.tokenEnv === "string") {
|
|
6805
|
+
sources.push({ env: raw.tokenEnv });
|
|
6806
|
+
}
|
|
6807
|
+
if (Array.isArray(raw.tokenCommand)) {
|
|
6808
|
+
sources.push({ command: raw.tokenCommand });
|
|
6809
|
+
}
|
|
6810
|
+
if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
|
|
6811
|
+
sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
|
|
6812
|
+
}
|
|
6813
|
+
const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
|
|
6814
|
+
const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
|
|
6815
|
+
const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
|
|
6819
6816
|
return {
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6817
|
+
fields,
|
|
6818
|
+
replacement: {
|
|
6819
|
+
...rest,
|
|
6820
|
+
credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
|
|
6821
|
+
...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
|
|
6822
|
+
}
|
|
6826
6823
|
};
|
|
6827
6824
|
}
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
var ErrorObjectSchema = import_zod8.z.looseObject({
|
|
6838
|
-
type: import_zod8.z.string().optional(),
|
|
6839
|
-
code: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.number()]).optional(),
|
|
6840
|
-
message: import_zod8.z.string().optional()
|
|
6841
|
-
});
|
|
6842
|
-
|
|
6843
|
-
// src/usage/middleware.ts
|
|
6844
|
-
var ANTHROPIC_PROVIDER = "anthropic";
|
|
6845
|
-
|
|
6846
|
-
// src/usage/pick.ts
|
|
6847
|
-
var MS_PER_SECOND4 = 1e3;
|
|
6848
|
-
var MS_PER_HOUR = 36e5;
|
|
6849
|
-
var HOURS_PER_DAY2 = 24;
|
|
6850
|
-
var FIVE_HOURS = 5;
|
|
6851
|
-
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
6852
|
-
var PERCENT = 100;
|
|
6853
|
-
var SEVEN_DAYS = 7;
|
|
6854
|
-
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
6855
|
-
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
6856
|
-
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
6857
|
-
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
6858
|
-
function unifiedOf(state) {
|
|
6859
|
-
return state?.rateLimit?.unified;
|
|
6860
|
-
}
|
|
6861
|
-
function limitBlockedUntil(state, nowMs) {
|
|
6862
|
-
const limit = state?.lastLimit;
|
|
6863
|
-
if (state === void 0 || limit === void 0) {
|
|
6864
|
-
return void 0;
|
|
6825
|
+
var LegacyProviderFileError = class extends CliError {
|
|
6826
|
+
constructor(filePath, conversion) {
|
|
6827
|
+
super(
|
|
6828
|
+
`${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
|
|
6829
|
+
` + JSON.stringify(conversion.replacement, null, 2)
|
|
6830
|
+
);
|
|
6831
|
+
this.filePath = filePath;
|
|
6832
|
+
this.conversion = conversion;
|
|
6833
|
+
this.name = "LegacyProviderFileError";
|
|
6865
6834
|
}
|
|
6866
|
-
|
|
6835
|
+
filePath;
|
|
6836
|
+
conversion;
|
|
6837
|
+
};
|
|
6838
|
+
function parseProviderFile(filePath, raw) {
|
|
6839
|
+
const conversion = legacyProviderConversion(raw);
|
|
6840
|
+
if (conversion !== void 0) {
|
|
6841
|
+
throw new LegacyProviderFileError(filePath, conversion);
|
|
6842
|
+
}
|
|
6843
|
+
const parsed = ProviderSchema.safeParse(raw);
|
|
6844
|
+
if (!parsed.success) {
|
|
6845
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
6846
|
+
}
|
|
6847
|
+
return parsed.data;
|
|
6848
|
+
}
|
|
6849
|
+
function readProvider(paths, name) {
|
|
6850
|
+
const filePath = providerJsonPath(paths, name);
|
|
6851
|
+
if (!import_node_fs5.default.existsSync(filePath)) {
|
|
6867
6852
|
return void 0;
|
|
6868
6853
|
}
|
|
6869
|
-
const
|
|
6870
|
-
return
|
|
6854
|
+
const raw = JSON.parse(import_node_fs5.default.readFileSync(filePath, "utf8"));
|
|
6855
|
+
return parseProviderFile(filePath, raw);
|
|
6871
6856
|
}
|
|
6872
|
-
function
|
|
6873
|
-
const
|
|
6874
|
-
|
|
6857
|
+
function loadProvider(providersDir, name, port) {
|
|
6858
|
+
const filePath = import_node_path17.default.join(providersDir, `${name}.json`);
|
|
6859
|
+
const raw = port.readConfigFile(filePath);
|
|
6860
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
6875
6861
|
}
|
|
6876
|
-
function
|
|
6877
|
-
const
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6862
|
+
function listProviders(paths) {
|
|
6863
|
+
const names = listProviderNames(
|
|
6864
|
+
(dir) => import_node_fs5.default.readdirSync(dir),
|
|
6865
|
+
paths.providersDir
|
|
6866
|
+
);
|
|
6867
|
+
const result = [];
|
|
6868
|
+
for (const name of names) {
|
|
6869
|
+
let provider;
|
|
6870
|
+
try {
|
|
6871
|
+
provider = readProvider(paths, name);
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
|
|
6874
|
+
throw error;
|
|
6875
|
+
}
|
|
6876
|
+
console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
|
|
6877
|
+
continue;
|
|
6878
|
+
}
|
|
6879
|
+
if (provider !== void 0) {
|
|
6880
|
+
result.push({ name, provider });
|
|
6882
6881
|
}
|
|
6883
6882
|
}
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6883
|
+
return result;
|
|
6884
|
+
}
|
|
6885
|
+
function listProviderNames(readdir, providersDir) {
|
|
6886
|
+
let entries;
|
|
6887
|
+
try {
|
|
6888
|
+
entries = readdir(providersDir);
|
|
6889
|
+
} catch {
|
|
6890
|
+
return [];
|
|
6890
6891
|
}
|
|
6891
|
-
return (
|
|
6892
|
+
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
|
|
6892
6893
|
}
|
|
6893
|
-
function
|
|
6894
|
-
return
|
|
6894
|
+
function describeProviderEndpoint(provider) {
|
|
6895
|
+
return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
|
|
6895
6896
|
}
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6897
|
+
var ProviderKindMismatchError = class extends UsageError {
|
|
6898
|
+
constructor(message) {
|
|
6899
|
+
super(message);
|
|
6900
|
+
this.name = "ProviderKindMismatchError";
|
|
6899
6901
|
}
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
return
|
|
6903
|
-
}
|
|
6904
|
-
function capacityOf(plan) {
|
|
6905
|
-
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
6902
|
+
};
|
|
6903
|
+
function isEmptyCodexConfig(config) {
|
|
6904
|
+
return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
|
|
6906
6905
|
}
|
|
6907
|
-
function
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
const base = { identity: member.identity, plan };
|
|
6911
|
-
if (member.readError !== void 0) {
|
|
6912
|
-
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
6906
|
+
function addProvider(paths, name, input) {
|
|
6907
|
+
if (providerExists(paths, name)) {
|
|
6908
|
+
throw new ProviderAlreadyExistsError(name);
|
|
6913
6909
|
}
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
6917
|
-
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
6910
|
+
if (!PROVIDER_NAME_RE.test(name)) {
|
|
6911
|
+
throw new InvalidProviderNameError(name);
|
|
6918
6912
|
}
|
|
6919
|
-
const
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
6923
|
-
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
6924
|
-
const windowReasons = [
|
|
6925
|
-
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
6926
|
-
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
6927
|
-
];
|
|
6928
|
-
const blockedUntil = Math.max(
|
|
6929
|
-
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
6930
|
-
limitBlockedUntil(state, nowMs) ?? 0
|
|
6931
|
-
);
|
|
6932
|
-
if (blockedUntil > nowMs) {
|
|
6933
|
-
if (unified.overageStatus === "allowed") {
|
|
6934
|
-
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
6935
|
-
}
|
|
6936
|
-
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
6913
|
+
const kind = input.kind ?? "http";
|
|
6914
|
+
if (kind === "codex" && input.baseUrl !== void 0) {
|
|
6915
|
+
throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
|
|
6937
6916
|
}
|
|
6938
|
-
if (
|
|
6939
|
-
|
|
6917
|
+
if (kind === "http" && input.baseUrl === void 0) {
|
|
6918
|
+
throw new ProviderKindMismatchError("An http provider needs --base-url.");
|
|
6940
6919
|
}
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
6920
|
+
if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
|
|
6921
|
+
throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
|
|
6944
6922
|
}
|
|
6945
|
-
const
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
6950
|
-
const burn = fiveHourBurn(member.records);
|
|
6951
|
-
return {
|
|
6952
|
-
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
6953
|
-
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
6954
|
-
...burn === void 0 ? {} : { burn }
|
|
6923
|
+
const shared = {
|
|
6924
|
+
displayName: input.displayName,
|
|
6925
|
+
credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
|
|
6926
|
+
...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
|
|
6955
6927
|
};
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
const { members, nowMs } = input;
|
|
6959
|
-
const assessments = members.map((member) => assess(member, nowMs));
|
|
6960
|
-
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
6961
|
-
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
6962
|
-
const candidates = assessments.map((assessment) => {
|
|
6963
|
-
const { candidate, fiveHour, burn } = assessment;
|
|
6964
|
-
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
6965
|
-
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
6966
|
-
return { ...candidate, feasible: true };
|
|
6967
|
-
}
|
|
6968
|
-
const runsDryInMs = fiveHour.remaining / pace;
|
|
6969
|
-
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
6970
|
-
return { ...candidate, feasible: true };
|
|
6971
|
-
}
|
|
6972
|
-
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
6973
|
-
});
|
|
6974
|
-
const ordered = [...candidates].sort(
|
|
6975
|
-
(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)
|
|
6928
|
+
const parsed = ProviderSchema.safeParse(
|
|
6929
|
+
kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
|
|
6976
6930
|
);
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
const stickyCandidate = ordered[stickyIndex];
|
|
6980
|
-
if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
|
|
6981
|
-
const ageMs = nowMs - Date.parse(sticky.at);
|
|
6982
|
-
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
6983
|
-
if (input.resuming || warm) {
|
|
6984
|
-
const why = warm ? `used here ${formatAge(ageMs)} ago, prompt cache still warm` : "resuming a conversation started on this account";
|
|
6985
|
-
ordered.splice(stickyIndex, 1);
|
|
6986
|
-
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
6987
|
-
}
|
|
6931
|
+
if (!parsed.success) {
|
|
6932
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6988
6933
|
}
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6934
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
6935
|
+
return parsed.data;
|
|
6936
|
+
}
|
|
6937
|
+
function updateProvider(paths, name, input) {
|
|
6938
|
+
const existing = readProvider(paths, name);
|
|
6939
|
+
if (existing === void 0) {
|
|
6940
|
+
throw new ProviderNotFoundError(name);
|
|
6941
|
+
}
|
|
6942
|
+
const unset = new Set(input.unsetEnv);
|
|
6943
|
+
const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
|
|
6944
|
+
const target = input.target ?? existing.credential.target;
|
|
6945
|
+
const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
|
|
6946
|
+
if (isCodexProvider(existing) && input.baseUrl !== void 0) {
|
|
6947
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
|
|
6948
|
+
}
|
|
6949
|
+
if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
|
|
6950
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
|
|
6951
|
+
}
|
|
6952
|
+
const shared = {
|
|
6953
|
+
...input.displayName === void 0 ? {} : { displayName: input.displayName },
|
|
6954
|
+
credential: {
|
|
6955
|
+
sources: input.sources ?? existing.credential.sources,
|
|
6956
|
+
...target === void 0 ? {} : { target },
|
|
6957
|
+
...cache === void 0 ? {} : { cache }
|
|
6958
|
+
},
|
|
6959
|
+
env: Object.keys(env).length === 0 ? void 0 : env
|
|
6995
6960
|
};
|
|
6961
|
+
let candidate;
|
|
6962
|
+
if (isCodexProvider(existing)) {
|
|
6963
|
+
const codex = {
|
|
6964
|
+
...existing.codex,
|
|
6965
|
+
...input.codex,
|
|
6966
|
+
...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
|
|
6967
|
+
};
|
|
6968
|
+
candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
|
|
6969
|
+
} else {
|
|
6970
|
+
candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
|
|
6971
|
+
}
|
|
6972
|
+
const parsed = ProviderSchema.safeParse(candidate);
|
|
6973
|
+
if (!parsed.success) {
|
|
6974
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
6975
|
+
}
|
|
6976
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
6977
|
+
return parsed.data;
|
|
6996
6978
|
}
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
// src/usage/poolPick.ts
|
|
7004
|
-
var SCHEMA_VERSION = 1;
|
|
7005
|
-
var PickEntrySchema = import_zod9.z.strictObject({ identity: import_zod9.z.string().min(1), at: import_zod9.z.iso.datetime() });
|
|
7006
|
-
var PicksFileSchema = import_zod9.z.strictObject({
|
|
7007
|
-
schemaVersion: import_zod9.z.literal(SCHEMA_VERSION),
|
|
7008
|
-
picks: import_zod9.z.record(import_zod9.z.string().min(1), PickEntrySchema)
|
|
7009
|
-
});
|
|
7010
|
-
function errorMessage(error) {
|
|
7011
|
-
return error instanceof Error ? error.message : String(error);
|
|
6979
|
+
function removeProvider(paths, name) {
|
|
6980
|
+
if (!providerExists(paths, name)) {
|
|
6981
|
+
throw new ProviderNotFoundError(name);
|
|
6982
|
+
}
|
|
6983
|
+
import_node_fs5.default.rmSync(providerJsonPath(paths, name));
|
|
7012
6984
|
}
|
|
7013
|
-
function
|
|
7014
|
-
|
|
7015
|
-
const
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
try {
|
|
7019
|
-
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
7020
|
-
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
7021
|
-
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
7022
|
-
} catch (error) {
|
|
7023
|
-
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
7024
|
-
return { identity, records, readError: errorMessage(error) };
|
|
7025
|
-
}
|
|
7026
|
-
throw error;
|
|
6985
|
+
function cascadeProviderName(cascade) {
|
|
6986
|
+
let name;
|
|
6987
|
+
for (const layer of assembleCascade(cascade).layers) {
|
|
6988
|
+
if (layer.launch?.provider !== void 0) {
|
|
6989
|
+
name = layer.launch.provider;
|
|
7027
6990
|
}
|
|
7028
|
-
}
|
|
6991
|
+
}
|
|
6992
|
+
return name;
|
|
7029
6993
|
}
|
|
7030
|
-
function
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
6994
|
+
function resolveProvider(params) {
|
|
6995
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
6996
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
6997
|
+
if (name === void 0) {
|
|
6998
|
+
return void 0;
|
|
6999
|
+
}
|
|
7000
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
7001
|
+
if (definition === void 0) {
|
|
7002
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
7003
|
+
return {
|
|
7004
|
+
ok: false,
|
|
7005
|
+
status: 1,
|
|
7006
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
7007
|
+
};
|
|
7008
|
+
}
|
|
7009
|
+
if (params.credentials === void 0) {
|
|
7010
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
7011
|
+
}
|
|
7012
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
7013
|
+
if (!resolution.ok) {
|
|
7014
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
7015
|
+
}
|
|
7016
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
7037
7017
|
}
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7018
|
+
|
|
7019
|
+
// src/directoryRulesStore.ts
|
|
7020
|
+
var DirectoryRuleNotFoundError = class extends CliError {
|
|
7021
|
+
constructor(rulePath) {
|
|
7022
|
+
super(`No directory rule found for path "${rulePath}".`);
|
|
7023
|
+
this.rulePath = rulePath;
|
|
7024
|
+
this.name = "DirectoryRuleNotFoundError";
|
|
7043
7025
|
}
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7026
|
+
rulePath;
|
|
7027
|
+
};
|
|
7028
|
+
var DirectoryRuleMissingTargetError = class extends CliError {
|
|
7029
|
+
constructor() {
|
|
7030
|
+
super("A directory rule must set at least one of --config-profile or --identity.");
|
|
7031
|
+
this.name = "DirectoryRuleMissingTargetError";
|
|
7049
7032
|
}
|
|
7050
|
-
|
|
7051
|
-
|
|
7033
|
+
};
|
|
7034
|
+
var DirectoryRuleAlreadyExistsError = class extends CliError {
|
|
7035
|
+
constructor(rulePath) {
|
|
7036
|
+
super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
|
|
7037
|
+
this.rulePath = rulePath;
|
|
7038
|
+
this.name = "DirectoryRuleAlreadyExistsError";
|
|
7039
|
+
}
|
|
7040
|
+
rulePath;
|
|
7041
|
+
};
|
|
7042
|
+
function readDirectoryRules(paths) {
|
|
7043
|
+
return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
|
|
7052
7044
|
}
|
|
7053
|
-
function
|
|
7054
|
-
const
|
|
7055
|
-
|
|
7056
|
-
|
|
7045
|
+
function writeDirectoryRules(paths, rules) {
|
|
7046
|
+
const parsed = DirectoryRulesSchema.safeParse(rules);
|
|
7047
|
+
if (!parsed.success) {
|
|
7048
|
+
throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
|
|
7049
|
+
}
|
|
7050
|
+
writeJsonAtomic(paths.directoryRulesFile, parsed.data);
|
|
7057
7051
|
}
|
|
7058
|
-
function
|
|
7059
|
-
|
|
7060
|
-
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
7061
|
-
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
7062
|
-
fs12.mkdirPrivate(import_node_path19.default.dirname(picksFile));
|
|
7063
|
-
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
7064
|
-
`);
|
|
7052
|
+
function listDirectoryRules(paths) {
|
|
7053
|
+
return readDirectoryRules(paths).rules;
|
|
7065
7054
|
}
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7055
|
+
function addDirectoryRule(paths, rulePath, options) {
|
|
7056
|
+
if (options.configProfile === void 0 && options.identity === void 0) {
|
|
7057
|
+
throw new DirectoryRuleMissingTargetError();
|
|
7058
|
+
}
|
|
7059
|
+
const current = readDirectoryRules(paths);
|
|
7060
|
+
if (current.rules.some((rule) => rule.path === rulePath)) {
|
|
7061
|
+
throw new DirectoryRuleAlreadyExistsError(rulePath);
|
|
7062
|
+
}
|
|
7063
|
+
const created = buildNewRule(rulePath, options);
|
|
7064
|
+
writeDirectoryRules(paths, { ...current, rules: [...current.rules, created] });
|
|
7065
|
+
return created;
|
|
7074
7066
|
}
|
|
7075
|
-
function
|
|
7076
|
-
|
|
7077
|
-
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
7067
|
+
function ruleHasEffect(rule) {
|
|
7068
|
+
return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
|
|
7078
7069
|
}
|
|
7079
|
-
function
|
|
7080
|
-
const
|
|
7081
|
-
const
|
|
7082
|
-
|
|
7083
|
-
|
|
7070
|
+
function updateDirectoryRule(paths, rulePath, options) {
|
|
7071
|
+
const current = readDirectoryRules(paths);
|
|
7072
|
+
const index = current.rules.findIndex((rule) => rule.path === rulePath);
|
|
7073
|
+
const existing = index === -1 ? void 0 : current.rules[index];
|
|
7074
|
+
if (existing === void 0) {
|
|
7075
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
7084
7076
|
}
|
|
7085
|
-
const {
|
|
7086
|
-
|
|
7087
|
-
|
|
7077
|
+
const { configProfile, identity, ...rest } = existing;
|
|
7078
|
+
const nextConfigProfile = options.configProfile === void 0 ? configProfile : options.configProfile === false ? void 0 : options.configProfile;
|
|
7079
|
+
const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
|
|
7080
|
+
const updated = {
|
|
7081
|
+
...rest,
|
|
7082
|
+
...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
|
|
7083
|
+
...nextIdentity === void 0 ? {} : { identity: nextIdentity }
|
|
7084
|
+
};
|
|
7085
|
+
if (!ruleHasEffect(updated)) {
|
|
7086
|
+
throw new DirectoryRuleMissingTargetError();
|
|
7088
7087
|
}
|
|
7089
|
-
|
|
7090
|
-
|
|
7088
|
+
const nextRules = [...current.rules];
|
|
7089
|
+
nextRules[index] = updated;
|
|
7090
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
7091
|
+
return updated;
|
|
7092
|
+
}
|
|
7093
|
+
function buildNewRule(rulePath, options) {
|
|
7094
|
+
return {
|
|
7095
|
+
path: rulePath,
|
|
7096
|
+
...options.configProfile !== void 0 ? { configProfile: options.configProfile } : {},
|
|
7097
|
+
...options.identity !== void 0 ? { identity: options.identity } : {}
|
|
7098
|
+
};
|
|
7099
|
+
}
|
|
7100
|
+
function removeDirectoryRule(paths, rulePath) {
|
|
7101
|
+
const current = readDirectoryRules(paths);
|
|
7102
|
+
const nextRules = current.rules.filter((rule) => rule.path !== rulePath);
|
|
7103
|
+
if (nextRules.length === current.rules.length) {
|
|
7104
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
7091
7105
|
}
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7106
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
7107
|
+
}
|
|
7108
|
+
|
|
7109
|
+
// src/checkReport.ts
|
|
7110
|
+
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
7111
|
+
|
|
7112
|
+
// src/launcher/cascade.ts
|
|
7113
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
7114
|
+
|
|
7115
|
+
// src/pathNorm.ts
|
|
7116
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
7117
|
+
function expandTilde(value, home) {
|
|
7118
|
+
if (value === "~") {
|
|
7119
|
+
return home;
|
|
7096
7120
|
}
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7121
|
+
if (value.startsWith("~/")) {
|
|
7122
|
+
return import_node_path18.default.join(home, value.slice(2));
|
|
7123
|
+
}
|
|
7124
|
+
return value;
|
|
7125
|
+
}
|
|
7126
|
+
function normaliseRulePath(rulePath, home) {
|
|
7127
|
+
return import_node_path18.default.resolve(expandTilde(rulePath, home));
|
|
7128
|
+
}
|
|
7129
|
+
|
|
7130
|
+
// src/launcher/cascade.ts
|
|
7131
|
+
var PORTABLE_CONFIG_FILENAME = ".agent-shim.json";
|
|
7132
|
+
var PORTABLE_LOCAL_CONFIG_FILENAME = ".agent-shim.local.json";
|
|
7133
|
+
function loadCascadeInput(params) {
|
|
7134
|
+
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
7135
|
+
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
7136
|
+
const loadProfile = (name) => {
|
|
7137
|
+
const filepath = import_node_path19.default.join(params.paths.configProfilesDir, `${name}.json`);
|
|
7138
|
+
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
7139
|
+
if (loaded === void 0) {
|
|
7140
|
+
return void 0;
|
|
7107
7141
|
}
|
|
7108
|
-
|
|
7109
|
-
|
|
7142
|
+
return { name, profile: loaded.config, entryOrder: loaded.entryOrder, filepath: loaded.filepath };
|
|
7143
|
+
};
|
|
7144
|
+
const limit = globalConfig?.config.walkUpLimit;
|
|
7145
|
+
const dirs = walkDirectoryAncestors(params.cwd, {
|
|
7146
|
+
home: params.home,
|
|
7147
|
+
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
7148
|
+
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
7149
|
+
});
|
|
7150
|
+
const levels = [];
|
|
7151
|
+
for (const dir of dirs) {
|
|
7152
|
+
const portable = loadConfigFile(import_node_path19.default.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path19.default.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7153
|
+
const portableLocal = loadConfigFile(import_node_path19.default.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path19.default.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7154
|
+
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
7155
|
+
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
7110
7156
|
return {
|
|
7111
|
-
|
|
7112
|
-
|
|
7157
|
+
rule,
|
|
7158
|
+
...entryOrder === void 0 ? {} : { entryOrder },
|
|
7159
|
+
filepath: params.paths.directoryRulesFile
|
|
7113
7160
|
};
|
|
7161
|
+
});
|
|
7162
|
+
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
7163
|
+
continue;
|
|
7114
7164
|
}
|
|
7115
|
-
|
|
7116
|
-
|
|
7165
|
+
levels.push({
|
|
7166
|
+
dir,
|
|
7167
|
+
...portable === void 0 ? {} : { portable: { config: portable.config, entryOrder: portable.entryOrder, filepath: portable.filepath } },
|
|
7168
|
+
...rules.length === 0 ? {} : { rules },
|
|
7169
|
+
...portableLocal === void 0 ? {} : { portableLocal: { config: portableLocal.config, entryOrder: portableLocal.entryOrder, filepath: portableLocal.filepath } }
|
|
7170
|
+
});
|
|
7117
7171
|
}
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
...candidate.score === void 0 ? {} : { score: candidate.score },
|
|
7126
|
-
feasible: candidate.feasible,
|
|
7127
|
-
...candidate.blockedUntilMs === void 0 ? {} : { blockedUntil: new Date(candidate.blockedUntilMs).toISOString() },
|
|
7128
|
-
plan: candidate.plan,
|
|
7129
|
-
reasons: candidate.reasons
|
|
7172
|
+
const input = {
|
|
7173
|
+
home: params.home,
|
|
7174
|
+
...globalConfig === void 0 ? {} : { globalConfig: { config: globalConfig.config, entryOrder: globalConfig.entryOrder, filepath: globalConfig.filepath } },
|
|
7175
|
+
...params.baseConfigProfile === void 0 ? {} : { baseConfigProfile: params.baseConfigProfile },
|
|
7176
|
+
loadProfile,
|
|
7177
|
+
levels,
|
|
7178
|
+
...params.cliOverride === void 0 ? {} : { cliOverride: params.cliOverride }
|
|
7130
7179
|
};
|
|
7180
|
+
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
7131
7181
|
}
|
|
7132
|
-
function
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
const
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
7150
|
-
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
7151
|
-
};
|
|
7182
|
+
function readDirectorySelections(loaded) {
|
|
7183
|
+
let identity;
|
|
7184
|
+
let configProfile;
|
|
7185
|
+
for (const level of loaded.input.levels ?? []) {
|
|
7186
|
+
for (const source of [level.portable?.config, ...(level.rules ?? []).map((entry) => entry.rule), level.portableLocal?.config]) {
|
|
7187
|
+
if (source === void 0) {
|
|
7188
|
+
continue;
|
|
7189
|
+
}
|
|
7190
|
+
if (source.identity !== void 0) {
|
|
7191
|
+
identity = source.identity;
|
|
7192
|
+
}
|
|
7193
|
+
if (source.configProfile !== void 0) {
|
|
7194
|
+
configProfile = source.configProfile;
|
|
7195
|
+
}
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
7198
|
+
return { ...identity === void 0 ? {} : { identity }, ...configProfile === void 0 ? {} : { configProfile } };
|
|
7152
7199
|
}
|
|
7153
7200
|
|
|
7154
7201
|
// src/realCredentialCache.ts
|
|
@@ -7711,7 +7758,7 @@ function realResolveClaudeBinary(isOwnBinary) {
|
|
|
7711
7758
|
|
|
7712
7759
|
// src/checkReport.ts
|
|
7713
7760
|
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
7714
|
-
var
|
|
7761
|
+
var import_zod12 = require("zod");
|
|
7715
7762
|
|
|
7716
7763
|
// src/cli/bool.ts
|
|
7717
7764
|
function parseBoolWord(input) {
|
|
@@ -7826,9 +7873,9 @@ function lookupKeychainService(run, farmRoot) {
|
|
|
7826
7873
|
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}".`
|
|
7827
7874
|
};
|
|
7828
7875
|
}
|
|
7829
|
-
var SettingsSecretsShapeSchema =
|
|
7830
|
-
env:
|
|
7831
|
-
hooks:
|
|
7876
|
+
var SettingsSecretsShapeSchema = import_zod12.z.looseObject({
|
|
7877
|
+
env: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.unknown()).optional(),
|
|
7878
|
+
hooks: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.array(import_zod12.z.unknown())).optional()
|
|
7832
7879
|
});
|
|
7833
7880
|
function hookCommandCountOf(group) {
|
|
7834
7881
|
if (typeof group !== "object" || group === null) {
|
|
@@ -8122,11 +8169,11 @@ function collectCheckReport(params) {
|
|
|
8122
8169
|
// src/claudeShim.ts
|
|
8123
8170
|
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
8124
8171
|
var import_node_path24 = __toESM(require("node:path"), 1);
|
|
8125
|
-
var
|
|
8126
|
-
var ClaudeShimStateSchema =
|
|
8127
|
-
targetPath:
|
|
8128
|
-
method:
|
|
8129
|
-
installedAtMs:
|
|
8172
|
+
var import_zod13 = require("zod");
|
|
8173
|
+
var ClaudeShimStateSchema = import_zod13.z.strictObject({
|
|
8174
|
+
targetPath: import_zod13.z.string().min(1),
|
|
8175
|
+
method: import_zod13.z.enum(["hardlink", "copy"]),
|
|
8176
|
+
installedAtMs: import_zod13.z.number()
|
|
8130
8177
|
});
|
|
8131
8178
|
function commandFilename(ownExecutablePath, commandName) {
|
|
8132
8179
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
@@ -9582,8 +9629,11 @@ function prepareClaudeLaunch(options) {
|
|
|
9582
9629
|
InvalidProviderNameError,
|
|
9583
9630
|
LaunchRefusedError,
|
|
9584
9631
|
LegacyProviderFileError,
|
|
9632
|
+
PROMPT_CACHE_TTL_MS,
|
|
9633
|
+
PlanClassSchema,
|
|
9585
9634
|
PoolNameSchema,
|
|
9586
9635
|
PoolNotFoundError,
|
|
9636
|
+
PoolPickReportSchema,
|
|
9587
9637
|
PoolSchema,
|
|
9588
9638
|
PortableConfigSchema,
|
|
9589
9639
|
ProfileAlreadyExistsError,
|
|
@@ -9620,16 +9670,19 @@ function prepareClaudeLaunch(options) {
|
|
|
9620
9670
|
checkReportToJson,
|
|
9621
9671
|
collectCheckReport,
|
|
9622
9672
|
collectDoctorReport,
|
|
9673
|
+
collectPoolPick,
|
|
9623
9674
|
createLeafCache,
|
|
9624
9675
|
createProfile,
|
|
9625
9676
|
createRcControlHandler,
|
|
9626
9677
|
createRcSessionTracker,
|
|
9627
9678
|
describeProviderEndpoint,
|
|
9628
9679
|
detectAmbientCredential,
|
|
9680
|
+
effectiveWindow,
|
|
9629
9681
|
ensureCa,
|
|
9630
9682
|
ensureFrontDoor,
|
|
9631
9683
|
ensureHeadroom,
|
|
9632
9684
|
evaluateAmbientCredentialGuard,
|
|
9685
|
+
evaluateWhen,
|
|
9633
9686
|
formatAmbientCredentialGuardMessage,
|
|
9634
9687
|
formatCheckReport,
|
|
9635
9688
|
formatDoctorReport,
|
|
@@ -9647,13 +9700,16 @@ function prepareClaudeLaunch(options) {
|
|
|
9647
9700
|
listProfiles,
|
|
9648
9701
|
listProviders,
|
|
9649
9702
|
listUsageSnapshots,
|
|
9703
|
+
matchBranch,
|
|
9650
9704
|
mintLeaf,
|
|
9651
9705
|
observingRoutedRoute,
|
|
9652
9706
|
parseConnectTarget,
|
|
9707
|
+
planOf,
|
|
9653
9708
|
prepareClaudeLaunch,
|
|
9654
9709
|
prepareLaunch,
|
|
9655
9710
|
profileExists,
|
|
9656
9711
|
providerExists,
|
|
9712
|
+
rankPool,
|
|
9657
9713
|
rcEventWriteResultFromAnswer,
|
|
9658
9714
|
rcObserverAsPassthrough,
|
|
9659
9715
|
readActiveIdentity,
|