clay-server 5.1.1-beta.1 → 5.2.0-beta.10

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 (588) hide show
  1. package/lib/account-auth-policy.js +21 -0
  2. package/lib/account-auth-socket.js +30 -0
  3. package/lib/account-auth-throttle.js +44 -0
  4. package/lib/account-auth-transport.js +48 -0
  5. package/lib/account-login-page.js +28 -0
  6. package/lib/account-passkey-challenges.js +31 -0
  7. package/lib/account-passkey-page.js +89 -0
  8. package/lib/account-passkey-policy.js +39 -0
  9. package/lib/account-password-page.js +69 -0
  10. package/lib/account-passwords.js +36 -0
  11. package/lib/account-remembered.js +77 -0
  12. package/lib/account-session-page.js +51 -0
  13. package/lib/account-session-store.js +82 -0
  14. package/lib/builtin-mates.js +19 -16
  15. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  16. package/lib/canonical-clay-record-authority.js +15 -0
  17. package/lib/canonical-clay-scheduling-authority.js +19 -0
  18. package/lib/claude-hook-installer.js +1 -239
  19. package/lib/claude-jsonl-watcher.js +1 -204
  20. package/lib/clay-control-presentation-binding.js +96 -0
  21. package/lib/clay-control-presentation.js +128 -0
  22. package/lib/clay-control-target-projection.js +26 -0
  23. package/lib/clay-mate-chat-receipts.js +26 -0
  24. package/lib/clay-mate-chat.js +88 -0
  25. package/lib/clay-mate-knowledge-input.js +26 -0
  26. package/lib/clay-mate-knowledge.js +95 -0
  27. package/lib/clay-mate-lifecycle.js +95 -0
  28. package/lib/clay-product-control-input.js +33 -0
  29. package/lib/clay-product-control-tools.js +51 -0
  30. package/lib/clay-product-control.js +247 -0
  31. package/lib/clay-project-lifecycle.js +154 -0
  32. package/lib/clay-project-record-input.js +44 -0
  33. package/lib/clay-project-records.js +84 -0
  34. package/lib/clay-scheduling-input.js +21 -0
  35. package/lib/clay-scheduling-receipts.js +26 -0
  36. package/lib/clay-scheduling.js +119 -0
  37. package/lib/clay-screen-controls.js +104 -0
  38. package/lib/clay-skill-install.js +1 -33
  39. package/lib/clay-workspace-controls.js +84 -0
  40. package/lib/clay-workspace-input.js +17 -0
  41. package/lib/clay-workspace-receipts.js +26 -0
  42. package/lib/cli-sessions.js +1 -300
  43. package/lib/codex-defaults.js +1 -28
  44. package/lib/context-usage.js +3 -2
  45. package/lib/contextual-screen-snapshot.js +38 -0
  46. package/lib/daemon-project-access.js +11 -0
  47. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  48. package/lib/daemon-project-lifecycle.js +262 -0
  49. package/lib/daemon-projects.js +1 -1
  50. package/lib/daemon.js +80 -276
  51. package/lib/default-ai-runtime.js +4 -8
  52. package/lib/dm-digest-cursor.js +120 -0
  53. package/lib/email-mcp-server.js +1 -1
  54. package/lib/issues-service.js +29 -15
  55. package/lib/kiro-defaults.js +1 -22
  56. package/lib/knowledge-db-import-validation.js +46 -0
  57. package/lib/knowledge-db-import.js +99 -0
  58. package/lib/knowledge-db-service.js +24 -33
  59. package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
  60. package/lib/knowledge-db-storage-nedb.js +66 -0
  61. package/lib/knowledge-db-storage.js +72 -0
  62. package/lib/knowledge-db-validation.js +0 -0
  63. package/lib/knowledge-document-index.js +2 -1
  64. package/lib/knowledge-file-store.js +7 -1
  65. package/lib/knowledge-product-operations.js +46 -0
  66. package/lib/mate-avatar-catalog.js +12 -0
  67. package/lib/mate-avatar-migration.js +41 -0
  68. package/lib/mate-context-service.js +138 -0
  69. package/lib/mate-context-tools.js +21 -0
  70. package/lib/mate-creation-mcp-server.js +2 -0
  71. package/lib/mate-knowledge-canonical-access.js +10 -0
  72. package/lib/mate-knowledge-mate-bound.js +53 -0
  73. package/lib/mate-knowledge-mcp-server.js +55 -8
  74. package/lib/mate-knowledge-service.js +16 -34
  75. package/lib/mate-knowledge-text-tools.js +106 -0
  76. package/lib/mate-lifecycle-service.js +94 -0
  77. package/lib/mate-message-delivery.js +43 -0
  78. package/lib/mate-ready-creation.js +9 -1
  79. package/lib/mate-schedule-input.js +29 -0
  80. package/lib/mate-schedule-lifecycle.js +151 -0
  81. package/lib/mate-scheduling.js +103 -0
  82. package/lib/mates.js +18 -6
  83. package/lib/mcp-agent-tools.js +117 -0
  84. package/lib/mcp-connection-store.js +119 -15
  85. package/lib/mcp-import-credentials.js +31 -0
  86. package/lib/mcp-local-transport.js +13 -0
  87. package/lib/mcp-oauth.js +8 -6
  88. package/lib/mcp-proxy.js +2 -32
  89. package/lib/mcp-query-bridge.js +30 -0
  90. package/lib/mcp-remote-connections.js +74 -20
  91. package/lib/mcp-source-adapters.js +1 -0
  92. package/lib/mcp-source-context.js +1 -0
  93. package/lib/mcp-source-discovery.js +1 -0
  94. package/lib/mcp-source-plugins.js +1 -0
  95. package/lib/mcp-source-restrictions.js +1 -0
  96. package/lib/mcp-tool-policy.js +9 -0
  97. package/lib/migrate-single-user.js +3 -23
  98. package/lib/notes-checklist.js +50 -0
  99. package/lib/notes.js +49 -3
  100. package/lib/os-user-diagnostics.js +2 -4
  101. package/lib/os-users.js +24 -70
  102. package/lib/pane-pending-interactions.js +20 -0
  103. package/lib/pane-session-activation.js +114 -0
  104. package/lib/pane-session-transport.js +26 -0
  105. package/lib/project-admin-overview.js +51 -0
  106. package/lib/project-ask-user.js +8 -4
  107. package/lib/project-connection.js +14 -10
  108. package/lib/project-control-git-view.js +12 -0
  109. package/lib/project-debate.js +1 -0
  110. package/lib/project-delegated-follow-up.js +2 -1
  111. package/lib/project-directory-claim.js +65 -0
  112. package/lib/project-file-operations.js +61 -0
  113. package/lib/project-filesystem.js +15 -85
  114. package/lib/project-full-access.js +23 -2
  115. package/lib/project-http.js +7 -4
  116. package/lib/project-knowledge.js +32 -18
  117. package/lib/project-lifecycle-operations.js +98 -0
  118. package/lib/project-lifecycle-process-witness.js +32 -0
  119. package/lib/project-lifecycle-runtime.js +50 -0
  120. package/lib/project-linux-access.js +22 -0
  121. package/lib/project-logs-canonical-clay.js +27 -0
  122. package/lib/project-logs-clay-query.js +16 -0
  123. package/lib/project-logs-schema.js +4 -2
  124. package/lib/project-logs-service.js +8 -7
  125. package/lib/project-logs.js +1 -0
  126. package/lib/project-loop.js +1 -0
  127. package/lib/project-mate-chat-control.js +55 -0
  128. package/lib/project-mate-creation-proposal.js +18 -3
  129. package/lib/project-mate-interaction.js +40 -62
  130. package/lib/project-mcp.js +54 -16
  131. package/lib/project-models.js +14 -31
  132. package/lib/project-notifications.js +76 -17
  133. package/lib/project-ownership.js +77 -0
  134. package/lib/project-pair-result-delivery.js +14 -3
  135. package/lib/project-pair-usage.js +12 -13
  136. package/lib/project-pending-message-queue.js +4 -1
  137. package/lib/project-product-control.js +89 -0
  138. package/lib/project-product-session-queue.js +46 -0
  139. package/lib/project-push-access.js +28 -0
  140. package/lib/project-scheduled-message-queue.js +30 -0
  141. package/lib/project-scheduled-messages.js +67 -68
  142. package/lib/project-scheduled-task-control.js +83 -0
  143. package/lib/project-scheduled-tasks.js +13 -2
  144. package/lib/project-scheduling-access.js +10 -0
  145. package/lib/project-server-settings.js +1 -0
  146. package/lib/project-session-actions.js +65 -0
  147. package/lib/project-session-delete.js +8 -1
  148. package/lib/project-session-document.js +27 -7
  149. package/lib/project-session-github.js +63 -19
  150. package/lib/project-session-history-actions.js +167 -0
  151. package/lib/project-session-linear.js +14 -6
  152. package/lib/project-session-notes.js +7 -3
  153. package/lib/project-session-pair.js +1 -1
  154. package/lib/project-session-runtime-actions.js +64 -0
  155. package/lib/project-session-spawn.js +2 -3
  156. package/lib/project-session-vendor.js +55 -0
  157. package/lib/project-sessions.js +103 -272
  158. package/lib/project-shared-browser.js +78 -17
  159. package/lib/project-studio-presentation.js +58 -0
  160. package/lib/project-user-knowledge.js +2 -1
  161. package/lib/project-user-message.js +67 -1
  162. package/lib/project-vendor-login.js +6 -36
  163. package/lib/project-workspace-operations.js +126 -0
  164. package/lib/project-workspace-query.js +19 -2
  165. package/lib/project.js +178 -45
  166. package/lib/public/app.js +46 -0
  167. package/lib/public/appearance-bootstrap.js +16 -0
  168. package/lib/public/auth/account-login.css +98 -0
  169. package/lib/public/auth/account-login.js +104 -0
  170. package/lib/public/css/clay-tool-activity.css +51 -0
  171. package/lib/public/css/clay-workspace.css +29 -0
  172. package/lib/public/css/command-palette.css +12 -33
  173. package/lib/public/css/contextual-clay.css +240 -0
  174. package/lib/public/css/home-debate-planning.css +2 -0
  175. package/lib/public/css/home-mate-creation.css +5 -0
  176. package/lib/public/css/input.css +6 -0
  177. package/lib/public/css/mate-avatar-gallery.css +12 -0
  178. package/lib/public/css/mate-conversation.css +10 -5
  179. package/lib/public/css/messages.css +6 -2
  180. package/lib/public/css/overlays.css +4 -2
  181. package/lib/public/css/project-admin-overview.css +13 -0
  182. package/lib/public/css/session-folders.css +11 -0
  183. package/lib/public/css/session-github.css +46 -1
  184. package/lib/public/css/shared-browser.css +35 -1
  185. package/lib/public/css/sticky-notes.css +87 -1
  186. package/lib/public/css/user-settings-sessions.css +42 -0
  187. package/lib/public/index.html +28 -10
  188. package/lib/public/mate-avatars/B01.png +0 -0
  189. package/lib/public/mate-avatars/B02.png +0 -0
  190. package/lib/public/mate-avatars/B03.png +0 -0
  191. package/lib/public/mate-avatars/B04.png +0 -0
  192. package/lib/public/mate-avatars/B05.png +0 -0
  193. package/lib/public/mate-avatars/B06.png +0 -0
  194. package/lib/public/mate-avatars/B07.png +0 -0
  195. package/lib/public/mate-avatars/B08.png +0 -0
  196. package/lib/public/mate-avatars/B09.png +0 -0
  197. package/lib/public/mate-avatars/B10.png +0 -0
  198. package/lib/public/mate-avatars/B11.png +0 -0
  199. package/lib/public/mate-avatars/B12.png +0 -0
  200. package/lib/public/mate-avatars/B13.png +0 -0
  201. package/lib/public/mate-avatars/B14.png +0 -0
  202. package/lib/public/mate-avatars/B15.png +0 -0
  203. package/lib/public/mate-avatars/B16.png +0 -0
  204. package/lib/public/mate-avatars/B17.png +0 -0
  205. package/lib/public/mate-avatars/B18.png +0 -0
  206. package/lib/public/mate-avatars/B19.png +0 -0
  207. package/lib/public/mate-avatars/B20.png +0 -0
  208. package/lib/public/mate-avatars/B21.png +0 -0
  209. package/lib/public/mate-avatars/B22.png +0 -0
  210. package/lib/public/mate-avatars/B23.png +0 -0
  211. package/lib/public/mate-avatars/B24.png +0 -0
  212. package/lib/public/mate-avatars/B25.png +0 -0
  213. package/lib/public/mate-avatars/B26.png +0 -0
  214. package/lib/public/mate-avatars/B27.png +0 -0
  215. package/lib/public/mate-avatars/B28.png +0 -0
  216. package/lib/public/mate-avatars/B29.png +0 -0
  217. package/lib/public/mate-avatars/B30.png +0 -0
  218. package/lib/public/mate-avatars/B31.png +0 -0
  219. package/lib/public/mate-avatars/B32.png +0 -0
  220. package/lib/public/mate-avatars/B33.png +0 -0
  221. package/lib/public/mate-avatars/B34.png +0 -0
  222. package/lib/public/mate-avatars/B35.png +0 -0
  223. package/lib/public/mate-avatars/B36.png +0 -0
  224. package/lib/public/mate-avatars/B37.png +0 -0
  225. package/lib/public/mate-avatars/B38.png +0 -0
  226. package/lib/public/mate-avatars/B39.png +0 -0
  227. package/lib/public/mate-avatars/B40.png +0 -0
  228. package/lib/public/mate-avatars/B41.png +0 -0
  229. package/lib/public/mate-avatars/B42.png +0 -0
  230. package/lib/public/mate-avatars/B43.png +0 -0
  231. package/lib/public/mate-avatars/B44.png +0 -0
  232. package/lib/public/mate-avatars/B45.png +0 -0
  233. package/lib/public/mate-avatars/B46.png +0 -0
  234. package/lib/public/mate-avatars/B47.png +0 -0
  235. package/lib/public/mate-avatars/B48.png +0 -0
  236. package/lib/public/mate-avatars/C01.png +0 -0
  237. package/lib/public/mate-avatars/C02.png +0 -0
  238. package/lib/public/mate-avatars/C03.png +0 -0
  239. package/lib/public/mate-avatars/C04.png +0 -0
  240. package/lib/public/mate-avatars/C05.png +0 -0
  241. package/lib/public/mate-avatars/C06.png +0 -0
  242. package/lib/public/mate-avatars/C07.png +0 -0
  243. package/lib/public/mate-avatars/C08.png +0 -0
  244. package/lib/public/mate-avatars/C09.png +0 -0
  245. package/lib/public/mate-avatars/C10.png +0 -0
  246. package/lib/public/mate-avatars/C11.png +0 -0
  247. package/lib/public/mate-avatars/C12.png +0 -0
  248. package/lib/public/mate-avatars/C13.png +0 -0
  249. package/lib/public/mate-avatars/C14.png +0 -0
  250. package/lib/public/mate-avatars/C15.png +0 -0
  251. package/lib/public/mate-avatars/C16.png +0 -0
  252. package/lib/public/mate-avatars/C17.png +0 -0
  253. package/lib/public/mate-avatars/C18.png +0 -0
  254. package/lib/public/mate-avatars/C19.png +0 -0
  255. package/lib/public/mate-avatars/C20.png +0 -0
  256. package/lib/public/mate-avatars/C21.png +0 -0
  257. package/lib/public/mate-avatars/C22.png +0 -0
  258. package/lib/public/mate-avatars/C23.png +0 -0
  259. package/lib/public/mate-avatars/C24.png +0 -0
  260. package/lib/public/mate-avatars/C25.png +0 -0
  261. package/lib/public/mate-avatars/C26.png +0 -0
  262. package/lib/public/mate-avatars/C27.png +0 -0
  263. package/lib/public/mate-avatars/C28.png +0 -0
  264. package/lib/public/mate-avatars/C29.png +0 -0
  265. package/lib/public/mate-avatars/C30.png +0 -0
  266. package/lib/public/mate-avatars/C31.png +0 -0
  267. package/lib/public/mate-avatars/C32.png +0 -0
  268. package/lib/public/mate-avatars/C33.png +0 -0
  269. package/lib/public/mate-avatars/C34.png +0 -0
  270. package/lib/public/mate-avatars/C35.png +0 -0
  271. package/lib/public/mate-avatars/C36.png +0 -0
  272. package/lib/public/mate-avatars/C37.png +0 -0
  273. package/lib/public/mate-avatars/C38.png +0 -0
  274. package/lib/public/mate-avatars/C39.png +0 -0
  275. package/lib/public/mate-avatars/C40.png +0 -0
  276. package/lib/public/mate-avatars/C41.png +0 -0
  277. package/lib/public/mate-avatars/C42.png +0 -0
  278. package/lib/public/mate-avatars/C43.png +0 -0
  279. package/lib/public/mate-avatars/C44.png +0 -0
  280. package/lib/public/mate-avatars/C45.png +0 -0
  281. package/lib/public/mate-avatars/C46.png +0 -0
  282. package/lib/public/mate-avatars/C47.png +0 -0
  283. package/lib/public/mate-avatars/C48.png +0 -0
  284. package/lib/public/mate-avatars/catalog.json +482 -0
  285. package/lib/public/mate-avatars/lead-clay.png +0 -0
  286. package/lib/public/modules/account-auth-settings.js +71 -0
  287. package/lib/public/modules/account-transport-settings.js +51 -0
  288. package/lib/public/modules/admin-settings-projects.js +6 -2
  289. package/lib/public/modules/app-connection.js +11 -1
  290. package/lib/public/modules/app-favicon.js +18 -9
  291. package/lib/public/modules/app-header.js +7 -4
  292. package/lib/public/modules/app-loop-ui.js +2 -0
  293. package/lib/public/modules/app-message-router.js +22 -1
  294. package/lib/public/modules/app-messages.js +119 -17
  295. package/lib/public/modules/app-misc.js +3 -1
  296. package/lib/public/modules/app-notifications.js +11 -5
  297. package/lib/public/modules/app-panels.js +61 -123
  298. package/lib/public/modules/app-projects.js +13 -0
  299. package/lib/public/modules/app-rate-limit.js +7 -27
  300. package/lib/public/modules/app-rendering.js +4 -3
  301. package/lib/public/modules/avatar.js +15 -41
  302. package/lib/public/modules/chat-render-runtime.js +20 -0
  303. package/lib/public/modules/clay-browser-links.js +47 -0
  304. package/lib/public/modules/clay-control-activity.js +214 -0
  305. package/lib/public/modules/clay-control-copy.js +36 -0
  306. package/lib/public/modules/clay-control-detail-targets.js +44 -0
  307. package/lib/public/modules/clay-control-object-targets.js +52 -0
  308. package/lib/public/modules/clay-control-target-visibility.js +12 -0
  309. package/lib/public/modules/clay-control-targets.js +44 -0
  310. package/lib/public/modules/clay-log-links.js +23 -0
  311. package/lib/public/modules/clay-navigation-render.js +52 -0
  312. package/lib/public/modules/clay-owner-navigation.js +33 -0
  313. package/lib/public/modules/clay-session-links.js +31 -0
  314. package/lib/public/modules/clay-studio-bridge.js +20 -0
  315. package/lib/public/modules/clay-studio-document.js +67 -0
  316. package/lib/public/modules/clay-studio-render.js +146 -0
  317. package/lib/public/modules/clay-tool-activity.js +140 -0
  318. package/lib/public/modules/clay-tool-presentation.js +342 -0
  319. package/lib/public/modules/command-palette.js +162 -40
  320. package/lib/public/modules/contextual-ask-session.js +26 -0
  321. package/lib/public/modules/contextual-clay-geometry.js +155 -0
  322. package/lib/public/modules/contextual-clay-session.js +130 -0
  323. package/lib/public/modules/contextual-clay-workspace.js +169 -0
  324. package/lib/public/modules/contextual-clay.js +250 -0
  325. package/lib/public/modules/contextual-composer.js +91 -0
  326. package/lib/public/modules/contextual-mate-context.js +117 -0
  327. package/lib/public/modules/contextual-mate-hint.js +40 -0
  328. package/lib/public/modules/contextual-mate-identity.js +23 -0
  329. package/lib/public/modules/contextual-mate-sessions.js +134 -0
  330. package/lib/public/modules/contextual-search.js +137 -0
  331. package/lib/public/modules/contextual-session-citations.js +58 -0
  332. package/lib/public/modules/debate.js +1 -0
  333. package/lib/public/modules/default-ai.js +11 -3
  334. package/lib/public/modules/elicitation-form.js +5 -0
  335. package/lib/public/modules/filebrowser.js +20 -1
  336. package/lib/public/modules/generation-stop.js +8 -5
  337. package/lib/public/modules/git-panel.js +3 -1
  338. package/lib/public/modules/history-replay-batch.js +13 -1
  339. package/lib/public/modules/home-debate-live.js +2 -0
  340. package/lib/public/modules/home-debate-planning.js +7 -7
  341. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  342. package/lib/public/modules/home-mate-chat.js +8 -6
  343. package/lib/public/modules/home-mate-creation.js +73 -1
  344. package/lib/public/modules/home-mate-settings.js +19 -12
  345. package/lib/public/modules/icon-strip-tooltip.js +2 -1
  346. package/lib/public/modules/input.js +88 -10
  347. package/lib/public/modules/issues-render.js +3 -2
  348. package/lib/public/modules/knowledge-db-workbench.js +4 -4
  349. package/lib/public/modules/markdown.js +6 -34
  350. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  351. package/lib/public/modules/mate-knowledge-workbench.js +1 -1
  352. package/lib/public/modules/mate-management.js +40 -7
  353. package/lib/public/modules/mcp-connection-view.js +31 -13
  354. package/lib/public/modules/mcp-discovery-view.js +31 -0
  355. package/lib/public/modules/mcp-ui.js +23 -4
  356. package/lib/public/modules/mention.js +3 -0
  357. package/lib/public/modules/message-delivery.js +20 -2
  358. package/lib/public/modules/model-picker.js +2 -0
  359. package/lib/public/modules/pane-activation.js +214 -0
  360. package/lib/public/modules/pane-binding.js +82 -0
  361. package/lib/public/modules/pane-bridge.js +7 -4
  362. package/lib/public/modules/pane-browser-bridge.js +22 -0
  363. package/lib/public/modules/pane-file-bridge.js +4 -0
  364. package/lib/public/modules/pane-issue-bridge.js +4 -1
  365. package/lib/public/modules/passkey-browser.js +69 -0
  366. package/lib/public/modules/passkey-server-settings.js +57 -0
  367. package/lib/public/modules/pending-message-queue.js +1 -0
  368. package/lib/public/modules/permission-control.js +5 -4
  369. package/lib/public/modules/personal-settings-dialog.js +7 -1
  370. package/lib/public/modules/product-review.js +26 -0
  371. package/lib/public/modules/profile.js +1 -238
  372. package/lib/public/modules/project-access-recovery.js +17 -0
  373. package/lib/public/modules/project-admin-overview.js +21 -0
  374. package/lib/public/modules/project-chat-identity.js +13 -4
  375. package/lib/public/modules/project-logs-render.js +6 -2
  376. package/lib/public/modules/project-mate-navigation.js +144 -22
  377. package/lib/public/modules/rewind.js +5 -0
  378. package/lib/public/modules/scheduled-tasks.js +2 -0
  379. package/lib/public/modules/scheduler.js +2 -0
  380. package/lib/public/modules/search-clay-chat.js +5 -440
  381. package/lib/public/modules/server-settings-controls.js +10 -0
  382. package/lib/public/modules/server-settings.js +7 -2
  383. package/lib/public/modules/session-actions.js +5 -2
  384. package/lib/public/modules/session-context-menu.js +5 -0
  385. package/lib/public/modules/session-create-form.js +10 -10
  386. package/lib/public/modules/session-folder-layout.js +1 -1
  387. package/lib/public/modules/session-folder-view-menu.js +9 -0
  388. package/lib/public/modules/session-github.js +85 -14
  389. package/lib/public/modules/session-rich-drafts.js +21 -0
  390. package/lib/public/modules/session-switch-defer.js +67 -0
  391. package/lib/public/modules/session-work-primary.js +51 -0
  392. package/lib/public/modules/settings-defaults.js +27 -28
  393. package/lib/public/modules/shared-browser-settings.js +112 -0
  394. package/lib/public/modules/shared-browser-tabs.js +13 -3
  395. package/lib/public/modules/shared-browser.js +29 -6
  396. package/lib/public/modules/sidebar-mobile.js +4 -3
  397. package/lib/public/modules/sidebar-projects.js +1 -0
  398. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  399. package/lib/public/modules/sidebar-sessions.js +1 -0
  400. package/lib/public/modules/split-group-restore.js +68 -0
  401. package/lib/public/modules/split-pair-ui.js +1 -0
  402. package/lib/public/modules/split-pane-reconciler.js +15 -0
  403. package/lib/public/modules/split-pane-renderer.js +87 -6
  404. package/lib/public/modules/split-session-boundary.js +1 -1
  405. package/lib/public/modules/split-view.js +60 -68
  406. package/lib/public/modules/sticky-note-markdown.js +72 -6
  407. package/lib/public/modules/sticky-notes-actions.js +30 -0
  408. package/lib/public/modules/sticky-notes-browser.js +9 -2
  409. package/lib/public/modules/sticky-notes-card.js +23 -7
  410. package/lib/public/modules/sticky-notes-checklist.js +116 -0
  411. package/lib/public/modules/sticky-notes-editor.js +108 -44
  412. package/lib/public/modules/sticky-notes-shared.js +50 -2
  413. package/lib/public/modules/sticky-notes-state.js +66 -0
  414. package/lib/public/modules/sticky-notes.js +34 -51
  415. package/lib/public/modules/stt-controller.js +4 -1
  416. package/lib/public/modules/stt-recording.js +1 -1
  417. package/lib/public/modules/stt.js +7 -2
  418. package/lib/public/modules/terminal.js +9 -0
  419. package/lib/public/modules/theme.js +8 -1
  420. package/lib/public/modules/thinking-lifecycle.js +33 -3
  421. package/lib/public/modules/thinking-view.js +23 -1
  422. package/lib/public/modules/tools.js +60 -11
  423. package/lib/public/modules/tui-grab.js +1 -2
  424. package/lib/public/modules/user-settings-account.js +14 -1
  425. package/lib/public/modules/user-settings-appearance.js +10 -3
  426. package/lib/public/modules/user-settings-editors.js +1 -1
  427. package/lib/public/modules/user-settings-layout.js +11 -2
  428. package/lib/public/modules/user-settings-preferences.js +6 -2
  429. package/lib/public/modules/user-settings-profile.js +12 -2
  430. package/lib/public/modules/user-settings-sessions.js +120 -0
  431. package/lib/public/modules/user-settings.js +6 -2
  432. package/lib/public/modules/vendor-login-browser.js +5 -3
  433. package/lib/public/modules/vendor-login.js +2 -2
  434. package/lib/public/modules/vendor-priority.js +15 -1
  435. package/lib/public/modules/wireframe-workbench.js +5 -2
  436. package/lib/public/modules/you-activity.js +3 -1
  437. package/lib/public/modules/you.js +91 -6
  438. package/lib/public/muse-avatar.svg +4 -0
  439. package/lib/public/style.css +6 -0
  440. package/lib/public/sw.js +3 -1
  441. package/lib/safe-bash-commands.js +3 -45
  442. package/lib/scheduled-task-records.js +3 -1
  443. package/lib/scheduler.js +2 -0
  444. package/lib/sdk-bridge.js +342 -401
  445. package/lib/sdk-foreground-admission.js +16 -0
  446. package/lib/sdk-mcp-descriptors.js +2 -37
  447. package/lib/sdk-message-processor.js +52 -17
  448. package/lib/sdk-quota-snapshot.js +1 -1
  449. package/lib/sdk-response-recovery.js +1 -0
  450. package/lib/sdk-worker.js +2 -677
  451. package/lib/server-account-passkeys.js +186 -0
  452. package/lib/server-account-passwords.js +127 -0
  453. package/lib/server-account-security.js +53 -0
  454. package/lib/server-admin.js +63 -16
  455. package/lib/server-appearance-settings.js +13 -1
  456. package/lib/server-auth.js +107 -79
  457. package/lib/server-contextual-clay-settings.js +40 -0
  458. package/lib/server-contextual-mate-sessions.js +70 -0
  459. package/lib/server-dm.js +2 -1
  460. package/lib/server-global-ws.js +10 -20
  461. package/lib/server-home-chat-events.js +18 -4
  462. package/lib/server-home-chat.js +22 -20
  463. package/lib/server-home-clay-entry.js +23 -4
  464. package/lib/server-home-clay-session-links.js +29 -9
  465. package/lib/server-home-mate-creation.js +1 -0
  466. package/lib/server-home-mcp-consent.js +14 -0
  467. package/lib/server-mates.js +25 -62
  468. package/lib/server-owner-ui.js +118 -0
  469. package/lib/server-path-access.js +38 -0
  470. package/lib/server-project-removal.js +22 -0
  471. package/lib/server-settings.js +8 -2
  472. package/lib/server.js +165 -43
  473. package/lib/session-config-transition.js +45 -0
  474. package/lib/session-folders.js +7 -2
  475. package/lib/session-fork-history.js +21 -0
  476. package/lib/session-github.js +9 -2
  477. package/lib/session-hygiene.js +3 -35
  478. package/lib/session-permission-mode.js +4 -3
  479. package/lib/session-skill-scope.js +11 -0
  480. package/lib/session-visibility.js +5 -2
  481. package/lib/session-work-primary.js +50 -0
  482. package/lib/sessions.js +64 -99
  483. package/lib/shared-browser-cookie-sync.js +32 -0
  484. package/lib/shared-browser-cookies.js +79 -0
  485. package/lib/shared-browser-runtime.js +25 -9
  486. package/lib/shared-browser-storage-sync.js +43 -0
  487. package/lib/shared-browser-storage.js +101 -0
  488. package/lib/shared-browser-tools.js +2 -1
  489. package/lib/shared-browser-worker.js +46 -10
  490. package/lib/terminal-manager.js +65 -6
  491. package/lib/tool-llm.js +1 -1
  492. package/lib/tui-transcript-index.js +1 -132
  493. package/lib/user-knowledge-service.js +23 -1
  494. package/lib/users-auth.js +38 -3
  495. package/lib/users-contextual-clay-preferences.js +31 -0
  496. package/lib/users-create.js +97 -0
  497. package/lib/users-permissions.js +2 -8
  498. package/lib/users-server-pin.js +37 -0
  499. package/lib/users.js +18 -84
  500. package/lib/vendor-login-browser.js +1 -11
  501. package/lib/workspace-assignment-service.js +25 -0
  502. package/lib/workspace-query-access.js +4 -1
  503. package/lib/workspace-query-mcp-server.js +1 -1
  504. package/lib/workspace-query-service.js +13 -2
  505. package/lib/ws-request.js +1 -0
  506. package/lib/ws-schema.js +18 -2
  507. package/lib/yoke/acp-agent-profiles.js +7 -5
  508. package/lib/yoke/adapters/antigravity.js +3 -14
  509. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  510. package/lib/yoke/adapters/claude.js +54 -50
  511. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  512. package/lib/yoke/adapters/codex.js +53 -111
  513. package/lib/yoke/adapters/kiro.js +3 -1
  514. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  515. package/lib/yoke/adapters/muse.js +122 -0
  516. package/lib/yoke/auth-events.js +17 -0
  517. package/lib/yoke/claude-hook-installer.js +271 -0
  518. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  519. package/lib/yoke/claude-maintenance.js +55 -0
  520. package/lib/yoke/claude-permission-patterns.js +53 -0
  521. package/lib/yoke/claude-response-recovery.js +155 -0
  522. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  523. package/lib/yoke/claude-worker-startup.js +28 -0
  524. package/lib/yoke/clay-skill-install.js +33 -0
  525. package/lib/yoke/cli-sessions.js +300 -0
  526. package/lib/yoke/codex-app-server.js +7 -12
  527. package/lib/yoke/codex-idle-recovery.js +81 -1
  528. package/lib/yoke/codex-recovery-admission.js +164 -0
  529. package/lib/yoke/configuration.js +13 -0
  530. package/lib/yoke/discovery.js +70 -0
  531. package/lib/yoke/errors.js +8 -0
  532. package/lib/yoke/host-environment.js +7 -0
  533. package/lib/yoke/index.js +9 -51
  534. package/lib/yoke/interface.js +116 -0
  535. package/lib/yoke/kiro-acp-server.js +5 -5
  536. package/lib/yoke/legacy-sessions.js +89 -0
  537. package/lib/yoke/login-protocol.js +25 -0
  538. package/lib/yoke/maintenance.js +12 -0
  539. package/lib/yoke/mcp-proxy.js +32 -0
  540. package/lib/yoke/mcp-source-adapters.js +91 -0
  541. package/lib/yoke/mcp-source-context.js +42 -0
  542. package/lib/yoke/mcp-source-discovery.js +169 -0
  543. package/lib/yoke/mcp-source-plugins.js +68 -0
  544. package/lib/yoke/mcp-source-restrictions.js +47 -0
  545. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  546. package/lib/yoke/model-preferences.js +6 -0
  547. package/lib/yoke/muse-artifacts.js +36 -0
  548. package/lib/yoke/muse-controls.js +50 -0
  549. package/lib/yoke/muse-input.js +64 -0
  550. package/lib/yoke/muse-items.js +157 -0
  551. package/lib/yoke/muse-policy.js +52 -0
  552. package/lib/yoke/muse-query.js +428 -0
  553. package/lib/yoke/muse-runtime.js +211 -0
  554. package/lib/yoke/muse-services.js +79 -0
  555. package/lib/yoke/muse-skills.js +79 -0
  556. package/lib/yoke/muse-turns.js +71 -0
  557. package/lib/yoke/options/acp.js +2 -0
  558. package/lib/yoke/options/antigravity.js +2 -0
  559. package/lib/yoke/options/claude.js +21 -0
  560. package/lib/yoke/options/codex-defaults.js +29 -0
  561. package/lib/yoke/options/codex-settings.js +21 -0
  562. package/lib/yoke/options/codex.js +6 -0
  563. package/lib/yoke/options/kiro-defaults.js +22 -0
  564. package/lib/yoke/options/kiro.js +5 -0
  565. package/lib/yoke/options/muse.js +5 -0
  566. package/lib/yoke/provider-contract.js +5 -0
  567. package/lib/yoke/providers/antigravity-executable.js +8 -0
  568. package/lib/yoke/providers/claude-executable.js +40 -0
  569. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  570. package/lib/yoke/providers/claude-process.js +97 -0
  571. package/lib/yoke/providers/claude-provision.js +75 -0
  572. package/lib/yoke/providers/codex-errors.js +8 -0
  573. package/lib/yoke/providers/muse-executable.js +17 -0
  574. package/lib/yoke/provisioning.js +6 -0
  575. package/lib/yoke/query-options.js +10 -0
  576. package/lib/yoke/response-recovery.js +41 -0
  577. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  578. package/lib/yoke/sdk-server-factory.js +7 -0
  579. package/lib/yoke/session-history.js +8 -0
  580. package/lib/yoke/session-tool-transport.js +34 -13
  581. package/lib/yoke/settings.js +23 -0
  582. package/lib/yoke/source-locations.js +4 -0
  583. package/lib/yoke/title-query.js +18 -7
  584. package/lib/yoke/tui-transcript-index.js +132 -0
  585. package/lib/yoke/usage.js +48 -0
  586. package/lib/yoke/user-input.js +13 -0
  587. package/lib/yoke/vendor-registry.js +81 -1
  588. package/package.json +11 -4
@@ -0,0 +1,186 @@
1
+ var crypto = require('crypto');
2
+ var webauthn = require('@simplewebauthn/server');
3
+ var passwords = require('./account-passwords');
4
+ var policyStore = require('./account-passkey-policy');
5
+
6
+ function attachAccountPasskeys(ctx) {
7
+ var users = ctx.users;
8
+ var http = ctx.http;
9
+ var challenges = require('./account-passkey-challenges').attachChallenges(ctx);
10
+ function policy() { return policyStore.configured(users.loadUsers()); }
11
+ function eligible(user) { return user && user.passwordHash && !user.mustChangePin; }
12
+ function current(req, original) {
13
+ var user = ctx.getUser(req);
14
+ if (!eligible(user) || original && (user.id !== original.id || user.passwordHash !== original.passwordHash)) {
15
+ throw new Error('Your account credentials changed. Sign in again.');
16
+ }
17
+ return user;
18
+ }
19
+ async function reauthenticate(req, data, admin) {
20
+ var user = current(req);
21
+ if (admin && user.role !== 'admin') throw new Error('Administrator access required.');
22
+ http.throttle(req, user.username);
23
+ if (!await passwords.verify(data.currentPassword, user.passwordHash)) throw new Error('Current password is incorrect.');
24
+ user = current(req, user);
25
+ if (admin && user.role !== 'admin') throw new Error('Administrator access required.');
26
+ http.clearAttempts(req, user.username);
27
+ return user;
28
+ }
29
+ function samePolicy(expected) {
30
+ var now = policy();
31
+ if (!now || now.origin !== expected.origin || now.rpID !== expected.rpID) throw new Error('Passkey settings changed. Please retry.');
32
+ }
33
+ function label(value) {
34
+ if (typeof value !== 'string' || !value.trim() || Array.from(value.trim()).length > 80) throw new Error('Give this passkey a name of 1–80 characters.');
35
+ return value.trim();
36
+ }
37
+ function responseData(data) {
38
+ var response = data.response;
39
+ if (!response || typeof response.id !== 'string' || !response.response || typeof response.response.clientDataJSON !== 'string') throw new Error('Invalid passkey response.');
40
+ var client = JSON.parse(Buffer.from(response.response.clientDataJSON, 'base64url').toString('utf8'));
41
+ if (client.crossOrigin || client.topOrigin) throw new Error('Open Clay directly to use a passkey.');
42
+ return response;
43
+ }
44
+ function list(user) {
45
+ return (user.passkeys || []).map(function (key) { return { id: key.id, name: key.name, createdAt: key.createdAt, lastUsedAt: key.lastUsedAt || null }; });
46
+ }
47
+ async function registerOptions(req, res, data, config) {
48
+ var user = await reauthenticate(req, data);
49
+ var name = label(data.name);
50
+ if ((user.passkeys || []).length >= 10) throw new Error('You can register up to 10 passkeys.');
51
+ var handle = user.passkeyUserHandle || crypto.randomBytes(32).toString('base64url');
52
+ var options = await webauthn.generateRegistrationOptions({
53
+ rpName: 'Clay', rpID: config.rpID, userID: Buffer.from(handle, 'base64url'), userName: user.username,
54
+ attestationType: 'none', timeout: 60000, supportedAlgorithmIDs: [-7, -257],
55
+ authenticatorSelection: { residentKey: 'required', userVerification: 'required' },
56
+ excludeCredentials: (user.passkeys || []).map(function (key) { return { id: key.id, transports: key.transports }; }),
57
+ });
58
+ current(req, user); samePolicy(config);
59
+ var cookie = challenges.issue(req, config, { purpose: 'register', challenge: options.challenge, userId: user.id,
60
+ passwordHash: user.passwordHash, session: ctx.sessionToken(req), handle: handle, name: name });
61
+ http.reply(res, 200, { options: options }, cookie);
62
+ }
63
+ async function registerVerify(req, res, data, config) {
64
+ var proof = challenges.take(req, config, 'register');
65
+ var user = current(req, { id: proof.userId, passwordHash: proof.passwordHash });
66
+ if (ctx.sessionToken(req) !== proof.session) throw new Error('Use the same signed-in session to register this passkey.');
67
+ var response = responseData(data);
68
+ var result;
69
+ try {
70
+ result = await webauthn.verifyRegistrationResponse({ response: response, expectedChallenge: proof.challenge,
71
+ expectedOrigin: config.origin, expectedRPID: config.rpID, requireUserVerification: true, supportedAlgorithmIDs: [-7, -257] });
72
+ } catch (error) { throw new Error('Could not verify this passkey. Please try registering again.'); }
73
+ if (!result.verified) throw new Error('Could not verify this passkey.');
74
+ current(req, user); samePolicy(config);
75
+ if (proof.until <= Date.now()) throw new Error('Registration expired. Please try again.');
76
+ var state = users.loadUsers();
77
+ user = state.users.find(function (entry) { return entry.id === user.id; });
78
+ var info = result.registrationInfo; var key = info.credential;
79
+ if (key.id !== response.id || state.users.some(function (entry) { return (entry.passkeys || []).some(function (saved) { return saved.id === key.id; }); })) {
80
+ throw new Error('This passkey is already registered or has an invalid identifier.');
81
+ }
82
+ if ((user.passkeys || []).length >= 10 || user.passkeyUserHandle && user.passkeyUserHandle !== proof.handle) throw new Error('Passkeys changed. Please register again.');
83
+ user.passkeyUserHandle = proof.handle;
84
+ user.passkeys = (user.passkeys || []).concat([{ id: key.id, publicKey: Buffer.from(key.publicKey).toString('base64url'),
85
+ counter: key.counter, transports: key.transports, name: proof.name, createdAt: new Date().toISOString(),
86
+ deviceType: info.credentialDeviceType, backedUp: info.credentialBackedUp }]);
87
+ users.saveUsers(state);
88
+ http.reply(res, 200, { ok: true, passkeys: list(user) }, challenges.cookie('', config.origin));
89
+ }
90
+ async function loginOptions(req, res, config) {
91
+ http.throttle(req, 'passkey:' + ctx.clientAddress(req));
92
+ var options = await webauthn.generateAuthenticationOptions({ rpID: config.rpID, userVerification: 'required', timeout: 60000 });
93
+ samePolicy(config);
94
+ http.reply(res, 200, { options: options }, challenges.issue(req, config, { purpose: 'login', challenge: options.challenge }));
95
+ }
96
+ async function loginVerify(req, res, data, config) {
97
+ var proof = challenges.take(req, config, 'login');
98
+ var response = responseData(data);
99
+ var state = users.loadUsers();
100
+ var user = state.users.find(function (entry) { return (entry.passkeys || []).some(function (key) { return key.id === response.id; }); });
101
+ if (!eligible(user) || response.response.userHandle !== user.passkeyUserHandle) throw new Error('Could not sign in with this passkey.');
102
+ var key = user.passkeys.find(function (entry) { return entry.id === response.id; });
103
+ var result;
104
+ try {
105
+ result = await webauthn.verifyAuthenticationResponse({ response: response, expectedChallenge: proof.challenge,
106
+ expectedOrigin: config.origin, expectedRPID: config.rpID, requireUserVerification: true,
107
+ credential: { id: key.id, publicKey: new Uint8Array(Buffer.from(key.publicKey, 'base64url')), counter: key.counter, transports: key.transports } });
108
+ } catch (error) { throw new Error('Could not sign in with this passkey. Try again or use your password.'); }
109
+ if (!result.verified || result.authenticationInfo.credentialDeviceType !== key.deviceType) throw new Error('Could not sign in with this passkey.');
110
+ samePolicy(config);
111
+ state = users.loadUsers();
112
+ var latest = state.users.find(function (entry) { return entry.id === user.id; });
113
+ var latestKey = latest && (latest.passkeys || []).find(function (entry) { return entry.id === key.id; });
114
+ if (!eligible(latest) || latest.passwordHash !== user.passwordHash || !latestKey || latestKey.publicKey !== key.publicKey ||
115
+ latestKey.counter !== key.counter || proof.until <= Date.now()) throw new Error('Account credentials changed. Please try again.');
116
+ latestKey.counter = result.authenticationInfo.newCounter;
117
+ latestKey.backedUp = result.authenticationInfo.credentialBackedUp;
118
+ latestKey.lastUsedAt = new Date().toISOString();
119
+ users.saveUsers(state);
120
+ var session = ctx.createSession(latest.id, latest.passwordHash, key.id, config.origin.indexOf('https:') === 0, req);
121
+ http.clearAttempts(req, 'passkey:' + ctx.clientAddress(req));
122
+ http.reply(res, 200, { ok: true }, session.cookies.concat([challenges.cookie('', config.origin)]));
123
+ }
124
+ async function manage(req, res, data, config, remove) {
125
+ var user = await reauthenticate(req, data);
126
+ samePolicy(config);
127
+ var state = users.loadUsers();
128
+ user = state.users.find(function (entry) { return entry.id === user.id; });
129
+ var key = (user.passkeys || []).find(function (entry) { return entry.id === data.id; });
130
+ if (!key) throw new Error('Passkey not found.');
131
+ if (remove) user.passkeys = user.passkeys.filter(function (entry) { return entry.id !== key.id; });
132
+ else key.name = label(data.name);
133
+ users.saveUsers(state);
134
+ // Removing a passkey also invalidates its sessions through the live session gate.
135
+ var cookie = remove ? ctx.createSession(user.id, user.passwordHash, null, config.origin.indexOf('https:') === 0, req).cookies : undefined;
136
+ http.reply(res, 200, { ok: true, passkeys: list(user) }, cookie);
137
+ }
138
+ function handleRequest(req, res, url) {
139
+ if (url === '/auth/passkey-browser.js' && req.method === 'GET') {
140
+ res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-cache' });
141
+ res.end(require('fs').readFileSync(require('path').join(__dirname, 'public/modules/passkey-browser.js'), 'utf8')); return true;
142
+ }
143
+ if (url === '/auth/logout' && req.method === 'POST') challenges.clear(req);
144
+ if (url === '/account/passkeys' && req.method === 'GET') {
145
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', 'Content-Security-Policy': "frame-ancestors 'none'" });
146
+ res.end(eligible(ctx.getUser(req)) ? require('./account-passkey-page').render() : require('./account-password-page').render());
147
+ return true;
148
+ }
149
+ if (url === '/auth/passkeys/status' && req.method === 'GET') { http.reply(res, 200, { origin: policy() && policy().origin }); return true; }
150
+ if (url === '/api/passkey-policy') {
151
+ var admin = ctx.getUser(req);
152
+ if (!admin || admin.role !== 'admin') { http.reply(res, 403, { error: 'Administrator access required.' }); return true; }
153
+ if (req.method === 'GET') {
154
+ var state = users.loadUsers();
155
+ http.reply(res, 200, { available: state.accountAuthMethod === 'password', origin: policy() && policy().origin, locked: policyStore.count(state) > 0 }); return true;
156
+ }
157
+ if (req.method !== 'PUT') { http.reply(res, 405, { error: 'Method not allowed.' }); return true; }
158
+ http.read(req, res, async function (data) {
159
+ if (req.headers['sec-fetch-site'] === 'cross-site') throw new Error('Open Clay directly to change passkey settings.');
160
+ await reauthenticate(req, data, true);
161
+ var saved = policyStore.save(users, data.origin);
162
+ http.reply(res, 200, { ok: true, available: true, origin: saved.origin, locked: policyStore.count(users.loadUsers()) > 0 });
163
+ }); return true;
164
+ }
165
+ if (url.indexOf('/api/passkeys') !== 0 && url.indexOf('/auth/passkeys/') !== 0) return false;
166
+ var config = policy();
167
+ if (!config) { http.reply(res, 409, { error: 'Your administrator needs to configure passkeys first.' }); return true; }
168
+ if (url === '/api/passkeys' && req.method === 'GET') {
169
+ var user = ctx.getUser(req);
170
+ http.reply(res, eligible(user) ? 200 : 401, eligible(user) ? { origin: config.origin, passkeys: list(user) } : { error: 'Sign in to manage passkeys.' }); return true;
171
+ }
172
+ if (req.method !== 'POST') { http.reply(res, 405, { error: 'Method not allowed.' }); return true; }
173
+ if (req.headers.origin !== config.origin || req.headers['sec-fetch-site'] === 'cross-site') { http.reply(res, 403, { error: 'Use the configured Clay address to use passkeys.' }); return true; }
174
+ http.read(req, res, async function (data) {
175
+ if (url === '/api/passkeys/register/options') return registerOptions(req, res, data, config);
176
+ if (url === '/api/passkeys/register/verify') return registerVerify(req, res, data, config);
177
+ if (url === '/auth/passkeys/login/options') return loginOptions(req, res, config);
178
+ if (url === '/auth/passkeys/login/verify') return loginVerify(req, res, data, config);
179
+ if (url === '/api/passkeys/remove' || url === '/api/passkeys/rename') return manage(req, res, data, config, url.endsWith('/remove'));
180
+ http.reply(res, 404, { error: 'Passkey endpoint not found.' });
181
+ }, 32768);
182
+ return true;
183
+ }
184
+ return { handleRequest: handleRequest };
185
+ }
186
+ module.exports = { attachAccountPasskeys: attachAccountPasskeys };
@@ -0,0 +1,127 @@
1
+ var crypto = require('crypto');
2
+ var passwords = require('./account-passwords');
3
+ var attachPolicy = require('./account-auth-policy').attachPolicy;
4
+
5
+ function attachAccountPasswords(ctx) {
6
+ var users = ctx.users;
7
+ var policy = attachPolicy(users);
8
+ var pending = new Map();
9
+
10
+ var cookieName = ctx.devMode ? 'clay_password_transition_dev' : 'clay_password_transition';
11
+ function reply(res, code, data, cookies) {
12
+ var headers = { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' };
13
+ if (cookies) headers['Set-Cookie'] = cookies;
14
+ res.writeHead(code, headers); res.end(JSON.stringify(data));
15
+ }
16
+ function transitionCookie(token, req) {
17
+ return cookieName + '=' + token + '; Path=/; HttpOnly; SameSite=Strict; Max-Age=' + (token ? '600' : '0') + (ctx.tlsOptions || req && ctx.transport.info(req).secure ? '; Secure' : '');
18
+ }
19
+ function read(req, res, action, maxBytes) {
20
+ if ((req.headers['content-type'] || '').split(';')[0] !== 'application/json') { reply(res, 415, { error: 'JSON required.' }); return; }
21
+ var body = ''; var oversized = false;
22
+ req.on('data', function (chunk) { if (oversized) return; body += chunk; if (Buffer.byteLength(body) > (maxBytes || 4096)) { oversized = true; reply(res, 413, { error: 'Request too large.' }); } });
23
+ req.on('end', function () {
24
+ if (oversized) return;
25
+ var data; try { data = JSON.parse(body); } catch (error) { reply(res, 400, { error: 'Invalid request.' }); return; }
26
+ if (!data || typeof data !== 'object' || Array.isArray(data)) { reply(res, 400, { error: 'Invalid request.' }); return; }
27
+ Promise.resolve().then(function () { return action(data); }).catch(function (error) { reply(res, error.status || 400, { error: error.message || 'Could not complete sign-in.' }); });
28
+ });
29
+ }
30
+ function throttle(req, username) { ctx.throttle.consume(req, username); }
31
+ function clearAttempts(req, username) { ctx.throttle.success(req, username); }
32
+ function grant(user, res, req) {
33
+ var session = ctx.createSession(user.id, user.passwordHash, null, ctx.transport.info(req).secure, req);
34
+ reply(res, 200, { ok: true }, session.cookies.concat([transitionCookie('', req)]));
35
+ }
36
+ function startTransition(user, res, req) {
37
+ var now = Date.now();
38
+ pending.forEach(function (entry, key) { if (entry.until <= now) pending.delete(key); });
39
+ if (pending.size >= 1000) throw new Error('Sign-in is busy. Please retry shortly.');
40
+ var token = crypto.randomBytes(32).toString('hex');
41
+ pending.set(token, { userId: user.id, pinHash: user.pinHash, until: now + 600000, busy: false });
42
+ reply(res, 200, { ok: true, passwordRequired: true }, transitionCookie(token, req));
43
+ }
44
+ function handleRequest(req, res, url) {
45
+ if (req.method === 'POST' && url === '/auth/logout') pending.delete(ctx.parseCookies(req)[cookieName]);
46
+ if (url === '/api/account-auth-policy') {
47
+ var admin = ctx.getUser(req);
48
+ if (!admin || admin.role !== 'admin') { reply(res, 403, { error: 'Administrator access required.' }); return true; }
49
+ if (req.method === 'GET') { reply(res, 200, { method: policy.enabled() ? 'password' : 'pin', available: policy.available() }); return true; }
50
+ if (req.method !== 'PUT') { reply(res, 405, { error: 'Method not allowed.' }); return true; }
51
+ read(req, res, function (data) {
52
+ if (data.method !== 'password') throw new Error('Only the password transition is supported.');
53
+ policy.activate();
54
+ reply(res, 200, { ok: true, method: 'password' });
55
+ if (ctx.onPolicyChanged) ctx.onPolicyChanged();
56
+ });
57
+ return true;
58
+ }
59
+ if (!policy.enabled()) return false;
60
+ if (req.method === 'GET' && url === '/account/password') {
61
+ var signedIn = ctx.getUser(req);
62
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
63
+ res.end(signedIn ? require('./account-password-page').render(true) : require('./account-login-page').render({ password: true }));
64
+ return true;
65
+ }
66
+ if (req.method === 'POST' && url === '/auth/password-change') {
67
+ read(req, res, async function (data) {
68
+ var user = ctx.getUser(req);
69
+ if (!user) throw new Error('Sign in again to change your password.');
70
+ throttle(req, user.username);
71
+ if (!await passwords.verify(data.currentPassword, user.passwordHash)) throw new Error('Current password is incorrect.');
72
+ if (data.password !== data.confirmPassword) throw new Error('Passwords do not match.');
73
+ var hash = await passwords.hash(data.password);
74
+ var state = users.loadUsers();
75
+ var current = state.users.find(function (entry) { return entry.id === user.id; });
76
+ if (!current || current.passwordHash !== user.passwordHash || current.mustChangePin) throw new Error('Credentials changed. Sign in again.');
77
+ current.passwordHash = hash; users.saveUsers(state);
78
+ ctx.revokeSessions(user.id); clearAttempts(req, user.username); grant(current, res, req);
79
+ });
80
+ return true;
81
+ }
82
+ if (url === '/api/user/pin') {
83
+ reply(res, 409, { error: 'This server uses account passwords. Contact an administrator for credential recovery.' });
84
+ return true;
85
+ }
86
+ if (req.method === 'POST' && url === '/auth/login') {
87
+ read(req, res, async function (data) {
88
+ throttle(req, data.username);
89
+ var user = typeof data.username === 'string' ? users.findUserByUsername(data.username) : null;
90
+ if (data.pin !== undefined) {
91
+ if (!user || user.passwordHash && !user.mustChangePin || typeof data.pin !== 'string' || !/^\d{6}$/.test(data.pin) || !users.verifyPin(data.pin, user.pinHash, user)) throw new Error('Invalid username or account PIN.');
92
+ clearAttempts(req, user.username); startTransition(user, res, req); return;
93
+ }
94
+ var originalHash = user && user.passwordHash;
95
+ if (!await passwords.verify(data.password, originalHash)) throw new Error('Invalid username or password.');
96
+ user = users.findUserById(user.id);
97
+ if (!user || user.passwordHash !== originalHash || user.mustChangePin) throw new Error('Account credentials changed. Please sign in again.');
98
+ clearAttempts(req, user.username); grant(user, res, req);
99
+ });
100
+ return true;
101
+ }
102
+ if (req.method === 'POST' && url === '/auth/password-transition') {
103
+ read(req, res, async function (data) {
104
+ var token = ctx.parseCookies(req)[cookieName];
105
+ var proof = pending.get(token);
106
+ if (!proof || proof.until <= Date.now() || proof.busy) throw new Error('Verify your account PIN again.');
107
+ proof.busy = true;
108
+ try {
109
+ if (data.password !== data.confirmPassword) throw new Error('Passwords do not match.');
110
+ var hash = await passwords.hash(data.password);
111
+ var state = users.loadUsers();
112
+ var user = state.users.find(function (entry) { return entry.id === proof.userId; });
113
+ if (!user || user.pinHash !== proof.pinHash || user.passwordHash && !user.mustChangePin || proof.until <= Date.now()) throw new Error('Account credentials changed. Verify your PIN again.');
114
+ user.passwordHash = hash; user.pinHash = null; user.mustChangePin = false;
115
+ users.saveUsers(state);
116
+ pending.delete(token);
117
+ ctx.revokeSessions(user.id);
118
+ grant(user, res, req);
119
+ } finally { proof.busy = false; }
120
+ });
121
+ return true;
122
+ }
123
+ return false;
124
+ }
125
+ return { handleRequest: handleRequest, policy: policy, http: { read: read, reply: reply, throttle: throttle, clearAttempts: clearAttempts } };
126
+ }
127
+ module.exports = { attachAccountPasswords: attachAccountPasswords };
@@ -0,0 +1,53 @@
1
+ var passwords = require('./account-passwords');
2
+ function attachAccountSecurity(ctx) {
3
+ function sessionList(req, user) {
4
+ var current = ctx.sessions.get(ctx.sessionToken(req), false);
5
+ return ctx.sessions.list(user.id).filter(function (entry) { return !!ctx.validSessionUser(entry); }).map(function (entry) {
6
+ return { id: entry.id, current: !!current && entry.id === current.id, userAgent: entry.userAgent || '',
7
+ address: entry.address, createdAt: entry.migrated ? null : entry.createdAt,
8
+ lastSeenAt: entry.migrated ? entry.observedAt || null : entry.lastSeenAt,
9
+ unknownDevice: !!entry.migrated && !entry.observedAt,
10
+ expiresAt: entry.expiresAt, method: entry.passkeyId ? 'Passkey' : entry.credentialVersion ? 'Password' : 'Account sign-in', migrated: !!entry.migrated };
11
+ }).sort(function (a, b) { return Number(b.current) - Number(a.current) || (b.lastSeenAt || 0) - (a.lastSeenAt || 0); });
12
+ }
13
+ async function verify(req, data, original) {
14
+ ctx.http.throttle(req, original.username);
15
+ if (!original.passwordHash || !await passwords.verify(data.currentPassword, original.passwordHash)) throw new Error('Current password is incorrect.');
16
+ var current = ctx.getUser(req);
17
+ if (!current || current.id !== original.id || current.passwordHash !== original.passwordHash) throw new Error('Sign in again to continue.');
18
+ ctx.http.clearAttempts(req, current.username); return current;
19
+ }
20
+ function handleRequest(req, res, url) {
21
+ if (url !== '/api/account-sessions' && url !== '/api/account-transport' && url !== '/account/sessions') return false;
22
+ var user = ctx.getUser(req);
23
+ if (!user) { ctx.http.reply(res, 401, { error: 'Sign in again to manage your account.' }); return true; }
24
+ if (url === '/account/sessions' && req.method === 'GET') {
25
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', 'Content-Security-Policy': "frame-ancestors 'none'" });
26
+ res.end(require('./account-session-page').render()); return true;
27
+ }
28
+ if (url === '/api/account-transport') {
29
+ if (user.role !== 'admin') { ctx.http.reply(res, 403, { error: 'Administrator access required.' }); return true; }
30
+ if (req.method === 'GET') { ctx.http.reply(res, 200, ctx.transport.settings()); return true; }
31
+ if (req.method !== 'PUT') { ctx.http.reply(res, 405, { error: 'Method not allowed.' }); return true; }
32
+ ctx.http.read(req, res, async function (data) {
33
+ var admin = await verify(req, data, user);
34
+ if (admin.role !== 'admin') throw new Error('Administrator access required.');
35
+ var saved = ctx.transport.save(data); ctx.http.reply(res, 200, Object.assign({ ok: true }, saved));
36
+ }); return true;
37
+ }
38
+ if (req.method === 'GET') { ctx.http.reply(res, 200, { sessions: sessionList(req, user), maximumDays: 30, idleDays: 7 }); return true; }
39
+ if (req.method !== 'POST') { ctx.http.reply(res, 405, { error: 'Method not allowed.' }); return true; }
40
+ ctx.http.read(req, res, function (data) {
41
+ var active = ctx.getUser(req);
42
+ if (!active || active.id !== user.id) throw new Error('Sign in again to manage sessions.');
43
+ var current = ctx.sessions.get(ctx.sessionToken(req), false);
44
+ if (!current || typeof data.id !== 'string') throw new Error('Invalid session.');
45
+ if (data.id !== 'others' && !ctx.sessions.list(user.id).some(function (entry) { return entry.id === data.id; })) throw new Error('Session not found.');
46
+ ctx.sessions.revoke(user.id, data.id, current.id);
47
+ ctx.http.reply(res, 200, { ok: true, signedOut: data.id === current.id, sessions: sessionList(req, user) });
48
+ });
49
+ return true;
50
+ }
51
+ return { handleRequest: handleRequest };
52
+ }
53
+ module.exports = { attachAccountSecurity: attachAccountSecurity };
@@ -18,6 +18,13 @@ function attachAdmin(ctx) {
18
18
  var onGetProjectAccess = ctx.onGetProjectAccess;
19
19
  var onProjectOwnerChanged = ctx.onProjectOwnerChanged;
20
20
 
21
+ function verifyCurrentOwner(req, route) {
22
+ if (!route || req.method === "GET" || !users.isMultiUser()) return;
23
+ var actor = getMultiUserFromReq(req);
24
+ var access = onGetProjectAccess && onGetProjectAccess(route[1]);
25
+ if (!actor || !access || access.ownerId !== actor.id || !users.findUserById(access.ownerId)) throw new Error("Project owner access required");
26
+ }
27
+
21
28
  function handleRequest(req, res, fullUrl) {
22
29
  var mateAccessRoute = /^\/api\/admin\/projects\/([^/]+)\/(visibility|owner|users|access)$/.exec(fullUrl);
23
30
  if (mateAccessRoute) {
@@ -31,6 +38,25 @@ function attachAdmin(ctx) {
31
38
  }
32
39
 
33
40
 
41
+ // Project content authority belongs to the valid owner, never the role.
42
+ if (mateAccessRoute && users.isMultiUser()) {
43
+ var actor = getMultiUserFromReq(req);
44
+ var governing = onGetProjectAccess && onGetProjectAccess(mateAccessRoute[1]);
45
+ var validOwner = governing && governing.ownerId && users.findUserById(governing.ownerId);
46
+ var owns = actor && validOwner && actor.id === validOwner.id;
47
+ if (req.method !== "GET" && !owns) {
48
+ res.writeHead(403, { "Content-Type": "application/json" });
49
+ res.end(JSON.stringify({ error: "Only the valid project owner can change access." }));
50
+ return true;
51
+ }
52
+ if (req.method === "GET" && !owns && actor && actor.role === "admin") {
53
+ var overview = require("./project-admin-overview").metadataFor(actor, target && target.getStatus(), governing, users);
54
+ res.writeHead(overview ? 200 : 404, { "Content-Type": "application/json", "Cache-Control": "no-store" });
55
+ res.end(JSON.stringify(overview || { error: "Project not found" }));
56
+ return true;
57
+ }
58
+ }
59
+
34
60
  // --- Admin API endpoints (multi-user mode only) ---
35
61
 
36
62
  // List all users (admin only)
@@ -83,6 +109,16 @@ function attachAdmin(ctx) {
83
109
  // Look up the user before deletion to get linuxUser for deactivation
84
110
  var targetUser = users.findUserById(targetUserId);
85
111
  var targetLinuxUser = targetUser ? targetUser.linuxUser : null;
112
+ var owned = false;
113
+ projects.forEach(function (project, slug) {
114
+ var access = onGetProjectAccess && onGetProjectAccess(slug);
115
+ if (access && !access.isMate && access.ownerId === targetUserId) owned = true;
116
+ });
117
+ if (owned) {
118
+ res.writeHead(409, { "Content-Type": "application/json" });
119
+ res.end(JSON.stringify({ error: "The owner must transfer ordinary projects before account deletion." }));
120
+ return true;
121
+ }
86
122
  var result = users.removeUser(targetUserId);
87
123
  if (result.error) {
88
124
  res.writeHead(404, { "Content-Type": "application/json" });
@@ -118,6 +154,7 @@ function attachAdmin(ctx) {
118
154
  req.on("end", function () {
119
155
  try {
120
156
  var data = JSON.parse(body);
157
+ verifyCurrentOwner(req, mateAccessRoute);
121
158
  if (!data.username || typeof data.username !== "string" || data.username.trim().length < 1) {
122
159
  res.writeHead(400, { "Content-Type": "application/json" });
123
160
  res.end('{"error":"Username is required"}');
@@ -182,6 +219,11 @@ function attachAdmin(ctx) {
182
219
  res.end('{"error":"User not found"}');
183
220
  return true;
184
221
  }
222
+ if (mu.id !== targetUserId) {
223
+ res.writeHead(403, { "Content-Type": "application/json" });
224
+ res.end(JSON.stringify({ error: "Use account-owner credential recovery. Administrators cannot impersonate another account." }));
225
+ return true;
226
+ }
185
227
  var newPin = users.generatePin();
186
228
  var pinResult = users.updateUserPin(targetUserId, newPin);
187
229
  if (pinResult.error) {
@@ -222,15 +264,10 @@ function attachAdmin(ctx) {
222
264
  req.on("data", function(chunk) { body += chunk; });
223
265
  req.on("end", function() {
224
266
  try {
225
- var parsed = JSON.parse(body);
226
- var result = users.updateLinuxUser(targetUserId, parsed.linuxUser || null);
227
- if (result.error) {
228
- res.writeHead(400, { "Content-Type": "application/json" });
229
- res.end(JSON.stringify({ error: result.error }));
230
- } else {
231
- res.writeHead(200, { "Content-Type": "application/json" });
232
- res.end('{"ok":true}');
233
- }
267
+ JSON.parse(body);
268
+ res.writeHead(403, { "Content-Type": "application/json" });
269
+ res.end(JSON.stringify({ error: "OS identity mapping requires server-side verified provisioning." }));
270
+ return;
234
271
  } catch (e) {
235
272
  res.writeHead(400, { "Content-Type": "application/json" });
236
273
  res.end('{"error":"Invalid request body"}');
@@ -363,6 +400,7 @@ function attachAdmin(ctx) {
363
400
  req.on("end", function () {
364
401
  try {
365
402
  var data = JSON.parse(body);
403
+ verifyCurrentOwner(req, mateAccessRoute);
366
404
  if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
367
405
  res.writeHead(400, { "Content-Type": "application/json" });
368
406
  res.end('{"error":"Valid email is required"}');
@@ -429,6 +467,7 @@ function attachAdmin(ctx) {
429
467
  req.on("end", function () {
430
468
  try {
431
469
  var data = JSON.parse(body);
470
+ verifyCurrentOwner(req, mateAccessRoute);
432
471
  // Allow clearing SMTP config by sending empty fields
433
472
  if (!data.host && !data.user && !data.pass && !data.from) {
434
473
  smtp.saveSmtpConfig(null);
@@ -484,6 +523,7 @@ function attachAdmin(ctx) {
484
523
  req.on("end", function () {
485
524
  try {
486
525
  var data = JSON.parse(body);
526
+ verifyCurrentOwner(req, mateAccessRoute);
487
527
  // Use provided config or fall back to saved
488
528
  var existingCfg = smtp.getSmtpConfig();
489
529
  var pass = data.pass;
@@ -548,6 +588,7 @@ function attachAdmin(ctx) {
548
588
  req.on("end", function () {
549
589
  try {
550
590
  var data = JSON.parse(body);
591
+ verifyCurrentOwner(req, mateAccessRoute);
551
592
  if (data.visibility !== "public" && data.visibility !== "private") {
552
593
  res.writeHead(400, { "Content-Type": "application/json" });
553
594
  res.end('{"error":"Visibility must be public or private"}');
@@ -582,9 +623,10 @@ function attachAdmin(ctx) {
582
623
  return true;
583
624
  }
584
625
  var mu = getMultiUserFromReq(req);
585
- if (!mu || mu.role !== "admin") {
626
+ var ownerAccess = onGetProjectAccess && onGetProjectAccess(fullUrl.split("/")[4]);
627
+ if (!mu || !ownerAccess || mu.id !== ownerAccess.ownerId) {
586
628
  res.writeHead(403, { "Content-Type": "application/json" });
587
- res.end('{"error":"Admin access required"}');
629
+ res.end('{"error":"Project owner access required"}');
588
630
  return true;
589
631
  }
590
632
  var projSlug = fullUrl.split("/")[4];
@@ -598,17 +640,21 @@ function attachAdmin(ctx) {
598
640
  req.on("end", function () {
599
641
  try {
600
642
  var data = JSON.parse(body);
643
+ verifyCurrentOwner(req, mateAccessRoute);
601
644
  var targetCtx = projects.get(projSlug);
602
645
  if (!targetCtx) {
603
646
  res.writeHead(404, { "Content-Type": "application/json" });
604
647
  res.end('{"error":"Project not found"}');
605
648
  return;
606
649
  }
607
- var ownerId = data.userId || null;
650
+ var ownerId = data.userId;
651
+ if (typeof ownerId !== "string" || !users.findUserById(ownerId)) throw new Error("A valid owner is required");
652
+ var currentAccess = onGetProjectAccess && onGetProjectAccess(projSlug);
653
+ if (!currentAccess || currentAccess.ownerId !== mu.id) throw new Error("Project owner access required");
654
+ if (!onProjectOwnerChanged) throw new Error("Ownership persistence is unavailable");
655
+ var saved = onProjectOwnerChanged(projSlug, ownerId);
656
+ if (saved && saved.error) throw new Error(saved.error);
608
657
  targetCtx.setProjectOwner(ownerId);
609
- if (onProjectOwnerChanged) {
610
- onProjectOwnerChanged(projSlug, ownerId);
611
- }
612
658
  // Broadcast to project clients
613
659
  var ownerName = null;
614
660
  if (ownerId) {
@@ -653,7 +699,8 @@ function attachAdmin(ctx) {
653
699
  req.on("end", function () {
654
700
  try {
655
701
  var data = JSON.parse(body);
656
- if (!Array.isArray(data.allowedUsers)) {
702
+ verifyCurrentOwner(req, mateAccessRoute);
703
+ if (!Array.isArray(data.allowedUsers) || data.allowedUsers.some(function (id) { return typeof id !== "string" || !users.findUserById(id); })) {
657
704
  res.writeHead(400, { "Content-Type": "application/json" });
658
705
  res.end('{"error":"allowedUsers must be an array"}');
659
706
  return;
@@ -34,6 +34,18 @@ function attachAppearanceSettings(ctx) {
34
34
  fs.renameSync(defaultPath + '.tmp', defaultPath);
35
35
  }
36
36
  }
37
+ function getInitialAppearance(req) {
38
+ var user = identity(req);
39
+ if (!user) return null;
40
+ var saved = read(user);
41
+ return {
42
+ configured: saved.configured,
43
+ theme: saved.theme,
44
+ mode: saved.mode,
45
+ lightTheme: saved.lightTheme || null,
46
+ darkTheme: saved.darkTheme || null
47
+ };
48
+ }
37
49
  function handleRequest(req, res, url) {
38
50
  if (url !== '/api/user/appearance') return false;
39
51
  var user = identity(req);
@@ -66,6 +78,6 @@ function attachAppearanceSettings(ctx) {
66
78
  });
67
79
  return true;
68
80
  }
69
- return { handleRequest: handleRequest };
81
+ return { handleRequest: handleRequest, getInitialAppearance: getInitialAppearance };
70
82
  }
71
83
  module.exports = { attachAppearanceSettings: attachAppearanceSettings };