@rubytech/create-maxy-code 0.1.444 → 0.1.446
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/graph-style/dist/index.d.ts +4 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +37 -1
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +39 -0
- package/payload/platform/lib/graph-style/src/index.ts +41 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +46 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/authorize.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/authorize.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/authorize.test.js +27 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/authorize.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +51 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-credential.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-credential.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-credential.test.js +39 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/house-credential.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/registry.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/registry.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/registry.test.js +59 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/registry.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate-run.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate-run.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate-run.test.js +77 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate-run.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.js +23 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/remediate.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/audit.d.ts +19 -0
- package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/audit.js +41 -0
- package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/authorize.d.ts +11 -0
- package/payload/platform/lib/storage-broker/dist/authorize.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/authorize.js +17 -0
- package/payload/platform/lib/storage-broker/dist/authorize.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.d.ts +25 -0
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.js +131 -0
- package/payload/platform/lib/storage-broker/dist/bin/remediate-run.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +20 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +53 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/house-credential.d.ts +6 -0
- package/payload/platform/lib/storage-broker/dist/house-credential.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/house-credential.js +33 -0
- package/payload/platform/lib/storage-broker/dist/house-credential.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts +7 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/index.js +30 -0
- package/payload/platform/lib/storage-broker/dist/index.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/registry.d.ts +18 -0
- package/payload/platform/lib/storage-broker/dist/registry.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/registry.js +45 -0
- package/payload/platform/lib/storage-broker/dist/registry.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/remediate.d.ts +7 -0
- package/payload/platform/lib/storage-broker/dist/remediate.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/remediate.js +25 -0
- package/payload/platform/lib/storage-broker/dist/remediate.js.map +1 -0
- package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +49 -0
- package/payload/platform/lib/storage-broker/src/__tests__/authorize.test.ts +28 -0
- package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +52 -0
- package/payload/platform/lib/storage-broker/src/__tests__/house-credential.test.ts +35 -0
- package/payload/platform/lib/storage-broker/src/__tests__/registry.test.ts +64 -0
- package/payload/platform/lib/storage-broker/src/__tests__/remediate-run.test.ts +79 -0
- package/payload/platform/lib/storage-broker/src/__tests__/remediate.test.ts +21 -0
- package/payload/platform/lib/storage-broker/src/audit.ts +55 -0
- package/payload/platform/lib/storage-broker/src/authorize.ts +22 -0
- package/payload/platform/lib/storage-broker/src/bin/remediate-run.ts +156 -0
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +73 -0
- package/payload/platform/lib/storage-broker/src/house-credential.ts +32 -0
- package/payload/platform/lib/storage-broker/src/index.ts +14 -0
- package/payload/platform/lib/storage-broker/src/registry.ts +85 -0
- package/payload/platform/lib/storage-broker/src/remediate.ts +31 -0
- package/payload/platform/lib/storage-broker/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +13 -3
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +8 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +20 -3
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +4 -1
- package/payload/platform/plugins/cloudflare/bin/__tests__/cf-schema-check.test.sh +128 -0
- package/payload/platform/plugins/cloudflare/bin/__tests__/cf-store-name.test.sh +77 -0
- package/payload/platform/plugins/cloudflare/bin/cf-schema-check.sh +60 -0
- package/payload/platform/plugins/cloudflare/bin/cf-store-name.sh +37 -0
- package/payload/platform/plugins/cloudflare/bin/remediate-storage-isolation.sh +36 -0
- package/payload/platform/plugins/cloudflare/references/api.md +4 -0
- package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +58 -0
- package/payload/platform/plugins/cloudflare/references/r2-object-storage.md +39 -0
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +2 -0
- package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +2 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +23 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.js +34 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-registry.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +26 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +20 -3
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +7 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +4 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +9 -3
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +11 -4
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +15 -10
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/plugins/storage-broker/.claude-plugin/plugin.json +21 -0
- package/payload/platform/plugins/storage-broker/PLUGIN.md +48 -0
- package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +193 -0
- package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/package.json +3 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/caller-scope-headers.test.d.ts +2 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/caller-scope-headers.test.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/caller-scope-headers.test.js +16 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/caller-scope-headers.test.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/call-api.d.ts +8 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/call-api.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/call-api.js +39 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/call-api.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/caller-scope-headers.d.ts +7 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/caller-scope-headers.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/caller-scope-headers.js +16 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/caller-scope-headers.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.d.ts +8 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.js +98 -0
- package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/storage-broker/mcp/package.json +10 -0
- package/payload/platform/plugins/storage-broker/mcp/vitest.config.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +6 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/server/chunk-4W6IEUFE.js +175 -0
- package/payload/server/public/assets/{AdminLoginScreens-BJVwtpLH.js → AdminLoginScreens-D9L-CuWi.js} +1 -1
- package/payload/server/public/assets/{AdminShell-CMu8_Xwn.js → AdminShell-DSQrvZXq.js} +1 -1
- package/payload/server/public/assets/{Checkbox-CzD0ORqA.js → Checkbox-CFdt8ljo.js} +1 -1
- package/payload/server/public/assets/{admin-D0yk2LMq.js → admin-Ch4n-G0j.js} +1 -1
- package/payload/server/public/assets/{browser-CvfbgznO.js → browser-B2AIxiuk.js} +1 -1
- package/payload/server/public/assets/{calendar-fw4WethJ.js → calendar-BvItH3B0.js} +1 -1
- package/payload/server/public/assets/chat-eQFMaX9y.js +1 -0
- package/payload/server/public/assets/chevron-left-D2AnCEyE.js +1 -0
- package/payload/server/public/assets/data-BzOmbHoe.js +1 -0
- package/payload/server/public/assets/{file-download-Px4s0Bhp.js → file-download-CK9j_Jx2.js} +1 -1
- package/payload/server/public/assets/graph-DrE7KupQ.js +51 -0
- package/payload/server/public/assets/graph-labels-CwEtvIRV.js +1 -0
- package/payload/server/public/assets/{maximize-2-BwphzI9S.js → maximize-2-CuzEom3K.js} +1 -1
- package/payload/server/public/assets/{operator-88m0DMl2.js → operator-DlboYPPu.js} +1 -1
- package/payload/server/public/assets/page-BuWH5gNK.js +1 -0
- package/payload/server/public/assets/page-Co9E8U5x.js +32 -0
- package/payload/server/public/assets/{public-BX2MjsFY.js → public-DtFpPMTG.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-C7BqkMec.js → rotate-ccw-Cqp4oqHV.js} +1 -1
- package/payload/server/public/assets/{tasks-DlgiCafL.js → tasks-bQKOhMga.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-CyNYVDkq.js → time-entry-format-BIL4ZaDK.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-rB8QuqyH.js +1 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-1izQvPiD.css → useSubAccountSwitcher-CVHKTSst.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-XgpWvXpk.js → useVoiceRecorder-Ctny8qWx.js} +1 -1
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +10 -10
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +13 -13
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/server.js +1033 -837
- package/payload/server/src-BQ2NYMOT.js +24 -0
- package/payload/server/public/assets/chat-Dq-X-ia8.js +0 -1
- package/payload/server/public/assets/chevron-left-B3xSeEDR.js +0 -1
- package/payload/server/public/assets/data-CUnDzDox.js +0 -1
- package/payload/server/public/assets/graph-DbT8nA6R.js +0 -51
- package/payload/server/public/assets/graph-labels-C1YIyLiA.js +0 -1
- package/payload/server/public/assets/page-BRfxFXyi.js +0 -32
- package/payload/server/public/assets/page-CfQFAJia.js +0 -1
- package/payload/server/public/assets/useCopyFeedback-ByGUMN94.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-s6WlRn52.js → useSubAccountSwitcher-CHEt_m6q.js} +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The only place the account-wide Cloudflare credential is used to touch
|
|
2
|
+
// Cloudflare. Every method injects the house token into the child env and shells
|
|
3
|
+
// wrangler. `run` is injectable so the wiring is unit-testable without wrangler.
|
|
4
|
+
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
import type { HouseCredential } from "./house-credential.js";
|
|
7
|
+
|
|
8
|
+
export type RunFn = (
|
|
9
|
+
cmd: string,
|
|
10
|
+
args: string[],
|
|
11
|
+
env: Record<string, string>,
|
|
12
|
+
) => Promise<{ stdout: string }>;
|
|
13
|
+
|
|
14
|
+
export interface CfExec {
|
|
15
|
+
d1List(): Promise<{ name: string; uuid: string }[]>;
|
|
16
|
+
d1Create(name: string): Promise<{ uuid: string }>;
|
|
17
|
+
d1Query(name: string, sql: string): Promise<unknown>;
|
|
18
|
+
r2List(): Promise<{ name: string }[]>;
|
|
19
|
+
r2Create(name: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const defaultRun: RunFn = (cmd, args, env) =>
|
|
23
|
+
new Promise((resolve, reject) => {
|
|
24
|
+
execFile(cmd, args, { env }, (err, stdout, stderr) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
reject(new Error(`${cmd} ${args.join(" ")} failed: ${err.message}\n${stderr}`));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
resolve({ stdout: stdout.toString() });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function parseJson(raw: string, context: string): unknown {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(raw);
|
|
36
|
+
} catch {
|
|
37
|
+
throw new Error(`storage-broker: ${context} returned non-JSON output: ${raw}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function makeCfExec(cred: HouseCredential, run: RunFn = defaultRun): CfExec {
|
|
42
|
+
const env = {
|
|
43
|
+
...process.env,
|
|
44
|
+
CLOUDFLARE_API_TOKEN: cred.apiToken,
|
|
45
|
+
CLOUDFLARE_ACCOUNT_ID: cred.accountId,
|
|
46
|
+
} as Record<string, string>;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
async d1List() {
|
|
50
|
+
const { stdout } = await run("wrangler", ["d1", "list", "--json"], env);
|
|
51
|
+
return parseJson(stdout, "d1 list") as { name: string; uuid: string }[];
|
|
52
|
+
},
|
|
53
|
+
async d1Create(name) {
|
|
54
|
+
const { stdout } = await run("wrangler", ["d1", "create", name, "--json"], env);
|
|
55
|
+
return parseJson(stdout, "d1 create") as { uuid: string };
|
|
56
|
+
},
|
|
57
|
+
async d1Query(name, sql) {
|
|
58
|
+
const { stdout } = await run(
|
|
59
|
+
"wrangler",
|
|
60
|
+
["d1", "execute", name, "--remote", "--json", "--command", sql],
|
|
61
|
+
env,
|
|
62
|
+
);
|
|
63
|
+
return parseJson(stdout, "d1 execute");
|
|
64
|
+
},
|
|
65
|
+
async r2List() {
|
|
66
|
+
const { stdout } = await run("wrangler", ["r2", "bucket", "list", "--json"], env);
|
|
67
|
+
return parseJson(stdout, "r2 bucket list") as { name: string }[];
|
|
68
|
+
},
|
|
69
|
+
async r2Create(name) {
|
|
70
|
+
await run("wrangler", ["r2", "bucket", "create", name], env);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Reads the single account-wide Cloudflare credential from the house-only
|
|
2
|
+
// config directory. This file is the one place the account-wide capability
|
|
3
|
+
// lives; sub-account spawns never source it. Parses KEY=value lines without
|
|
4
|
+
// polluting process.env (same discipline as reconcile-bookings' readEnvFile).
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
export interface HouseCredential {
|
|
10
|
+
apiToken: string;
|
|
11
|
+
accountId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function readHouseCredential(platformRoot: string): HouseCredential {
|
|
15
|
+
const path = join(platformRoot, "config", "cloudflare-house.env");
|
|
16
|
+
let raw: string;
|
|
17
|
+
try {
|
|
18
|
+
raw = readFileSync(path, "utf8");
|
|
19
|
+
} catch {
|
|
20
|
+
throw new Error(`storage-broker: house credential not found at ${path}`);
|
|
21
|
+
}
|
|
22
|
+
const env: Record<string, string> = {};
|
|
23
|
+
for (const line of raw.split("\n")) {
|
|
24
|
+
const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
25
|
+
if (m) env[m[1]] = m[2].trim();
|
|
26
|
+
}
|
|
27
|
+
const apiToken = env.CLOUDFLARE_API_TOKEN;
|
|
28
|
+
const accountId = env.CLOUDFLARE_ACCOUNT_ID;
|
|
29
|
+
if (!apiToken) throw new Error(`storage-broker: CLOUDFLARE_API_TOKEN missing in ${path}`);
|
|
30
|
+
if (!accountId) throw new Error(`storage-broker: CLOUDFLARE_ACCOUNT_ID missing in ${path}`);
|
|
31
|
+
return { apiToken, accountId };
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// storage-broker: the per-account Cloudflare D1/R2 isolation core.
|
|
2
|
+
//
|
|
3
|
+
// The lib is pure and dependency-free at runtime: it opens no Neo4j connection
|
|
4
|
+
// and shells no process on its own. Consumers inject a SessionLike (the ui
|
|
5
|
+
// route uses neo4j-store's getSession; the remediation entry opens its own
|
|
6
|
+
// driver) and, for cf-exec, a RunFn. That keeps every unit testable in
|
|
7
|
+
// isolation and the isolation logic auditable in one place.
|
|
8
|
+
|
|
9
|
+
export * from "./authorize.js";
|
|
10
|
+
export * from "./registry.js";
|
|
11
|
+
export * from "./house-credential.js";
|
|
12
|
+
export * from "./cf-exec.js";
|
|
13
|
+
export * from "./audit.js";
|
|
14
|
+
export * from "./remediate.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// The ownership registry: which account owns which Cloudflare storage resource.
|
|
2
|
+
//
|
|
3
|
+
// Written with raw cypher MERGE stamped with system provenance, mirroring the
|
|
4
|
+
// house-level seedAccountGraphRoot path in the admin plugin. It deliberately
|
|
5
|
+
// does NOT go through graph-write's writeNodeWithEdges: that primitive's
|
|
6
|
+
// accountId floor rejects a write whose props.accountId differs from the
|
|
7
|
+
// writer process's ACCOUNT_ID, and the broker runs house-level, writing nodes
|
|
8
|
+
// owned by many different sub-accounts. The owning accountId here is always the
|
|
9
|
+
// trusted header value the service resolved, never an agent argument.
|
|
10
|
+
|
|
11
|
+
// A minimal shape of a neo4j-driver Session, so this lib carries no runtime or
|
|
12
|
+
// type dependency on neo4j-driver (mirrors the SeedSessionLike pattern used in
|
|
13
|
+
// platform/plugins/admin/mcp/src/tools/account-lifecycle.ts).
|
|
14
|
+
// Uses PromiseLike (not Promise) and get(): any so that a real neo4j-driver
|
|
15
|
+
// Session — whose run() returns a Result (a PromiseLike, and whose records are
|
|
16
|
+
// driver Records) — is structurally assignable under both v5 and v6 without a
|
|
17
|
+
// cast at the call site.
|
|
18
|
+
export interface SessionLike {
|
|
19
|
+
run(
|
|
20
|
+
cypher: string,
|
|
21
|
+
params?: Record<string, unknown>,
|
|
22
|
+
): PromiseLike<{ records: Array<{ get(key: string): any }> }>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ResourceKind = "d1" | "r2";
|
|
26
|
+
|
|
27
|
+
export interface StorageResource {
|
|
28
|
+
accountId: string;
|
|
29
|
+
kind: ResourceKind;
|
|
30
|
+
name: string;
|
|
31
|
+
cfResourceId: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function registerResource(
|
|
35
|
+
session: SessionLike,
|
|
36
|
+
r: StorageResource,
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
// MERGE on (kind, name) — the ownership key. On a shared Cloudflare account a
|
|
39
|
+
// storage name maps to exactly one owner, so the write key must match the
|
|
40
|
+
// resolveOwner lookup key; keying on accountId too would let two accounts hold
|
|
41
|
+
// a node for the same name and make ownership ambiguous. accountId is set only
|
|
42
|
+
// ON CREATE and never overwritten ON MATCH, so a register can never silently
|
|
43
|
+
// reassign an existing resource to a different account.
|
|
44
|
+
await session.run(
|
|
45
|
+
`MERGE (r:StorageResource {kind: $kind, name: $name})
|
|
46
|
+
ON CREATE SET r.accountId = $accountId,
|
|
47
|
+
r.createdByAgent = 'system',
|
|
48
|
+
r.createdBySource = 'storage-broker',
|
|
49
|
+
r.cfResourceId = $cfResourceId
|
|
50
|
+
ON MATCH SET r.cfResourceId = $cfResourceId`,
|
|
51
|
+
{ accountId: r.accountId, kind: r.kind, name: r.name, cfResourceId: r.cfResourceId },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function resolveOwner(
|
|
56
|
+
session: SessionLike,
|
|
57
|
+
kind: ResourceKind,
|
|
58
|
+
name: string,
|
|
59
|
+
): Promise<string | null> {
|
|
60
|
+
const res = await session.run(
|
|
61
|
+
`MATCH (r:StorageResource {kind: $kind, name: $name})
|
|
62
|
+
RETURN r.accountId AS accountId LIMIT 1`,
|
|
63
|
+
{ kind, name },
|
|
64
|
+
);
|
|
65
|
+
return res.records.length ? (res.records[0].get("accountId") as string) : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function listResources(
|
|
69
|
+
session: SessionLike,
|
|
70
|
+
accountId: string,
|
|
71
|
+
kind: ResourceKind,
|
|
72
|
+
): Promise<StorageResource[]> {
|
|
73
|
+
const res = await session.run(
|
|
74
|
+
`MATCH (r:StorageResource {accountId: $accountId, kind: $kind})
|
|
75
|
+
RETURN r.name AS name, r.cfResourceId AS cfResourceId
|
|
76
|
+
ORDER BY r.name`,
|
|
77
|
+
{ accountId, kind },
|
|
78
|
+
);
|
|
79
|
+
return res.records.map((rec) => ({
|
|
80
|
+
accountId,
|
|
81
|
+
kind,
|
|
82
|
+
name: rec.get("name") as string,
|
|
83
|
+
cfResourceId: rec.get("cfResourceId") as string,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The testable core of the operator-run remediation. The mapping from existing
|
|
2
|
+
// resource name to owning account is operator-authored, and validation fails
|
|
3
|
+
// closed on any resource absent from it: ownership is a recorded fact, never a
|
|
4
|
+
// name-prefix guess. Token stripping removes every account-wide credential from
|
|
5
|
+
// a sub-account secrets file while preserving everything else verbatim.
|
|
6
|
+
|
|
7
|
+
export interface MappingResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
missing: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Account-wide credentials stripped from a sub-account's cloudflare.env.
|
|
13
|
+
// CLOUDFLARE_ACCOUNT_ID is intentionally kept: it is an identifier, not a
|
|
14
|
+
// credential, and does not grant access on its own.
|
|
15
|
+
const STRIP_KEYS = ["CLOUDFLARE_API_TOKEN", "CF_PAGES_D1_TOKEN", "CF_PAGES_TOKEN"];
|
|
16
|
+
|
|
17
|
+
export function validateMapping(
|
|
18
|
+
enumerated: string[],
|
|
19
|
+
mapping: Record<string, string>,
|
|
20
|
+
): MappingResult {
|
|
21
|
+
const missing = enumerated.filter((name) => !(name in mapping));
|
|
22
|
+
return { ok: missing.length === 0, missing };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function stripAccountWideTokens(contents: string): string {
|
|
26
|
+
const kept = contents.split("\n").filter((line) => {
|
|
27
|
+
const key = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=/)?.[1];
|
|
28
|
+
return !(key && STRIP_KEYS.includes(key));
|
|
29
|
+
});
|
|
30
|
+
return kept.join("\n");
|
|
31
|
+
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"services/*"
|
|
7
7
|
],
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/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/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json",
|
|
9
|
+
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/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/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json",
|
|
11
11
|
"gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
|
|
12
12
|
"build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
|
|
13
13
|
"build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
|
|
@@ -28,7 +28,7 @@ import { publishSite } from "./tools/publish-site.js";
|
|
|
28
28
|
import { resolveCapabilitiesHere } from "./tools/capabilities-here.js";
|
|
29
29
|
import { runCatalogueCensus } from "./lib/catalogue-census.js";
|
|
30
30
|
import { lifelineTool } from "../../../../lib/mcp-lifeline/dist/index.js";
|
|
31
|
-
import { listAccountsFromDir, isHouseAccount, archiveAccountDir, provisionClientAccount, findHouseAccount, getChannelRoutingAccountId, setChannelRoutingAccountId, seedAccountGraphRoot, isPlaceholderOwnerName, refuseClientAdminAdd, readHouseAdminUserIds, setManagingAdmin, findClientAccountsManagedBy, reassignManagingAdmin, ACCOUNT_NODE_COUNT_CYPHER, ACCOUNT_PURGE_CYPHER, classifyManagingAdmin, ownerNameStatus, readManagingAdminUserId, setOwnerName, } from "./tools/account-lifecycle.js";
|
|
31
|
+
import { listAccountsFromDir, isHouseAccount, archiveAccountDir, provisionClientAccount, findHouseAccount, getChannelRoutingAccountId, setChannelRoutingAccountId, seedAccountGraphRoot, isPlaceholderOwnerName, isBlankBusinessName, refuseClientAdminAdd, readHouseAdminUserIds, setManagingAdmin, findClientAccountsManagedBy, reassignManagingAdmin, ACCOUNT_NODE_COUNT_CYPHER, ACCOUNT_PURGE_CYPHER, classifyManagingAdmin, ownerNameStatus, readManagingAdminUserId, setOwnerName, } from "./tools/account-lifecycle.js";
|
|
32
32
|
function refusalGuidance(kind) {
|
|
33
33
|
switch (kind) {
|
|
34
34
|
case "unsafe-slug":
|
|
@@ -3416,10 +3416,11 @@ eagerTool(server, "account_roster", "Canonical per-account identity roster for t
|
|
|
3416
3416
|
process.stderr.write(`${TAG} op=roster accounts=${accounts.length} nameless-owners=${namelessOwners} absent-mgr=${absentMgr} dangling-mgr=${danglingMgr}\n`);
|
|
3417
3417
|
return { content: [{ type: "text", text: accounts.length ? `Account roster:\n\n${lines.join("\n")}` : "No accounts found." }] };
|
|
3418
3418
|
});
|
|
3419
|
-
eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this install (managed service). REQUIRES `ownerName`, the real name of the client business's owner (ask the operator for
|
|
3419
|
+
eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this install (managed service). REQUIRES `businessName`, the real name of the client business itself, and `ownerName`, the real name of the client business's owner (ask the operator for both — never invent them or reuse the house operator's name). OPTIONALLY takes `adminUserId`, the userId of the house team member who will manage this client (get the choices from admin-list; ask the operator, but omit it if the client is run by its owner rather than a house admin). Mints a UUID, scaffolds the account directory (agents, specialists, settings, account.json with role:\"client\", and managingAdminUserId when an admin is named), seeds default plugins, and seeds the account's graph root (a :LocalBusiness carrying businessName plus its owner :AdminUser and owner :Person carrying ownerName) so you can populate it immediately and the sub-account picker shows the business name at once. Refuses if businessName is missing/blank, if ownerName is missing/blank/the placeholder \"Owner\", or if a supplied adminUserId is not a current house admin. When no managing admin is set, that account's passive-intake tasks route to the install's primary admin. Returns the new accountId. Operator-only; never creates the house sub-account (that is the install itself).", {
|
|
3420
3420
|
adminUserId: z.string().optional().describe("The userId of the house admin who will manage this client sub-account. Optional — omit it for a client run by its owner rather than a house admin (its passive-intake tasks then route to the install's primary admin). When supplied, it must be a current house admin (see admin-list) or creation is refused."),
|
|
3421
|
+
businessName: z.string().describe("The real name of the client business itself. Written onto the new sub-account's :LocalBusiness so the sub-account picker/header shows it immediately. Required — creation is refused when missing or blank. Ask the operator for the client's real business name; never invent it."),
|
|
3421
3422
|
ownerName: z.string().describe("The real name of the client business's owner. Applied to both the owner AdminUser and owner Person for the new sub-account. Required — creation is refused when missing, blank, or the placeholder \"Owner\". Ask the operator; never reuse the house operator's name."),
|
|
3422
|
-
}, async ({ adminUserId, ownerName }) => {
|
|
3423
|
+
}, async ({ adminUserId, businessName, ownerName }) => {
|
|
3423
3424
|
if (!ACCOUNT_ID)
|
|
3424
3425
|
return refuseNoAccount("account_create");
|
|
3425
3426
|
// Task 1630 — a managing admin is optional. `adminUserId` omitted/blank ⇒ the
|
|
@@ -3436,6 +3437,14 @@ eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this
|
|
|
3436
3437
|
process.stderr.write(`[account-create] op=refused reason=no-owner-name\n`);
|
|
3437
3438
|
return { content: [{ type: "text", text: `account_create refused: give the client business owner's real name (ownerName). It cannot be blank or the placeholder "Owner". Ask the operator whose business this is.` }], isError: true };
|
|
3438
3439
|
}
|
|
3440
|
+
// Task 1647 — the client business's own name is an equal hard precondition,
|
|
3441
|
+
// refused before any scaffold so a nameless create leaves no partial account
|
|
3442
|
+
// directory or rootless graph behind. The same predicate guards the seed
|
|
3443
|
+
// (seedAccountGraphRoot) as defence in depth.
|
|
3444
|
+
if (isBlankBusinessName(businessName)) {
|
|
3445
|
+
process.stderr.write(`[account-create] op=refused reason=no-business-name\n`);
|
|
3446
|
+
return { content: [{ type: "text", text: `account_create refused: give the client business's real name (businessName). It cannot be blank. Ask the operator what the client business is called.` }], isError: true };
|
|
3447
|
+
}
|
|
3439
3448
|
if (managingAdminUserId && !readHouseAdminUserIds(ACCOUNTS_ROOT).includes(managingAdminUserId)) {
|
|
3440
3449
|
process.stderr.write(`[account-lifecycle] op=create-refused reason=unknown-admin adminUserId=${managingAdminUserId.slice(0, 8)}\n`);
|
|
3441
3450
|
return { content: [{ type: "text", text: `account_create refused: ${managingAdminUserId} is not a current house admin. Call admin-list for the valid managing-admin choices, or omit adminUserId to leave the client unmanaged.` }], isError: true };
|
|
@@ -3474,6 +3483,7 @@ eagerTool(server, "account_create", "Provision a new CLIENT sub-account on this
|
|
|
3474
3483
|
accountId: newId,
|
|
3475
3484
|
ownerUserId,
|
|
3476
3485
|
ownerName,
|
|
3486
|
+
businessName,
|
|
3477
3487
|
createdAt: new Date().toISOString(),
|
|
3478
3488
|
});
|
|
3479
3489
|
rootSeeded = businessSeeded && adminSeeded;
|