@kb-labs/shared-testing-e2e 2.60.0 → 2.62.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/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -306,7 +306,9 @@ declare function createIsolatedProjectRoot(opts?: IsolatedProjectRootOptions): P
|
|
|
306
306
|
declare function makeTestNamespace(fileUrlOrPath: string, prefix?: string): string;
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
|
-
* Walk up from a starting directory until we find
|
|
309
|
+
* Walk up from a starting directory until we find a KB Labs workspace root.
|
|
310
|
+
* Recognises both `.kb/devservices.yaml` (runtime, generated by kb-create) and
|
|
311
|
+
* `.kb/devservices.dev.yaml` (committed, present in CI checkouts).
|
|
310
312
|
* Returns the absolute path of the project root, or `null` if not found.
|
|
311
313
|
*/
|
|
312
314
|
declare function findWorkspaceRoot(startDir?: string): string | null;
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { nanoid } from 'nanoid';
|
|
|
12
12
|
function findWorkspaceRoot(startDir) {
|
|
13
13
|
let dir = startDir ?? process.cwd();
|
|
14
14
|
for (let i = 0; i < 20; i++) {
|
|
15
|
-
if (existsSync(resolve(dir, ".kb/devservices.yaml"))) {
|
|
15
|
+
if (existsSync(resolve(dir, ".kb/devservices.yaml")) || existsSync(resolve(dir, ".kb/devservices.dev.yaml"))) {
|
|
16
16
|
return dir;
|
|
17
17
|
}
|
|
18
18
|
const parent = dirname(dir);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workspace-root.ts","../src/kb-dev-controller.ts","../src/http-client.ts","../src/ws-client.ts","../src/sse-reader.ts","../src/jwt-helpers.ts","../src/isolated-project-root.ts","../src/namespace.ts"],"names":["resolve","existsSync"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Walk up from a starting directory until we find `.kb/devservices.yaml`.\n * Returns the absolute path of the project root, or `null` if not found.\n */\nexport function findWorkspaceRoot(startDir?: string): string | null {\n let dir = startDir ?? process.cwd();\n // Safety: bail after 20 levels so we never climb past `/`.\n for (let i = 0; i < 20; i++) {\n if (existsSync(resolve(dir, '.kb/devservices.yaml'))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n return null;\n}\n\n/**\n * Resolve the workspace root for the current package, honoring env overrides.\n * Precedence:\n * 1. `KB_PROJECT_ROOT` env var (matches kb-dev itself).\n * 2. Walk up from `process.cwd()`.\n * 3. Walk up from this source file's directory (handles running tests\n * from deeply nested package dirs).\n *\n * Throws if no root can be located.\n */\nexport function resolveWorkspaceRoot(): string {\n const fromEnv = process.env.KB_PROJECT_ROOT;\n if (fromEnv && existsSync(resolve(fromEnv, '.kb/devservices.yaml'))) {\n return fromEnv;\n }\n const fromCwd = findWorkspaceRoot(process.cwd());\n if (fromCwd) {\n return fromCwd;\n }\n const here = dirname(fileURLToPath(import.meta.url));\n const fromHere = findWorkspaceRoot(here);\n if (fromHere) {\n return fromHere;\n }\n throw new Error(\n 'Could not locate KB Labs workspace root (.kb/devservices.yaml). ' +\n 'Set KB_PROJECT_ROOT or run from inside the workspace.',\n );\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport type {\n EnsureOptions,\n KbDevControllerOptions,\n KbDevResult,\n ServiceId,\n ServiceStatus,\n StatusSnapshot,\n} from './types.js';\nimport { resolveWorkspaceRoot } from './workspace-root.js';\n\n/**\n * Ring buffer for the last N lines of subprocess output.\n * Used so that on failure the harness can dump recent kb-dev noise\n * into the test output without swamping it with tens of thousands of lines.\n */\nclass RingBuffer {\n private buf: string[] = [];\n constructor(private readonly capacity: number) {}\n push(line: string): void {\n this.buf.push(line);\n if (this.buf.length > this.capacity) {\n this.buf.splice(0, this.buf.length - this.capacity);\n }\n }\n snapshot(): string[] {\n return [...this.buf];\n }\n}\n\ninterface SubprocessResult {\n code: number;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Controller that drives kb-dev as a subprocess and exposes its JSON\n * agent protocol as a typed TypeScript API for e2e tests.\n *\n * Lifecycle: one controller per test file (beforeAll/afterAll).\n * Services booted through this controller are shared across every\n * `describe`/`it` in that file.\n */\nexport class KbDevController {\n readonly projectRoot: string;\n readonly kbDevBin: string;\n private readonly env: Record<string, string>;\n private readonly logSink?: (line: string) => void;\n private readonly ring = new RingBuffer(400);\n /** Services this controller started (and therefore should clean up). */\n private readonly startedByUs = new Set<ServiceId>();\n /** Cached last status snapshot (invalidated on any mutating call). */\n private lastStatus?: StatusSnapshot;\n\n constructor(opts: KbDevControllerOptions = {}) {\n this.projectRoot = opts.projectRoot ?? resolveWorkspaceRoot();\n this.kbDevBin = opts.kbDevBin ?? resolve(this.projectRoot, 'tools/kb-dev/kb-dev');\n if (!existsSync(this.kbDevBin)) {\n throw new Error(\n `kb-dev binary not found at ${this.kbDevBin}. ` +\n 'Build it with: cd tools/kb-dev && make build',\n );\n }\n this.env = {\n ...process.env as Record<string, string>,\n ...(opts.env ?? {}),\n KB_PROJECT_ROOT: this.projectRoot,\n };\n this.logSink = opts.logSink;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Ensure every listed service is `alive`. Idempotent — if a service is\n * already running, kb-dev reports `skipped` and we don't respawn.\n *\n * Throws with a formatted hint + recent log tail on failure.\n */\n async ensureServices(\n ids: ServiceId[],\n opts: EnsureOptions = {},\n ): Promise<StatusSnapshot> {\n const timeoutMs = opts.timeoutMs ?? 60_000;\n const args = ['ensure', ...ids, '--json'];\n if (opts.force) {args.push('--force');}\n\n const result = await this.runJson<KbDevResult>(args, timeoutMs);\n if (!result.ok) {\n throw this.formatError(`kb-dev ensure ${ids.join(' ')}`, result);\n }\n for (const action of result.actions ?? []) {\n if (action.action === 'started') {\n this.startedByUs.add(action.service as ServiceId);\n }\n }\n // Poll status until every requested service is alive (ensure returns\n // when spawn completes, but health may still be warming up).\n const snapshot = await this.waitUntilAlive(ids, timeoutMs);\n this.lastStatus = snapshot;\n return snapshot;\n }\n\n /**\n * Block until a single service reports `alive` or the timeout expires.\n */\n async ready(id: ServiceId, timeoutMs = 60_000): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const snap = await this.status();\n const svc = snap.services[id];\n if (svc?.state === 'alive') {return;}\n if (svc?.state === 'failed') {\n throw new Error(\n `Service ${id} entered 'failed' state.\\n` +\n `Logs tail:\\n${(svc.logsTail ?? []).join('\\n')}`,\n );\n }\n await sleep(500);\n }\n throw new Error(`Timeout waiting for ${id} to become alive (${timeoutMs}ms)`);\n }\n\n /** Query current status for all services. */\n async status(): Promise<StatusSnapshot> {\n const result = await this.runJson<StatusSnapshot>(['status', '--json'], 10_000);\n this.lastStatus = result;\n return result;\n }\n\n /**\n * Stop the given services (or everything this controller started, if none given).\n * Always best-effort — errors are logged but never thrown.\n */\n async stopServices(ids?: ServiceId[]): Promise<void> {\n const toStop = ids ?? Array.from(this.startedByUs);\n if (toStop.length === 0) {return;}\n try {\n await this.runJson<KbDevResult>(['stop', ...toStop, '--json'], 30_000);\n } catch (err) {\n // Stop failures should never mask the real test failure.\n this.logSink?.(`[kb-dev] stop failed (ignored): ${(err as Error).message}`);\n }\n for (const id of toStop) {this.startedByUs.delete(id);}\n this.lastStatus = undefined;\n }\n\n /** Disposes the controller — stops every service it started. */\n async dispose(): Promise<void> {\n await this.stopServices();\n }\n\n /**\n * Return the HTTP base URL for a service, e.g. `http://localhost:4000`.\n * Throws if the service is not in the last known status snapshot or has no URL.\n */\n getServiceUrl(id: ServiceId): string {\n const svc = this.requireService(id);\n if (!svc.url) {\n throw new Error(`Service ${id} has no URL in its status (state=${svc.state})`);\n }\n return svc.url;\n }\n\n /** Return the port for a service, or throw if unknown. */\n getServicePort(id: ServiceId): number {\n const svc = this.requireService(id);\n if (!svc.port) {\n throw new Error(`Service ${id} has no port in its status (state=${svc.state})`);\n }\n return svc.port;\n }\n\n /** Snapshot of recent subprocess log lines, newest last. */\n dumpRecentLogs(): string[] {\n return this.ring.snapshot();\n }\n\n // ── Internals ─────────────────────────────────────────────────────────────\n\n private requireService(id: ServiceId): ServiceStatus {\n if (!this.lastStatus) {\n throw new Error(\n `No status snapshot yet for ${id}. Call ensureServices() or status() first.`,\n );\n }\n const svc = this.lastStatus.services[id];\n if (!svc) {\n throw new Error(`Service ${id} not found in status snapshot`);\n }\n return svc;\n }\n\n private async waitUntilAlive(\n ids: ServiceId[],\n timeoutMs: number,\n ): Promise<StatusSnapshot> {\n const deadline = Date.now() + timeoutMs;\n let lastSnap: StatusSnapshot | undefined;\n while (Date.now() < deadline) {\n lastSnap = await this.status();\n const allAlive = ids.every((id) => lastSnap?.services[id]?.state === 'alive');\n if (allAlive) {return lastSnap;}\n // Fail fast on hard failures — no point waiting for a service already marked `failed`.\n const failed = ids.find((id) => lastSnap?.services[id]?.state === 'failed');\n if (failed) {\n const svc = lastSnap!.services[failed]!;\n throw new Error(\n `Service ${failed} failed during ensure.\\n` +\n `Logs tail:\\n${(svc.logsTail ?? []).join('\\n')}`,\n );\n }\n await sleep(500);\n }\n throw new Error(\n `Timeout waiting for services to become alive: ${ids.join(', ')}.\\n` +\n `Last known states: ${ids\n .map((id) => `${id}=${lastSnap?.services[id]?.state ?? 'unknown'}`)\n .join(', ')}`,\n );\n }\n\n private formatError(prefix: string, result: KbDevResult): Error {\n const lines: string[] = [`${prefix} failed`];\n if (result.hint) {lines.push(`Hint: ${result.hint}`);}\n for (const action of result.actions ?? []) {\n if (action.action === 'failed' || action.error) {\n lines.push(` - ${action.service}: ${action.error ?? 'failed'}`);\n for (const log of action.logsTail ?? []) {\n lines.push(` ${log}`);\n }\n }\n }\n const recent = this.ring.snapshot();\n if (recent.length > 0) {\n lines.push('Recent kb-dev output:');\n for (const l of recent.slice(-40)) {lines.push(` ${l}`);}\n }\n return new Error(lines.join('\\n'));\n }\n\n /**\n * Run a kb-dev subcommand and parse the stdout as JSON.\n * kb-dev prints only the JSON object on stdout when `--json` is set;\n * any logs go to stderr.\n */\n private async runJson<T>(args: string[], timeoutMs: number): Promise<T> {\n const result = await this.run(args, timeoutMs);\n if (result.code !== 0 && !result.stdout.trim()) {\n throw new Error(\n `kb-dev ${args.join(' ')} exited with code ${result.code}\\n` +\n `stderr:\\n${result.stderr}`,\n );\n }\n try {\n return JSON.parse(result.stdout) as T;\n } catch (err) {\n throw new Error(\n `Failed to parse JSON from \\`kb-dev ${args.join(' ')}\\`:\\n` +\n ` error: ${(err as Error).message}\\n` +\n ` stdout: ${result.stdout.slice(0, 2000)}\\n` +\n ` stderr: ${result.stderr.slice(0, 2000)}`,\n );\n }\n }\n\n private run(args: string[], timeoutMs: number): Promise<SubprocessResult> {\n return new Promise((resolvePromise) => {\n const child = spawn(this.kbDevBin, args, {\n cwd: this.projectRoot,\n env: this.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n const onLine = (chunk: Buffer, stream: 'stdout' | 'stderr') => {\n const text = chunk.toString();\n if (stream === 'stdout') {stdout += text;}\n else {stderr += text;}\n for (const line of text.split('\\n')) {\n if (line) {\n this.ring.push(`[${stream}] ${line}`);\n this.logSink?.(`[kb-dev ${args[0]}] [${stream}] ${line}`);\n }\n }\n };\n\n child.stdout.on('data', (d: Buffer) => onLine(d, 'stdout'));\n child.stderr.on('data', (d: Buffer) => onLine(d, 'stderr'));\n\n const timer = setTimeout(() => {\n child.kill('SIGKILL');\n }, timeoutMs);\n\n child.on('close', (code) => {\n clearTimeout(timer);\n resolvePromise({ code: code ?? -1, stdout, stderr });\n });\n child.on('error', (err) => {\n clearTimeout(timer);\n resolvePromise({ code: -1, stdout, stderr: stderr + '\\n' + err.message });\n });\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => { setTimeout(r, ms); });\n}\n","/**\n * Minimal HTTP client wrapper used by e2e tests.\n *\n * Intentionally thin — tests should be able to read one line and know\n * exactly what request went out. No retries, no interceptors, no magic.\n */\n\nexport interface HttpClientOptions {\n /** Extra headers included on every request. */\n headers?: Record<string, string>;\n /** Per-request timeout in ms. Default 15_000. */\n timeoutMs?: number;\n}\n\nexport interface HttpResponse<T = unknown> {\n status: number;\n ok: boolean;\n headers: Headers;\n /** Parsed JSON body, or `undefined` if the response had no body / non-JSON content. */\n body: T | undefined;\n /** Raw text body, always populated. */\n text: string;\n}\n\nexport class HttpClient {\n constructor(\n private readonly baseUrl: string,\n private readonly defaults: HttpClientOptions = {},\n ) {}\n\n async get<T = unknown>(path: string, opts: HttpClientOptions = {}): Promise<HttpResponse<T>> {\n return this.request<T>('GET', path, undefined, opts);\n }\n\n async post<T = unknown>(\n path: string,\n body: unknown,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('POST', path, body, opts);\n }\n\n async delete<T = unknown>(\n path: string,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('DELETE', path, undefined, opts);\n }\n\n async put<T = unknown>(\n path: string,\n body: unknown,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('PUT', path, body, opts);\n }\n\n async options<T = unknown>(\n path: string,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('OPTIONS', path, undefined, opts);\n }\n\n /** Build an absolute URL for a path under this client's base. */\n url(path: string): string {\n if (path.startsWith('http://') || path.startsWith('https://')) {return path;}\n return `${this.baseUrl.replace(/\\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;\n }\n\n private async request<T>(\n method: string,\n path: string,\n body: unknown,\n opts: HttpClientOptions,\n ): Promise<HttpResponse<T>> {\n const timeoutMs = opts.timeoutMs ?? this.defaults.timeoutMs ?? 15_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const headers: Record<string, string> = {\n ...this.defaults.headers,\n ...opts.headers,\n };\n if (body !== undefined && !headers['content-type'] && !headers['Content-Type']) {\n headers['content-type'] = 'application/json';\n }\n\n try {\n const res = await fetch(this.url(path), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await res.text();\n let parsed: T | undefined;\n const contentType = res.headers.get('content-type') ?? '';\n if (text && contentType.includes('json')) {\n try {\n parsed = JSON.parse(text) as T;\n } catch {\n parsed = undefined;\n }\n }\n return {\n status: res.status,\n ok: res.ok,\n headers: res.headers,\n body: parsed,\n text,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nexport function httpClient(baseUrl: string, opts: HttpClientOptions = {}): HttpClient {\n return new HttpClient(baseUrl, opts);\n}\n","import { WebSocket, type RawData } from 'ws';\n\n/**\n * Test-friendly WebSocket wrapper.\n *\n * Adapted from `infra/kb-labs-gateway/apps/gateway-app/src/__tests__/live-gateway.e2e.test.ts`\n * — same tracking semantics so the harness can clean up leaked sockets\n * between tests even when an assertion fails mid-flight.\n */\n\nexport interface WsOptions {\n headers?: Record<string, string>;\n /** ms. Default 8000. */\n openTimeoutMs?: number;\n}\n\n/** Opaque handle returned from `connectWs`. */\nexport interface WsHandle {\n readonly socket: WebSocket;\n /** Send a JSON-stringified message. */\n send(data: unknown): void;\n /** Wait for the next message, optionally satisfying a predicate. */\n waitForMessage<T = unknown>(\n opts?: { timeoutMs?: number; predicate?: (msg: T) => boolean },\n ): Promise<T>;\n /** Collect the next N messages (parsed as JSON). */\n collect<T = unknown>(count: number, timeoutMs?: number): Promise<T[]>;\n close(code?: number): void;\n}\n\n/**\n * Shared registry of open sockets. Call `closeAllTrackedSockets()` from an\n * `afterEach` to guarantee cleanup.\n */\nconst openSockets = new Set<WebSocket>();\n\nexport async function closeAllTrackedSockets(graceMs = 150): Promise<void> {\n const toClose = [...openSockets];\n for (const ws of toClose) {\n if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {\n try {\n ws.close(1000);\n } catch {\n // intentionally empty: best-effort cleanup, socket may already be dead\n }\n }\n }\n openSockets.clear();\n if (toClose.length > 0) {\n await new Promise((r) => { setTimeout(r, graceMs); });\n }\n}\n\nexport function connectWs(url: string, opts: WsOptions = {}): Promise<WsHandle> {\n const openTimeoutMs = opts.openTimeoutMs ?? 8000;\n return new Promise((resolvePromise, reject) => {\n const ws = new WebSocket(url, {\n headers: opts.headers,\n });\n openSockets.add(ws);\n ws.on('close', () => openSockets.delete(ws));\n\n const timer = setTimeout(() => {\n reject(new Error(`WebSocket open timeout after ${openTimeoutMs}ms: ${url}`));\n try { ws.close(); } catch {\n // intentionally empty: best-effort cleanup on timeout\n }\n }, openTimeoutMs);\n\n ws.once('open', () => {\n clearTimeout(timer);\n resolvePromise(wrapHandle(ws));\n });\n ws.once('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\nfunction wrapHandle(ws: WebSocket): WsHandle {\n return {\n socket: ws,\n send(data: unknown) {\n ws.send(typeof data === 'string' ? data : JSON.stringify(data));\n },\n waitForMessage<T = unknown>(\n opts: { timeoutMs?: number; predicate?: (msg: T) => boolean } = {},\n ): Promise<T> {\n const timeoutMs = opts.timeoutMs ?? 5000;\n return new Promise((resolvePromise, reject) => {\n const timer = setTimeout(() => {\n ws.off('message', onMessage);\n reject(new Error(`WebSocket message timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n const onMessage = (raw: RawData) => {\n let msg: T;\n try {\n msg = JSON.parse(raw.toString()) as T;\n } catch (err) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n reject(new Error(`Non-JSON WebSocket message: ${(err as Error).message}`));\n return;\n }\n if (!opts.predicate || opts.predicate(msg)) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n resolvePromise(msg);\n }\n };\n ws.on('message', onMessage);\n });\n },\n collect<T = unknown>(count: number, timeoutMs = 5000): Promise<T[]> {\n return new Promise((resolvePromise, reject) => {\n const msgs: T[] = [];\n const timer = setTimeout(() => {\n ws.off('message', onMessage);\n reject(\n new Error(\n `Timeout: expected ${count} WS messages, got ${msgs.length}: ${JSON.stringify(msgs)}`,\n ),\n );\n }, timeoutMs);\n const onMessage = (raw: RawData) => {\n msgs.push(JSON.parse(raw.toString()) as T);\n if (msgs.length >= count) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n resolvePromise(msgs);\n }\n };\n ws.on('message', onMessage);\n });\n },\n close(code = 1000) {\n try {\n ws.close(code);\n } catch {\n // intentionally empty: best-effort cleanup\n }\n },\n };\n}\n","/**\n * Minimal Server-Sent Events reader.\n *\n * Consumes `text/event-stream` responses and yields one `SseEvent` per\n * `event:`/`data:` pair. Terminates on close, explicit `untilEvent`, or timeout.\n */\n\nexport interface SseEvent {\n /** SSE `event:` field. Empty string if absent. */\n event: string;\n /** SSE `data:` field, concatenated with newlines if multi-line. */\n data: string;\n /** Optional `id:` field. */\n id?: string;\n /** Parsed JSON body if `data` is valid JSON, else undefined. */\n json?: unknown;\n}\n\nexport interface SseOptions {\n headers?: Record<string, string>;\n /** If set, the iterator terminates after the first event with this name. */\n untilEvent?: string;\n /** Wall-clock timeout in ms. Default 30_000. */\n timeoutMs?: number;\n}\n\n/**\n * Async iterator over an SSE stream. Caller is responsible for iterating\n * (e.g. `for await (const event of readSse(url)) { ... }`).\n */\nexport async function* readSse(url: string, opts: SseOptions = {}): AsyncGenerator<SseEvent> {\n const timeoutMs = opts.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { accept: 'text/event-stream', ...opts.headers },\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n throw new Error(`SSE fetch failed: ${(err as Error).message}`);\n }\n\n if (!res.ok || !res.body) {\n clearTimeout(timer);\n throw new Error(`SSE response not ok: ${res.status} ${res.statusText}`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {return;}\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by a blank line (\\n\\n).\n let sepIdx: number;\n while ((sepIdx = buffer.indexOf('\\n\\n')) !== -1) {\n const rawEvent = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n const parsed = parseEvent(rawEvent);\n if (!parsed) {continue;}\n yield parsed;\n if (opts.untilEvent && parsed.event === opts.untilEvent) {\n return;\n }\n }\n }\n } finally {\n clearTimeout(timer);\n try { reader.cancel(); } catch {\n // intentionally empty: reader may already be closed\n }\n }\n}\n\nfunction parseEvent(raw: string): SseEvent | null {\n if (!raw.trim()) {return null;}\n let eventName = '';\n const dataLines: string[] = [];\n let id: string | undefined;\n\n for (const line of raw.split('\\n')) {\n if (!line || line.startsWith(':')) {continue;} // comment / keep-alive\n const colonIdx = line.indexOf(':');\n if (colonIdx === -1) {continue;}\n const field = line.slice(0, colonIdx);\n // Per spec: strip a single leading space from value.\n const value = line.slice(colonIdx + 1).replace(/^ /, '');\n if (field === 'event') {eventName = value;}\n else if (field === 'data') {dataLines.push(value);}\n else if (field === 'id') {id = value;}\n }\n\n if (eventName === '' && dataLines.length === 0) {return null;}\n const data = dataLines.join('\\n');\n let json: unknown;\n if (data) {\n try { json = JSON.parse(data); } catch { /* not JSON, leave undefined */ }\n }\n return { event: eventName, data, id, json };\n}\n","/**\n * Gateway auth helpers.\n *\n * Extracted from `infra/kb-labs-gateway/apps/gateway-app/src/__tests__/live-gateway.e2e.test.ts`\n * (`getJwtToken`, `registerHost`) so every e2e test can reuse the same flow.\n *\n * Flow:\n * 1. POST /auth/register { name, namespaceId } -> { clientId, clientSecret, hostId }\n * 2. POST /auth/token { clientId, clientSecret } -> { accessToken }\n */\n\nimport type { HttpClient } from './http-client.js';\n\nexport interface AgentCredentials {\n accessToken: string;\n clientId: string;\n hostId: string;\n}\n\nexport interface HostCredentials {\n hostId: string;\n machineToken: string;\n}\n\nexport async function registerAgent(\n client: HttpClient,\n opts: { name?: string; namespaceId: string } = { namespaceId: 'e2e-default' },\n): Promise<AgentCredentials> {\n const name = opts.name ?? 'e2e-agent';\n const regRes = await client.post<{ clientId: string; clientSecret: string; hostId: string }>(\n '/auth/register',\n { name, namespaceId: opts.namespaceId },\n );\n if (!regRes.ok || !regRes.body) {\n throw new Error(`/auth/register failed: ${regRes.status} ${regRes.text}`);\n }\n const { clientId, clientSecret, hostId } = regRes.body;\n\n const tokenRes = await client.post<{ accessToken: string }>(\n '/auth/token',\n { clientId, clientSecret },\n );\n if (!tokenRes.ok || !tokenRes.body) {\n throw new Error(`/auth/token failed: ${tokenRes.status} ${tokenRes.text}`);\n }\n\n return { accessToken: tokenRes.body.accessToken, clientId, hostId };\n}\n\nexport async function registerHost(\n client: HttpClient,\n opts: {\n name?: string;\n namespaceId: string;\n capabilities?: string[];\n workspacePaths?: string[];\n },\n): Promise<HostCredentials> {\n const res = await client.post<HostCredentials>('/hosts/register', {\n name: opts.name ?? 'e2e-host',\n namespaceId: opts.namespaceId,\n capabilities: opts.capabilities ?? ['filesystem'],\n workspacePaths: opts.workspacePaths ?? [],\n });\n if (!res.ok || !res.body) {\n throw new Error(`/hosts/register failed: ${res.status} ${res.text}`);\n }\n return res.body;\n}\n","import { mkdir, mkdtemp, rm, writeFile, copyFile, readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join, resolve } from 'node:path';\n\n/**\n * An isolated temporary project root with its own `.kb/` directory.\n *\n * Used by marketplace/plugin e2e tests so real workspace `.kb/marketplace.lock`\n * and `.kb/plugins.json` are never mutated.\n *\n * Pass the returned `root` via `KB_PROJECT_ROOT` env when starting kb-dev.\n */\nexport interface IsolatedProjectRoot {\n /** Absolute path to the temp project root. */\n root: string;\n /** Absolute path to `<root>/.kb/devservices.yaml`. */\n devConfigPath: string;\n /** Delete the temp dir. Safe to call multiple times. */\n cleanup(): Promise<void>;\n}\n\nexport interface IsolatedProjectRootOptions {\n /**\n * If given, copies the devservices.yaml from this existing workspace root.\n * If omitted, a minimal config with only `state-daemon` is written.\n */\n copyDevConfigFrom?: string;\n /** Prefix for the temp dir name (for human diagnosis). */\n prefix?: string;\n}\n\nconst MINIMAL_DEV_CONFIG = `name: KB Labs E2E Isolated Root\n\ngroups:\n infra: [state-daemon]\n\nservices:\n state-daemon:\n name: State Daemon\n description: Distributed state management\n group: infra\n type: node\n command: node ./plugins/state/daemon/core-state-daemon/dist/bin.cjs\n healthCheck: http://localhost:7777/health\n port: 7777\n url: http://localhost:7777\n env:\n KB_STATE_DAEMON_PORT: \"7777\"\n KB_STATE_DAEMON_HOST: localhost\n\nsettings:\n logsDir: .kb/logs/tmp\n pidDir: .kb/tmp\n startTimeout: 30000\n healthCheckInterval: 1000\n`;\n\nexport async function createIsolatedProjectRoot(\n opts: IsolatedProjectRootOptions = {},\n): Promise<IsolatedProjectRoot> {\n const prefix = opts.prefix ?? 'kb-e2e-';\n const root = await mkdtemp(join(tmpdir(), prefix));\n const kbDir = join(root, '.kb');\n await mkdir(kbDir, { recursive: true });\n await mkdir(join(kbDir, 'tmp'), { recursive: true });\n await mkdir(join(kbDir, 'logs', 'tmp'), { recursive: true });\n\n const devConfigPath = join(kbDir, 'devservices.yaml');\n\n if (opts.copyDevConfigFrom) {\n const source = resolve(opts.copyDevConfigFrom, '.kb/devservices.yaml');\n if (!existsSync(source)) {\n throw new Error(`copyDevConfigFrom: no devservices.yaml at ${source}`);\n }\n // Read to validate it exists and is readable.\n await readFile(source, 'utf8');\n await copyFile(source, devConfigPath);\n } else {\n await writeFile(devConfigPath, MINIMAL_DEV_CONFIG, 'utf8');\n }\n\n let disposed = false;\n const cleanup = async () => {\n if (disposed) {return;}\n disposed = true;\n try {\n await rm(root, { recursive: true, force: true });\n } catch {\n // intentionally empty: best-effort cleanup, tmpdir will be reaped eventually\n }\n };\n\n return { root, devConfigPath, cleanup };\n}\n","import { createHash } from 'node:crypto';\nimport { nanoid } from 'nanoid';\n\n/**\n * Build a deterministic-yet-unique namespace prefix for e2e test resources.\n *\n * Shape: `e2e-<fileHash>-<nanoid>`\n *\n * - `fileHash` is 6 hex chars of sha1(filePath) — lets you grep leaked resources\n * back to the test file that created them.\n * - `nanoid` is a 10-char random suffix — lets parallel runs never collide.\n *\n * Pass `import.meta.url` from the test file.\n */\nexport function makeTestNamespace(fileUrlOrPath: string, prefix = 'e2e'): string {\n const hash = createHash('sha1').update(fileUrlOrPath).digest('hex').slice(0, 6);\n return `${prefix}-${hash}-${nanoid(10)}`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/workspace-root.ts","../src/kb-dev-controller.ts","../src/http-client.ts","../src/ws-client.ts","../src/sse-reader.ts","../src/jwt-helpers.ts","../src/isolated-project-root.ts","../src/namespace.ts"],"names":["resolve","existsSync"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Walk up from a starting directory until we find a KB Labs workspace root.\n * Recognises both `.kb/devservices.yaml` (runtime, generated by kb-create) and\n * `.kb/devservices.dev.yaml` (committed, present in CI checkouts).\n * Returns the absolute path of the project root, or `null` if not found.\n */\nexport function findWorkspaceRoot(startDir?: string): string | null {\n let dir = startDir ?? process.cwd();\n // Safety: bail after 20 levels so we never climb past `/`.\n for (let i = 0; i < 20; i++) {\n if (\n existsSync(resolve(dir, '.kb/devservices.yaml')) ||\n existsSync(resolve(dir, '.kb/devservices.dev.yaml'))\n ) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n return null;\n}\n\n/**\n * Resolve the workspace root for the current package, honoring env overrides.\n * Precedence:\n * 1. `KB_PROJECT_ROOT` env var (matches kb-dev itself).\n * 2. Walk up from `process.cwd()`.\n * 3. Walk up from this source file's directory (handles running tests\n * from deeply nested package dirs).\n *\n * Throws if no root can be located.\n */\nexport function resolveWorkspaceRoot(): string {\n const fromEnv = process.env.KB_PROJECT_ROOT;\n if (fromEnv && existsSync(resolve(fromEnv, '.kb/devservices.yaml'))) {\n return fromEnv;\n }\n const fromCwd = findWorkspaceRoot(process.cwd());\n if (fromCwd) {\n return fromCwd;\n }\n const here = dirname(fileURLToPath(import.meta.url));\n const fromHere = findWorkspaceRoot(here);\n if (fromHere) {\n return fromHere;\n }\n throw new Error(\n 'Could not locate KB Labs workspace root (.kb/devservices.yaml). ' +\n 'Set KB_PROJECT_ROOT or run from inside the workspace.',\n );\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport type {\n EnsureOptions,\n KbDevControllerOptions,\n KbDevResult,\n ServiceId,\n ServiceStatus,\n StatusSnapshot,\n} from './types.js';\nimport { resolveWorkspaceRoot } from './workspace-root.js';\n\n/**\n * Ring buffer for the last N lines of subprocess output.\n * Used so that on failure the harness can dump recent kb-dev noise\n * into the test output without swamping it with tens of thousands of lines.\n */\nclass RingBuffer {\n private buf: string[] = [];\n constructor(private readonly capacity: number) {}\n push(line: string): void {\n this.buf.push(line);\n if (this.buf.length > this.capacity) {\n this.buf.splice(0, this.buf.length - this.capacity);\n }\n }\n snapshot(): string[] {\n return [...this.buf];\n }\n}\n\ninterface SubprocessResult {\n code: number;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Controller that drives kb-dev as a subprocess and exposes its JSON\n * agent protocol as a typed TypeScript API for e2e tests.\n *\n * Lifecycle: one controller per test file (beforeAll/afterAll).\n * Services booted through this controller are shared across every\n * `describe`/`it` in that file.\n */\nexport class KbDevController {\n readonly projectRoot: string;\n readonly kbDevBin: string;\n private readonly env: Record<string, string>;\n private readonly logSink?: (line: string) => void;\n private readonly ring = new RingBuffer(400);\n /** Services this controller started (and therefore should clean up). */\n private readonly startedByUs = new Set<ServiceId>();\n /** Cached last status snapshot (invalidated on any mutating call). */\n private lastStatus?: StatusSnapshot;\n\n constructor(opts: KbDevControllerOptions = {}) {\n this.projectRoot = opts.projectRoot ?? resolveWorkspaceRoot();\n this.kbDevBin = opts.kbDevBin ?? resolve(this.projectRoot, 'tools/kb-dev/kb-dev');\n if (!existsSync(this.kbDevBin)) {\n throw new Error(\n `kb-dev binary not found at ${this.kbDevBin}. ` +\n 'Build it with: cd tools/kb-dev && make build',\n );\n }\n this.env = {\n ...process.env as Record<string, string>,\n ...(opts.env ?? {}),\n KB_PROJECT_ROOT: this.projectRoot,\n };\n this.logSink = opts.logSink;\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Ensure every listed service is `alive`. Idempotent — if a service is\n * already running, kb-dev reports `skipped` and we don't respawn.\n *\n * Throws with a formatted hint + recent log tail on failure.\n */\n async ensureServices(\n ids: ServiceId[],\n opts: EnsureOptions = {},\n ): Promise<StatusSnapshot> {\n const timeoutMs = opts.timeoutMs ?? 60_000;\n const args = ['ensure', ...ids, '--json'];\n if (opts.force) {args.push('--force');}\n\n const result = await this.runJson<KbDevResult>(args, timeoutMs);\n if (!result.ok) {\n throw this.formatError(`kb-dev ensure ${ids.join(' ')}`, result);\n }\n for (const action of result.actions ?? []) {\n if (action.action === 'started') {\n this.startedByUs.add(action.service as ServiceId);\n }\n }\n // Poll status until every requested service is alive (ensure returns\n // when spawn completes, but health may still be warming up).\n const snapshot = await this.waitUntilAlive(ids, timeoutMs);\n this.lastStatus = snapshot;\n return snapshot;\n }\n\n /**\n * Block until a single service reports `alive` or the timeout expires.\n */\n async ready(id: ServiceId, timeoutMs = 60_000): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const snap = await this.status();\n const svc = snap.services[id];\n if (svc?.state === 'alive') {return;}\n if (svc?.state === 'failed') {\n throw new Error(\n `Service ${id} entered 'failed' state.\\n` +\n `Logs tail:\\n${(svc.logsTail ?? []).join('\\n')}`,\n );\n }\n await sleep(500);\n }\n throw new Error(`Timeout waiting for ${id} to become alive (${timeoutMs}ms)`);\n }\n\n /** Query current status for all services. */\n async status(): Promise<StatusSnapshot> {\n const result = await this.runJson<StatusSnapshot>(['status', '--json'], 10_000);\n this.lastStatus = result;\n return result;\n }\n\n /**\n * Stop the given services (or everything this controller started, if none given).\n * Always best-effort — errors are logged but never thrown.\n */\n async stopServices(ids?: ServiceId[]): Promise<void> {\n const toStop = ids ?? Array.from(this.startedByUs);\n if (toStop.length === 0) {return;}\n try {\n await this.runJson<KbDevResult>(['stop', ...toStop, '--json'], 30_000);\n } catch (err) {\n // Stop failures should never mask the real test failure.\n this.logSink?.(`[kb-dev] stop failed (ignored): ${(err as Error).message}`);\n }\n for (const id of toStop) {this.startedByUs.delete(id);}\n this.lastStatus = undefined;\n }\n\n /** Disposes the controller — stops every service it started. */\n async dispose(): Promise<void> {\n await this.stopServices();\n }\n\n /**\n * Return the HTTP base URL for a service, e.g. `http://localhost:4000`.\n * Throws if the service is not in the last known status snapshot or has no URL.\n */\n getServiceUrl(id: ServiceId): string {\n const svc = this.requireService(id);\n if (!svc.url) {\n throw new Error(`Service ${id} has no URL in its status (state=${svc.state})`);\n }\n return svc.url;\n }\n\n /** Return the port for a service, or throw if unknown. */\n getServicePort(id: ServiceId): number {\n const svc = this.requireService(id);\n if (!svc.port) {\n throw new Error(`Service ${id} has no port in its status (state=${svc.state})`);\n }\n return svc.port;\n }\n\n /** Snapshot of recent subprocess log lines, newest last. */\n dumpRecentLogs(): string[] {\n return this.ring.snapshot();\n }\n\n // ── Internals ─────────────────────────────────────────────────────────────\n\n private requireService(id: ServiceId): ServiceStatus {\n if (!this.lastStatus) {\n throw new Error(\n `No status snapshot yet for ${id}. Call ensureServices() or status() first.`,\n );\n }\n const svc = this.lastStatus.services[id];\n if (!svc) {\n throw new Error(`Service ${id} not found in status snapshot`);\n }\n return svc;\n }\n\n private async waitUntilAlive(\n ids: ServiceId[],\n timeoutMs: number,\n ): Promise<StatusSnapshot> {\n const deadline = Date.now() + timeoutMs;\n let lastSnap: StatusSnapshot | undefined;\n while (Date.now() < deadline) {\n lastSnap = await this.status();\n const allAlive = ids.every((id) => lastSnap?.services[id]?.state === 'alive');\n if (allAlive) {return lastSnap;}\n // Fail fast on hard failures — no point waiting for a service already marked `failed`.\n const failed = ids.find((id) => lastSnap?.services[id]?.state === 'failed');\n if (failed) {\n const svc = lastSnap!.services[failed]!;\n throw new Error(\n `Service ${failed} failed during ensure.\\n` +\n `Logs tail:\\n${(svc.logsTail ?? []).join('\\n')}`,\n );\n }\n await sleep(500);\n }\n throw new Error(\n `Timeout waiting for services to become alive: ${ids.join(', ')}.\\n` +\n `Last known states: ${ids\n .map((id) => `${id}=${lastSnap?.services[id]?.state ?? 'unknown'}`)\n .join(', ')}`,\n );\n }\n\n private formatError(prefix: string, result: KbDevResult): Error {\n const lines: string[] = [`${prefix} failed`];\n if (result.hint) {lines.push(`Hint: ${result.hint}`);}\n for (const action of result.actions ?? []) {\n if (action.action === 'failed' || action.error) {\n lines.push(` - ${action.service}: ${action.error ?? 'failed'}`);\n for (const log of action.logsTail ?? []) {\n lines.push(` ${log}`);\n }\n }\n }\n const recent = this.ring.snapshot();\n if (recent.length > 0) {\n lines.push('Recent kb-dev output:');\n for (const l of recent.slice(-40)) {lines.push(` ${l}`);}\n }\n return new Error(lines.join('\\n'));\n }\n\n /**\n * Run a kb-dev subcommand and parse the stdout as JSON.\n * kb-dev prints only the JSON object on stdout when `--json` is set;\n * any logs go to stderr.\n */\n private async runJson<T>(args: string[], timeoutMs: number): Promise<T> {\n const result = await this.run(args, timeoutMs);\n if (result.code !== 0 && !result.stdout.trim()) {\n throw new Error(\n `kb-dev ${args.join(' ')} exited with code ${result.code}\\n` +\n `stderr:\\n${result.stderr}`,\n );\n }\n try {\n return JSON.parse(result.stdout) as T;\n } catch (err) {\n throw new Error(\n `Failed to parse JSON from \\`kb-dev ${args.join(' ')}\\`:\\n` +\n ` error: ${(err as Error).message}\\n` +\n ` stdout: ${result.stdout.slice(0, 2000)}\\n` +\n ` stderr: ${result.stderr.slice(0, 2000)}`,\n );\n }\n }\n\n private run(args: string[], timeoutMs: number): Promise<SubprocessResult> {\n return new Promise((resolvePromise) => {\n const child = spawn(this.kbDevBin, args, {\n cwd: this.projectRoot,\n env: this.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n const onLine = (chunk: Buffer, stream: 'stdout' | 'stderr') => {\n const text = chunk.toString();\n if (stream === 'stdout') {stdout += text;}\n else {stderr += text;}\n for (const line of text.split('\\n')) {\n if (line) {\n this.ring.push(`[${stream}] ${line}`);\n this.logSink?.(`[kb-dev ${args[0]}] [${stream}] ${line}`);\n }\n }\n };\n\n child.stdout.on('data', (d: Buffer) => onLine(d, 'stdout'));\n child.stderr.on('data', (d: Buffer) => onLine(d, 'stderr'));\n\n const timer = setTimeout(() => {\n child.kill('SIGKILL');\n }, timeoutMs);\n\n child.on('close', (code) => {\n clearTimeout(timer);\n resolvePromise({ code: code ?? -1, stdout, stderr });\n });\n child.on('error', (err) => {\n clearTimeout(timer);\n resolvePromise({ code: -1, stdout, stderr: stderr + '\\n' + err.message });\n });\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => { setTimeout(r, ms); });\n}\n","/**\n * Minimal HTTP client wrapper used by e2e tests.\n *\n * Intentionally thin — tests should be able to read one line and know\n * exactly what request went out. No retries, no interceptors, no magic.\n */\n\nexport interface HttpClientOptions {\n /** Extra headers included on every request. */\n headers?: Record<string, string>;\n /** Per-request timeout in ms. Default 15_000. */\n timeoutMs?: number;\n}\n\nexport interface HttpResponse<T = unknown> {\n status: number;\n ok: boolean;\n headers: Headers;\n /** Parsed JSON body, or `undefined` if the response had no body / non-JSON content. */\n body: T | undefined;\n /** Raw text body, always populated. */\n text: string;\n}\n\nexport class HttpClient {\n constructor(\n private readonly baseUrl: string,\n private readonly defaults: HttpClientOptions = {},\n ) {}\n\n async get<T = unknown>(path: string, opts: HttpClientOptions = {}): Promise<HttpResponse<T>> {\n return this.request<T>('GET', path, undefined, opts);\n }\n\n async post<T = unknown>(\n path: string,\n body: unknown,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('POST', path, body, opts);\n }\n\n async delete<T = unknown>(\n path: string,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('DELETE', path, undefined, opts);\n }\n\n async put<T = unknown>(\n path: string,\n body: unknown,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('PUT', path, body, opts);\n }\n\n async options<T = unknown>(\n path: string,\n opts: HttpClientOptions = {},\n ): Promise<HttpResponse<T>> {\n return this.request<T>('OPTIONS', path, undefined, opts);\n }\n\n /** Build an absolute URL for a path under this client's base. */\n url(path: string): string {\n if (path.startsWith('http://') || path.startsWith('https://')) {return path;}\n return `${this.baseUrl.replace(/\\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;\n }\n\n private async request<T>(\n method: string,\n path: string,\n body: unknown,\n opts: HttpClientOptions,\n ): Promise<HttpResponse<T>> {\n const timeoutMs = opts.timeoutMs ?? this.defaults.timeoutMs ?? 15_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const headers: Record<string, string> = {\n ...this.defaults.headers,\n ...opts.headers,\n };\n if (body !== undefined && !headers['content-type'] && !headers['Content-Type']) {\n headers['content-type'] = 'application/json';\n }\n\n try {\n const res = await fetch(this.url(path), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await res.text();\n let parsed: T | undefined;\n const contentType = res.headers.get('content-type') ?? '';\n if (text && contentType.includes('json')) {\n try {\n parsed = JSON.parse(text) as T;\n } catch {\n parsed = undefined;\n }\n }\n return {\n status: res.status,\n ok: res.ok,\n headers: res.headers,\n body: parsed,\n text,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nexport function httpClient(baseUrl: string, opts: HttpClientOptions = {}): HttpClient {\n return new HttpClient(baseUrl, opts);\n}\n","import { WebSocket, type RawData } from 'ws';\n\n/**\n * Test-friendly WebSocket wrapper.\n *\n * Adapted from `infra/kb-labs-gateway/apps/gateway-app/src/__tests__/live-gateway.e2e.test.ts`\n * — same tracking semantics so the harness can clean up leaked sockets\n * between tests even when an assertion fails mid-flight.\n */\n\nexport interface WsOptions {\n headers?: Record<string, string>;\n /** ms. Default 8000. */\n openTimeoutMs?: number;\n}\n\n/** Opaque handle returned from `connectWs`. */\nexport interface WsHandle {\n readonly socket: WebSocket;\n /** Send a JSON-stringified message. */\n send(data: unknown): void;\n /** Wait for the next message, optionally satisfying a predicate. */\n waitForMessage<T = unknown>(\n opts?: { timeoutMs?: number; predicate?: (msg: T) => boolean },\n ): Promise<T>;\n /** Collect the next N messages (parsed as JSON). */\n collect<T = unknown>(count: number, timeoutMs?: number): Promise<T[]>;\n close(code?: number): void;\n}\n\n/**\n * Shared registry of open sockets. Call `closeAllTrackedSockets()` from an\n * `afterEach` to guarantee cleanup.\n */\nconst openSockets = new Set<WebSocket>();\n\nexport async function closeAllTrackedSockets(graceMs = 150): Promise<void> {\n const toClose = [...openSockets];\n for (const ws of toClose) {\n if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {\n try {\n ws.close(1000);\n } catch {\n // intentionally empty: best-effort cleanup, socket may already be dead\n }\n }\n }\n openSockets.clear();\n if (toClose.length > 0) {\n await new Promise((r) => { setTimeout(r, graceMs); });\n }\n}\n\nexport function connectWs(url: string, opts: WsOptions = {}): Promise<WsHandle> {\n const openTimeoutMs = opts.openTimeoutMs ?? 8000;\n return new Promise((resolvePromise, reject) => {\n const ws = new WebSocket(url, {\n headers: opts.headers,\n });\n openSockets.add(ws);\n ws.on('close', () => openSockets.delete(ws));\n\n const timer = setTimeout(() => {\n reject(new Error(`WebSocket open timeout after ${openTimeoutMs}ms: ${url}`));\n try { ws.close(); } catch {\n // intentionally empty: best-effort cleanup on timeout\n }\n }, openTimeoutMs);\n\n ws.once('open', () => {\n clearTimeout(timer);\n resolvePromise(wrapHandle(ws));\n });\n ws.once('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\nfunction wrapHandle(ws: WebSocket): WsHandle {\n return {\n socket: ws,\n send(data: unknown) {\n ws.send(typeof data === 'string' ? data : JSON.stringify(data));\n },\n waitForMessage<T = unknown>(\n opts: { timeoutMs?: number; predicate?: (msg: T) => boolean } = {},\n ): Promise<T> {\n const timeoutMs = opts.timeoutMs ?? 5000;\n return new Promise((resolvePromise, reject) => {\n const timer = setTimeout(() => {\n ws.off('message', onMessage);\n reject(new Error(`WebSocket message timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n const onMessage = (raw: RawData) => {\n let msg: T;\n try {\n msg = JSON.parse(raw.toString()) as T;\n } catch (err) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n reject(new Error(`Non-JSON WebSocket message: ${(err as Error).message}`));\n return;\n }\n if (!opts.predicate || opts.predicate(msg)) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n resolvePromise(msg);\n }\n };\n ws.on('message', onMessage);\n });\n },\n collect<T = unknown>(count: number, timeoutMs = 5000): Promise<T[]> {\n return new Promise((resolvePromise, reject) => {\n const msgs: T[] = [];\n const timer = setTimeout(() => {\n ws.off('message', onMessage);\n reject(\n new Error(\n `Timeout: expected ${count} WS messages, got ${msgs.length}: ${JSON.stringify(msgs)}`,\n ),\n );\n }, timeoutMs);\n const onMessage = (raw: RawData) => {\n msgs.push(JSON.parse(raw.toString()) as T);\n if (msgs.length >= count) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n resolvePromise(msgs);\n }\n };\n ws.on('message', onMessage);\n });\n },\n close(code = 1000) {\n try {\n ws.close(code);\n } catch {\n // intentionally empty: best-effort cleanup\n }\n },\n };\n}\n","/**\n * Minimal Server-Sent Events reader.\n *\n * Consumes `text/event-stream` responses and yields one `SseEvent` per\n * `event:`/`data:` pair. Terminates on close, explicit `untilEvent`, or timeout.\n */\n\nexport interface SseEvent {\n /** SSE `event:` field. Empty string if absent. */\n event: string;\n /** SSE `data:` field, concatenated with newlines if multi-line. */\n data: string;\n /** Optional `id:` field. */\n id?: string;\n /** Parsed JSON body if `data` is valid JSON, else undefined. */\n json?: unknown;\n}\n\nexport interface SseOptions {\n headers?: Record<string, string>;\n /** If set, the iterator terminates after the first event with this name. */\n untilEvent?: string;\n /** Wall-clock timeout in ms. Default 30_000. */\n timeoutMs?: number;\n}\n\n/**\n * Async iterator over an SSE stream. Caller is responsible for iterating\n * (e.g. `for await (const event of readSse(url)) { ... }`).\n */\nexport async function* readSse(url: string, opts: SseOptions = {}): AsyncGenerator<SseEvent> {\n const timeoutMs = opts.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { accept: 'text/event-stream', ...opts.headers },\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n throw new Error(`SSE fetch failed: ${(err as Error).message}`);\n }\n\n if (!res.ok || !res.body) {\n clearTimeout(timer);\n throw new Error(`SSE response not ok: ${res.status} ${res.statusText}`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {return;}\n buffer += decoder.decode(value, { stream: true });\n\n // SSE events are separated by a blank line (\\n\\n).\n let sepIdx: number;\n while ((sepIdx = buffer.indexOf('\\n\\n')) !== -1) {\n const rawEvent = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n const parsed = parseEvent(rawEvent);\n if (!parsed) {continue;}\n yield parsed;\n if (opts.untilEvent && parsed.event === opts.untilEvent) {\n return;\n }\n }\n }\n } finally {\n clearTimeout(timer);\n try { reader.cancel(); } catch {\n // intentionally empty: reader may already be closed\n }\n }\n}\n\nfunction parseEvent(raw: string): SseEvent | null {\n if (!raw.trim()) {return null;}\n let eventName = '';\n const dataLines: string[] = [];\n let id: string | undefined;\n\n for (const line of raw.split('\\n')) {\n if (!line || line.startsWith(':')) {continue;} // comment / keep-alive\n const colonIdx = line.indexOf(':');\n if (colonIdx === -1) {continue;}\n const field = line.slice(0, colonIdx);\n // Per spec: strip a single leading space from value.\n const value = line.slice(colonIdx + 1).replace(/^ /, '');\n if (field === 'event') {eventName = value;}\n else if (field === 'data') {dataLines.push(value);}\n else if (field === 'id') {id = value;}\n }\n\n if (eventName === '' && dataLines.length === 0) {return null;}\n const data = dataLines.join('\\n');\n let json: unknown;\n if (data) {\n try { json = JSON.parse(data); } catch { /* not JSON, leave undefined */ }\n }\n return { event: eventName, data, id, json };\n}\n","/**\n * Gateway auth helpers.\n *\n * Extracted from `infra/kb-labs-gateway/apps/gateway-app/src/__tests__/live-gateway.e2e.test.ts`\n * (`getJwtToken`, `registerHost`) so every e2e test can reuse the same flow.\n *\n * Flow:\n * 1. POST /auth/register { name, namespaceId } -> { clientId, clientSecret, hostId }\n * 2. POST /auth/token { clientId, clientSecret } -> { accessToken }\n */\n\nimport type { HttpClient } from './http-client.js';\n\nexport interface AgentCredentials {\n accessToken: string;\n clientId: string;\n hostId: string;\n}\n\nexport interface HostCredentials {\n hostId: string;\n machineToken: string;\n}\n\nexport async function registerAgent(\n client: HttpClient,\n opts: { name?: string; namespaceId: string } = { namespaceId: 'e2e-default' },\n): Promise<AgentCredentials> {\n const name = opts.name ?? 'e2e-agent';\n const regRes = await client.post<{ clientId: string; clientSecret: string; hostId: string }>(\n '/auth/register',\n { name, namespaceId: opts.namespaceId },\n );\n if (!regRes.ok || !regRes.body) {\n throw new Error(`/auth/register failed: ${regRes.status} ${regRes.text}`);\n }\n const { clientId, clientSecret, hostId } = regRes.body;\n\n const tokenRes = await client.post<{ accessToken: string }>(\n '/auth/token',\n { clientId, clientSecret },\n );\n if (!tokenRes.ok || !tokenRes.body) {\n throw new Error(`/auth/token failed: ${tokenRes.status} ${tokenRes.text}`);\n }\n\n return { accessToken: tokenRes.body.accessToken, clientId, hostId };\n}\n\nexport async function registerHost(\n client: HttpClient,\n opts: {\n name?: string;\n namespaceId: string;\n capabilities?: string[];\n workspacePaths?: string[];\n },\n): Promise<HostCredentials> {\n const res = await client.post<HostCredentials>('/hosts/register', {\n name: opts.name ?? 'e2e-host',\n namespaceId: opts.namespaceId,\n capabilities: opts.capabilities ?? ['filesystem'],\n workspacePaths: opts.workspacePaths ?? [],\n });\n if (!res.ok || !res.body) {\n throw new Error(`/hosts/register failed: ${res.status} ${res.text}`);\n }\n return res.body;\n}\n","import { mkdir, mkdtemp, rm, writeFile, copyFile, readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join, resolve } from 'node:path';\n\n/**\n * An isolated temporary project root with its own `.kb/` directory.\n *\n * Used by marketplace/plugin e2e tests so real workspace `.kb/marketplace.lock`\n * and `.kb/plugins.json` are never mutated.\n *\n * Pass the returned `root` via `KB_PROJECT_ROOT` env when starting kb-dev.\n */\nexport interface IsolatedProjectRoot {\n /** Absolute path to the temp project root. */\n root: string;\n /** Absolute path to `<root>/.kb/devservices.yaml`. */\n devConfigPath: string;\n /** Delete the temp dir. Safe to call multiple times. */\n cleanup(): Promise<void>;\n}\n\nexport interface IsolatedProjectRootOptions {\n /**\n * If given, copies the devservices.yaml from this existing workspace root.\n * If omitted, a minimal config with only `state-daemon` is written.\n */\n copyDevConfigFrom?: string;\n /** Prefix for the temp dir name (for human diagnosis). */\n prefix?: string;\n}\n\nconst MINIMAL_DEV_CONFIG = `name: KB Labs E2E Isolated Root\n\ngroups:\n infra: [state-daemon]\n\nservices:\n state-daemon:\n name: State Daemon\n description: Distributed state management\n group: infra\n type: node\n command: node ./plugins/state/daemon/core-state-daemon/dist/bin.cjs\n healthCheck: http://localhost:7777/health\n port: 7777\n url: http://localhost:7777\n env:\n KB_STATE_DAEMON_PORT: \"7777\"\n KB_STATE_DAEMON_HOST: localhost\n\nsettings:\n logsDir: .kb/logs/tmp\n pidDir: .kb/tmp\n startTimeout: 30000\n healthCheckInterval: 1000\n`;\n\nexport async function createIsolatedProjectRoot(\n opts: IsolatedProjectRootOptions = {},\n): Promise<IsolatedProjectRoot> {\n const prefix = opts.prefix ?? 'kb-e2e-';\n const root = await mkdtemp(join(tmpdir(), prefix));\n const kbDir = join(root, '.kb');\n await mkdir(kbDir, { recursive: true });\n await mkdir(join(kbDir, 'tmp'), { recursive: true });\n await mkdir(join(kbDir, 'logs', 'tmp'), { recursive: true });\n\n const devConfigPath = join(kbDir, 'devservices.yaml');\n\n if (opts.copyDevConfigFrom) {\n const source = resolve(opts.copyDevConfigFrom, '.kb/devservices.yaml');\n if (!existsSync(source)) {\n throw new Error(`copyDevConfigFrom: no devservices.yaml at ${source}`);\n }\n // Read to validate it exists and is readable.\n await readFile(source, 'utf8');\n await copyFile(source, devConfigPath);\n } else {\n await writeFile(devConfigPath, MINIMAL_DEV_CONFIG, 'utf8');\n }\n\n let disposed = false;\n const cleanup = async () => {\n if (disposed) {return;}\n disposed = true;\n try {\n await rm(root, { recursive: true, force: true });\n } catch {\n // intentionally empty: best-effort cleanup, tmpdir will be reaped eventually\n }\n };\n\n return { root, devConfigPath, cleanup };\n}\n","import { createHash } from 'node:crypto';\nimport { nanoid } from 'nanoid';\n\n/**\n * Build a deterministic-yet-unique namespace prefix for e2e test resources.\n *\n * Shape: `e2e-<fileHash>-<nanoid>`\n *\n * - `fileHash` is 6 hex chars of sha1(filePath) — lets you grep leaked resources\n * back to the test file that created them.\n * - `nanoid` is a 10-char random suffix — lets parallel runs never collide.\n *\n * Pass `import.meta.url` from the test file.\n */\nexport function makeTestNamespace(fileUrlOrPath: string, prefix = 'e2e'): string {\n const hash = createHash('sha1').update(fileUrlOrPath).digest('hex').slice(0, 6);\n return `${prefix}-${hash}-${nanoid(10)}`;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kb-labs/shared-testing-e2e",
|
|
3
3
|
"description": "E2E test harness for KB Labs platform — kb-dev controller, HTTP/WS/SSE helpers, isolated project roots",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.62.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"tsup": "^8.5.0",
|
|
29
29
|
"typescript": "^5.6.3",
|
|
30
30
|
"vitest": "^3.2.4",
|
|
31
|
-
"@kb-labs/devkit": "2.
|
|
31
|
+
"@kb-labs/devkit": "2.62.0"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=20.0.0",
|