@pmate/cli 0.21.13 → 0.21.15

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 +482 -442
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -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,307 @@ 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);
16034
- }
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;
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;
16043
16100
  }
16044
- const value = Number.parseInt(line.replace("+", ""), 10);
16045
- if (Number.isFinite(value)) {
16046
- unread = Math.max(unread, value);
16101
+ var all = [];
16102
+ if (typeof controller.getAllPrivateConversations === 'function') {
16103
+ var privates = controller.getAllPrivateConversations();
16104
+ all.push.apply(all, privates.models || privates || []);
16105
+ }
16106
+ if (typeof controller.getAllGroups === 'function') {
16107
+ var groups = controller.getAllGroups();
16108
+ all.push.apply(all, groups.models || groups || []);
16109
+ }
16110
+ var seen = {};
16111
+ var threads = [];
16112
+ for (var i = 0; i < all.length; i++) {
16113
+ var model = all[i];
16114
+ var conversationId = normalize(model && model.get ? model.get('id') : model && model.id);
16115
+ if (!conversationId || seen[conversationId]) continue;
16116
+ seen[conversationId] = true;
16117
+ var name = pickName(model);
16118
+ if (!name) continue;
16119
+ threads.push({
16120
+ conversationId: conversationId,
16121
+ name: name,
16122
+ unread: pickUnread(model),
16123
+ preview: pickPreview(model),
16124
+ sentAt: pickSentAt(model),
16125
+ });
16047
16126
  }
16127
+ threads.sort(function(left, right) {
16128
+ return (Number(right.sentAt || 0) - Number(left.sentAt || 0)) || left.name.localeCompare(right.name);
16129
+ });
16130
+ return { ok: true, threads: threads };
16131
+ } catch(e) {
16132
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16048
16133
  }
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;
16134
+ })()`;
16135
+ }
16136
+ function buildGetMessagesByConversationExpr(conversationId, limit) {
16137
+ const escapedConversationId = escapeForJs(conversationId);
16138
+ return `(async function() {
16139
+ try {
16140
+ var runtime = globalThis;
16141
+ var controller = runtime.ConversationController;
16142
+ var signal = (runtime.window && runtime.window.Signal) || runtime.Signal;
16143
+ var whisper = runtime.Whisper;
16144
+ if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16145
+ if (!signal || !signal.Data || !signal.Data.getMessagesByConversation || !whisper || !whisper.MessageCollection) {
16146
+ return { ok: false, reason: 'Signal data APIs are unavailable' };
16147
+ }
16148
+ function normalize(value) {
16149
+ return String(value || '').replace(/\\s+/g, ' ').trim();
16150
+ }
16151
+ function resolveSenderName(source, model) {
16152
+ if (!source) return null;
16153
+ var contact = typeof controller.get === 'function' ? controller.get(source) : null;
16154
+ var fields = [
16155
+ contact && contact.get ? contact.get('name') : contact && contact.name,
16156
+ contact && contact.get ? contact.get('profileName') : contact && contact.profileName,
16157
+ contact && contact.get ? contact.get('title') : contact && contact.title,
16158
+ source,
16159
+ ];
16160
+ for (var i = 0; i < fields.length; i++) {
16161
+ var value = normalize(fields[i]);
16162
+ if (value) return value;
16163
+ }
16164
+ return null;
16067
16165
  }
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;
16166
+ var conversation = typeof controller.get === 'function' ? controller.get('${escapedConversationId}') : null;
16167
+ if (!conversation) return { ok: false, reason: 'Conversation not found: ${escapedConversationId}' };
16168
+ var fetched = await signal.Data.getMessagesByConversation('${escapedConversationId}', {
16169
+ limit: ${limit},
16170
+ MessageCollection: whisper.MessageCollection,
16171
+ });
16172
+ var models = (fetched.models || fetched || []).slice();
16173
+ models.sort(function(left, right) {
16174
+ var leftSentAt = left && left.get ? (left.get('sent_at') || 0) : (left && left.sent_at) || 0;
16175
+ var rightSentAt = right && right.get ? (right.get('sent_at') || 0) : (right && right.sent_at) || 0;
16176
+ return leftSentAt - rightSentAt;
16177
+ });
16178
+ return {
16179
+ ok: true,
16180
+ conversationId: '${escapedConversationId}',
16181
+ messages: models.map(function(model, index) {
16182
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model;
16183
+ var source = (typeof model.getSource === 'function' ? model.getSource() : attrs.source) || null;
16184
+ var quote = attrs.quote ? {
16185
+ id: attrs.quote.id || null,
16186
+ author: attrs.quote.author || null,
16187
+ text: attrs.quote.text || null,
16188
+ } : null;
16189
+ var attachments = Array.isArray(attrs.attachments) ? attrs.attachments.map(function(attachment) {
16190
+ return {
16191
+ path: attachment && attachment.path || null,
16192
+ contentType: attachment && attachment.contentType || null,
16193
+ fileName: attachment && attachment.fileName || null,
16194
+ };
16195
+ }) : [];
16196
+ return {
16197
+ index: index,
16198
+ body: attrs.body || '',
16199
+ source: source,
16200
+ senderName: resolveSenderName(source, model),
16201
+ sentAt: attrs.sent_at || attrs.timestamp || null,
16202
+ id: attrs.id || model.id || '',
16203
+ attachments: attachments,
16204
+ quote: quote,
16205
+ };
16206
+ }),
16207
+ };
16208
+ } catch(e) {
16209
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16090
16210
  }
16091
- seen.add(key);
16092
- result.push(thread);
16093
- }
16094
- return result;
16211
+ })()`;
16095
16212
  }
16096
- function normalizeText(value) {
16097
- return value.replace(/\s+/g, " ").replace(/\u200B/g, "").trim();
16213
+ function escapeForJs(value) {
16214
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
16098
16215
  }
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 };
16216
+ function formatSentAt(sentAt) {
16217
+ const value = typeof sentAt === "number" ? sentAt : typeof sentAt === "string" && /^\d+$/.test(sentAt) ? Number.parseInt(sentAt, 10) : NaN;
16218
+ if (!Number.isFinite(value) || value <= 0) {
16219
+ return null;
16127
16220
  }
16128
- return {
16129
- time,
16130
- sender: normalizeText(senderMatch[1] ?? "") || null,
16131
- content: normalizeText(senderMatch[2] ?? "")
16132
- };
16221
+ const date = new Date(value);
16222
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
16133
16223
  }
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
- }
16224
+ function finalizeIncrementalMessages(collectedNewestFirst, _reachedKnownMessage) {
16225
+ return [...collectedNewestFirst].reverse();
16174
16226
  }
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;
16227
+ function pad2(value) {
16228
+ return String(value).padStart(2, "0");
16189
16229
  }
16190
16230
 
16191
16231
  // src/commands/AgentCommandHandler.ts
16192
- var import_node_fs13 = require("fs");
16193
- var import_node_path18 = __toESM(require("path"));
16232
+ var import_node_fs12 = require("fs");
16233
+ var import_node_path17 = __toESM(require("path"));
16194
16234
  init_AgentLlmClient();
16195
16235
  init_sessionStore();
16196
16236
 
@@ -16198,7 +16238,7 @@ init_sessionStore();
16198
16238
  var STORE_LOG_VERSION = 1;
16199
16239
  var LOG_KIND_TABLE = 1e4;
16200
16240
  var LOG_KIND_MAPPING = 6003;
16201
- function createHash4() {
16241
+ function createHash3() {
16202
16242
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
16203
16243
  }
16204
16244
  function createDescriptor(input) {
@@ -16223,8 +16263,8 @@ function descriptorPathSegments(descriptor) {
16223
16263
  return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
16224
16264
  }
16225
16265
  function buildPathUrl(baseUrl, pathSegments, query) {
16226
- const path28 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16227
- const url = new URL(path28, baseUrl);
16266
+ const path27 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16267
+ const url = new URL(path27, baseUrl);
16228
16268
  if (query) {
16229
16269
  for (const [key, value] of Object.entries(query)) {
16230
16270
  url.searchParams.set(key, String(value));
@@ -16337,7 +16377,7 @@ var Namespace = class {
16337
16377
  },
16338
16378
  appendRow: async (data) => {
16339
16379
  const log = {
16340
- hash: createHash4(),
16380
+ hash: createHash3(),
16341
16381
  t: Date.now(),
16342
16382
  kind: LOG_KIND_TABLE,
16343
16383
  topic: descriptorToTopic(descriptor),
@@ -16349,7 +16389,7 @@ var Namespace = class {
16349
16389
  updateRow: async (data) => {
16350
16390
  const { id, ...value } = data;
16351
16391
  const log = {
16352
- hash: createHash4(),
16392
+ hash: createHash3(),
16353
16393
  t: Date.now(),
16354
16394
  kind: LOG_KIND_TABLE,
16355
16395
  topic: descriptorToTopic(descriptor),
@@ -16360,7 +16400,7 @@ var Namespace = class {
16360
16400
  },
16361
16401
  deleteRow: async (id) => {
16362
16402
  const log = {
16363
- hash: createHash4(),
16403
+ hash: createHash3(),
16364
16404
  t: Date.now(),
16365
16405
  kind: LOG_KIND_TABLE,
16366
16406
  topic: descriptorToTopic(descriptor),
@@ -16403,7 +16443,7 @@ var Namespace = class {
16403
16443
  },
16404
16444
  set: async (key, value) => {
16405
16445
  const log = {
16406
- hash: createHash4(),
16446
+ hash: createHash3(),
16407
16447
  t: Date.now(),
16408
16448
  kind: LOG_KIND_MAPPING,
16409
16449
  topic: descriptorToTopic(descriptor),
@@ -16414,7 +16454,7 @@ var Namespace = class {
16414
16454
  },
16415
16455
  delete: async (key) => {
16416
16456
  const log = {
16417
- hash: createHash4(),
16457
+ hash: createHash3(),
16418
16458
  t: Date.now(),
16419
16459
  kind: LOG_KIND_MAPPING,
16420
16460
  topic: descriptorToTopic(descriptor),
@@ -16656,11 +16696,11 @@ var AgentCommandHandler = class {
16656
16696
  }).token;
16657
16697
  }
16658
16698
  readJsonFile(filePath) {
16659
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16660
- if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16699
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16700
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16661
16701
  throw new Error(`JSON file not found: ${absolutePath}`);
16662
16702
  }
16663
- const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16703
+ const raw = (0, import_node_fs12.readFileSync)(absolutePath, "utf8");
16664
16704
  try {
16665
16705
  return JSON.parse(raw);
16666
16706
  } catch (error) {
@@ -16671,7 +16711,7 @@ var AgentCommandHandler = class {
16671
16711
  readJsonObjectFile(filePath) {
16672
16712
  const parsed = this.readJsonFile(filePath);
16673
16713
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16674
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16714
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16675
16715
  throw new Error(`Invalid JSON in ${absolutePath}: expected an object`);
16676
16716
  }
16677
16717
  return parsed;
@@ -16755,8 +16795,8 @@ var AgentCommandHandler = class {
16755
16795
  };
16756
16796
 
16757
16797
  // src/commands/RunnerCommandHandler.ts
16758
- var import_node_fs14 = require("fs");
16759
- var import_node_path19 = __toESM(require("path"));
16798
+ var import_node_fs13 = require("fs");
16799
+ var import_node_path18 = __toESM(require("path"));
16760
16800
  init_sessionStore();
16761
16801
  init_namespacedRef();
16762
16802
  var RunnerCommandHandler = class {
@@ -16851,11 +16891,11 @@ var RunnerCommandHandler = class {
16851
16891
  }).token;
16852
16892
  }
16853
16893
  readJsonFile(filePath) {
16854
- const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
16855
- if (!(0, import_node_fs14.existsSync)(absolutePath)) {
16894
+ const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16895
+ if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16856
16896
  throw new Error(`JSON file not found: ${absolutePath}`);
16857
16897
  }
16858
- const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
16898
+ const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16859
16899
  try {
16860
16900
  return JSON.parse(raw);
16861
16901
  } catch (error) {
@@ -16974,8 +17014,8 @@ var NsCommandHandler = class {
16974
17014
  };
16975
17015
 
16976
17016
  // src/commands/StoreCommandHandler.ts
16977
- var import_node_fs15 = require("fs");
16978
- var import_node_path20 = __toESM(require("path"));
17017
+ var import_node_fs14 = require("fs");
17018
+ var import_node_path19 = __toESM(require("path"));
16979
17019
  var StoreCommandHandler = class {
16980
17020
  async handle(args) {
16981
17021
  if (args.scope === "namespace") {
@@ -17117,11 +17157,11 @@ var StoreCommandHandler = class {
17117
17157
  return payload.data ?? [];
17118
17158
  }
17119
17159
  readJsonFile(filePath) {
17120
- const absolutePath = import_node_path20.default.resolve(process.cwd(), filePath);
17121
- if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17160
+ const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
17161
+ if (!(0, import_node_fs14.existsSync)(absolutePath)) {
17122
17162
  throw new Error(`JSON file not found: ${absolutePath}`);
17123
17163
  }
17124
- const raw = (0, import_node_fs15.readFileSync)(absolutePath, "utf8");
17164
+ const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
17125
17165
  try {
17126
17166
  return JSON.parse(raw);
17127
17167
  } catch (error) {
@@ -17173,13 +17213,13 @@ var StoreCommandHandler = class {
17173
17213
  };
17174
17214
 
17175
17215
  // src/commands/AppCommandHandler.ts
17176
- var import_node_fs17 = require("fs");
17177
- var import_node_path22 = __toESM(require("path"));
17216
+ var import_node_fs16 = require("fs");
17217
+ var import_node_path21 = __toESM(require("path"));
17178
17218
  init_sessionStore();
17179
17219
 
17180
17220
  // src/appIcon.ts
17181
- var import_node_fs16 = require("fs");
17182
- var import_node_path21 = __toESM(require("path"));
17221
+ var import_node_fs15 = require("fs");
17222
+ var import_node_path20 = __toESM(require("path"));
17183
17223
  var import_sharp = __toESM(require("sharp"));
17184
17224
  init_namespacedRef();
17185
17225
  var ANDROID_ICON_SPECS = [
@@ -17212,17 +17252,17 @@ function normalizeAppIcon(icon, cwd) {
17212
17252
  if (/^https?:\/\//i.test(trimmed)) {
17213
17253
  throw new Error("App icon must be SVG source, not a remote URL.");
17214
17254
  }
17215
- const absolutePath = import_node_path21.default.resolve(cwd, trimmed);
17216
- if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17255
+ const absolutePath = import_node_path20.default.resolve(cwd, trimmed);
17256
+ if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17217
17257
  if (trimmed.toLowerCase().endsWith(".svg")) {
17218
17258
  throw new Error(`App icon SVG file not found: ${absolutePath}`);
17219
17259
  }
17220
17260
  throw new Error("App icon must be inline SVG, an SVG data URL, or a .svg file path.");
17221
17261
  }
17222
- if (import_node_path21.default.extname(absolutePath).toLowerCase() !== ".svg") {
17262
+ if (import_node_path20.default.extname(absolutePath).toLowerCase() !== ".svg") {
17223
17263
  throw new Error(`App icon file must use the .svg extension: ${absolutePath}`);
17224
17264
  }
17225
- const svg = (0, import_node_fs16.readFileSync)(absolutePath, "utf8").trim();
17265
+ const svg = (0, import_node_fs15.readFileSync)(absolutePath, "utf8").trim();
17226
17266
  if (!looksLikeSvgMarkup(svg)) {
17227
17267
  throw new Error(`App icon file is not valid SVG: ${absolutePath}`);
17228
17268
  }
@@ -17247,13 +17287,13 @@ function createAppIconExportPlans(input) {
17247
17287
  if (!input.outDir) {
17248
17288
  throw new Error("App icon preset export requires --out-dir.");
17249
17289
  }
17250
- const outDir = import_node_path21.default.resolve(input.cwd, input.outDir);
17251
- (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 });
17252
17292
  return specs.map((spec) => ({
17253
17293
  width: spec.width,
17254
17294
  height: spec.height,
17255
17295
  format: "png",
17256
- outputPath: import_node_path21.default.join(
17296
+ outputPath: import_node_path20.default.join(
17257
17297
  outDir,
17258
17298
  `${namespace}-${objectId}-${spec.name}-${spec.width}x${spec.height}.png`
17259
17299
  )
@@ -17266,8 +17306,8 @@ function createAppIconExportPlans(input) {
17266
17306
  if (!input.output) {
17267
17307
  throw new Error("Single-size app icon export requires --output.");
17268
17308
  }
17269
- const resolvedOutput = import_node_path21.default.resolve(input.cwd, input.output);
17270
- (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 });
17271
17311
  return [
17272
17312
  {
17273
17313
  width: input.width,
@@ -17280,7 +17320,7 @@ function createAppIconExportPlans(input) {
17280
17320
  async function exportAppIcon(svg, plans) {
17281
17321
  for (const plan of plans) {
17282
17322
  const bytes = await renderAppIcon(svg, plan.width, plan.height, plan.format);
17283
- (0, import_node_fs16.writeFileSync)(plan.outputPath, bytes);
17323
+ (0, import_node_fs15.writeFileSync)(plan.outputPath, bytes);
17284
17324
  }
17285
17325
  }
17286
17326
  async function renderAppIcon(svg, width, height, format) {
@@ -17471,10 +17511,10 @@ var AppCommandHandler = class {
17471
17511
  }
17472
17512
  readJsonFile(filePath) {
17473
17513
  const absolutePath = this.resolveFilePath(filePath);
17474
- if (!(0, import_node_fs17.existsSync)(absolutePath)) {
17514
+ if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17475
17515
  throw new Error(`JSON file not found: ${absolutePath}`);
17476
17516
  }
17477
- const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
17517
+ const raw = (0, import_node_fs16.readFileSync)(absolutePath, "utf8");
17478
17518
  try {
17479
17519
  return JSON.parse(raw);
17480
17520
  } catch (error) {
@@ -17484,10 +17524,10 @@ var AppCommandHandler = class {
17484
17524
  }
17485
17525
  preparePayload(filePath) {
17486
17526
  const absolutePath = this.resolveFilePath(filePath);
17487
- return prepareAppPayload(this.readJsonFile(filePath), import_node_path22.default.dirname(absolutePath));
17527
+ return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath));
17488
17528
  }
17489
17529
  resolveFilePath(filePath) {
17490
- return import_node_path22.default.resolve(process.cwd(), filePath);
17530
+ return import_node_path21.default.resolve(process.cwd(), filePath);
17491
17531
  }
17492
17532
  async request(baseUrl, token, method, pathname, body) {
17493
17533
  const response = await fetch(`${baseUrl}${pathname}`, {
@@ -17561,15 +17601,15 @@ function readNamespaceFromPayload(payload) {
17561
17601
  }
17562
17602
 
17563
17603
  // src/commands/ServiceCommandHandler.ts
17564
- var import_node_fs19 = require("fs");
17604
+ var import_node_fs18 = require("fs");
17565
17605
  var import_promises5 = require("fs/promises");
17566
- var import_node_path24 = __toESM(require("path"));
17606
+ var import_node_path23 = __toESM(require("path"));
17567
17607
  var import_promises6 = require("readline/promises");
17568
17608
 
17569
17609
  // src/services/ServiceRuntimeRunner.ts
17570
- var import_node_fs18 = require("fs");
17571
- var import_node_os8 = __toESM(require("os"));
17572
- var import_node_path23 = __toESM(require("path"));
17610
+ var import_node_fs17 = require("fs");
17611
+ var import_node_os7 = __toESM(require("os"));
17612
+ var import_node_path22 = __toESM(require("path"));
17573
17613
  var import_node_child_process10 = require("child_process");
17574
17614
  var import_node_readline = require("readline");
17575
17615
  var import_chalk3 = __toESM(require("chalk"));
@@ -17734,14 +17774,14 @@ var ServiceRuntimeRunner = class {
17734
17774
  }
17735
17775
  resolveRepoRoot(service, cwd) {
17736
17776
  if (cwd) {
17737
- return import_node_path23.default.resolve(process.cwd(), cwd);
17777
+ return import_node_path22.default.resolve(process.cwd(), cwd);
17738
17778
  }
17739
17779
  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();
17740
17780
  if (repository) {
17741
17781
  const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
17742
17782
  if (repoName) {
17743
- const candidate = import_node_path23.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName);
17744
- if ((0, import_node_fs18.existsSync)(candidate)) {
17783
+ const candidate = import_node_path22.default.join(import_node_os7.default.homedir(), "dev", "pmate", repoName);
17784
+ if ((0, import_node_fs17.existsSync)(candidate)) {
17745
17785
  return candidate;
17746
17786
  }
17747
17787
  }
@@ -17754,12 +17794,12 @@ var ServiceRuntimeRunner = class {
17754
17794
  if (!dir) {
17755
17795
  throw new Error(`Missing service dir for ${service.id}`);
17756
17796
  }
17757
- return import_node_path23.default.resolve(repoRoot, dir);
17797
+ return import_node_path22.default.resolve(repoRoot, dir);
17758
17798
  }
17759
17799
  async deployVite(service, options) {
17760
17800
  const appDir = this.resolveLocalDir(service, options.cwd);
17761
- const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17762
- if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17801
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17802
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17763
17803
  throw new Error(`No package.json found at ${packageJsonPath}`);
17764
17804
  }
17765
17805
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -17768,8 +17808,8 @@ var ServiceRuntimeRunner = class {
17768
17808
  throw new Error(`Missing package name in ${packageJsonPath}`);
17769
17809
  }
17770
17810
  await this.runCommand("pnpm", ["--filter", pkgName, "build"], this.resolveRepoRoot(service, options.cwd));
17771
- const distDir = import_node_path23.default.join(appDir, "dist");
17772
- if (!(0, import_node_fs18.existsSync)(distDir)) {
17811
+ const distDir = import_node_path22.default.join(appDir, "dist");
17812
+ if (!(0, import_node_fs17.existsSync)(distDir)) {
17773
17813
  throw new Error(`No dist directory found at ${distDir}`);
17774
17814
  }
17775
17815
  const site = this.getSite(service);
@@ -17781,8 +17821,8 @@ var ServiceRuntimeRunner = class {
17781
17821
  }
17782
17822
  async deployStatic(service, options) {
17783
17823
  const appDir = this.resolveLocalDir(service, options.cwd);
17784
- const srcDir = import_node_path23.default.join(appDir, "src");
17785
- if (!(0, import_node_fs18.existsSync)(srcDir)) {
17824
+ const srcDir = import_node_path22.default.join(appDir, "src");
17825
+ if (!(0, import_node_fs17.existsSync)(srcDir)) {
17786
17826
  throw new Error(`No src directory found at ${srcDir}`);
17787
17827
  }
17788
17828
  const site = this.getSite(service);
@@ -17902,7 +17942,7 @@ var ServiceRuntimeRunner = class {
17902
17942
  const serverAddress = await resolveServerAddress(server);
17903
17943
  const templateName = exposeType === "websocket" ? "nginx.ws.template" : "nginx.rest.template";
17904
17944
  const templatePath = this.resolveTemplatePath(templateName);
17905
- const template = (0, import_node_fs18.readFileSync)(templatePath, "utf8");
17945
+ const template = (0, import_node_fs17.readFileSync)(templatePath, "utf8");
17906
17946
  const host = `${subdomain}.${("domain" in network ? network.domain?.trim() : void 0) || "pmate.chat"}`;
17907
17947
  const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
17908
17948
  const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
@@ -17934,11 +17974,11 @@ var ServiceRuntimeRunner = class {
17934
17974
  }
17935
17975
  if (config.ensurePackageJson) {
17936
17976
  const appDir = this.resolveLocalDir(service, options.cwd);
17937
- const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17938
- if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17977
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17978
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17939
17979
  throw new Error(`Missing package.json at ${packageJsonPath}`);
17940
17980
  }
17941
- const pkgStat = (0, import_node_fs18.statSync)(packageJsonPath);
17981
+ const pkgStat = (0, import_node_fs17.statSync)(packageJsonPath);
17942
17982
  if (!pkgStat.isFile()) {
17943
17983
  throw new Error(`package.json is not a file at ${packageJsonPath}`);
17944
17984
  }
@@ -18362,9 +18402,9 @@ var ServiceRuntimeRunner = class {
18362
18402
  return tokens;
18363
18403
  }
18364
18404
  readPathValue(source, pathExpr) {
18365
- const path28 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18405
+ const path27 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18366
18406
  let cursor = source;
18367
- for (const segment of path28) {
18407
+ for (const segment of path27) {
18368
18408
  if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
18369
18409
  return void 0;
18370
18410
  }
@@ -18407,12 +18447,12 @@ var ServiceRuntimeRunner = class {
18407
18447
  }
18408
18448
  resolveTemplatePath(templateName) {
18409
18449
  const candidates = [
18410
- import_node_path23.default.join(__dirname, "..", "templates", templateName),
18411
- import_node_path23.default.join(__dirname, "..", "src", "templates", templateName),
18412
- import_node_path23.default.join(__dirname, "templates", templateName)
18450
+ import_node_path22.default.join(__dirname, "..", "templates", templateName),
18451
+ import_node_path22.default.join(__dirname, "..", "src", "templates", templateName),
18452
+ import_node_path22.default.join(__dirname, "templates", templateName)
18413
18453
  ];
18414
18454
  for (const candidate of candidates) {
18415
- if ((0, import_node_fs18.existsSync)(candidate)) {
18455
+ if ((0, import_node_fs17.existsSync)(candidate)) {
18416
18456
  return candidate;
18417
18457
  }
18418
18458
  }
@@ -18444,7 +18484,7 @@ var ServiceRuntimeRunner = class {
18444
18484
  });
18445
18485
  }
18446
18486
  loadPackageJson(packageJsonPath) {
18447
- const raw = (0, import_node_fs18.readFileSync)(packageJsonPath, "utf8");
18487
+ const raw = (0, import_node_fs17.readFileSync)(packageJsonPath, "utf8");
18448
18488
  const parsed = JSON.parse(raw);
18449
18489
  if (!parsed || typeof parsed !== "object") {
18450
18490
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -18453,7 +18493,7 @@ var ServiceRuntimeRunner = class {
18453
18493
  }
18454
18494
  writePackageJson(packageJsonPath, data) {
18455
18495
  const serialized = JSON.stringify(data, null, 2);
18456
- (0, import_node_fs18.writeFileSync)(packageJsonPath, `${serialized}
18496
+ (0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
18457
18497
  `, "utf8");
18458
18498
  }
18459
18499
  runCommand(command, args, cwd) {
@@ -18790,7 +18830,7 @@ var ServiceCommandHandler = class {
18790
18830
  format: "dotenv"
18791
18831
  }
18792
18832
  );
18793
- const outputPath = import_node_path24.default.resolve(process.cwd(), args.output);
18833
+ const outputPath = import_node_path23.default.resolve(process.cwd(), args.output);
18794
18834
  await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
18795
18835
  ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
18796
18836
  this.renderJson({ ...payload, output: outputPath }, args.json);
@@ -18819,11 +18859,11 @@ var ServiceCommandHandler = class {
18819
18859
  return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
18820
18860
  }
18821
18861
  readJsonFile(filePath) {
18822
- const absolutePath = import_node_path24.default.resolve(process.cwd(), filePath);
18823
- if (!(0, import_node_fs19.existsSync)(absolutePath)) {
18862
+ const absolutePath = import_node_path23.default.resolve(process.cwd(), filePath);
18863
+ if (!(0, import_node_fs18.existsSync)(absolutePath)) {
18824
18864
  throw new Error(`JSON file not found: ${absolutePath}`);
18825
18865
  }
18826
- const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
18866
+ const raw = (0, import_node_fs18.readFileSync)(absolutePath, "utf8");
18827
18867
  try {
18828
18868
  return JSON.parse(raw);
18829
18869
  } catch (error) {
@@ -19163,8 +19203,8 @@ var RoleCommandHandler = class {
19163
19203
  };
19164
19204
 
19165
19205
  // src/commands/AccountCommandHandler.ts
19166
- var import_node_fs20 = require("fs");
19167
- var import_node_path25 = __toESM(require("path"));
19206
+ var import_node_fs19 = require("fs");
19207
+ var import_node_path24 = __toESM(require("path"));
19168
19208
  init_sessionStore();
19169
19209
  var AccountCommandHandler = class {
19170
19210
  async handle(args) {
@@ -19412,10 +19452,10 @@ var AccountCommandHandler = class {
19412
19452
  throw new Error("Missing visible apps input");
19413
19453
  }
19414
19454
  const absolutePath = this.resolveFilePath(args.filePath);
19415
- if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19455
+ if (!(0, import_node_fs19.existsSync)(absolutePath)) {
19416
19456
  throw new Error(`JSON file not found: ${absolutePath}`);
19417
19457
  }
19418
- const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19458
+ const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
19419
19459
  let parsed;
19420
19460
  try {
19421
19461
  parsed = JSON.parse(raw);
@@ -19429,7 +19469,7 @@ var AccountCommandHandler = class {
19429
19469
  return parsed;
19430
19470
  }
19431
19471
  resolveFilePath(filePath) {
19432
- return import_node_path25.default.resolve(process.cwd(), filePath);
19472
+ return import_node_path24.default.resolve(process.cwd(), filePath);
19433
19473
  }
19434
19474
  };
19435
19475
  function encodePathSegment(value) {
@@ -19437,8 +19477,8 @@ function encodePathSegment(value) {
19437
19477
  }
19438
19478
 
19439
19479
  // src/commands/OrgCommandHandler.ts
19440
- var import_node_fs21 = require("fs");
19441
- var import_node_path26 = __toESM(require("path"));
19480
+ var import_node_fs20 = require("fs");
19481
+ var import_node_path25 = __toESM(require("path"));
19442
19482
  init_sessionStore();
19443
19483
  var OrgCommandHandler = class {
19444
19484
  async handle(args) {
@@ -19609,11 +19649,11 @@ var OrgCommandHandler = class {
19609
19649
  if (!filePath) {
19610
19650
  throw new Error(`Missing ${itemLabel} input`);
19611
19651
  }
19612
- const absolutePath = import_node_path26.default.resolve(process.cwd(), filePath);
19613
- if (!(0, import_node_fs21.existsSync)(absolutePath)) {
19652
+ const absolutePath = import_node_path25.default.resolve(process.cwd(), filePath);
19653
+ if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19614
19654
  throw new Error(`JSON file not found: ${absolutePath}`);
19615
19655
  }
19616
- const raw = (0, import_node_fs21.readFileSync)(absolutePath, "utf8");
19656
+ const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19617
19657
  let parsed;
19618
19658
  try {
19619
19659
  parsed = JSON.parse(raw);
@@ -22098,7 +22138,7 @@ function requireValue11(tokens, option) {
22098
22138
 
22099
22139
  // src/index.ts
22100
22140
  var pkg = JSON.parse(
22101
- (0, import_node_fs22.readFileSync)(import_node_path27.default.resolve(__dirname, "..", "package.json"), "utf8")
22141
+ (0, import_node_fs21.readFileSync)(import_node_path26.default.resolve(__dirname, "..", "package.json"), "utf8")
22102
22142
  );
22103
22143
  var HELP_TEXT = [
22104
22144
  import_chalk4.default.bold(`pmate v${pkg.version}`),