(model: SmokeModel, prompt: SmokePrompt, apiKey: string)
| 216 | } |
| 217 | |
| 218 | async function runOne(model: SmokeModel, prompt: SmokePrompt, apiKey: string): Promise<RunResult> { |
| 219 | const t0 = Date.now(); |
| 220 | const workspaceRoot = mkdtempSync(join(tmpdir(), 'codesign-smoke-')); |
| 221 | try { |
| 222 | const fs = makeTmpdirFs(workspaceRoot); |
| 223 | const result = await generateViaAgent( |
| 224 | { |
| 225 | prompt: prompt.text, |
| 226 | history: [], |
| 227 | model: { provider: model.provider as never, modelId: model.modelId }, |
| 228 | apiKey, |
| 229 | onRetry: (info) => { |
| 230 | console.log(` ${COLOR.dim}↻ retry: ${info.reason}${COLOR.reset}`); |
| 231 | }, |
| 232 | }, |
| 233 | { |
| 234 | fs, |
| 235 | // Stub the runtime verifier so `done` skips the BrowserWindow check. |
| 236 | // We're outside Electron — running it would crash. Static lint still |
| 237 | // fires inside `done` itself. |
| 238 | runtimeVerify: async () => [], |
| 239 | }, |
| 240 | ); |
| 241 | const ms = Date.now() - t0; |
| 242 | const artifact = result.artifacts[0]; |
| 243 | if (!artifact?.content?.trim()) { |
| 244 | throw new Error('No HTML artifact returned from generateViaAgent()'); |
| 245 | } |
| 246 | const html = artifact.content; |
| 247 | const artifactPath = resolve(SMOKE_DIR, `${slug(model, prompt)}.html`); |
| 248 | // Use platform-aware containment instead of POSIX-only startsWith — slug |
| 249 | // sanitization should already prevent traversal, this is defense-in-depth. |
| 250 | const rel = relative(SMOKE_DIR, artifactPath); |
| 251 | if (rel.startsWith('..') || isAbsolute(rel)) { |
| 252 | throw new Error(`Refusing to write outside ${SMOKE_DIR}: ${artifactPath}`); |
| 253 | } |
| 254 | writeFileSync(artifactPath, html); |
| 255 | return { |
| 256 | model: `${model.provider}/${model.modelId}`, |
| 257 | prompt: prompt.name, |
| 258 | ok: true, |
| 259 | ms, |
| 260 | bytes: html.length, |
| 261 | artifactPath, |
| 262 | issues: qualityCheck(html), |
| 263 | }; |
| 264 | } catch (err) { |
| 265 | return { |
| 266 | model: `${model.provider}/${model.modelId}`, |
| 267 | prompt: prompt.name, |
| 268 | ok: false, |
| 269 | ms: Date.now() - t0, |
| 270 | bytes: 0, |
| 271 | issues: [], |
| 272 | error: err instanceof Error ? err.message : String(err), |
| 273 | }; |
| 274 | } finally { |
| 275 | try { |
no test coverage detected