Merge origin/master into feat/remote-host-wake

Resolves CLAUDE.md count tables (route counts recounted on the merged
tree: 235 handlers, sessions 37) and keeps both the host-wake and the
reboot-restore banner in index.html.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QdGP4jUTjc9J2RYYykDrCG
This commit is contained in:
Randalix
2026-09-18 22:20:41 +02:00
co-authored by Claude Opus 5
77 changed files with 5422 additions and 368 deletions
@@ -0,0 +1,28 @@
/**
* The mock route context must offer everything the real one does.
*
* Route tests pass their context as `ctx as never`, and `tsconfig.json` includes
* only `src/**`, so no type check ever compares the mock against the ports. A
* port that gained a method left this mock missing it twice; both times the
* route under test threw a TypeError inside its own catch, and the suite
* reported a plausible-looking failure for an unrelated reason.
*
* So the comparison is made at runtime, against `WebServer.createRouteContext()`
* rather than against the port types, which is what keeps it from drifting: the
* server's own context object is the thing route modules are really given.
*/
import { describe, expect, it } from 'vitest';
import { WebServer } from '../../src/web/server.js';
import { createMockRouteContext } from './mock-route-context.js';
describe('the mock route context', () => {
it('offers every member the real route context does', () => {
const server = new WebServer(0, false, true);
const real = (server as unknown as { createRouteContext(): Record<string, unknown> }).createRouteContext();
const mock = createMockRouteContext() as unknown as Record<string, unknown>;
const missing = Object.keys(real).filter((key) => !(key in mock));
expect(missing, `mock-route-context.ts is missing: ${missing.join(', ')}`).toEqual([]);
});
});
+5
View File
@@ -61,6 +61,10 @@ export function createMockRouteContext(options?: {
setupSessionListeners: vi.fn(async () => {}),
persistSessionState: vi.fn(),
persistSessionStateNow: vi.fn(),
reapplyPersistedSessionState: vi.fn(async () => {}),
discardPartiallyBuiltSession: vi.fn(async (id: string) => {
sessions.delete(id);
}),
getSessionStateWithRespawn: vi.fn((s: MockSession) => s.toState()),
// -- EventPort --
@@ -149,6 +153,7 @@ export function createMockRouteContext(options?: {
clearRespawnConfig: vi.fn(),
updateRespawnConfig: vi.fn(),
setHistoryLimit: vi.fn(async () => {}),
startStatsCollection: vi.fn(),
},
runSummaryTrackers: new Map(),
activePlanOrchestrators: new Map(),
+3
View File
@@ -68,6 +68,9 @@ export class MockSession extends EventEmitter {
this.lastSubmitAt = Date.now();
}
/** Mirrors Session.trackUserInput (the send-key route feeds it around the write path). */
trackUserInput(_data: string): void {}
private _muxName: string | null = null;
constructor(id: string = 'mock-session-id') {