code-squad-cli 1.2.9 → 1.2.11
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/output/autopaste.js +11 -7
- package/dist/flip/output/clipboard.js +30 -36
- package/dist/flip/routes/events.js +2 -1
- package/dist/flip/routes/static.js +8 -4
- package/dist/flip/server/Server.d.ts +1 -1
- package/dist/flip/server/Server.js +9 -4
- package/dist/index.js +52 -41
- package/package.json +1 -1
|
@@ -73,18 +73,22 @@ async function pasteToOriginalSession(sessionId) {
|
|
|
73
73
|
// Write script to temp file and execute via detached process
|
|
74
74
|
const tmpScript = path.join(os.tmpdir(), `flip-paste-${Date.now()}.scpt`);
|
|
75
75
|
fs.writeFileSync(tmpScript, script);
|
|
76
|
+
const cleanupScript = () => {
|
|
77
|
+
try {
|
|
78
|
+
fs.unlinkSync(tmpScript);
|
|
79
|
+
}
|
|
80
|
+
catch { /* ignore */ }
|
|
81
|
+
};
|
|
76
82
|
const child = spawn('osascript', [tmpScript], {
|
|
77
83
|
detached: true,
|
|
78
84
|
stdio: 'ignore',
|
|
79
85
|
});
|
|
86
|
+
// Cleanup on process exit or error
|
|
87
|
+
child.on('exit', cleanupScript);
|
|
88
|
+
child.on('error', cleanupScript);
|
|
80
89
|
child.unref();
|
|
81
|
-
//
|
|
82
|
-
setTimeout(
|
|
83
|
-
try {
|
|
84
|
-
fs.unlinkSync(tmpScript);
|
|
85
|
-
}
|
|
86
|
-
catch { }
|
|
87
|
-
}, 5000);
|
|
90
|
+
// Fallback cleanup in case events don't fire (safety net)
|
|
91
|
+
setTimeout(cleanupScript, 10000);
|
|
88
92
|
}
|
|
89
93
|
catch (e) {
|
|
90
94
|
console.error('Failed to paste to iTerm:', e);
|
|
@@ -6,56 +6,50 @@ import os from 'os';
|
|
|
6
6
|
* Copy text to system clipboard
|
|
7
7
|
*/
|
|
8
8
|
export async function copyToClipboard(text) {
|
|
9
|
+
const tmpFile = path.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
10
|
+
const cleanupTmpFile = () => {
|
|
11
|
+
try {
|
|
12
|
+
fs.unlinkSync(tmpFile);
|
|
13
|
+
}
|
|
14
|
+
catch { /* ignore */ }
|
|
15
|
+
};
|
|
16
|
+
const spawnWithCleanup = (command, args) => {
|
|
17
|
+
const child = spawn(command, args, {
|
|
18
|
+
detached: true,
|
|
19
|
+
stdio: 'ignore',
|
|
20
|
+
});
|
|
21
|
+
child.on('error', cleanupTmpFile);
|
|
22
|
+
child.on('exit', cleanupTmpFile);
|
|
23
|
+
child.unref();
|
|
24
|
+
};
|
|
9
25
|
if (process.platform === 'darwin') {
|
|
10
|
-
// Write to temp file first, then use pbcopy via shell
|
|
11
|
-
const tmpFile = path.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
12
26
|
try {
|
|
13
27
|
fs.writeFileSync(tmpFile, text);
|
|
14
|
-
|
|
15
|
-
const child = spawn('sh', ['-c', `cat "${tmpFile}" | pbcopy && rm "${tmpFile}"`], {
|
|
16
|
-
detached: true,
|
|
17
|
-
stdio: 'ignore',
|
|
18
|
-
});
|
|
19
|
-
child.unref();
|
|
28
|
+
spawnWithCleanup('sh', ['-c', `cat "${tmpFile}" | pbcopy && rm "${tmpFile}"`]);
|
|
20
29
|
}
|
|
21
30
|
catch (e) {
|
|
22
|
-
// Fallback: just leave the file for manual copy
|
|
23
31
|
console.log(`Output saved to: ${tmpFile}`);
|
|
24
32
|
throw e;
|
|
25
33
|
}
|
|
26
34
|
}
|
|
27
35
|
else if (process.platform === 'linux') {
|
|
28
|
-
const tmpFile = path.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
29
|
-
fs.writeFileSync(tmpFile, text);
|
|
30
36
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
stdio: 'ignore',
|
|
34
|
-
});
|
|
35
|
-
child.unref();
|
|
37
|
+
fs.writeFileSync(tmpFile, text);
|
|
38
|
+
spawnWithCleanup('sh', ['-c', `(xclip -selection clipboard < "${tmpFile}" || xsel --clipboard --input < "${tmpFile}") && rm "${tmpFile}"`]);
|
|
36
39
|
}
|
|
37
|
-
catch {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
detached: true,
|
|
41
|
-
stdio: 'ignore',
|
|
42
|
-
});
|
|
43
|
-
child.unref();
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
// Final fallback if both xclip and xsel fail
|
|
47
|
-
console.log(`Output saved to: ${tmpFile}`);
|
|
48
|
-
throw e;
|
|
49
|
-
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.log(`Output saved to: ${tmpFile}`);
|
|
42
|
+
throw e;
|
|
50
43
|
}
|
|
51
44
|
}
|
|
52
45
|
else if (process.platform === 'win32') {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
try {
|
|
47
|
+
fs.writeFileSync(tmpFile, text);
|
|
48
|
+
spawnWithCleanup('cmd', ['/c', `type "${tmpFile}" | clip && del "${tmpFile}"`]);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.log(`Output saved to: ${tmpFile}`);
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
60
54
|
}
|
|
61
55
|
}
|
|
@@ -16,9 +16,10 @@ export function createEventsRouter(sseManager) {
|
|
|
16
16
|
const heartbeat = setInterval(() => {
|
|
17
17
|
res.write(':heartbeat\n\n');
|
|
18
18
|
}, 30000);
|
|
19
|
-
// Cleanup on close (removeClient is handled by SSEManager)
|
|
19
|
+
// Cleanup on close (removeClient is handled by SSEManager's close listener)
|
|
20
20
|
req.on('close', () => {
|
|
21
21
|
clearInterval(heartbeat);
|
|
22
|
+
res.end();
|
|
22
23
|
});
|
|
23
24
|
});
|
|
24
25
|
return router;
|
|
@@ -8,10 +8,14 @@ export function createStaticRouter() {
|
|
|
8
8
|
// Get __dirname equivalent in ESM
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = path.dirname(__filename);
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// First, check for the bundled path. This is more robust than checking the filename.
|
|
12
|
+
// - Bundled: dist/flip-ui/dist
|
|
13
|
+
let distPath = path.resolve(__dirname, 'flip-ui/dist');
|
|
14
|
+
// If the bundled path doesn't exist, fall back to the unbundled (development) path.
|
|
15
|
+
// - Unbundled: packages/cli/flip-ui/dist
|
|
16
|
+
if (!fs.existsSync(distPath)) {
|
|
17
|
+
distPath = path.resolve(__dirname, '../../../flip-ui/dist');
|
|
18
|
+
}
|
|
15
19
|
// Check if dist exists (for development vs production)
|
|
16
20
|
if (fs.existsSync(distPath)) {
|
|
17
21
|
// Serve static files
|
|
@@ -8,4 +8,4 @@ export declare class Server {
|
|
|
8
8
|
constructor(cwd: string, port: number);
|
|
9
9
|
run(): Promise<string | null>;
|
|
10
10
|
}
|
|
11
|
-
export declare function findFreePort(preferred: number): Promise<number>;
|
|
11
|
+
export declare function findFreePort(preferred: number, maxPort?: number): Promise<number>;
|
|
@@ -69,8 +69,12 @@ export class Server {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
export async function findFreePort(preferred) {
|
|
73
|
-
return new Promise((resolve) => {
|
|
72
|
+
export async function findFreePort(preferred, maxPort = 65535) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
if (preferred > maxPort) {
|
|
75
|
+
reject(new Error(`No available port found in range up to ${maxPort}`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
74
78
|
const server = net.createServer();
|
|
75
79
|
server.listen(preferred, '127.0.0.1', () => {
|
|
76
80
|
server.close(() => {
|
|
@@ -78,8 +82,9 @@ export async function findFreePort(preferred) {
|
|
|
78
82
|
});
|
|
79
83
|
});
|
|
80
84
|
server.on('error', () => {
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
server.close(() => {
|
|
86
|
+
findFreePort(preferred + 1, maxPort).then(resolve).catch(reject);
|
|
87
|
+
});
|
|
83
88
|
});
|
|
84
89
|
});
|
|
85
90
|
}
|
package/dist/index.js
CHANGED
|
@@ -923,48 +923,46 @@ import fs5 from "fs";
|
|
|
923
923
|
import path5 from "path";
|
|
924
924
|
import os from "os";
|
|
925
925
|
async function copyToClipboard(text) {
|
|
926
|
+
const tmpFile = path5.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
927
|
+
const cleanupTmpFile = () => {
|
|
928
|
+
try {
|
|
929
|
+
fs5.unlinkSync(tmpFile);
|
|
930
|
+
} catch {
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
const spawnWithCleanup = (command, args) => {
|
|
934
|
+
const child = spawn(command, args, {
|
|
935
|
+
detached: true,
|
|
936
|
+
stdio: "ignore"
|
|
937
|
+
});
|
|
938
|
+
child.on("error", cleanupTmpFile);
|
|
939
|
+
child.on("exit", cleanupTmpFile);
|
|
940
|
+
child.unref();
|
|
941
|
+
};
|
|
926
942
|
if (process.platform === "darwin") {
|
|
927
|
-
const tmpFile = path5.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
928
943
|
try {
|
|
929
944
|
fs5.writeFileSync(tmpFile, text);
|
|
930
|
-
|
|
931
|
-
detached: true,
|
|
932
|
-
stdio: "ignore"
|
|
933
|
-
});
|
|
934
|
-
child.unref();
|
|
945
|
+
spawnWithCleanup("sh", ["-c", `cat "${tmpFile}" | pbcopy && rm "${tmpFile}"`]);
|
|
935
946
|
} catch (e) {
|
|
936
947
|
console.log(`Output saved to: ${tmpFile}`);
|
|
937
948
|
throw e;
|
|
938
949
|
}
|
|
939
950
|
} else if (process.platform === "linux") {
|
|
940
|
-
const tmpFile = path5.join(os.tmpdir(), `flip-clipboard-${Date.now()}.txt`);
|
|
941
|
-
fs5.writeFileSync(tmpFile, text);
|
|
942
951
|
try {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
} catch {
|
|
949
|
-
try {
|
|
950
|
-
const child = spawn("sh", ["-c", `cat "${tmpFile}" | xsel --clipboard --input && rm "${tmpFile}"`], {
|
|
951
|
-
detached: true,
|
|
952
|
-
stdio: "ignore"
|
|
953
|
-
});
|
|
954
|
-
child.unref();
|
|
955
|
-
} catch (e) {
|
|
956
|
-
console.log(`Output saved to: ${tmpFile}`);
|
|
957
|
-
throw e;
|
|
958
|
-
}
|
|
952
|
+
fs5.writeFileSync(tmpFile, text);
|
|
953
|
+
spawnWithCleanup("sh", ["-c", `(xclip -selection clipboard < "${tmpFile}" || xsel --clipboard --input < "${tmpFile}") && rm "${tmpFile}"`]);
|
|
954
|
+
} catch (e) {
|
|
955
|
+
console.log(`Output saved to: ${tmpFile}`);
|
|
956
|
+
throw e;
|
|
959
957
|
}
|
|
960
958
|
} else if (process.platform === "win32") {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
959
|
+
try {
|
|
960
|
+
fs5.writeFileSync(tmpFile, text);
|
|
961
|
+
spawnWithCleanup("cmd", ["/c", `type "${tmpFile}" | clip && del "${tmpFile}"`]);
|
|
962
|
+
} catch (e) {
|
|
963
|
+
console.log(`Output saved to: ${tmpFile}`);
|
|
964
|
+
throw e;
|
|
965
|
+
}
|
|
968
966
|
}
|
|
969
967
|
}
|
|
970
968
|
|
|
@@ -1030,17 +1028,20 @@ async function pasteToOriginalSession(sessionId) {
|
|
|
1030
1028
|
try {
|
|
1031
1029
|
const tmpScript = path6.join(os2.tmpdir(), `flip-paste-${Date.now()}.scpt`);
|
|
1032
1030
|
fs6.writeFileSync(tmpScript, script);
|
|
1031
|
+
const cleanupScript = () => {
|
|
1032
|
+
try {
|
|
1033
|
+
fs6.unlinkSync(tmpScript);
|
|
1034
|
+
} catch {
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1033
1037
|
const child = spawn2("osascript", [tmpScript], {
|
|
1034
1038
|
detached: true,
|
|
1035
1039
|
stdio: "ignore"
|
|
1036
1040
|
});
|
|
1041
|
+
child.on("exit", cleanupScript);
|
|
1042
|
+
child.on("error", cleanupScript);
|
|
1037
1043
|
child.unref();
|
|
1038
|
-
setTimeout(
|
|
1039
|
-
try {
|
|
1040
|
-
fs6.unlinkSync(tmpScript);
|
|
1041
|
-
} catch {
|
|
1042
|
-
}
|
|
1043
|
-
}, 5e3);
|
|
1044
|
+
setTimeout(cleanupScript, 1e4);
|
|
1044
1045
|
} catch (e) {
|
|
1045
1046
|
console.error("Failed to paste to iTerm:", e);
|
|
1046
1047
|
}
|
|
@@ -1106,7 +1107,10 @@ function createStaticRouter() {
|
|
|
1106
1107
|
const router6 = Router6();
|
|
1107
1108
|
const __filename = fileURLToPath(import.meta.url);
|
|
1108
1109
|
const __dirname = path7.dirname(__filename);
|
|
1109
|
-
|
|
1110
|
+
let distPath = path7.resolve(__dirname, "flip-ui/dist");
|
|
1111
|
+
if (!fs7.existsSync(distPath)) {
|
|
1112
|
+
distPath = path7.resolve(__dirname, "../../../flip-ui/dist");
|
|
1113
|
+
}
|
|
1110
1114
|
if (fs7.existsSync(distPath)) {
|
|
1111
1115
|
router6.use(express.static(distPath));
|
|
1112
1116
|
router6.get("*", (req, res) => {
|
|
@@ -1141,6 +1145,7 @@ function createEventsRouter(sseManager) {
|
|
|
1141
1145
|
}, 3e4);
|
|
1142
1146
|
req.on("close", () => {
|
|
1143
1147
|
clearInterval(heartbeat);
|
|
1148
|
+
res.end();
|
|
1144
1149
|
});
|
|
1145
1150
|
});
|
|
1146
1151
|
return router6;
|
|
@@ -1344,8 +1349,12 @@ var Server = class {
|
|
|
1344
1349
|
});
|
|
1345
1350
|
}
|
|
1346
1351
|
};
|
|
1347
|
-
async function findFreePort(preferred) {
|
|
1348
|
-
return new Promise((resolve2) => {
|
|
1352
|
+
async function findFreePort(preferred, maxPort = 65535) {
|
|
1353
|
+
return new Promise((resolve2, reject) => {
|
|
1354
|
+
if (preferred > maxPort) {
|
|
1355
|
+
reject(new Error(`No available port found in range up to ${maxPort}`));
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1349
1358
|
const server = net.createServer();
|
|
1350
1359
|
server.listen(preferred, "127.0.0.1", () => {
|
|
1351
1360
|
server.close(() => {
|
|
@@ -1353,7 +1362,9 @@ async function findFreePort(preferred) {
|
|
|
1353
1362
|
});
|
|
1354
1363
|
});
|
|
1355
1364
|
server.on("error", () => {
|
|
1356
|
-
|
|
1365
|
+
server.close(() => {
|
|
1366
|
+
findFreePort(preferred + 1, maxPort).then(resolve2).catch(reject);
|
|
1367
|
+
});
|
|
1357
1368
|
});
|
|
1358
1369
|
});
|
|
1359
1370
|
}
|