@rubytech/create-maxy-code 0.1.406 → 0.1.408

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-code",
3
- "version": "0.1.406",
3
+ "version": "0.1.408",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -115,6 +115,8 @@ The platform enforces this at multiple levels:
115
115
 
116
116
  **The scheduler path fails closed too.** A scheduled dispatch (`POST /api/channel/schedule-inject`) reaches the same spawn machine without going through the gate, resolving its own effective account via `effectiveAccountFor`. That resolver carries the same single-source + fail-closed shape: a non-manager destination scopes to the house (an owner/admin's real scope), a valid manager scopes to the bound sub-account, and a manager whose bound sub-account is not a valid account resolves to nothing — the route **rejects** (`op=schedule-account-manager-reject … reason=unresolved-effective-account`, HTTP 403, no spawn, no reply), never routing to the house. There is no `?? accountId` fallback on this path either. Telegram scheduled dispatch is house-only by construction (no account-manager routing), so there is nothing to fail closed there.
117
117
 
118
+ **Socket-key resolution — the reply transport.** The account id that names a *spawn scope* (a platform account UUID) is a different identifier from the key the *socket* is stored under (the credential dirname — `"default"` on a legacy single-account install that was never re-keyed to the UUID). These coincide when the credential dir is named for the UUID and diverge otherwise. The inbound reply path never trips on this: it closes over the live connection object directly. A scheduled dispatch has no live connection to close over, so it resolves the socket by asking the manager — `resolveSocket(accountId)` accepts **either** the platform UUID or the connection map key and returns the one connection that owns the socket (the manager holds the `accountId ↔ platformAccountId` bijection). This is the single resolver both the reply path and the scheduler go through, so the id `getSocket` is called with is always a key the connection map actually holds. A null resolution is **two distinct faults, never conflated**: `op=socket-key-miss accountId=… presentKeys=…` means no connection is registered under that id — an account-keying mismatch, the socket may be alive under a different key, and re-pairing is the wrong advice; a *disconnected* result means the connection exists but its transport is down. Reporting a key miss as "WhatsApp disconnected" is the bug that once told an operator to re-sign-in a connected line.
119
+
118
120
  **`dmPolicy` behaviour (per-account):**
119
121
 
120
122
  | Policy | Self phone | Admin phones (in `adminPhones`) | Public/unknown |
@@ -4248,6 +4248,16 @@ function getStatus() {
4248
4248
  function getSocket(accountId) {
4249
4249
  return connections.get(accountId)?.sock ?? null;
4250
4250
  }
4251
+ function selectConnection(conns, accountId) {
4252
+ const presentKeys = conns.map((c) => c.accountId);
4253
+ const conn = conns.find((c) => c.accountId === accountId) ?? conns.find((c) => c.platformAccountId === accountId);
4254
+ if (!conn) return { ok: false, reason: "key-miss", presentKeys };
4255
+ if (!conn.sock) return { ok: false, reason: "disconnected", presentKeys };
4256
+ return { ok: true, sock: conn.sock, accountId: conn.accountId };
4257
+ }
4258
+ function resolveSocket(accountId) {
4259
+ return selectConnection(Array.from(connections.values()), accountId);
4260
+ }
4251
4261
  async function registerLoginSocket(accountId, sock, authDir) {
4252
4262
  if (!configDir) throw new Error("WhatsApp manager not initialized");
4253
4263
  let platformAccountId;
@@ -7442,11 +7452,15 @@ app2.post("/send-admin", async (c) => {
7442
7452
  );
7443
7453
  }
7444
7454
  const accountId = validateAccountId(body.accountId);
7445
- const sock = getSocket(accountId);
7446
- if (!sock) {
7447
- return c.json({ error: `WhatsApp account "${accountId}" is not connected.` }, 503);
7455
+ const res = resolveSocket(accountId);
7456
+ if (!res.ok) {
7457
+ if (res.reason === "key-miss") {
7458
+ console.error(`[whatsapp:outbound] op=socket-key-miss accountId=${accountId} presentKeys=${res.presentKeys.join(",") || "none"}`);
7459
+ return c.json({ error: `No WhatsApp socket is registered for account "${accountId}" (present: ${res.presentKeys.join(", ") || "none"}). This is an account-keying mismatch, not a dropped connection.` }, 503);
7460
+ }
7461
+ return c.json({ error: `WhatsApp account "${accountId}" is registered but its socket is disconnected.` }, 503);
7448
7462
  }
7449
- const result = await sendTextMessage(sock, normalizeE164(phone), text, { accountId });
7463
+ const result = await sendTextMessage(res.sock, normalizeE164(phone), text, { accountId: res.accountId });
7450
7464
  if (!result.success) {
7451
7465
  return c.json({ error: result.error ?? "send failed" }, 500);
7452
7466
  }
@@ -23588,9 +23602,15 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
23588
23602
  waHandleInbound: (input) => waGateway.handleInbound(input),
23589
23603
  tgHandleInbound: (input) => telegramGateway.handleInbound(input),
23590
23604
  sendWhatsAppText: async (accountId, destination, text) => {
23591
- const sock = getSocket(accountId);
23592
- if (!sock) throw new Error("WhatsApp disconnected \u2014 cannot deliver scheduled reply");
23593
- await sendTextMessage(sock, toWhatsappJid(destination), text, { accountId });
23605
+ const res = resolveSocket(accountId);
23606
+ if (!res.ok) {
23607
+ if (res.reason === "key-miss") {
23608
+ console.error(`[whatsapp-native] op=socket-key-miss accountId=${accountId} presentKeys=${res.presentKeys.join(",") || "none"}`);
23609
+ throw new Error(`WhatsApp socket key miss: no socket is registered for accountId=${accountId} (present keys: ${res.presentKeys.join(",") || "none"}). This is an account-keying fault, not a dropped link \u2014 do not advise re-pairing.`);
23610
+ }
23611
+ throw new Error(`WhatsApp disconnected: the socket for accountId=${accountId} is registered but its transport is down; cannot deliver scheduled reply.`);
23612
+ }
23613
+ await sendTextMessage(res.sock, toWhatsappJid(destination), text, { accountId: res.accountId });
23594
23614
  },
23595
23615
  sendTelegramText: async (botToken, chatId, text) => {
23596
23616
  const sent = await sendTelegramText(botToken, chatId, text);