code-squad-cli 1.2.16 → 1.2.19
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 +53 -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 +72 -0
- package/dist/flip/session/SessionManager.js +169 -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 +598 -150
- 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,97 @@ 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
|
|
1169
1269
|
});
|
|
1170
1270
|
});
|
|
1171
1271
|
return router6;
|
|
1172
1272
|
}
|
|
1173
1273
|
|
|
1274
|
+
// dist/flip/routes/changes.js
|
|
1275
|
+
import { Router as Router9 } from "express";
|
|
1276
|
+
function createChangesRouter(sessionManager) {
|
|
1277
|
+
const router6 = Router9();
|
|
1278
|
+
router6.get("/", (req, res) => {
|
|
1279
|
+
const sessionId = req.query.session_id;
|
|
1280
|
+
if (!sessionId) {
|
|
1281
|
+
res.status(400).json({ error: "Missing session_id" });
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
sessionManager.touchSession(sessionId);
|
|
1285
|
+
const changes = sessionManager.consumePendingChanges(sessionId);
|
|
1286
|
+
if (!changes) {
|
|
1287
|
+
res.status(404).json({ error: "Session not found" });
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
const response = {
|
|
1291
|
+
filesChanged: changes.filesChanged,
|
|
1292
|
+
gitChanged: changes.gitChanged,
|
|
1293
|
+
changedFiles: Array.from(changes.changedFiles)
|
|
1294
|
+
};
|
|
1295
|
+
res.json(response);
|
|
1296
|
+
});
|
|
1297
|
+
return router6;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1174
1300
|
// dist/flip/watcher/FileWatcher.js
|
|
1175
1301
|
import chokidar from "chokidar";
|
|
1176
1302
|
import * as path8 from "path";
|
|
@@ -1294,87 +1420,266 @@ var FileWatcher = class {
|
|
|
1294
1420
|
}
|
|
1295
1421
|
};
|
|
1296
1422
|
|
|
1297
|
-
// dist/flip/
|
|
1298
|
-
var
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
});
|
|
1423
|
+
// dist/flip/session/SessionManager.js
|
|
1424
|
+
var log = (...args) => console.error(...args);
|
|
1425
|
+
var SessionManager = class {
|
|
1426
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1427
|
+
options;
|
|
1428
|
+
constructor(options) {
|
|
1429
|
+
this.options = options;
|
|
1305
1430
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1431
|
+
/**
|
|
1432
|
+
* Start the session manager (no-op, kept for API compatibility)
|
|
1433
|
+
*/
|
|
1434
|
+
start() {
|
|
1308
1435
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1436
|
+
/**
|
|
1437
|
+
* Stop the session manager and clean up all sessions
|
|
1438
|
+
*/
|
|
1439
|
+
async stop() {
|
|
1440
|
+
const stopPromises = Array.from(this.sessions.values()).map((session) => {
|
|
1441
|
+
if (session.timeoutTimer) {
|
|
1442
|
+
clearTimeout(session.timeoutTimer);
|
|
1443
|
+
}
|
|
1444
|
+
return session.watcher.stop();
|
|
1316
1445
|
});
|
|
1446
|
+
await Promise.all(stopPromises);
|
|
1447
|
+
this.sessions.clear();
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Register a new session or update existing one
|
|
1451
|
+
*/
|
|
1452
|
+
registerSession(sessionId, cwd) {
|
|
1453
|
+
const existing = this.sessions.get(sessionId);
|
|
1454
|
+
if (existing) {
|
|
1455
|
+
this.resetSessionTimeout(sessionId);
|
|
1456
|
+
if (existing.cwd !== cwd) {
|
|
1457
|
+
existing.watcher.stop();
|
|
1458
|
+
existing.cwd = cwd;
|
|
1459
|
+
existing.watcher = this.createWatcher(sessionId, cwd);
|
|
1460
|
+
existing.pendingChanges = this.createEmptyPendingChanges();
|
|
1461
|
+
}
|
|
1462
|
+
return existing;
|
|
1463
|
+
}
|
|
1464
|
+
const watcher = this.createWatcher(sessionId, cwd);
|
|
1465
|
+
const session = {
|
|
1466
|
+
id: sessionId,
|
|
1467
|
+
cwd,
|
|
1468
|
+
watcher,
|
|
1469
|
+
pendingChanges: this.createEmptyPendingChanges(),
|
|
1470
|
+
timeoutTimer: null
|
|
1471
|
+
};
|
|
1472
|
+
this.sessions.set(sessionId, session);
|
|
1473
|
+
this.resetSessionTimeout(sessionId);
|
|
1474
|
+
log(`[SessionManager] Session registered: ${sessionId} (cwd: ${cwd})`);
|
|
1475
|
+
return session;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Get a session by ID and reset timeout
|
|
1479
|
+
*/
|
|
1480
|
+
getSession(sessionId) {
|
|
1481
|
+
const session = this.sessions.get(sessionId);
|
|
1482
|
+
if (session) {
|
|
1483
|
+
this.resetSessionTimeout(sessionId);
|
|
1484
|
+
}
|
|
1485
|
+
return session;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Touch session to reset timeout (called on every API request)
|
|
1489
|
+
*/
|
|
1490
|
+
touchSession(sessionId) {
|
|
1491
|
+
this.resetSessionTimeout(sessionId);
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Unregister a session (e.g., on cancel/submit/timeout)
|
|
1495
|
+
*/
|
|
1496
|
+
async unregisterSession(sessionId) {
|
|
1497
|
+
const session = this.sessions.get(sessionId);
|
|
1498
|
+
if (session) {
|
|
1499
|
+
if (session.timeoutTimer) {
|
|
1500
|
+
clearTimeout(session.timeoutTimer);
|
|
1501
|
+
}
|
|
1502
|
+
await session.watcher.stop();
|
|
1503
|
+
this.sessions.delete(sessionId);
|
|
1504
|
+
log(`[SessionManager] Session unregistered: ${sessionId}`);
|
|
1505
|
+
if (this.sessions.size === 0 && this.options.onAllSessionsGone) {
|
|
1506
|
+
this.options.onAllSessionsGone();
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1317
1509
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1510
|
+
/**
|
|
1511
|
+
* Get pending changes for a session and clear them
|
|
1512
|
+
*/
|
|
1513
|
+
consumePendingChanges(sessionId) {
|
|
1514
|
+
const session = this.sessions.get(sessionId);
|
|
1515
|
+
if (!session)
|
|
1516
|
+
return null;
|
|
1517
|
+
const changes = { ...session.pendingChanges };
|
|
1518
|
+
changes.changedFiles = new Set(session.pendingChanges.changedFiles);
|
|
1519
|
+
session.pendingChanges = this.createEmptyPendingChanges();
|
|
1520
|
+
return changes;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Get number of active sessions
|
|
1524
|
+
*/
|
|
1525
|
+
get sessionCount() {
|
|
1526
|
+
return this.sessions.size;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Check if any sessions exist
|
|
1530
|
+
*/
|
|
1531
|
+
get hasSessions() {
|
|
1532
|
+
return this.sessions.size > 0;
|
|
1533
|
+
}
|
|
1534
|
+
createWatcher(sessionId, cwd) {
|
|
1535
|
+
const watcher = new FileWatcher({ cwd });
|
|
1536
|
+
watcher.onFilesChanged(() => {
|
|
1537
|
+
const session = this.sessions.get(sessionId);
|
|
1538
|
+
if (session) {
|
|
1539
|
+
session.pendingChanges.filesChanged = true;
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
watcher.onFileChanged((filePath) => {
|
|
1543
|
+
const session = this.sessions.get(sessionId);
|
|
1544
|
+
if (session) {
|
|
1545
|
+
session.pendingChanges.changedFiles.add(filePath);
|
|
1546
|
+
}
|
|
1321
1547
|
});
|
|
1322
|
-
|
|
1548
|
+
watcher.onGitChanged(() => {
|
|
1549
|
+
const session = this.sessions.get(sessionId);
|
|
1550
|
+
if (session) {
|
|
1551
|
+
session.pendingChanges.gitChanged = true;
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
watcher.start();
|
|
1555
|
+
return watcher;
|
|
1556
|
+
}
|
|
1557
|
+
createEmptyPendingChanges() {
|
|
1558
|
+
return {
|
|
1559
|
+
filesChanged: false,
|
|
1560
|
+
gitChanged: false,
|
|
1561
|
+
changedFiles: /* @__PURE__ */ new Set()
|
|
1562
|
+
};
|
|
1323
1563
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1564
|
+
/**
|
|
1565
|
+
* Reset the timeout timer for a session.
|
|
1566
|
+
* Called on every activity (register, poll, etc.)
|
|
1567
|
+
*/
|
|
1568
|
+
resetSessionTimeout(sessionId) {
|
|
1569
|
+
const session = this.sessions.get(sessionId);
|
|
1570
|
+
if (!session)
|
|
1571
|
+
return;
|
|
1572
|
+
if (session.timeoutTimer) {
|
|
1573
|
+
clearTimeout(session.timeoutTimer);
|
|
1574
|
+
}
|
|
1575
|
+
session.timeoutTimer = setTimeout(() => {
|
|
1576
|
+
log(`[SessionManager] Session timed out: ${sessionId}`);
|
|
1577
|
+
this.unregisterSession(sessionId).catch((error) => {
|
|
1578
|
+
log(`[SessionManager] Error during session unregister for ${sessionId}:`, error);
|
|
1579
|
+
});
|
|
1580
|
+
}, this.options.sessionTimeoutMs);
|
|
1326
1581
|
}
|
|
1327
1582
|
};
|
|
1328
1583
|
|
|
1329
1584
|
// dist/flip/server/Server.js
|
|
1585
|
+
var log2 = (...args) => console.error(...args);
|
|
1586
|
+
var SERVER_ID = "csq-flip";
|
|
1330
1587
|
var Server = class {
|
|
1331
|
-
cwd;
|
|
1332
1588
|
port;
|
|
1333
|
-
|
|
1334
|
-
|
|
1589
|
+
options;
|
|
1590
|
+
app = null;
|
|
1591
|
+
server = null;
|
|
1592
|
+
sessionManager = null;
|
|
1593
|
+
resolveShutdown = null;
|
|
1594
|
+
constructor(port, options = {}) {
|
|
1335
1595
|
this.port = port;
|
|
1596
|
+
this.options = {
|
|
1597
|
+
sessionTimeoutMs: options.sessionTimeoutMs ?? 3e4,
|
|
1598
|
+
idleTimeout: options.idleTimeout ?? 0
|
|
1599
|
+
};
|
|
1336
1600
|
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Start the server and return a promise that resolves when server shuts down
|
|
1603
|
+
*/
|
|
1337
1604
|
async run() {
|
|
1338
1605
|
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" });
|
|
1606
|
+
this.resolveShutdown = resolve2;
|
|
1607
|
+
this.app = express2();
|
|
1608
|
+
this.app.use(cors());
|
|
1609
|
+
this.app.use(express2.json());
|
|
1610
|
+
this.sessionManager = new SessionManager({
|
|
1611
|
+
sessionTimeoutMs: this.options.sessionTimeoutMs,
|
|
1612
|
+
onAllSessionsGone: () => {
|
|
1613
|
+
log2("[Server] All sessions gone, shutting down...");
|
|
1614
|
+
this.shutdown();
|
|
1615
|
+
}
|
|
1357
1616
|
});
|
|
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
|
-
|
|
1617
|
+
this.sessionManager.start();
|
|
1618
|
+
this.app.locals.sessionManager = this.sessionManager;
|
|
1619
|
+
this.app.use("/api/ping", createPingRouter());
|
|
1620
|
+
this.app.use("/api/session", createSessionRouter(this.sessionManager));
|
|
1621
|
+
this.app.use("/api/changes", createChangesRouter(this.sessionManager));
|
|
1622
|
+
this.app.use("/api/files", router);
|
|
1623
|
+
this.app.use("/api/file", router2);
|
|
1624
|
+
this.app.use("/api/git", router3);
|
|
1625
|
+
this.app.use("/api/submit", router4);
|
|
1626
|
+
this.app.use("/api/cancel", router5);
|
|
1627
|
+
this.app.use(createStaticRouter());
|
|
1628
|
+
this.server = http.createServer(this.app);
|
|
1629
|
+
const signalHandler = () => {
|
|
1630
|
+
log2("\nShutting down...");
|
|
1631
|
+
this.shutdown();
|
|
1372
1632
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1633
|
+
process.on("SIGINT", signalHandler);
|
|
1634
|
+
process.on("SIGTERM", signalHandler);
|
|
1635
|
+
let idleTimer = null;
|
|
1636
|
+
if (this.options.idleTimeout && this.options.idleTimeout > 0) {
|
|
1637
|
+
idleTimer = setTimeout(() => {
|
|
1638
|
+
if (!this.sessionManager?.hasSessions) {
|
|
1639
|
+
log2("\nNo session registered, shutting down...");
|
|
1640
|
+
this.shutdown();
|
|
1641
|
+
}
|
|
1642
|
+
}, this.options.idleTimeout);
|
|
1643
|
+
}
|
|
1644
|
+
this.app.locals.idleTimer = idleTimer;
|
|
1645
|
+
this.app.locals.clearIdleTimer = () => {
|
|
1646
|
+
if (idleTimer) {
|
|
1647
|
+
clearTimeout(idleTimer);
|
|
1648
|
+
idleTimer = null;
|
|
1649
|
+
this.app.locals.idleTimer = null;
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
this.server.listen(this.port, "127.0.0.1", () => {
|
|
1653
|
+
log2(`[Server] Running at http://localhost:${this.port}`);
|
|
1375
1654
|
});
|
|
1376
1655
|
});
|
|
1377
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Shutdown the server gracefully
|
|
1659
|
+
*/
|
|
1660
|
+
async shutdown() {
|
|
1661
|
+
if (!this.server)
|
|
1662
|
+
return;
|
|
1663
|
+
if (this.app?.locals.idleTimer) {
|
|
1664
|
+
clearTimeout(this.app.locals.idleTimer);
|
|
1665
|
+
}
|
|
1666
|
+
if (this.sessionManager) {
|
|
1667
|
+
await this.sessionManager.stop();
|
|
1668
|
+
}
|
|
1669
|
+
await new Promise((res) => {
|
|
1670
|
+
this.server.close(() => res());
|
|
1671
|
+
});
|
|
1672
|
+
log2("[Server] Shutdown complete");
|
|
1673
|
+
if (this.resolveShutdown) {
|
|
1674
|
+
this.resolveShutdown();
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Get the session manager
|
|
1679
|
+
*/
|
|
1680
|
+
getSessionManager() {
|
|
1681
|
+
return this.sessionManager;
|
|
1682
|
+
}
|
|
1378
1683
|
};
|
|
1379
1684
|
async function findFreePort(preferred, maxPort = 65535) {
|
|
1380
1685
|
return new Promise((resolve2, reject) => {
|
|
@@ -1395,13 +1700,53 @@ async function findFreePort(preferred, maxPort = 65535) {
|
|
|
1395
1700
|
});
|
|
1396
1701
|
});
|
|
1397
1702
|
}
|
|
1703
|
+
async function isFlipServerRunning(port, timeoutMs = 1e3) {
|
|
1704
|
+
return new Promise((resolve2) => {
|
|
1705
|
+
const req = http.request({
|
|
1706
|
+
hostname: "127.0.0.1",
|
|
1707
|
+
port,
|
|
1708
|
+
path: "/api/ping",
|
|
1709
|
+
method: "GET",
|
|
1710
|
+
timeout: timeoutMs
|
|
1711
|
+
}, (res) => {
|
|
1712
|
+
let data = "";
|
|
1713
|
+
res.on("data", (chunk) => data += chunk);
|
|
1714
|
+
res.on("end", () => {
|
|
1715
|
+
try {
|
|
1716
|
+
const json = JSON.parse(data);
|
|
1717
|
+
resolve2(json.id === SERVER_ID);
|
|
1718
|
+
} catch {
|
|
1719
|
+
resolve2(false);
|
|
1720
|
+
}
|
|
1721
|
+
});
|
|
1722
|
+
});
|
|
1723
|
+
req.on("error", () => resolve2(false));
|
|
1724
|
+
req.on("timeout", () => {
|
|
1725
|
+
req.destroy();
|
|
1726
|
+
resolve2(false);
|
|
1727
|
+
});
|
|
1728
|
+
req.end();
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
async function findExistingServer(startPort, endPort) {
|
|
1732
|
+
for (let port = startPort; port <= endPort; port++) {
|
|
1733
|
+
if (await isFlipServerRunning(port)) {
|
|
1734
|
+
return port;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return null;
|
|
1738
|
+
}
|
|
1398
1739
|
|
|
1399
1740
|
// dist/flip/index.js
|
|
1400
1741
|
import open from "open";
|
|
1401
1742
|
import path9 from "path";
|
|
1743
|
+
import fs8 from "fs";
|
|
1744
|
+
import os3 from "os";
|
|
1745
|
+
import http2 from "http";
|
|
1402
1746
|
import { execSync as execSync2 } from "child_process";
|
|
1403
1747
|
var DEFAULT_PORT = 51234;
|
|
1404
|
-
var
|
|
1748
|
+
var MAX_PORT = 51240;
|
|
1749
|
+
var log3 = (...args) => console.error(...args);
|
|
1405
1750
|
function formatTime() {
|
|
1406
1751
|
const now = /* @__PURE__ */ new Date();
|
|
1407
1752
|
const hours = String(now.getHours()).padStart(2, "0");
|
|
@@ -1409,6 +1754,43 @@ function formatTime() {
|
|
|
1409
1754
|
const secs = String(now.getSeconds()).padStart(2, "0");
|
|
1410
1755
|
return `${hours}:${mins}:${secs}`;
|
|
1411
1756
|
}
|
|
1757
|
+
function getSessionId() {
|
|
1758
|
+
try {
|
|
1759
|
+
const tty = execSync2("tty", { encoding: "utf-8" }).trim();
|
|
1760
|
+
return tty;
|
|
1761
|
+
} catch {
|
|
1762
|
+
return `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
async function registerSession(port, sessionId, cwd) {
|
|
1766
|
+
return new Promise((resolve2) => {
|
|
1767
|
+
const data = JSON.stringify({ session_id: sessionId, cwd });
|
|
1768
|
+
const req = http2.request({
|
|
1769
|
+
hostname: "127.0.0.1",
|
|
1770
|
+
port,
|
|
1771
|
+
path: "/api/session/register",
|
|
1772
|
+
method: "POST",
|
|
1773
|
+
headers: {
|
|
1774
|
+
"Content-Type": "application/json",
|
|
1775
|
+
"Content-Length": Buffer.byteLength(data)
|
|
1776
|
+
},
|
|
1777
|
+
timeout: 5e3
|
|
1778
|
+
}, (res) => {
|
|
1779
|
+
let body = "";
|
|
1780
|
+
res.on("data", (chunk) => body += chunk);
|
|
1781
|
+
res.on("end", () => {
|
|
1782
|
+
resolve2(res.statusCode === 200);
|
|
1783
|
+
});
|
|
1784
|
+
});
|
|
1785
|
+
req.on("error", () => resolve2(false));
|
|
1786
|
+
req.on("timeout", () => {
|
|
1787
|
+
req.destroy();
|
|
1788
|
+
resolve2(false);
|
|
1789
|
+
});
|
|
1790
|
+
req.write(data);
|
|
1791
|
+
req.end();
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1412
1794
|
async function runFlip(args) {
|
|
1413
1795
|
let sessionId;
|
|
1414
1796
|
const sessionIdx = args.indexOf("--session");
|
|
@@ -1449,58 +1831,88 @@ async function runFlip(args) {
|
|
|
1449
1831
|
command = "oneshot";
|
|
1450
1832
|
}
|
|
1451
1833
|
const cwd = pathArg ? path9.resolve(pathArg) : process.cwd();
|
|
1834
|
+
const finalSessionId = sessionId || getSessionId();
|
|
1452
1835
|
switch (command) {
|
|
1453
1836
|
case "setup": {
|
|
1454
1837
|
await setupHotkey();
|
|
1455
1838
|
return;
|
|
1456
1839
|
}
|
|
1457
1840
|
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...`);
|
|
1841
|
+
let port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1842
|
+
if (port) {
|
|
1843
|
+
log3(`[${formatTime()}] Found existing server at port ${port}`);
|
|
1844
|
+
} else {
|
|
1845
|
+
port = await findFreePort(DEFAULT_PORT, MAX_PORT);
|
|
1846
|
+
log3(`[${formatTime()}] Starting new server at port ${port}`);
|
|
1847
|
+
const server = new Server(port, { sessionTimeoutMs: 4e3 });
|
|
1848
|
+
server.run().catch((err) => {
|
|
1849
|
+
log3(`[${formatTime()}] Server error:`, err);
|
|
1850
|
+
});
|
|
1473
1851
|
}
|
|
1852
|
+
log3(`[${formatTime()}] Server running at http://localhost:${port}`);
|
|
1853
|
+
log3("Press Ctrl+C to stop");
|
|
1854
|
+
log3("");
|
|
1855
|
+
log3("To open browser, run: csq flip open");
|
|
1856
|
+
log3(`Or use hotkey to open: open http://localhost:${port}`);
|
|
1857
|
+
await new Promise(() => {
|
|
1858
|
+
});
|
|
1474
1859
|
}
|
|
1475
1860
|
case "open": {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1861
|
+
const port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1862
|
+
if (!port) {
|
|
1863
|
+
log3("No csq flip server running.");
|
|
1864
|
+
log3("Start with: csq flip serve");
|
|
1865
|
+
log3("Or use: csq flip (one-shot mode)");
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
if (!await registerSession(port, finalSessionId, cwd)) {
|
|
1869
|
+
log3("Failed to register session with server");
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const url = `http://localhost:${port}?session=${encodeURIComponent(finalSessionId)}`;
|
|
1873
|
+
log3(`Opening ${url} in browser...`);
|
|
1478
1874
|
try {
|
|
1479
1875
|
await open(url);
|
|
1480
1876
|
} catch (e) {
|
|
1481
|
-
|
|
1482
|
-
|
|
1877
|
+
log3("Failed to open browser:", e);
|
|
1878
|
+
log3(`Please open ${url} manually`);
|
|
1483
1879
|
}
|
|
1484
1880
|
break;
|
|
1485
1881
|
}
|
|
1486
1882
|
case "oneshot":
|
|
1487
1883
|
default: {
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1884
|
+
let port = await findExistingServer(DEFAULT_PORT, MAX_PORT);
|
|
1885
|
+
let serverPromise = null;
|
|
1886
|
+
if (!port) {
|
|
1887
|
+
port = await findFreePort(DEFAULT_PORT, MAX_PORT);
|
|
1888
|
+
log3(`Starting server at http://localhost:${port}...`);
|
|
1889
|
+
const server = new Server(port, {
|
|
1890
|
+
sessionTimeoutMs: 4e3,
|
|
1891
|
+
// 4s (polling is 2s, so 2 missed polls = dead)
|
|
1892
|
+
idleTimeout: 5e3
|
|
1893
|
+
});
|
|
1894
|
+
serverPromise = server.run();
|
|
1895
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
1896
|
+
} else {
|
|
1897
|
+
log3(`Using existing server at http://localhost:${port}`);
|
|
1898
|
+
}
|
|
1899
|
+
if (!await registerSession(port, finalSessionId, cwd)) {
|
|
1900
|
+
log3("Failed to register session with server");
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
const url = `http://localhost:${port}?session=${encodeURIComponent(finalSessionId)}`;
|
|
1904
|
+
log3(`Opening ${url} in browser...`);
|
|
1491
1905
|
try {
|
|
1492
1906
|
await open(url);
|
|
1493
1907
|
} catch (e) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1908
|
+
log3("Failed to open browser:", e);
|
|
1909
|
+
log3(`Please open ${url} manually`);
|
|
1496
1910
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
} else {
|
|
1503
|
-
log("\nCancelled");
|
|
1911
|
+
if (serverPromise) {
|
|
1912
|
+
log3(`Session: ${finalSessionId}`);
|
|
1913
|
+
log3("Waiting for session to complete... (Ctrl+C to exit)");
|
|
1914
|
+
await serverPromise;
|
|
1915
|
+
log3(`[${formatTime()}] Done`);
|
|
1504
1916
|
}
|
|
1505
1917
|
break;
|
|
1506
1918
|
}
|
|
@@ -1512,12 +1924,12 @@ function printUsage() {
|
|
|
1512
1924
|
console.error("Commands:");
|
|
1513
1925
|
console.error(" serve [path] Start server in daemon mode (keeps running)");
|
|
1514
1926
|
console.error(" open Open browser to existing server");
|
|
1515
|
-
console.error(" setup Setup
|
|
1927
|
+
console.error(" setup Setup hotkey in shell config");
|
|
1516
1928
|
console.error(" (no command) Start server + open browser (one-shot mode)");
|
|
1517
1929
|
console.error("");
|
|
1518
1930
|
console.error("Options:");
|
|
1519
1931
|
console.error(" path Directory to serve (default: current directory)");
|
|
1520
|
-
console.error(" --session <
|
|
1932
|
+
console.error(" --session <id> Session ID for paste-back tracking (auto-generated from tty)");
|
|
1521
1933
|
}
|
|
1522
1934
|
async function setupHotkey() {
|
|
1523
1935
|
let nodePath;
|
|
@@ -1526,8 +1938,44 @@ async function setupHotkey() {
|
|
|
1526
1938
|
} catch {
|
|
1527
1939
|
nodePath = "/usr/local/bin/node";
|
|
1528
1940
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1941
|
+
let csqPath;
|
|
1942
|
+
try {
|
|
1943
|
+
csqPath = execSync2("which csq", { encoding: "utf-8" }).trim();
|
|
1944
|
+
} catch {
|
|
1945
|
+
csqPath = new URL(import.meta.url).pathname;
|
|
1946
|
+
}
|
|
1947
|
+
const nodeDir = path9.dirname(nodePath);
|
|
1948
|
+
const wrapperScript = `#!/bin/bash
|
|
1949
|
+
# Add node to PATH (coprocess doesn't inherit shell PATH)
|
|
1950
|
+
export PATH="${nodeDir}:$PATH"
|
|
1951
|
+
|
|
1952
|
+
# Get current directory from iTerm2
|
|
1953
|
+
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)
|
|
1954
|
+
|
|
1955
|
+
# Fallback: try to get from tty
|
|
1956
|
+
if [ -z "$CWD" ] || [ "$CWD" = "missing value" ]; then
|
|
1957
|
+
TTY=$(osascript -e 'tell application "iTerm2" to tell current session of current tab of current window to return tty' 2>/dev/null)
|
|
1958
|
+
if [ -n "$TTY" ]; then
|
|
1959
|
+
PID=$(lsof -t "$TTY" 2>/dev/null | head -1)
|
|
1960
|
+
if [ -n "$PID" ]; then
|
|
1961
|
+
CWD=$(lsof -a -d cwd -p "$PID" -Fn 2>/dev/null | grep ^n | cut -c2-)
|
|
1962
|
+
fi
|
|
1963
|
+
fi
|
|
1964
|
+
fi
|
|
1965
|
+
|
|
1966
|
+
# Run csq flip with cwd
|
|
1967
|
+
if [ -n "$CWD" ] && [ "$CWD" != "missing value" ]; then
|
|
1968
|
+
exec ${csqPath} flip "$CWD"
|
|
1969
|
+
else
|
|
1970
|
+
exec ${csqPath} flip
|
|
1971
|
+
fi
|
|
1972
|
+
`;
|
|
1973
|
+
const scriptDir = path9.join(os3.homedir(), ".config", "csq");
|
|
1974
|
+
const scriptPath = path9.join(scriptDir, "flip-hotkey.sh");
|
|
1975
|
+
if (!fs8.existsSync(scriptDir)) {
|
|
1976
|
+
fs8.mkdirSync(scriptDir, { recursive: true });
|
|
1977
|
+
}
|
|
1978
|
+
fs8.writeFileSync(scriptPath, wrapperScript, { mode: 493 });
|
|
1531
1979
|
console.log("");
|
|
1532
1980
|
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
1981
|
console.log("\u2502 Flip Hotkey Setup (iTerm2) \u2502");
|
|
@@ -1539,10 +1987,10 @@ async function setupHotkey() {
|
|
|
1539
1987
|
console.log('4. Action: "Run Coprocess"');
|
|
1540
1988
|
console.log("5. Command (\uC544\uB798 \uC790\uB3D9 \uBCF5\uC0AC\uB428):");
|
|
1541
1989
|
console.log("");
|
|
1542
|
-
console.log(` ${
|
|
1990
|
+
console.log(` ${scriptPath}`);
|
|
1543
1991
|
console.log("");
|
|
1544
1992
|
try {
|
|
1545
|
-
await copyToClipboard(
|
|
1993
|
+
await copyToClipboard(scriptPath);
|
|
1546
1994
|
console.log(" (Copied to clipboard!)");
|
|
1547
1995
|
} catch {
|
|
1548
1996
|
}
|
|
@@ -1550,13 +1998,13 @@ async function setupHotkey() {
|
|
|
1550
1998
|
}
|
|
1551
1999
|
|
|
1552
2000
|
// dist/config.js
|
|
1553
|
-
import * as
|
|
1554
|
-
import * as
|
|
2001
|
+
import * as fs9 from "fs";
|
|
2002
|
+
import * as os4 from "os";
|
|
1555
2003
|
import * as path10 from "path";
|
|
1556
|
-
var GLOBAL_CONFIG_PATH = path10.join(
|
|
2004
|
+
var GLOBAL_CONFIG_PATH = path10.join(os4.homedir(), ".code-squad", "config.json");
|
|
1557
2005
|
async function loadGlobalConfig() {
|
|
1558
2006
|
try {
|
|
1559
|
-
const content = await
|
|
2007
|
+
const content = await fs9.promises.readFile(GLOBAL_CONFIG_PATH, "utf-8");
|
|
1560
2008
|
return JSON.parse(content);
|
|
1561
2009
|
} catch (error) {
|
|
1562
2010
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -1587,7 +2035,7 @@ function getWorktreeCopyPatterns(config) {
|
|
|
1587
2035
|
}
|
|
1588
2036
|
|
|
1589
2037
|
// dist/fileUtils.js
|
|
1590
|
-
import * as
|
|
2038
|
+
import * as fs10 from "fs";
|
|
1591
2039
|
import * as path11 from "path";
|
|
1592
2040
|
import fg from "fast-glob";
|
|
1593
2041
|
async function copyFilesWithPatterns(sourceRoot, destRoot, patterns) {
|
|
@@ -1624,8 +2072,8 @@ async function copySingleFile(absolutePath, sourceRoot, destRoot) {
|
|
|
1624
2072
|
const relativePath = path11.relative(sourceRoot, absolutePath);
|
|
1625
2073
|
const destPath = path11.join(destRoot, relativePath);
|
|
1626
2074
|
const destDir = path11.dirname(destPath);
|
|
1627
|
-
await
|
|
1628
|
-
await
|
|
2075
|
+
await fs10.promises.mkdir(destDir, { recursive: true });
|
|
2076
|
+
await fs10.promises.copyFile(absolutePath, destPath);
|
|
1629
2077
|
}
|
|
1630
2078
|
|
|
1631
2079
|
// dist/index.js
|
|
@@ -1681,12 +2129,12 @@ function getProjectHash(workspaceRoot) {
|
|
|
1681
2129
|
function getSessionsPath(workspaceRoot) {
|
|
1682
2130
|
const projectHash = getProjectHash(workspaceRoot);
|
|
1683
2131
|
const projectName = path12.basename(workspaceRoot);
|
|
1684
|
-
return path12.join(
|
|
2132
|
+
return path12.join(os5.homedir(), ".code-squad", "sessions", `${projectName}-${projectHash}.json`);
|
|
1685
2133
|
}
|
|
1686
2134
|
async function loadLocalThreads(workspaceRoot) {
|
|
1687
2135
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
1688
2136
|
try {
|
|
1689
|
-
const content = await
|
|
2137
|
+
const content = await fs11.promises.readFile(sessionsPath, "utf-8");
|
|
1690
2138
|
const data = JSON.parse(content);
|
|
1691
2139
|
return data.localThreads || [];
|
|
1692
2140
|
} catch {
|
|
@@ -1696,8 +2144,8 @@ async function loadLocalThreads(workspaceRoot) {
|
|
|
1696
2144
|
async function saveLocalThreads(workspaceRoot, threads) {
|
|
1697
2145
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
1698
2146
|
const dir = path12.dirname(sessionsPath);
|
|
1699
|
-
await
|
|
1700
|
-
await
|
|
2147
|
+
await fs11.promises.mkdir(dir, { recursive: true });
|
|
2148
|
+
await fs11.promises.writeFile(sessionsPath, JSON.stringify({ localThreads: threads }, null, 2));
|
|
1701
2149
|
}
|
|
1702
2150
|
async function addLocalThread(workspaceRoot, name) {
|
|
1703
2151
|
const threads = await loadLocalThreads(workspaceRoot);
|