@rubytech/create-realagent 1.0.771 → 1.0.773
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/admin/PLUGIN.md +2 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +131 -20
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +5 -6
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +5 -6
- 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 +21 -6
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +3 -2
- package/payload/server/chunk-2HBD6IRL.js +3242 -0
- package/payload/server/chunk-57S5JC7G.js +9563 -0
- package/payload/server/chunk-PIMJJCOQ.js +9563 -0
- package/payload/server/chunk-XHFMXKXI.js +3179 -0
- package/payload/server/client-pool-J4ZHJ6Z3.js +28 -0
- package/payload/server/client-pool-U3A5YUO7.js +28 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{admin-CaXX8wc3.js → admin-C4CTVtBu.js} +6 -6
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +81 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: admin
|
|
3
|
-
description: "Platform administration plugin. Provides system-status, brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, agent-list, agent-config-read, logs-read, plugin-read, render-component, session-reset, session-resume, file-attach, wifi, review-cadence tools (review-rules-list, review-rules-suppress, review-rules-unsuppress, review-rules-add, review-rules-remove, review-alerts-recent), adherence-read (attention-weighted adherence ledger), and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
3
|
+
description: "Platform administration plugin. Provides system-status, brand-settings, account-manage, account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, render-component, session-reset, session-resume, file-attach, wifi, review-cadence tools (review-rules-list, review-rules-suppress, review-rules-unsuppress, review-rules-add, review-rules-remove, review-alerts-recent), adherence-read (attention-weighted adherence ledger), and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
|
|
4
4
|
tools:
|
|
5
5
|
- system-status
|
|
6
6
|
- brand-settings
|
|
@@ -9,6 +9,7 @@ tools:
|
|
|
9
9
|
- admin-add
|
|
10
10
|
- admin-remove
|
|
11
11
|
- admin-list
|
|
12
|
+
- admin-update-pin
|
|
12
13
|
- agent-list
|
|
13
14
|
- agent-config-read
|
|
14
15
|
- logs-read
|
|
@@ -545,9 +545,9 @@ server.tool("account-update", "Update a user-configurable setting in account.jso
|
|
|
545
545
|
// ===================================================================
|
|
546
546
|
// Admin user management tools
|
|
547
547
|
// ===================================================================
|
|
548
|
-
server.tool("admin-add", "Add a new admin user to this account. Creates a device-level user entry (users.json) and adds them to this account's admins list (account.json).
|
|
549
|
-
name: z.string().describe("Display name for the new admin"),
|
|
550
|
-
pin: z.string().optional().describe("Optional 4
|
|
548
|
+
server.tool("admin-add", "Add a new admin user to this account. Creates a device-level user entry (users.json) and adds them to this account's admins list (account.json). PIN must be at least 4 digits. If no PIN is provided, a unique 4-digit PIN is generated. Returns the userId and PIN to share with the new admin.", {
|
|
549
|
+
name: z.string().describe("Display name for the new admin (stored on the AdminUser node in Neo4j)."),
|
|
550
|
+
pin: z.string().optional().describe("Optional PIN (minimum 4 digits). If omitted, a unique 4-digit PIN is generated."),
|
|
551
551
|
}, async ({ name, pin: rawPin }) => {
|
|
552
552
|
const TAG = "[admin]";
|
|
553
553
|
if (!name.trim()) {
|
|
@@ -577,7 +577,7 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
|
|
|
577
577
|
}
|
|
578
578
|
// Resolve the calling user's identity from the session environment
|
|
579
579
|
const callerUserId = process.env.USER_ID;
|
|
580
|
-
// PIN: use provided or generate
|
|
580
|
+
// PIN: use provided or generate. Constraint: minimum 4 digits, no upper bound.
|
|
581
581
|
let plaintextPin;
|
|
582
582
|
if (rawPin) {
|
|
583
583
|
if (rawPin.length < 4) {
|
|
@@ -599,8 +599,9 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
|
|
|
599
599
|
}
|
|
600
600
|
const pinHash = hashPin(plaintextPin);
|
|
601
601
|
const userId = crypto.randomUUID();
|
|
602
|
-
// 1. Write to users.json (device-level)
|
|
603
|
-
|
|
602
|
+
// 1. Write to users.json (device-level). Auth fields only — `name` lives
|
|
603
|
+
// on the AdminUser node in Neo4j (Task 829).
|
|
604
|
+
users.push({ userId, pin: pinHash });
|
|
604
605
|
try {
|
|
605
606
|
writeUsersJson(users);
|
|
606
607
|
}
|
|
@@ -622,20 +623,58 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
|
|
|
622
623
|
console.error(`${TAG} account.json write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
623
624
|
return { content: [{ type: "text", text: `${TAG} User created in users.json but failed to add to account: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
624
625
|
}
|
|
625
|
-
// 3. Write to Neo4j (graph-level)
|
|
626
|
+
// 3. Write to Neo4j (graph-level): AdminUser + Person + OWNS atomically,
|
|
627
|
+
// plus ADMIN_OF edge to the LocalBusiness for this account.
|
|
628
|
+
// Task 830 — deterministic identity creation: never delegate node
|
|
629
|
+
// creation to the LLM. Person reuse rule mirrors writeAdminUserAndPerson
|
|
630
|
+
// in platform/ui/app/lib/neo4j-store.ts (case-insensitive exact match
|
|
631
|
+
// on givenName + familyName; partial-name ambiguity does NOT match —
|
|
632
|
+
// rationalisation is a separate agent-mediated concern).
|
|
626
633
|
let neo4jWarning = "";
|
|
634
|
+
let personReused = false;
|
|
627
635
|
try {
|
|
628
636
|
const session = getSession();
|
|
629
637
|
try {
|
|
630
638
|
const createdAt = new Date().toISOString();
|
|
631
639
|
const trimmedName = name.trim();
|
|
632
|
-
|
|
640
|
+
const firstSpace = trimmedName.search(/\s/);
|
|
641
|
+
const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
|
|
642
|
+
const familyName = firstSpace === -1 ? null : (trimmedName.slice(firstSpace + 1).trim() || null);
|
|
643
|
+
const result = await session.run(`MERGE (au:AdminUser {userId: $userId})
|
|
633
644
|
ON CREATE SET au.name = $name, au.createdAt = $createdAt
|
|
634
|
-
ON MATCH SET au.name = $name
|
|
645
|
+
ON MATCH SET au.name = $name, au.updatedAt = $createdAt
|
|
635
646
|
WITH au
|
|
636
647
|
MATCH (b:LocalBusiness {accountId: $accountId})
|
|
637
648
|
MERGE (au)-[r:ADMIN_OF]->(b)
|
|
638
|
-
ON CREATE SET r.role = 'admin', r.grantedAt = $createdAt
|
|
649
|
+
ON CREATE SET r.role = 'admin', r.grantedAt = $createdAt
|
|
650
|
+
WITH au
|
|
651
|
+
OPTIONAL MATCH (existingPerson:Person {accountId: $accountId})
|
|
652
|
+
WHERE toLower(existingPerson.givenName) = toLower($givenName)
|
|
653
|
+
AND coalesce(toLower(existingPerson.familyName), '') = coalesce(toLower($familyName), '')
|
|
654
|
+
WITH au, existingPerson
|
|
655
|
+
CALL {
|
|
656
|
+
WITH au, existingPerson
|
|
657
|
+
WITH au, existingPerson WHERE existingPerson IS NOT NULL
|
|
658
|
+
MERGE (au)-[:OWNS]->(existingPerson)
|
|
659
|
+
RETURN true AS reused
|
|
660
|
+
UNION
|
|
661
|
+
WITH au, existingPerson
|
|
662
|
+
WITH au WHERE existingPerson IS NULL
|
|
663
|
+
CREATE (newPerson:Person {
|
|
664
|
+
accountId: $accountId,
|
|
665
|
+
givenName: $givenName,
|
|
666
|
+
familyName: $familyName,
|
|
667
|
+
role: 'admin-personal',
|
|
668
|
+
scope: 'admin',
|
|
669
|
+
createdAt: $createdAt
|
|
670
|
+
})
|
|
671
|
+
MERGE (au)-[:OWNS]->(newPerson)
|
|
672
|
+
RETURN false AS reused
|
|
673
|
+
}
|
|
674
|
+
RETURN reused`, { userId, name: trimmedName, createdAt, accountId: ACCOUNT_ID, givenName, familyName });
|
|
675
|
+
if (result.records.length > 0) {
|
|
676
|
+
personReused = result.records[0].get("reused");
|
|
677
|
+
}
|
|
639
678
|
}
|
|
640
679
|
finally {
|
|
641
680
|
await session.close();
|
|
@@ -646,6 +685,7 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
|
|
|
646
685
|
console.error(`${TAG} Neo4j sync failed during admin-add: userId=${userId} error=${errMsg} — files written, graph pending`);
|
|
647
686
|
neo4jWarning = ` Note: Neo4j sync failed (${errMsg}) — the admin user is functional but the graph will need reconciliation on next seed.`;
|
|
648
687
|
}
|
|
688
|
+
console.error(`${TAG} [admin-identity] adminuser-bound userId=${userId.slice(0, 8)} name=${name.trim()} personReused=${personReused}`);
|
|
649
689
|
console.error(`${TAG} admin added: userId=${userId} userName=${name.trim()} accountId=${ACCOUNT_ID} role=admin addedBy=${callerUserId ?? "unknown"}`);
|
|
650
690
|
return {
|
|
651
691
|
content: [{
|
|
@@ -676,13 +716,22 @@ server.tool("admin-remove", "Remove an admin from this account. Removes them fro
|
|
|
676
716
|
if (admins.length <= 1) {
|
|
677
717
|
return { content: [{ type: "text", text: `${TAG} Cannot remove the last admin. At least one admin must remain on the account.` }], isError: true };
|
|
678
718
|
}
|
|
679
|
-
// Resolve the admin's name for the confirmation
|
|
719
|
+
// Resolve the admin's name from Neo4j (canonical) for the confirmation
|
|
720
|
+
// message. Best-effort — fall back to userId if the graph is unreachable.
|
|
680
721
|
let removedName = userId;
|
|
681
722
|
try {
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
723
|
+
const session = getSession();
|
|
724
|
+
try {
|
|
725
|
+
const result = await session.run(`MATCH (au:AdminUser {userId: $userId}) RETURN au.name AS name LIMIT 1`, { userId });
|
|
726
|
+
if (result.records.length > 0) {
|
|
727
|
+
const name = result.records[0].get("name");
|
|
728
|
+
if (name && name.trim())
|
|
729
|
+
removedName = name.trim();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
finally {
|
|
733
|
+
await session.close();
|
|
734
|
+
}
|
|
686
735
|
}
|
|
687
736
|
catch { /* name lookup is best-effort */ }
|
|
688
737
|
// 1. Remove from account.json
|
|
@@ -733,13 +782,27 @@ server.tool("admin-list", "List all admins for this account with their names and
|
|
|
733
782
|
if (admins.length === 0) {
|
|
734
783
|
return { content: [{ type: "text", text: `${TAG} No admins configured for this account.` }] };
|
|
735
784
|
}
|
|
736
|
-
// Enrich with names from
|
|
737
|
-
|
|
785
|
+
// Enrich with names from Neo4j AdminUser (canonical, Task 829). Best
|
|
786
|
+
// effort — render "(unknown)" for any userId without a graph entry.
|
|
787
|
+
const userMap = new Map();
|
|
738
788
|
try {
|
|
739
|
-
|
|
789
|
+
const session = getSession();
|
|
790
|
+
try {
|
|
791
|
+
const result = await session.run(`UNWIND $userIds AS uid
|
|
792
|
+
MATCH (au:AdminUser {userId: uid})
|
|
793
|
+
RETURN au.userId AS userId, au.name AS name`, { userIds: admins.map(a => a.userId) });
|
|
794
|
+
for (const record of result.records) {
|
|
795
|
+
const uid = record.get("userId");
|
|
796
|
+
const name = record.get("name");
|
|
797
|
+
if (name && name.trim())
|
|
798
|
+
userMap.set(uid, name.trim());
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
finally {
|
|
802
|
+
await session.close();
|
|
803
|
+
}
|
|
740
804
|
}
|
|
741
|
-
catch { /* name lookup is best-effort —
|
|
742
|
-
const userMap = new Map(users.map(u => [u.userId, u.name]));
|
|
805
|
+
catch { /* name lookup is best-effort — userIds shown when graph is unreachable */ }
|
|
743
806
|
const lines = admins.map(a => {
|
|
744
807
|
const name = userMap.get(a.userId) ?? "(unknown)";
|
|
745
808
|
return `- **${name}** — role: ${a.role}, userId: ${a.userId}`;
|
|
@@ -748,6 +811,54 @@ server.tool("admin-list", "List all admins for this account with their names and
|
|
|
748
811
|
content: [{ type: "text", text: `Admins for this account:\n\n${lines.join("\n")}` }],
|
|
749
812
|
};
|
|
750
813
|
});
|
|
814
|
+
server.tool("admin-update-pin", "Update an existing admin user's PIN. Defaults to the calling admin if no userId is given. PIN must be at least 4 digits and unique across all users on the device. PINs are device-level: updating another admin's PIN does not require shared account membership — any admin on the device can rotate any other admin's PIN, matching the existing trust model used by admin-remove.", {
|
|
815
|
+
userId: z.string().optional().describe("The userId of the admin whose PIN to update. Defaults to the caller (the admin invoking this tool)."),
|
|
816
|
+
newPin: z.string().describe("The new PIN. Minimum 4 digits, no upper bound."),
|
|
817
|
+
}, async ({ userId: targetUserId, newPin }) => {
|
|
818
|
+
const TAG = "[admin-update-pin]";
|
|
819
|
+
const callerUserId = process.env.USER_ID;
|
|
820
|
+
const userId = targetUserId ?? callerUserId;
|
|
821
|
+
const userIdLabel = userId ? userId.slice(0, 8) : "unknown";
|
|
822
|
+
if (!userId) {
|
|
823
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=no-caller-context`);
|
|
824
|
+
return { content: [{ type: "text", text: `${TAG} No userId supplied and no caller context — cannot update.` }], isError: true };
|
|
825
|
+
}
|
|
826
|
+
if (newPin.length < 4) {
|
|
827
|
+
console.error(`${TAG} userId=${userIdLabel} result=too-short`);
|
|
828
|
+
return { content: [{ type: "text", text: `${TAG} PIN must be at least 4 digits.` }], isError: true };
|
|
829
|
+
}
|
|
830
|
+
let users;
|
|
831
|
+
try {
|
|
832
|
+
users = readUsersJson();
|
|
833
|
+
}
|
|
834
|
+
catch (err) {
|
|
835
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found reason=users-json-read-failed`);
|
|
836
|
+
return { content: [{ type: "text", text: `${TAG} ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
837
|
+
}
|
|
838
|
+
const targetIndex = users.findIndex(u => u.userId === userId);
|
|
839
|
+
if (targetIndex === -1) {
|
|
840
|
+
console.error(`${TAG} userId=${userIdLabel} result=user-not-found`);
|
|
841
|
+
return { content: [{ type: "text", text: `${TAG} User ${userId} not found in users.json.` }], isError: true };
|
|
842
|
+
}
|
|
843
|
+
const newHash = hashPin(newPin);
|
|
844
|
+
const collidesWithOther = users.some((u, i) => i !== targetIndex && u.pin === newHash);
|
|
845
|
+
if (collidesWithOther) {
|
|
846
|
+
console.error(`${TAG} userId=${userIdLabel} result=collision`);
|
|
847
|
+
return { content: [{ type: "text", text: `${TAG} That PIN is already in use by another user. Choose a different PIN.` }], isError: true };
|
|
848
|
+
}
|
|
849
|
+
users[targetIndex].pin = newHash;
|
|
850
|
+
try {
|
|
851
|
+
writeUsersJson(users);
|
|
852
|
+
}
|
|
853
|
+
catch (err) {
|
|
854
|
+
return { content: [{ type: "text", text: `${TAG} Failed to write users.json: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
855
|
+
}
|
|
856
|
+
console.error(`${TAG} userId=${userIdLabel} result=ok`);
|
|
857
|
+
const self = userId === callerUserId;
|
|
858
|
+
return {
|
|
859
|
+
content: [{ type: "text", text: `PIN updated${self ? "" : ` for userId ${userId}`}. The new PIN takes effect on the next login.` }],
|
|
860
|
+
};
|
|
861
|
+
});
|
|
751
862
|
server.tool("agent-image", "Upload, update, or remove an agent's image. action=set: copy the file at filePath to the agent's assets directory and update config.json with image URL and shape. action=remove: delete the image file and clear the image fields from config.json. imageShape: 'circle' for avatars/icons, 'rounded' for logos.", {
|
|
752
863
|
agentSlug: z.string().describe("Agent slug (e.g. 'coaching', 'sales')"),
|
|
753
864
|
action: z.enum(["set", "remove"]),
|