@rubytech/create-sitedesk-code 0.1.517 → 0.1.519

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 (243) hide show
  1. package/dist/__tests__/edge-bind-template-wiring.test.js +37 -0
  2. package/dist/__tests__/samba-provision.test.js +103 -1
  3. package/dist/__tests__/tier-flag.test.js +10 -6
  4. package/dist/index.js +20 -5
  5. package/dist/samba-provision.js +72 -0
  6. package/dist/tier-flag.js +10 -5
  7. package/package.json +1 -1
  8. package/payload/platform/lib/entitlement/dist/index.d.ts +24 -0
  9. package/payload/platform/lib/entitlement/dist/index.d.ts.map +1 -1
  10. package/payload/platform/lib/entitlement/dist/index.js +40 -2
  11. package/payload/platform/lib/entitlement/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/admin/mcp/dist/index.js +5 -9
  13. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  14. package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +1 -1
  15. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +261 -8
  16. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  17. package/payload/platform/plugins/docs/references/admin-ui.md +44 -6
  18. package/payload/platform/plugins/docs/references/samba.md +2 -0
  19. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +7 -3
  20. package/payload/platform/scripts/check-no-esm-require.mjs +5 -3
  21. package/payload/platform/scripts/generate-entitlement-fixture.mjs +1 -1
  22. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  23. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +16 -1
  24. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  25. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  26. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +15 -0
  27. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
  28. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -1
  29. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +5 -3
  30. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -1
  31. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  32. package/payload/platform/services/telegram-channel/dist/instructions.js +13 -10
  33. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  34. package/payload/platform/services/webchat-channel/dist/instructions.d.ts +6 -2
  35. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  36. package/payload/platform/services/webchat-channel/dist/instructions.js +66 -32
  37. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  38. package/payload/platform/services/webchat-channel/dist/notification.d.ts +2 -1
  39. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  40. package/payload/platform/services/webchat-channel/dist/notification.js +11 -4
  41. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  42. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  43. package/payload/platform/services/webchat-channel/dist/server.js +30 -14
  44. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  45. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -1
  46. package/payload/platform/services/webchat-channel/dist/turn-follow.js +11 -5
  47. package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -1
  48. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +18 -4
  49. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  50. package/payload/platform/services/whatsapp-channel/dist/notification.js +19 -7
  51. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  52. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  53. package/payload/platform/services/whatsapp-channel/dist/server.js +23 -3
  54. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  55. package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
  56. package/payload/platform/services/whatsapp-channel/dist/targets.js +8 -2
  57. package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
  58. package/payload/platform/templates/systemd/edge.service.template +1 -1
  59. package/payload/server/chunk-PXNY2TAP.js +1980 -0
  60. package/payload/server/chunk-TIIWMF5G.js +4837 -0
  61. package/payload/server/{chunk-S6HYTIU3.js → chunk-UX2FRNO7.js} +72 -2031
  62. package/payload/server/manager-B6OFOXSW.js +61 -0
  63. package/payload/server/maxy-edge.js +5 -3
  64. package/payload/server/package.json +2 -1
  65. package/payload/server/public/activity.html +5 -5
  66. package/payload/server/public/agents.html +4 -4
  67. package/payload/server/public/assets/{AdminLoginScreens-B5qKb3YK.js → AdminLoginScreens-BZPbCk8g.js} +1 -1
  68. package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.br +0 -0
  69. package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.gz +0 -0
  70. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js +2 -0
  71. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.br +0 -0
  72. package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.gz +0 -0
  73. package/payload/server/public/assets/{activity-DmnVUDYO.js → activity-DOsfKzT3.js} +1 -1
  74. package/payload/server/public/assets/activity-DOsfKzT3.js.br +0 -0
  75. package/payload/server/public/assets/activity-DOsfKzT3.js.gz +0 -0
  76. package/payload/server/public/assets/admin-COgD1z5a.js +1 -0
  77. package/payload/server/public/assets/admin-COgD1z5a.js.br +0 -0
  78. package/payload/server/public/assets/admin-COgD1z5a.js.gz +0 -0
  79. package/payload/server/public/assets/{agents-Bmq9X7P3.js → agents-DqDnZTH_.js} +1 -1
  80. package/payload/server/public/assets/agents-DqDnZTH_.js.br +0 -0
  81. package/payload/server/public/assets/agents-DqDnZTH_.js.gz +0 -0
  82. package/payload/server/public/assets/{browser-B1G-W80B.js → browser-FL17Dfp0.js} +1 -1
  83. package/payload/server/public/assets/browser-FL17Dfp0.js.br +0 -0
  84. package/payload/server/public/assets/browser-FL17Dfp0.js.gz +0 -0
  85. package/payload/server/public/assets/calendar-CLfLvsnV.js +1 -0
  86. package/payload/server/public/assets/calendar-CLfLvsnV.js.br +0 -0
  87. package/payload/server/public/assets/calendar-CLfLvsnV.js.gz +0 -0
  88. package/payload/server/public/assets/chat-DYq4FxMG.js +1 -0
  89. package/payload/server/public/assets/chat-DYq4FxMG.js.br +0 -0
  90. package/payload/server/public/assets/chat-DYq4FxMG.js.gz +0 -0
  91. package/payload/server/public/assets/chevron-left-DWt5cdUo.js +1 -0
  92. package/payload/server/public/assets/chevron-right-B9suVrYy.js +1 -0
  93. package/payload/server/public/assets/chevron-right-B9suVrYy.js.br +0 -0
  94. package/payload/server/public/assets/clock-DW6qVXtv.js +1 -0
  95. package/payload/server/public/assets/clock-DW6qVXtv.js.br +0 -0
  96. package/payload/server/public/assets/clock-DW6qVXtv.js.gz +0 -0
  97. package/payload/server/public/assets/data-C8rbOgds.js +1 -0
  98. package/payload/server/public/assets/data-C8rbOgds.js.br +0 -0
  99. package/payload/server/public/assets/data-C8rbOgds.js.gz +0 -0
  100. package/payload/server/public/assets/{file-text-BVqqaDun.js → file-text-DNm0AzUz.js} +1 -1
  101. package/payload/server/public/assets/file-text-DNm0AzUz.js.br +0 -0
  102. package/payload/server/public/assets/file-text-DNm0AzUz.js.gz +0 -0
  103. package/payload/server/public/assets/{graph-CEcME0lp.js → graph-fAvmlVzh.js} +3 -3
  104. package/payload/server/public/assets/graph-fAvmlVzh.js.br +0 -0
  105. package/payload/server/public/assets/graph-fAvmlVzh.js.gz +0 -0
  106. package/payload/server/public/assets/{graph-labels-Zl32_AOH.js → graph-labels-CwdkCxfs.js} +1 -1
  107. package/payload/server/public/assets/graph-labels-CwdkCxfs.js.br +0 -0
  108. package/payload/server/public/assets/graph-labels-CwdkCxfs.js.gz +0 -0
  109. package/payload/server/public/assets/{maximize-2-rkkMyAhf.js → maximize-2-B6tAPhC2.js} +1 -1
  110. package/payload/server/public/assets/maximize-2-B6tAPhC2.js.br +0 -0
  111. package/payload/server/public/assets/maximize-2-B6tAPhC2.js.gz +0 -0
  112. package/payload/server/public/assets/operator-CTKvBVjk.js +1 -0
  113. package/payload/server/public/assets/operator-CTKvBVjk.js.br +0 -0
  114. package/payload/server/public/assets/operator-CTKvBVjk.js.gz +0 -0
  115. package/payload/server/public/assets/page-BWoNW3sa.js +32 -0
  116. package/payload/server/public/assets/page-BWoNW3sa.js.br +0 -0
  117. package/payload/server/public/assets/page-BWoNW3sa.js.gz +0 -0
  118. package/payload/server/public/assets/page-O5HkHQw4.js +1 -0
  119. package/payload/server/public/assets/page-O5HkHQw4.js.br +0 -0
  120. package/payload/server/public/assets/page-O5HkHQw4.js.gz +0 -0
  121. package/payload/server/public/assets/{public-Cqgfmo1D.js → public-BF1rnVPa.js} +1 -1
  122. package/payload/server/public/assets/public-BF1rnVPa.js.br +0 -0
  123. package/payload/server/public/assets/public-BF1rnVPa.js.gz +0 -0
  124. package/payload/server/public/assets/{rotate-ccw-O3RmqoTy.js → rotate-ccw-B8TqfCy4.js} +1 -1
  125. package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.br +0 -0
  126. package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.gz +0 -0
  127. package/payload/server/public/assets/{routines-DMYObMUy.js → routines-4R7BLZyH.js} +1 -1
  128. package/payload/server/public/assets/routines-4R7BLZyH.js.br +0 -0
  129. package/payload/server/public/assets/routines-4R7BLZyH.js.gz +0 -0
  130. package/payload/server/public/assets/{skills-DON1BKu5.js → skills-CQ9Qs2tg.js} +1 -1
  131. package/payload/server/public/assets/skills-CQ9Qs2tg.js.br +0 -0
  132. package/payload/server/public/assets/skills-CQ9Qs2tg.js.gz +0 -0
  133. package/payload/server/public/assets/tasks-DePyOrRk.js +1 -0
  134. package/payload/server/public/assets/tasks-DePyOrRk.js.br +0 -0
  135. package/payload/server/public/assets/tasks-DePyOrRk.js.gz +0 -0
  136. package/payload/server/public/assets/{time-entry-format-4zgmuQI0.js → time-entry-format-D9gtnG01.js} +1 -1
  137. package/payload/server/public/assets/time-entry-format-D9gtnG01.js.br +0 -0
  138. package/payload/server/public/assets/time-entry-format-D9gtnG01.js.gz +0 -0
  139. package/payload/server/public/assets/{triangle-alert-By5D7wuI.js → triangle-alert-DAtm2mLL.js} +1 -1
  140. package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.br +0 -0
  141. package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.gz +0 -0
  142. package/payload/server/public/assets/{useCopyFeedback-Dx0vtm9s.js → useCopyFeedback-uMJT-p6i.js} +1 -1
  143. package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.br +0 -0
  144. package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.gz +0 -0
  145. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js +14 -0
  146. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.br +0 -0
  147. package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.gz +0 -0
  148. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css +1 -0
  149. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.br +0 -0
  150. package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.gz +0 -0
  151. package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js +2 -0
  152. package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.br +0 -0
  153. package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.gz +0 -0
  154. package/payload/server/public/assets/{wrench-CHfMwLza.js → wrench-CYgaN7K9.js} +1 -1
  155. package/payload/server/public/assets/wrench-CYgaN7K9.js.br +1 -0
  156. package/payload/server/public/assets/wrench-CYgaN7K9.js.gz +0 -0
  157. package/payload/server/public/browser.html +4 -4
  158. package/payload/server/public/calendar.html +7 -7
  159. package/payload/server/public/chat.html +13 -13
  160. package/payload/server/public/data.html +11 -11
  161. package/payload/server/public/graph.html +9 -9
  162. package/payload/server/public/index.html +14 -14
  163. package/payload/server/public/operator.html +14 -14
  164. package/payload/server/public/public.html +13 -13
  165. package/payload/server/public/routines.html +6 -6
  166. package/payload/server/public/skills.html +5 -5
  167. package/payload/server/public/tasks.html +6 -6
  168. package/payload/server/server.js +8156 -11969
  169. package/payload/server/public/assets/AdminLoginScreens-B5qKb3YK.js.br +0 -0
  170. package/payload/server/public/assets/AdminLoginScreens-B5qKb3YK.js.gz +0 -0
  171. package/payload/server/public/assets/AdminShell-BgJEq2zJ.js +0 -2
  172. package/payload/server/public/assets/AdminShell-BgJEq2zJ.js.br +0 -0
  173. package/payload/server/public/assets/AdminShell-BgJEq2zJ.js.gz +0 -0
  174. package/payload/server/public/assets/activity-DmnVUDYO.js.br +0 -0
  175. package/payload/server/public/assets/activity-DmnVUDYO.js.gz +0 -0
  176. package/payload/server/public/assets/admin-Dpge4OG9.js +0 -1
  177. package/payload/server/public/assets/admin-Dpge4OG9.js.br +0 -0
  178. package/payload/server/public/assets/admin-Dpge4OG9.js.gz +0 -0
  179. package/payload/server/public/assets/agents-Bmq9X7P3.js.br +0 -0
  180. package/payload/server/public/assets/agents-Bmq9X7P3.js.gz +0 -0
  181. package/payload/server/public/assets/browser-B1G-W80B.js.br +0 -0
  182. package/payload/server/public/assets/browser-B1G-W80B.js.gz +0 -0
  183. package/payload/server/public/assets/calendar-CHgrOFIN.js +0 -1
  184. package/payload/server/public/assets/calendar-CHgrOFIN.js.br +0 -0
  185. package/payload/server/public/assets/calendar-CHgrOFIN.js.gz +0 -0
  186. package/payload/server/public/assets/chat-CIwmmtwP.js +0 -1
  187. package/payload/server/public/assets/chat-CIwmmtwP.js.br +0 -0
  188. package/payload/server/public/assets/chat-CIwmmtwP.js.gz +0 -0
  189. package/payload/server/public/assets/chevron-left-EQjfaQ-n.js +0 -1
  190. package/payload/server/public/assets/chevron-left-EQjfaQ-n.js.br +0 -0
  191. package/payload/server/public/assets/chevron-right-Ct8jZUGP.js +0 -1
  192. package/payload/server/public/assets/chevron-right-Ct8jZUGP.js.br +0 -0
  193. package/payload/server/public/assets/clock-Cxz5ybck.js +0 -1
  194. package/payload/server/public/assets/clock-Cxz5ybck.js.br +0 -0
  195. package/payload/server/public/assets/clock-Cxz5ybck.js.gz +0 -0
  196. package/payload/server/public/assets/data-BQVXKVsJ.js +0 -1
  197. package/payload/server/public/assets/data-BQVXKVsJ.js.br +0 -1
  198. package/payload/server/public/assets/data-BQVXKVsJ.js.gz +0 -0
  199. package/payload/server/public/assets/file-text-BVqqaDun.js.br +0 -0
  200. package/payload/server/public/assets/file-text-BVqqaDun.js.gz +0 -0
  201. package/payload/server/public/assets/graph-CEcME0lp.js.br +0 -0
  202. package/payload/server/public/assets/graph-CEcME0lp.js.gz +0 -0
  203. package/payload/server/public/assets/graph-labels-Zl32_AOH.js.br +0 -0
  204. package/payload/server/public/assets/graph-labels-Zl32_AOH.js.gz +0 -0
  205. package/payload/server/public/assets/maximize-2-rkkMyAhf.js.br +0 -0
  206. package/payload/server/public/assets/maximize-2-rkkMyAhf.js.gz +0 -0
  207. package/payload/server/public/assets/operator-DlL023ch.js +0 -1
  208. package/payload/server/public/assets/operator-DlL023ch.js.br +0 -0
  209. package/payload/server/public/assets/operator-DlL023ch.js.gz +0 -0
  210. package/payload/server/public/assets/page-BAmYYqxp.js +0 -1
  211. package/payload/server/public/assets/page-BAmYYqxp.js.br +0 -0
  212. package/payload/server/public/assets/page-BAmYYqxp.js.gz +0 -0
  213. package/payload/server/public/assets/page-k0BhBkA2.js +0 -32
  214. package/payload/server/public/assets/page-k0BhBkA2.js.br +0 -0
  215. package/payload/server/public/assets/page-k0BhBkA2.js.gz +0 -0
  216. package/payload/server/public/assets/public-Cqgfmo1D.js.br +0 -0
  217. package/payload/server/public/assets/public-Cqgfmo1D.js.gz +0 -0
  218. package/payload/server/public/assets/rotate-ccw-O3RmqoTy.js.br +0 -0
  219. package/payload/server/public/assets/rotate-ccw-O3RmqoTy.js.gz +0 -0
  220. package/payload/server/public/assets/routines-DMYObMUy.js.br +0 -0
  221. package/payload/server/public/assets/routines-DMYObMUy.js.gz +0 -0
  222. package/payload/server/public/assets/skills-DON1BKu5.js.br +0 -0
  223. package/payload/server/public/assets/skills-DON1BKu5.js.gz +0 -0
  224. package/payload/server/public/assets/tasks-C2PhUSrf.js +0 -1
  225. package/payload/server/public/assets/tasks-C2PhUSrf.js.br +0 -0
  226. package/payload/server/public/assets/tasks-C2PhUSrf.js.gz +0 -0
  227. package/payload/server/public/assets/time-entry-format-4zgmuQI0.js.br +0 -0
  228. package/payload/server/public/assets/time-entry-format-4zgmuQI0.js.gz +0 -0
  229. package/payload/server/public/assets/triangle-alert-By5D7wuI.js.br +0 -0
  230. package/payload/server/public/assets/triangle-alert-By5D7wuI.js.gz +0 -0
  231. package/payload/server/public/assets/useCopyFeedback-Dx0vtm9s.js.br +0 -0
  232. package/payload/server/public/assets/useCopyFeedback-Dx0vtm9s.js.gz +0 -0
  233. package/payload/server/public/assets/useSubAccountSwitcher-CRWoQXuU.js +0 -9
  234. package/payload/server/public/assets/useSubAccountSwitcher-CRWoQXuU.js.br +0 -0
  235. package/payload/server/public/assets/useSubAccountSwitcher-CRWoQXuU.js.gz +0 -0
  236. package/payload/server/public/assets/useSubAccountSwitcher-UQQDcj0B.css +0 -1
  237. package/payload/server/public/assets/useSubAccountSwitcher-UQQDcj0B.css.br +0 -0
  238. package/payload/server/public/assets/useSubAccountSwitcher-UQQDcj0B.css.gz +0 -0
  239. package/payload/server/public/assets/useVoiceRecorder-BgwOCCwj.js +0 -2
  240. package/payload/server/public/assets/useVoiceRecorder-BgwOCCwj.js.br +0 -0
  241. package/payload/server/public/assets/useVoiceRecorder-BgwOCCwj.js.gz +0 -0
  242. package/payload/server/public/assets/wrench-CHfMwLza.js.br +0 -0
  243. package/payload/server/public/assets/wrench-CHfMwLza.js.gz +0 -0
@@ -0,0 +1,37 @@
1
+ // Nothing else in the repo ties the edge unit template's placeholders to the
2
+ // substitutions in index.ts, so either half can be edited alone and the whole
3
+ // suite stays green. Two silent failures this closes:
4
+ //
5
+ // - substitution added, template left at a literal value → the replace is a
6
+ // no-op, the unit still binds 0.0.0.0, and the Task 2082 security fix ships
7
+ // inert with every pickEdgeBindDecision test passing;
8
+ // - template placeholder added, substitution forgotten → the unit ships a
9
+ // literal `Environment=EDGE_HOSTNAME=__EDGE_HOSTNAME__`, which edge.ts's
10
+ // `process.env.EDGE_HOSTNAME ?? '0.0.0.0'` does NOT catch, because the
11
+ // variable is present, just not an address, so server.listen() gets a
12
+ // non-address and the edge restart-loops.
13
+ //
14
+ // Both files are read from disk at runtime, the same pattern as
15
+ // macos-darwin-branch.test.ts and base-toolchain-deps.test.ts.
16
+ import test from "node:test";
17
+ import assert from "node:assert/strict";
18
+ import { readFileSync } from "node:fs";
19
+ import { fileURLToPath } from "node:url";
20
+ import { dirname, resolve } from "node:path";
21
+ // dist/__tests__/edge-bind-template-wiring.test.js → ../../src/index.ts
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+ const INDEX_TS = resolve(here, "../../src/index.ts");
24
+ // dist/__tests__ → up four is the maxy-code root that holds platform/.
25
+ const EDGE_TEMPLATE = resolve(here, "../../../../platform/templates/systemd/edge.service.template");
26
+ const SRC = readFileSync(INDEX_TS, "utf-8");
27
+ const TEMPLATE = readFileSync(EDGE_TEMPLATE, "utf-8");
28
+ test("edge template: EDGE_HOSTNAME is a placeholder, not a hardcoded address", () => {
29
+ assert.match(TEMPLATE, /^Environment=EDGE_HOSTNAME=__EDGE_HOSTNAME__$/m, "edge.service.template must take EDGE_HOSTNAME from the installer's bind decision (Task 2082), not hardcode it");
30
+ });
31
+ test("edge template: every placeholder has a matching substitution in index.ts", () => {
32
+ const placeholders = [...new Set(TEMPLATE.match(/__[A-Z0-9_]+__/g) ?? [])].sort();
33
+ assert.ok(placeholders.includes("__EDGE_HOSTNAME__"), "template must carry __EDGE_HOSTNAME__");
34
+ for (const p of placeholders) {
35
+ assert.ok(SRC.includes(`.replace(/${p}/g,`), `${p} appears in edge.service.template but index.ts has no .replace(/${p}/g, ...) — the rendered unit would ship the literal placeholder`);
36
+ }
37
+ });
@@ -6,7 +6,7 @@
6
6
  // style and runs under `node --test dist/__tests__/*.test.js` after build.
7
7
  import test from "node:test";
8
8
  import assert from "node:assert/strict";
9
- import { renderBrandStanza, renderGlobalSection, renderFullSmbConf, isPrivateIPv4, pickBindDecision, mergeSmbConf, removeBrandStanza, hasAnyBrandStanza, formatSambaMarker, SAMBA_STEPS, SAMBA_ENABLE_UNITS, } from "../samba-provision.js";
9
+ import { renderBrandStanza, renderGlobalSection, renderFullSmbConf, isPrivateIPv4, pickBindDecision, pickEdgeBindDecision, formatEdgeBindDecision, mergeSmbConf, removeBrandStanza, hasAnyBrandStanza, formatSambaMarker, SAMBA_STEPS, SAMBA_ENABLE_UNITS, } from "../samba-provision.js";
10
10
  // ---------------------------------------------------------------------------
11
11
  // Fixtures
12
12
  // ---------------------------------------------------------------------------
@@ -49,6 +49,42 @@ const MIXED_PUBLIC_AND_PRIVATE = {
49
49
  eth0: [{ address: "178.105.251.82", netmask: "255.255.255.255", family: "IPv4", mac: "96:00:00:00:00:02", internal: false, cidr: "178.105.251.82/32" }],
50
50
  wg0: [{ address: "10.8.0.3", netmask: "255.255.255.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: false, cidr: "10.8.0.3/24" }],
51
51
  };
52
+ // Hetzner Cloud shape: public eth0 plus a provider private network on ens10.
53
+ // pickBindDecision calls this `lan`; the edge must still bind loopback.
54
+ const HETZNER_CLOUD_PUBLIC_PLUS_PRIVNET = {
55
+ lo: [{ address: "127.0.0.1", netmask: "255.0.0.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: true, cidr: "127.0.0.1/8" }],
56
+ eth0: [{ address: "167.233.21.246", netmask: "255.255.255.255", family: "IPv4", mac: "96:00:00:00:00:03", internal: false, cidr: "167.233.21.246/32" }],
57
+ ens10: [{ address: "10.0.0.2", netmask: "255.255.0.0", family: "IPv4", mac: "96:00:00:00:00:04", internal: false, cidr: "10.0.0.2/16" }],
58
+ };
59
+ // Private wlan0 plus a public address elsewhere. Reaches pickBindDecision's
60
+ // wlan0 early return, which MIXED_PUBLIC_AND_PRIVATE never does because its
61
+ // private address is on wg0.
62
+ const PRIVATE_WLAN0_PLUS_PUBLIC = {
63
+ lo: [{ address: "127.0.0.1", netmask: "255.0.0.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: true, cidr: "127.0.0.1/8" }],
64
+ wlan0: [{ address: "192.168.1.50", netmask: "255.255.255.0", family: "IPv4", mac: "aa:bb:cc:dd:ee:ff", internal: false, cidr: "192.168.1.50/24" }],
65
+ eth0: [{ address: "203.0.113.7", netmask: "255.255.255.255", family: "IPv4", mac: "96:00:00:00:00:05", internal: false, cidr: "203.0.113.7/32" }],
66
+ };
67
+ // Private eth0 plus a public address elsewhere. Reaches the eth0 early return.
68
+ const PRIVATE_ETH0_PLUS_PUBLIC = {
69
+ lo: [{ address: "127.0.0.1", netmask: "255.0.0.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: true, cidr: "127.0.0.1/8" }],
70
+ eth0: [{ address: "10.0.0.5", netmask: "255.255.255.0", family: "IPv4", mac: "11:11:11:11:11:11", internal: false, cidr: "10.0.0.5/24" }],
71
+ ens5: [{ address: "198.51.100.9", netmask: "255.255.255.255", family: "IPv4", mac: "96:00:00:00:00:06", internal: false, cidr: "198.51.100.9/32" }],
72
+ };
73
+ // One interface carrying both a public and a private IPv4. No other fixture in
74
+ // this file has a multi-address interface.
75
+ const ONE_IFACE_PUBLIC_AND_PRIVATE = {
76
+ lo: [{ address: "127.0.0.1", netmask: "255.0.0.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: true, cidr: "127.0.0.1/8" }],
77
+ eth0: [
78
+ { address: "192.0.2.44", netmask: "255.255.255.255", family: "IPv4", mac: "96:00:00:00:00:07", internal: false, cidr: "192.0.2.44/32" },
79
+ { address: "172.16.4.9", netmask: "255.255.0.0", family: "IPv4", mac: "96:00:00:00:00:07", internal: false, cidr: "172.16.4.9/16" },
80
+ ],
81
+ };
82
+ // CGNAT-only host. isPrivateIPv4 counts 100.64/10 as private, so this binds
83
+ // 0.0.0.0. Pinned so the outcome is declared rather than incidental.
84
+ const CGNAT_ONLY = {
85
+ lo: [{ address: "127.0.0.1", netmask: "255.0.0.0", family: "IPv4", mac: "00:00:00:00:00:00", internal: true, cidr: "127.0.0.1/8" }],
86
+ eth0: [{ address: "100.100.3.14", netmask: "255.192.0.0", family: "IPv4", mac: "96:00:00:00:00:08", internal: false, cidr: "100.100.3.14/10" }],
87
+ };
52
88
  // ---------------------------------------------------------------------------
53
89
  // isPrivateIPv4 — RFC1918 / CGNAT / link-local classification
54
90
  // ---------------------------------------------------------------------------
@@ -107,6 +143,72 @@ test("pickBindDecision: none when the only non-loopback iface has no IPv4", () =
107
143
  assert.deepEqual(pickBindDecision(PI_IPV6_ONLY_LAN), { kind: "none" });
108
144
  });
109
145
  // ---------------------------------------------------------------------------
146
+ // pickEdgeBindDecision — 0.0.0.0 only when private exists and public does not
147
+ // ---------------------------------------------------------------------------
148
+ test("pickEdgeBindDecision: private-only hosts keep the public bind", () => {
149
+ for (const [name, ifaces] of [
150
+ ["PI_WLAN0_ONLY", PI_WLAN0_ONLY],
151
+ ["PI_BOTH_IFACES", PI_BOTH_IFACES],
152
+ ["PI_ETH0_ONLY", PI_ETH0_ONLY],
153
+ ["PI_USB0_FALLBACK", PI_USB0_FALLBACK],
154
+ ["CGNAT_ONLY", CGNAT_ONLY],
155
+ ]) {
156
+ assert.equal(pickEdgeBindDecision(ifaces).hostname, "0.0.0.0", name);
157
+ }
158
+ });
159
+ // The fail-open this task exists to close: pickBindDecision calls every one of
160
+ // these `lan`, so keeping its verdict verbatim would leave the edge public on a
161
+ // cloud box that happens to carry a provider private network.
162
+ test("pickEdgeBindDecision: a public IPv4 forces loopback even when a private one exists", () => {
163
+ for (const [name, ifaces] of [
164
+ ["MIXED_PUBLIC_AND_PRIVATE", MIXED_PUBLIC_AND_PRIVATE],
165
+ ["HETZNER_CLOUD_PUBLIC_PLUS_PRIVNET", HETZNER_CLOUD_PUBLIC_PLUS_PRIVNET],
166
+ ["PRIVATE_WLAN0_PLUS_PUBLIC", PRIVATE_WLAN0_PLUS_PUBLIC],
167
+ ["PRIVATE_ETH0_PLUS_PUBLIC", PRIVATE_ETH0_PLUS_PUBLIC],
168
+ ["ONE_IFACE_PUBLIC_AND_PRIVATE", ONE_IFACE_PUBLIC_AND_PRIVATE],
169
+ ]) {
170
+ const d = pickEdgeBindDecision(ifaces);
171
+ assert.equal(d.hostname, "127.0.0.1", name);
172
+ assert.equal(d.kind, "lan", `${name} still classifies as lan for smbd`);
173
+ }
174
+ });
175
+ test("pickEdgeBindDecision: public-only host binds loopback", () => {
176
+ const d = pickEdgeBindDecision(HETZNER_PUBLIC_ETH0_ONLY);
177
+ assert.equal(d.hostname, "127.0.0.1");
178
+ assert.equal(d.kind, "loopback-only");
179
+ });
180
+ // Unlike the Samba call site, which throws on `none`, the edge binds loopback
181
+ // and lets the install finish: a loopback edge still serves the tunnel.
182
+ test("pickEdgeBindDecision: no non-loopback IPv4 binds loopback rather than throwing", () => {
183
+ for (const ifaces of [PI_LO_ONLY, PI_IPV6_ONLY_LAN]) {
184
+ const d = pickEdgeBindDecision(ifaces);
185
+ assert.equal(d.hostname, "127.0.0.1");
186
+ assert.equal(d.kind, "none");
187
+ assert.deepEqual(d.privateAddresses, []);
188
+ assert.deepEqual(d.publicAddresses, []);
189
+ }
190
+ });
191
+ test("pickEdgeBindDecision: reports the interface and addresses it decided from", () => {
192
+ assert.deepEqual(pickEdgeBindDecision(MIXED_PUBLIC_AND_PRIVATE), {
193
+ hostname: "127.0.0.1",
194
+ kind: "lan",
195
+ iface: "wg0",
196
+ publicAddresses: ["178.105.251.82"],
197
+ privateAddresses: ["10.8.0.3"],
198
+ });
199
+ assert.deepEqual(pickEdgeBindDecision(PI_WLAN0_ONLY), {
200
+ hostname: "0.0.0.0",
201
+ kind: "lan",
202
+ iface: "wlan0",
203
+ publicAddresses: [],
204
+ privateAddresses: ["192.168.1.50"],
205
+ });
206
+ });
207
+ test("formatEdgeBindDecision: carries both grep tokens and never leaves an empty field", () => {
208
+ assert.equal(formatEdgeBindDecision(pickEdgeBindDecision(PI_WLAN0_ONLY)), "[edge-bind-decision] kind=lan iface=wlan0 private=192.168.1.50 public=none EDGE_HOSTNAME=0.0.0.0");
209
+ assert.equal(formatEdgeBindDecision(pickEdgeBindDecision(HETZNER_PUBLIC_ETH0_ONLY)), "[edge-bind-decision] kind=loopback-only iface=none private=none public=167.233.21.246 EDGE_HOSTNAME=127.0.0.1");
210
+ });
211
+ // ---------------------------------------------------------------------------
110
212
  // renderBrandStanza — exact spec match
111
213
  // ---------------------------------------------------------------------------
112
214
  test("renderBrandStanza emits the spec directives in order", () => {
@@ -4,17 +4,20 @@
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
6
  import { validateTierFlag, applyTierToAccountConfig, isValidTier, } from "../tier-flag.js";
7
- test("validateTierFlag accepts solo|family|pro when commercialMode is false", () => {
7
+ test("validateTierFlag accepts solo|family|pro|max when commercialMode is false", () => {
8
8
  assert.equal(validateTierFlag("solo", false), "solo");
9
9
  assert.equal(validateTierFlag("family", false), "family");
10
10
  assert.equal(validateTierFlag("pro", false), "pro");
11
+ assert.equal(validateTierFlag("max", false), "max");
11
12
  });
12
- test("validateTierFlag rejects values outside the enum", () => {
13
- assert.throws(() => validateTierFlag("enterprise", false), /--tier value must be one of: solo, family, pro/);
14
- assert.throws(() => validateTierFlag("max", false), /--tier value must be one of: solo, family, pro/);
13
+ // Task 2063 both messages are joined from VALID_TIERS, never written out.
14
+ // The stale-payload failure mode is diagnosed from exactly this text, so a
15
+ // hardcoded list would let a stale build print a plausible one.
16
+ test("validateTierFlag rejects values outside the enum, naming every valid tier", () => {
17
+ assert.throws(() => validateTierFlag("enterprise", false), /--tier value must be one of: solo, family, pro, max \(got: enterprise\)/);
15
18
  });
16
- test("validateTierFlag rejects empty or flag-shaped value", () => {
17
- assert.throws(() => validateTierFlag(undefined, false), /--tier requires a value/);
19
+ test("validateTierFlag rejects empty or flag-shaped value, naming every valid tier", () => {
20
+ assert.throws(() => validateTierFlag(undefined, false), /--tier requires a value \(one of: solo, family, pro, max\)/);
18
21
  assert.throws(() => validateTierFlag("", false), /--tier requires a value/);
19
22
  assert.throws(() => validateTierFlag("--hostname", false), /--tier requires a value/);
20
23
  });
@@ -48,6 +51,7 @@ test("isValidTier narrows correctly", () => {
48
51
  assert.equal(isValidTier("solo"), true);
49
52
  assert.equal(isValidTier("family"), true);
50
53
  assert.equal(isValidTier("pro"), true);
54
+ assert.equal(isValidTier("max"), true);
51
55
  assert.equal(isValidTier("enterprise"), false);
52
56
  assert.equal(isValidTier(""), false);
53
57
  });
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import { classifyPortHolder } from "./preflight-port-classifier.js";
29
29
  import { memoryControllerMissing, cmdlineAlreadyPatched, patchCmdline } from "./cgroup-memory-controller.js";
30
30
  import { parsePluginList, computeInstallActions, parseExternalPlugins, findUnregisteredResyncs, discoverMarketplaceCandidateDirs, } from "./lib/plugin-install.js";
31
31
  import { findPremiumMcpDirs } from "./lib/premium-mcp-discover.js";
32
- import { pickBindDecision, mergeSmbConf, formatSambaMarker, SAMBA_ENABLE_UNITS, } from "./samba-provision.js";
32
+ import { pickBindDecision, pickEdgeBindDecision, formatEdgeBindDecision, mergeSmbConf, formatSambaMarker, SAMBA_ENABLE_UNITS, } from "./samba-provision.js";
33
33
  import { networkInterfaces, userInfo, cpus } from "node:os";
34
34
  const PAYLOAD_DIR = resolve(import.meta.dirname, "../payload");
35
35
  // Brand manifest — read from payload to derive all brand-specific installation values.
@@ -4493,11 +4493,20 @@ WantedBy=default.target
4493
4493
  // VNC_DISPLAY (defined above for the main brand unit) is also stamped into
4494
4494
  // the edge unit so both services agree on the X display. Per-brand display
4495
4495
  // closes the cross-brand cookie leak documented.
4496
+ // Task 2082 — the edge binds 0.0.0.0 only on a host with a private IPv4 and
4497
+ // no public one; anything else binds loopback so a rented server never puts
4498
+ // the admin edge on the internet. Logged with its inputs because a wrong
4499
+ // classification is otherwise invisible until an external probe finds it.
4500
+ // Linux-only by construction: this function returns at the darwin branch
4501
+ // above, and darwin runs no edge process at all.
4502
+ const edgeBind = pickEdgeBindDecision(networkInterfaces());
4503
+ logFile(` ${formatEdgeBindDecision(edgeBind)}`);
4496
4504
  const edgeTemplatePath = resolve(INSTALL_DIR, "platform/templates/systemd/edge.service.template");
4497
4505
  if (existsSync(edgeTemplatePath)) {
4498
4506
  const edgeServiceContent = readFileSync(edgeTemplatePath, "utf-8")
4499
4507
  .replace(/__INSTALL_DIR__/g, INSTALL_DIR)
4500
4508
  .replace(/__EDGE_PORT__/g, String(PORT))
4509
+ .replace(/__EDGE_HOSTNAME__/g, edgeBind.hostname)
4501
4510
  .replace(/__MAXY_UI_PORT__/g, String(MAXY_UI_INTERNAL_PORT))
4502
4511
  .replace(/__PERSIST_DIR__/g, persistDir)
4503
4512
  .replace(/__VNC_DISPLAY__/g, String(VNC_DISPLAY))
@@ -4505,7 +4514,7 @@ WantedBy=default.target
4505
4514
  .replace(/__CDP_PORT__/g, String(CDP_PORT_BRAND))
4506
4515
  .replace(/__RFB_PORT__/g, String(RFB_PORT));
4507
4516
  writeFileSync(join(serviceDir, edgeUnitName), edgeServiceContent);
4508
- logFile(` ${edgeUnitName}: EDGE_PORT=${PORT} MAXY_UI_PORT=${MAXY_UI_INTERNAL_PORT} VNC_DISPLAY=:${VNC_DISPLAY} RFB_PORT=${RFB_PORT} WEBSOCKIFY_PORT=${WEBSOCKIFY_PORT_BRAND} CDP_PORT=${CDP_PORT_BRAND}`);
4517
+ logFile(` ${edgeUnitName}: EDGE_PORT=${PORT} EDGE_HOSTNAME=${edgeBind.hostname} MAXY_UI_PORT=${MAXY_UI_INTERNAL_PORT} VNC_DISPLAY=:${VNC_DISPLAY} RFB_PORT=${RFB_PORT} WEBSOCKIFY_PORT=${WEBSOCKIFY_PORT_BRAND} CDP_PORT=${CDP_PORT_BRAND}`);
4509
4518
  }
4510
4519
  else {
4511
4520
  console.error(` WARNING: edge.service.template missing at ${edgeTemplatePath} — VNC transport unavailable`);
@@ -4998,7 +5007,7 @@ if (hostnameIdx !== -1) {
4998
5007
  // ---------------------------------------------------------------------------
4999
5008
  // Founder-side tier-set flags (Task 240).
5000
5009
  //
5001
- // `--tier <solo|family|pro>` stamps account.json.tier directly when the brand
5010
+ // `--tier <solo|family|pro|max>` stamps account.json.tier directly when the brand
5002
5011
  // is personal-mode (today's default for both maxy-code and realagent-code).
5003
5012
  // `--entitlement-base64 <b64>` decodes a signed payload and writes it to
5004
5013
  // ~/<BRAND.configDir>/entitlement.json when the brand is commercial-mode.
@@ -5389,8 +5398,14 @@ try {
5389
5398
  console.log("================================================================");
5390
5399
  console.log("");
5391
5400
  if (isLinux()) {
5392
- console.log(` Same network (Pi / LAN): http://${DEVICE_HOSTNAME}.local:${PORT}`);
5393
- console.log("");
5401
+ // Only true when the edge binds 0.0.0.0. On a host classified to loopback
5402
+ // (Task 2082) nothing answers on the LAN address, so printing this line
5403
+ // there would be a false instruction; the SSH-forward recipe below is the
5404
+ // whole story for that host class.
5405
+ if (pickEdgeBindDecision(networkInterfaces()).hostname === "0.0.0.0") {
5406
+ console.log(` Same network (Pi / LAN): http://${DEVICE_HOSTNAME}.local:${PORT}`);
5407
+ console.log("");
5408
+ }
5394
5409
  console.log(` Remote access (Hetzner / cloud) — on your local machine run:`);
5395
5410
  console.log(` ssh -L ${PORT}:localhost:${PORT} -L ${BRAND.websockifyPort}:localhost:${BRAND.websockifyPort} admin@<server-ipv4>`);
5396
5411
  console.log(` Then open:`);
@@ -14,6 +14,11 @@
14
14
  // stanza intact (peer-brand isolation is the structural invariant).
15
15
  // 5. Report whether any brand stanza remains so the uninstall step can
16
16
  // decide whether to apt-purge samba.
17
+ //
18
+ // Despite the file name, the interface classification here serves two
19
+ // consumers, not one: `isPrivateIPv4` and `pickBindDecision` answer where smbd
20
+ // binds, and `pickEdgeBindDecision` (Task 2082) answers where the edge binds.
21
+ // The two deliberately disagree on a dual-homed host; see that function.
17
22
  // ---------------------------------------------------------------------------
18
23
  // Brand-scoped Samba stanza
19
24
  // ---------------------------------------------------------------------------
@@ -152,6 +157,73 @@ export function pickBindDecision(ifaces) {
152
157
  const hasNonLoopbackIPv4 = Object.keys(ifaces).some((name) => name !== "lo" && ipv4Addrs(name).length > 0);
153
158
  return hasNonLoopbackIPv4 ? { kind: "loopback-only" } : { kind: "none" };
154
159
  }
160
+ /**
161
+ * Decide how the edge should bind, from the same interface table
162
+ * `pickBindDecision` reads. The edge binds `0.0.0.0` only when the host has at
163
+ * least one private IPv4 AND zero public IPv4; every other shape binds
164
+ * `127.0.0.1`. Task 2082.
165
+ *
166
+ * Three properties are deliberate, not oversights:
167
+ *
168
+ * 1. This DISAGREES with `pickBindDecision` on a dual-homed host. For SMB, a
169
+ * private interface alongside a public one keeps the LAN bind (operator
170
+ * confirmation, Task 755 — see the MIXED_PUBLIC_AND_PRIVATE fixture). For
171
+ * the edge the operator decided the opposite: a cloud server carrying a
172
+ * provider private network (Hetzner Cloud's public `eth0` plus private
173
+ * `ens10`) must not silently keep a public bind. Both functions are right
174
+ * for their own consumer; this is not a rename of the other.
175
+ * 2. IPv6 is ignored. `pickBindDecision` already filters to IPv4, and
176
+ * `0.0.0.0` binds IPv4 only, so a public IPv6 address cannot be exposed by
177
+ * this bind under either outcome.
178
+ * 3. A cloud host presenting its public address by 1:1 NAT is NOT covered.
179
+ * Its guest NIC carries only a private address, so this reads a
180
+ * private-only host and binds `0.0.0.0` while the box is publicly
181
+ * reachable. The scoped signal is a public IPv4 alongside the private one;
182
+ * no host in `.docs/install/` behaves that way, and catching it would need
183
+ * an outbound probe rather than the interface table.
184
+ *
185
+ * `kind: "none"` binds `127.0.0.1` rather than throwing, unlike the Samba call
186
+ * site, which hard-fails: a share bound to nothing is useless, whereas a
187
+ * loopback edge still serves the Cloudflare tunnel, which dials
188
+ * `http://localhost:<port>`. Cost: a host that gains an interface later stays
189
+ * loopback-only until the installer is re-run, which the decision line makes
190
+ * visible in the install log.
191
+ */
192
+ export function pickEdgeBindDecision(ifaces) {
193
+ const bind = pickBindDecision(ifaces);
194
+ const publicAddresses = [];
195
+ const privateAddresses = [];
196
+ for (const [name, addrs] of Object.entries(ifaces)) {
197
+ if (name === "lo" || !addrs)
198
+ continue;
199
+ for (const a of addrs) {
200
+ if (a.family !== "IPv4" || a.internal)
201
+ continue;
202
+ if (isPrivateIPv4(a.address))
203
+ privateAddresses.push(a.address);
204
+ else
205
+ publicAddresses.push(a.address);
206
+ }
207
+ }
208
+ return {
209
+ hostname: privateAddresses.length > 0 && publicAddresses.length === 0 ? "0.0.0.0" : "127.0.0.1",
210
+ kind: bind.kind,
211
+ iface: bind.kind === "lan" ? bind.lanInterface : null,
212
+ publicAddresses,
213
+ privateAddresses,
214
+ };
215
+ }
216
+ /**
217
+ * The install-log line for an edge bind decision. The shape is load-bearing:
218
+ * the task's diagnostic path greps `bind-decision` and `EDGE_HOSTNAME`, so both
219
+ * tokens must survive any edit. Empty lists render as the literal `none` so a
220
+ * host with no addresses is distinguishable from a truncated line.
221
+ */
222
+ export function formatEdgeBindDecision(d) {
223
+ const list = (xs) => (xs.length === 0 ? "none" : xs.join(","));
224
+ return `[edge-bind-decision] kind=${d.kind} iface=${d.iface ?? "none"} ` +
225
+ `private=${list(d.privateAddresses)} public=${list(d.publicAddresses)} EDGE_HOSTNAME=${d.hostname}`;
226
+ }
155
227
  // ---------------------------------------------------------------------------
156
228
  // smb.conf merge / remove
157
229
  // ---------------------------------------------------------------------------
package/dist/tier-flag.js CHANGED
@@ -1,12 +1,17 @@
1
1
  // Pure validation + path helpers for the founder-side tier-set installer flags
2
2
  // (Task 240). Two flags wired into the installer at src/index.ts:
3
- // --tier <solo|family|pro> personal-mode (brand.commercialMode === false)
3
+ // --tier <solo|family|pro|max> personal-mode (brand.commercialMode === false)
4
4
  // --entitlement-base64 <b64> commercial-mode (brand.commercialMode === true)
5
5
  //
6
6
  // Both helpers throw on invalid input; the caller exits non-zero and surfaces the
7
7
  // thrown message. No filesystem or console side effects — index.ts owns those.
8
8
  import { resolve } from "node:path";
9
- const VALID_TIERS = new Set(["solo", "family", "pro"]);
9
+ const VALID_TIERS = new Set(["solo", "family", "pro", "max"]);
10
+ // Task 2063 — the list in both thrown messages is JOINED FROM VALID_TIERS,
11
+ // never written out. A device that rejects a valid `--tier` because it carries
12
+ // a stale bundled build is diagnosed from exactly this text, so a hardcoded
13
+ // list would let a stale build print a plausible one and destroy the signal.
14
+ const TIER_LIST = [...VALID_TIERS].join(", ");
10
15
  export function isValidTier(value) {
11
16
  return VALID_TIERS.has(value);
12
17
  }
@@ -15,7 +20,7 @@ export function isValidTier(value) {
15
20
  *
16
21
  * Throws when:
17
22
  * - raw is missing / starts with `--` (no value supplied after the flag)
18
- * - raw is not in {solo, family, pro}
23
+ * - raw is not in {solo, family, pro, max}
19
24
  * - brand is commercial-mode (operator must use --entitlement-base64 instead)
20
25
  */
21
26
  export function validateTierFlag(raw, commercialMode) {
@@ -23,10 +28,10 @@ export function validateTierFlag(raw, commercialMode) {
23
28
  throw new Error("--tier is for personal mode. Commercial mode — use --entitlement-base64 to deliver a signed payload.");
24
29
  }
25
30
  if (!raw || raw.startsWith("--")) {
26
- throw new Error("--tier requires a value (one of: solo, family, pro).");
31
+ throw new Error(`--tier requires a value (one of: ${TIER_LIST}).`);
27
32
  }
28
33
  if (!isValidTier(raw)) {
29
- throw new Error(`--tier value must be one of: solo, family, pro (got: ${raw}).`);
34
+ throw new Error(`--tier value must be one of: ${TIER_LIST} (got: ${raw}).`);
30
35
  }
31
36
  return raw;
32
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-sitedesk-code",
3
- "version": "0.1.517",
3
+ "version": "0.1.519",
4
4
  "description": "Install SiteDesk — automated back office for independent building contractors",
5
5
  "bin": {
6
6
  "create-sitedesk-code": "./dist/index.js"
@@ -62,6 +62,30 @@ export interface BrandConfigSubset {
62
62
  /** True only when this brand has flipped to commercial entitlement enforcement. */
63
63
  commercialMode?: boolean;
64
64
  }
65
+ /**
66
+ * Admin seats per tier. Lives beside VALID_TIERS deliberately (Task 2063).
67
+ * The admin MCP used to hold its own copy and resolve through a silent
68
+ * `?? MAX_ADMINS_DEFAULT`, so a tier added to the signed-payload registry
69
+ * but not to that table refused the 6th admin with a message reading as a
70
+ * limit rather than a bug — no event, and re-running reproduced nothing.
71
+ * Adding a tier now puts both edits in one file, and the unmapped case is
72
+ * an event rather than a fallback.
73
+ */
74
+ export declare const MAX_ADMINS_BY_TIER: Readonly<Record<string, number>>;
75
+ export declare const MAX_ADMINS_DEFAULT = 5;
76
+ /**
77
+ * Admin seat cap for an effective tier. Emits `op=tier-cap-unmapped` when a
78
+ * tier that IS SET carries no entry — the standing signal for a tier that
79
+ * verifies but has no seat count, which is otherwise indistinguishable from a
80
+ * real limit.
81
+ *
82
+ * An EMPTY tier is not that fault and must stay silent. The installer stamps
83
+ * account.json.tier only when `--tier` is passed, so every default personal
84
+ * install reaches implicitTrust with `tier: ""` — the commonest shape there
85
+ * is. Alerting on it would fire this signal on installs where nothing is
86
+ * wrong, which is the same loss as having no signal at all.
87
+ */
88
+ export declare function resolveMaxAdmins(tier: string): number;
65
89
  /**
66
90
  * Resolve effective entitlement for the current install. Sync — Ed25519
67
91
  * verify is a CPU-bound primitive on the order of 0.3ms; no async surface.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAoBH,eAAO,MAAM,aAAa,qEAAqE,CAAC;AAMhG,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,cAAc,GACd,gBAAgB,GAChB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,2GAA2G;IAC3G,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA0BD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,mBAAmB,GAC3B,mBAAmB,CAuBrB;AAED,sEAAsE;AACtE,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAoBH,eAAO,MAAM,aAAa,qEAAqE,CAAC;AAMhG,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,cAAc,GACd,gBAAgB,GAChB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,2GAA2G;IAC3G,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA0BD;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK/D,CAAC;AACF,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASrD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,mBAAmB,GAC3B,mBAAmB,CAuBrB;AAED,sEAAsE;AACtE,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
@@ -41,7 +41,8 @@
41
41
  * is explicitly out of scope.
42
42
  */
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.PUBKEY_SHA256 = void 0;
44
+ exports.MAX_ADMINS_DEFAULT = exports.MAX_ADMINS_BY_TIER = exports.PUBKEY_SHA256 = void 0;
45
+ exports.resolveMaxAdmins = resolveMaxAdmins;
45
46
  exports.resolveEntitlement = resolveEntitlement;
46
47
  exports._clearMemo = _clearMemo;
47
48
  const node_crypto_1 = require("node:crypto");
@@ -78,7 +79,44 @@ function memoKey(mtimeMs, account) {
78
79
  const bucket = Math.floor(Date.now() / 60000);
79
80
  return `${mtimeMs}:${bucket}:${account.accountId}:${account.customerEmail ?? ""}`;
80
81
  }
81
- const VALID_TIERS = new Set(["solo", "family", "pro"]);
82
+ const VALID_TIERS = new Set(["solo", "family", "pro", "max"]);
83
+ /**
84
+ * Admin seats per tier. Lives beside VALID_TIERS deliberately (Task 2063).
85
+ * The admin MCP used to hold its own copy and resolve through a silent
86
+ * `?? MAX_ADMINS_DEFAULT`, so a tier added to the signed-payload registry
87
+ * but not to that table refused the 6th admin with a message reading as a
88
+ * limit rather than a bug — no event, and re-running reproduced nothing.
89
+ * Adding a tier now puts both edits in one file, and the unmapped case is
90
+ * an event rather than a fallback.
91
+ */
92
+ exports.MAX_ADMINS_BY_TIER = {
93
+ solo: 1,
94
+ family: 5,
95
+ pro: 5,
96
+ max: 20,
97
+ };
98
+ exports.MAX_ADMINS_DEFAULT = 5;
99
+ /**
100
+ * Admin seat cap for an effective tier. Emits `op=tier-cap-unmapped` when a
101
+ * tier that IS SET carries no entry — the standing signal for a tier that
102
+ * verifies but has no seat count, which is otherwise indistinguishable from a
103
+ * real limit.
104
+ *
105
+ * An EMPTY tier is not that fault and must stay silent. The installer stamps
106
+ * account.json.tier only when `--tier` is passed, so every default personal
107
+ * install reaches implicitTrust with `tier: ""` — the commonest shape there
108
+ * is. Alerting on it would fire this signal on installs where nothing is
109
+ * wrong, which is the same loss as having no signal at all.
110
+ */
111
+ function resolveMaxAdmins(tier) {
112
+ const mapped = exports.MAX_ADMINS_BY_TIER[tier];
113
+ if (typeof mapped === "number")
114
+ return mapped;
115
+ if (tier !== "") {
116
+ console.error(`[entitlement] op=tier-cap-unmapped tier=${tier} fallback=${exports.MAX_ADMINS_DEFAULT}`);
117
+ }
118
+ return exports.MAX_ADMINS_DEFAULT;
119
+ }
82
120
  /**
83
121
  * Resolve effective entitlement for the current install. Sync — Ed25519
84
122
  * verify is a CPU-bound primitive on the order of 0.3ms; no async surface.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ import { embed } from "../../../../lib/embed-client/dist/index.js";
13
13
  import { requirePortEnv } from "../../../../lib/require-port-env/dist/index.js";
14
14
  import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
15
15
  import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
16
- import { resolveEntitlement } from "../../../../lib/entitlement/dist/index.js";
16
+ import { resolveEntitlement, resolveMaxAdmins } from "../../../../lib/entitlement/dist/index.js";
17
17
  import { createHash, randomInt, randomUUID } from "node:crypto";
18
18
  import { createConnection } from "node:net";
19
19
  import { homedir, hostname as osHostname } from "node:os";
@@ -223,13 +223,6 @@ const USERS_FILE = resolve(CONFIG_DIR, "users.json");
223
223
  // Append-only credential-store audit log — same path the admin server resolves
224
224
  // from app/lib/paths (CONFIG_DIR == MAXY_DIR for both processes).
225
225
  const USERS_AUDIT_LOG = resolve(CONFIG_DIR, "logs", "users-audit.log");
226
- /** Maximum number of admin users allowed per tier. */
227
- const MAX_ADMINS_BY_TIER = {
228
- solo: 1,
229
- family: 5,
230
- pro: 5,
231
- };
232
- const MAX_ADMINS_DEFAULT = 5;
233
226
  function hashPin(pin) {
234
227
  return createHash("sha256").update(pin).digest("hex");
235
228
  }
@@ -905,7 +898,10 @@ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a
905
898
  const currentAdmins = (config.admins ?? []);
906
899
  const entitlement = await currentEntitlement();
907
900
  const tier = entitlement.tier;
908
- const maxAdmins = MAX_ADMINS_BY_TIER[tier] ?? MAX_ADMINS_DEFAULT;
901
+ // Task 2063 — the seat table and the tier list live together in the
902
+ // entitlement lib. A tier with no entry emits op=tier-cap-unmapped there
903
+ // rather than silently resolving to 5 here, which read as a limit.
904
+ const maxAdmins = resolveMaxAdmins(tier);
909
905
  if (currentAdmins.length >= maxAdmins) {
910
906
  return { content: [{ type: "text", text: `${TAG} Admin limit reached (${maxAdmins} for ${tier || "this"} tier). Remove an existing admin before adding a new one.` }], isError: true };
911
907
  }