@prismer/sdk 1.9.22 → 2.0.0

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/cli.js CHANGED
@@ -36,8 +36,8 @@ __export(cli_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(cli_exports);
38
38
  var import_commander = require("commander");
39
- var fs3 = __toESM(require("fs"));
40
- var path3 = __toESM(require("path"));
39
+ var fs4 = __toESM(require("fs"));
40
+ var path4 = __toESM(require("path"));
41
41
  var os2 = __toESM(require("os"));
42
42
  var TOML2 = __toESM(require("@iarna/toml"));
43
43
 
@@ -163,7 +163,7 @@ var RealtimeWSClient = class extends TypedEmitter {
163
163
  if (this._state === "connected" || this._state === "connecting") return;
164
164
  this._state = "connecting";
165
165
  this.intentionalClose = false;
166
- return new Promise((resolve2, reject) => {
166
+ return new Promise((resolve3, reject) => {
167
167
  try {
168
168
  this.ws = new this.WS(this.wsUrl);
169
169
  } catch (err) {
@@ -185,7 +185,7 @@ var RealtimeWSClient = class extends TypedEmitter {
185
185
  this.emit("connected", void 0);
186
186
  this.ws.removeEventListener("message", onFirstMessage);
187
187
  this.ws.addEventListener("message", this.handleMessage);
188
- resolve2();
188
+ resolve3();
189
189
  }
190
190
  } catch (_) {
191
191
  }
@@ -257,12 +257,12 @@ var RealtimeWSClient = class extends TypedEmitter {
257
257
  }
258
258
  ping() {
259
259
  const requestId = `ping-${++this.pingCounter}`;
260
- return new Promise((resolve2, reject) => {
260
+ return new Promise((resolve3, reject) => {
261
261
  const timer = setTimeout(() => {
262
262
  this.pendingPings.delete(requestId);
263
263
  reject(new Error("Ping timeout"));
264
264
  }, 1e4);
265
- this.pendingPings.set(requestId, { resolve: resolve2, timer });
265
+ this.pendingPings.set(requestId, { resolve: resolve3, timer });
266
266
  this.sendRaw({ type: "ping", payload: { requestId } });
267
267
  });
268
268
  }
@@ -515,9 +515,9 @@ var WRITE_PATTERNS = [
515
515
  { method: "POST", pattern: /\/api\/im\/community\/posts\/[^/]+\/comments$/, opType: "community_comment" },
516
516
  { method: "POST", pattern: /\/api\/im\/community\/vote$/, opType: "community_vote" }
517
517
  ];
518
- function matchWriteOp(method, path4) {
518
+ function matchWriteOp(method, path5) {
519
519
  for (const { method: m, pattern, opType } of WRITE_PATTERNS) {
520
- if (method === m && pattern.test(path4)) return opType;
520
+ if (method === m && pattern.test(path5)) return opType;
521
521
  }
522
522
  return null;
523
523
  }
@@ -585,18 +585,18 @@ var OfflineManager = class extends OfflineEmitter {
585
585
  /**
586
586
  * Dispatch an IM request. Write ops go through outbox; reads check local cache.
587
587
  */
588
- async dispatch(method, path4, body, query) {
589
- const opType = matchWriteOp(method, path4);
588
+ async dispatch(method, path5, body, query) {
589
+ const opType = matchWriteOp(method, path5);
590
590
  if (opType) {
591
- return this.dispatchWrite(opType, method, path4, body, query);
591
+ return this.dispatchWrite(opType, method, path5, body, query);
592
592
  }
593
593
  if (method === "GET") {
594
- const cached = await this.readFromCache(path4, query);
594
+ const cached = await this.readFromCache(path5, query);
595
595
  if (cached !== null) return cached;
596
596
  }
597
597
  try {
598
- const result = await this.networkRequest(method, path4, body, query);
599
- if (method === "GET") this.cacheReadResult(path4, query, result);
598
+ const result = await this.networkRequest(method, path5, body, query);
599
+ if (method === "GET") this.cacheReadResult(path5, query, result);
600
600
  return result;
601
601
  } catch {
602
602
  if (!this._isOnline) {
@@ -606,7 +606,7 @@ var OfflineManager = class extends OfflineEmitter {
606
606
  }
607
607
  }
608
608
  // ── Outbox: write operations ──────────────────────────────
609
- async dispatchWrite(opType, method, path4, body, query) {
609
+ async dispatchWrite(opType, method, path5, body, query) {
610
610
  const clientId = generateId();
611
611
  const idempotencyKey = `sdk-${clientId}`;
612
612
  let enrichedBody = body;
@@ -620,7 +620,7 @@ var OfflineManager = class extends OfflineEmitter {
620
620
  let localMessage;
621
621
  if (opType === "message.send" && body && typeof body === "object") {
622
622
  const b = body;
623
- const convIdMatch = path4.match(/\/(?:messages|direct|groups)\/([^/]+)/);
623
+ const convIdMatch = path5.match(/\/(?:messages|direct|groups)\/([^/]+)/);
624
624
  const conversationId = convIdMatch?.[1] ?? "";
625
625
  localMessage = {
626
626
  id: `local-${clientId}`,
@@ -641,7 +641,7 @@ var OfflineManager = class extends OfflineEmitter {
641
641
  id: clientId,
642
642
  type: opType,
643
643
  method,
644
- path: path4,
644
+ path: path5,
645
645
  body: enrichedBody,
646
646
  query,
647
647
  status: "pending",
@@ -961,28 +961,28 @@ var OfflineManager = class extends OfflineEmitter {
961
961
  return 0;
962
962
  }
963
963
  // ── Read cache ────────────────────────────────────────────
964
- async readFromCache(path4, query) {
965
- if (/\/api\/im\/conversations$/.test(path4)) {
964
+ async readFromCache(path5, query) {
965
+ if (/\/api\/im\/conversations$/.test(path5)) {
966
966
  const convos = await this.storage.getConversations({ limit: 50 });
967
967
  if (convos.length > 0) return { ok: true, data: convos };
968
968
  }
969
- const msgMatch = path4.match(/\/api\/im\/messages\/([^/]+)$/);
969
+ const msgMatch = path5.match(/\/api\/im\/messages\/([^/]+)$/);
970
970
  if (msgMatch) {
971
971
  const convId = msgMatch[1];
972
972
  const limit = query?.limit ? parseInt(query.limit) : 50;
973
973
  const messages = await this.storage.getMessages(convId, { limit, before: query?.before });
974
974
  if (messages.length > 0) return { ok: true, data: messages };
975
975
  }
976
- if (/\/api\/im\/contacts$/.test(path4)) {
976
+ if (/\/api\/im\/contacts$/.test(path5)) {
977
977
  const contacts = await this.storage.getContacts();
978
978
  if (contacts.length > 0) return { ok: true, data: contacts };
979
979
  }
980
980
  return null;
981
981
  }
982
- async cacheReadResult(path4, _query, result) {
982
+ async cacheReadResult(path5, _query, result) {
983
983
  if (!result?.ok || !result?.data) return;
984
984
  try {
985
- if (/\/api\/im\/conversations$/.test(path4) && Array.isArray(result.data)) {
985
+ if (/\/api\/im\/conversations$/.test(path5) && Array.isArray(result.data)) {
986
986
  const convos = result.data.map((c) => ({
987
987
  id: c.id,
988
988
  type: c.type ?? "direct",
@@ -996,7 +996,7 @@ var OfflineManager = class extends OfflineEmitter {
996
996
  }));
997
997
  await this.storage.putConversations(convos);
998
998
  }
999
- const msgMatch = path4.match(/\/api\/im\/messages\/([^/]+)$/);
999
+ const msgMatch = path5.match(/\/api\/im\/messages\/([^/]+)$/);
1000
1000
  if (msgMatch && Array.isArray(result.data)) {
1001
1001
  const messages = result.data.map((m) => ({
1002
1002
  id: m.id,
@@ -1011,7 +1011,7 @@ var OfflineManager = class extends OfflineEmitter {
1011
1011
  }));
1012
1012
  await this.storage.putMessages(messages);
1013
1013
  }
1014
- if (/\/api\/im\/contacts$/.test(path4) && Array.isArray(result.data)) {
1014
+ if (/\/api\/im\/contacts$/.test(path5) && Array.isArray(result.data)) {
1015
1015
  await this.storage.putContacts(result.data);
1016
1016
  }
1017
1017
  } catch {
@@ -1801,6 +1801,7 @@ var DirectClient = class {
1801
1801
  content,
1802
1802
  type: options?.type ?? "text",
1803
1803
  metadata: options?.metadata,
1804
+ attachments: options?.attachments,
1804
1805
  parentId: options?.parentId,
1805
1806
  quotedMessageId: options?.quotedMessageId
1806
1807
  });
@@ -1835,6 +1836,7 @@ var GroupsClient = class {
1835
1836
  content,
1836
1837
  type: options?.type ?? "text",
1837
1838
  metadata: options?.metadata,
1839
+ attachments: options?.attachments,
1838
1840
  parentId: options?.parentId,
1839
1841
  quotedMessageId: options?.quotedMessageId
1840
1842
  });
@@ -1913,6 +1915,7 @@ var MessagesClient = class {
1913
1915
  content,
1914
1916
  type: options?.type ?? "text",
1915
1917
  metadata: options?.metadata,
1918
+ attachments: options?.attachments,
1916
1919
  parentId: options?.parentId,
1917
1920
  quotedMessageId: options?.quotedMessageId
1918
1921
  });
@@ -1973,6 +1976,11 @@ var ContactsClient = class {
1973
1976
  const query = {};
1974
1977
  if (options?.type) query.type = options.type;
1975
1978
  if (options?.capability) query.capability = options.capability;
1979
+ if (options?.status) query.status = options.status;
1980
+ if (options?.onlineOnly) query.onlineOnly = options.onlineOnly;
1981
+ if (options?.q) query.q = options.q;
1982
+ if (options?.limit) query.limit = options.limit;
1983
+ if (options?.offset) query.offset = options.offset;
1976
1984
  return this._r("GET", "/api/im/discover", void 0, query);
1977
1985
  }
1978
1986
  // ─── Friend System (v1.8.0 P9) ─────────────────────────
@@ -2171,8 +2179,8 @@ var TasksClient = class {
2171
2179
  return this._r("POST", `/api/im/tasks/${taskId}/complete`, options);
2172
2180
  }
2173
2181
  /** Fail a task with error */
2174
- async fail(taskId, error2, metadata) {
2175
- return this._r("POST", `/api/im/tasks/${taskId}/fail`, { error: error2, metadata });
2182
+ async fail(taskId, error, metadata) {
2183
+ return this._r("POST", `/api/im/tasks/${taskId}/fail`, { error, metadata });
2176
2184
  }
2177
2185
  /** Approve a completed task */
2178
2186
  async approve(taskId) {
@@ -2186,6 +2194,32 @@ var TasksClient = class {
2186
2194
  async cancel(taskId) {
2187
2195
  return this._r("DELETE", `/api/im/tasks/${taskId}`);
2188
2196
  }
2197
+ /**
2198
+ * v2.0 release 200 §6.1 — unified state-machine transition.
2199
+ *
2200
+ * Drives every kanban / approve / reject / cancel / blocked / retry /
2201
+ * restore action through one endpoint. The 5 legacy endpoints
2202
+ * (start/complete/approve/reject/cancel) remain for backward
2203
+ * compatibility but new integrations should prefer this entrypoint.
2204
+ *
2205
+ * Server responds 409 (`code: 'invalid-transition'`) if the requested
2206
+ * `to` is not in the TRANSITIONS matrix from the current status, or
2207
+ * 403 (`code: 'forbidden'`) if the actor's tier is not in the rule's
2208
+ * `allowedActors`.
2209
+ */
2210
+ async transition(taskId, options) {
2211
+ return this._r("POST", `/api/im/tasks/${taskId}/transition`, options);
2212
+ }
2213
+ /**
2214
+ * v2.0 release 200 §5.3 — admin escape-hatch.
2215
+ *
2216
+ * Bypasses the TRANSITIONS matrix. Restricted to workspace owner /
2217
+ * admin / trustTier>=4. Reason is required; the call is audit-logged
2218
+ * with `force_transition: true`. UI does NOT expose this — ops only.
2219
+ */
2220
+ async forceTransition(taskId, options) {
2221
+ return this._r("POST", `/api/im/tasks/${taskId}/force-transition`, options);
2222
+ }
2189
2223
  };
2190
2224
  var MemoryClient = class {
2191
2225
  constructor(_r) {
@@ -2314,9 +2348,118 @@ var SecurityClient = class {
2314
2348
  return this._r("DELETE", `/api/im/conversations/${conversationId}/keys/${keyUserId}`);
2315
2349
  }
2316
2350
  };
2351
+ var EvolutionSkillsClient = class {
2352
+ constructor(_r) {
2353
+ this._r = _r;
2354
+ }
2355
+ /** List the skill catalog. Alias of search() for the v2.0 public surface. */
2356
+ async list(options) {
2357
+ return this.search(options);
2358
+ }
2359
+ /** Browse and search the skill catalog. */
2360
+ async search(options) {
2361
+ const query = {};
2362
+ if (options?.query) query.query = options.query;
2363
+ if (options?.category) query.category = options.category;
2364
+ if (options?.source) query.source = options.source;
2365
+ if (options?.compatibility) query.compatibility = options.compatibility;
2366
+ if (options?.sort) query.sort = options.sort;
2367
+ if (options?.page != null) query.page = String(options.page);
2368
+ if (options?.limit != null) query.limit = String(options.limit);
2369
+ return this._r("GET", "/api/im/skills/search", void 0, query);
2370
+ }
2371
+ /** Get skill catalog stats. */
2372
+ async stats() {
2373
+ return this._r("GET", "/api/im/skills/stats");
2374
+ }
2375
+ /** List available skill categories. */
2376
+ async categories() {
2377
+ return this._r("GET", "/api/im/skills/categories");
2378
+ }
2379
+ /** List trending skills. */
2380
+ async trending(limit) {
2381
+ const query = {};
2382
+ if (limit != null) query.limit = String(limit);
2383
+ return this._r("GET", "/api/im/skills/trending", void 0, query);
2384
+ }
2385
+ /** List skills created by the authenticated agent. */
2386
+ async created() {
2387
+ return this._r("GET", "/api/im/skills/created");
2388
+ }
2389
+ /** Get skill detail by slug or ID. */
2390
+ async get(slugOrId) {
2391
+ return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}`);
2392
+ }
2393
+ /** Get full SKILL.md content and package metadata. */
2394
+ async content(slugOrId) {
2395
+ return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
2396
+ }
2397
+ /** Create/submit a workspace or community skill. */
2398
+ async create(input) {
2399
+ return this._r("POST", "/api/im/skills", input);
2400
+ }
2401
+ /** Update a skill. */
2402
+ async update(skillId, input) {
2403
+ return this._r("PATCH", `/api/im/skills/${encodeURIComponent(skillId)}`, input);
2404
+ }
2405
+ /** Soft-delete/deprecate a skill. */
2406
+ async delete(skillId) {
2407
+ return this._r("DELETE", `/api/im/skills/${encodeURIComponent(skillId)}`);
2408
+ }
2409
+ /** Install a skill for the authenticated agent. */
2410
+ async install(slugOrId, scope) {
2411
+ return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, scope ? { scope } : void 0);
2412
+ }
2413
+ /** Uninstall a skill for the authenticated agent. */
2414
+ async uninstall(slugOrId, scope) {
2415
+ const query = scope ? { scope } : void 0;
2416
+ return this._r("DELETE", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, void 0, query);
2417
+ }
2418
+ /**
2419
+ * List installed skills. When agentId is supplied, this uses the v2.0 Layer 5
2420
+ * route and includes daemon sync state; otherwise it keeps the legacy current-agent route.
2421
+ */
2422
+ async installed(options) {
2423
+ const query = {};
2424
+ if (options?.workspaceId) query.workspaceId = options.workspaceId;
2425
+ if (options?.includeInactive) query.includeInactive = "true";
2426
+ if (options?.agentId) {
2427
+ return this._r("GET", `/api/im/agents/${encodeURIComponent(options.agentId)}/skills`, void 0, query);
2428
+ }
2429
+ return this._r("GET", "/api/im/skills/installed", void 0, query);
2430
+ }
2431
+ /** Install a skill to a specific agent. */
2432
+ async installForAgent(agentId, skillIdOrSlug, options) {
2433
+ return this._r("POST", `/api/im/agents/${encodeURIComponent(agentId)}/skills`, {
2434
+ skillId: skillIdOrSlug,
2435
+ ...options
2436
+ });
2437
+ }
2438
+ /** Disable/uninstall a skill from a specific agent. Built-ins are disabled cloud-side. */
2439
+ async uninstallFromAgent(agentId, skillIdOrSlug, options) {
2440
+ return this._r("DELETE", `/api/im/agents/${encodeURIComponent(agentId)}/skills`, {
2441
+ skillId: skillIdOrSlug,
2442
+ workspaceId: options?.workspaceId
2443
+ });
2444
+ }
2445
+ /** List skills whose daemon sync state is not current. */
2446
+ async pending(agentId, workspaceId) {
2447
+ const query = workspaceId ? { workspaceId } : void 0;
2448
+ return this._r("GET", `/api/im/agents/${encodeURIComponent(agentId)}/skills/pending`, void 0, query);
2449
+ }
2450
+ /** Acknowledge daemon sync for an installed skill. */
2451
+ async ack(agentId, input) {
2452
+ return this._r("POST", `/api/im/agents/${encodeURIComponent(agentId)}/skills/ack`, input);
2453
+ }
2454
+ /** Star a skill. */
2455
+ async star(skillId) {
2456
+ return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
2457
+ }
2458
+ };
2317
2459
  var EvolutionClient = class {
2318
2460
  constructor(_r) {
2319
2461
  this._r = _r;
2462
+ this.skills = new EvolutionSkillsClient(_r);
2320
2463
  }
2321
2464
  // ── Public endpoints (no auth required) ──
2322
2465
  /** Get evolution stats */
@@ -2446,7 +2589,7 @@ var EvolutionClient = class {
2446
2589
  const { outcome, score, summary, strategy_used, scope, ...analyzeOpts } = options;
2447
2590
  const analysis = await this.analyze({ ...analyzeOpts, ...scope ? { scope } : {} });
2448
2591
  if (!analysis.ok || !analysis.data) {
2449
- return { ok: false, error: analysis.error };
2592
+ return { ok: false, ...analysis.error ? { error: analysis.error } : {} };
2450
2593
  }
2451
2594
  const data = analysis.data;
2452
2595
  const geneId = data.gene_id;
@@ -2560,39 +2703,35 @@ var EvolutionClient = class {
2560
2703
  }
2561
2704
  /** Search skills catalog */
2562
2705
  async searchSkills(options) {
2563
- const q = {};
2564
- if (options?.query) q.query = options.query;
2565
- if (options?.category) q.category = options.category;
2566
- if (options?.limit != null) q.limit = String(options.limit);
2567
- return this._r("GET", "/api/im/skills/search", void 0, q);
2706
+ return this.skills.search(options);
2568
2707
  }
2569
2708
  /** Get skill catalog stats */
2570
2709
  async getSkillStats() {
2571
- return this._r("GET", "/api/im/skills/stats");
2710
+ return this.skills.stats();
2572
2711
  }
2573
2712
  /** Install a skill — creates Gene + returns content + install guide */
2574
2713
  async installSkill(slugOrId, scope) {
2575
- return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, scope ? { scope } : void 0);
2714
+ return this.skills.install(slugOrId, scope);
2576
2715
  }
2577
2716
  /** Uninstall a skill */
2578
- async uninstallSkill(slugOrId) {
2579
- return this._r("DELETE", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`);
2717
+ async uninstallSkill(slugOrId, scope) {
2718
+ return this.skills.uninstall(slugOrId, scope);
2580
2719
  }
2581
2720
  /** List installed skills for this agent */
2582
- async installedSkills() {
2583
- return this._r("GET", "/api/im/skills/installed");
2721
+ async installedSkills(options) {
2722
+ return this.skills.installed(options);
2584
2723
  }
2585
2724
  /** Get full skill content (SKILL.md + package info) */
2586
2725
  async getSkillContent(slugOrId) {
2587
- return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
2726
+ return this.skills.content(slugOrId);
2588
2727
  }
2589
2728
  /** Create/submit a community skill */
2590
2729
  async createSkill(input) {
2591
- return this._r("POST", "/api/im/skills", input);
2730
+ return this.skills.create(input);
2592
2731
  }
2593
2732
  /** Star a skill (increment community rating) */
2594
2733
  async starSkill(skillId) {
2595
- return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
2734
+ return this.skills.star(skillId);
2596
2735
  }
2597
2736
  /**
2598
2737
  * Install a skill and write SKILL.md to local filesystem.
@@ -2602,53 +2741,60 @@ var EvolutionClient = class {
2602
2741
  */
2603
2742
  async installSkillLocal(slugOrId, options) {
2604
2743
  const result = await this.installSkill(slugOrId);
2605
- if (!result.ok || !result.data) return result;
2606
- let content = result.data.skill?.content || "";
2744
+ if (!result.ok || !result.data) {
2745
+ return result;
2746
+ }
2747
+ const installData = result.data;
2748
+ const withLocalPaths = (localPaths2) => ({
2749
+ ok: true,
2750
+ data: { ...installData, localPaths: localPaths2 }
2751
+ });
2752
+ let content = installData.skill?.content || "";
2607
2753
  if (!content) {
2608
2754
  const contentResult = await this.getSkillContent(slugOrId);
2609
2755
  content = contentResult.data?.content || "";
2610
2756
  }
2611
2757
  if (!content) {
2612
- return { ...result, data: { ...result.data, localPaths: [] } };
2758
+ return withLocalPaths([]);
2613
2759
  }
2614
- const rawSlug = result.data.skill?.slug || slugOrId;
2760
+ const rawSlug = installData.skill?.slug || slugOrId;
2615
2761
  const slug = rawSlug.replace(/[\/\\]/g, "").replace(/\.\./g, "");
2616
2762
  if (!slug) {
2617
- return { ...result, data: { ...result.data, localPaths: [] } };
2763
+ return withLocalPaths([]);
2618
2764
  }
2619
2765
  const localPaths = [];
2620
2766
  try {
2621
- const fs4 = await import("fs");
2622
- const path4 = await import("path");
2767
+ const fs5 = await import("fs");
2768
+ const path5 = await import("path");
2623
2769
  const os3 = await import("os");
2624
2770
  const home = os3.homedir();
2625
- const pluginBase = process.env.PRISMER_PLUGIN_DIR || path4.join(home, ".claude", "plugins", "prismer");
2771
+ const pluginBase = process.env.PRISMER_PLUGIN_DIR || path5.join(home, ".claude", "plugins", "prismer");
2626
2772
  const platformPaths = options?.project ? {
2627
- "claude-code": path4.join(options.projectRoot || ".", ".claude", "skills", slug),
2628
- "openclaw": path4.join(options.projectRoot || ".", "skills", slug),
2629
- "opencode": path4.join(options.projectRoot || ".", ".opencode", "skills", slug),
2630
- "plugin": path4.join(options.projectRoot || ".", ".claude", "plugins", "prismer", "skills", slug)
2773
+ "claude-code": path5.join(options.projectRoot || ".", ".claude", "skills", slug),
2774
+ "openclaw": path5.join(options.projectRoot || ".", "skills", slug),
2775
+ "opencode": path5.join(options.projectRoot || ".", ".opencode", "skills", slug),
2776
+ "plugin": path5.join(options.projectRoot || ".", ".claude", "plugins", "prismer", "skills", slug)
2631
2777
  } : {
2632
- "claude-code": path4.join(home, ".claude", "skills", slug),
2633
- "openclaw": path4.join(home, ".openclaw", "skills", slug),
2634
- "opencode": path4.join(home, ".config", "opencode", "skills", slug),
2635
- "plugin": path4.join(pluginBase, "skills", slug)
2778
+ "claude-code": path5.join(home, ".claude", "skills", slug),
2779
+ "openclaw": path5.join(home, ".openclaw", "skills", slug),
2780
+ "opencode": path5.join(home, ".config", "opencode", "skills", slug),
2781
+ "plugin": path5.join(pluginBase, "skills", slug)
2636
2782
  };
2637
- const targets = options?.platforms || Object.keys(platformPaths);
2783
+ const targets = options?.platforms ?? Object.keys(platformPaths);
2638
2784
  for (const platform of targets) {
2639
2785
  const dir = platformPaths[platform];
2640
2786
  if (!dir) continue;
2641
2787
  try {
2642
- fs4.mkdirSync(dir, { recursive: true });
2643
- const filePath = path4.join(dir, "SKILL.md");
2644
- fs4.writeFileSync(filePath, content, "utf-8");
2788
+ fs5.mkdirSync(dir, { recursive: true });
2789
+ const filePath = path5.join(dir, "SKILL.md");
2790
+ fs5.writeFileSync(filePath, content, "utf-8");
2645
2791
  localPaths.push(filePath);
2646
2792
  } catch {
2647
2793
  }
2648
2794
  }
2649
2795
  } catch {
2650
2796
  }
2651
- return { ...result, data: { ...result.data, localPaths } };
2797
+ return withLocalPaths(localPaths);
2652
2798
  }
2653
2799
  /**
2654
2800
  * Uninstall a skill and remove local SKILL.md files.
@@ -2656,24 +2802,29 @@ var EvolutionClient = class {
2656
2802
  async uninstallSkillLocal(slugOrId) {
2657
2803
  const result = await this.uninstallSkill(slugOrId);
2658
2804
  const removedPaths = [];
2805
+ const withRemoved = (ok, paths) => ({
2806
+ ok: result.ok,
2807
+ ...result.error ? { error: result.error } : {},
2808
+ data: { uninstalled: ok, removedPaths: paths }
2809
+ });
2659
2810
  const slug = safeSlug(slugOrId);
2660
- if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
2811
+ if (!slug) return withRemoved(result.data?.uninstalled ?? false, removedPaths);
2661
2812
  try {
2662
- const fs4 = await import("fs");
2663
- const path4 = await import("path");
2813
+ const fs5 = await import("fs");
2814
+ const path5 = await import("path");
2664
2815
  const os3 = await import("os");
2665
2816
  const home = os3.homedir();
2666
- const pluginBase = process.env.PRISMER_PLUGIN_DIR || path4.join(home, ".claude", "plugins", "prismer");
2817
+ const pluginBase = process.env.PRISMER_PLUGIN_DIR || path5.join(home, ".claude", "plugins", "prismer");
2667
2818
  const dirs = [
2668
- path4.join(home, ".claude", "skills", slug),
2669
- path4.join(home, ".openclaw", "skills", slug),
2670
- path4.join(home, ".config", "opencode", "skills", slug),
2671
- path4.join(pluginBase, "skills", slug)
2819
+ path5.join(home, ".claude", "skills", slug),
2820
+ path5.join(home, ".openclaw", "skills", slug),
2821
+ path5.join(home, ".config", "opencode", "skills", slug),
2822
+ path5.join(pluginBase, "skills", slug)
2672
2823
  ];
2673
2824
  for (const dir of dirs) {
2674
2825
  try {
2675
- if (fs4.existsSync(dir)) {
2676
- fs4.rmSync(dir, { recursive: true });
2826
+ if (fs5.existsSync(dir)) {
2827
+ fs5.rmSync(dir, { recursive: true });
2677
2828
  removedPaths.push(dir);
2678
2829
  }
2679
2830
  } catch {
@@ -2681,7 +2832,7 @@ var EvolutionClient = class {
2681
2832
  }
2682
2833
  } catch {
2683
2834
  }
2684
- return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
2835
+ return withRemoved(result.data?.uninstalled ?? false, removedPaths);
2685
2836
  }
2686
2837
  /**
2687
2838
  * Sync all installed skills to local filesystem.
@@ -2710,25 +2861,25 @@ var EvolutionClient = class {
2710
2861
  failed++;
2711
2862
  continue;
2712
2863
  }
2713
- const fs4 = await import("fs");
2714
- const path4 = await import("path");
2864
+ const fs5 = await import("fs");
2865
+ const path5 = await import("path");
2715
2866
  const os3 = await import("os");
2716
2867
  const home = os3.homedir();
2717
- const pluginBase = process.env.PRISMER_PLUGIN_DIR || path4.join(home, ".claude", "plugins", "prismer");
2868
+ const pluginBase = process.env.PRISMER_PLUGIN_DIR || path5.join(home, ".claude", "plugins", "prismer");
2718
2869
  const platformPaths = {
2719
- "claude-code": path4.join(home, ".claude", "skills", slug),
2720
- "openclaw": path4.join(home, ".openclaw", "skills", slug),
2721
- "opencode": path4.join(home, ".config", "opencode", "skills", slug),
2722
- "plugin": path4.join(pluginBase, "skills", slug)
2870
+ "claude-code": path5.join(home, ".claude", "skills", slug),
2871
+ "openclaw": path5.join(home, ".openclaw", "skills", slug),
2872
+ "opencode": path5.join(home, ".config", "opencode", "skills", slug),
2873
+ "plugin": path5.join(pluginBase, "skills", slug)
2723
2874
  };
2724
- const targets = options?.platforms || Object.keys(platformPaths);
2875
+ const targets = options?.platforms ?? Object.keys(platformPaths);
2725
2876
  for (const platform of targets) {
2726
2877
  const dir = platformPaths[platform];
2727
2878
  if (!dir) continue;
2728
2879
  try {
2729
- fs4.mkdirSync(dir, { recursive: true });
2730
- const filePath = path4.join(dir, "SKILL.md");
2731
- fs4.writeFileSync(filePath, content, "utf-8");
2880
+ fs5.mkdirSync(dir, { recursive: true });
2881
+ const filePath = path5.join(dir, "SKILL.md");
2882
+ fs5.writeFileSync(filePath, content, "utf-8");
2732
2883
  paths.push(filePath);
2733
2884
  } catch {
2734
2885
  }
@@ -2770,12 +2921,34 @@ var EvolutionClient = class {
2770
2921
  if (since != null) query.since = String(since);
2771
2922
  return this._r("GET", "/api/im/evolution/sync/snapshot", void 0, query);
2772
2923
  }
2773
- /** Bidirectional sync: push local outcomes and pull remote updates */
2924
+ /**
2925
+ * Bidirectional sync: push local outcomes and pull remote updates.
2926
+ *
2927
+ * Accepts either the flat shape (`pushOutcomes` / `pullSince`) used by older
2928
+ * callers or the nested shape (`push` / `pull`) that mirrors the wire format
2929
+ * expected by `POST /api/im/evolution/sync`. The nested shape is preferred for
2930
+ * new code because it lets you pin a scope per-side.
2931
+ */
2774
2932
  async sync(options) {
2775
2933
  const body = {};
2776
- if (options?.pushOutcomes) body.push = { outcomes: options.pushOutcomes };
2777
- if (options?.pullSince != null) body.pull = { since: options.pullSince };
2778
- return this._r("POST", "/api/im/evolution/sync", body);
2934
+ const outcomes = options?.push?.outcomes ?? options?.pushOutcomes;
2935
+ if (outcomes) {
2936
+ body.push = {
2937
+ outcomes,
2938
+ ...options?.push?.scope ? { scope: options.push.scope } : {},
2939
+ ...options?.push?.workspaceId ? { workspaceId: options.push.workspaceId } : {}
2940
+ };
2941
+ }
2942
+ const since = options?.pull?.since ?? options?.pullSince;
2943
+ if (since != null) {
2944
+ body.pull = {
2945
+ since,
2946
+ ...options?.pull?.scope ? { scope: options.pull.scope } : {}
2947
+ };
2948
+ }
2949
+ const query = {};
2950
+ if (options?.scope) query.scope = options.scope;
2951
+ return this._r("POST", "/api/im/evolution/sync", body, query);
2779
2952
  }
2780
2953
  };
2781
2954
  function safeSlug(input) {
@@ -2819,6 +2992,26 @@ var WorkspacesClient = class {
2819
2992
  async archive(workspaceId) {
2820
2993
  return this._r("DELETE", `/api/im/workspaces/${workspaceId}`);
2821
2994
  }
2995
+ /**
2996
+ * Get the workspace's orchestrator agent (Chief of Staff) — readable by any
2997
+ * member. Returns `{ workspace, orchestrator: null }` when no active
2998
+ * appointment exists. See release 200 §4.
2999
+ */
3000
+ async getOrchestrator(workspaceId) {
3001
+ return this._r("GET", `/api/im/workspaces/${workspaceId}/orchestrator`);
3002
+ }
3003
+ /**
3004
+ * Appoint an agent as the workspace's orchestrator. Owner-only. If an
3005
+ * orchestrator is already active, this auto-revokes the previous one in the
3006
+ * same UPDATE.
3007
+ */
3008
+ async appointOrchestrator(workspaceId, agentImUserId) {
3009
+ return this._r("POST", `/api/im/workspaces/${workspaceId}/orchestrator`, { agentImUserId });
3010
+ }
3011
+ /** Revoke the workspace's current orchestrator. Owner-only. Idempotent. */
3012
+ async revokeOrchestrator(workspaceId) {
3013
+ return this._r("DELETE", `/api/im/workspaces/${workspaceId}/orchestrator`);
3014
+ }
2822
3015
  };
2823
3016
  var WorkspaceFilesClient = class {
2824
3017
  constructor(_r) {
@@ -2838,8 +3031,8 @@ var WorkspaceFilesClient = class {
2838
3031
  return this._r("POST", `/api/im/workspaces/${workspaceId}/files`, options);
2839
3032
  }
2840
3033
  /** Soft-delete the active binding at `path`. */
2841
- async delete(workspaceId, path4) {
2842
- return this._r("DELETE", `/api/im/workspaces/${workspaceId}/files`, void 0, { path: path4 });
3034
+ async delete(workspaceId, path5) {
3035
+ return this._r("DELETE", `/api/im/workspaces/${workspaceId}/files`, void 0, { path: path5 });
2843
3036
  }
2844
3037
  /** Daemon delta-sync workspace files since an ISO timestamp. */
2845
3038
  async sync(workspaceId, since) {
@@ -2852,6 +3045,83 @@ var WorkspaceFilesClient = class {
2852
3045
  return this._r("GET", `/api/im/workspaces/${workspaceId}/files/${fileId}/history`);
2853
3046
  }
2854
3047
  };
3048
+ var MAX_IM_ASSET_BYTES = 1024 * 1024 * 1024;
3049
+ var DIRECT_ASSET_UPLOAD_FALLBACK_STATUSES = /* @__PURE__ */ new Set([404, 501, 503]);
3050
+ function toHex(bytes) {
3051
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
3052
+ }
3053
+ async function sha256BytesHex(bytes) {
3054
+ if (globalThis.crypto?.subtle) {
3055
+ const ab = new ArrayBuffer(bytes.byteLength);
3056
+ new Uint8Array(ab).set(bytes);
3057
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", ab);
3058
+ return toHex(new Uint8Array(digest));
3059
+ }
3060
+ const { createHash } = await import("crypto");
3061
+ return createHash("sha256").update(bytes).digest("hex");
3062
+ }
3063
+ function bytesToBlob(bytes, mimeType) {
3064
+ const ab = new ArrayBuffer(bytes.byteLength);
3065
+ new Uint8Array(ab).set(bytes);
3066
+ return new Blob([ab], { type: mimeType });
3067
+ }
3068
+ function isNamedFile(input) {
3069
+ return typeof File !== "undefined" && input instanceof File;
3070
+ }
3071
+ function normalizeStringHeaders(value) {
3072
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3073
+ const out = {};
3074
+ for (const [key, raw] of Object.entries(value)) {
3075
+ if (typeof raw === "string") out[key] = raw;
3076
+ }
3077
+ return Object.keys(out).length > 0 ? out : void 0;
3078
+ }
3079
+ function isDirectAssetUploadPlan(value) {
3080
+ if (!value || typeof value !== "object") return false;
3081
+ const plan = value;
3082
+ if (plan.mode === "single") {
3083
+ return typeof plan.uploadUrl === "string" && typeof plan.bucket === "string" && typeof plan.key === "string";
3084
+ }
3085
+ if (plan.mode === "multipart") {
3086
+ return typeof plan.bucket === "string" && typeof plan.key === "string" && typeof plan.uploadId === "string" && typeof plan.partSizeBytes === "number" && Array.isArray(plan.parts) && plan.parts.every((part) => {
3087
+ const item = part;
3088
+ return typeof item.url === "string" && Number.isInteger(item.partNumber);
3089
+ });
3090
+ }
3091
+ return false;
3092
+ }
3093
+ function assetUploadError(code, message) {
3094
+ return { ok: false, error: { code, message } };
3095
+ }
3096
+ async function normalizeAssetUploadInput(input, options) {
3097
+ let bytes;
3098
+ let fileName;
3099
+ if (typeof input === "string") {
3100
+ const fs5 = await import("fs");
3101
+ const path5 = await import("path");
3102
+ const buf = await fs5.promises.readFile(input);
3103
+ bytes = new Uint8Array(buf);
3104
+ fileName = options.fileName || path5.basename(input);
3105
+ } else if (typeof Blob !== "undefined" && input instanceof Blob) {
3106
+ const ab = await input.arrayBuffer();
3107
+ bytes = new Uint8Array(ab);
3108
+ fileName = options.fileName || (isNamedFile(input) ? input.name : "");
3109
+ if (!fileName) throw new Error("fileName is required when uploading Blob without name");
3110
+ } else if (input instanceof Uint8Array) {
3111
+ bytes = input;
3112
+ fileName = options.fileName || "";
3113
+ if (!fileName) throw new Error("fileName is required when uploading Buffer or Uint8Array");
3114
+ } else {
3115
+ throw new Error("Unsupported input type");
3116
+ }
3117
+ const sizeBytes = bytes.byteLength;
3118
+ if (sizeBytes > MAX_IM_ASSET_BYTES) {
3119
+ throw new Error("Asset exceeds 1 GB cap");
3120
+ }
3121
+ const mimeType = options.mimeType || guessMimeType(fileName);
3122
+ const contentHash = await sha256BytesHex(bytes);
3123
+ return { bytes, fileName, mimeType, sizeBytes, contentHash };
3124
+ }
2855
3125
  var AssetsClient = class {
2856
3126
  constructor(_r, _baseUrl, _fetchFn, _getAuthHeaders) {
2857
3127
  this._r = _r;
@@ -2918,57 +3188,130 @@ var AssetsClient = class {
2918
3188
  };
2919
3189
  }
2920
3190
  /**
2921
- * Upload bytes as an asset (multipart). 100 MB hard cap; >50 MB returns
2922
- * 413 with `USE_PRESIGNED` use S3 presign flow for those (not yet wrapped).
3191
+ * Upload bytes as an asset. Uses direct-to-S3 upload when the server exposes
3192
+ * `/assets/direct-upload/*`; falls back to legacy multipart POST for local
3193
+ * filesystem mode and older deployments. The client always sends SHA-256 for
3194
+ * server-side byte integrity checks. Hard cap: 1 GB.
2923
3195
  */
2924
3196
  async upload(input, options) {
2925
- let bytes;
2926
- let fileName;
2927
- if (typeof input === "string") {
2928
- const fs4 = await import("fs");
2929
- const path4 = await import("path");
2930
- const buf = await fs4.promises.readFile(input);
2931
- bytes = new Uint8Array(buf);
2932
- fileName = options.fileName || path4.basename(input);
2933
- } else if (typeof Blob !== "undefined" && input instanceof Blob) {
2934
- const ab2 = await input.arrayBuffer();
2935
- bytes = new Uint8Array(ab2);
2936
- fileName = options.fileName || (input instanceof File ? input.name : "");
2937
- if (!fileName) throw new Error("fileName is required when uploading Blob without name");
2938
- } else if (input instanceof Uint8Array) {
2939
- bytes = input;
2940
- fileName = options.fileName || "";
2941
- if (!fileName) throw new Error("fileName is required when uploading Buffer or Uint8Array");
2942
- } else {
2943
- throw new Error("Unsupported input type");
3197
+ const normalized = await normalizeAssetUploadInput(input, options);
3198
+ const direct = await this._tryDirectUpload(normalized, options);
3199
+ if (direct) return direct;
3200
+ return this._uploadMultipart(normalized, options);
3201
+ }
3202
+ async _tryDirectUpload(file, options) {
3203
+ const init = await this._postAssetJson("/direct-upload/init", {
3204
+ workspaceId: options.workspaceId,
3205
+ filename: file.fileName,
3206
+ mime: file.mimeType,
3207
+ sizeBytes: file.sizeBytes,
3208
+ contentHash: file.contentHash
3209
+ });
3210
+ if (!init.response.ok) {
3211
+ if (DIRECT_ASSET_UPLOAD_FALLBACK_STATUSES.has(init.response.status)) return null;
3212
+ return init.data && init.data.ok === false ? {
3213
+ ok: false,
3214
+ error: init.data.error ?? {
3215
+ code: "http_error",
3216
+ message: `Direct upload init failed (${init.response.status})`
3217
+ }
3218
+ } : assetUploadError("http_error", `Direct upload init failed (${init.response.status})`);
3219
+ }
3220
+ if (!isDirectAssetUploadPlan(init.data?.data)) return null;
3221
+ const plan = init.data.data;
3222
+ try {
3223
+ const parts = await this._putDirectUploadBytes(plan, file, options.onProgress);
3224
+ const complete = await this._postAssetJson("/direct-upload/complete", {
3225
+ workspaceId: options.workspaceId,
3226
+ filename: file.fileName,
3227
+ mime: file.mimeType,
3228
+ sizeBytes: file.sizeBytes,
3229
+ contentHash: file.contentHash,
3230
+ kind: options.kind,
3231
+ metadata: options.metadata,
3232
+ sourceTaskId: options.sourceTaskId,
3233
+ sourceAgentImUserId: options.sourceAgentImUserId,
3234
+ bucket: plan.bucket,
3235
+ key: plan.key,
3236
+ ...plan.mode === "multipart" ? { uploadId: plan.uploadId, parts: parts ?? [] } : {}
3237
+ });
3238
+ if (!complete.response.ok) {
3239
+ return complete.data && complete.data.ok === false ? complete.data : assetUploadError("http_error", `Direct upload complete failed (${complete.response.status})`);
3240
+ }
3241
+ if (!complete.data) {
3242
+ return assetUploadError("invalid_response", "Direct upload complete returned an empty response");
3243
+ }
3244
+ return complete.data;
3245
+ } catch {
3246
+ return null;
3247
+ }
3248
+ }
3249
+ async _postAssetJson(path5, body) {
3250
+ const response = await this._fetchFn(`${this._baseUrl}/api/im/assets${path5}`, {
3251
+ method: "POST",
3252
+ headers: {
3253
+ ...this._getAuthHeaders(),
3254
+ "Content-Type": "application/json",
3255
+ Accept: "application/json"
3256
+ },
3257
+ body: JSON.stringify(body)
3258
+ });
3259
+ const data = await response.json().catch(() => null);
3260
+ return { response, data };
3261
+ }
3262
+ async _putDirectUploadBytes(plan, file, onProgress) {
3263
+ if (plan.mode === "single") {
3264
+ const response = await this._fetchFn(plan.uploadUrl, {
3265
+ method: plan.method ?? "PUT",
3266
+ headers: normalizeStringHeaders(plan.headers),
3267
+ body: bytesToBlob(file.bytes, file.mimeType)
3268
+ });
3269
+ if (!response.ok) throw new Error(`signed PUT failed (${response.status})`);
3270
+ onProgress?.(file.sizeBytes, file.sizeBytes);
3271
+ return null;
2944
3272
  }
2945
- const mimeType = options.mimeType || guessMimeType(fileName);
2946
- const sizeBytes = bytes.byteLength;
2947
- if (sizeBytes > 100 * 1024 * 1024) {
2948
- throw new Error("Asset exceeds 100 MB cap");
3273
+ const completedParts = [];
3274
+ let uploaded = 0;
3275
+ for (const part of plan.parts) {
3276
+ const start = (part.partNumber - 1) * plan.partSizeBytes;
3277
+ const end = Math.min(start + plan.partSizeBytes, file.sizeBytes);
3278
+ const chunk = file.bytes.slice(start, end);
3279
+ const response = await this._fetchFn(part.url, { method: "PUT", body: bytesToBlob(chunk, file.mimeType) });
3280
+ if (!response.ok) throw new Error(`signed multipart PUT failed for part ${part.partNumber} (${response.status})`);
3281
+ const etag = response.headers.get("etag")?.replace(/^"|"$/g, "");
3282
+ if (!etag) throw new Error(`signed multipart PUT missing ETag for part ${part.partNumber}`);
3283
+ completedParts.push({ partNumber: part.partNumber, etag });
3284
+ uploaded += chunk.byteLength;
3285
+ onProgress?.(uploaded, file.sizeBytes);
2949
3286
  }
3287
+ return completedParts;
3288
+ }
3289
+ async _uploadMultipart(file, options) {
3290
+ const { bytes, fileName, mimeType, sizeBytes, contentHash } = file;
2950
3291
  const formData = new FormData();
2951
- const ab = new ArrayBuffer(bytes.byteLength);
2952
- new Uint8Array(ab).set(bytes);
2953
- formData.append("file", new Blob([ab], { type: mimeType }), fileName);
3292
+ formData.append("file", bytesToBlob(bytes, mimeType), fileName);
2954
3293
  formData.append("workspaceId", options.workspaceId);
2955
3294
  if (options.kind) formData.append("kind", options.kind);
2956
3295
  if (options.sourceAgentImUserId) formData.append("sourceAgentImUserId", options.sourceAgentImUserId);
2957
3296
  if (options.sourceTaskId) formData.append("sourceTaskId", options.sourceTaskId);
2958
3297
  if (options.metadata) formData.append("metadata", JSON.stringify(options.metadata));
3298
+ formData.append("contentSha256", contentHash);
2959
3299
  const resp = await this._fetchFn(`${this._baseUrl}/api/im/assets`, {
2960
3300
  method: "POST",
2961
3301
  body: formData,
2962
- headers: this._getAuthHeaders()
3302
+ headers: {
3303
+ ...this._getAuthHeaders(),
3304
+ "X-Content-Sha256": contentHash
3305
+ }
2963
3306
  });
2964
- options.onProgress?.(sizeBytes, sizeBytes);
2965
3307
  const data = await resp.json().catch(() => ({}));
2966
3308
  if (!resp.ok) {
2967
3309
  return {
2968
3310
  ok: false,
2969
- error: data?.error || { code: "HTTP_ERROR", message: `Upload failed (${resp.status})` }
3311
+ error: data?.error || { code: "http_error", message: `Upload failed (${resp.status})` }
2970
3312
  };
2971
3313
  }
3314
+ options.onProgress?.(sizeBytes, sizeBytes);
2972
3315
  return data;
2973
3316
  }
2974
3317
  };
@@ -3088,11 +3431,11 @@ var FilesClient = class {
3088
3431
  let bytes;
3089
3432
  let fileName;
3090
3433
  if (typeof input === "string") {
3091
- const fs4 = await import("fs");
3092
- const path4 = await import("path");
3093
- const buf = await fs4.promises.readFile(input);
3434
+ const fs5 = await import("fs");
3435
+ const path5 = await import("path");
3436
+ const buf = await fs5.promises.readFile(input);
3094
3437
  bytes = new Uint8Array(buf);
3095
- fileName = opts?.fileName || path4.basename(input);
3438
+ fileName = opts?.fileName || path5.basename(input);
3096
3439
  } else if (typeof Blob !== "undefined" && input instanceof Blob) {
3097
3440
  const ab = await input.arrayBuffer();
3098
3441
  bytes = new Uint8Array(ab);
@@ -3334,6 +3677,7 @@ var IMRealtimeClient = class {
3334
3677
  };
3335
3678
  var IMClient = class {
3336
3679
  constructor(request2, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
3680
+ this._request = request2;
3337
3681
  this.account = new AccountClient(request2);
3338
3682
  this.direct = new DirectClient(request2);
3339
3683
  this.groups = new GroupsClient(request2);
@@ -3360,7 +3704,7 @@ var IMClient = class {
3360
3704
  }
3361
3705
  /** IM health check */
3362
3706
  async health() {
3363
- return this.account["_r"]("GET", "/api/im/health");
3707
+ return this._request("GET", "/api/im/health");
3364
3708
  }
3365
3709
  /** Get workspace superset view with slot filtering */
3366
3710
  async getWorkspace(scope, slots, includeContent) {
@@ -3368,7 +3712,22 @@ var IMClient = class {
3368
3712
  if (scope) params.set("scope", scope);
3369
3713
  if (slots?.length) params.set("slots", slots.join(","));
3370
3714
  if (includeContent) params.set("includeContent", "true");
3371
- return this.workspace["_r"]("GET", `/api/im/workspace/view?${params}`);
3715
+ return this._request("GET", `/api/im/workspace/view?${params}`);
3716
+ }
3717
+ /**
3718
+ * Issue a typed IM API request via the shared `RequestFn` pipeline.
3719
+ *
3720
+ * Use this when you need to hit an IM endpoint that isn't (yet) exposed by a
3721
+ * sub-client (e.g. `/api/im/approvals`). Same auth + offline routing + retry
3722
+ * behaviour as the typed sub-clients.
3723
+ *
3724
+ * @example
3725
+ * const res = await client.im.request<ApprovalCreateResponse>(
3726
+ * 'POST', '/api/im/approvals', { category, title, context, options },
3727
+ * );
3728
+ */
3729
+ async request(method, path5, body, query) {
3730
+ return this._request(method, path5, body, query);
3372
3731
  }
3373
3732
  };
3374
3733
  var PrismerClient = class {
@@ -3412,20 +3771,20 @@ var PrismerClient = class {
3412
3771
  let imRequest = this._offlineManager ? (m, p, b, q) => this._offlineManager.dispatch(m, p, b, q) : (m, p, b, q) => this._request(m, p, b, q);
3413
3772
  if (config.identity) {
3414
3773
  const baseRequest = imRequest;
3415
- imRequest = (method, path4, body, query) => {
3416
- if (method === "POST" && path4.includes("/messages") && body) {
3774
+ imRequest = (method, path5, body, query) => {
3775
+ if (method === "POST" && path5.includes("/messages") && body) {
3417
3776
  const b = body;
3418
3777
  if (!b.signature && !b.skipSigning) {
3419
3778
  const ready = this._identityReady || Promise.resolve();
3420
3779
  return ready.then(() => {
3421
3780
  if (this._identity) {
3422
- return this._signAndSend(baseRequest, method, path4, b, query);
3781
+ return this._signAndSend(baseRequest, method, path5, b, query);
3423
3782
  }
3424
- return baseRequest(method, path4, body, query);
3783
+ return baseRequest(method, path5, body, query);
3425
3784
  });
3426
3785
  }
3427
3786
  }
3428
- return baseRequest(method, path4, body, query);
3787
+ return baseRequest(method, path5, body, query);
3429
3788
  };
3430
3789
  }
3431
3790
  this.im = new IMClient(
@@ -3436,6 +3795,10 @@ var PrismerClient = class {
3436
3795
  this._offlineManager,
3437
3796
  config.community ?? null
3438
3797
  );
3798
+ this.workspaces = this.im.workspaces;
3799
+ this.workspaceFiles = this.im.workspaceFiles;
3800
+ this.assets = this.im.assets;
3801
+ this.evolution = this.im.evolution;
3439
3802
  }
3440
3803
  /** Wait for identity to be ready (useful for tests or explicit await) */
3441
3804
  async ensureIdentity() {
@@ -3443,9 +3806,9 @@ var PrismerClient = class {
3443
3806
  return this._identity;
3444
3807
  }
3445
3808
  /** Auto-sign a message body and send (v1.8.0 S1) */
3446
- async _signAndSend(baseRequest, method, path4, body, query) {
3809
+ async _signAndSend(baseRequest, method, path5, body, query) {
3447
3810
  if (this._identityReady) await this._identityReady;
3448
- if (!this._identity) return baseRequest(method, path4, body, query);
3811
+ if (!this._identity) return baseRequest(method, path5, body, query);
3449
3812
  const content = body.content || "";
3450
3813
  const contentHashBytes = new Uint8Array(
3451
3814
  await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
@@ -3455,7 +3818,7 @@ var PrismerClient = class {
3455
3818
  const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
3456
3819
  const payloadBytes = new TextEncoder().encode(payload);
3457
3820
  const signature = await this._identity.sign(payloadBytes);
3458
- return baseRequest(method, path4, {
3821
+ return baseRequest(method, path5, {
3459
3822
  ...body,
3460
3823
  secVersion: 1,
3461
3824
  senderDid: this._identity.did,
@@ -3484,14 +3847,47 @@ var PrismerClient = class {
3484
3847
  await this._offlineManager.destroy();
3485
3848
  }
3486
3849
  }
3850
+ /**
3851
+ * Issue an authenticated raw HTTP request against the configured base URL,
3852
+ * returning the underlying `Response` so callers can inspect headers
3853
+ * (`Content-Range`, `Content-Length`, etc.) and stream the body.
3854
+ *
3855
+ * The path may be absolute (`/api/...`) or a full URL — full URLs are used
3856
+ * verbatim (useful for following 302 redirects), otherwise the path is
3857
+ * appended to the client's configured `baseUrl`. Authorization + `X-IM-Agent`
3858
+ * headers are added automatically; caller-supplied headers in `init.headers`
3859
+ * override them on collision.
3860
+ *
3861
+ * Use this for binary downloads / partial fetches; for normal JSON-envelope
3862
+ * IM requests use `client.im.request()` (typed) or the typed sub-clients.
3863
+ */
3864
+ async fetchAuthed(url, init) {
3865
+ const fullUrl = /^https?:\/\//i.test(url) ? url : `${this.baseUrl}${url}`;
3866
+ const authHeaders = this._getAuthHeaders();
3867
+ const callerHeaders = {};
3868
+ if (init?.headers) {
3869
+ const h = init.headers;
3870
+ if (h instanceof Headers) {
3871
+ h.forEach((v, k) => {
3872
+ callerHeaders[k] = v;
3873
+ });
3874
+ } else if (Array.isArray(h)) {
3875
+ for (const [k, v] of h) callerHeaders[k] = v;
3876
+ } else {
3877
+ Object.assign(callerHeaders, h);
3878
+ }
3879
+ }
3880
+ const headers = { ...authHeaders, ...callerHeaders };
3881
+ return this.fetchFn(fullUrl, { ...init ?? {}, headers });
3882
+ }
3487
3883
  // --------------------------------------------------------------------------
3488
3884
  // Internal request helper
3489
3885
  // --------------------------------------------------------------------------
3490
- async _request(method, path4, body, query, _isRetry) {
3886
+ async _request(method, path5, body, query, _isRetry) {
3491
3887
  const controller = new AbortController();
3492
3888
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3493
3889
  try {
3494
- let url = `${this.baseUrl}${path4}`;
3890
+ let url = `${this.baseUrl}${path5}`;
3495
3891
  if (query && Object.keys(query).length > 0) {
3496
3892
  url += "?" + new URLSearchParams(query).toString();
3497
3893
  }
@@ -3509,29 +3905,29 @@ var PrismerClient = class {
3509
3905
  }
3510
3906
  const response = await this.fetchFn(url, init);
3511
3907
  const data = await response.json();
3512
- if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !path4.includes("/token/refresh")) {
3908
+ if (response.status === 401 && this.apiKey.startsWith("eyJ") && !_isRetry && !path5.includes("/token/refresh")) {
3513
3909
  try {
3514
3910
  const refreshRes = await this._request("POST", "/api/im/token/refresh", void 0, void 0, true);
3515
3911
  if (refreshRes?.ok && refreshRes?.data?.token) {
3516
3912
  this.apiKey = refreshRes.data.token;
3517
- return this._request(method, path4, body, query, true);
3913
+ return this._request(method, path5, body, query, true);
3518
3914
  }
3519
3915
  } catch {
3520
3916
  }
3521
3917
  }
3522
3918
  if (!response.ok) {
3523
- const err = data.error || { code: "HTTP_ERROR", message: `Request failed with status ${response.status}` };
3919
+ const err = data.error || { code: "http_error", message: `Request failed with status ${response.status}` };
3524
3920
  return { ...data, success: false, ok: false, error: err };
3525
3921
  }
3526
3922
  return data;
3527
- } catch (error2) {
3528
- if (error2 instanceof Error && error2.name === "AbortError") {
3529
- return { success: false, ok: false, error: { code: "TIMEOUT", message: "Request timed out" } };
3923
+ } catch (error) {
3924
+ if (error instanceof Error && error.name === "AbortError") {
3925
+ return { success: false, ok: false, error: { code: "timeout", message: "Request timed out" } };
3530
3926
  }
3531
3927
  return {
3532
3928
  success: false,
3533
3929
  ok: false,
3534
- error: { code: "NETWORK_ERROR", message: error2 instanceof Error ? error2.message : "Unknown error" }
3930
+ error: { code: "cloud_unreachable", message: error instanceof Error ? error.message : "Unknown error" }
3535
3931
  };
3536
3932
  } finally {
3537
3933
  clearTimeout(timeoutId);
@@ -3601,166 +3997,554 @@ var PrismerClient = class {
3601
3997
  }
3602
3998
  };
3603
3999
 
3604
- // src/ui.ts
3605
- var pc = __toESM(require("picocolors"));
3606
- var clack = __toESM(require("@clack/prompts"));
4000
+ // src/cli-ui.ts
3607
4001
  var fs = __toESM(require("fs"));
3608
4002
  var path = __toESM(require("path"));
3609
- var isTTY = !!process.stdout.isTTY || !!process.env.FORCE_COLOR;
3610
- function displayBanner() {
3611
- if (!isTTY) return;
3612
- let iconPath;
4003
+ var import_node_url = require("url");
4004
+ var import_meta = {};
4005
+ var BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4006
+ var COMPACT_BANNER = ["\u25C7 PRISMER", " Cloud CLI"];
4007
+ function thisDirname() {
4008
+ if (typeof __dirname !== "undefined") return __dirname;
3613
4009
  try {
3614
- iconPath = path.resolve(__dirname, "..", "icon");
3615
- if (!fs.existsSync(iconPath)) {
3616
- iconPath = path.resolve(__dirname, "..", "..", "icon");
3617
- }
3618
- if (!fs.existsSync(iconPath)) return;
3619
- const raw = fs.readFileSync(iconPath, "utf-8");
3620
- const lines = raw.split("\n");
3621
- const termWidth = process.stdout.columns || 80;
3622
- const colorized = lines.map((line) => {
3623
- const trimmed = line.trimEnd();
3624
- if (!trimmed) return "";
3625
- let result = "";
3626
- let visibleLen = 0;
3627
- for (const ch of trimmed) {
3628
- if (visibleLen >= termWidth - 1) break;
3629
- if (ch === "\u2592") {
3630
- result += pc.cyan(ch);
3631
- } else if (ch === "\u2593") {
3632
- result += pc.white(ch);
3633
- } else if (ch === "\u2588") {
3634
- result += pc.white(ch);
3635
- } else {
3636
- result += ch;
3637
- }
3638
- visibleLen++;
3639
- }
3640
- return result;
3641
- });
3642
- while (colorized.length > 0 && colorized[colorized.length - 1].trim() === "") {
3643
- colorized.pop();
3644
- }
3645
- console.log(colorized.join("\n"));
3646
- console.log();
4010
+ const metaUrl = typeof import_meta !== "undefined" ? import_meta.url : void 0;
4011
+ if (metaUrl) return path.dirname((0, import_node_url.fileURLToPath)(metaUrl));
3647
4012
  } catch {
3648
4013
  }
4014
+ return process.cwd();
3649
4015
  }
3650
- var SYMBOLS = {
3651
- success: isTTY ? "\u2713" : "[ok]",
3652
- //
3653
- error: isTTY ? "\u2717" : "[error]",
3654
- //
3655
- warn: isTTY ? "\u26A0" : "[warn]",
3656
- //
3657
- info: isTTY ? "\u2139" : "[info]"
3658
- //
3659
- };
3660
- function success(msg) {
3661
- console.log(pc.green(`${SYMBOLS.success} ${msg}`));
3662
- }
3663
- function error(msg) {
3664
- console.error(pc.red(`${SYMBOLS.error} ${msg}`));
3665
- }
3666
- function warn(msg) {
3667
- console.warn(pc.yellow(`${SYMBOLS.warn} ${msg}`));
3668
- }
3669
- function info(msg) {
3670
- console.log(pc.blue(`${SYMBOLS.info} ${msg}`));
3671
- }
3672
- function dim2(msg) {
3673
- console.log(pc.dim(msg));
4016
+ function findIconPath(size = "big") {
4017
+ const name = size === "big" ? "icon" : "smallicon";
4018
+ const here = thisDirname();
4019
+ const candidates = [
4020
+ // npm-installed: node_modules/@prismer/sdk/dist/cli.js → ../icon (SDK ships at pkg root)
4021
+ path.resolve(here, "..", name),
4022
+ // alternate dist layout (sub-bundle): dist/bin/cli.js → ../../icon
4023
+ path.resolve(here, "..", "..", name),
4024
+ // source/typecheck: src/cli-ui.ts → ../icon
4025
+ path.resolve(here, "..", name),
4026
+ // dev mode: cwd happens to be sdk root
4027
+ path.resolve(process.cwd(), name),
4028
+ path.resolve(process.cwd(), "sdk/prismer-cloud/typescript", name),
4029
+ // legacy assets/ layout (runtime parity, in case SDK ever adopts it)
4030
+ path.resolve(here, "..", "assets", name),
4031
+ path.resolve(here, "..", "..", "assets", name)
4032
+ ];
4033
+ for (const candidate of candidates) {
4034
+ try {
4035
+ if (fs.existsSync(candidate)) return candidate;
4036
+ } catch {
4037
+ }
4038
+ }
4039
+ return null;
3674
4040
  }
3675
- async function withSpinner(message, fn) {
3676
- if (!isTTY) {
3677
- return fn();
4041
+ var UI = class {
4042
+ constructor(opts) {
4043
+ this.mode = opts?.mode ?? "pretty";
4044
+ this.stream = opts?.stream ?? process.stdout;
4045
+ this.errStream = opts?.errStream ?? process.stderr;
4046
+ if (opts?.color !== void 0) {
4047
+ this.colorEnabled = opts.color;
4048
+ } else {
4049
+ const isTTY = this.stream.isTTY === true;
4050
+ const noColor = Boolean(process.env["NO_COLOR"]);
4051
+ this.colorEnabled = isTTY && !noColor;
4052
+ }
3678
4053
  }
3679
- const s = clack.spinner();
3680
- s.start(message);
3681
- try {
3682
- const result = await fn();
3683
- s.stop(pc.green(`${SYMBOLS.success} ${message}`));
3684
- return result;
3685
- } catch (err) {
3686
- s.stop(pc.red(`${SYMBOLS.error} ${message}`));
3687
- throw err;
4054
+ // ---- Internal color helpers ----
4055
+ ansi(open, close, text) {
4056
+ if (!this.colorEnabled) return text;
4057
+ return `\x1B[${open}m${text}\x1B[${close}m`;
3688
4058
  }
3689
- }
3690
- function table(headers, rows) {
3691
- if (headers.length === 0) return;
3692
- const widths = headers.map((h, i) => {
3693
- const dataMax = rows.reduce((max, row) => Math.max(max, (row[i] || "").length), 0);
3694
- return Math.max(h.length, dataMax);
3695
- });
3696
- const PAD = 2;
3697
- const headerLine = headers.map((h, i) => h.padEnd(widths[i] + PAD)).join("");
3698
- if (isTTY) {
3699
- console.log(pc.bold(headerLine));
3700
- const separator = widths.map((w) => "\u2500".repeat(w)).join(" ");
3701
- console.log(pc.dim(separator));
3702
- } else {
3703
- console.log(headerLine);
3704
- const separator = widths.map((w) => "-".repeat(w)).join(" ");
3705
- console.log(separator);
4059
+ green(t) {
4060
+ return this.ansi(32, 39, t);
3706
4061
  }
3707
- for (const row of rows) {
3708
- const line = headers.map((_, i) => (row[i] || "").padEnd(widths[i] + PAD)).join("");
3709
- console.log(line);
4062
+ red(t) {
4063
+ return this.ansi(31, 39, t);
3710
4064
  }
3711
- }
3712
- function keyValue(pairs) {
3713
- const keys = Object.keys(pairs);
3714
- if (keys.length === 0) return;
3715
- const maxKeyLen = keys.reduce((max, k) => Math.max(max, k.length), 0);
3716
- for (const key of keys) {
3717
- const label = isTTY ? pc.bold(key.padEnd(maxKeyLen)) : key.padEnd(maxKeyLen);
3718
- const value = pairs[key];
3719
- console.log(` ${label} ${value}`);
4065
+ yellow(t) {
4066
+ return this.ansi(33, 39, t);
3720
4067
  }
3721
- }
3722
-
3723
- // src/commands/im.ts
3724
- function register(parent, getIMClient2, _getAPIClient) {
3725
- const im = parent.command("im").description("IM messaging, groups, conversations, and credits");
3726
- im.command("send <user-id> <message>").description("Send a direct message to a user").option("-t, --type <type>", "Message type: text, markdown, code, file, etc.", "text").option("--reply-to <msg-id>", "Reply to a specific message ID (parentId)").option("--json", "Output raw JSON response").action(async (userId, message, opts) => {
3727
- const client = getIMClient2();
3728
- try {
3729
- const sendOpts = {
3730
- type: opts.type
3731
- };
3732
- if (opts.replyTo) sendOpts.parentId = opts.replyTo;
3733
- const res = await client.im.direct.send(userId, message, sendOpts);
3734
- if (!res.ok) {
3735
- process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
3736
- `);
3737
- process.exit(1);
4068
+ cyan(t) {
4069
+ return this.ansi(36, 39, t);
4070
+ }
4071
+ dim(t) {
4072
+ return this.ansi(2, 22, t);
4073
+ }
4074
+ bold(t) {
4075
+ return this.ansi(1, 22, t);
4076
+ }
4077
+ gray(t) {
4078
+ return this.ansi(90, 39, t);
4079
+ }
4080
+ brandMark() {
4081
+ return this.cyan("\u25C7");
4082
+ }
4083
+ colorBrandLine(line) {
4084
+ let out = "";
4085
+ for (const ch of line) {
4086
+ if (ch === "\u2592") {
4087
+ out += this.cyan(ch);
4088
+ } else if (ch === "\u2593") {
4089
+ out += this.dim(ch);
4090
+ } else {
4091
+ out += ch;
3738
4092
  }
3739
- if (opts.json) {
3740
- process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
3741
- return;
4093
+ }
4094
+ return out;
4095
+ }
4096
+ // ---- Core write helpers ----
4097
+ write(text) {
4098
+ this.stream.write(text);
4099
+ }
4100
+ writeErr(text) {
4101
+ this.errStream.write(text);
4102
+ }
4103
+ // ---- Level 1: Header ----
4104
+ header(text) {
4105
+ if (this.mode === "json") return;
4106
+ const prefix = text.startsWith("Prismer") ? this.brandMark() + " " : "";
4107
+ this.write(prefix + this.bold(text) + "\n");
4108
+ }
4109
+ smallHeader(subtitle) {
4110
+ if (this.mode === "json" || this.mode === "quiet") return;
4111
+ const iconPath = findIconPath("small");
4112
+ if (iconPath !== null) {
4113
+ try {
4114
+ const raw = fs.readFileSync(iconPath, "utf-8").replace(/\n+$/, "");
4115
+ for (const line of raw.split("\n")) {
4116
+ this.write(this.cyan(line) + "\n");
4117
+ }
4118
+ } catch {
4119
+ this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
3742
4120
  }
3743
- process.stdout.write(`Message sent (conversationId: ${res.data?.conversationId})
3744
- `);
3745
- } catch (err) {
3746
- process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
3747
- `);
3748
- process.exit(1);
4121
+ } else {
4122
+ this.write(this.brandMark() + " " + this.bold("Prismer") + "\n");
3749
4123
  }
3750
- });
3751
- im.command("messages <user-id>").description("View direct message history with a user").option("-n, --limit <n>", "Max number of messages to fetch", "20").option("--json", "Output raw JSON response").action(async (userId, opts) => {
3752
- const client = getIMClient2();
3753
- try {
3754
- const res = await client.im.direct.getMessages(userId, { limit: parseInt(opts.limit, 10) });
3755
- if (!res.ok) {
3756
- process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
3757
- `);
3758
- process.exit(1);
4124
+ if (subtitle !== void 0 && subtitle.length > 0) {
4125
+ this.write(this.dim(" " + subtitle) + "\n");
4126
+ }
4127
+ this.blank();
4128
+ }
4129
+ banner(subtitle, opts) {
4130
+ if (this.mode === "json" || this.mode === "quiet") return;
4131
+ const envColumns = process.env["COLUMNS"] !== void 0 ? parseInt(process.env["COLUMNS"], 10) : NaN;
4132
+ const width = this.stream.columns ?? process.stdout.columns ?? (Number.isFinite(envColumns) ? envColumns : 80);
4133
+ const iconPath = findIconPath("big");
4134
+ const shouldUseFull = opts?.full === true || width >= 120;
4135
+ if (shouldUseFull && iconPath !== null) {
4136
+ try {
4137
+ const raw = fs.readFileSync(iconPath, "utf-8");
4138
+ const lines = raw.split("\n");
4139
+ for (const line of lines) {
4140
+ const stripped = line.trimEnd();
4141
+ if (stripped.length === 0) {
4142
+ this.write("\n");
4143
+ continue;
4144
+ }
4145
+ const clipped = stripped.length >= width ? stripped.slice(0, Math.max(width - 1, 1)) : stripped;
4146
+ this.write(this.colorBrandLine(clipped) + "\n");
4147
+ }
4148
+ } catch {
4149
+ this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
4150
+ this.write(this.dim(COMPACT_BANNER[1] ?? " Cloud CLI") + "\n");
3759
4151
  }
3760
- const msgs = res.data || [];
3761
- if (opts.json) {
3762
- process.stdout.write(JSON.stringify(msgs, null, 2) + "\n");
3763
- return;
4152
+ } else {
4153
+ this.write(this.cyan(COMPACT_BANNER[0] ?? "\u25C7 PRISMER") + "\n");
4154
+ this.write(this.dim(COMPACT_BANNER[1] ?? " Cloud CLI") + "\n");
4155
+ }
4156
+ if (subtitle !== void 0 && subtitle.length > 0) {
4157
+ this.write(this.dim(" " + subtitle) + "\n");
4158
+ }
4159
+ this.blank();
4160
+ }
4161
+ // ---- Level 2: Primary data ----
4162
+ blank() {
4163
+ if (this.mode === "json") return;
4164
+ this.write("\n");
4165
+ }
4166
+ line(text) {
4167
+ if (this.mode === "json") return;
4168
+ this.write(text + "\n");
4169
+ }
4170
+ info(text) {
4171
+ this.line(text);
4172
+ }
4173
+ // ---- Level 3: Secondary ----
4174
+ secondary(text, indent = 2) {
4175
+ if (this.mode === "json") return;
4176
+ this.write(" ".repeat(indent) + this.dim(text) + "\n");
4177
+ }
4178
+ // ---- Level 4: Action tips ----
4179
+ tip(text) {
4180
+ if (this.mode === "json") return;
4181
+ this.write(this.cyan("Tip:") + " " + text + "\n");
4182
+ }
4183
+ next(text) {
4184
+ if (this.mode === "json") return;
4185
+ this.write(this.cyan("Next:") + " " + text + "\n");
4186
+ }
4187
+ // ---- Level 5: Status indicators ----
4188
+ ok(text, detail) {
4189
+ if (this.mode === "json") return;
4190
+ const suffix = detail ? " " + this.dim(detail) : "";
4191
+ this.write(" " + this.green("\u2713") + " " + text + suffix + "\n");
4192
+ }
4193
+ success(text, detail) {
4194
+ this.ok(text, detail);
4195
+ }
4196
+ fail(text, detail) {
4197
+ if (this.mode === "json") return;
4198
+ const suffix = detail ? " " + this.dim(detail) : "";
4199
+ this.write(" " + this.red("\u2717") + " " + text + suffix + "\n");
4200
+ }
4201
+ online(text) {
4202
+ if (this.mode === "json") return;
4203
+ this.write(" " + this.green("\u25CF") + " " + text + "\n");
4204
+ }
4205
+ offline(text) {
4206
+ if (this.mode === "json") return;
4207
+ this.write(" " + this.gray("\u25CB") + " " + text + "\n");
4208
+ }
4209
+ notInstalled(text) {
4210
+ if (this.mode === "json") return;
4211
+ this.write(" " + this.dim("\xB7") + " " + this.dim(text) + "\n");
4212
+ }
4213
+ pending(text) {
4214
+ if (this.mode === "json") return;
4215
+ this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
4216
+ }
4217
+ warn(text, detail) {
4218
+ if (this.mode === "json") return;
4219
+ const suffix = detail ? " " + this.dim(detail) : "";
4220
+ this.write(" " + this.yellow("!") + " " + text + suffix + "\n");
4221
+ }
4222
+ // ---- Level 6: Error block ----
4223
+ error(what, cause, fix) {
4224
+ if (this.mode === "json") return;
4225
+ this.writeErr(this.red("\u2717") + " " + what + "\n");
4226
+ if (cause !== void 0) {
4227
+ this.writeErr(" " + this.dim("Cause:") + " " + this.dim(cause) + "\n");
4228
+ }
4229
+ if (fix !== void 0) {
4230
+ this.writeErr(" " + this.cyan("Fix:") + " " + fix + "\n");
4231
+ }
4232
+ }
4233
+ table(rowsOrOpts, maybeOpts) {
4234
+ if (this.mode === "json") return;
4235
+ const rows = Array.isArray(rowsOrOpts) ? rowsOrOpts : rowsOrOpts.rows;
4236
+ const opts = Array.isArray(rowsOrOpts) ? maybeOpts : { columns: rowsOrOpts.columns, maxWidth: rowsOrOpts.maxWidth };
4237
+ if (!opts) throw new Error("table() requires columns");
4238
+ const maxWidth = opts.maxWidth ?? (process.stdout.columns || 80);
4239
+ const cols = opts.columns;
4240
+ const widths = cols.map((col) => col.length);
4241
+ for (const row of rows) {
4242
+ cols.forEach((col, i) => {
4243
+ const val = row[col] ?? "";
4244
+ const w = widths[i] ?? 0;
4245
+ if (val.length > w) widths[i] = val.length;
4246
+ });
4247
+ }
4248
+ const totalWidth = widths.reduce((a, b) => a + b, 0) + (cols.length - 1) * 2 + 2;
4249
+ if (totalWidth > maxWidth) {
4250
+ for (let i = 0; i < rows.length; i++) {
4251
+ const row = rows[i];
4252
+ if (!row) continue;
4253
+ for (const col of cols) {
4254
+ const val = row[col] ?? "";
4255
+ this.write(" " + this.bold(col + ":") + " " + val + "\n");
4256
+ }
4257
+ if (i < rows.length - 1) this.write("\n");
4258
+ }
4259
+ return;
4260
+ }
4261
+ const header = cols.map((col, i) => col.toUpperCase().padEnd(widths[i] ?? col.length)).join(" ");
4262
+ this.write(" " + this.dim(header) + "\n");
4263
+ for (const row of rows) {
4264
+ const line = cols.map((col, i) => (row[col] ?? "").padEnd(widths[i] ?? col.length)).join(" ");
4265
+ this.write(" " + line + "\n");
4266
+ }
4267
+ }
4268
+ // ---- Spinner ----
4269
+ spinner(text) {
4270
+ if (this.mode === "quiet" || this.mode === "json") {
4271
+ return {
4272
+ update() {
4273
+ },
4274
+ stop() {
4275
+ }
4276
+ };
4277
+ }
4278
+ const isTTY = this.stream.isTTY === true;
4279
+ if (!isTTY || !this.colorEnabled) {
4280
+ this.write(" " + this.yellow("\u27F3") + " " + text + "\n");
4281
+ return {
4282
+ update: (t) => {
4283
+ this.write(" " + this.yellow("\u27F3") + " " + t + "\n");
4284
+ },
4285
+ stop: (final) => {
4286
+ if (final) this.write(" " + this.green("\u2713") + " " + final + "\n");
4287
+ }
4288
+ };
4289
+ }
4290
+ let current = text;
4291
+ let frameIdx = 0;
4292
+ let stopped = false;
4293
+ const write = this.write.bind(this);
4294
+ const colorFn = this.yellow.bind(this);
4295
+ const greenFn = this.green.bind(this);
4296
+ function renderFrame() {
4297
+ const frame = BRAILLE_FRAMES[frameIdx % BRAILLE_FRAMES.length] ?? "\u280B";
4298
+ const line = " " + colorFn(frame) + " " + current;
4299
+ write("\r" + line);
4300
+ frameIdx++;
4301
+ }
4302
+ renderFrame();
4303
+ const timer = setInterval(renderFrame, 80);
4304
+ return {
4305
+ update(t) {
4306
+ if (stopped) return;
4307
+ current = t;
4308
+ },
4309
+ stop(final) {
4310
+ if (stopped) return;
4311
+ stopped = true;
4312
+ clearInterval(timer);
4313
+ write("\r\x1B[2K");
4314
+ if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
4315
+ }
4316
+ };
4317
+ }
4318
+ // ---- Progress bar ----
4319
+ progress(text, total) {
4320
+ if (this.mode === "quiet" || this.mode === "json") {
4321
+ return {
4322
+ update() {
4323
+ },
4324
+ stop() {
4325
+ }
4326
+ };
4327
+ }
4328
+ const isTTY = this.stream.isTTY === true;
4329
+ const start = Date.now();
4330
+ const write = this.write.bind(this);
4331
+ const colorFn = this.cyan.bind(this);
4332
+ const dimFn = this.dim.bind(this);
4333
+ const greenFn = this.green.bind(this);
4334
+ let last = 0;
4335
+ let lastDetail = "";
4336
+ let stopped = false;
4337
+ const render = () => {
4338
+ if (stopped) return;
4339
+ const frac = total > 0 ? Math.min(1, Math.max(0, last / total)) : 0;
4340
+ const pct = Math.floor(frac * 100);
4341
+ const width = 20;
4342
+ const filled = Math.floor(frac * width);
4343
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
4344
+ const elapsed = (Date.now() - start) / 1e3;
4345
+ const eta = frac > 0.01 ? Math.max(0, elapsed / frac - elapsed) : 0;
4346
+ const etaStr = frac >= 1 ? "" : ` \xB7 ${eta < 1 ? "<1s" : Math.round(eta) + "s"} left`;
4347
+ const detailStr = lastDetail ? ` \xB7 ${lastDetail}` : "";
4348
+ const line = ` ${text} [${colorFn(bar)}] ${String(pct).padStart(3)}%${detailStr}${dimFn(etaStr)}`;
4349
+ if (isTTY && this.colorEnabled) {
4350
+ write("\r\x1B[2K" + line);
4351
+ } else {
4352
+ write(line + "\n");
4353
+ }
4354
+ };
4355
+ render();
4356
+ return {
4357
+ update: (current, detail) => {
4358
+ if (stopped) return;
4359
+ last = current;
4360
+ if (detail !== void 0) lastDetail = detail;
4361
+ render();
4362
+ },
4363
+ stop: (final) => {
4364
+ if (stopped) return;
4365
+ stopped = true;
4366
+ if (isTTY && this.colorEnabled) write("\r\x1B[2K");
4367
+ if (final) write(" " + greenFn("\u2713") + " " + final + "\n");
4368
+ }
4369
+ };
4370
+ }
4371
+ // ---- JSON output ----
4372
+ json(payload, opts) {
4373
+ const indent = opts?.pretty ? 2 : void 0;
4374
+ this.write(JSON.stringify(payload, null, indent) + "\n");
4375
+ }
4376
+ result(pretty, jsonPayload) {
4377
+ if (this.mode === "pretty") {
4378
+ pretty();
4379
+ } else {
4380
+ this.json(jsonPayload);
4381
+ }
4382
+ }
4383
+ };
4384
+ var _ui = null;
4385
+ function getUI() {
4386
+ if (!_ui) _ui = new UI();
4387
+ return _ui;
4388
+ }
4389
+ function setUI(ui) {
4390
+ _ui = ui;
4391
+ }
4392
+ function applyCommonFlags(argv) {
4393
+ let mode = "pretty";
4394
+ const isTTY = process.stdout.isTTY === true;
4395
+ const noColorEnv = Boolean(process.env["NO_COLOR"]);
4396
+ let color = isTTY && !noColorEnv;
4397
+ const rest = [];
4398
+ for (const arg of argv) {
4399
+ switch (arg) {
4400
+ case "--no-color":
4401
+ color = false;
4402
+ break;
4403
+ case "--color":
4404
+ color = true;
4405
+ break;
4406
+ case "--json":
4407
+ case "--pretty-json":
4408
+ mode = "json";
4409
+ if (arg === "--json") rest.push(arg);
4410
+ break;
4411
+ case "--quiet":
4412
+ mode = "quiet";
4413
+ break;
4414
+ default:
4415
+ rest.push(arg);
4416
+ }
4417
+ }
4418
+ return { mode, color, restArgv: rest };
4419
+ }
4420
+ function displayBanner(subtitle) {
4421
+ getUI().banner(subtitle ?? "Cloud CLI v2.0.0", { full: true });
4422
+ }
4423
+ function success(msg) {
4424
+ getUI().ok(msg);
4425
+ }
4426
+ function warn(msg) {
4427
+ getUI().warn(msg);
4428
+ }
4429
+ function info(msg) {
4430
+ getUI().info(msg);
4431
+ }
4432
+ function dim(msg) {
4433
+ getUI().secondary(msg);
4434
+ }
4435
+ function errorLine(msg) {
4436
+ const ui = getUI();
4437
+ if (ui.mode === "json") {
4438
+ ui.json({ ok: false, error: { code: "cli_error", message: msg } }, { pretty: true });
4439
+ return;
4440
+ }
4441
+ ui.writeErr("Error: " + msg + "\n");
4442
+ }
4443
+ function keyValue(pairs) {
4444
+ const ui = getUI();
4445
+ if (ui.mode === "json") {
4446
+ ui.json(pairs);
4447
+ return;
4448
+ }
4449
+ const keys = Object.keys(pairs);
4450
+ if (keys.length === 0) return;
4451
+ const maxKeyLen = keys.reduce((max, k) => Math.max(max, k.length), 0);
4452
+ for (const key of keys) {
4453
+ const label = key.padEnd(maxKeyLen);
4454
+ const value = pairs[key] ?? "";
4455
+ ui.line(" " + label + " " + value);
4456
+ }
4457
+ }
4458
+ function table(headers, rows) {
4459
+ const objectRows = rows.map((r) => {
4460
+ const obj = {};
4461
+ headers.forEach((h, i) => {
4462
+ obj[h] = r[i] ?? "";
4463
+ });
4464
+ return obj;
4465
+ });
4466
+ getUI().table(objectRows, { columns: headers });
4467
+ }
4468
+ async function withSpinner(message, fn) {
4469
+ const sp = getUI().spinner(message);
4470
+ try {
4471
+ const result = await fn();
4472
+ sp.stop(message);
4473
+ return result;
4474
+ } catch (err) {
4475
+ sp.stop();
4476
+ throw err;
4477
+ }
4478
+ }
4479
+
4480
+ // src/commands/im.ts
4481
+ function register(parent, getIMClient2, _getAPIClient) {
4482
+ const im = parent.command("im").description("IM messaging, groups, conversations, and credits");
4483
+ im.command("send <user-id-or-username> <message>").description("Send a direct message to a user (id by default, --by-username to resolve)").option("-t, --type <type>", "Message type: text, markdown, code, file, etc.", "text").option("--reply-to <msg-id>", "Reply to a specific message ID (parentId)").option("--conversation-id <id>", "Pin message to a specific conversation/session").option("--asset-id <id>", "Attach a previously uploaded asset (treats type as file)").option("--by-username", "Treat the first arg as a username; resolve to imUserId first").option("--json", "Output raw JSON response").action(async (target, message, opts) => {
4484
+ const client = getIMClient2();
4485
+ try {
4486
+ let userId = target;
4487
+ if (opts.byUsername) {
4488
+ const discoverRes = await client.im.contacts.discover();
4489
+ if (!discoverRes.ok || !Array.isArray(discoverRes.data)) {
4490
+ process.stderr.write(`Error: could not resolve username "${target}" \u2014 discover failed.
4491
+ `);
4492
+ process.exit(1);
4493
+ }
4494
+ const needle = target.trim().toLowerCase().replace(/^@/, "");
4495
+ const match = discoverRes.data.find((u) => {
4496
+ const vals = [u.username, u.displayName, u.userId].map(
4497
+ (v) => typeof v === "string" ? v.trim().toLowerCase() : ""
4498
+ );
4499
+ return vals.includes(needle);
4500
+ });
4501
+ if (!match?.userId || typeof match.userId !== "string") {
4502
+ process.stderr.write(`Error: could not resolve username "${target}" to an IM user.
4503
+ `);
4504
+ process.exit(1);
4505
+ }
4506
+ userId = match.userId;
4507
+ }
4508
+ let effectiveType = opts.type;
4509
+ if (opts.assetId && (!effectiveType || effectiveType === "text")) effectiveType = "file";
4510
+ const sendOpts = {
4511
+ type: effectiveType
4512
+ };
4513
+ if (opts.replyTo) sendOpts.parentId = opts.replyTo;
4514
+ if (opts.assetId) {
4515
+ sendOpts.attachments = [{ kind: "asset", assetId: opts.assetId, role: "attachment" }];
4516
+ }
4517
+ const res = opts.conversationId ? await client.im.messages.send(opts.conversationId, message, sendOpts) : await client.im.direct.send(userId, message, sendOpts);
4518
+ if (!res.ok) {
4519
+ process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
4520
+ `);
4521
+ process.exit(1);
4522
+ }
4523
+ if (opts.json) {
4524
+ process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
4525
+ return;
4526
+ }
4527
+ process.stdout.write(`Message sent (conversationId: ${res.data?.conversationId})
4528
+ `);
4529
+ } catch (err) {
4530
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
4531
+ `);
4532
+ process.exit(1);
4533
+ }
4534
+ });
4535
+ im.command("messages <user-id>").description("View direct message history with a user").option("-n, --limit <n>", "Max number of messages to fetch", "20").option("--json", "Output raw JSON response").action(async (userId, opts) => {
4536
+ const client = getIMClient2();
4537
+ try {
4538
+ const res = await client.im.direct.getMessages(userId, { limit: parseInt(opts.limit, 10) });
4539
+ if (!res.ok) {
4540
+ process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
4541
+ `);
4542
+ process.exit(1);
4543
+ }
4544
+ const msgs = res.data || [];
4545
+ if (opts.json) {
4546
+ process.stdout.write(JSON.stringify(msgs, null, 2) + "\n");
4547
+ return;
3764
4548
  }
3765
4549
  if (msgs.length === 0) {
3766
4550
  process.stdout.write("No messages.\n");
@@ -3819,12 +4603,16 @@ function register(parent, getIMClient2, _getAPIClient) {
3819
4603
  process.exit(1);
3820
4604
  }
3821
4605
  });
3822
- im.command("discover").description("Discover available agents").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--json", "Output raw JSON response").action(async (opts) => {
4606
+ im.command("discover").description("Discover available agents").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--online-only", "Only return agents currently online").option("--json", "Output raw JSON response").action(async (opts) => {
3823
4607
  const client = getIMClient2();
3824
4608
  try {
3825
4609
  const discoverOpts = {};
3826
4610
  if (opts.type) discoverOpts.type = opts.type;
3827
4611
  if (opts.capability) discoverOpts.capability = opts.capability;
4612
+ if (opts.onlineOnly) {
4613
+ discoverOpts.status = "online";
4614
+ discoverOpts.onlineOnly = "true";
4615
+ }
3828
4616
  const res = await client.im.contacts.discover(Object.keys(discoverOpts).length ? discoverOpts : void 0);
3829
4617
  if (!res.ok) {
3830
4618
  process.stderr.write(`Error: ${res.error?.message || JSON.stringify(res.error)}
@@ -3888,9 +4676,50 @@ function register(parent, getIMClient2, _getAPIClient) {
3888
4676
  process.exit(1);
3889
4677
  }
3890
4678
  });
3891
- im.command("conversations").description("List conversations").option("--unread", "Show only conversations with unread messages").option("--json", "Output raw JSON response").action(async (opts) => {
4679
+ im.command("conversations [conversation-id]").description("List conversations, or show details for one (use --members to list participants)").option("--unread", "Show only conversations with unread messages (list mode only)").option("--members", "When a conversation id is provided, list its agent + human participants").option("--json", "Output raw JSON response").action(async (conversationId, opts) => {
3892
4680
  const client = getIMClient2();
3893
4681
  try {
4682
+ if (conversationId) {
4683
+ const res2 = await client.im.conversations.get(conversationId);
4684
+ if (!res2.ok) {
4685
+ process.stderr.write(`Error: ${res2.error?.message || JSON.stringify(res2.error)}
4686
+ `);
4687
+ process.exit(1);
4688
+ }
4689
+ const conv = res2.data;
4690
+ if (opts.json) {
4691
+ process.stdout.write(JSON.stringify(opts.members ? conv?.participants ?? [] : conv, null, 2) + "\n");
4692
+ return;
4693
+ }
4694
+ if (opts.members) {
4695
+ const participants = conv?.participants ?? [];
4696
+ if (participants.length === 0) {
4697
+ process.stdout.write("No participants.\n");
4698
+ return;
4699
+ }
4700
+ process.stdout.write(
4701
+ "User ID".padEnd(36) + "Role".padEnd(12) + "Type".padEnd(10) + "Display Name\n"
4702
+ );
4703
+ for (const p of participants) {
4704
+ const user = p.user ?? {};
4705
+ process.stdout.write(
4706
+ `${String(user.id ?? "").padEnd(36)}${String(p.role ?? "").padEnd(12)}${String(user.role ?? "").padEnd(10)}${String(user.displayName ?? user.username ?? "")}
4707
+ `
4708
+ );
4709
+ }
4710
+ return;
4711
+ }
4712
+ process.stdout.write(`ID: ${conv?.id ?? ""}
4713
+ `);
4714
+ process.stdout.write(`Type: ${conv?.type ?? ""}
4715
+ `);
4716
+ process.stdout.write(`Title: ${conv?.title ?? ""}
4717
+ `);
4718
+ const participantCount = Array.isArray(conv?.participants) ? (conv?.participants).length : 0;
4719
+ process.stdout.write(`Participants: ${participantCount}
4720
+ `);
4721
+ return;
4722
+ }
3894
4723
  const listOpts = {};
3895
4724
  if (opts.unread) {
3896
4725
  listOpts.withUnread = true;
@@ -4200,9 +5029,9 @@ function register2(parent, _getIMClient, getAPIClient2) {
4200
5029
  }
4201
5030
  for (const item of results) {
4202
5031
  process.stdout.write(`
4203
- --- ${item.url ?? item.input ?? "result"} ---
5032
+ --- ${item.url ?? "result"} ---
4204
5033
  `);
4205
- const hqcc = item.hqcc ?? item.content ?? "";
5034
+ const hqcc = item.hqcc ?? item.raw ?? "";
4206
5035
  if (hqcc) {
4207
5036
  const truncated = hqcc.length > 2e3 ? hqcc.slice(0, 2e3) + "... [truncated]" : hqcc;
4208
5037
  process.stdout.write(truncated + "\n");
@@ -4242,9 +5071,9 @@ function register2(parent, _getIMClient, getAPIClient2) {
4242
5071
 
4243
5072
  `);
4244
5073
  results.forEach((item, i) => {
4245
- process.stdout.write(`[${i + 1}] ${item.url ?? item.input ?? "result"}
5074
+ process.stdout.write(`[${i + 1}] ${item.url ?? "result"}
4246
5075
  `);
4247
- const hqcc = item.hqcc ?? item.content ?? "";
5076
+ const hqcc = item.hqcc ?? item.raw ?? "";
4248
5077
  if (hqcc) {
4249
5078
  const truncated = hqcc.length > 2e3 ? hqcc.slice(0, 2e3) + "... [truncated]" : hqcc;
4250
5079
  process.stdout.write(truncated + "\n");
@@ -4283,6 +5112,8 @@ function register2(parent, _getIMClient, getAPIClient2) {
4283
5112
  }
4284
5113
 
4285
5114
  // src/commands/evolve.ts
5115
+ var GENE_CATEGORIES = /* @__PURE__ */ new Set(["repair", "optimize", "innovate", "diagnostic"]);
5116
+ var GENE_SORTS = /* @__PURE__ */ new Set(["newest", "most_used", "highest_success"]);
4286
5117
  function parseSignals(raw) {
4287
5118
  if (!raw) return void 0;
4288
5119
  const trimmed = raw.trim();
@@ -4295,6 +5126,21 @@ function parseSignals(raw) {
4295
5126
  }
4296
5127
  return trimmed.split(",").map((s) => s.trim()).filter(Boolean);
4297
5128
  }
5129
+ function parseOutcome(raw) {
5130
+ if (raw === "success" || raw === "failed") return raw;
5131
+ if (raw === "failure") return "failed";
5132
+ throw new Error(`Invalid outcome "${raw}". Expected success or failed.`);
5133
+ }
5134
+ function parseGeneCategory(raw) {
5135
+ if (!raw) return void 0;
5136
+ if (GENE_CATEGORIES.has(raw)) return raw;
5137
+ throw new Error(`Invalid gene category "${raw}". Expected repair, optimize, innovate, or diagnostic.`);
5138
+ }
5139
+ function parseGeneSort(raw) {
5140
+ if (!raw) return void 0;
5141
+ if (GENE_SORTS.has(raw)) return raw;
5142
+ throw new Error(`Invalid sort "${raw}". Expected newest, most_used, or highest_success.`);
5143
+ }
4298
5144
  function handleError(err) {
4299
5145
  const message = err instanceof Error ? err.message : String(err);
4300
5146
  process.stderr.write(`Error: ${message}
@@ -4358,14 +5204,15 @@ function register3(parent, getIMClient2, _getAPIClient) {
4358
5204
  evolve.command("record").description("Record an outcome against an evolution gene").requiredOption("-g, --gene <id>", "gene ID to record against").requiredOption("-o, --outcome <outcome>", "outcome: success, failure, partial").option("-s, --signals <signals>", "signals as JSON array or comma-separated list").option("--score <n>", "outcome score (0-1)").option("--summary <text>", "brief summary of the outcome").option("--scope <scope>", "evolution scope (default: global)").option("--json", "output raw JSON response").action(async (opts) => {
4359
5205
  const client = getIMClient2();
4360
5206
  try {
4361
- const signals = parseSignals(opts.signals);
5207
+ const signals = parseSignals(opts.signals) ?? [];
4362
5208
  const score = opts.score !== void 0 ? parseFloat(opts.score) : void 0;
5209
+ const outcome = parseOutcome(opts.outcome);
4363
5210
  const res = await client.im.evolution.record({
4364
5211
  gene_id: opts.gene,
4365
5212
  signals,
4366
- outcome: opts.outcome,
5213
+ outcome,
4367
5214
  score,
4368
- summary: opts.summary,
5215
+ summary: opts.summary ?? "",
4369
5216
  scope: opts.scope
4370
5217
  });
4371
5218
  if (opts.json) {
@@ -4382,7 +5229,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
4382
5229
  try {
4383
5230
  const res = await client.im.evolution.submitReport({
4384
5231
  rawContext: opts.error,
4385
- outcome: opts.status,
5232
+ outcome: parseOutcome(opts.status),
4386
5233
  taskContext: opts.task
4387
5234
  });
4388
5235
  if (opts.json && !opts.wait) {
@@ -4416,7 +5263,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
4416
5263
  const maxIterations = 30;
4417
5264
  let lastStatus;
4418
5265
  for (let i = 0; i < maxIterations; i++) {
4419
- await new Promise((resolve2) => setTimeout(resolve2, 2e3));
5266
+ await new Promise((resolve3) => setTimeout(resolve3, 2e3));
4420
5267
  if (!opts.json) process.stdout.write(".");
4421
5268
  const statusRes = await client.im.evolution.getReportStatus(traceId);
4422
5269
  if (!statusRes.ok) break;
@@ -4476,7 +5323,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
4476
5323
  try {
4477
5324
  const signals_match = parseSignals(opts.signals) ?? [];
4478
5325
  const res = await client.im.evolution.createGene({
4479
- category: opts.category,
5326
+ category: parseGeneCategory(opts.category),
4480
5327
  signals_match,
4481
5328
  strategy: opts.strategy,
4482
5329
  title: opts.name,
@@ -4682,9 +5529,9 @@ function register3(parent, getIMClient2, _getAPIClient) {
4682
5529
  try {
4683
5530
  const limit = parseInt(opts.limit ?? "20", 10);
4684
5531
  const res = await client.im.evolution.browseGenes({
4685
- category: opts.category,
5532
+ category: parseGeneCategory(opts.category),
4686
5533
  search: opts.search,
4687
- sort: opts.sort,
5534
+ sort: parseGeneSort(opts.sort),
4688
5535
  limit
4689
5536
  });
4690
5537
  if (opts.json) {
@@ -4799,17 +5646,89 @@ function register3(parent, getIMClient2, _getAPIClient) {
4799
5646
  }
4800
5647
 
4801
5648
  // src/commands/task.ts
5649
+ var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned", "running", "review", "completed", "failed", "cancelled"]);
5650
+ var TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "urgent"]);
5651
+ var TASK_KINDS = /* @__PURE__ */ new Set(["work_item", "goal"]);
5652
+ function parseTaskStatus(raw) {
5653
+ if (!raw) return void 0;
5654
+ if (TASK_STATUSES.has(raw)) return raw;
5655
+ throw new Error(`Invalid task status "${raw}".`);
5656
+ }
5657
+ async function resolveAssigneeId(client, name) {
5658
+ const needle = name.trim().toLowerCase();
5659
+ const normalized = needle.replace(/^@/, "");
5660
+ if (!needle) return "";
5661
+ const res = await client.im.contacts.discover();
5662
+ if (!res.ok || !Array.isArray(res.data)) return "";
5663
+ const agents = res.data;
5664
+ const exact = agents.find((agent) => {
5665
+ const values = [agent.userId, agent.username, agent.displayName].map(
5666
+ (v) => typeof v === "string" ? v.trim().toLowerCase() : ""
5667
+ );
5668
+ return values.includes(needle) || values.includes(normalized);
5669
+ });
5670
+ if (exact && typeof exact.userId === "string") return exact.userId;
5671
+ const fuzzy = agents.find((agent) => {
5672
+ const labels = [agent.username, agent.displayName].map((v) => typeof v === "string" ? v.trim().toLowerCase() : "").filter(Boolean);
5673
+ return labels.some((label) => label.includes(normalized) || normalized.includes(label));
5674
+ });
5675
+ return fuzzy && typeof fuzzy.userId === "string" ? fuzzy.userId : "";
5676
+ }
4802
5677
  function register4(parent, getIMClient2, _getAPIClient) {
4803
5678
  const task = parent.command("task").description("Manage tasks in the task marketplace");
4804
- task.command("create").description("Create a new task").requiredOption("--title <title>", "task title").option("--description <description>", "task description").option("--capability <capability>", "required agent capability").option("--budget <budget>", "budget in credits", parseFloat).option("--json", "output raw JSON response").action(async (opts) => {
5679
+ task.command("create").description("Create a new task").requiredOption("--title <title>", "task title").option("--description <description>", "task description").option("--capability <capability>", "required agent capability").option("--budget <budget>", "budget in credits", parseFloat).option("--reward <reward>", "alias for --budget (credits offered for completion)", parseFloat).option("--priority <priority>", "task priority: low | medium | high | urgent").option("--assignee-id <imUserId>", "directly assign to a specific agent by IM user id").option("--assignee-name <name>", "assign by @username / display name (resolved via discover)").option("--conversation-id <id>", "pin task to a conversation/session").option("--kind <kind>", "board projection kind: work_item (default) | goal", "work_item").option("--schedule-at <iso>", "one-shot ISO 8601 scheduled time (sets scheduleType=once)").option("--schedule-cron <expr>", "cron expression (sets scheduleType=cron)").option("--json", "output raw JSON response").action(async (opts) => {
4805
5680
  const client = getIMClient2();
4806
5681
  try {
4807
- const res = await client.im.tasks.create({
5682
+ if (opts.priority && !TASK_PRIORITIES.has(opts.priority)) {
5683
+ throw new Error(`Invalid --priority "${opts.priority}". Use one of: low, medium, high, urgent.`);
5684
+ }
5685
+ const kind = opts.kind ?? "work_item";
5686
+ if (!TASK_KINDS.has(kind)) {
5687
+ throw new Error(`Invalid --kind "${kind}". Use one of: work_item, goal.`);
5688
+ }
5689
+ let assigneeId = opts.assigneeId;
5690
+ if (!assigneeId && opts.assigneeName) {
5691
+ assigneeId = await resolveAssigneeId(client, opts.assigneeName);
5692
+ if (!assigneeId) {
5693
+ throw new Error(
5694
+ `--assignee-name "${opts.assigneeName}" did not resolve to an agent. Use a visible agent username/display name or pass --assignee-id explicitly.`
5695
+ );
5696
+ }
5697
+ }
5698
+ const budget = opts.budget ?? opts.reward;
5699
+ const metadata = { kind };
5700
+ if (opts.priority) metadata.priority = opts.priority;
5701
+ if (opts.conversationId) {
5702
+ metadata.context = { linkedConversationId: opts.conversationId };
5703
+ }
5704
+ if (kind === "goal") {
5705
+ metadata.intent = "standing_objective";
5706
+ metadata.goal = {
5707
+ status: "active",
5708
+ priority: opts.priority === "urgent" ? "high" : opts.priority ?? "medium",
5709
+ linkedConversationIds: opts.conversationId ? [opts.conversationId] : [],
5710
+ linkedTaskIds: [],
5711
+ lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
5712
+ };
5713
+ }
5714
+ const createOpts = {
4808
5715
  title: opts.title,
4809
5716
  description: opts.description,
4810
5717
  capability: opts.capability,
4811
- budget: opts.budget
4812
- });
5718
+ budget,
5719
+ metadata
5720
+ };
5721
+ if (assigneeId) createOpts.assigneeId = assigneeId;
5722
+ if (opts.conversationId) createOpts.conversationId = opts.conversationId;
5723
+ if (opts.scheduleAt) {
5724
+ createOpts.scheduleType = "once";
5725
+ createOpts.scheduleAt = opts.scheduleAt;
5726
+ }
5727
+ if (opts.scheduleCron) {
5728
+ createOpts.scheduleType = "cron";
5729
+ createOpts.scheduleCron = opts.scheduleCron;
5730
+ }
5731
+ const res = await client.im.tasks.create(createOpts);
4813
5732
  if (opts.json) {
4814
5733
  process.stdout.write(JSON.stringify(res, null, 2) + "\n");
4815
5734
  return;
@@ -4846,7 +5765,7 @@ function register4(parent, getIMClient2, _getAPIClient) {
4846
5765
  const client = getIMClient2();
4847
5766
  try {
4848
5767
  const res = await client.im.tasks.list({
4849
- status: opts.status,
5768
+ status: parseTaskStatus(opts.status),
4850
5769
  capability: opts.capability,
4851
5770
  limit: parseInt(opts.limit, 10)
4852
5771
  });
@@ -4900,7 +5819,8 @@ ${tasks.length} task(s) listed.
4900
5819
  `);
4901
5820
  process.exit(1);
4902
5821
  }
4903
- const t = res.data;
5822
+ const detail = res.data;
5823
+ const t = detail.task;
4904
5824
  process.stdout.write(`ID: ${t.id}
4905
5825
  `);
4906
5826
  process.stdout.write(`Title: ${t.title}
@@ -4931,14 +5851,14 @@ ${tasks.length} task(s) listed.
4931
5851
  `);
4932
5852
  if (t.error) process.stdout.write(`Error: ${t.error}
4933
5853
  `);
4934
- const logs = t.logs ?? t.taskLogs ?? [];
5854
+ const logs = detail.logs ?? [];
4935
5855
  if (logs.length > 0) {
4936
5856
  process.stdout.write(`
4937
5857
  Logs (${logs.length}):
4938
5858
  `);
4939
5859
  for (const log of logs) {
4940
- const ts = log.createdAt ?? log.timestamp ?? "";
4941
- const msg = log.message ?? log.content ?? JSON.stringify(log);
5860
+ const ts = log.createdAt ?? "";
5861
+ const msg = log.message ?? JSON.stringify(log);
4942
5862
  process.stdout.write(` [${ts}] ${msg}
4943
5863
  `);
4944
5864
  }
@@ -5179,16 +6099,23 @@ Logs (${logs.length}):
5179
6099
  }
5180
6100
 
5181
6101
  // src/commands/memory.ts
6102
+ var MEMORY_TYPES = /* @__PURE__ */ new Set(["user", "feedback", "project", "reference"]);
5182
6103
  function register5(parent, getIMClient2, _getAPIClient) {
5183
6104
  const mem = parent.command("memory").description("Agent memory file management");
5184
- mem.command("write").description("Write a memory file").requiredOption("-s, --scope <scope>", "memory scope").requiredOption("-p, --path <path>", "file path within scope").requiredOption("-c, --content <content>", "file content").option("--json", "output raw JSON response").action(async (opts) => {
6105
+ mem.command("write").description("Write a memory file").requiredOption("-s, --scope <scope>", "memory scope").requiredOption("-p, --path <path>", "file path within scope").requiredOption("-c, --content <content>", "file content").option("--type <type>", "memory type: user | feedback | project | reference").option("--description <description>", "human-readable description of this memory").option("--json", "output raw JSON response").action(async (opts) => {
5185
6106
  const client = getIMClient2();
5186
6107
  try {
5187
- const res = await client.im.memory.createFile({
6108
+ if (opts.type && !MEMORY_TYPES.has(opts.type)) {
6109
+ throw new Error(`Invalid --type "${opts.type}". Use one of: user, feedback, project, reference.`);
6110
+ }
6111
+ const body = {
5188
6112
  scope: opts.scope,
5189
6113
  path: opts.path,
5190
6114
  content: opts.content
5191
- });
6115
+ };
6116
+ if (opts.type) body.memoryType = opts.type;
6117
+ if (opts.description) body.description = opts.description;
6118
+ const res = await client.im.memory.createFile(body);
5192
6119
  if (opts.json) {
5193
6120
  process.stdout.write(JSON.stringify(res, null, 2) + "\n");
5194
6121
  return;
@@ -5199,6 +6126,7 @@ function register5(parent, getIMClient2, _getAPIClient) {
5199
6126
  process.exit(1);
5200
6127
  }
5201
6128
  const file = res.data;
6129
+ if (!file) throw new Error("Memory file response missing data");
5202
6130
  process.stdout.write(`Memory file created
5203
6131
  `);
5204
6132
  process.stdout.write(` ID: ${file.id}
@@ -5229,6 +6157,7 @@ function register5(parent, getIMClient2, _getAPIClient) {
5229
6157
  process.exit(1);
5230
6158
  }
5231
6159
  const file = res.data;
6160
+ if (!file) throw new Error("Memory file response missing data");
5232
6161
  process.stdout.write(`ID: ${file.id}
5233
6162
  `);
5234
6163
  process.stdout.write(`Scope: ${file.scope}
@@ -5246,7 +6175,9 @@ ${file.content ?? ""}
5246
6175
  });
5247
6176
  if (opts.json) {
5248
6177
  if (listRes.ok && Array.isArray(listRes.data) && listRes.data.length === 1) {
5249
- const detailRes = await client.im.memory.getFile(listRes.data[0].id);
6178
+ const first = listRes.data[0];
6179
+ if (!first) throw new Error("Memory file response missing data");
6180
+ const detailRes = await client.im.memory.getFile(first.id);
5250
6181
  process.stdout.write(JSON.stringify(detailRes, null, 2) + "\n");
5251
6182
  } else {
5252
6183
  process.stdout.write(JSON.stringify(listRes, null, 2) + "\n");
@@ -5264,13 +6195,16 @@ ${file.content ?? ""}
5264
6195
  return;
5265
6196
  }
5266
6197
  if (files.length === 1) {
5267
- const detailRes = await client.im.memory.getFile(files[0].id);
6198
+ const first = files[0];
6199
+ if (!first) throw new Error("Memory file response missing data");
6200
+ const detailRes = await client.im.memory.getFile(first.id);
5268
6201
  if (!detailRes.ok) {
5269
6202
  process.stderr.write(`Error: ${detailRes.error?.message || "Unknown error"}
5270
6203
  `);
5271
6204
  process.exit(1);
5272
6205
  }
5273
6206
  const file = detailRes.data;
6207
+ if (!file) throw new Error("Memory file response missing data");
5274
6208
  process.stdout.write(`ID: ${file.id}
5275
6209
  `);
5276
6210
  process.stdout.write(`Scope: ${file.scope}
@@ -5338,10 +6272,10 @@ ${file.content ?? ""}
5338
6272
  process.exit(1);
5339
6273
  }
5340
6274
  });
5341
- mem.command("compact <conversation-id>").description("Create a compaction summary for a conversation").option("--json", "output raw JSON response").action(async (conversationId, opts) => {
6275
+ mem.command("compact <conversation-id>").description("Create a compaction summary for a conversation").requiredOption("--summary <text>", "summary text to persist").option("--json", "output raw JSON response").action(async (conversationId, opts) => {
5342
6276
  const client = getIMClient2();
5343
6277
  try {
5344
- const res = await client.im.memory.compact({ conversationId });
6278
+ const res = await client.im.memory.compact({ conversationId, summary: opts.summary });
5345
6279
  if (opts.json) {
5346
6280
  process.stdout.write(JSON.stringify(res, null, 2) + "\n");
5347
6281
  return;
@@ -5365,6 +6299,66 @@ ${file.content ?? ""}
5365
6299
  } catch (err) {
5366
6300
  const message = err instanceof Error ? err.message : String(err);
5367
6301
  process.stderr.write(`Error: ${message}
6302
+ `);
6303
+ process.exit(1);
6304
+ }
6305
+ });
6306
+ mem.command("extract").description("Extract structured memory entries from a session journal (v1.8.0 P1)").requiredOption("--journal <text>", "session journal text (min 50 chars)").option("--scope <scope>", "evolution scope", "global").option("--json", "output raw JSON response").action(async (opts) => {
6307
+ const client = getIMClient2();
6308
+ try {
6309
+ const res = await client.im.memory._r("POST", "/api/im/memory/extract", { journal: opts.journal, scope: opts.scope });
6310
+ if (opts.json) {
6311
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
6312
+ return;
6313
+ }
6314
+ if (!res.ok) {
6315
+ process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
6316
+ `);
6317
+ process.exit(1);
6318
+ }
6319
+ process.stdout.write("Memory extraction complete\n");
6320
+ const data = res.data;
6321
+ if (data) {
6322
+ if (Array.isArray(data.extracted)) {
6323
+ process.stdout.write(` Extracted: ${data.extracted.length}
6324
+ `);
6325
+ }
6326
+ if (Array.isArray(data.created)) {
6327
+ process.stdout.write(` Created: ${data.created.length}
6328
+ `);
6329
+ }
6330
+ if (Array.isArray(data.updated)) {
6331
+ process.stdout.write(` Updated: ${data.updated.length}
6332
+ `);
6333
+ }
6334
+ }
6335
+ } catch (err) {
6336
+ const message = err instanceof Error ? err.message : String(err);
6337
+ process.stderr.write(`Error: ${message}
6338
+ `);
6339
+ process.exit(1);
6340
+ }
6341
+ });
6342
+ mem.command("consolidate").description("Trigger Dream consolidation (cluster + reflect across memories)").option("--scope <scope>", "evolution scope", "global").option("--json", "output raw JSON response").action(async (opts) => {
6343
+ const client = getIMClient2();
6344
+ try {
6345
+ const res = await client.im.memory._r("POST", "/api/im/memory/consolidate", { scope: opts.scope });
6346
+ if (opts.json) {
6347
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
6348
+ return;
6349
+ }
6350
+ if (!res.ok) {
6351
+ process.stderr.write(`Error: ${res.error?.message || "Unknown error"}
6352
+ `);
6353
+ process.exit(1);
6354
+ }
6355
+ process.stdout.write("Dream consolidation triggered\n");
6356
+ if (res.data && typeof res.data === "object") {
6357
+ process.stdout.write(JSON.stringify(res.data, null, 2) + "\n");
6358
+ }
6359
+ } catch (err) {
6360
+ const message = err instanceof Error ? err.message : String(err);
6361
+ process.stderr.write(`Error: ${message}
5368
6362
  `);
5369
6363
  process.exit(1);
5370
6364
  }
@@ -5405,7 +6399,7 @@ function printFileTable(files) {
5405
6399
  const idLen = Math.max(2, ...files.map((f) => f.id.length));
5406
6400
  const scopeLen = Math.max(5, ...files.map((f) => f.scope.length));
5407
6401
  const pathLen = Math.max(4, ...files.map((f) => f.path.length));
5408
- const row = (id, scope, path4) => `${id.padEnd(idLen)} ${scope.padEnd(scopeLen)} ${path4.padEnd(pathLen)}`;
6402
+ const row = (id, scope, path5) => `${id.padEnd(idLen)} ${scope.padEnd(scopeLen)} ${path5.padEnd(pathLen)}`;
5409
6403
  process.stdout.write(row("ID", "SCOPE", "PATH") + "\n");
5410
6404
  process.stdout.write(`${"-".repeat(idLen)} ${"-".repeat(scopeLen)} ${"-".repeat(pathLen)}
5411
6405
  `);
@@ -5415,6 +6409,17 @@ function printFileTable(files) {
5415
6409
  }
5416
6410
 
5417
6411
  // src/commands/skill.ts
6412
+ var SKILL_LOCAL_PLATFORMS = /* @__PURE__ */ new Set(["claude-code", "openclaw", "opencode", "plugin"]);
6413
+ function parsePlatforms(raw) {
6414
+ if (raw === "all") return void 0;
6415
+ const values = raw.split(",").map((item) => item.trim()).filter(Boolean);
6416
+ for (const value of values) {
6417
+ if (!SKILL_LOCAL_PLATFORMS.has(value)) {
6418
+ throw new Error(`Invalid platform "${value}". Expected claude-code, openclaw, opencode, plugin, or all.`);
6419
+ }
6420
+ }
6421
+ return values;
6422
+ }
5418
6423
  function padEnd(str, len) {
5419
6424
  if (str.length >= len) return str.slice(0, len);
5420
6425
  return str + " ".repeat(len - str.length);
@@ -5475,10 +6480,11 @@ function register6(parent, getIMClient2, _getAPIClient) {
5475
6480
  if (!opts.local) {
5476
6481
  res = await client.im.evolution.installSkill(slug);
5477
6482
  } else {
5478
- const platforms = opts.platform === "all" ? void 0 : [opts.platform];
6483
+ const platforms = parsePlatforms(opts.platform);
5479
6484
  res = await client.im.evolution.installSkillLocal(slug, {
5480
6485
  platforms,
5481
- project: opts.project
6486
+ project: Boolean(opts.project),
6487
+ projectRoot: opts.project
5482
6488
  });
5483
6489
  }
5484
6490
  if (opts.json) {
@@ -5622,7 +6628,7 @@ ${result.content}
5622
6628
  skill.command("sync").description("Re-sync all installed skills to local filesystem").option("--platform <platform>", "target platform: claude-code, openclaw, opencode, or all", "all").option("--json", "output raw JSON response").action(async (opts) => {
5623
6629
  const client = getIMClient2();
5624
6630
  try {
5625
- const platforms = opts.platform === "all" ? void 0 : [opts.platform];
6631
+ const platforms = parsePlatforms(opts.platform);
5626
6632
  const res = await client.im.evolution.syncSkillsLocal({ platforms });
5627
6633
  if (opts.json) {
5628
6634
  process.stdout.write(JSON.stringify(res, null, 2) + "\n");
@@ -6292,9 +7298,487 @@ function register10(parent, getIMClient2, _getAPIClient) {
6292
7298
  });
6293
7299
  }
6294
7300
 
6295
- // src/daemon.ts
7301
+ // src/commands/asset.ts
6296
7302
  var fs2 = __toESM(require("fs"));
6297
7303
  var path2 = __toESM(require("path"));
7304
+ function resolveWorkspaceId(flag) {
7305
+ if (flag) return flag;
7306
+ if (typeof process !== "undefined" && process.env?.PRISMER_WORKSPACE_ID) {
7307
+ return process.env.PRISMER_WORKSPACE_ID;
7308
+ }
7309
+ return void 0;
7310
+ }
7311
+ function requireWorkspaceId(flag) {
7312
+ const wsId = resolveWorkspaceId(flag);
7313
+ if (!wsId) {
7314
+ process.stderr.write("Error: --workspace-id is required (or set PRISMER_WORKSPACE_ID env var).\n");
7315
+ process.exit(1);
7316
+ }
7317
+ return wsId;
7318
+ }
7319
+ function globToRegex(glob) {
7320
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&");
7321
+ const pattern = escaped.replace(/\*/g, ".*").replace(/\?/g, ".");
7322
+ return new RegExp(`^${pattern}$`, "i");
7323
+ }
7324
+ function assetFilename(a) {
7325
+ if (a.filename) return a.filename;
7326
+ const meta = a.metadata;
7327
+ if (meta) {
7328
+ const fn = meta["filename"] ?? meta["fileName"] ?? meta["name"];
7329
+ if (typeof fn === "string") return fn;
7330
+ }
7331
+ return null;
7332
+ }
7333
+ function formatBytes(n) {
7334
+ if (n == null) return "-";
7335
+ return String(n);
7336
+ }
7337
+ function register11(parent, getIMClient2, _getAPIClient) {
7338
+ const asset = parent.command("asset").description("Inspect and manage workspace assets (content-addressed)");
7339
+ asset.command("list").description("List assets in a workspace").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--filename <glob>", 'filter by filename glob (client-side, e.g. "design-doc*")').option("--mime <type>", 'filter by MIME type prefix (e.g. "application/pdf")').option("--kind <kind>", "filter by row-level kind (e.g. file, image, sandbox-output)").option("--updated-after <iso>", "filter to assets created/updated after this ISO timestamp").option("--task-id <id>", "filter by source task id").option("-n, --limit <n>", "maximum results to return", "50").option("--json", "output raw JSON response").action(async (opts) => {
7340
+ const wsId = requireWorkspaceId(opts.workspaceId);
7341
+ const client = getIMClient2();
7342
+ try {
7343
+ const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 50, 1), 200);
7344
+ const res = await client.im.assets.list({
7345
+ workspaceId: wsId,
7346
+ taskId: opts.taskId,
7347
+ kind: opts.kind,
7348
+ limit
7349
+ });
7350
+ if (!res.ok) {
7351
+ if (opts.json) process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7352
+ else process.stderr.write(`Error: ${res.error?.message || "list failed"}
7353
+ `);
7354
+ process.exit(1);
7355
+ }
7356
+ let rows = res.data ?? [];
7357
+ if (opts.filename) {
7358
+ const re = globToRegex(opts.filename);
7359
+ rows = rows.filter((r) => {
7360
+ const fn = assetFilename(r);
7361
+ return fn != null && re.test(fn);
7362
+ });
7363
+ }
7364
+ if (opts.mime) {
7365
+ const m = opts.mime.toLowerCase();
7366
+ rows = rows.filter((r) => (r.mime ?? "").toLowerCase().startsWith(m));
7367
+ }
7368
+ if (opts.updatedAfter) {
7369
+ const after = Date.parse(opts.updatedAfter);
7370
+ if (!Number.isNaN(after)) {
7371
+ rows = rows.filter((r) => {
7372
+ const ts = Date.parse(r.createdAt ?? "");
7373
+ return !Number.isNaN(ts) && ts >= after;
7374
+ });
7375
+ }
7376
+ }
7377
+ if (opts.json) {
7378
+ process.stdout.write(JSON.stringify({ ok: true, data: rows }, null, 2) + "\n");
7379
+ return;
7380
+ }
7381
+ if (rows.length === 0) {
7382
+ process.stdout.write("No assets found.\n");
7383
+ return;
7384
+ }
7385
+ const idW = 24;
7386
+ const mimeW = 20;
7387
+ const sizeW = 12;
7388
+ const hashW = 16;
7389
+ const header = "ID".padEnd(idW) + "MIME".padEnd(mimeW) + "SIZE".padEnd(sizeW) + "HASH".padEnd(hashW) + "NAME";
7390
+ process.stdout.write(header + "\n");
7391
+ process.stdout.write("-".repeat(idW + mimeW + sizeW + hashW + 20) + "\n");
7392
+ for (const r of rows) {
7393
+ const id = String(r.id).slice(0, idW - 1);
7394
+ const mime = (r.mime ?? "-").slice(0, mimeW - 1);
7395
+ const size = formatBytes(r.sizeBytes).slice(0, sizeW - 1);
7396
+ const hash = (r.contentHash ?? "-").slice(0, 12);
7397
+ const name = assetFilename(r) ?? "";
7398
+ process.stdout.write(
7399
+ id.padEnd(idW) + mime.padEnd(mimeW) + size.padEnd(sizeW) + hash.padEnd(hashW) + name + "\n"
7400
+ );
7401
+ }
7402
+ process.stdout.write(`
7403
+ ${rows.length} asset(s) listed.
7404
+ `);
7405
+ } catch (err) {
7406
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7407
+ `);
7408
+ process.exit(1);
7409
+ }
7410
+ });
7411
+ asset.command("get <assetId>").description("Show full asset metadata (no content)").option("--json", "output raw JSON response").action(async (assetId, opts) => {
7412
+ const client = getIMClient2();
7413
+ try {
7414
+ const res = await client.im.assets.detail(assetId);
7415
+ if (opts.json) {
7416
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7417
+ if (!res.ok) process.exit(1);
7418
+ return;
7419
+ }
7420
+ if (!res.ok) {
7421
+ process.stderr.write(`Error: ${res.error?.message || "get failed"}
7422
+ `);
7423
+ process.exit(1);
7424
+ }
7425
+ const a = res.data;
7426
+ process.stdout.write(`ID: ${a.id}
7427
+ `);
7428
+ process.stdout.write(`Workspace: ${a.workspaceId}
7429
+ `);
7430
+ process.stdout.write(`Hash: ${a.contentHash}
7431
+ `);
7432
+ process.stdout.write(`MIME: ${a.mime ?? "-"}
7433
+ `);
7434
+ process.stdout.write(`Size: ${formatBytes(a.sizeBytes)} bytes
7435
+ `);
7436
+ process.stdout.write(`Kind: ${a.kind}
7437
+ `);
7438
+ process.stdout.write(`Storage URI: ${a.storageUri}
7439
+ `);
7440
+ process.stdout.write(`Created: ${a.createdAt}
7441
+ `);
7442
+ const fn = assetFilename(a);
7443
+ if (fn) process.stdout.write(`Filename: ${fn}
7444
+ `);
7445
+ if (a.sourceTaskId) process.stdout.write(`Source Task: ${a.sourceTaskId}
7446
+ `);
7447
+ if (a.sourceAgentImUserId) process.stdout.write(`Source Agent: ${a.sourceAgentImUserId}
7448
+ `);
7449
+ if (a.cdnUrl) process.stdout.write(`CDN URL: ${a.cdnUrl}
7450
+ `);
7451
+ if (a.url) process.stdout.write(`Signed URL: ${a.url} (expires in ${a.expiresIn ?? "?"}s)
7452
+ `);
7453
+ if (a.metadata && Object.keys(a.metadata).length > 0) {
7454
+ process.stdout.write(`Metadata: ${JSON.stringify(a.metadata)}
7455
+ `);
7456
+ }
7457
+ } catch (err) {
7458
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7459
+ `);
7460
+ process.exit(1);
7461
+ }
7462
+ });
7463
+ asset.command("by-hash <sha256>").description("Look up an asset by content hash (sha256)").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--json", "output raw JSON response").action(async (sha256, opts) => {
7464
+ const wsId = requireWorkspaceId(opts.workspaceId);
7465
+ const client = getIMClient2();
7466
+ try {
7467
+ const res = await client.im.assets.byHash(sha256, wsId);
7468
+ if (opts.json) {
7469
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7470
+ if (!res.ok) process.exit(1);
7471
+ return;
7472
+ }
7473
+ if (!res.ok) {
7474
+ process.stderr.write(`Error: ${res.error?.message || "by-hash failed"}
7475
+ `);
7476
+ process.exit(1);
7477
+ }
7478
+ const a = res.data;
7479
+ process.stdout.write(`ID: ${a.id}
7480
+ `);
7481
+ process.stdout.write(`Hash: ${a.contentHash}
7482
+ `);
7483
+ process.stdout.write(`MIME: ${a.mime ?? "-"}
7484
+ `);
7485
+ process.stdout.write(`Size: ${formatBytes(a.sizeBytes)} bytes
7486
+ `);
7487
+ process.stdout.write(`Kind: ${a.kind}
7488
+ `);
7489
+ } catch (err) {
7490
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7491
+ `);
7492
+ process.exit(1);
7493
+ }
7494
+ });
7495
+ asset.command("upload <path>").description("Upload a local file as a workspace asset").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--conversation-id <id>", "pin upload to a conversation (stored in metadata)").option("--kind <kind>", "asset kind label", "user-upload").option("--task-id <id>", "source task id").option("--mime <type>", "override detected MIME type").option("--filename <name>", "override filename").option("--json", "output raw JSON response").action(async (filePath, opts) => {
7496
+ const wsId = requireWorkspaceId(opts.workspaceId);
7497
+ if (!fs2.existsSync(filePath)) {
7498
+ process.stderr.write(`Error: file not found: ${filePath}
7499
+ `);
7500
+ process.exit(1);
7501
+ }
7502
+ const client = getIMClient2();
7503
+ try {
7504
+ const metadata = {};
7505
+ if (opts.conversationId) metadata.conversationId = opts.conversationId;
7506
+ if (opts.filename) metadata.filename = opts.filename;
7507
+ const uploadOpts = {
7508
+ workspaceId: wsId,
7509
+ kind: opts.kind ?? "user-upload"
7510
+ };
7511
+ if (opts.taskId) uploadOpts.sourceTaskId = opts.taskId;
7512
+ if (opts.mime) uploadOpts.mimeType = opts.mime;
7513
+ if (opts.filename) uploadOpts.fileName = opts.filename;
7514
+ if (Object.keys(metadata).length > 0) uploadOpts.metadata = metadata;
7515
+ const res = await client.im.assets.upload(filePath, uploadOpts);
7516
+ if (opts.json) {
7517
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7518
+ if (!res.ok) process.exit(1);
7519
+ return;
7520
+ }
7521
+ if (!res.ok) {
7522
+ process.stderr.write(`Error: ${res.error?.message || "upload failed"}
7523
+ `);
7524
+ process.exit(1);
7525
+ }
7526
+ const a = res.data;
7527
+ process.stdout.write(`Uploaded ${path2.basename(filePath)}
7528
+ `);
7529
+ process.stdout.write(` ID: ${a.id}
7530
+ `);
7531
+ process.stdout.write(` Hash: ${a.contentHash}
7532
+ `);
7533
+ process.stdout.write(` Size: ${formatBytes(a.sizeBytes)} bytes
7534
+ `);
7535
+ process.stdout.write(` MIME: ${a.mime ?? "-"}
7536
+ `);
7537
+ process.stdout.write(` Kind: ${a.kind}
7538
+ `);
7539
+ } catch (err) {
7540
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7541
+ `);
7542
+ process.exit(1);
7543
+ }
7544
+ });
7545
+ asset.command("download <assetId>").description("Download asset bytes (to file or stdout)").option("--out <path>", "destination file (omit to write to stdout)").option("--offset <n>", "byte offset for partial download", parseIntOpt).option("--length <n>", "byte length for partial download", parseIntOpt).action(async (assetId, opts) => {
7546
+ try {
7547
+ const { bytes, truncated, totalSize } = await fetchAssetBytes(
7548
+ getIMClient2(),
7549
+ assetId,
7550
+ opts.offset,
7551
+ opts.length
7552
+ );
7553
+ if (opts.out) {
7554
+ fs2.writeFileSync(opts.out, bytes);
7555
+ const range = describeRange(opts.offset, opts.length, bytes.byteLength);
7556
+ process.stderr.write(
7557
+ `Downloaded ${assetId} -> ${opts.out} (${bytes.byteLength} bytes${range ? `, ${range}` : ""}${truncated ? ", truncated" : ""}${totalSize != null ? `, total=${totalSize}` : ""})
7558
+ `
7559
+ );
7560
+ } else {
7561
+ process.stdout.write(bytes);
7562
+ }
7563
+ } catch (err) {
7564
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7565
+ `);
7566
+ process.exit(1);
7567
+ }
7568
+ });
7569
+ asset.command("read <assetId>").description("Read an asset as text (bounded by --offset/--length)").option("--offset <n>", "byte offset for read", parseIntOpt).option("--length <n>", "maximum bytes to read (recommended for large files)", parseIntOpt).option("--force", "read even if MIME is not text-like").action(async (assetId, opts) => {
7570
+ const client = getIMClient2();
7571
+ try {
7572
+ const detailRes = await client.im.assets.detail(assetId);
7573
+ if (!detailRes.ok) {
7574
+ process.stderr.write(`Error: ${detailRes.error?.message || "asset get failed"}
7575
+ `);
7576
+ process.exit(1);
7577
+ }
7578
+ const detail = detailRes.data;
7579
+ const mime = (detail.mime ?? "").toLowerCase();
7580
+ const looksTextual = mime.startsWith("text/") || mime === "application/json" || mime === "application/xml" || mime === "application/javascript" || mime === "application/x-yaml" || mime.endsWith("+json") || mime.endsWith("+xml");
7581
+ if (!looksTextual && !opts.force) {
7582
+ process.stderr.write(
7583
+ `Refusing to read non-text asset (mime=${detail.mime ?? "unknown"}). Use --force to override, or use "cloud asset download" for binary content.
7584
+ `
7585
+ );
7586
+ process.exit(1);
7587
+ }
7588
+ const { bytes, truncated, totalSize } = await fetchAssetBytes(
7589
+ client,
7590
+ assetId,
7591
+ opts.offset,
7592
+ opts.length
7593
+ );
7594
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
7595
+ process.stdout.write(text);
7596
+ if (text.length > 0 && !text.endsWith("\n")) process.stdout.write("\n");
7597
+ const start = opts.offset ?? 0;
7598
+ const end = start + bytes.byteLength;
7599
+ const range = `${start}..${end}`;
7600
+ process.stderr.write(
7601
+ `asset=${detail.id} hash=${detail.contentHash} range=${range} bytes=${bytes.byteLength} truncated=${truncated}${totalSize != null ? ` total=${totalSize}` : ""}
7602
+ `
7603
+ );
7604
+ } catch (err) {
7605
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7606
+ `);
7607
+ process.exit(1);
7608
+ }
7609
+ });
7610
+ asset.command("sync").description("Refresh the local cache index by re-listing the workspace").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("-n, --limit <n>", "maximum results to walk", "200").option("--json", "output raw JSON response").action(async (opts) => {
7611
+ const wsId = requireWorkspaceId(opts.workspaceId);
7612
+ const client = getIMClient2();
7613
+ try {
7614
+ const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 200, 1), 200);
7615
+ const res = await client.im.assets.list({ workspaceId: wsId, limit });
7616
+ if (!res.ok) {
7617
+ if (opts.json) process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7618
+ else process.stderr.write(`Error: ${res.error?.message || "sync failed"}
7619
+ `);
7620
+ process.exit(1);
7621
+ }
7622
+ const count = (res.data ?? []).length;
7623
+ if (opts.json) {
7624
+ process.stdout.write(JSON.stringify({ ok: true, data: { workspaceId: wsId, count } }, null, 2) + "\n");
7625
+ return;
7626
+ }
7627
+ process.stdout.write(`Synced ${count} asset(s) for workspace ${wsId}.
7628
+ `);
7629
+ process.stdout.write(
7630
+ "Note: asset bytes are fetched lazily on first read; this command refreshes metadata only.\n"
7631
+ );
7632
+ } catch (err) {
7633
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7634
+ `);
7635
+ process.exit(1);
7636
+ }
7637
+ });
7638
+ }
7639
+ function parseIntOpt(value) {
7640
+ const n = parseInt(value, 10);
7641
+ if (Number.isNaN(n) || n < 0) {
7642
+ throw new Error(`expected non-negative integer, got "${value}"`);
7643
+ }
7644
+ return n;
7645
+ }
7646
+ function describeRange(offset, length, actual) {
7647
+ if (offset == null && length == null) return null;
7648
+ const start = offset ?? 0;
7649
+ const end = start + (actual ?? length ?? 0);
7650
+ return `range=${start}..${end}`;
7651
+ }
7652
+ async function fetchAssetBytes(client, assetId, offset, length) {
7653
+ const url = client.im.assets.url(assetId);
7654
+ const headers = {};
7655
+ if (offset != null || length != null) {
7656
+ const start = offset ?? 0;
7657
+ const end = length != null ? start + length - 1 : "";
7658
+ headers["Range"] = `bytes=${start}-${end}`;
7659
+ }
7660
+ const resp = await client.fetchAuthed(url, { method: "GET", headers });
7661
+ if (!resp.ok && resp.status !== 206) {
7662
+ let detail = "";
7663
+ try {
7664
+ detail = await resp.text();
7665
+ } catch {
7666
+ }
7667
+ throw new Error(`Asset fetch failed (${resp.status}): ${detail || resp.statusText}`);
7668
+ }
7669
+ const ab = await resp.arrayBuffer();
7670
+ const bytes = new Uint8Array(ab);
7671
+ let totalSize = null;
7672
+ const cr = resp.headers.get("content-range");
7673
+ if (cr) {
7674
+ const m = cr.match(/\/(\d+)$/);
7675
+ if (m) totalSize = Number(m[1]);
7676
+ } else {
7677
+ const cl = resp.headers.get("content-length");
7678
+ if (cl) totalSize = Number(cl);
7679
+ }
7680
+ let truncated = false;
7681
+ if (length != null) {
7682
+ if (totalSize != null) {
7683
+ const start = offset ?? 0;
7684
+ truncated = start + bytes.byteLength < totalSize;
7685
+ } else {
7686
+ truncated = bytes.byteLength >= length;
7687
+ }
7688
+ } else if (offset != null && totalSize != null) {
7689
+ truncated = offset + bytes.byteLength < totalSize;
7690
+ }
7691
+ return { bytes, truncated, totalSize };
7692
+ }
7693
+
7694
+ // src/commands/approval.ts
7695
+ function resolveWorkspaceId2(flag) {
7696
+ if (flag) return flag;
7697
+ if (typeof process !== "undefined" && process.env?.PRISMER_WORKSPACE_ID) {
7698
+ return process.env.PRISMER_WORKSPACE_ID;
7699
+ }
7700
+ return void 0;
7701
+ }
7702
+ function register12(parent, getIMClient2, _getAPIClient) {
7703
+ const approval = parent.command("approval").description("Submit and manage human approval requests");
7704
+ approval.command("request-human").description("Submit a human approval request and stop the current turn").requiredOption("--action <text>", "one-sentence summary of the gated action").requiredOption("--context <text>", "multi-sentence framing for the human").requiredOption("--risk <text>", "what breaks if approved wrongly; what is reversible").option("--options <opt...>", "explicit choice values (defaults to approve/reject if omitted)").option("--task-id <id>", "task to resume when the human decides").option("--conversation-id <id>", "conversation context for the request").option("--workspace-id <id>", "workspace id (defaults to PRISMER_WORKSPACE_ID env)").option("--category <category>", "approval category label", "human-approval").option("--expires-in <seconds>", "expiration window in seconds (default 24h)", parseIntOpt2).option("--json", "output raw JSON response").action(async (opts) => {
7705
+ if (!opts.conversationId && !opts.taskId) {
7706
+ process.stderr.write(
7707
+ "Error: --conversation-id or --task-id is required (human-approval needs an anchor).\n"
7708
+ );
7709
+ process.exit(1);
7710
+ }
7711
+ const workspaceId = resolveWorkspaceId2(opts.workspaceId);
7712
+ const composedContext = `${opts.context}
7713
+
7714
+ Risk: ${opts.risk}`;
7715
+ const optionList = opts.options && opts.options.length > 0 ? opts.options.map((v) => ({ value: v, label: v })) : [
7716
+ { value: "approve", label: "Approve" },
7717
+ { value: "reject", label: "Reject" }
7718
+ ];
7719
+ const metadata = {
7720
+ risk: opts.risk,
7721
+ source: "cli:prismer-approval-request-human"
7722
+ };
7723
+ const body = {
7724
+ category: opts.category,
7725
+ title: opts.action,
7726
+ context: composedContext,
7727
+ options: optionList,
7728
+ metadata
7729
+ };
7730
+ if (workspaceId) body.workspaceId = workspaceId;
7731
+ if (opts.conversationId) body.conversationId = opts.conversationId;
7732
+ if (opts.taskId) body.taskId = opts.taskId;
7733
+ if (opts.expiresIn) body.expiresInSeconds = opts.expiresIn;
7734
+ try {
7735
+ const client = getIMClient2();
7736
+ const res = await client.im.request(
7737
+ "POST",
7738
+ "/api/im/approvals",
7739
+ body
7740
+ );
7741
+ if (opts.json) {
7742
+ process.stdout.write(JSON.stringify(res, null, 2) + "\n");
7743
+ }
7744
+ if (!res.ok) {
7745
+ if (!opts.json) {
7746
+ process.stderr.write(`Error: ${res.error?.message || "approval submission failed"}
7747
+ `);
7748
+ }
7749
+ process.exit(1);
7750
+ }
7751
+ const data = res.data;
7752
+ const summary = {
7753
+ approvalId: data.id,
7754
+ status: data.status ?? "pending",
7755
+ expiresAt: data.expiresAt ?? null
7756
+ };
7757
+ if (!opts.json) {
7758
+ process.stdout.write(JSON.stringify(summary) + "\n");
7759
+ }
7760
+ process.stderr.write(
7761
+ `Submitted approval request ${data.id}. Stopping this turn.
7762
+ `
7763
+ );
7764
+ } catch (err) {
7765
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
7766
+ `);
7767
+ process.exit(1);
7768
+ }
7769
+ });
7770
+ }
7771
+ function parseIntOpt2(value) {
7772
+ const n = parseInt(value, 10);
7773
+ if (Number.isNaN(n) || n <= 0) {
7774
+ throw new Error(`expected positive integer, got "${value}"`);
7775
+ }
7776
+ return n;
7777
+ }
7778
+
7779
+ // src/daemon.ts
7780
+ var fs3 = __toESM(require("fs"));
7781
+ var path3 = __toESM(require("path"));
6298
7782
  var import_path = require("path");
6299
7783
  var os = __toESM(require("os"));
6300
7784
  var import_os = require("os");
@@ -6302,22 +7786,22 @@ var http = __toESM(require("http"));
6302
7786
  var import_http = require("http");
6303
7787
  var import_child_process = require("child_process");
6304
7788
  var TOML = __toESM(require("@iarna/toml"));
6305
- var CONFIG_DIR = path2.join(os.homedir(), ".prismer");
6306
- var CONFIG_PATH = path2.join(CONFIG_DIR, "config.toml");
6307
- var PID_PATH = path2.join(CONFIG_DIR, "daemon.pid");
6308
- var PORT_PATH = path2.join(CONFIG_DIR, "daemon.port");
6309
- var CACHE_DIR = path2.join(CONFIG_DIR, "cache");
6310
- var EVOLUTION_CACHE_PATH = path2.join(CACHE_DIR, "evolution.json");
6311
- var OUTBOX_PATH = path2.join(CACHE_DIR, "outbox.json");
7789
+ var CONFIG_DIR = path3.join(os.homedir(), ".prismer");
7790
+ var CONFIG_PATH = path3.join(CONFIG_DIR, "config.toml");
7791
+ var PID_PATH = path3.join(CONFIG_DIR, "daemon.pid");
7792
+ var PORT_PATH = path3.join(CONFIG_DIR, "daemon.port");
7793
+ var CACHE_DIR = path3.join(CONFIG_DIR, "cache");
7794
+ var EVOLUTION_CACHE_PATH = path3.join(CACHE_DIR, "evolution.json");
7795
+ var OUTBOX_PATH = path3.join(CACHE_DIR, "outbox.json");
6312
7796
  var SYNC_INTERVAL_MS = 6e4;
6313
7797
  var FLUSH_INTERVAL_MS = 3e4;
6314
7798
  var API_TIMEOUT_MS = 1e4;
6315
7799
  var EVENTS_FILE = (0, import_path.join)(CACHE_DIR, "events.json");
6316
7800
  var MAX_EVENTS = 1e3;
6317
7801
  function loadConfig() {
6318
- if (!fs2.existsSync(CONFIG_PATH)) return null;
7802
+ if (!fs3.existsSync(CONFIG_PATH)) return null;
6319
7803
  try {
6320
- const raw = fs2.readFileSync(CONFIG_PATH, "utf-8");
7804
+ const raw = fs3.readFileSync(CONFIG_PATH, "utf-8");
6321
7805
  const parsed = TOML.parse(raw);
6322
7806
  const apiKey = parsed?.default?.api_key || "";
6323
7807
  const baseUrl = parsed?.default?.base_url || "https://prismer.cloud";
@@ -6328,13 +7812,13 @@ function loadConfig() {
6328
7812
  }
6329
7813
  }
6330
7814
  function ensureCacheDir() {
6331
- if (!fs2.existsSync(CACHE_DIR)) {
6332
- fs2.mkdirSync(CACHE_DIR, { recursive: true });
7815
+ if (!fs3.existsSync(CACHE_DIR)) {
7816
+ fs3.mkdirSync(CACHE_DIR, { recursive: true });
6333
7817
  }
6334
7818
  }
6335
7819
  function loadEvents() {
6336
7820
  try {
6337
- return JSON.parse(fs2.readFileSync(EVENTS_FILE, "utf-8"));
7821
+ return JSON.parse(fs3.readFileSync(EVENTS_FILE, "utf-8"));
6338
7822
  } catch {
6339
7823
  return [];
6340
7824
  }
@@ -6343,7 +7827,7 @@ function appendEvent(event) {
6343
7827
  const events = loadEvents();
6344
7828
  events.push(event);
6345
7829
  if (events.length > MAX_EVENTS) events.splice(0, events.length - MAX_EVENTS);
6346
- fs2.writeFileSync(EVENTS_FILE, JSON.stringify(events), { encoding: "utf-8", mode: 384 });
7830
+ fs3.writeFileSync(EVENTS_FILE, JSON.stringify(events), { encoding: "utf-8", mode: 384 });
6347
7831
  }
6348
7832
  function emitSyncEvent(genesCount) {
6349
7833
  if (genesCount > 0) {
@@ -6358,9 +7842,9 @@ function emitSyncEvent(genesCount) {
6358
7842
  }
6359
7843
  }
6360
7844
  function readPid() {
6361
- if (!fs2.existsSync(PID_PATH)) return null;
7845
+ if (!fs3.existsSync(PID_PATH)) return null;
6362
7846
  try {
6363
- const raw = fs2.readFileSync(PID_PATH, "utf-8").trim();
7847
+ const raw = fs3.readFileSync(PID_PATH, "utf-8").trim();
6364
7848
  const pid = parseInt(raw, 10);
6365
7849
  return isNaN(pid) ? null : pid;
6366
7850
  } catch {
@@ -6368,9 +7852,9 @@ function readPid() {
6368
7852
  }
6369
7853
  }
6370
7854
  function readPort() {
6371
- if (!fs2.existsSync(PORT_PATH)) return null;
7855
+ if (!fs3.existsSync(PORT_PATH)) return null;
6372
7856
  try {
6373
- const raw = fs2.readFileSync(PORT_PATH, "utf-8").trim();
7857
+ const raw = fs3.readFileSync(PORT_PATH, "utf-8").trim();
6374
7858
  const port = parseInt(raw, 10);
6375
7859
  return isNaN(port) ? null : port;
6376
7860
  } catch {
@@ -6387,24 +7871,24 @@ function isProcessRunning(pid) {
6387
7871
  }
6388
7872
  function writePid(pid) {
6389
7873
  ensureCacheDir();
6390
- if (!fs2.existsSync(CONFIG_DIR)) {
6391
- fs2.mkdirSync(CONFIG_DIR, { recursive: true });
7874
+ if (!fs3.existsSync(CONFIG_DIR)) {
7875
+ fs3.mkdirSync(CONFIG_DIR, { recursive: true });
6392
7876
  }
6393
- fs2.writeFileSync(PID_PATH, String(pid), { encoding: "utf-8", mode: 384 });
7877
+ fs3.writeFileSync(PID_PATH, String(pid), { encoding: "utf-8", mode: 384 });
6394
7878
  }
6395
7879
  function writePort(port) {
6396
- if (!fs2.existsSync(CONFIG_DIR)) {
6397
- fs2.mkdirSync(CONFIG_DIR, { recursive: true });
7880
+ if (!fs3.existsSync(CONFIG_DIR)) {
7881
+ fs3.mkdirSync(CONFIG_DIR, { recursive: true });
6398
7882
  }
6399
- fs2.writeFileSync(PORT_PATH, String(port), { encoding: "utf-8", mode: 384 });
7883
+ fs3.writeFileSync(PORT_PATH, String(port), { encoding: "utf-8", mode: 384 });
6400
7884
  }
6401
7885
  function cleanupPidFiles() {
6402
7886
  try {
6403
- if (fs2.existsSync(PID_PATH)) fs2.unlinkSync(PID_PATH);
7887
+ if (fs3.existsSync(PID_PATH)) fs3.unlinkSync(PID_PATH);
6404
7888
  } catch {
6405
7889
  }
6406
7890
  try {
6407
- if (fs2.existsSync(PORT_PATH)) fs2.unlinkSync(PORT_PATH);
7891
+ if (fs3.existsSync(PORT_PATH)) fs3.unlinkSync(PORT_PATH);
6408
7892
  } catch {
6409
7893
  }
6410
7894
  }
@@ -6420,16 +7904,16 @@ async function fetchWithTimeout(url, options, timeoutMs = API_TIMEOUT_MS) {
6420
7904
  async function runDaemonProcess() {
6421
7905
  const cfg = loadConfig();
6422
7906
  if (!cfg) {
6423
- process.stderr.write('[prismer-daemon] No config found. Run "prismer setup" first.\n');
7907
+ process.stderr.write('[cloud-daemon] No config found. Run "cloud setup" first.\n');
6424
7908
  process.exit(1);
6425
7909
  }
6426
7910
  ensureCacheDir();
6427
7911
  let lastSync = 0;
6428
7912
  let syncCount = 0;
6429
7913
  let evolutionCursor = 0;
6430
- if (fs2.existsSync(EVOLUTION_CACHE_PATH)) {
7914
+ if (fs3.existsSync(EVOLUTION_CACHE_PATH)) {
6431
7915
  try {
6432
- const cached = JSON.parse(fs2.readFileSync(EVOLUTION_CACHE_PATH, "utf-8"));
7916
+ const cached = JSON.parse(fs3.readFileSync(EVOLUTION_CACHE_PATH, "utf-8"));
6433
7917
  if (typeof cached?.cursor === "number") evolutionCursor = cached.cursor;
6434
7918
  } catch {
6435
7919
  }
@@ -6437,9 +7921,9 @@ async function runDaemonProcess() {
6437
7921
  const server = (0, import_http.createServer)((req, res) => {
6438
7922
  if (req.method === "GET" && req.url === "/health") {
6439
7923
  let outboxSize = 0;
6440
- if (fs2.existsSync(OUTBOX_PATH)) {
7924
+ if (fs3.existsSync(OUTBOX_PATH)) {
6441
7925
  try {
6442
- const entries = JSON.parse(fs2.readFileSync(OUTBOX_PATH, "utf-8"));
7926
+ const entries = JSON.parse(fs3.readFileSync(OUTBOX_PATH, "utf-8"));
6443
7927
  if (Array.isArray(entries)) outboxSize = entries.length;
6444
7928
  } catch {
6445
7929
  }
@@ -6502,7 +7986,7 @@ async function runDaemonProcess() {
6502
7986
  }
6503
7987
  ensureCacheDir();
6504
7988
  const pulled = data?.data || data;
6505
- fs2.writeFileSync(
7989
+ fs3.writeFileSync(
6506
7990
  EVOLUTION_CACHE_PATH,
6507
7991
  JSON.stringify({ cursor: evolutionCursor, lastSync, data: pulled }, null, 2),
6508
7992
  { encoding: "utf-8", mode: 384 }
@@ -6513,10 +7997,10 @@ async function runDaemonProcess() {
6513
7997
  }
6514
7998
  };
6515
7999
  const doOutboxFlush = async () => {
6516
- if (!fs2.existsSync(OUTBOX_PATH)) return;
8000
+ if (!fs3.existsSync(OUTBOX_PATH)) return;
6517
8001
  let entries = [];
6518
8002
  try {
6519
- entries = JSON.parse(fs2.readFileSync(OUTBOX_PATH, "utf-8"));
8003
+ entries = JSON.parse(fs3.readFileSync(OUTBOX_PATH, "utf-8"));
6520
8004
  if (!Array.isArray(entries) || entries.length === 0) return;
6521
8005
  } catch {
6522
8006
  return;
@@ -6537,7 +8021,7 @@ async function runDaemonProcess() {
6537
8021
  }
6538
8022
  );
6539
8023
  if (res.ok) {
6540
- fs2.writeFileSync(OUTBOX_PATH, "[]", { encoding: "utf-8", mode: 384 });
8024
+ fs3.writeFileSync(OUTBOX_PATH, "[]", { encoding: "utf-8", mode: 384 });
6541
8025
  }
6542
8026
  } catch {
6543
8027
  }
@@ -6567,7 +8051,7 @@ async function startDaemon() {
6567
8051
  cleanupPidFiles();
6568
8052
  const cfg = loadConfig();
6569
8053
  if (!cfg) {
6570
- console.error('No API key found. Run "prismer setup" first.');
8054
+ console.error('No API key found. Run "cloud setup" first.');
6571
8055
  process.exit(1);
6572
8056
  }
6573
8057
  if (process.env["PRISMER_DAEMON"] === "1") {
@@ -6655,7 +8139,7 @@ function resolveNpxPath() {
6655
8139
  } catch {
6656
8140
  for (const p of ["/usr/local/bin/npx", "/opt/homebrew/bin/npx", `${(0, import_os.homedir)()}/.nvm/current/bin/npx`]) {
6657
8141
  try {
6658
- fs2.accessSync(p);
8142
+ fs3.accessSync(p);
6659
8143
  return p;
6660
8144
  } catch {
6661
8145
  }
@@ -6697,8 +8181,8 @@ function installLaunchd() {
6697
8181
  <string>${(0, import_path.join)((0, import_os.homedir)(), ".prismer", "daemon.stderr.log")}</string>
6698
8182
  </dict>
6699
8183
  </plist>`;
6700
- fs2.mkdirSync((0, import_path.dirname)(plistPath), { recursive: true });
6701
- fs2.writeFileSync(plistPath, plist, { mode: 384 });
8184
+ fs3.mkdirSync((0, import_path.dirname)(plistPath), { recursive: true });
8185
+ fs3.writeFileSync(plistPath, plist, { mode: 384 });
6702
8186
  try {
6703
8187
  (0, import_child_process.execSync)(`launchctl load ${plistPath}`, { stdio: "pipe" });
6704
8188
  console.log("[prismer] Daemon service installed and started (launchd)");
@@ -6714,7 +8198,7 @@ function uninstallLaunchd() {
6714
8198
  } catch {
6715
8199
  }
6716
8200
  try {
6717
- fs2.unlinkSync(plistPath);
8201
+ fs3.unlinkSync(plistPath);
6718
8202
  } catch {
6719
8203
  }
6720
8204
  console.log("[prismer] Daemon service uninstalled (launchd)");
@@ -6739,8 +8223,8 @@ RestartSec=10
6739
8223
  [Install]
6740
8224
  WantedBy=default.target
6741
8225
  `;
6742
- fs2.mkdirSync(serviceDir, { recursive: true });
6743
- fs2.writeFileSync(servicePath, unit, { mode: 420 });
8226
+ fs3.mkdirSync(serviceDir, { recursive: true });
8227
+ fs3.writeFileSync(servicePath, unit, { mode: 420 });
6744
8228
  try {
6745
8229
  (0, import_child_process.execSync)("systemctl --user daemon-reload", { stdio: "pipe" });
6746
8230
  (0, import_child_process.execSync)("systemctl --user enable prismer-daemon", { stdio: "pipe" });
@@ -6763,7 +8247,7 @@ function uninstallSystemd() {
6763
8247
  }
6764
8248
  const servicePath = (0, import_path.join)((0, import_os.homedir)(), ".config", "systemd", "user", "prismer-daemon.service");
6765
8249
  try {
6766
- fs2.unlinkSync(servicePath);
8250
+ fs3.unlinkSync(servicePath);
6767
8251
  } catch {
6768
8252
  }
6769
8253
  try {
@@ -6779,7 +8263,7 @@ function installDaemonService() {
6779
8263
  } else if (platform === "linux") {
6780
8264
  installSystemd();
6781
8265
  } else {
6782
- console.log(`Daemon auto-start not supported on ${platform}. Use: prismer daemon start`);
8266
+ console.log(`Daemon auto-start not supported on ${platform}. Use: cloud daemon start`);
6783
8267
  }
6784
8268
  }
6785
8269
  function uninstallDaemonService() {
@@ -6803,26 +8287,39 @@ if (process.env["PRISMER_DAEMON"] === "1") {
6803
8287
  // src/cli.ts
6804
8288
  var cliVersion = "1.7.2";
6805
8289
  try {
6806
- const pkgPath = path3.join(__dirname, "..", "package.json");
6807
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
8290
+ const pkgPath = path4.join(__dirname, "..", "package.json");
8291
+ const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf8"));
6808
8292
  cliVersion = pkg.version || cliVersion;
6809
8293
  } catch {
6810
8294
  }
6811
- var CONFIG_DIR2 = path3.join(os2.homedir(), ".prismer");
6812
- var CONFIG_PATH2 = path3.join(CONFIG_DIR2, "config.toml");
8295
+ var CONFIG_DIR2 = process.env.PRISMER_HOME ? path4.resolve(process.env.PRISMER_HOME) : path4.join(os2.homedir(), ".prismer");
8296
+ var CONFIG_PATH2 = path4.join(CONFIG_DIR2, "config.toml");
6813
8297
  function ensureConfigDir() {
6814
- if (!fs3.existsSync(CONFIG_DIR2)) {
6815
- fs3.mkdirSync(CONFIG_DIR2, { recursive: true });
8298
+ if (!fs4.existsSync(CONFIG_DIR2)) {
8299
+ fs4.mkdirSync(CONFIG_DIR2, { recursive: true });
6816
8300
  }
6817
8301
  }
6818
8302
  function readConfig() {
6819
- if (!fs3.existsSync(CONFIG_PATH2)) return {};
6820
- const raw = fs3.readFileSync(CONFIG_PATH2, "utf-8");
6821
- return TOML2.parse(raw);
8303
+ if (!fs4.existsSync(CONFIG_PATH2)) return {};
8304
+ const raw = fs4.readFileSync(CONFIG_PATH2, "utf-8");
8305
+ const parsed = TOML2.parse(raw);
8306
+ const flatApiKey = parsed.api_key;
8307
+ const flatBaseUrl = parsed.cloud_api_base ?? parsed.base_url;
8308
+ const flatEnv = parsed.environment;
8309
+ if (flatApiKey || flatBaseUrl || flatEnv) {
8310
+ parsed.default = {
8311
+ ...parsed.default ?? {},
8312
+ // Explicit [default].api_key wins; flat is the fallback (runtime-written).
8313
+ api_key: parsed.default?.api_key ?? flatApiKey,
8314
+ base_url: parsed.default?.base_url ?? flatBaseUrl,
8315
+ environment: parsed.default?.environment ?? flatEnv
8316
+ };
8317
+ }
8318
+ return parsed;
6822
8319
  }
6823
8320
  function writeConfig(config) {
6824
8321
  ensureConfigDir();
6825
- fs3.writeFileSync(CONFIG_PATH2, TOML2.stringify(config), { encoding: "utf-8", mode: 384 });
8322
+ fs4.writeFileSync(CONFIG_PATH2, TOML2.stringify(config), { encoding: "utf-8", mode: 384 });
6826
8323
  }
6827
8324
  function setNestedValue(obj, dotPath, value) {
6828
8325
  const parts = dotPath.split(".");
@@ -6836,20 +8333,24 @@ function setNestedValue(obj, dotPath, value) {
6836
8333
  }
6837
8334
  function getIMClient() {
6838
8335
  const cfg = readConfig();
6839
- const token = cfg?.auth?.im_token;
6840
- if (!token) {
6841
- error('No IM token. Run "prismer setup --agent" or "prismer register <username>" first.');
6842
- process.exit(1);
6843
- }
6844
8336
  const env = cfg?.default?.environment || "production";
6845
8337
  const baseUrl = cfg?.default?.base_url || "";
6846
- return new PrismerClient({ apiKey: token, environment: env, ...baseUrl ? { baseUrl } : {} });
8338
+ const imToken = cfg?.auth?.im_token;
8339
+ if (imToken) {
8340
+ return new PrismerClient({ apiKey: imToken, environment: env, ...baseUrl ? { baseUrl } : {} });
8341
+ }
8342
+ const apiKey = cfg?.default?.api_key;
8343
+ if (!apiKey) {
8344
+ errorLine('No credentials. Run "cloud setup" first (or "cloud setup --agent" / "cloud register <username>" for IM-JWT path).');
8345
+ process.exit(1);
8346
+ }
8347
+ return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {} });
6847
8348
  }
6848
8349
  function getAPIClient() {
6849
8350
  const cfg = readConfig();
6850
8351
  const apiKey = cfg?.default?.api_key;
6851
8352
  if (!apiKey) {
6852
- error('No API key. Run "prismer setup" to sign in and get your key.');
8353
+ errorLine('No API key. Run "cloud setup" to sign in and get your key.');
6853
8354
  process.exit(1);
6854
8355
  }
6855
8356
  const env = cfg?.default?.environment || "production";
@@ -6857,15 +8358,15 @@ function getAPIClient() {
6857
8358
  return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {} });
6858
8359
  }
6859
8360
  var program = new import_commander.Command();
6860
- program.name("prismer").description("Prismer Cloud SDK CLI").version(cliVersion);
8361
+ program.name("cloud").description("Prismer Cloud SDK CLI").version(cliVersion);
6861
8362
  async function verifyAndSaveKey(config, apiKey) {
6862
8363
  if (!apiKey) {
6863
- error("No key provided.");
8364
+ errorLine("No key provided.");
6864
8365
  process.exit(1);
6865
8366
  }
6866
8367
  if (!apiKey.startsWith("sk-prismer-")) {
6867
- error("Invalid key format. API keys start with sk-prismer-");
6868
- dim2(" Get your key at: https://prismer.cloud/setup");
8368
+ errorLine("Invalid key format. API keys start with sk-prismer-");
8369
+ dim(" Get your key at: https://prismer.cloud/setup");
6869
8370
  process.exit(1);
6870
8371
  }
6871
8372
  const baseUrl = config.default?.base_url || "https://prismer.cloud";
@@ -6874,8 +8375,8 @@ async function verifyAndSaveKey(config, apiKey) {
6874
8375
  headers: { Authorization: `Bearer ${apiKey}` }
6875
8376
  });
6876
8377
  if (res.status === 401) {
6877
- error("API key is invalid or expired.");
6878
- dim2(" Get a new key at: https://prismer.cloud/setup");
8378
+ errorLine("API key is invalid or expired.");
8379
+ dim(" Get a new key at: https://prismer.cloud/setup");
6879
8380
  process.exit(1);
6880
8381
  }
6881
8382
  success("API key verified");
@@ -6892,7 +8393,7 @@ async function verifyAndSaveKey(config, apiKey) {
6892
8393
  try {
6893
8394
  installDaemonService();
6894
8395
  } catch {
6895
- dim2("Daemon auto-start setup skipped. Run manually: prismer daemon install");
8396
+ dim("Daemon auto-start setup skipped. Run manually: cloud daemon install");
6896
8397
  }
6897
8398
  }
6898
8399
  function openBrowser(url) {
@@ -6919,8 +8420,8 @@ async function runSetup(opts, apiKey) {
6919
8420
  const masked = config.default.api_key.slice(0, 12) + "..." + config.default.api_key.slice(-4);
6920
8421
  success(`Already configured: ${masked}`);
6921
8422
  console.log("");
6922
- dim2(" To reconfigure, run: prismer setup --force");
6923
- dim2(" To check status: prismer status");
8423
+ dim(" To reconfigure, run: cloud setup --force");
8424
+ dim(" To check status: cloud status");
6924
8425
  return;
6925
8426
  }
6926
8427
  if (apiKey) {
@@ -6930,7 +8431,7 @@ async function runSetup(opts, apiKey) {
6930
8431
  if (opts.agent) {
6931
8432
  if (!opts.force && config.auth?.im_token) {
6932
8433
  success("Already registered as agent (IM token exists).");
6933
- dim2(" For API key access, run: prismer setup");
8434
+ dim(" For API key access, run: cloud setup");
6934
8435
  return;
6935
8436
  }
6936
8437
  const username = `agent-${Date.now().toString(36)}`;
@@ -6953,10 +8454,10 @@ async function runSetup(opts, apiKey) {
6953
8454
  "User ID": config.auth.im_user_id || ""
6954
8455
  });
6955
8456
  console.log("");
6956
- info("For full API access, sign in: prismer setup");
8457
+ info("For full API access, sign in: cloud setup");
6957
8458
  } catch (err) {
6958
- error(`Agent registration failed: ${err.message}`);
6959
- dim2(" Try signing in instead: prismer setup");
8459
+ errorLine(`Agent registration failed: ${err.message}`);
8460
+ dim(" Try signing in instead: cloud setup");
6960
8461
  process.exit(1);
6961
8462
  }
6962
8463
  return;
@@ -6964,7 +8465,7 @@ async function runSetup(opts, apiKey) {
6964
8465
  if (opts.manual) {
6965
8466
  const setupUrl = `${baseUrl}/setup?utm_source=cli&utm_medium=manual`;
6966
8467
  info("Opening browser to sign in...");
6967
- dim2(` ${setupUrl}`);
8468
+ dim(` ${setupUrl}`);
6968
8469
  console.log("");
6969
8470
  openBrowser(setupUrl);
6970
8471
  info("After signing in, copy the API key from the page and paste it below.");
@@ -6974,7 +8475,7 @@ async function runSetup(opts, apiKey) {
6974
8475
  rl.question("Paste your API key: ", (key) => {
6975
8476
  rl.close();
6976
8477
  verifyAndSaveKey(config, key.trim()).catch((err) => {
6977
- error(`Setup failed: ${err.message}`);
8478
+ errorLine(`Setup failed: ${err.message}`);
6978
8479
  process.exit(1);
6979
8480
  });
6980
8481
  });
@@ -7021,16 +8522,16 @@ async function runSetup(opts, apiKey) {
7021
8522
  console.log("");
7022
8523
  openBrowser(setupUrl);
7023
8524
  info("Waiting for authentication...");
7024
- dim2(" (If the browser didn't open, visit this URL manually:)");
7025
- dim2(` ${setupUrl}`);
8525
+ dim(" (If the browser didn't open, visit this URL manually:)");
8526
+ dim(` ${setupUrl}`);
7026
8527
  console.log("");
7027
8528
  setTimeout(() => {
7028
8529
  if (!resolved) {
7029
- error("Timed out waiting for authentication (5 min).");
8530
+ errorLine("Timed out waiting for authentication (5 min).");
7030
8531
  console.log("");
7031
- dim2(" Alternatives:");
7032
- dim2(" prismer setup --manual Paste key manually");
7033
- dim2(" prismer setup --agent Register as agent (free credits, no browser)");
8532
+ dim(" Alternatives:");
8533
+ dim(" cloud setup --manual Paste key manually");
8534
+ dim(" cloud setup --agent Register as agent (free credits, no browser)");
7034
8535
  server.close();
7035
8536
  process.exit(1);
7036
8537
  }
@@ -7040,8 +8541,8 @@ async function runSetup(opts, apiKey) {
7040
8541
  program.command("setup [api-key]").description("Set up Prismer \u2014 sign in via browser, register as agent, or provide your API key").option("--manual", "Paste API key manually instead of browser auto-flow").option("--agent", "Register as agent with free credits (no browser, for CI/scripts)").option("--force", "Reconfigure even if already set up").action(async (apiKey, opts) => {
7041
8542
  await runSetup(opts, apiKey);
7042
8543
  });
7043
- program.command("init [api-key]").description('Alias for "prismer setup" (deprecated, use setup instead)').option("--manual", "Paste API key manually").option("--agent", "Register as agent with free credits").option("--force", "Reconfigure even if already set up").action(async (apiKey, opts) => {
7044
- warn('"prismer init" is deprecated. Use "prismer setup" instead.');
8544
+ program.command("init [api-key]").description('Alias for "cloud setup" (deprecated, use setup instead)').option("--manual", "Paste API key manually").option("--agent", "Register as agent with free credits").option("--force", "Reconfigure even if already set up").action(async (apiKey, opts) => {
8545
+ warn('"cloud init" is deprecated. Use "cloud setup" instead.');
7045
8546
  console.log("");
7046
8547
  await runSetup(opts, apiKey);
7047
8548
  });
@@ -7049,7 +8550,7 @@ program.command("register <username>").description("Register an IM identity and
7049
8550
  const config = readConfig();
7050
8551
  const apiKey = config.default?.api_key;
7051
8552
  if (!apiKey) {
7052
- error('No API key. Run "prismer setup" first.');
8553
+ errorLine('No API key. Run "cloud setup" first.');
7053
8554
  process.exit(1);
7054
8555
  }
7055
8556
  const client = new PrismerClient({
@@ -7069,7 +8570,7 @@ program.command("register <username>").description("Register an IM identity and
7069
8570
  try {
7070
8571
  const result = await client.im.account.register(registerOpts);
7071
8572
  if (!result.ok || !result.data) {
7072
- error(`Registration failed: ${result.error?.message || "Unknown error"}`);
8573
+ errorLine(`Registration failed: ${result.error?.message || "Unknown error"}`);
7073
8574
  process.exit(1);
7074
8575
  }
7075
8576
  const data = result.data;
@@ -7087,9 +8588,9 @@ program.command("register <username>").description("Register an IM identity and
7087
8588
  "Role": data.role,
7088
8589
  "New": String(data.isNew)
7089
8590
  });
7090
- dim2(" Token stored in ~/.prismer/config.toml");
8591
+ dim(" Token stored in ~/.prismer/config.toml");
7091
8592
  } catch (err) {
7092
- error(`Registration failed: ${err instanceof Error ? err.message : String(err)}`);
8593
+ errorLine(`Registration failed: ${err instanceof Error ? err.message : String(err)}`);
7093
8594
  process.exit(1);
7094
8595
  }
7095
8596
  });
@@ -7146,16 +8647,16 @@ program.command("status").description("Show current config and live info").actio
7146
8647
  warn(`Could not fetch live info: ${me.error?.message || "unknown error"}`);
7147
8648
  }
7148
8649
  } else {
7149
- dim2(" IM Token: (not registered)");
8650
+ dim(" IM Token: (not registered)");
7150
8651
  }
7151
8652
  });
7152
8653
  var configCmd = program.command("config").description("Manage config file");
7153
8654
  configCmd.command("show").description("Print config file").action(() => {
7154
- if (!fs3.existsSync(CONFIG_PATH2)) {
7155
- warn('No config file. Run "prismer setup" to create one.');
8655
+ if (!fs4.existsSync(CONFIG_PATH2)) {
8656
+ warn('No config file. Run "cloud setup" to create one.');
7156
8657
  return;
7157
8658
  }
7158
- console.log(fs3.readFileSync(CONFIG_PATH2, "utf-8"));
8659
+ console.log(fs4.readFileSync(CONFIG_PATH2, "utf-8"));
7159
8660
  });
7160
8661
  configCmd.command("set <key> <value>").description("Set a config value (e.g. default.base_url)").action((key, value) => {
7161
8662
  const config = readConfig();
@@ -7172,7 +8673,7 @@ tokenCmd.command("refresh").description("Refresh IM JWT token").option("--json",
7172
8673
  return;
7173
8674
  }
7174
8675
  if (!res.ok) {
7175
- error(`Token refresh failed: ${JSON.stringify(res.error)}`);
8676
+ errorLine(`Token refresh failed: ${JSON.stringify(res.error)}`);
7176
8677
  process.exit(1);
7177
8678
  }
7178
8679
  const data = res.data;
@@ -7197,12 +8698,44 @@ register7(program, getIMClient, getAPIClient);
7197
8698
  register8(program, getIMClient, getAPIClient);
7198
8699
  register9(program, getIMClient, getAPIClient);
7199
8700
  register10(program, getIMClient, getAPIClient);
7200
- program.command("send").description("Send a direct message (shortcut for: im send)").argument("<user-id>", "Target user/agent ID").argument("<message>", "Message content").option("-t, --type <type>", "Message type: text, markdown, code, etc.", "text").option("--reply-to <id>", "Reply to a message ID").option("--json", "JSON output").action(async (userId, message, opts) => {
8701
+ register11(program, getIMClient, getAPIClient);
8702
+ register12(program, getIMClient, getAPIClient);
8703
+ program.command("send").description("Send a direct message (shortcut for: im send)").argument("<user-id-or-username>", "Target user/agent IM user ID (or username with --by-username)").argument("<message>", "Message content").option("-t, --type <type>", "Message type: text, markdown, code, etc.", "text").option("--reply-to <id>", "Reply to a message ID").option("--conversation-id <id>", "Pin message to a specific conversation/session").option("--asset-id <id>", "Attach a previously uploaded asset (treats type as file)").option("--by-username", "Treat the first argument as a username; resolve to imUserId first").option("--json", "JSON output").action(async (target, message, opts) => {
7201
8704
  const client = getIMClient();
8705
+ let userId = target;
8706
+ if (opts.byUsername) {
8707
+ const discoverRes = await client.im.contacts.discover();
8708
+ if (!discoverRes.ok || !Array.isArray(discoverRes.data)) {
8709
+ errorLine(`Could not resolve username "${target}" \u2014 discover failed.`);
8710
+ process.exit(1);
8711
+ }
8712
+ const needle = target.trim().toLowerCase().replace(/^@/, "");
8713
+ const match = discoverRes.data.find((u) => {
8714
+ const vals = [u.username, u.displayName, u.userId].map(
8715
+ (v) => typeof v === "string" ? v.trim().toLowerCase() : ""
8716
+ );
8717
+ return vals.includes(needle);
8718
+ });
8719
+ if (!match?.userId) {
8720
+ errorLine(`Could not resolve username "${target}" to an IM user.`);
8721
+ process.exit(1);
8722
+ }
8723
+ userId = match.userId;
8724
+ }
7202
8725
  const sendOpts = {};
7203
8726
  if (opts.type && opts.type !== "text") sendOpts.type = opts.type;
7204
8727
  if (opts.replyTo) sendOpts.parentId = opts.replyTo;
8728
+ if (opts.assetId) {
8729
+ if (!opts.type || opts.type === "text") sendOpts.type = "file";
8730
+ sendOpts.attachments = [{ kind: "asset", assetId: opts.assetId, role: "attachment" }];
8731
+ }
8732
+ if (opts.conversationId) {
8733
+ sendOpts.metadata = { ...sendOpts.metadata ?? {}, conversationId: opts.conversationId };
8734
+ }
7205
8735
  const res = await withSpinner("Sending message", async () => {
8736
+ if (opts.conversationId) {
8737
+ return client.im.messages.send(opts.conversationId, message, sendOpts);
8738
+ }
7206
8739
  return client.im.direct.send(userId, message, sendOpts);
7207
8740
  });
7208
8741
  if (opts.json) {
@@ -7210,7 +8743,7 @@ program.command("send").description("Send a direct message (shortcut for: im sen
7210
8743
  return;
7211
8744
  }
7212
8745
  if (!res.ok) {
7213
- error(`Send failed: ${JSON.stringify(res.error)}`);
8746
+ errorLine(`Send failed: ${JSON.stringify(res.error)}`);
7214
8747
  process.exit(1);
7215
8748
  }
7216
8749
  success(`Message sent (conversation: ${res.data?.conversationId})`);
@@ -7228,7 +8761,7 @@ program.command("load").description("Load URL(s) \u2192 compressed HQCC (shortcu
7228
8761
  return;
7229
8762
  }
7230
8763
  if (!res.success) {
7231
- error(res.error?.message || "Load failed");
8764
+ errorLine(res.error?.message || "Load failed");
7232
8765
  process.exit(1);
7233
8766
  }
7234
8767
  const results = res.results || (res.result ? [res.result] : []);
@@ -7256,7 +8789,7 @@ program.command("search").description("Search web content (shortcut for: context
7256
8789
  return;
7257
8790
  }
7258
8791
  if (!res.success) {
7259
- error(res.error?.message || "Search failed");
8792
+ errorLine(res.error?.message || "Search failed");
7260
8793
  process.exit(1);
7261
8794
  }
7262
8795
  const results = res.results || [];
@@ -7274,7 +8807,7 @@ program.command("search").description("Search web content (shortcut for: context
7274
8807
  const r = results[i];
7275
8808
  if (r.hqcc) {
7276
8809
  console.log("");
7277
- dim2(` ${i + 1}. ${r.hqcc.substring(0, 200)}`);
8810
+ dim(` ${i + 1}. ${r.hqcc.substring(0, 200)}`);
7278
8811
  }
7279
8812
  }
7280
8813
  });
@@ -7288,7 +8821,7 @@ program.command("parse").description("Parse a document via OCR (shortcut for: pa
7288
8821
  return;
7289
8822
  }
7290
8823
  if (!res.success) {
7291
- error(res.error?.message || "Parse failed");
8824
+ errorLine(res.error?.message || "Parse failed");
7292
8825
  process.exit(1);
7293
8826
  }
7294
8827
  if (res.taskId) {
@@ -7297,7 +8830,7 @@ program.command("parse").description("Parse a document via OCR (shortcut for: pa
7297
8830
  "Status": res.status || "processing"
7298
8831
  });
7299
8832
  console.log("");
7300
- dim2(` Check: prismer parse-status ${res.taskId}`);
8833
+ dim(` Check: cloud parse-status ${res.taskId}`);
7301
8834
  } else if (res.document) {
7302
8835
  success("Parse complete");
7303
8836
  const content = res.document.markdown || res.document.text || JSON.stringify(res.document, null, 2);
@@ -7327,27 +8860,50 @@ program.command("parse-result").description("Get parse result").argument("<task-
7327
8860
  return;
7328
8861
  }
7329
8862
  if (!res.success) {
7330
- error(res.error?.message || "Not ready");
8863
+ errorLine(res.error?.message || "Not ready");
7331
8864
  process.exit(1);
7332
8865
  }
7333
8866
  success("Parse result ready");
7334
8867
  const content = res.document?.markdown || res.document?.text || JSON.stringify(res.document, null, 2);
7335
8868
  console.log(content);
7336
8869
  });
7337
- program.command("recall").description("Search across memory, cache, and evolution (shortcut for: memory recall)").argument("<query>", "Search query").option("--scope <scope>", "Scope: all, memory, cache, evolution", "all").option("-n, --limit <n>", "Max results", "10").option("--json", "JSON output").action(async (query, opts) => {
8870
+ program.command("recall").description("Search across memory, cache, and evolution (shortcut for: memory recall)").argument("<query>", "Search query").option("--scope <scope>", "Scope: all, memory, cache, evolution", "all").option("--layer <layer>", "Alias for --scope (memory | cache | evolution | all)").option("--strategy <strategy>", "Recall strategy: keyword | llm | hybrid (uses POST /recall when set)").option("-n, --limit <n>", "Max results", "10").option("--json", "JSON output").action(async (query, opts) => {
7338
8871
  const client = getIMClient();
7339
- const params = { q: query };
7340
- if (opts.scope) params.scope = opts.scope;
7341
- if (opts.limit) params.limit = opts.limit;
8872
+ let scope = opts.layer || opts.scope || "all";
8873
+ if (scope === "context") scope = "cache";
8874
+ const validStrategies = ["keyword", "llm", "hybrid"];
8875
+ if (opts.strategy && !validStrategies.includes(opts.strategy)) {
8876
+ errorLine(`Invalid --strategy "${opts.strategy}". Use one of: ${validStrategies.join(", ")}.`);
8877
+ process.exit(1);
8878
+ }
7342
8879
  const res = await withSpinner(`Recalling: ${query}`, async () => {
7343
- return client.im.memory._r("GET", "/api/im/recall", void 0, params);
8880
+ if (opts.strategy) {
8881
+ return client.im.request(
8882
+ "POST",
8883
+ "/api/im/recall",
8884
+ {
8885
+ query,
8886
+ strategy: opts.strategy,
8887
+ scope,
8888
+ maxResults: opts.limit ? parseInt(opts.limit, 10) : void 0
8889
+ }
8890
+ );
8891
+ }
8892
+ const params = { q: query, scope };
8893
+ if (opts.limit) params.limit = String(opts.limit);
8894
+ return client.im.request(
8895
+ "GET",
8896
+ "/api/im/recall",
8897
+ void 0,
8898
+ params
8899
+ );
7344
8900
  });
7345
8901
  if (opts.json) {
7346
8902
  console.log(JSON.stringify(res, null, 2));
7347
8903
  return;
7348
8904
  }
7349
8905
  if (!res.ok) {
7350
- error(`Recall failed: ${JSON.stringify(res.error)}`);
8906
+ errorLine(`Recall failed: ${JSON.stringify(res.error)}`);
7351
8907
  process.exit(1);
7352
8908
  }
7353
8909
  const data = res.data || [];
@@ -7356,22 +8912,27 @@ program.command("recall").description("Search across memory, cache, and evolutio
7356
8912
  return;
7357
8913
  }
7358
8914
  const rows = data.map((item) => [
7359
- (item.source || "").toUpperCase(),
7360
- item.title || "?",
8915
+ (item.source || item.memoryType || "").toUpperCase(),
8916
+ item.title || item.path || "?",
7361
8917
  (item.score || 0).toFixed(2)
7362
8918
  ]);
7363
8919
  table(["Source", "Title", "Score"], rows);
7364
8920
  for (const item of data) {
7365
- if (item.snippet) {
7366
- dim2(` ${item.snippet.substring(0, 200)}`);
8921
+ const snippet = item.snippet || item.content;
8922
+ if (snippet) {
8923
+ dim(` ${String(snippet).substring(0, 200)}`);
7367
8924
  }
7368
8925
  }
7369
8926
  });
7370
- program.command("discover").description("Discover available agents (shortcut for: im discover)").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--json", "JSON output").action(async (opts) => {
8927
+ program.command("discover").description("Discover available agents (shortcut for: im discover)").option("--type <type>", "Filter by agent type").option("--capability <cap>", "Filter by capability").option("--online-only", "Only return agents currently online").option("--json", "JSON output").action(async (opts) => {
7371
8928
  const client = getIMClient();
7372
8929
  const discoverOpts = {};
7373
8930
  if (opts.type) discoverOpts.type = opts.type;
7374
8931
  if (opts.capability) discoverOpts.capability = opts.capability;
8932
+ if (opts.onlineOnly) {
8933
+ discoverOpts.status = "online";
8934
+ discoverOpts.onlineOnly = "true";
8935
+ }
7375
8936
  const res = await withSpinner("Discovering agents", async () => {
7376
8937
  return client.im.contacts.discover(discoverOpts);
7377
8938
  });
@@ -7380,7 +8941,7 @@ program.command("discover").description("Discover available agents (shortcut for
7380
8941
  return;
7381
8942
  }
7382
8943
  if (!res.ok) {
7383
- error(`Discovery failed: ${JSON.stringify(res.error)}`);
8944
+ errorLine(`Discovery failed: ${JSON.stringify(res.error)}`);
7384
8945
  process.exit(1);
7385
8946
  }
7386
8947
  const agents = res.data || [];
@@ -7414,12 +8975,18 @@ program.command("daemon <action>").description("Manage background sync daemon (s
7414
8975
  uninstallDaemonService();
7415
8976
  break;
7416
8977
  default:
7417
- error(`Unknown daemon action: ${action}. Use: start, stop, status, install, uninstall`);
8978
+ errorLine(`Unknown daemon action: ${action}. Use: start, stop, status, install, uninstall`);
7418
8979
  process.exit(1);
7419
8980
  }
7420
8981
  });
7421
- displayBanner();
7422
- program.parse(process.argv);
8982
+ var _head = process.argv.slice(0, 2);
8983
+ var _tail = process.argv.slice(2);
8984
+ var { mode: _mode, color: _color, restArgv: _restArgv } = applyCommonFlags(_tail);
8985
+ setUI(new UI({ mode: _mode, color: _color }));
8986
+ if (_mode === "pretty") {
8987
+ displayBanner();
8988
+ }
8989
+ program.parse([..._head, ..._restArgv]);
7423
8990
  // Annotate the CommonJS export names for ESM import in node:
7424
8991
  0 && (module.exports = {
7425
8992
  getAPIClient,