@prismer/runtime 1.9.9 → 1.9.22

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.cjs CHANGED
@@ -33,7 +33,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
33
33
  // src/cli/ui.ts
34
34
  function thisDirname() {
35
35
  try {
36
- return path8.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
36
+ return path6.dirname((0, import_node_url.fileURLToPath)(import_meta2.url));
37
37
  } catch {
38
38
  return process.cwd();
39
39
  }
@@ -43,18 +43,18 @@ function findIconPath(size = "big") {
43
43
  const here = thisDirname();
44
44
  const candidates = [
45
45
  // npm-installed: node_modules/@prismer/runtime/dist/cli.js → ../assets
46
- path8.resolve(here, "../assets", name),
46
+ path6.resolve(here, "../assets", name),
47
47
  // alternate dist layout (sub-bundle): dist/bin/cli.js → ../../assets
48
- path8.resolve(here, "../../assets", name),
48
+ path6.resolve(here, "../../assets", name),
49
49
  // source/typecheck: src/cli/ui.ts → ../../assets
50
- path8.resolve(here, "../../assets", name),
50
+ path6.resolve(here, "../../assets", name),
51
51
  // dev mode: cwd happens to be runtime root
52
- path8.resolve(process.cwd(), "assets", name),
53
- path8.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
52
+ path6.resolve(process.cwd(), "assets", name),
53
+ path6.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
54
54
  ];
55
55
  for (const candidate of candidates) {
56
56
  try {
57
- if (fs5.existsSync(candidate)) return candidate;
57
+ if (fs4.existsSync(candidate)) return candidate;
58
58
  } catch {
59
59
  }
60
60
  }
@@ -95,13 +95,13 @@ function applyCommonFlags(argv) {
95
95
  }
96
96
  return { mode, color: color2, restArgv: rest };
97
97
  }
98
- var fs5, path8, import_node_url2, import_meta2, BRAILLE_FRAMES, COMPACT_BANNER, UI, _ui;
98
+ var fs4, path6, import_node_url, import_meta2, BRAILLE_FRAMES, COMPACT_BANNER, UI, _ui;
99
99
  var init_ui = __esm({
100
100
  "src/cli/ui.ts"() {
101
101
  "use strict";
102
- fs5 = __toESM(require("fs"), 1);
103
- path8 = __toESM(require("path"), 1);
104
- import_node_url2 = require("url");
102
+ fs4 = __toESM(require("fs"), 1);
103
+ path6 = __toESM(require("path"), 1);
104
+ import_node_url = require("url");
105
105
  import_meta2 = {};
106
106
  BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
107
107
  COMPACT_BANNER = ["\u25C7 PRISMER", " Runtime CLI"];
@@ -182,7 +182,7 @@ var init_ui = __esm({
182
182
  const iconPath = findIconPath("small");
183
183
  if (iconPath !== null) {
184
184
  try {
185
- const raw = fs5.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
185
+ const raw = fs4.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
186
186
  for (const line of raw.split("\n")) {
187
187
  this.write(this.cyan(line) + "\n");
188
188
  }
@@ -205,7 +205,7 @@ var init_ui = __esm({
205
205
  const shouldUseFull = opts?.full === true || width >= 120;
206
206
  if (shouldUseFull && iconPath !== null) {
207
207
  try {
208
- const raw = fs5.readFileSync(iconPath, "utf-8");
208
+ const raw = fs4.readFileSync(iconPath, "utf-8");
209
209
  const lines = raw.split("\n");
210
210
  for (const line of lines) {
211
211
  const brandedLine = line.replace("Prismer Cloud SDK", "Prismer Runtime CLI");
@@ -469,7 +469,7 @@ __export(util_exports, {
469
469
  info: () => info,
470
470
  normalizeCloudUrl: () => normalizeCloudUrl,
471
471
  ok: () => ok,
472
- pidAlive: () => pidAlive2,
472
+ pidAlive: () => pidAlive,
473
473
  pidFilePath: () => pidFilePath,
474
474
  printBanner: () => printBanner,
475
475
  printJson: () => printJson,
@@ -544,10 +544,10 @@ function runAction(fn, opts = {}) {
544
544
  function printBanner(opts = {}) {
545
545
  const ui = getUI();
546
546
  if (opts.compact) {
547
- ui.smallHeader("Runtime CLI v1.9.7");
547
+ ui.smallHeader("Runtime CLI v${VERSION}");
548
548
  return;
549
549
  }
550
- ui.banner("Runtime CLI v1.9.7", { full: true });
550
+ ui.banner("Runtime CLI v${VERSION}", { full: true });
551
551
  }
552
552
  function ok(label, detail) {
553
553
  getUI().ok(label, detail);
@@ -573,29 +573,29 @@ function table(rows, columns) {
573
573
  getUI().table(rows, { columns });
574
574
  }
575
575
  function pidFilePath(paths) {
576
- return (0, import_node_path11.join)(paths.root, "daemon.pid");
576
+ return (0, import_node_path8.join)(paths.root, "daemon.pid");
577
577
  }
578
578
  function writePidFile(paths, pid) {
579
- (0, import_node_fs15.writeFileSync)(pidFilePath(paths), `${pid}
579
+ (0, import_node_fs12.writeFileSync)(pidFilePath(paths), `${pid}
580
580
  `, "utf8");
581
581
  }
582
582
  function readPidFile(paths) {
583
583
  const p = pidFilePath(paths);
584
- if (!(0, import_node_fs15.existsSync)(p)) return void 0;
585
- const raw = (0, import_node_fs15.readFileSync)(p, "utf8").trim();
584
+ if (!(0, import_node_fs12.existsSync)(p)) return void 0;
585
+ const raw = (0, import_node_fs12.readFileSync)(p, "utf8").trim();
586
586
  const pid = Number.parseInt(raw, 10);
587
587
  return Number.isFinite(pid) ? pid : void 0;
588
588
  }
589
589
  function clearPidFile(paths) {
590
590
  const p = pidFilePath(paths);
591
- if ((0, import_node_fs15.existsSync)(p)) {
591
+ if ((0, import_node_fs12.existsSync)(p)) {
592
592
  try {
593
- (0, import_node_fs15.unlinkSync)(p);
593
+ (0, import_node_fs12.unlinkSync)(p);
594
594
  } catch {
595
595
  }
596
596
  }
597
597
  }
598
- function pidAlive2(pid) {
598
+ function pidAlive(pid) {
599
599
  try {
600
600
  process.kill(pid, 0);
601
601
  return true;
@@ -603,12 +603,12 @@ function pidAlive2(pid) {
603
603
  return false;
604
604
  }
605
605
  }
606
- var import_node_fs15, import_node_path11, DEFAULT_CLOUD_BASE_URL, ANSI;
606
+ var import_node_fs12, import_node_path8, DEFAULT_CLOUD_BASE_URL, ANSI;
607
607
  var init_util = __esm({
608
608
  "src/cli/util.ts"() {
609
609
  "use strict";
610
- import_node_fs15 = require("fs");
611
- import_node_path11 = require("path");
610
+ import_node_fs12 = require("fs");
611
+ import_node_path8 = require("path");
612
612
  init_ui();
613
613
  DEFAULT_CLOUD_BASE_URL = "https://prismer.cloud";
614
614
  ANSI = {
@@ -729,35 +729,14 @@ var AdapterRegistry = class {
729
729
  // src/adapters/hermes/index.ts
730
730
  var import_node_child_process = require("child_process");
731
731
  var import_node_fs = require("fs");
732
- var import_node_module = require("module");
733
732
  var import_node_os = require("os");
734
733
  var import_node_path = require("path");
735
- var import_node_url = require("url");
736
734
  var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
737
735
  var YAML = __toESM(require("yaml"), 1);
738
736
  var import_zod = require("zod");
739
737
  var import_meta = {};
740
738
  var PRISMER_IM_SKILL_NAME = "prismer-im-collab";
741
- var PRISMER_IM_SKILL_CONTENT = `---
742
- name: prismer-im-collab
743
- description: Coordinate reliably in Prismer conversations, use workspace assets through bounded MCP tools, and keep task work on the board.
744
- allowed-tools: prismer.agent.send prismer.conversation.listAgents prismer.task.create prismer.task.update prismer.task.complete prismer.approval.request_human_approval prismer.asset.search prismer.asset.describe prismer.asset.read
745
- metadata:
746
- prismer:
747
- version: "1.0.0"
748
- mcp_tools:
749
- - prismer.agent.send
750
- - prismer.conversation.listAgents
751
- - prismer.task.create
752
- - prismer.task.update
753
- - prismer.task.complete
754
- - prismer.approval.request_human_approval
755
- - prismer.asset.search
756
- - prismer.asset.describe
757
- - prismer.asset.read
758
- ---
759
-
760
- # Prismer IM Collaboration
739
+ var PRISMER_IM_SKILL_CONTENT = `# Prismer IM Collaboration
761
740
 
762
741
  You are an agent in a Prismer multi-agent workspace. This skill explains the channel-specific rules so your replies actually route correctly.
763
742
 
@@ -787,19 +766,6 @@ You will receive a \`[Channel context]\` block at the top of each prompt indicat
787
766
  | Address another agent so the platform dispatches your message to them | Call \`prismer.agent.send\` (from the \`prismer-tasks\` MCP server). |
788
767
  | Look up which agents you can talk to | Call \`prismer.conversation.listAgents\` before guessing usernames. |
789
768
  | Create / update tasks on the workspace board | \`prismer.task.create\` / \`prismer.task.update\` from \`prismer-tasks\` MCP. |
790
- | Ask for structured human sign-off | \`prismer.approval.request_human_approval\`. |
791
- | Find uploaded workspace files | \`prismer.asset.search\`. |
792
- | Inspect file metadata before reading bytes | \`prismer.asset.describe\`. |
793
- | Read a bounded byte range from a file | \`prismer.asset.read\`. |
794
-
795
- ## Workspace asset rules
796
-
797
- - Web-uploaded workspace files are available as asset metadata first; bytes are fetched lazily by the local daemon.
798
- - When a task needs file content, call \`prismer.asset.describe\` first, then read only the needed range with \`prismer.asset.read\`.
799
- - For many files, call \`prismer.asset.search\` to find candidates. Do not ask the user to paste file contents already present in workspace assets.
800
- - Do not claim you read a file unless an asset tool call succeeded.
801
- - For large tables/documents, read headers, samples, or targeted byte ranges. Never try to load the whole file into a reply.
802
- - Cite the asset id/URI and locator returned by the tool when using file evidence.
803
769
 
804
770
  ### When to use \`prismer.agent.send\` vs inline \`@\`
805
771
 
@@ -813,7 +779,6 @@ You will receive a \`[Channel context]\` block at the top of each prompt indicat
813
779
  2. **Don't fabricate usernames.** If \`prismer.conversation.listAgents\` doesn't return a name, that agent doesn't exist; use plain text and address them by displayName (no @).
814
780
  3. **Don't pretend to call a tool.** If you write "I sent a message via the tool" without actually invoking the tool, the message doesn't exist; the human will see the inconsistency.
815
781
  4. **Check tool return values.** \`prismer.agent.send\` may return \`{ ok: false, error: 'agent_not_found' }\`; surface the error rather than pretending it worked.
816
- 5. **Use structured approvals.** For destructive, irreversible, external, spend, access, or publish operations, call \`prismer.approval.request_human_approval\`; do not inline-mention a human asking for approval.
817
782
 
818
783
  ## Example: group discussion handoff
819
784
 
@@ -901,23 +866,7 @@ var HermesProfileConfigSchema = import_zod.z.object({
901
866
  hermesSourceDir: import_zod.z.string().optional(),
902
867
  nativeMirrorTimeoutMs: import_zod.z.number().int().positive().default(2e3),
903
868
  /** Task authority level: executor (default) or orchestrator. */
904
- taskAuthority: import_zod.z.enum(["executor", "orchestrator"]).optional().default("executor"),
905
- /** Human approval escalation policy. Enforcement is currently prompt/cloud-side. */
906
- approvalPolicy: import_zod.z.enum(["strict", "auto-low-risk", "autonomous"]).optional().default("auto-low-risk"),
907
- /** Agent-level MCP tool allowlist. Empty/null means all tools. Supports exact names and trailing * wildcards. */
908
- mcpAllowlist: import_zod.z.array(import_zod.z.string()).nullable().optional(),
909
- /** Agent-level operating principles injected through /v1/runs instructions. */
910
- operatingPrinciples: import_zod.z.union([import_zod.z.string(), import_zod.z.record(import_zod.z.string())]).optional(),
911
- /**
912
- * Role-template snapshot carried in AgentProfile.config by cloud ACP.
913
- * Expected fields used here:
914
- * - roleTemplate.mcpServers[].toolsAllowlist
915
- * - roleTemplate.operatingPrinciples
916
- */
917
- roleTemplate: import_zod.z.object({
918
- mcpServers: import_zod.z.array(import_zod.z.object({ toolsAllowlist: import_zod.z.array(import_zod.z.string()).optional() }).passthrough()).optional(),
919
- operatingPrinciples: import_zod.z.union([import_zod.z.string(), import_zod.z.record(import_zod.z.string())]).optional()
920
- }).passthrough().optional()
869
+ taskAuthority: import_zod.z.enum(["executor", "orchestrator"]).optional().default("executor")
921
870
  });
922
871
  var hermesAdapter = {
923
872
  name: "hermes",
@@ -932,22 +881,14 @@ var hermesAdapter = {
932
881
  errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
933
882
  };
934
883
  },
935
- async prepareProfile(profile) {
936
- const config = HermesProfileConfigSchema.parse(profile.config);
937
- if (config.configurePrismerProvider) {
938
- const profileName = getHermesProfileName(profile);
939
- const agentUsername = profile.agentUsername || profileName;
940
- configurePrismerProvider(profileName, config, agentUsername, profile.workspaceId);
941
- }
942
- },
943
884
  async ensureService(profile) {
944
885
  const config = HermesProfileConfigSchema.parse(profile.config);
945
- const profileName = getHermesProfileName(profile);
946
- const portOverride = config.port !== 8642 ? config.port : portForProfile(`${profileName}:${config.apiKey}`, 8642);
886
+ const profileName = config.hermesProfileName && config.hermesProfileName !== "default" ? config.hermesProfileName : profile.agentUsername || profile.id.slice(0, 8);
887
+ const portOverride = config.port !== 8642 ? config.port : portForProfile(profileName, 8642);
947
888
  const baseUrl = `http://127.0.0.1:${portOverride}`;
948
889
  const agentUsername = profile.agentUsername || profileName;
949
890
  if (config.configurePrismerProvider) {
950
- configurePrismerProvider(profileName, config, agentUsername, profile.workspaceId);
891
+ configurePrismerProvider(profileName, config, agentUsername);
951
892
  }
952
893
  if (!await checkHealth(baseUrl, config.apiKey)) {
953
894
  if (!config.autoStart) {
@@ -957,7 +898,6 @@ var hermesAdapter = {
957
898
  (set autoStart: true on the AgentProfile to spawn it automatically)`
958
899
  );
959
900
  }
960
- await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
961
901
  (0, import_node_child_process.spawn)("hermes", ["-p", profileName, "gateway", "run"], {
962
902
  detached: false,
963
903
  stdio: "ignore",
@@ -1017,8 +957,6 @@ var HermesService = class {
1017
957
  const startedAt = Date.now();
1018
958
  let runId;
1019
959
  const sysPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
1020
- const operatingPrinciples = typeof task.metadata?.operatingPrinciples === "string" ? task.metadata.operatingPrinciples : void 0;
1021
- const instructions = [sysPrompt, operatingPrinciples].filter(Boolean).join("\n\n");
1022
960
  if (sysPrompt) {
1023
961
  try {
1024
962
  const profileDir = getHermesProfileDir(this.profileName);
@@ -1055,7 +993,7 @@ var HermesService = class {
1055
993
  // sent `messages: [...]` which Hermes 0.12 rejects with 400.
1056
994
  body: JSON.stringify({
1057
995
  input: task.prompt,
1058
- instructions: instructions || void 0,
996
+ instructions: sysPrompt,
1059
997
  session_id: typeof task.metadata?.conversationId === "string" ? task.metadata.conversationId : void 0,
1060
998
  stream: true
1061
999
  }),
@@ -1340,7 +1278,7 @@ function formatHermesGoalText(goals) {
1340
1278
  return `${index + 1}. ${priority}${goal.title}${description}`;
1341
1279
  }).join("\n");
1342
1280
  }
1343
- function configurePrismerProvider(profileName, config, agentUsername, workspaceId) {
1281
+ function configurePrismerProvider(profileName, config, agentUsername) {
1344
1282
  const baseUrl = resolvePrismerProviderBaseUrl(config);
1345
1283
  const apiKey = resolvePrismerApiKey(config);
1346
1284
  if (!baseUrl || !apiKey) {
@@ -1383,16 +1321,9 @@ function configurePrismerProvider(profileName, config, agentUsername, workspaceI
1383
1321
  PRISMER_API_KEY: apiKey,
1384
1322
  PRISMER_BASE_URL: baseUrl.replace(/\/api\/v1\/?$/, "")
1385
1323
  };
1386
- if (workspaceId) {
1387
- mcpEnv.PRISMER_WORKSPACE_ID = workspaceId;
1388
- }
1389
1324
  if (agentUsername) {
1390
1325
  mcpEnv.PRISMER_AGENT_USERNAME = agentUsername;
1391
1326
  }
1392
- const allowlist = resolveMcpAllowlist(config);
1393
- if (allowlist) {
1394
- mcpEnv.PRISMER_MCP_ALLOWLIST = allowlist.join(",");
1395
- }
1396
1327
  mcpServers["prismer-tasks"] = {
1397
1328
  command: "node",
1398
1329
  args: [serverPath],
@@ -1413,17 +1344,20 @@ function resolvePrismerMcpServerPath(config) {
1413
1344
  if (config.prismerMcpServerPath) return config.prismerMcpServerPath;
1414
1345
  if (process.env.PRISMER_MCP_SERVER) return process.env.PRISMER_MCP_SERVER;
1415
1346
  try {
1416
- const req = (0, import_node_module.createRequire)(import_meta.url);
1347
+ const { createRequire } = require("module");
1348
+ const req = createRequire(import_meta.url);
1417
1349
  return req.resolve("@prismer/mcp-server");
1418
1350
  } catch {
1419
1351
  }
1420
1352
  try {
1421
- const here = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
1422
- const candidate = (0, import_node_path.join)(here, "../../mcp/dist/index.js");
1353
+ const { fileURLToPath: fileURLToPath2 } = require("url");
1354
+ const { join: join15, dirname: dirname8 } = require("path");
1355
+ const here = dirname8(fileURLToPath2(import_meta.url));
1356
+ const candidate = join15(here, "../../mcp/dist/index.js");
1423
1357
  if ((0, import_node_fs.existsSync)(candidate)) return candidate;
1424
- const candidate2 = (0, import_node_path.join)(here, "../../../mcp/dist/index.js");
1358
+ const candidate2 = join15(here, "../../../mcp/dist/index.js");
1425
1359
  if ((0, import_node_fs.existsSync)(candidate2)) return candidate2;
1426
- const candidate3 = (0, import_node_path.join)(here, "../../../../mcp/dist/index.js");
1360
+ const candidate3 = join15(here, "../../../../mcp/dist/index.js");
1427
1361
  if ((0, import_node_fs.existsSync)(candidate3)) return candidate3;
1428
1362
  } catch {
1429
1363
  }
@@ -1592,29 +1526,25 @@ function runPythonJson(args, env, timeoutMs) {
1592
1526
  });
1593
1527
  });
1594
1528
  }
1595
- function readHermesConfig(path9) {
1596
- if (!(0, import_node_fs.existsSync)(path9)) return {};
1529
+ function readHermesConfig(path7) {
1530
+ if (!(0, import_node_fs.existsSync)(path7)) return {};
1597
1531
  try {
1598
- return YAML.parse((0, import_node_fs.readFileSync)(path9, "utf8")) ?? {};
1532
+ return YAML.parse((0, import_node_fs.readFileSync)(path7, "utf8")) ?? {};
1599
1533
  } catch (err) {
1600
- throw new Error(`Failed to parse Hermes config at ${path9}: ${err.message}`);
1534
+ throw new Error(`Failed to parse Hermes config at ${path7}: ${err.message}`);
1601
1535
  }
1602
1536
  }
1603
- function writeEnvValue(path9, key, value) {
1604
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path9), { recursive: true });
1605
- const lines = (0, import_node_fs.existsSync)(path9) ? (0, import_node_fs.readFileSync)(path9, "utf8").split(/\r?\n/) : [];
1537
+ function writeEnvValue(path7, key, value) {
1538
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path7), { recursive: true });
1539
+ const lines = (0, import_node_fs.existsSync)(path7) ? (0, import_node_fs.readFileSync)(path7, "utf8").split(/\r?\n/) : [];
1606
1540
  const next = lines.filter((line) => !line.startsWith(`${key}=`) && line.trim() !== "");
1607
1541
  next.push(`${key}=${quoteEnv(value)}`);
1608
- (0, import_node_fs.writeFileSync)(path9, `${next.join("\n")}
1542
+ (0, import_node_fs.writeFileSync)(path7, `${next.join("\n")}
1609
1543
  `, "utf8");
1610
1544
  }
1611
1545
  function quoteEnv(value) {
1612
1546
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1613
1547
  }
1614
- function getHermesProfileName(profile) {
1615
- const configured = profile.config && typeof profile.config.hermesProfileName === "string" ? profile.config.hermesProfileName : void 0;
1616
- return configured && configured !== "default" ? configured : profile.agentUsername || profile.id.slice(0, 8);
1617
- }
1618
1548
  function getHermesProfileDir(profileName) {
1619
1549
  const root = process.env.HERMES_HOME || (0, import_node_path.join)((0, import_node_os.homedir)(), ".hermes");
1620
1550
  if (!profileName || profileName === "default") return root;
@@ -1636,18 +1566,6 @@ function resolvePrismerProviderBaseUrl(config) {
1636
1566
  function resolvePrismerApiKey(config) {
1637
1567
  return process.env[config.prismerApiKeyEnv] || process.env.PRISMER_API_KEY || "";
1638
1568
  }
1639
- function resolveMcpAllowlist(config) {
1640
- if (Array.isArray(config.mcpAllowlist)) return normalizeAllowlist(config.mcpAllowlist);
1641
- const prismerServer = config.roleTemplate?.mcpServers?.find((server) => {
1642
- const record = server;
1643
- return record.package === "@prismer/mcp-server" || record.name === "prismer-tasks";
1644
- });
1645
- if (!prismerServer) return null;
1646
- return Array.isArray(prismerServer.toolsAllowlist) ? normalizeAllowlist(prismerServer.toolsAllowlist) : null;
1647
- }
1648
- function normalizeAllowlist(values) {
1649
- return values.map((value) => value.trim()).filter(Boolean);
1650
- }
1651
1569
  function normalizeProviderName(value) {
1652
1570
  return value.trim().toLowerCase().replace(/\s+/g, "-");
1653
1571
  }
@@ -1753,66 +1671,6 @@ async function waitForHealthy(baseUrl, apiKey, timeoutMs) {
1753
1671
  }
1754
1672
  throw new Error(`Hermes did not become healthy at ${baseUrl} within ${timeoutMs}ms`);
1755
1673
  }
1756
- async function stopStaleHermesGateways(profileName, port, timeoutMs) {
1757
- const pids = findHermesGatewayPids(profileName, port);
1758
- for (const pid of pids) {
1759
- try {
1760
- process.kill(pid, "SIGTERM");
1761
- } catch {
1762
- continue;
1763
- }
1764
- }
1765
- const deadline = Date.now() + Math.min(timeoutMs, 5e3);
1766
- while (Date.now() < deadline) {
1767
- const alive = pids.filter(pidAlive);
1768
- if (alive.length === 0) return;
1769
- await new Promise((r) => setTimeout(r, 100));
1770
- }
1771
- for (const pid of pids) {
1772
- if (!pidAlive(pid)) continue;
1773
- try {
1774
- process.kill(pid, "SIGKILL");
1775
- } catch {
1776
- }
1777
- }
1778
- }
1779
- function findHermesGatewayPids(profileName, port) {
1780
- try {
1781
- const out = (0, import_node_child_process.execFileSync)("ps", ["-axo", "pid=,command="], { encoding: "utf8" });
1782
- const current = process.pid;
1783
- const escapedProfile = escapeRegExp(profileName);
1784
- const profilePattern = new RegExp(`(?:^|\\s)-p\\s+${escapedProfile}(?:\\s|$)`);
1785
- const portPattern = new RegExp(`(?:^|\\s)API_SERVER_PORT=${port}(?:\\s|$)`);
1786
- return out.split(/\r?\n/).map((line) => {
1787
- const trimmed = line.trim();
1788
- const match = /^(\d+)\s+(.+)$/.exec(trimmed);
1789
- if (!match) return null;
1790
- const pid = Number.parseInt(match[1], 10);
1791
- const command = match[2];
1792
- if (!Number.isFinite(pid) || pid === current) return null;
1793
- if (!isHermesGatewayCommand(command)) return null;
1794
- if (!profilePattern.test(command) && !portPattern.test(command)) return null;
1795
- return pid;
1796
- }).filter((pid) => pid !== null);
1797
- } catch {
1798
- return [];
1799
- }
1800
- }
1801
- function isHermesGatewayCommand(command) {
1802
- if (!/\bgateway\b/.test(command) || !/\brun\b/.test(command)) return false;
1803
- return /(^|\s)(?:\S*\/)?hermes(?:\s|$)/.test(command);
1804
- }
1805
- function pidAlive(pid) {
1806
- try {
1807
- process.kill(pid, 0);
1808
- return true;
1809
- } catch {
1810
- return false;
1811
- }
1812
- }
1813
- function escapeRegExp(value) {
1814
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1815
- }
1816
1674
 
1817
1675
  // src/daemon/ws-client.ts
1818
1676
  var import_node_events = require("events");
@@ -2052,11 +1910,11 @@ var MIGRATIONS = [
2052
1910
  function runSql(db, sql) {
2053
1911
  db["exec"](sql);
2054
1912
  }
2055
- function openLocalDb(path9) {
2056
- if (path9 !== ":memory:" && !(0, import_node_fs2.existsSync)((0, import_node_path2.dirname)(path9))) {
2057
- (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(path9), { recursive: true });
1913
+ function openLocalDb(path7) {
1914
+ if (path7 !== ":memory:" && !(0, import_node_fs2.existsSync)((0, import_node_path2.dirname)(path7))) {
1915
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(path7), { recursive: true });
2058
1916
  }
2059
- const db = new import_better_sqlite32.default(path9);
1917
+ const db = new import_better_sqlite32.default(path7);
2060
1918
  db.pragma("journal_mode = WAL");
2061
1919
  db.pragma("foreign_keys = ON");
2062
1920
  runMigrations(db);
@@ -2374,12 +2232,11 @@ function deriveWsUrl(httpBase) {
2374
2232
  }
2375
2233
 
2376
2234
  // src/daemon-id.ts
2377
- var import_node_crypto = require("crypto");
2378
2235
  var import_node_os3 = require("os");
2379
2236
  var PREFIX = "daemon-";
2380
2237
  function newDaemonId() {
2381
2238
  const host = (0, import_node_os3.hostname)().replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unknown";
2382
- return `${PREFIX}${host}-${(0, import_node_crypto.randomBytes)(6).toString("hex")}`;
2239
+ return `${PREFIX}${host}`;
2383
2240
  }
2384
2241
  function isDaemonId(s) {
2385
2242
  return s.startsWith(PREFIX) && s.length > PREFIX.length;
@@ -2394,8 +2251,8 @@ var CloudClient = class {
2394
2251
  opts;
2395
2252
  fetchImpl;
2396
2253
  /** Low-level request. Caller decides on retry; sync-worker handles backoff. */
2397
- async request(method, path9, init) {
2398
- const url = this.urlFor(path9);
2254
+ async request(method, path7, init) {
2255
+ const url = this.urlFor(path7);
2399
2256
  const headers = {
2400
2257
  "Content-Type": "application/json",
2401
2258
  Accept: "application/json",
@@ -2454,8 +2311,8 @@ var CloudClient = class {
2454
2311
  return { ok: true, status: res.status, data: body ?? void 0 };
2455
2312
  }
2456
2313
  /** Convenience: GET expecting `{ ok, data }` envelope. Returns `data` or throws. */
2457
- async get(path9, opts) {
2458
- const res = await this.request("GET", path9, opts);
2314
+ async get(path7, opts) {
2315
+ const res = await this.request("GET", path7, opts);
2459
2316
  if (!res.ok) throw new CloudError(res.status, res.error?.code ?? "unknown", res.error?.message ?? "request failed");
2460
2317
  if (res.data && typeof res.data === "object" && "ok" in res.data) {
2461
2318
  const env = res.data;
@@ -2464,14 +2321,14 @@ var CloudClient = class {
2464
2321
  }
2465
2322
  return res.data;
2466
2323
  }
2467
- urlFor(path9) {
2324
+ urlFor(path7) {
2468
2325
  const trimmedBase = this.opts.baseUrl.replace(/\/$/, "");
2469
- const trimmedPath = path9.startsWith("/") ? path9 : `/${path9}`;
2326
+ const trimmedPath = path7.startsWith("/") ? path7 : `/${path7}`;
2470
2327
  return `${trimmedBase}${trimmedPath}`;
2471
2328
  }
2472
2329
  /** Raw byte download (for asset cache). Returns response so caller can stream body. */
2473
- async fetchRaw(path9, init) {
2474
- const url = this.urlFor(path9);
2330
+ async fetchRaw(path7, init) {
2331
+ const url = this.urlFor(path7);
2475
2332
  const headers = {
2476
2333
  Authorization: `Bearer ${this.opts.apiKey}`,
2477
2334
  ...init?.headers ?? {}
@@ -2520,7 +2377,6 @@ function envelope(type, payload, requestId) {
2520
2377
  }
2521
2378
 
2522
2379
  // src/asset-cache.ts
2523
- var import_node_crypto2 = require("crypto");
2524
2380
  var import_node_fs4 = require("fs");
2525
2381
  var import_node_path4 = require("path");
2526
2382
  function rowToCached(row) {
@@ -2614,17 +2470,11 @@ var AssetCache = class {
2614
2470
  throw new Error(`Asset bytes fetch failed for ${hash} (id=${assetId}): HTTP ${res.status}`);
2615
2471
  }
2616
2472
  const buf = Buffer.from(await res.arrayBuffer());
2617
- const actualHash = sha256(buf);
2618
- if (actualHash !== hash) {
2619
- throw new Error(`Asset hash mismatch for ${hash}: downloaded ${actualHash}`);
2620
- }
2621
2473
  const localPath = this.pathFor(hash);
2622
2474
  if (!(0, import_node_fs4.existsSync)((0, import_node_path4.dirname)(localPath))) {
2623
2475
  (0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(localPath), { recursive: true });
2624
2476
  }
2625
- const tmpPath = `${localPath}.${process.pid}.${Date.now()}.tmp`;
2626
- (0, import_node_fs4.writeFileSync)(tmpPath, buf);
2627
- (0, import_node_fs4.renameSync)(tmpPath, localPath);
2477
+ (0, import_node_fs4.writeFileSync)(localPath, buf);
2628
2478
  const mime = res.headers.get("content-type");
2629
2479
  const now = Date.now();
2630
2480
  this.db.prepare(
@@ -2645,10 +2495,6 @@ var AssetCache = class {
2645
2495
  }
2646
2496
  /** Insert an already-on-disk asset into the cache (e.g., asset just produced by adapter). */
2647
2497
  registerLocal(hash, localPath, mime) {
2648
- const actualHash = sha256(readFileBuffer(localPath));
2649
- if (actualHash !== hash) {
2650
- throw new Error(`Asset hash mismatch for ${hash}: local file is ${actualHash}`);
2651
- }
2652
2498
  const size = (0, import_node_fs4.statSync)(localPath).size;
2653
2499
  const now = Date.now();
2654
2500
  this.db.prepare(
@@ -2688,12 +2534,6 @@ var AssetCache = class {
2688
2534
  return { removed, freed };
2689
2535
  }
2690
2536
  };
2691
- function sha256(buf) {
2692
- return (0, import_node_crypto2.createHash)("sha256").update(buf).digest("hex");
2693
- }
2694
- function readFileBuffer(path9) {
2695
- return (0, import_node_fs4.readFileSync)(path9);
2696
- }
2697
2537
 
2698
2538
  // src/daemon/asset/mirror.ts
2699
2539
  var import_node_fs5 = require("fs");
@@ -2790,8 +2630,8 @@ var WorkspaceMirror = class {
2790
2630
  return { applied, cursor: envelope2.cursor };
2791
2631
  }
2792
2632
  /** Lookup the active binding for a workspace path. Returns undefined if not mirrored. */
2793
- lookupByPath(path9) {
2794
- const row = this.db.prepare("SELECT * FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?").get(this.workspaceId, path9);
2633
+ lookupByPath(path7) {
2634
+ const row = this.db.prepare("SELECT * FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?").get(this.workspaceId, path7);
2795
2635
  return row ? rowToBinding(row) : void 0;
2796
2636
  }
2797
2637
  /** Find an asset's contentHash via any path binding (asset may map to many paths). */
@@ -3053,20 +2893,20 @@ var UriResolver = class {
3053
2893
  }
3054
2894
  return { texts: all, resolvedHashes: dedupe(allHashes) };
3055
2895
  }
3056
- async lookupFile(workspaceId, path9, signal) {
2896
+ async lookupFile(workspaceId, path7, signal) {
3057
2897
  const local = this.db.prepare(
3058
2898
  "SELECT asset_id, content_hash, version FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?"
3059
- ).get(workspaceId, path9);
2899
+ ).get(workspaceId, path7);
3060
2900
  if (local) return local;
3061
2901
  const remote = await this.cloud.get(
3062
- `/api/im/workspaces/${encodeURIComponent(workspaceId)}/files?path=${encodeURIComponent(path9)}`,
2902
+ `/api/im/workspaces/${encodeURIComponent(workspaceId)}/files?path=${encodeURIComponent(path7)}`,
3063
2903
  { signal }
3064
2904
  );
3065
2905
  this.db.prepare(
3066
2906
  `INSERT OR REPLACE INTO workspace_files_mirror
3067
2907
  (workspace_id, path, asset_id, content_hash, version, synced_at, dirty)
3068
2908
  VALUES (?, ?, ?, ?, ?, ?, 0)`
3069
- ).run(workspaceId, path9, remote.assetId, remote.contentHash, remote.version, Date.now());
2909
+ ).run(workspaceId, path7, remote.assetId, remote.contentHash, remote.version, Date.now());
3070
2910
  return { asset_id: remote.assetId, content_hash: remote.contentHash, version: remote.version };
3071
2911
  }
3072
2912
  };
@@ -3075,244 +2915,11 @@ function dedupe(xs) {
3075
2915
  }
3076
2916
 
3077
2917
  // src/daemon/dispatch.ts
3078
- var import_node_fs7 = require("fs");
3079
- var path = __toESM(require("path"), 1);
3080
-
3081
- // src/daemon/skill-sync.ts
3082
2918
  var import_node_fs6 = require("fs");
3083
- var import_node_path7 = require("path");
3084
- var import_node_crypto3 = require("crypto");
3085
-
3086
- // src/adapters/openclaw/index.ts
3087
- var import_zod3 = require("zod");
3088
- var import_node_os4 = require("os");
3089
- var import_node_path6 = require("path");
3090
- var OpenClawProfileConfigSchema = import_zod3.z.object({
3091
- /** OpenClaw chat-completions HTTP port. */
3092
- port: import_zod3.z.number().int().min(1).max(65535).default(18789),
3093
- /** Bearer token expected by the gateway (from gateway.auth.bearerTokens). */
3094
- apiKey: import_zod3.z.string().min(1),
3095
- /**
3096
- * Model field passed to OpenClaw's /v1/chat/completions. OpenClaw
3097
- * intercepts this differently from a vanilla OpenAI server: the value
3098
- * MUST be `openclaw` (or `openclaw/<agentId>`) — it picks which
3099
- * OpenClaw agent answers, NOT the underlying LLM. The LLM is selected
3100
- * by `agents.defaults.model.primary` in openclaw.json (e.g.
3101
- * `prismer/us-kimi-k2.5`, set by the bootstrap).
3102
- */
3103
- model: import_zod3.z.string().min(1).default("openclaw"),
3104
- mcpAllowlist: import_zod3.z.array(import_zod3.z.string()).nullable().optional(),
3105
- approvalPolicy: import_zod3.z.enum(["strict", "auto-low-risk", "autonomous"]).optional().default("auto-low-risk"),
3106
- roleTemplate: import_zod3.z.object({
3107
- mcpServers: import_zod3.z.array(import_zod3.z.object({ toolsAllowlist: import_zod3.z.array(import_zod3.z.string()).optional() }).passthrough()).optional()
3108
- }).passthrough().optional(),
3109
- skillsDir: import_zod3.z.string().optional()
3110
- });
3111
- var openclawAdapter = {
3112
- name: "openclaw",
3113
- kind: "long-running",
3114
- capabilities: ["chat", "code", "multi-channel"],
3115
- workspaceSchema: OpenClawProfileConfigSchema,
3116
- validate(config) {
3117
- const r = OpenClawProfileConfigSchema.safeParse(config);
3118
- if (r.success) return { ok: true };
3119
- return {
3120
- ok: false,
3121
- errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
3122
- };
3123
- },
3124
- async ensureService(profile) {
3125
- const config = OpenClawProfileConfigSchema.parse(profile.config);
3126
- const baseUrl = `http://127.0.0.1:${config.port}`;
3127
- if (!await checkHealth2(baseUrl, config.apiKey)) {
3128
- throw new Error(
3129
- `OpenClaw gateway not reachable at ${baseUrl}. Start it with:
3130
- openclaw gateway --port ${config.port}`
3131
- );
3132
- }
3133
- return new OpenClawService(baseUrl, config.apiKey, config.model, resolveOpenClawSkillsDir(profile, config));
3134
- },
3135
- async health() {
3136
- return { available: true };
3137
- }
3138
- };
3139
- var OpenClawService = class {
3140
- constructor(baseUrl, apiKey, model, skillsDir) {
3141
- this.baseUrl = baseUrl;
3142
- this.apiKey = apiKey;
3143
- this.model = model;
3144
- this.skillsDir = skillsDir;
3145
- this.id = baseUrl;
3146
- }
3147
- baseUrl;
3148
- apiKey;
3149
- model;
3150
- skillsDir;
3151
- id;
3152
- async healthy() {
3153
- return checkHealth2(this.baseUrl, this.apiKey);
3154
- }
3155
- async dispatch(task) {
3156
- const startedAt = Date.now();
3157
- const systemPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
3158
- const messages = [];
3159
- if (systemPrompt) messages.push({ role: "system", content: systemPrompt });
3160
- messages.push({ role: "user", content: task.prompt });
3161
- try {
3162
- const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
3163
- method: "POST",
3164
- headers: {
3165
- Authorization: `Bearer ${this.apiKey}`,
3166
- "Content-Type": "application/json"
3167
- },
3168
- body: JSON.stringify({
3169
- model: this.model,
3170
- messages,
3171
- stream: false
3172
- }),
3173
- signal: task.signal
3174
- });
3175
- if (!res.ok) {
3176
- const body = await res.text().catch(() => "") ?? "";
3177
- return {
3178
- ok: false,
3179
- error: {
3180
- code: res.status === 401 || res.status === 403 ? "auth_invalid" : "adapter_dispatch_failed",
3181
- message: `OpenClaw ${res.status}: ${body.slice(0, 400)}`
3182
- }
3183
- };
3184
- }
3185
- const json = await res.json();
3186
- let output = json.choices?.[0]?.message?.content ?? "";
3187
- const ERR_PREFIX_RE = /^Cannot read properties of undefined \(reading '[^']+'\)\s*\n+/;
3188
- if (ERR_PREFIX_RE.test(output)) {
3189
- process.stderr.write(
3190
- `[openclaw-adapter] stripping known stderr-bleed prefix from chat output (OpenClaw 2026.4.x bug)
3191
- `
3192
- );
3193
- output = output.replace(ERR_PREFIX_RE, "");
3194
- }
3195
- return {
3196
- ok: true,
3197
- output,
3198
- metrics: { durationMs: Date.now() - startedAt }
3199
- };
3200
- } catch (err) {
3201
- if (task.signal?.aborted || err?.name === "AbortError") {
3202
- return { ok: false, error: { code: "task_cancelled", message: "Task cancelled by client" } };
3203
- }
3204
- return {
3205
- ok: false,
3206
- error: { code: "adapter_dispatch_failed", message: err.message }
3207
- };
3208
- }
3209
- }
3210
- };
3211
- function getOpenClawSkillsDir(profile) {
3212
- const config = OpenClawProfileConfigSchema.parse(profile.config);
3213
- return resolveOpenClawSkillsDir(profile, config);
3214
- }
3215
- function resolveOpenClawSkillsDir(profile, config) {
3216
- if (config.skillsDir) return config.skillsDir;
3217
- const name = profile.agentUsername || profile.id.slice(0, 8);
3218
- return (0, import_node_path6.join)(process.env.OPENCLAW_HOME || (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".openclaw"), "profiles", name, "skills");
3219
- }
3220
- async function checkHealth2(baseUrl, apiKey) {
3221
- try {
3222
- const res = await fetch(`${baseUrl}/health`, {
3223
- headers: { Authorization: `Bearer ${apiKey}` },
3224
- signal: AbortSignal.timeout(2e3)
3225
- });
3226
- return res.ok;
3227
- } catch {
3228
- return false;
3229
- }
3230
- }
3231
-
3232
- // src/daemon/skill-sync.ts
3233
- async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
3234
- if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
3235
- const data = await cloud.get(
3236
- `/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
3237
- { signal }
3238
- );
3239
- const entries = normalizeInstalledSkills(data);
3240
- const skillsRoot = resolveSkillsRoot(profile);
3241
- if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
3242
- let synced = 0;
3243
- let skipped = 0;
3244
- let unchanged = 0;
3245
- for (const entry of entries) {
3246
- const slug = sanitizeSlug(trimmedStringFrom(entry.skill?.slug) ?? trimmedStringFrom(entry.slug));
3247
- const content = contentStringFrom(entry.skill?.content) ?? contentStringFrom(entry.content);
3248
- if (!slug || !content) {
3249
- skipped++;
3250
- if (slug && !content) {
3251
- process.stderr.write(`[daemon] skill sync skipped ${slug}: missing content
3252
- `);
3253
- }
3254
- continue;
3255
- }
3256
- const skillDir = (0, import_node_path7.join)(skillsRoot, slug);
3257
- await import_node_fs6.promises.mkdir(skillDir, { recursive: true });
3258
- const target = (0, import_node_path7.join)(skillDir, "SKILL.md");
3259
- const existing = await import_node_fs6.promises.readFile(target, "utf8").catch(() => null);
3260
- if (existing != null && sha2562(existing) === sha2562(content)) {
3261
- unchanged++;
3262
- continue;
3263
- }
3264
- await import_node_fs6.promises.writeFile(target, content, "utf8");
3265
- synced++;
3266
- }
3267
- return { synced, skipped, unchanged };
3268
- }
3269
- function resolveSkillsRoot(profile) {
3270
- if (profile.adapterName === "hermes") return (0, import_node_path7.join)(getHermesProfileDir(getHermesProfileName(profile)), "skills");
3271
- if (profile.adapterName === "openclaw") return getOpenClawSkillsDir(profile);
3272
- return null;
3273
- }
3274
- function sha2562(value) {
3275
- return (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
3276
- }
3277
- function normalizeInstalledSkills(data) {
3278
- if (Array.isArray(data)) return data.filter(isInstalledSkillEntry);
3279
- if (!data || typeof data !== "object" || Array.isArray(data)) return [];
3280
- const record = data;
3281
- const nested = record.data;
3282
- if (Array.isArray(nested)) return nested.filter(isInstalledSkillEntry);
3283
- if (nested && typeof nested === "object" && !Array.isArray(nested)) {
3284
- const skills2 = nested.skills;
3285
- if (Array.isArray(skills2)) return skills2.filter(isInstalledSkillEntry);
3286
- }
3287
- const skills = record.skills;
3288
- if (Array.isArray(skills)) return skills.filter(isInstalledSkillEntry);
3289
- return [];
3290
- }
3291
- function isInstalledSkillEntry(value) {
3292
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
3293
- }
3294
- function trimmedStringFrom(value) {
3295
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
3296
- }
3297
- function contentStringFrom(value) {
3298
- return typeof value === "string" && value.trim() ? value : void 0;
3299
- }
3300
- function sanitizeSlug(value) {
3301
- if (!value) return void 0;
3302
- const safe = value.replace(/[^a-zA-Z0-9_-]/g, "");
3303
- return safe || void 0;
3304
- }
3305
-
3306
- // src/daemon/dispatch.ts
2919
+ var path = __toESM(require("path"), 1);
3307
2920
  var DEFAULT_CONTEXT_MAX = 8e3;
3308
2921
  var GOAL_CONTEXT_MAX = 4;
3309
2922
  var MEMORY_DIGEST_MAX_BYTES = 4e3;
3310
- var DEFAULT_OPERATING_PRINCIPLES = [
3311
- "- Decide and act; only request human approval for destructive operations or irreversible external effects.",
3312
- "- Assignable work should become explicit tasks; do not leave concrete work as chat discussion only.",
3313
- "- Use workspace asset tools for uploaded files: search/describe first, then read bounded ranges; do not claim file contents unless a tool call succeeded.",
3314
- "- Keep users informed with concise status and concrete next steps."
3315
- ].join("\n");
3316
2923
  var ASSET_INLINE_FULL_MAX_BYTES = 64 * 1024;
3317
2924
  var ASSET_INLINE_TRUNCATE_BYTES = 16 * 1024;
3318
2925
  async function handleDispatch(payload, requestId, deps) {
@@ -3323,7 +2930,7 @@ async function handleDispatch(payload, requestId, deps) {
3323
2930
  const outboxDir = outboxBase ? path.join(outboxBase, "_outbox") : null;
3324
2931
  if (outboxDir) {
3325
2932
  try {
3326
- await import_node_fs7.promises.mkdir(outboxDir, { recursive: true });
2933
+ await import_node_fs6.promises.mkdir(outboxDir, { recursive: true });
3327
2934
  } catch (err) {
3328
2935
  process.stderr.write(
3329
2936
  `[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
@@ -3350,18 +2957,6 @@ async function handleDispatch(payload, requestId, deps) {
3350
2957
  sendReply(deps.ws, reply, requestId);
3351
2958
  return reply;
3352
2959
  }
3353
- try {
3354
- const skillSync = await syncInstalledSkillsForDispatch(profile, agentImUserId, deps.cloud, deps.signal);
3355
- if (skillSync.synced > 0 || skillSync.skipped > 0) {
3356
- process.stderr.write(
3357
- `[daemon] skill sync profile=${profile.id} adapter=${profile.adapterName} synced=${skillSync.synced} skipped=${skillSync.skipped}
3358
- `
3359
- );
3360
- }
3361
- } catch (err) {
3362
- process.stderr.write(`[daemon] skill sync skipped profile=${profile.id}: ${err.message}
3363
- `);
3364
- }
3365
2960
  let hashRefResult = { text: payload.prompt, resolutions: [] };
3366
2961
  if (deps.assetMetadataIndexes && profile.workspaceId) {
3367
2962
  const assetIndex = deps.assetMetadataIndexes.get(profile.workspaceId);
@@ -3411,7 +3006,6 @@ async function handleDispatch(payload, requestId, deps) {
3411
3006
  `
3412
3007
  );
3413
3008
  }
3414
- const operatingPrinciples = resolveOperatingPrinciples(profile.config);
3415
3009
  const taskInput = {
3416
3010
  taskId,
3417
3011
  prompt: adapterPrompt,
@@ -3420,7 +3014,6 @@ async function handleDispatch(payload, requestId, deps) {
3420
3014
  conversationId: payload.conversationId,
3421
3015
  prismerGoals: goalContext.map(toGoalMirrorPayload),
3422
3016
  ...profileSystemPrompt ? { systemPrompt: profileSystemPrompt } : {},
3423
- ...operatingPrinciples ? { operatingPrinciples } : {},
3424
3017
  // Wave-9: spawn-style adapters (claude-code, codex, openclaw) read
3425
3018
  // this and inject `PRISMER_OUTBOX_DIR=<abs>` into the child env so
3426
3019
  // the LLM tool can resolve the path even if it doesn't read the
@@ -3691,7 +3284,7 @@ async function resolveAssetRefs(refs, cache) {
3691
3284
  const inlineCandidate = isTextLikeMime(effectiveMime);
3692
3285
  if (inlineCandidate) {
3693
3286
  try {
3694
- const buf = (0, import_node_fs7.readFileSync)(cached.localPath);
3287
+ const buf = (0, import_node_fs6.readFileSync)(cached.localPath);
3695
3288
  let strategy;
3696
3289
  let body;
3697
3290
  if (buf.byteLength <= ASSET_INLINE_FULL_MAX_BYTES) {
@@ -3765,36 +3358,6 @@ ${memory.digest.trim()}
3765
3358
 
3766
3359
  ${prompt}`;
3767
3360
  }
3768
- function resolveOperatingPrinciples(config) {
3769
- const cfg = readRecord(config);
3770
- const roleTemplate = readRecord(cfg.roleTemplate);
3771
- const principles = stringifyPrinciples(cfg.operatingPrinciples) ?? stringifyPrinciples(roleTemplate.operatingPrinciples) ?? DEFAULT_OPERATING_PRINCIPLES;
3772
- const policy = typeof cfg.approvalPolicy === "string" ? cfg.approvalPolicy : roleTemplate.approvalPolicy;
3773
- if (policy === "strict") {
3774
- return `${principles}
3775
- - Approval policy: strict. Request human approval before destructive, external, spend, access, or publish operations.`;
3776
- }
3777
- if (policy === "autonomous") {
3778
- return `${principles}
3779
- - Approval policy: autonomous. Do not request human approval unless a platform policy explicitly requires it.`;
3780
- }
3781
- return `${principles}
3782
- - Approval policy: auto-low-risk. Proceed on reversible low-risk work; request human approval for destructive or irreversible operations.`;
3783
- }
3784
- function stringifyPrinciples(value) {
3785
- if (typeof value === "string" && value.trim()) return value.trim();
3786
- if (value && typeof value === "object" && !Array.isArray(value)) {
3787
- const record = value;
3788
- for (const key of ["en", "zh"]) {
3789
- const text = record[key];
3790
- if (typeof text === "string" && text.trim()) return text.trim();
3791
- }
3792
- for (const text of Object.values(record)) {
3793
- if (typeof text === "string" && text.trim()) return text.trim();
3794
- }
3795
- }
3796
- return void 0;
3797
- }
3798
3361
  function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
3799
3362
  if (!outboxDir) return prompt;
3800
3363
  if (profileConfig?.disableOutboxHints === true) return prompt;
@@ -3813,9 +3376,6 @@ function appendChannelContext(prompt, payload, profile) {
3813
3376
  const type = payload.conversationType === "direct" || payload.conversationType === "group" ? payload.conversationType : "unknown";
3814
3377
  const youAre = profile.agentUsername || payload.agentImUserId || "this agent";
3815
3378
  const lines = ["[Channel context]", `Conversation type: ${type}`, `You are: ${youAre}`];
3816
- if (payload.conversationId) {
3817
- lines.push(`Conversation ID: ${payload.conversationId}`);
3818
- }
3819
3379
  const participants = Array.isArray(payload.participants) ? payload.participants : [];
3820
3380
  if (participants.length > 0) {
3821
3381
  if (type === "direct") {
@@ -4085,8 +3645,8 @@ var ServicePool = class {
4085
3645
 
4086
3646
  // src/daemon/local-server.ts
4087
3647
  var import_node_http = require("http");
4088
- var import_node_crypto4 = require("crypto");
4089
- var import_node_fs8 = require("fs");
3648
+ var import_node_crypto = require("crypto");
3649
+ var import_node_fs7 = require("fs");
4090
3650
  var path2 = __toESM(require("path"), 1);
4091
3651
  var LocalServer = class {
4092
3652
  constructor(opts) {
@@ -4239,7 +3799,7 @@ var LocalServer = class {
4239
3799
  async handleSnapshot(req, res) {
4240
3800
  const root = this.opts.snapshotRoot ?? "/workspace";
4241
3801
  try {
4242
- const st = await import_node_fs8.promises.stat(root);
3802
+ const st = await import_node_fs7.promises.stat(root);
4243
3803
  if (!st.isDirectory()) {
4244
3804
  respond(res, 400, { error: "snapshot_root_not_directory", rootPath: root });
4245
3805
  return;
@@ -4315,7 +3875,7 @@ var LocalServer = class {
4315
3875
  respond(res, 400, { error: "missing_taskId" });
4316
3876
  return;
4317
3877
  }
4318
- const runId = (0, import_node_crypto4.randomUUID)();
3878
+ const runId = (0, import_node_crypto.randomUUID)();
4319
3879
  try {
4320
3880
  this.opts.onDispatch?.(payload, runId);
4321
3881
  } catch (err) {
@@ -4370,7 +3930,7 @@ async function walkAndDigest(root, current) {
4370
3930
  const out = [];
4371
3931
  let entries;
4372
3932
  try {
4373
- entries = await import_node_fs8.promises.readdir(current);
3933
+ entries = await import_node_fs7.promises.readdir(current);
4374
3934
  } catch (err) {
4375
3935
  if (err.code === "ENOENT") return out;
4376
3936
  throw err;
@@ -4381,7 +3941,7 @@ async function walkAndDigest(root, current) {
4381
3941
  const rel = path2.relative(root, full);
4382
3942
  let st;
4383
3943
  try {
4384
- st = await import_node_fs8.promises.stat(full);
3944
+ st = await import_node_fs7.promises.stat(full);
4385
3945
  } catch {
4386
3946
  continue;
4387
3947
  }
@@ -4392,11 +3952,11 @@ async function walkAndDigest(root, current) {
4392
3952
  continue;
4393
3953
  }
4394
3954
  if (!st.isFile()) continue;
4395
- const buf = await import_node_fs8.promises.readFile(full);
4396
- const sha2564 = (0, import_node_crypto4.createHash)("sha256").update(buf).digest("hex");
3955
+ const buf = await import_node_fs7.promises.readFile(full);
3956
+ const sha2562 = (0, import_node_crypto.createHash)("sha256").update(buf).digest("hex");
4397
3957
  out.push({
4398
3958
  path: rel,
4399
- sha256: sha2564,
3959
+ sha256: sha2562,
4400
3960
  sizeBytes: st.size,
4401
3961
  mtime: Math.floor(st.mtimeMs)
4402
3962
  });
@@ -4406,14 +3966,12 @@ async function walkAndDigest(root, current) {
4406
3966
 
4407
3967
  // src/daemon/runner.ts
4408
3968
  var import_node_events3 = require("events");
4409
- var import_node_fs14 = require("fs");
4410
- var import_promises = require("fs/promises");
4411
- var import_node_os6 = require("os");
4412
- var import_node_path10 = require("path");
3969
+ var import_node_fs11 = require("fs");
3970
+ var import_node_os4 = require("os");
4413
3971
 
4414
3972
  // src/adapters/claude-code/index.ts
4415
3973
  var import_node_child_process2 = require("child_process");
4416
- var import_zod4 = require("zod");
3974
+ var import_zod3 = require("zod");
4417
3975
 
4418
3976
  // src/adapters/claude-code/output-parser.ts
4419
3977
  var MAX_OUTPUT_CHARS = 64 * 1024;
@@ -4445,28 +4003,28 @@ function parseHeadlessOutput(stdout) {
4445
4003
  }
4446
4004
 
4447
4005
  // src/adapters/claude-code/index.ts
4448
- var McpServerSchema = import_zod4.z.object({
4449
- name: import_zod4.z.string(),
4450
- command: import_zod4.z.string(),
4451
- args: import_zod4.z.array(import_zod4.z.string()).optional(),
4452
- env: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.string()).optional()
4006
+ var McpServerSchema = import_zod3.z.object({
4007
+ name: import_zod3.z.string(),
4008
+ command: import_zod3.z.string(),
4009
+ args: import_zod3.z.array(import_zod3.z.string()).optional(),
4010
+ env: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string()).optional()
4453
4011
  });
4454
- var CCConfigSchema = import_zod4.z.object({
4455
- cwd: import_zod4.z.string().min(1),
4012
+ var CCConfigSchema = import_zod3.z.object({
4013
+ cwd: import_zod3.z.string().min(1),
4456
4014
  // claude 2.x accepts aliases ('sonnet', 'haiku', 'opus') and full ids
4457
4015
  // (e.g. 'claude-sonnet-4-6'). The 1.x default 'claude-3-5-sonnet' is no
4458
4016
  // longer a valid model id under the current CLI.
4459
- model: import_zod4.z.string().default("sonnet"),
4460
- systemPrompt: import_zod4.z.string().optional(),
4461
- envVars: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.string()).optional(),
4462
- mcpServers: import_zod4.z.array(McpServerSchema).optional(),
4463
- allowedTools: import_zod4.z.array(import_zod4.z.string()).optional(),
4464
- maxTurns: import_zod4.z.number().int().positive().default(20),
4017
+ model: import_zod3.z.string().default("sonnet"),
4018
+ systemPrompt: import_zod3.z.string().optional(),
4019
+ envVars: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string()).optional(),
4020
+ mcpServers: import_zod3.z.array(McpServerSchema).optional(),
4021
+ allowedTools: import_zod3.z.array(import_zod3.z.string()).optional(),
4022
+ maxTurns: import_zod3.z.number().int().positive().default(20),
4465
4023
  // External-model route (1.9.x extension — optional, no breaking change)
4466
- baseURL: import_zod4.z.string().url().optional(),
4467
- apiKeyRef: import_zod4.z.string().regex(/^(env|keychain):[A-Za-z0-9_][A-Za-z0-9_.\-]*$/).optional(),
4024
+ baseURL: import_zod3.z.string().url().optional(),
4025
+ apiKeyRef: import_zod3.z.string().regex(/^(env|keychain):[A-Za-z0-9_][A-Za-z0-9_.\-]*$/).optional(),
4468
4026
  // informational tag — dispatch does not branch on this value yet
4469
- route: import_zod4.z.enum(["default", "prismer", "omniroute"]).default("default")
4027
+ route: import_zod3.z.enum(["default", "prismer", "omniroute"]).default("default")
4470
4028
  });
4471
4029
  var claudeCodeAdapter = {
4472
4030
  name: "claude-code",
@@ -4620,7 +4178,7 @@ function resolveKeyRef(ref) {
4620
4178
 
4621
4179
  // src/adapters/codex/index.ts
4622
4180
  var import_node_child_process3 = require("child_process");
4623
- var import_zod5 = require("zod");
4181
+ var import_zod4 = require("zod");
4624
4182
 
4625
4183
  // src/skills/memory-curation.ts
4626
4184
  var MEMORY_CURATION_SKILL_TEXT = `---
@@ -4753,31 +4311,31 @@ If the answer is no, do not save.
4753
4311
  `;
4754
4312
 
4755
4313
  // src/adapters/codex/index.ts
4756
- var CodexConfigSchema = import_zod5.z.object({
4314
+ var CodexConfigSchema = import_zod4.z.object({
4757
4315
  /** Working directory for the codex subprocess. */
4758
- cwd: import_zod5.z.string().min(1),
4316
+ cwd: import_zod4.z.string().min(1),
4759
4317
  /**
4760
4318
  * Model identifier passed to --model (e.g. 'codex-mini-latest', 'o4-mini').
4761
4319
  * Default matches the Codex CLI built-in default as of 2026-05.
4762
4320
  */
4763
- model: import_zod5.z.string().default("codex-mini-latest"),
4321
+ model: import_zod4.z.string().default("codex-mini-latest"),
4764
4322
  /**
4765
4323
  * Sandbox level for the codex subprocess.
4766
4324
  * read-only — analysis only; code-gen tasks will stall
4767
4325
  * workspace-write — write inside cwd, read-only outside (recommended)
4768
4326
  * danger-full-access — no restrictions; safe only inside isolated containers
4769
4327
  */
4770
- sandbox: import_zod5.z.enum(["read-only", "workspace-write", "danger-full-access"]).default("workspace-write"),
4328
+ sandbox: import_zod4.z.enum(["read-only", "workspace-write", "danger-full-access"]).default("workspace-write"),
4771
4329
  /** Optional system prompt prepended to the user prompt as a preamble (see U4). */
4772
- systemPrompt: import_zod5.z.string().optional(),
4330
+ systemPrompt: import_zod4.z.string().optional(),
4773
4331
  /** Extra env vars merged into the codex subprocess environment. */
4774
- envVars: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.string()).optional(),
4332
+ envVars: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.string()).optional(),
4775
4333
  /**
4776
4334
  * Name of the env var holding the OpenAI API key. Defaults to
4777
4335
  * 'OPENAI_API_KEY' (Codex CLI default). Override if using a workspace-
4778
4336
  * scoped key under a different name.
4779
4337
  */
4780
- apiKeyEnv: import_zod5.z.string().default("OPENAI_API_KEY")
4338
+ apiKeyEnv: import_zod4.z.string().default("OPENAI_API_KEY")
4781
4339
  });
4782
4340
  function buildCodexPrompt(config, taskPrompt) {
4783
4341
  const preambleParts = [MEMORY_CURATION_SKILL_TEXT.trim()];
@@ -4925,21 +4483,148 @@ var codexAdapter = {
4925
4483
  }
4926
4484
  };
4927
4485
 
4928
- // src/daemon/memory/runtime.ts
4929
- var os = __toESM(require("os"), 1);
4930
- var path4 = __toESM(require("path"), 1);
4931
-
4932
- // src/daemon/memory/store.ts
4933
- var import_better_sqlite33 = __toESM(require("better-sqlite3"), 1);
4934
- var fs2 = __toESM(require("fs"), 1);
4935
- var path3 = __toESM(require("path"), 1);
4936
- var import_node_crypto5 = require("crypto");
4937
-
4938
- // src/daemon/memory/crypto.ts
4939
- function sealPlaintext(content) {
4940
- return { kind: "inline", content };
4941
- }
4942
-
4486
+ // src/adapters/openclaw/index.ts
4487
+ var import_zod5 = require("zod");
4488
+ var OpenClawProfileConfigSchema = import_zod5.z.object({
4489
+ /** OpenClaw chat-completions HTTP port. */
4490
+ port: import_zod5.z.number().int().min(1).max(65535).default(18789),
4491
+ /** Bearer token expected by the gateway (from gateway.auth.bearerTokens). */
4492
+ apiKey: import_zod5.z.string().min(1),
4493
+ /**
4494
+ * Model field passed to OpenClaw's /v1/chat/completions. OpenClaw
4495
+ * intercepts this differently from a vanilla OpenAI server: the value
4496
+ * MUST be `openclaw` (or `openclaw/<agentId>`) — it picks which
4497
+ * OpenClaw agent answers, NOT the underlying LLM. The LLM is selected
4498
+ * by `agents.defaults.model.primary` in openclaw.json (e.g.
4499
+ * `prismer/us-kimi-k2.5`, set by the bootstrap).
4500
+ */
4501
+ model: import_zod5.z.string().min(1).default("openclaw")
4502
+ });
4503
+ var openclawAdapter = {
4504
+ name: "openclaw",
4505
+ kind: "long-running",
4506
+ capabilities: ["chat", "code", "multi-channel"],
4507
+ workspaceSchema: OpenClawProfileConfigSchema,
4508
+ validate(config) {
4509
+ const r = OpenClawProfileConfigSchema.safeParse(config);
4510
+ if (r.success) return { ok: true };
4511
+ return {
4512
+ ok: false,
4513
+ errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
4514
+ };
4515
+ },
4516
+ async ensureService(profile) {
4517
+ const config = OpenClawProfileConfigSchema.parse(profile.config);
4518
+ const baseUrl = `http://127.0.0.1:${config.port}`;
4519
+ if (!await checkHealth2(baseUrl, config.apiKey)) {
4520
+ throw new Error(
4521
+ `OpenClaw gateway not reachable at ${baseUrl}. Start it with:
4522
+ openclaw gateway --port ${config.port}`
4523
+ );
4524
+ }
4525
+ return new OpenClawService(baseUrl, config.apiKey, config.model);
4526
+ },
4527
+ async health() {
4528
+ return { available: true };
4529
+ }
4530
+ };
4531
+ var OpenClawService = class {
4532
+ constructor(baseUrl, apiKey, model) {
4533
+ this.baseUrl = baseUrl;
4534
+ this.apiKey = apiKey;
4535
+ this.model = model;
4536
+ this.id = baseUrl;
4537
+ }
4538
+ baseUrl;
4539
+ apiKey;
4540
+ model;
4541
+ id;
4542
+ async healthy() {
4543
+ return checkHealth2(this.baseUrl, this.apiKey);
4544
+ }
4545
+ async dispatch(task) {
4546
+ const startedAt = Date.now();
4547
+ const systemPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
4548
+ const messages = [];
4549
+ if (systemPrompt) messages.push({ role: "system", content: systemPrompt });
4550
+ messages.push({ role: "user", content: task.prompt });
4551
+ try {
4552
+ const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
4553
+ method: "POST",
4554
+ headers: {
4555
+ Authorization: `Bearer ${this.apiKey}`,
4556
+ "Content-Type": "application/json"
4557
+ },
4558
+ body: JSON.stringify({
4559
+ model: this.model,
4560
+ messages,
4561
+ stream: false
4562
+ }),
4563
+ signal: task.signal
4564
+ });
4565
+ if (!res.ok) {
4566
+ const body = await res.text().catch(() => "") ?? "";
4567
+ return {
4568
+ ok: false,
4569
+ error: {
4570
+ code: res.status === 401 || res.status === 403 ? "auth_invalid" : "adapter_dispatch_failed",
4571
+ message: `OpenClaw ${res.status}: ${body.slice(0, 400)}`
4572
+ }
4573
+ };
4574
+ }
4575
+ const json = await res.json();
4576
+ let output = json.choices?.[0]?.message?.content ?? "";
4577
+ const ERR_PREFIX_RE = /^Cannot read properties of undefined \(reading '[^']+'\)\s*\n+/;
4578
+ if (ERR_PREFIX_RE.test(output)) {
4579
+ process.stderr.write(
4580
+ `[openclaw-adapter] stripping known stderr-bleed prefix from chat output (OpenClaw 2026.4.x bug)
4581
+ `
4582
+ );
4583
+ output = output.replace(ERR_PREFIX_RE, "");
4584
+ }
4585
+ return {
4586
+ ok: true,
4587
+ output,
4588
+ metrics: { durationMs: Date.now() - startedAt }
4589
+ };
4590
+ } catch (err) {
4591
+ if (task.signal?.aborted || err?.name === "AbortError") {
4592
+ return { ok: false, error: { code: "task_cancelled", message: "Task cancelled by client" } };
4593
+ }
4594
+ return {
4595
+ ok: false,
4596
+ error: { code: "adapter_dispatch_failed", message: err.message }
4597
+ };
4598
+ }
4599
+ }
4600
+ };
4601
+ async function checkHealth2(baseUrl, apiKey) {
4602
+ try {
4603
+ const res = await fetch(`${baseUrl}/health`, {
4604
+ headers: { Authorization: `Bearer ${apiKey}` },
4605
+ signal: AbortSignal.timeout(2e3)
4606
+ });
4607
+ return res.ok;
4608
+ } catch {
4609
+ return false;
4610
+ }
4611
+ }
4612
+
4613
+ // src/daemon/memory/runtime.ts
4614
+ var os = __toESM(require("os"), 1);
4615
+ var path4 = __toESM(require("path"), 1);
4616
+
4617
+ // src/daemon/memory/store.ts
4618
+ var import_better_sqlite33 = __toESM(require("better-sqlite3"), 1);
4619
+ var fs2 = __toESM(require("fs"), 1);
4620
+ var path3 = __toESM(require("path"), 1);
4621
+ var import_node_crypto2 = require("crypto");
4622
+
4623
+ // src/daemon/memory/crypto.ts
4624
+ function sealPlaintext(content) {
4625
+ return { kind: "inline", content };
4626
+ }
4627
+
4943
4628
  // src/daemon/memory/store.ts
4944
4629
  var SCHEMA_VERSION2 = 1;
4945
4630
  var SCHEMA_V1_STATEMENTS = [
@@ -5084,9 +4769,9 @@ var MemoryStore = class {
5084
4769
  const row = db.prepare("SELECT * FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId);
5085
4770
  return row ? this.rowToPage(row) : null;
5086
4771
  }
5087
- loadContent(pageId, version) {
4772
+ loadContent(pageId, version2) {
5088
4773
  const db = this.requireDb();
5089
- const targetVersion = version ?? db.prepare("SELECT version FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId)?.version;
4774
+ const targetVersion = version2 ?? db.prepare("SELECT version FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId)?.version;
5090
4775
  if (targetVersion === void 0) return null;
5091
4776
  const row = db.prepare(
5092
4777
  `SELECT c.pageId, c.version, c.payloadKind, c.payloadValue
@@ -5124,7 +4809,7 @@ var MemoryStore = class {
5124
4809
  }
5125
4810
  const db = this.requireDb();
5126
4811
  const now = Date.now();
5127
- const contentHash = sha2563(input.content);
4812
+ const contentHash = sha256(input.content);
5128
4813
  const payload = sealPlaintext(input.content);
5129
4814
  if (payload.kind !== "inline") {
5130
4815
  throw new Error("MemoryStore.write: non-inline payload not yet supported in phase-0");
@@ -5135,7 +4820,7 @@ var MemoryStore = class {
5135
4820
  const existing = db.prepare(
5136
4821
  "SELECT id, version, createdAt FROM memory_pages WHERE workspaceId = ? AND path = ?"
5137
4822
  ).get(this.opts.workspaceId, input.path);
5138
- const pageId = existing?.id ?? `page_${(0, import_node_crypto5.randomUUID)().replace(/-/g, "").slice(0, 22)}`;
4823
+ const pageId = existing?.id ?? `page_${(0, import_node_crypto2.randomUUID)().replace(/-/g, "").slice(0, 22)}`;
5139
4824
  const newVersion = (existing?.version ?? 0) + 1;
5140
4825
  const createdAt = existing?.createdAt ?? now;
5141
4826
  const staleFlag = input.stale ? 1 : 0;
@@ -5318,8 +5003,8 @@ var MemoryStore = class {
5318
5003
  };
5319
5004
  }
5320
5005
  };
5321
- function sha2563(s) {
5322
- return (0, import_node_crypto5.createHash)("sha256").update(s, "utf8").digest("hex");
5006
+ function sha256(s) {
5007
+ return (0, import_node_crypto2.createHash)("sha256").update(s, "utf8").digest("hex");
5323
5008
  }
5324
5009
 
5325
5010
  // src/daemon/memory/search.ts
@@ -5425,7 +5110,7 @@ function clamp(n, lo, hi) {
5425
5110
  }
5426
5111
 
5427
5112
  // src/daemon/memory/outbox.ts
5428
- var import_node_crypto6 = require("crypto");
5113
+ var import_node_crypto3 = require("crypto");
5429
5114
 
5430
5115
  // src/daemon/memory/envelope.ts
5431
5116
  var import_zod6 = require("zod");
@@ -5558,7 +5243,7 @@ var MemoryOutbox = class {
5558
5243
  const now = Date.now();
5559
5244
  const parsed = MemoryOutboxEnvelope.safeParse(event);
5560
5245
  if (!parsed.success) {
5561
- const dlId = `dl_${(0, import_node_crypto6.randomUUID)()}`;
5246
+ const dlId = `dl_${(0, import_node_crypto3.randomUUID)()}`;
5562
5247
  db.prepare(
5563
5248
  `INSERT INTO memory_outbox_dead_letter (id, eventType, rawJson, errorJson, createdAt)
5564
5249
  VALUES (?, ?, ?, ?, ?)`
@@ -5575,7 +5260,7 @@ var MemoryOutbox = class {
5575
5260
  if (validated.eventType === "memory.feedback") {
5576
5261
  const check = validateFeedbackTarget(validated);
5577
5262
  if (!check.ok) {
5578
- const dlId = `dl_${(0, import_node_crypto6.randomUUID)()}`;
5263
+ const dlId = `dl_${(0, import_node_crypto3.randomUUID)()}`;
5579
5264
  db.prepare(
5580
5265
  `INSERT INTO memory_outbox_dead_letter (id, eventType, rawJson, errorJson, createdAt)
5581
5266
  VALUES (?, ?, ?, ?, ?)`
@@ -5589,7 +5274,7 @@ var MemoryOutbox = class {
5589
5274
  return { id: dlId, deadLetter: true };
5590
5275
  }
5591
5276
  }
5592
- const rowId = `out_${(0, import_node_crypto6.randomUUID)()}`;
5277
+ const rowId = `out_${(0, import_node_crypto3.randomUUID)()}`;
5593
5278
  try {
5594
5279
  db.prepare(
5595
5280
  `INSERT INTO memory_outbox (id, eventType, envelopeJson, idempotencyKey, status, createdAt)
@@ -6169,7 +5854,7 @@ function clamp2(n, lo, hi) {
6169
5854
  }
6170
5855
 
6171
5856
  // src/daemon/memory/fork/tracing.ts
6172
- var import_node_crypto7 = require("crypto");
5857
+ var import_node_crypto4 = require("crypto");
6173
5858
 
6174
5859
  // src/daemon/memory/fork/runner.ts
6175
5860
  var DEFAULT_SNIPPET_MAX_BYTES = 500;
@@ -6177,8 +5862,8 @@ function finalizeSelected(runtime, workspaceId, paths, snippetMaxBytes = DEFAULT
6177
5862
  const slot = runtime.peek(workspaceId);
6178
5863
  if (!slot) return [];
6179
5864
  const out = [];
6180
- for (const path9 of paths) {
6181
- const page = slot.store.loadByPath(path9);
5865
+ for (const path7 of paths) {
5866
+ const page = slot.store.loadByPath(path7);
6182
5867
  if (!page) continue;
6183
5868
  const content = slot.store.loadContent(page.id)?.content ?? "";
6184
5869
  out.push({
@@ -6453,14 +6138,14 @@ function parsePrismerUri(uri) {
6453
6138
  const memoryIdx = rest.indexOf(segMemory);
6454
6139
  if (memoryIdx <= 0) return null;
6455
6140
  const workspaceId = rest.slice(0, memoryIdx);
6456
- const path9 = rest.slice(memoryIdx + segMemory.length);
6457
- if (!workspaceId || !path9) return null;
6458
- return { workspaceId, path: path9 };
6141
+ const path7 = rest.slice(memoryIdx + segMemory.length);
6142
+ if (!workspaceId || !path7) return null;
6143
+ return { workspaceId, path: path7 };
6459
6144
  }
6460
6145
 
6461
6146
  // src/daemon/asset/metadata-index.ts
6462
- var import_node_fs9 = require("fs");
6463
- var import_node_path8 = require("path");
6147
+ var import_node_fs8 = require("fs");
6148
+ var import_node_path6 = require("path");
6464
6149
  var DEFAULT_LIMIT = 8;
6465
6150
  var PULL_PAGE_SIZE = 500;
6466
6151
  var THROTTLE_MS = 3e4;
@@ -6488,16 +6173,16 @@ var AssetMetadataIndex = class {
6488
6173
  this.db = opts.db;
6489
6174
  this.cloud = opts.cloud;
6490
6175
  this.workspaceId = opts.workspaceId;
6491
- if (!(0, import_node_fs9.existsSync)(opts.workspaceStateDir)) {
6492
- (0, import_node_fs9.mkdirSync)(opts.workspaceStateDir, { recursive: true });
6176
+ if (!(0, import_node_fs8.existsSync)(opts.workspaceStateDir)) {
6177
+ (0, import_node_fs8.mkdirSync)(opts.workspaceStateDir, { recursive: true });
6493
6178
  }
6494
- this.cursorPath = (0, import_node_path8.join)(opts.workspaceStateDir, "asset-metadata-cursor.json");
6179
+ this.cursorPath = (0, import_node_path6.join)(opts.workspaceStateDir, "asset-metadata-cursor.json");
6495
6180
  }
6496
6181
  /** Persisted cursor for this workspace, or 0 on first run / corrupted file. */
6497
6182
  readCursor() {
6498
- if (!(0, import_node_fs9.existsSync)(this.cursorPath)) return 0;
6183
+ if (!(0, import_node_fs8.existsSync)(this.cursorPath)) return 0;
6499
6184
  try {
6500
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)(this.cursorPath, "utf8"));
6185
+ const parsed = JSON.parse((0, import_node_fs8.readFileSync)(this.cursorPath, "utf8"));
6501
6186
  if (parsed.workspaceId !== this.workspaceId) return 0;
6502
6187
  return parsed.cursor;
6503
6188
  } catch {
@@ -6510,7 +6195,7 @@ var AssetMetadataIndex = class {
6510
6195
  cursor,
6511
6196
  writtenAt: Date.now()
6512
6197
  };
6513
- (0, import_node_fs9.writeFileSync)(this.cursorPath, JSON.stringify(payload, null, 2));
6198
+ (0, import_node_fs8.writeFileSync)(this.cursorPath, JSON.stringify(payload, null, 2));
6514
6199
  }
6515
6200
  /**
6516
6201
  * Pull incremental asset metadata changes since the persisted cursor and
@@ -6550,13 +6235,9 @@ var AssetMetadataIndex = class {
6550
6235
  let applied = 0;
6551
6236
  const tx = this.db.transaction((items) => {
6552
6237
  for (const item of items) {
6553
- const assetId = item.assetId ?? item.id;
6554
- if (!assetId) {
6555
- throw new Error("Asset metadata index item missing assetId/id");
6556
- }
6557
6238
  upsert.run(
6558
6239
  this.workspaceId,
6559
- assetId,
6240
+ item.assetId,
6560
6241
  item.contentHash,
6561
6242
  item.filename ?? null,
6562
6243
  item.folderPath ?? null,
@@ -6601,14 +6282,6 @@ var AssetMetadataIndex = class {
6601
6282
  const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND filename = ?").get(this.workspaceId, filename);
6602
6283
  return row ? rowToMetadata(row) : void 0;
6603
6284
  }
6604
- resolveByAssetId(assetId) {
6605
- const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND asset_id = ?").get(this.workspaceId, assetId);
6606
- return row ? rowToMetadata(row) : void 0;
6607
- }
6608
- resolveByContentHash(contentHash) {
6609
- const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND content_hash = ?").get(this.workspaceId, contentHash);
6610
- return row ? rowToMetadata(row) : void 0;
6611
- }
6612
6285
  /** Batch exact match — returns Map for O(1) access. */
6613
6286
  resolveByFilenames(filenames) {
6614
6287
  if (filenames.length === 0) return /* @__PURE__ */ new Map();
@@ -6627,10 +6300,7 @@ var AssetMetadataIndex = class {
6627
6300
  };
6628
6301
 
6629
6302
  // src/daemon/asset/rpc.ts
6630
- var import_node_fs10 = require("fs");
6631
6303
  var ASSET_PATH_PREFIX = "/local/asset/";
6632
- var MAX_READ_BYTES = 256 * 1024;
6633
- var MAX_SEARCH_BYTES = 1024 * 1024;
6634
6304
  function attachAssetRpc(opts) {
6635
6305
  return async (req, res) => {
6636
6306
  const url = req.url ?? "/";
@@ -6641,17 +6311,10 @@ function attachAssetRpc(opts) {
6641
6311
  try {
6642
6312
  if (method === "POST" && subpath === "search") {
6643
6313
  const body = await readJson3(req);
6644
- return await handleSearch2(opts, body, res);
6645
- }
6646
- if (method === "POST" && subpath === "describe") {
6647
- const body = await readJson3(req);
6648
- return handleDescribe(opts, body, res);
6314
+ return handleSearch2(opts.resolveIndex, body, res);
6649
6315
  }
6650
- if (method === "POST" && subpath === "read") {
6651
- const body = await readJson3(req);
6652
- return await handleRead(opts, body, res);
6653
- }
6654
- return false;
6316
+ respond3(res, 404, { error: "asset_route_not_found", path: url });
6317
+ return true;
6655
6318
  } catch (err) {
6656
6319
  respond3(res, 500, {
6657
6320
  error: "asset_rpc_failed",
@@ -6661,21 +6324,18 @@ function attachAssetRpc(opts) {
6661
6324
  }
6662
6325
  };
6663
6326
  }
6664
- async function handleSearch2(opts, body, res) {
6327
+ function handleSearch2(resolveIndex, body, res) {
6665
6328
  if (!body || typeof body !== "object") {
6666
6329
  return respond4002(res, "request body must be a JSON object");
6667
6330
  }
6668
6331
  const b = body;
6669
- if (typeof b.query !== "string" || !b.query.trim()) {
6670
- return respond4002(res, "query is required (non-empty string)");
6671
- }
6672
- if (typeof b.uri === "string" && b.uri.trim()) {
6673
- return searchAssetContent(opts, b.uri.trim(), b.query.trim(), res);
6674
- }
6675
6332
  if (typeof b.workspaceId !== "string" || !b.workspaceId) {
6676
6333
  return respond4002(res, "workspaceId is required (string)");
6677
6334
  }
6678
- const index = opts.resolveIndex(b.workspaceId);
6335
+ if (typeof b.query !== "string" || !b.query.trim()) {
6336
+ return respond4002(res, "query is required (non-empty string)");
6337
+ }
6338
+ const index = resolveIndex(b.workspaceId);
6679
6339
  if (!index) {
6680
6340
  respond3(res, 404, {
6681
6341
  error: "workspace_index_not_found",
@@ -6689,124 +6349,6 @@ async function handleSearch2(opts, body, res) {
6689
6349
  respond3(res, 200, { items });
6690
6350
  return true;
6691
6351
  }
6692
- function handleDescribe(opts, body, res) {
6693
- const resolved = resolveAssetRef(opts, body);
6694
- if ("error" in resolved) return respond4002(res, resolved.error);
6695
- const cached = opts.assetCache?.get(resolved.row.contentHash);
6696
- respond3(res, 200, {
6697
- asset: resolved.row,
6698
- uri: `prismer://workspace/${resolved.workspaceId}/asset/${resolved.row.contentHash}`,
6699
- cache: cached ? { status: "hit", localPath: cached.localPath, sizeBytes: cached.sizeBytes, lastUsedAt: cached.lastUsedAt } : { status: "miss" },
6700
- tools: ["asset.describe", "asset.read", "asset.search"]
6701
- });
6702
- return true;
6703
- }
6704
- async function handleRead(opts, body, res) {
6705
- const resolved = resolveAssetRef(opts, body);
6706
- if ("error" in resolved) return respond4002(res, resolved.error);
6707
- if (!opts.assetCache) {
6708
- respond3(res, 503, { error: "asset_cache_unavailable" });
6709
- return true;
6710
- }
6711
- const b = body;
6712
- const offset = typeof b.offset === "number" && b.offset > 0 ? Math.floor(b.offset) : 0;
6713
- const requested = typeof b.length === "number" && b.length > 0 ? Math.floor(b.length) : MAX_READ_BYTES;
6714
- const length = Math.min(requested, MAX_READ_BYTES);
6715
- const cached = await opts.assetCache.getOrFetch(resolved.row.contentHash, {
6716
- workspaceIdHint: resolved.workspaceId,
6717
- assetId: resolved.row.assetId
6718
- });
6719
- const chunk = readFileRange(cached.localPath, offset, length);
6720
- const textLike = isTextLike(resolved.row);
6721
- respond3(res, 200, {
6722
- asset: resolved.row,
6723
- locator: { byteOffset: offset, byteLength: chunk.bytesRead },
6724
- truncated: requested > MAX_READ_BYTES || offset + chunk.bytesRead < chunk.sizeBytes,
6725
- encoding: textLike ? "utf8" : "base64",
6726
- content: textLike ? chunk.buffer.toString("utf8") : chunk.buffer.toString("base64")
6727
- });
6728
- return true;
6729
- }
6730
- async function searchAssetContent(opts, uri, query, res) {
6731
- const resolved = resolveAssetRef(opts, { uri });
6732
- if ("error" in resolved) return respond4002(res, resolved.error);
6733
- if (!opts.assetCache) {
6734
- respond3(res, 503, { error: "asset_cache_unavailable" });
6735
- return true;
6736
- }
6737
- if (!isTextLike(resolved.row)) {
6738
- respond3(res, 200, {
6739
- items: [],
6740
- unsupported: true,
6741
- reason: "content search currently supports text-like assets only",
6742
- asset: resolved.row
6743
- });
6744
- return true;
6745
- }
6746
- const cached = await opts.assetCache.getOrFetch(resolved.row.contentHash, {
6747
- workspaceIdHint: resolved.workspaceId,
6748
- assetId: resolved.row.assetId
6749
- });
6750
- const chunk = readFileRange(cached.localPath, 0, MAX_SEARCH_BYTES);
6751
- const text = chunk.buffer.toString("utf8");
6752
- const needle = query.toLowerCase();
6753
- const lines = text.split(/\r?\n/);
6754
- const items = [];
6755
- let byteOffset = 0;
6756
- for (let i = 0; i < lines.length && items.length < 20; i += 1) {
6757
- const line = lines[i] ?? "";
6758
- if (line.toLowerCase().includes(needle)) {
6759
- items.push({ line: i + 1, byteOffset, preview: line.slice(0, 500) });
6760
- }
6761
- byteOffset += Buffer.byteLength(line) + 1;
6762
- }
6763
- respond3(res, 200, {
6764
- items,
6765
- asset: resolved.row,
6766
- searched: { byteOffset: 0, byteLength: chunk.bytesRead, truncated: chunk.bytesRead < chunk.sizeBytes }
6767
- });
6768
- return true;
6769
- }
6770
- function resolveAssetRef(opts, body) {
6771
- if (!body || typeof body !== "object") return { error: "request body must be a JSON object" };
6772
- const b = body;
6773
- let workspaceId = typeof b.workspaceId === "string" ? b.workspaceId : "";
6774
- let assetId = typeof b.assetId === "string" ? b.assetId : "";
6775
- let contentHash = typeof b.contentHash === "string" ? b.contentHash : "";
6776
- if (typeof b.uri === "string" && b.uri.trim()) {
6777
- const [uri] = parseUris(b.uri.trim());
6778
- if (!uri) return { error: "uri must be prismer://workspace/<workspaceId>/asset/<hash>" };
6779
- if (uri.type !== "asset") return { error: "file uri lookup is not available in this RPC yet; pass asset uri or assetId" };
6780
- workspaceId = uri.workspaceId ?? workspaceId;
6781
- contentHash = uri.rest;
6782
- }
6783
- if (!workspaceId) return { error: "workspaceId is required (string)" };
6784
- const index = opts.resolveIndex(workspaceId);
6785
- if (!index) return { error: "workspace asset index is not synced yet" };
6786
- const row = assetId ? index.resolveByAssetId(assetId) : contentHash ? index.resolveByContentHash(contentHash) : void 0;
6787
- if (!row) return { error: "asset not found in local metadata index" };
6788
- return { workspaceId, row };
6789
- }
6790
- function isTextLike(row) {
6791
- const mime = row.mime ?? "";
6792
- const name = row.filename ?? "";
6793
- return mime.startsWith("text/") || mime.includes("json") || mime.includes("xml") || mime.includes("markdown") || mime.includes("csv") || /\.(md|markdown|txt|json|csv|tsv|ndjson|js|jsx|ts|tsx|py|go|rs|java|kt|swift|rb|php|css|html|htm|xhtml|sql|yaml|yml|toml|log)$/i.test(
6794
- name
6795
- );
6796
- }
6797
- function readFileRange(path9, offset, length) {
6798
- const sizeBytes = (0, import_node_fs10.statSync)(path9).size;
6799
- if (offset >= sizeBytes) return { buffer: Buffer.alloc(0), bytesRead: 0, sizeBytes };
6800
- const fd = (0, import_node_fs10.openSync)(path9, "r");
6801
- try {
6802
- const target = Math.min(length, sizeBytes - offset);
6803
- const buffer = Buffer.alloc(target);
6804
- const bytesRead = (0, import_node_fs10.readSync)(fd, buffer, 0, target, offset);
6805
- return { buffer: buffer.subarray(0, bytesRead), bytesRead, sizeBytes };
6806
- } finally {
6807
- (0, import_node_fs10.closeSync)(fd);
6808
- }
6809
- }
6810
6352
  function respond3(res, status, body) {
6811
6353
  res.statusCode = status;
6812
6354
  res.setHeader("Content-Type", "application/json");
@@ -6828,532 +6370,9 @@ async function readJson3(req) {
6828
6370
  }
6829
6371
  }
6830
6372
 
6831
- // src/daemon/asset/origin/drop-folder.ts
6832
- var import_node_fs11 = require("fs");
6833
- var import_node_os5 = require("os");
6834
- var path5 = __toESM(require("path"), 1);
6835
- var DropFolderAdapter = class {
6836
- kind = "drop-folder";
6837
- workspaceDir;
6838
- rootDir;
6839
- constructor(opts = {}) {
6840
- this.workspaceDir = opts.workspaceDir;
6841
- this.rootDir = opts.rootDir ?? path5.join(opts.homeDir ?? (0, import_node_os5.homedir)(), ".prismer");
6842
- }
6843
- /**
6844
- * Long-lived async iterable wrapping `scanOnce` on a 1s polling interval.
6845
- * Stops when the iterator is broken (e.g. consumer returns / throws).
6846
- */
6847
- async *observe(workspaceId) {
6848
- while (true) {
6849
- const batch = await this.scanOnce(workspaceId);
6850
- for (const obs of batch) yield obs;
6851
- await sleep(1e3);
6852
- }
6853
- }
6854
- /**
6855
- * Single-tick scan exposed for tests and one-shot CLI usage. Reads every
6856
- * file currently under the workspace's drop directory, returning one
6857
- * observation per file. The caller is expected to feed each into the
6858
- * outbox; idempotency on the outbox side collapses re-observations of
6859
- * unchanged files.
6860
- */
6861
- async scanOnce(workspaceId) {
6862
- const drop = this.dropDir(workspaceId);
6863
- let entries = [];
6864
- try {
6865
- entries = await import_node_fs11.promises.readdir(drop);
6866
- } catch (err) {
6867
- if (err.code === "ENOENT") return [];
6868
- throw err;
6869
- }
6870
- const out = [];
6871
- for (const name of entries) {
6872
- if (name.startsWith(".")) continue;
6873
- const fullPath = path5.join(drop, name);
6874
- const stat = await import_node_fs11.promises.lstat(fullPath).catch(() => null);
6875
- if (!stat) continue;
6876
- if (stat.isSymbolicLink()) continue;
6877
- if (stat.isDirectory()) {
6878
- for (const nested of await walk(fullPath)) {
6879
- const nstat = await import_node_fs11.promises.lstat(nested).catch(() => null);
6880
- if (!nstat || !nstat.isFile()) continue;
6881
- out.push(makeObservation(workspaceId, nested, drop, nstat.size, Math.floor(nstat.mtimeMs)));
6882
- }
6883
- continue;
6884
- }
6885
- if (!stat.isFile()) continue;
6886
- out.push(makeObservation(workspaceId, fullPath, drop, stat.size, Math.floor(stat.mtimeMs)));
6887
- }
6888
- return out;
6889
- }
6890
- async identifySource(obs) {
6891
- const detail = obs.detail;
6892
- const rel = path5.relative(detail.watchDir, detail.path);
6893
- const folderDir = path5.dirname(rel);
6894
- return {
6895
- sourceRef: `folder://${obs.workspaceId}/${rel.split(path5.sep).join("/")}`,
6896
- hints: {
6897
- filename: path5.basename(detail.path),
6898
- folderPath: folderDir === "." ? "" : folderDir.split(path5.sep).join("/")
6899
- }
6900
- };
6901
- }
6902
- async fetch(obs) {
6903
- const detail = obs.detail;
6904
- const bytes = await import_node_fs11.promises.readFile(detail.path);
6905
- return {
6906
- bytes,
6907
- mime: mimeFromExtension(detail.path),
6908
- size: bytes.length
6909
- };
6910
- }
6911
- /** Hook called by UploadRunner after a successful upload of one observation. */
6912
- async onUploadSuccess(obs) {
6913
- await this.moveTo(obs, this.uploadedDir(obs.workspaceId));
6914
- }
6915
- /** Hook called by UploadRunner after attempts hit maxAttempts. */
6916
- async onUploadFailure(obs) {
6917
- await this.moveTo(obs, this.failedDir(obs.workspaceId));
6918
- }
6919
- async moveTo(obs, destDir) {
6920
- const detail = obs.detail;
6921
- if (!detail || typeof detail.path !== "string") return;
6922
- try {
6923
- const rel = typeof detail.watchDir === "string" ? path5.relative(detail.watchDir, detail.path) : path5.basename(detail.path);
6924
- const safeRel = rel && !rel.startsWith("..") && !path5.isAbsolute(rel) ? rel : path5.basename(detail.path);
6925
- const destPath = path5.join(destDir, safeRel);
6926
- await import_node_fs11.promises.mkdir(path5.dirname(destPath), { recursive: true });
6927
- await import_node_fs11.promises.rename(detail.path, destPath);
6928
- } catch (err) {
6929
- if (err.code === "ENOENT") return;
6930
- throw err;
6931
- }
6932
- }
6933
- dropDir(workspaceId) {
6934
- if (this.workspaceDir) return path5.join(this.workspaceDir, "drop");
6935
- return path5.join(this.rootDir, "workspaces", workspaceId, "drop");
6936
- }
6937
- uploadedDir(workspaceId) {
6938
- if (this.workspaceDir) return path5.join(this.workspaceDir, "uploaded");
6939
- return path5.join(this.rootDir, "workspaces", workspaceId, "uploaded");
6940
- }
6941
- failedDir(workspaceId) {
6942
- if (this.workspaceDir) return path5.join(this.workspaceDir, "upload-failed");
6943
- return path5.join(this.rootDir, "workspaces", workspaceId, "upload-failed");
6944
- }
6945
- };
6946
- function makeObservation(workspaceId, filePath, watchDir, size, mtime) {
6947
- return {
6948
- workspaceId,
6949
- detail: { path: filePath, watchDir, size, mtime },
6950
- observedAt: mtime
6951
- };
6952
- }
6953
- async function walk(root) {
6954
- const out = [];
6955
- const entries = await import_node_fs11.promises.readdir(root).catch(() => []);
6956
- for (const name of entries) {
6957
- if (name.startsWith(".")) continue;
6958
- const full = path5.join(root, name);
6959
- const stat = await import_node_fs11.promises.lstat(full).catch(() => null);
6960
- if (!stat) continue;
6961
- if (stat.isSymbolicLink()) continue;
6962
- if (stat.isDirectory()) {
6963
- out.push(...await walk(full));
6964
- } else if (stat.isFile()) {
6965
- out.push(full);
6966
- }
6967
- }
6968
- return out;
6969
- }
6970
- var MIME_BY_EXT = {
6971
- ".txt": "text/plain",
6972
- ".md": "text/markdown",
6973
- ".csv": "text/csv",
6974
- ".json": "application/json",
6975
- ".yaml": "application/yaml",
6976
- ".yml": "application/yaml",
6977
- ".pdf": "application/pdf",
6978
- ".png": "image/png",
6979
- ".jpg": "image/jpeg",
6980
- ".jpeg": "image/jpeg",
6981
- ".gif": "image/gif",
6982
- ".webp": "image/webp",
6983
- ".svg": "image/svg+xml",
6984
- ".html": "text/html",
6985
- ".htm": "text/html",
6986
- ".xhtml": "application/xhtml+xml",
6987
- ".xml": "application/xml",
6988
- ".zip": "application/zip",
6989
- ".tar": "application/x-tar",
6990
- ".gz": "application/gzip",
6991
- ".mp3": "audio/mpeg",
6992
- ".mp4": "video/mp4",
6993
- ".mov": "video/quicktime",
6994
- ".wav": "audio/wav"
6995
- };
6996
- function mimeFromExtension(p) {
6997
- const ext = path5.extname(p).toLowerCase();
6998
- return MIME_BY_EXT[ext] ?? null;
6999
- }
7000
- function sleep(ms) {
7001
- return new Promise((r) => setTimeout(r, ms));
7002
- }
7003
-
7004
- // src/daemon/asset/origin/outbox.ts
7005
- var import_better_sqlite34 = __toESM(require("better-sqlite3"), 1);
7006
- var import_node_crypto8 = require("crypto");
7007
- var import_zod7 = require("zod");
7008
- var OriginObservationRecordSchema = import_zod7.z.object({
7009
- workspaceId: import_zod7.z.string().min(1),
7010
- originKind: import_zod7.z.enum(["upload", "drop-folder", "agent-gen"]),
7011
- sourceRef: import_zod7.z.string().min(1),
7012
- payloadJson: import_zod7.z.string().min(2),
7013
- hintsJson: import_zod7.z.string().min(2),
7014
- observedAt: import_zod7.z.number().int().nonnegative()
7015
- });
7016
- var DDL_STATEMENTS = [
7017
- `CREATE TABLE IF NOT EXISTS asset_origin_outbox (
7018
- id TEXT PRIMARY KEY,
7019
- workspaceId TEXT NOT NULL,
7020
- originKind TEXT NOT NULL,
7021
- sourceRef TEXT NOT NULL,
7022
- payloadJson TEXT NOT NULL,
7023
- hintsJson TEXT NOT NULL,
7024
- observedAt INTEGER NOT NULL,
7025
- idempotencyKey TEXT NOT NULL UNIQUE,
7026
- status TEXT NOT NULL CHECK (status IN ('pending', 'claimed', 'uploaded')),
7027
- attempts INTEGER NOT NULL DEFAULT 0,
7028
- lastError TEXT,
7029
- assetId TEXT,
7030
- contentHash TEXT,
7031
- createdAt INTEGER NOT NULL,
7032
- updatedAt INTEGER NOT NULL
7033
- )`,
7034
- `CREATE INDEX IF NOT EXISTS idx_asset_origin_outbox_status
7035
- ON asset_origin_outbox(status, createdAt)`,
7036
- `CREATE TABLE IF NOT EXISTS asset_origin_outbox_dead_letter (
7037
- id TEXT PRIMARY KEY,
7038
- workspaceId TEXT NOT NULL,
7039
- originKind TEXT NOT NULL,
7040
- sourceRef TEXT NOT NULL,
7041
- payloadJson TEXT NOT NULL,
7042
- hintsJson TEXT NOT NULL,
7043
- observedAt INTEGER NOT NULL,
7044
- errorJson TEXT NOT NULL,
7045
- attempts INTEGER NOT NULL,
7046
- createdAt INTEGER NOT NULL
7047
- )`
7048
- ];
7049
- var OriginOutbox = class {
7050
- db;
7051
- constructor(opts) {
7052
- this.db = new import_better_sqlite34.default(opts.dbPath);
7053
- this.db.pragma("journal_mode = WAL");
7054
- for (const ddl of DDL_STATEMENTS) this.db.prepare(ddl).run();
7055
- }
7056
- /**
7057
- * Validate + persist an observation. Schema-invalid input lands in
7058
- * `asset_origin_outbox_dead_letter` (never silently dropped). Re-enqueue
7059
- * with the same {originKind, workspaceId, sourceRef, observedAt} returns
7060
- * the existing row id without inserting again.
7061
- */
7062
- enqueue(record) {
7063
- const now = Date.now();
7064
- const parsed = OriginObservationRecordSchema.safeParse(record);
7065
- if (!parsed.success) {
7066
- const dlId = `dl_${(0, import_node_crypto8.randomUUID)()}`;
7067
- const raw = record && typeof record === "object" ? record : {};
7068
- this.db.prepare(
7069
- `INSERT INTO asset_origin_outbox_dead_letter
7070
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt, errorJson, attempts, createdAt)
7071
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7072
- ).run(
7073
- dlId,
7074
- coerceToString(raw.workspaceId, ""),
7075
- coerceToString(raw.originKind, ""),
7076
- coerceToString(raw.sourceRef, ""),
7077
- coerceToJson(raw.payloadJson),
7078
- coerceToJson(raw.hintsJson),
7079
- typeof raw.observedAt === "number" ? raw.observedAt : 0,
7080
- JSON.stringify(parsed.error.errors),
7081
- 0,
7082
- now
7083
- );
7084
- return { id: dlId, deadLetter: true };
7085
- }
7086
- const validated = parsed.data;
7087
- const idempotencyKey = computeIdempotencyKey(validated);
7088
- const rowId = `out_${(0, import_node_crypto8.randomUUID)()}`;
7089
- try {
7090
- this.db.prepare(
7091
- `INSERT INTO asset_origin_outbox
7092
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt,
7093
- idempotencyKey, status, attempts, createdAt, updatedAt)
7094
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', 0, ?, ?)`
7095
- ).run(
7096
- rowId,
7097
- validated.workspaceId,
7098
- validated.originKind,
7099
- validated.sourceRef,
7100
- validated.payloadJson,
7101
- validated.hintsJson,
7102
- validated.observedAt,
7103
- idempotencyKey,
7104
- now,
7105
- now
7106
- );
7107
- return { id: rowId, deadLetter: false };
7108
- } catch (err) {
7109
- const code = err.code;
7110
- if (code === "SQLITE_CONSTRAINT_UNIQUE") {
7111
- const existing = this.db.prepare("SELECT id FROM asset_origin_outbox WHERE idempotencyKey = ?").get(idempotencyKey);
7112
- if (existing) return { id: existing.id, deadLetter: false };
7113
- }
7114
- throw err;
7115
- }
7116
- }
7117
- /**
7118
- * Atomically claim the oldest pending row, transition status to 'claimed',
7119
- * and return it. Returns null when the queue is empty. The caller MUST
7120
- * follow up with `markUploaded` (success) or `recordFailure` (error) so
7121
- * the row doesn't sit in 'claimed' forever — Phase-0 is a single worker
7122
- * so we don't need a claim-expiry sweeper yet.
7123
- */
7124
- claimNext() {
7125
- const pickAndClaim = this.db.transaction(() => {
7126
- const row = this.db.prepare(
7127
- `SELECT id, workspaceId, originKind, sourceRef, payloadJson, hintsJson,
7128
- observedAt, attempts, lastError, createdAt
7129
- FROM asset_origin_outbox
7130
- WHERE status = 'pending'
7131
- ORDER BY createdAt ASC
7132
- LIMIT 1`
7133
- ).get();
7134
- if (!row) return null;
7135
- this.db.prepare("UPDATE asset_origin_outbox SET status = 'claimed', updatedAt = ? WHERE id = ?").run(Date.now(), row.id);
7136
- return row;
7137
- });
7138
- const claimed = pickAndClaim();
7139
- if (!claimed) return null;
7140
- return {
7141
- id: claimed.id,
7142
- row: rawToRow(claimed)
7143
- };
7144
- }
7145
- markUploaded(id, meta) {
7146
- this.db.prepare(
7147
- `UPDATE asset_origin_outbox
7148
- SET status = 'uploaded', assetId = ?, contentHash = ?, updatedAt = ?
7149
- WHERE id = ?`
7150
- ).run(meta.assetId, meta.contentHash, Date.now(), id);
7151
- }
7152
- /**
7153
- * Record one failed upload attempt. Increments `attempts`, stores the
7154
- * latest error, and either re-queues for retry (status -> 'pending') or
7155
- * spills to dead-letter once attempts hit maxAttempts.
7156
- */
7157
- recordFailure(id, error, opts) {
7158
- const now = Date.now();
7159
- const updated = this.db.transaction(() => {
7160
- const row = this.db.prepare(
7161
- `SELECT id, workspaceId, originKind, sourceRef, payloadJson, hintsJson,
7162
- observedAt, attempts, createdAt
7163
- FROM asset_origin_outbox
7164
- WHERE id = ?`
7165
- ).get(id);
7166
- if (!row) return;
7167
- const nextAttempts = row.attempts + 1;
7168
- if (nextAttempts >= opts.maxAttempts) {
7169
- this.db.prepare(
7170
- `INSERT INTO asset_origin_outbox_dead_letter
7171
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt, errorJson, attempts, createdAt)
7172
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7173
- ).run(
7174
- `dl_${(0, import_node_crypto8.randomUUID)()}`,
7175
- row.workspaceId,
7176
- row.originKind,
7177
- row.sourceRef,
7178
- row.payloadJson,
7179
- row.hintsJson,
7180
- row.observedAt,
7181
- JSON.stringify({ message: error, attempts: nextAttempts }),
7182
- nextAttempts,
7183
- now
7184
- );
7185
- this.db.prepare("DELETE FROM asset_origin_outbox WHERE id = ?").run(id);
7186
- } else {
7187
- this.db.prepare(
7188
- `UPDATE asset_origin_outbox
7189
- SET status = 'pending', attempts = ?, lastError = ?, updatedAt = ?
7190
- WHERE id = ?`
7191
- ).run(nextAttempts, error, now, id);
7192
- }
7193
- });
7194
- updated();
7195
- }
7196
- /**
7197
- * Re-promote any rows orphaned in `'claimed'` state back to `'pending'`.
7198
- *
7199
- * Why this exists: claimNext() transitions a row to 'claimed' under a
7200
- * transaction, but the upload that follows runs OUTSIDE the transaction
7201
- * (network call). If the daemon dies between the claim and the
7202
- * markUploaded/recordFailure call, the row is stuck — claimNext() only
7203
- * selects status='pending', so it would never be picked up again.
7204
- *
7205
- * Phase-0 single-worker safety: there is at most one in-flight claim at
7206
- * any time, so on next process startup ANY 'claimed' row is by definition
7207
- * orphaned. We promote it back to 'pending' (preserving attempts /
7208
- * lastError) so the next drainOnce can retry it.
7209
- *
7210
- * If we ever go multi-worker, this naive sweep becomes unsafe and must be
7211
- * replaced with a lease/heartbeat scheme.
7212
- *
7213
- * Returns the number of rows recovered (for logging / observability).
7214
- */
7215
- resetClaimed() {
7216
- const result = this.db.prepare(
7217
- "UPDATE asset_origin_outbox SET status = 'pending', updatedAt = ? WHERE status = 'claimed'"
7218
- ).run(Date.now());
7219
- return Number(result.changes);
7220
- }
7221
- pendingCount() {
7222
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox WHERE status = 'pending'").get().n;
7223
- }
7224
- uploadedCount() {
7225
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox WHERE status = 'uploaded'").get().n;
7226
- }
7227
- deadLetterCount() {
7228
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox_dead_letter").get().n;
7229
- }
7230
- close() {
7231
- this.db.close();
7232
- }
7233
- };
7234
- function computeIdempotencyKey(record) {
7235
- return (0, import_node_crypto8.createHash)("sha256").update(record.originKind).update("|").update(record.workspaceId).update("|").update(record.sourceRef).update("|").update(String(record.observedAt)).digest("hex");
7236
- }
7237
- function coerceToString(value, fallback) {
7238
- if (typeof value === "string") return value;
7239
- if (value == null) return fallback;
7240
- return String(value);
7241
- }
7242
- function coerceToJson(value) {
7243
- if (typeof value === "string") return value;
7244
- if (value == null) return "{}";
7245
- try {
7246
- return JSON.stringify(value);
7247
- } catch {
7248
- return "{}";
7249
- }
7250
- }
7251
- function rawToRow(raw) {
7252
- return {
7253
- id: raw.id,
7254
- workspaceId: raw.workspaceId,
7255
- originKind: raw.originKind,
7256
- sourceRef: raw.sourceRef,
7257
- payloadJson: raw.payloadJson,
7258
- hintsJson: raw.hintsJson,
7259
- observedAt: raw.observedAt,
7260
- attempts: raw.attempts,
7261
- lastError: raw.lastError,
7262
- createdAt: raw.createdAt
7263
- };
7264
- }
7265
-
7266
- // src/daemon/asset/origin/upload-runner.ts
7267
- var UploadRunner = class {
7268
- constructor(opts) {
7269
- this.opts = opts;
7270
- opts.outbox.resetClaimed();
7271
- }
7272
- opts;
7273
- /**
7274
- * Process every currently-pending outbox row exactly once. Failed rows
7275
- * stay 'pending' (with attempts+1) for the next drainOnce call; rows
7276
- * that hit maxAttempts spill to dead-letter and trigger onUploadFailure.
7277
- *
7278
- * Returns the count of rows processed (success + failure).
7279
- */
7280
- async drainOnce() {
7281
- let count = 0;
7282
- while (true) {
7283
- const claim = this.opts.outbox.claimNext();
7284
- if (!claim) break;
7285
- count += 1;
7286
- const row = claim.row;
7287
- const adapter = this.opts.adapters[row.originKind];
7288
- if (!adapter) {
7289
- this.opts.outbox.recordFailure(claim.id, `no adapter registered for kind=${row.originKind}`, {
7290
- maxAttempts: 1
7291
- // unrecoverable — fast-fail to dead-letter
7292
- });
7293
- continue;
7294
- }
7295
- try {
7296
- const obs = rowToObservation(row);
7297
- const bytes = await adapter.fetch(obs);
7298
- const id = await adapter.identifySource(obs);
7299
- const filename = id.hints?.filename ?? path6.basename(row.sourceRef);
7300
- const metadata = {
7301
- sourceRef: row.sourceRef,
7302
- sourceKind: row.originKind
7303
- };
7304
- if (id.hints?.description) metadata.description = id.hints.description;
7305
- if (id.hints?.sourceAgentImUserId) {
7306
- metadata.sourceAgentImUserId = id.hints.sourceAgentImUserId;
7307
- }
7308
- const folderPath = id.hints?.folderPath || void 0;
7309
- const result = await this.opts.cloud.uploadAsset({
7310
- workspaceId: row.workspaceId,
7311
- filename,
7312
- bytes: bytes.bytes,
7313
- mime: bytes.mime,
7314
- size: bytes.size,
7315
- metadata,
7316
- folderPath
7317
- });
7318
- this.opts.outbox.markUploaded(claim.id, result);
7319
- await adapter.onUploadSuccess?.(obs);
7320
- } catch (err) {
7321
- const message = err instanceof Error ? err.message : String(err);
7322
- const before = this.opts.outbox.deadLetterCount();
7323
- this.opts.outbox.recordFailure(claim.id, message, { maxAttempts: this.opts.maxAttempts });
7324
- const after = this.opts.outbox.deadLetterCount();
7325
- if (after > before) {
7326
- const obs = rowToObservation(row);
7327
- await adapter.onUploadFailure?.(obs, err);
7328
- }
7329
- }
7330
- }
7331
- return count;
7332
- }
7333
- };
7334
- function rowToObservation(row) {
7335
- let detail = {};
7336
- try {
7337
- const parsed = JSON.parse(row.payloadJson);
7338
- if (parsed && typeof parsed === "object") detail = parsed;
7339
- } catch {
7340
- }
7341
- return {
7342
- workspaceId: row.workspaceId,
7343
- detail,
7344
- observedAt: row.observedAt
7345
- };
7346
- }
7347
- var path6 = {
7348
- basename(p) {
7349
- const idx = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
7350
- return idx === -1 ? p : p.slice(idx + 1);
7351
- }
7352
- };
7353
-
7354
6373
  // src/daemon/outbox-watcher.ts
7355
- var import_node_fs12 = require("fs");
7356
- var path7 = __toESM(require("path"), 1);
6374
+ var import_node_fs9 = require("fs");
6375
+ var path5 = __toESM(require("path"), 1);
7357
6376
  var DEFAULT_INTERVAL_MS = 2e3;
7358
6377
  var RESERVED_SUBDIR = "_uploaded";
7359
6378
  var OutboxWatcher = class {
@@ -7520,7 +6539,7 @@ var OutboxWatcher = class {
7520
6539
  async scanDir(dir, kind, task) {
7521
6540
  let entries = [];
7522
6541
  try {
7523
- entries = await import_node_fs12.promises.readdir(dir);
6542
+ entries = await import_node_fs9.promises.readdir(dir);
7524
6543
  } catch (err) {
7525
6544
  const code = err.code;
7526
6545
  if (code === "ENOENT") return;
@@ -7530,10 +6549,10 @@ var OutboxWatcher = class {
7530
6549
  for (const name of entries) {
7531
6550
  if (name.startsWith(".")) continue;
7532
6551
  if (name === RESERVED_SUBDIR) continue;
7533
- const full = path7.join(dir, name);
6552
+ const full = path5.join(dir, name);
7534
6553
  let st;
7535
6554
  try {
7536
- st = await import_node_fs12.promises.stat(full);
6555
+ st = await import_node_fs9.promises.stat(full);
7537
6556
  } catch {
7538
6557
  continue;
7539
6558
  }
@@ -7558,8 +6577,8 @@ var OutboxWatcher = class {
7558
6577
  this.log("warn", `skip ${filePath}: no active taskId (no dispatch/handoff received yet)`);
7559
6578
  return;
7560
6579
  }
7561
- const bytes = await import_node_fs12.promises.readFile(filePath);
7562
- const fileName = path7.basename(filePath);
6580
+ const bytes = await import_node_fs9.promises.readFile(filePath);
6581
+ const fileName = path5.basename(filePath);
7563
6582
  const blob = new Blob([new Uint8Array(bytes)]);
7564
6583
  const form = new FormData();
7565
6584
  form.append("workspaceId", wsId);
@@ -7608,8 +6627,8 @@ var OutboxWatcher = class {
7608
6627
 
7609
6628
  // src/daemon/shell-executor.ts
7610
6629
  var import_node_child_process4 = require("child_process");
7611
- var import_node_fs13 = require("fs");
7612
- var import_node_path9 = require("path");
6630
+ var import_node_fs10 = require("fs");
6631
+ var import_node_path7 = require("path");
7613
6632
  var DEFAULT_OUTPUT_LIMIT = 256 * 1024;
7614
6633
  var DEFAULT_TIMEOUT = 6e4;
7615
6634
  var HARD_MAX_TIMEOUT = 30 * 6e4;
@@ -7645,7 +6664,7 @@ async function executeShellDispatch(payload, deps) {
7645
6664
  const command = readCommand(payload, execution);
7646
6665
  if (!command.trim()) return fail(payload.taskId, "shell_command_required", "Shell command is required");
7647
6666
  const cwd = resolveCwd(execution.cwd, deps.config.defaultCwd);
7648
- if (!(0, import_node_fs13.existsSync)(cwd)) return fail(payload.taskId, "shell_cwd_missing", `cwd does not exist: ${cwd}`);
6667
+ if (!(0, import_node_fs10.existsSync)(cwd)) return fail(payload.taskId, "shell_cwd_missing", `cwd does not exist: ${cwd}`);
7649
6668
  const timeoutMs = Math.min(
7650
6669
  typeof payload.timeoutMs === "number" && payload.timeoutMs > 0 ? payload.timeoutMs : deps.config.maxTimeoutMs,
7651
6670
  deps.config.maxTimeoutMs
@@ -7747,8 +6766,8 @@ function readCommand(payload, execution) {
7747
6766
  return payload.prompt;
7748
6767
  }
7749
6768
  function resolveCwd(raw, fallback) {
7750
- if (typeof raw !== "string" || raw.trim() === "") return (0, import_node_path9.resolve)(fallback);
7751
- return (0, import_node_path9.resolve)(raw);
6769
+ if (typeof raw !== "string" || raw.trim() === "") return (0, import_node_path7.resolve)(fallback);
6770
+ return (0, import_node_path7.resolve)(raw);
7752
6771
  }
7753
6772
  function clampNumber(value, fallback, min, max) {
7754
6773
  const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
@@ -7798,15 +6817,6 @@ var Runner = class extends import_node_events3.EventEmitter {
7798
6817
  outboxWatcher;
7799
6818
  memoryWiring;
7800
6819
  assetMetadataIndexes = /* @__PURE__ */ new Map();
7801
- workspaceMirrors = /* @__PURE__ */ new Map();
7802
- assetOriginOutbox;
7803
- dropFolderAdapter;
7804
- dropFolderUploadRunner;
7805
- dropFolderTimer;
7806
- dropFolderWorkspaceId = "";
7807
- dropFolderWorkspaceDir = "";
7808
- dropFolderTickInFlight = false;
7809
- dropFolderStable = /* @__PURE__ */ new Map();
7810
6820
  state = "idle";
7811
6821
  startedAt = 0;
7812
6822
  workspaceId = "";
@@ -7848,7 +6858,6 @@ var Runner = class extends import_node_events3.EventEmitter {
7848
6858
  this.servicePool = new ServicePool();
7849
6859
  this.installStaticHostedAgentFromEnv();
7850
6860
  this.loadAgentsFromDb();
7851
- await this.prepareLocalProfiles();
7852
6861
  this.syncQueue = new SyncQueue(this.db);
7853
6862
  this.syncWorker = new SyncWorker({ queue: this.syncQueue, flush: (row) => this.flushSyncRow(row) });
7854
6863
  this.syncWorker.start();
@@ -7870,10 +6879,9 @@ var Runner = class extends import_node_events3.EventEmitter {
7870
6879
  );
7871
6880
  }
7872
6881
  if (this.workspaceId) {
7873
- this.syncAssetState(this.workspaceId).catch(
7874
- (err) => console.error("[Daemon] Initial asset sync failed:", err.message)
6882
+ this.syncAssetMetadata(this.workspaceId).catch(
6883
+ (err) => console.error("[Daemon] Initial asset metadata sync failed:", err.message)
7875
6884
  );
7876
- await this.ensureDropFolderRuntime(this.workspaceId);
7877
6885
  }
7878
6886
  const containerId = process.env.PRISMER_CONTAINER_ID;
7879
6887
  const isContainer = !!containerId || process.env.PRISMER_RUNTIME_MODE === "container";
@@ -7924,8 +6932,7 @@ var Runner = class extends import_node_events3.EventEmitter {
7924
6932
  // an actual agent process.
7925
6933
  attachMemory: this.memoryWiring ? attachMemoryRpc({ runtime: this.memoryWiring.runtime }) : void 0,
7926
6934
  attachAsset: attachAssetRpc({
7927
- resolveIndex: (workspaceId) => this.assetMetadataIndexes.get(workspaceId),
7928
- assetCache: this.assetCache
6935
+ resolveIndex: (workspaceId) => this.assetMetadataIndexes.get(workspaceId)
7929
6936
  })
7930
6937
  });
7931
6938
  await this.localServer.start();
@@ -7974,7 +6981,6 @@ var Runner = class extends import_node_events3.EventEmitter {
7974
6981
  this.memoryWiring = void 0;
7975
6982
  this.ws?.close();
7976
6983
  this.outboxWatcher?.stop();
7977
- this.stopDropFolderRuntime();
7978
6984
  await this.servicePool?.shutdown();
7979
6985
  await this.localServer?.stop();
7980
6986
  try {
@@ -8031,7 +7037,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8031
7037
  runningTaskIds: Array.from(this.runningTasks.keys()),
8032
7038
  observability: {
8033
7039
  adapters: this.snapshotAdapterObservability(),
8034
- assetSync: this.snapshotAssetSyncObservability(),
8035
7040
  ...this.lastTaskError ? { lastTaskError: this.lastTaskError } : {}
8036
7041
  }
8037
7042
  };
@@ -8067,35 +7072,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8067
7072
  });
8068
7073
  }
8069
7074
  }
8070
- async prepareLocalProfiles() {
8071
- const rows = this.db.prepare(
8072
- `SELECT id, workspace_id, agent_im_user_id, adapter_name, name, config, version, synced_at
8073
- FROM agent_profiles
8074
- WHERE deleted_at IS NULL`
8075
- ).all();
8076
- for (const row of rows) {
8077
- const adapter = this.registry.get(row.adapter_name);
8078
- if (!adapter?.prepareProfile) continue;
8079
- try {
8080
- await adapter.prepareProfile({
8081
- id: row.id,
8082
- workspaceId: row.workspace_id,
8083
- agentImUserId: row.agent_im_user_id,
8084
- adapterName: row.adapter_name,
8085
- name: row.name,
8086
- config: parseProfileConfig(row.config),
8087
- version: row.version,
8088
- createdAt: new Date(row.synced_at ?? Date.now()),
8089
- updatedAt: new Date(row.synced_at ?? Date.now())
8090
- });
8091
- } catch (err) {
8092
- process.stderr.write(
8093
- `[daemon] local profile preflight skipped agent=${row.agent_im_user_id} profile=${row.id}: ${err.message}
8094
- `
8095
- );
8096
- }
8097
- }
8098
- }
8099
7075
  /**
8100
7076
  * Register an in-process AgentProfile snapshot (called by agent CLI / sync layer).
8101
7077
  * Used to populate the `agents` list in agent.host.declare.
@@ -8153,10 +7129,10 @@ var Runner = class extends import_node_events3.EventEmitter {
8153
7129
  const rawJson = process.env.PRISMER_HOSTED_AGENT_JSON;
8154
7130
  let raw;
8155
7131
  if (rawFile) {
8156
- if (!(0, import_node_fs14.existsSync)(rawFile)) {
7132
+ if (!(0, import_node_fs11.existsSync)(rawFile)) {
8157
7133
  throw new Error(`PRISMER_HOSTED_AGENT_FILE not found: ${rawFile}`);
8158
7134
  }
8159
- raw = (0, import_node_fs14.readFileSync)(rawFile, "utf8");
7135
+ raw = (0, import_node_fs11.readFileSync)(rawFile, "utf8");
8160
7136
  } else if (rawJson) {
8161
7137
  raw = rawJson;
8162
7138
  }
@@ -8252,13 +7228,13 @@ var Runner = class extends import_node_events3.EventEmitter {
8252
7228
  const payload = {
8253
7229
  daemonId: this.config.daemon_id,
8254
7230
  daemonVersion: this.opts.daemonVersion ?? "0.0.0",
8255
- platform: (0, import_node_os6.platform)() === "win32" ? "win32" : (0, import_node_os6.platform)() === "linux" ? "linux" : "darwin",
7231
+ platform: (0, import_node_os4.platform)() === "win32" ? "win32" : (0, import_node_os4.platform)() === "linux" ? "linux" : "darwin",
8256
7232
  agents: Array.from(this.hostedAgents.values()).map((a) => ({
8257
7233
  imUserId: a.imUserId,
8258
7234
  name: a.name,
8259
7235
  adapterName: a.adapterName,
8260
7236
  capabilities: a.capabilities,
8261
- profiles: Array.from(a.profiles.entries()).map(([id, version]) => ({ id, version }))
7237
+ profiles: Array.from(a.profiles.entries()).map(([id, version2]) => ({ id, version: version2 }))
8262
7238
  }))
8263
7239
  };
8264
7240
  this.ws.send(envelope("agent.host.declare", payload, this.config.daemon_id));
@@ -8300,29 +7276,17 @@ var Runner = class extends import_node_events3.EventEmitter {
8300
7276
  (err) => console.error("[Daemon] Initial memory sync failed:", err.message)
8301
7277
  );
8302
7278
  }
8303
- this.syncAssetState(this.workspaceId).catch(
8304
- (err) => console.error("[Daemon] Asset sync failed:", err.message)
7279
+ this.syncAssetMetadata(this.workspaceId).catch(
7280
+ (err) => console.error("[Daemon] Asset metadata sync failed:", err.message)
8305
7281
  );
8306
- await this.ensureDropFolderRuntime(this.workspaceId);
8307
7282
  for (const id of payload.profilesToSync) {
8308
7283
  try {
8309
- await this.servicePool.drop(id);
8310
7284
  await this.syncProfileFromCloud(id);
8311
7285
  } catch (err) {
8312
7286
  this.emit("sync-error", err);
8313
7287
  }
8314
7288
  }
8315
- for (const id of payload.profilesToDelete ?? []) {
8316
- const row = this.db.prepare("SELECT agent_im_user_id FROM agent_profiles WHERE id = ?").get(id);
8317
- this.db.prepare("DELETE FROM agent_profiles WHERE id = ?").run(id);
8318
- if (row) {
8319
- this.db.prepare("DELETE FROM agents WHERE im_user_id = ?").run(row.agent_im_user_id);
8320
- }
8321
- process.stderr.write(`[daemon] tombstone profile=${id}
8322
- `);
8323
- }
8324
- if (payload.profilesToDelete?.length) this.loadAgentsFromDb();
8325
- if ((payload.profilesToSync.length > 0 || (payload.profilesToDelete?.length ?? 0) > 0) && this.wsConnected) this.sendDeclare();
7289
+ if (payload.profilesToSync.length > 0 && this.wsConnected) this.sendDeclare();
8326
7290
  this.emit("host-acked", payload);
8327
7291
  }
8328
7292
  async onTaskDispatch(payload, requestId) {
@@ -8363,14 +7327,8 @@ var Runner = class extends import_node_events3.EventEmitter {
8363
7327
  }
8364
7328
  });
8365
7329
  this.ws.send(envelope("task.dispatch.reply", reply, requestId));
8366
- if (!reply.ok) {
8367
- const code = reply.error?.code ?? "unknown";
8368
- const message = reply.error?.message ?? "unknown error";
8369
- process.stderr.write(`[daemon] dispatch failed task=${payload.taskId} code=${code} message=${message}
8370
- `);
8371
- }
8372
7330
  } else {
8373
- const reply = await handleDispatch(payload, requestId, {
7331
+ await handleDispatch(payload, requestId, {
8374
7332
  registry: this.registry,
8375
7333
  cloud: this.cloud,
8376
7334
  uriResolver: this.uriResolver,
@@ -8386,12 +7344,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8386
7344
  if (running) running.lastProgressAt = Date.now();
8387
7345
  }
8388
7346
  });
8389
- if (!reply.ok) {
8390
- const code = reply.error?.code ?? "unknown";
8391
- const message = reply.error?.message ?? "unknown error";
8392
- process.stderr.write(`[daemon] dispatch failed task=${payload.taskId} code=${code} message=${message}
8393
- `);
8394
- }
8395
7347
  }
8396
7348
  process.stdout.write(`[daemon] dispatch done task=${payload.taskId}
8397
7349
  `);
@@ -8419,7 +7371,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8419
7371
  }
8420
7372
  async onAgentProfileChanged(payload) {
8421
7373
  try {
8422
- await this.servicePool.drop(payload.profileId);
8423
7374
  await this.syncProfileFromCloud(payload.profileId);
8424
7375
  if (this.wsConnected) this.sendDeclare();
8425
7376
  } catch (err) {
@@ -8427,21 +7378,7 @@ var Runner = class extends import_node_events3.EventEmitter {
8427
7378
  }
8428
7379
  }
8429
7380
  async syncProfileFromCloud(profileId) {
8430
- let profile;
8431
- try {
8432
- profile = await this.cloud.get(`/api/im/agent_profiles/${encodeURIComponent(profileId)}`);
8433
- } catch (err) {
8434
- if (err instanceof CloudError && err.status === 404) {
8435
- const row = this.db.prepare("SELECT agent_im_user_id FROM agent_profiles WHERE id = ?").get(profileId);
8436
- this.db.prepare("DELETE FROM agent_profiles WHERE id = ?").run(profileId);
8437
- if (row) {
8438
- this.db.prepare("DELETE FROM agents WHERE im_user_id = ?").run(row.agent_im_user_id);
8439
- }
8440
- this.loadAgentsFromDb();
8441
- return;
8442
- }
8443
- throw err;
8444
- }
7381
+ const profile = await this.cloud.get(`/api/im/agent_profiles/${encodeURIComponent(profileId)}`);
8445
7382
  const agent = await this.resolveOwnedAgent(profile.agentImUserId);
8446
7383
  const adapter = this.registry.get(profile.adapterName);
8447
7384
  const capabilities = agent?.card?.capabilities?.length ? agent.card.capabilities : adapter?.capabilities ?? [];
@@ -8476,14 +7413,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8476
7413
  );
8477
7414
  });
8478
7415
  tx();
8479
- try {
8480
- await adapter?.prepareProfile?.(profile);
8481
- } catch (err) {
8482
- process.stderr.write(
8483
- `[daemon] profile preflight skipped agent=${profile.agentImUserId} profile=${profile.id}: ${err.message}
8484
- `
8485
- );
8486
- }
8487
7416
  this.loadAgentsFromDb();
8488
7417
  process.stdout.write(
8489
7418
  `[daemon] profile synced agent=${profile.agentImUserId} profile=${profile.id} adapter=${profile.adapterName}
@@ -8512,10 +7441,15 @@ var Runner = class extends import_node_events3.EventEmitter {
8512
7441
  }
8513
7442
  async onAssetChanged(payload) {
8514
7443
  if (!payload.workspaceId) return;
7444
+ const index = this.assetMetadataIndexes.get(payload.workspaceId);
7445
+ if (!index) return;
8515
7446
  try {
8516
- await this.syncAssetState(payload.workspaceId);
7447
+ const result = await index.pullDelta();
7448
+ if (result.applied > 0) {
7449
+ console.log(`[Daemon] asset.changed workspace=${payload.workspaceId} applied=${result.applied}`);
7450
+ }
8517
7451
  } catch (err) {
8518
- console.error(`[Daemon] asset.changed sync failed workspace=${payload.workspaceId}:`, err.message);
7452
+ console.error(`[Daemon] asset.changed pullDelta failed workspace=${payload.workspaceId}:`, err.message);
8519
7453
  }
8520
7454
  }
8521
7455
  onWorkspaceFileChanged(payload) {
@@ -8553,163 +7487,28 @@ var Runner = class extends import_node_events3.EventEmitter {
8553
7487
  }
8554
7488
  };
8555
7489
  }
8556
- snapshotAssetSyncObservability() {
8557
- return {
8558
- workspaceId: this.dropFolderWorkspaceId || null,
8559
- rootDir: this.dropFolderWorkspaceDir || null,
8560
- dropDir: this.dropFolderWorkspaceDir ? (0, import_node_path10.join)(this.dropFolderWorkspaceDir, "drop") : null,
8561
- uploadedDir: this.dropFolderWorkspaceDir ? (0, import_node_path10.join)(this.dropFolderWorkspaceDir, "uploaded") : null,
8562
- failedDir: this.dropFolderWorkspaceDir ? (0, import_node_path10.join)(this.dropFolderWorkspaceDir, "upload-failed") : null
8563
- };
8564
- }
8565
7490
  /**
8566
7491
  * Ensure an AssetMetadataIndex exists for the given workspace and pull
8567
7492
  * delta from cloud. Idempotent — creates the index on first call, reuses
8568
- * it on subsequent calls. Same cursor-catch-up semantics as WorkspaceMirror.
8569
- */
8570
- async syncAssetMetadata(workspaceId) {
8571
- let index = this.assetMetadataIndexes.get(workspaceId);
8572
- if (!index) {
8573
- const stateDir = `${this.paths.root}/${workspaceId}`;
8574
- index = new AssetMetadataIndex({
8575
- db: this.db,
8576
- cloud: this.cloud,
8577
- workspaceId,
8578
- workspaceStateDir: stateDir
8579
- });
8580
- this.assetMetadataIndexes.set(workspaceId, index);
8581
- }
8582
- const result = await index.pullDelta();
8583
- if (result.applied > 0) {
8584
- console.log(`[AssetMeta] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor}`);
8585
- }
8586
- }
8587
- /**
8588
- * Ensure a WorkspaceMirror exists for the workspace and pull path→asset
8589
- * bindings from cloud. Cold-start daemons previously only had this table
8590
- * populated by WS push or per-path URI fallback, so local asset state looked
8591
- * empty even though cloud files existed.
7493
+ * it on subsequent calls. Same cursor-catch-up semantics as WorkspaceMirror.
8592
7494
  */
8593
- async syncWorkspaceFiles(workspaceId) {
8594
- let mirror = this.workspaceMirrors.get(workspaceId);
8595
- if (!mirror) {
7495
+ async syncAssetMetadata(workspaceId) {
7496
+ let index = this.assetMetadataIndexes.get(workspaceId);
7497
+ if (!index) {
8596
7498
  const stateDir = `${this.paths.root}/${workspaceId}`;
8597
- mirror = new WorkspaceMirror({
7499
+ index = new AssetMetadataIndex({
8598
7500
  db: this.db,
8599
7501
  cloud: this.cloud,
8600
7502
  workspaceId,
8601
7503
  workspaceStateDir: stateDir
8602
7504
  });
8603
- this.workspaceMirrors.set(workspaceId, mirror);
7505
+ this.assetMetadataIndexes.set(workspaceId, index);
8604
7506
  }
8605
- const result = await mirror.pullDelta();
7507
+ const result = await index.pullDelta();
8606
7508
  if (result.applied > 0) {
8607
- console.log(`[WorkspaceFiles] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor ?? "null"}`);
8608
- }
8609
- }
8610
- async syncAssetState(workspaceId) {
8611
- if (!this.paths?.root) return;
8612
- await Promise.all([
8613
- this.syncAssetMetadata(workspaceId),
8614
- this.syncWorkspaceFiles(workspaceId)
8615
- ]);
8616
- }
8617
- /**
8618
- * Desktop asset ingress: watch a user-visible local asset folder
8619
- * and upload new files as IMAssets, preserving nested relative dirs in
8620
- * IMAsset.folderPath. The lower-level adapter/outbox/uploader already
8621
- * existed; this method wires them into the real daemon lifecycle.
8622
- */
8623
- async ensureDropFolderRuntime(workspaceId) {
8624
- if (!workspaceId || !this.paths?.root) return;
8625
- if (this.dropFolderWorkspaceId === workspaceId && this.dropFolderTimer) return;
8626
- this.stopDropFolderRuntime();
8627
- this.dropFolderWorkspaceId = workspaceId;
8628
- const workspaceRoot = resolveUserAssetWorkspaceDir(workspaceId);
8629
- this.dropFolderWorkspaceDir = workspaceRoot;
8630
- await (0, import_promises.mkdir)((0, import_node_path10.join)(workspaceRoot, "drop"), { recursive: true });
8631
- await (0, import_promises.mkdir)((0, import_node_path10.join)(workspaceRoot, "uploaded"), { recursive: true });
8632
- await (0, import_promises.mkdir)((0, import_node_path10.join)(workspaceRoot, "upload-failed"), { recursive: true });
8633
- this.assetOriginOutbox = new OriginOutbox({ dbPath: (0, import_node_path10.join)(this.paths.root, "asset-origin.db") });
8634
- this.dropFolderAdapter = new DropFolderAdapter({ workspaceDir: workspaceRoot });
8635
- this.dropFolderUploadRunner = new UploadRunner({
8636
- outbox: this.assetOriginOutbox,
8637
- cloud: new DaemonAssetUploadClient(this.config),
8638
- adapters: { "drop-folder": this.dropFolderAdapter },
8639
- maxAttempts: 3
8640
- });
8641
- const tick = () => {
8642
- this.runDropFolderTick().catch((err) => {
8643
- process.stderr.write(`[daemon] drop-folder sync failed: ${err.message}
8644
- `);
8645
- });
8646
- };
8647
- this.dropFolderTimer = setInterval(tick, 1e3);
8648
- tick();
8649
- process.stdout.write(`[daemon] drop-folder asset sync watching ${(0, import_node_path10.join)(workspaceRoot, "drop")}
8650
- `);
8651
- }
8652
- stopDropFolderRuntime() {
8653
- if (this.dropFolderTimer) {
8654
- clearInterval(this.dropFolderTimer);
8655
- this.dropFolderTimer = void 0;
8656
- }
8657
- try {
8658
- this.assetOriginOutbox?.close();
8659
- } catch {
8660
- }
8661
- this.assetOriginOutbox = void 0;
8662
- this.dropFolderAdapter = void 0;
8663
- this.dropFolderUploadRunner = void 0;
8664
- this.dropFolderWorkspaceId = "";
8665
- this.dropFolderWorkspaceDir = "";
8666
- this.dropFolderTickInFlight = false;
8667
- this.dropFolderStable.clear();
8668
- }
8669
- async runDropFolderTick() {
8670
- if (this.dropFolderTickInFlight) return;
8671
- const workspaceId = this.dropFolderWorkspaceId;
8672
- const adapter = this.dropFolderAdapter;
8673
- const outbox = this.assetOriginOutbox;
8674
- const runner = this.dropFolderUploadRunner;
8675
- if (!workspaceId || !adapter || !outbox || !runner) return;
8676
- this.dropFolderTickInFlight = true;
8677
- try {
8678
- const observations = await adapter.scanOnce(workspaceId);
8679
- for (const obs of observations) {
8680
- const id = await adapter.identifySource(obs);
8681
- if (!this.isDropFolderObservationStable(id.sourceRef, obs.detail)) continue;
8682
- outbox.enqueue({
8683
- workspaceId: obs.workspaceId,
8684
- originKind: "drop-folder",
8685
- sourceRef: id.sourceRef,
8686
- payloadJson: JSON.stringify(obs.detail),
8687
- hintsJson: JSON.stringify(id.hints ?? {}),
8688
- observedAt: obs.observedAt
8689
- });
8690
- }
8691
- const processed = await runner.drainOnce();
8692
- if (processed > 0) {
8693
- this.dropFolderStable.clear();
8694
- process.stdout.write(`[daemon] drop-folder uploaded/processed ${processed} asset(s)
8695
- `);
8696
- await this.syncAssetState(workspaceId).catch(
8697
- (err) => process.stderr.write(`[daemon] drop-folder post-sync failed: ${err.message}
8698
- `)
8699
- );
8700
- }
8701
- } finally {
8702
- this.dropFolderTickInFlight = false;
7509
+ console.log(`[AssetMeta] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor}`);
8703
7510
  }
8704
7511
  }
8705
- isDropFolderObservationStable(sourceRef, detail) {
8706
- const raw = detail && typeof detail === "object" ? detail : {};
8707
- const size = typeof raw.size === "number" ? raw.size : -1;
8708
- const mtime = typeof raw.mtime === "number" ? raw.mtime : -1;
8709
- const previous = this.dropFolderStable.get(sourceRef);
8710
- this.dropFolderStable.set(sourceRef, { size, mtime });
8711
- return Boolean(previous && previous.size === size && previous.mtime === mtime);
8712
- }
8713
7512
  /**
8714
7513
  * SyncWorker FlushFn — pushes local writes to cloud.
8715
7514
  *
@@ -8746,44 +7545,44 @@ var Runner = class extends import_node_events3.EventEmitter {
8746
7545
  const id = encodeURIComponent(row.resource_id);
8747
7546
  const body = row.operation === "delete" ? void 0 : safeJsonParse(row.payload);
8748
7547
  let method;
8749
- let path9;
7548
+ let path7;
8750
7549
  switch (op) {
8751
7550
  case "workspace.create":
8752
7551
  method = "POST";
8753
- path9 = "/api/im/workspaces";
7552
+ path7 = "/api/im/workspaces";
8754
7553
  break;
8755
7554
  case "workspace.update":
8756
7555
  method = "PATCH";
8757
- path9 = `/api/im/workspaces/${id}`;
7556
+ path7 = `/api/im/workspaces/${id}`;
8758
7557
  break;
8759
7558
  case "workspace.delete":
8760
7559
  method = "DELETE";
8761
- path9 = `/api/im/workspaces/${id}`;
7560
+ path7 = `/api/im/workspaces/${id}`;
8762
7561
  break;
8763
7562
  // agent.create must use /register — POST /api/im/agents is not exposed.
8764
7563
  case "agent.create":
8765
7564
  method = "POST";
8766
- path9 = "/api/im/register";
7565
+ path7 = "/api/im/register";
8767
7566
  break;
8768
7567
  case "agent.update":
8769
7568
  method = "PATCH";
8770
- path9 = `/api/im/agents/${id}`;
7569
+ path7 = `/api/im/agents/${id}`;
8771
7570
  break;
8772
7571
  case "agent.delete":
8773
7572
  method = "DELETE";
8774
- path9 = `/api/im/agents/${id}`;
7573
+ path7 = `/api/im/agents/${id}`;
8775
7574
  break;
8776
7575
  case "agent_profile.create":
8777
7576
  method = "POST";
8778
- path9 = "/api/im/agent_profiles";
7577
+ path7 = "/api/im/agent_profiles";
8779
7578
  break;
8780
7579
  case "agent_profile.update":
8781
7580
  method = "PATCH";
8782
- path9 = `/api/im/agent_profiles/${id}`;
7581
+ path7 = `/api/im/agent_profiles/${id}`;
8783
7582
  break;
8784
7583
  case "agent_profile.delete":
8785
7584
  method = "DELETE";
8786
- path9 = `/api/im/agent_profiles/${id}`;
7585
+ path7 = `/api/im/agent_profiles/${id}`;
8787
7586
  break;
8788
7587
  default:
8789
7588
  process.stderr.write(`[daemon] sync flush op=${op} id=${row.id} result=drop (unknown op)
@@ -8792,7 +7591,7 @@ var Runner = class extends import_node_events3.EventEmitter {
8792
7591
  }
8793
7592
  let res;
8794
7593
  try {
8795
- res = await this.cloud.request(method, path9, { body });
7594
+ res = await this.cloud.request(method, path7, { body });
8796
7595
  } catch (err) {
8797
7596
  process.stderr.write(
8798
7597
  `[daemon] sync flush op=${op} id=${row.id} result=retry (threw: ${err.message})
@@ -8815,89 +7614,6 @@ var Runner = class extends import_node_events3.EventEmitter {
8815
7614
  return { ok: res.ok, status: res.status, message: res.error?.message };
8816
7615
  }
8817
7616
  };
8818
- var DaemonAssetUploadClient = class {
8819
- constructor(config) {
8820
- this.config = config;
8821
- }
8822
- config;
8823
- async uploadAsset(req) {
8824
- const form = new FormData();
8825
- form.set("workspaceId", req.workspaceId);
8826
- form.set("kind", "file");
8827
- form.set("metadata", JSON.stringify(req.metadata));
8828
- if (req.folderPath !== void 0) form.set("folderPath", req.folderPath);
8829
- form.set("file", new Blob([req.bytes], { type: req.mime ?? "application/octet-stream" }), req.filename);
8830
- const url = `${this.config.cloud_api_base.replace(/\/$/, "")}/api/im/assets`;
8831
- const controller = new AbortController();
8832
- const timer = setTimeout(() => controller.abort(), 6e4);
8833
- let res;
8834
- try {
8835
- res = await fetch(url, {
8836
- method: "POST",
8837
- headers: { Authorization: `Bearer ${this.config.api_key}` },
8838
- body: form,
8839
- signal: controller.signal
8840
- });
8841
- } catch (err) {
8842
- if (err.name === "AbortError") {
8843
- throw new Error("asset upload timed out after 60000ms");
8844
- }
8845
- throw err;
8846
- } finally {
8847
- clearTimeout(timer);
8848
- }
8849
- const text = await res.text();
8850
- let body;
8851
- try {
8852
- body = text ? JSON.parse(text) : void 0;
8853
- } catch {
8854
- body = text;
8855
- }
8856
- if (!res.ok || isCloudErrorEnvelope(body)) {
8857
- throw new Error(`asset upload failed (${res.status}): ${cloudUploadErrorMessage(body)}`);
8858
- }
8859
- const data = unwrapCloudData(body);
8860
- const assetId = stringValue(data, "id");
8861
- const contentHash = stringValue(data, "contentHash");
8862
- if (!assetId || !contentHash) {
8863
- throw new Error("asset upload response missing id/contentHash");
8864
- }
8865
- return { assetId, contentHash };
8866
- }
8867
- };
8868
- function isCloudErrorEnvelope(raw) {
8869
- return Boolean(raw && typeof raw === "object" && "ok" in raw && raw.ok === false);
8870
- }
8871
- function cloudUploadErrorMessage(raw) {
8872
- if (raw && typeof raw === "object") {
8873
- const error = raw.error;
8874
- if (typeof error === "string") return error;
8875
- if (error && typeof error === "object") {
8876
- const message = error.message;
8877
- if (typeof message === "string") return message;
8878
- }
8879
- }
8880
- return typeof raw === "string" ? raw : "request failed";
8881
- }
8882
- function unwrapCloudData(raw) {
8883
- if (raw && typeof raw === "object") {
8884
- const data = raw.data;
8885
- if (data && typeof data === "object" && !Array.isArray(data)) return data;
8886
- }
8887
- return {};
8888
- }
8889
- function stringValue(obj, key) {
8890
- const value = obj[key];
8891
- return typeof value === "string" && value.length > 0 ? value : void 0;
8892
- }
8893
- function resolveUserAssetWorkspaceDir(workspaceId) {
8894
- const override = process.env.PRISMER_ASSET_SYNC_ROOT;
8895
- if (override) return (0, import_node_path10.join)(override, workspaceId);
8896
- const home = (0, import_node_os6.homedir)();
8897
- const desktop = (0, import_node_path10.join)(home, "Desktop");
8898
- const base = (0, import_node_fs14.existsSync)(desktop) ? desktop : home;
8899
- return (0, import_node_path10.join)(base, "Prismer Assets", workspaceId);
8900
- }
8901
7617
  function readTargetDaemonId(payload) {
8902
7618
  if (typeof payload.targetDaemonId === "string" && payload.targetDaemonId.length > 0) {
8903
7619
  return payload.targetDaemonId;
@@ -8948,14 +7664,6 @@ function validateStaticHostedAgent(raw) {
8948
7664
  }
8949
7665
  };
8950
7666
  }
8951
- function parseProfileConfig(raw) {
8952
- try {
8953
- const parsed = JSON.parse(raw);
8954
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
8955
- } catch {
8956
- return {};
8957
- }
8958
- }
8959
7667
  function safeJsonParse(raw) {
8960
7668
  try {
8961
7669
  return JSON.parse(raw);
@@ -8965,9 +7673,9 @@ function safeJsonParse(raw) {
8965
7673
  }
8966
7674
 
8967
7675
  // src/pair.ts
8968
- var import_node_crypto9 = require("crypto");
8969
- var import_node_os7 = require("os");
8970
- var import_promises2 = require("timers/promises");
7676
+ var import_node_crypto5 = require("crypto");
7677
+ var import_node_os5 = require("os");
7678
+ var import_promises = require("timers/promises");
8971
7679
  var import_qrcode = __toESM(require("qrcode"), 1);
8972
7680
  async function pair(opts) {
8973
7681
  const paths = opts.paths ?? resolvePaths();
@@ -8983,7 +7691,7 @@ async function pair(opts) {
8983
7691
  "pair: --as-user requires LOCAL_ONLY=1. Without that gate, this would skip mobile approval and silently mint a key for the named user."
8984
7692
  );
8985
7693
  }
8986
- const { publicKey } = (0, import_node_crypto9.generateKeyPairSync)("ed25519");
7694
+ const { publicKey } = (0, import_node_crypto5.generateKeyPairSync)("ed25519");
8987
7695
  const devicePub = publicKey.export({ format: "der", type: "spki" }).toString("base64");
8988
7696
  const cloud = new CloudClient({
8989
7697
  baseUrl: opts.cloudBaseUrl,
@@ -8996,7 +7704,7 @@ async function pair(opts) {
8996
7704
  "/api/im/pair/offer",
8997
7705
  {
8998
7706
  auth: false,
8999
- body: { devicePub, deviceName: opts.deviceName ?? (0, import_node_os7.hostname)() }
7707
+ body: { devicePub, deviceName: opts.deviceName ?? (0, import_node_os5.hostname)() }
9000
7708
  }
9001
7709
  );
9002
7710
  if (!offerRes.ok) {
@@ -9036,7 +7744,7 @@ Scan with Lumin to approve, or open: ${offer.qrUrl}
9036
7744
  const pollIntervalMs = opts.pollIntervalMs ?? 5e3;
9037
7745
  for (let i = 0; i < maxAttempts; i += 1) {
9038
7746
  if (i > 0 || !localOnlyMode) {
9039
- await (0, import_promises2.setTimeout)(pollIntervalMs);
7747
+ await (0, import_promises.setTimeout)(pollIntervalMs);
9040
7748
  }
9041
7749
  const res = await cloud.request(
9042
7750
  "GET",
@@ -9073,13 +7781,13 @@ function unwrapEnvelope(raw) {
9073
7781
  }
9074
7782
 
9075
7783
  // src/cli/index.ts
9076
- var import_commander19 = require("commander");
7784
+ var import_commander18 = require("commander");
9077
7785
 
9078
7786
  // src/cli/commands/adapter.ts
9079
7787
  var import_node_child_process5 = require("child_process");
9080
- var import_node_fs16 = require("fs");
9081
- var import_node_os8 = require("os");
9082
- var import_node_path12 = require("path");
7788
+ var import_node_fs13 = require("fs");
7789
+ var import_node_os6 = require("os");
7790
+ var import_node_path9 = require("path");
9083
7791
  var import_commander = require("commander");
9084
7792
  init_util();
9085
7793
  init_ui();
@@ -9092,7 +7800,7 @@ var INSTALL_SPECS = {
9092
7800
  binary: "claude",
9093
7801
  hint: "Set ANTHROPIC_API_KEY in your shell profile, or rely on Claude Code OAuth login. Run `claude login` to sign in.",
9094
7802
  authHints: ["ANTHROPIC_API_KEY or Claude Code OAuth login (`claude login`)"],
9095
- hookTarget: { path: (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".claude", "hooks.json"), kind: "json-file" }
7803
+ hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".claude", "hooks.json"), kind: "json-file" }
9096
7804
  },
9097
7805
  openclaw: {
9098
7806
  name: "openclaw",
@@ -9101,7 +7809,7 @@ var INSTALL_SPECS = {
9101
7809
  binary: "openclaw",
9102
7810
  hint: "OpenClaw runs as a gateway. Configure ~/.openclaw/openclaw.json and start it with `openclaw gateway` before tasks dispatch.",
9103
7811
  authHints: ["~/.openclaw/openclaw.json gateway.auth.bearerTokens", "Prismer daemon api_key"],
9104
- hookTarget: { path: (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".openclaw", "hooks"), kind: "directory" }
7812
+ hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "hooks"), kind: "directory" }
9105
7813
  },
9106
7814
  codex: {
9107
7815
  name: "codex",
@@ -9110,7 +7818,7 @@ var INSTALL_SPECS = {
9110
7818
  binary: "codex",
9111
7819
  hint: "Set OPENAI_API_KEY in your shell profile. Verify with `codex --help`.",
9112
7820
  authHints: ["OPENAI_API_KEY or Codex CLI account login"],
9113
- hookTarget: { path: (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".codex", "hooks.json"), kind: "json-file" }
7821
+ hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".codex", "hooks.json"), kind: "json-file" }
9114
7822
  },
9115
7823
  hermes: {
9116
7824
  name: "hermes",
@@ -9119,7 +7827,7 @@ var INSTALL_SPECS = {
9119
7827
  binary: "hermes",
9120
7828
  hint: "Hermes runs as a long-lived HTTP gateway in your own Python venv. Start with `hermes -p <profile> gateway` after configuring ~/.hermes/.env.",
9121
7829
  authHints: ["~/.hermes/.env API_SERVER_KEY", "Hermes profile provider credentials"],
9122
- hookTarget: { path: (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".hermes", "hooks.json"), kind: "json-file" }
7830
+ hookTarget: { path: (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".hermes", "hooks.json"), kind: "json-file" }
9123
7831
  }
9124
7832
  };
9125
7833
  function buildAdapterCommand() {
@@ -9207,8 +7915,8 @@ function buildAdapterCommand() {
9207
7915
  });
9208
7916
  return cmd;
9209
7917
  }
9210
- function runInstall(spec, version, dryRun) {
9211
- const { argv0, args } = installCommand(spec, version);
7918
+ function runInstall(spec, version2, dryRun) {
7919
+ const { argv0, args } = installCommand(spec, version2);
9212
7920
  getUI().line(`> ${argv0} ${args.join(" ")}`);
9213
7921
  if (dryRun) {
9214
7922
  getUI().line("(dry-run \u2014 not executing)");
@@ -9334,15 +8042,15 @@ function runHooks(spec, opts) {
9334
8042
  }
9335
8043
  let backup;
9336
8044
  if (planned.kind === "directory") {
9337
- (0, import_node_fs16.mkdirSync)(planned.path, { recursive: true });
9338
- const markerPath = (0, import_node_path12.join)(planned.path, "prismer.json");
8045
+ (0, import_node_fs13.mkdirSync)(planned.path, { recursive: true });
8046
+ const markerPath = (0, import_node_path9.join)(planned.path, "prismer.json");
9339
8047
  backup = backupIfExists(markerPath);
9340
- (0, import_node_fs16.writeFileSync)(markerPath, JSON.stringify(prismerHookMarker(spec), null, 2) + "\n", "utf8");
8048
+ (0, import_node_fs13.writeFileSync)(markerPath, JSON.stringify(prismerHookMarker(spec), null, 2) + "\n", "utf8");
9341
8049
  } else {
9342
- (0, import_node_fs16.mkdirSync)((0, import_node_path12.dirname)(planned.path), { recursive: true });
8050
+ (0, import_node_fs13.mkdirSync)((0, import_node_path9.dirname)(planned.path), { recursive: true });
9343
8051
  backup = backupIfExists(planned.path);
9344
8052
  const merged = mergeHookJson(planned.path, spec);
9345
- (0, import_node_fs16.writeFileSync)(planned.path, JSON.stringify(merged, null, 2) + "\n", "utf8");
8053
+ (0, import_node_fs13.writeFileSync)(planned.path, JSON.stringify(merged, null, 2) + "\n", "utf8");
9346
8054
  }
9347
8055
  return {
9348
8056
  ok: true,
@@ -9352,16 +8060,16 @@ function runHooks(spec, opts) {
9352
8060
  hook: inspectHook(spec)
9353
8061
  };
9354
8062
  }
9355
- function installCommand(spec, version) {
9356
- const ref = version === "latest" ? spec.package : `${spec.package}@${version}`;
8063
+ function installCommand(spec, version2) {
8064
+ const ref = version2 === "latest" ? spec.package : `${spec.package}@${version2}`;
9357
8065
  switch (spec.manager) {
9358
8066
  case "npm":
9359
8067
  return { argv0: "npm", args: ["install", "-g", ref] };
9360
8068
  case "pipx":
9361
- const pyRef = version === "latest" ? spec.package : `${spec.package}==${version}`;
8069
+ const pyRef = version2 === "latest" ? spec.package : `${spec.package}==${version2}`;
9362
8070
  return { argv0: "pipx", args: ["install", pyRef] };
9363
8071
  case "pip":
9364
- const pipRef = version === "latest" ? spec.package : `${spec.package}==${version}`;
8072
+ const pipRef = version2 === "latest" ? spec.package : `${spec.package}==${version2}`;
9365
8073
  return { argv0: "pip", args: ["install", "--user", pipRef] };
9366
8074
  }
9367
8075
  }
@@ -9409,23 +8117,23 @@ function inspectAuthEnv(spec) {
9409
8117
  hints: spec.authHints ?? []
9410
8118
  };
9411
8119
  case "hermes": {
9412
- const envFile = (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".hermes", ".env");
8120
+ const envFile = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".hermes", ".env");
9413
8121
  return {
9414
- ok: (0, import_node_fs16.existsSync)(envFile) || Boolean(process.env.HERMES_API_KEY || process.env.API_SERVER_KEY),
8122
+ ok: (0, import_node_fs13.existsSync)(envFile) || Boolean(process.env.HERMES_API_KEY || process.env.API_SERVER_KEY),
9415
8123
  present: [
9416
8124
  ...["HERMES_API_KEY", "API_SERVER_KEY"].filter((k) => Boolean(process.env[k])),
9417
- ...(0, import_node_fs16.existsSync)(envFile) ? [envFile] : []
8125
+ ...(0, import_node_fs13.existsSync)(envFile) ? [envFile] : []
9418
8126
  ],
9419
8127
  hints: spec.authHints ?? []
9420
8128
  };
9421
8129
  }
9422
8130
  case "openclaw": {
9423
- const cfgFile = (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".openclaw", "openclaw.json");
8131
+ const cfgFile = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "openclaw.json");
9424
8132
  return {
9425
- ok: (0, import_node_fs16.existsSync)(cfgFile) || Boolean(process.env.OPENCLAW_API_KEY),
8133
+ ok: (0, import_node_fs13.existsSync)(cfgFile) || Boolean(process.env.OPENCLAW_API_KEY),
9426
8134
  present: [
9427
8135
  ...["OPENCLAW_API_KEY"].filter((k) => Boolean(process.env[k])),
9428
- ...(0, import_node_fs16.existsSync)(cfgFile) ? [cfgFile] : []
8136
+ ...(0, import_node_fs13.existsSync)(cfgFile) ? [cfgFile] : []
9429
8137
  ],
9430
8138
  hints: spec.authHints ?? []
9431
8139
  };
@@ -9440,32 +8148,32 @@ function inspectHook(spec) {
9440
8148
  return { supported: false, markerPresent: false };
9441
8149
  }
9442
8150
  if (spec.name === "openclaw") {
9443
- const jsonPath = (0, import_node_path12.join)((0, import_node_os8.homedir)(), ".openclaw", "hooks.json");
9444
- const markerPath = (0, import_node_path12.join)(target.path, "prismer.json");
9445
- const jsonMarkerPresent = (0, import_node_fs16.existsSync)(jsonPath) && fileContainsPrismerMarker(jsonPath);
9446
- const dirMarkerPresent = (0, import_node_fs16.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath);
8151
+ const jsonPath = (0, import_node_path9.join)((0, import_node_os6.homedir)(), ".openclaw", "hooks.json");
8152
+ const markerPath = (0, import_node_path9.join)(target.path, "prismer.json");
8153
+ const jsonMarkerPresent = (0, import_node_fs13.existsSync)(jsonPath) && fileContainsPrismerMarker(jsonPath);
8154
+ const dirMarkerPresent = (0, import_node_fs13.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath);
9447
8155
  return {
9448
8156
  supported: true,
9449
8157
  kind: "json-file-or-directory",
9450
8158
  path: target.path,
9451
8159
  jsonPath,
9452
- exists: (0, import_node_fs16.existsSync)(target.path) || (0, import_node_fs16.existsSync)(jsonPath),
8160
+ exists: (0, import_node_fs13.existsSync)(target.path) || (0, import_node_fs13.existsSync)(jsonPath),
9453
8161
  markerPath,
9454
8162
  markerPresent: jsonMarkerPresent || dirMarkerPresent
9455
8163
  };
9456
8164
  }
9457
8165
  if (target.kind === "directory") {
9458
- const markerPath = (0, import_node_path12.join)(target.path, "prismer.json");
8166
+ const markerPath = (0, import_node_path9.join)(target.path, "prismer.json");
9459
8167
  return {
9460
8168
  supported: true,
9461
8169
  kind: target.kind,
9462
8170
  path: target.path,
9463
- exists: (0, import_node_fs16.existsSync)(target.path),
8171
+ exists: (0, import_node_fs13.existsSync)(target.path),
9464
8172
  markerPath,
9465
- markerPresent: (0, import_node_fs16.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath)
8173
+ markerPresent: (0, import_node_fs13.existsSync)(markerPath) && fileContainsPrismerMarker(markerPath)
9466
8174
  };
9467
8175
  }
9468
- const exists = (0, import_node_fs16.existsSync)(target.path);
8176
+ const exists = (0, import_node_fs13.existsSync)(target.path);
9469
8177
  if (!exists) {
9470
8178
  return {
9471
8179
  supported: true,
@@ -9499,21 +8207,21 @@ function prismerHookMarker(spec) {
9499
8207
  installed_at: (/* @__PURE__ */ new Date()).toISOString()
9500
8208
  };
9501
8209
  }
9502
- function mergeHookJson(path9, spec) {
8210
+ function mergeHookJson(path7, spec) {
9503
8211
  const marker = prismerHookMarker(spec);
9504
- if (!(0, import_node_fs16.existsSync)(path9)) {
8212
+ if (!(0, import_node_fs13.existsSync)(path7)) {
9505
8213
  return {
9506
8214
  prismer: marker
9507
8215
  };
9508
8216
  }
9509
8217
  let parsed;
9510
8218
  try {
9511
- parsed = JSON.parse((0, import_node_fs16.readFileSync)(path9, "utf8"));
8219
+ parsed = JSON.parse((0, import_node_fs13.readFileSync)(path7, "utf8"));
9512
8220
  } catch (err) {
9513
- throw new Error(`Cannot parse ${path9} as JSON: ${err.message}`);
8221
+ throw new Error(`Cannot parse ${path7} as JSON: ${err.message}`);
9514
8222
  }
9515
8223
  if (!isRecord(parsed)) {
9516
- throw new Error(`Cannot merge ${path9}: expected a JSON object`);
8224
+ throw new Error(`Cannot merge ${path7}: expected a JSON object`);
9517
8225
  }
9518
8226
  const next = { ...parsed };
9519
8227
  next.prismer = marker;
@@ -9531,18 +8239,18 @@ function mergeHookJson(path9, spec) {
9531
8239
  }
9532
8240
  return next;
9533
8241
  }
9534
- function backupIfExists(path9) {
9535
- if (!(0, import_node_fs16.existsSync)(path9)) return null;
8242
+ function backupIfExists(path7) {
8243
+ if (!(0, import_node_fs13.existsSync)(path7)) return null;
9536
8244
  const suffix = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9537
- const backup = `${path9}.bak.${suffix}`;
9538
- const stat = (0, import_node_fs16.statSync)(path9);
8245
+ const backup = `${path7}.bak.${suffix}`;
8246
+ const stat = (0, import_node_fs13.statSync)(path7);
9539
8247
  if (stat.isDirectory()) return null;
9540
- (0, import_node_fs16.copyFileSync)(path9, backup);
8248
+ (0, import_node_fs13.copyFileSync)(path7, backup);
9541
8249
  return backup;
9542
8250
  }
9543
- function fileContainsPrismerMarker(path9) {
8251
+ function fileContainsPrismerMarker(path7) {
9544
8252
  try {
9545
- return (0, import_node_fs16.readFileSync)(path9, "utf8").includes("prismer-daemon-runtime");
8253
+ return (0, import_node_fs13.readFileSync)(path7, "utf8").includes("prismer-daemon-runtime");
9546
8254
  } catch {
9547
8255
  return false;
9548
8256
  }
@@ -9560,7 +8268,7 @@ function readAdapterConfig() {
9560
8268
  return {};
9561
8269
  }
9562
8270
  }
9563
- function recordInstallInConfig(spec, binPath, version) {
8271
+ function recordInstallInConfig(spec, binPath, version2) {
9564
8272
  const paths = resolvePaths();
9565
8273
  if (!configExists(paths)) {
9566
8274
  process.stderr.write(
@@ -9582,7 +8290,7 @@ function recordInstallInConfig(spec, binPath, version) {
9582
8290
  package_manager: spec.manager,
9583
8291
  package_name: spec.package,
9584
8292
  binary: binPath,
9585
- version,
8293
+ version: version2,
9586
8294
  installed_at: (/* @__PURE__ */ new Date()).toISOString()
9587
8295
  };
9588
8296
  saveConfig({ ...cfg, adapters }, paths);
@@ -9962,7 +8670,7 @@ async function fetchCloudProfiles(cloud, imUserId) {
9962
8670
  async function fetchLocalServer() {
9963
8671
  const paths = resolvePaths();
9964
8672
  const pid = readPidFile(paths);
9965
- if (!pid || !pidAlive2(pid)) return { ok: false, error: "daemon process is not running", agents: [] };
8673
+ if (!pid || !pidAlive(pid)) return { ok: false, error: "daemon process is not running", agents: [] };
9966
8674
  try {
9967
8675
  const [healthRes, agentsRes] = await Promise.all([
9968
8676
  fetch(`${LOCAL_SERVER}/healthz`, { signal: AbortSignal.timeout(1e3) }),
@@ -9986,38 +8694,19 @@ function whichBinary2(bin) {
9986
8694
 
9987
8695
  // src/cli/commands/asset.ts
9988
8696
  var import_commander3 = require("commander");
9989
- var import_node_fs17 = require("fs");
9990
- var import_node_path13 = require("path");
8697
+ var import_node_fs14 = require("fs");
8698
+ var import_node_path10 = require("path");
9991
8699
  init_util();
9992
8700
  init_ui();
9993
8701
  function buildAssetCommand() {
9994
8702
  const cmd = new import_commander3.Command("asset").description("Inspect IM assets");
9995
- cmd.command("sync").description("Sync remote asset indexes into the local daemon DB; optionally prefetch bytes").requiredOption("--workspace-id <id>", "Workspace id").option("--bytes", "Also prefetch asset bytes into ~/.prismer/cache").option("--limit <n>", "Maximum number of assets to prefetch when --bytes is set", parsePositiveInt).option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
9996
- const result = await syncAssets(opts);
9997
- if (opts.json) {
9998
- printJson({ ok: true, data: result });
9999
- return;
10000
- }
10001
- getUI().line(`Synced asset metadata: ${result.metadata.applied} applied (cursor=${result.metadata.cursor})`);
10002
- getUI().line(`Synced workspace files: ${result.workspaceFiles.applied} applied (cursor=${result.workspaceFiles.cursor ?? "null"})`);
10003
- if (opts.bytes) {
10004
- getUI().line(
10005
- `Prefetched bytes: ${result.prefetch.fetched}/${result.prefetch.considered} assets, ${result.prefetch.bytes} bytes`
10006
- );
10007
- if (result.prefetch.failed.length > 0) {
10008
- getUI().line(`Prefetch failures: ${result.prefetch.failed.length}`);
10009
- }
10010
- } else {
10011
- getUI().line("Asset bytes were not prefetched; they are downloaded lazily on first use. Run with --bytes to fill ~/.prismer/cache.");
10012
- }
10013
- }, { code: "asset_sync_failed" }));
10014
8703
  cmd.command("list").description("List assets for a workspace, optionally filtered by task").option("--workspace-id <id>", "Workspace id").option("--task-id <id>", "Task id filter").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
10015
8704
  const cloud = mkCloud();
10016
8705
  const query = new URLSearchParams();
10017
8706
  if (opts.workspaceId) query.set("workspaceId", opts.workspaceId);
10018
8707
  if (opts.taskId) query.set("taskId", opts.taskId);
10019
- const path9 = `/api/im/assets${query.toString() ? `?${query.toString()}` : ""}`;
10020
- const res = await cloud.request("GET", path9);
8708
+ const path7 = `/api/im/assets${query.toString() ? `?${query.toString()}` : ""}`;
8709
+ const res = await cloud.request("GET", path7);
10021
8710
  if (!res.ok) exitWithError(`asset list failed (${res.status}): ${res.error?.message ?? "request failed"}`, { code: res.error?.code ?? "asset_list_failed" });
10022
8711
  const body = res.data;
10023
8712
  if (isErrorEnvelope(body)) {
@@ -10029,20 +8718,13 @@ function buildAssetCommand() {
10029
8718
  }
10030
8719
  printAssetList(body);
10031
8720
  }, { code: "asset_list_failed" }));
10032
- cmd.command("upload <path>").description("Upload a local file or directory as IM assets").requiredOption("--workspace-id <id>", "Workspace id").option("--kind <kind>", "Asset kind", "file").option("--folder-path <path>", "Cloud folderPath prefix for the uploaded file(s)").option("--task-id <id>", "Source task id").option("--agent-id <id>", "Source agent IM user id").option("--container-id <id>", "Container id; also added to metadata for sandbox-output assets").option("--metadata <json>", "Additional JSON metadata").option("--mime <type>", "Override file MIME type").option("--json", "Output machine-readable JSON").action(runAction(async (inputPath, opts) => {
10033
- const body = await uploadAssetPath(inputPath, opts);
8721
+ cmd.command("upload <file>").description("Upload a local file as an IM asset").requiredOption("--workspace-id <id>", "Workspace id").option("--kind <kind>", "Asset kind", "file").option("--task-id <id>", "Source task id").option("--agent-id <id>", "Source agent IM user id").option("--container-id <id>", "Container id; also added to metadata for sandbox-output assets").option("--metadata <json>", "Additional JSON metadata").option("--mime <type>", "Override file MIME type").option("--json", "Output machine-readable JSON").action(runAction(async (file, opts) => {
8722
+ const body = await uploadAsset(file, opts);
10034
8723
  if (opts.json) {
10035
8724
  printJson(body ?? null);
10036
8725
  return;
10037
8726
  }
10038
- if (isUploadManyResult(body)) {
10039
- getUI().line(`Uploaded ${body.data.count} assets`);
10040
- for (const item of body.data.items) {
10041
- printAssetDetail("Uploaded asset", item.file, item.response);
10042
- }
10043
- } else {
10044
- printAssetDetail("Uploaded asset", inputPath, body);
10045
- }
8727
+ printAssetDetail("Uploaded asset", file, body);
10046
8728
  }, { code: "asset_upload_failed" }));
10047
8729
  cmd.command("download <assetId>").description("Download asset bytes").requiredOption("--out <path>", "Destination file path").action(runAction(async (assetId, opts) => {
10048
8730
  await downloadAsset(assetId, opts.out);
@@ -10062,12 +8744,12 @@ function buildAssetCommand() {
10062
8744
  }
10063
8745
  printAssetDetail("Asset", assetId, body);
10064
8746
  }, { code: "asset_get_failed" }));
10065
- cmd.command("by-hash <sha256>").description("Fetch asset metadata by content hash").option("--workspace-id <id>", "Workspace id (required by the cloud API)").option("--json", "Output machine-readable JSON").action(runAction(async (sha2564, opts) => {
8747
+ cmd.command("by-hash <sha256>").description("Fetch asset metadata by content hash").option("--workspace-id <id>", "Workspace id (required by the cloud API)").option("--json", "Output machine-readable JSON").action(runAction(async (sha2562, opts) => {
10066
8748
  const cloud = mkCloud();
10067
8749
  const query = new URLSearchParams();
10068
8750
  if (opts.workspaceId) query.set("wsId", opts.workspaceId);
10069
- const path9 = `/api/im/assets/by-hash/${encodeURIComponent(sha2564)}${query.toString() ? `?${query.toString()}` : ""}`;
10070
- const res = await cloud.request("GET", path9);
8751
+ const path7 = `/api/im/assets/by-hash/${encodeURIComponent(sha2562)}${query.toString() ? `?${query.toString()}` : ""}`;
8752
+ const res = await cloud.request("GET", path7);
10071
8753
  if (!res.ok) exitWithError(`asset by-hash failed (${res.status}): ${res.error?.message ?? "request failed"}`, { code: res.error?.code ?? "asset_by_hash_failed" });
10072
8754
  const body = res.data;
10073
8755
  if (isErrorEnvelope(body)) {
@@ -10077,7 +8759,7 @@ function buildAssetCommand() {
10077
8759
  printJson(body ?? null);
10078
8760
  return;
10079
8761
  }
10080
- printAssetDetail("Asset hash", sha2564, body);
8762
+ printAssetDetail("Asset hash", sha2562, body);
10081
8763
  }, { code: "asset_by_hash_failed" }));
10082
8764
  return cmd;
10083
8765
  }
@@ -10085,102 +8767,10 @@ function mkCloud() {
10085
8767
  const cfg = loadConfig(resolvePaths());
10086
8768
  return new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
10087
8769
  }
10088
- async function syncAssets(opts) {
10089
- const paths = resolvePaths();
10090
- const cfg = loadConfig(paths);
10091
- const cloud = new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
10092
- const db = openLocalDb(paths.localDb);
10093
- try {
10094
- const stateDir = `${paths.root}/${opts.workspaceId}`;
10095
- const metadataIndex = new AssetMetadataIndex({
10096
- db,
10097
- cloud,
10098
- workspaceId: opts.workspaceId,
10099
- workspaceStateDir: stateDir
10100
- });
10101
- const mirror = new WorkspaceMirror({
10102
- db,
10103
- cloud,
10104
- workspaceId: opts.workspaceId,
10105
- workspaceStateDir: stateDir
10106
- });
10107
- const [metadata, workspaceFiles] = await Promise.all([
10108
- metadataIndex.pullDelta(),
10109
- mirror.pullDelta()
10110
- ]);
10111
- const prefetch = opts.bytes ? await prefetchAssetBytes({
10112
- db,
10113
- cloud,
10114
- workspaceId: opts.workspaceId,
10115
- cacheDir: paths.cacheDir,
10116
- maxBytes: cfg.cache?.max_bytes,
10117
- limit: opts.limit
10118
- }) : { considered: 0, fetched: 0, bytes: 0, failed: [] };
10119
- return { workspaceId: opts.workspaceId, metadata, workspaceFiles, prefetch };
10120
- } finally {
10121
- db.close();
10122
- }
10123
- }
10124
- async function prefetchAssetBytes(opts) {
10125
- const limitClause = opts.limit ? " LIMIT ?" : "";
10126
- const params = [opts.workspaceId];
10127
- if (opts.limit) params.push(opts.limit);
10128
- const rows = opts.db.prepare(
10129
- `SELECT asset_id, content_hash, mime, size_bytes
10130
- FROM asset_metadata_index
10131
- WHERE workspace_id = ?
10132
- ORDER BY asset_index_seq DESC${limitClause}`
10133
- ).all(...params);
10134
- const cache = new AssetCache({
10135
- db: opts.db,
10136
- cloud: opts.cloud,
10137
- cacheDir: opts.cacheDir,
10138
- maxBytes: opts.maxBytes
10139
- });
10140
- let fetched = 0;
10141
- let bytes = 0;
10142
- const failed = [];
10143
- for (const row of rows) {
10144
- try {
10145
- const cached = await cache.getOrFetch(row.content_hash, {
10146
- workspaceIdHint: opts.workspaceId,
10147
- assetId: row.asset_id
10148
- });
10149
- fetched += 1;
10150
- bytes += cached.sizeBytes;
10151
- } catch (err) {
10152
- failed.push({
10153
- assetId: row.asset_id,
10154
- contentHash: row.content_hash,
10155
- error: err.message
10156
- });
10157
- }
10158
- }
10159
- return { considered: rows.length, fetched, bytes, failed };
10160
- }
10161
- async function uploadAssetPath(inputPath, opts) {
10162
- if (!(0, import_node_fs17.existsSync)(inputPath)) exitWithError(`path not found: ${inputPath}`);
10163
- const stat = (0, import_node_fs17.lstatSync)(inputPath);
10164
- if (stat.isSymbolicLink()) exitWithError(`refusing to upload symlink: ${inputPath}`);
10165
- if (stat.isFile()) {
10166
- return uploadAsset(inputPath, { ...opts, folderPath: normalizeFolderPath(opts.folderPath) });
10167
- }
10168
- if (!stat.isDirectory()) exitWithError(`path is not a file or directory: ${inputPath}`);
10169
- const files = listFilesRecursive(inputPath);
10170
- if (files.length === 0) exitWithError(`directory has no uploadable files: ${inputPath}`);
10171
- const items = [];
10172
- for (const file of files) {
10173
- const relDir = (0, import_node_path13.dirname)((0, import_node_path13.relative)(inputPath, file));
10174
- const folderPath = combineFolderPath(opts.folderPath, relDir === "." ? "" : relDir);
10175
- const response = await uploadAsset(file, { ...opts, folderPath });
10176
- items.push({ file, folderPath: folderPath ?? null, response });
10177
- }
10178
- return { ok: true, data: { count: items.length, items } };
10179
- }
10180
8770
  async function uploadAsset(file, opts) {
10181
- if (!(0, import_node_fs17.existsSync)(file)) exitWithError(`file not found: ${file}`);
8771
+ if (!(0, import_node_fs14.existsSync)(file)) exitWithError(`file not found: ${file}`);
10182
8772
  const cfg = loadConfig(resolvePaths());
10183
- const bytes = (0, import_node_fs17.readFileSync)(file);
8773
+ const bytes = (0, import_node_fs14.readFileSync)(file);
10184
8774
  const metadata = parseMetadata(opts.metadata);
10185
8775
  if (opts.taskId && metadata.taskId === void 0) metadata.taskId = opts.taskId;
10186
8776
  if (opts.containerId && metadata.containerId === void 0) metadata.containerId = opts.containerId;
@@ -10189,9 +8779,8 @@ async function uploadAsset(file, opts) {
10189
8779
  form.set("kind", opts.kind);
10190
8780
  if (opts.agentId) form.set("sourceAgentImUserId", opts.agentId);
10191
8781
  if (opts.taskId) form.set("sourceTaskId", opts.taskId);
10192
- if (opts.folderPath !== void 0) form.set("folderPath", opts.folderPath);
10193
8782
  if (Object.keys(metadata).length > 0) form.set("metadata", JSON.stringify(metadata));
10194
- form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), (0, import_node_path13.basename)(file));
8783
+ form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), (0, import_node_path10.basename)(file));
10195
8784
  const url = `${cfg.cloud_api_base.replace(/\/$/, "")}/api/im/assets`;
10196
8785
  let res;
10197
8786
  try {
@@ -10209,37 +8798,6 @@ async function uploadAsset(file, opts) {
10209
8798
  }
10210
8799
  return body;
10211
8800
  }
10212
- function listFilesRecursive(root) {
10213
- const out = [];
10214
- for (const name of (0, import_node_fs17.readdirSync)(root)) {
10215
- if (name.startsWith(".")) continue;
10216
- const full = (0, import_node_path13.join)(root, name);
10217
- const stat = (0, import_node_fs17.lstatSync)(full);
10218
- if (stat.isSymbolicLink()) continue;
10219
- if (stat.isDirectory()) {
10220
- out.push(...listFilesRecursive(full));
10221
- } else if (stat.isFile()) {
10222
- out.push(full);
10223
- }
10224
- }
10225
- return out.sort();
10226
- }
10227
- function combineFolderPath(prefix, relDir) {
10228
- const normalizedPrefix = normalizeFolderPath(prefix);
10229
- const normalizedRel = normalizeFolderPath(relDir);
10230
- if (normalizedPrefix && normalizedRel) return `${normalizedPrefix}/${normalizedRel}`;
10231
- return normalizedPrefix ?? normalizedRel;
10232
- }
10233
- function normalizeFolderPath(value) {
10234
- if (value === void 0) return void 0;
10235
- const normalized = value.trim().replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
10236
- return normalized.length > 0 ? normalized : "";
10237
- }
10238
- function isUploadManyResult(raw) {
10239
- return Boolean(
10240
- raw && typeof raw === "object" && raw.ok === true && raw.data && Array.isArray(raw.data.items)
10241
- );
10242
- }
10243
8801
  async function downloadAsset(assetId, outPath) {
10244
8802
  const cloud = mkCloud();
10245
8803
  const res = await cloud.fetchRaw(`/api/im/assets/${encodeURIComponent(assetId)}`);
@@ -10248,7 +8806,7 @@ async function downloadAsset(assetId, outPath) {
10248
8806
  exitWithError(`asset download failed (${res.status}): ${errorMessage(body)}`);
10249
8807
  }
10250
8808
  const bytes = Buffer.from(await res.arrayBuffer());
10251
- (0, import_node_fs17.writeFileSync)(outPath, bytes);
8809
+ (0, import_node_fs14.writeFileSync)(outPath, bytes);
10252
8810
  }
10253
8811
  function parseMetadata(raw) {
10254
8812
  if (!raw) return {};
@@ -10333,13 +8891,6 @@ function stringField(obj, key) {
10333
8891
  const value = obj?.[key];
10334
8892
  return typeof value === "string" && value.length > 0 ? value : void 0;
10335
8893
  }
10336
- function parsePositiveInt(raw) {
10337
- const n = Number(raw);
10338
- if (!Number.isInteger(n) || n <= 0) {
10339
- throw new Error("--limit must be a positive integer");
10340
- }
10341
- return n;
10342
- }
10343
8894
 
10344
8895
  // src/cli/commands/banner.ts
10345
8896
  var import_commander4 = require("commander");
@@ -10395,7 +8946,7 @@ ${name} (${role})`);
10395
8946
  getUI().line(`Group created ${data.groupId ?? "(unknown)"}
10396
8947
  ${data.title ?? opts.name} (${memberCount} members)`);
10397
8948
  });
10398
- group.command("messages <groupId>").description("List group messages").option("--limit <n>", "Max messages", parsePositiveInt2).option("--before <id>", "Return messages before this message id").option("--json", "Print raw JSON response").action(async (groupId, opts) => {
8949
+ group.command("messages <groupId>").description("List group messages").option("--limit <n>", "Max messages", parsePositiveInt).option("--before <id>", "Return messages before this message id").option("--json", "Print raw JSON response").action(async (groupId, opts) => {
10399
8950
  const data = await requestOrExit(
10400
8951
  "GET",
10401
8952
  withQuery(`/api/im/groups/${encodeURIComponent(groupId)}/messages`, {
@@ -10433,7 +8984,7 @@ ${data.title ?? opts.name} (${memberCount} members)`);
10433
8984
  getUI().line(`Removed ${memberId} from group ${groupId}`);
10434
8985
  });
10435
8986
  cmd.addCommand(group);
10436
- cmd.command("messages <conversationId>").description("List conversation messages").option("--limit <n>", "Max messages", parsePositiveInt2).option("--before <id>", "Return messages before this message id").option("--json", "Print raw JSON response").action(async (conversationId, opts) => {
8987
+ cmd.command("messages <conversationId>").description("List conversation messages").option("--limit <n>", "Max messages", parsePositiveInt).option("--before <id>", "Return messages before this message id").option("--json", "Print raw JSON response").action(async (conversationId, opts) => {
10437
8988
  const data = await requestOrExit(
10438
8989
  "GET",
10439
8990
  withQuery(`/api/im/messages/${encodeURIComponent(conversationId)}`, {
@@ -10449,15 +9000,15 @@ ${data.title ?? opts.name} (${memberCount} members)`);
10449
9000
  });
10450
9001
  return cmd;
10451
9002
  }
10452
- async function requestOrExit(method, path9, body) {
9003
+ async function requestOrExit(method, path7, body) {
10453
9004
  let res;
10454
9005
  try {
10455
- res = await mkCloud2().request(method, path9, body === void 0 ? void 0 : { body });
9006
+ res = await mkCloud2().request(method, path7, body === void 0 ? void 0 : { body });
10456
9007
  } catch (err) {
10457
9008
  exitWithError(sanitizeError(err.message));
10458
9009
  }
10459
9010
  if (!res.ok) {
10460
- exitWithError(`${method} ${path9} failed (${res.status}): ${sanitizeError(res.error?.message ?? "unknown")}`);
9011
+ exitWithError(`${method} ${path7} failed (${res.status}): ${sanitizeError(res.error?.message ?? "unknown")}`);
10461
9012
  }
10462
9013
  return unwrapEnvelope2(res.data);
10463
9014
  }
@@ -10489,20 +9040,20 @@ function unwrapEnvelope2(raw) {
10489
9040
  function parseCsv(value) {
10490
9041
  return value.split(",").map((v) => v.trim()).filter(Boolean);
10491
9042
  }
10492
- function parsePositiveInt2(value) {
9043
+ function parsePositiveInt(value) {
10493
9044
  const parsed = Number.parseInt(value, 10);
10494
9045
  if (!Number.isInteger(parsed) || parsed < 1) {
10495
9046
  throw new Error("expected a positive integer");
10496
9047
  }
10497
9048
  return parsed;
10498
9049
  }
10499
- function withQuery(path9, params) {
9050
+ function withQuery(path7, params) {
10500
9051
  const query = new URLSearchParams();
10501
9052
  for (const [key, value] of Object.entries(params)) {
10502
9053
  if (value !== void 0) query.set(key, String(value));
10503
9054
  }
10504
9055
  const suffix = query.toString();
10505
- return suffix ? `${path9}?${suffix}` : path9;
9056
+ return suffix ? `${path7}?${suffix}` : path7;
10506
9057
  }
10507
9058
  function printMessageSummary(label, data) {
10508
9059
  const msg = data.message ?? {};
@@ -10629,7 +9180,7 @@ init_util();
10629
9180
  init_ui();
10630
9181
  function buildCookbookCommand() {
10631
9182
  const cmd = new import_commander7.Command("cookbook").description("Run CLI-only 54release MVP regression suites");
10632
- cmd.command("run").description("Run one or more cookbook smoke suites using the configured API key").option("--suite <name>", "status|im|task|group|asset|sandbox|all, comma-separated", "all").option("--workspace-id <id>", "Workspace id for workspace-scoped suites").option("--agent-id <id>", "Agent IM user id for task create smoke").option("--group-id <id>", "Group/conversation id for group message history smoke").option("--sandbox-id <id>", "Sandbox id for sandbox status smoke").option("--prompt <text>", "Prompt for optional task create smoke").option("--timeout-ms <ms>", "Request/task timeout", parsePositiveInt3, 6e4).option("--strict", "Treat skipped optional checks as failure").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
9183
+ cmd.command("run").description("Run one or more cookbook smoke suites using the configured API key").option("--suite <name>", "status|im|task|group|asset|sandbox|all, comma-separated", "all").option("--workspace-id <id>", "Workspace id for workspace-scoped suites").option("--agent-id <id>", "Agent IM user id for task create smoke").option("--group-id <id>", "Group/conversation id for group message history smoke").option("--sandbox-id <id>", "Sandbox id for sandbox status smoke").option("--prompt <text>", "Prompt for optional task create smoke").option("--timeout-ms <ms>", "Request/task timeout", parsePositiveInt2, 6e4).option("--strict", "Treat skipped optional checks as failure").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
10633
9184
  const suites = parseSuites(opts.suite);
10634
9185
  const cloud = mkCloud3(opts.timeoutMs);
10635
9186
  const checks = [];
@@ -10671,8 +9222,8 @@ async function runStatusSuite(cloud) {
10671
9222
  await check("status", "cloud health", "GET", "/api/health"),
10672
9223
  await check("status", "current IM identity", "GET", "/api/im/me", void 0, cloud)
10673
9224
  ];
10674
- async function check(suite, name, method, path9, body, client = cloud) {
10675
- return requestCheck(client, suite, name, method, path9, body);
9225
+ async function check(suite, name, method, path7, body, client = cloud) {
9226
+ return requestCheck(client, suite, name, method, path7, body);
10676
9227
  }
10677
9228
  }
10678
9229
  async function runIMSuite(cloud) {
@@ -10746,27 +9297,27 @@ async function runSandboxSuite(cloud, opts) {
10746
9297
  }
10747
9298
  return checks;
10748
9299
  }
10749
- async function requestCheck(cloud, suite, name, method, path9, body) {
9300
+ async function requestCheck(cloud, suite, name, method, path7, body) {
10750
9301
  let res;
10751
9302
  try {
10752
- res = await cloud.request(method, path9, body === void 0 ? void 0 : { body });
9303
+ res = await cloud.request(method, path7, body === void 0 ? void 0 : { body });
10753
9304
  } catch (err) {
10754
- return { suite, name, status: "fail", endpoint: path9, detail: sanitize(err.message) };
9305
+ return { suite, name, status: "fail", endpoint: path7, detail: sanitize(err.message) };
10755
9306
  }
10756
9307
  if (!res.ok) {
10757
9308
  return {
10758
9309
  suite,
10759
9310
  name,
10760
9311
  status: "fail",
10761
- endpoint: path9,
9312
+ endpoint: path7,
10762
9313
  detail: sanitize(`${res.status}: ${res.error?.message ?? "request failed"}`)
10763
9314
  };
10764
9315
  }
10765
9316
  const data = unwrap(res.data);
10766
9317
  if (isApiError(res.data)) {
10767
- return { suite, name, status: "fail", endpoint: path9, detail: sanitize(apiErrorMessage(res.data)), data: res.data };
9318
+ return { suite, name, status: "fail", endpoint: path7, detail: sanitize(apiErrorMessage(res.data)), data: res.data };
10768
9319
  }
10769
- return { suite, name, status: "pass", endpoint: path9, detail: summarizeData(data), data };
9320
+ return { suite, name, status: "pass", endpoint: path7, detail: summarizeData(data), data };
10770
9321
  }
10771
9322
  async function pollTask(cloud, taskId, timeoutMs) {
10772
9323
  const started = Date.now();
@@ -10873,7 +9424,7 @@ function taskFrom(data) {
10873
9424
  function sanitize(message) {
10874
9425
  return message.replace(/sk-prismer-[A-Za-z0-9_-]+/g, "sk-prismer-***");
10875
9426
  }
10876
- function parsePositiveInt3(value) {
9427
+ function parsePositiveInt2(value) {
10877
9428
  const parsed = Number.parseInt(value, 10);
10878
9429
  if (!Number.isFinite(parsed) || parsed <= 0) {
10879
9430
  throw new Error("expected a positive integer");
@@ -10884,9 +9435,9 @@ function parsePositiveInt3(value) {
10884
9435
  // src/cli/commands/daemon.ts
10885
9436
  var import_commander8 = require("commander");
10886
9437
  var import_node_child_process7 = require("child_process");
10887
- var import_node_fs18 = require("fs");
10888
- var import_promises3 = require("timers/promises");
10889
- var import_node_path14 = require("path");
9438
+ var import_node_fs15 = require("fs");
9439
+ var import_promises2 = require("timers/promises");
9440
+ var import_node_path11 = require("path");
10890
9441
  init_util();
10891
9442
  init_ui();
10892
9443
  function buildDaemonCommand() {
@@ -10904,7 +9455,7 @@ function buildDaemonCommand() {
10904
9455
  async function runForeground(opts) {
10905
9456
  const paths = resolvePaths();
10906
9457
  const existingPid = readPidFile(paths);
10907
- if (existingPid && pidAlive2(existingPid)) {
9458
+ if (existingPid && pidAlive(existingPid)) {
10908
9459
  exitWithError(`Daemon already running (pid ${existingPid}). Use \`prismer daemon stop\` first.`);
10909
9460
  }
10910
9461
  writePidFile(paths, process.pid);
@@ -10928,7 +9479,7 @@ function buildDaemonCommand() {
10928
9479
  while (!configExists(paths)) {
10929
9480
  if (stopRequested) return;
10930
9481
  getUI().info(`[daemon] waiting for ${paths.configFile} \u2014 run \`prismer setup\` to create it`);
10931
- await (0, import_promises3.setTimeout)(5e3);
9482
+ await (0, import_promises2.setTimeout)(5e3);
10932
9483
  }
10933
9484
  runner = new Runner({
10934
9485
  startLocalServer: opts.localServer !== false,
@@ -10957,12 +9508,12 @@ function buildDaemonCommand() {
10957
9508
  async function startBackground(opts) {
10958
9509
  const paths = resolvePaths();
10959
9510
  const existingPid = readPidFile(paths);
10960
- if (existingPid && pidAlive2(existingPid)) {
9511
+ if (existingPid && pidAlive(existingPid)) {
10961
9512
  exitWithError(`Daemon already running (pid ${existingPid}). Use \`prismer daemon stop\` first.`);
10962
9513
  }
10963
- if (!(0, import_node_fs18.existsSync)(paths.logsDir)) (0, import_node_fs18.mkdirSync)(paths.logsDir, { recursive: true });
10964
- const logFile = (0, import_node_path14.join)(paths.logsDir, "daemon.log");
10965
- const fd = (0, import_node_fs18.openSync)(logFile, "a");
9514
+ if (!(0, import_node_fs15.existsSync)(paths.logsDir)) (0, import_node_fs15.mkdirSync)(paths.logsDir, { recursive: true });
9515
+ const logFile = (0, import_node_path11.join)(paths.logsDir, "daemon.log");
9516
+ const fd = (0, import_node_fs15.openSync)(logFile, "a");
10966
9517
  const args = [process.argv[1], "daemon", "run"];
10967
9518
  if (opts.port) args.push("--port", String(opts.port));
10968
9519
  if (opts.localServer === false) args.push("--no-local-server");
@@ -10974,9 +9525,9 @@ function buildDaemonCommand() {
10974
9525
  child.unref();
10975
9526
  const deadline = Date.now() + 5e3;
10976
9527
  while (Date.now() < deadline) {
10977
- await (0, import_promises3.setTimeout)(200);
9528
+ await (0, import_promises2.setTimeout)(200);
10978
9529
  const pid = readPidFile(paths);
10979
- if (pid && pidAlive2(pid)) {
9530
+ if (pid && pidAlive(pid)) {
10980
9531
  if (opts.json) printJson({ ok: true, pid, logFile });
10981
9532
  else getUI().ok("Daemon started", `pid ${pid} \xB7 logs ${logFile}`);
10982
9533
  return;
@@ -10991,7 +9542,7 @@ function buildDaemonCommand() {
10991
9542
  const paths = resolvePaths();
10992
9543
  const pid = readPidFile(paths);
10993
9544
  if (!pid) exitWithError("No daemon.pid found \u2014 daemon does not appear to be running.");
10994
- if (!pidAlive2(pid)) {
9545
+ if (!pidAlive(pid)) {
10995
9546
  clearPidFile(paths);
10996
9547
  getUI().warn("Daemon was not alive", `pid ${pid}; cleared stale pid file`);
10997
9548
  return;
@@ -11003,12 +9554,12 @@ function buildDaemonCommand() {
11003
9554
  }
11004
9555
  const deadline = Date.now() + (opts.timeout ?? 5e3);
11005
9556
  while (Date.now() < deadline) {
11006
- if (!pidAlive2(pid)) {
9557
+ if (!pidAlive(pid)) {
11007
9558
  clearPidFile(paths);
11008
9559
  getUI().ok("Daemon stopped", `pid ${pid}`);
11009
9560
  return;
11010
9561
  }
11011
- await (0, import_promises3.setTimeout)(200);
9562
+ await (0, import_promises2.setTimeout)(200);
11012
9563
  }
11013
9564
  process.stderr.write(`Daemon did not exit within timeout; pid ${pid} may still be alive.
11014
9565
  `);
@@ -11028,7 +9579,7 @@ function buildDaemonCommand() {
11028
9579
  cmd.command("status").description("Show daemon process + WS state").option("--json", "Output JSON (default)").action(async () => {
11029
9580
  const paths = resolvePaths();
11030
9581
  const pid = readPidFile(paths);
11031
- const alive = pid ? pidAlive2(pid) : false;
9582
+ const alive = pid ? pidAlive(pid) : false;
11032
9583
  if (!pid || !alive) {
11033
9584
  printJson({ running: false, pid: pid ?? null, paths: { config: paths.configFile } });
11034
9585
  return;
@@ -11045,8 +9596,8 @@ function buildDaemonCommand() {
11045
9596
  });
11046
9597
  cmd.command("logs").description("Show daemon logs").option("--tail <n>", "Number of lines to show", (v) => Number.parseInt(v, 10), 80).option("--follow", "Follow log output").option("--json", "Accept --json for global flag compatibility (raw log bytes are streamed)").action(async (opts) => {
11047
9598
  const paths = resolvePaths();
11048
- const logFile = (0, import_node_path14.join)(paths.logsDir, "daemon.log");
11049
- if (!(0, import_node_fs18.existsSync)(logFile)) {
9599
+ const logFile = (0, import_node_path11.join)(paths.logsDir, "daemon.log");
9600
+ if (!(0, import_node_fs15.existsSync)(logFile)) {
11050
9601
  exitWithError(`No daemon log found at ${logFile}. Start the daemon with \`prismer daemon start\`.`);
11051
9602
  }
11052
9603
  const lines = Math.max(1, opts.tail);
@@ -11056,9 +9607,9 @@ function buildDaemonCommand() {
11056
9607
  });
11057
9608
  return cmd;
11058
9609
  }
11059
- async function tailFromEnd(path9, lines) {
9610
+ async function tailFromEnd(path7, lines) {
11060
9611
  const { open } = await import("fs/promises");
11061
- const handle = await open(path9, "r");
9612
+ const handle = await open(path7, "r");
11062
9613
  try {
11063
9614
  const stat = await handle.stat();
11064
9615
  let size = stat.size;
@@ -11086,14 +9637,14 @@ async function tailFromEnd(path9, lines) {
11086
9637
  await handle.close();
11087
9638
  }
11088
9639
  }
11089
- async function followFile(path9) {
11090
- let offset = (0, import_node_fs18.statSync)(path9).size;
9640
+ async function followFile(path7) {
9641
+ let offset = (0, import_node_fs15.statSync)(path7).size;
11091
9642
  for (; ; ) {
11092
- await (0, import_promises3.setTimeout)(1e3);
11093
- const size = (0, import_node_fs18.statSync)(path9).size;
9643
+ await (0, import_promises2.setTimeout)(1e3);
9644
+ const size = (0, import_node_fs15.statSync)(path7).size;
11094
9645
  if (size < offset) offset = 0;
11095
9646
  if (size === offset) continue;
11096
- const stream = (0, import_node_fs18.createReadStream)(path9, { start: offset, end: size - 1, encoding: "utf8" });
9647
+ const stream = (0, import_node_fs15.createReadStream)(path7, { start: offset, end: size - 1, encoding: "utf8" });
11097
9648
  for await (const chunk of stream) process.stdout.write(chunk);
11098
9649
  offset = size;
11099
9650
  }
@@ -11101,16 +9652,16 @@ async function followFile(path9) {
11101
9652
 
11102
9653
  // src/cli/commands/events.ts
11103
9654
  var import_commander9 = require("commander");
11104
- var import_node_fs19 = require("fs");
11105
- var import_node_os9 = require("os");
11106
- var import_node_path15 = require("path");
9655
+ var import_node_fs16 = require("fs");
9656
+ var import_node_os7 = require("os");
9657
+ var import_node_path12 = require("path");
11107
9658
  var import_node_readline = require("readline");
11108
9659
  init_util();
11109
9660
  var DEFAULT_LIMIT2 = 50;
11110
9661
  function buildEventsCommand() {
11111
9662
  return addEventOptions(new import_commander9.Command("events").description("Read local PARA events from ~/.prismer/para/events.jsonl")).action(async (opts) => {
11112
9663
  const file = eventsPath();
11113
- if (!(0, import_node_fs19.existsSync)(file)) {
9664
+ if (!(0, import_node_fs16.existsSync)(file)) {
11114
9665
  printJson(unavailable(file));
11115
9666
  process.exitCode = 1;
11116
9667
  return;
@@ -11127,7 +9678,7 @@ function buildEventsCommand() {
11127
9678
  function buildEventsStatsCommand() {
11128
9679
  return addEventOptions(new import_commander9.Command("events:stats").description("Summarize local PARA events from ~/.prismer/para/events.jsonl")).action(async (opts) => {
11129
9680
  const file = eventsPath();
11130
- if (!(0, import_node_fs19.existsSync)(file)) {
9681
+ if (!(0, import_node_fs16.existsSync)(file)) {
11131
9682
  printJson(unavailable(file));
11132
9683
  process.exitCode = 1;
11133
9684
  return;
@@ -11142,10 +9693,10 @@ function buildEventsStatsCommand() {
11142
9693
  });
11143
9694
  }
11144
9695
  function addEventOptions(cmd) {
11145
- return cmd.option("--limit <n>", "Max events to return/read", parsePositiveInt4, DEFAULT_LIMIT2).option("--agent-id <id>", "Filter by agent id").option("--session-id <id>", "Filter by session id").option("--family <name>", "Filter by event family").option("--type <name>", "Filter by event type").option("--json", "Output JSON (default)");
9696
+ return cmd.option("--limit <n>", "Max events to return/read", parsePositiveInt3, DEFAULT_LIMIT2).option("--agent-id <id>", "Filter by agent id").option("--session-id <id>", "Filter by session id").option("--family <name>", "Filter by event family").option("--type <name>", "Filter by event type").option("--json", "Output JSON (default)");
11146
9697
  }
11147
9698
  function eventsPath() {
11148
- return (0, import_node_path15.join)(process.env.PRISMER_HOME ?? (0, import_node_path15.join)((0, import_node_os9.homedir)(), ".prismer"), "para", "events.jsonl");
9699
+ return (0, import_node_path12.join)(process.env.PRISMER_HOME ?? (0, import_node_path12.join)((0, import_node_os7.homedir)(), ".prismer"), "para", "events.jsonl");
11149
9700
  }
11150
9701
  function unavailable(file) {
11151
9702
  return {
@@ -11161,7 +9712,7 @@ function unavailable(file) {
11161
9712
  async function readEvents(file, filters) {
11162
9713
  const out = [];
11163
9714
  const rl = (0, import_node_readline.createInterface)({
11164
- input: (0, import_node_fs19.createReadStream)(file, { encoding: "utf8" }),
9715
+ input: (0, import_node_fs16.createReadStream)(file, { encoding: "utf8" }),
11165
9716
  crlfDelay: Infinity
11166
9717
  });
11167
9718
  for await (const line of rl) {
@@ -11223,15 +9774,15 @@ function normalizeFilters(opts) {
11223
9774
  function cleanFilters(filters) {
11224
9775
  return Object.fromEntries(Object.entries(filters).filter(([, value]) => value !== void 0));
11225
9776
  }
11226
- function parsePositiveInt4(v) {
9777
+ function parsePositiveInt3(v) {
11227
9778
  const n = Number.parseInt(v, 10);
11228
9779
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_LIMIT2;
11229
9780
  }
11230
9781
 
11231
9782
  // src/cli/commands/memory.ts
11232
- var import_better_sqlite35 = __toESM(require("better-sqlite3"), 1);
9783
+ var import_better_sqlite34 = __toESM(require("better-sqlite3"), 1);
11233
9784
  var import_commander10 = require("commander");
11234
- var import_node_fs20 = require("fs");
9785
+ var import_node_fs17 = require("fs");
11235
9786
  init_util();
11236
9787
  var LOCAL_BASE = process.env.PRISMER_DAEMON_URL ?? "http://127.0.0.1:3210";
11237
9788
  function buildMemoryCommand() {
@@ -11257,7 +9808,7 @@ function buildMemoryCommand() {
11257
9808
  }));
11258
9809
  process.exitCode = 1;
11259
9810
  });
11260
- cmd.command("list").description("List memory records, or local cache records when gateway is unavailable").option("--workspace-id <id>", "Workspace to list (required for daemon RPC)").option("--page-type <type>", "Filter by pageType (hub/leaf/decision/glossary/archive)").option("--limit <n>", "Max items", parsePositiveInt5, 20).option("--json", "Output JSON (default)").action(async (opts) => {
9811
+ cmd.command("list").description("List memory records, or local cache records when gateway is unavailable").option("--workspace-id <id>", "Workspace to list (required for daemon RPC)").option("--page-type <type>", "Filter by pageType (hub/leaf/decision/glossary/archive)").option("--limit <n>", "Max items", parsePositiveInt4, 20).option("--json", "Output JSON (default)").action(async (opts) => {
11261
9812
  const limit = clampLimit(opts.limit);
11262
9813
  const daemonPaths = [];
11263
9814
  if (opts.workspaceId) {
@@ -11276,7 +9827,7 @@ function buildMemoryCommand() {
11276
9827
  printJson(unavailable2("list", snapshot, { items: snapshot.items, limit }));
11277
9828
  process.exitCode = 1;
11278
9829
  });
11279
- cmd.command("search").description("Search memory records, or local cache metadata when gateway is unavailable").argument("[query]", "Search text").option("--query <text>", "Search text").option("--workspace-id <id>", "Workspace to search (required for daemon RPC)").option("--limit <n>", "Max items", parsePositiveInt5, 20).option("--json", "Output JSON (default)").action(async (argQuery, opts) => {
9830
+ cmd.command("search").description("Search memory records, or local cache metadata when gateway is unavailable").argument("[query]", "Search text").option("--query <text>", "Search text").option("--workspace-id <id>", "Workspace to search (required for daemon RPC)").option("--limit <n>", "Max items", parsePositiveInt4, 20).option("--json", "Output JSON (default)").action(async (argQuery, opts) => {
11280
9831
  const query = opts.query ?? argQuery ?? "";
11281
9832
  const limit = clampLimit(opts.limit);
11282
9833
  const encoded = encodeURIComponent(query);
@@ -11320,9 +9871,9 @@ function buildMemoryCommand() {
11320
9871
  }
11321
9872
  async function tryDaemon(methods, paths) {
11322
9873
  for (const method of methods) {
11323
- for (const path9 of paths) {
9874
+ for (const path7 of paths) {
11324
9875
  try {
11325
- const res = await fetch(`${LOCAL_BASE}${path9}`, { method, signal: AbortSignal.timeout(1500) });
9876
+ const res = await fetch(`${LOCAL_BASE}${path7}`, { method, signal: AbortSignal.timeout(1500) });
11326
9877
  if (res.status === 404) continue;
11327
9878
  const body = await readJson4(res);
11328
9879
  if (!res.ok) {
@@ -11330,12 +9881,12 @@ async function tryDaemon(methods, paths) {
11330
9881
  ok: false,
11331
9882
  error: {
11332
9883
  code: `daemon_http_${res.status}`,
11333
- message: messageFromBody(body) ?? `Daemon endpoint ${method} ${path9} returned HTTP ${res.status}`
9884
+ message: messageFromBody(body) ?? `Daemon endpoint ${method} ${path7} returned HTTP ${res.status}`
11334
9885
  },
11335
- checks: { daemon: { url: LOCAL_BASE, endpoint: path9, reachable: true } }
9886
+ checks: { daemon: { url: LOCAL_BASE, endpoint: path7, reachable: true } }
11336
9887
  };
11337
9888
  }
11338
- return normalizeDaemonBody(body, { method, path: path9 });
9889
+ return normalizeDaemonBody(body, { method, path: path7 });
11339
9890
  } catch {
11340
9891
  continue;
11341
9892
  }
@@ -11383,7 +9934,7 @@ function readCacheSnapshot(limit) {
11383
9934
  const paths = resolvePaths();
11384
9935
  const empty = {
11385
9936
  dbPath: paths.localDb,
11386
- dbExists: (0, import_node_fs20.existsSync)(paths.localDb),
9937
+ dbExists: (0, import_node_fs17.existsSync)(paths.localDb),
11387
9938
  tables: {
11388
9939
  cached_assets: { exists: false, count: 0, sizeBytes: 0 },
11389
9940
  workspace_files_mirror: { exists: false, count: 0 }
@@ -11393,7 +9944,7 @@ function readCacheSnapshot(limit) {
11393
9944
  if (!empty.dbExists) return empty;
11394
9945
  let db;
11395
9946
  try {
11396
- db = new import_better_sqlite35.default(paths.localDb, { readonly: true, fileMustExist: true });
9947
+ db = new import_better_sqlite34.default(paths.localDb, { readonly: true, fileMustExist: true });
11397
9948
  const hasAssets = tableExists(db, "cached_assets");
11398
9949
  const hasFiles = tableExists(db, "workspace_files_mirror");
11399
9950
  const assets = hasAssets ? db.prepare("SELECT COUNT(*) AS count, COALESCE(SUM(size_bytes), 0) AS sizeBytes FROM cached_assets").get() : { count: 0, sizeBytes: 0 };
@@ -11482,7 +10033,7 @@ function searchableText(item) {
11482
10033
  item.localPath
11483
10034
  ].filter(Boolean).join(" ");
11484
10035
  }
11485
- function parsePositiveInt5(v) {
10036
+ function parsePositiveInt4(v) {
11486
10037
  const n = Number.parseInt(v, 10);
11487
10038
  return Number.isFinite(n) && n > 0 ? n : 20;
11488
10039
  }
@@ -11547,9 +10098,9 @@ function buildPairCommand() {
11547
10098
 
11548
10099
  // src/cli/commands/profile.ts
11549
10100
  var import_commander12 = require("commander");
11550
- var import_node_fs21 = require("fs");
11551
- var import_node_os10 = require("os");
11552
- var import_node_path16 = require("path");
10101
+ var import_node_fs18 = require("fs");
10102
+ var import_node_os8 = require("os");
10103
+ var import_node_path13 = require("path");
11553
10104
  var import_node_child_process8 = require("child_process");
11554
10105
  init_util();
11555
10106
  function buildProfileCommand() {
@@ -11597,12 +10148,12 @@ function buildProfileCommand() {
11597
10148
  const profile = await cloud.get(
11598
10149
  `/api/im/agent_profiles/${encodeURIComponent(profileId)}`
11599
10150
  );
11600
- const tmpFile = (0, import_node_path16.join)((0, import_node_os10.tmpdir)(), `prismer-profile-${profileId}.json`);
11601
- (0, import_node_fs21.writeFileSync)(tmpFile, JSON.stringify(profile.config, null, 2), "utf8");
10151
+ const tmpFile = (0, import_node_path13.join)((0, import_node_os8.tmpdir)(), `prismer-profile-${profileId}.json`);
10152
+ (0, import_node_fs18.writeFileSync)(tmpFile, JSON.stringify(profile.config, null, 2), "utf8");
11602
10153
  const editor = process.env.EDITOR || "vi";
11603
10154
  const ed = (0, import_node_child_process8.spawnSync)(editor, [tmpFile], { stdio: "inherit" });
11604
10155
  if (ed.status !== 0) exitWithError(`editor exited ${ed.status}`, { code: "editor_failed" });
11605
- const newConfig = JSON.parse((0, import_node_fs21.readFileSync)(tmpFile, "utf8"));
10156
+ const newConfig = JSON.parse((0, import_node_fs18.readFileSync)(tmpFile, "utf8"));
11606
10157
  const res = await cloud.request("PATCH", `/api/im/agent_profiles/${encodeURIComponent(profileId)}`, {
11607
10158
  body: { config: newConfig, version: profile.version }
11608
10159
  });
@@ -11623,9 +10174,9 @@ function mkCloud4() {
11623
10174
  }
11624
10175
  function readJsonArg(arg) {
11625
10176
  if (arg.startsWith("@")) {
11626
- const path9 = arg.slice(1);
11627
- if (!(0, import_node_fs21.existsSync)(path9)) throw new Error(`File not found: ${path9}`);
11628
- return JSON.parse((0, import_node_fs21.readFileSync)(path9, "utf8"));
10177
+ const path7 = arg.slice(1);
10178
+ if (!(0, import_node_fs18.existsSync)(path7)) throw new Error(`File not found: ${path7}`);
10179
+ return JSON.parse((0, import_node_fs18.readFileSync)(path7, "utf8"));
11629
10180
  }
11630
10181
  return JSON.parse(arg);
11631
10182
  }
@@ -11636,200 +10187,13 @@ async function resolveDefaultWorkspaceId(cloud) {
11636
10187
  return def.id;
11637
10188
  }
11638
10189
 
11639
- // src/cli/commands/reset.ts
11640
- var import_commander13 = require("commander");
11641
- var import_node_child_process9 = require("child_process");
11642
- var import_node_fs22 = require("fs");
11643
- var import_node_os11 = require("os");
11644
- var import_node_path17 = require("path");
11645
- var import_promises4 = require("timers/promises");
11646
- init_util();
11647
- init_ui();
11648
- function buildResetCommand() {
11649
- return new import_commander13.Command("reset").description("Stop daemon and reset local runtime state so setup can bind another account").option("--yes", "Actually perform the reset").option("--check", "Print the reset plan without changing files").option("--wipe", "Delete local state instead of archiving it").option("--timeout <ms>", "Time to wait for daemon shutdown (default 5000)", (v) => Number.parseInt(v, 10), 5e3).option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
11650
- const paths = resolvePaths();
11651
- const pid = readPidFile(paths);
11652
- const runningPid = pid && pidAlive2(pid) ? pid : null;
11653
- const archivePath = opts.wipe ? null : `${paths.root}.reset.${timestamp()}`;
11654
- const assetSyncRoot = resolveAssetSyncRoot();
11655
- const assetSyncArchivePath = opts.wipe ? null : `${assetSyncRoot}.reset.${timestamp()}`;
11656
- const hermesHome = resolveHermesHome();
11657
- const hermesArchivePath = opts.wipe ? null : `${hermesHome}.reset.${timestamp()}`;
11658
- const hermesGatewayPids = findHermesGatewayPids2();
11659
- const plan = {
11660
- root: paths.root,
11661
- exists: (0, import_node_fs22.existsSync)(paths.root),
11662
- runningPid,
11663
- mode: opts.wipe ? "wipe" : "archive",
11664
- archivePath,
11665
- assetSyncRoot,
11666
- assetSyncExists: (0, import_node_fs22.existsSync)(assetSyncRoot),
11667
- assetSyncArchivePath,
11668
- hermesHome,
11669
- hermesExists: (0, import_node_fs22.existsSync)(hermesHome),
11670
- hermesArchivePath,
11671
- hermesGatewayPids,
11672
- willStopDaemon: runningPid !== null,
11673
- nextStep: "prismer setup"
11674
- };
11675
- if (opts.check) {
11676
- printPlan(plan, opts.json);
11677
- return;
11678
- }
11679
- if (!opts.yes) {
11680
- exitWithError("Refusing to reset without --yes. Run `prismer reset --check` to preview.", {
11681
- code: "confirmation_required"
11682
- });
11683
- }
11684
- if (runningPid !== null) {
11685
- await stopDaemon(runningPid, opts.timeout ?? 5e3);
11686
- clearPidFile(paths);
11687
- } else if (pid) {
11688
- clearPidFile(paths);
11689
- }
11690
- for (const gatewayPid of hermesGatewayPids) {
11691
- await stopProcess(gatewayPid, opts.timeout ?? 5e3, "Hermes gateway");
11692
- }
11693
- if ((0, import_node_fs22.existsSync)(paths.root)) {
11694
- if (opts.wipe) {
11695
- (0, import_node_fs22.rmSync)(paths.root, { recursive: true, force: true });
11696
- } else {
11697
- if (!archivePath) throw new Error("internal: archivePath missing");
11698
- if (!(0, import_node_fs22.existsSync)((0, import_node_path17.dirname)(archivePath))) {
11699
- (0, import_node_fs22.mkdirSync)((0, import_node_path17.dirname)(archivePath), { recursive: true });
11700
- }
11701
- (0, import_node_fs22.renameSync)(paths.root, archivePath);
11702
- }
11703
- }
11704
- if ((0, import_node_fs22.existsSync)(assetSyncRoot)) {
11705
- if (opts.wipe) {
11706
- (0, import_node_fs22.rmSync)(assetSyncRoot, { recursive: true, force: true });
11707
- } else {
11708
- if (!assetSyncArchivePath) throw new Error("internal: assetSyncArchivePath missing");
11709
- if (!(0, import_node_fs22.existsSync)((0, import_node_path17.dirname)(assetSyncArchivePath))) {
11710
- (0, import_node_fs22.mkdirSync)((0, import_node_path17.dirname)(assetSyncArchivePath), { recursive: true });
11711
- }
11712
- (0, import_node_fs22.renameSync)(assetSyncRoot, assetSyncArchivePath);
11713
- }
11714
- }
11715
- if ((0, import_node_fs22.existsSync)(hermesHome)) {
11716
- if (opts.wipe) {
11717
- (0, import_node_fs22.rmSync)(hermesHome, { recursive: true, force: true });
11718
- } else {
11719
- if (!hermesArchivePath) throw new Error("internal: hermesArchivePath missing");
11720
- if (!(0, import_node_fs22.existsSync)((0, import_node_path17.dirname)(hermesArchivePath))) {
11721
- (0, import_node_fs22.mkdirSync)((0, import_node_path17.dirname)(hermesArchivePath), { recursive: true });
11722
- }
11723
- (0, import_node_fs22.renameSync)(hermesHome, hermesArchivePath);
11724
- }
11725
- }
11726
- (0, import_node_fs22.mkdirSync)(paths.root, { recursive: true });
11727
- if (opts.json) {
11728
- printJson({ ok: true, reset: true, plan });
11729
- return;
11730
- }
11731
- const ui = getUI();
11732
- ui.ok("Runtime reset complete", paths.root);
11733
- if (archivePath) ui.line(` archived: ${archivePath}`);
11734
- if (assetSyncArchivePath && plan.assetSyncExists) ui.line(` asset sync archived: ${assetSyncArchivePath}`);
11735
- if (hermesArchivePath && plan.hermesExists) ui.line(` Hermes archived: ${hermesArchivePath}`);
11736
- if (hermesGatewayPids.length > 0) ui.line(` stopped Hermes gateway pid(s): ${hermesGatewayPids.join(", ")}`);
11737
- ui.line(" next: prismer setup");
11738
- }, { code: "reset_failed" }));
11739
- }
11740
- function printPlan(plan, json) {
11741
- if (json) {
11742
- printJson({ ok: true, check: true, plan });
11743
- return;
11744
- }
11745
- const ui = getUI();
11746
- ui.header("Runtime reset plan");
11747
- ui.blank();
11748
- ui.line(` root: ${plan.root}`);
11749
- ui.line(` exists: ${plan.exists ? "yes" : "no"}`);
11750
- ui.line(` daemon: ${plan.runningPid ? `running pid ${plan.runningPid}` : "not running"}`);
11751
- ui.line(` mode: ${plan.mode}`);
11752
- if (plan.archivePath) ui.line(` archive: ${plan.archivePath}`);
11753
- ui.line(` asset sync: ${plan.assetSyncExists ? plan.assetSyncRoot : `${plan.assetSyncRoot} (not found)`}`);
11754
- if (plan.assetSyncArchivePath) ui.line(` asset archive: ${plan.assetSyncArchivePath}`);
11755
- ui.line(` Hermes: ${plan.hermesExists ? plan.hermesHome : `${plan.hermesHome} (not found)`}`);
11756
- if (plan.hermesArchivePath) ui.line(` Hermes archive: ${plan.hermesArchivePath}`);
11757
- ui.line(
11758
- ` Hermes gateway: ${plan.hermesGatewayPids.length > 0 ? `running pid(s) ${plan.hermesGatewayPids.join(", ")}` : "not running"}`
11759
- );
11760
- ui.blank();
11761
- ui.line("Run `prismer reset --yes` to apply, then `prismer setup` to bind a new account.");
11762
- }
11763
- async function stopDaemon(pid, timeoutMs) {
11764
- await stopProcess(pid, timeoutMs, "daemon");
11765
- }
11766
- async function stopProcess(pid, timeoutMs, label) {
11767
- try {
11768
- process.kill(pid, "SIGTERM");
11769
- } catch (err) {
11770
- throw new Error(`SIGTERM failed for ${label} pid ${pid}: ${err.message}`);
11771
- }
11772
- const deadline = Date.now() + timeoutMs;
11773
- while (Date.now() < deadline) {
11774
- if (!pidAlive2(pid)) return;
11775
- await (0, import_promises4.setTimeout)(100);
11776
- }
11777
- try {
11778
- process.kill(pid, "SIGKILL");
11779
- } catch (err) {
11780
- throw new Error(`SIGKILL failed for ${label} pid ${pid}: ${err.message}`);
11781
- }
11782
- const killDeadline = Date.now() + 1e3;
11783
- while (Date.now() < killDeadline) {
11784
- if (!pidAlive2(pid)) return;
11785
- await (0, import_promises4.setTimeout)(100);
11786
- }
11787
- throw new Error(`${label} pid ${pid} did not exit within ${timeoutMs + 1e3}ms`);
11788
- }
11789
- function timestamp() {
11790
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
11791
- }
11792
- function resolveAssetSyncRoot() {
11793
- const override = process.env.PRISMER_ASSET_SYNC_ROOT;
11794
- if (override) return override;
11795
- const home = (0, import_node_os11.homedir)();
11796
- const desktop = (0, import_node_path17.join)(home, "Desktop");
11797
- const base = (0, import_node_fs22.existsSync)(desktop) ? desktop : home;
11798
- return (0, import_node_path17.join)(base, "Prismer Assets");
11799
- }
11800
- function resolveHermesHome() {
11801
- return process.env.HERMES_HOME || (0, import_node_path17.join)((0, import_node_os11.homedir)(), ".hermes");
11802
- }
11803
- function findHermesGatewayPids2() {
11804
- try {
11805
- const out = (0, import_node_child_process9.execFileSync)("ps", ["-axo", "pid=,command="], { encoding: "utf8" });
11806
- const current = process.pid;
11807
- return out.split(/\r?\n/).map((line) => {
11808
- const trimmed = line.trim();
11809
- const match = /^(\d+)\s+(.+)$/.exec(trimmed);
11810
- if (!match) return null;
11811
- const pid = Number.parseInt(match[1], 10);
11812
- const command = match[2];
11813
- if (!Number.isFinite(pid) || pid === current) return null;
11814
- if (!isHermesGatewayCommand2(command)) return null;
11815
- return pid;
11816
- }).filter((pid) => pid !== null);
11817
- } catch {
11818
- return [];
11819
- }
11820
- }
11821
- function isHermesGatewayCommand2(command) {
11822
- if (!/\bgateway\b/.test(command) || !/\brun\b/.test(command)) return false;
11823
- return /(^|\s)(?:\S*\/)?hermes(?:\s|$)/.test(command);
11824
- }
11825
-
11826
10190
  // src/cli/commands/sandbox.ts
11827
- var import_commander14 = require("commander");
10191
+ var import_commander13 = require("commander");
11828
10192
  init_util();
11829
10193
  init_ui();
11830
10194
  function buildSandboxCommand() {
11831
- const cmd = new import_commander14.Command("sandbox").description("Inspect and smoke-test sandbox lifecycle");
11832
- cmd.command("list").description("List sandbox containers in a workspace").requiredOption("--workspace-id <id>", "Workspace id").option("--status <status>", "Status filter").option("--limit <n>", "Max rows", parsePositiveInt6, 50).option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
10195
+ const cmd = new import_commander13.Command("sandbox").description("Inspect and smoke-test sandbox lifecycle");
10196
+ cmd.command("list").description("List sandbox containers in a workspace").requiredOption("--workspace-id <id>", "Workspace id").option("--status <status>", "Status filter").option("--limit <n>", "Max rows", parsePositiveInt5, 50).option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
11833
10197
  const query = new URLSearchParams({ workspaceId: opts.workspaceId, limit: String(opts.limit) });
11834
10198
  if (opts.status) query.set("status", opts.status);
11835
10199
  const body = await cloudRequest("GET", `/api/sandboxes?${query.toString()}`);
@@ -11888,7 +10252,7 @@ function buildSandboxCommand() {
11888
10252
  }
11889
10253
  printSnapshotResult(id, body);
11890
10254
  }, { code: "sandbox_snapshot_failed" }));
11891
- cmd.command("runCmd <id> <command...>").description("Run a command in a sandbox container").option("--timeout-ms <ms>", "Command timeout", parsePositiveInt6, 6e4).option("--json", "Output machine-readable JSON").allowUnknownOption(true).action(runAction(async (id, command, opts) => {
10255
+ cmd.command("runCmd <id> <command...>").description("Run a command in a sandbox container").option("--timeout-ms <ms>", "Command timeout", parsePositiveInt5, 6e4).option("--json", "Output machine-readable JSON").allowUnknownOption(true).action(runAction(async (id, command, opts) => {
11892
10256
  if (!Array.isArray(command) || command.length === 0) exitWithError("runCmd requires a command after --", { code: "invalid_argument" });
11893
10257
  const body = await cloudRequest("POST", `/api/sandboxes/${encodeURIComponent(id)}/runCmd`, {
11894
10258
  command,
@@ -11929,12 +10293,12 @@ function errorMessage2(raw) {
11929
10293
  if (typeof raw.error === "string") return raw.error;
11930
10294
  return raw.error?.message ?? "request failed";
11931
10295
  }
11932
- async function cloudRequest(method, path9, body) {
10296
+ async function cloudRequest(method, path7, body) {
11933
10297
  const cloud = mkCloud5();
11934
- const res = await cloud.request(method, path9, body === void 0 ? void 0 : { body });
11935
- if (!res.ok) exitWithError(`${method} ${path9} failed (${res.status}): ${res.error?.message ?? "request failed"}`);
10298
+ const res = await cloud.request(method, path7, body === void 0 ? void 0 : { body });
10299
+ if (!res.ok) exitWithError(`${method} ${path7} failed (${res.status}): ${res.error?.message ?? "request failed"}`);
11936
10300
  const responseBody = res.data;
11937
- if (isErrorEnvelope2(responseBody)) exitWithError(`${method} ${path9} failed (${res.status}): ${errorMessage2(responseBody)}`);
10301
+ if (isErrorEnvelope2(responseBody)) exitWithError(`${method} ${path7} failed (${res.status}): ${errorMessage2(responseBody)}`);
11938
10302
  return responseBody;
11939
10303
  }
11940
10304
  function printSandboxList(raw) {
@@ -12009,7 +10373,7 @@ function writeLine2(label, value) {
12009
10373
  if (value === void 0 || value === null || value === "") return;
12010
10374
  getUI().line(` ${label}: ${String(value)}`);
12011
10375
  }
12012
- function parsePositiveInt6(value) {
10376
+ function parsePositiveInt5(value) {
12013
10377
  const parsed = Number.parseInt(value, 10);
12014
10378
  if (!Number.isFinite(parsed) || parsed <= 0) {
12015
10379
  throw new Error("expected a positive integer");
@@ -12027,16 +10391,16 @@ async function safeParseResponse(res) {
12027
10391
  }
12028
10392
 
12029
10393
  // src/cli/commands/setup.ts
12030
- var import_commander15 = require("commander");
12031
- var import_node_os12 = require("os");
12032
- var import_node_child_process10 = require("child_process");
12033
- var import_node_crypto10 = require("crypto");
12034
- var import_node_fs23 = require("fs");
10394
+ var import_commander14 = require("commander");
10395
+ var import_node_os9 = require("os");
10396
+ var import_node_child_process9 = require("child_process");
10397
+ var import_node_crypto6 = require("crypto");
10398
+ var import_node_fs19 = require("fs");
12035
10399
  var import_node_http2 = require("http");
12036
10400
  init_util();
12037
10401
  init_ui();
12038
10402
  function buildSetupCommand() {
12039
- return new import_commander15.Command("setup").description("Set up this local runtime and bind it to Prismer Cloud").argument("[api-key]", "Prismer daemon API key; primarily for manual recovery and automation").option(
10403
+ return new import_commander14.Command("setup").description("Set up this local runtime and bind it to Prismer Cloud").argument("[api-key]", "Prismer daemon API key; primarily for manual recovery and automation").option(
12040
10404
  "--cloud <url>",
12041
10405
  `Cloud base URL (default: ${DEFAULT_CLOUD_BASE_URL}; bare host:port is auto-prefixed http://)`,
12042
10406
  (v) => v
@@ -12049,7 +10413,7 @@ function buildSetupCommand() {
12049
10413
  } catch (err) {
12050
10414
  exitWithError(err.message, { code: "invalid_cloud_url" });
12051
10415
  }
12052
- let apiKey = apiKeyArg ?? (opts.force ? void 0 : process.env.PRISMER_API_KEY);
10416
+ let apiKey = apiKeyArg ?? process.env.PRISMER_API_KEY;
12053
10417
  const authToken = opts.token ?? process.env.PRISMER_AUTH_TOKEN;
12054
10418
  if (!opts.json) {
12055
10419
  printBanner({ compact: true });
@@ -12057,6 +10421,7 @@ function buildSetupCommand() {
12057
10421
  getUI().blank();
12058
10422
  }
12059
10423
  const shouldStart = opts.start !== false;
10424
+ stopRunningDaemon(paths);
12060
10425
  if (opts.pair || opts.asUser) {
12061
10426
  if (!opts.json) warn("Legacy pair setup path", "plain `prismer setup --start` is the canonical runtime binding flow");
12062
10427
  if (opts.asUser && process.env.LOCAL_ONLY !== "1") {
@@ -12073,7 +10438,7 @@ function buildSetupCommand() {
12073
10438
  }
12074
10439
  const result = await pair({
12075
10440
  cloudBaseUrl,
12076
- deviceName: opts.deviceName ?? (0, import_node_os12.hostname)(),
10441
+ deviceName: opts.deviceName ?? (0, import_node_os9.hostname)(),
12077
10442
  force: opts.force,
12078
10443
  paths,
12079
10444
  asUserEmail: opts.asUser
@@ -12090,13 +10455,13 @@ function buildSetupCommand() {
12090
10455
  apiKey = await mintDaemonApiKey({
12091
10456
  cloudBaseUrl,
12092
10457
  token: authToken,
12093
- deviceName: opts.deviceName ?? (0, import_node_os12.hostname)()
10458
+ deviceName: opts.deviceName ?? (0, import_node_os9.hostname)()
12094
10459
  });
12095
10460
  }
12096
10461
  if (!apiKey && !authToken && !opts.check && opts.browser !== false) {
12097
10462
  apiKey = await runBrowserSetup({
12098
10463
  cloudBaseUrl,
12099
- deviceName: opts.deviceName ?? (0, import_node_os12.hostname)(),
10464
+ deviceName: opts.deviceName ?? (0, import_node_os9.hostname)(),
12100
10465
  json: Boolean(opts.json)
12101
10466
  });
12102
10467
  }
@@ -12147,11 +10512,10 @@ function buildSetupCommand() {
12147
10512
  return;
12148
10513
  }
12149
10514
  const previousConfig = configExists(paths) ? loadConfig(paths) : null;
12150
- const daemonId = previousConfig && previousConfig.api_key === apiKey && previousConfig.cloud_api_base === cloudBaseUrl ? previousConfig.daemon_id : newDaemonId();
12151
10515
  const config = {
12152
10516
  api_key: apiKey,
12153
10517
  cloud_api_base: cloudBaseUrl,
12154
- daemon_id: daemonId
10518
+ daemon_id: newDaemonId()
12155
10519
  };
12156
10520
  if (previousConfig && shouldArchiveLocalDb(previousConfig, config)) {
12157
10521
  archiveLocalDb(paths.localDb);
@@ -12181,7 +10545,7 @@ function buildSetupCommand() {
12181
10545
  }, { code: "setup_failed" }));
12182
10546
  }
12183
10547
  async function runBrowserSetup(input) {
12184
- const state = (0, import_node_crypto10.randomBytes)(18).toString("base64url");
10548
+ const state = (0, import_node_crypto6.randomBytes)(18).toString("base64url");
12185
10549
  const server = (0, import_node_http2.createServer)();
12186
10550
  const apiKeyPromise = waitForSetupCallback(server, state);
12187
10551
  await new Promise((resolve3, reject) => {
@@ -12244,7 +10608,7 @@ function waitForSetupCallback(server, expectedState) {
12244
10608
  function openBrowser(url) {
12245
10609
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
12246
10610
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
12247
- const child = (0, import_node_child_process10.spawn)(command, args, { stdio: "ignore", detached: true });
10611
+ const child = (0, import_node_child_process9.spawn)(command, args, { stdio: "ignore", detached: true });
12248
10612
  child.on("error", () => {
12249
10613
  });
12250
10614
  child.unref();
@@ -12271,20 +10635,42 @@ function withTimeout(promise, ms, message) {
12271
10635
  });
12272
10636
  }
12273
10637
  function startDaemonDetached(home) {
12274
- const child = (0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], "daemon", "start"], {
10638
+ const child = (0, import_node_child_process9.spawn)(process.execPath, [process.argv[1], "daemon", "start"], {
12275
10639
  env: { ...process.env, PRISMER_HOME: home },
12276
10640
  stdio: "ignore",
12277
10641
  detached: true
12278
10642
  });
12279
10643
  child.unref();
12280
10644
  }
10645
+ function stopRunningDaemon(paths) {
10646
+ const pid = readPidFile(paths);
10647
+ if (!pid || !pidAlive(pid)) {
10648
+ if (pid) clearPidFile(paths);
10649
+ return;
10650
+ }
10651
+ try {
10652
+ process.kill(pid, "SIGTERM");
10653
+ } catch {
10654
+ return;
10655
+ }
10656
+ const deadline = Date.now() + 5e3;
10657
+ while (Date.now() < deadline) {
10658
+ if (!pidAlive(pid)) {
10659
+ clearPidFile(paths);
10660
+ return;
10661
+ }
10662
+ const start = Date.now();
10663
+ while (Date.now() - start < 200) {
10664
+ }
10665
+ }
10666
+ }
12281
10667
  function shouldArchiveLocalDb(previous, next) {
12282
10668
  return previous.api_key !== next.api_key || previous.cloud_api_base !== next.cloud_api_base || previous.daemon_id !== next.daemon_id;
12283
10669
  }
12284
10670
  function archiveLocalDb(localDbPath) {
12285
- if (!(0, import_node_fs23.existsSync)(localDbPath)) return;
10671
+ if (!(0, import_node_fs19.existsSync)(localDbPath)) return;
12286
10672
  const archived = `${localDbPath}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.bak`;
12287
- (0, import_node_fs23.renameSync)(localDbPath, archived);
10673
+ (0, import_node_fs19.renameSync)(localDbPath, archived);
12288
10674
  }
12289
10675
  async function mintDaemonApiKey(input) {
12290
10676
  const label = `Daemon: ${input.deviceName} ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
@@ -12304,12 +10690,11 @@ async function mintDaemonApiKey(input) {
12304
10690
  }
12305
10691
 
12306
10692
  // src/cli/commands/status.ts
12307
- var import_commander16 = require("commander");
12308
- var import_node_os13 = require("os");
10693
+ var import_commander15 = require("commander");
12309
10694
  init_util();
12310
10695
  init_ui();
12311
10696
  function buildStatusCommand() {
12312
- return new import_commander16.Command("status").description("Show daemon + config + cloud status").option("--json", "Output machine-readable JSON").action(async (opts) => {
10697
+ return new import_commander15.Command("status").description("Show daemon + config + cloud status").option("--json", "Output machine-readable JSON").action(async (opts) => {
12313
10698
  const paths = resolvePaths();
12314
10699
  if (!configExists(paths)) {
12315
10700
  const report2 = { configured: false, paired: false, paths: { config: paths.configFile } };
@@ -12377,7 +10762,11 @@ function buildStatusCommand() {
12377
10762
  info: daemonStatus.info ?? {}
12378
10763
  },
12379
10764
  cloud: { base: cfg.cloud_api_base, reachable: cloudOk, me, devices, agents },
12380
- local
10765
+ local,
10766
+ binding: {
10767
+ daemonId: cfg.daemon_id,
10768
+ apiKey: cfg.api_key
10769
+ }
12381
10770
  };
12382
10771
  if (opts.json) {
12383
10772
  printJson(report);
@@ -12400,8 +10789,8 @@ async function readDaemonStatus() {
12400
10789
  const paths = resolvePaths();
12401
10790
  const pid = readPidFile(paths);
12402
10791
  if (pid) {
12403
- const { pidAlive: pidAlive3 } = await Promise.resolve().then(() => (init_util(), util_exports));
12404
- if (pidAlive3(pid)) return { running: true, pid };
10792
+ const { pidAlive: pidAlive2 } = await Promise.resolve().then(() => (init_util(), util_exports));
10793
+ if (pidAlive2(pid)) return { running: true, pid };
12405
10794
  }
12406
10795
  return { running: false };
12407
10796
  }
@@ -12429,9 +10818,8 @@ function printPretty(report) {
12429
10818
  ui.blank();
12430
10819
  ok("Config", report.paths.config);
12431
10820
  if (report.daemon.running) {
12432
- const daemonName = report.cloud.me && typeof report.cloud.me === "object" ? report.cloud.me.user?.username ?? (0, import_node_os13.hostname)() : (0, import_node_os13.hostname)();
12433
10821
  const ws = report.daemon.wsConnected ? "connected" : "pending";
12434
- ok("Daemon", `${daemonName} pid=${report.daemon.pid} ws=${ws}`);
10822
+ ok("Daemon", `pid=${report.daemon.pid} ws=${ws}`);
12435
10823
  if (report.daemon.info) {
12436
10824
  const info2 = report.daemon.info;
12437
10825
  if (info2.version) ui.line(` Version: ${info2.version}`);
@@ -12446,7 +10834,8 @@ function printPretty(report) {
12446
10834
  ok("Cloud", report.cloud.base);
12447
10835
  const me = report.cloud.me;
12448
10836
  if (me?.user) {
12449
- ui.line(` User: ${me.user.displayName ?? me.user.username ?? "?"}`);
10837
+ const roleTag = me.user.role ? ` role=${me.user.role}` : "";
10838
+ ui.line(` Account: ${me.user.displayName ?? me.user.username ?? "?"}${roleTag}`);
12450
10839
  }
12451
10840
  if (me?.credits) {
12452
10841
  ui.line(` Credits: ${typeof me.credits.balance === "number" ? me.credits.balance.toLocaleString() : "?"}`);
@@ -12455,6 +10844,12 @@ function printPretty(report) {
12455
10844
  fail2("Cloud", `${report.cloud.base} unreachable or unauthorized`);
12456
10845
  tip("prismer setup --force");
12457
10846
  }
10847
+ if (report.binding) {
10848
+ ui.blank();
10849
+ ui.line(` Daemon ID: ${report.binding.daemonId}`);
10850
+ const masked = report.binding.apiKey.slice(0, 14) + "\u2022\u2022\u2022\u2022" + report.binding.apiKey.slice(-4);
10851
+ ui.line(` API Key: ${masked}`);
10852
+ }
12458
10853
  if (Array.isArray(report.cloud.devices) && report.cloud.devices.length > 0) {
12459
10854
  const devs = report.cloud.devices;
12460
10855
  ui.blank();
@@ -12465,6 +10860,8 @@ function printPretty(report) {
12465
10860
  const declared = d.hostedAgentSummary?.declared ?? 0;
12466
10861
  ui.line(` ${statusIcon} ${d.podName?.replace(/^daemon:/, "")?.slice(0, 28) ?? "?"} kind=${kind} agents=${declared}`);
12467
10862
  }
10863
+ } else {
10864
+ ui.line(` Workspace Devices: none`);
12468
10865
  }
12469
10866
  if (Array.isArray(report.cloud.agents) && report.cloud.agents.length > 0) {
12470
10867
  ui.line(` Hosted agents: ${report.cloud.agents.length}`);
@@ -12480,14 +10877,14 @@ function printPretty(report) {
12480
10877
  }
12481
10878
 
12482
10879
  // src/cli/commands/task.ts
12483
- var import_commander17 = require("commander");
12484
- var import_promises5 = require("timers/promises");
10880
+ var import_commander16 = require("commander");
10881
+ var import_promises3 = require("timers/promises");
12485
10882
  init_util();
12486
10883
  function describeStatus(status) {
12487
10884
  return status === 0 ? "network error" : `HTTP ${status}`;
12488
10885
  }
12489
10886
  function buildTaskCommand() {
12490
- const cmd = new import_commander17.Command("task").description("Manage tasks");
10887
+ const cmd = new import_commander16.Command("task").description("Manage tasks");
12491
10888
  cmd.command("create").description("Create + dispatch a task to the given agent and wait for completion").requiredOption("--agent <imUserId>", "Assignee agent IMUser.id").requiredOption("--prompt <text>", "Prompt to send to the adapter").option("--profile <profileId>", "AgentProfile id (cloud picks first if omitted)").option("--capability <name>", "Capability tag", "chat").option("--title <text>", "Task title (defaults to prompt prefix)").option("--timeout-ms <ms>", "Per-task timeout", (v) => Number.parseInt(v, 10)).option("--no-wait", "Just create + print task id; don't poll").option("--json", "Output JSON (default)").action(runAction(async (opts) => {
12492
10889
  const cloud = mkCloud6();
12493
10890
  const body = {
@@ -12564,7 +10961,7 @@ function unwrap2(raw) {
12564
10961
  async function pollUntilTerminal(cloud, taskId, timeoutMs) {
12565
10962
  const deadline = Date.now() + (timeoutMs ?? 5 * 6e4);
12566
10963
  while (Date.now() < deadline) {
12567
- await (0, import_promises5.setTimeout)(1e3);
10964
+ await (0, import_promises3.setTimeout)(1e3);
12568
10965
  const res = await cloud.request(
12569
10966
  "GET",
12570
10967
  `/api/im/tasks/${encodeURIComponent(taskId)}`
@@ -12601,11 +10998,11 @@ function taskFrom2(raw) {
12601
10998
  }
12602
10999
 
12603
11000
  // src/cli/commands/workspace.ts
12604
- var import_commander18 = require("commander");
11001
+ var import_commander17 = require("commander");
12605
11002
  init_util();
12606
11003
  init_ui();
12607
11004
  function buildWorkspaceCommand() {
12608
- const cmd = new import_commander18.Command("workspace").description("Manage workspaces, runtime snapshots, and workspace files");
11005
+ const cmd = new import_commander17.Command("workspace").description("Manage workspaces, runtime snapshots, and workspace files");
12609
11006
  cmd.command("list").description("List workspaces").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
12610
11007
  const cloud = mkCloud7();
12611
11008
  const data = await cloud.get("/api/im/workspaces");
@@ -12839,15 +11236,18 @@ async function readResponseError(res) {
12839
11236
 
12840
11237
  // src/cli/index.ts
12841
11238
  init_ui();
12842
- var VERSION = "1.9.7";
11239
+
11240
+ // package.json
11241
+ var version = "1.9.22";
11242
+
11243
+ // src/cli/index.ts
12843
11244
  function buildProgram() {
12844
- const program = new import_commander19.Command("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
11245
+ const program = new import_commander18.Command("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(version);
12845
11246
  program.addCommand(buildBannerCommand());
12846
11247
  program.addCommand(buildSetupCommand());
12847
11248
  program.addCommand(buildConfigCommand());
12848
11249
  program.addCommand(buildDaemonCommand());
12849
11250
  program.addCommand(buildPairCommand());
12850
- program.addCommand(buildResetCommand());
12851
11251
  program.addCommand(buildStatusCommand());
12852
11252
  program.addCommand(buildAdapterCommand());
12853
11253
  program.addCommand(buildAgentCommand());