@kvell007/embed-labs-cli 0.1.0-alpha.11 → 0.1.0-alpha.13

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 CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createHash } from "node:crypto";
3
3
  import { constants } from "node:fs";
4
4
  import { spawn } from "node:child_process";
5
- import { access, cp, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
5
+ import { access, cp, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6
6
  import { createRequire } from "node:module";
7
7
  import { homedir, tmpdir } from "node:os";
8
8
  import { basename, delimiter, dirname, join, resolve } from "node:path";
@@ -83,8 +83,8 @@ const BUILD_IMAGE_DTB_USAGE = "Usage: embed build image dtb --dtb <local.dtb|loc
83
83
  const IMAGE_DTB_COMPOSE_USAGE = "Usage: embed image dtb compose --package <dtb-package.json> --base-image <boot.img|image.img> --output <image> [--manifest <manifest.json>] [--force] [--json]";
84
84
  const LOCAL_TOOLCHAIN_LATEST_USAGE = "Usage: embed local toolchain latest [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--json]";
85
85
  const LOCAL_TOOLCHAIN_CURRENT_USAGE = "Usage: embed local toolchain current [--install-root <path>] [--json]";
86
- const LOCAL_TOOLCHAIN_INSTALL_USAGE = "Usage: embed local toolchain install [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--source-url <tar.gz-url>|--source-release-root <path>] [--install-root <path>] [--force] [--json]\nDefault source: the production download channel at download.embedboard.com.";
87
- const LOCAL_TOOLCHAIN_VALIDATE_USAGE = "Usage: embed local toolchain validate [--release-root <path>] [--json]";
86
+ const LOCAL_TOOLCHAIN_INSTALL_USAGE = "Usage: embed local toolchain install [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--source-url <tar.gz-url>|--source-release-root <path>] [--install-root <path>] [--mode minimal|compile|qt|full|images] [--force] [--json]\nDefault source: the production download channel at download.embedboard.com.";
87
+ const LOCAL_TOOLCHAIN_VALIDATE_USAGE = "Usage: embed local toolchain validate [--release-root <path>] [--mode minimal|compile|qt|full|images] [--json]";
88
88
  const LOCAL_COMPILE_TAISHANPI_USAGE = "Usage: embed local compile taishanpi --source <main.c|main.cpp> --output <artifact> [--release-root <path>] [--account <account_id>] [--json]";
89
89
  const LOCAL_BUILD_QT_SMOKE_USAGE = "Usage: embed local build qt-smoke --build-dir <dir> [--source <qt-smoke-dir>] [--release-root <path>] [--account <account_id>] [--json]";
90
90
  const BOARD_REGISTRY_LIST_USAGE = "Usage: embed board registry list [--json]";
@@ -543,7 +543,7 @@ async function main(argv) {
543
543
  if (typeof request === "string") {
544
544
  return output(parsed, fail("invalid_args", request), undefined, 2);
545
545
  }
546
- return output(parsed, ok(await validateLocalToolchain(request.releaseRoot)), renderLocalToolchainValidation);
546
+ return output(parsed, ok(await validateLocalToolchain(request)), renderLocalToolchainValidation);
547
547
  }
548
548
  if (action === "compile" && parsed.command[2] === "taishanpi") {
549
549
  const request = localCompileTaishanPiRequest(parsed, await authStatus());
@@ -1351,17 +1351,29 @@ function isApiResponse(value) {
1351
1351
  return isJsonObject(error) && typeof error.code === "string" && typeof error.message === "string";
1352
1352
  }
1353
1353
  async function bridgeGet(path) {
1354
- const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`);
1354
+ const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`, {
1355
+ headers: bridgeHeaders()
1356
+ });
1355
1357
  return await response.json();
1356
1358
  }
1357
1359
  async function bridgePost(path, body) {
1358
1360
  const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`, {
1359
1361
  method: "POST",
1360
- headers: { "content-type": "application/json" },
1362
+ headers: bridgeHeaders({ "content-type": "application/json" }),
1361
1363
  body: JSON.stringify(body)
1362
1364
  });
1363
1365
  return await response.json();
1364
1366
  }
1367
+ function bridgeHeaders(base = {}) {
1368
+ const token = process.env.EMBED_BRIDGE_TOKEN?.trim();
1369
+ if (!token) {
1370
+ return base;
1371
+ }
1372
+ return {
1373
+ ...base,
1374
+ authorization: `Bearer ${token}`
1375
+ };
1376
+ }
1365
1377
  async function cloudGet(path) {
1366
1378
  return await cloudRequest("GET", path);
1367
1379
  }
@@ -1535,6 +1547,7 @@ async function installCodexPlugin(parsed, context) {
1535
1547
  }
1536
1548
  const targetRoot = codexPluginTargetRoot(parsed, context.installingAll);
1537
1549
  const targetPath = join(targetRoot, "embed-labs");
1550
+ const legacyCleanup = await cleanupLegacyCodexPluginRemnants(targetRoot);
1538
1551
  if (await pathExists(targetPath) && !booleanFlag(parsed, "force")) {
1539
1552
  return fail("plugin_already_installed", `Codex plugin already exists at ${targetPath}.`, {
1540
1553
  remediation: "Pass --force to replace it, or pass --codex-target/--target to install into a different directory."
@@ -1552,8 +1565,10 @@ async function installCodexPlugin(parsed, context) {
1552
1565
  command_hint: mcpRegistration.registered
1553
1566
  ? "Codex MCP was registered. Start a new Codex session to reload tools."
1554
1567
  : mcpRegistration.hint,
1568
+ warning: legacyCodexCleanupWarning(legacyCleanup),
1555
1569
  mcp_registered: mcpRegistration.registered,
1556
- mcp_warning: mcpRegistration.warning
1570
+ mcp_warning: mcpRegistration.warning,
1571
+ cleanup: legacyCleanup
1557
1572
  });
1558
1573
  }
1559
1574
  async function installOpenCodePlugin(parsed, context) {
@@ -1562,8 +1577,10 @@ async function installOpenCodePlugin(parsed, context) {
1562
1577
  return source;
1563
1578
  }
1564
1579
  const targetRoot = openCodePluginTargetRoot(parsed, context.installingAll);
1565
- const wrapperPath = join(targetRoot, "plugins", "development-board-toolchain.js");
1566
- if (await pathExists(wrapperPath) && !booleanFlag(parsed, "force")) {
1580
+ const globalInstall = isGlobalOpenCodeRoot(targetRoot);
1581
+ const wrapperPath = join(targetRoot, "plugins", "embed-labs.js");
1582
+ const legacyCleanup = await cleanupLegacyOpenCodePluginRemnants(targetRoot, globalInstall);
1583
+ if (!globalInstall && await pathExists(wrapperPath) && !booleanFlag(parsed, "force")) {
1567
1584
  return fail("plugin_already_installed", `OpenCode plugin wrapper already exists at ${wrapperPath}.`, {
1568
1585
  remediation: "Pass --force to replace it, or pass --opencode-target/--target to install into a different directory."
1569
1586
  });
@@ -1589,15 +1606,25 @@ async function installOpenCodePlugin(parsed, context) {
1589
1606
  });
1590
1607
  }
1591
1608
  await ensureOpenCodeInstallPackageJson(targetRoot);
1592
- await writeFile(wrapperPath, `export { default, DevelopmentBoardToolchainPlugin } from "embed-labs";\n`, "utf8");
1609
+ if (globalInstall) {
1610
+ await rm(wrapperPath, { force: true });
1611
+ legacyCleanup.legacy_removed_config_entries?.push(...await ensureOpenCodeGlobalPluginConfig());
1612
+ }
1613
+ else {
1614
+ await writeFile(wrapperPath, `export { default, DevelopmentBoardToolchainPlugin } from "embed-labs";\n`, "utf8");
1615
+ }
1593
1616
  const duplicateWarning = await openCodeDuplicatePluginWarning(targetRoot);
1617
+ const cleanupWarning = legacyOpenCodeCleanupWarning(legacyCleanup);
1594
1618
  return ok({
1595
1619
  id: "opencode",
1596
1620
  target_path: targetRoot,
1597
1621
  source: source.data.sourceLabel,
1598
1622
  version: source.data.version,
1599
- command_hint: "Start OpenCode from the project containing this .opencode directory.",
1600
- warning: duplicateWarning
1623
+ command_hint: globalInstall
1624
+ ? "Restart OpenCode so the global embed-labs package plugin is reloaded."
1625
+ : "Start OpenCode from the project containing this .opencode directory.",
1626
+ warning: combineWarnings(cleanupWarning, duplicateWarning),
1627
+ cleanup: legacyCleanup
1601
1628
  });
1602
1629
  }
1603
1630
  async function resolveCodexPluginSource(context) {
@@ -1811,7 +1838,204 @@ function openCodePluginTargetRoot(parsed, installingAll) {
1811
1838
  return resolve(target ?? defaultOpenCodeRoot());
1812
1839
  }
1813
1840
  function defaultCodexPluginRoot() {
1814
- return join(process.env.CODEX_HOME?.trim() || join(homedir(), ".codex"), "plugins");
1841
+ return join(defaultCodexHome(), "plugins");
1842
+ }
1843
+ function defaultCodexHome() {
1844
+ return resolve(process.env.CODEX_HOME?.trim() || join(homedir(), ".codex"));
1845
+ }
1846
+ function codexConfigPath() {
1847
+ return join(defaultCodexHome(), "config.toml");
1848
+ }
1849
+ async function cleanupLegacyCodexPluginRemnants(targetRoot) {
1850
+ const removedPaths = [];
1851
+ const removedConfigTables = [];
1852
+ const warnings = [];
1853
+ const legacyPaths = [
1854
+ join(targetRoot, "dbt-agent"),
1855
+ join(targetRoot, "development-board-toolchain"),
1856
+ join(targetRoot, "cache", "embed-labs", "dbt-agent"),
1857
+ join(targetRoot, "cache", "dbt-agent"),
1858
+ join(targetRoot, "cache", "plugins", "dbt-agent")
1859
+ ];
1860
+ legacyPaths.push(...await discoverLegacyCodexCachePaths(targetRoot));
1861
+ if (resolve(targetRoot) === resolve(defaultCodexPluginRoot())) {
1862
+ legacyPaths.push(join(defaultCodexHome(), ".tmp", "plugins"), join(defaultCodexHome(), ".tmp", "plugins.sha"));
1863
+ }
1864
+ for (const candidate of legacyPaths) {
1865
+ try {
1866
+ if (await pathExists(candidate)) {
1867
+ await rm(candidate, { recursive: true, force: true });
1868
+ removedPaths.push(candidate);
1869
+ }
1870
+ }
1871
+ catch (error) {
1872
+ warnings.push(`Could not remove ${candidate}: ${error instanceof Error ? error.message : String(error)}`);
1873
+ }
1874
+ }
1875
+ if (resolve(targetRoot) === resolve(defaultCodexPluginRoot())) {
1876
+ const configPath = codexConfigPath();
1877
+ try {
1878
+ if (await pathExists(configPath)) {
1879
+ const current = await readFile(configPath, "utf8");
1880
+ const updated = removeLegacyCodexConfigTables(current);
1881
+ if (updated.text !== current) {
1882
+ await writeFile(configPath, updated.text, "utf8");
1883
+ }
1884
+ removedConfigTables.push(...updated.removedTables);
1885
+ }
1886
+ }
1887
+ catch (error) {
1888
+ warnings.push(`Could not update ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
1889
+ }
1890
+ }
1891
+ const removedHistoryEntries = await cleanupLegacyCodexHistoryMentions(warnings);
1892
+ return {
1893
+ legacy_removed_paths: Array.from(new Set(removedPaths)),
1894
+ legacy_removed_config_tables: removedConfigTables,
1895
+ legacy_removed_history_entries: removedHistoryEntries,
1896
+ ...(warnings.length > 0 ? { warnings } : {})
1897
+ };
1898
+ }
1899
+ async function discoverLegacyCodexCachePaths(targetRoot) {
1900
+ const paths = [];
1901
+ const cacheRoot = join(targetRoot, "cache");
1902
+ try {
1903
+ const marketplaces = await readdir(cacheRoot, { withFileTypes: true });
1904
+ for (const entry of marketplaces) {
1905
+ if (!entry.isDirectory())
1906
+ continue;
1907
+ paths.push(join(cacheRoot, entry.name, "dbt-agent"));
1908
+ paths.push(join(cacheRoot, entry.name, "development-board-toolchain"));
1909
+ }
1910
+ }
1911
+ catch {
1912
+ return paths;
1913
+ }
1914
+ return paths;
1915
+ }
1916
+ async function cleanupLegacyCodexHistoryMentions(warnings) {
1917
+ const historyPath = join(defaultCodexHome(), "history.jsonl");
1918
+ try {
1919
+ if (!await pathExists(historyPath))
1920
+ return 0;
1921
+ const current = await readFile(historyPath, "utf8");
1922
+ const lines = current.split("\n");
1923
+ let removed = 0;
1924
+ const kept = lines.filter((line) => {
1925
+ if (!line)
1926
+ return true;
1927
+ if (isLegacyCodexHistoryMention(line)) {
1928
+ removed += 1;
1929
+ return false;
1930
+ }
1931
+ return true;
1932
+ });
1933
+ if (removed > 0) {
1934
+ await writeFile(historyPath, kept.join("\n"), "utf8");
1935
+ }
1936
+ return removed;
1937
+ }
1938
+ catch (error) {
1939
+ warnings.push(`Could not clean Codex legacy history mentions: ${error instanceof Error ? error.message : String(error)}`);
1940
+ return 0;
1941
+ }
1942
+ }
1943
+ function isLegacyCodexHistoryMention(line) {
1944
+ return line.includes("plugin://dbt-agent@plugins")
1945
+ || line.includes("plugin://dbt-agent@embed-labs")
1946
+ || /dbt-agent/i.test(line);
1947
+ }
1948
+ function removeLegacyCodexConfigTables(text) {
1949
+ const lines = text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
1950
+ const output = [];
1951
+ const removedTables = [];
1952
+ let skipping = false;
1953
+ for (const line of lines) {
1954
+ const table = parseTomlTableHeader(line);
1955
+ if (table) {
1956
+ skipping = isLegacyCodexConfigTable(table);
1957
+ if (skipping) {
1958
+ removedTables.push(table);
1959
+ continue;
1960
+ }
1961
+ }
1962
+ if (!skipping) {
1963
+ output.push(line);
1964
+ }
1965
+ }
1966
+ return { text: output.join("").replace(/\n{3,}/g, "\n\n"), removedTables };
1967
+ }
1968
+ function parseTomlTableHeader(line) {
1969
+ const match = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/.exec(line);
1970
+ return match?.[1]?.trim();
1971
+ }
1972
+ function isLegacyCodexConfigTable(table) {
1973
+ return /^plugins\."dbt-agent@[^"]+"$/.test(table)
1974
+ || table === "mcp_servers.dbt-agent"
1975
+ || table.startsWith("mcp_servers.dbt-agent.")
1976
+ || table === 'mcp_servers."dbt-agent"'
1977
+ || table.startsWith('mcp_servers."dbt-agent".');
1978
+ }
1979
+ function legacyCodexCleanupWarning(cleanup) {
1980
+ const parts = [];
1981
+ if (cleanup.legacy_removed_paths.length > 0) {
1982
+ parts.push(`removed ${cleanup.legacy_removed_paths.length} legacy Codex plugin path(s)`);
1983
+ }
1984
+ if (cleanup.legacy_removed_config_tables?.length) {
1985
+ parts.push(`removed ${cleanup.legacy_removed_config_tables.length} legacy Codex config table(s)`);
1986
+ }
1987
+ if (cleanup.legacy_removed_history_entries) {
1988
+ parts.push(`removed ${cleanup.legacy_removed_history_entries} legacy Codex history mention(s)`);
1989
+ }
1990
+ if (cleanup.warnings?.length) {
1991
+ parts.push(`cleanup warning(s): ${cleanup.warnings.join("; ")}`);
1992
+ }
1993
+ return parts.length > 0 ? `Legacy dbt-agent cleanup: ${parts.join(", ")}.` : undefined;
1994
+ }
1995
+ async function cleanupLegacyOpenCodePluginRemnants(targetRoot, globalInstall) {
1996
+ const removedPaths = [];
1997
+ const warnings = [];
1998
+ const legacyPaths = [
1999
+ join(targetRoot, "plugins", "development-board-toolchain.js"),
2000
+ join(targetRoot, "plugins", "dbt-agent.js"),
2001
+ join(targetRoot, "node_modules", "dbt-agent")
2002
+ ];
2003
+ if (globalInstall) {
2004
+ legacyPaths.push(join(targetRoot, "plugins", "embed-labs.js"));
2005
+ }
2006
+ for (const candidate of legacyPaths) {
2007
+ try {
2008
+ if (await pathExists(candidate)) {
2009
+ await rm(candidate, { recursive: true, force: true });
2010
+ removedPaths.push(candidate);
2011
+ }
2012
+ }
2013
+ catch (error) {
2014
+ warnings.push(`Could not remove ${candidate}: ${error instanceof Error ? error.message : String(error)}`);
2015
+ }
2016
+ }
2017
+ return {
2018
+ legacy_removed_paths: removedPaths,
2019
+ legacy_removed_config_entries: [],
2020
+ ...(warnings.length > 0 ? { warnings } : {})
2021
+ };
2022
+ }
2023
+ function legacyOpenCodeCleanupWarning(cleanup) {
2024
+ const parts = [];
2025
+ if (cleanup.legacy_removed_paths.length > 0) {
2026
+ parts.push(`removed ${cleanup.legacy_removed_paths.length} legacy OpenCode plugin path(s)`);
2027
+ }
2028
+ if (cleanup.legacy_removed_config_entries?.length) {
2029
+ parts.push(`removed ${cleanup.legacy_removed_config_entries.length} legacy OpenCode config entry(s)`);
2030
+ }
2031
+ if (cleanup.warnings?.length) {
2032
+ parts.push(`cleanup warning(s): ${cleanup.warnings.join("; ")}`);
2033
+ }
2034
+ return parts.length > 0 ? `Legacy OpenCode cleanup: ${parts.join(", ")}.` : undefined;
2035
+ }
2036
+ function combineWarnings(...warnings) {
2037
+ const actual = warnings.filter((warning) => Boolean(warning));
2038
+ return actual.length > 0 ? actual.join(" ") : undefined;
1815
2039
  }
1816
2040
  async function maybeRegisterCodexMcp(parsed, targetRoot, targetPath) {
1817
2041
  const explicitTarget = Boolean(stringFlag(parsed, "target") || stringFlag(parsed, "codex-target"));
@@ -1984,10 +2208,54 @@ async function resolveExecutableOnPath(name) {
1984
2208
  return undefined;
1985
2209
  }
1986
2210
  function defaultOpenCodeRoot() {
1987
- return join(process.cwd(), ".opencode");
2211
+ return globalOpenCodeRoot();
2212
+ }
2213
+ function globalOpenCodeRoot() {
2214
+ return join(homedir(), ".config", "opencode");
2215
+ }
2216
+ function isGlobalOpenCodeRoot(targetRoot) {
2217
+ return resolve(targetRoot) === resolve(globalOpenCodeRoot());
2218
+ }
2219
+ async function ensureOpenCodeGlobalPluginConfig() {
2220
+ const configPath = join(globalOpenCodeRoot(), "opencode.json");
2221
+ let existing = {};
2222
+ try {
2223
+ const parsed = JSON.parse(await readFile(configPath, "utf8"));
2224
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2225
+ existing = parsed;
2226
+ }
2227
+ }
2228
+ catch {
2229
+ existing = {};
2230
+ }
2231
+ const configured = Array.isArray(existing.plugin)
2232
+ ? existing.plugin.filter((item) => typeof item === "string")
2233
+ : Array.isArray(existing.plugins)
2234
+ ? existing.plugins.filter((item) => typeof item === "string")
2235
+ : [];
2236
+ const removed = configured.filter(isLegacyOpenCodePluginConfigEntry);
2237
+ const cleaned = configured.filter((item) => !isLegacyOpenCodePluginConfigEntry(item));
2238
+ if (!cleaned.includes("embed-labs")) {
2239
+ cleaned.push("embed-labs");
2240
+ }
2241
+ await writeFile(configPath, `${JSON.stringify({
2242
+ ...existing,
2243
+ plugin: cleaned,
2244
+ plugins: undefined
2245
+ }, null, 2)}\n`, "utf8");
2246
+ return removed;
2247
+ }
2248
+ function isLegacyOpenCodePluginConfigEntry(item) {
2249
+ const normalized = item.replace(/\\/g, "/");
2250
+ return item === "dbt-agent"
2251
+ || item === "development-board-toolchain"
2252
+ || normalized === "./plugins/development-board-toolchain"
2253
+ || normalized === "./plugins/development-board-toolchain.js"
2254
+ || normalized.endsWith("/plugins/development-board-toolchain.js")
2255
+ || normalized.includes("development-board-toolchain");
1988
2256
  }
1989
2257
  async function openCodeDuplicatePluginWarning(targetRoot) {
1990
- const globalRoot = join(homedir(), ".config", "opencode");
2258
+ const globalRoot = globalOpenCodeRoot();
1991
2259
  if (resolve(targetRoot) === resolve(globalRoot))
1992
2260
  return undefined;
1993
2261
  const configPath = join(globalRoot, "opencode.json");
@@ -4164,7 +4432,7 @@ function localToolchainCurrentRequest(parsed) {
4164
4432
  return { installRoot: installRoot.value };
4165
4433
  }
4166
4434
  function localToolchainInstallRequest(parsed) {
4167
- const unknownFlag = firstUnknownFlag(parsed, ["json", "board", "board-id", "channel", "metadata-root", "source-url", "source-release-root", "install-root", "force"]);
4435
+ const unknownFlag = firstUnknownFlag(parsed, ["json", "board", "board-id", "channel", "metadata-root", "source-url", "source-release-root", "install-root", "mode", "force"]);
4168
4436
  if (unknownFlag) {
4169
4437
  return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_INSTALL_USAGE}`;
4170
4438
  }
@@ -4193,6 +4461,9 @@ function localToolchainInstallRequest(parsed) {
4193
4461
  const installRoot = optionalTrimmedStringFlag(parsed, "install-root");
4194
4462
  if (installRoot.error)
4195
4463
  return installRoot.error;
4464
+ const mode = optionalTrimmedStringFlag(parsed, "mode");
4465
+ if (mode.error)
4466
+ return mode.error;
4196
4467
  return {
4197
4468
  boardId: board.value,
4198
4469
  channel: channel.value,
@@ -4200,11 +4471,12 @@ function localToolchainInstallRequest(parsed) {
4200
4471
  sourceUrl: sourceUrl.value,
4201
4472
  sourceReleaseRoot: sourceReleaseRoot.value,
4202
4473
  installRoot: installRoot.value,
4474
+ mode: mode.value,
4203
4475
  force: booleanFlag(parsed, "force")
4204
4476
  };
4205
4477
  }
4206
4478
  function localToolchainValidateRequest(parsed) {
4207
- const unknownFlag = firstUnknownFlag(parsed, ["json", "release-root"]);
4479
+ const unknownFlag = firstUnknownFlag(parsed, ["json", "release-root", "mode"]);
4208
4480
  if (unknownFlag) {
4209
4481
  return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_VALIDATE_USAGE}`;
4210
4482
  }
@@ -4216,7 +4488,11 @@ function localToolchainValidateRequest(parsed) {
4216
4488
  if (releaseRoot.error) {
4217
4489
  return releaseRoot.error;
4218
4490
  }
4219
- return { releaseRoot: releaseRoot.value };
4491
+ const mode = optionalTrimmedStringFlag(parsed, "mode");
4492
+ if (mode.error) {
4493
+ return mode.error;
4494
+ }
4495
+ return { releaseRoot: releaseRoot.value, mode: mode.value };
4220
4496
  }
4221
4497
  function localCompileTaishanPiRequest(parsed, auth) {
4222
4498
  const unknownFlag = firstUnknownFlag(parsed, ["json", "source", "output", "release-root", "account", "account-id"]);
@@ -5359,11 +5635,19 @@ function renderLocalToolchainLatest(result) {
5359
5635
  `version=${result.version}`,
5360
5636
  `host=${result.host}`,
5361
5637
  result.metadata_root ? `metadata_root=${result.metadata_root}` : "metadata=built-in",
5362
- result.download ? `download=${result.download.mirror_kind}:${result.download.source_url}` : "",
5363
- result.download ? `archive_sha256=${result.download.archive.sha256}` : "",
5364
- result.download ? `archive_size_bytes=${result.download.archive.size_bytes}` : "",
5638
+ result.download?.source_url ? `download=${result.download.mirror_kind}:${result.download.source_url}` : "",
5639
+ result.download?.archive ? `archive_sha256=${result.download.archive.sha256}` : "",
5640
+ result.download?.archive ? `archive_size_bytes=${result.download.archive.size_bytes}` : "",
5641
+ result.download?.components?.length ? `components=${result.download.components.length}` : "",
5642
+ result.download?.default_mode ? `default_mode=${result.download.default_mode}` : "",
5365
5643
  result.download_error ? `download_error=${result.download_error}` : ""
5366
5644
  ].filter(Boolean);
5645
+ if (result.download?.components?.length) {
5646
+ lines.push("download_components:");
5647
+ for (const component of result.download.components) {
5648
+ lines.push(` ${component.id}@${component.version} modes=${component.install_modes?.join(",") || "all"} bytes=${component.archive.size_bytes}`);
5649
+ }
5650
+ }
5367
5651
  if (result.packages.length > 0) {
5368
5652
  lines.push("packages:");
5369
5653
  for (const pkg of result.packages) {
@@ -5386,6 +5670,7 @@ function renderLocalToolchainCurrent(result) {
5386
5670
  `board=${result.board_id}`,
5387
5671
  result.version ? `version=${result.version}` : "",
5388
5672
  result.channel ? `channel=${result.channel}` : "",
5673
+ result.mode ? `mode=${result.mode}` : "",
5389
5674
  result.release_root ? `release_root=${result.release_root}` : "",
5390
5675
  `install_root=${result.install_root}`,
5391
5676
  `registry=${result.registry_path}`
@@ -5398,13 +5683,21 @@ function renderLocalToolchainInstall(result) {
5398
5683
  `version=${result.version}`,
5399
5684
  `channel=${result.channel}`,
5400
5685
  `host=${result.host}`,
5686
+ `mode=${result.mode}`,
5401
5687
  `install_root=${result.install_root}`,
5402
5688
  `release_root=${result.release_root}`,
5403
5689
  `registry=${result.registry_path}`,
5404
5690
  `source=${result.source.kind}:${result.source.value}`,
5405
5691
  result.source.downloaded_path ? `downloaded=${result.source.downloaded_path}` : "",
5692
+ result.source.components?.length ? `components=${result.source.components.length}` : "",
5406
5693
  `validation=${result.validation.ok ? "ok" : "failed"}`
5407
5694
  ].filter(Boolean);
5695
+ if (result.source.components?.length) {
5696
+ lines.push("installed_components:");
5697
+ for (const component of result.source.components) {
5698
+ lines.push(` ${component.id}@${component.version} ${component.mirror_kind || ""} bytes=${component.size_bytes}`);
5699
+ }
5700
+ }
5408
5701
  if (result.installed_paths.length > 0) {
5409
5702
  lines.push("installed_paths:");
5410
5703
  for (const installedPath of result.installed_paths) {
@@ -5423,6 +5716,7 @@ function renderLocalToolchainValidation(result) {
5423
5716
  const lines = [
5424
5717
  result.ok ? "Local toolchain ready." : "Local toolchain not ready.",
5425
5718
  `board=${result.board_id}`,
5719
+ `mode=${result.mode}`,
5426
5720
  `host=${result.host.platform}/${result.host.arch}`,
5427
5721
  `release_root=${result.release_root}`
5428
5722
  ];
@@ -6450,9 +6744,9 @@ Usage:
6450
6744
  embed image boot-logo compose --package <boot-logo-package.json> --base-image <boot.img|image.img> --output <image> [--manifest <manifest.json>] [--force] [--json]
6451
6745
  embed local toolchain latest [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--json]
6452
6746
  embed local toolchain current [--install-root <path>] [--json]
6453
- embed local toolchain install [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--source-url <tar.gz-url>|--source-release-root <path>] [--install-root <path>] [--force] [--json]
6747
+ embed local toolchain install [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--source-url <tar.gz-url>|--source-release-root <path>] [--install-root <path>] [--mode minimal|compile|qt|full|images] [--force] [--json]
6454
6748
  Defaults to the production download channel at download.embedboard.com.
6455
- embed local toolchain validate [--release-root <path>] [--json]
6749
+ embed local toolchain validate [--release-root <path>] [--mode minimal|compile|qt|full|images] [--json]
6456
6750
  embed local compile taishanpi --source <main.c|main.cpp> --output <artifact> [--release-root <path>] [--account <account_id>] [--json]
6457
6751
  embed local build qt-smoke --build-dir <dir> [--source <qt-smoke-dir>] [--release-root <path>] [--account <account_id>] [--json]
6458
6752
  embed debug tools [--json]