@rubytech/create-sitedesk-code 0.1.511 → 0.1.513
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-27-task-2028-declared-file-write-deny.md +303 -0
- package/payload/platform/docs/superpowers/specs/2026-07-27-task-2028-declared-file-write-deny-design.md +126 -0
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts +5 -0
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/account-schema-regions/dist/index.js +5 -0
- package/payload/platform/lib/account-schema-regions/dist/index.js.map +1 -1
- package/payload/platform/lib/account-schema-regions/src/index.ts +5 -0
- package/payload/platform/package.json +3 -0
- package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard-bash.test.sh +14 -0
- package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +38 -0
- package/payload/platform/plugins/admin/hooks/fs-schema-guard-bash-post.sh +11 -0
- package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +25 -1
- package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +2 -2
- package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +15 -34
- package/payload/platform/plugins/business-assistant/skills/e-sign/stamp.mjs +269 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +1 -1
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +5 -5
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +2 -2
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +26 -0
- package/payload/platform/scripts/check-no-esm-require.mjs +6 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +40 -4
- package/payload/platform/scripts/logs-read.sh +154 -50
- package/payload/platform/scripts/logs-read.test.sh +276 -0
- package/payload/platform/scripts/provision-worktree.sh +80 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +13 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +7 -3
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +17 -11
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/server/{chunk-2WAXM5N2.js → chunk-S6HYTIU3.js} +2 -3
- package/payload/server/maxy-edge.js +7 -3
- package/payload/server/public/assets/admin-6QN-k3zQ.js +1 -0
- package/payload/server/public/assets/admin-6QN-k3zQ.js.br +0 -0
- package/payload/server/public/assets/admin-6QN-k3zQ.js.gz +0 -0
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +860 -384
- package/payload/server/public/assets/admin-Do53K90C.js +0 -1
- package/payload/server/public/assets/admin-Do53K90C.js.br +0 -0
- package/payload/server/public/assets/admin-Do53K90C.js.gz +0 -0
package/payload/server/server.js
CHANGED
|
@@ -118,7 +118,7 @@ import {
|
|
|
118
118
|
vncLog,
|
|
119
119
|
walkPremiumBundles,
|
|
120
120
|
writeAdminUserAndPerson
|
|
121
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-S6HYTIU3.js";
|
|
122
122
|
import {
|
|
123
123
|
D1_MAX_SQL_STATEMENT_BYTES,
|
|
124
124
|
D1_QUERY_MAX_BODY_BYTES,
|
|
@@ -1602,10 +1602,10 @@ var require_dist5 = __commonJS({
|
|
|
1602
1602
|
});
|
|
1603
1603
|
|
|
1604
1604
|
// server/index.ts
|
|
1605
|
-
import { readFileSync as readFileSync48, existsSync as
|
|
1605
|
+
import { readFileSync as readFileSync48, existsSync as existsSync48, watchFile, readdirSync as readdirSync29, statSync as statSync21 } from "fs";
|
|
1606
1606
|
import { spawn as spawn3 } from "child_process";
|
|
1607
1607
|
import { createHash as createHash8 } from "crypto";
|
|
1608
|
-
import { resolve as resolve45, join as
|
|
1608
|
+
import { resolve as resolve45, join as join51, basename as basename14 } from "path";
|
|
1609
1609
|
import { homedir as homedir4 } from "os";
|
|
1610
1610
|
|
|
1611
1611
|
// server/lib/route-cache-watchers.ts
|
|
@@ -2471,6 +2471,44 @@ var assetsVary = async (c, next) => {
|
|
|
2471
2471
|
if (!c.res.headers.has("Vary")) c.res.headers.set("Vary", "Accept-Encoding");
|
|
2472
2472
|
};
|
|
2473
2473
|
|
|
2474
|
+
// server/lib/client-error-reporter.ts
|
|
2475
|
+
var CLIENT_ERROR_SINK_PATH = "/api/_client-error";
|
|
2476
|
+
var clientErrorReporterBody = `(function(){
|
|
2477
|
+
var SINK=${JSON.stringify(CLIENT_ERROR_SINK_PATH)};
|
|
2478
|
+
var send=function(b){
|
|
2479
|
+
try{
|
|
2480
|
+
fetch(SINK,{
|
|
2481
|
+
method:'POST',
|
|
2482
|
+
headers:{'content-type':'application/json'},
|
|
2483
|
+
body:JSON.stringify(Object.assign({url:location.href,ua:navigator.userAgent,version:(window.__MAXY_VERSION__||'unknown')},b)),
|
|
2484
|
+
keepalive:true
|
|
2485
|
+
}).catch(function(){});
|
|
2486
|
+
}catch(e){}
|
|
2487
|
+
};
|
|
2488
|
+
window.addEventListener('error',function(e){
|
|
2489
|
+
var t=e.target;
|
|
2490
|
+
if(t&&t!==window&&t.tagName)send({kind:'subresource',tag:t.tagName,url:t.src||t.href||''});
|
|
2491
|
+
else send({kind:'uncaught',msg:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,stack:(e.error&&e.error.stack)||''});
|
|
2492
|
+
},true);
|
|
2493
|
+
window.addEventListener('unhandledrejection',function(e){
|
|
2494
|
+
var r=e.reason;
|
|
2495
|
+
send({kind:'unhandled-rejection',msg:String((r&&r.message)||r),stack:(r&&r.stack)||''});
|
|
2496
|
+
});
|
|
2497
|
+
try{
|
|
2498
|
+
new PerformanceObserver(function(list){
|
|
2499
|
+
list.getEntries().forEach(function(entry){
|
|
2500
|
+
var p;
|
|
2501
|
+
try{p=new URL(entry.name,location.href).pathname}catch(_){p=''}
|
|
2502
|
+
if(p===SINK)return;
|
|
2503
|
+
if(entry.responseStatus>=400)send({kind:'subresource',url:entry.name,status:entry.responseStatus});
|
|
2504
|
+
});
|
|
2505
|
+
}).observe({type:'resource',buffered:true});
|
|
2506
|
+
}catch(e){}
|
|
2507
|
+
})();`;
|
|
2508
|
+
var clientErrorReporterScript = `<script>
|
|
2509
|
+
${clientErrorReporterBody}
|
|
2510
|
+
</script>`;
|
|
2511
|
+
|
|
2474
2512
|
// app/lib/whatsapp/inbound/media-download-guard.ts
|
|
2475
2513
|
var TAG2 = "[media-guard]";
|
|
2476
2514
|
var inFlight2 = 0;
|
|
@@ -5067,6 +5105,18 @@ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
|
|
|
5067
5105
|
return routed ?? houseId;
|
|
5068
5106
|
}
|
|
5069
5107
|
|
|
5108
|
+
// app/lib/whatsapp/account-id-shape.ts
|
|
5109
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5110
|
+
function isUuidAccountId(id) {
|
|
5111
|
+
return UUID_RE2.test(id);
|
|
5112
|
+
}
|
|
5113
|
+
function warnNonUuidAccount(caller2, accountId) {
|
|
5114
|
+
if (isUuidAccountId(accountId)) return;
|
|
5115
|
+
console.error(
|
|
5116
|
+
`[whatsapp-account-scope] op=non-uuid-account caller=${caller2} accountId=${accountId}`
|
|
5117
|
+
);
|
|
5118
|
+
}
|
|
5119
|
+
|
|
5070
5120
|
// app/lib/managed-service.ts
|
|
5071
5121
|
import { join as join7 } from "path";
|
|
5072
5122
|
import { readFileSync as readFileSync9 } from "fs";
|
|
@@ -6183,8 +6233,9 @@ function buildSelfChatDispatch(input) {
|
|
|
6183
6233
|
isGroup: false
|
|
6184
6234
|
}),
|
|
6185
6235
|
media: [],
|
|
6186
|
-
// Self-chat is the owner: the session scopes to the house account (Task 1383)
|
|
6187
|
-
|
|
6236
|
+
// Self-chat is the owner: the session scopes to the house account (Task 1383),
|
|
6237
|
+
// named by its platform UUID (Task 1850).
|
|
6238
|
+
effectiveAccountId: input.platformAccountId,
|
|
6188
6239
|
// Task 1575 — self-chat is unambiguously the owner, so attribute the spawn
|
|
6189
6240
|
// to the owner userId even when the owner phone is unbound in users.json.
|
|
6190
6241
|
ownerSelfPhone: true
|
|
@@ -6315,20 +6366,26 @@ function resolveReadStoreKey(accountId) {
|
|
|
6315
6366
|
}
|
|
6316
6367
|
function resolveInboundPersistAccount(args) {
|
|
6317
6368
|
const result = args.accessResult;
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
return args.connPlatformAccountId;
|
|
6369
|
+
const account = result?.allowed && result.reason === "account-manager" && result.effectiveAccountId ? result.effectiveAccountId : args.connPlatformAccountId;
|
|
6370
|
+
warnNonUuidAccount("inbound-persist", account);
|
|
6371
|
+
return account;
|
|
6322
6372
|
}
|
|
6323
6373
|
function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
|
|
6324
6374
|
const conn = findConnectionByKey(Array.from(connections2.values()), sendAccountId);
|
|
6325
|
-
if (!conn)
|
|
6375
|
+
if (!conn) {
|
|
6376
|
+
warnNonUuidAccount("reply-append-no-connection", sendAccountId);
|
|
6377
|
+
return sendAccountId;
|
|
6378
|
+
}
|
|
6326
6379
|
const access = checkDmAccess({
|
|
6327
6380
|
senderPhone: counterpartyPhone,
|
|
6328
6381
|
selfPhone: conn.selfPhone ?? "",
|
|
6329
6382
|
config: whatsAppConfig,
|
|
6383
|
+
// The per-number sub-config is keyed by the credential DIRNAME.
|
|
6330
6384
|
accountConfig: whatsAppConfig.accounts?.[conn.accountId],
|
|
6331
|
-
|
|
6385
|
+
// Task 1850 — the account SCOPE is the platform UUID. The gate assigns this
|
|
6386
|
+
// to `effectiveAccountId` for every non-manager admit, and that value reaches
|
|
6387
|
+
// the graph and the spawn; the dirname is a credential path, not an account.
|
|
6388
|
+
accountId: conn.platformAccountId,
|
|
6332
6389
|
isValidAccount: (id) => isValidAccountId(id)
|
|
6333
6390
|
});
|
|
6334
6391
|
return resolveInboundPersistAccount({
|
|
@@ -6662,8 +6719,11 @@ function monitorInbound(conn) {
|
|
|
6662
6719
|
senderPhone,
|
|
6663
6720
|
selfPhone: conn.selfPhone ?? "",
|
|
6664
6721
|
config: whatsAppConfig,
|
|
6722
|
+
// Keyed by the credential DIRNAME — a per-number sub-config.
|
|
6665
6723
|
accountConfig: whatsAppConfig.accounts?.[conn.accountId],
|
|
6666
|
-
|
|
6724
|
+
// Task 1850 — the account SCOPE is the platform UUID, never the
|
|
6725
|
+
// dirname. See resolveConversationPersistAccount.
|
|
6726
|
+
accountId: conn.platformAccountId,
|
|
6667
6727
|
isValidAccount: (id) => isValidAccountId(id)
|
|
6668
6728
|
});
|
|
6669
6729
|
const persistAccountId = resolveInboundPersistAccount({
|
|
@@ -6856,6 +6916,7 @@ function monitorInbound(conn) {
|
|
|
6856
6916
|
};
|
|
6857
6917
|
dispatchInboundPayload(conn, buildSelfChatDispatch({
|
|
6858
6918
|
accountId: conn.accountId,
|
|
6919
|
+
platformAccountId: conn.platformAccountId,
|
|
6859
6920
|
selfPhone: conn.selfPhone,
|
|
6860
6921
|
remoteJid,
|
|
6861
6922
|
text: decision.text,
|
|
@@ -7005,7 +7066,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
7005
7066
|
selfPhone,
|
|
7006
7067
|
config: whatsAppConfig,
|
|
7007
7068
|
accountConfig: whatsAppConfig.accounts?.[conn.accountId],
|
|
7008
|
-
accountId: conn.
|
|
7069
|
+
accountId: conn.platformAccountId,
|
|
7009
7070
|
isValidAccount: (id) => isValidAccountId(id),
|
|
7010
7071
|
resolvePersonByPhone
|
|
7011
7072
|
});
|
|
@@ -8114,6 +8175,11 @@ function createScheduleInjectRoutes(deps) {
|
|
|
8114
8175
|
// server/index.ts
|
|
8115
8176
|
var import_dist9 = __toESM(require_dist(), 1);
|
|
8116
8177
|
|
|
8178
|
+
// app/lib/whatsapp/gateway/manages-account.ts
|
|
8179
|
+
function managesAccountFor(effectiveAccountId, connPlatformAccountId) {
|
|
8180
|
+
return effectiveAccountId === connPlatformAccountId ? "none" : effectiveAccountId;
|
|
8181
|
+
}
|
|
8182
|
+
|
|
8117
8183
|
// app/lib/whatsapp/socket-resolution-error.ts
|
|
8118
8184
|
function formatPresentKeys(presentKeys) {
|
|
8119
8185
|
return presentKeys.join(",") || "none";
|
|
@@ -11531,13 +11597,13 @@ async function runPortalIndexPush(root) {
|
|
|
11531
11597
|
import { existsSync as existsSync12, readdirSync as readdirSync9, statSync as statSync5 } from "fs";
|
|
11532
11598
|
import { join as join17 } from "path";
|
|
11533
11599
|
var GOOGLE_PENDING_LIFETIME_SEC = 600;
|
|
11534
|
-
var
|
|
11600
|
+
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11535
11601
|
function runGooglePendingAudit(accountsDir, nowMs, emit, readMtimeMs = (path3) => statSync5(path3).mtimeMs) {
|
|
11536
11602
|
let scanned = 0;
|
|
11537
11603
|
let stranded = 0;
|
|
11538
11604
|
if (existsSync12(accountsDir)) {
|
|
11539
11605
|
for (const accountId of readdirSync9(accountsDir)) {
|
|
11540
|
-
if (!
|
|
11606
|
+
if (!UUID_RE3.test(accountId)) continue;
|
|
11541
11607
|
const pendingPath2 = join17(accountsDir, accountId, "secrets", "google", "pending-oauth.enc");
|
|
11542
11608
|
try {
|
|
11543
11609
|
if (!existsSync12(pendingPath2)) continue;
|
|
@@ -11596,19 +11662,202 @@ function runReconcileLivenessAudit(statePath, nowMs, intervalMs, emit) {
|
|
|
11596
11662
|
|
|
11597
11663
|
// server/routes/whatsapp-reader.ts
|
|
11598
11664
|
import { readFileSync as readFileSync21, watch as watch2, statSync as statSync8, openSync, readSync, closeSync, existsSync as existsSync17, readdirSync as readdirSync14, realpathSync as realpathSync6 } from "fs";
|
|
11599
|
-
import { basename as basename6, dirname as dirname7, isAbsolute, join as
|
|
11665
|
+
import { basename as basename6, dirname as dirname7, isAbsolute, join as join23, relative as relative3, resolve as resolve17, sep as sep5 } from "path";
|
|
11600
11666
|
|
|
11601
11667
|
// server/routes/admin/sidebar-sessions.ts
|
|
11602
11668
|
import { readdirSync as readdirSync12, readFileSync as readFileSync19 } from "fs";
|
|
11603
|
-
import { readFile as
|
|
11604
|
-
import { dirname as dirname5, join as
|
|
11669
|
+
import { open as open2, readFile as readFile5, stat as stat5 } from "fs/promises";
|
|
11670
|
+
import { dirname as dirname5, join as join21, resolve as resolve15 } from "path";
|
|
11671
|
+
|
|
11672
|
+
// server/lib/session-title-cache.ts
|
|
11673
|
+
import { open, readFile as readFile4, rename as rename2, unlink as unlink3, writeFile as writeFile5 } from "fs/promises";
|
|
11674
|
+
import { join as join18 } from "path";
|
|
11675
|
+
var CACHE_FILENAME = "ai-title-cache.json";
|
|
11676
|
+
var tmpSeq = 0;
|
|
11677
|
+
function nextTmpSeq() {
|
|
11678
|
+
tmpSeq = (tmpSeq + 1) % Number.MAX_SAFE_INTEGER;
|
|
11679
|
+
return tmpSeq;
|
|
11680
|
+
}
|
|
11681
|
+
function errCode(err) {
|
|
11682
|
+
const code = err?.code;
|
|
11683
|
+
if (code) return code;
|
|
11684
|
+
return err instanceof Error && err.message ? err.message : String(err);
|
|
11685
|
+
}
|
|
11686
|
+
function titleCachePath(accountDir) {
|
|
11687
|
+
return join18(accountDir, CACHE_FILENAME);
|
|
11688
|
+
}
|
|
11689
|
+
function parseTitleCacheWithDropped(raw) {
|
|
11690
|
+
const out = /* @__PURE__ */ new Map();
|
|
11691
|
+
let dropped = 0;
|
|
11692
|
+
let parsed;
|
|
11693
|
+
try {
|
|
11694
|
+
parsed = JSON.parse(raw);
|
|
11695
|
+
} catch {
|
|
11696
|
+
return { cache: out, dropped, wellFormed: false };
|
|
11697
|
+
}
|
|
11698
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
11699
|
+
return { cache: out, dropped, wellFormed: false };
|
|
11700
|
+
}
|
|
11701
|
+
for (const [sessionId, value] of Object.entries(parsed)) {
|
|
11702
|
+
if (!sessionId) {
|
|
11703
|
+
dropped += 1;
|
|
11704
|
+
continue;
|
|
11705
|
+
}
|
|
11706
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11707
|
+
dropped += 1;
|
|
11708
|
+
continue;
|
|
11709
|
+
}
|
|
11710
|
+
const v = value;
|
|
11711
|
+
const aiTitle = v.aiTitle === null ? null : typeof v.aiTitle === "string" ? v.aiTitle : void 0;
|
|
11712
|
+
if (aiTitle === void 0) {
|
|
11713
|
+
dropped += 1;
|
|
11714
|
+
continue;
|
|
11715
|
+
}
|
|
11716
|
+
if (typeof v.scannedBytes !== "number" || !Number.isFinite(v.scannedBytes) || v.scannedBytes < 0) {
|
|
11717
|
+
dropped += 1;
|
|
11718
|
+
continue;
|
|
11719
|
+
}
|
|
11720
|
+
out.set(sessionId, { aiTitle, scannedBytes: v.scannedBytes });
|
|
11721
|
+
}
|
|
11722
|
+
return { cache: out, dropped, wellFormed: true };
|
|
11723
|
+
}
|
|
11724
|
+
function parseTitleCache(raw) {
|
|
11725
|
+
return parseTitleCacheWithDropped(raw).cache;
|
|
11726
|
+
}
|
|
11727
|
+
function serializeTitleCache(cache3) {
|
|
11728
|
+
const obj = {};
|
|
11729
|
+
for (const [sessionId, entry] of cache3) obj[sessionId] = entry;
|
|
11730
|
+
return JSON.stringify(obj);
|
|
11731
|
+
}
|
|
11732
|
+
async function loadTitleCacheOutcome(accountDir) {
|
|
11733
|
+
if (!accountDir) return { cache: /* @__PURE__ */ new Map(), state: "no-dir", dropped: 0 };
|
|
11734
|
+
let raw;
|
|
11735
|
+
try {
|
|
11736
|
+
raw = await readFile4(titleCachePath(accountDir), "utf8");
|
|
11737
|
+
} catch (err) {
|
|
11738
|
+
const code = err.code;
|
|
11739
|
+
if (code === "ENOENT") return { cache: /* @__PURE__ */ new Map(), state: "absent", dropped: 0 };
|
|
11740
|
+
console.error(
|
|
11741
|
+
`[session-title-cache] op=load-failed dir=${accountDir} code=${code ?? "unknown"} reason=${err instanceof Error ? err.message : String(err)}`
|
|
11742
|
+
);
|
|
11743
|
+
return { cache: /* @__PURE__ */ new Map(), state: "unreadable", dropped: 0 };
|
|
11744
|
+
}
|
|
11745
|
+
const { cache: cache3, dropped, wellFormed } = parseTitleCacheWithDropped(raw);
|
|
11746
|
+
const corrupt = !wellFormed || cache3.size === 0 && dropped > 0;
|
|
11747
|
+
return { cache: cache3, state: corrupt ? "corrupt" : "loaded", dropped };
|
|
11748
|
+
}
|
|
11749
|
+
async function loadTitleCache(accountDir) {
|
|
11750
|
+
return (await loadTitleCacheOutcome(accountDir)).cache;
|
|
11751
|
+
}
|
|
11752
|
+
async function saveTitleCacheMerged(accountDir, updates, evict) {
|
|
11753
|
+
if (updates.size === 0 && (evict === void 0 || evict.size === 0)) return 0;
|
|
11754
|
+
const path3 = titleCachePath(accountDir);
|
|
11755
|
+
let merged;
|
|
11756
|
+
try {
|
|
11757
|
+
merged = parseTitleCache(await readFile4(path3, "utf8"));
|
|
11758
|
+
} catch (err) {
|
|
11759
|
+
if (err.code !== "ENOENT") throw err;
|
|
11760
|
+
merged = /* @__PURE__ */ new Map();
|
|
11761
|
+
}
|
|
11762
|
+
for (const [sessionId, entry] of updates) merged.set(sessionId, entry);
|
|
11763
|
+
let evicted = 0;
|
|
11764
|
+
if (evict !== void 0) {
|
|
11765
|
+
for (const sessionId of evict) {
|
|
11766
|
+
if (merged.delete(sessionId)) evicted += 1;
|
|
11767
|
+
}
|
|
11768
|
+
}
|
|
11769
|
+
const tmp = `${path3}.tmp.${process.pid}.${Date.now()}.${nextTmpSeq()}`;
|
|
11770
|
+
try {
|
|
11771
|
+
await writeFile5(tmp, serializeTitleCache(merged), "utf8");
|
|
11772
|
+
await rename2(tmp, path3);
|
|
11773
|
+
} catch (err) {
|
|
11774
|
+
try {
|
|
11775
|
+
await unlink3(tmp);
|
|
11776
|
+
} catch (cleanupErr) {
|
|
11777
|
+
const code = cleanupErr.code;
|
|
11778
|
+
if (code !== "ENOENT") {
|
|
11779
|
+
console.error(`[session-title-cache] op=tmp-cleanup-failed path=${tmp} code=${code ?? "unknown"}`);
|
|
11780
|
+
}
|
|
11781
|
+
}
|
|
11782
|
+
throw err;
|
|
11783
|
+
}
|
|
11784
|
+
return evicted;
|
|
11785
|
+
}
|
|
11786
|
+
function lastAiTitleIn(text, initial) {
|
|
11787
|
+
let aiTitle = initial;
|
|
11788
|
+
for (const line of text.split("\n")) {
|
|
11789
|
+
if (!line || line[0] !== "{") continue;
|
|
11790
|
+
let parsed;
|
|
11791
|
+
try {
|
|
11792
|
+
parsed = JSON.parse(line);
|
|
11793
|
+
} catch {
|
|
11794
|
+
continue;
|
|
11795
|
+
}
|
|
11796
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
11797
|
+
const row = parsed;
|
|
11798
|
+
if (row.type !== "ai-title" || typeof row.aiTitle !== "string") continue;
|
|
11799
|
+
const t = row.aiTitle.trim();
|
|
11800
|
+
if (t) aiTitle = t;
|
|
11801
|
+
}
|
|
11802
|
+
return aiTitle;
|
|
11803
|
+
}
|
|
11804
|
+
function scanAiTitleFromBody(body) {
|
|
11805
|
+
const lastNl = body.lastIndexOf("\n");
|
|
11806
|
+
const complete = lastNl === -1 ? "" : body.slice(0, lastNl + 1);
|
|
11807
|
+
return {
|
|
11808
|
+
aiTitle: lastAiTitleIn(body, null),
|
|
11809
|
+
scannedBytes: Buffer.byteLength(complete, "utf8")
|
|
11810
|
+
};
|
|
11811
|
+
}
|
|
11812
|
+
async function scanAiTitleIncremental(path3, size, prior) {
|
|
11813
|
+
const usable = prior !== void 0 && prior.scannedBytes <= size;
|
|
11814
|
+
const start = usable ? prior.scannedBytes : 0;
|
|
11815
|
+
const carried = usable ? prior.aiTitle : null;
|
|
11816
|
+
if (start >= size) return { entry: { aiTitle: carried, scannedBytes: start }, bytesRead: 0 };
|
|
11817
|
+
const length = size - start;
|
|
11818
|
+
let chunk;
|
|
11819
|
+
let handle2;
|
|
11820
|
+
try {
|
|
11821
|
+
handle2 = await open(path3, "r");
|
|
11822
|
+
} catch (err) {
|
|
11823
|
+
return {
|
|
11824
|
+
entry: { aiTitle: carried, scannedBytes: start },
|
|
11825
|
+
bytesRead: 0,
|
|
11826
|
+
failure: `open ${errCode(err)}`
|
|
11827
|
+
};
|
|
11828
|
+
}
|
|
11829
|
+
try {
|
|
11830
|
+
const buf = Buffer.alloc(length);
|
|
11831
|
+
const { bytesRead } = await handle2.read(buf, 0, length, start);
|
|
11832
|
+
chunk = bytesRead < length ? buf.subarray(0, bytesRead) : buf;
|
|
11833
|
+
} catch (err) {
|
|
11834
|
+
return {
|
|
11835
|
+
entry: { aiTitle: carried, scannedBytes: start },
|
|
11836
|
+
bytesRead: 0,
|
|
11837
|
+
failure: `read ${errCode(err)}`
|
|
11838
|
+
};
|
|
11839
|
+
} finally {
|
|
11840
|
+
await handle2.close().catch(() => {
|
|
11841
|
+
});
|
|
11842
|
+
}
|
|
11843
|
+
const text = chunk.toString("utf8");
|
|
11844
|
+
const lastNl = text.lastIndexOf("\n");
|
|
11845
|
+
const complete = lastNl === -1 ? "" : text.slice(0, lastNl + 1);
|
|
11846
|
+
return {
|
|
11847
|
+
entry: {
|
|
11848
|
+
aiTitle: lastAiTitleIn(text, carried),
|
|
11849
|
+
scannedBytes: start + Buffer.byteLength(complete, "utf8")
|
|
11850
|
+
},
|
|
11851
|
+
bytesRead: chunk.length
|
|
11852
|
+
};
|
|
11853
|
+
}
|
|
11605
11854
|
|
|
11606
11855
|
// ../services/claude-session-manager/src/jsonl-path.ts
|
|
11607
11856
|
import { existsSync as existsSync14, readdirSync as readdirSync10 } from "fs";
|
|
11608
11857
|
import { homedir } from "os";
|
|
11609
|
-
import { join as
|
|
11858
|
+
import { join as join19 } from "path";
|
|
11610
11859
|
function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
11611
|
-
const projectsRoot =
|
|
11860
|
+
const projectsRoot = join19(claudeConfigDir2, "projects");
|
|
11612
11861
|
if (!existsSync14(projectsRoot)) return null;
|
|
11613
11862
|
let slugs;
|
|
11614
11863
|
try {
|
|
@@ -11618,9 +11867,9 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
|
11618
11867
|
}
|
|
11619
11868
|
const basename15 = `${sessionId}.jsonl`;
|
|
11620
11869
|
for (const slug of slugs) {
|
|
11621
|
-
const live =
|
|
11870
|
+
const live = join19(projectsRoot, slug, basename15);
|
|
11622
11871
|
if (existsSync14(live)) return live;
|
|
11623
|
-
const archived =
|
|
11872
|
+
const archived = join19(projectsRoot, slug, "archive", basename15);
|
|
11624
11873
|
if (existsSync14(archived)) return archived;
|
|
11625
11874
|
}
|
|
11626
11875
|
return null;
|
|
@@ -11629,7 +11878,7 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
|
11629
11878
|
// app/lib/admin-identity/pin-validator.ts
|
|
11630
11879
|
import { createHash as createHash3 } from "crypto";
|
|
11631
11880
|
import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as statSync6 } from "fs";
|
|
11632
|
-
import { join as
|
|
11881
|
+
import { join as join20 } from "path";
|
|
11633
11882
|
function hashPin(pin) {
|
|
11634
11883
|
return createHash3("sha256").update(pin).digest("hex");
|
|
11635
11884
|
}
|
|
@@ -11737,7 +11986,29 @@ function lastAssistantModel(body) {
|
|
|
11737
11986
|
}
|
|
11738
11987
|
return null;
|
|
11739
11988
|
}
|
|
11740
|
-
|
|
11989
|
+
async function lastAssistantModelFromTail(path3, size, maxBytes = 256 * 1024) {
|
|
11990
|
+
if (size <= 0) return { model: null, bytesRead: 0 };
|
|
11991
|
+
const start = Math.max(0, size - maxBytes);
|
|
11992
|
+
const length = size - start;
|
|
11993
|
+
let handle2;
|
|
11994
|
+
try {
|
|
11995
|
+
handle2 = await open2(path3, "r");
|
|
11996
|
+
} catch (err) {
|
|
11997
|
+
return { model: null, bytesRead: 0, failure: `open ${errCode(err)}` };
|
|
11998
|
+
}
|
|
11999
|
+
let chunk;
|
|
12000
|
+
try {
|
|
12001
|
+
const buf = Buffer.alloc(length);
|
|
12002
|
+
const { bytesRead } = await handle2.read(buf, 0, length, start);
|
|
12003
|
+
chunk = bytesRead < length ? buf.subarray(0, bytesRead) : buf;
|
|
12004
|
+
} catch (err) {
|
|
12005
|
+
return { model: null, bytesRead: 0, failure: `read ${errCode(err)}` };
|
|
12006
|
+
} finally {
|
|
12007
|
+
await handle2.close().catch(() => {
|
|
12008
|
+
});
|
|
12009
|
+
}
|
|
12010
|
+
return { model: lastAssistantModel(chunk.toString("utf8")), bytesRead: chunk.length };
|
|
12011
|
+
}
|
|
11741
12012
|
var ADMIN_ROLE = "admin";
|
|
11742
12013
|
var WEBCHAT_CHANNEL = "webchat";
|
|
11743
12014
|
var app4 = new Hono();
|
|
@@ -11750,62 +12021,69 @@ function findSessionProjectDir(sessionId) {
|
|
|
11750
12021
|
const jsonlPath = findExistingJsonlForSessionId(cfg, sessionId);
|
|
11751
12022
|
return jsonlPath ? dirname5(jsonlPath) : null;
|
|
11752
12023
|
}
|
|
11753
|
-
function
|
|
12024
|
+
function enumerateJsonlsWithSkips(projectsRoot) {
|
|
11754
12025
|
const out = [];
|
|
12026
|
+
let skipped = 0;
|
|
11755
12027
|
let slugs;
|
|
11756
12028
|
try {
|
|
11757
12029
|
slugs = readdirSync12(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
11758
12030
|
} catch (err) {
|
|
11759
|
-
if (err.code === "ENOENT") return out;
|
|
12031
|
+
if (err.code === "ENOENT") return { files: out, skipped };
|
|
11760
12032
|
throw err;
|
|
11761
12033
|
}
|
|
11762
12034
|
for (const slug of slugs) {
|
|
11763
|
-
const slugDir =
|
|
12035
|
+
const slugDir = join21(projectsRoot, slug);
|
|
11764
12036
|
let entries2;
|
|
11765
12037
|
try {
|
|
11766
12038
|
entries2 = readdirSync12(slugDir, { withFileTypes: true });
|
|
11767
12039
|
} catch (err) {
|
|
11768
12040
|
const code = err.code ?? "unknown";
|
|
11769
12041
|
console.error(`[admin-sessions-list] slug-skipped slug=${slug} code=${code}`);
|
|
12042
|
+
skipped += 1;
|
|
11770
12043
|
continue;
|
|
11771
12044
|
}
|
|
11772
12045
|
for (const entry of entries2) {
|
|
11773
12046
|
if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
|
|
11774
|
-
out.push({ path:
|
|
12047
|
+
out.push({ path: join21(slugDir, entry.name), isSubagent: false, archived: false });
|
|
11775
12048
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
11776
|
-
const subDir =
|
|
12049
|
+
const subDir = join21(slugDir, entry.name);
|
|
11777
12050
|
let subEntries;
|
|
11778
12051
|
try {
|
|
11779
12052
|
subEntries = readdirSync12(subDir, { withFileTypes: true });
|
|
11780
12053
|
} catch (err) {
|
|
11781
12054
|
const code = err.code ?? "unknown";
|
|
11782
12055
|
console.error(`[admin-sessions-list] subagents-skipped slug=${slug} code=${code}`);
|
|
12056
|
+
skipped += 1;
|
|
11783
12057
|
continue;
|
|
11784
12058
|
}
|
|
11785
12059
|
for (const sub of subEntries) {
|
|
11786
12060
|
if (sub.isFile() && SESSION_ID_RE.test(sub.name)) {
|
|
11787
|
-
out.push({ path:
|
|
12061
|
+
out.push({ path: join21(subDir, sub.name), isSubagent: true, archived: false });
|
|
11788
12062
|
}
|
|
11789
12063
|
}
|
|
11790
12064
|
} else if (entry.isDirectory() && entry.name === "archive") {
|
|
11791
|
-
const archiveDir =
|
|
12065
|
+
const archiveDir = join21(slugDir, entry.name);
|
|
11792
12066
|
let archiveEntries;
|
|
11793
12067
|
try {
|
|
11794
12068
|
archiveEntries = readdirSync12(archiveDir, { withFileTypes: true });
|
|
11795
12069
|
} catch (err) {
|
|
11796
12070
|
const code = err.code ?? "unknown";
|
|
11797
12071
|
console.error(`[admin-sessions-list] archive-skipped slug=${slug} code=${code}`);
|
|
12072
|
+
skipped += 1;
|
|
11798
12073
|
continue;
|
|
11799
12074
|
}
|
|
11800
12075
|
for (const arc of archiveEntries) {
|
|
11801
12076
|
if (arc.isFile() && SESSION_ID_RE.test(arc.name)) {
|
|
11802
|
-
out.push({ path:
|
|
12077
|
+
out.push({ path: join21(archiveDir, arc.name), isSubagent: false, archived: true });
|
|
11803
12078
|
}
|
|
11804
12079
|
}
|
|
11805
12080
|
}
|
|
11806
12081
|
}
|
|
11807
12082
|
}
|
|
11808
|
-
return out;
|
|
12083
|
+
return { files: out, skipped };
|
|
12084
|
+
}
|
|
12085
|
+
function enumerateJsonls(projectsRoot) {
|
|
12086
|
+
return enumerateJsonlsWithSkips(projectsRoot).files;
|
|
11809
12087
|
}
|
|
11810
12088
|
async function fetchLiveSessions() {
|
|
11811
12089
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -11833,7 +12111,7 @@ function loadUserTitles(accountDir) {
|
|
|
11833
12111
|
if (!accountDir) return /* @__PURE__ */ new Map();
|
|
11834
12112
|
let raw;
|
|
11835
12113
|
try {
|
|
11836
|
-
raw = readFileSync19(
|
|
12114
|
+
raw = readFileSync19(join21(accountDir, "session-titles.json"), "utf8");
|
|
11837
12115
|
} catch {
|
|
11838
12116
|
return /* @__PURE__ */ new Map();
|
|
11839
12117
|
}
|
|
@@ -11843,7 +12121,7 @@ async function loadUserTitlesAsync(accountDir) {
|
|
|
11843
12121
|
if (!accountDir) return /* @__PURE__ */ new Map();
|
|
11844
12122
|
let raw;
|
|
11845
12123
|
try {
|
|
11846
|
-
raw = await
|
|
12124
|
+
raw = await readFile5(join21(accountDir, "session-titles.json"), "utf8");
|
|
11847
12125
|
} catch {
|
|
11848
12126
|
return /* @__PURE__ */ new Map();
|
|
11849
12127
|
}
|
|
@@ -11884,7 +12162,7 @@ function emptySidecarMeta() {
|
|
|
11884
12162
|
async function readSidecarMetaAsync(metaPath) {
|
|
11885
12163
|
let raw;
|
|
11886
12164
|
try {
|
|
11887
|
-
raw = await
|
|
12165
|
+
raw = await readFile5(metaPath, "utf8");
|
|
11888
12166
|
} catch {
|
|
11889
12167
|
return emptySidecarMeta();
|
|
11890
12168
|
}
|
|
@@ -11913,67 +12191,14 @@ function parseSidecarMeta(raw) {
|
|
|
11913
12191
|
accountId: typeof r.accountId === "string" ? r.accountId : null
|
|
11914
12192
|
};
|
|
11915
12193
|
}
|
|
11916
|
-
function startsWithCliMarker(s) {
|
|
11917
|
-
for (const p of CLI_MARKER_PREFIXES) if (s.startsWith(p)) return true;
|
|
11918
|
-
return false;
|
|
11919
|
-
}
|
|
11920
12194
|
function trimToTitle(raw) {
|
|
11921
12195
|
return raw.trim().replace(/\s+/g, " ");
|
|
11922
12196
|
}
|
|
11923
|
-
|
|
11924
|
-
var SOURCE_ATTR = /\bsource="([^"]+)"/;
|
|
11925
|
-
var CHANNEL_SOURCE_MAP = {
|
|
11926
|
-
whatsapp: "whatsapp",
|
|
11927
|
-
"whatsapp-channel": "whatsapp",
|
|
11928
|
-
webchat: "webchat",
|
|
11929
|
-
"webchat-channel": "webchat"
|
|
11930
|
-
};
|
|
11931
|
-
function stripChannelEnvelope(text) {
|
|
11932
|
-
const m = text.match(CHANNEL_WRAPPER);
|
|
11933
|
-
if (!m) return { inner: text, channel: null };
|
|
11934
|
-
let raw = m[1].match(SOURCE_ATTR)?.[1] ?? "";
|
|
11935
|
-
let inner = m[2];
|
|
11936
|
-
const im = inner.match(CHANNEL_WRAPPER);
|
|
11937
|
-
const innerRaw = im?.[1].match(SOURCE_ATTR)?.[1];
|
|
11938
|
-
if (im && innerRaw) {
|
|
11939
|
-
raw = innerRaw;
|
|
11940
|
-
inner = im[2];
|
|
11941
|
-
}
|
|
11942
|
-
return { inner, channel: CHANNEL_SOURCE_MAP[raw] ?? (raw || null) };
|
|
11943
|
-
}
|
|
11944
|
-
function resolveTitle(body, sessionId, userTitles) {
|
|
12197
|
+
function resolveTitleFromParts(sessionId, userTitles, aiTitle) {
|
|
11945
12198
|
const userTitle = userTitles.get(sessionId);
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
if (!line || line[0] !== "{") continue;
|
|
11950
|
-
let parsed;
|
|
11951
|
-
try {
|
|
11952
|
-
parsed = JSON.parse(line);
|
|
11953
|
-
} catch {
|
|
11954
|
-
continue;
|
|
11955
|
-
}
|
|
11956
|
-
if (!parsed || typeof parsed !== "object") continue;
|
|
11957
|
-
const row = parsed;
|
|
11958
|
-
if (row.type === "ai-title" && typeof row.aiTitle === "string") {
|
|
11959
|
-
const t = row.aiTitle.trim();
|
|
11960
|
-
if (t) aiTitle = t;
|
|
11961
|
-
continue;
|
|
11962
|
-
}
|
|
11963
|
-
if (firstUserMessage === null && row.type === "user" && row.message && row.message.role === "user") {
|
|
11964
|
-
const content = row.message.content;
|
|
11965
|
-
if (typeof content !== "string") continue;
|
|
11966
|
-
const trimmed = content.trim();
|
|
11967
|
-
if (!trimmed) continue;
|
|
11968
|
-
if (startsWithCliMarker(trimmed)) continue;
|
|
11969
|
-
firstUserMessage = trimmed;
|
|
11970
|
-
}
|
|
11971
|
-
}
|
|
11972
|
-
const stripped = firstUserMessage !== null ? stripChannelEnvelope(firstUserMessage) : { inner: null, channel: null };
|
|
11973
|
-
const channel = stripped.channel;
|
|
11974
|
-
if (userTitle) return { title: trimToTitle(userTitle), source: "user", channel };
|
|
11975
|
-
if (aiTitle) return { title: trimToTitle(aiTitle), source: "ai", channel };
|
|
11976
|
-
return { title: sessionId.slice(0, 8), source: "prefix", channel };
|
|
12199
|
+
if (userTitle) return { title: trimToTitle(userTitle), source: "user" };
|
|
12200
|
+
if (aiTitle) return { title: trimToTitle(aiTitle), source: "ai" };
|
|
12201
|
+
return { title: sessionId.slice(0, 8), source: "prefix" };
|
|
11977
12202
|
}
|
|
11978
12203
|
app4.get("/", requireAdminSession, async (c) => {
|
|
11979
12204
|
const cacheKey = c.var.cacheKey;
|
|
@@ -11998,8 +12223,7 @@ async function computeSessionList(scopeAccountId) {
|
|
|
11998
12223
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
11999
12224
|
return { sessions: [], accountId: scopeAccountId };
|
|
12000
12225
|
}
|
|
12001
|
-
const projectsRoot =
|
|
12002
|
-
const jsonls = enumerateJsonls(projectsRoot);
|
|
12226
|
+
const projectsRoot = join21(configDir2, "projects");
|
|
12003
12227
|
const liveSessions = await fetchLiveSessions();
|
|
12004
12228
|
const accountDir = scopeAccountId ? resolve15(ACCOUNTS_DIR, scopeAccountId) : null;
|
|
12005
12229
|
const userTitles = await loadUserTitlesAsync(accountDir);
|
|
@@ -12029,6 +12253,24 @@ async function computeSessionList(scopeAccountId) {
|
|
|
12029
12253
|
const adminUserIdBySession = /* @__PURE__ */ new Map();
|
|
12030
12254
|
const channelSenderBySession = /* @__PURE__ */ new Map();
|
|
12031
12255
|
const channelMissNoSender = [];
|
|
12256
|
+
const titleCacheDir = account?.accountDir ?? accountDir;
|
|
12257
|
+
const cacheLoad = await loadTitleCacheOutcome(titleCacheDir);
|
|
12258
|
+
const titleCache = cacheLoad.cache;
|
|
12259
|
+
const cacheUpdates = /* @__PURE__ */ new Map();
|
|
12260
|
+
const { files: jsonls, skipped: enumSkipped } = enumerateJsonlsWithSkips(projectsRoot);
|
|
12261
|
+
let enumeratedCount = 0;
|
|
12262
|
+
let scopedCount = 0;
|
|
12263
|
+
let titleCacheHits = 0;
|
|
12264
|
+
let bodyScans = 0;
|
|
12265
|
+
let titleReadFails = 0;
|
|
12266
|
+
let titleBytes = 0;
|
|
12267
|
+
let tailReads = 0;
|
|
12268
|
+
let tailModelMiss = 0;
|
|
12269
|
+
let tailReadFails = 0;
|
|
12270
|
+
let tailBytes = 0;
|
|
12271
|
+
let cacheWriteFails = 0;
|
|
12272
|
+
let evicted = 0;
|
|
12273
|
+
const walkStartedAt = Date.now();
|
|
12032
12274
|
for (const { path: path3, isSubagent, archived } of jsonls) {
|
|
12033
12275
|
const lastSlash = path3.lastIndexOf("/");
|
|
12034
12276
|
const base = path3.slice(lastSlash + 1);
|
|
@@ -12039,24 +12281,40 @@ async function computeSessionList(scopeAccountId) {
|
|
|
12039
12281
|
continue;
|
|
12040
12282
|
}
|
|
12041
12283
|
seenIds.add(sessionId);
|
|
12042
|
-
|
|
12284
|
+
enumeratedCount += 1;
|
|
12043
12285
|
let mtimeMs;
|
|
12286
|
+
let size;
|
|
12044
12287
|
try {
|
|
12045
|
-
|
|
12046
|
-
mtimeMs =
|
|
12288
|
+
const st = await stat5(path3);
|
|
12289
|
+
mtimeMs = st.mtimeMs;
|
|
12290
|
+
size = st.size;
|
|
12047
12291
|
} catch {
|
|
12048
12292
|
continue;
|
|
12049
12293
|
}
|
|
12050
|
-
const
|
|
12051
|
-
const pid = live ? liveSessions.get(sessionId) ?? null : null;
|
|
12052
|
-
const resolved = resolveTitle(body, sessionId, userTitles);
|
|
12053
|
-
titleSourceCounts[resolved.source] += 1;
|
|
12054
|
-
const metaPath = join20(projectDir, `${sessionId}.meta.json`);
|
|
12294
|
+
const metaPath = join21(projectDir, `${sessionId}.meta.json`);
|
|
12055
12295
|
const { bridgeIds, role, channel: sidecarChannel, senderId, personId, adminUserId, accountId: rowAccountId } = await readSidecarMetaAsync(metaPath);
|
|
12056
12296
|
if (multiAccount) {
|
|
12057
12297
|
if (rowAccountId === null) untaggedCount += 1;
|
|
12058
12298
|
if (rowAccountId !== scopeAccountId) continue;
|
|
12059
12299
|
}
|
|
12300
|
+
scopedCount += 1;
|
|
12301
|
+
const live = liveSessions.has(sessionId);
|
|
12302
|
+
const pid = live ? liveSessions.get(sessionId) ?? null : null;
|
|
12303
|
+
const prior = titleCache.get(sessionId);
|
|
12304
|
+
const scan = await scanAiTitleIncremental(path3, size, prior);
|
|
12305
|
+
if (scan.failure) {
|
|
12306
|
+
titleReadFails += 1;
|
|
12307
|
+
console.error(
|
|
12308
|
+
`[admin-sessions-list] op=title-scan-failed sessionId=${sessionId.slice(0, 8)} reason=${scan.failure}`
|
|
12309
|
+
);
|
|
12310
|
+
} else if (scan.bytesRead > 0) bodyScans += 1;
|
|
12311
|
+
else if (prior !== void 0) titleCacheHits += 1;
|
|
12312
|
+
titleBytes += scan.bytesRead;
|
|
12313
|
+
if (!prior || prior.aiTitle !== scan.entry.aiTitle || prior.scannedBytes !== scan.entry.scannedBytes) {
|
|
12314
|
+
cacheUpdates.set(sessionId, scan.entry);
|
|
12315
|
+
}
|
|
12316
|
+
const resolved = resolveTitleFromParts(sessionId, userTitles, scan.entry.aiTitle);
|
|
12317
|
+
titleSourceCounts[resolved.source] += 1;
|
|
12060
12318
|
if (isSessionListExcluded(role, sidecarChannel)) {
|
|
12061
12319
|
excludedPublicWebchatCount += 1;
|
|
12062
12320
|
continue;
|
|
@@ -12065,6 +12323,17 @@ async function computeSessionList(scopeAccountId) {
|
|
|
12065
12323
|
if (adminUserId && role === ADMIN_ROLE && sidecarChannel === WEBCHAT_CHANNEL) {
|
|
12066
12324
|
adminUserIdBySession.set(sessionId, adminUserId);
|
|
12067
12325
|
}
|
|
12326
|
+
const tail = await lastAssistantModelFromTail(path3, size);
|
|
12327
|
+
if (tail.failure) {
|
|
12328
|
+
tailReadFails += 1;
|
|
12329
|
+
console.error(
|
|
12330
|
+
`[admin-sessions-list] op=tail-read-failed sessionId=${sessionId.slice(0, 8)} reason=${tail.failure}`
|
|
12331
|
+
);
|
|
12332
|
+
} else {
|
|
12333
|
+
if (tail.bytesRead > 0) tailReads += 1;
|
|
12334
|
+
if (tail.model === null) tailModelMiss += 1;
|
|
12335
|
+
}
|
|
12336
|
+
tailBytes += tail.bytesRead;
|
|
12068
12337
|
rows.push({
|
|
12069
12338
|
sessionId,
|
|
12070
12339
|
title: resolved.title,
|
|
@@ -12083,7 +12352,7 @@ async function computeSessionList(scopeAccountId) {
|
|
|
12083
12352
|
// diverge from the sidecar (e.g. an ai-titled channel row).
|
|
12084
12353
|
channel: sidecarChannel,
|
|
12085
12354
|
personName: null,
|
|
12086
|
-
model:
|
|
12355
|
+
model: tail.model,
|
|
12087
12356
|
accountId: rowAccountId,
|
|
12088
12357
|
senderId,
|
|
12089
12358
|
whatsappName: null
|
|
@@ -12163,6 +12432,24 @@ async function computeSessionList(scopeAccountId) {
|
|
|
12163
12432
|
console.error(`[admin-sessions-list] op=channel-name-unresolved sessionId=${m.sessionId.slice(0, 8)} channel=${m.channel} senderId= reason=no-senderId`);
|
|
12164
12433
|
}
|
|
12165
12434
|
rows.sort((a, b) => a.startedAt < b.startedAt ? 1 : -1);
|
|
12435
|
+
const evictIds = /* @__PURE__ */ new Set();
|
|
12436
|
+
if (enumSkipped === 0 && seenIds.size > 0) {
|
|
12437
|
+
for (const sessionId of titleCache.keys()) if (!seenIds.has(sessionId)) evictIds.add(sessionId);
|
|
12438
|
+
}
|
|
12439
|
+
if (titleCacheDir && (cacheUpdates.size > 0 || evictIds.size > 0)) {
|
|
12440
|
+
try {
|
|
12441
|
+
evicted = await saveTitleCacheMerged(titleCacheDir, cacheUpdates, evictIds);
|
|
12442
|
+
} catch (err) {
|
|
12443
|
+
cacheWriteFails += 1;
|
|
12444
|
+
console.error(
|
|
12445
|
+
`[admin-sessions-list] op=title-cache-write-failed dir=${titleCacheDir} code=${errCode(err)} reason=${err instanceof Error ? err.message : String(err)}`
|
|
12446
|
+
);
|
|
12447
|
+
}
|
|
12448
|
+
}
|
|
12449
|
+
const bytesMB = (titleBytes + tailBytes) / (1024 * 1024);
|
|
12450
|
+
console.log(
|
|
12451
|
+
`[admin-sessions-list] op=list-walk enumerated=${enumeratedCount} scoped=${scopedCount} cacheLoad=${cacheLoad.state} cacheEntries=${titleCache.size} cacheDropped=${cacheLoad.dropped} titleCacheHits=${titleCacheHits} bodyScans=${bodyScans} titleReadFails=${titleReadFails} tailReads=${tailReads} tailModelMiss=${tailModelMiss} tailReadFails=${tailReadFails} titleBytesMB=${(titleBytes / (1024 * 1024)).toFixed(3)} bytesMB=${bytesMB.toFixed(3)} cacheWriteFails=${cacheWriteFails} evicted=${evicted} enumSkipped=${enumSkipped} ms=${Date.now() - walkStartedAt}`
|
|
12452
|
+
);
|
|
12166
12453
|
console.log(
|
|
12167
12454
|
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} channelInList=whatsapp:${channelInList.whatsapp},telegram:${channelInList.telegram},webchat:${channelInList.webchat} channelNameCandidates=${channelNameCandidates} channelNamesResolved=${channelNamesResolved} (operator=${operatorResolvedCount} store=${storeResolvedCount}) unknownChannel=${unknownChannelCount} excludedPublicWebchat=${excludedPublicWebchatCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${scopeAccountId ? scopeAccountId.slice(0, 8) : "unset"}`
|
|
12168
12455
|
);
|
|
@@ -12194,15 +12481,15 @@ var WEBCHAT_REPLY_TOOL = "mcp__webchat-channel__reply";
|
|
|
12194
12481
|
var CHANNEL_REPLY_TOOL = "mcp__channel__reply";
|
|
12195
12482
|
var CHANNEL_REPLY_DOCUMENT_TOOL = "mcp__channel__reply-document";
|
|
12196
12483
|
var SEND_USER_FILE_TOOL = "SendUserFile";
|
|
12197
|
-
var
|
|
12484
|
+
var CLI_MARKER_PREFIXES = ["<local-command-", "<command-name>", "<command-message>"];
|
|
12198
12485
|
var HARNESS_LIFECYCLE_PREFIXES = ["<task-notification>"];
|
|
12199
12486
|
var CC_IMAGE_VISION_HINT = /^\[Image:[^\]]*Multiply coordinates by [\d.]+ to map to original image\.\]$/;
|
|
12200
12487
|
var COMPACT_SUMMARY_PREFIX = "This session is being continued from a previous conversation that ran out of context.";
|
|
12201
12488
|
function isCompactSummaryRow(row, content) {
|
|
12202
12489
|
return row.isCompactSummary === true || content !== null && content.startsWith(COMPACT_SUMMARY_PREFIX);
|
|
12203
12490
|
}
|
|
12204
|
-
var
|
|
12205
|
-
var
|
|
12491
|
+
var CHANNEL_WRAPPER = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\/channel>$/;
|
|
12492
|
+
var SOURCE_ATTR = /\bsource="([^"]+)"/;
|
|
12206
12493
|
var SOURCE_MAP = {
|
|
12207
12494
|
whatsapp: "whatsapp",
|
|
12208
12495
|
"whatsapp-channel": "whatsapp",
|
|
@@ -12212,18 +12499,18 @@ var SOURCE_MAP = {
|
|
|
12212
12499
|
"webchat-channel": "webchat"
|
|
12213
12500
|
};
|
|
12214
12501
|
function isScheduleSource(text) {
|
|
12215
|
-
const m = text.match(
|
|
12502
|
+
const m = text.match(CHANNEL_WRAPPER);
|
|
12216
12503
|
if (!m) return false;
|
|
12217
|
-
return m[1].match(
|
|
12504
|
+
return m[1].match(SOURCE_ATTR)?.[1] === "schedule";
|
|
12218
12505
|
}
|
|
12219
12506
|
function unwrapChannel(text) {
|
|
12220
|
-
const m = text.match(
|
|
12507
|
+
const m = text.match(CHANNEL_WRAPPER);
|
|
12221
12508
|
if (!m) return { text };
|
|
12222
|
-
let source = SOURCE_MAP[m[1].match(
|
|
12509
|
+
let source = SOURCE_MAP[m[1].match(SOURCE_ATTR)?.[1] ?? ""];
|
|
12223
12510
|
let inner = m[2];
|
|
12224
|
-
const im = inner.match(
|
|
12511
|
+
const im = inner.match(CHANNEL_WRAPPER);
|
|
12225
12512
|
if (im) {
|
|
12226
|
-
const innerSource = SOURCE_MAP[im[1].match(
|
|
12513
|
+
const innerSource = SOURCE_MAP[im[1].match(SOURCE_ATTR)?.[1] ?? ""];
|
|
12227
12514
|
if (innerSource) {
|
|
12228
12515
|
source = innerSource;
|
|
12229
12516
|
inner = im[2];
|
|
@@ -12395,7 +12682,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
12395
12682
|
}
|
|
12396
12683
|
if (row.type === "queue-operation" && row.operation === "enqueue") {
|
|
12397
12684
|
const content = asString(row.content);
|
|
12398
|
-
if (content !== null &&
|
|
12685
|
+
if (content !== null && CHANNEL_WRAPPER.test(content)) {
|
|
12399
12686
|
const u = unwrapChannel(content);
|
|
12400
12687
|
turnCtx.lastChannelSource = u.source;
|
|
12401
12688
|
if (!isScheduleSource(content)) {
|
|
@@ -12428,7 +12715,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
12428
12715
|
}
|
|
12429
12716
|
const text = asString(msg.content);
|
|
12430
12717
|
if (text === null) continue;
|
|
12431
|
-
if (
|
|
12718
|
+
if (CLI_MARKER_PREFIXES.some((p) => text.startsWith(p))) continue;
|
|
12432
12719
|
if (HARNESS_LIFECYCLE_PREFIXES.some((p) => text.startsWith(p))) continue;
|
|
12433
12720
|
if (CC_IMAGE_VISION_HINT.test(text)) continue;
|
|
12434
12721
|
const isChannel = row.isMeta === true && typeof row.origin === "object" && row.origin !== null && row.origin.kind === "channel" || // Native Claude Code channels (maxy-lite) journal the delivery twin as a PLAIN
|
|
@@ -12436,7 +12723,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
12436
12723
|
// `isMeta`/`origin` stamp. Detect it by the wrapper so it is treated as the
|
|
12437
12724
|
// channel twin and consumes the enqueue row's suppression — otherwise the
|
|
12438
12725
|
// enqueue row AND this row both render and every channel message doubles.
|
|
12439
|
-
|
|
12726
|
+
CHANNEL_WRAPPER.test(text);
|
|
12440
12727
|
if (isChannel) {
|
|
12441
12728
|
const u = unwrapChannel(text);
|
|
12442
12729
|
turnCtx.lastChannelSource = u.source;
|
|
@@ -12454,7 +12741,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
12454
12741
|
|
|
12455
12742
|
// app/lib/whatsapp-reader/enrich-attachments.ts
|
|
12456
12743
|
import { readFileSync as readFileSync20, readdirSync as readdirSync13, existsSync as existsSync16, statSync as statSync7, realpathSync as realpathSync5 } from "fs";
|
|
12457
|
-
import { basename as basename5, dirname as dirname6, join as
|
|
12744
|
+
import { basename as basename5, dirname as dirname6, join as join22, resolve as resolve16, sep as sep4 } from "path";
|
|
12458
12745
|
var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
|
|
12459
12746
|
function adminAttachmentIds(text) {
|
|
12460
12747
|
const ids = [];
|
|
@@ -12467,7 +12754,7 @@ function adminAttachmentIds(text) {
|
|
|
12467
12754
|
function adminAttachmentsFromText(text, uploadsBase) {
|
|
12468
12755
|
const out = [];
|
|
12469
12756
|
for (const id of adminAttachmentIds(text)) {
|
|
12470
|
-
const meta = readAttachmentMeta(
|
|
12757
|
+
const meta = readAttachmentMeta(join22(uploadsBase, id));
|
|
12471
12758
|
if (meta) out.push(meta);
|
|
12472
12759
|
}
|
|
12473
12760
|
return out;
|
|
@@ -12517,7 +12804,7 @@ function claudeUploadAttachmentsFromText(text, sessionId) {
|
|
|
12517
12804
|
function readSidecar(dir, attachmentId) {
|
|
12518
12805
|
let parsed;
|
|
12519
12806
|
try {
|
|
12520
|
-
parsed = JSON.parse(readFileSync20(
|
|
12807
|
+
parsed = JSON.parse(readFileSync20(join22(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
12521
12808
|
} catch {
|
|
12522
12809
|
return null;
|
|
12523
12810
|
}
|
|
@@ -12546,7 +12833,7 @@ function listSessionAttachmentsInDir(dir) {
|
|
|
12546
12833
|
}
|
|
12547
12834
|
const sidecars = [];
|
|
12548
12835
|
for (const name of entries2) {
|
|
12549
|
-
const sidecar = readSidecar(
|
|
12836
|
+
const sidecar = readSidecar(join22(dir, name), name);
|
|
12550
12837
|
if (sidecar) sidecars.push(sidecar);
|
|
12551
12838
|
}
|
|
12552
12839
|
sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
|
|
@@ -12706,25 +12993,59 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
12706
12993
|
}
|
|
12707
12994
|
const cfg = claudeConfigDir();
|
|
12708
12995
|
if (!cfg) return c.json({ conversations: [] });
|
|
12709
|
-
const projectsRoot =
|
|
12996
|
+
const projectsRoot = join23(cfg, "projects");
|
|
12710
12997
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
12711
12998
|
const rows = [];
|
|
12712
12999
|
let sessionRowsExcludedUntagged = 0;
|
|
13000
|
+
const account = multiAccount ? validAccounts.find((a) => a.accountId === scopeAccountId) ?? null : resolveAccount();
|
|
13001
|
+
const titleCache = await loadTitleCache(account?.accountDir ?? null);
|
|
13002
|
+
const titleCacheUpdates = /* @__PURE__ */ new Map();
|
|
13003
|
+
let enumeratedCount = 0;
|
|
13004
|
+
let bodyReadCount = 0;
|
|
13005
|
+
let bodyReadFails = 0;
|
|
13006
|
+
let titleCacheHits = 0;
|
|
13007
|
+
let bodyScans = 0;
|
|
13008
|
+
const walkStartedAt = Date.now();
|
|
13009
|
+
const untaggedSenders = /* @__PURE__ */ new Set();
|
|
12713
13010
|
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
13011
|
+
enumeratedCount += 1;
|
|
12714
13012
|
const sessionId = basename6(path3).replace(/\.jsonl$/, "");
|
|
12715
13013
|
const projectDir = dirname7(path3);
|
|
12716
|
-
const meta = readSidecarMeta(
|
|
13014
|
+
const meta = readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
12717
13015
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
12718
13016
|
if (multiAccount) {
|
|
12719
|
-
if (meta.accountId === null)
|
|
13017
|
+
if (meta.accountId === null) {
|
|
13018
|
+
sessionRowsExcludedUntagged += 1;
|
|
13019
|
+
if (meta.senderId) untaggedSenders.add(meta.senderId);
|
|
13020
|
+
}
|
|
12720
13021
|
if (meta.accountId !== scopeAccountId) continue;
|
|
12721
13022
|
}
|
|
12722
13023
|
let body = "";
|
|
13024
|
+
let bodyRead = false;
|
|
12723
13025
|
try {
|
|
12724
13026
|
body = readFileSync21(path3, "utf8");
|
|
12725
|
-
|
|
13027
|
+
bodyRead = true;
|
|
13028
|
+
bodyReadCount += 1;
|
|
13029
|
+
} catch (err) {
|
|
13030
|
+
bodyReadFails += 1;
|
|
13031
|
+
console.error(
|
|
13032
|
+
`[wa-reader] op=body-read-failed sessionId=${sessionId.slice(0, 8)} code=${errCode(err)}`
|
|
13033
|
+
);
|
|
13034
|
+
}
|
|
13035
|
+
let aiTitle = null;
|
|
13036
|
+
if (bodyRead) {
|
|
13037
|
+
const cached3 = titleCache.get(sessionId);
|
|
13038
|
+
if (cached3 && cached3.scannedBytes === Buffer.byteLength(body, "utf8")) {
|
|
13039
|
+
aiTitle = cached3.aiTitle;
|
|
13040
|
+
titleCacheHits += 1;
|
|
13041
|
+
} else {
|
|
13042
|
+
const scanned = scanAiTitleFromBody(body);
|
|
13043
|
+
aiTitle = scanned.aiTitle;
|
|
13044
|
+
titleCacheUpdates.set(sessionId, scanned);
|
|
13045
|
+
bodyScans += 1;
|
|
13046
|
+
}
|
|
12726
13047
|
}
|
|
12727
|
-
const { title } =
|
|
13048
|
+
const { title } = resolveTitleFromParts(sessionId, userTitles, aiTitle);
|
|
12728
13049
|
const turns = parseTranscript(body.split("\n"));
|
|
12729
13050
|
const lastMessageAt = lastTurnTs(turns);
|
|
12730
13051
|
const modelGated = isModelGated(turns);
|
|
@@ -12742,8 +13063,24 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
12742
13063
|
model: lastAssistantModel(body)
|
|
12743
13064
|
});
|
|
12744
13065
|
}
|
|
13066
|
+
if (sessionRowsExcludedUntagged > 0) {
|
|
13067
|
+
console.log(
|
|
13068
|
+
`[wa-reader] op=untagged-sidecars count=${sessionRowsExcludedUntagged} senders=${[...untaggedSenders].sort().join(",")} route=conversations`
|
|
13069
|
+
);
|
|
13070
|
+
}
|
|
12745
13071
|
const sessionRows = selectReaderChannelSessions(rows).filter((r) => r.channel !== "whatsapp");
|
|
12746
|
-
|
|
13072
|
+
if (account && titleCacheUpdates.size > 0) {
|
|
13073
|
+
try {
|
|
13074
|
+
await saveTitleCacheMerged(account.accountDir, titleCacheUpdates);
|
|
13075
|
+
} catch (err) {
|
|
13076
|
+
console.error(
|
|
13077
|
+
`[wa-reader] op=title-cache-write-failed dir=${account.accountDir} reason=${err instanceof Error ? err.message : String(err)}`
|
|
13078
|
+
);
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
13081
|
+
console.log(
|
|
13082
|
+
`[wa-reader] op=list-walk enumerated=${enumeratedCount} read=${bodyReadCount} readFails=${bodyReadFails} titleCacheHits=${titleCacheHits} bodyScans=${bodyScans} ms=${Date.now() - walkStartedAt}`
|
|
13083
|
+
);
|
|
12747
13084
|
let users = [];
|
|
12748
13085
|
if (account) {
|
|
12749
13086
|
try {
|
|
@@ -12844,24 +13181,61 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12844
13181
|
const { value, state } = await getOrCompute("conversations-all", houseId, async () => {
|
|
12845
13182
|
const cfg = claudeConfigDir();
|
|
12846
13183
|
if (!cfg) return { conversations: [] };
|
|
12847
|
-
const projectsRoot =
|
|
13184
|
+
const projectsRoot = join23(cfg, "projects");
|
|
12848
13185
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
12849
13186
|
const validAccounts = listValidAccounts();
|
|
12850
13187
|
const validIds = new Set(validAccounts.map((a) => a.accountId));
|
|
12851
13188
|
const houseIds = new Set(validAccounts.filter((a) => a.config.role === "house").map((a) => a.accountId));
|
|
12852
13189
|
const rawByAccount = /* @__PURE__ */ new Map();
|
|
13190
|
+
const cachesByAccount = /* @__PURE__ */ new Map();
|
|
13191
|
+
const accountDirById = new Map(validAccounts.map((a) => [a.accountId, a.accountDir]));
|
|
13192
|
+
let enumeratedCount = 0;
|
|
13193
|
+
let bodyReadCount = 0;
|
|
13194
|
+
let bodyReadFails = 0;
|
|
13195
|
+
let titleCacheHits = 0;
|
|
13196
|
+
let bodyScans = 0;
|
|
13197
|
+
const walkStartedAt = Date.now();
|
|
13198
|
+
let untaggedCount = 0;
|
|
13199
|
+
const untaggedSenders = /* @__PURE__ */ new Set();
|
|
12853
13200
|
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
13201
|
+
enumeratedCount += 1;
|
|
12854
13202
|
const sessionId = basename6(path3).replace(/\.jsonl$/, "");
|
|
12855
13203
|
const projectDir = dirname7(path3);
|
|
12856
|
-
const meta = readSidecarMeta(
|
|
13204
|
+
const meta = readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
12857
13205
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
12858
|
-
if (meta.accountId === null
|
|
13206
|
+
if (meta.accountId === null) {
|
|
13207
|
+
untaggedCount += 1;
|
|
13208
|
+
if (meta.senderId) untaggedSenders.add(meta.senderId);
|
|
13209
|
+
continue;
|
|
13210
|
+
}
|
|
13211
|
+
if (!validIds.has(meta.accountId)) continue;
|
|
12859
13212
|
let body = "";
|
|
13213
|
+
let bodyRead = false;
|
|
12860
13214
|
try {
|
|
12861
13215
|
body = readFileSync21(path3, "utf8");
|
|
12862
|
-
|
|
13216
|
+
bodyRead = true;
|
|
13217
|
+
bodyReadCount += 1;
|
|
13218
|
+
} catch (err) {
|
|
13219
|
+
bodyReadFails += 1;
|
|
13220
|
+
console.error(`[wa-reader] op=body-read-failed sessionId=${sessionId.slice(0, 8)} code=${errCode(err)}`);
|
|
13221
|
+
}
|
|
13222
|
+
let aiTitle = null;
|
|
13223
|
+
if (bodyRead) {
|
|
13224
|
+
let cache3 = cachesByAccount.get(meta.accountId);
|
|
13225
|
+
if (cache3 === void 0) {
|
|
13226
|
+
cache3 = await loadTitleCache(accountDirById.get(meta.accountId) ?? null);
|
|
13227
|
+
cachesByAccount.set(meta.accountId, cache3);
|
|
13228
|
+
}
|
|
13229
|
+
const cached3 = cache3.get(sessionId);
|
|
13230
|
+
if (cached3 && cached3.scannedBytes === Buffer.byteLength(body, "utf8")) {
|
|
13231
|
+
aiTitle = cached3.aiTitle;
|
|
13232
|
+
titleCacheHits += 1;
|
|
13233
|
+
} else {
|
|
13234
|
+
aiTitle = scanAiTitleFromBody(body).aiTitle;
|
|
13235
|
+
bodyScans += 1;
|
|
13236
|
+
}
|
|
12863
13237
|
}
|
|
12864
|
-
const { title } =
|
|
13238
|
+
const { title } = resolveTitleFromParts(sessionId, userTitles, aiTitle);
|
|
12865
13239
|
const turns = parseTranscript(body.split("\n"));
|
|
12866
13240
|
const row = {
|
|
12867
13241
|
sessionId,
|
|
@@ -12879,6 +13253,14 @@ app5.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
12879
13253
|
list.push(row);
|
|
12880
13254
|
rawByAccount.set(meta.accountId, list);
|
|
12881
13255
|
}
|
|
13256
|
+
console.log(
|
|
13257
|
+
`[wa-reader] op=list-walk enumerated=${enumeratedCount} read=${bodyReadCount} readFails=${bodyReadFails} titleCacheHits=${titleCacheHits} bodyScans=${bodyScans} ms=${Date.now() - walkStartedAt}`
|
|
13258
|
+
);
|
|
13259
|
+
if (untaggedCount > 0) {
|
|
13260
|
+
console.log(
|
|
13261
|
+
`[wa-reader] op=untagged-sidecars count=${untaggedCount} senders=${[...untaggedSenders].sort().join(",")} route=conversations-all`
|
|
13262
|
+
);
|
|
13263
|
+
}
|
|
12882
13264
|
let users = [];
|
|
12883
13265
|
try {
|
|
12884
13266
|
users = readUsersFile(USERS_FILE) ?? [];
|
|
@@ -13018,7 +13400,7 @@ function openTaskKey(turns) {
|
|
|
13018
13400
|
}
|
|
13019
13401
|
function readSubagentMeta(dir, hex) {
|
|
13020
13402
|
try {
|
|
13021
|
-
const m = JSON.parse(readFileSync21(
|
|
13403
|
+
const m = JSON.parse(readFileSync21(join23(dir, `agent-${hex}.meta.json`), "utf8"));
|
|
13022
13404
|
if (typeof m.agentType === "string" && typeof m.description === "string" && typeof m.toolUseId === "string") {
|
|
13023
13405
|
return { agentType: m.agentType, description: m.description, toolUseId: m.toolUseId };
|
|
13024
13406
|
}
|
|
@@ -13050,7 +13432,7 @@ function runActivityTick(c) {
|
|
|
13050
13432
|
let size = 0;
|
|
13051
13433
|
let mtimeMs = 0;
|
|
13052
13434
|
try {
|
|
13053
|
-
const st = statSync8(
|
|
13435
|
+
const st = statSync8(join23(c.subagentsDir, name));
|
|
13054
13436
|
size = st.size;
|
|
13055
13437
|
mtimeMs = st.mtimeMs;
|
|
13056
13438
|
} catch {
|
|
@@ -13095,11 +13477,11 @@ app5.get("/stream", requireAdminSession, (c) => {
|
|
|
13095
13477
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
13096
13478
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
13097
13479
|
const cfg = claudeConfigDir();
|
|
13098
|
-
const projectsRoot = cfg ? resolve17(
|
|
13480
|
+
const projectsRoot = cfg ? resolve17(join23(cfg, "projects")) : null;
|
|
13099
13481
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
13100
13482
|
return c.json({ error: "bad session reference" }, 400);
|
|
13101
13483
|
}
|
|
13102
|
-
const jsonlPath = resolve17(
|
|
13484
|
+
const jsonlPath = resolve17(join23(projectDir, `${sessionId}.jsonl`));
|
|
13103
13485
|
if (!jsonlPath.startsWith(projectsRoot + sep5)) {
|
|
13104
13486
|
return c.json({ error: "bad session reference" }, 400);
|
|
13105
13487
|
}
|
|
@@ -13126,7 +13508,7 @@ data: ${JSON.stringify(turn)}
|
|
|
13126
13508
|
let offset = resumeOffset(lastEventId, fileEnd0);
|
|
13127
13509
|
console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
|
|
13128
13510
|
const sessionKey = c.req.query("session_key") ?? "";
|
|
13129
|
-
const subagentsDir = resolve17(
|
|
13511
|
+
const subagentsDir = resolve17(join23(projectDir, sessionId, "subagents"));
|
|
13130
13512
|
console.log(
|
|
13131
13513
|
`[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync17(subagentsDir)}`
|
|
13132
13514
|
);
|
|
@@ -13255,7 +13637,7 @@ app5.get("/directives", requireAdminSession, (c) => {
|
|
|
13255
13637
|
const pid = Number(pidPart);
|
|
13256
13638
|
let len = 0;
|
|
13257
13639
|
try {
|
|
13258
|
-
len = statSync8(
|
|
13640
|
+
len = statSync8(join23(dir, name)).size;
|
|
13259
13641
|
} catch {
|
|
13260
13642
|
}
|
|
13261
13643
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -13270,7 +13652,7 @@ app5.get("/directive", requireAdminSession, (c) => {
|
|
|
13270
13652
|
}
|
|
13271
13653
|
const dir = directiveStoreDir(sessionId);
|
|
13272
13654
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
13273
|
-
const path3 = resolve17(
|
|
13655
|
+
const path3 = resolve17(join23(dir, name));
|
|
13274
13656
|
if (!path3.startsWith(dir + sep5)) return c.json({ error: "bad reference" }, 400);
|
|
13275
13657
|
let body;
|
|
13276
13658
|
try {
|
|
@@ -13328,7 +13710,7 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
13328
13710
|
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!q2}`);
|
|
13329
13711
|
}
|
|
13330
13712
|
if (r.attachmentId) {
|
|
13331
|
-
const meta = readAttachmentMeta(
|
|
13713
|
+
const meta = readAttachmentMeta(join23(uploadsBase, r.attachmentId));
|
|
13332
13714
|
if (meta) return { ...turn, attachments: [meta] };
|
|
13333
13715
|
}
|
|
13334
13716
|
return turn;
|
|
@@ -13709,7 +14091,7 @@ var whatsapp_reader_default = app5;
|
|
|
13709
14091
|
|
|
13710
14092
|
// server/routes/public-reader.ts
|
|
13711
14093
|
import { statSync as statSync9, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch3, readFileSync as readFileSync22, readdirSync as readdirSync15 } from "fs";
|
|
13712
|
-
import { basename as basename7, dirname as dirname8, join as
|
|
14094
|
+
import { basename as basename7, dirname as dirname8, join as join24, resolve as resolve18, sep as sep6 } from "path";
|
|
13713
14095
|
|
|
13714
14096
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
13715
14097
|
var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -13783,10 +14165,10 @@ function enumeratePublicRows() {
|
|
|
13783
14165
|
const cfg = claudeConfigDir();
|
|
13784
14166
|
if (!cfg) return [];
|
|
13785
14167
|
const rows = [];
|
|
13786
|
-
for (const { path: path3 } of enumerateJsonls(
|
|
14168
|
+
for (const { path: path3 } of enumerateJsonls(join24(cfg, "projects"))) {
|
|
13787
14169
|
const sessionId = basename7(path3).replace(/\.jsonl$/, "");
|
|
13788
14170
|
const projectDir = dirname8(path3);
|
|
13789
|
-
const meta = readSidecarMeta(
|
|
14171
|
+
const meta = readSidecarMeta(join24(projectDir, `${sessionId}.meta.json`));
|
|
13790
14172
|
rows.push({
|
|
13791
14173
|
sessionId,
|
|
13792
14174
|
projectDir,
|
|
@@ -13862,15 +14244,15 @@ app6.get("/stream", (c) => {
|
|
|
13862
14244
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
13863
14245
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
13864
14246
|
const cfg = claudeConfigDir();
|
|
13865
|
-
const projectsRoot = cfg ? resolve18(
|
|
14247
|
+
const projectsRoot = cfg ? resolve18(join24(cfg, "projects")) : null;
|
|
13866
14248
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
13867
14249
|
return c.json({ error: "bad session reference" }, 400);
|
|
13868
14250
|
}
|
|
13869
|
-
const jsonlPath = resolve18(
|
|
14251
|
+
const jsonlPath = resolve18(join24(projectDir, `${sessionId}.jsonl`));
|
|
13870
14252
|
if (!jsonlPath.startsWith(projectsRoot + sep6)) {
|
|
13871
14253
|
return c.json({ error: "bad session reference" }, 400);
|
|
13872
14254
|
}
|
|
13873
|
-
const meta = readSidecarMeta(
|
|
14255
|
+
const meta = readSidecarMeta(join24(projectDir, `${sessionId}.meta.json`));
|
|
13874
14256
|
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
13875
14257
|
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
13876
14258
|
if (!(isPublicWebchat && owns)) {
|
|
@@ -14007,16 +14389,16 @@ var public_reader_default = app6;
|
|
|
14007
14389
|
|
|
14008
14390
|
// server/routes/webchat.ts
|
|
14009
14391
|
import { basename as basename8, dirname as dirname9 } from "path";
|
|
14010
|
-
import { join as
|
|
14392
|
+
import { join as join27 } from "path";
|
|
14011
14393
|
import { existsSync as existsSync19, readdirSync as readdirSync17, readFileSync as readFileSync25, renameSync as renameSync4, statSync as statSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
14012
14394
|
|
|
14013
14395
|
// server/canonical-webchat-override.ts
|
|
14014
14396
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync7, renameSync as renameSync2 } from "fs";
|
|
14015
|
-
import { join as
|
|
14397
|
+
import { join as join25 } from "path";
|
|
14016
14398
|
var FILE = "canonical-webchat-session.json";
|
|
14017
14399
|
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
14018
14400
|
function canonicalOverridePath(accountDir) {
|
|
14019
|
-
return
|
|
14401
|
+
return join25(accountDir, FILE);
|
|
14020
14402
|
}
|
|
14021
14403
|
function readRaw(accountDir) {
|
|
14022
14404
|
try {
|
|
@@ -14121,7 +14503,7 @@ function isNewSessionEffortLevel(value) {
|
|
|
14121
14503
|
|
|
14122
14504
|
// ../services/claude-session-manager/src/sidecar-store.ts
|
|
14123
14505
|
import { existsSync as existsSync18, mkdirSync as mkdirSync4, readdirSync as readdirSync16, readFileSync as readFileSync24, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
14124
|
-
import { join as
|
|
14506
|
+
import { join as join26 } from "path";
|
|
14125
14507
|
function escapeRegExp(s) {
|
|
14126
14508
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14127
14509
|
}
|
|
@@ -14129,7 +14511,7 @@ function createSidecarStore(config) {
|
|
|
14129
14511
|
const { suffix, validate: validate2 } = config;
|
|
14130
14512
|
const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
|
|
14131
14513
|
function pathFor(sessionsDir, id) {
|
|
14132
|
-
return
|
|
14514
|
+
return join26(sessionsDir, `${id}${suffix}`);
|
|
14133
14515
|
}
|
|
14134
14516
|
function write(sessionsDir, id, record) {
|
|
14135
14517
|
mkdirSync4(sessionsDir, { recursive: true });
|
|
@@ -14173,7 +14555,7 @@ function createSidecarStore(config) {
|
|
|
14173
14555
|
const out = [];
|
|
14174
14556
|
for (const name of names) {
|
|
14175
14557
|
if (!suffixRe.test(name)) continue;
|
|
14176
|
-
const path3 =
|
|
14558
|
+
const path3 = join26(sessionsDir, name);
|
|
14177
14559
|
let raw;
|
|
14178
14560
|
try {
|
|
14179
14561
|
raw = readFileSync24(path3, "utf8");
|
|
@@ -14250,13 +14632,13 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
|
|
|
14250
14632
|
function locateSession(sessionId) {
|
|
14251
14633
|
const dir = findSessionProjectDir(sessionId);
|
|
14252
14634
|
if (!dir) return { projectDir: null, channel: null, accountId: null };
|
|
14253
|
-
const meta = readSidecarMeta(
|
|
14635
|
+
const meta = readSidecarMeta(join27(dir, `${sessionId}.meta.json`));
|
|
14254
14636
|
return { projectDir: dir, channel: meta.channel, accountId: meta.accountId };
|
|
14255
14637
|
}
|
|
14256
14638
|
function locateSidecar(sessionId) {
|
|
14257
14639
|
const cfg = claudeConfigDir();
|
|
14258
14640
|
if (!cfg) return null;
|
|
14259
|
-
const projectsRoot =
|
|
14641
|
+
const projectsRoot = join27(cfg, "projects");
|
|
14260
14642
|
let slugs;
|
|
14261
14643
|
try {
|
|
14262
14644
|
slugs = readdirSync17(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -14264,7 +14646,7 @@ function locateSidecar(sessionId) {
|
|
|
14264
14646
|
return null;
|
|
14265
14647
|
}
|
|
14266
14648
|
for (const slug of slugs) {
|
|
14267
|
-
const metaPath =
|
|
14649
|
+
const metaPath = join27(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
14268
14650
|
if (existsSync19(metaPath)) return readSidecarMeta(metaPath);
|
|
14269
14651
|
}
|
|
14270
14652
|
return null;
|
|
@@ -14274,7 +14656,7 @@ function readTargetOwner(sessionId) {
|
|
|
14274
14656
|
if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
|
|
14275
14657
|
const { projectDir } = locateSession(sessionId);
|
|
14276
14658
|
if (projectDir !== null) {
|
|
14277
|
-
return { found: true, owner: readSidecarMeta(
|
|
14659
|
+
return { found: true, owner: readSidecarMeta(join27(projectDir, `${sessionId}.meta.json`)).adminUserId };
|
|
14278
14660
|
}
|
|
14279
14661
|
return { found: false, owner: null };
|
|
14280
14662
|
}
|
|
@@ -14282,7 +14664,7 @@ function readSourceRowMeta(sessionId) {
|
|
|
14282
14664
|
const sidecar = locateSidecar(sessionId);
|
|
14283
14665
|
if (sidecar !== null) return sidecar;
|
|
14284
14666
|
const { projectDir } = locateSession(sessionId);
|
|
14285
|
-
if (projectDir !== null) return readSidecarMeta(
|
|
14667
|
+
if (projectDir !== null) return readSidecarMeta(join27(projectDir, `${sessionId}.meta.json`));
|
|
14286
14668
|
return null;
|
|
14287
14669
|
}
|
|
14288
14670
|
function ownedByRequester(owner, requesterUserId, primaryUserId) {
|
|
@@ -14295,7 +14677,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
14295
14677
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
14296
14678
|
if (projectDir === null) return null;
|
|
14297
14679
|
try {
|
|
14298
|
-
return statSync10(
|
|
14680
|
+
return statSync10(join27(projectDir, `${sessionId}.jsonl`)).size;
|
|
14299
14681
|
} catch {
|
|
14300
14682
|
return null;
|
|
14301
14683
|
}
|
|
@@ -14423,10 +14805,10 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
|
|
|
14423
14805
|
const cfg = claudeConfigDir();
|
|
14424
14806
|
if (!cfg) return null;
|
|
14425
14807
|
let best = null;
|
|
14426
|
-
for (const { path: path3, isSubagent, archived } of enumerateJsonls(
|
|
14808
|
+
for (const { path: path3, isSubagent, archived } of enumerateJsonls(join27(cfg, "projects"))) {
|
|
14427
14809
|
if (isSubagent || archived) continue;
|
|
14428
14810
|
const id = basename8(path3).slice(0, -".jsonl".length);
|
|
14429
|
-
const meta = readSidecarMeta(
|
|
14811
|
+
const meta = readSidecarMeta(join27(dirname9(path3), `${id}.meta.json`));
|
|
14430
14812
|
if (meta.role !== "admin" || meta.channel !== "webchat") continue;
|
|
14431
14813
|
if (multiAccount && meta.accountId !== scopeAccountId) continue;
|
|
14432
14814
|
let mtimeMs;
|
|
@@ -14730,7 +15112,7 @@ ${note}` : note;
|
|
|
14730
15112
|
console.error(`[webchat:settings] op=${op} outcome=refused value=${value} accountId=none reason=no-account-for-session`);
|
|
14731
15113
|
return c.json({ error: "account unresolved" }, 503);
|
|
14732
15114
|
}
|
|
14733
|
-
const accountJsonPath =
|
|
15115
|
+
const accountJsonPath = join27(account.accountDir, "account.json");
|
|
14734
15116
|
try {
|
|
14735
15117
|
const parsed = JSON.parse(readFileSync25(accountJsonPath, "utf8"));
|
|
14736
15118
|
if (op === "model") parsed.adminModel = value;
|
|
@@ -14778,7 +15160,7 @@ ${note}` : note;
|
|
|
14778
15160
|
const cfg2 = claudeConfigDir();
|
|
14779
15161
|
if (cfg2) {
|
|
14780
15162
|
const source2 = resolveBridgeSource(
|
|
14781
|
-
|
|
15163
|
+
join27(cfg2, "sessions"),
|
|
14782
15164
|
target,
|
|
14783
15165
|
(id) => locateSession(id).projectDir !== null
|
|
14784
15166
|
);
|
|
@@ -14805,7 +15187,7 @@ ${note}` : note;
|
|
|
14805
15187
|
const cfg = claudeConfigDir();
|
|
14806
15188
|
let projectDir = null;
|
|
14807
15189
|
if (cfg) {
|
|
14808
|
-
for (const { path: path3 } of enumerateJsonls(
|
|
15190
|
+
for (const { path: path3 } of enumerateJsonls(join27(cfg, "projects"))) {
|
|
14809
15191
|
if (basename8(path3) === `${sessionId}.jsonl`) {
|
|
14810
15192
|
projectDir = dirname9(path3);
|
|
14811
15193
|
break;
|
|
@@ -14813,7 +15195,7 @@ ${note}` : note;
|
|
|
14813
15195
|
}
|
|
14814
15196
|
}
|
|
14815
15197
|
if (source === "latest" && requesterUserId !== null) {
|
|
14816
|
-
const owner = readSidecarMeta(
|
|
15198
|
+
const owner = readSidecarMeta(join27(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
|
|
14817
15199
|
if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
|
|
14818
15200
|
console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
|
|
14819
15201
|
}
|
|
@@ -14846,7 +15228,7 @@ import { resolve as resolve19 } from "path";
|
|
|
14846
15228
|
|
|
14847
15229
|
// app/lib/claude-agent/specialist-roster.ts
|
|
14848
15230
|
import { existsSync as existsSync20, readFileSync as readFileSync26, readdirSync as readdirSync18 } from "fs";
|
|
14849
|
-
import { join as
|
|
15231
|
+
import { join as join28 } from "path";
|
|
14850
15232
|
function field(fm, name) {
|
|
14851
15233
|
const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
|
|
14852
15234
|
if (!m) return null;
|
|
@@ -14865,7 +15247,7 @@ function readSpecialistRoster(specialistsDir) {
|
|
|
14865
15247
|
if (!file.endsWith(".md")) continue;
|
|
14866
15248
|
let raw;
|
|
14867
15249
|
try {
|
|
14868
|
-
raw = readFileSync26(
|
|
15250
|
+
raw = readFileSync26(join28(specialistsDir, file), "utf-8");
|
|
14869
15251
|
} catch (err) {
|
|
14870
15252
|
skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
|
|
14871
15253
|
continue;
|
|
@@ -14982,7 +15364,7 @@ var webchat_greeting_default = app7;
|
|
|
14982
15364
|
|
|
14983
15365
|
// server/routes/telegram.ts
|
|
14984
15366
|
import { homedir as homedir2 } from "os";
|
|
14985
|
-
import { resolve as resolve20, join as
|
|
15367
|
+
import { resolve as resolve20, join as join29 } from "path";
|
|
14986
15368
|
import { existsSync as existsSync21, readFileSync as readFileSync27 } from "fs";
|
|
14987
15369
|
|
|
14988
15370
|
// app/lib/telegram/gateway/instance.ts
|
|
@@ -15042,7 +15424,7 @@ function routeTelegramUpdate(input) {
|
|
|
15042
15424
|
var TAG30 = "[telegram-inbound]";
|
|
15043
15425
|
function configDirName() {
|
|
15044
15426
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
15045
|
-
const brandPath =
|
|
15427
|
+
const brandPath = join29(platformRoot5, "config", "brand.json");
|
|
15046
15428
|
if (existsSync21(brandPath)) {
|
|
15047
15429
|
try {
|
|
15048
15430
|
return JSON.parse(readFileSync27(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
@@ -15131,16 +15513,16 @@ app8.post("/", async (c) => {
|
|
|
15131
15513
|
var telegram_default = app8;
|
|
15132
15514
|
|
|
15133
15515
|
// server/routes/quickbooks.ts
|
|
15134
|
-
import { join as
|
|
15516
|
+
import { join as join30 } from "path";
|
|
15135
15517
|
import { existsSync as existsSync22, readFileSync as readFileSync28, writeFileSync as writeFileSync10, mkdirSync as mkdirSync5, rmSync, renameSync as renameSync5 } from "fs";
|
|
15136
15518
|
var TAG31 = "[quickbooks]";
|
|
15137
15519
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
15138
15520
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
15139
15521
|
function pendingPath(accountDir, state) {
|
|
15140
|
-
return
|
|
15522
|
+
return join30(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
|
|
15141
15523
|
}
|
|
15142
15524
|
function storePath(accountDir) {
|
|
15143
|
-
return
|
|
15525
|
+
return join30(accountDir, "secrets", "quickbooks.json");
|
|
15144
15526
|
}
|
|
15145
15527
|
async function completeConsent(args) {
|
|
15146
15528
|
const { accountDir, code, realmId } = args;
|
|
@@ -15207,7 +15589,7 @@ async function completeConsent(args) {
|
|
|
15207
15589
|
refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
|
|
15208
15590
|
lastRefresh: now
|
|
15209
15591
|
};
|
|
15210
|
-
mkdirSync5(
|
|
15592
|
+
mkdirSync5(join30(accountDir, "secrets"), { recursive: true });
|
|
15211
15593
|
writeFileSync10(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
15212
15594
|
const persisted = JSON.parse(readFileSync28(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
15213
15595
|
console.error(`${TAG31} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
@@ -15249,7 +15631,7 @@ function claudeBin() {
|
|
|
15249
15631
|
}
|
|
15250
15632
|
|
|
15251
15633
|
// server/routes/onboarding.ts
|
|
15252
|
-
import { dirname as dirname11, join as
|
|
15634
|
+
import { dirname as dirname11, join as join32 } from "path";
|
|
15253
15635
|
|
|
15254
15636
|
// app/lib/claude-keychain.ts
|
|
15255
15637
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -15379,7 +15761,7 @@ function readHostCredsBlob(service) {
|
|
|
15379
15761
|
|
|
15380
15762
|
// ../lib/admins-write/src/index.ts
|
|
15381
15763
|
import { existsSync as existsSync23, readFileSync as readFileSync29, writeFileSync as writeFileSync11, renameSync as renameSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync19, statSync as statSync11, appendFileSync as appendFileSync3 } from "fs";
|
|
15382
|
-
import { dirname as dirname10, join as
|
|
15764
|
+
import { dirname as dirname10, join as join31 } from "path";
|
|
15383
15765
|
function id8(value) {
|
|
15384
15766
|
return value.slice(0, 8);
|
|
15385
15767
|
}
|
|
@@ -15452,7 +15834,7 @@ function writeAdminEntry(input) {
|
|
|
15452
15834
|
return result;
|
|
15453
15835
|
}
|
|
15454
15836
|
try {
|
|
15455
|
-
const accountJsonPath =
|
|
15837
|
+
const accountJsonPath = join31(input.accountDir, "account.json");
|
|
15456
15838
|
if (!existsSync23(accountJsonPath)) {
|
|
15457
15839
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
15458
15840
|
}
|
|
@@ -15500,13 +15882,13 @@ function computeAdminStoreDivergence(input) {
|
|
|
15500
15882
|
}
|
|
15501
15883
|
for (const entry of entries2) {
|
|
15502
15884
|
if (entry.startsWith(".")) continue;
|
|
15503
|
-
const accountDir =
|
|
15885
|
+
const accountDir = join31(input.accountsDir, entry);
|
|
15504
15886
|
try {
|
|
15505
15887
|
if (!statSync11(accountDir).isDirectory()) continue;
|
|
15506
15888
|
} catch {
|
|
15507
15889
|
continue;
|
|
15508
15890
|
}
|
|
15509
|
-
const accountJsonPath =
|
|
15891
|
+
const accountJsonPath = join31(accountDir, "account.json");
|
|
15510
15892
|
if (!existsSync23(accountJsonPath)) continue;
|
|
15511
15893
|
let admins = [];
|
|
15512
15894
|
try {
|
|
@@ -15684,7 +16066,7 @@ app10.post("/claude-auth", async (c) => {
|
|
|
15684
16066
|
});
|
|
15685
16067
|
persistKeychainService(
|
|
15686
16068
|
writebackResult,
|
|
15687
|
-
(service) => writeFileSync12(
|
|
16069
|
+
(service) => writeFileSync12(join32(dirname11(CLAUDE_CREDENTIALS_FILE), ".keychain-service"), service, { mode: 384 }),
|
|
15688
16070
|
(line) => console.log(line)
|
|
15689
16071
|
);
|
|
15690
16072
|
if (darwinAuthChild === claudeProc2) darwinAuthChild = null;
|
|
@@ -15950,8 +16332,8 @@ var onboarding_default = app10;
|
|
|
15950
16332
|
|
|
15951
16333
|
// server/routes/client-error.ts
|
|
15952
16334
|
import { appendFileSync as appendFileSync4, existsSync as existsSync25, renameSync as renameSync8, statSync as statSync12 } from "fs";
|
|
15953
|
-
import { join as
|
|
15954
|
-
var CLIENT_ERRORS_LOG =
|
|
16335
|
+
import { join as join33 } from "path";
|
|
16336
|
+
var CLIENT_ERRORS_LOG = join33(LOG_DIR, "client-errors.log");
|
|
15955
16337
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
15956
16338
|
var MAX_BODY_SIZE = 8 * 1024;
|
|
15957
16339
|
var MAX_STACK_LEN = 2e3;
|
|
@@ -16340,12 +16722,12 @@ import { resolve as resolve21, basename as basename9 } from "path";
|
|
|
16340
16722
|
|
|
16341
16723
|
// app/lib/logs-read-resolve.ts
|
|
16342
16724
|
import { existsSync as existsSync26 } from "fs";
|
|
16343
|
-
import { join as
|
|
16725
|
+
import { join as join34 } from "path";
|
|
16344
16726
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
16345
16727
|
const tried = [filename];
|
|
16346
16728
|
const hits = [];
|
|
16347
16729
|
for (const dir of logDirs) {
|
|
16348
|
-
const fullPath =
|
|
16730
|
+
const fullPath = join34(dir, filename);
|
|
16349
16731
|
if (existsSync26(fullPath)) {
|
|
16350
16732
|
hits.push({ path: fullPath, dir });
|
|
16351
16733
|
}
|
|
@@ -16527,7 +16909,7 @@ app15.get("/", requireAdminSession, (c) => {
|
|
|
16527
16909
|
var claude_info_default = app15;
|
|
16528
16910
|
|
|
16529
16911
|
// server/routes/admin/attachment.ts
|
|
16530
|
-
import { readFile as
|
|
16912
|
+
import { readFile as readFile6, readdir as readdir2 } from "fs/promises";
|
|
16531
16913
|
import { existsSync as existsSync28 } from "fs";
|
|
16532
16914
|
import { resolve as resolve22 } from "path";
|
|
16533
16915
|
var app16 = new Hono();
|
|
@@ -16538,15 +16920,15 @@ function uploadsDirFor2(accountId, attachmentId) {
|
|
|
16538
16920
|
app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
16539
16921
|
const attachmentId = c.req.param("attachmentId");
|
|
16540
16922
|
const cacheKey = c.var.cacheKey;
|
|
16541
|
-
const
|
|
16923
|
+
const shortId3 = attachmentId.slice(0, 8);
|
|
16542
16924
|
const shortSession = (c.req.query("session_key") ?? "").slice(0, 8);
|
|
16543
16925
|
const pinnedAccountId = getAccountIdForSession(cacheKey);
|
|
16544
16926
|
if (!pinnedAccountId) {
|
|
16545
|
-
console.log(`[admin-attachment] op=miss attachmentId=${
|
|
16927
|
+
console.log(`[admin-attachment] op=miss attachmentId=${shortId3} session=${shortSession} status=401 reason=no-account`);
|
|
16546
16928
|
return new Response("Unauthorized", { status: 401 });
|
|
16547
16929
|
}
|
|
16548
16930
|
if (!ATTACHMENT_ID_RE3.test(attachmentId)) {
|
|
16549
|
-
console.log(`[admin-attachment] op=miss attachmentId=${
|
|
16931
|
+
console.log(`[admin-attachment] op=miss attachmentId=${shortId3} session=${shortSession} status=404 reason=bad-id`);
|
|
16550
16932
|
return new Response("Not found", { status: 404 });
|
|
16551
16933
|
}
|
|
16552
16934
|
let accountId = pinnedAccountId;
|
|
@@ -16566,7 +16948,7 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16566
16948
|
}
|
|
16567
16949
|
const shortPinned = pinnedAccountId.slice(0, 8);
|
|
16568
16950
|
const crossAccountNote = found && accountId !== pinnedAccountId ? ` resolvedAccount=${accountId.slice(0, 8)}` : "";
|
|
16569
|
-
console.log(`[admin-attachment] op=lookup attachmentId=${
|
|
16951
|
+
console.log(`[admin-attachment] op=lookup attachmentId=${shortId3} session=${shortSession} pinnedAccount=${shortPinned} found=${found}${crossAccountNote}`);
|
|
16570
16952
|
if (!found) {
|
|
16571
16953
|
return new Response("Not found", { status: 404 });
|
|
16572
16954
|
}
|
|
@@ -16576,7 +16958,7 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16576
16958
|
}
|
|
16577
16959
|
let meta;
|
|
16578
16960
|
try {
|
|
16579
|
-
meta = JSON.parse(await
|
|
16961
|
+
meta = JSON.parse(await readFile6(metaPath, "utf-8"));
|
|
16580
16962
|
} catch {
|
|
16581
16963
|
return new Response("Not found", { status: 404 });
|
|
16582
16964
|
}
|
|
@@ -16586,11 +16968,11 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16586
16968
|
return new Response("Not found", { status: 404 });
|
|
16587
16969
|
}
|
|
16588
16970
|
const filePath = resolve22(dir, dataFile);
|
|
16589
|
-
const buffer = await
|
|
16971
|
+
const buffer = await readFile6(filePath);
|
|
16590
16972
|
const inlineSafe = isInlineSafeMime(meta.mimeType);
|
|
16591
16973
|
const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
|
|
16592
16974
|
if (!inlineSafe) {
|
|
16593
|
-
console.log(`[admin-attachment] op=served attachmentId=${
|
|
16975
|
+
console.log(`[admin-attachment] op=served attachmentId=${shortId3} disposition=attachment reason=non-preview-mime mime=${meta.mimeType}`);
|
|
16594
16976
|
}
|
|
16595
16977
|
return new Response(new Uint8Array(buffer), {
|
|
16596
16978
|
headers: {
|
|
@@ -16604,7 +16986,7 @@ app16.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
16604
16986
|
var attachment_default = app16;
|
|
16605
16987
|
|
|
16606
16988
|
// server/routes/admin/session-upload.ts
|
|
16607
|
-
import { readFile as
|
|
16989
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
16608
16990
|
import { existsSync as existsSync29, statSync as statSync14 } from "fs";
|
|
16609
16991
|
var app17 = new Hono();
|
|
16610
16992
|
app17.get("/", requireAdminSession, async (c) => {
|
|
@@ -16634,7 +17016,7 @@ app17.get("/", requireAdminSession, async (c) => {
|
|
|
16634
17016
|
const inlineSafe = isInlineSafeMime(mimeType);
|
|
16635
17017
|
const safeName = file.replace(/["\\\r\n]/g, "_");
|
|
16636
17018
|
console.log(`[session-upload] op=served session=${short2} file=${file} disposition=${inlineSafe ? "inline" : "attachment"} mime=${mimeType}`);
|
|
16637
|
-
const buffer = await
|
|
17019
|
+
const buffer = await readFile7(resolution.absolute);
|
|
16638
17020
|
return new Response(new Uint8Array(buffer), {
|
|
16639
17021
|
headers: {
|
|
16640
17022
|
"Content-Type": mimeType,
|
|
@@ -16653,7 +17035,7 @@ import { readdirSync as readdirSync22, readFileSync as readFileSync33, existsSyn
|
|
|
16653
17035
|
// ../services/claude-session-manager/src/tool-surface.ts
|
|
16654
17036
|
import { readFileSync as readFileSync32, readdirSync as readdirSync21, statSync as statSync15, existsSync as existsSync30 } from "fs";
|
|
16655
17037
|
import { createHash as createHash5 } from "crypto";
|
|
16656
|
-
import { join as
|
|
17038
|
+
import { join as join35 } from "path";
|
|
16657
17039
|
|
|
16658
17040
|
// ../services/claude-session-manager/src/canonical-tool-names.ts
|
|
16659
17041
|
function canonicalToolName(plugin, tool) {
|
|
@@ -16906,13 +17288,13 @@ function loadToolSurface(pluginsRoots) {
|
|
|
16906
17288
|
const riskByTool = /* @__PURE__ */ new Map();
|
|
16907
17289
|
const manifestPaths = /* @__PURE__ */ new Map();
|
|
16908
17290
|
const loadOne = (pluginsRoot, plugin) => {
|
|
16909
|
-
const pluginDir =
|
|
17291
|
+
const pluginDir = join35(pluginsRoot, plugin);
|
|
16910
17292
|
try {
|
|
16911
17293
|
if (!statSync15(pluginDir).isDirectory()) return;
|
|
16912
17294
|
} catch {
|
|
16913
17295
|
return;
|
|
16914
17296
|
}
|
|
16915
|
-
const manifestPath =
|
|
17297
|
+
const manifestPath = join35(pluginDir, "PLUGIN.md");
|
|
16916
17298
|
if (!existsSync30(manifestPath)) return;
|
|
16917
17299
|
const raw = readFileSync32(manifestPath, "utf-8");
|
|
16918
17300
|
const frontmatter = extractFrontmatter(raw, plugin);
|
|
@@ -16958,9 +17340,9 @@ function loadToolSurface(pluginsRoots) {
|
|
|
16958
17340
|
for (const root of roots) {
|
|
16959
17341
|
const entries2 = readdirSync21(root).sort();
|
|
16960
17342
|
for (const plugin of entries2) {
|
|
16961
|
-
const pluginDir =
|
|
17343
|
+
const pluginDir = join35(root, plugin);
|
|
16962
17344
|
loadOne(root, plugin);
|
|
16963
|
-
const subRoot =
|
|
17345
|
+
const subRoot = join35(pluginDir, "plugins");
|
|
16964
17346
|
let subEntries;
|
|
16965
17347
|
try {
|
|
16966
17348
|
if (!statSync15(subRoot).isDirectory()) continue;
|
|
@@ -18228,7 +18610,7 @@ var sessions_default = app19;
|
|
|
18228
18610
|
|
|
18229
18611
|
// app/lib/claude-agent/spawn-context.ts
|
|
18230
18612
|
import { existsSync as existsSync33, readFileSync as readFileSync34, readdirSync as readdirSync23, statSync as statSync16 } from "fs";
|
|
18231
|
-
import { dirname as dirname12, resolve as resolve24, join as
|
|
18613
|
+
import { dirname as dirname12, resolve as resolve24, join as join36 } from "path";
|
|
18232
18614
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
18233
18615
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
18234
18616
|
try {
|
|
@@ -18269,8 +18651,8 @@ function frontmatterField(manifest, field2) {
|
|
|
18269
18651
|
function extractPluginToolDescriptions(pluginDir) {
|
|
18270
18652
|
const out = /* @__PURE__ */ new Map();
|
|
18271
18653
|
const candidates = [
|
|
18272
|
-
|
|
18273
|
-
|
|
18654
|
+
join36(pluginDir, "mcp/dist/index.js"),
|
|
18655
|
+
join36(pluginDir, "mcp/src/index.ts")
|
|
18274
18656
|
];
|
|
18275
18657
|
let raw = null;
|
|
18276
18658
|
for (const path3 of candidates) {
|
|
@@ -18328,7 +18710,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
18328
18710
|
const platformPlugins = resolve24(PLATFORM_ROOT, "plugins");
|
|
18329
18711
|
if (existsSync33(platformPlugins)) {
|
|
18330
18712
|
for (const name of readdirSync23(platformPlugins)) {
|
|
18331
|
-
const dir =
|
|
18713
|
+
const dir = join36(platformPlugins, name);
|
|
18332
18714
|
try {
|
|
18333
18715
|
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
18334
18716
|
} catch {
|
|
@@ -18338,11 +18720,11 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
18338
18720
|
const premiumRoot = resolve24(dirname12(PLATFORM_ROOT), "premium-plugins");
|
|
18339
18721
|
if (existsSync33(premiumRoot)) {
|
|
18340
18722
|
for (const bundle of readdirSync23(premiumRoot)) {
|
|
18341
|
-
const bundlePlugins =
|
|
18723
|
+
const bundlePlugins = join36(premiumRoot, bundle, "plugins");
|
|
18342
18724
|
if (!existsSync33(bundlePlugins)) continue;
|
|
18343
18725
|
try {
|
|
18344
18726
|
for (const name of readdirSync23(bundlePlugins)) {
|
|
18345
|
-
const dir =
|
|
18727
|
+
const dir = join36(bundlePlugins, name);
|
|
18346
18728
|
try {
|
|
18347
18729
|
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
18348
18730
|
} catch {
|
|
@@ -18356,7 +18738,7 @@ function listPluginDirs(accountPluginsDir) {
|
|
|
18356
18738
|
try {
|
|
18357
18739
|
for (const name of readdirSync23(accountPluginsDir)) {
|
|
18358
18740
|
if (out.has(name)) continue;
|
|
18359
|
-
const dir =
|
|
18741
|
+
const dir = join36(accountPluginsDir, name);
|
|
18360
18742
|
try {
|
|
18361
18743
|
if (statSync16(dir).isDirectory()) out.set(name, dir);
|
|
18362
18744
|
} catch {
|
|
@@ -18375,7 +18757,7 @@ function listAccountPluginNames(accountPluginsDir) {
|
|
|
18375
18757
|
try {
|
|
18376
18758
|
return readdirSync23(accountPluginsDir).filter((name) => {
|
|
18377
18759
|
try {
|
|
18378
|
-
return statSync16(
|
|
18760
|
+
return statSync16(join36(accountPluginsDir, name)).isDirectory();
|
|
18379
18761
|
} catch {
|
|
18380
18762
|
return false;
|
|
18381
18763
|
}
|
|
@@ -18418,7 +18800,7 @@ function computeActivePlugins(accountId) {
|
|
|
18418
18800
|
for (const name of emitNames) {
|
|
18419
18801
|
const dir = pluginDirs.get(name);
|
|
18420
18802
|
if (!dir) continue;
|
|
18421
|
-
const fm = readFrontmatter(
|
|
18803
|
+
const fm = readFrontmatter(join36(dir, "PLUGIN.md"));
|
|
18422
18804
|
if (!fm) continue;
|
|
18423
18805
|
const description = frontmatterField(`---
|
|
18424
18806
|
${fm}
|
|
@@ -18435,7 +18817,7 @@ ${fm}
|
|
|
18435
18817
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
18436
18818
|
);
|
|
18437
18819
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
18438
|
-
const hasSource = existsSync33(
|
|
18820
|
+
const hasSource = existsSync33(join36(dir, "mcp/dist/index.js")) || existsSync33(join36(dir, "mcp/src/index.ts"));
|
|
18439
18821
|
if (hasSource) {
|
|
18440
18822
|
console.warn(
|
|
18441
18823
|
`[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
|
|
@@ -18445,7 +18827,7 @@ ${fm}
|
|
|
18445
18827
|
const skillPaths = parseSkillPathsFromFrontmatter(fm);
|
|
18446
18828
|
const skills = [];
|
|
18447
18829
|
for (const relPath of skillPaths) {
|
|
18448
|
-
const skillPath =
|
|
18830
|
+
const skillPath = join36(dir, relPath);
|
|
18449
18831
|
const skillFm = readFrontmatter(skillPath);
|
|
18450
18832
|
if (!skillFm) continue;
|
|
18451
18833
|
const skillName = frontmatterField(`---
|
|
@@ -18489,7 +18871,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
18489
18871
|
const out = [];
|
|
18490
18872
|
for (const file of entries2.sort()) {
|
|
18491
18873
|
if (!file.endsWith(".md")) continue;
|
|
18492
|
-
const fm = readFrontmatter(
|
|
18874
|
+
const fm = readFrontmatter(join36(specialistsDir, file));
|
|
18493
18875
|
if (!fm) continue;
|
|
18494
18876
|
const wrapped = `---
|
|
18495
18877
|
${fm}
|
|
@@ -18885,10 +19267,10 @@ var events_default = app22;
|
|
|
18885
19267
|
|
|
18886
19268
|
// server/routes/admin/files.ts
|
|
18887
19269
|
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync35 } from "fs";
|
|
18888
|
-
import { readdir as readdir4, readFile as
|
|
19270
|
+
import { readdir as readdir4, readFile as readFile9, stat as stat7, mkdir as mkdir4, unlink as unlink4, rename as rename3, rmdir } from "fs/promises";
|
|
18889
19271
|
import { realpathSync as realpathSync7 } from "fs";
|
|
18890
19272
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
18891
|
-
import { basename as basename11, dirname as dirname13, join as
|
|
19273
|
+
import { basename as basename11, dirname as dirname13, join as join38, relative as relative5, resolve as resolve27, sep as sep8 } from "path";
|
|
18892
19274
|
import { Readable as Readable2 } from "stream";
|
|
18893
19275
|
|
|
18894
19276
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -19125,7 +19507,7 @@ async function restoreNode(params) {
|
|
|
19125
19507
|
}
|
|
19126
19508
|
|
|
19127
19509
|
// app/lib/file-delete-cascade.ts
|
|
19128
|
-
var
|
|
19510
|
+
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19129
19511
|
function parseAttachmentPath(relPath) {
|
|
19130
19512
|
const segments = relPath.split("/").filter(Boolean);
|
|
19131
19513
|
if (segments.length !== 5) return null;
|
|
@@ -19133,7 +19515,7 @@ function parseAttachmentPath(relPath) {
|
|
|
19133
19515
|
const accountId = segments[1];
|
|
19134
19516
|
const attachmentId = segments[3];
|
|
19135
19517
|
const filename = segments[4];
|
|
19136
|
-
if (!
|
|
19518
|
+
if (!UUID_RE4.test(accountId) || !UUID_RE4.test(attachmentId)) return null;
|
|
19137
19519
|
const dot = filename.lastIndexOf(".");
|
|
19138
19520
|
if (dot === -1) return null;
|
|
19139
19521
|
const stem = filename.slice(0, dot);
|
|
@@ -19206,7 +19588,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
19206
19588
|
|
|
19207
19589
|
// app/lib/file-index.ts
|
|
19208
19590
|
import * as fsp from "fs/promises";
|
|
19209
|
-
import { resolve as resolve26, relative as relative4, join as
|
|
19591
|
+
import { resolve as resolve26, relative as relative4, join as join37, basename as basename10, extname as extname2, sep as sep7 } from "path";
|
|
19210
19592
|
import { tmpdir as tmpdir2 } from "os";
|
|
19211
19593
|
import { execFile as execFile2 } from "child_process";
|
|
19212
19594
|
import { promisify as promisify2 } from "util";
|
|
@@ -19288,7 +19670,7 @@ async function extractPdf(absolute) {
|
|
|
19288
19670
|
return stdout.trim();
|
|
19289
19671
|
}
|
|
19290
19672
|
async function ocrPdf(absolute) {
|
|
19291
|
-
const outPdf =
|
|
19673
|
+
const outPdf = join37(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
19292
19674
|
try {
|
|
19293
19675
|
await execFileAsync2(
|
|
19294
19676
|
"ocrmypdf",
|
|
@@ -19350,7 +19732,7 @@ async function readDisplayName(absolute) {
|
|
|
19350
19732
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
19351
19733
|
if (base === `${stem}.meta.json`) return null;
|
|
19352
19734
|
try {
|
|
19353
|
-
const raw = await fsp.readFile(
|
|
19735
|
+
const raw = await fsp.readFile(join37(dir, `${stem}.meta.json`), "utf-8");
|
|
19354
19736
|
const meta = JSON.parse(raw);
|
|
19355
19737
|
const dn = meta.displayName ?? meta.filename;
|
|
19356
19738
|
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
@@ -19371,7 +19753,7 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
19371
19753
|
for (const entry of entries2) {
|
|
19372
19754
|
if (entry.isSymbolicLink()) continue;
|
|
19373
19755
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
19374
|
-
const abs =
|
|
19756
|
+
const abs = join37(root, entry.name);
|
|
19375
19757
|
if (entry.isDirectory()) {
|
|
19376
19758
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
19377
19759
|
if (!sub.clean) clean = false;
|
|
@@ -19741,10 +20123,10 @@ function classifyAccountRoot(schemaMd, rootEntries) {
|
|
|
19741
20123
|
|
|
19742
20124
|
// server/routes/admin/files.ts
|
|
19743
20125
|
var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
19744
|
-
var
|
|
20126
|
+
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19745
20127
|
async function readMeta(absDir, baseName) {
|
|
19746
20128
|
try {
|
|
19747
|
-
const raw = await
|
|
20129
|
+
const raw = await readFile9(join38(absDir, `${baseName}.meta.json`), "utf8");
|
|
19748
20130
|
const parsed = JSON.parse(raw);
|
|
19749
20131
|
if (typeof parsed?.filename === "string") {
|
|
19750
20132
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -19763,10 +20145,10 @@ async function readAccountNames() {
|
|
|
19763
20145
|
return map;
|
|
19764
20146
|
}
|
|
19765
20147
|
for (const name of names) {
|
|
19766
|
-
if (!
|
|
20148
|
+
if (!UUID_RE5.test(name)) continue;
|
|
19767
20149
|
const configPath2 = resolve27(accountsDir, name, "account.json");
|
|
19768
20150
|
try {
|
|
19769
|
-
const raw = await
|
|
20151
|
+
const raw = await readFile9(configPath2, "utf8");
|
|
19770
20152
|
const parsed = JSON.parse(raw);
|
|
19771
20153
|
if (typeof parsed?.name === "string" && parsed.name.length > 0) {
|
|
19772
20154
|
map.set(name, parsed.name);
|
|
@@ -19798,8 +20180,8 @@ async function servableFilesIn(dirAbs, uuid) {
|
|
|
19798
20180
|
return out;
|
|
19799
20181
|
}
|
|
19800
20182
|
async function enrich(absolute, entry, accountNames) {
|
|
19801
|
-
if (entry.kind === "directory" &&
|
|
19802
|
-
const dirAbs =
|
|
20183
|
+
if (entry.kind === "directory" && UUID_RE5.test(entry.name)) {
|
|
20184
|
+
const dirAbs = join38(absolute, entry.name);
|
|
19803
20185
|
const meta = await readMeta(dirAbs, entry.name);
|
|
19804
20186
|
if (meta?.filename) {
|
|
19805
20187
|
const servable = await servableFilesIn(dirAbs, entry.name);
|
|
@@ -19808,7 +20190,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
19808
20190
|
let size = null;
|
|
19809
20191
|
let modifiedAt = entry.modifiedAt;
|
|
19810
20192
|
try {
|
|
19811
|
-
const st = await stat7(
|
|
20193
|
+
const st = await stat7(join38(dirAbs, innerName));
|
|
19812
20194
|
size = st.size;
|
|
19813
20195
|
modifiedAt = st.mtime.toISOString();
|
|
19814
20196
|
} catch {
|
|
@@ -19833,7 +20215,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
19833
20215
|
if (entry.kind === "file") {
|
|
19834
20216
|
const dot = entry.name.lastIndexOf(".");
|
|
19835
20217
|
const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
|
|
19836
|
-
if (
|
|
20218
|
+
if (UUID_RE5.test(base)) {
|
|
19837
20219
|
const meta = await readMeta(absolute, base);
|
|
19838
20220
|
if (meta?.filename) {
|
|
19839
20221
|
entry.displayName = meta.filename;
|
|
@@ -19846,7 +20228,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
19846
20228
|
function buildDisplayPath(relPath, accountNames) {
|
|
19847
20229
|
if (relPath === "." || relPath === "") return [];
|
|
19848
20230
|
return relPath.split("/").filter(Boolean).map((seg) => {
|
|
19849
|
-
const dn =
|
|
20231
|
+
const dn = UUID_RE5.test(seg) ? accountNames.get(seg) : void 0;
|
|
19850
20232
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
19851
20233
|
});
|
|
19852
20234
|
}
|
|
@@ -19858,13 +20240,13 @@ function parseGroupPath(raw, sessionAccountId) {
|
|
|
19858
20240
|
}
|
|
19859
20241
|
const segs = trimmed.split("/").filter(Boolean);
|
|
19860
20242
|
if (segs.length !== 3) return null;
|
|
19861
|
-
if (segs[0] !== "accounts" || !
|
|
20243
|
+
if (segs[0] !== "accounts" || !UUID_RE5.test(segs[1])) return null;
|
|
19862
20244
|
if (segs[2] !== "@home" && segs[2] !== "@system") return null;
|
|
19863
20245
|
return { view: segs[2].slice(1), accountRel: `accounts/${segs[1]}` };
|
|
19864
20246
|
}
|
|
19865
20247
|
function isAccountRoot(relPath) {
|
|
19866
20248
|
const segs = relPath.split("/").filter(Boolean);
|
|
19867
|
-
return segs.length === 2 && segs[0] === "accounts" &&
|
|
20249
|
+
return segs.length === 2 && segs[0] === "accounts" && UUID_RE5.test(segs[1]);
|
|
19868
20250
|
}
|
|
19869
20251
|
function sortDirEntries(entries2) {
|
|
19870
20252
|
entries2.sort((a, b) => {
|
|
@@ -19912,11 +20294,11 @@ function isProtectedFromRename(relPath) {
|
|
|
19912
20294
|
if (segments.length === 3 && segments[0] === "accounts" && segments[2] === "uploads") {
|
|
19913
20295
|
return true;
|
|
19914
20296
|
}
|
|
19915
|
-
if (segments.length === 4 && segments[0] === "accounts" && segments[2] === "uploads" &&
|
|
20297
|
+
if (segments.length === 4 && segments[0] === "accounts" && segments[2] === "uploads" && UUID_RE5.test(segments[3])) {
|
|
19916
20298
|
return true;
|
|
19917
20299
|
}
|
|
19918
20300
|
if (parseAttachmentPath(relPath) !== null) return true;
|
|
19919
|
-
if (
|
|
20301
|
+
if (UUID_RE5.test(base.replace(/\.meta\.json$/, "")) && base.endsWith(".meta.json")) {
|
|
19920
20302
|
return true;
|
|
19921
20303
|
}
|
|
19922
20304
|
return false;
|
|
@@ -19992,16 +20374,16 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
19992
20374
|
const isAccountParent = segs.length === 1 && segs[0] === "accounts";
|
|
19993
20375
|
const entries2 = [];
|
|
19994
20376
|
for (const name of names) {
|
|
19995
|
-
if (
|
|
20377
|
+
if (UUID_RE5.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
19996
20378
|
continue;
|
|
19997
20379
|
}
|
|
19998
|
-
if (isAccountParent &&
|
|
20380
|
+
if (isAccountParent && UUID_RE5.test(name) && name !== accountId) {
|
|
19999
20381
|
continue;
|
|
20000
20382
|
}
|
|
20001
20383
|
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
20002
20384
|
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
20003
20385
|
try {
|
|
20004
|
-
const entryPath =
|
|
20386
|
+
const entryPath = join38(absolute, name);
|
|
20005
20387
|
const s = await stat7(entryPath);
|
|
20006
20388
|
entries2.push({
|
|
20007
20389
|
name,
|
|
@@ -20030,7 +20412,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
20030
20412
|
const displayPath = buildDisplayPath(relPath, accountNames);
|
|
20031
20413
|
if (groupReq || isAccountRoot(relPath)) {
|
|
20032
20414
|
const acctId = relPath.split("/")[1] ?? accountId;
|
|
20033
|
-
const schemaMd = await
|
|
20415
|
+
const schemaMd = await readFile9(join38(absolute, "SCHEMA.md"), "utf8").catch(() => null);
|
|
20034
20416
|
const groups = classifyAccountRoot(schemaMd, entries2.map((e) => e.name));
|
|
20035
20417
|
console.error(
|
|
20036
20418
|
`[data-schema] op=classify account=${acctId} status=${groups.parsed ? "ok" : "parse-failed"} reason=${groups.reason} entries=${entries2.length} home=${groups.home.join(",") || "none"} system=${groups.system.length} unknown=${groups.unknown.join(",") || "none"}`
|
|
@@ -20068,7 +20450,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
20068
20450
|
for (const name of homeDirs) {
|
|
20069
20451
|
const base = byName.get(name);
|
|
20070
20452
|
try {
|
|
20071
|
-
const dirents = await readdir4(
|
|
20453
|
+
const dirents = await readdir4(join38(absolute, name), { withFileTypes: true });
|
|
20072
20454
|
const visible = dirents.filter((d) => !d.name.startsWith("."));
|
|
20073
20455
|
visible.sort((a, b) => {
|
|
20074
20456
|
const ad = a.isDirectory() ? 0 : 1;
|
|
@@ -20133,7 +20515,7 @@ app23.get("/download", requireAdminSession, async (c) => {
|
|
|
20133
20515
|
console.error(`[data] op=scratchpad-no-session sessionId="${sessionId.slice(0, 8)}\u2026"`);
|
|
20134
20516
|
return c.json({ error: "Not found" }, 404);
|
|
20135
20517
|
}
|
|
20136
|
-
const meta = readSidecarMeta(
|
|
20518
|
+
const meta = readSidecarMeta(join38(projectDir, `${sessionId}.meta.json`));
|
|
20137
20519
|
if (!meta.accountId || meta.accountId !== accountId) {
|
|
20138
20520
|
console.error(`[data] account-scope-blocked root="scratchpad" sessionId="${sessionId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
|
|
20139
20521
|
return c.json({ error: "Not found" }, 404);
|
|
@@ -20228,7 +20610,7 @@ async function* walkRegularFiles(dirAbsolute) {
|
|
|
20228
20610
|
const dirents = await readdir4(dirAbsolute, { withFileTypes: true });
|
|
20229
20611
|
for (const d of dirents) {
|
|
20230
20612
|
if (d.isSymbolicLink()) continue;
|
|
20231
|
-
const child =
|
|
20613
|
+
const child = join38(dirAbsolute, d.name);
|
|
20232
20614
|
if (d.isDirectory()) {
|
|
20233
20615
|
yield* walkRegularFiles(child);
|
|
20234
20616
|
continue;
|
|
@@ -20257,7 +20639,7 @@ app23.get("/download-zip", requireAdminSession, async (c) => {
|
|
|
20257
20639
|
if (entries2.length >= maxFiles) return c.json({ error: `Too many files (max ${maxFiles})` }, 413);
|
|
20258
20640
|
total += size;
|
|
20259
20641
|
if (total > maxTotalBytes) return c.json({ error: "Selection too large to zip" }, 413);
|
|
20260
|
-
entries2.push({ name: member, data: await
|
|
20642
|
+
entries2.push({ name: member, data: await readFile9(absolute) });
|
|
20261
20643
|
return null;
|
|
20262
20644
|
};
|
|
20263
20645
|
for (const rawPath of rawPaths) {
|
|
@@ -20338,7 +20720,7 @@ async function resolveUploadTarget(c, accountId, uid) {
|
|
|
20338
20720
|
const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
|
|
20339
20721
|
if (!base.ok) return { ok: false, status: base.status, error: base.error };
|
|
20340
20722
|
const relDir = relpath ? dirname13(relpath) : ".";
|
|
20341
|
-
const destRel = relDir === "." ? base.relative :
|
|
20723
|
+
const destRel = relDir === "." ? base.relative : join38(base.relative, relDir);
|
|
20342
20724
|
if (crossesForeignAccountPartition(destRel, accountId)) {
|
|
20343
20725
|
console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
20344
20726
|
return { ok: false, status: 404, error: "Not found" };
|
|
@@ -20410,7 +20792,7 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
20410
20792
|
out.once("close", () => res());
|
|
20411
20793
|
out.destroy();
|
|
20412
20794
|
});
|
|
20413
|
-
await
|
|
20795
|
+
await unlink4(tmpPath).catch(() => {
|
|
20414
20796
|
});
|
|
20415
20797
|
const tempRemoved = !existsSync35(tmpPath);
|
|
20416
20798
|
console.error(`[data-upload] op=cleanup uid=${uid} tempRemoved=${tempRemoved}`);
|
|
@@ -20456,7 +20838,7 @@ app23.post("/upload", requireAdminSession, async (c) => {
|
|
|
20456
20838
|
out.once("error", rej);
|
|
20457
20839
|
out.end(() => res());
|
|
20458
20840
|
});
|
|
20459
|
-
await
|
|
20841
|
+
await rename3(tmpPath, destPath);
|
|
20460
20842
|
} catch (err) {
|
|
20461
20843
|
const message = err instanceof Error ? err.message : String(err);
|
|
20462
20844
|
console.error(`[data-upload] op=error uid=${uid} err="${message}" token=${token} rel="${relpath ?? ""}"`);
|
|
@@ -20484,7 +20866,7 @@ async function discardChunkUpload(uploadId, state) {
|
|
|
20484
20866
|
state.out.once("close", () => res());
|
|
20485
20867
|
state.out.destroy();
|
|
20486
20868
|
});
|
|
20487
|
-
await
|
|
20869
|
+
await unlink4(state.tmpPath).catch(() => {
|
|
20488
20870
|
});
|
|
20489
20871
|
const tempRemoved = !existsSync35(state.tmpPath);
|
|
20490
20872
|
console.error(`[data-upload] op=cleanup uid=${uploadId} tempRemoved=${tempRemoved}`);
|
|
@@ -20612,11 +20994,11 @@ async function acceptChunk(c, uploadId, state, seq, body, ceiling) {
|
|
|
20612
20994
|
state.out.once("error", rej);
|
|
20613
20995
|
state.out.end(() => res());
|
|
20614
20996
|
});
|
|
20615
|
-
await
|
|
20997
|
+
await rename3(state.tmpPath, state.destPath);
|
|
20616
20998
|
} catch (err) {
|
|
20617
20999
|
const message = err instanceof Error ? err.message : String(err);
|
|
20618
21000
|
console.error(`[data-upload] op=error uid=${uploadId} err="${message}" token=${state.token} rel="${state.relpath ?? ""}"`);
|
|
20619
|
-
await
|
|
21001
|
+
await unlink4(state.tmpPath).catch(() => {
|
|
20620
21002
|
});
|
|
20621
21003
|
return c.json({ error: message }, 500);
|
|
20622
21004
|
}
|
|
@@ -20668,7 +21050,7 @@ async function sweepStaleUploadTemps() {
|
|
|
20668
21050
|
}
|
|
20669
21051
|
for (const name of entries2) {
|
|
20670
21052
|
try {
|
|
20671
|
-
await
|
|
21053
|
+
await unlink4(resolve27(tmpDir, name));
|
|
20672
21054
|
removed++;
|
|
20673
21055
|
} catch {
|
|
20674
21056
|
}
|
|
@@ -20715,17 +21097,17 @@ app23.delete("/", requireAdminSession, async (c) => {
|
|
|
20715
21097
|
const dot = base.lastIndexOf(".");
|
|
20716
21098
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
20717
21099
|
const containerDir = dirname13(absolute);
|
|
20718
|
-
const sidecarPath =
|
|
20719
|
-
const isAttachmentContainer =
|
|
20720
|
-
await
|
|
21100
|
+
const sidecarPath = UUID_RE5.test(stem) && base !== `${stem}.meta.json` ? join38(containerDir, `${stem}.meta.json`) : null;
|
|
21101
|
+
const isAttachmentContainer = UUID_RE5.test(stem) && basename11(containerDir) === stem;
|
|
21102
|
+
await unlink4(absolute);
|
|
20721
21103
|
if (sidecarPath) {
|
|
20722
21104
|
try {
|
|
20723
|
-
await
|
|
21105
|
+
await unlink4(sidecarPath);
|
|
20724
21106
|
} catch {
|
|
20725
21107
|
}
|
|
20726
21108
|
}
|
|
20727
21109
|
if (isAttachmentContainer) {
|
|
20728
|
-
await
|
|
21110
|
+
await unlink4(join38(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
|
|
20729
21111
|
});
|
|
20730
21112
|
await rmdir(containerDir).catch(() => {
|
|
20731
21113
|
});
|
|
@@ -20832,7 +21214,7 @@ app23.post("/rename", requireAdminSession, async (c) => {
|
|
|
20832
21214
|
if (e.code === "ENOENT") collision = false;
|
|
20833
21215
|
}
|
|
20834
21216
|
if (collision) return c.json({ error: "A file or folder with that name already exists" }, 409);
|
|
20835
|
-
await
|
|
21217
|
+
await rename3(src.absolute, destAbs);
|
|
20836
21218
|
} catch (err) {
|
|
20837
21219
|
const code = err.code;
|
|
20838
21220
|
if (code === "ENOENT") return c.json({ error: "Not found" }, 404);
|
|
@@ -21835,7 +22217,7 @@ function isHiddenByDefault(label) {
|
|
|
21835
22217
|
// server/lib/top-level-labels.ts
|
|
21836
22218
|
var import_dist5 = __toESM(require_dist4(), 1);
|
|
21837
22219
|
import { readdirSync as readdirSync24, readFileSync as readFileSync36 } from "fs";
|
|
21838
|
-
import { join as
|
|
22220
|
+
import { join as join39, resolve as resolve28 } from "path";
|
|
21839
22221
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
21840
22222
|
/* @__PURE__ */ new Set([
|
|
21841
22223
|
// Base-schema infra (schema-base.md has no top-level section).
|
|
@@ -21939,7 +22321,7 @@ function parseTopLevelLabels(content) {
|
|
|
21939
22321
|
}
|
|
21940
22322
|
function resolveReferencesDir() {
|
|
21941
22323
|
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
21942
|
-
return
|
|
22324
|
+
return join39(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
21943
22325
|
}
|
|
21944
22326
|
return resolve28(import.meta.dirname, "../../../plugins/memory/references");
|
|
21945
22327
|
}
|
|
@@ -21949,7 +22331,7 @@ function parseTableTopLevelLabels(referencesDir) {
|
|
|
21949
22331
|
try {
|
|
21950
22332
|
const fileNames = readdirSync24(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
|
|
21951
22333
|
for (const fileName of fileNames) {
|
|
21952
|
-
const content = readFileSync36(
|
|
22334
|
+
const content = readFileSync36(join39(referencesDir, fileName), "utf-8");
|
|
21953
22335
|
const parsed = parseTopLevelLabels(content);
|
|
21954
22336
|
if (parsed.length > 0) contributingFiles.push(fileName);
|
|
21955
22337
|
for (const label of parsed) labels.add(label);
|
|
@@ -23288,11 +23670,11 @@ var sidebar_artefacts_default = app30;
|
|
|
23288
23670
|
|
|
23289
23671
|
// server/channel-session-override.ts
|
|
23290
23672
|
import { readFileSync as readFileSync37, writeFileSync as writeFileSync14, renameSync as renameSync10 } from "fs";
|
|
23291
|
-
import { join as
|
|
23673
|
+
import { join as join40 } from "path";
|
|
23292
23674
|
var FILE2 = "channel-session-override.json";
|
|
23293
23675
|
var UUID3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
23294
23676
|
function channelOverridePath(accountDir) {
|
|
23295
|
-
return
|
|
23677
|
+
return join40(accountDir, FILE2);
|
|
23296
23678
|
}
|
|
23297
23679
|
function senderKey(channel, senderId) {
|
|
23298
23680
|
return `${channel}:${senderId}`;
|
|
@@ -23688,24 +24070,24 @@ var session_rc_spawn_default = app36;
|
|
|
23688
24070
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
23689
24071
|
|
|
23690
24072
|
// server/wa-descriptor.ts
|
|
23691
|
-
import { resolve as resolve30, join as
|
|
24073
|
+
import { resolve as resolve30, join as join41 } from "path";
|
|
23692
24074
|
function waGatewayUrl() {
|
|
23693
24075
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
23694
24076
|
}
|
|
23695
24077
|
function waServerPath() {
|
|
23696
|
-
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ??
|
|
24078
|
+
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve30(process.env.MAXY_PLATFORM_ROOT ?? join41(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
|
|
23697
24079
|
}
|
|
23698
24080
|
function waChannelDescriptor(senderId) {
|
|
23699
24081
|
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
23700
24082
|
}
|
|
23701
24083
|
|
|
23702
24084
|
// server/telegram-descriptor.ts
|
|
23703
|
-
import { resolve as resolve31, join as
|
|
24085
|
+
import { resolve as resolve31, join as join42 } from "path";
|
|
23704
24086
|
function telegramGatewayUrl() {
|
|
23705
24087
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
23706
24088
|
}
|
|
23707
24089
|
function telegramServerPath() {
|
|
23708
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ??
|
|
24090
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
23709
24091
|
}
|
|
23710
24092
|
function telegramChannelDescriptor(senderId) {
|
|
23711
24093
|
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
@@ -23870,7 +24252,7 @@ app37.post("/", requireAdminSession, async (c) => {
|
|
|
23870
24252
|
var session_reseat_default = app37;
|
|
23871
24253
|
|
|
23872
24254
|
// server/routes/admin/system-stats.ts
|
|
23873
|
-
import { readFile as
|
|
24255
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
23874
24256
|
import { cpus, loadavg, totalmem, freemem } from "os";
|
|
23875
24257
|
function parseCpuStat(blob) {
|
|
23876
24258
|
const line = blob.split("\n", 1)[0] ?? "";
|
|
@@ -23914,11 +24296,11 @@ function parseMeminfo(blob) {
|
|
|
23914
24296
|
}
|
|
23915
24297
|
var SAMPLE_DELAY_MS = 100;
|
|
23916
24298
|
async function sampleLinux() {
|
|
23917
|
-
const a = parseCpuStat(await
|
|
24299
|
+
const a = parseCpuStat(await readFile10("/proc/stat", "utf-8"));
|
|
23918
24300
|
await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
|
|
23919
|
-
const b = parseCpuStat(await
|
|
23920
|
-
const meminfo = parseMeminfo(await
|
|
23921
|
-
const loadavgRaw = (await
|
|
24301
|
+
const b = parseCpuStat(await readFile10("/proc/stat", "utf-8"));
|
|
24302
|
+
const meminfo = parseMeminfo(await readFile10("/proc/meminfo", "utf-8"));
|
|
24303
|
+
const loadavgRaw = (await readFile10("/proc/loadavg", "utf-8")).trim();
|
|
23922
24304
|
const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
|
|
23923
24305
|
if (![l1, l5, l15].every(Number.isFinite)) {
|
|
23924
24306
|
throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
|
|
@@ -23992,10 +24374,10 @@ var system_stats_default = app38;
|
|
|
23992
24374
|
|
|
23993
24375
|
// server/routes/admin/health.ts
|
|
23994
24376
|
import { existsSync as existsSync37, readFileSync as readFileSync38 } from "fs";
|
|
23995
|
-
import { resolve as resolve32, join as
|
|
24377
|
+
import { resolve as resolve32, join as join43 } from "path";
|
|
23996
24378
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve32(process.cwd(), "..");
|
|
23997
24379
|
var brandHostname = "maxy";
|
|
23998
|
-
var brandJsonPath =
|
|
24380
|
+
var brandJsonPath = join43(PLATFORM_ROOT6, "config", "brand.json");
|
|
23999
24381
|
if (existsSync37(brandJsonPath)) {
|
|
24000
24382
|
try {
|
|
24001
24383
|
const brand = JSON.parse(readFileSync38(brandJsonPath, "utf-8"));
|
|
@@ -24785,7 +25167,7 @@ var browser_default = app46;
|
|
|
24785
25167
|
|
|
24786
25168
|
// server/routes/admin/calendar.ts
|
|
24787
25169
|
import { existsSync as existsSync38 } from "fs";
|
|
24788
|
-
import { join as
|
|
25170
|
+
import { join as join45 } from "path";
|
|
24789
25171
|
|
|
24790
25172
|
// server/lib/calendar-ics.ts
|
|
24791
25173
|
var CRLF = "\r\n";
|
|
@@ -24885,7 +25267,7 @@ function countVEvents(ics) {
|
|
|
24885
25267
|
|
|
24886
25268
|
// server/lib/calendar-availability.ts
|
|
24887
25269
|
import { readFileSync as readFileSync39 } from "fs";
|
|
24888
|
-
import { join as
|
|
25270
|
+
import { join as join44 } from "path";
|
|
24889
25271
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
24890
25272
|
var REQUIRED_KEYS = [
|
|
24891
25273
|
"timezone",
|
|
@@ -24894,7 +25276,7 @@ var REQUIRED_KEYS = [
|
|
|
24894
25276
|
"weekly"
|
|
24895
25277
|
];
|
|
24896
25278
|
function readAvailabilityConfig(accountDir) {
|
|
24897
|
-
const path3 =
|
|
25279
|
+
const path3 = join44(accountDir, AVAILABILITY_FILENAME);
|
|
24898
25280
|
let raw;
|
|
24899
25281
|
try {
|
|
24900
25282
|
raw = readFileSync39(path3, "utf-8");
|
|
@@ -25257,7 +25639,7 @@ app47.get("/booking-link", requireAdminSession, (c) => {
|
|
|
25257
25639
|
}
|
|
25258
25640
|
const id82 = account.accountId.slice(0, 8);
|
|
25259
25641
|
try {
|
|
25260
|
-
if (!existsSync38(
|
|
25642
|
+
if (!existsSync38(join45(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
25261
25643
|
console.log(`[calendar] op=booking-link accountId=${id82} bookingDomain=none source=availability-config`);
|
|
25262
25644
|
return c.json({ bookingDomain: null });
|
|
25263
25645
|
}
|
|
@@ -25657,7 +26039,7 @@ async function computeTaskList(accountId) {
|
|
|
25657
26039
|
{ accountId }
|
|
25658
26040
|
);
|
|
25659
26041
|
const graphTotal = totalResult.records.length > 0 ? toNum(totalResult.records[0].get("total")) : 0;
|
|
25660
|
-
const
|
|
26042
|
+
const open3 = [];
|
|
25661
26043
|
const completed = [];
|
|
25662
26044
|
let missingTaskId = 0;
|
|
25663
26045
|
let missingCreatedAt = 0;
|
|
@@ -25692,7 +26074,7 @@ async function computeTaskList(accountId) {
|
|
|
25692
26074
|
assigneeName
|
|
25693
26075
|
};
|
|
25694
26076
|
if (COMPLETED.has(status)) completed.push(row);
|
|
25695
|
-
else if (!HIDDEN_FROM_OPEN.has(status))
|
|
26077
|
+
else if (!HIDDEN_FROM_OPEN.has(status)) open3.push(row);
|
|
25696
26078
|
}
|
|
25697
26079
|
const returned = result.records.length;
|
|
25698
26080
|
if (returned < graphTotal) {
|
|
@@ -25701,9 +26083,9 @@ async function computeTaskList(accountId) {
|
|
|
25701
26083
|
);
|
|
25702
26084
|
}
|
|
25703
26085
|
console.log(
|
|
25704
|
-
`${TAG40} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${
|
|
26086
|
+
`${TAG40} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open3.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
|
|
25705
26087
|
);
|
|
25706
|
-
return { open, completed };
|
|
26088
|
+
return { open: open3, completed };
|
|
25707
26089
|
} catch (err) {
|
|
25708
26090
|
console.error(`${TAG40} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25709
26091
|
return null;
|
|
@@ -25744,14 +26126,14 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
25744
26126
|
console.error(`${TAG41} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
|
|
25745
26127
|
return c.json({ error: "task-not-found" }, 404);
|
|
25746
26128
|
}
|
|
25747
|
-
const
|
|
26129
|
+
const open3 = await session.run(
|
|
25748
26130
|
`MATCH (te:TimeEntry {accountId: $accountId, taskId: $taskId}) WHERE te.endedAt IS NULL
|
|
25749
26131
|
RETURN te.entryId AS entryId, te.startedAt AS startedAt
|
|
25750
26132
|
ORDER BY te.startedAt LIMIT 1`,
|
|
25751
26133
|
{ accountId, taskId }
|
|
25752
26134
|
);
|
|
25753
|
-
const priorEntryId =
|
|
25754
|
-
const priorStartedAt =
|
|
26135
|
+
const priorEntryId = open3.records[0]?.get("entryId");
|
|
26136
|
+
const priorStartedAt = open3.records[0]?.get("startedAt");
|
|
25755
26137
|
if (priorEntryId) {
|
|
25756
26138
|
console.log(`${TAG41} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
|
|
25757
26139
|
invalidate("tasks-list", accountId);
|
|
@@ -25799,14 +26181,14 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
25799
26181
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25800
26182
|
const session = getSession();
|
|
25801
26183
|
try {
|
|
25802
|
-
const
|
|
26184
|
+
const open3 = await session.run(
|
|
25803
26185
|
`MATCH (te:TimeEntry {taskId: $taskId, accountId: $accountId}) WHERE te.endedAt IS NULL
|
|
25804
26186
|
RETURN te.entryId AS entryId, te.startedAt AS startedAt
|
|
25805
26187
|
ORDER BY te.startedAt LIMIT 1`,
|
|
25806
26188
|
{ taskId, accountId }
|
|
25807
26189
|
);
|
|
25808
|
-
const entryId =
|
|
25809
|
-
const startedAt =
|
|
26190
|
+
const entryId = open3.records[0]?.get("entryId");
|
|
26191
|
+
const startedAt = open3.records[0]?.get("startedAt");
|
|
25810
26192
|
if (!entryId || !startedAt) {
|
|
25811
26193
|
const cur = await session.run(
|
|
25812
26194
|
`MATCH (t:Task {taskId: $taskId, accountId: $accountId})
|
|
@@ -26807,7 +27189,7 @@ var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
|
26807
27189
|
|
|
26808
27190
|
// app/lib/brand-config.ts
|
|
26809
27191
|
import { existsSync as existsSync42, readFileSync as readFileSync44 } from "fs";
|
|
26810
|
-
import { join as
|
|
27192
|
+
import { join as join46 } from "path";
|
|
26811
27193
|
var cached2 = null;
|
|
26812
27194
|
var cachedAttempted = false;
|
|
26813
27195
|
function readBrandConfig() {
|
|
@@ -26815,7 +27197,7 @@ function readBrandConfig() {
|
|
|
26815
27197
|
cachedAttempted = true;
|
|
26816
27198
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
26817
27199
|
if (!platformRoot5) return null;
|
|
26818
|
-
const brandPath =
|
|
27200
|
+
const brandPath = join46(platformRoot5, "config", "brand.json");
|
|
26819
27201
|
if (!existsSync42(brandPath)) return null;
|
|
26820
27202
|
try {
|
|
26821
27203
|
cached2 = JSON.parse(readFileSync44(brandPath, "utf-8"));
|
|
@@ -27317,7 +27699,7 @@ var visitor_event_default = app65;
|
|
|
27317
27699
|
// server/routes/session.ts
|
|
27318
27700
|
import { resolve as resolve37 } from "path";
|
|
27319
27701
|
import { existsSync as existsSync43, writeFileSync as writeFileSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
27320
|
-
var
|
|
27702
|
+
var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
27321
27703
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
27322
27704
|
try {
|
|
27323
27705
|
const cacheDir = resolve37(MAXY_DIR, "branding-cache", accountId);
|
|
@@ -27332,7 +27714,7 @@ function parseVisitorCookie2(cookieHeader) {
|
|
|
27332
27714
|
const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
27333
27715
|
if (!match) return null;
|
|
27334
27716
|
const value = decodeURIComponent(match[1]).trim();
|
|
27335
|
-
return
|
|
27717
|
+
return UUID_RE6.test(value) ? value : null;
|
|
27336
27718
|
}
|
|
27337
27719
|
function parseAccessSessionId2(cookieHeader) {
|
|
27338
27720
|
if (!cookieHeader) return null;
|
|
@@ -27430,7 +27812,7 @@ app66.post("/", async (c) => {
|
|
|
27430
27812
|
);
|
|
27431
27813
|
}
|
|
27432
27814
|
}
|
|
27433
|
-
if (body.session_key && typeof body.session_key === "string" &&
|
|
27815
|
+
if (body.session_key && typeof body.session_key === "string" && UUID_RE6.test(body.session_key)) {
|
|
27434
27816
|
if (visitorId) {
|
|
27435
27817
|
const recent = await findRecentConversation(visitorId, accountId, agentSlug).catch(() => null);
|
|
27436
27818
|
if (recent) {
|
|
@@ -27570,9 +27952,9 @@ function computeOpenSlots(config, busy, fromISO, toISO) {
|
|
|
27570
27952
|
const weekday = WEEKDAYS[new Date(Date.UTC(y, m - 1, d)).getUTCDay()];
|
|
27571
27953
|
const windows = config.weekly[weekday] ?? [];
|
|
27572
27954
|
for (const [openStr, closeStr] of windows) {
|
|
27573
|
-
const
|
|
27955
|
+
const open3 = parseHHMM(openStr);
|
|
27574
27956
|
const close = parseHHMM(closeStr);
|
|
27575
|
-
const openUtc = zonedWallClockToUtc(y, m, d,
|
|
27957
|
+
const openUtc = zonedWallClockToUtc(y, m, d, open3.hh, open3.mm, tz).getTime();
|
|
27576
27958
|
const closeUtc = zonedWallClockToUtc(y, m, d, close.hh, close.mm, tz).getTime();
|
|
27577
27959
|
for (let s = openUtc; s + durMs <= closeUtc; s += durMs) {
|
|
27578
27960
|
const slotEnd = s + durMs;
|
|
@@ -27659,8 +28041,8 @@ var calendar_public_default = app67;
|
|
|
27659
28041
|
|
|
27660
28042
|
// server/routes/portal-fetch.ts
|
|
27661
28043
|
import { createReadStream as createReadStream3 } from "fs";
|
|
27662
|
-
import { stat as stat9, readFile as
|
|
27663
|
-
import { join as
|
|
28044
|
+
import { stat as stat9, readFile as readFile11, realpath } from "fs/promises";
|
|
28045
|
+
import { join as join47, resolve as resolve38, sep as sep10 } from "path";
|
|
27664
28046
|
import { Readable as Readable3 } from "stream";
|
|
27665
28047
|
var app68 = new Hono();
|
|
27666
28048
|
var TAG50 = "[portal-fetch]";
|
|
@@ -27716,7 +28098,7 @@ async function readAccountSecret(accountId) {
|
|
|
27716
28098
|
const path3 = resolve38(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
27717
28099
|
let text;
|
|
27718
28100
|
try {
|
|
27719
|
-
text = await
|
|
28101
|
+
text = await readFile11(path3, "utf8");
|
|
27720
28102
|
} catch {
|
|
27721
28103
|
console.error(`${TAG50} op=no-secret account="${accountId}" path="${path3}"`);
|
|
27722
28104
|
return "";
|
|
@@ -27733,7 +28115,7 @@ function isValidAccountId2(value) {
|
|
|
27733
28115
|
}
|
|
27734
28116
|
async function readExposeFolders2(accountDir) {
|
|
27735
28117
|
try {
|
|
27736
|
-
const cfg = JSON.parse(await
|
|
28118
|
+
const cfg = JSON.parse(await readFile11(join47(accountDir, "data-portal.json"), "utf8"));
|
|
27737
28119
|
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
27738
28120
|
} catch {
|
|
27739
28121
|
return [];
|
|
@@ -27742,7 +28124,7 @@ async function readExposeFolders2(accountDir) {
|
|
|
27742
28124
|
async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
27743
28125
|
let schemaText = null;
|
|
27744
28126
|
try {
|
|
27745
|
-
schemaText = await
|
|
28127
|
+
schemaText = await readFile11(join47(accountDir, "SCHEMA.md"), "utf8");
|
|
27746
28128
|
} catch {
|
|
27747
28129
|
schemaText = null;
|
|
27748
28130
|
}
|
|
@@ -27913,7 +28295,7 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
27913
28295
|
|
|
27914
28296
|
// app/lib/ledger-census.ts
|
|
27915
28297
|
import { readdirSync as readdirSync28, readFileSync as readFileSync45, statSync as statSync19 } from "fs";
|
|
27916
|
-
import { join as
|
|
28298
|
+
import { join as join48 } from "path";
|
|
27917
28299
|
|
|
27918
28300
|
// ../lib/ledger-core/dist/reconcile.js
|
|
27919
28301
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -27967,7 +28349,7 @@ function countWriteRejects24h(logDir, now) {
|
|
|
27967
28349
|
}
|
|
27968
28350
|
let total = 0;
|
|
27969
28351
|
for (const name of entries2) {
|
|
27970
|
-
const path3 =
|
|
28352
|
+
const path3 = join48(logDir, name);
|
|
27971
28353
|
try {
|
|
27972
28354
|
if (now - statSync19(path3).mtimeMs > DAY_MS) continue;
|
|
27973
28355
|
for (const line of readFileSync45(path3, "utf8").split("\n")) {
|
|
@@ -28341,7 +28723,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
28341
28723
|
}
|
|
28342
28724
|
|
|
28343
28725
|
// app/lib/migrate-uploads.ts
|
|
28344
|
-
import { mkdir as mkdir6, readdir as readdir6, rename as
|
|
28726
|
+
import { mkdir as mkdir6, readdir as readdir6, rename as rename4, rm as rm3 } from "fs/promises";
|
|
28345
28727
|
import { dirname as dirname15, relative as relative7, resolve as resolve40 } from "path";
|
|
28346
28728
|
var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
28347
28729
|
async function walkFiles(dir) {
|
|
@@ -28375,7 +28757,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
28375
28757
|
const suffix = relative7(srcDir, oldAbs);
|
|
28376
28758
|
const newAbs = resolve40(destDir, suffix);
|
|
28377
28759
|
await mkdir6(dirname15(newAbs), { recursive: true });
|
|
28378
|
-
await
|
|
28760
|
+
await rename4(oldAbs, newAbs);
|
|
28379
28761
|
moved++;
|
|
28380
28762
|
const oldRel = relative7(dataRoot, oldAbs);
|
|
28381
28763
|
const newRel = relative7(dataRoot, newAbs);
|
|
@@ -28464,9 +28846,9 @@ async function migrateUploads(opts = {}) {
|
|
|
28464
28846
|
}
|
|
28465
28847
|
|
|
28466
28848
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
28467
|
-
import { readdir as readdir7, readFile as
|
|
28849
|
+
import { readdir as readdir7, readFile as readFile12, rename as rename5, writeFile as writeFile6, unlink as unlink5 } from "fs/promises";
|
|
28468
28850
|
import { existsSync as existsSync44 } from "fs";
|
|
28469
|
-
import { join as
|
|
28851
|
+
import { join as join49 } from "path";
|
|
28470
28852
|
var ADMIN_ROLE2 = "admin";
|
|
28471
28853
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
28472
28854
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -28477,7 +28859,7 @@ var defaultResolveName = async (_accountId, userId) => {
|
|
|
28477
28859
|
async function readRaw3(path3) {
|
|
28478
28860
|
let raw;
|
|
28479
28861
|
try {
|
|
28480
|
-
raw = await
|
|
28862
|
+
raw = await readFile12(path3, "utf8");
|
|
28481
28863
|
} catch {
|
|
28482
28864
|
return null;
|
|
28483
28865
|
}
|
|
@@ -28491,11 +28873,11 @@ async function readRaw3(path3) {
|
|
|
28491
28873
|
async function writeRaw(path3, obj) {
|
|
28492
28874
|
const tmp = `${path3}.tmp.${process.pid}.${Date.now()}`;
|
|
28493
28875
|
try {
|
|
28494
|
-
await
|
|
28495
|
-
await
|
|
28876
|
+
await writeFile6(tmp, JSON.stringify(obj, null, 2), "utf8");
|
|
28877
|
+
await rename5(tmp, path3);
|
|
28496
28878
|
} catch (err) {
|
|
28497
28879
|
try {
|
|
28498
|
-
if (existsSync44(tmp)) await
|
|
28880
|
+
if (existsSync44(tmp)) await unlink5(tmp);
|
|
28499
28881
|
} catch {
|
|
28500
28882
|
}
|
|
28501
28883
|
throw err;
|
|
@@ -28511,7 +28893,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
28511
28893
|
}
|
|
28512
28894
|
for (const slug of slugs) {
|
|
28513
28895
|
if (!slug.isDirectory()) continue;
|
|
28514
|
-
const slugDir =
|
|
28896
|
+
const slugDir = join49(projectsRoot, slug.name);
|
|
28515
28897
|
let entries2;
|
|
28516
28898
|
try {
|
|
28517
28899
|
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
@@ -28520,9 +28902,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
28520
28902
|
}
|
|
28521
28903
|
for (const entry of entries2) {
|
|
28522
28904
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
28523
|
-
out.push(
|
|
28905
|
+
out.push(join49(slugDir, entry.name));
|
|
28524
28906
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
28525
|
-
const subDir =
|
|
28907
|
+
const subDir = join49(slugDir, entry.name);
|
|
28526
28908
|
let subs;
|
|
28527
28909
|
try {
|
|
28528
28910
|
subs = await readdir7(subDir, { withFileTypes: true });
|
|
@@ -28530,7 +28912,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
28530
28912
|
continue;
|
|
28531
28913
|
}
|
|
28532
28914
|
for (const s of subs) {
|
|
28533
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
28915
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join49(subDir, s.name));
|
|
28534
28916
|
}
|
|
28535
28917
|
}
|
|
28536
28918
|
}
|
|
@@ -28542,7 +28924,7 @@ function shortId(path3) {
|
|
|
28542
28924
|
return base.slice(0, 8);
|
|
28543
28925
|
}
|
|
28544
28926
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
28545
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
28927
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join49(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
28546
28928
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
28547
28929
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
28548
28930
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -28616,6 +28998,129 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
28616
28998
|
return result;
|
|
28617
28999
|
}
|
|
28618
29000
|
|
|
29001
|
+
// app/lib/migrate-sidecar-account-ids.ts
|
|
29002
|
+
import { readdir as readdir8, readFile as readFile13, rename as rename6, writeFile as writeFile7, unlink as unlink6 } from "fs/promises";
|
|
29003
|
+
import { existsSync as existsSync45 } from "fs";
|
|
29004
|
+
import { join as join50 } from "path";
|
|
29005
|
+
var SESSION_META_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
29006
|
+
function projectSlugForCwd(cwd) {
|
|
29007
|
+
return cwd.replace(/\//g, "-");
|
|
29008
|
+
}
|
|
29009
|
+
async function readRaw4(path3) {
|
|
29010
|
+
let raw;
|
|
29011
|
+
try {
|
|
29012
|
+
raw = await readFile13(path3, "utf8");
|
|
29013
|
+
} catch {
|
|
29014
|
+
return null;
|
|
29015
|
+
}
|
|
29016
|
+
try {
|
|
29017
|
+
const parsed = JSON.parse(raw);
|
|
29018
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
29019
|
+
} catch {
|
|
29020
|
+
return null;
|
|
29021
|
+
}
|
|
29022
|
+
}
|
|
29023
|
+
async function writeRaw2(path3, obj) {
|
|
29024
|
+
const tmp = `${path3}.tmp.${process.pid}.${Date.now()}`;
|
|
29025
|
+
try {
|
|
29026
|
+
await writeFile7(tmp, JSON.stringify(obj, null, 2), "utf8");
|
|
29027
|
+
await rename6(tmp, path3);
|
|
29028
|
+
} catch (err) {
|
|
29029
|
+
try {
|
|
29030
|
+
if (existsSync45(tmp)) await unlink6(tmp);
|
|
29031
|
+
} catch {
|
|
29032
|
+
}
|
|
29033
|
+
throw err;
|
|
29034
|
+
}
|
|
29035
|
+
}
|
|
29036
|
+
function shortId2(path3) {
|
|
29037
|
+
const base = path3.slice(path3.lastIndexOf("/") + 1).replace(/\.meta\.json$/, "");
|
|
29038
|
+
return base.slice(0, 8);
|
|
29039
|
+
}
|
|
29040
|
+
async function collectSidecars(projectsRoot) {
|
|
29041
|
+
const out = [];
|
|
29042
|
+
let slugs;
|
|
29043
|
+
try {
|
|
29044
|
+
slugs = await readdir8(projectsRoot, { withFileTypes: true });
|
|
29045
|
+
} catch {
|
|
29046
|
+
return out;
|
|
29047
|
+
}
|
|
29048
|
+
for (const slug of slugs) {
|
|
29049
|
+
if (!slug.isDirectory()) continue;
|
|
29050
|
+
const slugDir = join50(projectsRoot, slug.name);
|
|
29051
|
+
let entries2;
|
|
29052
|
+
try {
|
|
29053
|
+
entries2 = await readdir8(slugDir, { withFileTypes: true });
|
|
29054
|
+
} catch {
|
|
29055
|
+
continue;
|
|
29056
|
+
}
|
|
29057
|
+
for (const entry of entries2) {
|
|
29058
|
+
if (entry.isFile() && SESSION_META_RE2.test(entry.name)) {
|
|
29059
|
+
out.push({ path: join50(slugDir, entry.name), slug: slug.name });
|
|
29060
|
+
} else if (entry.isDirectory() && (entry.name === "subagents" || entry.name === "archive")) {
|
|
29061
|
+
const subDir = join50(slugDir, entry.name);
|
|
29062
|
+
let subs;
|
|
29063
|
+
try {
|
|
29064
|
+
subs = await readdir8(subDir, { withFileTypes: true });
|
|
29065
|
+
} catch {
|
|
29066
|
+
continue;
|
|
29067
|
+
}
|
|
29068
|
+
for (const s of subs) {
|
|
29069
|
+
if (s.isFile() && SESSION_META_RE2.test(s.name)) {
|
|
29070
|
+
out.push({ path: join50(subDir, s.name), slug: slug.name });
|
|
29071
|
+
}
|
|
29072
|
+
}
|
|
29073
|
+
}
|
|
29074
|
+
}
|
|
29075
|
+
}
|
|
29076
|
+
return out;
|
|
29077
|
+
}
|
|
29078
|
+
async function migrateSidecarAccountIds(opts = {}) {
|
|
29079
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join50(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
29080
|
+
const accounts = opts.accounts ?? listValidAccounts();
|
|
29081
|
+
const result = {
|
|
29082
|
+
scanned: 0,
|
|
29083
|
+
backfilled: 0,
|
|
29084
|
+
skippedHasId: 0,
|
|
29085
|
+
skippedUnknownSlug: 0,
|
|
29086
|
+
verified: 0
|
|
29087
|
+
};
|
|
29088
|
+
if (!projectsRoot) {
|
|
29089
|
+
console.error("[sidecar-account-backfill] skip reason=no-projects-root");
|
|
29090
|
+
return result;
|
|
29091
|
+
}
|
|
29092
|
+
const accountBySlug = /* @__PURE__ */ new Map();
|
|
29093
|
+
for (const a of accounts) accountBySlug.set(projectSlugForCwd(a.accountDir), a.accountId);
|
|
29094
|
+
for (const { path: path3, slug } of await collectSidecars(projectsRoot)) {
|
|
29095
|
+
const raw = await readRaw4(path3);
|
|
29096
|
+
if (!raw) continue;
|
|
29097
|
+
result.scanned++;
|
|
29098
|
+
if (typeof raw.accountId === "string" && raw.accountId.length > 0) {
|
|
29099
|
+
result.skippedHasId++;
|
|
29100
|
+
continue;
|
|
29101
|
+
}
|
|
29102
|
+
const accountId = accountBySlug.get(slug);
|
|
29103
|
+
if (!accountId) {
|
|
29104
|
+
result.skippedUnknownSlug++;
|
|
29105
|
+
console.error(`[sidecar-account-backfill] skip sessionId=${shortId2(path3)} reason=slug-not-in-registry slug=${slug}`);
|
|
29106
|
+
continue;
|
|
29107
|
+
}
|
|
29108
|
+
raw.accountId = accountId;
|
|
29109
|
+
await writeRaw2(path3, raw);
|
|
29110
|
+
result.backfilled++;
|
|
29111
|
+
const back = await readRaw4(path3);
|
|
29112
|
+
if (back && back.accountId === accountId) {
|
|
29113
|
+
result.verified++;
|
|
29114
|
+
} else {
|
|
29115
|
+
console.error(`[sidecar-account-backfill] post-write-verify-failed sessionId=${shortId2(path3)} accountId=${accountId.slice(0, 8)}`);
|
|
29116
|
+
}
|
|
29117
|
+
}
|
|
29118
|
+
console.error(
|
|
29119
|
+
`[sidecar-account-backfill] op=sidecar-account scanned=${result.scanned} backfilled=${result.backfilled} skipped-has-id=${result.skippedHasId} skipped-unknown-slug=${result.skippedUnknownSlug} verified=${result.verified}`
|
|
29120
|
+
);
|
|
29121
|
+
return result;
|
|
29122
|
+
}
|
|
29123
|
+
|
|
28619
29124
|
// app/lib/whatsapp/gateway/inbound-hub.ts
|
|
28620
29125
|
var InboundHub = class {
|
|
28621
29126
|
senders = /* @__PURE__ */ new Map();
|
|
@@ -30841,7 +31346,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
30841
31346
|
|
|
30842
31347
|
// app/lib/telegram/outbound/send-document.ts
|
|
30843
31348
|
import { realpathSync as realpathSync10 } from "fs";
|
|
30844
|
-
import { readFile as
|
|
31349
|
+
import { readFile as readFile14, stat as stat11 } from "fs/promises";
|
|
30845
31350
|
import { resolve as resolve42, basename as basename13 } from "path";
|
|
30846
31351
|
var TAG56 = "[telegram:outbound]";
|
|
30847
31352
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
@@ -30891,7 +31396,7 @@ async function sendTelegramDocument(input) {
|
|
|
30891
31396
|
};
|
|
30892
31397
|
}
|
|
30893
31398
|
const filename = basename13(resolvedPath);
|
|
30894
|
-
const buffer = Buffer.from(await
|
|
31399
|
+
const buffer = Buffer.from(await readFile14(resolvedPath));
|
|
30895
31400
|
const form = new FormData();
|
|
30896
31401
|
form.append("chat_id", String(chatId));
|
|
30897
31402
|
if (caption) form.append("caption", caption);
|
|
@@ -31245,12 +31750,12 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
31245
31750
|
}
|
|
31246
31751
|
|
|
31247
31752
|
// server/lib/booking-site-accounts.ts
|
|
31248
|
-
import { existsSync as
|
|
31753
|
+
import { existsSync as existsSync46, readFileSync as readFileSync46 } from "fs";
|
|
31249
31754
|
import { resolve as resolve43 } from "path";
|
|
31250
31755
|
function listBookingSiteAccounts(accounts) {
|
|
31251
31756
|
return accounts.filter((a) => {
|
|
31252
31757
|
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
31253
|
-
if (!
|
|
31758
|
+
if (!existsSync46(availPath)) return false;
|
|
31254
31759
|
try {
|
|
31255
31760
|
const cfg = JSON.parse(readFileSync46(availPath, "utf-8"));
|
|
31256
31761
|
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
@@ -31290,7 +31795,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
31290
31795
|
|
|
31291
31796
|
// ../lib/entitlement/src/index.ts
|
|
31292
31797
|
import { createPublicKey, createHash as createHash7, verify as cryptoVerify } from "crypto";
|
|
31293
|
-
import { existsSync as
|
|
31798
|
+
import { existsSync as existsSync47, readFileSync as readFileSync47, statSync as statSync20 } from "fs";
|
|
31294
31799
|
import { resolve as resolve44 } from "path";
|
|
31295
31800
|
|
|
31296
31801
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -31339,7 +31844,7 @@ function resolveEntitlement(brand, account) {
|
|
|
31339
31844
|
return logResolved(implicitTrust(account), null);
|
|
31340
31845
|
}
|
|
31341
31846
|
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
31342
|
-
if (!
|
|
31847
|
+
if (!existsSync47(entitlementPath)) {
|
|
31343
31848
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
31344
31849
|
}
|
|
31345
31850
|
const stat12 = statSync20(entitlementPath);
|
|
@@ -31530,12 +32035,12 @@ function clientFrom(c) {
|
|
|
31530
32035
|
}
|
|
31531
32036
|
installMediaDownloadGuard();
|
|
31532
32037
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
31533
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
32038
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join51(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
31534
32039
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
31535
|
-
if (BRAND_JSON_PATH && !
|
|
32040
|
+
if (BRAND_JSON_PATH && !existsSync48(BRAND_JSON_PATH)) {
|
|
31536
32041
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
31537
32042
|
}
|
|
31538
|
-
if (BRAND_JSON_PATH &&
|
|
32043
|
+
if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
|
|
31539
32044
|
try {
|
|
31540
32045
|
const parsed = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
31541
32046
|
BRAND = { ...BRAND, ...parsed };
|
|
@@ -31569,10 +32074,10 @@ var brandLoginOpts = {
|
|
|
31569
32074
|
appleTouchIconPath: brandAppIcon512Path,
|
|
31570
32075
|
themeColor: BRAND.defaultColors?.primary
|
|
31571
32076
|
};
|
|
31572
|
-
var ALIAS_DOMAINS_PATH =
|
|
32077
|
+
var ALIAS_DOMAINS_PATH = join51(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
31573
32078
|
function loadAliasDomains() {
|
|
31574
32079
|
try {
|
|
31575
|
-
if (!
|
|
32080
|
+
if (!existsSync48(ALIAS_DOMAINS_PATH)) return null;
|
|
31576
32081
|
const parsed = JSON.parse(readFileSync48(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
31577
32082
|
if (!Array.isArray(parsed)) {
|
|
31578
32083
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
@@ -31648,7 +32153,7 @@ var waGateway = new WaGateway({
|
|
|
31648
32153
|
fetchStandingRules: fetchAccountStandingRules,
|
|
31649
32154
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
31650
32155
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
31651
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
32156
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
31652
32157
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
31653
32158
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
31654
32159
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -31663,11 +32168,12 @@ var waGateway = new WaGateway({
|
|
|
31663
32168
|
caption,
|
|
31664
32169
|
accountId,
|
|
31665
32170
|
maxyAccountId,
|
|
31666
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
32171
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, ".."))
|
|
31667
32172
|
});
|
|
31668
32173
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
31669
32174
|
},
|
|
31670
32175
|
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone, replyContext }) => {
|
|
32176
|
+
warnNonUuidAccount("channel-spawn", effectiveAccountId);
|
|
31671
32177
|
if (passive) {
|
|
31672
32178
|
const phoneHash = createHash8("sha256").update(senderId).digest("hex").slice(0, 12);
|
|
31673
32179
|
const prompt = (text ?? "").trim();
|
|
@@ -31716,7 +32222,7 @@ var waGateway = new WaGateway({
|
|
|
31716
32222
|
return;
|
|
31717
32223
|
}
|
|
31718
32224
|
if (role === "admin") {
|
|
31719
|
-
const managesAccount = effectiveAccountId
|
|
32225
|
+
const managesAccount = managesAccountFor(effectiveAccountId, resolveReadStoreKey(accountId));
|
|
31720
32226
|
console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
|
|
31721
32227
|
}
|
|
31722
32228
|
const req = buildWaSpawnRequest({
|
|
@@ -31781,7 +32287,7 @@ function runDuplicateSenderAudit() {
|
|
|
31781
32287
|
try {
|
|
31782
32288
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
31783
32289
|
if (!configDir2) return;
|
|
31784
|
-
const projectsRoot =
|
|
32290
|
+
const projectsRoot = join51(configDir2, "projects");
|
|
31785
32291
|
const records = [];
|
|
31786
32292
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
31787
32293
|
if (isSubagent || archived) continue;
|
|
@@ -32356,7 +32862,7 @@ app69.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
32356
32862
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
32357
32863
|
return c.text("Forbidden", 403);
|
|
32358
32864
|
}
|
|
32359
|
-
if (!
|
|
32865
|
+
if (!existsSync48(filePath)) {
|
|
32360
32866
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
32361
32867
|
return c.text("Not found", 404);
|
|
32362
32868
|
}
|
|
@@ -32386,7 +32892,7 @@ app69.get("/generated/:filename", (c) => {
|
|
|
32386
32892
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
32387
32893
|
return c.text("Forbidden", 403);
|
|
32388
32894
|
}
|
|
32389
|
-
if (!
|
|
32895
|
+
if (!existsSync48(filePath)) {
|
|
32390
32896
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
32391
32897
|
return c.text("Not found", 404);
|
|
32392
32898
|
}
|
|
@@ -32406,7 +32912,7 @@ app69.route("/v", visitor_consent_default);
|
|
|
32406
32912
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
32407
32913
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
32408
32914
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
32409
|
-
if (BRAND_JSON_PATH &&
|
|
32915
|
+
if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
|
|
32410
32916
|
try {
|
|
32411
32917
|
const fullBrand = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
32412
32918
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
@@ -32435,7 +32941,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
32435
32941
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
32436
32942
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
32437
32943
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
32438
|
-
(p) =>
|
|
32944
|
+
(p) => existsSync48(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
32439
32945
|
);
|
|
32440
32946
|
var SW_SOURCE = (() => {
|
|
32441
32947
|
try {
|
|
@@ -32447,8 +32953,8 @@ var SW_SOURCE = (() => {
|
|
|
32447
32953
|
function readInstalledVersion() {
|
|
32448
32954
|
try {
|
|
32449
32955
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
32450
|
-
const versionFile =
|
|
32451
|
-
if (!
|
|
32956
|
+
const versionFile = join51(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
32957
|
+
if (!existsSync48(versionFile)) return "unknown";
|
|
32452
32958
|
const content = readFileSync48(versionFile, "utf-8").trim();
|
|
32453
32959
|
return content || "unknown";
|
|
32454
32960
|
} catch {
|
|
@@ -32457,36 +32963,6 @@ function readInstalledVersion() {
|
|
|
32457
32963
|
}
|
|
32458
32964
|
var INSTALLED_VERSION = readInstalledVersion();
|
|
32459
32965
|
var versionScript = `<script>window.__MAXY_VERSION__=${JSON.stringify(INSTALLED_VERSION)}</script>`;
|
|
32460
|
-
var clientErrorReporterScript = `<script>
|
|
32461
|
-
(function(){
|
|
32462
|
-
var send=function(b){
|
|
32463
|
-
try{
|
|
32464
|
-
fetch('/api/_client-error',{
|
|
32465
|
-
method:'POST',
|
|
32466
|
-
headers:{'content-type':'application/json'},
|
|
32467
|
-
body:JSON.stringify(Object.assign({url:location.href,ua:navigator.userAgent,version:(window.__MAXY_VERSION__||'unknown')},b)),
|
|
32468
|
-
keepalive:true
|
|
32469
|
-
}).catch(function(){});
|
|
32470
|
-
}catch(e){}
|
|
32471
|
-
};
|
|
32472
|
-
window.addEventListener('error',function(e){
|
|
32473
|
-
var t=e.target;
|
|
32474
|
-
if(t&&t!==window&&t.tagName)send({kind:'subresource',tag:t.tagName,url:t.src||t.href||''});
|
|
32475
|
-
else send({kind:'uncaught',msg:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,stack:(e.error&&e.error.stack)||''});
|
|
32476
|
-
},true);
|
|
32477
|
-
window.addEventListener('unhandledrejection',function(e){
|
|
32478
|
-
var r=e.reason;
|
|
32479
|
-
send({kind:'unhandled-rejection',msg:String((r&&r.message)||r),stack:(r&&r.stack)||''});
|
|
32480
|
-
});
|
|
32481
|
-
try{
|
|
32482
|
-
new PerformanceObserver(function(list){
|
|
32483
|
-
list.getEntries().forEach(function(entry){
|
|
32484
|
-
if(entry.responseStatus>=400)send({kind:'subresource',url:entry.name,status:entry.responseStatus});
|
|
32485
|
-
});
|
|
32486
|
-
}).observe({type:'resource',buffered:true});
|
|
32487
|
-
}catch(e){}
|
|
32488
|
-
})();
|
|
32489
|
-
</script>`;
|
|
32490
32966
|
function cachedHtml(file) {
|
|
32491
32967
|
let html = htmlCache.get(file);
|
|
32492
32968
|
if (!html) {
|
|
@@ -32507,12 +32983,12 @@ ${clientErrorReporterScript}
|
|
|
32507
32983
|
return html;
|
|
32508
32984
|
}
|
|
32509
32985
|
function loadBrandingCache(agentSlug) {
|
|
32510
|
-
const configDir2 =
|
|
32986
|
+
const configDir2 = join51(homedir4(), BRAND.configDir);
|
|
32511
32987
|
try {
|
|
32512
32988
|
const accountId = getDefaultAccountId();
|
|
32513
32989
|
if (!accountId) return null;
|
|
32514
|
-
const cachePath =
|
|
32515
|
-
if (!
|
|
32990
|
+
const cachePath = join51(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
32991
|
+
if (!existsSync48(cachePath)) return null;
|
|
32516
32992
|
return JSON.parse(readFileSync48(cachePath, "utf-8"));
|
|
32517
32993
|
} catch {
|
|
32518
32994
|
return null;
|
|
@@ -32760,10 +33236,10 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
32760
33236
|
var httpServer = serve({ fetch: app69.fetch, port, hostname });
|
|
32761
33237
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
32762
33238
|
{
|
|
32763
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33239
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32764
33240
|
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
32765
33241
|
const runReconcile = (ctx) => {
|
|
32766
|
-
if (!
|
|
33242
|
+
if (!existsSync48(reconcileScript)) return;
|
|
32767
33243
|
try {
|
|
32768
33244
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
32769
33245
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -32783,7 +33259,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32783
33259
|
detached: true,
|
|
32784
33260
|
run: runReconcile
|
|
32785
33261
|
});
|
|
32786
|
-
const reconcileStatePath =
|
|
33262
|
+
const reconcileStatePath = join51(MAXY_DIR, "booking-reconcile-state.json");
|
|
32787
33263
|
registerLoop({
|
|
32788
33264
|
name: "calendar-reconcile-liveness",
|
|
32789
33265
|
intervalMs: 6e5,
|
|
@@ -32801,7 +33277,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32801
33277
|
}
|
|
32802
33278
|
{
|
|
32803
33279
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
32804
|
-
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33280
|
+
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32805
33281
|
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
32806
33282
|
registerLoop({
|
|
32807
33283
|
name: "dream-cycle-sweep",
|
|
@@ -32809,7 +33285,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32809
33285
|
firstRunDelayMs: 3e4,
|
|
32810
33286
|
detached: true,
|
|
32811
33287
|
run: (ctx) => {
|
|
32812
|
-
if (!
|
|
33288
|
+
if (!existsSync48(dreamSweepScript)) return;
|
|
32813
33289
|
try {
|
|
32814
33290
|
const child = spawn3(process.execPath, [dreamSweepScript], {
|
|
32815
33291
|
env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
|
|
@@ -32825,11 +33301,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32825
33301
|
});
|
|
32826
33302
|
}
|
|
32827
33303
|
{
|
|
32828
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33304
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32829
33305
|
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
32830
33306
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
32831
33307
|
const runOutlookComplete = (ctx) => {
|
|
32832
|
-
if (!
|
|
33308
|
+
if (!existsSync48(outlookScript)) return;
|
|
32833
33309
|
try {
|
|
32834
33310
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
32835
33311
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -32851,19 +33327,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32851
33327
|
});
|
|
32852
33328
|
}
|
|
32853
33329
|
{
|
|
32854
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33330
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32855
33331
|
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
32856
33332
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
32857
33333
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
32858
33334
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32859
33335
|
const runStrandedAudit = () => {
|
|
32860
33336
|
try {
|
|
32861
|
-
if (!
|
|
33337
|
+
if (!existsSync48(strandedAccountsDir)) return;
|
|
32862
33338
|
const now = Date.now();
|
|
32863
33339
|
for (const name of readdirSync29(strandedAccountsDir)) {
|
|
32864
33340
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
32865
33341
|
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
32866
|
-
if (!
|
|
33342
|
+
if (!existsSync48(pendingPath2)) continue;
|
|
32867
33343
|
const ageMs2 = now - statSync21(pendingPath2).mtimeMs;
|
|
32868
33344
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
32869
33345
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
|
|
@@ -32881,7 +33357,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32881
33357
|
});
|
|
32882
33358
|
}
|
|
32883
33359
|
{
|
|
32884
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33360
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32885
33361
|
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
32886
33362
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
32887
33363
|
const runGooglePendingAuditSafe = () => {
|
|
@@ -32900,7 +33376,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32900
33376
|
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
32901
33377
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
32902
33378
|
const runGoogleAccountAudit = (ctx) => {
|
|
32903
|
-
if (!
|
|
33379
|
+
if (!existsSync48(googleAuditScript)) return;
|
|
32904
33380
|
try {
|
|
32905
33381
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
32906
33382
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -32925,7 +33401,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32925
33401
|
});
|
|
32926
33402
|
}
|
|
32927
33403
|
{
|
|
32928
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33404
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32929
33405
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
32930
33406
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
32931
33407
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -32971,7 +33447,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32971
33447
|
});
|
|
32972
33448
|
}
|
|
32973
33449
|
{
|
|
32974
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33450
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
32975
33451
|
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
32976
33452
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
32977
33453
|
const bookingAccounts = () => {
|
|
@@ -32983,7 +33459,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
32983
33459
|
}
|
|
32984
33460
|
};
|
|
32985
33461
|
const spawnPublish = (account, ctx) => {
|
|
32986
|
-
if (!
|
|
33462
|
+
if (!existsSync48(publishScript)) return;
|
|
32987
33463
|
try {
|
|
32988
33464
|
const child = spawn3(process.execPath, [publishScript], {
|
|
32989
33465
|
env: {
|
|
@@ -33005,7 +33481,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33005
33481
|
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
33006
33482
|
let hasBookingSite = false;
|
|
33007
33483
|
try {
|
|
33008
|
-
if (
|
|
33484
|
+
if (existsSync48(availPath)) {
|
|
33009
33485
|
const cfg = JSON.parse(readFileSync48(availPath, "utf-8"));
|
|
33010
33486
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
33011
33487
|
}
|
|
@@ -33014,7 +33490,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33014
33490
|
if (!hasBookingSite) return;
|
|
33015
33491
|
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
33016
33492
|
let lastSuccessAt = null;
|
|
33017
|
-
if (
|
|
33493
|
+
if (existsSync48(statePath)) {
|
|
33018
33494
|
try {
|
|
33019
33495
|
const rec = JSON.parse(readFileSync48(statePath, "utf-8"));
|
|
33020
33496
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
@@ -33048,12 +33524,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33048
33524
|
startTimeEntryCensus(() => getSession());
|
|
33049
33525
|
startLedgerCensus(() => getSession());
|
|
33050
33526
|
{
|
|
33051
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33527
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..");
|
|
33052
33528
|
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
33053
33529
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
33054
33530
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
33055
33531
|
const runConnectorAudit = (ctx) => {
|
|
33056
|
-
if (!
|
|
33532
|
+
if (!existsSync48(auditScript)) return;
|
|
33057
33533
|
try {
|
|
33058
33534
|
const child = spawn3(process.execPath, [auditScript], {
|
|
33059
33535
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -33171,7 +33647,7 @@ try {
|
|
|
33171
33647
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
33172
33648
|
async function runAdminUserReconcileTick() {
|
|
33173
33649
|
try {
|
|
33174
|
-
if (!
|
|
33650
|
+
if (!existsSync48(USERS_FILE)) {
|
|
33175
33651
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
33176
33652
|
return;
|
|
33177
33653
|
}
|
|
@@ -33222,7 +33698,7 @@ registerLoop({
|
|
|
33222
33698
|
});
|
|
33223
33699
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
33224
33700
|
function countUsersRows() {
|
|
33225
|
-
if (!
|
|
33701
|
+
if (!existsSync48(USERS_FILE)) return 0;
|
|
33226
33702
|
const raw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
33227
33703
|
if (!raw) return 0;
|
|
33228
33704
|
const users = JSON.parse(raw);
|
|
@@ -33273,7 +33749,7 @@ registerLoop({
|
|
|
33273
33749
|
throw err;
|
|
33274
33750
|
})
|
|
33275
33751
|
});
|
|
33276
|
-
void migrateAdminWebchatSidecars().catch((err) => console.error(`[sidecar-backfill] failed err="${err instanceof Error ? err.message : String(err)}"`));
|
|
33752
|
+
void migrateAdminWebchatSidecars().catch((err) => console.error(`[sidecar-backfill] failed err="${err instanceof Error ? err.message : String(err)}"`)).then(() => migrateSidecarAccountIds()).catch((err) => console.error(`[sidecar-account-backfill] failed err="${err instanceof Error ? err.message : String(err)}"`));
|
|
33277
33753
|
try {
|
|
33278
33754
|
const accounts = enumerateValidAccountIds(getAccountsDirFromEnv());
|
|
33279
33755
|
console.error(`[graph-health] account-enumeration accounts=${accounts.length}`);
|
|
@@ -33356,7 +33832,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
33356
33832
|
}
|
|
33357
33833
|
init({
|
|
33358
33834
|
configDir: configDirForWhatsApp,
|
|
33359
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
33835
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join51(__dirname, "..")),
|
|
33360
33836
|
accountConfig: bootAccountConfig,
|
|
33361
33837
|
onMessage: async (msg) => {
|
|
33362
33838
|
if (isObserveAccount(msg.accountId)) {
|