agent-shim 8.22.0 → 8.24.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 +11 -1
- package/dist/cli.cjs +2293 -1821
- package/dist/index.cjs +622 -101
- package/dist/index.mjs +597 -101
- package/dist/types/index.d.ts +416 -22
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -368,6 +368,11 @@ var FrontDoorGlobalConfigSchema = z.strictObject({
|
|
|
368
368
|
/** How long the front door may sit with no registered sessions before it closes and exits. Defaults to FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES. */
|
|
369
369
|
idleShutdownMinutes: z.number().int().positive().optional()
|
|
370
370
|
});
|
|
371
|
+
var UPDATE_MODES = ["off", "notify", "auto"];
|
|
372
|
+
var UpdateGlobalConfigSchema = z.strictObject({
|
|
373
|
+
/** What a launch does about a newer release of agent-shim itself; see `UPDATE_MODES`. Absent means `off`. */
|
|
374
|
+
mode: z.enum(UPDATE_MODES).optional()
|
|
375
|
+
});
|
|
371
376
|
var GlobalConfigSchema = z.strictObject({
|
|
372
377
|
$schema: z.string().optional(),
|
|
373
378
|
defaultConfigProfile: z.string().min(1).optional(),
|
|
@@ -377,6 +382,7 @@ var GlobalConfigSchema = z.strictObject({
|
|
|
377
382
|
launch: LaunchSchema.optional(),
|
|
378
383
|
headroom: HeadroomGlobalConfigSchema.optional(),
|
|
379
384
|
frontdoor: FrontDoorGlobalConfigSchema.optional(),
|
|
385
|
+
update: UpdateGlobalConfigSchema.optional(),
|
|
380
386
|
pools: z.record(PoolNameSchema, PoolSchema).optional()
|
|
381
387
|
});
|
|
382
388
|
var IdentitySchema = z.strictObject({
|
|
@@ -3624,6 +3630,8 @@ function rcObserverAsPassthrough(observer) {
|
|
|
3624
3630
|
}
|
|
3625
3631
|
};
|
|
3626
3632
|
}
|
|
3633
|
+
|
|
3634
|
+
// src/frontdoor/rcWrites.ts
|
|
3627
3635
|
function buildRcUserMessagePayload(uuid, sessionId, text) {
|
|
3628
3636
|
return {
|
|
3629
3637
|
type: "user",
|
|
@@ -3724,14 +3732,55 @@ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan"
|
|
|
3724
3732
|
function isRcPermissionMode(value) {
|
|
3725
3733
|
return RC_PERMISSION_MODES.some((mode) => mode === value);
|
|
3726
3734
|
}
|
|
3735
|
+
function buildRcControlRequestPayload(requestId, request4) {
|
|
3736
|
+
return { type: "control_request", request_id: requestId, request: request4 };
|
|
3737
|
+
}
|
|
3727
3738
|
function buildRcInterruptPayload(requestId) {
|
|
3728
|
-
return
|
|
3739
|
+
return buildRcControlRequestPayload(requestId, { subtype: "interrupt" });
|
|
3729
3740
|
}
|
|
3730
3741
|
function buildRcSetModelPayload(requestId, model) {
|
|
3731
|
-
return
|
|
3742
|
+
return buildRcControlRequestPayload(requestId, { subtype: "set_model", model });
|
|
3732
3743
|
}
|
|
3733
3744
|
function buildRcSetPermissionModePayload(requestId, mode) {
|
|
3734
|
-
return
|
|
3745
|
+
return buildRcControlRequestPayload(requestId, { subtype: "set_permission_mode", mode });
|
|
3746
|
+
}
|
|
3747
|
+
function buildRcEndSessionPayload(requestId, reason) {
|
|
3748
|
+
return buildRcControlRequestPayload(requestId, { subtype: "end_session", ...reason === void 0 ? {} : { reason } });
|
|
3749
|
+
}
|
|
3750
|
+
function buildRcGetUsagePayload(requestId, skipBehaviors) {
|
|
3751
|
+
return buildRcControlRequestPayload(requestId, { subtype: "get_usage", ...skipBehaviors === true ? { skip_behaviors: true } : {} });
|
|
3752
|
+
}
|
|
3753
|
+
var RC_CONTEXT_USAGE_DETAILS = ["summary", "full"];
|
|
3754
|
+
function isRcContextUsageDetail(value) {
|
|
3755
|
+
return RC_CONTEXT_USAGE_DETAILS.some((detail) => detail === value);
|
|
3756
|
+
}
|
|
3757
|
+
function buildRcGetContextUsagePayload(requestId, detail) {
|
|
3758
|
+
return buildRcControlRequestPayload(requestId, { subtype: "get_context_usage", ...detail === void 0 ? {} : { detail } });
|
|
3759
|
+
}
|
|
3760
|
+
var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
|
|
3761
|
+
function isRcReadFileEncoding(value) {
|
|
3762
|
+
return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
|
|
3763
|
+
}
|
|
3764
|
+
function buildRcReadFilePayload(requestId, path32, options) {
|
|
3765
|
+
return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path32, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
|
|
3766
|
+
}
|
|
3767
|
+
function buildRcFileSuggestionsPayload(requestId, query) {
|
|
3768
|
+
return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
|
|
3769
|
+
}
|
|
3770
|
+
function buildRcMcpStatusPayload(requestId) {
|
|
3771
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_status" });
|
|
3772
|
+
}
|
|
3773
|
+
function buildRcMcpReconnectPayload(requestId, serverName) {
|
|
3774
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_reconnect", serverName });
|
|
3775
|
+
}
|
|
3776
|
+
function buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri) {
|
|
3777
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_authenticate", serverName, redirectUri });
|
|
3778
|
+
}
|
|
3779
|
+
function buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl) {
|
|
3780
|
+
return buildRcControlRequestPayload(requestId, { subtype: "mcp_oauth_callback_url", serverName, callbackUrl });
|
|
3781
|
+
}
|
|
3782
|
+
function buildRcKeepAlivePayload() {
|
|
3783
|
+
return { type: "keep_alive" };
|
|
3735
3784
|
}
|
|
3736
3785
|
async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
3737
3786
|
const credential = deps.credentialOf(sessionId);
|
|
@@ -3758,7 +3807,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
|
3758
3807
|
}
|
|
3759
3808
|
return result;
|
|
3760
3809
|
}
|
|
3761
|
-
async function
|
|
3810
|
+
async function deliverRcClientPayload(deps, sessionId, payload, action) {
|
|
3762
3811
|
const credential = deps.credentialOf(sessionId);
|
|
3763
3812
|
if (credential === void 0) {
|
|
3764
3813
|
return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
|
|
@@ -3766,12 +3815,12 @@ async function interruptRcSession(deps, sessionId) {
|
|
|
3766
3815
|
if (credential.authorization === void 0) {
|
|
3767
3816
|
return noOAuthCredentialResult(sessionId);
|
|
3768
3817
|
}
|
|
3769
|
-
const body = JSON.stringify(buildRcEventWriteBody(
|
|
3818
|
+
const body = JSON.stringify(buildRcEventWriteBody(payload));
|
|
3770
3819
|
let answer;
|
|
3771
3820
|
try {
|
|
3772
3821
|
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
3773
3822
|
} catch (error) {
|
|
3774
|
-
return { ok: false, message: `the door could not reach the API host to
|
|
3823
|
+
return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
|
|
3775
3824
|
}
|
|
3776
3825
|
const result = rcEventWriteResultFromAnswer(answer);
|
|
3777
3826
|
if (result.ok) {
|
|
@@ -3779,50 +3828,89 @@ async function interruptRcSession(deps, sessionId) {
|
|
|
3779
3828
|
}
|
|
3780
3829
|
return result;
|
|
3781
3830
|
}
|
|
3831
|
+
async function interruptRcSession(deps, sessionId) {
|
|
3832
|
+
const requestId = deps.newUuid();
|
|
3833
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcInterruptPayload(requestId), `interrupt session ${sessionId}`);
|
|
3834
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3835
|
+
}
|
|
3782
3836
|
async function setRcSessionModel(deps, sessionId, model) {
|
|
3783
3837
|
if (model === "") {
|
|
3784
3838
|
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" };
|
|
3785
3839
|
}
|
|
3786
|
-
const
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3840
|
+
const requestId = deps.newUuid();
|
|
3841
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcSetModelPayload(requestId, model), `set the model on session ${sessionId}`);
|
|
3842
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3843
|
+
}
|
|
3844
|
+
async function setRcSessionPermissionMode(deps, sessionId, mode) {
|
|
3845
|
+
const requestId = deps.newUuid();
|
|
3846
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcSetPermissionModePayload(requestId, mode), `set the permission mode on session ${sessionId}`);
|
|
3847
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3848
|
+
}
|
|
3849
|
+
async function endRcSession(deps, sessionId, reason) {
|
|
3850
|
+
if (reason === "") {
|
|
3851
|
+
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" };
|
|
3792
3852
|
}
|
|
3793
|
-
const
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3853
|
+
const requestId = deps.newUuid();
|
|
3854
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcEndSessionPayload(requestId, reason), `end session ${sessionId}`);
|
|
3855
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3856
|
+
}
|
|
3857
|
+
async function getRcSessionUsage(deps, sessionId, skipBehaviors) {
|
|
3858
|
+
const requestId = deps.newUuid();
|
|
3859
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcGetUsagePayload(requestId, skipBehaviors), `request the usage of session ${sessionId}`);
|
|
3860
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3861
|
+
}
|
|
3862
|
+
async function getRcSessionContextUsage(deps, sessionId, detail) {
|
|
3863
|
+
const requestId = deps.newUuid();
|
|
3864
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
|
|
3865
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3866
|
+
}
|
|
3867
|
+
async function readRcSessionFile(deps, sessionId, path32, options) {
|
|
3868
|
+
if (path32 === "") {
|
|
3869
|
+
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" };
|
|
3799
3870
|
}
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
deps.noteSequenceNums(sessionId, result.sequenceNums);
|
|
3871
|
+
if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
|
|
3872
|
+
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" };
|
|
3803
3873
|
}
|
|
3804
|
-
|
|
3874
|
+
const requestId = deps.newUuid();
|
|
3875
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path32, options), `read ${path32} from session ${sessionId}`);
|
|
3876
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3805
3877
|
}
|
|
3806
|
-
async function
|
|
3807
|
-
const
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3878
|
+
async function suggestRcSessionFiles(deps, sessionId, query) {
|
|
3879
|
+
const requestId = deps.newUuid();
|
|
3880
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcFileSuggestionsPayload(requestId, query), `request file suggestions from session ${sessionId}`);
|
|
3881
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3882
|
+
}
|
|
3883
|
+
async function getRcSessionMcpStatus(deps, sessionId) {
|
|
3884
|
+
const requestId = deps.newUuid();
|
|
3885
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpStatusPayload(requestId), `request the MCP status of session ${sessionId}`);
|
|
3886
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3887
|
+
}
|
|
3888
|
+
async function reconnectRcSessionMcpServer(deps, sessionId, serverName) {
|
|
3889
|
+
if (serverName === "") {
|
|
3890
|
+
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" };
|
|
3813
3891
|
}
|
|
3814
|
-
const
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3892
|
+
const requestId = deps.newUuid();
|
|
3893
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpReconnectPayload(requestId, serverName), `reconnect MCP server ${serverName} on session ${sessionId}`);
|
|
3894
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3895
|
+
}
|
|
3896
|
+
async function authenticateRcSessionMcpServer(deps, sessionId, serverName, redirectUri) {
|
|
3897
|
+
if (serverName === "" || redirectUri === "") {
|
|
3898
|
+
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" };
|
|
3820
3899
|
}
|
|
3821
|
-
const
|
|
3822
|
-
|
|
3823
|
-
|
|
3900
|
+
const requestId = deps.newUuid();
|
|
3901
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri), `authenticate MCP server ${serverName} on session ${sessionId}`);
|
|
3902
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3903
|
+
}
|
|
3904
|
+
async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, callbackUrl) {
|
|
3905
|
+
if (serverName === "" || callbackUrl === "") {
|
|
3906
|
+
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" };
|
|
3824
3907
|
}
|
|
3825
|
-
|
|
3908
|
+
const requestId = deps.newUuid();
|
|
3909
|
+
const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl), `hand MCP server ${serverName}'s OAuth callback to session ${sessionId}`);
|
|
3910
|
+
return result.ok ? { ...result, requestId } : result;
|
|
3911
|
+
}
|
|
3912
|
+
async function sendRcKeepAlive(deps, sessionId) {
|
|
3913
|
+
return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
|
|
3826
3914
|
}
|
|
3827
3915
|
|
|
3828
3916
|
// src/frontdoor/rcControl.ts
|
|
@@ -3834,6 +3922,25 @@ function answerJson(response, status, body) {
|
|
|
3834
3922
|
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
3835
3923
|
response.end(text);
|
|
3836
3924
|
}
|
|
3925
|
+
function isJsonObject(value) {
|
|
3926
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3927
|
+
}
|
|
3928
|
+
function bodyString(body, field) {
|
|
3929
|
+
const value = body[field];
|
|
3930
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
3931
|
+
}
|
|
3932
|
+
function bodyStringAllowingEmpty(body, field) {
|
|
3933
|
+
const value = body[field];
|
|
3934
|
+
return typeof value === "string" ? value : void 0;
|
|
3935
|
+
}
|
|
3936
|
+
function bodyBoolean(body, field) {
|
|
3937
|
+
const value = body[field];
|
|
3938
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3939
|
+
}
|
|
3940
|
+
function bodyPositiveInteger(body, field) {
|
|
3941
|
+
const value = body[field];
|
|
3942
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
3943
|
+
}
|
|
3837
3944
|
async function readBody(request4) {
|
|
3838
3945
|
return await new Promise((resolve, reject) => {
|
|
3839
3946
|
const chunks = [];
|
|
@@ -3861,9 +3968,127 @@ function createRcControlHandler(deps) {
|
|
|
3861
3968
|
const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
|
|
3862
3969
|
const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
|
|
3863
3970
|
const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
|
|
3864
|
-
const
|
|
3865
|
-
|
|
3866
|
-
|
|
3971
|
+
const controlWrites = [
|
|
3972
|
+
{
|
|
3973
|
+
name: "interrupt",
|
|
3974
|
+
path: "interrupt",
|
|
3975
|
+
read: (body) => {
|
|
3976
|
+
const session = bodyString(body, "session");
|
|
3977
|
+
return session === void 0 ? { error: "the interrupt body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.interrupt(session) };
|
|
3978
|
+
}
|
|
3979
|
+
},
|
|
3980
|
+
{
|
|
3981
|
+
name: "set-model",
|
|
3982
|
+
path: "set-model",
|
|
3983
|
+
read: (body) => {
|
|
3984
|
+
const session = bodyString(body, "session");
|
|
3985
|
+
const model = bodyString(body, "model");
|
|
3986
|
+
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) };
|
|
3987
|
+
}
|
|
3988
|
+
},
|
|
3989
|
+
{
|
|
3990
|
+
name: "set-permission-mode",
|
|
3991
|
+
path: "set-permission-mode",
|
|
3992
|
+
read: (body) => {
|
|
3993
|
+
const session = bodyString(body, "session");
|
|
3994
|
+
const mode = body.mode;
|
|
3995
|
+
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) };
|
|
3996
|
+
}
|
|
3997
|
+
},
|
|
3998
|
+
{
|
|
3999
|
+
name: "end-session",
|
|
4000
|
+
path: "end-session",
|
|
4001
|
+
read: (body) => {
|
|
4002
|
+
const session = bodyString(body, "session");
|
|
4003
|
+
const reason = "reason" in body ? bodyString(body, "reason") : void 0;
|
|
4004
|
+
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) };
|
|
4005
|
+
}
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
name: "get-usage",
|
|
4009
|
+
path: "get-usage",
|
|
4010
|
+
read: (body) => {
|
|
4011
|
+
const session = bodyString(body, "session");
|
|
4012
|
+
const skipBehaviors = "skipBehaviors" in body ? bodyBoolean(body, "skipBehaviors") : void 0;
|
|
4013
|
+
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) };
|
|
4014
|
+
}
|
|
4015
|
+
},
|
|
4016
|
+
{
|
|
4017
|
+
name: "get-context-usage",
|
|
4018
|
+
path: "get-context-usage",
|
|
4019
|
+
read: (body) => {
|
|
4020
|
+
const session = bodyString(body, "session");
|
|
4021
|
+
const detail = "detail" in body && isRcContextUsageDetail(body.detail) ? body.detail : void 0;
|
|
4022
|
+
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) };
|
|
4023
|
+
}
|
|
4024
|
+
},
|
|
4025
|
+
{
|
|
4026
|
+
name: "read-file",
|
|
4027
|
+
path: "read-file",
|
|
4028
|
+
read: (body) => {
|
|
4029
|
+
const session = bodyString(body, "session");
|
|
4030
|
+
const path32 = bodyString(body, "path");
|
|
4031
|
+
const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
|
|
4032
|
+
const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
|
|
4033
|
+
const refused2 = session === void 0 || path32 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
|
|
4034
|
+
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 } }) };
|
|
4035
|
+
}
|
|
4036
|
+
},
|
|
4037
|
+
{
|
|
4038
|
+
name: "file-suggestions",
|
|
4039
|
+
path: "file-suggestions",
|
|
4040
|
+
read: (body) => {
|
|
4041
|
+
const session = bodyString(body, "session");
|
|
4042
|
+
const query = bodyStringAllowingEmpty(body, "query");
|
|
4043
|
+
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) };
|
|
4044
|
+
}
|
|
4045
|
+
},
|
|
4046
|
+
{
|
|
4047
|
+
name: "keep-alive",
|
|
4048
|
+
path: "keep-alive",
|
|
4049
|
+
read: (body) => {
|
|
4050
|
+
const session = bodyString(body, "session");
|
|
4051
|
+
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) };
|
|
4052
|
+
}
|
|
4053
|
+
},
|
|
4054
|
+
{
|
|
4055
|
+
name: "mcp-status",
|
|
4056
|
+
path: "mcp-status",
|
|
4057
|
+
read: (body) => {
|
|
4058
|
+
const session = bodyString(body, "session");
|
|
4059
|
+
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) };
|
|
4060
|
+
}
|
|
4061
|
+
},
|
|
4062
|
+
{
|
|
4063
|
+
name: "mcp-reconnect",
|
|
4064
|
+
path: "mcp-reconnect",
|
|
4065
|
+
read: (body) => {
|
|
4066
|
+
const session = bodyString(body, "session");
|
|
4067
|
+
const serverName = bodyString(body, "serverName");
|
|
4068
|
+
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) };
|
|
4069
|
+
}
|
|
4070
|
+
},
|
|
4071
|
+
{
|
|
4072
|
+
name: "mcp-authenticate",
|
|
4073
|
+
path: "mcp-authenticate",
|
|
4074
|
+
read: (body) => {
|
|
4075
|
+
const session = bodyString(body, "session");
|
|
4076
|
+
const serverName = bodyString(body, "serverName");
|
|
4077
|
+
const redirectUri = bodyString(body, "redirectUri");
|
|
4078
|
+
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) };
|
|
4079
|
+
}
|
|
4080
|
+
},
|
|
4081
|
+
{
|
|
4082
|
+
name: "mcp-oauth-callback-url",
|
|
4083
|
+
path: "mcp-oauth-callback-url",
|
|
4084
|
+
read: (body) => {
|
|
4085
|
+
const session = bodyString(body, "session");
|
|
4086
|
+
const serverName = bodyString(body, "serverName");
|
|
4087
|
+
const callbackUrl = bodyString(body, "callbackUrl");
|
|
4088
|
+
return session === void 0 || serverName === void 0 || callbackUrl === void 0 ? { error: "the mcp-oauth-callback-url body must be JSON naming a non-empty session id, server name, and callback URL" } : { session, run: async () => await deps.mcpOAuthCallbackUrl(session, serverName, callbackUrl) };
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
];
|
|
3867
4092
|
return (request4, response) => {
|
|
3868
4093
|
const presented = request4.headers.authorization;
|
|
3869
4094
|
const bearerPrefix = "bearer ".length;
|
|
@@ -3948,73 +4173,33 @@ function createRcControlHandler(deps) {
|
|
|
3948
4173
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
3949
4174
|
return;
|
|
3950
4175
|
}
|
|
3951
|
-
|
|
3952
|
-
if (
|
|
3953
|
-
|
|
3954
|
-
return;
|
|
3955
|
-
}
|
|
3956
|
-
let parsed;
|
|
3957
|
-
try {
|
|
3958
|
-
parsed = JSON.parse(await readBody(request4));
|
|
3959
|
-
} catch {
|
|
3960
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
|
|
3961
|
-
return;
|
|
3962
|
-
}
|
|
3963
|
-
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
|
|
3964
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
|
|
3965
|
-
return;
|
|
3966
|
-
}
|
|
3967
|
-
const result = await deps.interrupt(parsed.session);
|
|
3968
|
-
if (result.ok) {
|
|
3969
|
-
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
|
|
3970
|
-
return;
|
|
4176
|
+
for (const route of controlWrites) {
|
|
4177
|
+
if (url.pathname !== `${CONTROL_PATH_PREFIX}/${route.path}`) {
|
|
4178
|
+
continue;
|
|
3971
4179
|
}
|
|
3972
|
-
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
3973
|
-
return;
|
|
3974
|
-
}
|
|
3975
|
-
if (url.pathname === setModelPath) {
|
|
3976
4180
|
if (request4.method !== "POST") {
|
|
3977
|
-
answerJson(response, HTTP_STATUS.methodNotAllowed, { error:
|
|
4181
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: `the ${route.name} operation is a POST` });
|
|
3978
4182
|
return;
|
|
3979
4183
|
}
|
|
3980
4184
|
let parsed;
|
|
3981
4185
|
try {
|
|
3982
4186
|
parsed = JSON.parse(await readBody(request4));
|
|
3983
4187
|
} catch {
|
|
3984
|
-
answerJson(response, HTTP_STATUS.badRequest, { error:
|
|
3985
|
-
return;
|
|
3986
|
-
}
|
|
3987
|
-
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
|
|
3988
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
|
|
4188
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body is not readable JSON within the control body cap` });
|
|
3989
4189
|
return;
|
|
3990
4190
|
}
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
|
|
3994
|
-
return;
|
|
3995
|
-
}
|
|
3996
|
-
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
3997
|
-
return;
|
|
3998
|
-
}
|
|
3999
|
-
if (url.pathname === setPermissionModePath) {
|
|
4000
|
-
if (request4.method !== "POST") {
|
|
4001
|
-
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
|
|
4191
|
+
if (!isJsonObject(parsed)) {
|
|
4192
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body must be a JSON object` });
|
|
4002
4193
|
return;
|
|
4003
4194
|
}
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
} catch {
|
|
4008
|
-
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
|
|
4195
|
+
const read = route.read(parsed);
|
|
4196
|
+
if ("error" in read) {
|
|
4197
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: read.error });
|
|
4009
4198
|
return;
|
|
4010
4199
|
}
|
|
4011
|
-
|
|
4012
|
-
answerJson(response, HTTP_STATUS.badRequest, { 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})` });
|
|
4013
|
-
return;
|
|
4014
|
-
}
|
|
4015
|
-
const result = await deps.setPermissionMode(parsed.session, parsed.mode);
|
|
4200
|
+
const result = await read.run();
|
|
4016
4201
|
if (result.ok) {
|
|
4017
|
-
answerJson(response, HTTP_STATUS.ok, { session:
|
|
4202
|
+
answerJson(response, HTTP_STATUS.ok, { session: read.session, ...result.requestId === void 0 ? {} : { request: result.requestId }, sequenceNums: result.sequenceNums });
|
|
4018
4203
|
return;
|
|
4019
4204
|
}
|
|
4020
4205
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
@@ -4132,6 +4317,19 @@ function sequenceNumsOfBody(body) {
|
|
|
4132
4317
|
}
|
|
4133
4318
|
return sequenceNums;
|
|
4134
4319
|
}
|
|
4320
|
+
function requestIdOfBody(body) {
|
|
4321
|
+
let parsed;
|
|
4322
|
+
try {
|
|
4323
|
+
parsed = JSON.parse(body);
|
|
4324
|
+
} catch {
|
|
4325
|
+
return void 0;
|
|
4326
|
+
}
|
|
4327
|
+
if (typeof parsed !== "object" || parsed === null || !("request" in parsed)) {
|
|
4328
|
+
return void 0;
|
|
4329
|
+
}
|
|
4330
|
+
const value = parsed.request;
|
|
4331
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
4332
|
+
}
|
|
4135
4333
|
function frontDoorRcControl(transport, token) {
|
|
4136
4334
|
const headers = { authorization: `Bearer ${token}` };
|
|
4137
4335
|
const listAnswerOf = async (path32, what) => {
|
|
@@ -4220,7 +4418,8 @@ function frontDoorRcControl(transport, token) {
|
|
|
4220
4418
|
if (sequenceNums === void 0) {
|
|
4221
4419
|
return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
|
|
4222
4420
|
}
|
|
4223
|
-
|
|
4421
|
+
const requestId = requestIdOfBody(answer.body);
|
|
4422
|
+
return requestId === void 0 ? { ok: true, sequenceNums } : { ok: true, sequenceNums, requestId };
|
|
4224
4423
|
}
|
|
4225
4424
|
return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
|
|
4226
4425
|
};
|
|
@@ -4237,7 +4436,21 @@ function frontDoorRcControl(transport, token) {
|
|
|
4237
4436
|
),
|
|
4238
4437
|
interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4239
4438
|
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}`),
|
|
4240
|
-
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}`)
|
|
4439
|
+
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}`),
|
|
4440
|
+
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}`),
|
|
4441
|
+
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}`),
|
|
4442
|
+
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}`),
|
|
4443
|
+
readFile: async (sessionId, path32, options) => await writeOf(
|
|
4444
|
+
`${CONTROL_PATH_PREFIX}/read-file`,
|
|
4445
|
+
JSON.stringify({ session: sessionId, path: path32, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
|
|
4446
|
+
(status, message) => `reading ${path32} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
|
|
4447
|
+
),
|
|
4448
|
+
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}`),
|
|
4449
|
+
keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
|
|
4450
|
+
mcpStatus: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-status`, JSON.stringify({ session: sessionId }), (status, message) => `requesting the MCP status of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4451
|
+
mcpReconnect: async (sessionId, serverName) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-reconnect`, JSON.stringify({ session: sessionId, serverName }), (status, message) => `reconnecting MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4452
|
+
mcpAuthenticate: async (sessionId, serverName, redirectUri) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-authenticate`, JSON.stringify({ session: sessionId, serverName, redirectUri }), (status, message) => `authenticating MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4453
|
+
mcpOAuthCallbackUrl: async (sessionId, serverName, callbackUrl) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-oauth-callback-url`, JSON.stringify({ session: sessionId, serverName, callbackUrl }), (status, message) => `handing MCP server ${serverName}'s OAuth callback to ${sessionId} failed (HTTP ${String(status)}): ${message}`)
|
|
4241
4454
|
};
|
|
4242
4455
|
}
|
|
4243
4456
|
|
|
@@ -4859,6 +5072,48 @@ var RcSetPermissionModeInputSchema = z4.strictObject({
|
|
|
4859
5072
|
session: z4.string().min(1),
|
|
4860
5073
|
mode: RcPermissionModeSchema
|
|
4861
5074
|
});
|
|
5075
|
+
var RcEndSessionInputSchema = z4.strictObject({
|
|
5076
|
+
session: z4.string().min(1),
|
|
5077
|
+
reason: z4.string().min(1).optional()
|
|
5078
|
+
});
|
|
5079
|
+
var RcGetUsageInputSchema = z4.strictObject({
|
|
5080
|
+
session: z4.string().min(1),
|
|
5081
|
+
skipBehaviors: z4.boolean().optional()
|
|
5082
|
+
});
|
|
5083
|
+
var RcGetContextUsageInputSchema = z4.strictObject({
|
|
5084
|
+
session: z4.string().min(1),
|
|
5085
|
+
detail: z4.enum(RC_CONTEXT_USAGE_DETAILS).optional()
|
|
5086
|
+
});
|
|
5087
|
+
var RcReadFileInputSchema = z4.strictObject({
|
|
5088
|
+
session: z4.string().min(1),
|
|
5089
|
+
path: z4.string().min(1),
|
|
5090
|
+
maxBytes: z4.number().int().positive().optional(),
|
|
5091
|
+
encoding: z4.enum(RC_READ_FILE_ENCODINGS).optional()
|
|
5092
|
+
});
|
|
5093
|
+
var RcFileSuggestionsInputSchema = z4.strictObject({
|
|
5094
|
+
session: z4.string().min(1),
|
|
5095
|
+
query: z4.string()
|
|
5096
|
+
});
|
|
5097
|
+
var RcKeepAliveInputSchema = z4.strictObject({
|
|
5098
|
+
session: z4.string().min(1)
|
|
5099
|
+
});
|
|
5100
|
+
var RcMcpStatusInputSchema = z4.strictObject({
|
|
5101
|
+
session: z4.string().min(1)
|
|
5102
|
+
});
|
|
5103
|
+
var RcMcpReconnectInputSchema = z4.strictObject({
|
|
5104
|
+
session: z4.string().min(1),
|
|
5105
|
+
serverName: z4.string().min(1)
|
|
5106
|
+
});
|
|
5107
|
+
var RcMcpAuthenticateInputSchema = z4.strictObject({
|
|
5108
|
+
session: z4.string().min(1),
|
|
5109
|
+
serverName: z4.string().min(1),
|
|
5110
|
+
redirectUri: z4.string().min(1)
|
|
5111
|
+
});
|
|
5112
|
+
var RcMcpOAuthCallbackUrlInputSchema = z4.strictObject({
|
|
5113
|
+
session: z4.string().min(1),
|
|
5114
|
+
serverName: z4.string().min(1),
|
|
5115
|
+
callbackUrl: z4.string().min(1)
|
|
5116
|
+
});
|
|
4862
5117
|
var RcListOutputSchema = z4.strictObject({ sessions: z4.readonly(z4.array(RcSessionSummarySchema)) });
|
|
4863
5118
|
var RcStatusOutputSchema = z4.strictObject({ statuses: z4.readonly(z4.array(RcSessionStatusSchema)) });
|
|
4864
5119
|
var RcPendingOutputSchema = z4.strictObject({ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema)) });
|
|
@@ -4866,6 +5121,11 @@ var RcWriteOutputSchema = z4.strictObject({
|
|
|
4866
5121
|
session: z4.string(),
|
|
4867
5122
|
sequenceNums: z4.readonly(z4.array(z4.number()))
|
|
4868
5123
|
});
|
|
5124
|
+
var RcControlWriteOutputSchema = z4.strictObject({
|
|
5125
|
+
session: z4.string(),
|
|
5126
|
+
request: z4.string(),
|
|
5127
|
+
sequenceNums: z4.readonly(z4.array(z4.number()))
|
|
5128
|
+
});
|
|
4869
5129
|
var RcAnswerOutputSchema = z4.strictObject({
|
|
4870
5130
|
session: z4.string(),
|
|
4871
5131
|
request: z4.string(),
|
|
@@ -4894,6 +5154,15 @@ function createRcApiRouter(deps) {
|
|
|
4894
5154
|
}
|
|
4895
5155
|
return result.sequenceNums;
|
|
4896
5156
|
};
|
|
5157
|
+
const deliveredControl = (sessionId, result) => {
|
|
5158
|
+
if (!result.ok) {
|
|
5159
|
+
throw new ORPCError("BAD_GATEWAY", { message: result.message });
|
|
5160
|
+
}
|
|
5161
|
+
if (result.requestId === void 0) {
|
|
5162
|
+
throw new 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" });
|
|
5163
|
+
}
|
|
5164
|
+
return { request: result.requestId, sequenceNums: result.sequenceNums };
|
|
5165
|
+
};
|
|
4897
5166
|
return {
|
|
4898
5167
|
rc: {
|
|
4899
5168
|
list: authed.output(RcListOutputSchema).handler(() => ({ sessions: deps.list() })),
|
|
@@ -4916,9 +5185,19 @@ function createRcApiRouter(deps) {
|
|
|
4916
5185
|
const decision = { approve: input.approve, message: input.approve || input.text === void 0 ? void 0 : input.text };
|
|
4917
5186
|
return { session: input.session, request: input.request, sequenceNums: delivered(input.session, await deps.answer(input.session, input.request, decision)) };
|
|
4918
5187
|
}),
|
|
4919
|
-
interrupt: authed.input(RcInterruptInputSchema).output(
|
|
4920
|
-
setModel: authed.input(RcSetModelInputSchema).output(
|
|
4921
|
-
setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(
|
|
5188
|
+
interrupt: authed.input(RcInterruptInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.interrupt(input.session)) })),
|
|
5189
|
+
setModel: authed.input(RcSetModelInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setModel(input.session, input.model)) })),
|
|
5190
|
+
setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
|
|
5191
|
+
endSession: authed.input(RcEndSessionInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.endSession(input.session, input.reason)) })),
|
|
5192
|
+
getUsage: authed.input(RcGetUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getUsage(input.session, input.skipBehaviors)) })),
|
|
5193
|
+
getContextUsage: authed.input(RcGetContextUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getContextUsage(input.session, input.detail)) })),
|
|
5194
|
+
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 } })) })),
|
|
5195
|
+
fileSuggestions: authed.input(RcFileSuggestionsInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.fileSuggestions(input.session, input.query)) })),
|
|
5196
|
+
keepAlive: authed.input(RcKeepAliveInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.keepAlive(input.session)) })),
|
|
5197
|
+
mcpStatus: authed.input(RcMcpStatusInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpStatus(input.session)) })),
|
|
5198
|
+
mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
|
|
5199
|
+
mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
|
|
5200
|
+
mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
|
|
4922
5201
|
subscribe: authed.input(RcSessionQuerySchema).output(eventIterator(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
|
|
4923
5202
|
const queue = [];
|
|
4924
5203
|
let wake;
|
|
@@ -11185,6 +11464,106 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
11185
11464
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
11186
11465
|
import path30 from "node:path";
|
|
11187
11466
|
|
|
11467
|
+
// package.json
|
|
11468
|
+
var package_default = {
|
|
11469
|
+
name: "agent-shim",
|
|
11470
|
+
version: "8.24.0",
|
|
11471
|
+
description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
11472
|
+
license: "MIT",
|
|
11473
|
+
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
11474
|
+
repository: {
|
|
11475
|
+
type: "git",
|
|
11476
|
+
url: "git+https://github.com/ExaDev/agent-shim.git"
|
|
11477
|
+
},
|
|
11478
|
+
homepage: "https://github.com/ExaDev/agent-shim#readme",
|
|
11479
|
+
bugs: "https://github.com/ExaDev/agent-shim/issues",
|
|
11480
|
+
keywords: [
|
|
11481
|
+
"claude-code",
|
|
11482
|
+
"claude",
|
|
11483
|
+
"cli"
|
|
11484
|
+
],
|
|
11485
|
+
packageManager: "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
|
|
11486
|
+
type: "module",
|
|
11487
|
+
engines: {
|
|
11488
|
+
node: "^22.18 || >=24"
|
|
11489
|
+
},
|
|
11490
|
+
bin: {
|
|
11491
|
+
"agent-shim": "dist/cli.cjs",
|
|
11492
|
+
"claude-use": "dist/cli.cjs"
|
|
11493
|
+
},
|
|
11494
|
+
files: [
|
|
11495
|
+
"dist/cli.cjs",
|
|
11496
|
+
"dist/index.mjs",
|
|
11497
|
+
"dist/index.cjs",
|
|
11498
|
+
"dist/types"
|
|
11499
|
+
],
|
|
11500
|
+
scripts: {
|
|
11501
|
+
prepare: "husky && node scripts/build.mts --bundle-only",
|
|
11502
|
+
typecheck: "turbo run _typecheck",
|
|
11503
|
+
_typecheck: "tsc --noEmit",
|
|
11504
|
+
lint: "turbo run _lint",
|
|
11505
|
+
_lint: "eslint . --cache --cache-location .eslintcache --max-warnings 0",
|
|
11506
|
+
test: "turbo run _test",
|
|
11507
|
+
_test: "vitest run",
|
|
11508
|
+
"test:coverage": "turbo run _test:coverage",
|
|
11509
|
+
"_test:coverage": "vitest run --coverage",
|
|
11510
|
+
knip: "turbo run _knip",
|
|
11511
|
+
_knip: "knip --cache",
|
|
11512
|
+
build: "turbo run _build",
|
|
11513
|
+
_build: "node scripts/build.mts",
|
|
11514
|
+
"build:bundle": "node scripts/build.mts --bundle-only",
|
|
11515
|
+
prepublishOnly: "pnpm build:bundle",
|
|
11516
|
+
schema: "node scripts/gen-schema.mts"
|
|
11517
|
+
},
|
|
11518
|
+
dependencies: {
|
|
11519
|
+
"@clack/prompts": "^1.8.0",
|
|
11520
|
+
"@orpc/client": "1.15.4",
|
|
11521
|
+
"@orpc/server": "1.15.4",
|
|
11522
|
+
commander: "^15.0.0",
|
|
11523
|
+
cosmiconfig: "^10.0.1",
|
|
11524
|
+
picomatch: "^4.0.7",
|
|
11525
|
+
undici: "7.29.1",
|
|
11526
|
+
zod: "^4.6.1"
|
|
11527
|
+
},
|
|
11528
|
+
devDependencies: {
|
|
11529
|
+
"@commitlint/cli": "^21.2.2",
|
|
11530
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
11531
|
+
"@exadev/eslint-config": "2.12.1",
|
|
11532
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
11533
|
+
"@semantic-release/git": "^11.0.1",
|
|
11534
|
+
"@semantic-release/npm": "^13.1.5",
|
|
11535
|
+
"@types/node": "^26.5.1",
|
|
11536
|
+
"@types/picomatch": "^4.0.3",
|
|
11537
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
11538
|
+
"conventional-changelog-conventionalcommits": "^10.4.0",
|
|
11539
|
+
esbuild: "^0.28.2",
|
|
11540
|
+
eslint: "^10.10.0",
|
|
11541
|
+
globals: "^17.12.0",
|
|
11542
|
+
husky: "^9.1.7",
|
|
11543
|
+
jiti: "^2.7.0",
|
|
11544
|
+
knip: "^6.35.1",
|
|
11545
|
+
"lint-staged": "^17.5.0",
|
|
11546
|
+
rollup: "4.64.0",
|
|
11547
|
+
"rollup-plugin-dts": "6.5.1",
|
|
11548
|
+
"semantic-release": "^25.0.9",
|
|
11549
|
+
turbo: "^2.11.5",
|
|
11550
|
+
typescript: "^6.0.3",
|
|
11551
|
+
"typescript-eslint": "^8.70.0",
|
|
11552
|
+
vitest: "^4.1.11"
|
|
11553
|
+
},
|
|
11554
|
+
main: "dist/index.cjs",
|
|
11555
|
+
module: "dist/index.mjs",
|
|
11556
|
+
types: "dist/types/index.d.ts",
|
|
11557
|
+
exports: {
|
|
11558
|
+
".": {
|
|
11559
|
+
types: "./dist/types/index.d.ts",
|
|
11560
|
+
import: "./dist/index.mjs",
|
|
11561
|
+
require: "./dist/index.cjs"
|
|
11562
|
+
},
|
|
11563
|
+
"./package.json": "./package.json"
|
|
11564
|
+
}
|
|
11565
|
+
};
|
|
11566
|
+
|
|
11188
11567
|
// src/launcher/argv.ts
|
|
11189
11568
|
var ConflictingIdentityError = class extends UsageError {
|
|
11190
11569
|
constructor(positional, flag) {
|
|
@@ -11657,6 +12036,88 @@ function spawnClaude(params) {
|
|
|
11657
12036
|
return params.proc.exit(exitCodeFor(result));
|
|
11658
12037
|
}
|
|
11659
12038
|
|
|
12039
|
+
// src/update/update.ts
|
|
12040
|
+
var LATEST_RELEASE_URL = "https://github.com/ExaDev/agent-shim/releases/latest";
|
|
12041
|
+
function compareVersions3(left, right) {
|
|
12042
|
+
const parse = (version) => {
|
|
12043
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
12044
|
+
if (match === null) {
|
|
12045
|
+
throw new Error(`not a plain release version: ${version}`);
|
|
12046
|
+
}
|
|
12047
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
12048
|
+
};
|
|
12049
|
+
const leftParts = parse(left);
|
|
12050
|
+
const rightParts = parse(right);
|
|
12051
|
+
return leftParts.map((part, index) => part - (rightParts[index] ?? 0)).find((difference) => difference !== 0) ?? 0;
|
|
12052
|
+
}
|
|
12053
|
+
function releaseTagFromUrl(url) {
|
|
12054
|
+
const segment = url.split("?")[0]?.split("/").at(-1);
|
|
12055
|
+
const candidate = segment?.startsWith("v") === true ? segment.slice(1) : void 0;
|
|
12056
|
+
return candidate !== void 0 && /^\d+\.\d+\.\d+$/.test(candidate) ? candidate : void 0;
|
|
12057
|
+
}
|
|
12058
|
+
|
|
12059
|
+
// src/update/launchHook.ts
|
|
12060
|
+
var UPDATE_CHECK_COOLDOWN_MS = 864e5;
|
|
12061
|
+
function resolveUpdateMode(globalConfig) {
|
|
12062
|
+
return globalConfig?.update?.mode ?? "off";
|
|
12063
|
+
}
|
|
12064
|
+
function updateCheckDue(contents, nowMs, cooldownMs = UPDATE_CHECK_COOLDOWN_MS) {
|
|
12065
|
+
if (contents === void 0) {
|
|
12066
|
+
return true;
|
|
12067
|
+
}
|
|
12068
|
+
const checkedAt = Date.parse(contents.trim());
|
|
12069
|
+
if (Number.isNaN(checkedAt)) {
|
|
12070
|
+
return true;
|
|
12071
|
+
}
|
|
12072
|
+
const age = nowMs - checkedAt;
|
|
12073
|
+
return age < 0 || age >= cooldownMs;
|
|
12074
|
+
}
|
|
12075
|
+
var NOOP_HANDLE = { markChildStarted: () => void 0 };
|
|
12076
|
+
function updateNotifyLine(currentVersion, latest) {
|
|
12077
|
+
return `agent-shim: update available: ${currentVersion} -> ${latest} (run \`agent-shim update\` to install it)`;
|
|
12078
|
+
}
|
|
12079
|
+
function runLaunchUpdateCheck(params) {
|
|
12080
|
+
const { mode, checkPath, currentVersion, port } = params;
|
|
12081
|
+
if (mode === "off" || port === void 0) {
|
|
12082
|
+
return NOOP_HANDLE;
|
|
12083
|
+
}
|
|
12084
|
+
let childStarted = false;
|
|
12085
|
+
try {
|
|
12086
|
+
if (!updateCheckDue(port.readStamp(checkPath), port.now())) {
|
|
12087
|
+
return NOOP_HANDLE;
|
|
12088
|
+
}
|
|
12089
|
+
port.writeStamp(checkPath, new Date(port.now()).toISOString());
|
|
12090
|
+
} catch {
|
|
12091
|
+
return NOOP_HANDLE;
|
|
12092
|
+
}
|
|
12093
|
+
const answer = (url) => {
|
|
12094
|
+
try {
|
|
12095
|
+
const latest = releaseTagFromUrl(url);
|
|
12096
|
+
if (latest === void 0 || compareVersions3(latest, currentVersion) <= 0) {
|
|
12097
|
+
return;
|
|
12098
|
+
}
|
|
12099
|
+
if (mode === "auto") {
|
|
12100
|
+
port.spawnDetached(["update"]);
|
|
12101
|
+
}
|
|
12102
|
+
if (!childStarted) {
|
|
12103
|
+
port.writeErr(updateNotifyLine(currentVersion, latest));
|
|
12104
|
+
}
|
|
12105
|
+
} catch {
|
|
12106
|
+
}
|
|
12107
|
+
};
|
|
12108
|
+
port.effectiveUrl(LATEST_RELEASE_URL).then(
|
|
12109
|
+
(url) => {
|
|
12110
|
+
answer(url);
|
|
12111
|
+
},
|
|
12112
|
+
() => void 0
|
|
12113
|
+
);
|
|
12114
|
+
return {
|
|
12115
|
+
markChildStarted: () => {
|
|
12116
|
+
childStarted = true;
|
|
12117
|
+
}
|
|
12118
|
+
};
|
|
12119
|
+
}
|
|
12120
|
+
|
|
11660
12121
|
// src/launcher.ts
|
|
11661
12122
|
function resolveIdentityCredential(identity, credential, env, port) {
|
|
11662
12123
|
if (port === void 0) {
|
|
@@ -11822,6 +12283,12 @@ function prepareLaunch(params) {
|
|
|
11822
12283
|
log.info(
|
|
11823
12284
|
`agent-shim: identity ${identityDecision.name ?? "(none)"} (${identityDecision.source}${poolExplanation === void 0 ? "" : `, ${poolExplanation}`}), config profile ${configProfileDecision.name ?? "(none)"} (${configProfileDecision.source})` + (resolvedProvider === void 0 ? "" : `, provider ${resolvedProvider.name} (credential ${resolvedProvider.credential.target} from ${describeSource(resolvedProvider.credential.source)})`) + (identityCredential === void 0 ? "" : `, identity credential ${identityCredential.target} from ${describeSource(identityCredential.source)}`)
|
|
11824
12285
|
);
|
|
12286
|
+
const updateCheck = runLaunchUpdateCheck({
|
|
12287
|
+
mode: params.updateMode ?? "off",
|
|
12288
|
+
checkPath: path30.join(paths.root, "update.check"),
|
|
12289
|
+
currentVersion: package_default.version,
|
|
12290
|
+
port: params.update
|
|
12291
|
+
});
|
|
11825
12292
|
let cascadeLaunch;
|
|
11826
12293
|
if (farmContext !== void 0) {
|
|
11827
12294
|
const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
|
|
@@ -11927,11 +12394,12 @@ function prepareLaunch(params) {
|
|
|
11927
12394
|
registered();
|
|
11928
12395
|
}
|
|
11929
12396
|
};
|
|
11930
|
-
return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
|
|
12397
|
+
return { bin: discovered.path, args: finalArgv, env: finalEnv, release, markChildStarted: updateCheck.markChildStarted };
|
|
11931
12398
|
}
|
|
11932
12399
|
function runLauncher(params) {
|
|
11933
12400
|
const plan = prepareLaunch(params);
|
|
11934
12401
|
try {
|
|
12402
|
+
plan.markChildStarted();
|
|
11935
12403
|
spawnClaude({ bin: plan.bin, args: plan.args, env: plan.env, spawn: params.spawn, proc: params.proc, beforeExit: plan.release });
|
|
11936
12404
|
} finally {
|
|
11937
12405
|
plan.release();
|
|
@@ -12094,6 +12562,7 @@ function buildFarmRuntime(paths, cwd) {
|
|
|
12094
12562
|
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
12095
12563
|
const selections = readDirectorySelections(loaded);
|
|
12096
12564
|
const git = resolveGitBranch(realRunPort, cwd);
|
|
12565
|
+
const updateMode = resolveUpdateMode(loaded.globalConfig);
|
|
12097
12566
|
return {
|
|
12098
12567
|
runtime: {
|
|
12099
12568
|
fs: realFarmFs,
|
|
@@ -12119,7 +12588,8 @@ function buildFarmRuntime(paths, cwd) {
|
|
|
12119
12588
|
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
12120
12589
|
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
|
12121
12590
|
...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
|
|
12122
|
-
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
|
|
12591
|
+
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools },
|
|
12592
|
+
...updateMode === "off" ? {} : { updateMode }
|
|
12123
12593
|
};
|
|
12124
12594
|
}
|
|
12125
12595
|
function realClaudeBinaryResolver(paths) {
|
|
@@ -12141,6 +12611,7 @@ function realPrepareLaunchParams(paths, options) {
|
|
|
12141
12611
|
...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
|
|
12142
12612
|
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
|
|
12143
12613
|
...farm.pools === void 0 ? {} : { pools: farm.pools },
|
|
12614
|
+
...farm.updateMode === void 0 ? {} : { updateMode: farm.updateMode },
|
|
12144
12615
|
...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
|
|
12145
12616
|
};
|
|
12146
12617
|
}
|
|
@@ -12238,10 +12709,12 @@ export {
|
|
|
12238
12709
|
ProviderKindMismatchError,
|
|
12239
12710
|
ProviderNotFoundError,
|
|
12240
12711
|
ProviderSchema,
|
|
12712
|
+
RC_CONTEXT_USAGE_DETAILS,
|
|
12241
12713
|
RC_IDLE_EXPIRY_MS,
|
|
12242
12714
|
RC_ORPC_PATH_PREFIX,
|
|
12243
12715
|
RC_PENDING_DEADLINE_MS,
|
|
12244
12716
|
RC_PERMISSION_MODES,
|
|
12717
|
+
RC_READ_FILE_ENCODINGS,
|
|
12245
12718
|
RC_REQUEST_PARSE_CAP_BYTES,
|
|
12246
12719
|
RC_SELF_HOST_ENV,
|
|
12247
12720
|
RC_SELF_HOST_KEEPALIVE_MS,
|
|
@@ -12272,11 +12745,23 @@ export {
|
|
|
12272
12745
|
addPool,
|
|
12273
12746
|
addProvider,
|
|
12274
12747
|
answerRcControlRequest,
|
|
12748
|
+
authenticateRcSessionMcpServer,
|
|
12275
12749
|
buildEntryFacts,
|
|
12276
12750
|
buildLayoutPaths,
|
|
12751
|
+
buildRcControlRequestPayload,
|
|
12277
12752
|
buildRcControlResponsePayload,
|
|
12753
|
+
buildRcEndSessionPayload,
|
|
12278
12754
|
buildRcEventWriteBody,
|
|
12755
|
+
buildRcFileSuggestionsPayload,
|
|
12756
|
+
buildRcGetContextUsagePayload,
|
|
12757
|
+
buildRcGetUsagePayload,
|
|
12279
12758
|
buildRcInterruptPayload,
|
|
12759
|
+
buildRcKeepAlivePayload,
|
|
12760
|
+
buildRcMcpAuthenticatePayload,
|
|
12761
|
+
buildRcMcpOAuthCallbackUrlPayload,
|
|
12762
|
+
buildRcMcpReconnectPayload,
|
|
12763
|
+
buildRcMcpStatusPayload,
|
|
12764
|
+
buildRcReadFilePayload,
|
|
12280
12765
|
buildRcSetModelPayload,
|
|
12281
12766
|
buildRcSetPermissionModePayload,
|
|
12282
12767
|
buildRcUserMessagePayload,
|
|
@@ -12309,6 +12794,7 @@ export {
|
|
|
12309
12794
|
doorApiAuth,
|
|
12310
12795
|
doorApiNodeHandlerOf,
|
|
12311
12796
|
effectiveWindow,
|
|
12797
|
+
endRcSession,
|
|
12312
12798
|
ensureCa,
|
|
12313
12799
|
ensureFrontDoor,
|
|
12314
12800
|
ensureHeadroom,
|
|
@@ -12325,6 +12811,9 @@ export {
|
|
|
12325
12811
|
frontDoorRcApiClient,
|
|
12326
12812
|
frontDoorRcControl,
|
|
12327
12813
|
generateCa,
|
|
12814
|
+
getRcSessionContextUsage,
|
|
12815
|
+
getRcSessionMcpStatus,
|
|
12816
|
+
getRcSessionUsage,
|
|
12328
12817
|
headroomSocketTarget,
|
|
12329
12818
|
identityExists,
|
|
12330
12819
|
injectRcUserMessage,
|
|
@@ -12332,7 +12821,9 @@ export {
|
|
|
12332
12821
|
isIdentityDirectoryName,
|
|
12333
12822
|
isInterceptedHost,
|
|
12334
12823
|
isPrivateAddress,
|
|
12824
|
+
isRcContextUsageDetail,
|
|
12335
12825
|
isRcPermissionMode,
|
|
12826
|
+
isRcReadFileEncoding,
|
|
12336
12827
|
lateRcEventDial,
|
|
12337
12828
|
lateRcStreamDial,
|
|
12338
12829
|
listDirectoryRules,
|
|
@@ -12366,6 +12857,7 @@ export {
|
|
|
12366
12857
|
readProfile,
|
|
12367
12858
|
readProvider,
|
|
12368
12859
|
readRcSelfHostRecord,
|
|
12860
|
+
readRcSessionFile,
|
|
12369
12861
|
readUsageSnapshot,
|
|
12370
12862
|
realConnectCertStore,
|
|
12371
12863
|
realConnectEffects,
|
|
@@ -12373,6 +12865,7 @@ export {
|
|
|
12373
12865
|
realRcEventDial,
|
|
12374
12866
|
realRcStreamDial,
|
|
12375
12867
|
realRcWebFetch,
|
|
12868
|
+
reconnectRcSessionMcpServer,
|
|
12376
12869
|
recoverFarm,
|
|
12377
12870
|
recoveryDiagnostics,
|
|
12378
12871
|
removeDirectoryRule,
|
|
@@ -12392,6 +12885,7 @@ export {
|
|
|
12392
12885
|
runFrontDoorSupervisor,
|
|
12393
12886
|
runLauncher,
|
|
12394
12887
|
runSupervisor,
|
|
12888
|
+
sendRcKeepAlive,
|
|
12395
12889
|
servedByPipeline,
|
|
12396
12890
|
setAllowAmbientCredential,
|
|
12397
12891
|
setDefaultConfigProfile,
|
|
@@ -12407,6 +12901,8 @@ export {
|
|
|
12407
12901
|
snapshotPath,
|
|
12408
12902
|
spawnClaude,
|
|
12409
12903
|
startConnectServer,
|
|
12904
|
+
submitRcSessionMcpOAuthCallbackUrl,
|
|
12905
|
+
suggestRcSessionFiles,
|
|
12410
12906
|
topLevelNames,
|
|
12411
12907
|
updateDirectoryRule,
|
|
12412
12908
|
updateProvider,
|