@vibe-interviewing/core 0.1.0 → 0.3.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/README.md +26 -0
- package/dist/chunk-CI3BD2WQ.js +141 -0
- package/dist/chunk-CI3BD2WQ.js.map +1 -0
- package/dist/index.d.ts +19 -364
- package/dist/index.js +172 -105
- package/dist/index.js.map +1 -1
- package/dist/network/index.d.ts +81 -0
- package/dist/network/index.js +309 -0
- package/dist/network/index.js.map +1 -0
- package/dist/session-code-CfhXelpW.d.ts +519 -0
- package/package.json +6 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/network/server.ts","../../src/network/client.ts","../../src/network/cloud-client.ts"],"sourcesContent":["import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { execSync } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { networkInterfaces } from 'node:os'\nimport { EventEmitter } from 'node:events'\nimport type { Session } from '../session/types.js'\nimport type { ScenarioConfig } from '../scenario/types.js'\nimport { encodeSessionCode } from './session-code.js'\n\n/** Metadata served to candidates */\nexport interface SessionMetadata {\n scenarioName: string\n type: string\n difficulty: string\n estimatedTime: string\n briefing: string\n setupCommands: string[]\n}\n\n/** Events emitted by the session server */\nexport interface SessionServerEvents {\n 'candidate-connected': []\n 'download-complete': []\n}\n\n/** HTTP server that serves a prepared workspace to a remote candidate */\nexport class SessionServer extends EventEmitter<SessionServerEvents> {\n private server: Server | null = null\n\n /** Start serving the session workspace */\n async start(\n session: Session,\n config: ScenarioConfig,\n port?: number,\n ): Promise<{ code: string; port: number; host: string }> {\n // Create tarball of the workspace\n const tarball = join(session.workdir, '..', `${session.id}.tar.gz`)\n execSync(`tar czf \"${tarball}\" -C \"${session.workdir}\" .`, { stdio: 'pipe' })\n\n const systemPrompt = await readFile(session.systemPromptPath, 'utf-8')\n\n const metadata: SessionMetadata = {\n scenarioName: config.name,\n type: config.type,\n difficulty: config.difficulty,\n estimatedTime: config.estimated_time,\n briefing: config.briefing,\n setupCommands: config.setup,\n }\n\n this.server = createServer((req: IncomingMessage, res: ServerResponse) => {\n if (req.method !== 'GET') {\n res.writeHead(405)\n res.end()\n return\n }\n\n switch (req.url) {\n case '/metadata':\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(metadata))\n break\n\n case '/system-prompt':\n this.emit('candidate-connected')\n res.writeHead(200, { 'Content-Type': 'text/plain' })\n res.end(systemPrompt)\n break\n\n case '/workspace':\n readFile(tarball)\n .then((data) => {\n res.writeHead(200, {\n 'Content-Type': 'application/gzip',\n 'Content-Length': data.length,\n })\n res.end(data)\n this.emit('download-complete')\n })\n .catch(() => {\n res.writeHead(500)\n res.end('Failed to read workspace tarball')\n })\n break\n\n default:\n res.writeHead(404)\n res.end()\n }\n })\n\n const listenPort = port ?? 0\n\n return new Promise((resolve, reject) => {\n this.server!.listen(listenPort, () => {\n const addr = this.server!.address()\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to get server address'))\n return\n }\n\n const host = getLanIp()\n const code = encodeSessionCode(host, addr.port)\n\n resolve({ code, port: addr.port, host })\n })\n\n this.server!.on('error', reject)\n })\n }\n\n /** Stop the server and clean up */\n async stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.server) {\n this.server.close(() => resolve())\n } else {\n resolve()\n }\n })\n }\n}\n\n/** Get the first non-internal IPv4 address */\nfunction getLanIp(): string {\n const interfaces = networkInterfaces()\n for (const entries of Object.values(interfaces)) {\n if (!entries) continue\n for (const entry of entries) {\n if (entry.family === 'IPv4' && !entry.internal) {\n return entry.address\n }\n }\n }\n return '127.0.0.1'\n}\n","import { get } from 'node:http'\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { execSync } from 'node:child_process'\nimport { randomBytes } from 'node:crypto'\nimport { VibeError } from '../errors.js'\nimport type { SessionMetadata } from './server.js'\nimport type { ProgressCallback } from '../session/manager.js'\n\n/** Result of downloading a session from a host */\nexport interface DownloadedSession {\n /** Path to the extracted workspace */\n workdir: string\n /** Path to the system prompt file */\n systemPromptPath: string\n /** Session metadata from the host */\n metadata: SessionMetadata\n /** Generated session ID */\n id: string\n}\n\n/** Error for network/connection failures */\nexport class NetworkError extends VibeError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR', 'Check the session code and ensure the host is running')\n }\n}\n\n/** Fetch JSON from an HTTP endpoint */\nfunction fetchJson<T>(host: string, port: number, path: string): Promise<T> {\n return new Promise((resolve, reject) => {\n const req = get({ hostname: host, port, path, timeout: 10000 }, (res) => {\n if (res.statusCode !== 200) {\n reject(new NetworkError(`Server returned ${res.statusCode} for ${path}`))\n return\n }\n let data = ''\n res.on('data', (chunk: Buffer) => {\n data += chunk.toString()\n })\n res.on('end', () => {\n try {\n resolve(JSON.parse(data) as T)\n } catch {\n reject(new NetworkError(`Invalid response from server for ${path}`))\n }\n })\n })\n req.on('error', (err) => reject(new NetworkError(`Connection failed: ${err.message}`)))\n req.on('timeout', () => {\n req.destroy()\n reject(new NetworkError('Connection timed out'))\n })\n })\n}\n\n/** Fetch text from an HTTP endpoint */\nfunction fetchText(host: string, port: number, path: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const req = get({ hostname: host, port, path, timeout: 10000 }, (res) => {\n if (res.statusCode !== 200) {\n reject(new NetworkError(`Server returned ${res.statusCode} for ${path}`))\n return\n }\n let data = ''\n res.on('data', (chunk: Buffer) => {\n data += chunk.toString()\n })\n res.on('end', () => resolve(data))\n })\n req.on('error', (err) => reject(new NetworkError(`Connection failed: ${err.message}`)))\n req.on('timeout', () => {\n req.destroy()\n reject(new NetworkError('Connection timed out'))\n })\n })\n}\n\n/** Download a binary file from an HTTP endpoint */\nfunction fetchBinary(host: string, port: number, path: string): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const req = get({ hostname: host, port, path, timeout: 60000 }, (res) => {\n if (res.statusCode !== 200) {\n reject(new NetworkError(`Server returned ${res.statusCode} for ${path}`))\n return\n }\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => resolve(Buffer.concat(chunks)))\n })\n req.on('error', (err) => reject(new NetworkError(`Download failed: ${err.message}`)))\n req.on('timeout', () => {\n req.destroy()\n reject(new NetworkError('Download timed out'))\n })\n })\n}\n\n/**\n * Download a session from a host.\n *\n * Fetches metadata, system prompt, and workspace tarball, then\n * extracts everything to a local directory.\n */\nexport async function downloadSession(\n host: string,\n port: number,\n targetDir?: string,\n onProgress?: ProgressCallback,\n): Promise<DownloadedSession> {\n const id = randomBytes(4).toString('hex')\n\n // 1. Fetch metadata\n onProgress?.('Connecting to host...')\n const metadata = await fetchJson<SessionMetadata>(host, port, '/metadata')\n\n // 2. Fetch system prompt\n onProgress?.('Downloading scenario...')\n const systemPrompt = await fetchText(host, port, '/system-prompt')\n\n // 3. Download workspace tarball\n onProgress?.('Downloading workspace...')\n const tarball = await fetchBinary(host, port, '/workspace')\n\n // 4. Extract workspace\n onProgress?.('Extracting workspace...')\n const workdir = targetDir ?? join(homedir(), 'vibe-sessions', `${metadata.scenarioName}-${id}`)\n await mkdir(workdir, { recursive: true })\n\n // Write tarball to temp file, then extract\n const tarballPath = join(workdir, '..', `${id}-download.tar.gz`)\n await writeFile(tarballPath, tarball)\n execSync(`tar xzf \"${tarballPath}\" -C \"${workdir}\"`, { stdio: 'pipe' })\n\n // 5. Write system prompt outside workspace\n const promptDir = join(homedir(), '.vibe-interviewing', 'prompts')\n await mkdir(promptDir, { recursive: true })\n const systemPromptPath = join(promptDir, `${id}.md`)\n await writeFile(systemPromptPath, systemPrompt)\n\n return { workdir, systemPromptPath, metadata, id }\n}\n","import { writeFile, mkdir, readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { execSync } from 'node:child_process'\nimport { randomBytes } from 'node:crypto'\nimport { VibeError } from '../errors.js'\nimport type { SessionMetadata } from './server.js'\nimport type { ProgressCallback } from '../session/manager.js'\nimport type { DownloadedSession } from './client.js'\n\n/** Default Cloudflare Worker URL */\nexport const DEFAULT_WORKER_URL = 'https://api.vibe-interviewing.iar.dev'\n\n/** Get the configured worker URL (env override or default) */\nexport function getWorkerUrl(): string {\n return process.env['VIBE_WORKER_URL'] || DEFAULT_WORKER_URL\n}\n\n/** Error for cloud upload/download failures */\nexport class CloudError extends VibeError {\n constructor(message: string) {\n super(message, 'CLOUD_ERROR', 'Check your network connection and try again')\n }\n}\n\n/**\n * Upload a session to the cloud relay.\n *\n * Sends metadata, system prompt, and workspace tarball to the Cloudflare Worker.\n * Returns a session code the candidate can use to download.\n */\nexport async function uploadSession(\n workerUrl: string,\n metadata: SessionMetadata,\n systemPrompt: string,\n tarballPath: string,\n onProgress?: ProgressCallback,\n): Promise<{ code: string; expiresAt: string }> {\n onProgress?.('Reading workspace tarball...')\n const tarballData = await readFile(tarballPath)\n\n onProgress?.('Uploading to cloud...')\n const formData = new FormData()\n formData.set('metadata', JSON.stringify(metadata))\n formData.set('systemPrompt', systemPrompt)\n formData.set(\n 'workspace',\n new Blob([tarballData], { type: 'application/gzip' }),\n 'workspace.tar.gz',\n )\n\n let response: Response\n try {\n response = await fetch(`${workerUrl}/sessions`, {\n method: 'POST',\n body: formData,\n })\n } catch (err) {\n throw new CloudError(\n `Failed to connect to cloud relay at ${workerUrl}: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => 'unknown error')\n throw new CloudError(`Cloud upload failed (${response.status}): ${body}`)\n }\n\n const result = (await response.json()) as { code: string; expiresAt: string }\n return result\n}\n\n/**\n * Download a session from the cloud relay.\n *\n * Fetches metadata, system prompt, and workspace tarball from the Cloudflare Worker.\n */\nexport async function downloadSessionFromCloud(\n workerUrl: string,\n code: string,\n targetDir?: string,\n onProgress?: ProgressCallback,\n): Promise<DownloadedSession> {\n const id = randomBytes(4).toString('hex')\n\n // Strip VIBE- prefix and lowercase for the API\n let rawCode = code.trim().toUpperCase()\n if (rawCode.startsWith('VIBE-')) {\n rawCode = rawCode.slice(5)\n }\n rawCode = rawCode.toLowerCase()\n\n // 1. Fetch metadata\n onProgress?.('Connecting to cloud relay...')\n const metadataRes = await cloudFetch(`${workerUrl}/sessions/${rawCode}/metadata`)\n const metadata = (await metadataRes.json()) as SessionMetadata\n\n // 2. Fetch system prompt\n onProgress?.('Downloading scenario...')\n const promptRes = await cloudFetch(`${workerUrl}/sessions/${rawCode}/system-prompt`)\n const systemPrompt = await promptRes.text()\n\n // 3. Download workspace tarball\n onProgress?.('Downloading workspace...')\n const workspaceRes = await cloudFetch(`${workerUrl}/sessions/${rawCode}/workspace`)\n const tarball = Buffer.from(await workspaceRes.arrayBuffer())\n\n // 4. Extract workspace\n onProgress?.('Extracting workspace...')\n const workdir = targetDir ?? join(homedir(), 'vibe-sessions', `${metadata.scenarioName}-${id}`)\n await mkdir(workdir, { recursive: true })\n\n const tarballPath = join(workdir, '..', `${id}-download.tar.gz`)\n await writeFile(tarballPath, tarball)\n execSync(`tar xzf \"${tarballPath}\" -C \"${workdir}\"`, { stdio: 'pipe' })\n\n // 5. Write system prompt outside workspace\n const promptDir = join(homedir(), '.vibe-interviewing', 'prompts')\n await mkdir(promptDir, { recursive: true })\n const systemPromptPath = join(promptDir, `${id}.md`)\n await writeFile(systemPromptPath, systemPrompt)\n\n return { workdir, systemPromptPath, metadata, id }\n}\n\n/** Fetch from the cloud worker with error handling */\nasync function cloudFetch(url: string): Promise<Response> {\n let response: Response\n try {\n response = await fetch(url, { signal: AbortSignal.timeout(60000) })\n } catch (err) {\n if (err instanceof DOMException && err.name === 'TimeoutError') {\n throw new CloudError('Cloud download timed out')\n }\n throw new CloudError(\n `Failed to connect to cloud relay: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n if (response.status === 404) {\n throw new CloudError('Session not found or expired')\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => 'unknown error')\n throw new CloudError(`Cloud request failed (${response.status}): ${body}`)\n }\n\n return response\n}\n"],"mappings":"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","names":["join","execSync","join","execSync","writeFile","mkdir","readFile","join","homedir","execSync","randomBytes","readFile","randomBytes","join","homedir","mkdir","writeFile","execSync"]}
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const AIRulesSchema: z.ZodObject<{
|
|
4
|
+
/** Role description for the AI assistant */
|
|
5
|
+
role: z.ZodString;
|
|
6
|
+
/** Behavioral rules (e.g., "don't reveal the answer") */
|
|
7
|
+
rules: z.ZodArray<z.ZodString, "many">;
|
|
8
|
+
/** Knowledge about the bug/solution (hidden from candidate) */
|
|
9
|
+
knowledge: z.ZodString;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
role: string;
|
|
12
|
+
rules: string[];
|
|
13
|
+
knowledge: string;
|
|
14
|
+
}, {
|
|
15
|
+
role: string;
|
|
16
|
+
rules: string[];
|
|
17
|
+
knowledge: string;
|
|
18
|
+
}>;
|
|
19
|
+
declare const EvaluationSchema: z.ZodObject<{
|
|
20
|
+
/** Evaluation criteria for the interviewer */
|
|
21
|
+
criteria: z.ZodArray<z.ZodString, "many">;
|
|
22
|
+
/** Description of the expected fix */
|
|
23
|
+
expected_fix: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
criteria: string[];
|
|
26
|
+
expected_fix?: string | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
criteria: string[];
|
|
29
|
+
expected_fix?: string | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
/** A signal to watch for during the interview, with green and red flag indicators */
|
|
32
|
+
declare const KeySignalSchema: z.ZodObject<{
|
|
33
|
+
/** What behavior or skill this signal measures */
|
|
34
|
+
signal: z.ZodString;
|
|
35
|
+
/** What a strong candidate does (green flag) */
|
|
36
|
+
positive: z.ZodString;
|
|
37
|
+
/** What a weak candidate does (red flag) */
|
|
38
|
+
negative: z.ZodString;
|
|
39
|
+
}, "strip", z.ZodTypeAny, {
|
|
40
|
+
signal: string;
|
|
41
|
+
positive: string;
|
|
42
|
+
negative: string;
|
|
43
|
+
}, {
|
|
44
|
+
signal: string;
|
|
45
|
+
positive: string;
|
|
46
|
+
negative: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Structured guide for the interviewer — shown during hosting, never to the candidate */
|
|
49
|
+
declare const InterviewerGuideSchema: z.ZodObject<{
|
|
50
|
+
/** High-level summary of what this scenario evaluates and why */
|
|
51
|
+
overview: z.ZodString;
|
|
52
|
+
/** Specific behaviors to watch for, with green/red flag indicators */
|
|
53
|
+
key_signals: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
54
|
+
/** What behavior or skill this signal measures */
|
|
55
|
+
signal: z.ZodString;
|
|
56
|
+
/** What a strong candidate does (green flag) */
|
|
57
|
+
positive: z.ZodString;
|
|
58
|
+
/** What a weak candidate does (red flag) */
|
|
59
|
+
negative: z.ZodString;
|
|
60
|
+
}, "strip", z.ZodTypeAny, {
|
|
61
|
+
signal: string;
|
|
62
|
+
positive: string;
|
|
63
|
+
negative: string;
|
|
64
|
+
}, {
|
|
65
|
+
signal: string;
|
|
66
|
+
positive: string;
|
|
67
|
+
negative: string;
|
|
68
|
+
}>, "many">>;
|
|
69
|
+
/** Common mistakes candidates make */
|
|
70
|
+
common_pitfalls: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
71
|
+
/** Questions to ask the candidate after the session */
|
|
72
|
+
debrief_questions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
73
|
+
}, "strip", z.ZodTypeAny, {
|
|
74
|
+
overview: string;
|
|
75
|
+
key_signals: {
|
|
76
|
+
signal: string;
|
|
77
|
+
positive: string;
|
|
78
|
+
negative: string;
|
|
79
|
+
}[];
|
|
80
|
+
common_pitfalls: string[];
|
|
81
|
+
debrief_questions: string[];
|
|
82
|
+
}, {
|
|
83
|
+
overview: string;
|
|
84
|
+
key_signals?: {
|
|
85
|
+
signal: string;
|
|
86
|
+
positive: string;
|
|
87
|
+
negative: string;
|
|
88
|
+
}[] | undefined;
|
|
89
|
+
common_pitfalls?: string[] | undefined;
|
|
90
|
+
debrief_questions?: string[] | undefined;
|
|
91
|
+
}>;
|
|
92
|
+
/** Scenario type — determines validation rules and system prompt context */
|
|
93
|
+
declare const ScenarioTypeSchema: z.ZodDefault<z.ZodEnum<["debug", "feature", "refactor"]>>;
|
|
94
|
+
/** Full scenario configuration schema */
|
|
95
|
+
declare const ScenarioConfigSchema: z.ZodObject<{
|
|
96
|
+
/** Scenario display name */
|
|
97
|
+
name: z.ZodString;
|
|
98
|
+
/** One-line description (candidate-visible — describe symptoms/task, never the root cause or solution) */
|
|
99
|
+
description: z.ZodString;
|
|
100
|
+
/** Scenario type: debug (find a bug), feature (build something), refactor (improve code) */
|
|
101
|
+
type: z.ZodDefault<z.ZodEnum<["debug", "feature", "refactor"]>>;
|
|
102
|
+
/** Difficulty level */
|
|
103
|
+
difficulty: z.ZodEnum<["easy", "medium", "hard"]>;
|
|
104
|
+
/** Estimated time (e.g., "30-45m") */
|
|
105
|
+
estimated_time: z.ZodString;
|
|
106
|
+
/** Searchable tags */
|
|
107
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
108
|
+
/** GitHub repo URL or owner/repo shorthand */
|
|
109
|
+
repo: z.ZodString;
|
|
110
|
+
/** Commit SHA to pin the clone to (ensures reproducibility) */
|
|
111
|
+
commit: z.ZodString;
|
|
112
|
+
/** Shell commands to run after cloning (e.g., ["npm install"]) */
|
|
113
|
+
setup: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
114
|
+
/** Find-and-replace patches to inject the bug after cloning */
|
|
115
|
+
patch: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
116
|
+
/** Path to the file relative to repo root */
|
|
117
|
+
file: z.ZodString;
|
|
118
|
+
/** The original text to find */
|
|
119
|
+
find: z.ZodString;
|
|
120
|
+
/** The replacement text (with the bug) */
|
|
121
|
+
replace: z.ZodString;
|
|
122
|
+
}, "strip", z.ZodTypeAny, {
|
|
123
|
+
find: string;
|
|
124
|
+
file: string;
|
|
125
|
+
replace: string;
|
|
126
|
+
}, {
|
|
127
|
+
find: string;
|
|
128
|
+
file: string;
|
|
129
|
+
replace: string;
|
|
130
|
+
}>, "many">>;
|
|
131
|
+
/** Files or directories to delete after cloning (globs relative to repo root) */
|
|
132
|
+
delete_files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
133
|
+
/** Briefing shown to the candidate (written like a team lead message) */
|
|
134
|
+
briefing: z.ZodString;
|
|
135
|
+
/** AI behavioral rules (injected via system prompt, hidden from candidate) */
|
|
136
|
+
ai_rules: z.ZodObject<{
|
|
137
|
+
/** Role description for the AI assistant */
|
|
138
|
+
role: z.ZodString;
|
|
139
|
+
/** Behavioral rules (e.g., "don't reveal the answer") */
|
|
140
|
+
rules: z.ZodArray<z.ZodString, "many">;
|
|
141
|
+
/** Knowledge about the bug/solution (hidden from candidate) */
|
|
142
|
+
knowledge: z.ZodString;
|
|
143
|
+
}, "strip", z.ZodTypeAny, {
|
|
144
|
+
role: string;
|
|
145
|
+
rules: string[];
|
|
146
|
+
knowledge: string;
|
|
147
|
+
}, {
|
|
148
|
+
role: string;
|
|
149
|
+
rules: string[];
|
|
150
|
+
knowledge: string;
|
|
151
|
+
}>;
|
|
152
|
+
/** Interviewer reference — what the fix/implementation looks like */
|
|
153
|
+
solution: z.ZodOptional<z.ZodString>;
|
|
154
|
+
/** Acceptance criteria for feature scenarios (concrete, testable requirements) */
|
|
155
|
+
acceptance_criteria: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
156
|
+
/** Evaluation rubric */
|
|
157
|
+
evaluation: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
/** Evaluation criteria for the interviewer */
|
|
159
|
+
criteria: z.ZodArray<z.ZodString, "many">;
|
|
160
|
+
/** Description of the expected fix */
|
|
161
|
+
expected_fix: z.ZodOptional<z.ZodString>;
|
|
162
|
+
}, "strip", z.ZodTypeAny, {
|
|
163
|
+
criteria: string[];
|
|
164
|
+
expected_fix?: string | undefined;
|
|
165
|
+
}, {
|
|
166
|
+
criteria: string[];
|
|
167
|
+
expected_fix?: string | undefined;
|
|
168
|
+
}>>;
|
|
169
|
+
/** Structured interviewer guide — what to watch for, common pitfalls, debrief questions */
|
|
170
|
+
interviewer_guide: z.ZodOptional<z.ZodObject<{
|
|
171
|
+
/** High-level summary of what this scenario evaluates and why */
|
|
172
|
+
overview: z.ZodString;
|
|
173
|
+
/** Specific behaviors to watch for, with green/red flag indicators */
|
|
174
|
+
key_signals: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
175
|
+
/** What behavior or skill this signal measures */
|
|
176
|
+
signal: z.ZodString;
|
|
177
|
+
/** What a strong candidate does (green flag) */
|
|
178
|
+
positive: z.ZodString;
|
|
179
|
+
/** What a weak candidate does (red flag) */
|
|
180
|
+
negative: z.ZodString;
|
|
181
|
+
}, "strip", z.ZodTypeAny, {
|
|
182
|
+
signal: string;
|
|
183
|
+
positive: string;
|
|
184
|
+
negative: string;
|
|
185
|
+
}, {
|
|
186
|
+
signal: string;
|
|
187
|
+
positive: string;
|
|
188
|
+
negative: string;
|
|
189
|
+
}>, "many">>;
|
|
190
|
+
/** Common mistakes candidates make */
|
|
191
|
+
common_pitfalls: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
192
|
+
/** Questions to ask the candidate after the session */
|
|
193
|
+
debrief_questions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
194
|
+
}, "strip", z.ZodTypeAny, {
|
|
195
|
+
overview: string;
|
|
196
|
+
key_signals: {
|
|
197
|
+
signal: string;
|
|
198
|
+
positive: string;
|
|
199
|
+
negative: string;
|
|
200
|
+
}[];
|
|
201
|
+
common_pitfalls: string[];
|
|
202
|
+
debrief_questions: string[];
|
|
203
|
+
}, {
|
|
204
|
+
overview: string;
|
|
205
|
+
key_signals?: {
|
|
206
|
+
signal: string;
|
|
207
|
+
positive: string;
|
|
208
|
+
negative: string;
|
|
209
|
+
}[] | undefined;
|
|
210
|
+
common_pitfalls?: string[] | undefined;
|
|
211
|
+
debrief_questions?: string[] | undefined;
|
|
212
|
+
}>>;
|
|
213
|
+
/** License of the original project */
|
|
214
|
+
license: z.ZodOptional<z.ZodString>;
|
|
215
|
+
}, "strip", z.ZodTypeAny, {
|
|
216
|
+
type: "debug" | "feature" | "refactor";
|
|
217
|
+
name: string;
|
|
218
|
+
description: string;
|
|
219
|
+
difficulty: "easy" | "medium" | "hard";
|
|
220
|
+
estimated_time: string;
|
|
221
|
+
tags: string[];
|
|
222
|
+
repo: string;
|
|
223
|
+
commit: string;
|
|
224
|
+
setup: string[];
|
|
225
|
+
patch: {
|
|
226
|
+
find: string;
|
|
227
|
+
file: string;
|
|
228
|
+
replace: string;
|
|
229
|
+
}[];
|
|
230
|
+
delete_files: string[];
|
|
231
|
+
briefing: string;
|
|
232
|
+
ai_rules: {
|
|
233
|
+
role: string;
|
|
234
|
+
rules: string[];
|
|
235
|
+
knowledge: string;
|
|
236
|
+
};
|
|
237
|
+
solution?: string | undefined;
|
|
238
|
+
acceptance_criteria?: string[] | undefined;
|
|
239
|
+
evaluation?: {
|
|
240
|
+
criteria: string[];
|
|
241
|
+
expected_fix?: string | undefined;
|
|
242
|
+
} | undefined;
|
|
243
|
+
interviewer_guide?: {
|
|
244
|
+
overview: string;
|
|
245
|
+
key_signals: {
|
|
246
|
+
signal: string;
|
|
247
|
+
positive: string;
|
|
248
|
+
negative: string;
|
|
249
|
+
}[];
|
|
250
|
+
common_pitfalls: string[];
|
|
251
|
+
debrief_questions: string[];
|
|
252
|
+
} | undefined;
|
|
253
|
+
license?: string | undefined;
|
|
254
|
+
}, {
|
|
255
|
+
name: string;
|
|
256
|
+
description: string;
|
|
257
|
+
difficulty: "easy" | "medium" | "hard";
|
|
258
|
+
estimated_time: string;
|
|
259
|
+
repo: string;
|
|
260
|
+
commit: string;
|
|
261
|
+
briefing: string;
|
|
262
|
+
ai_rules: {
|
|
263
|
+
role: string;
|
|
264
|
+
rules: string[];
|
|
265
|
+
knowledge: string;
|
|
266
|
+
};
|
|
267
|
+
type?: "debug" | "feature" | "refactor" | undefined;
|
|
268
|
+
tags?: string[] | undefined;
|
|
269
|
+
setup?: string[] | undefined;
|
|
270
|
+
patch?: {
|
|
271
|
+
find: string;
|
|
272
|
+
file: string;
|
|
273
|
+
replace: string;
|
|
274
|
+
}[] | undefined;
|
|
275
|
+
delete_files?: string[] | undefined;
|
|
276
|
+
solution?: string | undefined;
|
|
277
|
+
acceptance_criteria?: string[] | undefined;
|
|
278
|
+
evaluation?: {
|
|
279
|
+
criteria: string[];
|
|
280
|
+
expected_fix?: string | undefined;
|
|
281
|
+
} | undefined;
|
|
282
|
+
interviewer_guide?: {
|
|
283
|
+
overview: string;
|
|
284
|
+
key_signals?: {
|
|
285
|
+
signal: string;
|
|
286
|
+
positive: string;
|
|
287
|
+
negative: string;
|
|
288
|
+
}[] | undefined;
|
|
289
|
+
common_pitfalls?: string[] | undefined;
|
|
290
|
+
debrief_questions?: string[] | undefined;
|
|
291
|
+
} | undefined;
|
|
292
|
+
license?: string | undefined;
|
|
293
|
+
}>;
|
|
294
|
+
type ScenarioConfig = z.infer<typeof ScenarioConfigSchema>;
|
|
295
|
+
type ScenarioType = z.infer<typeof ScenarioTypeSchema>;
|
|
296
|
+
type AIRules = z.infer<typeof AIRulesSchema>;
|
|
297
|
+
type Evaluation = z.infer<typeof EvaluationSchema>;
|
|
298
|
+
type InterviewerGuide = z.infer<typeof InterviewerGuideSchema>;
|
|
299
|
+
type KeySignal = z.infer<typeof KeySignalSchema>;
|
|
300
|
+
/** Metadata about a discovered scenario */
|
|
301
|
+
interface ScenarioInfo {
|
|
302
|
+
/** Scenario name */
|
|
303
|
+
name: string;
|
|
304
|
+
/** Parsed config */
|
|
305
|
+
config: ScenarioConfig;
|
|
306
|
+
/** Whether this is a built-in scenario */
|
|
307
|
+
builtIn: boolean;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Event types that can be captured during a session */
|
|
311
|
+
type SessionEventType = 'stdout' | 'stderr' | 'command' | 'note';
|
|
312
|
+
/** A single timestamped event captured during a session */
|
|
313
|
+
interface SessionEvent {
|
|
314
|
+
/** Milliseconds since recording started */
|
|
315
|
+
timestamp: number;
|
|
316
|
+
/** The kind of event */
|
|
317
|
+
type: SessionEventType;
|
|
318
|
+
/** The captured data */
|
|
319
|
+
data: string;
|
|
320
|
+
}
|
|
321
|
+
/** Serialized recording format */
|
|
322
|
+
interface RecordingData {
|
|
323
|
+
/** Session ID this recording belongs to */
|
|
324
|
+
sessionId: string;
|
|
325
|
+
/** ISO string of when recording started */
|
|
326
|
+
startedAt: string;
|
|
327
|
+
/** All captured events */
|
|
328
|
+
events: SessionEvent[];
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Records timestamped events during an interview session.
|
|
332
|
+
*
|
|
333
|
+
* Captures stdout, stderr, commands, and notes with millisecond timestamps
|
|
334
|
+
* relative to when the recorder was created.
|
|
335
|
+
*/
|
|
336
|
+
declare class SessionRecorder {
|
|
337
|
+
private readonly events;
|
|
338
|
+
private readonly startTime;
|
|
339
|
+
private readonly startedAt;
|
|
340
|
+
constructor();
|
|
341
|
+
/** Record a timestamped event */
|
|
342
|
+
record(type: SessionEventType, data: string): void;
|
|
343
|
+
/** Get all recorded events */
|
|
344
|
+
getEvents(): ReadonlyArray<SessionEvent>;
|
|
345
|
+
/** Serialize the recording to a JSON-compatible object */
|
|
346
|
+
toJSON(sessionId: string): RecordingData;
|
|
347
|
+
/** Create a SessionRecorder pre-populated with events from serialized data */
|
|
348
|
+
static fromJSON(data: RecordingData): SessionRecorder;
|
|
349
|
+
/** Save the recording to disk */
|
|
350
|
+
save(sessionId: string): Promise<void>;
|
|
351
|
+
/** Load a recording from disk */
|
|
352
|
+
static load(sessionId: string): Promise<SessionRecorder>;
|
|
353
|
+
/** List all available recording session IDs */
|
|
354
|
+
static list(): Promise<string[]>;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Configuration for launching an AI coding tool */
|
|
358
|
+
interface LaunchConfig {
|
|
359
|
+
/** Scenario name for display */
|
|
360
|
+
scenarioName: string;
|
|
361
|
+
/** Path to system prompt file (hidden from candidate) */
|
|
362
|
+
systemPromptPath: string;
|
|
363
|
+
/** Model to use */
|
|
364
|
+
model?: string;
|
|
365
|
+
/** Permission mode for the AI tool */
|
|
366
|
+
permissionMode?: 'default' | 'plan' | 'acceptEdits' | 'bypassPermissions';
|
|
367
|
+
/** Tools to disallow (e.g., WebSearch for fairness) */
|
|
368
|
+
disallowedTools?: string[];
|
|
369
|
+
/** Whether to record stdout/stderr during the session */
|
|
370
|
+
recording?: boolean;
|
|
371
|
+
}
|
|
372
|
+
/** A running AI tool process */
|
|
373
|
+
interface LaunchedProcess {
|
|
374
|
+
/** Wait for the process to exit */
|
|
375
|
+
wait(): Promise<{
|
|
376
|
+
exitCode: number;
|
|
377
|
+
}>;
|
|
378
|
+
/** Kill the process */
|
|
379
|
+
kill(): Promise<void>;
|
|
380
|
+
/** Session recorder, present when recording is enabled */
|
|
381
|
+
recorder?: SessionRecorder;
|
|
382
|
+
}
|
|
383
|
+
/** Interface for AI coding tool launchers */
|
|
384
|
+
interface AIToolLauncher {
|
|
385
|
+
/** Internal name identifier */
|
|
386
|
+
readonly name: string;
|
|
387
|
+
/** Human-readable display name */
|
|
388
|
+
readonly displayName: string;
|
|
389
|
+
/** Check if this tool is installed and accessible */
|
|
390
|
+
isInstalled(): Promise<boolean>;
|
|
391
|
+
/** Get the installed version string */
|
|
392
|
+
getVersion(): Promise<string | null>;
|
|
393
|
+
/** Launch the tool pointed at a working directory */
|
|
394
|
+
launch(workdir: string, config: LaunchConfig): Promise<LaunchedProcess>;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Status of an interview session */
|
|
398
|
+
type SessionStatus = 'cloning' | 'setting-up' | 'running' | 'complete';
|
|
399
|
+
/** Interview session — used both in-memory and for persistence */
|
|
400
|
+
interface Session {
|
|
401
|
+
/** Unique session identifier */
|
|
402
|
+
id: string;
|
|
403
|
+
/** Name of the scenario being run */
|
|
404
|
+
scenarioName: string;
|
|
405
|
+
/** Local working directory for the candidate */
|
|
406
|
+
workdir: string;
|
|
407
|
+
/** Path to the system prompt file (outside workspace) */
|
|
408
|
+
systemPromptPath: string;
|
|
409
|
+
/** Current session status */
|
|
410
|
+
status: SessionStatus;
|
|
411
|
+
/** ISO timestamp of session creation */
|
|
412
|
+
createdAt: string;
|
|
413
|
+
/** ISO timestamp of when the AI tool was launched */
|
|
414
|
+
startedAt?: string;
|
|
415
|
+
/** ISO timestamp of session completion */
|
|
416
|
+
completedAt?: string;
|
|
417
|
+
/** Name of the AI tool used */
|
|
418
|
+
aiTool?: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Serializable session data for persistence.
|
|
422
|
+
* Identical to Session — kept as an alias for API clarity at persistence boundaries.
|
|
423
|
+
*/
|
|
424
|
+
type StoredSession = Session;
|
|
425
|
+
/** Convert a Session to a StoredSession for persistence */
|
|
426
|
+
declare function toStoredSession(session: Session): StoredSession;
|
|
427
|
+
|
|
428
|
+
/** Callback for reporting session progress */
|
|
429
|
+
type ProgressCallback = (stage: string) => void;
|
|
430
|
+
/** Manages the lifecycle of an interview session */
|
|
431
|
+
declare class SessionManager {
|
|
432
|
+
private launcher;
|
|
433
|
+
constructor(launcher: AIToolLauncher);
|
|
434
|
+
/**
|
|
435
|
+
* Create a new interview session.
|
|
436
|
+
*
|
|
437
|
+
* Flow:
|
|
438
|
+
* 1. Clone the repo at a pinned commit
|
|
439
|
+
* 2. Apply bug patches (find/replace in source files)
|
|
440
|
+
* 3. Delete files excluded by the scenario (e.g., tests that reveal the bug)
|
|
441
|
+
* 4. Wipe git history so the candidate can't diff to find the bug
|
|
442
|
+
* 5. Remove scenario.yaml from workspace (interviewer-only)
|
|
443
|
+
* 6. Write BRIEFING.md and system prompt
|
|
444
|
+
* 7. Run setup commands (npm install, etc.)
|
|
445
|
+
*/
|
|
446
|
+
createSession(config: ScenarioConfig, workdir?: string, onProgress?: ProgressCallback, options?: {
|
|
447
|
+
skipSetup?: boolean;
|
|
448
|
+
}): Promise<{
|
|
449
|
+
session: Session;
|
|
450
|
+
config: ScenarioConfig;
|
|
451
|
+
}>;
|
|
452
|
+
/** Launch the AI coding tool for an active session */
|
|
453
|
+
launchAITool(session: Session, _config: ScenarioConfig, launchConfig?: Partial<LaunchConfig>): Promise<{
|
|
454
|
+
exitCode: number;
|
|
455
|
+
}>;
|
|
456
|
+
/** Destroy a session by removing its stored data */
|
|
457
|
+
destroySession(session: Session): Promise<void>;
|
|
458
|
+
/** Get elapsed time since the AI tool was launched, formatted as a human-readable string */
|
|
459
|
+
getElapsedTime(session: Session): string | null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/** Base error class for all vibe-interviewing errors */
|
|
463
|
+
declare class VibeError extends Error {
|
|
464
|
+
readonly code: string;
|
|
465
|
+
readonly hint?: string | undefined;
|
|
466
|
+
constructor(message: string, code: string, hint?: string | undefined);
|
|
467
|
+
}
|
|
468
|
+
declare class ScenarioNotFoundError extends VibeError {
|
|
469
|
+
constructor(name: string);
|
|
470
|
+
}
|
|
471
|
+
declare class ScenarioValidationError extends VibeError {
|
|
472
|
+
readonly issues: string[];
|
|
473
|
+
constructor(message: string, issues: string[]);
|
|
474
|
+
}
|
|
475
|
+
declare class AIToolNotFoundError extends VibeError {
|
|
476
|
+
static readonly installHints: Record<string, string>;
|
|
477
|
+
constructor(tool: string);
|
|
478
|
+
}
|
|
479
|
+
declare class SessionNotFoundError extends VibeError {
|
|
480
|
+
constructor(id: string);
|
|
481
|
+
}
|
|
482
|
+
declare class GitCloneError extends VibeError {
|
|
483
|
+
constructor(repo: string, reason?: string);
|
|
484
|
+
}
|
|
485
|
+
declare class SetupError extends VibeError {
|
|
486
|
+
constructor(command: string, reason?: string);
|
|
487
|
+
}
|
|
488
|
+
declare class ScenarioFetchError extends VibeError {
|
|
489
|
+
constructor(url: string, reason?: string);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Encode a host:port pair into a human-typeable session code.
|
|
494
|
+
*
|
|
495
|
+
* Format: VIBE-XXXXXXXXXX (10 base36 chars encoding 6 bytes: 4 IP octets + 2 port bytes)
|
|
496
|
+
*/
|
|
497
|
+
declare function encodeSessionCode(host: string, port: number): string;
|
|
498
|
+
/**
|
|
499
|
+
* Decode a session code back to host:port.
|
|
500
|
+
*
|
|
501
|
+
* Accepts formats: VIBE-XXXXXXXXXX, vibe-xxxxxxxxxx, or just XXXXXXXXXX
|
|
502
|
+
*/
|
|
503
|
+
declare function decodeSessionCode(code: string): {
|
|
504
|
+
host: string;
|
|
505
|
+
port: number;
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* Check if a session code is a cloud code (6 chars) vs LAN code (10 chars).
|
|
509
|
+
*
|
|
510
|
+
* Cloud codes are 6 alphanumeric characters: VIBE-A3X9K2
|
|
511
|
+
* LAN codes are 10 alphanumeric characters: VIBE-3R8KW1F0NX
|
|
512
|
+
*/
|
|
513
|
+
declare function isCloudSessionCode(code: string): boolean;
|
|
514
|
+
/** Error for invalid session codes */
|
|
515
|
+
declare class InvalidSessionCodeError extends VibeError {
|
|
516
|
+
constructor(message: string);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export { type AIToolLauncher as A, type Evaluation as E, GitCloneError as G, type InterviewerGuide as I, type KeySignal as K, type LaunchConfig as L, type ProgressCallback as P, type RecordingData as R, type ScenarioConfig as S, VibeError as V, type ScenarioInfo as a, type LaunchedProcess as b, type StoredSession as c, type AIRules as d, AIToolNotFoundError as e, InvalidSessionCodeError as f, ScenarioConfigSchema as g, ScenarioFetchError as h, ScenarioNotFoundError as i, type ScenarioType as j, ScenarioValidationError as k, type Session as l, type SessionEvent as m, type SessionEventType as n, SessionManager as o, SessionNotFoundError as p, SessionRecorder as q, SetupError as r, decodeSessionCode as s, encodeSessionCode as t, isCloudSessionCode as u, toStoredSession as v };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-interviewing/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Core library for vibe-interviewing — scenario engine, session management, AI tool launchers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,13 +9,17 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./network": {
|
|
14
|
+
"types": "./dist/network/index.d.ts",
|
|
15
|
+
"default": "./dist/network/index.js"
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
18
|
"dependencies": {
|
|
15
19
|
"simple-git": "^3.27.0",
|
|
16
20
|
"yaml": "^2.7.0",
|
|
17
21
|
"zod": "^3.24.0",
|
|
18
|
-
"@vibe-interviewing/scenarios": "0.
|
|
22
|
+
"@vibe-interviewing/scenarios": "0.3.0"
|
|
19
23
|
},
|
|
20
24
|
"devDependencies": {
|
|
21
25
|
"@types/node": "^20.17.0",
|