@rubytech/create-maxy 1.0.740 → 1.0.741
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/lib/brand-templating/dist/index.d.ts +18 -0
- package/payload/platform/lib/brand-templating/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/brand-templating/dist/index.js +69 -0
- package/payload/platform/lib/brand-templating/dist/index.js.map +1 -0
- package/payload/platform/lib/brand-templating/src/index.ts +76 -0
- package/payload/platform/lib/brand-templating/tsconfig.json +8 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +23 -1
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +27 -4
- package/payload/platform/neo4j/schema.cypher +5 -2
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +6 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +7 -7
- package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +1 -1
- package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +2 -2
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/access-control.md +10 -10
- package/payload/platform/plugins/docs/references/contacts-guide.md +11 -11
- package/payload/platform/plugins/docs/references/deployment.md +13 -13
- package/payload/platform/plugins/docs/references/getting-started.md +19 -19
- package/payload/platform/plugins/docs/references/internals.md +4 -4
- package/payload/platform/plugins/docs/references/memory-guide.md +21 -21
- package/payload/platform/plugins/docs/references/migration-guide.md +5 -5
- package/payload/platform/plugins/docs/references/platform.md +9 -9
- package/payload/platform/plugins/docs/references/plugins-guide.md +20 -12
- package/payload/platform/plugins/docs/references/projects-guide.md +10 -10
- package/payload/platform/plugins/docs/references/settings.md +13 -13
- package/payload/platform/plugins/docs/references/telegram-guide.md +14 -14
- package/payload/platform/plugins/docs/references/troubleshooting.md +23 -23
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +6 -6
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +2 -2
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +2 -2
- package/payload/platform/plugins/workflows/mcp/test-workflows.sh +5 -1
- package/payload/platform/scripts/dedupe-userprofile-ghosts.sh +388 -0
- package/payload/platform/scripts/embed-backfill.sh +8 -1
- package/payload/platform/scripts/migrate-import.sh +42 -1
- package/payload/platform/scripts/seed-neo4j.sh +1 -0
- package/payload/server/chunk-PQ6LDXZ4.js +2997 -0
- package/payload/server/chunk-W6ZUNLLS.js +9446 -0
- package/payload/server/client-pool-DQBHSKAF.js +28 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/server.js +41 -3
package/package.json
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand templating — single source of truth for the operator-visible brand name.
|
|
3
|
+
*
|
|
4
|
+
* Skills, agent templates, plugin manifests, and reference content reference
|
|
5
|
+
* the brand only via the literal `{{productName}}` placeholder. This module
|
|
6
|
+
* substitutes at read time from `brand.json`. Same shadow-source defect class
|
|
7
|
+
* as Tasks 787/788: there is no fallback; missing or empty `productName`
|
|
8
|
+
* hard-fails so the wrong brand can never reach the system prompt or the
|
|
9
|
+
* operator UI silently.
|
|
10
|
+
*
|
|
11
|
+
* Used by both `platform/ui` (admin/public agent system-prompt assembly) and
|
|
12
|
+
* `platform/plugins/admin/mcp` (the `plugin-read` MCP tool). Both call sites
|
|
13
|
+
* pass the absolute file path as `sourcePath` so the diagnostic line names
|
|
14
|
+
* exactly which file was substituted.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getBrandProductName(): string;
|
|
17
|
+
export declare function substituteBrandPlaceholders(content: string, sourcePath: string): string;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmBH,wBAAgB,mBAAmB,IAAI,MAAM,CAsB5C;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAkBvF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Brand templating — single source of truth for the operator-visible brand name.
|
|
4
|
+
*
|
|
5
|
+
* Skills, agent templates, plugin manifests, and reference content reference
|
|
6
|
+
* the brand only via the literal `{{productName}}` placeholder. This module
|
|
7
|
+
* substitutes at read time from `brand.json`. Same shadow-source defect class
|
|
8
|
+
* as Tasks 787/788: there is no fallback; missing or empty `productName`
|
|
9
|
+
* hard-fails so the wrong brand can never reach the system prompt or the
|
|
10
|
+
* operator UI silently.
|
|
11
|
+
*
|
|
12
|
+
* Used by both `platform/ui` (admin/public agent system-prompt assembly) and
|
|
13
|
+
* `platform/plugins/admin/mcp` (the `plugin-read` MCP tool). Both call sites
|
|
14
|
+
* pass the absolute file path as `sourcePath` so the diagnostic line names
|
|
15
|
+
* exactly which file was substituted.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.getBrandProductName = getBrandProductName;
|
|
19
|
+
exports.substituteBrandPlaceholders = substituteBrandPlaceholders;
|
|
20
|
+
const node_path_1 = require("node:path");
|
|
21
|
+
const node_fs_1 = require("node:fs");
|
|
22
|
+
const PLACEHOLDER = "{{productName}}";
|
|
23
|
+
let cachedProductName = null;
|
|
24
|
+
function brandJsonPath() {
|
|
25
|
+
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
26
|
+
if (!platformRoot) {
|
|
27
|
+
throw new Error("[skill-loader] MAXY_PLATFORM_ROOT not set — cannot resolve brand.json");
|
|
28
|
+
}
|
|
29
|
+
return (0, node_path_1.join)(platformRoot, "config", "brand.json");
|
|
30
|
+
}
|
|
31
|
+
function getBrandProductName() {
|
|
32
|
+
if (cachedProductName !== null)
|
|
33
|
+
return cachedProductName;
|
|
34
|
+
const path = brandJsonPath();
|
|
35
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
36
|
+
throw new Error(`[skill-loader] brand.json missing at ${path}`);
|
|
37
|
+
}
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8"));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new Error(`[skill-loader] brand.json unreadable at ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
44
|
+
}
|
|
45
|
+
const value = parsed.productName;
|
|
46
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
47
|
+
throw new Error(`[skill-loader] brand.json at ${path} has missing or empty productName`);
|
|
48
|
+
}
|
|
49
|
+
cachedProductName = value;
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
function substituteBrandPlaceholders(content, sourcePath) {
|
|
53
|
+
if (!content.includes(PLACEHOLDER))
|
|
54
|
+
return content;
|
|
55
|
+
let productName;
|
|
56
|
+
try {
|
|
57
|
+
productName = getBrandProductName();
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error(`[skill-loader] ERROR: brand.json missing — cannot resolve productName for skill ${sourcePath}`);
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
// split-length counts non-overlapping literal matches without regex-escape risk.
|
|
64
|
+
const occurrences = content.split(PLACEHOLDER).length - 1;
|
|
65
|
+
const substituted = content.split(PLACEHOLDER).join(productName);
|
|
66
|
+
console.log(`[skill-loader] brand-substituted productName=${productName} skill=${sourcePath} occurrences=${occurrences}`);
|
|
67
|
+
return substituted;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAmBH,kDAsBC;AAED,kEAkBC;AA3DD,yCAAiC;AACjC,qCAAmD;AAEnD,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAEtC,IAAI,iBAAiB,GAAkB,IAAI,CAAC;AAE5C,SAAS,aAAa;IACpB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IACpD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,gBAAI,EAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,mBAAmB;IACjC,IAAI,iBAAiB,KAAK,IAAI;QAAE,OAAO,iBAAiB,CAAC;IACzD,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAA,oBAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,MAAiC,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACvG,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,mCAAmC,CACxE,CAAC;IACJ,CAAC;IACD,iBAAiB,GAAG,KAAK,CAAC;IAC1B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,2BAA2B,CAAC,OAAe,EAAE,UAAkB;IAC7E,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,OAAO,CAAC;IACnD,IAAI,WAAmB,CAAC;IACxB,IAAI,CAAC;QACH,WAAW,GAAG,mBAAmB,EAAE,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,mFAAmF,UAAU,EAAE,CAChG,CAAC;QACF,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,iFAAiF;IACjF,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CACT,gDAAgD,WAAW,UAAU,UAAU,gBAAgB,WAAW,EAAE,CAC7G,CAAC;IACF,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand templating — single source of truth for the operator-visible brand name.
|
|
3
|
+
*
|
|
4
|
+
* Skills, agent templates, plugin manifests, and reference content reference
|
|
5
|
+
* the brand only via the literal `{{productName}}` placeholder. This module
|
|
6
|
+
* substitutes at read time from `brand.json`. Same shadow-source defect class
|
|
7
|
+
* as Tasks 787/788: there is no fallback; missing or empty `productName`
|
|
8
|
+
* hard-fails so the wrong brand can never reach the system prompt or the
|
|
9
|
+
* operator UI silently.
|
|
10
|
+
*
|
|
11
|
+
* Used by both `platform/ui` (admin/public agent system-prompt assembly) and
|
|
12
|
+
* `platform/plugins/admin/mcp` (the `plugin-read` MCP tool). Both call sites
|
|
13
|
+
* pass the absolute file path as `sourcePath` so the diagnostic line names
|
|
14
|
+
* exactly which file was substituted.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
19
|
+
|
|
20
|
+
const PLACEHOLDER = "{{productName}}";
|
|
21
|
+
|
|
22
|
+
let cachedProductName: string | null = null;
|
|
23
|
+
|
|
24
|
+
function brandJsonPath(): string {
|
|
25
|
+
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
26
|
+
if (!platformRoot) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"[skill-loader] MAXY_PLATFORM_ROOT not set — cannot resolve brand.json",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return join(platformRoot, "config", "brand.json");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getBrandProductName(): string {
|
|
35
|
+
if (cachedProductName !== null) return cachedProductName;
|
|
36
|
+
const path = brandJsonPath();
|
|
37
|
+
if (!existsSync(path)) {
|
|
38
|
+
throw new Error(`[skill-loader] brand.json missing at ${path}`);
|
|
39
|
+
}
|
|
40
|
+
let parsed: { productName?: unknown };
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`[skill-loader] brand.json unreadable at ${path}: ${err instanceof Error ? err.message : String(err)}`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const value = parsed.productName;
|
|
49
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`[skill-loader] brand.json at ${path} has missing or empty productName`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
cachedProductName = value;
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function substituteBrandPlaceholders(content: string, sourcePath: string): string {
|
|
59
|
+
if (!content.includes(PLACEHOLDER)) return content;
|
|
60
|
+
let productName: string;
|
|
61
|
+
try {
|
|
62
|
+
productName = getBrandProductName();
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error(
|
|
65
|
+
`[skill-loader] ERROR: brand.json missing — cannot resolve productName for skill ${sourcePath}`,
|
|
66
|
+
);
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
// split-length counts non-overlapping literal matches without regex-escape risk.
|
|
70
|
+
const occurrences = content.split(PLACEHOLDER).length - 1;
|
|
71
|
+
const substituted = content.split(PLACEHOLDER).join(productName);
|
|
72
|
+
console.log(
|
|
73
|
+
`[skill-loader] brand-substituted productName=${productName} skill=${sourcePath} occurrences=${occurrences}`,
|
|
74
|
+
);
|
|
75
|
+
return substituted;
|
|
76
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,4EAA4E;IAC5E,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,4EAA4E;IAC5E,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA8G1B"}
|
|
@@ -59,7 +59,29 @@ async function writeNodeWithEdges(params) {
|
|
|
59
59
|
process.stderr.write(`[graph-write] reject reason=unresolved-target labels=${labelCsv} agent=${agentLabel} requested=${uniqueRequested} found=${found}\n`);
|
|
60
60
|
throw new Error(`Write doctrine violated: ${uniqueRequested - found} of ${uniqueRequested} relationship target(s) did not resolve (elementId mismatch). No node created.`);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
let nodeRes;
|
|
63
|
+
try {
|
|
64
|
+
nodeRes = await tx.run(`CREATE (n:${labelStr} $props) RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`, { props: nodeProps });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// Task 792: surface UserProfile uniqueness violations as a structured
|
|
68
|
+
// reject line. Neo4j's ConstraintValidationFailed message names the
|
|
69
|
+
// violated label + properties (e.g. "Node(N) already exists with label
|
|
70
|
+
// `UserProfile` and properties accountId=… userId=…") but NOT the
|
|
71
|
+
// constraint name, so we key on the helper's input `labels` parameter
|
|
72
|
+
// rather than parsing the message. Other constraint violations
|
|
73
|
+
// propagate without specialised logging — they are unexpected and the
|
|
74
|
+
// raw error is the right signal.
|
|
75
|
+
const code = err?.code ?? "";
|
|
76
|
+
if (code === "Neo.ClientError.Schema.ConstraintValidationFailed" && labels.includes("UserProfile")) {
|
|
77
|
+
const accountIdProp = nodeProps.accountId;
|
|
78
|
+
const userIdProp = nodeProps.userId;
|
|
79
|
+
const acctSlice = typeof accountIdProp === "string" ? accountIdProp.slice(0, 8) : "unknown";
|
|
80
|
+
const userSlice = typeof userIdProp === "string" ? userIdProp.slice(0, 8) : "unknown";
|
|
81
|
+
process.stderr.write(`[graph-write] reject reason=user-profile-uniqueness-violation accountId=${acctSlice} userId=${userSlice} writer=${agentLabel}\n`);
|
|
82
|
+
}
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
63
85
|
const nodeId = nodeRes.records[0].get("nodeId");
|
|
64
86
|
const nodeLabels = nodeRes.records[0].get("nodeLabels");
|
|
65
87
|
// Neo4j Community's default isolation is read-committed (not snapshot)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAqCH,wCAWC;AAQD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -112,10 +112,33 @@ export async function writeNodeWithEdges(
|
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
let nodeRes;
|
|
116
|
+
try {
|
|
117
|
+
nodeRes = await tx.run(
|
|
118
|
+
`CREATE (n:${labelStr} $props) RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`,
|
|
119
|
+
{ props: nodeProps }
|
|
120
|
+
);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
// Task 792: surface UserProfile uniqueness violations as a structured
|
|
123
|
+
// reject line. Neo4j's ConstraintValidationFailed message names the
|
|
124
|
+
// violated label + properties (e.g. "Node(N) already exists with label
|
|
125
|
+
// `UserProfile` and properties accountId=… userId=…") but NOT the
|
|
126
|
+
// constraint name, so we key on the helper's input `labels` parameter
|
|
127
|
+
// rather than parsing the message. Other constraint violations
|
|
128
|
+
// propagate without specialised logging — they are unexpected and the
|
|
129
|
+
// raw error is the right signal.
|
|
130
|
+
const code = (err as { code?: string } | null)?.code ?? "";
|
|
131
|
+
if (code === "Neo.ClientError.Schema.ConstraintValidationFailed" && labels.includes("UserProfile")) {
|
|
132
|
+
const accountIdProp = (nodeProps as Record<string, unknown>).accountId;
|
|
133
|
+
const userIdProp = (nodeProps as Record<string, unknown>).userId;
|
|
134
|
+
const acctSlice = typeof accountIdProp === "string" ? accountIdProp.slice(0, 8) : "unknown";
|
|
135
|
+
const userSlice = typeof userIdProp === "string" ? userIdProp.slice(0, 8) : "unknown";
|
|
136
|
+
process.stderr.write(
|
|
137
|
+
`[graph-write] reject reason=user-profile-uniqueness-violation accountId=${acctSlice} userId=${userSlice} writer=${agentLabel}\n`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
119
142
|
const nodeId = nodeRes.records[0].get("nodeId") as string;
|
|
120
143
|
const nodeLabels = nodeRes.records[0].get("nodeLabels") as string[];
|
|
121
144
|
|
|
@@ -577,8 +577,11 @@ FOR (sr:StepResult) ON (sr.runId);
|
|
|
577
577
|
// ----------------------------------------------------------
|
|
578
578
|
|
|
579
579
|
// Composite unique constraint: one profile per admin per account.
|
|
580
|
-
// Replaces the single-key accountId constraint (Task 249)
|
|
581
|
-
|
|
580
|
+
// Replaces the single-key accountId constraint (Task 249) and the
|
|
581
|
+
// transitional name `user_profile_account_user_unique` (Task 792 — renamed
|
|
582
|
+
// for grep parity with the doctrine name in .docs/neo4j.md and the
|
|
583
|
+
// per-bucket dedupe script). Old name dropped in seed-neo4j.sh MIGRATE_EOF.
|
|
584
|
+
CREATE CONSTRAINT user_profile_unique_per_user IF NOT EXISTS
|
|
582
585
|
FOR (up:UserProfile) REQUIRE (up.accountId, up.userId) IS UNIQUE;
|
|
583
586
|
|
|
584
587
|
// ----------------------------------------------------------
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"plugins/*/mcp"
|
|
7
7
|
],
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
10
|
-
"build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json",
|
|
9
|
+
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
10
|
+
"build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json",
|
|
11
11
|
"build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
|
|
12
12
|
"build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
|
|
13
13
|
"build:telegram": "tsc -p plugins/telegram/mcp/tsconfig.json",
|
|
@@ -9,6 +9,7 @@ import { execFileSync } from "node:child_process";
|
|
|
9
9
|
import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { writeKey, validateKey, hasKey, keyFilePath, deleteKey } from "../../../../lib/anthropic-key/dist/index.js";
|
|
11
11
|
import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
|
|
12
|
+
import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
|
|
12
13
|
import { createHash, randomInt, randomUUID } from "node:crypto";
|
|
13
14
|
import { createConnection } from "node:net";
|
|
14
15
|
import { homedir, hostname as osHostname } from "node:os";
|
|
@@ -1226,7 +1227,11 @@ server.tool("plugin-read", "Read a plugin definition (PLUGIN.md) or one of its r
|
|
|
1226
1227
|
isError: true,
|
|
1227
1228
|
};
|
|
1228
1229
|
}
|
|
1229
|
-
const
|
|
1230
|
+
const rawContent = await readFile(pluginPath, "utf-8");
|
|
1231
|
+
// Brand-substitute every plugin file before it reaches the agent —
|
|
1232
|
+
// PLUGIN.md, skills/<name>/SKILL.md, references/*.md all flow through
|
|
1233
|
+
// here. Missing brand.json hard-throws (Tasks 787/788 doctrine).
|
|
1234
|
+
const content = substituteBrandPlaceholders(rawContent, pluginPath);
|
|
1230
1235
|
// Append file inventory on default calls (PLUGIN.md) so agents can discover
|
|
1231
1236
|
// skill and reference paths without guessing
|
|
1232
1237
|
if (resolvedFile === "PLUGIN.md") {
|