@pmate/cli 0.21.16 → 0.21.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9435,8 +9435,8 @@ __export(index_exports, {
9435
9435
  parseCommandArgs: () => parseCommandArgs
9436
9436
  });
9437
9437
  module.exports = __toCommonJS(index_exports);
9438
- var import_node_fs21 = require("fs");
9439
- var import_node_path26 = __toESM(require("path"));
9438
+ var import_node_fs22 = require("fs");
9439
+ var import_node_path27 = __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(path27, init) {
14588
+ static async requestJson(path28, init) {
14589
14589
  const token = this.getToken();
14590
- const response = await fetch(`${this.baseUrl}${path27}`, {
14590
+ const response = await fetch(`${this.baseUrl}${path28}`, {
14591
14591
  method: init.method,
14592
14592
  headers: {
14593
14593
  Authorization: `Bearer ${token}`,
@@ -15664,6 +15664,10 @@ 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"));
15667
15671
 
15668
15672
  // src/collectStore.ts
15669
15673
  var import_node_crypto3 = require("crypto");
@@ -15683,30 +15687,18 @@ function toThreadKey(name) {
15683
15687
  return `${base}-${suffix}`;
15684
15688
  }
15685
15689
  async function readStoredThread(rootDir, threadKey) {
15686
- const filePath = getThreadMetaFilePath(rootDir, threadKey);
15690
+ const filePath = getThreadFilePath(rootDir, threadKey);
15687
15691
  try {
15688
15692
  const raw = await import_node_fs11.promises.readFile(filePath, "utf8");
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
- };
15693
+ return JSON.parse(raw);
15698
15694
  } catch {
15699
- return readLegacyStoredThread(rootDir, threadKey);
15695
+ return null;
15700
15696
  }
15701
15697
  }
15702
15698
  async function writeStoredThread(rootDir, thread) {
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");
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");
15710
15702
  }
15711
15703
  async function writeStoredIndex(rootDir, index) {
15712
15704
  await import_node_fs11.promises.mkdir(rootDir, { recursive: true });
@@ -15714,91 +15706,22 @@ async function writeStoredIndex(rootDir, index) {
15714
15706
  await import_node_fs11.promises.writeFile(filePath, JSON.stringify(index, null, 2) + "\n", "utf8");
15715
15707
  }
15716
15708
  function mergeMessages(existing, incoming) {
15717
- const seenMessageIds = new Set(
15718
- existing.map((message) => normalizeMessageId(message.messageId)).filter((value) => !!value)
15719
- );
15709
+ const seen = new Set(existing.map((message) => message.hash));
15720
15710
  const merged = [...existing];
15721
15711
  let added = 0;
15722
15712
  for (const message of incoming) {
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
- }
15713
+ if (seen.has(message.hash)) {
15733
15714
  continue;
15734
15715
  }
15735
- if (incomingMessageId) {
15736
- seenMessageIds.add(incomingMessageId);
15737
- }
15716
+ seen.add(message.hash);
15738
15717
  merged.push(message);
15739
15718
  added += 1;
15740
15719
  }
15741
15720
  return { messages: merged, added };
15742
15721
  }
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) {
15783
- return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15784
- }
15785
- function getThreadMessagesFilePath(rootDir, threadKey) {
15786
- return import_node_path16.default.join(rootDir, "threads", `${threadKey}.jsonl`);
15787
- }
15788
- function getLegacyThreadFilePath(rootDir, threadKey) {
15722
+ function getThreadFilePath(rootDir, threadKey) {
15789
15723
  return import_node_path16.default.join(rootDir, "threads", `${threadKey}.json`);
15790
15724
  }
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
- }
15802
15725
 
15803
15726
  // src/commands/CollectCommandHandler.ts
15804
15727
  var CollectCommandHandler = class {
@@ -15813,39 +15736,22 @@ var CollectCommandHandler = class {
15813
15736
  if (!targetPage) {
15814
15737
  throw new Error("Cannot find target page from CDP contexts.");
15815
15738
  }
15816
- const threads = await this.extractThreadsFromConversationData(targetPage);
15739
+ const { rows } = await this.locateThreadRows(targetPage);
15740
+ const threads = await this.extractThreads(rows);
15817
15741
  if (threads.length === 0) {
15818
- throw new Error("No threads found in CC data store.");
15742
+ throw new Error("No threads found in target page.");
15819
15743
  }
15820
15744
  const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
15821
15745
  const results = [];
15822
- const failures = [];
15823
15746
  for (const thread of threads) {
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);
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);
15846
15752
  }
15847
15753
  await this.writeIndex(target.rootDir, target, results, syncedAt);
15848
- this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results, failures);
15754
+ this.printSyncSummary(target.sourceKey, target.rootDir, threads.length, results);
15849
15755
  } catch (error) {
15850
15756
  const message = error instanceof Error ? error.message : String(error);
15851
15757
  throw new Error(`collect failed: ${message}`);
@@ -15853,10 +15759,11 @@ var CollectCommandHandler = class {
15853
15759
  await browser?.close().catch(() => void 0);
15854
15760
  }
15855
15761
  }
15856
- async persistThread(rootDir, target, thread, messages2, syncedAt, existing) {
15762
+ async persistThread(rootDir, target, thread, messages2, syncedAt) {
15857
15763
  const threadKey = toThreadKey(thread.name);
15764
+ const existing = await readStoredThread(rootDir, threadKey);
15858
15765
  const incoming = messages2.map((message) => ({
15859
- messageId: message.messageId,
15766
+ hash: message.hash,
15860
15767
  time: message.time,
15861
15768
  sender: message.sender,
15862
15769
  content: message.content,
@@ -15904,18 +15811,14 @@ var CollectCommandHandler = class {
15904
15811
  threads
15905
15812
  });
15906
15813
  }
15907
- printSyncSummary(sourceKey, rootDir, scannedThreads, results, failures) {
15814
+ printSyncSummary(sourceKey, rootDir, scannedThreads, results) {
15908
15815
  const updatedThreads = results.filter((result) => result.updated).length;
15909
15816
  const newMessages = results.reduce((sum, result) => sum + result.addedMessages, 0);
15910
15817
  ui.plain(`Synced source: ${sourceKey}`);
15911
15818
  ui.plain(`Threads scanned: ${scannedThreads}`);
15912
15819
  ui.plain(`Threads updated: ${updatedThreads}`);
15913
- ui.plain(`Threads failed: ${failures.length}`);
15914
15820
  ui.plain(`New messages: ${newMessages}`);
15915
15821
  ui.plain(`Saved under: ${rootDir}`);
15916
- for (const failure of failures.slice(0, 10)) {
15917
- ui.warn(`Thread failed: ${failure.threadName}`, failure.message);
15918
- }
15919
15822
  }
15920
15823
  async pickTargetPage(pages) {
15921
15824
  let best = null;
@@ -15930,321 +15833,364 @@ var CollectCommandHandler = class {
15930
15833
  }
15931
15834
  return best?.page ?? null;
15932
15835
  }
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
- );
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
+ }
15942
15868
  }
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
- );
15869
+ if (!best) {
15870
+ throw new Error("Failed to detect thread list container.");
15961
15871
  }
15962
- return fromDataStore.messages;
15872
+ return { rows: best.rows };
15963
15873
  }
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) : [];
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);
15986
15907
  const message = {
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
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
15994
15915
  };
15995
- if (!message.content && message.images.length === 0) {
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) {
15996
15925
  continue;
15997
15926
  }
15998
- if (message.messageId && seenMessageIds.has(message.messageId)) {
15999
- break;
15927
+ if (fingerprint === lastFingerprint) {
15928
+ continue;
16000
15929
  }
16001
- collected.push(message);
15930
+ lastFingerprint = fingerprint;
15931
+ messages2.push(message);
16002
15932
  }
16003
- return { messages: finalizeIncrementalMessages(collected, true) };
15933
+ return messages2;
16004
15934
  }
16005
- async evaluateInPage(page, expression) {
16006
- const session = await page.context().newCDPSession(page);
16007
- try {
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");
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;
16015
15943
  }
16016
- return response.result?.value;
16017
- } finally {
16018
- await session.detach().catch(() => void 0);
15944
+ throw new Error(`Failed to select thread row: ${matched.name}`);
16019
15945
  }
16020
15946
  }
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() {
15947
+ async tryClick(row) {
16031
15948
  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;
16081
- }
16082
- return 0;
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);
16083
15964
  }
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;
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;
16098
15994
  }
16099
- return null;
16100
- }
16101
- function isArchived(model) {
16102
- var attrs = typeof model.attributes === 'object' ? model.attributes : model || {};
16103
- var candidates = [
16104
- attrs.isArchived,
16105
- attrs.archived,
16106
- model && model.get ? model.get('isArchived') : null,
16107
- model && model.get ? model.get('archived') : null,
16108
- ];
16109
- for (var i = 0; i < candidates.length; i++) {
16110
- if (candidates[i] === true) return true;
15995
+ const rows = await el.locator(":scope > *").count().catch(() => 0);
15996
+ if (rows >= bestRows) {
15997
+ bestRows = rows;
15998
+ bestIdx = i;
16111
15999
  }
16112
- return false;
16113
16000
  }
16114
- var all = [];
16115
- if (typeof controller.getAllPrivateConversations === 'function') {
16116
- var privates = controller.getAllPrivateConversations();
16117
- all.push.apply(all, privates.models || privates || []);
16118
- }
16119
- if (typeof controller.getAllGroups === 'function') {
16120
- var groups = controller.getAllGroups();
16121
- all.push.apply(all, groups.models || groups || []);
16122
- }
16123
- var seen = {};
16124
- var threads = [];
16125
- for (var i = 0; i < all.length; i++) {
16126
- var model = all[i];
16127
- var conversationId = normalize(model && model.get ? model.get('id') : model && model.id);
16128
- if (!conversationId || seen[conversationId]) continue;
16129
- seen[conversationId] = true;
16130
- if (isArchived(model)) continue;
16131
- var name = pickName(model);
16132
- if (!name) continue;
16133
- threads.push({
16134
- conversationId: conversationId,
16135
- name: name,
16136
- unread: pickUnread(model),
16137
- preview: pickPreview(model),
16138
- sentAt: pickSentAt(model),
16139
- });
16001
+ if (bestRows >= 0) {
16002
+ return strong.nth(bestIdx);
16140
16003
  }
16141
- threads.sort(function(left, right) {
16142
- return (Number(right.sentAt || 0) - Number(left.sentAt || 0)) || left.name.localeCompare(right.name);
16143
- });
16144
- return { ok: true, threads: threads };
16145
- } catch(e) {
16146
- return { ok: false, reason: e && e.message ? e.message : String(e) };
16147
16004
  }
16148
- })()`;
16149
- }
16150
- function buildGetMessagesByConversationExpr(conversationId, limit) {
16151
- const escapedConversationId = escapeForJs(conversationId);
16152
- return `(async function() {
16153
- try {
16154
- var runtime = globalThis;
16155
- var controller = runtime.ConversationController;
16156
- var signal = (runtime.window && runtime.window.Signal) || runtime.Signal;
16157
- var whisper = runtime.Whisper;
16158
- if (!controller) return { ok: false, reason: 'ConversationController unavailable' };
16159
- if (!signal || !signal.Data || !signal.Data.getMessagesByConversation || !whisper || !whisper.MessageCollection) {
16160
- return { ok: false, reason: 'Signal data APIs are unavailable' };
16161
- }
16162
- function normalize(value) {
16163
- return String(value || '').replace(/\\s+/g, ' ').trim();
16164
- }
16165
- function resolveSenderName(source, model) {
16166
- if (!source) return null;
16167
- var contact = typeof controller.get === 'function' ? controller.get(source) : null;
16168
- var fields = [
16169
- contact && contact.get ? contact.get('name') : contact && contact.name,
16170
- contact && contact.get ? contact.get('profileName') : contact && contact.profileName,
16171
- contact && contact.get ? contact.get('title') : contact && contact.title,
16172
- source,
16173
- ];
16174
- for (var i = 0; i < fields.length; i++) {
16175
- var value = normalize(fields[i]);
16176
- if (value) return value;
16177
- }
16178
- return null;
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);
16179
16047
  }
16180
- var conversation = typeof controller.get === 'function' ? controller.get('${escapedConversationId}') : null;
16181
- if (!conversation) return { ok: false, reason: 'Conversation not found: ${escapedConversationId}' };
16182
- var fetched = await signal.Data.getMessagesByConversation('${escapedConversationId}', {
16183
- limit: ${limit},
16184
- MessageCollection: whisper.MessageCollection,
16185
- });
16186
- var models = (fetched.models || fetched || []).slice();
16187
- models.sort(function(left, right) {
16188
- var leftSentAt = left && left.get ? (left.get('sent_at') || 0) : (left && left.sent_at) || 0;
16189
- var rightSentAt = right && right.get ? (right.get('sent_at') || 0) : (right && right.sent_at) || 0;
16190
- return leftSentAt - rightSentAt;
16191
- });
16192
- return {
16193
- ok: true,
16194
- conversationId: '${escapedConversationId}',
16195
- messages: models.map(function(model, index) {
16196
- var attrs = typeof model.attributes === 'object' ? model.attributes : model;
16197
- var source = (typeof model.getSource === 'function' ? model.getSource() : attrs.source) || null;
16198
- var quote = attrs.quote ? {
16199
- id: attrs.quote.id || null,
16200
- author: attrs.quote.author || null,
16201
- text: attrs.quote.text || null,
16202
- } : null;
16203
- var attachments = Array.isArray(attrs.attachments) ? attrs.attachments.map(function(attachment) {
16204
- return {
16205
- path: attachment && attachment.path || null,
16206
- contentType: attachment && attachment.contentType || null,
16207
- fileName: attachment && attachment.fileName || null,
16208
- };
16209
- }) : [];
16210
- return {
16211
- index: index,
16212
- body: attrs.body || '',
16213
- source: source,
16214
- senderName: resolveSenderName(source, model),
16215
- sentAt: attrs.sent_at || attrs.timestamp || null,
16216
- id: attrs.id || model.id || '',
16217
- attachments: attachments,
16218
- quote: quote,
16219
- };
16220
- }),
16221
- };
16222
- } catch(e) {
16223
- return { ok: false, reason: e && e.message ? e.message : String(e) };
16224
16048
  }
16225
- })()`;
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;
16067
+ }
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;
16090
+ }
16091
+ seen.add(key);
16092
+ result.push(thread);
16093
+ }
16094
+ return result;
16226
16095
  }
16227
- function escapeForJs(value) {
16228
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
16096
+ function normalizeText(value) {
16097
+ return value.replace(/\s+/g, " ").replace(/\u200B/g, "").trim();
16229
16098
  }
16230
- function formatSentAt(sentAt) {
16231
- const value = typeof sentAt === "number" ? sentAt : typeof sentAt === "string" && /^\d+$/.test(sentAt) ? Number.parseInt(sentAt, 10) : NaN;
16232
- if (!Number.isFinite(value) || value <= 0) {
16233
- return null;
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 };
16234
16127
  }
16235
- const date = new Date(value);
16236
- return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
16128
+ return {
16129
+ time,
16130
+ sender: normalizeText(senderMatch[1] ?? "") || null,
16131
+ content: normalizeText(senderMatch[2] ?? "")
16132
+ };
16237
16133
  }
16238
- function finalizeIncrementalMessages(collectedNewestFirst, _reachedKnownMessage) {
16239
- return [...collectedNewestFirst].reverse();
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
+ }
16240
16174
  }
16241
- function pad2(value) {
16242
- return String(value).padStart(2, "0");
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;
16243
16189
  }
16244
16190
 
16245
16191
  // src/commands/AgentCommandHandler.ts
16246
- var import_node_fs12 = require("fs");
16247
- var import_node_path17 = __toESM(require("path"));
16192
+ var import_node_fs13 = require("fs");
16193
+ var import_node_path18 = __toESM(require("path"));
16248
16194
  init_AgentLlmClient();
16249
16195
  init_sessionStore();
16250
16196
 
@@ -16252,7 +16198,7 @@ init_sessionStore();
16252
16198
  var STORE_LOG_VERSION = 1;
16253
16199
  var LOG_KIND_TABLE = 1e4;
16254
16200
  var LOG_KIND_MAPPING = 6003;
16255
- function createHash3() {
16201
+ function createHash4() {
16256
16202
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
16257
16203
  }
16258
16204
  function createDescriptor(input) {
@@ -16277,8 +16223,8 @@ function descriptorPathSegments(descriptor) {
16277
16223
  return ["chains", normalized.chain, "namespaces", normalized.ns, normalized.kind, normalized.name];
16278
16224
  }
16279
16225
  function buildPathUrl(baseUrl, pathSegments, query) {
16280
- const path27 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16281
- const url = new URL(path27, baseUrl);
16226
+ const path28 = `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}`;
16227
+ const url = new URL(path28, baseUrl);
16282
16228
  if (query) {
16283
16229
  for (const [key, value] of Object.entries(query)) {
16284
16230
  url.searchParams.set(key, String(value));
@@ -16391,7 +16337,7 @@ var Namespace = class {
16391
16337
  },
16392
16338
  appendRow: async (data) => {
16393
16339
  const log = {
16394
- hash: createHash3(),
16340
+ hash: createHash4(),
16395
16341
  t: Date.now(),
16396
16342
  kind: LOG_KIND_TABLE,
16397
16343
  topic: descriptorToTopic(descriptor),
@@ -16403,7 +16349,7 @@ var Namespace = class {
16403
16349
  updateRow: async (data) => {
16404
16350
  const { id, ...value } = data;
16405
16351
  const log = {
16406
- hash: createHash3(),
16352
+ hash: createHash4(),
16407
16353
  t: Date.now(),
16408
16354
  kind: LOG_KIND_TABLE,
16409
16355
  topic: descriptorToTopic(descriptor),
@@ -16414,7 +16360,7 @@ var Namespace = class {
16414
16360
  },
16415
16361
  deleteRow: async (id) => {
16416
16362
  const log = {
16417
- hash: createHash3(),
16363
+ hash: createHash4(),
16418
16364
  t: Date.now(),
16419
16365
  kind: LOG_KIND_TABLE,
16420
16366
  topic: descriptorToTopic(descriptor),
@@ -16457,7 +16403,7 @@ var Namespace = class {
16457
16403
  },
16458
16404
  set: async (key, value) => {
16459
16405
  const log = {
16460
- hash: createHash3(),
16406
+ hash: createHash4(),
16461
16407
  t: Date.now(),
16462
16408
  kind: LOG_KIND_MAPPING,
16463
16409
  topic: descriptorToTopic(descriptor),
@@ -16468,7 +16414,7 @@ var Namespace = class {
16468
16414
  },
16469
16415
  delete: async (key) => {
16470
16416
  const log = {
16471
- hash: createHash3(),
16417
+ hash: createHash4(),
16472
16418
  t: Date.now(),
16473
16419
  kind: LOG_KIND_MAPPING,
16474
16420
  topic: descriptorToTopic(descriptor),
@@ -16710,11 +16656,11 @@ var AgentCommandHandler = class {
16710
16656
  }).token;
16711
16657
  }
16712
16658
  readJsonFile(filePath) {
16713
- const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16714
- if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16659
+ const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16660
+ if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16715
16661
  throw new Error(`JSON file not found: ${absolutePath}`);
16716
16662
  }
16717
- const raw = (0, import_node_fs12.readFileSync)(absolutePath, "utf8");
16663
+ const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16718
16664
  try {
16719
16665
  return JSON.parse(raw);
16720
16666
  } catch (error) {
@@ -16725,7 +16671,7 @@ var AgentCommandHandler = class {
16725
16671
  readJsonObjectFile(filePath) {
16726
16672
  const parsed = this.readJsonFile(filePath);
16727
16673
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
16728
- const absolutePath = import_node_path17.default.resolve(process.cwd(), filePath);
16674
+ const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16729
16675
  throw new Error(`Invalid JSON in ${absolutePath}: expected an object`);
16730
16676
  }
16731
16677
  return parsed;
@@ -16809,8 +16755,8 @@ var AgentCommandHandler = class {
16809
16755
  };
16810
16756
 
16811
16757
  // src/commands/RunnerCommandHandler.ts
16812
- var import_node_fs13 = require("fs");
16813
- var import_node_path18 = __toESM(require("path"));
16758
+ var import_node_fs14 = require("fs");
16759
+ var import_node_path19 = __toESM(require("path"));
16814
16760
  init_sessionStore();
16815
16761
  init_namespacedRef();
16816
16762
  var RunnerCommandHandler = class {
@@ -16905,11 +16851,11 @@ var RunnerCommandHandler = class {
16905
16851
  }).token;
16906
16852
  }
16907
16853
  readJsonFile(filePath) {
16908
- const absolutePath = import_node_path18.default.resolve(process.cwd(), filePath);
16909
- if (!(0, import_node_fs13.existsSync)(absolutePath)) {
16854
+ const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
16855
+ if (!(0, import_node_fs14.existsSync)(absolutePath)) {
16910
16856
  throw new Error(`JSON file not found: ${absolutePath}`);
16911
16857
  }
16912
- const raw = (0, import_node_fs13.readFileSync)(absolutePath, "utf8");
16858
+ const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
16913
16859
  try {
16914
16860
  return JSON.parse(raw);
16915
16861
  } catch (error) {
@@ -17028,8 +16974,8 @@ var NsCommandHandler = class {
17028
16974
  };
17029
16975
 
17030
16976
  // src/commands/StoreCommandHandler.ts
17031
- var import_node_fs14 = require("fs");
17032
- var import_node_path19 = __toESM(require("path"));
16977
+ var import_node_fs15 = require("fs");
16978
+ var import_node_path20 = __toESM(require("path"));
17033
16979
  var StoreCommandHandler = class {
17034
16980
  async handle(args) {
17035
16981
  if (args.scope === "namespace") {
@@ -17171,11 +17117,11 @@ var StoreCommandHandler = class {
17171
17117
  return payload.data ?? [];
17172
17118
  }
17173
17119
  readJsonFile(filePath) {
17174
- const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
17175
- if (!(0, import_node_fs14.existsSync)(absolutePath)) {
17120
+ const absolutePath = import_node_path20.default.resolve(process.cwd(), filePath);
17121
+ if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17176
17122
  throw new Error(`JSON file not found: ${absolutePath}`);
17177
17123
  }
17178
- const raw = (0, import_node_fs14.readFileSync)(absolutePath, "utf8");
17124
+ const raw = (0, import_node_fs15.readFileSync)(absolutePath, "utf8");
17179
17125
  try {
17180
17126
  return JSON.parse(raw);
17181
17127
  } catch (error) {
@@ -17227,13 +17173,13 @@ var StoreCommandHandler = class {
17227
17173
  };
17228
17174
 
17229
17175
  // src/commands/AppCommandHandler.ts
17230
- var import_node_fs16 = require("fs");
17231
- var import_node_path21 = __toESM(require("path"));
17176
+ var import_node_fs17 = require("fs");
17177
+ var import_node_path22 = __toESM(require("path"));
17232
17178
  init_sessionStore();
17233
17179
 
17234
17180
  // src/appIcon.ts
17235
- var import_node_fs15 = require("fs");
17236
- var import_node_path20 = __toESM(require("path"));
17181
+ var import_node_fs16 = require("fs");
17182
+ var import_node_path21 = __toESM(require("path"));
17237
17183
  var import_sharp = __toESM(require("sharp"));
17238
17184
  init_namespacedRef();
17239
17185
  var ANDROID_ICON_SPECS = [
@@ -17254,33 +17200,19 @@ var IOS_ICON_SPECS = [
17254
17200
  var pngToIco = require("png-to-ico").default;
17255
17201
  function normalizeAppIcon(icon, cwd) {
17256
17202
  if (typeof icon !== "string" || !icon.trim()) {
17257
- throw new Error("App icon must be a non-empty SVG string or .svg file path.");
17203
+ throw new Error("App icon must be a non-empty http(s) URL.");
17258
17204
  }
17259
17205
  const trimmed = icon.trim();
17260
17206
  if (looksLikeSvgMarkup(trimmed)) {
17261
- return trimmed;
17207
+ throw new Error("App icon cannot be inline SVG. Upload the SVG to OSS first and use its http(s) URL.");
17262
17208
  }
17263
17209
  if (trimmed.startsWith("data:")) {
17264
- return normalizeSvgDataUrl(trimmed);
17210
+ throw new Error("App icon cannot be a data URL. Upload the SVG to OSS first and use its http(s) URL.");
17265
17211
  }
17266
17212
  if (/^https?:\/\//i.test(trimmed)) {
17267
- throw new Error("App icon must be SVG source, not a remote URL.");
17268
- }
17269
- const absolutePath = import_node_path20.default.resolve(cwd, trimmed);
17270
- if (!(0, import_node_fs15.existsSync)(absolutePath)) {
17271
- if (trimmed.toLowerCase().endsWith(".svg")) {
17272
- throw new Error(`App icon SVG file not found: ${absolutePath}`);
17273
- }
17274
- throw new Error("App icon must be inline SVG, an SVG data URL, or a .svg file path.");
17275
- }
17276
- if (import_node_path20.default.extname(absolutePath).toLowerCase() !== ".svg") {
17277
- throw new Error(`App icon file must use the .svg extension: ${absolutePath}`);
17278
- }
17279
- const svg = (0, import_node_fs15.readFileSync)(absolutePath, "utf8").trim();
17280
- if (!looksLikeSvgMarkup(svg)) {
17281
- throw new Error(`App icon file is not valid SVG: ${absolutePath}`);
17213
+ return trimmed;
17282
17214
  }
17283
- return svg;
17215
+ throw new Error("App icon must be an http(s) URL. Upload SVG files to OSS first and use the public link.");
17284
17216
  }
17285
17217
  function prepareAppPayload(payload, cwd) {
17286
17218
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
@@ -17301,13 +17233,13 @@ function createAppIconExportPlans(input) {
17301
17233
  if (!input.outDir) {
17302
17234
  throw new Error("App icon preset export requires --out-dir.");
17303
17235
  }
17304
- const outDir = import_node_path20.default.resolve(input.cwd, input.outDir);
17305
- (0, import_node_fs15.mkdirSync)(outDir, { recursive: true });
17236
+ const outDir = import_node_path21.default.resolve(input.cwd, input.outDir);
17237
+ (0, import_node_fs16.mkdirSync)(outDir, { recursive: true });
17306
17238
  return specs.map((spec) => ({
17307
17239
  width: spec.width,
17308
17240
  height: spec.height,
17309
17241
  format: "png",
17310
- outputPath: import_node_path20.default.join(
17242
+ outputPath: import_node_path21.default.join(
17311
17243
  outDir,
17312
17244
  `${namespace}-${objectId}-${spec.name}-${spec.width}x${spec.height}.png`
17313
17245
  )
@@ -17320,8 +17252,8 @@ function createAppIconExportPlans(input) {
17320
17252
  if (!input.output) {
17321
17253
  throw new Error("Single-size app icon export requires --output.");
17322
17254
  }
17323
- const resolvedOutput = import_node_path20.default.resolve(input.cwd, input.output);
17324
- (0, import_node_fs15.mkdirSync)(import_node_path20.default.dirname(resolvedOutput), { recursive: true });
17255
+ const resolvedOutput = import_node_path21.default.resolve(input.cwd, input.output);
17256
+ (0, import_node_fs16.mkdirSync)(import_node_path21.default.dirname(resolvedOutput), { recursive: true });
17325
17257
  return [
17326
17258
  {
17327
17259
  width: input.width,
@@ -17331,11 +17263,34 @@ function createAppIconExportPlans(input) {
17331
17263
  }
17332
17264
  ];
17333
17265
  }
17334
- async function exportAppIcon(svg, plans) {
17266
+ async function exportAppIcon(icon, plans) {
17267
+ const svg = await resolveAppIconSvg(icon);
17335
17268
  for (const plan of plans) {
17336
17269
  const bytes = await renderAppIcon(svg, plan.width, plan.height, plan.format);
17337
- (0, import_node_fs15.writeFileSync)(plan.outputPath, bytes);
17270
+ (0, import_node_fs16.writeFileSync)(plan.outputPath, bytes);
17271
+ }
17272
+ }
17273
+ async function resolveAppIconSvg(icon) {
17274
+ const trimmed = icon.trim();
17275
+ if (looksLikeSvgMarkup(trimmed)) {
17276
+ return trimmed;
17277
+ }
17278
+ if (!/^https?:\/\//i.test(trimmed)) {
17279
+ throw new Error("App icon export requires inline SVG or an http(s) SVG URL.");
17280
+ }
17281
+ const response = await fetch(trimmed);
17282
+ if (!response.ok) {
17283
+ throw new Error(`Failed to fetch app icon SVG: ${response.status} ${response.statusText}`.trim());
17284
+ }
17285
+ const contentType = response.headers.get("content-type")?.toLowerCase() || "";
17286
+ const svg = (await response.text()).trim();
17287
+ if (contentType && !contentType.includes("image/svg+xml") && !looksLikeSvgMarkup(svg)) {
17288
+ throw new Error(`App icon URL must return SVG content. Received: ${contentType}`);
17338
17289
  }
17290
+ if (!looksLikeSvgMarkup(svg)) {
17291
+ throw new Error("App icon URL does not contain valid SVG markup.");
17292
+ }
17293
+ return svg;
17339
17294
  }
17340
17295
  async function renderAppIcon(svg, width, height, format) {
17341
17296
  if (format === "svg") {
@@ -17357,23 +17312,6 @@ async function renderAppIcon(svg, width, height, format) {
17357
17312
  function looksLikeSvgMarkup(value) {
17358
17313
  return /<svg[\s>]/i.test(value);
17359
17314
  }
17360
- function normalizeSvgDataUrl(value) {
17361
- const match = /^data:([^;,]+)?(?:;charset=[^;,]+)?(?:;(base64))?,(.*)$/i.exec(value);
17362
- if (!match) {
17363
- throw new Error("Invalid app icon data URL.");
17364
- }
17365
- const mimeType = (match[1] || "").toLowerCase();
17366
- if (mimeType !== "image/svg+xml") {
17367
- throw new Error(`App icon data URL must be image/svg+xml. Received: ${mimeType || "unknown"}`);
17368
- }
17369
- const payload = match[3] || "";
17370
- const decoded = match[2] ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
17371
- const svg = decoded.trim();
17372
- if (!looksLikeSvgMarkup(svg)) {
17373
- throw new Error("App icon data URL does not contain valid SVG markup.");
17374
- }
17375
- return svg;
17376
- }
17377
17315
 
17378
17316
  // src/commands/AppCommandHandler.ts
17379
17317
  init_namespacedRef();
@@ -17389,7 +17327,12 @@ var AppCommandHandler = class {
17389
17327
  "GET",
17390
17328
  "/apps"
17391
17329
  );
17392
- const items = args.namespace ? response.items.filter((item) => item.namespace === args.namespace) : response.items;
17330
+ const items = response.items.filter((item) => {
17331
+ if (!args.all && item.status !== "active") {
17332
+ return false;
17333
+ }
17334
+ return args.namespace ? item.namespace === args.namespace : true;
17335
+ });
17393
17336
  this.renderList(items, args.json);
17394
17337
  return;
17395
17338
  }
@@ -17525,10 +17468,10 @@ var AppCommandHandler = class {
17525
17468
  }
17526
17469
  readJsonFile(filePath) {
17527
17470
  const absolutePath = this.resolveFilePath(filePath);
17528
- if (!(0, import_node_fs16.existsSync)(absolutePath)) {
17471
+ if (!(0, import_node_fs17.existsSync)(absolutePath)) {
17529
17472
  throw new Error(`JSON file not found: ${absolutePath}`);
17530
17473
  }
17531
- const raw = (0, import_node_fs16.readFileSync)(absolutePath, "utf8");
17474
+ const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
17532
17475
  try {
17533
17476
  return JSON.parse(raw);
17534
17477
  } catch (error) {
@@ -17538,10 +17481,10 @@ var AppCommandHandler = class {
17538
17481
  }
17539
17482
  preparePayload(filePath) {
17540
17483
  const absolutePath = this.resolveFilePath(filePath);
17541
- return prepareAppPayload(this.readJsonFile(filePath), import_node_path21.default.dirname(absolutePath));
17484
+ return prepareAppPayload(this.readJsonFile(filePath), import_node_path22.default.dirname(absolutePath));
17542
17485
  }
17543
17486
  resolveFilePath(filePath) {
17544
- return import_node_path21.default.resolve(process.cwd(), filePath);
17487
+ return import_node_path22.default.resolve(process.cwd(), filePath);
17545
17488
  }
17546
17489
  async request(baseUrl, token, method, pathname, body) {
17547
17490
  const response = await fetch(`${baseUrl}${pathname}`, {
@@ -17615,15 +17558,15 @@ function readNamespaceFromPayload(payload) {
17615
17558
  }
17616
17559
 
17617
17560
  // src/commands/ServiceCommandHandler.ts
17618
- var import_node_fs18 = require("fs");
17561
+ var import_node_fs19 = require("fs");
17619
17562
  var import_promises5 = require("fs/promises");
17620
- var import_node_path23 = __toESM(require("path"));
17563
+ var import_node_path24 = __toESM(require("path"));
17621
17564
  var import_promises6 = require("readline/promises");
17622
17565
 
17623
17566
  // src/services/ServiceRuntimeRunner.ts
17624
- var import_node_fs17 = require("fs");
17625
- var import_node_os7 = __toESM(require("os"));
17626
- var import_node_path22 = __toESM(require("path"));
17567
+ var import_node_fs18 = require("fs");
17568
+ var import_node_os8 = __toESM(require("os"));
17569
+ var import_node_path23 = __toESM(require("path"));
17627
17570
  var import_node_child_process10 = require("child_process");
17628
17571
  var import_node_readline = require("readline");
17629
17572
  var import_chalk3 = __toESM(require("chalk"));
@@ -17788,14 +17731,14 @@ var ServiceRuntimeRunner = class {
17788
17731
  }
17789
17732
  resolveRepoRoot(service, cwd) {
17790
17733
  if (cwd) {
17791
- return import_node_path22.default.resolve(process.cwd(), cwd);
17734
+ return import_node_path23.default.resolve(process.cwd(), cwd);
17792
17735
  }
17793
17736
  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();
17794
17737
  if (repository) {
17795
17738
  const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
17796
17739
  if (repoName) {
17797
- const candidate = import_node_path22.default.join(import_node_os7.default.homedir(), "dev", "pmate", repoName);
17798
- if ((0, import_node_fs17.existsSync)(candidate)) {
17740
+ const candidate = import_node_path23.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName);
17741
+ if ((0, import_node_fs18.existsSync)(candidate)) {
17799
17742
  return candidate;
17800
17743
  }
17801
17744
  }
@@ -17808,12 +17751,12 @@ var ServiceRuntimeRunner = class {
17808
17751
  if (!dir) {
17809
17752
  throw new Error(`Missing service dir for ${service.id}`);
17810
17753
  }
17811
- return import_node_path22.default.resolve(repoRoot, dir);
17754
+ return import_node_path23.default.resolve(repoRoot, dir);
17812
17755
  }
17813
17756
  async deployVite(service, options) {
17814
17757
  const appDir = this.resolveLocalDir(service, options.cwd);
17815
- const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17816
- if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17758
+ const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17759
+ if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17817
17760
  throw new Error(`No package.json found at ${packageJsonPath}`);
17818
17761
  }
17819
17762
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -17822,8 +17765,8 @@ var ServiceRuntimeRunner = class {
17822
17765
  throw new Error(`Missing package name in ${packageJsonPath}`);
17823
17766
  }
17824
17767
  await this.runCommand("pnpm", ["--filter", pkgName, "build"], this.resolveRepoRoot(service, options.cwd));
17825
- const distDir = import_node_path22.default.join(appDir, "dist");
17826
- if (!(0, import_node_fs17.existsSync)(distDir)) {
17768
+ const distDir = import_node_path23.default.join(appDir, "dist");
17769
+ if (!(0, import_node_fs18.existsSync)(distDir)) {
17827
17770
  throw new Error(`No dist directory found at ${distDir}`);
17828
17771
  }
17829
17772
  const site = this.getSite(service);
@@ -17835,8 +17778,8 @@ var ServiceRuntimeRunner = class {
17835
17778
  }
17836
17779
  async deployStatic(service, options) {
17837
17780
  const appDir = this.resolveLocalDir(service, options.cwd);
17838
- const srcDir = import_node_path22.default.join(appDir, "src");
17839
- if (!(0, import_node_fs17.existsSync)(srcDir)) {
17781
+ const srcDir = import_node_path23.default.join(appDir, "src");
17782
+ if (!(0, import_node_fs18.existsSync)(srcDir)) {
17840
17783
  throw new Error(`No src directory found at ${srcDir}`);
17841
17784
  }
17842
17785
  const site = this.getSite(service);
@@ -17956,7 +17899,7 @@ var ServiceRuntimeRunner = class {
17956
17899
  const serverAddress = await resolveServerAddress(server);
17957
17900
  const templateName = exposeType === "websocket" ? "nginx.ws.template" : "nginx.rest.template";
17958
17901
  const templatePath = this.resolveTemplatePath(templateName);
17959
- const template = (0, import_node_fs17.readFileSync)(templatePath, "utf8");
17902
+ const template = (0, import_node_fs18.readFileSync)(templatePath, "utf8");
17960
17903
  const host = `${subdomain}.${("domain" in network ? network.domain?.trim() : void 0) || "pmate.chat"}`;
17961
17904
  const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
17962
17905
  const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
@@ -17988,11 +17931,11 @@ var ServiceRuntimeRunner = class {
17988
17931
  }
17989
17932
  if (config.ensurePackageJson) {
17990
17933
  const appDir = this.resolveLocalDir(service, options.cwd);
17991
- const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
17992
- if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
17934
+ const packageJsonPath = import_node_path23.default.join(appDir, "package.json");
17935
+ if (!(0, import_node_fs18.existsSync)(packageJsonPath)) {
17993
17936
  throw new Error(`Missing package.json at ${packageJsonPath}`);
17994
17937
  }
17995
- const pkgStat = (0, import_node_fs17.statSync)(packageJsonPath);
17938
+ const pkgStat = (0, import_node_fs18.statSync)(packageJsonPath);
17996
17939
  if (!pkgStat.isFile()) {
17997
17940
  throw new Error(`package.json is not a file at ${packageJsonPath}`);
17998
17941
  }
@@ -18416,9 +18359,9 @@ var ServiceRuntimeRunner = class {
18416
18359
  return tokens;
18417
18360
  }
18418
18361
  readPathValue(source, pathExpr) {
18419
- const path27 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18362
+ const path28 = pathExpr.split(".").map((segment) => segment.trim()).filter(Boolean);
18420
18363
  let cursor = source;
18421
- for (const segment of path27) {
18364
+ for (const segment of path28) {
18422
18365
  if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
18423
18366
  return void 0;
18424
18367
  }
@@ -18461,12 +18404,12 @@ var ServiceRuntimeRunner = class {
18461
18404
  }
18462
18405
  resolveTemplatePath(templateName) {
18463
18406
  const candidates = [
18464
- import_node_path22.default.join(__dirname, "..", "templates", templateName),
18465
- import_node_path22.default.join(__dirname, "..", "src", "templates", templateName),
18466
- import_node_path22.default.join(__dirname, "templates", templateName)
18407
+ import_node_path23.default.join(__dirname, "..", "templates", templateName),
18408
+ import_node_path23.default.join(__dirname, "..", "src", "templates", templateName),
18409
+ import_node_path23.default.join(__dirname, "templates", templateName)
18467
18410
  ];
18468
18411
  for (const candidate of candidates) {
18469
- if ((0, import_node_fs17.existsSync)(candidate)) {
18412
+ if ((0, import_node_fs18.existsSync)(candidate)) {
18470
18413
  return candidate;
18471
18414
  }
18472
18415
  }
@@ -18498,7 +18441,7 @@ var ServiceRuntimeRunner = class {
18498
18441
  });
18499
18442
  }
18500
18443
  loadPackageJson(packageJsonPath) {
18501
- const raw = (0, import_node_fs17.readFileSync)(packageJsonPath, "utf8");
18444
+ const raw = (0, import_node_fs18.readFileSync)(packageJsonPath, "utf8");
18502
18445
  const parsed = JSON.parse(raw);
18503
18446
  if (!parsed || typeof parsed !== "object") {
18504
18447
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -18507,7 +18450,7 @@ var ServiceRuntimeRunner = class {
18507
18450
  }
18508
18451
  writePackageJson(packageJsonPath, data) {
18509
18452
  const serialized = JSON.stringify(data, null, 2);
18510
- (0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
18453
+ (0, import_node_fs18.writeFileSync)(packageJsonPath, `${serialized}
18511
18454
  `, "utf8");
18512
18455
  }
18513
18456
  runCommand(command, args, cwd) {
@@ -18844,7 +18787,7 @@ var ServiceCommandHandler = class {
18844
18787
  format: "dotenv"
18845
18788
  }
18846
18789
  );
18847
- const outputPath = import_node_path23.default.resolve(process.cwd(), args.output);
18790
+ const outputPath = import_node_path24.default.resolve(process.cwd(), args.output);
18848
18791
  await (0, import_promises5.writeFile)(outputPath, payload.content, "utf8");
18849
18792
  ui.success(`Wrote env file for ${args.appId} to ${outputPath}`);
18850
18793
  this.renderJson({ ...payload, output: outputPath }, args.json);
@@ -18873,11 +18816,11 @@ var ServiceCommandHandler = class {
18873
18816
  return (baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
18874
18817
  }
18875
18818
  readJsonFile(filePath) {
18876
- const absolutePath = import_node_path23.default.resolve(process.cwd(), filePath);
18877
- if (!(0, import_node_fs18.existsSync)(absolutePath)) {
18819
+ const absolutePath = import_node_path24.default.resolve(process.cwd(), filePath);
18820
+ if (!(0, import_node_fs19.existsSync)(absolutePath)) {
18878
18821
  throw new Error(`JSON file not found: ${absolutePath}`);
18879
18822
  }
18880
- const raw = (0, import_node_fs18.readFileSync)(absolutePath, "utf8");
18823
+ const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
18881
18824
  try {
18882
18825
  return JSON.parse(raw);
18883
18826
  } catch (error) {
@@ -19217,8 +19160,8 @@ var RoleCommandHandler = class {
19217
19160
  };
19218
19161
 
19219
19162
  // src/commands/AccountCommandHandler.ts
19220
- var import_node_fs19 = require("fs");
19221
- var import_node_path24 = __toESM(require("path"));
19163
+ var import_node_fs20 = require("fs");
19164
+ var import_node_path25 = __toESM(require("path"));
19222
19165
  init_sessionStore();
19223
19166
  var AccountCommandHandler = class {
19224
19167
  async handle(args) {
@@ -19466,10 +19409,10 @@ var AccountCommandHandler = class {
19466
19409
  throw new Error("Missing visible apps input");
19467
19410
  }
19468
19411
  const absolutePath = this.resolveFilePath(args.filePath);
19469
- if (!(0, import_node_fs19.existsSync)(absolutePath)) {
19412
+ if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19470
19413
  throw new Error(`JSON file not found: ${absolutePath}`);
19471
19414
  }
19472
- const raw = (0, import_node_fs19.readFileSync)(absolutePath, "utf8");
19415
+ const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19473
19416
  let parsed;
19474
19417
  try {
19475
19418
  parsed = JSON.parse(raw);
@@ -19483,7 +19426,7 @@ var AccountCommandHandler = class {
19483
19426
  return parsed;
19484
19427
  }
19485
19428
  resolveFilePath(filePath) {
19486
- return import_node_path24.default.resolve(process.cwd(), filePath);
19429
+ return import_node_path25.default.resolve(process.cwd(), filePath);
19487
19430
  }
19488
19431
  };
19489
19432
  function encodePathSegment(value) {
@@ -19491,8 +19434,8 @@ function encodePathSegment(value) {
19491
19434
  }
19492
19435
 
19493
19436
  // src/commands/OrgCommandHandler.ts
19494
- var import_node_fs20 = require("fs");
19495
- var import_node_path25 = __toESM(require("path"));
19437
+ var import_node_fs21 = require("fs");
19438
+ var import_node_path26 = __toESM(require("path"));
19496
19439
  init_sessionStore();
19497
19440
  var OrgCommandHandler = class {
19498
19441
  async handle(args) {
@@ -19663,11 +19606,11 @@ var OrgCommandHandler = class {
19663
19606
  if (!filePath) {
19664
19607
  throw new Error(`Missing ${itemLabel} input`);
19665
19608
  }
19666
- const absolutePath = import_node_path25.default.resolve(process.cwd(), filePath);
19667
- if (!(0, import_node_fs20.existsSync)(absolutePath)) {
19609
+ const absolutePath = import_node_path26.default.resolve(process.cwd(), filePath);
19610
+ if (!(0, import_node_fs21.existsSync)(absolutePath)) {
19668
19611
  throw new Error(`JSON file not found: ${absolutePath}`);
19669
19612
  }
19670
- const raw = (0, import_node_fs20.readFileSync)(absolutePath, "utf8");
19613
+ const raw = (0, import_node_fs21.readFileSync)(absolutePath, "utf8");
19671
19614
  let parsed;
19672
19615
  try {
19673
19616
  parsed = JSON.parse(raw);
@@ -20566,6 +20509,7 @@ function parseAppArgs(tokens) {
20566
20509
  };
20567
20510
  if (action === "list") {
20568
20511
  let namespace;
20512
+ let all = false;
20569
20513
  while (rest.length > 0) {
20570
20514
  const token = rest.shift();
20571
20515
  switch (token) {
@@ -20573,6 +20517,9 @@ function parseAppArgs(tokens) {
20573
20517
  case "--namespace":
20574
20518
  namespace = requireValue5(rest, token);
20575
20519
  break;
20520
+ case "--all":
20521
+ all = true;
20522
+ break;
20576
20523
  case "--base-url":
20577
20524
  baseUrl = requireValue5(rest, token);
20578
20525
  break;
@@ -20583,7 +20530,7 @@ function parseAppArgs(tokens) {
20583
20530
  throw new Error(`Unknown app list option: ${token}`);
20584
20531
  }
20585
20532
  }
20586
- return { type: "app" /* App */, action, namespace, baseUrl, json };
20533
+ return { type: "app" /* App */, action, namespace, all, baseUrl, json };
20587
20534
  }
20588
20535
  if (action === "view") {
20589
20536
  const rawRef = rest.shift();
@@ -22152,7 +22099,7 @@ function requireValue11(tokens, option) {
22152
22099
 
22153
22100
  // src/index.ts
22154
22101
  var pkg = JSON.parse(
22155
- (0, import_node_fs21.readFileSync)(import_node_path26.default.resolve(__dirname, "..", "package.json"), "utf8")
22102
+ (0, import_node_fs22.readFileSync)(import_node_path27.default.resolve(__dirname, "..", "package.json"), "utf8")
22156
22103
  );
22157
22104
  var HELP_TEXT = [
22158
22105
  import_chalk4.default.bold(`pmate v${pkg.version}`),
@@ -22295,7 +22242,7 @@ var HELP_TEXT = [
22295
22242
  ` ${import_chalk4.default.gray(" kv list|get|exists|set|delete <name> [options]")}`,
22296
22243
  ` ${import_chalk4.default.gray(" defaults: chain=pmate namespace=pmate env base URLs from BLOCKCHAIN_BASE_URL / INDEXER_BASE_URL")}`,
22297
22244
  ` ${import_chalk4.default.cyan("app")} Manage app registry entries via apps-api`,
22298
- ` ${import_chalk4.default.gray(" list [--ns <namespace>] [--base-url <url>] [--json]")}`,
22245
+ ` ${import_chalk4.default.gray(" list [--ns <namespace>] [--all] [--base-url <url>] [--json]")}`,
22299
22246
  ` ${import_chalk4.default.gray(" get <ns:objectId> [--base-url <url>] [--json]")}`,
22300
22247
  ` ${import_chalk4.default.gray(" view <ns:objectId> [--base-url <url>]")}`,
22301
22248
  ` ${import_chalk4.default.gray(" create --file <path> [--base-url <url>] [--json]")}`,