@hasna/testers 0.0.4 → 0.0.6
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/cli/index.js +490 -9
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/flows.d.ts +12 -0
- package/dist/db/flows.d.ts.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -8
- package/dist/lib/ai-client.d.ts +9 -0
- package/dist/lib/ai-client.d.ts.map +1 -1
- package/dist/lib/runner.d.ts +6 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/mcp/index.js +955 -669
- package/dist/server/index.js +380 -85
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -10,6 +10,574 @@ var __export = (target, all) => {
|
|
|
10
10
|
set: (newValue) => all[name] = () => newValue
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
|
+
|
|
15
|
+
// src/types/index.ts
|
|
16
|
+
function projectFromRow(row) {
|
|
17
|
+
return {
|
|
18
|
+
id: row.id,
|
|
19
|
+
name: row.name,
|
|
20
|
+
path: row.path,
|
|
21
|
+
description: row.description,
|
|
22
|
+
createdAt: row.created_at,
|
|
23
|
+
updatedAt: row.updated_at
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function agentFromRow(row) {
|
|
27
|
+
return {
|
|
28
|
+
id: row.id,
|
|
29
|
+
name: row.name,
|
|
30
|
+
description: row.description,
|
|
31
|
+
role: row.role,
|
|
32
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
33
|
+
createdAt: row.created_at,
|
|
34
|
+
lastSeenAt: row.last_seen_at
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function scenarioFromRow(row) {
|
|
38
|
+
return {
|
|
39
|
+
id: row.id,
|
|
40
|
+
shortId: row.short_id,
|
|
41
|
+
projectId: row.project_id,
|
|
42
|
+
name: row.name,
|
|
43
|
+
description: row.description,
|
|
44
|
+
steps: JSON.parse(row.steps),
|
|
45
|
+
tags: JSON.parse(row.tags),
|
|
46
|
+
priority: row.priority,
|
|
47
|
+
model: row.model,
|
|
48
|
+
timeoutMs: row.timeout_ms,
|
|
49
|
+
targetPath: row.target_path,
|
|
50
|
+
requiresAuth: row.requires_auth === 1,
|
|
51
|
+
authConfig: row.auth_config ? JSON.parse(row.auth_config) : null,
|
|
52
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
53
|
+
version: row.version,
|
|
54
|
+
createdAt: row.created_at,
|
|
55
|
+
updatedAt: row.updated_at
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function runFromRow(row) {
|
|
59
|
+
return {
|
|
60
|
+
id: row.id,
|
|
61
|
+
projectId: row.project_id,
|
|
62
|
+
status: row.status,
|
|
63
|
+
url: row.url,
|
|
64
|
+
model: row.model,
|
|
65
|
+
headed: row.headed === 1,
|
|
66
|
+
parallel: row.parallel,
|
|
67
|
+
total: row.total,
|
|
68
|
+
passed: row.passed,
|
|
69
|
+
failed: row.failed,
|
|
70
|
+
startedAt: row.started_at,
|
|
71
|
+
finishedAt: row.finished_at,
|
|
72
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resultFromRow(row) {
|
|
76
|
+
return {
|
|
77
|
+
id: row.id,
|
|
78
|
+
runId: row.run_id,
|
|
79
|
+
scenarioId: row.scenario_id,
|
|
80
|
+
status: row.status,
|
|
81
|
+
reasoning: row.reasoning,
|
|
82
|
+
error: row.error,
|
|
83
|
+
stepsCompleted: row.steps_completed,
|
|
84
|
+
stepsTotal: row.steps_total,
|
|
85
|
+
durationMs: row.duration_ms,
|
|
86
|
+
model: row.model,
|
|
87
|
+
tokensUsed: row.tokens_used,
|
|
88
|
+
costCents: row.cost_cents,
|
|
89
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
90
|
+
createdAt: row.created_at
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function screenshotFromRow(row) {
|
|
94
|
+
return {
|
|
95
|
+
id: row.id,
|
|
96
|
+
resultId: row.result_id,
|
|
97
|
+
stepNumber: row.step_number,
|
|
98
|
+
action: row.action,
|
|
99
|
+
filePath: row.file_path,
|
|
100
|
+
width: row.width,
|
|
101
|
+
height: row.height,
|
|
102
|
+
timestamp: row.timestamp,
|
|
103
|
+
description: row.description,
|
|
104
|
+
pageUrl: row.page_url,
|
|
105
|
+
thumbnailPath: row.thumbnail_path
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function scheduleFromRow(row) {
|
|
109
|
+
return {
|
|
110
|
+
id: row.id,
|
|
111
|
+
projectId: row.project_id,
|
|
112
|
+
name: row.name,
|
|
113
|
+
cronExpression: row.cron_expression,
|
|
114
|
+
url: row.url,
|
|
115
|
+
scenarioFilter: JSON.parse(row.scenario_filter),
|
|
116
|
+
model: row.model,
|
|
117
|
+
headed: row.headed === 1,
|
|
118
|
+
parallel: row.parallel,
|
|
119
|
+
timeoutMs: row.timeout_ms,
|
|
120
|
+
enabled: row.enabled === 1,
|
|
121
|
+
lastRunId: row.last_run_id,
|
|
122
|
+
lastRunAt: row.last_run_at,
|
|
123
|
+
nextRunAt: row.next_run_at,
|
|
124
|
+
createdAt: row.created_at,
|
|
125
|
+
updatedAt: row.updated_at
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function flowFromRow(row) {
|
|
129
|
+
return {
|
|
130
|
+
id: row.id,
|
|
131
|
+
projectId: row.project_id,
|
|
132
|
+
name: row.name,
|
|
133
|
+
description: row.description,
|
|
134
|
+
scenarioIds: JSON.parse(row.scenario_ids),
|
|
135
|
+
createdAt: row.created_at,
|
|
136
|
+
updatedAt: row.updated_at
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
var MODEL_MAP, VersionConflictError, BrowserError, AIClientError, TodosConnectionError, ScheduleNotFoundError, DependencyCycleError;
|
|
140
|
+
var init_types = __esm(() => {
|
|
141
|
+
MODEL_MAP = {
|
|
142
|
+
quick: "claude-haiku-4-5-20251001",
|
|
143
|
+
thorough: "claude-sonnet-4-6-20260311",
|
|
144
|
+
deep: "claude-opus-4-6-20260311"
|
|
145
|
+
};
|
|
146
|
+
VersionConflictError = class VersionConflictError extends Error {
|
|
147
|
+
constructor(entity, id) {
|
|
148
|
+
super(`Version conflict on ${entity}: ${id}`);
|
|
149
|
+
this.name = "VersionConflictError";
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
BrowserError = class BrowserError extends Error {
|
|
153
|
+
constructor(message) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.name = "BrowserError";
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
AIClientError = class AIClientError extends Error {
|
|
159
|
+
constructor(message) {
|
|
160
|
+
super(message);
|
|
161
|
+
this.name = "AIClientError";
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
TodosConnectionError = class TodosConnectionError extends Error {
|
|
165
|
+
constructor(message) {
|
|
166
|
+
super(message);
|
|
167
|
+
this.name = "TodosConnectionError";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
ScheduleNotFoundError = class ScheduleNotFoundError extends Error {
|
|
171
|
+
constructor(id) {
|
|
172
|
+
super(`Schedule not found: ${id}`);
|
|
173
|
+
this.name = "ScheduleNotFoundError";
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
DependencyCycleError = class DependencyCycleError extends Error {
|
|
177
|
+
constructor(scenarioId, dependsOn) {
|
|
178
|
+
super(`Adding dependency ${dependsOn} to ${scenarioId} would create a cycle`);
|
|
179
|
+
this.name = "DependencyCycleError";
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// src/db/database.ts
|
|
185
|
+
import { Database } from "bun:sqlite";
|
|
186
|
+
import { mkdirSync, existsSync } from "fs";
|
|
187
|
+
import { dirname, join } from "path";
|
|
188
|
+
import { homedir } from "os";
|
|
189
|
+
function now() {
|
|
190
|
+
return new Date().toISOString();
|
|
191
|
+
}
|
|
192
|
+
function uuid() {
|
|
193
|
+
return crypto.randomUUID();
|
|
194
|
+
}
|
|
195
|
+
function shortUuid() {
|
|
196
|
+
return uuid().slice(0, 8);
|
|
197
|
+
}
|
|
198
|
+
function resolveDbPath() {
|
|
199
|
+
const envPath = process.env["TESTERS_DB_PATH"];
|
|
200
|
+
if (envPath)
|
|
201
|
+
return envPath;
|
|
202
|
+
const dir = join(homedir(), ".testers");
|
|
203
|
+
if (!existsSync(dir))
|
|
204
|
+
mkdirSync(dir, { recursive: true });
|
|
205
|
+
return join(dir, "testers.db");
|
|
206
|
+
}
|
|
207
|
+
function applyMigrations(database) {
|
|
208
|
+
const applied = database.query("SELECT id FROM _migrations ORDER BY id").all();
|
|
209
|
+
const appliedIds = new Set(applied.map((r) => r.id));
|
|
210
|
+
for (let i = 0;i < MIGRATIONS.length; i++) {
|
|
211
|
+
const migrationId = i + 1;
|
|
212
|
+
if (appliedIds.has(migrationId))
|
|
213
|
+
continue;
|
|
214
|
+
const migration = MIGRATIONS[i];
|
|
215
|
+
database.exec(migration);
|
|
216
|
+
database.query("INSERT INTO _migrations (id, applied_at) VALUES (?, ?)").run(migrationId, now());
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function getDatabase() {
|
|
220
|
+
if (db)
|
|
221
|
+
return db;
|
|
222
|
+
const dbPath = resolveDbPath();
|
|
223
|
+
const dir = dirname(dbPath);
|
|
224
|
+
if (dbPath !== ":memory:" && !existsSync(dir)) {
|
|
225
|
+
mkdirSync(dir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
db = new Database(dbPath);
|
|
228
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
229
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
230
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
231
|
+
db.exec(`
|
|
232
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
233
|
+
id INTEGER PRIMARY KEY,
|
|
234
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
235
|
+
);
|
|
236
|
+
`);
|
|
237
|
+
applyMigrations(db);
|
|
238
|
+
return db;
|
|
239
|
+
}
|
|
240
|
+
function resolvePartialId(table, partialId) {
|
|
241
|
+
const database = getDatabase();
|
|
242
|
+
const rows = database.query(`SELECT id FROM ${table} WHERE id LIKE ? || '%'`).all(partialId);
|
|
243
|
+
if (rows.length === 1)
|
|
244
|
+
return rows[0].id;
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
var db = null, MIGRATIONS;
|
|
248
|
+
var init_database = __esm(() => {
|
|
249
|
+
MIGRATIONS = [
|
|
250
|
+
`
|
|
251
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
252
|
+
id TEXT PRIMARY KEY,
|
|
253
|
+
name TEXT NOT NULL UNIQUE,
|
|
254
|
+
path TEXT UNIQUE,
|
|
255
|
+
description TEXT,
|
|
256
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
257
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
261
|
+
id TEXT PRIMARY KEY,
|
|
262
|
+
name TEXT NOT NULL UNIQUE,
|
|
263
|
+
description TEXT,
|
|
264
|
+
role TEXT,
|
|
265
|
+
metadata TEXT DEFAULT '{}',
|
|
266
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
267
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
CREATE TABLE IF NOT EXISTS scenarios (
|
|
271
|
+
id TEXT PRIMARY KEY,
|
|
272
|
+
short_id TEXT NOT NULL UNIQUE,
|
|
273
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
274
|
+
name TEXT NOT NULL,
|
|
275
|
+
description TEXT NOT NULL DEFAULT '',
|
|
276
|
+
steps TEXT NOT NULL DEFAULT '[]',
|
|
277
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
278
|
+
priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('low','medium','high','critical')),
|
|
279
|
+
model TEXT,
|
|
280
|
+
timeout_ms INTEGER,
|
|
281
|
+
target_path TEXT,
|
|
282
|
+
requires_auth INTEGER NOT NULL DEFAULT 0,
|
|
283
|
+
auth_config TEXT,
|
|
284
|
+
metadata TEXT DEFAULT '{}',
|
|
285
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
286
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
287
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
CREATE TABLE IF NOT EXISTS runs (
|
|
291
|
+
id TEXT PRIMARY KEY,
|
|
292
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
293
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','running','passed','failed','cancelled')),
|
|
294
|
+
url TEXT NOT NULL,
|
|
295
|
+
model TEXT NOT NULL,
|
|
296
|
+
headed INTEGER NOT NULL DEFAULT 0,
|
|
297
|
+
parallel INTEGER NOT NULL DEFAULT 1,
|
|
298
|
+
total INTEGER NOT NULL DEFAULT 0,
|
|
299
|
+
passed INTEGER NOT NULL DEFAULT 0,
|
|
300
|
+
failed INTEGER NOT NULL DEFAULT 0,
|
|
301
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
302
|
+
finished_at TEXT,
|
|
303
|
+
metadata TEXT DEFAULT '{}'
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
CREATE TABLE IF NOT EXISTS results (
|
|
307
|
+
id TEXT PRIMARY KEY,
|
|
308
|
+
run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
|
|
309
|
+
scenario_id TEXT NOT NULL REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
310
|
+
status TEXT NOT NULL DEFAULT 'skipped' CHECK(status IN ('passed','failed','error','skipped')),
|
|
311
|
+
reasoning TEXT,
|
|
312
|
+
error TEXT,
|
|
313
|
+
steps_completed INTEGER NOT NULL DEFAULT 0,
|
|
314
|
+
steps_total INTEGER NOT NULL DEFAULT 0,
|
|
315
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
316
|
+
model TEXT NOT NULL,
|
|
317
|
+
tokens_used INTEGER NOT NULL DEFAULT 0,
|
|
318
|
+
cost_cents REAL NOT NULL DEFAULT 0,
|
|
319
|
+
metadata TEXT DEFAULT '{}',
|
|
320
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
CREATE TABLE IF NOT EXISTS screenshots (
|
|
324
|
+
id TEXT PRIMARY KEY,
|
|
325
|
+
result_id TEXT NOT NULL REFERENCES results(id) ON DELETE CASCADE,
|
|
326
|
+
step_number INTEGER NOT NULL,
|
|
327
|
+
action TEXT NOT NULL,
|
|
328
|
+
file_path TEXT NOT NULL,
|
|
329
|
+
width INTEGER NOT NULL DEFAULT 0,
|
|
330
|
+
height INTEGER NOT NULL DEFAULT 0,
|
|
331
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
335
|
+
id INTEGER PRIMARY KEY,
|
|
336
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
337
|
+
);
|
|
338
|
+
`,
|
|
339
|
+
`
|
|
340
|
+
CREATE INDEX IF NOT EXISTS idx_scenarios_project ON scenarios(project_id);
|
|
341
|
+
CREATE INDEX IF NOT EXISTS idx_scenarios_priority ON scenarios(priority);
|
|
342
|
+
CREATE INDEX IF NOT EXISTS idx_scenarios_short_id ON scenarios(short_id);
|
|
343
|
+
CREATE INDEX IF NOT EXISTS idx_runs_project ON runs(project_id);
|
|
344
|
+
CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
|
|
345
|
+
CREATE INDEX IF NOT EXISTS idx_results_run ON results(run_id);
|
|
346
|
+
CREATE INDEX IF NOT EXISTS idx_results_scenario ON results(scenario_id);
|
|
347
|
+
CREATE INDEX IF NOT EXISTS idx_results_status ON results(status);
|
|
348
|
+
CREATE INDEX IF NOT EXISTS idx_screenshots_result ON screenshots(result_id);
|
|
349
|
+
`,
|
|
350
|
+
`
|
|
351
|
+
ALTER TABLE projects ADD COLUMN scenario_prefix TEXT DEFAULT 'TST';
|
|
352
|
+
ALTER TABLE projects ADD COLUMN scenario_counter INTEGER DEFAULT 0;
|
|
353
|
+
`,
|
|
354
|
+
`
|
|
355
|
+
CREATE TABLE IF NOT EXISTS schedules (
|
|
356
|
+
id TEXT PRIMARY KEY,
|
|
357
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
358
|
+
name TEXT NOT NULL,
|
|
359
|
+
cron_expression TEXT NOT NULL,
|
|
360
|
+
url TEXT NOT NULL,
|
|
361
|
+
scenario_filter TEXT NOT NULL DEFAULT '{}',
|
|
362
|
+
model TEXT,
|
|
363
|
+
headed INTEGER NOT NULL DEFAULT 0,
|
|
364
|
+
parallel INTEGER NOT NULL DEFAULT 1,
|
|
365
|
+
timeout_ms INTEGER,
|
|
366
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
367
|
+
last_run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,
|
|
368
|
+
last_run_at TEXT,
|
|
369
|
+
next_run_at TEXT,
|
|
370
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
371
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id);
|
|
375
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
|
|
376
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON schedules(next_run_at);
|
|
377
|
+
`,
|
|
378
|
+
`
|
|
379
|
+
ALTER TABLE screenshots ADD COLUMN description TEXT;
|
|
380
|
+
ALTER TABLE screenshots ADD COLUMN page_url TEXT;
|
|
381
|
+
ALTER TABLE screenshots ADD COLUMN thumbnail_path TEXT;
|
|
382
|
+
`,
|
|
383
|
+
`
|
|
384
|
+
CREATE TABLE IF NOT EXISTS auth_presets (
|
|
385
|
+
id TEXT PRIMARY KEY,
|
|
386
|
+
name TEXT NOT NULL UNIQUE,
|
|
387
|
+
email TEXT NOT NULL,
|
|
388
|
+
password TEXT NOT NULL,
|
|
389
|
+
login_path TEXT DEFAULT '/login',
|
|
390
|
+
metadata TEXT DEFAULT '{}',
|
|
391
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
392
|
+
);
|
|
393
|
+
`,
|
|
394
|
+
`
|
|
395
|
+
CREATE TABLE IF NOT EXISTS webhooks (
|
|
396
|
+
id TEXT PRIMARY KEY,
|
|
397
|
+
url TEXT NOT NULL,
|
|
398
|
+
events TEXT NOT NULL DEFAULT '["failed"]',
|
|
399
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
400
|
+
secret TEXT,
|
|
401
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
402
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
403
|
+
);
|
|
404
|
+
CREATE INDEX IF NOT EXISTS idx_webhooks_active ON webhooks(active);
|
|
405
|
+
`,
|
|
406
|
+
`
|
|
407
|
+
CREATE TABLE IF NOT EXISTS scenario_dependencies (
|
|
408
|
+
scenario_id TEXT NOT NULL REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
409
|
+
depends_on TEXT NOT NULL REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
410
|
+
PRIMARY KEY (scenario_id, depends_on),
|
|
411
|
+
CHECK (scenario_id != depends_on)
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
CREATE TABLE IF NOT EXISTS flows (
|
|
415
|
+
id TEXT PRIMARY KEY,
|
|
416
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
417
|
+
name TEXT NOT NULL,
|
|
418
|
+
description TEXT,
|
|
419
|
+
scenario_ids TEXT NOT NULL DEFAULT '[]',
|
|
420
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
421
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
CREATE INDEX IF NOT EXISTS idx_deps_scenario ON scenario_dependencies(scenario_id);
|
|
425
|
+
CREATE INDEX IF NOT EXISTS idx_deps_depends ON scenario_dependencies(depends_on);
|
|
426
|
+
CREATE INDEX IF NOT EXISTS idx_flows_project ON flows(project_id);
|
|
427
|
+
`
|
|
428
|
+
];
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// src/db/flows.ts
|
|
432
|
+
var exports_flows = {};
|
|
433
|
+
__export(exports_flows, {
|
|
434
|
+
topologicalSort: () => topologicalSort,
|
|
435
|
+
removeDependency: () => removeDependency,
|
|
436
|
+
listFlows: () => listFlows,
|
|
437
|
+
getTransitiveDependencies: () => getTransitiveDependencies,
|
|
438
|
+
getFlow: () => getFlow,
|
|
439
|
+
getDependents: () => getDependents,
|
|
440
|
+
getDependencies: () => getDependencies,
|
|
441
|
+
deleteFlow: () => deleteFlow,
|
|
442
|
+
createFlow: () => createFlow,
|
|
443
|
+
addDependency: () => addDependency
|
|
444
|
+
});
|
|
445
|
+
function addDependency(scenarioId, dependsOn) {
|
|
446
|
+
const db2 = getDatabase();
|
|
447
|
+
const visited = new Set;
|
|
448
|
+
const queue = [dependsOn];
|
|
449
|
+
while (queue.length > 0) {
|
|
450
|
+
const current = queue.shift();
|
|
451
|
+
if (current === scenarioId) {
|
|
452
|
+
throw new DependencyCycleError(scenarioId, dependsOn);
|
|
453
|
+
}
|
|
454
|
+
if (visited.has(current))
|
|
455
|
+
continue;
|
|
456
|
+
visited.add(current);
|
|
457
|
+
const deps = db2.query("SELECT depends_on FROM scenario_dependencies WHERE scenario_id = ?").all(current);
|
|
458
|
+
for (const dep of deps) {
|
|
459
|
+
if (!visited.has(dep.depends_on)) {
|
|
460
|
+
queue.push(dep.depends_on);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
db2.query("INSERT OR IGNORE INTO scenario_dependencies (scenario_id, depends_on) VALUES (?, ?)").run(scenarioId, dependsOn);
|
|
465
|
+
}
|
|
466
|
+
function removeDependency(scenarioId, dependsOn) {
|
|
467
|
+
const db2 = getDatabase();
|
|
468
|
+
const result = db2.query("DELETE FROM scenario_dependencies WHERE scenario_id = ? AND depends_on = ?").run(scenarioId, dependsOn);
|
|
469
|
+
return result.changes > 0;
|
|
470
|
+
}
|
|
471
|
+
function getDependencies(scenarioId) {
|
|
472
|
+
const db2 = getDatabase();
|
|
473
|
+
const rows = db2.query("SELECT depends_on FROM scenario_dependencies WHERE scenario_id = ?").all(scenarioId);
|
|
474
|
+
return rows.map((r) => r.depends_on);
|
|
475
|
+
}
|
|
476
|
+
function getDependents(scenarioId) {
|
|
477
|
+
const db2 = getDatabase();
|
|
478
|
+
const rows = db2.query("SELECT scenario_id FROM scenario_dependencies WHERE depends_on = ?").all(scenarioId);
|
|
479
|
+
return rows.map((r) => r.scenario_id);
|
|
480
|
+
}
|
|
481
|
+
function getTransitiveDependencies(scenarioId) {
|
|
482
|
+
const db2 = getDatabase();
|
|
483
|
+
const visited = new Set;
|
|
484
|
+
const queue = [scenarioId];
|
|
485
|
+
while (queue.length > 0) {
|
|
486
|
+
const current = queue.shift();
|
|
487
|
+
const deps = db2.query("SELECT depends_on FROM scenario_dependencies WHERE scenario_id = ?").all(current);
|
|
488
|
+
for (const dep of deps) {
|
|
489
|
+
if (!visited.has(dep.depends_on)) {
|
|
490
|
+
visited.add(dep.depends_on);
|
|
491
|
+
queue.push(dep.depends_on);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return Array.from(visited);
|
|
496
|
+
}
|
|
497
|
+
function topologicalSort(scenarioIds) {
|
|
498
|
+
const db2 = getDatabase();
|
|
499
|
+
const idSet = new Set(scenarioIds);
|
|
500
|
+
const inDegree = new Map;
|
|
501
|
+
const dependents = new Map;
|
|
502
|
+
for (const id of scenarioIds) {
|
|
503
|
+
inDegree.set(id, 0);
|
|
504
|
+
dependents.set(id, []);
|
|
505
|
+
}
|
|
506
|
+
for (const id of scenarioIds) {
|
|
507
|
+
const deps = db2.query("SELECT depends_on FROM scenario_dependencies WHERE scenario_id = ?").all(id);
|
|
508
|
+
for (const dep of deps) {
|
|
509
|
+
if (idSet.has(dep.depends_on)) {
|
|
510
|
+
inDegree.set(id, (inDegree.get(id) ?? 0) + 1);
|
|
511
|
+
dependents.get(dep.depends_on).push(id);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const queue = [];
|
|
516
|
+
for (const [id, deg] of inDegree) {
|
|
517
|
+
if (deg === 0)
|
|
518
|
+
queue.push(id);
|
|
519
|
+
}
|
|
520
|
+
const sorted = [];
|
|
521
|
+
while (queue.length > 0) {
|
|
522
|
+
const current = queue.shift();
|
|
523
|
+
sorted.push(current);
|
|
524
|
+
for (const dep of dependents.get(current) ?? []) {
|
|
525
|
+
const newDeg = (inDegree.get(dep) ?? 1) - 1;
|
|
526
|
+
inDegree.set(dep, newDeg);
|
|
527
|
+
if (newDeg === 0)
|
|
528
|
+
queue.push(dep);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (sorted.length !== scenarioIds.length) {
|
|
532
|
+
throw new DependencyCycleError("multiple", "multiple");
|
|
533
|
+
}
|
|
534
|
+
return sorted;
|
|
535
|
+
}
|
|
536
|
+
function createFlow(input) {
|
|
537
|
+
const db2 = getDatabase();
|
|
538
|
+
const id = uuid();
|
|
539
|
+
const timestamp = now();
|
|
540
|
+
db2.query(`
|
|
541
|
+
INSERT INTO flows (id, project_id, name, description, scenario_ids, created_at, updated_at)
|
|
542
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
543
|
+
`).run(id, input.projectId ?? null, input.name, input.description ?? null, JSON.stringify(input.scenarioIds), timestamp, timestamp);
|
|
544
|
+
return getFlow(id);
|
|
545
|
+
}
|
|
546
|
+
function getFlow(id) {
|
|
547
|
+
const db2 = getDatabase();
|
|
548
|
+
let row = db2.query("SELECT * FROM flows WHERE id = ?").get(id);
|
|
549
|
+
if (row)
|
|
550
|
+
return flowFromRow(row);
|
|
551
|
+
const fullId = resolvePartialId("flows", id);
|
|
552
|
+
if (fullId) {
|
|
553
|
+
row = db2.query("SELECT * FROM flows WHERE id = ?").get(fullId);
|
|
554
|
+
if (row)
|
|
555
|
+
return flowFromRow(row);
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
function listFlows(projectId) {
|
|
560
|
+
const db2 = getDatabase();
|
|
561
|
+
if (projectId) {
|
|
562
|
+
const rows2 = db2.query("SELECT * FROM flows WHERE project_id = ? ORDER BY created_at DESC").all(projectId);
|
|
563
|
+
return rows2.map(flowFromRow);
|
|
564
|
+
}
|
|
565
|
+
const rows = db2.query("SELECT * FROM flows ORDER BY created_at DESC").all();
|
|
566
|
+
return rows.map(flowFromRow);
|
|
567
|
+
}
|
|
568
|
+
function deleteFlow(id) {
|
|
569
|
+
const db2 = getDatabase();
|
|
570
|
+
const flow = getFlow(id);
|
|
571
|
+
if (!flow)
|
|
572
|
+
return false;
|
|
573
|
+
const result = db2.query("DELETE FROM flows WHERE id = ?").run(flow.id);
|
|
574
|
+
return result.changes > 0;
|
|
575
|
+
}
|
|
576
|
+
var init_flows = __esm(() => {
|
|
577
|
+
init_database();
|
|
578
|
+
init_database();
|
|
579
|
+
init_types();
|
|
580
|
+
});
|
|
13
581
|
|
|
14
582
|
// src/mcp/index.ts
|
|
15
583
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -3681,689 +4249,316 @@ class ZodDefault extends ZodType {
|
|
|
3681
4249
|
}
|
|
3682
4250
|
return this._def.innerType._parse({
|
|
3683
4251
|
data,
|
|
3684
|
-
path: ctx.path,
|
|
3685
|
-
parent: ctx
|
|
3686
|
-
});
|
|
3687
|
-
}
|
|
3688
|
-
removeDefault() {
|
|
3689
|
-
return this._def.innerType;
|
|
3690
|
-
}
|
|
3691
|
-
}
|
|
3692
|
-
ZodDefault.create = (type, params) => {
|
|
3693
|
-
return new ZodDefault({
|
|
3694
|
-
innerType: type,
|
|
3695
|
-
typeName: ZodFirstPartyTypeKind.ZodDefault,
|
|
3696
|
-
defaultValue: typeof params.default === "function" ? params.default : () => params.default,
|
|
3697
|
-
...processCreateParams(params)
|
|
3698
|
-
});
|
|
3699
|
-
};
|
|
3700
|
-
|
|
3701
|
-
class ZodCatch extends ZodType {
|
|
3702
|
-
_parse(input) {
|
|
3703
|
-
const { ctx } = this._processInputParams(input);
|
|
3704
|
-
const newCtx = {
|
|
3705
|
-
...ctx,
|
|
3706
|
-
common: {
|
|
3707
|
-
...ctx.common,
|
|
3708
|
-
issues: []
|
|
3709
|
-
}
|
|
3710
|
-
};
|
|
3711
|
-
const result = this._def.innerType._parse({
|
|
3712
|
-
data: newCtx.data,
|
|
3713
|
-
path: newCtx.path,
|
|
3714
|
-
parent: {
|
|
3715
|
-
...newCtx
|
|
3716
|
-
}
|
|
3717
|
-
});
|
|
3718
|
-
if (isAsync(result)) {
|
|
3719
|
-
return result.then((result2) => {
|
|
3720
|
-
return {
|
|
3721
|
-
status: "valid",
|
|
3722
|
-
value: result2.status === "valid" ? result2.value : this._def.catchValue({
|
|
3723
|
-
get error() {
|
|
3724
|
-
return new ZodError(newCtx.common.issues);
|
|
3725
|
-
},
|
|
3726
|
-
input: newCtx.data
|
|
3727
|
-
})
|
|
3728
|
-
};
|
|
3729
|
-
});
|
|
3730
|
-
} else {
|
|
3731
|
-
return {
|
|
3732
|
-
status: "valid",
|
|
3733
|
-
value: result.status === "valid" ? result.value : this._def.catchValue({
|
|
3734
|
-
get error() {
|
|
3735
|
-
return new ZodError(newCtx.common.issues);
|
|
3736
|
-
},
|
|
3737
|
-
input: newCtx.data
|
|
3738
|
-
})
|
|
3739
|
-
};
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
removeCatch() {
|
|
3743
|
-
return this._def.innerType;
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
ZodCatch.create = (type, params) => {
|
|
3747
|
-
return new ZodCatch({
|
|
3748
|
-
innerType: type,
|
|
3749
|
-
typeName: ZodFirstPartyTypeKind.ZodCatch,
|
|
3750
|
-
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
|
|
3751
|
-
...processCreateParams(params)
|
|
3752
|
-
});
|
|
3753
|
-
};
|
|
3754
|
-
|
|
3755
|
-
class ZodNaN extends ZodType {
|
|
3756
|
-
_parse(input) {
|
|
3757
|
-
const parsedType = this._getType(input);
|
|
3758
|
-
if (parsedType !== ZodParsedType.nan) {
|
|
3759
|
-
const ctx = this._getOrReturnCtx(input);
|
|
3760
|
-
addIssueToContext(ctx, {
|
|
3761
|
-
code: ZodIssueCode.invalid_type,
|
|
3762
|
-
expected: ZodParsedType.nan,
|
|
3763
|
-
received: ctx.parsedType
|
|
3764
|
-
});
|
|
3765
|
-
return INVALID;
|
|
3766
|
-
}
|
|
3767
|
-
return { status: "valid", value: input.data };
|
|
3768
|
-
}
|
|
3769
|
-
}
|
|
3770
|
-
ZodNaN.create = (params) => {
|
|
3771
|
-
return new ZodNaN({
|
|
3772
|
-
typeName: ZodFirstPartyTypeKind.ZodNaN,
|
|
3773
|
-
...processCreateParams(params)
|
|
3774
|
-
});
|
|
3775
|
-
};
|
|
3776
|
-
var BRAND = Symbol("zod_brand");
|
|
3777
|
-
|
|
3778
|
-
class ZodBranded extends ZodType {
|
|
3779
|
-
_parse(input) {
|
|
3780
|
-
const { ctx } = this._processInputParams(input);
|
|
3781
|
-
const data = ctx.data;
|
|
3782
|
-
return this._def.type._parse({
|
|
3783
|
-
data,
|
|
3784
|
-
path: ctx.path,
|
|
3785
|
-
parent: ctx
|
|
3786
|
-
});
|
|
3787
|
-
}
|
|
3788
|
-
unwrap() {
|
|
3789
|
-
return this._def.type;
|
|
3790
|
-
}
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
class ZodPipeline extends ZodType {
|
|
3794
|
-
_parse(input) {
|
|
3795
|
-
const { status, ctx } = this._processInputParams(input);
|
|
3796
|
-
if (ctx.common.async) {
|
|
3797
|
-
const handleAsync = async () => {
|
|
3798
|
-
const inResult = await this._def.in._parseAsync({
|
|
3799
|
-
data: ctx.data,
|
|
3800
|
-
path: ctx.path,
|
|
3801
|
-
parent: ctx
|
|
3802
|
-
});
|
|
3803
|
-
if (inResult.status === "aborted")
|
|
3804
|
-
return INVALID;
|
|
3805
|
-
if (inResult.status === "dirty") {
|
|
3806
|
-
status.dirty();
|
|
3807
|
-
return DIRTY(inResult.value);
|
|
3808
|
-
} else {
|
|
3809
|
-
return this._def.out._parseAsync({
|
|
3810
|
-
data: inResult.value,
|
|
3811
|
-
path: ctx.path,
|
|
3812
|
-
parent: ctx
|
|
3813
|
-
});
|
|
3814
|
-
}
|
|
3815
|
-
};
|
|
3816
|
-
return handleAsync();
|
|
3817
|
-
} else {
|
|
3818
|
-
const inResult = this._def.in._parseSync({
|
|
3819
|
-
data: ctx.data,
|
|
3820
|
-
path: ctx.path,
|
|
3821
|
-
parent: ctx
|
|
3822
|
-
});
|
|
3823
|
-
if (inResult.status === "aborted")
|
|
3824
|
-
return INVALID;
|
|
3825
|
-
if (inResult.status === "dirty") {
|
|
3826
|
-
status.dirty();
|
|
3827
|
-
return {
|
|
3828
|
-
status: "dirty",
|
|
3829
|
-
value: inResult.value
|
|
3830
|
-
};
|
|
3831
|
-
} else {
|
|
3832
|
-
return this._def.out._parseSync({
|
|
3833
|
-
data: inResult.value,
|
|
3834
|
-
path: ctx.path,
|
|
3835
|
-
parent: ctx
|
|
3836
|
-
});
|
|
3837
|
-
}
|
|
3838
|
-
}
|
|
3839
|
-
}
|
|
3840
|
-
static create(a, b) {
|
|
3841
|
-
return new ZodPipeline({
|
|
3842
|
-
in: a,
|
|
3843
|
-
out: b,
|
|
3844
|
-
typeName: ZodFirstPartyTypeKind.ZodPipeline
|
|
3845
|
-
});
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
|
|
3849
|
-
class ZodReadonly extends ZodType {
|
|
3850
|
-
_parse(input) {
|
|
3851
|
-
const result = this._def.innerType._parse(input);
|
|
3852
|
-
const freeze = (data) => {
|
|
3853
|
-
if (isValid(data)) {
|
|
3854
|
-
data.value = Object.freeze(data.value);
|
|
3855
|
-
}
|
|
3856
|
-
return data;
|
|
3857
|
-
};
|
|
3858
|
-
return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
|
|
4252
|
+
path: ctx.path,
|
|
4253
|
+
parent: ctx
|
|
4254
|
+
});
|
|
3859
4255
|
}
|
|
3860
|
-
|
|
4256
|
+
removeDefault() {
|
|
3861
4257
|
return this._def.innerType;
|
|
3862
4258
|
}
|
|
3863
4259
|
}
|
|
3864
|
-
|
|
3865
|
-
return new
|
|
4260
|
+
ZodDefault.create = (type, params) => {
|
|
4261
|
+
return new ZodDefault({
|
|
3866
4262
|
innerType: type,
|
|
3867
|
-
typeName: ZodFirstPartyTypeKind.
|
|
4263
|
+
typeName: ZodFirstPartyTypeKind.ZodDefault,
|
|
4264
|
+
defaultValue: typeof params.default === "function" ? params.default : () => params.default,
|
|
3868
4265
|
...processCreateParams(params)
|
|
3869
4266
|
});
|
|
3870
4267
|
};
|
|
3871
|
-
function cleanParams(params, data) {
|
|
3872
|
-
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
3873
|
-
const p2 = typeof p === "string" ? { message: p } : p;
|
|
3874
|
-
return p2;
|
|
3875
|
-
}
|
|
3876
|
-
function custom(check, _params = {}, fatal) {
|
|
3877
|
-
if (check)
|
|
3878
|
-
return ZodAny.create().superRefine((data, ctx) => {
|
|
3879
|
-
const r = check(data);
|
|
3880
|
-
if (r instanceof Promise) {
|
|
3881
|
-
return r.then((r2) => {
|
|
3882
|
-
if (!r2) {
|
|
3883
|
-
const params = cleanParams(_params, data);
|
|
3884
|
-
const _fatal = params.fatal ?? fatal ?? true;
|
|
3885
|
-
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3886
|
-
}
|
|
3887
|
-
});
|
|
3888
|
-
}
|
|
3889
|
-
if (!r) {
|
|
3890
|
-
const params = cleanParams(_params, data);
|
|
3891
|
-
const _fatal = params.fatal ?? fatal ?? true;
|
|
3892
|
-
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
3893
|
-
}
|
|
3894
|
-
return;
|
|
3895
|
-
});
|
|
3896
|
-
return ZodAny.create();
|
|
3897
|
-
}
|
|
3898
|
-
var late = {
|
|
3899
|
-
object: ZodObject.lazycreate
|
|
3900
|
-
};
|
|
3901
|
-
var ZodFirstPartyTypeKind;
|
|
3902
|
-
(function(ZodFirstPartyTypeKind2) {
|
|
3903
|
-
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
3904
|
-
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
|
|
3905
|
-
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
|
|
3906
|
-
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
|
|
3907
|
-
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
|
|
3908
|
-
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
|
|
3909
|
-
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
|
|
3910
|
-
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
|
|
3911
|
-
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
|
|
3912
|
-
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
|
|
3913
|
-
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
|
|
3914
|
-
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
|
|
3915
|
-
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
|
|
3916
|
-
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
|
|
3917
|
-
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
|
|
3918
|
-
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
|
|
3919
|
-
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
|
|
3920
|
-
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
|
|
3921
|
-
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
|
|
3922
|
-
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
|
|
3923
|
-
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
|
|
3924
|
-
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
|
|
3925
|
-
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
|
|
3926
|
-
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
|
|
3927
|
-
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
|
|
3928
|
-
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
|
|
3929
|
-
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
|
|
3930
|
-
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
|
|
3931
|
-
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
|
|
3932
|
-
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
|
|
3933
|
-
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
|
|
3934
|
-
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
|
|
3935
|
-
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
|
|
3936
|
-
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
|
|
3937
|
-
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
3938
|
-
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
3939
|
-
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
3940
|
-
var instanceOfType = (cls, params = {
|
|
3941
|
-
message: `Input not instance of ${cls.name}`
|
|
3942
|
-
}) => custom((data) => data instanceof cls, params);
|
|
3943
|
-
var stringType = ZodString.create;
|
|
3944
|
-
var numberType = ZodNumber.create;
|
|
3945
|
-
var nanType = ZodNaN.create;
|
|
3946
|
-
var bigIntType = ZodBigInt.create;
|
|
3947
|
-
var booleanType = ZodBoolean.create;
|
|
3948
|
-
var dateType = ZodDate.create;
|
|
3949
|
-
var symbolType = ZodSymbol.create;
|
|
3950
|
-
var undefinedType = ZodUndefined.create;
|
|
3951
|
-
var nullType = ZodNull.create;
|
|
3952
|
-
var anyType = ZodAny.create;
|
|
3953
|
-
var unknownType = ZodUnknown.create;
|
|
3954
|
-
var neverType = ZodNever.create;
|
|
3955
|
-
var voidType = ZodVoid.create;
|
|
3956
|
-
var arrayType = ZodArray.create;
|
|
3957
|
-
var objectType = ZodObject.create;
|
|
3958
|
-
var strictObjectType = ZodObject.strictCreate;
|
|
3959
|
-
var unionType = ZodUnion.create;
|
|
3960
|
-
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
3961
|
-
var intersectionType = ZodIntersection.create;
|
|
3962
|
-
var tupleType = ZodTuple.create;
|
|
3963
|
-
var recordType = ZodRecord.create;
|
|
3964
|
-
var mapType = ZodMap.create;
|
|
3965
|
-
var setType = ZodSet.create;
|
|
3966
|
-
var functionType = ZodFunction.create;
|
|
3967
|
-
var lazyType = ZodLazy.create;
|
|
3968
|
-
var literalType = ZodLiteral.create;
|
|
3969
|
-
var enumType = ZodEnum.create;
|
|
3970
|
-
var nativeEnumType = ZodNativeEnum.create;
|
|
3971
|
-
var promiseType = ZodPromise.create;
|
|
3972
|
-
var effectsType = ZodEffects.create;
|
|
3973
|
-
var optionalType = ZodOptional.create;
|
|
3974
|
-
var nullableType = ZodNullable.create;
|
|
3975
|
-
var preprocessType = ZodEffects.createWithPreprocess;
|
|
3976
|
-
var pipelineType = ZodPipeline.create;
|
|
3977
|
-
var ostring = () => stringType().optional();
|
|
3978
|
-
var onumber = () => numberType().optional();
|
|
3979
|
-
var oboolean = () => booleanType().optional();
|
|
3980
|
-
var coerce = {
|
|
3981
|
-
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
3982
|
-
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
3983
|
-
boolean: (arg) => ZodBoolean.create({
|
|
3984
|
-
...arg,
|
|
3985
|
-
coerce: true
|
|
3986
|
-
}),
|
|
3987
|
-
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
3988
|
-
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
3989
|
-
};
|
|
3990
|
-
var NEVER = INVALID;
|
|
3991
|
-
// src/types/index.ts
|
|
3992
|
-
var MODEL_MAP = {
|
|
3993
|
-
quick: "claude-haiku-4-5-20251001",
|
|
3994
|
-
thorough: "claude-sonnet-4-6-20260311",
|
|
3995
|
-
deep: "claude-opus-4-6-20260311"
|
|
3996
|
-
};
|
|
3997
|
-
function projectFromRow(row) {
|
|
3998
|
-
return {
|
|
3999
|
-
id: row.id,
|
|
4000
|
-
name: row.name,
|
|
4001
|
-
path: row.path,
|
|
4002
|
-
description: row.description,
|
|
4003
|
-
createdAt: row.created_at,
|
|
4004
|
-
updatedAt: row.updated_at
|
|
4005
|
-
};
|
|
4006
|
-
}
|
|
4007
|
-
function agentFromRow(row) {
|
|
4008
|
-
return {
|
|
4009
|
-
id: row.id,
|
|
4010
|
-
name: row.name,
|
|
4011
|
-
description: row.description,
|
|
4012
|
-
role: row.role,
|
|
4013
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
4014
|
-
createdAt: row.created_at,
|
|
4015
|
-
lastSeenAt: row.last_seen_at
|
|
4016
|
-
};
|
|
4017
|
-
}
|
|
4018
|
-
function scenarioFromRow(row) {
|
|
4019
|
-
return {
|
|
4020
|
-
id: row.id,
|
|
4021
|
-
shortId: row.short_id,
|
|
4022
|
-
projectId: row.project_id,
|
|
4023
|
-
name: row.name,
|
|
4024
|
-
description: row.description,
|
|
4025
|
-
steps: JSON.parse(row.steps),
|
|
4026
|
-
tags: JSON.parse(row.tags),
|
|
4027
|
-
priority: row.priority,
|
|
4028
|
-
model: row.model,
|
|
4029
|
-
timeoutMs: row.timeout_ms,
|
|
4030
|
-
targetPath: row.target_path,
|
|
4031
|
-
requiresAuth: row.requires_auth === 1,
|
|
4032
|
-
authConfig: row.auth_config ? JSON.parse(row.auth_config) : null,
|
|
4033
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
4034
|
-
version: row.version,
|
|
4035
|
-
createdAt: row.created_at,
|
|
4036
|
-
updatedAt: row.updated_at
|
|
4037
|
-
};
|
|
4038
|
-
}
|
|
4039
|
-
function runFromRow(row) {
|
|
4040
|
-
return {
|
|
4041
|
-
id: row.id,
|
|
4042
|
-
projectId: row.project_id,
|
|
4043
|
-
status: row.status,
|
|
4044
|
-
url: row.url,
|
|
4045
|
-
model: row.model,
|
|
4046
|
-
headed: row.headed === 1,
|
|
4047
|
-
parallel: row.parallel,
|
|
4048
|
-
total: row.total,
|
|
4049
|
-
passed: row.passed,
|
|
4050
|
-
failed: row.failed,
|
|
4051
|
-
startedAt: row.started_at,
|
|
4052
|
-
finishedAt: row.finished_at,
|
|
4053
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
4054
|
-
};
|
|
4055
|
-
}
|
|
4056
|
-
function resultFromRow(row) {
|
|
4057
|
-
return {
|
|
4058
|
-
id: row.id,
|
|
4059
|
-
runId: row.run_id,
|
|
4060
|
-
scenarioId: row.scenario_id,
|
|
4061
|
-
status: row.status,
|
|
4062
|
-
reasoning: row.reasoning,
|
|
4063
|
-
error: row.error,
|
|
4064
|
-
stepsCompleted: row.steps_completed,
|
|
4065
|
-
stepsTotal: row.steps_total,
|
|
4066
|
-
durationMs: row.duration_ms,
|
|
4067
|
-
model: row.model,
|
|
4068
|
-
tokensUsed: row.tokens_used,
|
|
4069
|
-
costCents: row.cost_cents,
|
|
4070
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
4071
|
-
createdAt: row.created_at
|
|
4072
|
-
};
|
|
4073
|
-
}
|
|
4074
|
-
function screenshotFromRow(row) {
|
|
4075
|
-
return {
|
|
4076
|
-
id: row.id,
|
|
4077
|
-
resultId: row.result_id,
|
|
4078
|
-
stepNumber: row.step_number,
|
|
4079
|
-
action: row.action,
|
|
4080
|
-
filePath: row.file_path,
|
|
4081
|
-
width: row.width,
|
|
4082
|
-
height: row.height,
|
|
4083
|
-
timestamp: row.timestamp,
|
|
4084
|
-
description: row.description,
|
|
4085
|
-
pageUrl: row.page_url,
|
|
4086
|
-
thumbnailPath: row.thumbnail_path
|
|
4087
|
-
};
|
|
4088
|
-
}
|
|
4089
|
-
function scheduleFromRow(row) {
|
|
4090
|
-
return {
|
|
4091
|
-
id: row.id,
|
|
4092
|
-
projectId: row.project_id,
|
|
4093
|
-
name: row.name,
|
|
4094
|
-
cronExpression: row.cron_expression,
|
|
4095
|
-
url: row.url,
|
|
4096
|
-
scenarioFilter: JSON.parse(row.scenario_filter),
|
|
4097
|
-
model: row.model,
|
|
4098
|
-
headed: row.headed === 1,
|
|
4099
|
-
parallel: row.parallel,
|
|
4100
|
-
timeoutMs: row.timeout_ms,
|
|
4101
|
-
enabled: row.enabled === 1,
|
|
4102
|
-
lastRunId: row.last_run_id,
|
|
4103
|
-
lastRunAt: row.last_run_at,
|
|
4104
|
-
nextRunAt: row.next_run_at,
|
|
4105
|
-
createdAt: row.created_at,
|
|
4106
|
-
updatedAt: row.updated_at
|
|
4107
|
-
};
|
|
4108
|
-
}
|
|
4109
|
-
class VersionConflictError extends Error {
|
|
4110
|
-
constructor(entity, id) {
|
|
4111
|
-
super(`Version conflict on ${entity}: ${id}`);
|
|
4112
|
-
this.name = "VersionConflictError";
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
4268
|
|
|
4116
|
-
class
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4269
|
+
class ZodCatch extends ZodType {
|
|
4270
|
+
_parse(input) {
|
|
4271
|
+
const { ctx } = this._processInputParams(input);
|
|
4272
|
+
const newCtx = {
|
|
4273
|
+
...ctx,
|
|
4274
|
+
common: {
|
|
4275
|
+
...ctx.common,
|
|
4276
|
+
issues: []
|
|
4277
|
+
}
|
|
4278
|
+
};
|
|
4279
|
+
const result = this._def.innerType._parse({
|
|
4280
|
+
data: newCtx.data,
|
|
4281
|
+
path: newCtx.path,
|
|
4282
|
+
parent: {
|
|
4283
|
+
...newCtx
|
|
4284
|
+
}
|
|
4285
|
+
});
|
|
4286
|
+
if (isAsync(result)) {
|
|
4287
|
+
return result.then((result2) => {
|
|
4288
|
+
return {
|
|
4289
|
+
status: "valid",
|
|
4290
|
+
value: result2.status === "valid" ? result2.value : this._def.catchValue({
|
|
4291
|
+
get error() {
|
|
4292
|
+
return new ZodError(newCtx.common.issues);
|
|
4293
|
+
},
|
|
4294
|
+
input: newCtx.data
|
|
4295
|
+
})
|
|
4296
|
+
};
|
|
4297
|
+
});
|
|
4298
|
+
} else {
|
|
4299
|
+
return {
|
|
4300
|
+
status: "valid",
|
|
4301
|
+
value: result.status === "valid" ? result.value : this._def.catchValue({
|
|
4302
|
+
get error() {
|
|
4303
|
+
return new ZodError(newCtx.common.issues);
|
|
4304
|
+
},
|
|
4305
|
+
input: newCtx.data
|
|
4306
|
+
})
|
|
4307
|
+
};
|
|
4308
|
+
}
|
|
4120
4309
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
class AIClientError extends Error {
|
|
4124
|
-
constructor(message) {
|
|
4125
|
-
super(message);
|
|
4126
|
-
this.name = "AIClientError";
|
|
4310
|
+
removeCatch() {
|
|
4311
|
+
return this._def.innerType;
|
|
4127
4312
|
}
|
|
4128
4313
|
}
|
|
4314
|
+
ZodCatch.create = (type, params) => {
|
|
4315
|
+
return new ZodCatch({
|
|
4316
|
+
innerType: type,
|
|
4317
|
+
typeName: ZodFirstPartyTypeKind.ZodCatch,
|
|
4318
|
+
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
|
|
4319
|
+
...processCreateParams(params)
|
|
4320
|
+
});
|
|
4321
|
+
};
|
|
4129
4322
|
|
|
4130
|
-
class
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4323
|
+
class ZodNaN extends ZodType {
|
|
4324
|
+
_parse(input) {
|
|
4325
|
+
const parsedType = this._getType(input);
|
|
4326
|
+
if (parsedType !== ZodParsedType.nan) {
|
|
4327
|
+
const ctx = this._getOrReturnCtx(input);
|
|
4328
|
+
addIssueToContext(ctx, {
|
|
4329
|
+
code: ZodIssueCode.invalid_type,
|
|
4330
|
+
expected: ZodParsedType.nan,
|
|
4331
|
+
received: ctx.parsedType
|
|
4332
|
+
});
|
|
4333
|
+
return INVALID;
|
|
4334
|
+
}
|
|
4335
|
+
return { status: "valid", value: input.data };
|
|
4140
4336
|
}
|
|
4141
4337
|
}
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
var
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
const envPath = process.env["TESTERS_DB_PATH"];
|
|
4160
|
-
if (envPath)
|
|
4161
|
-
return envPath;
|
|
4162
|
-
const dir = join(homedir(), ".testers");
|
|
4163
|
-
if (!existsSync(dir))
|
|
4164
|
-
mkdirSync(dir, { recursive: true });
|
|
4165
|
-
return join(dir, "testers.db");
|
|
4166
|
-
}
|
|
4167
|
-
var MIGRATIONS = [
|
|
4168
|
-
`
|
|
4169
|
-
CREATE TABLE IF NOT EXISTS projects (
|
|
4170
|
-
id TEXT PRIMARY KEY,
|
|
4171
|
-
name TEXT NOT NULL UNIQUE,
|
|
4172
|
-
path TEXT UNIQUE,
|
|
4173
|
-
description TEXT,
|
|
4174
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4175
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4176
|
-
);
|
|
4177
|
-
|
|
4178
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
4179
|
-
id TEXT PRIMARY KEY,
|
|
4180
|
-
name TEXT NOT NULL UNIQUE,
|
|
4181
|
-
description TEXT,
|
|
4182
|
-
role TEXT,
|
|
4183
|
-
metadata TEXT DEFAULT '{}',
|
|
4184
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4185
|
-
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4186
|
-
);
|
|
4187
|
-
|
|
4188
|
-
CREATE TABLE IF NOT EXISTS scenarios (
|
|
4189
|
-
id TEXT PRIMARY KEY,
|
|
4190
|
-
short_id TEXT NOT NULL UNIQUE,
|
|
4191
|
-
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
4192
|
-
name TEXT NOT NULL,
|
|
4193
|
-
description TEXT NOT NULL DEFAULT '',
|
|
4194
|
-
steps TEXT NOT NULL DEFAULT '[]',
|
|
4195
|
-
tags TEXT NOT NULL DEFAULT '[]',
|
|
4196
|
-
priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('low','medium','high','critical')),
|
|
4197
|
-
model TEXT,
|
|
4198
|
-
timeout_ms INTEGER,
|
|
4199
|
-
target_path TEXT,
|
|
4200
|
-
requires_auth INTEGER NOT NULL DEFAULT 0,
|
|
4201
|
-
auth_config TEXT,
|
|
4202
|
-
metadata TEXT DEFAULT '{}',
|
|
4203
|
-
version INTEGER NOT NULL DEFAULT 1,
|
|
4204
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4205
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4206
|
-
);
|
|
4207
|
-
|
|
4208
|
-
CREATE TABLE IF NOT EXISTS runs (
|
|
4209
|
-
id TEXT PRIMARY KEY,
|
|
4210
|
-
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
4211
|
-
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','running','passed','failed','cancelled')),
|
|
4212
|
-
url TEXT NOT NULL,
|
|
4213
|
-
model TEXT NOT NULL,
|
|
4214
|
-
headed INTEGER NOT NULL DEFAULT 0,
|
|
4215
|
-
parallel INTEGER NOT NULL DEFAULT 1,
|
|
4216
|
-
total INTEGER NOT NULL DEFAULT 0,
|
|
4217
|
-
passed INTEGER NOT NULL DEFAULT 0,
|
|
4218
|
-
failed INTEGER NOT NULL DEFAULT 0,
|
|
4219
|
-
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4220
|
-
finished_at TEXT,
|
|
4221
|
-
metadata TEXT DEFAULT '{}'
|
|
4222
|
-
);
|
|
4223
|
-
|
|
4224
|
-
CREATE TABLE IF NOT EXISTS results (
|
|
4225
|
-
id TEXT PRIMARY KEY,
|
|
4226
|
-
run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
|
|
4227
|
-
scenario_id TEXT NOT NULL REFERENCES scenarios(id) ON DELETE CASCADE,
|
|
4228
|
-
status TEXT NOT NULL DEFAULT 'skipped' CHECK(status IN ('passed','failed','error','skipped')),
|
|
4229
|
-
reasoning TEXT,
|
|
4230
|
-
error TEXT,
|
|
4231
|
-
steps_completed INTEGER NOT NULL DEFAULT 0,
|
|
4232
|
-
steps_total INTEGER NOT NULL DEFAULT 0,
|
|
4233
|
-
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
4234
|
-
model TEXT NOT NULL,
|
|
4235
|
-
tokens_used INTEGER NOT NULL DEFAULT 0,
|
|
4236
|
-
cost_cents REAL NOT NULL DEFAULT 0,
|
|
4237
|
-
metadata TEXT DEFAULT '{}',
|
|
4238
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4239
|
-
);
|
|
4240
|
-
|
|
4241
|
-
CREATE TABLE IF NOT EXISTS screenshots (
|
|
4242
|
-
id TEXT PRIMARY KEY,
|
|
4243
|
-
result_id TEXT NOT NULL REFERENCES results(id) ON DELETE CASCADE,
|
|
4244
|
-
step_number INTEGER NOT NULL,
|
|
4245
|
-
action TEXT NOT NULL,
|
|
4246
|
-
file_path TEXT NOT NULL,
|
|
4247
|
-
width INTEGER NOT NULL DEFAULT 0,
|
|
4248
|
-
height INTEGER NOT NULL DEFAULT 0,
|
|
4249
|
-
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4250
|
-
);
|
|
4251
|
-
|
|
4252
|
-
CREATE TABLE IF NOT EXISTS _migrations (
|
|
4253
|
-
id INTEGER PRIMARY KEY,
|
|
4254
|
-
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4255
|
-
);
|
|
4256
|
-
`,
|
|
4257
|
-
`
|
|
4258
|
-
CREATE INDEX IF NOT EXISTS idx_scenarios_project ON scenarios(project_id);
|
|
4259
|
-
CREATE INDEX IF NOT EXISTS idx_scenarios_priority ON scenarios(priority);
|
|
4260
|
-
CREATE INDEX IF NOT EXISTS idx_scenarios_short_id ON scenarios(short_id);
|
|
4261
|
-
CREATE INDEX IF NOT EXISTS idx_runs_project ON runs(project_id);
|
|
4262
|
-
CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
|
|
4263
|
-
CREATE INDEX IF NOT EXISTS idx_results_run ON results(run_id);
|
|
4264
|
-
CREATE INDEX IF NOT EXISTS idx_results_scenario ON results(scenario_id);
|
|
4265
|
-
CREATE INDEX IF NOT EXISTS idx_results_status ON results(status);
|
|
4266
|
-
CREATE INDEX IF NOT EXISTS idx_screenshots_result ON screenshots(result_id);
|
|
4267
|
-
`,
|
|
4268
|
-
`
|
|
4269
|
-
ALTER TABLE projects ADD COLUMN scenario_prefix TEXT DEFAULT 'TST';
|
|
4270
|
-
ALTER TABLE projects ADD COLUMN scenario_counter INTEGER DEFAULT 0;
|
|
4271
|
-
`,
|
|
4272
|
-
`
|
|
4273
|
-
CREATE TABLE IF NOT EXISTS schedules (
|
|
4274
|
-
id TEXT PRIMARY KEY,
|
|
4275
|
-
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
4276
|
-
name TEXT NOT NULL,
|
|
4277
|
-
cron_expression TEXT NOT NULL,
|
|
4278
|
-
url TEXT NOT NULL,
|
|
4279
|
-
scenario_filter TEXT NOT NULL DEFAULT '{}',
|
|
4280
|
-
model TEXT,
|
|
4281
|
-
headed INTEGER NOT NULL DEFAULT 0,
|
|
4282
|
-
parallel INTEGER NOT NULL DEFAULT 1,
|
|
4283
|
-
timeout_ms INTEGER,
|
|
4284
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
4285
|
-
last_run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,
|
|
4286
|
-
last_run_at TEXT,
|
|
4287
|
-
next_run_at TEXT,
|
|
4288
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
4289
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4290
|
-
);
|
|
4291
|
-
|
|
4292
|
-
CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id);
|
|
4293
|
-
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
|
|
4294
|
-
CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON schedules(next_run_at);
|
|
4295
|
-
`,
|
|
4296
|
-
`
|
|
4297
|
-
ALTER TABLE screenshots ADD COLUMN description TEXT;
|
|
4298
|
-
ALTER TABLE screenshots ADD COLUMN page_url TEXT;
|
|
4299
|
-
ALTER TABLE screenshots ADD COLUMN thumbnail_path TEXT;
|
|
4300
|
-
`,
|
|
4301
|
-
`
|
|
4302
|
-
CREATE TABLE IF NOT EXISTS auth_presets (
|
|
4303
|
-
id TEXT PRIMARY KEY,
|
|
4304
|
-
name TEXT NOT NULL UNIQUE,
|
|
4305
|
-
email TEXT NOT NULL,
|
|
4306
|
-
password TEXT NOT NULL,
|
|
4307
|
-
login_path TEXT DEFAULT '/login',
|
|
4308
|
-
metadata TEXT DEFAULT '{}',
|
|
4309
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4310
|
-
);
|
|
4311
|
-
`,
|
|
4312
|
-
`
|
|
4313
|
-
CREATE TABLE IF NOT EXISTS webhooks (
|
|
4314
|
-
id TEXT PRIMARY KEY,
|
|
4315
|
-
url TEXT NOT NULL,
|
|
4316
|
-
events TEXT NOT NULL DEFAULT '["failed"]',
|
|
4317
|
-
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
4318
|
-
secret TEXT,
|
|
4319
|
-
active INTEGER NOT NULL DEFAULT 1,
|
|
4320
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4321
|
-
);
|
|
4322
|
-
CREATE INDEX IF NOT EXISTS idx_webhooks_active ON webhooks(active);
|
|
4323
|
-
`
|
|
4324
|
-
];
|
|
4325
|
-
function applyMigrations(database) {
|
|
4326
|
-
const applied = database.query("SELECT id FROM _migrations ORDER BY id").all();
|
|
4327
|
-
const appliedIds = new Set(applied.map((r) => r.id));
|
|
4328
|
-
for (let i = 0;i < MIGRATIONS.length; i++) {
|
|
4329
|
-
const migrationId = i + 1;
|
|
4330
|
-
if (appliedIds.has(migrationId))
|
|
4331
|
-
continue;
|
|
4332
|
-
const migration = MIGRATIONS[i];
|
|
4333
|
-
database.exec(migration);
|
|
4334
|
-
database.query("INSERT INTO _migrations (id, applied_at) VALUES (?, ?)").run(migrationId, now());
|
|
4338
|
+
ZodNaN.create = (params) => {
|
|
4339
|
+
return new ZodNaN({
|
|
4340
|
+
typeName: ZodFirstPartyTypeKind.ZodNaN,
|
|
4341
|
+
...processCreateParams(params)
|
|
4342
|
+
});
|
|
4343
|
+
};
|
|
4344
|
+
var BRAND = Symbol("zod_brand");
|
|
4345
|
+
|
|
4346
|
+
class ZodBranded extends ZodType {
|
|
4347
|
+
_parse(input) {
|
|
4348
|
+
const { ctx } = this._processInputParams(input);
|
|
4349
|
+
const data = ctx.data;
|
|
4350
|
+
return this._def.type._parse({
|
|
4351
|
+
data,
|
|
4352
|
+
path: ctx.path,
|
|
4353
|
+
parent: ctx
|
|
4354
|
+
});
|
|
4335
4355
|
}
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
if (db)
|
|
4339
|
-
return db;
|
|
4340
|
-
const dbPath = resolveDbPath();
|
|
4341
|
-
const dir = dirname(dbPath);
|
|
4342
|
-
if (dbPath !== ":memory:" && !existsSync(dir)) {
|
|
4343
|
-
mkdirSync(dir, { recursive: true });
|
|
4356
|
+
unwrap() {
|
|
4357
|
+
return this._def.type;
|
|
4344
4358
|
}
|
|
4345
|
-
db = new Database(dbPath);
|
|
4346
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
4347
|
-
db.exec("PRAGMA foreign_keys = ON");
|
|
4348
|
-
db.exec("PRAGMA busy_timeout = 5000");
|
|
4349
|
-
db.exec(`
|
|
4350
|
-
CREATE TABLE IF NOT EXISTS _migrations (
|
|
4351
|
-
id INTEGER PRIMARY KEY,
|
|
4352
|
-
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4353
|
-
);
|
|
4354
|
-
`);
|
|
4355
|
-
applyMigrations(db);
|
|
4356
|
-
return db;
|
|
4357
4359
|
}
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4360
|
+
|
|
4361
|
+
class ZodPipeline extends ZodType {
|
|
4362
|
+
_parse(input) {
|
|
4363
|
+
const { status, ctx } = this._processInputParams(input);
|
|
4364
|
+
if (ctx.common.async) {
|
|
4365
|
+
const handleAsync = async () => {
|
|
4366
|
+
const inResult = await this._def.in._parseAsync({
|
|
4367
|
+
data: ctx.data,
|
|
4368
|
+
path: ctx.path,
|
|
4369
|
+
parent: ctx
|
|
4370
|
+
});
|
|
4371
|
+
if (inResult.status === "aborted")
|
|
4372
|
+
return INVALID;
|
|
4373
|
+
if (inResult.status === "dirty") {
|
|
4374
|
+
status.dirty();
|
|
4375
|
+
return DIRTY(inResult.value);
|
|
4376
|
+
} else {
|
|
4377
|
+
return this._def.out._parseAsync({
|
|
4378
|
+
data: inResult.value,
|
|
4379
|
+
path: ctx.path,
|
|
4380
|
+
parent: ctx
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
};
|
|
4384
|
+
return handleAsync();
|
|
4385
|
+
} else {
|
|
4386
|
+
const inResult = this._def.in._parseSync({
|
|
4387
|
+
data: ctx.data,
|
|
4388
|
+
path: ctx.path,
|
|
4389
|
+
parent: ctx
|
|
4390
|
+
});
|
|
4391
|
+
if (inResult.status === "aborted")
|
|
4392
|
+
return INVALID;
|
|
4393
|
+
if (inResult.status === "dirty") {
|
|
4394
|
+
status.dirty();
|
|
4395
|
+
return {
|
|
4396
|
+
status: "dirty",
|
|
4397
|
+
value: inResult.value
|
|
4398
|
+
};
|
|
4399
|
+
} else {
|
|
4400
|
+
return this._def.out._parseSync({
|
|
4401
|
+
data: inResult.value,
|
|
4402
|
+
path: ctx.path,
|
|
4403
|
+
parent: ctx
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
static create(a, b) {
|
|
4409
|
+
return new ZodPipeline({
|
|
4410
|
+
in: a,
|
|
4411
|
+
out: b,
|
|
4412
|
+
typeName: ZodFirstPartyTypeKind.ZodPipeline
|
|
4413
|
+
});
|
|
4414
|
+
}
|
|
4364
4415
|
}
|
|
4365
4416
|
|
|
4417
|
+
class ZodReadonly extends ZodType {
|
|
4418
|
+
_parse(input) {
|
|
4419
|
+
const result = this._def.innerType._parse(input);
|
|
4420
|
+
const freeze = (data) => {
|
|
4421
|
+
if (isValid(data)) {
|
|
4422
|
+
data.value = Object.freeze(data.value);
|
|
4423
|
+
}
|
|
4424
|
+
return data;
|
|
4425
|
+
};
|
|
4426
|
+
return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
|
|
4427
|
+
}
|
|
4428
|
+
unwrap() {
|
|
4429
|
+
return this._def.innerType;
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
ZodReadonly.create = (type, params) => {
|
|
4433
|
+
return new ZodReadonly({
|
|
4434
|
+
innerType: type,
|
|
4435
|
+
typeName: ZodFirstPartyTypeKind.ZodReadonly,
|
|
4436
|
+
...processCreateParams(params)
|
|
4437
|
+
});
|
|
4438
|
+
};
|
|
4439
|
+
function cleanParams(params, data) {
|
|
4440
|
+
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
4441
|
+
const p2 = typeof p === "string" ? { message: p } : p;
|
|
4442
|
+
return p2;
|
|
4443
|
+
}
|
|
4444
|
+
function custom(check, _params = {}, fatal) {
|
|
4445
|
+
if (check)
|
|
4446
|
+
return ZodAny.create().superRefine((data, ctx) => {
|
|
4447
|
+
const r = check(data);
|
|
4448
|
+
if (r instanceof Promise) {
|
|
4449
|
+
return r.then((r2) => {
|
|
4450
|
+
if (!r2) {
|
|
4451
|
+
const params = cleanParams(_params, data);
|
|
4452
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
4453
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
4454
|
+
}
|
|
4455
|
+
});
|
|
4456
|
+
}
|
|
4457
|
+
if (!r) {
|
|
4458
|
+
const params = cleanParams(_params, data);
|
|
4459
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
4460
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
4461
|
+
}
|
|
4462
|
+
return;
|
|
4463
|
+
});
|
|
4464
|
+
return ZodAny.create();
|
|
4465
|
+
}
|
|
4466
|
+
var late = {
|
|
4467
|
+
object: ZodObject.lazycreate
|
|
4468
|
+
};
|
|
4469
|
+
var ZodFirstPartyTypeKind;
|
|
4470
|
+
(function(ZodFirstPartyTypeKind2) {
|
|
4471
|
+
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
4472
|
+
ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
|
|
4473
|
+
ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
|
|
4474
|
+
ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
|
|
4475
|
+
ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
|
|
4476
|
+
ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
|
|
4477
|
+
ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
|
|
4478
|
+
ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
|
|
4479
|
+
ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
|
|
4480
|
+
ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
|
|
4481
|
+
ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
|
|
4482
|
+
ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
|
|
4483
|
+
ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
|
|
4484
|
+
ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
|
|
4485
|
+
ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
|
|
4486
|
+
ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
|
|
4487
|
+
ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
|
|
4488
|
+
ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
|
|
4489
|
+
ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
|
|
4490
|
+
ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
|
|
4491
|
+
ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
|
|
4492
|
+
ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
|
|
4493
|
+
ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
|
|
4494
|
+
ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
|
|
4495
|
+
ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
|
|
4496
|
+
ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
|
|
4497
|
+
ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
|
|
4498
|
+
ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
|
|
4499
|
+
ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
|
|
4500
|
+
ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
|
|
4501
|
+
ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
|
|
4502
|
+
ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
|
|
4503
|
+
ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
|
|
4504
|
+
ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
|
|
4505
|
+
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
4506
|
+
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
4507
|
+
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
4508
|
+
var instanceOfType = (cls, params = {
|
|
4509
|
+
message: `Input not instance of ${cls.name}`
|
|
4510
|
+
}) => custom((data) => data instanceof cls, params);
|
|
4511
|
+
var stringType = ZodString.create;
|
|
4512
|
+
var numberType = ZodNumber.create;
|
|
4513
|
+
var nanType = ZodNaN.create;
|
|
4514
|
+
var bigIntType = ZodBigInt.create;
|
|
4515
|
+
var booleanType = ZodBoolean.create;
|
|
4516
|
+
var dateType = ZodDate.create;
|
|
4517
|
+
var symbolType = ZodSymbol.create;
|
|
4518
|
+
var undefinedType = ZodUndefined.create;
|
|
4519
|
+
var nullType = ZodNull.create;
|
|
4520
|
+
var anyType = ZodAny.create;
|
|
4521
|
+
var unknownType = ZodUnknown.create;
|
|
4522
|
+
var neverType = ZodNever.create;
|
|
4523
|
+
var voidType = ZodVoid.create;
|
|
4524
|
+
var arrayType = ZodArray.create;
|
|
4525
|
+
var objectType = ZodObject.create;
|
|
4526
|
+
var strictObjectType = ZodObject.strictCreate;
|
|
4527
|
+
var unionType = ZodUnion.create;
|
|
4528
|
+
var discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
4529
|
+
var intersectionType = ZodIntersection.create;
|
|
4530
|
+
var tupleType = ZodTuple.create;
|
|
4531
|
+
var recordType = ZodRecord.create;
|
|
4532
|
+
var mapType = ZodMap.create;
|
|
4533
|
+
var setType = ZodSet.create;
|
|
4534
|
+
var functionType = ZodFunction.create;
|
|
4535
|
+
var lazyType = ZodLazy.create;
|
|
4536
|
+
var literalType = ZodLiteral.create;
|
|
4537
|
+
var enumType = ZodEnum.create;
|
|
4538
|
+
var nativeEnumType = ZodNativeEnum.create;
|
|
4539
|
+
var promiseType = ZodPromise.create;
|
|
4540
|
+
var effectsType = ZodEffects.create;
|
|
4541
|
+
var optionalType = ZodOptional.create;
|
|
4542
|
+
var nullableType = ZodNullable.create;
|
|
4543
|
+
var preprocessType = ZodEffects.createWithPreprocess;
|
|
4544
|
+
var pipelineType = ZodPipeline.create;
|
|
4545
|
+
var ostring = () => stringType().optional();
|
|
4546
|
+
var onumber = () => numberType().optional();
|
|
4547
|
+
var oboolean = () => booleanType().optional();
|
|
4548
|
+
var coerce = {
|
|
4549
|
+
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
4550
|
+
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
4551
|
+
boolean: (arg) => ZodBoolean.create({
|
|
4552
|
+
...arg,
|
|
4553
|
+
coerce: true
|
|
4554
|
+
}),
|
|
4555
|
+
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
4556
|
+
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
4557
|
+
};
|
|
4558
|
+
var NEVER = INVALID;
|
|
4366
4559
|
// src/db/scenarios.ts
|
|
4560
|
+
init_types();
|
|
4561
|
+
init_database();
|
|
4367
4562
|
function nextShortId(projectId) {
|
|
4368
4563
|
const db2 = getDatabase();
|
|
4369
4564
|
if (projectId) {
|
|
@@ -4527,6 +4722,8 @@ function deleteScenario(id) {
|
|
|
4527
4722
|
}
|
|
4528
4723
|
|
|
4529
4724
|
// src/db/runs.ts
|
|
4725
|
+
init_types();
|
|
4726
|
+
init_database();
|
|
4530
4727
|
function createRun(input) {
|
|
4531
4728
|
const db2 = getDatabase();
|
|
4532
4729
|
const id = uuid();
|
|
@@ -4639,6 +4836,8 @@ function updateRun(id, updates) {
|
|
|
4639
4836
|
}
|
|
4640
4837
|
|
|
4641
4838
|
// src/db/results.ts
|
|
4839
|
+
init_types();
|
|
4840
|
+
init_database();
|
|
4642
4841
|
function createResult(input) {
|
|
4643
4842
|
const db2 = getDatabase();
|
|
4644
4843
|
const id = uuid();
|
|
@@ -4712,6 +4911,8 @@ function updateResult(id, updates) {
|
|
|
4712
4911
|
}
|
|
4713
4912
|
|
|
4714
4913
|
// src/db/screenshots.ts
|
|
4914
|
+
init_types();
|
|
4915
|
+
init_database();
|
|
4715
4916
|
function createScreenshot(input) {
|
|
4716
4917
|
const db2 = getDatabase();
|
|
4717
4918
|
const id = uuid();
|
|
@@ -4734,6 +4935,8 @@ function listScreenshots(resultId) {
|
|
|
4734
4935
|
}
|
|
4735
4936
|
|
|
4736
4937
|
// src/db/projects.ts
|
|
4938
|
+
init_types();
|
|
4939
|
+
init_database();
|
|
4737
4940
|
function createProject(input) {
|
|
4738
4941
|
const db2 = getDatabase();
|
|
4739
4942
|
const id = uuid();
|
|
@@ -4766,6 +4969,8 @@ function ensureProject(name, path) {
|
|
|
4766
4969
|
}
|
|
4767
4970
|
|
|
4768
4971
|
// src/db/agents.ts
|
|
4972
|
+
init_types();
|
|
4973
|
+
init_database();
|
|
4769
4974
|
function registerAgent(input) {
|
|
4770
4975
|
const db2 = getDatabase();
|
|
4771
4976
|
const existing = db2.query("SELECT * FROM agents WHERE name = ?").get(input.name);
|
|
@@ -4794,6 +4999,7 @@ function listAgents() {
|
|
|
4794
4999
|
|
|
4795
5000
|
// src/lib/browser.ts
|
|
4796
5001
|
import { chromium } from "playwright";
|
|
5002
|
+
init_types();
|
|
4797
5003
|
var DEFAULT_VIEWPORT = { width: 1280, height: 720 };
|
|
4798
5004
|
async function launchBrowser(options) {
|
|
4799
5005
|
const headless = options?.headless ?? true;
|
|
@@ -5013,6 +5219,7 @@ class Screenshotter {
|
|
|
5013
5219
|
}
|
|
5014
5220
|
|
|
5015
5221
|
// src/lib/ai-client.ts
|
|
5222
|
+
init_types();
|
|
5016
5223
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5017
5224
|
function resolveModel(nameOrPreset) {
|
|
5018
5225
|
if (nameOrPreset in MODEL_MAP) {
|
|
@@ -5565,7 +5772,8 @@ async function runAgentLoop(options) {
|
|
|
5565
5772
|
screenshotter,
|
|
5566
5773
|
model,
|
|
5567
5774
|
runId,
|
|
5568
|
-
maxTurns = 30
|
|
5775
|
+
maxTurns = 30,
|
|
5776
|
+
onStep
|
|
5569
5777
|
} = options;
|
|
5570
5778
|
const systemPrompt = [
|
|
5571
5779
|
"You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
|
|
@@ -5624,8 +5832,8 @@ async function runAgentLoop(options) {
|
|
|
5624
5832
|
}
|
|
5625
5833
|
const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
|
|
5626
5834
|
if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
|
|
5627
|
-
const
|
|
5628
|
-
const textReasoning =
|
|
5835
|
+
const textBlocks2 = response.content.filter((block) => block.type === "text");
|
|
5836
|
+
const textReasoning = textBlocks2.map((b) => b.text).join(`
|
|
5629
5837
|
`);
|
|
5630
5838
|
return {
|
|
5631
5839
|
status: "error",
|
|
@@ -5636,10 +5844,22 @@ async function runAgentLoop(options) {
|
|
|
5636
5844
|
};
|
|
5637
5845
|
}
|
|
5638
5846
|
const toolResults = [];
|
|
5847
|
+
const textBlocks = response.content.filter((block) => block.type === "text");
|
|
5848
|
+
if (textBlocks.length > 0 && onStep) {
|
|
5849
|
+
const thinking = textBlocks.map((b) => b.text).join(`
|
|
5850
|
+
`);
|
|
5851
|
+
onStep({ type: "thinking", thinking, stepNumber });
|
|
5852
|
+
}
|
|
5639
5853
|
for (const toolBlock of toolUseBlocks) {
|
|
5640
5854
|
stepNumber++;
|
|
5641
5855
|
const toolInput = toolBlock.input;
|
|
5856
|
+
if (onStep) {
|
|
5857
|
+
onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
|
|
5858
|
+
}
|
|
5642
5859
|
const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
|
|
5860
|
+
if (onStep) {
|
|
5861
|
+
onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
|
|
5862
|
+
}
|
|
5643
5863
|
if (execResult.screenshot) {
|
|
5644
5864
|
screenshots.push({
|
|
5645
5865
|
...execResult.screenshot,
|
|
@@ -5691,6 +5911,7 @@ function createClient(apiKey) {
|
|
|
5691
5911
|
}
|
|
5692
5912
|
|
|
5693
5913
|
// src/lib/config.ts
|
|
5914
|
+
init_types();
|
|
5694
5915
|
import { homedir as homedir3 } from "os";
|
|
5695
5916
|
import { join as join3 } from "path";
|
|
5696
5917
|
import { readFileSync, existsSync as existsSync3 } from "fs";
|
|
@@ -5781,7 +6002,20 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
5781
6002
|
screenshotter,
|
|
5782
6003
|
model,
|
|
5783
6004
|
runId,
|
|
5784
|
-
maxTurns: 30
|
|
6005
|
+
maxTurns: 30,
|
|
6006
|
+
onStep: (stepEvent) => {
|
|
6007
|
+
emit({
|
|
6008
|
+
type: `step:${stepEvent.type}`,
|
|
6009
|
+
scenarioId: scenario.id,
|
|
6010
|
+
scenarioName: scenario.name,
|
|
6011
|
+
runId,
|
|
6012
|
+
toolName: stepEvent.toolName,
|
|
6013
|
+
toolInput: stepEvent.toolInput,
|
|
6014
|
+
toolResult: stepEvent.toolResult,
|
|
6015
|
+
thinking: stepEvent.thinking,
|
|
6016
|
+
stepNumber: stepEvent.stepNumber
|
|
6017
|
+
});
|
|
6018
|
+
}
|
|
5785
6019
|
});
|
|
5786
6020
|
for (const ss of agentResult.screenshots) {
|
|
5787
6021
|
createScreenshot({
|
|
@@ -5834,24 +6068,70 @@ async function runBatch(scenarios, options) {
|
|
|
5834
6068
|
projectId: options.projectId
|
|
5835
6069
|
});
|
|
5836
6070
|
updateRun(run.id, { status: "running", total: scenarios.length });
|
|
6071
|
+
let sortedScenarios = scenarios;
|
|
6072
|
+
try {
|
|
6073
|
+
const { topologicalSort: topologicalSort2 } = await Promise.resolve().then(() => (init_flows(), exports_flows));
|
|
6074
|
+
const scenarioIds = scenarios.map((s) => s.id);
|
|
6075
|
+
const sortedIds = topologicalSort2(scenarioIds);
|
|
6076
|
+
const scenarioMap = new Map(scenarios.map((s) => [s.id, s]));
|
|
6077
|
+
sortedScenarios = sortedIds.map((id) => scenarioMap.get(id)).filter((s) => s !== undefined);
|
|
6078
|
+
for (const s of scenarios) {
|
|
6079
|
+
if (!sortedIds.includes(s.id))
|
|
6080
|
+
sortedScenarios.push(s);
|
|
6081
|
+
}
|
|
6082
|
+
} catch {}
|
|
5837
6083
|
const results = [];
|
|
6084
|
+
const failedScenarioIds = new Set;
|
|
6085
|
+
const canRun = async (scenario) => {
|
|
6086
|
+
try {
|
|
6087
|
+
const { getDependencies: getDependencies2 } = await Promise.resolve().then(() => (init_flows(), exports_flows));
|
|
6088
|
+
const deps = getDependencies2(scenario.id);
|
|
6089
|
+
for (const depId of deps) {
|
|
6090
|
+
if (failedScenarioIds.has(depId))
|
|
6091
|
+
return false;
|
|
6092
|
+
}
|
|
6093
|
+
} catch {}
|
|
6094
|
+
return true;
|
|
6095
|
+
};
|
|
5838
6096
|
if (parallel <= 1) {
|
|
5839
|
-
for (const scenario of
|
|
6097
|
+
for (const scenario of sortedScenarios) {
|
|
6098
|
+
if (!await canRun(scenario)) {
|
|
6099
|
+
const result2 = createResult({ runId: run.id, scenarioId: scenario.id, model, stepsTotal: 0 });
|
|
6100
|
+
const skipped = updateResult(result2.id, { status: "skipped", error: "Skipped: dependency failed" });
|
|
6101
|
+
results.push(skipped);
|
|
6102
|
+
failedScenarioIds.add(scenario.id);
|
|
6103
|
+
emit({ type: "scenario:error", scenarioId: scenario.id, scenarioName: scenario.name, error: "Dependency failed \u2014 skipped", runId: run.id });
|
|
6104
|
+
continue;
|
|
6105
|
+
}
|
|
5840
6106
|
const result = await runSingleScenario(scenario, run.id, options);
|
|
5841
6107
|
results.push(result);
|
|
6108
|
+
if (result.status === "failed" || result.status === "error") {
|
|
6109
|
+
failedScenarioIds.add(scenario.id);
|
|
6110
|
+
}
|
|
5842
6111
|
}
|
|
5843
6112
|
} else {
|
|
5844
|
-
const queue = [...
|
|
6113
|
+
const queue = [...sortedScenarios];
|
|
5845
6114
|
const running = [];
|
|
5846
6115
|
const processNext = async () => {
|
|
5847
6116
|
const scenario = queue.shift();
|
|
5848
6117
|
if (!scenario)
|
|
5849
6118
|
return;
|
|
6119
|
+
if (!await canRun(scenario)) {
|
|
6120
|
+
const result2 = createResult({ runId: run.id, scenarioId: scenario.id, model, stepsTotal: 0 });
|
|
6121
|
+
const skipped = updateResult(result2.id, { status: "skipped", error: "Skipped: dependency failed" });
|
|
6122
|
+
results.push(skipped);
|
|
6123
|
+
failedScenarioIds.add(scenario.id);
|
|
6124
|
+
await processNext();
|
|
6125
|
+
return;
|
|
6126
|
+
}
|
|
5850
6127
|
const result = await runSingleScenario(scenario, run.id, options);
|
|
5851
6128
|
results.push(result);
|
|
6129
|
+
if (result.status === "failed" || result.status === "error") {
|
|
6130
|
+
failedScenarioIds.add(scenario.id);
|
|
6131
|
+
}
|
|
5852
6132
|
await processNext();
|
|
5853
6133
|
};
|
|
5854
|
-
const workers = Math.min(parallel,
|
|
6134
|
+
const workers = Math.min(parallel, sortedScenarios.length);
|
|
5855
6135
|
for (let i = 0;i < workers; i++) {
|
|
5856
6136
|
running.push(processNext());
|
|
5857
6137
|
}
|
|
@@ -5979,6 +6259,7 @@ import { Database as Database2 } from "bun:sqlite";
|
|
|
5979
6259
|
import { existsSync as existsSync4 } from "fs";
|
|
5980
6260
|
import { join as join4 } from "path";
|
|
5981
6261
|
import { homedir as homedir4 } from "os";
|
|
6262
|
+
init_types();
|
|
5982
6263
|
function resolveTodosDbPath() {
|
|
5983
6264
|
const envPath = process.env["TODOS_DB_PATH"];
|
|
5984
6265
|
if (envPath)
|
|
@@ -6076,6 +6357,9 @@ function importFromTodos(options = {}) {
|
|
|
6076
6357
|
}
|
|
6077
6358
|
|
|
6078
6359
|
// src/db/schedules.ts
|
|
6360
|
+
init_database();
|
|
6361
|
+
init_types();
|
|
6362
|
+
init_database();
|
|
6079
6363
|
function createSchedule(input) {
|
|
6080
6364
|
const db2 = getDatabase();
|
|
6081
6365
|
const id = uuid();
|
|
@@ -6202,6 +6486,7 @@ function updateLastRun(id, runId, nextRunAt) {
|
|
|
6202
6486
|
}
|
|
6203
6487
|
|
|
6204
6488
|
// src/lib/scheduler.ts
|
|
6489
|
+
init_types();
|
|
6205
6490
|
function parseCronField(field, min, max) {
|
|
6206
6491
|
const results = new Set;
|
|
6207
6492
|
const parts = field.split(",");
|
|
@@ -6415,6 +6700,7 @@ class Scheduler {
|
|
|
6415
6700
|
}
|
|
6416
6701
|
|
|
6417
6702
|
// src/mcp/index.ts
|
|
6703
|
+
init_database();
|
|
6418
6704
|
var server = new McpServer({
|
|
6419
6705
|
name: "testers",
|
|
6420
6706
|
version: "0.0.1"
|