@rubytech/create-maxy-code 0.1.411 → 0.1.414

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 (162) hide show
  1. package/dist/__tests__/installer-cwd-pin-and-auth-verify.test.js +82 -0
  2. package/dist/index.js +44 -5
  3. package/package.json +1 -1
  4. package/payload/platform/lib/account-scope/dist/index.d.ts +27 -0
  5. package/payload/platform/lib/account-scope/dist/index.d.ts.map +1 -0
  6. package/payload/platform/lib/account-scope/dist/index.js +33 -0
  7. package/payload/platform/lib/account-scope/dist/index.js.map +1 -0
  8. package/payload/platform/lib/account-scope/src/index.ts +43 -0
  9. package/payload/platform/lib/account-scope/tsconfig.json +5 -0
  10. package/payload/platform/package.json +2 -2
  11. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  12. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  13. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +37 -1
  14. package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +2 -0
  15. package/payload/platform/plugins/docs/references/cross-account-authority.md +25 -0
  16. package/payload/platform/plugins/docs/references/memory-guide.md +6 -0
  17. package/payload/platform/plugins/filesystem/.claude-plugin/plugin.json +21 -0
  18. package/payload/platform/plugins/filesystem/PLUGIN.md +63 -0
  19. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +193 -0
  20. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/package.json +3 -0
  21. package/payload/platform/plugins/filesystem/mcp/dist/index.d.ts +2 -0
  22. package/payload/platform/plugins/filesystem/mcp/dist/index.d.ts.map +1 -0
  23. package/payload/platform/plugins/filesystem/mcp/dist/index.js +91 -0
  24. package/payload/platform/plugins/filesystem/mcp/dist/index.js.map +1 -0
  25. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.d.ts +2 -0
  26. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.d.ts.map +1 -0
  27. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js +89 -0
  28. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js.map +1 -0
  29. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts +12 -0
  30. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts.map +1 -0
  31. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js +6 -0
  32. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js.map +1 -0
  33. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts +45 -0
  34. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts.map +1 -0
  35. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js +86 -0
  36. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js.map +1 -0
  37. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.d.ts +11 -0
  38. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.d.ts.map +1 -0
  39. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.js +64 -0
  40. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.js.map +1 -0
  41. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.d.ts +2 -0
  42. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.d.ts.map +1 -0
  43. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js +94 -0
  44. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js.map +1 -0
  45. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts +8 -0
  46. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts.map +1 -0
  47. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js +40 -0
  48. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js.map +1 -0
  49. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.d.ts +6 -0
  50. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.d.ts.map +1 -0
  51. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.js +13 -0
  52. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.js.map +1 -0
  53. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.d.ts +7 -0
  54. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.d.ts.map +1 -0
  55. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.js +71 -0
  56. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.js.map +1 -0
  57. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.d.ts +6 -0
  58. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.d.ts.map +1 -0
  59. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.js +26 -0
  60. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.js.map +1 -0
  61. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.d.ts +6 -0
  62. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.d.ts.map +1 -0
  63. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js +28 -0
  64. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js.map +1 -0
  65. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts +7 -0
  66. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -0
  67. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +25 -0
  68. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -0
  69. package/payload/platform/plugins/filesystem/mcp/package.json +21 -0
  70. package/payload/platform/plugins/filesystem/mcp/vitest.config.ts +8 -0
  71. package/payload/platform/plugins/memory/PLUGIN.md +8 -0
  72. package/payload/platform/plugins/memory/mcp/dist/index-testable.d.ts +34 -0
  73. package/payload/platform/plugins/memory/mcp/dist/index-testable.d.ts.map +1 -0
  74. package/payload/platform/plugins/memory/mcp/dist/index-testable.js +60 -0
  75. package/payload/platform/plugins/memory/mcp/dist/index-testable.js.map +1 -0
  76. package/payload/platform/plugins/memory/mcp/dist/index.js +327 -146
  77. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-owner-userid.d.ts +15 -0
  79. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-owner-userid.d.ts.map +1 -0
  80. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-owner-userid.js +29 -0
  81. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-owner-userid.js.map +1 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.d.ts +2 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.d.ts.map +1 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js +74 -0
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js.map +1 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-owner-userid.test.d.ts +2 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-owner-userid.test.d.ts.map +1 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-owner-userid.test.js +40 -0
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-owner-userid.test.js.map +1 -0
  90. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.d.ts +2 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.d.ts.map +1 -0
  92. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js +54 -0
  93. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js.map +1 -0
  94. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +11 -0
  95. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  96. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +13 -1
  97. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  98. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +2 -0
  99. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  100. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +7 -0
  101. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  102. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.js +6 -1
  104. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.d.ts +7 -1
  106. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.d.ts.map +1 -1
  107. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.js +7 -2
  108. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.js.map +1 -1
  109. package/payload/platform/services/claude-session-manager/dist/config.d.ts +37 -0
  110. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  111. package/payload/platform/services/claude-session-manager/dist/config.js +34 -1
  112. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  113. package/payload/platform/services/claude-session-manager/dist/cross-account-census.d.ts +34 -0
  114. package/payload/platform/services/claude-session-manager/dist/cross-account-census.d.ts.map +1 -0
  115. package/payload/platform/services/claude-session-manager/dist/cross-account-census.js +98 -0
  116. package/payload/platform/services/claude-session-manager/dist/cross-account-census.js.map +1 -0
  117. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  118. package/payload/platform/services/claude-session-manager/dist/http-server.js +10 -0
  119. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  120. package/payload/platform/services/claude-session-manager/dist/index.js +17 -0
  121. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  122. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  123. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +25 -0
  124. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  125. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +7 -0
  126. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
  127. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
  128. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
  129. package/payload/server/{chunk-ZYS5V5QG.js → chunk-3Z5WE53X.js} +4 -0
  130. package/payload/server/maxy-edge.js +1 -1
  131. package/payload/server/public/assets/{AdminLoginScreens-BRQgI2lW.js → AdminLoginScreens-CYPTMmTk.js} +1 -1
  132. package/payload/server/public/assets/{AdminShell-By9n5icG.js → AdminShell-CZH_EZIy.js} +1 -1
  133. package/payload/server/public/assets/{Checkbox-DjEWgYu1.js → Checkbox-B0pbYJDQ.js} +1 -1
  134. package/payload/server/public/assets/{Transcript-BejWvOaO.js → Transcript-CYaAPBFX.js} +1 -1
  135. package/payload/server/public/assets/{admin-BFEMryQm.js → admin-BKDMSRto.js} +1 -1
  136. package/payload/server/public/assets/{browser-CMJMEUz6.js → browser-CxeMDDZA.js} +1 -1
  137. package/payload/server/public/assets/{calendar-C6ZGlHEP.js → calendar-DiIiTY9-.js} +1 -1
  138. package/payload/server/public/assets/chat-C56Oqvt1.js +1 -0
  139. package/payload/server/public/assets/chevron-left-Da-iZGi_.js +1 -0
  140. package/payload/server/public/assets/data-ZUdT44Ba.js +1 -0
  141. package/payload/server/public/assets/{graph-DIrXtPLv.js → graph-DC3dwnMZ.js} +1 -1
  142. package/payload/server/public/assets/{graph-labels-BtPf1Jom.js → graph-labels-Csnovm_c.js} +1 -1
  143. package/payload/server/public/assets/{maximize-2-DeJ4K8kL.js → maximize-2-WRUJsg5n.js} +1 -1
  144. package/payload/server/public/assets/{operator-B8QhqzLI.js → operator-BpzeHTAw.js} +1 -1
  145. package/payload/server/public/assets/{page-iFOy0dFx.js → page-8jJOxx6y.js} +1 -1
  146. package/payload/server/public/assets/{page-Df6KYkf4.js → page-IQDLdebG.js} +1 -1
  147. package/payload/server/public/assets/{public-BrUIRQZd.js → public-Dqg7jwjq.js} +1 -1
  148. package/payload/server/public/assets/{rotate-ccw-CYfntpwu.js → rotate-ccw-BayMWwfc.js} +1 -1
  149. package/payload/server/public/assets/{useSubAccountSwitcher-Cvnc_fwQ.css → useSubAccountSwitcher-CRZfyJXR.css} +1 -1
  150. package/payload/server/public/browser.html +4 -4
  151. package/payload/server/public/calendar.html +5 -5
  152. package/payload/server/public/chat.html +9 -9
  153. package/payload/server/public/data.html +8 -8
  154. package/payload/server/public/graph.html +9 -9
  155. package/payload/server/public/index.html +11 -11
  156. package/payload/server/public/operator.html +11 -11
  157. package/payload/server/public/public.html +9 -9
  158. package/payload/server/server.js +72 -15
  159. package/payload/server/public/assets/chat-B7s62Nnp.js +0 -1
  160. package/payload/server/public/assets/chevron-left-Dfjcaiyr.js +0 -1
  161. package/payload/server/public/assets/data-741ZbBwR.js +0 -1
  162. /package/payload/server/public/assets/{useSubAccountSwitcher-BK_KfOLC.js → useSubAccountSwitcher-C9aLg8yI.js} +0 -0
@@ -0,0 +1,64 @@
1
+ // Recursive-walk + glob helpers for the glob/grep tools. Both are rooted at an
2
+ // already-resolved account dir and NEVER follow symlinks, so the walk cannot
3
+ // leave the account directory even if a symlink points outside.
4
+ import { readdirSync } from "node:fs";
5
+ import { join, relative, sep } from "node:path";
6
+ /**
7
+ * List regular files under `root`, returning paths RELATIVE to root with "/"
8
+ * separators. Symlinked entries are skipped (no escape). Stops at `cap` files.
9
+ */
10
+ export function walkFiles(root, cap) {
11
+ const out = [];
12
+ const stack = [root];
13
+ while (stack.length && out.length < cap) {
14
+ const dir = stack.pop();
15
+ let entries;
16
+ try {
17
+ entries = readdirSync(dir, { withFileTypes: true });
18
+ }
19
+ catch {
20
+ continue;
21
+ }
22
+ for (const e of entries) {
23
+ if (out.length >= cap)
24
+ break;
25
+ if (e.isSymbolicLink())
26
+ continue; // never follow a symlink out of root
27
+ const full = join(dir, e.name);
28
+ if (e.isDirectory())
29
+ stack.push(full);
30
+ else if (e.isFile())
31
+ out.push(relative(root, full).split(sep).join("/"));
32
+ }
33
+ }
34
+ return out;
35
+ }
36
+ /**
37
+ * Translate a glob (`**`, `*`, `?`) to an anchored RegExp over "/"-joined
38
+ * relative paths. `*` and `?` do not cross "/"; `**` crosses any depth.
39
+ */
40
+ export function globToRegExp(glob) {
41
+ let re = "";
42
+ for (let i = 0; i < glob.length; i++) {
43
+ const c = glob[i];
44
+ if (c === "*") {
45
+ if (glob[i + 1] === "*") {
46
+ re += ".*";
47
+ i++;
48
+ if (glob[i + 1] === "/")
49
+ i++; // absorb the slash after **
50
+ }
51
+ else {
52
+ re += "[^/]*";
53
+ }
54
+ }
55
+ else if (c === "?") {
56
+ re += "[^/]";
57
+ }
58
+ else {
59
+ re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
60
+ }
61
+ }
62
+ return new RegExp("^" + re + "$");
63
+ }
64
+ //# sourceMappingURL=walk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../../src/lib/walk.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,6EAA6E;AAC7E,gEAAgE;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,GAAW;IACjD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAa,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAY,CAAC;QAClC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;gBAAE,MAAM;YAC7B,IAAI,CAAC,CAAC,cAAc,EAAE;gBAAE,SAAS,CAAC,qCAAqC;YACvE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;iBACjC,IAAI,CAAC,CAAC,MAAM,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,EAAE,CAAC;gBACJ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG;oBAAE,CAAC,EAAE,CAAC,CAAC,4BAA4B;YAC5D,CAAC;iBAAM,CAAC;gBACN,EAAE,IAAI,OAAO,CAAC;YAChB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,EAAE,IAAI,MAAM,CAAC;QACf,CAAC;aAAM,CAAC;YACN,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=handlers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/handlers.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,94 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { fileRead } from "../file-read.js";
6
+ import { fileWrite } from "../file-write.js";
7
+ import { fileEdit } from "../file-edit.js";
8
+ import { fileList } from "../file-list.js";
9
+ import { fileGlob } from "../file-glob.js";
10
+ import { fileGrep } from "../file-grep.js";
11
+ const HOUSE = "11111111-1111-4111-8111-111111111111";
12
+ const CLIENT = "22222222-2222-4222-8222-222222222222";
13
+ let accountsDir;
14
+ beforeEach(() => {
15
+ accountsDir = mkdtempSync(join(tmpdir(), "xacct-handlers-"));
16
+ for (const id of [HOUSE, CLIENT]) {
17
+ mkdirSync(join(accountsDir, id), { recursive: true });
18
+ writeFileSync(join(accountsDir, id, "account.json"), JSON.stringify({ accountId: id }));
19
+ }
20
+ });
21
+ afterEach(() => rmSync(accountsDir, { recursive: true, force: true }));
22
+ const house = () => ({ ownAccountId: HOUSE, houseScope: HOUSE, accountsDir });
23
+ const client = () => ({ ownAccountId: CLIENT, houseScope: null, accountsDir });
24
+ const text = (r) => r.content[0].text;
25
+ describe("file handlers — own account round-trip", () => {
26
+ it("write → read → edit → list on the house's own account", () => {
27
+ expect(fileWrite({ path: "notes/a.txt", content: "hello world" }, house()).isError).toBeUndefined();
28
+ expect(existsSync(join(accountsDir, HOUSE, "notes/a.txt"))).toBe(true);
29
+ expect(text(fileRead({ path: "notes/a.txt" }, house()))).toBe("hello world");
30
+ expect(fileEdit({ path: "notes/a.txt", oldString: "world", newString: "there" }, house()).isError).toBeUndefined();
31
+ expect(readFileSync(join(accountsDir, HOUSE, "notes/a.txt"), "utf-8")).toBe("hello there");
32
+ const listed = text(fileList({ path: "notes" }, house()));
33
+ expect(listed).toContain("a.txt");
34
+ });
35
+ it("file-edit refuses a non-unique oldString", () => {
36
+ fileWrite({ path: "d.txt", content: "x x" }, house());
37
+ const r = fileEdit({ path: "d.txt", oldString: "x", newString: "y" }, house());
38
+ expect(r.isError).toBe(true);
39
+ expect(text(r)).toContain("not unique");
40
+ });
41
+ it("glob and grep find content in the account", () => {
42
+ fileWrite({ path: "docs/readme.md", content: "alpha\nbeta MATCHME gamma" }, house());
43
+ expect(text(fileGlob({ pattern: "**/*.md" }, house()))).toContain("docs/readme.md");
44
+ expect(text(fileGrep({ pattern: "MATCHME" }, house()))).toContain("docs/readme.md:2:");
45
+ });
46
+ it("grep with a path arg reports account-root-relative paths that round-trip", () => {
47
+ fileWrite({ path: "logs/app.log", content: "line1\nMATCHME here" }, house());
48
+ const hit = text(fileGrep({ pattern: "MATCHME", path: "logs" }, house()));
49
+ // root-relative, not subdir-relative — so it feeds straight back into file-read
50
+ expect(hit).toContain("logs/app.log:2:");
51
+ const reported = hit.split(":")[0];
52
+ expect(text(fileRead({ path: reported }, house()))).toContain("MATCHME");
53
+ });
54
+ it("file-edit rejects an empty oldString instead of silently splicing", () => {
55
+ fileWrite({ path: "e.txt", content: "ab" }, house());
56
+ const r = fileEdit({ path: "e.txt", oldString: "", newString: "X" }, house());
57
+ expect(r.isError).toBe(true);
58
+ expect(text(r)).toContain("must not be empty");
59
+ expect(readFileSync(join(accountsDir, HOUSE, "e.txt"), "utf-8")).toBe("ab");
60
+ });
61
+ });
62
+ describe("file handlers — cross-account gate", () => {
63
+ it("house admin writes into a sub-account by naming targetAccountId", () => {
64
+ const r = fileWrite({ path: "from-house.txt", content: "hi", targetAccountId: CLIENT }, house());
65
+ expect(r.isError).toBeUndefined();
66
+ expect(readFileSync(join(accountsDir, CLIENT, "from-house.txt"), "utf-8")).toBe("hi");
67
+ });
68
+ it("a non-house session naming a target is denied", () => {
69
+ const r = fileRead({ path: "account.json", targetAccountId: HOUSE }, client());
70
+ expect(r.isError).toBe(true);
71
+ expect(text(r)).toContain("cross-account-denied");
72
+ // and it did not read the house file
73
+ expect(text(r)).not.toContain("accountId");
74
+ });
75
+ it("an unknown target is invalid-target", () => {
76
+ const r = fileList({ targetAccountId: "99999999-9999-4999-8999-999999999999" }, house());
77
+ expect(r.isError).toBe(true);
78
+ expect(text(r)).toContain("cross-account-invalid-target");
79
+ });
80
+ });
81
+ describe("file handlers — traversal guard", () => {
82
+ it("write to a ../ escape is rejected, no file created outside the account", () => {
83
+ const r = fileWrite({ path: `../${CLIENT}/injected.txt`, content: "x" }, house());
84
+ expect(r.isError).toBe(true);
85
+ expect(text(r)).toContain("escapes the account directory");
86
+ expect(existsSync(join(accountsDir, CLIENT, "injected.txt"))).toBe(false);
87
+ });
88
+ it("read of an absolute path outside the account is rejected", () => {
89
+ const r = fileRead({ path: "/etc/hosts" }, house());
90
+ expect(r.isError).toBe(true);
91
+ expect(text(r)).toContain("escapes the account directory");
92
+ });
93
+ });
94
+ //# sourceMappingURL=handlers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/handlers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileEdit(args: {
3
+ path: string;
4
+ oldString: string;
5
+ newString: string;
6
+ targetAccountId?: string;
7
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
8
+ //# sourceMappingURL=file-edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.d.ts","sourceRoot":"","sources":["../../src/tools/file-edit.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EACtF,GAAG,EAAE,GAAG,yCA6BT"}
@@ -0,0 +1,40 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ export function fileEdit(args, ctx) {
5
+ const r = resolveRoot(ctx, args.targetAccountId, "file-edit");
6
+ if ("reject" in r)
7
+ return err(`file-edit denied: ${r.reject}`);
8
+ // An empty oldString has no unique match semantics — "ab".split("") yields a
9
+ // spurious single "match" that would silently splice newString into the file.
10
+ if (args.oldString === "")
11
+ return err("file-edit: oldString must not be empty");
12
+ let abs;
13
+ try {
14
+ abs = containedPath(r.root, args.path);
15
+ }
16
+ catch {
17
+ return err("file-edit rejected: path escapes the account directory");
18
+ }
19
+ let content;
20
+ try {
21
+ content = readFileSync(abs, "utf-8");
22
+ }
23
+ catch (e) {
24
+ return err(`file-edit: ${e.message}`);
25
+ }
26
+ const parts = content.split(args.oldString);
27
+ const count = parts.length - 1;
28
+ if (count === 0)
29
+ return err(`file-edit: oldString not found in ${args.path}`);
30
+ if (count > 1)
31
+ return err(`file-edit: oldString is not unique in ${args.path} (${count} matches)`);
32
+ try {
33
+ writeFileSync(abs, parts.join(args.newString), "utf-8");
34
+ return ok(`edited ${args.path}`);
35
+ }
36
+ catch (e) {
37
+ return err(`file-edit: ${e.message}`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=file-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.js","sourceRoot":"","sources":["../../src/tools/file-edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,QAAQ,CACtB,IAAsF,EACtF,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,6EAA6E;IAC7E,8EAA8E;IAC9E,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC,wCAAwC,CAAC,CAAC;IAChF,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,qCAAqC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,yCAAyC,IAAI,CAAC,IAAI,KAAK,KAAK,WAAW,CAAC,CAAC;IACnG,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileGlob(args: {
3
+ pattern: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-glob.d.ts","sourceRoot":"","sources":["../../src/tools/file-glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAMxD,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAMrF"}
@@ -0,0 +1,13 @@
1
+ import { resolveRoot } from "../lib/scope.js";
2
+ import { walkFiles, globToRegExp } from "../lib/walk.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ const GLOB_CAP = 1000;
5
+ export function fileGlob(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-glob");
7
+ if ("reject" in r)
8
+ return err(`file-glob denied: ${r.reject}`);
9
+ const re = globToRegExp(args.pattern);
10
+ const hits = walkFiles(r.root, GLOB_CAP).filter((rel) => re.test(rel));
11
+ return ok(hits.length ? hits.join("\n") : "(no matches)");
12
+ }
13
+ //# sourceMappingURL=file-glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-glob.js","sourceRoot":"","sources":["../../src/tools/file-glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB,MAAM,UAAU,QAAQ,CAAC,IAAmD,EAAE,GAAQ;IACpF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileGrep(args: {
3
+ pattern: string;
4
+ path?: string;
5
+ targetAccountId?: string;
6
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
7
+ //# sourceMappingURL=file-grep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-grep.d.ts","sourceRoot":"","sources":["../../src/tools/file-grep.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAQvE,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAClE,GAAG,EAAE,GAAG,yCAmDT"}
@@ -0,0 +1,71 @@
1
+ import { statSync, readFileSync } from "node:fs";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import { resolveRoot, containedPath } from "../lib/scope.js";
4
+ import { walkFiles } from "../lib/walk.js";
5
+ import { ok, err } from "../lib/result.js";
6
+ const GREP_CAP = 500; // max match lines returned
7
+ const WALK_CAP = 20000; // max files scanned
8
+ const MAX_FILE = 5 * 1024 * 1024;
9
+ export function fileGrep(args, ctx) {
10
+ const r = resolveRoot(ctx, args.targetAccountId, "file-grep");
11
+ if ("reject" in r)
12
+ return err(`file-grep denied: ${r.reject}`);
13
+ let base;
14
+ // `prefix` is the search subdir expressed relative to the account root, so
15
+ // every reported path stays root-relative and round-trips into file-read /
16
+ // file-glob regardless of which subdir was searched.
17
+ let prefix = "";
18
+ try {
19
+ if (args.path) {
20
+ base = containedPath(r.root, args.path);
21
+ prefix = relative(r.root, resolve(r.root, args.path)).split(sep).join("/");
22
+ }
23
+ else {
24
+ base = r.root;
25
+ }
26
+ }
27
+ catch {
28
+ return err("file-grep rejected: path escapes the account directory");
29
+ }
30
+ let re;
31
+ try {
32
+ re = new RegExp(args.pattern);
33
+ }
34
+ catch (e) {
35
+ return err(`file-grep: invalid pattern: ${e.message}`);
36
+ }
37
+ const hits = [];
38
+ for (const rel of walkFiles(base, WALK_CAP)) {
39
+ if (hits.length >= GREP_CAP)
40
+ break;
41
+ const abs = join(base, rel);
42
+ const rootRel = prefix ? `${prefix}/${rel}` : rel;
43
+ let st;
44
+ try {
45
+ st = statSync(abs);
46
+ }
47
+ catch {
48
+ continue;
49
+ }
50
+ if (st.size > MAX_FILE)
51
+ continue;
52
+ let text;
53
+ try {
54
+ text = readFileSync(abs, "utf-8");
55
+ }
56
+ catch {
57
+ continue;
58
+ }
59
+ if (text.includes("\u0000"))
60
+ continue; // skip binary
61
+ const lines = text.split("\n");
62
+ for (let i = 0; i < lines.length; i++) {
63
+ if (hits.length >= GREP_CAP)
64
+ break;
65
+ if (re.test(lines[i]))
66
+ hits.push(`${rootRel}:${i + 1}:${lines[i].slice(0, 200)}`);
67
+ }
68
+ }
69
+ return ok(hits.length ? hits.join("\n") : "(no matches)");
70
+ }
71
+ //# sourceMappingURL=file-grep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-grep.js","sourceRoot":"","sources":["../../src/tools/file-grep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileList(args: {
3
+ path?: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-list.d.ts","sourceRoot":"","sources":["../../src/tools/file-list.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAkBnF"}
@@ -0,0 +1,26 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ export function fileList(args, ctx) {
5
+ const r = resolveRoot(ctx, args.targetAccountId, "file-list");
6
+ if ("reject" in r)
7
+ return err(`file-list denied: ${r.reject}`);
8
+ let dir;
9
+ try {
10
+ dir = args.path ? containedPath(r.root, args.path) : r.root;
11
+ }
12
+ catch {
13
+ return err("file-list rejected: path escapes the account directory");
14
+ }
15
+ try {
16
+ const rows = readdirSync(dir, { withFileTypes: true }).map((e) => {
17
+ const type = e.isDirectory() ? "dir" : e.isSymbolicLink() ? "symlink" : "file";
18
+ return `${type}\t${e.name}`;
19
+ });
20
+ return ok(rows.length ? rows.join("\n") : "(empty)");
21
+ }
22
+ catch (e) {
23
+ return err(`file-list: ${e.message}`);
24
+ }
25
+ }
26
+ //# sourceMappingURL=file-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-list.js","sourceRoot":"","sources":["../../src/tools/file-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,QAAQ,CAAC,IAAiD,EAAE,GAAQ;IAClF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC/D,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/E,OAAO,GAAG,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileRead(args: {
3
+ path: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAKvE,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAiBlF"}
@@ -0,0 +1,28 @@
1
+ import { statSync, readFileSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ const MAX_FILE = 5 * 1024 * 1024; // 5 MB
5
+ export function fileRead(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-read");
7
+ if ("reject" in r)
8
+ return err(`file-read denied: ${r.reject}`);
9
+ let abs;
10
+ try {
11
+ abs = containedPath(r.root, args.path);
12
+ }
13
+ catch {
14
+ return err("file-read rejected: path escapes the account directory");
15
+ }
16
+ try {
17
+ const st = statSync(abs);
18
+ if (!st.isFile())
19
+ return err(`file-read: not a file: ${args.path}`);
20
+ if (st.size > MAX_FILE)
21
+ return err(`file-read: file too large (${st.size} bytes, max ${MAX_FILE})`);
22
+ return ok(readFileSync(abs, "utf-8"));
23
+ }
24
+ catch (e) {
25
+ return err(`file-read: ${e.message}`);
26
+ }
27
+ }
28
+ //# sourceMappingURL=file-read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEzC,MAAM,UAAU,QAAQ,CAAC,IAAgD,EAAE,GAAQ;IACjF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,OAAO,GAAG,CAAC,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,GAAG,CAAC,8BAA8B,EAAE,CAAC,IAAI,eAAe,QAAQ,GAAG,CAAC,CAAC;QACpG,OAAO,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileWrite(args: {
3
+ path: string;
4
+ content: string;
5
+ targetAccountId?: string;
6
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
7
+ //# sourceMappingURL=file-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,SAAS,CACvB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,GAAG,EAAE,GAAG,yCAiBT"}
@@ -0,0 +1,25 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { resolveRoot, containedPath } from "../lib/scope.js";
4
+ import { ok, err } from "../lib/result.js";
5
+ export function fileWrite(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-write");
7
+ if ("reject" in r)
8
+ return err(`file-write denied: ${r.reject}`);
9
+ let abs;
10
+ try {
11
+ abs = containedPath(r.root, args.path);
12
+ }
13
+ catch {
14
+ return err("file-write rejected: path escapes the account directory");
15
+ }
16
+ try {
17
+ mkdirSync(dirname(abs), { recursive: true });
18
+ writeFileSync(abs, args.content, "utf-8");
19
+ return ok(`wrote ${Buffer.byteLength(args.content)} bytes to ${args.path}`);
20
+ }
21
+ catch (e) {
22
+ return err(`file-write: ${e.message}`);
23
+ }
24
+ }
25
+ //# sourceMappingURL=file-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,SAAS,CACvB,IAAiE,EACjE,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAC/D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,yDAAyD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC,SAAS,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,eAAgB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@maxy/filesystem",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "start": "node dist/index.js",
10
+ "test": "vitest run"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.12.1",
14
+ "zod": "^3.24.0"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.7.0",
18
+ "@types/node": "^22.0.0",
19
+ "vitest": "^2.0.0"
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ["src/**/__tests__/**/*.test.ts"],
6
+ environment: "node",
7
+ },
8
+ });
@@ -185,6 +185,14 @@ Writes targeting `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:Loc
185
185
 
186
186
  - **Direct-ask path — `SESSION_NODE_ID` env-stamp.** When `producedByTaskId` is not provided (typical of direct admin asks like "add Anneke as person"), `memory-write` falls back to the `SESSION_NODE_ID` env var. The admin server stamps it at PTY spawn time on both admin sessions and on the specialist subagent spawns the admin dispatches (listing-curator, content-producer, database-operator and friends inherit the same anchor). The value is the `sessionId` UUID of the active `:AdminConversation`; the wrapper's `injectConversationProvenance` MATCHes `(c:Conversation {sessionId, accountId})` so account isolation is enforced inside the natural key — a cross-account stamp returns zero rows and never injects. Autonomous spawns with no parent conversation (cron, scheduled-task) legitimately have no env-stamp; those must thread `producedByTaskId` via `work-create` instead. The env-stamp is invisible to the agent (no schema field changes); the LLM never decides whether to use it.
187
187
 
188
+ ## Cross-account access (house admin) — `targetAccountId`
189
+
190
+ The memory tools whose Neo4j read/write re-scopes on the resolved account accept an optional `targetAccountId`. A house-scoped `role:admin` session sets it to read or write a **sub-account's** subgraph instead of its own; omitting it is byte-identical to the pre-existing own-account behaviour. Only a house-scoped session may set it: a sub-account or public session that passes `targetAccountId` is rejected `cross-account-denied`, and a house session naming an account that does not exist on this install is rejected `cross-account-invalid-target`. Resolution runs through the shared `resolveEffectiveAccount` primitive — there is no house fallback and no local re-implementation. Each call that sets `targetAccountId` emits one `[xacct] op=call tool=<t> caller=<own8> target=<tgt8> houseScope=<bool> decision=<allow|deny> reason=<…>` line before the graph call, and the tool's own `[memory-<t>] [<accountId>]` line prints the resolved (target) id as a wrong-subgraph cross-check.
191
+
192
+ `profile-read/update/delete` also take `targetAccountId`. They key on `userId` as well as `accountId`, so cross-account they cannot use the caller's boot `USER_ID` (a house operator has no node in the sub-account subgraph). Instead they resolve the target sub-account's **owner** `AdminUser` — the single identity seeded per account (`role:"owner"`, one per account) — and attribute the read/write to it, emitting `[xacct] op=identity tool=profile-* target=<sub8> resolved-userId=<owner8>`. A target with no owner `AdminUser` is rejected loudly (never a wrong-identity write). `profile-update` bootstraps the owner's `UserProfile` on this path so a preference-only write lands. This covers preferences and every `profileFields` value (name via `givenName`/`familyName`, `role`, `timezone`, `locale`, `expertise`); no `Person` node or PIN is required. Setting `personFields` identity (email/telephone → the owner `Person`) cross-account is deferred and keeps loud-failing until then.
193
+
194
+ Tools that do **not** take `targetAccountId` fall into three groups. **Not account-scoped:** `image-fetch` fetches image bytes by URL; `graph-prune-denylist-add/remove/list` mutate a brand-global deny-list; `memory-ingest-extract` stages a temp file — none read or write a sub-account subgraph. **Bound to the caller's own live session:** `session-compact` compacts the caller's `SESSION_ID` Conversation, which belongs to the house account the operator sits in — re-scoping `accountId` to a sub-account matches nothing and "compact a sub-account's live session" has no referent. **Scheduled, not interactive:** the dream-cycle **cron** (`lib/dream-cycle/*`) takes its account from the scheduler context, not a tool argument; only the interactive `memory-dream-run` tool is house-targetable. These tools keep their own-account no-account guard.
195
+
188
196
  ## Graph Hygiene
189
197
 
190
198
  Graph hygiene is **agent-directed, case by case** — no autonomous rule engine, no cron. The Neo4j graph degrades into a landfill if pollution from prior agent errors (wrong-account hostnames, fabricated identifiers, raw monologue dumps) is allowed to compound. Cleanup happens when the admin agent observes a match against the operator-curated deny-list (or the user directly asks) and applies discretion using `memory-update`, `memory-delete`, or `conversation-memory-expunge`.
@@ -0,0 +1,34 @@
1
+ type RejectContent = {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ isError: true;
7
+ };
8
+ export type ToolAccount = {
9
+ accountId: string;
10
+ } | {
11
+ reject: RejectContent;
12
+ };
13
+ /**
14
+ * Build the per-tool account resolver. `accountId` / `houseScope` are the boot
15
+ * constants; `isValid` is injected (from `enumerateValidAccountIds(accountsDir)`
16
+ * in production, a stub in tests). Returns a closure the tool handlers call with
17
+ * their optional `targetAccountId`.
18
+ *
19
+ * Contract:
20
+ * - null boot `accountId` → refuse (no-account guard, unchanged behaviour).
21
+ * - no `targetAccountId` → own account, no `[xacct]` line (byte-identical).
22
+ * - `targetAccountId` set → resolve via the shared 1440 resolver, emit one
23
+ * `[xacct]` line, and on reject return the MCP error content.
24
+ * `houseScope=false decision=allow` cannot occur: a non-house session with a
25
+ * target is denied by the resolver before any allow path.
26
+ */
27
+ export declare function makeResolveToolAccount(deps: {
28
+ accountId: string | null;
29
+ houseScope: string | null;
30
+ isValid: (id: string) => boolean;
31
+ log?: (line: string) => void;
32
+ }): (toolName: string, targetAccountId?: string | null) => ToolAccount;
33
+ export {};
34
+ //# sourceMappingURL=index-testable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-testable.d.ts","sourceRoot":"","sources":["../src/index-testable.ts"],"names":[],"mappings":"AAQA,KAAK,aAAa,GAAG;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAClF,MAAM,MAAM,WAAW,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAuB5E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,WAAW,CA0BrE"}