(req, server)
| 493 | idleTimeout: 0, // PTY connections are long-lived; default idleTimeout would kill them |
| 494 | |
| 495 | fetch(req, server) { |
| 496 | const url = new URL(req.url); |
| 497 | |
| 498 | // /internal/grant — loopback-only handshake from parent server. |
| 499 | // v1.44+: accepts `{token, sessionId?}`. The sessionId binding lets |
| 500 | // the agent route re-attach attempts (same sessionId, fresh token) |
| 501 | // back to the same PtySession. Legacy callers passing just `{token}` |
| 502 | // still work — sessionId becomes null and re-attach is unavailable |
| 503 | // for that grant. |
| 504 | if (url.pathname === '/internal/grant' && req.method === 'POST') { |
| 505 | return internalHandler(req, (body) => { |
| 506 | if (typeof body?.token === 'string' && body.token.length > 16) { |
| 507 | const sid = typeof body?.sessionId === 'string' && body.sessionId.length > 0 |
| 508 | ? body.sessionId |
| 509 | : null; |
| 510 | validTokens.set(body.token, sid); |
| 511 | } |
| 512 | }); |
| 513 | } |
| 514 | |
| 515 | // /internal/revoke — drop a token (called on WS close or bootstrap reload) |
| 516 | if (url.pathname === '/internal/revoke' && req.method === 'POST') { |
| 517 | return internalHandler(req, (body) => { |
| 518 | if (typeof body?.token === 'string') validTokens.delete(body.token); |
| 519 | }); |
| 520 | } |
| 521 | |
| 522 | // /internal/restart — dispose the PtySession for a specific sessionId. |
| 523 | // Scoped to one caller (not enumerate-all). Server.ts /pty-restart |
| 524 | // posts here with the caller's sessionId; we kill ONLY that PTY, |
| 525 | // leaving any other live sidebar tabs untouched. Codex T2 of the |
| 526 | // eng review caught this gap — pre-spec the route would have |
| 527 | // disposed all sessions. |
| 528 | if (url.pathname === '/internal/restart' && req.method === 'POST') { |
| 529 | return internalHandler(req, (body) => { |
| 530 | const sid = typeof body?.sessionId === 'string' ? body.sessionId : null; |
| 531 | if (!sid) return { killed: 0 }; |
| 532 | const session = sessionsById.get(sid); |
| 533 | if (!session) return { killed: 0 }; |
| 534 | // Cancel any pending detach timer before disposal — otherwise it |
| 535 | // would fire later against an already-disposed session. |
| 536 | if (session.detachTimer) { |
| 537 | clearTimeout(session.detachTimer); |
| 538 | session.detachTimer = null; |
| 539 | } |
| 540 | disposeSession(session); |
| 541 | sessionsById.delete(sid); |
| 542 | return { killed: 1 }; |
| 543 | }); |
| 544 | } |
| 545 | |
| 546 | // /internal/healthz — liveness probe used by the v1.44 watchdog. |
| 547 | // Returns this agent's pid + gen + active session count without |
| 548 | // touching claude binary lookup (which can fail for non-process |
| 549 | // reasons and isn't a useful liveness signal). GET — no body to parse, |
| 550 | // so it stays on the bare checkInternalAuth gate. |
| 551 | if (url.pathname === '/internal/healthz' && req.method === 'GET') { |
| 552 | const denied = checkInternalAuth(req); |
no test coverage detected