@kvell007/embed-labs-cli 0.1.0-alpha.1 → 0.1.0-alpha.11
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/README.md +35 -13
- package/dist/image-compose.d.ts +38 -0
- package/dist/image-compose.js +334 -0
- package/dist/image-compose.js.map +1 -0
- package/dist/index.js +1182 -38
- package/dist/index.js.map +1 -1
- package/dist/local-toolchain.d.ts +173 -0
- package/dist/local-toolchain.js +834 -0
- package/dist/local-toolchain.js.map +1 -0
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
import { constants } from "node:fs";
|
|
3
4
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { cp, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { access, cp, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import { homedir, tmpdir } from "node:os";
|
|
7
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
8
|
+
import { basename, delimiter, dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
8
10
|
import { startServer } from "@embed-labs/local-bridge";
|
|
11
|
+
import { composeBootLogoPackage } from "./image-compose.js";
|
|
12
|
+
import { buildTaishanPiQtSmoke, compileTaishanPiSingleFile, currentLocalToolchain, installLocalToolchain, latestLocalToolchain, validateLocalToolchain } from "./local-toolchain.js";
|
|
9
13
|
import { fail, ok } from "@embed-labs/protocol";
|
|
10
14
|
const require = createRequire(import.meta.url);
|
|
15
|
+
const CLI_MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const SOURCE_CHECKOUT_ROOT = resolve(CLI_MODULE_DIR, "..", "..", "..");
|
|
11
17
|
const qrcodeTerminal = require("qrcode-terminal");
|
|
12
18
|
const DEFAULT_BRIDGE_URL = process.env.EMBED_BRIDGE_URL ?? "http://127.0.0.1:18083";
|
|
13
19
|
const DEFAULT_CLOUD_API_URL = process.env.EMBED_CLOUD_API_URL ?? "http://127.0.0.1:18100";
|
|
@@ -71,6 +77,16 @@ const BUILD_APPLICATION_STUB_USAGE = "Usage: embed build application stub --work
|
|
|
71
77
|
const BUILD_APPLICATION_GENERATE_USAGE = "Usage: embed build application generate --workspace <workspace_id> --prompt <request> [--account <account_id>] [--target <source_path>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--json]";
|
|
72
78
|
const BUILD_APPLICATION_COMPILE_USAGE = "Usage: embed build application compile --workspace <workspace_id> [--account <account_id>] [--source <source_path>] [--execution-mode docker_worker|server_direct|dry_run] [--compiler auto|cc|gcc|clang|aarch64-linux-gnu-gcc] [--json]";
|
|
73
79
|
const BUILD_IMAGE_GENERATE_USAGE = "Usage: embed build image generate --workspace <workspace_id> --prompt <request> [--account <account_id>] [--image-profile <profile_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--execution-mode cloud_worker|dry_run] [--worker-pool <pool>] [--json]";
|
|
80
|
+
const BUILD_IMAGE_BOOT_LOGO_USAGE = "Usage: embed build image boot-logo --logo <local_image> [--account <account_id>] [--project <project_id>] [--board taishanpi] [--variant 1M-RK3566] [--kernel-logo <local_image>] [--rotate -90] [--scale 100] [--output <package.json>] [--json]";
|
|
81
|
+
const IMAGE_BOOT_LOGO_COMPOSE_USAGE = "Usage: embed image boot-logo compose --package <boot-logo-package.json> --base-image <boot.img|image.img> --output <image> [--manifest <manifest.json>] [--force] [--json]";
|
|
82
|
+
const BUILD_IMAGE_DTB_USAGE = "Usage: embed build image dtb --dtb <local.dtb|local.dts> [--input-format auto|dtb|dts] [--account <account_id>] [--project <project_id>] [--board taishanpi] [--variant 1M-RK3566] [--output <package.json>] [--json]";
|
|
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
|
+
const LOCAL_TOOLCHAIN_LATEST_USAGE = "Usage: embed local toolchain latest [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--json]";
|
|
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]";
|
|
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
|
+
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]";
|
|
74
90
|
const BOARD_REGISTRY_LIST_USAGE = "Usage: embed board registry list [--json]";
|
|
75
91
|
const BOARD_REGISTRY_SHOW_USAGE = "Usage: embed board registry show <template_id> [--json]";
|
|
76
92
|
const BOARD_METHODS_USAGE = "Usage: embed board methods <template_id> [--json]";
|
|
@@ -79,9 +95,10 @@ const BOARD_KNOWLEDGE_FILE_USAGE = "Usage: embed board knowledge file <template_
|
|
|
79
95
|
const MODEL_LIST_USAGE = "Usage: embed model list [--json]";
|
|
80
96
|
const MODEL_DEFAULT_USAGE = "Usage: embed model default [--json]";
|
|
81
97
|
const SERVICE_MODES_USAGE = "Usage: embed service modes [--json]";
|
|
82
|
-
const AGENT_RUN_USAGE = "Usage: embed agent run --prompt <request> [--account <account_id>] [--workspace <workspace_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--max-tool-calls
|
|
98
|
+
const AGENT_RUN_USAGE = "Usage: embed agent run --prompt <request> [--account <account_id>] [--workspace <workspace_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--max-tool-calls 6] [--host <ip>] [--ports 22,15301] [--artifact <local_file>|--artifact-id <artifact_id>|--artifact-task <task_id>] [--artifact-output <path>] [--remote-path <path>] [--run] [--approve] [--json]";
|
|
83
99
|
const TOOL_LIST_USAGE = "Usage: embed tool list [--json]";
|
|
84
100
|
const TOOL_CALL_USAGE = "Usage: embed tool call <capability_id> [--input-json '<json>'] [--approve] [--json]";
|
|
101
|
+
const MCP_TOOL_EVENT_USAGE = "Usage: embed mcp log --tool <tool_name> [--client codex|opencode] [--mode local_ai|server_ai] [--server-model-used true|false] [--success true|false] [--request-id <id>] [--duration-ms <ms>] [--input-summary <text>] [--output-summary <text>] [--json]";
|
|
85
102
|
const BOARD_DEPLOY_TAISHANPI_USAGE = "Usage: embed deploy taishanpi --host <ip> --artifact <local_file> --approve [--user root] [--remote-path /userdata/embed-labs/apps/app] [--run] [--timeout 30] [--json]";
|
|
86
103
|
const CLOUD_TASK_EVENT_APPEND_USAGE = "Usage: embed cloud task event append <task_id> [--state <state>] [--progress-stage <stage>|--stage <stage>] [--progress-text <text>|--message <text>] [--progress-percent 0-100] [--severity info|warning|error] [--type <event_type>] [--artifact-json '<json>'] [--evidence-json '<json>'] [--json]";
|
|
87
104
|
const TASK_STATES = new Set([
|
|
@@ -351,6 +368,16 @@ async function main(argv) {
|
|
|
351
368
|
USAGE_EVENTS_USAGE
|
|
352
369
|
].join("\n")), undefined, 2);
|
|
353
370
|
}
|
|
371
|
+
if (area === "mcp") {
|
|
372
|
+
if (action === "log" || action === "tool-event") {
|
|
373
|
+
const body = mcpToolEventBody(parsed);
|
|
374
|
+
if (typeof body === "string") {
|
|
375
|
+
return output(parsed, fail("invalid_args", body), undefined, 2);
|
|
376
|
+
}
|
|
377
|
+
return output(parsed, await cloudPost("/v1/mcp/tool-events", body), renderMcpToolEvent);
|
|
378
|
+
}
|
|
379
|
+
return output(parsed, fail("invalid_args", MCP_TOOL_EVENT_USAGE), undefined, 2);
|
|
380
|
+
}
|
|
354
381
|
if (area === "billing") {
|
|
355
382
|
if (action === "statement") {
|
|
356
383
|
const request = billingStatementRequest(parsed);
|
|
@@ -472,6 +499,75 @@ async function main(argv) {
|
|
|
472
499
|
BILLING_SNAPSHOT_SHOW_USAGE
|
|
473
500
|
].join("\n")), undefined, 2);
|
|
474
501
|
}
|
|
502
|
+
if (area === "image") {
|
|
503
|
+
if ((action === "boot-logo" && parsed.command[2] === "compose") || (action === "compose" && parsed.command[2] === "boot-logo")) {
|
|
504
|
+
const request = imageBootLogoComposeRequest(parsed, IMAGE_BOOT_LOGO_COMPOSE_USAGE);
|
|
505
|
+
if (typeof request === "string") {
|
|
506
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
507
|
+
}
|
|
508
|
+
return output(parsed, ok(await composeBootLogoPackage(request)), renderBootLogoComposeResult);
|
|
509
|
+
}
|
|
510
|
+
if ((action === "dtb" && parsed.command[2] === "compose") || (action === "compose" && parsed.command[2] === "dtb")) {
|
|
511
|
+
const request = imageBootLogoComposeRequest(parsed, IMAGE_DTB_COMPOSE_USAGE);
|
|
512
|
+
if (typeof request === "string") {
|
|
513
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
514
|
+
}
|
|
515
|
+
return output(parsed, ok(await composeBootLogoPackage(request)), renderBootLogoComposeResult);
|
|
516
|
+
}
|
|
517
|
+
return output(parsed, fail("invalid_args", [IMAGE_BOOT_LOGO_COMPOSE_USAGE, IMAGE_DTB_COMPOSE_USAGE].join("\n")), undefined, 2);
|
|
518
|
+
}
|
|
519
|
+
if (area === "local") {
|
|
520
|
+
if (action === "toolchain" && parsed.command[2] === "latest") {
|
|
521
|
+
const request = localToolchainLatestRequest(parsed);
|
|
522
|
+
if (typeof request === "string") {
|
|
523
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
524
|
+
}
|
|
525
|
+
return output(parsed, ok(await latestLocalToolchain(request)), renderLocalToolchainLatest);
|
|
526
|
+
}
|
|
527
|
+
if (action === "toolchain" && parsed.command[2] === "current") {
|
|
528
|
+
const request = localToolchainCurrentRequest(parsed);
|
|
529
|
+
if (typeof request === "string") {
|
|
530
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
531
|
+
}
|
|
532
|
+
return output(parsed, ok(await currentLocalToolchain(request.installRoot)), renderLocalToolchainCurrent);
|
|
533
|
+
}
|
|
534
|
+
if (action === "toolchain" && parsed.command[2] === "install") {
|
|
535
|
+
const request = localToolchainInstallRequest(parsed);
|
|
536
|
+
if (typeof request === "string") {
|
|
537
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
538
|
+
}
|
|
539
|
+
return output(parsed, ok(await installLocalToolchain(request)), renderLocalToolchainInstall);
|
|
540
|
+
}
|
|
541
|
+
if (action === "toolchain" && parsed.command[2] === "validate") {
|
|
542
|
+
const request = localToolchainValidateRequest(parsed);
|
|
543
|
+
if (typeof request === "string") {
|
|
544
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
545
|
+
}
|
|
546
|
+
return output(parsed, ok(await validateLocalToolchain(request.releaseRoot)), renderLocalToolchainValidation);
|
|
547
|
+
}
|
|
548
|
+
if (action === "compile" && parsed.command[2] === "taishanpi") {
|
|
549
|
+
const request = localCompileTaishanPiRequest(parsed, await authStatus());
|
|
550
|
+
if (typeof request === "string") {
|
|
551
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
552
|
+
}
|
|
553
|
+
return output(parsed, ok(await compileTaishanPiSingleFile(request)), renderLocalCompileResult);
|
|
554
|
+
}
|
|
555
|
+
if (action === "build" && parsed.command[2] === "qt-smoke") {
|
|
556
|
+
const request = localBuildQtSmokeRequest(parsed, await authStatus());
|
|
557
|
+
if (typeof request === "string") {
|
|
558
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
559
|
+
}
|
|
560
|
+
return output(parsed, ok(await buildTaishanPiQtSmoke(request)), renderLocalCompileResult);
|
|
561
|
+
}
|
|
562
|
+
return output(parsed, fail("invalid_args", [
|
|
563
|
+
LOCAL_TOOLCHAIN_LATEST_USAGE,
|
|
564
|
+
LOCAL_TOOLCHAIN_CURRENT_USAGE,
|
|
565
|
+
LOCAL_TOOLCHAIN_INSTALL_USAGE,
|
|
566
|
+
LOCAL_TOOLCHAIN_VALIDATE_USAGE,
|
|
567
|
+
LOCAL_COMPILE_TAISHANPI_USAGE,
|
|
568
|
+
LOCAL_BUILD_QT_SMOKE_USAGE
|
|
569
|
+
].join("\n")), undefined, 2);
|
|
570
|
+
}
|
|
475
571
|
if (area === "build") {
|
|
476
572
|
if (action === "template") {
|
|
477
573
|
const templateAction = parsed.command[2];
|
|
@@ -617,6 +713,44 @@ async function main(argv) {
|
|
|
617
713
|
}
|
|
618
714
|
return output(parsed, await cloudPost("/v1/build/image-generate", body), renderJob);
|
|
619
715
|
}
|
|
716
|
+
if (action === "image" && (parsed.command[2] === "boot-logo" || parsed.command[2] === "boot-logo-package")) {
|
|
717
|
+
const request = await buildImageBootLogoPackageRequest(parsed);
|
|
718
|
+
if (typeof request === "string") {
|
|
719
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
720
|
+
}
|
|
721
|
+
const created = await cloudPost("/v1/build/image/boot-logo-package", request.body);
|
|
722
|
+
if (!created.ok || !request.output_path) {
|
|
723
|
+
return output(parsed, created, renderJob, created.ok ? 0 : 2);
|
|
724
|
+
}
|
|
725
|
+
const packageArtifact = created.data.artifacts?.find((artifact) => artifact.kind === "patch" && artifact.name === "boot-logo-package.json");
|
|
726
|
+
if (!packageArtifact) {
|
|
727
|
+
return output(parsed, fail("artifact_not_found", "Boot-logo package task did not include boot-logo-package.json."), undefined, 2);
|
|
728
|
+
}
|
|
729
|
+
const downloaded = await cloudDownloadArtifact(packageArtifact.artifact_id, request.output_path);
|
|
730
|
+
if (!downloaded.ok) {
|
|
731
|
+
return output(parsed, downloaded, renderArtifactDownload, 2);
|
|
732
|
+
}
|
|
733
|
+
return output(parsed, ok({ task: created.data, downloaded_artifact: downloaded.data }), renderBootLogoPackageResult);
|
|
734
|
+
}
|
|
735
|
+
if (action === "image" && (parsed.command[2] === "dtb" || parsed.command[2] === "dtb-package")) {
|
|
736
|
+
const request = await buildImageDtbPackageRequest(parsed);
|
|
737
|
+
if (typeof request === "string") {
|
|
738
|
+
return output(parsed, fail("invalid_args", request), undefined, 2);
|
|
739
|
+
}
|
|
740
|
+
const created = await cloudPost("/v1/build/image/dtb-package", request.body);
|
|
741
|
+
if (!created.ok || !request.output_path) {
|
|
742
|
+
return output(parsed, created, renderJob, created.ok ? 0 : 2);
|
|
743
|
+
}
|
|
744
|
+
const packageArtifact = created.data.artifacts?.find((artifact) => artifact.kind === "patch" && artifact.name === "dtb-package.json");
|
|
745
|
+
if (!packageArtifact) {
|
|
746
|
+
return output(parsed, fail("artifact_not_found", "DTB package task did not include dtb-package.json."), undefined, 2);
|
|
747
|
+
}
|
|
748
|
+
const downloaded = await cloudDownloadArtifact(packageArtifact.artifact_id, request.output_path);
|
|
749
|
+
if (!downloaded.ok) {
|
|
750
|
+
return output(parsed, downloaded, renderArtifactDownload, 2);
|
|
751
|
+
}
|
|
752
|
+
return output(parsed, ok({ task: created.data, downloaded_artifact: downloaded.data }), renderDtbPackageResult);
|
|
753
|
+
}
|
|
620
754
|
return output(parsed, fail("invalid_args", [
|
|
621
755
|
BUILD_TEMPLATE_LIST_USAGE,
|
|
622
756
|
BUILD_TEMPLATE_SHOW_USAGE,
|
|
@@ -633,7 +767,9 @@ async function main(argv) {
|
|
|
633
767
|
BUILD_APPLICATION_STUB_USAGE,
|
|
634
768
|
BUILD_APPLICATION_GENERATE_USAGE,
|
|
635
769
|
BUILD_APPLICATION_COMPILE_USAGE,
|
|
636
|
-
BUILD_IMAGE_GENERATE_USAGE
|
|
770
|
+
BUILD_IMAGE_GENERATE_USAGE,
|
|
771
|
+
BUILD_IMAGE_BOOT_LOGO_USAGE,
|
|
772
|
+
BUILD_IMAGE_DTB_USAGE
|
|
637
773
|
].join("\n")), undefined, 2);
|
|
638
774
|
}
|
|
639
775
|
if (area === "project" && action === "create") {
|
|
@@ -1304,16 +1440,16 @@ async function pluginList(parsed) {
|
|
|
1304
1440
|
return remoteManifest;
|
|
1305
1441
|
}
|
|
1306
1442
|
const effectiveManifest = manifest?.data ?? remoteManifest?.data;
|
|
1307
|
-
const codexPackage = manifest?.data.packages?.find((item) => item.id === "codex-
|
|
1308
|
-
const opencodePackage = manifest?.data.packages?.find((item) => item.id === "opencode-
|
|
1309
|
-
const effectiveCodexPackage = effectiveManifest?.packages?.find((item) => item.id === "codex-
|
|
1310
|
-
const effectiveOpenCodePackage = effectiveManifest?.packages?.find((item) => item.id === "opencode-
|
|
1443
|
+
const codexPackage = manifest?.data.packages?.find((item) => item.id === "codex-embed-labs");
|
|
1444
|
+
const opencodePackage = manifest?.data.packages?.find((item) => item.id === "opencode-embed-labs");
|
|
1445
|
+
const effectiveCodexPackage = effectiveManifest?.packages?.find((item) => item.id === "codex-embed-labs") ?? codexPackage;
|
|
1446
|
+
const effectiveOpenCodePackage = effectiveManifest?.packages?.find((item) => item.id === "opencode-embed-labs") ?? opencodePackage;
|
|
1311
1447
|
const source = releaseDir || remoteManifest ? "release_dir" : "source_checkout";
|
|
1312
1448
|
return ok({
|
|
1313
1449
|
plugins: [
|
|
1314
1450
|
{
|
|
1315
1451
|
id: "codex",
|
|
1316
|
-
display_name: "Embed Labs
|
|
1452
|
+
display_name: "Embed Labs Codex plugin",
|
|
1317
1453
|
source,
|
|
1318
1454
|
version: effectiveCodexPackage?.version ?? effectiveManifest?.version ?? await localPluginVersion("codex"),
|
|
1319
1455
|
release_file: effectiveCodexPackage?.file,
|
|
@@ -1322,7 +1458,7 @@ async function pluginList(parsed) {
|
|
|
1322
1458
|
},
|
|
1323
1459
|
{
|
|
1324
1460
|
id: "opencode",
|
|
1325
|
-
display_name: "Embed Labs
|
|
1461
|
+
display_name: "Embed Labs OpenCode plugin",
|
|
1326
1462
|
source,
|
|
1327
1463
|
version: effectiveOpenCodePackage?.version ?? effectiveManifest?.version ?? await localPluginVersion("opencode"),
|
|
1328
1464
|
release_file: effectiveOpenCodePackage?.file,
|
|
@@ -1398,7 +1534,7 @@ async function installCodexPlugin(parsed, context) {
|
|
|
1398
1534
|
return source;
|
|
1399
1535
|
}
|
|
1400
1536
|
const targetRoot = codexPluginTargetRoot(parsed, context.installingAll);
|
|
1401
|
-
const targetPath = join(targetRoot, "
|
|
1537
|
+
const targetPath = join(targetRoot, "embed-labs");
|
|
1402
1538
|
if (await pathExists(targetPath) && !booleanFlag(parsed, "force")) {
|
|
1403
1539
|
return fail("plugin_already_installed", `Codex plugin already exists at ${targetPath}.`, {
|
|
1404
1540
|
remediation: "Pass --force to replace it, or pass --codex-target/--target to install into a different directory."
|
|
@@ -1407,12 +1543,17 @@ async function installCodexPlugin(parsed, context) {
|
|
|
1407
1543
|
await rm(targetPath, { recursive: true, force: true });
|
|
1408
1544
|
await mkdir(targetRoot, { recursive: true });
|
|
1409
1545
|
await cp(source.data.sourcePath, targetPath, { recursive: true });
|
|
1546
|
+
const mcpRegistration = await maybeRegisterCodexMcp(parsed, targetRoot, targetPath);
|
|
1410
1547
|
return ok({
|
|
1411
1548
|
id: "codex",
|
|
1412
1549
|
target_path: targetPath,
|
|
1413
1550
|
source: source.data.sourceLabel,
|
|
1414
1551
|
version: source.data.version,
|
|
1415
|
-
command_hint:
|
|
1552
|
+
command_hint: mcpRegistration.registered
|
|
1553
|
+
? "Codex MCP was registered. Start a new Codex session to reload tools."
|
|
1554
|
+
: mcpRegistration.hint,
|
|
1555
|
+
mcp_registered: mcpRegistration.registered,
|
|
1556
|
+
mcp_warning: mcpRegistration.warning
|
|
1416
1557
|
});
|
|
1417
1558
|
}
|
|
1418
1559
|
async function installOpenCodePlugin(parsed, context) {
|
|
@@ -1448,20 +1589,22 @@ async function installOpenCodePlugin(parsed, context) {
|
|
|
1448
1589
|
});
|
|
1449
1590
|
}
|
|
1450
1591
|
await ensureOpenCodeInstallPackageJson(targetRoot);
|
|
1451
|
-
await writeFile(wrapperPath, `export { default, DevelopmentBoardToolchainPlugin } from "
|
|
1592
|
+
await writeFile(wrapperPath, `export { default, DevelopmentBoardToolchainPlugin } from "embed-labs";\n`, "utf8");
|
|
1593
|
+
const duplicateWarning = await openCodeDuplicatePluginWarning(targetRoot);
|
|
1452
1594
|
return ok({
|
|
1453
1595
|
id: "opencode",
|
|
1454
1596
|
target_path: targetRoot,
|
|
1455
1597
|
source: source.data.sourceLabel,
|
|
1456
1598
|
version: source.data.version,
|
|
1457
|
-
command_hint: "Start OpenCode from the project containing this .opencode directory."
|
|
1599
|
+
command_hint: "Start OpenCode from the project containing this .opencode directory.",
|
|
1600
|
+
warning: duplicateWarning
|
|
1458
1601
|
});
|
|
1459
1602
|
}
|
|
1460
1603
|
async function resolveCodexPluginSource(context) {
|
|
1461
1604
|
if (context.releaseDir) {
|
|
1462
|
-
const item = context.manifest?.packages?.find((entry) => entry.id === "codex-
|
|
1605
|
+
const item = context.manifest?.packages?.find((entry) => entry.id === "codex-embed-labs");
|
|
1463
1606
|
if (!item?.file) {
|
|
1464
|
-
return fail("plugin_release_not_found", `Release manifest in ${context.releaseDir} does not include codex-
|
|
1607
|
+
return fail("plugin_release_not_found", `Release manifest in ${context.releaseDir} does not include codex-embed-labs.`);
|
|
1465
1608
|
}
|
|
1466
1609
|
const tarball = resolve(context.releaseDir, item.file);
|
|
1467
1610
|
const verified = await verifyReleasePackage(tarball, item);
|
|
@@ -1476,13 +1619,13 @@ async function resolveCodexPluginSource(context) {
|
|
|
1476
1619
|
details: { exit_code: tarResult.code, stderr_tail: tarResult.stderr.split("\n").slice(-20) }
|
|
1477
1620
|
});
|
|
1478
1621
|
}
|
|
1479
|
-
const sourcePath = join(extractDir, "codex_plugin", "plugins", "
|
|
1622
|
+
const sourcePath = join(extractDir, "codex_plugin", "plugins", "embed-labs");
|
|
1480
1623
|
if (!await pathExists(join(sourcePath, ".codex-plugin", "plugin.json"))) {
|
|
1481
|
-
return fail("plugin_release_invalid", `Codex plugin release ${tarball} did not contain plugins/
|
|
1624
|
+
return fail("plugin_release_invalid", `Codex plugin release ${tarball} did not contain plugins/embed-labs/.codex-plugin/plugin.json.`);
|
|
1482
1625
|
}
|
|
1483
1626
|
return ok({ sourcePath, sourceLabel: tarball, version: item.version ?? context.manifest?.version });
|
|
1484
1627
|
}
|
|
1485
|
-
const sourcePath =
|
|
1628
|
+
const sourcePath = sourceCheckoutPath("platform_plugins", "codex_plugin", "plugins", "embed-labs");
|
|
1486
1629
|
if (!await pathExists(join(sourcePath, ".codex-plugin", "plugin.json"))) {
|
|
1487
1630
|
return fail("plugin_source_not_found", "Could not find Codex plugin source in this checkout.", {
|
|
1488
1631
|
remediation: "Run from the Embed-Labs-Cloud repo root or pass --release-dir pointing to a plugin release directory."
|
|
@@ -1492,9 +1635,9 @@ async function resolveCodexPluginSource(context) {
|
|
|
1492
1635
|
}
|
|
1493
1636
|
async function resolveOpenCodePluginSource(context) {
|
|
1494
1637
|
if (context.releaseDir) {
|
|
1495
|
-
const item = context.manifest?.packages?.find((entry) => entry.id === "opencode-
|
|
1638
|
+
const item = context.manifest?.packages?.find((entry) => entry.id === "opencode-embed-labs");
|
|
1496
1639
|
if (!item?.file) {
|
|
1497
|
-
return fail("plugin_release_not_found", `Release manifest in ${context.releaseDir} does not include opencode-
|
|
1640
|
+
return fail("plugin_release_not_found", `Release manifest in ${context.releaseDir} does not include opencode-embed-labs.`);
|
|
1498
1641
|
}
|
|
1499
1642
|
const tarball = resolve(context.releaseDir, item.file);
|
|
1500
1643
|
const verified = await verifyReleasePackage(tarball, item);
|
|
@@ -1503,13 +1646,38 @@ async function resolveOpenCodePluginSource(context) {
|
|
|
1503
1646
|
}
|
|
1504
1647
|
return ok({ packagePath: tarball, sourceLabel: tarball, version: item.version ?? context.manifest?.version });
|
|
1505
1648
|
}
|
|
1506
|
-
const packagePath =
|
|
1649
|
+
const packagePath = sourceCheckoutPath("platform_plugins", "opencode_plugin");
|
|
1507
1650
|
if (!await pathExists(join(packagePath, "package.json"))) {
|
|
1508
1651
|
return fail("plugin_source_not_found", "Could not find OpenCode plugin source in this checkout.", {
|
|
1509
1652
|
remediation: "Run from the Embed-Labs-Cloud repo root or pass --release-dir pointing to a plugin release directory."
|
|
1510
1653
|
});
|
|
1511
1654
|
}
|
|
1512
|
-
|
|
1655
|
+
const packed = await runLocalProcess("npm", ["pack", packagePath, "--pack-destination", context.tempDir, "--json"]);
|
|
1656
|
+
if (packed.code !== 0) {
|
|
1657
|
+
return fail("opencode_plugin_pack_failed", "npm pack failed while preparing the OpenCode plugin source package.", {
|
|
1658
|
+
details: {
|
|
1659
|
+
exit_code: packed.code,
|
|
1660
|
+
stdout_tail: packed.stdout.split("\n").slice(-20),
|
|
1661
|
+
stderr_tail: packed.stderr.split("\n").slice(-20)
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
let tarballName = "";
|
|
1666
|
+
try {
|
|
1667
|
+
const parsed = JSON.parse(packed.stdout);
|
|
1668
|
+
tarballName = basename(parsed[0]?.filename || "");
|
|
1669
|
+
}
|
|
1670
|
+
catch {
|
|
1671
|
+
tarballName = "";
|
|
1672
|
+
}
|
|
1673
|
+
if (!tarballName) {
|
|
1674
|
+
return fail("opencode_plugin_pack_failed", "npm pack did not report an OpenCode plugin tarball filename.");
|
|
1675
|
+
}
|
|
1676
|
+
const tarballPath = join(context.tempDir, tarballName);
|
|
1677
|
+
if (!await pathExists(tarballPath)) {
|
|
1678
|
+
return fail("opencode_plugin_pack_failed", `npm pack reported ${tarballName}, but the tarball was not found.`);
|
|
1679
|
+
}
|
|
1680
|
+
return ok({ packagePath: tarballPath, sourceLabel: `${packagePath} -> ${tarballPath}`, version: await localPluginVersion("opencode") });
|
|
1513
1681
|
}
|
|
1514
1682
|
async function fetchRemotePluginManifest(parsed) {
|
|
1515
1683
|
const manifestUrl = `${pluginReleaseBaseUrl(parsed)}/manifest.json`;
|
|
@@ -1645,13 +1813,204 @@ function openCodePluginTargetRoot(parsed, installingAll) {
|
|
|
1645
1813
|
function defaultCodexPluginRoot() {
|
|
1646
1814
|
return join(process.env.CODEX_HOME?.trim() || join(homedir(), ".codex"), "plugins");
|
|
1647
1815
|
}
|
|
1816
|
+
async function maybeRegisterCodexMcp(parsed, targetRoot, targetPath) {
|
|
1817
|
+
const explicitTarget = Boolean(stringFlag(parsed, "target") || stringFlag(parsed, "codex-target"));
|
|
1818
|
+
if (explicitTarget && process.env.EMBED_CODEX_MCP_REGISTER !== "1") {
|
|
1819
|
+
return {
|
|
1820
|
+
registered: false,
|
|
1821
|
+
hint: `Installed into a custom target. Register manually with: codex mcp add embed-labs -- node ${join(targetPath, "scripts", "embed-labs-mcp-bridge.mjs")}`
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
const bridgePath = join(targetPath, "scripts", "embed-labs-mcp-bridge.mjs");
|
|
1825
|
+
if (!await pathExists(bridgePath)) {
|
|
1826
|
+
return {
|
|
1827
|
+
registered: false,
|
|
1828
|
+
hint: "Restart Codex or reload plugins after installing.",
|
|
1829
|
+
warning: `Codex MCP bridge was not found at ${bridgePath}.`
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
const codexBin = await resolveExecutableOnPath("codex");
|
|
1833
|
+
if (!codexBin) {
|
|
1834
|
+
return {
|
|
1835
|
+
registered: false,
|
|
1836
|
+
hint: `Codex CLI was not found on PATH. Register manually with: codex mcp add embed-labs -- node ${bridgePath}`
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
const embedCliBin = process.env.EMBED_CLI_BIN?.trim() || await resolveExecutableOnPath("embedlabs") || await resolveExecutableOnPath("embed") || "";
|
|
1840
|
+
const authFile = resolve(process.env.EMBED_AUTH_FILE?.trim() || DEFAULT_AUTH_FILE);
|
|
1841
|
+
const cloudUrl = pluginMcpCloudApiUrl(parsed);
|
|
1842
|
+
const existing = await runLocalProcess(codexBin, ["mcp", "get", "embed-labs", "--json"]);
|
|
1843
|
+
if (existing.code === 0 && codexMcpAlreadyRegistered(existing.stdout, bridgePath, cloudUrl, authFile, embedCliBin)) {
|
|
1844
|
+
const warning = await upsertCodexMcpRuntimeConfig(bridgePath);
|
|
1845
|
+
if (warning) {
|
|
1846
|
+
return { registered: true, warning };
|
|
1847
|
+
}
|
|
1848
|
+
return { registered: true };
|
|
1849
|
+
}
|
|
1850
|
+
await runLocalProcess(codexBin, ["mcp", "remove", "embed-labs"]);
|
|
1851
|
+
const args = [
|
|
1852
|
+
"mcp",
|
|
1853
|
+
"add",
|
|
1854
|
+
"embed-labs",
|
|
1855
|
+
"--env",
|
|
1856
|
+
`EMBED_CLOUD_API_URL=${cloudUrl}`,
|
|
1857
|
+
"--env",
|
|
1858
|
+
`EMBED_AUTH_FILE=${authFile}`
|
|
1859
|
+
];
|
|
1860
|
+
if (embedCliBin) {
|
|
1861
|
+
args.push("--env", `EMBED_CLI_BIN=${embedCliBin}`);
|
|
1862
|
+
}
|
|
1863
|
+
args.push("--", process.execPath, bridgePath);
|
|
1864
|
+
const addResult = await runLocalProcess(codexBin, args);
|
|
1865
|
+
if (addResult.code !== 0) {
|
|
1866
|
+
return {
|
|
1867
|
+
registered: false,
|
|
1868
|
+
hint: `Codex plugin installed. Register manually with: codex mcp add embed-labs -- ${process.execPath} ${bridgePath}`,
|
|
1869
|
+
warning: `codex mcp add failed: ${addResult.stderr.trim() || addResult.stdout.trim() || `exit ${addResult.code}`}`
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
const warning = await upsertCodexMcpRuntimeConfig(bridgePath);
|
|
1873
|
+
return warning ? { registered: true, warning } : { registered: true };
|
|
1874
|
+
}
|
|
1875
|
+
function codexMcpAlreadyRegistered(stdout, bridgePath, cloudUrl, authFile, embedCliBin) {
|
|
1876
|
+
try {
|
|
1877
|
+
const parsed = JSON.parse(stdout);
|
|
1878
|
+
const transport = parsed.transport;
|
|
1879
|
+
if (transport?.type !== "stdio" || transport.command !== process.execPath)
|
|
1880
|
+
return false;
|
|
1881
|
+
if (!transport.args?.includes(bridgePath))
|
|
1882
|
+
return false;
|
|
1883
|
+
const env = transport.env || {};
|
|
1884
|
+
if (env.EMBED_CLOUD_API_URL !== cloudUrl)
|
|
1885
|
+
return false;
|
|
1886
|
+
if (env.EMBED_AUTH_FILE !== authFile)
|
|
1887
|
+
return false;
|
|
1888
|
+
if (embedCliBin && env.EMBED_CLI_BIN !== embedCliBin)
|
|
1889
|
+
return false;
|
|
1890
|
+
return true;
|
|
1891
|
+
}
|
|
1892
|
+
catch {
|
|
1893
|
+
return false;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
async function upsertCodexMcpRuntimeConfig(bridgePath) {
|
|
1897
|
+
const configPath = join(process.env.CODEX_HOME?.trim() || join(homedir(), ".codex"), "config.toml");
|
|
1898
|
+
try {
|
|
1899
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
1900
|
+
let text = "";
|
|
1901
|
+
try {
|
|
1902
|
+
text = await readFile(configPath, "utf8");
|
|
1903
|
+
}
|
|
1904
|
+
catch {
|
|
1905
|
+
text = "";
|
|
1906
|
+
}
|
|
1907
|
+
const updated = upsertTomlTableKeys(text, "mcp_servers.embed-labs", {
|
|
1908
|
+
command: tomlString(process.execPath),
|
|
1909
|
+
args: `[${tomlString(bridgePath)}]`,
|
|
1910
|
+
startup_timeout_sec: "120"
|
|
1911
|
+
});
|
|
1912
|
+
if (updated !== text) {
|
|
1913
|
+
await writeFile(configPath, updated, "utf8");
|
|
1914
|
+
}
|
|
1915
|
+
return undefined;
|
|
1916
|
+
}
|
|
1917
|
+
catch (error) {
|
|
1918
|
+
return `Codex MCP was registered, but ${configPath} could not be updated with startup_timeout_sec: ${error instanceof Error ? error.message : String(error)}`;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
function upsertTomlTableKeys(text, tableName, values) {
|
|
1922
|
+
const normalized = text.endsWith("\n") || text.length === 0 ? text : `${text}\n`;
|
|
1923
|
+
const header = `[${tableName}]`;
|
|
1924
|
+
const tablePattern = new RegExp(`(^|\\n)(\\[${escapeRegExp(tableName)}\\]\\n)([\\s\\S]*?)(?=\\n\\[|$)`);
|
|
1925
|
+
const match = tablePattern.exec(normalized);
|
|
1926
|
+
const bodyWithValues = (body) => {
|
|
1927
|
+
let updated = body;
|
|
1928
|
+
for (const [key, value] of Object.entries(values)) {
|
|
1929
|
+
const keyPattern = new RegExp(`^${escapeRegExp(key)}\\s*=.*$`, "m");
|
|
1930
|
+
if (keyPattern.test(updated)) {
|
|
1931
|
+
updated = updated.replace(keyPattern, `${key} = ${value}`);
|
|
1932
|
+
}
|
|
1933
|
+
else {
|
|
1934
|
+
updated = `${updated.replace(/\s*$/, "\n")}${key} = ${value}\n`;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return updated;
|
|
1938
|
+
};
|
|
1939
|
+
if (!match) {
|
|
1940
|
+
const prefix = normalized.length > 0 && !normalized.endsWith("\n\n") ? `${normalized}\n` : normalized;
|
|
1941
|
+
return `${prefix}${header}\n${bodyWithValues("")}`;
|
|
1942
|
+
}
|
|
1943
|
+
return `${normalized.slice(0, match.index)}${match[1]}${match[2]}${bodyWithValues(match[3])}${normalized.slice(match.index + match[0].length)}`;
|
|
1944
|
+
}
|
|
1945
|
+
function tomlString(value) {
|
|
1946
|
+
return JSON.stringify(value);
|
|
1947
|
+
}
|
|
1948
|
+
function escapeRegExp(value) {
|
|
1949
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1950
|
+
}
|
|
1951
|
+
function pluginMcpCloudApiUrl(parsed) {
|
|
1952
|
+
const explicit = process.env.EMBED_CLOUD_API_URL?.trim();
|
|
1953
|
+
if (explicit)
|
|
1954
|
+
return explicit.replace(/\/+$/, "");
|
|
1955
|
+
const releaseUrl = stringFlag(parsed, "release-url") || DEFAULT_PLUGIN_RELEASE_URL;
|
|
1956
|
+
try {
|
|
1957
|
+
return new URL(releaseUrl).origin;
|
|
1958
|
+
}
|
|
1959
|
+
catch {
|
|
1960
|
+
return DEFAULT_CLOUD_API_URL.replace(/\/+$/, "");
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
async function resolveExecutableOnPath(name) {
|
|
1964
|
+
if (name.includes("/")) {
|
|
1965
|
+
try {
|
|
1966
|
+
await access(name, constants.X_OK);
|
|
1967
|
+
return name;
|
|
1968
|
+
}
|
|
1969
|
+
catch {
|
|
1970
|
+
return undefined;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
const pathEntries = (process.env.PATH || "").split(delimiter).filter(Boolean);
|
|
1974
|
+
for (const entry of pathEntries) {
|
|
1975
|
+
const candidate = join(entry, name);
|
|
1976
|
+
try {
|
|
1977
|
+
await access(candidate, constants.X_OK);
|
|
1978
|
+
return candidate;
|
|
1979
|
+
}
|
|
1980
|
+
catch {
|
|
1981
|
+
// Keep searching PATH.
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
return undefined;
|
|
1985
|
+
}
|
|
1648
1986
|
function defaultOpenCodeRoot() {
|
|
1649
1987
|
return join(process.cwd(), ".opencode");
|
|
1650
1988
|
}
|
|
1989
|
+
async function openCodeDuplicatePluginWarning(targetRoot) {
|
|
1990
|
+
const globalRoot = join(homedir(), ".config", "opencode");
|
|
1991
|
+
if (resolve(targetRoot) === resolve(globalRoot))
|
|
1992
|
+
return undefined;
|
|
1993
|
+
const configPath = join(globalRoot, "opencode.json");
|
|
1994
|
+
try {
|
|
1995
|
+
const parsed = JSON.parse(await readFile(configPath, "utf8"));
|
|
1996
|
+
const configuredPlugins = Array.isArray(parsed.plugin)
|
|
1997
|
+
? parsed.plugin
|
|
1998
|
+
: Array.isArray(parsed.plugins)
|
|
1999
|
+
? parsed.plugins
|
|
2000
|
+
: [];
|
|
2001
|
+
if (configuredPlugins.some((item) => item === "embed-labs")) {
|
|
2002
|
+
return "Global OpenCode config already enables embed-labs; OpenCode may load both the global plugin and this project .opencode plugin. Disable one entry if duplicate DBT tools appear.";
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
catch {
|
|
2006
|
+
return undefined;
|
|
2007
|
+
}
|
|
2008
|
+
return undefined;
|
|
2009
|
+
}
|
|
1651
2010
|
async function localPluginVersion(kind) {
|
|
1652
2011
|
const filePath = kind === "codex"
|
|
1653
|
-
?
|
|
1654
|
-
:
|
|
2012
|
+
? sourceCheckoutPath("platform_plugins", "codex_plugin", "plugins", "embed-labs", ".codex-plugin", "plugin.json")
|
|
2013
|
+
: sourceCheckoutPath("platform_plugins", "opencode_plugin", "package.json");
|
|
1655
2014
|
try {
|
|
1656
2015
|
const parsed = JSON.parse(await readFile(filePath, "utf8"));
|
|
1657
2016
|
return parsed.version;
|
|
@@ -1661,8 +2020,11 @@ async function localPluginVersion(kind) {
|
|
|
1661
2020
|
}
|
|
1662
2021
|
}
|
|
1663
2022
|
async function localPluginSourcesAvailable() {
|
|
1664
|
-
return await pathExists(
|
|
1665
|
-
&& await pathExists(
|
|
2023
|
+
return await pathExists(sourceCheckoutPath("platform_plugins", "codex_plugin", "plugins", "embed-labs", ".codex-plugin", "plugin.json"))
|
|
2024
|
+
&& await pathExists(sourceCheckoutPath("platform_plugins", "opencode_plugin", "package.json"));
|
|
2025
|
+
}
|
|
2026
|
+
function sourceCheckoutPath(...segments) {
|
|
2027
|
+
return resolve(SOURCE_CHECKOUT_ROOT, ...segments);
|
|
1666
2028
|
}
|
|
1667
2029
|
async function pathExists(pathValue) {
|
|
1668
2030
|
try {
|
|
@@ -2098,6 +2460,36 @@ function agentRunToolInputs(parsed) {
|
|
|
2098
2460
|
ports: ports.values.length > 0 ? ports.values : [22, 15301],
|
|
2099
2461
|
timeout_ms: timeout.value !== undefined ? timeout.value * 1000 : undefined
|
|
2100
2462
|
});
|
|
2463
|
+
inputs["wifi.scan"] = compactBody({
|
|
2464
|
+
host: host.value,
|
|
2465
|
+
user: user.value,
|
|
2466
|
+
timeout_seconds: timeout.value
|
|
2467
|
+
});
|
|
2468
|
+
inputs["bluetooth.scan"] = compactBody({
|
|
2469
|
+
host: host.value,
|
|
2470
|
+
user: user.value,
|
|
2471
|
+
timeout_seconds: timeout.value
|
|
2472
|
+
});
|
|
2473
|
+
inputs["chip.cpu.frequency"] = compactBody({
|
|
2474
|
+
host: host.value,
|
|
2475
|
+
user: user.value,
|
|
2476
|
+
timeout_seconds: timeout.value
|
|
2477
|
+
});
|
|
2478
|
+
inputs["chip.temperature"] = compactBody({
|
|
2479
|
+
host: host.value,
|
|
2480
|
+
user: user.value,
|
|
2481
|
+
timeout_seconds: timeout.value
|
|
2482
|
+
});
|
|
2483
|
+
inputs["qml.runtime.status"] = compactBody({
|
|
2484
|
+
host: host.value,
|
|
2485
|
+
user: user.value,
|
|
2486
|
+
timeout_seconds: timeout.value
|
|
2487
|
+
});
|
|
2488
|
+
inputs["qml.runtime.start"] = compactBody({
|
|
2489
|
+
host: host.value,
|
|
2490
|
+
user: user.value,
|
|
2491
|
+
timeout_seconds: timeout.value
|
|
2492
|
+
});
|
|
2101
2493
|
}
|
|
2102
2494
|
if (artifact.value || remotePath.value || user.value || booleanFlag(parsed, "run")) {
|
|
2103
2495
|
inputs["taishanpi.deploy"] = compactBody({
|
|
@@ -2778,6 +3170,231 @@ function buildImageGenerateBody(parsed) {
|
|
|
2778
3170
|
worker_pool: workerPool.value
|
|
2779
3171
|
});
|
|
2780
3172
|
}
|
|
3173
|
+
async function buildImageBootLogoPackageRequest(parsed) {
|
|
3174
|
+
const unknownFlag = firstUnknownFlag(parsed, [
|
|
3175
|
+
"json",
|
|
3176
|
+
"account",
|
|
3177
|
+
"project",
|
|
3178
|
+
"project-id",
|
|
3179
|
+
"board",
|
|
3180
|
+
"board-id",
|
|
3181
|
+
"variant",
|
|
3182
|
+
"variant-id",
|
|
3183
|
+
"logo",
|
|
3184
|
+
"logo-path",
|
|
3185
|
+
"kernel-logo",
|
|
3186
|
+
"kernel-logo-path",
|
|
3187
|
+
"rotate",
|
|
3188
|
+
"scale",
|
|
3189
|
+
"output"
|
|
3190
|
+
]);
|
|
3191
|
+
if (unknownFlag) {
|
|
3192
|
+
return `Unknown flag --${unknownFlag}. ${BUILD_IMAGE_BOOT_LOGO_USAGE}`;
|
|
3193
|
+
}
|
|
3194
|
+
const extra = parsed.command.slice(3);
|
|
3195
|
+
if (extra.length > 0) {
|
|
3196
|
+
return `Unexpected argument: ${extra[0]}. ${BUILD_IMAGE_BOOT_LOGO_USAGE}`;
|
|
3197
|
+
}
|
|
3198
|
+
const logo = optionalTrimmedStringAliasFlag(parsed, ["logo", "logo-path"], "logo or logo-path");
|
|
3199
|
+
if (logo.error) {
|
|
3200
|
+
return logo.error;
|
|
3201
|
+
}
|
|
3202
|
+
if (!logo.value) {
|
|
3203
|
+
return BUILD_IMAGE_BOOT_LOGO_USAGE;
|
|
3204
|
+
}
|
|
3205
|
+
const logoPath = resolve(logo.value);
|
|
3206
|
+
const logoRead = await readLocalFileForUpload(logoPath, "logo");
|
|
3207
|
+
if (typeof logoRead === "string") {
|
|
3208
|
+
return logoRead;
|
|
3209
|
+
}
|
|
3210
|
+
const kernelLogo = optionalTrimmedStringAliasFlag(parsed, ["kernel-logo", "kernel-logo-path"], "kernel-logo or kernel-logo-path");
|
|
3211
|
+
if (kernelLogo.error) {
|
|
3212
|
+
return kernelLogo.error;
|
|
3213
|
+
}
|
|
3214
|
+
let kernelLogoRead;
|
|
3215
|
+
if (kernelLogo.value) {
|
|
3216
|
+
const kernelLogoPath = resolve(kernelLogo.value);
|
|
3217
|
+
const read = await readLocalFileForUpload(kernelLogoPath, "kernel-logo");
|
|
3218
|
+
if (typeof read === "string") {
|
|
3219
|
+
return read;
|
|
3220
|
+
}
|
|
3221
|
+
kernelLogoRead = read;
|
|
3222
|
+
}
|
|
3223
|
+
const account = optionalTrimmedStringFlag(parsed, "account");
|
|
3224
|
+
if (account.error) {
|
|
3225
|
+
return account.error;
|
|
3226
|
+
}
|
|
3227
|
+
const project = optionalTrimmedStringAliasFlag(parsed, ["project", "project-id"], "project or project-id");
|
|
3228
|
+
if (project.error) {
|
|
3229
|
+
return project.error;
|
|
3230
|
+
}
|
|
3231
|
+
const board = optionalTrimmedStringAliasFlag(parsed, ["board", "board-id"], "board or board-id");
|
|
3232
|
+
if (board.error) {
|
|
3233
|
+
return board.error;
|
|
3234
|
+
}
|
|
3235
|
+
const variant = optionalTrimmedStringAliasFlag(parsed, ["variant", "variant-id"], "variant or variant-id");
|
|
3236
|
+
if (variant.error) {
|
|
3237
|
+
return variant.error;
|
|
3238
|
+
}
|
|
3239
|
+
const rotate = optionalTrimmedStringFlag(parsed, "rotate");
|
|
3240
|
+
if (rotate.error) {
|
|
3241
|
+
return rotate.error;
|
|
3242
|
+
}
|
|
3243
|
+
const scale = optionalTrimmedStringFlag(parsed, "scale");
|
|
3244
|
+
if (scale.error) {
|
|
3245
|
+
return scale.error;
|
|
3246
|
+
}
|
|
3247
|
+
const outputPath = optionalTrimmedStringFlag(parsed, "output");
|
|
3248
|
+
if (outputPath.error) {
|
|
3249
|
+
return outputPath.error;
|
|
3250
|
+
}
|
|
3251
|
+
const body = {
|
|
3252
|
+
account_id: account.value,
|
|
3253
|
+
project_id: project.value,
|
|
3254
|
+
board_id: board.value ?? "taishanpi",
|
|
3255
|
+
variant_id: variant.value,
|
|
3256
|
+
logo_name: basename(logoRead.path),
|
|
3257
|
+
logo_content_base64: logoRead.bytes.toString("base64"),
|
|
3258
|
+
logo_sha256: logoRead.sha256,
|
|
3259
|
+
kernel_logo_name: kernelLogoRead ? basename(kernelLogoRead.path) : undefined,
|
|
3260
|
+
kernel_logo_content_base64: kernelLogoRead?.bytes.toString("base64"),
|
|
3261
|
+
kernel_logo_sha256: kernelLogoRead?.sha256,
|
|
3262
|
+
rotate: rotate.value,
|
|
3263
|
+
scale: scale.value
|
|
3264
|
+
};
|
|
3265
|
+
return {
|
|
3266
|
+
body,
|
|
3267
|
+
output_path: outputPath.value ? resolve(outputPath.value) : undefined
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
async function buildImageDtbPackageRequest(parsed) {
|
|
3271
|
+
const unknownFlag = firstUnknownFlag(parsed, [
|
|
3272
|
+
"json",
|
|
3273
|
+
"account",
|
|
3274
|
+
"project",
|
|
3275
|
+
"project-id",
|
|
3276
|
+
"board",
|
|
3277
|
+
"board-id",
|
|
3278
|
+
"variant",
|
|
3279
|
+
"variant-id",
|
|
3280
|
+
"dtb",
|
|
3281
|
+
"dtb-path",
|
|
3282
|
+
"dts",
|
|
3283
|
+
"dts-path",
|
|
3284
|
+
"input-format",
|
|
3285
|
+
"output"
|
|
3286
|
+
]);
|
|
3287
|
+
if (unknownFlag) {
|
|
3288
|
+
return `Unknown flag --${unknownFlag}. ${BUILD_IMAGE_DTB_USAGE}`;
|
|
3289
|
+
}
|
|
3290
|
+
const extra = parsed.command.slice(3);
|
|
3291
|
+
if (extra.length > 0) {
|
|
3292
|
+
return `Unexpected argument: ${extra[0]}. ${BUILD_IMAGE_DTB_USAGE}`;
|
|
3293
|
+
}
|
|
3294
|
+
const dtb = optionalTrimmedStringAliasFlag(parsed, ["dtb", "dtb-path", "dts", "dts-path"], "dtb or dts path");
|
|
3295
|
+
if (dtb.error) {
|
|
3296
|
+
return dtb.error;
|
|
3297
|
+
}
|
|
3298
|
+
if (!dtb.value) {
|
|
3299
|
+
return BUILD_IMAGE_DTB_USAGE;
|
|
3300
|
+
}
|
|
3301
|
+
const dtbPath = resolve(dtb.value);
|
|
3302
|
+
const dtbRead = await readLocalFileForUpload(dtbPath, "dtb");
|
|
3303
|
+
if (typeof dtbRead === "string") {
|
|
3304
|
+
return dtbRead;
|
|
3305
|
+
}
|
|
3306
|
+
const account = optionalTrimmedStringFlag(parsed, "account");
|
|
3307
|
+
if (account.error) {
|
|
3308
|
+
return account.error;
|
|
3309
|
+
}
|
|
3310
|
+
const project = optionalTrimmedStringAliasFlag(parsed, ["project", "project-id"], "project or project-id");
|
|
3311
|
+
if (project.error) {
|
|
3312
|
+
return project.error;
|
|
3313
|
+
}
|
|
3314
|
+
const board = optionalTrimmedStringAliasFlag(parsed, ["board", "board-id"], "board or board-id");
|
|
3315
|
+
if (board.error) {
|
|
3316
|
+
return board.error;
|
|
3317
|
+
}
|
|
3318
|
+
const variant = optionalTrimmedStringAliasFlag(parsed, ["variant", "variant-id"], "variant or variant-id");
|
|
3319
|
+
if (variant.error) {
|
|
3320
|
+
return variant.error;
|
|
3321
|
+
}
|
|
3322
|
+
const inputFormat = optionalTrimmedStringFlag(parsed, "input-format");
|
|
3323
|
+
if (inputFormat.error) {
|
|
3324
|
+
return inputFormat.error;
|
|
3325
|
+
}
|
|
3326
|
+
const outputPath = optionalTrimmedStringFlag(parsed, "output");
|
|
3327
|
+
if (outputPath.error) {
|
|
3328
|
+
return outputPath.error;
|
|
3329
|
+
}
|
|
3330
|
+
const body = {
|
|
3331
|
+
account_id: account.value,
|
|
3332
|
+
project_id: project.value,
|
|
3333
|
+
board_id: board.value ?? "taishanpi",
|
|
3334
|
+
variant_id: variant.value,
|
|
3335
|
+
dtb_name: basename(dtbRead.path),
|
|
3336
|
+
dtb_content_base64: dtbRead.bytes.toString("base64"),
|
|
3337
|
+
dtb_sha256: dtbRead.sha256,
|
|
3338
|
+
input_format: inputFormat.value
|
|
3339
|
+
};
|
|
3340
|
+
return {
|
|
3341
|
+
body,
|
|
3342
|
+
output_path: outputPath.value ? resolve(outputPath.value) : undefined
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
function imageBootLogoComposeRequest(parsed, usage) {
|
|
3346
|
+
const unknownFlag = firstUnknownFlag(parsed, [
|
|
3347
|
+
"json",
|
|
3348
|
+
"package",
|
|
3349
|
+
"package-path",
|
|
3350
|
+
"base-image",
|
|
3351
|
+
"base-image-path",
|
|
3352
|
+
"output",
|
|
3353
|
+
"output-image",
|
|
3354
|
+
"manifest",
|
|
3355
|
+
"manifest-path",
|
|
3356
|
+
"force"
|
|
3357
|
+
]);
|
|
3358
|
+
if (unknownFlag) {
|
|
3359
|
+
return `Unknown flag --${unknownFlag}. ${usage}`;
|
|
3360
|
+
}
|
|
3361
|
+
const extra = parsed.command.slice(3);
|
|
3362
|
+
if (extra.length > 0) {
|
|
3363
|
+
return `Unexpected argument: ${extra[0]}. ${usage}`;
|
|
3364
|
+
}
|
|
3365
|
+
const packagePath = optionalTrimmedStringAliasFlag(parsed, ["package", "package-path"], "package or package-path");
|
|
3366
|
+
if (packagePath.error) {
|
|
3367
|
+
return packagePath.error;
|
|
3368
|
+
}
|
|
3369
|
+
if (!packagePath.value) {
|
|
3370
|
+
return usage;
|
|
3371
|
+
}
|
|
3372
|
+
const baseImage = optionalTrimmedStringAliasFlag(parsed, ["base-image", "base-image-path"], "base-image or base-image-path");
|
|
3373
|
+
if (baseImage.error) {
|
|
3374
|
+
return baseImage.error;
|
|
3375
|
+
}
|
|
3376
|
+
if (!baseImage.value) {
|
|
3377
|
+
return usage;
|
|
3378
|
+
}
|
|
3379
|
+
const outputImage = optionalTrimmedStringAliasFlag(parsed, ["output", "output-image"], "output or output-image");
|
|
3380
|
+
if (outputImage.error) {
|
|
3381
|
+
return outputImage.error;
|
|
3382
|
+
}
|
|
3383
|
+
if (!outputImage.value) {
|
|
3384
|
+
return usage;
|
|
3385
|
+
}
|
|
3386
|
+
const manifest = optionalTrimmedStringAliasFlag(parsed, ["manifest", "manifest-path"], "manifest or manifest-path");
|
|
3387
|
+
if (manifest.error) {
|
|
3388
|
+
return manifest.error;
|
|
3389
|
+
}
|
|
3390
|
+
return {
|
|
3391
|
+
packagePath: resolve(packagePath.value),
|
|
3392
|
+
baseImagePath: resolve(baseImage.value),
|
|
3393
|
+
outputImagePath: resolve(outputImage.value),
|
|
3394
|
+
manifestPath: manifest.value ? resolve(manifest.value) : undefined,
|
|
3395
|
+
force: booleanFlag(parsed, "force")
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
2781
3398
|
function boardKnowledgeFileRequest(parsed) {
|
|
2782
3399
|
const unknownFlag = firstUnknownFlag(parsed, ["json", "source", "path", "output"]);
|
|
2783
3400
|
if (unknownFlag) {
|
|
@@ -3110,6 +3727,73 @@ function usageRecordBody(parsed) {
|
|
|
3110
3727
|
created_at: createdAtResult.value
|
|
3111
3728
|
});
|
|
3112
3729
|
}
|
|
3730
|
+
function mcpToolEventBody(parsed) {
|
|
3731
|
+
const unknownFlag = firstUnknownFlag(parsed, [
|
|
3732
|
+
"json",
|
|
3733
|
+
"account",
|
|
3734
|
+
"account-id",
|
|
3735
|
+
"tool",
|
|
3736
|
+
"client",
|
|
3737
|
+
"mode",
|
|
3738
|
+
"server-model-used",
|
|
3739
|
+
"success",
|
|
3740
|
+
"request-id",
|
|
3741
|
+
"duration-ms",
|
|
3742
|
+
"input-summary",
|
|
3743
|
+
"output-summary"
|
|
3744
|
+
]);
|
|
3745
|
+
if (unknownFlag) {
|
|
3746
|
+
return `Unknown flag --${unknownFlag}. ${MCP_TOOL_EVENT_USAGE}`;
|
|
3747
|
+
}
|
|
3748
|
+
const extra = parsed.command.slice(2);
|
|
3749
|
+
if (extra.length > 0) {
|
|
3750
|
+
return `Unexpected argument: ${extra[0]}. ${MCP_TOOL_EVENT_USAGE}`;
|
|
3751
|
+
}
|
|
3752
|
+
const toolResult = optionalTrimmedStringFlag(parsed, "tool");
|
|
3753
|
+
if (toolResult.error)
|
|
3754
|
+
return toolResult.error;
|
|
3755
|
+
if (!toolResult.value)
|
|
3756
|
+
return MCP_TOOL_EVENT_USAGE;
|
|
3757
|
+
const accountResult = optionalTrimmedStringAliasFlag(parsed, ["account", "account-id"], "account or account-id");
|
|
3758
|
+
if (accountResult.error)
|
|
3759
|
+
return accountResult.error;
|
|
3760
|
+
const clientResult = optionalTrimmedStringFlag(parsed, "client");
|
|
3761
|
+
if (clientResult.error)
|
|
3762
|
+
return clientResult.error;
|
|
3763
|
+
const modeResult = optionalTrimmedStringFlag(parsed, "mode");
|
|
3764
|
+
if (modeResult.error)
|
|
3765
|
+
return modeResult.error;
|
|
3766
|
+
const requestIdResult = optionalTrimmedStringFlag(parsed, "request-id");
|
|
3767
|
+
if (requestIdResult.error)
|
|
3768
|
+
return requestIdResult.error;
|
|
3769
|
+
const inputSummaryResult = optionalTrimmedStringFlag(parsed, "input-summary");
|
|
3770
|
+
if (inputSummaryResult.error)
|
|
3771
|
+
return inputSummaryResult.error;
|
|
3772
|
+
const outputSummaryResult = optionalTrimmedStringFlag(parsed, "output-summary");
|
|
3773
|
+
if (outputSummaryResult.error)
|
|
3774
|
+
return outputSummaryResult.error;
|
|
3775
|
+
const durationResult = optionalNonNegativeIntegerFlag(parsed, "duration-ms");
|
|
3776
|
+
if (durationResult.error)
|
|
3777
|
+
return durationResult.error;
|
|
3778
|
+
const serverModelUsed = optionalBooleanFlag(parsed, "server-model-used");
|
|
3779
|
+
if (typeof serverModelUsed === "string")
|
|
3780
|
+
return serverModelUsed;
|
|
3781
|
+
const success = optionalBooleanFlag(parsed, "success");
|
|
3782
|
+
if (typeof success === "string")
|
|
3783
|
+
return success;
|
|
3784
|
+
return compactBody({
|
|
3785
|
+
account_id: accountResult.value,
|
|
3786
|
+
tool_name: toolResult.value,
|
|
3787
|
+
client: clientResult.value,
|
|
3788
|
+
mode: modeResult.value,
|
|
3789
|
+
server_model_used: serverModelUsed,
|
|
3790
|
+
success,
|
|
3791
|
+
request_id: requestIdResult.value,
|
|
3792
|
+
duration_ms: durationResult.value,
|
|
3793
|
+
input_summary: inputSummaryResult.value,
|
|
3794
|
+
output_summary: outputSummaryResult.value
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3113
3797
|
function usageSummaryRequest(parsed) {
|
|
3114
3798
|
const unknownFlag = firstUnknownFlag(parsed, ["json", "account", "account-id", "api-key-id", "from", "to"]);
|
|
3115
3799
|
if (unknownFlag) {
|
|
@@ -3445,6 +4129,181 @@ function billingSnapshotListRequest(parsed) {
|
|
|
3445
4129
|
}
|
|
3446
4130
|
return { path: `/v1/accounts/${encodeURIComponent(accountResult.value)}/billing/snapshots` };
|
|
3447
4131
|
}
|
|
4132
|
+
function localToolchainLatestRequest(parsed) {
|
|
4133
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "board", "board-id", "channel", "metadata-root"]);
|
|
4134
|
+
if (unknownFlag) {
|
|
4135
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_LATEST_USAGE}`;
|
|
4136
|
+
}
|
|
4137
|
+
const extra = parsed.command.slice(3);
|
|
4138
|
+
if (extra.length > 0) {
|
|
4139
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_TOOLCHAIN_LATEST_USAGE}`;
|
|
4140
|
+
}
|
|
4141
|
+
const board = optionalTrimmedStringAliasFlag(parsed, ["board", "board-id"], "board or board-id");
|
|
4142
|
+
if (board.error)
|
|
4143
|
+
return board.error;
|
|
4144
|
+
const channel = optionalTrimmedStringFlag(parsed, "channel");
|
|
4145
|
+
if (channel.error)
|
|
4146
|
+
return channel.error;
|
|
4147
|
+
const metadataRoot = optionalTrimmedStringFlag(parsed, "metadata-root");
|
|
4148
|
+
if (metadataRoot.error)
|
|
4149
|
+
return metadataRoot.error;
|
|
4150
|
+
return { boardId: board.value, channel: channel.value, metadataRoot: metadataRoot.value };
|
|
4151
|
+
}
|
|
4152
|
+
function localToolchainCurrentRequest(parsed) {
|
|
4153
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "install-root"]);
|
|
4154
|
+
if (unknownFlag) {
|
|
4155
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_CURRENT_USAGE}`;
|
|
4156
|
+
}
|
|
4157
|
+
const extra = parsed.command.slice(3);
|
|
4158
|
+
if (extra.length > 0) {
|
|
4159
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_TOOLCHAIN_CURRENT_USAGE}`;
|
|
4160
|
+
}
|
|
4161
|
+
const installRoot = optionalTrimmedStringFlag(parsed, "install-root");
|
|
4162
|
+
if (installRoot.error)
|
|
4163
|
+
return installRoot.error;
|
|
4164
|
+
return { installRoot: installRoot.value };
|
|
4165
|
+
}
|
|
4166
|
+
function localToolchainInstallRequest(parsed) {
|
|
4167
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "board", "board-id", "channel", "metadata-root", "source-url", "source-release-root", "install-root", "force"]);
|
|
4168
|
+
if (unknownFlag) {
|
|
4169
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_INSTALL_USAGE}`;
|
|
4170
|
+
}
|
|
4171
|
+
const extra = parsed.command.slice(3);
|
|
4172
|
+
if (extra.length > 0) {
|
|
4173
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_TOOLCHAIN_INSTALL_USAGE}`;
|
|
4174
|
+
}
|
|
4175
|
+
const board = optionalTrimmedStringAliasFlag(parsed, ["board", "board-id"], "board or board-id");
|
|
4176
|
+
if (board.error)
|
|
4177
|
+
return board.error;
|
|
4178
|
+
const channel = optionalTrimmedStringFlag(parsed, "channel");
|
|
4179
|
+
if (channel.error)
|
|
4180
|
+
return channel.error;
|
|
4181
|
+
const metadataRoot = optionalTrimmedStringFlag(parsed, "metadata-root");
|
|
4182
|
+
if (metadataRoot.error)
|
|
4183
|
+
return metadataRoot.error;
|
|
4184
|
+
const sourceUrl = optionalTrimmedStringFlag(parsed, "source-url");
|
|
4185
|
+
if (sourceUrl.error)
|
|
4186
|
+
return sourceUrl.error;
|
|
4187
|
+
const sourceReleaseRoot = optionalTrimmedStringFlag(parsed, "source-release-root");
|
|
4188
|
+
if (sourceReleaseRoot.error)
|
|
4189
|
+
return sourceReleaseRoot.error;
|
|
4190
|
+
if (sourceUrl.value && sourceReleaseRoot.value) {
|
|
4191
|
+
return "Use only one of --source-url or --source-release-root.";
|
|
4192
|
+
}
|
|
4193
|
+
const installRoot = optionalTrimmedStringFlag(parsed, "install-root");
|
|
4194
|
+
if (installRoot.error)
|
|
4195
|
+
return installRoot.error;
|
|
4196
|
+
return {
|
|
4197
|
+
boardId: board.value,
|
|
4198
|
+
channel: channel.value,
|
|
4199
|
+
metadataRoot: metadataRoot.value,
|
|
4200
|
+
sourceUrl: sourceUrl.value,
|
|
4201
|
+
sourceReleaseRoot: sourceReleaseRoot.value,
|
|
4202
|
+
installRoot: installRoot.value,
|
|
4203
|
+
force: booleanFlag(parsed, "force")
|
|
4204
|
+
};
|
|
4205
|
+
}
|
|
4206
|
+
function localToolchainValidateRequest(parsed) {
|
|
4207
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "release-root"]);
|
|
4208
|
+
if (unknownFlag) {
|
|
4209
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_TOOLCHAIN_VALIDATE_USAGE}`;
|
|
4210
|
+
}
|
|
4211
|
+
const extra = parsed.command.slice(3);
|
|
4212
|
+
if (extra.length > 0) {
|
|
4213
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_TOOLCHAIN_VALIDATE_USAGE}`;
|
|
4214
|
+
}
|
|
4215
|
+
const releaseRoot = optionalTrimmedStringFlag(parsed, "release-root");
|
|
4216
|
+
if (releaseRoot.error) {
|
|
4217
|
+
return releaseRoot.error;
|
|
4218
|
+
}
|
|
4219
|
+
return { releaseRoot: releaseRoot.value };
|
|
4220
|
+
}
|
|
4221
|
+
function localCompileTaishanPiRequest(parsed, auth) {
|
|
4222
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "source", "output", "release-root", "account", "account-id"]);
|
|
4223
|
+
if (unknownFlag) {
|
|
4224
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_COMPILE_TAISHANPI_USAGE}`;
|
|
4225
|
+
}
|
|
4226
|
+
const extra = parsed.command.slice(3);
|
|
4227
|
+
if (extra.length > 0) {
|
|
4228
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_COMPILE_TAISHANPI_USAGE}`;
|
|
4229
|
+
}
|
|
4230
|
+
const source = optionalTrimmedStringFlag(parsed, "source");
|
|
4231
|
+
if (source.error) {
|
|
4232
|
+
return source.error;
|
|
4233
|
+
}
|
|
4234
|
+
const outputPath = optionalTrimmedStringFlag(parsed, "output");
|
|
4235
|
+
if (outputPath.error) {
|
|
4236
|
+
return outputPath.error;
|
|
4237
|
+
}
|
|
4238
|
+
if (!source.value || !outputPath.value) {
|
|
4239
|
+
return LOCAL_COMPILE_TAISHANPI_USAGE;
|
|
4240
|
+
}
|
|
4241
|
+
const releaseRoot = optionalTrimmedStringFlag(parsed, "release-root");
|
|
4242
|
+
if (releaseRoot.error) {
|
|
4243
|
+
return releaseRoot.error;
|
|
4244
|
+
}
|
|
4245
|
+
const account = optionalTrimmedStringAliasFlag(parsed, ["account", "account-id"], "account or account-id");
|
|
4246
|
+
if (account.error) {
|
|
4247
|
+
return account.error;
|
|
4248
|
+
}
|
|
4249
|
+
return {
|
|
4250
|
+
boardId: "taishanpi-1m-rk3566",
|
|
4251
|
+
sourcePath: source.value,
|
|
4252
|
+
outputPath: outputPath.value,
|
|
4253
|
+
releaseRoot: releaseRoot.value,
|
|
4254
|
+
accountId: account.value,
|
|
4255
|
+
auth: localToolchainAuthContext(auth, account.value)
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
function localBuildQtSmokeRequest(parsed, auth) {
|
|
4259
|
+
const unknownFlag = firstUnknownFlag(parsed, ["json", "source", "build-dir", "release-root", "target-name", "account", "account-id"]);
|
|
4260
|
+
if (unknownFlag) {
|
|
4261
|
+
return `Unknown flag --${unknownFlag}. ${LOCAL_BUILD_QT_SMOKE_USAGE}`;
|
|
4262
|
+
}
|
|
4263
|
+
const extra = parsed.command.slice(3);
|
|
4264
|
+
if (extra.length > 0) {
|
|
4265
|
+
return `Unexpected argument: ${extra[0]}. ${LOCAL_BUILD_QT_SMOKE_USAGE}`;
|
|
4266
|
+
}
|
|
4267
|
+
const buildDir = optionalTrimmedStringFlag(parsed, "build-dir");
|
|
4268
|
+
if (buildDir.error) {
|
|
4269
|
+
return buildDir.error;
|
|
4270
|
+
}
|
|
4271
|
+
if (!buildDir.value) {
|
|
4272
|
+
return LOCAL_BUILD_QT_SMOKE_USAGE;
|
|
4273
|
+
}
|
|
4274
|
+
const source = optionalTrimmedStringFlag(parsed, "source");
|
|
4275
|
+
if (source.error) {
|
|
4276
|
+
return source.error;
|
|
4277
|
+
}
|
|
4278
|
+
const releaseRoot = optionalTrimmedStringFlag(parsed, "release-root");
|
|
4279
|
+
if (releaseRoot.error) {
|
|
4280
|
+
return releaseRoot.error;
|
|
4281
|
+
}
|
|
4282
|
+
const targetName = optionalTrimmedStringFlag(parsed, "target-name");
|
|
4283
|
+
if (targetName.error) {
|
|
4284
|
+
return targetName.error;
|
|
4285
|
+
}
|
|
4286
|
+
const account = optionalTrimmedStringAliasFlag(parsed, ["account", "account-id"], "account or account-id");
|
|
4287
|
+
if (account.error) {
|
|
4288
|
+
return account.error;
|
|
4289
|
+
}
|
|
4290
|
+
return {
|
|
4291
|
+
sourceDir: source.value,
|
|
4292
|
+
buildDir: buildDir.value,
|
|
4293
|
+
releaseRoot: releaseRoot.value,
|
|
4294
|
+
targetName: targetName.value,
|
|
4295
|
+
accountId: account.value,
|
|
4296
|
+
auth: localToolchainAuthContext(auth, account.value)
|
|
4297
|
+
};
|
|
4298
|
+
}
|
|
4299
|
+
function localToolchainAuthContext(auth, accountId) {
|
|
4300
|
+
return {
|
|
4301
|
+
authenticated: auth.authenticated,
|
|
4302
|
+
profile: auth.profile,
|
|
4303
|
+
source: auth.source,
|
|
4304
|
+
account_id: accountId
|
|
4305
|
+
};
|
|
4306
|
+
}
|
|
3448
4307
|
function usageEventsRequest(parsed) {
|
|
3449
4308
|
const unknownFlag = firstUnknownFlag(parsed, ["json", "account", "account-id", "api-key-id", "from", "to", "limit"]);
|
|
3450
4309
|
if (unknownFlag) {
|
|
@@ -3502,6 +4361,26 @@ function compactBody(input) {
|
|
|
3502
4361
|
}
|
|
3503
4362
|
return body;
|
|
3504
4363
|
}
|
|
4364
|
+
async function readLocalFileForUpload(filePath, label) {
|
|
4365
|
+
try {
|
|
4366
|
+
const info = await stat(filePath);
|
|
4367
|
+
if (!info.isFile()) {
|
|
4368
|
+
return `${label} must point to a file: ${filePath}`;
|
|
4369
|
+
}
|
|
4370
|
+
const bytes = await readFile(filePath);
|
|
4371
|
+
if (bytes.length === 0) {
|
|
4372
|
+
return `${label} must not be empty: ${filePath}`;
|
|
4373
|
+
}
|
|
4374
|
+
return {
|
|
4375
|
+
path: filePath,
|
|
4376
|
+
bytes,
|
|
4377
|
+
sha256: createHash("sha256").update(bytes).digest("hex")
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
catch (error) {
|
|
4381
|
+
return `Could not read ${label} file ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
3505
4384
|
function projectCreateBody(parsed) {
|
|
3506
4385
|
const extra = parsed.command.slice(3);
|
|
3507
4386
|
if (extra.length > 0) {
|
|
@@ -3795,8 +4674,15 @@ function renderServiceModeCatalog(catalog) {
|
|
|
3795
4674
|
` model_gateway=${catalog.boundaries.model_gateway}`,
|
|
3796
4675
|
` server_resource_orchestration=${catalog.boundaries.server_resource_orchestration}`,
|
|
3797
4676
|
` local_bridge=${catalog.boundaries.local_bridge}`,
|
|
4677
|
+
catalog.boundaries.free_registered_operations ? ` free_registered_operations=${catalog.boundaries.free_registered_operations}` : "",
|
|
4678
|
+
catalog.boundaries.paid_server_operations ? ` paid_server_operations=${catalog.boundaries.paid_server_operations}` : "",
|
|
4679
|
+
catalog.allocation_rule ? `allocation_rule=${catalog.allocation_rule}` : "",
|
|
4680
|
+
"free_registered_capabilities:",
|
|
4681
|
+
...(catalog.free_registered_capabilities ?? []).map((item) => ` - ${item.policy_id} workspace=${item.requires_server_workspace} lease=${item.allocates_server_resource_lease} meters=${item.meters.join(",") || "none"}`),
|
|
4682
|
+
"paid_capabilities:",
|
|
4683
|
+
...(catalog.paid_capabilities ?? []).map((item) => ` - ${item.policy_id} workspace=${item.requires_server_workspace} lease=${item.allocates_server_resource_lease} meters=${item.meters.join(",") || "none"}`),
|
|
3798
4684
|
"modes:"
|
|
3799
|
-
];
|
|
4685
|
+
].filter(Boolean);
|
|
3800
4686
|
for (const mode of catalog.modes) {
|
|
3801
4687
|
lines.push([
|
|
3802
4688
|
` ${mode.default ? "*" : "-"}`,
|
|
@@ -3837,6 +4723,15 @@ function renderPluginInstall(result) {
|
|
|
3837
4723
|
if (item.command_hint) {
|
|
3838
4724
|
lines.push(` ${item.command_hint}`);
|
|
3839
4725
|
}
|
|
4726
|
+
if (item.warning) {
|
|
4727
|
+
lines.push(` warning=${item.warning}`);
|
|
4728
|
+
}
|
|
4729
|
+
if (item.mcp_registered !== undefined) {
|
|
4730
|
+
lines.push(` codex_mcp_registered=${item.mcp_registered}`);
|
|
4731
|
+
}
|
|
4732
|
+
if (item.mcp_warning) {
|
|
4733
|
+
lines.push(` warning=${item.mcp_warning}`);
|
|
4734
|
+
}
|
|
3840
4735
|
}
|
|
3841
4736
|
return lines.join("\n");
|
|
3842
4737
|
}
|
|
@@ -3920,6 +4815,20 @@ function renderUsageRecord(record) {
|
|
|
3920
4815
|
`created_at=${record.created_at}`
|
|
3921
4816
|
].filter(Boolean).join(" ");
|
|
3922
4817
|
}
|
|
4818
|
+
function renderMcpToolEvent(event) {
|
|
4819
|
+
return [
|
|
4820
|
+
`${event.event_id} tool=${event.tool_name}`,
|
|
4821
|
+
event.account_id ? `account=${event.account_id}` : "",
|
|
4822
|
+
event.api_key_id ? `api_key=${event.api_key_id}` : "",
|
|
4823
|
+
`client=${event.client}`,
|
|
4824
|
+
`mode=${event.mode}`,
|
|
4825
|
+
`server_model_used=${event.server_model_used}`,
|
|
4826
|
+
`success=${event.success}`,
|
|
4827
|
+
event.request_id ? `request=${event.request_id}` : "",
|
|
4828
|
+
event.duration_ms !== undefined ? `duration_ms=${event.duration_ms}` : "",
|
|
4829
|
+
`created_at=${event.created_at}`
|
|
4830
|
+
].filter(Boolean).join(" ");
|
|
4831
|
+
}
|
|
3923
4832
|
function renderUsageSummary(summary) {
|
|
3924
4833
|
const lines = [
|
|
3925
4834
|
summary.account_id ? `account=${summary.account_id}` : "",
|
|
@@ -4208,10 +5117,13 @@ function renderBoardMethods(data) {
|
|
|
4208
5117
|
method.operation,
|
|
4209
5118
|
`status=${method.status}`,
|
|
4210
5119
|
`runtime=${method.runtime}`,
|
|
5120
|
+
method.access_tier ? `access=${method.access_tier}` : "",
|
|
4211
5121
|
`endpoint=${method.http_method} ${method.path}`,
|
|
4212
5122
|
`workspace=${method.requires_workspace}`,
|
|
5123
|
+
method.allocates_server_workspace !== undefined ? `alloc_workspace=${method.allocates_server_workspace}` : "",
|
|
5124
|
+
method.allocates_server_resource_lease !== undefined ? `alloc_lease=${method.allocates_server_resource_lease}` : "",
|
|
4213
5125
|
`approval=${method.requires_approval}`
|
|
4214
|
-
].join(" ")).join("\n");
|
|
5126
|
+
].filter(Boolean).join(" ")).join("\n");
|
|
4215
5127
|
}
|
|
4216
5128
|
function renderBoardKnowledge(data) {
|
|
4217
5129
|
const files = isJsonObject(data) && Array.isArray(data.knowledge_files)
|
|
@@ -4440,6 +5352,123 @@ function renderBuildWorkspaceSourcePatch(result) {
|
|
|
4440
5352
|
}
|
|
4441
5353
|
return lines.join("\n");
|
|
4442
5354
|
}
|
|
5355
|
+
function renderLocalToolchainLatest(result) {
|
|
5356
|
+
const lines = [
|
|
5357
|
+
`board=${result.board_id}`,
|
|
5358
|
+
`channel=${result.channel}`,
|
|
5359
|
+
`version=${result.version}`,
|
|
5360
|
+
`host=${result.host}`,
|
|
5361
|
+
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}` : "",
|
|
5365
|
+
result.download_error ? `download_error=${result.download_error}` : ""
|
|
5366
|
+
].filter(Boolean);
|
|
5367
|
+
if (result.packages.length > 0) {
|
|
5368
|
+
lines.push("packages:");
|
|
5369
|
+
for (const pkg of result.packages) {
|
|
5370
|
+
lines.push(` ${pkg.id}@${pkg.version}`);
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
return lines.join("\n");
|
|
5374
|
+
}
|
|
5375
|
+
function renderLocalToolchainCurrent(result) {
|
|
5376
|
+
if (!result.installed) {
|
|
5377
|
+
return [
|
|
5378
|
+
"No local toolchain installed.",
|
|
5379
|
+
`board=${result.board_id}`,
|
|
5380
|
+
`install_root=${result.install_root}`,
|
|
5381
|
+
`registry=${result.registry_path}`
|
|
5382
|
+
].join("\n");
|
|
5383
|
+
}
|
|
5384
|
+
return [
|
|
5385
|
+
"Local toolchain installed.",
|
|
5386
|
+
`board=${result.board_id}`,
|
|
5387
|
+
result.version ? `version=${result.version}` : "",
|
|
5388
|
+
result.channel ? `channel=${result.channel}` : "",
|
|
5389
|
+
result.release_root ? `release_root=${result.release_root}` : "",
|
|
5390
|
+
`install_root=${result.install_root}`,
|
|
5391
|
+
`registry=${result.registry_path}`
|
|
5392
|
+
].filter(Boolean).join("\n");
|
|
5393
|
+
}
|
|
5394
|
+
function renderLocalToolchainInstall(result) {
|
|
5395
|
+
const lines = [
|
|
5396
|
+
"Local toolchain installed.",
|
|
5397
|
+
`board=${result.board_id}`,
|
|
5398
|
+
`version=${result.version}`,
|
|
5399
|
+
`channel=${result.channel}`,
|
|
5400
|
+
`host=${result.host}`,
|
|
5401
|
+
`install_root=${result.install_root}`,
|
|
5402
|
+
`release_root=${result.release_root}`,
|
|
5403
|
+
`registry=${result.registry_path}`,
|
|
5404
|
+
`source=${result.source.kind}:${result.source.value}`,
|
|
5405
|
+
result.source.downloaded_path ? `downloaded=${result.source.downloaded_path}` : "",
|
|
5406
|
+
`validation=${result.validation.ok ? "ok" : "failed"}`
|
|
5407
|
+
].filter(Boolean);
|
|
5408
|
+
if (result.installed_paths.length > 0) {
|
|
5409
|
+
lines.push("installed_paths:");
|
|
5410
|
+
for (const installedPath of result.installed_paths) {
|
|
5411
|
+
lines.push(` ${installedPath}`);
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
if (result.packages.length > 0) {
|
|
5415
|
+
lines.push("packages:");
|
|
5416
|
+
for (const pkg of result.packages) {
|
|
5417
|
+
lines.push(` ${pkg.id}@${pkg.version}`);
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
return lines.join("\n");
|
|
5421
|
+
}
|
|
5422
|
+
function renderLocalToolchainValidation(result) {
|
|
5423
|
+
const lines = [
|
|
5424
|
+
result.ok ? "Local toolchain ready." : "Local toolchain not ready.",
|
|
5425
|
+
`board=${result.board_id}`,
|
|
5426
|
+
`host=${result.host.platform}/${result.host.arch}`,
|
|
5427
|
+
`release_root=${result.release_root}`
|
|
5428
|
+
];
|
|
5429
|
+
for (const check of result.checked_paths) {
|
|
5430
|
+
lines.push(`${check.exists ? "ok" : "missing"} ${check.label}: ${check.path}`);
|
|
5431
|
+
}
|
|
5432
|
+
if (result.notes.length > 0) {
|
|
5433
|
+
lines.push("notes:");
|
|
5434
|
+
for (const note of result.notes) {
|
|
5435
|
+
lines.push(` ${note}`);
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
return lines.join("\n");
|
|
5439
|
+
}
|
|
5440
|
+
function renderLocalCompileResult(result) {
|
|
5441
|
+
const lines = [
|
|
5442
|
+
`${result.operation} succeeded.`,
|
|
5443
|
+
`board=${result.board_id}`,
|
|
5444
|
+
result.account_id ? `account=${result.account_id}` : "",
|
|
5445
|
+
`auth=${result.auth.authenticated ? "authenticated" : "anonymous"} profile=${result.auth.profile}${result.auth.source ? ` source=${result.auth.source}` : ""}`,
|
|
5446
|
+
`source=${result.source_path}`,
|
|
5447
|
+
result.build_dir ? `build_dir=${result.build_dir}` : "",
|
|
5448
|
+
`artifact=${result.artifact_path}`,
|
|
5449
|
+
`size=${result.artifact_size_bytes}`,
|
|
5450
|
+
`sha256=${result.artifact_sha256}`,
|
|
5451
|
+
result.file_info ? `file=${result.file_info}` : "",
|
|
5452
|
+
`manifest=${result.manifest_path}`
|
|
5453
|
+
].filter(Boolean);
|
|
5454
|
+
for (const command of result.commands) {
|
|
5455
|
+
lines.push(`command_exit=${command.exit_code} cwd=${command.cwd}`);
|
|
5456
|
+
lines.push(` ${command.command.join(" ")}`);
|
|
5457
|
+
if (command.stdout_tail.length > 0) {
|
|
5458
|
+
lines.push(" stdout_tail:");
|
|
5459
|
+
for (const line of command.stdout_tail.slice(-12)) {
|
|
5460
|
+
lines.push(` ${line}`);
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
if (command.stderr_tail.length > 0) {
|
|
5464
|
+
lines.push(" stderr_tail:");
|
|
5465
|
+
for (const line of command.stderr_tail.slice(-12)) {
|
|
5466
|
+
lines.push(` ${line}`);
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
return lines.join("\n");
|
|
5471
|
+
}
|
|
4443
5472
|
function renderDoctor(result) {
|
|
4444
5473
|
const lines = [
|
|
4445
5474
|
`${result.status === "ready" ? "Ready" : "Not ready"}: ${result.summary_for_user}`,
|
|
@@ -4610,6 +5639,46 @@ function renderJob(job) {
|
|
|
4610
5639
|
}
|
|
4611
5640
|
return lines.join("\n");
|
|
4612
5641
|
}
|
|
5642
|
+
function renderBootLogoPackageResult(result) {
|
|
5643
|
+
const lines = [renderJob(result.task)];
|
|
5644
|
+
if (result.downloaded_artifact) {
|
|
5645
|
+
lines.push("downloaded_package:");
|
|
5646
|
+
lines.push(` artifact=${result.downloaded_artifact.artifact_id}`);
|
|
5647
|
+
lines.push(` output=${result.downloaded_artifact.output_path}`);
|
|
5648
|
+
lines.push(` size=${result.downloaded_artifact.size_bytes}`);
|
|
5649
|
+
}
|
|
5650
|
+
lines.push("next=merge the downloaded package with the local base image, then flash only after explicit approval.");
|
|
5651
|
+
return lines.join("\n");
|
|
5652
|
+
}
|
|
5653
|
+
function renderDtbPackageResult(result) {
|
|
5654
|
+
const lines = [renderJob(result.task)];
|
|
5655
|
+
if (result.downloaded_artifact) {
|
|
5656
|
+
lines.push("downloaded_package:");
|
|
5657
|
+
lines.push(` artifact=${result.downloaded_artifact.artifact_id}`);
|
|
5658
|
+
lines.push(` output=${result.downloaded_artifact.output_path}`);
|
|
5659
|
+
lines.push(` size=${result.downloaded_artifact.size_bytes}`);
|
|
5660
|
+
}
|
|
5661
|
+
lines.push("next=merge the downloaded DTB package with the local base image, then flash only after explicit approval.");
|
|
5662
|
+
return lines.join("\n");
|
|
5663
|
+
}
|
|
5664
|
+
function renderBootLogoComposeResult(result) {
|
|
5665
|
+
const label = result.operation_kind === "image.dtb.compose" ? "DTB" : "Boot-logo";
|
|
5666
|
+
return [
|
|
5667
|
+
result.ready_for_flash ? `${label} compose ready for flash review.` : `${label} compose created a guarded local result.`,
|
|
5668
|
+
`board=${result.board_id}`,
|
|
5669
|
+
result.variant_id ? `variant=${result.variant_id}` : "",
|
|
5670
|
+
`package=${result.package_path}`,
|
|
5671
|
+
`base=${result.base_image_path}`,
|
|
5672
|
+
`output=${result.output_image_path}`,
|
|
5673
|
+
`manifest=${result.manifest_path}`,
|
|
5674
|
+
`strategy=${result.local_merge_strategy}`,
|
|
5675
|
+
`patches=${result.patch_operations_applied}/${result.patch_operations_total}`,
|
|
5676
|
+
`replacement_applied=${result.replacement_applied}`,
|
|
5677
|
+
`ready_for_flash=${result.ready_for_flash}`,
|
|
5678
|
+
`cross_platform=${result.cross_platform} platform=${result.platform}/${result.arch}`,
|
|
5679
|
+
result.summary_for_user
|
|
5680
|
+
].filter(Boolean).join("\n");
|
|
5681
|
+
}
|
|
4613
5682
|
function safeArtifactOutputFileName(name) {
|
|
4614
5683
|
return name.replace(/[^A-Za-z0-9._-]+/g, "_") || "artifact.out";
|
|
4615
5684
|
}
|
|
@@ -4808,6 +5877,22 @@ function stringFlag(parsed, name) {
|
|
|
4808
5877
|
function booleanFlag(parsed, name) {
|
|
4809
5878
|
return parsed.flags[name] === true;
|
|
4810
5879
|
}
|
|
5880
|
+
function optionalBooleanFlag(parsed, name) {
|
|
5881
|
+
const values = flagValues(parsed, name);
|
|
5882
|
+
if (values.length === 0)
|
|
5883
|
+
return undefined;
|
|
5884
|
+
const value = values[values.length - 1];
|
|
5885
|
+
if (value === true)
|
|
5886
|
+
return true;
|
|
5887
|
+
if (typeof value !== "string")
|
|
5888
|
+
return `--${name} must be true or false.`;
|
|
5889
|
+
const normalized = value.trim().toLowerCase();
|
|
5890
|
+
if (["1", "true", "yes", "y", "on"].includes(normalized))
|
|
5891
|
+
return true;
|
|
5892
|
+
if (["0", "false", "no", "n", "off"].includes(normalized))
|
|
5893
|
+
return false;
|
|
5894
|
+
return `--${name} must be true or false.`;
|
|
5895
|
+
}
|
|
4811
5896
|
function switchFlag(parsed, name) {
|
|
4812
5897
|
const values = flagValues(parsed, name);
|
|
4813
5898
|
for (const value of values) {
|
|
@@ -5089,7 +6174,7 @@ async function waitForever() {
|
|
|
5089
6174
|
return 0;
|
|
5090
6175
|
}
|
|
5091
6176
|
function printHelp() {
|
|
5092
|
-
|
|
6177
|
+
printCliHelp(`embed CLI
|
|
5093
6178
|
|
|
5094
6179
|
Usage:
|
|
5095
6180
|
embed <command> [options]
|
|
@@ -5111,13 +6196,19 @@ Main workflow:
|
|
|
5111
6196
|
embed model default
|
|
5112
6197
|
4. Run a natural-language local tool loop:
|
|
5113
6198
|
embed agent run --prompt "验证开发板状态"
|
|
5114
|
-
5.
|
|
6199
|
+
5. Validate or use the local TaishanPi toolchain:
|
|
6200
|
+
embed local toolchain latest
|
|
6201
|
+
embed local toolchain install
|
|
6202
|
+
embed local toolchain validate
|
|
6203
|
+
embed local compile taishanpi --source ./main.c --output ./.embed-labs/build/main
|
|
6204
|
+
embed local build qt-smoke --build-dir ./.embed-labs/build/qt-smoke
|
|
6205
|
+
6. Pick a cloud build template:
|
|
5115
6206
|
embed board registry list
|
|
5116
6207
|
embed board methods taishanpi-1m-rk3566
|
|
5117
6208
|
embed board knowledge taishanpi-1m-rk3566
|
|
5118
6209
|
embed build template list
|
|
5119
6210
|
embed build template show <template_id>
|
|
5120
|
-
|
|
6211
|
+
7. Provision and populate a build workspace:
|
|
5121
6212
|
embed build workspace provision --account <account_id> --project <project_id> --template <template_id>
|
|
5122
6213
|
embed build resource lease create --workspace <workspace_id> --execution-mode cloud_worker
|
|
5123
6214
|
embed build workspace source put <workspace_id> --file ./main.c:src/main.c
|
|
@@ -5126,13 +6217,15 @@ Main workflow:
|
|
|
5126
6217
|
embed build workspace source search <workspace_id> --query init --glob "**/*.c"
|
|
5127
6218
|
embed build workspace source patch <workspace_id> --patch ./fix.patch
|
|
5128
6219
|
embed build workspace release <workspace_id> --dry-run
|
|
5129
|
-
|
|
6220
|
+
8. Generate application source on the server and follow artifacts:
|
|
5130
6221
|
embed build application generate --workspace <workspace_id> --prompt "Create a minimal Linux app" --provider bai --model gpt-5.2
|
|
5131
6222
|
embed build application compile --workspace <workspace_id> --source app/generated.c --execution-mode docker_worker
|
|
5132
6223
|
embed build image generate --workspace <workspace_id> --prompt "Generate a minimal TaishanPi image"
|
|
6224
|
+
embed build image boot-logo --logo ./logo.png --board taishanpi --variant 1M-RK3566 --output ./boot-logo-package.json
|
|
6225
|
+
embed image boot-logo compose --package ./boot-logo-package.json --base-image ./boot.img --output ./boot-logo.img
|
|
5133
6226
|
embed cloud task artifacts <task_id>
|
|
5134
6227
|
embed artifact download <artifact_id> --output ./artifact.bin
|
|
5135
|
-
|
|
6228
|
+
9. Check credits or create a recharge QR:
|
|
5136
6229
|
embed billing balance --account <account_id>
|
|
5137
6230
|
embed billing tokens --account <account_id>
|
|
5138
6231
|
embed billing ledger --account <account_id>
|
|
@@ -5147,7 +6240,17 @@ Local hardware:
|
|
|
5147
6240
|
embed agent run --prompt "部署生成的泰山派应用" --host 198.19.77.2 --artifact-task <task_id> --remote-path /userdata/embed-labs/apps/app --approve --run
|
|
5148
6241
|
embed tool list
|
|
5149
6242
|
embed tool call device.probe --input-json '{"host":"198.19.77.2","ports":[22,15301]}'
|
|
6243
|
+
embed tool call wifi.scan --input-json '{"host":"198.19.77.2","user":"root"}'
|
|
6244
|
+
embed tool call chip.temperature --input-json '{"host":"198.19.77.2","user":"root"}'
|
|
6245
|
+
embed tool call qml.runtime.status --input-json '{"host":"198.19.77.2","user":"root","port":18130}'
|
|
5150
6246
|
embed device list
|
|
6247
|
+
embed local toolchain latest
|
|
6248
|
+
embed local toolchain current
|
|
6249
|
+
embed local toolchain install
|
|
6250
|
+
embed local toolchain validate
|
|
6251
|
+
embed local compile taishanpi --source ./main.c --output ./.embed-labs/build/main
|
|
6252
|
+
embed local build qt-smoke --build-dir ./.embed-labs/build/qt-smoke
|
|
6253
|
+
embed image boot-logo compose --package ./boot-logo-package.json --base-image ./boot.img --output ./boot-logo.img
|
|
5151
6254
|
embed deploy taishanpi --host 198.19.77.2 --artifact ./artifact.bin --approve --run
|
|
5152
6255
|
embed flash plan --board <rp2350|taishanpi>
|
|
5153
6256
|
|
|
@@ -5182,7 +6285,7 @@ function printHelpTopic(topic) {
|
|
|
5182
6285
|
printCommandReference();
|
|
5183
6286
|
return;
|
|
5184
6287
|
}
|
|
5185
|
-
|
|
6288
|
+
printCliHelp(`Unknown help topic: ${topic}
|
|
5186
6289
|
|
|
5187
6290
|
Available topics:
|
|
5188
6291
|
embed help getting-started
|
|
@@ -5190,7 +6293,7 @@ Available topics:
|
|
|
5190
6293
|
`);
|
|
5191
6294
|
}
|
|
5192
6295
|
function printGettingStartedHelp() {
|
|
5193
|
-
|
|
6296
|
+
printCliHelp(`embed getting started
|
|
5194
6297
|
|
|
5195
6298
|
After npm install, use the installed embed binary directly:
|
|
5196
6299
|
|
|
@@ -5219,6 +6322,9 @@ Cloud build path:
|
|
|
5219
6322
|
embed model list
|
|
5220
6323
|
embed model default
|
|
5221
6324
|
embed agent run --prompt "验证开发板状态"
|
|
6325
|
+
embed local toolchain validate
|
|
6326
|
+
embed local compile taishanpi --source ./main.c --output ./.embed-labs/build/main
|
|
6327
|
+
embed local build qt-smoke --build-dir ./.embed-labs/build/qt-smoke
|
|
5222
6328
|
embed board registry list
|
|
5223
6329
|
embed board registry show taishanpi-1m-rk3566
|
|
5224
6330
|
embed board methods taishanpi-1m-rk3566
|
|
@@ -5236,6 +6342,8 @@ Cloud build path:
|
|
|
5236
6342
|
embed build application generate --workspace <workspace_id> --prompt "Create a minimal Linux app" --provider bai --model gpt-5.2
|
|
5237
6343
|
embed build application compile --workspace <workspace_id> --source app/generated.c --execution-mode docker_worker
|
|
5238
6344
|
embed build image generate --workspace <workspace_id> --prompt "Generate a minimal TaishanPi image"
|
|
6345
|
+
embed build image boot-logo --logo ./logo.png --board taishanpi --variant 1M-RK3566 --output ./boot-logo-package.json
|
|
6346
|
+
embed image boot-logo compose --package ./boot-logo-package.json --base-image ./boot.img --output ./boot-logo.img
|
|
5239
6347
|
embed cloud task status <task_id>
|
|
5240
6348
|
embed cloud task artifacts <task_id>
|
|
5241
6349
|
embed artifact download <artifact_id> --output ./artifact.bin
|
|
@@ -5273,7 +6381,7 @@ Use --json on commands when scripting or running in CI.
|
|
|
5273
6381
|
`);
|
|
5274
6382
|
}
|
|
5275
6383
|
function printCommandReference() {
|
|
5276
|
-
|
|
6384
|
+
printCliHelp(`embed CLI
|
|
5277
6385
|
|
|
5278
6386
|
Usage:
|
|
5279
6387
|
embed doctor [--json]
|
|
@@ -5293,7 +6401,7 @@ Usage:
|
|
|
5293
6401
|
embed board methods <template_id> [--json]
|
|
5294
6402
|
embed board knowledge <template_id> [--json]
|
|
5295
6403
|
embed board knowledge file <template_id> --source board_pack|build_template|registry --path <relative_path> [--output <local_path>] [--json]
|
|
5296
|
-
embed agent run --prompt <request> [--account <account_id>] [--workspace <workspace_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--max-tool-calls
|
|
6404
|
+
embed agent run --prompt <request> [--account <account_id>] [--workspace <workspace_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--max-tool-calls 6] [--host <ip>] [--ports 22,15301] [--artifact <local_file>|--artifact-id <artifact_id>|--artifact-task <task_id>] [--artifact-output <path>] [--remote-path <path>] [--run] [--approve] [--json]
|
|
5297
6405
|
embed run <natural language request> [--provider stub|openai|bai|cc|claude-code] [--approve] [--json]
|
|
5298
6406
|
embed tool list [--json]
|
|
5299
6407
|
embed tool call <capability_id> [--input-json '<json>'] [--approve] [--json]
|
|
@@ -5338,7 +6446,26 @@ Usage:
|
|
|
5338
6446
|
embed build application generate --workspace <workspace_id> --prompt <request> [--account <account_id>] [--target <source_path>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--json]
|
|
5339
6447
|
embed build application compile --workspace <workspace_id> [--account <account_id>] [--source <source_path>] [--execution-mode docker_worker|server_direct|dry_run] [--compiler auto|cc|gcc|clang|aarch64-linux-gnu-gcc] [--json]
|
|
5340
6448
|
embed build image generate --workspace <workspace_id> --prompt <request> [--account <account_id>] [--image-profile <profile_id>] [--provider stub|openai|bai|cc|claude-code] [--model <model>] [--execution-mode cloud_worker|dry_run] [--worker-pool <pool>] [--json]
|
|
6449
|
+
embed build image boot-logo --logo <local_image> [--account <account_id>] [--project <project_id>] [--board taishanpi] [--variant 1M-RK3566] [--kernel-logo <local_image>] [--rotate -90] [--scale 100] [--output <package.json>] [--json]
|
|
6450
|
+
embed image boot-logo compose --package <boot-logo-package.json> --base-image <boot.img|image.img> --output <image> [--manifest <manifest.json>] [--force] [--json]
|
|
6451
|
+
embed local toolchain latest [--board taishanpi-1m-rk3566] [--channel stable] [--metadata-root <path>] [--json]
|
|
6452
|
+
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]
|
|
6454
|
+
Defaults to the production download channel at download.embedboard.com.
|
|
6455
|
+
embed local toolchain validate [--release-root <path>] [--json]
|
|
6456
|
+
embed local compile taishanpi --source <main.c|main.cpp> --output <artifact> [--release-root <path>] [--account <account_id>] [--json]
|
|
6457
|
+
embed local build qt-smoke --build-dir <dir> [--source <qt-smoke-dir>] [--release-root <path>] [--account <account_id>] [--json]
|
|
5341
6458
|
embed debug tools [--json]
|
|
6459
|
+
embed tool list [--json]
|
|
6460
|
+
embed tool call wifi.scan --input-json '{"host":"198.19.77.2","user":"root"}' [--json]
|
|
6461
|
+
embed tool call bluetooth.scan --input-json '{"host":"198.19.77.2","user":"root","duration_seconds":8}' [--json]
|
|
6462
|
+
embed tool call chip.cpu.frequency --input-json '{"host":"198.19.77.2","user":"root"}' [--json]
|
|
6463
|
+
embed tool call chip.temperature --input-json '{"host":"198.19.77.2","user":"root"}' [--json]
|
|
6464
|
+
embed tool call qml.runtime.status --input-json '{"host":"198.19.77.2","user":"root","port":18130}' [--json]
|
|
6465
|
+
embed tool call qml.runtime.start --input-json '{"host":"198.19.77.2","user":"root","port":18130}' [--json]
|
|
6466
|
+
embed tool call rp2350.monitor.status [--json]
|
|
6467
|
+
embed tool call rp2350.monitor.logic.capture --input-json '{"pin_base":16,"pin_count":4,"sample_rate":1000000,"samples":4096}' [--json]
|
|
6468
|
+
embed tool call rp2350.monitor.logic.decode --input-json '{"input_path":".embed-labs/rp2350-monitor/captures/logic.jsonl","decoder":"summary"}' [--json]
|
|
5342
6469
|
embed deploy taishanpi --host <ip> --artifact <local_file> --approve [--remote-path /userdata/embed-labs/apps/app] [--run] [--json]
|
|
5343
6470
|
embed board deploy taishanpi --host <ip> --artifact <local_file> --approve [--remote-path /userdata/embed-labs/apps/app] [--run] [--json]
|
|
5344
6471
|
embed device list [--json]
|
|
@@ -5377,6 +6504,23 @@ Environment:
|
|
|
5377
6504
|
CODEX_HOME=~/.codex
|
|
5378
6505
|
`);
|
|
5379
6506
|
}
|
|
6507
|
+
function printCliHelp(text) {
|
|
6508
|
+
console.log(formatCliHelp(text));
|
|
6509
|
+
}
|
|
6510
|
+
function formatCliHelp(text) {
|
|
6511
|
+
if (currentCommandName() !== "embedlabs") {
|
|
6512
|
+
return text;
|
|
6513
|
+
}
|
|
6514
|
+
return text
|
|
6515
|
+
.replace(/^embed CLI$/m, "embedlabs CLI")
|
|
6516
|
+
.replace(/^embed getting started$/m, "embedlabs getting started")
|
|
6517
|
+
.replace("After npm install, use the installed embed binary directly:", "After npm install, use the installed embedlabs binary directly:")
|
|
6518
|
+
.replace(/(^|\n)(\s*)embed(\s+)/g, "$1$2embedlabs$3");
|
|
6519
|
+
}
|
|
6520
|
+
function currentCommandName() {
|
|
6521
|
+
const invoked = basename(process.argv[1] ?? "");
|
|
6522
|
+
return invoked.startsWith("embedlabs") ? "embedlabs" : "embed";
|
|
6523
|
+
}
|
|
5380
6524
|
main(process.argv.slice(2))
|
|
5381
6525
|
.then((code) => {
|
|
5382
6526
|
process.exitCode = code;
|