code-squad-cli 1.2.16 → 1.2.17
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/flip/index.js +159 -36
- package/dist/flip/routes/cancel.d.ts +3 -0
- package/dist/flip/routes/cancel.js +10 -7
- package/dist/flip/routes/changes.d.ts +8 -0
- package/dist/flip/routes/changes.js +26 -0
- package/dist/flip/routes/file.js +23 -4
- package/dist/flip/routes/files.js +28 -7
- package/dist/flip/routes/git.js +30 -9
- package/dist/flip/routes/ping.d.ts +6 -0
- package/dist/flip/routes/ping.js +14 -0
- package/dist/flip/routes/session.d.ts +14 -0
- package/dist/flip/routes/session.js +54 -0
- package/dist/flip/routes/submit.js +8 -6
- package/dist/flip/server/Server.d.ts +40 -6
- package/dist/flip/server/Server.js +151 -46
- package/dist/flip/session/SessionManager.d.ts +69 -0
- package/dist/flip/session/SessionManager.js +163 -0
- package/dist/flip-ui/dist/assets/{index-B2PjDe6F.js → index-KAtdqB2p.js} +58 -58
- package/dist/flip-ui/dist/index.html +1 -1
- package/dist/index.js +597 -151
- package/package.json +1 -1
- package/dist/flip/events/SSEManager.d.ts +0 -10
- package/dist/flip/events/SSEManager.js +0 -28
- package/dist/flip/events/types.d.ts +0 -11
- package/dist/flip/events/types.js +0 -1
- package/dist/flip/routes/events.d.ts +0 -3
- package/dist/flip/routes/events.js +0 -26
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// dist/index.js
|
|
4
4
|
import * as path12 from "path";
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
5
|
+
import * as fs11 from "fs";
|
|
6
|
+
import * as os5 from "os";
|
|
7
7
|
import * as crypto from "crypto";
|
|
8
8
|
import chalk2 from "chalk";
|
|
9
9
|
|
|
@@ -533,18 +533,36 @@ function collectFlatFiles(rootPath, currentPath, maxDepth, depth = 0, result = {
|
|
|
533
533
|
}
|
|
534
534
|
return result;
|
|
535
535
|
}
|
|
536
|
+
function getCwdFromSession(req, res) {
|
|
537
|
+
const sessionId = req.query.session_id;
|
|
538
|
+
if (!sessionId) {
|
|
539
|
+
res.status(400).json({ error: "Missing session_id parameter" });
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
const sessionManager = req.app.locals.sessionManager;
|
|
543
|
+
const session = sessionManager.getSession(sessionId);
|
|
544
|
+
if (!session) {
|
|
545
|
+
res.status(404).json({ error: "Session not found" });
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
return session.cwd;
|
|
549
|
+
}
|
|
536
550
|
router.get("/", (req, res) => {
|
|
537
|
-
const
|
|
538
|
-
|
|
551
|
+
const cwd = getCwdFromSession(req, res);
|
|
552
|
+
if (!cwd)
|
|
553
|
+
return;
|
|
554
|
+
const tree = buildFileTree(cwd, cwd, 10);
|
|
539
555
|
const response = {
|
|
540
|
-
root:
|
|
556
|
+
root: cwd,
|
|
541
557
|
tree
|
|
542
558
|
};
|
|
543
559
|
res.json(response);
|
|
544
560
|
});
|
|
545
561
|
router.get("/flat", (req, res) => {
|
|
546
|
-
const
|
|
547
|
-
|
|
562
|
+
const cwd = getCwdFromSession(req, res);
|
|
563
|
+
if (!cwd)
|
|
564
|
+
return;
|
|
565
|
+
const result = collectFlatFiles(cwd, cwd, 10);
|
|
548
566
|
result.files.sort((a, b) => a.path.localeCompare(b.path));
|
|
549
567
|
result.filteredDirs.sort();
|
|
550
568
|
let recentFile = null;
|
|
@@ -686,16 +704,32 @@ function detectLanguage(filePath) {
|
|
|
686
704
|
}
|
|
687
705
|
return "text";
|
|
688
706
|
}
|
|
707
|
+
function getCwdFromSession2(req, res) {
|
|
708
|
+
const sessionId = req.query.session_id;
|
|
709
|
+
if (!sessionId) {
|
|
710
|
+
res.status(400).json({ error: "Missing session_id parameter" });
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const sessionManager = req.app.locals.sessionManager;
|
|
714
|
+
const session = sessionManager.getSession(sessionId);
|
|
715
|
+
if (!session) {
|
|
716
|
+
res.status(404).json({ error: "Session not found" });
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
return session.cwd;
|
|
720
|
+
}
|
|
689
721
|
router2.get("/", (req, res) => {
|
|
690
|
-
const
|
|
722
|
+
const cwd = getCwdFromSession2(req, res);
|
|
723
|
+
if (!cwd)
|
|
724
|
+
return;
|
|
691
725
|
const relativePath = req.query.path;
|
|
692
726
|
if (!relativePath) {
|
|
693
727
|
res.status(400).json({ error: "Missing path parameter" });
|
|
694
728
|
return;
|
|
695
729
|
}
|
|
696
|
-
const filePath = path3.join(
|
|
730
|
+
const filePath = path3.join(cwd, relativePath);
|
|
697
731
|
const resolvedPath = path3.resolve(filePath);
|
|
698
|
-
const resolvedCwd = path3.resolve(
|
|
732
|
+
const resolvedCwd = path3.resolve(cwd);
|
|
699
733
|
if (!resolvedPath.startsWith(resolvedCwd)) {
|
|
700
734
|
res.status(403).json({ error: "Access denied" });
|
|
701
735
|
return;
|
|
@@ -823,12 +857,28 @@ function createAddedFileDiff(content) {
|
|
|
823
857
|
lines: diffLines
|
|
824
858
|
}];
|
|
825
859
|
}
|
|
860
|
+
function getCwdFromSession3(req, res) {
|
|
861
|
+
const sessionId = req.query.session_id;
|
|
862
|
+
if (!sessionId) {
|
|
863
|
+
res.status(400).json({ error: "Missing session_id parameter" });
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
const sessionManager = req.app.locals.sessionManager;
|
|
867
|
+
const session = sessionManager.getSession(sessionId);
|
|
868
|
+
if (!session) {
|
|
869
|
+
res.status(404).json({ error: "Session not found" });
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
return session.cwd;
|
|
873
|
+
}
|
|
826
874
|
router3.get("/status", (req, res) => {
|
|
827
|
-
const
|
|
875
|
+
const cwd = getCwdFromSession3(req, res);
|
|
876
|
+
if (!cwd)
|
|
877
|
+
return;
|
|
828
878
|
let isGitRepo = false;
|
|
829
879
|
try {
|
|
830
880
|
execSync("git rev-parse --git-dir", {
|
|
831
|
-
cwd
|
|
881
|
+
cwd,
|
|
832
882
|
stdio: "pipe"
|
|
833
883
|
});
|
|
834
884
|
isGitRepo = true;
|
|
@@ -845,7 +895,7 @@ router3.get("/status", (req, res) => {
|
|
|
845
895
|
let unstaged = [];
|
|
846
896
|
try {
|
|
847
897
|
const output = execSync("git status --porcelain", {
|
|
848
|
-
cwd
|
|
898
|
+
cwd,
|
|
849
899
|
encoding: "utf-8"
|
|
850
900
|
});
|
|
851
901
|
unstaged = parseGitStatus(output);
|
|
@@ -858,17 +908,19 @@ router3.get("/status", (req, res) => {
|
|
|
858
908
|
res.json(response);
|
|
859
909
|
});
|
|
860
910
|
router3.get("/diff", (req, res) => {
|
|
861
|
-
const
|
|
911
|
+
const cwd = getCwdFromSession3(req, res);
|
|
912
|
+
if (!cwd)
|
|
913
|
+
return;
|
|
862
914
|
const relativePath = req.query.path;
|
|
863
915
|
if (!relativePath) {
|
|
864
916
|
res.status(400).json({ error: "Missing path parameter" });
|
|
865
917
|
return;
|
|
866
918
|
}
|
|
867
|
-
const fullPath = path4.join(
|
|
919
|
+
const fullPath = path4.join(cwd, relativePath);
|
|
868
920
|
let fileStatus = "modified";
|
|
869
921
|
try {
|
|
870
922
|
const statusOutput = execSync(`git status --porcelain -- "${relativePath}"`, {
|
|
871
|
-
cwd
|
|
923
|
+
cwd,
|
|
872
924
|
encoding: "utf-8"
|
|
873
925
|
});
|
|
874
926
|
if (statusOutput.startsWith("??")) {
|
|
@@ -896,7 +948,7 @@ router3.get("/diff", (req, res) => {
|
|
|
896
948
|
}
|
|
897
949
|
try {
|
|
898
950
|
const diffOutput = execSync(`git diff --no-color -- "${relativePath}"`, {
|
|
899
|
-
cwd
|
|
951
|
+
cwd,
|
|
900
952
|
encoding: "utf-8"
|
|
901
953
|
});
|
|
902
954
|
if (!diffOutput.trim()) {
|
|
@@ -1070,8 +1122,12 @@ async function pasteToOriginalSession(sessionId) {
|
|
|
1070
1122
|
// dist/flip/routes/submit.js
|
|
1071
1123
|
var router4 = Router4();
|
|
1072
1124
|
router4.post("/", async (req, res) => {
|
|
1073
|
-
const
|
|
1125
|
+
const sessionManager = req.app.locals.sessionManager;
|
|
1074
1126
|
const body = req.body;
|
|
1127
|
+
if (!body.session_id) {
|
|
1128
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1075
1131
|
if (!body.items || body.items.length === 0) {
|
|
1076
1132
|
res.status(400).json({ error: "No items to submit" });
|
|
1077
1133
|
return;
|
|
@@ -1101,20 +1157,21 @@ router4.post("/", async (req, res) => {
|
|
|
1101
1157
|
}
|
|
1102
1158
|
}
|
|
1103
1159
|
res.json({ status: "ok" });
|
|
1104
|
-
|
|
1105
|
-
state.resolve(formatted);
|
|
1106
|
-
}
|
|
1160
|
+
await sessionManager.unregisterSession(body.session_id);
|
|
1107
1161
|
});
|
|
1108
1162
|
|
|
1109
1163
|
// dist/flip/routes/cancel.js
|
|
1110
1164
|
import { Router as Router5 } from "express";
|
|
1111
1165
|
var router5 = Router5();
|
|
1112
|
-
router5.post("/", (req, res) => {
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1115
|
-
if (
|
|
1116
|
-
|
|
1166
|
+
router5.post("/", async (req, res) => {
|
|
1167
|
+
const sessionManager = req.app.locals.sessionManager;
|
|
1168
|
+
const body = req.body;
|
|
1169
|
+
if (!body.session_id) {
|
|
1170
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1171
|
+
return;
|
|
1117
1172
|
}
|
|
1173
|
+
res.json({ status: "ok" });
|
|
1174
|
+
await sessionManager.unregisterSession(body.session_id);
|
|
1118
1175
|
});
|
|
1119
1176
|
|
|
1120
1177
|
// dist/flip/routes/static.js
|
|
@@ -1149,28 +1206,98 @@ function createStaticRouter() {
|
|
|
1149
1206
|
return router6;
|
|
1150
1207
|
}
|
|
1151
1208
|
|
|
1152
|
-
// dist/flip/routes/
|
|
1209
|
+
// dist/flip/routes/ping.js
|
|
1153
1210
|
import { Router as Router7 } from "express";
|
|
1154
|
-
function
|
|
1211
|
+
function createPingRouter() {
|
|
1155
1212
|
const router6 = Router7();
|
|
1156
|
-
router6.get("/", (
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
res.
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1213
|
+
router6.get("/", (_req, res) => {
|
|
1214
|
+
const response = {
|
|
1215
|
+
id: SERVER_ID,
|
|
1216
|
+
version: "1.0.0"
|
|
1217
|
+
};
|
|
1218
|
+
res.json(response);
|
|
1219
|
+
});
|
|
1220
|
+
return router6;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// dist/flip/routes/session.js
|
|
1224
|
+
import { Router as Router8 } from "express";
|
|
1225
|
+
function createSessionRouter(sessionManager) {
|
|
1226
|
+
const router6 = Router8();
|
|
1227
|
+
router6.post("/register", (req, res) => {
|
|
1228
|
+
const body = req.body;
|
|
1229
|
+
if (!body.session_id || !body.cwd) {
|
|
1230
|
+
res.status(400).json({ error: "Missing session_id or cwd" });
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
if (!sessionManager.hasSessions && req.app.locals.clearIdleTimer) {
|
|
1234
|
+
req.app.locals.clearIdleTimer();
|
|
1235
|
+
}
|
|
1236
|
+
sessionManager.registerSession(body.session_id, body.cwd);
|
|
1237
|
+
const response = {
|
|
1238
|
+
status: "ok",
|
|
1239
|
+
session_id: body.session_id
|
|
1240
|
+
};
|
|
1241
|
+
res.json(response);
|
|
1242
|
+
});
|
|
1243
|
+
router6.post("/unregister", async (req, res) => {
|
|
1244
|
+
const body = req.body;
|
|
1245
|
+
if (!body.session_id) {
|
|
1246
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
await sessionManager.unregisterSession(body.session_id);
|
|
1250
|
+
const response = {
|
|
1251
|
+
status: "ok"
|
|
1252
|
+
};
|
|
1253
|
+
res.json(response);
|
|
1254
|
+
});
|
|
1255
|
+
router6.get("/info", (req, res) => {
|
|
1256
|
+
const sessionId = req.query.session_id;
|
|
1257
|
+
if (!sessionId) {
|
|
1258
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
const session = sessionManager.getSession(sessionId);
|
|
1262
|
+
if (!session) {
|
|
1263
|
+
res.status(404).json({ error: "Session not found" });
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
res.json({
|
|
1267
|
+
session_id: session.id,
|
|
1268
|
+
cwd: session.cwd,
|
|
1269
|
+
lastActivity: session.lastActivity
|
|
1169
1270
|
});
|
|
1170
1271
|
});
|
|
1171
1272
|
return router6;
|
|
1172
1273
|
}
|
|
1173
1274
|
|
|
1275
|
+
// dist/flip/routes/changes.js
|
|
1276
|
+
import { Router as Router9 } from "express";
|
|
1277
|
+
function createChangesRouter(sessionManager) {
|
|
1278
|
+
const router6 = Router9();
|
|
1279
|
+
router6.get("/", (req, res) => {
|
|
1280
|
+
const sessionId = req.query.session_id;
|
|
1281
|
+
if (!sessionId) {
|
|
1282
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
sessionManager.touchSession(sessionId);
|
|
1286
|
+
const changes = sessionManager.consumePendingChanges(sessionId);
|
|
1287
|
+
if (!changes) {
|
|
1288
|
+
res.status(404).json({ error: "Session not found" });
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
const response = {
|
|
1292
|
+
filesChanged: changes.filesChanged,
|
|
1293
|
+
gitChanged: changes.gitChanged,
|
|
1294
|
+
changedFiles: Array.from(changes.changedFiles)
|
|
1295
|
+
};
|
|
1296
|
+
res.json(response);
|
|
1297
|
+
});
|
|
1298
|
+
return router6;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1174
1301
|
// dist/flip/watcher/FileWatcher.js
|
|
1175
1302
|
import chokidar from "chokidar";
|
|
1176
1303
|
import * as path8 from "path";
|
|
@@ -1294,87 +1421,263 @@ var FileWatcher = class {
|
|
|
1294
1421
|
}
|
|
1295
1422
|
};
|
|
1296
1423
|
|
|
1297
|
-
// dist/flip/
|
|
1298
|
-
var
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1424
|
+
// dist/flip/session/SessionManager.js
|
|
1425
|
+
var log = (...args) => console.error(...args);
|
|
1426
|
+
var SessionManager = class {
|
|
1427
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1428
|
+
options;
|
|
1429
|
+
cleanupInterval = null;
|
|
1430
|
+
constructor(options) {
|
|
1431
|
+
this.options = options;
|
|
1305
1432
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1433
|
+
/**
|
|
1434
|
+
* Start the session cleanup timer
|
|
1435
|
+
*/
|
|
1436
|
+
start() {
|
|
1437
|
+
this.cleanupInterval = setInterval(() => {
|
|
1438
|
+
this.cleanupTimedOutSessions();
|
|
1439
|
+
}, 5e3);
|
|
1308
1440
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Stop the session manager and clean up all sessions
|
|
1443
|
+
*/
|
|
1444
|
+
async stop() {
|
|
1445
|
+
if (this.cleanupInterval) {
|
|
1446
|
+
clearInterval(this.cleanupInterval);
|
|
1447
|
+
this.cleanupInterval = null;
|
|
1448
|
+
}
|
|
1449
|
+
const stopPromises = Array.from(this.sessions.values()).map((session) => session.watcher.stop());
|
|
1450
|
+
await Promise.all(stopPromises);
|
|
1451
|
+
this.sessions.clear();
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Register a new session or update existing one
|
|
1455
|
+
*/
|
|
1456
|
+
registerSession(sessionId, cwd) {
|
|
1457
|
+
const existing = this.sessions.get(sessionId);
|
|
1458
|
+
if (existing) {
|
|
1459
|
+
existing.lastActivity = Date.now();
|
|
1460
|
+
if (existing.cwd !== cwd) {
|
|
1461
|
+
existing.watcher.stop();
|
|
1462
|
+
existing.cwd = cwd;
|
|
1463
|
+
existing.watcher = this.createWatcher(sessionId, cwd);
|
|
1464
|
+
existing.pendingChanges = this.createEmptyPendingChanges();
|
|
1465
|
+
}
|
|
1466
|
+
return existing;
|
|
1467
|
+
}
|
|
1468
|
+
const watcher = this.createWatcher(sessionId, cwd);
|
|
1469
|
+
const session = {
|
|
1470
|
+
id: sessionId,
|
|
1471
|
+
cwd,
|
|
1472
|
+
watcher,
|
|
1473
|
+
lastActivity: Date.now(),
|
|
1474
|
+
pendingChanges: this.createEmptyPendingChanges()
|
|
1475
|
+
};
|
|
1476
|
+
this.sessions.set(sessionId, session);
|
|
1477
|
+
log(`[SessionManager] Session registered: ${sessionId} (cwd: ${cwd})`);
|
|
1478
|
+
return session;
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Get a session by ID and update activity
|
|
1482
|
+
*/
|
|
1483
|
+
getSession(sessionId) {
|
|
1484
|
+
const session = this.sessions.get(sessionId);
|
|
1485
|
+
if (session) {
|
|
1486
|
+
session.lastActivity = Date.now();
|
|
1487
|
+
}
|
|
1488
|
+
return session;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Touch session to update activity timestamp (called on every API request)
|
|
1492
|
+
*/
|
|
1493
|
+
touchSession(sessionId) {
|
|
1494
|
+
const session = this.sessions.get(sessionId);
|
|
1495
|
+
if (session) {
|
|
1496
|
+
session.lastActivity = Date.now();
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Unregister a session (e.g., on cancel/submit)
|
|
1501
|
+
*/
|
|
1502
|
+
async unregisterSession(sessionId) {
|
|
1503
|
+
const session = this.sessions.get(sessionId);
|
|
1504
|
+
if (session) {
|
|
1505
|
+
await session.watcher.stop();
|
|
1506
|
+
this.sessions.delete(sessionId);
|
|
1507
|
+
log(`[SessionManager] Session unregistered: ${sessionId}`);
|
|
1508
|
+
if (this.sessions.size === 0 && this.options.onAllSessionsGone) {
|
|
1509
|
+
this.options.onAllSessionsGone();
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Get pending changes for a session and clear them
|
|
1515
|
+
*/
|
|
1516
|
+
consumePendingChanges(sessionId) {
|
|
1517
|
+
const session = this.sessions.get(sessionId);
|
|
1518
|
+
if (!session)
|
|
1519
|
+
return null;
|
|
1520
|
+
const changes = { ...session.pendingChanges };
|
|
1521
|
+
changes.changedFiles = new Set(session.pendingChanges.changedFiles);
|
|
1522
|
+
session.pendingChanges = this.createEmptyPendingChanges();
|
|
1523
|
+
return changes;
|
|
1317
1524
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1525
|
+
/**
|
|
1526
|
+
* Get number of active sessions
|
|
1527
|
+
*/
|
|
1528
|
+
get sessionCount() {
|
|
1529
|
+
return this.sessions.size;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Check if any sessions exist
|
|
1533
|
+
*/
|
|
1534
|
+
get hasSessions() {
|
|
1535
|
+
return this.sessions.size > 0;
|
|
1536
|
+
}
|
|
1537
|
+
createWatcher(sessionId, cwd) {
|
|
1538
|
+
const watcher = new FileWatcher({ cwd });
|
|
1539
|
+
watcher.onFilesChanged(() => {
|
|
1540
|
+
const session = this.sessions.get(sessionId);
|
|
1541
|
+
if (session) {
|
|
1542
|
+
session.pendingChanges.filesChanged = true;
|
|
1543
|
+
}
|
|
1321
1544
|
});
|
|
1322
|
-
|
|
1545
|
+
watcher.onFileChanged((filePath) => {
|
|
1546
|
+
const session = this.sessions.get(sessionId);
|
|
1547
|
+
if (session) {
|
|
1548
|
+
session.pendingChanges.changedFiles.add(filePath);
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
watcher.onGitChanged(() => {
|
|
1552
|
+
const session = this.sessions.get(sessionId);
|
|
1553
|
+
if (session) {
|
|
1554
|
+
session.pendingChanges.gitChanged = true;
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
watcher.start();
|
|
1558
|
+
return watcher;
|
|
1559
|
+
}
|
|
1560
|
+
createEmptyPendingChanges() {
|
|
1561
|
+
return {
|
|
1562
|
+
filesChanged: false,
|
|
1563
|
+
gitChanged: false,
|
|
1564
|
+
changedFiles: /* @__PURE__ */ new Set()
|
|
1565
|
+
};
|
|
1323
1566
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1567
|
+
cleanupTimedOutSessions() {
|
|
1568
|
+
const now = Date.now();
|
|
1569
|
+
const timedOut = [];
|
|
1570
|
+
for (const [sessionId, session] of this.sessions) {
|
|
1571
|
+
if (now - session.lastActivity > this.options.sessionTimeoutMs) {
|
|
1572
|
+
timedOut.push(sessionId);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
for (const sessionId of timedOut) {
|
|
1576
|
+
log(`[SessionManager] Session timed out: ${sessionId}`);
|
|
1577
|
+
this.unregisterSession(sessionId);
|
|
1578
|
+
}
|
|
1326
1579
|
}
|
|
1327
1580
|
};
|
|
1328
1581
|
|
|
1329
1582
|
// dist/flip/server/Server.js
|
|
1583
|
+
var log2 = (...args) => console.error(...args);
|
|
1584
|
+
var SERVER_ID = "csq-flip";
|
|
1330
1585
|
var Server = class {
|
|
1331
|
-
cwd;
|
|
1332
1586
|
port;
|
|
1333
|
-
|
|
1334
|
-
|
|
1587
|
+
options;
|
|
1588
|
+
app = null;
|
|
1589
|
+
server = null;
|
|
1590
|
+
sessionManager = null;
|
|
1591
|
+
resolveShutdown = null;
|
|
1592
|
+
constructor(port, options = {}) {
|
|
1335
1593
|
this.port = port;
|
|
1594
|
+
this.options = {
|
|
1595
|
+
sessionTimeoutMs: options.sessionTimeoutMs ?? 3e4,
|
|
1596
|
+
idleTimeout: options.idleTimeout ?? 0
|
|
1597
|
+
};
|
|
1336
1598
|
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Start the server and return a promise that resolves when server shuts down
|
|
1601
|
+
*/
|
|
1337
1602
|
async run() {
|
|
1338
1603
|
return new Promise((resolve2) => {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
fileWatcher.onFilesChanged(() => {
|
|
1350
|
-
sseManager.broadcast({ type: "files-changed" });
|
|
1351
|
-
});
|
|
1352
|
-
fileWatcher.onFileChanged((path13) => {
|
|
1353
|
-
sseManager.broadcast({ type: "file-changed", path: path13 });
|
|
1354
|
-
});
|
|
1355
|
-
fileWatcher.onGitChanged(() => {
|
|
1356
|
-
sseManager.broadcast({ type: "git-changed" });
|
|
1604
|
+
this.resolveShutdown = resolve2;
|
|
1605
|
+
this.app = express2();
|
|
1606
|
+
this.app.use(cors());
|
|
1607
|
+
this.app.use(express2.json());
|
|
1608
|
+
this.sessionManager = new SessionManager({
|
|
1609
|
+
sessionTimeoutMs: this.options.sessionTimeoutMs,
|
|
1610
|
+
onAllSessionsGone: () => {
|
|
1611
|
+
log2("[Server] All sessions gone, shutting down...");
|
|
1612
|
+
this.shutdown();
|
|
1613
|
+
}
|
|
1357
1614
|
});
|
|
1358
|
-
|
|
1359
|
-
app.
|
|
1360
|
-
app.use("/api/
|
|
1361
|
-
app.use("/api/
|
|
1362
|
-
app.use("/api/
|
|
1363
|
-
app.use("/api/
|
|
1364
|
-
app.use("/api/
|
|
1365
|
-
app.use(
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1615
|
+
this.sessionManager.start();
|
|
1616
|
+
this.app.locals.sessionManager = this.sessionManager;
|
|
1617
|
+
this.app.use("/api/ping", createPingRouter());
|
|
1618
|
+
this.app.use("/api/session", createSessionRouter(this.sessionManager));
|
|
1619
|
+
this.app.use("/api/changes", createChangesRouter(this.sessionManager));
|
|
1620
|
+
this.app.use("/api/files", router);
|
|
1621
|
+
this.app.use("/api/file", router2);
|
|
1622
|
+
this.app.use("/api/git", router3);
|
|
1623
|
+
this.app.use("/api/submit", router4);
|
|
1624
|
+
this.app.use("/api/cancel", router5);
|
|
1625
|
+
this.app.use(createStaticRouter());
|
|
1626
|
+
this.server = http.createServer(this.app);
|
|
1627
|
+
const signalHandler = () => {
|
|
1628
|
+
log2("\nShutting down...");
|
|
1629
|
+
this.shutdown();
|
|
1372
1630
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1631
|
+
process.on("SIGINT", signalHandler);
|
|
1632
|
+
process.on("SIGTERM", signalHandler);
|
|
1633
|
+
let idleTimer = null;
|
|
1634
|
+
if (this.options.idleTimeout && this.options.idleTimeout > 0) {
|
|
1635
|
+
idleTimer = setTimeout(() => {
|
|
1636
|
+
if (!this.sessionManager?.hasSessions) {
|
|
1637
|
+
log2("\nNo session registered, shutting down...");
|
|
1638
|
+
this.shutdown();
|
|
1639
|
+
}
|
|
1640
|
+
}, this.options.idleTimeout);
|
|
1641
|
+
}
|
|
1642
|
+
this.app.locals.idleTimer = idleTimer;
|
|
1643
|
+
this.app.locals.clearIdleTimer = () => {
|
|
1644
|
+
if (idleTimer) {
|
|
1645
|
+
clearTimeout(idleTimer);
|
|
1646
|
+
idleTimer = null;
|
|
1647
|
+
this.app.locals.idleTimer = null;
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
this.server.listen(this.port, "127.0.0.1", () => {
|
|
1651
|
+
log2(`[Server] Running at http://localhost:${this.port}`);
|
|
1375
1652
|
});
|
|
1376
1653
|
});
|
|
1377
1654
|
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Shutdown the server gracefully
|
|
1657
|
+
*/
|
|
1658
|
+
async shutdown() {
|
|
1659
|
+
if (!this.server)
|
|
1660
|
+
return;
|
|
1661
|
+
if (this.app?.locals.idleTimer) {
|
|
1662
|
+
clearTimeout(this.app.locals.idleTimer);
|
|
1663
|
+
}
|
|
1664
|
+
if (this.sessionManager) {
|
|
1665
|
+
await this.sessionManager.stop();
|
|
1666
|
+
}
|
|
1667
|
+
await new Promise((res) => {
|
|
1668
|
+
this.server.close(() => res());
|
|
1669
|
+
});
|
|
1670
|
+
log2("[Server] Shutdown complete");
|
|
1671
|
+
if (this.resolveShutdown) {
|
|
1672
|
+
this.resolveShutdown();
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Get the session manager
|
|
1677
|
+
*/
|
|
1678
|
+
getSessionManager() {
|
|
1679
|
+
return this.sessionManager;
|
|
1680
|
+
}
|
|
1378
1681
|
};
|
|
1379
1682
|
async function findFreePort(preferred, maxPort = 65535) {
|
|
1380
1683
|
return new Promise((resolve2, reject) => {
|
|
@@ -1395,13 +1698,53 @@ async function findFreePort(preferred, maxPort = 65535) {
|
|
|
1395
1698
|
});
|
|
1396
1699
|
});
|
|
1397
1700
|
}
|
|
1701
|
+
async function isFlipServerRunning(port, timeoutMs = 1e3) {
|
|
1702
|
+
return new Promise((resolve2) => {
|
|
1703
|
+
const req = http.request({
|
|
1704
|
+
hostname: "127.0.0.1",
|
|
1705
|
+
port,
|
|
1706
|
+
path: "/api/ping",
|
|
1707
|
+
method: "GET",
|
|
1708
|
+
timeout: timeoutMs
|
|
1709
|
+
}, (res) => {
|
|
1710
|
+
let data = "";
|
|
1711
|
+
res.on("data", (chunk) => data += chunk);
|
|
1712
|
+
res.on("end", () => {
|
|
1713
|
+
try {
|
|
1714
|
+
const json = JSON.parse(data);
|
|
1715
|
+
resolve2(json.id === SERVER_ID);
|
|
1716
|
+
} catch {
|
|
1717
|
+
resolve2(false);
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
req.on("error", () => resolve2(false));
|
|
1722
|
+
req.on("timeout", () => {
|
|
1723
|
+
req.destroy();
|
|
1724
|
+
resolve2(false);
|
|
1725
|
+
});
|
|
1726
|
+
req.end();
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
async function findExistingServer(startPort, endPort) {
|
|
1730
|
+
for (let port = startPort; port <= endPort; port++) {
|
|
1731
|
+
if (await isFlipServerRunning(port)) {
|
|
1732
|
+
return port;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return null;
|
|
1736
|
+
}
|
|
1398
1737
|
|
|
1399
1738
|
// dist/flip/index.js
|
|
1400
1739
|
import open from "open";
|
|
1401
1740
|
import path9 from "path";
|
|
1741
|
+
import fs8 from "fs";
|
|
1742
|
+
import os3 from "os";
|
|
1743
|
+
import http2 from "http";
|
|
1402
1744
|
import { execSync as execSync2 } from "child_process";
|
|
1403
1745
|
var DEFAULT_PORT = 51234;
|
|
1404
|
-
var
|
|
1746
|
+
var MAX_PORT = 51240;
|
|
1747
|
+
var log3 = (...args) => console.error(...args);
|
|
1405
1748
|
function formatTime() {
|
|
1406
1749
|
const now = /* @__PURE__ */ new Date();
|
|
1407
1750
|
const hours = String(now.getHours()).padStart(2, "0");
|
|
@@ -1409,6 +1752,43 @@ function formatTime() {
|
|
|
1409
1752
|
const secs = String(now.getSeconds()).padStart(2, "0");
|
|
1410
1753
|
return `${hours}:${mins}:${secs}`;
|
|
1411
1754
|
}
|
|
1755
|
+
function getSessionId() {
|
|
1756
|
+
try {
|
|
1757
|
+
const tty = execSync2("tty", { encoding: "utf-8" }).trim();
|
|
1758
|
+
return tty;
|
|
1759
|
+
} catch {
|
|
1760
|
+
return `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
async function registerSession(port, sessionId, cwd) {
|
|
1764
|
+
return new Promise((resolve2) => {
|
|
1765
|
+
const data = JSON.stringify({ session_id: sessionId, cwd });
|
|
1766
|
+
const req = http2.request({
|
|
1767
|
+
hostname: "127.0.0.1",
|
|
1768
|
+
port,
|
|
1769
|
+
path: "/api/session/register",
|
|
1770
|
+
method: "POST",
|
|
1771
|
+
headers: {
|
|
1772
|
+
"Content-Type": "application/json",
|
|
1773
|
+
"Content-Length": Buffer.byteLength(data)
|
|
1774
|
+
},
|
|
1775
|
+
timeout: 5e3
|
|
1776
|
+
}, (res) => {
|
|
1777
|
+
let body = "";
|
|
1778
|
+
res.on("data", (chunk) => body += chunk);
|
|
1779
|
+
res.on("end", () => {
|
|
1780
|
+
resolve2(res.statusCode === 200);
|
|
1781
|
+
});
|
|
1782
|
+
});
|
|
1783
|
+
req.on("error", () => resolve2(false));
|
|
1784
|
+
req.on("timeout", () => {
|
|
1785
|
+
req.destroy();
|
|
1786
|
+
resolve2(false);
|
|
1787
|
+
});
|
|
1788
|
+
req.write(data);
|
|
1789
|
+
req.end();
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1412
1792
|
async function runFlip(args) {
|
|
1413
1793
|
let sessionId;
|
|
1414
1794
|
const sessionIdx = args.indexOf("--session");
|
|
@@ -1449,58 +1829,88 @@ async function runFlip(args) {
|
|
|
1449
1829
|
command = "oneshot";
|
|
1450
1830
|
}
|
|
1451
1831
|
const cwd = pathArg ? path9.resolve(pathArg) : process.cwd();
|
|
1832
|
+
const finalSessionId = sessionId || getSessionId();
|
|
1452
1833
|
switch (command) {
|
|
1453
1834
|
case "setup": {
|
|
1454
1835
|
await setupHotkey();
|
|
1455
1836
|
return;
|
|
1456
1837
|
}
|
|
1457
1838
|
case "serve": {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
log(`[${formatTime()}] Submitted ${result.length} characters`);
|
|
1469
|
-
} else {
|
|
1470
|
-
log(`[${formatTime()}] Cancelled`);
|
|
1471
|
-
}
|
|
1472
|
-
log(`[${formatTime()}] Ready for next session...`);
|
|
1839
|
+
let port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1840
|
+
if (port) {
|
|
1841
|
+
log3(`[${formatTime()}] Found existing server at port ${port}`);
|
|
1842
|
+
} else {
|
|
1843
|
+
port = await findFreePort(DEFAULT_PORT, MAX_PORT);
|
|
1844
|
+
log3(`[${formatTime()}] Starting new server at port ${port}`);
|
|
1845
|
+
const server = new Server(port, { sessionTimeoutMs: 4e3 });
|
|
1846
|
+
server.run().catch((err) => {
|
|
1847
|
+
log3(`[${formatTime()}] Server error:`, err);
|
|
1848
|
+
});
|
|
1473
1849
|
}
|
|
1850
|
+
log3(`[${formatTime()}] Server running at http://localhost:${port}`);
|
|
1851
|
+
log3("Press Ctrl+C to stop");
|
|
1852
|
+
log3("");
|
|
1853
|
+
log3("To open browser, run: csq flip open");
|
|
1854
|
+
log3(`Or use hotkey to open: open http://localhost:${port}`);
|
|
1855
|
+
await new Promise(() => {
|
|
1856
|
+
});
|
|
1474
1857
|
}
|
|
1475
1858
|
case "open": {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1859
|
+
const port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1860
|
+
if (!port) {
|
|
1861
|
+
log3("No csq flip server running.");
|
|
1862
|
+
log3("Start with: csq flip serve");
|
|
1863
|
+
log3("Or use: csq flip (one-shot mode)");
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
if (!await registerSession(port, finalSessionId, cwd)) {
|
|
1867
|
+
log3("Failed to register session with server");
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
const url = `http://localhost:${port}?session=${encodeURIComponent(finalSessionId)}`;
|
|
1871
|
+
log3(`Opening ${url} in browser...`);
|
|
1478
1872
|
try {
|
|
1479
1873
|
await open(url);
|
|
1480
1874
|
} catch (e) {
|
|
1481
|
-
|
|
1482
|
-
|
|
1875
|
+
log3("Failed to open browser:", e);
|
|
1876
|
+
log3(`Please open ${url} manually`);
|
|
1483
1877
|
}
|
|
1484
1878
|
break;
|
|
1485
1879
|
}
|
|
1486
1880
|
case "oneshot":
|
|
1487
1881
|
default: {
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1882
|
+
let port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1883
|
+
let serverPromise = null;
|
|
1884
|
+
if (!port) {
|
|
1885
|
+
port = await findFreePort(DEFAULT_PORT, MAX_PORT);
|
|
1886
|
+
log3(`Starting server at http://localhost:${port}...`);
|
|
1887
|
+
const server = new Server(port, {
|
|
1888
|
+
sessionTimeoutMs: 4e3,
|
|
1889
|
+
// 4s (polling is 2s, so 2 missed polls = dead)
|
|
1890
|
+
idleTimeout: 5e3
|
|
1891
|
+
});
|
|
1892
|
+
serverPromise = server.run();
|
|
1893
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
1894
|
+
} else {
|
|
1895
|
+
log3(`Using existing server at http://localhost:${port}`);
|
|
1896
|
+
}
|
|
1897
|
+
if (!await registerSession(port, finalSessionId, cwd)) {
|
|
1898
|
+
log3("Failed to register session with server");
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
const url = `http://localhost:${port}?session=${encodeURIComponent(finalSessionId)}`;
|
|
1902
|
+
log3(`Opening ${url} in browser...`);
|
|
1491
1903
|
try {
|
|
1492
1904
|
await open(url);
|
|
1493
1905
|
} catch (e) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1906
|
+
log3("Failed to open browser:", e);
|
|
1907
|
+
log3(`Please open ${url} manually`);
|
|
1496
1908
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
} else {
|
|
1503
|
-
log("\nCancelled");
|
|
1909
|
+
if (serverPromise) {
|
|
1910
|
+
log3(`Session: ${finalSessionId}`);
|
|
1911
|
+
log3("Waiting for session to complete... (Ctrl+C to exit)");
|
|
1912
|
+
await serverPromise;
|
|
1913
|
+
log3(`[${formatTime()}] Done`);
|
|
1504
1914
|
}
|
|
1505
1915
|
break;
|
|
1506
1916
|
}
|
|
@@ -1512,12 +1922,12 @@ function printUsage() {
|
|
|
1512
1922
|
console.error("Commands:");
|
|
1513
1923
|
console.error(" serve [path] Start server in daemon mode (keeps running)");
|
|
1514
1924
|
console.error(" open Open browser to existing server");
|
|
1515
|
-
console.error(" setup Setup
|
|
1925
|
+
console.error(" setup Setup hotkey in shell config");
|
|
1516
1926
|
console.error(" (no command) Start server + open browser (one-shot mode)");
|
|
1517
1927
|
console.error("");
|
|
1518
1928
|
console.error("Options:");
|
|
1519
1929
|
console.error(" path Directory to serve (default: current directory)");
|
|
1520
|
-
console.error(" --session <
|
|
1930
|
+
console.error(" --session <id> Session ID for paste-back tracking (auto-generated from tty)");
|
|
1521
1931
|
}
|
|
1522
1932
|
async function setupHotkey() {
|
|
1523
1933
|
let nodePath;
|
|
@@ -1526,8 +1936,44 @@ async function setupHotkey() {
|
|
|
1526
1936
|
} catch {
|
|
1527
1937
|
nodePath = "/usr/local/bin/node";
|
|
1528
1938
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1939
|
+
let csqPath;
|
|
1940
|
+
try {
|
|
1941
|
+
csqPath = execSync2("which csq", { encoding: "utf-8" }).trim();
|
|
1942
|
+
} catch {
|
|
1943
|
+
csqPath = new URL(import.meta.url).pathname;
|
|
1944
|
+
}
|
|
1945
|
+
const nodeDir = path9.dirname(nodePath);
|
|
1946
|
+
const wrapperScript = `#!/bin/bash
|
|
1947
|
+
# Add node to PATH (coprocess doesn't inherit shell PATH)
|
|
1948
|
+
export PATH="${nodeDir}:$PATH"
|
|
1949
|
+
|
|
1950
|
+
# Get current directory from iTerm2
|
|
1951
|
+
CWD=$(osascript -e 'tell application "iTerm2" to tell current session of current tab of current window to return variable named "session.path"' 2>/dev/null)
|
|
1952
|
+
|
|
1953
|
+
# Fallback: try to get from tty
|
|
1954
|
+
if [ -z "$CWD" ] || [ "$CWD" = "missing value" ]; then
|
|
1955
|
+
TTY=$(osascript -e 'tell application "iTerm2" to tell current session of current tab of current window to return tty' 2>/dev/null)
|
|
1956
|
+
if [ -n "$TTY" ]; then
|
|
1957
|
+
PID=$(lsof -t "$TTY" 2>/dev/null | head -1)
|
|
1958
|
+
if [ -n "$PID" ]; then
|
|
1959
|
+
CWD=$(lsof -a -d cwd -p "$PID" -Fn 2>/dev/null | grep ^n | cut -c2-)
|
|
1960
|
+
fi
|
|
1961
|
+
fi
|
|
1962
|
+
fi
|
|
1963
|
+
|
|
1964
|
+
# Run csq flip with cwd
|
|
1965
|
+
if [ -n "$CWD" ] && [ "$CWD" != "missing value" ]; then
|
|
1966
|
+
exec ${csqPath} flip "$CWD"
|
|
1967
|
+
else
|
|
1968
|
+
exec ${csqPath} flip
|
|
1969
|
+
fi
|
|
1970
|
+
`;
|
|
1971
|
+
const scriptDir = path9.join(os3.homedir(), ".config", "csq");
|
|
1972
|
+
const scriptPath = path9.join(scriptDir, "flip-hotkey.sh");
|
|
1973
|
+
if (!fs8.existsSync(scriptDir)) {
|
|
1974
|
+
fs8.mkdirSync(scriptDir, { recursive: true });
|
|
1975
|
+
}
|
|
1976
|
+
fs8.writeFileSync(scriptPath, wrapperScript, { mode: 493 });
|
|
1531
1977
|
console.log("");
|
|
1532
1978
|
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1533
1979
|
console.log("\u2502 Flip Hotkey Setup (iTerm2) \u2502");
|
|
@@ -1539,10 +1985,10 @@ async function setupHotkey() {
|
|
|
1539
1985
|
console.log('4. Action: "Run Coprocess"');
|
|
1540
1986
|
console.log("5. Command (\uC544\uB798 \uC790\uB3D9 \uBCF5\uC0AC\uB428):");
|
|
1541
1987
|
console.log("");
|
|
1542
|
-
console.log(` ${
|
|
1988
|
+
console.log(` ${scriptPath}`);
|
|
1543
1989
|
console.log("");
|
|
1544
1990
|
try {
|
|
1545
|
-
await copyToClipboard(
|
|
1991
|
+
await copyToClipboard(scriptPath);
|
|
1546
1992
|
console.log(" (Copied to clipboard!)");
|
|
1547
1993
|
} catch {
|
|
1548
1994
|
}
|
|
@@ -1550,13 +1996,13 @@ async function setupHotkey() {
|
|
|
1550
1996
|
}
|
|
1551
1997
|
|
|
1552
1998
|
// dist/config.js
|
|
1553
|
-
import * as
|
|
1554
|
-
import * as
|
|
1999
|
+
import * as fs9 from "fs";
|
|
2000
|
+
import * as os4 from "os";
|
|
1555
2001
|
import * as path10 from "path";
|
|
1556
|
-
var GLOBAL_CONFIG_PATH = path10.join(
|
|
2002
|
+
var GLOBAL_CONFIG_PATH = path10.join(os4.homedir(), ".code-squad", "config.json");
|
|
1557
2003
|
async function loadGlobalConfig() {
|
|
1558
2004
|
try {
|
|
1559
|
-
const content = await
|
|
2005
|
+
const content = await fs9.promises.readFile(GLOBAL_CONFIG_PATH, "utf-8");
|
|
1560
2006
|
return JSON.parse(content);
|
|
1561
2007
|
} catch (error) {
|
|
1562
2008
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -1587,7 +2033,7 @@ function getWorktreeCopyPatterns(config) {
|
|
|
1587
2033
|
}
|
|
1588
2034
|
|
|
1589
2035
|
// dist/fileUtils.js
|
|
1590
|
-
import * as
|
|
2036
|
+
import * as fs10 from "fs";
|
|
1591
2037
|
import * as path11 from "path";
|
|
1592
2038
|
import fg from "fast-glob";
|
|
1593
2039
|
async function copyFilesWithPatterns(sourceRoot, destRoot, patterns) {
|
|
@@ -1624,8 +2070,8 @@ async function copySingleFile(absolutePath, sourceRoot, destRoot) {
|
|
|
1624
2070
|
const relativePath = path11.relative(sourceRoot, absolutePath);
|
|
1625
2071
|
const destPath = path11.join(destRoot, relativePath);
|
|
1626
2072
|
const destDir = path11.dirname(destPath);
|
|
1627
|
-
await
|
|
1628
|
-
await
|
|
2073
|
+
await fs10.promises.mkdir(destDir, { recursive: true });
|
|
2074
|
+
await fs10.promises.copyFile(absolutePath, destPath);
|
|
1629
2075
|
}
|
|
1630
2076
|
|
|
1631
2077
|
// dist/index.js
|
|
@@ -1681,12 +2127,12 @@ function getProjectHash(workspaceRoot) {
|
|
|
1681
2127
|
function getSessionsPath(workspaceRoot) {
|
|
1682
2128
|
const projectHash = getProjectHash(workspaceRoot);
|
|
1683
2129
|
const projectName = path12.basename(workspaceRoot);
|
|
1684
|
-
return path12.join(
|
|
2130
|
+
return path12.join(os5.homedir(), ".code-squad", "sessions", `${projectName}-${projectHash}.json`);
|
|
1685
2131
|
}
|
|
1686
2132
|
async function loadLocalThreads(workspaceRoot) {
|
|
1687
2133
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
1688
2134
|
try {
|
|
1689
|
-
const content = await
|
|
2135
|
+
const content = await fs11.promises.readFile(sessionsPath, "utf-8");
|
|
1690
2136
|
const data = JSON.parse(content);
|
|
1691
2137
|
return data.localThreads || [];
|
|
1692
2138
|
} catch {
|
|
@@ -1696,8 +2142,8 @@ async function loadLocalThreads(workspaceRoot) {
|
|
|
1696
2142
|
async function saveLocalThreads(workspaceRoot, threads) {
|
|
1697
2143
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
1698
2144
|
const dir = path12.dirname(sessionsPath);
|
|
1699
|
-
await
|
|
1700
|
-
await
|
|
2145
|
+
await fs11.promises.mkdir(dir, { recursive: true });
|
|
2146
|
+
await fs11.promises.writeFile(sessionsPath, JSON.stringify({ localThreads: threads }, null, 2));
|
|
1701
2147
|
}
|
|
1702
2148
|
async function addLocalThread(workspaceRoot, name) {
|
|
1703
2149
|
const threads = await loadLocalThreads(workspaceRoot);
|