@rubytech/create-maxy-code 0.1.283 → 0.1.285

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/__tests__/installer-settings-permissions.test.js +86 -8
  2. package/dist/index.js +1 -1
  3. package/dist/permissions-seed.js +72 -11
  4. package/package.json +1 -1
  5. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.js +3 -3
  6. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.js.map +1 -1
  7. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -1
  8. package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +28 -4
  9. package/payload/platform/plugins/cloudflare/references/api.md +2 -1
  10. package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
  11. package/payload/platform/plugins/docs/references/platform.md +2 -0
  12. package/payload/server/public/assets/AdminShell-DwPVr9K1.js +1 -0
  13. package/payload/server/public/assets/{Checkbox-syHoU9nY.js → Checkbox-BdDmGlvK.js} +1 -1
  14. package/payload/server/public/assets/{admin-DyuZ4NT5.js → admin-BYtGQJ0d.js} +1 -1
  15. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DsionCio.js → architectureDiagram-Q4EWVU46-CBx_GoUY.js} +1 -1
  16. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-NOG5yVZk.js → blockDiagram-DXYQGD6D-DvWR4fpv.js} +1 -1
  17. package/payload/server/public/assets/{browser-XrVAUQdG.js → browser-2PXsTraZ.js} +1 -1
  18. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-DwFV1k9I.js → c4Diagram-AHTNJAMY-BbSOxeeU.js} +1 -1
  19. package/payload/server/public/assets/channel-C4NqYS8u.js +1 -0
  20. package/payload/server/public/assets/{chunk-336JU56O-iJ4ISPCi.js → chunk-336JU56O-DJzmXTk5.js} +2 -2
  21. package/payload/server/public/assets/{chunk-426QAEUC-sWe0Iexz.js → chunk-426QAEUC-BSwzskM5.js} +1 -1
  22. package/payload/server/public/assets/{chunk-4TB4RGXK-kKNYi2_i.js → chunk-4TB4RGXK-C-fH8BTZ.js} +1 -1
  23. package/payload/server/public/assets/{chunk-5FUZZQ4R-Car3r0he.js → chunk-5FUZZQ4R-DW-MaiGX.js} +1 -1
  24. package/payload/server/public/assets/{chunk-5PVQY5BW-DR9EfLvj.js → chunk-5PVQY5BW-DlKbwC1X.js} +1 -1
  25. package/payload/server/public/assets/{chunk-EDXVE4YY-DLUWVRZV.js → chunk-EDXVE4YY-DnkdxwAt.js} +1 -1
  26. package/payload/server/public/assets/{chunk-ENJZ2VHE-B4bNJo6C.js → chunk-ENJZ2VHE-BTfezWsj.js} +1 -1
  27. package/payload/server/public/assets/{chunk-ICPOFSXX-Qzl8Ki0l.js → chunk-ICPOFSXX-BGg8uTtu.js} +1 -1
  28. package/payload/server/public/assets/{chunk-OYMX7WX6-BenBO6O_.js → chunk-OYMX7WX6-CYJInav8.js} +1 -1
  29. package/payload/server/public/assets/{chunk-U2HBQHQK-DewM8R0D.js → chunk-U2HBQHQK-B6kD6ZHU.js} +1 -1
  30. package/payload/server/public/assets/{chunk-X2U36JSP-C22Q6Ea4.js → chunk-X2U36JSP-7tvWkjym.js} +1 -1
  31. package/payload/server/public/assets/{chunk-YZCP3GAM-DfUUNTnA.js → chunk-YZCP3GAM-D6NjOb5l.js} +1 -1
  32. package/payload/server/public/assets/{chunk-ZZ45TVLE-CUT6zma2.js → chunk-ZZ45TVLE-Dqz69eJy.js} +1 -1
  33. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CI3_kz04.js +1 -0
  34. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-UP3RcKH9.js +1 -0
  35. package/payload/server/public/assets/clone-CvAPweMB.js +1 -0
  36. package/payload/server/public/assets/{dagre-KV5264BT-D4EGb60h.js → dagre-KV5264BT-DswETLwm.js} +1 -1
  37. package/payload/server/public/assets/{dagre-CYXRIdv0.js → dagre-di-KHpsq.js} +1 -1
  38. package/payload/server/public/assets/{data-tcu3MXmK.js → data-B6vpe0Zj.js} +1 -1
  39. package/payload/server/public/assets/{diagram-5BDNPKRD-BzieCEfT.js → diagram-5BDNPKRD-DWmq_Zkl.js} +1 -1
  40. package/payload/server/public/assets/{diagram-G4DWMVQ6-DDbO71XD.js → diagram-G4DWMVQ6-BVi6yjLL.js} +1 -1
  41. package/payload/server/public/assets/{diagram-MMDJMWI5-DqQ_UXDA.js → diagram-MMDJMWI5-D6XKyUAT.js} +1 -1
  42. package/payload/server/public/assets/{diagram-TYMM5635-CFyxA1aZ.js → diagram-TYMM5635-sU1d7P6W.js} +1 -1
  43. package/payload/server/public/assets/{erDiagram-SMLLAGMA-BMaEBbOK.js → erDiagram-SMLLAGMA-CY6qPZvu.js} +1 -1
  44. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-De5ChZQP.js → flowDiagram-DWJPFMVM-CE9hd869.js} +1 -1
  45. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CXEMPVaK.js → ganttDiagram-T4ZO3ILL-Cbb885gt.js} +1 -1
  46. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-OZedr_6y.js → gitGraphDiagram-UUTBAWPF-BNzO3jBu.js} +1 -1
  47. package/payload/server/public/assets/{graph-I4GyC2MW.js → graph-DfegJdnT.js} +1 -1
  48. package/payload/server/public/assets/{graph-labels-DSDTSBfM.js → graph-labels-BG4aqzs6.js} +1 -1
  49. package/payload/server/public/assets/{graphlib-CN4nhD2U.js → graphlib-CIzKPoW_.js} +1 -1
  50. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CSqg4nRL.js → infoDiagram-42DDH7IO-DWGtofAT.js} +1 -1
  51. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-ChlzrZyx.js → ishikawaDiagram-UXIWVN3A-UG1rus6G.js} +1 -1
  52. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DsrK55ZO.js → journeyDiagram-VCZTEJTY-CYhHYXmj.js} +1 -1
  53. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DYF-_Rym.js → kanban-definition-6JOO6SKY-CWHj9DHX.js} +1 -1
  54. package/payload/server/public/assets/{line-3zmwI8XK.js → line-vMN9CmXx.js} +1 -1
  55. package/payload/server/public/assets/{mermaid-parser.core-BrcX8-cl.js → mermaid-parser.core-5X7WWCZe.js} +1 -1
  56. package/payload/server/public/assets/{mermaid.core-CBdz3b1N.js → mermaid.core-B0zUZsT6.js} +3 -3
  57. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BqCqlYZb.js → mindmap-definition-QFDTVHPH-BGr68ynR.js} +1 -1
  58. package/payload/server/public/assets/{pieDiagram-DEJITSTG-D5LiILuM.js → pieDiagram-DEJITSTG-Bo3zU_w2.js} +1 -1
  59. package/payload/server/public/assets/{public-udi_Z7la.js → public-DCZ92LR9.js} +3 -3
  60. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-C3iuI5l_.js → quadrantDiagram-34T5L4WZ-o-dLMRj-.js} +1 -1
  61. package/payload/server/public/assets/{requirementDiagram-MS252O5E-CTPOw5qQ.js → requirementDiagram-MS252O5E-D_O_ZclX.js} +1 -1
  62. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-D_5aVlYp.js → sankeyDiagram-XADWPNL6-Doz7uWIn.js} +1 -1
  63. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-D7h5vocM.js → sequenceDiagram-FGHM5R23-r1VW_jgc.js} +1 -1
  64. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DyZjuwPj.js → stateDiagram-FHFEXIEX-B7O06aua.js} +1 -1
  65. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bo1g66pA.js +1 -0
  66. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BqpVYJz_.js → timeline-definition-GMOUNBTQ-WS2AL0yL.js} +1 -1
  67. package/payload/server/public/assets/{useSelectionMode-BnRcJeg2.css → useSelectionMode-lFraIQzT.css} +1 -1
  68. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Czflml0e.js → vennDiagram-DHZGUBPP-_-FLcVAh.js} +1 -1
  69. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Cqz3pSTK.js → wardleyDiagram-NUSXRM2D-nZ3oO0ih.js} +1 -1
  70. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CS5dEuUO.js → xychartDiagram-5P7HB3ND-CqTdRM37.js} +1 -1
  71. package/payload/server/public/browser.html +4 -4
  72. package/payload/server/public/data.html +5 -5
  73. package/payload/server/public/graph.html +6 -6
  74. package/payload/server/public/index.html +5 -5
  75. package/payload/server/public/public.html +4 -4
  76. package/payload/server/server.js +236 -155
  77. package/payload/server/public/assets/AdminShell-8Y7-maNi.js +0 -1
  78. package/payload/server/public/assets/channel-D8Go_zJk.js +0 -1
  79. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BLb1mzrT.js +0 -1
  80. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Ctj5qYTS.js +0 -1
  81. package/payload/server/public/assets/clone-DGZ2vydA.js +0 -1
  82. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BiW9pGXy.js +0 -1
  83. /package/payload/server/public/assets/{useSelectionMode-BPzPjKP_.js → useSelectionMode-mzxTOyBv.js} +0 -0
@@ -6861,8 +6861,8 @@ app3.get("/group-info", async (c) => {
6861
6861
  var whatsapp_default = app3;
6862
6862
 
6863
6863
  // server/routes/whatsapp-reader.ts
6864
- import { readFileSync as readFileSync9, watch, statSync as statSync3, openSync, readSync, closeSync } from "fs";
6865
- import { basename as basename2, dirname, join as join8, resolve as resolve9, sep as sep2 } from "path";
6864
+ import { readFileSync as readFileSync10, watch, statSync as statSync4, openSync, readSync, closeSync } from "fs";
6865
+ import { basename as basename2, dirname, join as join9, resolve as resolve9, sep as sep2 } from "path";
6866
6866
 
6867
6867
  // server/routes/admin/sidebar-sessions.ts
6868
6868
  import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
@@ -7118,16 +7118,115 @@ app4.get("/", requireAdminSession, async (c) => {
7118
7118
  var sidebar_sessions_default = app4;
7119
7119
 
7120
7120
  // app/lib/whatsapp-reader/select-sessions.ts
7121
- function selectAdminWhatsappSessions(rows) {
7122
- return rows.filter((r) => r.role === "admin" && r.channel === "whatsapp").sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7121
+ function selectAdminChannelSessions(rows) {
7122
+ return rows.filter((r) => r.role === "admin" && (r.channel === "whatsapp" || r.channel === "telegram")).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7123
7123
  sessionId: r.sessionId,
7124
7124
  projectDir: r.projectDir,
7125
7125
  title: r.title,
7126
7126
  senderId: r.senderId,
7127
- startedAt: r.startedAt
7127
+ startedAt: r.startedAt,
7128
+ channel: r.channel,
7129
+ operatorName: null
7128
7130
  }));
7129
7131
  }
7130
7132
 
7133
+ // app/lib/admin-identity/pin-validator.ts
7134
+ import { createHash as createHash2 } from "crypto";
7135
+ import { existsSync as existsSync6, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
7136
+ import { join as join8 } from "path";
7137
+ function hashPin(pin) {
7138
+ return createHash2("sha256").update(pin).digest("hex");
7139
+ }
7140
+ function readUsersFile(usersFilePath) {
7141
+ if (!existsSync6(usersFilePath)) return null;
7142
+ const raw = readFileSync9(usersFilePath, "utf-8").trim();
7143
+ if (!raw) return [];
7144
+ return JSON.parse(raw);
7145
+ }
7146
+ function validatePin(pin, usersFilePath) {
7147
+ let users;
7148
+ try {
7149
+ users = readUsersFile(usersFilePath);
7150
+ } catch (err) {
7151
+ return { kind: "corrupt", message: err instanceof Error ? err.message : String(err) };
7152
+ }
7153
+ if (users === null || users.length === 0) return { kind: "unavailable" };
7154
+ const inputHash = hashPin(pin);
7155
+ const matched = users.find((u) => u.pin === inputHash);
7156
+ return matched ? { kind: "match", userId: matched.userId } : { kind: "miss" };
7157
+ }
7158
+ function scanForOrphanedAccountAdmins(users, accountsDir) {
7159
+ const known = new Set(users.map((u) => u.userId));
7160
+ const orphans = [];
7161
+ if (!existsSync6(accountsDir)) return orphans;
7162
+ let entries;
7163
+ try {
7164
+ entries = readdirSync5(accountsDir);
7165
+ } catch {
7166
+ return orphans;
7167
+ }
7168
+ for (const entry of entries) {
7169
+ if (entry.startsWith(".")) continue;
7170
+ const accountDir = join8(accountsDir, entry);
7171
+ try {
7172
+ if (!statSync3(accountDir).isDirectory()) continue;
7173
+ } catch {
7174
+ continue;
7175
+ }
7176
+ const accountJsonPath = join8(accountDir, "account.json");
7177
+ if (!existsSync6(accountJsonPath)) continue;
7178
+ try {
7179
+ const config = JSON.parse(readFileSync9(accountJsonPath, "utf-8"));
7180
+ const admins = config.admins ?? [];
7181
+ for (const a of admins) {
7182
+ if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
7183
+ }
7184
+ } catch {
7185
+ continue;
7186
+ }
7187
+ }
7188
+ return orphans;
7189
+ }
7190
+
7191
+ // app/lib/whatsapp/inbound/resolve-admin-userid.ts
7192
+ function classifyAdminUserId(users, admins, senderPhone) {
7193
+ const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
7194
+ if (!matched) return { kind: "no-users-binding" };
7195
+ return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
7196
+ }
7197
+ function pickAdminUserId(users, admins, senderPhone) {
7198
+ const r = classifyAdminUserId(users, admins, senderPhone);
7199
+ return r.kind === "resolved" ? r.userId : null;
7200
+ }
7201
+ function resolveAdminUserId(params) {
7202
+ try {
7203
+ const users = readUsersFile(USERS_FILE);
7204
+ if (!users) return null;
7205
+ const account = listValidAccounts().find((a) => a.accountId === params.accountId);
7206
+ const admins = account?.config.admins ?? [];
7207
+ const direct = pickAdminUserId(users, admins, params.senderPhone);
7208
+ if (direct) return direct;
7209
+ const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
7210
+ if (!mapped) return null;
7211
+ const viaLid = pickAdminUserId(users, admins, mapped);
7212
+ if (viaLid) {
7213
+ console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
7214
+ }
7215
+ return viaLid;
7216
+ } catch {
7217
+ return null;
7218
+ }
7219
+ }
7220
+
7221
+ // app/lib/whatsapp-reader/resolve-operator-name.ts
7222
+ function resolveOperatorDisplay(userIdRes, nameRes) {
7223
+ if (userIdRes.kind === "no-users-binding") return { name: null, reason: "no-binding" };
7224
+ if (userIdRes.kind === "userid-not-admin") return { name: null, reason: "not-admin" };
7225
+ if (nameRes && nameRes.source === "neo4j") return { name: nameRes.joined, reason: null };
7226
+ if (nameRes && nameRes.reason === "neo4j-unreachable") return { name: null, reason: "neo4j-unreachable" };
7227
+ return { name: null, reason: "no-person-name" };
7228
+ }
7229
+
7131
7230
  // app/lib/whatsapp-reader/parse-transcript.ts
7132
7231
  var CLI_MARKER_PREFIXES2 = ["<local-command-", "<command-name>", "<command-message>"];
7133
7232
  var CHANNEL_WRAPPER = /^<channel\b[^>]*>\n?([\s\S]*?)\n?<\/channel>$/;
@@ -7227,17 +7326,17 @@ var app5 = new Hono();
7227
7326
  app5.get("/conversations", requireAdminSession, async (c) => {
7228
7327
  const cfg = claudeConfigDir();
7229
7328
  if (!cfg) return c.json({ conversations: [] });
7230
- const projectsRoot = join8(cfg, "projects");
7329
+ const projectsRoot = join9(cfg, "projects");
7231
7330
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
7232
7331
  const rows = [];
7233
7332
  for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
7234
7333
  const sessionId = basename2(path2).replace(/\.jsonl$/, "");
7235
7334
  const projectDir = dirname(path2);
7236
- const meta = readSidecarMeta(join8(projectDir, `${sessionId}.meta.json`));
7237
- if (meta.role !== "admin" || meta.channel !== "whatsapp") continue;
7335
+ const meta = readSidecarMeta(join9(projectDir, `${sessionId}.meta.json`));
7336
+ if (meta.role !== "admin" || meta.channel !== "whatsapp" && meta.channel !== "telegram") continue;
7238
7337
  let body = "";
7239
7338
  try {
7240
- body = readFileSync9(path2, "utf8");
7339
+ body = readFileSync10(path2, "utf8");
7241
7340
  } catch {
7242
7341
  }
7243
7342
  const { title } = resolveTitle(body, sessionId, userTitles);
@@ -7251,10 +7350,41 @@ app5.get("/conversations", requireAdminSession, async (c) => {
7251
7350
  senderId: meta.senderId
7252
7351
  });
7253
7352
  }
7254
- return c.json({ conversations: selectAdminWhatsappSessions(rows) });
7353
+ const selected = selectAdminChannelSessions(rows);
7354
+ const account = resolveAccount();
7355
+ let users = [];
7356
+ if (account) {
7357
+ try {
7358
+ users = readUsersFile(USERS_FILE) ?? [];
7359
+ } catch (err) {
7360
+ console.error(`[wa-reader] op=users-file-unreadable reason=${err instanceof Error ? err.message : String(err)}`);
7361
+ users = [];
7362
+ }
7363
+ }
7364
+ const admins = account?.config.admins ?? [];
7365
+ const nameBySender = /* @__PURE__ */ new Map();
7366
+ const distinct = [...new Set(selected.map((r) => r.senderId).filter((s) => !!s))];
7367
+ await Promise.all(
7368
+ distinct.map(async (senderId) => {
7369
+ if (!account) {
7370
+ nameBySender.set(senderId, null);
7371
+ return;
7372
+ }
7373
+ const idRes = classifyAdminUserId(users, admins, senderId);
7374
+ const nameRes = idRes.kind === "resolved" ? await loadAdminUserName(account.accountId, idRes.userId) : null;
7375
+ const { name, reason } = resolveOperatorDisplay(idRes, nameRes);
7376
+ if (reason) console.error(`[wa-reader] op=operator-unresolved senderId=${senderId} reason=${reason}`);
7377
+ nameBySender.set(senderId, name);
7378
+ })
7379
+ );
7380
+ const conversations = selected.map((r) => ({
7381
+ ...r,
7382
+ operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
7383
+ }));
7384
+ return c.json({ conversations });
7255
7385
  });
7256
7386
  function readFrom(path2, from) {
7257
- const end = statSync3(path2).size;
7387
+ const end = statSync4(path2).size;
7258
7388
  if (end <= from) return { buf: Buffer.alloc(0), end: from };
7259
7389
  const fd = openSync(path2, "r");
7260
7390
  try {
@@ -7265,41 +7395,73 @@ function readFrom(path2, from) {
7265
7395
  closeSync(fd);
7266
7396
  }
7267
7397
  }
7398
+ function resumeOffset(lastEventId, fileEnd) {
7399
+ if (!lastEventId) return 0;
7400
+ const n = Number(lastEventId);
7401
+ if (!Number.isFinite(n) || n < 0) return 0;
7402
+ return Math.min(n, fileEnd);
7403
+ }
7268
7404
  app5.get("/stream", requireAdminSession, (c) => {
7269
7405
  const sessionId = c.req.query("sessionId") ?? "";
7270
7406
  const projectDir = c.req.query("projectDir") ?? "";
7271
7407
  const cfg = claudeConfigDir();
7272
- const projectsRoot = cfg ? resolve9(join8(cfg, "projects")) : null;
7408
+ const projectsRoot = cfg ? resolve9(join9(cfg, "projects")) : null;
7273
7409
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
7274
7410
  return c.json({ error: "bad session reference" }, 400);
7275
7411
  }
7276
- const jsonlPath = resolve9(join8(projectDir, `${sessionId}.jsonl`));
7412
+ const jsonlPath = resolve9(join9(projectDir, `${sessionId}.jsonl`));
7277
7413
  if (!jsonlPath.startsWith(projectsRoot + sep2)) {
7278
7414
  return c.json({ error: "bad session reference" }, 400);
7279
7415
  }
7280
7416
  const encoder = new TextEncoder();
7281
- const send = (controller, turn) => controller.enqueue(encoder.encode(`data: ${JSON.stringify(turn)}
7417
+ const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
7418
+ data: ${JSON.stringify(turn)}
7282
7419
 
7283
7420
  `));
7421
+ const connId = `${sessionId.slice(0, 8)}-${process.hrtime.bigint().toString(36).slice(-6)}`;
7422
+ const startedAt = Date.now();
7423
+ const lastEventId = c.req.header("Last-Event-ID");
7284
7424
  const readable = new ReadableStream({
7285
7425
  start(controller) {
7286
- let offset = 0;
7426
+ const fileEnd0 = (() => {
7427
+ try {
7428
+ return statSync4(jsonlPath).size;
7429
+ } catch {
7430
+ return 0;
7431
+ }
7432
+ })();
7433
+ let offset = resumeOffset(lastEventId, fileEnd0);
7434
+ console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
7287
7435
  const drain = () => {
7288
7436
  try {
7289
7437
  const { buf, end } = readFrom(jsonlPath, offset);
7290
7438
  if (end <= offset) return;
7291
7439
  const { lines, nextOffset } = splitNewLines(buf, offset);
7292
7440
  offset = nextOffset;
7293
- for (const turn of parseTranscript(lines)) send(controller, turn);
7441
+ let n = 0;
7442
+ for (const turn of parseTranscript(lines)) {
7443
+ send(controller, turn, offset);
7444
+ n++;
7445
+ }
7446
+ if (n > 0) console.log(`[wa-stream] op=drain conn=${connId} turns=${n}`);
7294
7447
  } catch {
7295
7448
  }
7296
7449
  };
7297
- try {
7298
- const { buf, end } = readFrom(jsonlPath, 0);
7299
- offset = end;
7300
- for (const turn of parseTranscript(buf.toString("utf8").split("\n"))) send(controller, turn);
7301
- } catch {
7302
- controller.enqueue(encoder.encode('event: error\ndata: "read-failed"\n\n'));
7450
+ if (offset === 0) {
7451
+ try {
7452
+ const { buf, end } = readFrom(jsonlPath, 0);
7453
+ offset = end;
7454
+ let n = 0;
7455
+ for (const turn of parseTranscript(buf.toString("utf8").split("\n"))) {
7456
+ send(controller, turn, offset);
7457
+ n++;
7458
+ }
7459
+ console.log(`[wa-stream] op=backlog conn=${connId} turns=${n} bytes=${end}`);
7460
+ } catch {
7461
+ controller.enqueue(encoder.encode('event: error\ndata: "read-failed"\n\n'));
7462
+ }
7463
+ } else {
7464
+ drain();
7303
7465
  }
7304
7466
  let watcher = null;
7305
7467
  try {
@@ -7307,9 +7469,18 @@ app5.get("/stream", requireAdminSession, (c) => {
7307
7469
  } catch {
7308
7470
  }
7309
7471
  const poll = setInterval(drain, 1e3);
7472
+ const heartbeat = setInterval(() => {
7473
+ try {
7474
+ controller.enqueue(encoder.encode(": ping\n\n"));
7475
+ console.log(`[wa-stream] op=heartbeat conn=${connId}`);
7476
+ } catch {
7477
+ }
7478
+ }, 2e4);
7310
7479
  c.req.raw.signal.addEventListener("abort", () => {
7311
7480
  watcher?.close();
7312
7481
  clearInterval(poll);
7482
+ clearInterval(heartbeat);
7483
+ console.log(`[wa-stream] op=close conn=${connId} ranMs=${Date.now() - startedAt} reason=client-abort`);
7313
7484
  try {
7314
7485
  controller.close();
7315
7486
  } catch {
@@ -7325,8 +7496,8 @@ var whatsapp_reader_default = app5;
7325
7496
 
7326
7497
  // server/routes/telegram.ts
7327
7498
  import { homedir } from "os";
7328
- import { resolve as resolve10, join as join9 } from "path";
7329
- import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
7499
+ import { resolve as resolve10, join as join10 } from "path";
7500
+ import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
7330
7501
 
7331
7502
  // app/lib/telegram/access-control.ts
7332
7503
  function checkTelegramAccess(params) {
@@ -7377,10 +7548,10 @@ var TAG21 = "[telegram-inbound]";
7377
7548
  var DISPATCH_TIMEOUT_MS = 12e4;
7378
7549
  function configDirName() {
7379
7550
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve10(process.cwd(), "..");
7380
- const brandPath = join9(platformRoot2, "config", "brand.json");
7381
- if (existsSync6(brandPath)) {
7551
+ const brandPath = join10(platformRoot2, "config", "brand.json");
7552
+ if (existsSync7(brandPath)) {
7382
7553
  try {
7383
- return JSON.parse(readFileSync10(brandPath, "utf-8")).configDir ?? ".maxy";
7554
+ return JSON.parse(readFileSync11(brandPath, "utf-8")).configDir ?? ".maxy";
7384
7555
  } catch {
7385
7556
  }
7386
7557
  }
@@ -7412,11 +7583,11 @@ app6.post("/", async (c) => {
7412
7583
  return c.json({ ok: false }, 400);
7413
7584
  }
7414
7585
  const sp = secretPath(botType);
7415
- if (!existsSync6(sp)) {
7586
+ if (!existsSync7(sp)) {
7416
7587
  console.error(`${TAG21} op=reject reason=no-secret-file botType=${botType}`);
7417
7588
  return c.json({ ok: false }, 401);
7418
7589
  }
7419
- const expected = readFileSync10(sp, "utf-8").trim();
7590
+ const expected = readFileSync11(sp, "utf-8").trim();
7420
7591
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
7421
7592
  if (got !== expected) {
7422
7593
  console.error(`${TAG21} op=reject reason=bad-secret botType=${botType}`);
@@ -7479,12 +7650,12 @@ var telegram_default = app6;
7479
7650
 
7480
7651
  // server/routes/onboarding.ts
7481
7652
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
7482
- import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync8, readFileSync as readFileSync12, unlinkSync } from "fs";
7483
- import { createHash as createHash2, randomUUID as randomUUID7 } from "crypto";
7653
+ import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync9, readFileSync as readFileSync13, unlinkSync } from "fs";
7654
+ import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
7484
7655
 
7485
7656
  // ../lib/admins-write/src/index.ts
7486
- import { existsSync as existsSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
7487
- import { dirname as dirname2, join as join10 } from "path";
7657
+ import { existsSync as existsSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7658
+ import { dirname as dirname2, join as join11 } from "path";
7488
7659
  function logLine(input, result) {
7489
7660
  const userIdShort = input.userId.slice(0, 8);
7490
7661
  console.error(
@@ -7501,8 +7672,8 @@ function writeAdminEntry(input) {
7501
7672
  const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
7502
7673
  try {
7503
7674
  let users = [];
7504
- if (existsSync7(input.usersFile)) {
7505
- const raw = readFileSync11(input.usersFile, "utf-8").trim();
7675
+ if (existsSync8(input.usersFile)) {
7676
+ const raw = readFileSync12(input.usersFile, "utf-8").trim();
7506
7677
  if (raw) {
7507
7678
  const parsed = JSON.parse(raw);
7508
7679
  if (!Array.isArray(parsed)) {
@@ -7526,11 +7697,11 @@ function writeAdminEntry(input) {
7526
7697
  return result;
7527
7698
  }
7528
7699
  try {
7529
- const accountJsonPath = join10(input.accountDir, "account.json");
7530
- if (!existsSync7(accountJsonPath)) {
7700
+ const accountJsonPath = join11(input.accountDir, "account.json");
7701
+ if (!existsSync8(accountJsonPath)) {
7531
7702
  throw new Error(`account.json not found at ${accountJsonPath}`);
7532
7703
  }
7533
- const config = JSON.parse(readFileSync11(accountJsonPath, "utf-8"));
7704
+ const config = JSON.parse(readFileSync12(accountJsonPath, "utf-8"));
7534
7705
  const admins = config.admins ?? [];
7535
7706
  if (admins.some((a) => a.userId === input.userId)) {
7536
7707
  result.accountJsonResult = "noop";
@@ -7554,9 +7725,9 @@ function checkAdminAuthInvariant(input) {
7554
7725
  usersWithoutAccount: []
7555
7726
  };
7556
7727
  const usersUserIds = /* @__PURE__ */ new Set();
7557
- if (existsSync7(input.usersFile)) {
7728
+ if (existsSync8(input.usersFile)) {
7558
7729
  try {
7559
- const raw = readFileSync11(input.usersFile, "utf-8").trim();
7730
+ const raw = readFileSync12(input.usersFile, "utf-8").trim();
7560
7731
  if (raw) {
7561
7732
  const users = JSON.parse(raw);
7562
7733
  for (const u of users) {
@@ -7569,10 +7740,10 @@ function checkAdminAuthInvariant(input) {
7569
7740
  }
7570
7741
  }
7571
7742
  const accountUserIds = /* @__PURE__ */ new Set();
7572
- if (existsSync7(input.accountsDir)) {
7743
+ if (existsSync8(input.accountsDir)) {
7573
7744
  let entries;
7574
7745
  try {
7575
- entries = readdirSync5(input.accountsDir);
7746
+ entries = readdirSync6(input.accountsDir);
7576
7747
  } catch (err) {
7577
7748
  const msg = err instanceof Error ? err.message : String(err);
7578
7749
  console.error(`[${input.tag}] accounts-dir unreadable accountsDir=${input.accountsDir} error=${msg}`);
@@ -7581,17 +7752,17 @@ function checkAdminAuthInvariant(input) {
7581
7752
  }
7582
7753
  for (const entry of entries) {
7583
7754
  if (entry.startsWith(".")) continue;
7584
- const accountDir = join10(input.accountsDir, entry);
7755
+ const accountDir = join11(input.accountsDir, entry);
7585
7756
  try {
7586
- if (!statSync4(accountDir).isDirectory()) continue;
7757
+ if (!statSync5(accountDir).isDirectory()) continue;
7587
7758
  } catch {
7588
7759
  continue;
7589
7760
  }
7590
- const accountJsonPath = join10(accountDir, "account.json");
7591
- if (!existsSync7(accountJsonPath)) continue;
7761
+ const accountJsonPath = join11(accountDir, "account.json");
7762
+ if (!existsSync8(accountJsonPath)) continue;
7592
7763
  let admins = [];
7593
7764
  try {
7594
- const config = JSON.parse(readFileSync11(accountJsonPath, "utf-8"));
7765
+ const config = JSON.parse(readFileSync12(accountJsonPath, "utf-8"));
7595
7766
  admins = config.admins ?? [];
7596
7767
  } catch (err) {
7597
7768
  const msg = err instanceof Error ? err.message : String(err);
@@ -7627,12 +7798,12 @@ function checkAdminAuthInvariant(input) {
7627
7798
  }
7628
7799
 
7629
7800
  // server/routes/onboarding.ts
7630
- function hashPin(pin) {
7631
- return createHash2("sha256").update(pin).digest("hex");
7801
+ function hashPin2(pin) {
7802
+ return createHash3("sha256").update(pin).digest("hex");
7632
7803
  }
7633
- function readUsersFile() {
7634
- if (!existsSync8(USERS_FILE)) return null;
7635
- const raw = readFileSync12(USERS_FILE, "utf-8").trim();
7804
+ function readUsersFile2() {
7805
+ if (!existsSync9(USERS_FILE)) return null;
7806
+ const raw = readFileSync13(USERS_FILE, "utf-8").trim();
7636
7807
  if (!raw) return [];
7637
7808
  return JSON.parse(raw);
7638
7809
  }
@@ -7733,7 +7904,7 @@ app7.post("/claude-auth", async (c) => {
7733
7904
  app7.post("/set-pin", async (c) => {
7734
7905
  let existingUsers = null;
7735
7906
  try {
7736
- existingUsers = readUsersFile();
7907
+ existingUsers = readUsersFile2();
7737
7908
  } catch (err) {
7738
7909
  console.error(`[set-pin] users.json corrupt: ${err instanceof Error ? err.message : String(err)}`);
7739
7910
  return c.json({ error: "User configuration is corrupt." }, 400);
@@ -7754,7 +7925,7 @@ app7.post("/set-pin", async (c) => {
7754
7925
  if (!fullName) {
7755
7926
  return c.json({ error: "Your name is required." }, 400);
7756
7927
  }
7757
- const hash = hashPin(body.pin);
7928
+ const hash = hashPin2(body.pin);
7758
7929
  const account = resolveAccount();
7759
7930
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
7760
7931
  const userId = existingOwnerUserId ?? randomUUID7();
@@ -7786,9 +7957,9 @@ app7.post("/set-pin", async (c) => {
7786
7957
  console.log(`[set-pin] wrote users.json + account.json admins: userId=${userId.slice(0, 8)}\u2026 role=owner`);
7787
7958
  if (process.platform === "linux") {
7788
7959
  let installOwner = null;
7789
- if (existsSync8(INSTALL_OWNER_FILE)) {
7960
+ if (existsSync9(INSTALL_OWNER_FILE)) {
7790
7961
  try {
7791
- const raw = readFileSync12(INSTALL_OWNER_FILE, "utf-8").trim();
7962
+ const raw = readFileSync13(INSTALL_OWNER_FILE, "utf-8").trim();
7792
7963
  if (raw.length > 0) installOwner = raw;
7793
7964
  } catch (err) {
7794
7965
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -7835,7 +8006,7 @@ ${body.pin}
7835
8006
  app7.delete("/set-pin", async (c) => {
7836
8007
  let users = null;
7837
8008
  try {
7838
- users = readUsersFile();
8009
+ users = readUsersFile2();
7839
8010
  } catch (err) {
7840
8011
  console.error(`[set-pin] users.json corrupt on DELETE: ${err instanceof Error ? err.message : String(err)}`);
7841
8012
  return c.json({ error: "User configuration is corrupt." }, 400);
@@ -7850,7 +8021,7 @@ app7.delete("/set-pin", async (c) => {
7850
8021
  return c.json({ error: "Invalid request" }, 400);
7851
8022
  }
7852
8023
  if (!body.currentPin) return c.json({ error: "Current PIN required." }, 400);
7853
- const inputHash = hashPin(body.currentPin);
8024
+ const inputHash = hashPin2(body.currentPin);
7854
8025
  const matchedUser = users.find((u) => u.pin === inputHash);
7855
8026
  if (!matchedUser) {
7856
8027
  console.log(`[set-pin] DELETE PIN mismatch: input=${inputHash.slice(0, 8)}\u2026`);
@@ -7869,9 +8040,9 @@ app7.delete("/set-pin", async (c) => {
7869
8040
  var onboarding_default = app7;
7870
8041
 
7871
8042
  // server/routes/client-error.ts
7872
- import { appendFileSync, existsSync as existsSync9, renameSync as renameSync2, statSync as statSync5 } from "fs";
7873
- import { join as join11 } from "path";
7874
- var CLIENT_ERRORS_LOG = join11(LOG_DIR, "client-errors.log");
8043
+ import { appendFileSync, existsSync as existsSync10, renameSync as renameSync2, statSync as statSync6 } from "fs";
8044
+ import { join as join12 } from "path";
8045
+ var CLIENT_ERRORS_LOG = join12(LOG_DIR, "client-errors.log");
7875
8046
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
7876
8047
  var MAX_BODY_SIZE = 8 * 1024;
7877
8048
  var MAX_STACK_LEN = 2e3;
@@ -7914,8 +8085,8 @@ function stackHead(stack) {
7914
8085
  }
7915
8086
  function rotateIfNeeded() {
7916
8087
  try {
7917
- if (!existsSync9(CLIENT_ERRORS_LOG)) return;
7918
- const stats = statSync5(CLIENT_ERRORS_LOG);
8088
+ if (!existsSync10(CLIENT_ERRORS_LOG)) return;
8089
+ const stats = statSync6(CLIENT_ERRORS_LOG);
7919
8090
  if (stats.size < MAX_LOG_SIZE) return;
7920
8091
  renameSync2(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
7921
8092
  } catch (err) {
@@ -8029,66 +8200,6 @@ var client_error_default = app8;
8029
8200
 
8030
8201
  // server/routes/admin/session.ts
8031
8202
  import { randomUUID as randomUUID8 } from "crypto";
8032
-
8033
- // app/lib/admin-identity/pin-validator.ts
8034
- import { createHash as createHash3 } from "crypto";
8035
- import { existsSync as existsSync10, readFileSync as readFileSync13, readdirSync as readdirSync6, statSync as statSync6 } from "fs";
8036
- import { join as join12 } from "path";
8037
- function hashPin2(pin) {
8038
- return createHash3("sha256").update(pin).digest("hex");
8039
- }
8040
- function readUsersFile2(usersFilePath) {
8041
- if (!existsSync10(usersFilePath)) return null;
8042
- const raw = readFileSync13(usersFilePath, "utf-8").trim();
8043
- if (!raw) return [];
8044
- return JSON.parse(raw);
8045
- }
8046
- function validatePin(pin, usersFilePath) {
8047
- let users;
8048
- try {
8049
- users = readUsersFile2(usersFilePath);
8050
- } catch (err) {
8051
- return { kind: "corrupt", message: err instanceof Error ? err.message : String(err) };
8052
- }
8053
- if (users === null || users.length === 0) return { kind: "unavailable" };
8054
- const inputHash = hashPin2(pin);
8055
- const matched = users.find((u) => u.pin === inputHash);
8056
- return matched ? { kind: "match", userId: matched.userId } : { kind: "miss" };
8057
- }
8058
- function scanForOrphanedAccountAdmins(users, accountsDir) {
8059
- const known = new Set(users.map((u) => u.userId));
8060
- const orphans = [];
8061
- if (!existsSync10(accountsDir)) return orphans;
8062
- let entries;
8063
- try {
8064
- entries = readdirSync6(accountsDir);
8065
- } catch {
8066
- return orphans;
8067
- }
8068
- for (const entry of entries) {
8069
- if (entry.startsWith(".")) continue;
8070
- const accountDir = join12(accountsDir, entry);
8071
- try {
8072
- if (!statSync6(accountDir).isDirectory()) continue;
8073
- } catch {
8074
- continue;
8075
- }
8076
- const accountJsonPath = join12(accountDir, "account.json");
8077
- if (!existsSync10(accountJsonPath)) continue;
8078
- try {
8079
- const config = JSON.parse(readFileSync13(accountJsonPath, "utf-8"));
8080
- const admins = config.admins ?? [];
8081
- for (const a of admins) {
8082
- if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
8083
- }
8084
- } catch {
8085
- continue;
8086
- }
8087
- }
8088
- return orphans;
8089
- }
8090
-
8091
- // server/routes/admin/session.ts
8092
8203
  async function resolveUserIdentity(accountId, userId) {
8093
8204
  const result = await loadAdminUserName(accountId, userId);
8094
8205
  if (result.source === "neo4j") {
@@ -8209,7 +8320,7 @@ app9.post("/", async (c) => {
8209
8320
  if (verdict.kind === "miss") {
8210
8321
  let users = [];
8211
8322
  try {
8212
- users = readUsersFile2(USERS_FILE) ?? [];
8323
+ users = readUsersFile(USERS_FILE) ?? [];
8213
8324
  } catch {
8214
8325
  }
8215
8326
  const orphanUserIds = scanForOrphanedAccountAdmins(users, ACCOUNTS_DIR);
@@ -16060,36 +16171,6 @@ var WaGateway = class {
16060
16171
  }
16061
16172
  };
16062
16173
 
16063
- // app/lib/whatsapp/inbound/resolve-admin-userid.ts
16064
- function classifyAdminUserId(users, admins, senderPhone) {
16065
- const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
16066
- if (!matched) return { kind: "no-users-binding" };
16067
- return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
16068
- }
16069
- function pickAdminUserId(users, admins, senderPhone) {
16070
- const r = classifyAdminUserId(users, admins, senderPhone);
16071
- return r.kind === "resolved" ? r.userId : null;
16072
- }
16073
- function resolveAdminUserId(params) {
16074
- try {
16075
- const users = readUsersFile2(USERS_FILE);
16076
- if (!users) return null;
16077
- const account = listValidAccounts().find((a) => a.accountId === params.accountId);
16078
- const admins = account?.config.admins ?? [];
16079
- const direct = pickAdminUserId(users, admins, params.senderPhone);
16080
- if (direct) return direct;
16081
- const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
16082
- if (!mapped) return null;
16083
- const viaLid = pickAdminUserId(users, admins, mapped);
16084
- if (viaLid) {
16085
- console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
16086
- }
16087
- return viaLid;
16088
- } catch {
16089
- return null;
16090
- }
16091
- }
16092
-
16093
16174
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
16094
16175
  function findChannelAdminBindingDrift(adminPhones, admins, users) {
16095
16176
  const drift = [];
@@ -16103,7 +16184,7 @@ function findChannelAdminBindingDrift(adminPhones, admins, users) {
16103
16184
  function warnOnChannelAdminBindingDrift() {
16104
16185
  let users;
16105
16186
  try {
16106
- users = readUsersFile2(USERS_FILE) ?? [];
16187
+ users = readUsersFile(USERS_FILE) ?? [];
16107
16188
  } catch {
16108
16189
  return;
16109
16190
  }