@rubytech/create-maxy 1.0.459 → 1.0.461

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.459",
3
+ "version": "1.0.461",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -23641,7 +23641,8 @@ var WhatsAppAccountSchema = external_exports.object({
23641
23641
  dmPolicy: DmPolicySchema.optional().describe("Who can message your public agent via WhatsApp DM. 'Open' lets anyone message. 'Allowlist' restricts to approved phone numbers. 'Disabled' blocks all public messages."),
23642
23642
  groupPolicy: GroupPolicySchema.optional().describe("Whether your agent responds in WhatsApp group chats. 'Open' enables group responses (subject to per-group activation). 'Allowlist' restricts to approved senders. 'Disabled' ignores all group messages."),
23643
23643
  selfChatMode: external_exports.boolean().optional().describe("Enable if the bot uses the owner's personal WhatsApp number (same phone for admin and bot). Changes how self-messages are routed."),
23644
- allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164 format, e.g. +44...) allowed to message via DM when policy is 'allowlist'. Add '*' to allow everyone."),
23644
+ adminPhones: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164) that route to the admin agent. Managed via add-admin-phone / remove-admin-phone."),
23645
+ allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
23645
23646
  groupAllowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to trigger the agent in groups when group policy is 'allowlist'."),
23646
23647
  sendReadReceipts: external_exports.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
23647
23648
  groups: external_exports.record(
@@ -23671,7 +23672,8 @@ var WhatsAppConfigSchema = external_exports.object({
23671
23672
  accounts: external_exports.record(external_exports.string(), WhatsAppAccountSchema.optional()).optional().describe("Per-account config keyed by account ID."),
23672
23673
  dmPolicy: DmPolicySchema.optional().default("disabled").describe("Who can message your public agent via WhatsApp DM. 'Open' lets anyone message. 'Allowlist' restricts to approved phone numbers. 'Disabled' blocks all public messages."),
23673
23674
  groupPolicy: GroupPolicySchema.optional().default("disabled").describe("Whether your agent responds in WhatsApp group chats. 'Open' enables group responses (subject to per-group activation). 'Allowlist' restricts to approved senders. 'Disabled' ignores all group messages."),
23674
- allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164 format, e.g. +44...) allowed to message via DM when policy is 'allowlist'. Add '*' to allow everyone."),
23675
+ adminPhones: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164) that route to the admin agent. Managed via add-admin-phone / remove-admin-phone."),
23676
+ allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
23675
23677
  groupAllowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to trigger the agent in groups when group policy is 'allowlist'."),
23676
23678
  sendReadReceipts: external_exports.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
23677
23679
  mediaMaxMb: external_exports.number().int().positive().optional().default(50).describe("Maximum file size in MB for media the agent will download and process."),
@@ -23689,6 +23691,282 @@ var WhatsAppConfigSchema = external_exports.object({
23689
23691
  });
23690
23692
  });
23691
23693
 
23694
+ // app/lib/whatsapp/config-persist.ts
23695
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
23696
+ import { resolve as resolve9, join as join5 } from "path";
23697
+ var TAG4 = "[whatsapp:config]";
23698
+ function configPath(accountDir) {
23699
+ return resolve9(accountDir, "account.json");
23700
+ }
23701
+ function readConfig(accountDir) {
23702
+ const path2 = configPath(accountDir);
23703
+ if (!existsSync10(path2)) throw new Error(`account.json not found at ${path2}`);
23704
+ return JSON.parse(readFileSync10(path2, "utf-8"));
23705
+ }
23706
+ function writeConfig(accountDir, config2) {
23707
+ const path2 = configPath(accountDir);
23708
+ writeFileSync6(path2, JSON.stringify(config2, null, 2) + "\n", "utf-8");
23709
+ }
23710
+ function reloadManagerConfig(accountDir) {
23711
+ try {
23712
+ const config2 = readConfig(accountDir);
23713
+ reloadConfig(config2);
23714
+ console.error(`${TAG4} reloaded manager config`);
23715
+ } catch (err) {
23716
+ console.error(`${TAG4} manager config reload failed: ${String(err)}`);
23717
+ }
23718
+ }
23719
+ var E164_PATTERN = /^\+\d{7,15}$/;
23720
+ function migrateAdminPhones(wa) {
23721
+ if (wa.adminPhones !== void 0) return 0;
23722
+ if (!Array.isArray(wa.allowFrom)) return 0;
23723
+ const allowFrom = wa.allowFrom;
23724
+ const phones = [];
23725
+ const remaining = [];
23726
+ for (const entry of allowFrom) {
23727
+ if (E164_PATTERN.test(entry)) {
23728
+ phones.push(entry);
23729
+ } else {
23730
+ remaining.push(entry);
23731
+ }
23732
+ }
23733
+ if (phones.length === 0) return 0;
23734
+ wa.adminPhones = phones;
23735
+ wa.allowFrom = remaining;
23736
+ console.error(`${TAG4} migrated ${phones.length} phone(s) from allowFrom to adminPhones`);
23737
+ return phones.length;
23738
+ }
23739
+ function persistAfterPairing(accountDir, accountId, selfPhone) {
23740
+ try {
23741
+ const config2 = readConfig(accountDir);
23742
+ if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
23743
+ config2.whatsapp = {};
23744
+ }
23745
+ const wa = config2.whatsapp;
23746
+ if (!wa.accounts || typeof wa.accounts !== "object") {
23747
+ wa.accounts = {};
23748
+ }
23749
+ const accounts = wa.accounts;
23750
+ if (!accounts[accountId] || typeof accounts[accountId] !== "object") {
23751
+ accounts[accountId] = { name: "Main" };
23752
+ }
23753
+ if (selfPhone) {
23754
+ const normalized = selfPhone.startsWith("+") ? selfPhone : `+${selfPhone}`;
23755
+ if (!Array.isArray(wa.adminPhones)) {
23756
+ wa.adminPhones = [];
23757
+ }
23758
+ const adminPhones = wa.adminPhones;
23759
+ if (!adminPhones.includes(normalized)) {
23760
+ adminPhones.push(normalized);
23761
+ console.error(`${TAG4} added selfPhone=${normalized} to adminPhones`);
23762
+ }
23763
+ } else {
23764
+ console.error(`${TAG4} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
23765
+ }
23766
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23767
+ if (!parsed.success) {
23768
+ const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
23769
+ console.error(`${TAG4} validation failed after pairing: ${msg}`);
23770
+ return { ok: false, error: `Validation failed: ${msg}` };
23771
+ }
23772
+ config2.whatsapp = parsed.data;
23773
+ writeConfig(accountDir, config2);
23774
+ console.error(`${TAG4} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
23775
+ reloadManagerConfig(accountDir);
23776
+ return { ok: true };
23777
+ } catch (err) {
23778
+ const msg = err instanceof Error ? err.message : String(err);
23779
+ console.error(`${TAG4} persist failed account=${accountId}: ${msg}`);
23780
+ return { ok: false, error: msg };
23781
+ }
23782
+ }
23783
+ function addAdminPhone(accountDir, phone) {
23784
+ const normalized = phone.trim();
23785
+ if (!E164_PATTERN.test(normalized)) {
23786
+ return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
23787
+ }
23788
+ try {
23789
+ const config2 = readConfig(accountDir);
23790
+ if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
23791
+ config2.whatsapp = {};
23792
+ }
23793
+ const wa = config2.whatsapp;
23794
+ migrateAdminPhones(wa);
23795
+ if (!Array.isArray(wa.adminPhones)) {
23796
+ wa.adminPhones = [];
23797
+ }
23798
+ const adminPhones = wa.adminPhones;
23799
+ if (adminPhones.includes(normalized)) {
23800
+ return { ok: true, message: `Phone ${normalized} is already in the admin list.` };
23801
+ }
23802
+ adminPhones.push(normalized);
23803
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23804
+ if (!parsed.success) {
23805
+ const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
23806
+ return { ok: false, error: `Validation failed: ${msg}` };
23807
+ }
23808
+ config2.whatsapp = parsed.data;
23809
+ writeConfig(accountDir, config2);
23810
+ console.error(`${TAG4} added admin phone=${normalized}`);
23811
+ reloadManagerConfig(accountDir);
23812
+ return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
23813
+ } catch (err) {
23814
+ const msg = err instanceof Error ? err.message : String(err);
23815
+ console.error(`${TAG4} addAdminPhone failed: ${msg}`);
23816
+ return { ok: false, error: msg };
23817
+ }
23818
+ }
23819
+ function removeAdminPhone(accountDir, phone) {
23820
+ const normalized = phone.trim();
23821
+ try {
23822
+ const config2 = readConfig(accountDir);
23823
+ if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
23824
+ return { ok: true, message: `No WhatsApp config exists \u2014 nothing to remove.` };
23825
+ }
23826
+ const wa = config2.whatsapp;
23827
+ migrateAdminPhones(wa);
23828
+ if (!Array.isArray(wa.adminPhones)) {
23829
+ return { ok: true, message: `No admin phones configured \u2014 nothing to remove.` };
23830
+ }
23831
+ const adminPhones = wa.adminPhones;
23832
+ const idx = adminPhones.indexOf(normalized);
23833
+ if (idx === -1) {
23834
+ return { ok: true, message: `Phone ${normalized} is not in the admin list.` };
23835
+ }
23836
+ adminPhones.splice(idx, 1);
23837
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23838
+ if (!parsed.success) {
23839
+ const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
23840
+ return { ok: false, error: `Validation failed: ${msg}` };
23841
+ }
23842
+ config2.whatsapp = parsed.data;
23843
+ writeConfig(accountDir, config2);
23844
+ console.error(`${TAG4} removed admin phone=${normalized}`);
23845
+ reloadManagerConfig(accountDir);
23846
+ return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
23847
+ } catch (err) {
23848
+ const msg = err instanceof Error ? err.message : String(err);
23849
+ console.error(`${TAG4} removeAdminPhone failed: ${msg}`);
23850
+ return { ok: false, error: msg };
23851
+ }
23852
+ }
23853
+ function readAdminPhones(accountDir) {
23854
+ try {
23855
+ const config2 = readConfig(accountDir);
23856
+ const wa = config2.whatsapp;
23857
+ if (!wa) return [];
23858
+ const migrated = migrateAdminPhones(wa);
23859
+ if (migrated > 0) {
23860
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23861
+ if (parsed.success) {
23862
+ config2.whatsapp = parsed.data;
23863
+ writeConfig(accountDir, config2);
23864
+ reloadManagerConfig(accountDir);
23865
+ }
23866
+ }
23867
+ if (!Array.isArray(wa.adminPhones)) return [];
23868
+ return wa.adminPhones.filter((p) => typeof p === "string");
23869
+ } catch {
23870
+ return [];
23871
+ }
23872
+ }
23873
+ function setPublicAgent(accountDir, slug) {
23874
+ const trimmed = slug.trim();
23875
+ if (!trimmed) {
23876
+ return { ok: false, error: "Agent slug cannot be empty." };
23877
+ }
23878
+ const agentConfigPath = join5(accountDir, "agents", trimmed, "config.json");
23879
+ if (!existsSync10(agentConfigPath)) {
23880
+ return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
23881
+ }
23882
+ try {
23883
+ const config2 = readConfig(accountDir);
23884
+ if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
23885
+ config2.whatsapp = {};
23886
+ }
23887
+ const wa = config2.whatsapp;
23888
+ wa.publicAgent = trimmed;
23889
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23890
+ if (!parsed.success) {
23891
+ const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
23892
+ return { ok: false, error: `Validation failed: ${msg}` };
23893
+ }
23894
+ config2.whatsapp = parsed.data;
23895
+ writeConfig(accountDir, config2);
23896
+ console.error(`${TAG4} publicAgent set to ${trimmed}`);
23897
+ reloadManagerConfig(accountDir);
23898
+ return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
23899
+ } catch (err) {
23900
+ const msg = err instanceof Error ? err.message : String(err);
23901
+ console.error(`${TAG4} setPublicAgent failed: ${msg}`);
23902
+ return { ok: false, error: msg };
23903
+ }
23904
+ }
23905
+ function getPublicAgent(accountDir) {
23906
+ try {
23907
+ const config2 = readConfig(accountDir);
23908
+ const wa = config2.whatsapp;
23909
+ if (!wa) return null;
23910
+ const slug = wa.publicAgent;
23911
+ if (typeof slug === "string" && slug.trim()) {
23912
+ return slug.trim();
23913
+ }
23914
+ return null;
23915
+ } catch {
23916
+ return null;
23917
+ }
23918
+ }
23919
+ function updateConfig(accountDir, fields) {
23920
+ const fieldNames = Object.keys(fields);
23921
+ if (fieldNames.length === 0) {
23922
+ return { ok: false, error: "No fields provided to update." };
23923
+ }
23924
+ try {
23925
+ const config2 = readConfig(accountDir);
23926
+ if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
23927
+ config2.whatsapp = {};
23928
+ }
23929
+ const wa = config2.whatsapp;
23930
+ migrateAdminPhones(wa);
23931
+ for (const [key, value] of Object.entries(fields)) {
23932
+ wa[key] = value;
23933
+ }
23934
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23935
+ if (!parsed.success) {
23936
+ const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
23937
+ console.error(`${TAG4} update validation failed: ${msg}`);
23938
+ return { ok: false, error: `Validation failed: ${msg}` };
23939
+ }
23940
+ config2.whatsapp = parsed.data;
23941
+ writeConfig(accountDir, config2);
23942
+ console.error(`${TAG4} updated fields=[${fieldNames.join(",")}]`);
23943
+ reloadManagerConfig(accountDir);
23944
+ return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
23945
+ } catch (err) {
23946
+ const msg = err instanceof Error ? err.message : String(err);
23947
+ console.error(`${TAG4} updateConfig failed: ${msg}`);
23948
+ return { ok: false, error: msg };
23949
+ }
23950
+ }
23951
+ function getConfig(accountDir) {
23952
+ try {
23953
+ const config2 = readConfig(accountDir);
23954
+ const wa = config2.whatsapp;
23955
+ if (!wa) return null;
23956
+ const migrated = migrateAdminPhones(wa);
23957
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
23958
+ if (!parsed.success) return wa;
23959
+ if (migrated > 0) {
23960
+ config2.whatsapp = parsed.data;
23961
+ writeConfig(accountDir, config2);
23962
+ reloadManagerConfig(accountDir);
23963
+ }
23964
+ return parsed.data;
23965
+ } catch {
23966
+ return null;
23967
+ }
23968
+ }
23969
+
23692
23970
  // app/lib/whatsapp/normalize.ts
23693
23971
  var WHATSAPP_USER_JID_RE = /^(\d+)(?::\d+)?@s\.whatsapp\.net$/i;
23694
23972
  var WHATSAPP_LID_RE = /^(\d+)@lid$/i;
@@ -23852,18 +24130,16 @@ function resolveAccountConfig(config2, accountConfig) {
23852
24130
  return {
23853
24131
  dmPolicy: accountConfig?.dmPolicy ?? config2.dmPolicy ?? "disabled",
23854
24132
  groupPolicy: accountConfig?.groupPolicy ?? config2.groupPolicy ?? "disabled",
24133
+ adminPhones: accountConfig?.adminPhones ?? config2.adminPhones ?? [],
23855
24134
  allowFrom: accountConfig?.allowFrom ?? config2.allowFrom ?? [],
23856
24135
  groupAllowFrom: accountConfig?.groupAllowFrom ?? config2.groupAllowFrom ?? [],
23857
24136
  selfChatMode: accountConfig?.selfChatMode ?? false
23858
24137
  };
23859
24138
  }
23860
- function isAdminPhone(phone, allowFrom) {
24139
+ function isAdminPhone(phone, adminPhones) {
23861
24140
  const normalized = normalizeE164(phone);
23862
24141
  if (!normalized) return false;
23863
- return allowFrom.some((entry) => {
23864
- if (entry === "*") return false;
23865
- return phonesMatch(entry, normalized);
23866
- });
24142
+ return adminPhones.some((entry) => phonesMatch(entry, normalized));
23867
24143
  }
23868
24144
  function checkDmAccess(params) {
23869
24145
  const { senderPhone, selfPhone } = params;
@@ -23871,7 +24147,7 @@ function checkDmAccess(params) {
23871
24147
  if (phonesMatch(senderPhone, selfPhone)) {
23872
24148
  return { allowed: true, reason: "self-phone", agentType: "admin" };
23873
24149
  }
23874
- if (isAdminPhone(senderPhone, cfg.allowFrom)) {
24150
+ if (isAdminPhone(senderPhone, cfg.adminPhones)) {
23875
24151
  return { allowed: true, reason: "admin-binding", agentType: "admin" };
23876
24152
  }
23877
24153
  switch (cfg.dmPolicy) {
@@ -23912,10 +24188,7 @@ function checkGroupAccess(params) {
23912
24188
  return { allowed: true, reason: "group-policy-open", agentType: "public" };
23913
24189
  }
23914
24190
  case "allowlist": {
23915
- const inList = cfg.groupAllowFrom.some((entry) => phonesMatch(entry, senderPhone)) || cfg.allowFrom.some((entry) => {
23916
- if (entry === "*") return false;
23917
- return phonesMatch(entry, senderPhone);
23918
- });
24191
+ const inList = cfg.groupAllowFrom.some((entry) => phonesMatch(entry, senderPhone)) || cfg.adminPhones.some((entry) => phonesMatch(entry, senderPhone));
23919
24192
  if (!inList) {
23920
24193
  return { allowed: false, reason: "not-in-group-allowlist", agentType: "public" };
23921
24194
  }
@@ -23979,7 +24252,7 @@ function isDuplicateInbound(key) {
23979
24252
  }
23980
24253
 
23981
24254
  // app/lib/whatsapp/outbound/send.ts
23982
- var TAG4 = "[whatsapp:outbound]";
24255
+ var TAG5 = "[whatsapp:outbound]";
23983
24256
  async function sendTextMessage(sock, to, text, opts) {
23984
24257
  try {
23985
24258
  const jid = to.includes("@") ? to : toWhatsappJid(to);
@@ -23991,11 +24264,11 @@ async function sendTextMessage(sock, to, text, opts) {
23991
24264
  const messageId = result?.key?.id;
23992
24265
  if (messageId) {
23993
24266
  trackAgentSentMessage(messageId);
23994
- console.error(`${TAG4} sent text to=${jid} id=${messageId}`);
24267
+ console.error(`${TAG5} sent text to=${jid} id=${messageId}`);
23995
24268
  }
23996
24269
  return { success: true, messageId: messageId ?? void 0 };
23997
24270
  } catch (err) {
23998
- console.error(`${TAG4} send failed to=${to}: ${String(err)}`);
24271
+ console.error(`${TAG5} send failed to=${to}: ${String(err)}`);
23999
24272
  return { success: false, error: String(err) };
24000
24273
  }
24001
24274
  }
@@ -24015,13 +24288,13 @@ async function sendReadReceipt(sock, chatJid, messageIds, participant) {
24015
24288
  // app/lib/whatsapp/inbound/media.ts
24016
24289
  import { randomUUID as randomUUID6 } from "crypto";
24017
24290
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
24018
- import { join as join5 } from "path";
24291
+ import { join as join6 } from "path";
24019
24292
  import {
24020
24293
  downloadMediaMessage,
24021
24294
  downloadContentFromMessage,
24022
24295
  normalizeMessageContent as normalizeMessageContent2
24023
24296
  } from "@whiskeysockets/baileys";
24024
- var TAG5 = "[whatsapp:media]";
24297
+ var TAG6 = "[whatsapp:media]";
24025
24298
  var MEDIA_DIR = "/tmp/maxy-media";
24026
24299
  function mimeToExt(mimetype) {
24027
24300
  const map2 = {
@@ -24077,47 +24350,47 @@ async function downloadInboundMedia(msg, sock, opts) {
24077
24350
  }
24078
24351
  );
24079
24352
  if (!buffer || buffer.length === 0) {
24080
- console.error(`${TAG5} primary download returned empty, trying direct fallback`);
24353
+ console.error(`${TAG6} primary download returned empty, trying direct fallback`);
24081
24354
  const downloadable = getDownloadableContent(content);
24082
24355
  if (downloadable) {
24083
24356
  try {
24084
24357
  const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
24085
24358
  buffer = await streamToBuffer(stream);
24086
24359
  } catch (fallbackErr) {
24087
- console.error(`${TAG5} direct download fallback failed: ${String(fallbackErr)}`);
24360
+ console.error(`${TAG6} direct download fallback failed: ${String(fallbackErr)}`);
24088
24361
  }
24089
24362
  }
24090
24363
  }
24091
24364
  if (!buffer || buffer.length === 0) {
24092
- console.error(`${TAG5} download failed: empty buffer for ${mimetype ?? "unknown"}`);
24365
+ console.error(`${TAG6} download failed: empty buffer for ${mimetype ?? "unknown"}`);
24093
24366
  return void 0;
24094
24367
  }
24095
24368
  if (buffer.length > maxBytes) {
24096
24369
  const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
24097
24370
  const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
24098
- console.error(`${TAG5} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
24371
+ console.error(`${TAG6} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
24099
24372
  return void 0;
24100
24373
  }
24101
24374
  await mkdir2(MEDIA_DIR, { recursive: true });
24102
24375
  const ext = mimeToExt(mimetype ?? "application/octet-stream");
24103
24376
  const filename = `${randomUUID6()}.${ext}`;
24104
- const filePath = join5(MEDIA_DIR, filename);
24377
+ const filePath = join6(MEDIA_DIR, filename);
24105
24378
  await writeFile2(filePath, buffer);
24106
24379
  const sizeKB = (buffer.length / 1024).toFixed(0);
24107
- console.error(`${TAG5} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
24380
+ console.error(`${TAG6} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
24108
24381
  return {
24109
24382
  path: filePath,
24110
24383
  mimetype: mimetype ?? "application/octet-stream",
24111
24384
  size: buffer.length
24112
24385
  };
24113
24386
  } catch (err) {
24114
- console.error(`${TAG5} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
24387
+ console.error(`${TAG6} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
24115
24388
  return void 0;
24116
24389
  }
24117
24390
  }
24118
24391
 
24119
24392
  // app/lib/whatsapp/inbound/debounce.ts
24120
- var TAG6 = "[whatsapp:debounce]";
24393
+ var TAG7 = "[whatsapp:debounce]";
24121
24394
  function createInboundDebouncer(opts) {
24122
24395
  const { debounceMs, buildKey, onFlush, onError } = opts;
24123
24396
  const pending = /* @__PURE__ */ new Map();
@@ -24129,7 +24402,7 @@ function createInboundDebouncer(opts) {
24129
24402
  pending.delete(key);
24130
24403
  const batchSize = batch.entries.length;
24131
24404
  try {
24132
- console.error(`${TAG6} debounce flush key=${key} batchSize=${batchSize}`);
24405
+ console.error(`${TAG7} debounce flush key=${key} batchSize=${batchSize}`);
24133
24406
  const result = onFlush(batch.entries);
24134
24407
  if (result && typeof result.catch === "function") {
24135
24408
  result.catch(onError);
@@ -24187,7 +24460,7 @@ function createInboundDebouncer(opts) {
24187
24460
  }
24188
24461
 
24189
24462
  // app/lib/whatsapp/manager.ts
24190
- var TAG7 = "[whatsapp:manager]";
24463
+ var TAG8 = "[whatsapp:manager]";
24191
24464
  var MAX_RECONNECT_ATTEMPTS = 10;
24192
24465
  var connections = /* @__PURE__ */ new Map();
24193
24466
  var configDir = null;
@@ -24196,7 +24469,7 @@ var onInboundMessage = null;
24196
24469
  var initialized = false;
24197
24470
  async function init(opts) {
24198
24471
  if (initialized) {
24199
- console.error(`${TAG7} already initialized`);
24472
+ console.error(`${TAG8} already initialized`);
24200
24473
  return;
24201
24474
  }
24202
24475
  configDir = opts.configDir;
@@ -24204,20 +24477,20 @@ async function init(opts) {
24204
24477
  loadConfig(opts.accountConfig);
24205
24478
  const accountIds = listCredentialAccountIds(configDir);
24206
24479
  if (accountIds.length === 0) {
24207
- console.error(`${TAG7} init: no stored WhatsApp credentials found`);
24480
+ console.error(`${TAG8} init: no stored WhatsApp credentials found`);
24208
24481
  initialized = true;
24209
24482
  return;
24210
24483
  }
24211
- console.error(`${TAG7} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
24484
+ console.error(`${TAG8} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
24212
24485
  initialized = true;
24213
24486
  for (const accountId of accountIds) {
24214
24487
  const accountCfg = whatsAppConfig.accounts?.[accountId];
24215
24488
  if (accountCfg?.enabled === false) {
24216
- console.error(`${TAG7} skipping disabled account=${accountId}`);
24489
+ console.error(`${TAG8} skipping disabled account=${accountId}`);
24217
24490
  continue;
24218
24491
  }
24219
24492
  startConnection(accountId).catch((err) => {
24220
- console.error(`${TAG7} failed to auto-start account=${accountId}: ${formatError(err)}`);
24493
+ console.error(`${TAG8} failed to auto-start account=${accountId}: ${formatError(err)}`);
24221
24494
  });
24222
24495
  }
24223
24496
  }
@@ -24226,7 +24499,7 @@ async function startConnection(accountId) {
24226
24499
  const authDir = resolveAuthDir(configDir, accountId);
24227
24500
  const hasAuth = await authExists(authDir);
24228
24501
  if (!hasAuth) {
24229
- console.error(`${TAG7} no credentials for account=${accountId}`);
24502
+ console.error(`${TAG8} no credentials for account=${accountId}`);
24230
24503
  return;
24231
24504
  }
24232
24505
  await stopConnection(accountId);
@@ -24261,11 +24534,11 @@ async function stopConnection(accountId) {
24261
24534
  conn.sock.ev.removeAllListeners("creds.update");
24262
24535
  conn.sock.ws?.close?.();
24263
24536
  } catch (err) {
24264
- console.warn(`${TAG7} socket cleanup error during stop account=${accountId}: ${String(err)}`);
24537
+ console.warn(`${TAG8} socket cleanup error during stop account=${accountId}: ${String(err)}`);
24265
24538
  }
24266
24539
  }
24267
24540
  connections.delete(accountId);
24268
- console.error(`${TAG7} stopped account=${accountId}`);
24541
+ console.error(`${TAG8} stopped account=${accountId}`);
24269
24542
  }
24270
24543
  function getStatus() {
24271
24544
  return Array.from(connections.values()).map((conn) => ({
@@ -24304,13 +24577,13 @@ async function registerLoginSocket(accountId, sock, authDir) {
24304
24577
  connections.set(accountId, conn);
24305
24578
  try {
24306
24579
  await sock.sendPresenceUpdate("available");
24307
- console.error(`${TAG7} presence set to available account=${accountId}`);
24580
+ console.error(`${TAG8} presence set to available account=${accountId}`);
24308
24581
  } catch (err) {
24309
- console.error(`${TAG7} presence update failed account=${accountId}: ${String(err)}`);
24582
+ console.error(`${TAG8} presence update failed account=${accountId}: ${String(err)}`);
24310
24583
  }
24311
24584
  monitorInbound(conn);
24312
24585
  watchForDisconnect(conn);
24313
- console.error(`${TAG7} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
24586
+ console.error(`${TAG8} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
24314
24587
  }
24315
24588
  function reloadConfig(accountConfig) {
24316
24589
  loadConfig(accountConfig);
@@ -24320,22 +24593,24 @@ async function shutdown() {
24320
24593
  const ids = Array.from(connections.keys());
24321
24594
  await Promise.all(ids.map((id) => stopConnection(id)));
24322
24595
  initialized = false;
24323
- console.error(`${TAG7} shutdown complete`);
24596
+ console.error(`${TAG8} shutdown complete`);
24324
24597
  }
24325
24598
  function loadConfig(accountConfig) {
24326
24599
  try {
24327
24600
  const raw2 = accountConfig?.whatsapp;
24328
24601
  if (raw2 && typeof raw2 === "object") {
24329
- const parsed = WhatsAppConfigSchema.safeParse(raw2);
24602
+ const wa = raw2;
24603
+ migrateAdminPhones(wa);
24604
+ const parsed = WhatsAppConfigSchema.safeParse(wa);
24330
24605
  if (parsed.success) {
24331
24606
  whatsAppConfig = parsed.data;
24332
24607
  } else {
24333
- console.error(`${TAG7} config validation failed: ${parsed.error.message}`);
24608
+ console.error(`${TAG8} config validation failed: ${parsed.error.message}`);
24334
24609
  whatsAppConfig = {};
24335
24610
  }
24336
24611
  }
24337
24612
  } catch (err) {
24338
- console.error(`${TAG7} config load error: ${String(err)}`);
24613
+ console.error(`${TAG8} config load error: ${String(err)}`);
24339
24614
  whatsAppConfig = {};
24340
24615
  }
24341
24616
  }
@@ -24343,13 +24618,13 @@ async function connectWithReconnect(conn) {
24343
24618
  const maxAttempts = MAX_RECONNECT_ATTEMPTS;
24344
24619
  while (!conn.abortController.signal.aborted) {
24345
24620
  try {
24346
- console.error(`${TAG7} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
24621
+ console.error(`${TAG8} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
24347
24622
  const sock = await createWaSocket({
24348
24623
  authDir: conn.authDir,
24349
24624
  silent: true
24350
24625
  });
24351
24626
  conn.sock = sock;
24352
- console.error(`${TAG7} socket created account=${conn.accountId} \u2014 waiting for connection`);
24627
+ console.error(`${TAG8} socket created account=${conn.accountId} \u2014 waiting for connection`);
24353
24628
  await waitForConnection(sock);
24354
24629
  const selfId = readSelfId(conn.authDir);
24355
24630
  conn.connected = true;
@@ -24359,12 +24634,12 @@ async function connectWithReconnect(conn) {
24359
24634
  conn.reconnectAttempts = 0;
24360
24635
  conn.lastError = void 0;
24361
24636
  conn.lidMapping = sock.signalRepository?.lidMapping ?? null;
24362
- console.error(`${TAG7} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
24637
+ console.error(`${TAG8} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
24363
24638
  try {
24364
24639
  await sock.sendPresenceUpdate("available");
24365
- console.error(`${TAG7} presence set to available account=${conn.accountId}`);
24640
+ console.error(`${TAG8} presence set to available account=${conn.accountId}`);
24366
24641
  } catch (err) {
24367
- console.error(`${TAG7} presence update failed account=${conn.accountId}: ${String(err)}`);
24642
+ console.error(`${TAG8} presence update failed account=${conn.accountId}: ${String(err)}`);
24368
24643
  }
24369
24644
  if (conn.debouncer) {
24370
24645
  conn.debouncer.destroy();
@@ -24379,19 +24654,19 @@ async function connectWithReconnect(conn) {
24379
24654
  conn.sock = null;
24380
24655
  const classification = classifyDisconnect(err);
24381
24656
  conn.lastError = classification.message;
24382
- console.error(`${TAG7} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
24657
+ console.error(`${TAG8} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
24383
24658
  if (!classification.shouldRetry) {
24384
- console.error(`${TAG7} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
24659
+ console.error(`${TAG8} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
24385
24660
  return;
24386
24661
  }
24387
24662
  conn.reconnectAttempts++;
24388
24663
  if (conn.reconnectAttempts > maxAttempts) {
24389
- console.error(`${TAG7} max retries exceeded for account=${conn.accountId}`);
24664
+ console.error(`${TAG8} max retries exceeded for account=${conn.accountId}`);
24390
24665
  conn.lastError = `Max reconnect attempts (${maxAttempts}) exceeded`;
24391
24666
  return;
24392
24667
  }
24393
24668
  const delay = computeBackoff(conn.reconnectAttempts);
24394
- console.error(`${TAG7} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${conn.reconnectAttempts}/${maxAttempts})`);
24669
+ console.error(`${TAG8} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${conn.reconnectAttempts}/${maxAttempts})`);
24395
24670
  await new Promise((resolve17) => {
24396
24671
  const timer = setTimeout(resolve17, delay);
24397
24672
  conn.abortController.signal.addEventListener("abort", () => {
@@ -24421,11 +24696,11 @@ function watchForDisconnect(conn) {
24421
24696
  conn.sock.ev.on("connection.update", (update) => {
24422
24697
  if (update.connection === "close") {
24423
24698
  if (connections.get(conn.accountId) !== conn) return;
24424
- console.error(`${TAG7} socket disconnected for account=${conn.accountId}`);
24699
+ console.error(`${TAG8} socket disconnected for account=${conn.accountId}`);
24425
24700
  conn.connected = false;
24426
24701
  conn.sock = null;
24427
24702
  connectWithReconnect(conn).catch((err) => {
24428
- console.error(`${TAG7} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
24703
+ console.error(`${TAG8} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
24429
24704
  });
24430
24705
  }
24431
24706
  });
@@ -24434,7 +24709,7 @@ function monitorInbound(conn) {
24434
24709
  if (!conn.sock || !onInboundMessage) return;
24435
24710
  const sock = conn.sock;
24436
24711
  const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
24437
- console.error(`${TAG7} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
24712
+ console.error(`${TAG8} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
24438
24713
  conn.debouncer = createInboundDebouncer({
24439
24714
  debounceMs,
24440
24715
  buildKey: (payload) => {
@@ -24447,7 +24722,7 @@ function monitorInbound(conn) {
24447
24722
  onInboundMessage(entries[0]);
24448
24723
  return;
24449
24724
  }
24450
- console.error(`${TAG7} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
24725
+ console.error(`${TAG8} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
24451
24726
  const last = entries[entries.length - 1];
24452
24727
  const mediaEntry = entries.find((e) => e.mediaPath);
24453
24728
  const combinedText = entries.map((e) => e.text).filter(Boolean).join("\n");
@@ -24460,7 +24735,7 @@ function monitorInbound(conn) {
24460
24735
  });
24461
24736
  },
24462
24737
  onError: (err) => {
24463
- console.error(`${TAG7} debounce flush error account=${conn.accountId}: ${String(err)}`);
24738
+ console.error(`${TAG8} debounce flush error account=${conn.accountId}: ${String(err)}`);
24464
24739
  }
24465
24740
  });
24466
24741
  sock.ev.on("messages.upsert", async (upsert) => {
@@ -24469,7 +24744,7 @@ function monitorInbound(conn) {
24469
24744
  try {
24470
24745
  await handleInboundMessage(conn, msg);
24471
24746
  } catch (err) {
24472
- console.error(`${TAG7} inbound handler error account=${conn.accountId}: ${String(err)}`);
24747
+ console.error(`${TAG8} inbound handler error account=${conn.accountId}: ${String(err)}`);
24473
24748
  }
24474
24749
  }
24475
24750
  });
@@ -24479,31 +24754,31 @@ async function handleInboundMessage(conn, msg) {
24479
24754
  const remoteJid = msg.key.remoteJid;
24480
24755
  if (!remoteJid) return;
24481
24756
  if (remoteJid === "status@broadcast") {
24482
- console.error(`${TAG7} drop: status broadcast account=${conn.accountId}`);
24757
+ console.error(`${TAG8} drop: status broadcast account=${conn.accountId}`);
24483
24758
  return;
24484
24759
  }
24485
24760
  if (!msg.message) {
24486
- console.error(`${TAG7} drop: empty message account=${conn.accountId} from=${remoteJid}`);
24761
+ console.error(`${TAG8} drop: empty message account=${conn.accountId} from=${remoteJid}`);
24487
24762
  return;
24488
24763
  }
24489
24764
  const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
24490
24765
  if (isDuplicateInbound(dedupKey)) {
24491
- console.error(`${TAG7} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
24766
+ console.error(`${TAG8} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
24492
24767
  return;
24493
24768
  }
24494
24769
  if (msg.key.fromMe) {
24495
24770
  if (msg.key.id && isAgentSentMessage(msg.key.id)) {
24496
- console.error(`${TAG7} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
24771
+ console.error(`${TAG8} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
24497
24772
  return;
24498
24773
  }
24499
24774
  const extracted2 = extractMessage(msg);
24500
24775
  if (!extracted2.text) {
24501
- console.error(`${TAG7} owner reply skipped \u2014 no text content account=${conn.accountId}`);
24776
+ console.error(`${TAG8} owner reply skipped \u2014 no text content account=${conn.accountId}`);
24502
24777
  return;
24503
24778
  }
24504
24779
  const isGroup2 = isGroupJid(remoteJid);
24505
24780
  const senderPhone2 = conn.selfPhone ?? "owner";
24506
- console.error(`${TAG7} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
24781
+ console.error(`${TAG8} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
24507
24782
  const reply2 = async (text) => {
24508
24783
  const currentSock = conn.sock;
24509
24784
  if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
@@ -24524,7 +24799,7 @@ async function handleInboundMessage(conn, msg) {
24524
24799
  }
24525
24800
  const extracted = extractMessage(msg);
24526
24801
  if (!extracted.text && !extracted.mediaType) {
24527
- console.error(`${TAG7} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
24802
+ console.error(`${TAG8} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
24528
24803
  return;
24529
24804
  }
24530
24805
  let mediaResult;
@@ -24534,7 +24809,7 @@ async function handleInboundMessage(conn, msg) {
24534
24809
  maxBytes: maxMb * 1024 * 1024
24535
24810
  });
24536
24811
  if (!mediaResult) {
24537
- console.error(`${TAG7} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
24812
+ console.error(`${TAG8} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
24538
24813
  }
24539
24814
  }
24540
24815
  const isGroup = isGroupJid(remoteJid);
@@ -24562,7 +24837,7 @@ async function handleInboundMessage(conn, msg) {
24562
24837
  });
24563
24838
  }
24564
24839
  console.error(
24565
- `${TAG7} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
24840
+ `${TAG8} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
24566
24841
  );
24567
24842
  if (!accessResult.allowed) return;
24568
24843
  const sendReceipts = whatsAppConfig.accounts?.[conn.accountId]?.sendReadReceipts ?? whatsAppConfig.sendReadReceipts ?? true;
@@ -24597,201 +24872,6 @@ async function handleInboundMessage(conn, msg) {
24597
24872
  }
24598
24873
  }
24599
24874
 
24600
- // app/lib/whatsapp/config-persist.ts
24601
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
24602
- import { resolve as resolve9, join as join6 } from "path";
24603
- var TAG8 = "[whatsapp:config]";
24604
- function configPath(accountDir) {
24605
- return resolve9(accountDir, "account.json");
24606
- }
24607
- function readConfig(accountDir) {
24608
- const path2 = configPath(accountDir);
24609
- if (!existsSync10(path2)) throw new Error(`account.json not found at ${path2}`);
24610
- return JSON.parse(readFileSync10(path2, "utf-8"));
24611
- }
24612
- function writeConfig(accountDir, config2) {
24613
- const path2 = configPath(accountDir);
24614
- writeFileSync6(path2, JSON.stringify(config2, null, 2) + "\n", "utf-8");
24615
- }
24616
- function reloadManagerConfig(accountDir) {
24617
- try {
24618
- const config2 = readConfig(accountDir);
24619
- reloadConfig(config2);
24620
- console.error(`${TAG8} reloaded manager config`);
24621
- } catch (err) {
24622
- console.error(`${TAG8} manager config reload failed: ${String(err)}`);
24623
- }
24624
- }
24625
- var E164_PATTERN = /^\+\d{7,15}$/;
24626
- function persistAfterPairing(accountDir, accountId, selfPhone) {
24627
- try {
24628
- const config2 = readConfig(accountDir);
24629
- if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
24630
- config2.whatsapp = {};
24631
- }
24632
- const wa = config2.whatsapp;
24633
- if (!wa.accounts || typeof wa.accounts !== "object") {
24634
- wa.accounts = {};
24635
- }
24636
- const accounts = wa.accounts;
24637
- if (!accounts[accountId] || typeof accounts[accountId] !== "object") {
24638
- accounts[accountId] = { name: "Main" };
24639
- }
24640
- if (selfPhone) {
24641
- const normalized = selfPhone.startsWith("+") ? selfPhone : `+${selfPhone}`;
24642
- if (!Array.isArray(wa.allowFrom)) {
24643
- wa.allowFrom = [];
24644
- }
24645
- const allowFrom = wa.allowFrom;
24646
- if (!allowFrom.includes(normalized)) {
24647
- allowFrom.push(normalized);
24648
- console.error(`${TAG8} added selfPhone=${normalized} to allowFrom`);
24649
- }
24650
- } else {
24651
- console.error(`${TAG8} skipping allowFrom \u2014 selfPhone is null account=${accountId}`);
24652
- }
24653
- const parsed = WhatsAppConfigSchema.safeParse(wa);
24654
- if (!parsed.success) {
24655
- const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
24656
- console.error(`${TAG8} validation failed after pairing: ${msg}`);
24657
- return { ok: false, error: `Validation failed: ${msg}` };
24658
- }
24659
- config2.whatsapp = parsed.data;
24660
- writeConfig(accountDir, config2);
24661
- console.error(`${TAG8} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
24662
- reloadManagerConfig(accountDir);
24663
- return { ok: true };
24664
- } catch (err) {
24665
- const msg = err instanceof Error ? err.message : String(err);
24666
- console.error(`${TAG8} persist failed account=${accountId}: ${msg}`);
24667
- return { ok: false, error: msg };
24668
- }
24669
- }
24670
- function addAdminPhone(accountDir, phone) {
24671
- const normalized = phone.trim();
24672
- if (!E164_PATTERN.test(normalized)) {
24673
- return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
24674
- }
24675
- try {
24676
- const config2 = readConfig(accountDir);
24677
- if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
24678
- config2.whatsapp = {};
24679
- }
24680
- const wa = config2.whatsapp;
24681
- if (!Array.isArray(wa.allowFrom)) {
24682
- wa.allowFrom = [];
24683
- }
24684
- const allowFrom = wa.allowFrom;
24685
- if (allowFrom.includes(normalized)) {
24686
- return { ok: true, message: `Phone ${normalized} is already in the admin list.` };
24687
- }
24688
- allowFrom.push(normalized);
24689
- const parsed = WhatsAppConfigSchema.safeParse(wa);
24690
- if (!parsed.success) {
24691
- const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
24692
- return { ok: false, error: `Validation failed: ${msg}` };
24693
- }
24694
- config2.whatsapp = parsed.data;
24695
- writeConfig(accountDir, config2);
24696
- console.error(`${TAG8} added admin phone=${normalized}`);
24697
- reloadManagerConfig(accountDir);
24698
- return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
24699
- } catch (err) {
24700
- const msg = err instanceof Error ? err.message : String(err);
24701
- console.error(`${TAG8} addAdminPhone failed: ${msg}`);
24702
- return { ok: false, error: msg };
24703
- }
24704
- }
24705
- function removeAdminPhone(accountDir, phone) {
24706
- const normalized = phone.trim();
24707
- try {
24708
- const config2 = readConfig(accountDir);
24709
- if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
24710
- return { ok: true, message: `No WhatsApp config exists \u2014 nothing to remove.` };
24711
- }
24712
- const wa = config2.whatsapp;
24713
- if (!Array.isArray(wa.allowFrom)) {
24714
- return { ok: true, message: `No admin phones configured \u2014 nothing to remove.` };
24715
- }
24716
- const allowFrom = wa.allowFrom;
24717
- const idx = allowFrom.indexOf(normalized);
24718
- if (idx === -1) {
24719
- return { ok: true, message: `Phone ${normalized} is not in the admin list.` };
24720
- }
24721
- allowFrom.splice(idx, 1);
24722
- const parsed = WhatsAppConfigSchema.safeParse(wa);
24723
- if (!parsed.success) {
24724
- const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
24725
- return { ok: false, error: `Validation failed: ${msg}` };
24726
- }
24727
- config2.whatsapp = parsed.data;
24728
- writeConfig(accountDir, config2);
24729
- console.error(`${TAG8} removed admin phone=${normalized}`);
24730
- reloadManagerConfig(accountDir);
24731
- return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
24732
- } catch (err) {
24733
- const msg = err instanceof Error ? err.message : String(err);
24734
- console.error(`${TAG8} removeAdminPhone failed: ${msg}`);
24735
- return { ok: false, error: msg };
24736
- }
24737
- }
24738
- function readAdminPhones(accountDir) {
24739
- try {
24740
- const config2 = readConfig(accountDir);
24741
- const wa = config2.whatsapp;
24742
- if (!wa || !Array.isArray(wa.allowFrom)) return [];
24743
- return wa.allowFrom.filter((p) => typeof p === "string");
24744
- } catch {
24745
- return [];
24746
- }
24747
- }
24748
- function setPublicAgent(accountDir, slug) {
24749
- const trimmed = slug.trim();
24750
- if (!trimmed) {
24751
- return { ok: false, error: "Agent slug cannot be empty." };
24752
- }
24753
- const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
24754
- if (!existsSync10(agentConfigPath)) {
24755
- return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
24756
- }
24757
- try {
24758
- const config2 = readConfig(accountDir);
24759
- if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
24760
- config2.whatsapp = {};
24761
- }
24762
- const wa = config2.whatsapp;
24763
- wa.publicAgent = trimmed;
24764
- const parsed = WhatsAppConfigSchema.safeParse(wa);
24765
- if (!parsed.success) {
24766
- const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
24767
- return { ok: false, error: `Validation failed: ${msg}` };
24768
- }
24769
- config2.whatsapp = parsed.data;
24770
- writeConfig(accountDir, config2);
24771
- console.error(`${TAG8} publicAgent set to ${trimmed}`);
24772
- reloadManagerConfig(accountDir);
24773
- return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
24774
- } catch (err) {
24775
- const msg = err instanceof Error ? err.message : String(err);
24776
- console.error(`${TAG8} setPublicAgent failed: ${msg}`);
24777
- return { ok: false, error: msg };
24778
- }
24779
- }
24780
- function getPublicAgent(accountDir) {
24781
- try {
24782
- const config2 = readConfig(accountDir);
24783
- const wa = config2.whatsapp;
24784
- if (!wa) return null;
24785
- const slug = wa.publicAgent;
24786
- if (typeof slug === "string" && slug.trim()) {
24787
- return slug.trim();
24788
- }
24789
- return null;
24790
- } catch {
24791
- return null;
24792
- }
24793
- }
24794
-
24795
24875
  // app/api/whatsapp/login/wait/route.ts
24796
24876
  async function POST10(req) {
24797
24877
  try {
@@ -24977,7 +25057,7 @@ function serializeWhatsAppSchema() {
24977
25057
  "",
24978
25058
  "## Constraints (superRefine)",
24979
25059
  "",
24980
- '- `dmPolicy: "open"` requires `allowFrom` to include `"*"` (both top-level and per-account)',
25060
+ '- `dmPolicy: "open"` requires `allowFrom` to include `"*"` (both top-level and per-account). This is a DM policy constraint \u2014 it does not affect admin phones, which live in the separate `adminPhones` field.',
24981
25061
  "- Account-level fields override top-level defaults when present"
24982
25062
  ].join("\n");
24983
25063
  return [topLevel, "", account, constraints].join("\n");
@@ -24987,7 +25067,7 @@ function serializeWhatsAppSchema() {
24987
25067
  async function POST14(req) {
24988
25068
  try {
24989
25069
  const body = await req.json().catch(() => ({}));
24990
- const { action, phone, slug, accountId } = body;
25070
+ const { action, phone, slug, fields, accountId } = body;
24991
25071
  if (!action || typeof action !== "string") {
24992
25072
  return Response.json({ ok: false, error: 'Missing required field "action".' }, { status: 400 });
24993
25073
  }
@@ -25056,9 +25136,23 @@ async function POST14(req) {
25056
25136
  return Response.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
25057
25137
  }
25058
25138
  }
25139
+ case "update-config": {
25140
+ if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
25141
+ return Response.json({ ok: false, error: 'Missing required field "fields" (object with config field names and values).' }, { status: 400 });
25142
+ }
25143
+ const result = updateConfig(account.accountDir, fields);
25144
+ const fieldNames = Object.keys(fields);
25145
+ console.error(`[whatsapp:api] config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
25146
+ return Response.json(result, { status: result.ok ? 200 : 400 });
25147
+ }
25148
+ case "get-config": {
25149
+ const waConfig = getConfig(account.accountDir);
25150
+ console.error(`[whatsapp:api] config action=get-config`);
25151
+ return Response.json({ ok: true, config: waConfig });
25152
+ }
25059
25153
  default:
25060
25154
  return Response.json(
25061
- { ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, schema, list-groups.` },
25155
+ { ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, update-config, get-config, schema, list-groups.` },
25062
25156
  { status: 400 }
25063
25157
  );
25064
25158
  }
@@ -127,14 +127,25 @@ server.tool("whatsapp-send", "Send a WhatsApp message to a phone number or group
127
127
  }
128
128
  });
129
129
  // ─── whatsapp-config ──────────────────────────────────────────────────
130
- server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admin-phone (register a phone as admin — messages from it route to admin agent), remove-admin-phone (demote to public routing), list-admin-phones (show current admin phones), set-public-agent (set which agent handles WhatsApp messages from non-admin phones — requires the agent slug), get-public-agent (show the currently configured public agent), schema (return all config field definitions — names, types, defaults, descriptions, constraints), list-groups (return WhatsApp groups the account belongs to — names, JIDs, participant counts). Phone numbers must be E.164 format (e.g. +441234567890).", {
131
- action: z.enum(["add-admin-phone", "remove-admin-phone", "list-admin-phones", "set-public-agent", "get-public-agent", "schema", "list-groups"]).describe("The config action to perform"),
130
+ server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admin-phone (register a phone as admin — messages from it route to admin agent), remove-admin-phone (demote to public routing), list-admin-phones (show current admin phones), set-public-agent (set which agent handles WhatsApp messages from non-admin phones — requires the agent slug), get-public-agent (show the currently configured public agent), update-config (change any WhatsApp config field — pass field names and values in the fields parameter), get-config (return the full current WhatsApp config object), schema (return all config field definitions — names, types, defaults, descriptions, constraints), list-groups (return WhatsApp groups the account belongs to — names, JIDs, participant counts). Phone numbers must be E.164 format (e.g. +441234567890).", {
131
+ action: z.enum(["add-admin-phone", "remove-admin-phone", "list-admin-phones", "set-public-agent", "get-public-agent", "update-config", "get-config", "schema", "list-groups"]).describe("The config action to perform"),
132
132
  phone: z.string().optional().describe("Phone number in E.164 format (required for add/remove admin phone)"),
133
133
  slug: z.string().optional().describe("Agent slug (required for set-public-agent)"),
134
+ fields: z.string().optional().describe('JSON string of config fields to update (required for update-config, e.g. \'{"dmPolicy":"open","allowFrom":["*"]}\')'),
134
135
  accountId: z.string().optional().describe('Account ID for list-groups (default: "default")'),
135
- }, async ({ action, phone, slug, accountId }) => {
136
+ }, async ({ action, phone, slug, fields, accountId }) => {
136
137
  try {
137
- const result = await callApi("/api/whatsapp/config", "POST", { action, phone, slug, accountId });
138
+ // Parse fields JSON string for update-config
139
+ let parsedFields;
140
+ if (action === "update-config" && fields) {
141
+ try {
142
+ parsedFields = JSON.parse(fields);
143
+ }
144
+ catch {
145
+ return textResult('Invalid JSON in "fields" parameter. Expected a JSON object, e.g. {"dmPolicy":"open","allowFrom":["*"]}', true);
146
+ }
147
+ }
148
+ const result = await callApi("/api/whatsapp/config", "POST", { action, phone, slug, fields: parsedFields, accountId });
138
149
  if (!result.ok)
139
150
  return textResult(result.error ?? "Config operation failed.", true);
140
151
  if (action === "list-admin-phones") {
@@ -152,6 +163,12 @@ server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admi
152
163
  if (action === "schema") {
153
164
  return textResult(result.text ?? "Schema unavailable.");
154
165
  }
166
+ if (action === "get-config") {
167
+ const waConfig = result.config;
168
+ if (!waConfig)
169
+ return textResult("No WhatsApp config found. Connect WhatsApp first.");
170
+ return textResult(JSON.stringify(waConfig, null, 2));
171
+ }
155
172
  if (action === "list-groups") {
156
173
  const groups = result.groups ?? [];
157
174
  if (groups.length === 0)
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;AAChD,IAAI,CAAC,aAAa,EAAE,CAAC;IACnB,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;AAChH,CAAC;AACD,MAAM,QAAQ,GAAG,oBAAoB,aAAa,EAAE,CAAC;AAErD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,KAAK,UAAU,OAAO,CAAC,IAAY,EAAE,SAAyB,MAAM,EAAE,IAAc;IAClF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;QAC5C,MAAM;QACN,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS;QAClE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9C,CAAC,CAAC;IACH,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,OAAO,GAAG,KAAK;IAC/C,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;QAC1C,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,qMAAqM,EACrM;IACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;IAC5E,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;CAClF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,2BAA2B,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAQ,CAAC;QAC/F,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC,uBAAuB,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAEjF,IAAI,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,QAAQ,IAAI,4FAA4F,CAAC;YACzG,QAAQ,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAAE,CAAC,EAAE,CAAC;QAC/H,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,QAAQ,IAAI,mBAAmB,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,uBAAuB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACrG,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,wIAAwI,EACxI;IACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;IAC5E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;CAClF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,0BAA0B,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAQ,CAAC;QAClG,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC,sBAAsB,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAEhF,IAAI,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACzC,QAAQ,IAAI,mBAAmB,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACpG,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,6IAA6I,EAC7I,EAAE,EACF,KAAK,IAAI,EAAE;IACT,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAQ,CAAC;QACnE,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC,wBAAwB,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAElF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;QACvC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,UAAU,CAAC,2EAA2E,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;YACpC,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC9D,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,MAAM,OAAO,GAAG,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9F,OAAO,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtG,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,iFAAiF,EACjF;IACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;CAC3F,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,0BAA0B,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,CAAQ,CAAC;QACvF,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC,sBAAsB,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAChF,OAAO,UAAU,CAAC,qBAAqB,MAAM,CAAC,SAAS,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACpG,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,CAAC,IAAI,CACT,eAAe,EACf,sHAAsH,EACtH;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;IACnE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACjD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;CACvF,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAQ,CAAC;QAC3F,IAAI,MAAM,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC,gBAAgB,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1E,OAAO,UAAU,CAAC,4BAA4B,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxG,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,gBAAgB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC,CACF,CAAC;AAEF,yEAAyE;AAEzE,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,moBAAmoB,EACnoB;IACE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IACxL,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;IAC3G,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;IAClF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;CAC7F,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,sBAAsB,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,CAAQ,CAAC;QACxG,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,IAAI,0BAA0B,EAAE,IAAI,CAAC,CAAC;QAEpF,IAAI,MAAM,KAAK,mBAAmB,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,UAAU,CAAC,kEAAkE,CAAC,CAAC;YAC/G,OAAO,UAAU,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC;QAED,IAAI,MAAM,KAAK,kBAAkB,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC;YAC9B,IAAI,CAAC,SAAS;gBAAE,OAAO,UAAU,CAAC,8EAA8E,CAAC,CAAC;YAClH,OAAO,UAAU,CAAC,0BAA0B,SAAS,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,qBAAqB,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,UAAU,CAAC,+FAA+F,CAAC,CAAC;YAC5I,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,gBAAgB,eAAe,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YAC/F,OAAO,UAAU,CAAC,oBAAoB,MAAM,CAAC,MAAM,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC,CACF,CAAC;AAEF,0EAA0E;AAE1E,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ The initial first-run wizard flow stays single-account. Multi-account is managed
30
30
  ```yaml
31
31
  channels:
32
32
  whatsapp:
33
- allowFrom:
33
+ adminPhones:
34
34
  - "<admin-phone>" # E.164 phone the user messages from
35
35
  accounts:
36
36
  default:
@@ -42,7 +42,7 @@ Accounts are linked via QR in the Channels UI. Outbound routing (`src/web/outbou
42
42
 
43
43
  | Target | Account | Reason |
44
44
  |--------|---------|--------|
45
- | Admin/allowFrom phones | Account with admin binding | Admin's registered number |
45
+ | Admin phones | Account with admin binding | Admin's registered number |
46
46
  | Self-chat | Account with selfChatMode | Admin talking to themselves |
47
47
  | Group messages | Account that is a group member | Whichever is in the group |
48
48
  | Customer DMs | Account with open DM policy | Dedicated agent number |
@@ -88,14 +88,14 @@ The platform enforces this at multiple levels:
88
88
  | Term | Definition | Code |
89
89
  |------|-----------|------|
90
90
  | **Self phone** | The WhatsApp account's own phone number (the phone that scanned the QR code). Self-chat only. | `phonesMatch(senderPhone, selfPhone)` in `access-control.ts` |
91
- | **Admin phones** | Phones with explicit non-wildcard entries in `allowFrom` in `account.json`. The user's personal phone must be registered here during setup it is not auto-registered on QR link. | `isAdminPhone()` in `access-control.ts` — iterates `allowFrom`, skips `"*"` entries |
92
- | **Public/unknown** | Any phone that is not self and not in `allowFrom` (non-wildcard). Subject to DM policy gating. | Everything else in `checkDmAccess()` |
91
+ | **Admin phones** | Phones listed in `adminPhones` in `account.json`. The user's personal phone is auto-registered on QR link; additional phones are added via `whatsapp-config action: "add-admin-phone"`. | `isAdminPhone()` in `access-control.ts` — iterates `adminPhones` |
92
+ | **Public/unknown** | Any phone that is not self and not in `adminPhones`. Subject to DM policy gating. | Everything else in `checkDmAccess()` |
93
93
 
94
- **Critical distinction:** The *self phone* is the paired device's number (the WhatsApp account Maxy controls). The *admin phone* is the user's personal phone (the phone they message *from*). These are typically different numbers. If the user's personal phone is not in `allowFrom`, their messages route as public — not admin.
94
+ **Critical distinction:** The *self phone* is the paired device's number (the WhatsApp account Maxy controls). The *admin phone* is the user's personal phone (the phone they message *from*). These are typically different numbers. If the user's personal phone is not in `adminPhones`, their messages route as public — not admin.
95
95
 
96
96
  **`dmPolicy` behaviour (per-account):**
97
97
 
98
- | Policy | Self phone | Admin phones (in `allowFrom`) | Public/unknown |
98
+ | Policy | Self phone | Admin phones (in `adminPhones`) | Public/unknown |
99
99
  |--------|-----------|-------------------------------|----------------|
100
100
  | `"open"` | Allowed | Allowed | Allowed |
101
101
  | `"allowlist"` | Allowed | Allowed | Allowed only if in `allowFrom` (wildcard `"*"` counts) |
@@ -107,7 +107,7 @@ Admin phones always bypass DM policy — the `isAdminPhone()` check runs before
107
107
 
108
108
  **Key file:** `access-control.ts` — `checkDmAccess()` and `checkGroupAccess()`.
109
109
 
110
- **Config path:** Per-account at `whatsapp.accounts.{accountId}.dmPolicy` in `account.json`. Falls back to `whatsapp.dmPolicy`. Changes are conversational — the admin agent writes to config via the `account-update` tool.
110
+ **Config path:** Per-account at `whatsapp.accounts.{accountId}.dmPolicy` in `account.json`. Falls back to `whatsapp.dmPolicy`. Changes are conversational — the admin agent writes to config via `whatsapp-config action: "update-config"`.
111
111
 
112
112
  For config field definitions (field names, types, valid values), use `whatsapp-config action: schema`.
113
113
 
@@ -68,7 +68,7 @@ Admin phone registration and public agent selection persist across relinks — n
68
68
  - **QR expired** — generate a new one with `whatsapp-login-start`
69
69
  - **Phone not scanning** — ensure the phone has internet, WhatsApp is updated, and the user is in Linked Devices
70
70
  - **Keeps disconnecting** — check `whatsapp-status` for error details. A 401 means re-link is needed. Repeated transient errors suggest network issues on the device.
71
- - **Messages routing as public** — the user's personal phone is not in `allowFrom`. Call `whatsapp-config` with `action: "add-admin-phone"` and the user's phone number to register it.
71
+ - **Messages routing as public** — the user's personal phone is not in `adminPhones`. Call `whatsapp-config` with `action: "add-admin-phone"` and the user's phone number to register it.
72
72
 
73
73
  ## Language
74
74
 
@@ -47,7 +47,7 @@ When the user wants to configure per-group settings:
47
47
 
48
48
  ## Writing changes
49
49
 
50
- After the user submits, write changes via `account-update` with `field: "whatsapp"` and the updated config object. Confirm what changed — name each setting that was modified and its new value.
50
+ After the user submits, write changes via `whatsapp-config action: "update-config"` with the `fields` parameter containing a JSON object of the changed fields. Each field is merged individually — unchanged fields are left untouched. Confirm what changed — name each setting that was modified and its new value.
51
51
 
52
52
  ## Language
53
53