crosspad-mcp-server 4.0.0

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.
Files changed (80) hide show
  1. package/README.md +187 -0
  2. package/dist/config.d.ts +10 -0
  3. package/dist/config.js +33 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +360 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/tools/architecture.d.ts +16 -0
  9. package/dist/tools/architecture.js +198 -0
  10. package/dist/tools/architecture.js.map +1 -0
  11. package/dist/tools/build-check.d.ts +23 -0
  12. package/dist/tools/build-check.js +162 -0
  13. package/dist/tools/build-check.js.map +1 -0
  14. package/dist/tools/build.d.ts +14 -0
  15. package/dist/tools/build.js +101 -0
  16. package/dist/tools/build.js.map +1 -0
  17. package/dist/tools/diff-core.d.ts +24 -0
  18. package/dist/tools/diff-core.js +88 -0
  19. package/dist/tools/diff-core.js.map +1 -0
  20. package/dist/tools/idf-build.d.ts +10 -0
  21. package/dist/tools/idf-build.js +155 -0
  22. package/dist/tools/idf-build.js.map +1 -0
  23. package/dist/tools/input.d.ts +36 -0
  24. package/dist/tools/input.js +61 -0
  25. package/dist/tools/input.js.map +1 -0
  26. package/dist/tools/log.d.ts +16 -0
  27. package/dist/tools/log.js +49 -0
  28. package/dist/tools/log.js.map +1 -0
  29. package/dist/tools/repos.d.ts +12 -0
  30. package/dist/tools/repos.js +63 -0
  31. package/dist/tools/repos.js.map +1 -0
  32. package/dist/tools/scaffold.d.ts +15 -0
  33. package/dist/tools/scaffold.js +192 -0
  34. package/dist/tools/scaffold.js.map +1 -0
  35. package/dist/tools/screenshot.d.ts +24 -0
  36. package/dist/tools/screenshot.js +80 -0
  37. package/dist/tools/screenshot.js.map +1 -0
  38. package/dist/tools/settings.d.ts +25 -0
  39. package/dist/tools/settings.js +48 -0
  40. package/dist/tools/settings.js.map +1 -0
  41. package/dist/tools/stats.d.ts +18 -0
  42. package/dist/tools/stats.js +31 -0
  43. package/dist/tools/stats.js.map +1 -0
  44. package/dist/tools/symbols.d.ts +20 -0
  45. package/dist/tools/symbols.js +157 -0
  46. package/dist/tools/symbols.js.map +1 -0
  47. package/dist/tools/test.d.ts +24 -0
  48. package/dist/tools/test.js +227 -0
  49. package/dist/tools/test.js.map +1 -0
  50. package/dist/utils/exec.d.ts +58 -0
  51. package/dist/utils/exec.js +292 -0
  52. package/dist/utils/exec.js.map +1 -0
  53. package/dist/utils/git.d.ts +10 -0
  54. package/dist/utils/git.js +29 -0
  55. package/dist/utils/git.js.map +1 -0
  56. package/dist/utils/remote-client.d.ts +17 -0
  57. package/dist/utils/remote-client.js +94 -0
  58. package/dist/utils/remote-client.js.map +1 -0
  59. package/package.json +21 -0
  60. package/server.json +23 -0
  61. package/src/config.ts +45 -0
  62. package/src/index.ts +484 -0
  63. package/src/tools/architecture.ts +260 -0
  64. package/src/tools/build-check.ts +178 -0
  65. package/src/tools/build.ts +130 -0
  66. package/src/tools/diff-core.ts +130 -0
  67. package/src/tools/idf-build.ts +182 -0
  68. package/src/tools/input.ts +80 -0
  69. package/src/tools/log.ts +75 -0
  70. package/src/tools/repos.ts +75 -0
  71. package/src/tools/scaffold.ts +229 -0
  72. package/src/tools/screenshot.ts +100 -0
  73. package/src/tools/settings.ts +68 -0
  74. package/src/tools/stats.ts +38 -0
  75. package/src/tools/symbols.ts +185 -0
  76. package/src/tools/test.ts +264 -0
  77. package/src/utils/exec.ts +376 -0
  78. package/src/utils/git.ts +45 -0
  79. package/src/utils/remote-client.ts +107 -0
  80. package/tsconfig.json +16 -0
@@ -0,0 +1,227 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { CROSSPAD_PC_ROOT, VCPKG_TOOLCHAIN } from "../config.js";
4
+ import { runBuild, runBuildStream } from "../utils/exec.js";
5
+ import { IS_WINDOWS } from "../config.js";
6
+ const TESTS_DIR = path.join(CROSSPAD_PC_ROOT, "tests");
7
+ const BIN_DIR = path.join(CROSSPAD_PC_ROOT, "bin");
8
+ const EXE_EXT = IS_WINDOWS ? ".exe" : "";
9
+ const TEST_EXE = path.join(BIN_DIR, `crosspad_tests${EXE_EXT}`);
10
+ /**
11
+ * Build and run the crosspad test suite (Catch2).
12
+ * If tests/ dir doesn't exist, offers to scaffold it.
13
+ */
14
+ export async function crosspadTest(filter = "", listOnly = false, onLine) {
15
+ const startTime = Date.now();
16
+ // Check if test infrastructure exists
17
+ if (!fs.existsSync(TESTS_DIR)) {
18
+ return {
19
+ success: false,
20
+ tests_found: false,
21
+ build_output: "",
22
+ test_output: `No tests/ directory found. Use crosspad_test_scaffold to create test infrastructure.`,
23
+ passed: 0,
24
+ failed: 0,
25
+ errors: ["tests/ directory not found"],
26
+ duration_seconds: 0,
27
+ };
28
+ }
29
+ // Ensure cmake is configured with BUILD_TESTING=ON
30
+ onLine?.("stdout", "[crosspad] Configuring cmake with BUILD_TESTING=ON...");
31
+ const generator = process.env.CMAKE_GENERATOR || (IS_WINDOWS ? "Ninja" : "");
32
+ const genFlag = generator ? ` -G ${generator}` : "";
33
+ const configCmd = `cmake -B build${genFlag} -DCMAKE_TOOLCHAIN_FILE=${VCPKG_TOOLCHAIN} -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON`;
34
+ let configResult;
35
+ if (onLine) {
36
+ configResult = await runBuildStream(configCmd, CROSSPAD_PC_ROOT, onLine, 120_000);
37
+ }
38
+ else {
39
+ configResult = runBuild(configCmd, CROSSPAD_PC_ROOT, 120_000);
40
+ }
41
+ if (!configResult.success) {
42
+ return {
43
+ success: false,
44
+ tests_found: true,
45
+ build_output: (configResult.stdout + "\n" + configResult.stderr).slice(-3000),
46
+ test_output: "",
47
+ passed: 0,
48
+ failed: 0,
49
+ errors: parseErrors(configResult.stdout + "\n" + configResult.stderr),
50
+ duration_seconds: (Date.now() - startTime) / 1000,
51
+ };
52
+ }
53
+ // Build tests target
54
+ onLine?.("stdout", "[crosspad] Building test target...");
55
+ const buildCmd = "cmake --build build --target crosspad_tests";
56
+ let buildResult;
57
+ if (onLine) {
58
+ buildResult = await runBuildStream(buildCmd, CROSSPAD_PC_ROOT, onLine, 300_000);
59
+ }
60
+ else {
61
+ buildResult = runBuild(buildCmd, CROSSPAD_PC_ROOT, 300_000);
62
+ }
63
+ if (!buildResult.success) {
64
+ return {
65
+ success: false,
66
+ tests_found: true,
67
+ build_output: (buildResult.stdout + "\n" + buildResult.stderr).slice(-3000),
68
+ test_output: "",
69
+ passed: 0,
70
+ failed: 0,
71
+ errors: parseErrors(buildResult.stdout + "\n" + buildResult.stderr),
72
+ duration_seconds: (Date.now() - startTime) / 1000,
73
+ };
74
+ }
75
+ if (!fs.existsSync(TEST_EXE)) {
76
+ return {
77
+ success: false,
78
+ tests_found: true,
79
+ build_output: buildResult.stdout.slice(-1000),
80
+ test_output: "Test executable not found after build",
81
+ passed: 0,
82
+ failed: 0,
83
+ errors: [`${TEST_EXE} not found`],
84
+ duration_seconds: (Date.now() - startTime) / 1000,
85
+ };
86
+ }
87
+ // Run tests
88
+ let testCmd = `"${TEST_EXE}"`;
89
+ if (listOnly) {
90
+ testCmd += " --list-tests";
91
+ }
92
+ else {
93
+ testCmd += " --reporter compact";
94
+ if (filter) {
95
+ testCmd += ` "${filter}"`;
96
+ }
97
+ }
98
+ onLine?.("stdout", "[crosspad] Running tests...");
99
+ let testResult;
100
+ if (onLine) {
101
+ testResult = await runBuildStream(testCmd, CROSSPAD_PC_ROOT, onLine, 120_000);
102
+ }
103
+ else {
104
+ testResult = runBuild(testCmd, CROSSPAD_PC_ROOT, 120_000);
105
+ }
106
+ const testOutput = testResult.stdout + "\n" + testResult.stderr;
107
+ // Parse Catch2 compact output
108
+ const { passed, failed } = parseCatch2Output(testOutput);
109
+ const result = {
110
+ success: testResult.success,
111
+ tests_found: true,
112
+ build_output: buildResult.stdout.slice(-500),
113
+ test_output: testOutput.slice(-5000),
114
+ passed,
115
+ failed,
116
+ errors: testResult.success ? [] : parseErrors(testOutput),
117
+ duration_seconds: (Date.now() - startTime) / 1000,
118
+ };
119
+ onLine?.("stdout", `[crosspad] Tests ${result.success ? "PASSED" : "FAILED"}: ${passed} passed, ${failed} failed (${result.duration_seconds.toFixed(1)}s)`);
120
+ return result;
121
+ }
122
+ /**
123
+ * Scaffold the test infrastructure: CMakeLists.txt additions + sample test file.
124
+ * Returns file contents — does NOT write to disk.
125
+ */
126
+ export function crosspadTestScaffold() {
127
+ const files = {};
128
+ // tests/CMakeLists.txt
129
+ files["tests/CMakeLists.txt"] = `# CrossPad test suite — Catch2 v3
130
+ Include(FetchContent)
131
+
132
+ FetchContent_Declare(
133
+ Catch2
134
+ GIT_REPOSITORY https://github.com/catchorg/Catch2.git
135
+ GIT_TAG v3.5.2
136
+ )
137
+ FetchContent_MakeAvailable(Catch2)
138
+
139
+ # Collect test sources
140
+ file(GLOB_RECURSE TEST_SOURCES "\${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
141
+
142
+ add_executable(crosspad_tests \${TEST_SOURCES})
143
+
144
+ target_link_libraries(crosspad_tests PRIVATE
145
+ Catch2::Catch2WithMain
146
+ )
147
+
148
+ # Include crosspad-core headers (for testing core logic)
149
+ target_include_directories(crosspad_tests PRIVATE
150
+ \${CMAKE_SOURCE_DIR}/crosspad-core/include
151
+ \${CMAKE_SOURCE_DIR}/crosspad-gui/include
152
+ \${CMAKE_SOURCE_DIR}/src
153
+ )
154
+
155
+ # Same defines as main target
156
+ target_compile_definitions(crosspad_tests PRIVATE
157
+ PLATFORM_PC=1
158
+ CP_LCD_HOR_RES=320
159
+ CP_LCD_VER_RES=240
160
+ )
161
+
162
+ # Add crosspad-core sources we want to test (non-platform-specific)
163
+ # Add individual source files as needed:
164
+ # target_sources(crosspad_tests PRIVATE
165
+ # \${CMAKE_SOURCE_DIR}/crosspad-core/src/SomeFile.cpp
166
+ # )
167
+
168
+ include(CTest)
169
+ include(Catch)
170
+ catch_discover_tests(crosspad_tests)
171
+ `;
172
+ // tests/test_pad_manager.cpp — sample test
173
+ files["tests/test_pad_manager.cpp"] = `#include <catch2/catch_test_macros.hpp>
174
+
175
+ // Example: test crosspad-core types without full platform init
176
+ // #include <crosspad/pad/PadManager.hpp>
177
+ // #include <crosspad/platform/PlatformCapabilities.hpp>
178
+
179
+ TEST_CASE("Sanity check", "[core]") {
180
+ REQUIRE(1 + 1 == 2);
181
+ }
182
+
183
+ // TEST_CASE("PlatformCapabilities bitflags", "[core][capabilities]") {
184
+ // using crosspad::Capability;
185
+ // using crosspad::setPlatformCapabilities;
186
+ // using crosspad::hasCapability;
187
+ // using crosspad::hasAnyCapability;
188
+ //
189
+ // setPlatformCapabilities(Capability::Midi | Capability::Pads);
190
+ //
191
+ // REQUIRE(hasCapability(Capability::Midi));
192
+ // REQUIRE(hasCapability(Capability::Pads));
193
+ // REQUIRE_FALSE(hasCapability(Capability::AudioOut));
194
+ // REQUIRE(hasAnyCapability(Capability::Midi | Capability::AudioOut));
195
+ // }
196
+ `;
197
+ // Patch for root CMakeLists.txt
198
+ const cmakePatch = `
199
+ # Add this near the end of your CMakeLists.txt, before any final install/packaging:
200
+ # --- Test suite ---
201
+ if(EXISTS "\${CMAKE_SOURCE_DIR}/tests/CMakeLists.txt")
202
+ add_subdirectory(tests)
203
+ endif()
204
+ `;
205
+ return { files, cmake_patch: cmakePatch };
206
+ }
207
+ function parseCatch2Output(output) {
208
+ // Catch2 compact reporter: "Passed X test(s)" / "Failed X test(s)"
209
+ const passedMatch = output.match(/(\d+)\s+assertion[s]?\s+.*passed/i) ||
210
+ output.match(/All tests passed\s*\((\d+)/i);
211
+ const failedMatch = output.match(/(\d+)\s+assertion[s]?\s+.*failed/i) ||
212
+ output.match(/test cases?:\s*\d+\s*\|\s*(\d+)\s+failed/i);
213
+ return {
214
+ passed: passedMatch ? parseInt(passedMatch[1], 10) : 0,
215
+ failed: failedMatch ? parseInt(failedMatch[1], 10) : 0,
216
+ };
217
+ }
218
+ function parseErrors(output) {
219
+ const errors = [];
220
+ for (const line of output.split("\n")) {
221
+ if (/\berror\b/i.test(line) && !line.includes("error(s)")) {
222
+ errors.push(line.trim());
223
+ }
224
+ }
225
+ return errors.slice(0, 20);
226
+ }
227
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/tools/test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /** Callback invoked for each line of stdout/stderr during streaming exec. */
2
+ export type OnLine = (stream: "stdout" | "stderr", line: string) => void;
3
+ /**
4
+ * Capture MSVC environment by running vcvarsall.bat and parsing `set` output.
5
+ * Cached for the lifetime of the server process. Windows-only.
6
+ */
7
+ export declare function getMsvcEnv(): Record<string, string>;
8
+ export interface ExecResult {
9
+ success: boolean;
10
+ stdout: string;
11
+ stderr: string;
12
+ exitCode: number;
13
+ durationMs: number;
14
+ }
15
+ /**
16
+ * Run a command with the MSVC environment, capturing output.
17
+ */
18
+ export declare function runWithMsvc(cmd: string, cwd: string, timeoutMs?: number): ExecResult;
19
+ /**
20
+ * Run a command in the default shell, capturing output.
21
+ */
22
+ export declare function runCommand(cmd: string, cwd: string, timeoutMs?: number): ExecResult;
23
+ /**
24
+ * Run a command with the MSVC environment, streaming output line-by-line.
25
+ */
26
+ export declare function runWithMsvcStream(cmd: string, cwd: string, onLine: OnLine, timeoutMs?: number): Promise<ExecResult>;
27
+ /**
28
+ * Run a command in the default shell, streaming output line-by-line.
29
+ */
30
+ export declare function runCommandStream(cmd: string, cwd: string, onLine: OnLine, timeoutMs?: number): Promise<ExecResult>;
31
+ /**
32
+ * Capture ESP-IDF environment by running export.bat and parsing `set` output.
33
+ * Must unset MSYSTEM to prevent ESP-IDF from rejecting MSYS shells.
34
+ * Cached for the lifetime of the server process. Windows-only.
35
+ */
36
+ export declare function getIdfEnv(): Record<string, string>;
37
+ /**
38
+ * Run a command with the ESP-IDF environment, capturing output.
39
+ */
40
+ export declare function runWithIdf(cmd: string, cwd: string, timeoutMs?: number): ExecResult;
41
+ /**
42
+ * Run a command with the ESP-IDF environment, streaming output line-by-line.
43
+ */
44
+ export declare function runWithIdfStream(cmd: string, cwd: string, onLine: OnLine, timeoutMs?: number): Promise<ExecResult>;
45
+ /**
46
+ * Run a build command with the appropriate environment for the current platform.
47
+ * Windows: MSVC env + cmd.exe shell. Unix: default shell.
48
+ */
49
+ export declare function runBuild(cmd: string, cwd: string, timeoutMs?: number): ExecResult;
50
+ /**
51
+ * Run a build command with streaming output, platform-aware.
52
+ * Windows: MSVC env + cmd.exe shell. Unix: default shell.
53
+ */
54
+ export declare function runBuildStream(cmd: string, cwd: string, onLine: OnLine, timeoutMs?: number): Promise<ExecResult>;
55
+ /**
56
+ * Spawn a detached process (for crosspad_run).
57
+ */
58
+ export declare function spawnDetached(exe: string, args: string[], cwd: string): number | null;
@@ -0,0 +1,292 @@
1
+ import { execSync, spawn } from "child_process";
2
+ import { VCVARSALL, IS_WINDOWS, IDF_PATH } from "../config.js";
3
+ let cachedMsvcEnv = null;
4
+ /**
5
+ * Capture MSVC environment by running vcvarsall.bat and parsing `set` output.
6
+ * Cached for the lifetime of the server process. Windows-only.
7
+ */
8
+ export function getMsvcEnv() {
9
+ if (!IS_WINDOWS) {
10
+ return { ...process.env };
11
+ }
12
+ if (cachedMsvcEnv)
13
+ return cachedMsvcEnv;
14
+ const cmd = `"${VCVARSALL}" x64 >nul 2>&1 && set`;
15
+ const output = execSync(cmd, {
16
+ shell: "cmd.exe",
17
+ encoding: "utf-8",
18
+ timeout: 30_000,
19
+ });
20
+ const env = {};
21
+ for (const line of output.split("\n")) {
22
+ const eq = line.indexOf("=");
23
+ if (eq > 0) {
24
+ env[line.slice(0, eq)] = line.slice(eq + 1).trimEnd();
25
+ }
26
+ }
27
+ cachedMsvcEnv = env;
28
+ return env;
29
+ }
30
+ /**
31
+ * Run a command with the MSVC environment, capturing output.
32
+ */
33
+ export function runWithMsvc(cmd, cwd, timeoutMs = 300_000) {
34
+ const env = getMsvcEnv();
35
+ const start = Date.now();
36
+ try {
37
+ const stdout = execSync(cmd, {
38
+ cwd,
39
+ env,
40
+ shell: "cmd.exe",
41
+ encoding: "utf-8",
42
+ timeout: timeoutMs,
43
+ stdio: ["pipe", "pipe", "pipe"],
44
+ });
45
+ return {
46
+ success: true,
47
+ stdout: normalizeLineEndings(stdout),
48
+ stderr: "",
49
+ exitCode: 0,
50
+ durationMs: Date.now() - start,
51
+ };
52
+ }
53
+ catch (err) {
54
+ return {
55
+ success: false,
56
+ stdout: normalizeLineEndings(err.stdout?.toString() ?? ""),
57
+ stderr: normalizeLineEndings(err.stderr?.toString() ?? ""),
58
+ exitCode: err.status ?? 1,
59
+ durationMs: Date.now() - start,
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Run a command in the default shell, capturing output.
65
+ */
66
+ export function runCommand(cmd, cwd, timeoutMs = 60_000) {
67
+ const start = Date.now();
68
+ try {
69
+ const stdout = execSync(cmd, {
70
+ cwd,
71
+ encoding: "utf-8",
72
+ timeout: timeoutMs,
73
+ stdio: ["pipe", "pipe", "pipe"],
74
+ });
75
+ return {
76
+ success: true,
77
+ stdout: normalizeLineEndings(stdout),
78
+ stderr: "",
79
+ exitCode: 0,
80
+ durationMs: Date.now() - start,
81
+ };
82
+ }
83
+ catch (err) {
84
+ return {
85
+ success: false,
86
+ stdout: normalizeLineEndings(err.stdout?.toString() ?? ""),
87
+ stderr: normalizeLineEndings(err.stderr?.toString() ?? ""),
88
+ exitCode: err.status ?? 1,
89
+ durationMs: Date.now() - start,
90
+ };
91
+ }
92
+ }
93
+ // ═══════════════════════════════════════════════════════════════════════
94
+ // STREAMING VARIANTS (spawn-based, line-by-line callbacks)
95
+ // ═══════════════════════════════════════════════════════════════════════
96
+ /**
97
+ * Helper: spawn a process and stream stdout/stderr line-by-line via onLine.
98
+ * Returns the same ExecResult as the sync variants.
99
+ */
100
+ function spawnStreaming(cmd, cwd, env, shell, onLine, timeoutMs) {
101
+ return new Promise((resolve) => {
102
+ const start = Date.now();
103
+ const child = spawn(cmd, [], {
104
+ cwd,
105
+ env,
106
+ shell,
107
+ stdio: ["pipe", "pipe", "pipe"],
108
+ windowsHide: true,
109
+ });
110
+ let stdout = "";
111
+ let stderr = "";
112
+ let stdoutBuf = "";
113
+ let stderrBuf = "";
114
+ let killed = false;
115
+ const timer = setTimeout(() => {
116
+ killed = true;
117
+ child.kill("SIGTERM");
118
+ }, timeoutMs);
119
+ function flushLines(buf, stream) {
120
+ const parts = buf.split("\n");
121
+ // Last part is incomplete — keep it in buffer
122
+ for (let i = 0; i < parts.length - 1; i++) {
123
+ const line = parts[i].replace(/\r$/, "");
124
+ onLine(stream, line);
125
+ }
126
+ return parts[parts.length - 1];
127
+ }
128
+ child.stdout?.on("data", (chunk) => {
129
+ const text = chunk.toString();
130
+ stdout += text;
131
+ stdoutBuf += text;
132
+ stdoutBuf = flushLines(stdoutBuf, "stdout");
133
+ });
134
+ child.stderr?.on("data", (chunk) => {
135
+ const text = chunk.toString();
136
+ stderr += text;
137
+ stderrBuf += text;
138
+ stderrBuf = flushLines(stderrBuf, "stderr");
139
+ });
140
+ child.on("close", (code) => {
141
+ clearTimeout(timer);
142
+ // Flush remaining partial lines
143
+ if (stdoutBuf.length > 0)
144
+ onLine("stdout", stdoutBuf.replace(/\r$/, ""));
145
+ if (stderrBuf.length > 0)
146
+ onLine("stderr", stderrBuf.replace(/\r$/, ""));
147
+ resolve({
148
+ success: killed ? false : code === 0,
149
+ stdout: normalizeLineEndings(stdout),
150
+ stderr: normalizeLineEndings(stderr),
151
+ exitCode: killed ? -1 : (code ?? 1),
152
+ durationMs: Date.now() - start,
153
+ });
154
+ });
155
+ child.on("error", (err) => {
156
+ clearTimeout(timer);
157
+ resolve({
158
+ success: false,
159
+ stdout: normalizeLineEndings(stdout),
160
+ stderr: normalizeLineEndings(stderr + "\n" + err.message),
161
+ exitCode: 1,
162
+ durationMs: Date.now() - start,
163
+ });
164
+ });
165
+ });
166
+ }
167
+ /**
168
+ * Run a command with the MSVC environment, streaming output line-by-line.
169
+ */
170
+ export function runWithMsvcStream(cmd, cwd, onLine, timeoutMs = 300_000) {
171
+ const env = getMsvcEnv();
172
+ return spawnStreaming(cmd, cwd, env, "cmd.exe", onLine, timeoutMs);
173
+ }
174
+ /**
175
+ * Run a command in the default shell, streaming output line-by-line.
176
+ */
177
+ export function runCommandStream(cmd, cwd, onLine, timeoutMs = 60_000) {
178
+ return spawnStreaming(cmd, cwd, undefined, true, onLine, timeoutMs);
179
+ }
180
+ // ═══════════════════════════════════════════════════════════════════════
181
+ // ESP-IDF ENVIRONMENT
182
+ // ═══════════════════════════════════════════════════════════════════════
183
+ let cachedIdfEnv = null;
184
+ /**
185
+ * Capture ESP-IDF environment by running export.bat and parsing `set` output.
186
+ * Must unset MSYSTEM to prevent ESP-IDF from rejecting MSYS shells.
187
+ * Cached for the lifetime of the server process. Windows-only.
188
+ */
189
+ export function getIdfEnv() {
190
+ if (!IS_WINDOWS) {
191
+ return { ...process.env };
192
+ }
193
+ if (cachedIdfEnv)
194
+ return cachedIdfEnv;
195
+ const exportBat = `${IDF_PATH}\\export.bat`;
196
+ const cmd = `set MSYSTEM=&& set PYTHONIOENCODING=utf-8&& call ${exportBat} >nul 2>&1 && set`;
197
+ const output = execSync(cmd, {
198
+ shell: "cmd.exe",
199
+ encoding: "utf-8",
200
+ timeout: 60_000,
201
+ });
202
+ const env = {};
203
+ for (const line of output.split("\n")) {
204
+ const eq = line.indexOf("=");
205
+ if (eq > 0) {
206
+ env[line.slice(0, eq)] = line.slice(eq + 1).trimEnd();
207
+ }
208
+ }
209
+ cachedIdfEnv = env;
210
+ return env;
211
+ }
212
+ /**
213
+ * Run a command with the ESP-IDF environment, capturing output.
214
+ */
215
+ export function runWithIdf(cmd, cwd, timeoutMs = 600_000) {
216
+ const env = getIdfEnv();
217
+ const start = Date.now();
218
+ try {
219
+ const stdout = execSync(cmd, {
220
+ cwd,
221
+ env,
222
+ shell: "cmd.exe",
223
+ encoding: "utf-8",
224
+ timeout: timeoutMs,
225
+ stdio: ["pipe", "pipe", "pipe"],
226
+ });
227
+ return {
228
+ success: true,
229
+ stdout: normalizeLineEndings(stdout),
230
+ stderr: "",
231
+ exitCode: 0,
232
+ durationMs: Date.now() - start,
233
+ };
234
+ }
235
+ catch (err) {
236
+ return {
237
+ success: false,
238
+ stdout: normalizeLineEndings(err.stdout?.toString() ?? ""),
239
+ stderr: normalizeLineEndings(err.stderr?.toString() ?? ""),
240
+ exitCode: err.status ?? 1,
241
+ durationMs: Date.now() - start,
242
+ };
243
+ }
244
+ }
245
+ /**
246
+ * Run a command with the ESP-IDF environment, streaming output line-by-line.
247
+ */
248
+ export function runWithIdfStream(cmd, cwd, onLine, timeoutMs = 600_000) {
249
+ const env = getIdfEnv();
250
+ return spawnStreaming(cmd, cwd, env, "cmd.exe", onLine, timeoutMs);
251
+ }
252
+ // ═══════════════════════════════════════════════════════════════════════
253
+ // PLATFORM-AGNOSTIC BUILD WRAPPERS
254
+ // ═══════════════════════════════════════════════════════════════════════
255
+ /**
256
+ * Run a build command with the appropriate environment for the current platform.
257
+ * Windows: MSVC env + cmd.exe shell. Unix: default shell.
258
+ */
259
+ export function runBuild(cmd, cwd, timeoutMs = 300_000) {
260
+ if (IS_WINDOWS) {
261
+ return runWithMsvc(cmd, cwd, timeoutMs);
262
+ }
263
+ return runCommand(cmd, cwd, timeoutMs);
264
+ }
265
+ /**
266
+ * Run a build command with streaming output, platform-aware.
267
+ * Windows: MSVC env + cmd.exe shell. Unix: default shell.
268
+ */
269
+ export function runBuildStream(cmd, cwd, onLine, timeoutMs = 300_000) {
270
+ if (IS_WINDOWS) {
271
+ return runWithMsvcStream(cmd, cwd, onLine, timeoutMs);
272
+ }
273
+ return runCommandStream(cmd, cwd, onLine, timeoutMs);
274
+ }
275
+ /** Strip \r from Windows line endings */
276
+ function normalizeLineEndings(s) {
277
+ return s.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
278
+ }
279
+ /**
280
+ * Spawn a detached process (for crosspad_run).
281
+ */
282
+ export function spawnDetached(exe, args, cwd) {
283
+ const opts = {
284
+ cwd,
285
+ detached: true,
286
+ stdio: "ignore",
287
+ };
288
+ const child = spawn(exe, args, opts);
289
+ child.unref();
290
+ return child.pid ?? null;
291
+ }
292
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface RepoStatus {
2
+ name: string;
3
+ path: string;
4
+ branch: string;
5
+ head: string;
6
+ dirtyFiles: string[];
7
+ }
8
+ export declare function getRepoStatus(name: string, repoPath: string): RepoStatus;
9
+ export declare function getSubmodulePin(repoPath: string, submodule: string): string | null;
10
+ export declare function getHead(repoPath: string): string | null;
@@ -0,0 +1,29 @@
1
+ import { runCommand } from "./exec.js";
2
+ export function getRepoStatus(name, repoPath) {
3
+ const branch = runCommand("git branch --show-current", repoPath);
4
+ const log = runCommand("git log --oneline -1", repoPath);
5
+ const status = runCommand("git status --porcelain", repoPath);
6
+ return {
7
+ name,
8
+ path: repoPath,
9
+ branch: branch.stdout.trim(),
10
+ head: log.stdout.trim(),
11
+ dirtyFiles: status.stdout
12
+ .trim()
13
+ .split("\n")
14
+ .filter((l) => l.length > 0),
15
+ };
16
+ }
17
+ export function getSubmodulePin(repoPath, submodule) {
18
+ const result = runCommand(`git submodule status ${submodule}`, repoPath);
19
+ if (!result.success)
20
+ return null;
21
+ // Output format: " abc1234 submodule-name (desc)" or "+abc1234 ..."
22
+ const match = result.stdout.match(/[+ -]?([0-9a-f]+)/);
23
+ return match ? match[1] : null;
24
+ }
25
+ export function getHead(repoPath) {
26
+ const result = runCommand("git rev-parse HEAD", repoPath);
27
+ return result.success ? result.stdout.trim() : null;
28
+ }
29
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAUvC,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAgB;IAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,2BAA2B,EAAE,QAAQ,CAAC,CAAC;IACjE,MAAM,GAAG,GAAG,UAAU,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,UAAU,CAAC,wBAAwB,EAAE,QAAQ,CAAC,CAAC;IAE9D,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAC5B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QACvB,UAAU,EAAE,MAAM,CAAC,MAAM;aACtB,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,SAAiB;IAEjB,MAAM,MAAM,GAAG,UAAU,CACvB,wBAAwB,SAAS,EAAE,EACnC,QAAQ,CACT,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACjC,oEAAoE;IACpE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * TCP client for communicating with the CrossPad simulator's remote control server.
3
+ * Protocol: newline-delimited JSON over TCP on localhost:19840.
4
+ */
5
+ export interface RemoteResponse {
6
+ ok: boolean;
7
+ [key: string]: unknown;
8
+ }
9
+ /**
10
+ * Send a JSON command to the running simulator and return the response.
11
+ * Opens a fresh TCP connection per call (simple, stateless).
12
+ */
13
+ export declare function sendRemoteCommand(command: Record<string, unknown>): Promise<RemoteResponse>;
14
+ /**
15
+ * Check if the simulator's remote control server is reachable.
16
+ */
17
+ export declare function isSimulatorRunning(): Promise<boolean>;