@rubytech/create-realagent 1.0.435 → 1.0.437
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/uninstall.js +44 -26
- package/package.json +1 -1
- package/payload/maxy/server.js +295 -84
- package/payload/platform/neo4j/schema.cypher +6 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +379 -10
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/onboarding/skill.md +1 -1
- package/payload/platform/plugins/admin/skills/plugin-management/skill.md +16 -58
- package/payload/platform/plugins/anthropic/PLUGIN.md +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/index.js +34 -16
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +2 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +34 -9
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts +2 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js +13 -7
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts +2 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +2 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +10 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js +8 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js.map +1 -1
- package/payload/platform/plugins/docs/references/contacts-guide.md +7 -5
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.d.ts +1 -1
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +30 -4
- package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js +31 -3
- package/payload/platform/plugins/email/mcp/dist/scripts/email-fetch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +30 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
package/payload/maxy/server.js
CHANGED
|
@@ -2845,7 +2845,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2845
2845
|
};
|
|
2846
2846
|
|
|
2847
2847
|
// server/index.ts
|
|
2848
|
-
import { readFileSync as
|
|
2848
|
+
import { readFileSync as readFileSync18, existsSync as existsSync18, watchFile } from "fs";
|
|
2849
2849
|
import { resolve as resolve15, join as join5 } from "path";
|
|
2850
2850
|
import { homedir as homedir2 } from "os";
|
|
2851
2851
|
|
|
@@ -3128,7 +3128,7 @@ async function GET() {
|
|
|
3128
3128
|
|
|
3129
3129
|
// app/lib/claude-agent.ts
|
|
3130
3130
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3131
|
-
import { spawn } from "child_process";
|
|
3131
|
+
import { spawn as spawn2 } from "child_process";
|
|
3132
3132
|
import { resolve as resolve4 } from "path";
|
|
3133
3133
|
import { readFileSync as readFileSync5, readdirSync, existsSync as existsSync5, mkdirSync as mkdirSync2, createWriteStream, statSync as statSync2, unlinkSync } from "fs";
|
|
3134
3134
|
|
|
@@ -3267,6 +3267,7 @@ function buildX11Env(chromiumWrapperPath) {
|
|
|
3267
3267
|
// app/lib/neo4j-store.ts
|
|
3268
3268
|
import neo4j from "neo4j-driver";
|
|
3269
3269
|
import { randomUUID } from "crypto";
|
|
3270
|
+
import { spawn } from "child_process";
|
|
3270
3271
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3271
3272
|
import { resolve as resolve3 } from "path";
|
|
3272
3273
|
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
|
|
@@ -3620,47 +3621,162 @@ async function deleteConversation(conversationId) {
|
|
|
3620
3621
|
}
|
|
3621
3622
|
}
|
|
3622
3623
|
var GENERIC_MESSAGE = /^(h(i|ello|ey|owdy)|yo|sup|thanks|thank you|ok|okay|yes|no|good\s*(morning|afternoon|evening|night)|greetings|what'?s\s*up)[\s!?.,:;]*$/i;
|
|
3623
|
-
|
|
3624
|
+
var SESSION_LABEL_MSG_CAP = 500;
|
|
3625
|
+
var SESSION_LABEL_TIMEOUT_MS = 15e3;
|
|
3626
|
+
var SESSION_LABEL_MODEL = "claude-haiku-4-5-20251001";
|
|
3627
|
+
var SESSION_LABEL_MAX_WORDS = 6;
|
|
3628
|
+
var SESSION_LABEL_MAX_ATTEMPTS = 3;
|
|
3629
|
+
var SESSION_LABEL_MAX_STDERR = 2048;
|
|
3630
|
+
function isMessageUseful(message) {
|
|
3624
3631
|
const trimmed = message.trim();
|
|
3625
|
-
if (trimmed.length < 4) return
|
|
3626
|
-
if (
|
|
3627
|
-
if (
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
+
if (trimmed.length < 4) return false;
|
|
3633
|
+
if (trimmed.startsWith('{"')) return false;
|
|
3634
|
+
if (GENERIC_MESSAGE.test(trimmed)) return false;
|
|
3635
|
+
if (trimmed.startsWith("[New session.")) return false;
|
|
3636
|
+
return true;
|
|
3637
|
+
}
|
|
3638
|
+
var labelAccumulator = /* @__PURE__ */ new Map();
|
|
3639
|
+
var _spawnOverride = null;
|
|
3640
|
+
var SESSION_LABEL_SYSTEM = `You are a session labeler. Given the opening messages of a conversation with an AI assistant, produce a concise topic label.
|
|
3641
|
+
|
|
3642
|
+
Rules:
|
|
3643
|
+
- Exactly 3 to 6 words
|
|
3644
|
+
- Summarize the user's intent, do not copy verbatim
|
|
3645
|
+
- Capitalize the first word only (sentence case)
|
|
3646
|
+
- No punctuation, no quotes
|
|
3647
|
+
- If the messages are too vague or meaningless to summarize, respond with exactly: SKIP`;
|
|
3648
|
+
async function generateSessionLabel(messages) {
|
|
3649
|
+
const cappedMessages = messages.map((m) => m.slice(0, SESSION_LABEL_MSG_CAP));
|
|
3650
|
+
const userContent = cappedMessages.map((m, i) => `Message ${i + 1}: ${m}`).join("\n");
|
|
3651
|
+
const prompt = `${SESSION_LABEL_SYSTEM}
|
|
3652
|
+
|
|
3653
|
+
${userContent}`;
|
|
3654
|
+
const args = [
|
|
3655
|
+
"--print",
|
|
3656
|
+
"--model",
|
|
3657
|
+
SESSION_LABEL_MODEL,
|
|
3658
|
+
"--max-turns",
|
|
3659
|
+
"1",
|
|
3660
|
+
"--permission-mode",
|
|
3661
|
+
"dontAsk",
|
|
3662
|
+
prompt
|
|
3663
|
+
];
|
|
3664
|
+
return new Promise((resolve16) => {
|
|
3665
|
+
let stdout = "";
|
|
3666
|
+
let stderr = "";
|
|
3667
|
+
const spawnFn = _spawnOverride ?? spawn;
|
|
3668
|
+
const proc = spawnFn("claude", args, {
|
|
3669
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3670
|
+
});
|
|
3671
|
+
proc.stdout?.on("data", (chunk) => {
|
|
3672
|
+
stdout += chunk.toString("utf-8");
|
|
3673
|
+
});
|
|
3674
|
+
proc.stderr?.on("data", (chunk) => {
|
|
3675
|
+
if (stderr.length < SESSION_LABEL_MAX_STDERR) {
|
|
3676
|
+
stderr += chunk.toString("utf-8").slice(0, SESSION_LABEL_MAX_STDERR - stderr.length);
|
|
3677
|
+
}
|
|
3678
|
+
});
|
|
3679
|
+
const timer = setTimeout(() => {
|
|
3680
|
+
proc.kill("SIGTERM");
|
|
3681
|
+
console.error("[persist] autoLabel: haiku subprocess timed out");
|
|
3682
|
+
resolve16(null);
|
|
3683
|
+
}, SESSION_LABEL_TIMEOUT_MS);
|
|
3684
|
+
proc.on("error", (err) => {
|
|
3685
|
+
clearTimeout(timer);
|
|
3686
|
+
console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
|
|
3687
|
+
resolve16(null);
|
|
3688
|
+
});
|
|
3689
|
+
proc.on("close", (code) => {
|
|
3690
|
+
clearTimeout(timer);
|
|
3691
|
+
if (code !== 0) {
|
|
3692
|
+
console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
|
|
3693
|
+
resolve16(null);
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
const text = stdout.trim();
|
|
3697
|
+
if (!text) {
|
|
3698
|
+
console.error("[persist] autoLabel: haiku returned empty response");
|
|
3699
|
+
resolve16(null);
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
if (text === "SKIP") {
|
|
3703
|
+
console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
|
|
3704
|
+
resolve16(null);
|
|
3705
|
+
return;
|
|
3706
|
+
}
|
|
3707
|
+
const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
|
|
3708
|
+
const label = words.join(" ");
|
|
3709
|
+
console.error(`[persist] autoLabel: haiku response="${label}"`);
|
|
3710
|
+
resolve16(label);
|
|
3711
|
+
});
|
|
3712
|
+
});
|
|
3632
3713
|
}
|
|
3633
3714
|
async function autoLabelSession(conversationId, userMessage) {
|
|
3634
|
-
|
|
3635
|
-
if (!
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
} catch (err) {
|
|
3640
|
-
console.error(`[persist] Conversation embedding failed, labelling without: ${err instanceof Error ? err.message : String(err)}`);
|
|
3715
|
+
if (!conversationId) return;
|
|
3716
|
+
if (!isMessageUseful(userMessage)) {
|
|
3717
|
+
const reason = userMessage.trim().startsWith('{"') ? "JSON envelope" : userMessage.trim().length < 4 ? "too short" : GENERIC_MESSAGE.test(userMessage.trim()) ? "greeting" : "directive";
|
|
3718
|
+
console.error(`[persist] autoLabel: skipped ${conversationId.slice(0, 8)}\u2026 \u2014 ${reason}`);
|
|
3719
|
+
return;
|
|
3641
3720
|
}
|
|
3642
|
-
|
|
3721
|
+
let entry = labelAccumulator.get(conversationId);
|
|
3722
|
+
if (!entry) {
|
|
3723
|
+
entry = { messages: [], pending: false, attempts: 0 };
|
|
3724
|
+
labelAccumulator.set(conversationId, entry);
|
|
3725
|
+
}
|
|
3726
|
+
if (entry.attempts >= SESSION_LABEL_MAX_ATTEMPTS) {
|
|
3727
|
+
console.error(`[persist] autoLabel: evicted ${conversationId.slice(0, 8)}\u2026 after ${SESSION_LABEL_MAX_ATTEMPTS} attempts`);
|
|
3728
|
+
labelAccumulator.delete(conversationId);
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3731
|
+
entry.messages.push(userMessage.trim());
|
|
3732
|
+
if (entry.pending) {
|
|
3733
|
+
console.error(`[persist] autoLabel: accumulated for ${conversationId.slice(0, 8)}\u2026 (pending, ${entry.messages.length} msgs)`);
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
entry.pending = true;
|
|
3737
|
+
entry.attempts++;
|
|
3643
3738
|
try {
|
|
3644
|
-
const
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
);
|
|
3657
|
-
|
|
3658
|
-
|
|
3739
|
+
const label = await generateSessionLabel(entry.messages);
|
|
3740
|
+
if (!label) {
|
|
3741
|
+
entry.pending = false;
|
|
3742
|
+
return;
|
|
3743
|
+
}
|
|
3744
|
+
const fullLabel = `${label} \xB7 ${conversationId.slice(0, 8)}`;
|
|
3745
|
+
let embedding = null;
|
|
3746
|
+
try {
|
|
3747
|
+
embedding = await embed(fullLabel);
|
|
3748
|
+
} catch (err) {
|
|
3749
|
+
console.error(`[persist] Conversation embedding failed, labelling without: ${err instanceof Error ? err.message : String(err)}`);
|
|
3750
|
+
}
|
|
3751
|
+
const session = getSession();
|
|
3752
|
+
try {
|
|
3753
|
+
const result = await session.run(
|
|
3754
|
+
`MATCH (c:Conversation {conversationId: $conversationId})
|
|
3755
|
+
WHERE c.name IS NULL
|
|
3756
|
+
WITH c
|
|
3757
|
+
OPTIONAL MATCH (m:Message)-[:PART_OF]->(c)
|
|
3758
|
+
WHERE m.role = 'user'
|
|
3759
|
+
WITH c, count(m) AS userCount
|
|
3760
|
+
WHERE userCount <= 3
|
|
3761
|
+
SET c.name = $label, c.updatedAt = datetime()
|
|
3762
|
+
${embedding ? ", c.embedding = $embedding" : ""}
|
|
3763
|
+
RETURN c.name AS name`,
|
|
3764
|
+
{ conversationId, label: fullLabel, ...embedding ? { embedding } : {} }
|
|
3765
|
+
);
|
|
3766
|
+
if (result.records.length > 0) {
|
|
3767
|
+
console.error(`[persist] Auto-labeled session ${conversationId.slice(0, 8)}\u2026: "${fullLabel}"${embedding ? " (embedded)" : ""}`);
|
|
3768
|
+
labelAccumulator.delete(conversationId);
|
|
3769
|
+
}
|
|
3770
|
+
} catch (err) {
|
|
3771
|
+
console.error(`[persist] autoLabelSession failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3772
|
+
} finally {
|
|
3773
|
+
await session.close();
|
|
3659
3774
|
}
|
|
3660
3775
|
} catch (err) {
|
|
3661
|
-
console.error(`[persist]
|
|
3776
|
+
console.error(`[persist] autoLabel: unexpected error \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
3662
3777
|
} finally {
|
|
3663
|
-
|
|
3778
|
+
const currentEntry = labelAccumulator.get(conversationId);
|
|
3779
|
+
if (currentEntry) currentEntry.pending = false;
|
|
3664
3780
|
}
|
|
3665
3781
|
}
|
|
3666
3782
|
var INITIAL_CONFIDENCE = 0.5;
|
|
@@ -4552,7 +4668,7 @@ function fetchMcpToolsList(pluginDir) {
|
|
|
4552
4668
|
if (!existsSync5(serverPath)) return Promise.resolve([]);
|
|
4553
4669
|
const startMs = Date.now();
|
|
4554
4670
|
return new Promise((resolvePromise) => {
|
|
4555
|
-
const proc =
|
|
4671
|
+
const proc = spawn2(process.execPath, [serverPath], {
|
|
4556
4672
|
env: {
|
|
4557
4673
|
...process.env,
|
|
4558
4674
|
PLATFORM_ROOT: PLATFORM_ROOT3,
|
|
@@ -5190,7 +5306,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
5190
5306
|
}
|
|
5191
5307
|
const startMs = Date.now();
|
|
5192
5308
|
return new Promise((resolve16) => {
|
|
5193
|
-
const proc =
|
|
5309
|
+
const proc = spawn2(process.execPath, [serverPath], {
|
|
5194
5310
|
env: {
|
|
5195
5311
|
...process.env,
|
|
5196
5312
|
ACCOUNT_ID: accountId,
|
|
@@ -5285,7 +5401,7 @@ async function compactTrimmedMessages(accountId, trimmedMessages) {
|
|
|
5285
5401
|
if (!existsSync5(serverPath)) return false;
|
|
5286
5402
|
const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
|
|
5287
5403
|
return new Promise((resolvePromise) => {
|
|
5288
|
-
const proc =
|
|
5404
|
+
const proc = spawn2(process.execPath, [serverPath], {
|
|
5289
5405
|
env: { ...process.env, ACCOUNT_ID: accountId }
|
|
5290
5406
|
});
|
|
5291
5407
|
let buffer = "";
|
|
@@ -5710,7 +5826,7 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
5710
5826
|
resumeSessionId,
|
|
5711
5827
|
COMPACTION_PROMPT
|
|
5712
5828
|
];
|
|
5713
|
-
const proc =
|
|
5829
|
+
const proc = spawn2("claude", args, {
|
|
5714
5830
|
cwd: accountDir,
|
|
5715
5831
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5716
5832
|
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
|
|
@@ -6318,7 +6434,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6318
6434
|
}
|
|
6319
6435
|
const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
|
|
6320
6436
|
args.push(fullMessage);
|
|
6321
|
-
const proc =
|
|
6437
|
+
const proc = spawn2("claude", args, {
|
|
6322
6438
|
cwd: accountDir,
|
|
6323
6439
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6324
6440
|
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
|
|
@@ -6567,7 +6683,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
6567
6683
|
specialistsDir,
|
|
6568
6684
|
fullMessage
|
|
6569
6685
|
];
|
|
6570
|
-
const proc =
|
|
6686
|
+
const proc = spawn2("claude", args, {
|
|
6571
6687
|
cwd: accountDir,
|
|
6572
6688
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6573
6689
|
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
|
|
@@ -9074,7 +9190,7 @@ async function POST8(req) {
|
|
|
9074
9190
|
}
|
|
9075
9191
|
|
|
9076
9192
|
// app/api/onboarding/claude-auth/route.ts
|
|
9077
|
-
import { spawn as
|
|
9193
|
+
import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
|
|
9078
9194
|
import { openSync, closeSync, writeFileSync as writeFileSync6, writeSync } from "fs";
|
|
9079
9195
|
function checkAuthStatus() {
|
|
9080
9196
|
try {
|
|
@@ -9141,7 +9257,7 @@ async function POST9(req) {
|
|
|
9141
9257
|
const chromiumWrapper = writeChromiumWrapper();
|
|
9142
9258
|
const x11Env = buildX11Env(chromiumWrapper);
|
|
9143
9259
|
const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
|
|
9144
|
-
const claudeProc =
|
|
9260
|
+
const claudeProc = spawn3("claude", ["auth", "login"], {
|
|
9145
9261
|
env: x11Env,
|
|
9146
9262
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9147
9263
|
});
|
|
@@ -9259,6 +9375,86 @@ async function POST11(req) {
|
|
|
9259
9375
|
}
|
|
9260
9376
|
|
|
9261
9377
|
// app/api/admin/chat/route.ts
|
|
9378
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
9379
|
+
import { dirname as dirname2 } from "path";
|
|
9380
|
+
function isComponentDone(parsed) {
|
|
9381
|
+
return typeof parsed === "object" && parsed !== null && parsed._componentDone === true && typeof parsed.component === "string" && typeof parsed.payload === "string";
|
|
9382
|
+
}
|
|
9383
|
+
function transformComponentDone(envelope) {
|
|
9384
|
+
const { component, payload } = envelope;
|
|
9385
|
+
if (component === "document-editor") {
|
|
9386
|
+
try {
|
|
9387
|
+
const inner = JSON.parse(payload);
|
|
9388
|
+
if (typeof inner === "object" && inner !== null) {
|
|
9389
|
+
if (inner.action === "approve" && typeof inner.content === "string") {
|
|
9390
|
+
const path = inner.filePath ?? "unknown";
|
|
9391
|
+
return `[User approved document: ${path}]
|
|
9392
|
+
|
|
9393
|
+
${inner.content}`;
|
|
9394
|
+
}
|
|
9395
|
+
if (inner.type === "comment" && typeof inner.text === "string") {
|
|
9396
|
+
return `[User commented on document: "${inner.text}"]`;
|
|
9397
|
+
}
|
|
9398
|
+
}
|
|
9399
|
+
} catch {
|
|
9400
|
+
}
|
|
9401
|
+
}
|
|
9402
|
+
return payload;
|
|
9403
|
+
}
|
|
9404
|
+
var VOLATILE_STATE_FILE = "/tmp/maxy-agent-create-state.json";
|
|
9405
|
+
function advanceGateIfNeeded(envelope, accountDir, logFn) {
|
|
9406
|
+
if (!existsSync12(VOLATILE_STATE_FILE)) return;
|
|
9407
|
+
const { component, payload } = envelope;
|
|
9408
|
+
let gateFlag = null;
|
|
9409
|
+
let agentSlug = "";
|
|
9410
|
+
if (component === "document-editor") {
|
|
9411
|
+
try {
|
|
9412
|
+
const inner = typeof payload === "string" ? JSON.parse(payload) : payload;
|
|
9413
|
+
if (typeof inner === "object" && inner !== null && typeof inner.filePath === "string") {
|
|
9414
|
+
const match2 = inner.filePath.match(/agents\/([^/]+)\/(SOUL\.md|KNOWLEDGE\.md)$/);
|
|
9415
|
+
if (match2 && match2[1] !== "admin") {
|
|
9416
|
+
agentSlug = match2[1];
|
|
9417
|
+
gateFlag = match2[2] === "SOUL.md" ? "soul" : "knowledge";
|
|
9418
|
+
}
|
|
9419
|
+
}
|
|
9420
|
+
} catch {
|
|
9421
|
+
}
|
|
9422
|
+
} else if (component === "form") {
|
|
9423
|
+
gateFlag = "config";
|
|
9424
|
+
}
|
|
9425
|
+
if (!gateFlag) return;
|
|
9426
|
+
let state;
|
|
9427
|
+
try {
|
|
9428
|
+
state = JSON.parse(readFileSync12(VOLATILE_STATE_FILE, "utf-8"));
|
|
9429
|
+
} catch (err) {
|
|
9430
|
+
logFn(`ERROR \u2014 failed to read state file for advancement: ${err instanceof Error ? err.message : String(err)}`);
|
|
9431
|
+
return;
|
|
9432
|
+
}
|
|
9433
|
+
const gates = state.gates;
|
|
9434
|
+
if (!gates || typeof gates !== "object") {
|
|
9435
|
+
logFn("WARNING \u2014 state file has unexpected structure, skipping advancement");
|
|
9436
|
+
return;
|
|
9437
|
+
}
|
|
9438
|
+
if (!state.slug && agentSlug) {
|
|
9439
|
+
state.slug = agentSlug;
|
|
9440
|
+
}
|
|
9441
|
+
if (gates[gateFlag] === true) return;
|
|
9442
|
+
gates[gateFlag] = true;
|
|
9443
|
+
try {
|
|
9444
|
+
writeFileSync8(VOLATILE_STATE_FILE, JSON.stringify(state));
|
|
9445
|
+
} catch (err) {
|
|
9446
|
+
logFn(`ERROR \u2014 failed to write volatile state after advancement: ${err instanceof Error ? err.message : String(err)}`);
|
|
9447
|
+
}
|
|
9448
|
+
const durablePath = `${accountDir}/.claude/agent-create-state.json`;
|
|
9449
|
+
try {
|
|
9450
|
+
mkdirSync6(dirname2(durablePath), { recursive: true });
|
|
9451
|
+
writeFileSync8(durablePath, JSON.stringify(state));
|
|
9452
|
+
} catch (err) {
|
|
9453
|
+
logFn(`WARNING \u2014 failed to write durable state after advancement: ${err instanceof Error ? err.message : String(err)}`);
|
|
9454
|
+
}
|
|
9455
|
+
const pending = Object.entries(gates).filter(([, v]) => !v).map(([k]) => k);
|
|
9456
|
+
logFn(`${gateFlag} gate advanced (route-level). Pending: ${pending.length > 0 ? pending.join(", ") : "none"}`);
|
|
9457
|
+
}
|
|
9262
9458
|
async function POST12(req) {
|
|
9263
9459
|
const contentType = req.headers.get("content-type") ?? "";
|
|
9264
9460
|
let message;
|
|
@@ -9394,6 +9590,21 @@ async function POST12(req) {
|
|
|
9394
9590
|
}
|
|
9395
9591
|
} catch {
|
|
9396
9592
|
}
|
|
9593
|
+
try {
|
|
9594
|
+
const parsed = JSON.parse(message);
|
|
9595
|
+
if (isComponentDone(parsed)) {
|
|
9596
|
+
const componentLog = agentLogStream("component-lifecycle", account.accountDir);
|
|
9597
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
9598
|
+
const logFn = (msg) => componentLog.write(`[${ts}] [agent-creation-approval:route] ${msg}
|
|
9599
|
+
`);
|
|
9600
|
+
advanceGateIfNeeded(parsed, account.accountDir, logFn);
|
|
9601
|
+
message = transformComponentDone(parsed);
|
|
9602
|
+
componentLog.write(`[${ts}] [component:${parsed.component}] componentDone \u2192 transformed
|
|
9603
|
+
`);
|
|
9604
|
+
componentLog.end();
|
|
9605
|
+
}
|
|
9606
|
+
} catch {
|
|
9607
|
+
}
|
|
9397
9608
|
const encoder = new TextEncoder();
|
|
9398
9609
|
const sseLog = agentLogStream("sse-events", account.accountDir);
|
|
9399
9610
|
const sk = session_key.slice(0, 8);
|
|
@@ -9503,7 +9714,7 @@ async function POST13(req) {
|
|
|
9503
9714
|
}
|
|
9504
9715
|
|
|
9505
9716
|
// app/api/admin/logs/route.ts
|
|
9506
|
-
import { existsSync as
|
|
9717
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync3 } from "fs";
|
|
9507
9718
|
import { resolve as resolve9, basename } from "path";
|
|
9508
9719
|
var TAIL_BYTES = 8192;
|
|
9509
9720
|
async function GET3(request) {
|
|
@@ -9519,7 +9730,7 @@ async function GET3(request) {
|
|
|
9519
9730
|
if (!dir) continue;
|
|
9520
9731
|
const filePath = resolve9(dir, safe);
|
|
9521
9732
|
try {
|
|
9522
|
-
const content =
|
|
9733
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
9523
9734
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
9524
9735
|
if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
|
|
9525
9736
|
return new Response(content, { headers });
|
|
@@ -9545,7 +9756,7 @@ async function GET3(request) {
|
|
|
9545
9756
|
if (!dir) continue;
|
|
9546
9757
|
const filePath = resolve9(dir, fileName);
|
|
9547
9758
|
try {
|
|
9548
|
-
const content =
|
|
9759
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
9549
9760
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
9550
9761
|
if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
|
|
9551
9762
|
return new Response(content, { headers });
|
|
@@ -9557,7 +9768,7 @@ async function GET3(request) {
|
|
|
9557
9768
|
const seen = /* @__PURE__ */ new Set();
|
|
9558
9769
|
const logs = {};
|
|
9559
9770
|
for (const dir of [accountLogDir, LOG_DIR]) {
|
|
9560
|
-
if (!dir || !
|
|
9771
|
+
if (!dir || !existsSync13(dir)) continue;
|
|
9561
9772
|
let files;
|
|
9562
9773
|
try {
|
|
9563
9774
|
files = readdirSync2(dir).filter((f) => f.endsWith(".log"));
|
|
@@ -9567,7 +9778,7 @@ async function GET3(request) {
|
|
|
9567
9778
|
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync3(resolve9(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
9568
9779
|
seen.add(name);
|
|
9569
9780
|
try {
|
|
9570
|
-
const content =
|
|
9781
|
+
const content = readFileSync13(resolve9(dir, name));
|
|
9571
9782
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
9572
9783
|
logs[name] = tail.trim() || "(empty)";
|
|
9573
9784
|
} catch {
|
|
@@ -9602,7 +9813,7 @@ async function GET4() {
|
|
|
9602
9813
|
|
|
9603
9814
|
// app/api/admin/attachment/[attachmentId]/route.ts
|
|
9604
9815
|
import { readFile, readdir } from "fs/promises";
|
|
9605
|
-
import { existsSync as
|
|
9816
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9606
9817
|
import { resolve as resolve10 } from "path";
|
|
9607
9818
|
async function GET5(req, attachmentId) {
|
|
9608
9819
|
const sessionKey = new URL(req.url).searchParams.get("session_key") ?? "";
|
|
@@ -9617,11 +9828,11 @@ async function GET5(req, attachmentId) {
|
|
|
9617
9828
|
return new Response("Not found", { status: 404 });
|
|
9618
9829
|
}
|
|
9619
9830
|
const dir = resolve10(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
9620
|
-
if (!
|
|
9831
|
+
if (!existsSync14(dir)) {
|
|
9621
9832
|
return new Response("Not found", { status: 404 });
|
|
9622
9833
|
}
|
|
9623
9834
|
const metaPath = resolve10(dir, `${attachmentId}.meta.json`);
|
|
9624
|
-
if (!
|
|
9835
|
+
if (!existsSync14(metaPath)) {
|
|
9625
9836
|
return new Response("Not found", { status: 404 });
|
|
9626
9837
|
}
|
|
9627
9838
|
let meta;
|
|
@@ -9647,7 +9858,7 @@ async function GET5(req, attachmentId) {
|
|
|
9647
9858
|
}
|
|
9648
9859
|
|
|
9649
9860
|
// app/api/admin/account/route.ts
|
|
9650
|
-
import { readFileSync as
|
|
9861
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
9651
9862
|
import { resolve as resolve11 } from "path";
|
|
9652
9863
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
9653
9864
|
async function PATCH(req) {
|
|
@@ -9673,10 +9884,10 @@ async function PATCH(req) {
|
|
|
9673
9884
|
}
|
|
9674
9885
|
const configPath = resolve11(account.accountDir, "account.json");
|
|
9675
9886
|
try {
|
|
9676
|
-
const raw2 =
|
|
9887
|
+
const raw2 = readFileSync14(configPath, "utf-8");
|
|
9677
9888
|
const config = JSON.parse(raw2);
|
|
9678
9889
|
config.contextMode = contextMode;
|
|
9679
|
-
|
|
9890
|
+
writeFileSync9(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
9680
9891
|
console.error(`[account-update] contextMode=${contextMode}`);
|
|
9681
9892
|
return Response.json({ ok: true, contextMode });
|
|
9682
9893
|
} catch (err) {
|
|
@@ -9687,14 +9898,14 @@ async function PATCH(req) {
|
|
|
9687
9898
|
|
|
9688
9899
|
// app/api/admin/agents/route.ts
|
|
9689
9900
|
import { resolve as resolve12 } from "path";
|
|
9690
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
9901
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
9691
9902
|
async function GET6() {
|
|
9692
9903
|
const account = resolveAccount();
|
|
9693
9904
|
if (!account) {
|
|
9694
9905
|
return Response.json({ agents: [] });
|
|
9695
9906
|
}
|
|
9696
9907
|
const agentsDir = resolve12(account.accountDir, "agents");
|
|
9697
|
-
if (!
|
|
9908
|
+
if (!existsSync15(agentsDir)) {
|
|
9698
9909
|
return Response.json({ agents: [] });
|
|
9699
9910
|
}
|
|
9700
9911
|
const agents = [];
|
|
@@ -9704,9 +9915,9 @@ async function GET6() {
|
|
|
9704
9915
|
if (!entry.isDirectory()) continue;
|
|
9705
9916
|
if (entry.name === "admin") continue;
|
|
9706
9917
|
const configPath = resolve12(agentsDir, entry.name, "config.json");
|
|
9707
|
-
if (!
|
|
9918
|
+
if (!existsSync15(configPath)) continue;
|
|
9708
9919
|
try {
|
|
9709
|
-
const config = JSON.parse(
|
|
9920
|
+
const config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
9710
9921
|
agents.push({
|
|
9711
9922
|
slug: entry.name,
|
|
9712
9923
|
displayName: config.displayName ?? entry.name,
|
|
@@ -9724,15 +9935,15 @@ async function GET6() {
|
|
|
9724
9935
|
}
|
|
9725
9936
|
|
|
9726
9937
|
// app/api/admin/version/route.ts
|
|
9727
|
-
import { readFileSync as
|
|
9938
|
+
import { readFileSync as readFileSync16, existsSync as existsSync16 } from "fs";
|
|
9728
9939
|
import { resolve as resolve13, join as join3 } from "path";
|
|
9729
9940
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
9730
9941
|
var brandHostname = "maxy";
|
|
9731
9942
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
9732
9943
|
var brandJsonPath = join3(PLATFORM_ROOT6, "config", "brand.json");
|
|
9733
|
-
if (
|
|
9944
|
+
if (existsSync16(brandJsonPath)) {
|
|
9734
9945
|
try {
|
|
9735
|
-
const brand = JSON.parse(
|
|
9946
|
+
const brand = JSON.parse(readFileSync16(brandJsonPath, "utf-8"));
|
|
9736
9947
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
9737
9948
|
if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
|
|
9738
9949
|
} catch {
|
|
@@ -9745,8 +9956,8 @@ var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
|
9745
9956
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
9746
9957
|
var latestCache = null;
|
|
9747
9958
|
function readInstalled() {
|
|
9748
|
-
if (!
|
|
9749
|
-
const content =
|
|
9959
|
+
if (!existsSync16(VERSION_FILE)) return "unknown";
|
|
9960
|
+
const content = readFileSync16(VERSION_FILE, "utf-8").trim();
|
|
9750
9961
|
return content || "unknown";
|
|
9751
9962
|
}
|
|
9752
9963
|
async function fetchLatest() {
|
|
@@ -9799,16 +10010,16 @@ async function GET7() {
|
|
|
9799
10010
|
}
|
|
9800
10011
|
|
|
9801
10012
|
// app/api/admin/version/upgrade/route.ts
|
|
9802
|
-
import { spawn as
|
|
9803
|
-
import { existsSync as
|
|
10013
|
+
import { spawn as spawn4 } from "child_process";
|
|
10014
|
+
import { existsSync as existsSync17, statSync as statSync4, writeFileSync as writeFileSync10, readFileSync as readFileSync17, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
9804
10015
|
import { resolve as resolve14, join as join4 } from "path";
|
|
9805
10016
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
|
|
9806
10017
|
var upgradePkg = "@rubytech/create-maxy";
|
|
9807
10018
|
var upgradeHostname = "maxy";
|
|
9808
10019
|
var brandPath = join4(PLATFORM_ROOT7, "config", "brand.json");
|
|
9809
|
-
if (
|
|
10020
|
+
if (existsSync17(brandPath)) {
|
|
9810
10021
|
try {
|
|
9811
|
-
const brand = JSON.parse(
|
|
10022
|
+
const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
|
|
9812
10023
|
if (brand.npm?.packageName) upgradePkg = brand.npm.packageName;
|
|
9813
10024
|
if (brand.hostname) upgradeHostname = brand.hostname;
|
|
9814
10025
|
} catch {
|
|
@@ -9818,7 +10029,7 @@ var LOCK_FILE = `/tmp/${upgradeHostname}-upgrade.lock`;
|
|
|
9818
10029
|
var LOG_FILE = `/tmp/${upgradeHostname}-upgrade.log`;
|
|
9819
10030
|
var LOCK_MAX_AGE_MS = 3 * 60 * 1e3;
|
|
9820
10031
|
function isLockFresh() {
|
|
9821
|
-
if (!
|
|
10032
|
+
if (!existsSync17(LOCK_FILE)) return false;
|
|
9822
10033
|
try {
|
|
9823
10034
|
const stat = statSync4(LOCK_FILE);
|
|
9824
10035
|
return Date.now() - stat.mtimeMs < LOCK_MAX_AGE_MS;
|
|
@@ -9844,14 +10055,14 @@ async function POST14(req) {
|
|
|
9844
10055
|
return Response.json({ ok: false, error: "upgrade already in progress" }, { status: 409 });
|
|
9845
10056
|
}
|
|
9846
10057
|
try {
|
|
9847
|
-
|
|
10058
|
+
writeFileSync10(LOCK_FILE, String(Date.now()));
|
|
9848
10059
|
} catch (err) {
|
|
9849
10060
|
console.error("[admin/version/upgrade] failed to write lock file:", err);
|
|
9850
10061
|
}
|
|
9851
10062
|
invalidateVersionCache();
|
|
9852
10063
|
try {
|
|
9853
10064
|
const logFd = openSync2(LOG_FILE, "w");
|
|
9854
|
-
const child =
|
|
10065
|
+
const child = spawn4("npx", ["-y", `${upgradePkg}@latest`], {
|
|
9855
10066
|
detached: true,
|
|
9856
10067
|
stdio: ["ignore", logFd, logFd],
|
|
9857
10068
|
env: { ...process.env, npm_config_yes: "true" },
|
|
@@ -9974,12 +10185,12 @@ async function POST15() {
|
|
|
9974
10185
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
9975
10186
|
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join5(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
9976
10187
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
9977
|
-
if (BRAND_JSON_PATH && !
|
|
10188
|
+
if (BRAND_JSON_PATH && !existsSync18(BRAND_JSON_PATH)) {
|
|
9978
10189
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
9979
10190
|
}
|
|
9980
|
-
if (BRAND_JSON_PATH &&
|
|
10191
|
+
if (BRAND_JSON_PATH && existsSync18(BRAND_JSON_PATH)) {
|
|
9981
10192
|
try {
|
|
9982
|
-
const parsed = JSON.parse(
|
|
10193
|
+
const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
9983
10194
|
BRAND = { ...BRAND, ...parsed };
|
|
9984
10195
|
} catch (err) {
|
|
9985
10196
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -9994,8 +10205,8 @@ var brandLoginOpts = {
|
|
|
9994
10205
|
var ALIAS_DOMAINS_PATH = join5(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
9995
10206
|
function loadAliasDomains() {
|
|
9996
10207
|
try {
|
|
9997
|
-
if (!
|
|
9998
|
-
const parsed = JSON.parse(
|
|
10208
|
+
if (!existsSync18(ALIAS_DOMAINS_PATH)) return null;
|
|
10209
|
+
const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
9999
10210
|
if (!Array.isArray(parsed)) {
|
|
10000
10211
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
10001
10212
|
return null;
|
|
@@ -10325,9 +10536,9 @@ app.get(
|
|
|
10325
10536
|
);
|
|
10326
10537
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
10327
10538
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
10328
|
-
if (BRAND_JSON_PATH &&
|
|
10539
|
+
if (BRAND_JSON_PATH && existsSync18(BRAND_JSON_PATH)) {
|
|
10329
10540
|
try {
|
|
10330
|
-
const fullBrand = JSON.parse(
|
|
10541
|
+
const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
10331
10542
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
10332
10543
|
} catch {
|
|
10333
10544
|
}
|
|
@@ -10341,7 +10552,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
10341
10552
|
function cachedHtml(file) {
|
|
10342
10553
|
let html = htmlCache.get(file);
|
|
10343
10554
|
if (!html) {
|
|
10344
|
-
html =
|
|
10555
|
+
html = readFileSync18(resolve15(process.cwd(), "public", file), "utf-8");
|
|
10345
10556
|
html = html.replace("</head>", `${brandScript}
|
|
10346
10557
|
</head>`);
|
|
10347
10558
|
htmlCache.set(file, html);
|
|
@@ -10353,13 +10564,13 @@ function loadBrandingCache(agentSlug) {
|
|
|
10353
10564
|
const configDir = join5(homedir2(), BRAND.configDir);
|
|
10354
10565
|
try {
|
|
10355
10566
|
const accountJsonPath = join5(configDir, "account.json");
|
|
10356
|
-
if (!
|
|
10357
|
-
const account = JSON.parse(
|
|
10567
|
+
if (!existsSync18(accountJsonPath)) return null;
|
|
10568
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
10358
10569
|
const accountId = account.accountId;
|
|
10359
10570
|
if (!accountId) return null;
|
|
10360
10571
|
const cachePath = join5(configDir, "branding-cache", accountId, `${agentSlug}.json`);
|
|
10361
|
-
if (!
|
|
10362
|
-
return JSON.parse(
|
|
10572
|
+
if (!existsSync18(cachePath)) return null;
|
|
10573
|
+
return JSON.parse(readFileSync18(cachePath, "utf-8"));
|
|
10363
10574
|
} catch {
|
|
10364
10575
|
return null;
|
|
10365
10576
|
}
|
|
@@ -10368,8 +10579,8 @@ function resolveDefaultSlug() {
|
|
|
10368
10579
|
try {
|
|
10369
10580
|
const configDir = join5(homedir2(), BRAND.configDir);
|
|
10370
10581
|
const accountJsonPath = join5(configDir, "account.json");
|
|
10371
|
-
if (!
|
|
10372
|
-
const account = JSON.parse(
|
|
10582
|
+
if (!existsSync18(accountJsonPath)) return null;
|
|
10583
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
10373
10584
|
return account.defaultAgent || null;
|
|
10374
10585
|
} catch {
|
|
10375
10586
|
return null;
|