@rubytech/create-realagent 1.0.772 → 1.0.775

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -0
  3. package/payload/platform/lib/entitlement/PUBKEY-HASH.txt +1 -0
  4. package/payload/platform/lib/entitlement/dist/canonicalize.d.ts +26 -0
  5. package/payload/platform/lib/entitlement/dist/canonicalize.d.ts.map +1 -0
  6. package/payload/platform/lib/entitlement/dist/canonicalize.js +54 -0
  7. package/payload/platform/lib/entitlement/dist/canonicalize.js.map +1 -0
  8. package/payload/platform/lib/entitlement/dist/index.d.ts +76 -0
  9. package/payload/platform/lib/entitlement/dist/index.d.ts.map +1 -0
  10. package/payload/platform/lib/entitlement/dist/index.js +293 -0
  11. package/payload/platform/lib/entitlement/dist/index.js.map +1 -0
  12. package/payload/platform/lib/entitlement/rubytech-pubkey.pem +3 -0
  13. package/payload/platform/package.json +2 -2
  14. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +32 -0
  15. package/payload/platform/plugins/admin/mcp/dist/index.js +140 -10
  16. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +5 -6
  18. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +5 -6
  19. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +10 -5
  20. package/payload/platform/scripts/generate-entitlement-fixture.mjs +152 -0
  21. package/payload/server/chunk-2HBD6IRL.js +3242 -0
  22. package/payload/server/chunk-MIP54X7Q.js +3244 -0
  23. package/payload/server/chunk-PIMJJCOQ.js +9563 -0
  24. package/payload/server/chunk-TM3EQSID.js +9800 -0
  25. package/payload/server/client-pool-4MZN42GG.js +28 -0
  26. package/payload/server/client-pool-U3A5YUO7.js +28 -0
  27. package/payload/server/maxy-edge.js +2 -2
  28. package/payload/server/public/assets/{Checkbox-DEE8t2QO.js → Checkbox-C_KxaLc-.js} +1 -1
  29. package/payload/server/public/assets/{admin-CFttroHB.js → admin-xbKPR6ZI.js} +30 -30
  30. package/payload/server/public/assets/data-D23IzpJ2.js +1 -0
  31. package/payload/server/public/assets/graph-D2AS9zFS.js +1 -0
  32. package/payload/server/public/assets/{jsx-runtime-DSbkOE76.css → jsx-runtime-BZtBxBng.css} +1 -1
  33. package/payload/server/public/assets/{page-YUT5e7hL.js → page-CjTfZ3O6.js} +2 -2
  34. package/payload/server/public/assets/{page-ZATk95ZG.js → page-DEWgk_nR.js} +1 -1
  35. package/payload/server/public/assets/{public-BLi3J8KU.js → public-CehiL-qZ.js} +1 -1
  36. package/payload/server/public/assets/{share-2-DS7Pnkkq.js → share-2-BG1VXt3z.js} +1 -1
  37. package/payload/server/public/assets/{useVoiceRecorder-pEHqS1ib.js → useVoiceRecorder-1Dvb-yHn.js} +1 -1
  38. package/payload/server/public/data.html +5 -5
  39. package/payload/server/public/graph.html +6 -6
  40. package/payload/server/public/index.html +8 -8
  41. package/payload/server/public/public.html +5 -5
  42. package/payload/server/server.js +52 -17
  43. package/payload/server/public/assets/data-ryPag-T-.js +0 -1
  44. package/payload/server/public/assets/graph-BPnH-UZB.js +0 -1
  45. /package/payload/server/public/assets/{jsx-runtime-DeNudFNA.js → jsx-runtime-DrneHL3t.js} +0 -0
@@ -203,13 +203,12 @@ Invoke the `business-profile` skill. Follow its first-run path: create the `Admi
203
203
 
204
204
  ### `personal`
205
205
 
206
- Personal mode does not register a `LocalBusiness`. Instead, bootstrap the operator's profile so the graph-write gate is satisfied without a business node:
206
+ Personal mode does not register a `LocalBusiness`. The `AdminUser` and personal-profile `Person` nodes were written deterministically at PIN setup time (Task 830 — `writeAdminUserAndPerson`), so this step only enriches the existing Person with an email:
207
207
 
208
- 1. **Ask the user for their email** in one short conversational message — {{productName}} needs an email or phone number on the personal-profile node, and email is the more useful signal for downstream features.
209
- 2. **Read the admin's `userId` and `name` from `admin-identity` in your system prompt.** Split `name` into `givenName` (first token) and `familyName` (remainder, or empty if a single token).
210
- 3. **Write the `AdminUser` node.** Call `memory-write` with `labels: ["AdminUser"]`, `properties: { userId, name }`, `scope: "admin"`, `relationships: [{ type: "HAS_ACCOUNT_SCOPE", direction: "outgoing", targetNodeId: "<account-anchor>" }]` or whatever adjacency convention the current schema requires (grep `AdminUser` in the codebase for a live example if unsure).
211
- 4. **Write the personal-profile `Person` node.** Call `memory-write` with `labels: ["Person"]`, `properties: { givenName, familyName, email, role: "admin-personal" }`, `scope: "admin"`, `relationships: [{ type: "OWNS", direction: "incoming", targetNodeId: "<AdminUser-elementId>" }]`. The `role: "admin-personal"` property is what the graph-write gate looks for in lieu of a `LocalBusiness`.
212
- 5. **Mark step 9 complete.** Call `onboarding-complete-step` with step 9.
208
+ 1. **Ask the user for their email** in one short conversational message — {{productName}} needs an email or phone number on the personal-profile node for downstream features (notifications, contact-method matching).
209
+ 2. **Locate the personal-profile `Person`.** Call `memory-search` with the user's name from `admin-identity` plus `role: "admin-personal"` to find the existing node bound to the `AdminUser` via `OWNS`.
210
+ 3. **Attach the email.** Call `memory-update` on that Person with `properties: { email }`. Do NOT create a new Person — the set-pin path already created and bound it.
211
+ 4. **Mark step 9 complete.** Call `onboarding-complete-step` with step 9.
213
212
 
214
213
  After step 9 completes in personal mode, tell the user that {{productName}} is configured for personal use — their employer (if any) is not registered here. If they later become the operator for a business of their own, they can ask {{productName}} to set up a business profile, which invokes the `business-profile` skill directly.
215
214
 
@@ -20,10 +20,9 @@ External Claude Code plugins (Stripe, Playground):
20
20
 
21
21
  Built-in plugins under `$PLATFORM_ROOT/plugins/`:
22
22
 
23
- - Enable: read `account.json` via `account-manage`, add the plugin name to the `enabledPlugins` array, write back via `Edit`. Activates the plugin's behaviour embed and MCP server from the next session. Plugins with scheduled behaviour (declared via `lifecycle` in PLUGIN.md frontmatter) are activated automatically by the platform heartbeat within one minute — no separate setup command needed.
24
- - Disable: same process, remove the name from the `enabledPlugins` array. Deactivates from the next session. Any scheduled Events owned by the plugin (`sourcePlugin` field) are cancelled automatically by the platform heartbeat.
25
- - Core plugins (admin, memory, docs, cloudflare, anthropic) cannot be disabledthey are always active.
26
- - Validate: before enabling, confirm the plugin directory exists under `$PLATFORM_ROOT/plugins/` and its PLUGIN.md has `optional: true` in `metadata.platform`.
23
+ - Enable: call `plugin-toggle-enabled` with `pluginName` and `action: "enable"`. Activates the plugin's behaviour embed and MCP server from the next session. Plugins with scheduled behaviour (declared via `lifecycle` in PLUGIN.md frontmatter) are activated automatically by the platform heartbeat within one minute — no separate setup command needed.
24
+ - Disable: call `plugin-toggle-enabled` with `pluginName` and `action: "disable"`. Deactivates from the next session. Any scheduled Events owned by the plugin (`sourcePlugin` field) are cancelled automatically by the platform heartbeat.
25
+ - The tool refuses core plugins (admin, memory, docs, cloudflare, anthropic) and refuses plugins not installed under `$PLATFORM_ROOT/plugins/`. Direct `Edit` of `account.json` is denied by the pre-tool-use hook (Task 831) `plugin-toggle-enabled` is the only legitimate path.
27
26
 
28
27
  ## Premium Plugins
29
28
 
@@ -39,7 +38,13 @@ When the user asks about a specific premium plugin (e.g., "tell me about the tea
39
38
 
40
39
  ### Recording a purchase
41
40
 
42
- When the user states they have purchased a premium plugin, validate the plugin name, then confirm the plugin exists in the staging directory. Read `account.json` via `account-manage`, add the plugin name to the `purchasedPlugins` array (if not already present), and write back via `Edit`. Then proceed to delivery.
41
+ `purchasedPlugins` is an entitlement-bearing field (Task 831): the effective list derives from the Rubytech-signed entitlement payload, not from raw `account.json`. The pre-tool-use hook denies any agent write to `account.json`. Direct purchase recording is therefore unavailable to the agent.
42
+
43
+ - **Production path:** Task 832 — Rubytech-side issuance endpoint signs an updated entitlement payload after a Stripe purchase event and delivers it to `~/<configDir>/entitlement.json`. The verifier picks up the new payload at the next session start.
44
+ - **Pre-launch / dev path:** the founder runs `node platform/scripts/generate-entitlement-fixture.mjs sign --account-id <id> --email <e> --tier <t> --plugins a,b,c --out ~/<configDir>/entitlement.json` to produce a signed test payload. The agent does not invoke this script.
45
+ - **Personal-mode installs (`brand.json.commercialMode: false`, the default today):** purchasedPlugins is read from raw `account.json` via implicit-trust mode. Pre-launch the agent has no way to add purchases without operator intervention; this is intentional during the gap before Task 832 ships.
46
+
47
+ If the user asks the agent to record a purchase, explain that their purchase is processed by Rubytech billing and the entitlement payload arrives on their device automatically — no agent action is required.
43
48
 
44
49
  ### Delivering a premium plugin
45
50
 
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * One-shot Rubytech-side entitlement fixture generator.
4
+ *
5
+ * RUN BY FOUNDER ONCE; NEVER ON CUSTOMER DEVICES.
6
+ *
7
+ * Produces:
8
+ * - platform/lib/entitlement/rubytech-pubkey.pem (vendored, committed)
9
+ * - platform/lib/entitlement/PUBKEY-HASH.txt (sha256 of pubkey, committed)
10
+ * - .secrets/rubytech-private-key.pem (LOCAL ONLY — never commit)
11
+ * - <out-dir>/entitlement.json (signed payload fixture)
12
+ *
13
+ * Usage:
14
+ * node platform/scripts/generate-entitlement-fixture.mjs init
15
+ * → generates a fresh keypair + writes the pubkey + hash
16
+ *
17
+ * node platform/scripts/generate-entitlement-fixture.mjs sign \
18
+ * --account-id <id> --email <email> --tier <solo|family|pro> \
19
+ * --plugins <comma-list> --days <n> --out <path>
20
+ * → signs a payload using the existing private key
21
+ *
22
+ * The verifier at platform/lib/entitlement/src/index.ts has PUBKEY_SHA256
23
+ * baked into source. After running `init` the script prints the new hash —
24
+ * paste it into the verifier and rebuild.
25
+ *
26
+ * For Task 832 (Rubytech-side issuance endpoint), this script will be
27
+ * replaced by a server-side equivalent. Until then, it stands in.
28
+ */
29
+
30
+ import {
31
+ generateKeyPairSync,
32
+ createPublicKey,
33
+ createPrivateKey,
34
+ createHash,
35
+ sign as cryptoSign,
36
+ } from "node:crypto";
37
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
38
+ import { resolve, dirname } from "node:path";
39
+ import { fileURLToPath } from "node:url";
40
+
41
+ const __filename = fileURLToPath(import.meta.url);
42
+ const __dirname = dirname(__filename);
43
+ const PLATFORM_ROOT = resolve(__dirname, "..");
44
+ const REPO_ROOT = resolve(PLATFORM_ROOT, "..");
45
+ const PUBKEY_PATH = resolve(PLATFORM_ROOT, "lib", "entitlement", "rubytech-pubkey.pem");
46
+ const HASH_PATH = resolve(PLATFORM_ROOT, "lib", "entitlement", "PUBKEY-HASH.txt");
47
+ const SECRET_DIR = resolve(REPO_ROOT, ".secrets");
48
+ const PRIVKEY_PATH = resolve(SECRET_DIR, "rubytech-private-key.pem");
49
+
50
+ function canonicalize(value) {
51
+ if (value === null) return "null";
52
+ if (typeof value === "boolean") return value ? "true" : "false";
53
+ if (typeof value === "string") return JSON.stringify(value);
54
+ if (typeof value === "number") {
55
+ if (!Number.isFinite(value)) throw new Error("non-finite number");
56
+ if (Object.is(value, -0)) throw new Error("negative zero");
57
+ return JSON.stringify(value);
58
+ }
59
+ if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
60
+ if (typeof value === "object") {
61
+ const keys = Object.keys(value).sort();
62
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(value[k])).join(",") + "}";
63
+ }
64
+ throw new Error(`unsupported value type ${typeof value}`);
65
+ }
66
+
67
+ function init() {
68
+ if (existsSync(PRIVKEY_PATH)) {
69
+ console.error(`ABORT: private key already exists at ${PRIVKEY_PATH}.`);
70
+ console.error("Delete it manually if you really want to regenerate (ALL EXISTING SIGNED PAYLOADS WILL BREAK).");
71
+ process.exit(1);
72
+ }
73
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
74
+ const pubPem = publicKey.export({ type: "spki", format: "pem" });
75
+ const privPem = privateKey.export({ type: "pkcs8", format: "pem" });
76
+
77
+ if (!existsSync(SECRET_DIR)) mkdirSync(SECRET_DIR, { recursive: true });
78
+ writeFileSync(PRIVKEY_PATH, privPem, { mode: 0o600 });
79
+ writeFileSync(PUBKEY_PATH, pubPem, "utf-8");
80
+
81
+ const hash = createHash("sha256").update(pubPem).digest("hex");
82
+ writeFileSync(HASH_PATH, hash + "\n", "utf-8");
83
+
84
+ console.log(`Generated keypair.`);
85
+ console.log(` Public key : ${PUBKEY_PATH}`);
86
+ console.log(` Hash file : ${HASH_PATH} (${hash})`);
87
+ console.log(` Private key : ${PRIVKEY_PATH} (DO NOT COMMIT — verify .gitignore)`);
88
+ console.log("");
89
+ console.log(`NEXT: paste the hash into platform/lib/entitlement/src/index.ts:`);
90
+ console.log(` export const PUBKEY_SHA256 = "${hash}";`);
91
+ console.log(`Then run npm --prefix platform run build:lib`);
92
+ }
93
+
94
+ function parseArgs(argv) {
95
+ const out = {};
96
+ for (let i = 0; i < argv.length; i += 2) {
97
+ const key = argv[i].replace(/^--/, "");
98
+ out[key] = argv[i + 1];
99
+ }
100
+ return out;
101
+ }
102
+
103
+ function sign(args) {
104
+ const required = ["account-id", "email", "tier", "out"];
105
+ for (const k of required) {
106
+ if (!args[k]) {
107
+ console.error(`Missing --${k}`);
108
+ process.exit(1);
109
+ }
110
+ }
111
+ if (!existsSync(PRIVKEY_PATH)) {
112
+ console.error(`Private key missing at ${PRIVKEY_PATH}. Run 'init' first.`);
113
+ process.exit(1);
114
+ }
115
+ const days = Number(args.days ?? "365");
116
+ const plugins = (args.plugins ?? "")
117
+ .split(",")
118
+ .map((s) => s.trim())
119
+ .filter(Boolean);
120
+
121
+ const now = Date.now();
122
+ const issued = new Date(now).toISOString();
123
+ const expires = new Date(now + days * 86400 * 1000).toISOString();
124
+
125
+ const payload = {
126
+ accountId: args["account-id"],
127
+ customerEmail: args.email,
128
+ tier: args.tier,
129
+ purchasedPlugins: plugins,
130
+ issued,
131
+ expires,
132
+ };
133
+ const canonical = canonicalize(payload);
134
+ const privKey = createPrivateKey(readFileSync(PRIVKEY_PATH, "utf-8"));
135
+ const sigBuf = cryptoSign(null, Buffer.from(canonical, "utf-8"), privKey);
136
+ const envelope = { payload, signature: sigBuf.toString("base64") };
137
+ writeFileSync(args.out, JSON.stringify(envelope, null, 2) + "\n", "utf-8");
138
+ console.log(`Signed payload written to ${args.out}`);
139
+ console.log(` tier=${payload.tier} plugins=${plugins.length} expires=${expires}`);
140
+ }
141
+
142
+ const cmd = process.argv[2];
143
+ if (cmd === "init") {
144
+ init();
145
+ } else if (cmd === "sign") {
146
+ sign(parseArgs(process.argv.slice(3)));
147
+ } else {
148
+ console.error("Usage:");
149
+ console.error(" generate-entitlement-fixture.mjs init");
150
+ console.error(" generate-entitlement-fixture.mjs sign --account-id <id> --email <e> --tier <t> [--plugins a,b] [--days N] --out <path>");
151
+ process.exit(1);
152
+ }