@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.
- package/README.md +496 -0
- package/bin/intel.mjs +134 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
- package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
- package/dist/adapters/cloudflare/cloudflare.js +416 -0
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
- package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
- package/dist/adapters/content/content.d.ts +3 -0
- package/dist/adapters/content/content.js +128 -0
- package/dist/adapters/content/content.types.d.ts +38 -0
- package/dist/adapters/content/content.types.js +1 -0
- package/dist/adapters/db/db-audit.d.ts +62 -0
- package/dist/adapters/db/db-audit.js +150 -0
- package/dist/adapters/db/db-boards.d.ts +6 -0
- package/dist/adapters/db/db-boards.js +324 -0
- package/dist/adapters/db/db-feed.d.ts +30 -0
- package/dist/adapters/db/db-feed.js +276 -0
- package/dist/adapters/db/db-flows.d.ts +42 -0
- package/dist/adapters/db/db-flows.js +1311 -0
- package/dist/adapters/db/db-grants.d.ts +133 -0
- package/dist/adapters/db/db-grants.js +269 -0
- package/dist/adapters/db/db-indexing.d.ts +3 -0
- package/dist/adapters/db/db-indexing.js +200 -0
- package/dist/adapters/db/db-oauth.d.ts +3 -0
- package/dist/adapters/db/db-oauth.js +26 -0
- package/dist/adapters/db/db-prompts.d.ts +46 -0
- package/dist/adapters/db/db-prompts.js +144 -0
- package/dist/adapters/db/db.d.ts +6 -0
- package/dist/adapters/db/db.js +1687 -0
- package/dist/adapters/db/db.types.d.ts +16 -0
- package/dist/adapters/db/db.types.js +1 -0
- package/dist/adapters/document-converter/document-converter.d.ts +2 -0
- package/dist/adapters/document-converter/document-converter.js +26 -0
- package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
- package/dist/adapters/document-converter/document-converter.types.js +1 -0
- package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
- package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
- package/dist/adapters/index-queue/index-queue.d.ts +10 -0
- package/dist/adapters/index-queue/index-queue.js +12 -0
- package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
- package/dist/adapters/index-queue/index-queue.types.js +1 -0
- package/dist/adapters/json-schema/json-schema.d.ts +2 -0
- package/dist/adapters/json-schema/json-schema.js +77 -0
- package/dist/adapters/openid/openid.d.ts +5 -0
- package/dist/adapters/openid/openid.js +301 -0
- package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
- package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
- package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
- package/dist/adapters/remote-tools/remote-tools.js +129 -0
- package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
- package/dist/adapters/semantic-index/semantic-index.js +139 -0
- package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
- package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
- package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
- package/dist/adapters/session-cookie/session-cookie.js +87 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
- package/dist/audit/audit.d.ts +2 -0
- package/dist/audit/audit.js +87 -0
- package/dist/audit/audit.types.d.ts +23 -0
- package/dist/audit/audit.types.js +1 -0
- package/dist/auth/auth.d.ts +2 -0
- package/dist/auth/auth.js +284 -0
- package/dist/auth/auth.types.d.ts +108 -0
- package/dist/auth/auth.types.js +1 -0
- package/dist/boards/boards.d.ts +44 -0
- package/dist/boards/boards.js +447 -0
- package/dist/boards/boards.types.d.ts +117 -0
- package/dist/boards/boards.types.js +1 -0
- package/dist/build/build.d.ts +2 -0
- package/dist/build/build.js +192 -0
- package/dist/build/build.types.d.ts +17 -0
- package/dist/build/build.types.js +1 -0
- package/dist/bundle/bundle.d.ts +4 -0
- package/dist/bundle/bundle.js +1194 -0
- package/dist/bundle/bundle.types.d.ts +33 -0
- package/dist/bundle/bundle.types.js +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.js +262 -0
- package/dist/cli/cli.types.d.ts +14 -0
- package/dist/cli/cli.types.js +1 -0
- package/dist/contract/audit.d.ts +50 -0
- package/dist/contract/audit.js +72 -0
- package/dist/contract/board.d.ts +150 -0
- package/dist/contract/board.js +262 -0
- package/dist/contract/bundle.d.ts +147 -0
- package/dist/contract/bundle.js +139 -0
- package/dist/contract/contract.d.ts +112 -0
- package/dist/contract/contract.js +143 -0
- package/dist/contract/feed.d.ts +126 -0
- package/dist/contract/feed.js +149 -0
- package/dist/contract/flow-run.d.ts +346 -0
- package/dist/contract/flow-run.js +194 -0
- package/dist/contract/flow.d.ts +1057 -0
- package/dist/contract/flow.js +552 -0
- package/dist/contract/node.d.ts +551 -0
- package/dist/contract/node.js +456 -0
- package/dist/contract/share.d.ts +216 -0
- package/dist/contract/share.js +134 -0
- package/dist/contract/table.d.ts +171 -0
- package/dist/contract/table.js +117 -0
- package/dist/contract/tool.d.ts +122 -0
- package/dist/contract/tool.js +172 -0
- package/dist/feed/feed.d.ts +2 -0
- package/dist/feed/feed.js +77 -0
- package/dist/feed/feed.types.d.ts +23 -0
- package/dist/feed/feed.types.js +1 -0
- package/dist/flows/flows.d.ts +41 -0
- package/dist/flows/flows.js +2216 -0
- package/dist/flows/flows.types.d.ts +339 -0
- package/dist/flows/flows.types.js +1 -0
- package/dist/http/http.d.ts +9 -0
- package/dist/http/http.js +874 -0
- package/dist/http/http.types.d.ts +22 -0
- package/dist/http/http.types.js +1 -0
- package/dist/indexing/indexing.d.ts +6 -0
- package/dist/indexing/indexing.js +239 -0
- package/dist/indexing/indexing.types.d.ts +11 -0
- package/dist/indexing/indexing.types.js +1 -0
- package/dist/intel/intel.d.ts +3 -0
- package/dist/intel/intel.js +194 -0
- package/dist/intel/intel.types.d.ts +24 -0
- package/dist/intel/intel.types.js +1 -0
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +1292 -0
- package/dist/mcp/mcp.types.d.ts +25 -0
- package/dist/mcp/mcp.types.js +1 -0
- package/dist/nodes/document-links/document-links.d.ts +20 -0
- package/dist/nodes/document-links/document-links.js +57 -0
- package/dist/nodes/nodes.d.ts +2 -0
- package/dist/nodes/nodes.js +1667 -0
- package/dist/nodes/nodes.types.d.ts +511 -0
- package/dist/nodes/nodes.types.js +1 -0
- package/dist/prepare/prepare.d.ts +4 -0
- package/dist/prepare/prepare.js +16 -0
- package/dist/prepare/prepare.types.d.ts +9 -0
- package/dist/prepare/prepare.types.js +1 -0
- package/dist/prompts/prompts.d.ts +2 -0
- package/dist/prompts/prompts.js +65 -0
- package/dist/prompts/prompts.types.d.ts +71 -0
- package/dist/prompts/prompts.types.js +1 -0
- package/dist/shared/callers/callers.d.ts +28 -0
- package/dist/shared/callers/callers.js +30 -0
- package/dist/shared/csv/csv.d.ts +13 -0
- package/dist/shared/csv/csv.js +85 -0
- package/dist/shared/document-text/document-text.d.ts +21 -0
- package/dist/shared/document-text/document-text.js +31 -0
- package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
- package/dist/shared/gate-authorization/gate-authorization.js +26 -0
- package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
- package/dist/shared/grant-expiry/grant-expiry.js +26 -0
- package/dist/shared/intel-error/intel-error.d.ts +22 -0
- package/dist/shared/intel-error/intel-error.js +27 -0
- package/dist/shared/plain-title/plain-title.d.ts +16 -0
- package/dist/shared/plain-title/plain-title.js +45 -0
- package/dist/shared/problem-details/problem-details.d.ts +30 -0
- package/dist/shared/problem-details/problem-details.js +18 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
- package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
- package/dist/shared/safe-return-path/safe-return-path.js +11 -0
- package/dist/shared/sha256/sha256.d.ts +2 -0
- package/dist/shared/sha256/sha256.js +14 -0
- package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
- package/dist/tools/tool-servers/tool-servers.js +177 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +207 -0
- package/dist/tools/tools.types.d.ts +56 -0
- package/dist/tools/tools.types.js +1 -0
- package/examples/branding/favicon.svg +5 -0
- package/examples/branding/logo.svg +5 -0
- package/examples/branding/theme.css +16 -0
- package/examples/dev.vars.example +7 -0
- package/examples/intel.json +9 -0
- package/migrations/0000_intel_foundation.sql +203 -0
- package/migrations/0001_portal_native_tools.sql +21 -0
- package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
- package/migrations/0003_folder_permissions.sql +211 -0
- package/migrations/0004_subflow_runs.sql +14 -0
- package/migrations/0005_flow_node_cleanup.sql +28 -0
- package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
- package/migrations/0006_links_are_written_in_the_text.sql +20 -0
- package/migrations/0007_no_node_waits.sql +69 -0
- package/migrations/0008_three_layers.sql +130 -0
- package/migrations/0009_no_context_policy.sql +48 -0
- package/migrations/0010_no_run_waits_a_year.sql +27 -0
- package/migrations/0011_one_name_for_the_tree.sql +53 -0
- package/migrations/0012_table_snapshots.sql +29 -0
- package/migrations/0017_a_vector_per_card.sql +38 -0
- package/migrations/0018_no_context_policy_at_last.sql +97 -0
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/migrations/0020_cascade_purge_replay.sql +11 -0
- package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
- package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
- package/migrations/0023_a_board_and_its_tasks.sql +116 -0
- package/migrations/0024_the_archive_is_a_column.sql +33 -0
- package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
- package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
- package/migrations/0027_the_runs_of_every_flow.sql +27 -0
- package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
- package/package.json +118 -0
- package/src/contract/CLAUDE.md +152 -0
- package/src/contract/README.md +102 -0
- package/src/contract/audit.ts +85 -0
- package/src/contract/board.ts +306 -0
- package/src/contract/bundle.ts +157 -0
- package/src/contract/contract.ts +161 -0
- package/src/contract/feed.ts +165 -0
- package/src/contract/flow-run.ts +241 -0
- package/src/contract/flow.ts +675 -0
- package/src/contract/node.ts +583 -0
- package/src/contract/share.ts +196 -0
- package/src/contract/table.ts +178 -0
- package/src/contract/tool.ts +202 -0
- package/ui/README.md +110 -0
- package/ui/components.json +21 -0
- package/ui/index.html +13 -0
- package/ui/package.json +11 -0
- package/ui/scripts/lint-tokens.mjs +187 -0
- package/ui/src/access-summary/access-summary.tsx +385 -0
- package/ui/src/app/action-slot/action-slot.tsx +27 -0
- package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
- package/ui/src/app/app-tree/app-tree.tsx +1175 -0
- package/ui/src/app/app.tsx +87 -0
- package/ui/src/app/header-search/header-search.tsx +294 -0
- package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
- package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
- package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
- package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
- package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
- package/ui/src/app/tree-move/tree-move.tsx +427 -0
- package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
- package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
- package/ui/src/app/user-footer/user-footer.tsx +188 -0
- package/ui/src/app/view-menu/view-menu.tsx +115 -0
- package/ui/src/app-root/app-root.tsx +109 -0
- package/ui/src/app-root/app-root.types.ts +27 -0
- package/ui/src/archive/archive.tsx +417 -0
- package/ui/src/attachment-detail/attachment-detail.ts +52 -0
- package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
- package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
- package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
- package/ui/src/board/board-assignee/board-assignee.ts +48 -0
- package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
- package/ui/src/board/board-chip/board-chip.tsx +46 -0
- package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
- package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
- package/ui/src/board/board-data/board-data.ts +195 -0
- package/ui/src/board/board-data/board-data.types.ts +38 -0
- package/ui/src/board/board-dates/board-dates.ts +40 -0
- package/ui/src/board/board-dates/board-dates.tsx +105 -0
- package/ui/src/board/board-kanban/board-kanban.ts +162 -0
- package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
- package/ui/src/board/board-open-task/board-open-task.ts +32 -0
- package/ui/src/board/board-panel/board-panel.tsx +259 -0
- package/ui/src/board/board-settings/board-settings.tsx +226 -0
- package/ui/src/board/board-status/board-status.tsx +60 -0
- package/ui/src/board/board-stripes/board-stripes.ts +128 -0
- package/ui/src/board/board-table/board-table.ts +53 -0
- package/ui/src/board/board-table/board-table.tsx +610 -0
- package/ui/src/board/board-task/board-task.ts +108 -0
- package/ui/src/board/board-task/board-task.tsx +335 -0
- package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
- package/ui/src/branding/branding.tsx +17 -0
- package/ui/src/branding/custom-logo.ts +1 -0
- package/ui/src/branding/favicon.default.svg +5 -0
- package/ui/src/branding/favicon.svg +5 -0
- package/ui/src/components/ui/avatar.tsx +39 -0
- package/ui/src/components/ui/button.tsx +64 -0
- package/ui/src/components/ui/collapsible.tsx +20 -0
- package/ui/src/components/ui/command.tsx +160 -0
- package/ui/src/components/ui/dialog.tsx +143 -0
- package/ui/src/components/ui/dropdown-menu.tsx +187 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/popover.tsx +41 -0
- package/ui/src/components/ui/select.tsx +163 -0
- package/ui/src/components/ui/separator.tsx +26 -0
- package/ui/src/components/ui/sheet.tsx +136 -0
- package/ui/src/components/ui/sidebar.tsx +693 -0
- package/ui/src/components/ui/skeleton.tsx +13 -0
- package/ui/src/components/ui/switch.tsx +25 -0
- package/ui/src/components/ui/table.tsx +82 -0
- package/ui/src/components/ui/tabs.tsx +52 -0
- package/ui/src/components/ui/tooltip.tsx +51 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
- package/ui/src/data/query-client.ts +18 -0
- package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
- package/ui/src/data/request-refusal/request-refusal.ts +51 -0
- package/ui/src/data/sign-in/sign-in.ts +97 -0
- package/ui/src/data/sign-in/sign-in.types.ts +15 -0
- package/ui/src/document-link/document-link.tsx +114 -0
- package/ui/src/document-markdown/document-markdown.ts +195 -0
- package/ui/src/editor-schema/editor-schema.ts +23 -0
- package/ui/src/entry-picker/entry-picker.tsx +238 -0
- package/ui/src/feed/feed.tsx +344 -0
- package/ui/src/file-preview/file-preview-view.tsx +496 -0
- package/ui/src/file-preview/file-preview.tsx +28 -0
- package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
- package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
- package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
- package/ui/src/file-preview/word-file-preview.tsx +5 -0
- package/ui/src/flow-runs/flow-runs.tsx +238 -0
- package/ui/src/flows/flows.tsx +1646 -0
- package/ui/src/flows/node-icon/node-icon.ts +34 -0
- package/ui/src/flows/node-palette/node-palette.tsx +214 -0
- package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
- package/ui/src/folder-contents/folder-contents.tsx +321 -0
- package/ui/src/folder-index/folder-index.ts +55 -0
- package/ui/src/frontmatter/frontmatter.tsx +193 -0
- package/ui/src/graph-pane/graph-pane.tsx +53 -0
- package/ui/src/hooks/use-mobile.ts +19 -0
- package/ui/src/i18n/custom.json +4 -0
- package/ui/src/i18n/de.json +609 -0
- package/ui/src/i18n/en.json +609 -0
- package/ui/src/i18n/es.json +609 -0
- package/ui/src/i18n/i18n-context.tsx +65 -0
- package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
- package/ui/src/i18n/i18n.ts +32 -0
- package/ui/src/i18n/i18n.types.ts +49 -0
- package/ui/src/kind-icon.ts +97 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +68 -0
- package/ui/src/modal/modal.tsx +53 -0
- package/ui/src/node-details/node-details.tsx +545 -0
- package/ui/src/node-editor/node-editor.tsx +304 -0
- package/ui/src/node-graph/graph-notice.tsx +21 -0
- package/ui/src/node-graph/node-graph.ts +214 -0
- package/ui/src/node-graph/node-graph.tsx +295 -0
- package/ui/src/node-import/node-import.tsx +418 -0
- package/ui/src/node-save-error.ts +46 -0
- package/ui/src/node-source/node-source.tsx +128 -0
- package/ui/src/node-table/node-table.tsx +107 -0
- package/ui/src/nodes/nodes.tsx +240 -0
- package/ui/src/rename-resource/rename-resource.ts +80 -0
- package/ui/src/resolved-names/resolved-names.tsx +67 -0
- package/ui/src/resource-error.ts +33 -0
- package/ui/src/resource-menu/resource-menu.tsx +1156 -0
- package/ui/src/router/router-context.ts +6 -0
- package/ui/src/router/router.tsx +67 -0
- package/ui/src/router/router.types.ts +8 -0
- package/ui/src/router/selection-search.ts +88 -0
- package/ui/src/save-button/save-button.tsx +94 -0
- package/ui/src/section-hint/section-hint.tsx +40 -0
- package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
- package/ui/src/styles.css +189 -0
- package/ui/src/table-columns/table-columns.tsx +379 -0
- package/ui/src/theme/custom.css +1 -0
- package/ui/src/theme/theme-context.tsx +89 -0
- package/ui/src/theme/theme.ts +70 -0
- package/ui/src/time/relative-time.tsx +41 -0
- package/ui/src/time/time-context.tsx +78 -0
- package/ui/src/time/time.ts +165 -0
- package/ui/src/title-row/title-row.tsx +185 -0
- package/ui/src/tools/tools.tsx +546 -0
- package/ui/src/user-name/user-name.ts +61 -0
- package/ui/src/verbatim/verbatim.tsx +68 -0
- package/ui/tsconfig.json +17 -0
- package/ui/vite.config.ts +129 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The runtime module is provided by workerd and intentionally has no Node package resolution path.
|
|
2
|
+
// @ts-expect-error cloudflare:workers is available inside the deployed Worker runtime.
|
|
3
|
+
import { WorkflowEntrypoint as RuntimeWorkflowEntrypoint } from "cloudflare:workers";
|
|
4
|
+
class WorkflowEntrypoint extends RuntimeWorkflowEntrypoint {
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* How long a run may stand on one step before nobody is working on it any more (#83).
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ This is a product decision and not a constant somebody may tune while reading the code. Too
|
|
10
|
+
* short is worse than the problem it fixes: it ends runs that were only slow, which destroys work
|
|
11
|
+
* instead of merely displaying it wrongly. 30 minutes covers a large document, a sluggish API and
|
|
12
|
+
* a person who walks away from an approval; it does not cover a tab that was closed yesterday.
|
|
13
|
+
*
|
|
14
|
+
* The deployment may override it with `FLOW_RUN_STALL_TIMEOUT` (a Workflows duration, e.g. "2
|
|
15
|
+
* hours"), which is what makes the figure readable in operation rather than buried here.
|
|
16
|
+
*/
|
|
17
|
+
const DefaultStallTimeout = "30 minutes";
|
|
18
|
+
/**
|
|
19
|
+
* What the durable wait actually does, with storage handed in.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ Separated from the class because `WorkflowEntrypoint` is a runtime class that cannot be
|
|
22
|
+
* constructed outside workerd's workflow context — so as long as this logic lived inside it, none
|
|
23
|
+
* of it could be tested, including the rule that keeps a caller alive while its child runs. That
|
|
24
|
+
* rule is the one that decides whether this feature destroys work or not, and it had to be
|
|
25
|
+
* provable. The shell below is now only bindings and SQL, which is what `CLAUDE.md` asks of it.
|
|
26
|
+
*/
|
|
27
|
+
export async function driveFlowRun(step, deps) {
|
|
28
|
+
const fail = async (name, error) => await step.do(name, async () => {
|
|
29
|
+
await deps.markFailed(error);
|
|
30
|
+
return { status: "failed", error };
|
|
31
|
+
});
|
|
32
|
+
for (let iteration = 0; iteration < 500; iteration += 1) {
|
|
33
|
+
const state = await step.do(`inspect flow run ${iteration}`, async () => await deps.readStatus());
|
|
34
|
+
if (["completed", "failed", "cancelled"].includes(state.status))
|
|
35
|
+
return state;
|
|
36
|
+
try {
|
|
37
|
+
await step.waitForEvent(`wait for flow run ${iteration}`, {
|
|
38
|
+
type: "advance",
|
|
39
|
+
// ⚠️ The clock starts HERE, which is what makes it "since the last step" rather than "since
|
|
40
|
+
// the run began": every `advance` returns to the top of the loop and waits anew. A flow
|
|
41
|
+
// with one genuinely long step is measured against that step, not against its total length.
|
|
42
|
+
timeout: deps.stallTimeout,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// ⚠️ A caller standing on a sub-flow node looks exactly like a run nobody is working on — it
|
|
47
|
+
// is waiting, by design, for the called run to finish. Ending it here would tear down a
|
|
48
|
+
// caller because its child was slow, which is the one way this fix could destroy work rather
|
|
49
|
+
// than merely display it wrongly. So the timeout asks first, and waits again if a child is
|
|
50
|
+
// still going.
|
|
51
|
+
const waiting = await step.do(`check called run ${iteration}`, async () => await deps.hasRunningChild());
|
|
52
|
+
if (waiting)
|
|
53
|
+
continue;
|
|
54
|
+
return await fail(`expire flow run ${iteration}`, "Nothing has moved this run forward, so it was ended. Whatever was running it stopped without reporting a result — start it again if it is still needed.");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return await fail("fail flow step limit", "Flow exceeded the maximum durable step count");
|
|
58
|
+
}
|
|
59
|
+
class IntelFlowWorkflowImplementation extends WorkflowEntrypoint {
|
|
60
|
+
async run(event, step) {
|
|
61
|
+
const runId = event.payload.runId;
|
|
62
|
+
return await driveFlowRun(step, {
|
|
63
|
+
readStatus: async () => {
|
|
64
|
+
const row = await this.env.DB.prepare("SELECT status, error FROM flow_runs WHERE id = ?")
|
|
65
|
+
.bind(runId)
|
|
66
|
+
.first();
|
|
67
|
+
return row ?? { status: "failed", error: "Flow run disappeared" };
|
|
68
|
+
},
|
|
69
|
+
hasRunningChild: async () => {
|
|
70
|
+
const row = await this.env.DB.prepare(`SELECT id FROM flow_runs
|
|
71
|
+
WHERE parent_run_id = ? AND status IN ('queued', 'running')
|
|
72
|
+
LIMIT 1`)
|
|
73
|
+
.bind(runId)
|
|
74
|
+
.first();
|
|
75
|
+
return row !== null;
|
|
76
|
+
},
|
|
77
|
+
markFailed: async (error) => {
|
|
78
|
+
const occurredAt = new Date().toISOString();
|
|
79
|
+
await this.env.DB.prepare(`UPDATE flow_runs
|
|
80
|
+
SET status = 'failed', error = ?, completed_at = ?, updated_at = ?
|
|
81
|
+
WHERE id = ? AND status NOT IN ('completed', 'failed', 'cancelled')`)
|
|
82
|
+
.bind(error, occurredAt, occurredAt, runId)
|
|
83
|
+
.run();
|
|
84
|
+
},
|
|
85
|
+
stallTimeout: this.env.FLOW_RUN_STALL_TIMEOUT || DefaultStallTimeout,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export const IntelFlowWorkflow = IntelFlowWorkflowImplementation;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DocumentConverter } from "../document-converter/document-converter.types.js";
|
|
2
|
+
import type { SemanticIndex } from "../semantic-index/semantic-index.types.js";
|
|
3
|
+
import type { CloudflareEnv, CloudflareExecutionContext, IndexQueueBatch } from "./cloudflare.types.js";
|
|
4
|
+
export { IntelFlowWorkflow } from "./cloudflare-flow-workflow.js";
|
|
5
|
+
export declare function isSpaNavigation(request: Request): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The two ports that call Workers AI, and the one condition that decides whether they exist at all
|
|
8
|
+
* (#800).
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ **No gateway id, no ports.** The obvious spelling would have been to pass the gateway when
|
|
11
|
+
* there is one and call Workers AI directly when there is not — and that is precisely the silent
|
|
12
|
+
* bypass the spend limit exists against: the deployment keeps embedding on every save and every
|
|
13
|
+
* `reindex`, the gateway counts none of it, and nobody notices because everything works. A missing
|
|
14
|
+
* semantic index is a state this repository already knows and describes (see the bindings table in
|
|
15
|
+
* the package README); an unmetered one is not.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ Both ports fall away together, because both call Workers AI: the embeddings through `run` and
|
|
18
|
+
* the attachment conversion through `toMarkdown`. Repairing only the first half would have left a
|
|
19
|
+
* second door open on the same account.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createAiPorts(env: CloudflareEnv): {
|
|
22
|
+
semantic: SemanticIndex | undefined;
|
|
23
|
+
converter: DocumentConverter | undefined;
|
|
24
|
+
};
|
|
25
|
+
declare const _default: {
|
|
26
|
+
fetch(request: Request, env: CloudflareEnv, _context: CloudflareExecutionContext): Promise<Response>;
|
|
27
|
+
queue(batch: IndexQueueBatch, env: CloudflareEnv): Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
export default _default;
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { createGateClient } from "@anchrd/gate-sdk";
|
|
2
|
+
import { ulid } from "ulid";
|
|
3
|
+
import { createAudit } from "../../audit/audit.js";
|
|
4
|
+
import { createBrowserAuth } from "../../auth/auth.js";
|
|
5
|
+
import { createBoards, FIRST_DEFAULT_COLUMN } from "../../boards/boards.js";
|
|
6
|
+
import { createBundle } from "../../bundle/bundle.js";
|
|
7
|
+
import { serverOf } from "../../contract/tool.js";
|
|
8
|
+
import { createFeed } from "../../feed/feed.js";
|
|
9
|
+
import { createFlows } from "../../flows/flows.js";
|
|
10
|
+
import { createIndexing, PermanentIndexingError } from "../../indexing/indexing.js";
|
|
11
|
+
import { createIntel } from "../../intel/intel.js";
|
|
12
|
+
import { createNodes } from "../../nodes/nodes.js";
|
|
13
|
+
import { createPrompts } from "../../prompts/prompts.js";
|
|
14
|
+
import { bearer } from "../../shared/gate-authorization/gate-authorization.js";
|
|
15
|
+
import { IntelError } from "../../shared/intel-error/intel-error.js";
|
|
16
|
+
import { sha256Hex } from "../../shared/sha256/sha256.js";
|
|
17
|
+
import { createTools } from "../../tools/tools.js";
|
|
18
|
+
import { createContentStore } from "../content/content.js";
|
|
19
|
+
import { createNodeRepository } from "../db/db.js";
|
|
20
|
+
import { createAuditRepository } from "../db/db-audit.js";
|
|
21
|
+
import { createBoardRepository } from "../db/db-boards.js";
|
|
22
|
+
import { createFeedRepository } from "../db/db-feed.js";
|
|
23
|
+
import { createFlowRepository } from "../db/db-flows.js";
|
|
24
|
+
import { createNodeIndexRepository } from "../db/db-indexing.js";
|
|
25
|
+
import { createOAuthClientStore } from "../db/db-oauth.js";
|
|
26
|
+
import { createPromptRepository } from "../db/db-prompts.js";
|
|
27
|
+
import { createDocumentConverter } from "../document-converter/document-converter.js";
|
|
28
|
+
import { createFlowRuntime } from "../flow-runtime/flow-runtime.js";
|
|
29
|
+
import { createIndexQueue, IndexMessage } from "../index-queue/index-queue.js";
|
|
30
|
+
import { createJsonSchemaValidator } from "../json-schema/json-schema.js";
|
|
31
|
+
import { createOpenId } from "../openid/openid.js";
|
|
32
|
+
import { createPortalTokenStore } from "../portal-tokens/portal-tokens.js";
|
|
33
|
+
import { createRemoteTools } from "../remote-tools/remote-tools.js";
|
|
34
|
+
import { createSemanticIndex } from "../semantic-index/semantic-index.js";
|
|
35
|
+
import { createSessionCodec } from "../session-cookie/session-cookie.js";
|
|
36
|
+
import { createToolSourcePolicy } from "../tool-source-policy/tool-source-policy.js";
|
|
37
|
+
// biome-ignore lint/performance/noBarrelFile: The package entry must expose Wrangler's named Workflow class.
|
|
38
|
+
export { IntelFlowWorkflow } from "./cloudflare-flow-workflow.js";
|
|
39
|
+
const workerPrefixes = ["/api", "/auth", "/.well-known", "/mcp", "/health"];
|
|
40
|
+
export function isSpaNavigation(request) {
|
|
41
|
+
if (request.method !== "GET" && request.method !== "HEAD")
|
|
42
|
+
return false;
|
|
43
|
+
if (!request.headers.get("accept")?.includes("text/html"))
|
|
44
|
+
return false;
|
|
45
|
+
const { pathname } = new URL(request.url);
|
|
46
|
+
return !workerPrefixes.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
|
|
47
|
+
}
|
|
48
|
+
// A missing variable would otherwise surface deep inside a request as an opaque URL or crypto
|
|
49
|
+
// error. Naming it once here keeps a misconfigured deployment diagnosable.
|
|
50
|
+
const requiredVariables = [
|
|
51
|
+
"GATE_URL",
|
|
52
|
+
"GATE_SERVICE_KEY",
|
|
53
|
+
"INTEL_URL",
|
|
54
|
+
"INTEL_SESSION_SECRET",
|
|
55
|
+
"TOOL_SOURCE_ORIGINS",
|
|
56
|
+
];
|
|
57
|
+
function assertConfigured(env) {
|
|
58
|
+
const missing = requiredVariables.filter((name) => !env[name]?.trim());
|
|
59
|
+
if (missing.length) {
|
|
60
|
+
throw new IntelError(500, "configuration_missing", `Intel is missing required configuration: ${missing.join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The two ports that call Workers AI, and the one condition that decides whether they exist at all
|
|
65
|
+
* (#800).
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ **No gateway id, no ports.** The obvious spelling would have been to pass the gateway when
|
|
68
|
+
* there is one and call Workers AI directly when there is not — and that is precisely the silent
|
|
69
|
+
* bypass the spend limit exists against: the deployment keeps embedding on every save and every
|
|
70
|
+
* `reindex`, the gateway counts none of it, and nobody notices because everything works. A missing
|
|
71
|
+
* semantic index is a state this repository already knows and describes (see the bindings table in
|
|
72
|
+
* the package README); an unmetered one is not.
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ Both ports fall away together, because both call Workers AI: the embeddings through `run` and
|
|
75
|
+
* the attachment conversion through `toMarkdown`. Repairing only the first half would have left a
|
|
76
|
+
* second door open on the same account.
|
|
77
|
+
*/
|
|
78
|
+
export function createAiPorts(env) {
|
|
79
|
+
const gateway = env.AI_GATEWAY_ID?.trim();
|
|
80
|
+
if (!env.AI || !gateway)
|
|
81
|
+
return { semantic: undefined, converter: undefined };
|
|
82
|
+
return {
|
|
83
|
+
// Vectorize stays its own condition: without an index to write into there is nothing to embed
|
|
84
|
+
// for, and that half of the answer predates this ticket.
|
|
85
|
+
semantic: env.SEARCH
|
|
86
|
+
? createSemanticIndex({ ai: env.AI, index: env.SEARCH, gateway })
|
|
87
|
+
: undefined,
|
|
88
|
+
converter: createDocumentConverter({ ai: env.AI, gateway }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export default {
|
|
92
|
+
async fetch(request, env, _context) {
|
|
93
|
+
if (env.ASSETS && isSpaNavigation(request)) {
|
|
94
|
+
return await env.ASSETS.fetch(new Request(new URL("/", request.url), { method: "GET" }));
|
|
95
|
+
}
|
|
96
|
+
assertConfigured(env);
|
|
97
|
+
const now = () => new Date();
|
|
98
|
+
const { semantic } = createAiPorts(env);
|
|
99
|
+
// Built before the tree because the tree has to ask it one question: who calls into a folder
|
|
100
|
+
// from outside it. The repository knows no service, so this stays one direction of dependency.
|
|
101
|
+
const flowRepository = createFlowRepository({ db: env.DB, now });
|
|
102
|
+
const nodeRepository = createNodeRepository({ db: env.DB, now });
|
|
103
|
+
const auditRepository = createAuditRepository({ db: env.DB, now });
|
|
104
|
+
const feedRepository = createFeedRepository({ db: env.DB, now });
|
|
105
|
+
const boardRepository = createBoardRepository({ db: env.DB, now });
|
|
106
|
+
const contentStore = createContentStore(env.CONTENT);
|
|
107
|
+
const gate = createGateClient({ url: env.GATE_URL, serviceKey: env.GATE_SERVICE_KEY });
|
|
108
|
+
const oauth = createOpenId({
|
|
109
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
110
|
+
allowInsecure: env.ALLOW_INSECURE_OAUTH === "true",
|
|
111
|
+
});
|
|
112
|
+
const intelResource = `${env.INTEL_URL.replace(/\/+$/, "")}/mcp`;
|
|
113
|
+
const portalUrl = env.MCP_PORTAL_URL?.trim() || null;
|
|
114
|
+
const portalTokens = createPortalTokenStore({
|
|
115
|
+
db: env.DB,
|
|
116
|
+
secret: env.INTEL_SESSION_SECRET,
|
|
117
|
+
crypto,
|
|
118
|
+
now,
|
|
119
|
+
});
|
|
120
|
+
const tools = createTools({
|
|
121
|
+
portalUrl,
|
|
122
|
+
remote: createRemoteTools({ fetch: globalThis.fetch.bind(globalThis) }),
|
|
123
|
+
tokens: portalTokens,
|
|
124
|
+
refresh: async (token) => {
|
|
125
|
+
try {
|
|
126
|
+
const refreshed = await oauth.refresh({
|
|
127
|
+
issuer: token.issuer,
|
|
128
|
+
clientId: token.clientId,
|
|
129
|
+
resource: token.resource,
|
|
130
|
+
refreshToken: token.refreshToken ?? "",
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
...token,
|
|
134
|
+
accessToken: refreshed.accessToken,
|
|
135
|
+
refreshToken: refreshed.refreshToken ?? token.refreshToken,
|
|
136
|
+
expiresAt: now().getTime() + Math.max(30, refreshed.expiresIn) * 1_000,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// A refusal from the portal's issuer means the grant is gone; the caller drops the token
|
|
141
|
+
// and asks the user to reconnect rather than retrying a dead credential.
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
now,
|
|
146
|
+
validate: createJsonSchemaValidator(),
|
|
147
|
+
sourceAllowed: createToolSourcePolicy(env.TOOL_SOURCE_ORIGINS),
|
|
148
|
+
fingerprint: async (value) => {
|
|
149
|
+
return await sha256Hex(crypto, JSON.stringify(value));
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
const nodes = createNodes({
|
|
153
|
+
repository: nodeRepository,
|
|
154
|
+
content: contentStore,
|
|
155
|
+
id: ulid,
|
|
156
|
+
now,
|
|
157
|
+
// ⚠️ The REPOSITORY, not the board service — that is what keeps this from being a cycle. The
|
|
158
|
+
// board service needs the node service to create a task; the node service needs only the two
|
|
159
|
+
// reads below to file one.
|
|
160
|
+
// ⚠️ `"write"` for the same reason as below: this answers "may this actor file a card here",
|
|
161
|
+
// not "may they look at it". Reading it with `"read"` would let somebody nest a card under a
|
|
162
|
+
// task on a board they may only look at.
|
|
163
|
+
boardOfTask: async (who, taskId) => (await boardRepository.taskRow(who, taskId, "write"))?.boardId ?? null,
|
|
164
|
+
attachToBoard: async (who, taskId, boardId, occurredAt) => {
|
|
165
|
+
// ⚠️ `"write"` — the actor is filing a task on this board, and the board's own creation
|
|
166
|
+
// path is a write too. `"read"` here would put back exactly the hole #649 found.
|
|
167
|
+
const columns = await boardRepository.columnsOf(who, boardId, "write");
|
|
168
|
+
// `null` means the parent is not a board this actor may read — then there is nothing to
|
|
169
|
+
// file the task on, and the node stands on its own. That is the folder case, and it is
|
|
170
|
+
// silent on purpose: a `task` filed in a plain folder is unusual, not an error.
|
|
171
|
+
if (columns === null)
|
|
172
|
+
return;
|
|
173
|
+
const status = columns[0] ?? FIRST_DEFAULT_COLUMN;
|
|
174
|
+
await boardRepository.attach({
|
|
175
|
+
nodeId: taskId,
|
|
176
|
+
boardId,
|
|
177
|
+
status,
|
|
178
|
+
assigneeId: null,
|
|
179
|
+
labels: [],
|
|
180
|
+
startDate: null,
|
|
181
|
+
dueDate: null,
|
|
182
|
+
dependsOn: null,
|
|
183
|
+
position: await boardRepository.nextPosition(boardId, status),
|
|
184
|
+
occurredAt,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
indexing: createIndexQueue(env.INDEXING),
|
|
188
|
+
semantic,
|
|
189
|
+
externalFlowCallers: async (who, folderId) => await flowRepository.externalCallers(who, folderId),
|
|
190
|
+
flowNodeReferences: async (who, folderId) => await flowRepository.nodeReferences(who, folderId),
|
|
191
|
+
// ⚠️ The real one, like its two neighbours: the refusal is only worth anything if it reads
|
|
192
|
+
// real published graphs and the real visibility predicate (#457).
|
|
193
|
+
flowsUsingNode: async (who, nodeId) => await flowRepository.flowsUsingNode({ ...who, canRun: true }, nodeId),
|
|
194
|
+
// ⚠️ `canExecute: true` is not a permission being granted here — the servers list touches
|
|
195
|
+
// nothing and executes nothing. It exists because `ToolActor` carries the flag for the one
|
|
196
|
+
// method that needs it, and a `false` would read as if listing were gated on execution.
|
|
197
|
+
toolServers: async (who) => {
|
|
198
|
+
const catalog = await tools.servers({ id: who.id, email: who.email, canExecute: true });
|
|
199
|
+
return catalog.items.map((server) => server.handle);
|
|
200
|
+
},
|
|
201
|
+
hash: async (content) => {
|
|
202
|
+
const source = typeof content === "string"
|
|
203
|
+
? new TextEncoder().encode(content)
|
|
204
|
+
: Uint8Array.from(content).buffer;
|
|
205
|
+
return await sha256Hex(crypto, source);
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const auth = createBrowserAuth({
|
|
209
|
+
baseUrl: env.INTEL_URL,
|
|
210
|
+
gateUrl: env.GATE_URL,
|
|
211
|
+
resource: intelResource,
|
|
212
|
+
oauth,
|
|
213
|
+
clients: createOAuthClientStore(env.DB),
|
|
214
|
+
sessions: createSessionCodec({ secret: env.INTEL_SESSION_SECRET, crypto }),
|
|
215
|
+
portalUrl,
|
|
216
|
+
portalTokens,
|
|
217
|
+
now,
|
|
218
|
+
});
|
|
219
|
+
const flows = createFlows({
|
|
220
|
+
repository: flowRepository,
|
|
221
|
+
runtime: createFlowRuntime(env.FLOWS),
|
|
222
|
+
id: ulid,
|
|
223
|
+
now,
|
|
224
|
+
folderAccess: async (actor, folderId) => await nodes.folderAccess(actor, folderId),
|
|
225
|
+
// ⚠️ Straight to the repository, past the node service: this one question is asked WITHOUT the
|
|
226
|
+
// visibility predicate on purpose (#733), and a service method taking an actor would invite
|
|
227
|
+
// somebody to hand one in.
|
|
228
|
+
parentArchivedAt: async (folderId) => await nodeRepository.parentArchivedAt(folderId),
|
|
229
|
+
nodeChildren: async (actor, folderId, limit) => await nodes.childrenBounded(actor, { parentId: folderId, limit }),
|
|
230
|
+
// The tree's own visibility lookup, unchanged on the way through: the relation graph, the
|
|
231
|
+
// requirements list and every tree link of every run read this one answer, so none of
|
|
232
|
+
// them can be kinder than the others.
|
|
233
|
+
visibleNodes: async (actor, nodeId) => await nodes.visibleNode(actor, nodeId),
|
|
234
|
+
// The outer ring of the relation graph (#825), straight to the repository like
|
|
235
|
+
// `parentArchivedAt` above: the graph has already proven a node visible through `visibleNodes`
|
|
236
|
+
// by the time it asks either of these, so there is nothing left for a service-level wrapper to
|
|
237
|
+
// add beyond what `nodes.ts:1537` already checks for `node_effective_access_list`.
|
|
238
|
+
nodeShareable: async (actor, nodeId) => await nodeRepository.can(actor, nodeId, "share"),
|
|
239
|
+
nodeEffectiveAccess: async (nodeId) => await nodeRepository.listEffectiveAccess(nodeId),
|
|
240
|
+
/**
|
|
241
|
+
* ⚠️ The fingerprint of a whole SURFACE, not of one tool (#489). A step names a server and
|
|
242
|
+
* optionally the functions of it it may use, so what publishing freezes is that set — every
|
|
243
|
+
* covered function's own fingerprint, sorted so the order the portal happens to answer in
|
|
244
|
+
* cannot change the result, and hashed into one value.
|
|
245
|
+
*
|
|
246
|
+
* `null` in three cases, and all three have to refuse a publish: the catalog is unreachable,
|
|
247
|
+
* the server offers this user nothing, or a function the step explicitly allows is gone. The
|
|
248
|
+
* last one matters most — a narrowed step whose function disappeared is not a step that may
|
|
249
|
+
* quietly fall back to the rest of the server.
|
|
250
|
+
*/
|
|
251
|
+
toolSurfaceFingerprint: async (actor, server, allow) => {
|
|
252
|
+
const catalog = await tools
|
|
253
|
+
.catalog({ id: actor.id, email: actor.email, canExecute: true })
|
|
254
|
+
.catch(() => null);
|
|
255
|
+
if (!catalog)
|
|
256
|
+
return null;
|
|
257
|
+
const reachable = new Map(catalog.items.map((item) => [item.name, item.fingerprint]));
|
|
258
|
+
const covered = allow === null
|
|
259
|
+
? [...reachable.keys()].filter((name) => serverOf(name, [server]) !== null)
|
|
260
|
+
: allow;
|
|
261
|
+
if (covered.length === 0)
|
|
262
|
+
return null;
|
|
263
|
+
const parts = [];
|
|
264
|
+
for (const name of [...new Set(covered)].sort()) {
|
|
265
|
+
const fingerprint = reachable.get(name);
|
|
266
|
+
if (!fingerprint)
|
|
267
|
+
return null;
|
|
268
|
+
parts.push(`${name}:${fingerprint}`);
|
|
269
|
+
}
|
|
270
|
+
// ⚠️ The handle and the narrowing mode are part of what is hashed, not just the covered
|
|
271
|
+
// functions. Without them two different surfaces could collide: a server whose only
|
|
272
|
+
// function is allowed explicitly would hash the same as the same server left open, and the
|
|
273
|
+
// difference between "this one function" and "whatever this server offers" is exactly what
|
|
274
|
+
// publishing is meant to freeze.
|
|
275
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode([server, allow === null ? "*" : "allow", ...parts].join("\n")));
|
|
276
|
+
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
277
|
+
},
|
|
278
|
+
// Which of the named servers this user does NOT reach. A server is reached when the live
|
|
279
|
+
// catalog carries at least one function of it — the same live answer the tool surface uses,
|
|
280
|
+
// never a mirrored table.
|
|
281
|
+
// ⚠️ The catalog error is NOT caught here, and that is the whole point. Answering an
|
|
282
|
+
// unreadable portal with "all of them are missing" turns an outage into a sentence about
|
|
283
|
+
// PERMISSIONS — the reader goes asking for access they already have, while the portal is
|
|
284
|
+
// simply down. `tools.catalog` already tells the two apart (`tool_catalog_unreadable`,
|
|
285
|
+
// `portal_not_connected`), and letting that answer through is the same rule the tool surface
|
|
286
|
+
// keeps one screen up: a sentence more specific than the evidence sends the reader looking in
|
|
287
|
+
// the wrong place.
|
|
288
|
+
unavailableServers: async (actor, servers) => {
|
|
289
|
+
if (servers.length === 0)
|
|
290
|
+
return [];
|
|
291
|
+
const catalog = await tools.catalog({ id: actor.id, email: actor.email, canExecute: true });
|
|
292
|
+
return servers.filter((server) => !catalog.items.some((item) => serverOf(item.name, [server]) !== null));
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
return await createIntel({
|
|
296
|
+
baseUrl: env.INTEL_URL,
|
|
297
|
+
gateUrl: env.GATE_URL,
|
|
298
|
+
gate,
|
|
299
|
+
nodes,
|
|
300
|
+
flows,
|
|
301
|
+
tools,
|
|
302
|
+
audit: createAudit({ audit: auditRepository }),
|
|
303
|
+
feed: createFeed({ feed: feedRepository }),
|
|
304
|
+
/**
|
|
305
|
+
* The slash-command catalogue (#775). It reads its own rows through its own repository and
|
|
306
|
+
* borrows exactly one thing from the node service: reading one document, with the
|
|
307
|
+
* authorization walk that service already performs. A second reader into R2 here would be a
|
|
308
|
+
* second place for that ACL to be applied, and the one that gets it wrong is the one nobody
|
|
309
|
+
* looks at.
|
|
310
|
+
*/
|
|
311
|
+
prompts: createPrompts({
|
|
312
|
+
repository: createPromptRepository({ db: env.DB, now }),
|
|
313
|
+
// ⚠️ Handed on whole, and nothing is decided here. Turning a stored version into readable
|
|
314
|
+
// text is business behaviour, and this shell maps bindings — it does not get to know what a
|
|
315
|
+
// BlockNote payload is.
|
|
316
|
+
readDocument: async (actor, nodeId) => await nodes.get(actor, nodeId),
|
|
317
|
+
}),
|
|
318
|
+
boards: createBoards({
|
|
319
|
+
boards: boardRepository,
|
|
320
|
+
nodes,
|
|
321
|
+
now,
|
|
322
|
+
/**
|
|
323
|
+
* The seam to gate's directory (#700, D70).
|
|
324
|
+
*
|
|
325
|
+
* ⚠️ BUILT HERE AND NOWHERE ELSE, because this is the only place that legitimately holds
|
|
326
|
+
* the caller's bearer. `Actor` carries the resolved identity, never the credential that
|
|
327
|
+
* produced it, and the board service must stay that way: it narrows what gate offers, it
|
|
328
|
+
* does not get to speak for anybody.
|
|
329
|
+
*
|
|
330
|
+
* ⚠️ The token is resolved LAZILY and at most once. Two doors lead in and they answer
|
|
331
|
+
* differently — `/mcp` and the API carry an `Authorization` header, the browser carries a
|
|
332
|
+
* cookie the session has to be asked about — and doing that work eagerly would cost a
|
|
333
|
+
* session read on every request that never opens a picker.
|
|
334
|
+
*
|
|
335
|
+
* ⚠️ No token means an EMPTY answer, never a throw. A picker with no names shows nothing;
|
|
336
|
+
* a board that refuses to load because gate was slow is the worse outcome, and it would
|
|
337
|
+
* take the whole screen with it.
|
|
338
|
+
*/
|
|
339
|
+
directory: (() => {
|
|
340
|
+
let pending = null;
|
|
341
|
+
const token = () => {
|
|
342
|
+
pending ??= (async () => bearer(request.headers) ?? (await auth.resolve(request.headers))?.bearer ?? null)();
|
|
343
|
+
return pending;
|
|
344
|
+
};
|
|
345
|
+
return {
|
|
346
|
+
async search(query) {
|
|
347
|
+
const value = await token();
|
|
348
|
+
if (!value)
|
|
349
|
+
return [];
|
|
350
|
+
return await gate.directory.search(value, { q: query });
|
|
351
|
+
},
|
|
352
|
+
async resolve(ids) {
|
|
353
|
+
const value = await token();
|
|
354
|
+
if (!value)
|
|
355
|
+
return [];
|
|
356
|
+
return await gate.directory.resolve(value, { ids });
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
})(),
|
|
360
|
+
}),
|
|
361
|
+
bundle: createBundle({
|
|
362
|
+
repository: nodeRepository,
|
|
363
|
+
flows: flowRepository,
|
|
364
|
+
content: contentStore,
|
|
365
|
+
id: ulid,
|
|
366
|
+
now,
|
|
367
|
+
hash: async (content) => {
|
|
368
|
+
const source = typeof content === "string"
|
|
369
|
+
? new TextEncoder().encode(content)
|
|
370
|
+
: Uint8Array.from(content).buffer;
|
|
371
|
+
return await sha256Hex(crypto, source);
|
|
372
|
+
},
|
|
373
|
+
indexing: createIndexQueue(env.INDEXING),
|
|
374
|
+
}),
|
|
375
|
+
auth,
|
|
376
|
+
}).fetch(request);
|
|
377
|
+
},
|
|
378
|
+
async queue(batch, env) {
|
|
379
|
+
const { semantic, converter } = createAiPorts(env);
|
|
380
|
+
const indexing = createIndexing({
|
|
381
|
+
repository: createNodeIndexRepository(env.DB),
|
|
382
|
+
content: createContentStore(env.CONTENT),
|
|
383
|
+
semantic,
|
|
384
|
+
converter,
|
|
385
|
+
hash: async (content) => await sha256Hex(crypto, content),
|
|
386
|
+
now: () => new Date(),
|
|
387
|
+
});
|
|
388
|
+
for (const message of batch.messages) {
|
|
389
|
+
const parsed = IndexMessage.safeParse(message.body);
|
|
390
|
+
if (!parsed.success) {
|
|
391
|
+
console.warn("Intel discarded an invalid indexing queue message");
|
|
392
|
+
message.ack();
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
await indexing.index(parsed.data.versionId);
|
|
397
|
+
message.ack();
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (error instanceof PermanentIndexingError) {
|
|
401
|
+
console.warn("Intel discarded a permanently unindexable node version", {
|
|
402
|
+
versionId: parsed.data.versionId,
|
|
403
|
+
error: error.message,
|
|
404
|
+
});
|
|
405
|
+
message.ack();
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
console.error("Intel indexing failed; Cloudflare retry policy will apply", {
|
|
409
|
+
versionId: parsed.data.versionId,
|
|
410
|
+
error: error instanceof Error ? error.message : "indexing_failed",
|
|
411
|
+
});
|
|
412
|
+
message.retry();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { R2Bucket } from "../content/content.types.js";
|
|
2
|
+
import type { D1Database } from "../db/db.types.js";
|
|
3
|
+
import type { MarkdownAiBinding } from "../document-converter/document-converter.types.js";
|
|
4
|
+
import type { WorkflowBinding } from "../flow-runtime/flow-runtime.types.js";
|
|
5
|
+
import type { IndexMessage } from "../index-queue/index-queue.js";
|
|
6
|
+
import type { QueueBatch, QueueProducer } from "../index-queue/index-queue.types.js";
|
|
7
|
+
import type { VectorizeBinding, WorkersAiBinding } from "../semantic-index/semantic-index.types.js";
|
|
8
|
+
export interface CloudflareEnv {
|
|
9
|
+
ASSETS?: {
|
|
10
|
+
fetch(request: Request): Promise<Response>;
|
|
11
|
+
};
|
|
12
|
+
DB: D1Database;
|
|
13
|
+
CONTENT: R2Bucket;
|
|
14
|
+
INDEXING: QueueProducer<IndexMessage>;
|
|
15
|
+
FLOWS: WorkflowBinding;
|
|
16
|
+
AI?: WorkersAiBinding & MarkdownAiBinding;
|
|
17
|
+
SEARCH?: VectorizeBinding;
|
|
18
|
+
GATE_URL: string;
|
|
19
|
+
GATE_SERVICE_KEY: string;
|
|
20
|
+
INTEL_URL: string;
|
|
21
|
+
INTEL_SESSION_SECRET: string;
|
|
22
|
+
TOOL_SOURCE_ORIGINS: string;
|
|
23
|
+
MCP_PORTAL_URL?: string;
|
|
24
|
+
ALLOW_INSECURE_OAUTH?: string;
|
|
25
|
+
AI_GATEWAY_ID?: string;
|
|
26
|
+
FLOW_RUN_STALL_TIMEOUT?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CloudflareExecutionContext {
|
|
29
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
30
|
+
}
|
|
31
|
+
export type IndexQueueBatch = QueueBatch<unknown>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|