@pmate/cli 0.21.17 → 0.21.18

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.
Files changed (2) hide show
  1. package/dist/index.js +493 -439
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -278,7 +278,7 @@ var init_sessionStore = __esm({
278
278
  import_node_os2 = __toESM(require("os"));
279
279
  import_node_path3 = __toESM(require("path"));
280
280
  init_namespacedRef();
281
- DEFAULT_APP_ID = "pmate:cli";
281
+ DEFAULT_APP_ID = "pmate:factory";
282
282
  SESSION_STORE_VERSION = 3;
283
283
  }
284
284
  });
@@ -9435,8 +9435,8 @@ __export(index_exports, {
9435
9435
  parseCommandArgs: () => parseCommandArgs
9436
9436
  });
9437
9437
  module.exports = __toCommonJS(index_exports);
9438
- var import_node_fs22 = require("fs");
9439
- var import_node_path27 = __toESM(require("path"));
9438
+ var import_node_fs21 = require("fs");
9439
+ var import_node_path26 = __toESM(require("path"));
9440
9440
  var import_chalk4 = __toESM(require("chalk"));
9441
9441
 
9442
9442
  // src/cli.ts
@@ -14585,9 +14585,9 @@ ${indent}\`\`\``;
14585
14585
  static richTextToPlain(richText) {
14586
14586
  return richText.map((item) => item.plain_text ?? item.text?.content ?? "").join("").trim();
14587
14587
  }
14588
- static async requestJson(path28, init) {
14588
+ static async requestJson(path27, init) {
14589
14589
  const token = this.getToken();
14590
- const response = await fetch(`${this.baseUrl}${path28}`, {
14590
+ const response = await fetch(`${this.baseUrl}${path27}`, {
14591
14591
  method: init.method,
14592
14592
  headers: {
14593
14593
  Authorization: `Bearer ${token}`,
@@ -15664,10 +15664,6 @@ var QuizCommandHandler = class {
15664
15664
 
15665
15665
  // src/commands/CollectCommandHandler.ts
15666
15666
  var import_playwright = require("playwright");
15667
- var import_node_crypto4 = require("crypto");
15668
- var import_node_fs12 = require("fs");
15669
- var import_node_os7 = __toESM(require("os"));
15670
- var import_node_path17 = __toESM(require("path"));
15671
15667
 
15672
15668
  // src/collectStore.ts
15673
15669
  var import_node_crypto3 = require("crypto");
@@ -15687,18 +15683,30 @@ function toThreadKey(name) {
15687
15683
  return `${base}-${suffix}`;
15688
15684
  }
15689
15685
  async function readStoredThread(rootDir, threadKey) {
15690
- const filePath = getThreadFilePath(rootDir, threadKey);
15686
+ const filePath = getThreadMetaFilePath(rootDir, threadKey);
15691
15687
  try {
15692
15688
  const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15693
- return JSON.parse(raw);
15689
+ const parsed = JSON.parse(raw);
15690
+ if ("messages" in parsed && Array.isArray(parsed.messages)) {
15691
+ return parsed;
15692
+ }
15693
+ const messages2 = await readStoredMessages(rootDir, threadKey);
15694
+ return {
15695
+ ...parsed,
15696
+ messages: messages2
15697
+ };
15694
15698
  } catch {
15695
- return null;
15699
+ return readLegacyStoredThread(rootDir, threadKey);
15696
15700
  }
15697
15701
  }
15698
15702
  async function writeStoredThread(rootDir, thread) {
15699
- const filePath = getThreadFilePath(rootDir, thread.threadKey);
15700
- await import_node_fs11.promises.mkdir(import_node_path16.default.dirname(filePath), { recursive: true });
15701
- await import_node_fs11.promises.writeFile(filePath, JSON.stringify(thread, null, 2) + "\n", "utf8");
15703
+ const metaPath = getThreadMetaFilePath(rootDir, thread.threadKey);
15704
+ const messagePath = getThreadMessagesFilePath(rootDir, thread.threadKey);
15705
+ const { messages: messages2, ...meta } = thread;
15706
+ await import_node_fs11.promises.mkdir(import_node_path16.default.dirname(metaPath), { recursive: true });
15707
+ await import_node_fs11.promises.writeFile(metaPath, JSON.stringify(meta, null, 2) + "\n", "utf8");
15708
+ const body = messages2.map((message) => JSON.stringify(message)).join("\n");
15709
+ await import_node_fs11.promises.writeFile(messagePath, `${body}${body ? "\n" : ""}`, "utf8");
15702
15710
  }
15703
15711
  async function writeStoredIndex(rootDir, index) {
15704
15712
  await import_node_fs11.promises.mkdir(rootDir, { recursive: true });
@@ -15706,22 +15714,91 @@ async function writeStoredIndex(rootDir, index) {
15706
15714
  await import_node_fs11.promises.writeFile(filePath, JSON.stringify(index, null, 2) + "\n", "utf8");
15707
15715
  }
15708
15716
  function mergeMessages(existing, incoming) {
15709
- const seen = new Set(existing.map((message) => message.hash));
15717
+ const seenMessageIds = new Set(
15718
+ existing.map((message) => normalizeMessageId(message.messageId)).filter((value) => !!value)
15719
+ );
15710
15720
  const merged = [...existing];
15711
15721
  let added = 0;
15712
15722
  for (const message of incoming) {
15713
- if (seen.has(message.hash)) {
15723
+ const incomingMessageId = normalizeMessageId(message.messageId);
15724
+ if (incomingMessageId && seenMessageIds.has(incomingMessageId)) {
15725
+ continue;
15726
+ }
15727
+ const upgradeIndex = merged.findIndex((current) => shouldUpgradeMessage(current, message));
15728
+ if (upgradeIndex >= 0) {
15729
+ merged[upgradeIndex] = message;
15730
+ if (incomingMessageId) {
15731
+ seenMessageIds.add(incomingMessageId);
15732
+ }
15714
15733
  continue;
15715
15734
  }
15716
- seen.add(message.hash);
15735
+ if (incomingMessageId) {
15736
+ seenMessageIds.add(incomingMessageId);
15737
+ }
15717
15738
  merged.push(message);
15718
15739
  added += 1;
15719
15740
  }
15720
15741
  return { messages: merged, added };
15721
15742
  }
15722
- function getThreadFilePath(rootDir, threadKey) {
15743
+ function shouldUpgradeMessage(existing, incoming) {
15744
+ const existingMessageId = normalizeMessageId(existing.messageId);
15745
+ const incomingMessageId = normalizeMessageId(incoming.messageId);
15746
+ if (existingMessageId && incomingMessageId && existingMessageId === incomingMessageId) {
15747
+ return false;
15748
+ }
15749
+ if (existing.time && incoming.time && existing.time !== incoming.time) {
15750
+ return false;
15751
+ }
15752
+ return normalizeMessageField(existing.sender) === normalizeMessageField(incoming.sender) && normalizeMessageField(existing.content) === normalizeMessageField(incoming.content) && joinNormalized(existing.links) === joinNormalized(incoming.links) && joinNormalized(existing.images) === joinNormalized(incoming.images) && (!!incoming.time && !existing.time);
15753
+ }
15754
+ function normalizeMessageField(value) {
15755
+ return (value ?? "").trim();
15756
+ }
15757
+ function normalizeMessageId(value) {
15758
+ const normalized = (value ?? "").trim();
15759
+ return normalized || null;
15760
+ }
15761
+ function joinNormalized(values) {
15762
+ return values.map((value) => value.trim()).join("|");
15763
+ }
15764
+ async function readStoredMessages(rootDir, threadKey) {
15765
+ const filePath = getThreadMessagesFilePath(rootDir, threadKey);
15766
+ try {
15767
+ const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15768
+ return raw.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => normalizeStoredMessage(JSON.parse(line)));
15769
+ } catch {
15770
+ return [];
15771
+ }
15772
+ }
15773
+ async function readLegacyStoredThread(rootDir, threadKey) {
15774
+ const filePath = getLegacyThreadFilePath(rootDir, threadKey);
15775
+ try {
15776
+ const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15777
+ return JSON.parse(raw);
15778
+ } catch {
15779
+ return null;
15780
+ }
15781
+ }
15782
+ function getThreadMetaFilePath(rootDir, threadKey) {
15723
15783
  return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15724
15784
  }
15785
+ function getThreadMessagesFilePath(rootDir, threadKey) {
15786
+ return import_node_path16.default.join(rootDir, "threads", `${threadKey}.jsonl`);
15787
+ }
15788
+ function getLegacyThreadFilePath(rootDir, threadKey) {
15789
+ return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15790
+ }
15791
+ function normalizeStoredMessage(message) {
15792
+ return {
15793
+ messageId: normalizeMessageId(message.messageId),
15794
+ time: message.time ?? null,
15795
+ sender: message.sender ?? null,
15796
+ content: message.content ?? "",
15797
+ links: Array.isArray(message.links) ? message.links : [],
15798
+ images: Array.isArray(message.images) ? message.images : [],
15799
+ collectedAt: message.collectedAt
15800
+ };
15801
+ }
15725
15802
 
15726
15803
  // src/commands/CollectCommandHandler.ts
15727
15804
  var CollectCommandHandler = class {
@@ -15736,22 +15813,39 @@ var CollectCommandHandler = class {
15736
15813
  if (!targetPage) {
15737
15814
  throw new Error("Cannot find target page from CDP contexts.");
15738
15815
  }
15739
- const { rows } = await this.locateThreadRows(targetPage);
15740
- const threads = await this.extractThreads(rows);
15816
+ const threads = await this.extractThreadsFromConversationData(targetPage);
15741
15817
  if (threads.length === 0) {
15742
- throw new Error("No threads found in target page.");
15818
+ throw new Error("No threads found in CC data store.");
15743
15819
  }
15744
15820
  const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
15745
15821
  const results = [];
15822
+ const failures = [];
15746
15823
  for (const thread of threads) {
15747
- await this.clickThreadRow(rows, thread);
15748
- await targetPage.waitForTimeout(1200);
15749
- const messages2 = await this.extractMessages(targetPage, thread.name);
15750
- const result = await this.persistThread(target.rootDir, target, thread, messages2, syncedAt);
15751
- results.push(result);
15824
+ try {
15825
+ const threadKey = toThreadKey(thread.name);
15826
+ const existing = await readStoredThread(target.rootDir, threadKey);
15827
+ const messages2 = await this.extractMessages(targetPage, thread, existing?.messages ?? []);
15828
+ const result = await this.persistThread(
15829
+ target.rootDir,
15830
+ target,
15831
+ thread,
15832
+ messages2,
15833
+ syncedAt,
15834
+ existing
15835
+ );
15836
+ results.push(result);
15837
+ } catch (error) {
15838
+ const message = error instanceof Error ? error.message : String(error);
15839
+ failures.push({ threadName: thread.name, message });
15840
+ ui.warn(`Skipped thread: ${thread.name}`, message);
15841
+ }
15842
+ }
15843
+ if (results.length === 0) {
15844
+ const firstFailure = failures[0]?.message ?? "No thread could be collected.";
15845
+ throw new Error(firstFailure);
15752
15846
  }
15753
15847
  await this.writeIndex(target.rootDir, target, results, syncedAt);
15754
- this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results);
15848
+ this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results, failures);
15755
15849
  } catch (error) {
15756
15850
  const message = error instanceof Error ? error.message : String(error);
15757
15851
  throw new Error(`collect failed: ${message}`);
@@ -15759,11 +15853,10 @@ var CollectCommandHandler = class {
15759
15853
  await browser?.close().catch(() => void 0);
15760
15854
  }
15761
15855
  }
15762
- async persistThread(rootDir, target, thread, messages2, syncedAt) {
15856
+ async persistThread(rootDir, target, thread, messages2, syncedAt, existing) {
15763
15857
  const threadKey = toThreadKey(thread.name);
15764
- const existing = await readStoredThread(rootDir, threadKey);
15765
15858
  const incoming = messages2.map((message) => ({
15766
- hash: message.hash,
15859
+ messageId: message.messageId,
15767
15860
  time: message.time,
15768
15861
  sender: message.sender,
15769
15862
  content: message.content,
@@ -15811,14 +15904,18 @@ var CollectCommandHandler = class {
15811
15904
  threads
15812
15905
  });
15813
15906
  }
15814
- printSyncSummary(sourceKey, rootDir, scannedThreads, results) {
15907
+ printSyncSummary(sourceKey, rootDir, scannedThreads, results, failures) {
15815
15908
  const updatedThreads = results.filter((result) => result.updated).length;
15816
15909
  const newMessages = results.reduce((sum, result) => sum + result.addedMessages, 0);
15817
15910
  ui.plain(`Synced source: ${sourceKey}`);
15818
15911
  ui.plain(`Threads scanned: ${scannedThreads}`);
15819
15912
  ui.plain(`Threads updated: ${updatedThreads}`);
15913
+ ui.plain(`Threads failed: ${failures.length}`);
15820
15914
  ui.plain(`New messages: ${newMessages}`);
15821
15915
  ui.plain(`Saved under: ${rootDir}`);
15916
+ for (const failure of failures.slice(0, 10)) {
15917
+ ui.warn(`Thread failed: ${failure.threadName}`, failure.message);
15918
+ }
15822
15919
  }
15823
15920
  async pickTargetPage(pages) {
15824
15921
  let best = null;
@@ -15833,364 +15930,321 @@ var CollectCommandHandler = class {
15833
15930
  }
15834
15931
  return best?.page ?? null;
15835
15932
  }
15836
- async locateThreadRows(page) {
15837
- const preferred = page.locator(".ReactVirtualized__Grid__innerScrollContainer").first();
15838
- const preferredCount = await preferred.locator(":scope > *").count().catch(() => 0);
15839
- if (preferredCount >= 1) {
15840
- return { rows: preferred.locator(":scope > *") };
15841
- }
15842
- const seeds = [
15843
- '[role="list"]',
15844
- '[role="listbox"]',
15845
- '[class*="list" i]',
15846
- '[class*="conversation" i]',
15847
- '[class*="thread" i]',
15848
- '[class*="chat" i]',
15849
- "div"
15850
- ];
15851
- let best = null;
15852
- for (const seed of seeds) {
15853
- const containers = page.locator(seed);
15854
- const count = await containers.count().catch(() => 0);
15855
- const max = seed === "div" ? Math.min(count, 180) : Math.min(count, 40);
15856
- for (let i = 0; i < max; i += 1) {
15857
- const rows = containers.nth(i).locator(":scope > *");
15858
- const rowCount = await rows.count().catch(() => 0);
15859
- if (rowCount < 1 || rowCount > 500) {
15860
- continue;
15861
- }
15862
- const textCount = await containers.nth(i).locator("text=/./").count().catch(() => 0);
15863
- const score = rowCount + Math.min(textCount, rowCount * 4);
15864
- if (!best || score > best.score) {
15865
- best = { rows, score };
15866
- }
15867
- }
15933
+ async extractThreadsFromConversationData(page) {
15934
+ const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch((error) => ({
15935
+ ok: false,
15936
+ reason: error instanceof Error ? error.message : String(error)
15937
+ }));
15938
+ if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.threads)) {
15939
+ throw new Error(
15940
+ result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
15941
+ );
15868
15942
  }
15869
- if (!best) {
15870
- throw new Error("Failed to detect thread list container.");
15943
+ return result.threads.map((thread) => ({
15944
+ conversationId: normalizeText(thread.conversationId ?? ""),
15945
+ name: normalizeText(thread.name ?? ""),
15946
+ unread: normalizeUnreadCount(thread.unread),
15947
+ preview: normalizeText(thread.preview ?? ""),
15948
+ time: formatSentAt(thread.sentAt)
15949
+ })).filter((thread) => thread.conversationId && thread.name);
15950
+ }
15951
+ async extractMessages(page, thread, existingMessages) {
15952
+ const fromDataStore = await this.extractMessagesFromConversationData(
15953
+ page,
15954
+ thread,
15955
+ existingMessages
15956
+ );
15957
+ if ("error" in fromDataStore) {
15958
+ throw new Error(
15959
+ `Failed to load messages from CC data store for thread: ${thread.name} (${fromDataStore.error})`
15960
+ );
15871
15961
  }
15872
- return { rows: best.rows };
15962
+ return fromDataStore.messages;
15873
15963
  }
15874
- async extractThreads(rows) {
15875
- const count = await rows.count().catch(() => 0);
15876
- const result = [];
15877
- for (let i = 0; i < count; i += 1) {
15878
- const lines = await this.collectLines(rows.nth(i), 12);
15879
- if (lines.length === 0) {
15880
- continue;
15881
- }
15882
- const unread = this.extractUnread(lines);
15883
- const time = this.findTime(lines);
15884
- const name = this.findThreadName(lines, time);
15885
- if (!name) {
15886
- continue;
15887
- }
15888
- const preview = lines.find(
15889
- (line) => line !== name && line !== time && !/^\d{1,4}\+?$/.test(line) && !/^\[[^\]]+\]$/.test(line)
15890
- ) ?? "";
15891
- result.push({ index: i, name, unread, preview, time });
15892
- }
15893
- return dedupeThreads(result);
15894
- }
15895
- async extractMessages(page, threadName) {
15896
- const container = await this.locateMessageContainer(page);
15897
- const rows = container.locator(":scope > *");
15898
- const count = await rows.count().catch(() => 0);
15899
- const messages2 = [];
15900
- let lastFingerprint = "";
15901
- for (let i = 0; i < count; i += 1) {
15902
- const row = rows.nth(i);
15903
- const rawText = normalizeText(await row.textContent().catch(() => "") || "");
15904
- const links = await this.extractLinks(row);
15905
- const images = await this.extractImages(row);
15906
- const parsed = parseMessageText(rawText);
15964
+ async extractMessagesFromConversationData(page, thread, existingMessages) {
15965
+ const limit = Math.max(existingMessages.length + 40, 100);
15966
+ const result = await this.evaluateInPage(
15967
+ page,
15968
+ buildGetMessagesByConversationExpr(thread.conversationId, limit)
15969
+ ).catch((error) => ({ ok: false, reason: error instanceof Error ? error.message : String(error) }));
15970
+ if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.messages)) {
15971
+ return {
15972
+ error: result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
15973
+ };
15974
+ }
15975
+ const seenMessageIds = new Set(
15976
+ existingMessages.map((message) => (message.messageId ?? "").trim()).filter((value) => !!value)
15977
+ );
15978
+ const collected = [];
15979
+ for (let i = result.messages.length - 1; i >= 0; i -= 1) {
15980
+ const raw = result.messages[i];
15981
+ const contentParts = [normalizeText(raw.body ?? "")];
15982
+ if (raw.quote?.text) {
15983
+ contentParts.unshift(`Quote: ${normalizeText(raw.quote.text)}`);
15984
+ }
15985
+ const attachmentPaths = Array.isArray(raw.attachments) ? raw.attachments.map((attachment) => attachment.path ?? null).filter((value) => !!value) : [];
15907
15986
  const message = {
15908
- index: i,
15909
- hash: "",
15910
- time: parsed.time,
15911
- sender: parsed.sender,
15912
- content: parsed.content || (images.length > 0 ? "[Image]" : ""),
15913
- links,
15914
- images
15987
+ index: raw.index,
15988
+ messageId: normalizeText(raw.id ?? "") || null,
15989
+ time: formatSentAt(raw.sentAt),
15990
+ sender: normalizeText(raw.senderName ?? raw.source ?? "") || null,
15991
+ content: contentParts.filter(Boolean).join("\n") || (attachmentPaths.length > 0 ? "[Image]" : ""),
15992
+ links: [],
15993
+ images: attachmentPaths
15915
15994
  };
15916
- message.hash = computeMessageHash(threadName, message);
15917
- const fingerprint = [
15918
- message.time ?? "",
15919
- message.sender ?? "",
15920
- message.content,
15921
- message.links.join("|"),
15922
- message.images.join("|")
15923
- ].join("||");
15924
- if (!message.content && message.links.length === 0 && message.images.length === 0) {
15925
- continue;
15926
- }
15927
- if (fingerprint === lastFingerprint) {
15995
+ if (!message.content && message.images.length === 0) {
15928
15996
  continue;
15929
15997
  }
15930
- lastFingerprint = fingerprint;
15931
- messages2.push(message);
15932
- }
15933
- return messages2;
15934
- }
15935
- async clickThreadRow(rows, matched) {
15936
- const row = rows.nth(matched.index);
15937
- const clicked = await this.tryClick(row);
15938
- if (!clicked) {
15939
- const exactInRows = rows.filter({ hasText: matched.name }).first();
15940
- const exactCount = await exactInRows.count().catch(() => 0);
15941
- if (exactCount > 0 && await this.tryClick(exactInRows)) {
15942
- return;
15998
+ if (message.messageId && seenMessageIds.has(message.messageId)) {
15999
+ break;
15943
16000
  }
15944
- throw new Error(`Failed to select thread row: ${matched.name}`);
16001
+ collected.push(message);
15945
16002
  }
16003
+ return { messages: finalizeIncrementalMessages(collected, true) };
15946
16004
  }
15947
- async tryClick(row) {
16005
+ async evaluateInPage(page, expression) {
16006
+ const session = await page.context().newCDPSession(page);
15948
16007
  try {
15949
- await row.scrollIntoViewIfNeeded({ timeout: 3e3 });
15950
- await row.click({ timeout: 5e3 });
15951
- return true;
15952
- } catch {
15953
- return false;
15954
- }
15955
- }
15956
- async extractLinks(row) {
15957
- const links = row.locator("a[href]");
15958
- const count = await links.count().catch(() => 0);
15959
- const out = [];
15960
- for (let i = 0; i < count; i += 1) {
15961
- const href = await links.nth(i).getAttribute("href").catch(() => null);
15962
- if (href && !out.includes(href)) {
15963
- out.push(href);
16008
+ const response = await session.send("Runtime.evaluate", {
16009
+ expression,
16010
+ returnByValue: true,
16011
+ awaitPromise: true
16012
+ });
16013
+ if (response.exceptionDetails) {
16014
+ throw new Error(response.exceptionDetails.text || "Runtime.evaluate failed");
15964
16015
  }
16016
+ return response.result?.value;
16017
+ } finally {
16018
+ await session.detach().catch(() => void 0);
15965
16019
  }
15966
- return out;
15967
16020
  }
15968
- async extractImages(row) {
15969
- const imgs = row.locator(
15970
- 'img.module-image__image, img[alt="Image attached to message"]'
15971
- );
15972
- const count = await imgs.count().catch(() => 0);
15973
- const out = [];
15974
- for (let i = 0; i < count; i += 1) {
15975
- const src = await imgs.nth(i).getAttribute("src").catch(() => null);
15976
- if (src && !out.includes(src)) {
15977
- const displayable = await toDisplayableImagePath(src);
15978
- out.push(displayable);
15979
- }
15980
- }
15981
- return out;
15982
- }
15983
- async locateMessageContainer(page) {
15984
- const strong = page.locator(".messages");
15985
- const strongCount = await strong.count().catch(() => 0);
15986
- if (strongCount > 0) {
15987
- let bestIdx = 0;
15988
- let bestRows = -1;
15989
- for (let i = 0; i < strongCount; i += 1) {
15990
- const el = strong.nth(i);
15991
- const visible = await el.isVisible().catch(() => false);
15992
- if (!visible) {
15993
- continue;
15994
- }
15995
- const rows = await el.locator(":scope > *").count().catch(() => 0);
15996
- if (rows >= bestRows) {
15997
- bestRows = rows;
15998
- bestIdx = i;
16021
+ };
16022
+ function normalizeText(value) {
16023
+ return value.replace(/\s+/g, " ").replace(/\u200B/g, "").trim();
16024
+ }
16025
+ function normalizeUnreadCount(value) {
16026
+ const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number.parseInt(value, 10) : 0;
16027
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
16028
+ }
16029
+ function buildListThreadsExpr() {
16030
+ return `(function() {
16031
+ try {
16032
+ var runtime = globalThis;
16033
+ var controller = runtime.ConversationController;
16034
+ if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16035
+ function normalize(value) {
16036
+ return String(value || '').replace(/\\s+/g, ' ').trim();
16037
+ }
16038
+ function pickName(model) {
16039
+ var fields = [
16040
+ model && model.get ? model.get('name') : model && model.name,
16041
+ model && model.get ? model.get('groupDisplayName') : model && model.groupDisplayName,
16042
+ model && model.get ? model.get('title') : model && model.title,
16043
+ model && model.get ? model.get('profileName') : model && model.profileName,
16044
+ model && model.get ? model.get('e164') : model && model.e164,
16045
+ model && model.get ? model.get('id') : model && model.id,
16046
+ ];
16047
+ for (var i = 0; i < fields.length; i++) {
16048
+ var value = normalize(fields[i]);
16049
+ if (value) return value;
16050
+ }
16051
+ return '';
16052
+ }
16053
+ function pickPreview(model) {
16054
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16055
+ var fields = [
16056
+ attrs.draft,
16057
+ attrs.lastMessage,
16058
+ attrs.lastMessageText,
16059
+ attrs.snippet,
16060
+ attrs.body,
16061
+ ];
16062
+ for (var i = 0; i < fields.length; i++) {
16063
+ var value = normalize(fields[i]);
16064
+ if (value) return value;
16065
+ }
16066
+ return '';
16067
+ }
16068
+ function pickUnread(model) {
16069
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16070
+ var candidates = [
16071
+ attrs.unreadCount,
16072
+ attrs.unreadMessagesCount,
16073
+ attrs.unread,
16074
+ model && model.get ? model.get('unreadCount') : null,
16075
+ model && model.get ? model.get('unreadMessagesCount') : null,
16076
+ model && model.get ? model.get('unread') : null,
16077
+ ];
16078
+ for (var i = 0; i < candidates.length; i++) {
16079
+ var value = Number(candidates[i] || 0);
16080
+ if (Number.isFinite(value) && value > 0) return value;
15999
16081
  }
16082
+ return 0;
16000
16083
  }
16001
- if (bestRows >= 0) {
16002
- return strong.nth(bestIdx);
16003
- }
16004
- }
16005
- const fallback = page.locator(".discussion-container, .conversation-stack, .main.panel, div");
16006
- const count = await fallback.count().catch(() => 0);
16007
- let best = null;
16008
- for (let i = 0; i < Math.min(count, 280); i += 1) {
16009
- const el = fallback.nth(i);
16010
- const visible = await el.isVisible().catch(() => false);
16011
- if (!visible) {
16012
- continue;
16013
- }
16014
- const children = await el.locator(":scope > *").count().catch(() => 0);
16015
- const texts = await el.locator("text=/./").count().catch(() => 0);
16016
- const score = texts + children;
16017
- if (!best || score > best.score) {
16018
- best = { idx: i, score };
16019
- }
16020
- }
16021
- if (!best) {
16022
- throw new Error("Cannot find conversation container.");
16023
- }
16024
- return fallback.nth(best.idx);
16025
- }
16026
- async collectLines(row, max = 12) {
16027
- const items = row.locator("text=/./");
16028
- const count = await items.count().catch(() => 0);
16029
- const lines = [];
16030
- for (let i = 0; i < Math.min(count, max); i += 1) {
16031
- const text = normalizeText(await items.nth(i).textContent().catch(() => "") || "");
16032
- if (text && !lines.includes(text)) {
16033
- lines.push(text);
16084
+ function pickSentAt(model) {
16085
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16086
+ var candidates = [
16087
+ attrs.active_at,
16088
+ attrs.timestamp,
16089
+ attrs.sent_at,
16090
+ attrs.lastMessageReceivedAt,
16091
+ model && model.get ? model.get('active_at') : null,
16092
+ model && model.get ? model.get('timestamp') : null,
16093
+ model && model.get ? model.get('sent_at') : null,
16094
+ ];
16095
+ for (var i = 0; i < candidates.length; i++) {
16096
+ var value = Number(candidates[i] || 0);
16097
+ if (Number.isFinite(value) && value > 0) return value;
16098
+ }
16099
+ return null;
16034
16100
  }
16035
- }
16036
- return lines;
16037
- }
16038
- extractUnread(lines) {
16039
- let unread = 0;
16040
- for (const line of lines) {
16041
- if (!/^\d{1,4}\+?$/.test(line)) {
16042
- continue;
16101
+ function isArchived(model) {
16102
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16103
+ var candidates = [
16104
+ attrs.isArchived,
16105
+ attrs.archived,
16106
+ model && model.get ? model.get('isArchived') : null,
16107
+ model && model.get ? model.get('archived') : null,
16108
+ ];
16109
+ for (var i = 0; i < candidates.length; i++) {
16110
+ if (candidates[i] === true) return true;
16111
+ }
16112
+ return false;
16043
16113
  }
16044
- const value = Number.parseInt(line.replace("+", ""), 10);
16045
- if (Number.isFinite(value)) {
16046
- unread = Math.max(unread, value);
16114
+ var all = [];
16115
+ if (typeof controller.getAllPrivateConversations === 'function') {
16116
+ var privates = controller.getAllPrivateConversations();
16117
+ all.push.apply(all, privates.models || privates || []);
16118
+ }
16119
+ if (typeof controller.getAllGroups === 'function') {
16120
+ var groups = controller.getAllGroups();
16121
+ all.push.apply(all, groups.models || groups || []);
16122
+ }
16123
+ var seen = {};
16124
+ var threads = [];
16125
+ for (var i = 0; i < all.length; i++) {
16126
+ var model = all[i];
16127
+ var conversationId = normalize(model && model.get ? model.get('id') : model && model.id);
16128
+ if (!conversationId || seen[conversationId]) continue;
16129
+ seen[conversationId] = true;
16130
+ if (isArchived(model)) continue;
16131
+ var name = pickName(model);
16132
+ if (!name) continue;
16133
+ threads.push({
16134
+ conversationId: conversationId,
16135
+ name: name,
16136
+ unread: pickUnread(model),
16137
+ preview: pickPreview(model),
16138
+ sentAt: pickSentAt(model),
16139
+ });
16047
16140
  }
16141
+ threads.sort(function(left, right) {
16142
+ return (Number(right.sentAt || 0) - Number(left.sentAt || 0)) || left.name.localeCompare(right.name);
16143
+ });
16144
+ return { ok: true, threads: threads };
16145
+ } catch(e) {
16146
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16048
16147
  }
16049
- return unread;
16050
- }
16051
- findTime(lines) {
16052
- return lines.find(
16053
- (line) => /^\d{1,2}:\d{2}\s?(?:AM|PM)?$/i.test(line) || /^\d{1,2}\/\d{1,2}$/.test(line) || /^\d{4}\/\d{1,2}\/\d{1,2}$/.test(line)
16054
- ) ?? null;
16055
- }
16056
- findThreadName(lines, time) {
16057
- const candidates = [];
16058
- for (const line of lines) {
16059
- if (line === time) {
16060
- continue;
16061
- }
16062
- if (/^\d{1,4}\+?$/.test(line)) {
16063
- continue;
16064
- }
16065
- if (/^\[[^\]]+\]$/.test(line)) {
16066
- continue;
16148
+ })()`;
16149
+ }
16150
+ function buildGetMessagesByConversationExpr(conversationId, limit) {
16151
+ const escapedConversationId = escapeForJs(conversationId);
16152
+ return `(async function() {
16153
+ try {
16154
+ var runtime = globalThis;
16155
+ var controller = runtime.ConversationController;
16156
+ var signal = (runtime.window && runtime.window.Signal) || runtime.Signal;
16157
+ var whisper = runtime.Whisper;
16158
+ if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16159
+ if (!signal || !signal.Data || !signal.Data.getMessagesByConversation || !whisper || !whisper.MessageCollection) {
16160
+ return { ok: false, reason: 'Signal data APIs are unavailable' };
16161
+ }
16162
+ function normalize(value) {
16163
+ return String(value || '').replace(/\\s+/g, ' ').trim();
16164
+ }
16165
+ function resolveSenderName(source, model) {
16166
+ if (!source) return null;
16167
+ var contact = typeof controller.get === 'function' ? controller.get(source) : null;
16168
+ var fields = [
16169
+ contact && contact.get ? contact.get('name') : contact && contact.name,
16170
+ contact && contact.get ? contact.get('profileName') : contact && contact.profileName,
16171
+ contact && contact.get ? contact.get('title') : contact && contact.title,
16172
+ source,
16173
+ ];
16174
+ for (var i = 0; i < fields.length; i++) {
16175
+ var value = normalize(fields[i]);
16176
+ if (value) return value;
16177
+ }
16178
+ return null;
16067
16179
  }
16068
- candidates.push(line);
16069
- }
16070
- const alphaNoColon = candidates.find(
16071
- (line) => /[A-Za-z\u4e00-\u9fff]/.test(line) && !line.includes(":")
16072
- );
16073
- if (alphaNoColon) {
16074
- return alphaNoColon;
16075
- }
16076
- const alphaPreferred = candidates.find((line) => /[A-Za-z\u4e00-\u9fff]/.test(line));
16077
- if (alphaPreferred) {
16078
- return alphaPreferred;
16079
- }
16080
- return candidates[0] ?? "";
16081
- }
16082
- };
16083
- function dedupeThreads(threads) {
16084
- const seen = /* @__PURE__ */ new Set();
16085
- const result = [];
16086
- for (const thread of threads) {
16087
- const key = `${thread.name}::${thread.preview}::${thread.time ?? ""}`;
16088
- if (seen.has(key)) {
16089
- continue;
16180
+ var conversation = typeof controller.get === 'function' ? controller.get('${escapedConversationId}') : null;
16181
+ if (!conversation) return { ok: false, reason: 'Conversation not found: ${escapedConversationId}' };
16182
+ var fetched = await signal.Data.getMessagesByConversation('${escapedConversationId}', {
16183
+ limit: ${limit},
16184
+ MessageCollection: whisper.MessageCollection,
16185
+ });
16186
+ var models = (fetched.models || fetched || []).slice();
16187
+ models.sort(function(left, right) {
16188
+ var leftSentAt = left && left.get ? (left.get('sent_at') || 0) : (left && left.sent_at) || 0;
16189
+ var rightSentAt = right && right.get ? (right.get('sent_at') || 0) : (right && right.sent_at) || 0;
16190
+ return leftSentAt - rightSentAt;
16191
+ });
16192
+ return {
16193
+ ok: true,
16194
+ conversationId: '${escapedConversationId}',
16195
+ messages: models.map(function(model, index) {
16196
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model;
16197
+ var source = (typeof model.getSource === 'function' ? model.getSource() : attrs.source) || null;
16198
+ var quote = attrs.quote ? {
16199
+ id: attrs.quote.id || null,
16200
+ author: attrs.quote.author || null,
16201
+ text: attrs.quote.text || null,
16202
+ } : null;
16203
+ var attachments = Array.isArray(attrs.attachments) ? attrs.attachments.map(function(attachment) {
16204
+ return {
16205
+ path: attachment && attachment.path || null,
16206
+ contentType: attachment && attachment.contentType || null,
16207
+ fileName: attachment && attachment.fileName || null,
16208
+ };
16209
+ }) : [];
16210
+ return {
16211
+ index: index,
16212
+ body: attrs.body || '',
16213
+ source: source,
16214
+ senderName: resolveSenderName(source, model),
16215
+ sentAt: attrs.sent_at || attrs.timestamp || null,
16216
+ id: attrs.id || model.id || '',
16217
+ attachments: attachments,
16218
+ quote: quote,
16219
+ };
16220
+ }),
16221
+ };
16222
+ } catch(e) {
16223
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16090
16224
  }
16091
- seen.add(key);
16092
- result.push(thread);
16093
- }
16094
- return result;
16225
+ })()`;
16095
16226
  }
16096
- function normalizeText(value) {
16097
- return value.replace(/\s+/g, " ").replace(/\u200B/g, "").trim();
16227
+ function escapeForJs(value) {
16228
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
16098
16229
  }
16099
- function computeMessageHash(threadName, message) {
16100
- const base = [
16101
- normalizeText(threadName),
16102
- normalizeText(message.sender ?? ""),
16103
- normalizeText(message.time ?? ""),
16104
- normalizeText(message.content),
16105
- message.links.map(normalizeText).join("|"),
16106
- message.images.map(normalizeText).join("|")
16107
- ].join("||");
16108
- return (0, import_node_crypto4.createHash)("sha1").update(base).digest("hex").slice(0, 16);
16109
- }
16110
- function parseMessageText(rawText) {
16111
- const text = normalizeText(rawText);
16112
- if (!text) {
16113
- return { time: null, sender: null, content: "" };
16114
- }
16115
- const timeMatch = text.match(/^(\d{1,2}:\d{2}\s?(?:AM|PM)?)\s+(.*)$/i) ?? text.match(/^(\d{1,2}\/\d{1,2}|\d{4}\/\d{1,2}\/\d{1,2})\s+(.*)$/);
16116
- if (!timeMatch) {
16117
- return { time: null, sender: null, content: text };
16118
- }
16119
- const time = timeMatch[1];
16120
- const rest = normalizeText(timeMatch[2] ?? "");
16121
- if (!rest) {
16122
- return { time, sender: null, content: "" };
16123
- }
16124
- const senderMatch = rest.match(/^([^:]{1,80}):\s*(.*)$/);
16125
- if (!senderMatch) {
16126
- return { time, sender: null, content: rest };
16230
+ function formatSentAt(sentAt) {
16231
+ const value = typeof sentAt === "number" ? sentAt : typeof sentAt === "string" && /^\d+$/.test(sentAt) ? Number.parseInt(sentAt, 10) : NaN;
16232
+ if (!Number.isFinite(value) || value <= 0) {
16233
+ return null;
16127
16234
  }
16128
- return {
16129
- time,
16130
- sender: normalizeText(senderMatch[1] ?? "") || null,
16131
- content: normalizeText(senderMatch[2] ?? "")
16132
- };
16235
+ const date = new Date(value);
16236
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
16133
16237
  }
16134
- async function toDisplayableImagePath(src) {
16135
- if (!import_node_path17.default.isAbsolute(src)) {
16136
- return src;
16137
- }
16138
- const ext = import_node_path17.default.extname(src);
16139
- if (ext) {
16140
- return src;
16141
- }
16142
- try {
16143
- const stat = await import_node_fs12.promises.stat(src);
16144
- if (!stat.isFile()) {
16145
- return src;
16146
- }
16147
- const fh = await import_node_fs12.promises.open(src, "r");
16148
- try {
16149
- const buf = Buffer.alloc(32);
16150
- const { bytesRead } = await fh.read(buf, 0, 32, 0);
16151
- const header = buf.subarray(0, bytesRead);
16152
- const guessedExt = guessImageExtension(header);
16153
- if (!guessedExt) {
16154
- return src;
16155
- }
16156
- const cacheDir = import_node_path17.default.join(
16157
- import_node_os7.default.homedir(),
16158
- ".pmate",
16159
- "messages",
16160
- "images"
16161
- );
16162
- await import_node_fs12.promises.mkdir(cacheDir, { recursive: true });
16163
- const base = (0, import_node_crypto4.createHash)("sha1").update(src).digest("hex");
16164
- const displayPath = import_node_path17.default.join(cacheDir, `${base}.${guessedExt}`);
16165
- await import_node_fs12.promises.rm(displayPath, { force: true }).catch(() => void 0);
16166
- await import_node_fs12.promises.copyFile(src, displayPath);
16167
- return displayPath;
16168
- } finally {
16169
- await fh.close();
16170
- }
16171
- } catch {
16172
- return src;
16173
- }
16238
+ function finalizeIncrementalMessages(collectedNewestFirst, _reachedKnownMessage) {
16239
+ return [...collectedNewestFirst].reverse();
16174
16240
  }
16175
- function guessImageExtension(header) {
16176
- if (header.length >= 3 && header[0] === 255 && header[1] === 216 && header[2] === 255) {
16177
- return "jpg";
16178
- }
16179
- if (header.length >= 8 && header[0] === 137 && header[1] === 80 && header[2] === 78 && header[3] === 71) {
16180
- return "png";
16181
- }
16182
- if (header.length >= 4 && header[0] === 71 && header[1] === 73 && header[2] === 70 && header[3] === 56) {
16183
- return "gif";
16184
- }
16185
- if (header.length >= 12 && header.subarray(0, 4).toString("ascii") === "RIFF" && header.subarray(8, 12).toString("ascii") === "WEBP") {
16186
- return "webp";
16187
- }
16188
- return null;
16241
+ function pad2(value) {
16242
+ return String(value).padStart(2, "0");
16189
16243
  }
16190
16244
 
16191
16245
  // src/commands/AgentCommandHandler.ts
16192
- var import_node_fs13 = require("fs");
16193
- var import_node_path18 = __toESM(require("path"));
16246
+ var import_node_fs12 = require("fs");
16247
+ var import_node_path17 = __toESM(require("path"));
16194
16248
  init_AgentLlmClient();
16195
16249
  init_sessionStore();
16196
16250
 
@@ -16198,7 +16252,7 @@ init_sessionStore();
16198
16252
  var STORE_LOG_VERSION = 1;
16199
16253
  var LOG_KIND_TABLE = 1e4;
16200
16254
  var LOG_KIND_MAPPING = 6003;
16201
- function createHash4() {
16255
+ function createHash3() {
16202
16256
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
16203
16257
  }
16204
16258
  function createDescriptor(input) {
@@ -16223,8 +16277,8 @@ function descriptorPathSegments(descriptor) {
16223
16277
  return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
16224
16278
  }
16225
16279
  function buildPathUrl(baseUrl, pathSegments, query) {
16226
- const path28 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16227
- const url = new URL(path28, baseUrl);
16280
+ const path27 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16281
+ const url = new URL(path27, baseUrl);
16228
16282
  if (query) {
16229
16283
  for (const [key, value] of Object.entries(query)) {
16230
16284
  url.searchParams.set(key, String(value));
@@ -16337,7 +16391,7 @@ var Namespace = class {
16337
16391
  },
16338
16392
  appendRow: async (data) => {
16339
16393
  const log = {
16340
- hash: createHash4(),
16394
+ hash: createHash3(),
16341
16395
  t: Date.now(),
16342
16396
  kind: LOG_KIND_TABLE,
16343
16397
  topic: descriptorToTopic(descriptor),
@@ -16349,7 +16403,7 @@ var Namespace = class {
16349
16403
  updateRow: async (data) => {
16350
16404
  const { id, ...value } = data;
16351
16405
  const log = {
16352
- hash: createHash4(),
16406
+ hash: createHash3(),
16353
16407
  t: Date.now(),
16354
16408
  kind: LOG_KIND_TABLE,
16355
16409
  topic: descriptorToTopic(descriptor),
@@ -16360,7 +16414,7 @@ var Namespace = class {
16360
16414
  },
16361
16415
  deleteRow: async (id) => {
16362
16416
  const log = {
16363
- hash: createHash4(),
16417
+ hash: createHash3(),
16364
16418
  t: Date.now(),
16365
16419
  kind: LOG_KIND_TABLE,
16366
16420
  topic: descriptorToTopic(descriptor),
@@ -16403,7 +16457,7 @@ var Namespace = class {
16403
16457
  },
16404
16458
  set: async (key, value) => {
16405
16459
  const log = {
16406
- hash: createHash4(),
16460
+ hash: createHash3(),
16407
16461
  t: Date.now(),
16408
16462
  kind: LOG_KIND_MAPPING,
16409
16463
  topic: descriptorToTopic(descriptor),
@@ -16414,7 +16468,7 @@ var Namespace = class {
16414
16468
  },
16415
16469
  delete: async (key) => {
16416
16470
  const log = {
16417
- hash: createHash4(),
16471
+ hash: createHash3(),
16418
16472
  t: Date.now(),
16419
16473
  kind: LOG_KIND_MAPPING,
16420
16474
  topic: descriptorToTopic(descriptor),
@@ -16656,11 +16710,11 @@ var AgentCommandHandler = class {
16656
16710
  }).token;
16657
16711
  }
16658
16712
  readJsonFile(filePath) {
16659
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16660
- if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16713
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16714
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16661
16715
  throw new Error(`JSON file not found: ${absolutePath}`);
16662
16716
  }
16663
- const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16717
+ const raw = (0, import_node_fs12.readFileSync)(absolutePath, "utf8");
16664
16718
  try {
16665
16719
  return JSON.parse(raw);
16666
16720
  } catch (error) {
@@ -16671,7 +16725,7 @@ var AgentCommandHandler = class {
16671
16725
  readJsonObjectFile(filePath) {
16672
16726
  const parsed = this.readJsonFile(filePath);
16673
16727
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16674
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16728
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16675
16729
  throw new Error(`Invalid JSON in ${absolutePath}: expected an object`);
16676
16730
  }
16677
16731
  return parsed;
@@ -16755,8 +16809,8 @@ var AgentCommandHandler = class {
16755
16809
  };
16756
16810
 
16757
16811
  // src/commands/RunnerCommandHandler.ts
16758
- var import_node_fs14 = require("fs");
16759
- var import_node_path19 = __toESM(require("path"));
16812
+ var import_node_fs13 = require("fs");
16813
+ var import_node_path18 = __toESM(require("path"));
16760
16814
  init_sessionStore();
16761
16815
  init_namespacedRef();
16762
16816
  var RunnerCommandHandler = class {
@@ -16851,11 +16905,11 @@ var RunnerCommandHandler = class {
16851
16905
  }).token;
16852
16906
  }
16853
16907
  readJsonFile(filePath) {
16854
- const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
16855
- if (!(0, import_node_fs14.existsSync)(absolutePath)) {
16908
+ const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16909
+ if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16856
16910
  throw new Error(`JSON file not found: ${absolutePath}`);
16857
16911
  }
16858
- const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
16912
+ const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16859
16913
  try {
16860
16914
  return JSON.parse(raw);
16861
16915
  } catch (error) {
@@ -16974,8 +17028,8 @@ var NsCommandHandler = class {
16974
17028
  };
16975
17029
 
16976
17030
  // src/commands/StoreCommandHandler.ts
16977
- var import_node_fs15 = require("fs");
16978
- var import_node_path20 = __toESM(require("path"));
17031
+ var import_node_fs14 = require("fs");
17032
+ var import_node_path19 = __toESM(require("path"));
16979
17033
  var StoreCommandHandler = class {
16980
17034
  async handle(args) {
16981
17035
  if (args.scope === "namespace") {
@@ -17117,11 +17171,11 @@ var StoreCommandHandler = class {
17117
17171
  return payload.data ?? [];
17118
17172
  }
17119
17173
  readJsonFile(filePath) {
17120
- const absolutePath = import_node_path20.default.resolve(process.cwd(), filePath);
17121
- if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17174
+ const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
17175
+ if (!(0, import_node_fs14.existsSync)(absolutePath)) {
17122
17176
  throw new Error(`JSON file not found: ${absolutePath}`);
17123
17177
  }
17124
- const raw = (0, import_node_fs15.readFileSync)(absolutePath, "utf8");
17178
+ const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
17125
17179
  try {
17126
17180
  return JSON.parse(raw);
17127
17181
  } catch (error) {
@@ -17173,13 +17227,13 @@ var StoreCommandHandler = class {
17173
17227
  };
17174
17228
 
17175
17229
  // src/commands/AppCommandHandler.ts
17176
- var import_node_fs17 = require("fs");
17177
- var import_node_path22 = __toESM(require("path"));
17230
+ var import_node_fs16 = require("fs");
17231
+ var import_node_path21 = __toESM(require("path"));
17178
17232
  init_sessionStore();
17179
17233
 
17180
17234
  // src/appIcon.ts
17181
- var import_node_fs16 = require("fs");
17182
- var import_node_path21 = __toESM(require("path"));
17235
+ var import_node_fs15 = require("fs");
17236
+ var import_node_path20 = __toESM(require("path"));
17183
17237
  var import_sharp = __toESM(require("sharp"));
17184
17238
  init_namespacedRef();
17185
17239
  var ANDROID_ICON_SPECS = [
@@ -17233,13 +17287,13 @@ function createAppIconExportPlans(input) {
17233
17287
  if (!input.outDir) {
17234
17288
  throw new Error("App icon preset export requires --out-dir.");
17235
17289
  }
17236
- const outDir = import_node_path21.default.resolve(input.cwd, input.outDir);
17237
- (0, import_node_fs16.mkdirSync)(outDir, { recursive: true });
17290
+ const outDir = import_node_path20.default.resolve(input.cwd, input.outDir);
17291
+ (0, import_node_fs15.mkdirSync)(outDir, { recursive: true });
17238
17292
  return specs.map((spec) => ({
17239
17293
  width: spec.width,
17240
17294
  height: spec.height,
17241
17295
  format: "png",
17242
- outputPath: import_node_path21.default.join(
17296
+ outputPath: import_node_path20.default.join(
17243
17297
  outDir,
17244
17298
  `${namespace}-${objectId}-${spec.name}-${spec.width}x${spec.height}.png`
17245
17299
  )
@@ -17252,8 +17306,8 @@ function createAppIconExportPlans(input) {
17252
17306
  if (!input.output) {
17253
17307
  throw new Error("Single-size app icon export requires --output.");
17254
17308
  }
17255
- const resolvedOutput = import_node_path21.default.resolve(input.cwd, input.output);
17256
- (0, import_node_fs16.mkdirSync)(import_node_path21.default.dirname(resolvedOutput), { recursive: true });
17309
+ const resolvedOutput = import_node_path20.default.resolve(input.cwd, input.output);
17310
+ (0, import_node_fs15.mkdirSync)(import_node_path20.default.dirname(resolvedOutput), { recursive: true });
17257
17311
  return [
17258
17312
  {
17259
17313
  width: input.width,
@@ -17267,7 +17321,7 @@ async function exportAppIcon(icon, plans) {
17267
17321
  const svg = await resolveAppIconSvg(icon);
17268
17322
  for (const plan of plans) {
17269
17323
  const bytes = await renderAppIcon(svg, plan.width, plan.height, plan.format);
17270
- (0, import_node_fs16.writeFileSync)(plan.outputPath, bytes);
17324
+ (0, import_node_fs15.writeFileSync)(plan.outputPath, bytes);
17271
17325
  }
17272
17326
  }
17273
17327
  async function resolveAppIconSvg(icon) {
@@ -17468,10 +17522,10 @@ var AppCommandHandler = class {
17468
17522
  }
17469
17523
  readJsonFile(filePath) {
17470
17524
  const absolutePath = this.resolveFilePath(filePath);
17471
- if (!(0, import_node_fs17.existsSync)(absolutePath)) {
17525
+ if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17472
17526
  throw new Error(`JSON file not found: ${absolutePath}`);
17473
17527
  }
17474
- const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
17528
+ const raw = (0, import_node_fs16.readFileSync)(absolutePath, "utf8");
17475
17529
  try {
17476
17530
  return JSON.parse(raw);
17477
17531
  } catch (error) {
@@ -17481,10 +17535,10 @@ var AppCommandHandler = class {
17481
17535
  }
17482
17536
  preparePayload(filePath) {
17483
17537
  const absolutePath = this.resolveFilePath(filePath);
17484
- return prepareAppPayload(this.readJsonFile(filePath), import_node_path22.default.dirname(absolutePath));
17538
+ return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath));
17485
17539
  }
17486
17540
  resolveFilePath(filePath) {
17487
- return import_node_path22.default.resolve(process.cwd(), filePath);
17541
+ return import_node_path21.default.resolve(process.cwd(), filePath);
17488
17542
  }
17489
17543
  async request(baseUrl, token, method, pathname, body) {
17490
17544
  const response = await fetch(`${baseUrl}${pathname}`, {
@@ -17558,15 +17612,15 @@ function readNamespaceFromPayload(payload) {
17558
17612
  }
17559
17613
 
17560
17614
  // src/commands/ServiceCommandHandler.ts
17561
- var import_node_fs19 = require("fs");
17615
+ var import_node_fs18 = require("fs");
17562
17616
  var import_promises5 = require("fs/promises");
17563
- var import_node_path24 = __toESM(require("path"));
17617
+ var import_node_path23 = __toESM(require("path"));
17564
17618
  var import_promises6 = require("readline/promises");
17565
17619
 
17566
17620
  // src/services/ServiceRuntimeRunner.ts
17567
- var import_node_fs18 = require("fs");
17568
- var import_node_os8 = __toESM(require("os"));
17569
- var import_node_path23 = __toESM(require("path"));
17621
+ var import_node_fs17 = require("fs");
17622
+ var import_node_os7 = __toESM(require("os"));
17623
+ var import_node_path22 = __toESM(require("path"));
17570
17624
  var import_node_child_process10 = require("child_process");
17571
17625
  var import_node_readline = require("readline");
17572
17626
  var import_chalk3 = __toESM(require("chalk"));
@@ -17731,14 +17785,14 @@ var ServiceRuntimeRunner = class {
17731
17785
  }
17732
17786
  resolveRepoRoot(service, cwd) {
17733
17787
  if (cwd) {
17734
- return import_node_path23.default.resolve(process.cwd(), cwd);
17788
+ return import_node_path22.default.resolve(process.cwd(), cwd);
17735
17789
  }
17736
17790
  const repository = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getSite(service).repository?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).repository?.trim();
17737
17791
  if (repository) {
17738
17792
  const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
17739
17793
  if (repoName) {
17740
- const candidate = import_node_path23.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName);
17741
- if ((0, import_node_fs18.existsSync)(candidate)) {
17794
+ const candidate = import_node_path22.default.join(import_node_os7.default.homedir(), "dev", "pmate", repoName);
17795
+ if ((0, import_node_fs17.existsSync)(candidate)) {
17742
17796
  return candidate;
17743
17797
  }
17744
17798
  }
@@ -17751,12 +17805,12 @@ var ServiceRuntimeRunner = class {
17751
17805
  if (!dir) {
17752
17806
  throw new Error(`Missing service dir for ${service.id}`);
17753
17807
  }
17754
- return import_node_path23.default.resolve(repoRoot, dir);
17808
+ return import_node_path22.default.resolve(repoRoot, dir);
17755
17809
  }
17756
17810
  async deployVite(service, options) {
17757
17811
  const appDir = this.resolveLocalDir(service, options.cwd);
17758
- const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17759
- if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17812
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17813
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17760
17814
  throw new Error(`No package.json found at ${packageJsonPath}`);
17761
17815
  }
17762
17816
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -17765,8 +17819,8 @@ var ServiceRuntimeRunner = class {
17765
17819
  throw new Error(`Missing package name in ${packageJsonPath}`);
17766
17820
  }
17767
17821
  await this.runCommand("pnpm", ["--filter", pkgName, "build"], this.resolveRepoRoot(service, options.cwd));
17768
- const distDir = import_node_path23.default.join(appDir, "dist");
17769
- if (!(0, import_node_fs18.existsSync)(distDir)) {
17822
+ const distDir = import_node_path22.default.join(appDir, "dist");
17823
+ if (!(0, import_node_fs17.existsSync)(distDir)) {
17770
17824
  throw new Error(`No dist directory found at ${distDir}`);
17771
17825
  }
17772
17826
  const site = this.getSite(service);
@@ -17778,8 +17832,8 @@ var ServiceRuntimeRunner = class {
17778
17832
  }
17779
17833
  async deployStatic(service, options) {
17780
17834
  const appDir = this.resolveLocalDir(service, options.cwd);
17781
- const srcDir = import_node_path23.default.join(appDir, "src");
17782
- if (!(0, import_node_fs18.existsSync)(srcDir)) {
17835
+ const srcDir = import_node_path22.default.join(appDir, "src");
17836
+ if (!(0, import_node_fs17.existsSync)(srcDir)) {
17783
17837
  throw new Error(`No src directory found at ${srcDir}`);
17784
17838
  }
17785
17839
  const site = this.getSite(service);
@@ -17899,7 +17953,7 @@ var ServiceRuntimeRunner = class {
17899
17953
  const serverAddress = await resolveServerAddress(server);
17900
17954
  const templateName = exposeType === "websocket" ? "nginx.ws.template" : "nginx.rest.template";
17901
17955
  const templatePath = this.resolveTemplatePath(templateName);
17902
- const template = (0, import_node_fs18.readFileSync)(templatePath, "utf8");
17956
+ const template = (0, import_node_fs17.readFileSync)(templatePath, "utf8");
17903
17957
  const host = `${subdomain}.${("domain" in network ? network.domain?.trim() : void 0) || "pmate.chat"}`;
17904
17958
  const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
17905
17959
  const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
@@ -17931,11 +17985,11 @@ var ServiceRuntimeRunner = class {
17931
17985
  }
17932
17986
  if (config.ensurePackageJson) {
17933
17987
  const appDir = this.resolveLocalDir(service, options.cwd);
17934
- const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17935
- if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17988
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17989
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17936
17990
  throw new Error(`Missing package.json at ${packageJsonPath}`);
17937
17991
  }
17938
- const pkgStat = (0, import_node_fs18.statSync)(packageJsonPath);
17992
+ const pkgStat = (0, import_node_fs17.statSync)(packageJsonPath);
17939
17993
  if (!pkgStat.isFile()) {
17940
17994
  throw new Error(`package.json is not a file at ${packageJsonPath}`);
17941
17995
  }
@@ -18359,9 +18413,9 @@ var ServiceRuntimeRunner = class {
18359
18413
  return tokens;
18360
18414
  }
18361
18415
  readPathValue(source, pathExpr) {
18362
- const path28 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18416
+ const path27 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18363
18417
  let cursor = source;
18364
- for (const segment of path28) {
18418
+ for (const segment of path27) {
18365
18419
  if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
18366
18420
  return void 0;
18367
18421
  }
@@ -18404,12 +18458,12 @@ var ServiceRuntimeRunner = class {
18404
18458
  }
18405
18459
  resolveTemplatePath(templateName) {
18406
18460
  const candidates = [
18407
- import_node_path23.default.join(__dirname, "..", "templates", templateName),
18408
- import_node_path23.default.join(__dirname, "..", "src", "templates", templateName),
18409
- import_node_path23.default.join(__dirname, "templates", templateName)
18461
+ import_node_path22.default.join(__dirname, "..", "templates", templateName),
18462
+ import_node_path22.default.join(__dirname, "..", "src", "templates", templateName),
18463
+ import_node_path22.default.join(__dirname, "templates", templateName)
18410
18464
  ];
18411
18465
  for (const candidate of candidates) {
18412
- if ((0, import_node_fs18.existsSync)(candidate)) {
18466
+ if ((0, import_node_fs17.existsSync)(candidate)) {
18413
18467
  return candidate;
18414
18468
  }
18415
18469
  }
@@ -18441,7 +18495,7 @@ var ServiceRuntimeRunner = class {
18441
18495
  });
18442
18496
  }
18443
18497
  loadPackageJson(packageJsonPath) {
18444
- const raw = (0, import_node_fs18.readFileSync)(packageJsonPath, "utf8");
18498
+ const raw = (0, import_node_fs17.readFileSync)(packageJsonPath, "utf8");
18445
18499
  const parsed = JSON.parse(raw);
18446
18500
  if (!parsed || typeof parsed !== "object") {
18447
18501
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -18450,7 +18504,7 @@ var ServiceRuntimeRunner = class {
18450
18504
  }
18451
18505
  writePackageJson(packageJsonPath, data) {
18452
18506
  const serialized = JSON.stringify(data, null, 2);
18453
- (0, import_node_fs18.writeFileSync)(packageJsonPath, `${serialized}
18507
+ (0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
18454
18508
  `, "utf8");
18455
18509
  }
18456
18510
  runCommand(command, args, cwd) {
@@ -18787,7 +18841,7 @@ var ServiceCommandHandler = class {
18787
18841
  format: "dotenv"
18788
18842
  }
18789
18843
  );
18790
- const outputPath = import_node_path24.default.resolve(process.cwd(), args.output);
18844
+ const outputPath = import_node_path23.default.resolve(process.cwd(), args.output);
18791
18845
  await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
18792
18846
  ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
18793
18847
  this.renderJson({ ...payload, output: outputPath }, args.json);
@@ -18816,11 +18870,11 @@ var ServiceCommandHandler = class {
18816
18870
  return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
18817
18871
  }
18818
18872
  readJsonFile(filePath) {
18819
- const absolutePath = import_node_path24.default.resolve(process.cwd(), filePath);
18820
- if (!(0, import_node_fs19.existsSync)(absolutePath)) {
18873
+ const absolutePath = import_node_path23.default.resolve(process.cwd(), filePath);
18874
+ if (!(0, import_node_fs18.existsSync)(absolutePath)) {
18821
18875
  throw new Error(`JSON file not found: ${absolutePath}`);
18822
18876
  }
18823
- const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
18877
+ const raw = (0, import_node_fs18.readFileSync)(absolutePath, "utf8");
18824
18878
  try {
18825
18879
  return JSON.parse(raw);
18826
18880
  } catch (error) {
@@ -19160,8 +19214,8 @@ var RoleCommandHandler = class {
19160
19214
  };
19161
19215
 
19162
19216
  // src/commands/AccountCommandHandler.ts
19163
- var import_node_fs20 = require("fs");
19164
- var import_node_path25 = __toESM(require("path"));
19217
+ var import_node_fs19 = require("fs");
19218
+ var import_node_path24 = __toESM(require("path"));
19165
19219
  init_sessionStore();
19166
19220
  var AccountCommandHandler = class {
19167
19221
  async handle(args) {
@@ -19409,10 +19463,10 @@ var AccountCommandHandler = class {
19409
19463
  throw new Error("Missing visible apps input");
19410
19464
  }
19411
19465
  const absolutePath = this.resolveFilePath(args.filePath);
19412
- if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19466
+ if (!(0, import_node_fs19.existsSync)(absolutePath)) {
19413
19467
  throw new Error(`JSON file not found: ${absolutePath}`);
19414
19468
  }
19415
- const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19469
+ const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
19416
19470
  let parsed;
19417
19471
  try {
19418
19472
  parsed = JSON.parse(raw);
@@ -19426,7 +19480,7 @@ var AccountCommandHandler = class {
19426
19480
  return parsed;
19427
19481
  }
19428
19482
  resolveFilePath(filePath) {
19429
- return import_node_path25.default.resolve(process.cwd(), filePath);
19483
+ return import_node_path24.default.resolve(process.cwd(), filePath);
19430
19484
  }
19431
19485
  };
19432
19486
  function encodePathSegment(value) {
@@ -19434,8 +19488,8 @@ function encodePathSegment(value) {
19434
19488
  }
19435
19489
 
19436
19490
  // src/commands/OrgCommandHandler.ts
19437
- var import_node_fs21 = require("fs");
19438
- var import_node_path26 = __toESM(require("path"));
19491
+ var import_node_fs20 = require("fs");
19492
+ var import_node_path25 = __toESM(require("path"));
19439
19493
  init_sessionStore();
19440
19494
  var OrgCommandHandler = class {
19441
19495
  async handle(args) {
@@ -19606,11 +19660,11 @@ var OrgCommandHandler = class {
19606
19660
  if (!filePath) {
19607
19661
  throw new Error(`Missing ${itemLabel} input`);
19608
19662
  }
19609
- const absolutePath = import_node_path26.default.resolve(process.cwd(), filePath);
19610
- if (!(0, import_node_fs21.existsSync)(absolutePath)) {
19663
+ const absolutePath = import_node_path25.default.resolve(process.cwd(), filePath);
19664
+ if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19611
19665
  throw new Error(`JSON file not found: ${absolutePath}`);
19612
19666
  }
19613
- const raw = (0, import_node_fs21.readFileSync)(absolutePath, "utf8");
19667
+ const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19614
19668
  let parsed;
19615
19669
  try {
19616
19670
  parsed = JSON.parse(raw);
@@ -22099,7 +22153,7 @@ function requireValue11(tokens, option) {
22099
22153
 
22100
22154
  // src/index.ts
22101
22155
  var pkg = JSON.parse(
22102
- (0, import_node_fs22.readFileSync)(import_node_path27.default.resolve(__dirname, "..", "package.json"), "utf8")
22156
+ (0, import_node_fs21.readFileSync)(import_node_path26.default.resolve(__dirname, "..", "package.json"), "utf8")
22103
22157
  );
22104
22158
  var HELP_TEXT = [
22105
22159
  import_chalk4.default.bold(`pmate v${pkg.version}`),
@@ -22193,7 +22247,7 @@ var HELP_TEXT = [
22193
22247
  ` ${import_chalk4.default.gray(" move --from <id> --ns <namespace> --to-ns <namespace> [--to <id>] [--base-url <url>] [--json]")}`,
22194
22248
  ` ${import_chalk4.default.gray(" call <ns:objectId> [--payload <json>] [--file <path>] [--base-url <url>] [--hub-base-url <url>] [--from <id>] [--json]")}`,
22195
22249
  ` ${import_chalk4.default.gray(" delete <ns:objectId> [--base-url <url>] [--json]")}`,
22196
- ` ${import_chalk4.default.gray(" list defaults to all namespaces; create defaults to namespace: pmate; default base URL: https://agent-api.pmate.chat; call uses pmate:cli session and default hub: https://hub.pmate.chat")}`,
22250
+ ` ${import_chalk4.default.gray(" list defaults to all namespaces; create defaults to namespace: pmate; default base URL: https://agent-api.pmate.chat; call uses pmate:factory session and default hub: https://hub.pmate.chat")}`,
22197
22251
  ` ${import_chalk4.default.cyan("runner")} Manage runner registry entries via agent-api`,
22198
22252
  ` ${import_chalk4.default.gray(" list [--namespace <ns>] [--base-url <url>] [--json]")}`,
22199
22253
  ` ${import_chalk4.default.gray(" get <ns:objectId> [--base-url <url>] [--json]")}`,