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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createHash } from "node:crypto";
2
+ import { createHash, createHmac, randomBytes } from "node:crypto";
3
3
  import { constants } from "node:fs";
4
4
  import { spawn } from "node:child_process";
5
5
  import { access, cp, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
@@ -1386,6 +1386,7 @@ async function cloudDownloadArtifact(artifactId, outputPath) {
1386
1386
  const token = await cloudAuthToken();
1387
1387
  if (token) {
1388
1388
  headers.authorization = `Bearer ${token}`;
1389
+ addCloudRequestSignature(headers, "GET", `/v1/artifacts/${encodeURIComponent(artifactId)}/download`, "", token);
1389
1390
  }
1390
1391
  const response = await fetch(`${serviceBaseUrl(DEFAULT_CLOUD_API_URL)}/v1/artifacts/${encodeURIComponent(artifactId)}/download`, {
1391
1392
  headers: Object.keys(headers).length > 0 ? headers : undefined
@@ -1421,17 +1422,19 @@ async function cloudDownloadArtifact(artifactId, outputPath) {
1421
1422
  async function cloudRequest(method, path, body) {
1422
1423
  try {
1423
1424
  const headers = {};
1424
- if (body !== undefined) {
1425
+ const bodyText = body === undefined ? "" : JSON.stringify(body);
1426
+ if (bodyText) {
1425
1427
  headers["content-type"] = "application/json";
1426
1428
  }
1427
1429
  const token = await cloudAuthToken();
1428
1430
  if (token) {
1429
1431
  headers.authorization = `Bearer ${token}`;
1432
+ addCloudRequestSignature(headers, method, path, bodyText, token);
1430
1433
  }
1431
1434
  const response = await fetch(`${serviceBaseUrl(DEFAULT_CLOUD_API_URL)}${path}`, {
1432
1435
  method,
1433
1436
  headers: Object.keys(headers).length > 0 ? headers : undefined,
1434
- body: body === undefined ? undefined : JSON.stringify(body)
1437
+ body: body === undefined ? undefined : bodyText
1435
1438
  });
1436
1439
  return await response.json();
1437
1440
  }
@@ -1441,6 +1444,39 @@ async function cloudRequest(method, path, body) {
1441
1444
  });
1442
1445
  }
1443
1446
  }
1447
+ function addCloudRequestSignature(headers, method, pathWithQuery, bodyText, token) {
1448
+ if (process.env.EMBED_CLOUD_API_SIGNING === "0") {
1449
+ return;
1450
+ }
1451
+ const timestamp = String(Math.floor(Date.now() / 1000));
1452
+ const nonce = randomBytes(16).toString("hex");
1453
+ const bodySha256 = createHash("sha256").update(bodyText).digest("hex");
1454
+ const keyId = createHash("sha256").update(token).digest("hex").slice(0, 16);
1455
+ const canonical = cloudRequestCanonicalString(method, pathWithQuery, timestamp, nonce, bodySha256);
1456
+ headers["x-embed-key-id"] = keyId;
1457
+ headers["x-embed-timestamp"] = timestamp;
1458
+ headers["x-embed-nonce"] = nonce;
1459
+ headers["x-embed-body-sha256"] = bodySha256;
1460
+ headers["x-embed-signature"] = createHmac("sha256", token).update(canonical).digest("hex");
1461
+ }
1462
+ function cloudRequestCanonicalString(method, pathWithQuery, timestamp, nonce, bodySha256) {
1463
+ return [
1464
+ method.toUpperCase(),
1465
+ normalizeCloudPathForSignature(pathWithQuery),
1466
+ timestamp,
1467
+ nonce,
1468
+ bodySha256
1469
+ ].join("\n");
1470
+ }
1471
+ function normalizeCloudPathForSignature(pathWithQuery) {
1472
+ try {
1473
+ const parsed = new URL(pathWithQuery, "http://embed.local");
1474
+ return `${parsed.pathname}${parsed.search}`;
1475
+ }
1476
+ catch {
1477
+ return pathWithQuery.startsWith("/") ? pathWithQuery : `/${pathWithQuery}`;
1478
+ }
1479
+ }
1444
1480
  async function pluginList(parsed) {
1445
1481
  const releaseDir = stringFlag(parsed, "release-dir");
1446
1482
  const manifest = releaseDir ? await readPluginReleaseManifest(releaseDir) : undefined;
@@ -1850,6 +1886,7 @@ async function cleanupLegacyCodexPluginRemnants(targetRoot) {
1850
1886
  const removedPaths = [];
1851
1887
  const removedConfigTables = [];
1852
1888
  const warnings = [];
1889
+ const stoppedProcesses = await stopLegacyCodexPluginProcesses(warnings);
1853
1890
  const legacyPaths = [
1854
1891
  join(targetRoot, "dbt-agent"),
1855
1892
  join(targetRoot, "development-board-toolchain"),
@@ -1859,7 +1896,7 @@ async function cleanupLegacyCodexPluginRemnants(targetRoot) {
1859
1896
  ];
1860
1897
  legacyPaths.push(...await discoverLegacyCodexCachePaths(targetRoot));
1861
1898
  if (resolve(targetRoot) === resolve(defaultCodexPluginRoot())) {
1862
- legacyPaths.push(join(defaultCodexHome(), ".tmp", "plugins"), join(defaultCodexHome(), ".tmp", "plugins.sha"));
1899
+ legacyPaths.push(join(defaultCodexHome(), ".tmp", "plugins"), join(defaultCodexHome(), ".tmp", "plugins.sha"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-live-board-ops"), join(defaultCodexHome(), "memories", "skills", "dbt-agent-platform-plugin-maintenance"));
1863
1900
  }
1864
1901
  for (const candidate of legacyPaths) {
1865
1902
  try {
@@ -1888,14 +1925,51 @@ async function cleanupLegacyCodexPluginRemnants(targetRoot) {
1888
1925
  warnings.push(`Could not update ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
1889
1926
  }
1890
1927
  }
1891
- const removedHistoryEntries = await cleanupLegacyCodexHistoryMentions(warnings);
1928
+ const removedHistoryEntries = await cleanupLegacyCodexTextState(warnings);
1892
1929
  return {
1893
1930
  legacy_removed_paths: Array.from(new Set(removedPaths)),
1894
1931
  legacy_removed_config_tables: removedConfigTables,
1895
1932
  legacy_removed_history_entries: removedHistoryEntries,
1933
+ legacy_stopped_processes: stoppedProcesses,
1896
1934
  ...(warnings.length > 0 ? { warnings } : {})
1897
1935
  };
1898
1936
  }
1937
+ async function stopLegacyCodexPluginProcesses(warnings) {
1938
+ if (process.platform === "win32")
1939
+ return 0;
1940
+ try {
1941
+ const ps = await runLocalProcess("ps", ["-axo", "pid=,command="]);
1942
+ if (ps.code !== 0)
1943
+ return 0;
1944
+ let stopped = 0;
1945
+ for (const line of ps.stdout.split("\n")) {
1946
+ const match = /^\s*(\d+)\s+(.+)$/.exec(line);
1947
+ if (!match)
1948
+ continue;
1949
+ const pid = Number(match[1]);
1950
+ const command = match[2] || "";
1951
+ if (!isLegacyCodexPluginProcess(command))
1952
+ continue;
1953
+ try {
1954
+ process.kill(pid, "SIGTERM");
1955
+ stopped += 1;
1956
+ }
1957
+ catch (error) {
1958
+ warnings.push(`Could not stop legacy Codex plugin process ${pid}: ${error instanceof Error ? error.message : String(error)}`);
1959
+ }
1960
+ }
1961
+ return stopped;
1962
+ }
1963
+ catch (error) {
1964
+ warnings.push(`Could not scan legacy Codex plugin processes: ${error instanceof Error ? error.message : String(error)}`);
1965
+ return 0;
1966
+ }
1967
+ }
1968
+ function isLegacyCodexPluginProcess(command) {
1969
+ const trimmed = command.trim();
1970
+ return /^\/.*\/dbt-agent-mcp-bridge(?:\s|$)/.test(trimmed)
1971
+ || /^dbt-agent-mcp-bridge(?:\s|$)/.test(trimmed);
1972
+ }
1899
1973
  async function discoverLegacyCodexCachePaths(targetRoot) {
1900
1974
  const paths = [];
1901
1975
  const cacheRoot = join(targetRoot, "cache");
@@ -1913,12 +1987,18 @@ async function discoverLegacyCodexCachePaths(targetRoot) {
1913
1987
  }
1914
1988
  return paths;
1915
1989
  }
1916
- async function cleanupLegacyCodexHistoryMentions(warnings) {
1917
- const historyPath = join(defaultCodexHome(), "history.jsonl");
1990
+ async function cleanupLegacyCodexTextState(warnings) {
1991
+ let removed = 0;
1992
+ removed += await cleanupLegacyCodexTextFile(join(defaultCodexHome(), "history.jsonl"), warnings);
1993
+ removed += await cleanupLegacyCodexTextFile(join(defaultCodexHome(), "session_index.jsonl"), warnings);
1994
+ removed += await cleanupLegacyCodexTextFile(join(defaultCodexHome(), "rules", "default.rules"), warnings);
1995
+ return removed;
1996
+ }
1997
+ async function cleanupLegacyCodexTextFile(filePath, warnings) {
1918
1998
  try {
1919
- if (!await pathExists(historyPath))
1999
+ if (!await pathExists(filePath))
1920
2000
  return 0;
1921
- const current = await readFile(historyPath, "utf8");
2001
+ const current = await readFile(filePath, "utf8");
1922
2002
  const lines = current.split("\n");
1923
2003
  let removed = 0;
1924
2004
  const kept = lines.filter((line) => {
@@ -1931,12 +2011,12 @@ async function cleanupLegacyCodexHistoryMentions(warnings) {
1931
2011
  return true;
1932
2012
  });
1933
2013
  if (removed > 0) {
1934
- await writeFile(historyPath, kept.join("\n"), "utf8");
2014
+ await writeFile(filePath, kept.join("\n"), "utf8");
1935
2015
  }
1936
2016
  return removed;
1937
2017
  }
1938
2018
  catch (error) {
1939
- warnings.push(`Could not clean Codex legacy history mentions: ${error instanceof Error ? error.message : String(error)}`);
2019
+ warnings.push(`Could not clean Codex legacy text state ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1940
2020
  return 0;
1941
2021
  }
1942
2022
  }
@@ -1985,7 +2065,10 @@ function legacyCodexCleanupWarning(cleanup) {
1985
2065
  parts.push(`removed ${cleanup.legacy_removed_config_tables.length} legacy Codex config table(s)`);
1986
2066
  }
1987
2067
  if (cleanup.legacy_removed_history_entries) {
1988
- parts.push(`removed ${cleanup.legacy_removed_history_entries} legacy Codex history mention(s)`);
2068
+ parts.push(`removed ${cleanup.legacy_removed_history_entries} legacy Codex text-state mention(s)`);
2069
+ }
2070
+ if (cleanup.legacy_stopped_processes) {
2071
+ parts.push(`stopped ${cleanup.legacy_stopped_processes} legacy Codex plugin process(es)`);
1989
2072
  }
1990
2073
  if (cleanup.warnings?.length) {
1991
2074
  parts.push(`cleanup warning(s): ${cleanup.warnings.join("; ")}`);