@anchrd/intel 0.59.0

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 (364) hide show
  1. package/README.md +496 -0
  2. package/bin/intel.mjs +134 -0
  3. package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
  4. package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
  5. package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
  6. package/dist/adapters/cloudflare/cloudflare.js +416 -0
  7. package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
  8. package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
  9. package/dist/adapters/content/content.d.ts +3 -0
  10. package/dist/adapters/content/content.js +128 -0
  11. package/dist/adapters/content/content.types.d.ts +38 -0
  12. package/dist/adapters/content/content.types.js +1 -0
  13. package/dist/adapters/db/db-audit.d.ts +62 -0
  14. package/dist/adapters/db/db-audit.js +150 -0
  15. package/dist/adapters/db/db-boards.d.ts +6 -0
  16. package/dist/adapters/db/db-boards.js +324 -0
  17. package/dist/adapters/db/db-feed.d.ts +30 -0
  18. package/dist/adapters/db/db-feed.js +276 -0
  19. package/dist/adapters/db/db-flows.d.ts +42 -0
  20. package/dist/adapters/db/db-flows.js +1311 -0
  21. package/dist/adapters/db/db-grants.d.ts +133 -0
  22. package/dist/adapters/db/db-grants.js +269 -0
  23. package/dist/adapters/db/db-indexing.d.ts +3 -0
  24. package/dist/adapters/db/db-indexing.js +200 -0
  25. package/dist/adapters/db/db-oauth.d.ts +3 -0
  26. package/dist/adapters/db/db-oauth.js +26 -0
  27. package/dist/adapters/db/db-prompts.d.ts +46 -0
  28. package/dist/adapters/db/db-prompts.js +144 -0
  29. package/dist/adapters/db/db.d.ts +6 -0
  30. package/dist/adapters/db/db.js +1687 -0
  31. package/dist/adapters/db/db.types.d.ts +16 -0
  32. package/dist/adapters/db/db.types.js +1 -0
  33. package/dist/adapters/document-converter/document-converter.d.ts +2 -0
  34. package/dist/adapters/document-converter/document-converter.js +26 -0
  35. package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
  36. package/dist/adapters/document-converter/document-converter.types.js +1 -0
  37. package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
  38. package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
  39. package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
  40. package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
  41. package/dist/adapters/index-queue/index-queue.d.ts +10 -0
  42. package/dist/adapters/index-queue/index-queue.js +12 -0
  43. package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
  44. package/dist/adapters/index-queue/index-queue.types.js +1 -0
  45. package/dist/adapters/json-schema/json-schema.d.ts +2 -0
  46. package/dist/adapters/json-schema/json-schema.js +77 -0
  47. package/dist/adapters/openid/openid.d.ts +5 -0
  48. package/dist/adapters/openid/openid.js +301 -0
  49. package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
  50. package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
  51. package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
  52. package/dist/adapters/remote-tools/remote-tools.js +129 -0
  53. package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
  54. package/dist/adapters/semantic-index/semantic-index.js +139 -0
  55. package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
  56. package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
  57. package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
  58. package/dist/adapters/session-cookie/session-cookie.js +87 -0
  59. package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
  60. package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
  61. package/dist/audit/audit.d.ts +2 -0
  62. package/dist/audit/audit.js +87 -0
  63. package/dist/audit/audit.types.d.ts +23 -0
  64. package/dist/audit/audit.types.js +1 -0
  65. package/dist/auth/auth.d.ts +2 -0
  66. package/dist/auth/auth.js +284 -0
  67. package/dist/auth/auth.types.d.ts +108 -0
  68. package/dist/auth/auth.types.js +1 -0
  69. package/dist/boards/boards.d.ts +44 -0
  70. package/dist/boards/boards.js +447 -0
  71. package/dist/boards/boards.types.d.ts +117 -0
  72. package/dist/boards/boards.types.js +1 -0
  73. package/dist/build/build.d.ts +2 -0
  74. package/dist/build/build.js +192 -0
  75. package/dist/build/build.types.d.ts +17 -0
  76. package/dist/build/build.types.js +1 -0
  77. package/dist/bundle/bundle.d.ts +4 -0
  78. package/dist/bundle/bundle.js +1194 -0
  79. package/dist/bundle/bundle.types.d.ts +33 -0
  80. package/dist/bundle/bundle.types.js +1 -0
  81. package/dist/cli/cli.d.ts +4 -0
  82. package/dist/cli/cli.js +262 -0
  83. package/dist/cli/cli.types.d.ts +14 -0
  84. package/dist/cli/cli.types.js +1 -0
  85. package/dist/contract/audit.d.ts +50 -0
  86. package/dist/contract/audit.js +72 -0
  87. package/dist/contract/board.d.ts +150 -0
  88. package/dist/contract/board.js +262 -0
  89. package/dist/contract/bundle.d.ts +147 -0
  90. package/dist/contract/bundle.js +139 -0
  91. package/dist/contract/contract.d.ts +112 -0
  92. package/dist/contract/contract.js +143 -0
  93. package/dist/contract/feed.d.ts +126 -0
  94. package/dist/contract/feed.js +149 -0
  95. package/dist/contract/flow-run.d.ts +346 -0
  96. package/dist/contract/flow-run.js +194 -0
  97. package/dist/contract/flow.d.ts +1057 -0
  98. package/dist/contract/flow.js +552 -0
  99. package/dist/contract/node.d.ts +551 -0
  100. package/dist/contract/node.js +456 -0
  101. package/dist/contract/share.d.ts +216 -0
  102. package/dist/contract/share.js +134 -0
  103. package/dist/contract/table.d.ts +171 -0
  104. package/dist/contract/table.js +117 -0
  105. package/dist/contract/tool.d.ts +122 -0
  106. package/dist/contract/tool.js +172 -0
  107. package/dist/feed/feed.d.ts +2 -0
  108. package/dist/feed/feed.js +77 -0
  109. package/dist/feed/feed.types.d.ts +23 -0
  110. package/dist/feed/feed.types.js +1 -0
  111. package/dist/flows/flows.d.ts +41 -0
  112. package/dist/flows/flows.js +2216 -0
  113. package/dist/flows/flows.types.d.ts +339 -0
  114. package/dist/flows/flows.types.js +1 -0
  115. package/dist/http/http.d.ts +9 -0
  116. package/dist/http/http.js +874 -0
  117. package/dist/http/http.types.d.ts +22 -0
  118. package/dist/http/http.types.js +1 -0
  119. package/dist/indexing/indexing.d.ts +6 -0
  120. package/dist/indexing/indexing.js +239 -0
  121. package/dist/indexing/indexing.types.d.ts +11 -0
  122. package/dist/indexing/indexing.types.js +1 -0
  123. package/dist/intel/intel.d.ts +3 -0
  124. package/dist/intel/intel.js +194 -0
  125. package/dist/intel/intel.types.d.ts +24 -0
  126. package/dist/intel/intel.types.js +1 -0
  127. package/dist/mcp/mcp.d.ts +2 -0
  128. package/dist/mcp/mcp.js +1292 -0
  129. package/dist/mcp/mcp.types.d.ts +25 -0
  130. package/dist/mcp/mcp.types.js +1 -0
  131. package/dist/nodes/document-links/document-links.d.ts +20 -0
  132. package/dist/nodes/document-links/document-links.js +57 -0
  133. package/dist/nodes/nodes.d.ts +2 -0
  134. package/dist/nodes/nodes.js +1667 -0
  135. package/dist/nodes/nodes.types.d.ts +511 -0
  136. package/dist/nodes/nodes.types.js +1 -0
  137. package/dist/prepare/prepare.d.ts +4 -0
  138. package/dist/prepare/prepare.js +16 -0
  139. package/dist/prepare/prepare.types.d.ts +9 -0
  140. package/dist/prepare/prepare.types.js +1 -0
  141. package/dist/prompts/prompts.d.ts +2 -0
  142. package/dist/prompts/prompts.js +65 -0
  143. package/dist/prompts/prompts.types.d.ts +71 -0
  144. package/dist/prompts/prompts.types.js +1 -0
  145. package/dist/shared/callers/callers.d.ts +28 -0
  146. package/dist/shared/callers/callers.js +30 -0
  147. package/dist/shared/csv/csv.d.ts +13 -0
  148. package/dist/shared/csv/csv.js +85 -0
  149. package/dist/shared/document-text/document-text.d.ts +21 -0
  150. package/dist/shared/document-text/document-text.js +31 -0
  151. package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
  152. package/dist/shared/gate-authorization/gate-authorization.js +26 -0
  153. package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
  154. package/dist/shared/grant-expiry/grant-expiry.js +26 -0
  155. package/dist/shared/intel-error/intel-error.d.ts +22 -0
  156. package/dist/shared/intel-error/intel-error.js +27 -0
  157. package/dist/shared/plain-title/plain-title.d.ts +16 -0
  158. package/dist/shared/plain-title/plain-title.js +45 -0
  159. package/dist/shared/problem-details/problem-details.d.ts +30 -0
  160. package/dist/shared/problem-details/problem-details.js +18 -0
  161. package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
  162. package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
  163. package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
  164. package/dist/shared/safe-return-path/safe-return-path.js +11 -0
  165. package/dist/shared/sha256/sha256.d.ts +2 -0
  166. package/dist/shared/sha256/sha256.js +14 -0
  167. package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
  168. package/dist/tools/tool-servers/tool-servers.js +177 -0
  169. package/dist/tools/tools.d.ts +2 -0
  170. package/dist/tools/tools.js +207 -0
  171. package/dist/tools/tools.types.d.ts +56 -0
  172. package/dist/tools/tools.types.js +1 -0
  173. package/examples/branding/favicon.svg +5 -0
  174. package/examples/branding/logo.svg +5 -0
  175. package/examples/branding/theme.css +16 -0
  176. package/examples/dev.vars.example +7 -0
  177. package/examples/intel.json +9 -0
  178. package/migrations/0000_intel_foundation.sql +203 -0
  179. package/migrations/0001_portal_native_tools.sql +21 -0
  180. package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
  181. package/migrations/0003_folder_permissions.sql +211 -0
  182. package/migrations/0004_subflow_runs.sql +14 -0
  183. package/migrations/0005_flow_node_cleanup.sql +28 -0
  184. package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
  185. package/migrations/0006_links_are_written_in_the_text.sql +20 -0
  186. package/migrations/0007_no_node_waits.sql +69 -0
  187. package/migrations/0008_three_layers.sql +130 -0
  188. package/migrations/0009_no_context_policy.sql +48 -0
  189. package/migrations/0010_no_run_waits_a_year.sql +27 -0
  190. package/migrations/0011_one_name_for_the_tree.sql +53 -0
  191. package/migrations/0012_table_snapshots.sql +29 -0
  192. package/migrations/0017_a_vector_per_card.sql +38 -0
  193. package/migrations/0018_no_context_policy_at_last.sql +97 -0
  194. package/migrations/0019_one_name_for_the_grants.sql +52 -0
  195. package/migrations/0020_cascade_purge_replay.sql +11 -0
  196. package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
  197. package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
  198. package/migrations/0023_a_board_and_its_tasks.sql +116 -0
  199. package/migrations/0024_the_archive_is_a_column.sql +33 -0
  200. package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
  201. package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
  202. package/migrations/0027_the_runs_of_every_flow.sql +27 -0
  203. package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
  204. package/package.json +118 -0
  205. package/src/contract/CLAUDE.md +152 -0
  206. package/src/contract/README.md +102 -0
  207. package/src/contract/audit.ts +85 -0
  208. package/src/contract/board.ts +306 -0
  209. package/src/contract/bundle.ts +157 -0
  210. package/src/contract/contract.ts +161 -0
  211. package/src/contract/feed.ts +165 -0
  212. package/src/contract/flow-run.ts +241 -0
  213. package/src/contract/flow.ts +675 -0
  214. package/src/contract/node.ts +583 -0
  215. package/src/contract/share.ts +196 -0
  216. package/src/contract/table.ts +178 -0
  217. package/src/contract/tool.ts +202 -0
  218. package/ui/README.md +110 -0
  219. package/ui/components.json +21 -0
  220. package/ui/index.html +13 -0
  221. package/ui/package.json +11 -0
  222. package/ui/scripts/lint-tokens.mjs +187 -0
  223. package/ui/src/access-summary/access-summary.tsx +385 -0
  224. package/ui/src/app/action-slot/action-slot.tsx +27 -0
  225. package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
  226. package/ui/src/app/app-tree/app-tree.tsx +1175 -0
  227. package/ui/src/app/app.tsx +87 -0
  228. package/ui/src/app/header-search/header-search.tsx +294 -0
  229. package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
  230. package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
  231. package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
  232. package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
  233. package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
  234. package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
  235. package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
  236. package/ui/src/app/tree-move/tree-move.tsx +427 -0
  237. package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
  238. package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
  239. package/ui/src/app/user-footer/user-footer.tsx +188 -0
  240. package/ui/src/app/view-menu/view-menu.tsx +115 -0
  241. package/ui/src/app-root/app-root.tsx +109 -0
  242. package/ui/src/app-root/app-root.types.ts +27 -0
  243. package/ui/src/archive/archive.tsx +417 -0
  244. package/ui/src/attachment-detail/attachment-detail.ts +52 -0
  245. package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
  246. package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
  247. package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
  248. package/ui/src/board/board-assignee/board-assignee.ts +48 -0
  249. package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
  250. package/ui/src/board/board-chip/board-chip.tsx +46 -0
  251. package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
  252. package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
  253. package/ui/src/board/board-data/board-data.ts +195 -0
  254. package/ui/src/board/board-data/board-data.types.ts +38 -0
  255. package/ui/src/board/board-dates/board-dates.ts +40 -0
  256. package/ui/src/board/board-dates/board-dates.tsx +105 -0
  257. package/ui/src/board/board-kanban/board-kanban.ts +162 -0
  258. package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
  259. package/ui/src/board/board-open-task/board-open-task.ts +32 -0
  260. package/ui/src/board/board-panel/board-panel.tsx +259 -0
  261. package/ui/src/board/board-settings/board-settings.tsx +226 -0
  262. package/ui/src/board/board-status/board-status.tsx +60 -0
  263. package/ui/src/board/board-stripes/board-stripes.ts +128 -0
  264. package/ui/src/board/board-table/board-table.ts +53 -0
  265. package/ui/src/board/board-table/board-table.tsx +610 -0
  266. package/ui/src/board/board-task/board-task.ts +108 -0
  267. package/ui/src/board/board-task/board-task.tsx +335 -0
  268. package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
  269. package/ui/src/branding/branding.tsx +17 -0
  270. package/ui/src/branding/custom-logo.ts +1 -0
  271. package/ui/src/branding/favicon.default.svg +5 -0
  272. package/ui/src/branding/favicon.svg +5 -0
  273. package/ui/src/components/ui/avatar.tsx +39 -0
  274. package/ui/src/components/ui/button.tsx +64 -0
  275. package/ui/src/components/ui/collapsible.tsx +20 -0
  276. package/ui/src/components/ui/command.tsx +160 -0
  277. package/ui/src/components/ui/dialog.tsx +143 -0
  278. package/ui/src/components/ui/dropdown-menu.tsx +187 -0
  279. package/ui/src/components/ui/input.tsx +21 -0
  280. package/ui/src/components/ui/popover.tsx +41 -0
  281. package/ui/src/components/ui/select.tsx +163 -0
  282. package/ui/src/components/ui/separator.tsx +26 -0
  283. package/ui/src/components/ui/sheet.tsx +136 -0
  284. package/ui/src/components/ui/sidebar.tsx +693 -0
  285. package/ui/src/components/ui/skeleton.tsx +13 -0
  286. package/ui/src/components/ui/switch.tsx +25 -0
  287. package/ui/src/components/ui/table.tsx +82 -0
  288. package/ui/src/components/ui/tabs.tsx +52 -0
  289. package/ui/src/components/ui/tooltip.tsx +51 -0
  290. package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
  291. package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
  292. package/ui/src/data/query-client.ts +18 -0
  293. package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
  294. package/ui/src/data/request-refusal/request-refusal.ts +51 -0
  295. package/ui/src/data/sign-in/sign-in.ts +97 -0
  296. package/ui/src/data/sign-in/sign-in.types.ts +15 -0
  297. package/ui/src/document-link/document-link.tsx +114 -0
  298. package/ui/src/document-markdown/document-markdown.ts +195 -0
  299. package/ui/src/editor-schema/editor-schema.ts +23 -0
  300. package/ui/src/entry-picker/entry-picker.tsx +238 -0
  301. package/ui/src/feed/feed.tsx +344 -0
  302. package/ui/src/file-preview/file-preview-view.tsx +496 -0
  303. package/ui/src/file-preview/file-preview.tsx +28 -0
  304. package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
  305. package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
  306. package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
  307. package/ui/src/file-preview/word-file-preview.tsx +5 -0
  308. package/ui/src/flow-runs/flow-runs.tsx +238 -0
  309. package/ui/src/flows/flows.tsx +1646 -0
  310. package/ui/src/flows/node-icon/node-icon.ts +34 -0
  311. package/ui/src/flows/node-palette/node-palette.tsx +214 -0
  312. package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
  313. package/ui/src/folder-contents/folder-contents.tsx +321 -0
  314. package/ui/src/folder-index/folder-index.ts +55 -0
  315. package/ui/src/frontmatter/frontmatter.tsx +193 -0
  316. package/ui/src/graph-pane/graph-pane.tsx +53 -0
  317. package/ui/src/hooks/use-mobile.ts +19 -0
  318. package/ui/src/i18n/custom.json +4 -0
  319. package/ui/src/i18n/de.json +609 -0
  320. package/ui/src/i18n/en.json +609 -0
  321. package/ui/src/i18n/es.json +609 -0
  322. package/ui/src/i18n/i18n-context.tsx +65 -0
  323. package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
  324. package/ui/src/i18n/i18n.ts +32 -0
  325. package/ui/src/i18n/i18n.types.ts +49 -0
  326. package/ui/src/kind-icon.ts +97 -0
  327. package/ui/src/lib/utils.ts +6 -0
  328. package/ui/src/main.tsx +68 -0
  329. package/ui/src/modal/modal.tsx +53 -0
  330. package/ui/src/node-details/node-details.tsx +545 -0
  331. package/ui/src/node-editor/node-editor.tsx +304 -0
  332. package/ui/src/node-graph/graph-notice.tsx +21 -0
  333. package/ui/src/node-graph/node-graph.ts +214 -0
  334. package/ui/src/node-graph/node-graph.tsx +295 -0
  335. package/ui/src/node-import/node-import.tsx +418 -0
  336. package/ui/src/node-save-error.ts +46 -0
  337. package/ui/src/node-source/node-source.tsx +128 -0
  338. package/ui/src/node-table/node-table.tsx +107 -0
  339. package/ui/src/nodes/nodes.tsx +240 -0
  340. package/ui/src/rename-resource/rename-resource.ts +80 -0
  341. package/ui/src/resolved-names/resolved-names.tsx +67 -0
  342. package/ui/src/resource-error.ts +33 -0
  343. package/ui/src/resource-menu/resource-menu.tsx +1156 -0
  344. package/ui/src/router/router-context.ts +6 -0
  345. package/ui/src/router/router.tsx +67 -0
  346. package/ui/src/router/router.types.ts +8 -0
  347. package/ui/src/router/selection-search.ts +88 -0
  348. package/ui/src/save-button/save-button.tsx +94 -0
  349. package/ui/src/section-hint/section-hint.tsx +40 -0
  350. package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
  351. package/ui/src/styles.css +189 -0
  352. package/ui/src/table-columns/table-columns.tsx +379 -0
  353. package/ui/src/theme/custom.css +1 -0
  354. package/ui/src/theme/theme-context.tsx +89 -0
  355. package/ui/src/theme/theme.ts +70 -0
  356. package/ui/src/time/relative-time.tsx +41 -0
  357. package/ui/src/time/time-context.tsx +78 -0
  358. package/ui/src/time/time.ts +165 -0
  359. package/ui/src/title-row/title-row.tsx +185 -0
  360. package/ui/src/tools/tools.tsx +546 -0
  361. package/ui/src/user-name/user-name.ts +61 -0
  362. package/ui/src/verbatim/verbatim.tsx +68 -0
  363. package/ui/tsconfig.json +17 -0
  364. package/ui/vite.config.ts +129 -0
@@ -0,0 +1,71 @@
1
+ import type { NodeDocument } from "../contract/node.js";
2
+ import type { Actor } from "../nodes/nodes.types.js";
3
+ /**
4
+ * One entry of the slash-command catalogue (#775): a document offered as an instruction, or a
5
+ * published flow offered as a way to start one.
6
+ *
7
+ * ⚠️ `kind` is two values and not `NodeKind`. A flow is not a node, and the five node kinds that
8
+ * cannot be offered have no business appearing in this union — a widened type here would be a
9
+ * standing invitation to hand `prompts/list` a folder.
10
+ */
11
+ export interface PromptEntry {
12
+ name: string;
13
+ kind: "document" | "flow";
14
+ targetId: string;
15
+ title: string;
16
+ description: string | null;
17
+ }
18
+ /**
19
+ * Who holds a name, as much of it as the asking actor is entitled to learn.
20
+ *
21
+ * ⚠️ `title` is `null` when the holder exists but this actor may not see it, and that is not the
22
+ * same as "no holder". ADR-0004 §3 is explicit that a refusal must not become a way of reading the
23
+ * tree: naming the document that holds `payroll` would tell somebody without access that a document
24
+ * by that title exists. The caller is told the name is taken — which they must be, or they cannot
25
+ * proceed — and nothing else.
26
+ */
27
+ export interface PromptNameHolder {
28
+ kind: "document" | "flow";
29
+ title: string | null;
30
+ }
31
+ /**
32
+ * Who is asking for the catalogue, and which halves of it they are entitled to at all.
33
+ *
34
+ * ⚠️ **The capability follows the KIND, exactly as it does for `audit_list`** (#774). A prompt
35
+ * entry says the same thing its resource says, so listing it asks what reading that resource asks:
36
+ * `nodes/read` for a document, `flows/read` for a flow. Somebody holding only one of the two gets
37
+ * the half they hold — not an empty list, and not the other half by accident.
38
+ *
39
+ * ⚠️ These are the GATE capabilities and they are the coarse half of the answer. The resource ACL
40
+ * still runs underneath, per row, in the query itself; a caller with `nodes/read` sees the offered
41
+ * documents THEY may read and no others.
42
+ */
43
+ export interface PromptActor extends Actor {
44
+ canReadNodes: boolean;
45
+ canReadFlows: boolean;
46
+ }
47
+ export interface PromptRepository {
48
+ listOffered(actor: PromptActor): Promise<PromptEntry[]>;
49
+ findOffered(actor: PromptActor, name: string): Promise<PromptEntry | null>;
50
+ }
51
+ export interface PromptsDeps {
52
+ repository: PromptRepository;
53
+ /**
54
+ * One document, as the node service reads it: the same authorization walk, the same content
55
+ * store. `prompts/get` does not reach into R2 on its own — a second reader would be a second
56
+ * place for the ACL to be applied, and the one that gets it wrong is the one nobody looks at.
57
+ *
58
+ * ⚠️ **The whole document, not a string.** What is stored under a document node is the editor's
59
+ * payload, and turning that into readable text needs the `mediaType` beside the content. Handing
60
+ * a string in would put that decision in whatever wired this up — in the Cloudflare shell, which
61
+ * is the one place in this package that may not hold business behaviour.
62
+ */
63
+ readDocument(actor: Actor, nodeId: string): Promise<NodeDocument>;
64
+ }
65
+ export interface Prompts {
66
+ list(actor: PromptActor): Promise<PromptEntry[]>;
67
+ get(actor: PromptActor, name: string): Promise<{
68
+ entry: PromptEntry;
69
+ text: string;
70
+ }>;
71
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { NamedOrCounted } from "../../contract/contract.js";
2
+ type Callers = {
3
+ visible: string[];
4
+ hidden: number;
5
+ };
6
+ /**
7
+ * ⚠️ Named where the reader may see it, counted where they may not (ADR-0004 §3). A refusal has to
8
+ * be actionable without becoming a way of reading the tree: whoever holds one grant on one folder
9
+ * must not learn the titles of flows they are refused directly.
10
+ *
11
+ * This is the middle of both refusals that ask the question — narrowing a folder (`nodes.ts`) and
12
+ * deleting a flow for good (`flows.ts`). ⚠️ **The two SENTENCES around it stay two**, because they
13
+ * say different things to different people: one is about a folder that is about to lose a grant,
14
+ * the other about a flow that is about to be gone. Folding them into one parameterised sentence
15
+ * would buy a shared string and cost the reason each one gives.
16
+ */
17
+ export declare function callersPhrase(callers: Callers): string;
18
+ /**
19
+ * The same two facts the sentence is built from, in the shape a grant's own warning already uses
20
+ * (`UnreadableNodes`, #448). One question — what may you see, and of how much — deserves one answer
21
+ * shape on every surface, and a screen that has to write the sentence itself needs the facts and
22
+ * not the sentence.
23
+ *
24
+ * ⚠️ It reads the SAME argument the phrase above reads. A surface that recomputed the split would
25
+ * be deciding what it may not, and a second decision here would be the one that drifts.
26
+ */
27
+ export declare function namedOrCounted(callers: Callers): NamedOrCounted;
28
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ⚠️ Named where the reader may see it, counted where they may not (ADR-0004 §3). A refusal has to
3
+ * be actionable without becoming a way of reading the tree: whoever holds one grant on one folder
4
+ * must not learn the titles of flows they are refused directly.
5
+ *
6
+ * This is the middle of both refusals that ask the question — narrowing a folder (`nodes.ts`) and
7
+ * deleting a flow for good (`flows.ts`). ⚠️ **The two SENTENCES around it stay two**, because they
8
+ * say different things to different people: one is about a folder that is about to lose a grant,
9
+ * the other about a flow that is about to be gone. Folding them into one parameterised sentence
10
+ * would buy a shared string and cost the reason each one gives.
11
+ */
12
+ export function callersPhrase(callers) {
13
+ const named = callers.visible.map((title) => `“${title}”`).join(", ");
14
+ const rest = callers.hidden === 0
15
+ ? ""
16
+ : `${named ? " and " : ""}${callers.hidden} more flow${callers.hidden === 1 ? "" : "s"} you cannot see`;
17
+ return `${named}${rest}`;
18
+ }
19
+ /**
20
+ * The same two facts the sentence is built from, in the shape a grant's own warning already uses
21
+ * (`UnreadableNodes`, #448). One question — what may you see, and of how much — deserves one answer
22
+ * shape on every surface, and a screen that has to write the sentence itself needs the facts and
23
+ * not the sentence.
24
+ *
25
+ * ⚠️ It reads the SAME argument the phrase above reads. A surface that recomputed the split would
26
+ * be deciding what it may not, and a second decision here would be the one that drifts.
27
+ */
28
+ export function namedOrCounted(callers) {
29
+ return { titles: callers.visible, hidden: callers.hidden };
30
+ }
@@ -0,0 +1,13 @@
1
+ export declare function encodeCsvRow(cells: readonly string[]): string;
2
+ export declare function encodeCsv(rows: readonly (readonly string[])[]): string;
3
+ /**
4
+ * Every row of a CSV body, quoted fields included.
5
+ *
6
+ * ⚠️ A quoted field may contain a line break, so this cannot split on `\n` first. A row is closed
7
+ * by a line break that falls outside quotes and by nothing else — the reason a naive `split` reads
8
+ * a table with one multi-line cell as two broken rows.
9
+ *
10
+ * A trailing newline does not produce an empty final row; an empty line in the middle does produce
11
+ * a row with one empty cell, because that is what the file says.
12
+ */
13
+ export declare function parseCsv(text: string): string[][];
@@ -0,0 +1,85 @@
1
+ // The one CSV reader and writer in Intel. A table's canonical body is CSV (#40), and every surface
2
+ // that has to look inside one — the grid, the column check, the search index — goes through here so
3
+ // none of them can grow a private opinion about quoting.
4
+ //
5
+ // RFC 4180 with two deliberate narrowings: rows are separated by `\n` when written (a reader
6
+ // accepts `\r\n` too), and a field is quoted only when it has to be. Nothing else is escaped,
7
+ // because CSV has no escapes — a quote inside a quoted field is written twice.
8
+ function needsQuoting(cell) {
9
+ return (cell.includes(",") ||
10
+ cell.includes('"') ||
11
+ cell.includes("\n") ||
12
+ cell.includes("\r") ||
13
+ cell !== cell.trim());
14
+ }
15
+ export function encodeCsvRow(cells) {
16
+ return cells
17
+ .map((cell) => (needsQuoting(cell) ? `"${cell.replaceAll('"', '""')}"` : cell))
18
+ .join(",");
19
+ }
20
+ export function encodeCsv(rows) {
21
+ return rows.length === 0 ? "" : `${rows.map(encodeCsvRow).join("\n")}\n`;
22
+ }
23
+ /**
24
+ * Every row of a CSV body, quoted fields included.
25
+ *
26
+ * ⚠️ A quoted field may contain a line break, so this cannot split on `\n` first. A row is closed
27
+ * by a line break that falls outside quotes and by nothing else — the reason a naive `split` reads
28
+ * a table with one multi-line cell as two broken rows.
29
+ *
30
+ * A trailing newline does not produce an empty final row; an empty line in the middle does produce
31
+ * a row with one empty cell, because that is what the file says.
32
+ */
33
+ export function parseCsv(text) {
34
+ const rows = [];
35
+ let row = [];
36
+ let cell = "";
37
+ let quoted = false;
38
+ let started = false;
39
+ for (let index = 0; index < text.length; index += 1) {
40
+ const character = text[index];
41
+ if (quoted) {
42
+ if (character === '"') {
43
+ if (text[index + 1] === '"') {
44
+ cell += '"';
45
+ index += 1;
46
+ }
47
+ else {
48
+ quoted = false;
49
+ }
50
+ }
51
+ else {
52
+ cell += character;
53
+ }
54
+ continue;
55
+ }
56
+ if (character === '"' && cell === "") {
57
+ quoted = true;
58
+ started = true;
59
+ continue;
60
+ }
61
+ if (character === ",") {
62
+ row.push(cell);
63
+ cell = "";
64
+ started = true;
65
+ continue;
66
+ }
67
+ if (character === "\n" || character === "\r") {
68
+ if (character === "\r" && text[index + 1] === "\n")
69
+ index += 1;
70
+ row.push(cell);
71
+ rows.push(row);
72
+ row = [];
73
+ cell = "";
74
+ started = false;
75
+ continue;
76
+ }
77
+ cell += character;
78
+ started = true;
79
+ }
80
+ if (started || cell !== "" || row.length > 0) {
81
+ row.push(cell);
82
+ rows.push(row);
83
+ }
84
+ return rows;
85
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The READABLE text of one stored version, for the two places that need a document as prose rather
3
+ * than as the editor's own payload.
4
+ *
5
+ * ⚠️ **What a document node stores is NOT its text.** The editor writes a BlockNote payload —
6
+ * `{ blocks, markdown, … }` — and `markdown` beside the blocks is the whole of what anybody outside
7
+ * the editor can read. `packages/intel/ui/CLAUDE.md` states it for the search: `indexing.ts` pulls that
8
+ * field and nothing else into full text and vectors, so it is what anyone searching Intel matches
9
+ * against. A caller who hands the raw content on instead is handing on a JSON blob.
10
+ *
11
+ * ⚠️ **Every other media type travels unchanged**, and that is not an oversight: a `text/markdown`
12
+ * version IS its text, and a table renders its own. Only the editor's payload has a wrapper around
13
+ * the words.
14
+ *
15
+ * ⚠️ **`null` means "this content is not readable as text", not "empty".** Immutable content never
16
+ * becomes parsable on a retry, so the two callers answer it differently and each is right for its
17
+ * own surface: the indexer refuses the version permanently rather than requeueing it forever, and
18
+ * `prompts/get` refuses the call rather than handing a model half a JSON document. Returning `""`
19
+ * here would collapse both into "there is nothing to say".
20
+ */
21
+ export declare function documentText(mediaType: string, content: string): string | null;
@@ -0,0 +1,31 @@
1
+ import { BlockNoteDocument, BlockNoteMediaType } from "../../contract/node.js";
2
+ /**
3
+ * The READABLE text of one stored version, for the two places that need a document as prose rather
4
+ * than as the editor's own payload.
5
+ *
6
+ * ⚠️ **What a document node stores is NOT its text.** The editor writes a BlockNote payload —
7
+ * `{ blocks, markdown, … }` — and `markdown` beside the blocks is the whole of what anybody outside
8
+ * the editor can read. `packages/intel/ui/CLAUDE.md` states it for the search: `indexing.ts` pulls that
9
+ * field and nothing else into full text and vectors, so it is what anyone searching Intel matches
10
+ * against. A caller who hands the raw content on instead is handing on a JSON blob.
11
+ *
12
+ * ⚠️ **Every other media type travels unchanged**, and that is not an oversight: a `text/markdown`
13
+ * version IS its text, and a table renders its own. Only the editor's payload has a wrapper around
14
+ * the words.
15
+ *
16
+ * ⚠️ **`null` means "this content is not readable as text", not "empty".** Immutable content never
17
+ * becomes parsable on a retry, so the two callers answer it differently and each is right for its
18
+ * own surface: the indexer refuses the version permanently rather than requeueing it forever, and
19
+ * `prompts/get` refuses the call rather than handing a model half a JSON document. Returning `""`
20
+ * here would collapse both into "there is nothing to say".
21
+ */
22
+ export function documentText(mediaType, content) {
23
+ if (mediaType !== BlockNoteMediaType)
24
+ return content;
25
+ try {
26
+ return BlockNoteDocument.parse(JSON.parse(content)).markdown;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ import type { Authorized, GateClient } from "@anchrd/gate-sdk";
2
+ export declare function bearer(headers: Headers): string | null;
3
+ export declare function authorize(gate: Pick<GateClient, "authorize">, headers: Headers, resource: string): Promise<Authorized | null>;
4
+ export declare function permits(authorization: Pick<Authorized, "can">, handle: string, fn: string): boolean;
5
+ export declare function authorizeBearer(gate: Pick<GateClient, "authorize">, token: string, resource: string): Promise<Authorized | null>;
@@ -0,0 +1,26 @@
1
+ export function bearer(headers) {
2
+ const value = headers.get("authorization");
3
+ // RFC 9110 defines the auth scheme as case-insensitive, so a client sending "bearer" must not be
4
+ // turned away as unauthenticated.
5
+ if (!value || !/^bearer /i.test(value))
6
+ return null;
7
+ const token = value.slice(7);
8
+ return token.length > 0 ? token : null;
9
+ }
10
+ export async function authorize(gate, headers, resource) {
11
+ const token = bearer(headers);
12
+ if (!token)
13
+ return null;
14
+ return await authorizeBearer(gate, token, resource);
15
+ }
16
+ // `intel/admin` opens every door, and it has to: `share` inherits downwards only, so without a way
17
+ // in that needs no grant, a fresh instance has nobody who can set the first one (ADR-0004 §6). Gate
18
+ // learned the same lesson at three separate doors — a guard written per route is a list that goes
19
+ // quietly incomplete at the next one, so both surfaces ask this one function instead.
20
+ export function permits(authorization, handle, fn) {
21
+ return authorization.can(handle, fn) || authorization.can("intel", "admin");
22
+ }
23
+ export async function authorizeBearer(gate, token, resource) {
24
+ const result = await gate.authorize(token);
25
+ return result.ok && result.resource === resource ? result : null;
26
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ⚠️ A grant whose expiry has already passed is refused, never written (#442).
3
+ *
4
+ * It would otherwise be created, answered with a `201`, and listed — while reaching nobody. The
5
+ * sharer reads the row as done; the grantee gets no notification and finds out days later, if at
6
+ * all. That is the failure mode this repository keeps meeting: something that looks like success,
7
+ * and the only feedback is the wrong one.
8
+ *
9
+ * ⚠️ The check cannot live in the Zod schema. That boundary knows nothing about `deps.now()`, and
10
+ * the rule is decided by the very clock that evaluates the grant afterwards — so it belongs in the
11
+ * application layer, where every surface passes through it. The `.describe()` on `expiresAt`
12
+ * documents the rule; this refuses it.
13
+ *
14
+ * ⚠️ Equal to `now` is refused too: a grant that expires this instant is spent before the answer
15
+ * reaches the caller.
16
+ *
17
+ * `null` is untouched and stays what it always meant — a grant that does not expire on its own.
18
+ */
19
+ export declare function requireFutureExpiry(expiresAt: string | null, now: Date): void;
@@ -0,0 +1,26 @@
1
+ import { IntelError } from "../intel-error/intel-error.js";
2
+ /**
3
+ * ⚠️ A grant whose expiry has already passed is refused, never written (#442).
4
+ *
5
+ * It would otherwise be created, answered with a `201`, and listed — while reaching nobody. The
6
+ * sharer reads the row as done; the grantee gets no notification and finds out days later, if at
7
+ * all. That is the failure mode this repository keeps meeting: something that looks like success,
8
+ * and the only feedback is the wrong one.
9
+ *
10
+ * ⚠️ The check cannot live in the Zod schema. That boundary knows nothing about `deps.now()`, and
11
+ * the rule is decided by the very clock that evaluates the grant afterwards — so it belongs in the
12
+ * application layer, where every surface passes through it. The `.describe()` on `expiresAt`
13
+ * documents the rule; this refuses it.
14
+ *
15
+ * ⚠️ Equal to `now` is refused too: a grant that expires this instant is spent before the answer
16
+ * reaches the caller.
17
+ *
18
+ * `null` is untouched and stays what it always meant — a grant that does not expire on its own.
19
+ */
20
+ export function requireFutureExpiry(expiresAt, now) {
21
+ if (expiresAt === null)
22
+ return;
23
+ if (Date.parse(expiresAt) > now.getTime())
24
+ return;
25
+ throw new IntelError(400, "grant_already_expired", "A grant's expiry has to lie in the future; pass null for one that does not expire on its own");
26
+ }
@@ -0,0 +1,22 @@
1
+ import type { NamedOrCounted } from "../../contract/contract.js";
2
+ export declare class IntelError extends Error {
3
+ readonly status: number;
4
+ readonly code: string;
5
+ readonly callers?: NamedOrCounted | undefined;
6
+ /**
7
+ * ⚠️ `callers` is what a refusal that NAMES flows carries beside its sentence — today
8
+ * `folder_execute_in_use` (#448) and `flow_in_use_by_flow` (#593). It rides on the error rather
9
+ * than on the route because the split it belongs to is an application-layer one: who may be named
10
+ * and who may only be counted is an authorization answer, and it must be the same over HTTP, over
11
+ * MCP and in the browser. A route that assembled it would be a second place for that rule to live.
12
+ *
13
+ * `message` still carries the finished English sentence — a reader that only takes text must lose
14
+ * nothing — and the field is what lets a screen say it in its own language instead.
15
+ *
16
+ * ⚠️ **Setting this field is only half of reaching MCP.** The SDK formats a thrown error through
17
+ * `createToolError`, which keeps `message` and drops everything else, so a tool whose handler is
18
+ * not wrapped in `refusing()` (`mcp/mcp.ts`) answers prose and nothing more (#597). Whoever
19
+ * attaches `callers` to a new refusal wraps that tool's handler in the same change.
20
+ */
21
+ constructor(status: number, code: string, message: string, callers?: NamedOrCounted | undefined);
22
+ }
@@ -0,0 +1,27 @@
1
+ export class IntelError extends Error {
2
+ status;
3
+ code;
4
+ callers;
5
+ /**
6
+ * ⚠️ `callers` is what a refusal that NAMES flows carries beside its sentence — today
7
+ * `folder_execute_in_use` (#448) and `flow_in_use_by_flow` (#593). It rides on the error rather
8
+ * than on the route because the split it belongs to is an application-layer one: who may be named
9
+ * and who may only be counted is an authorization answer, and it must be the same over HTTP, over
10
+ * MCP and in the browser. A route that assembled it would be a second place for that rule to live.
11
+ *
12
+ * `message` still carries the finished English sentence — a reader that only takes text must lose
13
+ * nothing — and the field is what lets a screen say it in its own language instead.
14
+ *
15
+ * ⚠️ **Setting this field is only half of reaching MCP.** The SDK formats a thrown error through
16
+ * `createToolError`, which keeps `message` and drops everything else, so a tool whose handler is
17
+ * not wrapped in `refusing()` (`mcp/mcp.ts`) answers prose and nothing more (#597). Whoever
18
+ * attaches `callers` to a new refusal wraps that tool's handler in the same change.
19
+ */
20
+ constructor(status, code, message, callers) {
21
+ super(message);
22
+ this.status = status;
23
+ this.code = code;
24
+ this.callers = callers;
25
+ this.name = "IntelError";
26
+ }
27
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ⚠️ A title is plain text, and an HTML entity in one is never what somebody meant.
3
+ *
4
+ * Intel escapes nothing, anywhere — the entity arrives already in the value (#202). The chain that
5
+ * carries a title to a write can HTML-escape what it hands to a model: the GitHub MCP server, for
6
+ * one, returns every string with `&` as `&amp;` and `"` as `&#34;`, so a model that copies a name
7
+ * out of one tool result into the next call writes the entity, not the character. Refusing the
8
+ * write would be the louder answer and the wrong one — the model reads the same escaped value
9
+ * again on its retry and cannot get past it. So the write boundary accepts it and stores what was
10
+ * meant.
11
+ *
12
+ * Repeated until it stops changing rather than once, because that is the failure the ticket names:
13
+ * a title read back escaped and written again is escaped twice, and a single round would leave
14
+ * `&amp;amp;` as `&amp;` — still an entity, still spreading.
15
+ */
16
+ export declare function plainTitle(title: string): string;
@@ -0,0 +1,45 @@
1
+ // Exactly what an HTML escaper emits, and nothing else. `&#34;` and `&#39;` are Go's
2
+ // `html.EscapeString` spelling of the quotes — the fingerprint of the tool that produced the
3
+ // titles this exists for. A general entity table is deliberately not here: `&nbsp;` or `&auml;` in
4
+ // a title is a person's choice, an escaped `&` never is.
5
+ const Escapes = [
6
+ [/&lt;/g, "<"],
7
+ [/&gt;/g, ">"],
8
+ [/&quot;/g, '"'],
9
+ [/&#0*34;/g, '"'],
10
+ [/&apos;/g, "'"],
11
+ [/&#0*39;/g, "'"],
12
+ // Last in the pass, so one round of unescaping is the exact inverse of one round of escaping:
13
+ // `&amp;lt;` becomes `&lt;` here and only the NEXT round turns it into `<`.
14
+ [/&amp;/g, "&"],
15
+ ];
16
+ // A title that survived one more escaping than any writer intended. Eight is far past anything a
17
+ // real chain produces and stops a crafted title from spinning here.
18
+ const MaxRounds = 8;
19
+ /**
20
+ * ⚠️ A title is plain text, and an HTML entity in one is never what somebody meant.
21
+ *
22
+ * Intel escapes nothing, anywhere — the entity arrives already in the value (#202). The chain that
23
+ * carries a title to a write can HTML-escape what it hands to a model: the GitHub MCP server, for
24
+ * one, returns every string with `&` as `&amp;` and `"` as `&#34;`, so a model that copies a name
25
+ * out of one tool result into the next call writes the entity, not the character. Refusing the
26
+ * write would be the louder answer and the wrong one — the model reads the same escaped value
27
+ * again on its retry and cannot get past it. So the write boundary accepts it and stores what was
28
+ * meant.
29
+ *
30
+ * Repeated until it stops changing rather than once, because that is the failure the ticket names:
31
+ * a title read back escaped and written again is escaped twice, and a single round would leave
32
+ * `&amp;amp;` as `&amp;` — still an entity, still spreading.
33
+ */
34
+ export function plainTitle(title) {
35
+ let current = title;
36
+ for (let round = 0; round < MaxRounds; round += 1) {
37
+ let next = current;
38
+ for (const [pattern, character] of Escapes)
39
+ next = next.replace(pattern, character);
40
+ if (next === current)
41
+ break;
42
+ current = next;
43
+ }
44
+ return current;
45
+ }
@@ -0,0 +1,30 @@
1
+ import type { IntelError } from "../intel-error/intel-error.js";
2
+ export declare function problemDetails(status: number, code: string, title: string, detail?: string): {
3
+ type: string;
4
+ status: number;
5
+ title: string;
6
+ detail?: string | undefined;
7
+ instance?: string | undefined;
8
+ code?: string | undefined;
9
+ callers?: {
10
+ titles: string[];
11
+ hidden: number;
12
+ } | undefined;
13
+ };
14
+ /**
15
+ * One refusal, one document — written here rather than at each `onError`, because two apps turn an
16
+ * `IntelError` into a body and an extension member added at one of them would silently be missing
17
+ * from the other (#448). Whatever the application layer decided to say travels with it.
18
+ */
19
+ export declare function intelProblem(error: IntelError): {
20
+ type: string;
21
+ status: number;
22
+ title: string;
23
+ detail?: string | undefined;
24
+ instance?: string | undefined;
25
+ code?: string | undefined;
26
+ callers?: {
27
+ titles: string[];
28
+ hidden: number;
29
+ } | undefined;
30
+ };
@@ -0,0 +1,18 @@
1
+ import { ProblemDetails } from "../../contract/contract.js";
2
+ export function problemDetails(status, code, title, detail) {
3
+ return ProblemDetails.parse({ type: "about:blank", status, code, title, detail });
4
+ }
5
+ /**
6
+ * One refusal, one document — written here rather than at each `onError`, because two apps turn an
7
+ * `IntelError` into a body and an extension member added at one of them would silently be missing
8
+ * from the other (#448). Whatever the application layer decided to say travels with it.
9
+ */
10
+ export function intelProblem(error) {
11
+ return ProblemDetails.parse({
12
+ type: "about:blank",
13
+ status: error.status,
14
+ code: error.code,
15
+ title: error.message,
16
+ callers: error.callers,
17
+ });
18
+ }
@@ -0,0 +1 @@
1
+ export declare function reportUnexpectedError(error: unknown): void;
@@ -0,0 +1,37 @@
1
+ // A library error can quote whatever the peer sent — an echoed Authorization header, a token in a
2
+ // URL, a response body. The log must show the failure and never the credential, so everything
3
+ // shaped like one is cut before the line is written. The patterns match shapes, not sources: a JWT
4
+ // is recognizable on its own, and other tokens only ever appear behind their label.
5
+ function redact(text) {
6
+ // ⚠️ The order is load-bearing: "Authorization: Bearer x" must hit the bearer rule first — a
7
+ // combined alternation would let the label rule win the leftmost match, swallow the word
8
+ // "Bearer" as the value, and leave the token itself standing.
9
+ return text
10
+ .replace(/\beyJ[\w-]{4,}\.[\w-]+\.[\w-]*/g, "[redacted]")
11
+ .replace(/\b(bearer\s+)[\w.~+/-]+=*/gi, "$1[redacted]")
12
+ .replace(/\b((?:access_token|refresh_token|id_token|client_secret|api_key|authorization)"?\s*[:=]\s*"?)[\w.~+/-]+=*/gi, "$1[redacted]");
13
+ }
14
+ function describe(error) {
15
+ if (error instanceof Error)
16
+ return error.stack ?? `${error.name}: ${error.message}`;
17
+ if (typeof error === "string")
18
+ return error;
19
+ try {
20
+ return JSON.stringify(error);
21
+ }
22
+ catch {
23
+ return String(error);
24
+ }
25
+ }
26
+ // openid-client wraps the informative part — the HTTP response, the OAuth error body — into
27
+ // `cause`, so a log line without the cause chain would name the wrapper and hide the reason (#93).
28
+ const MaxCauseDepth = 5;
29
+ export function reportUnexpectedError(error) {
30
+ const parts = [];
31
+ let current = error;
32
+ for (let depth = 0; depth < MaxCauseDepth && current !== undefined; depth += 1) {
33
+ parts.push(describe(current));
34
+ current = current instanceof Error ? current.cause : undefined;
35
+ }
36
+ console.error(redact(parts.join("\ncaused by: ")));
37
+ }
@@ -0,0 +1,2 @@
1
+ import { z } from "zod";
2
+ export declare const SafeReturnPath: z.ZodString;
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ export const SafeReturnPath = z
3
+ .string()
4
+ .startsWith("/")
5
+ .refine((value) => !value.startsWith("//") &&
6
+ !value.startsWith("/\\") &&
7
+ !value.includes("\\") &&
8
+ ![...value].some((character) => {
9
+ const code = character.charCodeAt(0);
10
+ return code < 32 || code === 127;
11
+ }), "Return path must be a local absolute path");
@@ -0,0 +1,2 @@
1
+ export declare function hexDigest(digest: ArrayBuffer): string;
2
+ export declare function sha256Hex(crypto: Crypto, content: string | ArrayBuffer | Uint8Array): Promise<string>;
@@ -0,0 +1,14 @@
1
+ // The one place a digest becomes text. Both doors for an attachment write a `contentHash`: the
2
+ // base64 one hashes a buffer it already holds, the streaming one takes its digest as the bytes pass
3
+ // (#821). Two spellings of the same digest would make the two versions of one file look different.
4
+ export function hexDigest(digest) {
5
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
6
+ }
7
+ export async function sha256Hex(crypto, content) {
8
+ const source = typeof content === "string"
9
+ ? new TextEncoder().encode(content).buffer
10
+ : content instanceof Uint8Array
11
+ ? Uint8Array.from(content).buffer
12
+ : content;
13
+ return hexDigest(await crypto.subtle.digest("SHA-256", source));
14
+ }