@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,284 @@
|
|
|
1
|
+
import { calculatePKCECodeChallenge, randomPKCECodeVerifier, randomState } from "openid-client";
|
|
2
|
+
import { IntelError } from "../shared/intel-error/intel-error.js";
|
|
3
|
+
import { reportUnexpectedError } from "../shared/report-unexpected-error/report-unexpected-error.js";
|
|
4
|
+
import { SafeReturnPath } from "../shared/safe-return-path/safe-return-path.js";
|
|
5
|
+
const cookieName = "intel_session";
|
|
6
|
+
// ⚠️ The cookie outlives the handoff on purpose. Ten minutes is right for something carrying a PKCE
|
|
7
|
+
// verifier, but the portal's consent screen can stand open longer — it waits for every server to
|
|
8
|
+
// connect, and a person steps away meanwhile. An expired handoff that is still *readable* tells the
|
|
9
|
+
// callback what was being attempted, where the person wanted to go, and whether their Intel session
|
|
10
|
+
// survived; a cookie that vanished with it leaves nothing but a raw error page (#95). `expiresAt`
|
|
11
|
+
// remains the only authority — a stale verifier is never exchanged, no matter how long it is legible.
|
|
12
|
+
const HandoffValidSeconds = 10 * 60;
|
|
13
|
+
const HandoffCookieSeconds = 60 * 60;
|
|
14
|
+
// What an authorization server answers when `prompt=none` would have worked, but only with somebody
|
|
15
|
+
// looking at a screen. It is the expected answer to a silent attempt, not a broken deployment.
|
|
16
|
+
const InteractionRequired = new Set([
|
|
17
|
+
"login_required",
|
|
18
|
+
"interaction_required",
|
|
19
|
+
"consent_required",
|
|
20
|
+
"account_selection_required",
|
|
21
|
+
]);
|
|
22
|
+
// Fixed markers, never the portal's own words. `error_description` is a sentence written by the
|
|
23
|
+
// authorization server about somebody else's policy: putting it in the URL would hand whatever
|
|
24
|
+
// renders the page next a string Intel does not control. Which marker matters — a refusal is about
|
|
25
|
+
// access, running out of time is not, and a screen that confuses the two sends the reader hunting
|
|
26
|
+
// in the wrong place (#93).
|
|
27
|
+
function withConnectError(returnTo, code = "portal_sign_in_refused") {
|
|
28
|
+
const url = new URL(returnTo, "https://intel.invalid");
|
|
29
|
+
url.searchParams.set("connectError", code);
|
|
30
|
+
return `${url.pathname}${url.search}`;
|
|
31
|
+
}
|
|
32
|
+
function cookieValue(headers) {
|
|
33
|
+
const values = headers.get("cookie")?.split(";") ?? [];
|
|
34
|
+
for (const value of values) {
|
|
35
|
+
const [name, ...rest] = value.trim().split("=");
|
|
36
|
+
if (name === cookieName)
|
|
37
|
+
return rest.join("=") || null;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function sessionCookie(value, secure, maxAge) {
|
|
42
|
+
return `${cookieName}=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${Math.max(0, Math.floor(maxAge))}${secure ? "; Secure" : ""}`;
|
|
43
|
+
}
|
|
44
|
+
function redirect(location, cookie) {
|
|
45
|
+
const headers = new Headers({ location, "cache-control": "no-store" });
|
|
46
|
+
if (cookie)
|
|
47
|
+
headers.set("set-cookie", cookie);
|
|
48
|
+
return new Response(null, { status: 302, headers });
|
|
49
|
+
}
|
|
50
|
+
async function pkce() {
|
|
51
|
+
for (;;) {
|
|
52
|
+
const verifier = randomPKCECodeVerifier();
|
|
53
|
+
const challenge = await calculatePKCECodeChallenge(verifier);
|
|
54
|
+
if (/^[A-Za-z0-9]/.test(challenge))
|
|
55
|
+
return { verifier, challenge };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function createBrowserAuth(deps) {
|
|
59
|
+
const baseUrl = deps.baseUrl.replace(/\/+$/, "");
|
|
60
|
+
const gateIssuer = deps.gateUrl.replace(/\/+$/, "");
|
|
61
|
+
const redirectUri = `${baseUrl}/auth/callback`;
|
|
62
|
+
const secure = new URL(baseUrl).protocol === "https:";
|
|
63
|
+
async function clientId(issuer, resource, scope) {
|
|
64
|
+
// ⚠️ The suffix is a registration generation, and it is what makes a fix to the registration
|
|
65
|
+
// reach an installation that already has one. A stored client is reused forever, so #97 —
|
|
66
|
+
// clients registered without the `resource` parameter and therefore never given a refresh
|
|
67
|
+
// token — would have been repaired in code and nowhere else: every existing deployment would
|
|
68
|
+
// keep handing back the broken registration it made months ago. Raise this whenever the
|
|
69
|
+
// REGISTRATION changes shape, never for anything else; each raise costs one silent
|
|
70
|
+
// re-registration per issuer and resource.
|
|
71
|
+
const registrationKey = `${issuer}#${resource}#r2`;
|
|
72
|
+
const stored = await deps.clients.get(registrationKey, redirectUri);
|
|
73
|
+
if (stored)
|
|
74
|
+
return stored;
|
|
75
|
+
// A refused registration becomes a named state instead of a bare 500 (#93). The conversion
|
|
76
|
+
// must not eat the reason: an IntelError is an expected refusal nobody logs, so the library's
|
|
77
|
+
// own exception is written here — it is the only place that still holds it.
|
|
78
|
+
const registered = await deps.oauth
|
|
79
|
+
.register({
|
|
80
|
+
issuer,
|
|
81
|
+
redirectUri,
|
|
82
|
+
clientName: "Intel",
|
|
83
|
+
resource,
|
|
84
|
+
...(scope ? { scope } : {}),
|
|
85
|
+
})
|
|
86
|
+
.catch((error) => {
|
|
87
|
+
reportUnexpectedError(error);
|
|
88
|
+
throw new IntelError(502, "client_registration_failed", "The authorization server refused the client registration");
|
|
89
|
+
});
|
|
90
|
+
return await deps.clients.put({
|
|
91
|
+
issuer: registrationKey,
|
|
92
|
+
redirectUri,
|
|
93
|
+
clientId: registered,
|
|
94
|
+
createdAt: deps.now().toISOString(),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// The connection handoff parks the Intel session inside the pending cookie, so every way out of
|
|
98
|
+
// the callback has to put it back — the portal attempt must never cost somebody their Intel login.
|
|
99
|
+
async function restore(user, returnTo, location) {
|
|
100
|
+
const remaining = (user.expiresAt - deps.now().getTime()) / 1_000;
|
|
101
|
+
if (remaining <= 0) {
|
|
102
|
+
return redirect(`/auth/login?returnTo=${encodeURIComponent(returnTo)}`, sessionCookie("", secure, 0));
|
|
103
|
+
}
|
|
104
|
+
return redirect(location, sessionCookie(await deps.sessions.seal(user), secure, remaining));
|
|
105
|
+
}
|
|
106
|
+
async function userSession(client, tokens) {
|
|
107
|
+
return {
|
|
108
|
+
version: 1,
|
|
109
|
+
kind: "user",
|
|
110
|
+
clientId: client,
|
|
111
|
+
accessToken: tokens.accessToken,
|
|
112
|
+
expiresAt: deps.now().getTime() + Math.max(30, tokens.expiresIn) * 1_000,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
async login(requestUrl) {
|
|
117
|
+
const returnTo = SafeReturnPath.catch("/").parse(requestUrl.searchParams.get("returnTo") ?? "/");
|
|
118
|
+
const client = await clientId(gateIssuer, deps.resource);
|
|
119
|
+
const { verifier: codeVerifier, challenge: codeChallenge } = await pkce();
|
|
120
|
+
const state = randomState();
|
|
121
|
+
const pending = {
|
|
122
|
+
version: 1,
|
|
123
|
+
kind: "pending",
|
|
124
|
+
clientId: client,
|
|
125
|
+
codeVerifier,
|
|
126
|
+
state,
|
|
127
|
+
returnTo,
|
|
128
|
+
expiresAt: deps.now().getTime() + 10 * 60 * 1_000,
|
|
129
|
+
};
|
|
130
|
+
const authorizationUrl = await deps.oauth.authorizationUrl({
|
|
131
|
+
issuer: gateIssuer,
|
|
132
|
+
clientId: client,
|
|
133
|
+
redirectUri,
|
|
134
|
+
resource: deps.resource,
|
|
135
|
+
codeChallenge,
|
|
136
|
+
state,
|
|
137
|
+
scope: "openid profile email",
|
|
138
|
+
});
|
|
139
|
+
return redirect(authorizationUrl.href, sessionCookie(await deps.sessions.seal(pending), secure, 10 * 60));
|
|
140
|
+
},
|
|
141
|
+
async connect(requestUrl, headers, userId) {
|
|
142
|
+
const encoded = cookieValue(headers);
|
|
143
|
+
const user = encoded ? await deps.sessions.open(encoded) : null;
|
|
144
|
+
if (user?.kind !== "user" || user.expiresAt <= deps.now().getTime() + 30_000) {
|
|
145
|
+
throw new IntelError(401, "authentication_required", "Authentication is required");
|
|
146
|
+
}
|
|
147
|
+
const returnTo = SafeReturnPath.catch("/tools").parse(requestUrl.searchParams.get("returnTo") ?? "/tools");
|
|
148
|
+
// Gate is the OIDC provider Cloudflare Access consumes, so whoever holds a valid Intel
|
|
149
|
+
// session is already the person the portal would ask about. `prompt=none` says exactly that:
|
|
150
|
+
// answer from the session that exists, and refuse rather than show anybody a login (#60).
|
|
151
|
+
const silent = requestUrl.searchParams.get("silent") === "1";
|
|
152
|
+
const resumed = requestUrl.searchParams.get("resumed") === "1";
|
|
153
|
+
if (!deps.portalUrl) {
|
|
154
|
+
throw new IntelError(503, "portal_not_configured", "No MCP portal is configured for this deployment");
|
|
155
|
+
}
|
|
156
|
+
const discovered = await deps.oauth.discoverResource(deps.portalUrl).catch(() => {
|
|
157
|
+
throw new IntelError(409, "portal_oauth_unavailable", "The MCP portal does not publish usable OAuth metadata");
|
|
158
|
+
});
|
|
159
|
+
const connectionClientId = await clientId(discovered.issuer, discovered.resource, discovered.scope);
|
|
160
|
+
const { verifier: codeVerifier, challenge: codeChallenge } = await pkce();
|
|
161
|
+
const state = randomState();
|
|
162
|
+
const pending = {
|
|
163
|
+
version: 1,
|
|
164
|
+
kind: "connection-pending",
|
|
165
|
+
clientId: connectionClientId,
|
|
166
|
+
codeVerifier,
|
|
167
|
+
state,
|
|
168
|
+
returnTo,
|
|
169
|
+
issuer: discovered.issuer,
|
|
170
|
+
resource: discovered.resource,
|
|
171
|
+
userId,
|
|
172
|
+
user,
|
|
173
|
+
silent,
|
|
174
|
+
resumed,
|
|
175
|
+
expiresAt: deps.now().getTime() + HandoffValidSeconds * 1_000,
|
|
176
|
+
};
|
|
177
|
+
const authorizationUrl = await deps.oauth.authorizationUrl({
|
|
178
|
+
issuer: discovered.issuer,
|
|
179
|
+
clientId: connectionClientId,
|
|
180
|
+
redirectUri,
|
|
181
|
+
resource: discovered.resource,
|
|
182
|
+
codeChallenge,
|
|
183
|
+
state,
|
|
184
|
+
...(discovered.scope ? { scope: discovered.scope } : {}),
|
|
185
|
+
...(silent ? { prompt: "none" } : {}),
|
|
186
|
+
});
|
|
187
|
+
return redirect(authorizationUrl.href, sessionCookie(await deps.sessions.seal(pending), secure, HandoffCookieSeconds));
|
|
188
|
+
},
|
|
189
|
+
async callback(requestUrl, headers) {
|
|
190
|
+
const encoded = cookieValue(headers);
|
|
191
|
+
const pending = encoded ? await deps.sessions.open(encoded) : null;
|
|
192
|
+
if (pending?.kind !== "pending" && pending?.kind !== "connection-pending") {
|
|
193
|
+
throw new IntelError(400, "oauth_session_invalid", "OAuth session is missing or expired");
|
|
194
|
+
}
|
|
195
|
+
// A handoff that ran out while somebody was still on the consent screen is not a failure they
|
|
196
|
+
// caused, and everything needed to carry on is in it: their parked Intel session and where
|
|
197
|
+
// they were going. `restore` decides which — it sends them back to the Gate login when that
|
|
198
|
+
// session is gone too, rather than into an attempt that could not finish (#95).
|
|
199
|
+
if (pending.kind === "connection-pending" && pending.expiresAt <= deps.now().getTime()) {
|
|
200
|
+
return await restore(pending.user, pending.returnTo, pending.resumed
|
|
201
|
+
? withConnectError(pending.returnTo, "portal_sign_in_expired")
|
|
202
|
+
: `/auth/connect?returnTo=${encodeURIComponent(pending.returnTo)}&resumed=1`);
|
|
203
|
+
}
|
|
204
|
+
// The Gate login handoff parks nothing, so an expired one has nowhere to return to.
|
|
205
|
+
if (pending.expiresAt <= deps.now().getTime()) {
|
|
206
|
+
throw new IntelError(400, "oauth_session_invalid", "OAuth session is missing or expired");
|
|
207
|
+
}
|
|
208
|
+
// ⚠️ An authorization server reports a refusal on the redirect URI, not by failing the token
|
|
209
|
+
// exchange. Reading it here is what keeps a refused silent attempt from surfacing as a raw
|
|
210
|
+
// OAuth error, and it is the only place that knows whether a visible attempt is still owed.
|
|
211
|
+
const refusal = requestUrl.searchParams.get("error");
|
|
212
|
+
if (pending.kind === "connection-pending" && refusal) {
|
|
213
|
+
// The silent attempt only asked whether the sign-in works without a screen. "Not without
|
|
214
|
+
// one" is an answer, so the visible flow runs once — and because that one is not silent, a
|
|
215
|
+
// second refusal ends in the message instead of a third attempt.
|
|
216
|
+
const visibleAttemptLeft = pending.silent && InteractionRequired.has(refusal);
|
|
217
|
+
return await restore(pending.user, pending.returnTo, visibleAttemptLeft
|
|
218
|
+
? `/auth/connect?returnTo=${encodeURIComponent(pending.returnTo)}`
|
|
219
|
+
: withConnectError(pending.returnTo));
|
|
220
|
+
}
|
|
221
|
+
// ⚠️ Everything below runs with the handoff READ, and that is the whole difference between
|
|
222
|
+
// this catch and the one on the route (#444): here a failure knows where the person was
|
|
223
|
+
// going, and for a connection handoff it still holds their parked Intel session. The route
|
|
224
|
+
// can only send somebody to a decided screen, so whatever can be answered here is answered
|
|
225
|
+
// here — the token exchange, the portal token write and the sealing of the new session all
|
|
226
|
+
// used to end as a bare 500 in JSON, in the middle of a sign-in.
|
|
227
|
+
try {
|
|
228
|
+
const tokens = await deps.oauth.exchange({
|
|
229
|
+
issuer: pending.kind === "connection-pending" ? pending.issuer : gateIssuer,
|
|
230
|
+
clientId: pending.clientId,
|
|
231
|
+
callbackUrl: requestUrl,
|
|
232
|
+
redirectUri,
|
|
233
|
+
resource: pending.kind === "connection-pending" ? pending.resource : deps.resource,
|
|
234
|
+
codeVerifier: pending.codeVerifier,
|
|
235
|
+
state: pending.state,
|
|
236
|
+
});
|
|
237
|
+
if (pending.kind === "connection-pending") {
|
|
238
|
+
await deps.portalTokens.write(pending.userId, {
|
|
239
|
+
accessToken: tokens.accessToken,
|
|
240
|
+
refreshToken: tokens.refreshToken ?? null,
|
|
241
|
+
expiresAt: deps.now().getTime() + Math.max(30, tokens.expiresIn) * 1_000,
|
|
242
|
+
issuer: pending.issuer,
|
|
243
|
+
clientId: pending.clientId,
|
|
244
|
+
resource: pending.resource,
|
|
245
|
+
});
|
|
246
|
+
return await restore(pending.user, pending.returnTo, pending.returnTo);
|
|
247
|
+
}
|
|
248
|
+
const session = await userSession(pending.clientId, tokens);
|
|
249
|
+
return redirect(pending.returnTo, sessionCookie(await deps.sessions.seal(session), secure, (session.expiresAt - deps.now().getTime()) / 1_000));
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
// The exception is the only thing that still holds the reason: what the reader gets is a
|
|
253
|
+
// fixed marker, and a marker names a KIND of failure, never its cause (#93).
|
|
254
|
+
reportUnexpectedError(error);
|
|
255
|
+
// `portal_sign_in_failed` is the marker `/auth/connect` already writes for exactly this
|
|
256
|
+
// situation, so nothing new is invented here and no screen has to learn a word. The Tools
|
|
257
|
+
// screen reads it as "the sign-in is broken", which is true of both handoffs — a Gate login
|
|
258
|
+
// that could not be exchanged is no more the reader's doing than a portal one.
|
|
259
|
+
if (pending.kind === "connection-pending") {
|
|
260
|
+
// Every other way out of the callback puts the parked Intel session back, and so does
|
|
261
|
+
// this one: the portal attempt must never cost somebody their Intel login.
|
|
262
|
+
return await restore(pending.user, pending.returnTo, withConnectError(pending.returnTo, "portal_sign_in_failed"));
|
|
263
|
+
}
|
|
264
|
+
// A Gate login handoff parks nothing, so there is no session to put back — and the cookie
|
|
265
|
+
// is spent either way: its verifier belongs to an authorization code that has now been
|
|
266
|
+
// used. Clearing it is what makes the next attempt start a fresh one instead of replaying
|
|
267
|
+
// this one.
|
|
268
|
+
return redirect(withConnectError(pending.returnTo, "portal_sign_in_failed"), sessionCookie("", secure, 0));
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
logout() {
|
|
272
|
+
return redirect("/", sessionCookie("", secure, 0));
|
|
273
|
+
},
|
|
274
|
+
async resolve(headers) {
|
|
275
|
+
const encoded = cookieValue(headers);
|
|
276
|
+
const session = encoded ? await deps.sessions.open(encoded) : null;
|
|
277
|
+
if (session?.kind !== "user")
|
|
278
|
+
return null;
|
|
279
|
+
return session.expiresAt > deps.now().getTime() + 30_000
|
|
280
|
+
? { bearer: session.accessToken }
|
|
281
|
+
: null;
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { PortalTokenStore } from "../tools/tools.types.js";
|
|
2
|
+
export interface OAuthTokens {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
expiresIn: number;
|
|
5
|
+
refreshToken?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface OAuthPort {
|
|
8
|
+
discoverResource(resourceUrl: string): Promise<{
|
|
9
|
+
resource: string;
|
|
10
|
+
issuer: string;
|
|
11
|
+
scope?: string;
|
|
12
|
+
}>;
|
|
13
|
+
register(input: {
|
|
14
|
+
issuer: string;
|
|
15
|
+
redirectUri: string;
|
|
16
|
+
clientName: string;
|
|
17
|
+
resource: string;
|
|
18
|
+
scope?: string;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
authorizationUrl(input: {
|
|
21
|
+
issuer: string;
|
|
22
|
+
clientId: string;
|
|
23
|
+
redirectUri: string;
|
|
24
|
+
resource: string;
|
|
25
|
+
codeChallenge: string;
|
|
26
|
+
state: string;
|
|
27
|
+
scope?: string;
|
|
28
|
+
prompt?: "none";
|
|
29
|
+
}): Promise<URL>;
|
|
30
|
+
exchange(input: {
|
|
31
|
+
issuer: string;
|
|
32
|
+
clientId: string;
|
|
33
|
+
callbackUrl: URL;
|
|
34
|
+
redirectUri: string;
|
|
35
|
+
resource: string;
|
|
36
|
+
codeVerifier: string;
|
|
37
|
+
state: string;
|
|
38
|
+
}): Promise<OAuthTokens>;
|
|
39
|
+
refresh(input: {
|
|
40
|
+
issuer: string;
|
|
41
|
+
clientId: string;
|
|
42
|
+
resource: string;
|
|
43
|
+
refreshToken: string;
|
|
44
|
+
}): Promise<OAuthTokens>;
|
|
45
|
+
}
|
|
46
|
+
export interface OAuthClientStore {
|
|
47
|
+
get(issuer: string, redirectUri: string): Promise<string | null>;
|
|
48
|
+
put(input: {
|
|
49
|
+
issuer: string;
|
|
50
|
+
redirectUri: string;
|
|
51
|
+
clientId: string;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
}): Promise<string>;
|
|
54
|
+
}
|
|
55
|
+
export interface UserAuthSession {
|
|
56
|
+
version: 1;
|
|
57
|
+
kind: "user";
|
|
58
|
+
clientId: string;
|
|
59
|
+
accessToken: string;
|
|
60
|
+
expiresAt: number;
|
|
61
|
+
}
|
|
62
|
+
export type AuthSession = {
|
|
63
|
+
version: 1;
|
|
64
|
+
kind: "pending";
|
|
65
|
+
clientId: string;
|
|
66
|
+
codeVerifier: string;
|
|
67
|
+
state: string;
|
|
68
|
+
returnTo: string;
|
|
69
|
+
expiresAt: number;
|
|
70
|
+
} | {
|
|
71
|
+
version: 1;
|
|
72
|
+
kind: "connection-pending";
|
|
73
|
+
clientId: string;
|
|
74
|
+
codeVerifier: string;
|
|
75
|
+
state: string;
|
|
76
|
+
returnTo: string;
|
|
77
|
+
issuer: string;
|
|
78
|
+
resource: string;
|
|
79
|
+
userId: string;
|
|
80
|
+
user: UserAuthSession;
|
|
81
|
+
silent: boolean;
|
|
82
|
+
resumed: boolean;
|
|
83
|
+
expiresAt: number;
|
|
84
|
+
} | UserAuthSession;
|
|
85
|
+
export interface SessionCodec {
|
|
86
|
+
seal(session: AuthSession): Promise<string>;
|
|
87
|
+
open(value: string): Promise<AuthSession | null>;
|
|
88
|
+
}
|
|
89
|
+
export interface BrowserAuthDeps {
|
|
90
|
+
baseUrl: string;
|
|
91
|
+
gateUrl: string;
|
|
92
|
+
resource: string;
|
|
93
|
+
oauth: OAuthPort;
|
|
94
|
+
clients: OAuthClientStore;
|
|
95
|
+
sessions: SessionCodec;
|
|
96
|
+
portalUrl: string | null;
|
|
97
|
+
portalTokens: Pick<PortalTokenStore, "write">;
|
|
98
|
+
now(): Date;
|
|
99
|
+
}
|
|
100
|
+
export interface BrowserAuth {
|
|
101
|
+
login(requestUrl: URL): Promise<Response>;
|
|
102
|
+
connect(requestUrl: URL, headers: Headers, userId: string): Promise<Response>;
|
|
103
|
+
callback(requestUrl: URL, headers: Headers): Promise<Response>;
|
|
104
|
+
logout(): Response;
|
|
105
|
+
resolve(headers: Headers): Promise<{
|
|
106
|
+
bearer: string;
|
|
107
|
+
} | null>;
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BoardDeps, BoardService } from "./boards.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* What a board with no configuration answers with. A board is usable the moment it is created, and
|
|
4
|
+
* nothing asks whoever made it to define columns before filing the first card.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ **This is the FALLBACK, not what a board made through the application gets.** Columns are
|
|
7
|
+
* shared data: two people on one board must read the same names, and the settings dialog edits one
|
|
8
|
+
* list. So the application writes them at creation, in the language of whoever created it (#675),
|
|
9
|
+
* and from then on they are data anybody can rename. This list only answers for a board nobody
|
|
10
|
+
* configured — one made over MCP, or one whose seeding did not land.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* ⚠️ **The four start coloured, and the colours mean something** (Jack's decision 2026-08-22): grey
|
|
14
|
+
* for what is only lying there, yellow for what is ready, orange for what is being worked on, green
|
|
15
|
+
* for what is done. That order is the one people already read without a legend, which is the whole
|
|
16
|
+
* reason a default is worth having — a board that starts grey teaches nothing, and one that starts
|
|
17
|
+
* in arbitrary colours teaches something false.
|
|
18
|
+
*
|
|
19
|
+
* They are a suggestion, not a setting: every one of them can be overwritten, and a board that has
|
|
20
|
+
* been recoloured keeps its own values.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_COLUMNS: readonly [{
|
|
23
|
+
readonly id: "backlog";
|
|
24
|
+
readonly title: "Backlog";
|
|
25
|
+
readonly terminal: false;
|
|
26
|
+
readonly color: "#9ca3af";
|
|
27
|
+
}, {
|
|
28
|
+
readonly id: "todo";
|
|
29
|
+
readonly title: "Offen";
|
|
30
|
+
readonly terminal: false;
|
|
31
|
+
readonly color: "#eab308";
|
|
32
|
+
}, {
|
|
33
|
+
readonly id: "doing";
|
|
34
|
+
readonly title: "Läuft";
|
|
35
|
+
readonly terminal: false;
|
|
36
|
+
readonly color: "#f97316";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "done";
|
|
39
|
+
readonly title: "Fertig";
|
|
40
|
+
readonly terminal: true;
|
|
41
|
+
readonly color: "#22c55e";
|
|
42
|
+
}];
|
|
43
|
+
export declare const FIRST_DEFAULT_COLUMN: "backlog";
|
|
44
|
+
export declare function createBoards(deps: BoardDeps): BoardService;
|