@rubytech/create-maxy-code 0.1.549 → 0.1.551
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/lib/agent-dispatch-rule/dist/index.d.ts +22 -8
- package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js +47 -13
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/src/__tests__/rule.test.ts +68 -9
- package/payload/platform/lib/agent-dispatch-rule/src/index.ts +60 -16
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +8 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js +21 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-embedding.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-embedding.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-embedding.test.js +142 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-embedding.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +41 -5
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/telegram/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/telegram/PLUGIN.md +6 -4
- package/payload/platform/plugins/telegram/mcp/dist/index.js +62 -38
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts +18 -5
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js +34 -6
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts +25 -8
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js +28 -16
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.d.ts +12 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.js +26 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.d.ts +16 -13
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.js +19 -23
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.d.ts +9 -7
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.js +11 -8
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +4 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +13 -3
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -1
- package/payload/platform/plugins/telegram/references/setup-guide.md +16 -8
- package/payload/platform/plugins/telegram/skills/configure/SKILL.md +30 -32
- package/payload/server/server.js +511 -240
package/payload/server/server.js
CHANGED
|
@@ -1836,10 +1836,10 @@ var require_dist6 = __commonJS({
|
|
|
1836
1836
|
});
|
|
1837
1837
|
|
|
1838
1838
|
// server/index.ts
|
|
1839
|
-
import { readFileSync as
|
|
1839
|
+
import { readFileSync as readFileSync48, existsSync as existsSync52, watchFile, readdirSync as readdirSync30, statSync as statSync27 } from "fs";
|
|
1840
1840
|
import { spawn as spawn3 } from "child_process";
|
|
1841
1841
|
import { createHash as createHash7 } from "crypto";
|
|
1842
|
-
import { resolve as resolve45, join as
|
|
1842
|
+
import { resolve as resolve45, join as join56, basename as basename18 } from "path";
|
|
1843
1843
|
import { homedir as homedir4 } from "os";
|
|
1844
1844
|
|
|
1845
1845
|
// server/lib/route-cache-watchers.ts
|
|
@@ -3847,11 +3847,22 @@ ${result.result.text}` : result.result.text;
|
|
|
3847
3847
|
import { statSync as statSync5 } from "fs";
|
|
3848
3848
|
import { join as join10 } from "path";
|
|
3849
3849
|
|
|
3850
|
+
// app/lib/telegram/channel-key.ts
|
|
3851
|
+
function telegramChannelKey(botId, senderId) {
|
|
3852
|
+
return `${botId}:${senderId}`;
|
|
3853
|
+
}
|
|
3854
|
+
function parseTelegramChannelKey(key) {
|
|
3855
|
+
const i = key.indexOf(":");
|
|
3856
|
+
if (i <= 0 || i === key.length - 1) return null;
|
|
3857
|
+
return { botId: key.slice(0, i), senderId: key.slice(i + 1) };
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3850
3860
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
3851
3861
|
import { createHash } from "crypto";
|
|
3852
|
-
function adminSessionIdFor(accountId, senderId, personId) {
|
|
3862
|
+
function adminSessionIdFor(accountId, senderId, personId, botId) {
|
|
3853
3863
|
const subject = personId && personId.length > 0 ? `person:${personId}` : senderId;
|
|
3854
|
-
const
|
|
3864
|
+
const scope = botId && botId.length > 0 ? `admin:${botId}` : "admin";
|
|
3865
|
+
const b = createHash("sha256").update(`${accountId}:${scope}:${subject}`).digest().subarray(0, 16);
|
|
3855
3866
|
const v = Buffer.from(b);
|
|
3856
3867
|
v[6] = v[6] & 15 | 64;
|
|
3857
3868
|
v[8] = v[8] & 63 | 128;
|
|
@@ -4598,7 +4609,8 @@ async function computeSessionList(scopeAccountId) {
|
|
|
4598
4609
|
continue;
|
|
4599
4610
|
}
|
|
4600
4611
|
const metaPath = join9(projectDir, `${sessionId}.meta.json`);
|
|
4601
|
-
const { bridgeIds, role, channel: sidecarChannel, senderId, personId, adminUserId, accountId: rowAccountId } = await readSidecarMetaAsync(metaPath);
|
|
4612
|
+
const { bridgeIds, role, channel: sidecarChannel, senderId: rawSenderId, personId, adminUserId, accountId: rowAccountId } = await readSidecarMetaAsync(metaPath);
|
|
4613
|
+
const senderId = sidecarChannel === "telegram" ? parseTelegramChannelKey(rawSenderId ?? "")?.senderId ?? rawSenderId : rawSenderId;
|
|
4602
4614
|
if (multiAccount) {
|
|
4603
4615
|
if (rowAccountId === null) untaggedCount += 1;
|
|
4604
4616
|
if (rowAccountId !== scopeAccountId) continue;
|
|
@@ -4872,19 +4884,15 @@ function createScheduleInjectRoutes(deps) {
|
|
|
4872
4884
|
console.error(`${TAG5} op=telegram-destination-ambiguous eventId=${eventId} destination=${destination} accounts=${resolution.count} dispatched=no`);
|
|
4873
4885
|
return c.json({ ok: false, error: "telegram-destination-ambiguous" }, 403);
|
|
4874
4886
|
}
|
|
4875
|
-
const effectiveAccount = resolution
|
|
4876
|
-
console.error(`${TAG5} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount}`);
|
|
4887
|
+
const { accountId: effectiveAccount, botId, botToken } = resolution;
|
|
4888
|
+
console.error(`${TAG5} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount} botId=${botId}`);
|
|
4877
4889
|
if (eventAccountId === null || eventAccountId !== effectiveAccount) {
|
|
4878
4890
|
console.error(`${TAG5} op=event-account-scope-mismatch eventId=${eventId} eventAccount=${eventAccountId ?? "-"} effectiveAccount=${effectiveAccount} destination=${destination} dispatched=no`);
|
|
4879
4891
|
return c.json({ ok: false, error: "event-account-scope-mismatch" }, 403);
|
|
4880
4892
|
}
|
|
4881
|
-
const botToken = resolution.botToken;
|
|
4882
|
-
if (!botToken) {
|
|
4883
|
-
console.error(`${TAG5} reject reason=no-admin-bot-token eventId=${eventId} accountId=${effectiveAccount}`);
|
|
4884
|
-
return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
|
|
4885
|
-
}
|
|
4886
4893
|
const chatId = Number(destination);
|
|
4887
|
-
const
|
|
4894
|
+
const channelKey = telegramChannelKey(botId, destination);
|
|
4895
|
+
const sessionId = deps.resolveSessionOverride?.(effectiveAccount, "telegram", channelKey) ?? adminSessionIdFor(effectiveAccount, destination, void 0, botId);
|
|
4888
4896
|
const reply = (text) => deps.sendTelegramText(botToken, chatId, text);
|
|
4889
4897
|
try {
|
|
4890
4898
|
await deps.tgHandleInbound({
|
|
@@ -4893,6 +4901,14 @@ function createScheduleInjectRoutes(deps) {
|
|
|
4893
4901
|
// session scope are one account and Telegram needs no
|
|
4894
4902
|
// effectiveAccountId companion field the way WhatsApp does above.
|
|
4895
4903
|
accountId: effectiveAccount,
|
|
4904
|
+
// A scheduled dispatch is always role:'admin', and the admin branch of
|
|
4905
|
+
// the spawn never reads the account dir (only the public branch
|
|
4906
|
+
// re-checks its paired agent against it), so there is nothing to
|
|
4907
|
+
// resolve here.
|
|
4908
|
+
accountDir: "",
|
|
4909
|
+
botId,
|
|
4910
|
+
botToken,
|
|
4911
|
+
agentSlug: "admin",
|
|
4896
4912
|
senderId: destination,
|
|
4897
4913
|
chatId: destination,
|
|
4898
4914
|
role: "admin",
|
|
@@ -12907,6 +12923,44 @@ import { homedir as homedir2 } from "os";
|
|
|
12907
12923
|
import { resolve as resolve17, join as join29 } from "path";
|
|
12908
12924
|
import { existsSync as existsSync21, readFileSync as readFileSync23 } from "fs";
|
|
12909
12925
|
|
|
12926
|
+
// app/lib/telegram/bot-registry.ts
|
|
12927
|
+
function botIdFromToken(token) {
|
|
12928
|
+
const i = token.indexOf(":");
|
|
12929
|
+
if (i <= 0) return null;
|
|
12930
|
+
const id = token.slice(0, i);
|
|
12931
|
+
return /^\d+$/.test(id) ? id : null;
|
|
12932
|
+
}
|
|
12933
|
+
function listBotEntries(config) {
|
|
12934
|
+
const raw = config?.bots;
|
|
12935
|
+
if (!Array.isArray(raw)) return [];
|
|
12936
|
+
const out = [];
|
|
12937
|
+
for (const e of raw) {
|
|
12938
|
+
if (!e || typeof e !== "object") continue;
|
|
12939
|
+
const { id, token, role } = e;
|
|
12940
|
+
if (typeof id !== "string" || id.length === 0) continue;
|
|
12941
|
+
if (typeof token !== "string" || token.length === 0) continue;
|
|
12942
|
+
if (role === "admin") {
|
|
12943
|
+
out.push(e);
|
|
12944
|
+
} else if (role === "public") {
|
|
12945
|
+
const agent = e.agent;
|
|
12946
|
+
if (typeof agent !== "string" || agent.length === 0) continue;
|
|
12947
|
+
out.push(e);
|
|
12948
|
+
}
|
|
12949
|
+
}
|
|
12950
|
+
return out;
|
|
12951
|
+
}
|
|
12952
|
+
function resolveBotEntry(accounts, botId) {
|
|
12953
|
+
const hits = [];
|
|
12954
|
+
for (const a of accounts) {
|
|
12955
|
+
for (const entry of listBotEntries(a.config.telegram)) {
|
|
12956
|
+
if (entry.id === botId) hits.push({ accountId: a.accountId, accountDir: a.accountDir, entry });
|
|
12957
|
+
}
|
|
12958
|
+
}
|
|
12959
|
+
if (hits.length === 0) return { kind: "none" };
|
|
12960
|
+
if (hits.length > 1) return { kind: "duplicate", count: hits.length };
|
|
12961
|
+
return { kind: "one", ...hits[0] };
|
|
12962
|
+
}
|
|
12963
|
+
|
|
12910
12964
|
// app/lib/telegram/gateway/instance.ts
|
|
12911
12965
|
var instance = null;
|
|
12912
12966
|
function setTelegramGateway(gw) {
|
|
@@ -12918,20 +12972,20 @@ function getTelegramGateway() {
|
|
|
12918
12972
|
|
|
12919
12973
|
// app/lib/telegram/access-control.ts
|
|
12920
12974
|
function checkTelegramAccess(params) {
|
|
12921
|
-
const { senderId,
|
|
12922
|
-
if (
|
|
12923
|
-
const adminUsers =
|
|
12975
|
+
const { senderId, entry } = params;
|
|
12976
|
+
if (entry.role === "admin") {
|
|
12977
|
+
const adminUsers = entry.adminUsers ?? [];
|
|
12924
12978
|
if (adminUsers.includes(senderId)) {
|
|
12925
12979
|
return { allowed: true, reason: "admin-binding", agentType: "admin" };
|
|
12926
12980
|
}
|
|
12927
12981
|
return { allowed: false, reason: "not-admin-user", agentType: "admin" };
|
|
12928
12982
|
}
|
|
12929
|
-
const policy =
|
|
12983
|
+
const policy = entry.dmPolicy ?? "disabled";
|
|
12930
12984
|
switch (policy) {
|
|
12931
12985
|
case "open":
|
|
12932
12986
|
return { allowed: true, reason: "dm-policy-open", agentType: "public" };
|
|
12933
12987
|
case "allowlist": {
|
|
12934
|
-
const allowFrom =
|
|
12988
|
+
const allowFrom = entry.allowFrom ?? [];
|
|
12935
12989
|
if (allowFrom.includes(senderId)) {
|
|
12936
12990
|
return { allowed: true, reason: "allowlist-match", agentType: "public" };
|
|
12937
12991
|
}
|
|
@@ -12953,11 +13007,11 @@ function routeTelegramUpdate(input) {
|
|
|
12953
13007
|
if (typeof m.text !== "string" || m.text.length === 0) {
|
|
12954
13008
|
return { kind: "ignore", reason: "no-text" };
|
|
12955
13009
|
}
|
|
12956
|
-
const access = checkTelegramAccess({ senderId: m.from.id,
|
|
13010
|
+
const access = checkTelegramAccess({ senderId: m.from.id, entry: input.entry });
|
|
12957
13011
|
if (!access.allowed) {
|
|
12958
13012
|
return { kind: "denied", reason: access.reason, agentType: access.agentType };
|
|
12959
13013
|
}
|
|
12960
|
-
return { kind: "dispatch", senderId: String(m.from.id), chatId: m.chat.id, text: m.text, agentType: access.agentType };
|
|
13014
|
+
return { kind: "dispatch", senderId: String(m.from.id), chatId: m.chat.id, text: m.text, agentType: access.agentType, reason: access.reason };
|
|
12961
13015
|
}
|
|
12962
13016
|
|
|
12963
13017
|
// server/routes/telegram.ts
|
|
@@ -12973,81 +13027,85 @@ function configDirName() {
|
|
|
12973
13027
|
}
|
|
12974
13028
|
return ".maxy";
|
|
12975
13029
|
}
|
|
12976
|
-
function
|
|
12977
|
-
|
|
12978
|
-
|
|
13030
|
+
function telegramSecretsDir() {
|
|
13031
|
+
return resolve17(homedir2(), configDirName(), "telegram-secrets");
|
|
13032
|
+
}
|
|
13033
|
+
function secretPath(botId) {
|
|
13034
|
+
return resolve17(telegramSecretsDir(), botId);
|
|
12979
13035
|
}
|
|
12980
13036
|
var app10 = new Hono();
|
|
12981
13037
|
app10.post("/", async (c) => {
|
|
12982
|
-
const
|
|
12983
|
-
|
|
12984
|
-
if (!botType) {
|
|
13038
|
+
const botId = c.req.query("bot");
|
|
13039
|
+
if (!botId) {
|
|
12985
13040
|
console.error(`${TAG19} op=reject reason=missing-bot-param`);
|
|
12986
13041
|
return c.json({ ok: false }, 400);
|
|
12987
13042
|
}
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
13043
|
+
console.error(`${TAG19} op=received botId=${botId} path=/api/telegram/`);
|
|
13044
|
+
const resolved = resolveBotEntry(listValidAccounts(), botId);
|
|
13045
|
+
if (resolved.kind === "none") {
|
|
13046
|
+
console.error(`${TAG19} op=reject reason=unknown-bot botId=${botId}`);
|
|
12991
13047
|
return c.json({ ok: false }, 401);
|
|
12992
13048
|
}
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
console.error(`${TAG19} op=reject reason=bad-account botType=${botType}`);
|
|
13049
|
+
if (resolved.kind === "duplicate") {
|
|
13050
|
+
console.error(`${TAG19} op=reject reason=duplicate-bot-id botId=${botId} accounts=${resolved.count}`);
|
|
12996
13051
|
return c.json({ ok: false }, 401);
|
|
12997
13052
|
}
|
|
12998
|
-
const
|
|
13053
|
+
const { accountId, accountDir, entry } = resolved;
|
|
13054
|
+
const agentSlug = entry.role === "admin" ? "admin" : entry.agent;
|
|
13055
|
+
console.error(`${TAG19} op=entry botId=${botId} accountId=${accountId} role=${entry.role} agent=${agentSlug}`);
|
|
13056
|
+
const sp = secretPath(botId);
|
|
12999
13057
|
if (!existsSync21(sp)) {
|
|
13000
|
-
console.error(`${TAG19} op=
|
|
13058
|
+
console.error(`${TAG19} op=secret botId=${botId} result=missing-file`);
|
|
13001
13059
|
return c.json({ ok: false }, 401);
|
|
13002
13060
|
}
|
|
13003
13061
|
const expected = readFileSync23(sp, "utf-8").trim();
|
|
13004
13062
|
const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
|
|
13005
13063
|
if (got !== expected) {
|
|
13006
|
-
console.error(`${TAG19} op=
|
|
13064
|
+
console.error(`${TAG19} op=secret botId=${botId} result=mismatch`);
|
|
13007
13065
|
return c.json({ ok: false }, 401);
|
|
13008
13066
|
}
|
|
13067
|
+
console.error(`${TAG19} op=secret botId=${botId} result=ok`);
|
|
13009
13068
|
let update;
|
|
13010
13069
|
try {
|
|
13011
13070
|
update = await c.req.json();
|
|
13012
13071
|
} catch {
|
|
13013
13072
|
return c.json({ ok: true }, 200);
|
|
13014
13073
|
}
|
|
13015
|
-
const decision = routeTelegramUpdate({ update,
|
|
13074
|
+
const decision = routeTelegramUpdate({ update, entry });
|
|
13016
13075
|
if (decision.kind === "ignore") {
|
|
13017
|
-
console.error(`${TAG19} op=ignore reason=${decision.reason}`);
|
|
13076
|
+
console.error(`${TAG19} op=ignore botId=${botId} reason=${decision.reason}`);
|
|
13018
13077
|
return c.json({ ok: true }, 200);
|
|
13019
13078
|
}
|
|
13020
13079
|
if (decision.kind === "denied") {
|
|
13021
|
-
console.error(`${TAG19} op=
|
|
13080
|
+
console.error(`${TAG19} op=access botId=${botId} senderId=- allowed=false reason=${decision.reason}`);
|
|
13022
13081
|
return c.json({ ok: true }, 200);
|
|
13023
13082
|
}
|
|
13024
|
-
const
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13083
|
+
const { senderId, chatId, text } = decision;
|
|
13084
|
+
console.error(`${TAG19} op=access botId=${botId} senderId=${senderId} allowed=true reason=${decision.reason}`);
|
|
13085
|
+
const active = entry.role === "admin" ? true : isActiveAgentSlug(accountDir, entry.agent);
|
|
13086
|
+
console.error(`${TAG19} op=agent botId=${botId} slug=${agentSlug} active=${active}`);
|
|
13087
|
+
if (!active) {
|
|
13088
|
+
console.error(`${TAG19} op=reject reason=agent-inactive botId=${botId} agent=${agentSlug}`);
|
|
13028
13089
|
return c.json({ ok: true }, 200);
|
|
13029
13090
|
}
|
|
13030
|
-
console.error(`${TAG19} op=update accountId=${account.accountId} from=${decision.senderId}`);
|
|
13031
|
-
const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
|
|
13032
|
-
const { senderId, chatId, text } = decision;
|
|
13033
13091
|
const gateway = getTelegramGateway();
|
|
13034
13092
|
if (!gateway) {
|
|
13035
|
-
console.error(`${TAG19} op=
|
|
13093
|
+
console.error(`${TAG19} op=reply-dropped botId=${botId} reason=gateway-not-ready`);
|
|
13036
13094
|
return c.json({ ok: true }, 200);
|
|
13037
13095
|
}
|
|
13038
13096
|
const reply = async (replyText) => {
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
return;
|
|
13043
|
-
}
|
|
13044
|
-
const sent = await sendTelegramText(botToken, chatId, replyText);
|
|
13045
|
-
console.error(`${TAG19} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
|
|
13097
|
+
const sent = await sendTelegramText(entry.token, chatId, replyText);
|
|
13098
|
+
if (sent.ok) console.error(`${TAG19} op=reply-sent botId=${botId} ok=true`);
|
|
13099
|
+
else console.error(`${TAG19} op=reply-dropped botId=${botId} reason=${sent.error}`);
|
|
13046
13100
|
};
|
|
13047
13101
|
void gateway.handleInbound({
|
|
13048
|
-
accountId
|
|
13102
|
+
accountId,
|
|
13103
|
+
accountDir,
|
|
13104
|
+
botId,
|
|
13105
|
+
botToken: entry.token,
|
|
13106
|
+
agentSlug,
|
|
13049
13107
|
senderId,
|
|
13050
|
-
role,
|
|
13108
|
+
role: entry.role,
|
|
13051
13109
|
personId: null,
|
|
13052
13110
|
chatId: String(chatId),
|
|
13053
13111
|
text,
|
|
@@ -16927,18 +16985,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
16927
16985
|
]);
|
|
16928
16986
|
var app24 = new Hono();
|
|
16929
16987
|
app24.post("/", async (c) => {
|
|
16930
|
-
const
|
|
16988
|
+
const TAG54 = "[admin:events]";
|
|
16931
16989
|
let body;
|
|
16932
16990
|
try {
|
|
16933
16991
|
body = await c.req.json();
|
|
16934
16992
|
} catch (err) {
|
|
16935
16993
|
const detail = err instanceof Error ? err.message : String(err);
|
|
16936
|
-
console.error(`${
|
|
16994
|
+
console.error(`${TAG54} reject reason=body-not-json detail=${detail}`);
|
|
16937
16995
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
16938
16996
|
}
|
|
16939
16997
|
const event = typeof body.event === "string" ? body.event : "";
|
|
16940
16998
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
16941
|
-
console.error(`${
|
|
16999
|
+
console.error(`${TAG54} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
16942
17000
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
16943
17001
|
}
|
|
16944
17002
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -22405,6 +22463,7 @@ async function managerSpawn(opts) {
|
|
|
22405
22463
|
name: opts.name,
|
|
22406
22464
|
webchatChannel: opts.webchatChannel,
|
|
22407
22465
|
waChannel: opts.waChannel,
|
|
22466
|
+
telegramChannel: opts.telegramChannel,
|
|
22408
22467
|
sessionId: opts.sessionId,
|
|
22409
22468
|
previousContext: opts.previousContext,
|
|
22410
22469
|
initialMessage: opts.initialMessage,
|
|
@@ -22448,6 +22507,7 @@ async function managerRcSpawn(opts) {
|
|
|
22448
22507
|
adminUserId: opts.adminUserId,
|
|
22449
22508
|
waChannel: opts.waChannel,
|
|
22450
22509
|
webchatChannel: opts.webchatChannel,
|
|
22510
|
+
telegramChannel: opts.telegramChannel,
|
|
22451
22511
|
role: opts.role,
|
|
22452
22512
|
channel: opts.channel,
|
|
22453
22513
|
senderId: opts.senderId,
|
|
@@ -23934,8 +23994,24 @@ function readChannelAdmins(accountDir, channel) {
|
|
|
23934
23994
|
return { adminPhones, accountManagers };
|
|
23935
23995
|
}
|
|
23936
23996
|
const tg = config.telegram ?? {};
|
|
23937
|
-
const
|
|
23938
|
-
|
|
23997
|
+
const byBot = [];
|
|
23998
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23999
|
+
const adminUsers = [];
|
|
24000
|
+
if (Array.isArray(tg.bots)) {
|
|
24001
|
+
for (const raw of tg.bots) {
|
|
24002
|
+
if (!raw || typeof raw !== "object") continue;
|
|
24003
|
+
const b = raw;
|
|
24004
|
+
if (b.role !== "admin" || typeof b.id !== "string" || b.id.length === 0) continue;
|
|
24005
|
+
const users = Array.isArray(b.adminUsers) ? b.adminUsers.filter((u) => typeof u === "number") : [];
|
|
24006
|
+
byBot.push({ botId: b.id, adminUsers: users });
|
|
24007
|
+
for (const u of users) {
|
|
24008
|
+
if (seen.has(u)) continue;
|
|
24009
|
+
seen.add(u);
|
|
24010
|
+
adminUsers.push(u);
|
|
24011
|
+
}
|
|
24012
|
+
}
|
|
24013
|
+
}
|
|
24014
|
+
return { adminUsers, byBot };
|
|
23939
24015
|
}
|
|
23940
24016
|
function registeredAccountForDestination(houseDir, houseAccountId, destination) {
|
|
23941
24017
|
const { adminPhones, accountManagers } = readChannelAdmins(houseDir, "whatsapp");
|
|
@@ -23994,8 +24070,13 @@ function listDispatchDestinations(input) {
|
|
|
23994
24070
|
admins.push({ channel: "whatsapp", destination: phone, label: "House admin" });
|
|
23995
24071
|
}
|
|
23996
24072
|
}
|
|
23997
|
-
const {
|
|
23998
|
-
const telegram =
|
|
24073
|
+
const { byBot } = readChannelAdmins(resolve31(accountsDir, accountId), "telegram");
|
|
24074
|
+
const telegram = byBot.flatMap((b) => b.adminUsers.map((id) => ({ botId: b.botId, id }))).sort((a, b) => a.id - b.id || a.botId.localeCompare(b.botId)).map(({ botId, id }) => ({
|
|
24075
|
+
channel: "telegram",
|
|
24076
|
+
destination: String(id),
|
|
24077
|
+
label: "Telegram admin",
|
|
24078
|
+
botId
|
|
24079
|
+
}));
|
|
23999
24080
|
const byDestination = (a, b) => a.destination.localeCompare(b.destination);
|
|
24000
24081
|
return [...managers.sort(byDestination), ...admins.sort(byDestination), ...telegram];
|
|
24001
24082
|
}
|
|
@@ -31102,19 +31183,22 @@ var TelegramGateway = class {
|
|
|
31102
31183
|
* its own `sessionIdFor`.
|
|
31103
31184
|
*/
|
|
31104
31185
|
sessionIdFor(input) {
|
|
31105
|
-
|
|
31186
|
+
const key = telegramChannelKey(input.botId, input.senderId);
|
|
31187
|
+
return (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.accountId, key) : null) || adminSessionIdFor(
|
|
31106
31188
|
input.accountId,
|
|
31107
31189
|
input.senderId,
|
|
31108
|
-
input.role === "public" && input.personId ? input.personId : void 0
|
|
31190
|
+
input.role === "public" && input.personId ? input.personId : void 0,
|
|
31191
|
+
input.botId
|
|
31109
31192
|
);
|
|
31110
31193
|
}
|
|
31111
31194
|
/** Handle one inbound telegram message. */
|
|
31112
31195
|
async handleInbound(input) {
|
|
31113
31196
|
const bytes = Buffer.byteLength(input.text, "utf8");
|
|
31114
|
-
const
|
|
31115
|
-
const
|
|
31197
|
+
const key = telegramChannelKey(input.botId, input.senderId);
|
|
31198
|
+
const hadSubscriber = this.hub.hasSubscriber(key);
|
|
31199
|
+
const willDeliverNow = this.hub.isReady(key);
|
|
31116
31200
|
const source = input.source ?? "user";
|
|
31117
|
-
this.replies.set(
|
|
31201
|
+
this.replies.set(key, input.reply);
|
|
31118
31202
|
let standingRules;
|
|
31119
31203
|
if (input.role === "admin" && this.deps.fetchStandingRules) {
|
|
31120
31204
|
const res = await this.deps.fetchStandingRules(input.accountId);
|
|
@@ -31126,19 +31210,24 @@ var TelegramGateway = class {
|
|
|
31126
31210
|
);
|
|
31127
31211
|
}
|
|
31128
31212
|
this.hub.deliver(
|
|
31129
|
-
{ key
|
|
31213
|
+
{ key, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
|
|
31130
31214
|
Date.now()
|
|
31131
31215
|
);
|
|
31132
31216
|
const resolvedSessionId = this.sessionIdFor(input);
|
|
31133
31217
|
console.error(
|
|
31134
|
-
`[telegram-native] op=inbound key=${input.
|
|
31218
|
+
`[telegram-native] op=inbound key=${key} botId=${input.botId} accountId=${input.accountId} role=${input.role} agent=${input.agentSlug} bytes=${bytes} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"} sessionId=${resolvedSessionId}`
|
|
31135
31219
|
);
|
|
31136
|
-
if (!hadSubscriber && !this.spawning.has(
|
|
31137
|
-
this.spawning.add(
|
|
31138
|
-
console.error(`[telegram-native] op=spawn-trigger key=${input.
|
|
31220
|
+
if (!hadSubscriber && !this.spawning.has(key)) {
|
|
31221
|
+
this.spawning.add(key);
|
|
31222
|
+
console.error(`[telegram-native] op=spawn-trigger key=${key} botId=${input.botId} role=${input.role}`);
|
|
31139
31223
|
try {
|
|
31140
31224
|
await this.deps.ensureChannelSession({
|
|
31141
31225
|
accountId: input.accountId,
|
|
31226
|
+
accountDir: input.accountDir,
|
|
31227
|
+
botId: input.botId,
|
|
31228
|
+
botToken: input.botToken,
|
|
31229
|
+
agentSlug: input.agentSlug,
|
|
31230
|
+
channelKey: key,
|
|
31142
31231
|
senderId: input.senderId,
|
|
31143
31232
|
role: input.role,
|
|
31144
31233
|
personId: input.personId ?? null,
|
|
@@ -31147,7 +31236,7 @@ var TelegramGateway = class {
|
|
|
31147
31236
|
serverPath: this.deps.serverPath
|
|
31148
31237
|
});
|
|
31149
31238
|
} finally {
|
|
31150
|
-
this.spawning.delete(
|
|
31239
|
+
this.spawning.delete(key);
|
|
31151
31240
|
}
|
|
31152
31241
|
}
|
|
31153
31242
|
}
|
|
@@ -31161,12 +31250,17 @@ var TelegramGateway = class {
|
|
|
31161
31250
|
// app/lib/telegram/gateway/spawn-request.ts
|
|
31162
31251
|
function buildTelegramSpawnRequest(input) {
|
|
31163
31252
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
31253
|
+
const channelKey = telegramChannelKey(input.botId, input.senderId);
|
|
31164
31254
|
return {
|
|
31165
31255
|
// Task 1746 / 1789 — the deterministic per-sender id is the DEFAULT; an
|
|
31166
31256
|
// operator re-seat sets a stable per-sender forward pointer that wins here.
|
|
31167
31257
|
// Mirror of the WhatsApp builder; see its comment for the full distinction
|
|
31168
31258
|
// between this pointer and Task 1526's removed per-firing seat.
|
|
31169
|
-
|
|
31259
|
+
// Task 2368 — the bot id is a hash component of its own, not part of the
|
|
31260
|
+
// subject: with a resolved personId the subject is `person:<personId>` and
|
|
31261
|
+
// the sender never enters the hash at all.
|
|
31262
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId, input.botId),
|
|
31263
|
+
channelKey,
|
|
31170
31264
|
role: input.role,
|
|
31171
31265
|
channel: "telegram",
|
|
31172
31266
|
personId,
|
|
@@ -31176,8 +31270,78 @@ function buildTelegramSpawnRequest(input) {
|
|
|
31176
31270
|
// session resolve under that account, not the manager's boot-time house
|
|
31177
31271
|
// default. Mirror of the WhatsApp builder.
|
|
31178
31272
|
targetAccountId: input.accountId,
|
|
31179
|
-
telegramChannel: { senderId:
|
|
31180
|
-
logContext: `channel=telegram-native role=${input.role} senderId=${input.senderId}`
|
|
31273
|
+
telegramChannel: { senderId: channelKey, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath },
|
|
31274
|
+
logContext: `channel=telegram-native role=${input.role} botId=${input.botId} senderId=${input.senderId}`
|
|
31275
|
+
};
|
|
31276
|
+
}
|
|
31277
|
+
|
|
31278
|
+
// server/telegram-ensure-session.ts
|
|
31279
|
+
var TAG50 = "[telegram-inbound]";
|
|
31280
|
+
function buildTelegramEnsureSession(deps) {
|
|
31281
|
+
return async (input) => {
|
|
31282
|
+
const req = buildTelegramSpawnRequest({
|
|
31283
|
+
accountId: input.accountId,
|
|
31284
|
+
senderId: input.senderId,
|
|
31285
|
+
role: input.role,
|
|
31286
|
+
personId: input.personId,
|
|
31287
|
+
gatewayUrl: input.gatewayUrl,
|
|
31288
|
+
serverPath: input.serverPath,
|
|
31289
|
+
botId: input.botId,
|
|
31290
|
+
// Task 1789 — admin rows only; see the WhatsApp twin.
|
|
31291
|
+
overrideSessionId: input.role === "admin" ? deps.resolveSessionOverride(input.accountId, input.channelKey) : null
|
|
31292
|
+
});
|
|
31293
|
+
if (input.role === "public") {
|
|
31294
|
+
const result2 = await deps.managerSpawn({
|
|
31295
|
+
senderId: req.channelKey,
|
|
31296
|
+
role: "public",
|
|
31297
|
+
channel: "telegram",
|
|
31298
|
+
accountId: input.accountId,
|
|
31299
|
+
targetAccountId: input.accountId,
|
|
31300
|
+
// The slug came from THIS account's own bot entry, so the spawn reads
|
|
31301
|
+
// that account's agents directory, runs in its directory and files its
|
|
31302
|
+
// session under it. Mirror of the WhatsApp public-agent path.
|
|
31303
|
+
agentAccountId: input.accountId,
|
|
31304
|
+
agentSlug: input.agentSlug,
|
|
31305
|
+
sessionId: req.sessionId,
|
|
31306
|
+
personId: input.personId ?? void 0,
|
|
31307
|
+
telegramChannel: req.telegramChannel
|
|
31308
|
+
});
|
|
31309
|
+
if ("error" in result2) {
|
|
31310
|
+
console.error(
|
|
31311
|
+
`[telegram-native] op=spawn-failed botId=${input.botId} key=${req.channelKey} role=public agent=${input.agentSlug} error=${result2.error} status=${result2.status}`
|
|
31312
|
+
);
|
|
31313
|
+
return;
|
|
31314
|
+
}
|
|
31315
|
+
console.error(
|
|
31316
|
+
`${TAG50} op=spawn botId=${input.botId} sessionId=${result2.sessionId} targetAccountId=${input.accountId} role=public`
|
|
31317
|
+
);
|
|
31318
|
+
return;
|
|
31319
|
+
}
|
|
31320
|
+
const result = await deps.managerRcSpawn(req);
|
|
31321
|
+
if ("error" in result) {
|
|
31322
|
+
console.error(
|
|
31323
|
+
`[telegram-native] op=spawn-failed botId=${input.botId} key=${req.channelKey} role=admin error=${result.error} status=${result.status}`
|
|
31324
|
+
);
|
|
31325
|
+
return;
|
|
31326
|
+
}
|
|
31327
|
+
console.error(
|
|
31328
|
+
`${TAG50} op=spawn botId=${input.botId} sessionId=${req.sessionId} targetAccountId=${input.accountId} role=admin`
|
|
31329
|
+
);
|
|
31330
|
+
deps.followers.get(req.channelKey)?.abort();
|
|
31331
|
+
let ac;
|
|
31332
|
+
ac = deps.startFileFollower({
|
|
31333
|
+
sessionId: req.sessionId,
|
|
31334
|
+
senderId: req.channelKey,
|
|
31335
|
+
accountId: input.accountId,
|
|
31336
|
+
chatId: input.chatId,
|
|
31337
|
+
botToken: input.botToken,
|
|
31338
|
+
// Identity-guarded eviction: only delete if this controller is still the
|
|
31339
|
+
// current one, so a stale self-close never removes a live follower.
|
|
31340
|
+
onClose: () => {
|
|
31341
|
+
if (deps.followers.get(req.channelKey) === ac) deps.followers.delete(req.channelKey);
|
|
31342
|
+
}
|
|
31343
|
+
});
|
|
31344
|
+
deps.followers.set(req.channelKey, ac);
|
|
31181
31345
|
};
|
|
31182
31346
|
}
|
|
31183
31347
|
|
|
@@ -31185,7 +31349,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
31185
31349
|
import { realpathSync as realpathSync10 } from "fs";
|
|
31186
31350
|
import { readFile as readFile14, stat as stat10 } from "fs/promises";
|
|
31187
31351
|
import { resolve as resolve42, basename as basename16 } from "path";
|
|
31188
|
-
var
|
|
31352
|
+
var TAG51 = "[telegram:outbound]";
|
|
31189
31353
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
31190
31354
|
async function sendTelegramDocument(input) {
|
|
31191
31355
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -31202,10 +31366,10 @@ async function sendTelegramDocument(input) {
|
|
|
31202
31366
|
} catch (err) {
|
|
31203
31367
|
const code = err.code;
|
|
31204
31368
|
if (code === "ENOENT") {
|
|
31205
|
-
console.error(`${
|
|
31369
|
+
console.error(`${TAG51} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
|
|
31206
31370
|
return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
|
|
31207
31371
|
}
|
|
31208
|
-
console.error(`${
|
|
31372
|
+
console.error(`${TAG51} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
31209
31373
|
return { ok: false, status: 500, error: String(err) };
|
|
31210
31374
|
}
|
|
31211
31375
|
let resolvedPath;
|
|
@@ -31214,14 +31378,14 @@ async function sendTelegramDocument(input) {
|
|
|
31214
31378
|
} catch (err) {
|
|
31215
31379
|
const code = err.code;
|
|
31216
31380
|
if (code === "ENOENT") {
|
|
31217
|
-
console.error(`${
|
|
31381
|
+
console.error(`${TAG51} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
|
|
31218
31382
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
31219
31383
|
}
|
|
31220
|
-
console.error(`${
|
|
31384
|
+
console.error(`${TAG51} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
31221
31385
|
return { ok: false, status: 500, error: String(err) };
|
|
31222
31386
|
}
|
|
31223
31387
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
31224
|
-
console.error(`${
|
|
31388
|
+
console.error(`${TAG51} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
31225
31389
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
31226
31390
|
}
|
|
31227
31391
|
const fileStat = await stat10(resolvedPath);
|
|
@@ -31254,29 +31418,24 @@ async function sendTelegramDocument(input) {
|
|
|
31254
31418
|
error = e instanceof Error ? e.message : String(e);
|
|
31255
31419
|
}
|
|
31256
31420
|
console.error(
|
|
31257
|
-
`${
|
|
31421
|
+
`${TAG51} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok2}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
31258
31422
|
);
|
|
31259
31423
|
return ok2 ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
31260
31424
|
}
|
|
31261
31425
|
|
|
31262
31426
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
31263
|
-
var
|
|
31427
|
+
var TAG52 = "[telegram:outbound]";
|
|
31264
31428
|
function platformRoot4() {
|
|
31265
31429
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
31266
31430
|
}
|
|
31267
|
-
function makeTelegramSendFile(entry) {
|
|
31268
|
-
let botToken;
|
|
31431
|
+
function makeTelegramSendFile(entry, botToken) {
|
|
31269
31432
|
return async (filePath, caption) => {
|
|
31270
|
-
if (botToken === void 0) {
|
|
31271
|
-
const tg = resolveAccount()?.config.telegram;
|
|
31272
|
-
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
31273
|
-
}
|
|
31274
31433
|
if (!botToken) {
|
|
31275
|
-
console.error(`${
|
|
31434
|
+
console.error(`${TAG52} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
31276
31435
|
return { ok: false, error: "no-bot-token" };
|
|
31277
31436
|
}
|
|
31278
31437
|
if (entry.replyTarget == null) {
|
|
31279
|
-
console.error(`${
|
|
31438
|
+
console.error(`${TAG52} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
31280
31439
|
return { ok: false, error: "no-reply-target" };
|
|
31281
31440
|
}
|
|
31282
31441
|
const result = await sendTelegramDocument({
|
|
@@ -31290,12 +31449,12 @@ function makeTelegramSendFile(entry) {
|
|
|
31290
31449
|
return result.ok ? { ok: true } : { ok: false, error: result.error };
|
|
31291
31450
|
};
|
|
31292
31451
|
}
|
|
31293
|
-
function makeTelegramFileDelivery(entry) {
|
|
31452
|
+
function makeTelegramFileDelivery(entry, botToken) {
|
|
31294
31453
|
return makeFileDelivery({
|
|
31295
31454
|
entry,
|
|
31296
|
-
tag:
|
|
31455
|
+
tag: TAG52,
|
|
31297
31456
|
channel: "telegram",
|
|
31298
|
-
sendFile: makeTelegramSendFile(entry)
|
|
31457
|
+
sendFile: makeTelegramSendFile(entry, botToken)
|
|
31299
31458
|
});
|
|
31300
31459
|
}
|
|
31301
31460
|
|
|
@@ -31323,7 +31482,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
31323
31482
|
return startFollower({
|
|
31324
31483
|
entry,
|
|
31325
31484
|
tag: "[telegram-adaptor]",
|
|
31326
|
-
fileDelivery: makeTelegramFileDelivery(entry),
|
|
31485
|
+
fileDelivery: makeTelegramFileDelivery(entry, input.botToken),
|
|
31327
31486
|
// A resumed session's JSONL already holds prior SendUserFile tool_uses;
|
|
31328
31487
|
// suppress replay so historical files are not re-sent on attach.
|
|
31329
31488
|
suppressResumeReplay: true,
|
|
@@ -31334,7 +31493,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
31334
31493
|
|
|
31335
31494
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
31336
31495
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
31337
|
-
var
|
|
31496
|
+
var TAG53 = "[public-session-review]";
|
|
31338
31497
|
var CONSUMED_CAP = 500;
|
|
31339
31498
|
var consumed = /* @__PURE__ */ new Set();
|
|
31340
31499
|
function consumeOnce(sessionId) {
|
|
@@ -31361,7 +31520,7 @@ async function fetchJsonl(sessionId) {
|
|
|
31361
31520
|
return await res.text();
|
|
31362
31521
|
} catch (err) {
|
|
31363
31522
|
console.error(
|
|
31364
|
-
`${
|
|
31523
|
+
`${TAG53} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
31365
31524
|
);
|
|
31366
31525
|
return null;
|
|
31367
31526
|
}
|
|
@@ -31385,7 +31544,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
31385
31544
|
const res = await fetch(url);
|
|
31386
31545
|
if (!res.ok) {
|
|
31387
31546
|
console.error(
|
|
31388
|
-
`${
|
|
31547
|
+
`${TAG53} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
31389
31548
|
);
|
|
31390
31549
|
return [];
|
|
31391
31550
|
}
|
|
@@ -31393,7 +31552,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
31393
31552
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
31394
31553
|
} catch (err) {
|
|
31395
31554
|
console.error(
|
|
31396
|
-
`${
|
|
31555
|
+
`${TAG53} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
31397
31556
|
);
|
|
31398
31557
|
return [];
|
|
31399
31558
|
}
|
|
@@ -31422,7 +31581,7 @@ function composeInitialMessage(input) {
|
|
|
31422
31581
|
}
|
|
31423
31582
|
async function dispatchReviewer(input, initialMessage) {
|
|
31424
31583
|
const sessionId = randomUUID14();
|
|
31425
|
-
console.log(`${
|
|
31584
|
+
console.log(`${TAG53} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
31426
31585
|
const spawned = await managerRcSpawn({
|
|
31427
31586
|
sessionId,
|
|
31428
31587
|
initialMessage,
|
|
@@ -31446,21 +31605,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
31446
31605
|
const dispatchedAt = Date.now();
|
|
31447
31606
|
if (consumed.has(input.sessionId)) {
|
|
31448
31607
|
console.log(
|
|
31449
|
-
`${
|
|
31608
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
31450
31609
|
);
|
|
31451
31610
|
return;
|
|
31452
31611
|
}
|
|
31453
31612
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
31454
31613
|
if (!jsonl) {
|
|
31455
31614
|
console.log(
|
|
31456
|
-
`${
|
|
31615
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
31457
31616
|
);
|
|
31458
31617
|
return;
|
|
31459
31618
|
}
|
|
31460
31619
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
31461
31620
|
if (operatorTurns === 0) {
|
|
31462
31621
|
console.log(
|
|
31463
|
-
`${
|
|
31622
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
31464
31623
|
);
|
|
31465
31624
|
return;
|
|
31466
31625
|
}
|
|
@@ -31474,19 +31633,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
31474
31633
|
});
|
|
31475
31634
|
if (!consumeOnce(input.sessionId)) {
|
|
31476
31635
|
console.log(
|
|
31477
|
-
`${
|
|
31636
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
31478
31637
|
);
|
|
31479
31638
|
return;
|
|
31480
31639
|
}
|
|
31481
31640
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
31482
31641
|
if (!dispatched.ok) {
|
|
31483
31642
|
console.error(
|
|
31484
|
-
`${
|
|
31643
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
31485
31644
|
);
|
|
31486
31645
|
return;
|
|
31487
31646
|
}
|
|
31488
31647
|
console.log(
|
|
31489
|
-
`${
|
|
31648
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
31490
31649
|
);
|
|
31491
31650
|
}
|
|
31492
31651
|
|
|
@@ -31624,28 +31783,59 @@ import { statSync as statSync25 } from "fs";
|
|
|
31624
31783
|
import { basename as basename17, dirname as dirname17, join as join54 } from "path";
|
|
31625
31784
|
function buildTelegramAuditLines(input) {
|
|
31626
31785
|
const lines = [];
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
|
|
31633
|
-
|
|
31634
|
-
|
|
31635
|
-
|
|
31636
|
-
|
|
31637
|
-
|
|
31786
|
+
const entries2 = input.accounts.flatMap(
|
|
31787
|
+
(a) => listBotEntries(a.telegram).map((entry) => ({ accountId: a.accountId, accountDir: a.accountDir, entry }))
|
|
31788
|
+
);
|
|
31789
|
+
if (entries2.length === 0) return lines;
|
|
31790
|
+
const byId = /* @__PURE__ */ new Map();
|
|
31791
|
+
for (const { entry } of entries2) byId.set(entry.id, (byId.get(entry.id) ?? 0) + 1);
|
|
31792
|
+
for (const [botId, count] of byId) {
|
|
31793
|
+
if (count > 1) lines.push(`[telegram-audit] op=duplicate-bot-id botId=${botId} accounts=${count}`);
|
|
31794
|
+
}
|
|
31795
|
+
for (const { accountId, accountDir, entry } of entries2) {
|
|
31796
|
+
if (entry.role === "public" && !input.agentActive(accountDir, entry.agent)) {
|
|
31638
31797
|
lines.push(
|
|
31639
|
-
`[telegram-audit] op=
|
|
31798
|
+
`[telegram-audit] op=entry-agent-inactive accountId=${accountId} botId=${entry.id} agent=${entry.agent}`
|
|
31640
31799
|
);
|
|
31641
31800
|
}
|
|
31801
|
+
if (!input.secretFileExists(entry.id)) {
|
|
31802
|
+
lines.push(`[telegram-audit] op=secret-orphan botId=${entry.id} side=entry`);
|
|
31803
|
+
}
|
|
31804
|
+
}
|
|
31805
|
+
const known = new Set(entries2.map((e) => e.entry.id));
|
|
31806
|
+
for (const botId of input.secretFilesOnDisk) {
|
|
31807
|
+
if (!known.has(botId)) lines.push(`[telegram-audit] op=secret-orphan botId=${botId} side=file`);
|
|
31808
|
+
}
|
|
31809
|
+
for (const { accountId, entry } of entries2) {
|
|
31810
|
+
const last = input.lastInboundByBot(entry.id);
|
|
31811
|
+
const age = last === null ? "never" : String(input.now - last);
|
|
31812
|
+
const agent = entry.role === "admin" ? "admin" : entry.agent;
|
|
31813
|
+
const secretFile = input.secretFileExists(entry.id) ? "present" : "absent";
|
|
31814
|
+
lines.push(
|
|
31815
|
+
`[telegram-audit] op=configured botId=${entry.id} accountId=${accountId} agent=${agent} secretFile=${secretFile} lastInboundAgeMs=${age}`
|
|
31816
|
+
);
|
|
31642
31817
|
}
|
|
31643
31818
|
return lines;
|
|
31644
31819
|
}
|
|
31645
|
-
function
|
|
31820
|
+
function newestTelegramSessionByBot() {
|
|
31646
31821
|
const out = /* @__PURE__ */ new Map();
|
|
31822
|
+
for (const { botId, mtime } of walkTelegramSidecars()) {
|
|
31823
|
+
if (botId === null) continue;
|
|
31824
|
+
const prev = out.get(botId);
|
|
31825
|
+
if (prev === void 0 || mtime > prev) out.set(botId, mtime);
|
|
31826
|
+
}
|
|
31827
|
+
return out;
|
|
31828
|
+
}
|
|
31829
|
+
function countTelegramSessionsForAccount(accountId) {
|
|
31830
|
+
let n = 0;
|
|
31831
|
+
for (const { accountId: id } of walkTelegramSidecars()) {
|
|
31832
|
+
if (id === accountId) n += 1;
|
|
31833
|
+
}
|
|
31834
|
+
return n;
|
|
31835
|
+
}
|
|
31836
|
+
function* walkTelegramSidecars() {
|
|
31647
31837
|
const cfg = claudeConfigDir();
|
|
31648
|
-
if (!cfg) return
|
|
31838
|
+
if (!cfg) return;
|
|
31649
31839
|
const projectsRoot = join54(cfg, "projects");
|
|
31650
31840
|
for (const { path } of enumerateJsonls(projectsRoot)) {
|
|
31651
31841
|
const sessionId = basename17(path).replace(/\.jsonl$/, "");
|
|
@@ -31657,11 +31847,80 @@ function newestTelegramSessionByAccount() {
|
|
|
31657
31847
|
} catch {
|
|
31658
31848
|
continue;
|
|
31659
31849
|
}
|
|
31660
|
-
|
|
31661
|
-
|
|
31850
|
+
yield {
|
|
31851
|
+
accountId: meta.accountId,
|
|
31852
|
+
botId: parseTelegramChannelKey(meta.senderId ?? "")?.botId ?? null,
|
|
31853
|
+
mtime
|
|
31854
|
+
};
|
|
31855
|
+
}
|
|
31856
|
+
}
|
|
31857
|
+
|
|
31858
|
+
// server/telegram-migrate.ts
|
|
31859
|
+
import { readFileSync as readFileSync45, writeFileSync as writeFileSync17, existsSync as existsSync49, renameSync as renameSync11, mkdirSync as mkdirSync10 } from "fs";
|
|
31860
|
+
import { dirname as dirname18, join as join55 } from "path";
|
|
31861
|
+
function migrateTelegramTwoSlot(input) {
|
|
31862
|
+
const out = [];
|
|
31863
|
+
for (const account of input.accounts) {
|
|
31864
|
+
const configPath3 = join55(account.accountDir, "account.json");
|
|
31865
|
+
let config;
|
|
31866
|
+
try {
|
|
31867
|
+
config = JSON.parse(readFileSync45(configPath3, "utf-8"));
|
|
31868
|
+
} catch {
|
|
31869
|
+
console.error(`[telegram-audit] op=migrate-unreadable accountId=${account.accountId}`);
|
|
31870
|
+
continue;
|
|
31871
|
+
}
|
|
31872
|
+
const tg = config.telegram;
|
|
31873
|
+
if (!tg) continue;
|
|
31874
|
+
const adminToken = typeof tg.adminBotToken === "string" ? tg.adminBotToken : null;
|
|
31875
|
+
const publicToken = typeof tg.publicBotToken === "string" ? tg.publicBotToken : null;
|
|
31876
|
+
if (!adminToken && !publicToken) continue;
|
|
31877
|
+
const outcome = { accountId: account.accountId, converted: 0, orphanSessions: 0 };
|
|
31878
|
+
const bots = Array.isArray(tg.bots) ? [...tg.bots] : [];
|
|
31879
|
+
if (adminToken) {
|
|
31880
|
+
const id = botIdFromToken(adminToken);
|
|
31881
|
+
if (!id) {
|
|
31882
|
+
outcome.skippedAdmin = "malformed-token";
|
|
31883
|
+
} else {
|
|
31884
|
+
const adminUsers = Array.isArray(tg.adminUsers) ? tg.adminUsers.filter((u) => typeof u === "number") : void 0;
|
|
31885
|
+
bots.push({ id, token: adminToken, role: "admin", ...adminUsers ? { adminUsers } : {} });
|
|
31886
|
+
moveSecret(input.legacySecretPathFor("admin", account.accountId), input.secretPathFor(id));
|
|
31887
|
+
outcome.converted += 1;
|
|
31888
|
+
}
|
|
31889
|
+
}
|
|
31890
|
+
if (publicToken) {
|
|
31891
|
+
const id = botIdFromToken(publicToken);
|
|
31892
|
+
const agent = typeof config.defaultAgent === "string" && config.defaultAgent.length > 0 ? config.defaultAgent : null;
|
|
31893
|
+
if (!id) {
|
|
31894
|
+
outcome.skippedPublic = "malformed-token";
|
|
31895
|
+
} else if (!agent) {
|
|
31896
|
+
outcome.skippedPublic = "no-default-agent";
|
|
31897
|
+
} else {
|
|
31898
|
+
const dmPolicy = typeof tg.dmPolicy === "string" ? tg.dmPolicy : void 0;
|
|
31899
|
+
const allowFrom = Array.isArray(tg.allowFrom) ? tg.allowFrom.filter((u) => typeof u === "number") : void 0;
|
|
31900
|
+
bots.push({
|
|
31901
|
+
id,
|
|
31902
|
+
token: publicToken,
|
|
31903
|
+
role: "public",
|
|
31904
|
+
agent,
|
|
31905
|
+
...dmPolicy ? { dmPolicy } : {},
|
|
31906
|
+
...allowFrom ? { allowFrom } : {}
|
|
31907
|
+
});
|
|
31908
|
+
moveSecret(input.legacySecretPathFor("public", account.accountId), input.secretPathFor(id));
|
|
31909
|
+
outcome.converted += 1;
|
|
31910
|
+
}
|
|
31911
|
+
}
|
|
31912
|
+
config.telegram = { bots };
|
|
31913
|
+
writeFileSync17(configPath3, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
31914
|
+
outcome.orphanSessions = input.countOrphanSessions(account.accountId);
|
|
31915
|
+
out.push(outcome);
|
|
31662
31916
|
}
|
|
31663
31917
|
return out;
|
|
31664
31918
|
}
|
|
31919
|
+
function moveSecret(from, to) {
|
|
31920
|
+
if (!existsSync49(from)) return;
|
|
31921
|
+
mkdirSync10(dirname18(to), { recursive: true, mode: 448 });
|
|
31922
|
+
renameSync11(from, to);
|
|
31923
|
+
}
|
|
31665
31924
|
|
|
31666
31925
|
// app/lib/whatsapp/recall-store-backfill.ts
|
|
31667
31926
|
function backfillMisfiledRecords(params) {
|
|
@@ -31699,14 +31958,14 @@ function backfillMisfiledRecords(params) {
|
|
|
31699
31958
|
}
|
|
31700
31959
|
|
|
31701
31960
|
// server/lib/booking-site-accounts.ts
|
|
31702
|
-
import { existsSync as
|
|
31961
|
+
import { existsSync as existsSync50, readFileSync as readFileSync46 } from "fs";
|
|
31703
31962
|
import { resolve as resolve43 } from "path";
|
|
31704
31963
|
function listBookingSiteAccounts(accounts) {
|
|
31705
31964
|
return accounts.filter((a) => {
|
|
31706
31965
|
const availPath = resolve43(a.accountDir, "calendar-availability.json");
|
|
31707
|
-
if (!
|
|
31966
|
+
if (!existsSync50(availPath)) return false;
|
|
31708
31967
|
try {
|
|
31709
|
-
const cfg = JSON.parse(
|
|
31968
|
+
const cfg = JSON.parse(readFileSync46(availPath, "utf-8"));
|
|
31710
31969
|
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
31711
31970
|
} catch {
|
|
31712
31971
|
return false;
|
|
@@ -31744,7 +32003,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
31744
32003
|
|
|
31745
32004
|
// ../lib/entitlement/src/index.ts
|
|
31746
32005
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
31747
|
-
import { existsSync as
|
|
32006
|
+
import { existsSync as existsSync51, readFileSync as readFileSync47, statSync as statSync26 } from "fs";
|
|
31748
32007
|
import { resolve as resolve44 } from "path";
|
|
31749
32008
|
|
|
31750
32009
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -31793,7 +32052,7 @@ function resolveEntitlement(brand, account) {
|
|
|
31793
32052
|
return logResolved(implicitTrust(account), null);
|
|
31794
32053
|
}
|
|
31795
32054
|
const entitlementPath = resolve44(brand.configDir, "entitlement.json");
|
|
31796
|
-
if (!
|
|
32055
|
+
if (!existsSync51(entitlementPath)) {
|
|
31797
32056
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
31798
32057
|
}
|
|
31799
32058
|
const stat11 = statSync26(entitlementPath);
|
|
@@ -31808,7 +32067,7 @@ function resolveEntitlement(brand, account) {
|
|
|
31808
32067
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
31809
32068
|
let pubkeyPem;
|
|
31810
32069
|
try {
|
|
31811
|
-
pubkeyPem =
|
|
32070
|
+
pubkeyPem = readFileSync47(pubkeyPath(brand), "utf-8");
|
|
31812
32071
|
} catch (err) {
|
|
31813
32072
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
31814
32073
|
reason: "pubkey-missing"
|
|
@@ -31822,7 +32081,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
31822
32081
|
}
|
|
31823
32082
|
let envelope;
|
|
31824
32083
|
try {
|
|
31825
|
-
envelope = JSON.parse(
|
|
32084
|
+
envelope = JSON.parse(readFileSync47(entitlementPath, "utf-8"));
|
|
31826
32085
|
} catch {
|
|
31827
32086
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
31828
32087
|
}
|
|
@@ -31984,14 +32243,14 @@ function clientFrom(c) {
|
|
|
31984
32243
|
}
|
|
31985
32244
|
installMediaDownloadGuard();
|
|
31986
32245
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
31987
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT6 ?
|
|
32246
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join56(PLATFORM_ROOT6, "config", "brand.json") : "";
|
|
31988
32247
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
31989
|
-
if (BRAND_JSON_PATH && !
|
|
32248
|
+
if (BRAND_JSON_PATH && !existsSync52(BRAND_JSON_PATH)) {
|
|
31990
32249
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
31991
32250
|
}
|
|
31992
|
-
if (BRAND_JSON_PATH &&
|
|
32251
|
+
if (BRAND_JSON_PATH && existsSync52(BRAND_JSON_PATH)) {
|
|
31993
32252
|
try {
|
|
31994
|
-
const parsed = JSON.parse(
|
|
32253
|
+
const parsed = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
31995
32254
|
BRAND = { ...BRAND, ...parsed };
|
|
31996
32255
|
} catch (err) {
|
|
31997
32256
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -32023,11 +32282,11 @@ var brandLoginOpts = {
|
|
|
32023
32282
|
appleTouchIconPath: brandAppIcon512Path,
|
|
32024
32283
|
themeColor: BRAND.defaultColors?.primary
|
|
32025
32284
|
};
|
|
32026
|
-
var ALIAS_DOMAINS_PATH =
|
|
32285
|
+
var ALIAS_DOMAINS_PATH = join56(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
32027
32286
|
function loadAliasDomains() {
|
|
32028
32287
|
try {
|
|
32029
|
-
if (!
|
|
32030
|
-
const parsed = JSON.parse(
|
|
32288
|
+
if (!existsSync52(ALIAS_DOMAINS_PATH)) return null;
|
|
32289
|
+
const parsed = JSON.parse(readFileSync48(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
32031
32290
|
if (!Array.isArray(parsed)) {
|
|
32032
32291
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
32033
32292
|
return null;
|
|
@@ -32118,7 +32377,7 @@ var waGateway = new WaGateway({
|
|
|
32118
32377
|
sendToParty: (accountId, phone, text) => sendWhatsAppTextForRelay(accountId, phone, text),
|
|
32119
32378
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
32120
32379
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
32121
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
32380
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve45(process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
32122
32381
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
32123
32382
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
32124
32383
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -32133,7 +32392,7 @@ var waGateway = new WaGateway({
|
|
|
32133
32392
|
caption,
|
|
32134
32393
|
accountId,
|
|
32135
32394
|
maxyAccountId,
|
|
32136
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
32395
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, ".."))
|
|
32137
32396
|
});
|
|
32138
32397
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
32139
32398
|
},
|
|
@@ -32300,7 +32559,7 @@ function runDuplicateSenderAudit() {
|
|
|
32300
32559
|
try {
|
|
32301
32560
|
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
32302
32561
|
if (!configDir) return;
|
|
32303
|
-
const projectsRoot =
|
|
32562
|
+
const projectsRoot = join56(configDir, "projects");
|
|
32304
32563
|
const records = [];
|
|
32305
32564
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
32306
32565
|
if (isSubagent || archived) continue;
|
|
@@ -32441,42 +32700,22 @@ var telegramGateway = new TelegramGateway({
|
|
|
32441
32700
|
// Task 2353 — no turn-instruction resolver on this door. See the dep comment in
|
|
32442
32701
|
// telegram-gateway.ts: the account file governs the relay turn, which only the
|
|
32443
32702
|
// WhatsApp door raises.
|
|
32444
|
-
|
|
32703
|
+
// Task 2368 — the pointer keys on the `<botId>:<senderId>` channel key, which
|
|
32704
|
+
// is what the classification sidecar records (the manager reads
|
|
32705
|
+
// telegramChannel.senderId) and therefore what session-reseat.ts writes.
|
|
32706
|
+
resolveSessionOverride: (accountId, channelKey) => resolveChannelOverride(accountId, "telegram", channelKey),
|
|
32445
32707
|
gatewayUrl: telegramGatewayUrl(),
|
|
32446
32708
|
serverPath: telegramServerPath(),
|
|
32447
|
-
|
|
32448
|
-
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32455
|
-
|
|
32456
|
-
|
|
32457
|
-
});
|
|
32458
|
-
const result = await managerRcSpawn(req);
|
|
32459
|
-
if ("error" in result) {
|
|
32460
|
-
console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
|
|
32461
|
-
return;
|
|
32462
|
-
}
|
|
32463
|
-
if (role !== "admin") return;
|
|
32464
|
-
const prior = telegramFileFollowers.get(senderId);
|
|
32465
|
-
if (prior) prior.abort();
|
|
32466
|
-
let ac;
|
|
32467
|
-
ac = startTelegramNativeFileFollower({
|
|
32468
|
-
sessionId: req.sessionId,
|
|
32469
|
-
senderId,
|
|
32470
|
-
accountId,
|
|
32471
|
-
chatId,
|
|
32472
|
-
// Identity-guarded eviction: only delete if this controller is still the
|
|
32473
|
-
// current one, so a stale self-close never removes a live follower.
|
|
32474
|
-
onClose: () => {
|
|
32475
|
-
if (telegramFileFollowers.get(senderId) === ac) telegramFileFollowers.delete(senderId);
|
|
32476
|
-
}
|
|
32477
|
-
});
|
|
32478
|
-
telegramFileFollowers.set(senderId, ac);
|
|
32479
|
-
}
|
|
32709
|
+
// Task 2368 — the role branch lives in its own module so it has a test that
|
|
32710
|
+
// needs no live manager. An admin bot keeps /rc-spawn; a public bot goes to
|
|
32711
|
+
// /public-spawn, the only door that accepts an agent slug.
|
|
32712
|
+
ensureChannelSession: buildTelegramEnsureSession({
|
|
32713
|
+
managerRcSpawn,
|
|
32714
|
+
managerSpawn,
|
|
32715
|
+
resolveSessionOverride: (accountId, channelKey) => resolveChannelOverride(accountId, "telegram", channelKey),
|
|
32716
|
+
startFileFollower: startTelegramNativeFileFollower,
|
|
32717
|
+
followers: telegramFileFollowers
|
|
32718
|
+
})
|
|
32480
32719
|
});
|
|
32481
32720
|
setTelegramGateway(telegramGateway);
|
|
32482
32721
|
app76.route("/", telegramGateway.routes());
|
|
@@ -32518,24 +32757,29 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
32518
32757
|
// effective-account.ts for why `resolveAccount()` was the wrong side to move,
|
|
32519
32758
|
// and why the real dependencies are bound there rather than assembled here.
|
|
32520
32759
|
effectiveAccountFor: resolveScheduledEffectiveAccountForInstall,
|
|
32521
|
-
// Task
|
|
32522
|
-
//
|
|
32760
|
+
// Task 2368 — which BOT claims this chat id, across every account. Reads the
|
|
32761
|
+
// same per-bot split, through the same readChannelAdmins call, that
|
|
32523
32762
|
// validateAgentDestination uses (agent-dispatch-rule), so the routine picker,
|
|
32524
|
-
// the save and this dispatch cannot disagree. Two claimants is reported to
|
|
32525
|
-
//
|
|
32763
|
+
// the save and this dispatch cannot disagree. Two claimants is reported to the
|
|
32764
|
+
// route and refused there, never picked between — and two bots in ONE account
|
|
32765
|
+
// is as ambiguous as two accounts, because the dispatch has no basis to choose
|
|
32766
|
+
// which bot the person hears from.
|
|
32526
32767
|
telegramAccountForDestination: (destination) => {
|
|
32527
32768
|
const asNumber = Number(destination);
|
|
32528
32769
|
if (!Number.isInteger(asNumber)) return { kind: "none" };
|
|
32529
|
-
const
|
|
32530
|
-
|
|
32531
|
-
|
|
32532
|
-
|
|
32533
|
-
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
|
|
32537
|
-
|
|
32538
|
-
}
|
|
32770
|
+
const hits = [];
|
|
32771
|
+
for (const a of listValidAccounts()) {
|
|
32772
|
+
const entries2 = listBotEntries(a.config.telegram);
|
|
32773
|
+
for (const bot of readChannelAdmins(a.accountDir, "telegram").byBot) {
|
|
32774
|
+
if (!bot.adminUsers.includes(asNumber)) continue;
|
|
32775
|
+
const entry = entries2.find((e) => e.id === bot.botId);
|
|
32776
|
+
if (!entry) continue;
|
|
32777
|
+
hits.push({ accountId: a.accountId, botId: bot.botId, botToken: entry.token });
|
|
32778
|
+
}
|
|
32779
|
+
}
|
|
32780
|
+
if (hits.length === 0) return { kind: "none" };
|
|
32781
|
+
if (hits.length > 1) return { kind: "ambiguous", count: hits.length };
|
|
32782
|
+
return { kind: "one", ...hits[0] };
|
|
32539
32783
|
}
|
|
32540
32784
|
});
|
|
32541
32785
|
app76.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
@@ -32908,14 +33152,14 @@ app76.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
32908
33152
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
32909
33153
|
return c.text("Forbidden", 403);
|
|
32910
33154
|
}
|
|
32911
|
-
if (!
|
|
33155
|
+
if (!existsSync52(filePath)) {
|
|
32912
33156
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
32913
33157
|
return c.text("Not found", 404);
|
|
32914
33158
|
}
|
|
32915
33159
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
32916
33160
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
32917
33161
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
32918
|
-
const body =
|
|
33162
|
+
const body = readFileSync48(filePath);
|
|
32919
33163
|
return c.body(body, 200, {
|
|
32920
33164
|
"Content-Type": contentType,
|
|
32921
33165
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -32938,14 +33182,14 @@ app76.get("/generated/:filename", (c) => {
|
|
|
32938
33182
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
32939
33183
|
return c.text("Forbidden", 403);
|
|
32940
33184
|
}
|
|
32941
|
-
if (!
|
|
33185
|
+
if (!existsSync52(filePath)) {
|
|
32942
33186
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
32943
33187
|
return c.text("Not found", 404);
|
|
32944
33188
|
}
|
|
32945
33189
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
32946
33190
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
32947
33191
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
32948
|
-
const body =
|
|
33192
|
+
const body = readFileSync48(filePath);
|
|
32949
33193
|
return c.body(body, 200, {
|
|
32950
33194
|
"Content-Type": contentType,
|
|
32951
33195
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -32958,9 +33202,9 @@ app76.route("/v", visitor_consent_default);
|
|
|
32958
33202
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
32959
33203
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
32960
33204
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
32961
|
-
if (BRAND_JSON_PATH &&
|
|
33205
|
+
if (BRAND_JSON_PATH && existsSync52(BRAND_JSON_PATH)) {
|
|
32962
33206
|
try {
|
|
32963
|
-
const fullBrand = JSON.parse(
|
|
33207
|
+
const fullBrand = JSON.parse(readFileSync48(BRAND_JSON_PATH, "utf-8"));
|
|
32964
33208
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
32965
33209
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
32966
33210
|
} catch {
|
|
@@ -32987,11 +33231,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
32987
33231
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
32988
33232
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
32989
33233
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
32990
|
-
(p) =>
|
|
33234
|
+
(p) => existsSync52(resolve45(process.cwd(), "public", p.replace(/^\//, "")))
|
|
32991
33235
|
);
|
|
32992
33236
|
var SW_SOURCE = (() => {
|
|
32993
33237
|
try {
|
|
32994
|
-
return
|
|
33238
|
+
return readFileSync48(resolve45(process.cwd(), "public", "sw.js"), "utf-8");
|
|
32995
33239
|
} catch {
|
|
32996
33240
|
return null;
|
|
32997
33241
|
}
|
|
@@ -32999,9 +33243,9 @@ var SW_SOURCE = (() => {
|
|
|
32999
33243
|
function readInstalledVersion() {
|
|
33000
33244
|
try {
|
|
33001
33245
|
if (!PLATFORM_ROOT6) return "unknown";
|
|
33002
|
-
const versionFile =
|
|
33003
|
-
if (!
|
|
33004
|
-
const content =
|
|
33246
|
+
const versionFile = join56(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
|
|
33247
|
+
if (!existsSync52(versionFile)) return "unknown";
|
|
33248
|
+
const content = readFileSync48(versionFile, "utf-8").trim();
|
|
33005
33249
|
return content || "unknown";
|
|
33006
33250
|
} catch {
|
|
33007
33251
|
return "unknown";
|
|
@@ -33012,7 +33256,7 @@ var versionScript = `<script>window.__MAXY_VERSION__=${JSON.stringify(INSTALLED_
|
|
|
33012
33256
|
function cachedHtml(file) {
|
|
33013
33257
|
let html = htmlCache.get(file);
|
|
33014
33258
|
if (!html) {
|
|
33015
|
-
html =
|
|
33259
|
+
html = readFileSync48(resolve45(process.cwd(), "public", file), "utf-8");
|
|
33016
33260
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
33017
33261
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
33018
33262
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -33029,14 +33273,14 @@ ${clientErrorReporterScript}
|
|
|
33029
33273
|
return html;
|
|
33030
33274
|
}
|
|
33031
33275
|
function loadBrandingCache(agentSlug) {
|
|
33032
|
-
const configDir =
|
|
33276
|
+
const configDir = join56(homedir4(), BRAND.configDir);
|
|
33033
33277
|
try {
|
|
33034
33278
|
const verdict = resolvePublicAddressFromDisk(agentSlug);
|
|
33035
33279
|
const accountId = verdict.kind === "served" ? verdict.owner.accountId : getDefaultAccountId();
|
|
33036
33280
|
if (!accountId) return null;
|
|
33037
|
-
const cachePath =
|
|
33038
|
-
if (!
|
|
33039
|
-
return JSON.parse(
|
|
33281
|
+
const cachePath = join56(configDir, "branding-cache", accountId, `${agentSlug}.json`);
|
|
33282
|
+
if (!existsSync52(cachePath)) return null;
|
|
33283
|
+
return JSON.parse(readFileSync48(cachePath, "utf-8"));
|
|
33040
33284
|
} catch {
|
|
33041
33285
|
return null;
|
|
33042
33286
|
}
|
|
@@ -33131,7 +33375,7 @@ app76.use("/vnc-popout.html", logViewerFetch);
|
|
|
33131
33375
|
app76.get("/vnc-popout.html", (c) => {
|
|
33132
33376
|
let html = htmlCache.get("vnc-popout.html");
|
|
33133
33377
|
if (!html) {
|
|
33134
|
-
html =
|
|
33378
|
+
html = readFileSync48(resolve45(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
33135
33379
|
const name = escapeHtml(BRAND.productName);
|
|
33136
33380
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
33137
33381
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -33284,10 +33528,10 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
33284
33528
|
var httpServer = serve({ fetch: app76.fetch, port, hostname });
|
|
33285
33529
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
33286
33530
|
{
|
|
33287
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33531
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33288
33532
|
const reconcileScript = resolve45(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
33289
33533
|
const runReconcile = (ctx) => {
|
|
33290
|
-
if (!
|
|
33534
|
+
if (!existsSync52(reconcileScript)) return;
|
|
33291
33535
|
try {
|
|
33292
33536
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
33293
33537
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -33307,7 +33551,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33307
33551
|
detached: true,
|
|
33308
33552
|
run: runReconcile
|
|
33309
33553
|
});
|
|
33310
|
-
const reconcileStatePath =
|
|
33554
|
+
const reconcileStatePath = join56(MAXY_DIR, "booking-reconcile-state.json");
|
|
33311
33555
|
registerLoop({
|
|
33312
33556
|
name: "calendar-reconcile-liveness",
|
|
33313
33557
|
intervalMs: 6e5,
|
|
@@ -33325,7 +33569,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33325
33569
|
}
|
|
33326
33570
|
{
|
|
33327
33571
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
33328
|
-
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33572
|
+
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33329
33573
|
const dreamSweepScript = resolve45(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
33330
33574
|
registerLoop({
|
|
33331
33575
|
name: "dream-cycle-sweep",
|
|
@@ -33333,7 +33577,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33333
33577
|
firstRunDelayMs: 3e4,
|
|
33334
33578
|
detached: true,
|
|
33335
33579
|
run: (ctx) => {
|
|
33336
|
-
if (!
|
|
33580
|
+
if (!existsSync52(dreamSweepScript)) return;
|
|
33337
33581
|
try {
|
|
33338
33582
|
const child = spawn3(process.execPath, [dreamSweepScript], {
|
|
33339
33583
|
env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
|
|
@@ -33349,11 +33593,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33349
33593
|
});
|
|
33350
33594
|
}
|
|
33351
33595
|
{
|
|
33352
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33596
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33353
33597
|
const outlookScript = resolve45(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
33354
33598
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
33355
33599
|
const runOutlookComplete = (ctx) => {
|
|
33356
|
-
if (!
|
|
33600
|
+
if (!existsSync52(outlookScript)) return;
|
|
33357
33601
|
try {
|
|
33358
33602
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
33359
33603
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -33375,19 +33619,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33375
33619
|
});
|
|
33376
33620
|
}
|
|
33377
33621
|
{
|
|
33378
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33622
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33379
33623
|
const strandedAccountsDir = resolve45(auditRoot, "..", "data/accounts");
|
|
33380
33624
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
33381
33625
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
33382
33626
|
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;
|
|
33383
33627
|
const runStrandedAudit = () => {
|
|
33384
33628
|
try {
|
|
33385
|
-
if (!
|
|
33629
|
+
if (!existsSync52(strandedAccountsDir)) return;
|
|
33386
33630
|
const now = Date.now();
|
|
33387
33631
|
for (const name of readdirSync30(strandedAccountsDir)) {
|
|
33388
33632
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
33389
33633
|
const pendingPath2 = resolve45(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
33390
|
-
if (!
|
|
33634
|
+
if (!existsSync52(pendingPath2)) continue;
|
|
33391
33635
|
const ageMs2 = now - statSync27(pendingPath2).mtimeMs;
|
|
33392
33636
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
33393
33637
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
|
|
@@ -33405,7 +33649,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33405
33649
|
});
|
|
33406
33650
|
}
|
|
33407
33651
|
{
|
|
33408
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33652
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33409
33653
|
const googleAccountsDir = resolve45(googleRoot, "..", "data/accounts");
|
|
33410
33654
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
33411
33655
|
const runGooglePendingAuditSafe = () => {
|
|
@@ -33424,7 +33668,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33424
33668
|
const googleAuditScript = resolve45(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
33425
33669
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
33426
33670
|
const runGoogleAccountAudit = (ctx) => {
|
|
33427
|
-
if (!
|
|
33671
|
+
if (!existsSync52(googleAuditScript)) return;
|
|
33428
33672
|
try {
|
|
33429
33673
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
33430
33674
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -33449,7 +33693,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33449
33693
|
});
|
|
33450
33694
|
}
|
|
33451
33695
|
{
|
|
33452
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33696
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33453
33697
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
33454
33698
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
33455
33699
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -33481,15 +33725,42 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33481
33725
|
firstRunDelayMs: 5e4,
|
|
33482
33726
|
run: runDataPortalAuditSafe
|
|
33483
33727
|
});
|
|
33728
|
+
for (const o of migrateTelegramTwoSlot({
|
|
33729
|
+
accounts: listValidAccounts().map((a) => ({ accountId: a.accountId, accountDir: a.accountDir })),
|
|
33730
|
+
secretPathFor: secretPath,
|
|
33731
|
+
legacySecretPathFor: (botType, accountId) => resolve45(
|
|
33732
|
+
homedir4(),
|
|
33733
|
+
configDirName(),
|
|
33734
|
+
`${botType === "admin" ? ".telegram-admin-webhook-secret" : ".telegram-webhook-secret"}.${accountId}`
|
|
33735
|
+
),
|
|
33736
|
+
// Counted here and nowhere else: the session-key re-key strands these and no
|
|
33737
|
+
// later event reveals it, because the new key never resolves to the old JSONL.
|
|
33738
|
+
countOrphanSessions: countTelegramSessionsForAccount
|
|
33739
|
+
})) {
|
|
33740
|
+
console.error(
|
|
33741
|
+
`[telegram-audit] op=migrate-session-orphans accountId=${o.accountId} count=${o.orphanSessions} converted=${o.converted}${o.skippedAdmin ? ` skippedAdmin=${o.skippedAdmin}` : ""}${o.skippedPublic ? ` skippedPublic=${o.skippedPublic}` : ""}`
|
|
33742
|
+
);
|
|
33743
|
+
}
|
|
33484
33744
|
const runTelegramAudit = async () => {
|
|
33485
|
-
const accounts = listValidAccounts().map((a) => ({
|
|
33486
|
-
|
|
33745
|
+
const accounts = listValidAccounts().map((a) => ({
|
|
33746
|
+
accountId: a.accountId,
|
|
33747
|
+
accountDir: a.accountDir,
|
|
33748
|
+
telegram: a.config.telegram
|
|
33749
|
+
}));
|
|
33750
|
+
const configured = accounts.some((a) => listBotEntries(a.telegram).length > 0);
|
|
33487
33751
|
if (!configured) return;
|
|
33488
|
-
const
|
|
33752
|
+
const lastByBot = newestTelegramSessionByBot();
|
|
33753
|
+
let secretFilesOnDisk = [];
|
|
33754
|
+
try {
|
|
33755
|
+
secretFilesOnDisk = readdirSync30(telegramSecretsDir());
|
|
33756
|
+
} catch {
|
|
33757
|
+
}
|
|
33489
33758
|
const lines = buildTelegramAuditLines({
|
|
33490
33759
|
accounts,
|
|
33491
|
-
secretFileExists: (
|
|
33492
|
-
|
|
33760
|
+
secretFileExists: (botId) => existsSync52(secretPath(botId)),
|
|
33761
|
+
agentActive: isActiveAgentSlug,
|
|
33762
|
+
secretFilesOnDisk,
|
|
33763
|
+
lastInboundByBot: (botId) => lastByBot.get(botId) ?? null,
|
|
33493
33764
|
now: Date.now()
|
|
33494
33765
|
});
|
|
33495
33766
|
for (const line of lines) console.error(line);
|
|
@@ -33565,7 +33836,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33565
33836
|
});
|
|
33566
33837
|
}
|
|
33567
33838
|
{
|
|
33568
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33839
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33569
33840
|
const publishScript = resolve45(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
33570
33841
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
33571
33842
|
const bookingAccounts = () => {
|
|
@@ -33577,7 +33848,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33577
33848
|
}
|
|
33578
33849
|
};
|
|
33579
33850
|
const spawnPublish = (account, ctx) => {
|
|
33580
|
-
if (!
|
|
33851
|
+
if (!existsSync52(publishScript)) return;
|
|
33581
33852
|
try {
|
|
33582
33853
|
const child = spawn3(process.execPath, [publishScript], {
|
|
33583
33854
|
env: {
|
|
@@ -33599,8 +33870,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33599
33870
|
const availPath = resolve45(account.accountDir, "calendar-availability.json");
|
|
33600
33871
|
let hasBookingSite = false;
|
|
33601
33872
|
try {
|
|
33602
|
-
if (
|
|
33603
|
-
const cfg = JSON.parse(
|
|
33873
|
+
if (existsSync52(availPath)) {
|
|
33874
|
+
const cfg = JSON.parse(readFileSync48(availPath, "utf-8"));
|
|
33604
33875
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
33605
33876
|
}
|
|
33606
33877
|
} catch {
|
|
@@ -33608,9 +33879,9 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33608
33879
|
if (!hasBookingSite) return;
|
|
33609
33880
|
const statePath = resolve45(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
33610
33881
|
let lastSuccessAt = null;
|
|
33611
|
-
if (
|
|
33882
|
+
if (existsSync52(statePath)) {
|
|
33612
33883
|
try {
|
|
33613
|
-
const rec = JSON.parse(
|
|
33884
|
+
const rec = JSON.parse(readFileSync48(statePath, "utf-8"));
|
|
33614
33885
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
33615
33886
|
} catch {
|
|
33616
33887
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -33642,12 +33913,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
33642
33913
|
startTimeEntryCensus(() => getSession());
|
|
33643
33914
|
startLedgerCensus(() => getSession());
|
|
33644
33915
|
{
|
|
33645
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
33916
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..");
|
|
33646
33917
|
const auditScript = resolve45(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
33647
33918
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
33648
33919
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
33649
33920
|
const runConnectorAudit = (ctx) => {
|
|
33650
|
-
if (!
|
|
33921
|
+
if (!existsSync52(auditScript)) return;
|
|
33651
33922
|
try {
|
|
33652
33923
|
const child = spawn3(process.execPath, [auditScript], {
|
|
33653
33924
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -33767,11 +34038,11 @@ try {
|
|
|
33767
34038
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
33768
34039
|
async function runAdminUserReconcileTick() {
|
|
33769
34040
|
try {
|
|
33770
|
-
if (!
|
|
34041
|
+
if (!existsSync52(USERS_FILE)) {
|
|
33771
34042
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
33772
34043
|
return;
|
|
33773
34044
|
}
|
|
33774
|
-
const usersRaw =
|
|
34045
|
+
const usersRaw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
33775
34046
|
if (!usersRaw) {
|
|
33776
34047
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
33777
34048
|
return;
|
|
@@ -33853,8 +34124,8 @@ var runIngestAuditTick = () => {
|
|
|
33853
34124
|
},
|
|
33854
34125
|
agentExists: (accountId, slug) => {
|
|
33855
34126
|
if (!house) return false;
|
|
33856
|
-
const dir = isHouseAccountKey(house.accountId, accountId) ? house.accountDir :
|
|
33857
|
-
return
|
|
34127
|
+
const dir = isHouseAccountKey(house.accountId, accountId) ? house.accountDir : join56(ACCOUNTS_DIR, accountId);
|
|
34128
|
+
return existsSync52(join56(dir, "agents", slug, "config.json"));
|
|
33858
34129
|
}
|
|
33859
34130
|
});
|
|
33860
34131
|
} catch (err) {
|
|
@@ -33902,8 +34173,8 @@ registerLoop({
|
|
|
33902
34173
|
runPublicAddressCollisionAudit({
|
|
33903
34174
|
houseAccountId: () => resolveAccount()?.accountId ?? null,
|
|
33904
34175
|
activeSlugs: () => listValidAccounts().map((a) => {
|
|
33905
|
-
const agentsRoot =
|
|
33906
|
-
if (!
|
|
34176
|
+
const agentsRoot = join56(a.accountDir, "agents");
|
|
34177
|
+
if (!existsSync52(agentsRoot)) return { accountId: a.accountId, slugs: [] };
|
|
33907
34178
|
const slugs = readdirSync30(agentsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).filter((slug) => isActiveAgentSlug(a.accountDir, slug));
|
|
33908
34179
|
return { accountId: a.accountId, slugs };
|
|
33909
34180
|
})
|
|
@@ -33917,8 +34188,8 @@ registerLoop({
|
|
|
33917
34188
|
});
|
|
33918
34189
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
33919
34190
|
function countUsersRows() {
|
|
33920
|
-
if (!
|
|
33921
|
-
const raw =
|
|
34191
|
+
if (!existsSync52(USERS_FILE)) return 0;
|
|
34192
|
+
const raw = readFileSync48(USERS_FILE, "utf-8").trim();
|
|
33922
34193
|
if (!raw) return 0;
|
|
33923
34194
|
const users = JSON.parse(raw);
|
|
33924
34195
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -34054,7 +34325,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
34054
34325
|
}
|
|
34055
34326
|
init({
|
|
34056
34327
|
configDir: configDirForWhatsApp,
|
|
34057
|
-
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ??
|
|
34328
|
+
platformRoot: resolve45(process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, "..")),
|
|
34058
34329
|
accountConfig: bootAccountConfig,
|
|
34059
34330
|
// Task 2074 — enrich with the sub-account name and fan out to every open
|
|
34060
34331
|
// admin SSE stream. Fire-and-forget: the manager's persist path must not
|