agent-shim 8.25.0 → 8.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +2411 -2078
- package/dist/index.cjs +423 -185
- package/dist/index.mjs +417 -185
- package/dist/types/index.d.ts +287 -13
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5074,6 +5074,35 @@ var RcStreamEventSchema = z4.strictObject({
|
|
|
5074
5074
|
session: z4.string(),
|
|
5075
5075
|
envelope: RcStreamEnvelopeSchema
|
|
5076
5076
|
});
|
|
5077
|
+
var RcRateLimitWindowSchema = z4.looseObject({
|
|
5078
|
+
/** The fraction of the window used, usually between 0 and 1, above 1 when usage legitimately runs past the window's cap (the CLI's own field description states both). */
|
|
5079
|
+
utilization: z4.number().optional(),
|
|
5080
|
+
/** When the window resets, in unix epoch seconds: the unit is the CLI's own field description, stated in the 2.1.289 bundle's schema. */
|
|
5081
|
+
resetsAt: z4.number().optional()
|
|
5082
|
+
});
|
|
5083
|
+
var RcRateLimitInfoSchema = z4.looseObject({
|
|
5084
|
+
/** The limiting window's own status (`allowed`, `allowed_warning`, `rejected`), asserted only as a string because the vocabulary grows with the CLI. */
|
|
5085
|
+
status: z4.string(),
|
|
5086
|
+
/** When the limiting window resets, in unix epoch seconds. */
|
|
5087
|
+
resetsAt: z4.number().optional(),
|
|
5088
|
+
/** Which window the top-level fields describe (`five_hour`, `seven_day`, and kin), the payload's own name for it. */
|
|
5089
|
+
rateLimitType: z4.string().optional(),
|
|
5090
|
+
/** The fraction of the limiting window used, on the same scale the per-window utilizations carry. */
|
|
5091
|
+
utilization: z4.number().optional(),
|
|
5092
|
+
/** Whether usage beyond the plan (extra usage) is currently covering the overflow, the payload's own flag for it. */
|
|
5093
|
+
isUsingOverage: z4.boolean().optional(),
|
|
5094
|
+
/** The overage's own status, the same vocabulary the limiting window's `status` carries. */
|
|
5095
|
+
overageStatus: z4.string().optional(),
|
|
5096
|
+
/** When the overage window resets, in unix epoch seconds. */
|
|
5097
|
+
overageResetsAt: z4.number().optional(),
|
|
5098
|
+
/** Each subscription window the account's response headers carried: the session (five-hour), weekly (seven-day), and overage-included weekly windows, each present only when the account's responses carry it. */
|
|
5099
|
+
unifiedWindows: z4.looseObject({ five_hour: RcRateLimitWindowSchema.optional(), seven_day: RcRateLimitWindowSchema.optional(), seven_day_overage_included: RcRateLimitWindowSchema.optional() }).optional()
|
|
5100
|
+
});
|
|
5101
|
+
var RcLiveRateLimitSchema = z4.strictObject({
|
|
5102
|
+
session: z4.string(),
|
|
5103
|
+
observedAt: z4.number(),
|
|
5104
|
+
rateLimit: RcRateLimitInfoSchema
|
|
5105
|
+
});
|
|
5077
5106
|
var RcSessionQuerySchema = z4.strictObject({
|
|
5078
5107
|
session: z4.string().min(1).optional()
|
|
5079
5108
|
});
|
|
@@ -9684,6 +9713,12 @@ var UsageWindowsOutputSchema = z19.strictObject({
|
|
|
9684
9713
|
fiveHour: EffectiveWindowSchema.optional(),
|
|
9685
9714
|
sevenDay: EffectiveWindowSchema.optional()
|
|
9686
9715
|
});
|
|
9716
|
+
var UsageLiveOutputSchema = z19.strictObject({
|
|
9717
|
+
/** The freshest observation the door holds, never narrowed by the read's session filter because it is the door's single latest statement of the account's quota; absent while no rate_limit_event has been filed at all. */
|
|
9718
|
+
latest: RcLiveRateLimitSchema.optional(),
|
|
9719
|
+
/** One observation per session whose stream has filed a rate_limit_event this door generation. */
|
|
9720
|
+
sessions: z19.readonly(z19.array(RcLiveRateLimitSchema))
|
|
9721
|
+
});
|
|
9687
9722
|
var FrontDoorSessionStatusSchema = z19.strictObject({
|
|
9688
9723
|
pid: z19.int().positive(),
|
|
9689
9724
|
startedAt: z19.number(),
|
|
@@ -9798,6 +9833,16 @@ function createControlApiRouter(deps) {
|
|
|
9798
9833
|
...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
|
|
9799
9834
|
...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
|
|
9800
9835
|
};
|
|
9836
|
+
}),
|
|
9837
|
+
live: authed.input(RcSessionQuerySchema).output(UsageLiveOutputSchema).handler(({ input }) => {
|
|
9838
|
+
const sessions = deps.liveRateLimits(input.session);
|
|
9839
|
+
if (input.session !== void 0 && sessions.length === 0) {
|
|
9840
|
+
if (deps.list().some((session) => session.id === input.session)) {
|
|
9841
|
+
throw new ORPCError3("NOT_FOUND", { message: `session ${input.session} has filed no rate_limit_event on its stream yet: the worker emits one only once a turn has completed, so retry after this session's next turn` });
|
|
9842
|
+
}
|
|
9843
|
+
throw new ORPCError3("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
|
|
9844
|
+
}
|
|
9845
|
+
return { latest: deps.latestRateLimit(), sessions };
|
|
9801
9846
|
})
|
|
9802
9847
|
},
|
|
9803
9848
|
frontdoor: {
|
|
@@ -10018,6 +10063,42 @@ function createLaunchEventPublisher(hub, now) {
|
|
|
10018
10063
|
};
|
|
10019
10064
|
}
|
|
10020
10065
|
|
|
10066
|
+
// src/frontdoor/rcLiveUsage.ts
|
|
10067
|
+
var RC_RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
|
|
10068
|
+
function isRecord5(value) {
|
|
10069
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10070
|
+
}
|
|
10071
|
+
function rcRateLimitInfoOf(event) {
|
|
10072
|
+
const payload = event.envelope.payload;
|
|
10073
|
+
if (!isRecord5(payload) || payload.type !== RC_RATE_LIMIT_EVENT_TYPE) {
|
|
10074
|
+
return void 0;
|
|
10075
|
+
}
|
|
10076
|
+
if (!isRecord5(payload.rate_limit_info)) {
|
|
10077
|
+
return void 0;
|
|
10078
|
+
}
|
|
10079
|
+
const parsed = RcRateLimitInfoSchema.safeParse(payload.rate_limit_info);
|
|
10080
|
+
return parsed.success ? parsed.data : void 0;
|
|
10081
|
+
}
|
|
10082
|
+
function createRcLiveUsage(deps) {
|
|
10083
|
+
const observed = /* @__PURE__ */ new Map();
|
|
10084
|
+
deps.hub.subscribe([DOOR_EVENT_SOURCE_RC], (event) => {
|
|
10085
|
+
const stream = RcStreamEventSchema.safeParse(event.payload);
|
|
10086
|
+
if (!stream.success) {
|
|
10087
|
+
return;
|
|
10088
|
+
}
|
|
10089
|
+
const rateLimit = rcRateLimitInfoOf(stream.data);
|
|
10090
|
+
if (rateLimit === void 0) {
|
|
10091
|
+
return;
|
|
10092
|
+
}
|
|
10093
|
+
observed.set(stream.data.session, { session: stream.data.session, observedAt: deps.now(), rateLimit });
|
|
10094
|
+
});
|
|
10095
|
+
const sorted = () => [...observed.values()].sort((left, right) => left.observedAt - right.observedAt || (left.session < right.session ? -1 : 1));
|
|
10096
|
+
return {
|
|
10097
|
+
liveOf: (sessionId) => sorted().filter((entry) => sessionId === void 0 || entry.session === sessionId),
|
|
10098
|
+
latestOf: () => sorted().at(-1)
|
|
10099
|
+
};
|
|
10100
|
+
}
|
|
10101
|
+
|
|
10021
10102
|
// src/frontdoor/rcStream.ts
|
|
10022
10103
|
var RC_STREAM_EVENT_NAME = "client_event";
|
|
10023
10104
|
var SSE_BYTE_ORDER_MARK = 65279;
|
|
@@ -10080,7 +10161,7 @@ function createSseParser() {
|
|
|
10080
10161
|
}
|
|
10081
10162
|
};
|
|
10082
10163
|
}
|
|
10083
|
-
function
|
|
10164
|
+
function isRecord6(value) {
|
|
10084
10165
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10085
10166
|
}
|
|
10086
10167
|
function sequenceNumOf(value) {
|
|
@@ -10102,7 +10183,7 @@ function parseRcStreamEnvelope(parsed) {
|
|
|
10102
10183
|
} catch {
|
|
10103
10184
|
return void 0;
|
|
10104
10185
|
}
|
|
10105
|
-
if (!
|
|
10186
|
+
if (!isRecord6(data)) {
|
|
10106
10187
|
return void 0;
|
|
10107
10188
|
}
|
|
10108
10189
|
const eventType = data.event_type;
|
|
@@ -10333,7 +10414,7 @@ function createRcStreamHub(deps) {
|
|
|
10333
10414
|
// src/frontdoor/rcSelfHostEndpoints.ts
|
|
10334
10415
|
var RC_SOURCE_WORKER = "worker";
|
|
10335
10416
|
var RC_SOURCE_CLIENT = "client";
|
|
10336
|
-
function
|
|
10417
|
+
function isRecord7(value) {
|
|
10337
10418
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10338
10419
|
}
|
|
10339
10420
|
function parseJsonObject2(body) {
|
|
@@ -10343,7 +10424,7 @@ function parseJsonObject2(body) {
|
|
|
10343
10424
|
} catch {
|
|
10344
10425
|
return void 0;
|
|
10345
10426
|
}
|
|
10346
|
-
return
|
|
10427
|
+
return isRecord7(parsed) ? parsed : void 0;
|
|
10347
10428
|
}
|
|
10348
10429
|
function nonEmptyString2(record, field) {
|
|
10349
10430
|
const value = record === void 0 ? void 0 : record[field];
|
|
@@ -10376,7 +10457,7 @@ function pageOfRcChannel(url, rows, idOf, rowOf) {
|
|
|
10376
10457
|
return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
|
|
10377
10458
|
}
|
|
10378
10459
|
async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
10379
|
-
const { request: request4, response,
|
|
10460
|
+
const { request: request4, response, host } = ctx;
|
|
10380
10461
|
if (tail === "client/presence" && method === "POST") {
|
|
10381
10462
|
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10382
10463
|
const clientId = nonEmptyString2(body, "client_id");
|
|
@@ -10386,11 +10467,11 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
|
10386
10467
|
return true;
|
|
10387
10468
|
}
|
|
10388
10469
|
if (clear === true) {
|
|
10389
|
-
|
|
10390
|
-
ctx.log?.(`rc selfhost ${
|
|
10470
|
+
host.clients.delete(clientId);
|
|
10471
|
+
ctx.log?.(`rc selfhost ${host.id}: client ${clientId} cleared its presence`);
|
|
10391
10472
|
} else {
|
|
10392
|
-
|
|
10393
|
-
ctx.log?.(`rc selfhost ${
|
|
10473
|
+
host.clients.set(clientId, ctx.now());
|
|
10474
|
+
ctx.log?.(`rc selfhost ${host.id}: client ${clientId} announced its presence${host.clients.size === 1 ? "" : ` (${String(host.clients.size)} clients now announced)`}`);
|
|
10394
10475
|
}
|
|
10395
10476
|
await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
|
|
10396
10477
|
return true;
|
|
@@ -10403,8 +10484,8 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
|
10403
10484
|
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a read receipt is JSON naming the event_id it read or none for the whole session, and client_id only as a non-empty string or absent" } });
|
|
10404
10485
|
return true;
|
|
10405
10486
|
}
|
|
10406
|
-
const receipt = ctx.publish(
|
|
10407
|
-
ctx.log?.(`rc selfhost ${
|
|
10487
|
+
const receipt = ctx.publish(host.conversation, host.writer, clientId ?? RC_SOURCE_CLIENT, { type: "mark_read", ...eventId === void 0 ? {} : { event_id: eventId }, ...clientId === void 0 ? {} : { client_id: clientId } });
|
|
10488
|
+
ctx.log?.(`rc selfhost ${host.id}: ${clientId === void 0 ? "an unnamed client" : `client ${clientId}`} marked ${eventId === void 0 ? "the session read" : `event ${eventId} read`} as sequence ${String(receipt.sequenceNum)}`);
|
|
10408
10489
|
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10409
10490
|
return true;
|
|
10410
10491
|
}
|
|
@@ -10417,20 +10498,20 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
|
10417
10498
|
}
|
|
10418
10499
|
const results = [];
|
|
10419
10500
|
for (const event of events) {
|
|
10420
|
-
if (!
|
|
10501
|
+
if (!isRecord7(event) || !isRecord7(event.payload)) {
|
|
10421
10502
|
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a teleport write is an object carrying one payload object" } });
|
|
10422
10503
|
return true;
|
|
10423
10504
|
}
|
|
10424
|
-
const published = ctx.publish(
|
|
10505
|
+
const published = ctx.publish(host.conversation, host.writer, RC_SOURCE_CLIENT, event.payload);
|
|
10425
10506
|
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
10426
10507
|
}
|
|
10427
10508
|
ctx.sweep();
|
|
10428
|
-
ctx.log?.(`rc selfhost ${
|
|
10509
|
+
ctx.log?.(`rc selfhost ${host.id}: teleport write published ${String(results.length)} marker event(s)`);
|
|
10429
10510
|
await ctx.answerJson(response, HTTP_STATUS.ok, { results });
|
|
10430
10511
|
return true;
|
|
10431
10512
|
}
|
|
10432
10513
|
if (tail === "teleport-events" && method === "GET") {
|
|
10433
|
-
const page = pageOfRcChannel(request4.url,
|
|
10514
|
+
const page = pageOfRcChannel(request4.url, host.conversation.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
|
|
10434
10515
|
if ("error" in page) {
|
|
10435
10516
|
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
|
|
10436
10517
|
return true;
|
|
@@ -10506,10 +10587,7 @@ async function serveRcCompatSessions(ctx, pathname, compatPath) {
|
|
|
10506
10587
|
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10507
10588
|
return;
|
|
10508
10589
|
}
|
|
10509
|
-
|
|
10510
|
-
sink.retire();
|
|
10511
|
-
}
|
|
10512
|
-
ctx.sessions.delete(session.id);
|
|
10590
|
+
ctx.retireSession(session);
|
|
10513
10591
|
ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
|
|
10514
10592
|
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10515
10593
|
return;
|
|
@@ -10517,6 +10595,202 @@ async function serveRcCompatSessions(ctx, pathname, compatPath) {
|
|
|
10517
10595
|
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
|
|
10518
10596
|
}
|
|
10519
10597
|
|
|
10598
|
+
// src/frontdoor/rcSelfHostConversations.ts
|
|
10599
|
+
var RC_CONVERSATIONS_PATH_PREFIX = "/v1/code/conversations";
|
|
10600
|
+
var RC_CONVERSATION_ID_PREFIX = "conv_";
|
|
10601
|
+
function isRecord8(value) {
|
|
10602
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10603
|
+
}
|
|
10604
|
+
function parseJsonObject3(body) {
|
|
10605
|
+
let parsed;
|
|
10606
|
+
try {
|
|
10607
|
+
parsed = JSON.parse(body);
|
|
10608
|
+
} catch {
|
|
10609
|
+
return void 0;
|
|
10610
|
+
}
|
|
10611
|
+
return isRecord8(parsed) ? parsed : void 0;
|
|
10612
|
+
}
|
|
10613
|
+
function nonEmptyString3(record, field) {
|
|
10614
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10615
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10616
|
+
}
|
|
10617
|
+
function newRcSelfHostConversation(newUuid, now) {
|
|
10618
|
+
return { id: `${RC_CONVERSATION_ID_PREFIX}${newUuid()}`, createdAt: now(), sequenceNum: 0, events: [], seenPayloadUuids: /* @__PURE__ */ new Set(), workerStreams: /* @__PURE__ */ new Set(), clientStreams: /* @__PURE__ */ new Set(), clients: /* @__PURE__ */ new Map(), sessionIds: /* @__PURE__ */ new Set(), lastTrafficAt: now() };
|
|
10619
|
+
}
|
|
10620
|
+
function conversationRow(conversation) {
|
|
10621
|
+
return { id: conversation.id, created_at: new Date(conversation.createdAt).toISOString(), updated_at: new Date(conversation.lastTrafficAt).toISOString(), sessions: [...conversation.sessionIds] };
|
|
10622
|
+
}
|
|
10623
|
+
async function serveRcConversations(ctx, pathname, method) {
|
|
10624
|
+
const { request: request4, response, conversations } = ctx;
|
|
10625
|
+
const refusal = ctx.credentialRefusal(request4.headers);
|
|
10626
|
+
if (refusal !== void 0) {
|
|
10627
|
+
await ctx.unauthorized(response, refusal);
|
|
10628
|
+
return;
|
|
10629
|
+
}
|
|
10630
|
+
if (pathname === RC_CONVERSATIONS_PATH_PREFIX) {
|
|
10631
|
+
if (method !== "GET") {
|
|
10632
|
+
await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the conversation collection is a GET; a conversation is born from a session create, never here" } });
|
|
10633
|
+
return;
|
|
10634
|
+
}
|
|
10635
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...conversations.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1)).map(conversationRow) });
|
|
10636
|
+
return;
|
|
10637
|
+
}
|
|
10638
|
+
const rest = pathname.slice(`${RC_CONVERSATIONS_PATH_PREFIX}/`.length);
|
|
10639
|
+
const slash = rest.indexOf("/");
|
|
10640
|
+
const conversationId = slash === -1 ? rest : rest.slice(0, slash);
|
|
10641
|
+
const tail = slash === -1 ? "" : rest.slice(slash + 1);
|
|
10642
|
+
if (!conversationId.startsWith(RC_CONVERSATION_ID_PREFIX)) {
|
|
10643
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "the conversation family names one conv_-prefixed conversation id" } });
|
|
10644
|
+
return;
|
|
10645
|
+
}
|
|
10646
|
+
const conversation = conversations.get(conversationId);
|
|
10647
|
+
if (conversation === void 0) {
|
|
10648
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "no such Remote Control conversation on this door" } });
|
|
10649
|
+
return;
|
|
10650
|
+
}
|
|
10651
|
+
conversation.lastTrafficAt = ctx.now();
|
|
10652
|
+
if (tail === "" && method === "GET") {
|
|
10653
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { conversation: conversationRow(conversation) });
|
|
10654
|
+
return;
|
|
10655
|
+
}
|
|
10656
|
+
if (tail === "events" && method === "POST") {
|
|
10657
|
+
const body = parseJsonObject3(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10658
|
+
const events = body?.events;
|
|
10659
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > ctx.maxBatchEvents) {
|
|
10660
|
+
await ctx.answerJson(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(ctx.maxBatchEvents)} events` } });
|
|
10661
|
+
return;
|
|
10662
|
+
}
|
|
10663
|
+
const results = [];
|
|
10664
|
+
for (const event of events) {
|
|
10665
|
+
if (!isRecord8(event) || !isRecord8(event.payload)) {
|
|
10666
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a write is an object carrying one payload object" } });
|
|
10667
|
+
return;
|
|
10668
|
+
}
|
|
10669
|
+
const published = ctx.publish(conversation, { session: void 0, half: "client" }, RC_SOURCE_CLIENT, event.payload);
|
|
10670
|
+
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
10671
|
+
}
|
|
10672
|
+
ctx.sweep();
|
|
10673
|
+
ctx.log?.(`rc selfhost ${conversation.id}: a conversation client wrote ${String(results.length)} event(s) into the shared space`);
|
|
10674
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { results });
|
|
10675
|
+
return;
|
|
10676
|
+
}
|
|
10677
|
+
if (tail === "events" && method === "GET") {
|
|
10678
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { data: conversation.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString3(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
|
|
10679
|
+
return;
|
|
10680
|
+
}
|
|
10681
|
+
if (tail === "events/stream" && method === "GET") {
|
|
10682
|
+
await ctx.holdStream(conversation, conversation.id, "clientStreams", () => true, request4, response);
|
|
10683
|
+
return;
|
|
10684
|
+
}
|
|
10685
|
+
if (await serveRcClientChannels({ request: request4, response, host: { id: conversation.id, clients: conversation.clients, conversation, writer: { session: void 0, half: "client" } }, answerJson: ctx.answerJson, readBody: ctx.readBody, publish: ctx.publish, sweep: ctx.sweep, now: ctx.now, presenceRefreshSeconds: ctx.presenceRefreshSeconds, ...ctx.log === void 0 ? {} : { log: ctx.log } }, tail, method, ctx.maxBatchEvents)) {
|
|
10686
|
+
return;
|
|
10687
|
+
}
|
|
10688
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the conversation family serves no path "${tail}"` } });
|
|
10689
|
+
}
|
|
10690
|
+
|
|
10691
|
+
// src/frontdoor/rcSelfHostLocal.ts
|
|
10692
|
+
var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
|
|
10693
|
+
var RC_SELF_HOST_SCOPE_LIST = ["user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"];
|
|
10694
|
+
var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
|
|
10695
|
+
var MS_PER_SECOND5 = 1e3;
|
|
10696
|
+
var HTTP_SERVICE_UNAVAILABLE = 503;
|
|
10697
|
+
function isRecord9(value) {
|
|
10698
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10699
|
+
}
|
|
10700
|
+
function parseJsonObject4(body) {
|
|
10701
|
+
let parsed;
|
|
10702
|
+
try {
|
|
10703
|
+
parsed = JSON.parse(body);
|
|
10704
|
+
} catch {
|
|
10705
|
+
return void 0;
|
|
10706
|
+
}
|
|
10707
|
+
return isRecord9(parsed) ? parsed : void 0;
|
|
10708
|
+
}
|
|
10709
|
+
function growthBookFeatures() {
|
|
10710
|
+
return {
|
|
10711
|
+
tengu_ccr_bridge: { defaultValue: true },
|
|
10712
|
+
tengu_bridge_repl_v2: { defaultValue: true },
|
|
10713
|
+
tengu_bridge_repl_v2_cse_shim_enabled: { defaultValue: true },
|
|
10714
|
+
tengu_bridge_min_version: { defaultValue: { minVersion: "0.0.0" } }
|
|
10715
|
+
};
|
|
10716
|
+
}
|
|
10717
|
+
var answerServerJson = (response, status, body) => {
|
|
10718
|
+
const text = JSON.stringify(body);
|
|
10719
|
+
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
10720
|
+
response.end(text);
|
|
10721
|
+
};
|
|
10722
|
+
function createRcSelfHostLocal(deps) {
|
|
10723
|
+
return {
|
|
10724
|
+
parsesHost: (host) => host === RC_SELF_HOST_OAUTH_HOST,
|
|
10725
|
+
serve: async (host, request4, response) => {
|
|
10726
|
+
const method = (request4.method ?? "").toUpperCase();
|
|
10727
|
+
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
10728
|
+
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
10729
|
+
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
10730
|
+
const body = parseJsonObject4(await deps.readBody(request4, CONTROL_BODY_CAP_BYTES));
|
|
10731
|
+
const record = deps.credentialRecord();
|
|
10732
|
+
if (record === void 0) {
|
|
10733
|
+
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" });
|
|
10734
|
+
return true;
|
|
10735
|
+
}
|
|
10736
|
+
if (body?.grant_type !== void 0 && body.grant_type !== "refresh_token") {
|
|
10737
|
+
answerServerJson(response, HTTP_STATUS.badRequest, { error: "unsupported_grant_type" });
|
|
10738
|
+
return true;
|
|
10739
|
+
}
|
|
10740
|
+
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(" ") });
|
|
10741
|
+
return true;
|
|
10742
|
+
}
|
|
10743
|
+
if (method === "GET" && pathname === "/v1/oauth/hello") {
|
|
10744
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
10745
|
+
return true;
|
|
10746
|
+
}
|
|
10747
|
+
return false;
|
|
10748
|
+
}
|
|
10749
|
+
if (method === "POST" && pathname.startsWith("/api/eval/")) {
|
|
10750
|
+
await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
|
|
10751
|
+
answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND5) });
|
|
10752
|
+
return true;
|
|
10753
|
+
}
|
|
10754
|
+
if (method === "GET" && pathname === "/api/oauth/profile") {
|
|
10755
|
+
const record = deps.credentialRecord();
|
|
10756
|
+
if (record === void 0) {
|
|
10757
|
+
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" });
|
|
10758
|
+
return true;
|
|
10759
|
+
}
|
|
10760
|
+
answerServerJson(response, HTTP_STATUS.ok, {
|
|
10761
|
+
account: { uuid: record.accountUuid, display_name: "agent-shim self-hosted", created_at: (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
10762
|
+
organization: { uuid: record.organizationUuid, organization_type: "claude_max" }
|
|
10763
|
+
});
|
|
10764
|
+
return true;
|
|
10765
|
+
}
|
|
10766
|
+
if (method === "GET" && pathname === "/api/claude_code/policy_limits") {
|
|
10767
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
10768
|
+
return true;
|
|
10769
|
+
}
|
|
10770
|
+
if (method === "POST" && pathname === "/api/oauth/validate") {
|
|
10771
|
+
await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
|
|
10772
|
+
const record = deps.credentialRecord();
|
|
10773
|
+
if (record === void 0) {
|
|
10774
|
+
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" });
|
|
10775
|
+
return true;
|
|
10776
|
+
}
|
|
10777
|
+
answerServerJson(response, HTTP_STATUS.ok, { scopes: [...RC_SELF_HOST_SCOPE_LIST], expiresAt: null, subscriptionType: "max", account_uuid: record.accountUuid, organization_uuid: record.organizationUuid });
|
|
10778
|
+
return true;
|
|
10779
|
+
}
|
|
10780
|
+
if (method === "POST" && (pathname === "/api/event_logging/v2/batch" || pathname === "/api/claude_code/metrics" || pathname === "/api/claude_cli_feedback")) {
|
|
10781
|
+
await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
|
|
10782
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
10783
|
+
return true;
|
|
10784
|
+
}
|
|
10785
|
+
if (method === "GET" && pathname === "/api/hello") {
|
|
10786
|
+
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
10787
|
+
return true;
|
|
10788
|
+
}
|
|
10789
|
+
return false;
|
|
10790
|
+
}
|
|
10791
|
+
};
|
|
10792
|
+
}
|
|
10793
|
+
|
|
10520
10794
|
// src/frontdoor/rcWebFetch.ts
|
|
10521
10795
|
import * as dns2 from "node:dns";
|
|
10522
10796
|
import * as http2 from "node:http";
|
|
@@ -10768,17 +11042,14 @@ var RC_SELF_HOST_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST";
|
|
|
10768
11042
|
function rcSelfHostFromEnv(env) {
|
|
10769
11043
|
return env[RC_SELF_HOST_ENV] === "1";
|
|
10770
11044
|
}
|
|
10771
|
-
var RC_SELF_HOST_SCOPE_LIST = ["user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"];
|
|
10772
11045
|
var RC_SESSIONS_PATH_PREFIX2 = "/v1/code/sessions";
|
|
10773
11046
|
var RC_COMPAT_SESSIONS_PATH = "/v1/sessions";
|
|
10774
11047
|
var RC_SESSION_ID_PREFIX2 = "cse_";
|
|
10775
11048
|
var RC_SELF_HOST_API_BASE_URL = "https://api.anthropic.com";
|
|
10776
|
-
var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
|
|
10777
11049
|
var HTTP_CONFLICT = 409;
|
|
10778
|
-
var
|
|
10779
|
-
var
|
|
11050
|
+
var MS_PER_SECOND6 = 1e3;
|
|
11051
|
+
var HTTP_SERVICE_UNAVAILABLE2 = 503;
|
|
10780
11052
|
var RC_SELF_HOST_WORKER_JWT_TTL_SECONDS = 46800;
|
|
10781
|
-
var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
|
|
10782
11053
|
var RC_SELF_HOST_KEEPALIVE_MS = 15e3;
|
|
10783
11054
|
var RC_SELF_HOST_RETENTION_MS = 6e5;
|
|
10784
11055
|
var RC_PRESENCE_REFRESH_SECONDS = 60;
|
|
@@ -10787,13 +11058,13 @@ var RC_SELF_HOST_WORKER_EPOCH = 1;
|
|
|
10787
11058
|
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
10788
11059
|
var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
|
|
10789
11060
|
var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
10790
|
-
function
|
|
11061
|
+
function isRecord10(value) {
|
|
10791
11062
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10792
11063
|
}
|
|
10793
11064
|
function isWebSearchRefusal(value) {
|
|
10794
11065
|
return !Array.isArray(value);
|
|
10795
11066
|
}
|
|
10796
|
-
function
|
|
11067
|
+
function nonEmptyString4(record, field) {
|
|
10797
11068
|
const value = record === void 0 ? void 0 : record[field];
|
|
10798
11069
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10799
11070
|
}
|
|
@@ -10864,56 +11135,69 @@ async function readBody2(request4, capBytes) {
|
|
|
10864
11135
|
});
|
|
10865
11136
|
});
|
|
10866
11137
|
}
|
|
10867
|
-
function
|
|
11138
|
+
function parseJsonObject5(body) {
|
|
10868
11139
|
let parsed;
|
|
10869
11140
|
try {
|
|
10870
11141
|
parsed = JSON.parse(body);
|
|
10871
11142
|
} catch {
|
|
10872
11143
|
return void 0;
|
|
10873
11144
|
}
|
|
10874
|
-
return
|
|
11145
|
+
return isRecord10(parsed) ? parsed : void 0;
|
|
10875
11146
|
}
|
|
10876
11147
|
function mintWorkerJwt(randomToken, issuedAt) {
|
|
10877
11148
|
const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
10878
11149
|
const header = encode({ alg: "HS256", typ: "JWT" });
|
|
10879
|
-
const payload = encode({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS *
|
|
11150
|
+
const payload = encode({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND6) / MS_PER_SECOND6), iat: Math.floor(issuedAt / MS_PER_SECOND6) });
|
|
10880
11151
|
return `${header}.${payload}.${Buffer.from(randomToken(), "utf8").toString("base64url")}`;
|
|
10881
11152
|
}
|
|
10882
|
-
function growthBookFeatures() {
|
|
10883
|
-
return {
|
|
10884
|
-
tengu_ccr_bridge: { defaultValue: true },
|
|
10885
|
-
tengu_bridge_repl_v2: { defaultValue: true },
|
|
10886
|
-
tengu_bridge_repl_v2_cse_shim_enabled: { defaultValue: true },
|
|
10887
|
-
tengu_bridge_min_version: { defaultValue: { minVersion: "0.0.0" } }
|
|
10888
|
-
};
|
|
10889
|
-
}
|
|
10890
11153
|
function createRcSelfHostSurface(deps) {
|
|
10891
11154
|
const sessions = /* @__PURE__ */ new Map();
|
|
11155
|
+
const conversations = /* @__PURE__ */ new Map();
|
|
11156
|
+
const retireSession = (session) => {
|
|
11157
|
+
for (const sink of [...session.conversation.workerStreams, ...session.conversation.clientStreams]) {
|
|
11158
|
+
if (sink.owner === session.id) {
|
|
11159
|
+
sink.retire();
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
11162
|
+
session.conversation.sessionIds.delete(session.id);
|
|
11163
|
+
sessions.delete(session.id);
|
|
11164
|
+
};
|
|
10892
11165
|
const sweep = () => {
|
|
10893
11166
|
const now = deps.now();
|
|
10894
11167
|
for (const session of sessions.values()) {
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
11168
|
+
for (const [clientId, lastSeenAt] of session.clients) {
|
|
11169
|
+
if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11170
|
+
session.clients.delete(clientId);
|
|
11171
|
+
}
|
|
11172
|
+
}
|
|
11173
|
+
const holdsStream = [...session.conversation.workerStreams, ...session.conversation.clientStreams].some((sink) => sink.owner === session.id);
|
|
11174
|
+
if (!holdsStream && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11175
|
+
retireSession(session);
|
|
11176
|
+
}
|
|
11177
|
+
}
|
|
11178
|
+
for (const conversation of conversations.values()) {
|
|
11179
|
+
while (conversation.events[0] !== void 0 && now - conversation.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11180
|
+
const dropped = conversation.events.shift();
|
|
11181
|
+
const uuid = dropped === void 0 ? void 0 : nonEmptyString4(dropped.payload, "uuid");
|
|
10898
11182
|
if (uuid !== void 0) {
|
|
10899
|
-
|
|
11183
|
+
conversation.seenPayloadUuids.delete(uuid);
|
|
10900
11184
|
}
|
|
10901
11185
|
}
|
|
10902
|
-
for (const [clientId, lastSeenAt] of
|
|
11186
|
+
for (const [clientId, lastSeenAt] of conversation.clients) {
|
|
10903
11187
|
if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10904
|
-
|
|
11188
|
+
conversation.clients.delete(clientId);
|
|
10905
11189
|
}
|
|
10906
11190
|
}
|
|
10907
|
-
if (
|
|
10908
|
-
|
|
11191
|
+
if (conversation.sessionIds.size === 0 && conversation.workerStreams.size === 0 && conversation.clientStreams.size === 0 && now - conversation.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11192
|
+
conversations.delete(conversation.id);
|
|
10909
11193
|
}
|
|
10910
11194
|
}
|
|
10911
11195
|
};
|
|
10912
11196
|
const timer = setInterval(sweep, RC_SELF_HOST_RETENTION_MS);
|
|
10913
11197
|
timer.unref();
|
|
10914
11198
|
const keepalive = setInterval(() => {
|
|
10915
|
-
for (const
|
|
10916
|
-
for (const sink of [...
|
|
11199
|
+
for (const conversation of conversations.values()) {
|
|
11200
|
+
for (const sink of [...conversation.workerStreams, ...conversation.clientStreams]) {
|
|
10917
11201
|
sink.write(": keep-alive\n\n");
|
|
10918
11202
|
}
|
|
10919
11203
|
}
|
|
@@ -10942,7 +11226,7 @@ function createRcSelfHostSurface(deps) {
|
|
|
10942
11226
|
const frameOf = (event) => {
|
|
10943
11227
|
const envelope = {
|
|
10944
11228
|
event_id: event.eventId,
|
|
10945
|
-
event_type:
|
|
11229
|
+
event_type: nonEmptyString4(event.payload, "type") ?? "event",
|
|
10946
11230
|
sequence_num: event.sequenceNum,
|
|
10947
11231
|
source: event.source,
|
|
10948
11232
|
payload: event.payload,
|
|
@@ -10954,14 +11238,16 @@ data: ${JSON.stringify(envelope)}
|
|
|
10954
11238
|
|
|
10955
11239
|
`;
|
|
10956
11240
|
};
|
|
10957
|
-
const
|
|
11241
|
+
const reachesWorkerStream = (event, owner) => !(event.writer.half === "worker" && event.writer.session === owner);
|
|
11242
|
+
const holdStream = async (conversation, owner, which, wantsReplay, request4, response) => {
|
|
10958
11243
|
const url = new URL(request4.url, "http://127.0.0.1");
|
|
10959
11244
|
const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
|
|
10960
|
-
const replay = cursor === void 0 ? [] :
|
|
11245
|
+
const replay = cursor === void 0 ? [] : conversation.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
|
|
10961
11246
|
response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
10962
11247
|
response.flush();
|
|
10963
11248
|
let pending = Promise.resolve();
|
|
10964
11249
|
const sink = {
|
|
11250
|
+
owner,
|
|
10965
11251
|
write: (frame) => {
|
|
10966
11252
|
pending = pending.then(async () => {
|
|
10967
11253
|
await response.write(frame);
|
|
@@ -10969,11 +11255,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
10969
11255
|
});
|
|
10970
11256
|
},
|
|
10971
11257
|
retire: () => {
|
|
10972
|
-
|
|
11258
|
+
conversation[which].delete(sink);
|
|
10973
11259
|
}
|
|
10974
11260
|
};
|
|
10975
|
-
|
|
10976
|
-
|
|
11261
|
+
conversation[which].add(sink);
|
|
11262
|
+
conversation.lastTrafficAt = deps.now();
|
|
10977
11263
|
for (const event of replay) {
|
|
10978
11264
|
sink.write(frameOf(event));
|
|
10979
11265
|
}
|
|
@@ -10985,36 +11271,37 @@ data: ${JSON.stringify(envelope)}
|
|
|
10985
11271
|
sink.retire();
|
|
10986
11272
|
response.end();
|
|
10987
11273
|
};
|
|
10988
|
-
const publish = (
|
|
10989
|
-
const uuid =
|
|
10990
|
-
const duplicate = uuid !== void 0 &&
|
|
10991
|
-
|
|
11274
|
+
const publish = (conversation, writer, source, payload) => {
|
|
11275
|
+
const uuid = nonEmptyString4(payload, "uuid");
|
|
11276
|
+
const duplicate = uuid !== void 0 && conversation.seenPayloadUuids.has(uuid);
|
|
11277
|
+
conversation.sequenceNum += 1;
|
|
10992
11278
|
const event = {
|
|
10993
|
-
sequenceNum:
|
|
11279
|
+
sequenceNum: conversation.sequenceNum,
|
|
10994
11280
|
eventId: uuid ?? deps.newUuid(),
|
|
10995
11281
|
source,
|
|
10996
11282
|
// 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.
|
|
10997
11283
|
payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
|
|
10998
11284
|
createdAt: new Date(deps.now()).toISOString(),
|
|
10999
|
-
|
|
11285
|
+
writer,
|
|
11000
11286
|
storedAt: deps.now()
|
|
11001
11287
|
};
|
|
11002
|
-
|
|
11288
|
+
conversation.events.push(event);
|
|
11003
11289
|
if (uuid !== void 0) {
|
|
11004
|
-
|
|
11290
|
+
conversation.seenPayloadUuids.add(uuid);
|
|
11005
11291
|
}
|
|
11006
|
-
|
|
11292
|
+
conversation.lastTrafficAt = event.storedAt;
|
|
11007
11293
|
const frame = frameOf(event);
|
|
11008
|
-
|
|
11009
|
-
|
|
11294
|
+
for (const sink of [...conversation.workerStreams]) {
|
|
11295
|
+
if (reachesWorkerStream(event, sink.owner)) {
|
|
11010
11296
|
sink.write(frame);
|
|
11011
11297
|
}
|
|
11012
11298
|
}
|
|
11013
|
-
for (const sink of [...
|
|
11299
|
+
for (const sink of [...conversation.clientStreams]) {
|
|
11014
11300
|
sink.write(frame);
|
|
11015
11301
|
}
|
|
11016
11302
|
return { sequenceNum: event.sequenceNum, eventId: event.eventId, duplicate };
|
|
11017
11303
|
};
|
|
11304
|
+
const sessionRow = (session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active", conversation_id: session.conversation.id });
|
|
11018
11305
|
const answerJson2 = async (response, status, body, extraHeaders = {}) => {
|
|
11019
11306
|
const text = JSON.stringify(body);
|
|
11020
11307
|
response.start(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")), ...extraHeaders });
|
|
@@ -11022,7 +11309,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11022
11309
|
response.end();
|
|
11023
11310
|
};
|
|
11024
11311
|
const noCredential = async (response) => {
|
|
11025
|
-
await answerJson2(response,
|
|
11312
|
+
await answerJson2(response, HTTP_SERVICE_UNAVAILABLE2, { 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" } });
|
|
11026
11313
|
};
|
|
11027
11314
|
const epochStale = async (response) => {
|
|
11028
11315
|
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 });
|
|
@@ -11050,8 +11337,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11050
11337
|
await unauthorized(response, refusal);
|
|
11051
11338
|
return;
|
|
11052
11339
|
}
|
|
11053
|
-
const body =
|
|
11054
|
-
const title =
|
|
11340
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11341
|
+
const title = nonEmptyString4(body, "title");
|
|
11055
11342
|
if (title === void 0) {
|
|
11056
11343
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
|
|
11057
11344
|
return;
|
|
@@ -11066,7 +11353,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11066
11353
|
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET, and its update a PATCH or PUT" } });
|
|
11067
11354
|
return;
|
|
11068
11355
|
}
|
|
11069
|
-
await answerJson2(response, HTTP_STATUS.ok, { session:
|
|
11356
|
+
await answerJson2(response, HTTP_STATUS.ok, { session: sessionRow(session) });
|
|
11070
11357
|
return;
|
|
11071
11358
|
}
|
|
11072
11359
|
if (tail === "bridge" && method === "POST") {
|
|
@@ -11087,10 +11374,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11087
11374
|
await unauthorized(response, refusal);
|
|
11088
11375
|
return;
|
|
11089
11376
|
}
|
|
11090
|
-
|
|
11091
|
-
sink.retire();
|
|
11092
|
-
}
|
|
11093
|
-
sessions.delete(session.id);
|
|
11377
|
+
retireSession(session);
|
|
11094
11378
|
deps.log?.(`rc selfhost ${session.id}: archived at its client's request`);
|
|
11095
11379
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11096
11380
|
return;
|
|
@@ -11108,17 +11392,17 @@ data: ${JSON.stringify(envelope)}
|
|
|
11108
11392
|
return;
|
|
11109
11393
|
}
|
|
11110
11394
|
if (workerTail === "" && method === "PUT") {
|
|
11111
|
-
const body =
|
|
11395
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11112
11396
|
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11113
11397
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11114
11398
|
await epochStale(response);
|
|
11115
11399
|
return;
|
|
11116
11400
|
}
|
|
11117
|
-
const status =
|
|
11401
|
+
const status = nonEmptyString4(body, "worker_status");
|
|
11118
11402
|
if (status !== void 0) {
|
|
11119
11403
|
session.workerStatus = status;
|
|
11120
11404
|
}
|
|
11121
|
-
if (
|
|
11405
|
+
if (isRecord10(body?.external_metadata)) {
|
|
11122
11406
|
session.externalMetadata = body.external_metadata;
|
|
11123
11407
|
}
|
|
11124
11408
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
@@ -11135,7 +11419,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11135
11419
|
return;
|
|
11136
11420
|
}
|
|
11137
11421
|
if (workerTail === "events" && method === "POST") {
|
|
11138
|
-
const body =
|
|
11422
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11139
11423
|
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11140
11424
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11141
11425
|
await epochStale(response);
|
|
@@ -11148,11 +11432,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11148
11432
|
}
|
|
11149
11433
|
const results = [];
|
|
11150
11434
|
for (const event of events) {
|
|
11151
|
-
if (!
|
|
11435
|
+
if (!isRecord10(event) || !isRecord10(event.payload)) {
|
|
11152
11436
|
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" } });
|
|
11153
11437
|
return;
|
|
11154
11438
|
}
|
|
11155
|
-
const published = publish(session, RC_SOURCE_WORKER, event.payload);
|
|
11439
|
+
const published = publish(session.conversation, { session: session.id, half: "worker" }, RC_SOURCE_WORKER, event.payload);
|
|
11156
11440
|
results.push({ event_id: published.eventId, sequence_num: String(published.sequenceNum) });
|
|
11157
11441
|
}
|
|
11158
11442
|
sweep();
|
|
@@ -11160,7 +11444,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11160
11444
|
return;
|
|
11161
11445
|
}
|
|
11162
11446
|
if (workerTail === "events/stream" && method === "GET") {
|
|
11163
|
-
await holdStream(session, "workerStreams", (event) => event.
|
|
11447
|
+
await holdStream(session.conversation, session.id, "workerStreams", (event) => reachesWorkerStream(event, session.id), request4, response);
|
|
11164
11448
|
return;
|
|
11165
11449
|
}
|
|
11166
11450
|
if (workerTail === "events/delivery" && method === "POST") {
|
|
@@ -11169,7 +11453,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11169
11453
|
return;
|
|
11170
11454
|
}
|
|
11171
11455
|
if (workerTail === "internal-events" && method === "POST") {
|
|
11172
|
-
const body =
|
|
11456
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11173
11457
|
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11174
11458
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11175
11459
|
await epochStale(response);
|
|
@@ -11181,7 +11465,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11181
11465
|
return;
|
|
11182
11466
|
}
|
|
11183
11467
|
for (const event of events) {
|
|
11184
|
-
if (
|
|
11468
|
+
if (isRecord10(event)) {
|
|
11185
11469
|
session.internalEvents.push({ eventId: deps.newUuid(), body: event });
|
|
11186
11470
|
}
|
|
11187
11471
|
}
|
|
@@ -11209,8 +11493,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11209
11493
|
return;
|
|
11210
11494
|
}
|
|
11211
11495
|
if (workerTail === "web-fetch" && method === "POST") {
|
|
11212
|
-
const body =
|
|
11213
|
-
const target =
|
|
11496
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11497
|
+
const target = nonEmptyString4(body, "url");
|
|
11214
11498
|
if (target === void 0) {
|
|
11215
11499
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
|
|
11216
11500
|
return;
|
|
@@ -11232,8 +11516,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11232
11516
|
return;
|
|
11233
11517
|
}
|
|
11234
11518
|
if (workerTail === "web-search" && method === "POST") {
|
|
11235
|
-
const body =
|
|
11236
|
-
const query =
|
|
11519
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11520
|
+
const query = nonEmptyString4(body, "query");
|
|
11237
11521
|
if (query === void 0) {
|
|
11238
11522
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
|
|
11239
11523
|
return;
|
|
@@ -11244,7 +11528,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11244
11528
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "the domain filters of a worker web-search are string arrays" } });
|
|
11245
11529
|
return;
|
|
11246
11530
|
}
|
|
11247
|
-
const searchProfile =
|
|
11531
|
+
const searchProfile = nonEmptyString4(body, "search_profile");
|
|
11248
11532
|
const backend = deps.webSearch;
|
|
11249
11533
|
if (backend === void 0) {
|
|
11250
11534
|
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
|
|
@@ -11269,7 +11553,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11269
11553
|
return;
|
|
11270
11554
|
}
|
|
11271
11555
|
if (tail === "events" && method === "POST") {
|
|
11272
|
-
const body =
|
|
11556
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11273
11557
|
const events = body?.events;
|
|
11274
11558
|
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
11275
11559
|
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` } });
|
|
@@ -11277,11 +11561,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11277
11561
|
}
|
|
11278
11562
|
const results = [];
|
|
11279
11563
|
for (const event of events) {
|
|
11280
|
-
if (!
|
|
11564
|
+
if (!isRecord10(event) || !isRecord10(event.payload)) {
|
|
11281
11565
|
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" } });
|
|
11282
11566
|
return;
|
|
11283
11567
|
}
|
|
11284
|
-
const published = publish(session, RC_SOURCE_CLIENT, event.payload);
|
|
11568
|
+
const published = publish(session.conversation, { session: session.id, half: "client" }, RC_SOURCE_CLIENT, event.payload);
|
|
11285
11569
|
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
11286
11570
|
}
|
|
11287
11571
|
sweep();
|
|
@@ -11289,14 +11573,14 @@ data: ${JSON.stringify(envelope)}
|
|
|
11289
11573
|
return;
|
|
11290
11574
|
}
|
|
11291
11575
|
if (tail === "events" && method === "GET") {
|
|
11292
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type:
|
|
11576
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: session.conversation.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString4(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
|
|
11293
11577
|
return;
|
|
11294
11578
|
}
|
|
11295
11579
|
if (tail === "events/stream" && method === "GET") {
|
|
11296
|
-
await holdStream(session, "clientStreams", () => true, request4, response);
|
|
11580
|
+
await holdStream(session.conversation, session.id, "clientStreams", () => true, request4, response);
|
|
11297
11581
|
return;
|
|
11298
11582
|
}
|
|
11299
|
-
if (await serveRcClientChannels({ request: request4, response, session, answerJson: answerJson2, readBody: readBody2, publish, sweep, now: deps.now, presenceRefreshSeconds: RC_PRESENCE_REFRESH_SECONDS, ...deps.log === void 0 ? {} : { log: deps.log } }, tail, method, RC_SELF_HOST_MAX_BATCH_EVENTS)) {
|
|
11583
|
+
if (await serveRcClientChannels({ request: request4, response, host: { id: session.id, clients: session.clients, conversation: session.conversation, writer: { session: session.id, half: "client" } }, answerJson: answerJson2, readBody: readBody2, publish, sweep, now: deps.now, presenceRefreshSeconds: RC_PRESENCE_REFRESH_SECONDS, ...deps.log === void 0 ? {} : { log: deps.log } }, tail, method, RC_SELF_HOST_MAX_BATCH_EVENTS)) {
|
|
11300
11584
|
return;
|
|
11301
11585
|
}
|
|
11302
11586
|
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
|
|
@@ -11314,7 +11598,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11314
11598
|
return;
|
|
11315
11599
|
}
|
|
11316
11600
|
if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
|
|
11317
|
-
await serveRcCompatSessions({ request: request4, response, sessions, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, now: deps.now, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, RC_COMPAT_SESSIONS_PATH);
|
|
11601
|
+
await serveRcCompatSessions({ request: request4, response, sessions, retireSession, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, now: deps.now, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, RC_COMPAT_SESSIONS_PATH);
|
|
11602
|
+
return;
|
|
11603
|
+
}
|
|
11604
|
+
if (pathname === RC_CONVERSATIONS_PATH_PREFIX || pathname.startsWith(`${RC_CONVERSATIONS_PATH_PREFIX}/`)) {
|
|
11605
|
+
await serveRcConversations({ request: request4, response, conversations, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, publish, holdStream, sweep, now: deps.now, presenceRefreshSeconds: RC_PRESENCE_REFRESH_SECONDS, maxBatchEvents: RC_SELF_HOST_MAX_BATCH_EVENTS, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, method);
|
|
11318
11606
|
return;
|
|
11319
11607
|
}
|
|
11320
11608
|
if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
|
|
@@ -11328,26 +11616,36 @@ data: ${JSON.stringify(envelope)}
|
|
|
11328
11616
|
await unauthorized(response, createRefusal);
|
|
11329
11617
|
return;
|
|
11330
11618
|
}
|
|
11331
|
-
const body =
|
|
11619
|
+
const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11620
|
+
const namedConversation = nonEmptyString4(body, "conversation");
|
|
11621
|
+
let conversation;
|
|
11622
|
+
if (namedConversation === void 0) {
|
|
11623
|
+
conversation = newRcSelfHostConversation(deps.newUuid, deps.now);
|
|
11624
|
+
conversations.set(conversation.id, conversation);
|
|
11625
|
+
} else {
|
|
11626
|
+
conversation = conversations.get(namedConversation);
|
|
11627
|
+
if (conversation === void 0) {
|
|
11628
|
+
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `no such Remote Control conversation on this door: ${namedConversation}` } });
|
|
11629
|
+
return;
|
|
11630
|
+
}
|
|
11631
|
+
conversation.lastTrafficAt = deps.now();
|
|
11632
|
+
}
|
|
11332
11633
|
const session = {
|
|
11333
11634
|
id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
|
|
11334
11635
|
createdAt: deps.now(),
|
|
11335
|
-
title:
|
|
11636
|
+
title: nonEmptyString4(body, "title"),
|
|
11637
|
+
conversation,
|
|
11336
11638
|
workerJwts: /* @__PURE__ */ new Set(),
|
|
11337
|
-
sequenceNum: 0,
|
|
11338
|
-
events: [],
|
|
11339
|
-
seenPayloadUuids: /* @__PURE__ */ new Set(),
|
|
11340
|
-
workerStreams: /* @__PURE__ */ new Set(),
|
|
11341
|
-
clientStreams: /* @__PURE__ */ new Set(),
|
|
11342
11639
|
internalEvents: [],
|
|
11343
11640
|
clients: /* @__PURE__ */ new Map(),
|
|
11344
11641
|
workerStatus: void 0,
|
|
11345
11642
|
externalMetadata: void 0,
|
|
11346
11643
|
lastTrafficAt: deps.now()
|
|
11347
11644
|
};
|
|
11645
|
+
conversation.sessionIds.add(session.id);
|
|
11348
11646
|
sessions.set(session.id, session);
|
|
11349
|
-
deps.log?.(`rc selfhost ${session.id}: session created on the door's own surface`);
|
|
11350
|
-
await answerJson2(response, HTTP_STATUS.ok, { session:
|
|
11647
|
+
deps.log?.(`rc selfhost ${session.id}: session created on the door's own surface, attached to conversation ${conversation.id}${namedConversation === void 0 ? "" : " at its client's request"}`);
|
|
11648
|
+
await answerJson2(response, HTTP_STATUS.ok, { session: sessionRow(session) });
|
|
11351
11649
|
return;
|
|
11352
11650
|
}
|
|
11353
11651
|
if (method === "GET") {
|
|
@@ -11356,7 +11654,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11356
11654
|
await unauthorized(response, readRefusal);
|
|
11357
11655
|
return;
|
|
11358
11656
|
}
|
|
11359
|
-
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" })) });
|
|
11657
|
+
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", conversation_id: session.conversation.id })) });
|
|
11360
11658
|
return;
|
|
11361
11659
|
}
|
|
11362
11660
|
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session collection is a POST (create) and a GET (list)" } });
|
|
@@ -11373,92 +11671,20 @@ data: ${JSON.stringify(envelope)}
|
|
|
11373
11671
|
await serveSessionPath(sessions.get(id), tail, request4, response);
|
|
11374
11672
|
}
|
|
11375
11673
|
};
|
|
11376
|
-
const
|
|
11377
|
-
const text = JSON.stringify(body);
|
|
11378
|
-
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
11379
|
-
response.end(text);
|
|
11380
|
-
};
|
|
11381
|
-
const local = {
|
|
11382
|
-
parsesHost: (host) => host === RC_SELF_HOST_OAUTH_HOST,
|
|
11383
|
-
serve: async (host, request4, response) => {
|
|
11384
|
-
const method = (request4.method ?? "").toUpperCase();
|
|
11385
|
-
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
11386
|
-
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
11387
|
-
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
11388
|
-
const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
|
|
11389
|
-
const record = deps.credentialRecord();
|
|
11390
|
-
if (record === void 0) {
|
|
11391
|
-
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" });
|
|
11392
|
-
return true;
|
|
11393
|
-
}
|
|
11394
|
-
if (body?.grant_type !== void 0 && body.grant_type !== "refresh_token") {
|
|
11395
|
-
answerServerJson(response, HTTP_STATUS.badRequest, { error: "unsupported_grant_type" });
|
|
11396
|
-
return true;
|
|
11397
|
-
}
|
|
11398
|
-
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(" ") });
|
|
11399
|
-
return true;
|
|
11400
|
-
}
|
|
11401
|
-
if (method === "GET" && pathname === "/v1/oauth/hello") {
|
|
11402
|
-
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
11403
|
-
return true;
|
|
11404
|
-
}
|
|
11405
|
-
return false;
|
|
11406
|
-
}
|
|
11407
|
-
if (method === "POST" && pathname.startsWith("/api/eval/")) {
|
|
11408
|
-
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
11409
|
-
answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND5) });
|
|
11410
|
-
return true;
|
|
11411
|
-
}
|
|
11412
|
-
if (method === "GET" && pathname === "/api/oauth/profile") {
|
|
11413
|
-
const record = deps.credentialRecord();
|
|
11414
|
-
if (record === void 0) {
|
|
11415
|
-
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" });
|
|
11416
|
-
return true;
|
|
11417
|
-
}
|
|
11418
|
-
answerServerJson(response, HTTP_STATUS.ok, {
|
|
11419
|
-
account: { uuid: record.accountUuid, display_name: "agent-shim self-hosted", created_at: (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
11420
|
-
organization: { uuid: record.organizationUuid, organization_type: "claude_max" }
|
|
11421
|
-
});
|
|
11422
|
-
return true;
|
|
11423
|
-
}
|
|
11424
|
-
if (method === "GET" && pathname === "/api/claude_code/policy_limits") {
|
|
11425
|
-
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
11426
|
-
return true;
|
|
11427
|
-
}
|
|
11428
|
-
if (method === "POST" && pathname === "/api/oauth/validate") {
|
|
11429
|
-
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
11430
|
-
const record = deps.credentialRecord();
|
|
11431
|
-
if (record === void 0) {
|
|
11432
|
-
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" });
|
|
11433
|
-
return true;
|
|
11434
|
-
}
|
|
11435
|
-
answerServerJson(response, HTTP_STATUS.ok, { scopes: [...RC_SELF_HOST_SCOPE_LIST], expiresAt: null, subscriptionType: "max", account_uuid: record.accountUuid, organization_uuid: record.organizationUuid });
|
|
11436
|
-
return true;
|
|
11437
|
-
}
|
|
11438
|
-
if (method === "POST" && (pathname === "/api/event_logging/v2/batch" || pathname === "/api/claude_code/metrics" || pathname === "/api/claude_cli_feedback")) {
|
|
11439
|
-
await readBody2(request4, CONTROL_BODY_CAP_BYTES);
|
|
11440
|
-
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
11441
|
-
return true;
|
|
11442
|
-
}
|
|
11443
|
-
if (method === "GET" && pathname === "/api/hello") {
|
|
11444
|
-
answerServerJson(response, HTTP_STATUS.ok, {});
|
|
11445
|
-
return true;
|
|
11446
|
-
}
|
|
11447
|
-
return false;
|
|
11448
|
-
}
|
|
11449
|
-
};
|
|
11674
|
+
const local = createRcSelfHostLocal({ now: deps.now, credentialRecord: deps.credentialRecord, readBody: readBody2 });
|
|
11450
11675
|
return {
|
|
11451
11676
|
route,
|
|
11452
11677
|
local,
|
|
11453
11678
|
close: () => {
|
|
11454
11679
|
clearInterval(timer);
|
|
11455
11680
|
clearInterval(keepalive);
|
|
11456
|
-
for (const
|
|
11457
|
-
for (const sink of [...
|
|
11681
|
+
for (const conversation of conversations.values()) {
|
|
11682
|
+
for (const sink of [...conversation.workerStreams, ...conversation.clientStreams]) {
|
|
11458
11683
|
sink.retire();
|
|
11459
11684
|
}
|
|
11460
11685
|
}
|
|
11461
11686
|
sessions.clear();
|
|
11687
|
+
conversations.clear();
|
|
11462
11688
|
}
|
|
11463
11689
|
};
|
|
11464
11690
|
}
|
|
@@ -11471,7 +11697,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
|
|
|
11471
11697
|
var RC_SELF_HOST_RECORD_FILE = "credential.json";
|
|
11472
11698
|
var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
|
|
11473
11699
|
var SELF_HOST_SUBSCRIPTION_TYPE = "max";
|
|
11474
|
-
function
|
|
11700
|
+
function isRecord11(value) {
|
|
11475
11701
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11476
11702
|
}
|
|
11477
11703
|
function readJsonObject(fs12, file) {
|
|
@@ -11485,7 +11711,7 @@ function readJsonObject(fs12, file) {
|
|
|
11485
11711
|
} catch {
|
|
11486
11712
|
return void 0;
|
|
11487
11713
|
}
|
|
11488
|
-
return
|
|
11714
|
+
return isRecord11(parsed) ? parsed : void 0;
|
|
11489
11715
|
}
|
|
11490
11716
|
function readRcSelfHostRecord(fs12, frontdoorDir) {
|
|
11491
11717
|
const parsed = readJsonObject(fs12, path28.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
|
|
@@ -11509,7 +11735,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11509
11735
|
const recordFile = path28.join(recordDir, RC_SELF_HOST_RECORD_FILE);
|
|
11510
11736
|
const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
|
|
11511
11737
|
const existingCredentials = readJsonObject(deps.fs, credentialsFile);
|
|
11512
|
-
const existingOauth =
|
|
11738
|
+
const existingOauth = isRecord11(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
|
|
11513
11739
|
const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
|
|
11514
11740
|
const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
|
|
11515
11741
|
if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
|
|
@@ -11526,7 +11752,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11526
11752
|
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)}
|
|
11527
11753
|
`);
|
|
11528
11754
|
const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
|
|
11529
|
-
const existingBlock = (value) =>
|
|
11755
|
+
const existingBlock = (value) => isRecord11(value) ? value : {};
|
|
11530
11756
|
deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
|
|
11531
11757
|
...claudeJson,
|
|
11532
11758
|
oauthAccount: {
|
|
@@ -11725,7 +11951,7 @@ import path30 from "node:path";
|
|
|
11725
11951
|
// package.json
|
|
11726
11952
|
var package_default = {
|
|
11727
11953
|
name: "agent-shim",
|
|
11728
|
-
version: "8.
|
|
11954
|
+
version: "8.27.0",
|
|
11729
11955
|
description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
11730
11956
|
license: "MIT",
|
|
11731
11957
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -12988,11 +13214,15 @@ export {
|
|
|
12988
13214
|
RC_WEB_FETCH_MAX_BYTES,
|
|
12989
13215
|
RC_WEB_FETCH_MAX_REDIRECTS,
|
|
12990
13216
|
ROUTED_PATH_PREFIX,
|
|
13217
|
+
RcLiveRateLimitSchema,
|
|
12991
13218
|
RcPermissionModeSchema,
|
|
13219
|
+
RcRateLimitInfoSchema,
|
|
13220
|
+
RcRateLimitWindowSchema,
|
|
12992
13221
|
RcStreamEnvelopeSchema,
|
|
12993
13222
|
RcStreamEventSchema,
|
|
12994
13223
|
UsageError,
|
|
12995
13224
|
UsageListOutputSchema,
|
|
13225
|
+
UsageLiveOutputSchema,
|
|
12996
13226
|
UsageSnapshotError,
|
|
12997
13227
|
UsageSnapshotSchema,
|
|
12998
13228
|
UsageWindowsInputSchema,
|
|
@@ -13044,6 +13274,7 @@ export {
|
|
|
13044
13274
|
createRcControlHandler,
|
|
13045
13275
|
createRcCredentialStore,
|
|
13046
13276
|
createRcEventFanout,
|
|
13277
|
+
createRcLiveUsage,
|
|
13047
13278
|
createRcSelfHostSurface,
|
|
13048
13279
|
createRcSessionTracker,
|
|
13049
13280
|
createRcStreamHub,
|
|
@@ -13105,6 +13336,7 @@ export {
|
|
|
13105
13336
|
rcEventWriteResultFromAnswer,
|
|
13106
13337
|
rcFanoutOnDoorHub,
|
|
13107
13338
|
rcObserverAsPassthrough,
|
|
13339
|
+
rcRateLimitInfoOf,
|
|
13108
13340
|
rcSelfHostFromEnv,
|
|
13109
13341
|
rcWebFetchAllowsPrivateFromEnv,
|
|
13110
13342
|
readActiveIdentity,
|