@rubytech/create-realagent 1.0.658 → 1.0.660
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +115 -61
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts +13 -1
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js +20 -5
- package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js.map +1 -1
- 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 +6 -1
- 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.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +8 -3
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +14 -6
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
- package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +7 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +7 -0
- package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts +0 -16
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +0 -39
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +10 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +20 -7
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +4 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +24 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/index.js +6 -3
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js +2 -1
- package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts +25 -3
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js +44 -12
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +19 -3
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js +3 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js +3 -2
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js +7 -2
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +5 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js +3 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js.map +1 -1
- package/payload/server/public/assets/{admin-Czc-XCGo.js → admin-BFIYSS4u.js} +1 -1
- package/payload/server/public/assets/{data-CrSbmVOi.js → data-BmeeI1Ix.js} +1 -1
- package/payload/server/public/assets/{file-DJN8iDYd.js → file-D4Qulqz_.js} +1 -1
- package/payload/server/public/assets/graph-MoIys9Ub.js +49 -0
- package/payload/server/public/assets/{house-0e8rAfIV.js → house-B0Hukxjp.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-c8K7DPME.css → jsx-runtime-C6owBiFB.css} +1 -1
- package/payload/server/public/assets/{public-CWJExYI2.js → public-LvjJTLGn.js} +1 -1
- package/payload/server/public/assets/{share-2-DZ7p8QqC.js → share-2-BafZBLp9.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-BgiIIuSz.js → useVoiceRecorder-dLOpmcYJ.js} +1 -1
- package/payload/server/public/assets/{x-CknuyFAk.js → x-BOZIeV0f.js} +1 -1
- package/payload/server/public/data.html +6 -6
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +7 -7
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +262 -160
- package/payload/server/public/assets/graph-BtnUfXRD.js +0 -49
- /package/payload/server/public/assets/{jsx-runtime-DHkQ2la3.js → jsx-runtime-CmCvZzVE.js} +0 -0
package/package.json
CHANGED
|
@@ -363,14 +363,24 @@ export const SCRAPE_EXPRESSION = `(${scrapeCurrentPage.toString()})(document, lo
|
|
|
363
363
|
// constructing a full CdpClient + WebSocket.
|
|
364
364
|
export type CdpEvaluator = (expression: string) => Promise<unknown>;
|
|
365
365
|
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
366
|
+
// Trailing-run length (consecutive polls at `max_count`) the complete-line
|
|
367
|
+
// asserts before it declares `unstable=false`. Two iterations at 500 ms each
|
|
368
|
+
// = 1 s of continuous observation at the high-water mark. Task 615 removed
|
|
369
|
+
// the early-exit on this threshold (it shortcuts multi-zone accounts where
|
|
370
|
+
// the first zone hydrates one poll ahead of the second — the plateau at the
|
|
371
|
+
// intermediate count trips the threshold before the final zone arrives);
|
|
372
|
+
// the threshold now only labels the exit, it does not gate it.
|
|
372
373
|
const STABLE_POLL_THRESHOLD = 2;
|
|
373
374
|
|
|
375
|
+
// Cap on `document.documentElement.outerHTML` captured into the dump file.
|
|
376
|
+
// The observed Cloudflare dashboard outerHTML is ~1 MB; 5 MB is 5× headroom
|
|
377
|
+
// and still fits comfortably in one `writeFile` syscall on the Pi. Pre-Task
|
|
378
|
+
// 615 this was 100 KB — the dump ended mid-`<head>` CSS on every scrape,
|
|
379
|
+
// rendering the forensic artifact premise of Task 608 silently broken: the
|
|
380
|
+
// zone table lived past the cutoff and `grep <zone-host> <dump>` always
|
|
381
|
+
// returned nothing, forcing re-scrapes to answer post-hoc questions.
|
|
382
|
+
const OUTER_HTML_CAPTURE_MAX_CHARS = 5_000_000;
|
|
383
|
+
|
|
374
384
|
// Cap the `domains=[…]` payload on per-poll and complete phase lines. The
|
|
375
385
|
// extractor already implicitly bounds the list via the FQDN regex + Set
|
|
376
386
|
// dedupe, but a pathological CF redesign could emit thousands of href
|
|
@@ -386,7 +396,7 @@ function formatDomains(domains: string[]): string {
|
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
type DumpMode = "stable" | "unstable" | "empty-or-drift";
|
|
389
|
-
type DumpResult = { path: string } | { err: string };
|
|
399
|
+
type DumpResult = { path: string; truncated: boolean } | { err: string };
|
|
390
400
|
|
|
391
401
|
// Snapshot the operator's current dashboard HTML so post-hoc diagnosis of a
|
|
392
402
|
// partial, unstable, or empty scrape has the exact DOM the scrape observed
|
|
@@ -404,7 +414,7 @@ async function dumpHtml(
|
|
|
404
414
|
): Promise<DumpResult> {
|
|
405
415
|
try {
|
|
406
416
|
const html = (await evaluator(
|
|
407
|
-
|
|
417
|
+
`document.documentElement.outerHTML.slice(0, ${OUTER_HTML_CAPTURE_MAX_CHARS})`,
|
|
408
418
|
)) as string;
|
|
409
419
|
// CONFIG_DIR is set by list-cf-domains.sh before the spawn. A silent
|
|
410
420
|
// fallback would dump logs into the wrong brand's directory on a Real
|
|
@@ -423,8 +433,19 @@ async function dumpHtml(
|
|
|
423
433
|
logDir,
|
|
424
434
|
`list-cf-domains-${ts}-count${count}-${mode}-pid${process.pid}.html`,
|
|
425
435
|
);
|
|
426
|
-
|
|
427
|
-
|
|
436
|
+
const htmlStr = typeof html === "string" ? html : String(html);
|
|
437
|
+
await writeFile(dumpPath, htmlStr, "utf-8");
|
|
438
|
+
// Heuristic: if the slice returned exactly OUTER_HTML_CAPTURE_MAX_CHARS,
|
|
439
|
+
// the source outerHTML almost certainly exceeded the cap and we
|
|
440
|
+
// truncated mid-document. The false positive (outerHTML exactly at the
|
|
441
|
+
// ceiling) is a rounding coincidence on the order of 1-in-5M and is
|
|
442
|
+
// acceptable — a loud truncation signal with a ~10^-7 false-positive
|
|
443
|
+
// rate is strictly better than silent truncation (the pre-Task-615
|
|
444
|
+
// failure mode at the 100 KB ceiling, which silently broke every scrape
|
|
445
|
+
// for five months). A downstream investigator seeing `truncated=true`
|
|
446
|
+
// can re-scrape with a larger cap or `outerHTML` unsliced.
|
|
447
|
+
const truncated = htmlStr.length >= OUTER_HTML_CAPTURE_MAX_CHARS;
|
|
448
|
+
return { path: dumpPath, truncated };
|
|
428
449
|
} catch (err) {
|
|
429
450
|
return {
|
|
430
451
|
err: (err instanceof Error ? err.message : String(err)).slice(0, 120),
|
|
@@ -437,7 +458,17 @@ async function dumpHtml(
|
|
|
437
458
|
// complete line terse and routes every failure through the same observation
|
|
438
459
|
// primitive so investigators have one grep pattern for all dump write errors.
|
|
439
460
|
function dumpField(result: DumpResult, mode: DumpMode): string {
|
|
440
|
-
if ("path" in result)
|
|
461
|
+
if ("path" in result) {
|
|
462
|
+
if (result.truncated) {
|
|
463
|
+
// Loud separate phase line — an investigator grepping for truncation
|
|
464
|
+
// incidents across the stream log has one exact pattern to match.
|
|
465
|
+
logPhase(
|
|
466
|
+
`phase=dump-truncated mode=${mode} path=${result.path} captured_bytes=${OUTER_HTML_CAPTURE_MAX_CHARS}`,
|
|
467
|
+
);
|
|
468
|
+
return `dump=${result.path} dump_truncated=true`;
|
|
469
|
+
}
|
|
470
|
+
return `dump=${result.path}`;
|
|
471
|
+
}
|
|
441
472
|
logPhase(
|
|
442
473
|
`phase=dump-write-failed mode=${mode} detail="${result.err.replace(/"/g, "'")}"`,
|
|
443
474
|
);
|
|
@@ -446,13 +477,30 @@ function dumpField(result: DumpResult, mode: DumpMode): string {
|
|
|
446
477
|
|
|
447
478
|
export async function scrapeDomains(evaluator: CdpEvaluator): Promise<string[]> {
|
|
448
479
|
const deadline = Date.now() + SCRAPE_POLL_MS;
|
|
480
|
+
// Task 615: pure poll-to-deadline. The earlier "first N stable polls wins"
|
|
481
|
+
// heuristic short-circuited on the first plateau a multi-zone dashboard
|
|
482
|
+
// rendered — a 2-zone account whose second zone hydrates one poll later
|
|
483
|
+
// than the first would lock `count=1` before the second zone arrived.
|
|
484
|
+
// Removing early-exit entirely is the only formulation that eliminates the
|
|
485
|
+
// class: any "N-consec-at-max wins" variant still fires on the intermediate
|
|
486
|
+
// plateau (`[] [] [a] [a] [a,b] …` → threshold met at poll 4 before zone B
|
|
487
|
+
// observed). The cost is ~9 s of happy-path latency under the 10 s budget
|
|
488
|
+
// (still within onboarding tolerance). `max_count` tracks the high-water
|
|
489
|
+
// count; `consecutiveAtMax` tracks the trailing run at that water-mark;
|
|
490
|
+
// `seenDecrease` tracks any mid-trajectory count drop. `unstable=false`
|
|
491
|
+
// requires reaching max, ending at max, at least STABLE_POLL_THRESHOLD
|
|
492
|
+
// consecutive trailing polls at max, and no mid-trajectory decrease — so a
|
|
493
|
+
// trajectory like `[a][a][a][a,b]{never reaches 2 consecutive}` correctly
|
|
494
|
+
// reports `unstable=true` even though `final_count === max_count`.
|
|
449
495
|
let lastOutcome: ScrapeOutcome | null = null;
|
|
450
|
-
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
let
|
|
455
|
-
let
|
|
496
|
+
let maxCount = 0;
|
|
497
|
+
// Snapshot of the domains array at the most-recent observation of
|
|
498
|
+
// `maxCount`. Updated on every `>=` poll so it always reflects the latest
|
|
499
|
+
// capture at the high-water mark; returned at deadline when `maxCount > 0`.
|
|
500
|
+
let maxDomains: string[] = [];
|
|
501
|
+
let finalCount = 0;
|
|
502
|
+
let consecutiveAtMax = 0;
|
|
503
|
+
let seenDecrease = false;
|
|
456
504
|
let polls = 0;
|
|
457
505
|
|
|
458
506
|
while (Date.now() < deadline) {
|
|
@@ -460,40 +508,44 @@ export async function scrapeDomains(evaluator: CdpEvaluator): Promise<string[]>
|
|
|
460
508
|
try {
|
|
461
509
|
const outcome = (await evaluator(SCRAPE_EXPRESSION)) as ScrapeOutcome;
|
|
462
510
|
lastOutcome = outcome;
|
|
511
|
+
const count = outcome.domains.length;
|
|
463
512
|
|
|
464
|
-
// Per-poll trajectory line (Task 608). Names the exact list captured at
|
|
465
|
-
// this iteration so a partial-capture scenario (e.g. 2-zone account
|
|
466
|
-
// rendering count=1 on poll K because zone B has not hydrated yet) is
|
|
467
|
-
// legible from the stream log alone. Emitted only on successful
|
|
468
|
-
// evaluator returns — the catch branch emits `phase=scrape-retry`
|
|
469
|
-
// instead, because "zero observed" and "failed to observe" are
|
|
470
|
-
// semantically distinct signals for a downstream reader.
|
|
471
513
|
logPhase(
|
|
472
|
-
`phase=dom-scrape-poll n=${polls} count=${
|
|
514
|
+
`phase=dom-scrape-poll n=${polls} count=${count} domains=[${formatDomains(outcome.domains)}]`,
|
|
473
515
|
);
|
|
474
516
|
|
|
475
|
-
if (outcome.reason === "ok" &&
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
517
|
+
if (outcome.reason === "ok" && count > 0) {
|
|
518
|
+
if (count > maxCount) {
|
|
519
|
+
// New high-water mark. Post-stable increases reset the run counter —
|
|
520
|
+
// the new stable window must also satisfy the threshold to qualify
|
|
521
|
+
// as `unstable=false`.
|
|
522
|
+
maxCount = count;
|
|
523
|
+
maxDomains = outcome.domains;
|
|
524
|
+
consecutiveAtMax = 1;
|
|
525
|
+
} else if (count === maxCount) {
|
|
526
|
+
maxDomains = outcome.domains;
|
|
527
|
+
consecutiveAtMax += 1;
|
|
486
528
|
} else {
|
|
487
|
-
|
|
488
|
-
|
|
529
|
+
// count > 0 && count < maxCount — the page shrunk mid-poll
|
|
530
|
+
// (virtual-scroll pagination, zone deletion concurrent with scrape,
|
|
531
|
+
// SPA re-render collapsing rows). Preserve the high-water snapshot;
|
|
532
|
+
// flag the trajectory as unstable.
|
|
533
|
+
seenDecrease = true;
|
|
534
|
+
consecutiveAtMax = 0;
|
|
489
535
|
}
|
|
536
|
+
finalCount = count;
|
|
490
537
|
} else {
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
|
|
496
|
-
|
|
538
|
+
// Empty poll or non-ok reason (no-account-id — SPA mid-navigation):
|
|
539
|
+
// the page momentarily showed no zones, breaking any trailing at-max
|
|
540
|
+
// run. Don't flip `seenDecrease` — treating a hydration gap as a
|
|
541
|
+
// "shrink" would be a false positive on the leading empty polls of
|
|
542
|
+
// every scrape. `finalCount` reflects the last poll's observed count
|
|
543
|
+
// (always 0 in this branch, since `scrapeCurrentPage` returns empty
|
|
544
|
+
// domains on any non-ok reason), so the stream log's `final_count`
|
|
545
|
+
// field accurately names what the last poll saw, not a stale value
|
|
546
|
+
// from an earlier successful observation.
|
|
547
|
+
consecutiveAtMax = 0;
|
|
548
|
+
finalCount = count;
|
|
497
549
|
}
|
|
498
550
|
} catch (err) {
|
|
499
551
|
logPhase(
|
|
@@ -503,31 +555,33 @@ export async function scrapeDomains(evaluator: CdpEvaluator): Promise<string[]>
|
|
|
503
555
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
504
556
|
}
|
|
505
557
|
|
|
506
|
-
// Deadline reached. Two
|
|
558
|
+
// Deadline reached. Two branches:
|
|
507
559
|
//
|
|
508
|
-
// (i) We
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
// the dump to this path — pre-task, only empty-or-drift dumped).
|
|
560
|
+
// (i) We observed at least one non-empty poll. Return the max-count
|
|
561
|
+
// snapshot (which, when `finalCount === maxCount`, is also the last
|
|
562
|
+
// non-empty observation). `unstable=true` whenever any signal suggests
|
|
563
|
+
// the page wasn't settled: shrinkage, end-below-max, or fewer than
|
|
564
|
+
// STABLE_POLL_THRESHOLD consecutive trailing polls at max.
|
|
514
565
|
//
|
|
515
|
-
// (ii)
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
566
|
+
// (ii) Every poll was empty — either a genuinely empty account OR CF
|
|
567
|
+
// drifted the href shape. The `empty-or-drift` dump is the operator's
|
|
568
|
+
// single-artifact signal for distinguishing the two.
|
|
569
|
+
if (maxCount > 0) {
|
|
570
|
+
const unstable =
|
|
571
|
+
seenDecrease ||
|
|
572
|
+
finalCount !== maxCount ||
|
|
573
|
+
consecutiveAtMax < STABLE_POLL_THRESHOLD;
|
|
574
|
+
const mode: DumpMode = unstable ? "unstable" : "stable";
|
|
575
|
+
const dump = await dumpHtml(evaluator, maxCount, mode);
|
|
522
576
|
logPhase(
|
|
523
|
-
`phase=dom-scrape-complete result=ok count=${
|
|
577
|
+
`phase=dom-scrape-complete result=ok count=${maxCount} total_polls=${polls} max_count=${maxCount} final_count=${finalCount} stable_polls=${consecutiveAtMax} unstable=${unstable} domains=[${formatDomains(maxDomains)}] ${dumpField(dump, mode)}`,
|
|
524
578
|
);
|
|
525
|
-
return
|
|
579
|
+
return maxDomains;
|
|
526
580
|
}
|
|
527
581
|
|
|
528
582
|
const dump = await dumpHtml(evaluator, 0, "empty-or-drift");
|
|
529
583
|
logPhase(
|
|
530
|
-
`phase=dom-scrape-complete result=empty-or-drift count=0
|
|
584
|
+
`phase=dom-scrape-complete result=empty-or-drift count=0 total_polls=${polls} max_count=0 final_count=0 stable_polls=0 lastReason=${lastOutcome?.reason ?? "unknown"} ${dumpField(dump, "empty-or-drift")}`,
|
|
531
585
|
);
|
|
532
586
|
return [];
|
|
533
587
|
}
|
|
@@ -7,15 +7,27 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Used by: contact-delete, contact-update, contact-export, contact-erase.
|
|
9
9
|
* NOT used by: contact-create (dedup OR logic), contact-lookup (name search).
|
|
10
|
+
*
|
|
11
|
+
* Task 602 — trashed Person nodes are excluded by default. `contact-erase`
|
|
12
|
+
* passes `includeTrashed: true` because GDPR Article 17 must cascade-delete
|
|
13
|
+
* trashed persons too (they were marked but not yet emptied). Trashed
|
|
14
|
+
* persons also have nulled email/telephone (UNIQUE_KEYS_BY_LABEL snapshot),
|
|
15
|
+
* so identifier-by-email/telephone already fails — the filter is primarily
|
|
16
|
+
* relevant to the nodeId path and guards against the legacy deletedAt
|
|
17
|
+
* soft-delete state surfaced by `notTrashed()`.
|
|
10
18
|
*/
|
|
11
19
|
export interface PersonIdentifier {
|
|
12
20
|
nodeId?: string;
|
|
13
21
|
email?: string;
|
|
14
22
|
telephone?: string;
|
|
15
23
|
}
|
|
24
|
+
export interface ResolveOptions {
|
|
25
|
+
/** Set true for GDPR erasure paths that must match trashed persons too. */
|
|
26
|
+
includeTrashed?: boolean;
|
|
27
|
+
}
|
|
16
28
|
export interface ResolvedMatch {
|
|
17
29
|
matchClause: string;
|
|
18
30
|
queryParams: Record<string, unknown>;
|
|
19
31
|
}
|
|
20
|
-
export declare function resolvePersonMatch(identifier: PersonIdentifier, accountId: string): ResolvedMatch;
|
|
32
|
+
export declare function resolvePersonMatch(identifier: PersonIdentifier, accountId: string, options?: ResolveOptions): ResolvedMatch;
|
|
21
33
|
//# sourceMappingURL=resolve-person.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-person.d.ts","sourceRoot":"","sources":["../../src/lib/resolve-person.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"resolve-person.d.ts","sourceRoot":"","sources":["../../src/lib/resolve-person.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAiCf"}
|
|
@@ -7,30 +7,45 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Used by: contact-delete, contact-update, contact-export, contact-erase.
|
|
9
9
|
* NOT used by: contact-create (dedup OR logic), contact-lookup (name search).
|
|
10
|
+
*
|
|
11
|
+
* Task 602 — trashed Person nodes are excluded by default. `contact-erase`
|
|
12
|
+
* passes `includeTrashed: true` because GDPR Article 17 must cascade-delete
|
|
13
|
+
* trashed persons too (they were marked but not yet emptied). Trashed
|
|
14
|
+
* persons also have nulled email/telephone (UNIQUE_KEYS_BY_LABEL snapshot),
|
|
15
|
+
* so identifier-by-email/telephone already fails — the filter is primarily
|
|
16
|
+
* relevant to the nodeId path and guards against the legacy deletedAt
|
|
17
|
+
* soft-delete state surfaced by `notTrashed()`.
|
|
10
18
|
*/
|
|
11
|
-
|
|
19
|
+
import { notTrashed } from "../../../../../lib/graph-trash/dist/index.js";
|
|
20
|
+
export function resolvePersonMatch(identifier, accountId, options = {}) {
|
|
12
21
|
const { nodeId, email, telephone } = identifier;
|
|
22
|
+
const { includeTrashed = false } = options;
|
|
13
23
|
if (!nodeId && !email && !telephone) {
|
|
14
24
|
throw new Error("Either nodeId, email, or telephone must be provided");
|
|
15
25
|
}
|
|
16
26
|
const queryParams = { accountId };
|
|
27
|
+
const trashSuffix = includeTrashed ? "" : ` AND ${notTrashed("p")}`;
|
|
17
28
|
let matchClause;
|
|
18
29
|
if (nodeId) {
|
|
19
30
|
if (nodeId.includes(":")) {
|
|
20
|
-
matchClause = `MATCH (p:Person) WHERE elementId(p) = $nodeId AND p.accountId = $accountId`;
|
|
31
|
+
matchClause = `MATCH (p:Person) WHERE elementId(p) = $nodeId AND p.accountId = $accountId${trashSuffix}`;
|
|
21
32
|
queryParams.nodeId = nodeId;
|
|
22
33
|
}
|
|
23
34
|
else {
|
|
24
|
-
matchClause = `MATCH (p:Person) WHERE id(p) = $nodeId AND p.accountId = $accountId`;
|
|
35
|
+
matchClause = `MATCH (p:Person) WHERE id(p) = $nodeId AND p.accountId = $accountId${trashSuffix}`;
|
|
25
36
|
queryParams.nodeId = parseInt(nodeId, 10);
|
|
26
37
|
}
|
|
27
38
|
}
|
|
28
39
|
else if (email) {
|
|
29
|
-
matchClause =
|
|
40
|
+
matchClause = includeTrashed
|
|
41
|
+
? `MATCH (p:Person {email: $email, accountId: $accountId})`
|
|
42
|
+
: `MATCH (p:Person {email: $email, accountId: $accountId}) WHERE ${notTrashed("p")}`;
|
|
30
43
|
queryParams.email = email.toLowerCase();
|
|
31
44
|
}
|
|
32
45
|
else {
|
|
33
|
-
matchClause =
|
|
46
|
+
matchClause = includeTrashed
|
|
47
|
+
? `MATCH (p:Person {telephone: $telephone, accountId: $accountId})`
|
|
48
|
+
: `MATCH (p:Person {telephone: $telephone, accountId: $accountId}) WHERE ${notTrashed("p")}`;
|
|
34
49
|
queryParams.telephone = telephone.trim();
|
|
35
50
|
}
|
|
36
51
|
return { matchClause, queryParams };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-person.js","sourceRoot":"","sources":["../../src/lib/resolve-person.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"resolve-person.js","sourceRoot":"","sources":["../../src/lib/resolve-person.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAkB1E,MAAM,UAAU,kBAAkB,CAChC,UAA4B,EAC5B,SAAiB,EACjB,UAA0B,EAAE;IAE5B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC;IAChD,MAAM,EAAE,cAAc,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAE3C,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,CAAC;IAC3D,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;IAEpE,IAAI,WAAmB,CAAC;IACxB,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,WAAW,GAAG,6EAA6E,WAAW,EAAE,CAAC;YACzG,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,sEAAsE,WAAW,EAAE,CAAC;YAClG,WAAW,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,WAAW,GAAG,cAAc;YAC1B,CAAC,CAAC,yDAAyD;YAC3D,CAAC,CAAC,iEAAiE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACvF,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,WAAW,GAAG,cAAc;YAC1B,CAAC,CAAC,iEAAiE;YACnE,CAAC,CAAC,yEAAyE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/F,WAAW,CAAC,SAAS,GAAG,SAAU,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACtC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-create.d.ts","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contact-create.d.ts","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"AAsBA,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,CAmG9B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
|
+
import { notTrashed } from "../../../../../lib/graph-trash/dist/index.js";
|
|
2
3
|
const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
3
4
|
const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
|
|
4
5
|
async function computeEmbedding(text) {
|
|
@@ -38,8 +39,12 @@ export async function contactCreate(params) {
|
|
|
38
39
|
dedupConditions.push("p.telephone = $telephone");
|
|
39
40
|
dedupParams.telephone = normalizedPhone;
|
|
40
41
|
}
|
|
42
|
+
// Dedup only matches live persons. Trashed persons already have null
|
|
43
|
+
// email/telephone (UNIQUE_KEYS_BY_LABEL snapshot), so OR-matching by
|
|
44
|
+
// those keys would miss them anyway — the notTrashed() filter is
|
|
45
|
+
// belt-and-braces against the legacy deletedAt soft-delete state.
|
|
41
46
|
const existing = await session.run(`MATCH (p:Person {accountId: $accountId})
|
|
42
|
-
WHERE ${dedupConditions.join(" OR ")}
|
|
47
|
+
WHERE (${dedupConditions.join(" OR ")}) AND ${notTrashed("p")}
|
|
43
48
|
RETURN elementId(p) AS nodeId, p.email AS email, p.telephone AS telephone`, dedupParams);
|
|
44
49
|
if (existing.records.length > 0) {
|
|
45
50
|
const matchedEmail = existing.records[0].get("email");
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"contact-create.js","sourceRoot":"","sources":["../../src/tools/contact-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-delete.d.ts","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"contact-delete.d.ts","sourceRoot":"","sources":["../../src/tools/contact-delete.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA+C9B"}
|
|
@@ -3,7 +3,7 @@ import { resolvePersonMatch } from "../lib/resolve-person.js";
|
|
|
3
3
|
import { trashNode } from "../../../../../lib/graph-trash/dist/index.js";
|
|
4
4
|
export async function contactDelete(params) {
|
|
5
5
|
const { nodeId, email, telephone, reason, accountId } = params;
|
|
6
|
-
const { matchClause, queryParams } = resolvePersonMatch({ nodeId, email, telephone }, accountId);
|
|
6
|
+
const { matchClause, queryParams } = resolvePersonMatch({ nodeId, email, telephone }, accountId, { includeTrashed: true });
|
|
7
7
|
const session = getSession();
|
|
8
8
|
try {
|
|
9
9
|
const lookup = await session.run(`${matchClause}
|
|
@@ -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;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AA8BzE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAE/D,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,kBAAkB,CACrD,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAC5B,SAAU,
|
|
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,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AA8BzE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAE/D,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,kBAAkB,CACrD,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAC5B,SAAU,EACV,EAAE,cAAc,EAAE,IAAI,EAAE,CACzB,CAAC;IAEF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,GAAG,WAAW;;;;mCAIe,EAC7B,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,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAW,CAAC;QACnD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAkB,CAAC;QACpE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAkB,CAAC;QACxE,MAAM,UAAU,GAAG,WAAW,IAAI,WAAW,IAAI,MAAM,IAAI,SAAS,CAAC;QAErE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;YAC7B,OAAO;YACP,SAAS,EAAE,SAAU;YACrB,SAAS,EAAE,GAAG;YACd,EAAE,EAAE,gBAAgB;YACpB,MAAM;SACP,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,SAAS,EAAE,GAAG;YACd,UAAU;SACX,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-erase.d.ts","sourceRoot":"","sources":["../../src/tools/contact-erase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,KAAK,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,YAAY,CAAC;AAE7D,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"contact-erase.d.ts","sourceRoot":"","sources":["../../src/tools/contact-erase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,KAAK,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,YAAY,CAAC;AAE7D,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAsL7B"}
|
|
@@ -16,7 +16,7 @@ import { resolvePersonMatch } from "../lib/resolve-person.js";
|
|
|
16
16
|
export async function contactErase(params) {
|
|
17
17
|
const { nodeId, email, telephone, confirm = false, accountId } = params;
|
|
18
18
|
const startMs = Date.now();
|
|
19
|
-
const { matchClause, queryParams } = resolvePersonMatch({ nodeId, email, telephone }, accountId);
|
|
19
|
+
const { matchClause, queryParams } = resolvePersonMatch({ nodeId, email, telephone }, accountId, { includeTrashed: true });
|
|
20
20
|
const session = getSession();
|
|
21
21
|
try {
|
|
22
22
|
// Step 1: Resolve Person node and gather its properties
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact-erase.js","sourceRoot":"","sources":["../../src/tools/contact-erase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAgC9D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAA0B;IAE1B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IACxE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,kBAAkB,CACrD,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAC5B,SAAS,
|
|
1
|
+
{"version":3,"file":"contact-erase.js","sourceRoot":"","sources":["../../src/tools/contact-erase.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-create.d.ts","sourceRoot":"","sources":["../../src/tools/group-create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"group-create.d.ts","sourceRoot":"","sources":["../../src/tools/group-create.ts"],"names":[],"mappings":"AAaA,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkGvF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { getSession } from "../lib/neo4j.js";
|
|
3
|
+
import { notTrashed } from "../../../../../lib/graph-trash/dist/index.js";
|
|
3
4
|
function generateGroupSlug(name) {
|
|
4
5
|
const base = name
|
|
5
6
|
.toLowerCase()
|
|
@@ -17,8 +18,11 @@ export async function groupCreate(params) {
|
|
|
17
18
|
let groupSlug = generateGroupSlug(groupName);
|
|
18
19
|
const session = getSession();
|
|
19
20
|
try {
|
|
20
|
-
// Check slug uniqueness
|
|
21
|
+
// Check slug uniqueness — only against live conversations. A trashed
|
|
22
|
+
// conversation holding the same slug would otherwise force an
|
|
23
|
+
// unnecessary slug suffix.
|
|
21
24
|
const existing = await session.run(`MATCH (c:Conversation {groupSlug: $slug, accountId: $accountId})
|
|
25
|
+
WHERE ${notTrashed("c")}
|
|
22
26
|
RETURN count(c) AS cnt`, { slug: groupSlug, accountId });
|
|
23
27
|
if ((existing.records[0]?.get("cnt")?.toNumber?.() ?? 0) > 0) {
|
|
24
28
|
groupSlug = `${groupSlug}-${randomUUID().slice(0, 4)}`;
|
|
@@ -51,9 +55,10 @@ export async function groupCreate(params) {
|
|
|
51
55
|
const notFound = [];
|
|
52
56
|
for (const identifier of participants) {
|
|
53
57
|
const linkResult = await session.run(`MATCH (p:Person {accountId: $accountId})
|
|
54
|
-
WHERE p.givenName + COALESCE(' ' + p.familyName, '') = $identifier
|
|
58
|
+
WHERE (p.givenName + COALESCE(' ' + p.familyName, '') = $identifier
|
|
55
59
|
OR p.email = $identifier
|
|
56
|
-
OR p.telephone = $identifier
|
|
60
|
+
OR p.telephone = $identifier)
|
|
61
|
+
AND ${notTrashed("p")}
|
|
57
62
|
WITH p LIMIT 1
|
|
58
63
|
MATCH (c:Conversation {conversationId: $conversationId})
|
|
59
64
|
MERGE (p)-[r:PARTICIPATES_IN]->(c)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-create.js","sourceRoot":"","sources":["../../src/tools/group-create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"group-create.js","sourceRoot":"","sources":["../../src/tools/group-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-manage.d.ts","sourceRoot":"","sources":["../../src/tools/group-manage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"group-manage.d.ts","sourceRoot":"","sources":["../../src/tools/group-manage.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2FvF"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
|
+
import { notTrashed } from "../../../../../lib/graph-trash/dist/index.js";
|
|
2
3
|
export async function groupManage(params) {
|
|
3
4
|
const { groupSlug, action, participant, accountId } = params;
|
|
4
5
|
const session = getSession();
|
|
5
6
|
try {
|
|
6
|
-
// Find the group
|
|
7
|
+
// Find the group (skip trashed — they should behave as not-found).
|
|
7
8
|
const groupResult = await session.run(`MATCH (c:Conversation {groupSlug: $groupSlug, accountId: $accountId, type: 'group'})
|
|
9
|
+
WHERE ${notTrashed("c")}
|
|
8
10
|
RETURN c.conversationId AS conversationId, c.groupName AS groupName`, { groupSlug, accountId });
|
|
9
11
|
const record = groupResult.records[0];
|
|
10
12
|
if (!record) {
|
|
@@ -16,9 +18,10 @@ export async function groupManage(params) {
|
|
|
16
18
|
if (!participant)
|
|
17
19
|
throw new Error("participant is required for add action");
|
|
18
20
|
const addResult = await session.run(`MATCH (p:Person {accountId: $accountId})
|
|
19
|
-
WHERE p.givenName + COALESCE(' ' + p.familyName, '') = $participant
|
|
21
|
+
WHERE (p.givenName + COALESCE(' ' + p.familyName, '') = $participant
|
|
20
22
|
OR p.email = $participant
|
|
21
|
-
OR p.telephone = $participant
|
|
23
|
+
OR p.telephone = $participant)
|
|
24
|
+
AND ${notTrashed("p")}
|
|
22
25
|
WITH p LIMIT 1
|
|
23
26
|
MATCH (c:Conversation {conversationId: $conversationId})
|
|
24
27
|
MERGE (p)-[r:PARTICIPATES_IN]->(c)
|
|
@@ -34,9 +37,10 @@ export async function groupManage(params) {
|
|
|
34
37
|
if (!participant)
|
|
35
38
|
throw new Error("participant is required for remove action");
|
|
36
39
|
const removeResult = await session.run(`MATCH (p:Person {accountId: $accountId})-[r:PARTICIPATES_IN]->(c:Conversation {conversationId: $conversationId})
|
|
37
|
-
WHERE p.givenName + COALESCE(' ' + p.familyName, '') = $participant
|
|
40
|
+
WHERE (p.givenName + COALESCE(' ' + p.familyName, '') = $participant
|
|
38
41
|
OR p.email = $participant
|
|
39
|
-
OR p.telephone = $participant
|
|
42
|
+
OR p.telephone = $participant)
|
|
43
|
+
AND ${notTrashed("p")}
|
|
40
44
|
DELETE r
|
|
41
45
|
RETURN count(r) AS removed`, { accountId, participant, conversationId });
|
|
42
46
|
const removed = removeResult.records[0]?.get("removed")?.toNumber?.() ?? 0;
|
|
@@ -45,8 +49,12 @@ export async function groupManage(params) {
|
|
|
45
49
|
}
|
|
46
50
|
console.error(`[group] participant-removed id=${conversationId.slice(0, 8)}… name=${participant}`);
|
|
47
51
|
}
|
|
48
|
-
// Always return current participants
|
|
52
|
+
// Always return current participants. Trashed persons are filtered out
|
|
53
|
+
// so the group view reflects active membership only — the PARTICIPATES_IN
|
|
54
|
+
// edge survives soft-delete, but the participant list shouldn't surface
|
|
55
|
+
// them.
|
|
49
56
|
const listResult = await session.run(`MATCH (p:Person)-[r:PARTICIPATES_IN]->(c:Conversation {conversationId: $conversationId})
|
|
57
|
+
WHERE ${notTrashed("p")}
|
|
50
58
|
RETURN r.displayName AS displayName
|
|
51
59
|
ORDER BY r.joinedAt ASC`, { conversationId });
|
|
52
60
|
const participants = listResult.records.map((r) => r.get("displayName"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-manage.js","sourceRoot":"","sources":["../../src/tools/group-manage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"group-manage.js","sourceRoot":"","sources":["../../src/tools/group-manage.ts"],"names":[],"mappings":"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"}
|