agent-shim 8.23.0 → 8.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli.cjs +2494 -1867
- package/dist/index.cjs +792 -207
- package/dist/index.mjs +765 -207
- package/dist/types/index.d.ts +410 -15
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -93,10 +93,12 @@ __export(index_exports, {
|
|
|
93
93
|
ProviderKindMismatchError: () => ProviderKindMismatchError,
|
|
94
94
|
ProviderNotFoundError: () => ProviderNotFoundError,
|
|
95
95
|
ProviderSchema: () => ProviderSchema,
|
|
96
|
+
RC_CONTEXT_USAGE_DETAILS: () => RC_CONTEXT_USAGE_DETAILS,
|
|
96
97
|
RC_IDLE_EXPIRY_MS: () => RC_IDLE_EXPIRY_MS,
|
|
97
98
|
RC_ORPC_PATH_PREFIX: () => RC_ORPC_PATH_PREFIX,
|
|
98
99
|
RC_PENDING_DEADLINE_MS: () => RC_PENDING_DEADLINE_MS,
|
|
99
100
|
RC_PERMISSION_MODES: () => RC_PERMISSION_MODES,
|
|
101
|
+
RC_READ_FILE_ENCODINGS: () => RC_READ_FILE_ENCODINGS,
|
|
100
102
|
RC_REQUEST_PARSE_CAP_BYTES: () => RC_REQUEST_PARSE_CAP_BYTES,
|
|
101
103
|
RC_SELF_HOST_ENV: () => RC_SELF_HOST_ENV,
|
|
102
104
|
RC_SELF_HOST_KEEPALIVE_MS: () => RC_SELF_HOST_KEEPALIVE_MS,
|
|
@@ -127,11 +129,24 @@ __export(index_exports, {
|
|
|
127
129
|
addPool: () => addPool,
|
|
128
130
|
addProvider: () => addProvider,
|
|
129
131
|
answerRcControlRequest: () => answerRcControlRequest,
|
|
132
|
+
authenticateRcSessionMcpServer: () => authenticateRcSessionMcpServer,
|
|
130
133
|
buildEntryFacts: () => buildEntryFacts,
|
|
131
134
|
buildLayoutPaths: () => buildLayoutPaths,
|
|
135
|
+
buildRcControlRequestPayload: () => buildRcControlRequestPayload,
|
|
132
136
|
buildRcControlResponsePayload: () => buildRcControlResponsePayload,
|
|
137
|
+
buildRcEndSessionPayload: () => buildRcEndSessionPayload,
|
|
133
138
|
buildRcEventWriteBody: () => buildRcEventWriteBody,
|
|
139
|
+
buildRcFileSuggestionsPayload: () => buildRcFileSuggestionsPayload,
|
|
140
|
+
buildRcGetContextUsagePayload: () => buildRcGetContextUsagePayload,
|
|
141
|
+
buildRcGetUsagePayload: () => buildRcGetUsagePayload,
|
|
134
142
|
buildRcInterruptPayload: () => buildRcInterruptPayload,
|
|
143
|
+
buildRcKeepAlivePayload: () => buildRcKeepAlivePayload,
|
|
144
|
+
buildRcMarkReadBody: () => buildRcMarkReadBody,
|
|
145
|
+
buildRcMcpAuthenticatePayload: () => buildRcMcpAuthenticatePayload,
|
|
146
|
+
buildRcMcpOAuthCallbackUrlPayload: () => buildRcMcpOAuthCallbackUrlPayload,
|
|
147
|
+
buildRcMcpReconnectPayload: () => buildRcMcpReconnectPayload,
|
|
148
|
+
buildRcMcpStatusPayload: () => buildRcMcpStatusPayload,
|
|
149
|
+
buildRcReadFilePayload: () => buildRcReadFilePayload,
|
|
135
150
|
buildRcSetModelPayload: () => buildRcSetModelPayload,
|
|
136
151
|
buildRcSetPermissionModePayload: () => buildRcSetPermissionModePayload,
|
|
137
152
|
buildRcUserMessagePayload: () => buildRcUserMessagePayload,
|
|
@@ -164,6 +179,7 @@ __export(index_exports, {
|
|
|
164
179
|
doorApiAuth: () => doorApiAuth,
|
|
165
180
|
doorApiNodeHandlerOf: () => doorApiNodeHandlerOf,
|
|
166
181
|
effectiveWindow: () => effectiveWindow,
|
|
182
|
+
endRcSession: () => endRcSession,
|
|
167
183
|
ensureCa: () => ensureCa,
|
|
168
184
|
ensureFrontDoor: () => ensureFrontDoor,
|
|
169
185
|
ensureHeadroom: () => ensureHeadroom,
|
|
@@ -180,6 +196,9 @@ __export(index_exports, {
|
|
|
180
196
|
frontDoorRcApiClient: () => frontDoorRcApiClient,
|
|
181
197
|
frontDoorRcControl: () => frontDoorRcControl,
|
|
182
198
|
generateCa: () => generateCa,
|
|
199
|
+
getRcSessionContextUsage: () => getRcSessionContextUsage,
|
|
200
|
+
getRcSessionMcpStatus: () => getRcSessionMcpStatus,
|
|
201
|
+
getRcSessionUsage: () => getRcSessionUsage,
|
|
183
202
|
headroomSocketTarget: () => headroomSocketTarget,
|
|
184
203
|
identityExists: () => identityExists,
|
|
185
204
|
injectRcUserMessage: () => injectRcUserMessage,
|
|
@@ -187,7 +206,9 @@ __export(index_exports, {
|
|
|
187
206
|
isIdentityDirectoryName: () => isIdentityDirectoryName,
|
|
188
207
|
isInterceptedHost: () => isInterceptedHost,
|
|
189
208
|
isPrivateAddress: () => isPrivateAddress,
|
|
209
|
+
isRcContextUsageDetail: () => isRcContextUsageDetail,
|
|
190
210
|
isRcPermissionMode: () => isRcPermissionMode,
|
|
211
|
+
isRcReadFileEncoding: () => isRcReadFileEncoding,
|
|
191
212
|
lateRcEventDial: () => lateRcEventDial,
|
|
192
213
|
lateRcStreamDial: () => lateRcStreamDial,
|
|
193
214
|
listDirectoryRules: () => listDirectoryRules,
|
|
@@ -221,6 +242,7 @@ __export(index_exports, {
|
|
|
221
242
|
readProfile: () => readProfile,
|
|
222
243
|
readProvider: () => readProvider,
|
|
223
244
|
readRcSelfHostRecord: () => readRcSelfHostRecord,
|
|
245
|
+
readRcSessionFile: () => readRcSessionFile,
|
|
224
246
|
readUsageSnapshot: () => readUsageSnapshot,
|
|
225
247
|
realConnectCertStore: () => realConnectCertStore,
|
|
226
248
|
realConnectEffects: () => realConnectEffects,
|
|
@@ -228,6 +250,7 @@ __export(index_exports, {
|
|
|
228
250
|
realRcEventDial: () => realRcEventDial,
|
|
229
251
|
realRcStreamDial: () => realRcStreamDial,
|
|
230
252
|
realRcWebFetch: () => realRcWebFetch,
|
|
253
|
+
reconnectRcSessionMcpServer: () => reconnectRcSessionMcpServer,
|
|
231
254
|
recoverFarm: () => recoverFarm,
|
|
232
255
|
recoveryDiagnostics: () => recoveryDiagnostics,
|
|
233
256
|
removeDirectoryRule: () => removeDirectoryRule,
|
|
@@ -247,6 +270,7 @@ __export(index_exports, {
|
|
|
247
270
|
runFrontDoorSupervisor: () => runFrontDoorSupervisor,
|
|
248
271
|
runLauncher: () => runLauncher,
|
|
249
272
|
runSupervisor: () => runSupervisor,
|
|
273
|
+
sendRcKeepAlive: () => sendRcKeepAlive,
|
|
250
274
|
servedByPipeline: () => servedByPipeline,
|
|
251
275
|
setAllowAmbientCredential: () => setAllowAmbientCredential,
|
|
252
276
|
setDefaultConfigProfile: () => setDefaultConfigProfile,
|
|
@@ -262,6 +286,9 @@ __export(index_exports, {
|
|
|
262
286
|
snapshotPath: () => snapshotPath,
|
|
263
287
|
spawnClaude: () => spawnClaude,
|
|
264
288
|
startConnectServer: () => startConnectServer,
|
|
289
|
+
submitRcSessionMcpOAuthCallbackUrl: () => submitRcSessionMcpOAuthCallbackUrl,
|
|
290
|
+
suggestRcSessionFiles: () => suggestRcSessionFiles,
|
|
291
|
+
teleportRcSession: () => teleportRcSession,
|
|
265
292
|
topLevelNames: () => topLevelNames,
|
|
266
293
|
updateDirectoryRule: () => updateDirectoryRule,
|
|
267
294
|
updateProvider: () => updateProvider,
|
|
@@ -3479,9 +3506,6 @@ var HTTP_STATUS = {
|
|
|
3479
3506
|
badGateway: 502
|
|
3480
3507
|
};
|
|
3481
3508
|
|
|
3482
|
-
// src/frontdoor/rcControl.ts
|
|
3483
|
-
var https = __toESM(require("node:https"), 1);
|
|
3484
|
-
|
|
3485
3509
|
// src/frontdoor/rcSessions.ts
|
|
3486
3510
|
var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
|
|
3487
3511
|
var RC_SESSION_ID_PREFIX = "cse_";
|
|
@@ -3902,6 +3926,8 @@ function rcObserverAsPassthrough(observer) {
|
|
|
3902
3926
|
}
|
|
3903
3927
|
};
|
|
3904
3928
|
}
|
|
3929
|
+
|
|
3930
|
+
// src/frontdoor/rcWrites.ts
|
|
3905
3931
|
function buildRcUserMessagePayload(uuid, sessionId, text) {
|
|
3906
3932
|
return {
|
|
3907
3933
|
type: "user",
|
|
@@ -4002,14 +4028,55 @@ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan"
|
|
|
4002
4028
|
function isRcPermissionMode(value) {
|
|
4003
4029
|
return RC_PERMISSION_MODES.some((mode) => mode === value);
|
|
4004
4030
|
}
|
|
4031
|
+
function buildRcControlRequestPayload(requestId, request4) {
|
|
4032
|
+
return { type: "control_request", request_id: requestId, request: request4 };
|
|
4033
|
+
}
|
|
4005
4034
|
function buildRcInterruptPayload(requestId) {
|
|
4006
|
-
return
|
|
4035
|
+
return buildRcControlRequestPayload(requestId, { subtype: "interrupt" });
|
|
4007
4036
|
}
|
|
4008
4037
|
function buildRcSetModelPayload(requestId, model) {
|
|
4009
|
-
return
|
|
4038
|
+
return buildRcControlRequestPayload(requestId, { subtype: "set_model", model });
|
|
4010
4039
|
}
|
|
4011
4040
|
function buildRcSetPermissionModePayload(requestId, mode) {
|
|
4012
|
-
return
|
|
4041
|
+
return buildRcControlRequestPayload(requestId, { subtype: "set_permission_mode", mode });
|
|
4042
|
+
}
|
|
4043
|
+
function buildRcEndSessionPayload(requestId, reason) {
|
|
4044
|
+
return buildRcControlRequestPayload(requestId, { subtype: "end_session", ...reason === void 0 ? {} : { reason } });
|
|
4045
|
+
}
|
|
4046
|
+
function buildRcGetUsagePayload(requestId, skipBehaviors) {
|
|
4047
|
+
return buildRcControlRequestPayload(requestId, { subtype: "get_usage", ...skipBehaviors === true ? { skip_behaviors: true } : {} });
|
|
4048
|
+
}
|
|
4049
|
+
var RC_CONTEXT_USAGE_DETAILS = ["summary", "full"];
|
|
4050
|
+
function isRcContextUsageDetail(value) {
|
|
4051
|
+
return RC_CONTEXT_USAGE_DETAILS.some((detail) => detail === value);
|
|
4052
|
+
}
|
|
4053
|
+
function buildRcGetContextUsagePayload(requestId, detail) {
|
|
4054
|
+
return buildRcControlRequestPayload(requestId, { subtype: "get_context_usage", ...detail === void 0 ? {} : { detail } });
|
|
4055
|
+
}
|
|
4056
|
+
var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
|
|
4057
|
+
function isRcReadFileEncoding(value) {
|
|
4058
|
+
return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
|
|
4059
|
+
}
|
|
4060
|
+
function buildRcReadFilePayload(requestId, path32, options) {
|
|
4061
|
+
return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path32, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
|
|
4062
|
+
}
|
|
4063
|
+
function buildRcFileSuggestionsPayload(requestId, query) {
|
|
4064
|
+
return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
|
|
4065
|
+
}
|
|
4066
|
+
function buildRcMcpStatusPayload(requestId) {
|
|
4067
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_status" });
|
|
4068
|
+
}
|
|
4069
|
+
function buildRcMcpReconnectPayload(requestId, serverName) {
|
|
4070
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_reconnect", serverName });
|
|
4071
|
+
}
|
|
4072
|
+
function buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri) {
|
|
4073
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_authenticate", serverName, redirectUri });
|
|
4074
|
+
}
|
|
4075
|
+
function buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl) {
|
|
4076
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_oauth_callback_url", serverName, callbackUrl });
|
|
4077
|
+
}
|
|
4078
|
+
function buildRcKeepAlivePayload() {
|
|
4079
|
+
return { type: "keep_alive" };
|
|
4013
4080
|
}
|
|
4014
4081
|
async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
4015
4082
|
const credential = deps.credentialOf(sessionId);
|
|
@@ -4036,7 +4103,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
|
4036
4103
|
}
|
|
4037
4104
|
return result;
|
|
4038
4105
|
}
|
|
4039
|
-
async function
|
|
4106
|
+
async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
|
|
4040
4107
|
const credential = deps.credentialOf(sessionId);
|
|
4041
4108
|
if (credential === void 0) {
|
|
4042
4109
|
return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
|
|
@@ -4044,12 +4111,12 @@ async function interruptRcSession(deps, sessionId) {
|
|
|
4044
4111
|
if (credential.authorization === void 0) {
|
|
4045
4112
|
return noOAuthCredentialResult(sessionId);
|
|
4046
4113
|
}
|
|
4047
|
-
const body = JSON.stringify(buildRcEventWriteBody(
|
|
4114
|
+
const body = JSON.stringify(buildRcEventWriteBody(payload));
|
|
4048
4115
|
let answer;
|
|
4049
4116
|
try {
|
|
4050
|
-
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
4117
|
+
answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
|
|
4051
4118
|
} catch (error) {
|
|
4052
|
-
return { ok: false, message: `the door could not reach the API host to
|
|
4119
|
+
return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
|
|
4053
4120
|
}
|
|
4054
4121
|
const result = rcEventWriteResultFromAnswer(answer);
|
|
4055
4122
|
if (result.ok) {
|
|
@@ -4057,53 +4124,102 @@ async function interruptRcSession(deps, sessionId) {
|
|
|
4057
4124
|
}
|
|
4058
4125
|
return result;
|
|
4059
4126
|
}
|
|
4127
|
+
async function interruptRcSession(deps, sessionId) {
|
|
4128
|
+
const requestId = deps.newUuid();
|
|
4129
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcInterruptPayload(requestId), `interrupt session ${sessionId}`);
|
|
4130
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4131
|
+
}
|
|
4060
4132
|
async function setRcSessionModel(deps, sessionId, model) {
|
|
4061
4133
|
if (model === "") {
|
|
4062
4134
|
return { ok: false, message: "a set-model write needs a non-empty model id: the SDK's own field treats an omitted or null value as a reset to the session default, which this operation never sends, and an empty string is neither" };
|
|
4063
4135
|
}
|
|
4064
|
-
const
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4136
|
+
const requestId = deps.newUuid();
|
|
4137
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcSetModelPayload(requestId, model), `set the model on session ${sessionId}`);
|
|
4138
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4139
|
+
}
|
|
4140
|
+
async function setRcSessionPermissionMode(deps, sessionId, mode) {
|
|
4141
|
+
const requestId = deps.newUuid();
|
|
4142
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcSetPermissionModePayload(requestId, mode), `set the permission mode on session ${sessionId}`);
|
|
4143
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4144
|
+
}
|
|
4145
|
+
async function endRcSession(deps, sessionId, reason) {
|
|
4146
|
+
if (reason === "") {
|
|
4147
|
+
return { ok: false, message: "an end-session write needs either no reason or a non-empty one: the worker reads an absent reason as its own unspecified form, and an empty string is neither" };
|
|
4070
4148
|
}
|
|
4071
|
-
const
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4149
|
+
const requestId = deps.newUuid();
|
|
4150
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcEndSessionPayload(requestId, reason), `end session ${sessionId}`);
|
|
4151
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4152
|
+
}
|
|
4153
|
+
async function getRcSessionUsage(deps, sessionId, skipBehaviors) {
|
|
4154
|
+
const requestId = deps.newUuid();
|
|
4155
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcGetUsagePayload(requestId, skipBehaviors), `request the usage of session ${sessionId}`);
|
|
4156
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4157
|
+
}
|
|
4158
|
+
async function getRcSessionContextUsage(deps, sessionId, detail) {
|
|
4159
|
+
const requestId = deps.newUuid();
|
|
4160
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
|
|
4161
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4162
|
+
}
|
|
4163
|
+
async function readRcSessionFile(deps, sessionId, path32, options) {
|
|
4164
|
+
if (path32 === "") {
|
|
4165
|
+
return { ok: false, message: "a read-file write needs a non-empty path: the SDK's own field names the file the worker resolves against the session's cwd, and an empty string names none" };
|
|
4077
4166
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
deps.noteSequenceNums(sessionId, result.sequenceNums);
|
|
4167
|
+
if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
|
|
4168
|
+
return { ok: false, message: "a read-file write's max-bytes is a positive whole number of bytes: the SDK's own field caps the read, and a zero or negative cap reads nothing" };
|
|
4081
4169
|
}
|
|
4082
|
-
|
|
4170
|
+
const requestId = deps.newUuid();
|
|
4171
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path32, options), `read ${path32} from session ${sessionId}`);
|
|
4172
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4083
4173
|
}
|
|
4084
|
-
async function
|
|
4085
|
-
const
|
|
4086
|
-
|
|
4087
|
-
|
|
4174
|
+
async function suggestRcSessionFiles(deps, sessionId, query) {
|
|
4175
|
+
const requestId = deps.newUuid();
|
|
4176
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcFileSuggestionsPayload(requestId, query), `request file suggestions from session ${sessionId}`);
|
|
4177
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4178
|
+
}
|
|
4179
|
+
async function getRcSessionMcpStatus(deps, sessionId) {
|
|
4180
|
+
const requestId = deps.newUuid();
|
|
4181
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpStatusPayload(requestId), `request the MCP status of session ${sessionId}`);
|
|
4182
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4183
|
+
}
|
|
4184
|
+
async function reconnectRcSessionMcpServer(deps, sessionId, serverName) {
|
|
4185
|
+
if (serverName === "") {
|
|
4186
|
+
return { ok: false, message: "an mcp-reconnect write needs a non-empty server name: the SDK's own field names the server as mcp_status reports it, and an empty string names none" };
|
|
4088
4187
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4188
|
+
const requestId = deps.newUuid();
|
|
4189
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpReconnectPayload(requestId, serverName), `reconnect MCP server ${serverName} on session ${sessionId}`);
|
|
4190
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4191
|
+
}
|
|
4192
|
+
async function authenticateRcSessionMcpServer(deps, sessionId, serverName, redirectUri) {
|
|
4193
|
+
if (serverName === "" || redirectUri === "") {
|
|
4194
|
+
return { ok: false, message: "an mcp-authenticate write needs a non-empty server name and redirect URI: the CLI's own request names the server and where its OAuth flow redirects back to, and an empty string names neither" };
|
|
4091
4195
|
}
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4196
|
+
const requestId = deps.newUuid();
|
|
4197
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri), `authenticate MCP server ${serverName} on session ${sessionId}`);
|
|
4198
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4199
|
+
}
|
|
4200
|
+
async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, callbackUrl) {
|
|
4201
|
+
if (serverName === "" || callbackUrl === "") {
|
|
4202
|
+
return { ok: false, message: "an mcp-oauth-callback-url write needs a non-empty server name and callback URL: the CLI's own request names the server and the URL its OAuth flow landed on, and an empty string names neither" };
|
|
4098
4203
|
}
|
|
4099
|
-
const
|
|
4100
|
-
|
|
4101
|
-
|
|
4204
|
+
const requestId = deps.newUuid();
|
|
4205
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl), `hand MCP server ${serverName}'s OAuth callback to session ${sessionId}`);
|
|
4206
|
+
return result.ok ? { ...result, requestId } : result;
|
|
4207
|
+
}
|
|
4208
|
+
async function sendRcKeepAlive(deps, sessionId) {
|
|
4209
|
+
return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
|
|
4210
|
+
}
|
|
4211
|
+
async function teleportRcSession(deps, sessionId, marker) {
|
|
4212
|
+
if (marker === "") {
|
|
4213
|
+
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" };
|
|
4102
4214
|
}
|
|
4103
|
-
return
|
|
4215
|
+
return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
|
|
4216
|
+
}
|
|
4217
|
+
function buildRcMarkReadBody(eventId) {
|
|
4218
|
+
return eventId === void 0 ? {} : { event_id: eventId };
|
|
4104
4219
|
}
|
|
4105
4220
|
|
|
4106
4221
|
// src/frontdoor/rcControl.ts
|
|
4222
|
+
var https = __toESM(require("node:https"), 1);
|
|
4107
4223
|
var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
|
|
4108
4224
|
var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
4109
4225
|
var CONTROL_BODY_CAP_BYTES = 10485760;
|
|
@@ -4112,6 +4228,25 @@ function answerJson(response, status, body) {
|
|
|
4112
4228
|
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
4113
4229
|
response.end(text);
|
|
4114
4230
|
}
|
|
4231
|
+
function isJsonObject(value) {
|
|
4232
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4233
|
+
}
|
|
4234
|
+
function bodyString(body, field) {
|
|
4235
|
+
const value = body[field];
|
|
4236
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
4237
|
+
}
|
|
4238
|
+
function bodyStringAllowingEmpty(body, field) {
|
|
4239
|
+
const value = body[field];
|
|
4240
|
+
return typeof value === "string" ? value : void 0;
|
|
4241
|
+
}
|
|
4242
|
+
function bodyBoolean(body, field) {
|
|
4243
|
+
const value = body[field];
|
|
4244
|
+
return typeof value === "boolean" ? value : void 0;
|
|
4245
|
+
}
|
|
4246
|
+
function bodyPositiveInteger(body, field) {
|
|
4247
|
+
const value = body[field];
|
|
4248
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
4249
|
+
}
|
|
4115
4250
|
async function readBody(request4) {
|
|
4116
4251
|
return await new Promise((resolve, reject) => {
|
|
4117
4252
|
const chunks = [];
|
|
@@ -4139,9 +4274,136 @@ function createRcControlHandler(deps) {
|
|
|
4139
4274
|
const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
|
|
4140
4275
|
const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
|
|
4141
4276
|
const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
|
|
4142
|
-
const
|
|
4143
|
-
|
|
4144
|
-
|
|
4277
|
+
const controlWrites = [
|
|
4278
|
+
{
|
|
4279
|
+
name: "interrupt",
|
|
4280
|
+
path: "interrupt",
|
|
4281
|
+
read: (body) => {
|
|
4282
|
+
const session = bodyString(body, "session");
|
|
4283
|
+
return session === void 0 ? { error: "the interrupt body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.interrupt(session) };
|
|
4284
|
+
}
|
|
4285
|
+
},
|
|
4286
|
+
{
|
|
4287
|
+
name: "set-model",
|
|
4288
|
+
path: "set-model",
|
|
4289
|
+
read: (body) => {
|
|
4290
|
+
const session = bodyString(body, "session");
|
|
4291
|
+
const model = bodyString(body, "model");
|
|
4292
|
+
return session === void 0 || model === void 0 ? { error: "the set-model body must be JSON naming a non-empty session id and model id" } : { session, run: async () => await deps.setModel(session, model) };
|
|
4293
|
+
}
|
|
4294
|
+
},
|
|
4295
|
+
{
|
|
4296
|
+
name: "set-permission-mode",
|
|
4297
|
+
path: "set-permission-mode",
|
|
4298
|
+
read: (body) => {
|
|
4299
|
+
const session = bodyString(body, "session");
|
|
4300
|
+
const mode = body.mode;
|
|
4301
|
+
return session === void 0 || !isRcPermissionMode(mode) ? { error: `the set-permission-mode body must be JSON naming a non-empty session id and a mode the SDK's own type permits (${RC_PERMISSION_MODE_LIST})` } : { session, run: async () => await deps.setPermissionMode(session, mode) };
|
|
4302
|
+
}
|
|
4303
|
+
},
|
|
4304
|
+
{
|
|
4305
|
+
name: "end-session",
|
|
4306
|
+
path: "end-session",
|
|
4307
|
+
read: (body) => {
|
|
4308
|
+
const session = bodyString(body, "session");
|
|
4309
|
+
const reason = "reason" in body ? bodyString(body, "reason") : void 0;
|
|
4310
|
+
return session === void 0 || "reason" in body && reason === void 0 ? { error: "the end-session body must be JSON naming a non-empty session id, and a reason only as a non-empty string or absent" } : { session, run: async () => await deps.endSession(session, reason) };
|
|
4311
|
+
}
|
|
4312
|
+
},
|
|
4313
|
+
{
|
|
4314
|
+
name: "get-usage",
|
|
4315
|
+
path: "get-usage",
|
|
4316
|
+
read: (body) => {
|
|
4317
|
+
const session = bodyString(body, "session");
|
|
4318
|
+
const skipBehaviors = "skipBehaviors" in body ? bodyBoolean(body, "skipBehaviors") : void 0;
|
|
4319
|
+
return session === void 0 || "skipBehaviors" in body && skipBehaviors === void 0 ? { error: "the get-usage body must be JSON naming a non-empty session id, and skipBehaviors only as a boolean or absent" } : { session, run: async () => await deps.getUsage(session, skipBehaviors) };
|
|
4320
|
+
}
|
|
4321
|
+
},
|
|
4322
|
+
{
|
|
4323
|
+
name: "get-context-usage",
|
|
4324
|
+
path: "get-context-usage",
|
|
4325
|
+
read: (body) => {
|
|
4326
|
+
const session = bodyString(body, "session");
|
|
4327
|
+
const detail = "detail" in body && isRcContextUsageDetail(body.detail) ? body.detail : void 0;
|
|
4328
|
+
return session === void 0 || "detail" in body && detail === void 0 ? { error: `the get-context-usage body must be JSON naming a non-empty session id, and a detail level the SDK's own type permits (${RC_CONTEXT_USAGE_DETAILS.join(", ")}) or none` } : { session, run: async () => await deps.getContextUsage(session, detail) };
|
|
4329
|
+
}
|
|
4330
|
+
},
|
|
4331
|
+
{
|
|
4332
|
+
name: "read-file",
|
|
4333
|
+
path: "read-file",
|
|
4334
|
+
read: (body) => {
|
|
4335
|
+
const session = bodyString(body, "session");
|
|
4336
|
+
const path32 = bodyString(body, "path");
|
|
4337
|
+
const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
|
|
4338
|
+
const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
|
|
4339
|
+
const refused2 = session === void 0 || path32 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
|
|
4340
|
+
return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path32, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
|
|
4341
|
+
}
|
|
4342
|
+
},
|
|
4343
|
+
{
|
|
4344
|
+
name: "file-suggestions",
|
|
4345
|
+
path: "file-suggestions",
|
|
4346
|
+
read: (body) => {
|
|
4347
|
+
const session = bodyString(body, "session");
|
|
4348
|
+
const query = bodyStringAllowingEmpty(body, "query");
|
|
4349
|
+
return session === void 0 || query === void 0 ? { error: "the file-suggestions body must be JSON naming a non-empty session id and a query string, which may be empty" } : { session, run: async () => await deps.fileSuggestions(session, query) };
|
|
4350
|
+
}
|
|
4351
|
+
},
|
|
4352
|
+
{
|
|
4353
|
+
name: "keep-alive",
|
|
4354
|
+
path: "keep-alive",
|
|
4355
|
+
read: (body) => {
|
|
4356
|
+
const session = bodyString(body, "session");
|
|
4357
|
+
return session === void 0 ? { error: "the keep-alive body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.keepAlive(session) };
|
|
4358
|
+
}
|
|
4359
|
+
},
|
|
4360
|
+
{
|
|
4361
|
+
name: "mcp-status",
|
|
4362
|
+
path: "mcp-status",
|
|
4363
|
+
read: (body) => {
|
|
4364
|
+
const session = bodyString(body, "session");
|
|
4365
|
+
return session === void 0 ? { error: "the mcp-status body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.mcpStatus(session) };
|
|
4366
|
+
}
|
|
4367
|
+
},
|
|
4368
|
+
{
|
|
4369
|
+
name: "mcp-reconnect",
|
|
4370
|
+
path: "mcp-reconnect",
|
|
4371
|
+
read: (body) => {
|
|
4372
|
+
const session = bodyString(body, "session");
|
|
4373
|
+
const serverName = bodyString(body, "serverName");
|
|
4374
|
+
return session === void 0 || serverName === void 0 ? { error: "the mcp-reconnect body must be JSON naming a non-empty session id and server name" } : { session, run: async () => await deps.mcpReconnect(session, serverName) };
|
|
4375
|
+
}
|
|
4376
|
+
},
|
|
4377
|
+
{
|
|
4378
|
+
name: "mcp-authenticate",
|
|
4379
|
+
path: "mcp-authenticate",
|
|
4380
|
+
read: (body) => {
|
|
4381
|
+
const session = bodyString(body, "session");
|
|
4382
|
+
const serverName = bodyString(body, "serverName");
|
|
4383
|
+
const redirectUri = bodyString(body, "redirectUri");
|
|
4384
|
+
return session === void 0 || serverName === void 0 || redirectUri === void 0 ? { error: "the mcp-authenticate body must be JSON naming a non-empty session id, server name, and redirect URI" } : { session, run: async () => await deps.mcpAuthenticate(session, serverName, redirectUri) };
|
|
4385
|
+
}
|
|
4386
|
+
},
|
|
4387
|
+
{
|
|
4388
|
+
name: "mcp-oauth-callback-url",
|
|
4389
|
+
path: "mcp-oauth-callback-url",
|
|
4390
|
+
read: (body) => {
|
|
4391
|
+
const session = bodyString(body, "session");
|
|
4392
|
+
const serverName = bodyString(body, "serverName");
|
|
4393
|
+
const callbackUrl = bodyString(body, "callbackUrl");
|
|
4394
|
+
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) };
|
|
4395
|
+
}
|
|
4396
|
+
},
|
|
4397
|
+
{
|
|
4398
|
+
name: "teleport",
|
|
4399
|
+
path: "teleport",
|
|
4400
|
+
read: (body) => {
|
|
4401
|
+
const session = bodyString(body, "session");
|
|
4402
|
+
const marker = bodyString(body, "marker");
|
|
4403
|
+
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) };
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
];
|
|
4145
4407
|
return (request4, response) => {
|
|
4146
4408
|
const presented = request4.headers.authorization;
|
|
4147
4409
|
const bearerPrefix = "bearer ".length;
|
|
@@ -4226,73 +4488,33 @@ function createRcControlHandler(deps) {
|
|
|
4226
4488
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4227
4489
|
return;
|
|
4228
4490
|
}
|
|
4229
|
-
|
|
4230
|
-
if (
|
|
4231
|
-
|
|
4232
|
-
return;
|
|
4233
|
-
}
|
|
4234
|
-
let parsed;
|
|
4235
|
-
try {
|
|
4236
|
-
parsed = JSON.parse(await readBody(request4));
|
|
4237
|
-
} catch {
|
|
4238
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
|
|
4239
|
-
return;
|
|
4240
|
-
}
|
|
4241
|
-
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
|
|
4242
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
|
|
4243
|
-
return;
|
|
4244
|
-
}
|
|
4245
|
-
const result = await deps.interrupt(parsed.session);
|
|
4246
|
-
if (result.ok) {
|
|
4247
|
-
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
|
|
4248
|
-
return;
|
|
4491
|
+
for (const route of controlWrites) {
|
|
4492
|
+
if (url.pathname !== `${CONTROL_PATH_PREFIX}/${route.path}`) {
|
|
4493
|
+
continue;
|
|
4249
4494
|
}
|
|
4250
|
-
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4251
|
-
return;
|
|
4252
|
-
}
|
|
4253
|
-
if (url.pathname === setModelPath) {
|
|
4254
4495
|
if (request4.method !== "POST") {
|
|
4255
|
-
answerJson(response, HTTP_STATUS.methodNotAllowed, { error:
|
|
4496
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: `the ${route.name} operation is a POST` });
|
|
4256
4497
|
return;
|
|
4257
4498
|
}
|
|
4258
4499
|
let parsed;
|
|
4259
4500
|
try {
|
|
4260
4501
|
parsed = JSON.parse(await readBody(request4));
|
|
4261
4502
|
} catch {
|
|
4262
|
-
answerJson(response, HTTP_STATUS.badRequest, { error:
|
|
4263
|
-
return;
|
|
4264
|
-
}
|
|
4265
|
-
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
|
|
4266
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
|
|
4267
|
-
return;
|
|
4268
|
-
}
|
|
4269
|
-
const result = await deps.setModel(parsed.session, parsed.model);
|
|
4270
|
-
if (result.ok) {
|
|
4271
|
-
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
|
|
4272
|
-
return;
|
|
4273
|
-
}
|
|
4274
|
-
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4275
|
-
return;
|
|
4276
|
-
}
|
|
4277
|
-
if (url.pathname === setPermissionModePath) {
|
|
4278
|
-
if (request4.method !== "POST") {
|
|
4279
|
-
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
|
|
4503
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body is not readable JSON within the control body cap` });
|
|
4280
4504
|
return;
|
|
4281
4505
|
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
parsed = JSON.parse(await readBody(request4));
|
|
4285
|
-
} catch {
|
|
4286
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
|
|
4506
|
+
if (!isJsonObject(parsed)) {
|
|
4507
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body must be a JSON object` });
|
|
4287
4508
|
return;
|
|
4288
4509
|
}
|
|
4289
|
-
|
|
4290
|
-
|
|
4510
|
+
const read = route.read(parsed);
|
|
4511
|
+
if ("error" in read) {
|
|
4512
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: read.error });
|
|
4291
4513
|
return;
|
|
4292
4514
|
}
|
|
4293
|
-
const result = await
|
|
4515
|
+
const result = await read.run();
|
|
4294
4516
|
if (result.ok) {
|
|
4295
|
-
answerJson(response, HTTP_STATUS.ok, { session:
|
|
4517
|
+
answerJson(response, HTTP_STATUS.ok, { session: read.session, ...result.requestId === void 0 ? {} : { request: result.requestId }, sequenceNums: result.sequenceNums });
|
|
4296
4518
|
return;
|
|
4297
4519
|
}
|
|
4298
4520
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
@@ -4410,6 +4632,19 @@ function sequenceNumsOfBody(body) {
|
|
|
4410
4632
|
}
|
|
4411
4633
|
return sequenceNums;
|
|
4412
4634
|
}
|
|
4635
|
+
function requestIdOfBody(body) {
|
|
4636
|
+
let parsed;
|
|
4637
|
+
try {
|
|
4638
|
+
parsed = JSON.parse(body);
|
|
4639
|
+
} catch {
|
|
4640
|
+
return void 0;
|
|
4641
|
+
}
|
|
4642
|
+
if (typeof parsed !== "object" || parsed === null || !("request" in parsed)) {
|
|
4643
|
+
return void 0;
|
|
4644
|
+
}
|
|
4645
|
+
const value = parsed.request;
|
|
4646
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
4647
|
+
}
|
|
4413
4648
|
function frontDoorRcControl(transport, token) {
|
|
4414
4649
|
const headers = { authorization: `Bearer ${token}` };
|
|
4415
4650
|
const listAnswerOf = async (path32, what) => {
|
|
@@ -4498,7 +4733,8 @@ function frontDoorRcControl(transport, token) {
|
|
|
4498
4733
|
if (sequenceNums === void 0) {
|
|
4499
4734
|
return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
|
|
4500
4735
|
}
|
|
4501
|
-
|
|
4736
|
+
const requestId = requestIdOfBody(answer.body);
|
|
4737
|
+
return requestId === void 0 ? { ok: true, sequenceNums } : { ok: true, sequenceNums, requestId };
|
|
4502
4738
|
}
|
|
4503
4739
|
return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
|
|
4504
4740
|
};
|
|
@@ -4515,7 +4751,22 @@ function frontDoorRcControl(transport, token) {
|
|
|
4515
4751
|
),
|
|
4516
4752
|
interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4517
4753
|
setModel: async (sessionId, model) => await writeOf(`${CONTROL_PATH_PREFIX}/set-model`, JSON.stringify({ session: sessionId, model }), (status, message) => `setting the model on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4518
|
-
setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`)
|
|
4754
|
+
setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4755
|
+
endSession: async (sessionId, reason) => await writeOf(`${CONTROL_PATH_PREFIX}/end-session`, JSON.stringify({ session: sessionId, ...reason === void 0 ? {} : { reason } }), (status, message) => `ending ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4756
|
+
getUsage: async (sessionId, skipBehaviors) => await writeOf(`${CONTROL_PATH_PREFIX}/get-usage`, JSON.stringify({ session: sessionId, ...skipBehaviors === void 0 ? {} : { skipBehaviors } }), (status, message) => `requesting the usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4757
|
+
getContextUsage: async (sessionId, detail) => await writeOf(`${CONTROL_PATH_PREFIX}/get-context-usage`, JSON.stringify({ session: sessionId, ...detail === void 0 ? {} : { detail } }), (status, message) => `requesting the context usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4758
|
+
readFile: async (sessionId, path32, options) => await writeOf(
|
|
4759
|
+
`${CONTROL_PATH_PREFIX}/read-file`,
|
|
4760
|
+
JSON.stringify({ session: sessionId, path: path32, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
|
|
4761
|
+
(status, message) => `reading ${path32} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
|
|
4762
|
+
),
|
|
4763
|
+
fileSuggestions: async (sessionId, query) => await writeOf(`${CONTROL_PATH_PREFIX}/file-suggestions`, JSON.stringify({ session: sessionId, query }), (status, message) => `requesting file suggestions from ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4764
|
+
keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
|
|
4765
|
+
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}`),
|
|
4766
|
+
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}`),
|
|
4767
|
+
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}`),
|
|
4768
|
+
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}`),
|
|
4769
|
+
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}`)
|
|
4519
4770
|
};
|
|
4520
4771
|
}
|
|
4521
4772
|
|
|
@@ -4662,36 +4913,38 @@ function tlsFacts(options) {
|
|
|
4662
4913
|
}
|
|
4663
4914
|
function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
4664
4915
|
const agent = new ExemptTlsAgent();
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
const
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
chunks.push(chunk);
|
|
4687
|
-
});
|
|
4688
|
-
response.on("end", () => {
|
|
4689
|
-
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4690
|
-
});
|
|
4916
|
+
const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
|
|
4917
|
+
const options = {
|
|
4918
|
+
host: target.host,
|
|
4919
|
+
port: target.port,
|
|
4920
|
+
method: "POST",
|
|
4921
|
+
path: path32,
|
|
4922
|
+
// 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.
|
|
4923
|
+
headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
|
|
4924
|
+
agent
|
|
4925
|
+
};
|
|
4926
|
+
options.servername = target.servername ?? target.host;
|
|
4927
|
+
if (target.ca !== void 0) {
|
|
4928
|
+
options.ca = [...target.ca];
|
|
4929
|
+
}
|
|
4930
|
+
if (target.rejectUnauthorized !== void 0) {
|
|
4931
|
+
options.rejectUnauthorized = target.rejectUnauthorized;
|
|
4932
|
+
}
|
|
4933
|
+
const request4 = https2.request(options, (response) => {
|
|
4934
|
+
const chunks = [];
|
|
4935
|
+
response.on("data", (chunk) => {
|
|
4936
|
+
chunks.push(chunk);
|
|
4691
4937
|
});
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4938
|
+
response.on("end", () => {
|
|
4939
|
+
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
4940
|
+
});
|
|
4941
|
+
});
|
|
4942
|
+
request4.once("error", reject);
|
|
4943
|
+
request4.end(body);
|
|
4944
|
+
});
|
|
4945
|
+
return {
|
|
4946
|
+
writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
|
|
4947
|
+
writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
|
|
4695
4948
|
};
|
|
4696
4949
|
}
|
|
4697
4950
|
var NO_CHUNKS = {
|
|
@@ -4709,7 +4962,8 @@ function lateRcEventDial(resolve) {
|
|
|
4709
4962
|
return built.dial;
|
|
4710
4963
|
};
|
|
4711
4964
|
return {
|
|
4712
|
-
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
|
|
4965
|
+
writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
|
|
4966
|
+
writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
|
|
4713
4967
|
};
|
|
4714
4968
|
}
|
|
4715
4969
|
function lateRcStreamDial(resolve) {
|
|
@@ -4722,7 +4976,8 @@ function lateRcStreamDial(resolve) {
|
|
|
4722
4976
|
return built.dial;
|
|
4723
4977
|
};
|
|
4724
4978
|
return {
|
|
4725
|
-
announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
|
|
4979
|
+
announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
|
|
4980
|
+
markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
|
|
4726
4981
|
openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
|
|
4727
4982
|
};
|
|
4728
4983
|
}
|
|
@@ -4764,11 +5019,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4764
5019
|
return Buffer.concat(chunks).toString("utf8");
|
|
4765
5020
|
};
|
|
4766
5021
|
return {
|
|
4767
|
-
announcePresence: async (sessionId, headers, clientId) => {
|
|
4768
|
-
const body = JSON.stringify({ client_id: clientId, clear
|
|
5022
|
+
announcePresence: async (sessionId, headers, clientId, clear) => {
|
|
5023
|
+
const body = JSON.stringify({ client_id: clientId, clear });
|
|
4769
5024
|
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
4770
5025
|
return { status, body: await readAll(response) };
|
|
4771
5026
|
},
|
|
5027
|
+
markRead: async (sessionId, headers, eventId) => {
|
|
5028
|
+
const body = JSON.stringify(buildRcMarkReadBody(eventId));
|
|
5029
|
+
const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
|
|
5030
|
+
return { status, body: await readAll(response) };
|
|
5031
|
+
},
|
|
4772
5032
|
openStream: async (sessionId, headers, resume, signal) => {
|
|
4773
5033
|
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)}`;
|
|
4774
5034
|
const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
|
|
@@ -5137,6 +5397,52 @@ var RcSetPermissionModeInputSchema = import_zod4.z.strictObject({
|
|
|
5137
5397
|
session: import_zod4.z.string().min(1),
|
|
5138
5398
|
mode: RcPermissionModeSchema
|
|
5139
5399
|
});
|
|
5400
|
+
var RcEndSessionInputSchema = import_zod4.z.strictObject({
|
|
5401
|
+
session: import_zod4.z.string().min(1),
|
|
5402
|
+
reason: import_zod4.z.string().min(1).optional()
|
|
5403
|
+
});
|
|
5404
|
+
var RcGetUsageInputSchema = import_zod4.z.strictObject({
|
|
5405
|
+
session: import_zod4.z.string().min(1),
|
|
5406
|
+
skipBehaviors: import_zod4.z.boolean().optional()
|
|
5407
|
+
});
|
|
5408
|
+
var RcGetContextUsageInputSchema = import_zod4.z.strictObject({
|
|
5409
|
+
session: import_zod4.z.string().min(1),
|
|
5410
|
+
detail: import_zod4.z.enum(RC_CONTEXT_USAGE_DETAILS).optional()
|
|
5411
|
+
});
|
|
5412
|
+
var RcReadFileInputSchema = import_zod4.z.strictObject({
|
|
5413
|
+
session: import_zod4.z.string().min(1),
|
|
5414
|
+
path: import_zod4.z.string().min(1),
|
|
5415
|
+
maxBytes: import_zod4.z.number().int().positive().optional(),
|
|
5416
|
+
encoding: import_zod4.z.enum(RC_READ_FILE_ENCODINGS).optional()
|
|
5417
|
+
});
|
|
5418
|
+
var RcFileSuggestionsInputSchema = import_zod4.z.strictObject({
|
|
5419
|
+
session: import_zod4.z.string().min(1),
|
|
5420
|
+
query: import_zod4.z.string()
|
|
5421
|
+
});
|
|
5422
|
+
var RcKeepAliveInputSchema = import_zod4.z.strictObject({
|
|
5423
|
+
session: import_zod4.z.string().min(1)
|
|
5424
|
+
});
|
|
5425
|
+
var RcMcpStatusInputSchema = import_zod4.z.strictObject({
|
|
5426
|
+
session: import_zod4.z.string().min(1)
|
|
5427
|
+
});
|
|
5428
|
+
var RcMcpReconnectInputSchema = import_zod4.z.strictObject({
|
|
5429
|
+
session: import_zod4.z.string().min(1),
|
|
5430
|
+
serverName: import_zod4.z.string().min(1)
|
|
5431
|
+
});
|
|
5432
|
+
var RcMcpAuthenticateInputSchema = import_zod4.z.strictObject({
|
|
5433
|
+
session: import_zod4.z.string().min(1),
|
|
5434
|
+
serverName: import_zod4.z.string().min(1),
|
|
5435
|
+
redirectUri: import_zod4.z.string().min(1)
|
|
5436
|
+
});
|
|
5437
|
+
var RcMcpOAuthCallbackUrlInputSchema = import_zod4.z.strictObject({
|
|
5438
|
+
session: import_zod4.z.string().min(1),
|
|
5439
|
+
serverName: import_zod4.z.string().min(1),
|
|
5440
|
+
callbackUrl: import_zod4.z.string().min(1)
|
|
5441
|
+
});
|
|
5442
|
+
var RcTeleportInputSchema = import_zod4.z.strictObject({
|
|
5443
|
+
session: import_zod4.z.string().min(1),
|
|
5444
|
+
marker: import_zod4.z.string().min(1)
|
|
5445
|
+
});
|
|
5140
5446
|
var RcListOutputSchema = import_zod4.z.strictObject({ sessions: import_zod4.z.readonly(import_zod4.z.array(RcSessionSummarySchema)) });
|
|
5141
5447
|
var RcStatusOutputSchema = import_zod4.z.strictObject({ statuses: import_zod4.z.readonly(import_zod4.z.array(RcSessionStatusSchema)) });
|
|
5142
5448
|
var RcPendingOutputSchema = import_zod4.z.strictObject({ pending: import_zod4.z.readonly(import_zod4.z.array(RcPendingRequestSummarySchema)) });
|
|
@@ -5144,6 +5450,11 @@ var RcWriteOutputSchema = import_zod4.z.strictObject({
|
|
|
5144
5450
|
session: import_zod4.z.string(),
|
|
5145
5451
|
sequenceNums: import_zod4.z.readonly(import_zod4.z.array(import_zod4.z.number()))
|
|
5146
5452
|
});
|
|
5453
|
+
var RcControlWriteOutputSchema = import_zod4.z.strictObject({
|
|
5454
|
+
session: import_zod4.z.string(),
|
|
5455
|
+
request: import_zod4.z.string(),
|
|
5456
|
+
sequenceNums: import_zod4.z.readonly(import_zod4.z.array(import_zod4.z.number()))
|
|
5457
|
+
});
|
|
5147
5458
|
var RcAnswerOutputSchema = import_zod4.z.strictObject({
|
|
5148
5459
|
session: import_zod4.z.string(),
|
|
5149
5460
|
request: import_zod4.z.string(),
|
|
@@ -5172,6 +5483,15 @@ function createRcApiRouter(deps) {
|
|
|
5172
5483
|
}
|
|
5173
5484
|
return result.sequenceNums;
|
|
5174
5485
|
};
|
|
5486
|
+
const deliveredControl = (sessionId, result) => {
|
|
5487
|
+
if (!result.ok) {
|
|
5488
|
+
throw new import_client.ORPCError("BAD_GATEWAY", { message: result.message });
|
|
5489
|
+
}
|
|
5490
|
+
if (result.requestId === void 0) {
|
|
5491
|
+
throw new import_client.ORPCError("INTERNAL_SERVER_ERROR", { message: "the control request was delivered but the door cannot name the request id it minted, so the worker's answer cannot be matched" });
|
|
5492
|
+
}
|
|
5493
|
+
return { request: result.requestId, sequenceNums: result.sequenceNums };
|
|
5494
|
+
};
|
|
5175
5495
|
return {
|
|
5176
5496
|
rc: {
|
|
5177
5497
|
list: authed.output(RcListOutputSchema).handler(() => ({ sessions: deps.list() })),
|
|
@@ -5194,9 +5514,20 @@ function createRcApiRouter(deps) {
|
|
|
5194
5514
|
const decision = { approve: input.approve, message: input.approve || input.text === void 0 ? void 0 : input.text };
|
|
5195
5515
|
return { session: input.session, request: input.request, sequenceNums: delivered(input.session, await deps.answer(input.session, input.request, decision)) };
|
|
5196
5516
|
}),
|
|
5197
|
-
interrupt: authed.input(RcInterruptInputSchema).output(
|
|
5198
|
-
setModel: authed.input(RcSetModelInputSchema).output(
|
|
5199
|
-
setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(
|
|
5517
|
+
interrupt: authed.input(RcInterruptInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.interrupt(input.session)) })),
|
|
5518
|
+
setModel: authed.input(RcSetModelInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setModel(input.session, input.model)) })),
|
|
5519
|
+
setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
|
|
5520
|
+
endSession: authed.input(RcEndSessionInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.endSession(input.session, input.reason)) })),
|
|
5521
|
+
getUsage: authed.input(RcGetUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getUsage(input.session, input.skipBehaviors)) })),
|
|
5522
|
+
getContextUsage: authed.input(RcGetContextUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getContextUsage(input.session, input.detail)) })),
|
|
5523
|
+
readFile: authed.input(RcReadFileInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.readFile(input.session, input.path, { ...input.maxBytes === void 0 ? {} : { maxBytes: input.maxBytes }, ...input.encoding === void 0 ? {} : { encoding: input.encoding } })) })),
|
|
5524
|
+
fileSuggestions: authed.input(RcFileSuggestionsInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.fileSuggestions(input.session, input.query)) })),
|
|
5525
|
+
keepAlive: authed.input(RcKeepAliveInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.keepAlive(input.session)) })),
|
|
5526
|
+
mcpStatus: authed.input(RcMcpStatusInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpStatus(input.session)) })),
|
|
5527
|
+
mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
|
|
5528
|
+
mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
|
|
5529
|
+
mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
|
|
5530
|
+
teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
|
|
5200
5531
|
subscribe: authed.input(RcSessionQuerySchema).output((0, import_server2.eventIterator)(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
|
|
5201
5532
|
const queue = [];
|
|
5202
5533
|
let wake;
|
|
@@ -10153,9 +10484,26 @@ function createRcStreamHub(deps) {
|
|
|
10153
10484
|
}
|
|
10154
10485
|
return headers;
|
|
10155
10486
|
};
|
|
10487
|
+
let receiptFailureLogged = false;
|
|
10488
|
+
const sendReceipt = (attachment, headers, eventId) => {
|
|
10489
|
+
if (eventId === void 0) {
|
|
10490
|
+
return;
|
|
10491
|
+
}
|
|
10492
|
+
deps.dial.markRead(attachment.sessionId, headers, eventId).then(
|
|
10493
|
+
() => {
|
|
10494
|
+
receiptFailureLogged = false;
|
|
10495
|
+
},
|
|
10496
|
+
(error) => {
|
|
10497
|
+
if (!receiptFailureLogged) {
|
|
10498
|
+
receiptFailureLogged = true;
|
|
10499
|
+
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`);
|
|
10500
|
+
}
|
|
10501
|
+
}
|
|
10502
|
+
);
|
|
10503
|
+
};
|
|
10156
10504
|
const attempt = async (attachment, credential) => {
|
|
10157
10505
|
const headers = streamHeaders(credential);
|
|
10158
|
-
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
|
|
10506
|
+
const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
|
|
10159
10507
|
if (presence.status === HTTP_STATUS.unauthorized) {
|
|
10160
10508
|
return "unauthorized";
|
|
10161
10509
|
}
|
|
@@ -10175,6 +10523,7 @@ function createRcStreamHub(deps) {
|
|
|
10175
10523
|
const parser = createSseParser();
|
|
10176
10524
|
for await (const chunk of answer.chunks) {
|
|
10177
10525
|
attachment.controller.signal.throwIfAborted();
|
|
10526
|
+
let lastEventId;
|
|
10178
10527
|
for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
|
|
10179
10528
|
const envelope = parseRcStreamEnvelope(parsed);
|
|
10180
10529
|
if (envelope === void 0) {
|
|
@@ -10182,10 +10531,21 @@ function createRcStreamHub(deps) {
|
|
|
10182
10531
|
}
|
|
10183
10532
|
deps.fileStreamEvent(attachment.sessionId, envelope);
|
|
10184
10533
|
deps.fanout.publish({ session: attachment.sessionId, envelope });
|
|
10534
|
+
if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
|
|
10535
|
+
lastEventId = envelope.event_id;
|
|
10536
|
+
}
|
|
10185
10537
|
}
|
|
10538
|
+
sendReceipt(attachment, headers, lastEventId);
|
|
10186
10539
|
}
|
|
10187
10540
|
return "dropped";
|
|
10188
10541
|
};
|
|
10542
|
+
const clearPresence = (attachment, headers) => {
|
|
10543
|
+
if (headers === void 0) {
|
|
10544
|
+
return;
|
|
10545
|
+
}
|
|
10546
|
+
void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
|
|
10547
|
+
});
|
|
10548
|
+
};
|
|
10189
10549
|
const runLoop = async (attachment) => {
|
|
10190
10550
|
let retriedUnauthorized = false;
|
|
10191
10551
|
for (; ; ) {
|
|
@@ -10197,6 +10557,7 @@ function createRcStreamHub(deps) {
|
|
|
10197
10557
|
if (!live || credential?.authorization === void 0) {
|
|
10198
10558
|
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"})`);
|
|
10199
10559
|
persistCursor(attachment.sessionId);
|
|
10560
|
+
clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
|
|
10200
10561
|
if (attachments.get(attachment.sessionId) === attachment) {
|
|
10201
10562
|
attachments.delete(attachment.sessionId);
|
|
10202
10563
|
}
|
|
@@ -10259,6 +10620,8 @@ function createRcStreamHub(deps) {
|
|
|
10259
10620
|
close: () => {
|
|
10260
10621
|
for (const attachment of attachments.values()) {
|
|
10261
10622
|
persistCursor(attachment.sessionId);
|
|
10623
|
+
const credential = deps.credentialOf(attachment.sessionId);
|
|
10624
|
+
clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
|
|
10262
10625
|
attachment.stop();
|
|
10263
10626
|
}
|
|
10264
10627
|
attachments.clear();
|
|
@@ -10266,6 +10629,193 @@ function createRcStreamHub(deps) {
|
|
|
10266
10629
|
};
|
|
10267
10630
|
}
|
|
10268
10631
|
|
|
10632
|
+
// src/frontdoor/rcSelfHostEndpoints.ts
|
|
10633
|
+
var RC_SOURCE_WORKER = "worker";
|
|
10634
|
+
var RC_SOURCE_CLIENT = "client";
|
|
10635
|
+
function isRecord6(value) {
|
|
10636
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10637
|
+
}
|
|
10638
|
+
function parseJsonObject2(body) {
|
|
10639
|
+
let parsed;
|
|
10640
|
+
try {
|
|
10641
|
+
parsed = JSON.parse(body);
|
|
10642
|
+
} catch {
|
|
10643
|
+
return void 0;
|
|
10644
|
+
}
|
|
10645
|
+
return isRecord6(parsed) ? parsed : void 0;
|
|
10646
|
+
}
|
|
10647
|
+
function nonEmptyString2(record, field) {
|
|
10648
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10649
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10650
|
+
}
|
|
10651
|
+
function numberOf(record, field) {
|
|
10652
|
+
const value = record === void 0 ? void 0 : record[field];
|
|
10653
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10654
|
+
return value;
|
|
10655
|
+
}
|
|
10656
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value)) {
|
|
10657
|
+
return Number(value);
|
|
10658
|
+
}
|
|
10659
|
+
return void 0;
|
|
10660
|
+
}
|
|
10661
|
+
function pageOfRcChannel(url, rows, idOf, rowOf) {
|
|
10662
|
+
const query = new URL(url, "http://127.0.0.1").searchParams;
|
|
10663
|
+
const anchor = query.get("after_event_id") ?? query.get("cursor");
|
|
10664
|
+
const rawLimit = query.get("limit");
|
|
10665
|
+
const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
|
|
10666
|
+
if (limit !== void 0 && limit < 1) {
|
|
10667
|
+
return { error: "the limit names a whole number of rows of at least one" };
|
|
10668
|
+
}
|
|
10669
|
+
const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
|
|
10670
|
+
if (anchor !== null && from === 0) {
|
|
10671
|
+
return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
|
|
10672
|
+
}
|
|
10673
|
+
const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
|
|
10674
|
+
const last = page[page.length - 1];
|
|
10675
|
+
return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
|
|
10676
|
+
}
|
|
10677
|
+
async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
|
|
10678
|
+
const { request: request4, response, session } = ctx;
|
|
10679
|
+
if (tail === "client/presence" && method === "POST") {
|
|
10680
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10681
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10682
|
+
const clear = body?.clear;
|
|
10683
|
+
if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
|
|
10684
|
+
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" } });
|
|
10685
|
+
return true;
|
|
10686
|
+
}
|
|
10687
|
+
if (clear === true) {
|
|
10688
|
+
session.clients.delete(clientId);
|
|
10689
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
|
|
10690
|
+
} else {
|
|
10691
|
+
session.clients.set(clientId, ctx.now());
|
|
10692
|
+
ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
|
|
10693
|
+
}
|
|
10694
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
|
|
10695
|
+
return true;
|
|
10696
|
+
}
|
|
10697
|
+
if (tail === "mark_read" && method === "POST") {
|
|
10698
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10699
|
+
const eventId = nonEmptyString2(body, "event_id");
|
|
10700
|
+
const clientId = nonEmptyString2(body, "client_id");
|
|
10701
|
+
if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
|
|
10702
|
+
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" } });
|
|
10703
|
+
return true;
|
|
10704
|
+
}
|
|
10705
|
+
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);
|
|
10706
|
+
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)}`);
|
|
10707
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10708
|
+
return true;
|
|
10709
|
+
}
|
|
10710
|
+
if (tail === "teleport-events" && method === "POST") {
|
|
10711
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10712
|
+
const events = body?.events;
|
|
10713
|
+
if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
|
|
10714
|
+
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` } });
|
|
10715
|
+
return true;
|
|
10716
|
+
}
|
|
10717
|
+
const results = [];
|
|
10718
|
+
for (const event of events) {
|
|
10719
|
+
if (!isRecord6(event) || !isRecord6(event.payload)) {
|
|
10720
|
+
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" } });
|
|
10721
|
+
return true;
|
|
10722
|
+
}
|
|
10723
|
+
const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
|
|
10724
|
+
results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
|
|
10725
|
+
}
|
|
10726
|
+
ctx.sweep();
|
|
10727
|
+
ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
|
|
10728
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { results });
|
|
10729
|
+
return true;
|
|
10730
|
+
}
|
|
10731
|
+
if (tail === "teleport-events" && method === "GET") {
|
|
10732
|
+
const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
|
|
10733
|
+
if ("error" in page) {
|
|
10734
|
+
await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
|
|
10735
|
+
return true;
|
|
10736
|
+
}
|
|
10737
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, page);
|
|
10738
|
+
return true;
|
|
10739
|
+
}
|
|
10740
|
+
return false;
|
|
10741
|
+
}
|
|
10742
|
+
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" });
|
|
10743
|
+
async function serveRcCompatSessions(ctx, pathname, compatPath) {
|
|
10744
|
+
const { request: request4, response } = ctx;
|
|
10745
|
+
const method = request4.method.toUpperCase();
|
|
10746
|
+
if (pathname === compatPath) {
|
|
10747
|
+
if (method !== "GET") {
|
|
10748
|
+
await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
10749
|
+
return;
|
|
10750
|
+
}
|
|
10751
|
+
const listRefusal = ctx.credentialRefusal(request4.headers);
|
|
10752
|
+
if (listRefusal !== void 0) {
|
|
10753
|
+
await ctx.unauthorized(response, listRefusal);
|
|
10754
|
+
return;
|
|
10755
|
+
}
|
|
10756
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
|
|
10757
|
+
return;
|
|
10758
|
+
}
|
|
10759
|
+
const rest = pathname.slice(`${compatPath}/`.length);
|
|
10760
|
+
const slash = rest.indexOf("/");
|
|
10761
|
+
const address = slash === -1 ? rest : rest.slice(0, slash);
|
|
10762
|
+
const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
|
|
10763
|
+
const refusal = ctx.credentialRefusal(request4.headers);
|
|
10764
|
+
if (refusal !== void 0) {
|
|
10765
|
+
await ctx.unauthorized(response, refusal);
|
|
10766
|
+
return;
|
|
10767
|
+
}
|
|
10768
|
+
const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
|
|
10769
|
+
if (compatTail === "") {
|
|
10770
|
+
if (method === "GET") {
|
|
10771
|
+
if (session === void 0) {
|
|
10772
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10773
|
+
return;
|
|
10774
|
+
}
|
|
10775
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10776
|
+
return;
|
|
10777
|
+
}
|
|
10778
|
+
if (method === "PATCH" || method === "PUT") {
|
|
10779
|
+
if (session === void 0) {
|
|
10780
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10781
|
+
return;
|
|
10782
|
+
}
|
|
10783
|
+
const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10784
|
+
const title = nonEmptyString2(body, "title");
|
|
10785
|
+
if (title === void 0) {
|
|
10786
|
+
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" } });
|
|
10787
|
+
return;
|
|
10788
|
+
}
|
|
10789
|
+
session.title = title;
|
|
10790
|
+
session.lastTrafficAt = ctx.now();
|
|
10791
|
+
ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
|
|
10792
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10793
|
+
return;
|
|
10794
|
+
}
|
|
10795
|
+
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" } });
|
|
10796
|
+
return;
|
|
10797
|
+
}
|
|
10798
|
+
if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
|
|
10799
|
+
await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
10800
|
+
if (session === void 0) {
|
|
10801
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
|
|
10802
|
+
return;
|
|
10803
|
+
}
|
|
10804
|
+
if (compatTail === "unarchive") {
|
|
10805
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
|
|
10806
|
+
return;
|
|
10807
|
+
}
|
|
10808
|
+
for (const sink of [...session.workerStreams, ...session.clientStreams]) {
|
|
10809
|
+
sink.retire();
|
|
10810
|
+
}
|
|
10811
|
+
ctx.sessions.delete(session.id);
|
|
10812
|
+
ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
|
|
10813
|
+
await ctx.answerJson(response, HTTP_STATUS.ok, {});
|
|
10814
|
+
return;
|
|
10815
|
+
}
|
|
10816
|
+
await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
|
|
10817
|
+
}
|
|
10818
|
+
|
|
10269
10819
|
// src/frontdoor/rcWebFetch.ts
|
|
10270
10820
|
var dns2 = __toESM(require("node:dns"), 1);
|
|
10271
10821
|
var http2 = __toESM(require("node:http"), 1);
|
|
@@ -10534,21 +11084,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
|
|
|
10534
11084
|
var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
|
|
10535
11085
|
var RC_SELF_HOST_WORKER_EPOCH = 1;
|
|
10536
11086
|
var RC_STREAM_EVENT_NAME2 = "client_event";
|
|
10537
|
-
var RC_SOURCE_WORKER = "worker";
|
|
10538
|
-
var RC_SOURCE_CLIENT = "client";
|
|
10539
11087
|
var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
|
|
10540
11088
|
var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
|
|
10541
|
-
function
|
|
11089
|
+
function isRecord7(value) {
|
|
10542
11090
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10543
11091
|
}
|
|
10544
11092
|
function isWebSearchRefusal(value) {
|
|
10545
11093
|
return !Array.isArray(value);
|
|
10546
11094
|
}
|
|
10547
|
-
function
|
|
11095
|
+
function nonEmptyString3(record, field) {
|
|
10548
11096
|
const value = record === void 0 ? void 0 : record[field];
|
|
10549
11097
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10550
11098
|
}
|
|
10551
|
-
function
|
|
11099
|
+
function numberOf2(record, field) {
|
|
10552
11100
|
const value = record === void 0 ? void 0 : record[field];
|
|
10553
11101
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10554
11102
|
return value;
|
|
@@ -10615,14 +11163,14 @@ async function readBody2(request4, capBytes) {
|
|
|
10615
11163
|
});
|
|
10616
11164
|
});
|
|
10617
11165
|
}
|
|
10618
|
-
function
|
|
11166
|
+
function parseJsonObject3(body) {
|
|
10619
11167
|
let parsed;
|
|
10620
11168
|
try {
|
|
10621
11169
|
parsed = JSON.parse(body);
|
|
10622
11170
|
} catch {
|
|
10623
11171
|
return void 0;
|
|
10624
11172
|
}
|
|
10625
|
-
return
|
|
11173
|
+
return isRecord7(parsed) ? parsed : void 0;
|
|
10626
11174
|
}
|
|
10627
11175
|
function mintWorkerJwt(randomToken, issuedAt) {
|
|
10628
11176
|
const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
|
|
@@ -10645,11 +11193,16 @@ function createRcSelfHostSurface(deps) {
|
|
|
10645
11193
|
for (const session of sessions.values()) {
|
|
10646
11194
|
while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10647
11195
|
const dropped = session.events.shift();
|
|
10648
|
-
const uuid = dropped === void 0 ? void 0 :
|
|
11196
|
+
const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
|
|
10649
11197
|
if (uuid !== void 0) {
|
|
10650
11198
|
session.seenPayloadUuids.delete(uuid);
|
|
10651
11199
|
}
|
|
10652
11200
|
}
|
|
11201
|
+
for (const [clientId, lastSeenAt] of session.clients) {
|
|
11202
|
+
if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
|
|
11203
|
+
session.clients.delete(clientId);
|
|
11204
|
+
}
|
|
11205
|
+
}
|
|
10653
11206
|
if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
|
|
10654
11207
|
sessions.delete(session.id);
|
|
10655
11208
|
}
|
|
@@ -10688,7 +11241,7 @@ function createRcSelfHostSurface(deps) {
|
|
|
10688
11241
|
const frameOf = (event) => {
|
|
10689
11242
|
const envelope = {
|
|
10690
11243
|
event_id: event.eventId,
|
|
10691
|
-
event_type:
|
|
11244
|
+
event_type: nonEmptyString3(event.payload, "type") ?? "event",
|
|
10692
11245
|
sequence_num: event.sequenceNum,
|
|
10693
11246
|
source: event.source,
|
|
10694
11247
|
payload: event.payload,
|
|
@@ -10702,7 +11255,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10702
11255
|
};
|
|
10703
11256
|
const holdStream = async (session, which, wantsReplay, request4, response) => {
|
|
10704
11257
|
const url = new URL(request4.url, "http://127.0.0.1");
|
|
10705
|
-
const cursor =
|
|
11258
|
+
const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
|
|
10706
11259
|
const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
|
|
10707
11260
|
response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
10708
11261
|
response.flush();
|
|
@@ -10731,8 +11284,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10731
11284
|
sink.retire();
|
|
10732
11285
|
response.end();
|
|
10733
11286
|
};
|
|
10734
|
-
const publish = (session, source, payload) => {
|
|
10735
|
-
const uuid =
|
|
11287
|
+
const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
|
|
11288
|
+
const uuid = nonEmptyString3(payload, "uuid");
|
|
10736
11289
|
const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
|
|
10737
11290
|
session.sequenceNum += 1;
|
|
10738
11291
|
const event = {
|
|
@@ -10740,9 +11293,9 @@ data: ${JSON.stringify(envelope)}
|
|
|
10740
11293
|
eventId: uuid ?? deps.newUuid(),
|
|
10741
11294
|
source,
|
|
10742
11295
|
// 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.
|
|
10743
|
-
payload: source ===
|
|
11296
|
+
payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
|
|
10744
11297
|
createdAt: new Date(deps.now()).toISOString(),
|
|
10745
|
-
toWorker
|
|
11298
|
+
toWorker,
|
|
10746
11299
|
storedAt: deps.now()
|
|
10747
11300
|
};
|
|
10748
11301
|
session.events.push(event);
|
|
@@ -10790,8 +11343,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
10790
11343
|
}
|
|
10791
11344
|
session.lastTrafficAt = deps.now();
|
|
10792
11345
|
if (tail === "") {
|
|
11346
|
+
if (method === "PATCH" || method === "PUT") {
|
|
11347
|
+
const refusal = credentialRefusal(request4.headers);
|
|
11348
|
+
if (refusal !== void 0) {
|
|
11349
|
+
await unauthorized(response, refusal);
|
|
11350
|
+
return;
|
|
11351
|
+
}
|
|
11352
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11353
|
+
const title = nonEmptyString3(body, "title");
|
|
11354
|
+
if (title === void 0) {
|
|
11355
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
|
|
11356
|
+
return;
|
|
11357
|
+
}
|
|
11358
|
+
session.title = title;
|
|
11359
|
+
session.lastTrafficAt = deps.now();
|
|
11360
|
+
deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
|
|
11361
|
+
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11362
|
+
return;
|
|
11363
|
+
}
|
|
10793
11364
|
if (method !== "GET") {
|
|
10794
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
|
|
11365
|
+
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" } });
|
|
10795
11366
|
return;
|
|
10796
11367
|
}
|
|
10797
11368
|
await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
|
|
@@ -10836,17 +11407,17 @@ data: ${JSON.stringify(envelope)}
|
|
|
10836
11407
|
return;
|
|
10837
11408
|
}
|
|
10838
11409
|
if (workerTail === "" && method === "PUT") {
|
|
10839
|
-
const body =
|
|
10840
|
-
const claimedEpoch =
|
|
11410
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11411
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10841
11412
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10842
11413
|
await epochStale(response);
|
|
10843
11414
|
return;
|
|
10844
11415
|
}
|
|
10845
|
-
const status =
|
|
11416
|
+
const status = nonEmptyString3(body, "worker_status");
|
|
10846
11417
|
if (status !== void 0) {
|
|
10847
11418
|
session.workerStatus = status;
|
|
10848
11419
|
}
|
|
10849
|
-
if (
|
|
11420
|
+
if (isRecord7(body?.external_metadata)) {
|
|
10850
11421
|
session.externalMetadata = body.external_metadata;
|
|
10851
11422
|
}
|
|
10852
11423
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
@@ -10863,8 +11434,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10863
11434
|
return;
|
|
10864
11435
|
}
|
|
10865
11436
|
if (workerTail === "events" && method === "POST") {
|
|
10866
|
-
const body =
|
|
10867
|
-
const claimedEpoch =
|
|
11437
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11438
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10868
11439
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10869
11440
|
await epochStale(response);
|
|
10870
11441
|
return;
|
|
@@ -10876,7 +11447,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10876
11447
|
}
|
|
10877
11448
|
const results = [];
|
|
10878
11449
|
for (const event of events) {
|
|
10879
|
-
if (!
|
|
11450
|
+
if (!isRecord7(event) || !isRecord7(event.payload)) {
|
|
10880
11451
|
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" } });
|
|
10881
11452
|
return;
|
|
10882
11453
|
}
|
|
@@ -10897,8 +11468,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10897
11468
|
return;
|
|
10898
11469
|
}
|
|
10899
11470
|
if (workerTail === "internal-events" && method === "POST") {
|
|
10900
|
-
const body =
|
|
10901
|
-
const claimedEpoch =
|
|
11471
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11472
|
+
const claimedEpoch = numberOf2(body, "worker_epoch");
|
|
10902
11473
|
if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
|
|
10903
11474
|
await epochStale(response);
|
|
10904
11475
|
return;
|
|
@@ -10909,19 +11480,26 @@ data: ${JSON.stringify(envelope)}
|
|
|
10909
11480
|
return;
|
|
10910
11481
|
}
|
|
10911
11482
|
for (const event of events) {
|
|
10912
|
-
if (
|
|
10913
|
-
session.internalEvents.push({
|
|
11483
|
+
if (isRecord7(event)) {
|
|
11484
|
+
session.internalEvents.push({ eventId: deps.newUuid(), body: event });
|
|
10914
11485
|
}
|
|
10915
11486
|
}
|
|
10916
11487
|
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
10917
11488
|
return;
|
|
10918
11489
|
}
|
|
10919
11490
|
if (workerTail === "internal-events" && method === "GET") {
|
|
10920
|
-
const
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
11491
|
+
const query = new URL(request4.url, "http://127.0.0.1").searchParams;
|
|
11492
|
+
if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
|
|
11493
|
+
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" } });
|
|
11494
|
+
return;
|
|
11495
|
+
}
|
|
11496
|
+
const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
|
|
11497
|
+
if ("error" in page) {
|
|
11498
|
+
const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
|
|
11499
|
+
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
|
|
11500
|
+
return;
|
|
11501
|
+
}
|
|
11502
|
+
await answerJson2(response, HTTP_STATUS.ok, page);
|
|
10925
11503
|
return;
|
|
10926
11504
|
}
|
|
10927
11505
|
if (workerTail === "diagnostics" && method === "POST") {
|
|
@@ -10930,8 +11508,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10930
11508
|
return;
|
|
10931
11509
|
}
|
|
10932
11510
|
if (workerTail === "web-fetch" && method === "POST") {
|
|
10933
|
-
const body =
|
|
10934
|
-
const target =
|
|
11511
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11512
|
+
const target = nonEmptyString3(body, "url");
|
|
10935
11513
|
if (target === void 0) {
|
|
10936
11514
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
|
|
10937
11515
|
return;
|
|
@@ -10953,8 +11531,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
10953
11531
|
return;
|
|
10954
11532
|
}
|
|
10955
11533
|
if (workerTail === "web-search" && method === "POST") {
|
|
10956
|
-
const body =
|
|
10957
|
-
const query =
|
|
11534
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11535
|
+
const query = nonEmptyString3(body, "query");
|
|
10958
11536
|
if (query === void 0) {
|
|
10959
11537
|
await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
|
|
10960
11538
|
return;
|
|
@@ -10965,7 +11543,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10965
11543
|
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" } });
|
|
10966
11544
|
return;
|
|
10967
11545
|
}
|
|
10968
|
-
const searchProfile =
|
|
11546
|
+
const searchProfile = nonEmptyString3(body, "search_profile");
|
|
10969
11547
|
const backend = deps.webSearch;
|
|
10970
11548
|
if (backend === void 0) {
|
|
10971
11549
|
deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
|
|
@@ -10990,7 +11568,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10990
11568
|
return;
|
|
10991
11569
|
}
|
|
10992
11570
|
if (tail === "events" && method === "POST") {
|
|
10993
|
-
const body =
|
|
11571
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
10994
11572
|
const events = body?.events;
|
|
10995
11573
|
if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
|
|
10996
11574
|
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` } });
|
|
@@ -10998,7 +11576,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
10998
11576
|
}
|
|
10999
11577
|
const results = [];
|
|
11000
11578
|
for (const event of events) {
|
|
11001
|
-
if (!
|
|
11579
|
+
if (!isRecord7(event) || !isRecord7(event.payload)) {
|
|
11002
11580
|
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" } });
|
|
11003
11581
|
return;
|
|
11004
11582
|
}
|
|
@@ -11010,26 +11588,14 @@ data: ${JSON.stringify(envelope)}
|
|
|
11010
11588
|
return;
|
|
11011
11589
|
}
|
|
11012
11590
|
if (tail === "events" && method === "GET") {
|
|
11013
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type:
|
|
11591
|
+
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 })) });
|
|
11014
11592
|
return;
|
|
11015
11593
|
}
|
|
11016
11594
|
if (tail === "events/stream" && method === "GET") {
|
|
11017
11595
|
await holdStream(session, "clientStreams", () => true, request4, response);
|
|
11018
11596
|
return;
|
|
11019
11597
|
}
|
|
11020
|
-
if (
|
|
11021
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11022
|
-
await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
|
|
11023
|
-
return;
|
|
11024
|
-
}
|
|
11025
|
-
if (tail === "mark_read" && method === "POST") {
|
|
11026
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11027
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11028
|
-
return;
|
|
11029
|
-
}
|
|
11030
|
-
if (tail === "teleport-events" && method === "POST") {
|
|
11031
|
-
await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
|
|
11032
|
-
await answerJson2(response, HTTP_STATUS.ok, {});
|
|
11598
|
+
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)) {
|
|
11033
11599
|
return;
|
|
11034
11600
|
}
|
|
11035
11601
|
await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
|
|
@@ -11046,17 +11612,8 @@ data: ${JSON.stringify(envelope)}
|
|
|
11046
11612
|
await noCredential(response);
|
|
11047
11613
|
return;
|
|
11048
11614
|
}
|
|
11049
|
-
if (pathname === RC_COMPAT_SESSIONS_PATH) {
|
|
11050
|
-
|
|
11051
|
-
await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
|
|
11052
|
-
return;
|
|
11053
|
-
}
|
|
11054
|
-
const listRefusal = credentialRefusal(request4.headers);
|
|
11055
|
-
if (listRefusal !== void 0) {
|
|
11056
|
-
await unauthorized(response, listRefusal);
|
|
11057
|
-
return;
|
|
11058
|
-
}
|
|
11059
|
-
await answerJson2(response, HTTP_STATUS.ok, { data: [] });
|
|
11615
|
+
if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
|
|
11616
|
+
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);
|
|
11060
11617
|
return;
|
|
11061
11618
|
}
|
|
11062
11619
|
if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
|
|
@@ -11070,11 +11627,11 @@ data: ${JSON.stringify(envelope)}
|
|
|
11070
11627
|
await unauthorized(response, createRefusal);
|
|
11071
11628
|
return;
|
|
11072
11629
|
}
|
|
11073
|
-
const body =
|
|
11630
|
+
const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
|
|
11074
11631
|
const session = {
|
|
11075
11632
|
id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
|
|
11076
11633
|
createdAt: deps.now(),
|
|
11077
|
-
title:
|
|
11634
|
+
title: nonEmptyString3(body, "title"),
|
|
11078
11635
|
workerJwts: /* @__PURE__ */ new Set(),
|
|
11079
11636
|
sequenceNum: 0,
|
|
11080
11637
|
events: [],
|
|
@@ -11082,6 +11639,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11082
11639
|
workerStreams: /* @__PURE__ */ new Set(),
|
|
11083
11640
|
clientStreams: /* @__PURE__ */ new Set(),
|
|
11084
11641
|
internalEvents: [],
|
|
11642
|
+
clients: /* @__PURE__ */ new Map(),
|
|
11085
11643
|
workerStatus: void 0,
|
|
11086
11644
|
externalMetadata: void 0,
|
|
11087
11645
|
lastTrafficAt: deps.now()
|
|
@@ -11126,7 +11684,7 @@ data: ${JSON.stringify(envelope)}
|
|
|
11126
11684
|
const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
11127
11685
|
if (host === RC_SELF_HOST_OAUTH_HOST) {
|
|
11128
11686
|
if (method === "POST" && pathname === "/v1/oauth/token") {
|
|
11129
|
-
const body =
|
|
11687
|
+
const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
|
|
11130
11688
|
const record = deps.credentialRecord();
|
|
11131
11689
|
if (record === void 0) {
|
|
11132
11690
|
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" });
|
|
@@ -11212,7 +11770,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
|
|
|
11212
11770
|
var RC_SELF_HOST_RECORD_FILE = "credential.json";
|
|
11213
11771
|
var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
|
|
11214
11772
|
var SELF_HOST_SUBSCRIPTION_TYPE = "max";
|
|
11215
|
-
function
|
|
11773
|
+
function isRecord8(value) {
|
|
11216
11774
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11217
11775
|
}
|
|
11218
11776
|
function readJsonObject(fs12, file) {
|
|
@@ -11226,7 +11784,7 @@ function readJsonObject(fs12, file) {
|
|
|
11226
11784
|
} catch {
|
|
11227
11785
|
return void 0;
|
|
11228
11786
|
}
|
|
11229
|
-
return
|
|
11787
|
+
return isRecord8(parsed) ? parsed : void 0;
|
|
11230
11788
|
}
|
|
11231
11789
|
function readRcSelfHostRecord(fs12, frontdoorDir) {
|
|
11232
11790
|
const parsed = readJsonObject(fs12, import_node_path28.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
|
|
@@ -11250,7 +11808,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11250
11808
|
const recordFile = import_node_path28.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
|
|
11251
11809
|
const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
|
|
11252
11810
|
const existingCredentials = readJsonObject(deps.fs, credentialsFile);
|
|
11253
|
-
const existingOauth =
|
|
11811
|
+
const existingOauth = isRecord8(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
|
|
11254
11812
|
const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
|
|
11255
11813
|
const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
|
|
11256
11814
|
if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
|
|
@@ -11267,7 +11825,7 @@ function mintRcSelfHostCredential(deps) {
|
|
|
11267
11825
|
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)}
|
|
11268
11826
|
`);
|
|
11269
11827
|
const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
|
|
11270
|
-
const existingBlock = (value) =>
|
|
11828
|
+
const existingBlock = (value) => isRecord8(value) ? value : {};
|
|
11271
11829
|
deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
|
|
11272
11830
|
...claudeJson,
|
|
11273
11831
|
oauthAccount: {
|
|
@@ -11466,7 +12024,7 @@ var import_node_path30 = __toESM(require("node:path"), 1);
|
|
|
11466
12024
|
// package.json
|
|
11467
12025
|
var package_default = {
|
|
11468
12026
|
name: "agent-shim",
|
|
11469
|
-
version: "8.
|
|
12027
|
+
version: "8.25.0",
|
|
11470
12028
|
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.",
|
|
11471
12029
|
license: "MIT",
|
|
11472
12030
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -12709,10 +13267,12 @@ function prepareClaudeLaunch(options) {
|
|
|
12709
13267
|
ProviderKindMismatchError,
|
|
12710
13268
|
ProviderNotFoundError,
|
|
12711
13269
|
ProviderSchema,
|
|
13270
|
+
RC_CONTEXT_USAGE_DETAILS,
|
|
12712
13271
|
RC_IDLE_EXPIRY_MS,
|
|
12713
13272
|
RC_ORPC_PATH_PREFIX,
|
|
12714
13273
|
RC_PENDING_DEADLINE_MS,
|
|
12715
13274
|
RC_PERMISSION_MODES,
|
|
13275
|
+
RC_READ_FILE_ENCODINGS,
|
|
12716
13276
|
RC_REQUEST_PARSE_CAP_BYTES,
|
|
12717
13277
|
RC_SELF_HOST_ENV,
|
|
12718
13278
|
RC_SELF_HOST_KEEPALIVE_MS,
|
|
@@ -12743,11 +13303,24 @@ function prepareClaudeLaunch(options) {
|
|
|
12743
13303
|
addPool,
|
|
12744
13304
|
addProvider,
|
|
12745
13305
|
answerRcControlRequest,
|
|
13306
|
+
authenticateRcSessionMcpServer,
|
|
12746
13307
|
buildEntryFacts,
|
|
12747
13308
|
buildLayoutPaths,
|
|
13309
|
+
buildRcControlRequestPayload,
|
|
12748
13310
|
buildRcControlResponsePayload,
|
|
13311
|
+
buildRcEndSessionPayload,
|
|
12749
13312
|
buildRcEventWriteBody,
|
|
13313
|
+
buildRcFileSuggestionsPayload,
|
|
13314
|
+
buildRcGetContextUsagePayload,
|
|
13315
|
+
buildRcGetUsagePayload,
|
|
12750
13316
|
buildRcInterruptPayload,
|
|
13317
|
+
buildRcKeepAlivePayload,
|
|
13318
|
+
buildRcMarkReadBody,
|
|
13319
|
+
buildRcMcpAuthenticatePayload,
|
|
13320
|
+
buildRcMcpOAuthCallbackUrlPayload,
|
|
13321
|
+
buildRcMcpReconnectPayload,
|
|
13322
|
+
buildRcMcpStatusPayload,
|
|
13323
|
+
buildRcReadFilePayload,
|
|
12751
13324
|
buildRcSetModelPayload,
|
|
12752
13325
|
buildRcSetPermissionModePayload,
|
|
12753
13326
|
buildRcUserMessagePayload,
|
|
@@ -12780,6 +13353,7 @@ function prepareClaudeLaunch(options) {
|
|
|
12780
13353
|
doorApiAuth,
|
|
12781
13354
|
doorApiNodeHandlerOf,
|
|
12782
13355
|
effectiveWindow,
|
|
13356
|
+
endRcSession,
|
|
12783
13357
|
ensureCa,
|
|
12784
13358
|
ensureFrontDoor,
|
|
12785
13359
|
ensureHeadroom,
|
|
@@ -12796,6 +13370,9 @@ function prepareClaudeLaunch(options) {
|
|
|
12796
13370
|
frontDoorRcApiClient,
|
|
12797
13371
|
frontDoorRcControl,
|
|
12798
13372
|
generateCa,
|
|
13373
|
+
getRcSessionContextUsage,
|
|
13374
|
+
getRcSessionMcpStatus,
|
|
13375
|
+
getRcSessionUsage,
|
|
12799
13376
|
headroomSocketTarget,
|
|
12800
13377
|
identityExists,
|
|
12801
13378
|
injectRcUserMessage,
|
|
@@ -12803,7 +13380,9 @@ function prepareClaudeLaunch(options) {
|
|
|
12803
13380
|
isIdentityDirectoryName,
|
|
12804
13381
|
isInterceptedHost,
|
|
12805
13382
|
isPrivateAddress,
|
|
13383
|
+
isRcContextUsageDetail,
|
|
12806
13384
|
isRcPermissionMode,
|
|
13385
|
+
isRcReadFileEncoding,
|
|
12807
13386
|
lateRcEventDial,
|
|
12808
13387
|
lateRcStreamDial,
|
|
12809
13388
|
listDirectoryRules,
|
|
@@ -12837,6 +13416,7 @@ function prepareClaudeLaunch(options) {
|
|
|
12837
13416
|
readProfile,
|
|
12838
13417
|
readProvider,
|
|
12839
13418
|
readRcSelfHostRecord,
|
|
13419
|
+
readRcSessionFile,
|
|
12840
13420
|
readUsageSnapshot,
|
|
12841
13421
|
realConnectCertStore,
|
|
12842
13422
|
realConnectEffects,
|
|
@@ -12844,6 +13424,7 @@ function prepareClaudeLaunch(options) {
|
|
|
12844
13424
|
realRcEventDial,
|
|
12845
13425
|
realRcStreamDial,
|
|
12846
13426
|
realRcWebFetch,
|
|
13427
|
+
reconnectRcSessionMcpServer,
|
|
12847
13428
|
recoverFarm,
|
|
12848
13429
|
recoveryDiagnostics,
|
|
12849
13430
|
removeDirectoryRule,
|
|
@@ -12863,6 +13444,7 @@ function prepareClaudeLaunch(options) {
|
|
|
12863
13444
|
runFrontDoorSupervisor,
|
|
12864
13445
|
runLauncher,
|
|
12865
13446
|
runSupervisor,
|
|
13447
|
+
sendRcKeepAlive,
|
|
12866
13448
|
servedByPipeline,
|
|
12867
13449
|
setAllowAmbientCredential,
|
|
12868
13450
|
setDefaultConfigProfile,
|
|
@@ -12878,6 +13460,9 @@ function prepareClaudeLaunch(options) {
|
|
|
12878
13460
|
snapshotPath,
|
|
12879
13461
|
spawnClaude,
|
|
12880
13462
|
startConnectServer,
|
|
13463
|
+
submitRcSessionMcpOAuthCallbackUrl,
|
|
13464
|
+
suggestRcSessionFiles,
|
|
13465
|
+
teleportRcSession,
|
|
12881
13466
|
topLevelNames,
|
|
12882
13467
|
updateDirectoryRule,
|
|
12883
13468
|
updateProvider,
|