studiograph 1.3.48-next.21 → 1.3.48-next.210

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 (966) hide show
  1. package/README.md +26 -16
  2. package/dist/agent/agent-pool.d.ts +87 -3
  3. package/dist/agent/agent-pool.js +188 -15
  4. package/dist/agent/agent-pool.js.map +1 -1
  5. package/dist/agent/followups.d.ts +34 -0
  6. package/dist/agent/followups.js +52 -0
  7. package/dist/agent/followups.js.map +1 -0
  8. package/dist/agent/orchestrator.d.ts +101 -4
  9. package/dist/agent/orchestrator.js +464 -66
  10. package/dist/agent/orchestrator.js.map +1 -1
  11. package/dist/agent/prompts/entity-types-section.d.ts +34 -0
  12. package/dist/agent/prompts/entity-types-section.js +76 -0
  13. package/dist/agent/prompts/entity-types-section.js.map +1 -0
  14. package/dist/agent/prompts/system.md +112 -54
  15. package/dist/agent/skill-loader.d.ts +30 -17
  16. package/dist/agent/skill-loader.js +63 -30
  17. package/dist/agent/skill-loader.js.map +1 -1
  18. package/dist/agent/skills/artifact-canvas/skill.md +152 -0
  19. package/dist/agent/skills/entity-schema.md +75 -431
  20. package/dist/agent/skills/interview/entity-templates.md +38 -37
  21. package/dist/agent/skills/interview/question-domains.md +54 -49
  22. package/dist/agent/skills/interview/skill.md +84 -16
  23. package/dist/agent/skills/schema-rules.md +28 -28
  24. package/dist/agent/tools/artifact-tools.d.ts +124 -0
  25. package/dist/agent/tools/artifact-tools.js +207 -0
  26. package/dist/agent/tools/artifact-tools.js.map +1 -0
  27. package/dist/agent/tools/capture-tools.d.ts +31 -0
  28. package/dist/agent/tools/capture-tools.js +40 -0
  29. package/dist/agent/tools/capture-tools.js.map +1 -0
  30. package/dist/agent/tools/folder-tools.d.ts +47 -0
  31. package/dist/agent/tools/folder-tools.js +249 -0
  32. package/dist/agent/tools/folder-tools.js.map +1 -0
  33. package/dist/agent/tools/fs-tools.d.ts +6 -5
  34. package/dist/agent/tools/fs-tools.js +5 -5
  35. package/dist/agent/tools/fs-tools.js.map +1 -1
  36. package/dist/agent/tools/graph-tools.d.ts +48 -72
  37. package/dist/agent/tools/graph-tools.js +852 -329
  38. package/dist/agent/tools/graph-tools.js.map +1 -1
  39. package/dist/agent/tools/history-tools.d.ts +95 -0
  40. package/dist/agent/tools/history-tools.js +461 -0
  41. package/dist/agent/tools/history-tools.js.map +1 -0
  42. package/dist/agent/tools/load-skill.d.ts +6 -5
  43. package/dist/agent/tools/load-skill.js +3 -3
  44. package/dist/agent/tools/load-skill.js.map +1 -1
  45. package/dist/agent/tools/ops-tools.d.ts +5 -4
  46. package/dist/agent/tools/ops-tools.js +130 -236
  47. package/dist/agent/tools/ops-tools.js.map +1 -1
  48. package/dist/agent/tools/permission-tools.d.ts +87 -17
  49. package/dist/agent/tools/permission-tools.js +233 -43
  50. package/dist/agent/tools/permission-tools.js.map +1 -1
  51. package/dist/agent/tools/tool-loader.js +5 -4
  52. package/dist/agent/tools/tool-loader.js.map +1 -1
  53. package/dist/agent/tools/web-tools.js +58 -9
  54. package/dist/agent/tools/web-tools.js.map +1 -1
  55. package/dist/cli/commands/about.d.ts +1 -0
  56. package/dist/cli/commands/about.js +55 -3
  57. package/dist/cli/commands/about.js.map +1 -1
  58. package/dist/cli/commands/admin/audit-workspace.d.ts +23 -0
  59. package/dist/cli/commands/admin/audit-workspace.js +133 -0
  60. package/dist/cli/commands/admin/audit-workspace.js.map +1 -0
  61. package/dist/cli/commands/admin/audit.d.ts +21 -0
  62. package/dist/cli/commands/admin/audit.js +174 -0
  63. package/dist/cli/commands/admin/audit.js.map +1 -0
  64. package/dist/cli/commands/admin/backup.d.ts +54 -0
  65. package/dist/cli/commands/admin/backup.js +207 -0
  66. package/dist/cli/commands/admin/backup.js.map +1 -0
  67. package/dist/cli/commands/admin/folder.d.ts +11 -0
  68. package/dist/cli/commands/{collection.js → admin/folder.js} +33 -32
  69. package/dist/cli/commands/admin/folder.js.map +1 -0
  70. package/dist/cli/commands/admin/index.d.ts +16 -0
  71. package/dist/cli/commands/admin/index.js +46 -0
  72. package/dist/cli/commands/admin/index.js.map +1 -0
  73. package/dist/cli/commands/admin/migrate-assets.d.ts +53 -0
  74. package/dist/cli/commands/admin/migrate-assets.js +184 -0
  75. package/dist/cli/commands/admin/migrate-assets.js.map +1 -0
  76. package/dist/cli/commands/admin/migrate.d.ts +56 -0
  77. package/dist/cli/commands/admin/migrate.js +263 -0
  78. package/dist/cli/commands/admin/migrate.js.map +1 -0
  79. package/dist/cli/commands/admin/restore.d.ts +24 -0
  80. package/dist/cli/commands/admin/restore.js +228 -0
  81. package/dist/cli/commands/admin/restore.js.map +1 -0
  82. package/dist/cli/commands/admin/secrets.d.ts +23 -0
  83. package/dist/cli/commands/admin/secrets.js +256 -0
  84. package/dist/cli/commands/admin/secrets.js.map +1 -0
  85. package/dist/cli/commands/admin/settings.d.ts +28 -0
  86. package/dist/cli/commands/admin/settings.js +284 -0
  87. package/dist/cli/commands/admin/settings.js.map +1 -0
  88. package/dist/cli/commands/admin/token.d.ts +42 -0
  89. package/dist/cli/commands/admin/token.js +126 -0
  90. package/dist/cli/commands/admin/token.js.map +1 -0
  91. package/dist/cli/commands/admin/transfer-ownership.d.ts +15 -0
  92. package/dist/cli/commands/admin/transfer-ownership.js +106 -0
  93. package/dist/cli/commands/admin/transfer-ownership.js.map +1 -0
  94. package/dist/cli/commands/admin/user.d.ts +11 -0
  95. package/dist/cli/commands/admin/user.js +187 -0
  96. package/dist/cli/commands/admin/user.js.map +1 -0
  97. package/dist/cli/commands/clone.d.ts +25 -3
  98. package/dist/cli/commands/clone.js +142 -36
  99. package/dist/cli/commands/clone.js.map +1 -1
  100. package/dist/cli/commands/config.d.ts +23 -107
  101. package/dist/cli/commands/config.js +52 -260
  102. package/dist/cli/commands/config.js.map +1 -1
  103. package/dist/cli/commands/connector.d.ts +10 -13
  104. package/dist/cli/commands/connector.js +16 -58
  105. package/dist/cli/commands/connector.js.map +1 -1
  106. package/dist/cli/commands/deploy.js +215 -68
  107. package/dist/cli/commands/deploy.js.map +1 -1
  108. package/dist/cli/commands/index.js +5 -1
  109. package/dist/cli/commands/index.js.map +1 -1
  110. package/dist/cli/commands/init.js +10 -30
  111. package/dist/cli/commands/init.js.map +1 -1
  112. package/dist/cli/commands/mcp.js +54 -6
  113. package/dist/cli/commands/mcp.js.map +1 -1
  114. package/dist/cli/commands/r2.d.ts +3 -0
  115. package/dist/cli/commands/r2.js +223 -204
  116. package/dist/cli/commands/r2.js.map +1 -1
  117. package/dist/cli/commands/redeploy.js +65 -12
  118. package/dist/cli/commands/redeploy.js.map +1 -1
  119. package/dist/cli/commands/reset.d.ts +22 -6
  120. package/dist/cli/commands/reset.js +132 -34
  121. package/dist/cli/commands/reset.js.map +1 -1
  122. package/dist/cli/commands/serve.js +231 -26
  123. package/dist/cli/commands/serve.js.map +1 -1
  124. package/dist/cli/commands/sync.d.ts +1 -1
  125. package/dist/cli/commands/sync.js +237 -227
  126. package/dist/cli/commands/sync.js.map +1 -1
  127. package/dist/cli/crypto-bundle.d.ts +39 -0
  128. package/dist/cli/crypto-bundle.js +94 -0
  129. package/dist/cli/crypto-bundle.js.map +1 -0
  130. package/dist/cli/index.js +24 -26
  131. package/dist/cli/index.js.map +1 -1
  132. package/dist/cli/railway-pin.d.ts +68 -0
  133. package/dist/cli/railway-pin.js +96 -0
  134. package/dist/cli/railway-pin.js.map +1 -0
  135. package/dist/cli/railway-provisioning.d.ts +53 -0
  136. package/dist/cli/railway-provisioning.js +85 -0
  137. package/dist/cli/railway-provisioning.js.map +1 -0
  138. package/dist/cli/setup-wizard.d.ts +30 -49
  139. package/dist/cli/setup-wizard.js +39 -40
  140. package/dist/cli/setup-wizard.js.map +1 -1
  141. package/dist/core/accent-palette.d.ts +22 -0
  142. package/dist/core/accent-palette.js +47 -0
  143. package/dist/core/accent-palette.js.map +1 -0
  144. package/dist/core/artifact-asset-urls.d.ts +51 -0
  145. package/dist/core/artifact-asset-urls.js +79 -0
  146. package/dist/core/artifact-asset-urls.js.map +1 -0
  147. package/dist/core/artifact-bundle.d.ts +69 -0
  148. package/dist/core/artifact-bundle.js +305 -0
  149. package/dist/core/artifact-bundle.js.map +1 -0
  150. package/dist/core/artifact-escape.d.ts +5 -0
  151. package/dist/core/artifact-escape.js +26 -0
  152. package/dist/core/artifact-escape.js.map +1 -0
  153. package/dist/core/artifact-export.d.ts +17 -0
  154. package/dist/core/artifact-export.js +529 -0
  155. package/dist/core/artifact-export.js.map +1 -0
  156. package/dist/core/cell-anchor.d.ts +29 -0
  157. package/dist/core/cell-anchor.js +53 -0
  158. package/dist/core/cell-anchor.js.map +1 -0
  159. package/dist/core/comment-anchor.d.ts +41 -0
  160. package/dist/core/comment-anchor.js +147 -0
  161. package/dist/core/comment-anchor.js.map +1 -0
  162. package/dist/core/commit-messages.d.ts +57 -0
  163. package/dist/core/commit-messages.js +85 -0
  164. package/dist/core/commit-messages.js.map +1 -0
  165. package/dist/core/embeds.d.ts +96 -0
  166. package/dist/core/embeds.js +187 -0
  167. package/dist/core/embeds.js.map +1 -0
  168. package/dist/core/entity-body-templates.d.ts +21 -0
  169. package/dist/core/entity-body-templates.js +67 -0
  170. package/dist/core/entity-body-templates.js.map +1 -0
  171. package/dist/core/entity-types-catalog.d.ts +65 -0
  172. package/dist/core/entity-types-catalog.js +142 -0
  173. package/dist/core/entity-types-catalog.js.map +1 -0
  174. package/dist/core/field-library.d.ts +62 -0
  175. package/dist/core/field-library.js +129 -0
  176. package/dist/core/field-library.js.map +1 -0
  177. package/dist/core/folder-paths.d.ts +41 -0
  178. package/dist/core/folder-paths.js +95 -0
  179. package/dist/core/folder-paths.js.map +1 -0
  180. package/dist/core/folder-sidecar.d.ts +36 -0
  181. package/dist/core/folder-sidecar.js +91 -0
  182. package/dist/core/folder-sidecar.js.map +1 -0
  183. package/dist/core/format-registry.d.ts +170 -0
  184. package/dist/core/format-registry.js +412 -0
  185. package/dist/core/format-registry.js.map +1 -0
  186. package/dist/core/graph.d.ts +773 -14
  187. package/dist/core/graph.js +2269 -308
  188. package/dist/core/graph.js.map +1 -1
  189. package/dist/core/history-diff.d.ts +35 -0
  190. package/dist/core/history-diff.js +114 -0
  191. package/dist/core/history-diff.js.map +1 -0
  192. package/dist/core/refs.d.ts +41 -0
  193. package/dist/core/refs.js +80 -0
  194. package/dist/core/refs.js.map +1 -0
  195. package/dist/core/schema-registry.d.ts +66 -0
  196. package/dist/core/schema-registry.js +198 -37
  197. package/dist/core/schema-registry.js.map +1 -1
  198. package/dist/core/schemas/connector.d.ts +67 -0
  199. package/dist/core/schemas/connector.js +100 -0
  200. package/dist/core/schemas/connector.js.map +1 -0
  201. package/dist/core/schemas/workspace.d.ts +122 -0
  202. package/dist/core/schemas/workspace.js +211 -0
  203. package/dist/core/schemas/workspace.js.map +1 -0
  204. package/dist/core/secrets/SecretStore.d.ts +77 -0
  205. package/dist/core/secrets/SecretStore.js +13 -0
  206. package/dist/core/secrets/SecretStore.js.map +1 -0
  207. package/dist/core/secrets/SettingsResolver.d.ts +54 -0
  208. package/dist/core/secrets/SettingsResolver.js +80 -0
  209. package/dist/core/secrets/SettingsResolver.js.map +1 -0
  210. package/dist/core/secrets/SettingsStore.d.ts +30 -0
  211. package/dist/core/secrets/SettingsStore.js +9 -0
  212. package/dist/core/secrets/SettingsStore.js.map +1 -0
  213. package/dist/core/secrets/SqliteEncryptedStore.d.ts +90 -0
  214. package/dist/core/secrets/SqliteEncryptedStore.js +598 -0
  215. package/dist/core/secrets/SqliteEncryptedStore.js.map +1 -0
  216. package/dist/core/secrets/audit.d.ts +36 -0
  217. package/dist/core/secrets/audit.js +60 -0
  218. package/dist/core/secrets/audit.js.map +1 -0
  219. package/dist/core/secrets/auth-db-migration.d.ts +129 -0
  220. package/dist/core/secrets/auth-db-migration.js +653 -0
  221. package/dist/core/secrets/auth-db-migration.js.map +1 -0
  222. package/dist/core/secrets/bundle.d.ts +141 -0
  223. package/dist/core/secrets/bundle.js +435 -0
  224. package/dist/core/secrets/bundle.js.map +1 -0
  225. package/dist/core/secrets/dual-write.d.ts +81 -0
  226. package/dist/core/secrets/dual-write.js +247 -0
  227. package/dist/core/secrets/dual-write.js.map +1 -0
  228. package/dist/core/secrets/encryption.d.ts +44 -0
  229. package/dist/core/secrets/encryption.js +97 -0
  230. package/dist/core/secrets/encryption.js.map +1 -0
  231. package/dist/core/secrets/index.d.ts +49 -0
  232. package/dist/core/secrets/index.js +74 -0
  233. package/dist/core/secrets/index.js.map +1 -0
  234. package/dist/core/secrets/master-key.d.ts +38 -0
  235. package/dist/core/secrets/master-key.js +122 -0
  236. package/dist/core/secrets/master-key.js.map +1 -0
  237. package/dist/core/secrets/probes/anthropic.d.ts +98 -0
  238. package/dist/core/secrets/probes/anthropic.js +300 -0
  239. package/dist/core/secrets/probes/anthropic.js.map +1 -0
  240. package/dist/core/secrets/probes/brave.d.ts +9 -0
  241. package/dist/core/secrets/probes/brave.js +15 -0
  242. package/dist/core/secrets/probes/brave.js.map +1 -0
  243. package/dist/core/secrets/probes/r2.d.ts +15 -0
  244. package/dist/core/secrets/probes/r2.js +14 -0
  245. package/dist/core/secrets/probes/r2.js.map +1 -0
  246. package/dist/core/secrets/probes/tavily.d.ts +18 -0
  247. package/dist/core/secrets/probes/tavily.js +58 -0
  248. package/dist/core/secrets/probes/tavily.js.map +1 -0
  249. package/dist/core/secrets/probes/voyage.d.ts +13 -0
  250. package/dist/core/secrets/probes/voyage.js +52 -0
  251. package/dist/core/secrets/probes/voyage.js.map +1 -0
  252. package/dist/core/secrets/r2-structural-migration.d.ts +39 -0
  253. package/dist/core/secrets/r2-structural-migration.js +109 -0
  254. package/dist/core/secrets/r2-structural-migration.js.map +1 -0
  255. package/dist/core/secrets/read-flip.d.ts +59 -0
  256. package/dist/core/secrets/read-flip.js +87 -0
  257. package/dist/core/secrets/read-flip.js.map +1 -0
  258. package/dist/core/secrets/registry.d.ts +188 -0
  259. package/dist/core/secrets/registry.js +616 -0
  260. package/dist/core/secrets/registry.js.map +1 -0
  261. package/dist/core/types.d.ts +77 -483
  262. package/dist/core/types.js +25 -3
  263. package/dist/core/types.js.map +1 -1
  264. package/dist/core/user-config.d.ts +75 -12
  265. package/dist/core/user-config.js +155 -14
  266. package/dist/core/user-config.js.map +1 -1
  267. package/dist/core/validation.d.ts +786 -1373
  268. package/dist/core/validation.js +458 -147
  269. package/dist/core/validation.js.map +1 -1
  270. package/dist/core/workspace-manager.d.ts +87 -14
  271. package/dist/core/workspace-manager.js +222 -94
  272. package/dist/core/workspace-manager.js.map +1 -1
  273. package/dist/core/workspace.d.ts +19 -5
  274. package/dist/core/workspace.js +67 -39
  275. package/dist/core/workspace.js.map +1 -1
  276. package/dist/mcp/comment-virtual-entity.d.ts +36 -0
  277. package/dist/mcp/comment-virtual-entity.js +71 -0
  278. package/dist/mcp/comment-virtual-entity.js.map +1 -0
  279. package/dist/mcp/connector-manager.d.ts +72 -2
  280. package/dist/mcp/connector-manager.js +211 -32
  281. package/dist/mcp/connector-manager.js.map +1 -1
  282. package/dist/mcp/oauth-provider.d.ts +0 -5
  283. package/dist/mcp/oauth-provider.js +10 -22
  284. package/dist/mcp/oauth-provider.js.map +1 -1
  285. package/dist/mcp/oauth-registry.d.ts +46 -8
  286. package/dist/mcp/oauth-registry.js +52 -13
  287. package/dist/mcp/oauth-registry.js.map +1 -1
  288. package/dist/mcp/server-discovery.d.ts +73 -0
  289. package/dist/mcp/server-discovery.js +164 -0
  290. package/dist/mcp/server-discovery.js.map +1 -0
  291. package/dist/mcp/server.d.ts +24 -3
  292. package/dist/mcp/server.js +53 -10
  293. package/dist/mcp/server.js.map +1 -1
  294. package/dist/mcp/state-signer.d.ts +62 -0
  295. package/dist/mcp/state-signer.js +205 -0
  296. package/dist/mcp/state-signer.js.map +1 -0
  297. package/dist/mcp/stdio-proxy.d.ts +67 -0
  298. package/dist/mcp/stdio-proxy.js +149 -0
  299. package/dist/mcp/stdio-proxy.js.map +1 -0
  300. package/dist/mcp/tools.d.ts +65 -2
  301. package/dist/mcp/tools.js +1772 -124
  302. package/dist/mcp/tools.js.map +1 -1
  303. package/dist/server/__tests__/helpers/graph-api.d.ts +16 -0
  304. package/dist/server/__tests__/helpers/graph-api.js +16 -0
  305. package/dist/server/__tests__/helpers/graph-api.js.map +1 -0
  306. package/dist/server/artifact-asset-mint.d.ts +41 -0
  307. package/dist/server/artifact-asset-mint.js +59 -0
  308. package/dist/server/artifact-asset-mint.js.map +1 -0
  309. package/dist/server/asset-index-queue.d.ts +98 -0
  310. package/dist/server/asset-index-queue.js +193 -0
  311. package/dist/server/asset-index-queue.js.map +1 -0
  312. package/dist/server/body-write-coordinator.d.ts +161 -0
  313. package/dist/server/body-write-coordinator.js +182 -0
  314. package/dist/server/body-write-coordinator.js.map +1 -0
  315. package/dist/server/cloud-billing-flow.d.ts +32 -0
  316. package/dist/server/cloud-billing-flow.js +75 -0
  317. package/dist/server/cloud-billing-flow.js.map +1 -0
  318. package/dist/server/commit-audit.d.ts +26 -0
  319. package/dist/server/commit-audit.js +30 -0
  320. package/dist/server/commit-audit.js.map +1 -0
  321. package/dist/server/index.d.ts +29 -3
  322. package/dist/server/index.js +657 -269
  323. package/dist/server/index.js.map +1 -1
  324. package/dist/server/middleware/sanitize.d.ts +46 -0
  325. package/dist/server/middleware/sanitize.js +182 -0
  326. package/dist/server/middleware/sanitize.js.map +1 -0
  327. package/dist/server/routes/artifact-export-api.d.ts +11 -0
  328. package/dist/server/routes/artifact-export-api.js +159 -0
  329. package/dist/server/routes/artifact-export-api.js.map +1 -0
  330. package/dist/server/routes/asset-api.d.ts +76 -1
  331. package/dist/server/routes/asset-api.js +761 -61
  332. package/dist/server/routes/asset-api.js.map +1 -1
  333. package/dist/server/routes/audit-api.d.ts +24 -0
  334. package/dist/server/routes/audit-api.js +117 -0
  335. package/dist/server/routes/audit-api.js.map +1 -0
  336. package/dist/server/routes/auth-api.js +532 -63
  337. package/dist/server/routes/auth-api.js.map +1 -1
  338. package/dist/server/routes/capture-api.d.ts +10 -3
  339. package/dist/server/routes/capture-api.js +213 -25
  340. package/dist/server/routes/capture-api.js.map +1 -1
  341. package/dist/server/routes/chat-sessions-api.d.ts +13 -0
  342. package/dist/server/routes/chat-sessions-api.js +455 -0
  343. package/dist/server/routes/chat-sessions-api.js.map +1 -0
  344. package/dist/server/routes/chat.d.ts +35 -1
  345. package/dist/server/routes/chat.js +699 -54
  346. package/dist/server/routes/chat.js.map +1 -1
  347. package/dist/server/routes/comments-api.d.ts +15 -0
  348. package/dist/server/routes/comments-api.js +444 -0
  349. package/dist/server/routes/comments-api.js.map +1 -0
  350. package/dist/server/routes/config-api.d.ts +3 -2
  351. package/dist/server/routes/config-api.js +179 -49
  352. package/dist/server/routes/config-api.js.map +1 -1
  353. package/dist/server/routes/connectors-api.js +177 -42
  354. package/dist/server/routes/connectors-api.js.map +1 -1
  355. package/dist/server/routes/event-ingest-api.d.ts +15 -0
  356. package/dist/server/routes/{meeting-ingest-api.js → event-ingest-api.js} +35 -20
  357. package/dist/server/routes/event-ingest-api.js.map +1 -0
  358. package/dist/server/routes/field-library-api.d.ts +28 -0
  359. package/dist/server/routes/field-library-api.js +126 -0
  360. package/dist/server/routes/field-library-api.js.map +1 -0
  361. package/dist/server/routes/git-http.d.ts +2 -2
  362. package/dist/server/routes/git-http.js +86 -39
  363. package/dist/server/routes/git-http.js.map +1 -1
  364. package/dist/server/routes/graph-api-access.d.ts +57 -0
  365. package/dist/server/routes/graph-api-access.js +112 -0
  366. package/dist/server/routes/graph-api-access.js.map +1 -0
  367. package/dist/server/routes/graph-api.d.ts +1 -1
  368. package/dist/server/routes/graph-api.js +1455 -325
  369. package/dist/server/routes/graph-api.js.map +1 -1
  370. package/dist/server/routes/health.d.ts +18 -0
  371. package/dist/server/routes/health.js +74 -0
  372. package/dist/server/routes/health.js.map +1 -0
  373. package/dist/server/routes/import-batch.d.ts +24 -0
  374. package/dist/server/routes/import-batch.js +33 -0
  375. package/dist/server/routes/import-batch.js.map +1 -0
  376. package/dist/server/routes/insights-api.d.ts +9 -2
  377. package/dist/server/routes/insights-api.js +355 -61
  378. package/dist/server/routes/insights-api.js.map +1 -1
  379. package/dist/server/routes/mcp.d.ts +7 -3
  380. package/dist/server/routes/mcp.js +24 -8
  381. package/dist/server/routes/mcp.js.map +1 -1
  382. package/dist/server/routes/oauth-api.d.ts +67 -0
  383. package/dist/server/routes/oauth-api.js +421 -0
  384. package/dist/server/routes/oauth-api.js.map +1 -0
  385. package/dist/server/routes/permissions-api.d.ts +9 -2
  386. package/dist/server/routes/permissions-api.js +87 -31
  387. package/dist/server/routes/permissions-api.js.map +1 -1
  388. package/dist/server/routes/r2-api.d.ts +25 -0
  389. package/dist/server/routes/r2-api.js +276 -0
  390. package/dist/server/routes/r2-api.js.map +1 -0
  391. package/dist/server/routes/secrets-api.d.ts +36 -0
  392. package/dist/server/routes/secrets-api.js +308 -0
  393. package/dist/server/routes/secrets-api.js.map +1 -0
  394. package/dist/server/routes/settings-api.d.ts +29 -0
  395. package/dist/server/routes/settings-api.js +180 -0
  396. package/dist/server/routes/settings-api.js.map +1 -0
  397. package/dist/server/routes/share-api.d.ts +32 -0
  398. package/dist/server/routes/share-api.js +234 -0
  399. package/dist/server/routes/share-api.js.map +1 -0
  400. package/dist/server/routes/views-api.js +51 -0
  401. package/dist/server/routes/views-api.js.map +1 -1
  402. package/dist/server/routes/workspace-api.d.ts +2 -1
  403. package/dist/server/routes/workspace-api.js +142 -23
  404. package/dist/server/routes/workspace-api.js.map +1 -1
  405. package/dist/server/routes/ws.d.ts +3 -2
  406. package/dist/server/routes/ws.js +68 -17
  407. package/dist/server/routes/ws.js.map +1 -1
  408. package/dist/server/session-manager.d.ts +48 -5
  409. package/dist/server/session-manager.js +182 -14
  410. package/dist/server/session-manager.js.map +1 -1
  411. package/dist/server/ws-hub.d.ts +124 -37
  412. package/dist/server/ws-hub.js +0 -0
  413. package/dist/server/ws-hub.js.map +1 -1
  414. package/dist/server/yjs-manager.d.ts +278 -7
  415. package/dist/server/yjs-manager.js +830 -38
  416. package/dist/server/yjs-manager.js.map +1 -1
  417. package/dist/server/yjs-persistence.d.ts +165 -0
  418. package/dist/server/yjs-persistence.js +557 -0
  419. package/dist/server/yjs-persistence.js.map +1 -0
  420. package/dist/server/yjs-text-diff.d.ts +32 -0
  421. package/dist/server/yjs-text-diff.js +61 -0
  422. package/dist/server/yjs-text-diff.js.map +1 -0
  423. package/dist/services/access-control.d.ts +73 -0
  424. package/dist/services/access-control.js +165 -0
  425. package/dist/services/access-control.js.map +1 -0
  426. package/dist/services/artifact-asset-token.d.ts +69 -0
  427. package/dist/services/artifact-asset-token.js +94 -0
  428. package/dist/services/artifact-asset-token.js.map +1 -0
  429. package/dist/services/artifact-library-sources.d.ts +3 -0
  430. package/dist/services/artifact-library-sources.js +44 -0
  431. package/dist/services/artifact-library-sources.js.map +1 -0
  432. package/dist/services/artifact-render-export.d.ts +97 -0
  433. package/dist/services/artifact-render-export.js +467 -0
  434. package/dist/services/artifact-render-export.js.map +1 -0
  435. package/dist/services/asset-caption-service.d.ts +103 -0
  436. package/dist/services/asset-caption-service.js +186 -0
  437. package/dist/services/asset-caption-service.js.map +1 -0
  438. package/dist/services/asset-delete-authz.d.ts +31 -0
  439. package/dist/services/asset-delete-authz.js +61 -0
  440. package/dist/services/asset-delete-authz.js.map +1 -0
  441. package/dist/services/asset-finalize-service.d.ts +58 -0
  442. package/dist/services/asset-finalize-service.js +216 -0
  443. package/dist/services/asset-finalize-service.js.map +1 -0
  444. package/dist/services/asset-import-service.d.ts +111 -0
  445. package/dist/services/asset-import-service.js +394 -0
  446. package/dist/services/asset-import-service.js.map +1 -0
  447. package/dist/services/asset-index-sweep.d.ts +95 -0
  448. package/dist/services/asset-index-sweep.js +249 -0
  449. package/dist/services/asset-index-sweep.js.map +1 -0
  450. package/dist/services/asset-lifecycle-check.d.ts +22 -0
  451. package/dist/services/asset-lifecycle-check.js +80 -0
  452. package/dist/services/asset-lifecycle-check.js.map +1 -0
  453. package/dist/services/asset-listing.d.ts +72 -0
  454. package/dist/services/asset-listing.js +321 -0
  455. package/dist/services/asset-listing.js.map +1 -0
  456. package/dist/services/asset-presign-service.d.ts +68 -0
  457. package/dist/services/asset-presign-service.js +124 -0
  458. package/dist/services/asset-presign-service.js.map +1 -0
  459. package/dist/services/asset-sidecar-service.d.ts +28 -0
  460. package/dist/services/asset-sidecar-service.js +79 -0
  461. package/dist/services/asset-sidecar-service.js.map +1 -0
  462. package/dist/services/asset-upload-errors.d.ts +41 -0
  463. package/dist/services/asset-upload-errors.js +45 -0
  464. package/dist/services/asset-upload-errors.js.map +1 -0
  465. package/dist/services/asset-upload-service.d.ts +56 -0
  466. package/dist/services/asset-upload-service.js +200 -0
  467. package/dist/services/asset-upload-service.js.map +1 -0
  468. package/dist/services/asset-upload-token.d.ts +58 -0
  469. package/dist/services/asset-upload-token.js +75 -0
  470. package/dist/services/asset-upload-token.js.map +1 -0
  471. package/dist/services/assets/asset-index-service.d.ts +127 -0
  472. package/dist/services/assets/asset-index-service.js +227 -0
  473. package/dist/services/assets/asset-index-service.js.map +1 -0
  474. package/dist/services/assets/base.d.ts +128 -2
  475. package/dist/services/assets/base.js +98 -1
  476. package/dist/services/assets/base.js.map +1 -1
  477. package/dist/services/assets/index.d.ts +114 -1
  478. package/dist/services/assets/index.js +221 -0
  479. package/dist/services/assets/index.js.map +1 -1
  480. package/dist/services/assets/local.d.ts +16 -1
  481. package/dist/services/assets/local.js +110 -1
  482. package/dist/services/assets/local.js.map +1 -1
  483. package/dist/services/assets/r2-sync.d.ts +88 -0
  484. package/dist/services/assets/r2-sync.js +412 -0
  485. package/dist/services/assets/r2-sync.js.map +1 -0
  486. package/dist/services/assets/r2.d.ts +87 -1
  487. package/dist/services/assets/r2.js +203 -1
  488. package/dist/services/assets/r2.js.map +1 -1
  489. package/dist/services/auth-service.d.ts +312 -45
  490. package/dist/services/auth-service.js +1011 -195
  491. package/dist/services/auth-service.js.map +1 -1
  492. package/dist/services/chat-session-service.d.ts +188 -0
  493. package/dist/services/chat-session-service.js +512 -0
  494. package/dist/services/chat-session-service.js.map +1 -0
  495. package/dist/services/cloud-inference-billing.d.ts +64 -0
  496. package/dist/services/cloud-inference-billing.js +313 -0
  497. package/dist/services/cloud-inference-billing.js.map +1 -0
  498. package/dist/services/comment-service.d.ts +97 -0
  499. package/dist/services/comment-service.js +341 -0
  500. package/dist/services/comment-service.js.map +1 -0
  501. package/dist/services/comment-virtual-entity.d.ts +36 -0
  502. package/dist/services/comment-virtual-entity.js +71 -0
  503. package/dist/services/comment-virtual-entity.js.map +1 -0
  504. package/dist/services/convert-service.d.ts +64 -0
  505. package/dist/services/convert-service.js +68 -0
  506. package/dist/services/convert-service.js.map +1 -0
  507. package/dist/services/csv-service.d.ts +22 -17
  508. package/dist/services/csv-service.js +55 -92
  509. package/dist/services/csv-service.js.map +1 -1
  510. package/dist/services/entity-mutations.d.ts +213 -0
  511. package/dist/services/entity-mutations.js +380 -0
  512. package/dist/services/entity-mutations.js.map +1 -0
  513. package/dist/services/{meeting-processor.d.ts → event-processor.d.ts} +15 -8
  514. package/dist/services/{meeting-processor.js → event-processor.js} +124 -65
  515. package/dist/services/event-processor.js.map +1 -0
  516. package/dist/services/extract/docx.d.ts +1 -0
  517. package/dist/services/extract/docx.js +233 -0
  518. package/dist/services/extract/docx.js.map +1 -0
  519. package/dist/services/extract/index.d.ts +9 -0
  520. package/dist/services/extract/index.js +10 -0
  521. package/dist/services/extract/index.js.map +1 -0
  522. package/dist/services/extract/pdf.d.ts +13 -0
  523. package/dist/services/extract/pdf.js +69 -0
  524. package/dist/services/extract/pdf.js.map +1 -0
  525. package/dist/services/folder-creation.d.ts +33 -0
  526. package/dist/services/folder-creation.js +103 -0
  527. package/dist/services/folder-creation.js.map +1 -0
  528. package/dist/services/git.d.ts +58 -0
  529. package/dist/services/git.js +194 -55
  530. package/dist/services/git.js.map +1 -1
  531. package/dist/services/graph-maintenance.js +4 -4
  532. package/dist/services/graph-maintenance.js.map +1 -1
  533. package/dist/services/import-service.d.ts +51 -2
  534. package/dist/services/import-service.js +254 -107
  535. package/dist/services/import-service.js.map +1 -1
  536. package/dist/services/lint-service.js +10 -17
  537. package/dist/services/lint-service.js.map +1 -1
  538. package/dist/services/markdown.d.ts +12 -1
  539. package/dist/services/markdown.js +77 -9
  540. package/dist/services/markdown.js.map +1 -1
  541. package/dist/services/mention-detector.d.ts +23 -0
  542. package/dist/services/mention-detector.js +47 -0
  543. package/dist/services/mention-detector.js.map +1 -0
  544. package/dist/services/model-capabilities.d.ts +41 -0
  545. package/dist/services/model-capabilities.js +107 -0
  546. package/dist/services/model-capabilities.js.map +1 -0
  547. package/dist/services/notification-broadcast.d.ts +32 -0
  548. package/dist/services/notification-broadcast.js +38 -0
  549. package/dist/services/notification-broadcast.js.map +1 -0
  550. package/dist/services/notification-service.d.ts +41 -0
  551. package/dist/services/notification-service.js +100 -0
  552. package/dist/services/notification-service.js.map +1 -0
  553. package/dist/services/oauth-server-store.d.ts +147 -0
  554. package/dist/services/oauth-server-store.js +319 -0
  555. package/dist/services/oauth-server-store.js.map +1 -0
  556. package/dist/services/orphan-service.js +2 -2
  557. package/dist/services/orphan-service.js.map +1 -1
  558. package/dist/services/personal-folder.d.ts +40 -0
  559. package/dist/services/personal-folder.js +101 -0
  560. package/dist/services/personal-folder.js.map +1 -0
  561. package/dist/services/scratch-asset-promote.d.ts +57 -0
  562. package/dist/services/scratch-asset-promote.js +95 -0
  563. package/dist/services/scratch-asset-promote.js.map +1 -0
  564. package/dist/services/scratch-attachment-service.d.ts +196 -0
  565. package/dist/services/scratch-attachment-service.js +347 -0
  566. package/dist/services/scratch-attachment-service.js.map +1 -0
  567. package/dist/services/share-link-service.d.ts +57 -0
  568. package/dist/services/share-link-service.js +142 -0
  569. package/dist/services/share-link-service.js.map +1 -0
  570. package/dist/services/user-person-bridge.d.ts +136 -0
  571. package/dist/services/user-person-bridge.js +340 -0
  572. package/dist/services/user-person-bridge.js.map +1 -0
  573. package/dist/services/vector-service.d.ts +282 -5
  574. package/dist/services/vector-service.js +585 -29
  575. package/dist/services/vector-service.js.map +1 -1
  576. package/dist/services/workspace-access.d.ts +108 -0
  577. package/dist/services/workspace-access.js +149 -0
  578. package/dist/services/workspace-access.js.map +1 -0
  579. package/dist/services/workspace-assets-folder.d.ts +46 -0
  580. package/dist/services/workspace-assets-folder.js +75 -0
  581. package/dist/services/workspace-assets-folder.js.map +1 -0
  582. package/dist/utils/git.d.ts +17 -2
  583. package/dist/utils/git.js +23 -7
  584. package/dist/utils/git.js.map +1 -1
  585. package/dist/utils/log.d.ts +42 -0
  586. package/dist/utils/log.js +137 -0
  587. package/dist/utils/log.js.map +1 -0
  588. package/dist/utils/preflight.js +2 -2
  589. package/dist/utils/preflight.js.map +1 -1
  590. package/dist/utils/version-checker.d.ts +12 -19
  591. package/dist/utils/version-checker.js +14 -104
  592. package/dist/utils/version-checker.js.map +1 -1
  593. package/dist/web/_app/immutable/assets/0.Bs_z3Z86.css +2 -0
  594. package/dist/web/_app/immutable/assets/12.DlIf1mKh.css +1 -0
  595. package/dist/web/_app/immutable/assets/14.7d-Q37wc.css +1 -0
  596. package/dist/web/_app/immutable/assets/15.BwBFdc1D.css +1 -0
  597. package/dist/web/_app/immutable/assets/16.CmLrbzZp.css +1 -0
  598. package/dist/web/_app/immutable/assets/17.DcuK5ZVm.css +1 -0
  599. package/dist/web/_app/immutable/assets/2.DATnR31c.css +1 -0
  600. package/dist/web/_app/immutable/assets/3.Cz4H8n_O.css +1 -0
  601. package/dist/web/_app/immutable/assets/4.aDHteTDy.css +1 -0
  602. package/dist/web/_app/immutable/assets/6.CF1i5Bon.css +1 -0
  603. package/dist/web/_app/immutable/assets/ArtifactEntityView.C4ooklFD.css +1 -0
  604. package/dist/web/_app/immutable/assets/AssetLibraryPicker.BOFW0MvF.css +1 -0
  605. package/dist/web/_app/immutable/assets/BulkActionsBar.cmKvHzRK.css +1 -0
  606. package/dist/web/_app/immutable/assets/CalendarView.lBCvS3X5.css +1 -0
  607. package/dist/web/_app/immutable/assets/CodeMirrorEditor.D6hf2pNJ.css +1 -0
  608. package/dist/web/_app/immutable/assets/CopyField.DVGZtw4U.css +1 -0
  609. package/dist/web/_app/immutable/assets/FolderMembersPanel.kiYnDFHu.css +1 -0
  610. package/dist/web/_app/immutable/assets/FolderMutationDialogs.BMRF6Z3z.css +1 -0
  611. package/dist/web/_app/immutable/assets/FolderPicker.Cp46GHe2.css +1 -0
  612. package/dist/web/_app/immutable/assets/GalleryView.WBGxxUQc.css +1 -0
  613. package/dist/web/_app/immutable/assets/GraphView.BJtGL9py.css +1 -0
  614. package/dist/web/_app/immutable/assets/KanbanView.BOaI5KFL.css +1 -0
  615. package/dist/web/_app/immutable/assets/Link.vV0ne105.css +1 -0
  616. package/dist/web/_app/immutable/assets/Markdown.BZnsSOSf.css +1 -0
  617. package/dist/web/_app/immutable/assets/Rail.ilusFgD4.css +1 -0
  618. package/dist/web/_app/immutable/assets/SettingsDialog.D7-BO6S_.css +1 -0
  619. package/dist/web/_app/immutable/assets/Spinner.UBfl0pab.css +1 -0
  620. package/dist/web/_app/immutable/assets/StopFilledAlt.fNlDN65D.css +1 -0
  621. package/dist/web/_app/immutable/assets/TimelineView.xAZBJhHw.css +1 -0
  622. package/dist/web/_app/immutable/assets/WorkspaceView.C8ErUJAY.css +1 -0
  623. package/dist/web/_app/immutable/assets/button.BlLm4Dg1.css +1 -0
  624. package/dist/web/_app/immutable/assets/history-summary.LpmHXasl.css +1 -0
  625. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-400-normal.CvHOgSBP.woff +0 -0
  626. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-400-normal.DMJ8VG8y.woff2 +0 -0
  627. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-500-normal.CB9ihrfo.woff +0 -0
  628. package/dist/web/_app/immutable/assets/ibm-plex-mono-latin-500-normal.DSY6xOcd.woff2 +0 -0
  629. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-italic.CZTNEAuW.woff2 +0 -0
  630. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-italic.CsGl1sm0.woff +0 -0
  631. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-normal.CDDApCn2.woff2 +0 -0
  632. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-400-normal.CYLoc0-x.woff +0 -0
  633. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-italic.BNK2_mGO.woff2 +0 -0
  634. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-italic.DpEwFAQM.woff +0 -0
  635. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-normal.6ng42L7E.woff2 +0 -0
  636. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-500-normal.BgVn5rGT.woff +0 -0
  637. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-600-normal.Cu4Hd6ag.woff +0 -0
  638. package/dist/web/_app/immutable/assets/ibm-plex-sans-latin-600-normal.CuJfVYMP.woff2 +0 -0
  639. package/dist/web/_app/immutable/assets/inline-list.cqga56xT.css +1 -0
  640. package/dist/web/_app/immutable/assets/list-columns.BgnafZ4K.css +1 -0
  641. package/dist/web/_app/immutable/assets/realtime.DDCWxn3B.css +1 -0
  642. package/dist/web/_app/immutable/assets/select.B90KUqR4.css +1 -0
  643. package/dist/web/_app/immutable/assets/sheet.RF9RBmml.css +1 -0
  644. package/dist/web/_app/immutable/chunks/22qYtvr82.js +1 -0
  645. package/dist/web/_app/immutable/chunks/3anKjXOb.js +1 -0
  646. package/dist/web/_app/immutable/chunks/6S9WOky52.js +1 -0
  647. package/dist/web/_app/immutable/chunks/7xJ91z7d.js +2 -0
  648. package/dist/web/_app/immutable/chunks/9K8VREGP.js +1 -0
  649. package/dist/web/_app/immutable/chunks/B-TGZnCt2.js +1 -0
  650. package/dist/web/_app/immutable/chunks/B-n5q6ku.js +1 -0
  651. package/dist/web/_app/immutable/chunks/B0p2fSEV2.js +1 -0
  652. package/dist/web/_app/immutable/chunks/B2oi87jG.js +2 -0
  653. package/dist/web/_app/immutable/chunks/B4DG5QOL.js +1 -0
  654. package/dist/web/_app/immutable/chunks/B6Lt-qaJ.js +1 -0
  655. package/dist/web/_app/immutable/chunks/B6qeIQZz.js +2 -0
  656. package/dist/web/_app/immutable/chunks/B9KfEsQw2.js +1 -0
  657. package/dist/web/_app/immutable/chunks/B9VRF5aH2.js +1 -0
  658. package/dist/web/_app/immutable/chunks/BCQQzpbj.js +1 -0
  659. package/dist/web/_app/immutable/chunks/BF0vIqH9.js +1 -0
  660. package/dist/web/_app/immutable/chunks/BFp1K70l.js +8 -0
  661. package/dist/web/_app/immutable/chunks/BFr7Funh.js +1 -0
  662. package/dist/web/_app/immutable/chunks/BFx32D20.js +2 -0
  663. package/dist/web/_app/immutable/chunks/BLs1h0Rh2.js +1 -0
  664. package/dist/web/_app/immutable/chunks/BPzV_xOS2.js +1 -0
  665. package/dist/web/_app/immutable/chunks/BRvMjAzW.js +2 -0
  666. package/dist/web/_app/immutable/chunks/BV-iCKqa.js +1 -0
  667. package/dist/web/_app/immutable/chunks/BZi3uKF4.js +1 -0
  668. package/dist/web/_app/immutable/chunks/B_PyL87x.js +2 -0
  669. package/dist/web/_app/immutable/chunks/BbfHGU9n2.js +1 -0
  670. package/dist/web/_app/immutable/chunks/Bbqu2-5G2.js +1 -0
  671. package/dist/web/_app/immutable/chunks/BdWZJpmv.js +2 -0
  672. package/dist/web/_app/immutable/chunks/BfnFtsiY.js +1 -0
  673. package/dist/web/_app/immutable/chunks/BlgjVmFM2.js +14 -0
  674. package/dist/web/_app/immutable/chunks/BmgXvmIG.js +5 -0
  675. package/dist/web/_app/immutable/chunks/BpCG9m962.js +1 -0
  676. package/dist/web/_app/immutable/chunks/BrveeylZ.js +1 -0
  677. package/dist/web/_app/immutable/chunks/BsW72fyR2.js +1 -0
  678. package/dist/web/_app/immutable/chunks/Bti_XKwx.js +2692 -0
  679. package/dist/web/_app/immutable/chunks/BuhW5Fr0.js +689 -0
  680. package/dist/web/_app/immutable/chunks/Bx74EEyn2.js +1 -0
  681. package/dist/web/_app/immutable/chunks/BxPN0T61.js +1 -0
  682. package/dist/web/_app/immutable/chunks/Bxk27YWG.js +1 -0
  683. package/dist/web/_app/immutable/chunks/Bz9lrgGs.js +1 -0
  684. package/dist/web/_app/immutable/chunks/BzaxZ6j12.js +1 -0
  685. package/dist/web/_app/immutable/chunks/C1B4xDs0.js +1 -0
  686. package/dist/web/_app/immutable/chunks/C1trcC8M.js +7 -0
  687. package/dist/web/_app/immutable/chunks/C3F_NMTk.js +3 -0
  688. package/dist/web/_app/immutable/chunks/C4PZmGUQ.js +2 -0
  689. package/dist/web/_app/immutable/chunks/C6wYiSz8.js +1 -0
  690. package/dist/web/_app/immutable/chunks/C70saxIl.js +1 -0
  691. package/dist/web/_app/immutable/chunks/C9tvkk7Y2.js +22 -0
  692. package/dist/web/_app/immutable/chunks/CE1XZ5DE.js +1 -0
  693. package/dist/web/_app/immutable/chunks/CEahbtnP.js +1 -0
  694. package/dist/web/_app/immutable/chunks/CGkd0a-c2.js +2 -0
  695. package/dist/web/_app/immutable/chunks/CIoNEHQ82.js +184 -0
  696. package/dist/web/_app/immutable/chunks/CK4z74w5.js +1 -0
  697. package/dist/web/_app/immutable/chunks/COo3HgCZ.js +1 -0
  698. package/dist/web/_app/immutable/chunks/CP8_U450.js +1 -0
  699. package/dist/web/_app/immutable/chunks/CQ1eQ93t2.js +6 -0
  700. package/dist/web/_app/immutable/chunks/CQyBDccA.js +1 -0
  701. package/dist/web/_app/immutable/chunks/CUFQMEBn2.js +1 -0
  702. package/dist/web/_app/immutable/chunks/CV5OSCJc.js +1 -0
  703. package/dist/web/_app/immutable/chunks/CYEYDNwO2.js +1 -0
  704. package/dist/web/_app/immutable/chunks/CZgFaimi2.js +83 -0
  705. package/dist/web/_app/immutable/chunks/CZqvCTCv.js +1 -0
  706. package/dist/web/_app/immutable/chunks/CcWaWbrI2.js +1 -0
  707. package/dist/web/_app/immutable/chunks/CdtzRVZK.js +1 -0
  708. package/dist/web/_app/immutable/chunks/CfYG72Hc.js +1 -0
  709. package/dist/web/_app/immutable/chunks/Cg-ND0cl.js +1 -0
  710. package/dist/web/_app/immutable/chunks/Ch1kmm3J2.js +1 -0
  711. package/dist/web/_app/immutable/chunks/CjiMLTwX2.js +1 -0
  712. package/dist/web/_app/immutable/chunks/CkoeSJ8K.js +6 -0
  713. package/dist/web/_app/immutable/chunks/Cp87MaaW.js +1 -0
  714. package/dist/web/_app/immutable/chunks/Cqm5wAin.js +1 -0
  715. package/dist/web/_app/immutable/chunks/Ct1uesdW.js +2 -0
  716. package/dist/web/_app/immutable/chunks/CuYPXpWU.js +1 -0
  717. package/dist/web/_app/immutable/chunks/Cuq8J2Td.js +1 -0
  718. package/dist/web/_app/immutable/chunks/CvSEodTA.js +1 -0
  719. package/dist/web/_app/immutable/chunks/CvqqrTrj.js +1 -0
  720. package/dist/web/_app/immutable/chunks/CzXdOgVl2.js +7 -0
  721. package/dist/web/_app/immutable/chunks/D0W7c6Sg.js +185 -0
  722. package/dist/web/_app/immutable/chunks/D0rBhnqB2.js +1 -0
  723. package/dist/web/_app/immutable/chunks/D1-GZSN1.js +1 -0
  724. package/dist/web/_app/immutable/chunks/D1BLcNJ7.js +1 -0
  725. package/dist/web/_app/immutable/chunks/D1yvtriI.js +1 -0
  726. package/dist/web/_app/immutable/chunks/D25NMRpl.js +1 -0
  727. package/dist/web/_app/immutable/chunks/D3F675fu2.js +2 -0
  728. package/dist/web/_app/immutable/chunks/D4UrtGC1.js +1 -0
  729. package/dist/web/_app/immutable/chunks/D8wZ2xul.js +4 -0
  730. package/dist/web/_app/immutable/chunks/DA3HEX3X.js +1 -0
  731. package/dist/web/_app/immutable/chunks/DE2TCESD2.js +1 -0
  732. package/dist/web/_app/immutable/chunks/DN-gIRdd2.js +1 -0
  733. package/dist/web/_app/immutable/chunks/DPUVu0T0.js +1 -0
  734. package/dist/web/_app/immutable/chunks/DPcejqKZ.js +1 -0
  735. package/dist/web/_app/immutable/chunks/DQMWa2t7.js +1 -0
  736. package/dist/web/_app/immutable/chunks/DRPMJ-3M.js +2 -0
  737. package/dist/web/_app/immutable/chunks/DRPnwQAm.js +1 -0
  738. package/dist/web/_app/immutable/chunks/DXmfl4Hq2.js +1 -0
  739. package/dist/web/_app/immutable/chunks/DXsFrDer2.js +1 -0
  740. package/dist/web/_app/immutable/chunks/DYc5KW3F.js +1 -0
  741. package/dist/web/_app/immutable/chunks/DcPrzUMJ.js +1 -0
  742. package/dist/web/_app/immutable/chunks/DdQVGKgM.js +1 -0
  743. package/dist/web/_app/immutable/chunks/DiFfW--R2.js +2 -0
  744. package/dist/web/_app/immutable/chunks/DjIqa1_y.js +2 -0
  745. package/dist/web/_app/immutable/chunks/Dl63IUnH2.js +224 -0
  746. package/dist/web/_app/immutable/chunks/DmFdD6zT.js +1 -0
  747. package/dist/web/_app/immutable/chunks/Dn3R8S-U.js +1 -0
  748. package/dist/web/_app/immutable/chunks/DpNr1GQ2.js +1 -0
  749. package/dist/web/_app/immutable/chunks/DsFEd8vL2.js +1 -0
  750. package/dist/web/_app/immutable/chunks/DuxziNnw.js +1 -0
  751. package/dist/web/_app/immutable/chunks/FuiJnZG0.js +1 -0
  752. package/dist/web/_app/immutable/chunks/Izkulod7.js +1 -0
  753. package/dist/web/_app/immutable/chunks/LYL8znYR2.js +23 -0
  754. package/dist/web/_app/immutable/chunks/ON7Gta0d.js +1 -0
  755. package/dist/web/_app/immutable/chunks/PhN6tXuJ2.js +1 -0
  756. package/dist/web/_app/immutable/chunks/PyLyCpuL.js +1 -0
  757. package/dist/web/_app/immutable/chunks/RG7IUVLn2.js +2 -0
  758. package/dist/web/_app/immutable/chunks/RK4gCWN0.js +2 -0
  759. package/dist/web/_app/immutable/chunks/SnzlL4ZV.js +1 -0
  760. package/dist/web/_app/immutable/chunks/XcWejFm_.js +5 -0
  761. package/dist/web/_app/immutable/chunks/Xkp5hSA1.js +1 -0
  762. package/dist/web/_app/immutable/chunks/al_nidE9.js +1 -0
  763. package/dist/web/_app/immutable/chunks/e50-izZO2.js +1 -0
  764. package/dist/web/_app/immutable/chunks/golz20La2.js +2 -0
  765. package/dist/web/_app/immutable/chunks/hG-QCarB.js +1 -0
  766. package/dist/web/_app/immutable/chunks/kNaey6uv.js +1 -0
  767. package/dist/web/_app/immutable/chunks/nXReuy-5.js +3 -0
  768. package/dist/web/_app/immutable/chunks/o61aZZFZ2.js +66 -0
  769. package/dist/web/_app/immutable/chunks/qwr5boV8.js +1 -0
  770. package/dist/web/_app/immutable/chunks/rjjyuGhb.js +1 -0
  771. package/dist/web/_app/immutable/chunks/uBgoVlsx.js +3 -0
  772. package/dist/web/_app/immutable/chunks/xY0c-UfG.js +3 -0
  773. package/dist/web/_app/immutable/chunks/xeNvx4Bl2.js +1 -0
  774. package/dist/web/_app/immutable/chunks/xihTtKlq.js +1 -0
  775. package/dist/web/_app/immutable/entry/app.dqA31fjy.js +2 -0
  776. package/dist/web/_app/immutable/entry/start.BB1XrAea.js +1 -0
  777. package/dist/web/_app/immutable/nodes/0.dALEQAnu.js +2 -0
  778. package/dist/web/_app/immutable/nodes/1.aFU32Ukj.js +1 -0
  779. package/dist/web/_app/immutable/nodes/10.C2wB-OnW.js +1 -0
  780. package/dist/web/_app/immutable/nodes/11.CI3a8Rj5.js +1 -0
  781. package/dist/web/_app/immutable/nodes/12.CsTvsKzB.js +1 -0
  782. package/dist/web/_app/immutable/nodes/13.CYrHXCA8.js +1 -0
  783. package/dist/web/_app/immutable/nodes/14.B31HrQ26.js +1 -0
  784. package/dist/web/_app/immutable/nodes/15.DxgnZ61h.js +1 -0
  785. package/dist/web/_app/immutable/nodes/16.DAP8cwVb.js +1 -0
  786. package/dist/web/_app/immutable/nodes/17.CmwXtCDY.js +77 -0
  787. package/dist/web/_app/immutable/nodes/2.BuGXEIlN.js +122 -0
  788. package/dist/web/_app/immutable/nodes/3.CmhZJWHE.js +6 -0
  789. package/dist/web/_app/immutable/nodes/4.BRfoPTm-.js +11 -0
  790. package/dist/web/_app/immutable/nodes/5.C7gPuZM_.js +1 -0
  791. package/dist/web/_app/immutable/nodes/6.qKODVbya.js +6 -0
  792. package/dist/web/_app/immutable/nodes/7.BZO37zAJ.js +1 -0
  793. package/dist/web/_app/immutable/nodes/8.xF_Dd1BO.js +1 -0
  794. package/dist/web/_app/immutable/nodes/9.Bow4UyKV.js +1 -0
  795. package/dist/web/_app/version.json +1 -1
  796. package/dist/web/favicon-16.png +0 -0
  797. package/dist/web/favicon-180.png +0 -0
  798. package/dist/web/favicon-32.png +0 -0
  799. package/dist/web/favicon-48.png +0 -0
  800. package/dist/web/favicon-512.png +0 -0
  801. package/dist/web/favicon.ico +0 -0
  802. package/dist/web/favicon.svg +8 -0
  803. package/dist/web/index.html +56 -21
  804. package/dist/web/studiograph-logomark.svg +29 -0
  805. package/package.json +35 -14
  806. package/dist/agent/skills/enrich-entities.md +0 -136
  807. package/dist/agent/skills/sync-configuration.md +0 -119
  808. package/dist/agent/skills/sync-setup.md +0 -82
  809. package/dist/agent/tools/message-tools.d.ts +0 -42
  810. package/dist/agent/tools/message-tools.js +0 -106
  811. package/dist/agent/tools/message-tools.js.map +0 -1
  812. package/dist/cli/commands/app.d.ts +0 -7
  813. package/dist/cli/commands/app.js +0 -268
  814. package/dist/cli/commands/app.js.map +0 -1
  815. package/dist/cli/commands/clear.d.ts +0 -5
  816. package/dist/cli/commands/clear.js +0 -36
  817. package/dist/cli/commands/clear.js.map +0 -1
  818. package/dist/cli/commands/collection.d.ts +0 -10
  819. package/dist/cli/commands/collection.js.map +0 -1
  820. package/dist/cli/commands/join.d.ts +0 -9
  821. package/dist/cli/commands/join.js +0 -255
  822. package/dist/cli/commands/join.js.map +0 -1
  823. package/dist/cli/commands/start.d.ts +0 -7
  824. package/dist/cli/commands/start.js +0 -584
  825. package/dist/cli/commands/start.js.map +0 -1
  826. package/dist/cli/commands/update.d.ts +0 -8
  827. package/dist/cli/commands/update.js +0 -155
  828. package/dist/cli/commands/update.js.map +0 -1
  829. package/dist/cli/commands/user.d.ts +0 -7
  830. package/dist/cli/commands/user.js +0 -175
  831. package/dist/cli/commands/user.js.map +0 -1
  832. package/dist/cli/scaffolding.d.ts +0 -12
  833. package/dist/cli/scaffolding.js +0 -397
  834. package/dist/cli/scaffolding.js.map +0 -1
  835. package/dist/integrations/registry.d.ts +0 -8
  836. package/dist/integrations/registry.js +0 -7
  837. package/dist/integrations/registry.js.map +0 -1
  838. package/dist/integrations/types.d.ts +0 -43
  839. package/dist/integrations/types.js +0 -5
  840. package/dist/integrations/types.js.map +0 -1
  841. package/dist/lib/lib/utils.d.ts +0 -2
  842. package/dist/lib/lib/utils.js +0 -6
  843. package/dist/lib/lib/utils.js.map +0 -1
  844. package/dist/server/chrome/chrome.css +0 -691
  845. package/dist/server/chrome/chrome.js +0 -374
  846. package/dist/server/commit-scheduler.d.ts +0 -39
  847. package/dist/server/commit-scheduler.js +0 -113
  848. package/dist/server/commit-scheduler.js.map +0 -1
  849. package/dist/server/plugin-loader.d.ts +0 -53
  850. package/dist/server/plugin-loader.js +0 -155
  851. package/dist/server/plugin-loader.js.map +0 -1
  852. package/dist/server/routes/meeting-ingest-api.d.ts +0 -14
  853. package/dist/server/routes/meeting-ingest-api.js.map +0 -1
  854. package/dist/server/routes/messages-api.d.ts +0 -15
  855. package/dist/server/routes/messages-api.js +0 -385
  856. package/dist/server/routes/messages-api.js.map +0 -1
  857. package/dist/services/batch-processor.d.ts +0 -49
  858. package/dist/services/batch-processor.js +0 -166
  859. package/dist/services/batch-processor.js.map +0 -1
  860. package/dist/services/briefing.d.ts +0 -26
  861. package/dist/services/briefing.js +0 -230
  862. package/dist/services/briefing.js.map +0 -1
  863. package/dist/services/heartbeat.d.ts +0 -28
  864. package/dist/services/heartbeat.js +0 -183
  865. package/dist/services/heartbeat.js.map +0 -1
  866. package/dist/services/image-import-service.d.ts +0 -24
  867. package/dist/services/image-import-service.js +0 -108
  868. package/dist/services/image-import-service.js.map +0 -1
  869. package/dist/services/insights.d.ts +0 -38
  870. package/dist/services/insights.js +0 -269
  871. package/dist/services/insights.js.map +0 -1
  872. package/dist/services/meeting-processor.js.map +0 -1
  873. package/dist/services/message-service.d.ts +0 -68
  874. package/dist/services/message-service.js +0 -220
  875. package/dist/services/message-service.js.map +0 -1
  876. package/dist/utils/workspace-config.d.ts +0 -8
  877. package/dist/utils/workspace-config.js +0 -22
  878. package/dist/utils/workspace-config.js.map +0 -1
  879. package/dist/web/_app/immutable/assets/0.uPSqtsC5.css +0 -1
  880. package/dist/web/_app/immutable/assets/11.2jzI3cZY.css +0 -1
  881. package/dist/web/_app/immutable/assets/12.CT4xL4K3.css +0 -1
  882. package/dist/web/_app/immutable/assets/13.BUrHkYry.css +0 -1
  883. package/dist/web/_app/immutable/assets/2.DpQWr6q6.css +0 -1
  884. package/dist/web/_app/immutable/assets/3.BxdqT7zi.css +0 -1
  885. package/dist/web/_app/immutable/assets/4.DdNqjd0T.css +0 -1
  886. package/dist/web/_app/immutable/assets/5.DFeGxLYf.css +0 -1
  887. package/dist/web/_app/immutable/assets/6.DXZr_rI_.css +0 -1
  888. package/dist/web/_app/immutable/assets/GraphView.D9ePpZez.css +0 -1
  889. package/dist/web/_app/immutable/assets/Toaster.rN8r_Hzv.css +0 -1
  890. package/dist/web/_app/immutable/assets/ViewToolbar.BWE03S64.css +0 -1
  891. package/dist/web/_app/immutable/assets/WorkspaceView.CgGDi_Zb.css +0 -1
  892. package/dist/web/_app/immutable/assets/wikilinks.CzMBkUMB.css +0 -1
  893. package/dist/web/_app/immutable/chunks/-jG4Obyh.js +0 -4
  894. package/dist/web/_app/immutable/chunks/2nrc8f_O.js +0 -1
  895. package/dist/web/_app/immutable/chunks/7S2LGqoP.js +0 -2
  896. package/dist/web/_app/immutable/chunks/8Q3ZJIYu.js +0 -1
  897. package/dist/web/_app/immutable/chunks/B8ydT5xX.js +0 -2
  898. package/dist/web/_app/immutable/chunks/BB_5th5W.js +0 -3383
  899. package/dist/web/_app/immutable/chunks/BEn6N5c2.js +0 -1
  900. package/dist/web/_app/immutable/chunks/BFNNbCAM.js +0 -1
  901. package/dist/web/_app/immutable/chunks/BFZZEUd5.js +0 -1
  902. package/dist/web/_app/immutable/chunks/BGk8kfOE.js +0 -2
  903. package/dist/web/_app/immutable/chunks/BIBCr278.js +0 -1
  904. package/dist/web/_app/immutable/chunks/BPCkkwqe.js +0 -1
  905. package/dist/web/_app/immutable/chunks/BhlvzGQx.js +0 -1
  906. package/dist/web/_app/immutable/chunks/BlMknQeF.js +0 -1
  907. package/dist/web/_app/immutable/chunks/BpjqrlCg.js +0 -1
  908. package/dist/web/_app/immutable/chunks/BrebtJNG.js +0 -18
  909. package/dist/web/_app/immutable/chunks/ByBZ7JIT.js +0 -1
  910. package/dist/web/_app/immutable/chunks/Bz0ZjVQE.js +0 -2
  911. package/dist/web/_app/immutable/chunks/BzJTZOK2.js +0 -1
  912. package/dist/web/_app/immutable/chunks/C0LVZhvn.js +0 -1
  913. package/dist/web/_app/immutable/chunks/C3uBrOS5.js +0 -1
  914. package/dist/web/_app/immutable/chunks/C8R1sDpW.js +0 -1
  915. package/dist/web/_app/immutable/chunks/CDRHrs0g.js +0 -1
  916. package/dist/web/_app/immutable/chunks/CN5_py1I.js +0 -1
  917. package/dist/web/_app/immutable/chunks/CPne482a.js +0 -1
  918. package/dist/web/_app/immutable/chunks/CSVbGg_b.js +0 -5
  919. package/dist/web/_app/immutable/chunks/CWyU-seV.js +0 -1
  920. package/dist/web/_app/immutable/chunks/CX_61fY8.js +0 -5
  921. package/dist/web/_app/immutable/chunks/CYrVHOHA.js +0 -1
  922. package/dist/web/_app/immutable/chunks/ChsiIm-E.js +0 -1
  923. package/dist/web/_app/immutable/chunks/CojKppbh.js +0 -1
  924. package/dist/web/_app/immutable/chunks/CqkleIqs.js +0 -1
  925. package/dist/web/_app/immutable/chunks/CtYLtD7K.js +0 -1
  926. package/dist/web/_app/immutable/chunks/D-HDy5qS.js +0 -1
  927. package/dist/web/_app/immutable/chunks/D5aIYSkd.js +0 -1
  928. package/dist/web/_app/immutable/chunks/DBBXARVf.js +0 -1
  929. package/dist/web/_app/immutable/chunks/DFYOFE3o.js +0 -1
  930. package/dist/web/_app/immutable/chunks/DKaafS50.js +0 -1
  931. package/dist/web/_app/immutable/chunks/DUMOo1Pn.js +0 -1
  932. package/dist/web/_app/immutable/chunks/DWX9f6AF.js +0 -1
  933. package/dist/web/_app/immutable/chunks/DY8-EONP.js +0 -1
  934. package/dist/web/_app/immutable/chunks/DkoHPElM.js +0 -1
  935. package/dist/web/_app/immutable/chunks/DnL9f0lc.js +0 -7
  936. package/dist/web/_app/immutable/chunks/DsnmJJEf.js +0 -1
  937. package/dist/web/_app/immutable/chunks/DujahU3W.js +0 -1
  938. package/dist/web/_app/immutable/chunks/FBn-ES5k.js +0 -1
  939. package/dist/web/_app/immutable/chunks/HQpwSOb3.js +0 -1
  940. package/dist/web/_app/immutable/chunks/KyjzlWRN.js +0 -2
  941. package/dist/web/_app/immutable/chunks/PPVm8Dsz.js +0 -1
  942. package/dist/web/_app/immutable/chunks/UwYfmrPx.js +0 -2
  943. package/dist/web/_app/immutable/chunks/W25ZVI8L.js +0 -2
  944. package/dist/web/_app/immutable/chunks/WSUKABI_.js +0 -1
  945. package/dist/web/_app/immutable/chunks/XjsuEnNE.js +0 -5
  946. package/dist/web/_app/immutable/chunks/ZD6ARAtb.js +0 -1
  947. package/dist/web/_app/immutable/chunks/pBTf4stg.js +0 -2
  948. package/dist/web/_app/immutable/chunks/rGIarcnp.js +0 -1
  949. package/dist/web/_app/immutable/chunks/vxEtkL8z.js +0 -1
  950. package/dist/web/_app/immutable/chunks/wDIGUaoU.js +0 -3
  951. package/dist/web/_app/immutable/entry/app.ZHiTM8WS.js +0 -2
  952. package/dist/web/_app/immutable/entry/start.C9-FfAVc.js +0 -1
  953. package/dist/web/_app/immutable/nodes/0.D92Orz8k.js +0 -2
  954. package/dist/web/_app/immutable/nodes/1.BMdyglM_.js +0 -1
  955. package/dist/web/_app/immutable/nodes/10.BilVDHSL.js +0 -1
  956. package/dist/web/_app/immutable/nodes/11.CS9E0Hic.js +0 -1
  957. package/dist/web/_app/immutable/nodes/12.D3BOxYzG.js +0 -1
  958. package/dist/web/_app/immutable/nodes/13.CClMKYtN.js +0 -1
  959. package/dist/web/_app/immutable/nodes/2.C8KKElPL.js +0 -267
  960. package/dist/web/_app/immutable/nodes/3.C5n5uiWU.js +0 -5
  961. package/dist/web/_app/immutable/nodes/4.DcGVvgiL.js +0 -11
  962. package/dist/web/_app/immutable/nodes/5.DxIjnFQO.js +0 -4
  963. package/dist/web/_app/immutable/nodes/6.lkh6kKLj.js +0 -1
  964. package/dist/web/_app/immutable/nodes/7.Bg7SbfhA.js +0 -1
  965. package/dist/web/_app/immutable/nodes/8.DcXWCNBE.js +0 -1
  966. package/dist/web/_app/immutable/nodes/9.Cefi8Jfm.js +0 -1
@@ -4,37 +4,270 @@
4
4
  * POST /api/repos/:repo/entities/:type/:id/assets — multipart image upload
5
5
  * GET /api/assets/* — serve local assets
6
6
  */
7
- import { isAdminOrOwner } from '../../services/auth-service.js';
7
+ import { reqGitUser } from './graph-api-access.js';
8
+ import * as access from '../../services/access-control.js';
9
+ import { listRepoAssets, listWorkspaceAssets, clearLegacyBucketCache, isFreeStandingOwner } from '../../services/asset-listing.js';
10
+ import { createEntityFromAsset } from '../../services/asset-import-service.js';
11
+ import { assetDeleteDenial, assetDeleteDenialByAssetId } from '../../services/asset-delete-authz.js';
12
+ import { resolveWorkspaceAssetsRepo } from '../../services/workspace-assets-folder.js';
13
+ import { Workspace } from '../../core/workspace.js';
8
14
  import { existsSync, createReadStream, statSync } from 'fs';
9
15
  import { join, extname } from 'path';
16
+ import { artifactAssetKey } from '../../services/artifact-asset-token.js';
17
+ import { mintArtifactAssetTokenForText } from '../artifact-asset-mint.js';
18
+ /** Max bytes proxied same-origin through the artifact-asset token path.
19
+ * `downloadAssetById` buffers, so this guard (checked BEFORE download against
20
+ * the index size) bounds memory. Images well under this; refuse unknown size. */
21
+ const ARTIFACT_PROXY_MAX_BYTES = 25 * 1024 * 1024;
22
+ /** Extensions the artifact-asset token proxy will serve — images + fonts only.
23
+ * An artifact embeds these; a same-repo doc/dataset/binary referenced in text
24
+ * must NOT become a 12h bearer URL. */
25
+ const PROXYABLE_ASSET_EXTS = new Set([
26
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.avif',
27
+ '.woff', '.woff2', '.ttf', '.otf',
28
+ ]);
10
29
  const MIME_TYPES = {
11
30
  '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
12
31
  '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
13
32
  '.ico': 'image/x-icon', '.avif': 'image/avif',
33
+ '.woff': 'font/woff', '.woff2': 'font/woff2',
34
+ '.ttf': 'font/ttf', '.otf': 'font/otf',
14
35
  };
36
+ function isFontExtension(filename) {
37
+ return ['.woff', '.woff2', '.ttf', '.otf'].includes(extname(filename).toLowerCase());
38
+ }
39
+ function addFontResourceHeaders(reply, filename) {
40
+ if (!isFontExtension(filename))
41
+ return;
42
+ reply.header('Access-Control-Allow-Origin', '*');
43
+ reply.header('Cross-Origin-Resource-Policy', 'cross-origin');
44
+ }
15
45
  function hasRepoAccess(req, repoName, workspaceManager, authService) {
16
46
  const user = req.user;
17
- const repoConfig = workspaceManager.getRepoConfig(repoName);
18
- if (repoConfig?.private) {
19
- if (!user)
20
- return !repoConfig.owner_id;
21
- if (repoConfig.owner_id === user.id)
22
- return true;
23
- if (!repoConfig.owner_id)
24
- return false;
47
+ return access.hasRepoAccess(user, repoName, workspaceManager, authService);
48
+ }
49
+ /**
50
+ * Serve an immutable-key asset's bytes once authorization has already been
51
+ * decided by the caller. This is the shared serving entry behind BOTH the
52
+ * authed `/api/assets/:assetId/:filename` route (authz = hasRepoAccess) and the
53
+ * public `/api/share/:token/asset/...` route (authz = valid token + repo scope).
54
+ *
55
+ * It does serving ONLY — R2 302-redirect vs local stream, path-segment
56
+ * hardening, and cache headers. The index lookup, filename match, and the
57
+ * access decision stay in each route so neither surface can drift into serving
58
+ * a file the other wouldn't authorize.
59
+ *
60
+ * Mechanically this is a THIN WRAPPER over the generic `serveBackendObject`
61
+ * primitive (the same primitive that backs conversation-scratch images), so
62
+ * both surfaces share one serving codepath. Two asset-specific concerns stay
63
+ * here as pre-steps rather than leaking into the generic primitive:
64
+ * 1. The R2 FONT branch — fonts are proxied through the app origin (not 302'd)
65
+ * with cross-origin CORS headers, and HEAD is answered from the indexed
66
+ * size WITHOUT downloading the bytes. This is unique to the catalog.
67
+ * 2. The catalog's stricter local segment hardening (rejects a `.`-prefixed
68
+ * filename, `/` or `\` in either segment), preserved verbatim.
69
+ *
70
+ * The owning `repo` must be resolved by the caller (from the asset index) so the
71
+ * correct graph/backend is selected. Always fail-closed with 404 — never leak
72
+ * existence.
73
+ */
74
+ export async function serveIndexedAsset(workspaceManager, reply, opts) {
75
+ const { assetId, filename, repo, isHead, size } = opts;
76
+ let graph;
77
+ try {
78
+ graph = workspaceManager.getGraph(repo);
79
+ }
80
+ catch {
81
+ return reply.status(404).send({ error: 'Asset not found' });
82
+ }
83
+ const storage = graph.getAssetStorage();
84
+ // Artifact-asset token path: uniform same-origin serve for BOTH backends,
85
+ // with private/no-cache + a strong (content-immutable) ETag so revocation is
86
+ // enforced on every revalidation while a 304 avoids re-download. Restricted
87
+ // to images + fonts (no Range/video this pass).
88
+ if (opts.proxyR2) {
89
+ const ext = extname(filename).toLowerCase();
90
+ // Token proxy is restricted to images + fonts (the only types an artifact
91
+ // legitimately embeds). Refuse anything else so a same-repo doc/dataset/etc.
92
+ // referenced in artifact text can't be turned into a bearer URL.
93
+ if (!PROXYABLE_ASSET_EXTS.has(ext)) {
94
+ reply.log?.warn?.({ assetId, filename, ext }, 'artifact-asset proxy refused (non-image/font)');
95
+ return reply.status(404).send({ error: 'Asset not found' });
96
+ }
97
+ const etag = `"${assetId}/${filename}"`;
98
+ reply.header('ETag', etag);
99
+ reply.header('Cache-Control', 'private, no-cache');
100
+ reply.header('X-Content-Type-Options', 'nosniff');
101
+ // Neutralize active content (legacy/migrated SVGs) served same-origin.
102
+ reply.header('Content-Security-Policy', "default-src 'none'; style-src 'unsafe-inline'; sandbox");
103
+ reply.header('Content-Type', MIME_TYPES[ext] || 'application/octet-stream');
104
+ addFontResourceHeaders(reply, filename);
105
+ if (opts.ifNoneMatch === etag)
106
+ return reply.status(304).send();
107
+ try {
108
+ if (graph.getAssetStorage() === 'r2') {
109
+ // The index `size` is advisory and often null (migrate-assets/sweep
110
+ // never set it), so DON'T fail-closed on unknown size — the normal
111
+ // serve path doesn't either. Pre-reject only a KNOWN-oversize asset;
112
+ // for HEAD with a known size, answer without downloading.
113
+ if (typeof size === 'number' && size > ARTIFACT_PROXY_MAX_BYTES) {
114
+ reply.log?.warn?.({ assetId, filename, size }, 'artifact-asset proxy refused (oversized)');
115
+ return reply.status(404).send({ error: 'Asset not found' });
116
+ }
117
+ if (isHead) {
118
+ if (typeof size === 'number' && size > 0)
119
+ reply.header('Content-Length', size);
120
+ return reply.send();
121
+ }
122
+ const bytes = await graph.downloadAssetById(assetId, filename);
123
+ // Post-download cap bounds what we serve when size was unknown.
124
+ if (bytes.length > ARTIFACT_PROXY_MAX_BYTES) {
125
+ reply.log?.warn?.({ assetId, filename, len: bytes.length }, 'artifact-asset proxy refused (oversized after download)');
126
+ return reply.status(404).send({ error: 'Asset not found' });
127
+ }
128
+ reply.header('Content-Length', bytes.length);
129
+ return reply.send(bytes);
130
+ }
131
+ // Local backend: stream from disk (same path-hardening as below).
132
+ if (filename.includes('..') || filename.includes('/') || filename.includes('\\') || filename.startsWith('.') ||
133
+ assetId.includes('..') || assetId.includes('/') || assetId.includes('\\')) {
134
+ return reply.status(404).send({ error: 'Asset not found' });
135
+ }
136
+ const base = workspaceManager.workspacePath ?? process.cwd();
137
+ const fp = join(base, '.studiograph', 'assets', assetId, filename);
138
+ if (!existsSync(fp))
139
+ return reply.status(404).send({ error: 'Asset not found' });
140
+ reply.header('Content-Length', statSync(fp).size);
141
+ if (isHead)
142
+ return reply.send();
143
+ return reply.send(createReadStream(fp));
144
+ }
145
+ catch (err) {
146
+ reply.log?.warn?.({ err, assetId, filename }, 'artifact-asset proxy serve failed');
147
+ return reply.status(404).send({ error: 'Asset not found' });
148
+ }
149
+ }
150
+ // (1) R2 font pre-step — catalog-only CORS proxy + HEAD-from-size optimization.
151
+ if (storage === 'r2' && isFontExtension(filename)) {
152
+ try {
153
+ const ext = extname(filename).toLowerCase();
154
+ if (isHead && typeof size === 'number') {
155
+ reply.header('Content-Type', MIME_TYPES[ext] || 'application/octet-stream');
156
+ reply.header('Content-Length', size);
157
+ reply.header('Cache-Control', 'private, max-age=14400');
158
+ reply.header('X-Content-Type-Options', 'nosniff');
159
+ addFontResourceHeaders(reply, filename);
160
+ return reply.send();
161
+ }
162
+ const bytes = await graph.downloadAssetById(assetId, filename);
163
+ reply.header('Content-Type', MIME_TYPES[ext] || 'application/octet-stream');
164
+ reply.header('Content-Length', bytes.length);
165
+ reply.header('Cache-Control', 'private, max-age=14400');
166
+ reply.header('X-Content-Type-Options', 'nosniff');
167
+ addFontResourceHeaders(reply, filename);
168
+ if (isHead)
169
+ return reply.send();
170
+ return reply.send(bytes);
171
+ }
172
+ catch (err) {
173
+ reply.log?.warn?.({ err, assetId, filename }, 'asset r2 signed-url generation failed');
174
+ return reply.status(404).send({ error: 'Asset not found' });
175
+ }
176
+ }
177
+ // (2) Local-backend catalog segment hardening (stricter than the generic
178
+ // primitive: also rejects a `.`-prefixed filename). Applied before delegation
179
+ // so observable behavior is identical to the pre-unification serve path.
180
+ if (storage !== 'r2') {
181
+ if (filename.includes('..') || filename.includes('/') || filename.includes('\\') || filename.startsWith('.') ||
182
+ assetId.includes('..') || assetId.includes('/') || assetId.includes('\\')) {
183
+ return reply.status(404).send({ error: 'Asset not found' });
184
+ }
185
+ }
186
+ // Delegate to the shared primitive. The catalog key is `{assetId}/{filename}`,
187
+ // per-backend cache differs (R2 private/4h vs local immutable/1y), fonts on the
188
+ // local path keep their CORS headers, and the R2 signer stays the catalog one.
189
+ return serveBackendObject(workspaceManager, reply, {
190
+ repo,
191
+ key: `${assetId}/${filename}`,
192
+ filename,
193
+ isHead,
194
+ cacheControl: { r2: 'private, max-age=14400', local: 'public, max-age=31536000, immutable' },
195
+ fontHeaders: true,
196
+ notFoundError: 'Asset not found',
197
+ signR2Url: (method) => graph.getAssetSignedUrlByAssetId(assetId, filename, method),
198
+ });
199
+ }
200
+ /**
201
+ * Generic backend-object serving primitive (composer-vision-attach / E2a). Serves
202
+ * bytes for a RAW backend key once the caller has decided authorization: R2 → 302
203
+ * to a method-bound signed URL; local → stream from `.studiograph/assets/{key}`
204
+ * with a sandbox CSP + nosniff. The R2 signer is INJECTED so the caller controls
205
+ * which keyspace is signed (catalog asset vs conversation-scratch image), and the
206
+ * cache header may be a single value or a per-backend pair. Always fail-closed 404.
207
+ *
208
+ * This is the single serving codepath behind BOTH `serveIndexedAsset` (the
209
+ * catalog, which layers an R2 font pre-step + stricter segment hardening on top)
210
+ * and the conversation-scratch image route.
211
+ */
212
+ export async function serveBackendObject(workspaceManager, reply, opts) {
213
+ const { repo, key, filename, contentType, isHead, cacheControl, fontHeaders, signR2Url } = opts;
214
+ const notFound = opts.notFoundError ?? 'Not found';
215
+ const r2Cache = typeof cacheControl === 'string' ? cacheControl : cacheControl.r2;
216
+ const localCache = typeof cacheControl === 'string' ? cacheControl : cacheControl.local;
217
+ let graph;
218
+ try {
219
+ graph = workspaceManager.getGraph(repo);
25
220
  }
26
- if (!user)
27
- return true;
28
- if (isAdminOrOwner(user.role))
29
- return true;
30
- return authService.getUserCollections(user.id).includes(repoName);
221
+ catch {
222
+ return reply.status(404).send({ error: notFound });
223
+ }
224
+ if (graph.getAssetStorage() === 'r2') {
225
+ try {
226
+ const signed = await signR2Url(isHead ? 'HEAD' : 'GET');
227
+ reply.header('Cache-Control', r2Cache);
228
+ return reply.redirect(signed, 302);
229
+ }
230
+ catch (err) {
231
+ reply.log?.warn?.({ err, key }, 'backend object r2 signed-url generation failed');
232
+ return reply.status(404).send({ error: notFound });
233
+ }
234
+ }
235
+ // Local backend: stream from .studiograph/assets/{key}. Defense-in-depth
236
+ // segment validation (the key is stored server-side, but never trust it).
237
+ for (const seg of key.split('/')) {
238
+ if (seg === '' || seg === '.' || seg === '..' || seg.includes('\\')) {
239
+ return reply.status(404).send({ error: notFound });
240
+ }
241
+ }
242
+ const localBase = workspaceManager.workspacePath ?? process.cwd();
243
+ const filePath = join(localBase, '.studiograph', 'assets', key);
244
+ if (!existsSync(filePath)) {
245
+ return reply.status(404).send({ error: notFound });
246
+ }
247
+ const ext = extname(filename).toLowerCase();
248
+ reply.header('Content-Type', contentType || MIME_TYPES[ext] || 'application/octet-stream');
249
+ reply.header('Content-Length', statSync(filePath).size);
250
+ reply.header('Cache-Control', localCache);
251
+ if (fontHeaders)
252
+ addFontResourceHeaders(reply, filename);
253
+ // Neutralize active content served from the app origin. SVG uploads are
254
+ // blocked, but legacy/migrated/indexed SVGs would otherwise run script when
255
+ // opened directly as image/svg+xml — now reachable anonymously via the share
256
+ // route. A sandbox CSP blocks script + plugins on direct navigation (img
257
+ // embeds still render, since <img> never executes SVG script), and nosniff
258
+ // stops a mislabeled asset from being re-interpreted as HTML.
259
+ reply.header('Content-Security-Policy', "default-src 'none'; style-src 'unsafe-inline'; sandbox");
260
+ reply.header('X-Content-Type-Options', 'nosniff');
261
+ if (isHead)
262
+ return reply.send();
263
+ return reply.send(createReadStream(filePath));
31
264
  }
32
265
  export async function registerAssetApiRoutes(fastify, workspaceManager, authService) {
33
266
  // POST /api/repos/:repo/entities/:type/:id/assets — upload an image
34
267
  fastify.post('/api/repos/:repo/entities/:type/:id/assets', async (req, reply) => {
35
268
  const { repo, type, id } = req.params;
36
269
  if (!hasRepoAccess(req, repo, workspaceManager, authService)) {
37
- return reply.status(404).send({ error: `Collection "${repo}" not found` });
270
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
38
271
  }
39
272
  let graph;
40
273
  try {
@@ -49,15 +282,21 @@ export async function registerAssetApiRoutes(fastify, workspaceManager, authServ
49
282
  if (!file.mimetype.startsWith('image/')) {
50
283
  return reply.status(400).send({ error: 'Only image files are supported' });
51
284
  }
285
+ // Block SVG: stored XSS — SVG can contain <script> and the GET route
286
+ // serves it inline as image/svg+xml, executing in the app origin.
287
+ if (file.mimetype === 'image/svg+xml' || file.filename?.toLowerCase().endsWith('.svg')) {
288
+ return reply.status(415).send({ error: 'SVG uploads are not supported (stored XSS risk).' });
289
+ }
52
290
  const buffer = await file.toBuffer();
53
291
  const filename = `${Date.now()}-${file.filename.replace(/[^a-zA-Z0-9._-]/g, '_')}`;
292
+ const uploader = req.user;
54
293
  try {
55
- const asset = await graph.uploadAsset(type, id, filename, buffer);
56
- // For local storage, return an HTTP-servable URL instead of the filesystem path
57
- const url = asset.url.startsWith('file://') || !asset.url.startsWith('http')
58
- ? `/api/assets/${type}/${id}/${asset.filename}`
59
- : asset.path; // R2 returns a full public URL
60
- return reply.send({ url, filename: asset.filename, size: asset.size });
294
+ const asset = await graph.uploadAsset(type, id, filename, buffer, undefined, {
295
+ uploaderUserId: uploader?.id != null ? String(uploader.id) : null,
296
+ });
297
+ // uploadAsset returns the canonical public URL (immutable-key scheme
298
+ // when the index is wired, legacy otherwise) — don't re-derive it.
299
+ return reply.send({ url: asset.publicUrl, filename: asset.filename, size: asset.size });
61
300
  }
62
301
  catch (err) {
63
302
  if (err.message?.includes('not configured')) {
@@ -66,66 +305,398 @@ export async function registerAssetApiRoutes(fastify, workspaceManager, authServ
66
305
  return reply.status(400).send({ error: err.message });
67
306
  }
68
307
  });
69
- // GET /api/assets/:type/:id/:filenameserve local assets
70
- fastify.get('/api/assets/:type/:id/:filename', async (req, reply) => {
71
- const { type, id, filename } = req.params;
72
- const workspacePath = workspaceManager.workspacePath ?? process.cwd();
73
- const filePath = join(workspacePath, '.studiograph', 'assets', type, id, filename);
74
- if (!existsSync(filePath)) {
75
- return reply.status(404).send({ error: 'Asset not found' });
308
+ // POST /api/repos/:repo/entities/artifact/:id/asset-tokenmint a short-lived
309
+ // (12h), ARTIFACT-SCOPED capability token so a sandboxed iframe (opaque origin,
310
+ // no cookie) can load the exact images this artifact embeds. Authz: a normal
311
+ // session with repo access. The allowlist is the set of indexed assets the
312
+ // bundle references that actually live in THIS repo — cross-repo / unknown /
313
+ // filename-mismatched refs are dropped (fail-closed).
314
+ fastify.post('/api/repos/:repo/entities/artifact/:id/asset-token', async (req, reply) => {
315
+ const { repo, id } = req.params;
316
+ const user = req.user;
317
+ if (!hasRepoAccess(req, repo, workspaceManager, authService)) {
318
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
76
319
  }
77
- const ext = extname(filename).toLowerCase();
78
- const contentType = MIME_TYPES[ext] || 'application/octet-stream';
79
- const stats = statSync(filePath);
80
- reply.header('Content-Type', contentType);
81
- reply.header('Content-Length', stats.size);
82
- reply.header('Cache-Control', 'public, max-age=31536000, immutable');
83
- return reply.send(createReadStream(filePath));
320
+ let graph;
321
+ try {
322
+ graph = workspaceManager.getGraph(repo);
323
+ }
324
+ catch (err) {
325
+ return reply.status(404).send({ error: err.message });
326
+ }
327
+ // Allowlist source = the EXACT version being rendered. For a history
328
+ // preview the client passes the version's commit; we derive the allowlist
329
+ // from the artifact body AT THAT COMMIT (server-trusted, not client refs),
330
+ // so a preview token authorizes the previewed version's assets — not the
331
+ // current ones. Otherwise use the live body.
332
+ const commit = typeof req.query.commit === 'string' && /^[0-9a-f]{7,40}$/i.test(req.query.commit)
333
+ ? req.query.commit : null;
334
+ let text;
335
+ if (commit) {
336
+ const atCommit = graph.getEntityAtCommit('artifact', id, commit);
337
+ if (!atCommit)
338
+ return reply.status(404).send({ error: 'Artifact version not found' });
339
+ text = atCommit.content;
340
+ }
341
+ else {
342
+ let entity;
343
+ try {
344
+ entity = graph.get('artifact', id);
345
+ }
346
+ catch {
347
+ return reply.status(404).send({ error: 'Artifact not found' });
348
+ }
349
+ if (!entity)
350
+ return reply.status(404).send({ error: 'Artifact not found' });
351
+ text = entity.document?.content ?? '';
352
+ }
353
+ return reply.send(mintArtifactAssetTokenForText(workspaceManager, authService, repo, id, text, user ? String(user.id) : '0'));
354
+ });
355
+ // GET / HEAD /api/assets/:assetId/:filename — IMMUTABLE-KEY serve route.
356
+ //
357
+ // New scheme: assets are keyed `{assetId}/{filename}` (assetId = UUIDv4) and
358
+ // ownership/authz lives in the AssetIndexService, not in the URL. Two path
359
+ // segments after /api/assets/ vs three for the legacy route below — Fastify
360
+ // disambiguates by arity, so both coexist during the migration cutover.
361
+ //
362
+ // Authz: index.get(assetId) → repo → hasRepoAccess. Fail-closed on a miss.
363
+ // The filename in the URL MUST match the indexed filename, otherwise a valid
364
+ // assetId would authorize arbitrary keys under that UUID directory.
365
+ fastify.route({
366
+ method: ['GET', 'HEAD'],
367
+ url: '/api/assets/:assetId/:filename',
368
+ handler: async (req, reply) => {
369
+ const { assetId, filename } = req.params;
370
+ const isHead = req.method === 'HEAD';
371
+ const ifNoneMatch = req.headers['if-none-match'];
372
+ const row = workspaceManager.getAssetIndex().get(assetId);
373
+ // Fail-closed: unknown id, or a filename that doesn't match the indexed
374
+ // one, both 404 (never leak existence; never authorize a sibling key).
375
+ if (!row || row.filename !== filename) {
376
+ return reply.status(404).send({ error: 'Asset not found' });
377
+ }
378
+ const repoConfig = workspaceManager.getRepoConfig(row.repo);
379
+ const isPublicWorkspaceFont = isFontExtension(filename) &&
380
+ repoConfig?.system === true &&
381
+ repoConfig?.workspace_wide === true;
382
+ const user = req.user;
383
+ if (!isPublicWorkspaceFont && !user && authService.hasUsers()) {
384
+ // No session cookie/Bearer (e.g. a sandboxed artifact iframe). Try the
385
+ // artifact-asset capability token before failing. The preHandler only
386
+ // verified the signature + 2-seg shape and stashed the payload as a
387
+ // CANDIDATE — the authoritative checks happen HERE against the resolved
388
+ // row: repo match + exact-asset allowlist + the token's user STILL has
389
+ // repo access (revocation enforced per-serve). Serve same-origin.
390
+ const tok = req.artifactAssetToken;
391
+ if (tok) {
392
+ const tokenUser = authService.findUserById(Number(tok.userId));
393
+ if (tok.repo === row.repo &&
394
+ tok.assets.includes(artifactAssetKey(assetId, filename)) &&
395
+ tokenUser &&
396
+ access.hasRepoAccess(tokenUser, row.repo, workspaceManager, authService)) {
397
+ return serveIndexedAsset(workspaceManager, reply, {
398
+ assetId, filename, repo: row.repo, isHead, size: row.size, proxyR2: true, ifNoneMatch,
399
+ });
400
+ }
401
+ }
402
+ return reply.status(404).send({ error: 'Asset not found' });
403
+ }
404
+ if (!isPublicWorkspaceFont && !hasRepoAccess(req, row.repo, workspaceManager, authService)) {
405
+ return reply.status(404).send({ error: 'Asset not found' });
406
+ }
407
+ // Authz decided above (folder access); shared helper does the serving.
408
+ return serveIndexedAsset(workspaceManager, reply, { assetId, filename, repo: row.repo, isHead, size: row.size });
409
+ },
84
410
  });
85
- // GET /api/repos/:repo/assetslist all assets in a collection
411
+ // GET / HEAD /api/assets/:type/:id/:filenameLEGACY serve route (kept for
412
+ // the migration cutover window; removed once legacy-hit logs drain to 0).
413
+ //
414
+ // Per-repo authz: the asset storage layout is workspace-global
415
+ // (`.studiograph/assets/{type}/{id}/{filename}` for local;
416
+ // `{type}/{id}/{filename}` keys for R2) — the path doesn't carry repo
417
+ // information. To enforce that a user with access to folder A can't
418
+ // read assets owned by folder B, we resolve the owning repo from the
419
+ // entity ID via workspaceManager.findEntityById (the same primitive
420
+ // the agent's create_entity uses for cross-repo dedup checks), then
421
+ // run hasRepoAccess against that repo. Both miss and forbid return 404
422
+ // — never leak entity IDs as an existence oracle.
423
+ //
424
+ // Stage 2 (this commit): the route is now the single chokepoint for
425
+ // both backends. Local files stream directly. R2-backed assets get a
426
+ // 302 redirect to a short-lived pre-signed URL, so the underlying
427
+ // bucket can be flipped to private without breaking the workflow.
428
+ // Both GET and HEAD are registered — link unfurlers, OpenGraph
429
+ // crawlers, and CDN healthchecks issue HEAD against this URL and
430
+ // expect the same 302 (signed for HEAD) or local headers.
431
+ fastify.route({
432
+ method: ['GET', 'HEAD'],
433
+ url: '/api/assets/:type/:id/:filename',
434
+ handler: async (req, reply) => {
435
+ const { type, id, filename } = req.params;
436
+ const isHead = req.method === 'HEAD';
437
+ // Legacy-scheme hit. Monitored during cutover: once this drains to 0
438
+ // (all bodies migrated to /api/assets/{assetId}/...), this route is
439
+ // removed. Tagged so it's greppable in logs / metrics.
440
+ req.log?.info?.({ type, id, filename }, 'asset legacy-scheme route hit');
441
+ // Legacy keys carry NO repo identity, and entity IDs are unique within
442
+ // (repo, type) but NOT across the workspace — a case-study in `portfolio`
443
+ // and a reference in `analogous-experiences` can share an id. The old
444
+ // first-match `findEntityByTypeAndId` would authorize against an arbitrary
445
+ // owner, so on a slug collision a request could be authorized via an
446
+ // accessible repo yet serve a *different* repo's blob (codex P1). FAIL
447
+ // CLOSED: resolve every repo that owns (type,id); serve only when exactly
448
+ // one repo owns it AND that repo is accessible. (Immutable assets use the
449
+ // /api/assets/{assetId}/... route and are unaffected.)
450
+ const owners = [];
451
+ for (const cfg of workspaceManager.getAllRepoConfigs()) {
452
+ let g;
453
+ try {
454
+ g = workspaceManager.getGraph(cfg.name);
455
+ }
456
+ catch {
457
+ continue;
458
+ }
459
+ if (g.getEntityFilePath(type, id))
460
+ owners.push(cfg.name);
461
+ }
462
+ if (owners.length !== 1) {
463
+ return reply.status(404).send({ error: 'Asset not found' }); // ambiguous / none → fail closed
464
+ }
465
+ const repoName = owners[0];
466
+ if (!hasRepoAccess(req, repoName, workspaceManager, authService)) {
467
+ return reply.status(404).send({ error: 'Asset not found' });
468
+ }
469
+ let graph;
470
+ try {
471
+ graph = workspaceManager.getGraph(repoName);
472
+ }
473
+ catch {
474
+ return reply.status(404).send({ error: 'Asset not found' });
475
+ }
476
+ // R2 backend: 302 to a short-lived pre-signed URL. Cache the 302
477
+ // for the lifetime of the signed URL (4 hours) — this is the
478
+ // load-bearing perf optimization. Signed URLs are unique per
479
+ // request (each carries its own X-Amz-Signature), so without
480
+ // caching the redirect, every <img> tag fires a fresh proxy
481
+ // roundtrip AND a fresh R2 download (the signed URL is different
482
+ // each time, so the browser's image cache miss-matches and
483
+ // re-downloads bytes it already has). Caching the 302 means
484
+ // subsequent requests in the same 4-hour window resolve to the
485
+ // SAME signed URL → cached image bytes → zero network. Single
486
+ // line, multi-second page-load improvement.
487
+ //
488
+ // Trade-off: a signed URL cached in the browser keeps working
489
+ // for up to 4 hours after the user loses folder access. Same
490
+ // threat model the 4-hour signed-URL TTL already accepted —
491
+ // fresh URLs lived 4 hours, now cached URLs do too. `private`
492
+ // keeps shared caches (CDNs, corporate proxies) from holding
493
+ // the 302; only the originating browser does.
494
+ //
495
+ // Pre-signed URLs are method-bound, so HEAD requests need a
496
+ // HEAD-signed URL.
497
+ if (graph.getAssetStorage() === 'r2') {
498
+ try {
499
+ const signed = await graph.getAssetSignedUrl(type, id, filename, undefined, undefined, isHead ? 'HEAD' : 'GET');
500
+ reply.header('Cache-Control', 'private, max-age=14400');
501
+ return reply.redirect(signed, 302);
502
+ }
503
+ catch (err) {
504
+ // Surface SDK / network failures so credential rotation issues,
505
+ // R2 outages, etc. aren't silently presented to users as
506
+ // 'Asset not found'. The user-facing response stays 404 so
507
+ // entity IDs aren't leaked as an existence oracle.
508
+ req.log?.warn?.({ err, type, id, filename }, 'asset r2 signed-url generation failed');
509
+ return reply.status(404).send({ error: 'Asset not found' });
510
+ }
511
+ }
512
+ // Local backend: stream the file directly. Defense-in-depth — the
513
+ // R2 branch already validates filename inside AssetService, so
514
+ // hold the local branch to the same standard. Fastify's segment
515
+ // matcher blocks `/` in path params, but a literal `..` or hidden
516
+ // dotfile would otherwise be passed through to `join` here.
517
+ if (filename.includes('..') || filename.includes('/') || filename.includes('\\') || filename.startsWith('.') ||
518
+ id.includes('..') || id.includes('/') || id.includes('\\') ||
519
+ type.includes('..') || type.includes('/') || type.includes('\\')) {
520
+ return reply.status(404).send({ error: 'Asset not found' });
521
+ }
522
+ const workspacePath = workspaceManager.workspacePath ?? process.cwd();
523
+ const filePath = join(workspacePath, '.studiograph', 'assets', type, id, filename);
524
+ if (!existsSync(filePath)) {
525
+ return reply.status(404).send({ error: 'Asset not found' });
526
+ }
527
+ const ext = extname(filename).toLowerCase();
528
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
529
+ const stats = statSync(filePath);
530
+ reply.header('Content-Type', contentType);
531
+ reply.header('Content-Length', stats.size);
532
+ reply.header('Cache-Control', 'public, max-age=31536000, immutable');
533
+ // HEAD: send headers without a body. Fastify auto-strips body on
534
+ // HEAD, but being explicit keeps the file handle from opening.
535
+ if (isHead)
536
+ return reply.send();
537
+ return reply.send(createReadStream(filePath));
538
+ },
539
+ });
540
+ // GET /api/repos/:repo/assets — list all assets in a folder. The listing
541
+ // logic (backend walk + immutable-index merge + name resolution) lives in
542
+ // listRepoAssets() so the MCP `list_assets` tool returns an identical shape.
86
543
  fastify.get('/api/repos/:repo/assets', async (req, reply) => {
87
544
  const { repo } = req.params;
88
545
  if (!hasRepoAccess(req, repo, workspaceManager, authService)) {
89
- return reply.status(404).send({ error: `Collection "${repo}" not found` });
546
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
90
547
  }
91
- let graph;
548
+ // Verify the repo exists before delegating; listRepoAssets swallows
549
+ // missing-graph errors as [], which is right for the bulk endpoint but
550
+ // wrong for the single-repo one (we want a 404).
92
551
  try {
93
- graph = workspaceManager.getGraph(repo);
552
+ workspaceManager.getGraph(repo);
94
553
  }
95
554
  catch (err) {
96
555
  return reply.status(404).send({ error: err.message });
97
556
  }
98
- const entities = graph.list();
99
- const allAssets = [];
100
- for (const entity of entities) {
557
+ return { assets: await listRepoAssets(repo, workspaceManager) };
558
+ });
559
+ // GET /api/workspace/assets — bulk asset lists for ALL accessible repos in
560
+ // one round-trip. Replaces 15+ parallel /api/repos/:repo/assets calls so
561
+ // GalleryView's thumbnail effect doesn't saturate the browser HTTP/1.1
562
+ // 6-slot queue and block other navigation fetches.
563
+ // Response shape: `{ [repoName]: RepoAsset[] }`.
564
+ fastify.get('/api/workspace/assets', async (req, reply) => {
565
+ const user = req.user;
566
+ // Use the shared access-control helper so personal-folder restrictions
567
+ // and workspace-owner-on-shared-folder semantics apply consistently.
568
+ const accessible = access.getAccessibleRepos(user, workspaceManager, authService).map(r => r.name);
569
+ // One bucket walk for the whole workspace (see listWorkspaceAssets) rather
570
+ // than a per-repo listAll fan-out. Seed every accessible repo so folders
571
+ // with no assets still appear as empty arrays (shape-stable for callers).
572
+ const result = {};
573
+ for (const repo of accessible)
574
+ result[repo] = [];
575
+ const flat = await listWorkspaceAssets(accessible, workspaceManager);
576
+ for (const { repo, ...asset } of flat)
577
+ (result[repo] ??= []).push(asset);
578
+ return reply.send(result);
579
+ });
580
+ // GET /api/workspace/assets/manager — the dedicated asset-manager view.
581
+ //
582
+ // Distinct from /api/workspace/assets (kept shape-stable for GalleryView's
583
+ // thumbnail effect). This composes the SAME primitives (getAccessibleRepos +
584
+ // listRepoAssets + assetDeleteDenial) into the shape the /assets UI needs:
585
+ // - `folders`: every accessible folder's metadata (display name +
586
+ // workspace_wide + system flags) so the client can render the folder
587
+ // filter, group headers, the Workspace/folder-scoped visibility badge,
588
+ // and pin the shared store first — the system store is excluded from
589
+ // /api/repos so the client can't get this elsewhere.
590
+ // - `assets`: a FLAT list annotated with the owning `repo` and a per-asset
591
+ // `canDelete` decision (uploader/owner gate, via the same authz primitive
592
+ // the DELETE route enforces) so the UI can disable delete with a reason
593
+ // instead of letting the user hit a 403.
594
+ fastify.get('/api/workspace/assets/manager', async (req, reply) => {
595
+ const user = req.user;
596
+ workspaceManager.reloadConfig();
597
+ const accessible = access.getAccessibleRepos(user, workspaceManager, authService);
598
+ const index = workspaceManager.getAssetIndex?.();
599
+ const folders = accessible.map(r => ({
600
+ repo: r.name,
601
+ displayName: r.display_name ?? null,
602
+ workspaceWide: r.workspace_wide === true,
603
+ system: r.system === true,
604
+ }));
605
+ const configByRepo = new Map(accessible.map(r => [r.name, r]));
606
+ // listWorkspaceAssets does ONE bucket walk for the whole workspace (instead
607
+ // of one per folder) and already dedupes legacy/immutable pairs per repo.
608
+ // Here we just layer on the per-asset delete decision so the UI can disable
609
+ // delete with a reason instead of letting the user hit a 403.
610
+ const flat = await listWorkspaceAssets(accessible.map(r => r.name), workspaceManager);
611
+ const assets = flat.map((a) => {
612
+ const r = configByRepo.get(a.repo);
613
+ // Index-tracked assets (incl. free-standing folder/workspace-owned ones)
614
+ // carry an assetId — gate per-asset on that id. Legacy un-indexed blobs
615
+ // fall back to the entity-coordinate gate, keyed to the exact filename.
616
+ const denial = a.assetId
617
+ ? assetDeleteDenialByAssetId(index, r, user, a.assetId)
618
+ : assetDeleteDenial(index, r, user, a.repo, a.entityType, a.entityId, a.filename);
619
+ return { ...a, canDelete: denial === null, denialReason: denial };
620
+ });
621
+ return reply.send({ folders, assets });
622
+ });
623
+ // GET /api/workspace/assets/:assetId/references — entries whose markdown
624
+ // body (or frontmatter) references this asset's URL. Scans accessible
625
+ // repos via graph.search, which already includes body-content scanning,
626
+ // so no schema change is needed. Filters self-reference: a STANDALONE
627
+ // asset's own wrapper entity has the URL in its `asset_url` frontmatter
628
+ // and would self-match — drop it. Embedded assets keep their parent
629
+ // entity in results (the parent IS the only "use", and clicking it is
630
+ // the user's path to managing the image's context).
631
+ fastify.get('/api/workspace/assets/:assetId/references', async (req, reply) => {
632
+ const { assetId } = req.params;
633
+ const user = req.user;
634
+ const index = workspaceManager.getAssetIndex?.();
635
+ const owner = index?.get(assetId) ?? null;
636
+ const accessible = access.getAccessibleRepos(user, workspaceManager, authService);
637
+ // The immutable-asset URL pattern. assetId is a UUID so this string
638
+ // is unique enough that a substring match across markdown bodies is
639
+ // a reliable "this entry references the asset" signal.
640
+ const pattern = `/api/assets/${assetId}/`;
641
+ const references = [];
642
+ for (const repo of accessible) {
643
+ let graph;
101
644
  try {
102
- const assets = await graph.listAssets(entity.entityType, entity.id);
103
- for (const asset of assets) {
104
- const url = asset.url.startsWith('file://') || !asset.url.startsWith('http')
105
- ? `/api/assets/${entity.entityType}/${entity.id}/${asset.filename}`
106
- : asset.path;
107
- allAssets.push({
108
- filename: asset.filename,
109
- size: asset.size,
110
- contentType: asset.contentType,
111
- url,
112
- entityType: entity.entityType,
113
- entityId: entity.id,
114
- entityName: entity.data.name ?? entity.id,
115
- mediaType: asset.mediaType,
116
- });
117
- }
645
+ graph = workspaceManager.getGraph(repo.name);
646
+ }
647
+ catch {
648
+ continue;
649
+ }
650
+ const matches = graph.search({ query: pattern });
651
+ for (const e of matches) {
652
+ // Skip self: a standalone asset's own entity holds the URL in
653
+ // its `asset_url` frontmatter so it always matches its own
654
+ // search. Embedded assets fall through (their owner is the
655
+ // parent entry, which IS a legitimate reference to show).
656
+ if (owner &&
657
+ owner.ownerType === 'asset' &&
658
+ owner.repo === repo.name &&
659
+ e.id === owner.ownerId)
660
+ continue;
661
+ const data = e.data;
662
+ const title = typeof data.name === 'string' && data.name.trim() !== ''
663
+ ? data.name
664
+ : e.id;
665
+ references.push({
666
+ repo: repo.name,
667
+ entityType: e.entityType,
668
+ entityId: e.id,
669
+ title,
670
+ });
118
671
  }
119
- catch { /* no assets or service not configured */ }
120
672
  }
121
- return { assets: allAssets };
673
+ return reply.send({ references });
674
+ });
675
+ // GET /api/workspace/assets/store — resolve (lazy-create) the reserved
676
+ // Workspace Assets store and return its repo name. The manager's upload
677
+ // affordance calls this, then POSTs the file to /api/repos/<repo>/assets.
678
+ // This is how an existing workspace backfills the store on first use (D3 /
679
+ // resolveWorkspaceAssetsRepo) — never a boot sweep. Authed users only; the
680
+ // store is workspace_wide so any member can upload once it exists.
681
+ fastify.get('/api/workspace/assets/store', async (_req, reply) => {
682
+ // Match the upload route's auth posture: open-mode (no user) is allowed.
683
+ const workspace = new Workspace(workspaceManager.getWorkspacePath());
684
+ const repo = await resolveWorkspaceAssetsRepo(workspace, workspaceManager);
685
+ return reply.send({ repo });
122
686
  });
123
687
  // DELETE /api/repos/:repo/entities/:type/:id/assets/:filename — delete an asset
124
688
  fastify.delete('/api/repos/:repo/entities/:type/:id/assets/:filename', async (req, reply) => {
125
689
  const { repo, type, id, filename } = req.params;
126
690
  if (!hasRepoAccess(req, repo, workspaceManager, authService)) {
127
- return reply.status(404).send({ error: `Collection "${repo}" not found` });
691
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
128
692
  }
693
+ // Workspace Assets store delete gate (codex P1 / D2): workspace_wide grants
694
+ // every member write, so this filename delete needs the same uploader/owner
695
+ // gate the entity-delete route applies — otherwise a direct API call deletes
696
+ // a shared/legacy file asset the manager already marks canDelete:false.
697
+ const denial = assetDeleteDenial(workspaceManager.getAssetIndex?.(), workspaceManager.getRepoConfig(repo), req.user, repo, type, id, filename);
698
+ if (denial)
699
+ return reply.status(403).send({ error: denial });
129
700
  let graph;
130
701
  try {
131
702
  graph = workspaceManager.getGraph(repo);
@@ -135,11 +706,140 @@ export async function registerAssetApiRoutes(fastify, workspaceManager, authServ
135
706
  }
136
707
  try {
137
708
  await graph.deleteAsset(type, id, filename);
709
+ // A legacy blob just disappeared — drop the cached bucket walk so the
710
+ // manager stops listing it before the next process restart (P2).
711
+ clearLegacyBucketCache();
712
+ return { success: true };
713
+ }
714
+ catch (err) {
715
+ return reply.status(404).send({ error: err.message });
716
+ }
717
+ });
718
+ // DELETE /api/repos/:repo/assets/:assetId/:filename — delete a free-standing
719
+ // (folder/workspace-owned) asset by its immutable id. Folder-owned assets
720
+ // have no owning entity, so the entity-coordinate route above can't address
721
+ // them (it rejects 'folder' as an EntityType). This removes the blob + index
722
+ // row in one step.
723
+ fastify.delete('/api/repos/:repo/assets/:assetId/:filename', async (req, reply) => {
724
+ const { repo, assetId, filename } = req.params;
725
+ if (!hasRepoAccess(req, repo, workspaceManager, authService)) {
726
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
727
+ }
728
+ // Confirm the asset exists and actually belongs to this folder — don't let
729
+ // access to one folder delete another folder's asset by id.
730
+ const index = workspaceManager.getAssetIndex?.();
731
+ const row = index?.get(assetId);
732
+ if (!row || row.repo !== repo) {
733
+ return reply.status(404).send({ error: 'Asset not found' });
734
+ }
735
+ // Same uploader/owner gate as the filename route, keyed on the asset row.
736
+ const denial = assetDeleteDenialByAssetId(index, workspaceManager.getRepoConfig(repo), req.user, assetId);
737
+ if (denial)
738
+ return reply.status(403).send({ error: denial });
739
+ // Filename must match the stored row — the key is `{assetId}/{filename}`.
740
+ if (row.filename !== filename) {
741
+ return reply.status(404).send({ error: 'Asset not found' });
742
+ }
743
+ let graph;
744
+ try {
745
+ graph = workspaceManager.getGraph(repo);
746
+ }
747
+ catch (err) {
748
+ return reply.status(404).send({ error: err.message });
749
+ }
750
+ try {
751
+ await graph.deleteAssetById(assetId, filename);
138
752
  return { success: true };
139
753
  }
140
754
  catch (err) {
141
755
  return reply.status(404).send({ error: err.message });
142
756
  }
143
757
  });
758
+ // POST /api/repos/:repo/assets/:assetId/create-entry — the second import
759
+ // "door": create a typed entity (auto-derived type, re-extracted body) that
760
+ // embeds an EXISTING free-standing asset, in the asset's own folder. Makes
761
+ // deletion reversible — a surviving asset can be re-embedded into a new entry.
762
+ fastify.post('/api/repos/:repo/assets/:assetId/create-entry', async (req, reply) => {
763
+ const { repo, assetId } = req.params;
764
+ const user = req.user;
765
+ // Creating an entry is a write — require folder write access.
766
+ if (!access.canWriteRepo(user, repo, workspaceManager, authService)) {
767
+ return reply.status(403).send({ error: 'Editor access required to create an entry here.' });
768
+ }
769
+ const index = workspaceManager.getAssetIndex?.();
770
+ const row = index?.get(assetId);
771
+ if (!row || row.repo !== repo) {
772
+ return reply.status(404).send({ error: 'Asset not found' });
773
+ }
774
+ // Only free-standing assets get the second import door (parity with the
775
+ // delete-by-id + move routes). Re-embedding an entity-OWNED asset would
776
+ // leave the new entry's embed deletable by the owning entity's
777
+ // cleanupOwnedAssets — so refuse it here, not just in the UI.
778
+ if (!isFreeStandingOwner(row.ownerType)) {
779
+ return reply.status(400).send({ error: 'Only free-standing assets can be made into a new entry.' });
780
+ }
781
+ let graph;
782
+ try {
783
+ graph = workspaceManager.getGraph(repo);
784
+ }
785
+ catch (err) {
786
+ return reply.status(404).send({ error: err.message });
787
+ }
788
+ try {
789
+ const result = await createEntityFromAsset({
790
+ graph,
791
+ assetId,
792
+ name: req.body?.name,
793
+ user: reqGitUser(req),
794
+ });
795
+ return { entities: result.entities, assetId: result.assetId, asset_url: result.asset_url };
796
+ }
797
+ catch (err) {
798
+ return reply.status(400).send({ error: err.message ?? 'Could not create entry from asset' });
799
+ }
800
+ });
801
+ // POST /api/repos/:repo/assets/:assetId/move — relocate a free-standing asset
802
+ // to another folder. Index-only (the blob key is repo-independent). Requires
803
+ // write access to BOTH the source and target folders; only free-standing
804
+ // (folder/workspace-owned) assets can move — entity-embedded assets move with
805
+ // their owning entry.
806
+ fastify.post('/api/repos/:repo/assets/:assetId/move', async (req, reply) => {
807
+ const { repo, assetId } = req.params;
808
+ const targetRepo = req.body?.targetRepo;
809
+ const user = req.user;
810
+ if (!targetRepo)
811
+ return reply.status(400).send({ error: 'targetRepo is required' });
812
+ if (!access.canWriteRepo(user, repo, workspaceManager, authService)) {
813
+ return reply.status(404).send({ error: `Folder "${repo}" not found` });
814
+ }
815
+ if (!access.canWriteRepo(user, targetRepo, workspaceManager, authService)) {
816
+ return reply.status(403).send({ error: `Editor access required to move into "${targetRepo}".` });
817
+ }
818
+ const index = workspaceManager.getAssetIndex?.();
819
+ const row = index?.get(assetId);
820
+ if (!index || !row || row.repo !== repo) {
821
+ return reply.status(404).send({ error: 'Asset not found' });
822
+ }
823
+ if (!isFreeStandingOwner(row.ownerType)) {
824
+ return reply.status(400).send({ error: 'Only free-standing assets can be moved on their own.' });
825
+ }
826
+ // Moving OUT of a workspace_wide store reassigns the asset's repo, after
827
+ // which the tighter uploader/owner delete rule no longer applies — so the
828
+ // move itself must honor that rule (mirrors the entity-move guard in
829
+ // graph-api). A no-op for non-workspace_wide sources, so normal
830
+ // folder→folder moves are unaffected.
831
+ const denial = assetDeleteDenialByAssetId(index, workspaceManager.getRepoConfig(repo), user, assetId);
832
+ if (denial)
833
+ return reply.status(403).send({ error: denial });
834
+ if (targetRepo === repo)
835
+ return { success: true }; // no-op
836
+ // Honor the result — a concurrent delete/move could have removed the row
837
+ // between the get() above and here; reporting success then would make the
838
+ // asset appear moved when it wasn't.
839
+ if (!index.moveByAssetId(assetId, targetRepo)) {
840
+ return reply.status(404).send({ error: 'Asset not found' });
841
+ }
842
+ return { success: true };
843
+ });
144
844
  }
145
845
  //# sourceMappingURL=asset-api.js.map