@rubytech/create-maxy-code 0.1.378 → 0.1.382
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/plugin-install.test.js +18 -1
- package/dist/index.js +22 -8
- package/dist/lib/plugin-install.js +22 -0
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +11 -3
- package/payload/platform/plugins/docs/references/admin-ui.md +4 -2
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +13 -0
- package/payload/platform/services/claude-session-manager/dist/index.js +32 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.d.ts +51 -0
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.js +163 -0
- package/payload/platform/services/claude-session-manager/dist/skill-registration-drift.js.map +1 -0
- package/payload/server/{chunk-AQO6KLIH.js → chunk-ZYS5V5QG.js} +0 -21
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-B9u1V35g.js → AdminLoginScreens-C_Qn0UPy.js} +1 -1
- package/payload/server/public/assets/AdminShell-iHdYhspw.js +1 -0
- package/payload/server/public/assets/{Checkbox-B2g2SCpw.js → Checkbox-B76SHnPC.js} +1 -1
- package/payload/server/public/assets/admin-RTEhnrPk.js +1 -0
- package/payload/server/public/assets/{browser-B8omhYsK.js → browser-Chmp3Xfv.js} +1 -1
- package/payload/server/public/assets/calendar-CjE7hiqV.js +1 -0
- package/payload/server/public/assets/chat-DiKmCMO1.js +1 -0
- package/payload/server/public/assets/data-5PpEsUR3.js +1 -0
- package/payload/server/public/assets/{graph-DJOKFr_s.js → graph-DEHmvpNl.js} +3 -3
- package/payload/server/public/assets/{graph-labels-DFdBydWC.js → graph-labels-CYAjU3rz.js} +1 -1
- package/payload/server/public/assets/operator-BDTnuQhB.js +1 -0
- package/payload/server/public/assets/page-D8FFXoI9.js +1 -0
- package/payload/server/public/assets/page-HeiBebPq.js +32 -0
- package/payload/server/public/assets/{public-CGl0i1f-.js → public-BIOxrxZK.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-Cg_V4XIU.js → rotate-ccw-K1xZvf3B.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-Ca1GJeBC.css → useSubAccountSwitcher-B44qYovc.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-DPEkYkYd.js +9 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +4 -4
- package/payload/server/public/chat.html +6 -6
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +7 -7
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/operator.html +8 -8
- package/payload/server/public/public.html +6 -6
- package/payload/server/server.js +387 -122
- package/payload/server/public/assets/AdminShell-C2UXTLSB.js +0 -1
- package/payload/server/public/assets/admin-CRBQgWsm.js +0 -1
- package/payload/server/public/assets/calendar-Blb-5arm.js +0 -1
- package/payload/server/public/assets/chat--1ajLxwi.js +0 -1
- package/payload/server/public/assets/data-BYUmu98z.js +0 -1
- package/payload/server/public/assets/operator-DUVqlubZ.js +0 -1
- package/payload/server/public/assets/page-BMeSKYGZ.js +0 -32
- package/payload/server/public/assets/page-DBkTNfei.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-DAgBUed_.js +0 -9
package/payload/server/server.js
CHANGED
|
@@ -72,7 +72,6 @@ import {
|
|
|
72
72
|
loadAdminUserName,
|
|
73
73
|
loadPersonNameByElementId,
|
|
74
74
|
loadUserProfile,
|
|
75
|
-
loadWhatsappSenderName,
|
|
76
75
|
migrateLegacyRemotePassword,
|
|
77
76
|
persistMessage,
|
|
78
77
|
projectAgent,
|
|
@@ -110,7 +109,7 @@ import {
|
|
|
110
109
|
vncLog,
|
|
111
110
|
walkPremiumBundles,
|
|
112
111
|
writeAdminUserAndPerson
|
|
113
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-ZYS5V5QG.js";
|
|
114
113
|
import {
|
|
115
114
|
__commonJS,
|
|
116
115
|
__toESM
|
|
@@ -1869,6 +1868,32 @@ function readSelfId(authDir) {
|
|
|
1869
1868
|
return { e164: null, jid: null, lid: null };
|
|
1870
1869
|
}
|
|
1871
1870
|
}
|
|
1871
|
+
function credsCensus(authDir) {
|
|
1872
|
+
const raw = readCredsJsonRaw(resolveCredsPath(authDir));
|
|
1873
|
+
if (!raw) return { credsPresent: false, registered: false, hasAccountSignature: false, e164: null };
|
|
1874
|
+
try {
|
|
1875
|
+
const parsed = JSON.parse(raw);
|
|
1876
|
+
const registered = parsed?.registered === true;
|
|
1877
|
+
const hasAccountSignature = parsed?.account != null;
|
|
1878
|
+
let e164 = null;
|
|
1879
|
+
const jid = parsed?.me?.id;
|
|
1880
|
+
if (jid) {
|
|
1881
|
+
const match = jid.match(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i);
|
|
1882
|
+
if (match) e164 = match[1];
|
|
1883
|
+
}
|
|
1884
|
+
return { credsPresent: true, registered, hasAccountSignature, e164 };
|
|
1885
|
+
} catch {
|
|
1886
|
+
return { credsPresent: true, registered: false, hasAccountSignature: false, e164: null };
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
function getAuthAgeMs(authDir) {
|
|
1890
|
+
try {
|
|
1891
|
+
const stats = fsSync.statSync(resolveCredsPath(authDir));
|
|
1892
|
+
return Date.now() - stats.mtimeMs;
|
|
1893
|
+
} catch {
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1872
1897
|
async function clearAuth(authDir) {
|
|
1873
1898
|
try {
|
|
1874
1899
|
await fs.access(authDir);
|
|
@@ -1880,6 +1905,17 @@ async function clearAuth(authDir) {
|
|
|
1880
1905
|
console.error(`${TAG} cleared credentials at ${authDir}`);
|
|
1881
1906
|
return true;
|
|
1882
1907
|
}
|
|
1908
|
+
function discardAuthDirSync(authDir) {
|
|
1909
|
+
try {
|
|
1910
|
+
if (!fsSync.existsSync(authDir)) return false;
|
|
1911
|
+
fsSync.rmSync(authDir, { recursive: true, force: true });
|
|
1912
|
+
console.error(`${TAG} discarded credential dir ${authDir}`);
|
|
1913
|
+
return true;
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
console.warn(`${TAG} discardAuthDirSync failed authDir=${authDir}: ${String(err)}`);
|
|
1916
|
+
return false;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1883
1919
|
function listCredentialAccountIds(configDir2) {
|
|
1884
1920
|
const credsRoot = path.join(os.homedir(), configDir2, "credentials", "whatsapp");
|
|
1885
1921
|
try {
|
|
@@ -2027,13 +2063,22 @@ function enqueueSaveCreds(authDir, saveCreds) {
|
|
|
2027
2063
|
});
|
|
2028
2064
|
}
|
|
2029
2065
|
async function createWaSocket(opts) {
|
|
2030
|
-
const { authDir, onQr, onConnectionUpdate, silent } = opts;
|
|
2066
|
+
const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs } = opts;
|
|
2067
|
+
const cid = correlationId ?? "none";
|
|
2068
|
+
const acct = account ?? "unknown";
|
|
2031
2069
|
await fs2.mkdir(authDir, { recursive: true });
|
|
2032
2070
|
maybeRestoreCredsFromBackup(authDir);
|
|
2033
2071
|
const { state, saveCreds } = await useMultiFileAuthState(authDir);
|
|
2034
2072
|
const { version } = await fetchLatestBaileysVersion();
|
|
2073
|
+
if (!silent) {
|
|
2074
|
+
const preCensus = credsCensus(authDir);
|
|
2075
|
+
console.error(
|
|
2076
|
+
`${TAG2} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
const openTs = Date.now();
|
|
2035
2080
|
console.error(
|
|
2036
|
-
`${TAG2}
|
|
2081
|
+
`${TAG2} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
|
|
2037
2082
|
);
|
|
2038
2083
|
const baileysLogger = createBaileysLogger();
|
|
2039
2084
|
const sock = makeWASocket({
|
|
@@ -2054,6 +2099,9 @@ async function createWaSocket(opts) {
|
|
|
2054
2099
|
enqueueSaveCreds(authDir, saveCreds);
|
|
2055
2100
|
});
|
|
2056
2101
|
let qrSequence = 0;
|
|
2102
|
+
let everOpen = false;
|
|
2103
|
+
let lastQrTs = 0;
|
|
2104
|
+
let pairSuccessLogged = false;
|
|
2057
2105
|
sock.ev.on("connection.update", (update) => {
|
|
2058
2106
|
try {
|
|
2059
2107
|
const parts = [];
|
|
@@ -2069,17 +2117,40 @@ async function createWaSocket(opts) {
|
|
|
2069
2117
|
if (!silent && parts.length > 0) {
|
|
2070
2118
|
console.error(`${TAG2} connection.update ${parts.join(" ")}`);
|
|
2071
2119
|
}
|
|
2120
|
+
if (update.qr && !silent) {
|
|
2121
|
+
const now = Date.now();
|
|
2122
|
+
const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
|
|
2123
|
+
lastQrTs = now;
|
|
2124
|
+
console.error(
|
|
2125
|
+
`${TAG2} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
|
|
2129
|
+
pairSuccessLogged = true;
|
|
2130
|
+
const codeIssuedTs = getCodeIssuedTs?.();
|
|
2131
|
+
const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
|
|
2132
|
+
const afterCensus = credsCensus(authDir);
|
|
2133
|
+
console.error(
|
|
2134
|
+
`${TAG2} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
|
|
2135
|
+
);
|
|
2136
|
+
}
|
|
2072
2137
|
if (update.qr && onQr) {
|
|
2073
2138
|
onQr(update.qr);
|
|
2074
2139
|
}
|
|
2075
2140
|
if (update.connection === "close") {
|
|
2076
2141
|
const status = getStatusCode(update.lastDisconnect?.error);
|
|
2142
|
+
if (!silent) {
|
|
2143
|
+
console.error(
|
|
2144
|
+
`${TAG2} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2077
2147
|
if (status === DisconnectReason.loggedOut && !silent) {
|
|
2078
2148
|
console.error(`${TAG2} session logged out (401) \u2014 re-link required`);
|
|
2079
2149
|
}
|
|
2080
2150
|
}
|
|
2081
|
-
if (update.connection === "open"
|
|
2082
|
-
|
|
2151
|
+
if (update.connection === "open") {
|
|
2152
|
+
everOpen = true;
|
|
2153
|
+
if (!silent) console.error(`${TAG2} connected`);
|
|
2083
2154
|
}
|
|
2084
2155
|
onConnectionUpdate?.(update);
|
|
2085
2156
|
} catch (err) {
|
|
@@ -2143,6 +2214,12 @@ function extractBoomDetails(err) {
|
|
|
2143
2214
|
if (!statusCode && !error && !message) return null;
|
|
2144
2215
|
return { statusCode, error, message };
|
|
2145
2216
|
}
|
|
2217
|
+
function closeKind(status, everOpen) {
|
|
2218
|
+
if (status === DisconnectReason.restartRequired) return "restart-required-515";
|
|
2219
|
+
if (status === DisconnectReason.loggedOut) return "logged-out-401";
|
|
2220
|
+
if (status === 408) return everOpen ? "conn-lost-408" : "qr-refs-ended-408";
|
|
2221
|
+
return "other";
|
|
2222
|
+
}
|
|
2146
2223
|
|
|
2147
2224
|
// app/lib/whatsapp/reconnect.ts
|
|
2148
2225
|
import { DisconnectReason as DisconnectReason2 } from "@whiskeysockets/baileys";
|
|
@@ -3096,12 +3173,15 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
3096
3173
|
}
|
|
3097
3174
|
|
|
3098
3175
|
// app/lib/whatsapp/message-store.ts
|
|
3099
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, appendFileSync, readdirSync } from "fs";
|
|
3176
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, appendFileSync, readdirSync, statSync as statSync2 } from "fs";
|
|
3100
3177
|
import { dirname, join as join3 } from "path";
|
|
3101
3178
|
var STORE_ROOT = () => join3(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-messages");
|
|
3102
3179
|
function conversationFile(platformAccountId, remoteJid) {
|
|
3103
3180
|
return join3(STORE_ROOT(), platformAccountId, `${remoteJid}.jsonl`);
|
|
3104
3181
|
}
|
|
3182
|
+
function conversationFilePath(platformAccountId, remoteJid) {
|
|
3183
|
+
return conversationFile(platformAccountId, remoteJid);
|
|
3184
|
+
}
|
|
3105
3185
|
function parseLines(raw) {
|
|
3106
3186
|
const out = [];
|
|
3107
3187
|
for (const line of raw.split("\n")) {
|
|
@@ -3114,6 +3194,7 @@ function parseLines(raw) {
|
|
|
3114
3194
|
}
|
|
3115
3195
|
return out;
|
|
3116
3196
|
}
|
|
3197
|
+
var byDateSentAsc = (a, b) => a.dateSent.localeCompare(b.dateSent);
|
|
3117
3198
|
function appendMessage(platformAccountId, record) {
|
|
3118
3199
|
const file = conversationFile(platformAccountId, record.remoteJid);
|
|
3119
3200
|
try {
|
|
@@ -3148,13 +3229,79 @@ function readConversation(platformAccountId, remoteJid) {
|
|
|
3148
3229
|
const file = conversationFile(platformAccountId, remoteJid);
|
|
3149
3230
|
try {
|
|
3150
3231
|
if (!existsSync3(file)) return [];
|
|
3151
|
-
return parseLines(readFileSync4(file, "utf8")).sort(
|
|
3232
|
+
return parseLines(readFileSync4(file, "utf8")).sort(byDateSentAsc);
|
|
3152
3233
|
} catch (err) {
|
|
3153
3234
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3154
3235
|
console.error(`[whatsapp-message-store] FAIL op=read reason=${reason.slice(0, 100)} remoteJid=${remoteJid}`);
|
|
3155
3236
|
return [];
|
|
3156
3237
|
}
|
|
3157
3238
|
}
|
|
3239
|
+
var summaryCache = /* @__PURE__ */ new Map();
|
|
3240
|
+
function summariseRecords(records) {
|
|
3241
|
+
if (records.length === 0) return null;
|
|
3242
|
+
const sorted = records.slice().sort(byDateSentAsc);
|
|
3243
|
+
return {
|
|
3244
|
+
first: sorted[0],
|
|
3245
|
+
last: sorted[sorted.length - 1],
|
|
3246
|
+
firstNamedSender: sorted.find((r) => r.senderName)?.senderName ?? null
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
function readConversationSummaries(platformAccountId) {
|
|
3250
|
+
const dir = join3(STORE_ROOT(), platformAccountId);
|
|
3251
|
+
const out = /* @__PURE__ */ new Map();
|
|
3252
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3253
|
+
let fullReads = 0;
|
|
3254
|
+
let cacheHits = 0;
|
|
3255
|
+
let bytesRead = 0;
|
|
3256
|
+
try {
|
|
3257
|
+
if (!existsSync3(dir)) return out;
|
|
3258
|
+
for (const entry of readdirSync(dir)) {
|
|
3259
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
3260
|
+
const remoteJid = entry.slice(0, -".jsonl".length);
|
|
3261
|
+
const file = join3(dir, entry);
|
|
3262
|
+
seen.add(file);
|
|
3263
|
+
let size;
|
|
3264
|
+
let mtimeMs;
|
|
3265
|
+
try {
|
|
3266
|
+
const st = statSync2(file);
|
|
3267
|
+
size = st.size;
|
|
3268
|
+
mtimeMs = st.mtimeMs;
|
|
3269
|
+
} catch {
|
|
3270
|
+
continue;
|
|
3271
|
+
}
|
|
3272
|
+
const cached2 = summaryCache.get(file);
|
|
3273
|
+
if (cached2 && cached2.size === size && cached2.mtimeMs === mtimeMs) {
|
|
3274
|
+
cacheHits++;
|
|
3275
|
+
out.set(remoteJid, cached2.summary);
|
|
3276
|
+
continue;
|
|
3277
|
+
}
|
|
3278
|
+
let summary;
|
|
3279
|
+
try {
|
|
3280
|
+
const raw = readFileSync4(file, "utf8");
|
|
3281
|
+
bytesRead += Buffer.byteLength(raw);
|
|
3282
|
+
fullReads++;
|
|
3283
|
+
summary = summariseRecords(parseLines(raw));
|
|
3284
|
+
} catch (err) {
|
|
3285
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3286
|
+
console.error(`[whatsapp-message-store] FAIL op=summarise reason=${reason.slice(0, 100)} remoteJid=${remoteJid}`);
|
|
3287
|
+
continue;
|
|
3288
|
+
}
|
|
3289
|
+
if (!summary) continue;
|
|
3290
|
+
summaryCache.set(file, { size, mtimeMs, summary });
|
|
3291
|
+
out.set(remoteJid, summary);
|
|
3292
|
+
}
|
|
3293
|
+
for (const key of summaryCache.keys()) {
|
|
3294
|
+
if (dirname(key) === dir && !seen.has(key)) summaryCache.delete(key);
|
|
3295
|
+
}
|
|
3296
|
+
} catch (err) {
|
|
3297
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3298
|
+
console.error(`[whatsapp-message-store] FAIL op=summaries reason=${reason.slice(0, 100)} platformAccountId=${platformAccountId}`);
|
|
3299
|
+
}
|
|
3300
|
+
console.log(
|
|
3301
|
+
`[whatsapp-message-store] op=summaries accountId=${platformAccountId} conversations=${out.size} fullReads=${fullReads} cacheHits=${cacheHits} bytesRead=${bytesRead}`
|
|
3302
|
+
);
|
|
3303
|
+
return out;
|
|
3304
|
+
}
|
|
3158
3305
|
function readAllConversations(platformAccountId) {
|
|
3159
3306
|
const dir = join3(STORE_ROOT(), platformAccountId);
|
|
3160
3307
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -6055,6 +6202,13 @@ async function runPostPairing(login) {
|
|
|
6055
6202
|
}
|
|
6056
6203
|
async function loginConnectionLoop(accountId, login) {
|
|
6057
6204
|
let attempt = 0;
|
|
6205
|
+
const cidShort = login.id.slice(0, 8);
|
|
6206
|
+
const logTerminal = (outcome, errMsg) => {
|
|
6207
|
+
const fc = credsCensus(login.authDir);
|
|
6208
|
+
console.error(
|
|
6209
|
+
`${TAG17} op=terminal cid=${cidShort} outcome=${outcome} credsFinal={registered=${fc.registered},account=${fc.hasAccountSignature},me=${fc.e164 ?? "null"}}` + (errMsg ? ` error="${errMsg}"` : "")
|
|
6210
|
+
);
|
|
6211
|
+
};
|
|
6058
6212
|
while (true) {
|
|
6059
6213
|
try {
|
|
6060
6214
|
await waitForConnection(login.sock);
|
|
@@ -6063,6 +6217,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6063
6217
|
await runPostPairing(current);
|
|
6064
6218
|
current.connected = true;
|
|
6065
6219
|
console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
|
|
6220
|
+
logTerminal("connected");
|
|
6066
6221
|
}
|
|
6067
6222
|
return;
|
|
6068
6223
|
} catch (err) {
|
|
@@ -6079,6 +6234,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6079
6234
|
current.error = formatError(err);
|
|
6080
6235
|
}
|
|
6081
6236
|
current.errorStatus = getStatusCode(err);
|
|
6237
|
+
logTerminal("error", current.error);
|
|
6082
6238
|
return;
|
|
6083
6239
|
}
|
|
6084
6240
|
attempt++;
|
|
@@ -6090,14 +6246,19 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6090
6246
|
await new Promise((r) => setTimeout(r, delay));
|
|
6091
6247
|
const afterDelay = activeLogins.get(accountId);
|
|
6092
6248
|
if (afterDelay?.id !== login.id) return;
|
|
6249
|
+
const rc = credsCensus(login.authDir);
|
|
6250
|
+
console.error(
|
|
6251
|
+
`${TAG17} op=reconnect cid=${cidShort} attempt=${attempt}/${LOGIN_MAX_RECONNECTS} withRegistered=${rc.registered} withAccount=${rc.hasAccountSignature}`
|
|
6252
|
+
);
|
|
6093
6253
|
try {
|
|
6094
|
-
const newSock = await createWaSocket({ authDir: login.authDir });
|
|
6254
|
+
const newSock = await createWaSocket({ authDir: login.authDir, correlationId: cidShort, account: accountId });
|
|
6095
6255
|
current.sock = newSock;
|
|
6096
6256
|
} catch (sockErr) {
|
|
6097
6257
|
console.error(
|
|
6098
6258
|
`${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
6099
6259
|
);
|
|
6100
6260
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
6261
|
+
logTerminal("error", current.error);
|
|
6101
6262
|
return;
|
|
6102
6263
|
}
|
|
6103
6264
|
}
|
|
@@ -6105,11 +6266,13 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6105
6266
|
}
|
|
6106
6267
|
async function startLogin(opts) {
|
|
6107
6268
|
const { accountId, authDir, phone, accountDir = null, force, timeoutMs = 3e4 } = opts;
|
|
6269
|
+
const loginId = randomUUID6();
|
|
6270
|
+
const cid = loginId.slice(0, 8);
|
|
6108
6271
|
const existing0 = activeLogins.get(accountId);
|
|
6109
6272
|
const hasAuth = await authExists(authDir);
|
|
6110
6273
|
const selfId = readSelfId(authDir);
|
|
6111
6274
|
console.error(
|
|
6112
|
-
`${TAG17}
|
|
6275
|
+
`${TAG17} op=start cid=${cid} account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasCode=${!!existing0.pairingCode}}` : " activeLogin=none")
|
|
6113
6276
|
);
|
|
6114
6277
|
if (hasAuth && !force) {
|
|
6115
6278
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -6148,21 +6311,32 @@ async function startLogin(opts) {
|
|
|
6148
6311
|
);
|
|
6149
6312
|
let requested = false;
|
|
6150
6313
|
let sock;
|
|
6314
|
+
let socketOpenTs = 0;
|
|
6315
|
+
let codeIssuedTs;
|
|
6151
6316
|
try {
|
|
6152
6317
|
sock = await createWaSocket({
|
|
6153
6318
|
authDir,
|
|
6319
|
+
correlationId: cid,
|
|
6320
|
+
account: accountId,
|
|
6321
|
+
getCodeIssuedTs: () => codeIssuedTs,
|
|
6154
6322
|
// The first QR event means the noise handshake is complete and the
|
|
6155
6323
|
// socket can send the pairing IQ — the safe moment to request the code.
|
|
6156
6324
|
// We never render the QR; it is only a readiness signal.
|
|
6157
6325
|
onQr: () => {
|
|
6158
6326
|
if (requested) return;
|
|
6159
6327
|
requested = true;
|
|
6328
|
+
console.error(
|
|
6329
|
+
`${TAG17} op=pairing-request cid=${cid} account=${accountId} qrRef=#1 sinceOpenMs=${socketOpenTs ? Date.now() - socketOpenTs : 0}`
|
|
6330
|
+
);
|
|
6160
6331
|
void sock.requestPairingCode(digits).then((code) => {
|
|
6161
6332
|
clearTimeout(codeTimer);
|
|
6333
|
+
codeIssuedTs = Date.now();
|
|
6162
6334
|
const current = activeLogins.get(accountId);
|
|
6163
6335
|
if (current?.id !== login.id) return;
|
|
6164
6336
|
if (!current.pairingCode) current.pairingCode = code;
|
|
6165
|
-
console.error(
|
|
6337
|
+
console.error(
|
|
6338
|
+
`${TAG17} op=code-issued cid=${cid} account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length} sinceOpenMs=${socketOpenTs ? codeIssuedTs - socketOpenTs : 0}`
|
|
6339
|
+
);
|
|
6166
6340
|
resolveCode?.(code);
|
|
6167
6341
|
}).catch((err) => {
|
|
6168
6342
|
clearTimeout(codeTimer);
|
|
@@ -6170,6 +6344,7 @@ async function startLogin(opts) {
|
|
|
6170
6344
|
});
|
|
6171
6345
|
}
|
|
6172
6346
|
});
|
|
6347
|
+
socketOpenTs = Date.now();
|
|
6173
6348
|
} catch (err) {
|
|
6174
6349
|
clearTimeout(codeTimer);
|
|
6175
6350
|
resetActiveLogin(accountId);
|
|
@@ -6179,7 +6354,7 @@ async function startLogin(opts) {
|
|
|
6179
6354
|
accountId,
|
|
6180
6355
|
authDir,
|
|
6181
6356
|
accountDir,
|
|
6182
|
-
id:
|
|
6357
|
+
id: loginId,
|
|
6183
6358
|
sock,
|
|
6184
6359
|
startedAt: Date.now(),
|
|
6185
6360
|
phone: digits,
|
|
@@ -6260,6 +6435,13 @@ async function waitForLogin(opts) {
|
|
|
6260
6435
|
console.error(`${TAG17} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
|
|
6261
6436
|
return { connected: false, message: "Still waiting for you to enter the pairing code. The code is still valid \u2014 ask me to keep waiting, or request a new code.", configPersisted: false };
|
|
6262
6437
|
}
|
|
6438
|
+
function listActiveLoginAccountIds() {
|
|
6439
|
+
const ids = [];
|
|
6440
|
+
for (const [accountId, login] of activeLogins) {
|
|
6441
|
+
if (isLoginFresh(login)) ids.push(accountId);
|
|
6442
|
+
}
|
|
6443
|
+
return ids;
|
|
6444
|
+
}
|
|
6263
6445
|
|
|
6264
6446
|
// app/lib/whatsapp/outbound/send-document.ts
|
|
6265
6447
|
import { realpathSync as realpathSync3 } from "fs";
|
|
@@ -6456,6 +6638,7 @@ function serializeWhatsAppSchema() {
|
|
|
6456
6638
|
import { readdirSync as readdirSync3 } from "fs";
|
|
6457
6639
|
import { join as join9 } from "path";
|
|
6458
6640
|
var TAG19 = "[whatsapp:reconcile]";
|
|
6641
|
+
var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
|
|
6459
6642
|
function listCredsAccountIds(credsRoot) {
|
|
6460
6643
|
try {
|
|
6461
6644
|
return readdirSync3(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join9(credsRoot, e.name))).map((e) => e.name);
|
|
@@ -6469,11 +6652,29 @@ function isAccountConfigured(accountDir, accountId) {
|
|
|
6469
6652
|
return !!accounts && Object.prototype.hasOwnProperty.call(accounts, accountId);
|
|
6470
6653
|
}
|
|
6471
6654
|
function reconcileCredsOnDisk(opts) {
|
|
6472
|
-
const { credsRoot, accountDir, liveAccountIds } = opts;
|
|
6655
|
+
const { credsRoot, accountDir, liveAccountIds, activeLoginAccountIds } = opts;
|
|
6473
6656
|
const entries = [];
|
|
6474
6657
|
for (const accountId of listCredsAccountIds(credsRoot)) {
|
|
6475
6658
|
if (liveAccountIds.has(accountId)) continue;
|
|
6476
6659
|
const authDir = join9(credsRoot, accountId);
|
|
6660
|
+
const census = credsCensus(authDir);
|
|
6661
|
+
if (census.registered && !census.hasAccountSignature) {
|
|
6662
|
+
const inFlight = activeLoginAccountIds.has(accountId);
|
|
6663
|
+
const ageMs = getAuthAgeMs(authDir);
|
|
6664
|
+
const abandoned = !inFlight && ageMs !== null && ageMs >= HALF_REGISTERED_MIN_AGE_MS;
|
|
6665
|
+
if (!abandoned) {
|
|
6666
|
+
console.error(`${TAG19} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight} ageMs=${ageMs ?? "null"}`);
|
|
6667
|
+
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
6668
|
+
continue;
|
|
6669
|
+
}
|
|
6670
|
+
console.error(`${TAG19} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs}`);
|
|
6671
|
+
const removed = discardAuthDirSync(authDir);
|
|
6672
|
+
if (!removed) {
|
|
6673
|
+
console.error(`${TAG19} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
|
|
6674
|
+
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
6675
|
+
}
|
|
6676
|
+
continue;
|
|
6677
|
+
}
|
|
6477
6678
|
const selfPhone = readSelfId(authDir).e164 ?? void 0;
|
|
6478
6679
|
const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
|
|
6479
6680
|
console.error(
|
|
@@ -6500,9 +6701,10 @@ app2.get("/status", (c) => {
|
|
|
6500
6701
|
try {
|
|
6501
6702
|
const live = getStatus();
|
|
6502
6703
|
const liveIds = new Set(live.map((a) => a.accountId));
|
|
6704
|
+
const activeLoginIds = new Set(listActiveLoginAccountIds());
|
|
6503
6705
|
const credsRoot = join10(MAXY_DIR, "credentials", "whatsapp");
|
|
6504
6706
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
6505
|
-
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds });
|
|
6707
|
+
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
|
|
6506
6708
|
const accounts = [...live, ...reconciled];
|
|
6507
6709
|
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
|
|
6508
6710
|
console.error(`${TAG20} status accounts=${accounts.length} [${summary}]`);
|
|
@@ -6922,11 +7124,11 @@ app2.get("/group-info", async (c) => {
|
|
|
6922
7124
|
var whatsapp_default = app2;
|
|
6923
7125
|
|
|
6924
7126
|
// server/routes/whatsapp-reader.ts
|
|
6925
|
-
import { readFileSync as readFileSync15, watch, statSync as
|
|
7127
|
+
import { readFileSync as readFileSync15, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync8, readdirSync as readdirSync8, realpathSync as realpathSync4 } from "fs";
|
|
6926
7128
|
import { basename as basename4, dirname as dirname3, isAbsolute, join as join14, relative as relative2, resolve as resolve12, sep as sep3 } from "path";
|
|
6927
7129
|
|
|
6928
7130
|
// server/routes/admin/sidebar-sessions.ts
|
|
6929
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as
|
|
7131
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
6930
7132
|
import { basename as basename3, dirname as dirname2, join as join11, resolve as resolve11 } from "path";
|
|
6931
7133
|
|
|
6932
7134
|
// app/lib/whatsapp-reader/select-sessions.ts
|
|
@@ -6947,9 +7149,36 @@ function selectReaderChannelSessions(rows) {
|
|
|
6947
7149
|
lastMessageAt: r.lastMessageAt,
|
|
6948
7150
|
modelGated: r.modelGated,
|
|
6949
7151
|
model: r.model,
|
|
6950
|
-
|
|
7152
|
+
source: "session"
|
|
6951
7153
|
}));
|
|
6952
7154
|
}
|
|
7155
|
+
function buildStoreConversationRows(accountId, summaries) {
|
|
7156
|
+
const rows = [];
|
|
7157
|
+
for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
|
|
7158
|
+
const scope = last.scope;
|
|
7159
|
+
const isGroup = remoteJid.endsWith("@g.us");
|
|
7160
|
+
const senderTelephone = isGroup ? null : first.senderTelephone;
|
|
7161
|
+
rows.push({
|
|
7162
|
+
sessionId: "",
|
|
7163
|
+
projectDir: "",
|
|
7164
|
+
title: remoteJid,
|
|
7165
|
+
senderId: senderTelephone,
|
|
7166
|
+
startedAt: first.dateSent,
|
|
7167
|
+
channel: "whatsapp",
|
|
7168
|
+
role: scope === "admin" ? "admin" : "public",
|
|
7169
|
+
operatorName: null,
|
|
7170
|
+
whatsappName: firstNamedSender,
|
|
7171
|
+
lastMessageAt: last.dateSent,
|
|
7172
|
+
modelGated: false,
|
|
7173
|
+
model: null,
|
|
7174
|
+
source: "store",
|
|
7175
|
+
accountId,
|
|
7176
|
+
remoteJid,
|
|
7177
|
+
scope
|
|
7178
|
+
});
|
|
7179
|
+
}
|
|
7180
|
+
return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
|
|
7181
|
+
}
|
|
6953
7182
|
|
|
6954
7183
|
// server/routes/admin/sidebar-sessions.ts
|
|
6955
7184
|
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
@@ -7189,26 +7418,10 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7189
7418
|
const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
7190
7419
|
const validAccounts = listValidAccounts();
|
|
7191
7420
|
const multiAccount = validAccounts.length > 1;
|
|
7192
|
-
const accounts = [];
|
|
7193
|
-
for (const a of validAccounts) {
|
|
7194
|
-
let businessName = null;
|
|
7195
|
-
try {
|
|
7196
|
-
const branding = await fetchBranding(a.accountId);
|
|
7197
|
-
businessName = branding?.name || null;
|
|
7198
|
-
} catch {
|
|
7199
|
-
}
|
|
7200
|
-
accounts.push({
|
|
7201
|
-
accountId: a.accountId,
|
|
7202
|
-
role: a.config.role ?? "",
|
|
7203
|
-
businessName,
|
|
7204
|
-
isHouse: a.config.role === "house"
|
|
7205
|
-
});
|
|
7206
|
-
}
|
|
7207
|
-
console.log(`[admin-sessions-list] accounts=${accounts.length}`);
|
|
7208
7421
|
const configDir2 = claudeConfigDir();
|
|
7209
7422
|
if (!configDir2) {
|
|
7210
7423
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
7211
|
-
return c.json({ sessions: [], accountId: scopeAccountId
|
|
7424
|
+
return c.json({ sessions: [], accountId: scopeAccountId });
|
|
7212
7425
|
}
|
|
7213
7426
|
const projectsRoot = join11(configDir2, "projects");
|
|
7214
7427
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
@@ -7239,7 +7452,7 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7239
7452
|
let mtimeMs;
|
|
7240
7453
|
try {
|
|
7241
7454
|
body = readFileSync12(path2, "utf8");
|
|
7242
|
-
mtimeMs =
|
|
7455
|
+
mtimeMs = statSync3(path2).mtimeMs;
|
|
7243
7456
|
} catch {
|
|
7244
7457
|
continue;
|
|
7245
7458
|
}
|
|
@@ -7317,13 +7530,13 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7317
7530
|
if (multiAccount) {
|
|
7318
7531
|
console.log(`[admin-sessions-list] untagged-accountId-rows=${untaggedCount}`);
|
|
7319
7532
|
}
|
|
7320
|
-
return c.json({ sessions: rows, accountId: scopeAccountId
|
|
7533
|
+
return c.json({ sessions: rows, accountId: scopeAccountId });
|
|
7321
7534
|
});
|
|
7322
7535
|
var sidebar_sessions_default = app3;
|
|
7323
7536
|
|
|
7324
7537
|
// app/lib/admin-identity/pin-validator.ts
|
|
7325
7538
|
import { createHash } from "crypto";
|
|
7326
|
-
import { existsSync as existsSync7, readFileSync as readFileSync13, readdirSync as readdirSync6, statSync as
|
|
7539
|
+
import { existsSync as existsSync7, readFileSync as readFileSync13, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
7327
7540
|
import { join as join12 } from "path";
|
|
7328
7541
|
function hashPin(pin) {
|
|
7329
7542
|
return createHash("sha256").update(pin).digest("hex");
|
|
@@ -7744,7 +7957,7 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7744
7957
|
model: lastAssistantModel(body)
|
|
7745
7958
|
});
|
|
7746
7959
|
}
|
|
7747
|
-
const
|
|
7960
|
+
const sessionRows = selectReaderChannelSessions(rows).filter((r) => r.channel !== "whatsapp");
|
|
7748
7961
|
const account = resolveAccount();
|
|
7749
7962
|
let users = [];
|
|
7750
7963
|
if (account) {
|
|
@@ -7757,13 +7970,11 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7757
7970
|
}
|
|
7758
7971
|
const admins = account?.config.admins ?? [];
|
|
7759
7972
|
const nameBySender = /* @__PURE__ */ new Map();
|
|
7760
|
-
const
|
|
7761
|
-
const distinct = [...new Set(selected.map((r) => r.senderId).filter((s) => !!s))];
|
|
7973
|
+
const distinct = [...new Set(sessionRows.map((r) => r.senderId).filter((s) => !!s))];
|
|
7762
7974
|
await Promise.all(
|
|
7763
7975
|
distinct.map(async (senderId) => {
|
|
7764
7976
|
if (!account) {
|
|
7765
7977
|
nameBySender.set(senderId, null);
|
|
7766
|
-
whatsappNameBySender.set(senderId, null);
|
|
7767
7978
|
return;
|
|
7768
7979
|
}
|
|
7769
7980
|
const idRes = classifyAdminUserId(users, admins, senderId);
|
|
@@ -7771,60 +7982,22 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7771
7982
|
const { name, reason } = resolveOperatorDisplay(idRes, nameRes);
|
|
7772
7983
|
if (reason) console.error(`[wa-reader] op=operator-unresolved senderId=${senderId} reason=${reason}`);
|
|
7773
7984
|
nameBySender.set(senderId, name);
|
|
7774
|
-
if (!name) {
|
|
7775
|
-
const waName = await loadWhatsappSenderName(account.accountId, senderId);
|
|
7776
|
-
if (waName) console.error(`[wa-reader] op=operator-name-fallback senderId=${senderId} source=whatsapp-pushname`);
|
|
7777
|
-
whatsappNameBySender.set(senderId, waName);
|
|
7778
|
-
} else {
|
|
7779
|
-
whatsappNameBySender.set(senderId, null);
|
|
7780
|
-
}
|
|
7781
7985
|
})
|
|
7782
7986
|
);
|
|
7783
|
-
const
|
|
7987
|
+
const sessionConversations = sessionRows.map((r) => ({
|
|
7784
7988
|
...r,
|
|
7785
|
-
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
7786
|
-
whatsappName: r.senderId ? whatsappNameBySender.get(r.senderId) ?? null : null,
|
|
7787
|
-
rowType: "session"
|
|
7989
|
+
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
7788
7990
|
}));
|
|
7789
|
-
const
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
for (const [remoteJid, messages] of allConvos) {
|
|
7797
|
-
if (messages.length === 0) continue;
|
|
7798
|
-
const isGroup = remoteJid.endsWith("@g.us");
|
|
7799
|
-
const senderTelephone = isGroup ? null : messages[0].senderTelephone;
|
|
7800
|
-
if (!isGroup && senderTelephone && coveredSenderIds.has(senderTelephone)) continue;
|
|
7801
|
-
const last = messages[messages.length - 1];
|
|
7802
|
-
const resolvedName = isGroup ? null : messages.find((m) => m.senderName)?.senderName ?? null;
|
|
7803
|
-
persistedOnlyRows.push({
|
|
7804
|
-
sessionId: `persisted:${account.accountId}:${remoteJid}`,
|
|
7805
|
-
projectDir: "",
|
|
7806
|
-
title: remoteJid,
|
|
7807
|
-
senderId: senderTelephone,
|
|
7808
|
-
startedAt: messages[0].dateSent,
|
|
7809
|
-
channel: "whatsapp",
|
|
7810
|
-
role: "public",
|
|
7811
|
-
operatorName: null,
|
|
7812
|
-
whatsappName: resolvedName,
|
|
7813
|
-
lastMessageAt: last.dateSent,
|
|
7814
|
-
modelGated: false,
|
|
7815
|
-
model: null,
|
|
7816
|
-
rowType: "persisted-only",
|
|
7817
|
-
accountId: account.accountId,
|
|
7818
|
-
remoteJid
|
|
7819
|
-
});
|
|
7820
|
-
}
|
|
7821
|
-
}
|
|
7822
|
-
const allRows = [...conversations, ...persistedOnlyRows];
|
|
7823
|
-
console.log(`[wa-reader] op=full-list accountId=${account?.accountId ?? "null"} sessionRows=${conversations.length} persistedOnlyRows=${persistedOnlyRows.length}`);
|
|
7824
|
-
return c.json({ conversations: allRows });
|
|
7991
|
+
const storeRows = account ? buildStoreConversationRows(account.accountId, readConversationSummaries(account.accountId)) : [];
|
|
7992
|
+
const adminScope = storeRows.filter((r) => r.scope === "admin").length;
|
|
7993
|
+
const publicScope = storeRows.length - adminScope;
|
|
7994
|
+
console.log(
|
|
7995
|
+
`[wa-reader] op=list accountId=${account?.accountId ?? "null"} conversations=${storeRows.length} adminScope=${adminScope} publicScope=${publicScope}`
|
|
7996
|
+
);
|
|
7997
|
+
return c.json({ conversations: [...sessionConversations, ...storeRows] });
|
|
7825
7998
|
});
|
|
7826
7999
|
function readFrom(path2, from) {
|
|
7827
|
-
const end =
|
|
8000
|
+
const end = statSync5(path2).size;
|
|
7828
8001
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
7829
8002
|
const fd = openSync(path2, "r");
|
|
7830
8003
|
try {
|
|
@@ -7941,7 +8114,7 @@ function runActivityTick(c) {
|
|
|
7941
8114
|
let size = 0;
|
|
7942
8115
|
let mtimeMs = 0;
|
|
7943
8116
|
try {
|
|
7944
|
-
const st =
|
|
8117
|
+
const st = statSync5(join14(c.subagentsDir, name));
|
|
7945
8118
|
size = st.size;
|
|
7946
8119
|
mtimeMs = st.mtimeMs;
|
|
7947
8120
|
} catch {
|
|
@@ -8007,7 +8180,7 @@ data: ${JSON.stringify(turn)}
|
|
|
8007
8180
|
start(controller) {
|
|
8008
8181
|
const fileEnd0 = (() => {
|
|
8009
8182
|
try {
|
|
8010
|
-
return
|
|
8183
|
+
return statSync5(jsonlPath).size;
|
|
8011
8184
|
} catch {
|
|
8012
8185
|
return 0;
|
|
8013
8186
|
}
|
|
@@ -8136,7 +8309,7 @@ app4.get("/directives", requireAdminSession, (c) => {
|
|
|
8136
8309
|
const pid = Number(pidPart);
|
|
8137
8310
|
let len = 0;
|
|
8138
8311
|
try {
|
|
8139
|
-
len =
|
|
8312
|
+
len = statSync5(join14(dir, name)).size;
|
|
8140
8313
|
} catch {
|
|
8141
8314
|
}
|
|
8142
8315
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -8161,7 +8334,7 @@ app4.get("/directive", requireAdminSession, (c) => {
|
|
|
8161
8334
|
}
|
|
8162
8335
|
return c.text(body);
|
|
8163
8336
|
});
|
|
8164
|
-
app4.get("/
|
|
8337
|
+
app4.get("/store-stream", requireAdminSession, (c) => {
|
|
8165
8338
|
const accountIdRaw = c.req.query("accountId");
|
|
8166
8339
|
const remoteJid = c.req.query("remoteJid");
|
|
8167
8340
|
if (!accountIdRaw || !remoteJid) {
|
|
@@ -8176,21 +8349,113 @@ app4.get("/persisted-messages", requireAdminSession, async (c) => {
|
|
|
8176
8349
|
if (!isUserJid(remoteJid) && !isGroupJid(remoteJid)) {
|
|
8177
8350
|
return c.json({ error: "invalid remoteJid" }, 400);
|
|
8178
8351
|
}
|
|
8179
|
-
const
|
|
8180
|
-
const
|
|
8352
|
+
const file = conversationFilePath(accountId, remoteJid);
|
|
8353
|
+
const encoder = new TextEncoder();
|
|
8354
|
+
const connId = `${remoteJid.slice(0, 12)}-${process.hrtime.bigint().toString(36).slice(-6)}`;
|
|
8355
|
+
const startedAt = Date.now();
|
|
8356
|
+
const lastEventId = c.req.header("Last-Event-ID");
|
|
8357
|
+
const toTurn = (r) => ({
|
|
8181
8358
|
kind: "whatsapp-persisted",
|
|
8182
|
-
text:
|
|
8183
|
-
ts:
|
|
8184
|
-
fromMe:
|
|
8185
|
-
senderName:
|
|
8186
|
-
})
|
|
8187
|
-
|
|
8188
|
-
|
|
8359
|
+
text: r.body,
|
|
8360
|
+
ts: r.dateSent,
|
|
8361
|
+
fromMe: r.fromMe,
|
|
8362
|
+
senderName: r.senderName
|
|
8363
|
+
});
|
|
8364
|
+
const readable = new ReadableStream({
|
|
8365
|
+
start(controller) {
|
|
8366
|
+
const send = (turn, id) => {
|
|
8367
|
+
try {
|
|
8368
|
+
controller.enqueue(encoder.encode(`id: ${id}
|
|
8369
|
+
data: ${JSON.stringify(turn)}
|
|
8370
|
+
|
|
8371
|
+
`));
|
|
8372
|
+
} catch {
|
|
8373
|
+
}
|
|
8374
|
+
};
|
|
8375
|
+
const fileEnd0 = (() => {
|
|
8376
|
+
try {
|
|
8377
|
+
return statSync5(file).size;
|
|
8378
|
+
} catch {
|
|
8379
|
+
return 0;
|
|
8380
|
+
}
|
|
8381
|
+
})();
|
|
8382
|
+
let offset = resumeOffset(lastEventId, fileEnd0);
|
|
8383
|
+
if (offset === 0) {
|
|
8384
|
+
const records = [];
|
|
8385
|
+
try {
|
|
8386
|
+
const { buf, end } = readFrom(file, 0);
|
|
8387
|
+
offset = end;
|
|
8388
|
+
for (const line of buf.toString("utf8").split("\n")) {
|
|
8389
|
+
if (!line.trim()) continue;
|
|
8390
|
+
try {
|
|
8391
|
+
records.push(JSON.parse(line));
|
|
8392
|
+
} catch {
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
} catch {
|
|
8396
|
+
offset = fileEnd0;
|
|
8397
|
+
}
|
|
8398
|
+
records.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
|
|
8399
|
+
const scope = records.length ? records[records.length - 1].scope : "none";
|
|
8400
|
+
for (const m of records) send(toTurn(m), offset);
|
|
8401
|
+
console.log(
|
|
8402
|
+
`[wa-reader] op=open remoteJid=${remoteJid} scope=${scope} messages=${records.length} source=store`
|
|
8403
|
+
);
|
|
8404
|
+
}
|
|
8405
|
+
const drain = () => {
|
|
8406
|
+
try {
|
|
8407
|
+
const { buf, end } = readFrom(file, offset);
|
|
8408
|
+
if (end <= offset) return;
|
|
8409
|
+
const { lines, nextOffset } = splitNewLines(buf, offset);
|
|
8410
|
+
offset = nextOffset;
|
|
8411
|
+
let n = 0;
|
|
8412
|
+
for (const line of lines) {
|
|
8413
|
+
if (!line.trim()) continue;
|
|
8414
|
+
let rec;
|
|
8415
|
+
try {
|
|
8416
|
+
rec = JSON.parse(line);
|
|
8417
|
+
} catch {
|
|
8418
|
+
continue;
|
|
8419
|
+
}
|
|
8420
|
+
send(toTurn(rec), offset);
|
|
8421
|
+
n++;
|
|
8422
|
+
}
|
|
8423
|
+
if (n > 0) console.log(`[wa-reader] op=store-drain conn=${connId} turns=${n}`);
|
|
8424
|
+
} catch {
|
|
8425
|
+
}
|
|
8426
|
+
};
|
|
8427
|
+
let watcher = null;
|
|
8428
|
+
try {
|
|
8429
|
+
watcher = watch(file, drain);
|
|
8430
|
+
} catch {
|
|
8431
|
+
}
|
|
8432
|
+
const poll = setInterval(drain, 1e3);
|
|
8433
|
+
const heartbeat = setInterval(() => {
|
|
8434
|
+
try {
|
|
8435
|
+
controller.enqueue(encoder.encode(": ping\n\n"));
|
|
8436
|
+
} catch {
|
|
8437
|
+
}
|
|
8438
|
+
}, 2e4);
|
|
8439
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
8440
|
+
watcher?.close();
|
|
8441
|
+
clearInterval(poll);
|
|
8442
|
+
clearInterval(heartbeat);
|
|
8443
|
+
console.log(`[wa-reader] op=store-close conn=${connId} ranMs=${Date.now() - startedAt}`);
|
|
8444
|
+
try {
|
|
8445
|
+
controller.close();
|
|
8446
|
+
} catch {
|
|
8447
|
+
}
|
|
8448
|
+
});
|
|
8449
|
+
}
|
|
8450
|
+
});
|
|
8451
|
+
return new Response(readable, {
|
|
8452
|
+
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
|
|
8453
|
+
});
|
|
8189
8454
|
});
|
|
8190
8455
|
var whatsapp_reader_default = app4;
|
|
8191
8456
|
|
|
8192
8457
|
// server/routes/public-reader.ts
|
|
8193
|
-
import { statSync as
|
|
8458
|
+
import { statSync as statSync6, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync16, readdirSync as readdirSync9 } from "fs";
|
|
8194
8459
|
import { basename as basename5, dirname as dirname4, join as join15, resolve as resolve13, sep as sep4 } from "path";
|
|
8195
8460
|
|
|
8196
8461
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
@@ -8324,7 +8589,7 @@ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
|
8324
8589
|
}
|
|
8325
8590
|
}
|
|
8326
8591
|
function readFrom2(path2, from) {
|
|
8327
|
-
const end =
|
|
8592
|
+
const end = statSync6(path2).size;
|
|
8328
8593
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
8329
8594
|
const fd = openSync2(path2, "r");
|
|
8330
8595
|
try {
|
|
@@ -8487,7 +8752,7 @@ var public_reader_default = app5;
|
|
|
8487
8752
|
// server/routes/webchat.ts
|
|
8488
8753
|
import { basename as basename6, dirname as dirname5 } from "path";
|
|
8489
8754
|
import { join as join18 } from "path";
|
|
8490
|
-
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as renameSync3, statSync as
|
|
8755
|
+
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as renameSync3, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
8491
8756
|
|
|
8492
8757
|
// server/canonical-webchat-override.ts
|
|
8493
8758
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync5, renameSync } from "fs";
|
|
@@ -8757,7 +9022,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
8757
9022
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
8758
9023
|
if (projectDir === null) return null;
|
|
8759
9024
|
try {
|
|
8760
|
-
return
|
|
9025
|
+
return statSync7(join18(projectDir, `${sessionId}.jsonl`)).size;
|
|
8761
9026
|
} catch {
|
|
8762
9027
|
return null;
|
|
8763
9028
|
}
|
|
@@ -8901,7 +9166,7 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
|
|
|
8901
9166
|
if (multiAccount && meta.accountId !== scopeAccountId) continue;
|
|
8902
9167
|
let mtimeMs;
|
|
8903
9168
|
try {
|
|
8904
|
-
mtimeMs =
|
|
9169
|
+
mtimeMs = statSync7(path2).mtimeMs;
|
|
8905
9170
|
} catch {
|
|
8906
9171
|
continue;
|
|
8907
9172
|
}
|
|
@@ -9733,7 +9998,7 @@ import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeF
|
|
|
9733
9998
|
import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
|
|
9734
9999
|
|
|
9735
10000
|
// ../lib/admins-write/src/index.ts
|
|
9736
|
-
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync13, statSync as
|
|
10001
|
+
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync13, statSync as statSync8, appendFileSync as appendFileSync2 } from "fs";
|
|
9737
10002
|
import { dirname as dirname6, join as join22 } from "path";
|
|
9738
10003
|
function id8(value) {
|
|
9739
10004
|
return value.slice(0, 8);
|
|
@@ -9856,7 +10121,7 @@ function computeAdminStoreDivergence(input) {
|
|
|
9856
10121
|
if (entry.startsWith(".")) continue;
|
|
9857
10122
|
const accountDir = join22(input.accountsDir, entry);
|
|
9858
10123
|
try {
|
|
9859
|
-
if (!
|
|
10124
|
+
if (!statSync8(accountDir).isDirectory()) continue;
|
|
9860
10125
|
} catch {
|
|
9861
10126
|
continue;
|
|
9862
10127
|
}
|
|
@@ -10256,7 +10521,7 @@ ${body.newPin}
|
|
|
10256
10521
|
var onboarding_default = app9;
|
|
10257
10522
|
|
|
10258
10523
|
// server/routes/client-error.ts
|
|
10259
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync6, statSync as
|
|
10524
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync6, statSync as statSync9 } from "fs";
|
|
10260
10525
|
import { join as join23 } from "path";
|
|
10261
10526
|
var CLIENT_ERRORS_LOG = join23(LOG_DIR, "client-errors.log");
|
|
10262
10527
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -10307,7 +10572,7 @@ function stackHead(stack) {
|
|
|
10307
10572
|
function rotateIfNeeded() {
|
|
10308
10573
|
try {
|
|
10309
10574
|
if (!existsSync16(CLIENT_ERRORS_LOG)) return;
|
|
10310
|
-
const stats =
|
|
10575
|
+
const stats = statSync9(CLIENT_ERRORS_LOG);
|
|
10311
10576
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
10312
10577
|
renameSync6(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
10313
10578
|
} catch (err) {
|
|
@@ -10613,7 +10878,7 @@ app12.get("/", requireAdminSession, async (c) => {
|
|
|
10613
10878
|
var accounts_default = app12;
|
|
10614
10879
|
|
|
10615
10880
|
// server/routes/admin/logs.ts
|
|
10616
|
-
import { existsSync as existsSync18, readdirSync as readdirSync14, readFileSync as readFileSync25, statSync as
|
|
10881
|
+
import { existsSync as existsSync18, readdirSync as readdirSync14, readFileSync as readFileSync25, statSync as statSync10 } from "fs";
|
|
10617
10882
|
import { resolve as resolve16, basename as basename7 } from "path";
|
|
10618
10883
|
|
|
10619
10884
|
// app/lib/logs-read-resolve.ts
|
|
@@ -10653,7 +10918,7 @@ app13.get("/", async (c) => {
|
|
|
10653
10918
|
searched.push(filePath);
|
|
10654
10919
|
try {
|
|
10655
10920
|
const buffer = readFileSync25(filePath);
|
|
10656
|
-
const onDiskBytes =
|
|
10921
|
+
const onDiskBytes = statSync10(filePath).size;
|
|
10657
10922
|
const headers = {
|
|
10658
10923
|
"Content-Type": "text/plain; charset=utf-8",
|
|
10659
10924
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -10718,7 +10983,7 @@ app13.get("/", async (c) => {
|
|
|
10718
10983
|
try {
|
|
10719
10984
|
const filename = basename7(hit.path);
|
|
10720
10985
|
const buffer = readFileSync25(hit.path);
|
|
10721
|
-
const onDiskBytes =
|
|
10986
|
+
const onDiskBytes = statSync10(hit.path).size;
|
|
10722
10987
|
const headers = {
|
|
10723
10988
|
"Content-Type": "text/plain; charset=utf-8",
|
|
10724
10989
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -10761,7 +11026,7 @@ app13.get("/", async (c) => {
|
|
|
10761
11026
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
10762
11027
|
continue;
|
|
10763
11028
|
}
|
|
10764
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime:
|
|
11029
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync10(resolve16(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
10765
11030
|
seen.add(name);
|
|
10766
11031
|
try {
|
|
10767
11032
|
const content = readFileSync25(resolve16(dir, name));
|
|
@@ -11646,7 +11911,7 @@ app17.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
11646
11911
|
var sessions_default = app17;
|
|
11647
11912
|
|
|
11648
11913
|
// app/lib/claude-agent/spawn-context.ts
|
|
11649
|
-
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync16, statSync as
|
|
11914
|
+
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync16, statSync as statSync11 } from "fs";
|
|
11650
11915
|
import { dirname as dirname7, resolve as resolve19, join as join25 } from "path";
|
|
11651
11916
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
11652
11917
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
@@ -11749,7 +12014,7 @@ function listPluginDirs() {
|
|
|
11749
12014
|
for (const name of readdirSync16(platformPlugins)) {
|
|
11750
12015
|
const dir = join25(platformPlugins, name);
|
|
11751
12016
|
try {
|
|
11752
|
-
if (
|
|
12017
|
+
if (statSync11(dir).isDirectory()) out.set(name, dir);
|
|
11753
12018
|
} catch {
|
|
11754
12019
|
}
|
|
11755
12020
|
}
|
|
@@ -11763,7 +12028,7 @@ function listPluginDirs() {
|
|
|
11763
12028
|
for (const name of readdirSync16(bundlePlugins)) {
|
|
11764
12029
|
const dir = join25(bundlePlugins, name);
|
|
11765
12030
|
try {
|
|
11766
|
-
if (
|
|
12031
|
+
if (statSync11(dir).isDirectory()) out.set(name, dir);
|
|
11767
12032
|
} catch {
|
|
11768
12033
|
}
|
|
11769
12034
|
}
|
|
@@ -18350,7 +18615,7 @@ app48.route("/request-magic-link", request_magic_link_default);
|
|
|
18350
18615
|
var access_default = app48;
|
|
18351
18616
|
|
|
18352
18617
|
// server/routes/sites.ts
|
|
18353
|
-
import { existsSync as existsSync28, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as
|
|
18618
|
+
import { existsSync as existsSync28, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
|
|
18354
18619
|
import { resolve as resolve26 } from "path";
|
|
18355
18620
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
18356
18621
|
var MIME = {
|
|
@@ -18416,7 +18681,7 @@ app49.get("/:rel{.*}", (c) => {
|
|
|
18416
18681
|
}
|
|
18417
18682
|
let stat8;
|
|
18418
18683
|
try {
|
|
18419
|
-
stat8 = existsSync28(filePath) ?
|
|
18684
|
+
stat8 = existsSync28(filePath) ? statSync12(filePath) : null;
|
|
18420
18685
|
} catch {
|
|
18421
18686
|
stat8 = null;
|
|
18422
18687
|
}
|
|
@@ -22112,7 +22377,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
22112
22377
|
|
|
22113
22378
|
// ../lib/entitlement/src/index.ts
|
|
22114
22379
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
22115
|
-
import { existsSync as existsSync32, readFileSync as readFileSync34, statSync as
|
|
22380
|
+
import { existsSync as existsSync32, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
|
|
22116
22381
|
import { resolve as resolve32 } from "path";
|
|
22117
22382
|
|
|
22118
22383
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -22164,7 +22429,7 @@ function resolveEntitlement(brand, account) {
|
|
|
22164
22429
|
if (!existsSync32(entitlementPath)) {
|
|
22165
22430
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
22166
22431
|
}
|
|
22167
|
-
const stat8 =
|
|
22432
|
+
const stat8 = statSync13(entitlementPath);
|
|
22168
22433
|
const key = memoKey(stat8.mtimeMs, account);
|
|
22169
22434
|
if (memo && memo.key === key) {
|
|
22170
22435
|
return memo.result;
|