@kvell007/embed-labs-cli 0.1.0-alpha.16 → 0.1.0-alpha.17

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 CHANGED
@@ -81,6 +81,18 @@ embedlabs doctor
81
81
  embedlabs auth login --token <token>
82
82
  ```
83
83
 
84
+ If a cloud or plugin command reaches a protected API before a token is
85
+ configured, the CLI returns `auth_token_missing` with the registration URL and
86
+ these setup options:
87
+
88
+ ```bash
89
+ open https://api.embedboard.com/dashboard
90
+ embedlabs auth login --token <your_token>
91
+ # or for automation:
92
+ export EMBED_API_TOKEN=<your_token>
93
+ embedlabs auth status
94
+ ```
95
+
84
96
  For the current public API plus local TaishanPi verification path, see
85
97
  `docs/runbooks/PUBLIC_CLI_USER_VERIFICATION.md`.
86
98
 
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ const qrcodeTerminal = require("qrcode-terminal");
18
18
  const DEFAULT_BRIDGE_URL = process.env.EMBED_BRIDGE_URL ?? "http://127.0.0.1:18083";
19
19
  const DEFAULT_CLOUD_API_URL = process.env.EMBED_CLOUD_API_URL ?? "http://127.0.0.1:18100";
20
20
  const DEFAULT_AUTH_FILE = process.env.EMBED_AUTH_FILE ?? ".embed-labs/auth.json";
21
+ const DEFAULT_DASHBOARD_URL = process.env.EMBED_DASHBOARD_URL ?? "https://api.embedboard.com/dashboard";
21
22
  const DEFAULT_AGENT_ARTIFACT_DIR = process.env.EMBED_AGENT_ARTIFACT_DIR ?? ".embed-labs/artifacts";
22
23
  const DOCTOR_USAGE = "Usage: embed doctor [--json]";
23
24
  const DOCTOR_HTTP_TIMEOUT_MS = 8000;
@@ -1200,7 +1201,8 @@ function authDoctorCheck(status) {
1200
1201
  : {
1201
1202
  code: "auth_not_ready",
1202
1203
  message: "No CLI auth token is configured.",
1203
- remediation: "Run: embed auth login --token <token>"
1204
+ remediation: cloudAuthSetupRemediation(),
1205
+ details: cloudAuthSetupDetails()
1204
1206
  }
1205
1207
  };
1206
1208
  }
@@ -1411,7 +1413,7 @@ async function cloudDownloadArtifact(artifactId, outputPath) {
1411
1413
  });
1412
1414
  if (!response.ok) {
1413
1415
  const parsed = await parseErrorResponse(response);
1414
- return parsed ?? fail("artifact_download_failed", `Artifact download failed with HTTP ${response.status}.`);
1416
+ return parsed ? enrichCloudAuthFailure(parsed, Boolean(token)) : fail("artifact_download_failed", `Artifact download failed with HTTP ${response.status}.`);
1415
1417
  }
1416
1418
  const bytes = Buffer.from(await response.arrayBuffer());
1417
1419
  const expectedSha256 = response.headers.get("x-embed-artifact-sha256")?.trim();
@@ -1454,7 +1456,8 @@ async function cloudRequest(method, path, body) {
1454
1456
  headers: Object.keys(headers).length > 0 ? headers : undefined,
1455
1457
  body: body === undefined ? undefined : bodyText
1456
1458
  });
1457
- return await response.json();
1459
+ const parsed = await response.json();
1460
+ return enrichCloudAuthFailure(parsed, Boolean(token));
1458
1461
  }
1459
1462
  catch (error) {
1460
1463
  return fail("cloud_api_unreachable", error instanceof Error ? error.message : String(error), {
@@ -1462,6 +1465,39 @@ async function cloudRequest(method, path, body) {
1462
1465
  });
1463
1466
  }
1464
1467
  }
1468
+ function enrichCloudAuthFailure(response, hadToken) {
1469
+ if (response.ok || response.error.code !== "unauthorized") {
1470
+ return response;
1471
+ }
1472
+ if (!hadToken) {
1473
+ return fail("auth_token_missing", "Embed Labs API Token is not configured. Register or sign in, create an API Token, then configure it locally before using cloud and plugin services.", {
1474
+ remediation: cloudAuthSetupRemediation(),
1475
+ details: cloudAuthSetupDetails()
1476
+ });
1477
+ }
1478
+ return fail("auth_token_rejected", "The configured Embed Labs API Token was rejected by the server. Recreate or copy a fresh token from the dashboard and sign in again.", {
1479
+ remediation: cloudAuthSetupRemediation(),
1480
+ details: cloudAuthSetupDetails()
1481
+ });
1482
+ }
1483
+ function cloudAuthSetupRemediation() {
1484
+ return [
1485
+ `1. Open ${DEFAULT_DASHBOARD_URL} and register or sign in.`,
1486
+ "2. Create or copy your Embed Labs API Token from the user dashboard.",
1487
+ "3. Run: embedlabs auth login --token <your_token>",
1488
+ "4. For automation, set: EMBED_API_TOKEN=<your_token>",
1489
+ "5. Verify with: embedlabs auth status"
1490
+ ].join("\n");
1491
+ }
1492
+ function cloudAuthSetupDetails() {
1493
+ return {
1494
+ dashboard_url: DEFAULT_DASHBOARD_URL,
1495
+ login_command: "embedlabs auth login --token <your_token>",
1496
+ env_var: "EMBED_API_TOKEN",
1497
+ auth_status_command: "embedlabs auth status",
1498
+ auth_file: DEFAULT_AUTH_FILE
1499
+ };
1500
+ }
1465
1501
  function addCloudRequestSignature(headers, method, pathWithQuery, bodyText, token) {
1466
1502
  if (process.env.EMBED_CLOUD_API_SIGNING === "0") {
1467
1503
  return;
@@ -1914,7 +1950,8 @@ async function cleanupLegacyCodexPluginRemnants(targetRoot) {
1914
1950
  ];
1915
1951
  legacyPaths.push(...await discoverLegacyCodexCachePaths(targetRoot));
1916
1952
  if (resolve(targetRoot) === resolve(defaultCodexPluginRoot())) {
1917
- legacyPaths.push(join(defaultCodexHome(), ".tmp", "plugins"), join(defaultCodexHome(), ".tmp", "plugins.sha"), join(defaultCodexHome(), "skills", "dbt-agent"), join(defaultCodexHome(), "skills", "development-board-toolchain-dev"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-live-board-ops"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-platform-plugin-maintenance"));
1953
+ legacyPaths.push(join(defaultCodexHome(), ".tmp", "plugins"), join(defaultCodexHome(), ".tmp", "plugins.sha"), join(defaultCodexHome(), "ambient-suggestions"), join(defaultCodexHome(), "skills", "dbt-agent"), join(defaultCodexHome(), "skills", "development-board-toolchain-dev"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-live-board-ops"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-platform-plugin-maintenance"));
1954
+ legacyPaths.push(...await discoverLegacyHomeAgentsMarketplacePaths(warnings), ...legacyDevelopmentBoardRuntimePluginPaths());
1918
1955
  }
1919
1956
  for (const candidate of legacyPaths) {
1920
1957
  try {
@@ -1986,7 +2023,56 @@ async function stopLegacyCodexPluginProcesses(warnings) {
1986
2023
  function isLegacyCodexPluginProcess(command) {
1987
2024
  const trimmed = command.trim();
1988
2025
  return /^\/.*\/dbt-agent-mcp-bridge(?:\s|$)/.test(trimmed)
1989
- || /^dbt-agent-mcp-bridge(?:\s|$)/.test(trimmed);
2026
+ || /^dbt-agent-mcp-bridge(?:\s|$)/.test(trimmed)
2027
+ || legacyDevelopmentBoardRuntimeProcessPatterns().some((pattern) => pattern.test(trimmed));
2028
+ }
2029
+ function legacyDevelopmentBoardRuntimeProcessPatterns() {
2030
+ const home = escapeRegExp(homedir());
2031
+ return [
2032
+ new RegExp(`^${home}/Library/development-board-toolchain/agent/bin/dbt-agentd(?:\\s|$)`),
2033
+ new RegExp(`^${home}/Library/Application Support/development-board-toolchain/agent/bin/dbt-agentd(?:\\s|$)`),
2034
+ new RegExp(`^${home}/Library/development-board-toolchain/runtime/dbtctl\\s+status(?:\\s|$)`),
2035
+ new RegExp(`^${home}/Library/Application Support/development-board-toolchain/runtime/dbtctl\\s+status(?:\\s|$)`),
2036
+ new RegExp(`^${home}/.*?/DBT-Agent\\.app/Contents/MacOS/DBT-Agent(?:\\s|$)`)
2037
+ ];
2038
+ }
2039
+ function legacyDevelopmentBoardRuntimePluginPaths() {
2040
+ return [
2041
+ join(homedir(), "Library", "development-board-toolchain", "runtime", "editor_plugins"),
2042
+ join(homedir(), "Library", "development-board-toolchain", "runtime", "opencode_plugin"),
2043
+ join(homedir(), "Library", "Application Support", "development-board-toolchain", "runtime", "editor_plugins"),
2044
+ join(homedir(), "Library", "Application Support", "development-board-toolchain", "runtime", "opencode_plugin")
2045
+ ];
2046
+ }
2047
+ async function discoverLegacyHomeAgentsMarketplacePaths(warnings) {
2048
+ const paths = [];
2049
+ const pluginRoot = join(homedir(), ".agents", "plugins");
2050
+ try {
2051
+ const entries = await readdir(pluginRoot, { withFileTypes: true });
2052
+ for (const entry of entries) {
2053
+ if (!entry.isFile() || !entry.name.startsWith("marketplace.json"))
2054
+ continue;
2055
+ const filePath = join(pluginRoot, entry.name);
2056
+ try {
2057
+ const current = await readFile(filePath, "utf8");
2058
+ if (isLegacyHomeAgentsMarketplace(current)) {
2059
+ paths.push(filePath);
2060
+ }
2061
+ }
2062
+ catch (error) {
2063
+ warnings.push(`Could not inspect legacy Codex home marketplace ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
2064
+ }
2065
+ }
2066
+ }
2067
+ catch {
2068
+ return paths;
2069
+ }
2070
+ return paths;
2071
+ }
2072
+ function isLegacyHomeAgentsMarketplace(text) {
2073
+ return text.includes('"name" : "plugins"') || text.includes('"name": "plugins"')
2074
+ ? text.includes('"dbt-agent"') || text.includes('"./.codex/plugins/dbt-agent"') || text.includes('"./plugins/dbt-agent"')
2075
+ : false;
1990
2076
  }
1991
2077
  async function discoverLegacyCodexCachePaths(targetRoot) {
1992
2078
  const paths = [];
@@ -2074,7 +2160,8 @@ function isLegacyCodexConfigTable(table) {
2074
2160
  || table === "mcp_servers.dbt-agent"
2075
2161
  || table.startsWith("mcp_servers.dbt-agent.")
2076
2162
  || table === 'mcp_servers."dbt-agent"'
2077
- || table.startsWith('mcp_servers."dbt-agent".');
2163
+ || table.startsWith('mcp_servers."dbt-agent".')
2164
+ || /^projects\."[^"]*\/DBT-Agent-Project(?:\/[^"]*)?"$/.test(table);
2078
2165
  }
2079
2166
  function legacyCodexCleanupWarning(cleanup) {
2080
2167
  const parts = [];