agent-shim 8.1.0 → 8.3.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 +2 -0
- package/dist/cli.cjs +1186 -631
- package/dist/index.cjs +592 -39
- package/dist/index.mjs +569 -30
- package/dist/types/index.d.ts +231 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2449,6 +2449,7 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
|
|
|
2449
2449
|
}
|
|
2450
2450
|
|
|
2451
2451
|
// src/frontdoor/capability.ts
|
|
2452
|
+
import { createHash as createHash2, timingSafeEqual } from "node:crypto";
|
|
2452
2453
|
var CONNECT_PROXY_USER = "agent-shim";
|
|
2453
2454
|
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2454
2455
|
var BASIC_SCHEME = "basic";
|
|
@@ -2469,6 +2470,18 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2469
2470
|
const password = userPass.slice(colon + 1);
|
|
2470
2471
|
return password === "" ? void 0 : password;
|
|
2471
2472
|
}
|
|
2473
|
+
function digest(token) {
|
|
2474
|
+
return createHash2("sha256").update(token, "utf8").digest();
|
|
2475
|
+
}
|
|
2476
|
+
function isLiveCapability(presented, live) {
|
|
2477
|
+
const presentedDigest = digest(presented);
|
|
2478
|
+
let matched = false;
|
|
2479
|
+
for (const token of live) {
|
|
2480
|
+
const equal = timingSafeEqual(presentedDigest, digest(token));
|
|
2481
|
+
matched = equal || matched;
|
|
2482
|
+
}
|
|
2483
|
+
return matched;
|
|
2484
|
+
}
|
|
2472
2485
|
|
|
2473
2486
|
// src/frontdoor/route.ts
|
|
2474
2487
|
var IDENTITY_HEADER = "x-agent-shim-identity";
|
|
@@ -2779,14 +2792,14 @@ function caParses(ca) {
|
|
|
2779
2792
|
var TAP_ALPN_PROTOCOLS = ["h2", "http/1.1"];
|
|
2780
2793
|
async function startConnectServer(config, effects, preferredPort) {
|
|
2781
2794
|
const tunnelTokens = /* @__PURE__ */ new WeakMap();
|
|
2782
|
-
const onUpgrade = (host) => (
|
|
2783
|
-
config.capture?.upgrade(
|
|
2795
|
+
const onUpgrade = (host) => (request4, socket, head) => {
|
|
2796
|
+
config.capture?.upgrade(request4);
|
|
2784
2797
|
if (head.length > 0) {
|
|
2785
2798
|
socket.pause();
|
|
2786
2799
|
socket.unshift(head);
|
|
2787
2800
|
}
|
|
2788
|
-
const headText = `${
|
|
2789
|
-
${
|
|
2801
|
+
const headText = `${request4.method ?? "GET"} ${request4.url ?? "/"} HTTP/1.1\r
|
|
2802
|
+
${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `${accumulated}${value}: ` : `${accumulated}${value}\r
|
|
2790
2803
|
`, "")}\r
|
|
2791
2804
|
`;
|
|
2792
2805
|
pumpTapSession(socket, async (clientAlpn) => await effects.connectTlsUpstream(host, HTTPS_PORT, clientAlpn), config.capture?.tapStream?.(host), Buffer.from(headText, "utf8"));
|
|
@@ -2799,16 +2812,16 @@ ${request3.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
|
|
|
2799
2812
|
const tlsAcceptor2 = effects.createTlsAcceptor(config.leafFor(host), serveSecure2, TAP_ALPN_PROTOCOLS);
|
|
2800
2813
|
return { host, serveSecure: serveSecure2, tlsAcceptor: tlsAcceptor2 };
|
|
2801
2814
|
}
|
|
2802
|
-
const handler = (
|
|
2803
|
-
if (host === config.routedHost && servedByPipeline(
|
|
2804
|
-
const tunnelToken = tunnelTokens.get(
|
|
2805
|
-
if (tunnelToken !== void 0 &&
|
|
2806
|
-
|
|
2815
|
+
const handler = (request4, response) => {
|
|
2816
|
+
if (host === config.routedHost && servedByPipeline(request4.url)) {
|
|
2817
|
+
const tunnelToken = tunnelTokens.get(request4.socket);
|
|
2818
|
+
if (tunnelToken !== void 0 && request4.headers[AUTH_HEADER] === void 0) {
|
|
2819
|
+
request4.headers[AUTH_HEADER] = tunnelToken;
|
|
2807
2820
|
}
|
|
2808
|
-
config.serveRouted(
|
|
2821
|
+
config.serveRouted(request4, response);
|
|
2809
2822
|
return;
|
|
2810
2823
|
}
|
|
2811
|
-
effects.forwardHttp(config.upstreamFor(host),
|
|
2824
|
+
effects.forwardHttp(config.upstreamFor(host), request4, response, config.capture?.observePassthrough(request4));
|
|
2812
2825
|
};
|
|
2813
2826
|
const httpSession = effects.createHttpSession(handler, onUpgrade(host));
|
|
2814
2827
|
const serveSecure = (secure, capability) => {
|
|
@@ -2850,7 +2863,7 @@ ${request3.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
|
|
|
2850
2863
|
}
|
|
2851
2864
|
};
|
|
2852
2865
|
}
|
|
2853
|
-
function createConnectionLedger(limits,
|
|
2866
|
+
function createConnectionLedger(limits, isLiveCapability2) {
|
|
2854
2867
|
const pending = /* @__PURE__ */ new Map();
|
|
2855
2868
|
const authenticated = /* @__PURE__ */ new Map();
|
|
2856
2869
|
let tunnels = 0;
|
|
@@ -2863,7 +2876,7 @@ function createConnectionLedger(limits, isLiveCapability) {
|
|
|
2863
2876
|
};
|
|
2864
2877
|
const revalidation = setInterval(() => {
|
|
2865
2878
|
for (const [capability, sockets] of authenticated) {
|
|
2866
|
-
if (!
|
|
2879
|
+
if (!isLiveCapability2(capability)) {
|
|
2867
2880
|
for (const socket of sockets) {
|
|
2868
2881
|
socket.destroy();
|
|
2869
2882
|
}
|
|
@@ -3105,6 +3118,40 @@ var ExemptTlsAgent = class extends https.Agent {
|
|
|
3105
3118
|
return tls.connect(tlsOptions);
|
|
3106
3119
|
}
|
|
3107
3120
|
};
|
|
3121
|
+
function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
3122
|
+
const agent = new ExemptTlsAgent();
|
|
3123
|
+
return {
|
|
3124
|
+
writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
|
|
3125
|
+
const options = {
|
|
3126
|
+
host: target.host,
|
|
3127
|
+
port: target.port,
|
|
3128
|
+
method: "POST",
|
|
3129
|
+
path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
|
|
3130
|
+
// 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.
|
|
3131
|
+
headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
|
|
3132
|
+
agent
|
|
3133
|
+
};
|
|
3134
|
+
options.servername = target.servername ?? target.host;
|
|
3135
|
+
if (target.ca !== void 0) {
|
|
3136
|
+
options.ca = [...target.ca];
|
|
3137
|
+
}
|
|
3138
|
+
if (target.rejectUnauthorized !== void 0) {
|
|
3139
|
+
options.rejectUnauthorized = target.rejectUnauthorized;
|
|
3140
|
+
}
|
|
3141
|
+
const request4 = https.request(options, (response) => {
|
|
3142
|
+
const chunks = [];
|
|
3143
|
+
response.on("data", (chunk) => {
|
|
3144
|
+
chunks.push(chunk);
|
|
3145
|
+
});
|
|
3146
|
+
response.on("end", () => {
|
|
3147
|
+
resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
|
|
3148
|
+
});
|
|
3149
|
+
});
|
|
3150
|
+
request4.once("error", reject);
|
|
3151
|
+
request4.end(body);
|
|
3152
|
+
})
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3108
3155
|
function realConnectEffects() {
|
|
3109
3156
|
const plainAgent = new ExemptHttpAgent();
|
|
3110
3157
|
const tlsAgent = new ExemptTlsAgent();
|
|
@@ -3210,8 +3257,8 @@ function realConnectEffects() {
|
|
|
3210
3257
|
socket.destroy();
|
|
3211
3258
|
});
|
|
3212
3259
|
if (onUpgrade !== void 0) {
|
|
3213
|
-
server.on("upgrade", (
|
|
3214
|
-
onUpgrade(
|
|
3260
|
+
server.on("upgrade", (request4, socket, head) => {
|
|
3261
|
+
onUpgrade(request4, socket, head);
|
|
3215
3262
|
});
|
|
3216
3263
|
}
|
|
3217
3264
|
return {
|
|
@@ -3250,14 +3297,14 @@ function realConnectEffects() {
|
|
|
3250
3297
|
});
|
|
3251
3298
|
socket.once("error", reject);
|
|
3252
3299
|
}),
|
|
3253
|
-
forwardHttp: (target,
|
|
3254
|
-
const headers = forwardableHeaders(
|
|
3300
|
+
forwardHttp: (target, request4, response, observer) => {
|
|
3301
|
+
const headers = forwardableHeaders(request4.headers);
|
|
3255
3302
|
const options = {
|
|
3256
3303
|
host: target.host,
|
|
3257
3304
|
port: target.port,
|
|
3258
3305
|
lookup: realAddressLookup,
|
|
3259
|
-
method:
|
|
3260
|
-
path:
|
|
3306
|
+
method: request4.method,
|
|
3307
|
+
path: request4.url,
|
|
3261
3308
|
headers,
|
|
3262
3309
|
agent: target.tls ? tlsAgent : plainAgent
|
|
3263
3310
|
};
|
|
@@ -3292,15 +3339,15 @@ function realConnectEffects() {
|
|
|
3292
3339
|
response.end(body);
|
|
3293
3340
|
});
|
|
3294
3341
|
if (observer !== void 0) {
|
|
3295
|
-
|
|
3342
|
+
request4.on("data", (chunk) => {
|
|
3296
3343
|
observer.onRequestChunk(chunk);
|
|
3297
3344
|
});
|
|
3298
3345
|
response.on("close", () => {
|
|
3299
3346
|
observer.onEnd();
|
|
3300
3347
|
});
|
|
3301
3348
|
}
|
|
3302
|
-
|
|
3303
|
-
|
|
3349
|
+
request4.pipe(upstream);
|
|
3350
|
+
request4.socket.setNoDelay(true);
|
|
3304
3351
|
upstream.on("socket", (socket) => {
|
|
3305
3352
|
socket.setNoDelay(true);
|
|
3306
3353
|
});
|
|
@@ -3308,11 +3355,488 @@ function realConnectEffects() {
|
|
|
3308
3355
|
};
|
|
3309
3356
|
}
|
|
3310
3357
|
|
|
3358
|
+
// src/codex/http.ts
|
|
3359
|
+
var HTTP_STATUS = {
|
|
3360
|
+
ok: 200,
|
|
3361
|
+
badRequest: 400,
|
|
3362
|
+
unauthorized: 401,
|
|
3363
|
+
forbidden: 403,
|
|
3364
|
+
notFound: 404,
|
|
3365
|
+
methodNotAllowed: 405,
|
|
3366
|
+
payloadTooLarge: 413,
|
|
3367
|
+
tooManyRequests: 429,
|
|
3368
|
+
/** Not sent to anyone: the client is gone. Recorded for a response abandoned because the client disconnected, following the convention nginx uses for the same case. */
|
|
3369
|
+
clientClosedRequest: 499,
|
|
3370
|
+
internalServerError: 500,
|
|
3371
|
+
badGateway: 502
|
|
3372
|
+
};
|
|
3373
|
+
|
|
3374
|
+
// src/frontdoor/rcSessions.ts
|
|
3375
|
+
var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
|
|
3376
|
+
var RC_SESSION_ID_PREFIX = "cse_";
|
|
3377
|
+
var RC_IDLE_EXPIRY_MS = 45e3;
|
|
3378
|
+
var RC_CREATE_RESPONSE_CAP_BYTES = 8192;
|
|
3379
|
+
var CONFLICT_REASON_HEADER = "x-ccr-conflict-reason";
|
|
3380
|
+
var SUCCESS_STATUS_MAX_EXCLUSIVE = 300;
|
|
3381
|
+
function isSuccessful(status) {
|
|
3382
|
+
return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE;
|
|
3383
|
+
}
|
|
3384
|
+
function singleHeader(headers, name) {
|
|
3385
|
+
const value = headers[name];
|
|
3386
|
+
return typeof value === "string" ? value : void 0;
|
|
3387
|
+
}
|
|
3388
|
+
function sessionCreateId(body) {
|
|
3389
|
+
let parsed;
|
|
3390
|
+
try {
|
|
3391
|
+
parsed = JSON.parse(body);
|
|
3392
|
+
} catch {
|
|
3393
|
+
return void 0;
|
|
3394
|
+
}
|
|
3395
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed)) {
|
|
3396
|
+
return void 0;
|
|
3397
|
+
}
|
|
3398
|
+
const session = parsed.session;
|
|
3399
|
+
if (typeof session !== "object" || session === null || !("id" in session)) {
|
|
3400
|
+
return void 0;
|
|
3401
|
+
}
|
|
3402
|
+
const id = session.id;
|
|
3403
|
+
return typeof id === "string" && id.startsWith(RC_SESSION_ID_PREFIX) ? id : void 0;
|
|
3404
|
+
}
|
|
3405
|
+
function createRcSessionTracker(deps) {
|
|
3406
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3407
|
+
const expire = (id) => {
|
|
3408
|
+
entries.delete(id);
|
|
3409
|
+
};
|
|
3410
|
+
const sweep = () => {
|
|
3411
|
+
for (const [id, entry] of entries) {
|
|
3412
|
+
if (deps.now() - entry.lastSeenAt >= deps.idleMs) {
|
|
3413
|
+
expire(id);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
};
|
|
3417
|
+
const timer = setInterval(sweep, deps.idleMs);
|
|
3418
|
+
timer.unref();
|
|
3419
|
+
const observeExchange = (request4) => {
|
|
3420
|
+
const method = (request4.method ?? "").toUpperCase();
|
|
3421
|
+
const path29 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
3422
|
+
if (!path29.startsWith(RC_SESSIONS_PATH_PREFIX)) {
|
|
3423
|
+
return void 0;
|
|
3424
|
+
}
|
|
3425
|
+
const tail = path29.slice(RC_SESSIONS_PATH_PREFIX.length);
|
|
3426
|
+
const facts = {
|
|
3427
|
+
authorization: singleHeader(request4.headers, "authorization"),
|
|
3428
|
+
anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
|
|
3429
|
+
anthropicClientPlatform: singleHeader(request4.headers, "anthropic-client-platform")
|
|
3430
|
+
};
|
|
3431
|
+
if (tail === "") {
|
|
3432
|
+
if (method !== "POST") {
|
|
3433
|
+
return void 0;
|
|
3434
|
+
}
|
|
3435
|
+
let accepted = false;
|
|
3436
|
+
let buffered = 0;
|
|
3437
|
+
let body = "";
|
|
3438
|
+
return {
|
|
3439
|
+
onResponse: (status) => {
|
|
3440
|
+
accepted = isSuccessful(status);
|
|
3441
|
+
},
|
|
3442
|
+
onBodyChunk: (chunk) => {
|
|
3443
|
+
if (!accepted || buffered >= RC_CREATE_RESPONSE_CAP_BYTES) {
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
const text = Buffer.from(chunk).toString("utf8");
|
|
3447
|
+
body = body + text.slice(0, RC_CREATE_RESPONSE_CAP_BYTES - buffered);
|
|
3448
|
+
buffered = body.length;
|
|
3449
|
+
},
|
|
3450
|
+
onEnd: () => {
|
|
3451
|
+
if (!accepted) {
|
|
3452
|
+
return;
|
|
3453
|
+
}
|
|
3454
|
+
const id2 = sessionCreateId(body);
|
|
3455
|
+
if (id2 === void 0) {
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
entries.set(id2, { id: id2, createdAt: deps.now(), lastSeenAt: deps.now(), ...facts });
|
|
3459
|
+
}
|
|
3460
|
+
};
|
|
3461
|
+
}
|
|
3462
|
+
const rest = tail.slice(1);
|
|
3463
|
+
const [id] = rest.split("/");
|
|
3464
|
+
if (id?.startsWith(RC_SESSION_ID_PREFIX) !== true) {
|
|
3465
|
+
return void 0;
|
|
3466
|
+
}
|
|
3467
|
+
const entry = entries.get(id);
|
|
3468
|
+
if (entry === void 0) {
|
|
3469
|
+
entries.set(id, { id, createdAt: deps.now(), lastSeenAt: deps.now(), ...facts });
|
|
3470
|
+
} else {
|
|
3471
|
+
entry.lastSeenAt = deps.now();
|
|
3472
|
+
entry.authorization = facts.authorization ?? entry.authorization;
|
|
3473
|
+
entry.anthropicVersion = facts.anthropicVersion ?? entry.anthropicVersion;
|
|
3474
|
+
entry.anthropicClientPlatform = facts.anthropicClientPlatform ?? entry.anthropicClientPlatform;
|
|
3475
|
+
}
|
|
3476
|
+
const archiving = method === "POST" && rest === `${id}/archive`;
|
|
3477
|
+
return {
|
|
3478
|
+
onResponse: (status, headers) => {
|
|
3479
|
+
if (singleHeader(headers, CONFLICT_REASON_HEADER) !== void 0 || archiving && isSuccessful(status)) {
|
|
3480
|
+
expire(id);
|
|
3481
|
+
}
|
|
3482
|
+
},
|
|
3483
|
+
onBodyChunk: () => {
|
|
3484
|
+
},
|
|
3485
|
+
onEnd: () => {
|
|
3486
|
+
}
|
|
3487
|
+
};
|
|
3488
|
+
};
|
|
3489
|
+
return {
|
|
3490
|
+
observeExchange,
|
|
3491
|
+
list: () => {
|
|
3492
|
+
sweep();
|
|
3493
|
+
return [...entries.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1)).map(({ id, createdAt, lastSeenAt }) => ({ id, createdAt, lastSeenAt }));
|
|
3494
|
+
},
|
|
3495
|
+
credentialOf: (sessionId) => {
|
|
3496
|
+
const entry = entries.get(sessionId);
|
|
3497
|
+
return entry === void 0 ? void 0 : { authorization: entry.authorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform };
|
|
3498
|
+
},
|
|
3499
|
+
close: () => {
|
|
3500
|
+
clearInterval(timer);
|
|
3501
|
+
}
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
function observingRoutedRoute(route, tracker) {
|
|
3505
|
+
return {
|
|
3506
|
+
...route,
|
|
3507
|
+
serve: async (request4, response) => {
|
|
3508
|
+
const observed = tracker.observeExchange({ method: request4.method, url: request4.url, headers: request4.headers });
|
|
3509
|
+
await route.serve(request4, observed === void 0 ? response : teeRoutedResponse(response, observed));
|
|
3510
|
+
}
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
function teeRoutedResponse(response, observer) {
|
|
3514
|
+
return {
|
|
3515
|
+
get headersSent() {
|
|
3516
|
+
return response.headersSent;
|
|
3517
|
+
},
|
|
3518
|
+
start: (status, headers) => {
|
|
3519
|
+
observer.onResponse(status, headers);
|
|
3520
|
+
response.start(status, headers);
|
|
3521
|
+
},
|
|
3522
|
+
flush: () => {
|
|
3523
|
+
response.flush();
|
|
3524
|
+
},
|
|
3525
|
+
write: async (chunk) => {
|
|
3526
|
+
observer.onBodyChunk(typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk);
|
|
3527
|
+
await response.write(chunk);
|
|
3528
|
+
},
|
|
3529
|
+
end: () => {
|
|
3530
|
+
response.end();
|
|
3531
|
+
observer.onEnd();
|
|
3532
|
+
},
|
|
3533
|
+
destroy: () => {
|
|
3534
|
+
response.destroy();
|
|
3535
|
+
observer.onEnd();
|
|
3536
|
+
}
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
function rcObserverAsPassthrough(observer) {
|
|
3540
|
+
return {
|
|
3541
|
+
// The request body carries nothing the tracker reads: the credential and the session id are in the request's headers and path, already handed over with the request.
|
|
3542
|
+
onRequestChunk: () => {
|
|
3543
|
+
return;
|
|
3544
|
+
},
|
|
3545
|
+
onResponse: (status, headers) => {
|
|
3546
|
+
observer.onResponse(status, headers);
|
|
3547
|
+
},
|
|
3548
|
+
onResponseChunk: (chunk) => {
|
|
3549
|
+
observer.onBodyChunk(chunk);
|
|
3550
|
+
},
|
|
3551
|
+
onEnd: () => {
|
|
3552
|
+
observer.onEnd();
|
|
3553
|
+
}
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
function buildRcUserMessagePayload(uuid, sessionId, text) {
|
|
3557
|
+
return {
|
|
3558
|
+
type: "user",
|
|
3559
|
+
uuid,
|
|
3560
|
+
session_id: sessionId,
|
|
3561
|
+
parent_tool_use_id: null,
|
|
3562
|
+
message: { role: "user", content: text }
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
function buildRcEventWriteBody(payload) {
|
|
3566
|
+
return { events: [{ payload }] };
|
|
3567
|
+
}
|
|
3568
|
+
function sequenceNumsOf(body) {
|
|
3569
|
+
let parsed;
|
|
3570
|
+
try {
|
|
3571
|
+
parsed = JSON.parse(body);
|
|
3572
|
+
} catch {
|
|
3573
|
+
return void 0;
|
|
3574
|
+
}
|
|
3575
|
+
if (typeof parsed !== "object" || parsed === null || !("results" in parsed) || !Array.isArray(parsed.results)) {
|
|
3576
|
+
return void 0;
|
|
3577
|
+
}
|
|
3578
|
+
const sequenceNums = [];
|
|
3579
|
+
const results = parsed.results;
|
|
3580
|
+
for (const result of results) {
|
|
3581
|
+
if (typeof result !== "object" || result === null || !("sequence_num" in result)) {
|
|
3582
|
+
return void 0;
|
|
3583
|
+
}
|
|
3584
|
+
const sequenceNum = result.sequence_num;
|
|
3585
|
+
if (typeof sequenceNum !== "number") {
|
|
3586
|
+
return void 0;
|
|
3587
|
+
}
|
|
3588
|
+
sequenceNums.push(sequenceNum);
|
|
3589
|
+
}
|
|
3590
|
+
return sequenceNums.length === 0 ? void 0 : sequenceNums;
|
|
3591
|
+
}
|
|
3592
|
+
function rcInjectResultFromAnswer(answer) {
|
|
3593
|
+
if (!isSuccessful(answer.status)) {
|
|
3594
|
+
if (answer.status === HTTP_STATUS.unauthorized) {
|
|
3595
|
+
return { ok: false, message: `the API refused the observed Authorization bearer for this session (HTTP 401): the bearer went stale after the door last saw the session's traffic. The session's own client refreshes it on its next call, which the door will observe, so retry once Remote Control has been active again. API answer: ${answer.body}` };
|
|
3596
|
+
}
|
|
3597
|
+
return { ok: false, message: `the API answered HTTP ${String(answer.status)} to the injected event: ${answer.body}` };
|
|
3598
|
+
}
|
|
3599
|
+
const sequenceNums = sequenceNumsOf(answer.body);
|
|
3600
|
+
if (sequenceNums === void 0) {
|
|
3601
|
+
return { ok: false, message: `the API accepted the injected event but its answer named no results[].sequence_num, so delivery cannot be confirmed: ${answer.body}` };
|
|
3602
|
+
}
|
|
3603
|
+
return { ok: true, sequenceNums };
|
|
3604
|
+
}
|
|
3605
|
+
async function injectRcUserMessage(deps, sessionId, text) {
|
|
3606
|
+
const credential = deps.credentialOf(sessionId);
|
|
3607
|
+
if (credential === void 0) {
|
|
3608
|
+
return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
|
|
3609
|
+
}
|
|
3610
|
+
const headers = { "content-type": "application/json" };
|
|
3611
|
+
if (credential.authorization !== void 0) {
|
|
3612
|
+
headers.authorization = credential.authorization;
|
|
3613
|
+
}
|
|
3614
|
+
if (credential.anthropicVersion !== void 0) {
|
|
3615
|
+
headers["anthropic-version"] = credential.anthropicVersion;
|
|
3616
|
+
}
|
|
3617
|
+
if (credential.anthropicClientPlatform !== void 0) {
|
|
3618
|
+
headers["anthropic-client-platform"] = credential.anthropicClientPlatform;
|
|
3619
|
+
}
|
|
3620
|
+
const body = JSON.stringify(buildRcEventWriteBody(buildRcUserMessagePayload(deps.newUuid(), sessionId, text)));
|
|
3621
|
+
let answer;
|
|
3622
|
+
try {
|
|
3623
|
+
answer = await deps.dial.writeEvents(sessionId, headers, body);
|
|
3624
|
+
} catch (error) {
|
|
3625
|
+
return { ok: false, message: `the door could not reach the API host to inject into session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
|
|
3626
|
+
}
|
|
3627
|
+
return rcInjectResultFromAnswer(answer);
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
// src/frontdoor/rcControl.ts
|
|
3631
|
+
import * as https2 from "node:https";
|
|
3632
|
+
var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
3633
|
+
var CONTROL_BODY_CAP_BYTES = 10485760;
|
|
3634
|
+
function answerJson(response, status, body) {
|
|
3635
|
+
const text = JSON.stringify(body);
|
|
3636
|
+
response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
|
|
3637
|
+
response.end(text);
|
|
3638
|
+
}
|
|
3639
|
+
async function readBody(request4) {
|
|
3640
|
+
return await new Promise((resolve, reject) => {
|
|
3641
|
+
const chunks = [];
|
|
3642
|
+
let total = 0;
|
|
3643
|
+
request4.on("data", (chunk) => {
|
|
3644
|
+
total += chunk.length;
|
|
3645
|
+
if (total > CONTROL_BODY_CAP_BYTES) {
|
|
3646
|
+
request4.destroy();
|
|
3647
|
+
reject(new Error("too large"));
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
chunks.push(chunk);
|
|
3651
|
+
});
|
|
3652
|
+
request4.on("end", () => {
|
|
3653
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
3654
|
+
});
|
|
3655
|
+
request4.on("error", () => {
|
|
3656
|
+
reject(new Error("the request body ended early"));
|
|
3657
|
+
});
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
function createRcControlHandler(deps) {
|
|
3661
|
+
const sessionsPath = `${CONTROL_PATH_PREFIX}/sessions`;
|
|
3662
|
+
const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
|
|
3663
|
+
return (request4, response) => {
|
|
3664
|
+
const presented = request4.headers.authorization;
|
|
3665
|
+
const bearerPrefix = "bearer ".length;
|
|
3666
|
+
const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
|
|
3667
|
+
if (token === void 0 || !isLiveCapability(token, [deps.expectedToken])) {
|
|
3668
|
+
answerJson(response, HTTP_STATUS.unauthorized, { error: "the front door's control routes demand this generation's control token as a Bearer credential" });
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3671
|
+
const url = request4.url ?? "/";
|
|
3672
|
+
const handle = async () => {
|
|
3673
|
+
if (url === sessionsPath) {
|
|
3674
|
+
if (request4.method !== "GET") {
|
|
3675
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the session list is a GET" });
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
answerJson(response, HTTP_STATUS.ok, { sessions: deps.list() });
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
if (url === injectPath) {
|
|
3682
|
+
if (request4.method !== "POST") {
|
|
3683
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the inject operation is a POST" });
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3686
|
+
let parsed;
|
|
3687
|
+
try {
|
|
3688
|
+
parsed = JSON.parse(await readBody(request4));
|
|
3689
|
+
} catch {
|
|
3690
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body is not readable JSON within the control body cap" });
|
|
3691
|
+
return;
|
|
3692
|
+
}
|
|
3693
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("text" in parsed) || typeof parsed.session !== "string" || typeof parsed.text !== "string" || parsed.session === "" || parsed.text === "") {
|
|
3694
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body must be JSON naming a non-empty session id and text" });
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
3697
|
+
const result = await deps.inject(parsed.session, parsed.text);
|
|
3698
|
+
if (result.ok) {
|
|
3699
|
+
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
3703
|
+
return;
|
|
3704
|
+
}
|
|
3705
|
+
answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url}` });
|
|
3706
|
+
};
|
|
3707
|
+
handle().catch((error) => {
|
|
3708
|
+
answerJson(response, HTTP_STATUS.internalServerError, { error: `the control route failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
3709
|
+
});
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
function realRcControlTransport(port, ca) {
|
|
3713
|
+
return {
|
|
3714
|
+
request: async (options) => await new Promise((resolve, reject) => {
|
|
3715
|
+
const request4 = https2.request(
|
|
3716
|
+
{
|
|
3717
|
+
host: "127.0.0.1",
|
|
3718
|
+
port,
|
|
3719
|
+
method: options.method,
|
|
3720
|
+
path: options.path,
|
|
3721
|
+
headers: options.body === void 0 ? options.headers : { ...options.headers, "content-length": String(Buffer.byteLength(options.body, "utf8")) },
|
|
3722
|
+
agent: false,
|
|
3723
|
+
ca: [ca]
|
|
3724
|
+
},
|
|
3725
|
+
(response) => {
|
|
3726
|
+
const chunks = [];
|
|
3727
|
+
response.on("data", (chunk) => {
|
|
3728
|
+
chunks.push(chunk);
|
|
3729
|
+
});
|
|
3730
|
+
response.on("end", () => {
|
|
3731
|
+
resolve({ status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") });
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3734
|
+
);
|
|
3735
|
+
request4.once("error", reject);
|
|
3736
|
+
if (options.body === void 0) {
|
|
3737
|
+
request4.end();
|
|
3738
|
+
} else {
|
|
3739
|
+
request4.end(options.body);
|
|
3740
|
+
}
|
|
3741
|
+
})
|
|
3742
|
+
};
|
|
3743
|
+
}
|
|
3744
|
+
function controlErrorOf(body) {
|
|
3745
|
+
try {
|
|
3746
|
+
const parsed = JSON.parse(body);
|
|
3747
|
+
if (typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string") {
|
|
3748
|
+
return parsed.error;
|
|
3749
|
+
}
|
|
3750
|
+
} catch {
|
|
3751
|
+
return body;
|
|
3752
|
+
}
|
|
3753
|
+
return void 0;
|
|
3754
|
+
}
|
|
3755
|
+
function isRcSessionSummary(value) {
|
|
3756
|
+
if (typeof value !== "object" || value === null) {
|
|
3757
|
+
return false;
|
|
3758
|
+
}
|
|
3759
|
+
if (!("id" in value) || !("createdAt" in value) || !("lastSeenAt" in value)) {
|
|
3760
|
+
return false;
|
|
3761
|
+
}
|
|
3762
|
+
return typeof value.id === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
|
|
3763
|
+
}
|
|
3764
|
+
function sequenceNumsOfBody(body) {
|
|
3765
|
+
let parsed;
|
|
3766
|
+
try {
|
|
3767
|
+
parsed = JSON.parse(body);
|
|
3768
|
+
} catch {
|
|
3769
|
+
return void 0;
|
|
3770
|
+
}
|
|
3771
|
+
if (typeof parsed !== "object" || parsed === null || !("sequenceNums" in parsed) || !Array.isArray(parsed.sequenceNums)) {
|
|
3772
|
+
return void 0;
|
|
3773
|
+
}
|
|
3774
|
+
const sequenceNums = [];
|
|
3775
|
+
const values = parsed.sequenceNums;
|
|
3776
|
+
for (const value of values) {
|
|
3777
|
+
if (typeof value !== "number") {
|
|
3778
|
+
return void 0;
|
|
3779
|
+
}
|
|
3780
|
+
sequenceNums.push(value);
|
|
3781
|
+
}
|
|
3782
|
+
return sequenceNums;
|
|
3783
|
+
}
|
|
3784
|
+
function frontDoorRcControl(transport, token) {
|
|
3785
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
3786
|
+
const sessionsOf = (answer, what) => {
|
|
3787
|
+
if (answer.status !== HTTP_STATUS.ok) {
|
|
3788
|
+
throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
|
|
3789
|
+
}
|
|
3790
|
+
let parsed;
|
|
3791
|
+
try {
|
|
3792
|
+
parsed = JSON.parse(answer.body);
|
|
3793
|
+
} catch {
|
|
3794
|
+
throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
|
|
3795
|
+
}
|
|
3796
|
+
if (typeof parsed !== "object" || parsed === null || !("sessions" in parsed) || !Array.isArray(parsed.sessions)) {
|
|
3797
|
+
throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
|
|
3798
|
+
}
|
|
3799
|
+
const candidates = parsed.sessions;
|
|
3800
|
+
const sessions = [];
|
|
3801
|
+
for (const value of candidates) {
|
|
3802
|
+
if (!isRcSessionSummary(value)) {
|
|
3803
|
+
throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
|
|
3804
|
+
}
|
|
3805
|
+
sessions.push(value);
|
|
3806
|
+
}
|
|
3807
|
+
return sessions;
|
|
3808
|
+
};
|
|
3809
|
+
return {
|
|
3810
|
+
listSessions: async () => sessionsOf(await transport.request({ method: "GET", path: `${CONTROL_PATH_PREFIX}/sessions`, headers }), "listing the door's Remote Control sessions"),
|
|
3811
|
+
sendPrompt: async (sessionId, text) => {
|
|
3812
|
+
let answer;
|
|
3813
|
+
try {
|
|
3814
|
+
answer = await transport.request({
|
|
3815
|
+
method: "POST",
|
|
3816
|
+
path: `${CONTROL_PATH_PREFIX}/inject`,
|
|
3817
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
3818
|
+
body: JSON.stringify({ session: sessionId, text })
|
|
3819
|
+
});
|
|
3820
|
+
} catch (error) {
|
|
3821
|
+
return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
|
|
3822
|
+
}
|
|
3823
|
+
if (answer.status === HTTP_STATUS.ok) {
|
|
3824
|
+
const sequenceNums = sequenceNumsOfBody(answer.body);
|
|
3825
|
+
if (sequenceNums === void 0) {
|
|
3826
|
+
return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
|
|
3827
|
+
}
|
|
3828
|
+
return { ok: true, sequenceNums };
|
|
3829
|
+
}
|
|
3830
|
+
return { ok: false, message: `injecting into ${sessionId} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}` };
|
|
3831
|
+
}
|
|
3832
|
+
};
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3311
3835
|
// src/frontdoor/ensure.ts
|
|
3312
3836
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3313
3837
|
|
|
3314
3838
|
// src/headroom/state.ts
|
|
3315
|
-
import { createHash as
|
|
3839
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3316
3840
|
import path8 from "node:path";
|
|
3317
3841
|
import { z as z4 } from "zod";
|
|
3318
3842
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
@@ -3373,7 +3897,7 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3373
3897
|
return upstreams;
|
|
3374
3898
|
}
|
|
3375
3899
|
function hashAllowlist(allowlist) {
|
|
3376
|
-
return
|
|
3900
|
+
return createHash3("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3377
3901
|
}
|
|
3378
3902
|
function readAllProviders(fs12, providersDir) {
|
|
3379
3903
|
const result = [];
|
|
@@ -3871,7 +4395,7 @@ function ensureHeadroom(params) {
|
|
|
3871
4395
|
}
|
|
3872
4396
|
|
|
3873
4397
|
// src/headroom/settings.ts
|
|
3874
|
-
import { createHash as
|
|
4398
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3875
4399
|
function settingsOf(config) {
|
|
3876
4400
|
return {
|
|
3877
4401
|
...config.mode === void 0 ? {} : { mode: config.mode },
|
|
@@ -3896,7 +4420,7 @@ function settingsEnv(settings) {
|
|
|
3896
4420
|
return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
|
|
3897
4421
|
}
|
|
3898
4422
|
function hashSettings(settings) {
|
|
3899
|
-
return
|
|
4423
|
+
return createHash4("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
|
|
3900
4424
|
}
|
|
3901
4425
|
|
|
3902
4426
|
// src/headroom/socket.ts
|
|
@@ -4327,6 +4851,7 @@ function buildLayoutPaths(root) {
|
|
|
4327
4851
|
frontdoorCaBundlesDir: path12.join(root, "frontdoor", "ca", "bundles"),
|
|
4328
4852
|
frontdoorStateFile: path12.join(root, "frontdoor", "state.json"),
|
|
4329
4853
|
frontdoorLockFile: path12.join(root, "frontdoor", "start.lock"),
|
|
4854
|
+
frontdoorControlTokenFile: path12.join(root, "frontdoor", "control-token"),
|
|
4330
4855
|
frontdoorSessionsDir: path12.join(root, "frontdoor", "sessions"),
|
|
4331
4856
|
frontdoorLogPath: path12.join(root, "logs", "frontdoor.log"),
|
|
4332
4857
|
usageDir: path12.join(root, "usage"),
|
|
@@ -5821,7 +6346,7 @@ function collectPoolPick(params) {
|
|
|
5821
6346
|
|
|
5822
6347
|
// src/realPorts.ts
|
|
5823
6348
|
import { spawn, spawnSync } from "node:child_process";
|
|
5824
|
-
import { createHash as
|
|
6349
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5825
6350
|
import fs7 from "node:fs";
|
|
5826
6351
|
import os3 from "node:os";
|
|
5827
6352
|
import path21 from "node:path";
|
|
@@ -6016,7 +6541,7 @@ var realFarmFs = {
|
|
|
6016
6541
|
},
|
|
6017
6542
|
hashFile(filePath) {
|
|
6018
6543
|
try {
|
|
6019
|
-
return
|
|
6544
|
+
return createHash5("sha256").update(fs7.readFileSync(filePath)).digest("hex");
|
|
6020
6545
|
} catch (error) {
|
|
6021
6546
|
if (isEnoent3(error) || isErrorWithCode(error, "EISDIR")) {
|
|
6022
6547
|
return void 0;
|
|
@@ -8012,7 +8537,7 @@ function probeFrontDoorSync(port, caPem) {
|
|
|
8012
8537
|
}
|
|
8013
8538
|
|
|
8014
8539
|
// src/frontdoor/trust.ts
|
|
8015
|
-
import { createHash as
|
|
8540
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
8016
8541
|
import path28 from "node:path";
|
|
8017
8542
|
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
8018
8543
|
function resolveTrustBundle(params) {
|
|
@@ -8035,7 +8560,7 @@ function resolveTrustBundle(params) {
|
|
|
8035
8560
|
}
|
|
8036
8561
|
const combined = `${inheritedPem.trimEnd()}
|
|
8037
8562
|
${caPem}`;
|
|
8038
|
-
const bundle = path28.join(bundlesDir, `${
|
|
8563
|
+
const bundle = path28.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
8039
8564
|
if (!fs12.exists(bundle)) {
|
|
8040
8565
|
fs12.mkdirp(bundlesDir);
|
|
8041
8566
|
fs12.writeFileAtomic(bundle, combined);
|
|
@@ -8178,6 +8703,7 @@ export {
|
|
|
8178
8703
|
CONNECT_INTERCEPT_HOST,
|
|
8179
8704
|
CONNECT_INTERCEPT_HOSTS,
|
|
8180
8705
|
CONNECT_LIMITS,
|
|
8706
|
+
CONTROL_PATH_PREFIX,
|
|
8181
8707
|
CategoryClassificationOverlaySchema,
|
|
8182
8708
|
CategoryClassificationSchema,
|
|
8183
8709
|
CategoryMapSchema,
|
|
@@ -8216,6 +8742,8 @@ export {
|
|
|
8216
8742
|
ProviderKindMismatchError,
|
|
8217
8743
|
ProviderNotFoundError,
|
|
8218
8744
|
ProviderSchema,
|
|
8745
|
+
RC_IDLE_EXPIRY_MS,
|
|
8746
|
+
RC_SESSIONS_PATH_PREFIX,
|
|
8219
8747
|
ROUTED_PATH_PREFIX,
|
|
8220
8748
|
UsageError,
|
|
8221
8749
|
UsageSnapshotError,
|
|
@@ -8227,6 +8755,8 @@ export {
|
|
|
8227
8755
|
addProvider,
|
|
8228
8756
|
buildEntryFacts,
|
|
8229
8757
|
buildLayoutPaths,
|
|
8758
|
+
buildRcEventWriteBody,
|
|
8759
|
+
buildRcUserMessagePayload,
|
|
8230
8760
|
carryOver,
|
|
8231
8761
|
checkReportHasWarnings,
|
|
8232
8762
|
checkReportToJson,
|
|
@@ -8234,6 +8764,8 @@ export {
|
|
|
8234
8764
|
collectDoctorReport,
|
|
8235
8765
|
createLeafCache,
|
|
8236
8766
|
createProfile,
|
|
8767
|
+
createRcControlHandler,
|
|
8768
|
+
createRcSessionTracker,
|
|
8237
8769
|
describeProviderEndpoint,
|
|
8238
8770
|
detectAmbientCredential,
|
|
8239
8771
|
ensureCa,
|
|
@@ -8244,8 +8776,10 @@ export {
|
|
|
8244
8776
|
formatCheckReport,
|
|
8245
8777
|
formatDoctorReport,
|
|
8246
8778
|
forwardableHeaders,
|
|
8779
|
+
frontDoorRcControl,
|
|
8247
8780
|
generateCa,
|
|
8248
8781
|
identityExists,
|
|
8782
|
+
injectRcUserMessage,
|
|
8249
8783
|
isIdentityDirectoryName,
|
|
8250
8784
|
isInterceptedHost,
|
|
8251
8785
|
listDirectoryRules,
|
|
@@ -8254,11 +8788,14 @@ export {
|
|
|
8254
8788
|
listProviders,
|
|
8255
8789
|
listUsageSnapshots,
|
|
8256
8790
|
mintLeaf,
|
|
8791
|
+
observingRoutedRoute,
|
|
8257
8792
|
parseConnectTarget,
|
|
8258
8793
|
prepareClaudeLaunch,
|
|
8259
8794
|
prepareLaunch,
|
|
8260
8795
|
profileExists,
|
|
8261
8796
|
providerExists,
|
|
8797
|
+
rcInjectResultFromAnswer,
|
|
8798
|
+
rcObserverAsPassthrough,
|
|
8262
8799
|
readActiveIdentity,
|
|
8263
8800
|
readDirectoryRules,
|
|
8264
8801
|
readFarmManifest,
|
|
@@ -8270,6 +8807,8 @@ export {
|
|
|
8270
8807
|
readUsageSnapshot,
|
|
8271
8808
|
realConnectCertStore,
|
|
8272
8809
|
realConnectEffects,
|
|
8810
|
+
realRcControlTransport,
|
|
8811
|
+
realRcEventDial,
|
|
8273
8812
|
recoverFarm,
|
|
8274
8813
|
recoveryDiagnostics,
|
|
8275
8814
|
removeDirectoryRule,
|