okengine 0.14.1 → 0.16.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/package.json +3 -4
- package/site/content/docs/elements/ai.mdx +2 -1
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +3 -0
- package/site/content/docs/elements/flow.mdx +9 -0
- package/site/content/docs/elements/gate.mdx +80 -22
- package/site/content/docs/elements/signal.mdx +4 -4
- package/site/content/docs/elements/store.mdx +84 -27
- package/site/content/docs/elements/vault.mdx +4 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +28 -21
- package/site/content/docs/reference/fx.mdx +29 -18
- package/site/content/docs/reference/security.md +2 -2
- package/src/auth/api-key-sql.test.ts +47 -0
- package/src/auth/api-key-sql.ts +254 -0
- package/src/auth/api-keys.ts +273 -14
- package/src/auth/auth.test.ts +56 -0
- package/src/auth/bindings.ts +14 -5
- package/src/auth/config.ts +5 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/auth/index.ts +25 -1
- package/src/auth/schema.ts +8 -0
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/attach-host-console.test.ts +40 -2
- package/src/cli/attach-host-console.ts +9 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +17 -2
- package/src/compiler/extract.test.ts +409 -33
- package/src/compiler/extract.ts +318 -67
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/access.test.ts +116 -8
- package/src/console/server/access.ts +70 -23
- package/src/console/server/app.ts +1 -0
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/dev-identities.test.ts +127 -0
- package/src/console/server/dev-identities.ts +229 -0
- package/src/console/server/flows-invoke.test.ts +21 -4
- package/src/console/server/flows.ts +233 -98
- package/src/console/server/gates.test.ts +24 -0
- package/src/console/server/gates.ts +8 -21
- package/src/console/server/index.ts +1 -0
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/live.test.ts +26 -1
- package/src/console/server/live.ts +4 -2
- package/src/console/server/operator-db.ts +2 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +37 -55
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
- package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
- package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
- package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
- package/src/console/ui-next/dist/index.html +12 -10
- package/src/console/ui-next/index.html +3 -3
- package/src/console/ui-next/src/client.ts +202 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
- package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
- package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
- package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
- package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
- package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
- package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
- package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
- package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
- package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
- package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
- package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
- package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
- package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
- package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
- package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
- package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
- package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
- package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
- package/src/console/ui-next/src/lib/document-meta.ts +2 -1
- package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
- package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
- package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
- package/src/console/ui-next/src/lib/shortcut.ts +3 -0
- package/src/console/ui-next/src/router.tsx +10 -1
- package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
- package/src/console/ui-next/ui-next-seed-store.ts +43 -15
- package/src/console/xss.gate.test.ts +1 -0
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +3 -0
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/types.ts +10 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +38 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/store/cache.ts +1 -1
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +18 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +260 -41
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-select.types.test.ts +71 -0
- package/src/elements/store/sql-session.ts +135 -35
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +17 -0
- package/src/http.ts +20 -3
- package/src/index.ts +11 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/api-key-host-persist.test.ts +143 -0
- package/src/kernel/app.ts +145 -16
- package/src/kernel/auth-resolve.ts +10 -5
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +49 -9
- package/src/kernel/boot.test.ts +5 -5
- package/src/kernel/boot.ts +2 -2
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +12 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-auth-keys.test.ts +88 -0
- package/src/kernel/fx-auth-keys.ts +198 -0
- package/src/kernel/fx.test.ts +17 -1
- package/src/kernel/fx.ts +59 -18
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +4 -4
- package/src/kernel/pipeline-api-key.test.ts +192 -7
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/pipeline.ts +4 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/types.ts +22 -3
- package/src/release/build-lib.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +8 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fx.auth` key-management methods — session-only, attenuated, one store.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AUTH_API_KEYS_RESOURCE,
|
|
7
|
+
createApiKey,
|
|
8
|
+
getApiKey,
|
|
9
|
+
listApiKeys,
|
|
10
|
+
revokeApiKey,
|
|
11
|
+
rotateApiKey,
|
|
12
|
+
toApiKeyPublicRow,
|
|
13
|
+
updateApiKey,
|
|
14
|
+
type ApiKeyPublicRow,
|
|
15
|
+
type ApiKeyStore,
|
|
16
|
+
type CreatedApiKey,
|
|
17
|
+
} from "../auth/api-keys.ts";
|
|
18
|
+
import { parseDurationMs } from "../elements/clock/duration.ts";
|
|
19
|
+
import type { EffectKind } from "./effects.ts";
|
|
20
|
+
import { fail, type FlowFailure } from "./errors.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Identity bag on {@link FxAuth} before key-management methods attach.
|
|
24
|
+
* Passed into {@link createFx} / {@link attach}.
|
|
25
|
+
*/
|
|
26
|
+
export interface FxAuthIdentity {
|
|
27
|
+
/** User-plane subject id, or null when unauthenticated. */
|
|
28
|
+
readonly userId: string | null;
|
|
29
|
+
/** Granted scopes for the live principal. */
|
|
30
|
+
readonly scopes: ReadonlySet<string>;
|
|
31
|
+
/** Whether the identity has completed verification (email / MFA). */
|
|
32
|
+
readonly verified?: boolean;
|
|
33
|
+
/** Authenticating API key id when Bearer was a key secret; otherwise null. */
|
|
34
|
+
readonly apiKeyId?: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Options for {@link FxAuth.createApiKey}. */
|
|
38
|
+
export interface FxCreateApiKeyInput {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly scopes: readonly string[];
|
|
41
|
+
readonly expiresIn?: string;
|
|
42
|
+
readonly ipAllowlist?: readonly string[];
|
|
43
|
+
readonly rateLimit?: { max: number; per: string } | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Options for {@link FxAuth.updateApiKey}. */
|
|
47
|
+
export interface FxUpdateApiKeyInput {
|
|
48
|
+
readonly name?: string;
|
|
49
|
+
readonly scopes?: readonly string[];
|
|
50
|
+
readonly expiresIn?: string;
|
|
51
|
+
readonly ipAllowlist?: readonly string[];
|
|
52
|
+
readonly rateLimit?: { max: number; per: string } | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Create result for `fx.auth.createApiKey` / `rotateApiKey`. */
|
|
56
|
+
export interface FxCreatedApiKey {
|
|
57
|
+
readonly key: ApiKeyPublicRow;
|
|
58
|
+
readonly secret: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Session-only key management attached onto {@link FxAuthIdentity}.
|
|
63
|
+
* Methods throw {@link FlowFailure} values (`Unauthorized` / `Forbidden`).
|
|
64
|
+
*/
|
|
65
|
+
export interface FxAuthKeyMethods {
|
|
66
|
+
createApiKey(input: FxCreateApiKeyInput): Promise<FxCreatedApiKey>;
|
|
67
|
+
listApiKeys(): Promise<readonly ApiKeyPublicRow[]>;
|
|
68
|
+
revokeApiKey(id: string): Promise<ApiKeyPublicRow>;
|
|
69
|
+
rotateApiKey(id: string): Promise<FxCreatedApiKey>;
|
|
70
|
+
updateApiKey(id: string, input: FxUpdateApiKeyInput): Promise<ApiKeyPublicRow>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Dependencies for {@link attach}. */
|
|
74
|
+
export interface AttachAuthKeyMethodsOptions {
|
|
75
|
+
readonly auth: FxAuthIdentity;
|
|
76
|
+
readonly store: ApiKeyStore | undefined;
|
|
77
|
+
readonly now: () => number;
|
|
78
|
+
readonly gated: <T>(kind: EffectKind, resource: string, body: () => T | Promise<T>) => Promise<T>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function sessionOnly(auth: FxAuthIdentity): FlowFailure | null {
|
|
82
|
+
if (auth.apiKeyId) {
|
|
83
|
+
return fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "session_only" });
|
|
84
|
+
}
|
|
85
|
+
if (!auth.userId) {
|
|
86
|
+
return fail("Unauthorized", {});
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function requireStore(store: ApiKeyStore | undefined): ApiKeyStore {
|
|
92
|
+
if (!store) {
|
|
93
|
+
throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "no_store" });
|
|
94
|
+
}
|
|
95
|
+
return store;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function requireOwned(
|
|
99
|
+
store: ApiKeyStore,
|
|
100
|
+
id: string,
|
|
101
|
+
userId: string,
|
|
102
|
+
): ReturnType<typeof getApiKey> {
|
|
103
|
+
const row = getApiKey(store, id);
|
|
104
|
+
if (!row || row.creatorId !== userId) {
|
|
105
|
+
throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "not_owner" });
|
|
106
|
+
}
|
|
107
|
+
return row;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function expiresAtFrom(expiresIn: string | undefined, now: () => number): number | undefined {
|
|
111
|
+
if (expiresIn === undefined) return undefined;
|
|
112
|
+
return now() + parseDurationMs(expiresIn);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Attach create / list / revoke / rotate / update onto a live identity bag.
|
|
117
|
+
*
|
|
118
|
+
* @param options - Auth bag + store + capability gate
|
|
119
|
+
*/
|
|
120
|
+
export function attach(options: AttachAuthKeyMethodsOptions): FxAuthIdentity & FxAuthKeyMethods {
|
|
121
|
+
const { auth, now, gated } = options;
|
|
122
|
+
|
|
123
|
+
const methods: FxAuthKeyMethods = {
|
|
124
|
+
createApiKey(input) {
|
|
125
|
+
return gated("write", AUTH_API_KEYS_RESOURCE, async () => {
|
|
126
|
+
const denied = sessionOnly(auth);
|
|
127
|
+
if (denied) throw denied;
|
|
128
|
+
const store = requireStore(options.store);
|
|
129
|
+
const created: CreatedApiKey = await createApiKey(store, {
|
|
130
|
+
plane: "user",
|
|
131
|
+
name: input.name,
|
|
132
|
+
scopes: input.scopes,
|
|
133
|
+
creatorId: auth.userId!,
|
|
134
|
+
creatorScopes: auth.scopes,
|
|
135
|
+
expiresAt: expiresAtFrom(input.expiresIn, now) ?? null,
|
|
136
|
+
rateLimit: input.rateLimit ?? null,
|
|
137
|
+
ipAllowlist: input.ipAllowlist,
|
|
138
|
+
now,
|
|
139
|
+
});
|
|
140
|
+
return { key: toApiKeyPublicRow(created.row), secret: created.secret };
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
listApiKeys() {
|
|
144
|
+
return gated("read", AUTH_API_KEYS_RESOURCE, async () => {
|
|
145
|
+
const denied = sessionOnly(auth);
|
|
146
|
+
if (denied) throw denied;
|
|
147
|
+
const store = requireStore(options.store);
|
|
148
|
+
return listApiKeys(store, auth.userId!).map(toApiKeyPublicRow);
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
revokeApiKey(id) {
|
|
152
|
+
return gated("write", AUTH_API_KEYS_RESOURCE, async () => {
|
|
153
|
+
const denied = sessionOnly(auth);
|
|
154
|
+
if (denied) throw denied;
|
|
155
|
+
const store = requireStore(options.store);
|
|
156
|
+
requireOwned(store, id, auth.userId!);
|
|
157
|
+
const row = revokeApiKey(store, id, now);
|
|
158
|
+
if (!row) throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "not_owner" });
|
|
159
|
+
return toApiKeyPublicRow(row);
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
rotateApiKey(id) {
|
|
163
|
+
return gated("write", AUTH_API_KEYS_RESOURCE, async () => {
|
|
164
|
+
const denied = sessionOnly(auth);
|
|
165
|
+
if (denied) throw denied;
|
|
166
|
+
const store = requireStore(options.store);
|
|
167
|
+
requireOwned(store, id, auth.userId!);
|
|
168
|
+
const rotated = await rotateApiKey(store, id, { now });
|
|
169
|
+
if (!rotated)
|
|
170
|
+
throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "not_owner" });
|
|
171
|
+
return { key: toApiKeyPublicRow(rotated.row), secret: rotated.secret };
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
updateApiKey(id, input) {
|
|
175
|
+
return gated("write", AUTH_API_KEYS_RESOURCE, async () => {
|
|
176
|
+
const denied = sessionOnly(auth);
|
|
177
|
+
if (denied) throw denied;
|
|
178
|
+
const store = requireStore(options.store);
|
|
179
|
+
const existing = requireOwned(store, id, auth.userId!);
|
|
180
|
+
if (!existing)
|
|
181
|
+
throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "not_owner" });
|
|
182
|
+
const ceiling = new Set([...existing.creatorScopes].filter((s) => auth.scopes.has(s)));
|
|
183
|
+
const row = updateApiKey(store, id, {
|
|
184
|
+
ceiling,
|
|
185
|
+
name: input.name,
|
|
186
|
+
scopes: input.scopes,
|
|
187
|
+
expiresAt: expiresAtFrom(input.expiresIn, now),
|
|
188
|
+
rateLimit: input.rateLimit,
|
|
189
|
+
ipAllowlist: input.ipAllowlist,
|
|
190
|
+
});
|
|
191
|
+
if (!row) throw fail("Forbidden", { gate: AUTH_API_KEYS_RESOURCE, reason: "not_owner" });
|
|
192
|
+
return toApiKeyPublicRow(row);
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return Object.assign(auth, methods);
|
|
198
|
+
}
|
package/src/kernel/fx.test.ts
CHANGED
|
@@ -432,7 +432,23 @@ describe("fx — wholesale swap", () => {
|
|
|
432
432
|
t: (key) => key,
|
|
433
433
|
locale: "en",
|
|
434
434
|
id: () => "fixed-id",
|
|
435
|
-
auth: {
|
|
435
|
+
auth: {
|
|
436
|
+
userId: "u1",
|
|
437
|
+
scopes: new Set(["a"]),
|
|
438
|
+
createApiKey: async () => {
|
|
439
|
+
throw new Error("auth.createApiKey must not be used");
|
|
440
|
+
},
|
|
441
|
+
listApiKeys: async () => [],
|
|
442
|
+
revokeApiKey: async () => {
|
|
443
|
+
throw new Error("auth.revokeApiKey must not be used");
|
|
444
|
+
},
|
|
445
|
+
rotateApiKey: async () => {
|
|
446
|
+
throw new Error("auth.rotateApiKey must not be used");
|
|
447
|
+
},
|
|
448
|
+
updateApiKey: async () => {
|
|
449
|
+
throw new Error("auth.updateApiKey must not be used");
|
|
450
|
+
},
|
|
451
|
+
},
|
|
436
452
|
operator: { id: null },
|
|
437
453
|
principal: { userId: "u1", operatorId: null, scopes: new Set(["a"]) },
|
|
438
454
|
tenant: { id: "t1" },
|
package/src/kernel/fx.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
IndexStoreFxHandle,
|
|
21
21
|
KvStoreDecl,
|
|
22
22
|
KvStoreFxHandle,
|
|
23
|
+
SelectFromBuilder,
|
|
23
24
|
SelectOrderBuilder,
|
|
24
25
|
SqlStoreDecl,
|
|
25
26
|
StoreDecl,
|
|
@@ -27,6 +28,8 @@ import type {
|
|
|
27
28
|
StoreRuntime,
|
|
28
29
|
SqlStoreHandle,
|
|
29
30
|
} from "../elements/store.ts";
|
|
31
|
+
import { rlsIdentityFromAuth } from "../elements/store.ts";
|
|
32
|
+
import type { RlsIdentity } from "../drivers/pg-rls.ts";
|
|
30
33
|
import type { SqlRow } from "../drivers/types.ts";
|
|
31
34
|
import type { SignalDecl, SignalRuntime } from "../elements/signal.ts";
|
|
32
35
|
import type { DeadLetter, SignalEmitOptions } from "../drivers/signal-types.ts";
|
|
@@ -34,6 +37,8 @@ import type { VaultActor, VaultAdapter, VaultRuntime } from "../elements/vault.t
|
|
|
34
37
|
import type { ChannelRuntime } from "../elements/channel.ts";
|
|
35
38
|
import type { AiRuntime } from "../elements/ai.ts";
|
|
36
39
|
import { parseDurationMs } from "../elements/clock/duration.ts";
|
|
40
|
+
import type { ApiKeyStore } from "../auth/api-keys.ts";
|
|
41
|
+
import type { FxAuthIdentity, FxAuthKeyMethods } from "./fx-auth-keys.ts";
|
|
37
42
|
import { createCapabilityToken, type CapabilityToken } from "./capability.ts";
|
|
38
43
|
import { createEffectLedger, recordEffect, reversibilityOf, type EffectLedger } from "./effects.ts";
|
|
39
44
|
import { resolveDurationMs } from "./elapsed.ts";
|
|
@@ -74,6 +79,13 @@ async function loadRunsWindow(): Promise<typeof import("../runs/window.ts")> {
|
|
|
74
79
|
/** Resource ref Flows declare to read the Runs store via {@link Fx.runs}. */
|
|
75
80
|
export const RUNS_RESOURCE = "runs";
|
|
76
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Resource ref Flows declare for {@link Fx.auth} key management.
|
|
84
|
+
* Same string as `src/auth/api-keys.ts` — defined here so the HMAC / key-store
|
|
85
|
+
* module stays off the edge and Store-only graphs.
|
|
86
|
+
*/
|
|
87
|
+
export const AUTH_API_KEYS_RESOURCE = "auth:api-keys";
|
|
88
|
+
|
|
77
89
|
/**
|
|
78
90
|
* Capability ref for {@link Fx.deadLetters} — `signal:<name>`, never a store facet.
|
|
79
91
|
*
|
|
@@ -101,13 +113,10 @@ export function resolveStoreRef(ref: NamedRef | { readonly ref: ResourceRef }):
|
|
|
101
113
|
return ref.name as ResourceRef;
|
|
102
114
|
}
|
|
103
115
|
|
|
116
|
+
export type { FxAuthIdentity } from "./fx-auth-keys.ts";
|
|
117
|
+
|
|
104
118
|
/** Auth principal on the user plane. */
|
|
105
|
-
export interface FxAuth {
|
|
106
|
-
readonly userId: string | null;
|
|
107
|
-
readonly scopes: ReadonlySet<string>;
|
|
108
|
-
/** Whether the identity has completed verification (email / MFA). */
|
|
109
|
-
readonly verified?: boolean;
|
|
110
|
-
}
|
|
119
|
+
export interface FxAuth extends FxAuthIdentity, FxAuthKeyMethods {}
|
|
111
120
|
|
|
112
121
|
/** Operator principal on the Console plane. */
|
|
113
122
|
export interface FxOperator {
|
|
@@ -835,8 +844,10 @@ export interface CreateFxOptions {
|
|
|
835
844
|
readonly capability?: CapabilityToken;
|
|
836
845
|
/** Injectable clock for timestamps / `fx.clock.now`. */
|
|
837
846
|
readonly now?: () => number;
|
|
838
|
-
/** Auth principal. */
|
|
839
|
-
readonly auth?:
|
|
847
|
+
/** Auth principal bag (key methods attach at create time). */
|
|
848
|
+
readonly auth?: FxAuthIdentity;
|
|
849
|
+
/** Shared API key store for {@link Fx.auth} key methods. */
|
|
850
|
+
readonly apiKeyStore?: ApiKeyStore;
|
|
840
851
|
/** Operator principal. */
|
|
841
852
|
readonly operator?: FxOperator;
|
|
842
853
|
/**
|
|
@@ -911,6 +922,12 @@ export interface CreateFxOptions {
|
|
|
911
922
|
readonly runId?: string;
|
|
912
923
|
/** Reveal PII through the store runtime (requires `pii:reveal` upstream). */
|
|
913
924
|
readonly revealPii?: boolean;
|
|
925
|
+
/** Trigger gate names for RLS (`oke.gate` = first policy/public). */
|
|
926
|
+
readonly rlsGateNames?: readonly string[];
|
|
927
|
+
/** Skip RLS stamp (Operator / `bypassGates` / operator-plane flows). */
|
|
928
|
+
readonly rlsBypass?: boolean;
|
|
929
|
+
/** Forced bag (Console / Call API). Wins over {@link rlsGateNames}. */
|
|
930
|
+
readonly rls?: import("../drivers/pg-rls.ts").RlsIdentity;
|
|
914
931
|
/**
|
|
915
932
|
* Active journal session when the flow is durable. Every `fx` call is
|
|
916
933
|
* recorded; `step` / `sleep` replay from the journal on resume.
|
|
@@ -993,10 +1010,16 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
993
1010
|
}
|
|
994
1011
|
const cacheStore = new Map<string, unknown>();
|
|
995
1012
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1013
|
+
// Computed stem — a static import would pin HMAC / api-keys on every createFx.
|
|
1014
|
+
const auth: FxAuth = lazyRequire<typeof import("./fx-auth-keys.ts")>(
|
|
1015
|
+
import.meta.dir,
|
|
1016
|
+
["fx", "auth", "keys"].join("-"),
|
|
1017
|
+
).attach({
|
|
1018
|
+
auth: options.auth ?? { userId: null, scopes: new Set() },
|
|
1019
|
+
store: options.apiKeyStore,
|
|
1020
|
+
now,
|
|
1021
|
+
gated,
|
|
1022
|
+
});
|
|
1000
1023
|
const operator: FxOperator = options.operator ?? { id: null };
|
|
1001
1024
|
const tenant: FxTenant = options.tenant ?? { id: null };
|
|
1002
1025
|
const defaultLocale = options.i18n?.defaultLocale ?? "en";
|
|
@@ -1025,6 +1048,19 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1025
1048
|
} satisfies FxPrincipal);
|
|
1026
1049
|
const journal = options.journal;
|
|
1027
1050
|
|
|
1051
|
+
function rlsInvokeContext(): { rls?: RlsIdentity } {
|
|
1052
|
+
if (options.rlsBypass === true) return {};
|
|
1053
|
+
if (options.rls) return { rls: options.rls };
|
|
1054
|
+
const identity = rlsIdentityFromAuth({
|
|
1055
|
+
userId: auth.userId,
|
|
1056
|
+
scopes: auth.scopes,
|
|
1057
|
+
gateNames: options.rlsGateNames ?? [],
|
|
1058
|
+
bypass: false,
|
|
1059
|
+
operator: false,
|
|
1060
|
+
});
|
|
1061
|
+
return identity ? { rls: identity } : {};
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1028
1064
|
async function gated<T>(
|
|
1029
1065
|
kind: Parameters<CapabilityToken["assert"]>[0],
|
|
1030
1066
|
resource: string,
|
|
@@ -1150,9 +1186,9 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1150
1186
|
get driverId() {
|
|
1151
1187
|
return cached?.driverId ?? "memory";
|
|
1152
1188
|
},
|
|
1153
|
-
select(columns
|
|
1189
|
+
select: ((columns?: unknown) => {
|
|
1154
1190
|
return {
|
|
1155
|
-
from(table) {
|
|
1191
|
+
from(table: unknown) {
|
|
1156
1192
|
const run = (plan: {
|
|
1157
1193
|
where?: unknown;
|
|
1158
1194
|
orders?: readonly unknown[];
|
|
@@ -1161,7 +1197,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1161
1197
|
}): Promise<SqlRow[]> =>
|
|
1162
1198
|
gatedTable("read", table, async () => {
|
|
1163
1199
|
const h = await ensure();
|
|
1164
|
-
const from = h.select(columns).from(table);
|
|
1200
|
+
const from = h.select(columns).from(table) as SelectFromBuilder;
|
|
1165
1201
|
const filtered = plan.where === undefined ? from : from.where(plan.where);
|
|
1166
1202
|
const ordered =
|
|
1167
1203
|
plan.orders === undefined ? filtered : filtered.orderBy(...plan.orders);
|
|
@@ -1185,7 +1221,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1185
1221
|
});
|
|
1186
1222
|
|
|
1187
1223
|
return {
|
|
1188
|
-
where(where) {
|
|
1224
|
+
where(where: unknown) {
|
|
1189
1225
|
return {
|
|
1190
1226
|
...tail({ where }),
|
|
1191
1227
|
orderBy: (...orders: readonly unknown[]) => tail({ where, orders }),
|
|
@@ -1198,13 +1234,16 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1198
1234
|
offset(n: number) {
|
|
1199
1235
|
return run({ offset: n });
|
|
1200
1236
|
},
|
|
1201
|
-
then(
|
|
1237
|
+
then(
|
|
1238
|
+
onfulfilled: (value: SqlRow[]) => unknown,
|
|
1239
|
+
onrejected?: (reason: unknown) => unknown,
|
|
1240
|
+
) {
|
|
1202
1241
|
return run({}).then(onfulfilled, onrejected);
|
|
1203
1242
|
},
|
|
1204
1243
|
};
|
|
1205
1244
|
},
|
|
1206
1245
|
};
|
|
1207
|
-
},
|
|
1246
|
+
}) as SqlStoreHandle["select"],
|
|
1208
1247
|
insert(table) {
|
|
1209
1248
|
return {
|
|
1210
1249
|
values(row) {
|
|
@@ -1342,6 +1381,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1342
1381
|
cache.handle = await runtime.open(decl, {
|
|
1343
1382
|
effects: options.effects ?? {},
|
|
1344
1383
|
revealPii: options.revealPii,
|
|
1384
|
+
...rlsInvokeContext(),
|
|
1345
1385
|
});
|
|
1346
1386
|
}
|
|
1347
1387
|
return cache.handle;
|
|
@@ -1361,6 +1401,7 @@ export function createFxContext(options: CreateFxOptions): FxContext {
|
|
|
1361
1401
|
{
|
|
1362
1402
|
effects: options.effects ?? {},
|
|
1363
1403
|
revealPii: options.revealPii,
|
|
1404
|
+
...rlsInvokeContext(),
|
|
1364
1405
|
},
|
|
1365
1406
|
{
|
|
1366
1407
|
gate: gated,
|
|
@@ -190,11 +190,11 @@ const charge = flow("horizontal.charge", {
|
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
const bindings: Binding[] = [
|
|
193
|
-
{ trigger: http.get("/ping").
|
|
193
|
+
{ trigger: http.get("/ping").public(), flow: ping as AnyFlowDef },
|
|
194
194
|
{ trigger: http.get("/rate").gate(rateGate), flow: rate as AnyFlowDef },
|
|
195
|
-
{ trigger: http.post("/write").
|
|
196
|
-
{ trigger: http.post("/emit").
|
|
197
|
-
{ trigger: http.post("/charge").
|
|
195
|
+
{ trigger: http.post("/write").public(), flow: write as AnyFlowDef },
|
|
196
|
+
{ trigger: http.post("/emit").public(), flow: emit as AnyFlowDef },
|
|
197
|
+
{ trigger: http.post("/charge").public(), flow: charge as AnyFlowDef },
|
|
198
198
|
];
|
|
199
199
|
|
|
200
200
|
const app = oke({
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branded sentinel for `http.get()` with no path — never `"/"`.
|
|
3
|
+
* Lives in a leaf module so `oke()` can refuse leftovers without pulling
|
|
4
|
+
* the HTTP trigger constructors onto a Store-only graph.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Unresolved path token. {@link stampHttpPath} replaces it from the file tree. */
|
|
8
|
+
export const HTTP_PATH_PENDING = "__oke_http_path_pending__" as const;
|
|
9
|
+
|
|
10
|
+
/** Type of {@link HTTP_PATH_PENDING}. */
|
|
11
|
+
export type HttpPathPending = typeof HTTP_PATH_PENDING;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* True when an HTTP trigger still carries the unresolved path sentinel.
|
|
15
|
+
*
|
|
16
|
+
* @param path - Trigger path
|
|
17
|
+
*/
|
|
18
|
+
export function isPendingHttpPath(path: string): boolean {
|
|
19
|
+
return path === HTTP_PATH_PENDING;
|
|
20
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* apps do not pay for the five-verb CRUD helper.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { GATE_PUBLIC_NAME } from "../elements/gate/flatten.ts";
|
|
6
7
|
import {
|
|
7
8
|
createGateAttach,
|
|
8
9
|
createHttpTrigger,
|
|
@@ -47,6 +48,9 @@ export function httpResource<P extends string>(
|
|
|
47
48
|
{ trigger: verb("DELETE", id, false), flow: ops.remove },
|
|
48
49
|
],
|
|
49
50
|
gate: createGateAttach((next) => httpResource(path, ops, next, isLive), gates),
|
|
51
|
+
public() {
|
|
52
|
+
return httpResource(path, ops, [...gates, GATE_PUBLIC_NAME], isLive);
|
|
53
|
+
},
|
|
50
54
|
live() {
|
|
51
55
|
return httpResource(path, ops, gates, true);
|
|
52
56
|
},
|
|
@@ -83,12 +83,12 @@ describe("fx.json.stream HTTP SSE", () => {
|
|
|
83
83
|
test("encodes token chunks as SSE and leaves JSON flows buffered", async () => {
|
|
84
84
|
const smart = ai.model("smart");
|
|
85
85
|
on(
|
|
86
|
-
http.get("/chat").
|
|
86
|
+
http.get("/chat").public(),
|
|
87
87
|
flow("chat.stream", {
|
|
88
88
|
do: (_input, fx) => fx.json.stream(fx.stream(smart, { prompt: "hi" })),
|
|
89
89
|
}),
|
|
90
90
|
);
|
|
91
|
-
on(http.get("/ping").
|
|
91
|
+
on(http.get("/ping").public(), flow("ping", { do: () => ({ ok: true as const }) }));
|
|
92
92
|
|
|
93
93
|
const app = oke({
|
|
94
94
|
name: "http-stream",
|
|
@@ -111,7 +111,7 @@ describe("fx.json.stream HTTP SSE", () => {
|
|
|
111
111
|
let providerAborted = false;
|
|
112
112
|
const smart = ai.model("smart");
|
|
113
113
|
on(
|
|
114
|
-
http.get("/slow").
|
|
114
|
+
http.get("/slow").public(),
|
|
115
115
|
flow("chat.slow", {
|
|
116
116
|
do: (_input, fx) => fx.json.stream(fx.stream(smart, { prompt: "long" })),
|
|
117
117
|
}),
|
|
@@ -139,7 +139,7 @@ describe("fx.json.stream HTTP SSE", () => {
|
|
|
139
139
|
await runs.open();
|
|
140
140
|
const smart = ai.model("smart");
|
|
141
141
|
on(
|
|
142
|
-
http.get("/chat").
|
|
142
|
+
http.get("/chat").public(),
|
|
143
143
|
flow("chat.stream", {
|
|
144
144
|
do: (_input, fx) => fx.json.stream(fx.stream(smart, { prompt: "hi" })),
|
|
145
145
|
}),
|