v1.39.0.0 feat: buildFetchHandler factory unblocks gbrowser submodule consumption (#1511)

* feat: buildFetchHandler factory unblocks gbrowser submodule consumption

Add buildFetchHandler(cfg: ServerConfig): ServerHandle in browse/src/server.ts.
Refactor start() to delegate handler construction to the factory and read env
once via resolveConfigFromEnv(). Wire the beforeRoute hook (runs after the
tunnel surface filter, before per-route dispatch).

Auth is now cfg-driven end-to-end. Module-level AUTH_TOKEN const +
initRegistry(AUTH_TOKEN) boot call, validateAuth, and shutdown are deleted;
factory closure owns them. start() threads cfg.authToken into launchHeaded,
the state-file write, and the factory.

initRegistry is idempotent for same-token re-init; throws clearly for
different-token re-init. __resetRegistry() test helper added (mirrors
__resetConnectRateLimit). Existing tests that did rotateRoot() ->
initRegistry('fixed-token') swap to __resetRegistry() to avoid the new guard.

14 factory contract tests added covering ServerHandle shape, auth wiring,
validation throws, hook semantics across both surfaces, and registry
idempotency.

Source-pattern tests in dual-listener.test.ts and server-auth.test.ts
updated for the new identifiers (handle.fetchLocal/fetchTunnel, authToken,
shutdownFn).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: bump version and changelog (v1.39.0.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-14 21:55:29 -07:00
committed by GitHub
parent ea51b45e08
commit 25cf5edf21
12 changed files with 587 additions and 200 deletions

View File

@@ -6,12 +6,15 @@ import {
revokeToken, rotateRoot, listTokens, recordCommand,
serializeRegistry, restoreRegistry, checkConnectRateLimit,
SCOPE_READ, SCOPE_WRITE, SCOPE_ADMIN, SCOPE_CONTROL, SCOPE_META,
__resetRegistry,
} from '../src/token-registry';
describe('token-registry', () => {
beforeEach(() => {
// rotateRoot clears all tokens and rate buckets, then initRegistry sets the root
rotateRoot();
// __resetRegistry zeroes rootToken so the new initRegistry mismatch guard
// doesn't fire on the immediate initRegistry call. rotateRoot would leave
// a UUID in rootToken and the guard would throw.
__resetRegistry();
initRegistry('root-token-for-tests');
});
@@ -333,8 +336,10 @@ describe('token-registry', () => {
const state = serializeRegistry();
expect(Object.keys(state.agents)).toHaveLength(2);
// Clear and restore
rotateRoot();
// Clear and restore. __resetRegistry instead of rotateRoot+initRegistry
// so the new initRegistry mismatch guard doesn't fire — rotateRoot
// leaves a UUID in rootToken and initRegistry('new-root') would throw.
__resetRegistry();
initRegistry('new-root');
restoreRegistry(state);