@kvell007/embed-labs-cli 0.1.0-alpha.60 → 0.1.0-alpha.62
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/embed-labs-mcp-bridge.mjs +3104 -0
- package/dist/index.js +141 -79
- package/dist/index.js.map +1 -1
- package/dist/local-toolchain.js +153 -13
- package/dist/local-toolchain.js.map +1 -1
- package/package.json +3 -3
package/dist/local-toolchain.js
CHANGED
|
@@ -184,6 +184,7 @@ export function defaultLocalReleaseRoot() {
|
|
|
184
184
|
export async function latestLocalToolchain(options = {}) {
|
|
185
185
|
const boardId = options.boardId ?? DEFAULT_BOARD_ID;
|
|
186
186
|
const channelName = options.channel ?? DEFAULT_CHANNEL;
|
|
187
|
+
const host = localToolchainHostId();
|
|
187
188
|
const { channel, manifests, metadataRoot } = await loadLocalToolchainMetadata(options.metadataRoot, channelName);
|
|
188
189
|
const boardPackageId = boardPackageIdFor(boardId);
|
|
189
190
|
const board = manifests.get(boardPackageId);
|
|
@@ -198,20 +199,20 @@ export async function latestLocalToolchain(options = {}) {
|
|
|
198
199
|
download = await resolveLocalToolchainDownloadPlan({
|
|
199
200
|
boardId: canonicalBoardId,
|
|
200
201
|
channel: channelName,
|
|
201
|
-
host
|
|
202
|
+
host,
|
|
202
203
|
toolchain: "llvm"
|
|
203
204
|
});
|
|
204
205
|
}
|
|
205
206
|
catch (error) {
|
|
206
207
|
downloadError = error instanceof Error ? error.message : String(error);
|
|
207
208
|
}
|
|
208
|
-
if (!download && isNativeWindowsTaishanPiHost(canonicalBoardId,
|
|
209
|
-
downloadError = taishanPiWindowsRequirementMessage(
|
|
209
|
+
if (!download && isNativeWindowsTaishanPiHost(canonicalBoardId, host)) {
|
|
210
|
+
downloadError = taishanPiWindowsRequirementMessage(host);
|
|
210
211
|
}
|
|
211
212
|
return {
|
|
212
213
|
board_id: canonicalBoardId,
|
|
213
|
-
channel:
|
|
214
|
-
host
|
|
214
|
+
channel: channelName,
|
|
215
|
+
host,
|
|
215
216
|
version: download?.version ?? board.version,
|
|
216
217
|
metadata_root: metadataRoot,
|
|
217
218
|
packages,
|
|
@@ -324,7 +325,7 @@ async function discoverInstalledLocalToolchains(installRoot, current) {
|
|
|
324
325
|
}
|
|
325
326
|
export async function listLocalToolchainEnvironments(options = {}) {
|
|
326
327
|
const channelName = options.channel ?? DEFAULT_CHANNEL;
|
|
327
|
-
const host =
|
|
328
|
+
const host = localToolchainHostId();
|
|
328
329
|
const installRoot = resolveInstallRoot(options.installRoot);
|
|
329
330
|
const registryPath = localToolchainRegistryPath(installRoot);
|
|
330
331
|
const { channel, manifests, metadataRoot } = await loadLocalToolchainMetadata(options.metadataRoot, channelName);
|
|
@@ -412,7 +413,7 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
412
413
|
display_name: board.display_name || [board.board, board.variant].filter(Boolean).join(" ") || boardId,
|
|
413
414
|
family: board.family,
|
|
414
415
|
variant: board.variant,
|
|
415
|
-
channel:
|
|
416
|
+
channel: channelName,
|
|
416
417
|
host,
|
|
417
418
|
status,
|
|
418
419
|
supported_host: effectiveHostSupport.supported,
|
|
@@ -430,9 +431,9 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
430
431
|
packages,
|
|
431
432
|
components: download?.components?.map(localToolchainEnvironmentComponent),
|
|
432
433
|
execution,
|
|
433
|
-
install_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId} --mode ${mode}`,
|
|
434
|
-
update_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId} --mode ${mode} --force`,
|
|
435
|
-
notes: environmentNotes({ boardId, status, downloadError, unsupportedPackages: effectiveHostSupport.unsupportedPackages })
|
|
434
|
+
install_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId}${localToolchainChannelFlag(channelName)} --mode ${mode}`,
|
|
435
|
+
update_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId}${localToolchainChannelFlag(channelName)} --mode ${mode} --force`,
|
|
436
|
+
notes: environmentNotes({ boardId, host, status, downloadError, unsupportedPackages: effectiveHostSupport.unsupportedPackages })
|
|
436
437
|
});
|
|
437
438
|
}
|
|
438
439
|
const filteredEnvironments = options.installedOnly
|
|
@@ -440,7 +441,7 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
440
441
|
: environments;
|
|
441
442
|
return {
|
|
442
443
|
host,
|
|
443
|
-
channel:
|
|
444
|
+
channel: channelName,
|
|
444
445
|
metadata_source: metadataRoot ? "local_override" : "built_in",
|
|
445
446
|
metadata_root: metadataRoot,
|
|
446
447
|
install_root: installRoot,
|
|
@@ -1070,6 +1071,9 @@ function rp2350ArmToolchainDirName() {
|
|
|
1070
1071
|
}
|
|
1071
1072
|
export async function compileTaishanPiSingleFile(options) {
|
|
1072
1073
|
assertAuthenticated(options.auth);
|
|
1074
|
+
if (platform() === "win32" && normalizeBoardId(options.boardId) === DEFAULT_BOARD_ID) {
|
|
1075
|
+
return await compileTaishanPiSingleFileWindowsWsl(options);
|
|
1076
|
+
}
|
|
1073
1077
|
const releaseRoot = await resolveLocalReleaseRoot(options.releaseRoot);
|
|
1074
1078
|
const sourcePath = resolve(options.sourcePath);
|
|
1075
1079
|
const outputPath = resolve(options.outputPath);
|
|
@@ -1095,6 +1099,44 @@ export async function compileTaishanPiSingleFile(options) {
|
|
|
1095
1099
|
commands: [buildResult]
|
|
1096
1100
|
});
|
|
1097
1101
|
}
|
|
1102
|
+
async function compileTaishanPiSingleFileWindowsWsl(options) {
|
|
1103
|
+
const route = taishanPiWindowsExecutionRoute(hostId(), await windowsWslStatus());
|
|
1104
|
+
if (route.actual_host !== "linux-x86_64" || route.status === "wsl_missing" || route.status === "wsl_not_configured" || route.status === "architecture_mismatch") {
|
|
1105
|
+
throw new Error(`${route.reason} Run: embedlabs local wsl status`);
|
|
1106
|
+
}
|
|
1107
|
+
const sourcePath = resolve(options.sourcePath);
|
|
1108
|
+
const outputPath = resolve(options.outputPath);
|
|
1109
|
+
await access(sourcePath, constants.R_OK);
|
|
1110
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
1111
|
+
const releaseRoot = options.releaseRoot?.trim()
|
|
1112
|
+
? normalizeWindowsWslReleaseRoot(options.releaseRoot)
|
|
1113
|
+
: await resolveWindowsWslTaishanPiReleaseRoot();
|
|
1114
|
+
const sourceWslPath = windowsPathToWslPath(sourcePath, "source");
|
|
1115
|
+
const outputWslPath = windowsPathToWslPath(outputPath, "output");
|
|
1116
|
+
const compiler = wslCompilerForSource(releaseRoot, sourcePath);
|
|
1117
|
+
const sysroot = `${trimTrailingSlash(releaseRoot)}/toolchain/host/aarch64-buildroot-linux-gnu/sysroot`;
|
|
1118
|
+
const commandScript = [
|
|
1119
|
+
"set -euo pipefail",
|
|
1120
|
+
`test -x ${sh(compiler)}`,
|
|
1121
|
+
`test -r ${sh(sysroot)}`,
|
|
1122
|
+
`mkdir -p ${sh(posixDirname(outputWslPath))}`,
|
|
1123
|
+
`${sh(compiler)} --sysroot=${sh(sysroot)} -O2 ${sh(sourceWslPath)} -o ${sh(outputWslPath)}`
|
|
1124
|
+
].join("\n");
|
|
1125
|
+
const buildResult = await runCommand(windowsWslBashCommand(commandScript), homedir());
|
|
1126
|
+
if (buildResult.exit_code !== 0) {
|
|
1127
|
+
throw new Error(`Windows WSL TaishanPi compile failed with exit code ${buildResult.exit_code}: ${buildResult.stderr_tail.join("\n")}`);
|
|
1128
|
+
}
|
|
1129
|
+
return await localCompileResult({
|
|
1130
|
+
boardId: options.boardId,
|
|
1131
|
+
operation: "local.compile.single_file",
|
|
1132
|
+
releaseRoot,
|
|
1133
|
+
accountId: options.accountId,
|
|
1134
|
+
auth: options.auth,
|
|
1135
|
+
sourcePath,
|
|
1136
|
+
artifactPath: outputPath,
|
|
1137
|
+
commands: [buildResult]
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1098
1140
|
export async function buildTaishanPiQtSmoke(options) {
|
|
1099
1141
|
assertAuthenticated(options.auth);
|
|
1100
1142
|
const releaseRoot = await resolveLocalReleaseRoot(options.releaseRoot);
|
|
@@ -1444,8 +1486,8 @@ function environmentNotes(input) {
|
|
|
1444
1486
|
if (isRp2350MonitorBoardId(input.boardId)) {
|
|
1445
1487
|
notes.push("RP2350 Monitor is an optional hardware-control firmware image; the board environment itself is the Pico 2 W or ColorEasyPICO2 C/C++ SDK environment.");
|
|
1446
1488
|
}
|
|
1447
|
-
if (isNativeWindowsTaishanPiHost(input.boardId,
|
|
1448
|
-
notes.push(taishanPiWindowsRequirementMessage(
|
|
1489
|
+
if (isNativeWindowsTaishanPiHost(input.boardId, input.host)) {
|
|
1490
|
+
notes.push(taishanPiWindowsRequirementMessage(input.host));
|
|
1449
1491
|
notes.push("Check this computer with: embedlabs local wsl status");
|
|
1450
1492
|
}
|
|
1451
1493
|
if (input.status === "available") {
|
|
@@ -1476,6 +1518,9 @@ function localToolchainInstallModesForDownload(download) {
|
|
|
1476
1518
|
? [...modes].filter((mode) => LOCAL_TOOLCHAIN_INSTALL_MODES.includes(mode))
|
|
1477
1519
|
: [...LOCAL_TOOLCHAIN_INSTALL_MODES];
|
|
1478
1520
|
}
|
|
1521
|
+
function localToolchainChannelFlag(channelName) {
|
|
1522
|
+
return channelName === DEFAULT_CHANNEL ? "" : ` --channel ${channelName}`;
|
|
1523
|
+
}
|
|
1479
1524
|
function localToolchainEnvironmentComponent(component) {
|
|
1480
1525
|
return {
|
|
1481
1526
|
id: component.id,
|
|
@@ -1496,6 +1541,23 @@ function hostId() {
|
|
|
1496
1541
|
}
|
|
1497
1542
|
return `${platform()}-${arch()}`;
|
|
1498
1543
|
}
|
|
1544
|
+
const LOCAL_TOOLCHAIN_HOST_IDS = new Set([
|
|
1545
|
+
"darwin-arm64",
|
|
1546
|
+
"linux-x86_64",
|
|
1547
|
+
"linux-arm64",
|
|
1548
|
+
"win32-x64",
|
|
1549
|
+
"win32-arm64"
|
|
1550
|
+
]);
|
|
1551
|
+
function localToolchainHostId() {
|
|
1552
|
+
const override = process.env.EMBEDLABS_TEST_LOCAL_TOOLCHAIN_HOST?.trim();
|
|
1553
|
+
if (override) {
|
|
1554
|
+
if (!LOCAL_TOOLCHAIN_HOST_IDS.has(override)) {
|
|
1555
|
+
throw new Error(`Unsupported EMBEDLABS_TEST_LOCAL_TOOLCHAIN_HOST value: ${override}`);
|
|
1556
|
+
}
|
|
1557
|
+
return override;
|
|
1558
|
+
}
|
|
1559
|
+
return hostId();
|
|
1560
|
+
}
|
|
1499
1561
|
function isNativeWindowsTaishanPiHost(boardId, host) {
|
|
1500
1562
|
return normalizeBoardId(boardId) === DEFAULT_BOARD_ID && host.startsWith("win32-");
|
|
1501
1563
|
}
|
|
@@ -2017,6 +2079,84 @@ function compilerForSource(releaseRoot, sourcePath) {
|
|
|
2017
2079
|
}
|
|
2018
2080
|
throw new Error(`Unsupported source extension ${extension || "<none>"}; use .c, .cc, .cpp, or .cxx.`);
|
|
2019
2081
|
}
|
|
2082
|
+
function wslCompilerForSource(releaseRoot, sourcePath) {
|
|
2083
|
+
const extension = extname(sourcePath).toLowerCase();
|
|
2084
|
+
const binDir = `${trimTrailingSlash(releaseRoot)}/toolchain/llvm-cross/bin`;
|
|
2085
|
+
if (extension === ".c") {
|
|
2086
|
+
return `${binDir}/aarch64-linux-gnu-gcc`;
|
|
2087
|
+
}
|
|
2088
|
+
if ([".cc", ".cpp", ".cxx"].includes(extension)) {
|
|
2089
|
+
return `${binDir}/aarch64-linux-gnu-g++`;
|
|
2090
|
+
}
|
|
2091
|
+
throw new Error(`Unsupported source extension ${extension || "<none>"}; use .c, .cc, .cpp, or .cxx.`);
|
|
2092
|
+
}
|
|
2093
|
+
async function resolveWindowsWslTaishanPiReleaseRoot() {
|
|
2094
|
+
const explicit = process.env.EMBEDLABS_WINDOWS_WSL_TAISHANPI_RELEASE_ROOT?.trim()
|
|
2095
|
+
|| process.env.EMBEDLABS_WSL_TAISHANPI_RELEASE_ROOT?.trim();
|
|
2096
|
+
if (explicit) {
|
|
2097
|
+
return normalizeWindowsWslReleaseRoot(explicit);
|
|
2098
|
+
}
|
|
2099
|
+
const script = `
|
|
2100
|
+
set -euo pipefail
|
|
2101
|
+
REGISTRY="\${EMBEDLABS_WSL_INSTALL_ROOT:-$HOME/.embedlabs}/registry/local-toolchains.json"
|
|
2102
|
+
if [ ! -f "$REGISTRY" ]; then
|
|
2103
|
+
echo "Missing WSL TaishanPi registry at $REGISTRY. Run inside WSL first: npx -y embedlabs@latest local toolchain install --board taishanpi-1m-rk3566 --channel windows-test --mode compile" >&2
|
|
2104
|
+
exit 2
|
|
2105
|
+
fi
|
|
2106
|
+
node - "$REGISTRY" <<'NODE'
|
|
2107
|
+
const fs = require("fs");
|
|
2108
|
+
const registry = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
2109
|
+
const env = registry.environments && registry.environments["taishanpi-1m-rk3566"] ? registry.environments["taishanpi-1m-rk3566"] : registry;
|
|
2110
|
+
if (!env.release_root) {
|
|
2111
|
+
console.error("WSL TaishanPi registry does not contain release_root. Reinstall with: npx -y embedlabs@latest local toolchain install --board taishanpi-1m-rk3566 --channel windows-test --mode compile");
|
|
2112
|
+
process.exit(2);
|
|
2113
|
+
}
|
|
2114
|
+
console.log(env.release_root);
|
|
2115
|
+
NODE
|
|
2116
|
+
`;
|
|
2117
|
+
const result = await runCommand(windowsWslBashCommand(script), homedir());
|
|
2118
|
+
if (result.exit_code !== 0 || result.stdout_tail.length < 1) {
|
|
2119
|
+
throw new Error(`Unable to resolve TaishanPi WSL release root: ${result.stderr_tail.join("\n") || result.stdout_tail.join("\n")}`);
|
|
2120
|
+
}
|
|
2121
|
+
return normalizeWindowsWslReleaseRoot(result.stdout_tail[result.stdout_tail.length - 1]);
|
|
2122
|
+
}
|
|
2123
|
+
function normalizeWindowsWslReleaseRoot(value) {
|
|
2124
|
+
const trimmed = value.trim();
|
|
2125
|
+
if (!trimmed) {
|
|
2126
|
+
throw new Error("WSL release root cannot be empty.");
|
|
2127
|
+
}
|
|
2128
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed)) {
|
|
2129
|
+
return windowsPathToWslPath(resolve(trimmed), "release-root");
|
|
2130
|
+
}
|
|
2131
|
+
if (trimmed.startsWith("/")) {
|
|
2132
|
+
return trimTrailingSlash(trimmed);
|
|
2133
|
+
}
|
|
2134
|
+
throw new Error(`WSL release root must be a Linux absolute path or Windows drive path, got ${value}.`);
|
|
2135
|
+
}
|
|
2136
|
+
function windowsPathToWslPath(value, label) {
|
|
2137
|
+
const normalized = value.replaceAll("\\", "/");
|
|
2138
|
+
const match = normalized.match(/^([A-Za-z]):\/(.*)$/);
|
|
2139
|
+
if (!match) {
|
|
2140
|
+
throw new Error(`Windows WSL ${label} path must be an absolute drive path, got ${value}.`);
|
|
2141
|
+
}
|
|
2142
|
+
return `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
2143
|
+
}
|
|
2144
|
+
function windowsWslBashCommand(script) {
|
|
2145
|
+
const command = ["wsl.exe"];
|
|
2146
|
+
const distro = process.env.EMBEDLABS_WINDOWS_WSL_DISTRO?.trim();
|
|
2147
|
+
if (distro) {
|
|
2148
|
+
command.push("-d", distro);
|
|
2149
|
+
}
|
|
2150
|
+
command.push("--", "bash", "-lc", script);
|
|
2151
|
+
return command;
|
|
2152
|
+
}
|
|
2153
|
+
function posixDirname(value) {
|
|
2154
|
+
const index = value.lastIndexOf("/");
|
|
2155
|
+
return index > 0 ? value.slice(0, index) : "/";
|
|
2156
|
+
}
|
|
2157
|
+
function sh(value) {
|
|
2158
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
2159
|
+
}
|
|
2020
2160
|
async function localCompileResult(input) {
|
|
2021
2161
|
await access(input.artifactPath, constants.R_OK);
|
|
2022
2162
|
const artifactInfo = await stat(input.artifactPath);
|