@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.js CHANGED
@@ -1,5 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
  var __esm = (fn, res) => function __init() {
4
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
11
  };
@@ -9,12 +15,12 @@ var __export = (target, all) => {
9
15
  };
10
16
 
11
17
  // src/cli/ui.ts
12
- import * as fs5 from "fs";
13
- import * as path8 from "path";
14
- import { fileURLToPath as fileURLToPath2 } from "url";
18
+ import * as fs4 from "fs";
19
+ import * as path6 from "path";
20
+ import { fileURLToPath } from "url";
15
21
  function thisDirname() {
16
22
  try {
17
- return path8.dirname(fileURLToPath2(import.meta.url));
23
+ return path6.dirname(fileURLToPath(import.meta.url));
18
24
  } catch {
19
25
  return process.cwd();
20
26
  }
@@ -24,18 +30,18 @@ function findIconPath(size = "big") {
24
30
  const here = thisDirname();
25
31
  const candidates = [
26
32
  // npm-installed: node_modules/@prismer/runtime/dist/cli.js → ../assets
27
- path8.resolve(here, "../assets", name),
33
+ path6.resolve(here, "../assets", name),
28
34
  // alternate dist layout (sub-bundle): dist/bin/cli.js → ../../assets
29
- path8.resolve(here, "../../assets", name),
35
+ path6.resolve(here, "../../assets", name),
30
36
  // source/typecheck: src/cli/ui.ts → ../../assets
31
- path8.resolve(here, "../../assets", name),
37
+ path6.resolve(here, "../../assets", name),
32
38
  // dev mode: cwd happens to be runtime root
33
- path8.resolve(process.cwd(), "assets", name),
34
- path8.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
39
+ path6.resolve(process.cwd(), "assets", name),
40
+ path6.resolve(process.cwd(), "sdk/prismer-cloud/runtime/assets", name)
35
41
  ];
36
42
  for (const candidate of candidates) {
37
43
  try {
38
- if (fs5.existsSync(candidate)) return candidate;
44
+ if (fs4.existsSync(candidate)) return candidate;
39
45
  } catch {
40
46
  }
41
47
  }
@@ -159,7 +165,7 @@ var init_ui = __esm({
159
165
  const iconPath = findIconPath("small");
160
166
  if (iconPath !== null) {
161
167
  try {
162
- const raw = fs5.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
168
+ const raw = fs4.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
163
169
  for (const line of raw.split("\n")) {
164
170
  this.write(this.cyan(line) + "\n");
165
171
  }
@@ -182,7 +188,7 @@ var init_ui = __esm({
182
188
  const shouldUseFull = opts?.full === true || width >= 120;
183
189
  if (shouldUseFull && iconPath !== null) {
184
190
  try {
185
- const raw = fs5.readFileSync(iconPath, "utf-8");
191
+ const raw = fs4.readFileSync(iconPath, "utf-8");
186
192
  const lines = raw.split("\n");
187
193
  for (const line of lines) {
188
194
  const brandedLine = line.replace("Prismer Cloud SDK", "Prismer Runtime CLI");
@@ -446,7 +452,7 @@ __export(util_exports, {
446
452
  info: () => info,
447
453
  normalizeCloudUrl: () => normalizeCloudUrl,
448
454
  ok: () => ok,
449
- pidAlive: () => pidAlive2,
455
+ pidAlive: () => pidAlive,
450
456
  pidFilePath: () => pidFilePath,
451
457
  printBanner: () => printBanner,
452
458
  printJson: () => printJson,
@@ -457,8 +463,8 @@ __export(util_exports, {
457
463
  warn: () => warn,
458
464
  writePidFile: () => writePidFile
459
465
  });
460
- import { existsSync as existsSync11, readFileSync as readFileSync9, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
461
- import { join as join14 } from "path";
466
+ import { existsSync as existsSync11, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
467
+ import { join as join10 } from "path";
462
468
  function color(kind, text) {
463
469
  if (process.env.NO_COLOR === "1" || process.env.NO_COLOR === "true") return text;
464
470
  return `${ANSI[kind]}${text}${ANSI.reset}`;
@@ -523,10 +529,10 @@ function runAction(fn, opts = {}) {
523
529
  function printBanner(opts = {}) {
524
530
  const ui = getUI();
525
531
  if (opts.compact) {
526
- ui.smallHeader("Runtime CLI v1.9.7");
532
+ ui.smallHeader("Runtime CLI v${VERSION}");
527
533
  return;
528
534
  }
529
- ui.banner("Runtime CLI v1.9.7", { full: true });
535
+ ui.banner("Runtime CLI v${VERSION}", { full: true });
530
536
  }
531
537
  function ok(label, detail) {
532
538
  getUI().ok(label, detail);
@@ -552,7 +558,7 @@ function table(rows, columns) {
552
558
  getUI().table(rows, { columns });
553
559
  }
554
560
  function pidFilePath(paths) {
555
- return join14(paths.root, "daemon.pid");
561
+ return join10(paths.root, "daemon.pid");
556
562
  }
557
563
  function writePidFile(paths, pid) {
558
564
  writeFileSync6(pidFilePath(paths), `${pid}
@@ -561,7 +567,7 @@ function writePidFile(paths, pid) {
561
567
  function readPidFile(paths) {
562
568
  const p = pidFilePath(paths);
563
569
  if (!existsSync11(p)) return void 0;
564
- const raw = readFileSync9(p, "utf8").trim();
570
+ const raw = readFileSync8(p, "utf8").trim();
565
571
  const pid = Number.parseInt(raw, 10);
566
572
  return Number.isFinite(pid) ? pid : void 0;
567
573
  }
@@ -574,7 +580,7 @@ function clearPidFile(paths) {
574
580
  }
575
581
  }
576
582
  }
577
- function pidAlive2(pid) {
583
+ function pidAlive(pid) {
578
584
  try {
579
585
  process.kill(pid, 0);
580
586
  return true;
@@ -657,36 +663,15 @@ var AdapterRegistry = class {
657
663
  };
658
664
 
659
665
  // src/adapters/hermes/index.ts
660
- import { execFileSync, spawn } from "child_process";
666
+ import { spawn } from "child_process";
661
667
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
662
- import { createRequire } from "module";
663
668
  import { homedir } from "os";
664
669
  import { dirname, join } from "path";
665
- import { fileURLToPath } from "url";
666
670
  import Database from "better-sqlite3";
667
671
  import * as YAML from "yaml";
668
672
  import { z } from "zod";
669
673
  var PRISMER_IM_SKILL_NAME = "prismer-im-collab";
670
- var PRISMER_IM_SKILL_CONTENT = `---
671
- name: prismer-im-collab
672
- description: Coordinate reliably in Prismer conversations, use workspace assets through bounded MCP tools, and keep task work on the board.
673
- 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
674
- metadata:
675
- prismer:
676
- version: "1.0.0"
677
- mcp_tools:
678
- - prismer.agent.send
679
- - prismer.conversation.listAgents
680
- - prismer.task.create
681
- - prismer.task.update
682
- - prismer.task.complete
683
- - prismer.approval.request_human_approval
684
- - prismer.asset.search
685
- - prismer.asset.describe
686
- - prismer.asset.read
687
- ---
688
-
689
- # Prismer IM Collaboration
674
+ var PRISMER_IM_SKILL_CONTENT = `# Prismer IM Collaboration
690
675
 
691
676
  You are an agent in a Prismer multi-agent workspace. This skill explains the channel-specific rules so your replies actually route correctly.
692
677
 
@@ -716,19 +701,6 @@ You will receive a \`[Channel context]\` block at the top of each prompt indicat
716
701
  | Address another agent so the platform dispatches your message to them | Call \`prismer.agent.send\` (from the \`prismer-tasks\` MCP server). |
717
702
  | Look up which agents you can talk to | Call \`prismer.conversation.listAgents\` before guessing usernames. |
718
703
  | Create / update tasks on the workspace board | \`prismer.task.create\` / \`prismer.task.update\` from \`prismer-tasks\` MCP. |
719
- | Ask for structured human sign-off | \`prismer.approval.request_human_approval\`. |
720
- | Find uploaded workspace files | \`prismer.asset.search\`. |
721
- | Inspect file metadata before reading bytes | \`prismer.asset.describe\`. |
722
- | Read a bounded byte range from a file | \`prismer.asset.read\`. |
723
-
724
- ## Workspace asset rules
725
-
726
- - Web-uploaded workspace files are available as asset metadata first; bytes are fetched lazily by the local daemon.
727
- - When a task needs file content, call \`prismer.asset.describe\` first, then read only the needed range with \`prismer.asset.read\`.
728
- - For many files, call \`prismer.asset.search\` to find candidates. Do not ask the user to paste file contents already present in workspace assets.
729
- - Do not claim you read a file unless an asset tool call succeeded.
730
- - For large tables/documents, read headers, samples, or targeted byte ranges. Never try to load the whole file into a reply.
731
- - Cite the asset id/URI and locator returned by the tool when using file evidence.
732
704
 
733
705
  ### When to use \`prismer.agent.send\` vs inline \`@\`
734
706
 
@@ -742,7 +714,6 @@ You will receive a \`[Channel context]\` block at the top of each prompt indicat
742
714
  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 @).
743
715
  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.
744
716
  4. **Check tool return values.** \`prismer.agent.send\` may return \`{ ok: false, error: 'agent_not_found' }\`; surface the error rather than pretending it worked.
745
- 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.
746
717
 
747
718
  ## Example: group discussion handoff
748
719
 
@@ -830,23 +801,7 @@ var HermesProfileConfigSchema = z.object({
830
801
  hermesSourceDir: z.string().optional(),
831
802
  nativeMirrorTimeoutMs: z.number().int().positive().default(2e3),
832
803
  /** Task authority level: executor (default) or orchestrator. */
833
- taskAuthority: z.enum(["executor", "orchestrator"]).optional().default("executor"),
834
- /** Human approval escalation policy. Enforcement is currently prompt/cloud-side. */
835
- approvalPolicy: z.enum(["strict", "auto-low-risk", "autonomous"]).optional().default("auto-low-risk"),
836
- /** Agent-level MCP tool allowlist. Empty/null means all tools. Supports exact names and trailing * wildcards. */
837
- mcpAllowlist: z.array(z.string()).nullable().optional(),
838
- /** Agent-level operating principles injected through /v1/runs instructions. */
839
- operatingPrinciples: z.union([z.string(), z.record(z.string())]).optional(),
840
- /**
841
- * Role-template snapshot carried in AgentProfile.config by cloud ACP.
842
- * Expected fields used here:
843
- * - roleTemplate.mcpServers[].toolsAllowlist
844
- * - roleTemplate.operatingPrinciples
845
- */
846
- roleTemplate: z.object({
847
- mcpServers: z.array(z.object({ toolsAllowlist: z.array(z.string()).optional() }).passthrough()).optional(),
848
- operatingPrinciples: z.union([z.string(), z.record(z.string())]).optional()
849
- }).passthrough().optional()
804
+ taskAuthority: z.enum(["executor", "orchestrator"]).optional().default("executor")
850
805
  });
851
806
  var hermesAdapter = {
852
807
  name: "hermes",
@@ -861,22 +816,14 @@ var hermesAdapter = {
861
816
  errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
862
817
  };
863
818
  },
864
- async prepareProfile(profile) {
865
- const config = HermesProfileConfigSchema.parse(profile.config);
866
- if (config.configurePrismerProvider) {
867
- const profileName = getHermesProfileName(profile);
868
- const agentUsername = profile.agentUsername || profileName;
869
- configurePrismerProvider(profileName, config, agentUsername, profile.workspaceId);
870
- }
871
- },
872
819
  async ensureService(profile) {
873
820
  const config = HermesProfileConfigSchema.parse(profile.config);
874
- const profileName = getHermesProfileName(profile);
875
- const portOverride = config.port !== 8642 ? config.port : portForProfile(`${profileName}:${config.apiKey}`, 8642);
821
+ const profileName = config.hermesProfileName && config.hermesProfileName !== "default" ? config.hermesProfileName : profile.agentUsername || profile.id.slice(0, 8);
822
+ const portOverride = config.port !== 8642 ? config.port : portForProfile(profileName, 8642);
876
823
  const baseUrl = `http://127.0.0.1:${portOverride}`;
877
824
  const agentUsername = profile.agentUsername || profileName;
878
825
  if (config.configurePrismerProvider) {
879
- configurePrismerProvider(profileName, config, agentUsername, profile.workspaceId);
826
+ configurePrismerProvider(profileName, config, agentUsername);
880
827
  }
881
828
  if (!await checkHealth(baseUrl, config.apiKey)) {
882
829
  if (!config.autoStart) {
@@ -886,7 +833,6 @@ var hermesAdapter = {
886
833
  (set autoStart: true on the AgentProfile to spawn it automatically)`
887
834
  );
888
835
  }
889
- await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
890
836
  spawn("hermes", ["-p", profileName, "gateway", "run"], {
891
837
  detached: false,
892
838
  stdio: "ignore",
@@ -946,8 +892,6 @@ var HermesService = class {
946
892
  const startedAt = Date.now();
947
893
  let runId;
948
894
  const sysPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
949
- const operatingPrinciples = typeof task.metadata?.operatingPrinciples === "string" ? task.metadata.operatingPrinciples : void 0;
950
- const instructions = [sysPrompt, operatingPrinciples].filter(Boolean).join("\n\n");
951
895
  if (sysPrompt) {
952
896
  try {
953
897
  const profileDir = getHermesProfileDir(this.profileName);
@@ -984,7 +928,7 @@ var HermesService = class {
984
928
  // sent `messages: [...]` which Hermes 0.12 rejects with 400.
985
929
  body: JSON.stringify({
986
930
  input: task.prompt,
987
- instructions: instructions || void 0,
931
+ instructions: sysPrompt,
988
932
  session_id: typeof task.metadata?.conversationId === "string" ? task.metadata.conversationId : void 0,
989
933
  stream: true
990
934
  }),
@@ -1269,7 +1213,7 @@ function formatHermesGoalText(goals) {
1269
1213
  return `${index + 1}. ${priority}${goal.title}${description}`;
1270
1214
  }).join("\n");
1271
1215
  }
1272
- function configurePrismerProvider(profileName, config, agentUsername, workspaceId) {
1216
+ function configurePrismerProvider(profileName, config, agentUsername) {
1273
1217
  const baseUrl = resolvePrismerProviderBaseUrl(config);
1274
1218
  const apiKey = resolvePrismerApiKey(config);
1275
1219
  if (!baseUrl || !apiKey) {
@@ -1312,16 +1256,9 @@ function configurePrismerProvider(profileName, config, agentUsername, workspaceI
1312
1256
  PRISMER_API_KEY: apiKey,
1313
1257
  PRISMER_BASE_URL: baseUrl.replace(/\/api\/v1\/?$/, "")
1314
1258
  };
1315
- if (workspaceId) {
1316
- mcpEnv.PRISMER_WORKSPACE_ID = workspaceId;
1317
- }
1318
1259
  if (agentUsername) {
1319
1260
  mcpEnv.PRISMER_AGENT_USERNAME = agentUsername;
1320
1261
  }
1321
- const allowlist = resolveMcpAllowlist(config);
1322
- if (allowlist) {
1323
- mcpEnv.PRISMER_MCP_ALLOWLIST = allowlist.join(",");
1324
- }
1325
1262
  mcpServers["prismer-tasks"] = {
1326
1263
  command: "node",
1327
1264
  args: [serverPath],
@@ -1342,17 +1279,20 @@ function resolvePrismerMcpServerPath(config) {
1342
1279
  if (config.prismerMcpServerPath) return config.prismerMcpServerPath;
1343
1280
  if (process.env.PRISMER_MCP_SERVER) return process.env.PRISMER_MCP_SERVER;
1344
1281
  try {
1282
+ const { createRequire } = __require("module");
1345
1283
  const req = createRequire(import.meta.url);
1346
1284
  return req.resolve("@prismer/mcp-server");
1347
1285
  } catch {
1348
1286
  }
1349
1287
  try {
1350
- const here = dirname(fileURLToPath(import.meta.url));
1351
- const candidate = join(here, "../../mcp/dist/index.js");
1288
+ const { fileURLToPath: fileURLToPath2 } = __require("url");
1289
+ const { join: join15, dirname: dirname8 } = __require("path");
1290
+ const here = dirname8(fileURLToPath2(import.meta.url));
1291
+ const candidate = join15(here, "../../mcp/dist/index.js");
1352
1292
  if (existsSync(candidate)) return candidate;
1353
- const candidate2 = join(here, "../../../mcp/dist/index.js");
1293
+ const candidate2 = join15(here, "../../../mcp/dist/index.js");
1354
1294
  if (existsSync(candidate2)) return candidate2;
1355
- const candidate3 = join(here, "../../../../mcp/dist/index.js");
1295
+ const candidate3 = join15(here, "../../../../mcp/dist/index.js");
1356
1296
  if (existsSync(candidate3)) return candidate3;
1357
1297
  } catch {
1358
1298
  }
@@ -1521,29 +1461,25 @@ function runPythonJson(args, env, timeoutMs) {
1521
1461
  });
1522
1462
  });
1523
1463
  }
1524
- function readHermesConfig(path9) {
1525
- if (!existsSync(path9)) return {};
1464
+ function readHermesConfig(path7) {
1465
+ if (!existsSync(path7)) return {};
1526
1466
  try {
1527
- return YAML.parse(readFileSync(path9, "utf8")) ?? {};
1467
+ return YAML.parse(readFileSync(path7, "utf8")) ?? {};
1528
1468
  } catch (err) {
1529
- throw new Error(`Failed to parse Hermes config at ${path9}: ${err.message}`);
1469
+ throw new Error(`Failed to parse Hermes config at ${path7}: ${err.message}`);
1530
1470
  }
1531
1471
  }
1532
- function writeEnvValue(path9, key, value) {
1533
- mkdirSync(dirname(path9), { recursive: true });
1534
- const lines = existsSync(path9) ? readFileSync(path9, "utf8").split(/\r?\n/) : [];
1472
+ function writeEnvValue(path7, key, value) {
1473
+ mkdirSync(dirname(path7), { recursive: true });
1474
+ const lines = existsSync(path7) ? readFileSync(path7, "utf8").split(/\r?\n/) : [];
1535
1475
  const next = lines.filter((line) => !line.startsWith(`${key}=`) && line.trim() !== "");
1536
1476
  next.push(`${key}=${quoteEnv(value)}`);
1537
- writeFileSync(path9, `${next.join("\n")}
1477
+ writeFileSync(path7, `${next.join("\n")}
1538
1478
  `, "utf8");
1539
1479
  }
1540
1480
  function quoteEnv(value) {
1541
1481
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1542
1482
  }
1543
- function getHermesProfileName(profile) {
1544
- const configured = profile.config && typeof profile.config.hermesProfileName === "string" ? profile.config.hermesProfileName : void 0;
1545
- return configured && configured !== "default" ? configured : profile.agentUsername || profile.id.slice(0, 8);
1546
- }
1547
1483
  function getHermesProfileDir(profileName) {
1548
1484
  const root = process.env.HERMES_HOME || join(homedir(), ".hermes");
1549
1485
  if (!profileName || profileName === "default") return root;
@@ -1565,18 +1501,6 @@ function resolvePrismerProviderBaseUrl(config) {
1565
1501
  function resolvePrismerApiKey(config) {
1566
1502
  return process.env[config.prismerApiKeyEnv] || process.env.PRISMER_API_KEY || "";
1567
1503
  }
1568
- function resolveMcpAllowlist(config) {
1569
- if (Array.isArray(config.mcpAllowlist)) return normalizeAllowlist(config.mcpAllowlist);
1570
- const prismerServer = config.roleTemplate?.mcpServers?.find((server) => {
1571
- const record = server;
1572
- return record.package === "@prismer/mcp-server" || record.name === "prismer-tasks";
1573
- });
1574
- if (!prismerServer) return null;
1575
- return Array.isArray(prismerServer.toolsAllowlist) ? normalizeAllowlist(prismerServer.toolsAllowlist) : null;
1576
- }
1577
- function normalizeAllowlist(values) {
1578
- return values.map((value) => value.trim()).filter(Boolean);
1579
- }
1580
1504
  function normalizeProviderName(value) {
1581
1505
  return value.trim().toLowerCase().replace(/\s+/g, "-");
1582
1506
  }
@@ -1682,66 +1606,6 @@ async function waitForHealthy(baseUrl, apiKey, timeoutMs) {
1682
1606
  }
1683
1607
  throw new Error(`Hermes did not become healthy at ${baseUrl} within ${timeoutMs}ms`);
1684
1608
  }
1685
- async function stopStaleHermesGateways(profileName, port, timeoutMs) {
1686
- const pids = findHermesGatewayPids(profileName, port);
1687
- for (const pid of pids) {
1688
- try {
1689
- process.kill(pid, "SIGTERM");
1690
- } catch {
1691
- continue;
1692
- }
1693
- }
1694
- const deadline = Date.now() + Math.min(timeoutMs, 5e3);
1695
- while (Date.now() < deadline) {
1696
- const alive = pids.filter(pidAlive);
1697
- if (alive.length === 0) return;
1698
- await new Promise((r) => setTimeout(r, 100));
1699
- }
1700
- for (const pid of pids) {
1701
- if (!pidAlive(pid)) continue;
1702
- try {
1703
- process.kill(pid, "SIGKILL");
1704
- } catch {
1705
- }
1706
- }
1707
- }
1708
- function findHermesGatewayPids(profileName, port) {
1709
- try {
1710
- const out = execFileSync("ps", ["-axo", "pid=,command="], { encoding: "utf8" });
1711
- const current = process.pid;
1712
- const escapedProfile = escapeRegExp(profileName);
1713
- const profilePattern = new RegExp(`(?:^|\\s)-p\\s+${escapedProfile}(?:\\s|$)`);
1714
- const portPattern = new RegExp(`(?:^|\\s)API_SERVER_PORT=${port}(?:\\s|$)`);
1715
- return out.split(/\r?\n/).map((line) => {
1716
- const trimmed = line.trim();
1717
- const match = /^(\d+)\s+(.+)$/.exec(trimmed);
1718
- if (!match) return null;
1719
- const pid = Number.parseInt(match[1], 10);
1720
- const command = match[2];
1721
- if (!Number.isFinite(pid) || pid === current) return null;
1722
- if (!isHermesGatewayCommand(command)) return null;
1723
- if (!profilePattern.test(command) && !portPattern.test(command)) return null;
1724
- return pid;
1725
- }).filter((pid) => pid !== null);
1726
- } catch {
1727
- return [];
1728
- }
1729
- }
1730
- function isHermesGatewayCommand(command) {
1731
- if (!/\bgateway\b/.test(command) || !/\brun\b/.test(command)) return false;
1732
- return /(^|\s)(?:\S*\/)?hermes(?:\s|$)/.test(command);
1733
- }
1734
- function pidAlive(pid) {
1735
- try {
1736
- process.kill(pid, 0);
1737
- return true;
1738
- } catch {
1739
- return false;
1740
- }
1741
- }
1742
- function escapeRegExp(value) {
1743
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1744
- }
1745
1609
 
1746
1610
  // src/daemon/ws-client.ts
1747
1611
  import { EventEmitter } from "events";
@@ -1981,11 +1845,11 @@ var MIGRATIONS = [
1981
1845
  function runSql(db, sql) {
1982
1846
  db["exec"](sql);
1983
1847
  }
1984
- function openLocalDb(path9) {
1985
- if (path9 !== ":memory:" && !existsSync2(dirname2(path9))) {
1986
- mkdirSync2(dirname2(path9), { recursive: true });
1848
+ function openLocalDb(path7) {
1849
+ if (path7 !== ":memory:" && !existsSync2(dirname2(path7))) {
1850
+ mkdirSync2(dirname2(path7), { recursive: true });
1987
1851
  }
1988
- const db = new Database2(path9);
1852
+ const db = new Database2(path7);
1989
1853
  db.pragma("journal_mode = WAL");
1990
1854
  db.pragma("foreign_keys = ON");
1991
1855
  runMigrations(db);
@@ -2303,12 +2167,11 @@ function deriveWsUrl(httpBase) {
2303
2167
  }
2304
2168
 
2305
2169
  // src/daemon-id.ts
2306
- import { randomBytes } from "crypto";
2307
2170
  import { hostname } from "os";
2308
2171
  var PREFIX = "daemon-";
2309
2172
  function newDaemonId() {
2310
2173
  const host = hostname().replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "unknown";
2311
- return `${PREFIX}${host}-${randomBytes(6).toString("hex")}`;
2174
+ return `${PREFIX}${host}`;
2312
2175
  }
2313
2176
  function isDaemonId(s) {
2314
2177
  return s.startsWith(PREFIX) && s.length > PREFIX.length;
@@ -2323,8 +2186,8 @@ var CloudClient = class {
2323
2186
  opts;
2324
2187
  fetchImpl;
2325
2188
  /** Low-level request. Caller decides on retry; sync-worker handles backoff. */
2326
- async request(method, path9, init) {
2327
- const url = this.urlFor(path9);
2189
+ async request(method, path7, init) {
2190
+ const url = this.urlFor(path7);
2328
2191
  const headers = {
2329
2192
  "Content-Type": "application/json",
2330
2193
  Accept: "application/json",
@@ -2383,8 +2246,8 @@ var CloudClient = class {
2383
2246
  return { ok: true, status: res.status, data: body ?? void 0 };
2384
2247
  }
2385
2248
  /** Convenience: GET expecting `{ ok, data }` envelope. Returns `data` or throws. */
2386
- async get(path9, opts) {
2387
- const res = await this.request("GET", path9, opts);
2249
+ async get(path7, opts) {
2250
+ const res = await this.request("GET", path7, opts);
2388
2251
  if (!res.ok) throw new CloudError(res.status, res.error?.code ?? "unknown", res.error?.message ?? "request failed");
2389
2252
  if (res.data && typeof res.data === "object" && "ok" in res.data) {
2390
2253
  const env = res.data;
@@ -2393,14 +2256,14 @@ var CloudClient = class {
2393
2256
  }
2394
2257
  return res.data;
2395
2258
  }
2396
- urlFor(path9) {
2259
+ urlFor(path7) {
2397
2260
  const trimmedBase = this.opts.baseUrl.replace(/\/$/, "");
2398
- const trimmedPath = path9.startsWith("/") ? path9 : `/${path9}`;
2261
+ const trimmedPath = path7.startsWith("/") ? path7 : `/${path7}`;
2399
2262
  return `${trimmedBase}${trimmedPath}`;
2400
2263
  }
2401
2264
  /** Raw byte download (for asset cache). Returns response so caller can stream body. */
2402
- async fetchRaw(path9, init) {
2403
- const url = this.urlFor(path9);
2265
+ async fetchRaw(path7, init) {
2266
+ const url = this.urlFor(path7);
2404
2267
  const headers = {
2405
2268
  Authorization: `Bearer ${this.opts.apiKey}`,
2406
2269
  ...init?.headers ?? {}
@@ -2449,8 +2312,7 @@ function envelope(type, payload, requestId) {
2449
2312
  }
2450
2313
 
2451
2314
  // src/asset-cache.ts
2452
- import { createHash } from "crypto";
2453
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2315
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, statSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2454
2316
  import { dirname as dirname4, join as join3 } from "path";
2455
2317
  function rowToCached(row) {
2456
2318
  return {
@@ -2543,17 +2405,11 @@ var AssetCache = class {
2543
2405
  throw new Error(`Asset bytes fetch failed for ${hash} (id=${assetId}): HTTP ${res.status}`);
2544
2406
  }
2545
2407
  const buf = Buffer.from(await res.arrayBuffer());
2546
- const actualHash = sha256(buf);
2547
- if (actualHash !== hash) {
2548
- throw new Error(`Asset hash mismatch for ${hash}: downloaded ${actualHash}`);
2549
- }
2550
2408
  const localPath = this.pathFor(hash);
2551
2409
  if (!existsSync4(dirname4(localPath))) {
2552
2410
  mkdirSync4(dirname4(localPath), { recursive: true });
2553
2411
  }
2554
- const tmpPath = `${localPath}.${process.pid}.${Date.now()}.tmp`;
2555
- writeFileSync3(tmpPath, buf);
2556
- renameSync(tmpPath, localPath);
2412
+ writeFileSync3(localPath, buf);
2557
2413
  const mime = res.headers.get("content-type");
2558
2414
  const now = Date.now();
2559
2415
  this.db.prepare(
@@ -2574,10 +2430,6 @@ var AssetCache = class {
2574
2430
  }
2575
2431
  /** Insert an already-on-disk asset into the cache (e.g., asset just produced by adapter). */
2576
2432
  registerLocal(hash, localPath, mime) {
2577
- const actualHash = sha256(readFileBuffer(localPath));
2578
- if (actualHash !== hash) {
2579
- throw new Error(`Asset hash mismatch for ${hash}: local file is ${actualHash}`);
2580
- }
2581
2433
  const size = statSync(localPath).size;
2582
2434
  const now = Date.now();
2583
2435
  this.db.prepare(
@@ -2617,15 +2469,9 @@ var AssetCache = class {
2617
2469
  return { removed, freed };
2618
2470
  }
2619
2471
  };
2620
- function sha256(buf) {
2621
- return createHash("sha256").update(buf).digest("hex");
2622
- }
2623
- function readFileBuffer(path9) {
2624
- return readFileSync3(path9);
2625
- }
2626
2472
 
2627
2473
  // src/daemon/asset/mirror.ts
2628
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
2474
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
2629
2475
  import { join as join4 } from "path";
2630
2476
  function rowToBinding(row) {
2631
2477
  return {
@@ -2656,7 +2502,7 @@ var WorkspaceMirror = class {
2656
2502
  readCursor() {
2657
2503
  if (!existsSync5(this.cursorPath)) return null;
2658
2504
  try {
2659
- const parsed = JSON.parse(readFileSync4(this.cursorPath, "utf8"));
2505
+ const parsed = JSON.parse(readFileSync3(this.cursorPath, "utf8"));
2660
2506
  if (parsed.workspaceId !== this.workspaceId) return null;
2661
2507
  return parsed.cursor;
2662
2508
  } catch {
@@ -2719,8 +2565,8 @@ var WorkspaceMirror = class {
2719
2565
  return { applied, cursor: envelope2.cursor };
2720
2566
  }
2721
2567
  /** Lookup the active binding for a workspace path. Returns undefined if not mirrored. */
2722
- lookupByPath(path9) {
2723
- const row = this.db.prepare("SELECT * FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?").get(this.workspaceId, path9);
2568
+ lookupByPath(path7) {
2569
+ const row = this.db.prepare("SELECT * FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?").get(this.workspaceId, path7);
2724
2570
  return row ? rowToBinding(row) : void 0;
2725
2571
  }
2726
2572
  /** Find an asset's contentHash via any path binding (asset may map to many paths). */
@@ -2982,20 +2828,20 @@ var UriResolver = class {
2982
2828
  }
2983
2829
  return { texts: all, resolvedHashes: dedupe(allHashes) };
2984
2830
  }
2985
- async lookupFile(workspaceId, path9, signal) {
2831
+ async lookupFile(workspaceId, path7, signal) {
2986
2832
  const local = this.db.prepare(
2987
2833
  "SELECT asset_id, content_hash, version FROM workspace_files_mirror WHERE workspace_id = ? AND path = ?"
2988
- ).get(workspaceId, path9);
2834
+ ).get(workspaceId, path7);
2989
2835
  if (local) return local;
2990
2836
  const remote = await this.cloud.get(
2991
- `/api/im/workspaces/${encodeURIComponent(workspaceId)}/files?path=${encodeURIComponent(path9)}`,
2837
+ `/api/im/workspaces/${encodeURIComponent(workspaceId)}/files?path=${encodeURIComponent(path7)}`,
2992
2838
  { signal }
2993
2839
  );
2994
2840
  this.db.prepare(
2995
2841
  `INSERT OR REPLACE INTO workspace_files_mirror
2996
2842
  (workspace_id, path, asset_id, content_hash, version, synced_at, dirty)
2997
2843
  VALUES (?, ?, ?, ?, ?, ?, 0)`
2998
- ).run(workspaceId, path9, remote.assetId, remote.contentHash, remote.version, Date.now());
2844
+ ).run(workspaceId, path7, remote.assetId, remote.contentHash, remote.version, Date.now());
2999
2845
  return { asset_id: remote.assetId, content_hash: remote.contentHash, version: remote.version };
3000
2846
  }
3001
2847
  };
@@ -3004,244 +2850,11 @@ function dedupe(xs) {
3004
2850
  }
3005
2851
 
3006
2852
  // src/daemon/dispatch.ts
3007
- import { readFileSync as readFileSync5, promises as fsp2 } from "fs";
2853
+ import { readFileSync as readFileSync4, promises as fsp } from "fs";
3008
2854
  import * as path from "path";
3009
-
3010
- // src/daemon/skill-sync.ts
3011
- import { promises as fsp } from "fs";
3012
- import { join as join6 } from "path";
3013
- import { createHash as createHash2 } from "crypto";
3014
-
3015
- // src/adapters/openclaw/index.ts
3016
- import { z as z3 } from "zod";
3017
- import { homedir as homedir3 } from "os";
3018
- import { join as join5 } from "path";
3019
- var OpenClawProfileConfigSchema = z3.object({
3020
- /** OpenClaw chat-completions HTTP port. */
3021
- port: z3.number().int().min(1).max(65535).default(18789),
3022
- /** Bearer token expected by the gateway (from gateway.auth.bearerTokens). */
3023
- apiKey: z3.string().min(1),
3024
- /**
3025
- * Model field passed to OpenClaw's /v1/chat/completions. OpenClaw
3026
- * intercepts this differently from a vanilla OpenAI server: the value
3027
- * MUST be `openclaw` (or `openclaw/<agentId>`) — it picks which
3028
- * OpenClaw agent answers, NOT the underlying LLM. The LLM is selected
3029
- * by `agents.defaults.model.primary` in openclaw.json (e.g.
3030
- * `prismer/us-kimi-k2.5`, set by the bootstrap).
3031
- */
3032
- model: z3.string().min(1).default("openclaw"),
3033
- mcpAllowlist: z3.array(z3.string()).nullable().optional(),
3034
- approvalPolicy: z3.enum(["strict", "auto-low-risk", "autonomous"]).optional().default("auto-low-risk"),
3035
- roleTemplate: z3.object({
3036
- mcpServers: z3.array(z3.object({ toolsAllowlist: z3.array(z3.string()).optional() }).passthrough()).optional()
3037
- }).passthrough().optional(),
3038
- skillsDir: z3.string().optional()
3039
- });
3040
- var openclawAdapter = {
3041
- name: "openclaw",
3042
- kind: "long-running",
3043
- capabilities: ["chat", "code", "multi-channel"],
3044
- workspaceSchema: OpenClawProfileConfigSchema,
3045
- validate(config) {
3046
- const r = OpenClawProfileConfigSchema.safeParse(config);
3047
- if (r.success) return { ok: true };
3048
- return {
3049
- ok: false,
3050
- errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
3051
- };
3052
- },
3053
- async ensureService(profile) {
3054
- const config = OpenClawProfileConfigSchema.parse(profile.config);
3055
- const baseUrl = `http://127.0.0.1:${config.port}`;
3056
- if (!await checkHealth2(baseUrl, config.apiKey)) {
3057
- throw new Error(
3058
- `OpenClaw gateway not reachable at ${baseUrl}. Start it with:
3059
- openclaw gateway --port ${config.port}`
3060
- );
3061
- }
3062
- return new OpenClawService(baseUrl, config.apiKey, config.model, resolveOpenClawSkillsDir(profile, config));
3063
- },
3064
- async health() {
3065
- return { available: true };
3066
- }
3067
- };
3068
- var OpenClawService = class {
3069
- constructor(baseUrl, apiKey, model, skillsDir) {
3070
- this.baseUrl = baseUrl;
3071
- this.apiKey = apiKey;
3072
- this.model = model;
3073
- this.skillsDir = skillsDir;
3074
- this.id = baseUrl;
3075
- }
3076
- baseUrl;
3077
- apiKey;
3078
- model;
3079
- skillsDir;
3080
- id;
3081
- async healthy() {
3082
- return checkHealth2(this.baseUrl, this.apiKey);
3083
- }
3084
- async dispatch(task) {
3085
- const startedAt = Date.now();
3086
- const systemPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
3087
- const messages = [];
3088
- if (systemPrompt) messages.push({ role: "system", content: systemPrompt });
3089
- messages.push({ role: "user", content: task.prompt });
3090
- try {
3091
- const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
3092
- method: "POST",
3093
- headers: {
3094
- Authorization: `Bearer ${this.apiKey}`,
3095
- "Content-Type": "application/json"
3096
- },
3097
- body: JSON.stringify({
3098
- model: this.model,
3099
- messages,
3100
- stream: false
3101
- }),
3102
- signal: task.signal
3103
- });
3104
- if (!res.ok) {
3105
- const body = await res.text().catch(() => "") ?? "";
3106
- return {
3107
- ok: false,
3108
- error: {
3109
- code: res.status === 401 || res.status === 403 ? "auth_invalid" : "adapter_dispatch_failed",
3110
- message: `OpenClaw ${res.status}: ${body.slice(0, 400)}`
3111
- }
3112
- };
3113
- }
3114
- const json = await res.json();
3115
- let output = json.choices?.[0]?.message?.content ?? "";
3116
- const ERR_PREFIX_RE = /^Cannot read properties of undefined \(reading '[^']+'\)\s*\n+/;
3117
- if (ERR_PREFIX_RE.test(output)) {
3118
- process.stderr.write(
3119
- `[openclaw-adapter] stripping known stderr-bleed prefix from chat output (OpenClaw 2026.4.x bug)
3120
- `
3121
- );
3122
- output = output.replace(ERR_PREFIX_RE, "");
3123
- }
3124
- return {
3125
- ok: true,
3126
- output,
3127
- metrics: { durationMs: Date.now() - startedAt }
3128
- };
3129
- } catch (err) {
3130
- if (task.signal?.aborted || err?.name === "AbortError") {
3131
- return { ok: false, error: { code: "task_cancelled", message: "Task cancelled by client" } };
3132
- }
3133
- return {
3134
- ok: false,
3135
- error: { code: "adapter_dispatch_failed", message: err.message }
3136
- };
3137
- }
3138
- }
3139
- };
3140
- function getOpenClawSkillsDir(profile) {
3141
- const config = OpenClawProfileConfigSchema.parse(profile.config);
3142
- return resolveOpenClawSkillsDir(profile, config);
3143
- }
3144
- function resolveOpenClawSkillsDir(profile, config) {
3145
- if (config.skillsDir) return config.skillsDir;
3146
- const name = profile.agentUsername || profile.id.slice(0, 8);
3147
- return join5(process.env.OPENCLAW_HOME || join5(homedir3(), ".openclaw"), "profiles", name, "skills");
3148
- }
3149
- async function checkHealth2(baseUrl, apiKey) {
3150
- try {
3151
- const res = await fetch(`${baseUrl}/health`, {
3152
- headers: { Authorization: `Bearer ${apiKey}` },
3153
- signal: AbortSignal.timeout(2e3)
3154
- });
3155
- return res.ok;
3156
- } catch {
3157
- return false;
3158
- }
3159
- }
3160
-
3161
- // src/daemon/skill-sync.ts
3162
- async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
3163
- if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
3164
- const data = await cloud.get(
3165
- `/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
3166
- { signal }
3167
- );
3168
- const entries = normalizeInstalledSkills(data);
3169
- const skillsRoot = resolveSkillsRoot(profile);
3170
- if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
3171
- let synced = 0;
3172
- let skipped = 0;
3173
- let unchanged = 0;
3174
- for (const entry of entries) {
3175
- const slug = sanitizeSlug(trimmedStringFrom(entry.skill?.slug) ?? trimmedStringFrom(entry.slug));
3176
- const content = contentStringFrom(entry.skill?.content) ?? contentStringFrom(entry.content);
3177
- if (!slug || !content) {
3178
- skipped++;
3179
- if (slug && !content) {
3180
- process.stderr.write(`[daemon] skill sync skipped ${slug}: missing content
3181
- `);
3182
- }
3183
- continue;
3184
- }
3185
- const skillDir = join6(skillsRoot, slug);
3186
- await fsp.mkdir(skillDir, { recursive: true });
3187
- const target = join6(skillDir, "SKILL.md");
3188
- const existing = await fsp.readFile(target, "utf8").catch(() => null);
3189
- if (existing != null && sha2562(existing) === sha2562(content)) {
3190
- unchanged++;
3191
- continue;
3192
- }
3193
- await fsp.writeFile(target, content, "utf8");
3194
- synced++;
3195
- }
3196
- return { synced, skipped, unchanged };
3197
- }
3198
- function resolveSkillsRoot(profile) {
3199
- if (profile.adapterName === "hermes") return join6(getHermesProfileDir(getHermesProfileName(profile)), "skills");
3200
- if (profile.adapterName === "openclaw") return getOpenClawSkillsDir(profile);
3201
- return null;
3202
- }
3203
- function sha2562(value) {
3204
- return createHash2("sha256").update(value).digest("hex");
3205
- }
3206
- function normalizeInstalledSkills(data) {
3207
- if (Array.isArray(data)) return data.filter(isInstalledSkillEntry);
3208
- if (!data || typeof data !== "object" || Array.isArray(data)) return [];
3209
- const record = data;
3210
- const nested = record.data;
3211
- if (Array.isArray(nested)) return nested.filter(isInstalledSkillEntry);
3212
- if (nested && typeof nested === "object" && !Array.isArray(nested)) {
3213
- const skills2 = nested.skills;
3214
- if (Array.isArray(skills2)) return skills2.filter(isInstalledSkillEntry);
3215
- }
3216
- const skills = record.skills;
3217
- if (Array.isArray(skills)) return skills.filter(isInstalledSkillEntry);
3218
- return [];
3219
- }
3220
- function isInstalledSkillEntry(value) {
3221
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
3222
- }
3223
- function trimmedStringFrom(value) {
3224
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
3225
- }
3226
- function contentStringFrom(value) {
3227
- return typeof value === "string" && value.trim() ? value : void 0;
3228
- }
3229
- function sanitizeSlug(value) {
3230
- if (!value) return void 0;
3231
- const safe = value.replace(/[^a-zA-Z0-9_-]/g, "");
3232
- return safe || void 0;
3233
- }
3234
-
3235
- // src/daemon/dispatch.ts
3236
2855
  var DEFAULT_CONTEXT_MAX = 8e3;
3237
2856
  var GOAL_CONTEXT_MAX = 4;
3238
2857
  var MEMORY_DIGEST_MAX_BYTES = 4e3;
3239
- var DEFAULT_OPERATING_PRINCIPLES = [
3240
- "- Decide and act; only request human approval for destructive operations or irreversible external effects.",
3241
- "- Assignable work should become explicit tasks; do not leave concrete work as chat discussion only.",
3242
- "- Use workspace asset tools for uploaded files: search/describe first, then read bounded ranges; do not claim file contents unless a tool call succeeded.",
3243
- "- Keep users informed with concise status and concrete next steps."
3244
- ].join("\n");
3245
2858
  var ASSET_INLINE_FULL_MAX_BYTES = 64 * 1024;
3246
2859
  var ASSET_INLINE_TRUNCATE_BYTES = 16 * 1024;
3247
2860
  async function handleDispatch(payload, requestId, deps) {
@@ -3252,7 +2865,7 @@ async function handleDispatch(payload, requestId, deps) {
3252
2865
  const outboxDir = outboxBase ? path.join(outboxBase, "_outbox") : null;
3253
2866
  if (outboxDir) {
3254
2867
  try {
3255
- await fsp2.mkdir(outboxDir, { recursive: true });
2868
+ await fsp.mkdir(outboxDir, { recursive: true });
3256
2869
  } catch (err) {
3257
2870
  process.stderr.write(
3258
2871
  `[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
@@ -3279,18 +2892,6 @@ async function handleDispatch(payload, requestId, deps) {
3279
2892
  sendReply(deps.ws, reply, requestId);
3280
2893
  return reply;
3281
2894
  }
3282
- try {
3283
- const skillSync = await syncInstalledSkillsForDispatch(profile, agentImUserId, deps.cloud, deps.signal);
3284
- if (skillSync.synced > 0 || skillSync.skipped > 0) {
3285
- process.stderr.write(
3286
- `[daemon] skill sync profile=${profile.id} adapter=${profile.adapterName} synced=${skillSync.synced} skipped=${skillSync.skipped}
3287
- `
3288
- );
3289
- }
3290
- } catch (err) {
3291
- process.stderr.write(`[daemon] skill sync skipped profile=${profile.id}: ${err.message}
3292
- `);
3293
- }
3294
2895
  let hashRefResult = { text: payload.prompt, resolutions: [] };
3295
2896
  if (deps.assetMetadataIndexes && profile.workspaceId) {
3296
2897
  const assetIndex = deps.assetMetadataIndexes.get(profile.workspaceId);
@@ -3340,7 +2941,6 @@ async function handleDispatch(payload, requestId, deps) {
3340
2941
  `
3341
2942
  );
3342
2943
  }
3343
- const operatingPrinciples = resolveOperatingPrinciples(profile.config);
3344
2944
  const taskInput = {
3345
2945
  taskId,
3346
2946
  prompt: adapterPrompt,
@@ -3349,7 +2949,6 @@ async function handleDispatch(payload, requestId, deps) {
3349
2949
  conversationId: payload.conversationId,
3350
2950
  prismerGoals: goalContext.map(toGoalMirrorPayload),
3351
2951
  ...profileSystemPrompt ? { systemPrompt: profileSystemPrompt } : {},
3352
- ...operatingPrinciples ? { operatingPrinciples } : {},
3353
2952
  // Wave-9: spawn-style adapters (claude-code, codex, openclaw) read
3354
2953
  // this and inject `PRISMER_OUTBOX_DIR=<abs>` into the child env so
3355
2954
  // the LLM tool can resolve the path even if it doesn't read the
@@ -3620,7 +3219,7 @@ async function resolveAssetRefs(refs, cache) {
3620
3219
  const inlineCandidate = isTextLikeMime(effectiveMime);
3621
3220
  if (inlineCandidate) {
3622
3221
  try {
3623
- const buf = readFileSync5(cached.localPath);
3222
+ const buf = readFileSync4(cached.localPath);
3624
3223
  let strategy;
3625
3224
  let body;
3626
3225
  if (buf.byteLength <= ASSET_INLINE_FULL_MAX_BYTES) {
@@ -3694,36 +3293,6 @@ ${memory.digest.trim()}
3694
3293
 
3695
3294
  ${prompt}`;
3696
3295
  }
3697
- function resolveOperatingPrinciples(config) {
3698
- const cfg = readRecord(config);
3699
- const roleTemplate = readRecord(cfg.roleTemplate);
3700
- const principles = stringifyPrinciples(cfg.operatingPrinciples) ?? stringifyPrinciples(roleTemplate.operatingPrinciples) ?? DEFAULT_OPERATING_PRINCIPLES;
3701
- const policy = typeof cfg.approvalPolicy === "string" ? cfg.approvalPolicy : roleTemplate.approvalPolicy;
3702
- if (policy === "strict") {
3703
- return `${principles}
3704
- - Approval policy: strict. Request human approval before destructive, external, spend, access, or publish operations.`;
3705
- }
3706
- if (policy === "autonomous") {
3707
- return `${principles}
3708
- - Approval policy: autonomous. Do not request human approval unless a platform policy explicitly requires it.`;
3709
- }
3710
- return `${principles}
3711
- - Approval policy: auto-low-risk. Proceed on reversible low-risk work; request human approval for destructive or irreversible operations.`;
3712
- }
3713
- function stringifyPrinciples(value) {
3714
- if (typeof value === "string" && value.trim()) return value.trim();
3715
- if (value && typeof value === "object" && !Array.isArray(value)) {
3716
- const record = value;
3717
- for (const key of ["en", "zh"]) {
3718
- const text = record[key];
3719
- if (typeof text === "string" && text.trim()) return text.trim();
3720
- }
3721
- for (const text of Object.values(record)) {
3722
- if (typeof text === "string" && text.trim()) return text.trim();
3723
- }
3724
- }
3725
- return void 0;
3726
- }
3727
3296
  function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
3728
3297
  if (!outboxDir) return prompt;
3729
3298
  if (profileConfig?.disableOutboxHints === true) return prompt;
@@ -3742,9 +3311,6 @@ function appendChannelContext(prompt, payload, profile) {
3742
3311
  const type = payload.conversationType === "direct" || payload.conversationType === "group" ? payload.conversationType : "unknown";
3743
3312
  const youAre = profile.agentUsername || payload.agentImUserId || "this agent";
3744
3313
  const lines = ["[Channel context]", `Conversation type: ${type}`, `You are: ${youAre}`];
3745
- if (payload.conversationId) {
3746
- lines.push(`Conversation ID: ${payload.conversationId}`);
3747
- }
3748
3314
  const participants = Array.isArray(payload.participants) ? payload.participants : [];
3749
3315
  if (participants.length > 0) {
3750
3316
  if (type === "direct") {
@@ -4014,7 +3580,7 @@ var ServicePool = class {
4014
3580
 
4015
3581
  // src/daemon/local-server.ts
4016
3582
  import { createServer } from "http";
4017
- import { randomUUID, createHash as createHash3 } from "crypto";
3583
+ import { randomUUID, createHash } from "crypto";
4018
3584
  import { promises as fs } from "fs";
4019
3585
  import * as path2 from "path";
4020
3586
  var LocalServer = class {
@@ -4322,10 +3888,10 @@ async function walkAndDigest(root, current) {
4322
3888
  }
4323
3889
  if (!st.isFile()) continue;
4324
3890
  const buf = await fs.readFile(full);
4325
- const sha2564 = createHash3("sha256").update(buf).digest("hex");
3891
+ const sha2562 = createHash("sha256").update(buf).digest("hex");
4326
3892
  out.push({
4327
3893
  path: rel,
4328
- sha256: sha2564,
3894
+ sha256: sha2562,
4329
3895
  sizeBytes: st.size,
4330
3896
  mtime: Math.floor(st.mtimeMs)
4331
3897
  });
@@ -4335,14 +3901,12 @@ async function walkAndDigest(root, current) {
4335
3901
 
4336
3902
  // src/daemon/runner.ts
4337
3903
  import { EventEmitter as EventEmitter3 } from "events";
4338
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
4339
- import { mkdir } from "fs/promises";
4340
- import { homedir as homedir6, platform } from "os";
4341
- import { join as join13 } from "path";
3904
+ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
3905
+ import { platform } from "os";
4342
3906
 
4343
3907
  // src/adapters/claude-code/index.ts
4344
3908
  import { spawn as spawn2, spawnSync } from "child_process";
4345
- import { z as z4 } from "zod";
3909
+ import { z as z3 } from "zod";
4346
3910
 
4347
3911
  // src/adapters/claude-code/output-parser.ts
4348
3912
  var MAX_OUTPUT_CHARS = 64 * 1024;
@@ -4374,28 +3938,28 @@ function parseHeadlessOutput(stdout) {
4374
3938
  }
4375
3939
 
4376
3940
  // src/adapters/claude-code/index.ts
4377
- var McpServerSchema = z4.object({
4378
- name: z4.string(),
4379
- command: z4.string(),
4380
- args: z4.array(z4.string()).optional(),
4381
- env: z4.record(z4.string(), z4.string()).optional()
3941
+ var McpServerSchema = z3.object({
3942
+ name: z3.string(),
3943
+ command: z3.string(),
3944
+ args: z3.array(z3.string()).optional(),
3945
+ env: z3.record(z3.string(), z3.string()).optional()
4382
3946
  });
4383
- var CCConfigSchema = z4.object({
4384
- cwd: z4.string().min(1),
3947
+ var CCConfigSchema = z3.object({
3948
+ cwd: z3.string().min(1),
4385
3949
  // claude 2.x accepts aliases ('sonnet', 'haiku', 'opus') and full ids
4386
3950
  // (e.g. 'claude-sonnet-4-6'). The 1.x default 'claude-3-5-sonnet' is no
4387
3951
  // longer a valid model id under the current CLI.
4388
- model: z4.string().default("sonnet"),
4389
- systemPrompt: z4.string().optional(),
4390
- envVars: z4.record(z4.string(), z4.string()).optional(),
4391
- mcpServers: z4.array(McpServerSchema).optional(),
4392
- allowedTools: z4.array(z4.string()).optional(),
4393
- maxTurns: z4.number().int().positive().default(20),
3952
+ model: z3.string().default("sonnet"),
3953
+ systemPrompt: z3.string().optional(),
3954
+ envVars: z3.record(z3.string(), z3.string()).optional(),
3955
+ mcpServers: z3.array(McpServerSchema).optional(),
3956
+ allowedTools: z3.array(z3.string()).optional(),
3957
+ maxTurns: z3.number().int().positive().default(20),
4394
3958
  // External-model route (1.9.x extension — optional, no breaking change)
4395
- baseURL: z4.string().url().optional(),
4396
- apiKeyRef: z4.string().regex(/^(env|keychain):[A-Za-z0-9_][A-Za-z0-9_.\-]*$/).optional(),
3959
+ baseURL: z3.string().url().optional(),
3960
+ apiKeyRef: z3.string().regex(/^(env|keychain):[A-Za-z0-9_][A-Za-z0-9_.\-]*$/).optional(),
4397
3961
  // informational tag — dispatch does not branch on this value yet
4398
- route: z4.enum(["default", "prismer", "omniroute"]).default("default")
3962
+ route: z3.enum(["default", "prismer", "omniroute"]).default("default")
4399
3963
  });
4400
3964
  var claudeCodeAdapter = {
4401
3965
  name: "claude-code",
@@ -4549,7 +4113,7 @@ function resolveKeyRef(ref) {
4549
4113
 
4550
4114
  // src/adapters/codex/index.ts
4551
4115
  import { spawn as spawn3 } from "child_process";
4552
- import { z as z5 } from "zod";
4116
+ import { z as z4 } from "zod";
4553
4117
 
4554
4118
  // src/skills/memory-curation.ts
4555
4119
  var MEMORY_CURATION_SKILL_TEXT = `---
@@ -4682,31 +4246,31 @@ If the answer is no, do not save.
4682
4246
  `;
4683
4247
 
4684
4248
  // src/adapters/codex/index.ts
4685
- var CodexConfigSchema = z5.object({
4249
+ var CodexConfigSchema = z4.object({
4686
4250
  /** Working directory for the codex subprocess. */
4687
- cwd: z5.string().min(1),
4251
+ cwd: z4.string().min(1),
4688
4252
  /**
4689
4253
  * Model identifier passed to --model (e.g. 'codex-mini-latest', 'o4-mini').
4690
4254
  * Default matches the Codex CLI built-in default as of 2026-05.
4691
4255
  */
4692
- model: z5.string().default("codex-mini-latest"),
4256
+ model: z4.string().default("codex-mini-latest"),
4693
4257
  /**
4694
4258
  * Sandbox level for the codex subprocess.
4695
4259
  * read-only — analysis only; code-gen tasks will stall
4696
4260
  * workspace-write — write inside cwd, read-only outside (recommended)
4697
4261
  * danger-full-access — no restrictions; safe only inside isolated containers
4698
4262
  */
4699
- sandbox: z5.enum(["read-only", "workspace-write", "danger-full-access"]).default("workspace-write"),
4263
+ sandbox: z4.enum(["read-only", "workspace-write", "danger-full-access"]).default("workspace-write"),
4700
4264
  /** Optional system prompt prepended to the user prompt as a preamble (see U4). */
4701
- systemPrompt: z5.string().optional(),
4265
+ systemPrompt: z4.string().optional(),
4702
4266
  /** Extra env vars merged into the codex subprocess environment. */
4703
- envVars: z5.record(z5.string(), z5.string()).optional(),
4267
+ envVars: z4.record(z4.string(), z4.string()).optional(),
4704
4268
  /**
4705
4269
  * Name of the env var holding the OpenAI API key. Defaults to
4706
4270
  * 'OPENAI_API_KEY' (Codex CLI default). Override if using a workspace-
4707
4271
  * scoped key under a different name.
4708
4272
  */
4709
- apiKeyEnv: z5.string().default("OPENAI_API_KEY")
4273
+ apiKeyEnv: z4.string().default("OPENAI_API_KEY")
4710
4274
  });
4711
4275
  function buildCodexPrompt(config, taskPrompt) {
4712
4276
  const preambleParts = [MEMORY_CURATION_SKILL_TEXT.trim()];
@@ -4854,33 +4418,160 @@ var codexAdapter = {
4854
4418
  }
4855
4419
  };
4856
4420
 
4857
- // src/daemon/memory/runtime.ts
4858
- import * as os from "os";
4859
- import * as path4 from "path";
4860
-
4861
- // src/daemon/memory/store.ts
4862
- import Database3 from "better-sqlite3";
4863
- import * as fs2 from "fs";
4864
- import * as path3 from "path";
4865
- import { randomUUID as randomUUID2, createHash as createHash4 } from "crypto";
4866
-
4867
- // src/daemon/memory/crypto.ts
4868
- function sealPlaintext(content) {
4869
- return { kind: "inline", content };
4870
- }
4871
-
4872
- // src/daemon/memory/store.ts
4873
- var SCHEMA_VERSION2 = 1;
4874
- var SCHEMA_V1_STATEMENTS = [
4875
- `CREATE TABLE IF NOT EXISTS memory_schema_version (
4876
- version INTEGER PRIMARY KEY
4877
- )`,
4878
- `CREATE TABLE IF NOT EXISTS memory_pages (
4879
- id TEXT PRIMARY KEY,
4880
- workspaceId TEXT NOT NULL,
4881
- path TEXT NOT NULL,
4882
- title TEXT,
4883
- description TEXT,
4421
+ // src/adapters/openclaw/index.ts
4422
+ import { z as z5 } from "zod";
4423
+ var OpenClawProfileConfigSchema = z5.object({
4424
+ /** OpenClaw chat-completions HTTP port. */
4425
+ port: z5.number().int().min(1).max(65535).default(18789),
4426
+ /** Bearer token expected by the gateway (from gateway.auth.bearerTokens). */
4427
+ apiKey: z5.string().min(1),
4428
+ /**
4429
+ * Model field passed to OpenClaw's /v1/chat/completions. OpenClaw
4430
+ * intercepts this differently from a vanilla OpenAI server: the value
4431
+ * MUST be `openclaw` (or `openclaw/<agentId>`) — it picks which
4432
+ * OpenClaw agent answers, NOT the underlying LLM. The LLM is selected
4433
+ * by `agents.defaults.model.primary` in openclaw.json (e.g.
4434
+ * `prismer/us-kimi-k2.5`, set by the bootstrap).
4435
+ */
4436
+ model: z5.string().min(1).default("openclaw")
4437
+ });
4438
+ var openclawAdapter = {
4439
+ name: "openclaw",
4440
+ kind: "long-running",
4441
+ capabilities: ["chat", "code", "multi-channel"],
4442
+ workspaceSchema: OpenClawProfileConfigSchema,
4443
+ validate(config) {
4444
+ const r = OpenClawProfileConfigSchema.safeParse(config);
4445
+ if (r.success) return { ok: true };
4446
+ return {
4447
+ ok: false,
4448
+ errors: r.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
4449
+ };
4450
+ },
4451
+ async ensureService(profile) {
4452
+ const config = OpenClawProfileConfigSchema.parse(profile.config);
4453
+ const baseUrl = `http://127.0.0.1:${config.port}`;
4454
+ if (!await checkHealth2(baseUrl, config.apiKey)) {
4455
+ throw new Error(
4456
+ `OpenClaw gateway not reachable at ${baseUrl}. Start it with:
4457
+ openclaw gateway --port ${config.port}`
4458
+ );
4459
+ }
4460
+ return new OpenClawService(baseUrl, config.apiKey, config.model);
4461
+ },
4462
+ async health() {
4463
+ return { available: true };
4464
+ }
4465
+ };
4466
+ var OpenClawService = class {
4467
+ constructor(baseUrl, apiKey, model) {
4468
+ this.baseUrl = baseUrl;
4469
+ this.apiKey = apiKey;
4470
+ this.model = model;
4471
+ this.id = baseUrl;
4472
+ }
4473
+ baseUrl;
4474
+ apiKey;
4475
+ model;
4476
+ id;
4477
+ async healthy() {
4478
+ return checkHealth2(this.baseUrl, this.apiKey);
4479
+ }
4480
+ async dispatch(task) {
4481
+ const startedAt = Date.now();
4482
+ const systemPrompt = typeof task.metadata?.systemPrompt === "string" ? task.metadata.systemPrompt : void 0;
4483
+ const messages = [];
4484
+ if (systemPrompt) messages.push({ role: "system", content: systemPrompt });
4485
+ messages.push({ role: "user", content: task.prompt });
4486
+ try {
4487
+ const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
4488
+ method: "POST",
4489
+ headers: {
4490
+ Authorization: `Bearer ${this.apiKey}`,
4491
+ "Content-Type": "application/json"
4492
+ },
4493
+ body: JSON.stringify({
4494
+ model: this.model,
4495
+ messages,
4496
+ stream: false
4497
+ }),
4498
+ signal: task.signal
4499
+ });
4500
+ if (!res.ok) {
4501
+ const body = await res.text().catch(() => "") ?? "";
4502
+ return {
4503
+ ok: false,
4504
+ error: {
4505
+ code: res.status === 401 || res.status === 403 ? "auth_invalid" : "adapter_dispatch_failed",
4506
+ message: `OpenClaw ${res.status}: ${body.slice(0, 400)}`
4507
+ }
4508
+ };
4509
+ }
4510
+ const json = await res.json();
4511
+ let output = json.choices?.[0]?.message?.content ?? "";
4512
+ const ERR_PREFIX_RE = /^Cannot read properties of undefined \(reading '[^']+'\)\s*\n+/;
4513
+ if (ERR_PREFIX_RE.test(output)) {
4514
+ process.stderr.write(
4515
+ `[openclaw-adapter] stripping known stderr-bleed prefix from chat output (OpenClaw 2026.4.x bug)
4516
+ `
4517
+ );
4518
+ output = output.replace(ERR_PREFIX_RE, "");
4519
+ }
4520
+ return {
4521
+ ok: true,
4522
+ output,
4523
+ metrics: { durationMs: Date.now() - startedAt }
4524
+ };
4525
+ } catch (err) {
4526
+ if (task.signal?.aborted || err?.name === "AbortError") {
4527
+ return { ok: false, error: { code: "task_cancelled", message: "Task cancelled by client" } };
4528
+ }
4529
+ return {
4530
+ ok: false,
4531
+ error: { code: "adapter_dispatch_failed", message: err.message }
4532
+ };
4533
+ }
4534
+ }
4535
+ };
4536
+ async function checkHealth2(baseUrl, apiKey) {
4537
+ try {
4538
+ const res = await fetch(`${baseUrl}/health`, {
4539
+ headers: { Authorization: `Bearer ${apiKey}` },
4540
+ signal: AbortSignal.timeout(2e3)
4541
+ });
4542
+ return res.ok;
4543
+ } catch {
4544
+ return false;
4545
+ }
4546
+ }
4547
+
4548
+ // src/daemon/memory/runtime.ts
4549
+ import * as os from "os";
4550
+ import * as path4 from "path";
4551
+
4552
+ // src/daemon/memory/store.ts
4553
+ import Database3 from "better-sqlite3";
4554
+ import * as fs2 from "fs";
4555
+ import * as path3 from "path";
4556
+ import { randomUUID as randomUUID2, createHash as createHash2 } from "crypto";
4557
+
4558
+ // src/daemon/memory/crypto.ts
4559
+ function sealPlaintext(content) {
4560
+ return { kind: "inline", content };
4561
+ }
4562
+
4563
+ // src/daemon/memory/store.ts
4564
+ var SCHEMA_VERSION2 = 1;
4565
+ var SCHEMA_V1_STATEMENTS = [
4566
+ `CREATE TABLE IF NOT EXISTS memory_schema_version (
4567
+ version INTEGER PRIMARY KEY
4568
+ )`,
4569
+ `CREATE TABLE IF NOT EXISTS memory_pages (
4570
+ id TEXT PRIMARY KEY,
4571
+ workspaceId TEXT NOT NULL,
4572
+ path TEXT NOT NULL,
4573
+ title TEXT,
4574
+ description TEXT,
4884
4575
  contentHash TEXT NOT NULL,
4885
4576
  version INTEGER NOT NULL DEFAULT 1,
4886
4577
  pageType TEXT NOT NULL DEFAULT 'leaf',
@@ -5013,9 +4704,9 @@ var MemoryStore = class {
5013
4704
  const row = db.prepare("SELECT * FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId);
5014
4705
  return row ? this.rowToPage(row) : null;
5015
4706
  }
5016
- loadContent(pageId, version) {
4707
+ loadContent(pageId, version2) {
5017
4708
  const db = this.requireDb();
5018
- const targetVersion = version ?? db.prepare("SELECT version FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId)?.version;
4709
+ const targetVersion = version2 ?? db.prepare("SELECT version FROM memory_pages WHERE workspaceId = ? AND id = ?").get(this.opts.workspaceId, pageId)?.version;
5019
4710
  if (targetVersion === void 0) return null;
5020
4711
  const row = db.prepare(
5021
4712
  `SELECT c.pageId, c.version, c.payloadKind, c.payloadValue
@@ -5053,7 +4744,7 @@ var MemoryStore = class {
5053
4744
  }
5054
4745
  const db = this.requireDb();
5055
4746
  const now = Date.now();
5056
- const contentHash = sha2563(input.content);
4747
+ const contentHash = sha256(input.content);
5057
4748
  const payload = sealPlaintext(input.content);
5058
4749
  if (payload.kind !== "inline") {
5059
4750
  throw new Error("MemoryStore.write: non-inline payload not yet supported in phase-0");
@@ -5247,8 +4938,8 @@ var MemoryStore = class {
5247
4938
  };
5248
4939
  }
5249
4940
  };
5250
- function sha2563(s) {
5251
- return createHash4("sha256").update(s, "utf8").digest("hex");
4941
+ function sha256(s) {
4942
+ return createHash2("sha256").update(s, "utf8").digest("hex");
5252
4943
  }
5253
4944
 
5254
4945
  // src/daemon/memory/search.ts
@@ -6106,8 +5797,8 @@ function finalizeSelected(runtime, workspaceId, paths, snippetMaxBytes = DEFAULT
6106
5797
  const slot = runtime.peek(workspaceId);
6107
5798
  if (!slot) return [];
6108
5799
  const out = [];
6109
- for (const path9 of paths) {
6110
- const page = slot.store.loadByPath(path9);
5800
+ for (const path7 of paths) {
5801
+ const page = slot.store.loadByPath(path7);
6111
5802
  if (!page) continue;
6112
5803
  const content = slot.store.loadContent(page.id)?.content ?? "";
6113
5804
  out.push({
@@ -6382,14 +6073,14 @@ function parsePrismerUri(uri) {
6382
6073
  const memoryIdx = rest.indexOf(segMemory);
6383
6074
  if (memoryIdx <= 0) return null;
6384
6075
  const workspaceId = rest.slice(0, memoryIdx);
6385
- const path9 = rest.slice(memoryIdx + segMemory.length);
6386
- if (!workspaceId || !path9) return null;
6387
- return { workspaceId, path: path9 };
6076
+ const path7 = rest.slice(memoryIdx + segMemory.length);
6077
+ if (!workspaceId || !path7) return null;
6078
+ return { workspaceId, path: path7 };
6388
6079
  }
6389
6080
 
6390
6081
  // src/daemon/asset/metadata-index.ts
6391
- import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
6392
- import { join as join10 } from "path";
6082
+ import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
6083
+ import { join as join8 } from "path";
6393
6084
  var DEFAULT_LIMIT = 8;
6394
6085
  var PULL_PAGE_SIZE = 500;
6395
6086
  var THROTTLE_MS = 3e4;
@@ -6420,13 +6111,13 @@ var AssetMetadataIndex = class {
6420
6111
  if (!existsSync7(opts.workspaceStateDir)) {
6421
6112
  mkdirSync7(opts.workspaceStateDir, { recursive: true });
6422
6113
  }
6423
- this.cursorPath = join10(opts.workspaceStateDir, "asset-metadata-cursor.json");
6114
+ this.cursorPath = join8(opts.workspaceStateDir, "asset-metadata-cursor.json");
6424
6115
  }
6425
6116
  /** Persisted cursor for this workspace, or 0 on first run / corrupted file. */
6426
6117
  readCursor() {
6427
6118
  if (!existsSync7(this.cursorPath)) return 0;
6428
6119
  try {
6429
- const parsed = JSON.parse(readFileSync6(this.cursorPath, "utf8"));
6120
+ const parsed = JSON.parse(readFileSync5(this.cursorPath, "utf8"));
6430
6121
  if (parsed.workspaceId !== this.workspaceId) return 0;
6431
6122
  return parsed.cursor;
6432
6123
  } catch {
@@ -6479,13 +6170,9 @@ var AssetMetadataIndex = class {
6479
6170
  let applied = 0;
6480
6171
  const tx = this.db.transaction((items) => {
6481
6172
  for (const item of items) {
6482
- const assetId = item.assetId ?? item.id;
6483
- if (!assetId) {
6484
- throw new Error("Asset metadata index item missing assetId/id");
6485
- }
6486
6173
  upsert.run(
6487
6174
  this.workspaceId,
6488
- assetId,
6175
+ item.assetId,
6489
6176
  item.contentHash,
6490
6177
  item.filename ?? null,
6491
6178
  item.folderPath ?? null,
@@ -6530,14 +6217,6 @@ var AssetMetadataIndex = class {
6530
6217
  const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND filename = ?").get(this.workspaceId, filename);
6531
6218
  return row ? rowToMetadata(row) : void 0;
6532
6219
  }
6533
- resolveByAssetId(assetId) {
6534
- const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND asset_id = ?").get(this.workspaceId, assetId);
6535
- return row ? rowToMetadata(row) : void 0;
6536
- }
6537
- resolveByContentHash(contentHash) {
6538
- const row = this.db.prepare("SELECT * FROM asset_metadata_index WHERE workspace_id = ? AND content_hash = ?").get(this.workspaceId, contentHash);
6539
- return row ? rowToMetadata(row) : void 0;
6540
- }
6541
6220
  /** Batch exact match — returns Map for O(1) access. */
6542
6221
  resolveByFilenames(filenames) {
6543
6222
  if (filenames.length === 0) return /* @__PURE__ */ new Map();
@@ -6556,10 +6235,7 @@ var AssetMetadataIndex = class {
6556
6235
  };
6557
6236
 
6558
6237
  // src/daemon/asset/rpc.ts
6559
- import { closeSync, openSync, readSync, statSync as statSync2 } from "fs";
6560
6238
  var ASSET_PATH_PREFIX = "/local/asset/";
6561
- var MAX_READ_BYTES = 256 * 1024;
6562
- var MAX_SEARCH_BYTES = 1024 * 1024;
6563
6239
  function attachAssetRpc(opts) {
6564
6240
  return async (req, res) => {
6565
6241
  const url = req.url ?? "/";
@@ -6570,17 +6246,10 @@ function attachAssetRpc(opts) {
6570
6246
  try {
6571
6247
  if (method === "POST" && subpath === "search") {
6572
6248
  const body = await readJson3(req);
6573
- return await handleSearch2(opts, body, res);
6249
+ return handleSearch2(opts.resolveIndex, body, res);
6574
6250
  }
6575
- if (method === "POST" && subpath === "describe") {
6576
- const body = await readJson3(req);
6577
- return handleDescribe(opts, body, res);
6578
- }
6579
- if (method === "POST" && subpath === "read") {
6580
- const body = await readJson3(req);
6581
- return await handleRead(opts, body, res);
6582
- }
6583
- return false;
6251
+ respond3(res, 404, { error: "asset_route_not_found", path: url });
6252
+ return true;
6584
6253
  } catch (err) {
6585
6254
  respond3(res, 500, {
6586
6255
  error: "asset_rpc_failed",
@@ -6590,21 +6259,18 @@ function attachAssetRpc(opts) {
6590
6259
  }
6591
6260
  };
6592
6261
  }
6593
- async function handleSearch2(opts, body, res) {
6262
+ function handleSearch2(resolveIndex, body, res) {
6594
6263
  if (!body || typeof body !== "object") {
6595
6264
  return respond4002(res, "request body must be a JSON object");
6596
6265
  }
6597
6266
  const b = body;
6598
- if (typeof b.query !== "string" || !b.query.trim()) {
6599
- return respond4002(res, "query is required (non-empty string)");
6600
- }
6601
- if (typeof b.uri === "string" && b.uri.trim()) {
6602
- return searchAssetContent(opts, b.uri.trim(), b.query.trim(), res);
6603
- }
6604
6267
  if (typeof b.workspaceId !== "string" || !b.workspaceId) {
6605
6268
  return respond4002(res, "workspaceId is required (string)");
6606
6269
  }
6607
- const index = opts.resolveIndex(b.workspaceId);
6270
+ if (typeof b.query !== "string" || !b.query.trim()) {
6271
+ return respond4002(res, "query is required (non-empty string)");
6272
+ }
6273
+ const index = resolveIndex(b.workspaceId);
6608
6274
  if (!index) {
6609
6275
  respond3(res, 404, {
6610
6276
  error: "workspace_index_not_found",
@@ -6618,124 +6284,6 @@ async function handleSearch2(opts, body, res) {
6618
6284
  respond3(res, 200, { items });
6619
6285
  return true;
6620
6286
  }
6621
- function handleDescribe(opts, body, res) {
6622
- const resolved = resolveAssetRef(opts, body);
6623
- if ("error" in resolved) return respond4002(res, resolved.error);
6624
- const cached = opts.assetCache?.get(resolved.row.contentHash);
6625
- respond3(res, 200, {
6626
- asset: resolved.row,
6627
- uri: `prismer://workspace/${resolved.workspaceId}/asset/${resolved.row.contentHash}`,
6628
- cache: cached ? { status: "hit", localPath: cached.localPath, sizeBytes: cached.sizeBytes, lastUsedAt: cached.lastUsedAt } : { status: "miss" },
6629
- tools: ["asset.describe", "asset.read", "asset.search"]
6630
- });
6631
- return true;
6632
- }
6633
- async function handleRead(opts, body, res) {
6634
- const resolved = resolveAssetRef(opts, body);
6635
- if ("error" in resolved) return respond4002(res, resolved.error);
6636
- if (!opts.assetCache) {
6637
- respond3(res, 503, { error: "asset_cache_unavailable" });
6638
- return true;
6639
- }
6640
- const b = body;
6641
- const offset = typeof b.offset === "number" && b.offset > 0 ? Math.floor(b.offset) : 0;
6642
- const requested = typeof b.length === "number" && b.length > 0 ? Math.floor(b.length) : MAX_READ_BYTES;
6643
- const length = Math.min(requested, MAX_READ_BYTES);
6644
- const cached = await opts.assetCache.getOrFetch(resolved.row.contentHash, {
6645
- workspaceIdHint: resolved.workspaceId,
6646
- assetId: resolved.row.assetId
6647
- });
6648
- const chunk = readFileRange(cached.localPath, offset, length);
6649
- const textLike = isTextLike(resolved.row);
6650
- respond3(res, 200, {
6651
- asset: resolved.row,
6652
- locator: { byteOffset: offset, byteLength: chunk.bytesRead },
6653
- truncated: requested > MAX_READ_BYTES || offset + chunk.bytesRead < chunk.sizeBytes,
6654
- encoding: textLike ? "utf8" : "base64",
6655
- content: textLike ? chunk.buffer.toString("utf8") : chunk.buffer.toString("base64")
6656
- });
6657
- return true;
6658
- }
6659
- async function searchAssetContent(opts, uri, query, res) {
6660
- const resolved = resolveAssetRef(opts, { uri });
6661
- if ("error" in resolved) return respond4002(res, resolved.error);
6662
- if (!opts.assetCache) {
6663
- respond3(res, 503, { error: "asset_cache_unavailable" });
6664
- return true;
6665
- }
6666
- if (!isTextLike(resolved.row)) {
6667
- respond3(res, 200, {
6668
- items: [],
6669
- unsupported: true,
6670
- reason: "content search currently supports text-like assets only",
6671
- asset: resolved.row
6672
- });
6673
- return true;
6674
- }
6675
- const cached = await opts.assetCache.getOrFetch(resolved.row.contentHash, {
6676
- workspaceIdHint: resolved.workspaceId,
6677
- assetId: resolved.row.assetId
6678
- });
6679
- const chunk = readFileRange(cached.localPath, 0, MAX_SEARCH_BYTES);
6680
- const text = chunk.buffer.toString("utf8");
6681
- const needle = query.toLowerCase();
6682
- const lines = text.split(/\r?\n/);
6683
- const items = [];
6684
- let byteOffset = 0;
6685
- for (let i = 0; i < lines.length && items.length < 20; i += 1) {
6686
- const line = lines[i] ?? "";
6687
- if (line.toLowerCase().includes(needle)) {
6688
- items.push({ line: i + 1, byteOffset, preview: line.slice(0, 500) });
6689
- }
6690
- byteOffset += Buffer.byteLength(line) + 1;
6691
- }
6692
- respond3(res, 200, {
6693
- items,
6694
- asset: resolved.row,
6695
- searched: { byteOffset: 0, byteLength: chunk.bytesRead, truncated: chunk.bytesRead < chunk.sizeBytes }
6696
- });
6697
- return true;
6698
- }
6699
- function resolveAssetRef(opts, body) {
6700
- if (!body || typeof body !== "object") return { error: "request body must be a JSON object" };
6701
- const b = body;
6702
- let workspaceId = typeof b.workspaceId === "string" ? b.workspaceId : "";
6703
- let assetId = typeof b.assetId === "string" ? b.assetId : "";
6704
- let contentHash = typeof b.contentHash === "string" ? b.contentHash : "";
6705
- if (typeof b.uri === "string" && b.uri.trim()) {
6706
- const [uri] = parseUris(b.uri.trim());
6707
- if (!uri) return { error: "uri must be prismer://workspace/<workspaceId>/asset/<hash>" };
6708
- if (uri.type !== "asset") return { error: "file uri lookup is not available in this RPC yet; pass asset uri or assetId" };
6709
- workspaceId = uri.workspaceId ?? workspaceId;
6710
- contentHash = uri.rest;
6711
- }
6712
- if (!workspaceId) return { error: "workspaceId is required (string)" };
6713
- const index = opts.resolveIndex(workspaceId);
6714
- if (!index) return { error: "workspace asset index is not synced yet" };
6715
- const row = assetId ? index.resolveByAssetId(assetId) : contentHash ? index.resolveByContentHash(contentHash) : void 0;
6716
- if (!row) return { error: "asset not found in local metadata index" };
6717
- return { workspaceId, row };
6718
- }
6719
- function isTextLike(row) {
6720
- const mime = row.mime ?? "";
6721
- const name = row.filename ?? "";
6722
- 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(
6723
- name
6724
- );
6725
- }
6726
- function readFileRange(path9, offset, length) {
6727
- const sizeBytes = statSync2(path9).size;
6728
- if (offset >= sizeBytes) return { buffer: Buffer.alloc(0), bytesRead: 0, sizeBytes };
6729
- const fd = openSync(path9, "r");
6730
- try {
6731
- const target = Math.min(length, sizeBytes - offset);
6732
- const buffer = Buffer.alloc(target);
6733
- const bytesRead = readSync(fd, buffer, 0, target, offset);
6734
- return { buffer: buffer.subarray(0, bytesRead), bytesRead, sizeBytes };
6735
- } finally {
6736
- closeSync(fd);
6737
- }
6738
- }
6739
6287
  function respond3(res, status, body) {
6740
6288
  res.statusCode = status;
6741
6289
  res.setHeader("Content-Type", "application/json");
@@ -6757,532 +6305,9 @@ async function readJson3(req) {
6757
6305
  }
6758
6306
  }
6759
6307
 
6760
- // src/daemon/asset/origin/drop-folder.ts
6308
+ // src/daemon/outbox-watcher.ts
6761
6309
  import { promises as fs3 } from "fs";
6762
- import { homedir as homedir5 } from "os";
6763
6310
  import * as path5 from "path";
6764
- var DropFolderAdapter = class {
6765
- kind = "drop-folder";
6766
- workspaceDir;
6767
- rootDir;
6768
- constructor(opts = {}) {
6769
- this.workspaceDir = opts.workspaceDir;
6770
- this.rootDir = opts.rootDir ?? path5.join(opts.homeDir ?? homedir5(), ".prismer");
6771
- }
6772
- /**
6773
- * Long-lived async iterable wrapping `scanOnce` on a 1s polling interval.
6774
- * Stops when the iterator is broken (e.g. consumer returns / throws).
6775
- */
6776
- async *observe(workspaceId) {
6777
- while (true) {
6778
- const batch = await this.scanOnce(workspaceId);
6779
- for (const obs of batch) yield obs;
6780
- await sleep(1e3);
6781
- }
6782
- }
6783
- /**
6784
- * Single-tick scan exposed for tests and one-shot CLI usage. Reads every
6785
- * file currently under the workspace's drop directory, returning one
6786
- * observation per file. The caller is expected to feed each into the
6787
- * outbox; idempotency on the outbox side collapses re-observations of
6788
- * unchanged files.
6789
- */
6790
- async scanOnce(workspaceId) {
6791
- const drop = this.dropDir(workspaceId);
6792
- let entries = [];
6793
- try {
6794
- entries = await fs3.readdir(drop);
6795
- } catch (err) {
6796
- if (err.code === "ENOENT") return [];
6797
- throw err;
6798
- }
6799
- const out = [];
6800
- for (const name of entries) {
6801
- if (name.startsWith(".")) continue;
6802
- const fullPath = path5.join(drop, name);
6803
- const stat = await fs3.lstat(fullPath).catch(() => null);
6804
- if (!stat) continue;
6805
- if (stat.isSymbolicLink()) continue;
6806
- if (stat.isDirectory()) {
6807
- for (const nested of await walk(fullPath)) {
6808
- const nstat = await fs3.lstat(nested).catch(() => null);
6809
- if (!nstat || !nstat.isFile()) continue;
6810
- out.push(makeObservation(workspaceId, nested, drop, nstat.size, Math.floor(nstat.mtimeMs)));
6811
- }
6812
- continue;
6813
- }
6814
- if (!stat.isFile()) continue;
6815
- out.push(makeObservation(workspaceId, fullPath, drop, stat.size, Math.floor(stat.mtimeMs)));
6816
- }
6817
- return out;
6818
- }
6819
- async identifySource(obs) {
6820
- const detail = obs.detail;
6821
- const rel = path5.relative(detail.watchDir, detail.path);
6822
- const folderDir = path5.dirname(rel);
6823
- return {
6824
- sourceRef: `folder://${obs.workspaceId}/${rel.split(path5.sep).join("/")}`,
6825
- hints: {
6826
- filename: path5.basename(detail.path),
6827
- folderPath: folderDir === "." ? "" : folderDir.split(path5.sep).join("/")
6828
- }
6829
- };
6830
- }
6831
- async fetch(obs) {
6832
- const detail = obs.detail;
6833
- const bytes = await fs3.readFile(detail.path);
6834
- return {
6835
- bytes,
6836
- mime: mimeFromExtension(detail.path),
6837
- size: bytes.length
6838
- };
6839
- }
6840
- /** Hook called by UploadRunner after a successful upload of one observation. */
6841
- async onUploadSuccess(obs) {
6842
- await this.moveTo(obs, this.uploadedDir(obs.workspaceId));
6843
- }
6844
- /** Hook called by UploadRunner after attempts hit maxAttempts. */
6845
- async onUploadFailure(obs) {
6846
- await this.moveTo(obs, this.failedDir(obs.workspaceId));
6847
- }
6848
- async moveTo(obs, destDir) {
6849
- const detail = obs.detail;
6850
- if (!detail || typeof detail.path !== "string") return;
6851
- try {
6852
- const rel = typeof detail.watchDir === "string" ? path5.relative(detail.watchDir, detail.path) : path5.basename(detail.path);
6853
- const safeRel = rel && !rel.startsWith("..") && !path5.isAbsolute(rel) ? rel : path5.basename(detail.path);
6854
- const destPath = path5.join(destDir, safeRel);
6855
- await fs3.mkdir(path5.dirname(destPath), { recursive: true });
6856
- await fs3.rename(detail.path, destPath);
6857
- } catch (err) {
6858
- if (err.code === "ENOENT") return;
6859
- throw err;
6860
- }
6861
- }
6862
- dropDir(workspaceId) {
6863
- if (this.workspaceDir) return path5.join(this.workspaceDir, "drop");
6864
- return path5.join(this.rootDir, "workspaces", workspaceId, "drop");
6865
- }
6866
- uploadedDir(workspaceId) {
6867
- if (this.workspaceDir) return path5.join(this.workspaceDir, "uploaded");
6868
- return path5.join(this.rootDir, "workspaces", workspaceId, "uploaded");
6869
- }
6870
- failedDir(workspaceId) {
6871
- if (this.workspaceDir) return path5.join(this.workspaceDir, "upload-failed");
6872
- return path5.join(this.rootDir, "workspaces", workspaceId, "upload-failed");
6873
- }
6874
- };
6875
- function makeObservation(workspaceId, filePath, watchDir, size, mtime) {
6876
- return {
6877
- workspaceId,
6878
- detail: { path: filePath, watchDir, size, mtime },
6879
- observedAt: mtime
6880
- };
6881
- }
6882
- async function walk(root) {
6883
- const out = [];
6884
- const entries = await fs3.readdir(root).catch(() => []);
6885
- for (const name of entries) {
6886
- if (name.startsWith(".")) continue;
6887
- const full = path5.join(root, name);
6888
- const stat = await fs3.lstat(full).catch(() => null);
6889
- if (!stat) continue;
6890
- if (stat.isSymbolicLink()) continue;
6891
- if (stat.isDirectory()) {
6892
- out.push(...await walk(full));
6893
- } else if (stat.isFile()) {
6894
- out.push(full);
6895
- }
6896
- }
6897
- return out;
6898
- }
6899
- var MIME_BY_EXT = {
6900
- ".txt": "text/plain",
6901
- ".md": "text/markdown",
6902
- ".csv": "text/csv",
6903
- ".json": "application/json",
6904
- ".yaml": "application/yaml",
6905
- ".yml": "application/yaml",
6906
- ".pdf": "application/pdf",
6907
- ".png": "image/png",
6908
- ".jpg": "image/jpeg",
6909
- ".jpeg": "image/jpeg",
6910
- ".gif": "image/gif",
6911
- ".webp": "image/webp",
6912
- ".svg": "image/svg+xml",
6913
- ".html": "text/html",
6914
- ".htm": "text/html",
6915
- ".xhtml": "application/xhtml+xml",
6916
- ".xml": "application/xml",
6917
- ".zip": "application/zip",
6918
- ".tar": "application/x-tar",
6919
- ".gz": "application/gzip",
6920
- ".mp3": "audio/mpeg",
6921
- ".mp4": "video/mp4",
6922
- ".mov": "video/quicktime",
6923
- ".wav": "audio/wav"
6924
- };
6925
- function mimeFromExtension(p) {
6926
- const ext = path5.extname(p).toLowerCase();
6927
- return MIME_BY_EXT[ext] ?? null;
6928
- }
6929
- function sleep(ms) {
6930
- return new Promise((r) => setTimeout(r, ms));
6931
- }
6932
-
6933
- // src/daemon/asset/origin/outbox.ts
6934
- import Database4 from "better-sqlite3";
6935
- import { createHash as createHash5, randomUUID as randomUUID5 } from "crypto";
6936
- import { z as z7 } from "zod";
6937
- var OriginObservationRecordSchema = z7.object({
6938
- workspaceId: z7.string().min(1),
6939
- originKind: z7.enum(["upload", "drop-folder", "agent-gen"]),
6940
- sourceRef: z7.string().min(1),
6941
- payloadJson: z7.string().min(2),
6942
- hintsJson: z7.string().min(2),
6943
- observedAt: z7.number().int().nonnegative()
6944
- });
6945
- var DDL_STATEMENTS = [
6946
- `CREATE TABLE IF NOT EXISTS asset_origin_outbox (
6947
- id TEXT PRIMARY KEY,
6948
- workspaceId TEXT NOT NULL,
6949
- originKind TEXT NOT NULL,
6950
- sourceRef TEXT NOT NULL,
6951
- payloadJson TEXT NOT NULL,
6952
- hintsJson TEXT NOT NULL,
6953
- observedAt INTEGER NOT NULL,
6954
- idempotencyKey TEXT NOT NULL UNIQUE,
6955
- status TEXT NOT NULL CHECK (status IN ('pending', 'claimed', 'uploaded')),
6956
- attempts INTEGER NOT NULL DEFAULT 0,
6957
- lastError TEXT,
6958
- assetId TEXT,
6959
- contentHash TEXT,
6960
- createdAt INTEGER NOT NULL,
6961
- updatedAt INTEGER NOT NULL
6962
- )`,
6963
- `CREATE INDEX IF NOT EXISTS idx_asset_origin_outbox_status
6964
- ON asset_origin_outbox(status, createdAt)`,
6965
- `CREATE TABLE IF NOT EXISTS asset_origin_outbox_dead_letter (
6966
- id TEXT PRIMARY KEY,
6967
- workspaceId TEXT NOT NULL,
6968
- originKind TEXT NOT NULL,
6969
- sourceRef TEXT NOT NULL,
6970
- payloadJson TEXT NOT NULL,
6971
- hintsJson TEXT NOT NULL,
6972
- observedAt INTEGER NOT NULL,
6973
- errorJson TEXT NOT NULL,
6974
- attempts INTEGER NOT NULL,
6975
- createdAt INTEGER NOT NULL
6976
- )`
6977
- ];
6978
- var OriginOutbox = class {
6979
- db;
6980
- constructor(opts) {
6981
- this.db = new Database4(opts.dbPath);
6982
- this.db.pragma("journal_mode = WAL");
6983
- for (const ddl of DDL_STATEMENTS) this.db.prepare(ddl).run();
6984
- }
6985
- /**
6986
- * Validate + persist an observation. Schema-invalid input lands in
6987
- * `asset_origin_outbox_dead_letter` (never silently dropped). Re-enqueue
6988
- * with the same {originKind, workspaceId, sourceRef, observedAt} returns
6989
- * the existing row id without inserting again.
6990
- */
6991
- enqueue(record) {
6992
- const now = Date.now();
6993
- const parsed = OriginObservationRecordSchema.safeParse(record);
6994
- if (!parsed.success) {
6995
- const dlId = `dl_${randomUUID5()}`;
6996
- const raw = record && typeof record === "object" ? record : {};
6997
- this.db.prepare(
6998
- `INSERT INTO asset_origin_outbox_dead_letter
6999
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt, errorJson, attempts, createdAt)
7000
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7001
- ).run(
7002
- dlId,
7003
- coerceToString(raw.workspaceId, ""),
7004
- coerceToString(raw.originKind, ""),
7005
- coerceToString(raw.sourceRef, ""),
7006
- coerceToJson(raw.payloadJson),
7007
- coerceToJson(raw.hintsJson),
7008
- typeof raw.observedAt === "number" ? raw.observedAt : 0,
7009
- JSON.stringify(parsed.error.errors),
7010
- 0,
7011
- now
7012
- );
7013
- return { id: dlId, deadLetter: true };
7014
- }
7015
- const validated = parsed.data;
7016
- const idempotencyKey = computeIdempotencyKey(validated);
7017
- const rowId = `out_${randomUUID5()}`;
7018
- try {
7019
- this.db.prepare(
7020
- `INSERT INTO asset_origin_outbox
7021
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt,
7022
- idempotencyKey, status, attempts, createdAt, updatedAt)
7023
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', 0, ?, ?)`
7024
- ).run(
7025
- rowId,
7026
- validated.workspaceId,
7027
- validated.originKind,
7028
- validated.sourceRef,
7029
- validated.payloadJson,
7030
- validated.hintsJson,
7031
- validated.observedAt,
7032
- idempotencyKey,
7033
- now,
7034
- now
7035
- );
7036
- return { id: rowId, deadLetter: false };
7037
- } catch (err) {
7038
- const code = err.code;
7039
- if (code === "SQLITE_CONSTRAINT_UNIQUE") {
7040
- const existing = this.db.prepare("SELECT id FROM asset_origin_outbox WHERE idempotencyKey = ?").get(idempotencyKey);
7041
- if (existing) return { id: existing.id, deadLetter: false };
7042
- }
7043
- throw err;
7044
- }
7045
- }
7046
- /**
7047
- * Atomically claim the oldest pending row, transition status to 'claimed',
7048
- * and return it. Returns null when the queue is empty. The caller MUST
7049
- * follow up with `markUploaded` (success) or `recordFailure` (error) so
7050
- * the row doesn't sit in 'claimed' forever — Phase-0 is a single worker
7051
- * so we don't need a claim-expiry sweeper yet.
7052
- */
7053
- claimNext() {
7054
- const pickAndClaim = this.db.transaction(() => {
7055
- const row = this.db.prepare(
7056
- `SELECT id, workspaceId, originKind, sourceRef, payloadJson, hintsJson,
7057
- observedAt, attempts, lastError, createdAt
7058
- FROM asset_origin_outbox
7059
- WHERE status = 'pending'
7060
- ORDER BY createdAt ASC
7061
- LIMIT 1`
7062
- ).get();
7063
- if (!row) return null;
7064
- this.db.prepare("UPDATE asset_origin_outbox SET status = 'claimed', updatedAt = ? WHERE id = ?").run(Date.now(), row.id);
7065
- return row;
7066
- });
7067
- const claimed = pickAndClaim();
7068
- if (!claimed) return null;
7069
- return {
7070
- id: claimed.id,
7071
- row: rawToRow(claimed)
7072
- };
7073
- }
7074
- markUploaded(id, meta) {
7075
- this.db.prepare(
7076
- `UPDATE asset_origin_outbox
7077
- SET status = 'uploaded', assetId = ?, contentHash = ?, updatedAt = ?
7078
- WHERE id = ?`
7079
- ).run(meta.assetId, meta.contentHash, Date.now(), id);
7080
- }
7081
- /**
7082
- * Record one failed upload attempt. Increments `attempts`, stores the
7083
- * latest error, and either re-queues for retry (status -> 'pending') or
7084
- * spills to dead-letter once attempts hit maxAttempts.
7085
- */
7086
- recordFailure(id, error, opts) {
7087
- const now = Date.now();
7088
- const updated = this.db.transaction(() => {
7089
- const row = this.db.prepare(
7090
- `SELECT id, workspaceId, originKind, sourceRef, payloadJson, hintsJson,
7091
- observedAt, attempts, createdAt
7092
- FROM asset_origin_outbox
7093
- WHERE id = ?`
7094
- ).get(id);
7095
- if (!row) return;
7096
- const nextAttempts = row.attempts + 1;
7097
- if (nextAttempts >= opts.maxAttempts) {
7098
- this.db.prepare(
7099
- `INSERT INTO asset_origin_outbox_dead_letter
7100
- (id, workspaceId, originKind, sourceRef, payloadJson, hintsJson, observedAt, errorJson, attempts, createdAt)
7101
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
7102
- ).run(
7103
- `dl_${randomUUID5()}`,
7104
- row.workspaceId,
7105
- row.originKind,
7106
- row.sourceRef,
7107
- row.payloadJson,
7108
- row.hintsJson,
7109
- row.observedAt,
7110
- JSON.stringify({ message: error, attempts: nextAttempts }),
7111
- nextAttempts,
7112
- now
7113
- );
7114
- this.db.prepare("DELETE FROM asset_origin_outbox WHERE id = ?").run(id);
7115
- } else {
7116
- this.db.prepare(
7117
- `UPDATE asset_origin_outbox
7118
- SET status = 'pending', attempts = ?, lastError = ?, updatedAt = ?
7119
- WHERE id = ?`
7120
- ).run(nextAttempts, error, now, id);
7121
- }
7122
- });
7123
- updated();
7124
- }
7125
- /**
7126
- * Re-promote any rows orphaned in `'claimed'` state back to `'pending'`.
7127
- *
7128
- * Why this exists: claimNext() transitions a row to 'claimed' under a
7129
- * transaction, but the upload that follows runs OUTSIDE the transaction
7130
- * (network call). If the daemon dies between the claim and the
7131
- * markUploaded/recordFailure call, the row is stuck — claimNext() only
7132
- * selects status='pending', so it would never be picked up again.
7133
- *
7134
- * Phase-0 single-worker safety: there is at most one in-flight claim at
7135
- * any time, so on next process startup ANY 'claimed' row is by definition
7136
- * orphaned. We promote it back to 'pending' (preserving attempts /
7137
- * lastError) so the next drainOnce can retry it.
7138
- *
7139
- * If we ever go multi-worker, this naive sweep becomes unsafe and must be
7140
- * replaced with a lease/heartbeat scheme.
7141
- *
7142
- * Returns the number of rows recovered (for logging / observability).
7143
- */
7144
- resetClaimed() {
7145
- const result = this.db.prepare(
7146
- "UPDATE asset_origin_outbox SET status = 'pending', updatedAt = ? WHERE status = 'claimed'"
7147
- ).run(Date.now());
7148
- return Number(result.changes);
7149
- }
7150
- pendingCount() {
7151
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox WHERE status = 'pending'").get().n;
7152
- }
7153
- uploadedCount() {
7154
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox WHERE status = 'uploaded'").get().n;
7155
- }
7156
- deadLetterCount() {
7157
- return this.db.prepare("SELECT COUNT(*) AS n FROM asset_origin_outbox_dead_letter").get().n;
7158
- }
7159
- close() {
7160
- this.db.close();
7161
- }
7162
- };
7163
- function computeIdempotencyKey(record) {
7164
- return createHash5("sha256").update(record.originKind).update("|").update(record.workspaceId).update("|").update(record.sourceRef).update("|").update(String(record.observedAt)).digest("hex");
7165
- }
7166
- function coerceToString(value, fallback) {
7167
- if (typeof value === "string") return value;
7168
- if (value == null) return fallback;
7169
- return String(value);
7170
- }
7171
- function coerceToJson(value) {
7172
- if (typeof value === "string") return value;
7173
- if (value == null) return "{}";
7174
- try {
7175
- return JSON.stringify(value);
7176
- } catch {
7177
- return "{}";
7178
- }
7179
- }
7180
- function rawToRow(raw) {
7181
- return {
7182
- id: raw.id,
7183
- workspaceId: raw.workspaceId,
7184
- originKind: raw.originKind,
7185
- sourceRef: raw.sourceRef,
7186
- payloadJson: raw.payloadJson,
7187
- hintsJson: raw.hintsJson,
7188
- observedAt: raw.observedAt,
7189
- attempts: raw.attempts,
7190
- lastError: raw.lastError,
7191
- createdAt: raw.createdAt
7192
- };
7193
- }
7194
-
7195
- // src/daemon/asset/origin/upload-runner.ts
7196
- var UploadRunner = class {
7197
- constructor(opts) {
7198
- this.opts = opts;
7199
- opts.outbox.resetClaimed();
7200
- }
7201
- opts;
7202
- /**
7203
- * Process every currently-pending outbox row exactly once. Failed rows
7204
- * stay 'pending' (with attempts+1) for the next drainOnce call; rows
7205
- * that hit maxAttempts spill to dead-letter and trigger onUploadFailure.
7206
- *
7207
- * Returns the count of rows processed (success + failure).
7208
- */
7209
- async drainOnce() {
7210
- let count = 0;
7211
- while (true) {
7212
- const claim = this.opts.outbox.claimNext();
7213
- if (!claim) break;
7214
- count += 1;
7215
- const row = claim.row;
7216
- const adapter = this.opts.adapters[row.originKind];
7217
- if (!adapter) {
7218
- this.opts.outbox.recordFailure(claim.id, `no adapter registered for kind=${row.originKind}`, {
7219
- maxAttempts: 1
7220
- // unrecoverable — fast-fail to dead-letter
7221
- });
7222
- continue;
7223
- }
7224
- try {
7225
- const obs = rowToObservation(row);
7226
- const bytes = await adapter.fetch(obs);
7227
- const id = await adapter.identifySource(obs);
7228
- const filename = id.hints?.filename ?? path6.basename(row.sourceRef);
7229
- const metadata = {
7230
- sourceRef: row.sourceRef,
7231
- sourceKind: row.originKind
7232
- };
7233
- if (id.hints?.description) metadata.description = id.hints.description;
7234
- if (id.hints?.sourceAgentImUserId) {
7235
- metadata.sourceAgentImUserId = id.hints.sourceAgentImUserId;
7236
- }
7237
- const folderPath = id.hints?.folderPath || void 0;
7238
- const result = await this.opts.cloud.uploadAsset({
7239
- workspaceId: row.workspaceId,
7240
- filename,
7241
- bytes: bytes.bytes,
7242
- mime: bytes.mime,
7243
- size: bytes.size,
7244
- metadata,
7245
- folderPath
7246
- });
7247
- this.opts.outbox.markUploaded(claim.id, result);
7248
- await adapter.onUploadSuccess?.(obs);
7249
- } catch (err) {
7250
- const message = err instanceof Error ? err.message : String(err);
7251
- const before = this.opts.outbox.deadLetterCount();
7252
- this.opts.outbox.recordFailure(claim.id, message, { maxAttempts: this.opts.maxAttempts });
7253
- const after = this.opts.outbox.deadLetterCount();
7254
- if (after > before) {
7255
- const obs = rowToObservation(row);
7256
- await adapter.onUploadFailure?.(obs, err);
7257
- }
7258
- }
7259
- }
7260
- return count;
7261
- }
7262
- };
7263
- function rowToObservation(row) {
7264
- let detail = {};
7265
- try {
7266
- const parsed = JSON.parse(row.payloadJson);
7267
- if (parsed && typeof parsed === "object") detail = parsed;
7268
- } catch {
7269
- }
7270
- return {
7271
- workspaceId: row.workspaceId,
7272
- detail,
7273
- observedAt: row.observedAt
7274
- };
7275
- }
7276
- var path6 = {
7277
- basename(p) {
7278
- const idx = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
7279
- return idx === -1 ? p : p.slice(idx + 1);
7280
- }
7281
- };
7282
-
7283
- // src/daemon/outbox-watcher.ts
7284
- import { promises as fs4 } from "fs";
7285
- import * as path7 from "path";
7286
6311
  var DEFAULT_INTERVAL_MS = 2e3;
7287
6312
  var RESERVED_SUBDIR = "_uploaded";
7288
6313
  var OutboxWatcher = class {
@@ -7449,7 +6474,7 @@ var OutboxWatcher = class {
7449
6474
  async scanDir(dir, kind, task) {
7450
6475
  let entries = [];
7451
6476
  try {
7452
- entries = await fs4.readdir(dir);
6477
+ entries = await fs3.readdir(dir);
7453
6478
  } catch (err) {
7454
6479
  const code = err.code;
7455
6480
  if (code === "ENOENT") return;
@@ -7459,10 +6484,10 @@ var OutboxWatcher = class {
7459
6484
  for (const name of entries) {
7460
6485
  if (name.startsWith(".")) continue;
7461
6486
  if (name === RESERVED_SUBDIR) continue;
7462
- const full = path7.join(dir, name);
6487
+ const full = path5.join(dir, name);
7463
6488
  let st;
7464
6489
  try {
7465
- st = await fs4.stat(full);
6490
+ st = await fs3.stat(full);
7466
6491
  } catch {
7467
6492
  continue;
7468
6493
  }
@@ -7487,8 +6512,8 @@ var OutboxWatcher = class {
7487
6512
  this.log("warn", `skip ${filePath}: no active taskId (no dispatch/handoff received yet)`);
7488
6513
  return;
7489
6514
  }
7490
- const bytes = await fs4.readFile(filePath);
7491
- const fileName = path7.basename(filePath);
6515
+ const bytes = await fs3.readFile(filePath);
6516
+ const fileName = path5.basename(filePath);
7492
6517
  const blob = new Blob([new Uint8Array(bytes)]);
7493
6518
  const form = new FormData();
7494
6519
  form.append("workspaceId", wsId);
@@ -7727,15 +6752,6 @@ var Runner = class extends EventEmitter3 {
7727
6752
  outboxWatcher;
7728
6753
  memoryWiring;
7729
6754
  assetMetadataIndexes = /* @__PURE__ */ new Map();
7730
- workspaceMirrors = /* @__PURE__ */ new Map();
7731
- assetOriginOutbox;
7732
- dropFolderAdapter;
7733
- dropFolderUploadRunner;
7734
- dropFolderTimer;
7735
- dropFolderWorkspaceId = "";
7736
- dropFolderWorkspaceDir = "";
7737
- dropFolderTickInFlight = false;
7738
- dropFolderStable = /* @__PURE__ */ new Map();
7739
6755
  state = "idle";
7740
6756
  startedAt = 0;
7741
6757
  workspaceId = "";
@@ -7777,7 +6793,6 @@ var Runner = class extends EventEmitter3 {
7777
6793
  this.servicePool = new ServicePool();
7778
6794
  this.installStaticHostedAgentFromEnv();
7779
6795
  this.loadAgentsFromDb();
7780
- await this.prepareLocalProfiles();
7781
6796
  this.syncQueue = new SyncQueue(this.db);
7782
6797
  this.syncWorker = new SyncWorker({ queue: this.syncQueue, flush: (row) => this.flushSyncRow(row) });
7783
6798
  this.syncWorker.start();
@@ -7799,10 +6814,9 @@ var Runner = class extends EventEmitter3 {
7799
6814
  );
7800
6815
  }
7801
6816
  if (this.workspaceId) {
7802
- this.syncAssetState(this.workspaceId).catch(
7803
- (err) => console.error("[Daemon] Initial asset sync failed:", err.message)
6817
+ this.syncAssetMetadata(this.workspaceId).catch(
6818
+ (err) => console.error("[Daemon] Initial asset metadata sync failed:", err.message)
7804
6819
  );
7805
- await this.ensureDropFolderRuntime(this.workspaceId);
7806
6820
  }
7807
6821
  const containerId = process.env.PRISMER_CONTAINER_ID;
7808
6822
  const isContainer = !!containerId || process.env.PRISMER_RUNTIME_MODE === "container";
@@ -7853,8 +6867,7 @@ var Runner = class extends EventEmitter3 {
7853
6867
  // an actual agent process.
7854
6868
  attachMemory: this.memoryWiring ? attachMemoryRpc({ runtime: this.memoryWiring.runtime }) : void 0,
7855
6869
  attachAsset: attachAssetRpc({
7856
- resolveIndex: (workspaceId) => this.assetMetadataIndexes.get(workspaceId),
7857
- assetCache: this.assetCache
6870
+ resolveIndex: (workspaceId) => this.assetMetadataIndexes.get(workspaceId)
7858
6871
  })
7859
6872
  });
7860
6873
  await this.localServer.start();
@@ -7903,7 +6916,6 @@ var Runner = class extends EventEmitter3 {
7903
6916
  this.memoryWiring = void 0;
7904
6917
  this.ws?.close();
7905
6918
  this.outboxWatcher?.stop();
7906
- this.stopDropFolderRuntime();
7907
6919
  await this.servicePool?.shutdown();
7908
6920
  await this.localServer?.stop();
7909
6921
  try {
@@ -7960,7 +6972,6 @@ var Runner = class extends EventEmitter3 {
7960
6972
  runningTaskIds: Array.from(this.runningTasks.keys()),
7961
6973
  observability: {
7962
6974
  adapters: this.snapshotAdapterObservability(),
7963
- assetSync: this.snapshotAssetSyncObservability(),
7964
6975
  ...this.lastTaskError ? { lastTaskError: this.lastTaskError } : {}
7965
6976
  }
7966
6977
  };
@@ -7996,35 +7007,6 @@ var Runner = class extends EventEmitter3 {
7996
7007
  });
7997
7008
  }
7998
7009
  }
7999
- async prepareLocalProfiles() {
8000
- const rows = this.db.prepare(
8001
- `SELECT id, workspace_id, agent_im_user_id, adapter_name, name, config, version, synced_at
8002
- FROM agent_profiles
8003
- WHERE deleted_at IS NULL`
8004
- ).all();
8005
- for (const row of rows) {
8006
- const adapter = this.registry.get(row.adapter_name);
8007
- if (!adapter?.prepareProfile) continue;
8008
- try {
8009
- await adapter.prepareProfile({
8010
- id: row.id,
8011
- workspaceId: row.workspace_id,
8012
- agentImUserId: row.agent_im_user_id,
8013
- adapterName: row.adapter_name,
8014
- name: row.name,
8015
- config: parseProfileConfig(row.config),
8016
- version: row.version,
8017
- createdAt: new Date(row.synced_at ?? Date.now()),
8018
- updatedAt: new Date(row.synced_at ?? Date.now())
8019
- });
8020
- } catch (err) {
8021
- process.stderr.write(
8022
- `[daemon] local profile preflight skipped agent=${row.agent_im_user_id} profile=${row.id}: ${err.message}
8023
- `
8024
- );
8025
- }
8026
- }
8027
- }
8028
7010
  /**
8029
7011
  * Register an in-process AgentProfile snapshot (called by agent CLI / sync layer).
8030
7012
  * Used to populate the `agents` list in agent.host.declare.
@@ -8085,7 +7067,7 @@ var Runner = class extends EventEmitter3 {
8085
7067
  if (!existsSync9(rawFile)) {
8086
7068
  throw new Error(`PRISMER_HOSTED_AGENT_FILE not found: ${rawFile}`);
8087
7069
  }
8088
- raw = readFileSync7(rawFile, "utf8");
7070
+ raw = readFileSync6(rawFile, "utf8");
8089
7071
  } else if (rawJson) {
8090
7072
  raw = rawJson;
8091
7073
  }
@@ -8187,7 +7169,7 @@ var Runner = class extends EventEmitter3 {
8187
7169
  name: a.name,
8188
7170
  adapterName: a.adapterName,
8189
7171
  capabilities: a.capabilities,
8190
- profiles: Array.from(a.profiles.entries()).map(([id, version]) => ({ id, version }))
7172
+ profiles: Array.from(a.profiles.entries()).map(([id, version2]) => ({ id, version: version2 }))
8191
7173
  }))
8192
7174
  };
8193
7175
  this.ws.send(envelope("agent.host.declare", payload, this.config.daemon_id));
@@ -8229,29 +7211,17 @@ var Runner = class extends EventEmitter3 {
8229
7211
  (err) => console.error("[Daemon] Initial memory sync failed:", err.message)
8230
7212
  );
8231
7213
  }
8232
- this.syncAssetState(this.workspaceId).catch(
8233
- (err) => console.error("[Daemon] Asset sync failed:", err.message)
7214
+ this.syncAssetMetadata(this.workspaceId).catch(
7215
+ (err) => console.error("[Daemon] Asset metadata sync failed:", err.message)
8234
7216
  );
8235
- await this.ensureDropFolderRuntime(this.workspaceId);
8236
7217
  for (const id of payload.profilesToSync) {
8237
7218
  try {
8238
- await this.servicePool.drop(id);
8239
7219
  await this.syncProfileFromCloud(id);
8240
7220
  } catch (err) {
8241
7221
  this.emit("sync-error", err);
8242
7222
  }
8243
7223
  }
8244
- for (const id of payload.profilesToDelete ?? []) {
8245
- const row = this.db.prepare("SELECT agent_im_user_id FROM agent_profiles WHERE id = ?").get(id);
8246
- this.db.prepare("DELETE FROM agent_profiles WHERE id = ?").run(id);
8247
- if (row) {
8248
- this.db.prepare("DELETE FROM agents WHERE im_user_id = ?").run(row.agent_im_user_id);
8249
- }
8250
- process.stderr.write(`[daemon] tombstone profile=${id}
8251
- `);
8252
- }
8253
- if (payload.profilesToDelete?.length) this.loadAgentsFromDb();
8254
- if ((payload.profilesToSync.length > 0 || (payload.profilesToDelete?.length ?? 0) > 0) && this.wsConnected) this.sendDeclare();
7224
+ if (payload.profilesToSync.length > 0 && this.wsConnected) this.sendDeclare();
8255
7225
  this.emit("host-acked", payload);
8256
7226
  }
8257
7227
  async onTaskDispatch(payload, requestId) {
@@ -8291,15 +7261,9 @@ var Runner = class extends EventEmitter3 {
8291
7261
  this.ws.send(envelope("task.dispatch.progress", progressPayload));
8292
7262
  }
8293
7263
  });
8294
- this.ws.send(envelope("task.dispatch.reply", reply, requestId));
8295
- if (!reply.ok) {
8296
- const code = reply.error?.code ?? "unknown";
8297
- const message = reply.error?.message ?? "unknown error";
8298
- process.stderr.write(`[daemon] dispatch failed task=${payload.taskId} code=${code} message=${message}
8299
- `);
8300
- }
7264
+ this.ws.send(envelope("task.dispatch.reply", reply, requestId));
8301
7265
  } else {
8302
- const reply = await handleDispatch(payload, requestId, {
7266
+ await handleDispatch(payload, requestId, {
8303
7267
  registry: this.registry,
8304
7268
  cloud: this.cloud,
8305
7269
  uriResolver: this.uriResolver,
@@ -8315,12 +7279,6 @@ var Runner = class extends EventEmitter3 {
8315
7279
  if (running) running.lastProgressAt = Date.now();
8316
7280
  }
8317
7281
  });
8318
- if (!reply.ok) {
8319
- const code = reply.error?.code ?? "unknown";
8320
- const message = reply.error?.message ?? "unknown error";
8321
- process.stderr.write(`[daemon] dispatch failed task=${payload.taskId} code=${code} message=${message}
8322
- `);
8323
- }
8324
7282
  }
8325
7283
  process.stdout.write(`[daemon] dispatch done task=${payload.taskId}
8326
7284
  `);
@@ -8348,7 +7306,6 @@ var Runner = class extends EventEmitter3 {
8348
7306
  }
8349
7307
  async onAgentProfileChanged(payload) {
8350
7308
  try {
8351
- await this.servicePool.drop(payload.profileId);
8352
7309
  await this.syncProfileFromCloud(payload.profileId);
8353
7310
  if (this.wsConnected) this.sendDeclare();
8354
7311
  } catch (err) {
@@ -8356,21 +7313,7 @@ var Runner = class extends EventEmitter3 {
8356
7313
  }
8357
7314
  }
8358
7315
  async syncProfileFromCloud(profileId) {
8359
- let profile;
8360
- try {
8361
- profile = await this.cloud.get(`/api/im/agent_profiles/${encodeURIComponent(profileId)}`);
8362
- } catch (err) {
8363
- if (err instanceof CloudError && err.status === 404) {
8364
- const row = this.db.prepare("SELECT agent_im_user_id FROM agent_profiles WHERE id = ?").get(profileId);
8365
- this.db.prepare("DELETE FROM agent_profiles WHERE id = ?").run(profileId);
8366
- if (row) {
8367
- this.db.prepare("DELETE FROM agents WHERE im_user_id = ?").run(row.agent_im_user_id);
8368
- }
8369
- this.loadAgentsFromDb();
8370
- return;
8371
- }
8372
- throw err;
8373
- }
7316
+ const profile = await this.cloud.get(`/api/im/agent_profiles/${encodeURIComponent(profileId)}`);
8374
7317
  const agent = await this.resolveOwnedAgent(profile.agentImUserId);
8375
7318
  const adapter = this.registry.get(profile.adapterName);
8376
7319
  const capabilities = agent?.card?.capabilities?.length ? agent.card.capabilities : adapter?.capabilities ?? [];
@@ -8405,14 +7348,6 @@ var Runner = class extends EventEmitter3 {
8405
7348
  );
8406
7349
  });
8407
7350
  tx();
8408
- try {
8409
- await adapter?.prepareProfile?.(profile);
8410
- } catch (err) {
8411
- process.stderr.write(
8412
- `[daemon] profile preflight skipped agent=${profile.agentImUserId} profile=${profile.id}: ${err.message}
8413
- `
8414
- );
8415
- }
8416
7351
  this.loadAgentsFromDb();
8417
7352
  process.stdout.write(
8418
7353
  `[daemon] profile synced agent=${profile.agentImUserId} profile=${profile.id} adapter=${profile.adapterName}
@@ -8441,10 +7376,15 @@ var Runner = class extends EventEmitter3 {
8441
7376
  }
8442
7377
  async onAssetChanged(payload) {
8443
7378
  if (!payload.workspaceId) return;
7379
+ const index = this.assetMetadataIndexes.get(payload.workspaceId);
7380
+ if (!index) return;
8444
7381
  try {
8445
- await this.syncAssetState(payload.workspaceId);
7382
+ const result = await index.pullDelta();
7383
+ if (result.applied > 0) {
7384
+ console.log(`[Daemon] asset.changed workspace=${payload.workspaceId} applied=${result.applied}`);
7385
+ }
8446
7386
  } catch (err) {
8447
- console.error(`[Daemon] asset.changed sync failed workspace=${payload.workspaceId}:`, err.message);
7387
+ console.error(`[Daemon] asset.changed pullDelta failed workspace=${payload.workspaceId}:`, err.message);
8448
7388
  }
8449
7389
  }
8450
7390
  onWorkspaceFileChanged(payload) {
@@ -8482,15 +7422,6 @@ var Runner = class extends EventEmitter3 {
8482
7422
  }
8483
7423
  };
8484
7424
  }
8485
- snapshotAssetSyncObservability() {
8486
- return {
8487
- workspaceId: this.dropFolderWorkspaceId || null,
8488
- rootDir: this.dropFolderWorkspaceDir || null,
8489
- dropDir: this.dropFolderWorkspaceDir ? join13(this.dropFolderWorkspaceDir, "drop") : null,
8490
- uploadedDir: this.dropFolderWorkspaceDir ? join13(this.dropFolderWorkspaceDir, "uploaded") : null,
8491
- failedDir: this.dropFolderWorkspaceDir ? join13(this.dropFolderWorkspaceDir, "upload-failed") : null
8492
- };
8493
- }
8494
7425
  /**
8495
7426
  * Ensure an AssetMetadataIndex exists for the given workspace and pull
8496
7427
  * delta from cloud. Idempotent — creates the index on first call, reuses
@@ -8513,132 +7444,6 @@ var Runner = class extends EventEmitter3 {
8513
7444
  console.log(`[AssetMeta] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor}`);
8514
7445
  }
8515
7446
  }
8516
- /**
8517
- * Ensure a WorkspaceMirror exists for the workspace and pull path→asset
8518
- * bindings from cloud. Cold-start daemons previously only had this table
8519
- * populated by WS push or per-path URI fallback, so local asset state looked
8520
- * empty even though cloud files existed.
8521
- */
8522
- async syncWorkspaceFiles(workspaceId) {
8523
- let mirror = this.workspaceMirrors.get(workspaceId);
8524
- if (!mirror) {
8525
- const stateDir = `${this.paths.root}/${workspaceId}`;
8526
- mirror = new WorkspaceMirror({
8527
- db: this.db,
8528
- cloud: this.cloud,
8529
- workspaceId,
8530
- workspaceStateDir: stateDir
8531
- });
8532
- this.workspaceMirrors.set(workspaceId, mirror);
8533
- }
8534
- const result = await mirror.pullDelta();
8535
- if (result.applied > 0) {
8536
- console.log(`[WorkspaceFiles] workspace=${workspaceId}: ${result.applied} applied, cursor=${result.cursor ?? "null"}`);
8537
- }
8538
- }
8539
- async syncAssetState(workspaceId) {
8540
- if (!this.paths?.root) return;
8541
- await Promise.all([
8542
- this.syncAssetMetadata(workspaceId),
8543
- this.syncWorkspaceFiles(workspaceId)
8544
- ]);
8545
- }
8546
- /**
8547
- * Desktop asset ingress: watch a user-visible local asset folder
8548
- * and upload new files as IMAssets, preserving nested relative dirs in
8549
- * IMAsset.folderPath. The lower-level adapter/outbox/uploader already
8550
- * existed; this method wires them into the real daemon lifecycle.
8551
- */
8552
- async ensureDropFolderRuntime(workspaceId) {
8553
- if (!workspaceId || !this.paths?.root) return;
8554
- if (this.dropFolderWorkspaceId === workspaceId && this.dropFolderTimer) return;
8555
- this.stopDropFolderRuntime();
8556
- this.dropFolderWorkspaceId = workspaceId;
8557
- const workspaceRoot = resolveUserAssetWorkspaceDir(workspaceId);
8558
- this.dropFolderWorkspaceDir = workspaceRoot;
8559
- await mkdir(join13(workspaceRoot, "drop"), { recursive: true });
8560
- await mkdir(join13(workspaceRoot, "uploaded"), { recursive: true });
8561
- await mkdir(join13(workspaceRoot, "upload-failed"), { recursive: true });
8562
- this.assetOriginOutbox = new OriginOutbox({ dbPath: join13(this.paths.root, "asset-origin.db") });
8563
- this.dropFolderAdapter = new DropFolderAdapter({ workspaceDir: workspaceRoot });
8564
- this.dropFolderUploadRunner = new UploadRunner({
8565
- outbox: this.assetOriginOutbox,
8566
- cloud: new DaemonAssetUploadClient(this.config),
8567
- adapters: { "drop-folder": this.dropFolderAdapter },
8568
- maxAttempts: 3
8569
- });
8570
- const tick = () => {
8571
- this.runDropFolderTick().catch((err) => {
8572
- process.stderr.write(`[daemon] drop-folder sync failed: ${err.message}
8573
- `);
8574
- });
8575
- };
8576
- this.dropFolderTimer = setInterval(tick, 1e3);
8577
- tick();
8578
- process.stdout.write(`[daemon] drop-folder asset sync watching ${join13(workspaceRoot, "drop")}
8579
- `);
8580
- }
8581
- stopDropFolderRuntime() {
8582
- if (this.dropFolderTimer) {
8583
- clearInterval(this.dropFolderTimer);
8584
- this.dropFolderTimer = void 0;
8585
- }
8586
- try {
8587
- this.assetOriginOutbox?.close();
8588
- } catch {
8589
- }
8590
- this.assetOriginOutbox = void 0;
8591
- this.dropFolderAdapter = void 0;
8592
- this.dropFolderUploadRunner = void 0;
8593
- this.dropFolderWorkspaceId = "";
8594
- this.dropFolderWorkspaceDir = "";
8595
- this.dropFolderTickInFlight = false;
8596
- this.dropFolderStable.clear();
8597
- }
8598
- async runDropFolderTick() {
8599
- if (this.dropFolderTickInFlight) return;
8600
- const workspaceId = this.dropFolderWorkspaceId;
8601
- const adapter = this.dropFolderAdapter;
8602
- const outbox = this.assetOriginOutbox;
8603
- const runner = this.dropFolderUploadRunner;
8604
- if (!workspaceId || !adapter || !outbox || !runner) return;
8605
- this.dropFolderTickInFlight = true;
8606
- try {
8607
- const observations = await adapter.scanOnce(workspaceId);
8608
- for (const obs of observations) {
8609
- const id = await adapter.identifySource(obs);
8610
- if (!this.isDropFolderObservationStable(id.sourceRef, obs.detail)) continue;
8611
- outbox.enqueue({
8612
- workspaceId: obs.workspaceId,
8613
- originKind: "drop-folder",
8614
- sourceRef: id.sourceRef,
8615
- payloadJson: JSON.stringify(obs.detail),
8616
- hintsJson: JSON.stringify(id.hints ?? {}),
8617
- observedAt: obs.observedAt
8618
- });
8619
- }
8620
- const processed = await runner.drainOnce();
8621
- if (processed > 0) {
8622
- this.dropFolderStable.clear();
8623
- process.stdout.write(`[daemon] drop-folder uploaded/processed ${processed} asset(s)
8624
- `);
8625
- await this.syncAssetState(workspaceId).catch(
8626
- (err) => process.stderr.write(`[daemon] drop-folder post-sync failed: ${err.message}
8627
- `)
8628
- );
8629
- }
8630
- } finally {
8631
- this.dropFolderTickInFlight = false;
8632
- }
8633
- }
8634
- isDropFolderObservationStable(sourceRef, detail) {
8635
- const raw = detail && typeof detail === "object" ? detail : {};
8636
- const size = typeof raw.size === "number" ? raw.size : -1;
8637
- const mtime = typeof raw.mtime === "number" ? raw.mtime : -1;
8638
- const previous = this.dropFolderStable.get(sourceRef);
8639
- this.dropFolderStable.set(sourceRef, { size, mtime });
8640
- return Boolean(previous && previous.size === size && previous.mtime === mtime);
8641
- }
8642
7447
  /**
8643
7448
  * SyncWorker FlushFn — pushes local writes to cloud.
8644
7449
  *
@@ -8675,44 +7480,44 @@ var Runner = class extends EventEmitter3 {
8675
7480
  const id = encodeURIComponent(row.resource_id);
8676
7481
  const body = row.operation === "delete" ? void 0 : safeJsonParse(row.payload);
8677
7482
  let method;
8678
- let path9;
7483
+ let path7;
8679
7484
  switch (op) {
8680
7485
  case "workspace.create":
8681
7486
  method = "POST";
8682
- path9 = "/api/im/workspaces";
7487
+ path7 = "/api/im/workspaces";
8683
7488
  break;
8684
7489
  case "workspace.update":
8685
7490
  method = "PATCH";
8686
- path9 = `/api/im/workspaces/${id}`;
7491
+ path7 = `/api/im/workspaces/${id}`;
8687
7492
  break;
8688
7493
  case "workspace.delete":
8689
7494
  method = "DELETE";
8690
- path9 = `/api/im/workspaces/${id}`;
7495
+ path7 = `/api/im/workspaces/${id}`;
8691
7496
  break;
8692
7497
  // agent.create must use /register — POST /api/im/agents is not exposed.
8693
7498
  case "agent.create":
8694
7499
  method = "POST";
8695
- path9 = "/api/im/register";
7500
+ path7 = "/api/im/register";
8696
7501
  break;
8697
7502
  case "agent.update":
8698
7503
  method = "PATCH";
8699
- path9 = `/api/im/agents/${id}`;
7504
+ path7 = `/api/im/agents/${id}`;
8700
7505
  break;
8701
7506
  case "agent.delete":
8702
7507
  method = "DELETE";
8703
- path9 = `/api/im/agents/${id}`;
7508
+ path7 = `/api/im/agents/${id}`;
8704
7509
  break;
8705
7510
  case "agent_profile.create":
8706
7511
  method = "POST";
8707
- path9 = "/api/im/agent_profiles";
7512
+ path7 = "/api/im/agent_profiles";
8708
7513
  break;
8709
7514
  case "agent_profile.update":
8710
7515
  method = "PATCH";
8711
- path9 = `/api/im/agent_profiles/${id}`;
7516
+ path7 = `/api/im/agent_profiles/${id}`;
8712
7517
  break;
8713
7518
  case "agent_profile.delete":
8714
7519
  method = "DELETE";
8715
- path9 = `/api/im/agent_profiles/${id}`;
7520
+ path7 = `/api/im/agent_profiles/${id}`;
8716
7521
  break;
8717
7522
  default:
8718
7523
  process.stderr.write(`[daemon] sync flush op=${op} id=${row.id} result=drop (unknown op)
@@ -8721,7 +7526,7 @@ var Runner = class extends EventEmitter3 {
8721
7526
  }
8722
7527
  let res;
8723
7528
  try {
8724
- res = await this.cloud.request(method, path9, { body });
7529
+ res = await this.cloud.request(method, path7, { body });
8725
7530
  } catch (err) {
8726
7531
  process.stderr.write(
8727
7532
  `[daemon] sync flush op=${op} id=${row.id} result=retry (threw: ${err.message})
@@ -8744,89 +7549,6 @@ var Runner = class extends EventEmitter3 {
8744
7549
  return { ok: res.ok, status: res.status, message: res.error?.message };
8745
7550
  }
8746
7551
  };
8747
- var DaemonAssetUploadClient = class {
8748
- constructor(config) {
8749
- this.config = config;
8750
- }
8751
- config;
8752
- async uploadAsset(req) {
8753
- const form = new FormData();
8754
- form.set("workspaceId", req.workspaceId);
8755
- form.set("kind", "file");
8756
- form.set("metadata", JSON.stringify(req.metadata));
8757
- if (req.folderPath !== void 0) form.set("folderPath", req.folderPath);
8758
- form.set("file", new Blob([req.bytes], { type: req.mime ?? "application/octet-stream" }), req.filename);
8759
- const url = `${this.config.cloud_api_base.replace(/\/$/, "")}/api/im/assets`;
8760
- const controller = new AbortController();
8761
- const timer = setTimeout(() => controller.abort(), 6e4);
8762
- let res;
8763
- try {
8764
- res = await fetch(url, {
8765
- method: "POST",
8766
- headers: { Authorization: `Bearer ${this.config.api_key}` },
8767
- body: form,
8768
- signal: controller.signal
8769
- });
8770
- } catch (err) {
8771
- if (err.name === "AbortError") {
8772
- throw new Error("asset upload timed out after 60000ms");
8773
- }
8774
- throw err;
8775
- } finally {
8776
- clearTimeout(timer);
8777
- }
8778
- const text = await res.text();
8779
- let body;
8780
- try {
8781
- body = text ? JSON.parse(text) : void 0;
8782
- } catch {
8783
- body = text;
8784
- }
8785
- if (!res.ok || isCloudErrorEnvelope(body)) {
8786
- throw new Error(`asset upload failed (${res.status}): ${cloudUploadErrorMessage(body)}`);
8787
- }
8788
- const data = unwrapCloudData(body);
8789
- const assetId = stringValue(data, "id");
8790
- const contentHash = stringValue(data, "contentHash");
8791
- if (!assetId || !contentHash) {
8792
- throw new Error("asset upload response missing id/contentHash");
8793
- }
8794
- return { assetId, contentHash };
8795
- }
8796
- };
8797
- function isCloudErrorEnvelope(raw) {
8798
- return Boolean(raw && typeof raw === "object" && "ok" in raw && raw.ok === false);
8799
- }
8800
- function cloudUploadErrorMessage(raw) {
8801
- if (raw && typeof raw === "object") {
8802
- const error = raw.error;
8803
- if (typeof error === "string") return error;
8804
- if (error && typeof error === "object") {
8805
- const message = error.message;
8806
- if (typeof message === "string") return message;
8807
- }
8808
- }
8809
- return typeof raw === "string" ? raw : "request failed";
8810
- }
8811
- function unwrapCloudData(raw) {
8812
- if (raw && typeof raw === "object") {
8813
- const data = raw.data;
8814
- if (data && typeof data === "object" && !Array.isArray(data)) return data;
8815
- }
8816
- return {};
8817
- }
8818
- function stringValue(obj, key) {
8819
- const value = obj[key];
8820
- return typeof value === "string" && value.length > 0 ? value : void 0;
8821
- }
8822
- function resolveUserAssetWorkspaceDir(workspaceId) {
8823
- const override = process.env.PRISMER_ASSET_SYNC_ROOT;
8824
- if (override) return join13(override, workspaceId);
8825
- const home = homedir6();
8826
- const desktop = join13(home, "Desktop");
8827
- const base = existsSync9(desktop) ? desktop : home;
8828
- return join13(base, "Prismer Assets", workspaceId);
8829
- }
8830
7552
  function readTargetDaemonId(payload) {
8831
7553
  if (typeof payload.targetDaemonId === "string" && payload.targetDaemonId.length > 0) {
8832
7554
  return payload.targetDaemonId;
@@ -8877,14 +7599,6 @@ function validateStaticHostedAgent(raw) {
8877
7599
  }
8878
7600
  };
8879
7601
  }
8880
- function parseProfileConfig(raw) {
8881
- try {
8882
- const parsed = JSON.parse(raw);
8883
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
8884
- } catch {
8885
- return {};
8886
- }
8887
- }
8888
7602
  function safeJsonParse(raw) {
8889
7603
  try {
8890
7604
  return JSON.parse(raw);
@@ -8896,7 +7610,7 @@ function safeJsonParse(raw) {
8896
7610
  // src/pair.ts
8897
7611
  import { generateKeyPairSync } from "crypto";
8898
7612
  import { hostname as hostname2 } from "os";
8899
- import { setTimeout as sleep2 } from "timers/promises";
7613
+ import { setTimeout as sleep } from "timers/promises";
8900
7614
  import qrcode from "qrcode";
8901
7615
  async function pair(opts) {
8902
7616
  const paths = opts.paths ?? resolvePaths();
@@ -8965,7 +7679,7 @@ Scan with Lumin to approve, or open: ${offer.qrUrl}
8965
7679
  const pollIntervalMs = opts.pollIntervalMs ?? 5e3;
8966
7680
  for (let i = 0; i < maxAttempts; i += 1) {
8967
7681
  if (i > 0 || !localOnlyMode) {
8968
- await sleep2(pollIntervalMs);
7682
+ await sleep(pollIntervalMs);
8969
7683
  }
8970
7684
  const res = await cloud.request(
8971
7685
  "GET",
@@ -9002,7 +7716,7 @@ function unwrapEnvelope(raw) {
9002
7716
  }
9003
7717
 
9004
7718
  // src/cli/index.ts
9005
- import { Command as Command19 } from "commander";
7719
+ import { Command as Command18 } from "commander";
9006
7720
 
9007
7721
  // src/cli/commands/adapter.ts
9008
7722
  import { spawnSync as spawnSync2 } from "child_process";
@@ -9010,12 +7724,12 @@ import {
9010
7724
  copyFileSync,
9011
7725
  existsSync as existsSync12,
9012
7726
  mkdirSync as mkdirSync8,
9013
- readFileSync as readFileSync10,
9014
- statSync as statSync3,
7727
+ readFileSync as readFileSync9,
7728
+ statSync as statSync2,
9015
7729
  writeFileSync as writeFileSync7
9016
7730
  } from "fs";
9017
- import { homedir as homedir7 } from "os";
9018
- import { dirname as dirname8, join as join15 } from "path";
7731
+ import { homedir as homedir4 } from "os";
7732
+ import { dirname as dirname7, join as join11 } from "path";
9019
7733
  import { Command } from "commander";
9020
7734
  init_util();
9021
7735
  init_ui();
@@ -9028,7 +7742,7 @@ var INSTALL_SPECS = {
9028
7742
  binary: "claude",
9029
7743
  hint: "Set ANTHROPIC_API_KEY in your shell profile, or rely on Claude Code OAuth login. Run `claude login` to sign in.",
9030
7744
  authHints: ["ANTHROPIC_API_KEY or Claude Code OAuth login (`claude login`)"],
9031
- hookTarget: { path: join15(homedir7(), ".claude", "hooks.json"), kind: "json-file" }
7745
+ hookTarget: { path: join11(homedir4(), ".claude", "hooks.json"), kind: "json-file" }
9032
7746
  },
9033
7747
  openclaw: {
9034
7748
  name: "openclaw",
@@ -9037,7 +7751,7 @@ var INSTALL_SPECS = {
9037
7751
  binary: "openclaw",
9038
7752
  hint: "OpenClaw runs as a gateway. Configure ~/.openclaw/openclaw.json and start it with `openclaw gateway` before tasks dispatch.",
9039
7753
  authHints: ["~/.openclaw/openclaw.json gateway.auth.bearerTokens", "Prismer daemon api_key"],
9040
- hookTarget: { path: join15(homedir7(), ".openclaw", "hooks"), kind: "directory" }
7754
+ hookTarget: { path: join11(homedir4(), ".openclaw", "hooks"), kind: "directory" }
9041
7755
  },
9042
7756
  codex: {
9043
7757
  name: "codex",
@@ -9046,7 +7760,7 @@ var INSTALL_SPECS = {
9046
7760
  binary: "codex",
9047
7761
  hint: "Set OPENAI_API_KEY in your shell profile. Verify with `codex --help`.",
9048
7762
  authHints: ["OPENAI_API_KEY or Codex CLI account login"],
9049
- hookTarget: { path: join15(homedir7(), ".codex", "hooks.json"), kind: "json-file" }
7763
+ hookTarget: { path: join11(homedir4(), ".codex", "hooks.json"), kind: "json-file" }
9050
7764
  },
9051
7765
  hermes: {
9052
7766
  name: "hermes",
@@ -9055,7 +7769,7 @@ var INSTALL_SPECS = {
9055
7769
  binary: "hermes",
9056
7770
  hint: "Hermes runs as a long-lived HTTP gateway in your own Python venv. Start with `hermes -p <profile> gateway` after configuring ~/.hermes/.env.",
9057
7771
  authHints: ["~/.hermes/.env API_SERVER_KEY", "Hermes profile provider credentials"],
9058
- hookTarget: { path: join15(homedir7(), ".hermes", "hooks.json"), kind: "json-file" }
7772
+ hookTarget: { path: join11(homedir4(), ".hermes", "hooks.json"), kind: "json-file" }
9059
7773
  }
9060
7774
  };
9061
7775
  function buildAdapterCommand() {
@@ -9143,8 +7857,8 @@ function buildAdapterCommand() {
9143
7857
  });
9144
7858
  return cmd;
9145
7859
  }
9146
- function runInstall(spec, version, dryRun) {
9147
- const { argv0, args } = installCommand(spec, version);
7860
+ function runInstall(spec, version2, dryRun) {
7861
+ const { argv0, args } = installCommand(spec, version2);
9148
7862
  getUI().line(`> ${argv0} ${args.join(" ")}`);
9149
7863
  if (dryRun) {
9150
7864
  getUI().line("(dry-run \u2014 not executing)");
@@ -9271,11 +7985,11 @@ function runHooks(spec, opts) {
9271
7985
  let backup;
9272
7986
  if (planned.kind === "directory") {
9273
7987
  mkdirSync8(planned.path, { recursive: true });
9274
- const markerPath = join15(planned.path, "prismer.json");
7988
+ const markerPath = join11(planned.path, "prismer.json");
9275
7989
  backup = backupIfExists(markerPath);
9276
7990
  writeFileSync7(markerPath, JSON.stringify(prismerHookMarker(spec), null, 2) + "\n", "utf8");
9277
7991
  } else {
9278
- mkdirSync8(dirname8(planned.path), { recursive: true });
7992
+ mkdirSync8(dirname7(planned.path), { recursive: true });
9279
7993
  backup = backupIfExists(planned.path);
9280
7994
  const merged = mergeHookJson(planned.path, spec);
9281
7995
  writeFileSync7(planned.path, JSON.stringify(merged, null, 2) + "\n", "utf8");
@@ -9288,16 +8002,16 @@ function runHooks(spec, opts) {
9288
8002
  hook: inspectHook(spec)
9289
8003
  };
9290
8004
  }
9291
- function installCommand(spec, version) {
9292
- const ref = version === "latest" ? spec.package : `${spec.package}@${version}`;
8005
+ function installCommand(spec, version2) {
8006
+ const ref = version2 === "latest" ? spec.package : `${spec.package}@${version2}`;
9293
8007
  switch (spec.manager) {
9294
8008
  case "npm":
9295
8009
  return { argv0: "npm", args: ["install", "-g", ref] };
9296
8010
  case "pipx":
9297
- const pyRef = version === "latest" ? spec.package : `${spec.package}==${version}`;
8011
+ const pyRef = version2 === "latest" ? spec.package : `${spec.package}==${version2}`;
9298
8012
  return { argv0: "pipx", args: ["install", pyRef] };
9299
8013
  case "pip":
9300
- const pipRef = version === "latest" ? spec.package : `${spec.package}==${version}`;
8014
+ const pipRef = version2 === "latest" ? spec.package : `${spec.package}==${version2}`;
9301
8015
  return { argv0: "pip", args: ["install", "--user", pipRef] };
9302
8016
  }
9303
8017
  }
@@ -9345,7 +8059,7 @@ function inspectAuthEnv(spec) {
9345
8059
  hints: spec.authHints ?? []
9346
8060
  };
9347
8061
  case "hermes": {
9348
- const envFile = join15(homedir7(), ".hermes", ".env");
8062
+ const envFile = join11(homedir4(), ".hermes", ".env");
9349
8063
  return {
9350
8064
  ok: existsSync12(envFile) || Boolean(process.env.HERMES_API_KEY || process.env.API_SERVER_KEY),
9351
8065
  present: [
@@ -9356,7 +8070,7 @@ function inspectAuthEnv(spec) {
9356
8070
  };
9357
8071
  }
9358
8072
  case "openclaw": {
9359
- const cfgFile = join15(homedir7(), ".openclaw", "openclaw.json");
8073
+ const cfgFile = join11(homedir4(), ".openclaw", "openclaw.json");
9360
8074
  return {
9361
8075
  ok: existsSync12(cfgFile) || Boolean(process.env.OPENCLAW_API_KEY),
9362
8076
  present: [
@@ -9376,8 +8090,8 @@ function inspectHook(spec) {
9376
8090
  return { supported: false, markerPresent: false };
9377
8091
  }
9378
8092
  if (spec.name === "openclaw") {
9379
- const jsonPath = join15(homedir7(), ".openclaw", "hooks.json");
9380
- const markerPath = join15(target.path, "prismer.json");
8093
+ const jsonPath = join11(homedir4(), ".openclaw", "hooks.json");
8094
+ const markerPath = join11(target.path, "prismer.json");
9381
8095
  const jsonMarkerPresent = existsSync12(jsonPath) && fileContainsPrismerMarker(jsonPath);
9382
8096
  const dirMarkerPresent = existsSync12(markerPath) && fileContainsPrismerMarker(markerPath);
9383
8097
  return {
@@ -9391,7 +8105,7 @@ function inspectHook(spec) {
9391
8105
  };
9392
8106
  }
9393
8107
  if (target.kind === "directory") {
9394
- const markerPath = join15(target.path, "prismer.json");
8108
+ const markerPath = join11(target.path, "prismer.json");
9395
8109
  return {
9396
8110
  supported: true,
9397
8111
  kind: target.kind,
@@ -9435,21 +8149,21 @@ function prismerHookMarker(spec) {
9435
8149
  installed_at: (/* @__PURE__ */ new Date()).toISOString()
9436
8150
  };
9437
8151
  }
9438
- function mergeHookJson(path9, spec) {
8152
+ function mergeHookJson(path7, spec) {
9439
8153
  const marker = prismerHookMarker(spec);
9440
- if (!existsSync12(path9)) {
8154
+ if (!existsSync12(path7)) {
9441
8155
  return {
9442
8156
  prismer: marker
9443
8157
  };
9444
8158
  }
9445
8159
  let parsed;
9446
8160
  try {
9447
- parsed = JSON.parse(readFileSync10(path9, "utf8"));
8161
+ parsed = JSON.parse(readFileSync9(path7, "utf8"));
9448
8162
  } catch (err) {
9449
- throw new Error(`Cannot parse ${path9} as JSON: ${err.message}`);
8163
+ throw new Error(`Cannot parse ${path7} as JSON: ${err.message}`);
9450
8164
  }
9451
8165
  if (!isRecord(parsed)) {
9452
- throw new Error(`Cannot merge ${path9}: expected a JSON object`);
8166
+ throw new Error(`Cannot merge ${path7}: expected a JSON object`);
9453
8167
  }
9454
8168
  const next = { ...parsed };
9455
8169
  next.prismer = marker;
@@ -9467,18 +8181,18 @@ function mergeHookJson(path9, spec) {
9467
8181
  }
9468
8182
  return next;
9469
8183
  }
9470
- function backupIfExists(path9) {
9471
- if (!existsSync12(path9)) return null;
8184
+ function backupIfExists(path7) {
8185
+ if (!existsSync12(path7)) return null;
9472
8186
  const suffix = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9473
- const backup = `${path9}.bak.${suffix}`;
9474
- const stat = statSync3(path9);
8187
+ const backup = `${path7}.bak.${suffix}`;
8188
+ const stat = statSync2(path7);
9475
8189
  if (stat.isDirectory()) return null;
9476
- copyFileSync(path9, backup);
8190
+ copyFileSync(path7, backup);
9477
8191
  return backup;
9478
8192
  }
9479
- function fileContainsPrismerMarker(path9) {
8193
+ function fileContainsPrismerMarker(path7) {
9480
8194
  try {
9481
- return readFileSync10(path9, "utf8").includes("prismer-daemon-runtime");
8195
+ return readFileSync9(path7, "utf8").includes("prismer-daemon-runtime");
9482
8196
  } catch {
9483
8197
  return false;
9484
8198
  }
@@ -9496,7 +8210,7 @@ function readAdapterConfig() {
9496
8210
  return {};
9497
8211
  }
9498
8212
  }
9499
- function recordInstallInConfig(spec, binPath, version) {
8213
+ function recordInstallInConfig(spec, binPath, version2) {
9500
8214
  const paths = resolvePaths();
9501
8215
  if (!configExists(paths)) {
9502
8216
  process.stderr.write(
@@ -9518,7 +8232,7 @@ function recordInstallInConfig(spec, binPath, version) {
9518
8232
  package_manager: spec.manager,
9519
8233
  package_name: spec.package,
9520
8234
  binary: binPath,
9521
- version,
8235
+ version: version2,
9522
8236
  installed_at: (/* @__PURE__ */ new Date()).toISOString()
9523
8237
  };
9524
8238
  saveConfig({ ...cfg, adapters }, paths);
@@ -9898,7 +8612,7 @@ async function fetchCloudProfiles(cloud, imUserId) {
9898
8612
  async function fetchLocalServer() {
9899
8613
  const paths = resolvePaths();
9900
8614
  const pid = readPidFile(paths);
9901
- if (!pid || !pidAlive2(pid)) return { ok: false, error: "daemon process is not running", agents: [] };
8615
+ if (!pid || !pidAlive(pid)) return { ok: false, error: "daemon process is not running", agents: [] };
9902
8616
  try {
9903
8617
  const [healthRes, agentsRes] = await Promise.all([
9904
8618
  fetch(`${LOCAL_SERVER}/healthz`, { signal: AbortSignal.timeout(1e3) }),
@@ -9922,38 +8636,19 @@ function whichBinary2(bin) {
9922
8636
 
9923
8637
  // src/cli/commands/asset.ts
9924
8638
  import { Command as Command3 } from "commander";
9925
- import { existsSync as existsSync13, lstatSync, readdirSync, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
9926
- import { basename as basename3, dirname as dirname9, join as join16, relative as relative3 } from "path";
8639
+ import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
8640
+ import { basename as basename2 } from "path";
9927
8641
  init_util();
9928
8642
  init_ui();
9929
8643
  function buildAssetCommand() {
9930
8644
  const cmd = new Command3("asset").description("Inspect IM assets");
9931
- 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) => {
9932
- const result = await syncAssets(opts);
9933
- if (opts.json) {
9934
- printJson({ ok: true, data: result });
9935
- return;
9936
- }
9937
- getUI().line(`Synced asset metadata: ${result.metadata.applied} applied (cursor=${result.metadata.cursor})`);
9938
- getUI().line(`Synced workspace files: ${result.workspaceFiles.applied} applied (cursor=${result.workspaceFiles.cursor ?? "null"})`);
9939
- if (opts.bytes) {
9940
- getUI().line(
9941
- `Prefetched bytes: ${result.prefetch.fetched}/${result.prefetch.considered} assets, ${result.prefetch.bytes} bytes`
9942
- );
9943
- if (result.prefetch.failed.length > 0) {
9944
- getUI().line(`Prefetch failures: ${result.prefetch.failed.length}`);
9945
- }
9946
- } else {
9947
- getUI().line("Asset bytes were not prefetched; they are downloaded lazily on first use. Run with --bytes to fill ~/.prismer/cache.");
9948
- }
9949
- }, { code: "asset_sync_failed" }));
9950
8645
  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) => {
9951
8646
  const cloud = mkCloud();
9952
8647
  const query = new URLSearchParams();
9953
8648
  if (opts.workspaceId) query.set("workspaceId", opts.workspaceId);
9954
8649
  if (opts.taskId) query.set("taskId", opts.taskId);
9955
- const path9 = `/api/im/assets${query.toString() ? `?${query.toString()}` : ""}`;
9956
- const res = await cloud.request("GET", path9);
8650
+ const path7 = `/api/im/assets${query.toString() ? `?${query.toString()}` : ""}`;
8651
+ const res = await cloud.request("GET", path7);
9957
8652
  if (!res.ok) exitWithError(`asset list failed (${res.status}): ${res.error?.message ?? "request failed"}`, { code: res.error?.code ?? "asset_list_failed" });
9958
8653
  const body = res.data;
9959
8654
  if (isErrorEnvelope(body)) {
@@ -9965,20 +8660,13 @@ function buildAssetCommand() {
9965
8660
  }
9966
8661
  printAssetList(body);
9967
8662
  }, { code: "asset_list_failed" }));
9968
- 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) => {
9969
- const body = await uploadAssetPath(inputPath, opts);
8663
+ 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) => {
8664
+ const body = await uploadAsset(file, opts);
9970
8665
  if (opts.json) {
9971
8666
  printJson(body ?? null);
9972
8667
  return;
9973
8668
  }
9974
- if (isUploadManyResult(body)) {
9975
- getUI().line(`Uploaded ${body.data.count} assets`);
9976
- for (const item of body.data.items) {
9977
- printAssetDetail("Uploaded asset", item.file, item.response);
9978
- }
9979
- } else {
9980
- printAssetDetail("Uploaded asset", inputPath, body);
9981
- }
8669
+ printAssetDetail("Uploaded asset", file, body);
9982
8670
  }, { code: "asset_upload_failed" }));
9983
8671
  cmd.command("download <assetId>").description("Download asset bytes").requiredOption("--out <path>", "Destination file path").action(runAction(async (assetId, opts) => {
9984
8672
  await downloadAsset(assetId, opts.out);
@@ -9998,12 +8686,12 @@ function buildAssetCommand() {
9998
8686
  }
9999
8687
  printAssetDetail("Asset", assetId, body);
10000
8688
  }, { code: "asset_get_failed" }));
10001
- 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) => {
8689
+ 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) => {
10002
8690
  const cloud = mkCloud();
10003
8691
  const query = new URLSearchParams();
10004
8692
  if (opts.workspaceId) query.set("wsId", opts.workspaceId);
10005
- const path9 = `/api/im/assets/by-hash/${encodeURIComponent(sha2564)}${query.toString() ? `?${query.toString()}` : ""}`;
10006
- const res = await cloud.request("GET", path9);
8693
+ const path7 = `/api/im/assets/by-hash/${encodeURIComponent(sha2562)}${query.toString() ? `?${query.toString()}` : ""}`;
8694
+ const res = await cloud.request("GET", path7);
10007
8695
  if (!res.ok) exitWithError(`asset by-hash failed (${res.status}): ${res.error?.message ?? "request failed"}`, { code: res.error?.code ?? "asset_by_hash_failed" });
10008
8696
  const body = res.data;
10009
8697
  if (isErrorEnvelope(body)) {
@@ -10013,7 +8701,7 @@ function buildAssetCommand() {
10013
8701
  printJson(body ?? null);
10014
8702
  return;
10015
8703
  }
10016
- printAssetDetail("Asset hash", sha2564, body);
8704
+ printAssetDetail("Asset hash", sha2562, body);
10017
8705
  }, { code: "asset_by_hash_failed" }));
10018
8706
  return cmd;
10019
8707
  }
@@ -10021,102 +8709,10 @@ function mkCloud() {
10021
8709
  const cfg = loadConfig(resolvePaths());
10022
8710
  return new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
10023
8711
  }
10024
- async function syncAssets(opts) {
10025
- const paths = resolvePaths();
10026
- const cfg = loadConfig(paths);
10027
- const cloud = new CloudClient({ baseUrl: cfg.cloud_api_base, apiKey: cfg.api_key });
10028
- const db = openLocalDb(paths.localDb);
10029
- try {
10030
- const stateDir = `${paths.root}/${opts.workspaceId}`;
10031
- const metadataIndex = new AssetMetadataIndex({
10032
- db,
10033
- cloud,
10034
- workspaceId: opts.workspaceId,
10035
- workspaceStateDir: stateDir
10036
- });
10037
- const mirror = new WorkspaceMirror({
10038
- db,
10039
- cloud,
10040
- workspaceId: opts.workspaceId,
10041
- workspaceStateDir: stateDir
10042
- });
10043
- const [metadata, workspaceFiles] = await Promise.all([
10044
- metadataIndex.pullDelta(),
10045
- mirror.pullDelta()
10046
- ]);
10047
- const prefetch = opts.bytes ? await prefetchAssetBytes({
10048
- db,
10049
- cloud,
10050
- workspaceId: opts.workspaceId,
10051
- cacheDir: paths.cacheDir,
10052
- maxBytes: cfg.cache?.max_bytes,
10053
- limit: opts.limit
10054
- }) : { considered: 0, fetched: 0, bytes: 0, failed: [] };
10055
- return { workspaceId: opts.workspaceId, metadata, workspaceFiles, prefetch };
10056
- } finally {
10057
- db.close();
10058
- }
10059
- }
10060
- async function prefetchAssetBytes(opts) {
10061
- const limitClause = opts.limit ? " LIMIT ?" : "";
10062
- const params = [opts.workspaceId];
10063
- if (opts.limit) params.push(opts.limit);
10064
- const rows = opts.db.prepare(
10065
- `SELECT asset_id, content_hash, mime, size_bytes
10066
- FROM asset_metadata_index
10067
- WHERE workspace_id = ?
10068
- ORDER BY asset_index_seq DESC${limitClause}`
10069
- ).all(...params);
10070
- const cache = new AssetCache({
10071
- db: opts.db,
10072
- cloud: opts.cloud,
10073
- cacheDir: opts.cacheDir,
10074
- maxBytes: opts.maxBytes
10075
- });
10076
- let fetched = 0;
10077
- let bytes = 0;
10078
- const failed = [];
10079
- for (const row of rows) {
10080
- try {
10081
- const cached = await cache.getOrFetch(row.content_hash, {
10082
- workspaceIdHint: opts.workspaceId,
10083
- assetId: row.asset_id
10084
- });
10085
- fetched += 1;
10086
- bytes += cached.sizeBytes;
10087
- } catch (err) {
10088
- failed.push({
10089
- assetId: row.asset_id,
10090
- contentHash: row.content_hash,
10091
- error: err.message
10092
- });
10093
- }
10094
- }
10095
- return { considered: rows.length, fetched, bytes, failed };
10096
- }
10097
- async function uploadAssetPath(inputPath, opts) {
10098
- if (!existsSync13(inputPath)) exitWithError(`path not found: ${inputPath}`);
10099
- const stat = lstatSync(inputPath);
10100
- if (stat.isSymbolicLink()) exitWithError(`refusing to upload symlink: ${inputPath}`);
10101
- if (stat.isFile()) {
10102
- return uploadAsset(inputPath, { ...opts, folderPath: normalizeFolderPath(opts.folderPath) });
10103
- }
10104
- if (!stat.isDirectory()) exitWithError(`path is not a file or directory: ${inputPath}`);
10105
- const files = listFilesRecursive(inputPath);
10106
- if (files.length === 0) exitWithError(`directory has no uploadable files: ${inputPath}`);
10107
- const items = [];
10108
- for (const file of files) {
10109
- const relDir = dirname9(relative3(inputPath, file));
10110
- const folderPath = combineFolderPath(opts.folderPath, relDir === "." ? "" : relDir);
10111
- const response = await uploadAsset(file, { ...opts, folderPath });
10112
- items.push({ file, folderPath: folderPath ?? null, response });
10113
- }
10114
- return { ok: true, data: { count: items.length, items } };
10115
- }
10116
8712
  async function uploadAsset(file, opts) {
10117
8713
  if (!existsSync13(file)) exitWithError(`file not found: ${file}`);
10118
8714
  const cfg = loadConfig(resolvePaths());
10119
- const bytes = readFileSync11(file);
8715
+ const bytes = readFileSync10(file);
10120
8716
  const metadata = parseMetadata(opts.metadata);
10121
8717
  if (opts.taskId && metadata.taskId === void 0) metadata.taskId = opts.taskId;
10122
8718
  if (opts.containerId && metadata.containerId === void 0) metadata.containerId = opts.containerId;
@@ -10125,9 +8721,8 @@ async function uploadAsset(file, opts) {
10125
8721
  form.set("kind", opts.kind);
10126
8722
  if (opts.agentId) form.set("sourceAgentImUserId", opts.agentId);
10127
8723
  if (opts.taskId) form.set("sourceTaskId", opts.taskId);
10128
- if (opts.folderPath !== void 0) form.set("folderPath", opts.folderPath);
10129
8724
  if (Object.keys(metadata).length > 0) form.set("metadata", JSON.stringify(metadata));
10130
- form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), basename3(file));
8725
+ form.set("file", new Blob([bytes], { type: opts.mime ?? "application/octet-stream" }), basename2(file));
10131
8726
  const url = `${cfg.cloud_api_base.replace(/\/$/, "")}/api/im/assets`;
10132
8727
  let res;
10133
8728
  try {
@@ -10145,37 +8740,6 @@ async function uploadAsset(file, opts) {
10145
8740
  }
10146
8741
  return body;
10147
8742
  }
10148
- function listFilesRecursive(root) {
10149
- const out = [];
10150
- for (const name of readdirSync(root)) {
10151
- if (name.startsWith(".")) continue;
10152
- const full = join16(root, name);
10153
- const stat = lstatSync(full);
10154
- if (stat.isSymbolicLink()) continue;
10155
- if (stat.isDirectory()) {
10156
- out.push(...listFilesRecursive(full));
10157
- } else if (stat.isFile()) {
10158
- out.push(full);
10159
- }
10160
- }
10161
- return out.sort();
10162
- }
10163
- function combineFolderPath(prefix, relDir) {
10164
- const normalizedPrefix = normalizeFolderPath(prefix);
10165
- const normalizedRel = normalizeFolderPath(relDir);
10166
- if (normalizedPrefix && normalizedRel) return `${normalizedPrefix}/${normalizedRel}`;
10167
- return normalizedPrefix ?? normalizedRel;
10168
- }
10169
- function normalizeFolderPath(value) {
10170
- if (value === void 0) return void 0;
10171
- const normalized = value.trim().replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
10172
- return normalized.length > 0 ? normalized : "";
10173
- }
10174
- function isUploadManyResult(raw) {
10175
- return Boolean(
10176
- raw && typeof raw === "object" && raw.ok === true && raw.data && Array.isArray(raw.data.items)
10177
- );
10178
- }
10179
8743
  async function downloadAsset(assetId, outPath) {
10180
8744
  const cloud = mkCloud();
10181
8745
  const res = await cloud.fetchRaw(`/api/im/assets/${encodeURIComponent(assetId)}`);
@@ -10269,13 +8833,6 @@ function stringField(obj, key) {
10269
8833
  const value = obj?.[key];
10270
8834
  return typeof value === "string" && value.length > 0 ? value : void 0;
10271
8835
  }
10272
- function parsePositiveInt(raw) {
10273
- const n = Number(raw);
10274
- if (!Number.isInteger(n) || n <= 0) {
10275
- throw new Error("--limit must be a positive integer");
10276
- }
10277
- return n;
10278
- }
10279
8836
 
10280
8837
  // src/cli/commands/banner.ts
10281
8838
  init_util();
@@ -10331,7 +8888,7 @@ ${name} (${role})`);
10331
8888
  getUI().line(`Group created ${data.groupId ?? "(unknown)"}
10332
8889
  ${data.title ?? opts.name} (${memberCount} members)`);
10333
8890
  });
10334
- 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) => {
8891
+ 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) => {
10335
8892
  const data = await requestOrExit(
10336
8893
  "GET",
10337
8894
  withQuery(`/api/im/groups/${encodeURIComponent(groupId)}/messages`, {
@@ -10369,7 +8926,7 @@ ${data.title ?? opts.name} (${memberCount} members)`);
10369
8926
  getUI().line(`Removed ${memberId} from group ${groupId}`);
10370
8927
  });
10371
8928
  cmd.addCommand(group);
10372
- 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) => {
8929
+ 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) => {
10373
8930
  const data = await requestOrExit(
10374
8931
  "GET",
10375
8932
  withQuery(`/api/im/messages/${encodeURIComponent(conversationId)}`, {
@@ -10385,15 +8942,15 @@ ${data.title ?? opts.name} (${memberCount} members)`);
10385
8942
  });
10386
8943
  return cmd;
10387
8944
  }
10388
- async function requestOrExit(method, path9, body) {
8945
+ async function requestOrExit(method, path7, body) {
10389
8946
  let res;
10390
8947
  try {
10391
- res = await mkCloud2().request(method, path9, body === void 0 ? void 0 : { body });
8948
+ res = await mkCloud2().request(method, path7, body === void 0 ? void 0 : { body });
10392
8949
  } catch (err) {
10393
8950
  exitWithError(sanitizeError(err.message));
10394
8951
  }
10395
8952
  if (!res.ok) {
10396
- exitWithError(`${method} ${path9} failed (${res.status}): ${sanitizeError(res.error?.message ?? "unknown")}`);
8953
+ exitWithError(`${method} ${path7} failed (${res.status}): ${sanitizeError(res.error?.message ?? "unknown")}`);
10397
8954
  }
10398
8955
  return unwrapEnvelope2(res.data);
10399
8956
  }
@@ -10425,20 +8982,20 @@ function unwrapEnvelope2(raw) {
10425
8982
  function parseCsv(value) {
10426
8983
  return value.split(",").map((v) => v.trim()).filter(Boolean);
10427
8984
  }
10428
- function parsePositiveInt2(value) {
8985
+ function parsePositiveInt(value) {
10429
8986
  const parsed = Number.parseInt(value, 10);
10430
8987
  if (!Number.isInteger(parsed) || parsed < 1) {
10431
8988
  throw new Error("expected a positive integer");
10432
8989
  }
10433
8990
  return parsed;
10434
8991
  }
10435
- function withQuery(path9, params) {
8992
+ function withQuery(path7, params) {
10436
8993
  const query = new URLSearchParams();
10437
8994
  for (const [key, value] of Object.entries(params)) {
10438
8995
  if (value !== void 0) query.set(key, String(value));
10439
8996
  }
10440
8997
  const suffix = query.toString();
10441
- return suffix ? `${path9}?${suffix}` : path9;
8998
+ return suffix ? `${path7}?${suffix}` : path7;
10442
8999
  }
10443
9000
  function printMessageSummary(label, data) {
10444
9001
  const msg = data.message ?? {};
@@ -10565,7 +9122,7 @@ init_util();
10565
9122
  init_ui();
10566
9123
  function buildCookbookCommand() {
10567
9124
  const cmd = new Command7("cookbook").description("Run CLI-only 54release MVP regression suites");
10568
- 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) => {
9125
+ 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) => {
10569
9126
  const suites = parseSuites(opts.suite);
10570
9127
  const cloud = mkCloud3(opts.timeoutMs);
10571
9128
  const checks = [];
@@ -10607,8 +9164,8 @@ async function runStatusSuite(cloud) {
10607
9164
  await check("status", "cloud health", "GET", "/api/health"),
10608
9165
  await check("status", "current IM identity", "GET", "/api/im/me", void 0, cloud)
10609
9166
  ];
10610
- async function check(suite, name, method, path9, body, client = cloud) {
10611
- return requestCheck(client, suite, name, method, path9, body);
9167
+ async function check(suite, name, method, path7, body, client = cloud) {
9168
+ return requestCheck(client, suite, name, method, path7, body);
10612
9169
  }
10613
9170
  }
10614
9171
  async function runIMSuite(cloud) {
@@ -10682,27 +9239,27 @@ async function runSandboxSuite(cloud, opts) {
10682
9239
  }
10683
9240
  return checks;
10684
9241
  }
10685
- async function requestCheck(cloud, suite, name, method, path9, body) {
9242
+ async function requestCheck(cloud, suite, name, method, path7, body) {
10686
9243
  let res;
10687
9244
  try {
10688
- res = await cloud.request(method, path9, body === void 0 ? void 0 : { body });
9245
+ res = await cloud.request(method, path7, body === void 0 ? void 0 : { body });
10689
9246
  } catch (err) {
10690
- return { suite, name, status: "fail", endpoint: path9, detail: sanitize(err.message) };
9247
+ return { suite, name, status: "fail", endpoint: path7, detail: sanitize(err.message) };
10691
9248
  }
10692
9249
  if (!res.ok) {
10693
9250
  return {
10694
9251
  suite,
10695
9252
  name,
10696
9253
  status: "fail",
10697
- endpoint: path9,
9254
+ endpoint: path7,
10698
9255
  detail: sanitize(`${res.status}: ${res.error?.message ?? "request failed"}`)
10699
9256
  };
10700
9257
  }
10701
9258
  const data = unwrap(res.data);
10702
9259
  if (isApiError(res.data)) {
10703
- return { suite, name, status: "fail", endpoint: path9, detail: sanitize(apiErrorMessage(res.data)), data: res.data };
9260
+ return { suite, name, status: "fail", endpoint: path7, detail: sanitize(apiErrorMessage(res.data)), data: res.data };
10704
9261
  }
10705
- return { suite, name, status: "pass", endpoint: path9, detail: summarizeData(data), data };
9262
+ return { suite, name, status: "pass", endpoint: path7, detail: summarizeData(data), data };
10706
9263
  }
10707
9264
  async function pollTask(cloud, taskId, timeoutMs) {
10708
9265
  const started = Date.now();
@@ -10809,7 +9366,7 @@ function taskFrom(data) {
10809
9366
  function sanitize(message) {
10810
9367
  return message.replace(/sk-prismer-[A-Za-z0-9_-]+/g, "sk-prismer-***");
10811
9368
  }
10812
- function parsePositiveInt3(value) {
9369
+ function parsePositiveInt2(value) {
10813
9370
  const parsed = Number.parseInt(value, 10);
10814
9371
  if (!Number.isFinite(parsed) || parsed <= 0) {
10815
9372
  throw new Error("expected a positive integer");
@@ -10820,9 +9377,9 @@ function parsePositiveInt3(value) {
10820
9377
  // src/cli/commands/daemon.ts
10821
9378
  import { Command as Command8 } from "commander";
10822
9379
  import { spawn as spawn5 } from "child_process";
10823
- import { createReadStream, existsSync as existsSync14, mkdirSync as mkdirSync9, openSync as openSync2, statSync as statSync4 } from "fs";
10824
- import { setTimeout as sleep3 } from "timers/promises";
10825
- import { join as join17 } from "path";
9380
+ import { createReadStream, existsSync as existsSync14, mkdirSync as mkdirSync9, openSync, statSync as statSync3 } from "fs";
9381
+ import { setTimeout as sleep2 } from "timers/promises";
9382
+ import { join as join12 } from "path";
10826
9383
  init_util();
10827
9384
  init_ui();
10828
9385
  function buildDaemonCommand() {
@@ -10840,7 +9397,7 @@ function buildDaemonCommand() {
10840
9397
  async function runForeground(opts) {
10841
9398
  const paths = resolvePaths();
10842
9399
  const existingPid = readPidFile(paths);
10843
- if (existingPid && pidAlive2(existingPid)) {
9400
+ if (existingPid && pidAlive(existingPid)) {
10844
9401
  exitWithError(`Daemon already running (pid ${existingPid}). Use \`prismer daemon stop\` first.`);
10845
9402
  }
10846
9403
  writePidFile(paths, process.pid);
@@ -10864,7 +9421,7 @@ function buildDaemonCommand() {
10864
9421
  while (!configExists(paths)) {
10865
9422
  if (stopRequested) return;
10866
9423
  getUI().info(`[daemon] waiting for ${paths.configFile} \u2014 run \`prismer setup\` to create it`);
10867
- await sleep3(5e3);
9424
+ await sleep2(5e3);
10868
9425
  }
10869
9426
  runner = new Runner({
10870
9427
  startLocalServer: opts.localServer !== false,
@@ -10893,12 +9450,12 @@ function buildDaemonCommand() {
10893
9450
  async function startBackground(opts) {
10894
9451
  const paths = resolvePaths();
10895
9452
  const existingPid = readPidFile(paths);
10896
- if (existingPid && pidAlive2(existingPid)) {
9453
+ if (existingPid && pidAlive(existingPid)) {
10897
9454
  exitWithError(`Daemon already running (pid ${existingPid}). Use \`prismer daemon stop\` first.`);
10898
9455
  }
10899
9456
  if (!existsSync14(paths.logsDir)) mkdirSync9(paths.logsDir, { recursive: true });
10900
- const logFile = join17(paths.logsDir, "daemon.log");
10901
- const fd = openSync2(logFile, "a");
9457
+ const logFile = join12(paths.logsDir, "daemon.log");
9458
+ const fd = openSync(logFile, "a");
10902
9459
  const args = [process.argv[1], "daemon", "run"];
10903
9460
  if (opts.port) args.push("--port", String(opts.port));
10904
9461
  if (opts.localServer === false) args.push("--no-local-server");
@@ -10910,9 +9467,9 @@ function buildDaemonCommand() {
10910
9467
  child.unref();
10911
9468
  const deadline = Date.now() + 5e3;
10912
9469
  while (Date.now() < deadline) {
10913
- await sleep3(200);
9470
+ await sleep2(200);
10914
9471
  const pid = readPidFile(paths);
10915
- if (pid && pidAlive2(pid)) {
9472
+ if (pid && pidAlive(pid)) {
10916
9473
  if (opts.json) printJson({ ok: true, pid, logFile });
10917
9474
  else getUI().ok("Daemon started", `pid ${pid} \xB7 logs ${logFile}`);
10918
9475
  return;
@@ -10927,7 +9484,7 @@ function buildDaemonCommand() {
10927
9484
  const paths = resolvePaths();
10928
9485
  const pid = readPidFile(paths);
10929
9486
  if (!pid) exitWithError("No daemon.pid found \u2014 daemon does not appear to be running.");
10930
- if (!pidAlive2(pid)) {
9487
+ if (!pidAlive(pid)) {
10931
9488
  clearPidFile(paths);
10932
9489
  getUI().warn("Daemon was not alive", `pid ${pid}; cleared stale pid file`);
10933
9490
  return;
@@ -10939,12 +9496,12 @@ function buildDaemonCommand() {
10939
9496
  }
10940
9497
  const deadline = Date.now() + (opts.timeout ?? 5e3);
10941
9498
  while (Date.now() < deadline) {
10942
- if (!pidAlive2(pid)) {
9499
+ if (!pidAlive(pid)) {
10943
9500
  clearPidFile(paths);
10944
9501
  getUI().ok("Daemon stopped", `pid ${pid}`);
10945
9502
  return;
10946
9503
  }
10947
- await sleep3(200);
9504
+ await sleep2(200);
10948
9505
  }
10949
9506
  process.stderr.write(`Daemon did not exit within timeout; pid ${pid} may still be alive.
10950
9507
  `);
@@ -10964,7 +9521,7 @@ function buildDaemonCommand() {
10964
9521
  cmd.command("status").description("Show daemon process + WS state").option("--json", "Output JSON (default)").action(async () => {
10965
9522
  const paths = resolvePaths();
10966
9523
  const pid = readPidFile(paths);
10967
- const alive = pid ? pidAlive2(pid) : false;
9524
+ const alive = pid ? pidAlive(pid) : false;
10968
9525
  if (!pid || !alive) {
10969
9526
  printJson({ running: false, pid: pid ?? null, paths: { config: paths.configFile } });
10970
9527
  return;
@@ -10981,7 +9538,7 @@ function buildDaemonCommand() {
10981
9538
  });
10982
9539
  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) => {
10983
9540
  const paths = resolvePaths();
10984
- const logFile = join17(paths.logsDir, "daemon.log");
9541
+ const logFile = join12(paths.logsDir, "daemon.log");
10985
9542
  if (!existsSync14(logFile)) {
10986
9543
  exitWithError(`No daemon log found at ${logFile}. Start the daemon with \`prismer daemon start\`.`);
10987
9544
  }
@@ -10992,9 +9549,9 @@ function buildDaemonCommand() {
10992
9549
  });
10993
9550
  return cmd;
10994
9551
  }
10995
- async function tailFromEnd(path9, lines) {
9552
+ async function tailFromEnd(path7, lines) {
10996
9553
  const { open } = await import("fs/promises");
10997
- const handle = await open(path9, "r");
9554
+ const handle = await open(path7, "r");
10998
9555
  try {
10999
9556
  const stat = await handle.stat();
11000
9557
  let size = stat.size;
@@ -11022,14 +9579,14 @@ async function tailFromEnd(path9, lines) {
11022
9579
  await handle.close();
11023
9580
  }
11024
9581
  }
11025
- async function followFile(path9) {
11026
- let offset = statSync4(path9).size;
9582
+ async function followFile(path7) {
9583
+ let offset = statSync3(path7).size;
11027
9584
  for (; ; ) {
11028
- await sleep3(1e3);
11029
- const size = statSync4(path9).size;
9585
+ await sleep2(1e3);
9586
+ const size = statSync3(path7).size;
11030
9587
  if (size < offset) offset = 0;
11031
9588
  if (size === offset) continue;
11032
- const stream = createReadStream(path9, { start: offset, end: size - 1, encoding: "utf8" });
9589
+ const stream = createReadStream(path7, { start: offset, end: size - 1, encoding: "utf8" });
11033
9590
  for await (const chunk of stream) process.stdout.write(chunk);
11034
9591
  offset = size;
11035
9592
  }
@@ -11039,8 +9596,8 @@ async function followFile(path9) {
11039
9596
  init_util();
11040
9597
  import { Command as Command9 } from "commander";
11041
9598
  import { createReadStream as createReadStream2, existsSync as existsSync15 } from "fs";
11042
- import { homedir as homedir8 } from "os";
11043
- import { join as join18 } from "path";
9599
+ import { homedir as homedir5 } from "os";
9600
+ import { join as join13 } from "path";
11044
9601
  import { createInterface } from "readline";
11045
9602
  var DEFAULT_LIMIT2 = 50;
11046
9603
  function buildEventsCommand() {
@@ -11078,10 +9635,10 @@ function buildEventsStatsCommand() {
11078
9635
  });
11079
9636
  }
11080
9637
  function addEventOptions(cmd) {
11081
- 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)");
9638
+ 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)");
11082
9639
  }
11083
9640
  function eventsPath() {
11084
- return join18(process.env.PRISMER_HOME ?? join18(homedir8(), ".prismer"), "para", "events.jsonl");
9641
+ return join13(process.env.PRISMER_HOME ?? join13(homedir5(), ".prismer"), "para", "events.jsonl");
11085
9642
  }
11086
9643
  function unavailable(file) {
11087
9644
  return {
@@ -11159,13 +9716,13 @@ function normalizeFilters(opts) {
11159
9716
  function cleanFilters(filters) {
11160
9717
  return Object.fromEntries(Object.entries(filters).filter(([, value]) => value !== void 0));
11161
9718
  }
11162
- function parsePositiveInt4(v) {
9719
+ function parsePositiveInt3(v) {
11163
9720
  const n = Number.parseInt(v, 10);
11164
9721
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_LIMIT2;
11165
9722
  }
11166
9723
 
11167
9724
  // src/cli/commands/memory.ts
11168
- import Database5 from "better-sqlite3";
9725
+ import Database4 from "better-sqlite3";
11169
9726
  import { Command as Command10 } from "commander";
11170
9727
  import { existsSync as existsSync16 } from "fs";
11171
9728
  init_util();
@@ -11193,7 +9750,7 @@ function buildMemoryCommand() {
11193
9750
  }));
11194
9751
  process.exitCode = 1;
11195
9752
  });
11196
- 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) => {
9753
+ 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) => {
11197
9754
  const limit = clampLimit(opts.limit);
11198
9755
  const daemonPaths = [];
11199
9756
  if (opts.workspaceId) {
@@ -11212,7 +9769,7 @@ function buildMemoryCommand() {
11212
9769
  printJson(unavailable2("list", snapshot, { items: snapshot.items, limit }));
11213
9770
  process.exitCode = 1;
11214
9771
  });
11215
- 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) => {
9772
+ 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) => {
11216
9773
  const query = opts.query ?? argQuery ?? "";
11217
9774
  const limit = clampLimit(opts.limit);
11218
9775
  const encoded = encodeURIComponent(query);
@@ -11256,9 +9813,9 @@ function buildMemoryCommand() {
11256
9813
  }
11257
9814
  async function tryDaemon(methods, paths) {
11258
9815
  for (const method of methods) {
11259
- for (const path9 of paths) {
9816
+ for (const path7 of paths) {
11260
9817
  try {
11261
- const res = await fetch(`${LOCAL_BASE}${path9}`, { method, signal: AbortSignal.timeout(1500) });
9818
+ const res = await fetch(`${LOCAL_BASE}${path7}`, { method, signal: AbortSignal.timeout(1500) });
11262
9819
  if (res.status === 404) continue;
11263
9820
  const body = await readJson4(res);
11264
9821
  if (!res.ok) {
@@ -11266,12 +9823,12 @@ async function tryDaemon(methods, paths) {
11266
9823
  ok: false,
11267
9824
  error: {
11268
9825
  code: `daemon_http_${res.status}`,
11269
- message: messageFromBody(body) ?? `Daemon endpoint ${method} ${path9} returned HTTP ${res.status}`
9826
+ message: messageFromBody(body) ?? `Daemon endpoint ${method} ${path7} returned HTTP ${res.status}`
11270
9827
  },
11271
- checks: { daemon: { url: LOCAL_BASE, endpoint: path9, reachable: true } }
9828
+ checks: { daemon: { url: LOCAL_BASE, endpoint: path7, reachable: true } }
11272
9829
  };
11273
9830
  }
11274
- return normalizeDaemonBody(body, { method, path: path9 });
9831
+ return normalizeDaemonBody(body, { method, path: path7 });
11275
9832
  } catch {
11276
9833
  continue;
11277
9834
  }
@@ -11329,7 +9886,7 @@ function readCacheSnapshot(limit) {
11329
9886
  if (!empty.dbExists) return empty;
11330
9887
  let db;
11331
9888
  try {
11332
- db = new Database5(paths.localDb, { readonly: true, fileMustExist: true });
9889
+ db = new Database4(paths.localDb, { readonly: true, fileMustExist: true });
11333
9890
  const hasAssets = tableExists(db, "cached_assets");
11334
9891
  const hasFiles = tableExists(db, "workspace_files_mirror");
11335
9892
  const assets = hasAssets ? db.prepare("SELECT COUNT(*) AS count, COALESCE(SUM(size_bytes), 0) AS sizeBytes FROM cached_assets").get() : { count: 0, sizeBytes: 0 };
@@ -11418,7 +9975,7 @@ function searchableText(item) {
11418
9975
  item.localPath
11419
9976
  ].filter(Boolean).join(" ");
11420
9977
  }
11421
- function parsePositiveInt5(v) {
9978
+ function parsePositiveInt4(v) {
11422
9979
  const n = Number.parseInt(v, 10);
11423
9980
  return Number.isFinite(n) && n > 0 ? n : 20;
11424
9981
  }
@@ -11483,9 +10040,9 @@ function buildPairCommand() {
11483
10040
 
11484
10041
  // src/cli/commands/profile.ts
11485
10042
  import { Command as Command12 } from "commander";
11486
- import { existsSync as existsSync17, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
10043
+ import { existsSync as existsSync17, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
11487
10044
  import { tmpdir } from "os";
11488
- import { join as join19 } from "path";
10045
+ import { join as join14 } from "path";
11489
10046
  import { spawnSync as spawnSync4 } from "child_process";
11490
10047
  init_util();
11491
10048
  function buildProfileCommand() {
@@ -11533,12 +10090,12 @@ function buildProfileCommand() {
11533
10090
  const profile = await cloud.get(
11534
10091
  `/api/im/agent_profiles/${encodeURIComponent(profileId)}`
11535
10092
  );
11536
- const tmpFile = join19(tmpdir(), `prismer-profile-${profileId}.json`);
10093
+ const tmpFile = join14(tmpdir(), `prismer-profile-${profileId}.json`);
11537
10094
  writeFileSync9(tmpFile, JSON.stringify(profile.config, null, 2), "utf8");
11538
10095
  const editor = process.env.EDITOR || "vi";
11539
10096
  const ed = spawnSync4(editor, [tmpFile], { stdio: "inherit" });
11540
10097
  if (ed.status !== 0) exitWithError(`editor exited ${ed.status}`, { code: "editor_failed" });
11541
- const newConfig = JSON.parse(readFileSync12(tmpFile, "utf8"));
10098
+ const newConfig = JSON.parse(readFileSync11(tmpFile, "utf8"));
11542
10099
  const res = await cloud.request("PATCH", `/api/im/agent_profiles/${encodeURIComponent(profileId)}`, {
11543
10100
  body: { config: newConfig, version: profile.version }
11544
10101
  });
@@ -11559,9 +10116,9 @@ function mkCloud4() {
11559
10116
  }
11560
10117
  function readJsonArg(arg) {
11561
10118
  if (arg.startsWith("@")) {
11562
- const path9 = arg.slice(1);
11563
- if (!existsSync17(path9)) throw new Error(`File not found: ${path9}`);
11564
- return JSON.parse(readFileSync12(path9, "utf8"));
10119
+ const path7 = arg.slice(1);
10120
+ if (!existsSync17(path7)) throw new Error(`File not found: ${path7}`);
10121
+ return JSON.parse(readFileSync11(path7, "utf8"));
11565
10122
  }
11566
10123
  return JSON.parse(arg);
11567
10124
  }
@@ -11572,200 +10129,13 @@ async function resolveDefaultWorkspaceId(cloud) {
11572
10129
  return def.id;
11573
10130
  }
11574
10131
 
11575
- // src/cli/commands/reset.ts
11576
- import { Command as Command13 } from "commander";
11577
- import { execFileSync as execFileSync2 } from "child_process";
11578
- import { existsSync as existsSync18, mkdirSync as mkdirSync10, rmSync, renameSync as renameSync2 } from "fs";
11579
- import { homedir as homedir9 } from "os";
11580
- import { dirname as dirname10, join as join20 } from "path";
11581
- import { setTimeout as sleep4 } from "timers/promises";
11582
- init_util();
11583
- init_ui();
11584
- function buildResetCommand() {
11585
- return new Command13("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) => {
11586
- const paths = resolvePaths();
11587
- const pid = readPidFile(paths);
11588
- const runningPid = pid && pidAlive2(pid) ? pid : null;
11589
- const archivePath = opts.wipe ? null : `${paths.root}.reset.${timestamp()}`;
11590
- const assetSyncRoot = resolveAssetSyncRoot();
11591
- const assetSyncArchivePath = opts.wipe ? null : `${assetSyncRoot}.reset.${timestamp()}`;
11592
- const hermesHome = resolveHermesHome();
11593
- const hermesArchivePath = opts.wipe ? null : `${hermesHome}.reset.${timestamp()}`;
11594
- const hermesGatewayPids = findHermesGatewayPids2();
11595
- const plan = {
11596
- root: paths.root,
11597
- exists: existsSync18(paths.root),
11598
- runningPid,
11599
- mode: opts.wipe ? "wipe" : "archive",
11600
- archivePath,
11601
- assetSyncRoot,
11602
- assetSyncExists: existsSync18(assetSyncRoot),
11603
- assetSyncArchivePath,
11604
- hermesHome,
11605
- hermesExists: existsSync18(hermesHome),
11606
- hermesArchivePath,
11607
- hermesGatewayPids,
11608
- willStopDaemon: runningPid !== null,
11609
- nextStep: "prismer setup"
11610
- };
11611
- if (opts.check) {
11612
- printPlan(plan, opts.json);
11613
- return;
11614
- }
11615
- if (!opts.yes) {
11616
- exitWithError("Refusing to reset without --yes. Run `prismer reset --check` to preview.", {
11617
- code: "confirmation_required"
11618
- });
11619
- }
11620
- if (runningPid !== null) {
11621
- await stopDaemon(runningPid, opts.timeout ?? 5e3);
11622
- clearPidFile(paths);
11623
- } else if (pid) {
11624
- clearPidFile(paths);
11625
- }
11626
- for (const gatewayPid of hermesGatewayPids) {
11627
- await stopProcess(gatewayPid, opts.timeout ?? 5e3, "Hermes gateway");
11628
- }
11629
- if (existsSync18(paths.root)) {
11630
- if (opts.wipe) {
11631
- rmSync(paths.root, { recursive: true, force: true });
11632
- } else {
11633
- if (!archivePath) throw new Error("internal: archivePath missing");
11634
- if (!existsSync18(dirname10(archivePath))) {
11635
- mkdirSync10(dirname10(archivePath), { recursive: true });
11636
- }
11637
- renameSync2(paths.root, archivePath);
11638
- }
11639
- }
11640
- if (existsSync18(assetSyncRoot)) {
11641
- if (opts.wipe) {
11642
- rmSync(assetSyncRoot, { recursive: true, force: true });
11643
- } else {
11644
- if (!assetSyncArchivePath) throw new Error("internal: assetSyncArchivePath missing");
11645
- if (!existsSync18(dirname10(assetSyncArchivePath))) {
11646
- mkdirSync10(dirname10(assetSyncArchivePath), { recursive: true });
11647
- }
11648
- renameSync2(assetSyncRoot, assetSyncArchivePath);
11649
- }
11650
- }
11651
- if (existsSync18(hermesHome)) {
11652
- if (opts.wipe) {
11653
- rmSync(hermesHome, { recursive: true, force: true });
11654
- } else {
11655
- if (!hermesArchivePath) throw new Error("internal: hermesArchivePath missing");
11656
- if (!existsSync18(dirname10(hermesArchivePath))) {
11657
- mkdirSync10(dirname10(hermesArchivePath), { recursive: true });
11658
- }
11659
- renameSync2(hermesHome, hermesArchivePath);
11660
- }
11661
- }
11662
- mkdirSync10(paths.root, { recursive: true });
11663
- if (opts.json) {
11664
- printJson({ ok: true, reset: true, plan });
11665
- return;
11666
- }
11667
- const ui = getUI();
11668
- ui.ok("Runtime reset complete", paths.root);
11669
- if (archivePath) ui.line(` archived: ${archivePath}`);
11670
- if (assetSyncArchivePath && plan.assetSyncExists) ui.line(` asset sync archived: ${assetSyncArchivePath}`);
11671
- if (hermesArchivePath && plan.hermesExists) ui.line(` Hermes archived: ${hermesArchivePath}`);
11672
- if (hermesGatewayPids.length > 0) ui.line(` stopped Hermes gateway pid(s): ${hermesGatewayPids.join(", ")}`);
11673
- ui.line(" next: prismer setup");
11674
- }, { code: "reset_failed" }));
11675
- }
11676
- function printPlan(plan, json) {
11677
- if (json) {
11678
- printJson({ ok: true, check: true, plan });
11679
- return;
11680
- }
11681
- const ui = getUI();
11682
- ui.header("Runtime reset plan");
11683
- ui.blank();
11684
- ui.line(` root: ${plan.root}`);
11685
- ui.line(` exists: ${plan.exists ? "yes" : "no"}`);
11686
- ui.line(` daemon: ${plan.runningPid ? `running pid ${plan.runningPid}` : "not running"}`);
11687
- ui.line(` mode: ${plan.mode}`);
11688
- if (plan.archivePath) ui.line(` archive: ${plan.archivePath}`);
11689
- ui.line(` asset sync: ${plan.assetSyncExists ? plan.assetSyncRoot : `${plan.assetSyncRoot} (not found)`}`);
11690
- if (plan.assetSyncArchivePath) ui.line(` asset archive: ${plan.assetSyncArchivePath}`);
11691
- ui.line(` Hermes: ${plan.hermesExists ? plan.hermesHome : `${plan.hermesHome} (not found)`}`);
11692
- if (plan.hermesArchivePath) ui.line(` Hermes archive: ${plan.hermesArchivePath}`);
11693
- ui.line(
11694
- ` Hermes gateway: ${plan.hermesGatewayPids.length > 0 ? `running pid(s) ${plan.hermesGatewayPids.join(", ")}` : "not running"}`
11695
- );
11696
- ui.blank();
11697
- ui.line("Run `prismer reset --yes` to apply, then `prismer setup` to bind a new account.");
11698
- }
11699
- async function stopDaemon(pid, timeoutMs) {
11700
- await stopProcess(pid, timeoutMs, "daemon");
11701
- }
11702
- async function stopProcess(pid, timeoutMs, label) {
11703
- try {
11704
- process.kill(pid, "SIGTERM");
11705
- } catch (err) {
11706
- throw new Error(`SIGTERM failed for ${label} pid ${pid}: ${err.message}`);
11707
- }
11708
- const deadline = Date.now() + timeoutMs;
11709
- while (Date.now() < deadline) {
11710
- if (!pidAlive2(pid)) return;
11711
- await sleep4(100);
11712
- }
11713
- try {
11714
- process.kill(pid, "SIGKILL");
11715
- } catch (err) {
11716
- throw new Error(`SIGKILL failed for ${label} pid ${pid}: ${err.message}`);
11717
- }
11718
- const killDeadline = Date.now() + 1e3;
11719
- while (Date.now() < killDeadline) {
11720
- if (!pidAlive2(pid)) return;
11721
- await sleep4(100);
11722
- }
11723
- throw new Error(`${label} pid ${pid} did not exit within ${timeoutMs + 1e3}ms`);
11724
- }
11725
- function timestamp() {
11726
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
11727
- }
11728
- function resolveAssetSyncRoot() {
11729
- const override = process.env.PRISMER_ASSET_SYNC_ROOT;
11730
- if (override) return override;
11731
- const home = homedir9();
11732
- const desktop = join20(home, "Desktop");
11733
- const base = existsSync18(desktop) ? desktop : home;
11734
- return join20(base, "Prismer Assets");
11735
- }
11736
- function resolveHermesHome() {
11737
- return process.env.HERMES_HOME || join20(homedir9(), ".hermes");
11738
- }
11739
- function findHermesGatewayPids2() {
11740
- try {
11741
- const out = execFileSync2("ps", ["-axo", "pid=,command="], { encoding: "utf8" });
11742
- const current = process.pid;
11743
- return out.split(/\r?\n/).map((line) => {
11744
- const trimmed = line.trim();
11745
- const match = /^(\d+)\s+(.+)$/.exec(trimmed);
11746
- if (!match) return null;
11747
- const pid = Number.parseInt(match[1], 10);
11748
- const command = match[2];
11749
- if (!Number.isFinite(pid) || pid === current) return null;
11750
- if (!isHermesGatewayCommand2(command)) return null;
11751
- return pid;
11752
- }).filter((pid) => pid !== null);
11753
- } catch {
11754
- return [];
11755
- }
11756
- }
11757
- function isHermesGatewayCommand2(command) {
11758
- if (!/\bgateway\b/.test(command) || !/\brun\b/.test(command)) return false;
11759
- return /(^|\s)(?:\S*\/)?hermes(?:\s|$)/.test(command);
11760
- }
11761
-
11762
10132
  // src/cli/commands/sandbox.ts
11763
- import { Command as Command14 } from "commander";
10133
+ import { Command as Command13 } from "commander";
11764
10134
  init_util();
11765
10135
  init_ui();
11766
10136
  function buildSandboxCommand() {
11767
- const cmd = new Command14("sandbox").description("Inspect and smoke-test sandbox lifecycle");
11768
- 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) => {
10137
+ const cmd = new Command13("sandbox").description("Inspect and smoke-test sandbox lifecycle");
10138
+ 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) => {
11769
10139
  const query = new URLSearchParams({ workspaceId: opts.workspaceId, limit: String(opts.limit) });
11770
10140
  if (opts.status) query.set("status", opts.status);
11771
10141
  const body = await cloudRequest("GET", `/api/sandboxes?${query.toString()}`);
@@ -11824,7 +10194,7 @@ function buildSandboxCommand() {
11824
10194
  }
11825
10195
  printSnapshotResult(id, body);
11826
10196
  }, { code: "sandbox_snapshot_failed" }));
11827
- 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) => {
10197
+ 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) => {
11828
10198
  if (!Array.isArray(command) || command.length === 0) exitWithError("runCmd requires a command after --", { code: "invalid_argument" });
11829
10199
  const body = await cloudRequest("POST", `/api/sandboxes/${encodeURIComponent(id)}/runCmd`, {
11830
10200
  command,
@@ -11865,12 +10235,12 @@ function errorMessage2(raw) {
11865
10235
  if (typeof raw.error === "string") return raw.error;
11866
10236
  return raw.error?.message ?? "request failed";
11867
10237
  }
11868
- async function cloudRequest(method, path9, body) {
10238
+ async function cloudRequest(method, path7, body) {
11869
10239
  const cloud = mkCloud5();
11870
- const res = await cloud.request(method, path9, body === void 0 ? void 0 : { body });
11871
- if (!res.ok) exitWithError(`${method} ${path9} failed (${res.status}): ${res.error?.message ?? "request failed"}`);
10240
+ const res = await cloud.request(method, path7, body === void 0 ? void 0 : { body });
10241
+ if (!res.ok) exitWithError(`${method} ${path7} failed (${res.status}): ${res.error?.message ?? "request failed"}`);
11872
10242
  const responseBody = res.data;
11873
- if (isErrorEnvelope2(responseBody)) exitWithError(`${method} ${path9} failed (${res.status}): ${errorMessage2(responseBody)}`);
10243
+ if (isErrorEnvelope2(responseBody)) exitWithError(`${method} ${path7} failed (${res.status}): ${errorMessage2(responseBody)}`);
11874
10244
  return responseBody;
11875
10245
  }
11876
10246
  function printSandboxList(raw) {
@@ -11945,7 +10315,7 @@ function writeLine2(label, value) {
11945
10315
  if (value === void 0 || value === null || value === "") return;
11946
10316
  getUI().line(` ${label}: ${String(value)}`);
11947
10317
  }
11948
- function parsePositiveInt6(value) {
10318
+ function parsePositiveInt5(value) {
11949
10319
  const parsed = Number.parseInt(value, 10);
11950
10320
  if (!Number.isFinite(parsed) || parsed <= 0) {
11951
10321
  throw new Error("expected a positive integer");
@@ -11963,16 +10333,16 @@ async function safeParseResponse(res) {
11963
10333
  }
11964
10334
 
11965
10335
  // src/cli/commands/setup.ts
11966
- import { Command as Command15 } from "commander";
10336
+ import { Command as Command14 } from "commander";
11967
10337
  import { hostname as hostname3 } from "os";
11968
10338
  import { spawn as spawn6 } from "child_process";
11969
- import { randomBytes as randomBytes2 } from "crypto";
11970
- import { existsSync as existsSync19, renameSync as renameSync3 } from "fs";
10339
+ import { randomBytes } from "crypto";
10340
+ import { existsSync as existsSync18, renameSync } from "fs";
11971
10341
  import { createServer as createServer2 } from "http";
11972
10342
  init_util();
11973
10343
  init_ui();
11974
10344
  function buildSetupCommand() {
11975
- return new Command15("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(
10345
+ return new Command14("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(
11976
10346
  "--cloud <url>",
11977
10347
  `Cloud base URL (default: ${DEFAULT_CLOUD_BASE_URL}; bare host:port is auto-prefixed http://)`,
11978
10348
  (v) => v
@@ -11985,7 +10355,7 @@ function buildSetupCommand() {
11985
10355
  } catch (err) {
11986
10356
  exitWithError(err.message, { code: "invalid_cloud_url" });
11987
10357
  }
11988
- let apiKey = apiKeyArg ?? (opts.force ? void 0 : process.env.PRISMER_API_KEY);
10358
+ let apiKey = apiKeyArg ?? process.env.PRISMER_API_KEY;
11989
10359
  const authToken = opts.token ?? process.env.PRISMER_AUTH_TOKEN;
11990
10360
  if (!opts.json) {
11991
10361
  printBanner({ compact: true });
@@ -11993,6 +10363,7 @@ function buildSetupCommand() {
11993
10363
  getUI().blank();
11994
10364
  }
11995
10365
  const shouldStart = opts.start !== false;
10366
+ stopRunningDaemon(paths);
11996
10367
  if (opts.pair || opts.asUser) {
11997
10368
  if (!opts.json) warn("Legacy pair setup path", "plain `prismer setup --start` is the canonical runtime binding flow");
11998
10369
  if (opts.asUser && process.env.LOCAL_ONLY !== "1") {
@@ -12083,11 +10454,10 @@ function buildSetupCommand() {
12083
10454
  return;
12084
10455
  }
12085
10456
  const previousConfig = configExists(paths) ? loadConfig(paths) : null;
12086
- const daemonId = previousConfig && previousConfig.api_key === apiKey && previousConfig.cloud_api_base === cloudBaseUrl ? previousConfig.daemon_id : newDaemonId();
12087
10457
  const config = {
12088
10458
  api_key: apiKey,
12089
10459
  cloud_api_base: cloudBaseUrl,
12090
- daemon_id: daemonId
10460
+ daemon_id: newDaemonId()
12091
10461
  };
12092
10462
  if (previousConfig && shouldArchiveLocalDb(previousConfig, config)) {
12093
10463
  archiveLocalDb(paths.localDb);
@@ -12117,7 +10487,7 @@ function buildSetupCommand() {
12117
10487
  }, { code: "setup_failed" }));
12118
10488
  }
12119
10489
  async function runBrowserSetup(input) {
12120
- const state = randomBytes2(18).toString("base64url");
10490
+ const state = randomBytes(18).toString("base64url");
12121
10491
  const server = createServer2();
12122
10492
  const apiKeyPromise = waitForSetupCallback(server, state);
12123
10493
  await new Promise((resolve3, reject) => {
@@ -12214,13 +10584,35 @@ function startDaemonDetached(home) {
12214
10584
  });
12215
10585
  child.unref();
12216
10586
  }
10587
+ function stopRunningDaemon(paths) {
10588
+ const pid = readPidFile(paths);
10589
+ if (!pid || !pidAlive(pid)) {
10590
+ if (pid) clearPidFile(paths);
10591
+ return;
10592
+ }
10593
+ try {
10594
+ process.kill(pid, "SIGTERM");
10595
+ } catch {
10596
+ return;
10597
+ }
10598
+ const deadline = Date.now() + 5e3;
10599
+ while (Date.now() < deadline) {
10600
+ if (!pidAlive(pid)) {
10601
+ clearPidFile(paths);
10602
+ return;
10603
+ }
10604
+ const start = Date.now();
10605
+ while (Date.now() - start < 200) {
10606
+ }
10607
+ }
10608
+ }
12217
10609
  function shouldArchiveLocalDb(previous, next) {
12218
10610
  return previous.api_key !== next.api_key || previous.cloud_api_base !== next.cloud_api_base || previous.daemon_id !== next.daemon_id;
12219
10611
  }
12220
10612
  function archiveLocalDb(localDbPath) {
12221
- if (!existsSync19(localDbPath)) return;
10613
+ if (!existsSync18(localDbPath)) return;
12222
10614
  const archived = `${localDbPath}.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.bak`;
12223
- renameSync3(localDbPath, archived);
10615
+ renameSync(localDbPath, archived);
12224
10616
  }
12225
10617
  async function mintDaemonApiKey(input) {
12226
10618
  const label = `Daemon: ${input.deviceName} ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
@@ -12240,12 +10632,11 @@ async function mintDaemonApiKey(input) {
12240
10632
  }
12241
10633
 
12242
10634
  // src/cli/commands/status.ts
12243
- import { Command as Command16 } from "commander";
12244
- import { hostname as hostname4 } from "os";
10635
+ import { Command as Command15 } from "commander";
12245
10636
  init_util();
12246
10637
  init_ui();
12247
10638
  function buildStatusCommand() {
12248
- return new Command16("status").description("Show daemon + config + cloud status").option("--json", "Output machine-readable JSON").action(async (opts) => {
10639
+ return new Command15("status").description("Show daemon + config + cloud status").option("--json", "Output machine-readable JSON").action(async (opts) => {
12249
10640
  const paths = resolvePaths();
12250
10641
  if (!configExists(paths)) {
12251
10642
  const report2 = { configured: false, paired: false, paths: { config: paths.configFile } };
@@ -12313,7 +10704,11 @@ function buildStatusCommand() {
12313
10704
  info: daemonStatus.info ?? {}
12314
10705
  },
12315
10706
  cloud: { base: cfg.cloud_api_base, reachable: cloudOk, me, devices, agents },
12316
- local
10707
+ local,
10708
+ binding: {
10709
+ daemonId: cfg.daemon_id,
10710
+ apiKey: cfg.api_key
10711
+ }
12317
10712
  };
12318
10713
  if (opts.json) {
12319
10714
  printJson(report);
@@ -12336,8 +10731,8 @@ async function readDaemonStatus() {
12336
10731
  const paths = resolvePaths();
12337
10732
  const pid = readPidFile(paths);
12338
10733
  if (pid) {
12339
- const { pidAlive: pidAlive3 } = await Promise.resolve().then(() => (init_util(), util_exports));
12340
- if (pidAlive3(pid)) return { running: true, pid };
10734
+ const { pidAlive: pidAlive2 } = await Promise.resolve().then(() => (init_util(), util_exports));
10735
+ if (pidAlive2(pid)) return { running: true, pid };
12341
10736
  }
12342
10737
  return { running: false };
12343
10738
  }
@@ -12365,9 +10760,8 @@ function printPretty(report) {
12365
10760
  ui.blank();
12366
10761
  ok("Config", report.paths.config);
12367
10762
  if (report.daemon.running) {
12368
- const daemonName = report.cloud.me && typeof report.cloud.me === "object" ? report.cloud.me.user?.username ?? hostname4() : hostname4();
12369
10763
  const ws = report.daemon.wsConnected ? "connected" : "pending";
12370
- ok("Daemon", `${daemonName} pid=${report.daemon.pid} ws=${ws}`);
10764
+ ok("Daemon", `pid=${report.daemon.pid} ws=${ws}`);
12371
10765
  if (report.daemon.info) {
12372
10766
  const info2 = report.daemon.info;
12373
10767
  if (info2.version) ui.line(` Version: ${info2.version}`);
@@ -12382,7 +10776,8 @@ function printPretty(report) {
12382
10776
  ok("Cloud", report.cloud.base);
12383
10777
  const me = report.cloud.me;
12384
10778
  if (me?.user) {
12385
- ui.line(` User: ${me.user.displayName ?? me.user.username ?? "?"}`);
10779
+ const roleTag = me.user.role ? ` role=${me.user.role}` : "";
10780
+ ui.line(` Account: ${me.user.displayName ?? me.user.username ?? "?"}${roleTag}`);
12386
10781
  }
12387
10782
  if (me?.credits) {
12388
10783
  ui.line(` Credits: ${typeof me.credits.balance === "number" ? me.credits.balance.toLocaleString() : "?"}`);
@@ -12391,6 +10786,12 @@ function printPretty(report) {
12391
10786
  fail2("Cloud", `${report.cloud.base} unreachable or unauthorized`);
12392
10787
  tip("prismer setup --force");
12393
10788
  }
10789
+ if (report.binding) {
10790
+ ui.blank();
10791
+ ui.line(` Daemon ID: ${report.binding.daemonId}`);
10792
+ const masked = report.binding.apiKey.slice(0, 14) + "\u2022\u2022\u2022\u2022" + report.binding.apiKey.slice(-4);
10793
+ ui.line(` API Key: ${masked}`);
10794
+ }
12394
10795
  if (Array.isArray(report.cloud.devices) && report.cloud.devices.length > 0) {
12395
10796
  const devs = report.cloud.devices;
12396
10797
  ui.blank();
@@ -12401,6 +10802,8 @@ function printPretty(report) {
12401
10802
  const declared = d.hostedAgentSummary?.declared ?? 0;
12402
10803
  ui.line(` ${statusIcon} ${d.podName?.replace(/^daemon:/, "")?.slice(0, 28) ?? "?"} kind=${kind} agents=${declared}`);
12403
10804
  }
10805
+ } else {
10806
+ ui.line(` Workspace Devices: none`);
12404
10807
  }
12405
10808
  if (Array.isArray(report.cloud.agents) && report.cloud.agents.length > 0) {
12406
10809
  ui.line(` Hosted agents: ${report.cloud.agents.length}`);
@@ -12416,14 +10819,14 @@ function printPretty(report) {
12416
10819
  }
12417
10820
 
12418
10821
  // src/cli/commands/task.ts
12419
- import { Command as Command17 } from "commander";
12420
- import { setTimeout as sleep5 } from "timers/promises";
10822
+ import { Command as Command16 } from "commander";
10823
+ import { setTimeout as sleep3 } from "timers/promises";
12421
10824
  init_util();
12422
10825
  function describeStatus(status) {
12423
10826
  return status === 0 ? "network error" : `HTTP ${status}`;
12424
10827
  }
12425
10828
  function buildTaskCommand() {
12426
- const cmd = new Command17("task").description("Manage tasks");
10829
+ const cmd = new Command16("task").description("Manage tasks");
12427
10830
  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) => {
12428
10831
  const cloud = mkCloud6();
12429
10832
  const body = {
@@ -12500,7 +10903,7 @@ function unwrap2(raw) {
12500
10903
  async function pollUntilTerminal(cloud, taskId, timeoutMs) {
12501
10904
  const deadline = Date.now() + (timeoutMs ?? 5 * 6e4);
12502
10905
  while (Date.now() < deadline) {
12503
- await sleep5(1e3);
10906
+ await sleep3(1e3);
12504
10907
  const res = await cloud.request(
12505
10908
  "GET",
12506
10909
  `/api/im/tasks/${encodeURIComponent(taskId)}`
@@ -12537,11 +10940,11 @@ function taskFrom2(raw) {
12537
10940
  }
12538
10941
 
12539
10942
  // src/cli/commands/workspace.ts
12540
- import { Command as Command18 } from "commander";
10943
+ import { Command as Command17 } from "commander";
12541
10944
  init_util();
12542
10945
  init_ui();
12543
10946
  function buildWorkspaceCommand() {
12544
- const cmd = new Command18("workspace").description("Manage workspaces, runtime snapshots, and workspace files");
10947
+ const cmd = new Command17("workspace").description("Manage workspaces, runtime snapshots, and workspace files");
12545
10948
  cmd.command("list").description("List workspaces").option("--json", "Output machine-readable JSON").action(runAction(async (opts) => {
12546
10949
  const cloud = mkCloud7();
12547
10950
  const data = await cloud.get("/api/im/workspaces");
@@ -12775,15 +11178,18 @@ async function readResponseError(res) {
12775
11178
 
12776
11179
  // src/cli/index.ts
12777
11180
  init_ui();
12778
- var VERSION = "1.9.7";
11181
+
11182
+ // package.json
11183
+ var version = "1.9.22";
11184
+
11185
+ // src/cli/index.ts
12779
11186
  function buildProgram() {
12780
- const program = new Command19("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
11187
+ const program = new Command18("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(version);
12781
11188
  program.addCommand(buildBannerCommand());
12782
11189
  program.addCommand(buildSetupCommand());
12783
11190
  program.addCommand(buildConfigCommand());
12784
11191
  program.addCommand(buildDaemonCommand());
12785
11192
  program.addCommand(buildPairCommand());
12786
- program.addCommand(buildResetCommand());
12787
11193
  program.addCommand(buildStatusCommand());
12788
11194
  program.addCommand(buildAdapterCommand());
12789
11195
  program.addCommand(buildAgentCommand());