agent-shim 8.24.0 → 8.26.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 +713 -259
- package/dist/index.cjs +468 -113
- package/dist/index.mjs +460 -113
- package/dist/types/index.d.ts +310 -6
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3207,9 +3207,6 @@ var HTTP_STATUS = {
|
|
|
3207
3207
|
badGateway: 502
|
|
3208
3208
|
};
|
|
3209
3209
|
|
|
3210
|
-
// src/frontdoor/rcControl.ts
|
|
3211
|
-
import * as https from "node:https";
|
|
3212
|
-
|
|
3213
3210
|
// src/frontdoor/rcSessions.ts
|
|
3214
3211
|
var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
|
|
3215
3212
|
var RC_SESSION_ID_PREFIX = "cse_";
|
|
@@ -3807,7 +3804,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
|
3807
3804
|
}
|
|
3808
3805
|
return result;
|
|
3809
3806
|
}
|
|
3810
|
-
async function deliverRcClientPayload(deps, sessionId, payload, action) {
|
|
3807
|
+
async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
|
|
3811
3808
|
const credential = deps.credentialOf(sessionId);
|
|
3812
3809
|
if (credential === void 0) {
|
|
3813
3810
|
return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
|
|
@@ -3818,7 +3815,7 @@ async function deliverRcClientPayload(deps, sessionId, payload, action) {
|
|
|
3818
3815
|
const body = JSON.stringify(buildRcEventWriteBody(payload));
|
|
3819
3816
|
let answer;
|
|
3820
3817
|
try {
|
|
3821
|
-
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
3818
|
+
answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
|
|
3822
3819
|
} catch (error) {
|
|
3823
3820
|
return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
|
|
3824
3821
|
}
|
|
@@ -3912,8 +3909,18 @@ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, c
|
|
|
3912
3909
|
async function sendRcKeepAlive(deps, sessionId) {
|
|
3913
3910
|
return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
|
|
3914
3911
|
}
|
|
3912
|
+
async function teleportRcSession(deps, sessionId, marker) {
|
|
3913
|
+
if (marker === "") {
|
|
3914
|
+
return { ok: false, message: "a teleport write needs a non-empty marker: the teleport relay anchors on a marker line's uuid and string content, and an empty string carries neither" };
|
|
3915
|
+
}
|
|
3916
|
+
return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
|
|
3917
|
+
}
|
|
3918
|
+
function buildRcMarkReadBody(eventId) {
|
|
3919
|
+
return eventId === void 0 ? {} : { event_id: eventId };
|
|
3920
|
+
}
|
|
3915
3921
|
|
|
3916
3922
|
// src/frontdoor/rcControl.ts
|
|
3923
|
+
import * as https from "node:https";
|
|
3917
3924
|
var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
|
|
3918
3925
|
var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
3919
3926
|
var CONTROL_BODY_CAP_BYTES = 10485760;
|
|
@@ -4087,6 +4094,15 @@ function createRcControlHandler(deps) {
|
|
|
4087
4094
|
const callbackUrl = bodyString(body, "callbackUrl");
|
|
4088
4095
|
return session === void 0 || serverName === void 0 || callbackUrl === void 0 ? { error: "the mcp-oauth-callback-url body must be JSON naming a non-empty session id, server name, and callback URL" } : { session, run: async () => await deps.mcpOAuthCallbackUrl(session, serverName, callbackUrl) };
|
|
4089
4096
|
}
|
|
4097
|
+
},
|
|
4098
|
+
{
|
|
4099
|
+
name: "teleport",
|
|
4100
|
+
path: "teleport",
|
|
4101
|
+
read: (body) => {
|
|
4102
|
+
const session = bodyString(body, "session");
|
|
4103
|
+
const marker = bodyString(body, "marker");
|
|
4104
|
+
return session === void 0 || marker === void 0 ? { error: "the teleport body must be JSON naming a non-empty session id and marker" } : { session, run: async () => await deps.teleport(session, marker) };
|
|
4105
|
+
}
|
|
4090
4106
|
}
|
|
4091
4107
|
];
|
|
4092
4108
|
return (request4, response) => {
|
|
@@ -4450,7 +4466,8 @@ function frontDoorRcControl(transport, token) {
|
|
|
4450
4466
|
mcpStatus: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-status`, JSON.stringify({ session: sessionId }), (status, message) => `requesting the MCP status of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4451
4467
|
mcpReconnect: async (sessionId, serverName) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-reconnect`, JSON.stringify({ session: sessionId, serverName }), (status, message) => `reconnecting MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4452
4468
|
mcpAuthenticate: async (sessionId, serverName, redirectUri) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-authenticate`, JSON.stringify({ session: sessionId, serverName, redirectUri }), (status, message) => `authenticating MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4453
|
-
mcpOAuthCallbackUrl: async (sessionId, serverName, callbackUrl) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-oauth-callback-url`, JSON.stringify({ session: sessionId, serverName, callbackUrl }), (status, message) => `handing MCP server ${serverName}'s OAuth callback to ${sessionId} failed (HTTP ${String(status)}): ${message}`)
|
|
4469
|
+
mcpOAuthCallbackUrl: async (sessionId, serverName, callbackUrl) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-oauth-callback-url`, JSON.stringify({ session: sessionId, serverName, callbackUrl }), (status, message) => `handing MCP server ${serverName}'s OAuth callback to ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4470
|
+
teleport: async (sessionId, marker) => await writeOf(`${CONTROL_PATH_PREFIX}/teleport`, JSON.stringify({ session: sessionId, marker }), (status, message) => `teleporting a marker into ${sessionId} failed (HTTP ${String(status)}): ${message}`)
|
|
4454
4471
|
};
|
|
4455
4472
|
}
|
|
4456
4473
|
|
|
@@ -4597,36 +4614,38 @@ function tlsFacts(options) {
|
|
|
4597
4614
|
}
|
|
4598
4615
|
function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
4599
4616
|
const agent = new ExemptTlsAgent();
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
const
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
chunks.push(chunk);
|
|
4622
|
-
});
|
|
4623
|
-
response.on("end", () => {
|
|
4624
|
-
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4625
|
-
});
|
|
4617
|
+
const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
|
|
4618
|
+
const options = {
|
|
4619
|
+
host: target.host,
|
|
4620
|
+
port: target.port,
|
|
4621
|
+
method: "POST",
|
|
4622
|
+
path: path32,
|
|
4623
|
+
// 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.
|
|
4624
|
+
headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
|
|
4625
|
+
agent
|
|
4626
|
+
};
|
|
4627
|
+
options.servername = target.servername ?? target.host;
|
|
4628
|
+
if (target.ca !== void 0) {
|
|
4629
|
+
options.ca = [...target.ca];
|
|
4630
|
+
}
|
|
4631
|
+
if (target.rejectUnauthorized !== void 0) {
|
|
4632
|
+
options.rejectUnauthorized = target.rejectUnauthorized;
|
|
4633
|
+
}
|
|
4634
|
+
const request4 = https2.request(options, (response) => {
|
|
4635
|
+
const chunks = [];
|
|
4636
|
+
response.on("data", (chunk) => {
|
|
4637
|
+
chunks.push(chunk);
|
|
4626
4638
|
});
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4639
|
+
response.on("end", () => {
|
|
4640
|
+
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4641
|
+
});
|
|
4642
|
+
});
|
|
4643
|
+
request4.once("error", reject);
|
|
4644
|
+
request4.end(body);
|
|
4645
|
+
});
|
|
4646
|
+
return {
|
|
4647
|
+
writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
|
|
4648
|
+
writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
|
|
4630
4649
|
};
|
|
4631
4650
|
}
|
|
4632
4651
|
var NO_CHUNKS = {
|
|
@@ -4644,7 +4663,8 @@ function lateRcEventDial(resolve) {
|
|
|
4644
4663
|
return built.dial;
|
|
4645
4664
|
};
|
|
4646
4665
|
return {
|
|
4647
|
-
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
|
|
4666
|
+
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
|
|
4667
|
+
writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
|
|
4648
4668
|
};
|
|
4649
4669
|
}
|
|
4650
4670
|
function lateRcStreamDial(resolve) {
|
|
@@ -4657,7 +4677,8 @@ function lateRcStreamDial(resolve) {
|
|
|
4657
4677
|
return built.dial;
|
|
4658
4678
|
};
|
|
4659
4679
|
return {
|
|
4660
|
-
announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
|
|
4680
|
+
announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
|
|
4681
|
+
markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
|
|
4661
4682
|
openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
|
|
4662
4683
|
};
|
|
4663
4684
|
}
|
|
@@ -4699,11 +4720,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4699
4720
|
return Buffer.concat(chunks).toString("utf8");
|
|
4700
4721
|
};
|
|
4701
4722
|
return {
|
|
4702
|
-
announcePresence: async (sessionId, headers, clientId) => {
|
|
4703
|
-
const body = JSON.stringify({ client_id: clientId, clear
|
|
4723
|
+
announcePresence: async (sessionId, headers, clientId, clear) => {
|
|
4724
|
+
const body = JSON.stringify({ client_id: clientId, clear });
|
|
4704
4725
|
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
4705
4726
|
return { status, body: await readAll(response) };
|
|
4706
4727
|
},
|
|
4728
|
+
markRead: async (sessionId, headers, eventId) => {
|
|
4729
|
+
const body = JSON.stringify(buildRcMarkReadBody(eventId));
|
|
4730
|
+
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
4731
|
+
return { status, body: await readAll(response) };
|
|
4732
|
+
},
|
|
4707
4733
|
openStream: async (sessionId, headers, resume, signal) => {
|
|
4708
4734
|
const path32 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
|
|
4709
4735
|
const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
|
|
@@ -5048,6 +5074,35 @@ var RcStreamEventSchema = z4.strictObject({
|
|
|
5048
5074
|
session: z4.string(),
|
|
5049
5075
|
envelope: RcStreamEnvelopeSchema
|
|
5050
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
|
+
});
|
|
5051
5106
|
var RcSessionQuerySchema = z4.strictObject({
|
|
5052
5107
|
session: z4.string().min(1).optional()
|
|
5053
5108
|
});
|
|
@@ -5114,6 +5169,10 @@ var RcMcpOAuthCallbackUrlInputSchema = z4.strictObject({
|
|
|
5114
5169
|
serverName: z4.string().min(1),
|
|
5115
5170
|
callbackUrl: z4.string().min(1)
|
|
5116
5171
|
});
|
|
5172
|
+
var RcTeleportInputSchema = z4.strictObject({
|
|
5173
|
+
session: z4.string().min(1),
|
|
5174
|
+
marker: z4.string().min(1)
|
|
5175
|
+
});
|
|
5117
5176
|
var RcListOutputSchema = z4.strictObject({ sessions: z4.readonly(z4.array(RcSessionSummarySchema)) });
|
|
5118
5177
|
var RcStatusOutputSchema = z4.strictObject({ statuses: z4.readonly(z4.array(RcSessionStatusSchema)) });
|
|
5119
5178
|
var RcPendingOutputSchema = z4.strictObject({ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema)) });
|
|
@@ -5198,6 +5257,7 @@ function createRcApiRouter(deps) {
|
|
|
5198
5257
|
mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
|
|
5199
5258
|
mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
|
|
5200
5259
|
mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
|
|
5260
|
+
teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
|
|
5201
5261
|
subscribe: authed.input(RcSessionQuerySchema).output(eventIterator(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
|
|
5202
5262
|
const queue = [];
|
|
5203
5263
|
let wake;
|
|
@@ -9653,6 +9713,12 @@ var UsageWindowsOutputSchema = z19.strictObject({
|
|
|
9653
9713
|
fiveHour: EffectiveWindowSchema.optional(),
|
|
9654
9714
|
sevenDay: EffectiveWindowSchema.optional()
|
|
9655
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
|
+
});
|
|
9656
9722
|
var FrontDoorSessionStatusSchema = z19.strictObject({
|
|
9657
9723
|
pid: z19.int().positive(),
|
|
9658
9724
|
startedAt: z19.number(),
|
|
@@ -9767,6 +9833,16 @@ function createControlApiRouter(deps) {
|
|
|
9767
9833
|
...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
|
|
9768
9834
|
...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
|
|
9769
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 };
|
|
9770
9846
|
})
|
|
9771
9847
|
},
|
|
9772
9848
|
frontdoor: {
|
|
@@ -9987,6 +10063,42 @@ function createLaunchEventPublisher(hub, now) {
|
|
|
9987
10063
|
};
|
|
9988
10064
|
}
|
|
9989
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
|
+
|
|
9990
10102
|
// src/frontdoor/rcStream.ts
|
|
9991
10103
|
var RC_STREAM_EVENT_NAME = "client_event";
|
|
9992
10104
|
var SSE_BYTE_ORDER_MARK = 65279;
|
|
@@ -10049,7 +10161,7 @@ function createSseParser() {
|
|
|
10049
10161
|
}
|
|
10050
10162
|
};
|
|
10051
10163
|
}
|
|
10052
|
-
function
|
|
10164
|
+
function isRecord6(value) {
|
|
10053
10165
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10054
10166
|
}
|
|
10055
10167
|
function sequenceNumOf(value) {
|
|
@@ -10071,7 +10183,7 @@ function parseRcStreamEnvelope(parsed) {
|
|
|
10071
10183
|
} catch {
|
|
10072
10184
|
return void 0;
|
|
10073
10185
|
}
|
|
10074
|
-
if (!
|
|
10186
|
+
if (!isRecord6(data)) {
|
|
10075
10187
|
return void 0;
|
|
10076
10188
|
}
|
|
10077
10189
|
const eventType = data.event_type;
|
|
@@ -10154,9 +10266,26 @@ function createRcStreamHub(deps) {
|
|
|
10154
10266
|
}
|
|
10155
10267
|
return headers;
|
|
10156
10268
|
};
|
|
10269
|
+
let receiptFailureLogged = false;
|
|
10270
|
+
const sendReceipt = (attachment, headers, eventId) => {
|
|
10271
|
+
if (eventId === void 0) {
|
|
10272
|
+
return;
|
|
10273
|
+
}
|
|
10274
|
+
deps.dial.markRead(attachment.sessionId, headers, eventId).then(
|
|
10275
|
+
() => {
|
|
10276
|
+
receiptFailureLogged = false;
|
|
10277
|
+
},
|
|
10278
|
+
(error) => {
|
|
10279
|
+
if (!receiptFailureLogged) {
|
|
10280
|
+
receiptFailureLogged = true;
|
|
10281
|
+
deps.log?.(`rc stream ${attachment.sessionId}: the read receipt for event ${eventId} could not be sent (${error instanceof Error ? error.message : String(error)}); further receipt failures stay quiet until one lands`);
|
|
10282
|
+
}
|
|
10283
|
+
}
|
|
10284
|
+
);
|
|
10285
|
+
};
|
|
10157
10286
|
const attempt = async (attachment, credential) => {
|
|
10158
10287
|
const headers = streamHeaders(credential);
|
|
10159
|
-
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
|
|
10288
|
+
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
|
|
10160
10289
|
if (presence.status === HTTP_STATUS.unauthorized) {
|
|
10161
10290
|
return "unauthorized";
|
|
10162
10291
|
}
|
|
@@ -10176,6 +10305,7 @@ function createRcStreamHub(deps) {
|
|
|
10176
10305
|
const parser = createSseParser();
|
|
10177
10306
|
for await (const chunk of answer.chunks) {
|
|
10178
10307
|
attachment.controller.signal.throwIfAborted();
|
|
10308
|
+
let lastEventId;
|
|
10179
10309
|
for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
|
|
10180
10310
|
const envelope = parseRcStreamEnvelope(parsed);
|
|
10181
10311
|
if (envelope === void 0) {
|
|
@@ -10183,10 +10313,21 @@ function createRcStreamHub(deps) {
|
|
|
10183
10313
|
}
|
|
10184
10314
|
deps.fileStreamEvent(attachment.sessionId, envelope);
|
|
10185
10315
|
deps.fanout.publish({ session: attachment.sessionId, envelope });
|
|
10316
|
+
if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
|
|
10317
|
+
lastEventId = envelope.event_id;
|
|
10318
|
+
}
|
|
10186
10319
|
}
|
|
10320
|
+
sendReceipt(attachment, headers, lastEventId);
|
|
10187
10321
|
}
|
|
10188
10322
|
return "dropped";
|
|
10189
10323
|
};
|
|
10324
|
+
const clearPresence = (attachment, headers) => {
|
|
10325
|
+
if (headers === void 0) {
|
|
10326
|
+
return;
|
|
10327
|
+
}
|
|
10328
|
+
void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
|
|
10329
|
+
});
|
|
10330
|
+
};
|
|
10190
10331
|
const runLoop = async (attachment) => {
|
|
10191
10332
|
let retriedUnauthorized = false;
|
|
10192
10333
|
for (; ; ) {
|
|
@@ -10198,6 +10339,7 @@ function createRcStreamHub(deps) {
|
|
|
10198
10339
|
if (!live || credential?.authorization === void 0) {
|
|
10199
10340
|
deps.log?.(`rc stream ${attachment.sessionId}: the attachment ended (${live ? "the session's client credential is no longer known" : "the tracker gave up on the session"})`);
|
|
10200
10341
|
persistCursor(attachment.sessionId);
|
|
10342
|
+
clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
|
|
10201
10343
|
if (attachments.get(attachment.sessionId) === attachment) {
|
|
10202
10344
|
attachments.delete(attachment.sessionId);
|
|
10203
10345
|
}
|
|
@@ -10260,6 +10402,8 @@ function createRcStreamHub(deps) {
|
|
|
10260
10402
|
close: () => {
|
|
10261
10403
|
for (const attachment of attachments.values()) {
|
|
10262
10404
|
persistCursor(attachment.sessionId);
|
|
10405
|
+
const credential = deps.credentialOf(attachment.sessionId);
|
|
10406
|
+
clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
|
|
10263
10407
|
attachment.stop();
|
|
10264
10408
|
}
|
|
10265
10409
|
attachments.clear();
|
|
@@ -10267,6 +10411,193 @@ function createRcStreamHub(deps) {
|
|
|
10267
10411
|
};
|
|
10268
10412
|
}
|
|
10269
10413
|
|
|
10414
|
+
// src/frontdoor/rcSelfHostEndpoints.ts
|
|
10415
|
+
var RC_SOURCE_WORKER = "worker";
|
|
10416
|
+
var RC_SOURCE_CLIENT = "client";
|
|
10417
|
+
function isRecord7(value) {
|
|
10418
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10419
|
+
}
|
|
10420
|
+
function parseJsonObject2(body) {
|
|
10421
|
+
let parsed;
|
|
10422
|
+
try {
|
|
10423
|
+
parsed = JSON.parse(body);
|
|
10424
|
+
} catch {
|
|
10425
|
+
return void 0;
|
|
10426
|
+
}
|
|
10427
|
+
return isRecord7(parsed) ? parsed : void 0;
|
|
10428
|
+
}
|
|
10429
|
+
function nonEmptyString2(record, field) {
|
|
10430
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10431
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10432
|
+
}
|
|
10433
|
+
function numberOf(record, field) {
|
|
10434
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10435
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10436
|
+
return value;
|
|
10437
|
+
}
|
|
10438
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value)) {
|
|
10439
|
+
return Number(value);
|
|
10440
|
+
}
|
|
10441
|
+
return void 0;
|
|
10442
|
+
}
|
|
10443
|
+
function pageOfRcChannel(url, rows, idOf, rowOf) {
|
|
10444
|
+
const query = new URL(url, "http://127.0.0.1").searchParams;
|
|
10445
|
+
const anchor = query.get("after_event_id") ?? query.get("cursor");
|
|
10446
|
+
const rawLimit = query.get("limit");
|
|
10447
|
+
const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
|
|
10448
|
+
if (limit !== void 0 && limit < 1) {
|
|
10449
|
+
return { error: "the limit names a whole number of rows of at least one" };
|
|
10450
|
+
}
|
|
10451
|
+
const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
|
|
10452
|
+
if (anchor !== null && from === 0) {
|
|
10453
|
+
return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
|
|
10454
|
+
}
|
|
10455
|
+
const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
|
|
10456
|
+
const last = page[page.length - 1];
|
|
10457
|
+
return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
|
|
10458
|
+
}
|
|
10459
|
+
async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
10460
|
+
const { request: request4, response, session } = ctx;
|
|
10461
|
+
if (tail === "client/presence" && method === "POST") {
|
|
10462
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10463
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10464
|
+
const clear = body?.clear;
|
|
10465
|
+
if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
|
|
10466
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a presence announcement is JSON naming a non-empty client_id, with clear only as a boolean or absent" } });
|
|
10467
|
+
return true;
|
|
10468
|
+
}
|
|
10469
|
+
if (clear === true) {
|
|
10470
|
+
session.clients.delete(clientId);
|
|
10471
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
|
|
10472
|
+
} else {
|
|
10473
|
+
session.clients.set(clientId, ctx.now());
|
|
10474
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
|
|
10475
|
+
}
|
|
10476
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
|
|
10477
|
+
return true;
|
|
10478
|
+
}
|
|
10479
|
+
if (tail === "mark_read" && method === "POST") {
|
|
10480
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10481
|
+
const eventId = nonEmptyString2(body, "event_id");
|
|
10482
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10483
|
+
if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
|
|
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" } });
|
|
10485
|
+
return true;
|
|
10486
|
+
}
|
|
10487
|
+
const receipt = ctx.publish(session, clientId ?? RC_SOURCE_CLIENT, { type: "mark_read", ...eventId === void 0 ? {} : { event_id: eventId }, ...clientId === void 0 ? {} : { client_id: clientId } }, true);
|
|
10488
|
+
ctx.log?.(`rc selfhost ${session.id}: ${clientId === void 0 ? "an unnamed client" : `client ${clientId}`} marked ${eventId === void 0 ? "the session read" : `event ${eventId} read`} as sequence ${String(receipt.sequenceNum)}`);
|
|
10489
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10490
|
+
return true;
|
|
10491
|
+
}
|
|
10492
|
+
if (tail === "teleport-events" && method === "POST") {
|
|
10493
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10494
|
+
const events = body?.events;
|
|
10495
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
|
|
10496
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: `a teleport write is a non-empty array of at most ${String(maxBatchEvents)} events` } });
|
|
10497
|
+
return true;
|
|
10498
|
+
}
|
|
10499
|
+
const results = [];
|
|
10500
|
+
for (const event of events) {
|
|
10501
|
+
if (!isRecord7(event) || !isRecord7(event.payload)) {
|
|
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" } });
|
|
10503
|
+
return true;
|
|
10504
|
+
}
|
|
10505
|
+
const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
|
|
10506
|
+
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
10507
|
+
}
|
|
10508
|
+
ctx.sweep();
|
|
10509
|
+
ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
|
|
10510
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { results });
|
|
10511
|
+
return true;
|
|
10512
|
+
}
|
|
10513
|
+
if (tail === "teleport-events" && method === "GET") {
|
|
10514
|
+
const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
|
|
10515
|
+
if ("error" in page) {
|
|
10516
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
|
|
10517
|
+
return true;
|
|
10518
|
+
}
|
|
10519
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, page);
|
|
10520
|
+
return true;
|
|
10521
|
+
}
|
|
10522
|
+
return false;
|
|
10523
|
+
}
|
|
10524
|
+
var compatSessionRow = (session, now) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(now()).toISOString(), session_status: "active" });
|
|
10525
|
+
async function serveRcCompatSessions(ctx, pathname, compatPath) {
|
|
10526
|
+
const { request: request4, response } = ctx;
|
|
10527
|
+
const method = request4.method.toUpperCase();
|
|
10528
|
+
if (pathname === compatPath) {
|
|
10529
|
+
if (method !== "GET") {
|
|
10530
|
+
await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
10531
|
+
return;
|
|
10532
|
+
}
|
|
10533
|
+
const listRefusal = ctx.credentialRefusal(request4.headers);
|
|
10534
|
+
if (listRefusal !== void 0) {
|
|
10535
|
+
await ctx.unauthorized(response, listRefusal);
|
|
10536
|
+
return;
|
|
10537
|
+
}
|
|
10538
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
|
|
10539
|
+
return;
|
|
10540
|
+
}
|
|
10541
|
+
const rest = pathname.slice(`${compatPath}/`.length);
|
|
10542
|
+
const slash = rest.indexOf("/");
|
|
10543
|
+
const address = slash === -1 ? rest : rest.slice(0, slash);
|
|
10544
|
+
const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
|
|
10545
|
+
const refusal = ctx.credentialRefusal(request4.headers);
|
|
10546
|
+
if (refusal !== void 0) {
|
|
10547
|
+
await ctx.unauthorized(response, refusal);
|
|
10548
|
+
return;
|
|
10549
|
+
}
|
|
10550
|
+
const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
|
|
10551
|
+
if (compatTail === "") {
|
|
10552
|
+
if (method === "GET") {
|
|
10553
|
+
if (session === void 0) {
|
|
10554
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10555
|
+
return;
|
|
10556
|
+
}
|
|
10557
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10558
|
+
return;
|
|
10559
|
+
}
|
|
10560
|
+
if (method === "PATCH" || method === "PUT") {
|
|
10561
|
+
if (session === void 0) {
|
|
10562
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10563
|
+
return;
|
|
10564
|
+
}
|
|
10565
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10566
|
+
const title = nonEmptyString2(body, "title");
|
|
10567
|
+
if (title === void 0) {
|
|
10568
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a compatibility session update is JSON naming a non-empty title" } });
|
|
10569
|
+
return;
|
|
10570
|
+
}
|
|
10571
|
+
session.title = title;
|
|
10572
|
+
session.lastTrafficAt = ctx.now();
|
|
10573
|
+
ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
|
|
10574
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10575
|
+
return;
|
|
10576
|
+
}
|
|
10577
|
+
await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session read is a GET and its update a PATCH or PUT" } });
|
|
10578
|
+
return;
|
|
10579
|
+
}
|
|
10580
|
+
if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
|
|
10581
|
+
await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
10582
|
+
if (session === void 0) {
|
|
10583
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10584
|
+
return;
|
|
10585
|
+
}
|
|
10586
|
+
if (compatTail === "unarchive") {
|
|
10587
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10588
|
+
return;
|
|
10589
|
+
}
|
|
10590
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
10591
|
+
sink.retire();
|
|
10592
|
+
}
|
|
10593
|
+
ctx.sessions.delete(session.id);
|
|
10594
|
+
ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
|
|
10595
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10596
|
+
return;
|
|
10597
|
+
}
|
|
10598
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
|
|
10599
|
+
}
|
|
10600
|
+
|
|
10270
10601
|
// src/frontdoor/rcWebFetch.ts
|
|
10271
10602
|
import * as dns2 from "node:dns";
|
|
10272
10603
|
import * as http2 from "node:http";
|
|
@@ -10535,21 +10866,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
|
|
|
10535
10866
|
var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
|
|
10536
10867
|
var RC_SELF_HOST_WORKER_EPOCH = 1;
|
|
10537
10868
|
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
10538
|
-
var RC_SOURCE_WORKER = "worker";
|
|
10539
|
-
var RC_SOURCE_CLIENT = "client";
|
|
10540
10869
|
var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
|
|
10541
10870
|
var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
10542
|
-
function
|
|
10871
|
+
function isRecord8(value) {
|
|
10543
10872
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10544
10873
|
}
|
|
10545
10874
|
function isWebSearchRefusal(value) {
|
|
10546
10875
|
return !Array.isArray(value);
|
|
10547
10876
|
}
|
|
10548
|
-
function
|
|
10877
|
+
function nonEmptyString3(record, field) {
|
|
10549
10878
|
const value = record === void 0 ? void 0 : record[field];
|
|
10550
10879
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10551
10880
|
}
|
|
10552
|
-
function
|
|
10881
|
+
function numberOf2(record, field) {
|
|
10553
10882
|
const value = record === void 0 ? void 0 : record[field];
|
|
10554
10883
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10555
10884
|
return value;
|
|
@@ -10616,14 +10945,14 @@ async function readBody2(request4, capBytes) {
|
|
|
10616
10945
|
});
|
|
10617
10946
|
});
|
|
10618
10947
|
}
|
|
10619
|
-
function
|
|
10948
|
+
function parseJsonObject3(body) {
|
|
10620
10949
|
let parsed;
|
|
10621
10950
|
try {
|
|
10622
10951
|
parsed = JSON.parse(body);
|
|
10623
10952
|
} catch {
|
|
10624
10953
|
return void 0;
|
|
10625
10954
|
}
|
|
10626
|
-
return
|
|
10955
|
+
return isRecord8(parsed) ? parsed : void 0;
|
|
10627
10956
|
}
|
|
10628
10957
|
function mintWorkerJwt(randomToken, issuedAt) {
|
|
10629
10958
|
const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
@@ -10646,11 +10975,16 @@ function createRcSelfHostSurface(deps) {
|
|
|
10646
10975
|
for (const session of sessions.values()) {
|
|
10647
10976
|
while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10648
10977
|
const dropped = session.events.shift();
|
|
10649
|
-
const uuid = dropped === void 0 ? void 0 :
|
|
10978
|
+
const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
|
|
10650
10979
|
if (uuid !== void 0) {
|
|
10651
10980
|
session.seenPayloadUuids.delete(uuid);
|
|
10652
10981
|
}
|
|
10653
10982
|
}
|
|
10983
|
+
for (const [clientId, lastSeenAt] of session.clients) {
|
|
10984
|
+
if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10985
|
+
session.clients.delete(clientId);
|
|
10986
|
+
}
|
|
10987
|
+
}
|
|
10654
10988
|
if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10655
10989
|
sessions.delete(session.id);
|
|
10656
10990
|
}
|
|
@@ -10689,7 +11023,7 @@ function createRcSelfHostSurface(deps) {
|
|
|
10689
11023
|
const frameOf = (event) => {
|
|
10690
11024
|
const envelope = {
|
|
10691
11025
|
event_id: event.eventId,
|
|
10692
|
-
event_type:
|
|
11026
|
+
event_type: nonEmptyString3(event.payload, "type") ?? "event",
|
|
10693
11027
|
sequence_num: event.sequenceNum,
|
|
10694
11028
|
source: event.source,
|
|
10695
11029
|
payload: event.payload,
|
|
@@ -10703,7 +11037,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10703
11037
|
};
|
|
10704
11038
|
const holdStream = async (session, which, wantsReplay, request4, response) => {
|
|
10705
11039
|
const url = new URL(request4.url, "http://127.0.0.1");
|
|
10706
|
-
const cursor =
|
|
11040
|
+
const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
|
|
10707
11041
|
const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
|
|
10708
11042
|
response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
10709
11043
|
response.flush();
|
|
@@ -10732,8 +11066,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10732
11066
|
sink.retire();
|
|
10733
11067
|
response.end();
|
|
10734
11068
|
};
|
|
10735
|
-
const publish = (session, source, payload) => {
|
|
10736
|
-
const uuid =
|
|
11069
|
+
const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
|
|
11070
|
+
const uuid = nonEmptyString3(payload, "uuid");
|
|
10737
11071
|
const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
|
|
10738
11072
|
session.sequenceNum += 1;
|
|
10739
11073
|
const event = {
|
|
@@ -10741,9 +11075,9 @@ data: ${JSON.stringify(envelope)}
|
|
|
10741
11075
|
eventId: uuid ?? deps.newUuid(),
|
|
10742
11076
|
source,
|
|
10743
11077
|
// 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.
|
|
10744
|
-
payload: source ===
|
|
11078
|
+
payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
|
|
10745
11079
|
createdAt: new Date(deps.now()).toISOString(),
|
|
10746
|
-
toWorker
|
|
11080
|
+
toWorker,
|
|
10747
11081
|
storedAt: deps.now()
|
|
10748
11082
|
};
|
|
10749
11083
|
session.events.push(event);
|
|
@@ -10791,8 +11125,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
10791
11125
|
}
|
|
10792
11126
|
session.lastTrafficAt = deps.now();
|
|
10793
11127
|
if (tail === "") {
|
|
11128
|
+
if (method === "PATCH" || method === "PUT") {
|
|
11129
|
+
const refusal = credentialRefusal(request4.headers);
|
|
11130
|
+
if (refusal !== void 0) {
|
|
11131
|
+
await unauthorized(response, refusal);
|
|
11132
|
+
return;
|
|
11133
|
+
}
|
|
11134
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11135
|
+
const title = nonEmptyString3(body, "title");
|
|
11136
|
+
if (title === void 0) {
|
|
11137
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
|
|
11138
|
+
return;
|
|
11139
|
+
}
|
|
11140
|
+
session.title = title;
|
|
11141
|
+
session.lastTrafficAt = deps.now();
|
|
11142
|
+
deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
|
|
11143
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11144
|
+
return;
|
|
11145
|
+
}
|
|
10794
11146
|
if (method !== "GET") {
|
|
10795
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
|
|
11147
|
+
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" } });
|
|
10796
11148
|
return;
|
|
10797
11149
|
}
|
|
10798
11150
|
await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
|
|
@@ -10837,17 +11189,17 @@ data: ${JSON.stringify(envelope)}
|
|
|
10837
11189
|
return;
|
|
10838
11190
|
}
|
|
10839
11191
|
if (workerTail === "" && method === "PUT") {
|
|
10840
|
-
const body =
|
|
10841
|
-
const claimedEpoch =
|
|
11192
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11193
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10842
11194
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10843
11195
|
await epochStale(response);
|
|
10844
11196
|
return;
|
|
10845
11197
|
}
|
|
10846
|
-
const status =
|
|
11198
|
+
const status = nonEmptyString3(body, "worker_status");
|
|
10847
11199
|
if (status !== void 0) {
|
|
10848
11200
|
session.workerStatus = status;
|
|
10849
11201
|
}
|
|
10850
|
-
if (
|
|
11202
|
+
if (isRecord8(body?.external_metadata)) {
|
|
10851
11203
|
session.externalMetadata = body.external_metadata;
|
|
10852
11204
|
}
|
|
10853
11205
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
@@ -10864,8 +11216,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10864
11216
|
return;
|
|
10865
11217
|
}
|
|
10866
11218
|
if (workerTail === "events" && method === "POST") {
|
|
10867
|
-
const body =
|
|
10868
|
-
const claimedEpoch =
|
|
11219
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11220
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10869
11221
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10870
11222
|
await epochStale(response);
|
|
10871
11223
|
return;
|
|
@@ -10877,7 +11229,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10877
11229
|
}
|
|
10878
11230
|
const results = [];
|
|
10879
11231
|
for (const event of events) {
|
|
10880
|
-
if (!
|
|
11232
|
+
if (!isRecord8(event) || !isRecord8(event.payload)) {
|
|
10881
11233
|
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" } });
|
|
10882
11234
|
return;
|
|
10883
11235
|
}
|
|
@@ -10898,8 +11250,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10898
11250
|
return;
|
|
10899
11251
|
}
|
|
10900
11252
|
if (workerTail === "internal-events" && method === "POST") {
|
|
10901
|
-
const body =
|
|
10902
|
-
const claimedEpoch =
|
|
11253
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11254
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10903
11255
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10904
11256
|
await epochStale(response);
|
|
10905
11257
|
return;
|
|
@@ -10910,19 +11262,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
10910
11262
|
return;
|
|
10911
11263
|
}
|
|
10912
11264
|
for (const event of events) {
|
|
10913
|
-
if (
|
|
10914
|
-
session.internalEvents.push({
|
|
11265
|
+
if (isRecord8(event)) {
|
|
11266
|
+
session.internalEvents.push({ eventId: deps.newUuid(), body: event });
|
|
10915
11267
|
}
|
|
10916
11268
|
}
|
|
10917
11269
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
10918
11270
|
return;
|
|
10919
11271
|
}
|
|
10920
11272
|
if (workerTail === "internal-events" && method === "GET") {
|
|
10921
|
-
const
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
11273
|
+
const query = new URL(request4.url, "http://127.0.0.1").searchParams;
|
|
11274
|
+
if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
|
|
11275
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "this surface keeps one internal-events channel per session, so an agent-scoped read has no subset to serve" } });
|
|
11276
|
+
return;
|
|
11277
|
+
}
|
|
11278
|
+
const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
|
|
11279
|
+
if ("error" in page) {
|
|
11280
|
+
const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
|
|
11281
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
|
|
11282
|
+
return;
|
|
11283
|
+
}
|
|
11284
|
+
await answerJson2(response, HTTP_STATUS.ok, page);
|
|
10926
11285
|
return;
|
|
10927
11286
|
}
|
|
10928
11287
|
if (workerTail === "diagnostics" && method === "POST") {
|
|
@@ -10931,8 +11290,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10931
11290
|
return;
|
|
10932
11291
|
}
|
|
10933
11292
|
if (workerTail === "web-fetch" && method === "POST") {
|
|
10934
|
-
const body =
|
|
10935
|
-
const target =
|
|
11293
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11294
|
+
const target = nonEmptyString3(body, "url");
|
|
10936
11295
|
if (target === void 0) {
|
|
10937
11296
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
|
|
10938
11297
|
return;
|
|
@@ -10954,8 +11313,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10954
11313
|
return;
|
|
10955
11314
|
}
|
|
10956
11315
|
if (workerTail === "web-search" && method === "POST") {
|
|
10957
|
-
const body =
|
|
10958
|
-
const query =
|
|
11316
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11317
|
+
const query = nonEmptyString3(body, "query");
|
|
10959
11318
|
if (query === void 0) {
|
|
10960
11319
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
|
|
10961
11320
|
return;
|
|
@@ -10966,7 +11325,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10966
11325
|
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" } });
|
|
10967
11326
|
return;
|
|
10968
11327
|
}
|
|
10969
|
-
const searchProfile =
|
|
11328
|
+
const searchProfile = nonEmptyString3(body, "search_profile");
|
|
10970
11329
|
const backend = deps.webSearch;
|
|
10971
11330
|
if (backend === void 0) {
|
|
10972
11331
|
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
|
|
@@ -10991,7 +11350,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10991
11350
|
return;
|
|
10992
11351
|
}
|
|
10993
11352
|
if (tail === "events" && method === "POST") {
|
|
10994
|
-
const body =
|
|
11353
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10995
11354
|
const events = body?.events;
|
|
10996
11355
|
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
10997
11356
|
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` } });
|
|
@@ -10999,7 +11358,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10999
11358
|
}
|
|
11000
11359
|
const results = [];
|
|
11001
11360
|
for (const event of events) {
|
|
11002
|
-
if (!
|
|
11361
|
+
if (!isRecord8(event) || !isRecord8(event.payload)) {
|
|
11003
11362
|
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" } });
|
|
11004
11363
|
return;
|
|
11005
11364
|
}
|
|
@@ -11011,26 +11370,14 @@ data: ${JSON.stringify(envelope)}
|
|
|
11011
11370
|
return;
|
|
11012
11371
|
}
|
|
11013
11372
|
if (tail === "events" && method === "GET") {
|
|
11014
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type:
|
|
11373
|
+
await answerJson2(response, HTTP_STATUS.ok, { data: session.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 })) });
|
|
11015
11374
|
return;
|
|
11016
11375
|
}
|
|
11017
11376
|
if (tail === "events/stream" && method === "GET") {
|
|
11018
11377
|
await holdStream(session, "clientStreams", () => true, request4, response);
|
|
11019
11378
|
return;
|
|
11020
11379
|
}
|
|
11021
|
-
if (
|
|
11022
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11023
|
-
await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
|
|
11024
|
-
return;
|
|
11025
|
-
}
|
|
11026
|
-
if (tail === "mark_read" && method === "POST") {
|
|
11027
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11028
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11029
|
-
return;
|
|
11030
|
-
}
|
|
11031
|
-
if (tail === "teleport-events" && method === "POST") {
|
|
11032
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11033
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11380
|
+
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)) {
|
|
11034
11381
|
return;
|
|
11035
11382
|
}
|
|
11036
11383
|
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
|
|
@@ -11047,17 +11394,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11047
11394
|
await noCredential(response);
|
|
11048
11395
|
return;
|
|
11049
11396
|
}
|
|
11050
|
-
if (pathname === RC_COMPAT_SESSIONS_PATH) {
|
|
11051
|
-
|
|
11052
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
11053
|
-
return;
|
|
11054
|
-
}
|
|
11055
|
-
const listRefusal = credentialRefusal(request4.headers);
|
|
11056
|
-
if (listRefusal !== void 0) {
|
|
11057
|
-
await unauthorized(response, listRefusal);
|
|
11058
|
-
return;
|
|
11059
|
-
}
|
|
11060
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: [] });
|
|
11397
|
+
if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
|
|
11398
|
+
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);
|
|
11061
11399
|
return;
|
|
11062
11400
|
}
|
|
11063
11401
|
if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
|
|
@@ -11071,11 +11409,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11071
11409
|
await unauthorized(response, createRefusal);
|
|
11072
11410
|
return;
|
|
11073
11411
|
}
|
|
11074
|
-
const body =
|
|
11412
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11075
11413
|
const session = {
|
|
11076
11414
|
id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
|
|
11077
11415
|
createdAt: deps.now(),
|
|
11078
|
-
title:
|
|
11416
|
+
title: nonEmptyString3(body, "title"),
|
|
11079
11417
|
workerJwts: /* @__PURE__ */ new Set(),
|
|
11080
11418
|
sequenceNum: 0,
|
|
11081
11419
|
events: [],
|
|
@@ -11083,6 +11421,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11083
11421
|
workerStreams: /* @__PURE__ */ new Set(),
|
|
11084
11422
|
clientStreams: /* @__PURE__ */ new Set(),
|
|
11085
11423
|
internalEvents: [],
|
|
11424
|
+
clients: /* @__PURE__ */ new Map(),
|
|
11086
11425
|
workerStatus: void 0,
|
|
11087
11426
|
externalMetadata: void 0,
|
|
11088
11427
|
lastTrafficAt: deps.now()
|
|
@@ -11127,7 +11466,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11127
11466
|
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
11128
11467
|
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
11129
11468
|
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
11130
|
-
const body =
|
|
11469
|
+
const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
|
|
11131
11470
|
const record = deps.credentialRecord();
|
|
11132
11471
|
if (record === void 0) {
|
|
11133
11472
|
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" });
|
|
@@ -11213,7 +11552,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
|
|
|
11213
11552
|
var RC_SELF_HOST_RECORD_FILE = "credential.json";
|
|
11214
11553
|
var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
|
|
11215
11554
|
var SELF_HOST_SUBSCRIPTION_TYPE = "max";
|
|
11216
|
-
function
|
|
11555
|
+
function isRecord9(value) {
|
|
11217
11556
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11218
11557
|
}
|
|
11219
11558
|
function readJsonObject(fs12, file) {
|
|
@@ -11227,7 +11566,7 @@ function readJsonObject(fs12, file) {
|
|
|
11227
11566
|
} catch {
|
|
11228
11567
|
return void 0;
|
|
11229
11568
|
}
|
|
11230
|
-
return
|
|
11569
|
+
return isRecord9(parsed) ? parsed : void 0;
|
|
11231
11570
|
}
|
|
11232
11571
|
function readRcSelfHostRecord(fs12, frontdoorDir) {
|
|
11233
11572
|
const parsed = readJsonObject(fs12, path28.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
|
|
@@ -11251,7 +11590,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11251
11590
|
const recordFile = path28.join(recordDir, RC_SELF_HOST_RECORD_FILE);
|
|
11252
11591
|
const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
|
|
11253
11592
|
const existingCredentials = readJsonObject(deps.fs, credentialsFile);
|
|
11254
|
-
const existingOauth =
|
|
11593
|
+
const existingOauth = isRecord9(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
|
|
11255
11594
|
const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
|
|
11256
11595
|
const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
|
|
11257
11596
|
if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
|
|
@@ -11268,7 +11607,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11268
11607
|
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)}
|
|
11269
11608
|
`);
|
|
11270
11609
|
const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
|
|
11271
|
-
const existingBlock = (value) =>
|
|
11610
|
+
const existingBlock = (value) => isRecord9(value) ? value : {};
|
|
11272
11611
|
deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
|
|
11273
11612
|
...claudeJson,
|
|
11274
11613
|
oauthAccount: {
|
|
@@ -11467,7 +11806,7 @@ import path30 from "node:path";
|
|
|
11467
11806
|
// package.json
|
|
11468
11807
|
var package_default = {
|
|
11469
11808
|
name: "agent-shim",
|
|
11470
|
-
version: "8.
|
|
11809
|
+
version: "8.26.0",
|
|
11471
11810
|
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.",
|
|
11472
11811
|
license: "MIT",
|
|
11473
11812
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -12730,11 +13069,15 @@ export {
|
|
|
12730
13069
|
RC_WEB_FETCH_MAX_BYTES,
|
|
12731
13070
|
RC_WEB_FETCH_MAX_REDIRECTS,
|
|
12732
13071
|
ROUTED_PATH_PREFIX,
|
|
13072
|
+
RcLiveRateLimitSchema,
|
|
12733
13073
|
RcPermissionModeSchema,
|
|
13074
|
+
RcRateLimitInfoSchema,
|
|
13075
|
+
RcRateLimitWindowSchema,
|
|
12734
13076
|
RcStreamEnvelopeSchema,
|
|
12735
13077
|
RcStreamEventSchema,
|
|
12736
13078
|
UsageError,
|
|
12737
13079
|
UsageListOutputSchema,
|
|
13080
|
+
UsageLiveOutputSchema,
|
|
12738
13081
|
UsageSnapshotError,
|
|
12739
13082
|
UsageSnapshotSchema,
|
|
12740
13083
|
UsageWindowsInputSchema,
|
|
@@ -12757,6 +13100,7 @@ export {
|
|
|
12757
13100
|
buildRcGetUsagePayload,
|
|
12758
13101
|
buildRcInterruptPayload,
|
|
12759
13102
|
buildRcKeepAlivePayload,
|
|
13103
|
+
buildRcMarkReadBody,
|
|
12760
13104
|
buildRcMcpAuthenticatePayload,
|
|
12761
13105
|
buildRcMcpOAuthCallbackUrlPayload,
|
|
12762
13106
|
buildRcMcpReconnectPayload,
|
|
@@ -12785,6 +13129,7 @@ export {
|
|
|
12785
13129
|
createRcControlHandler,
|
|
12786
13130
|
createRcCredentialStore,
|
|
12787
13131
|
createRcEventFanout,
|
|
13132
|
+
createRcLiveUsage,
|
|
12788
13133
|
createRcSelfHostSurface,
|
|
12789
13134
|
createRcSessionTracker,
|
|
12790
13135
|
createRcStreamHub,
|
|
@@ -12846,6 +13191,7 @@ export {
|
|
|
12846
13191
|
rcEventWriteResultFromAnswer,
|
|
12847
13192
|
rcFanoutOnDoorHub,
|
|
12848
13193
|
rcObserverAsPassthrough,
|
|
13194
|
+
rcRateLimitInfoOf,
|
|
12849
13195
|
rcSelfHostFromEnv,
|
|
12850
13196
|
rcWebFetchAllowsPrivateFromEnv,
|
|
12851
13197
|
readActiveIdentity,
|
|
@@ -12903,6 +13249,7 @@ export {
|
|
|
12903
13249
|
startConnectServer,
|
|
12904
13250
|
submitRcSessionMcpOAuthCallbackUrl,
|
|
12905
13251
|
suggestRcSessionFiles,
|
|
13252
|
+
teleportRcSession,
|
|
12906
13253
|
topLevelNames,
|
|
12907
13254
|
updateDirectoryRule,
|
|
12908
13255
|
updateProvider,
|