@joeyshi12/casper 0.8.7 → 0.8.9

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.
Files changed (69) hide show
  1. package/dist/mcp.js +1 -1
  2. package/dist/server.js +228 -146
  3. package/dist/web/assets/{abnfDiagram-N423BO3Z-Dhz_IyNe.js → abnfDiagram-N423BO3Z-BmFvzapC.js} +1 -1
  4. package/dist/web/assets/{arc-DzF1URju.js → arc-Cd5BktE5.js} +1 -1
  5. package/dist/web/assets/{architectureDiagram-T3A2C74G-DQSZXIJU.js → architectureDiagram-T3A2C74G-3eW0H6qU.js} +1 -1
  6. package/dist/web/assets/{blockDiagram-VBNYF7ZC-CDuMxsQe.js → blockDiagram-VBNYF7ZC-_xlpEu4Q.js} +1 -1
  7. package/dist/web/assets/{c4Diagram-5PPSVZJV-DdU-Awjg.js → c4Diagram-5PPSVZJV-s96OlA9D.js} +1 -1
  8. package/dist/web/assets/channel-D3Yqmp7Y.js +1 -0
  9. package/dist/web/assets/{chunk-2GRJ4B5K-uH9Ri1ah.js → chunk-2GRJ4B5K-CqX-74PD.js} +1 -1
  10. package/dist/web/assets/{chunk-2Q5K7J3B-C0S0u7jB.js → chunk-2Q5K7J3B-CvIudCSJ.js} +1 -1
  11. package/dist/web/assets/{chunk-5RXB4S5H-DTGoUogm.js → chunk-5RXB4S5H-BJ6lEqCw.js} +1 -1
  12. package/dist/web/assets/{chunk-5VM5RSS4-CypF_LPA.js → chunk-5VM5RSS4-D4J2Xbnp.js} +1 -1
  13. package/dist/web/assets/{chunk-6Q2QTUOP-CpzuxYIb.js → chunk-6Q2QTUOP-v1IZhnXL.js} +1 -1
  14. package/dist/web/assets/{chunk-GF5L2VYU-DTWeiDkK.js → chunk-GF5L2VYU-Bk9156ET.js} +1 -1
  15. package/dist/web/assets/{chunk-JWPE2WC7-BCmZ-kjb.js → chunk-JWPE2WC7-i1A56dGQ.js} +1 -1
  16. package/dist/web/assets/{chunk-KBJHAD2P-BT1wNiLg.js → chunk-KBJHAD2P-CN2i-L5S.js} +1 -1
  17. package/dist/web/assets/{chunk-RYQCIY6F-DvOQ6Jnz.js → chunk-RYQCIY6F-Cj6wUep-.js} +1 -1
  18. package/dist/web/assets/{chunk-XXDRQBXY-BlYpYIGW.js → chunk-XXDRQBXY-BnRenOna.js} +1 -1
  19. package/dist/web/assets/classDiagram-JCYQIIEL-SHf7X5cy.js +1 -0
  20. package/dist/web/assets/classDiagram-v2-OCEON4UE-SHf7X5cy.js +1 -0
  21. package/dist/web/assets/{cose-bilkent-JH36ORCC-CQ7JxfYK.js → cose-bilkent-JH36ORCC-BEfJKlx9.js} +1 -1
  22. package/dist/web/assets/{cynefin-VYW2F7L2-BiVd6im2.js → cynefin-VYW2F7L2-Cv5g-uRn.js} +1 -1
  23. package/dist/web/assets/{cynefinDiagram-MW4NZA55-LMxKG7qE.js → cynefinDiagram-MW4NZA55-DLdyRXdU.js} +1 -1
  24. package/dist/web/assets/{dagre-VZM6K2ZE-BnhWEfsc.js → dagre-VZM6K2ZE-BnInDljp.js} +1 -1
  25. package/dist/web/assets/{diagram-7IWD3JNH-CFw92SVA.js → diagram-7IWD3JNH-DBru5UtC.js} +1 -1
  26. package/dist/web/assets/{diagram-B4RE2ZJO-CdMzf7CM.js → diagram-B4RE2ZJO-h5XgrM6n.js} +1 -1
  27. package/dist/web/assets/{diagram-LBJQPF4R-MI03pa5B.js → diagram-LBJQPF4R-D0nROY9y.js} +1 -1
  28. package/dist/web/assets/{diagram-Q27KOJAE-CpvOmh6o.js → diagram-Q27KOJAE-MlOl8gx1.js} +1 -1
  29. package/dist/web/assets/{diagram-UB23O5K3-DJiB6NV5.js → diagram-UB23O5K3-B7uFVWTV.js} +1 -1
  30. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-BCpOO6pe.js → ebnfDiagram-BXEA7PRR-Dm_lVozw.js} +1 -1
  31. package/dist/web/assets/{erDiagram-JOGREHBK-DMPGC4ou.js → erDiagram-JOGREHBK-CYrJZ_97.js} +1 -1
  32. package/dist/web/assets/{flowDiagram-UKHOOZJN-D7bEx45R.js → flowDiagram-UKHOOZJN-jwUkyKHX.js} +1 -1
  33. package/dist/web/assets/{ganttDiagram-PKOTCBZU-ANJuDxTY.js → ganttDiagram-PKOTCBZU-BLy64t1V.js} +1 -1
  34. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-CZzebS7P.js → gitGraphDiagram-DS77QQ5N-C4d4hAIx.js} +1 -1
  35. package/dist/web/assets/index-BZa7UuXn.js +510 -0
  36. package/dist/web/assets/index-DvasuQ2p.css +1 -0
  37. package/dist/web/assets/{infoDiagram-6WML65LV-xyrLyMcU.js → infoDiagram-6WML65LV-CWuG5oht.js} +1 -1
  38. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-CShD5xD0.js → ishikawaDiagram-WSZJBQD7-CYni8Aaq.js} +1 -1
  39. package/dist/web/assets/{journeyDiagram-NVQOT4AX-mqkt6Et_.js → journeyDiagram-NVQOT4AX-v7cwKNft.js} +1 -1
  40. package/dist/web/assets/{kanban-definition-27J2QSJJ-DXHdm2tc.js → kanban-definition-27J2QSJJ-DWb3cYAv.js} +1 -1
  41. package/dist/web/assets/{linear-CPKrcxzB.js → linear-DpgHYB5J.js} +1 -1
  42. package/dist/web/assets/{mermaid.core-BYWDTTnX.js → mermaid.core-D3Va4lsu.js} +5 -5
  43. package/dist/web/assets/{mindmap-definition-FAOFIHXS-CT2-p6pl.js → mindmap-definition-FAOFIHXS-CGC97dBB.js} +1 -1
  44. package/dist/web/assets/{pegDiagram-VL7TDLO6-CXEnqnZV.js → pegDiagram-VL7TDLO6-CrhjqKA2.js} +1 -1
  45. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-BYYGf1Lu.js → pieDiagram-7S7Q4E2Y-DEtmyKtS.js} +1 -1
  46. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-CH6wC_7O.js → quadrantDiagram-CIZ2JOQS-BkzpFfx5.js} +1 -1
  47. package/dist/web/assets/{railroadDiagram-AXF67PYL-D43iU80e.js → railroadDiagram-AXF67PYL-OKSnYylk.js} +1 -1
  48. package/dist/web/assets/{requirementDiagram-LRYGKXZP-tESkrJke.js → requirementDiagram-LRYGKXZP-DoB0OuhM.js} +1 -1
  49. package/dist/web/assets/{sankeyDiagram-W5VNT64P-D5LUSMYM.js → sankeyDiagram-W5VNT64P-AW-_HJeC.js} +1 -1
  50. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-DRwadHbx.js → sequenceDiagram-SI44F4Z6-D9L9fxlv.js} +1 -1
  51. package/dist/web/assets/{sizeCapture-X5ZJPWSS-Bu18zwx1.js → sizeCapture-X5ZJPWSS-BdSEicdm.js} +1 -1
  52. package/dist/web/assets/{stateDiagram-OKZ733FA-C9LLF02P.js → stateDiagram-OKZ733FA-D67382A0.js} +1 -1
  53. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-Chp2TMP_.js +1 -0
  54. package/dist/web/assets/{swimlanes-SLNWSIFB-D6MmwMwb.js → swimlanes-SLNWSIFB-DgQTxkXQ.js} +2 -2
  55. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-D41_9LPP.js +8 -0
  56. package/dist/web/assets/{timeline-definition-Z64GVDOM-mZ16pSyt.js → timeline-definition-Z64GVDOM-BNjykOiD.js} +1 -1
  57. package/dist/web/assets/{vennDiagram-T6HMQDX7-BTI60Dqz.js → vennDiagram-T6HMQDX7-DM7hKHNd.js} +1 -1
  58. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-D_J4Wkfh.js → wardleyDiagram-T6FBY63Y-D98r0ni6.js} +1 -1
  59. package/dist/web/assets/{xychartDiagram-ELKLHX3M-1iOt_ZR5.js → xychartDiagram-ELKLHX3M-c7t6MgKj.js} +1 -1
  60. package/dist/web/index.html +2 -2
  61. package/dist/web/sw.js +1 -1
  62. package/package.json +1 -1
  63. package/dist/web/assets/channel-KMMRJi94.js +0 -1
  64. package/dist/web/assets/classDiagram-JCYQIIEL-DDQ55f-4.js +0 -1
  65. package/dist/web/assets/classDiagram-v2-OCEON4UE-DDQ55f-4.js +0 -1
  66. package/dist/web/assets/index-CBPuTuU6.js +0 -509
  67. package/dist/web/assets/index-iMxQdvho.css +0 -1
  68. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-UPgcgFV1.js +0 -1
  69. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DjDu_UjY.js +0 -8
package/dist/server.js CHANGED
@@ -319,12 +319,10 @@ var init_config = __esm({
319
319
  */
320
320
  defaultAgent: setting("DEFAULT_AGENT", "defaultAgent", "casper"),
321
321
  /**
322
- * Filesystem root that file-serving endpoints (/api/fs/dirs, /api/fs/image)
323
- * are confined to. Requests resolving outside this root are rejected.
324
- * Defaults to the filesystem root (/), so file browsing spans everything the
325
- * server process can read. Set CASPER_FILE_ROOT to a narrower path (e.g. the
326
- * user's home directory) to stop authenticated users from reading system
327
- * files such as /etc or SSH keys.
322
+ * Filesystem root the file endpoints (/api/fs/dirs, /api/fs/image) are confined to; anything
323
+ * resolving outside it is rejected. Defaults to /, so browsing spans everything the process
324
+ * can read - set a narrower path to keep authenticated users out of system files such as /etc
325
+ * or SSH keys.
328
326
  */
329
327
  fileRoot: path5.resolve(setting("CASPER_FILE_ROOT", "fileRoot", "/")),
330
328
  /** Directory where kiro-cli persists its own session files. */
@@ -704,6 +702,34 @@ var init_rest_dto = __esm({
704
702
  }
705
703
  });
706
704
 
705
+ // shared/dist/titles.js
706
+ function titleFromPrompt(content) {
707
+ const text2 = content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
708
+ const words = stripAttachmentsLine(text2).replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim();
709
+ if (!words)
710
+ return "";
711
+ if (words.length <= MAX_TITLE)
712
+ return words.replace(/[.,;:!?]+$/, "");
713
+ const cut = words.slice(0, MAX_TITLE);
714
+ const lastSpace = cut.lastIndexOf(" ");
715
+ return `${(lastSpace > MAX_TITLE / 2 ? cut.slice(0, lastSpace) : cut).replace(/[.,;:!?]+$/, "")}\u2026`;
716
+ }
717
+ function sanitizeTitle(title) {
718
+ return title.replace(/\s+/g, " ").trim().slice(0, MAX_TITLE);
719
+ }
720
+ function resolveSessionTitle(parts) {
721
+ return sanitizeTitle(parts.override ?? "") || sanitizeTitle(parts.kiroTitle ?? "") || titleFromPrompt(parts.firstPrompt ? [{ type: "text", text: parts.firstPrompt }] : []) || sanitizeTitle(parts.folder ?? "") || UNNAMED_SESSION;
722
+ }
723
+ var MAX_TITLE, UNNAMED_SESSION;
724
+ var init_titles = __esm({
725
+ "shared/dist/titles.js"() {
726
+ "use strict";
727
+ init_attachments();
728
+ MAX_TITLE = 60;
729
+ UNNAMED_SESSION = "New session";
730
+ }
731
+ });
732
+
707
733
  // shared/dist/ws-protocol.js
708
734
  var init_ws_protocol = __esm({
709
735
  "shared/dist/ws-protocol.js"() {
@@ -719,28 +745,55 @@ var init_dist = __esm({
719
745
  init_attachments();
720
746
  init_observability();
721
747
  init_rest_dto();
748
+ init_titles();
722
749
  init_ws_protocol();
723
750
  }
724
751
  });
725
752
 
726
- // server/src/util/paths.ts
753
+ // server/src/session/workspaces.ts
754
+ import crypto3 from "node:crypto";
727
755
  import fs7 from "node:fs";
728
- import fsp from "node:fs/promises";
729
756
  import path8 from "node:path";
757
+ function workspacesRoot() {
758
+ return path8.join(config.casperDataDir, "workspaces");
759
+ }
760
+ function workspaceDir(workspaceId) {
761
+ return path8.join(workspacesRoot(), workspaceId);
762
+ }
763
+ function createWorkspace() {
764
+ const id = crypto3.randomUUID();
765
+ const dir = workspaceDir(id);
766
+ fs7.mkdirSync(dir, { recursive: true, mode: 448 });
767
+ return { id, dir };
768
+ }
769
+ function isWorkspacePath(dir) {
770
+ return dir === workspacesRoot() || dir.startsWith(workspacesRoot() + path8.sep);
771
+ }
772
+ var init_workspaces = __esm({
773
+ "server/src/session/workspaces.ts"() {
774
+ "use strict";
775
+ init_config();
776
+ }
777
+ });
778
+
779
+ // server/src/util/paths.ts
780
+ import fs8 from "node:fs";
781
+ import fsp from "node:fs/promises";
782
+ import path9 from "node:path";
730
783
  function isWithinRoot(root, target) {
731
- const resolved = path8.resolve(target);
732
- if (root === path8.sep) return true;
733
- return resolved === root || resolved.startsWith(root + path8.sep);
784
+ const resolved = path9.resolve(target);
785
+ if (root === path9.sep) return true;
786
+ return resolved === root || resolved.startsWith(root + path9.sep);
734
787
  }
735
788
  function confineToRoot(root, input) {
736
- const resolved = path8.resolve(root, input);
789
+ const resolved = path9.resolve(root, input);
737
790
  return isWithinRoot(root, resolved) ? resolved : null;
738
791
  }
739
792
  function resolveRealRoot(root) {
740
793
  if (realRootCache && realRootCache.root === root) return realRootCache.real;
741
794
  let real;
742
795
  try {
743
- real = fs7.realpathSync(root);
796
+ real = fs8.realpathSync(root);
744
797
  } catch {
745
798
  real = root;
746
799
  }
@@ -1221,8 +1274,8 @@ var init_TurnState = __esm({
1221
1274
  });
1222
1275
 
1223
1276
  // server/src/session/kiroFiles.ts
1224
- import fs8 from "node:fs/promises";
1225
- import path9 from "node:path";
1277
+ import fs9 from "node:fs/promises";
1278
+ import path10 from "node:path";
1226
1279
  function isContentBlock(v) {
1227
1280
  return isRecord(v) && typeof v.kind === "string";
1228
1281
  }
@@ -1259,7 +1312,8 @@ function summarize(j) {
1259
1312
  const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
1260
1313
  return {
1261
1314
  sessionId: j.session_id,
1262
- title: j.title?.trim() || "Untitled session",
1315
+ // Left empty when kiro has not named it; resolveSessionTitle decides what to show.
1316
+ title: j.title?.trim() ?? "",
1263
1317
  cwd: j.cwd,
1264
1318
  createdAt: j.created_at,
1265
1319
  updatedAt: j.updated_at,
@@ -1274,7 +1328,7 @@ function summarize(j) {
1274
1328
  async function listPersistedSessions(log) {
1275
1329
  let files;
1276
1330
  try {
1277
- files = await fs8.readdir(config.kiroSessionsDir);
1331
+ files = await fs9.readdir(config.kiroSessionsDir);
1278
1332
  } catch {
1279
1333
  return [];
1280
1334
  }
@@ -1283,7 +1337,7 @@ async function listPersistedSessions(log) {
1283
1337
  await Promise.all(
1284
1338
  jsonFiles.map(async (f) => {
1285
1339
  try {
1286
- const raw = await fs8.readFile(path9.join(config.kiroSessionsDir, f), "utf8");
1340
+ const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, f), "utf8");
1287
1341
  summaries.push(summarize(JSON.parse(raw)));
1288
1342
  } catch (err) {
1289
1343
  log.debug({ err, f }, "kiroFiles: skipping unreadable session file");
@@ -1296,18 +1350,18 @@ async function listPersistedSessions(log) {
1296
1350
  async function deletePersistedSession(sessionId) {
1297
1351
  if (!isValidSessionId(sessionId)) return;
1298
1352
  const targets = [
1299
- path9.join(config.kiroSessionsDir, `${sessionId}.json`),
1300
- path9.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1301
- path9.join(config.kiroSessionsDir, `${sessionId}.history`),
1302
- path9.join(config.kiroSessionsDir, sessionId)
1353
+ path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1354
+ path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1355
+ path10.join(config.kiroSessionsDir, `${sessionId}.history`),
1356
+ path10.join(config.kiroSessionsDir, sessionId)
1303
1357
  ];
1304
- await Promise.all(targets.map((f) => fs8.rm(f, { recursive: true, force: true })));
1358
+ await Promise.all(targets.map((f) => fs9.rm(f, { recursive: true, force: true })));
1305
1359
  }
1306
1360
  async function readPersistedSession(sessionId) {
1307
1361
  if (!isValidSessionId(sessionId)) return null;
1308
1362
  try {
1309
- const raw = await fs8.readFile(
1310
- path9.join(config.kiroSessionsDir, `${sessionId}.json`),
1363
+ const raw = await fs9.readFile(
1364
+ path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1311
1365
  "utf8"
1312
1366
  );
1313
1367
  return summarize(JSON.parse(raw));
@@ -1319,8 +1373,8 @@ async function hydrateTranscript(sessionId) {
1319
1373
  if (!isValidSessionId(sessionId)) return [];
1320
1374
  let raw;
1321
1375
  try {
1322
- raw = await fs8.readFile(
1323
- path9.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1376
+ raw = await fs9.readFile(
1377
+ path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1324
1378
  "utf8"
1325
1379
  );
1326
1380
  } catch {
@@ -1435,17 +1489,17 @@ var init_sessionStore = __esm({
1435
1489
  });
1436
1490
 
1437
1491
  // server/src/session/SessionManager.ts
1438
- import fs9 from "node:fs";
1439
- import path10 from "node:path";
1492
+ import fs10 from "node:fs";
1493
+ import path11 from "node:path";
1440
1494
  function resolveCwd(input) {
1441
1495
  const raw = input?.trim();
1442
- const abs = raw ? path10.resolve(config.defaultCwd, raw) : config.defaultCwd;
1496
+ const abs = raw ? path11.resolve(config.defaultCwd, raw) : config.defaultCwd;
1443
1497
  if (!isWithinRoot(config.fileRoot, abs)) {
1444
1498
  throw new Error(`Working directory is outside the allowed root: ${abs}`);
1445
1499
  }
1446
1500
  let stat;
1447
1501
  try {
1448
- stat = fs9.statSync(abs);
1502
+ stat = fs10.statSync(abs);
1449
1503
  } catch {
1450
1504
  stat = void 0;
1451
1505
  }
@@ -1453,7 +1507,7 @@ function resolveCwd(input) {
1453
1507
  throw new Error(`Working directory path is a file, not a directory: ${abs}`);
1454
1508
  }
1455
1509
  if (!stat) {
1456
- fs9.mkdirSync(abs, { recursive: true });
1510
+ fs10.mkdirSync(abs, { recursive: true });
1457
1511
  }
1458
1512
  return abs;
1459
1513
  }
@@ -1479,12 +1533,17 @@ function mapNotification(n) {
1479
1533
  return null;
1480
1534
  }
1481
1535
  }
1536
+ function firstPromptText(transcript) {
1537
+ const first = transcript.find((it) => it.type === "message" && it.message.role === "user");
1538
+ return first?.type === "message" ? first.message.text : void 0;
1539
+ }
1482
1540
  var Session, TRANSCRIPT_PAGE_SIZE, SessionManager;
1483
1541
  var init_SessionManager = __esm({
1484
1542
  "server/src/session/SessionManager.ts"() {
1485
1543
  "use strict";
1486
1544
  init_dist();
1487
1545
  init_config();
1546
+ init_workspaces();
1488
1547
  init_paths2();
1489
1548
  init_KiroProcess();
1490
1549
  init_EventStore();
@@ -1500,7 +1559,8 @@ var init_SessionManager = __esm({
1500
1559
  modelId;
1501
1560
  currentModeId;
1502
1561
  availableModes = [];
1503
- title = "New session";
1562
+ /** Empty until something names it; resolveSessionTitle owns the fallback. */
1563
+ title = "";
1504
1564
  createdAt = (/* @__PURE__ */ new Date()).toISOString();
1505
1565
  updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1506
1566
  lastActivity = Date.now();
@@ -1544,24 +1604,30 @@ var init_SessionManager = __esm({
1544
1604
  constructor(log) {
1545
1605
  this.log = log;
1546
1606
  }
1607
+ /**
1608
+ * What a session is called, from every read path. The folder is skipped for a workspace of
1609
+ * ours, whose name is a uuid.
1610
+ */
1611
+ titleOf(sessionId, parts) {
1612
+ return resolveSessionTitle({
1613
+ override: this.store.getTitle(sessionId),
1614
+ kiroTitle: parts.kiroTitle,
1615
+ firstPrompt: parts.firstPrompt,
1616
+ folder: isWorkspacePath(parts.cwd) ? void 0 : path11.basename(parts.cwd)
1617
+ });
1618
+ }
1547
1619
  /** Set a user title override for a session. */
1548
1620
  renameSession(sessionId, title) {
1549
- this.store.setTitle(sessionId, title);
1621
+ const clean = sanitizeTitle(title);
1622
+ this.store.setTitle(sessionId, clean);
1550
1623
  const s = this.sessions.get(sessionId);
1551
- if (s) s.title = title.trim() || s.title;
1624
+ if (s) s.title = clean || s.title;
1552
1625
  }
1553
1626
  /**
1554
- * Re-point a session at a different working directory. A session's cwd is
1555
- * fixed at creation, so this is needed when the original folder was moved or
1556
- * deleted (the workspace endpoints and the kiro process are scoped to it).
1557
- *
1558
- * The target is validated and created exactly like a new session's cwd, so a
1559
- * folder is only created on this explicit action - never silently on resume.
1560
- * Any live kiro process was spawned with the old cwd, so it's disposed here;
1561
- * the next turn respawns in the new directory with the transcript intact
1562
- * (kiro reloads the session from its own file).
1563
- *
1564
- * Returns the resolved absolute path.
1627
+ * Re-point a session at a different working directory, for when the original was moved
1628
+ * or deleted. Validates and creates the target exactly like a new session's cwd, so a
1629
+ * folder is only created on this explicit action. Any live process was spawned with the
1630
+ * old cwd, so it is disposed and the next turn respawns in the new one, transcript intact.
1565
1631
  */
1566
1632
  async setSessionCwd(sessionId, input) {
1567
1633
  const resolved = resolveCwd(input);
@@ -1709,7 +1775,7 @@ var init_SessionManager = __esm({
1709
1775
  // -------------------------------------------------------------------------
1710
1776
  /** Create a new session. Spawns immediately so we get a real kiro sessionId. */
1711
1777
  async createSession(opts) {
1712
- const cwd = resolveCwd(opts.cwd);
1778
+ const cwd = opts.freshWorkspace ? createWorkspace().dir : resolveCwd(opts.cwd);
1713
1779
  const tempId = `pending-${Date.now()}-${Math.floor(this.sessions.size)}`;
1714
1780
  const store = new EventStore(tempId);
1715
1781
  const s = new Session(tempId, store, cwd);
@@ -1723,10 +1789,10 @@ var init_SessionManager = __esm({
1723
1789
  this.evict(s.sessionId);
1724
1790
  throw err;
1725
1791
  }
1726
- const folder = path10.basename(s.cwd);
1727
- if (folder) {
1728
- this.store.setTitle(s.sessionId, folder);
1729
- s.title = folder;
1792
+ const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path11.basename(s.cwd));
1793
+ if (name) {
1794
+ this.store.setTitle(s.sessionId, name);
1795
+ s.title = name;
1730
1796
  }
1731
1797
  return this.buildDetail(s, []);
1732
1798
  }
@@ -1739,6 +1805,13 @@ var init_SessionManager = __esm({
1739
1805
  if (s.running) throw new Error("A turn is already running for this session");
1740
1806
  s.running = true;
1741
1807
  s.lastActivity = Date.now();
1808
+ if (!this.store.getTitle(s.sessionId)) {
1809
+ const title = titleFromPrompt(content);
1810
+ if (title) {
1811
+ this.store.setTitle(s.sessionId, title);
1812
+ s.title = title;
1813
+ }
1814
+ }
1742
1815
  s.record({ kind: "turn_started", prompt: content });
1743
1816
  proc.prompt({ sessionId: s.sessionId, prompt: content }).then((res) => s.record({ kind: "turn_ended", stopReason: res.stopReason })).catch((err) => {
1744
1817
  this.log.error({ err, sessionId: s.sessionId }, "prompt turn failed");
@@ -1787,7 +1860,10 @@ ${tail}` : err.message;
1787
1860
  for (const p of persisted) {
1788
1861
  byId.set(p.sessionId, {
1789
1862
  ...p,
1790
- title: this.store.getTitle(p.sessionId) ?? p.title,
1863
+ title: this.titleOf(p.sessionId, {
1864
+ kiroTitle: p.title,
1865
+ cwd: this.store.getCwd(p.sessionId) ?? p.cwd
1866
+ }),
1791
1867
  cwd: this.store.getCwd(p.sessionId) ?? p.cwd
1792
1868
  });
1793
1869
  }
@@ -1796,7 +1872,7 @@ ${tail}` : err.message;
1796
1872
  const base = byId.get(s.sessionId);
1797
1873
  byId.set(s.sessionId, {
1798
1874
  sessionId: s.sessionId,
1799
- title: this.store.getTitle(s.sessionId) ?? base?.title ?? s.title,
1875
+ title: this.titleOf(s.sessionId, { kiroTitle: base?.title, cwd: s.cwd }),
1800
1876
  cwd: s.cwd,
1801
1877
  createdAt: base?.createdAt ?? s.createdAt,
1802
1878
  updatedAt: base?.updatedAt ?? s.updatedAt,
@@ -1819,7 +1895,11 @@ ${tail}` : err.message;
1819
1895
  return {
1820
1896
  summary: {
1821
1897
  ...persisted,
1822
- title: this.store.getTitle(sessionId) ?? persisted.title,
1898
+ title: this.titleOf(sessionId, {
1899
+ kiroTitle: persisted.title,
1900
+ firstPrompt: firstPromptText(transcript),
1901
+ cwd: this.store.getCwd(sessionId) ?? persisted.cwd
1902
+ }),
1823
1903
  cwd: this.store.getCwd(sessionId) ?? persisted.cwd
1824
1904
  },
1825
1905
  modes: [],
@@ -1847,12 +1927,14 @@ ${tail}` : err.message;
1847
1927
  }
1848
1928
  buildDetail(s, transcript) {
1849
1929
  const snap = s.turnState.get();
1850
- const firstMessage = transcript.find((it) => it.type === "message");
1851
- const firstText = firstMessage?.type === "message" ? firstMessage.message.text : void 0;
1852
1930
  return {
1853
1931
  summary: {
1854
1932
  sessionId: s.sessionId,
1855
- title: this.store.getTitle(s.sessionId) ?? (firstText?.slice(0, 60) || s.title),
1933
+ title: this.titleOf(s.sessionId, {
1934
+ kiroTitle: s.title,
1935
+ firstPrompt: firstPromptText(transcript),
1936
+ cwd: s.cwd
1937
+ }),
1856
1938
  cwd: s.cwd,
1857
1939
  createdAt: s.createdAt,
1858
1940
  updatedAt: s.updatedAt,
@@ -1873,12 +1955,10 @@ ${tail}` : err.message;
1873
1955
  };
1874
1956
  }
1875
1957
  /**
1876
- * The cursor a reconnecting client should start from. Normally the store
1877
- * head, but while a turn is in flight the prompt/response are not yet in
1878
- * kiro's persisted jsonl (kiro writes a turn only when it completes), so the
1879
- * hydrated transcript is missing them. Rewind to just before the in-flight
1880
- * turn_started so the WS replays the whole in-flight turn. Guarded against
1881
- * duplication in case kiro ever persists the prompt at turn start.
1958
+ * The cursor a reconnecting client should start from. Normally the store head, but kiro writes
1959
+ * a turn to its jsonl only once the turn completes, so a hydrated transcript is missing one in
1960
+ * flight. Rewind to just before its turn_started and let the WS replay the whole turn. Guarded
1961
+ * against duplication in case kiro ever persists the prompt at turn start.
1882
1962
  */
1883
1963
  replayHead(s, transcript) {
1884
1964
  const head = s.store.head();
@@ -1983,13 +2063,13 @@ var init_rateLimit = __esm({
1983
2063
  });
1984
2064
 
1985
2065
  // server/src/routes/auth.ts
1986
- import crypto3 from "node:crypto";
2066
+ import crypto4 from "node:crypto";
1987
2067
  import fastifyCookie from "@fastify/cookie";
1988
2068
  function secretMatches(supplied, expected) {
1989
2069
  if (typeof supplied !== "string") return false;
1990
- const a = crypto3.createHash("sha256").update(supplied).digest();
1991
- const b = crypto3.createHash("sha256").update(expected).digest();
1992
- return crypto3.timingSafeEqual(a, b);
2070
+ const a = crypto4.createHash("sha256").update(supplied).digest();
2071
+ const b = crypto4.createHash("sha256").update(expected).digest();
2072
+ return crypto4.timingSafeEqual(a, b);
1993
2073
  }
1994
2074
  function extractToken(req) {
1995
2075
  const auth = req.headers["authorization"];
@@ -2193,9 +2273,9 @@ var init_agents2 = __esm({
2193
2273
  });
2194
2274
 
2195
2275
  // server/src/routes/fs.ts
2196
- import fs10 from "node:fs/promises";
2276
+ import fs11 from "node:fs/promises";
2197
2277
  import { createReadStream } from "node:fs";
2198
- import path11 from "node:path";
2278
+ import path12 from "node:path";
2199
2279
  function confinedPath(input) {
2200
2280
  return confineToRoot(config.fileRoot, input) ?? confineToRoot(config.casperDataDir, input);
2201
2281
  }
@@ -2206,10 +2286,10 @@ function registerFsRoutes(app) {
2206
2286
  const input = (req.query.path ?? "").trim();
2207
2287
  const base = config.defaultCwd;
2208
2288
  const endsWithSep = input.endsWith("/");
2209
- const resolved = input ? path11.resolve(base, input) : base;
2210
- const dir = endsWithSep || !input ? resolved : path11.dirname(resolved);
2211
- const prefix = endsWithSep || !input ? "" : path11.basename(resolved);
2212
- const targetKind = await fs10.stat(resolved).then((s) => s.isDirectory() ? "directory" : "file").catch(() => "missing");
2289
+ const resolved = input ? path12.resolve(base, input) : base;
2290
+ const dir = endsWithSep || !input ? resolved : path12.dirname(resolved);
2291
+ const prefix = endsWithSep || !input ? "" : path12.basename(resolved);
2292
+ const targetKind = await fs11.stat(resolved).then((s) => s.isDirectory() ? "directory" : "file").catch(() => "missing");
2213
2293
  if (confinedPath(dir) === null) {
2214
2294
  reply.code(403);
2215
2295
  return { error: "Path outside allowed root" };
@@ -2220,23 +2300,23 @@ function registerFsRoutes(app) {
2220
2300
  }
2221
2301
  let entries = [];
2222
2302
  try {
2223
- const dirents = await fs10.readdir(realDir, { withFileTypes: true });
2303
+ const dirents = await fs11.readdir(realDir, { withFileTypes: true });
2224
2304
  const candidates = dirents.filter((d) => !d.name.startsWith("."));
2225
2305
  const checks2 = await Promise.all(
2226
2306
  candidates.map(async (d) => {
2227
2307
  if (d.isDirectory()) return d.name;
2228
2308
  if (!d.isSymbolicLink()) return null;
2229
- const full = path11.join(realDir, d.name);
2309
+ const full = path12.join(realDir, d.name);
2230
2310
  const real = await realConfineToRoot(config.fileRoot, full);
2231
2311
  if (!real) return null;
2232
2312
  try {
2233
- return (await fs10.stat(real)).isDirectory() ? d.name : null;
2313
+ return (await fs11.stat(real)).isDirectory() ? d.name : null;
2234
2314
  } catch {
2235
2315
  return null;
2236
2316
  }
2237
2317
  })
2238
2318
  );
2239
- entries = checks2.filter((name) => name !== null).filter((name) => name.toLowerCase().startsWith(prefix.toLowerCase())).sort((a, b) => a.localeCompare(b)).slice(0, 20).map((name) => path11.join(dir, name));
2319
+ entries = checks2.filter((name) => name !== null).filter((name) => name.toLowerCase().startsWith(prefix.toLowerCase())).sort((a, b) => a.localeCompare(b)).slice(0, 20).map((name) => path12.join(dir, name));
2240
2320
  } catch {
2241
2321
  entries = [];
2242
2322
  }
@@ -2251,7 +2331,7 @@ function registerFsRoutes(app) {
2251
2331
  reply.code(400);
2252
2332
  return { error: "path parameter is required" };
2253
2333
  }
2254
- if (!path11.isAbsolute(filePath)) {
2334
+ if (!path12.isAbsolute(filePath)) {
2255
2335
  reply.code(400);
2256
2336
  return { error: "path must be absolute" };
2257
2337
  }
@@ -2260,7 +2340,7 @@ function registerFsRoutes(app) {
2260
2340
  reply.code(403);
2261
2341
  return { error: "Path outside allowed root" };
2262
2342
  }
2263
- const ext = path11.extname(resolved).toLowerCase();
2343
+ const ext = path12.extname(resolved).toLowerCase();
2264
2344
  const mime = IMAGE_MIMES[ext];
2265
2345
  if (!mime) {
2266
2346
  reply.code(400);
@@ -2273,7 +2353,7 @@ function registerFsRoutes(app) {
2273
2353
  }
2274
2354
  let stat;
2275
2355
  try {
2276
- stat = await fs10.stat(real);
2356
+ stat = await fs11.stat(real);
2277
2357
  } catch {
2278
2358
  reply.code(404);
2279
2359
  return { error: "File not found" };
@@ -2332,7 +2412,9 @@ function registerSessionRoutes(app, manager) {
2332
2412
  return await manager.createSession({
2333
2413
  cwd: body.cwd,
2334
2414
  agentId: body.agentId,
2335
- modelId: body.modelId
2415
+ modelId: body.modelId,
2416
+ freshWorkspace: body.freshWorkspace,
2417
+ title: body.title
2336
2418
  });
2337
2419
  } catch (err) {
2338
2420
  reply.code(400);
@@ -2429,7 +2511,7 @@ var init_sessions = __esm({
2429
2511
  });
2430
2512
 
2431
2513
  // server/src/util/filekind.ts
2432
- import path12 from "node:path";
2514
+ import path13 from "node:path";
2433
2515
  function mimeForExt(ext) {
2434
2516
  return MIME_MAP[ext.toLowerCase()] ?? "application/octet-stream";
2435
2517
  }
@@ -2449,7 +2531,7 @@ function looksBinary(buf) {
2449
2531
  return control / buf.length > 0.3;
2450
2532
  }
2451
2533
  function classifyKind(name) {
2452
- const ext = path12.extname(name).toLowerCase();
2534
+ const ext = path13.extname(name).toLowerCase();
2453
2535
  if (ext in IMAGE_EXTS2) return "image";
2454
2536
  if (TEXT_EXTS.has(ext)) return "text";
2455
2537
  return "binary";
@@ -2559,9 +2641,9 @@ var init_filekind = __esm({
2559
2641
  });
2560
2642
 
2561
2643
  // server/src/routes/workspace.ts
2562
- import fs11 from "node:fs/promises";
2644
+ import fs12 from "node:fs/promises";
2563
2645
  import { createReadStream as createReadStream2 } from "node:fs";
2564
- import path13 from "node:path";
2646
+ import path14 from "node:path";
2565
2647
  function tooLargeForPreview(size, maxBytes) {
2566
2648
  return {
2567
2649
  error: `File too large for preview (${(size / 1024 / 1024).toFixed(1)} MB, max ${maxBytes / 1024 / 1024} MB)`
@@ -2606,7 +2688,7 @@ function registerWorkspaceRoutes(app, manager) {
2606
2688
  }
2607
2689
  const workspaceMissing = async () => {
2608
2690
  try {
2609
- return !(await fs11.stat(cwd)).isDirectory();
2691
+ return !(await fs12.stat(cwd)).isDirectory();
2610
2692
  } catch {
2611
2693
  return true;
2612
2694
  }
@@ -2621,7 +2703,7 @@ function registerWorkspaceRoutes(app, manager) {
2621
2703
  }
2622
2704
  let dirents;
2623
2705
  try {
2624
- dirents = await fs11.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
2706
+ dirents = await fs12.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
2625
2707
  } catch {
2626
2708
  return notFound();
2627
2709
  }
@@ -2629,14 +2711,14 @@ function registerWorkspaceRoutes(app, manager) {
2629
2711
  for (const d of dirents) {
2630
2712
  const name = d.name;
2631
2713
  const entryRelative = relative ? `${relative}/${name}` : name;
2632
- const entryAbsolute = path13.join(realTarget, name);
2714
+ const entryAbsolute = path14.join(realTarget, name);
2633
2715
  let kind = d.isDirectory() ? "directory" : d.isFile() ? "file" : null;
2634
2716
  let statTarget = entryAbsolute;
2635
2717
  if (kind === null && d.isSymbolicLink()) {
2636
2718
  const real = await realConfineToRoot(config.fileRoot, entryAbsolute);
2637
2719
  if (!real) continue;
2638
2720
  try {
2639
- const st = await fs11.stat(real);
2721
+ const st = await fs12.stat(real);
2640
2722
  kind = st.isDirectory() ? "directory" : st.isFile() ? "file" : null;
2641
2723
  statTarget = real;
2642
2724
  } catch {
@@ -2648,7 +2730,7 @@ function registerWorkspaceRoutes(app, manager) {
2648
2730
  entries.push({ name, path: entryRelative, type: "directory" });
2649
2731
  } else {
2650
2732
  try {
2651
- const stat = await fs11.stat(statTarget);
2733
+ const stat = await fs12.stat(statTarget);
2652
2734
  entries.push({
2653
2735
  name,
2654
2736
  path: entryRelative,
@@ -2695,7 +2777,7 @@ function registerWorkspaceRoutes(app, manager) {
2695
2777
  }
2696
2778
  let stat;
2697
2779
  try {
2698
- stat = await fs11.stat(realTarget);
2780
+ stat = await fs12.stat(realTarget);
2699
2781
  } catch {
2700
2782
  reply.code(404);
2701
2783
  return { error: "File not found" };
@@ -2708,8 +2790,8 @@ function registerWorkspaceRoutes(app, manager) {
2708
2790
  reply.code(413);
2709
2791
  return { error: `File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB, max 100 MB)` };
2710
2792
  }
2711
- const ext = path13.extname(realTarget);
2712
- const filename = path13.basename(realTarget);
2793
+ const ext = path14.extname(realTarget);
2794
+ const filename = path14.basename(realTarget);
2713
2795
  const asciiName = filename.replace(/[^\w.\-]/g, "_");
2714
2796
  reply.header("Content-Type", mimeForExt(ext));
2715
2797
  reply.header(
@@ -2747,7 +2829,7 @@ function registerWorkspaceRoutes(app, manager) {
2747
2829
  }
2748
2830
  let stat;
2749
2831
  try {
2750
- stat = await fs11.stat(realTarget);
2832
+ stat = await fs12.stat(realTarget);
2751
2833
  } catch {
2752
2834
  reply.code(404);
2753
2835
  return { error: "File not found" };
@@ -2756,7 +2838,7 @@ function registerWorkspaceRoutes(app, manager) {
2756
2838
  reply.code(400);
2757
2839
  return { error: "Path is not a file" };
2758
2840
  }
2759
- const ext = path13.extname(realTarget).toLowerCase();
2841
+ const ext = path14.extname(realTarget).toLowerCase();
2760
2842
  const mime = mimeForExt(ext);
2761
2843
  const isImage = mime.startsWith("image/");
2762
2844
  const isPdf = mime === "application/pdf";
@@ -2801,7 +2883,7 @@ function registerWorkspaceRoutes(app, manager) {
2801
2883
  if (kind === "binary") {
2802
2884
  let fh;
2803
2885
  try {
2804
- fh = await fs11.open(realTarget, "r");
2886
+ fh = await fs12.open(realTarget, "r");
2805
2887
  const buf = Buffer.alloc(Math.min(HEXDUMP_BYTES, stat.size));
2806
2888
  const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
2807
2889
  const head = buf.subarray(0, bytesRead);
@@ -2851,36 +2933,36 @@ var init_workspace = __esm({
2851
2933
 
2852
2934
  // server/src/routes/uploads.ts
2853
2935
  import { createReadStream as createReadStream3, createWriteStream } from "node:fs";
2854
- import fs12 from "node:fs/promises";
2855
- import path14 from "node:path";
2856
- import crypto4 from "node:crypto";
2936
+ import fs13 from "node:fs/promises";
2937
+ import path15 from "node:path";
2938
+ import crypto5 from "node:crypto";
2857
2939
  import { execFile as execFile3 } from "node:child_process";
2858
2940
  import { pipeline } from "node:stream/promises";
2859
2941
  function uploadDirFor(sessionId) {
2860
- return path14.join(config.casperDataDir, "sessions", sessionId, "uploads");
2942
+ return path15.join(config.casperDataDir, "sessions", sessionId, "uploads");
2861
2943
  }
2862
2944
  function sanitizeName(raw) {
2863
- const base = path14.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
2945
+ const base = path15.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
2864
2946
  if (!base || base === "." || base === "..") return `upload-${Date.now()}`;
2865
2947
  return base.replace(/^\.+/, "") || `upload-${Date.now()}`;
2866
2948
  }
2867
2949
  async function uniquePath(dir, name) {
2868
- const ext = path14.extname(name);
2950
+ const ext = path15.extname(name);
2869
2951
  const stem = name.slice(0, name.length - ext.length);
2870
2952
  let candidate = name;
2871
2953
  let n = 1;
2872
2954
  while (true) {
2873
2955
  try {
2874
- await fs12.access(path14.join(dir, candidate));
2956
+ await fs13.access(path15.join(dir, candidate));
2875
2957
  candidate = `${stem} (${n++})${ext}`;
2876
2958
  } catch {
2877
- return path14.join(dir, candidate);
2959
+ return path15.join(dir, candidate);
2878
2960
  }
2879
2961
  }
2880
2962
  }
2881
2963
  function sha256File(absPath) {
2882
2964
  return new Promise((resolve, reject) => {
2883
- const hash = crypto4.createHash("sha256");
2965
+ const hash = crypto5.createHash("sha256");
2884
2966
  createReadStream3(absPath).on("data", (d) => hash.update(d)).on("end", () => resolve(hash.digest("hex"))).on("error", reject);
2885
2967
  });
2886
2968
  }
@@ -2894,7 +2976,7 @@ function fileType(absPath) {
2894
2976
  async function sampleStrings(absPath, limit = 40) {
2895
2977
  let fh;
2896
2978
  try {
2897
- fh = await fs12.open(absPath, "r");
2979
+ fh = await fs13.open(absPath, "r");
2898
2980
  const buf = Buffer.alloc(STRINGS_SCAN_BYTES);
2899
2981
  const { bytesRead } = await fh.read(buf, 0, STRINGS_SCAN_BYTES, 0);
2900
2982
  const out = [];
@@ -2932,7 +3014,7 @@ function registerUploadRoutes(app, manager) {
2932
3014
  return { error: "Invalid session id" };
2933
3015
  }
2934
3016
  const uploadDir = uploadDirFor(req.params.id);
2935
- await fs12.mkdir(uploadDir, { recursive: true, mode: 448 });
3017
+ await fs13.mkdir(uploadDir, { recursive: true, mode: 448 });
2936
3018
  const results = [];
2937
3019
  const parts = req.files();
2938
3020
  for await (const part of parts) {
@@ -2946,17 +3028,17 @@ function registerUploadRoutes(app, manager) {
2946
3028
  return { error: `Failed to store ${name}` };
2947
3029
  }
2948
3030
  if (part.file.truncated) {
2949
- await fs12.rm(dest, { force: true });
3031
+ await fs13.rm(dest, { force: true });
2950
3032
  reply.code(413);
2951
3033
  return {
2952
3034
  error: `${name} exceeds the max upload size (${Math.round(config.maxUploadBytes / 1024 / 1024)} MB)`
2953
3035
  };
2954
3036
  }
2955
- const stat = await fs12.stat(dest);
2956
- const ext = path14.extname(dest);
3037
+ const stat = await fs13.stat(dest);
3038
+ const ext = path15.extname(dest);
2957
3039
  const kind = classifyKind(dest);
2958
3040
  const uploaded = {
2959
- name: path14.basename(dest),
3041
+ name: path15.basename(dest),
2960
3042
  // Absolute, because the file sits outside the session's workspace: a
2961
3043
  // relative path would resolve against the wrong root for both the agent
2962
3044
  // and the image endpoint.
@@ -3103,7 +3185,7 @@ var init_dispatch = __esm({
3103
3185
  });
3104
3186
 
3105
3187
  // server/src/ws/dirWatchers.ts
3106
- import fs13 from "node:fs";
3188
+ import fs14 from "node:fs";
3107
3189
  function diffWatchSet(current, next) {
3108
3190
  const have = new Set(current);
3109
3191
  const want = new Set(next.slice(0, MAX_WATCHES));
@@ -3127,7 +3209,7 @@ function createDirWatchers(opts) {
3127
3209
  const target = await opts.resolve(relative);
3128
3210
  if (!target || closed) return;
3129
3211
  try {
3130
- const watcher = fs13.watch(target, { persistent: false }, () => {
3212
+ const watcher = fs14.watch(target, { persistent: false }, () => {
3131
3213
  const existing = timers.get(relative);
3132
3214
  if (existing) clearTimeout(existing);
3133
3215
  timers.set(
@@ -3193,7 +3275,7 @@ function registerWsGateway(app, manager) {
3193
3275
  return null;
3194
3276
  }
3195
3277
  },
3196
- onChange: (path17) => send(socket, { type: "fs_changed", path: path17 })
3278
+ onChange: (path18) => send(socket, { type: "fs_changed", path: path18 })
3197
3279
  });
3198
3280
  let alive = true;
3199
3281
  let ready = false;
@@ -3278,7 +3360,7 @@ var init_gateway = __esm({
3278
3360
  });
3279
3361
 
3280
3362
  // server/src/app.ts
3281
- import fs14 from "node:fs";
3363
+ import fs15 from "node:fs";
3282
3364
  import Fastify from "fastify";
3283
3365
  import cors from "@fastify/cors";
3284
3366
  import websocket from "@fastify/websocket";
@@ -3316,14 +3398,14 @@ async function buildApp() {
3316
3398
  registerWorkspaceRoutes(app, manager);
3317
3399
  registerUploadRoutes(app, manager);
3318
3400
  registerWsGateway(app, manager);
3319
- if (fs14.existsSync(config.webDist)) {
3401
+ if (fs15.existsSync(config.webDist)) {
3320
3402
  await app.register(fastifyStatic, {
3321
3403
  root: config.webDist
3322
3404
  });
3323
3405
  app.setNotFoundHandler((req, reply) => {
3324
- const path17 = req.url.split("?")[0] ?? "";
3325
- const looksLikeAsset = path17.startsWith("/assets/") || /\.[a-zA-Z0-9]+$/.test(path17);
3326
- if (path17.startsWith("/api/") || path17.startsWith("/ws") || looksLikeAsset) {
3406
+ const path18 = req.url.split("?")[0] ?? "";
3407
+ const looksLikeAsset = path18.startsWith("/assets/") || /\.[a-zA-Z0-9]+$/.test(path18);
3408
+ if (path18.startsWith("/api/") || path18.startsWith("/ws") || looksLikeAsset) {
3327
3409
  reply.code(404).send({ error: "Not found" });
3328
3410
  return;
3329
3411
  }
@@ -3397,15 +3479,15 @@ __export(doctor_exports, {
3397
3479
  mcpWiring: () => mcpWiring
3398
3480
  });
3399
3481
  import { execFileSync as execFileSync3 } from "node:child_process";
3400
- import fs15 from "node:fs";
3401
- import path15 from "node:path";
3482
+ import fs16 from "node:fs";
3483
+ import path16 from "node:path";
3402
3484
  function which(bin) {
3403
- if (bin.includes("/")) return fs15.existsSync(bin) ? bin : void 0;
3404
- for (const dir of (process.env.PATH ?? "").split(path15.delimiter)) {
3485
+ if (bin.includes("/")) return fs16.existsSync(bin) ? bin : void 0;
3486
+ for (const dir of (process.env.PATH ?? "").split(path16.delimiter)) {
3405
3487
  if (dir === "") continue;
3406
- const candidate = path15.join(dir, bin);
3488
+ const candidate = path16.join(dir, bin);
3407
3489
  try {
3408
- fs15.accessSync(candidate, fs15.constants.X_OK);
3490
+ fs16.accessSync(candidate, fs16.constants.X_OK);
3409
3491
  return candidate;
3410
3492
  } catch {
3411
3493
  }
@@ -3429,7 +3511,7 @@ function mcpWiring(agentJson, resolve) {
3429
3511
  return { ok: false, detail: `${command || "(no command)"} not found - widgets are unavailable` };
3430
3512
  }
3431
3513
  const script = args.find((a) => a.endsWith(".js"));
3432
- if (script && !fs15.existsSync(script)) {
3514
+ if (script && !fs16.existsSync(script)) {
3433
3515
  return { ok: false, detail: `${script} is gone - delete the agent file and restart to rewrite it` };
3434
3516
  }
3435
3517
  return { ok: true, detail: script ?? `${command} ${args.join(" ")}` };
@@ -3461,7 +3543,7 @@ function checks() {
3461
3543
  const settings = readSettings(config.configFile);
3462
3544
  const hasToken = typeof settings.token === "string" && settings.token !== "";
3463
3545
  out.push(
3464
- fs15.existsSync(config.configFile) ? {
3546
+ fs16.existsSync(config.configFile) ? {
3465
3547
  ok: hasToken,
3466
3548
  name: "settings",
3467
3549
  detail: hasToken ? `${config.configFile} (token set)` : `${config.configFile} exists but has no token - auth is DISABLED`
@@ -3471,8 +3553,8 @@ function checks() {
3471
3553
  detail: `${config.configFile} not found; a token is generated on first run`
3472
3554
  }
3473
3555
  );
3474
- if (fs15.existsSync(config.configFile)) {
3475
- const mode = fs15.statSync(config.configFile).mode & 511;
3556
+ if (fs16.existsSync(config.configFile)) {
3557
+ const mode = fs16.statSync(config.configFile).mode & 511;
3476
3558
  out.push({
3477
3559
  ok: mode === 384,
3478
3560
  name: "settings mode",
@@ -3480,10 +3562,10 @@ function checks() {
3480
3562
  });
3481
3563
  }
3482
3564
  try {
3483
- fs15.mkdirSync(config.casperDataDir, { recursive: true });
3484
- fs15.accessSync(config.casperDataDir, fs15.constants.W_OK);
3485
- const db2 = path15.join(config.casperDataDir, "casper.db");
3486
- const size = fs15.existsSync(db2) ? `, casper.db ${(fs15.statSync(db2).size / 1024).toFixed(0)} KB` : "";
3565
+ fs16.mkdirSync(config.casperDataDir, { recursive: true });
3566
+ fs16.accessSync(config.casperDataDir, fs16.constants.W_OK);
3567
+ const db2 = path16.join(config.casperDataDir, "casper.db");
3568
+ const size = fs16.existsSync(db2) ? `, casper.db ${(fs16.statSync(db2).size / 1024).toFixed(0)} KB` : "";
3487
3569
  out.push({ ok: true, name: "data dir", detail: `${config.casperDataDir} writable${size}` });
3488
3570
  } catch (err) {
3489
3571
  out.push({
@@ -3494,22 +3576,22 @@ function checks() {
3494
3576
  });
3495
3577
  }
3496
3578
  out.push(
3497
- fs15.existsSync(config.webDist) ? { ok: true, name: "web app", detail: config.webDist } : {
3579
+ fs16.existsSync(config.webDist) ? { ok: true, name: "web app", detail: config.webDist } : {
3498
3580
  ok: false,
3499
3581
  fatal: true,
3500
3582
  name: "web app",
3501
3583
  detail: `${config.webDist} not found - the package is incomplete or CASPER_WEB_DIST is wrong`
3502
3584
  }
3503
3585
  );
3504
- const agent = path15.join(config.kiroSessionsDir, "..", "..", "agents", "casper.json");
3505
- const hasAgent = fs15.existsSync(agent);
3586
+ const agent = path16.join(config.kiroSessionsDir, "..", "..", "agents", "casper.json");
3587
+ const hasAgent = fs16.existsSync(agent);
3506
3588
  out.push({
3507
3589
  ok: hasAgent,
3508
3590
  name: "casper agent",
3509
3591
  detail: hasAgent ? agent : `${agent} missing - it is installed on first run`
3510
3592
  });
3511
3593
  if (hasAgent) {
3512
- const wiring = mcpWiring(fs15.readFileSync(agent, "utf8"), which);
3594
+ const wiring = mcpWiring(fs16.readFileSync(agent, "utf8"), which);
3513
3595
  out.push({ ok: wiring.ok, name: "casper mcp", detail: wiring.detail });
3514
3596
  }
3515
3597
  return out;
@@ -3973,14 +4055,14 @@ var init_server2 = __esm({
3973
4055
  "server/src/mcp/server.ts"() {
3974
4056
  "use strict";
3975
4057
  init_protocol();
3976
- VERSION = true ? "0.8.7" : "dev";
4058
+ VERSION = true ? "0.8.9" : "dev";
3977
4059
  }
3978
4060
  });
3979
4061
 
3980
4062
  // server/src/cli/program.ts
3981
- import fs16 from "node:fs";
4063
+ import fs17 from "node:fs";
3982
4064
  import os4 from "node:os";
3983
- import path16 from "node:path";
4065
+ import path17 from "node:path";
3984
4066
 
3985
4067
  // server/node_modules/commander/lib/error.js
3986
4068
  var CommanderError = class extends Error {
@@ -7093,9 +7175,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7093
7175
  * @param {string} [path]
7094
7176
  * @return {(string|null|Command)}
7095
7177
  */
7096
- executableDir(path17) {
7097
- if (path17 === void 0) return this._executableDir;
7098
- this._executableDir = path17;
7178
+ executableDir(path18) {
7179
+ if (path18 === void 0) return this._executableDir;
7180
+ this._executableDir = path18;
7099
7181
  return this;
7100
7182
  }
7101
7183
  /**
@@ -7353,7 +7435,7 @@ var program = new Command();
7353
7435
  // server/src/cli/program.ts
7354
7436
  init_paths();
7355
7437
  init_settings();
7356
- var VERSION2 = true ? "0.8.7" : "dev";
7438
+ var VERSION2 = true ? "0.8.9" : "dev";
7357
7439
  async function bootstrap() {
7358
7440
  const file = configFilePath();
7359
7441
  const settings = readSettings(file);
@@ -7399,8 +7481,8 @@ async function resetToken(value) {
7399
7481
  updateSettings(file, { token });
7400
7482
  process.stdout.write(`wrote a new token to ${file}
7401
7483
  `);
7402
- const dbFile = path16.join(dataDirPath(), "casper.db");
7403
- if (fs16.existsSync(dbFile)) {
7484
+ const dbFile = path17.join(dataDirPath(), "casper.db");
7485
+ if (fs17.existsSync(dbFile)) {
7404
7486
  const { LoginStore: LoginStore2 } = await Promise.resolve().then(() => (init_logins(), logins_exports));
7405
7487
  new LoginStore2().revokeAll();
7406
7488
  process.stdout.write("revoked all device sessions\n");