@rubytech/create-realagent 1.0.436 → 1.0.438
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/index.js +18 -1
- package/package.json +1 -1
- package/payload/maxy/server.js +135 -40
- package/payload/platform/neo4j/schema.cypher +6 -0
- 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/dist/index.js
CHANGED
|
@@ -206,7 +206,7 @@ function installSystemDeps() {
|
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
208
|
shell("apt-get", ["update"], { sudo: true });
|
|
209
|
-
shell("apt-get", ["install", "-y", "curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils"], { sudo: true });
|
|
209
|
+
shell("apt-get", ["install", "-y", "curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg"], { sudo: true });
|
|
210
210
|
shell("apt-get", ["install", "-y", "tigervnc-standalone-server", "python3-websockify", "novnc", "xdg-utils", "chromium"], { sudo: true });
|
|
211
211
|
// Set hostname from brand manifest
|
|
212
212
|
try {
|
|
@@ -541,6 +541,23 @@ function deployPayload() {
|
|
|
541
541
|
mkdirSync(persistentDir, { recursive: true });
|
|
542
542
|
cpSync(oldAccountsDir, persistentAccountsDir, { recursive: true });
|
|
543
543
|
}
|
|
544
|
+
// Migrate secrets from ~/.maxy/ to brand-specific config dir.
|
|
545
|
+
// Pre-fix code (before Task 262) hardcoded ~/.maxy/ for all brands.
|
|
546
|
+
// On branded builds (configDir != .maxy), secrets may exist at ~/.maxy/ but not
|
|
547
|
+
// at the brand-specific location. Copy them over so they survive the path fix.
|
|
548
|
+
if (BRAND.configDir !== ".maxy") {
|
|
549
|
+
const legacyDir = resolve(process.env.HOME ?? "/root", ".maxy");
|
|
550
|
+
const MIGRATABLE_SECRETS = [".anthropic-api-key", ".admin-pin", ".remote-password"];
|
|
551
|
+
for (const secret of MIGRATABLE_SECRETS) {
|
|
552
|
+
const legacyFile = join(legacyDir, secret);
|
|
553
|
+
const brandFile = join(persistentDir, secret);
|
|
554
|
+
if (existsSync(legacyFile) && !existsSync(brandFile)) {
|
|
555
|
+
mkdirSync(persistentDir, { recursive: true });
|
|
556
|
+
cpSync(legacyFile, brandFile);
|
|
557
|
+
console.log(` Migrated ${secret} from ~/.maxy/ to ~/${BRAND.configDir}/`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
544
561
|
// Sync runtime account data to persistent backup before wipe.
|
|
545
562
|
// The admin agent writes SOUL.md and other files at runtime — capture
|
|
546
563
|
// those changes so they survive the directory wipe that follows.
|
package/package.json
CHANGED
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
|
|
|
@@ -9375,6 +9375,86 @@ async function POST11(req) {
|
|
|
9375
9375
|
}
|
|
9376
9376
|
|
|
9377
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
|
+
}
|
|
9378
9458
|
async function POST12(req) {
|
|
9379
9459
|
const contentType = req.headers.get("content-type") ?? "";
|
|
9380
9460
|
let message;
|
|
@@ -9510,6 +9590,21 @@ async function POST12(req) {
|
|
|
9510
9590
|
}
|
|
9511
9591
|
} catch {
|
|
9512
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
|
+
}
|
|
9513
9608
|
const encoder = new TextEncoder();
|
|
9514
9609
|
const sseLog = agentLogStream("sse-events", account.accountDir);
|
|
9515
9610
|
const sk = session_key.slice(0, 8);
|
|
@@ -9619,7 +9714,7 @@ async function POST13(req) {
|
|
|
9619
9714
|
}
|
|
9620
9715
|
|
|
9621
9716
|
// app/api/admin/logs/route.ts
|
|
9622
|
-
import { existsSync as
|
|
9717
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync3 } from "fs";
|
|
9623
9718
|
import { resolve as resolve9, basename } from "path";
|
|
9624
9719
|
var TAIL_BYTES = 8192;
|
|
9625
9720
|
async function GET3(request) {
|
|
@@ -9635,7 +9730,7 @@ async function GET3(request) {
|
|
|
9635
9730
|
if (!dir) continue;
|
|
9636
9731
|
const filePath = resolve9(dir, safe);
|
|
9637
9732
|
try {
|
|
9638
|
-
const content =
|
|
9733
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
9639
9734
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
9640
9735
|
if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
|
|
9641
9736
|
return new Response(content, { headers });
|
|
@@ -9661,7 +9756,7 @@ async function GET3(request) {
|
|
|
9661
9756
|
if (!dir) continue;
|
|
9662
9757
|
const filePath = resolve9(dir, fileName);
|
|
9663
9758
|
try {
|
|
9664
|
-
const content =
|
|
9759
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
9665
9760
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
9666
9761
|
if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
|
|
9667
9762
|
return new Response(content, { headers });
|
|
@@ -9673,7 +9768,7 @@ async function GET3(request) {
|
|
|
9673
9768
|
const seen = /* @__PURE__ */ new Set();
|
|
9674
9769
|
const logs = {};
|
|
9675
9770
|
for (const dir of [accountLogDir, LOG_DIR]) {
|
|
9676
|
-
if (!dir || !
|
|
9771
|
+
if (!dir || !existsSync13(dir)) continue;
|
|
9677
9772
|
let files;
|
|
9678
9773
|
try {
|
|
9679
9774
|
files = readdirSync2(dir).filter((f) => f.endsWith(".log"));
|
|
@@ -9683,7 +9778,7 @@ async function GET3(request) {
|
|
|
9683
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 }) => {
|
|
9684
9779
|
seen.add(name);
|
|
9685
9780
|
try {
|
|
9686
|
-
const content =
|
|
9781
|
+
const content = readFileSync13(resolve9(dir, name));
|
|
9687
9782
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
9688
9783
|
logs[name] = tail.trim() || "(empty)";
|
|
9689
9784
|
} catch {
|
|
@@ -9718,7 +9813,7 @@ async function GET4() {
|
|
|
9718
9813
|
|
|
9719
9814
|
// app/api/admin/attachment/[attachmentId]/route.ts
|
|
9720
9815
|
import { readFile, readdir } from "fs/promises";
|
|
9721
|
-
import { existsSync as
|
|
9816
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9722
9817
|
import { resolve as resolve10 } from "path";
|
|
9723
9818
|
async function GET5(req, attachmentId) {
|
|
9724
9819
|
const sessionKey = new URL(req.url).searchParams.get("session_key") ?? "";
|
|
@@ -9733,11 +9828,11 @@ async function GET5(req, attachmentId) {
|
|
|
9733
9828
|
return new Response("Not found", { status: 404 });
|
|
9734
9829
|
}
|
|
9735
9830
|
const dir = resolve10(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
9736
|
-
if (!
|
|
9831
|
+
if (!existsSync14(dir)) {
|
|
9737
9832
|
return new Response("Not found", { status: 404 });
|
|
9738
9833
|
}
|
|
9739
9834
|
const metaPath = resolve10(dir, `${attachmentId}.meta.json`);
|
|
9740
|
-
if (!
|
|
9835
|
+
if (!existsSync14(metaPath)) {
|
|
9741
9836
|
return new Response("Not found", { status: 404 });
|
|
9742
9837
|
}
|
|
9743
9838
|
let meta;
|
|
@@ -9763,7 +9858,7 @@ async function GET5(req, attachmentId) {
|
|
|
9763
9858
|
}
|
|
9764
9859
|
|
|
9765
9860
|
// app/api/admin/account/route.ts
|
|
9766
|
-
import { readFileSync as
|
|
9861
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
9767
9862
|
import { resolve as resolve11 } from "path";
|
|
9768
9863
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
9769
9864
|
async function PATCH(req) {
|
|
@@ -9789,10 +9884,10 @@ async function PATCH(req) {
|
|
|
9789
9884
|
}
|
|
9790
9885
|
const configPath = resolve11(account.accountDir, "account.json");
|
|
9791
9886
|
try {
|
|
9792
|
-
const raw2 =
|
|
9887
|
+
const raw2 = readFileSync14(configPath, "utf-8");
|
|
9793
9888
|
const config = JSON.parse(raw2);
|
|
9794
9889
|
config.contextMode = contextMode;
|
|
9795
|
-
|
|
9890
|
+
writeFileSync9(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
9796
9891
|
console.error(`[account-update] contextMode=${contextMode}`);
|
|
9797
9892
|
return Response.json({ ok: true, contextMode });
|
|
9798
9893
|
} catch (err) {
|
|
@@ -9803,14 +9898,14 @@ async function PATCH(req) {
|
|
|
9803
9898
|
|
|
9804
9899
|
// app/api/admin/agents/route.ts
|
|
9805
9900
|
import { resolve as resolve12 } from "path";
|
|
9806
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
9901
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
9807
9902
|
async function GET6() {
|
|
9808
9903
|
const account = resolveAccount();
|
|
9809
9904
|
if (!account) {
|
|
9810
9905
|
return Response.json({ agents: [] });
|
|
9811
9906
|
}
|
|
9812
9907
|
const agentsDir = resolve12(account.accountDir, "agents");
|
|
9813
|
-
if (!
|
|
9908
|
+
if (!existsSync15(agentsDir)) {
|
|
9814
9909
|
return Response.json({ agents: [] });
|
|
9815
9910
|
}
|
|
9816
9911
|
const agents = [];
|
|
@@ -9820,9 +9915,9 @@ async function GET6() {
|
|
|
9820
9915
|
if (!entry.isDirectory()) continue;
|
|
9821
9916
|
if (entry.name === "admin") continue;
|
|
9822
9917
|
const configPath = resolve12(agentsDir, entry.name, "config.json");
|
|
9823
|
-
if (!
|
|
9918
|
+
if (!existsSync15(configPath)) continue;
|
|
9824
9919
|
try {
|
|
9825
|
-
const config = JSON.parse(
|
|
9920
|
+
const config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
9826
9921
|
agents.push({
|
|
9827
9922
|
slug: entry.name,
|
|
9828
9923
|
displayName: config.displayName ?? entry.name,
|
|
@@ -9840,15 +9935,15 @@ async function GET6() {
|
|
|
9840
9935
|
}
|
|
9841
9936
|
|
|
9842
9937
|
// app/api/admin/version/route.ts
|
|
9843
|
-
import { readFileSync as
|
|
9938
|
+
import { readFileSync as readFileSync16, existsSync as existsSync16 } from "fs";
|
|
9844
9939
|
import { resolve as resolve13, join as join3 } from "path";
|
|
9845
9940
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
9846
9941
|
var brandHostname = "maxy";
|
|
9847
9942
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
9848
9943
|
var brandJsonPath = join3(PLATFORM_ROOT6, "config", "brand.json");
|
|
9849
|
-
if (
|
|
9944
|
+
if (existsSync16(brandJsonPath)) {
|
|
9850
9945
|
try {
|
|
9851
|
-
const brand = JSON.parse(
|
|
9946
|
+
const brand = JSON.parse(readFileSync16(brandJsonPath, "utf-8"));
|
|
9852
9947
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
9853
9948
|
if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
|
|
9854
9949
|
} catch {
|
|
@@ -9861,8 +9956,8 @@ var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
|
9861
9956
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
9862
9957
|
var latestCache = null;
|
|
9863
9958
|
function readInstalled() {
|
|
9864
|
-
if (!
|
|
9865
|
-
const content =
|
|
9959
|
+
if (!existsSync16(VERSION_FILE)) return "unknown";
|
|
9960
|
+
const content = readFileSync16(VERSION_FILE, "utf-8").trim();
|
|
9866
9961
|
return content || "unknown";
|
|
9867
9962
|
}
|
|
9868
9963
|
async function fetchLatest() {
|
|
@@ -9916,15 +10011,15 @@ async function GET7() {
|
|
|
9916
10011
|
|
|
9917
10012
|
// app/api/admin/version/upgrade/route.ts
|
|
9918
10013
|
import { spawn as spawn4 } from "child_process";
|
|
9919
|
-
import { existsSync as
|
|
10014
|
+
import { existsSync as existsSync17, statSync as statSync4, writeFileSync as writeFileSync10, readFileSync as readFileSync17, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
9920
10015
|
import { resolve as resolve14, join as join4 } from "path";
|
|
9921
10016
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
|
|
9922
10017
|
var upgradePkg = "@rubytech/create-maxy";
|
|
9923
10018
|
var upgradeHostname = "maxy";
|
|
9924
10019
|
var brandPath = join4(PLATFORM_ROOT7, "config", "brand.json");
|
|
9925
|
-
if (
|
|
10020
|
+
if (existsSync17(brandPath)) {
|
|
9926
10021
|
try {
|
|
9927
|
-
const brand = JSON.parse(
|
|
10022
|
+
const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
|
|
9928
10023
|
if (brand.npm?.packageName) upgradePkg = brand.npm.packageName;
|
|
9929
10024
|
if (brand.hostname) upgradeHostname = brand.hostname;
|
|
9930
10025
|
} catch {
|
|
@@ -9934,7 +10029,7 @@ var LOCK_FILE = `/tmp/${upgradeHostname}-upgrade.lock`;
|
|
|
9934
10029
|
var LOG_FILE = `/tmp/${upgradeHostname}-upgrade.log`;
|
|
9935
10030
|
var LOCK_MAX_AGE_MS = 3 * 60 * 1e3;
|
|
9936
10031
|
function isLockFresh() {
|
|
9937
|
-
if (!
|
|
10032
|
+
if (!existsSync17(LOCK_FILE)) return false;
|
|
9938
10033
|
try {
|
|
9939
10034
|
const stat = statSync4(LOCK_FILE);
|
|
9940
10035
|
return Date.now() - stat.mtimeMs < LOCK_MAX_AGE_MS;
|
|
@@ -9960,7 +10055,7 @@ async function POST14(req) {
|
|
|
9960
10055
|
return Response.json({ ok: false, error: "upgrade already in progress" }, { status: 409 });
|
|
9961
10056
|
}
|
|
9962
10057
|
try {
|
|
9963
|
-
|
|
10058
|
+
writeFileSync10(LOCK_FILE, String(Date.now()));
|
|
9964
10059
|
} catch (err) {
|
|
9965
10060
|
console.error("[admin/version/upgrade] failed to write lock file:", err);
|
|
9966
10061
|
}
|
|
@@ -10090,12 +10185,12 @@ async function POST15() {
|
|
|
10090
10185
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
10091
10186
|
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join5(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
10092
10187
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
10093
|
-
if (BRAND_JSON_PATH && !
|
|
10188
|
+
if (BRAND_JSON_PATH && !existsSync18(BRAND_JSON_PATH)) {
|
|
10094
10189
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
10095
10190
|
}
|
|
10096
|
-
if (BRAND_JSON_PATH &&
|
|
10191
|
+
if (BRAND_JSON_PATH && existsSync18(BRAND_JSON_PATH)) {
|
|
10097
10192
|
try {
|
|
10098
|
-
const parsed = JSON.parse(
|
|
10193
|
+
const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
10099
10194
|
BRAND = { ...BRAND, ...parsed };
|
|
10100
10195
|
} catch (err) {
|
|
10101
10196
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -10110,8 +10205,8 @@ var brandLoginOpts = {
|
|
|
10110
10205
|
var ALIAS_DOMAINS_PATH = join5(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
10111
10206
|
function loadAliasDomains() {
|
|
10112
10207
|
try {
|
|
10113
|
-
if (!
|
|
10114
|
-
const parsed = JSON.parse(
|
|
10208
|
+
if (!existsSync18(ALIAS_DOMAINS_PATH)) return null;
|
|
10209
|
+
const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
10115
10210
|
if (!Array.isArray(parsed)) {
|
|
10116
10211
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
10117
10212
|
return null;
|
|
@@ -10441,9 +10536,9 @@ app.get(
|
|
|
10441
10536
|
);
|
|
10442
10537
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
10443
10538
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
10444
|
-
if (BRAND_JSON_PATH &&
|
|
10539
|
+
if (BRAND_JSON_PATH && existsSync18(BRAND_JSON_PATH)) {
|
|
10445
10540
|
try {
|
|
10446
|
-
const fullBrand = JSON.parse(
|
|
10541
|
+
const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
10447
10542
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
10448
10543
|
} catch {
|
|
10449
10544
|
}
|
|
@@ -10457,7 +10552,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
10457
10552
|
function cachedHtml(file) {
|
|
10458
10553
|
let html = htmlCache.get(file);
|
|
10459
10554
|
if (!html) {
|
|
10460
|
-
html =
|
|
10555
|
+
html = readFileSync18(resolve15(process.cwd(), "public", file), "utf-8");
|
|
10461
10556
|
html = html.replace("</head>", `${brandScript}
|
|
10462
10557
|
</head>`);
|
|
10463
10558
|
htmlCache.set(file, html);
|
|
@@ -10469,13 +10564,13 @@ function loadBrandingCache(agentSlug) {
|
|
|
10469
10564
|
const configDir = join5(homedir2(), BRAND.configDir);
|
|
10470
10565
|
try {
|
|
10471
10566
|
const accountJsonPath = join5(configDir, "account.json");
|
|
10472
|
-
if (!
|
|
10473
|
-
const account = JSON.parse(
|
|
10567
|
+
if (!existsSync18(accountJsonPath)) return null;
|
|
10568
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
10474
10569
|
const accountId = account.accountId;
|
|
10475
10570
|
if (!accountId) return null;
|
|
10476
10571
|
const cachePath = join5(configDir, "branding-cache", accountId, `${agentSlug}.json`);
|
|
10477
|
-
if (!
|
|
10478
|
-
return JSON.parse(
|
|
10572
|
+
if (!existsSync18(cachePath)) return null;
|
|
10573
|
+
return JSON.parse(readFileSync18(cachePath, "utf-8"));
|
|
10479
10574
|
} catch {
|
|
10480
10575
|
return null;
|
|
10481
10576
|
}
|
|
@@ -10484,8 +10579,8 @@ function resolveDefaultSlug() {
|
|
|
10484
10579
|
try {
|
|
10485
10580
|
const configDir = join5(homedir2(), BRAND.configDir);
|
|
10486
10581
|
const accountJsonPath = join5(configDir, "account.json");
|
|
10487
|
-
if (!
|
|
10488
|
-
const account = JSON.parse(
|
|
10582
|
+
if (!existsSync18(accountJsonPath)) return null;
|
|
10583
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
10489
10584
|
return account.defaultAgent || null;
|
|
10490
10585
|
} catch {
|
|
10491
10586
|
return null;
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
// ----------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
// Person.email must be unique (waitlist signups, future users)
|
|
11
|
+
// Allows multiple nulls — phone-only contacts are not affected.
|
|
11
12
|
CREATE CONSTRAINT person_email_unique IF NOT EXISTS
|
|
12
13
|
FOR (p:Person) REQUIRE p.email IS UNIQUE;
|
|
13
14
|
|
|
15
|
+
// Person.telephone must be unique (phone-first channels: WhatsApp, SMS, Telegram)
|
|
16
|
+
// Allows multiple nulls — email-only contacts are not affected.
|
|
17
|
+
CREATE CONSTRAINT person_telephone_unique IF NOT EXISTS
|
|
18
|
+
FOR (p:Person) REQUIRE p.telephone IS UNIQUE;
|
|
19
|
+
|
|
14
20
|
// Service.serviceId must be unique within the Maxy account
|
|
15
21
|
CREATE CONSTRAINT service_id_unique IF NOT EXISTS
|
|
16
22
|
FOR (s:Service) REQUIRE s.serviceId IS UNIQUE;
|
|
@@ -15,11 +15,11 @@ const accountId = process.env.ACCOUNT_ID;
|
|
|
15
15
|
if (!accountId) {
|
|
16
16
|
throw new Error("ACCOUNT_ID environment variable is required");
|
|
17
17
|
}
|
|
18
|
-
server.tool("contact-create", "Create a new contact (schema:Person) in the graph. Deduplicates by
|
|
18
|
+
server.tool("contact-create", "Create a new contact (schema:Person) in the graph. Requires at least one of email or telephone. Deduplicates by whichever identifier is provided — returns existing contact if email or phone already exists.", {
|
|
19
19
|
givenName: z.string().describe("First name"),
|
|
20
20
|
familyName: z.string().optional().describe("Last name"),
|
|
21
|
-
email: z.string().email().describe("Email address (
|
|
22
|
-
telephone: z.string().optional().describe("Phone number"),
|
|
21
|
+
email: z.string().email().optional().describe("Email address (identifier — at least one of email or telephone required)"),
|
|
22
|
+
telephone: z.string().min(1).max(30).optional().describe("Phone number (identifier — at least one of email or telephone required)"),
|
|
23
23
|
jobTitle: z.string().optional().describe("Job title"),
|
|
24
24
|
source: z
|
|
25
25
|
.string()
|
|
@@ -36,7 +36,7 @@ server.tool("contact-create", "Create a new contact (schema:Person) in the graph
|
|
|
36
36
|
content: [
|
|
37
37
|
{
|
|
38
38
|
type: "text",
|
|
39
|
-
text: `Contact already exists with
|
|
39
|
+
text: `Contact already exists with ${result.identifier} (ID: ${result.nodeId}). No duplicate created.`,
|
|
40
40
|
},
|
|
41
41
|
],
|
|
42
42
|
};
|
|
@@ -45,7 +45,7 @@ server.tool("contact-create", "Create a new contact (schema:Person) in the graph
|
|
|
45
45
|
content: [
|
|
46
46
|
{
|
|
47
47
|
type: "text",
|
|
48
|
-
text: `Contact created: ${params.givenName}
|
|
48
|
+
text: `Contact created: ${params.givenName} ${result.identifier} (ID: ${result.nodeId}, status: ${params.status ?? "waitlist"})`,
|
|
49
49
|
},
|
|
50
50
|
],
|
|
51
51
|
};
|
|
@@ -62,13 +62,14 @@ server.tool("contact-create", "Create a new contact (schema:Person) in the graph
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
-
server.tool("contact-lookup", "Search for contacts by email, name, or list recent contacts. Returns schema:Person nodes.", {
|
|
65
|
+
server.tool("contact-lookup", "Search for contacts by email, telephone, name, or list recent contacts. Returns schema:Person nodes.", {
|
|
66
66
|
email: z.string().optional().describe("Exact email match"),
|
|
67
|
+
telephone: z.string().optional().describe("Exact phone number match"),
|
|
67
68
|
name: z.string().optional().describe("Partial name search (first or last name)"),
|
|
68
69
|
limit: z.number().int().optional().describe("Max results (default 10)"),
|
|
69
|
-
}, async ({ email, name, limit }) => {
|
|
70
|
+
}, async ({ email, telephone, name, limit }) => {
|
|
70
71
|
try {
|
|
71
|
-
const results = await contactLookup({ email, name, accountId, limit });
|
|
72
|
+
const results = await contactLookup({ email, telephone, name, accountId, limit });
|
|
72
73
|
if (results.length === 0) {
|
|
73
74
|
return {
|
|
74
75
|
content: [
|
|
@@ -77,7 +78,14 @@ server.tool("contact-lookup", "Search for contacts by email, name, or list recen
|
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
const formatted = results
|
|
80
|
-
.map((c) =>
|
|
81
|
+
.map((c) => {
|
|
82
|
+
const identifierPart = c.email
|
|
83
|
+
? `<${c.email}>`
|
|
84
|
+
: c.telephone
|
|
85
|
+
? `<${c.telephone}>`
|
|
86
|
+
: "";
|
|
87
|
+
return `${c.givenName}${c.familyName ? ` ${c.familyName}` : ""} ${identifierPart} — ${c.status} (from ${c.source}, ${c.createdOn})`;
|
|
88
|
+
})
|
|
81
89
|
.join("\n");
|
|
82
90
|
return {
|
|
83
91
|
content: [{ type: "text", text: formatted }],
|
|
@@ -95,16 +103,18 @@ server.tool("contact-lookup", "Search for contacts by email, name, or list recen
|
|
|
95
103
|
};
|
|
96
104
|
}
|
|
97
105
|
});
|
|
98
|
-
server.tool("contact-update", "Update properties of an existing contact. Cannot change email or accountId.", {
|
|
106
|
+
server.tool("contact-update", "Update properties of an existing contact. Identify by nodeId, email, or telephone. Cannot change email or accountId.", {
|
|
99
107
|
email: z.string().optional().describe("Find contact by email"),
|
|
108
|
+
telephone: z.string().optional().describe("Find contact by phone number"),
|
|
100
109
|
nodeId: z.string().optional().describe("Find contact by node ID"),
|
|
101
110
|
updates: z
|
|
102
111
|
.record(z.string(), z.unknown())
|
|
103
112
|
.describe("Properties to update (e.g. status, telephone, jobTitle)"),
|
|
104
|
-
}, async ({ email, nodeId, updates }) => {
|
|
113
|
+
}, async ({ email, telephone, nodeId, updates }) => {
|
|
105
114
|
try {
|
|
106
115
|
const result = await contactUpdate({
|
|
107
116
|
email,
|
|
117
|
+
telephone,
|
|
108
118
|
nodeId,
|
|
109
119
|
updates: updates,
|
|
110
120
|
accountId,
|
|
@@ -156,7 +166,14 @@ server.tool("contact-list", "List contacts with optional filters. Returns summar
|
|
|
156
166
|
}
|
|
157
167
|
const header = `${result.total} total contacts${params.status ? ` with status '${params.status}'` : ""}:\n\n`;
|
|
158
168
|
const formatted = result.contacts
|
|
159
|
-
.map((c) =>
|
|
169
|
+
.map((c) => {
|
|
170
|
+
const identifierPart = c.email
|
|
171
|
+
? `<${c.email}>`
|
|
172
|
+
: c.telephone
|
|
173
|
+
? `<${c.telephone}>`
|
|
174
|
+
: "";
|
|
175
|
+
return `${c.givenName}${c.familyName ? ` ${c.familyName}` : ""} ${identifierPart} — ${c.status} (${c.source}, ${c.createdOn})`;
|
|
176
|
+
})
|
|
160
177
|
.join("\n");
|
|
161
178
|
return {
|
|
162
179
|
content: [{ type: "text", text: header + formatted }],
|
|
@@ -174,17 +191,18 @@ server.tool("contact-list", "List contacts with optional filters. Returns summar
|
|
|
174
191
|
};
|
|
175
192
|
}
|
|
176
193
|
});
|
|
177
|
-
server.tool("contact-delete", "Permanently delete a contact (Person node) and all its relationships from the graph. Identify
|
|
194
|
+
server.tool("contact-delete", "Permanently delete a contact (Person node) and all its relationships from the graph. Identify by email, telephone, or node ID. This is irreversible.", {
|
|
178
195
|
email: z.string().optional().describe("Find contact by email"),
|
|
196
|
+
telephone: z.string().optional().describe("Find contact by phone number"),
|
|
179
197
|
nodeId: z.string().optional().describe("Find contact by node ID"),
|
|
180
|
-
}, async ({ email, nodeId }) => {
|
|
198
|
+
}, async ({ email, telephone, nodeId }) => {
|
|
181
199
|
try {
|
|
182
|
-
const result = await contactDelete({ email, nodeId, accountId });
|
|
200
|
+
const result = await contactDelete({ email, telephone, nodeId, accountId });
|
|
183
201
|
return {
|
|
184
202
|
content: [
|
|
185
203
|
{
|
|
186
204
|
type: "text",
|
|
187
|
-
text: `Contact deleted: ${result.
|
|
205
|
+
text: `Contact deleted: ${result.identifier}`,
|
|
188
206
|
},
|
|
189
207
|
],
|
|
190
208
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;AACzC,IAAI,CAAC,SAAS,EAAE,CAAC;IACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
interface ContactCreateParams {
|
|
2
2
|
givenName: string;
|
|
3
3
|
familyName?: string;
|
|
4
|
-
email
|
|
4
|
+
email?: string;
|
|
5
5
|
telephone?: string;
|
|
6
6
|
jobTitle?: string;
|
|
7
7
|
source: string;
|
|
@@ -10,7 +10,7 @@ interface ContactCreateParams {
|
|
|
10
10
|
}
|
|
11
11
|
interface ContactCreateResult {
|
|
12
12
|
nodeId: string;
|
|
13
|
-
|
|
13
|
+
identifier: string;
|
|
14
14
|
created: boolean;
|
|
15
15
|
alreadyExists: boolean;
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-create.d.ts","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"AAqBA,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-create.d.ts","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"AAqBA,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA+F9B"}
|
|
@@ -20,14 +20,34 @@ async function computeEmbedding(text) {
|
|
|
20
20
|
}
|
|
21
21
|
export async function contactCreate(params) {
|
|
22
22
|
const { givenName, familyName, email, telephone, jobTitle, source, status = "waitlist", accountId, } = params;
|
|
23
|
+
const normalizedEmail = email?.toLowerCase().trim();
|
|
24
|
+
const normalizedPhone = telephone?.trim();
|
|
25
|
+
if (!normalizedEmail && !normalizedPhone) {
|
|
26
|
+
throw new Error("At least one of email or telephone is required");
|
|
27
|
+
}
|
|
23
28
|
const session = getSession();
|
|
24
29
|
try {
|
|
25
|
-
//
|
|
26
|
-
const
|
|
30
|
+
// Dedup: check for existing contact by whichever identifiers are provided (OR logic)
|
|
31
|
+
const dedupConditions = [];
|
|
32
|
+
const dedupParams = { accountId };
|
|
33
|
+
if (normalizedEmail) {
|
|
34
|
+
dedupConditions.push("p.email = $email");
|
|
35
|
+
dedupParams.email = normalizedEmail;
|
|
36
|
+
}
|
|
37
|
+
if (normalizedPhone) {
|
|
38
|
+
dedupConditions.push("p.telephone = $telephone");
|
|
39
|
+
dedupParams.telephone = normalizedPhone;
|
|
40
|
+
}
|
|
41
|
+
const existing = await session.run(`MATCH (p:Person {accountId: $accountId})
|
|
42
|
+
WHERE ${dedupConditions.join(" OR ")}
|
|
43
|
+
RETURN elementId(p) AS nodeId, p.email AS email, p.telephone AS telephone`, dedupParams);
|
|
27
44
|
if (existing.records.length > 0) {
|
|
45
|
+
const matchedEmail = existing.records[0].get("email");
|
|
46
|
+
const matchedPhone = existing.records[0].get("telephone");
|
|
47
|
+
const matchedIdentifier = matchedEmail ?? matchedPhone ?? "unknown";
|
|
28
48
|
return {
|
|
29
49
|
nodeId: existing.records[0].get("nodeId"),
|
|
30
|
-
|
|
50
|
+
identifier: matchedIdentifier,
|
|
31
51
|
created: false,
|
|
32
52
|
alreadyExists: true,
|
|
33
53
|
};
|
|
@@ -35,28 +55,33 @@ export async function contactCreate(params) {
|
|
|
35
55
|
// Create new Person node (schema:Person)
|
|
36
56
|
const props = {
|
|
37
57
|
givenName,
|
|
38
|
-
email: email.toLowerCase(),
|
|
39
58
|
source,
|
|
40
59
|
status,
|
|
41
60
|
accountId,
|
|
42
61
|
createdOn: new Date().toISOString(),
|
|
43
62
|
};
|
|
63
|
+
if (normalizedEmail)
|
|
64
|
+
props.email = normalizedEmail;
|
|
65
|
+
if (normalizedPhone)
|
|
66
|
+
props.telephone = normalizedPhone;
|
|
44
67
|
if (familyName)
|
|
45
68
|
props.familyName = familyName;
|
|
46
|
-
if (telephone)
|
|
47
|
-
props.telephone = telephone;
|
|
48
69
|
if (jobTitle)
|
|
49
70
|
props.jobTitle = jobTitle;
|
|
50
|
-
// Compute embedding for vector search
|
|
51
|
-
const
|
|
71
|
+
// Compute embedding for vector search — use whichever identifier is available
|
|
72
|
+
const identifierText = normalizedEmail
|
|
73
|
+
? `<${normalizedEmail}>`
|
|
74
|
+
: normalizedPhone ?? "";
|
|
75
|
+
const textForEmbedding = `Person: ${givenName}${familyName ? ` ${familyName}` : ""} ${identifierText} source: ${source} status: ${status}`;
|
|
52
76
|
const embedding = await computeEmbedding(textForEmbedding);
|
|
53
77
|
if (embedding.length > 0) {
|
|
54
78
|
props.embedding = embedding;
|
|
55
79
|
}
|
|
56
80
|
const result = await session.run(`CREATE (p:Person $props) RETURN elementId(p) AS nodeId`, { props });
|
|
81
|
+
const primaryIdentifier = normalizedEmail ?? normalizedPhone ?? "unknown";
|
|
57
82
|
return {
|
|
58
83
|
nodeId: result.records[0].get("nodeId"),
|
|
59
|
-
|
|
84
|
+
identifier: primaryIdentifier,
|
|
60
85
|
created: true,
|
|
61
86
|
alreadyExists: false,
|
|
62
87
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-create.js","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,wBAAwB,CAAC;AACtE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,kBAAkB,CAAC;AAElE,KAAK,UAAU,gBAAgB,CAAC,IAAY;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,YAAY,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;SAC1D,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;QAC9D,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAoBD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EACJ,SAAS,EACT,UAAU,EACV,KAAK,EACL,SAAS,EACT,QAAQ,EACR,MAAM,EACN,MAAM,GAAG,UAAU,EACnB,SAAS,GACV,GAAG,MAAM,CAAC;IAEX,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,
|
|
1
|
+
{"version":3,"file":"contact-create.js","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
interface ContactDeleteParams {
|
|
2
2
|
nodeId?: string;
|
|
3
3
|
email?: string;
|
|
4
|
+
telephone?: string;
|
|
4
5
|
accountId?: string;
|
|
5
6
|
}
|
|
6
7
|
interface ContactDeleteResult {
|
|
7
8
|
deleted: boolean;
|
|
8
|
-
|
|
9
|
+
identifier: string;
|
|
9
10
|
}
|
|
10
11
|
export declare function contactDelete(params: ContactDeleteParams): Promise<ContactDeleteResult>;
|
|
11
12
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-delete.d.ts","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAEA,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,
|
|
1
|
+
{"version":3,"file":"contact-delete.d.ts","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAEA,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA8C9B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
2
|
export async function contactDelete(params) {
|
|
3
|
-
const { nodeId, email, accountId } = params;
|
|
4
|
-
if (!nodeId && !email) {
|
|
5
|
-
throw new Error("Either nodeId or
|
|
3
|
+
const { nodeId, email, telephone, accountId } = params;
|
|
4
|
+
if (!nodeId && !email && !telephone) {
|
|
5
|
+
throw new Error("Either nodeId, email, or telephone must be provided");
|
|
6
6
|
}
|
|
7
7
|
const session = getSession();
|
|
8
8
|
try {
|
|
@@ -12,20 +12,26 @@ export async function contactDelete(params) {
|
|
|
12
12
|
matchClause = `MATCH (p:Person) WHERE elementId(p) = $nodeId AND p.accountId = $accountId`;
|
|
13
13
|
queryParams.nodeId = nodeId;
|
|
14
14
|
}
|
|
15
|
-
else {
|
|
15
|
+
else if (email) {
|
|
16
16
|
matchClause = `MATCH (p:Person {email: $email, accountId: $accountId})`;
|
|
17
17
|
queryParams.email = email.toLowerCase();
|
|
18
18
|
}
|
|
19
|
+
else {
|
|
20
|
+
matchClause = `MATCH (p:Person {telephone: $telephone, accountId: $accountId})`;
|
|
21
|
+
queryParams.telephone = telephone.trim();
|
|
22
|
+
}
|
|
19
23
|
const result = await session.run(`${matchClause}
|
|
20
|
-
WITH p, p.email AS deletedEmail
|
|
24
|
+
WITH p, p.email AS deletedEmail, p.telephone AS deletedPhone
|
|
21
25
|
DETACH DELETE p
|
|
22
|
-
RETURN deletedEmail`, queryParams);
|
|
26
|
+
RETURN deletedEmail, deletedPhone`, queryParams);
|
|
23
27
|
if (result.records.length === 0) {
|
|
24
28
|
throw new Error("Contact not found");
|
|
25
29
|
}
|
|
30
|
+
const deletedEmail = result.records[0].get("deletedEmail");
|
|
31
|
+
const deletedPhone = result.records[0].get("deletedPhone");
|
|
26
32
|
return {
|
|
27
33
|
deleted: true,
|
|
28
|
-
|
|
34
|
+
identifier: deletedEmail ?? deletedPhone ?? "unknown",
|
|
29
35
|
};
|
|
30
36
|
}
|
|
31
37
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-delete.js","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-delete.js","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAc7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAEvD,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,IAAI,WAAmB,CAAC;QACxB,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,CAAC;QAE3D,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,GAAG,4EAA4E,CAAC;YAC3F,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YACjB,WAAW,GAAG,yDAAyD,CAAC;YACxE,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,iEAAiE,CAAC;YAChF,WAAW,CAAC,SAAS,GAAG,SAAU,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,GAAG,WAAW;;;yCAGqB,EACnC,WAAW,CACZ,CAAC;QAEF,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAkB,CAAC;QAC5E,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAkB,CAAC;QAE5E,OAAO;YACL,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,YAAY,IAAI,YAAY,IAAI,SAAS;SACtD,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-list.d.ts","sourceRoot":"","sources":["../../src/tools/contact-list.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-list.d.ts","sourceRoot":"","sources":["../../src/tools/contact-list.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC;IAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAmExD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-list.js","sourceRoot":"","sources":["../../src/tools/contact-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-list.js","sourceRoot":"","sources":["../../src/tools/contact-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAqB7C,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAyB;IAEzB,MAAM,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,SAAS,EACT,KAAK,GAAG,EAAE,GACX,GAAG,MAAM,CAAC;IAEX,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAChD,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAEpF,IAAI,MAAM,EAAE,CAAC;YACX,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACtC,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACtC,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,UAAU,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACzC,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC;QAC5B,CAAC;QAED,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC,0BAA0B,WAAW,2BAA2B,EAChE,WAAW,CACZ,CAAC;QACF,MAAM,KAAK,GACT,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CACnC,CAAC,QAAQ,EAAE,CAAC;QAEb,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC;eACS,WAAW;;;oBAGN,EACd,WAAW,CACZ,CAAC;QAEF,MAAM,QAAQ,GAAqB,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACnE,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;YACrC,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAW;gBACtC,SAAS,EAAE,CAAC,CAAC,SAAmB;gBAChC,UAAU,EAAE,CAAC,CAAC,UAAgC;gBAC9C,KAAK,EAAE,CAAC,CAAC,KAA2B;gBACpC,SAAS,EAAE,CAAC,CAAC,SAA+B;gBAC5C,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAmB;aACjC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
interface ContactLookupParams {
|
|
2
2
|
email?: string;
|
|
3
|
+
telephone?: string;
|
|
3
4
|
name?: string;
|
|
4
5
|
accountId?: string;
|
|
5
6
|
limit?: number;
|
|
@@ -8,7 +9,7 @@ interface ContactRecord {
|
|
|
8
9
|
nodeId: string;
|
|
9
10
|
givenName: string;
|
|
10
11
|
familyName?: string;
|
|
11
|
-
email
|
|
12
|
+
email?: string;
|
|
12
13
|
telephone?: string;
|
|
13
14
|
jobTitle?: string;
|
|
14
15
|
source: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-lookup.d.ts","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-lookup.d.ts","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,aAAa,EAAE,CAAC,CA+D1B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import neo4j from "neo4j-driver";
|
|
2
2
|
import { getSession } from "../lib/neo4j.js";
|
|
3
3
|
export async function contactLookup(params) {
|
|
4
|
-
const { email, name, accountId, limit = 10 } = params;
|
|
4
|
+
const { email, telephone, name, accountId, limit = 10 } = params;
|
|
5
5
|
const session = getSession();
|
|
6
6
|
try {
|
|
7
7
|
let query;
|
|
@@ -15,6 +15,15 @@ export async function contactLookup(params) {
|
|
|
15
15
|
`;
|
|
16
16
|
queryParams = { email: email.toLowerCase(), accountId, limit: neo4j.int(limit) };
|
|
17
17
|
}
|
|
18
|
+
else if (telephone) {
|
|
19
|
+
query = `
|
|
20
|
+
MATCH (p:Person {accountId: $accountId})
|
|
21
|
+
WHERE p.telephone = $telephone
|
|
22
|
+
RETURN p, elementId(p) AS nodeId
|
|
23
|
+
LIMIT $limit
|
|
24
|
+
`;
|
|
25
|
+
queryParams = { telephone: telephone.trim(), accountId, limit: neo4j.int(limit) };
|
|
26
|
+
}
|
|
18
27
|
else if (name) {
|
|
19
28
|
query = `
|
|
20
29
|
MATCH (p:Person {accountId: $accountId})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-lookup.js","sourceRoot":"","sources":["../../src/tools/contact-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAsB7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAEjE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,IAAI,KAAa,CAAC;QAClB,IAAI,WAAoC,CAAC;QAEzC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,GAAG;;;;;OAKP,CAAC;YACF,WAAW,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACnF,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,KAAK,GAAG;;;;;OAKP,CAAC;YACF,WAAW,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpF,CAAC;aAAM,IAAI,IAAI,EAAE,CAAC;YAChB,KAAK,GAAG;;;;;;OAMP,CAAC;YACF,WAAW,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,KAAK,GAAG;;;;;OAKP,CAAC;YACF,WAAW,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAErD,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACnC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;YACrC,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAW;gBACtC,SAAS,EAAE,CAAC,CAAC,SAAmB;gBAChC,UAAU,EAAE,CAAC,CAAC,UAAgC;gBAC9C,KAAK,EAAE,CAAC,CAAC,KAA2B;gBACpC,SAAS,EAAE,CAAC,CAAC,SAA+B;gBAC5C,QAAQ,EAAE,CAAC,CAAC,QAA8B;gBAC1C,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAmB;aACjC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-update.d.ts","sourceRoot":"","sources":["../../src/tools/contact-update.ts"],"names":[],"mappings":"AAEA,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"contact-update.d.ts","sourceRoot":"","sources":["../../src/tools/contact-update.ts"],"names":[],"mappings":"AAEA,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA+D9B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
2
|
export async function contactUpdate(params) {
|
|
3
|
-
const { nodeId, email, updates, accountId } = params;
|
|
4
|
-
if (!nodeId && !email) {
|
|
5
|
-
throw new Error("Either nodeId or
|
|
3
|
+
const { nodeId, email, telephone, updates, accountId } = params;
|
|
4
|
+
if (!nodeId && !email && !telephone) {
|
|
5
|
+
throw new Error("Either nodeId, email, or telephone must be provided");
|
|
6
6
|
}
|
|
7
7
|
const session = getSession();
|
|
8
8
|
try {
|
|
@@ -12,10 +12,14 @@ export async function contactUpdate(params) {
|
|
|
12
12
|
matchClause = `MATCH (p:Person) WHERE elementId(p) = $nodeId AND p.accountId = $accountId`;
|
|
13
13
|
queryParams.nodeId = nodeId;
|
|
14
14
|
}
|
|
15
|
-
else {
|
|
15
|
+
else if (email) {
|
|
16
16
|
matchClause = `MATCH (p:Person {email: $email, accountId: $accountId})`;
|
|
17
17
|
queryParams.email = email.toLowerCase();
|
|
18
18
|
}
|
|
19
|
+
else {
|
|
20
|
+
matchClause = `MATCH (p:Person {telephone: $telephone, accountId: $accountId})`;
|
|
21
|
+
queryParams.telephone = telephone.trim();
|
|
22
|
+
}
|
|
19
23
|
// Build SET clauses from updates (excluding protected fields)
|
|
20
24
|
const protectedFields = new Set(["accountId", "email", "createdOn", "embedding"]);
|
|
21
25
|
const setClauses = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-update.js","sourceRoot":"","sources":["../../src/tools/contact-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"contact-update.js","sourceRoot":"","sources":["../../src/tools/contact-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAe7C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAEhE,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,IAAI,WAAmB,CAAC;QACxB,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,CAAC;QAE3D,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,GAAG,4EAA4E,CAAC;YAC3F,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YACjB,WAAW,GAAG,yDAAyD,CAAC;YACxE,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,iEAAiE,CAAC;YAChF,WAAW,CAAC,SAAS,GAAG,SAAU,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC;QAED,8DAA8D;QAC9D,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;QAClF,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACvC,MAAM,SAAS,GAAG,OAAO,UAAU,EAAE,CAAC;YACtC,UAAU,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,SAAS,EAAE,CAAC,CAAC;YAC5C,WAAW,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC;YAC/B,UAAU,EAAE,CAAC;QACf,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC5C,WAAW,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEjD,MAAM,KAAK,GAAG;QACV,WAAW;YACP,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;;KAE5B,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAErD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAED,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAW;YACjD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What a Contact Is
|
|
4
4
|
|
|
5
|
-
A contact is a Person node in Maxy's memory graph. Each person has a first name and email address,
|
|
5
|
+
A contact is a Person node in Maxy's memory graph. Each person has a first name and at least one identifier — email address, phone number, or both. Optional fields include last name and job title. Contacts are linked to conversations, other people, and business context.
|
|
6
6
|
|
|
7
7
|
## Adding a Contact
|
|
8
8
|
|
|
@@ -10,10 +10,11 @@ Tell Maxy naturally:
|
|
|
10
10
|
|
|
11
11
|
- "Add John Smith to my contacts — he's a potential client I met at the conference"
|
|
12
12
|
- "Create a contact for sarah@acme.com, her name is Sarah Chen, she's the head of procurement at Acme"
|
|
13
|
+
- "Add Hazel to contacts, phone +27747309676, she's a virtual assistant"
|
|
13
14
|
|
|
14
15
|
Maxy will extract the details and confirm the record before saving.
|
|
15
16
|
|
|
16
|
-
Required: first name and email
|
|
17
|
+
Required: first name and at least one of email or phone number. Everything else is optional but useful.
|
|
17
18
|
|
|
18
19
|
## Looking Up a Contact
|
|
19
20
|
|
|
@@ -22,8 +23,9 @@ Ask naturally:
|
|
|
22
23
|
- "What do you know about John Smith?"
|
|
23
24
|
- "Look up Sarah Chen"
|
|
24
25
|
- "Find the contact from Acme procurement"
|
|
26
|
+
- "Look up +27747309676"
|
|
25
27
|
|
|
26
|
-
Maxy searches by name,
|
|
28
|
+
Maxy searches by name, email, phone number, or any detail you provide.
|
|
27
29
|
|
|
28
30
|
## Updating a Contact
|
|
29
31
|
|
|
@@ -51,8 +53,8 @@ To review the waitlist: "Show me the waitlist" or "Who's on the waitlist?"
|
|
|
51
53
|
|-------|-------------|
|
|
52
54
|
| `givenName` | First name (required) |
|
|
53
55
|
| `familyName` | Last name (optional) |
|
|
54
|
-
| `email` | Email address (
|
|
55
|
-
| `telephone` | Phone number |
|
|
56
|
+
| `email` | Email address (identifier — at least one of email or telephone required; used to deduplicate) |
|
|
57
|
+
| `telephone` | Phone number (identifier — at least one of email or telephone required; used to deduplicate) |
|
|
56
58
|
| `jobTitle` | Job title or role |
|
|
57
59
|
| `source` | Where this contact came from (e.g. "public.maxy.bot", "telegram", "manual") |
|
|
58
60
|
| `status` | Contact status (e.g. "active", "prospect", "waitlist") |
|