@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
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { arch, hostname, homedir, platform, tmpdir } from "node:os";
|
|
|
8
8
|
import { basename, delimiter, dirname, join, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { composeBootLogoPackage } from "./image-compose.js";
|
|
11
|
-
import { buildTaishanPiQtSmoke, compileTaishanPiSingleFile, currentLocalToolchain, installLocalToolchain, latestLocalToolchain, listLocalToolchainEnvironments, uninstallLocalToolchain, validateLocalToolchain } from "./local-toolchain.js";
|
|
11
|
+
import { buildTaishanPiQtSmoke, compileTaishanPiSingleFile, currentLocalToolchain, installLocalToolchain, latestLocalToolchain, listLocalToolchainEnvironments, uninstallLocalToolchain, validateLocalToolchain, windowsWslInstall, windowsWslStatus } from "./local-toolchain.js";
|
|
12
12
|
import { fail, ok } from "@embed-labs/protocol";
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
const CLI_MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
@@ -104,6 +104,8 @@ const LOCAL_TOOLCHAIN_CURRENT_USAGE = "Usage: embed local toolchain current [--i
|
|
|
104
104
|
const LOCAL_TOOLCHAIN_INSTALL_USAGE = "Usage: embed local toolchain install [--board taishanpi-1m-rk3566|pico2w-rp2350-monitor|coloreasypico2-rp2350-monitor] [--channel stable] [--metadata-root <path>] [--source-url <tar.gz-url>|--source-release-root <path>] [--install-root <path>] [--mode minimal|runtime|compile|qt|firmware|full|images] [--force] [--json]\nDefault source: the production download channel at download.embedboard.com.";
|
|
105
105
|
const LOCAL_TOOLCHAIN_UNINSTALL_USAGE = "Usage: embed local toolchain uninstall --board taishanpi-1m-rk3566|pico2w-rp2350-monitor|coloreasypico2-rp2350-monitor [--install-root <path>] [--json]";
|
|
106
106
|
const LOCAL_TOOLCHAIN_VALIDATE_USAGE = "Usage: embed local toolchain validate [--board taishanpi-1m-rk3566|pico2w-rp2350-monitor|coloreasypico2-rp2350-monitor] [--release-root <path>] [--mode minimal|runtime|compile|qt|firmware|full|images] [--json]";
|
|
107
|
+
const LOCAL_WSL_STATUS_USAGE = "Usage: embed local wsl status [--json]";
|
|
108
|
+
const LOCAL_WSL_INSTALL_USAGE = "Usage: embed local wsl install [--distribution Ubuntu] [--no-launch true|false] [--web-download true|false] [--timeout-ms 600000] [--json]";
|
|
107
109
|
const LOCAL_COMPILE_TAISHANPI_USAGE = "Usage: embed local compile taishanpi --source <main.c|main.cpp> --output <artifact> [--release-root <path>] [--account <account_id>] [--json]";
|
|
108
110
|
const LOCAL_BUILD_QT_SMOKE_USAGE = "Usage: embed local build qt-smoke --build-dir <dir> [--source <qt-cmake-dir>] [--target-name <executable>] [--release-root <path>] [--account <account_id>] [--json]";
|
|
109
111
|
const AUTH_DEVICE_STATUS_USAGE = "Usage: embed auth device status [--json]";
|
|
@@ -635,6 +637,21 @@ async function main(argv) {
|
|
|
635
637
|
}
|
|
636
638
|
return output(parsed, ok(await validateLocalToolchain(request)), renderLocalToolchainValidation);
|
|
637
639
|
}
|
|
640
|
+
if (action === "wsl" && parsed.command[2] === "status") {
|
|
641
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json"]);
|
|
642
|
+
const extra = parsed.command.slice(3);
|
|
643
|
+
if (unknownFlag || extra.length > 0) {
|
|
644
|
+
return output(parsed, fail("invalid_args", unknownFlag ? `Unknown flag --${unknownFlag}. ${LOCAL_WSL_STATUS_USAGE}` : LOCAL_WSL_STATUS_USAGE), undefined, 2);
|
|
645
|
+
}
|
|
646
|
+
return output(parsed, ok(await windowsWslStatus()), renderWindowsWslStatus);
|
|
647
|
+
}
|
|
648
|
+
if (action === "wsl" && parsed.command[2] === "install") {
|
|
649
|
+
const request = localWslInstallRequest(parsed);
|
|
650
|
+
if (typeof request === "string") {
|
|
651
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
652
|
+
}
|
|
653
|
+
return output(parsed, ok(await windowsWslInstall(request)), renderWindowsWslInstall);
|
|
654
|
+
}
|
|
638
655
|
if (action === "compile" && parsed.command[2] === "taishanpi") {
|
|
639
656
|
const request = localCompileTaishanPiRequest(parsed, await authStatus());
|
|
640
657
|
if (typeof request === "string") {
|
|
@@ -657,6 +674,8 @@ async function main(argv) {
|
|
|
657
674
|
LOCAL_TOOLCHAIN_INSTALL_USAGE,
|
|
658
675
|
LOCAL_TOOLCHAIN_UNINSTALL_USAGE,
|
|
659
676
|
LOCAL_TOOLCHAIN_VALIDATE_USAGE,
|
|
677
|
+
LOCAL_WSL_STATUS_USAGE,
|
|
678
|
+
LOCAL_WSL_INSTALL_USAGE,
|
|
660
679
|
LOCAL_COMPILE_TAISHANPI_USAGE,
|
|
661
680
|
LOCAL_BUILD_QT_SMOKE_USAGE
|
|
662
681
|
].join("\n")), undefined, 2);
|
|
@@ -1088,11 +1107,18 @@ async function main(argv) {
|
|
|
1088
1107
|
return output(parsed, fail("unknown_command", `Unknown command: ${parsed.command.join(" ")}`), undefined, 2);
|
|
1089
1108
|
}
|
|
1090
1109
|
catch (error) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1111
|
+
return output(parsed, fail("command_failed", message, {
|
|
1112
|
+
remediation: commandFailureRemediation(message)
|
|
1093
1113
|
}), undefined, 1);
|
|
1094
1114
|
}
|
|
1095
1115
|
}
|
|
1116
|
+
function commandFailureRemediation(message) {
|
|
1117
|
+
if (message.includes("TaishanPi") && message.includes("WSL2")) {
|
|
1118
|
+
return "Run: embedlabs local wsl status";
|
|
1119
|
+
}
|
|
1120
|
+
return `Check that embed-local-bridge is running at ${DEFAULT_BRIDGE_URL}. Start it with: embed bridge start`;
|
|
1121
|
+
}
|
|
1096
1122
|
function parseArgs(argv) {
|
|
1097
1123
|
const command = [];
|
|
1098
1124
|
const flags = {};
|
|
@@ -5710,6 +5736,32 @@ function localToolchainLatestRequest(parsed) {
|
|
|
5710
5736
|
return metadataRoot.error;
|
|
5711
5737
|
return { boardId: board.value, channel: channel.value, metadataRoot: metadataRoot.value };
|
|
5712
5738
|
}
|
|
5739
|
+
function localWslInstallRequest(parsed) {
|
|
5740
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "distribution", "distro", "no-launch", "web-download", "timeout-ms"]);
|
|
5741
|
+
if (unknownFlag) {
|
|
5742
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_WSL_INSTALL_USAGE}`;
|
|
5743
|
+
}
|
|
5744
|
+
const extra = parsed.command.slice(3);
|
|
5745
|
+
if (extra.length > 0) {
|
|
5746
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_WSL_INSTALL_USAGE}`;
|
|
5747
|
+
}
|
|
5748
|
+
const distribution = stringFlag(parsed, "distribution") ?? stringFlag(parsed, "distro");
|
|
5749
|
+
const noLaunch = optionalBooleanFlag(parsed, "no-launch");
|
|
5750
|
+
if (typeof noLaunch === "string")
|
|
5751
|
+
return noLaunch;
|
|
5752
|
+
const webDownload = optionalBooleanFlag(parsed, "web-download");
|
|
5753
|
+
if (typeof webDownload === "string")
|
|
5754
|
+
return webDownload;
|
|
5755
|
+
const timeoutMs = optionalIntegerFlag(parsed, "timeout-ms", 1_000, 3_600_000);
|
|
5756
|
+
if (timeoutMs.error)
|
|
5757
|
+
return timeoutMs.error;
|
|
5758
|
+
return {
|
|
5759
|
+
distribution,
|
|
5760
|
+
noLaunch,
|
|
5761
|
+
webDownload,
|
|
5762
|
+
timeoutMs: timeoutMs.value
|
|
5763
|
+
};
|
|
5764
|
+
}
|
|
5713
5765
|
function localToolchainCurrentRequest(parsed) {
|
|
5714
5766
|
const unknownFlag = firstUnknownFlag(parsed, ["json", "install-root"]);
|
|
5715
5767
|
if (unknownFlag) {
|
|
@@ -7103,6 +7155,61 @@ function localToolchainStatusLabel(status) {
|
|
|
7103
7155
|
return "unsupported_host/当前系统暂不支持";
|
|
7104
7156
|
return status;
|
|
7105
7157
|
}
|
|
7158
|
+
function renderWindowsWslStatus(result) {
|
|
7159
|
+
const lines = [
|
|
7160
|
+
"Windows WSL2 status",
|
|
7161
|
+
`host=${result.host}`,
|
|
7162
|
+
`applicable=${result.applicable}`,
|
|
7163
|
+
`wsl_available=${result.wsl_available}`,
|
|
7164
|
+
`usable=${result.usable}`,
|
|
7165
|
+
`checked_at=${result.checked_at}`,
|
|
7166
|
+
`status_command=${result.commands.status}`,
|
|
7167
|
+
`list_command=${result.commands.list}`,
|
|
7168
|
+
`list_online_command=${result.commands.list_online}`,
|
|
7169
|
+
`install_command=${result.commands.install_ubuntu}`
|
|
7170
|
+
];
|
|
7171
|
+
if (result.distributions.length > 0) {
|
|
7172
|
+
lines.push("distributions:");
|
|
7173
|
+
for (const distro of result.distributions) {
|
|
7174
|
+
lines.push(` ${distro.default ? "*" : "-"} ${distro.name} state=${distro.state ?? "unknown"} version=${distro.version ?? "unknown"}`);
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
if (result.online_distributions.length > 0) {
|
|
7178
|
+
lines.push("online_distributions:");
|
|
7179
|
+
for (const distro of result.online_distributions) {
|
|
7180
|
+
lines.push(` ${distro.default ? "*" : "-"} ${distro.name}${distro.friendly_name ? ` (${distro.friendly_name})` : ""}`);
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
for (const note of result.notes) {
|
|
7184
|
+
lines.push(`note=${note}`);
|
|
7185
|
+
}
|
|
7186
|
+
return lines.join("\n");
|
|
7187
|
+
}
|
|
7188
|
+
function renderWindowsWslInstall(result) {
|
|
7189
|
+
const lines = [
|
|
7190
|
+
"Windows WSL2 install",
|
|
7191
|
+
`host=${result.host}`,
|
|
7192
|
+
`command=${result.command.join(" ")}`,
|
|
7193
|
+
`exit_code=${result.exit_code}`,
|
|
7194
|
+
`usable_after=${result.status_after.usable}`
|
|
7195
|
+
];
|
|
7196
|
+
if (result.stdout_tail.length > 0) {
|
|
7197
|
+
lines.push("stdout_tail:");
|
|
7198
|
+
for (const line of result.stdout_tail) {
|
|
7199
|
+
lines.push(` ${line}`);
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
if (result.stderr_tail.length > 0) {
|
|
7203
|
+
lines.push("stderr_tail:");
|
|
7204
|
+
for (const line of result.stderr_tail) {
|
|
7205
|
+
lines.push(` ${line}`);
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
for (const note of result.notes) {
|
|
7209
|
+
lines.push(`note=${note}`);
|
|
7210
|
+
}
|
|
7211
|
+
return lines.join("\n");
|
|
7212
|
+
}
|
|
7106
7213
|
function localToolchainComponentSummary(components) {
|
|
7107
7214
|
const totalBytes = components.reduce((total, component) => total + component.size_bytes, 0);
|
|
7108
7215
|
return `${components.length} components, ${formatByteSize(totalBytes)}`;
|
|
@@ -7927,6 +8034,24 @@ function optionalPositiveIntegerFlag(parsed, name) {
|
|
|
7927
8034
|
}
|
|
7928
8035
|
return { value };
|
|
7929
8036
|
}
|
|
8037
|
+
function optionalIntegerFlag(parsed, name, min, max) {
|
|
8038
|
+
const values = flagValues(parsed, name);
|
|
8039
|
+
if (values.length === 0) {
|
|
8040
|
+
return {};
|
|
8041
|
+
}
|
|
8042
|
+
if (values.some((value) => typeof value !== "string")) {
|
|
8043
|
+
return { error: `--${name} requires a value.` };
|
|
8044
|
+
}
|
|
8045
|
+
const raw = values[values.length - 1].trim();
|
|
8046
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
8047
|
+
return { error: `--${name} must be an integer.` };
|
|
8048
|
+
}
|
|
8049
|
+
const value = Number(raw);
|
|
8050
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) {
|
|
8051
|
+
return { error: `--${name} must be an integer from ${min} through ${max}.` };
|
|
8052
|
+
}
|
|
8053
|
+
return { value };
|
|
8054
|
+
}
|
|
7930
8055
|
function jsonObjectListFlag(parsed, name) {
|
|
7931
8056
|
const objects = [];
|
|
7932
8057
|
for (const raw of flagValues(parsed, name)) {
|