agent-shim 8.11.3 → 8.12.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/README.md +1 -0
- package/dist/cli.cjs +1032 -238
- package/dist/index.cjs +1129 -384
- package/dist/index.mjs +1116 -384
- package/dist/types/index.d.ts +126 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -81,6 +81,13 @@ __export(index_exports, {
|
|
|
81
81
|
RC_PENDING_DEADLINE_MS: () => RC_PENDING_DEADLINE_MS,
|
|
82
82
|
RC_PERMISSION_MODES: () => RC_PERMISSION_MODES,
|
|
83
83
|
RC_REQUEST_PARSE_CAP_BYTES: () => RC_REQUEST_PARSE_CAP_BYTES,
|
|
84
|
+
RC_SELF_HOST_ENV: () => RC_SELF_HOST_ENV,
|
|
85
|
+
RC_SELF_HOST_KEEPALIVE_MS: () => RC_SELF_HOST_KEEPALIVE_MS,
|
|
86
|
+
RC_SELF_HOST_RECORD_DIR: () => RC_SELF_HOST_RECORD_DIR,
|
|
87
|
+
RC_SELF_HOST_RECORD_FILE: () => RC_SELF_HOST_RECORD_FILE,
|
|
88
|
+
RC_SELF_HOST_RETENTION_MS: () => RC_SELF_HOST_RETENTION_MS,
|
|
89
|
+
RC_SELF_HOST_SCOPE_LIST: () => RC_SELF_HOST_SCOPE_LIST,
|
|
90
|
+
RC_SELF_HOST_WORKER_JWT_TTL_SECONDS: () => RC_SELF_HOST_WORKER_JWT_TTL_SECONDS,
|
|
84
91
|
RC_SESSIONS_PATH_PREFIX: () => RC_SESSIONS_PATH_PREFIX,
|
|
85
92
|
RC_STREAM_BACKOFF_MS: () => RC_STREAM_BACKOFF_MS,
|
|
86
93
|
ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
|
|
@@ -117,6 +124,7 @@ __export(index_exports, {
|
|
|
117
124
|
createRcControlHandler: () => createRcControlHandler,
|
|
118
125
|
createRcCredentialStore: () => createRcCredentialStore,
|
|
119
126
|
createRcEventFanout: () => createRcEventFanout,
|
|
127
|
+
createRcSelfHostSurface: () => createRcSelfHostSurface,
|
|
120
128
|
createRcSessionTracker: () => createRcSessionTracker,
|
|
121
129
|
createRcStreamHub: () => createRcStreamHub,
|
|
122
130
|
createSseParser: () => createSseParser,
|
|
@@ -141,6 +149,8 @@ __export(index_exports, {
|
|
|
141
149
|
isIdentityDirectoryName: () => isIdentityDirectoryName,
|
|
142
150
|
isInterceptedHost: () => isInterceptedHost,
|
|
143
151
|
isRcPermissionMode: () => isRcPermissionMode,
|
|
152
|
+
lateRcEventDial: () => lateRcEventDial,
|
|
153
|
+
lateRcStreamDial: () => lateRcStreamDial,
|
|
144
154
|
listDirectoryRules: () => listDirectoryRules,
|
|
145
155
|
listIdentities: () => listIdentities,
|
|
146
156
|
listProfiles: () => listProfiles,
|
|
@@ -148,6 +158,7 @@ __export(index_exports, {
|
|
|
148
158
|
listUsageSnapshots: () => listUsageSnapshots,
|
|
149
159
|
matchBranch: () => matchBranch,
|
|
150
160
|
mintLeaf: () => mintLeaf,
|
|
161
|
+
mintRcSelfHostCredential: () => mintRcSelfHostCredential,
|
|
151
162
|
observingRoutedRoute: () => observingRoutedRoute,
|
|
152
163
|
parseConnectTarget: () => parseConnectTarget,
|
|
153
164
|
parseRcStreamEnvelope: () => parseRcStreamEnvelope,
|
|
@@ -159,6 +170,7 @@ __export(index_exports, {
|
|
|
159
170
|
rankPool: () => rankPool,
|
|
160
171
|
rcEventWriteResultFromAnswer: () => rcEventWriteResultFromAnswer,
|
|
161
172
|
rcObserverAsPassthrough: () => rcObserverAsPassthrough,
|
|
173
|
+
rcSelfHostFromEnv: () => rcSelfHostFromEnv,
|
|
162
174
|
readActiveIdentity: () => readActiveIdentity,
|
|
163
175
|
readDirectoryRules: () => readDirectoryRules,
|
|
164
176
|
readFarmManifest: () => readFarmManifest,
|
|
@@ -167,6 +179,7 @@ __export(index_exports, {
|
|
|
167
179
|
readPools: () => readPools,
|
|
168
180
|
readProfile: () => readProfile,
|
|
169
181
|
readProvider: () => readProvider,
|
|
182
|
+
readRcSelfHostRecord: () => readRcSelfHostRecord,
|
|
170
183
|
readUsageSnapshot: () => readUsageSnapshot,
|
|
171
184
|
realConnectCertStore: () => realConnectCertStore,
|
|
172
185
|
realConnectEffects: () => realConnectEffects,
|
|
@@ -3030,8 +3043,8 @@ function parseConnectTarget(authority) {
|
|
|
3030
3043
|
function isInterceptedHost(host, interceptHosts) {
|
|
3031
3044
|
return interceptHosts.includes(host);
|
|
3032
3045
|
}
|
|
3033
|
-
function servedByPipeline(
|
|
3034
|
-
return
|
|
3046
|
+
function servedByPipeline(path31) {
|
|
3047
|
+
return path31?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
3035
3048
|
}
|
|
3036
3049
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
3037
3050
|
"connection",
|
|
@@ -3113,14 +3126,17 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
|
|
|
3113
3126
|
pumpTapSession(socket, async (clientAlpn) => await effects.connectTlsUpstream(host, HTTPS_PORT, clientAlpn), config.capture?.tapStream?.(host), Buffer.from(headText, "utf8"));
|
|
3114
3127
|
};
|
|
3115
3128
|
const sessions = config.interceptHosts.map((host) => {
|
|
3116
|
-
if (config.tapHosts.includes(host)) {
|
|
3129
|
+
if (config.tapHosts.includes(host) && !(config.localSurface?.parsesHost(host) ?? false)) {
|
|
3117
3130
|
const serveSecure2 = (secure) => {
|
|
3118
3131
|
pumpTapSession(secure, async (clientAlpn) => await effects.connectTlsUpstream(host, HTTPS_PORT, clientAlpn), config.capture?.tapStream?.(host));
|
|
3119
3132
|
};
|
|
3120
3133
|
const tlsAcceptor2 = effects.createTlsAcceptor(config.leafFor(host), serveSecure2, TAP_ALPN_PROTOCOLS);
|
|
3121
3134
|
return { host, serveSecure: serveSecure2, tlsAcceptor: tlsAcceptor2 };
|
|
3122
3135
|
}
|
|
3123
|
-
const
|
|
3136
|
+
const handleHostRequest = async (request4, response) => {
|
|
3137
|
+
if (config.localSurface !== void 0 && await config.localSurface.serve(host, request4, response)) {
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3124
3140
|
if (host === config.routedHost && servedByPipeline(request4.url)) {
|
|
3125
3141
|
const tunnelToken = tunnelTokens.get(request4.socket);
|
|
3126
3142
|
if (tunnelToken !== void 0 && request4.headers[AUTH_HEADER] === void 0) {
|
|
@@ -3131,6 +3147,9 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
|
|
|
3131
3147
|
}
|
|
3132
3148
|
effects.forwardHttp(config.upstreamFor(host), request4, response, config.capture?.observePassthrough(request4));
|
|
3133
3149
|
};
|
|
3150
|
+
const handler = (request4, response) => {
|
|
3151
|
+
void handleHostRequest(request4, response);
|
|
3152
|
+
};
|
|
3134
3153
|
const httpSession = effects.createHttpSession(handler, onUpgrade(host));
|
|
3135
3154
|
const serveSecure = (secure, capability) => {
|
|
3136
3155
|
tunnelTokens.set(secure, capability);
|
|
@@ -3513,7 +3532,7 @@ function parseRcRequestBody(kind, body) {
|
|
|
3513
3532
|
return { controlRequests: [], answeredRequestIds: [], idleSeconds: finiteNumber(parseJsonObject(body), "idle_seconds"), workerStatus: void 0 };
|
|
3514
3533
|
}
|
|
3515
3534
|
if (kind === "worker-registration") {
|
|
3516
|
-
return { controlRequests: [], answeredRequestIds: [], idleSeconds: void 0, workerStatus: nonEmptyString(parseJsonObject(body), "
|
|
3535
|
+
return { controlRequests: [], answeredRequestIds: [], idleSeconds: void 0, workerStatus: nonEmptyString(parseJsonObject(body), "worker_status") };
|
|
3517
3536
|
}
|
|
3518
3537
|
const controlRequests = [];
|
|
3519
3538
|
const answeredRequestIds = [];
|
|
@@ -3562,11 +3581,11 @@ function createRcSessionTracker(deps) {
|
|
|
3562
3581
|
};
|
|
3563
3582
|
const observeExchange = (request4) => {
|
|
3564
3583
|
const method = (request4.method ?? "").toUpperCase();
|
|
3565
|
-
const
|
|
3566
|
-
if (!
|
|
3584
|
+
const path31 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
3585
|
+
if (!path31.startsWith(RC_SESSIONS_PATH_PREFIX)) {
|
|
3567
3586
|
return void 0;
|
|
3568
3587
|
}
|
|
3569
|
-
const tail =
|
|
3588
|
+
const tail = path31.slice(RC_SESSIONS_PATH_PREFIX.length);
|
|
3570
3589
|
const facts = {
|
|
3571
3590
|
oauthAuthorization: oauthAuthorizationOf(request4.headers),
|
|
3572
3591
|
anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
|
|
@@ -4327,9 +4346,9 @@ function sequenceNumsOfBody(body) {
|
|
|
4327
4346
|
}
|
|
4328
4347
|
function frontDoorRcControl(transport, token) {
|
|
4329
4348
|
const headers = { authorization: `Bearer ${token}` };
|
|
4330
|
-
const listAnswerOf = async (
|
|
4349
|
+
const listAnswerOf = async (path31, what) => {
|
|
4331
4350
|
try {
|
|
4332
|
-
return await transport.request({ method: "GET", path:
|
|
4351
|
+
return await transport.request({ method: "GET", path: path31, headers });
|
|
4333
4352
|
} catch (error) {
|
|
4334
4353
|
throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
4335
4354
|
}
|
|
@@ -4401,10 +4420,10 @@ function frontDoorRcControl(transport, token) {
|
|
|
4401
4420
|
}
|
|
4402
4421
|
return pending;
|
|
4403
4422
|
};
|
|
4404
|
-
const writeOf = async (
|
|
4423
|
+
const writeOf = async (path31, body, describeFailure) => {
|
|
4405
4424
|
let answer;
|
|
4406
4425
|
try {
|
|
4407
|
-
answer = await transport.request({ method: "POST", path:
|
|
4426
|
+
answer = await transport.request({ method: "POST", path: path31, headers: { ...headers, "content-type": "application/json" }, body });
|
|
4408
4427
|
} catch (error) {
|
|
4409
4428
|
return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
|
|
4410
4429
|
}
|
|
@@ -4614,14 +4633,41 @@ var NO_CHUNKS = {
|
|
|
4614
4633
|
next: async () => await Promise.resolve({ done: true, value: void 0 })
|
|
4615
4634
|
})
|
|
4616
4635
|
};
|
|
4636
|
+
function lateRcEventDial(resolve) {
|
|
4637
|
+
let built;
|
|
4638
|
+
const dialFor = () => {
|
|
4639
|
+
const target = resolve();
|
|
4640
|
+
if (built === void 0 || built.for !== target) {
|
|
4641
|
+
built = { for: target, dial: realRcEventDial(target) };
|
|
4642
|
+
}
|
|
4643
|
+
return built.dial;
|
|
4644
|
+
};
|
|
4645
|
+
return {
|
|
4646
|
+
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
|
|
4647
|
+
};
|
|
4648
|
+
}
|
|
4649
|
+
function lateRcStreamDial(resolve) {
|
|
4650
|
+
let built;
|
|
4651
|
+
const dialFor = () => {
|
|
4652
|
+
const target = resolve();
|
|
4653
|
+
if (built === void 0 || built.for !== target) {
|
|
4654
|
+
built = { for: target, dial: realRcStreamDial(target) };
|
|
4655
|
+
}
|
|
4656
|
+
return built.dial;
|
|
4657
|
+
};
|
|
4658
|
+
return {
|
|
4659
|
+
announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
|
|
4660
|
+
openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
|
|
4661
|
+
};
|
|
4662
|
+
}
|
|
4617
4663
|
function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
4618
4664
|
const agent = new ExemptTlsAgent();
|
|
4619
|
-
const request4 = async (method,
|
|
4665
|
+
const request4 = async (method, path31, headers, body, signal) => await new Promise((resolve, reject) => {
|
|
4620
4666
|
const options = {
|
|
4621
4667
|
host: target.host,
|
|
4622
4668
|
port: target.port,
|
|
4623
4669
|
method,
|
|
4624
|
-
path:
|
|
4670
|
+
path: path31,
|
|
4625
4671
|
// The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
|
|
4626
4672
|
headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
|
|
4627
4673
|
agent,
|
|
@@ -4658,9 +4704,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4658
4704
|
return { status, body: await readAll(response) };
|
|
4659
4705
|
},
|
|
4660
4706
|
openStream: async (sessionId, headers, resume, signal) => {
|
|
4661
|
-
const
|
|
4707
|
+
const path31 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
|
|
4662
4708
|
const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
|
|
4663
|
-
const { status, response } = await request4("GET",
|
|
4709
|
+
const { status, response } = await request4("GET", path31, streamHeaders, void 0, signal);
|
|
4664
4710
|
if (!isSuccessful2(status)) {
|
|
4665
4711
|
await readAll(response);
|
|
4666
4712
|
return { status, chunks: NO_CHUNKS };
|
|
@@ -5355,37 +5401,930 @@ function createRcStreamHub(deps) {
|
|
|
5355
5401
|
await deps.sleep(deps.backoffMs);
|
|
5356
5402
|
retriedUnauthorized = false;
|
|
5357
5403
|
}
|
|
5358
|
-
};
|
|
5359
|
-
return {
|
|
5360
|
-
reconcile: () => {
|
|
5361
|
-
const live = new Set(deps.trackedSessions());
|
|
5362
|
-
for (const [sessionId, attachment] of attachments) {
|
|
5363
|
-
if (!live.has(sessionId)) {
|
|
5364
|
-
attachment.stop();
|
|
5365
|
-
attachments.delete(sessionId);
|
|
5366
|
-
}
|
|
5367
|
-
}
|
|
5368
|
-
for (const sessionId of live) {
|
|
5369
|
-
if (attachments.has(sessionId)) {
|
|
5370
|
-
continue;
|
|
5371
|
-
}
|
|
5372
|
-
if (deps.credentialOf(sessionId)?.authorization === void 0) {
|
|
5373
|
-
continue;
|
|
5374
|
-
}
|
|
5375
|
-
const attachment = birth(sessionId);
|
|
5376
|
-
attachments.set(sessionId, attachment);
|
|
5377
|
-
void runLoop(attachment).catch((error) => {
|
|
5378
|
-
deps.log?.(`rc stream ${sessionId}: the attachment loop ended unexpectedly (${error instanceof Error ? error.message : String(error)})`);
|
|
5379
|
-
});
|
|
5380
|
-
}
|
|
5404
|
+
};
|
|
5405
|
+
return {
|
|
5406
|
+
reconcile: () => {
|
|
5407
|
+
const live = new Set(deps.trackedSessions());
|
|
5408
|
+
for (const [sessionId, attachment] of attachments) {
|
|
5409
|
+
if (!live.has(sessionId)) {
|
|
5410
|
+
attachment.stop();
|
|
5411
|
+
attachments.delete(sessionId);
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
for (const sessionId of live) {
|
|
5415
|
+
if (attachments.has(sessionId)) {
|
|
5416
|
+
continue;
|
|
5417
|
+
}
|
|
5418
|
+
if (deps.credentialOf(sessionId)?.authorization === void 0) {
|
|
5419
|
+
continue;
|
|
5420
|
+
}
|
|
5421
|
+
const attachment = birth(sessionId);
|
|
5422
|
+
attachments.set(sessionId, attachment);
|
|
5423
|
+
void runLoop(attachment).catch((error) => {
|
|
5424
|
+
deps.log?.(`rc stream ${sessionId}: the attachment loop ended unexpectedly (${error instanceof Error ? error.message : String(error)})`);
|
|
5425
|
+
});
|
|
5426
|
+
}
|
|
5427
|
+
},
|
|
5428
|
+
close: () => {
|
|
5429
|
+
for (const attachment of attachments.values()) {
|
|
5430
|
+
attachment.stop();
|
|
5431
|
+
}
|
|
5432
|
+
attachments.clear();
|
|
5433
|
+
}
|
|
5434
|
+
};
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5437
|
+
// src/frontdoor/rcSelfHost.ts
|
|
5438
|
+
var RC_SELF_HOST_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST";
|
|
5439
|
+
function rcSelfHostFromEnv(env) {
|
|
5440
|
+
return env[RC_SELF_HOST_ENV] === "1";
|
|
5441
|
+
}
|
|
5442
|
+
var RC_SELF_HOST_SCOPE_LIST = ["user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"];
|
|
5443
|
+
var RC_SESSIONS_PATH_PREFIX2 = "/v1/code/sessions";
|
|
5444
|
+
var RC_COMPAT_SESSIONS_PATH = "/v1/sessions";
|
|
5445
|
+
var RC_SESSION_ID_PREFIX2 = "cse_";
|
|
5446
|
+
var RC_SELF_HOST_API_BASE_URL = "https://api.anthropic.com";
|
|
5447
|
+
var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
|
|
5448
|
+
var HTTP_CONFLICT = 409;
|
|
5449
|
+
var MS_PER_SECOND2 = 1e3;
|
|
5450
|
+
var HTTP_SERVICE_UNAVAILABLE = 503;
|
|
5451
|
+
var RC_SELF_HOST_WORKER_JWT_TTL_SECONDS = 46800;
|
|
5452
|
+
var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
|
|
5453
|
+
var RC_SELF_HOST_KEEPALIVE_MS = 15e3;
|
|
5454
|
+
var RC_SELF_HOST_RETENTION_MS = 6e5;
|
|
5455
|
+
var RC_PRESENCE_REFRESH_SECONDS = 60;
|
|
5456
|
+
var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
|
|
5457
|
+
var RC_SELF_HOST_WORKER_EPOCH = 1;
|
|
5458
|
+
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
5459
|
+
var RC_SOURCE_WORKER = "worker";
|
|
5460
|
+
var RC_SOURCE_CLIENT = "client";
|
|
5461
|
+
var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
|
|
5462
|
+
var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
5463
|
+
function isRecord5(value) {
|
|
5464
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5465
|
+
}
|
|
5466
|
+
function nonEmptyString2(record, field) {
|
|
5467
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
5468
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
5469
|
+
}
|
|
5470
|
+
function numberOf(record, field) {
|
|
5471
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
5472
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5473
|
+
return value;
|
|
5474
|
+
}
|
|
5475
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value)) {
|
|
5476
|
+
return Number(value);
|
|
5477
|
+
}
|
|
5478
|
+
return void 0;
|
|
5479
|
+
}
|
|
5480
|
+
function singleHeader2(headers, name) {
|
|
5481
|
+
const value = headers[name];
|
|
5482
|
+
return typeof value === "string" ? value : void 0;
|
|
5483
|
+
}
|
|
5484
|
+
function bearerOf(headers) {
|
|
5485
|
+
const presented = singleHeader2(headers, "authorization");
|
|
5486
|
+
if (presented === void 0) {
|
|
5487
|
+
return void 0;
|
|
5488
|
+
}
|
|
5489
|
+
return presented.slice(0, "bearer ".length).toLowerCase() === "bearer " ? presented.slice("bearer ".length) : void 0;
|
|
5490
|
+
}
|
|
5491
|
+
async function readBody2(request4, capBytes) {
|
|
5492
|
+
return await new Promise((resolve, reject) => {
|
|
5493
|
+
const chunks = [];
|
|
5494
|
+
let total = 0;
|
|
5495
|
+
request4.on("data", (chunk) => {
|
|
5496
|
+
total += chunk.length;
|
|
5497
|
+
if (total > capBytes) {
|
|
5498
|
+
request4.destroy();
|
|
5499
|
+
reject(new Error("body too large"));
|
|
5500
|
+
return;
|
|
5501
|
+
}
|
|
5502
|
+
chunks.push(chunk);
|
|
5503
|
+
});
|
|
5504
|
+
request4.on("end", () => {
|
|
5505
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
5506
|
+
});
|
|
5507
|
+
request4.on("error", () => {
|
|
5508
|
+
reject(new Error("the request body ended early"));
|
|
5509
|
+
});
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
function parseJsonObject2(body) {
|
|
5513
|
+
let parsed;
|
|
5514
|
+
try {
|
|
5515
|
+
parsed = JSON.parse(body);
|
|
5516
|
+
} catch {
|
|
5517
|
+
return void 0;
|
|
5518
|
+
}
|
|
5519
|
+
return isRecord5(parsed) ? parsed : void 0;
|
|
5520
|
+
}
|
|
5521
|
+
function mintWorkerJwt(randomToken, issuedAt) {
|
|
5522
|
+
const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
5523
|
+
const header = encode({ alg: "HS256", typ: "JWT" });
|
|
5524
|
+
const payload = encode({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND2) / MS_PER_SECOND2), iat: Math.floor(issuedAt / MS_PER_SECOND2) });
|
|
5525
|
+
return `${header}.${payload}.${Buffer.from(randomToken(), "utf8").toString("base64url")}`;
|
|
5526
|
+
}
|
|
5527
|
+
function growthBookFeatures() {
|
|
5528
|
+
return {
|
|
5529
|
+
tengu_ccr_bridge: { defaultValue: true },
|
|
5530
|
+
tengu_bridge_repl_v2: { defaultValue: true },
|
|
5531
|
+
tengu_bridge_repl_v2_cse_shim_enabled: { defaultValue: true },
|
|
5532
|
+
tengu_bridge_min_version: { defaultValue: { minVersion: "0.0.0" } }
|
|
5533
|
+
};
|
|
5534
|
+
}
|
|
5535
|
+
function createRcSelfHostSurface(deps) {
|
|
5536
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
5537
|
+
const sweep = () => {
|
|
5538
|
+
const now = deps.now();
|
|
5539
|
+
for (const session of sessions.values()) {
|
|
5540
|
+
while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
|
|
5541
|
+
const dropped = session.events.shift();
|
|
5542
|
+
const uuid = dropped === void 0 ? void 0 : nonEmptyString2(dropped.payload, "uuid");
|
|
5543
|
+
if (uuid !== void 0) {
|
|
5544
|
+
session.seenPayloadUuids.delete(uuid);
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
5548
|
+
sessions.delete(session.id);
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
};
|
|
5552
|
+
const timer = setInterval(sweep, RC_SELF_HOST_RETENTION_MS);
|
|
5553
|
+
timer.unref();
|
|
5554
|
+
const keepalive = setInterval(() => {
|
|
5555
|
+
for (const session of sessions.values()) {
|
|
5556
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
5557
|
+
sink.write(": keep-alive\n\n");
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
}, RC_SELF_HOST_KEEPALIVE_MS);
|
|
5561
|
+
keepalive.unref();
|
|
5562
|
+
const presentsCredential = (headers) => {
|
|
5563
|
+
const expected = deps.credentialRecord()?.accessToken;
|
|
5564
|
+
const presented = bearerOf(headers);
|
|
5565
|
+
return expected !== void 0 && presented !== void 0 && isLiveCapability(presented, [expected]);
|
|
5566
|
+
};
|
|
5567
|
+
const presentsWorkerJwt = (headers, session) => {
|
|
5568
|
+
const presented = bearerOf(headers);
|
|
5569
|
+
if (presented === void 0) {
|
|
5570
|
+
return false;
|
|
5571
|
+
}
|
|
5572
|
+
for (const minted of session.workerJwts) {
|
|
5573
|
+
if (isLiveCapability(presented, [minted])) {
|
|
5574
|
+
return true;
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
return false;
|
|
5578
|
+
};
|
|
5579
|
+
const frameOf = (event) => {
|
|
5580
|
+
const envelope = {
|
|
5581
|
+
event_id: event.eventId,
|
|
5582
|
+
event_type: nonEmptyString2(event.payload, "type") ?? "event",
|
|
5583
|
+
sequence_num: event.sequenceNum,
|
|
5584
|
+
source: event.source,
|
|
5585
|
+
payload: event.payload,
|
|
5586
|
+
created_at: event.createdAt
|
|
5587
|
+
};
|
|
5588
|
+
return `event: ${RC_STREAM_EVENT_NAME2}
|
|
5589
|
+
id: ${String(event.sequenceNum)}
|
|
5590
|
+
data: ${JSON.stringify(envelope)}
|
|
5591
|
+
|
|
5592
|
+
`;
|
|
5593
|
+
};
|
|
5594
|
+
const holdStream = async (session, which, wantsReplay, request4, response) => {
|
|
5595
|
+
const url = new URL(request4.url, "http://127.0.0.1");
|
|
5596
|
+
const cursor = numberOf({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
|
|
5597
|
+
const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
|
|
5598
|
+
response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
5599
|
+
response.flush();
|
|
5600
|
+
let pending = Promise.resolve();
|
|
5601
|
+
const sink = {
|
|
5602
|
+
write: (frame) => {
|
|
5603
|
+
pending = pending.then(async () => {
|
|
5604
|
+
await response.write(frame);
|
|
5605
|
+
}).catch(() => {
|
|
5606
|
+
});
|
|
5607
|
+
},
|
|
5608
|
+
retire: () => {
|
|
5609
|
+
session[which].delete(sink);
|
|
5610
|
+
}
|
|
5611
|
+
};
|
|
5612
|
+
session[which].add(sink);
|
|
5613
|
+
session.lastTrafficAt = deps.now();
|
|
5614
|
+
for (const event of replay) {
|
|
5615
|
+
sink.write(frameOf(event));
|
|
5616
|
+
}
|
|
5617
|
+
await new Promise((resolve) => {
|
|
5618
|
+
request4.signal.addEventListener("abort", () => {
|
|
5619
|
+
resolve(void 0);
|
|
5620
|
+
}, { once: true });
|
|
5621
|
+
});
|
|
5622
|
+
sink.retire();
|
|
5623
|
+
response.end();
|
|
5624
|
+
};
|
|
5625
|
+
const publish = (session, source, payload) => {
|
|
5626
|
+
const uuid = nonEmptyString2(payload, "uuid");
|
|
5627
|
+
const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
|
|
5628
|
+
session.sequenceNum += 1;
|
|
5629
|
+
const event = {
|
|
5630
|
+
sequenceNum: session.sequenceNum,
|
|
5631
|
+
eventId: uuid ?? deps.newUuid(),
|
|
5632
|
+
source,
|
|
5633
|
+
// The one enrichment the real service was observed making on the live rig's stream: the wall-clock instant it took the write. The worker's handler ignores it; the door's own parser forwards it verbatim.
|
|
5634
|
+
payload: source === RC_SOURCE_CLIENT ? { ...payload, server_received_wall_ms: deps.now() } : payload,
|
|
5635
|
+
createdAt: new Date(deps.now()).toISOString(),
|
|
5636
|
+
toWorker: source === RC_SOURCE_CLIENT,
|
|
5637
|
+
storedAt: deps.now()
|
|
5638
|
+
};
|
|
5639
|
+
session.events.push(event);
|
|
5640
|
+
if (uuid !== void 0) {
|
|
5641
|
+
session.seenPayloadUuids.add(uuid);
|
|
5642
|
+
}
|
|
5643
|
+
session.lastTrafficAt = event.storedAt;
|
|
5644
|
+
const frame = frameOf(event);
|
|
5645
|
+
if (event.toWorker) {
|
|
5646
|
+
for (const sink of [...session.workerStreams]) {
|
|
5647
|
+
sink.write(frame);
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
for (const sink of [...session.clientStreams]) {
|
|
5651
|
+
sink.write(frame);
|
|
5652
|
+
}
|
|
5653
|
+
return { sequenceNum: event.sequenceNum, eventId: event.eventId, duplicate };
|
|
5654
|
+
};
|
|
5655
|
+
const answerJson2 = async (response, status, body, extraHeaders = {}) => {
|
|
5656
|
+
const text = JSON.stringify(body);
|
|
5657
|
+
response.start(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")), ...extraHeaders });
|
|
5658
|
+
await response.write(text);
|
|
5659
|
+
response.end();
|
|
5660
|
+
};
|
|
5661
|
+
const noCredential = async (response) => {
|
|
5662
|
+
await answerJson2(response, HTTP_SERVICE_UNAVAILABLE, { type: "error", error: { type: "api_error", message: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it; run agent-shim frontdoor rc selfhost mint first" } });
|
|
5663
|
+
};
|
|
5664
|
+
const epochStale = async (response) => {
|
|
5665
|
+
await answerJson2(response, HTTP_CONFLICT, { type: "error", error: { type: "api_error", message: "worker_epoch does not match the session's live epoch" } }, { [CONFLICT_REASON_HEADER2]: CONFLICT_REASON_EPOCH_STALE });
|
|
5666
|
+
};
|
|
5667
|
+
const unauthorized = async (response) => {
|
|
5668
|
+
await answerJson2(response, HTTP_STATUS.unauthorized, { type: "error", error: { type: "authentication_error", message: "the self-hosted Remote Control surface did not recognise the presented credential" } });
|
|
5669
|
+
};
|
|
5670
|
+
const serveSessionPath = async (session, tail, request4, response) => {
|
|
5671
|
+
const method = request4.method.toUpperCase();
|
|
5672
|
+
if (session === void 0) {
|
|
5673
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "no such Remote Control session on this door" } });
|
|
5674
|
+
return;
|
|
5675
|
+
}
|
|
5676
|
+
session.lastTrafficAt = deps.now();
|
|
5677
|
+
if (tail === "") {
|
|
5678
|
+
if (method !== "GET") {
|
|
5679
|
+
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
|
|
5680
|
+
return;
|
|
5681
|
+
}
|
|
5682
|
+
await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
|
|
5683
|
+
return;
|
|
5684
|
+
}
|
|
5685
|
+
if (tail === "bridge" && method === "POST") {
|
|
5686
|
+
if (!presentsCredential(request4.headers)) {
|
|
5687
|
+
await unauthorized(response);
|
|
5688
|
+
return;
|
|
5689
|
+
}
|
|
5690
|
+
const workerJwt = mintWorkerJwt(deps.randomToken, deps.now());
|
|
5691
|
+
session.workerJwts.add(workerJwt);
|
|
5692
|
+
deps.log?.(`rc selfhost ${session.id}: bridge registration minted a worker credential`);
|
|
5693
|
+
await answerJson2(response, HTTP_STATUS.ok, { worker_jwt: workerJwt, expires_in: RC_SELF_HOST_WORKER_JWT_TTL_SECONDS, api_base_url: RC_SELF_HOST_API_BASE_URL, worker_epoch: RC_SELF_HOST_WORKER_EPOCH });
|
|
5694
|
+
return;
|
|
5695
|
+
}
|
|
5696
|
+
if (tail === "archive" && method === "POST") {
|
|
5697
|
+
if (!presentsCredential(request4.headers)) {
|
|
5698
|
+
await unauthorized(response);
|
|
5699
|
+
return;
|
|
5700
|
+
}
|
|
5701
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
5702
|
+
sink.retire();
|
|
5703
|
+
}
|
|
5704
|
+
sessions.delete(session.id);
|
|
5705
|
+
deps.log?.(`rc selfhost ${session.id}: archived at its client's request`);
|
|
5706
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5707
|
+
return;
|
|
5708
|
+
}
|
|
5709
|
+
if (tail === "worker" || tail.startsWith("worker/")) {
|
|
5710
|
+
if (!presentsWorkerJwt(request4.headers, session)) {
|
|
5711
|
+
await unauthorized(response);
|
|
5712
|
+
return;
|
|
5713
|
+
}
|
|
5714
|
+
const workerTail = tail === "worker" ? "" : tail.slice("worker/".length);
|
|
5715
|
+
if (workerTail === "" && method === "GET") {
|
|
5716
|
+
await answerJson2(response, HTTP_STATUS.ok, { worker: { external_metadata: session.externalMetadata ?? null } });
|
|
5717
|
+
return;
|
|
5718
|
+
}
|
|
5719
|
+
if (workerTail === "" && method === "PUT") {
|
|
5720
|
+
const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
5721
|
+
const claimedEpoch = numberOf(body, "worker_epoch");
|
|
5722
|
+
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
5723
|
+
await epochStale(response);
|
|
5724
|
+
return;
|
|
5725
|
+
}
|
|
5726
|
+
const status = nonEmptyString2(body, "worker_status");
|
|
5727
|
+
if (status !== void 0) {
|
|
5728
|
+
session.workerStatus = status;
|
|
5729
|
+
}
|
|
5730
|
+
if (isRecord5(body?.external_metadata)) {
|
|
5731
|
+
session.externalMetadata = body.external_metadata;
|
|
5732
|
+
}
|
|
5733
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5734
|
+
return;
|
|
5735
|
+
}
|
|
5736
|
+
if (workerTail === "register" && method === "POST") {
|
|
5737
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5738
|
+
await answerJson2(response, HTTP_STATUS.ok, { worker_epoch: RC_SELF_HOST_WORKER_EPOCH });
|
|
5739
|
+
return;
|
|
5740
|
+
}
|
|
5741
|
+
if (workerTail === "heartbeat" && method === "POST") {
|
|
5742
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5743
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5744
|
+
return;
|
|
5745
|
+
}
|
|
5746
|
+
if (workerTail === "events" && method === "POST") {
|
|
5747
|
+
const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
5748
|
+
const claimedEpoch = numberOf(body, "worker_epoch");
|
|
5749
|
+
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
5750
|
+
await epochStale(response);
|
|
5751
|
+
return;
|
|
5752
|
+
}
|
|
5753
|
+
const events = body?.events;
|
|
5754
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
5755
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: `a worker event batch is a non-empty array of at most ${String(RC_SELF_HOST_MAX_BATCH_EVENTS)} events` } });
|
|
5756
|
+
return;
|
|
5757
|
+
}
|
|
5758
|
+
const results = [];
|
|
5759
|
+
for (const event of events) {
|
|
5760
|
+
if (!isRecord5(event) || !isRecord5(event.payload)) {
|
|
5761
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a batch is an object carrying one payload object" } });
|
|
5762
|
+
return;
|
|
5763
|
+
}
|
|
5764
|
+
const published = publish(session, RC_SOURCE_WORKER, event.payload);
|
|
5765
|
+
results.push({ event_id: published.eventId, sequence_num: String(published.sequenceNum) });
|
|
5766
|
+
}
|
|
5767
|
+
sweep();
|
|
5768
|
+
await answerJson2(response, HTTP_STATUS.ok, { results });
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
if (workerTail === "events/stream" && method === "GET") {
|
|
5772
|
+
await holdStream(session, "workerStreams", (event) => event.toWorker, request4, response);
|
|
5773
|
+
return;
|
|
5774
|
+
}
|
|
5775
|
+
if (workerTail === "events/delivery" && method === "POST") {
|
|
5776
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5777
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5778
|
+
return;
|
|
5779
|
+
}
|
|
5780
|
+
if (workerTail === "internal-events" && method === "POST") {
|
|
5781
|
+
const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
5782
|
+
const claimedEpoch = numberOf(body, "worker_epoch");
|
|
5783
|
+
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
5784
|
+
await epochStale(response);
|
|
5785
|
+
return;
|
|
5786
|
+
}
|
|
5787
|
+
const events = body?.events;
|
|
5788
|
+
if (!Array.isArray(events)) {
|
|
5789
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "an internal event batch is an array" } });
|
|
5790
|
+
return;
|
|
5791
|
+
}
|
|
5792
|
+
for (const event of events) {
|
|
5793
|
+
if (isRecord5(event)) {
|
|
5794
|
+
session.internalEvents.push({ cursor: deps.newUuid(), body: event });
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5797
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5798
|
+
return;
|
|
5799
|
+
}
|
|
5800
|
+
if (workerTail === "internal-events" && method === "GET") {
|
|
5801
|
+
const url = new URL(request4.url, "http://127.0.0.1");
|
|
5802
|
+
const cursor = url.searchParams.get("cursor");
|
|
5803
|
+
const from = cursor === null ? 0 : session.internalEvents.findIndex((event) => event.cursor === cursor) + 1;
|
|
5804
|
+
const page = session.internalEvents.slice(from);
|
|
5805
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: page.map((event) => event.body), next_cursor: page.length === 0 ? void 0 : page[page.length - 1]?.cursor });
|
|
5806
|
+
return;
|
|
5807
|
+
}
|
|
5808
|
+
if (workerTail === "diagnostics" && method === "POST") {
|
|
5809
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5810
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5811
|
+
return;
|
|
5812
|
+
}
|
|
5813
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no worker path "${workerTail}"` } });
|
|
5814
|
+
return;
|
|
5815
|
+
}
|
|
5816
|
+
if (!presentsCredential(request4.headers)) {
|
|
5817
|
+
await unauthorized(response);
|
|
5818
|
+
return;
|
|
5819
|
+
}
|
|
5820
|
+
if (tail === "events" && method === "POST") {
|
|
5821
|
+
const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
5822
|
+
const events = body?.events;
|
|
5823
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
5824
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: `a client event write is a non-empty array of at most ${String(RC_SELF_HOST_MAX_BATCH_EVENTS)} events` } });
|
|
5825
|
+
return;
|
|
5826
|
+
}
|
|
5827
|
+
const results = [];
|
|
5828
|
+
for (const event of events) {
|
|
5829
|
+
if (!isRecord5(event) || !isRecord5(event.payload)) {
|
|
5830
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a write is an object carrying one payload object" } });
|
|
5831
|
+
return;
|
|
5832
|
+
}
|
|
5833
|
+
const published = publish(session, RC_SOURCE_CLIENT, event.payload);
|
|
5834
|
+
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
5835
|
+
}
|
|
5836
|
+
sweep();
|
|
5837
|
+
await answerJson2(response, HTTP_STATUS.ok, { results });
|
|
5838
|
+
return;
|
|
5839
|
+
}
|
|
5840
|
+
if (tail === "events" && method === "GET") {
|
|
5841
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString2(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
|
|
5842
|
+
return;
|
|
5843
|
+
}
|
|
5844
|
+
if (tail === "events/stream" && method === "GET") {
|
|
5845
|
+
await holdStream(session, "clientStreams", () => true, request4, response);
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
if (tail === "client/presence" && method === "POST") {
|
|
5849
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5850
|
+
await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
|
|
5851
|
+
return;
|
|
5852
|
+
}
|
|
5853
|
+
if (tail === "mark_read" && method === "POST") {
|
|
5854
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5855
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5856
|
+
return;
|
|
5857
|
+
}
|
|
5858
|
+
if (tail === "teleport-events" && method === "POST") {
|
|
5859
|
+
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
5860
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
5861
|
+
return;
|
|
5862
|
+
}
|
|
5863
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
|
|
5864
|
+
};
|
|
5865
|
+
const recordMissing = () => deps.credentialRecord() === void 0;
|
|
5866
|
+
const route = {
|
|
5867
|
+
name: "rc-selfhost",
|
|
5868
|
+
headroomEligible: false,
|
|
5869
|
+
headroomUpstream: void 0,
|
|
5870
|
+
serve: async (request4, response) => {
|
|
5871
|
+
const method = request4.method.toUpperCase();
|
|
5872
|
+
const pathname = new URL(request4.url, "http://127.0.0.1").pathname;
|
|
5873
|
+
if (recordMissing()) {
|
|
5874
|
+
await noCredential(response);
|
|
5875
|
+
return;
|
|
5876
|
+
}
|
|
5877
|
+
if (pathname === RC_COMPAT_SESSIONS_PATH) {
|
|
5878
|
+
if (method !== "GET") {
|
|
5879
|
+
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
5880
|
+
return;
|
|
5881
|
+
}
|
|
5882
|
+
if (!presentsCredential(request4.headers)) {
|
|
5883
|
+
await unauthorized(response);
|
|
5884
|
+
return;
|
|
5885
|
+
}
|
|
5886
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: [] });
|
|
5887
|
+
return;
|
|
5888
|
+
}
|
|
5889
|
+
if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
|
|
5890
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${pathname}"` } });
|
|
5891
|
+
return;
|
|
5892
|
+
}
|
|
5893
|
+
if (pathname === RC_SESSIONS_PATH_PREFIX2) {
|
|
5894
|
+
if (method === "POST") {
|
|
5895
|
+
if (!presentsCredential(request4.headers)) {
|
|
5896
|
+
await unauthorized(response);
|
|
5897
|
+
return;
|
|
5898
|
+
}
|
|
5899
|
+
const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
5900
|
+
const session = {
|
|
5901
|
+
id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
|
|
5902
|
+
createdAt: deps.now(),
|
|
5903
|
+
title: nonEmptyString2(body, "title"),
|
|
5904
|
+
workerJwts: /* @__PURE__ */ new Set(),
|
|
5905
|
+
sequenceNum: 0,
|
|
5906
|
+
events: [],
|
|
5907
|
+
seenPayloadUuids: /* @__PURE__ */ new Set(),
|
|
5908
|
+
workerStreams: /* @__PURE__ */ new Set(),
|
|
5909
|
+
clientStreams: /* @__PURE__ */ new Set(),
|
|
5910
|
+
internalEvents: [],
|
|
5911
|
+
workerStatus: void 0,
|
|
5912
|
+
externalMetadata: void 0,
|
|
5913
|
+
lastTrafficAt: deps.now()
|
|
5914
|
+
};
|
|
5915
|
+
sessions.set(session.id, session);
|
|
5916
|
+
deps.log?.(`rc selfhost ${session.id}: session created on the door's own surface`);
|
|
5917
|
+
await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
if (method === "GET") {
|
|
5921
|
+
if (!presentsCredential(request4.headers)) {
|
|
5922
|
+
await unauthorized(response);
|
|
5923
|
+
return;
|
|
5924
|
+
}
|
|
5925
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: [...sessions.values()].map((session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(session.lastTrafficAt).toISOString(), session_status: "active" })) });
|
|
5926
|
+
return;
|
|
5927
|
+
}
|
|
5928
|
+
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session collection is a POST (create) and a GET (list)" } });
|
|
5929
|
+
return;
|
|
5930
|
+
}
|
|
5931
|
+
const rest = pathname.slice(`${RC_SESSIONS_PATH_PREFIX2}/`.length);
|
|
5932
|
+
const slash = rest.indexOf("/");
|
|
5933
|
+
const id = slash === -1 ? rest : rest.slice(0, slash);
|
|
5934
|
+
const tail = slash === -1 ? "" : rest.slice(slash + 1);
|
|
5935
|
+
if (!id.startsWith(RC_SESSION_ID_PREFIX2)) {
|
|
5936
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "the session family names one cse_-prefixed session id" } });
|
|
5937
|
+
return;
|
|
5938
|
+
}
|
|
5939
|
+
await serveSessionPath(sessions.get(id), tail, request4, response);
|
|
5940
|
+
}
|
|
5941
|
+
};
|
|
5942
|
+
const answerServerJson = (response, status, body) => {
|
|
5943
|
+
const text = JSON.stringify(body);
|
|
5944
|
+
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
5945
|
+
response.end(text);
|
|
5946
|
+
};
|
|
5947
|
+
const local = {
|
|
5948
|
+
parsesHost: (host) => host === RC_SELF_HOST_OAUTH_HOST,
|
|
5949
|
+
serve: async (host, request4, response) => {
|
|
5950
|
+
const method = (request4.method ?? "").toUpperCase();
|
|
5951
|
+
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
5952
|
+
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
5953
|
+
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
5954
|
+
const body = parseJsonObject2(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
|
|
5955
|
+
const record = deps.credentialRecord();
|
|
5956
|
+
if (record === void 0) {
|
|
5957
|
+
answerServerJson(response, HTTP_SERVICE_UNAVAILABLE, { error: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it" });
|
|
5958
|
+
return true;
|
|
5959
|
+
}
|
|
5960
|
+
if (body?.grant_type !== void 0 && body.grant_type !== "refresh_token") {
|
|
5961
|
+
answerServerJson(response, HTTP_STATUS.badRequest, { error: "unsupported_grant_type" });
|
|
5962
|
+
return true;
|
|
5963
|
+
}
|
|
5964
|
+
answerServerJson(response, HTTP_STATUS.ok, { access_token: record.accessToken, refresh_token: record.refreshToken, expires_in: RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS, scope: RC_SELF_HOST_SCOPE_LIST.join(" ") });
|
|
5965
|
+
return true;
|
|
5966
|
+
}
|
|
5967
|
+
if (method === "GET" && pathname === "/v1/oauth/hello") {
|
|
5968
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
5969
|
+
return true;
|
|
5970
|
+
}
|
|
5971
|
+
return false;
|
|
5972
|
+
}
|
|
5973
|
+
if (method === "POST" && pathname.startsWith("/api/eval/")) {
|
|
5974
|
+
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
5975
|
+
answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND2) });
|
|
5976
|
+
return true;
|
|
5977
|
+
}
|
|
5978
|
+
if (method === "GET" && pathname === "/api/oauth/profile") {
|
|
5979
|
+
const record = deps.credentialRecord();
|
|
5980
|
+
if (record === void 0) {
|
|
5981
|
+
answerServerJson(response, HTTP_SERVICE_UNAVAILABLE, { error: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it" });
|
|
5982
|
+
return true;
|
|
5983
|
+
}
|
|
5984
|
+
answerServerJson(response, HTTP_STATUS.ok, {
|
|
5985
|
+
account: { uuid: record.accountUuid, display_name: "agent-shim self-hosted", created_at: (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
5986
|
+
organization: { uuid: record.organizationUuid, organization_type: "claude_max" }
|
|
5987
|
+
});
|
|
5988
|
+
return true;
|
|
5989
|
+
}
|
|
5990
|
+
if (method === "GET" && pathname === "/api/claude_code/policy_limits") {
|
|
5991
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
5992
|
+
return true;
|
|
5993
|
+
}
|
|
5994
|
+
if (method === "POST" && pathname === "/api/oauth/validate") {
|
|
5995
|
+
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
5996
|
+
const record = deps.credentialRecord();
|
|
5997
|
+
if (record === void 0) {
|
|
5998
|
+
answerServerJson(response, HTTP_SERVICE_UNAVAILABLE, { error: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it" });
|
|
5999
|
+
return true;
|
|
6000
|
+
}
|
|
6001
|
+
answerServerJson(response, HTTP_STATUS.ok, { scopes: [...RC_SELF_HOST_SCOPE_LIST], expiresAt: null, subscriptionType: "max", account_uuid: record.accountUuid, organization_uuid: record.organizationUuid });
|
|
6002
|
+
return true;
|
|
6003
|
+
}
|
|
6004
|
+
if (method === "POST" && (pathname === "/api/event_logging/v2/batch" || pathname === "/api/claude_code/metrics" || pathname === "/api/claude_cli_feedback")) {
|
|
6005
|
+
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
6006
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
6007
|
+
return true;
|
|
6008
|
+
}
|
|
6009
|
+
if (method === "GET" && pathname === "/api/hello") {
|
|
6010
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
6011
|
+
return true;
|
|
6012
|
+
}
|
|
6013
|
+
return false;
|
|
6014
|
+
}
|
|
6015
|
+
};
|
|
6016
|
+
return {
|
|
6017
|
+
route,
|
|
6018
|
+
local,
|
|
6019
|
+
close: () => {
|
|
6020
|
+
clearInterval(timer);
|
|
6021
|
+
clearInterval(keepalive);
|
|
6022
|
+
for (const session of sessions.values()) {
|
|
6023
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
6024
|
+
sink.retire();
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
sessions.clear();
|
|
6028
|
+
}
|
|
6029
|
+
};
|
|
6030
|
+
}
|
|
6031
|
+
|
|
6032
|
+
// src/frontdoor/rcSelfHostMint.ts
|
|
6033
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
6034
|
+
|
|
6035
|
+
// src/usage/account.ts
|
|
6036
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
6037
|
+
|
|
6038
|
+
// src/usage/schema.ts
|
|
6039
|
+
var import_zod5 = require("zod");
|
|
6040
|
+
var USAGE_SCHEMA_VERSION = 1;
|
|
6041
|
+
var VersionSchema = import_zod5.z.literal(USAGE_SCHEMA_VERSION);
|
|
6042
|
+
var InstantSchema = import_zod5.z.iso.datetime();
|
|
6043
|
+
var TokenCountSchema = import_zod5.z.number().int().nonnegative();
|
|
6044
|
+
var TokenUsageSchema = import_zod5.z.strictObject({
|
|
6045
|
+
inputTokens: TokenCountSchema.optional(),
|
|
6046
|
+
outputTokens: TokenCountSchema.optional(),
|
|
6047
|
+
cacheCreationInputTokens: TokenCountSchema.optional(),
|
|
6048
|
+
cacheReadInputTokens: TokenCountSchema.optional()
|
|
6049
|
+
});
|
|
6050
|
+
var LIMIT_KINDS = ["rate-limited", "quota-exhausted"];
|
|
6051
|
+
var LimitClassificationSchema = import_zod5.z.strictObject({
|
|
6052
|
+
kind: import_zod5.z.enum(LIMIT_KINDS),
|
|
6053
|
+
/** Seconds to wait before retrying, from the response's `retry-after`. */
|
|
6054
|
+
retryAfterSeconds: import_zod5.z.number().nonnegative().optional(),
|
|
6055
|
+
/** When the limiting window resets, from the upstream's own reset header. */
|
|
6056
|
+
resetAt: InstantSchema.optional(),
|
|
6057
|
+
/** The window the upstream named as the binding one (`anthropic-ratelimit-unified-representative-claim`), such as `five_hour` or `seven_day`. */
|
|
6058
|
+
window: import_zod5.z.string().optional(),
|
|
6059
|
+
/** The signals the classification rests on, each `<name>=<value>` from a response header or the error body's `type` or `code`. Never the error message. */
|
|
6060
|
+
evidence: import_zod5.z.array(import_zod5.z.string())
|
|
6061
|
+
});
|
|
6062
|
+
var UsageRecordSchema = import_zod5.z.strictObject({
|
|
6063
|
+
schemaVersion: VersionSchema,
|
|
6064
|
+
/** When the front door received the request. */
|
|
6065
|
+
at: InstantSchema,
|
|
6066
|
+
/** The launching identity, when the launch resolved one. */
|
|
6067
|
+
identity: import_zod5.z.string().optional(),
|
|
6068
|
+
/** `anthropic` for an OAuth session's request to Claude Code's own API, otherwise the provider's name. */
|
|
6069
|
+
provider: import_zod5.z.string(),
|
|
6070
|
+
/** The launch's session id, generated per launch. */
|
|
6071
|
+
sessionId: import_zod5.z.string().optional(),
|
|
6072
|
+
/** The project the launch ran in (its git repository root), when the launch sent one: headroom launches do. */
|
|
6073
|
+
project: import_zod5.z.string().optional(),
|
|
6074
|
+
/** The front-door route that served the request. */
|
|
6075
|
+
route: import_zod5.z.string(),
|
|
6076
|
+
method: import_zod5.z.string(),
|
|
6077
|
+
/** The API path as the upstream sees it (`/v1/messages`), without the provider prefix or a query string. */
|
|
6078
|
+
endpoint: import_zod5.z.string(),
|
|
6079
|
+
/** The model the response named. */
|
|
6080
|
+
model: import_zod5.z.string().optional(),
|
|
6081
|
+
status: import_zod5.z.number().int(),
|
|
6082
|
+
/** Milliseconds from the request's arrival to its response head. */
|
|
6083
|
+
latencyMs: import_zod5.z.number().int().nonnegative(),
|
|
6084
|
+
/** Milliseconds from the request's arrival to the end of its response body. */
|
|
6085
|
+
durationMs: import_zod5.z.number().int().nonnegative(),
|
|
6086
|
+
/** Whether the client received the whole body. */
|
|
6087
|
+
outcome: import_zod5.z.enum(["completed", "aborted"]),
|
|
6088
|
+
usage: TokenUsageSchema.optional(),
|
|
6089
|
+
/** The response's quota and rate-limit headers, by name (see `isRateLimitHeader`). */
|
|
6090
|
+
rateLimitHeaders: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.string()).optional(),
|
|
6091
|
+
limit: LimitClassificationSchema.optional(),
|
|
6092
|
+
/** The upstream's request id (`request-id`), for matching a record to the provider's own logs. */
|
|
6093
|
+
requestId: import_zod5.z.string().optional()
|
|
6094
|
+
});
|
|
6095
|
+
var QuotaWindowSchema = import_zod5.z.strictObject({
|
|
6096
|
+
/** The fraction of the window used, as the upstream reports it (0 to 1). */
|
|
6097
|
+
utilization: import_zod5.z.number().nonnegative().optional(),
|
|
6098
|
+
resetsAt: InstantSchema.optional(),
|
|
6099
|
+
/** The window's own status (`allowed`, `allowed_warning`, `rejected`). */
|
|
6100
|
+
status: import_zod5.z.string().optional()
|
|
6101
|
+
});
|
|
6102
|
+
var UnifiedRateLimitSchema = import_zod5.z.strictObject({
|
|
6103
|
+
/** The overall status: `allowed`, `allowed_warning` or `rejected`. */
|
|
6104
|
+
status: import_zod5.z.string().optional(),
|
|
6105
|
+
fiveHour: QuotaWindowSchema.optional(),
|
|
6106
|
+
sevenDay: QuotaWindowSchema.optional(),
|
|
6107
|
+
/** The window that currently binds. */
|
|
6108
|
+
representativeClaim: import_zod5.z.string().optional(),
|
|
6109
|
+
/** When the binding window resets. */
|
|
6110
|
+
resetAt: InstantSchema.optional(),
|
|
6111
|
+
/** Whether usage beyond the plan (extra usage) is available: `allowed` or `rejected`. */
|
|
6112
|
+
overageStatus: import_zod5.z.string().optional()
|
|
6113
|
+
});
|
|
6114
|
+
var RateLimitStateSchema = import_zod5.z.strictObject({
|
|
6115
|
+
observedAt: InstantSchema,
|
|
6116
|
+
/** Every quota and rate-limit header on that response, by name. */
|
|
6117
|
+
headers: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.string()),
|
|
6118
|
+
unified: UnifiedRateLimitSchema.optional()
|
|
6119
|
+
});
|
|
6120
|
+
var LimitEventSchema = LimitClassificationSchema.extend({
|
|
6121
|
+
observedAt: InstantSchema,
|
|
6122
|
+
status: import_zod5.z.number().int()
|
|
6123
|
+
});
|
|
6124
|
+
var ProviderQuotaWindowSchema = import_zod5.z.strictObject({
|
|
6125
|
+
/** What the window measures, in the provider's terms (`tokens`, `tool-calls`). */
|
|
6126
|
+
measures: import_zod5.z.string(),
|
|
6127
|
+
/** The window's length in milliseconds, when the provider's unit is one this project knows; absent for an unrecognised unit, with `period` carrying the raw code. */
|
|
6128
|
+
periodMs: import_zod5.z.number().int().positive().optional(),
|
|
6129
|
+
/** The provider's own description of the period when `periodMs` could not be derived, such as `unit 4 x 1`. */
|
|
6130
|
+
period: import_zod5.z.string().optional(),
|
|
6131
|
+
/** The fraction of the window used (0 to 1). */
|
|
6132
|
+
utilization: import_zod5.z.number().nonnegative(),
|
|
6133
|
+
resetsAt: InstantSchema.optional(),
|
|
6134
|
+
/** Absolute counts, when the provider reports them for this window. */
|
|
6135
|
+
limit: import_zod5.z.number().nonnegative().optional(),
|
|
6136
|
+
used: import_zod5.z.number().nonnegative().optional(),
|
|
6137
|
+
remaining: import_zod5.z.number().nonnegative().optional()
|
|
6138
|
+
});
|
|
6139
|
+
var ProviderQuotaSchema = import_zod5.z.strictObject({
|
|
6140
|
+
observedAt: InstantSchema,
|
|
6141
|
+
/** Which usage endpoint produced it (`z.ai`). */
|
|
6142
|
+
source: import_zod5.z.string(),
|
|
6143
|
+
/** The subscription level the endpoint reports, when it does. */
|
|
6144
|
+
level: import_zod5.z.string().optional(),
|
|
6145
|
+
windows: import_zod5.z.array(ProviderQuotaWindowSchema)
|
|
6146
|
+
});
|
|
6147
|
+
var ProviderUsageStateSchema = import_zod5.z.strictObject({
|
|
6148
|
+
lastRequestAt: InstantSchema,
|
|
6149
|
+
lastStatus: import_zod5.z.number().int(),
|
|
6150
|
+
lastModel: import_zod5.z.string().optional(),
|
|
6151
|
+
/** The latest rate-limit state, from the last response that carried any rate-limit header. */
|
|
6152
|
+
rateLimit: RateLimitStateSchema.optional(),
|
|
6153
|
+
/** The latest classified refusal. It stays after later successes, so a reader compares its `resetAt` (or `observedAt` plus `retryAfterSeconds`) with the current time. */
|
|
6154
|
+
lastLimit: LimitEventSchema.optional(),
|
|
6155
|
+
/** The latest quota the provider's usage endpoint reported, for providers whose quota is not visible in response headers. */
|
|
6156
|
+
quota: ProviderQuotaSchema.optional()
|
|
6157
|
+
});
|
|
6158
|
+
var AccountMetadataSchema = import_zod5.z.strictObject({
|
|
6159
|
+
accountUuid: import_zod5.z.string().optional(),
|
|
6160
|
+
emailAddress: import_zod5.z.string().optional(),
|
|
6161
|
+
displayName: import_zod5.z.string().optional(),
|
|
6162
|
+
organizationUuid: import_zod5.z.string().optional(),
|
|
6163
|
+
organizationName: import_zod5.z.string().optional(),
|
|
6164
|
+
organizationType: import_zod5.z.string().optional(),
|
|
6165
|
+
organizationRole: import_zod5.z.string().optional(),
|
|
6166
|
+
workspaceRole: import_zod5.z.string().optional(),
|
|
6167
|
+
billingType: import_zod5.z.string().optional(),
|
|
6168
|
+
seatTier: import_zod5.z.string().optional(),
|
|
6169
|
+
organizationRateLimitTier: import_zod5.z.string().optional(),
|
|
6170
|
+
userRateLimitTier: import_zod5.z.string().optional(),
|
|
6171
|
+
hasExtraUsageEnabled: import_zod5.z.boolean().optional(),
|
|
6172
|
+
subscriptionCreatedAt: import_zod5.z.string().optional(),
|
|
6173
|
+
accountCreatedAt: import_zod5.z.string().optional()
|
|
6174
|
+
});
|
|
6175
|
+
var UsageSnapshotSchema = import_zod5.z.strictObject({
|
|
6176
|
+
schemaVersion: VersionSchema,
|
|
6177
|
+
identity: import_zod5.z.string(),
|
|
6178
|
+
updatedAt: InstantSchema,
|
|
6179
|
+
/** The identity's account metadata as of `updatedAt`. */
|
|
6180
|
+
account: AccountMetadataSchema.optional(),
|
|
6181
|
+
/** Latest state per provider, keyed by provider name (`anthropic` for OAuth sessions). */
|
|
6182
|
+
providers: import_zod5.z.record(import_zod5.z.string(), ProviderUsageStateSchema)
|
|
6183
|
+
});
|
|
6184
|
+
|
|
6185
|
+
// src/usage/account.ts
|
|
6186
|
+
var CLAUDE_JSON = ".claude.json";
|
|
6187
|
+
var AccountMetadataError = class extends CliError {
|
|
6188
|
+
constructor(file, detail) {
|
|
6189
|
+
super(`${file}: ${detail}, so its account metadata cannot be read.`);
|
|
6190
|
+
this.file = file;
|
|
6191
|
+
this.name = "AccountMetadataError";
|
|
6192
|
+
}
|
|
6193
|
+
file;
|
|
6194
|
+
};
|
|
6195
|
+
function isObject(value) {
|
|
6196
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6197
|
+
}
|
|
6198
|
+
function isIdentityName(name) {
|
|
6199
|
+
return IdentitySchema.shape.name.safeParse(name).success;
|
|
6200
|
+
}
|
|
6201
|
+
function claudeJsonPath(identitiesDir, identity) {
|
|
6202
|
+
return import_node_path10.default.join(identitiesDir, identity, CLAUDE_JSON);
|
|
6203
|
+
}
|
|
6204
|
+
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
6205
|
+
if (!isIdentityName(identity)) {
|
|
6206
|
+
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
6207
|
+
}
|
|
6208
|
+
const file = claudeJsonPath(identitiesDir, identity);
|
|
6209
|
+
const raw = fs12.readFileUtf8(file);
|
|
6210
|
+
if (raw === void 0) {
|
|
6211
|
+
return void 0;
|
|
6212
|
+
}
|
|
6213
|
+
let parsed;
|
|
6214
|
+
try {
|
|
6215
|
+
parsed = JSON.parse(raw);
|
|
6216
|
+
} catch {
|
|
6217
|
+
throw new AccountMetadataError(file, "not valid JSON");
|
|
6218
|
+
}
|
|
6219
|
+
if (!isObject(parsed)) {
|
|
6220
|
+
throw new AccountMetadataError(file, "not a JSON object");
|
|
6221
|
+
}
|
|
6222
|
+
const oauthAccount = parsed.oauthAccount;
|
|
6223
|
+
if (!isObject(oauthAccount)) {
|
|
6224
|
+
return void 0;
|
|
6225
|
+
}
|
|
6226
|
+
const picked = {};
|
|
6227
|
+
for (const key of Object.keys(AccountMetadataSchema.shape)) {
|
|
6228
|
+
const value = oauthAccount[key];
|
|
6229
|
+
if (value !== null && value !== void 0) {
|
|
6230
|
+
picked[key] = value;
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
const account = AccountMetadataSchema.safeParse(picked);
|
|
6234
|
+
if (!account.success) {
|
|
6235
|
+
throw new AccountMetadataError(file, account.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
6236
|
+
}
|
|
6237
|
+
return account.data;
|
|
6238
|
+
}
|
|
6239
|
+
|
|
6240
|
+
// src/frontdoor/rcSelfHostMint.ts
|
|
6241
|
+
var CREDENTIALS_FILE = ".credentials.json";
|
|
6242
|
+
var CLAUDE_JSON2 = ".claude.json";
|
|
6243
|
+
var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
|
|
6244
|
+
var RC_SELF_HOST_RECORD_FILE = "credential.json";
|
|
6245
|
+
var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
|
|
6246
|
+
var SELF_HOST_SUBSCRIPTION_TYPE = "max";
|
|
6247
|
+
function isRecord6(value) {
|
|
6248
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6249
|
+
}
|
|
6250
|
+
function readJsonObject(fs12, file) {
|
|
6251
|
+
const raw = fs12.readFileUtf8(file);
|
|
6252
|
+
if (raw === void 0) {
|
|
6253
|
+
return void 0;
|
|
6254
|
+
}
|
|
6255
|
+
let parsed;
|
|
6256
|
+
try {
|
|
6257
|
+
parsed = JSON.parse(raw);
|
|
6258
|
+
} catch {
|
|
6259
|
+
return void 0;
|
|
6260
|
+
}
|
|
6261
|
+
return isRecord6(parsed) ? parsed : void 0;
|
|
6262
|
+
}
|
|
6263
|
+
function readRcSelfHostRecord(fs12, frontdoorDir) {
|
|
6264
|
+
const parsed = readJsonObject(fs12, import_node_path11.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
|
|
6265
|
+
if (parsed === void 0) {
|
|
6266
|
+
return void 0;
|
|
6267
|
+
}
|
|
6268
|
+
const { accessToken, refreshToken, organizationUuid, accountUuid } = parsed;
|
|
6269
|
+
if (typeof accessToken !== "string" || accessToken === "" || typeof refreshToken !== "string" || refreshToken === "" || typeof organizationUuid !== "string" || organizationUuid === "" || typeof accountUuid !== "string" || accountUuid === "") {
|
|
6270
|
+
return void 0;
|
|
6271
|
+
}
|
|
6272
|
+
return { accessToken, refreshToken, organizationUuid, accountUuid };
|
|
6273
|
+
}
|
|
6274
|
+
function mintRcSelfHostCredential(deps) {
|
|
6275
|
+
if (!isIdentityName(deps.identity)) {
|
|
6276
|
+
throw new Error(`"${deps.identity}" is not a valid identity name.`);
|
|
6277
|
+
}
|
|
6278
|
+
const identityDir = import_node_path11.default.join(deps.identitiesDir, deps.identity);
|
|
6279
|
+
const credentialsFile = import_node_path11.default.join(identityDir, CREDENTIALS_FILE);
|
|
6280
|
+
const claudeJsonFile = import_node_path11.default.join(identityDir, CLAUDE_JSON2);
|
|
6281
|
+
const recordDir = import_node_path11.default.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
|
|
6282
|
+
const recordFile = import_node_path11.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
|
|
6283
|
+
const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
|
|
6284
|
+
const existingCredentials = readJsonObject(deps.fs, credentialsFile);
|
|
6285
|
+
const existingOauth = isRecord6(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
|
|
6286
|
+
const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
|
|
6287
|
+
const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
|
|
6288
|
+
if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
|
|
6289
|
+
throw new Error(
|
|
6290
|
+
`identity ${deps.identity} already holds an OAuth credential this door did not mint (a real claude.ai login): pass --force to replace it, or mint into an identity with no login. The self-hosted mode expects no Anthropic credential to be in play.`
|
|
6291
|
+
);
|
|
6292
|
+
}
|
|
6293
|
+
const accessToken = `sk-ant-oat${deps.randomToken()}`;
|
|
6294
|
+
const refreshToken = deps.randomToken();
|
|
6295
|
+
const organizationUuid = deps.newUuid();
|
|
6296
|
+
const accountUuid = deps.newUuid();
|
|
6297
|
+
const mintedAt = new Date(deps.now()).toISOString();
|
|
6298
|
+
deps.fs.mkdirPrivate(import_node_path11.default.dirname(credentialsFile));
|
|
6299
|
+
deps.fs.writeFilePrivate(credentialsFile, `${JSON.stringify({ ...existingCredentials, claudeAiOauth: { accessToken, refreshToken, expiresAt: null, scopes: [...RC_SELF_HOST_SCOPE_LIST], subscriptionType: SELF_HOST_SUBSCRIPTION_TYPE, rateLimitTier: null } }, null, 2)}
|
|
6300
|
+
`);
|
|
6301
|
+
const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
|
|
6302
|
+
const existingBlock = (value) => isRecord6(value) ? value : {};
|
|
6303
|
+
deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
|
|
6304
|
+
...claudeJson,
|
|
6305
|
+
oauthAccount: {
|
|
6306
|
+
...existingBlock(claudeJson.oauthAccount),
|
|
6307
|
+
accountUuid,
|
|
6308
|
+
emailAddress: SELF_HOST_EMAIL,
|
|
6309
|
+
organizationUuid,
|
|
6310
|
+
organizationName: "agent-shim self-hosted",
|
|
6311
|
+
// The billing and creation fields exist so the CLI's "populate the account block if it is missing" step finds a complete block and skips its profile fetch; the door answers that fetch locally anyway, so these are the quiet path, not a load-bearing claim.
|
|
6312
|
+
billingType: "stripe_subscription",
|
|
6313
|
+
accountCreatedAt: mintedAt,
|
|
6314
|
+
subscriptionCreatedAt: mintedAt
|
|
5381
6315
|
},
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
6316
|
+
cachedGrowthBookFeatures: {
|
|
6317
|
+
...existingBlock(claudeJson.cachedGrowthBookFeatures),
|
|
6318
|
+
tengu_ccr_bridge: true,
|
|
6319
|
+
tengu_bridge_repl_v2: true,
|
|
6320
|
+
tengu_bridge_repl_v2_cse_shim_enabled: true
|
|
5387
6321
|
}
|
|
5388
|
-
}
|
|
6322
|
+
}, null, 2)}
|
|
6323
|
+
`);
|
|
6324
|
+
deps.fs.mkdirPrivate(recordDir);
|
|
6325
|
+
deps.fs.writeFilePrivate(recordFile, `${JSON.stringify({ accessToken, refreshToken, organizationUuid, accountUuid }, null, 2)}
|
|
6326
|
+
`);
|
|
6327
|
+
return { identity: deps.identity, organizationUuid, credentialsFile, claudeJsonFile, recordFile, replaced: existingIsOwn };
|
|
5389
6328
|
}
|
|
5390
6329
|
|
|
5391
6330
|
// src/frontdoor/ensure.ts
|
|
@@ -5393,32 +6332,32 @@ var import_node_crypto7 = require("node:crypto");
|
|
|
5393
6332
|
|
|
5394
6333
|
// src/headroom/state.ts
|
|
5395
6334
|
var import_node_crypto6 = require("node:crypto");
|
|
5396
|
-
var
|
|
5397
|
-
var
|
|
6335
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
6336
|
+
var import_zod6 = require("zod");
|
|
5398
6337
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
5399
6338
|
var HEADROOM_STATE_SCHEMA_VERSION = 2;
|
|
5400
|
-
var HeadroomStateSchema =
|
|
6339
|
+
var HeadroomStateSchema = import_zod6.z.strictObject({
|
|
5401
6340
|
/** The supervisor process owning the daemon. Alive means someone is keeping headroom running. */
|
|
5402
|
-
supervisorPid:
|
|
6341
|
+
supervisorPid: import_zod6.z.number().int().positive().optional(),
|
|
5403
6342
|
/** The headroom proxy process itself. Absent while the supervisor is between restarts. */
|
|
5404
|
-
headroomPid:
|
|
6343
|
+
headroomPid: import_zod6.z.number().int().positive().optional(),
|
|
5405
6344
|
/**
|
|
5406
6345
|
* The unix socket the proxy serves on, inside the owner-only socket directory. Absent until the proxy has passed its readiness check, so "socketPath is set" is itself the ready signal a launcher polls for. The front door reads it on every hop, so a restart (which keeps the generation's path) is followed without anything being relaunched.
|
|
5407
6346
|
*/
|
|
5408
|
-
socketPath:
|
|
6347
|
+
socketPath: import_zod6.z.string().min(1).optional(),
|
|
5409
6348
|
/** The `headroom --version` output of the running install. */
|
|
5410
|
-
version:
|
|
6349
|
+
version: import_zod6.z.string().optional(),
|
|
5411
6350
|
/** Hash of the allowlist the running proxy was started with, so a provider-file change is detected as drift. */
|
|
5412
|
-
allowlistHash:
|
|
6351
|
+
allowlistHash: import_zod6.z.string().optional(),
|
|
5413
6352
|
/** Hash of the token-saving settings the running proxy was started with, so a settings change is detected as drift. */
|
|
5414
|
-
settingsHash:
|
|
6353
|
+
settingsHash: import_zod6.z.string().optional(),
|
|
5415
6354
|
/** The install spec the running install was last installed from, so a `source` config change is detected as drift. */
|
|
5416
|
-
installedSource:
|
|
6355
|
+
installedSource: import_zod6.z.string().optional(),
|
|
5417
6356
|
/** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
|
|
5418
|
-
lastError:
|
|
6357
|
+
lastError: import_zod6.z.string().optional()
|
|
5419
6358
|
});
|
|
5420
|
-
var HeadroomSessionSchema =
|
|
5421
|
-
var HeadroomLockSchema =
|
|
6359
|
+
var HeadroomSessionSchema = import_zod6.z.strictObject({ pid: import_zod6.z.number().int().positive(), startedAt: import_zod6.z.number(), supervisorPid: import_zod6.z.number().int().positive() });
|
|
6360
|
+
var HeadroomLockSchema = import_zod6.z.strictObject({ pid: import_zod6.z.number().int().positive(), at: import_zod6.z.number() });
|
|
5422
6361
|
function parseJson(raw, schema) {
|
|
5423
6362
|
if (raw === void 0 || raw.trim() === "") {
|
|
5424
6363
|
return void 0;
|
|
@@ -5434,7 +6373,7 @@ function readHeadroomState(fs12, stateFile) {
|
|
|
5434
6373
|
}
|
|
5435
6374
|
}
|
|
5436
6375
|
function writeHeadroomState(fs12, stateFile, state) {
|
|
5437
|
-
fs12.mkdirp(
|
|
6376
|
+
fs12.mkdirp(import_node_path12.default.dirname(stateFile));
|
|
5438
6377
|
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
5439
6378
|
`);
|
|
5440
6379
|
}
|
|
@@ -5462,7 +6401,7 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5462
6401
|
continue;
|
|
5463
6402
|
}
|
|
5464
6403
|
try {
|
|
5465
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
6404
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path12.default.join(providersDir, name)), ProviderSchema);
|
|
5466
6405
|
if (parsed !== void 0) {
|
|
5467
6406
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
5468
6407
|
}
|
|
@@ -5474,11 +6413,11 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5474
6413
|
}
|
|
5475
6414
|
function writeSession(fs12, sessionsDir, session) {
|
|
5476
6415
|
fs12.mkdirp(sessionsDir);
|
|
5477
|
-
fs12.writeFileUtf8(
|
|
6416
|
+
fs12.writeFileUtf8(import_node_path12.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
5478
6417
|
`);
|
|
5479
6418
|
}
|
|
5480
6419
|
function removeSession(fs12, sessionsDir, pid) {
|
|
5481
|
-
fs12.removeRecursive(
|
|
6420
|
+
fs12.removeRecursive(import_node_path12.default.join(sessionsDir, `${String(pid)}.json`));
|
|
5482
6421
|
}
|
|
5483
6422
|
function listSessions(fs12, sessionsDir) {
|
|
5484
6423
|
const sessions = [];
|
|
@@ -5487,7 +6426,7 @@ function listSessions(fs12, sessionsDir) {
|
|
|
5487
6426
|
continue;
|
|
5488
6427
|
}
|
|
5489
6428
|
try {
|
|
5490
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
6429
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path12.default.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
5491
6430
|
if (parsed !== void 0) {
|
|
5492
6431
|
sessions.push(parsed);
|
|
5493
6432
|
}
|
|
@@ -5592,214 +6531,7 @@ function ensureFrontDoor(params) {
|
|
|
5592
6531
|
}
|
|
5593
6532
|
|
|
5594
6533
|
// src/usage/read.ts
|
|
5595
|
-
var
|
|
5596
|
-
|
|
5597
|
-
// src/usage/account.ts
|
|
5598
|
-
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
5599
|
-
|
|
5600
|
-
// src/usage/schema.ts
|
|
5601
|
-
var import_zod6 = require("zod");
|
|
5602
|
-
var USAGE_SCHEMA_VERSION = 1;
|
|
5603
|
-
var VersionSchema = import_zod6.z.literal(USAGE_SCHEMA_VERSION);
|
|
5604
|
-
var InstantSchema = import_zod6.z.iso.datetime();
|
|
5605
|
-
var TokenCountSchema = import_zod6.z.number().int().nonnegative();
|
|
5606
|
-
var TokenUsageSchema = import_zod6.z.strictObject({
|
|
5607
|
-
inputTokens: TokenCountSchema.optional(),
|
|
5608
|
-
outputTokens: TokenCountSchema.optional(),
|
|
5609
|
-
cacheCreationInputTokens: TokenCountSchema.optional(),
|
|
5610
|
-
cacheReadInputTokens: TokenCountSchema.optional()
|
|
5611
|
-
});
|
|
5612
|
-
var LIMIT_KINDS = ["rate-limited", "quota-exhausted"];
|
|
5613
|
-
var LimitClassificationSchema = import_zod6.z.strictObject({
|
|
5614
|
-
kind: import_zod6.z.enum(LIMIT_KINDS),
|
|
5615
|
-
/** Seconds to wait before retrying, from the response's `retry-after`. */
|
|
5616
|
-
retryAfterSeconds: import_zod6.z.number().nonnegative().optional(),
|
|
5617
|
-
/** When the limiting window resets, from the upstream's own reset header. */
|
|
5618
|
-
resetAt: InstantSchema.optional(),
|
|
5619
|
-
/** The window the upstream named as the binding one (`anthropic-ratelimit-unified-representative-claim`), such as `five_hour` or `seven_day`. */
|
|
5620
|
-
window: import_zod6.z.string().optional(),
|
|
5621
|
-
/** The signals the classification rests on, each `<name>=<value>` from a response header or the error body's `type` or `code`. Never the error message. */
|
|
5622
|
-
evidence: import_zod6.z.array(import_zod6.z.string())
|
|
5623
|
-
});
|
|
5624
|
-
var UsageRecordSchema = import_zod6.z.strictObject({
|
|
5625
|
-
schemaVersion: VersionSchema,
|
|
5626
|
-
/** When the front door received the request. */
|
|
5627
|
-
at: InstantSchema,
|
|
5628
|
-
/** The launching identity, when the launch resolved one. */
|
|
5629
|
-
identity: import_zod6.z.string().optional(),
|
|
5630
|
-
/** `anthropic` for an OAuth session's request to Claude Code's own API, otherwise the provider's name. */
|
|
5631
|
-
provider: import_zod6.z.string(),
|
|
5632
|
-
/** The launch's session id, generated per launch. */
|
|
5633
|
-
sessionId: import_zod6.z.string().optional(),
|
|
5634
|
-
/** The project the launch ran in (its git repository root), when the launch sent one: headroom launches do. */
|
|
5635
|
-
project: import_zod6.z.string().optional(),
|
|
5636
|
-
/** The front-door route that served the request. */
|
|
5637
|
-
route: import_zod6.z.string(),
|
|
5638
|
-
method: import_zod6.z.string(),
|
|
5639
|
-
/** The API path as the upstream sees it (`/v1/messages`), without the provider prefix or a query string. */
|
|
5640
|
-
endpoint: import_zod6.z.string(),
|
|
5641
|
-
/** The model the response named. */
|
|
5642
|
-
model: import_zod6.z.string().optional(),
|
|
5643
|
-
status: import_zod6.z.number().int(),
|
|
5644
|
-
/** Milliseconds from the request's arrival to its response head. */
|
|
5645
|
-
latencyMs: import_zod6.z.number().int().nonnegative(),
|
|
5646
|
-
/** Milliseconds from the request's arrival to the end of its response body. */
|
|
5647
|
-
durationMs: import_zod6.z.number().int().nonnegative(),
|
|
5648
|
-
/** Whether the client received the whole body. */
|
|
5649
|
-
outcome: import_zod6.z.enum(["completed", "aborted"]),
|
|
5650
|
-
usage: TokenUsageSchema.optional(),
|
|
5651
|
-
/** The response's quota and rate-limit headers, by name (see `isRateLimitHeader`). */
|
|
5652
|
-
rateLimitHeaders: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.string()).optional(),
|
|
5653
|
-
limit: LimitClassificationSchema.optional(),
|
|
5654
|
-
/** The upstream's request id (`request-id`), for matching a record to the provider's own logs. */
|
|
5655
|
-
requestId: import_zod6.z.string().optional()
|
|
5656
|
-
});
|
|
5657
|
-
var QuotaWindowSchema = import_zod6.z.strictObject({
|
|
5658
|
-
/** The fraction of the window used, as the upstream reports it (0 to 1). */
|
|
5659
|
-
utilization: import_zod6.z.number().nonnegative().optional(),
|
|
5660
|
-
resetsAt: InstantSchema.optional(),
|
|
5661
|
-
/** The window's own status (`allowed`, `allowed_warning`, `rejected`). */
|
|
5662
|
-
status: import_zod6.z.string().optional()
|
|
5663
|
-
});
|
|
5664
|
-
var UnifiedRateLimitSchema = import_zod6.z.strictObject({
|
|
5665
|
-
/** The overall status: `allowed`, `allowed_warning` or `rejected`. */
|
|
5666
|
-
status: import_zod6.z.string().optional(),
|
|
5667
|
-
fiveHour: QuotaWindowSchema.optional(),
|
|
5668
|
-
sevenDay: QuotaWindowSchema.optional(),
|
|
5669
|
-
/** The window that currently binds. */
|
|
5670
|
-
representativeClaim: import_zod6.z.string().optional(),
|
|
5671
|
-
/** When the binding window resets. */
|
|
5672
|
-
resetAt: InstantSchema.optional(),
|
|
5673
|
-
/** Whether usage beyond the plan (extra usage) is available: `allowed` or `rejected`. */
|
|
5674
|
-
overageStatus: import_zod6.z.string().optional()
|
|
5675
|
-
});
|
|
5676
|
-
var RateLimitStateSchema = import_zod6.z.strictObject({
|
|
5677
|
-
observedAt: InstantSchema,
|
|
5678
|
-
/** Every quota and rate-limit header on that response, by name. */
|
|
5679
|
-
headers: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.string()),
|
|
5680
|
-
unified: UnifiedRateLimitSchema.optional()
|
|
5681
|
-
});
|
|
5682
|
-
var LimitEventSchema = LimitClassificationSchema.extend({
|
|
5683
|
-
observedAt: InstantSchema,
|
|
5684
|
-
status: import_zod6.z.number().int()
|
|
5685
|
-
});
|
|
5686
|
-
var ProviderQuotaWindowSchema = import_zod6.z.strictObject({
|
|
5687
|
-
/** What the window measures, in the provider's terms (`tokens`, `tool-calls`). */
|
|
5688
|
-
measures: import_zod6.z.string(),
|
|
5689
|
-
/** The window's length in milliseconds, when the provider's unit is one this project knows; absent for an unrecognised unit, with `period` carrying the raw code. */
|
|
5690
|
-
periodMs: import_zod6.z.number().int().positive().optional(),
|
|
5691
|
-
/** The provider's own description of the period when `periodMs` could not be derived, such as `unit 4 x 1`. */
|
|
5692
|
-
period: import_zod6.z.string().optional(),
|
|
5693
|
-
/** The fraction of the window used (0 to 1). */
|
|
5694
|
-
utilization: import_zod6.z.number().nonnegative(),
|
|
5695
|
-
resetsAt: InstantSchema.optional(),
|
|
5696
|
-
/** Absolute counts, when the provider reports them for this window. */
|
|
5697
|
-
limit: import_zod6.z.number().nonnegative().optional(),
|
|
5698
|
-
used: import_zod6.z.number().nonnegative().optional(),
|
|
5699
|
-
remaining: import_zod6.z.number().nonnegative().optional()
|
|
5700
|
-
});
|
|
5701
|
-
var ProviderQuotaSchema = import_zod6.z.strictObject({
|
|
5702
|
-
observedAt: InstantSchema,
|
|
5703
|
-
/** Which usage endpoint produced it (`z.ai`). */
|
|
5704
|
-
source: import_zod6.z.string(),
|
|
5705
|
-
/** The subscription level the endpoint reports, when it does. */
|
|
5706
|
-
level: import_zod6.z.string().optional(),
|
|
5707
|
-
windows: import_zod6.z.array(ProviderQuotaWindowSchema)
|
|
5708
|
-
});
|
|
5709
|
-
var ProviderUsageStateSchema = import_zod6.z.strictObject({
|
|
5710
|
-
lastRequestAt: InstantSchema,
|
|
5711
|
-
lastStatus: import_zod6.z.number().int(),
|
|
5712
|
-
lastModel: import_zod6.z.string().optional(),
|
|
5713
|
-
/** The latest rate-limit state, from the last response that carried any rate-limit header. */
|
|
5714
|
-
rateLimit: RateLimitStateSchema.optional(),
|
|
5715
|
-
/** The latest classified refusal. It stays after later successes, so a reader compares its `resetAt` (or `observedAt` plus `retryAfterSeconds`) with the current time. */
|
|
5716
|
-
lastLimit: LimitEventSchema.optional(),
|
|
5717
|
-
/** The latest quota the provider's usage endpoint reported, for providers whose quota is not visible in response headers. */
|
|
5718
|
-
quota: ProviderQuotaSchema.optional()
|
|
5719
|
-
});
|
|
5720
|
-
var AccountMetadataSchema = import_zod6.z.strictObject({
|
|
5721
|
-
accountUuid: import_zod6.z.string().optional(),
|
|
5722
|
-
emailAddress: import_zod6.z.string().optional(),
|
|
5723
|
-
displayName: import_zod6.z.string().optional(),
|
|
5724
|
-
organizationUuid: import_zod6.z.string().optional(),
|
|
5725
|
-
organizationName: import_zod6.z.string().optional(),
|
|
5726
|
-
organizationType: import_zod6.z.string().optional(),
|
|
5727
|
-
organizationRole: import_zod6.z.string().optional(),
|
|
5728
|
-
workspaceRole: import_zod6.z.string().optional(),
|
|
5729
|
-
billingType: import_zod6.z.string().optional(),
|
|
5730
|
-
seatTier: import_zod6.z.string().optional(),
|
|
5731
|
-
organizationRateLimitTier: import_zod6.z.string().optional(),
|
|
5732
|
-
userRateLimitTier: import_zod6.z.string().optional(),
|
|
5733
|
-
hasExtraUsageEnabled: import_zod6.z.boolean().optional(),
|
|
5734
|
-
subscriptionCreatedAt: import_zod6.z.string().optional(),
|
|
5735
|
-
accountCreatedAt: import_zod6.z.string().optional()
|
|
5736
|
-
});
|
|
5737
|
-
var UsageSnapshotSchema = import_zod6.z.strictObject({
|
|
5738
|
-
schemaVersion: VersionSchema,
|
|
5739
|
-
identity: import_zod6.z.string(),
|
|
5740
|
-
updatedAt: InstantSchema,
|
|
5741
|
-
/** The identity's account metadata as of `updatedAt`. */
|
|
5742
|
-
account: AccountMetadataSchema.optional(),
|
|
5743
|
-
/** Latest state per provider, keyed by provider name (`anthropic` for OAuth sessions). */
|
|
5744
|
-
providers: import_zod6.z.record(import_zod6.z.string(), ProviderUsageStateSchema)
|
|
5745
|
-
});
|
|
5746
|
-
|
|
5747
|
-
// src/usage/account.ts
|
|
5748
|
-
var CLAUDE_JSON = ".claude.json";
|
|
5749
|
-
var AccountMetadataError = class extends CliError {
|
|
5750
|
-
constructor(file, detail) {
|
|
5751
|
-
super(`${file}: ${detail}, so its account metadata cannot be read.`);
|
|
5752
|
-
this.file = file;
|
|
5753
|
-
this.name = "AccountMetadataError";
|
|
5754
|
-
}
|
|
5755
|
-
file;
|
|
5756
|
-
};
|
|
5757
|
-
function isObject(value) {
|
|
5758
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5759
|
-
}
|
|
5760
|
-
function isIdentityName(name) {
|
|
5761
|
-
return IdentitySchema.shape.name.safeParse(name).success;
|
|
5762
|
-
}
|
|
5763
|
-
function claudeJsonPath(identitiesDir, identity) {
|
|
5764
|
-
return import_node_path11.default.join(identitiesDir, identity, CLAUDE_JSON);
|
|
5765
|
-
}
|
|
5766
|
-
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
5767
|
-
if (!isIdentityName(identity)) {
|
|
5768
|
-
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
5769
|
-
}
|
|
5770
|
-
const file = claudeJsonPath(identitiesDir, identity);
|
|
5771
|
-
const raw = fs12.readFileUtf8(file);
|
|
5772
|
-
if (raw === void 0) {
|
|
5773
|
-
return void 0;
|
|
5774
|
-
}
|
|
5775
|
-
let parsed;
|
|
5776
|
-
try {
|
|
5777
|
-
parsed = JSON.parse(raw);
|
|
5778
|
-
} catch {
|
|
5779
|
-
throw new AccountMetadataError(file, "not valid JSON");
|
|
5780
|
-
}
|
|
5781
|
-
if (!isObject(parsed)) {
|
|
5782
|
-
throw new AccountMetadataError(file, "not a JSON object");
|
|
5783
|
-
}
|
|
5784
|
-
const oauthAccount = parsed.oauthAccount;
|
|
5785
|
-
if (!isObject(oauthAccount)) {
|
|
5786
|
-
return void 0;
|
|
5787
|
-
}
|
|
5788
|
-
const picked = {};
|
|
5789
|
-
for (const key of Object.keys(AccountMetadataSchema.shape)) {
|
|
5790
|
-
const value = oauthAccount[key];
|
|
5791
|
-
if (value !== null && value !== void 0) {
|
|
5792
|
-
picked[key] = value;
|
|
5793
|
-
}
|
|
5794
|
-
}
|
|
5795
|
-
const account = AccountMetadataSchema.safeParse(picked);
|
|
5796
|
-
if (!account.success) {
|
|
5797
|
-
throw new AccountMetadataError(file, account.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
5798
|
-
}
|
|
5799
|
-
return account.data;
|
|
5800
|
-
}
|
|
5801
|
-
|
|
5802
|
-
// src/usage/read.ts
|
|
6534
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
5803
6535
|
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
5804
6536
|
var DAY_MS = 864e5;
|
|
5805
6537
|
function listLogSegments(fs12, logDir) {
|
|
@@ -5820,7 +6552,7 @@ function readUsageLog(fs12, logDir, options = {}) {
|
|
|
5820
6552
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
5821
6553
|
continue;
|
|
5822
6554
|
}
|
|
5823
|
-
const contents = fs12.readFileUtf8(
|
|
6555
|
+
const contents = fs12.readFileUtf8(import_node_path13.default.join(logDir, segment.name));
|
|
5824
6556
|
if (contents === void 0) {
|
|
5825
6557
|
continue;
|
|
5826
6558
|
}
|
|
@@ -5862,7 +6594,7 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
5862
6594
|
if (!isIdentityName(identity)) {
|
|
5863
6595
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
5864
6596
|
}
|
|
5865
|
-
return
|
|
6597
|
+
return import_node_path13.default.join(snapshotsDir, `${identity}.json`);
|
|
5866
6598
|
}
|
|
5867
6599
|
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
5868
6600
|
const file = snapshotPath(snapshotsDir, identity);
|
|
@@ -5897,13 +6629,13 @@ function listUsageSnapshots(fs12, snapshotsDir) {
|
|
|
5897
6629
|
}
|
|
5898
6630
|
|
|
5899
6631
|
// src/usage/preflight.ts
|
|
5900
|
-
var
|
|
6632
|
+
var MS_PER_SECOND3 = 1e3;
|
|
5901
6633
|
var SECONDS_PER_MINUTE = 60;
|
|
5902
6634
|
var MINUTES_PER_HOUR = 60;
|
|
5903
6635
|
var HOURS_PER_DAY = 24;
|
|
5904
6636
|
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5905
6637
|
function formatAge(ms) {
|
|
5906
|
-
const minutes = Math.floor(ms / (
|
|
6638
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND3 * SECONDS_PER_MINUTE));
|
|
5907
6639
|
if (minutes < MINUTES_PER_HOUR) {
|
|
5908
6640
|
return `${String(Math.max(minutes, 0))}m`;
|
|
5909
6641
|
}
|
|
@@ -5981,13 +6713,13 @@ function planOf(account) {
|
|
|
5981
6713
|
}
|
|
5982
6714
|
|
|
5983
6715
|
// src/usage/rateLimit.ts
|
|
5984
|
-
var
|
|
6716
|
+
var MS_PER_SECOND4 = 1e3;
|
|
5985
6717
|
function epochSecondsToIso(value) {
|
|
5986
6718
|
if (value === void 0) {
|
|
5987
6719
|
return void 0;
|
|
5988
6720
|
}
|
|
5989
6721
|
const seconds = Number(value);
|
|
5990
|
-
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds *
|
|
6722
|
+
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND4).toISOString() : void 0;
|
|
5991
6723
|
}
|
|
5992
6724
|
function nonNegativeNumber(value) {
|
|
5993
6725
|
if (value === void 0 || value.trim() === "") {
|
|
@@ -6044,7 +6776,7 @@ var ErrorObjectSchema = import_zod8.z.looseObject({
|
|
|
6044
6776
|
var ANTHROPIC_PROVIDER = "anthropic";
|
|
6045
6777
|
|
|
6046
6778
|
// src/usage/pick.ts
|
|
6047
|
-
var
|
|
6779
|
+
var MS_PER_SECOND5 = 1e3;
|
|
6048
6780
|
var MS_PER_HOUR = 36e5;
|
|
6049
6781
|
var HOURS_PER_DAY2 = 24;
|
|
6050
6782
|
var FIVE_HOURS = 5;
|
|
@@ -6066,7 +6798,7 @@ function limitBlockedUntil(state, nowMs) {
|
|
|
6066
6798
|
if (Date.parse(state.lastRequestAt) > Date.parse(limit.observedAt)) {
|
|
6067
6799
|
return void 0;
|
|
6068
6800
|
}
|
|
6069
|
-
const until = limit.resetAt === void 0 ? limit.retryAfterSeconds === void 0 ? void 0 : Date.parse(limit.observedAt) + limit.retryAfterSeconds *
|
|
6801
|
+
const until = limit.resetAt === void 0 ? limit.retryAfterSeconds === void 0 ? void 0 : Date.parse(limit.observedAt) + limit.retryAfterSeconds * MS_PER_SECOND5 : Date.parse(limit.resetAt);
|
|
6070
6802
|
return until !== void 0 && until > nowMs ? until : void 0;
|
|
6071
6803
|
}
|
|
6072
6804
|
function windowBlockedUntil(windows) {
|
|
@@ -6143,10 +6875,10 @@ function assess(member, nowMs) {
|
|
|
6143
6875
|
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
6144
6876
|
}
|
|
6145
6877
|
const remaining = Math.max(0, 1 - basis.window.utilization);
|
|
6146
|
-
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs,
|
|
6878
|
+
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs, MS_PER_SECOND5);
|
|
6147
6879
|
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
6148
6880
|
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
6149
|
-
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs,
|
|
6881
|
+
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND5);
|
|
6150
6882
|
const burn = fiveHourBurn(member.records);
|
|
6151
6883
|
return {
|
|
6152
6884
|
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
@@ -6196,7 +6928,7 @@ function rankPool(input) {
|
|
|
6196
6928
|
}
|
|
6197
6929
|
|
|
6198
6930
|
// src/usage/poolPick.ts
|
|
6199
|
-
var
|
|
6931
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
6200
6932
|
var import_zod9 = require("zod");
|
|
6201
6933
|
|
|
6202
6934
|
// src/usage/store.ts
|
|
@@ -6263,13 +6995,13 @@ function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
|
6263
6995
|
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
6264
6996
|
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
6265
6997
|
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
6266
|
-
fs12.mkdirPrivate(
|
|
6998
|
+
fs12.mkdirPrivate(import_node_path14.default.dirname(picksFile));
|
|
6267
6999
|
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
6268
7000
|
`);
|
|
6269
7001
|
}
|
|
6270
7002
|
|
|
6271
7003
|
// src/launcher/identity.ts
|
|
6272
|
-
var
|
|
7004
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
6273
7005
|
function poolNameOf(selector) {
|
|
6274
7006
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
6275
7007
|
}
|
|
@@ -6318,7 +7050,7 @@ function decideConfigProfile(params) {
|
|
|
6318
7050
|
return { source: "none" };
|
|
6319
7051
|
}
|
|
6320
7052
|
function loadIdentity(identitiesDir, name, fs12) {
|
|
6321
|
-
return loadConfigFile(
|
|
7053
|
+
return loadConfigFile(import_node_path15.default.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
6322
7054
|
}
|
|
6323
7055
|
|
|
6324
7056
|
// src/launcher/pool.ts
|
|
@@ -6533,7 +7265,7 @@ function hashSettings(settings) {
|
|
|
6533
7265
|
}
|
|
6534
7266
|
|
|
6535
7267
|
// src/headroom/socket.ts
|
|
6536
|
-
var
|
|
7268
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
6537
7269
|
var GROUP_OR_OTHER_BITS = 63;
|
|
6538
7270
|
var OCTAL_RADIX = 8;
|
|
6539
7271
|
var MODE_DIGITS = 4;
|
|
@@ -6542,7 +7274,7 @@ var HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
|
|
|
6542
7274
|
var UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
6543
7275
|
var PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
|
|
6544
7276
|
function headroomSocketPath(paths, supervisorPid) {
|
|
6545
|
-
return
|
|
7277
|
+
return import_node_path16.default.join(paths.headroomSocketDir, `${String(supervisorPid)}${HEADROOM_SOCKET_SUFFIX}`);
|
|
6546
7278
|
}
|
|
6547
7279
|
function platformRefusal(platform) {
|
|
6548
7280
|
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;
|
|
@@ -6591,7 +7323,7 @@ function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
|
|
|
6591
7323
|
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
6592
7324
|
continue;
|
|
6593
7325
|
}
|
|
6594
|
-
fs12.removeRecursive(
|
|
7326
|
+
fs12.removeRecursive(import_node_path16.default.join(paths.headroomSocketDir, name));
|
|
6595
7327
|
removed.push(name);
|
|
6596
7328
|
}
|
|
6597
7329
|
return removed;
|
|
@@ -6915,14 +7647,14 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
6915
7647
|
// src/paths.ts
|
|
6916
7648
|
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
6917
7649
|
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
6918
|
-
var
|
|
7650
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
6919
7651
|
function resolveAgentShimHome(env = process.env, home = import_node_os2.default.homedir(), isDirectory = isExistingDirectory) {
|
|
6920
7652
|
const fromEnv = env.AGENT_SHIM_HOME;
|
|
6921
7653
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6922
7654
|
return fromEnv;
|
|
6923
7655
|
}
|
|
6924
|
-
const current =
|
|
6925
|
-
const legacy =
|
|
7656
|
+
const current = import_node_path17.default.join(home, HOME_DIRNAME);
|
|
7657
|
+
const legacy = import_node_path17.default.join(home, LEGACY_HOME_DIRNAME);
|
|
6926
7658
|
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
6927
7659
|
}
|
|
6928
7660
|
function isExistingDirectory(candidate) {
|
|
@@ -6933,41 +7665,41 @@ function resolveClaudeHome() {
|
|
|
6933
7665
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6934
7666
|
return fromEnv;
|
|
6935
7667
|
}
|
|
6936
|
-
return
|
|
7668
|
+
return import_node_path17.default.join(import_node_os2.default.homedir(), ".claude");
|
|
6937
7669
|
}
|
|
6938
7670
|
function buildLayoutPaths(root) {
|
|
6939
7671
|
return {
|
|
6940
7672
|
root,
|
|
6941
|
-
identitiesDir:
|
|
6942
|
-
configProfilesDir:
|
|
6943
|
-
providersDir:
|
|
6944
|
-
directoryRulesFile:
|
|
6945
|
-
activeIdentityFile:
|
|
6946
|
-
globalConfigFile:
|
|
6947
|
-
categoriesLocalFile:
|
|
6948
|
-
claudeShimFile:
|
|
6949
|
-
headroomDir:
|
|
6950
|
-
headroomStateFile:
|
|
6951
|
-
headroomSocketDir:
|
|
6952
|
-
headroomLockFile:
|
|
6953
|
-
headroomSessionsDir:
|
|
6954
|
-
logsDir:
|
|
6955
|
-
headroomLogPath:
|
|
6956
|
-
frontdoorDir:
|
|
6957
|
-
frontdoorCaDir:
|
|
6958
|
-
frontdoorCaCertFile:
|
|
6959
|
-
frontdoorCaKeyFile:
|
|
6960
|
-
frontdoorCaBundlesDir:
|
|
6961
|
-
frontdoorStateFile:
|
|
6962
|
-
frontdoorLockFile:
|
|
6963
|
-
frontdoorControlTokenFile:
|
|
6964
|
-
frontdoorSessionsDir:
|
|
6965
|
-
frontdoorRcCredentialsDir:
|
|
6966
|
-
frontdoorLogPath:
|
|
6967
|
-
usageDir:
|
|
6968
|
-
usageLogDir:
|
|
6969
|
-
usageSnapshotsDir:
|
|
6970
|
-
usagePicksFile:
|
|
7673
|
+
identitiesDir: import_node_path17.default.join(root, "identities"),
|
|
7674
|
+
configProfilesDir: import_node_path17.default.join(root, "config-profiles"),
|
|
7675
|
+
providersDir: import_node_path17.default.join(root, "providers"),
|
|
7676
|
+
directoryRulesFile: import_node_path17.default.join(root, "directory-rules.json"),
|
|
7677
|
+
activeIdentityFile: import_node_path17.default.join(root, "active-identity"),
|
|
7678
|
+
globalConfigFile: import_node_path17.default.join(root, "config.json"),
|
|
7679
|
+
categoriesLocalFile: import_node_path17.default.join(root, "categories.local.json"),
|
|
7680
|
+
claudeShimFile: import_node_path17.default.join(root, "claude-shim.json"),
|
|
7681
|
+
headroomDir: import_node_path17.default.join(root, "headroom"),
|
|
7682
|
+
headroomStateFile: import_node_path17.default.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
7683
|
+
headroomSocketDir: import_node_path17.default.join(root, "headroom", "run"),
|
|
7684
|
+
headroomLockFile: import_node_path17.default.join(root, "headroom", "start.lock"),
|
|
7685
|
+
headroomSessionsDir: import_node_path17.default.join(root, "headroom", "sessions"),
|
|
7686
|
+
logsDir: import_node_path17.default.join(root, "logs"),
|
|
7687
|
+
headroomLogPath: import_node_path17.default.join(root, "logs", "headroom.log"),
|
|
7688
|
+
frontdoorDir: import_node_path17.default.join(root, "frontdoor"),
|
|
7689
|
+
frontdoorCaDir: import_node_path17.default.join(root, "frontdoor", "ca"),
|
|
7690
|
+
frontdoorCaCertFile: import_node_path17.default.join(root, "frontdoor", "ca", "ca.pem"),
|
|
7691
|
+
frontdoorCaKeyFile: import_node_path17.default.join(root, "frontdoor", "ca", "ca.key"),
|
|
7692
|
+
frontdoorCaBundlesDir: import_node_path17.default.join(root, "frontdoor", "ca", "bundles"),
|
|
7693
|
+
frontdoorStateFile: import_node_path17.default.join(root, "frontdoor", "state.json"),
|
|
7694
|
+
frontdoorLockFile: import_node_path17.default.join(root, "frontdoor", "start.lock"),
|
|
7695
|
+
frontdoorControlTokenFile: import_node_path17.default.join(root, "frontdoor", "control-token"),
|
|
7696
|
+
frontdoorSessionsDir: import_node_path17.default.join(root, "frontdoor", "sessions"),
|
|
7697
|
+
frontdoorRcCredentialsDir: import_node_path17.default.join(root, "frontdoor", "rc-credentials"),
|
|
7698
|
+
frontdoorLogPath: import_node_path17.default.join(root, "logs", "frontdoor.log"),
|
|
7699
|
+
usageDir: import_node_path17.default.join(root, "usage"),
|
|
7700
|
+
usageLogDir: import_node_path17.default.join(root, "usage", "log"),
|
|
7701
|
+
usageSnapshotsDir: import_node_path17.default.join(root, "usage", "snapshots"),
|
|
7702
|
+
usagePicksFile: import_node_path17.default.join(root, "usage", "picks.json")
|
|
6971
7703
|
};
|
|
6972
7704
|
}
|
|
6973
7705
|
function resolveLayoutPaths() {
|
|
@@ -6976,11 +7708,11 @@ function resolveLayoutPaths() {
|
|
|
6976
7708
|
|
|
6977
7709
|
// src/identityStore.ts
|
|
6978
7710
|
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
6979
|
-
var
|
|
7711
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
6980
7712
|
|
|
6981
7713
|
// src/configProfilesStore.ts
|
|
6982
7714
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
6983
|
-
var
|
|
7715
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
6984
7716
|
var ProfileNotFoundError = class extends CliError {
|
|
6985
7717
|
constructor(profileName) {
|
|
6986
7718
|
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
@@ -7006,7 +7738,7 @@ var InvalidCategoryNameError = class extends CliError {
|
|
|
7006
7738
|
categoryName;
|
|
7007
7739
|
};
|
|
7008
7740
|
function profileJsonPath(paths, name) {
|
|
7009
|
-
return
|
|
7741
|
+
return import_node_path18.default.join(paths.configProfilesDir, `${name}.json`);
|
|
7010
7742
|
}
|
|
7011
7743
|
function profileExists(paths, name) {
|
|
7012
7744
|
return import_node_fs3.default.existsSync(profileJsonPath(paths, name));
|
|
@@ -7194,7 +7926,7 @@ var InvalidIdentityNameError = class extends CliError {
|
|
|
7194
7926
|
attemptedName;
|
|
7195
7927
|
};
|
|
7196
7928
|
function identityJsonPath(paths, name) {
|
|
7197
|
-
return
|
|
7929
|
+
return import_node_path19.default.join(paths.identitiesDir, name, "identity.json");
|
|
7198
7930
|
}
|
|
7199
7931
|
function identityExists(paths, name) {
|
|
7200
7932
|
return import_node_fs4.default.existsSync(identityJsonPath(paths, name));
|
|
@@ -7295,7 +8027,7 @@ function setIdentityCredential(paths, identityName, change) {
|
|
|
7295
8027
|
});
|
|
7296
8028
|
}
|
|
7297
8029
|
function belongsToIdentity(entry, name) {
|
|
7298
|
-
return entry === name || entry ===
|
|
8030
|
+
return entry === name || entry === import_node_path19.default.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
7299
8031
|
}
|
|
7300
8032
|
function removeIdentity(paths, name) {
|
|
7301
8033
|
if (!identityExists(paths, name)) {
|
|
@@ -7303,7 +8035,7 @@ function removeIdentity(paths, name) {
|
|
|
7303
8035
|
}
|
|
7304
8036
|
for (const entry of import_node_fs4.default.readdirSync(paths.identitiesDir)) {
|
|
7305
8037
|
if (belongsToIdentity(entry, name)) {
|
|
7306
|
-
import_node_fs4.default.rmSync(
|
|
8038
|
+
import_node_fs4.default.rmSync(import_node_path19.default.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
7307
8039
|
}
|
|
7308
8040
|
}
|
|
7309
8041
|
if (readActiveIdentity(paths) === name) {
|
|
@@ -7313,7 +8045,7 @@ function removeIdentity(paths, name) {
|
|
|
7313
8045
|
|
|
7314
8046
|
// src/providersStore.ts
|
|
7315
8047
|
var import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
7316
|
-
var
|
|
8048
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
7317
8049
|
|
|
7318
8050
|
// src/credentialCache.ts
|
|
7319
8051
|
var import_zod12 = require("zod");
|
|
@@ -7578,7 +8310,7 @@ var InvalidProviderNameError = class extends CliError {
|
|
|
7578
8310
|
};
|
|
7579
8311
|
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
7580
8312
|
function providerJsonPath(paths, name) {
|
|
7581
|
-
return
|
|
8313
|
+
return import_node_path20.default.join(paths.providersDir, `${name}.json`);
|
|
7582
8314
|
}
|
|
7583
8315
|
function providerExists(paths, name) {
|
|
7584
8316
|
return import_node_fs5.default.existsSync(providerJsonPath(paths, name));
|
|
@@ -7586,14 +8318,14 @@ function providerExists(paths, name) {
|
|
|
7586
8318
|
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
7587
8319
|
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
7588
8320
|
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
7589
|
-
function
|
|
8321
|
+
function isRecord7(value) {
|
|
7590
8322
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7591
8323
|
}
|
|
7592
8324
|
function legacyProviderConversion(raw) {
|
|
7593
|
-
if (!
|
|
8325
|
+
if (!isRecord7(raw) || "credential" in raw) {
|
|
7594
8326
|
return void 0;
|
|
7595
8327
|
}
|
|
7596
|
-
const env =
|
|
8328
|
+
const env = isRecord7(raw.env) ? raw.env : void 0;
|
|
7597
8329
|
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
7598
8330
|
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
7599
8331
|
if (fields.length === 0) {
|
|
@@ -7654,7 +8386,7 @@ function readProvider(paths, name) {
|
|
|
7654
8386
|
return parseProviderFile(filePath, raw);
|
|
7655
8387
|
}
|
|
7656
8388
|
function loadProvider(providersDir, name, port) {
|
|
7657
|
-
const filePath =
|
|
8389
|
+
const filePath = import_node_path20.default.join(providersDir, `${name}.json`);
|
|
7658
8390
|
const raw = port.readConfigFile(filePath);
|
|
7659
8391
|
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
7660
8392
|
}
|
|
@@ -7909,21 +8641,21 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
7909
8641
|
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
7910
8642
|
|
|
7911
8643
|
// src/launcher/cascade.ts
|
|
7912
|
-
var
|
|
8644
|
+
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
7913
8645
|
|
|
7914
8646
|
// src/pathNorm.ts
|
|
7915
|
-
var
|
|
8647
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
7916
8648
|
function expandTilde(value, home) {
|
|
7917
8649
|
if (value === "~") {
|
|
7918
8650
|
return home;
|
|
7919
8651
|
}
|
|
7920
8652
|
if (value.startsWith("~/")) {
|
|
7921
|
-
return
|
|
8653
|
+
return import_node_path21.default.join(home, value.slice(2));
|
|
7922
8654
|
}
|
|
7923
8655
|
return value;
|
|
7924
8656
|
}
|
|
7925
8657
|
function normaliseRulePath(rulePath, home) {
|
|
7926
|
-
return
|
|
8658
|
+
return import_node_path21.default.resolve(expandTilde(rulePath, home));
|
|
7927
8659
|
}
|
|
7928
8660
|
|
|
7929
8661
|
// src/launcher/cascade.ts
|
|
@@ -7933,7 +8665,7 @@ function loadCascadeInput(params) {
|
|
|
7933
8665
|
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
7934
8666
|
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
7935
8667
|
const loadProfile = (name) => {
|
|
7936
|
-
const filepath =
|
|
8668
|
+
const filepath = import_node_path22.default.join(params.paths.configProfilesDir, `${name}.json`);
|
|
7937
8669
|
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
7938
8670
|
if (loaded === void 0) {
|
|
7939
8671
|
return void 0;
|
|
@@ -7948,8 +8680,8 @@ function loadCascadeInput(params) {
|
|
|
7948
8680
|
});
|
|
7949
8681
|
const levels = [];
|
|
7950
8682
|
for (const dir of dirs) {
|
|
7951
|
-
const portable = loadConfigFile(
|
|
7952
|
-
const portableLocal = loadConfigFile(
|
|
8683
|
+
const portable = loadConfigFile(import_node_path22.default.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path22.default.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
8684
|
+
const portableLocal = loadConfigFile(import_node_path22.default.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path22.default.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7953
8685
|
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
7954
8686
|
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
7955
8687
|
return {
|
|
@@ -8000,7 +8732,7 @@ function readDirectorySelections(loaded) {
|
|
|
8000
8732
|
// src/realCredentialCache.ts
|
|
8001
8733
|
var import_node_child_process = require("node:child_process");
|
|
8002
8734
|
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
8003
|
-
var
|
|
8735
|
+
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
8004
8736
|
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
8005
8737
|
var CACHE_DIR_MODE = 448;
|
|
8006
8738
|
var CACHE_FILE_MODE = 384;
|
|
@@ -8027,8 +8759,8 @@ function security(args, stdin) {
|
|
|
8027
8759
|
return { status: result.status, stdout: result.stdout };
|
|
8028
8760
|
}
|
|
8029
8761
|
function createRealCredentialCache(root) {
|
|
8030
|
-
const dir =
|
|
8031
|
-
const filePath = (owner) =>
|
|
8762
|
+
const dir = import_node_path23.default.join(root, "credential-cache");
|
|
8763
|
+
const filePath = (owner) => import_node_path23.default.join(dir, cacheFileNameFor(owner));
|
|
8032
8764
|
return {
|
|
8033
8765
|
read(store, owner) {
|
|
8034
8766
|
if (store === "file") {
|
|
@@ -8080,7 +8812,7 @@ var import_node_child_process2 = require("node:child_process");
|
|
|
8080
8812
|
var import_node_crypto9 = require("node:crypto");
|
|
8081
8813
|
var import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
8082
8814
|
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
8083
|
-
var
|
|
8815
|
+
var import_node_path24 = __toESM(require("node:path"), 1);
|
|
8084
8816
|
var import_node_sea = require("node:sea");
|
|
8085
8817
|
function isEnoent3(error) {
|
|
8086
8818
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
@@ -8239,7 +8971,7 @@ var realRunPort = {
|
|
|
8239
8971
|
}
|
|
8240
8972
|
};
|
|
8241
8973
|
function expandHome2(filePath) {
|
|
8242
|
-
return filePath.startsWith("~/") ?
|
|
8974
|
+
return filePath.startsWith("~/") ? import_node_path24.default.join(import_node_os3.default.homedir(), filePath.slice(1)) : filePath;
|
|
8243
8975
|
}
|
|
8244
8976
|
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
8245
8977
|
function hasDesktopSession() {
|
|
@@ -8312,7 +9044,7 @@ function spawnDaemonThrough(executable) {
|
|
|
8312
9044
|
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
8313
9045
|
}
|
|
8314
9046
|
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
8315
|
-
import_node_fs7.default.mkdirSync(
|
|
9047
|
+
import_node_fs7.default.mkdirSync(import_node_path24.default.dirname(logPath), { recursive: true });
|
|
8316
9048
|
const logFd = import_node_fs7.default.openSync(logPath, "a");
|
|
8317
9049
|
try {
|
|
8318
9050
|
const child = (0, import_node_child_process2.spawn)(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
@@ -8394,7 +9126,7 @@ function listVersionsDir(dir) {
|
|
|
8394
9126
|
throw error;
|
|
8395
9127
|
}
|
|
8396
9128
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
8397
|
-
const fullPath =
|
|
9129
|
+
const fullPath = import_node_path24.default.join(dir, entry.name);
|
|
8398
9130
|
const stat = import_node_fs7.default.statSync(fullPath);
|
|
8399
9131
|
return {
|
|
8400
9132
|
name: entry.name,
|
|
@@ -8409,7 +9141,7 @@ function windowsPathExtensions(pathext) {
|
|
|
8409
9141
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
8410
9142
|
}
|
|
8411
9143
|
function resolveExecutableCandidate(dir, name, env) {
|
|
8412
|
-
const candidate =
|
|
9144
|
+
const candidate = import_node_path24.default.join(dir, name);
|
|
8413
9145
|
if (env.platform === "win32") {
|
|
8414
9146
|
const extensions = windowsPathExtensions(env.pathext);
|
|
8415
9147
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -8458,13 +9190,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
8458
9190
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
8459
9191
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
8460
9192
|
}
|
|
8461
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
8462
|
-
const candidateDir =
|
|
8463
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
9193
|
+
const candidate = env.argv1 === void 0 ? env.execPath : import_node_path24.default.resolve(env.cwd, env.argv1);
|
|
9194
|
+
const candidateDir = import_node_path24.default.dirname(candidate);
|
|
9195
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => import_node_path24.default.resolve(dir) === candidateDir);
|
|
8464
9196
|
if (candidateDirOnPath) {
|
|
8465
9197
|
return candidate;
|
|
8466
9198
|
}
|
|
8467
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
9199
|
+
const viaPath = searchPathForExecutable(env.pathDirs, import_node_path24.default.basename(candidate), env.findExecutableInDir);
|
|
8468
9200
|
return viaPath ?? candidate;
|
|
8469
9201
|
}
|
|
8470
9202
|
function realOwnExecutablePath() {
|
|
@@ -8472,7 +9204,7 @@ function realOwnExecutablePath() {
|
|
|
8472
9204
|
argv1: process.argv[1],
|
|
8473
9205
|
execPath: process.execPath,
|
|
8474
9206
|
cwd: process.cwd(),
|
|
8475
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
9207
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path24.default.delimiter).filter((dir) => dir !== ""),
|
|
8476
9208
|
findExecutableInDir
|
|
8477
9209
|
});
|
|
8478
9210
|
}
|
|
@@ -8485,7 +9217,7 @@ function realContentSourcePath() {
|
|
|
8485
9217
|
function realResolveClaudeBinary(isOwnBinary) {
|
|
8486
9218
|
return (request4) => discoverClaudeBinary({
|
|
8487
9219
|
listVersionsDir,
|
|
8488
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
9220
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path24.default.delimiter).filter((dir) => dir !== ""),
|
|
8489
9221
|
findExecutableInDir,
|
|
8490
9222
|
isOwnBinary,
|
|
8491
9223
|
...request4?.version === void 0 ? {} : { version: request4.version }
|
|
@@ -8496,7 +9228,7 @@ function realInstalledClaudeVersions() {
|
|
|
8496
9228
|
}
|
|
8497
9229
|
|
|
8498
9230
|
// src/checkReport.ts
|
|
8499
|
-
var
|
|
9231
|
+
var import_node_path25 = __toESM(require("node:path"), 1);
|
|
8500
9232
|
var import_zod13 = require("zod");
|
|
8501
9233
|
|
|
8502
9234
|
// src/cli/bool.ts
|
|
@@ -8928,10 +9660,10 @@ function collectCheckReport(params) {
|
|
|
8928
9660
|
}).input;
|
|
8929
9661
|
const providerName = cascadeProviderName(cascade);
|
|
8930
9662
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
8931
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
9663
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : import_node_path25.default.join(paths.identitiesDir, identityDecision.name);
|
|
8932
9664
|
const settingsFiles = {
|
|
8933
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
8934
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
9665
|
+
"settings.json": realFsPort.readFileUtf8(import_node_path25.default.join(claudeHome, "settings.json")),
|
|
9666
|
+
"settings.local.json": realFsPort.readFileUtf8(import_node_path25.default.join(claudeHome, "settings.local.json"))
|
|
8935
9667
|
};
|
|
8936
9668
|
const report = runCheck({
|
|
8937
9669
|
cwd,
|
|
@@ -8963,7 +9695,7 @@ function collectCheckReport(params) {
|
|
|
8963
9695
|
|
|
8964
9696
|
// src/claudeShim.ts
|
|
8965
9697
|
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
8966
|
-
var
|
|
9698
|
+
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
8967
9699
|
var import_zod14 = require("zod");
|
|
8968
9700
|
var ClaudeShimStateSchema = import_zod14.z.strictObject({
|
|
8969
9701
|
targetPath: import_zod14.z.string().min(1),
|
|
@@ -8975,10 +9707,10 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
8975
9707
|
}
|
|
8976
9708
|
function resolveOwnBinaryCheck(paths, contentSourcePath) {
|
|
8977
9709
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
8978
|
-
const recordedTarget = state === void 0 ? void 0 :
|
|
9710
|
+
const recordedTarget = state === void 0 ? void 0 : import_node_path26.default.resolve(state.targetPath);
|
|
8979
9711
|
let ownStat;
|
|
8980
9712
|
return (candidate) => {
|
|
8981
|
-
if (recordedTarget !== void 0 &&
|
|
9713
|
+
if (recordedTarget !== void 0 && import_node_path26.default.resolve(candidate) === recordedTarget) {
|
|
8982
9714
|
return true;
|
|
8983
9715
|
}
|
|
8984
9716
|
ownStat ??= statOrUndefined(contentSourcePath);
|
|
@@ -9002,20 +9734,20 @@ function statOrUndefined(target) {
|
|
|
9002
9734
|
}
|
|
9003
9735
|
var nodeLinkFs = { link: import_node_fs8.default.linkSync, copyFile: import_node_fs8.default.copyFileSync, chmod: import_node_fs8.default.chmodSync };
|
|
9004
9736
|
function findPathShadow(params) {
|
|
9005
|
-
const resolvedTargetDir =
|
|
9737
|
+
const resolvedTargetDir = import_node_path26.default.resolve(params.targetDir);
|
|
9006
9738
|
for (const dir of params.pathDirs) {
|
|
9007
9739
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
9008
9740
|
if (found === void 0) {
|
|
9009
9741
|
continue;
|
|
9010
9742
|
}
|
|
9011
|
-
return
|
|
9743
|
+
return import_node_path26.default.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
9012
9744
|
}
|
|
9013
|
-
return params.pathDirs.some((dir) =>
|
|
9745
|
+
return params.pathDirs.some((dir) => import_node_path26.default.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
9014
9746
|
}
|
|
9015
9747
|
|
|
9016
9748
|
// src/doctorReport.ts
|
|
9017
9749
|
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
9018
|
-
var
|
|
9750
|
+
var import_node_path27 = __toESM(require("node:path"), 1);
|
|
9019
9751
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
9020
9752
|
if (status.status !== "shadowed") {
|
|
9021
9753
|
return status;
|
|
@@ -9039,7 +9771,7 @@ function validateJson(schema, input) {
|
|
|
9039
9771
|
return { ok: true, data: result.data };
|
|
9040
9772
|
}
|
|
9041
9773
|
function pushPathResolution(push, resolution) {
|
|
9042
|
-
const ownDir =
|
|
9774
|
+
const ownDir = import_node_path27.default.dirname(resolution.ownExecutablePath);
|
|
9043
9775
|
switch (resolution.agentShim.status) {
|
|
9044
9776
|
case "ok":
|
|
9045
9777
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -9118,14 +9850,14 @@ function pushProvider(push, entry, cacheEnv) {
|
|
|
9118
9850
|
}
|
|
9119
9851
|
function pushLegacyName(push, params) {
|
|
9120
9852
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
9121
|
-
if (legacyVariables.length === 0 &&
|
|
9853
|
+
if (legacyVariables.length === 0 && import_node_path27.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
9122
9854
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
9123
9855
|
return;
|
|
9124
9856
|
}
|
|
9125
9857
|
for (const name of legacyVariables) {
|
|
9126
9858
|
push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
|
|
9127
9859
|
}
|
|
9128
|
-
if (
|
|
9860
|
+
if (import_node_path27.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
9129
9861
|
push(
|
|
9130
9862
|
"legacy-name",
|
|
9131
9863
|
"warn",
|
|
@@ -9444,18 +10176,18 @@ function collectDoctorReport(params) {
|
|
|
9444
10176
|
const { paths } = params;
|
|
9445
10177
|
const identityNames = import_node_fs9.default.existsSync(paths.identitiesDir) ? import_node_fs9.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
9446
10178
|
const identities = identityNames.map((name) => {
|
|
9447
|
-
const farmRoot =
|
|
9448
|
-
const identityPath =
|
|
10179
|
+
const farmRoot = import_node_path27.default.join(paths.identitiesDir, name);
|
|
10180
|
+
const identityPath = import_node_path27.default.join(farmRoot, "identity.json");
|
|
9449
10181
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
9450
10182
|
});
|
|
9451
10183
|
const profileNames = import_node_fs9.default.existsSync(paths.configProfilesDir) ? import_node_fs9.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
9452
10184
|
const configProfiles = profileNames.map((name) => {
|
|
9453
|
-
const profilePath =
|
|
10185
|
+
const profilePath = import_node_path27.default.join(paths.configProfilesDir, `${name}.json`);
|
|
9454
10186
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
9455
10187
|
});
|
|
9456
10188
|
const providerNames = import_node_fs9.default.existsSync(paths.providersDir) ? import_node_fs9.default.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
9457
10189
|
const providers = providerNames.map((name) => {
|
|
9458
|
-
const providerPath =
|
|
10190
|
+
const providerPath = import_node_path27.default.join(paths.providersDir, `${name}.json`);
|
|
9459
10191
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
9460
10192
|
});
|
|
9461
10193
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -9467,12 +10199,12 @@ function collectDoctorReport(params) {
|
|
|
9467
10199
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
9468
10200
|
}
|
|
9469
10201
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
9470
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
10202
|
+
const pathDirs = (params.env.PATH ?? "").split(import_node_path27.default.delimiter).filter((dir) => dir !== "");
|
|
9471
10203
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
9472
10204
|
findPathShadow({
|
|
9473
10205
|
pathDirs,
|
|
9474
|
-
targetDir:
|
|
9475
|
-
targetFilename:
|
|
10206
|
+
targetDir: import_node_path27.default.dirname(shimState.targetPath),
|
|
10207
|
+
targetFilename: import_node_path27.default.basename(shimState.targetPath),
|
|
9476
10208
|
findExecutableInDir
|
|
9477
10209
|
}),
|
|
9478
10210
|
shimState.targetPath,
|
|
@@ -9497,7 +10229,7 @@ function collectDoctorReport(params) {
|
|
|
9497
10229
|
agentShim: refinePathShadow(
|
|
9498
10230
|
findPathShadow({
|
|
9499
10231
|
pathDirs,
|
|
9500
|
-
targetDir:
|
|
10232
|
+
targetDir: import_node_path27.default.dirname(ownExecutablePath),
|
|
9501
10233
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
9502
10234
|
findExecutableInDir
|
|
9503
10235
|
}),
|
|
@@ -9518,7 +10250,7 @@ function collectDoctorReport(params) {
|
|
|
9518
10250
|
|
|
9519
10251
|
// src/launcher.ts
|
|
9520
10252
|
var import_node_crypto10 = require("node:crypto");
|
|
9521
|
-
var
|
|
10253
|
+
var import_node_path29 = __toESM(require("node:path"), 1);
|
|
9522
10254
|
|
|
9523
10255
|
// src/launcher/argv.ts
|
|
9524
10256
|
var ConflictingIdentityError = class extends UsageError {
|
|
@@ -9783,7 +10515,7 @@ function buildCliOverride(params) {
|
|
|
9783
10515
|
}
|
|
9784
10516
|
|
|
9785
10517
|
// src/launcher/flags.ts
|
|
9786
|
-
var
|
|
10518
|
+
var import_node_path28 = __toESM(require("node:path"), 1);
|
|
9787
10519
|
|
|
9788
10520
|
// src/headroom/headers.ts
|
|
9789
10521
|
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
@@ -9853,7 +10585,7 @@ function buildEnv(params) {
|
|
|
9853
10585
|
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
9854
10586
|
let providerCustomHeaders;
|
|
9855
10587
|
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
9856
|
-
env.CLAUDE_CONFIG_DIR =
|
|
10588
|
+
env.CLAUDE_CONFIG_DIR = import_node_path28.default.join(params.identitiesDir, params.resolvedIdentityName);
|
|
9857
10589
|
}
|
|
9858
10590
|
if (params.provider !== void 0) {
|
|
9859
10591
|
const providerEnv = params.provider.definition.env ?? {};
|
|
@@ -10014,7 +10746,7 @@ function prepareLaunch(params) {
|
|
|
10014
10746
|
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
10015
10747
|
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
10016
10748
|
});
|
|
10017
|
-
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(
|
|
10749
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(import_node_path29.default.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
10018
10750
|
log.error(
|
|
10019
10751
|
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
10020
10752
|
);
|
|
@@ -10260,11 +10992,11 @@ function probeFrontDoorSync(port, caPem) {
|
|
|
10260
10992
|
|
|
10261
10993
|
// src/frontdoor/trust.ts
|
|
10262
10994
|
var import_node_crypto11 = require("node:crypto");
|
|
10263
|
-
var
|
|
10995
|
+
var import_node_path30 = __toESM(require("node:path"), 1);
|
|
10264
10996
|
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
10265
10997
|
function resolveTrustBundle(params) {
|
|
10266
10998
|
const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
|
|
10267
|
-
if (inherited === void 0 || inherited === "" ||
|
|
10999
|
+
if (inherited === void 0 || inherited === "" || import_node_path30.default.resolve(inherited) === import_node_path30.default.resolve(caCertFile)) {
|
|
10268
11000
|
return { path: caCertFile };
|
|
10269
11001
|
}
|
|
10270
11002
|
const caPem = fs12.readFileUtf8(caCertFile);
|
|
@@ -10282,7 +11014,7 @@ function resolveTrustBundle(params) {
|
|
|
10282
11014
|
}
|
|
10283
11015
|
const combined = `${inheritedPem.trimEnd()}
|
|
10284
11016
|
${caPem}`;
|
|
10285
|
-
const bundle =
|
|
11017
|
+
const bundle = import_node_path30.default.join(bundlesDir, `${(0, import_node_crypto11.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
10286
11018
|
if (!fs12.exists(bundle)) {
|
|
10287
11019
|
fs12.mkdirp(bundlesDir);
|
|
10288
11020
|
fs12.writeFileAtomic(bundle, combined);
|
|
@@ -10476,6 +11208,13 @@ function prepareClaudeLaunch(options) {
|
|
|
10476
11208
|
RC_PENDING_DEADLINE_MS,
|
|
10477
11209
|
RC_PERMISSION_MODES,
|
|
10478
11210
|
RC_REQUEST_PARSE_CAP_BYTES,
|
|
11211
|
+
RC_SELF_HOST_ENV,
|
|
11212
|
+
RC_SELF_HOST_KEEPALIVE_MS,
|
|
11213
|
+
RC_SELF_HOST_RECORD_DIR,
|
|
11214
|
+
RC_SELF_HOST_RECORD_FILE,
|
|
11215
|
+
RC_SELF_HOST_RETENTION_MS,
|
|
11216
|
+
RC_SELF_HOST_SCOPE_LIST,
|
|
11217
|
+
RC_SELF_HOST_WORKER_JWT_TTL_SECONDS,
|
|
10479
11218
|
RC_SESSIONS_PATH_PREFIX,
|
|
10480
11219
|
RC_STREAM_BACKOFF_MS,
|
|
10481
11220
|
ROUTED_PATH_PREFIX,
|
|
@@ -10512,6 +11251,7 @@ function prepareClaudeLaunch(options) {
|
|
|
10512
11251
|
createRcControlHandler,
|
|
10513
11252
|
createRcCredentialStore,
|
|
10514
11253
|
createRcEventFanout,
|
|
11254
|
+
createRcSelfHostSurface,
|
|
10515
11255
|
createRcSessionTracker,
|
|
10516
11256
|
createRcStreamHub,
|
|
10517
11257
|
createSseParser,
|
|
@@ -10536,6 +11276,8 @@ function prepareClaudeLaunch(options) {
|
|
|
10536
11276
|
isIdentityDirectoryName,
|
|
10537
11277
|
isInterceptedHost,
|
|
10538
11278
|
isRcPermissionMode,
|
|
11279
|
+
lateRcEventDial,
|
|
11280
|
+
lateRcStreamDial,
|
|
10539
11281
|
listDirectoryRules,
|
|
10540
11282
|
listIdentities,
|
|
10541
11283
|
listProfiles,
|
|
@@ -10543,6 +11285,7 @@ function prepareClaudeLaunch(options) {
|
|
|
10543
11285
|
listUsageSnapshots,
|
|
10544
11286
|
matchBranch,
|
|
10545
11287
|
mintLeaf,
|
|
11288
|
+
mintRcSelfHostCredential,
|
|
10546
11289
|
observingRoutedRoute,
|
|
10547
11290
|
parseConnectTarget,
|
|
10548
11291
|
parseRcStreamEnvelope,
|
|
@@ -10554,6 +11297,7 @@ function prepareClaudeLaunch(options) {
|
|
|
10554
11297
|
rankPool,
|
|
10555
11298
|
rcEventWriteResultFromAnswer,
|
|
10556
11299
|
rcObserverAsPassthrough,
|
|
11300
|
+
rcSelfHostFromEnv,
|
|
10557
11301
|
readActiveIdentity,
|
|
10558
11302
|
readDirectoryRules,
|
|
10559
11303
|
readFarmManifest,
|
|
@@ -10562,6 +11306,7 @@ function prepareClaudeLaunch(options) {
|
|
|
10562
11306
|
readPools,
|
|
10563
11307
|
readProfile,
|
|
10564
11308
|
readProvider,
|
|
11309
|
+
readRcSelfHostRecord,
|
|
10565
11310
|
readUsageSnapshot,
|
|
10566
11311
|
realConnectCertStore,
|
|
10567
11312
|
realConnectEffects,
|