@pmate/cli 0.21.13 → 0.21.14

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 +448 -421
  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}`,
@@ -15665,9 +15665,6 @@ var QuizCommandHandler = class {
15665
15665
  // src/commands/CollectCommandHandler.ts
15666
15666
  var import_playwright = require("playwright");
15667
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
15668
 
15672
15669
  // src/collectStore.ts
15673
15670
  var import_node_crypto3 = require("crypto");
@@ -15687,18 +15684,30 @@ function toThreadKey(name) {
15687
15684
  return `${base}-${suffix}`;
15688
15685
  }
15689
15686
  async function readStoredThread(rootDir, threadKey) {
15690
- const filePath = getThreadFilePath(rootDir, threadKey);
15687
+ const filePath = getThreadMetaFilePath(rootDir, threadKey);
15691
15688
  try {
15692
15689
  const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15693
- return JSON.parse(raw);
15690
+ const parsed = JSON.parse(raw);
15691
+ if ("messages" in parsed && Array.isArray(parsed.messages)) {
15692
+ return parsed;
15693
+ }
15694
+ const messages2 = await readStoredMessages(rootDir, threadKey);
15695
+ return {
15696
+ ...parsed,
15697
+ messages: messages2
15698
+ };
15694
15699
  } catch {
15695
- return null;
15700
+ return readLegacyStoredThread(rootDir, threadKey);
15696
15701
  }
15697
15702
  }
15698
15703
  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");
15704
+ const metaPath = getThreadMetaFilePath(rootDir, thread.threadKey);
15705
+ const messagePath = getThreadMessagesFilePath(rootDir, thread.threadKey);
15706
+ const { messages: messages2, ...meta } = thread;
15707
+ await import_node_fs11.promises.mkdir(import_node_path16.default.dirname(metaPath), { recursive: true });
15708
+ await import_node_fs11.promises.writeFile(metaPath, JSON.stringify(meta, null, 2) + "\n", "utf8");
15709
+ const body = messages2.map((message) => JSON.stringify(message)).join("\n");
15710
+ await import_node_fs11.promises.writeFile(messagePath, `${body}${body ? "\n" : ""}`, "utf8");
15702
15711
  }
15703
15712
  async function writeStoredIndex(rootDir, index) {
15704
15713
  await import_node_fs11.promises.mkdir(rootDir, { recursive: true });
@@ -15713,13 +15722,58 @@ function mergeMessages(existing, incoming) {
15713
15722
  if (seen.has(message.hash)) {
15714
15723
  continue;
15715
15724
  }
15725
+ const upgradeIndex = merged.findIndex((current) => shouldUpgradeMessage(current, message));
15726
+ if (upgradeIndex >= 0) {
15727
+ merged[upgradeIndex] = message;
15728
+ seen.add(message.hash);
15729
+ continue;
15730
+ }
15716
15731
  seen.add(message.hash);
15717
15732
  merged.push(message);
15718
15733
  added += 1;
15719
15734
  }
15720
15735
  return { messages: merged, added };
15721
15736
  }
15722
- function getThreadFilePath(rootDir, threadKey) {
15737
+ function shouldUpgradeMessage(existing, incoming) {
15738
+ if (existing.hash === incoming.hash) {
15739
+ return false;
15740
+ }
15741
+ if (existing.time && incoming.time && existing.time !== incoming.time) {
15742
+ return false;
15743
+ }
15744
+ 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);
15745
+ }
15746
+ function normalizeMessageField(value) {
15747
+ return (value ?? "").trim();
15748
+ }
15749
+ function joinNormalized(values) {
15750
+ return values.map((value) => value.trim()).join("|");
15751
+ }
15752
+ async function readStoredMessages(rootDir, threadKey) {
15753
+ const filePath = getThreadMessagesFilePath(rootDir, threadKey);
15754
+ try {
15755
+ const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15756
+ return raw.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
15757
+ } catch {
15758
+ return [];
15759
+ }
15760
+ }
15761
+ async function readLegacyStoredThread(rootDir, threadKey) {
15762
+ const filePath = getLegacyThreadFilePath(rootDir, threadKey);
15763
+ try {
15764
+ const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
15765
+ return JSON.parse(raw);
15766
+ } catch {
15767
+ return null;
15768
+ }
15769
+ }
15770
+ function getThreadMetaFilePath(rootDir, threadKey) {
15771
+ return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15772
+ }
15773
+ function getThreadMessagesFilePath(rootDir, threadKey) {
15774
+ return import_node_path16.default.join(rootDir, "threads", `${threadKey}.jsonl`);
15775
+ }
15776
+ function getLegacyThreadFilePath(rootDir, threadKey) {
15723
15777
  return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15724
15778
  }
15725
15779
 
@@ -15736,22 +15790,39 @@ var CollectCommandHandler = class {
15736
15790
  if (!targetPage) {
15737
15791
  throw new Error("Cannot find target page from CDP contexts.");
15738
15792
  }
15739
- const { rows } = await this.locateThreadRows(targetPage);
15740
- const threads = await this.extractThreads(rows);
15793
+ const threads = await this.extractThreadsFromConversationData(targetPage);
15741
15794
  if (threads.length === 0) {
15742
- throw new Error("No threads found in target page.");
15795
+ throw new Error("No threads found in CC data store.");
15743
15796
  }
15744
15797
  const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
15745
15798
  const results = [];
15799
+ const failures = [];
15746
15800
  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);
15801
+ try {
15802
+ const threadKey = toThreadKey(thread.name);
15803
+ const existing = await readStoredThread(target.rootDir, threadKey);
15804
+ const messages2 = await this.extractMessages(targetPage, thread, existing?.messages ?? []);
15805
+ const result = await this.persistThread(
15806
+ target.rootDir,
15807
+ target,
15808
+ thread,
15809
+ messages2,
15810
+ syncedAt,
15811
+ existing
15812
+ );
15813
+ results.push(result);
15814
+ } catch (error) {
15815
+ const message = error instanceof Error ? error.message : String(error);
15816
+ failures.push({ threadName: thread.name, message });
15817
+ ui.warn(`Skipped thread: ${thread.name}`, message);
15818
+ }
15819
+ }
15820
+ if (results.length === 0) {
15821
+ const firstFailure = failures[0]?.message ?? "No thread could be collected.";
15822
+ throw new Error(firstFailure);
15752
15823
  }
15753
15824
  await this.writeIndex(target.rootDir, target, results, syncedAt);
15754
- this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results);
15825
+ this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results, failures);
15755
15826
  } catch (error) {
15756
15827
  const message = error instanceof Error ? error.message : String(error);
15757
15828
  throw new Error(`collect failed: ${message}`);
@@ -15759,9 +15830,8 @@ var CollectCommandHandler = class {
15759
15830
  await browser?.close().catch(() => void 0);
15760
15831
  }
15761
15832
  }
15762
- async persistThread(rootDir, target, thread, messages2, syncedAt) {
15833
+ async persistThread(rootDir, target, thread, messages2, syncedAt, existing) {
15763
15834
  const threadKey = toThreadKey(thread.name);
15764
- const existing = await readStoredThread(rootDir, threadKey);
15765
15835
  const incoming = messages2.map((message) => ({
15766
15836
  hash: message.hash,
15767
15837
  time: message.time,
@@ -15811,14 +15881,18 @@ var CollectCommandHandler = class {
15811
15881
  threads
15812
15882
  });
15813
15883
  }
15814
- printSyncSummary(sourceKey, rootDir, scannedThreads, results) {
15884
+ printSyncSummary(sourceKey, rootDir, scannedThreads, results, failures) {
15815
15885
  const updatedThreads = results.filter((result) => result.updated).length;
15816
15886
  const newMessages = results.reduce((sum, result) => sum + result.addedMessages, 0);
15817
15887
  ui.plain(`Synced source: ${sourceKey}`);
15818
15888
  ui.plain(`Threads scanned: ${scannedThreads}`);
15819
15889
  ui.plain(`Threads updated: ${updatedThreads}`);
15890
+ ui.plain(`Threads failed: ${failures.length}`);
15820
15891
  ui.plain(`New messages: ${newMessages}`);
15821
15892
  ui.plain(`Saved under: ${rootDir}`);
15893
+ for (const failure of failures.slice(0, 10)) {
15894
+ ui.warn(`Thread failed: ${failure.threadName}`, failure.message);
15895
+ }
15822
15896
  }
15823
15897
  async pickTargetPage(pages) {
15824
15898
  let best = null;
@@ -15833,269 +15907,101 @@ var CollectCommandHandler = class {
15833
15907
  }
15834
15908
  return best?.page ?? null;
15835
15909
  }
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
- }
15910
+ async extractThreadsFromConversationData(page) {
15911
+ const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch((error) => ({
15912
+ ok: false,
15913
+ reason: error instanceof Error ? error.message : String(error)
15914
+ }));
15915
+ if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.threads)) {
15916
+ throw new Error(
15917
+ result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
15918
+ );
15868
15919
  }
15869
- if (!best) {
15870
- throw new Error("Failed to detect thread list container.");
15920
+ return result.threads.map((thread) => ({
15921
+ conversationId: normalizeText(thread.conversationId ?? ""),
15922
+ name: normalizeText(thread.name ?? ""),
15923
+ unread: normalizeUnreadCount(thread.unread),
15924
+ preview: normalizeText(thread.preview ?? ""),
15925
+ time: formatSentAt(thread.sentAt)
15926
+ })).filter((thread) => thread.conversationId && thread.name);
15927
+ }
15928
+ async extractMessages(page, thread, existingMessages) {
15929
+ const fromDataStore = await this.extractMessagesFromConversationData(
15930
+ page,
15931
+ thread,
15932
+ existingMessages
15933
+ );
15934
+ if ("error" in fromDataStore) {
15935
+ throw new Error(
15936
+ `Failed to load messages from CC data store for thread: ${thread.name} (${fromDataStore.error})`
15937
+ );
15871
15938
  }
15872
- return { rows: best.rows };
15939
+ return fromDataStore.messages;
15873
15940
  }
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;
15941
+ async extractMessagesFromConversationData(page, thread, existingMessages) {
15942
+ const limit = Math.max(existingMessages.length + 40, 100);
15943
+ const result = await this.evaluateInPage(
15944
+ page,
15945
+ buildGetMessagesByConversationExpr(thread.conversationId, limit)
15946
+ ).catch((error) => ({ ok: false, reason: error instanceof Error ? error.message : String(error) }));
15947
+ if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.messages)) {
15948
+ return {
15949
+ error: result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
15950
+ };
15951
+ }
15952
+ const seenHashes = new Set(existingMessages.map((message) => message.hash));
15953
+ const collected = [];
15954
+ for (let i = result.messages.length - 1; i >= 0; i -= 1) {
15955
+ const raw = result.messages[i];
15956
+ const contentParts = [normalizeText(raw.body ?? "")];
15957
+ if (raw.quote?.text) {
15958
+ contentParts.unshift(`Quote: ${normalizeText(raw.quote.text)}`);
15887
15959
  }
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);
15960
+ const attachmentPaths = Array.isArray(raw.attachments) ? raw.attachments.map((attachment) => attachment.path ?? null).filter((value) => !!value) : [];
15907
15961
  const message = {
15908
- index: i,
15962
+ index: raw.index,
15909
15963
  hash: "",
15910
- time: parsed.time,
15911
- sender: parsed.sender,
15912
- content: parsed.content || (images.length > 0 ? "[Image]" : ""),
15913
- links,
15914
- images
15964
+ time: formatSentAt(raw.sentAt),
15965
+ sender: normalizeText(raw.senderName ?? raw.source ?? "") || null,
15966
+ content: contentParts.filter(Boolean).join("\n") || (attachmentPaths.length > 0 ? "[Image]" : ""),
15967
+ links: [],
15968
+ images: attachmentPaths
15915
15969
  };
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) {
15970
+ message.hash = computeMessageHash(thread.name, message);
15971
+ if (!message.content && message.images.length === 0) {
15925
15972
  continue;
15926
15973
  }
15927
- if (fingerprint === lastFingerprint) {
15928
- continue;
15929
- }
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;
15974
+ if (seenHashes.has(message.hash)) {
15975
+ break;
15943
15976
  }
15944
- throw new Error(`Failed to select thread row: ${matched.name}`);
15977
+ collected.push(message);
15945
15978
  }
15979
+ return { messages: finalizeIncrementalMessages(collected, true) };
15946
15980
  }
15947
- async tryClick(row) {
15981
+ async evaluateInPage(page, expression) {
15982
+ const session = await page.context().newCDPSession(page);
15948
15983
  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);
15964
- }
15965
- }
15966
- return out;
15967
- }
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;
15999
- }
16000
- }
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;
16043
- }
16044
- const value = Number.parseInt(line.replace("+", ""), 10);
16045
- if (Number.isFinite(value)) {
16046
- unread = Math.max(unread, value);
16047
- }
16048
- }
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;
15984
+ const response = await session.send("Runtime.evaluate", {
15985
+ expression,
15986
+ returnByValue: true,
15987
+ awaitPromise: true
15988
+ });
15989
+ if (response.exceptionDetails) {
15990
+ throw new Error(response.exceptionDetails.text || "Runtime.evaluate failed");
16067
15991
  }
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;
15992
+ return response.result?.value;
15993
+ } finally {
15994
+ await session.detach().catch(() => void 0);
16079
15995
  }
16080
- return candidates[0] ?? "";
16081
15996
  }
16082
15997
  };
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;
16090
- }
16091
- seen.add(key);
16092
- result.push(thread);
16093
- }
16094
- return result;
16095
- }
16096
15998
  function normalizeText(value) {
16097
15999
  return value.replace(/\s+/g, " ").replace(/\u200B/g, "").trim();
16098
16000
  }
16001
+ function normalizeUnreadCount(value) {
16002
+ const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number.parseInt(value, 10) : 0;
16003
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
16004
+ }
16099
16005
  function computeMessageHash(threadName, message) {
16100
16006
  const base = [
16101
16007
  normalizeText(threadName),
@@ -16107,90 +16013,211 @@ function computeMessageHash(threadName, message) {
16107
16013
  ].join("||");
16108
16014
  return (0, import_node_crypto4.createHash)("sha1").update(base).digest("hex").slice(0, 16);
16109
16015
  }
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 };
16127
- }
16128
- return {
16129
- time,
16130
- sender: normalizeText(senderMatch[1] ?? "") || null,
16131
- content: normalizeText(senderMatch[2] ?? "")
16132
- };
16133
- }
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;
16016
+ function buildListThreadsExpr() {
16017
+ return `(function() {
16018
+ try {
16019
+ var runtime = globalThis;
16020
+ var controller = runtime.ConversationController;
16021
+ if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16022
+ function normalize(value) {
16023
+ return String(value || '').replace(/\\s+/g, ' ').trim();
16024
+ }
16025
+ function pickName(model) {
16026
+ var fields = [
16027
+ model && model.get ? model.get('name') : model && model.name,
16028
+ model && model.get ? model.get('groupDisplayName') : model && model.groupDisplayName,
16029
+ model && model.get ? model.get('title') : model && model.title,
16030
+ model && model.get ? model.get('profileName') : model && model.profileName,
16031
+ model && model.get ? model.get('e164') : model && model.e164,
16032
+ model && model.get ? model.get('id') : model && model.id,
16033
+ ];
16034
+ for (var i = 0; i < fields.length; i++) {
16035
+ var value = normalize(fields[i]);
16036
+ if (value) return value;
16037
+ }
16038
+ return '';
16039
+ }
16040
+ function pickPreview(model) {
16041
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16042
+ var fields = [
16043
+ attrs.draft,
16044
+ attrs.lastMessage,
16045
+ attrs.lastMessageText,
16046
+ attrs.snippet,
16047
+ attrs.body,
16048
+ ];
16049
+ for (var i = 0; i < fields.length; i++) {
16050
+ var value = normalize(fields[i]);
16051
+ if (value) return value;
16052
+ }
16053
+ return '';
16054
+ }
16055
+ function pickUnread(model) {
16056
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16057
+ var candidates = [
16058
+ attrs.unreadCount,
16059
+ attrs.unreadMessagesCount,
16060
+ attrs.unread,
16061
+ model && model.get ? model.get('unreadCount') : null,
16062
+ model && model.get ? model.get('unreadMessagesCount') : null,
16063
+ model && model.get ? model.get('unread') : null,
16064
+ ];
16065
+ for (var i = 0; i < candidates.length; i++) {
16066
+ var value = Number(candidates[i] || 0);
16067
+ if (Number.isFinite(value) && value > 0) return value;
16068
+ }
16069
+ return 0;
16070
+ }
16071
+ function pickSentAt(model) {
16072
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16073
+ var candidates = [
16074
+ attrs.active_at,
16075
+ attrs.timestamp,
16076
+ attrs.sent_at,
16077
+ attrs.lastMessageReceivedAt,
16078
+ model && model.get ? model.get('active_at') : null,
16079
+ model && model.get ? model.get('timestamp') : null,
16080
+ model && model.get ? model.get('sent_at') : null,
16081
+ ];
16082
+ for (var i = 0; i < candidates.length; i++) {
16083
+ var value = Number(candidates[i] || 0);
16084
+ if (Number.isFinite(value) && value > 0) return value;
16085
+ }
16086
+ return null;
16087
+ }
16088
+ var all = [];
16089
+ if (typeof controller.getAllPrivateConversations === 'function') {
16090
+ var privates = controller.getAllPrivateConversations();
16091
+ all.push.apply(all, privates.models || privates || []);
16092
+ }
16093
+ if (typeof controller.getAllGroups === 'function') {
16094
+ var groups = controller.getAllGroups();
16095
+ all.push.apply(all, groups.models || groups || []);
16096
+ }
16097
+ var seen = {};
16098
+ var threads = [];
16099
+ for (var i = 0; i < all.length; i++) {
16100
+ var model = all[i];
16101
+ var conversationId = normalize(model && model.get ? model.get('id') : model && model.id);
16102
+ if (!conversationId || seen[conversationId]) continue;
16103
+ seen[conversationId] = true;
16104
+ var name = pickName(model);
16105
+ if (!name) continue;
16106
+ threads.push({
16107
+ conversationId: conversationId,
16108
+ name: name,
16109
+ unread: pickUnread(model),
16110
+ preview: pickPreview(model),
16111
+ sentAt: pickSentAt(model),
16112
+ });
16113
+ }
16114
+ threads.sort(function(left, right) {
16115
+ return (Number(right.sentAt || 0) - Number(left.sentAt || 0)) || left.name.localeCompare(right.name);
16116
+ });
16117
+ return { ok: true, threads: threads };
16118
+ } catch(e) {
16119
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16146
16120
  }
16147
- const fh = await import_node_fs12.promises.open(src, "r");
16121
+ })()`;
16122
+ }
16123
+ function buildGetMessagesByConversationExpr(conversationId, limit) {
16124
+ const escapedConversationId = escapeForJs(conversationId);
16125
+ return `(async function() {
16148
16126
  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();
16127
+ var runtime = globalThis;
16128
+ var controller = runtime.ConversationController;
16129
+ var signal = (runtime.window && runtime.window.Signal) || runtime.Signal;
16130
+ var whisper = runtime.Whisper;
16131
+ if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16132
+ if (!signal || !signal.Data || !signal.Data.getMessagesByConversation || !whisper || !whisper.MessageCollection) {
16133
+ return { ok: false, reason: 'Signal data APIs are unavailable' };
16134
+ }
16135
+ function normalize(value) {
16136
+ return String(value || '').replace(/\\s+/g, ' ').trim();
16137
+ }
16138
+ function resolveSenderName(source, model) {
16139
+ if (!source) return null;
16140
+ var contact = typeof controller.get === 'function' ? controller.get(source) : null;
16141
+ var fields = [
16142
+ contact && contact.get ? contact.get('name') : contact && contact.name,
16143
+ contact && contact.get ? contact.get('profileName') : contact && contact.profileName,
16144
+ contact && contact.get ? contact.get('title') : contact && contact.title,
16145
+ source,
16146
+ ];
16147
+ for (var i = 0; i < fields.length; i++) {
16148
+ var value = normalize(fields[i]);
16149
+ if (value) return value;
16150
+ }
16151
+ return null;
16152
+ }
16153
+ var conversation = typeof controller.get === 'function' ? controller.get('${escapedConversationId}') : null;
16154
+ if (!conversation) return { ok: false, reason: 'Conversation not found: ${escapedConversationId}' };
16155
+ var fetched = await signal.Data.getMessagesByConversation('${escapedConversationId}', {
16156
+ limit: ${limit},
16157
+ MessageCollection: whisper.MessageCollection,
16158
+ });
16159
+ var models = (fetched.models || fetched || []).slice();
16160
+ models.sort(function(left, right) {
16161
+ var leftSentAt = left && left.get ? (left.get('sent_at') || 0) : (left && left.sent_at) || 0;
16162
+ var rightSentAt = right && right.get ? (right.get('sent_at') || 0) : (right && right.sent_at) || 0;
16163
+ return leftSentAt - rightSentAt;
16164
+ });
16165
+ return {
16166
+ ok: true,
16167
+ conversationId: '${escapedConversationId}',
16168
+ messages: models.map(function(model, index) {
16169
+ var attrs = typeof model.attributes === 'object' ? model.attributes : model;
16170
+ var source = (typeof model.getSource === 'function' ? model.getSource() : attrs.source) || null;
16171
+ var quote = attrs.quote ? {
16172
+ id: attrs.quote.id || null,
16173
+ author: attrs.quote.author || null,
16174
+ text: attrs.quote.text || null,
16175
+ } : null;
16176
+ var attachments = Array.isArray(attrs.attachments) ? attrs.attachments.map(function(attachment) {
16177
+ return {
16178
+ path: attachment && attachment.path || null,
16179
+ contentType: attachment && attachment.contentType || null,
16180
+ fileName: attachment && attachment.fileName || null,
16181
+ };
16182
+ }) : [];
16183
+ return {
16184
+ index: index,
16185
+ body: attrs.body || '',
16186
+ source: source,
16187
+ senderName: resolveSenderName(source, model),
16188
+ sentAt: attrs.sent_at || attrs.timestamp || null,
16189
+ id: attrs.id || model.id || '',
16190
+ attachments: attachments,
16191
+ quote: quote,
16192
+ };
16193
+ }),
16194
+ };
16195
+ } catch(e) {
16196
+ return { ok: false, reason: e && e.message ? e.message : String(e) };
16170
16197
  }
16171
- } catch {
16172
- return src;
16173
- }
16198
+ })()`;
16174
16199
  }
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";
16200
+ function escapeForJs(value) {
16201
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
16202
+ }
16203
+ function formatSentAt(sentAt) {
16204
+ const value = typeof sentAt === "number" ? sentAt : typeof sentAt === "string" && /^\d+$/.test(sentAt) ? Number.parseInt(sentAt, 10) : NaN;
16205
+ if (!Number.isFinite(value) || value <= 0) {
16206
+ return null;
16187
16207
  }
16188
- return null;
16208
+ const date = new Date(value);
16209
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
16210
+ }
16211
+ function finalizeIncrementalMessages(collectedNewestFirst, _reachedKnownMessage) {
16212
+ return [...collectedNewestFirst].reverse();
16213
+ }
16214
+ function pad2(value) {
16215
+ return String(value).padStart(2, "0");
16189
16216
  }
16190
16217
 
16191
16218
  // src/commands/AgentCommandHandler.ts
16192
- var import_node_fs13 = require("fs");
16193
- var import_node_path18 = __toESM(require("path"));
16219
+ var import_node_fs12 = require("fs");
16220
+ var import_node_path17 = __toESM(require("path"));
16194
16221
  init_AgentLlmClient();
16195
16222
  init_sessionStore();
16196
16223
 
@@ -16223,8 +16250,8 @@ function descriptorPathSegments(descriptor) {
16223
16250
  return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
16224
16251
  }
16225
16252
  function buildPathUrl(baseUrl, pathSegments, query) {
16226
- const path28 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16227
- const url = new URL(path28, baseUrl);
16253
+ const path27 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16254
+ const url = new URL(path27, baseUrl);
16228
16255
  if (query) {
16229
16256
  for (const [key, value] of Object.entries(query)) {
16230
16257
  url.searchParams.set(key, String(value));
@@ -16656,11 +16683,11 @@ var AgentCommandHandler = class {
16656
16683
  }).token;
16657
16684
  }
16658
16685
  readJsonFile(filePath) {
16659
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16660
- if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16686
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16687
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16661
16688
  throw new Error(`JSON file not found: ${absolutePath}`);
16662
16689
  }
16663
- const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16690
+ const raw = (0, import_node_fs12.readFileSync)(absolutePath, "utf8");
16664
16691
  try {
16665
16692
  return JSON.parse(raw);
16666
16693
  } catch (error) {
@@ -16671,7 +16698,7 @@ var AgentCommandHandler = class {
16671
16698
  readJsonObjectFile(filePath) {
16672
16699
  const parsed = this.readJsonFile(filePath);
16673
16700
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16674
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16701
+ const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16675
16702
  throw new Error(`Invalid JSON in ${absolutePath}: expected an object`);
16676
16703
  }
16677
16704
  return parsed;
@@ -16755,8 +16782,8 @@ var AgentCommandHandler = class {
16755
16782
  };
16756
16783
 
16757
16784
  // src/commands/RunnerCommandHandler.ts
16758
- var import_node_fs14 = require("fs");
16759
- var import_node_path19 = __toESM(require("path"));
16785
+ var import_node_fs13 = require("fs");
16786
+ var import_node_path18 = __toESM(require("path"));
16760
16787
  init_sessionStore();
16761
16788
  init_namespacedRef();
16762
16789
  var RunnerCommandHandler = class {
@@ -16851,11 +16878,11 @@ var RunnerCommandHandler = class {
16851
16878
  }).token;
16852
16879
  }
16853
16880
  readJsonFile(filePath) {
16854
- const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
16855
- if (!(0, import_node_fs14.existsSync)(absolutePath)) {
16881
+ const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16882
+ if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16856
16883
  throw new Error(`JSON file not found: ${absolutePath}`);
16857
16884
  }
16858
- const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
16885
+ const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16859
16886
  try {
16860
16887
  return JSON.parse(raw);
16861
16888
  } catch (error) {
@@ -16974,8 +17001,8 @@ var NsCommandHandler = class {
16974
17001
  };
16975
17002
 
16976
17003
  // src/commands/StoreCommandHandler.ts
16977
- var import_node_fs15 = require("fs");
16978
- var import_node_path20 = __toESM(require("path"));
17004
+ var import_node_fs14 = require("fs");
17005
+ var import_node_path19 = __toESM(require("path"));
16979
17006
  var StoreCommandHandler = class {
16980
17007
  async handle(args) {
16981
17008
  if (args.scope === "namespace") {
@@ -17117,11 +17144,11 @@ var StoreCommandHandler = class {
17117
17144
  return payload.data ?? [];
17118
17145
  }
17119
17146
  readJsonFile(filePath) {
17120
- const absolutePath = import_node_path20.default.resolve(process.cwd(), filePath);
17121
- if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17147
+ const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
17148
+ if (!(0, import_node_fs14.existsSync)(absolutePath)) {
17122
17149
  throw new Error(`JSON file not found: ${absolutePath}`);
17123
17150
  }
17124
- const raw = (0, import_node_fs15.readFileSync)(absolutePath, "utf8");
17151
+ const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
17125
17152
  try {
17126
17153
  return JSON.parse(raw);
17127
17154
  } catch (error) {
@@ -17173,13 +17200,13 @@ var StoreCommandHandler = class {
17173
17200
  };
17174
17201
 
17175
17202
  // src/commands/AppCommandHandler.ts
17176
- var import_node_fs17 = require("fs");
17177
- var import_node_path22 = __toESM(require("path"));
17203
+ var import_node_fs16 = require("fs");
17204
+ var import_node_path21 = __toESM(require("path"));
17178
17205
  init_sessionStore();
17179
17206
 
17180
17207
  // src/appIcon.ts
17181
- var import_node_fs16 = require("fs");
17182
- var import_node_path21 = __toESM(require("path"));
17208
+ var import_node_fs15 = require("fs");
17209
+ var import_node_path20 = __toESM(require("path"));
17183
17210
  var import_sharp = __toESM(require("sharp"));
17184
17211
  init_namespacedRef();
17185
17212
  var ANDROID_ICON_SPECS = [
@@ -17212,17 +17239,17 @@ function normalizeAppIcon(icon, cwd) {
17212
17239
  if (/^https?:\/\//i.test(trimmed)) {
17213
17240
  throw new Error("App icon must be SVG source, not a remote URL.");
17214
17241
  }
17215
- const absolutePath = import_node_path21.default.resolve(cwd, trimmed);
17216
- if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17242
+ const absolutePath = import_node_path20.default.resolve(cwd, trimmed);
17243
+ if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17217
17244
  if (trimmed.toLowerCase().endsWith(".svg")) {
17218
17245
  throw new Error(`App icon SVG file not found: ${absolutePath}`);
17219
17246
  }
17220
17247
  throw new Error("App icon must be inline SVG, an SVG data URL, or a .svg file path.");
17221
17248
  }
17222
- if (import_node_path21.default.extname(absolutePath).toLowerCase() !== ".svg") {
17249
+ if (import_node_path20.default.extname(absolutePath).toLowerCase() !== ".svg") {
17223
17250
  throw new Error(`App icon file must use the .svg extension: ${absolutePath}`);
17224
17251
  }
17225
- const svg = (0, import_node_fs16.readFileSync)(absolutePath, "utf8").trim();
17252
+ const svg = (0, import_node_fs15.readFileSync)(absolutePath, "utf8").trim();
17226
17253
  if (!looksLikeSvgMarkup(svg)) {
17227
17254
  throw new Error(`App icon file is not valid SVG: ${absolutePath}`);
17228
17255
  }
@@ -17247,13 +17274,13 @@ function createAppIconExportPlans(input) {
17247
17274
  if (!input.outDir) {
17248
17275
  throw new Error("App icon preset export requires --out-dir.");
17249
17276
  }
17250
- const outDir = import_node_path21.default.resolve(input.cwd, input.outDir);
17251
- (0, import_node_fs16.mkdirSync)(outDir, { recursive: true });
17277
+ const outDir = import_node_path20.default.resolve(input.cwd, input.outDir);
17278
+ (0, import_node_fs15.mkdirSync)(outDir, { recursive: true });
17252
17279
  return specs.map((spec) => ({
17253
17280
  width: spec.width,
17254
17281
  height: spec.height,
17255
17282
  format: "png",
17256
- outputPath: import_node_path21.default.join(
17283
+ outputPath: import_node_path20.default.join(
17257
17284
  outDir,
17258
17285
  `${namespace}-${objectId}-${spec.name}-${spec.width}x${spec.height}.png`
17259
17286
  )
@@ -17266,8 +17293,8 @@ function createAppIconExportPlans(input) {
17266
17293
  if (!input.output) {
17267
17294
  throw new Error("Single-size app icon export requires --output.");
17268
17295
  }
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 });
17296
+ const resolvedOutput = import_node_path20.default.resolve(input.cwd, input.output);
17297
+ (0, import_node_fs15.mkdirSync)(import_node_path20.default.dirname(resolvedOutput), { recursive: true });
17271
17298
  return [
17272
17299
  {
17273
17300
  width: input.width,
@@ -17280,7 +17307,7 @@ function createAppIconExportPlans(input) {
17280
17307
  async function exportAppIcon(svg, plans) {
17281
17308
  for (const plan of plans) {
17282
17309
  const bytes = await renderAppIcon(svg, plan.width, plan.height, plan.format);
17283
- (0, import_node_fs16.writeFileSync)(plan.outputPath, bytes);
17310
+ (0, import_node_fs15.writeFileSync)(plan.outputPath, bytes);
17284
17311
  }
17285
17312
  }
17286
17313
  async function renderAppIcon(svg, width, height, format) {
@@ -17471,10 +17498,10 @@ var AppCommandHandler = class {
17471
17498
  }
17472
17499
  readJsonFile(filePath) {
17473
17500
  const absolutePath = this.resolveFilePath(filePath);
17474
- if (!(0, import_node_fs17.existsSync)(absolutePath)) {
17501
+ if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17475
17502
  throw new Error(`JSON file not found: ${absolutePath}`);
17476
17503
  }
17477
- const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
17504
+ const raw = (0, import_node_fs16.readFileSync)(absolutePath, "utf8");
17478
17505
  try {
17479
17506
  return JSON.parse(raw);
17480
17507
  } catch (error) {
@@ -17484,10 +17511,10 @@ var AppCommandHandler = class {
17484
17511
  }
17485
17512
  preparePayload(filePath) {
17486
17513
  const absolutePath = this.resolveFilePath(filePath);
17487
- return prepareAppPayload(this.readJsonFile(filePath), import_node_path22.default.dirname(absolutePath));
17514
+ return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath));
17488
17515
  }
17489
17516
  resolveFilePath(filePath) {
17490
- return import_node_path22.default.resolve(process.cwd(), filePath);
17517
+ return import_node_path21.default.resolve(process.cwd(), filePath);
17491
17518
  }
17492
17519
  async request(baseUrl, token, method, pathname, body) {
17493
17520
  const response = await fetch(`${baseUrl}${pathname}`, {
@@ -17561,15 +17588,15 @@ function readNamespaceFromPayload(payload) {
17561
17588
  }
17562
17589
 
17563
17590
  // src/commands/ServiceCommandHandler.ts
17564
- var import_node_fs19 = require("fs");
17591
+ var import_node_fs18 = require("fs");
17565
17592
  var import_promises5 = require("fs/promises");
17566
- var import_node_path24 = __toESM(require("path"));
17593
+ var import_node_path23 = __toESM(require("path"));
17567
17594
  var import_promises6 = require("readline/promises");
17568
17595
 
17569
17596
  // 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"));
17597
+ var import_node_fs17 = require("fs");
17598
+ var import_node_os7 = __toESM(require("os"));
17599
+ var import_node_path22 = __toESM(require("path"));
17573
17600
  var import_node_child_process10 = require("child_process");
17574
17601
  var import_node_readline = require("readline");
17575
17602
  var import_chalk3 = __toESM(require("chalk"));
@@ -17734,14 +17761,14 @@ var ServiceRuntimeRunner = class {
17734
17761
  }
17735
17762
  resolveRepoRoot(service, cwd) {
17736
17763
  if (cwd) {
17737
- return import_node_path23.default.resolve(process.cwd(), cwd);
17764
+ return import_node_path22.default.resolve(process.cwd(), cwd);
17738
17765
  }
17739
17766
  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
17767
  if (repository) {
17741
17768
  const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
17742
17769
  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)) {
17770
+ const candidate = import_node_path22.default.join(import_node_os7.default.homedir(), "dev", "pmate", repoName);
17771
+ if ((0, import_node_fs17.existsSync)(candidate)) {
17745
17772
  return candidate;
17746
17773
  }
17747
17774
  }
@@ -17754,12 +17781,12 @@ var ServiceRuntimeRunner = class {
17754
17781
  if (!dir) {
17755
17782
  throw new Error(`Missing service dir for ${service.id}`);
17756
17783
  }
17757
- return import_node_path23.default.resolve(repoRoot, dir);
17784
+ return import_node_path22.default.resolve(repoRoot, dir);
17758
17785
  }
17759
17786
  async deployVite(service, options) {
17760
17787
  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)) {
17788
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17789
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17763
17790
  throw new Error(`No package.json found at ${packageJsonPath}`);
17764
17791
  }
17765
17792
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -17768,8 +17795,8 @@ var ServiceRuntimeRunner = class {
17768
17795
  throw new Error(`Missing package name in ${packageJsonPath}`);
17769
17796
  }
17770
17797
  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)) {
17798
+ const distDir = import_node_path22.default.join(appDir, "dist");
17799
+ if (!(0, import_node_fs17.existsSync)(distDir)) {
17773
17800
  throw new Error(`No dist directory found at ${distDir}`);
17774
17801
  }
17775
17802
  const site = this.getSite(service);
@@ -17781,8 +17808,8 @@ var ServiceRuntimeRunner = class {
17781
17808
  }
17782
17809
  async deployStatic(service, options) {
17783
17810
  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)) {
17811
+ const srcDir = import_node_path22.default.join(appDir, "src");
17812
+ if (!(0, import_node_fs17.existsSync)(srcDir)) {
17786
17813
  throw new Error(`No src directory found at ${srcDir}`);
17787
17814
  }
17788
17815
  const site = this.getSite(service);
@@ -17902,7 +17929,7 @@ var ServiceRuntimeRunner = class {
17902
17929
  const serverAddress = await resolveServerAddress(server);
17903
17930
  const templateName = exposeType === "websocket" ? "nginx.ws.template" : "nginx.rest.template";
17904
17931
  const templatePath = this.resolveTemplatePath(templateName);
17905
- const template = (0, import_node_fs18.readFileSync)(templatePath, "utf8");
17932
+ const template = (0, import_node_fs17.readFileSync)(templatePath, "utf8");
17906
17933
  const host = `${subdomain}.${("domain" in network ? network.domain?.trim() : void 0) || "pmate.chat"}`;
17907
17934
  const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
17908
17935
  const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
@@ -17934,11 +17961,11 @@ var ServiceRuntimeRunner = class {
17934
17961
  }
17935
17962
  if (config.ensurePackageJson) {
17936
17963
  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)) {
17964
+ const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17965
+ if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17939
17966
  throw new Error(`Missing package.json at ${packageJsonPath}`);
17940
17967
  }
17941
- const pkgStat = (0, import_node_fs18.statSync)(packageJsonPath);
17968
+ const pkgStat = (0, import_node_fs17.statSync)(packageJsonPath);
17942
17969
  if (!pkgStat.isFile()) {
17943
17970
  throw new Error(`package.json is not a file at ${packageJsonPath}`);
17944
17971
  }
@@ -18362,9 +18389,9 @@ var ServiceRuntimeRunner = class {
18362
18389
  return tokens;
18363
18390
  }
18364
18391
  readPathValue(source, pathExpr) {
18365
- const path28 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18392
+ const path27 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18366
18393
  let cursor = source;
18367
- for (const segment of path28) {
18394
+ for (const segment of path27) {
18368
18395
  if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
18369
18396
  return void 0;
18370
18397
  }
@@ -18407,12 +18434,12 @@ var ServiceRuntimeRunner = class {
18407
18434
  }
18408
18435
  resolveTemplatePath(templateName) {
18409
18436
  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)
18437
+ import_node_path22.default.join(__dirname, "..", "templates", templateName),
18438
+ import_node_path22.default.join(__dirname, "..", "src", "templates", templateName),
18439
+ import_node_path22.default.join(__dirname, "templates", templateName)
18413
18440
  ];
18414
18441
  for (const candidate of candidates) {
18415
- if ((0, import_node_fs18.existsSync)(candidate)) {
18442
+ if ((0, import_node_fs17.existsSync)(candidate)) {
18416
18443
  return candidate;
18417
18444
  }
18418
18445
  }
@@ -18444,7 +18471,7 @@ var ServiceRuntimeRunner = class {
18444
18471
  });
18445
18472
  }
18446
18473
  loadPackageJson(packageJsonPath) {
18447
- const raw = (0, import_node_fs18.readFileSync)(packageJsonPath, "utf8");
18474
+ const raw = (0, import_node_fs17.readFileSync)(packageJsonPath, "utf8");
18448
18475
  const parsed = JSON.parse(raw);
18449
18476
  if (!parsed || typeof parsed !== "object") {
18450
18477
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -18453,7 +18480,7 @@ var ServiceRuntimeRunner = class {
18453
18480
  }
18454
18481
  writePackageJson(packageJsonPath, data) {
18455
18482
  const serialized = JSON.stringify(data, null, 2);
18456
- (0, import_node_fs18.writeFileSync)(packageJsonPath, `${serialized}
18483
+ (0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
18457
18484
  `, "utf8");
18458
18485
  }
18459
18486
  runCommand(command, args, cwd) {
@@ -18790,7 +18817,7 @@ var ServiceCommandHandler = class {
18790
18817
  format: "dotenv"
18791
18818
  }
18792
18819
  );
18793
- const outputPath = import_node_path24.default.resolve(process.cwd(), args.output);
18820
+ const outputPath = import_node_path23.default.resolve(process.cwd(), args.output);
18794
18821
  await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
18795
18822
  ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
18796
18823
  this.renderJson({ ...payload, output: outputPath }, args.json);
@@ -18819,11 +18846,11 @@ var ServiceCommandHandler = class {
18819
18846
  return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
18820
18847
  }
18821
18848
  readJsonFile(filePath) {
18822
- const absolutePath = import_node_path24.default.resolve(process.cwd(), filePath);
18823
- if (!(0, import_node_fs19.existsSync)(absolutePath)) {
18849
+ const absolutePath = import_node_path23.default.resolve(process.cwd(), filePath);
18850
+ if (!(0, import_node_fs18.existsSync)(absolutePath)) {
18824
18851
  throw new Error(`JSON file not found: ${absolutePath}`);
18825
18852
  }
18826
- const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
18853
+ const raw = (0, import_node_fs18.readFileSync)(absolutePath, "utf8");
18827
18854
  try {
18828
18855
  return JSON.parse(raw);
18829
18856
  } catch (error) {
@@ -19163,8 +19190,8 @@ var RoleCommandHandler = class {
19163
19190
  };
19164
19191
 
19165
19192
  // src/commands/AccountCommandHandler.ts
19166
- var import_node_fs20 = require("fs");
19167
- var import_node_path25 = __toESM(require("path"));
19193
+ var import_node_fs19 = require("fs");
19194
+ var import_node_path24 = __toESM(require("path"));
19168
19195
  init_sessionStore();
19169
19196
  var AccountCommandHandler = class {
19170
19197
  async handle(args) {
@@ -19412,10 +19439,10 @@ var AccountCommandHandler = class {
19412
19439
  throw new Error("Missing visible apps input");
19413
19440
  }
19414
19441
  const absolutePath = this.resolveFilePath(args.filePath);
19415
- if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19442
+ if (!(0, import_node_fs19.existsSync)(absolutePath)) {
19416
19443
  throw new Error(`JSON file not found: ${absolutePath}`);
19417
19444
  }
19418
- const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19445
+ const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
19419
19446
  let parsed;
19420
19447
  try {
19421
19448
  parsed = JSON.parse(raw);
@@ -19429,7 +19456,7 @@ var AccountCommandHandler = class {
19429
19456
  return parsed;
19430
19457
  }
19431
19458
  resolveFilePath(filePath) {
19432
- return import_node_path25.default.resolve(process.cwd(), filePath);
19459
+ return import_node_path24.default.resolve(process.cwd(), filePath);
19433
19460
  }
19434
19461
  };
19435
19462
  function encodePathSegment(value) {
@@ -19437,8 +19464,8 @@ function encodePathSegment(value) {
19437
19464
  }
19438
19465
 
19439
19466
  // src/commands/OrgCommandHandler.ts
19440
- var import_node_fs21 = require("fs");
19441
- var import_node_path26 = __toESM(require("path"));
19467
+ var import_node_fs20 = require("fs");
19468
+ var import_node_path25 = __toESM(require("path"));
19442
19469
  init_sessionStore();
19443
19470
  var OrgCommandHandler = class {
19444
19471
  async handle(args) {
@@ -19609,11 +19636,11 @@ var OrgCommandHandler = class {
19609
19636
  if (!filePath) {
19610
19637
  throw new Error(`Missing ${itemLabel} input`);
19611
19638
  }
19612
- const absolutePath = import_node_path26.default.resolve(process.cwd(), filePath);
19613
- if (!(0, import_node_fs21.existsSync)(absolutePath)) {
19639
+ const absolutePath = import_node_path25.default.resolve(process.cwd(), filePath);
19640
+ if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19614
19641
  throw new Error(`JSON file not found: ${absolutePath}`);
19615
19642
  }
19616
- const raw = (0, import_node_fs21.readFileSync)(absolutePath, "utf8");
19643
+ const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19617
19644
  let parsed;
19618
19645
  try {
19619
19646
  parsed = JSON.parse(raw);
@@ -22098,7 +22125,7 @@ function requireValue11(tokens, option) {
22098
22125
 
22099
22126
  // src/index.ts
22100
22127
  var pkg = JSON.parse(
22101
- (0, import_node_fs22.readFileSync)(import_node_path27.default.resolve(__dirname, "..", "package.json"), "utf8")
22128
+ (0, import_node_fs21.readFileSync)(import_node_path26.default.resolve(__dirname, "..", "package.json"), "utf8")
22102
22129
  );
22103
22130
  var HELP_TEXT = [
22104
22131
  import_chalk4.default.bold(`pmate v${pkg.version}`),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/cli",
3
- "version": "0.21.13",
3
+ "version": "0.21.14",
4
4
  "description": "CLI utilities for the pMate project",
5
5
  "license": "MIT",
6
6
  "bin": {