agent-shim 8.3.0 → 8.4.1
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 +3 -0
- package/dist/cli.cjs +494 -69
- package/dist/index.cjs +414 -58
- package/dist/index.mjs +409 -57
- package/dist/types/index.d.ts +109 -26
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -61040,7 +61040,7 @@ function registerIdentityCommand(program2, deps) {
|
|
|
61040
61040
|
// package.json
|
|
61041
61041
|
var package_default = {
|
|
61042
61042
|
name: "agent-shim",
|
|
61043
|
-
version: "8.
|
|
61043
|
+
version: "8.4.1",
|
|
61044
61044
|
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.",
|
|
61045
61045
|
license: "MIT",
|
|
61046
61046
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -64064,19 +64064,19 @@ function createFileCapture(file2, now = () => /* @__PURE__ */ new Date()) {
|
|
|
64064
64064
|
if (budget <= 0) {
|
|
64065
64065
|
return;
|
|
64066
64066
|
}
|
|
64067
|
-
const
|
|
64068
|
-
stored[direction] +=
|
|
64069
|
-
const text2 =
|
|
64070
|
-
const faithful = Buffer.from(text2, "utf8").equals(
|
|
64067
|
+
const excerpt2 = chunk.subarray(0, Math.min(CHUNK_LOG_CAP_BYTES, budget));
|
|
64068
|
+
stored[direction] += excerpt2.length;
|
|
64069
|
+
const text2 = excerpt2.toString("utf8");
|
|
64070
|
+
const faithful = Buffer.from(text2, "utf8").equals(excerpt2);
|
|
64071
64071
|
append({
|
|
64072
64072
|
kind: "stream-chunk",
|
|
64073
64073
|
id,
|
|
64074
64074
|
dir: direction,
|
|
64075
64075
|
seq: sequences[direction],
|
|
64076
64076
|
bytes: chunk.length,
|
|
64077
|
-
stored:
|
|
64078
|
-
truncated:
|
|
64079
|
-
...faithful ? { body: redactText(text2) } : { b64:
|
|
64077
|
+
stored: excerpt2.length,
|
|
64078
|
+
truncated: excerpt2.length < chunk.length,
|
|
64079
|
+
...faithful ? { body: redactText(text2) } : { b64: excerpt2.toString("base64") }
|
|
64080
64080
|
});
|
|
64081
64081
|
},
|
|
64082
64082
|
onEnd: () => {
|
|
@@ -64096,16 +64096,16 @@ function createFileCapture(file2, now = () => /* @__PURE__ */ new Date()) {
|
|
|
64096
64096
|
if (budget <= 0) {
|
|
64097
64097
|
return;
|
|
64098
64098
|
}
|
|
64099
|
-
const
|
|
64100
|
-
logged[direction] +=
|
|
64099
|
+
const excerpt2 = incoming.subarray(0, Math.min(CHUNK_LOG_CAP_BYTES, budget));
|
|
64100
|
+
logged[direction] += excerpt2.length;
|
|
64101
64101
|
append({
|
|
64102
64102
|
kind: `${direction}-chunk`,
|
|
64103
64103
|
id,
|
|
64104
64104
|
seq: sequences[direction],
|
|
64105
64105
|
bytes: incoming.length,
|
|
64106
|
-
stored:
|
|
64107
|
-
truncated:
|
|
64108
|
-
body: redactBody(
|
|
64106
|
+
stored: excerpt2.length,
|
|
64107
|
+
truncated: excerpt2.length < incoming.length,
|
|
64108
|
+
body: redactBody(excerpt2)
|
|
64109
64109
|
});
|
|
64110
64110
|
};
|
|
64111
64111
|
return {
|
|
@@ -65202,6 +65202,11 @@ function realConnectEffects() {
|
|
|
65202
65202
|
request7.on("data", (chunk) => {
|
|
65203
65203
|
observer.onRequestChunk(chunk);
|
|
65204
65204
|
});
|
|
65205
|
+
if (observer.onRequestEnd !== void 0) {
|
|
65206
|
+
request7.on("end", () => {
|
|
65207
|
+
observer.onRequestEnd?.();
|
|
65208
|
+
});
|
|
65209
|
+
}
|
|
65205
65210
|
response.on("close", () => {
|
|
65206
65211
|
observer.onEnd();
|
|
65207
65212
|
});
|
|
@@ -65254,7 +65259,10 @@ async function readBody(request7) {
|
|
|
65254
65259
|
}
|
|
65255
65260
|
function createRcControlHandler(deps) {
|
|
65256
65261
|
const sessionsPath = `${CONTROL_PATH_PREFIX}/sessions`;
|
|
65262
|
+
const statusPath = `${CONTROL_PATH_PREFIX}/status`;
|
|
65263
|
+
const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
|
|
65257
65264
|
const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
|
|
65265
|
+
const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
|
|
65258
65266
|
return (request7, response) => {
|
|
65259
65267
|
const presented = request7.headers.authorization;
|
|
65260
65268
|
const bearerPrefix = "bearer ".length;
|
|
@@ -65263,9 +65271,9 @@ function createRcControlHandler(deps) {
|
|
|
65263
65271
|
answerJson(response, HTTP_STATUS.unauthorized, { error: "the front door's control routes demand this generation's control token as a Bearer credential" });
|
|
65264
65272
|
return;
|
|
65265
65273
|
}
|
|
65266
|
-
const url2 = request7.url ?? "/";
|
|
65274
|
+
const url2 = new URL(request7.url ?? "/", "http://127.0.0.1");
|
|
65267
65275
|
const handle = async () => {
|
|
65268
|
-
if (url2 === sessionsPath) {
|
|
65276
|
+
if (url2.pathname === sessionsPath) {
|
|
65269
65277
|
if (request7.method !== "GET") {
|
|
65270
65278
|
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the session list is a GET" });
|
|
65271
65279
|
return;
|
|
@@ -65273,7 +65281,20 @@ function createRcControlHandler(deps) {
|
|
|
65273
65281
|
answerJson(response, HTTP_STATUS.ok, { sessions: deps.list() });
|
|
65274
65282
|
return;
|
|
65275
65283
|
}
|
|
65276
|
-
if (url2 ===
|
|
65284
|
+
if (url2.pathname === statusPath || url2.pathname === pendingPath) {
|
|
65285
|
+
if (request7.method !== "GET") {
|
|
65286
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the status and pending reads are GETs" });
|
|
65287
|
+
return;
|
|
65288
|
+
}
|
|
65289
|
+
const session = url2.searchParams.get("session") ?? void 0;
|
|
65290
|
+
if (session !== void 0 && !deps.statusOf(session).some((entry) => entry.id === session)) {
|
|
65291
|
+
answerJson(response, HTTP_STATUS.notFound, { error: `the front door has not observed Remote Control session ${session}: 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)` });
|
|
65292
|
+
return;
|
|
65293
|
+
}
|
|
65294
|
+
answerJson(response, HTTP_STATUS.ok, url2.pathname === statusPath ? { statuses: deps.statusOf(session) } : { pending: deps.pendingOf(session) });
|
|
65295
|
+
return;
|
|
65296
|
+
}
|
|
65297
|
+
if (url2.pathname === injectPath) {
|
|
65277
65298
|
if (request7.method !== "POST") {
|
|
65278
65299
|
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the inject operation is a POST" });
|
|
65279
65300
|
return;
|
|
@@ -65297,7 +65318,36 @@ function createRcControlHandler(deps) {
|
|
|
65297
65318
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
65298
65319
|
return;
|
|
65299
65320
|
}
|
|
65300
|
-
|
|
65321
|
+
if (url2.pathname === answerPath) {
|
|
65322
|
+
if (request7.method !== "POST") {
|
|
65323
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the answer operation is a POST" });
|
|
65324
|
+
return;
|
|
65325
|
+
}
|
|
65326
|
+
let parsed;
|
|
65327
|
+
try {
|
|
65328
|
+
parsed = JSON.parse(await readBody(request7));
|
|
65329
|
+
} catch {
|
|
65330
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body is not readable JSON within the control body cap" });
|
|
65331
|
+
return;
|
|
65332
|
+
}
|
|
65333
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("request" in parsed) || !("approve" in parsed) || typeof parsed.session !== "string" || typeof parsed.request !== "string" || typeof parsed.approve !== "boolean" || parsed.session === "" || parsed.request === "") {
|
|
65334
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body must be JSON naming a non-empty session id, request id, and an approve boolean" });
|
|
65335
|
+
return;
|
|
65336
|
+
}
|
|
65337
|
+
const text2 = "text" in parsed && typeof parsed.text === "string" ? parsed.text : void 0;
|
|
65338
|
+
if (parsed.approve && text2 !== void 0 && text2 !== "") {
|
|
65339
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "an approval carries no text (the protocol's allow result has no message field); text is the denial message" });
|
|
65340
|
+
return;
|
|
65341
|
+
}
|
|
65342
|
+
const result = await deps.answer(parsed.session, parsed.request, { approve: parsed.approve, message: parsed.approve ? void 0 : text2 });
|
|
65343
|
+
if (result.ok) {
|
|
65344
|
+
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, request: parsed.request, sequenceNums: result.sequenceNums });
|
|
65345
|
+
return;
|
|
65346
|
+
}
|
|
65347
|
+
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
65348
|
+
return;
|
|
65349
|
+
}
|
|
65350
|
+
answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url2.pathname}` });
|
|
65301
65351
|
};
|
|
65302
65352
|
handle().catch((error62) => {
|
|
65303
65353
|
answerJson(response, HTTP_STATUS.internalServerError, { error: `the control route failed: ${error62 instanceof Error ? error62.message : String(error62)}` });
|
|
@@ -65356,6 +65406,39 @@ function isRcSessionSummary(value) {
|
|
|
65356
65406
|
}
|
|
65357
65407
|
return typeof value.id === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
|
|
65358
65408
|
}
|
|
65409
|
+
function isRcWorkerFact(value, valueIs) {
|
|
65410
|
+
if (typeof value !== "object" || value === null) {
|
|
65411
|
+
return false;
|
|
65412
|
+
}
|
|
65413
|
+
if (!("value" in value) || !("observedAt" in value)) {
|
|
65414
|
+
return false;
|
|
65415
|
+
}
|
|
65416
|
+
return valueIs(value.value) && typeof value.observedAt === "number";
|
|
65417
|
+
}
|
|
65418
|
+
function isRcSessionStatus(value) {
|
|
65419
|
+
if (!isRcSessionSummary(value)) {
|
|
65420
|
+
return false;
|
|
65421
|
+
}
|
|
65422
|
+
if (!("pending" in value) || !Array.isArray(value.pending)) {
|
|
65423
|
+
return false;
|
|
65424
|
+
}
|
|
65425
|
+
if ("workerState" in value && value.workerState !== void 0 && !isRcWorkerFact(value.workerState, (candidate) => typeof candidate === "string")) {
|
|
65426
|
+
return false;
|
|
65427
|
+
}
|
|
65428
|
+
if ("workerIdleSeconds" in value && value.workerIdleSeconds !== void 0 && !isRcWorkerFact(value.workerIdleSeconds, (candidate) => typeof candidate === "number")) {
|
|
65429
|
+
return false;
|
|
65430
|
+
}
|
|
65431
|
+
return value.pending.every((candidate) => isRcPendingRequestSummary(candidate));
|
|
65432
|
+
}
|
|
65433
|
+
function isRcPendingRequestSummary(value) {
|
|
65434
|
+
if (typeof value !== "object" || value === null) {
|
|
65435
|
+
return false;
|
|
65436
|
+
}
|
|
65437
|
+
if (!("sessionId" in value) || !("requestId" in value) || !("type" in value) || !("summary" in value) || !("observedAt" in value)) {
|
|
65438
|
+
return false;
|
|
65439
|
+
}
|
|
65440
|
+
return typeof value.sessionId === "string" && typeof value.requestId === "string" && typeof value.type === "string" && typeof value.summary === "string" && typeof value.observedAt === "number";
|
|
65441
|
+
}
|
|
65359
65442
|
function sequenceNumsOfBody(body) {
|
|
65360
65443
|
let parsed;
|
|
65361
65444
|
try {
|
|
@@ -65378,6 +65461,13 @@ function sequenceNumsOfBody(body) {
|
|
|
65378
65461
|
}
|
|
65379
65462
|
function frontDoorRcControl(transport, token) {
|
|
65380
65463
|
const headers = { authorization: `Bearer ${token}` };
|
|
65464
|
+
const listAnswerOf = async (path41, what) => {
|
|
65465
|
+
try {
|
|
65466
|
+
return await transport.request({ method: "GET", path: path41, headers });
|
|
65467
|
+
} catch (error62) {
|
|
65468
|
+
throw new Error(`${what}: could not reach the front door's control listener: ${error62 instanceof Error ? error62.message : String(error62)}`, { cause: error62 });
|
|
65469
|
+
}
|
|
65470
|
+
};
|
|
65381
65471
|
const sessionsOf = (answer, what) => {
|
|
65382
65472
|
if (answer.status !== HTTP_STATUS.ok) {
|
|
65383
65473
|
throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
|
|
@@ -65401,29 +65491,77 @@ function frontDoorRcControl(transport, token) {
|
|
|
65401
65491
|
}
|
|
65402
65492
|
return sessions;
|
|
65403
65493
|
};
|
|
65404
|
-
|
|
65405
|
-
|
|
65406
|
-
|
|
65407
|
-
|
|
65408
|
-
|
|
65409
|
-
|
|
65410
|
-
|
|
65411
|
-
|
|
65412
|
-
|
|
65413
|
-
|
|
65414
|
-
|
|
65415
|
-
|
|
65416
|
-
|
|
65494
|
+
const statusesOf = (answer, what) => {
|
|
65495
|
+
if (answer.status !== HTTP_STATUS.ok) {
|
|
65496
|
+
throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
|
|
65497
|
+
}
|
|
65498
|
+
let parsed;
|
|
65499
|
+
try {
|
|
65500
|
+
parsed = JSON.parse(answer.body);
|
|
65501
|
+
} catch {
|
|
65502
|
+
throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
|
|
65503
|
+
}
|
|
65504
|
+
if (typeof parsed !== "object" || parsed === null || !("statuses" in parsed) || !Array.isArray(parsed.statuses)) {
|
|
65505
|
+
throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
|
|
65506
|
+
}
|
|
65507
|
+
const statuses = [];
|
|
65508
|
+
for (const value of parsed.statuses) {
|
|
65509
|
+
if (!isRcSessionStatus(value)) {
|
|
65510
|
+
throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
|
|
65417
65511
|
}
|
|
65418
|
-
|
|
65419
|
-
|
|
65420
|
-
|
|
65421
|
-
|
|
65422
|
-
|
|
65423
|
-
|
|
65512
|
+
statuses.push(value);
|
|
65513
|
+
}
|
|
65514
|
+
return statuses;
|
|
65515
|
+
};
|
|
65516
|
+
const pendingOfAnswer = (answer, what) => {
|
|
65517
|
+
if (answer.status !== HTTP_STATUS.ok) {
|
|
65518
|
+
throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
|
|
65519
|
+
}
|
|
65520
|
+
let parsed;
|
|
65521
|
+
try {
|
|
65522
|
+
parsed = JSON.parse(answer.body);
|
|
65523
|
+
} catch {
|
|
65524
|
+
throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
|
|
65525
|
+
}
|
|
65526
|
+
if (typeof parsed !== "object" || parsed === null || !("pending" in parsed) || !Array.isArray(parsed.pending)) {
|
|
65527
|
+
throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
|
|
65528
|
+
}
|
|
65529
|
+
const pending = [];
|
|
65530
|
+
for (const value of parsed.pending) {
|
|
65531
|
+
if (!isRcPendingRequestSummary(value)) {
|
|
65532
|
+
throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
|
|
65424
65533
|
}
|
|
65425
|
-
|
|
65534
|
+
pending.push(value);
|
|
65426
65535
|
}
|
|
65536
|
+
return pending;
|
|
65537
|
+
};
|
|
65538
|
+
const writeOf = async (path41, body, describeFailure) => {
|
|
65539
|
+
let answer;
|
|
65540
|
+
try {
|
|
65541
|
+
answer = await transport.request({ method: "POST", path: path41, headers: { ...headers, "content-type": "application/json" }, body });
|
|
65542
|
+
} catch (error62) {
|
|
65543
|
+
return { ok: false, message: `could not reach the front door's control listener: ${error62 instanceof Error ? error62.message : String(error62)}` };
|
|
65544
|
+
}
|
|
65545
|
+
if (answer.status === HTTP_STATUS.ok) {
|
|
65546
|
+
const sequenceNums = sequenceNumsOfBody(answer.body);
|
|
65547
|
+
if (sequenceNums === void 0) {
|
|
65548
|
+
return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
|
|
65549
|
+
}
|
|
65550
|
+
return { ok: true, sequenceNums };
|
|
65551
|
+
}
|
|
65552
|
+
return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
|
|
65553
|
+
};
|
|
65554
|
+
const sessionQuery = (sessionId) => sessionId === void 0 ? "" : `?session=${encodeURIComponent(sessionId)}`;
|
|
65555
|
+
return {
|
|
65556
|
+
listSessions: async () => sessionsOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/sessions`, "listing the door's Remote Control sessions"), "listing the door's Remote Control sessions"),
|
|
65557
|
+
statusOf: async (sessionId) => statusesOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/status${sessionQuery(sessionId)}`, "reading the door's Remote Control session status"), "reading the door's Remote Control session status"),
|
|
65558
|
+
pendingOf: async (sessionId) => pendingOfAnswer(await listAnswerOf(`${CONTROL_PATH_PREFIX}/pending${sessionQuery(sessionId)}`, "listing the door's pending Remote Control control requests"), "listing the door's pending Remote Control control requests"),
|
|
65559
|
+
sendPrompt: async (sessionId, text2) => await writeOf(`${CONTROL_PATH_PREFIX}/inject`, JSON.stringify({ session: sessionId, text: text2 }), (status, message) => `injecting into ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
65560
|
+
answerRequest: async (sessionId, requestId, decision) => await writeOf(
|
|
65561
|
+
`${CONTROL_PATH_PREFIX}/answer`,
|
|
65562
|
+
JSON.stringify({ session: sessionId, request: requestId, approve: decision.approve, ...decision.approve || decision.message === void 0 ? {} : { text: decision.message } }),
|
|
65563
|
+
(status, message) => `answering control request ${requestId} on ${sessionId} failed (HTTP ${String(status)}): ${message}`
|
|
65564
|
+
)
|
|
65427
65565
|
};
|
|
65428
65566
|
}
|
|
65429
65567
|
|
|
@@ -66787,6 +66925,9 @@ var RC_SESSION_ID_PREFIX = "cse_";
|
|
|
66787
66925
|
var RC_IDLE_EXPIRY_MS = 45e3;
|
|
66788
66926
|
var RC_CREATE_RESPONSE_CAP_BYTES = 8192;
|
|
66789
66927
|
var CONFLICT_REASON_HEADER = "x-ccr-conflict-reason";
|
|
66928
|
+
var RC_PENDING_DEADLINE_MS = 14e3;
|
|
66929
|
+
var RC_REQUEST_PARSE_CAP_BYTES = 262144;
|
|
66930
|
+
var RC_PENDING_SUMMARY_EXCERPT_CHARS = 300;
|
|
66790
66931
|
var SUCCESS_STATUS_MAX_EXCLUSIVE = 300;
|
|
66791
66932
|
function isSuccessful(status) {
|
|
66792
66933
|
return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE;
|
|
@@ -66812,20 +66953,124 @@ function sessionCreateId(body) {
|
|
|
66812
66953
|
const id = session.id;
|
|
66813
66954
|
return typeof id === "string" && id.startsWith(RC_SESSION_ID_PREFIX) ? id : void 0;
|
|
66814
66955
|
}
|
|
66956
|
+
function isRecord3(value) {
|
|
66957
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66958
|
+
}
|
|
66959
|
+
function parseJsonObject(body) {
|
|
66960
|
+
let parsed;
|
|
66961
|
+
try {
|
|
66962
|
+
parsed = JSON.parse(body);
|
|
66963
|
+
} catch {
|
|
66964
|
+
return void 0;
|
|
66965
|
+
}
|
|
66966
|
+
return isRecord3(parsed) ? parsed : void 0;
|
|
66967
|
+
}
|
|
66968
|
+
function nonEmptyString(record2, field) {
|
|
66969
|
+
const value = record2 === void 0 ? void 0 : record2[field];
|
|
66970
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
66971
|
+
}
|
|
66972
|
+
function finiteNumber(record2, field) {
|
|
66973
|
+
const value = record2 === void 0 ? void 0 : record2[field];
|
|
66974
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
66975
|
+
}
|
|
66976
|
+
function excerpt(text2) {
|
|
66977
|
+
return text2.length <= RC_PENDING_SUMMARY_EXCERPT_CHARS ? text2 : `${text2.slice(0, RC_PENDING_SUMMARY_EXCERPT_CHARS)}...`;
|
|
66978
|
+
}
|
|
66979
|
+
function payloadEventsOf(body) {
|
|
66980
|
+
const parsed = parseJsonObject(body);
|
|
66981
|
+
const events = parsed === void 0 ? void 0 : parsed.events;
|
|
66982
|
+
if (!Array.isArray(events)) {
|
|
66983
|
+
return [];
|
|
66984
|
+
}
|
|
66985
|
+
const payloads = [];
|
|
66986
|
+
for (const event of events) {
|
|
66987
|
+
if (!isRecord3(event)) {
|
|
66988
|
+
continue;
|
|
66989
|
+
}
|
|
66990
|
+
const payload = event.payload;
|
|
66991
|
+
if (!isRecord3(payload)) {
|
|
66992
|
+
continue;
|
|
66993
|
+
}
|
|
66994
|
+
payloads.push(payload);
|
|
66995
|
+
}
|
|
66996
|
+
return payloads;
|
|
66997
|
+
}
|
|
66998
|
+
function pendingOfControlRequestPayload(payload) {
|
|
66999
|
+
if (payload.type !== "control_request") {
|
|
67000
|
+
return void 0;
|
|
67001
|
+
}
|
|
67002
|
+
const requestId = nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
|
|
67003
|
+
if (requestId === void 0) {
|
|
67004
|
+
return void 0;
|
|
67005
|
+
}
|
|
67006
|
+
const request7 = isRecord3(payload.request) ? payload.request : void 0;
|
|
67007
|
+
const subtype = nonEmptyString(request7, "subtype");
|
|
67008
|
+
const type = subtype ?? "control_request";
|
|
67009
|
+
let summary = "";
|
|
67010
|
+
if (subtype === "can_use_tool") {
|
|
67011
|
+
const toolName = nonEmptyString(request7, "tool_name") ?? "unknown tool";
|
|
67012
|
+
const input2 = request7 === void 0 ? void 0 : request7.input;
|
|
67013
|
+
summary = input2 === void 0 ? toolName : `${toolName} ${excerpt(JSON.stringify(input2))}`;
|
|
67014
|
+
} else if (request7 !== void 0) {
|
|
67015
|
+
summary = excerpt(JSON.stringify(request7));
|
|
67016
|
+
}
|
|
67017
|
+
return { requestId, type, summary };
|
|
67018
|
+
}
|
|
67019
|
+
function answeredRequestIdsOfControlResponsePayload(payload) {
|
|
67020
|
+
if (payload.type !== "control_response") {
|
|
67021
|
+
return [];
|
|
67022
|
+
}
|
|
67023
|
+
const response = isRecord3(payload.response) ? payload.response : void 0;
|
|
67024
|
+
const id = nonEmptyString(response, "request_id") ?? nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
|
|
67025
|
+
return id === void 0 ? [] : [id];
|
|
67026
|
+
}
|
|
67027
|
+
function parseRcRequestBody(kind, body) {
|
|
67028
|
+
if (kind === "heartbeat") {
|
|
67029
|
+
return { controlRequests: [], answeredRequestIds: [], idleSeconds: finiteNumber(parseJsonObject(body), "idle_seconds"), workerStatus: void 0 };
|
|
67030
|
+
}
|
|
67031
|
+
if (kind === "worker-registration") {
|
|
67032
|
+
return { controlRequests: [], answeredRequestIds: [], idleSeconds: void 0, workerStatus: nonEmptyString(parseJsonObject(body), "status") };
|
|
67033
|
+
}
|
|
67034
|
+
const controlRequests = [];
|
|
67035
|
+
const answeredRequestIds = [];
|
|
67036
|
+
for (const payload of payloadEventsOf(body)) {
|
|
67037
|
+
const pending = pendingOfControlRequestPayload(payload);
|
|
67038
|
+
if (pending !== void 0) {
|
|
67039
|
+
controlRequests.push({ requestId: pending.requestId, type: pending.type, summary: pending.summary });
|
|
67040
|
+
continue;
|
|
67041
|
+
}
|
|
67042
|
+
answeredRequestIds.push(...answeredRequestIdsOfControlResponsePayload(payload));
|
|
67043
|
+
}
|
|
67044
|
+
return { controlRequests, answeredRequestIds, idleSeconds: void 0, workerStatus: void 0 };
|
|
67045
|
+
}
|
|
66815
67046
|
function createRcSessionTracker(deps) {
|
|
66816
67047
|
const entries = /* @__PURE__ */ new Map();
|
|
66817
67048
|
const expire = (id) => {
|
|
66818
67049
|
entries.delete(id);
|
|
66819
67050
|
};
|
|
67051
|
+
const expirePendings = (entry, now) => {
|
|
67052
|
+
for (const [requestId, pending] of entry.pending) {
|
|
67053
|
+
if (now - pending.observedAt >= RC_PENDING_DEADLINE_MS) {
|
|
67054
|
+
entry.pending.delete(requestId);
|
|
67055
|
+
}
|
|
67056
|
+
}
|
|
67057
|
+
};
|
|
66820
67058
|
const sweep = () => {
|
|
66821
67059
|
for (const [id, entry] of entries) {
|
|
66822
67060
|
if (deps.now() - entry.lastSeenAt >= deps.idleMs) {
|
|
66823
67061
|
expire(id);
|
|
67062
|
+
continue;
|
|
66824
67063
|
}
|
|
67064
|
+
expirePendings(entry, deps.now());
|
|
66825
67065
|
}
|
|
66826
67066
|
};
|
|
66827
67067
|
const timer = setInterval(sweep, deps.idleMs);
|
|
66828
67068
|
timer.unref();
|
|
67069
|
+
const birth = (id, facts) => {
|
|
67070
|
+
const record2 = { id, createdAt: deps.now(), lastSeenAt: deps.now(), ...facts, pending: /* @__PURE__ */ new Map(), workerState: void 0, workerIdleSeconds: void 0 };
|
|
67071
|
+
entries.set(id, record2);
|
|
67072
|
+
return record2;
|
|
67073
|
+
};
|
|
66829
67074
|
const observeExchange = (request7) => {
|
|
66830
67075
|
const method = (request7.method ?? "").toUpperCase();
|
|
66831
67076
|
const path41 = new URL(request7.url ?? "/", "http://127.0.0.1").pathname;
|
|
@@ -66842,30 +67087,30 @@ function createRcSessionTracker(deps) {
|
|
|
66842
67087
|
if (method !== "POST") {
|
|
66843
67088
|
return void 0;
|
|
66844
67089
|
}
|
|
66845
|
-
let
|
|
66846
|
-
let
|
|
67090
|
+
let accepted2 = false;
|
|
67091
|
+
let buffered2 = 0;
|
|
66847
67092
|
let body = "";
|
|
66848
67093
|
return {
|
|
66849
67094
|
onResponse: (status) => {
|
|
66850
|
-
|
|
67095
|
+
accepted2 = isSuccessful(status);
|
|
66851
67096
|
},
|
|
66852
67097
|
onBodyChunk: (chunk) => {
|
|
66853
|
-
if (!
|
|
67098
|
+
if (!accepted2 || buffered2 >= RC_CREATE_RESPONSE_CAP_BYTES) {
|
|
66854
67099
|
return;
|
|
66855
67100
|
}
|
|
66856
67101
|
const text2 = Buffer.from(chunk).toString("utf8");
|
|
66857
|
-
body = body + text2.slice(0, RC_CREATE_RESPONSE_CAP_BYTES -
|
|
66858
|
-
|
|
67102
|
+
body = body + text2.slice(0, RC_CREATE_RESPONSE_CAP_BYTES - buffered2);
|
|
67103
|
+
buffered2 = body.length;
|
|
66859
67104
|
},
|
|
66860
67105
|
onEnd: () => {
|
|
66861
|
-
if (!
|
|
67106
|
+
if (!accepted2) {
|
|
66862
67107
|
return;
|
|
66863
67108
|
}
|
|
66864
67109
|
const id2 = sessionCreateId(body);
|
|
66865
67110
|
if (id2 === void 0) {
|
|
66866
67111
|
return;
|
|
66867
67112
|
}
|
|
66868
|
-
|
|
67113
|
+
birth(id2, facts);
|
|
66869
67114
|
}
|
|
66870
67115
|
};
|
|
66871
67116
|
}
|
|
@@ -66874,21 +67119,69 @@ function createRcSessionTracker(deps) {
|
|
|
66874
67119
|
if (id?.startsWith(RC_SESSION_ID_PREFIX) !== true) {
|
|
66875
67120
|
return void 0;
|
|
66876
67121
|
}
|
|
66877
|
-
const entry = entries.get(id);
|
|
66878
|
-
|
|
66879
|
-
|
|
66880
|
-
|
|
66881
|
-
|
|
66882
|
-
|
|
66883
|
-
|
|
66884
|
-
|
|
66885
|
-
|
|
67122
|
+
const entry = entries.get(id) ?? birth(id, facts);
|
|
67123
|
+
entry.lastSeenAt = deps.now();
|
|
67124
|
+
entry.authorization = facts.authorization ?? entry.authorization;
|
|
67125
|
+
entry.anthropicVersion = facts.anthropicVersion ?? entry.anthropicVersion;
|
|
67126
|
+
entry.anthropicClientPlatform = facts.anthropicClientPlatform ?? entry.anthropicClientPlatform;
|
|
67127
|
+
const bodyKind = method === "POST" && rest === `${id}/worker/events` ? "worker-events" : method === "POST" && rest === `${id}/events` ? "client-events" : method === "POST" && rest === `${id}/worker/heartbeat` ? "heartbeat" : method === "PUT" && rest === `${id}/worker` ? "worker-registration" : void 0;
|
|
67128
|
+
let chunks = [];
|
|
67129
|
+
let buffered = 0;
|
|
67130
|
+
let oversized = false;
|
|
67131
|
+
let requestEnded = false;
|
|
67132
|
+
let accepted;
|
|
67133
|
+
let filed = false;
|
|
67134
|
+
const file2 = () => {
|
|
67135
|
+
if (filed || accepted !== true || !requestEnded || bodyKind === void 0) {
|
|
67136
|
+
return;
|
|
67137
|
+
}
|
|
67138
|
+
filed = true;
|
|
67139
|
+
const factsOfBody = oversized ? void 0 : parseRcRequestBody(bodyKind, Buffer.concat(chunks).toString("utf8"));
|
|
67140
|
+
chunks = [];
|
|
67141
|
+
const entryNow = entries.get(id);
|
|
67142
|
+
if (entryNow === void 0 || factsOfBody === void 0) {
|
|
67143
|
+
return;
|
|
67144
|
+
}
|
|
67145
|
+
const now = deps.now();
|
|
67146
|
+
for (const controlRequest of factsOfBody.controlRequests) {
|
|
67147
|
+
entryNow.pending.set(controlRequest.requestId, { sessionId: id, ...controlRequest, observedAt: now });
|
|
67148
|
+
}
|
|
67149
|
+
for (const requestId of factsOfBody.answeredRequestIds) {
|
|
67150
|
+
entryNow.pending.delete(requestId);
|
|
67151
|
+
}
|
|
67152
|
+
if (factsOfBody.idleSeconds !== void 0) {
|
|
67153
|
+
entryNow.workerIdleSeconds = { value: factsOfBody.idleSeconds, observedAt: now };
|
|
67154
|
+
}
|
|
67155
|
+
if (factsOfBody.workerStatus !== void 0) {
|
|
67156
|
+
entryNow.workerState = { value: factsOfBody.workerStatus, observedAt: now };
|
|
67157
|
+
}
|
|
67158
|
+
};
|
|
66886
67159
|
const archiving = method === "POST" && rest === `${id}/archive`;
|
|
66887
67160
|
return {
|
|
67161
|
+
...bodyKind === void 0 ? {} : {
|
|
67162
|
+
onRequestChunk: (chunk) => {
|
|
67163
|
+
if (oversized) {
|
|
67164
|
+
return;
|
|
67165
|
+
}
|
|
67166
|
+
chunks.push(Buffer.from(chunk));
|
|
67167
|
+
buffered += chunk.length;
|
|
67168
|
+
if (buffered > RC_REQUEST_PARSE_CAP_BYTES) {
|
|
67169
|
+
oversized = true;
|
|
67170
|
+
chunks = [];
|
|
67171
|
+
}
|
|
67172
|
+
},
|
|
67173
|
+
onRequestEnd: () => {
|
|
67174
|
+
requestEnded = true;
|
|
67175
|
+
file2();
|
|
67176
|
+
}
|
|
67177
|
+
},
|
|
66888
67178
|
onResponse: (status, headers) => {
|
|
66889
67179
|
if (singleHeader(headers, CONFLICT_REASON_HEADER) !== void 0 || archiving && isSuccessful(status)) {
|
|
66890
67180
|
expire(id);
|
|
67181
|
+
return;
|
|
66891
67182
|
}
|
|
67183
|
+
accepted = isSuccessful(status);
|
|
67184
|
+
file2();
|
|
66892
67185
|
},
|
|
66893
67186
|
onBodyChunk: () => {
|
|
66894
67187
|
},
|
|
@@ -66896,16 +67189,31 @@ function createRcSessionTracker(deps) {
|
|
|
66896
67189
|
}
|
|
66897
67190
|
};
|
|
66898
67191
|
};
|
|
67192
|
+
const summaryOf = ({ id, createdAt, lastSeenAt }) => ({ id, createdAt, lastSeenAt });
|
|
67193
|
+
const pendingSummaryOf = ({ sessionId, requestId, type, summary, observedAt: observedAt2 }) => ({ sessionId, requestId, type, summary, observedAt: observedAt2 });
|
|
67194
|
+
const sortedEntries = () => [...entries.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1));
|
|
67195
|
+
const pendingSorted = (entry) => [...entry.pending.values()].sort((left, right) => left.observedAt - right.observedAt || (left.requestId < right.requestId ? -1 : 1)).map(pendingSummaryOf);
|
|
66899
67196
|
return {
|
|
66900
67197
|
observeExchange,
|
|
66901
67198
|
list: () => {
|
|
66902
67199
|
sweep();
|
|
66903
|
-
return
|
|
67200
|
+
return sortedEntries().map(summaryOf);
|
|
67201
|
+
},
|
|
67202
|
+
statusOf: (sessionId) => {
|
|
67203
|
+
sweep();
|
|
67204
|
+
return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).map((entry) => ({ ...summaryOf(entry), workerState: entry.workerState, workerIdleSeconds: entry.workerIdleSeconds, pending: pendingSorted(entry) }));
|
|
67205
|
+
},
|
|
67206
|
+
pendingOf: (sessionId) => {
|
|
67207
|
+
sweep();
|
|
67208
|
+
return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).flatMap((entry) => pendingSorted(entry));
|
|
66904
67209
|
},
|
|
66905
67210
|
credentialOf: (sessionId) => {
|
|
66906
67211
|
const entry = entries.get(sessionId);
|
|
66907
67212
|
return entry === void 0 ? void 0 : { authorization: entry.authorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform };
|
|
66908
67213
|
},
|
|
67214
|
+
completePending: (sessionId, requestId) => {
|
|
67215
|
+
entries.get(sessionId)?.pending.delete(requestId);
|
|
67216
|
+
},
|
|
66909
67217
|
close: () => {
|
|
66910
67218
|
clearInterval(timer);
|
|
66911
67219
|
}
|
|
@@ -66916,6 +67224,14 @@ function observingRoutedRoute(route, tracker) {
|
|
|
66916
67224
|
...route,
|
|
66917
67225
|
serve: async (request7, response) => {
|
|
66918
67226
|
const observed = tracker.observeExchange({ method: request7.method, url: request7.url, headers: request7.headers });
|
|
67227
|
+
if (observed?.onRequestChunk !== void 0) {
|
|
67228
|
+
request7.body.on("data", (chunk) => {
|
|
67229
|
+
observed.onRequestChunk?.(chunk);
|
|
67230
|
+
});
|
|
67231
|
+
request7.body.on("end", () => {
|
|
67232
|
+
observed.onRequestEnd?.();
|
|
67233
|
+
});
|
|
67234
|
+
}
|
|
66919
67235
|
await route.serve(request7, observed === void 0 ? response : teeRoutedResponse(response, observed));
|
|
66920
67236
|
}
|
|
66921
67237
|
};
|
|
@@ -66982,7 +67298,7 @@ function sequenceNumsOf(body) {
|
|
|
66982
67298
|
}
|
|
66983
67299
|
return sequenceNums.length === 0 ? void 0 : sequenceNums;
|
|
66984
67300
|
}
|
|
66985
|
-
function
|
|
67301
|
+
function rcEventWriteResultFromAnswer(answer) {
|
|
66986
67302
|
if (!isSuccessful(answer.status)) {
|
|
66987
67303
|
if (answer.status === HTTP_STATUS.unauthorized) {
|
|
66988
67304
|
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}` };
|
|
@@ -66995,11 +67311,7 @@ function rcInjectResultFromAnswer(answer) {
|
|
|
66995
67311
|
}
|
|
66996
67312
|
return { ok: true, sequenceNums };
|
|
66997
67313
|
}
|
|
66998
|
-
|
|
66999
|
-
const credential = deps.credentialOf(sessionId);
|
|
67000
|
-
if (credential === void 0) {
|
|
67001
|
-
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)` };
|
|
67002
|
-
}
|
|
67314
|
+
function rcWriteHeaders(credential) {
|
|
67003
67315
|
const headers = { "content-type": "application/json" };
|
|
67004
67316
|
if (credential.authorization !== void 0) {
|
|
67005
67317
|
headers.authorization = credential.authorization;
|
|
@@ -67010,14 +67322,46 @@ async function injectRcUserMessage(deps, sessionId, text2) {
|
|
|
67010
67322
|
if (credential.anthropicClientPlatform !== void 0) {
|
|
67011
67323
|
headers["anthropic-client-platform"] = credential.anthropicClientPlatform;
|
|
67012
67324
|
}
|
|
67325
|
+
return headers;
|
|
67326
|
+
}
|
|
67327
|
+
async function injectRcUserMessage(deps, sessionId, text2) {
|
|
67328
|
+
const credential = deps.credentialOf(sessionId);
|
|
67329
|
+
if (credential === void 0) {
|
|
67330
|
+
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)` };
|
|
67331
|
+
}
|
|
67013
67332
|
const body = JSON.stringify(buildRcEventWriteBody(buildRcUserMessagePayload(deps.newUuid(), sessionId, text2)));
|
|
67014
67333
|
let answer;
|
|
67015
67334
|
try {
|
|
67016
|
-
answer = await deps.dial.writeEvents(sessionId,
|
|
67335
|
+
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
67017
67336
|
} catch (error62) {
|
|
67018
67337
|
return { ok: false, message: `the door could not reach the API host to inject into session ${sessionId}: ${error62 instanceof Error ? error62.message : String(error62)}` };
|
|
67019
67338
|
}
|
|
67020
|
-
return
|
|
67339
|
+
return rcEventWriteResultFromAnswer(answer);
|
|
67340
|
+
}
|
|
67341
|
+
function buildRcControlResponsePayload(requestId, decision) {
|
|
67342
|
+
const result = decision.approve ? { behavior: "allow" } : { behavior: "deny", message: decision.message ?? "" };
|
|
67343
|
+
return { type: "control_response", response: { subtype: "success", request_id: requestId, response: result } };
|
|
67344
|
+
}
|
|
67345
|
+
async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
67346
|
+
const credential = deps.credentialOf(sessionId);
|
|
67347
|
+
if (credential === void 0) {
|
|
67348
|
+
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)` };
|
|
67349
|
+
}
|
|
67350
|
+
if (!deps.pendingOf(sessionId).some((pending) => pending.requestId === requestId)) {
|
|
67351
|
+
return { ok: false, message: `the front door has not observed control request ${requestId} pending on session ${sessionId}: it was answered already (by this door or by an attached client), it passed its answer deadline (the protocol's permission round-trip bound), or it never passed through this door` };
|
|
67352
|
+
}
|
|
67353
|
+
const body = JSON.stringify(buildRcEventWriteBody(buildRcControlResponsePayload(requestId, decision)));
|
|
67354
|
+
let answer;
|
|
67355
|
+
try {
|
|
67356
|
+
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
67357
|
+
} catch (error62) {
|
|
67358
|
+
return { ok: false, message: `the door could not reach the API host to answer on session ${sessionId}: ${error62 instanceof Error ? error62.message : String(error62)}` };
|
|
67359
|
+
}
|
|
67360
|
+
const result = rcEventWriteResultFromAnswer(answer);
|
|
67361
|
+
if (result.ok) {
|
|
67362
|
+
deps.completePending(sessionId, requestId);
|
|
67363
|
+
}
|
|
67364
|
+
return result;
|
|
67021
67365
|
}
|
|
67022
67366
|
|
|
67023
67367
|
// src/usage/providerQuota.ts
|
|
@@ -67377,7 +67721,9 @@ function realFrontDoorSupervisorPorts(paths) {
|
|
|
67377
67721
|
isRunning: realIsProcessRunning,
|
|
67378
67722
|
startProviderListener: async (preferredPort) => {
|
|
67379
67723
|
const authority = loadCa();
|
|
67380
|
-
const
|
|
67724
|
+
const rcDial = realRcEventDial();
|
|
67725
|
+
const rcInject = async (sessionId, text2) => await injectRcUserMessage({ credentialOf: rcTracker.credentialOf, dial: rcDial, newUuid: import_node_crypto11.randomUUID }, sessionId, text2);
|
|
67726
|
+
const rcAnswer = async (sessionId, requestId, decision) => await answerRcControlRequest({ credentialOf: rcTracker.credentialOf, pendingOf: (id) => rcTracker.pendingOf(id), completePending: rcTracker.completePending, dial: rcDial }, sessionId, requestId, decision);
|
|
67381
67727
|
realFarmFs.mkdirp(paths.frontdoorDir);
|
|
67382
67728
|
realFarmFs.writeFilePrivate(paths.frontdoorControlTokenFile, `${rcControlToken}
|
|
67383
67729
|
`);
|
|
@@ -67387,7 +67733,7 @@ function realFrontDoorSupervisorPorts(paths) {
|
|
|
67387
67733
|
},
|
|
67388
67734
|
log,
|
|
67389
67735
|
mintLeaf(authority, LOOPBACK_LEAF_NAMES, /* @__PURE__ */ new Date()),
|
|
67390
|
-
createRcControlHandler({ expectedToken: rcControlToken, list: rcTracker.list, inject: rcInject })
|
|
67736
|
+
createRcControlHandler({ expectedToken: rcControlToken, list: rcTracker.list, statusOf: rcTracker.statusOf, pendingOf: rcTracker.pendingOf, inject: rcInject, answer: rcAnswer })
|
|
67391
67737
|
);
|
|
67392
67738
|
const handle = await listenFrontDoor(server, { ...preferredPort === void 0 ? {} : { preferredPort }, ca: authority.certPem, onError: onListenerError("provider") });
|
|
67393
67739
|
return {
|
|
@@ -67516,6 +67862,35 @@ function formatRcSessionList(sessions) {
|
|
|
67516
67862
|
}
|
|
67517
67863
|
return sessions.map((session) => `${session.id} created ${new Date(session.createdAt).toISOString()} last seen ${new Date(session.lastSeenAt).toISOString()}`);
|
|
67518
67864
|
}
|
|
67865
|
+
function formatRcSessionStatus(statuses) {
|
|
67866
|
+
if (statuses.length === 0) {
|
|
67867
|
+
return ["no Remote Control sessions observed"];
|
|
67868
|
+
}
|
|
67869
|
+
const lines = [];
|
|
67870
|
+
for (const status of statuses) {
|
|
67871
|
+
lines.push(`${status.id} created ${new Date(status.createdAt).toISOString()} last seen ${new Date(status.lastSeenAt).toISOString()}`);
|
|
67872
|
+
const worker = status.workerState === void 0 && status.workerIdleSeconds === void 0 ? "not observed" : [
|
|
67873
|
+
status.workerState === void 0 ? "state unknown" : `state ${status.workerState.value} since ${new Date(status.workerState.observedAt).toISOString()}`,
|
|
67874
|
+
status.workerIdleSeconds === void 0 ? "idle unknown" : `idle ${String(status.workerIdleSeconds.value)} s as of ${new Date(status.workerIdleSeconds.observedAt).toISOString()}`
|
|
67875
|
+
].join(", ");
|
|
67876
|
+
lines.push(` worker: ${worker}`);
|
|
67877
|
+
if (status.pending.length === 0) {
|
|
67878
|
+
lines.push(" pending: none");
|
|
67879
|
+
} else {
|
|
67880
|
+
lines.push(" pending:");
|
|
67881
|
+
for (const pending of status.pending) {
|
|
67882
|
+
lines.push(...formatRcPendingList([pending]).map((line) => ` ${line}`));
|
|
67883
|
+
}
|
|
67884
|
+
}
|
|
67885
|
+
}
|
|
67886
|
+
return lines;
|
|
67887
|
+
}
|
|
67888
|
+
function formatRcPendingList(pending) {
|
|
67889
|
+
if (pending.length === 0) {
|
|
67890
|
+
return ["no pending control requests observed"];
|
|
67891
|
+
}
|
|
67892
|
+
return pending.map((request7) => `${request7.sessionId} ${request7.requestId} ${request7.type}${request7.summary === "" ? "" : ` ${request7.summary}`} observed ${new Date(request7.observedAt).toISOString()}`);
|
|
67893
|
+
}
|
|
67519
67894
|
function frontDoorRcControlFromState(fsPort, paths) {
|
|
67520
67895
|
const state = readFrontDoorState(fsPort, paths.frontdoorStateFile);
|
|
67521
67896
|
if (state?.port === void 0) {
|
|
@@ -67549,9 +67924,10 @@ function registerFrontDoorCommand(program2, deps) {
|
|
|
67549
67924
|
}),
|
|
67550
67925
|
["agent-shim frontdoor status", "agent-shim frontdoor status --json"]
|
|
67551
67926
|
);
|
|
67552
|
-
const rc = withExamples(
|
|
67553
|
-
"
|
|
67554
|
-
|
|
67927
|
+
const rc = withExamples(
|
|
67928
|
+
frontdoor.command("rc").description("Observe the Remote Control sessions passing through the front door, send a prompt into one, and answer its pending control requests."),
|
|
67929
|
+
["agent-shim frontdoor rc list", "agent-shim frontdoor rc pending"]
|
|
67930
|
+
);
|
|
67555
67931
|
withExamples(
|
|
67556
67932
|
rc.command("list").description("List the Remote Control sessions the front door has observed, with when each was created and last seen. Read-only.").option("--json", "Print the sessions as JSON.").action(async (options) => {
|
|
67557
67933
|
const sessions = await frontDoorRcControlFromState(realFarmFs, paths).listSessions();
|
|
@@ -67565,6 +67941,32 @@ function registerFrontDoorCommand(program2, deps) {
|
|
|
67565
67941
|
}),
|
|
67566
67942
|
["agent-shim frontdoor rc list", "agent-shim frontdoor rc list --json"]
|
|
67567
67943
|
);
|
|
67944
|
+
withExamples(
|
|
67945
|
+
rc.command("status").description("Report each observed Remote Control session's worker state and idle from its latest observed heartbeat and registration, with its pending control requests. Read-only.").argument("[session]", "One cse_ session id, as `frontdoor rc list` shows it; every observed session when omitted.").option("--json", "Print the statuses as JSON.").action(async (session, options) => {
|
|
67946
|
+
const statuses = await frontDoorRcControlFromState(realFarmFs, paths).statusOf(session);
|
|
67947
|
+
if (options.json === true) {
|
|
67948
|
+
printJson({ statuses });
|
|
67949
|
+
return;
|
|
67950
|
+
}
|
|
67951
|
+
for (const line of formatRcSessionStatus(statuses)) {
|
|
67952
|
+
console.log(line);
|
|
67953
|
+
}
|
|
67954
|
+
}),
|
|
67955
|
+
["agent-shim frontdoor rc status", "agent-shim frontdoor rc status cse_00000000-0000-4000-8000-000000000000 --json"]
|
|
67956
|
+
);
|
|
67957
|
+
withExamples(
|
|
67958
|
+
rc.command("pending").description("List the control requests (tool and plan approvals above all) the front door has observed and that are still awaiting an answer. Read-only.").argument("[session]", "One cse_ session id, as `frontdoor rc list` shows it; every observed session when omitted.").option("--json", "Print the pending requests as JSON.").action(async (session, options) => {
|
|
67959
|
+
const pending = await frontDoorRcControlFromState(realFarmFs, paths).pendingOf(session);
|
|
67960
|
+
if (options.json === true) {
|
|
67961
|
+
printJson({ pending });
|
|
67962
|
+
return;
|
|
67963
|
+
}
|
|
67964
|
+
for (const line of formatRcPendingList(pending)) {
|
|
67965
|
+
console.log(line);
|
|
67966
|
+
}
|
|
67967
|
+
}),
|
|
67968
|
+
["agent-shim frontdoor rc pending", "agent-shim frontdoor rc pending --json"]
|
|
67969
|
+
);
|
|
67568
67970
|
withExamples(
|
|
67569
67971
|
rc.command("send").description("Send a text prompt into one observed Remote Control session, arriving as a message from an attached client would.").requiredOption("--session <id>", "The cse_ session id, as `frontdoor rc list` shows it.").requiredOption("--text <text>", "The prompt text to deliver.").option("--json", "Print the delivery result as JSON.").action(async (options) => {
|
|
67570
67972
|
const result = await frontDoorRcControlFromState(realFarmFs, paths).sendPrompt(options.session, options.text);
|
|
@@ -67579,6 +67981,29 @@ function registerFrontDoorCommand(program2, deps) {
|
|
|
67579
67981
|
}),
|
|
67580
67982
|
['agent-shim frontdoor rc send --session cse_00000000-0000-4000-8000-000000000000 --text "run the tests"']
|
|
67581
67983
|
);
|
|
67984
|
+
withExamples(
|
|
67985
|
+
rc.command("answer").description("Answer one pending control request on an observed session, approving or denying it as an attached client's approval would.").requiredOption("--session <id>", "The cse_ session id, as `frontdoor rc list` shows it.").requiredOption("--request <id>", "The control request's id, as `frontdoor rc pending` shows it.").option("--approve", "Approve the request.").option("--deny", "Deny the request.").option("--text <text>", "The denial message the worker sees; applies to --deny only.").option("--json", "Print the delivery result as JSON.").action(async (options) => {
|
|
67986
|
+
if (options.approve === options.deny) {
|
|
67987
|
+
throw new UsageError("answer exactly one way: pass --approve or --deny, not both and not neither");
|
|
67988
|
+
}
|
|
67989
|
+
if (options.approve === true && options.text !== void 0) {
|
|
67990
|
+
throw new UsageError("--text is the denial message; an approval carries no text (the protocol's allow result has no message field)");
|
|
67991
|
+
}
|
|
67992
|
+
const result = await frontDoorRcControlFromState(realFarmFs, paths).answerRequest(options.session, options.request, { approve: options.approve === true, message: options.deny === true ? options.text : void 0 });
|
|
67993
|
+
if (!result.ok) {
|
|
67994
|
+
throw new Error(result.message);
|
|
67995
|
+
}
|
|
67996
|
+
if (options.json === true) {
|
|
67997
|
+
printJson({ session: options.session, request: options.request, sequenceNums: result.sequenceNums });
|
|
67998
|
+
return;
|
|
67999
|
+
}
|
|
68000
|
+
console.log(`${options.approve === true ? "approved" : "denied"} ${options.request} on ${options.session}: sequence_num ${result.sequenceNums.join(", ")}`);
|
|
68001
|
+
}),
|
|
68002
|
+
[
|
|
68003
|
+
"agent-shim frontdoor rc answer --session cse_00000000-0000-4000-8000-000000000000 --request req_00000000-0000-4000-8000-000000000000 --approve",
|
|
68004
|
+
'agent-shim frontdoor rc answer --session cse_00000000-0000-4000-8000-000000000000 --request req_00000000-0000-4000-8000-000000000000 --deny --text "not today"'
|
|
68005
|
+
]
|
|
68006
|
+
);
|
|
67582
68007
|
program2.command("__frontdoor-supervisor", { hidden: true }).description("Internal: serve the front-door routing listeners. Started by the launcher; never run by hand.").allowUnknownOption().action(async () => {
|
|
67583
68008
|
const idleShutdownMinutes = readGlobalConfig(paths)?.frontdoor?.idleShutdownMinutes ?? FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES;
|
|
67584
68009
|
process.on("SIGTERM", () => {
|
|
@@ -69247,7 +69672,7 @@ function headroomSpawnEnv(parentEnv, allowlist, settings2, trustBundlePath) {
|
|
|
69247
69672
|
};
|
|
69248
69673
|
}
|
|
69249
69674
|
function headroomProxyArgs(socketPath, settings2) {
|
|
69250
|
-
return ["proxy", "--uds", socketPath, ...settingsArgs(settings2)];
|
|
69675
|
+
return ["proxy", "--uds", socketPath, "--no-rate-limit", ...settingsArgs(settings2)];
|
|
69251
69676
|
}
|
|
69252
69677
|
async function readyOverSocket(socketPath) {
|
|
69253
69678
|
return await new Promise((resolve) => {
|