crosspad-mcp-server 6.0.0 → 8.1.1
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/README.md +109 -44
- package/dist/config.d.ts +2 -2
- package/dist/config.js +42 -8
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +956 -312
- package/dist/index.js.map +1 -1
- package/dist/tools/app-manager.d.ts +13 -5
- package/dist/tools/app-manager.js +69 -46
- package/dist/tools/app-manager.js.map +1 -1
- package/dist/tools/architecture.js +13 -4
- package/dist/tools/architecture.js.map +1 -1
- package/dist/tools/build-check.d.ts +1 -1
- package/dist/tools/build-check.js +19 -9
- package/dist/tools/build-check.js.map +1 -1
- package/dist/tools/build.d.ts +95 -4
- package/dist/tools/build.js +312 -15
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/diff-core.d.ts +5 -1
- package/dist/tools/diff-core.js +11 -6
- package/dist/tools/diff-core.js.map +1 -1
- package/dist/tools/idf-build.d.ts +1 -1
- package/dist/tools/idf-build.js +5 -5
- package/dist/tools/idf-build.js.map +1 -1
- package/dist/tools/idf-flash.d.ts +2 -2
- package/dist/tools/idf-flash.js +12 -11
- package/dist/tools/idf-flash.js.map +1 -1
- package/dist/tools/idf-monitor.d.ts +1 -1
- package/dist/tools/idf-monitor.js +57 -3
- package/dist/tools/idf-monitor.js.map +1 -1
- package/dist/tools/log.d.ts +2 -2
- package/dist/tools/log.js +6 -5
- package/dist/tools/log.js.map +1 -1
- package/dist/tools/repo-actions.js +88 -75
- package/dist/tools/repo-actions.js.map +1 -1
- package/dist/tools/repos.js +14 -10
- package/dist/tools/repos.js.map +1 -1
- package/dist/tools/screenshot.js +42 -7
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/settings.js +11 -1
- package/dist/tools/settings.js.map +1 -1
- package/dist/tools/symbols.js +13 -8
- package/dist/tools/symbols.js.map +1 -1
- package/dist/tools/test.d.ts +1 -9
- package/dist/tools/test.js +11 -92
- package/dist/tools/test.js.map +1 -1
- package/dist/utils/exec.d.ts +17 -5
- package/dist/utils/exec.js +73 -27
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.js +61 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/remote-client.js +32 -14
- package/dist/utils/remote-client.js.map +1 -1
- package/package.json +4 -1
- package/dist/tools/scaffold.d.ts +0 -15
- package/dist/tools/scaffold.js +0 -192
- package/dist/tools/scaffold.js.map +0 -1
package/dist/tools/build.d.ts
CHANGED
|
@@ -6,13 +6,104 @@ export interface BuildResult {
|
|
|
6
6
|
warnings_count: number;
|
|
7
7
|
output_path: string;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Match real compiler / linker / build-system errors only. Keyword-only
|
|
11
|
+
* matching ("any line containing 'error'") is too greedy — it picks up
|
|
12
|
+
* comments like `// error handling` from cmake output and inflates the
|
|
13
|
+
* error list.
|
|
14
|
+
*
|
|
15
|
+
* Patterns: GCC/Clang/MSVC compiler diagnostics, linker errors, CMake
|
|
16
|
+
* Errors, Ninja FAILED markers.
|
|
17
|
+
*
|
|
18
|
+
* @internal exported for testing
|
|
19
|
+
*/
|
|
10
20
|
export declare function parseErrors(output: string): string[];
|
|
11
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Count compiler warnings only — same tightening logic as parseErrors.
|
|
23
|
+
* @internal exported for testing
|
|
24
|
+
*/
|
|
12
25
|
export declare function countWarnings(output: string): number;
|
|
13
|
-
export
|
|
26
|
+
export type BuildType = "Debug" | "Release" | "RelWithDebInfo";
|
|
27
|
+
export declare function crosspadBuild(mode: "incremental" | "clean" | "reconfigure", onLine?: OnLine, buildType?: BuildType, signal?: AbortSignal): Promise<BuildResult>;
|
|
14
28
|
export interface RunResult {
|
|
15
29
|
pid: number | null;
|
|
16
30
|
exe_path: string;
|
|
31
|
+
already_running?: boolean;
|
|
32
|
+
responsive?: boolean;
|
|
33
|
+
error?: string;
|
|
17
34
|
}
|
|
18
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Launch the simulator binary in the background.
|
|
37
|
+
*
|
|
38
|
+
* Refuses to spawn a second instance if one is already responding on the
|
|
39
|
+
* remote-control port — multiple instances clobber each other's window
|
|
40
|
+
* state and the TCP listener binds to the same port.
|
|
41
|
+
*
|
|
42
|
+
* After spawn we poll the TCP control port for up to ~3s — this distinguishes
|
|
43
|
+
* "process started, ready to accept commands" from "process started but
|
|
44
|
+
* crashed before binding" so callers don't fire screenshot/stats too early.
|
|
45
|
+
*/
|
|
46
|
+
export declare function crosspadRun(force?: boolean): Promise<RunResult>;
|
|
47
|
+
export interface KillResult {
|
|
48
|
+
success: boolean;
|
|
49
|
+
killed_pids: number[];
|
|
50
|
+
was_running: boolean;
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Canonicalize a filesystem path. realpath resolves every symlink in the
|
|
55
|
+
* chain — needed because /proc/<pid>/exe always returns the kernel's view
|
|
56
|
+
* (post-symlink) while BIN_EXE comes from string concatenation under
|
|
57
|
+
* CROSSPAD_PC_ROOT, which is commonly itself a symlink (e.g. ~/GIT/crosspad-pc
|
|
58
|
+
* → /mnt/big-disk/crosspad-pc). Without canonicalization the string compare
|
|
59
|
+
* silently misses every running sim.
|
|
60
|
+
*
|
|
61
|
+
* Falls back to path.resolve when realpath fails (binary not built yet, or
|
|
62
|
+
* a non-existent /proc path during a TOCTOU race) so callers always get a
|
|
63
|
+
* comparable absolute path.
|
|
64
|
+
*
|
|
65
|
+
* @internal exported for testing
|
|
66
|
+
*/
|
|
67
|
+
export declare function canonicalize(p: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Strip the kernel's " (deleted)" suffix that appears in /proc/<pid>/exe
|
|
70
|
+
* when the executable was unlinked or replaced after the process started
|
|
71
|
+
* (typical during dev: rebuild while sim is still running). Without this
|
|
72
|
+
* strip the path compare misses any running-but-rebuilt sim — the most
|
|
73
|
+
* common reason a developer would hit "agent can't kill the simulator."
|
|
74
|
+
*
|
|
75
|
+
* @internal exported for testing
|
|
76
|
+
*/
|
|
77
|
+
export declare function stripDeletedSuffix(p: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Find PIDs running the CrossPad simulator binary.
|
|
80
|
+
*
|
|
81
|
+
* On Linux scans /proc/<pid>/exe and matches the resolved+canonicalized
|
|
82
|
+
* symlink against BIN_EXE. This is the only reliable identification:
|
|
83
|
+
* pgrep -x compares /proc/<pid>/comm, which Qt/pthread routinely overwrite
|
|
84
|
+
* via pthread_setname_np / prctl(PR_SET_NAME), so a sim launched as
|
|
85
|
+
* "CrossPad" shows up under whatever Qt named the main thread last.
|
|
86
|
+
* /proc/<pid>/exe is the kernel's record of the executed binary and cannot
|
|
87
|
+
* be spoofed by userspace renames.
|
|
88
|
+
*
|
|
89
|
+
* On macOS/Windows there's no /proc, so fall back to pgrep by basename.
|
|
90
|
+
* macOS suffers the same Qt comm-rename in theory but our binary name is
|
|
91
|
+
* 8 chars (fits in comm's 15-char limit) and PC builds are predominantly
|
|
92
|
+
* exercised on Linux, so this is documented as best-effort.
|
|
93
|
+
*
|
|
94
|
+
* @internal exported for testing
|
|
95
|
+
*/
|
|
96
|
+
export declare function findCrosspadPids(): number[];
|
|
97
|
+
/**
|
|
98
|
+
* Kill the running PC simulator. SIGTERM matched PIDs, poll up to 3s for
|
|
99
|
+
* graceful exit, SIGKILL stragglers. Detection uses both /proc and the TCP
|
|
100
|
+
* control port — either signal alone has been observed to lie (TCP port
|
|
101
|
+
* occasionally lingers after the binary exits, and a crashed process may
|
|
102
|
+
* keep its socket past the syscall return).
|
|
103
|
+
*
|
|
104
|
+
* Per-PID kill errors (EPERM, EUNKNOWN) are aggregated and surfaced in
|
|
105
|
+
* `error` so the caller sees *why* a kill didn't take, not just "still
|
|
106
|
+
* running" — historically this was the hardest kill failure to debug
|
|
107
|
+
* because Node swallowed EPERM and the user just saw success=false.
|
|
108
|
+
*/
|
|
109
|
+
export declare function crosspadKill(): Promise<KillResult>;
|
package/dist/tools/build.js
CHANGED
|
@@ -1,27 +1,84 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
2
4
|
import { CROSSPAD_PC_ROOT, BUILD_DIR, BIN_EXE, VCPKG_TOOLCHAIN } from "../config.js";
|
|
3
5
|
import { runBuild, runBuildStream, spawnDetached } from "../utils/exec.js";
|
|
4
|
-
|
|
6
|
+
import { isSimulatorRunning } from "../utils/remote-client.js";
|
|
7
|
+
/**
|
|
8
|
+
* Match real compiler / linker / build-system errors only. Keyword-only
|
|
9
|
+
* matching ("any line containing 'error'") is too greedy — it picks up
|
|
10
|
+
* comments like `// error handling` from cmake output and inflates the
|
|
11
|
+
* error list.
|
|
12
|
+
*
|
|
13
|
+
* Patterns: GCC/Clang/MSVC compiler diagnostics, linker errors, CMake
|
|
14
|
+
* Errors, Ninja FAILED markers.
|
|
15
|
+
*
|
|
16
|
+
* @internal exported for testing
|
|
17
|
+
*/
|
|
5
18
|
export function parseErrors(output) {
|
|
6
19
|
const errors = [];
|
|
7
|
-
for (const
|
|
8
|
-
|
|
9
|
-
|
|
20
|
+
for (const raw of output.split("\n")) {
|
|
21
|
+
const line = raw.trim();
|
|
22
|
+
if (!line)
|
|
23
|
+
continue;
|
|
24
|
+
// GCC/Clang: foo.cpp:10:5: error: ... (also "fatal error:")
|
|
25
|
+
if (/:\d+:\d+:\s*(?:fatal\s+)?error\s*:/i.test(line)) {
|
|
26
|
+
errors.push(line);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
// MSVC: foo.cpp(10,5): error C1234: ...
|
|
30
|
+
if (/\(\d+(?:,\d+)?\):\s*(?:fatal\s+)?error\s+[A-Z]\d+\s*:/i.test(line)) {
|
|
31
|
+
errors.push(line);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Linker
|
|
35
|
+
if (/undefined reference to /.test(line)) {
|
|
36
|
+
errors.push(line);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (/^.*ld(?:\.exe)?\s*:\s*error\s*:/i.test(line)) {
|
|
40
|
+
errors.push(line);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (/LNK\d+:\s*error\b/i.test(line)) {
|
|
44
|
+
errors.push(line);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// CMake
|
|
48
|
+
if (/^CMake Error\b/i.test(line)) {
|
|
49
|
+
errors.push(line);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// Ninja
|
|
53
|
+
if (/^FAILED:\s/.test(line)) {
|
|
54
|
+
errors.push(line);
|
|
55
|
+
continue;
|
|
10
56
|
}
|
|
11
57
|
}
|
|
12
|
-
return errors.slice(0, 20);
|
|
58
|
+
return errors.slice(0, 20);
|
|
13
59
|
}
|
|
14
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Count compiler warnings only — same tightening logic as parseErrors.
|
|
62
|
+
* @internal exported for testing
|
|
63
|
+
*/
|
|
15
64
|
export function countWarnings(output) {
|
|
16
65
|
let count = 0;
|
|
17
|
-
for (const
|
|
18
|
-
|
|
66
|
+
for (const raw of output.split("\n")) {
|
|
67
|
+
const line = raw.trim();
|
|
68
|
+
if (!line)
|
|
69
|
+
continue;
|
|
70
|
+
if (/:\d+:\d+:\s*warning\s*:/i.test(line)) {
|
|
19
71
|
count++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (/\(\d+(?:,\d+)?\):\s*warning\s+[A-Z]\d+\s*:/i.test(line)) {
|
|
75
|
+
count++;
|
|
76
|
+
continue;
|
|
20
77
|
}
|
|
21
78
|
}
|
|
22
79
|
return count;
|
|
23
80
|
}
|
|
24
|
-
export async function crosspadBuild(mode, onLine) {
|
|
81
|
+
export async function crosspadBuild(mode, onLine, buildType = "Debug", signal) {
|
|
25
82
|
const startTime = Date.now();
|
|
26
83
|
// Clean: remove build dir
|
|
27
84
|
if (mode === "clean" && fs.existsSync(BUILD_DIR)) {
|
|
@@ -33,11 +90,11 @@ export async function crosspadBuild(mode, onLine) {
|
|
|
33
90
|
const configCmd = [
|
|
34
91
|
"cmake -B build -G Ninja",
|
|
35
92
|
`-DCMAKE_TOOLCHAIN_FILE=${VCPKG_TOOLCHAIN}`,
|
|
36
|
-
|
|
93
|
+
`-DCMAKE_BUILD_TYPE=${buildType}`,
|
|
37
94
|
].join(" ");
|
|
38
|
-
onLine?.("stdout", `[crosspad] Configuring: ${mode}...`);
|
|
95
|
+
onLine?.("stdout", `[crosspad] Configuring: ${mode} (${buildType})...`);
|
|
39
96
|
if (onLine) {
|
|
40
|
-
const configResult = await runBuildStream(configCmd, CROSSPAD_PC_ROOT, onLine, 600_000);
|
|
97
|
+
const configResult = await runBuildStream(configCmd, CROSSPAD_PC_ROOT, onLine, 600_000, signal);
|
|
41
98
|
if (!configResult.success) {
|
|
42
99
|
const combined = configResult.stdout + "\n" + configResult.stderr;
|
|
43
100
|
return {
|
|
@@ -69,7 +126,7 @@ export async function crosspadBuild(mode, onLine) {
|
|
|
69
126
|
let buildStderr;
|
|
70
127
|
let buildSuccess;
|
|
71
128
|
if (onLine) {
|
|
72
|
-
const buildResult = await runBuildStream("cmake --build build", CROSSPAD_PC_ROOT, onLine, 600_000);
|
|
129
|
+
const buildResult = await runBuildStream("cmake --build build", CROSSPAD_PC_ROOT, onLine, 600_000, signal);
|
|
73
130
|
buildStdout = buildResult.stdout;
|
|
74
131
|
buildStderr = buildResult.stderr;
|
|
75
132
|
buildSuccess = buildResult.success;
|
|
@@ -91,11 +148,251 @@ export async function crosspadBuild(mode, onLine) {
|
|
|
91
148
|
onLine?.("stdout", `[crosspad] Build ${result.success ? "succeeded" : "FAILED"} in ${result.duration_seconds.toFixed(1)}s`);
|
|
92
149
|
return result;
|
|
93
150
|
}
|
|
94
|
-
|
|
151
|
+
function delay(ms) {
|
|
152
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Launch the simulator binary in the background.
|
|
156
|
+
*
|
|
157
|
+
* Refuses to spawn a second instance if one is already responding on the
|
|
158
|
+
* remote-control port — multiple instances clobber each other's window
|
|
159
|
+
* state and the TCP listener binds to the same port.
|
|
160
|
+
*
|
|
161
|
+
* After spawn we poll the TCP control port for up to ~3s — this distinguishes
|
|
162
|
+
* "process started, ready to accept commands" from "process started but
|
|
163
|
+
* crashed before binding" so callers don't fire screenshot/stats too early.
|
|
164
|
+
*/
|
|
165
|
+
export async function crosspadRun(force = false) {
|
|
95
166
|
if (!fs.existsSync(BIN_EXE)) {
|
|
96
167
|
return { pid: null, exe_path: BIN_EXE };
|
|
97
168
|
}
|
|
169
|
+
if (!force && (await isSimulatorRunning())) {
|
|
170
|
+
return {
|
|
171
|
+
pid: null,
|
|
172
|
+
exe_path: BIN_EXE,
|
|
173
|
+
already_running: true,
|
|
174
|
+
error: "Simulator already running on port 19840. Pass force=true to spawn another instance anyway.",
|
|
175
|
+
};
|
|
176
|
+
}
|
|
98
177
|
const pid = spawnDetached(BIN_EXE, [], CROSSPAD_PC_ROOT);
|
|
99
|
-
|
|
178
|
+
if (pid === null)
|
|
179
|
+
return { pid, exe_path: BIN_EXE };
|
|
180
|
+
// Poll for TCP readiness so callers know if the sim actually came up.
|
|
181
|
+
let responsive = false;
|
|
182
|
+
for (let i = 0; i < 6; i++) {
|
|
183
|
+
await delay(500);
|
|
184
|
+
if (await isSimulatorRunning()) {
|
|
185
|
+
responsive = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { pid, exe_path: BIN_EXE, responsive };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Canonicalize a filesystem path. realpath resolves every symlink in the
|
|
193
|
+
* chain — needed because /proc/<pid>/exe always returns the kernel's view
|
|
194
|
+
* (post-symlink) while BIN_EXE comes from string concatenation under
|
|
195
|
+
* CROSSPAD_PC_ROOT, which is commonly itself a symlink (e.g. ~/GIT/crosspad-pc
|
|
196
|
+
* → /mnt/big-disk/crosspad-pc). Without canonicalization the string compare
|
|
197
|
+
* silently misses every running sim.
|
|
198
|
+
*
|
|
199
|
+
* Falls back to path.resolve when realpath fails (binary not built yet, or
|
|
200
|
+
* a non-existent /proc path during a TOCTOU race) so callers always get a
|
|
201
|
+
* comparable absolute path.
|
|
202
|
+
*
|
|
203
|
+
* @internal exported for testing
|
|
204
|
+
*/
|
|
205
|
+
export function canonicalize(p) {
|
|
206
|
+
try {
|
|
207
|
+
return fs.realpathSync(p);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return path.resolve(p);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Strip the kernel's " (deleted)" suffix that appears in /proc/<pid>/exe
|
|
215
|
+
* when the executable was unlinked or replaced after the process started
|
|
216
|
+
* (typical during dev: rebuild while sim is still running). Without this
|
|
217
|
+
* strip the path compare misses any running-but-rebuilt sim — the most
|
|
218
|
+
* common reason a developer would hit "agent can't kill the simulator."
|
|
219
|
+
*
|
|
220
|
+
* @internal exported for testing
|
|
221
|
+
*/
|
|
222
|
+
export function stripDeletedSuffix(p) {
|
|
223
|
+
return p.replace(/ \(deleted\)$/, "");
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Find PIDs running the CrossPad simulator binary.
|
|
227
|
+
*
|
|
228
|
+
* On Linux scans /proc/<pid>/exe and matches the resolved+canonicalized
|
|
229
|
+
* symlink against BIN_EXE. This is the only reliable identification:
|
|
230
|
+
* pgrep -x compares /proc/<pid>/comm, which Qt/pthread routinely overwrite
|
|
231
|
+
* via pthread_setname_np / prctl(PR_SET_NAME), so a sim launched as
|
|
232
|
+
* "CrossPad" shows up under whatever Qt named the main thread last.
|
|
233
|
+
* /proc/<pid>/exe is the kernel's record of the executed binary and cannot
|
|
234
|
+
* be spoofed by userspace renames.
|
|
235
|
+
*
|
|
236
|
+
* On macOS/Windows there's no /proc, so fall back to pgrep by basename.
|
|
237
|
+
* macOS suffers the same Qt comm-rename in theory but our binary name is
|
|
238
|
+
* 8 chars (fits in comm's 15-char limit) and PC builds are predominantly
|
|
239
|
+
* exercised on Linux, so this is documented as best-effort.
|
|
240
|
+
*
|
|
241
|
+
* @internal exported for testing
|
|
242
|
+
*/
|
|
243
|
+
export function findCrosspadPids() {
|
|
244
|
+
if (process.platform === "linux") {
|
|
245
|
+
const target = canonicalize(BIN_EXE);
|
|
246
|
+
let entries;
|
|
247
|
+
try {
|
|
248
|
+
entries = fs.readdirSync("/proc");
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
const pids = [];
|
|
254
|
+
const self = process.pid;
|
|
255
|
+
for (const entry of entries) {
|
|
256
|
+
// /proc has many non-pid entries (cpuinfo, self, etc.) — skip anything
|
|
257
|
+
// that doesn't round-trip as an integer. Also skip our own PID; node
|
|
258
|
+
// can't be CrossPad but defensive in case BIN_EXE is misconfigured to
|
|
259
|
+
// /usr/bin/node during testing.
|
|
260
|
+
if (!/^\d+$/.test(entry))
|
|
261
|
+
continue;
|
|
262
|
+
const pid = parseInt(entry, 10);
|
|
263
|
+
if (pid === self)
|
|
264
|
+
continue;
|
|
265
|
+
try {
|
|
266
|
+
const raw = fs.readlinkSync(`/proc/${pid}/exe`);
|
|
267
|
+
const exe = canonicalize(stripDeletedSuffix(raw));
|
|
268
|
+
if (exe === target)
|
|
269
|
+
pids.push(pid);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Process exited mid-scan, or no permission. Either is fine.
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return pids;
|
|
276
|
+
}
|
|
277
|
+
// macOS / Windows / other: pgrep by basename. Strip .exe so the same
|
|
278
|
+
// binary works under wine/CI and on real Windows (where pgrep is absent
|
|
279
|
+
// — spawnSync.error is caught and we return []).
|
|
280
|
+
try {
|
|
281
|
+
const base = path.basename(BIN_EXE).replace(/\.exe$/i, "");
|
|
282
|
+
const r = spawnSync("pgrep", ["-x", base], { encoding: "utf-8", timeout: 5000 });
|
|
283
|
+
return (r.stdout || "")
|
|
284
|
+
.split("\n")
|
|
285
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
286
|
+
.filter((n) => Number.isFinite(n));
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Try to deliver a signal; returns the errno code on failure, undefined on
|
|
294
|
+
* success. ESRCH is folded into success ("already dead" is the desired state).
|
|
295
|
+
*
|
|
296
|
+
* @internal
|
|
297
|
+
*/
|
|
298
|
+
function trySignal(pid, signal) {
|
|
299
|
+
try {
|
|
300
|
+
process.kill(pid, signal);
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
const code = e?.code;
|
|
305
|
+
if (code === "ESRCH")
|
|
306
|
+
return undefined; // already exited — fine
|
|
307
|
+
return code ?? "EUNKNOWN";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Kill the running PC simulator. SIGTERM matched PIDs, poll up to 3s for
|
|
312
|
+
* graceful exit, SIGKILL stragglers. Detection uses both /proc and the TCP
|
|
313
|
+
* control port — either signal alone has been observed to lie (TCP port
|
|
314
|
+
* occasionally lingers after the binary exits, and a crashed process may
|
|
315
|
+
* keep its socket past the syscall return).
|
|
316
|
+
*
|
|
317
|
+
* Per-PID kill errors (EPERM, EUNKNOWN) are aggregated and surfaced in
|
|
318
|
+
* `error` so the caller sees *why* a kill didn't take, not just "still
|
|
319
|
+
* running" — historically this was the hardest kill failure to debug
|
|
320
|
+
* because Node swallowed EPERM and the user just saw success=false.
|
|
321
|
+
*/
|
|
322
|
+
export async function crosspadKill() {
|
|
323
|
+
const initialPids = findCrosspadPids();
|
|
324
|
+
const tcpAliveInitial = await isSimulatorRunning();
|
|
325
|
+
const wasRunning = initialPids.length > 0 || tcpAliveInitial;
|
|
326
|
+
if (!wasRunning) {
|
|
327
|
+
return { success: true, killed_pids: [], was_running: false };
|
|
328
|
+
}
|
|
329
|
+
const killedPids = [];
|
|
330
|
+
const failures = [];
|
|
331
|
+
for (const pid of initialPids) {
|
|
332
|
+
const errCode = trySignal(pid, "SIGTERM");
|
|
333
|
+
if (errCode === undefined) {
|
|
334
|
+
killedPids.push(pid);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
failures.push(`SIGTERM pid=${pid} ${errCode}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Poll up to 3s for SIGTERM to take effect. The first poll runs after a
|
|
341
|
+
// single 150 ms beat; this is enough for a Qt event loop to acknowledge
|
|
342
|
+
// SIGTERM in the common case so the kill returns fast.
|
|
343
|
+
const deadline = Date.now() + 3000;
|
|
344
|
+
let stillAlive = initialPids;
|
|
345
|
+
while (Date.now() < deadline) {
|
|
346
|
+
await delay(150);
|
|
347
|
+
stillAlive = findCrosspadPids();
|
|
348
|
+
if (stillAlive.length === 0) {
|
|
349
|
+
// /proc is clean — confirm the TCP port is gone too. We only pay the
|
|
350
|
+
// TCP probe in this branch to avoid stretching the polling deadline:
|
|
351
|
+
// isSimulatorRunning can take seconds when the sim is hung.
|
|
352
|
+
if (!(await isSimulatorRunning()))
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Force-kill anything that survived SIGTERM. Re-check /proc/<pid>/exe by
|
|
357
|
+
// way of findCrosspadPids (already done above) — guards against PID
|
|
358
|
+
// recycling in the gap between SIGTERM and SIGKILL.
|
|
359
|
+
if (stillAlive.length > 0) {
|
|
360
|
+
for (const pid of stillAlive) {
|
|
361
|
+
const errCode = trySignal(pid, "SIGKILL");
|
|
362
|
+
if (errCode === undefined) {
|
|
363
|
+
if (!killedPids.includes(pid))
|
|
364
|
+
killedPids.push(pid);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
failures.push(`SIGKILL pid=${pid} ${errCode}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await delay(300);
|
|
371
|
+
}
|
|
372
|
+
const finalAlive = findCrosspadPids();
|
|
373
|
+
const tcpAliveFinal = await isSimulatorRunning();
|
|
374
|
+
const stillRunning = finalAlive.length > 0 || tcpAliveFinal;
|
|
375
|
+
let errorMsg;
|
|
376
|
+
if (stillRunning) {
|
|
377
|
+
const parts = [
|
|
378
|
+
`Simulator still alive after SIGTERM+SIGKILL`,
|
|
379
|
+
`pids=${finalAlive.join(",") || "none"}`,
|
|
380
|
+
`tcp_alive=${tcpAliveFinal}`,
|
|
381
|
+
];
|
|
382
|
+
if (failures.length > 0)
|
|
383
|
+
parts.push(`failures=[${failures.join("; ")}]`);
|
|
384
|
+
errorMsg = parts.join(", ") + ".";
|
|
385
|
+
}
|
|
386
|
+
else if (failures.length > 0) {
|
|
387
|
+
// Sim is dead but some kill attempts errored (e.g. PID gone before our
|
|
388
|
+
// SIGKILL). Worth surfacing but not a hard failure.
|
|
389
|
+
errorMsg = `Sim stopped, but some signals errored: ${failures.join("; ")}.`;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
success: !stillRunning,
|
|
393
|
+
killed_pids: killedPids,
|
|
394
|
+
was_running: true,
|
|
395
|
+
error: errorMsg,
|
|
396
|
+
};
|
|
100
397
|
}
|
|
101
398
|
//# sourceMappingURL=build.js.map
|
package/dist/tools/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/tools/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/tools/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,11 +14,15 @@ export interface SubmoduleDiff {
|
|
|
14
14
|
commit_log: string[];
|
|
15
15
|
}
|
|
16
16
|
export interface DiffCoreResult {
|
|
17
|
+
parent_repo: string;
|
|
17
18
|
submodules: SubmoduleDiff[];
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Show what changed in crosspad-core and/or crosspad-gui relative to the
|
|
21
22
|
* pinned submodule commit. Essential for dev-mode workflows where you're
|
|
22
23
|
* editing shared repos but haven't committed/pinned yet.
|
|
24
|
+
*
|
|
25
|
+
* Submodule paths are resolved dynamically from .gitmodules (handles both
|
|
26
|
+
* `crosspad-core` and `lib/crosspad-core` layouts).
|
|
23
27
|
*/
|
|
24
|
-
export declare function crosspadDiffCore(submodule?: "crosspad-core" | "crosspad-gui" | "both"): DiffCoreResult;
|
|
28
|
+
export declare function crosspadDiffCore(submodule?: "crosspad-core" | "crosspad-gui" | "both", parent?: "crosspad-pc" | "platform-idf"): DiffCoreResult;
|
package/dist/tools/diff-core.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { CROSSPAD_PC_ROOT } from "../config.js";
|
|
3
|
+
import { CROSSPAD_PC_ROOT, CROSSPAD_IDF_ROOT } from "../config.js";
|
|
4
4
|
import { runCommand } from "../utils/exec.js";
|
|
5
|
-
import { getSubmodulePin } from "../utils/git.js";
|
|
5
|
+
import { getSubmodulePin, findSubmodulePath } from "../utils/git.js";
|
|
6
6
|
/**
|
|
7
7
|
* Show what changed in crosspad-core and/or crosspad-gui relative to the
|
|
8
8
|
* pinned submodule commit. Essential for dev-mode workflows where you're
|
|
9
9
|
* editing shared repos but haven't committed/pinned yet.
|
|
10
|
+
*
|
|
11
|
+
* Submodule paths are resolved dynamically from .gitmodules (handles both
|
|
12
|
+
* `crosspad-core` and `lib/crosspad-core` layouts).
|
|
10
13
|
*/
|
|
11
|
-
export function crosspadDiffCore(submodule = "both") {
|
|
14
|
+
export function crosspadDiffCore(submodule = "both", parent = "crosspad-pc") {
|
|
15
|
+
const parentRoot = parent === "platform-idf" ? CROSSPAD_IDF_ROOT : CROSSPAD_PC_ROOT;
|
|
12
16
|
const targets = submodule === "both"
|
|
13
17
|
? ["crosspad-core", "crosspad-gui"]
|
|
14
18
|
: [submodule];
|
|
15
19
|
const submodules = [];
|
|
16
20
|
for (const sub of targets) {
|
|
17
|
-
const
|
|
21
|
+
const relPath = findSubmodulePath(parentRoot, sub);
|
|
22
|
+
const subPath = relPath ? path.join(parentRoot, relPath) : path.join(parentRoot, sub);
|
|
18
23
|
const isDevMode = isJunction(subPath);
|
|
19
|
-
const pinnedCommit = getSubmodulePin(
|
|
24
|
+
const pinnedCommit = getSubmodulePin(parentRoot, sub);
|
|
20
25
|
// Get current HEAD
|
|
21
26
|
const headResult = runCommand("git rev-parse HEAD", subPath);
|
|
22
27
|
const currentCommit = headResult.success ? headResult.stdout.trim() : null;
|
|
@@ -74,7 +79,7 @@ export function crosspadDiffCore(submodule = "both") {
|
|
|
74
79
|
commit_log: commitLog,
|
|
75
80
|
});
|
|
76
81
|
}
|
|
77
|
-
return { submodules };
|
|
82
|
+
return { parent_repo: parent, submodules };
|
|
78
83
|
}
|
|
79
84
|
function isJunction(p) {
|
|
80
85
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-core.js","sourceRoot":"","sources":["../../src/tools/diff-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"diff-core.js","sourceRoot":"","sources":["../../src/tools/diff-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,4 +20,4 @@ export declare function parseErrors(output: string): string[];
|
|
|
20
20
|
export declare function parseWarnings(output: string): string[];
|
|
21
21
|
/** @internal exported for testing */
|
|
22
22
|
export declare function getTail(output: string, n: number): string[];
|
|
23
|
-
export declare function crosspadIdfBuild(mode: "build" | "fullclean" | "clean", onLine?: OnLine): Promise<IdfBuildResult>;
|
|
23
|
+
export declare function crosspadIdfBuild(mode: "build" | "fullclean" | "clean", onLine?: OnLine, signal?: AbortSignal): Promise<IdfBuildResult>;
|
package/dist/tools/idf-build.js
CHANGED
|
@@ -97,14 +97,14 @@ export function parseWarnings(output) {
|
|
|
97
97
|
export function getTail(output, n) {
|
|
98
98
|
return output.split("\n").filter(l => l.trim()).slice(-n);
|
|
99
99
|
}
|
|
100
|
-
async function runIdfCmd(cmd, onLine, timeoutMs) {
|
|
100
|
+
async function runIdfCmd(cmd, onLine, timeoutMs, signal) {
|
|
101
101
|
if (onLine) {
|
|
102
|
-
const r = await runIdfStream(cmd, CROSSPAD_IDF_ROOT, onLine, timeoutMs);
|
|
102
|
+
const r = await runIdfStream(cmd, CROSSPAD_IDF_ROOT, onLine, timeoutMs, signal);
|
|
103
103
|
return r;
|
|
104
104
|
}
|
|
105
105
|
return runIdf(cmd, CROSSPAD_IDF_ROOT, timeoutMs);
|
|
106
106
|
}
|
|
107
|
-
export async function crosspadIdfBuild(mode, onLine) {
|
|
107
|
+
export async function crosspadIdfBuild(mode, onLine, signal) {
|
|
108
108
|
const startTime = Date.now();
|
|
109
109
|
let autoReconfigured = false;
|
|
110
110
|
// Auto-detect unregistered apps — if found, escalate to fullclean
|
|
@@ -118,7 +118,7 @@ export async function crosspadIdfBuild(mode, onLine) {
|
|
|
118
118
|
}
|
|
119
119
|
if (mode === "fullclean") {
|
|
120
120
|
onLine?.("stdout", "[idf] Running idf.py fullclean...");
|
|
121
|
-
const r = await runIdfCmd("idf.py fullclean", onLine, 60_000);
|
|
121
|
+
const r = await runIdfCmd("idf.py fullclean", onLine, 60_000, signal);
|
|
122
122
|
if (!r.success) {
|
|
123
123
|
const combined = r.stdout + "\n" + r.stderr;
|
|
124
124
|
return {
|
|
@@ -139,7 +139,7 @@ export async function crosspadIdfBuild(mode, onLine) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
onLine?.("stdout", "[idf] Building...");
|
|
142
|
-
const r = await runIdfCmd("idf.py build", onLine, 600_000);
|
|
142
|
+
const r = await runIdfCmd("idf.py build", onLine, 600_000, signal);
|
|
143
143
|
const combined = r.stdout + "\n" + r.stderr;
|
|
144
144
|
const errors = parseErrors(combined);
|
|
145
145
|
const warnings = parseWarnings(combined);
|