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