@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.
- package/dist/mcp.js +1 -1
- package/dist/server.js +228 -146
- package/dist/web/assets/{abnfDiagram-N423BO3Z-Dhz_IyNe.js → abnfDiagram-N423BO3Z-BmFvzapC.js} +1 -1
- package/dist/web/assets/{arc-DzF1URju.js → arc-Cd5BktE5.js} +1 -1
- package/dist/web/assets/{architectureDiagram-T3A2C74G-DQSZXIJU.js → architectureDiagram-T3A2C74G-3eW0H6qU.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-CDuMxsQe.js → blockDiagram-VBNYF7ZC-_xlpEu4Q.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-DdU-Awjg.js → c4Diagram-5PPSVZJV-s96OlA9D.js} +1 -1
- package/dist/web/assets/channel-D3Yqmp7Y.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-uH9Ri1ah.js → chunk-2GRJ4B5K-CqX-74PD.js} +1 -1
- package/dist/web/assets/{chunk-2Q5K7J3B-C0S0u7jB.js → chunk-2Q5K7J3B-CvIudCSJ.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-DTGoUogm.js → chunk-5RXB4S5H-BJ6lEqCw.js} +1 -1
- package/dist/web/assets/{chunk-5VM5RSS4-CypF_LPA.js → chunk-5VM5RSS4-D4J2Xbnp.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-CpzuxYIb.js → chunk-6Q2QTUOP-v1IZhnXL.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-DTWeiDkK.js → chunk-GF5L2VYU-Bk9156ET.js} +1 -1
- package/dist/web/assets/{chunk-JWPE2WC7-BCmZ-kjb.js → chunk-JWPE2WC7-i1A56dGQ.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-BT1wNiLg.js → chunk-KBJHAD2P-CN2i-L5S.js} +1 -1
- package/dist/web/assets/{chunk-RYQCIY6F-DvOQ6Jnz.js → chunk-RYQCIY6F-Cj6wUep-.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-BlYpYIGW.js → chunk-XXDRQBXY-BnRenOna.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-SHf7X5cy.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-SHf7X5cy.js +1 -0
- package/dist/web/assets/{cose-bilkent-JH36ORCC-CQ7JxfYK.js → cose-bilkent-JH36ORCC-BEfJKlx9.js} +1 -1
- package/dist/web/assets/{cynefin-VYW2F7L2-BiVd6im2.js → cynefin-VYW2F7L2-Cv5g-uRn.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-LMxKG7qE.js → cynefinDiagram-MW4NZA55-DLdyRXdU.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-BnhWEfsc.js → dagre-VZM6K2ZE-BnInDljp.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-CFw92SVA.js → diagram-7IWD3JNH-DBru5UtC.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-CdMzf7CM.js → diagram-B4RE2ZJO-h5XgrM6n.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-MI03pa5B.js → diagram-LBJQPF4R-D0nROY9y.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-CpvOmh6o.js → diagram-Q27KOJAE-MlOl8gx1.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-DJiB6NV5.js → diagram-UB23O5K3-B7uFVWTV.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-BCpOO6pe.js → ebnfDiagram-BXEA7PRR-Dm_lVozw.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-DMPGC4ou.js → erDiagram-JOGREHBK-CYrJZ_97.js} +1 -1
- package/dist/web/assets/{flowDiagram-UKHOOZJN-D7bEx45R.js → flowDiagram-UKHOOZJN-jwUkyKHX.js} +1 -1
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-ANJuDxTY.js → ganttDiagram-PKOTCBZU-BLy64t1V.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-CZzebS7P.js → gitGraphDiagram-DS77QQ5N-C4d4hAIx.js} +1 -1
- package/dist/web/assets/index-BZa7UuXn.js +510 -0
- package/dist/web/assets/index-DvasuQ2p.css +1 -0
- package/dist/web/assets/{infoDiagram-6WML65LV-xyrLyMcU.js → infoDiagram-6WML65LV-CWuG5oht.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-CShD5xD0.js → ishikawaDiagram-WSZJBQD7-CYni8Aaq.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-mqkt6Et_.js → journeyDiagram-NVQOT4AX-v7cwKNft.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DXHdm2tc.js → kanban-definition-27J2QSJJ-DWb3cYAv.js} +1 -1
- package/dist/web/assets/{linear-CPKrcxzB.js → linear-DpgHYB5J.js} +1 -1
- package/dist/web/assets/{mermaid.core-BYWDTTnX.js → mermaid.core-D3Va4lsu.js} +5 -5
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-CT2-p6pl.js → mindmap-definition-FAOFIHXS-CGC97dBB.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-CXEnqnZV.js → pegDiagram-VL7TDLO6-CrhjqKA2.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-BYYGf1Lu.js → pieDiagram-7S7Q4E2Y-DEtmyKtS.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-CH6wC_7O.js → quadrantDiagram-CIZ2JOQS-BkzpFfx5.js} +1 -1
- package/dist/web/assets/{railroadDiagram-AXF67PYL-D43iU80e.js → railroadDiagram-AXF67PYL-OKSnYylk.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-tESkrJke.js → requirementDiagram-LRYGKXZP-DoB0OuhM.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-D5LUSMYM.js → sankeyDiagram-W5VNT64P-AW-_HJeC.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-DRwadHbx.js → sequenceDiagram-SI44F4Z6-D9L9fxlv.js} +1 -1
- package/dist/web/assets/{sizeCapture-X5ZJPWSS-Bu18zwx1.js → sizeCapture-X5ZJPWSS-BdSEicdm.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-C9LLF02P.js → stateDiagram-OKZ733FA-D67382A0.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-Chp2TMP_.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-D6MmwMwb.js → swimlanes-SLNWSIFB-DgQTxkXQ.js} +2 -2
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-D41_9LPP.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-mZ16pSyt.js → timeline-definition-Z64GVDOM-BNjykOiD.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-BTI60Dqz.js → vennDiagram-T6HMQDX7-DM7hKHNd.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-D_J4Wkfh.js → wardleyDiagram-T6FBY63Y-D98r0ni6.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-1iOt_ZR5.js → xychartDiagram-ELKLHX3M-c7t6MgKj.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/assets/channel-KMMRJi94.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-DDQ55f-4.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DDQ55f-4.js +0 -1
- package/dist/web/assets/index-CBPuTuU6.js +0 -509
- package/dist/web/assets/index-iMxQdvho.css +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-UPgcgFV1.js +0 -1
- 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
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
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/
|
|
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 =
|
|
732
|
-
if (root ===
|
|
733
|
-
return resolved === root || resolved.startsWith(root +
|
|
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 =
|
|
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 =
|
|
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
|
|
1225
|
-
import
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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) =>
|
|
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
|
|
1310
|
-
|
|
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
|
|
1323
|
-
|
|
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
|
|
1439
|
-
import
|
|
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 ?
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1621
|
+
const clean = sanitizeTitle(title);
|
|
1622
|
+
this.store.setTitle(sessionId, clean);
|
|
1550
1623
|
const s = this.sessions.get(sessionId);
|
|
1551
|
-
if (s) s.title =
|
|
1624
|
+
if (s) s.title = clean || s.title;
|
|
1552
1625
|
}
|
|
1553
1626
|
/**
|
|
1554
|
-
* Re-point a session at a different working directory
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
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
|
|
1727
|
-
if (
|
|
1728
|
-
this.store.setTitle(s.sessionId,
|
|
1729
|
-
s.title =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
1878
|
-
*
|
|
1879
|
-
*
|
|
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
|
|
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 =
|
|
1991
|
-
const b =
|
|
1992
|
-
return
|
|
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
|
|
2276
|
+
import fs11 from "node:fs/promises";
|
|
2197
2277
|
import { createReadStream } from "node:fs";
|
|
2198
|
-
import
|
|
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 ?
|
|
2210
|
-
const dir = endsWithSep || !input ? resolved :
|
|
2211
|
-
const prefix = endsWithSep || !input ? "" :
|
|
2212
|
-
const targetKind = await
|
|
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
|
|
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 =
|
|
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
|
|
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) =>
|
|
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 (!
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
2644
|
+
import fs12 from "node:fs/promises";
|
|
2563
2645
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
2564
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
2712
|
-
const filename =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
2855
|
-
import
|
|
2856
|
-
import
|
|
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
|
|
2942
|
+
return path15.join(config.casperDataDir, "sessions", sessionId, "uploads");
|
|
2861
2943
|
}
|
|
2862
2944
|
function sanitizeName(raw) {
|
|
2863
|
-
const base =
|
|
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 =
|
|
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
|
|
2956
|
+
await fs13.access(path15.join(dir, candidate));
|
|
2875
2957
|
candidate = `${stem} (${n++})${ext}`;
|
|
2876
2958
|
} catch {
|
|
2877
|
-
return
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2956
|
-
const ext =
|
|
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:
|
|
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
|
|
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 =
|
|
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: (
|
|
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
|
|
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 (
|
|
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
|
|
3325
|
-
const looksLikeAsset =
|
|
3326
|
-
if (
|
|
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
|
|
3401
|
-
import
|
|
3482
|
+
import fs16 from "node:fs";
|
|
3483
|
+
import path16 from "node:path";
|
|
3402
3484
|
function which(bin) {
|
|
3403
|
-
if (bin.includes("/")) return
|
|
3404
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
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 =
|
|
3488
|
+
const candidate = path16.join(dir, bin);
|
|
3407
3489
|
try {
|
|
3408
|
-
|
|
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 && !
|
|
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
|
-
|
|
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 (
|
|
3475
|
-
const mode =
|
|
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
|
-
|
|
3484
|
-
|
|
3485
|
-
const db2 =
|
|
3486
|
-
const size =
|
|
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
|
-
|
|
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 =
|
|
3505
|
-
const hasAgent =
|
|
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(
|
|
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.
|
|
4058
|
+
VERSION = true ? "0.8.9" : "dev";
|
|
3977
4059
|
}
|
|
3978
4060
|
});
|
|
3979
4061
|
|
|
3980
4062
|
// server/src/cli/program.ts
|
|
3981
|
-
import
|
|
4063
|
+
import fs17 from "node:fs";
|
|
3982
4064
|
import os4 from "node:os";
|
|
3983
|
-
import
|
|
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(
|
|
7097
|
-
if (
|
|
7098
|
-
this._executableDir =
|
|
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.
|
|
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 =
|
|
7403
|
-
if (
|
|
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");
|