@hasna/recordings 0.2.5 → 0.2.7

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 CHANGED
@@ -1397,6 +1397,59 @@ function getRecordingStats(db) {
1397
1397
  };
1398
1398
  }
1399
1399
 
1400
+ // src/db/projects.ts
1401
+ function parseProject(row) {
1402
+ return {
1403
+ id: row["id"],
1404
+ name: row["name"],
1405
+ path: row["path"],
1406
+ description: row["description"] || null,
1407
+ created_at: row["created_at"],
1408
+ updated_at: row["updated_at"]
1409
+ };
1410
+ }
1411
+ function registerProject(name, path, description, db) {
1412
+ const d = db || getDatabase();
1413
+ const now2 = new Date().toISOString();
1414
+ const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
1415
+ if (existing) {
1416
+ d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now2, existing["id"]);
1417
+ return getProject(existing["id"], d);
1418
+ }
1419
+ const id = crypto.randomUUID();
1420
+ d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now2, now2);
1421
+ return getProject(id, d);
1422
+ }
1423
+ function getProject(idOrPath, db) {
1424
+ const d = db || getDatabase();
1425
+ let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
1426
+ if (!row) {
1427
+ row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
1428
+ }
1429
+ if (!row) {
1430
+ row = d.query("SELECT * FROM projects WHERE name = ?").get(idOrPath);
1431
+ }
1432
+ if (!row && idOrPath) {
1433
+ row = d.query("SELECT * FROM projects WHERE id LIKE ? || '%'").get(idOrPath);
1434
+ }
1435
+ return row ? parseProject(row) : null;
1436
+ }
1437
+ function listProjects(db) {
1438
+ const d = db || getDatabase();
1439
+ const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
1440
+ return rows.map(parseProject);
1441
+ }
1442
+
1443
+ // src/db/errors.ts
1444
+ class ProjectNotFoundError extends Error {
1445
+ ref;
1446
+ constructor(ref) {
1447
+ super(`project not found: ${ref}`);
1448
+ this.name = "ProjectNotFoundError";
1449
+ this.ref = ref;
1450
+ }
1451
+ }
1452
+
1400
1453
  // src/db/agents.ts
1401
1454
  function parseAgent(row) {
1402
1455
  return {
@@ -1450,49 +1503,19 @@ function setAgentFocus(idOrName, projectId, db) {
1450
1503
  const agent = getAgent(idOrName, d);
1451
1504
  if (!agent)
1452
1505
  return null;
1453
- d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(projectId, new Date().toISOString(), agent.id);
1506
+ let resolvedProjectId = null;
1507
+ if (projectId) {
1508
+ const project = getProject(projectId, d);
1509
+ if (!project)
1510
+ throw new ProjectNotFoundError(projectId);
1511
+ resolvedProjectId = project.id;
1512
+ }
1513
+ d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
1454
1514
  return getAgent(agent.id, d);
1455
1515
  }
1456
1516
 
1457
- // src/db/projects.ts
1458
- function parseProject(row) {
1459
- return {
1460
- id: row["id"],
1461
- name: row["name"],
1462
- path: row["path"],
1463
- description: row["description"] || null,
1464
- created_at: row["created_at"],
1465
- updated_at: row["updated_at"]
1466
- };
1467
- }
1468
- function registerProject(name, path, description, db) {
1469
- const d = db || getDatabase();
1470
- const now2 = new Date().toISOString();
1471
- const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
1472
- if (existing) {
1473
- d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now2, existing["id"]);
1474
- return getProject(existing["id"], d);
1475
- }
1476
- const id = crypto.randomUUID();
1477
- d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now2, now2);
1478
- return getProject(id, d);
1479
- }
1480
- function getProject(idOrPath, db) {
1481
- const d = db || getDatabase();
1482
- let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
1483
- if (!row) {
1484
- row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
1485
- }
1486
- return row ? parseProject(row) : null;
1487
- }
1488
- function listProjects(db) {
1489
- const d = db || getDatabase();
1490
- const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
1491
- return rows.map(parseProject);
1492
- }
1493
-
1494
1517
  // src/version.ts
1495
- var VERSION = "0.2.5";
1518
+ var VERSION = "0.2.7";
1496
1519
 
1497
1520
  // src/db/feedback.ts
1498
1521
  function saveFeedback(input) {
@@ -1923,8 +1946,16 @@ function apiStore(client) {
1923
1946
  const res = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/focus`, { project_id: projectId });
1924
1947
  return res ? unwrap(res, "agent") : null;
1925
1948
  } catch (error) {
1926
- if (error && typeof error === "object" && error.status === 404)
1927
- return null;
1949
+ if (error && typeof error === "object") {
1950
+ const status = error.status;
1951
+ if (status === 404)
1952
+ return null;
1953
+ if (status === 400) {
1954
+ const body = error.body;
1955
+ const msg = body && typeof body === "object" && typeof body.error === "string" ? body.error : "invalid focus request";
1956
+ throw new Error(msg);
1957
+ }
1958
+ }
1928
1959
  throw error;
1929
1960
  }
1930
1961
  },
@@ -2345,6 +2376,50 @@ function applyEnhancementOptions(config, opts) {
2345
2376
  return config;
2346
2377
  }
2347
2378
 
2379
+ // src/cli/mcp-config.ts
2380
+ function escapeRegExp(value) {
2381
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2382
+ }
2383
+ function isTableHeader(line) {
2384
+ return /^\s*\[/.test(line);
2385
+ }
2386
+ function removeCodexServerBlock(content, name) {
2387
+ const headerRe = new RegExp(`^\\s*\\[mcp_servers\\.${escapeRegExp(name)}(\\..+)?\\]\\s*$`);
2388
+ const lines = content.split(`
2389
+ `);
2390
+ const out = [];
2391
+ let skipping = false;
2392
+ let removed = false;
2393
+ for (const line of lines) {
2394
+ if (isTableHeader(line)) {
2395
+ skipping = headerRe.test(line);
2396
+ if (skipping) {
2397
+ removed = true;
2398
+ continue;
2399
+ }
2400
+ }
2401
+ if (skipping)
2402
+ continue;
2403
+ out.push(line);
2404
+ }
2405
+ const normalized = out.join(`
2406
+ `).replace(/\n{3,}/g, `
2407
+
2408
+ `).replace(/^\n+/, "");
2409
+ return { content: normalized, removed };
2410
+ }
2411
+ function upsertCodexStdioBlock(content, name, mcpCmd) {
2412
+ const { content: cleaned } = removeCodexServerBlock(content, name);
2413
+ const trimmed = cleaned.replace(/\s+$/, "");
2414
+ const block = `[mcp_servers.${name}]
2415
+ command = "${mcpCmd}"
2416
+ args = []
2417
+ `;
2418
+ return trimmed.length > 0 ? `${trimmed}
2419
+
2420
+ ${block}` : block;
2421
+ }
2422
+
2348
2423
  // src/cli/index.ts
2349
2424
  var program = new Command;
2350
2425
  program.name("recordings").description("Speech-to-text recording tool \u2014 record, transcribe, and enhance with AI").version(VERSION).option("--json", "Output as JSON").option("--agent <name>", "Agent name or ID").option("--project <name>", "Project name or ID").option("--session <id>", "Session ID");
@@ -3173,19 +3248,16 @@ program.command("mcp").description("Install recordings MCP server into Claude Co
3173
3248
  if (target === "codex") {
3174
3249
  const configPath = pathJoin2(home, ".codex", "config.toml");
3175
3250
  if (fileExists(configPath)) {
3176
- let content = readFileSync3(configPath, "utf-8");
3251
+ const content = readFileSync3(configPath, "utf-8");
3177
3252
  if (opts.uninstall) {
3178
- content = content.replace(/\n\[mcp_servers\.recordings\]\ncommand = "[^"]*"\nargs = \[\]\n?/g, `
3179
- `);
3180
- } else if (!content.includes("[mcp_servers.recordings]")) {
3181
- content += `
3182
- [mcp_servers.recordings]
3183
- command = "${mcpCmd}"
3184
- args = []
3185
- `;
3253
+ const { content: next, removed } = removeCodexServerBlock(content, "recordings");
3254
+ writeFileSync(configPath, next, "utf-8");
3255
+ console.log(removed ? chalk.green(`Removed from Codex: ${configPath}`) : chalk.yellow(`Codex: no recordings MCP block found in ${configPath}`));
3256
+ } else {
3257
+ const next = upsertCodexStdioBlock(content, "recordings", mcpCmd);
3258
+ writeFileSync(configPath, next, "utf-8");
3259
+ console.log(chalk.green(`Installed into Codex: ${configPath}`));
3186
3260
  }
3187
- writeFileSync(configPath, content, "utf-8");
3188
- console.log(chalk.green(`${action} Codex: ${configPath}`));
3189
3261
  } else {
3190
3262
  console.log(chalk.yellow(`Codex config not found: ${configPath}`));
3191
3263
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pure helpers for editing an agent's MCP server config.
3
+ *
4
+ * Codex stores MCP servers as TOML tables (`[mcp_servers.<name>]`). A server
5
+ * block can be written in several transport forms — stdio (`command`/`args`)
6
+ * or streamable-HTTP (`url`/`http`, optional `headers`/`env` subtables). The
7
+ * uninstall/upsert logic must treat the whole table (and any of its subtables)
8
+ * as one unit regardless of which keys it holds, instead of matching one
9
+ * hand-written key layout. These functions are exported so the round-trip is
10
+ * unit-tested without touching the real `~/.codex/config.toml`.
11
+ */
12
+ /**
13
+ * Remove the `[mcp_servers.<name>]` table AND any of its subtables
14
+ * (`[mcp_servers.<name>.env]`, `.headers`, …) with their bodies — whatever the
15
+ * transport form. A block runs from its header until the next table header or
16
+ * end of file. Returns the rewritten content and whether anything was removed.
17
+ */
18
+ export declare function removeCodexServerBlock(content: string, name: string): {
19
+ content: string;
20
+ removed: boolean;
21
+ };
22
+ /**
23
+ * Ensure exactly one stdio `[mcp_servers.<name>]` block exists, pointing at
24
+ * `mcpCmd`. Any pre-existing block (in any transport form) is replaced, so the
25
+ * install is authoritative and idempotent rather than a silent no-op.
26
+ */
27
+ export declare function upsertCodexStdioBlock(content: string, name: string, mcpCmd: string): string;
28
+ //# sourceMappingURL=mcp-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.d.ts","sourceRoot":"","sources":["../../src/cli/mcp-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4BvC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM,CAKR"}
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAc/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAuBP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAuBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAS5E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAUrG"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAc/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAuBP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAuBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAS5E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI,CAoBrG"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared domain errors for the storage layer.
3
+ *
4
+ * `ProjectNotFoundError` is thrown when a focus request references a project
5
+ * that cannot be resolved to a real row. It exists so both backends (LocalStore
6
+ * SQLite and the server Postgres repo) fail focus the SAME clean way — and so
7
+ * the `/v1` route can translate it into a 400 instead of leaking the raw
8
+ * foreign-key error (`agents_active_project_id_fkey` / "FOREIGN KEY constraint
9
+ * failed").
10
+ */
11
+ export declare class ProjectNotFoundError extends Error {
12
+ readonly ref: string;
13
+ constructor(ref: string);
14
+ }
15
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/db/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBACT,GAAG,EAAE,MAAM;CAKxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAajD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAuBT;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,GAAG,IAAI,CAchB;AAED,wBAAgB,YAAY,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,EAAE,CAMrD"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAajD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAuBT;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,GAAG,IAAI,CA4BhB;AAED,wBAAgB,YAAY,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,EAAE,CAMrD"}
package/dist/index.js CHANGED
@@ -656,6 +656,59 @@ function getRecordingStats(db) {
656
656
  };
657
657
  }
658
658
 
659
+ // src/db/projects.ts
660
+ function parseProject(row) {
661
+ return {
662
+ id: row["id"],
663
+ name: row["name"],
664
+ path: row["path"],
665
+ description: row["description"] || null,
666
+ created_at: row["created_at"],
667
+ updated_at: row["updated_at"]
668
+ };
669
+ }
670
+ function registerProject(name, path, description, db) {
671
+ const d = db || getDatabase();
672
+ const now = new Date().toISOString();
673
+ const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
674
+ if (existing) {
675
+ d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
676
+ return getProject(existing["id"], d);
677
+ }
678
+ const id = crypto.randomUUID();
679
+ d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
680
+ return getProject(id, d);
681
+ }
682
+ function getProject(idOrPath, db) {
683
+ const d = db || getDatabase();
684
+ let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
685
+ if (!row) {
686
+ row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
687
+ }
688
+ if (!row) {
689
+ row = d.query("SELECT * FROM projects WHERE name = ?").get(idOrPath);
690
+ }
691
+ if (!row && idOrPath) {
692
+ row = d.query("SELECT * FROM projects WHERE id LIKE ? || '%'").get(idOrPath);
693
+ }
694
+ return row ? parseProject(row) : null;
695
+ }
696
+ function listProjects(db) {
697
+ const d = db || getDatabase();
698
+ const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
699
+ return rows.map(parseProject);
700
+ }
701
+
702
+ // src/db/errors.ts
703
+ class ProjectNotFoundError extends Error {
704
+ ref;
705
+ constructor(ref) {
706
+ super(`project not found: ${ref}`);
707
+ this.name = "ProjectNotFoundError";
708
+ this.ref = ref;
709
+ }
710
+ }
711
+
659
712
  // src/db/agents.ts
660
713
  function parseAgent(row) {
661
714
  return {
@@ -709,49 +762,19 @@ function setAgentFocus(idOrName, projectId, db) {
709
762
  const agent = getAgent(idOrName, d);
710
763
  if (!agent)
711
764
  return null;
712
- d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(projectId, new Date().toISOString(), agent.id);
765
+ let resolvedProjectId = null;
766
+ if (projectId) {
767
+ const project = getProject(projectId, d);
768
+ if (!project)
769
+ throw new ProjectNotFoundError(projectId);
770
+ resolvedProjectId = project.id;
771
+ }
772
+ d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
713
773
  return getAgent(agent.id, d);
714
774
  }
715
775
 
716
- // src/db/projects.ts
717
- function parseProject(row) {
718
- return {
719
- id: row["id"],
720
- name: row["name"],
721
- path: row["path"],
722
- description: row["description"] || null,
723
- created_at: row["created_at"],
724
- updated_at: row["updated_at"]
725
- };
726
- }
727
- function registerProject(name, path, description, db) {
728
- const d = db || getDatabase();
729
- const now = new Date().toISOString();
730
- const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
731
- if (existing) {
732
- d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
733
- return getProject(existing["id"], d);
734
- }
735
- const id = crypto.randomUUID();
736
- d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
737
- return getProject(id, d);
738
- }
739
- function getProject(idOrPath, db) {
740
- const d = db || getDatabase();
741
- let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
742
- if (!row) {
743
- row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
744
- }
745
- return row ? parseProject(row) : null;
746
- }
747
- function listProjects(db) {
748
- const d = db || getDatabase();
749
- const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
750
- return rows.map(parseProject);
751
- }
752
-
753
776
  // src/version.ts
754
- var VERSION = "0.2.5";
777
+ var VERSION = "0.2.7";
755
778
 
756
779
  // src/db/feedback.ts
757
780
  function saveFeedback(input) {
@@ -1182,8 +1205,16 @@ function apiStore(client) {
1182
1205
  const res = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/focus`, { project_id: projectId });
1183
1206
  return res ? unwrap(res, "agent") : null;
1184
1207
  } catch (error) {
1185
- if (error && typeof error === "object" && error.status === 404)
1186
- return null;
1208
+ if (error && typeof error === "object") {
1209
+ const status = error.status;
1210
+ if (status === 404)
1211
+ return null;
1212
+ if (status === 400) {
1213
+ const body = error.body;
1214
+ const msg = body && typeof body === "object" && typeof body.error === "string" ? body.error : "invalid focus request";
1215
+ throw new Error(msg);
1216
+ }
1217
+ }
1187
1218
  throw error;
1188
1219
  }
1189
1220
  },
package/dist/mcp/index.js CHANGED
@@ -4530,6 +4530,59 @@ function getRecordingStats(db) {
4530
4530
  };
4531
4531
  }
4532
4532
 
4533
+ // src/db/projects.ts
4534
+ function parseProject(row) {
4535
+ return {
4536
+ id: row["id"],
4537
+ name: row["name"],
4538
+ path: row["path"],
4539
+ description: row["description"] || null,
4540
+ created_at: row["created_at"],
4541
+ updated_at: row["updated_at"]
4542
+ };
4543
+ }
4544
+ function registerProject(name, path, description, db) {
4545
+ const d = db || getDatabase();
4546
+ const now = new Date().toISOString();
4547
+ const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
4548
+ if (existing) {
4549
+ d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
4550
+ return getProject(existing["id"], d);
4551
+ }
4552
+ const id = crypto.randomUUID();
4553
+ d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
4554
+ return getProject(id, d);
4555
+ }
4556
+ function getProject(idOrPath, db) {
4557
+ const d = db || getDatabase();
4558
+ let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
4559
+ if (!row) {
4560
+ row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
4561
+ }
4562
+ if (!row) {
4563
+ row = d.query("SELECT * FROM projects WHERE name = ?").get(idOrPath);
4564
+ }
4565
+ if (!row && idOrPath) {
4566
+ row = d.query("SELECT * FROM projects WHERE id LIKE ? || '%'").get(idOrPath);
4567
+ }
4568
+ return row ? parseProject(row) : null;
4569
+ }
4570
+ function listProjects(db) {
4571
+ const d = db || getDatabase();
4572
+ const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
4573
+ return rows.map(parseProject);
4574
+ }
4575
+
4576
+ // src/db/errors.ts
4577
+ class ProjectNotFoundError extends Error {
4578
+ ref;
4579
+ constructor(ref) {
4580
+ super(`project not found: ${ref}`);
4581
+ this.name = "ProjectNotFoundError";
4582
+ this.ref = ref;
4583
+ }
4584
+ }
4585
+
4533
4586
  // src/db/agents.ts
4534
4587
  function parseAgent(row) {
4535
4588
  return {
@@ -4583,49 +4636,19 @@ function setAgentFocus(idOrName, projectId, db) {
4583
4636
  const agent = getAgent(idOrName, d);
4584
4637
  if (!agent)
4585
4638
  return null;
4586
- d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(projectId, new Date().toISOString(), agent.id);
4639
+ let resolvedProjectId = null;
4640
+ if (projectId) {
4641
+ const project = getProject(projectId, d);
4642
+ if (!project)
4643
+ throw new ProjectNotFoundError(projectId);
4644
+ resolvedProjectId = project.id;
4645
+ }
4646
+ d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
4587
4647
  return getAgent(agent.id, d);
4588
4648
  }
4589
4649
 
4590
- // src/db/projects.ts
4591
- function parseProject(row) {
4592
- return {
4593
- id: row["id"],
4594
- name: row["name"],
4595
- path: row["path"],
4596
- description: row["description"] || null,
4597
- created_at: row["created_at"],
4598
- updated_at: row["updated_at"]
4599
- };
4600
- }
4601
- function registerProject(name, path, description, db) {
4602
- const d = db || getDatabase();
4603
- const now = new Date().toISOString();
4604
- const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
4605
- if (existing) {
4606
- d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
4607
- return getProject(existing["id"], d);
4608
- }
4609
- const id = crypto.randomUUID();
4610
- d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
4611
- return getProject(id, d);
4612
- }
4613
- function getProject(idOrPath, db) {
4614
- const d = db || getDatabase();
4615
- let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
4616
- if (!row) {
4617
- row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
4618
- }
4619
- return row ? parseProject(row) : null;
4620
- }
4621
- function listProjects(db) {
4622
- const d = db || getDatabase();
4623
- const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
4624
- return rows.map(parseProject);
4625
- }
4626
-
4627
4650
  // src/version.ts
4628
- var VERSION = "0.2.5";
4651
+ var VERSION = "0.2.7";
4629
4652
 
4630
4653
  // src/db/feedback.ts
4631
4654
  function saveFeedback(input) {
@@ -5056,8 +5079,16 @@ function apiStore(client) {
5056
5079
  const res = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/focus`, { project_id: projectId });
5057
5080
  return res ? unwrap(res, "agent") : null;
5058
5081
  } catch (error) {
5059
- if (error && typeof error === "object" && error.status === 404)
5060
- return null;
5082
+ if (error && typeof error === "object") {
5083
+ const status = error.status;
5084
+ if (status === 404)
5085
+ return null;
5086
+ if (status === 400) {
5087
+ const body = error.body;
5088
+ const msg = body && typeof body === "object" && typeof body.error === "string" ? body.error : "invalid focus request";
5089
+ throw new Error(msg);
5090
+ }
5091
+ }
5061
5092
  throw error;
5062
5093
  }
5063
5094
  },
@@ -18,7 +18,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = import.meta.require;
19
19
 
20
20
  // src/version.ts
21
- var VERSION = "0.2.5";
21
+ var VERSION = "0.2.7";
22
22
 
23
23
  // src/db/remote-storage.ts
24
24
  import pg from "pg";
@@ -261,6 +261,19 @@ var init_cloud = __esm(() => {
261
261
  init_pg_migrations();
262
262
  });
263
263
 
264
+ // src/db/errors.ts
265
+ var ProjectNotFoundError;
266
+ var init_errors = __esm(() => {
267
+ ProjectNotFoundError = class ProjectNotFoundError extends Error {
268
+ ref;
269
+ constructor(ref) {
270
+ super(`project not found: ${ref}`);
271
+ this.name = "ProjectNotFoundError";
272
+ this.ref = ref;
273
+ }
274
+ };
275
+ });
276
+
264
277
  // src/server/repo.ts
265
278
  function shortUuid() {
266
279
  return crypto.randomUUID().slice(0, 8);
@@ -446,7 +459,14 @@ async function setAgentFocus(pg2, idOrName, projectId) {
446
459
  const agent = await getAgent(pg2, idOrName);
447
460
  if (!agent)
448
461
  return null;
449
- await pg2.run("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?", projectId, new Date().toISOString(), agent.id);
462
+ let resolvedProjectId = null;
463
+ if (projectId) {
464
+ const project = await getProject(pg2, projectId);
465
+ if (!project)
466
+ throw new ProjectNotFoundError(projectId);
467
+ resolvedProjectId = project.id;
468
+ }
469
+ await pg2.run("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?", resolvedProjectId, new Date().toISOString(), agent.id);
450
470
  return getAgent(pg2, agent.id);
451
471
  }
452
472
  async function registerProject(pg2, name, path, description) {
@@ -467,6 +487,12 @@ async function getProject(pg2, idOrPath) {
467
487
  if (!row) {
468
488
  row = await pg2.get("SELECT * FROM projects WHERE path = ?", idOrPath);
469
489
  }
490
+ if (!row) {
491
+ row = await pg2.get("SELECT * FROM projects WHERE name = ?", idOrPath);
492
+ }
493
+ if (!row && idOrPath) {
494
+ row = await pg2.get("SELECT * FROM projects WHERE id LIKE ? || '%'", idOrPath);
495
+ }
470
496
  return row ? parseProject(row) : null;
471
497
  }
472
498
  async function listProjects(pg2) {
@@ -480,6 +506,9 @@ async function saveFeedback(pg2, input) {
480
506
  await pg2.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", input.message, input.email || null, input.category || "general", input.version || null);
481
507
  return { saved: true };
482
508
  }
509
+ var init_repo = __esm(() => {
510
+ init_errors();
511
+ });
483
512
 
484
513
  // src/server/v1.ts
485
514
  function json(body, status = 200) {
@@ -592,8 +621,14 @@ async function handleV1Request(req, url) {
592
621
  if (method !== "POST")
593
622
  return error(405, `method ${method} not allowed on /v1/agents/:id/focus`);
594
623
  const body = await readJson(req);
595
- const agent = await setAgentFocus(pg2, id, body?.project_id ?? null);
596
- return agent ? json({ agent }) : error(404, "agent not found");
624
+ try {
625
+ const agent = await setAgentFocus(pg2, id, body?.project_id ?? null);
626
+ return agent ? json({ agent }) : error(404, "agent not found");
627
+ } catch (e) {
628
+ if (e instanceof ProjectNotFoundError)
629
+ return error(400, e.message);
630
+ throw e;
631
+ }
597
632
  }
598
633
  if (action)
599
634
  return error(404, `unknown agent action: ${action}`);
@@ -650,6 +685,7 @@ async function handleV1Request(req, url) {
650
685
  var JSON_HEADERS;
651
686
  var init_v1 = __esm(() => {
652
687
  init_cloud();
688
+ init_repo();
653
689
  JSON_HEADERS = { "Content-Type": "application/json" };
654
690
  });
655
691
 
@@ -1430,7 +1466,7 @@ function getErrorMap() {
1430
1466
  return overrideErrorMap;
1431
1467
  }
1432
1468
  var overrideErrorMap;
1433
- var init_errors = __esm(() => {
1469
+ var init_errors2 = __esm(() => {
1434
1470
  init_en();
1435
1471
  overrideErrorMap = en_default;
1436
1472
  });
@@ -1532,7 +1568,7 @@ var makeIssue = (params) => {
1532
1568
  };
1533
1569
  }, EMPTY_PATH, INVALID, DIRTY = (value) => ({ status: "dirty", value }), OK = (value) => ({ status: "valid", value }), isAborted = (x) => x.status === "aborted", isDirty = (x) => x.status === "dirty", isValid = (x) => x.status === "valid", isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
1534
1570
  var init_parseUtil = __esm(() => {
1535
- init_errors();
1571
+ init_errors2();
1536
1572
  init_en();
1537
1573
  EMPTY_PATH = [];
1538
1574
  INVALID = Object.freeze({
@@ -2100,7 +2136,7 @@ var handleResult = (ctx, result) => {
2100
2136
  }) => custom((data) => data instanceof cls, params), stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, ostring = () => stringType().optional(), onumber = () => numberType().optional(), oboolean = () => booleanType().optional(), coerce, NEVER;
2101
2137
  var init_types = __esm(() => {
2102
2138
  init_ZodError();
2103
- init_errors();
2139
+ init_errors2();
2104
2140
  init_errorUtil();
2105
2141
  init_parseUtil();
2106
2142
  init_util();
@@ -5015,7 +5051,7 @@ __export(exports_external, {
5015
5051
  BRAND: () => BRAND
5016
5052
  });
5017
5053
  var init_external = __esm(() => {
5018
- init_errors();
5054
+ init_errors2();
5019
5055
  init_parseUtil();
5020
5056
  init_typeAliases();
5021
5057
  init_util();
@@ -5531,6 +5567,52 @@ var init_recordings = __esm(() => {
5531
5567
  init_database();
5532
5568
  });
5533
5569
 
5570
+ // src/db/projects.ts
5571
+ function parseProject2(row) {
5572
+ return {
5573
+ id: row["id"],
5574
+ name: row["name"],
5575
+ path: row["path"],
5576
+ description: row["description"] || null,
5577
+ created_at: row["created_at"],
5578
+ updated_at: row["updated_at"]
5579
+ };
5580
+ }
5581
+ function registerProject2(name, path, description, db) {
5582
+ const d = db || getDatabase();
5583
+ const now = new Date().toISOString();
5584
+ const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
5585
+ if (existing) {
5586
+ d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
5587
+ return getProject2(existing["id"], d);
5588
+ }
5589
+ const id = crypto.randomUUID();
5590
+ d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
5591
+ return getProject2(id, d);
5592
+ }
5593
+ function getProject2(idOrPath, db) {
5594
+ const d = db || getDatabase();
5595
+ let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
5596
+ if (!row) {
5597
+ row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
5598
+ }
5599
+ if (!row) {
5600
+ row = d.query("SELECT * FROM projects WHERE name = ?").get(idOrPath);
5601
+ }
5602
+ if (!row && idOrPath) {
5603
+ row = d.query("SELECT * FROM projects WHERE id LIKE ? || '%'").get(idOrPath);
5604
+ }
5605
+ return row ? parseProject2(row) : null;
5606
+ }
5607
+ function listProjects2(db) {
5608
+ const d = db || getDatabase();
5609
+ const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
5610
+ return rows.map(parseProject2);
5611
+ }
5612
+ var init_projects = __esm(() => {
5613
+ init_database();
5614
+ });
5615
+
5534
5616
  // src/db/agents.ts
5535
5617
  function parseAgent2(row) {
5536
5618
  return {
@@ -5584,51 +5666,20 @@ function setAgentFocus2(idOrName, projectId, db) {
5584
5666
  const agent = getAgent2(idOrName, d);
5585
5667
  if (!agent)
5586
5668
  return null;
5587
- d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(projectId, new Date().toISOString(), agent.id);
5669
+ let resolvedProjectId = null;
5670
+ if (projectId) {
5671
+ const project = getProject2(projectId, d);
5672
+ if (!project)
5673
+ throw new ProjectNotFoundError(projectId);
5674
+ resolvedProjectId = project.id;
5675
+ }
5676
+ d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
5588
5677
  return getAgent2(agent.id, d);
5589
5678
  }
5590
5679
  var init_agents = __esm(() => {
5591
5680
  init_database();
5592
- });
5593
-
5594
- // src/db/projects.ts
5595
- function parseProject2(row) {
5596
- return {
5597
- id: row["id"],
5598
- name: row["name"],
5599
- path: row["path"],
5600
- description: row["description"] || null,
5601
- created_at: row["created_at"],
5602
- updated_at: row["updated_at"]
5603
- };
5604
- }
5605
- function registerProject2(name, path, description, db) {
5606
- const d = db || getDatabase();
5607
- const now = new Date().toISOString();
5608
- const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
5609
- if (existing) {
5610
- d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
5611
- return getProject2(existing["id"], d);
5612
- }
5613
- const id = crypto.randomUUID();
5614
- d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
5615
- return getProject2(id, d);
5616
- }
5617
- function getProject2(idOrPath, db) {
5618
- const d = db || getDatabase();
5619
- let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
5620
- if (!row) {
5621
- row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
5622
- }
5623
- return row ? parseProject2(row) : null;
5624
- }
5625
- function listProjects2(db) {
5626
- const d = db || getDatabase();
5627
- const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
5628
- return rows.map(parseProject2);
5629
- }
5630
- var init_projects = __esm(() => {
5631
- init_database();
5681
+ init_projects();
5682
+ init_errors();
5632
5683
  });
5633
5684
 
5634
5685
  // src/db/feedback.ts
@@ -6014,8 +6065,16 @@ function apiStore(client) {
6014
6065
  const res = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/focus`, { project_id: projectId });
6015
6066
  return res ? unwrap(res, "agent") : null;
6016
6067
  } catch (error2) {
6017
- if (error2 && typeof error2 === "object" && error2.status === 404)
6018
- return null;
6068
+ if (error2 && typeof error2 === "object") {
6069
+ const status = error2.status;
6070
+ if (status === 404)
6071
+ return null;
6072
+ if (status === 400) {
6073
+ const body = error2.body;
6074
+ const msg = body && typeof body === "object" && typeof body.error === "string" ? body.error : "invalid focus request";
6075
+ throw new Error(msg);
6076
+ }
6077
+ }
6019
6078
  throw error2;
6020
6079
  }
6021
6080
  },
@@ -12,7 +12,9 @@
12
12
  * stubs — every operation executes real SQL.
13
13
  */
14
14
  import type { PgAdapterAsync } from "../db/remote-storage.js";
15
+ import { ProjectNotFoundError } from "../db/errors.js";
15
16
  import type { Recording, CreateRecordingInput, RecordingFilter, Agent, Project } from "../types/index.js";
17
+ export { ProjectNotFoundError };
16
18
  export declare function createRecording(pg: PgAdapterAsync, input: CreateRecordingInput): Promise<Recording>;
17
19
  export declare function getRecording(pg: PgAdapterAsync, id: string): Promise<Recording | null>;
18
20
  export declare function listRecordings(pg: PgAdapterAsync, filter?: RecordingFilter): Promise<Recording[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/server/repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/server/repo.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAkCA;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAuKtF"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAkCA;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA6KtF"}
package/dist/storage.js CHANGED
@@ -495,6 +495,59 @@ function getRecordingStats(db) {
495
495
  };
496
496
  }
497
497
 
498
+ // src/db/projects.ts
499
+ function parseProject(row) {
500
+ return {
501
+ id: row["id"],
502
+ name: row["name"],
503
+ path: row["path"],
504
+ description: row["description"] || null,
505
+ created_at: row["created_at"],
506
+ updated_at: row["updated_at"]
507
+ };
508
+ }
509
+ function registerProject(name, path, description, db) {
510
+ const d = db || getDatabase();
511
+ const now = new Date().toISOString();
512
+ const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
513
+ if (existing) {
514
+ d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
515
+ return getProject(existing["id"], d);
516
+ }
517
+ const id = crypto.randomUUID();
518
+ d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
519
+ return getProject(id, d);
520
+ }
521
+ function getProject(idOrPath, db) {
522
+ const d = db || getDatabase();
523
+ let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
524
+ if (!row) {
525
+ row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
526
+ }
527
+ if (!row) {
528
+ row = d.query("SELECT * FROM projects WHERE name = ?").get(idOrPath);
529
+ }
530
+ if (!row && idOrPath) {
531
+ row = d.query("SELECT * FROM projects WHERE id LIKE ? || '%'").get(idOrPath);
532
+ }
533
+ return row ? parseProject(row) : null;
534
+ }
535
+ function listProjects(db) {
536
+ const d = db || getDatabase();
537
+ const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
538
+ return rows.map(parseProject);
539
+ }
540
+
541
+ // src/db/errors.ts
542
+ class ProjectNotFoundError extends Error {
543
+ ref;
544
+ constructor(ref) {
545
+ super(`project not found: ${ref}`);
546
+ this.name = "ProjectNotFoundError";
547
+ this.ref = ref;
548
+ }
549
+ }
550
+
498
551
  // src/db/agents.ts
499
552
  function parseAgent(row) {
500
553
  return {
@@ -548,49 +601,19 @@ function setAgentFocus(idOrName, projectId, db) {
548
601
  const agent = getAgent(idOrName, d);
549
602
  if (!agent)
550
603
  return null;
551
- d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(projectId, new Date().toISOString(), agent.id);
604
+ let resolvedProjectId = null;
605
+ if (projectId) {
606
+ const project = getProject(projectId, d);
607
+ if (!project)
608
+ throw new ProjectNotFoundError(projectId);
609
+ resolvedProjectId = project.id;
610
+ }
611
+ d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
552
612
  return getAgent(agent.id, d);
553
613
  }
554
614
 
555
- // src/db/projects.ts
556
- function parseProject(row) {
557
- return {
558
- id: row["id"],
559
- name: row["name"],
560
- path: row["path"],
561
- description: row["description"] || null,
562
- created_at: row["created_at"],
563
- updated_at: row["updated_at"]
564
- };
565
- }
566
- function registerProject(name, path, description, db) {
567
- const d = db || getDatabase();
568
- const now = new Date().toISOString();
569
- const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
570
- if (existing) {
571
- d.query("UPDATE projects SET updated_at = ? WHERE id = ?").run(now, existing["id"]);
572
- return getProject(existing["id"], d);
573
- }
574
- const id = crypto.randomUUID();
575
- d.query("INSERT INTO projects (id, name, path, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, name, path, description || null, now, now);
576
- return getProject(id, d);
577
- }
578
- function getProject(idOrPath, db) {
579
- const d = db || getDatabase();
580
- let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
581
- if (!row) {
582
- row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
583
- }
584
- return row ? parseProject(row) : null;
585
- }
586
- function listProjects(db) {
587
- const d = db || getDatabase();
588
- const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
589
- return rows.map(parseProject);
590
- }
591
-
592
615
  // src/version.ts
593
- var VERSION = "0.2.5";
616
+ var VERSION = "0.2.7";
594
617
 
595
618
  // src/db/feedback.ts
596
619
  function saveFeedback(input) {
@@ -1021,8 +1044,16 @@ function apiStore(client) {
1021
1044
  const res = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/focus`, { project_id: projectId });
1022
1045
  return res ? unwrap(res, "agent") : null;
1023
1046
  } catch (error) {
1024
- if (error && typeof error === "object" && error.status === 404)
1025
- return null;
1047
+ if (error && typeof error === "object") {
1048
+ const status = error.status;
1049
+ if (status === 404)
1050
+ return null;
1051
+ if (status === 400) {
1052
+ const body = error.body;
1053
+ const msg = body && typeof body === "object" && typeof body.error === "string" ? body.error : "invalid focus request";
1054
+ throw new Error(msg);
1055
+ }
1056
+ }
1026
1057
  throw error;
1027
1058
  }
1028
1059
  },
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,KAAK,EACL,OAAO,EACR,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAqC,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGzF,eAAO,MAAM,GAAG,eAAe,CAAC;AAEhC,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAChF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAG7C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClD,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/B,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACxD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAGjF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAGnC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAuKD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,KAAK,CAMzG;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,KAAK,EACL,OAAO,EACR,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAqC,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGzF,eAAO,MAAM,GAAG,eAAe,CAAC;AAEhC,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACjE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAChF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAG7C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClD,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/B,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACxD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAGjF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAGnC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAoLD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,KAAK,CAMzG;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.2.5";
1
+ export declare const VERSION = "0.2.7";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/recordings",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Speech-to-text recording tool with MCP and CLI — records, transcribes, and optionally enhances text using AI",
6
6
  "repository": {