@rubytech/create-maxy-code 0.1.379 → 0.1.383
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 +23 -8
- package/payload/platform/plugins/docs/references/admin-ui.md +11 -6
- package/payload/platform/plugins/docs/references/attachments.md +5 -1
- 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/scheduling/mcp/dist/lib/__tests__/d1-command.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js +23 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts +12 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js +14 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +3 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-title-stores.d.ts +21 -0
- package/payload/platform/services/claude-session-manager/dist/account-title-stores.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-title-stores.js +71 -0
- package/payload/platform/services/claude-session-manager/dist/account-title-stores.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +2 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +46 -103
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +45 -8
- 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/package.json +2 -1
- package/payload/server/public/assets/{AdminLoginScreens-BHo8w_9J.js → AdminLoginScreens-C32Kg8HN.js} +1 -1
- package/payload/server/public/assets/AdminShell-DizHgGBB.js +1 -0
- package/payload/server/public/assets/{Checkbox-LmjNFccS.js → Checkbox-C-dvkjKb.js} +1 -1
- package/payload/server/public/assets/{admin-BBWILfl8.js → admin-CSBsndZ4.js} +1 -1
- package/payload/server/public/assets/admin-types-hioowVct.js +1 -0
- package/payload/server/public/assets/{browser-DtLvD_ES.js → browser-BUuA2PPl.js} +1 -1
- package/payload/server/public/assets/{calendar-D3i4IqOV.js → calendar-CGlyx54B.js} +1 -1
- package/payload/server/public/assets/chat-DT1PbRkD.js +1 -0
- package/payload/server/public/assets/data-BC4KTIX4.js +1 -0
- package/payload/server/public/assets/{graph-ClQeGEH4.js → graph-CvQZVxwZ.js} +1 -1
- package/payload/server/public/assets/{graph-labels-OaIKHgMg.js → graph-labels-C7bJMcXs.js} +1 -1
- package/payload/server/public/assets/{operator-Dxqfta5U.js → operator-4skjNtEa.js} +1 -1
- package/payload/server/public/assets/{page-Dv_YEOUW.js → page-B0zvkWaG.js} +1 -1
- package/payload/server/public/assets/page-BYlBTwqJ.js +32 -0
- package/payload/server/public/assets/{public-C_mM8sSZ.js → public-ClYyeEA1.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-VF0b6NTt.js → rotate-ccw-DPCP-Lmz.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-B8FEcQNM.js +9 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-VzvzZC7h.css → useSubAccountSwitcher-DqxG4I5C.css} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +4 -4
- package/payload/server/public/chat.html +7 -7
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +7 -7
- package/payload/server/public/index.html +9 -9
- package/payload/server/public/operator.html +9 -9
- package/payload/server/public/public.html +7 -7
- package/payload/server/server.js +301 -82
- package/payload/server/public/assets/AdminShell-DbTijFVA.js +0 -1
- package/payload/server/public/assets/admin-types-DJoj6VJv.js +0 -1
- package/payload/server/public/assets/chat-DU-T6blN.js +0 -1
- package/payload/server/public/assets/data-B_vDxQrO.js +0 -1
- package/payload/server/public/assets/page-BkIb7CHh.js +0 -32
- package/payload/server/public/assets/useSubAccountSwitcher-CQbgG-FH.js +0 -9
package/payload/server/server.js
CHANGED
|
@@ -1886,6 +1886,14 @@ function credsCensus(authDir) {
|
|
|
1886
1886
|
return { credsPresent: true, registered: false, hasAccountSignature: false, e164: null };
|
|
1887
1887
|
}
|
|
1888
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
|
+
}
|
|
1889
1897
|
async function clearAuth(authDir) {
|
|
1890
1898
|
try {
|
|
1891
1899
|
await fs.access(authDir);
|
|
@@ -1897,6 +1905,17 @@ async function clearAuth(authDir) {
|
|
|
1897
1905
|
console.error(`${TAG} cleared credentials at ${authDir}`);
|
|
1898
1906
|
return true;
|
|
1899
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
|
+
}
|
|
1900
1919
|
function listCredentialAccountIds(configDir2) {
|
|
1901
1920
|
const credsRoot = path.join(os.homedir(), configDir2, "credentials", "whatsapp");
|
|
1902
1921
|
try {
|
|
@@ -3154,7 +3173,7 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
3154
3173
|
}
|
|
3155
3174
|
|
|
3156
3175
|
// app/lib/whatsapp/message-store.ts
|
|
3157
|
-
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";
|
|
3158
3177
|
import { dirname, join as join3 } from "path";
|
|
3159
3178
|
var STORE_ROOT = () => join3(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-messages");
|
|
3160
3179
|
function conversationFile(platformAccountId, remoteJid) {
|
|
@@ -3175,6 +3194,7 @@ function parseLines(raw) {
|
|
|
3175
3194
|
}
|
|
3176
3195
|
return out;
|
|
3177
3196
|
}
|
|
3197
|
+
var byDateSentAsc = (a, b) => a.dateSent.localeCompare(b.dateSent);
|
|
3178
3198
|
function appendMessage(platformAccountId, record) {
|
|
3179
3199
|
const file = conversationFile(platformAccountId, record.remoteJid);
|
|
3180
3200
|
try {
|
|
@@ -3209,13 +3229,79 @@ function readConversation(platformAccountId, remoteJid) {
|
|
|
3209
3229
|
const file = conversationFile(platformAccountId, remoteJid);
|
|
3210
3230
|
try {
|
|
3211
3231
|
if (!existsSync3(file)) return [];
|
|
3212
|
-
return parseLines(readFileSync4(file, "utf8")).sort(
|
|
3232
|
+
return parseLines(readFileSync4(file, "utf8")).sort(byDateSentAsc);
|
|
3213
3233
|
} catch (err) {
|
|
3214
3234
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3215
3235
|
console.error(`[whatsapp-message-store] FAIL op=read reason=${reason.slice(0, 100)} remoteJid=${remoteJid}`);
|
|
3216
3236
|
return [];
|
|
3217
3237
|
}
|
|
3218
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
|
+
}
|
|
3219
3305
|
function readAllConversations(platformAccountId) {
|
|
3220
3306
|
const dir = join3(STORE_ROOT(), platformAccountId);
|
|
3221
3307
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -5186,6 +5272,92 @@ function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
|
5186
5272
|
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
5187
5273
|
}
|
|
5188
5274
|
|
|
5275
|
+
// app/lib/doc-to-text.ts
|
|
5276
|
+
import { unzipSync, strFromU8 } from "fflate";
|
|
5277
|
+
var DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
5278
|
+
var ODT = "application/vnd.oasis.opendocument.text";
|
|
5279
|
+
var CONVERTIBLE_DOC_MIME = /* @__PURE__ */ new Set([
|
|
5280
|
+
DOCX,
|
|
5281
|
+
ODT,
|
|
5282
|
+
"text/rtf",
|
|
5283
|
+
"application/rtf"
|
|
5284
|
+
]);
|
|
5285
|
+
function bareMime(mimeType) {
|
|
5286
|
+
return mimeType.split(";")[0].trim().toLowerCase();
|
|
5287
|
+
}
|
|
5288
|
+
function unescapeXml(s) {
|
|
5289
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
5290
|
+
}
|
|
5291
|
+
function unzipEntry(buffer, entry) {
|
|
5292
|
+
const files = unzipSync(new Uint8Array(buffer));
|
|
5293
|
+
const bytes = files[entry];
|
|
5294
|
+
if (!bytes) throw new Error(`zip entry not found: ${entry}`);
|
|
5295
|
+
return strFromU8(bytes);
|
|
5296
|
+
}
|
|
5297
|
+
function docxToText(buffer) {
|
|
5298
|
+
const xml = unzipEntry(buffer, "word/document.xml");
|
|
5299
|
+
const paras = [];
|
|
5300
|
+
for (const p of xml.split("</w:p>")) {
|
|
5301
|
+
const withBreaks = p.replace(/<w:tab\b[^>]*\/?>/g, " ").replace(/<w:br\b[^>]*\/?>/g, "\n");
|
|
5302
|
+
let line = "";
|
|
5303
|
+
for (const m of withBreaks.matchAll(/<w:t\b[^>]*>([\s\S]*?)<\/w:t>/g)) {
|
|
5304
|
+
line += unescapeXml(m[1]);
|
|
5305
|
+
}
|
|
5306
|
+
if (line.length > 0) paras.push(line);
|
|
5307
|
+
}
|
|
5308
|
+
return paras.join("\n");
|
|
5309
|
+
}
|
|
5310
|
+
function odtToText(buffer) {
|
|
5311
|
+
const xml = unzipEntry(buffer, "content.xml");
|
|
5312
|
+
const body = xml.replace(/^[\s\S]*?<office:text\b[^>]*>/, "").replace(/<\/office:text>[\s\S]*$/, "");
|
|
5313
|
+
const withBreaks = body.replace(/<text:line-break\b[^>]*\/?>/g, "\n").replace(/<text:tab\b[^>]*\/?>/g, " ").replace(/<\/text:(?:p|h)>/g, "\n");
|
|
5314
|
+
const stripped = withBreaks.replace(/<[^>]+>/g, "");
|
|
5315
|
+
return unescapeXml(stripped).replace(/\n{3,}/g, "\n\n").trim();
|
|
5316
|
+
}
|
|
5317
|
+
var RTF_DROP_DEST = /^\\(?:\*|fonttbl|colortbl|stylesheet|listtable|info|pict)\b/;
|
|
5318
|
+
function stripRtfDestinations(s) {
|
|
5319
|
+
let out = "";
|
|
5320
|
+
for (let i = 0; i < s.length; i++) {
|
|
5321
|
+
const ch = s[i];
|
|
5322
|
+
if (ch === "\\" && i + 1 < s.length) {
|
|
5323
|
+
out += ch + s[i + 1];
|
|
5324
|
+
i++;
|
|
5325
|
+
continue;
|
|
5326
|
+
}
|
|
5327
|
+
if (ch === "{" && RTF_DROP_DEST.test(s.slice(i + 1))) {
|
|
5328
|
+
let depth = 1;
|
|
5329
|
+
let j = i + 1;
|
|
5330
|
+
for (; j < s.length && depth > 0; j++) {
|
|
5331
|
+
if (s[j] === "\\") {
|
|
5332
|
+
j++;
|
|
5333
|
+
continue;
|
|
5334
|
+
}
|
|
5335
|
+
if (s[j] === "{") depth++;
|
|
5336
|
+
else if (s[j] === "}") depth--;
|
|
5337
|
+
}
|
|
5338
|
+
i = j - 1;
|
|
5339
|
+
continue;
|
|
5340
|
+
}
|
|
5341
|
+
out += ch;
|
|
5342
|
+
}
|
|
5343
|
+
return out;
|
|
5344
|
+
}
|
|
5345
|
+
function rtfToText(rtf) {
|
|
5346
|
+
let s = stripRtfDestinations(rtf);
|
|
5347
|
+
s = s.replace(/\\par[d]?\b/g, "\n").replace(/\\line\b/g, "\n").replace(/\\tab\b/g, " ");
|
|
5348
|
+
s = s.replace(/\\'([0-9a-fA-F]{2})/g, (_m, h) => String.fromCharCode(parseInt(h, 16)));
|
|
5349
|
+
s = s.replace(/\\[a-zA-Z]+-?\d* ?/g, "");
|
|
5350
|
+
s = s.replace(/\\([{}\\])/g, "$1").replace(/[{}]/g, "");
|
|
5351
|
+
return s.replace(/[ \t]{2,}/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
5352
|
+
}
|
|
5353
|
+
function docToText(buffer, mimeType) {
|
|
5354
|
+
const mime = bareMime(mimeType);
|
|
5355
|
+
if (mime === DOCX) return docxToText(buffer);
|
|
5356
|
+
if (mime === ODT) return odtToText(buffer);
|
|
5357
|
+
if (mime === "text/rtf" || mime === "application/rtf") return rtfToText(buffer.toString("latin1"));
|
|
5358
|
+
return null;
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5189
5361
|
// app/lib/attachments.ts
|
|
5190
5362
|
function uploadsDirFor(accountId, subPath, attachmentId) {
|
|
5191
5363
|
return subPath ? resolve7(DATA_ROOT, "accounts", accountId, "uploads", subPath, attachmentId) : resolve7(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
@@ -5203,6 +5375,14 @@ var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
5203
5375
|
"text/calendar",
|
|
5204
5376
|
"application/zip",
|
|
5205
5377
|
"application/x-zip-compressed",
|
|
5378
|
+
// Task 1347 — Word/OpenDocument/RTF text documents. The bytes are converted to
|
|
5379
|
+
// a readable-text sibling at ingestion (writeAttachment → doc-to-text.ts)
|
|
5380
|
+
// because the on-device Read tool cannot parse the binary containers. rtf is
|
|
5381
|
+
// reported by browsers as either text/rtf or application/rtf.
|
|
5382
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5383
|
+
"application/vnd.oasis.opendocument.text",
|
|
5384
|
+
"text/rtf",
|
|
5385
|
+
"application/rtf",
|
|
5206
5386
|
// Task 1240 — voice recordings persist as playable clips in the admin bubble.
|
|
5207
5387
|
// These match AUDIO_MIME_TYPES in app/lib/stt/audio-mime.ts exactly.
|
|
5208
5388
|
"audio/ogg",
|
|
@@ -5218,6 +5398,7 @@ var MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024;
|
|
|
5218
5398
|
var MAX_FILES_PER_MESSAGE = 5;
|
|
5219
5399
|
var MAX_DATA_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;
|
|
5220
5400
|
var MAX_ZIP_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;
|
|
5401
|
+
var EXTRACTED_TEXT_SUFFIX = ".extracted.txt";
|
|
5221
5402
|
function assertSupportedMime(mimeType) {
|
|
5222
5403
|
const bare = mimeType.split(";")[0].trim().toLowerCase();
|
|
5223
5404
|
if (!SUPPORTED_MIME_TYPES.has(bare)) {
|
|
@@ -5243,7 +5424,15 @@ async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes
|
|
|
5243
5424
|
};
|
|
5244
5425
|
await writeFile2(storagePath, buffer);
|
|
5245
5426
|
await writeFile2(metaPath, JSON.stringify(meta, null, 2));
|
|
5246
|
-
|
|
5427
|
+
let readableTextPath;
|
|
5428
|
+
if (CONVERTIBLE_DOC_MIME.has(mimeType.split(";")[0].trim().toLowerCase())) {
|
|
5429
|
+
const text = docToText(buffer, mimeType);
|
|
5430
|
+
if (text) {
|
|
5431
|
+
readableTextPath = resolve7(dir, `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`);
|
|
5432
|
+
await writeFile2(readableTextPath, text);
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
return { attachmentId, filename, storagePath, metaPath, mimeType, sizeBytes, readableTextPath };
|
|
5247
5436
|
}
|
|
5248
5437
|
async function storeAttachment(accountId, file) {
|
|
5249
5438
|
assertSupportedMime(file.type);
|
|
@@ -5284,6 +5473,8 @@ var MIME_BY_EXT = {
|
|
|
5284
5473
|
".zip": "application/zip",
|
|
5285
5474
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
5286
5475
|
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5476
|
+
".odt": "application/vnd.oasis.opendocument.text",
|
|
5477
|
+
".rtf": "text/rtf",
|
|
5287
5478
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
5288
5479
|
".ics": "text/calendar"
|
|
5289
5480
|
};
|
|
@@ -6137,6 +6328,15 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
6137
6328
|
} catch (err) {
|
|
6138
6329
|
const current = activeLogins.get(accountId);
|
|
6139
6330
|
if (current?.id !== login.id) return;
|
|
6331
|
+
await drainCredsSaveQueue();
|
|
6332
|
+
const preRetryCensus = credsCensus(login.authDir);
|
|
6333
|
+
if (!preRetryCensus.hasAccountSignature) {
|
|
6334
|
+
current.error = "WhatsApp accepted the code but did not finish linking. This usually means the number has reached its linked-device limit or has a stale linked device. On the phone, open WhatsApp, Linked Devices, remove existing devices, then ask for a new code.";
|
|
6335
|
+
console.error(
|
|
6336
|
+
`${TAG17} op=pairing-incomplete cid=${cidShort} closeKind=${closeKind(getStatusCode(err), false)} reason=no-account-signature`
|
|
6337
|
+
);
|
|
6338
|
+
return;
|
|
6339
|
+
}
|
|
6140
6340
|
const classification = classifyDisconnect(err);
|
|
6141
6341
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
6142
6342
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
@@ -6349,6 +6549,13 @@ async function waitForLogin(opts) {
|
|
|
6349
6549
|
console.error(`${TAG17} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
|
|
6350
6550
|
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 };
|
|
6351
6551
|
}
|
|
6552
|
+
function listActiveLoginAccountIds() {
|
|
6553
|
+
const ids = [];
|
|
6554
|
+
for (const [accountId, login] of activeLogins) {
|
|
6555
|
+
if (isLoginFresh(login)) ids.push(accountId);
|
|
6556
|
+
}
|
|
6557
|
+
return ids;
|
|
6558
|
+
}
|
|
6352
6559
|
|
|
6353
6560
|
// app/lib/whatsapp/outbound/send-document.ts
|
|
6354
6561
|
import { realpathSync as realpathSync3 } from "fs";
|
|
@@ -6545,6 +6752,7 @@ function serializeWhatsAppSchema() {
|
|
|
6545
6752
|
import { readdirSync as readdirSync3 } from "fs";
|
|
6546
6753
|
import { join as join9 } from "path";
|
|
6547
6754
|
var TAG19 = "[whatsapp:reconcile]";
|
|
6755
|
+
var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
|
|
6548
6756
|
function listCredsAccountIds(credsRoot) {
|
|
6549
6757
|
try {
|
|
6550
6758
|
return readdirSync3(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join9(credsRoot, e.name))).map((e) => e.name);
|
|
@@ -6558,14 +6766,28 @@ function isAccountConfigured(accountDir, accountId) {
|
|
|
6558
6766
|
return !!accounts && Object.prototype.hasOwnProperty.call(accounts, accountId);
|
|
6559
6767
|
}
|
|
6560
6768
|
function reconcileCredsOnDisk(opts) {
|
|
6561
|
-
const { credsRoot, accountDir, liveAccountIds } = opts;
|
|
6769
|
+
const { credsRoot, accountDir, liveAccountIds, activeLoginAccountIds } = opts;
|
|
6562
6770
|
const entries = [];
|
|
6563
6771
|
for (const accountId of listCredsAccountIds(credsRoot)) {
|
|
6564
6772
|
if (liveAccountIds.has(accountId)) continue;
|
|
6565
6773
|
const authDir = join9(credsRoot, accountId);
|
|
6566
6774
|
const census = credsCensus(authDir);
|
|
6567
6775
|
if (census.registered && !census.hasAccountSignature) {
|
|
6568
|
-
|
|
6776
|
+
const inFlight = activeLoginAccountIds.has(accountId);
|
|
6777
|
+
const ageMs = getAuthAgeMs(authDir);
|
|
6778
|
+
const abandoned = !inFlight && ageMs !== null && ageMs >= HALF_REGISTERED_MIN_AGE_MS;
|
|
6779
|
+
if (!abandoned) {
|
|
6780
|
+
console.error(`${TAG19} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight} ageMs=${ageMs ?? "null"}`);
|
|
6781
|
+
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
6782
|
+
continue;
|
|
6783
|
+
}
|
|
6784
|
+
console.error(`${TAG19} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs}`);
|
|
6785
|
+
const removed = discardAuthDirSync(authDir);
|
|
6786
|
+
if (!removed) {
|
|
6787
|
+
console.error(`${TAG19} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
|
|
6788
|
+
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
6789
|
+
}
|
|
6790
|
+
continue;
|
|
6569
6791
|
}
|
|
6570
6792
|
const selfPhone = readSelfId(authDir).e164 ?? void 0;
|
|
6571
6793
|
const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
|
|
@@ -6593,9 +6815,10 @@ app2.get("/status", (c) => {
|
|
|
6593
6815
|
try {
|
|
6594
6816
|
const live = getStatus();
|
|
6595
6817
|
const liveIds = new Set(live.map((a) => a.accountId));
|
|
6818
|
+
const activeLoginIds = new Set(listActiveLoginAccountIds());
|
|
6596
6819
|
const credsRoot = join10(MAXY_DIR, "credentials", "whatsapp");
|
|
6597
6820
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
6598
|
-
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds });
|
|
6821
|
+
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
|
|
6599
6822
|
const accounts = [...live, ...reconciled];
|
|
6600
6823
|
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
|
|
6601
6824
|
console.error(`${TAG20} status accounts=${accounts.length} [${summary}]`);
|
|
@@ -7015,11 +7238,11 @@ app2.get("/group-info", async (c) => {
|
|
|
7015
7238
|
var whatsapp_default = app2;
|
|
7016
7239
|
|
|
7017
7240
|
// server/routes/whatsapp-reader.ts
|
|
7018
|
-
import { readFileSync as readFileSync15, watch, statSync as
|
|
7241
|
+
import { readFileSync as readFileSync15, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync8, readdirSync as readdirSync8, realpathSync as realpathSync4 } from "fs";
|
|
7019
7242
|
import { basename as basename4, dirname as dirname3, isAbsolute, join as join14, relative as relative2, resolve as resolve12, sep as sep3 } from "path";
|
|
7020
7243
|
|
|
7021
7244
|
// server/routes/admin/sidebar-sessions.ts
|
|
7022
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as
|
|
7245
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
7023
7246
|
import { basename as basename3, dirname as dirname2, join as join11, resolve as resolve11 } from "path";
|
|
7024
7247
|
|
|
7025
7248
|
// app/lib/whatsapp-reader/select-sessions.ts
|
|
@@ -7043,25 +7266,22 @@ function selectReaderChannelSessions(rows) {
|
|
|
7043
7266
|
source: "session"
|
|
7044
7267
|
}));
|
|
7045
7268
|
}
|
|
7046
|
-
function buildStoreConversationRows(accountId,
|
|
7269
|
+
function buildStoreConversationRows(accountId, summaries) {
|
|
7047
7270
|
const rows = [];
|
|
7048
|
-
for (const [remoteJid,
|
|
7049
|
-
if (messages.length === 0) continue;
|
|
7050
|
-
const last = messages[messages.length - 1];
|
|
7271
|
+
for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
|
|
7051
7272
|
const scope = last.scope;
|
|
7052
7273
|
const isGroup = remoteJid.endsWith("@g.us");
|
|
7053
|
-
const senderTelephone = isGroup ? null :
|
|
7054
|
-
const senderName = messages.find((m) => m.senderName)?.senderName ?? null;
|
|
7274
|
+
const senderTelephone = isGroup ? null : first.senderTelephone;
|
|
7055
7275
|
rows.push({
|
|
7056
7276
|
sessionId: "",
|
|
7057
7277
|
projectDir: "",
|
|
7058
7278
|
title: remoteJid,
|
|
7059
7279
|
senderId: senderTelephone,
|
|
7060
|
-
startedAt:
|
|
7280
|
+
startedAt: first.dateSent,
|
|
7061
7281
|
channel: "whatsapp",
|
|
7062
7282
|
role: scope === "admin" ? "admin" : "public",
|
|
7063
7283
|
operatorName: null,
|
|
7064
|
-
whatsappName:
|
|
7284
|
+
whatsappName: firstNamedSender,
|
|
7065
7285
|
lastMessageAt: last.dateSent,
|
|
7066
7286
|
modelGated: false,
|
|
7067
7287
|
model: null,
|
|
@@ -7312,26 +7532,10 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7312
7532
|
const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
7313
7533
|
const validAccounts = listValidAccounts();
|
|
7314
7534
|
const multiAccount = validAccounts.length > 1;
|
|
7315
|
-
const accounts = [];
|
|
7316
|
-
for (const a of validAccounts) {
|
|
7317
|
-
let businessName = null;
|
|
7318
|
-
try {
|
|
7319
|
-
const branding = await fetchBranding(a.accountId);
|
|
7320
|
-
businessName = branding?.name || null;
|
|
7321
|
-
} catch {
|
|
7322
|
-
}
|
|
7323
|
-
accounts.push({
|
|
7324
|
-
accountId: a.accountId,
|
|
7325
|
-
role: a.config.role ?? "",
|
|
7326
|
-
businessName,
|
|
7327
|
-
isHouse: a.config.role === "house"
|
|
7328
|
-
});
|
|
7329
|
-
}
|
|
7330
|
-
console.log(`[admin-sessions-list] accounts=${accounts.length}`);
|
|
7331
7535
|
const configDir2 = claudeConfigDir();
|
|
7332
7536
|
if (!configDir2) {
|
|
7333
7537
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
7334
|
-
return c.json({ sessions: [], accountId: scopeAccountId
|
|
7538
|
+
return c.json({ sessions: [], accountId: scopeAccountId });
|
|
7335
7539
|
}
|
|
7336
7540
|
const projectsRoot = join11(configDir2, "projects");
|
|
7337
7541
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
@@ -7362,7 +7566,7 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7362
7566
|
let mtimeMs;
|
|
7363
7567
|
try {
|
|
7364
7568
|
body = readFileSync12(path2, "utf8");
|
|
7365
|
-
mtimeMs =
|
|
7569
|
+
mtimeMs = statSync3(path2).mtimeMs;
|
|
7366
7570
|
} catch {
|
|
7367
7571
|
continue;
|
|
7368
7572
|
}
|
|
@@ -7440,13 +7644,13 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7440
7644
|
if (multiAccount) {
|
|
7441
7645
|
console.log(`[admin-sessions-list] untagged-accountId-rows=${untaggedCount}`);
|
|
7442
7646
|
}
|
|
7443
|
-
return c.json({ sessions: rows, accountId: scopeAccountId
|
|
7647
|
+
return c.json({ sessions: rows, accountId: scopeAccountId });
|
|
7444
7648
|
});
|
|
7445
7649
|
var sidebar_sessions_default = app3;
|
|
7446
7650
|
|
|
7447
7651
|
// app/lib/admin-identity/pin-validator.ts
|
|
7448
7652
|
import { createHash } from "crypto";
|
|
7449
|
-
import { existsSync as existsSync7, readFileSync as readFileSync13, readdirSync as readdirSync6, statSync as
|
|
7653
|
+
import { existsSync as existsSync7, readFileSync as readFileSync13, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
7450
7654
|
import { join as join12 } from "path";
|
|
7451
7655
|
function hashPin(pin) {
|
|
7452
7656
|
return createHash("sha256").update(pin).digest("hex");
|
|
@@ -7834,16 +8038,29 @@ function pickActivity(entries, grown, nowMs, concurrencyWindowMs) {
|
|
|
7834
8038
|
// server/routes/whatsapp-reader.ts
|
|
7835
8039
|
var app4 = new Hono();
|
|
7836
8040
|
app4.get("/conversations", requireAdminSession, async (c) => {
|
|
8041
|
+
const cacheKey = c.var.cacheKey;
|
|
8042
|
+
const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
8043
|
+
const validAccounts = listValidAccounts();
|
|
8044
|
+
const multiAccount = validAccounts.length > 1;
|
|
8045
|
+
if (multiAccount && !scopeAccountId) {
|
|
8046
|
+
console.log(`[wa-reader] op=list-scope-unresolved cacheKey=${cacheKey.slice(0, 8)}`);
|
|
8047
|
+
return c.json({ conversations: [] });
|
|
8048
|
+
}
|
|
7837
8049
|
const cfg = claudeConfigDir();
|
|
7838
8050
|
if (!cfg) return c.json({ conversations: [] });
|
|
7839
8051
|
const projectsRoot = join14(cfg, "projects");
|
|
7840
8052
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
7841
8053
|
const rows = [];
|
|
8054
|
+
let sessionRowsExcludedUntagged = 0;
|
|
7842
8055
|
for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
|
|
7843
8056
|
const sessionId = basename4(path2).replace(/\.jsonl$/, "");
|
|
7844
8057
|
const projectDir = dirname3(path2);
|
|
7845
8058
|
const meta = readSidecarMeta(join14(projectDir, `${sessionId}.meta.json`));
|
|
7846
8059
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
8060
|
+
if (multiAccount) {
|
|
8061
|
+
if (meta.accountId === null) sessionRowsExcludedUntagged += 1;
|
|
8062
|
+
if (meta.accountId !== scopeAccountId) continue;
|
|
8063
|
+
}
|
|
7847
8064
|
let body = "";
|
|
7848
8065
|
try {
|
|
7849
8066
|
body = readFileSync15(path2, "utf8");
|
|
@@ -7868,7 +8085,7 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7868
8085
|
});
|
|
7869
8086
|
}
|
|
7870
8087
|
const sessionRows = selectReaderChannelSessions(rows).filter((r) => r.channel !== "whatsapp");
|
|
7871
|
-
const account = resolveAccount();
|
|
8088
|
+
const account = multiAccount ? validAccounts.find((a) => a.accountId === scopeAccountId) ?? null : resolveAccount();
|
|
7872
8089
|
let users = [];
|
|
7873
8090
|
if (account) {
|
|
7874
8091
|
try {
|
|
@@ -7898,16 +8115,17 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7898
8115
|
...r,
|
|
7899
8116
|
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
7900
8117
|
}));
|
|
7901
|
-
const storeRows = account ? buildStoreConversationRows(account.accountId,
|
|
8118
|
+
const storeRows = account ? buildStoreConversationRows(account.accountId, readConversationSummaries(account.accountId)) : [];
|
|
7902
8119
|
const adminScope = storeRows.filter((r) => r.scope === "admin").length;
|
|
7903
8120
|
const publicScope = storeRows.length - adminScope;
|
|
8121
|
+
const sessionRowCount = sessionConversations.length;
|
|
7904
8122
|
console.log(
|
|
7905
|
-
`[wa-reader] op=list
|
|
8123
|
+
`[wa-reader] op=list pinnedAccount=${scopeAccountId?.slice(0, 8) ?? "null"} resolvedAccount=${account?.accountId.slice(0, 8) ?? "null"} multiAccount=${multiAccount} conversations=${sessionRowCount + storeRows.length} sessionRows=${sessionRowCount} storeRows=${storeRows.length} adminScope=${adminScope} publicScope=${publicScope} sessionRowsExcludedUntagged=${sessionRowsExcludedUntagged}`
|
|
7906
8124
|
);
|
|
7907
8125
|
return c.json({ conversations: [...sessionConversations, ...storeRows] });
|
|
7908
8126
|
});
|
|
7909
8127
|
function readFrom(path2, from) {
|
|
7910
|
-
const end =
|
|
8128
|
+
const end = statSync5(path2).size;
|
|
7911
8129
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
7912
8130
|
const fd = openSync(path2, "r");
|
|
7913
8131
|
try {
|
|
@@ -8024,7 +8242,7 @@ function runActivityTick(c) {
|
|
|
8024
8242
|
let size = 0;
|
|
8025
8243
|
let mtimeMs = 0;
|
|
8026
8244
|
try {
|
|
8027
|
-
const st =
|
|
8245
|
+
const st = statSync5(join14(c.subagentsDir, name));
|
|
8028
8246
|
size = st.size;
|
|
8029
8247
|
mtimeMs = st.mtimeMs;
|
|
8030
8248
|
} catch {
|
|
@@ -8090,7 +8308,7 @@ data: ${JSON.stringify(turn)}
|
|
|
8090
8308
|
start(controller) {
|
|
8091
8309
|
const fileEnd0 = (() => {
|
|
8092
8310
|
try {
|
|
8093
|
-
return
|
|
8311
|
+
return statSync5(jsonlPath).size;
|
|
8094
8312
|
} catch {
|
|
8095
8313
|
return 0;
|
|
8096
8314
|
}
|
|
@@ -8219,7 +8437,7 @@ app4.get("/directives", requireAdminSession, (c) => {
|
|
|
8219
8437
|
const pid = Number(pidPart);
|
|
8220
8438
|
let len = 0;
|
|
8221
8439
|
try {
|
|
8222
|
-
len =
|
|
8440
|
+
len = statSync5(join14(dir, name)).size;
|
|
8223
8441
|
} catch {
|
|
8224
8442
|
}
|
|
8225
8443
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -8284,7 +8502,7 @@ data: ${JSON.stringify(turn)}
|
|
|
8284
8502
|
};
|
|
8285
8503
|
const fileEnd0 = (() => {
|
|
8286
8504
|
try {
|
|
8287
|
-
return
|
|
8505
|
+
return statSync5(file).size;
|
|
8288
8506
|
} catch {
|
|
8289
8507
|
return 0;
|
|
8290
8508
|
}
|
|
@@ -8365,7 +8583,7 @@ data: ${JSON.stringify(turn)}
|
|
|
8365
8583
|
var whatsapp_reader_default = app4;
|
|
8366
8584
|
|
|
8367
8585
|
// server/routes/public-reader.ts
|
|
8368
|
-
import { statSync as
|
|
8586
|
+
import { statSync as statSync6, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync16, readdirSync as readdirSync9 } from "fs";
|
|
8369
8587
|
import { basename as basename5, dirname as dirname4, join as join15, resolve as resolve13, sep as sep4 } from "path";
|
|
8370
8588
|
|
|
8371
8589
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
@@ -8499,7 +8717,7 @@ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
|
8499
8717
|
}
|
|
8500
8718
|
}
|
|
8501
8719
|
function readFrom2(path2, from) {
|
|
8502
|
-
const end =
|
|
8720
|
+
const end = statSync6(path2).size;
|
|
8503
8721
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
8504
8722
|
const fd = openSync2(path2, "r");
|
|
8505
8723
|
try {
|
|
@@ -8640,7 +8858,7 @@ app5.get("/attachment/:attachmentId", (c) => {
|
|
|
8640
8858
|
let dataFile;
|
|
8641
8859
|
let buffer;
|
|
8642
8860
|
try {
|
|
8643
|
-
dataFile = readdirSync9(dir).find((f) => !f.endsWith(".meta.json"));
|
|
8861
|
+
dataFile = readdirSync9(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
|
|
8644
8862
|
if (!dataFile) throw new Error("no data file");
|
|
8645
8863
|
buffer = readFileSync16(resolve13(dir, dataFile));
|
|
8646
8864
|
} catch {
|
|
@@ -8662,7 +8880,7 @@ var public_reader_default = app5;
|
|
|
8662
8880
|
// server/routes/webchat.ts
|
|
8663
8881
|
import { basename as basename6, dirname as dirname5 } from "path";
|
|
8664
8882
|
import { join as join18 } from "path";
|
|
8665
|
-
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as renameSync3, statSync as
|
|
8883
|
+
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as renameSync3, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
8666
8884
|
|
|
8667
8885
|
// server/canonical-webchat-override.ts
|
|
8668
8886
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync5, renameSync } from "fs";
|
|
@@ -8932,7 +9150,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
8932
9150
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
8933
9151
|
if (projectDir === null) return null;
|
|
8934
9152
|
try {
|
|
8935
|
-
return
|
|
9153
|
+
return statSync7(join18(projectDir, `${sessionId}.jsonl`)).size;
|
|
8936
9154
|
} catch {
|
|
8937
9155
|
return null;
|
|
8938
9156
|
}
|
|
@@ -9076,7 +9294,7 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
|
|
|
9076
9294
|
if (multiAccount && meta.accountId !== scopeAccountId) continue;
|
|
9077
9295
|
let mtimeMs;
|
|
9078
9296
|
try {
|
|
9079
|
-
mtimeMs =
|
|
9297
|
+
mtimeMs = statSync7(path2).mtimeMs;
|
|
9080
9298
|
} catch {
|
|
9081
9299
|
continue;
|
|
9082
9300
|
}
|
|
@@ -9269,8 +9487,13 @@ ${result.result.text}` : result.result.text;
|
|
|
9269
9487
|
console.error(`[webchat:inbound] op=send-refused kind=files reason=store-failed detail=${detail}`);
|
|
9270
9488
|
return c.json({ error: "failed to store attachment" }, 500);
|
|
9271
9489
|
}
|
|
9490
|
+
for (const s of stored) {
|
|
9491
|
+
const dot = s.filename.lastIndexOf(".");
|
|
9492
|
+
const ext = dot >= 0 ? s.filename.slice(dot).toLowerCase() : "";
|
|
9493
|
+
console.log(`[attach-ingest] mime=${s.mimeType} ext=${ext} converted=${!!s.readableTextPath} path=${s.storagePath}`);
|
|
9494
|
+
}
|
|
9272
9495
|
if (stored.length > 0) {
|
|
9273
|
-
const listing = stored.map((s) => `${s.filename} (${s.storagePath})`).join(", ");
|
|
9496
|
+
const listing = stored.map((s) => `${s.filename} (${s.readableTextPath ?? s.storagePath})`).join(", ");
|
|
9274
9497
|
const note = `[The operator uploaded ${stored.length} file(s): ${listing}]`;
|
|
9275
9498
|
text = text.length > 0 ? `${text}
|
|
9276
9499
|
|
|
@@ -9908,7 +10131,7 @@ import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeF
|
|
|
9908
10131
|
import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
|
|
9909
10132
|
|
|
9910
10133
|
// ../lib/admins-write/src/index.ts
|
|
9911
|
-
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync13, statSync as
|
|
10134
|
+
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";
|
|
9912
10135
|
import { dirname as dirname6, join as join22 } from "path";
|
|
9913
10136
|
function id8(value) {
|
|
9914
10137
|
return value.slice(0, 8);
|
|
@@ -10031,7 +10254,7 @@ function computeAdminStoreDivergence(input) {
|
|
|
10031
10254
|
if (entry.startsWith(".")) continue;
|
|
10032
10255
|
const accountDir = join22(input.accountsDir, entry);
|
|
10033
10256
|
try {
|
|
10034
|
-
if (!
|
|
10257
|
+
if (!statSync8(accountDir).isDirectory()) continue;
|
|
10035
10258
|
} catch {
|
|
10036
10259
|
continue;
|
|
10037
10260
|
}
|
|
@@ -10431,7 +10654,7 @@ ${body.newPin}
|
|
|
10431
10654
|
var onboarding_default = app9;
|
|
10432
10655
|
|
|
10433
10656
|
// server/routes/client-error.ts
|
|
10434
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync6, statSync as
|
|
10657
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync6, statSync as statSync9 } from "fs";
|
|
10435
10658
|
import { join as join23 } from "path";
|
|
10436
10659
|
var CLIENT_ERRORS_LOG = join23(LOG_DIR, "client-errors.log");
|
|
10437
10660
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -10482,7 +10705,7 @@ function stackHead(stack) {
|
|
|
10482
10705
|
function rotateIfNeeded() {
|
|
10483
10706
|
try {
|
|
10484
10707
|
if (!existsSync16(CLIENT_ERRORS_LOG)) return;
|
|
10485
|
-
const stats =
|
|
10708
|
+
const stats = statSync9(CLIENT_ERRORS_LOG);
|
|
10486
10709
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
10487
10710
|
renameSync6(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
10488
10711
|
} catch (err) {
|
|
@@ -10788,7 +11011,7 @@ app12.get("/", requireAdminSession, async (c) => {
|
|
|
10788
11011
|
var accounts_default = app12;
|
|
10789
11012
|
|
|
10790
11013
|
// server/routes/admin/logs.ts
|
|
10791
|
-
import { existsSync as existsSync18, readdirSync as readdirSync14, readFileSync as readFileSync25, statSync as
|
|
11014
|
+
import { existsSync as existsSync18, readdirSync as readdirSync14, readFileSync as readFileSync25, statSync as statSync10 } from "fs";
|
|
10792
11015
|
import { resolve as resolve16, basename as basename7 } from "path";
|
|
10793
11016
|
|
|
10794
11017
|
// app/lib/logs-read-resolve.ts
|
|
@@ -10828,7 +11051,7 @@ app13.get("/", async (c) => {
|
|
|
10828
11051
|
searched.push(filePath);
|
|
10829
11052
|
try {
|
|
10830
11053
|
const buffer = readFileSync25(filePath);
|
|
10831
|
-
const onDiskBytes =
|
|
11054
|
+
const onDiskBytes = statSync10(filePath).size;
|
|
10832
11055
|
const headers = {
|
|
10833
11056
|
"Content-Type": "text/plain; charset=utf-8",
|
|
10834
11057
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -10893,7 +11116,7 @@ app13.get("/", async (c) => {
|
|
|
10893
11116
|
try {
|
|
10894
11117
|
const filename = basename7(hit.path);
|
|
10895
11118
|
const buffer = readFileSync25(hit.path);
|
|
10896
|
-
const onDiskBytes =
|
|
11119
|
+
const onDiskBytes = statSync10(hit.path).size;
|
|
10897
11120
|
const headers = {
|
|
10898
11121
|
"Content-Type": "text/plain; charset=utf-8",
|
|
10899
11122
|
"Content-Length": String(buffer.byteLength)
|
|
@@ -10936,7 +11159,7 @@ app13.get("/", async (c) => {
|
|
|
10936
11159
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
10937
11160
|
continue;
|
|
10938
11161
|
}
|
|
10939
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime:
|
|
11162
|
+
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 }) => {
|
|
10940
11163
|
seen.add(name);
|
|
10941
11164
|
try {
|
|
10942
11165
|
const content = readFileSync25(resolve16(dir, name));
|
|
@@ -11028,7 +11251,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11028
11251
|
return new Response("Not found", { status: 404 });
|
|
11029
11252
|
}
|
|
11030
11253
|
const entries = await readdir(dir);
|
|
11031
|
-
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11254
|
+
const dataFile = entries.find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
|
|
11032
11255
|
if (!dataFile) {
|
|
11033
11256
|
return new Response("Not found", { status: 404 });
|
|
11034
11257
|
}
|
|
@@ -11821,7 +12044,7 @@ app17.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
11821
12044
|
var sessions_default = app17;
|
|
11822
12045
|
|
|
11823
12046
|
// app/lib/claude-agent/spawn-context.ts
|
|
11824
|
-
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync16, statSync as
|
|
12047
|
+
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync16, statSync as statSync11 } from "fs";
|
|
11825
12048
|
import { dirname as dirname7, resolve as resolve19, join as join25 } from "path";
|
|
11826
12049
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
11827
12050
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
@@ -11924,7 +12147,7 @@ function listPluginDirs() {
|
|
|
11924
12147
|
for (const name of readdirSync16(platformPlugins)) {
|
|
11925
12148
|
const dir = join25(platformPlugins, name);
|
|
11926
12149
|
try {
|
|
11927
|
-
if (
|
|
12150
|
+
if (statSync11(dir).isDirectory()) out.set(name, dir);
|
|
11928
12151
|
} catch {
|
|
11929
12152
|
}
|
|
11930
12153
|
}
|
|
@@ -11938,7 +12161,7 @@ function listPluginDirs() {
|
|
|
11938
12161
|
for (const name of readdirSync16(bundlePlugins)) {
|
|
11939
12162
|
const dir = join25(bundlePlugins, name);
|
|
11940
12163
|
try {
|
|
11941
|
-
if (
|
|
12164
|
+
if (statSync11(dir).isDirectory()) out.set(name, dir);
|
|
11942
12165
|
} catch {
|
|
11943
12166
|
}
|
|
11944
12167
|
}
|
|
@@ -13508,11 +13731,11 @@ app21.get("/download", requireAdminSession, async (c) => {
|
|
|
13508
13731
|
const nodeStream = createReadStream2(absolute);
|
|
13509
13732
|
const webStream = Readable2.toWeb(nodeStream);
|
|
13510
13733
|
if (disposition === "inline") {
|
|
13511
|
-
if (!mimeType.startsWith("image/")) {
|
|
13512
|
-
console.error(`[data] op=
|
|
13513
|
-
return c.json({ error: "Inline serve is image-only" }, 400);
|
|
13734
|
+
if (!mimeType.startsWith("image/") && mimeType !== "application/pdf") {
|
|
13735
|
+
console.error(`[data] op=inline-refused path="${relPath}" mime="${mimeType}" reason=not-image-or-pdf`);
|
|
13736
|
+
return c.json({ error: "Inline serve is image- or PDF-only" }, 400);
|
|
13514
13737
|
}
|
|
13515
|
-
console.error(`[data] op=
|
|
13738
|
+
console.error(`[data] op=inline-serve path="${relPath}" mime="${mimeType}" size=${info.size} status=200`);
|
|
13516
13739
|
return new Response(webStream, {
|
|
13517
13740
|
status: 200,
|
|
13518
13741
|
headers: {
|
|
@@ -16456,10 +16679,8 @@ app29.post("/", requireAdminSession, async (c) => {
|
|
|
16456
16679
|
return c.json({ error: "sessionId must be a v4 UUID" }, 400);
|
|
16457
16680
|
}
|
|
16458
16681
|
const base = managerBase("session-delete:wrapper");
|
|
16459
|
-
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
16460
|
-
const acctQuery = accountId ? `?accountId=${encodeURIComponent(accountId)}` : "";
|
|
16461
16682
|
try {
|
|
16462
|
-
const stop = await fetch(`${base}/${sessionId}/stop
|
|
16683
|
+
const stop = await fetch(`${base}/${sessionId}/stop`, { method: "POST" });
|
|
16463
16684
|
if (stop.status !== 204 && stop.status !== 200 && stop.status !== 404) {
|
|
16464
16685
|
console.error(`[session-delete] sessionId=${sessionId} stop-status=${stop.status}`);
|
|
16465
16686
|
return c.json({ error: `stop-failed-${stop.status}` }, 502);
|
|
@@ -16470,7 +16691,7 @@ app29.post("/", requireAdminSession, async (c) => {
|
|
|
16470
16691
|
}
|
|
16471
16692
|
let del;
|
|
16472
16693
|
try {
|
|
16473
|
-
del = await fetch(`${base}/${sessionId}
|
|
16694
|
+
del = await fetch(`${base}/${sessionId}`, { method: "DELETE" });
|
|
16474
16695
|
} catch (err) {
|
|
16475
16696
|
console.error(`[session-delete] sessionId=${sessionId} delete-unreachable err=${err instanceof Error ? err.message : String(err)}`);
|
|
16476
16697
|
return c.json({ error: "manager-unreachable" }, 502);
|
|
@@ -16499,11 +16720,9 @@ app30.post("/", requireAdminSession, async (c) => {
|
|
|
16499
16720
|
if (!SESSION_ID_RE2.test(sessionId)) {
|
|
16500
16721
|
return c.json({ error: "sessionId must be a v4 UUID" }, 400);
|
|
16501
16722
|
}
|
|
16502
|
-
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
16503
|
-
const acctQuery = accountId ? `?accountId=${encodeURIComponent(accountId)}` : "";
|
|
16504
16723
|
let res;
|
|
16505
16724
|
try {
|
|
16506
|
-
res = await fetch(`${managerBase("session-stop:wrapper")}/${sessionId}/stop
|
|
16725
|
+
res = await fetch(`${managerBase("session-stop:wrapper")}/${sessionId}/stop`, { method: "POST" });
|
|
16507
16726
|
} catch (err) {
|
|
16508
16727
|
console.error(`[session-stop] sessionId=${sessionId} manager-unreachable err=${err instanceof Error ? err.message : String(err)}`);
|
|
16509
16728
|
return c.json({ error: "manager-unreachable" }, 502);
|
|
@@ -18525,7 +18744,7 @@ app48.route("/request-magic-link", request_magic_link_default);
|
|
|
18525
18744
|
var access_default = app48;
|
|
18526
18745
|
|
|
18527
18746
|
// server/routes/sites.ts
|
|
18528
|
-
import { existsSync as existsSync28, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as
|
|
18747
|
+
import { existsSync as existsSync28, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
|
|
18529
18748
|
import { resolve as resolve26 } from "path";
|
|
18530
18749
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
18531
18750
|
var MIME = {
|
|
@@ -18591,7 +18810,7 @@ app49.get("/:rel{.*}", (c) => {
|
|
|
18591
18810
|
}
|
|
18592
18811
|
let stat8;
|
|
18593
18812
|
try {
|
|
18594
|
-
stat8 = existsSync28(filePath) ?
|
|
18813
|
+
stat8 = existsSync28(filePath) ? statSync12(filePath) : null;
|
|
18595
18814
|
} catch {
|
|
18596
18815
|
stat8 = null;
|
|
18597
18816
|
}
|
|
@@ -22287,7 +22506,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
22287
22506
|
|
|
22288
22507
|
// ../lib/entitlement/src/index.ts
|
|
22289
22508
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
22290
|
-
import { existsSync as existsSync32, readFileSync as readFileSync34, statSync as
|
|
22509
|
+
import { existsSync as existsSync32, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
|
|
22291
22510
|
import { resolve as resolve32 } from "path";
|
|
22292
22511
|
|
|
22293
22512
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -22339,7 +22558,7 @@ function resolveEntitlement(brand, account) {
|
|
|
22339
22558
|
if (!existsSync32(entitlementPath)) {
|
|
22340
22559
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
22341
22560
|
}
|
|
22342
|
-
const stat8 =
|
|
22561
|
+
const stat8 = statSync13(entitlementPath);
|
|
22343
22562
|
const key = memoKey(stat8.mtimeMs, account);
|
|
22344
22563
|
if (memo && memo.key === key) {
|
|
22345
22564
|
return memo.result;
|
|
@@ -22766,14 +22985,14 @@ var chatRoutes = createChatRoutes({
|
|
|
22766
22985
|
awaitReply: (key, timeoutMs) => webchatGateway.awaitReply(key, timeoutMs)
|
|
22767
22986
|
});
|
|
22768
22987
|
app55.use("*", clientIpMiddleware);
|
|
22988
|
+
function allowsSameOriginFraming(path2) {
|
|
22989
|
+
return path2 === "/vnc-viewer.html" || path2.startsWith("/api/admin/attachment/") || path2.startsWith("/api/public-reader/attachment/") || path2 === "/api/admin/files/download";
|
|
22990
|
+
}
|
|
22769
22991
|
app55.use("*", async (c, next) => {
|
|
22770
22992
|
await next();
|
|
22771
22993
|
c.header("X-Content-Type-Options", "nosniff");
|
|
22772
22994
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
22773
|
-
c.header(
|
|
22774
|
-
"X-Frame-Options",
|
|
22775
|
-
c.req.path === "/vnc-viewer.html" ? "SAMEORIGIN" : "DENY"
|
|
22776
|
-
);
|
|
22995
|
+
c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
|
|
22777
22996
|
});
|
|
22778
22997
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
22779
22998
|
app55.use("*", async (c, next) => {
|