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.cjs
CHANGED
|
@@ -114,11 +114,15 @@ __export(index_exports, {
|
|
|
114
114
|
RC_WEB_FETCH_MAX_BYTES: () => RC_WEB_FETCH_MAX_BYTES,
|
|
115
115
|
RC_WEB_FETCH_MAX_REDIRECTS: () => RC_WEB_FETCH_MAX_REDIRECTS,
|
|
116
116
|
ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
|
|
117
|
+
RcLiveRateLimitSchema: () => RcLiveRateLimitSchema,
|
|
117
118
|
RcPermissionModeSchema: () => RcPermissionModeSchema,
|
|
119
|
+
RcRateLimitInfoSchema: () => RcRateLimitInfoSchema,
|
|
120
|
+
RcRateLimitWindowSchema: () => RcRateLimitWindowSchema,
|
|
118
121
|
RcStreamEnvelopeSchema: () => RcStreamEnvelopeSchema,
|
|
119
122
|
RcStreamEventSchema: () => RcStreamEventSchema,
|
|
120
123
|
UsageError: () => UsageError,
|
|
121
124
|
UsageListOutputSchema: () => UsageListOutputSchema,
|
|
125
|
+
UsageLiveOutputSchema: () => UsageLiveOutputSchema,
|
|
122
126
|
UsageSnapshotError: () => UsageSnapshotError,
|
|
123
127
|
UsageSnapshotSchema: () => UsageSnapshotSchema,
|
|
124
128
|
UsageWindowsInputSchema: () => UsageWindowsInputSchema,
|
|
@@ -141,6 +145,7 @@ __export(index_exports, {
|
|
|
141
145
|
buildRcGetUsagePayload: () => buildRcGetUsagePayload,
|
|
142
146
|
buildRcInterruptPayload: () => buildRcInterruptPayload,
|
|
143
147
|
buildRcKeepAlivePayload: () => buildRcKeepAlivePayload,
|
|
148
|
+
buildRcMarkReadBody: () => buildRcMarkReadBody,
|
|
144
149
|
buildRcMcpAuthenticatePayload: () => buildRcMcpAuthenticatePayload,
|
|
145
150
|
buildRcMcpOAuthCallbackUrlPayload: () => buildRcMcpOAuthCallbackUrlPayload,
|
|
146
151
|
buildRcMcpReconnectPayload: () => buildRcMcpReconnectPayload,
|
|
@@ -169,6 +174,7 @@ __export(index_exports, {
|
|
|
169
174
|
createRcControlHandler: () => createRcControlHandler,
|
|
170
175
|
createRcCredentialStore: () => createRcCredentialStore,
|
|
171
176
|
createRcEventFanout: () => createRcEventFanout,
|
|
177
|
+
createRcLiveUsage: () => createRcLiveUsage,
|
|
172
178
|
createRcSelfHostSurface: () => createRcSelfHostSurface,
|
|
173
179
|
createRcSessionTracker: () => createRcSessionTracker,
|
|
174
180
|
createRcStreamHub: () => createRcStreamHub,
|
|
@@ -230,6 +236,7 @@ __export(index_exports, {
|
|
|
230
236
|
rcEventWriteResultFromAnswer: () => rcEventWriteResultFromAnswer,
|
|
231
237
|
rcFanoutOnDoorHub: () => rcFanoutOnDoorHub,
|
|
232
238
|
rcObserverAsPassthrough: () => rcObserverAsPassthrough,
|
|
239
|
+
rcRateLimitInfoOf: () => rcRateLimitInfoOf,
|
|
233
240
|
rcSelfHostFromEnv: () => rcSelfHostFromEnv,
|
|
234
241
|
rcWebFetchAllowsPrivateFromEnv: () => rcWebFetchAllowsPrivateFromEnv,
|
|
235
242
|
readActiveIdentity: () => readActiveIdentity,
|
|
@@ -287,6 +294,7 @@ __export(index_exports, {
|
|
|
287
294
|
startConnectServer: () => startConnectServer,
|
|
288
295
|
submitRcSessionMcpOAuthCallbackUrl: () => submitRcSessionMcpOAuthCallbackUrl,
|
|
289
296
|
suggestRcSessionFiles: () => suggestRcSessionFiles,
|
|
297
|
+
teleportRcSession: () => teleportRcSession,
|
|
290
298
|
topLevelNames: () => topLevelNames,
|
|
291
299
|
updateDirectoryRule: () => updateDirectoryRule,
|
|
292
300
|
updateProvider: () => updateProvider,
|
|
@@ -3504,9 +3512,6 @@ var HTTP_STATUS = {
|
|
|
3504
3512
|
badGateway: 502
|
|
3505
3513
|
};
|
|
3506
3514
|
|
|
3507
|
-
// src/frontdoor/rcControl.ts
|
|
3508
|
-
var https = __toESM(require("node:https"), 1);
|
|
3509
|
-
|
|
3510
3515
|
// src/frontdoor/rcSessions.ts
|
|
3511
3516
|
var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
|
|
3512
3517
|
var RC_SESSION_ID_PREFIX = "cse_";
|
|
@@ -4104,7 +4109,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
|
4104
4109
|
}
|
|
4105
4110
|
return result;
|
|
4106
4111
|
}
|
|
4107
|
-
async function deliverRcClientPayload(deps, sessionId, payload, action) {
|
|
4112
|
+
async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
|
|
4108
4113
|
const credential = deps.credentialOf(sessionId);
|
|
4109
4114
|
if (credential === void 0) {
|
|
4110
4115
|
return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
|
|
@@ -4115,7 +4120,7 @@ async function deliverRcClientPayload(deps, sessionId, payload, action) {
|
|
|
4115
4120
|
const body = JSON.stringify(buildRcEventWriteBody(payload));
|
|
4116
4121
|
let answer;
|
|
4117
4122
|
try {
|
|
4118
|
-
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
4123
|
+
answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
|
|
4119
4124
|
} catch (error) {
|
|
4120
4125
|
return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
|
|
4121
4126
|
}
|
|
@@ -4209,8 +4214,18 @@ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, c
|
|
|
4209
4214
|
async function sendRcKeepAlive(deps, sessionId) {
|
|
4210
4215
|
return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
|
|
4211
4216
|
}
|
|
4217
|
+
async function teleportRcSession(deps, sessionId, marker) {
|
|
4218
|
+
if (marker === "") {
|
|
4219
|
+
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" };
|
|
4220
|
+
}
|
|
4221
|
+
return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
|
|
4222
|
+
}
|
|
4223
|
+
function buildRcMarkReadBody(eventId) {
|
|
4224
|
+
return eventId === void 0 ? {} : { event_id: eventId };
|
|
4225
|
+
}
|
|
4212
4226
|
|
|
4213
4227
|
// src/frontdoor/rcControl.ts
|
|
4228
|
+
var https = __toESM(require("node:https"), 1);
|
|
4214
4229
|
var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
|
|
4215
4230
|
var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
4216
4231
|
var CONTROL_BODY_CAP_BYTES = 10485760;
|
|
@@ -4384,6 +4399,15 @@ function createRcControlHandler(deps) {
|
|
|
4384
4399
|
const callbackUrl = bodyString(body, "callbackUrl");
|
|
4385
4400
|
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) };
|
|
4386
4401
|
}
|
|
4402
|
+
},
|
|
4403
|
+
{
|
|
4404
|
+
name: "teleport",
|
|
4405
|
+
path: "teleport",
|
|
4406
|
+
read: (body) => {
|
|
4407
|
+
const session = bodyString(body, "session");
|
|
4408
|
+
const marker = bodyString(body, "marker");
|
|
4409
|
+
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) };
|
|
4410
|
+
}
|
|
4387
4411
|
}
|
|
4388
4412
|
];
|
|
4389
4413
|
return (request4, response) => {
|
|
@@ -4747,7 +4771,8 @@ function frontDoorRcControl(transport, token) {
|
|
|
4747
4771
|
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}`),
|
|
4748
4772
|
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}`),
|
|
4749
4773
|
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}`),
|
|
4750
|
-
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}`)
|
|
4774
|
+
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}`),
|
|
4775
|
+
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}`)
|
|
4751
4776
|
};
|
|
4752
4777
|
}
|
|
4753
4778
|
|
|
@@ -4894,36 +4919,38 @@ function tlsFacts(options) {
|
|
|
4894
4919
|
}
|
|
4895
4920
|
function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
4896
4921
|
const agent = new ExemptTlsAgent();
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
const
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
chunks.push(chunk);
|
|
4919
|
-
});
|
|
4920
|
-
response.on("end", () => {
|
|
4921
|
-
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4922
|
-
});
|
|
4922
|
+
const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
|
|
4923
|
+
const options = {
|
|
4924
|
+
host: target.host,
|
|
4925
|
+
port: target.port,
|
|
4926
|
+
method: "POST",
|
|
4927
|
+
path: path32,
|
|
4928
|
+
// 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.
|
|
4929
|
+
headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
|
|
4930
|
+
agent
|
|
4931
|
+
};
|
|
4932
|
+
options.servername = target.servername ?? target.host;
|
|
4933
|
+
if (target.ca !== void 0) {
|
|
4934
|
+
options.ca = [...target.ca];
|
|
4935
|
+
}
|
|
4936
|
+
if (target.rejectUnauthorized !== void 0) {
|
|
4937
|
+
options.rejectUnauthorized = target.rejectUnauthorized;
|
|
4938
|
+
}
|
|
4939
|
+
const request4 = https2.request(options, (response) => {
|
|
4940
|
+
const chunks = [];
|
|
4941
|
+
response.on("data", (chunk) => {
|
|
4942
|
+
chunks.push(chunk);
|
|
4923
4943
|
});
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4944
|
+
response.on("end", () => {
|
|
4945
|
+
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4946
|
+
});
|
|
4947
|
+
});
|
|
4948
|
+
request4.once("error", reject);
|
|
4949
|
+
request4.end(body);
|
|
4950
|
+
});
|
|
4951
|
+
return {
|
|
4952
|
+
writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
|
|
4953
|
+
writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
|
|
4927
4954
|
};
|
|
4928
4955
|
}
|
|
4929
4956
|
var NO_CHUNKS = {
|
|
@@ -4941,7 +4968,8 @@ function lateRcEventDial(resolve) {
|
|
|
4941
4968
|
return built.dial;
|
|
4942
4969
|
};
|
|
4943
4970
|
return {
|
|
4944
|
-
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
|
|
4971
|
+
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
|
|
4972
|
+
writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
|
|
4945
4973
|
};
|
|
4946
4974
|
}
|
|
4947
4975
|
function lateRcStreamDial(resolve) {
|
|
@@ -4954,7 +4982,8 @@ function lateRcStreamDial(resolve) {
|
|
|
4954
4982
|
return built.dial;
|
|
4955
4983
|
};
|
|
4956
4984
|
return {
|
|
4957
|
-
announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
|
|
4985
|
+
announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
|
|
4986
|
+
markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
|
|
4958
4987
|
openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
|
|
4959
4988
|
};
|
|
4960
4989
|
}
|
|
@@ -4996,11 +5025,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4996
5025
|
return Buffer.concat(chunks).toString("utf8");
|
|
4997
5026
|
};
|
|
4998
5027
|
return {
|
|
4999
|
-
announcePresence: async (sessionId, headers, clientId) => {
|
|
5000
|
-
const body = JSON.stringify({ client_id: clientId, clear
|
|
5028
|
+
announcePresence: async (sessionId, headers, clientId, clear) => {
|
|
5029
|
+
const body = JSON.stringify({ client_id: clientId, clear });
|
|
5001
5030
|
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
5002
5031
|
return { status, body: await readAll(response) };
|
|
5003
5032
|
},
|
|
5033
|
+
markRead: async (sessionId, headers, eventId) => {
|
|
5034
|
+
const body = JSON.stringify(buildRcMarkReadBody(eventId));
|
|
5035
|
+
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
5036
|
+
return { status, body: await readAll(response) };
|
|
5037
|
+
},
|
|
5004
5038
|
openStream: async (sessionId, headers, resume, signal) => {
|
|
5005
5039
|
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)}`;
|
|
5006
5040
|
const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
|
|
@@ -5345,6 +5379,35 @@ var RcStreamEventSchema = import_zod4.z.strictObject({
|
|
|
5345
5379
|
session: import_zod4.z.string(),
|
|
5346
5380
|
envelope: RcStreamEnvelopeSchema
|
|
5347
5381
|
});
|
|
5382
|
+
var RcRateLimitWindowSchema = import_zod4.z.looseObject({
|
|
5383
|
+
/** 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). */
|
|
5384
|
+
utilization: import_zod4.z.number().optional(),
|
|
5385
|
+
/** 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. */
|
|
5386
|
+
resetsAt: import_zod4.z.number().optional()
|
|
5387
|
+
});
|
|
5388
|
+
var RcRateLimitInfoSchema = import_zod4.z.looseObject({
|
|
5389
|
+
/** The limiting window's own status (`allowed`, `allowed_warning`, `rejected`), asserted only as a string because the vocabulary grows with the CLI. */
|
|
5390
|
+
status: import_zod4.z.string(),
|
|
5391
|
+
/** When the limiting window resets, in unix epoch seconds. */
|
|
5392
|
+
resetsAt: import_zod4.z.number().optional(),
|
|
5393
|
+
/** Which window the top-level fields describe (`five_hour`, `seven_day`, and kin), the payload's own name for it. */
|
|
5394
|
+
rateLimitType: import_zod4.z.string().optional(),
|
|
5395
|
+
/** The fraction of the limiting window used, on the same scale the per-window utilizations carry. */
|
|
5396
|
+
utilization: import_zod4.z.number().optional(),
|
|
5397
|
+
/** Whether usage beyond the plan (extra usage) is currently covering the overflow, the payload's own flag for it. */
|
|
5398
|
+
isUsingOverage: import_zod4.z.boolean().optional(),
|
|
5399
|
+
/** The overage's own status, the same vocabulary the limiting window's `status` carries. */
|
|
5400
|
+
overageStatus: import_zod4.z.string().optional(),
|
|
5401
|
+
/** When the overage window resets, in unix epoch seconds. */
|
|
5402
|
+
overageResetsAt: import_zod4.z.number().optional(),
|
|
5403
|
+
/** 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. */
|
|
5404
|
+
unifiedWindows: import_zod4.z.looseObject({ five_hour: RcRateLimitWindowSchema.optional(), seven_day: RcRateLimitWindowSchema.optional(), seven_day_overage_included: RcRateLimitWindowSchema.optional() }).optional()
|
|
5405
|
+
});
|
|
5406
|
+
var RcLiveRateLimitSchema = import_zod4.z.strictObject({
|
|
5407
|
+
session: import_zod4.z.string(),
|
|
5408
|
+
observedAt: import_zod4.z.number(),
|
|
5409
|
+
rateLimit: RcRateLimitInfoSchema
|
|
5410
|
+
});
|
|
5348
5411
|
var RcSessionQuerySchema = import_zod4.z.strictObject({
|
|
5349
5412
|
session: import_zod4.z.string().min(1).optional()
|
|
5350
5413
|
});
|
|
@@ -5411,6 +5474,10 @@ var RcMcpOAuthCallbackUrlInputSchema = import_zod4.z.strictObject({
|
|
|
5411
5474
|
serverName: import_zod4.z.string().min(1),
|
|
5412
5475
|
callbackUrl: import_zod4.z.string().min(1)
|
|
5413
5476
|
});
|
|
5477
|
+
var RcTeleportInputSchema = import_zod4.z.strictObject({
|
|
5478
|
+
session: import_zod4.z.string().min(1),
|
|
5479
|
+
marker: import_zod4.z.string().min(1)
|
|
5480
|
+
});
|
|
5414
5481
|
var RcListOutputSchema = import_zod4.z.strictObject({ sessions: import_zod4.z.readonly(import_zod4.z.array(RcSessionSummarySchema)) });
|
|
5415
5482
|
var RcStatusOutputSchema = import_zod4.z.strictObject({ statuses: import_zod4.z.readonly(import_zod4.z.array(RcSessionStatusSchema)) });
|
|
5416
5483
|
var RcPendingOutputSchema = import_zod4.z.strictObject({ pending: import_zod4.z.readonly(import_zod4.z.array(RcPendingRequestSummarySchema)) });
|
|
@@ -5495,6 +5562,7 @@ function createRcApiRouter(deps) {
|
|
|
5495
5562
|
mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
|
|
5496
5563
|
mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
|
|
5497
5564
|
mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
|
|
5565
|
+
teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
|
|
5498
5566
|
subscribe: authed.input(RcSessionQuerySchema).output((0, import_server2.eventIterator)(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
|
|
5499
5567
|
const queue = [];
|
|
5500
5568
|
let wake;
|
|
@@ -9950,6 +10018,12 @@ var UsageWindowsOutputSchema = import_zod19.z.strictObject({
|
|
|
9950
10018
|
fiveHour: EffectiveWindowSchema.optional(),
|
|
9951
10019
|
sevenDay: EffectiveWindowSchema.optional()
|
|
9952
10020
|
});
|
|
10021
|
+
var UsageLiveOutputSchema = import_zod19.z.strictObject({
|
|
10022
|
+
/** 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. */
|
|
10023
|
+
latest: RcLiveRateLimitSchema.optional(),
|
|
10024
|
+
/** One observation per session whose stream has filed a rate_limit_event this door generation. */
|
|
10025
|
+
sessions: import_zod19.z.readonly(import_zod19.z.array(RcLiveRateLimitSchema))
|
|
10026
|
+
});
|
|
9953
10027
|
var FrontDoorSessionStatusSchema = import_zod19.z.strictObject({
|
|
9954
10028
|
pid: import_zod19.z.int().positive(),
|
|
9955
10029
|
startedAt: import_zod19.z.number(),
|
|
@@ -10064,6 +10138,16 @@ function createControlApiRouter(deps) {
|
|
|
10064
10138
|
...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
|
|
10065
10139
|
...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
|
|
10066
10140
|
};
|
|
10141
|
+
}),
|
|
10142
|
+
live: authed.input(RcSessionQuerySchema).output(UsageLiveOutputSchema).handler(({ input }) => {
|
|
10143
|
+
const sessions = deps.liveRateLimits(input.session);
|
|
10144
|
+
if (input.session !== void 0 && sessions.length === 0) {
|
|
10145
|
+
if (deps.list().some((session) => session.id === input.session)) {
|
|
10146
|
+
throw new import_client3.ORPCError("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` });
|
|
10147
|
+
}
|
|
10148
|
+
throw new import_client3.ORPCError("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
|
|
10149
|
+
}
|
|
10150
|
+
return { latest: deps.latestRateLimit(), sessions };
|
|
10067
10151
|
})
|
|
10068
10152
|
},
|
|
10069
10153
|
frontdoor: {
|
|
@@ -10284,6 +10368,42 @@ function createLaunchEventPublisher(hub, now) {
|
|
|
10284
10368
|
};
|
|
10285
10369
|
}
|
|
10286
10370
|
|
|
10371
|
+
// src/frontdoor/rcLiveUsage.ts
|
|
10372
|
+
var RC_RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
|
|
10373
|
+
function isRecord5(value) {
|
|
10374
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10375
|
+
}
|
|
10376
|
+
function rcRateLimitInfoOf(event) {
|
|
10377
|
+
const payload = event.envelope.payload;
|
|
10378
|
+
if (!isRecord5(payload) || payload.type !== RC_RATE_LIMIT_EVENT_TYPE) {
|
|
10379
|
+
return void 0;
|
|
10380
|
+
}
|
|
10381
|
+
if (!isRecord5(payload.rate_limit_info)) {
|
|
10382
|
+
return void 0;
|
|
10383
|
+
}
|
|
10384
|
+
const parsed = RcRateLimitInfoSchema.safeParse(payload.rate_limit_info);
|
|
10385
|
+
return parsed.success ? parsed.data : void 0;
|
|
10386
|
+
}
|
|
10387
|
+
function createRcLiveUsage(deps) {
|
|
10388
|
+
const observed = /* @__PURE__ */ new Map();
|
|
10389
|
+
deps.hub.subscribe([DOOR_EVENT_SOURCE_RC], (event) => {
|
|
10390
|
+
const stream = RcStreamEventSchema.safeParse(event.payload);
|
|
10391
|
+
if (!stream.success) {
|
|
10392
|
+
return;
|
|
10393
|
+
}
|
|
10394
|
+
const rateLimit = rcRateLimitInfoOf(stream.data);
|
|
10395
|
+
if (rateLimit === void 0) {
|
|
10396
|
+
return;
|
|
10397
|
+
}
|
|
10398
|
+
observed.set(stream.data.session, { session: stream.data.session, observedAt: deps.now(), rateLimit });
|
|
10399
|
+
});
|
|
10400
|
+
const sorted = () => [...observed.values()].sort((left, right) => left.observedAt - right.observedAt || (left.session < right.session ? -1 : 1));
|
|
10401
|
+
return {
|
|
10402
|
+
liveOf: (sessionId) => sorted().filter((entry) => sessionId === void 0 || entry.session === sessionId),
|
|
10403
|
+
latestOf: () => sorted().at(-1)
|
|
10404
|
+
};
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10287
10407
|
// src/frontdoor/rcStream.ts
|
|
10288
10408
|
var RC_STREAM_EVENT_NAME = "client_event";
|
|
10289
10409
|
var SSE_BYTE_ORDER_MARK = 65279;
|
|
@@ -10346,7 +10466,7 @@ function createSseParser() {
|
|
|
10346
10466
|
}
|
|
10347
10467
|
};
|
|
10348
10468
|
}
|
|
10349
|
-
function
|
|
10469
|
+
function isRecord6(value) {
|
|
10350
10470
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10351
10471
|
}
|
|
10352
10472
|
function sequenceNumOf(value) {
|
|
@@ -10368,7 +10488,7 @@ function parseRcStreamEnvelope(parsed) {
|
|
|
10368
10488
|
} catch {
|
|
10369
10489
|
return void 0;
|
|
10370
10490
|
}
|
|
10371
|
-
if (!
|
|
10491
|
+
if (!isRecord6(data)) {
|
|
10372
10492
|
return void 0;
|
|
10373
10493
|
}
|
|
10374
10494
|
const eventType = data.event_type;
|
|
@@ -10451,9 +10571,26 @@ function createRcStreamHub(deps) {
|
|
|
10451
10571
|
}
|
|
10452
10572
|
return headers;
|
|
10453
10573
|
};
|
|
10574
|
+
let receiptFailureLogged = false;
|
|
10575
|
+
const sendReceipt = (attachment, headers, eventId) => {
|
|
10576
|
+
if (eventId === void 0) {
|
|
10577
|
+
return;
|
|
10578
|
+
}
|
|
10579
|
+
deps.dial.markRead(attachment.sessionId, headers, eventId).then(
|
|
10580
|
+
() => {
|
|
10581
|
+
receiptFailureLogged = false;
|
|
10582
|
+
},
|
|
10583
|
+
(error) => {
|
|
10584
|
+
if (!receiptFailureLogged) {
|
|
10585
|
+
receiptFailureLogged = true;
|
|
10586
|
+
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`);
|
|
10587
|
+
}
|
|
10588
|
+
}
|
|
10589
|
+
);
|
|
10590
|
+
};
|
|
10454
10591
|
const attempt = async (attachment, credential) => {
|
|
10455
10592
|
const headers = streamHeaders(credential);
|
|
10456
|
-
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
|
|
10593
|
+
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
|
|
10457
10594
|
if (presence.status === HTTP_STATUS.unauthorized) {
|
|
10458
10595
|
return "unauthorized";
|
|
10459
10596
|
}
|
|
@@ -10473,6 +10610,7 @@ function createRcStreamHub(deps) {
|
|
|
10473
10610
|
const parser = createSseParser();
|
|
10474
10611
|
for await (const chunk of answer.chunks) {
|
|
10475
10612
|
attachment.controller.signal.throwIfAborted();
|
|
10613
|
+
let lastEventId;
|
|
10476
10614
|
for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
|
|
10477
10615
|
const envelope = parseRcStreamEnvelope(parsed);
|
|
10478
10616
|
if (envelope === void 0) {
|
|
@@ -10480,10 +10618,21 @@ function createRcStreamHub(deps) {
|
|
|
10480
10618
|
}
|
|
10481
10619
|
deps.fileStreamEvent(attachment.sessionId, envelope);
|
|
10482
10620
|
deps.fanout.publish({ session: attachment.sessionId, envelope });
|
|
10621
|
+
if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
|
|
10622
|
+
lastEventId = envelope.event_id;
|
|
10623
|
+
}
|
|
10483
10624
|
}
|
|
10625
|
+
sendReceipt(attachment, headers, lastEventId);
|
|
10484
10626
|
}
|
|
10485
10627
|
return "dropped";
|
|
10486
10628
|
};
|
|
10629
|
+
const clearPresence = (attachment, headers) => {
|
|
10630
|
+
if (headers === void 0) {
|
|
10631
|
+
return;
|
|
10632
|
+
}
|
|
10633
|
+
void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
|
|
10634
|
+
});
|
|
10635
|
+
};
|
|
10487
10636
|
const runLoop = async (attachment) => {
|
|
10488
10637
|
let retriedUnauthorized = false;
|
|
10489
10638
|
for (; ; ) {
|
|
@@ -10495,6 +10644,7 @@ function createRcStreamHub(deps) {
|
|
|
10495
10644
|
if (!live || credential?.authorization === void 0) {
|
|
10496
10645
|
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"})`);
|
|
10497
10646
|
persistCursor(attachment.sessionId);
|
|
10647
|
+
clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
|
|
10498
10648
|
if (attachments.get(attachment.sessionId) === attachment) {
|
|
10499
10649
|
attachments.delete(attachment.sessionId);
|
|
10500
10650
|
}
|
|
@@ -10557,6 +10707,8 @@ function createRcStreamHub(deps) {
|
|
|
10557
10707
|
close: () => {
|
|
10558
10708
|
for (const attachment of attachments.values()) {
|
|
10559
10709
|
persistCursor(attachment.sessionId);
|
|
10710
|
+
const credential = deps.credentialOf(attachment.sessionId);
|
|
10711
|
+
clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
|
|
10560
10712
|
attachment.stop();
|
|
10561
10713
|
}
|
|
10562
10714
|
attachments.clear();
|
|
@@ -10564,6 +10716,193 @@ function createRcStreamHub(deps) {
|
|
|
10564
10716
|
};
|
|
10565
10717
|
}
|
|
10566
10718
|
|
|
10719
|
+
// src/frontdoor/rcSelfHostEndpoints.ts
|
|
10720
|
+
var RC_SOURCE_WORKER = "worker";
|
|
10721
|
+
var RC_SOURCE_CLIENT = "client";
|
|
10722
|
+
function isRecord7(value) {
|
|
10723
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10724
|
+
}
|
|
10725
|
+
function parseJsonObject2(body) {
|
|
10726
|
+
let parsed;
|
|
10727
|
+
try {
|
|
10728
|
+
parsed = JSON.parse(body);
|
|
10729
|
+
} catch {
|
|
10730
|
+
return void 0;
|
|
10731
|
+
}
|
|
10732
|
+
return isRecord7(parsed) ? parsed : void 0;
|
|
10733
|
+
}
|
|
10734
|
+
function nonEmptyString2(record, field) {
|
|
10735
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10736
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10737
|
+
}
|
|
10738
|
+
function numberOf(record, field) {
|
|
10739
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10740
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10741
|
+
return value;
|
|
10742
|
+
}
|
|
10743
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value)) {
|
|
10744
|
+
return Number(value);
|
|
10745
|
+
}
|
|
10746
|
+
return void 0;
|
|
10747
|
+
}
|
|
10748
|
+
function pageOfRcChannel(url, rows, idOf, rowOf) {
|
|
10749
|
+
const query = new URL(url, "http://127.0.0.1").searchParams;
|
|
10750
|
+
const anchor = query.get("after_event_id") ?? query.get("cursor");
|
|
10751
|
+
const rawLimit = query.get("limit");
|
|
10752
|
+
const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
|
|
10753
|
+
if (limit !== void 0 && limit < 1) {
|
|
10754
|
+
return { error: "the limit names a whole number of rows of at least one" };
|
|
10755
|
+
}
|
|
10756
|
+
const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
|
|
10757
|
+
if (anchor !== null && from === 0) {
|
|
10758
|
+
return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
|
|
10759
|
+
}
|
|
10760
|
+
const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
|
|
10761
|
+
const last = page[page.length - 1];
|
|
10762
|
+
return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
|
|
10763
|
+
}
|
|
10764
|
+
async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
10765
|
+
const { request: request4, response, session } = ctx;
|
|
10766
|
+
if (tail === "client/presence" && method === "POST") {
|
|
10767
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10768
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10769
|
+
const clear = body?.clear;
|
|
10770
|
+
if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
|
|
10771
|
+
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" } });
|
|
10772
|
+
return true;
|
|
10773
|
+
}
|
|
10774
|
+
if (clear === true) {
|
|
10775
|
+
session.clients.delete(clientId);
|
|
10776
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
|
|
10777
|
+
} else {
|
|
10778
|
+
session.clients.set(clientId, ctx.now());
|
|
10779
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
|
|
10780
|
+
}
|
|
10781
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
|
|
10782
|
+
return true;
|
|
10783
|
+
}
|
|
10784
|
+
if (tail === "mark_read" && method === "POST") {
|
|
10785
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10786
|
+
const eventId = nonEmptyString2(body, "event_id");
|
|
10787
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10788
|
+
if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
|
|
10789
|
+
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" } });
|
|
10790
|
+
return true;
|
|
10791
|
+
}
|
|
10792
|
+
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);
|
|
10793
|
+
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)}`);
|
|
10794
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10795
|
+
return true;
|
|
10796
|
+
}
|
|
10797
|
+
if (tail === "teleport-events" && method === "POST") {
|
|
10798
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10799
|
+
const events = body?.events;
|
|
10800
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
|
|
10801
|
+
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` } });
|
|
10802
|
+
return true;
|
|
10803
|
+
}
|
|
10804
|
+
const results = [];
|
|
10805
|
+
for (const event of events) {
|
|
10806
|
+
if (!isRecord7(event) || !isRecord7(event.payload)) {
|
|
10807
|
+
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" } });
|
|
10808
|
+
return true;
|
|
10809
|
+
}
|
|
10810
|
+
const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
|
|
10811
|
+
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
10812
|
+
}
|
|
10813
|
+
ctx.sweep();
|
|
10814
|
+
ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
|
|
10815
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { results });
|
|
10816
|
+
return true;
|
|
10817
|
+
}
|
|
10818
|
+
if (tail === "teleport-events" && method === "GET") {
|
|
10819
|
+
const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
|
|
10820
|
+
if ("error" in page) {
|
|
10821
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
|
|
10822
|
+
return true;
|
|
10823
|
+
}
|
|
10824
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, page);
|
|
10825
|
+
return true;
|
|
10826
|
+
}
|
|
10827
|
+
return false;
|
|
10828
|
+
}
|
|
10829
|
+
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" });
|
|
10830
|
+
async function serveRcCompatSessions(ctx, pathname, compatPath) {
|
|
10831
|
+
const { request: request4, response } = ctx;
|
|
10832
|
+
const method = request4.method.toUpperCase();
|
|
10833
|
+
if (pathname === compatPath) {
|
|
10834
|
+
if (method !== "GET") {
|
|
10835
|
+
await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
10836
|
+
return;
|
|
10837
|
+
}
|
|
10838
|
+
const listRefusal = ctx.credentialRefusal(request4.headers);
|
|
10839
|
+
if (listRefusal !== void 0) {
|
|
10840
|
+
await ctx.unauthorized(response, listRefusal);
|
|
10841
|
+
return;
|
|
10842
|
+
}
|
|
10843
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
|
|
10844
|
+
return;
|
|
10845
|
+
}
|
|
10846
|
+
const rest = pathname.slice(`${compatPath}/`.length);
|
|
10847
|
+
const slash = rest.indexOf("/");
|
|
10848
|
+
const address = slash === -1 ? rest : rest.slice(0, slash);
|
|
10849
|
+
const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
|
|
10850
|
+
const refusal = ctx.credentialRefusal(request4.headers);
|
|
10851
|
+
if (refusal !== void 0) {
|
|
10852
|
+
await ctx.unauthorized(response, refusal);
|
|
10853
|
+
return;
|
|
10854
|
+
}
|
|
10855
|
+
const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
|
|
10856
|
+
if (compatTail === "") {
|
|
10857
|
+
if (method === "GET") {
|
|
10858
|
+
if (session === void 0) {
|
|
10859
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10860
|
+
return;
|
|
10861
|
+
}
|
|
10862
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10863
|
+
return;
|
|
10864
|
+
}
|
|
10865
|
+
if (method === "PATCH" || method === "PUT") {
|
|
10866
|
+
if (session === void 0) {
|
|
10867
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10868
|
+
return;
|
|
10869
|
+
}
|
|
10870
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10871
|
+
const title = nonEmptyString2(body, "title");
|
|
10872
|
+
if (title === void 0) {
|
|
10873
|
+
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" } });
|
|
10874
|
+
return;
|
|
10875
|
+
}
|
|
10876
|
+
session.title = title;
|
|
10877
|
+
session.lastTrafficAt = ctx.now();
|
|
10878
|
+
ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
|
|
10879
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10880
|
+
return;
|
|
10881
|
+
}
|
|
10882
|
+
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" } });
|
|
10883
|
+
return;
|
|
10884
|
+
}
|
|
10885
|
+
if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
|
|
10886
|
+
await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
10887
|
+
if (session === void 0) {
|
|
10888
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10889
|
+
return;
|
|
10890
|
+
}
|
|
10891
|
+
if (compatTail === "unarchive") {
|
|
10892
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10893
|
+
return;
|
|
10894
|
+
}
|
|
10895
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
10896
|
+
sink.retire();
|
|
10897
|
+
}
|
|
10898
|
+
ctx.sessions.delete(session.id);
|
|
10899
|
+
ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
|
|
10900
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10901
|
+
return;
|
|
10902
|
+
}
|
|
10903
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
|
|
10904
|
+
}
|
|
10905
|
+
|
|
10567
10906
|
// src/frontdoor/rcWebFetch.ts
|
|
10568
10907
|
var dns2 = __toESM(require("node:dns"), 1);
|
|
10569
10908
|
var http2 = __toESM(require("node:http"), 1);
|
|
@@ -10832,21 +11171,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
|
|
|
10832
11171
|
var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
|
|
10833
11172
|
var RC_SELF_HOST_WORKER_EPOCH = 1;
|
|
10834
11173
|
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
10835
|
-
var RC_SOURCE_WORKER = "worker";
|
|
10836
|
-
var RC_SOURCE_CLIENT = "client";
|
|
10837
11174
|
var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
|
|
10838
11175
|
var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
10839
|
-
function
|
|
11176
|
+
function isRecord8(value) {
|
|
10840
11177
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10841
11178
|
}
|
|
10842
11179
|
function isWebSearchRefusal(value) {
|
|
10843
11180
|
return !Array.isArray(value);
|
|
10844
11181
|
}
|
|
10845
|
-
function
|
|
11182
|
+
function nonEmptyString3(record, field) {
|
|
10846
11183
|
const value = record === void 0 ? void 0 : record[field];
|
|
10847
11184
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10848
11185
|
}
|
|
10849
|
-
function
|
|
11186
|
+
function numberOf2(record, field) {
|
|
10850
11187
|
const value = record === void 0 ? void 0 : record[field];
|
|
10851
11188
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10852
11189
|
return value;
|
|
@@ -10913,14 +11250,14 @@ async function readBody2(request4, capBytes) {
|
|
|
10913
11250
|
});
|
|
10914
11251
|
});
|
|
10915
11252
|
}
|
|
10916
|
-
function
|
|
11253
|
+
function parseJsonObject3(body) {
|
|
10917
11254
|
let parsed;
|
|
10918
11255
|
try {
|
|
10919
11256
|
parsed = JSON.parse(body);
|
|
10920
11257
|
} catch {
|
|
10921
11258
|
return void 0;
|
|
10922
11259
|
}
|
|
10923
|
-
return
|
|
11260
|
+
return isRecord8(parsed) ? parsed : void 0;
|
|
10924
11261
|
}
|
|
10925
11262
|
function mintWorkerJwt(randomToken, issuedAt) {
|
|
10926
11263
|
const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
@@ -10943,11 +11280,16 @@ function createRcSelfHostSurface(deps) {
|
|
|
10943
11280
|
for (const session of sessions.values()) {
|
|
10944
11281
|
while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10945
11282
|
const dropped = session.events.shift();
|
|
10946
|
-
const uuid = dropped === void 0 ? void 0 :
|
|
11283
|
+
const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
|
|
10947
11284
|
if (uuid !== void 0) {
|
|
10948
11285
|
session.seenPayloadUuids.delete(uuid);
|
|
10949
11286
|
}
|
|
10950
11287
|
}
|
|
11288
|
+
for (const [clientId, lastSeenAt] of session.clients) {
|
|
11289
|
+
if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11290
|
+
session.clients.delete(clientId);
|
|
11291
|
+
}
|
|
11292
|
+
}
|
|
10951
11293
|
if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10952
11294
|
sessions.delete(session.id);
|
|
10953
11295
|
}
|
|
@@ -10986,7 +11328,7 @@ function createRcSelfHostSurface(deps) {
|
|
|
10986
11328
|
const frameOf = (event) => {
|
|
10987
11329
|
const envelope = {
|
|
10988
11330
|
event_id: event.eventId,
|
|
10989
|
-
event_type:
|
|
11331
|
+
event_type: nonEmptyString3(event.payload, "type") ?? "event",
|
|
10990
11332
|
sequence_num: event.sequenceNum,
|
|
10991
11333
|
source: event.source,
|
|
10992
11334
|
payload: event.payload,
|
|
@@ -11000,7 +11342,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11000
11342
|
};
|
|
11001
11343
|
const holdStream = async (session, which, wantsReplay, request4, response) => {
|
|
11002
11344
|
const url = new URL(request4.url, "http://127.0.0.1");
|
|
11003
|
-
const cursor =
|
|
11345
|
+
const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
|
|
11004
11346
|
const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
|
|
11005
11347
|
response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
11006
11348
|
response.flush();
|
|
@@ -11029,8 +11371,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11029
11371
|
sink.retire();
|
|
11030
11372
|
response.end();
|
|
11031
11373
|
};
|
|
11032
|
-
const publish = (session, source, payload) => {
|
|
11033
|
-
const uuid =
|
|
11374
|
+
const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
|
|
11375
|
+
const uuid = nonEmptyString3(payload, "uuid");
|
|
11034
11376
|
const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
|
|
11035
11377
|
session.sequenceNum += 1;
|
|
11036
11378
|
const event = {
|
|
@@ -11038,9 +11380,9 @@ data: ${JSON.stringify(envelope)}
|
|
|
11038
11380
|
eventId: uuid ?? deps.newUuid(),
|
|
11039
11381
|
source,
|
|
11040
11382
|
// 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.
|
|
11041
|
-
payload: source ===
|
|
11383
|
+
payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
|
|
11042
11384
|
createdAt: new Date(deps.now()).toISOString(),
|
|
11043
|
-
toWorker
|
|
11385
|
+
toWorker,
|
|
11044
11386
|
storedAt: deps.now()
|
|
11045
11387
|
};
|
|
11046
11388
|
session.events.push(event);
|
|
@@ -11088,8 +11430,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
11088
11430
|
}
|
|
11089
11431
|
session.lastTrafficAt = deps.now();
|
|
11090
11432
|
if (tail === "") {
|
|
11433
|
+
if (method === "PATCH" || method === "PUT") {
|
|
11434
|
+
const refusal = credentialRefusal(request4.headers);
|
|
11435
|
+
if (refusal !== void 0) {
|
|
11436
|
+
await unauthorized(response, refusal);
|
|
11437
|
+
return;
|
|
11438
|
+
}
|
|
11439
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11440
|
+
const title = nonEmptyString3(body, "title");
|
|
11441
|
+
if (title === void 0) {
|
|
11442
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
|
|
11443
|
+
return;
|
|
11444
|
+
}
|
|
11445
|
+
session.title = title;
|
|
11446
|
+
session.lastTrafficAt = deps.now();
|
|
11447
|
+
deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
|
|
11448
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11449
|
+
return;
|
|
11450
|
+
}
|
|
11091
11451
|
if (method !== "GET") {
|
|
11092
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
|
|
11452
|
+
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" } });
|
|
11093
11453
|
return;
|
|
11094
11454
|
}
|
|
11095
11455
|
await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
|
|
@@ -11134,17 +11494,17 @@ data: ${JSON.stringify(envelope)}
|
|
|
11134
11494
|
return;
|
|
11135
11495
|
}
|
|
11136
11496
|
if (workerTail === "" && method === "PUT") {
|
|
11137
|
-
const body =
|
|
11138
|
-
const claimedEpoch =
|
|
11497
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11498
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11139
11499
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11140
11500
|
await epochStale(response);
|
|
11141
11501
|
return;
|
|
11142
11502
|
}
|
|
11143
|
-
const status =
|
|
11503
|
+
const status = nonEmptyString3(body, "worker_status");
|
|
11144
11504
|
if (status !== void 0) {
|
|
11145
11505
|
session.workerStatus = status;
|
|
11146
11506
|
}
|
|
11147
|
-
if (
|
|
11507
|
+
if (isRecord8(body?.external_metadata)) {
|
|
11148
11508
|
session.externalMetadata = body.external_metadata;
|
|
11149
11509
|
}
|
|
11150
11510
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
@@ -11161,8 +11521,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11161
11521
|
return;
|
|
11162
11522
|
}
|
|
11163
11523
|
if (workerTail === "events" && method === "POST") {
|
|
11164
|
-
const body =
|
|
11165
|
-
const claimedEpoch =
|
|
11524
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11525
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11166
11526
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11167
11527
|
await epochStale(response);
|
|
11168
11528
|
return;
|
|
@@ -11174,7 +11534,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11174
11534
|
}
|
|
11175
11535
|
const results = [];
|
|
11176
11536
|
for (const event of events) {
|
|
11177
|
-
if (!
|
|
11537
|
+
if (!isRecord8(event) || !isRecord8(event.payload)) {
|
|
11178
11538
|
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" } });
|
|
11179
11539
|
return;
|
|
11180
11540
|
}
|
|
@@ -11195,8 +11555,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11195
11555
|
return;
|
|
11196
11556
|
}
|
|
11197
11557
|
if (workerTail === "internal-events" && method === "POST") {
|
|
11198
|
-
const body =
|
|
11199
|
-
const claimedEpoch =
|
|
11558
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11559
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
11200
11560
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
11201
11561
|
await epochStale(response);
|
|
11202
11562
|
return;
|
|
@@ -11207,19 +11567,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
11207
11567
|
return;
|
|
11208
11568
|
}
|
|
11209
11569
|
for (const event of events) {
|
|
11210
|
-
if (
|
|
11211
|
-
session.internalEvents.push({
|
|
11570
|
+
if (isRecord8(event)) {
|
|
11571
|
+
session.internalEvents.push({ eventId: deps.newUuid(), body: event });
|
|
11212
11572
|
}
|
|
11213
11573
|
}
|
|
11214
11574
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11215
11575
|
return;
|
|
11216
11576
|
}
|
|
11217
11577
|
if (workerTail === "internal-events" && method === "GET") {
|
|
11218
|
-
const
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11578
|
+
const query = new URL(request4.url, "http://127.0.0.1").searchParams;
|
|
11579
|
+
if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
|
|
11580
|
+
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" } });
|
|
11581
|
+
return;
|
|
11582
|
+
}
|
|
11583
|
+
const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
|
|
11584
|
+
if ("error" in page) {
|
|
11585
|
+
const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
|
|
11586
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
|
|
11587
|
+
return;
|
|
11588
|
+
}
|
|
11589
|
+
await answerJson2(response, HTTP_STATUS.ok, page);
|
|
11223
11590
|
return;
|
|
11224
11591
|
}
|
|
11225
11592
|
if (workerTail === "diagnostics" && method === "POST") {
|
|
@@ -11228,8 +11595,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11228
11595
|
return;
|
|
11229
11596
|
}
|
|
11230
11597
|
if (workerTail === "web-fetch" && method === "POST") {
|
|
11231
|
-
const body =
|
|
11232
|
-
const target =
|
|
11598
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11599
|
+
const target = nonEmptyString3(body, "url");
|
|
11233
11600
|
if (target === void 0) {
|
|
11234
11601
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
|
|
11235
11602
|
return;
|
|
@@ -11251,8 +11618,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11251
11618
|
return;
|
|
11252
11619
|
}
|
|
11253
11620
|
if (workerTail === "web-search" && method === "POST") {
|
|
11254
|
-
const body =
|
|
11255
|
-
const query =
|
|
11621
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11622
|
+
const query = nonEmptyString3(body, "query");
|
|
11256
11623
|
if (query === void 0) {
|
|
11257
11624
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
|
|
11258
11625
|
return;
|
|
@@ -11263,7 +11630,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11263
11630
|
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" } });
|
|
11264
11631
|
return;
|
|
11265
11632
|
}
|
|
11266
|
-
const searchProfile =
|
|
11633
|
+
const searchProfile = nonEmptyString3(body, "search_profile");
|
|
11267
11634
|
const backend = deps.webSearch;
|
|
11268
11635
|
if (backend === void 0) {
|
|
11269
11636
|
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
|
|
@@ -11288,7 +11655,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11288
11655
|
return;
|
|
11289
11656
|
}
|
|
11290
11657
|
if (tail === "events" && method === "POST") {
|
|
11291
|
-
const body =
|
|
11658
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11292
11659
|
const events = body?.events;
|
|
11293
11660
|
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
11294
11661
|
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` } });
|
|
@@ -11296,7 +11663,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11296
11663
|
}
|
|
11297
11664
|
const results = [];
|
|
11298
11665
|
for (const event of events) {
|
|
11299
|
-
if (!
|
|
11666
|
+
if (!isRecord8(event) || !isRecord8(event.payload)) {
|
|
11300
11667
|
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" } });
|
|
11301
11668
|
return;
|
|
11302
11669
|
}
|
|
@@ -11308,26 +11675,14 @@ data: ${JSON.stringify(envelope)}
|
|
|
11308
11675
|
return;
|
|
11309
11676
|
}
|
|
11310
11677
|
if (tail === "events" && method === "GET") {
|
|
11311
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type:
|
|
11678
|
+
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 })) });
|
|
11312
11679
|
return;
|
|
11313
11680
|
}
|
|
11314
11681
|
if (tail === "events/stream" && method === "GET") {
|
|
11315
11682
|
await holdStream(session, "clientStreams", () => true, request4, response);
|
|
11316
11683
|
return;
|
|
11317
11684
|
}
|
|
11318
|
-
if (
|
|
11319
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11320
|
-
await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
|
|
11321
|
-
return;
|
|
11322
|
-
}
|
|
11323
|
-
if (tail === "mark_read" && method === "POST") {
|
|
11324
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11325
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11326
|
-
return;
|
|
11327
|
-
}
|
|
11328
|
-
if (tail === "teleport-events" && method === "POST") {
|
|
11329
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11330
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11685
|
+
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)) {
|
|
11331
11686
|
return;
|
|
11332
11687
|
}
|
|
11333
11688
|
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
|
|
@@ -11344,17 +11699,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11344
11699
|
await noCredential(response);
|
|
11345
11700
|
return;
|
|
11346
11701
|
}
|
|
11347
|
-
if (pathname === RC_COMPAT_SESSIONS_PATH) {
|
|
11348
|
-
|
|
11349
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
11350
|
-
return;
|
|
11351
|
-
}
|
|
11352
|
-
const listRefusal = credentialRefusal(request4.headers);
|
|
11353
|
-
if (listRefusal !== void 0) {
|
|
11354
|
-
await unauthorized(response, listRefusal);
|
|
11355
|
-
return;
|
|
11356
|
-
}
|
|
11357
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: [] });
|
|
11702
|
+
if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
|
|
11703
|
+
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);
|
|
11358
11704
|
return;
|
|
11359
11705
|
}
|
|
11360
11706
|
if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
|
|
@@ -11368,11 +11714,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11368
11714
|
await unauthorized(response, createRefusal);
|
|
11369
11715
|
return;
|
|
11370
11716
|
}
|
|
11371
|
-
const body =
|
|
11717
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11372
11718
|
const session = {
|
|
11373
11719
|
id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
|
|
11374
11720
|
createdAt: deps.now(),
|
|
11375
|
-
title:
|
|
11721
|
+
title: nonEmptyString3(body, "title"),
|
|
11376
11722
|
workerJwts: /* @__PURE__ */ new Set(),
|
|
11377
11723
|
sequenceNum: 0,
|
|
11378
11724
|
events: [],
|
|
@@ -11380,6 +11726,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11380
11726
|
workerStreams: /* @__PURE__ */ new Set(),
|
|
11381
11727
|
clientStreams: /* @__PURE__ */ new Set(),
|
|
11382
11728
|
internalEvents: [],
|
|
11729
|
+
clients: /* @__PURE__ */ new Map(),
|
|
11383
11730
|
workerStatus: void 0,
|
|
11384
11731
|
externalMetadata: void 0,
|
|
11385
11732
|
lastTrafficAt: deps.now()
|
|
@@ -11424,7 +11771,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11424
11771
|
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
11425
11772
|
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
11426
11773
|
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
11427
|
-
const body =
|
|
11774
|
+
const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
|
|
11428
11775
|
const record = deps.credentialRecord();
|
|
11429
11776
|
if (record === void 0) {
|
|
11430
11777
|
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" });
|
|
@@ -11510,7 +11857,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
|
|
|
11510
11857
|
var RC_SELF_HOST_RECORD_FILE = "credential.json";
|
|
11511
11858
|
var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
|
|
11512
11859
|
var SELF_HOST_SUBSCRIPTION_TYPE = "max";
|
|
11513
|
-
function
|
|
11860
|
+
function isRecord9(value) {
|
|
11514
11861
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11515
11862
|
}
|
|
11516
11863
|
function readJsonObject(fs12, file) {
|
|
@@ -11524,7 +11871,7 @@ function readJsonObject(fs12, file) {
|
|
|
11524
11871
|
} catch {
|
|
11525
11872
|
return void 0;
|
|
11526
11873
|
}
|
|
11527
|
-
return
|
|
11874
|
+
return isRecord9(parsed) ? parsed : void 0;
|
|
11528
11875
|
}
|
|
11529
11876
|
function readRcSelfHostRecord(fs12, frontdoorDir) {
|
|
11530
11877
|
const parsed = readJsonObject(fs12, import_node_path28.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
|
|
@@ -11548,7 +11895,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11548
11895
|
const recordFile = import_node_path28.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
|
|
11549
11896
|
const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
|
|
11550
11897
|
const existingCredentials = readJsonObject(deps.fs, credentialsFile);
|
|
11551
|
-
const existingOauth =
|
|
11898
|
+
const existingOauth = isRecord9(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
|
|
11552
11899
|
const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
|
|
11553
11900
|
const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
|
|
11554
11901
|
if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
|
|
@@ -11565,7 +11912,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11565
11912
|
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)}
|
|
11566
11913
|
`);
|
|
11567
11914
|
const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
|
|
11568
|
-
const existingBlock = (value) =>
|
|
11915
|
+
const existingBlock = (value) => isRecord9(value) ? value : {};
|
|
11569
11916
|
deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
|
|
11570
11917
|
...claudeJson,
|
|
11571
11918
|
oauthAccount: {
|
|
@@ -11764,7 +12111,7 @@ var import_node_path30 = __toESM(require("node:path"), 1);
|
|
|
11764
12111
|
// package.json
|
|
11765
12112
|
var package_default = {
|
|
11766
12113
|
name: "agent-shim",
|
|
11767
|
-
version: "8.
|
|
12114
|
+
version: "8.26.0",
|
|
11768
12115
|
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.",
|
|
11769
12116
|
license: "MIT",
|
|
11770
12117
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -13028,11 +13375,15 @@ function prepareClaudeLaunch(options) {
|
|
|
13028
13375
|
RC_WEB_FETCH_MAX_BYTES,
|
|
13029
13376
|
RC_WEB_FETCH_MAX_REDIRECTS,
|
|
13030
13377
|
ROUTED_PATH_PREFIX,
|
|
13378
|
+
RcLiveRateLimitSchema,
|
|
13031
13379
|
RcPermissionModeSchema,
|
|
13380
|
+
RcRateLimitInfoSchema,
|
|
13381
|
+
RcRateLimitWindowSchema,
|
|
13032
13382
|
RcStreamEnvelopeSchema,
|
|
13033
13383
|
RcStreamEventSchema,
|
|
13034
13384
|
UsageError,
|
|
13035
13385
|
UsageListOutputSchema,
|
|
13386
|
+
UsageLiveOutputSchema,
|
|
13036
13387
|
UsageSnapshotError,
|
|
13037
13388
|
UsageSnapshotSchema,
|
|
13038
13389
|
UsageWindowsInputSchema,
|
|
@@ -13055,6 +13406,7 @@ function prepareClaudeLaunch(options) {
|
|
|
13055
13406
|
buildRcGetUsagePayload,
|
|
13056
13407
|
buildRcInterruptPayload,
|
|
13057
13408
|
buildRcKeepAlivePayload,
|
|
13409
|
+
buildRcMarkReadBody,
|
|
13058
13410
|
buildRcMcpAuthenticatePayload,
|
|
13059
13411
|
buildRcMcpOAuthCallbackUrlPayload,
|
|
13060
13412
|
buildRcMcpReconnectPayload,
|
|
@@ -13083,6 +13435,7 @@ function prepareClaudeLaunch(options) {
|
|
|
13083
13435
|
createRcControlHandler,
|
|
13084
13436
|
createRcCredentialStore,
|
|
13085
13437
|
createRcEventFanout,
|
|
13438
|
+
createRcLiveUsage,
|
|
13086
13439
|
createRcSelfHostSurface,
|
|
13087
13440
|
createRcSessionTracker,
|
|
13088
13441
|
createRcStreamHub,
|
|
@@ -13144,6 +13497,7 @@ function prepareClaudeLaunch(options) {
|
|
|
13144
13497
|
rcEventWriteResultFromAnswer,
|
|
13145
13498
|
rcFanoutOnDoorHub,
|
|
13146
13499
|
rcObserverAsPassthrough,
|
|
13500
|
+
rcRateLimitInfoOf,
|
|
13147
13501
|
rcSelfHostFromEnv,
|
|
13148
13502
|
rcWebFetchAllowsPrivateFromEnv,
|
|
13149
13503
|
readActiveIdentity,
|
|
@@ -13201,6 +13555,7 @@ function prepareClaudeLaunch(options) {
|
|
|
13201
13555
|
startConnectServer,
|
|
13202
13556
|
submitRcSessionMcpOAuthCallbackUrl,
|
|
13203
13557
|
suggestRcSessionFiles,
|
|
13558
|
+
teleportRcSession,
|
|
13204
13559
|
topLevelNames,
|
|
13205
13560
|
updateDirectoryRule,
|
|
13206
13561
|
updateProvider,
|