@kvell007/embed-labs-cli 0.1.0-alpha.55 → 0.1.0-alpha.57
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/index.js +128 -3
- package/dist/index.js.map +1 -1
- package/dist/local-toolchain.d.ts +46 -0
- package/dist/local-toolchain.js +245 -5
- package/dist/local-toolchain.js.map +1 -1
- package/package.json +3 -3
|
@@ -116,6 +116,50 @@ export interface LocalToolchainLatestResult {
|
|
|
116
116
|
download?: LocalToolchainDownloadPlan;
|
|
117
117
|
download_error?: string;
|
|
118
118
|
}
|
|
119
|
+
export interface WindowsWslStatusResult {
|
|
120
|
+
host: string;
|
|
121
|
+
platform: string;
|
|
122
|
+
arch: string;
|
|
123
|
+
checked_at: string;
|
|
124
|
+
applicable: boolean;
|
|
125
|
+
wsl_available: boolean;
|
|
126
|
+
usable: boolean;
|
|
127
|
+
distributions: Array<{
|
|
128
|
+
name: string;
|
|
129
|
+
state?: string;
|
|
130
|
+
version?: string;
|
|
131
|
+
default?: boolean;
|
|
132
|
+
}>;
|
|
133
|
+
online_distributions: Array<{
|
|
134
|
+
name: string;
|
|
135
|
+
friendly_name?: string;
|
|
136
|
+
default?: boolean;
|
|
137
|
+
}>;
|
|
138
|
+
commands: {
|
|
139
|
+
status: string;
|
|
140
|
+
list: string;
|
|
141
|
+
list_online: string;
|
|
142
|
+
install_ubuntu: string;
|
|
143
|
+
};
|
|
144
|
+
notes: string[];
|
|
145
|
+
}
|
|
146
|
+
export interface WindowsWslInstallOptions {
|
|
147
|
+
distribution?: string;
|
|
148
|
+
noLaunch?: boolean;
|
|
149
|
+
webDownload?: boolean;
|
|
150
|
+
timeoutMs?: number;
|
|
151
|
+
}
|
|
152
|
+
export interface WindowsWslInstallResult {
|
|
153
|
+
host: string;
|
|
154
|
+
platform: string;
|
|
155
|
+
arch: string;
|
|
156
|
+
command: string[];
|
|
157
|
+
exit_code: number;
|
|
158
|
+
stdout_tail: string[];
|
|
159
|
+
stderr_tail: string[];
|
|
160
|
+
status_after: WindowsWslStatusResult;
|
|
161
|
+
notes: string[];
|
|
162
|
+
}
|
|
119
163
|
export interface LocalToolchainInstallOptions extends LocalToolchainLatestOptions {
|
|
120
164
|
sourceReleaseRoot?: string;
|
|
121
165
|
sourceUrl?: string;
|
|
@@ -281,6 +325,8 @@ export declare function latestLocalToolchain(options?: LocalToolchainLatestOptio
|
|
|
281
325
|
export declare function currentLocalToolchain(installRoot?: string, boardId?: string): Promise<LocalToolchainCurrentResult>;
|
|
282
326
|
export declare function listLocalToolchainEnvironments(options?: LocalToolchainListOptions): Promise<LocalToolchainListResult>;
|
|
283
327
|
export declare function installLocalToolchain(options?: LocalToolchainInstallOptions): Promise<LocalToolchainInstallResult>;
|
|
328
|
+
export declare function windowsWslStatus(): Promise<WindowsWslStatusResult>;
|
|
329
|
+
export declare function windowsWslInstall(options?: WindowsWslInstallOptions): Promise<WindowsWslInstallResult>;
|
|
284
330
|
export declare function uninstallLocalToolchain(options?: LocalToolchainUninstallOptions): Promise<LocalToolchainUninstallResult>;
|
|
285
331
|
export declare function validateLocalToolchain(input?: string | {
|
|
286
332
|
releaseRoot?: string;
|
package/dist/local-toolchain.js
CHANGED
|
@@ -205,6 +205,9 @@ export async function latestLocalToolchain(options = {}) {
|
|
|
205
205
|
catch (error) {
|
|
206
206
|
downloadError = error instanceof Error ? error.message : String(error);
|
|
207
207
|
}
|
|
208
|
+
if (!download && isNativeWindowsTaishanPiHost(canonicalBoardId, hostId())) {
|
|
209
|
+
downloadError = taishanPiWindowsRequirementMessage(hostId());
|
|
210
|
+
}
|
|
208
211
|
return {
|
|
209
212
|
board_id: canonicalBoardId,
|
|
210
213
|
channel: channel.channel,
|
|
@@ -357,6 +360,9 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
357
360
|
catch (error) {
|
|
358
361
|
downloadError = error instanceof Error ? error.message : String(error);
|
|
359
362
|
}
|
|
363
|
+
if (!download && isNativeWindowsTaishanPiHost(boardId, host)) {
|
|
364
|
+
downloadError = taishanPiWindowsRequirementMessage(host);
|
|
365
|
+
}
|
|
360
366
|
const latestVersion = download?.version ?? board.version;
|
|
361
367
|
const currentForBoard = await currentLocalToolchain(installRoot, boardId);
|
|
362
368
|
const installedCandidate = currentForBoard.installed
|
|
@@ -371,9 +377,12 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
371
377
|
}
|
|
372
378
|
: undefined;
|
|
373
379
|
const updateAvailable = !!installed?.version && installed.version !== latestVersion;
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
:
|
|
380
|
+
const nativeWindowsTaishanPi = isNativeWindowsTaishanPiHost(boardId, host);
|
|
381
|
+
const effectiveHostSupport = nativeWindowsTaishanPi
|
|
382
|
+
? { supported: false, unsupportedPackages: ["taishanpi-1m-rk3566 native Windows toolchain"] }
|
|
383
|
+
: download || installed
|
|
384
|
+
? { supported: true, unsupportedPackages: [] }
|
|
385
|
+
: hostSupport;
|
|
377
386
|
const status = !effectiveHostSupport.supported
|
|
378
387
|
? "unsupported_host"
|
|
379
388
|
: updateAvailable
|
|
@@ -410,8 +419,8 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
410
419
|
},
|
|
411
420
|
packages,
|
|
412
421
|
components: download?.components?.map(localToolchainEnvironmentComponent),
|
|
413
|
-
install_command: `embedlabs local toolchain install --board ${boardId} --mode ${mode}`,
|
|
414
|
-
update_command: `embedlabs local toolchain install --board ${boardId} --mode ${mode} --force`,
|
|
422
|
+
install_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId} --mode ${mode}`,
|
|
423
|
+
update_command: nativeWindowsTaishanPi ? "embedlabs local wsl status" : `embedlabs local toolchain install --board ${boardId} --mode ${mode} --force`,
|
|
415
424
|
notes: environmentNotes({ boardId, status, downloadError, unsupportedPackages: effectiveHostSupport.unsupportedPackages })
|
|
416
425
|
});
|
|
417
426
|
}
|
|
@@ -430,6 +439,9 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
430
439
|
}
|
|
431
440
|
export async function installLocalToolchain(options = {}) {
|
|
432
441
|
const latest = await latestLocalToolchain(options);
|
|
442
|
+
if (isNativeWindowsTaishanPiHost(latest.board_id, latest.host)) {
|
|
443
|
+
throw new Error(taishanPiWindowsRequirementMessage(latest.host));
|
|
444
|
+
}
|
|
433
445
|
const installRoot = resolveInstallRoot(options.installRoot);
|
|
434
446
|
const releaseRoot = resolve(installRoot, "toolchains", latest.board_id, latest.version);
|
|
435
447
|
const installMode = normalizeLocalToolchainInstallMode(options.mode ?? latest.download?.default_mode);
|
|
@@ -511,6 +523,100 @@ export async function installLocalToolchain(options = {}) {
|
|
|
511
523
|
await rm(tempDir, { recursive: true, force: true });
|
|
512
524
|
}
|
|
513
525
|
}
|
|
526
|
+
export async function windowsWslStatus() {
|
|
527
|
+
const result = {
|
|
528
|
+
host: hostId(),
|
|
529
|
+
platform: platform(),
|
|
530
|
+
arch: arch(),
|
|
531
|
+
checked_at: new Date().toISOString(),
|
|
532
|
+
applicable: platform() === "win32",
|
|
533
|
+
wsl_available: false,
|
|
534
|
+
usable: false,
|
|
535
|
+
distributions: [],
|
|
536
|
+
online_distributions: [],
|
|
537
|
+
commands: {
|
|
538
|
+
status: "wsl.exe --status",
|
|
539
|
+
list: "wsl.exe -l -v",
|
|
540
|
+
list_online: "wsl.exe --list --online",
|
|
541
|
+
install_ubuntu: "wsl.exe --install -d Ubuntu --web-download"
|
|
542
|
+
},
|
|
543
|
+
notes: []
|
|
544
|
+
};
|
|
545
|
+
if (!result.applicable) {
|
|
546
|
+
result.notes.push("WSL2 is only required for TaishanPi local development on Windows hosts.");
|
|
547
|
+
return result;
|
|
548
|
+
}
|
|
549
|
+
const whereWsl = await runCommand(["cmd", "/c", "where wsl.exe"], homedir());
|
|
550
|
+
result.wsl_available = whereWsl.exit_code === 0;
|
|
551
|
+
if (!result.wsl_available) {
|
|
552
|
+
result.notes.push("wsl.exe was not found. Enable Windows Subsystem for Linux before installing TaishanPi local tools.");
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
const list = await runCommand(["wsl.exe", "-l", "-v"], homedir());
|
|
556
|
+
const normalized = normalizeWindowsCommandText([...list.stdout_tail, ...list.stderr_tail].join("\n"));
|
|
557
|
+
result.distributions = list.exit_code === 0 ? parseWslDistributionList(normalized) : [];
|
|
558
|
+
const online = await runCommand(["wsl.exe", "--list", "--online"], homedir());
|
|
559
|
+
const normalizedOnline = normalizeWindowsCommandText([...online.stdout_tail, ...online.stderr_tail].join("\n"));
|
|
560
|
+
result.online_distributions = online.exit_code === 0 ? parseWslOnlineDistributionList(normalizedOnline) : [];
|
|
561
|
+
result.usable = list.exit_code === 0 && result.distributions.length > 0;
|
|
562
|
+
if (!result.usable) {
|
|
563
|
+
result.notes.push("No usable WSL2 distribution is configured yet. Install Ubuntu, restart Windows if requested, then run Embed Labs TaishanPi tools inside WSL2.");
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
result.notes.push("Run TaishanPi local compile, Qt, and image tooling inside the listed WSL2 distribution. Native Windows TaishanPi toolchain packages are intentionally not published.");
|
|
567
|
+
}
|
|
568
|
+
if (result.host === "win32-arm64") {
|
|
569
|
+
result.notes.push("This Windows ARM64 host cannot run the current TaishanPi linux-x86_64 WSL2 SDK package. Use Windows x64 with WSL2, or publish a linux-arm64/native Windows TaishanPi package before claiming TaishanPi parity on this host.");
|
|
570
|
+
}
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
export async function windowsWslInstall(options = {}) {
|
|
574
|
+
const distro = options.distribution?.trim() || "Ubuntu";
|
|
575
|
+
const command = ["wsl.exe", "--install", "-d", distro];
|
|
576
|
+
if (options.noLaunch !== false) {
|
|
577
|
+
command.push("--no-launch");
|
|
578
|
+
}
|
|
579
|
+
if (options.webDownload !== false) {
|
|
580
|
+
command.push("--web-download");
|
|
581
|
+
}
|
|
582
|
+
const notes = [];
|
|
583
|
+
if (platform() !== "win32") {
|
|
584
|
+
notes.push("WSL installation can only run on Windows hosts.");
|
|
585
|
+
return {
|
|
586
|
+
host: hostId(),
|
|
587
|
+
platform: platform(),
|
|
588
|
+
arch: arch(),
|
|
589
|
+
command,
|
|
590
|
+
exit_code: 1,
|
|
591
|
+
stdout_tail: [],
|
|
592
|
+
stderr_tail: [],
|
|
593
|
+
status_after: await windowsWslStatus(),
|
|
594
|
+
notes
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
const result = await runCommandWithTimeout(command, homedir(), options.timeoutMs ?? 600_000);
|
|
598
|
+
const statusAfter = await windowsWslStatus();
|
|
599
|
+
if (result.exit_code === 0) {
|
|
600
|
+
notes.push("WSL install command completed. If Windows requests a restart, restart before running TaishanPi local tools.");
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
notes.push("WSL install command did not complete successfully. Run the same command in an elevated Windows terminal if the error indicates permissions or reboot requirements.");
|
|
604
|
+
}
|
|
605
|
+
if (!statusAfter.usable) {
|
|
606
|
+
notes.push("WSL2 is still not usable after this command. Complete first-launch setup for the distribution, then rerun embedlabs local wsl status.");
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
host: hostId(),
|
|
610
|
+
platform: platform(),
|
|
611
|
+
arch: arch(),
|
|
612
|
+
command,
|
|
613
|
+
exit_code: result.exit_code,
|
|
614
|
+
stdout_tail: result.stdout_tail,
|
|
615
|
+
stderr_tail: result.stderr_tail,
|
|
616
|
+
status_after: statusAfter,
|
|
617
|
+
notes
|
|
618
|
+
};
|
|
619
|
+
}
|
|
514
620
|
export async function uninstallLocalToolchain(options = {}) {
|
|
515
621
|
if (!options.boardId?.trim()) {
|
|
516
622
|
throw new Error("board_id is required for local toolchain uninstall.");
|
|
@@ -1311,6 +1417,10 @@ function environmentNotes(input) {
|
|
|
1311
1417
|
if (isRp2350MonitorBoardId(input.boardId)) {
|
|
1312
1418
|
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.");
|
|
1313
1419
|
}
|
|
1420
|
+
if (isNativeWindowsTaishanPiHost(input.boardId, hostId())) {
|
|
1421
|
+
notes.push(taishanPiWindowsRequirementMessage(hostId()));
|
|
1422
|
+
notes.push("Check this computer with: embedlabs local wsl status");
|
|
1423
|
+
}
|
|
1314
1424
|
if (input.status === "available") {
|
|
1315
1425
|
notes.push("Environment is available but not installed on this computer.");
|
|
1316
1426
|
}
|
|
@@ -1359,6 +1469,74 @@ function hostId() {
|
|
|
1359
1469
|
}
|
|
1360
1470
|
return `${platform()}-${arch()}`;
|
|
1361
1471
|
}
|
|
1472
|
+
function isNativeWindowsTaishanPiHost(boardId, host) {
|
|
1473
|
+
return normalizeBoardId(boardId) === DEFAULT_BOARD_ID && host.startsWith("win32-");
|
|
1474
|
+
}
|
|
1475
|
+
function taishanPiWindowsRequirementMessage(host) {
|
|
1476
|
+
if (host === "win32-arm64") {
|
|
1477
|
+
return "TaishanPi local compile, Qt, image, and Rockchip tooling is not available on native Windows ARM64. The current Windows route requires Windows x64 with WSL2/Linux x86_64, or a future linux-arm64/native Windows TaishanPi package.";
|
|
1478
|
+
}
|
|
1479
|
+
return "TaishanPi local compile, Qt, image, and Rockchip tooling is supported on Windows through WSL2/Linux x86_64 only; native Windows TaishanPi packages are not published.";
|
|
1480
|
+
}
|
|
1481
|
+
function normalizeWindowsCommandText(value) {
|
|
1482
|
+
return value.replace(/\0/g, "").replace(/\r/g, "\n");
|
|
1483
|
+
}
|
|
1484
|
+
function parseWslDistributionList(value) {
|
|
1485
|
+
const distributions = [];
|
|
1486
|
+
const lines = value.split(/\n+/)
|
|
1487
|
+
.map((line) => line.trim())
|
|
1488
|
+
.filter(Boolean);
|
|
1489
|
+
for (const rawLine of lines) {
|
|
1490
|
+
if (/^(NAME|Windows Subsystem|Usage|Copyright|\-|用法|选项|命令)/i.test(rawLine)) {
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
const isDefault = rawLine.startsWith("*");
|
|
1494
|
+
const line = rawLine.replace(/^\*\s*/, "").trim();
|
|
1495
|
+
const parts = line.split(/\s+/).filter(Boolean);
|
|
1496
|
+
if (parts.length === 0 || parts[0].includes(":")) {
|
|
1497
|
+
continue;
|
|
1498
|
+
}
|
|
1499
|
+
const maybeVersion = parts.at(-1);
|
|
1500
|
+
const version = maybeVersion && /^[12]$/.test(maybeVersion) ? maybeVersion : undefined;
|
|
1501
|
+
const state = version && parts.length >= 3 ? parts.at(-2) : parts.length >= 2 ? parts.at(-1) : undefined;
|
|
1502
|
+
distributions.push({
|
|
1503
|
+
name: parts[0],
|
|
1504
|
+
state,
|
|
1505
|
+
version,
|
|
1506
|
+
default: isDefault
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
return distributions;
|
|
1510
|
+
}
|
|
1511
|
+
function parseWslOnlineDistributionList(value) {
|
|
1512
|
+
const distributions = [];
|
|
1513
|
+
const lines = value.split(/\n+/)
|
|
1514
|
+
.map((line) => line.trim())
|
|
1515
|
+
.filter(Boolean);
|
|
1516
|
+
let inTable = false;
|
|
1517
|
+
for (const rawLine of lines) {
|
|
1518
|
+
if (/^NAME\s+FRIENDLY NAME/i.test(rawLine)) {
|
|
1519
|
+
inTable = true;
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
if (!inTable) {
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
const isDefault = rawLine.startsWith("*");
|
|
1526
|
+
const line = rawLine.replace(/^\*\s*/, "").trim();
|
|
1527
|
+
const parts = line.split(/\s{2,}/).filter(Boolean);
|
|
1528
|
+
const name = parts[0]?.trim();
|
|
1529
|
+
if (!name || name.includes(":")) {
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
distributions.push({
|
|
1533
|
+
name,
|
|
1534
|
+
friendly_name: parts.slice(1).join(" ").trim() || undefined,
|
|
1535
|
+
default: isDefault
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return distributions;
|
|
1539
|
+
}
|
|
1362
1540
|
function resolveInstallRoot(installRoot) {
|
|
1363
1541
|
return resolve(installRoot
|
|
1364
1542
|
|| process.env.EMBEDLABS_HOME?.trim()
|
|
@@ -1800,6 +1978,68 @@ async function runCommand(command, cwd) {
|
|
|
1800
1978
|
});
|
|
1801
1979
|
});
|
|
1802
1980
|
}
|
|
1981
|
+
async function runCommandWithTimeout(command, cwd, timeoutMs) {
|
|
1982
|
+
return await new Promise((resolve) => {
|
|
1983
|
+
const child = spawn(command[0], command.slice(1), {
|
|
1984
|
+
cwd,
|
|
1985
|
+
env: process.env,
|
|
1986
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1987
|
+
});
|
|
1988
|
+
let stdout = "";
|
|
1989
|
+
let stderr = "";
|
|
1990
|
+
let settled = false;
|
|
1991
|
+
const timer = setTimeout(() => {
|
|
1992
|
+
if (settled) {
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
stderr += `Command timed out after ${timeoutMs} ms.\n`;
|
|
1996
|
+
child.kill("SIGTERM");
|
|
1997
|
+
setTimeout(() => {
|
|
1998
|
+
if (!settled) {
|
|
1999
|
+
child.kill("SIGKILL");
|
|
2000
|
+
}
|
|
2001
|
+
}, 2_000).unref();
|
|
2002
|
+
}, timeoutMs);
|
|
2003
|
+
child.stdout.setEncoding("utf8");
|
|
2004
|
+
child.stderr.setEncoding("utf8");
|
|
2005
|
+
child.stdout.on("data", (chunk) => {
|
|
2006
|
+
stdout += chunk;
|
|
2007
|
+
});
|
|
2008
|
+
child.stderr.on("data", (chunk) => {
|
|
2009
|
+
stderr += chunk;
|
|
2010
|
+
});
|
|
2011
|
+
child.on("error", (error) => {
|
|
2012
|
+
if (settled) {
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
settled = true;
|
|
2016
|
+
clearTimeout(timer);
|
|
2017
|
+
stderr += `${error.message}\n`;
|
|
2018
|
+
resolve({
|
|
2019
|
+
command,
|
|
2020
|
+
cwd,
|
|
2021
|
+
exit_code: 127,
|
|
2022
|
+
stdout_tail: tailLines(stdout),
|
|
2023
|
+
stderr_tail: tailLines(stderr)
|
|
2024
|
+
});
|
|
2025
|
+
});
|
|
2026
|
+
child.on("close", (code) => {
|
|
2027
|
+
if (settled) {
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
settled = true;
|
|
2031
|
+
clearTimeout(timer);
|
|
2032
|
+
const timedOut = stderr.includes(`Command timed out after ${timeoutMs} ms.`);
|
|
2033
|
+
resolve({
|
|
2034
|
+
command,
|
|
2035
|
+
cwd,
|
|
2036
|
+
exit_code: timedOut ? 124 : code ?? 1,
|
|
2037
|
+
stdout_tail: tailLines(stdout),
|
|
2038
|
+
stderr_tail: tailLines(stderr)
|
|
2039
|
+
});
|
|
2040
|
+
});
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
1803
2043
|
async function fileInfoFor(filePath) {
|
|
1804
2044
|
const result = await runCommand(["file", filePath], dirname(filePath));
|
|
1805
2045
|
return result.exit_code === 0 ? result.stdout_tail.join("\n") : undefined;
|