okengine 0.10.3 → 0.11.1
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 +76 -10
- package/site/content/docs/deployment/docker-swarm.mdx +48 -111
- package/site/content/docs/deployment/docker.mdx +43 -36
- package/site/content/docs/deployment/index.mdx +9 -9
- package/site/content/docs/deployment/kubernetes.mdx +4 -4
- package/site/content/docs/elements/ai.mdx +7 -7
- package/site/content/docs/elements/channel.mdx +9 -7
- package/site/content/docs/elements/clock.mdx +8 -8
- package/site/content/docs/elements/flow.mdx +46 -47
- package/site/content/docs/elements/gate.mdx +6 -5
- package/site/content/docs/elements/signal.mdx +2 -2
- package/site/content/docs/elements/store.mdx +71 -78
- package/site/content/docs/elements/vault.mdx +89 -70
- package/site/content/docs/get-started/basic-usage.mdx +8 -8
- package/site/content/docs/get-started/installation.mdx +36 -36
- package/site/content/docs/get-started/introduction.mdx +11 -0
- package/site/content/docs/get-started/why.mdx +2 -2
- package/site/content/docs/plugins/anonymous.mdx +1 -1
- package/site/content/docs/plugins/magic-link.mdx +11 -11
- package/site/content/docs/plugins/otp.mdx +2 -2
- package/site/content/docs/plugins/passkey.mdx +1 -1
- package/site/content/docs/plugins/two-factor.mdx +1 -1
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +1 -1
- package/site/content/docs/recipes/cockroachdb.mdx +2 -2
- package/site/content/docs/recipes/dragonfly.mdx +1 -1
- package/site/content/docs/recipes/index.mdx +1 -2
- package/site/content/docs/recipes/llama-cpp.mdx +21 -23
- package/site/content/docs/recipes/mailpit.mdx +11 -11
- package/site/content/docs/recipes/meilisearch.mdx +10 -10
- package/site/content/docs/recipes/meta.json +0 -1
- package/site/content/docs/recipes/ollama.mdx +1 -1
- package/site/content/docs/recipes/pgdog.mdx +12 -9
- package/site/content/docs/recipes/postgres.mdx +3 -3
- package/site/content/docs/recipes/redis.mdx +2 -2
- package/site/content/docs/recipes/rustfs.mdx +10 -7
- package/site/content/docs/recipes/sglang.mdx +1 -1
- package/site/content/docs/recipes/supabase-docker.mdx +1 -1
- package/site/content/docs/recipes/timescale.mdx +2 -2
- package/site/content/docs/recipes/valkey.mdx +1 -1
- package/site/content/docs/recipes/vllm.mdx +1 -1
- package/site/content/docs/recipes/yugabytedb.mdx +2 -2
- package/site/content/docs/reference/cli.md +61 -38
- package/site/content/docs/reference/configuration.mdx +145 -70
- package/site/content/docs/reference/environment-variables.mdx +36 -28
- package/site/content/docs/reference/errors.mdx +28 -29
- package/site/content/docs/reference/fx.mdx +11 -4
- package/site/content/docs/reference/i18n.mdx +17 -6
- package/site/content/docs/reference/index.mdx +5 -0
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/migrating-environments.mdx +158 -0
- package/site/content/docs/reference/plugins.mdx +2 -2
- package/src/auth/gate-auth.test.ts +12 -10
- package/src/cli/ai-setup/ai-setup.test.ts +27 -15
- package/src/cli/ai-setup/apply.ts +111 -15
- package/src/cli/ai-setup/catalog.ts +3 -3
- package/src/cli/ai-setup/index.ts +1 -1
- package/src/cli/ai-setup/prompts.ts +1 -1
- package/src/cli/ai-setup/recommend.test.ts +1 -1
- package/src/cli/ask-seed.test.ts +3 -3
- package/src/cli/build.ts +1 -1
- package/src/cli/db-auto-push.test.ts +6 -3
- package/src/cli/db-auto-push.ts +7 -5
- package/src/cli/db-seed.ts +7 -8
- package/src/cli/db.test.ts +70 -71
- package/src/cli/db.ts +86 -12
- package/src/cli/dev-controller.test.ts +35 -0
- package/src/cli/dev-controller.ts +298 -0
- package/src/cli/dev-controls.ts +11 -65
- package/src/cli/dev-db-push.test.ts +47 -15
- package/src/cli/dev-schema-sync.test.ts +11 -11
- package/src/cli/dev-schema-sync.ts +10 -10
- package/src/cli/dev-session-lock.test.ts +106 -0
- package/src/cli/dev-session-lock.ts +209 -0
- package/src/cli/dev.test.ts +166 -121
- package/src/cli/dev.ts +138 -192
- package/src/cli/doc-staleness.test.ts +3 -6
- package/src/cli/docker-clean.test.ts +33 -1
- package/src/cli/docker-clean.ts +28 -33
- package/src/cli/docker-cli.test.ts +12 -11
- package/src/cli/docker.ts +95 -20
- package/src/cli/doctor.test.ts +40 -0
- package/src/cli/doctor.ts +55 -15
- package/src/cli/drizzle-env.test.ts +11 -13
- package/src/cli/drizzle-env.ts +18 -11
- package/src/cli/ensure-drizzle-config.ts +11 -14
- package/src/cli/hero-meta.test.ts +15 -15
- package/src/cli/hero-meta.ts +10 -9
- package/src/cli/index.ts +36 -6
- package/src/cli/load-config.images.test.ts +16 -12
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +34 -28
- package/src/cli/meilisearch-local.ts +1 -2
- package/src/cli/mode.ts +7 -58
- package/src/cli/registry.ts +175 -19
- package/src/cli/resolve-dev-sql-env.test.ts +8 -16
- package/src/cli/resolve-dev-sql-env.ts +13 -15
- package/src/cli/safe-defaults.test.ts +28 -10
- package/src/cli/schema.ts +65 -16
- package/src/cli/test.ts +58 -0
- package/src/cli/tui/App.tsx +252 -0
- package/src/cli/tui/DevLive.tsx +116 -0
- package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
- package/src/cli/tui/components/HeaderBar.tsx +67 -0
- package/src/cli/tui/components/LogStream.tsx +47 -0
- package/src/cli/tui/components/PanelCard.tsx +39 -0
- package/src/cli/tui/components/SelectRow.tsx +37 -0
- package/src/cli/tui/components/SlashPalette.tsx +172 -0
- package/src/cli/tui/components/Spinner.tsx +32 -0
- package/src/cli/tui/components/StatusBar.tsx +51 -0
- package/src/cli/tui/components/StatusDot.tsx +37 -0
- package/src/cli/tui/components/TabBar.tsx +49 -0
- package/src/cli/tui/components/Wordmark.tsx +43 -0
- package/src/cli/tui/format.ts +38 -0
- package/src/cli/tui/keys.test.ts +18 -0
- package/src/cli/tui/keys.ts +26 -0
- package/src/cli/tui/launch.test.ts +18 -0
- package/src/cli/tui/launch.ts +57 -0
- package/src/cli/tui/panels/Dashboard.tsx +201 -0
- package/src/cli/tui/panels/Database.tsx +198 -0
- package/src/cli/tui/panels/Dev.tsx +257 -0
- package/src/cli/tui/panels/Docker.tsx +357 -0
- package/src/cli/tui/panels/Navigator.test.ts +22 -0
- package/src/cli/tui/panels/Navigator.tsx +213 -0
- package/src/cli/tui/slash-catalog.test.ts +31 -0
- package/src/cli/tui/slash-catalog.ts +184 -0
- package/src/cli/tui/slash-run.ts +54 -0
- package/src/cli/tui/state/ports.ts +95 -0
- package/src/cli/tui/theme.ts +24 -0
- package/src/cli/vault-cmd.test.ts +402 -0
- package/src/cli/vault-cmd.ts +727 -69
- package/src/cli/vault-secure-input.test.ts +56 -0
- package/src/cli/vault-secure-input.ts +124 -0
- package/src/compiler/effects-infer.ts +7 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +16 -23
- package/src/config/driver-defaults.ts +33 -63
- package/src/config/index.ts +169 -144
- package/src/config/resolve-driver.test.ts +14 -19
- package/src/console/server/app.ts +4 -2
- package/src/console/server/console.test.ts +58 -1
- package/src/console/server/flows.ts +22 -2
- package/src/console/server/index.ts +3 -0
- package/src/console/server/operator-db.test.ts +55 -13
- package/src/console/server/operator-db.ts +305 -263
- package/src/console/server/serve.ts +16 -7
- package/src/console/server/state.ts +31 -9
- package/src/console/server/vault.test.ts +120 -5
- package/src/console/server/vault.ts +208 -12
- package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
- package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
- package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/shell/client.ts +16 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
- package/src/console/ui/vault/VaultA11yView.tsx +33 -1
- package/src/console/ui/vault/backend.test.ts +75 -0
- package/src/console/ui/vault/backend.ts +117 -0
- package/src/console/ui/vault/fixture.ts +21 -2
- package/src/console/ui/vault/index.ts +11 -1
- package/src/console/ui/vault/types.ts +26 -0
- package/src/docker/compose.ts +164 -61
- package/src/docker/derive.ts +106 -12
- package/src/docker/docker.test.ts +156 -110
- package/src/docker/helpers.ts +98 -0
- package/src/docker/images-config.test.ts +3 -8
- package/src/docker/index.ts +30 -5
- package/src/docker/recipes/index.ts +0 -3
- package/src/docker/recipes/llama-cpp.ts +4 -4
- package/src/docker/recipes/pgdog.ts +9 -2
- package/src/docker/resources.test.ts +71 -0
- package/src/docker/resources.ts +176 -0
- package/src/docker/stack-id.test.ts +11 -4
- package/src/docker/stack-id.ts +42 -32
- package/src/docker/stack.integration.test.ts +4 -15
- package/src/docker/types.ts +35 -3
- package/src/drivers/ai-ollama.integration.test.ts +2 -2
- package/src/drivers/conformance.test.ts +11 -12
- package/src/drivers/drizzle-dialect.test.ts +0 -4
- package/src/drivers/drizzle-dialect.ts +7 -13
- package/src/drivers/index.ts +24 -7
- package/src/drivers/journal-postgres.test.ts +3 -1
- package/src/drivers/journal-postgres.ts +4 -2
- package/src/drivers/meilisearch.ts +1 -1
- package/src/drivers/pglite.ts +28 -5
- package/src/drivers/types.ts +9 -11
- package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
- package/src/drivers/vault-aws-secrets-manager.ts +333 -0
- package/src/drivers/vault-builtin.test.ts +110 -0
- package/src/drivers/vault-builtin.ts +184 -0
- package/src/drivers/vault-driver-removal.test.ts +13 -0
- package/src/drivers/vault-managed.test.ts +95 -0
- package/src/drivers/vault-managed.ts +101 -25
- package/src/drivers/vault-types.ts +25 -5
- package/src/elements/ai/runtime.ts +1 -1
- package/src/elements/clock/chaos.test.ts +4 -1
- package/src/elements/clock/durable.ts +57 -4
- package/src/elements/clock.test.ts +13 -14
- package/src/elements/gate/config.ts +13 -3
- package/src/elements/gate/constants.ts +18 -0
- package/src/elements/gate/declare.ts +1 -1
- package/src/elements/gate/strategies.ts +2 -12
- package/src/elements/gate.test.ts +1 -1
- package/src/elements/gate.ts +2 -2
- package/src/elements/store/domain-ddl.test.ts +5 -12
- package/src/elements/store/emit-drizzle.ts +44 -19
- package/src/elements/store/index-boot.test.ts +15 -116
- package/src/elements/store/load-plugin-tables.ts +1 -1
- package/src/elements/store/resource-list-docs.fixture.ts +12 -6
- package/src/elements/store/resource-list-docs.test.ts +64 -44
- package/src/elements/store/resource.test.ts +17 -10
- package/src/elements/store/runtime.ts +9 -12
- package/src/elements/store/schema-decl.test.ts +16 -32
- package/src/elements/store/seed.test.ts +1 -2
- package/src/elements/store/seed.ts +7 -8
- package/src/elements/store/sql-condition.test.ts +2 -2
- package/src/elements/store/sql-session.test.ts +8 -10
- package/src/elements/store/sql-session.ts +10 -2
- package/src/elements/store.test.ts +13 -13
- package/src/elements/vault/audit.ts +233 -0
- package/src/elements/vault/boot-chain.test.ts +78 -0
- package/src/elements/vault/boot-chain.ts +43 -39
- package/src/elements/vault/builtin-adapter.test.ts +479 -0
- package/src/elements/vault/builtin-adapter.ts +1184 -0
- package/src/elements/vault/crypto.test.ts +237 -0
- package/src/elements/vault/crypto.ts +431 -0
- package/src/elements/vault/declare.ts +164 -3
- package/src/elements/vault/errors.ts +56 -0
- package/src/elements/vault/kms-unseal.test.ts +76 -0
- package/src/elements/vault/kms-unseal.ts +181 -0
- package/src/elements/vault/path.ts +90 -0
- package/src/elements/vault/resilience.test.ts +102 -0
- package/src/elements/vault/resilience.ts +107 -0
- package/src/elements/vault/runtime.ts +36 -4
- package/src/elements/vault/security-checklist.test.ts +130 -0
- package/src/elements/vault/storage.ts +448 -0
- package/src/elements/vault/types.ts +288 -0
- package/src/elements/vault/unseal.ts +150 -0
- package/src/elements/vault.test.ts +72 -4
- package/src/elements/vault.ts +91 -2
- package/src/full.ts +248 -0
- package/src/http.ts +41 -0
- package/src/i18n/interpolate-simple.ts +37 -0
- package/src/i18n/messages.ts +51 -4
- package/src/i18n-entry.ts +7 -0
- package/src/index.ts +35 -145
- package/src/journal-entry.ts +20 -0
- package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
- package/src/kernel/app-auth.ts +98 -0
- package/src/kernel/app.ts +77 -110
- package/src/kernel/auto-registry.test.ts +2 -2
- package/src/kernel/boot-bind/ai.test.ts +5 -5
- package/src/kernel/boot-bind/ai.ts +2 -1
- package/src/kernel/boot-bind/channel.test.ts +6 -6
- package/src/kernel/boot-bind/channel.ts +3 -10
- package/src/kernel/boot-bind/clock.ts +2 -2
- package/src/kernel/boot-bind/gate.ts +5 -9
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +2 -2
- package/src/kernel/boot-bind/store.test.ts +43 -44
- package/src/kernel/boot-bind/store.ts +16 -41
- package/src/kernel/boot-bind/vault.ts +16 -1
- package/src/kernel/boot.test.ts +13 -19
- package/src/kernel/boot.ts +31 -19
- package/src/kernel/budget-entry.ts +1 -1
- package/src/kernel/compensate.test.ts +288 -1
- package/src/kernel/compensate.ts +126 -0
- package/src/kernel/concurrency.test.ts +10 -9
- package/src/kernel/effects-stamping.test.ts +27 -2
- package/src/kernel/element-registries.ts +5 -3
- package/src/kernel/flow.ts +4 -2
- package/src/kernel/fx.test.ts +119 -3
- package/src/kernel/fx.ts +215 -40
- package/src/kernel/index.ts +14 -0
- package/src/kernel/journal.ts +130 -7
- package/src/kernel/pipeline.test.ts +20 -6
- package/src/kernel/redacted.test.ts +35 -0
- package/src/kernel/redacted.ts +24 -2
- package/src/kernel/router/create-default.ts +16 -0
- package/src/kernel/router/create-edge.ts +18 -0
- package/src/kernel/router/create.ts +23 -0
- package/src/kernel/router/index.ts +23 -0
- package/src/kernel/router/linear.ts +88 -0
- package/src/kernel/router/regexp.ts +188 -0
- package/src/kernel/router/smart.ts +120 -0
- package/src/kernel/router/trie.ts +108 -0
- package/src/kernel/router/types.ts +56 -0
- package/src/kernel/router.ts +18 -532
- package/src/kernel-entry.ts +43 -0
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/plugins/config-source.test.ts +7 -7
- package/src/release/build-lib.ts +61 -0
- package/src/release/exports.test.ts +4 -2
- package/src/release/exports.ts +30 -4
- package/src/release/measure.ts +101 -22
- package/src/runs/duckdb.ts +7 -2
- package/src/shared/lazy-src.ts +79 -0
- package/src/shared/optional-peer.ts +41 -0
- package/src/term.test.ts +3 -44
- package/src/test/create-test-app.ts +12 -0
- package/src/test/provisions.integration.test.ts +1 -1
- package/src/test/reset-element-registries.ts +2 -0
- package/src/upgrade/codemods.test.ts +7 -20
- package/src/upgrade/codemods.ts +93 -60
- package/site/content/docs/recipes/openbao.mdx +0 -141
- package/src/cli/ask-dev-mode.ts +0 -40
- package/src/cli/dev-mode.test.ts +0 -45
- package/src/cli/dev-mode.ts +0 -78
- package/src/cli/openbao-bootstrap.test.ts +0 -150
- package/src/cli/openbao-bootstrap.ts +0 -321
- package/src/cli/openbao-restart.integration.test.ts +0 -145
- package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
- package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
- package/src/docker/recipes/openbao.ts +0 -47
- package/src/drivers/libsql.ts +0 -179
- package/src/drivers/sqlite.ts +0 -44
- package/src/drivers/vault-openbao.test.ts +0 -97
- package/src/drivers/vault-openbao.ts +0 -143
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* flow under that unit — the stale/missing `.adopt()` barrel case.
|
|
4
4
|
*
|
|
5
5
|
* Mirrors `effects-stamping.test.ts`'s OKE1008 shape exactly: opt-in only
|
|
6
|
-
* via `rootDir`, `
|
|
7
|
-
* `
|
|
6
|
+
* via `rootDir`, `test` warns once (dev-loop stays unbroken),
|
|
7
|
+
* `dev`+compose / `prod` hard-fail (`OKE1009`) — never a silently-incomplete route
|
|
8
8
|
* table in a deploy-shaped environment.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -41,23 +41,23 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
41
41
|
try {
|
|
42
42
|
await makeUnitDir(root, "notes");
|
|
43
43
|
const notesFlow = flow("notes.create", { do: () => ({ ok: true as const }) });
|
|
44
|
-
await expect(assertAdoptBarrelFresh([notesFlow], "
|
|
44
|
+
await expect(assertAdoptBarrelFresh([notesFlow], "dev", root, true)).resolves.toBeUndefined();
|
|
45
45
|
} finally {
|
|
46
46
|
await rm(root, { recursive: true, force: true });
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
test("stale: unit on disk, zero adopted flows for it —
|
|
50
|
+
test("stale: unit on disk, zero adopted flows for it — dev+compose hard-fails OKE1009", async () => {
|
|
51
51
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
52
52
|
try {
|
|
53
53
|
await makeUnitDir(root, "notes");
|
|
54
|
-
await expect(assertAdoptBarrelFresh([], "
|
|
54
|
+
await expect(assertAdoptBarrelFresh([], "dev", root, true)).rejects.toThrow(/OKE1009/);
|
|
55
55
|
} finally {
|
|
56
56
|
await rm(root, { recursive: true, force: true });
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
test("stale: prod — same posture as
|
|
60
|
+
test("stale: prod — same posture as dev+compose", async () => {
|
|
61
61
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
62
62
|
try {
|
|
63
63
|
await makeUnitDir(root, "notes");
|
|
@@ -67,11 +67,11 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
test("stale:
|
|
70
|
+
test("stale: test — warns, never throws (dev-loop stays unbroken)", async () => {
|
|
71
71
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
72
72
|
try {
|
|
73
73
|
await makeUnitDir(root, "notes");
|
|
74
|
-
await expect(assertAdoptBarrelFresh([], "
|
|
74
|
+
await expect(assertAdoptBarrelFresh([], "test", root)).resolves.toBeUndefined();
|
|
75
75
|
} finally {
|
|
76
76
|
await rm(root, { recursive: true, force: true });
|
|
77
77
|
}
|
|
@@ -79,7 +79,7 @@ describe("assertAdoptBarrelFresh — direct", () => {
|
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
describe("boot-level: stale barrel through a real oke() boot", () => {
|
|
82
|
-
test("
|
|
82
|
+
test("dev+compose: real app.boot() hard-fails OKE1009 when a unit folder was never adopted", async () => {
|
|
83
83
|
const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
|
|
84
84
|
try {
|
|
85
85
|
// "notes" exists on disk but this app only ever adopted "main" —
|
|
@@ -94,7 +94,22 @@ describe("boot-level: stale barrel through a real oke() boot", () => {
|
|
|
94
94
|
const app = oke({ name: "stale-barrel", gate: { unguardedHttp: "allow" } });
|
|
95
95
|
|
|
96
96
|
await expect(
|
|
97
|
-
app.boot({
|
|
97
|
+
app.boot({
|
|
98
|
+
env: "dev",
|
|
99
|
+
docker: true,
|
|
100
|
+
rootDir: root,
|
|
101
|
+
unguardedHttp: "allow",
|
|
102
|
+
startScheduler: false,
|
|
103
|
+
config: {
|
|
104
|
+
drivers: {
|
|
105
|
+
store: {
|
|
106
|
+
sql: { dev: "memory" },
|
|
107
|
+
kv: { dev: "memory" },
|
|
108
|
+
},
|
|
109
|
+
channel: { email: { dev: "console" } },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
98
113
|
).rejects.toThrow(/OKE1009/);
|
|
99
114
|
} finally {
|
|
100
115
|
await rm(root, { recursive: true, force: true });
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync auth wiring for `oke({ gate: { auth } })`.
|
|
3
|
+
*
|
|
4
|
+
* Loaded only when auth is configured so HTTP-only cold graphs never import
|
|
5
|
+
* bindings / sessions / identity (`requirePackageModule` in `app.ts`).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ResolvedGateAuth } from "../auth/config.ts";
|
|
9
|
+
import { auth as authPlugin } from "../auth/plugin.ts";
|
|
10
|
+
import { createAuthHttpBindings, type AuthHttpMaterialization } from "../auth/bindings.ts";
|
|
11
|
+
import { tokenFromCookieHeader } from "../auth/cookies.ts";
|
|
12
|
+
import { setActiveGateAuthContext } from "../auth/method-context.ts";
|
|
13
|
+
import type { ResolvedGateConfig } from "../elements/gate/config.ts";
|
|
14
|
+
import { createAppAuthBinding, verifyBearerToken, type AppAuthBinding } from "./auth-resolve.ts";
|
|
15
|
+
import type { PluginDef } from "./plugin.ts";
|
|
16
|
+
|
|
17
|
+
/** Result of wiring Gate auth at construction. */
|
|
18
|
+
export interface WiredGateAuth {
|
|
19
|
+
readonly materialization: AuthHttpMaterialization | undefined;
|
|
20
|
+
readonly authPlugin: PluginDef | undefined;
|
|
21
|
+
readonly authBinding: AppAuthBinding;
|
|
22
|
+
readonly verifyBearerToken: typeof verifyBearerToken;
|
|
23
|
+
readonly tokenFromCookieHeader: typeof tokenFromCookieHeader;
|
|
24
|
+
/** Rebuild binding when boot supplies a clock. */
|
|
25
|
+
rebind(now: () => number): AppAuthBinding;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Options for {@link wireGateAuth}. */
|
|
29
|
+
export interface WireGateAuthOptions {
|
|
30
|
+
readonly gateConfig: ResolvedGateConfig & { readonly auth: ResolvedGateAuth };
|
|
31
|
+
/** Clock fallback when `gate.auth.now` is omitted (`oke({ fx: { now } })`). */
|
|
32
|
+
readonly now?: () => number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Materialize auth HTTP bindings, absorb the builtin auth plugin, and publish
|
|
37
|
+
* the active Gate auth context for `.plug(username())` etc.
|
|
38
|
+
*
|
|
39
|
+
* @param options - Resolved Gate bag + optional clock
|
|
40
|
+
*/
|
|
41
|
+
export function wireGateAuth(options: WireGateAuthOptions): WiredGateAuth {
|
|
42
|
+
const { gateConfig } = options;
|
|
43
|
+
const auth = gateConfig.auth;
|
|
44
|
+
let materialization: AuthHttpMaterialization | undefined;
|
|
45
|
+
if (auth.http) {
|
|
46
|
+
materialization = createAuthHttpBindings(auth, {
|
|
47
|
+
rateLimitEnabled: gateConfig.rateLimitEnabled,
|
|
48
|
+
sessions: auth.sessions,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pluginDef = authPlugin({
|
|
53
|
+
secret: auth.secret,
|
|
54
|
+
accessTtlMs: auth.session.accessTtlMs,
|
|
55
|
+
refreshTtlMs: auth.session.refreshTtlMs,
|
|
56
|
+
session: {
|
|
57
|
+
accessTtlMs: auth.session.accessTtlMs,
|
|
58
|
+
refreshTtlMs: auth.session.refreshTtlMs,
|
|
59
|
+
idleTtlMs: auth.session.idleTtlMs,
|
|
60
|
+
absoluteTtlMs: auth.session.absoluteTtlMs,
|
|
61
|
+
singleSessionPerUser: auth.session.singleSessionPerUser,
|
|
62
|
+
},
|
|
63
|
+
password: auth.password,
|
|
64
|
+
passwordPolicy: auth.passwordPolicy,
|
|
65
|
+
breachCheck: auth.breachCheck,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let authBinding = createAppAuthBinding({
|
|
69
|
+
secret: auth.secret,
|
|
70
|
+
sessions: materialization?.ctx.sessions ?? auth.sessions,
|
|
71
|
+
now: auth.now ?? options.now,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
setActiveGateAuthContext({
|
|
75
|
+
secret: authBinding.secret,
|
|
76
|
+
sessions: authBinding.sessions,
|
|
77
|
+
now: authBinding.now,
|
|
78
|
+
passwordPolicy: auth.passwordPolicy,
|
|
79
|
+
password: auth.password,
|
|
80
|
+
breachCheck: auth.breachCheck,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
materialization,
|
|
85
|
+
authPlugin: pluginDef,
|
|
86
|
+
authBinding,
|
|
87
|
+
verifyBearerToken,
|
|
88
|
+
tokenFromCookieHeader,
|
|
89
|
+
rebind(now) {
|
|
90
|
+
authBinding = createAppAuthBinding({
|
|
91
|
+
secret: authBinding.secret,
|
|
92
|
+
sessions: authBinding.sessions,
|
|
93
|
+
now,
|
|
94
|
+
});
|
|
95
|
+
return authBinding;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
package/src/kernel/app.ts
CHANGED
|
@@ -6,12 +6,9 @@
|
|
|
6
6
|
* pipeline, and wires `fx.call` to untriggered (and triggered) flows.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
encodeFailure,
|
|
13
|
-
type CompiledRoute,
|
|
14
|
-
} from "../compiler/index.ts";
|
|
9
|
+
import { compileRoute } from "../compiler/dynamic.ts";
|
|
10
|
+
import type { CompiledRoute } from "../compiler/aot.ts";
|
|
11
|
+
import { encodeExecuteResult, encodeFailure } from "../compiler/response.ts";
|
|
15
12
|
import { validate } from "../validation/standard-schema.ts";
|
|
16
13
|
import {
|
|
17
14
|
accumulateAdoptArgs,
|
|
@@ -25,22 +22,22 @@ import {
|
|
|
25
22
|
// when an app actually boots (AGENTS.md / unified-theory budget: cold start < 75 ms).
|
|
26
23
|
import type { BootOptions, BootResult, ElementRuntimes } from "./boot.ts";
|
|
27
24
|
import type { CapabilityToken } from "./capability.ts";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { tokenFromCookieHeader } from "../auth/cookies.ts";
|
|
32
|
-
import { setActiveGateAuthContext } from "../auth/method-context.ts";
|
|
25
|
+
import type { AppAuthBinding } from "./auth-resolve.ts";
|
|
26
|
+
import type { AuthHttpMaterialization } from "../auth/bindings.ts";
|
|
27
|
+
import type { WiredGateAuth } from "./app-auth.ts";
|
|
33
28
|
import {
|
|
34
29
|
resolveGateConfig,
|
|
35
30
|
type GateOptions,
|
|
36
31
|
type ResolvedGateConfig,
|
|
37
32
|
} from "../elements/gate/config.ts";
|
|
33
|
+
import { requirePackageModule } from "../shared/lazy-src.ts";
|
|
38
34
|
import type { TemplateCatalog } from "../elements/channel/runtime.ts";
|
|
39
35
|
import { parseAcceptLanguage } from "../elements/channel/locale.ts";
|
|
40
36
|
import { runWithLocale } from "../i18n/locale-context.ts";
|
|
41
37
|
import { getMessageCatalogs, matchConfiguredLocale } from "../i18n/messages.ts";
|
|
42
38
|
import { runDurable } from "../elements/clock/durable.ts";
|
|
43
39
|
import { isFlow, type AnyFlowDef } from "./flow.ts";
|
|
40
|
+
import { failureCodeOf, runCompensationPhase } from "./compensate.ts";
|
|
44
41
|
import { fxRetry } from "./concurrency.ts";
|
|
45
42
|
import {
|
|
46
43
|
createFxContext,
|
|
@@ -85,6 +82,7 @@ import type { JournalRuntime } from "./boot-bind/journal.ts";
|
|
|
85
82
|
import { listBindings, resetBindings, type Binding } from "./on.ts";
|
|
86
83
|
import {
|
|
87
84
|
channelTemplateRegistry,
|
|
85
|
+
requiredEnvRegistry,
|
|
88
86
|
secretRegistry,
|
|
89
87
|
signalRegistry,
|
|
90
88
|
storeRegistry,
|
|
@@ -159,11 +157,11 @@ export interface OkeOptions {
|
|
|
159
157
|
* subject key.
|
|
160
158
|
*/
|
|
161
159
|
readonly archiveInputFields?: readonly string[];
|
|
162
|
-
/** Active environment for {@link OkeApp.boot} (
|
|
160
|
+
/** Active environment for {@link OkeApp.boot} (`dev` / `test` / `prod`). */
|
|
163
161
|
readonly env?: BootOptions["env"];
|
|
164
162
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
163
|
+
* Compose mode (`oke dev` / `OKE_DOCKER=1`) — select the `dev` driver
|
|
164
|
+
* profile at boot when `env` is omitted.
|
|
167
165
|
*/
|
|
168
166
|
readonly docker?: BootOptions["docker"];
|
|
169
167
|
/** Optional `oke.config.ts` document consumed at boot. */
|
|
@@ -485,22 +483,35 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
485
483
|
// explicitly-passed decl that is also in the registry is not doubled.
|
|
486
484
|
const registrySnapshot =
|
|
487
485
|
registry === "ignore"
|
|
488
|
-
? { stores: [], secrets: [], signals: [], channelTemplates: [] }
|
|
486
|
+
? { stores: [], secrets: [], requiredEnv: [], signals: [], channelTemplates: [] }
|
|
489
487
|
: {
|
|
490
488
|
stores: storeRegistry.slice(),
|
|
491
489
|
secrets: secretRegistry.slice(),
|
|
490
|
+
requiredEnv: requiredEnvRegistry.slice(),
|
|
492
491
|
signals: signalRegistry.slice(),
|
|
493
492
|
channelTemplates: channelTemplateRegistry.slice(),
|
|
494
493
|
};
|
|
495
494
|
if (registry === "consume") {
|
|
496
495
|
storeRegistry.length = 0;
|
|
497
496
|
secretRegistry.length = 0;
|
|
497
|
+
requiredEnvRegistry.length = 0;
|
|
498
498
|
signalRegistry.length = 0;
|
|
499
499
|
channelTemplateRegistry.length = 0;
|
|
500
500
|
}
|
|
501
501
|
const effectiveStores = mergeUnique(options.stores, registrySnapshot.stores);
|
|
502
502
|
const effectiveSecrets = mergeUnique(options.secrets, registrySnapshot.secrets);
|
|
503
503
|
const effectiveSignals = mergeUnique(options.signals, registrySnapshot.signals);
|
|
504
|
+
/**
|
|
505
|
+
* Fold registered `vault.env.required` names into the vault boot options so
|
|
506
|
+
* boot reports missing env vars in the same gap list as missing secrets.
|
|
507
|
+
*
|
|
508
|
+
* @param base - Vault options from `oke({ vault })` / a boot override
|
|
509
|
+
*/
|
|
510
|
+
const withRequiredEnv = (base: BootOptions["vault"]): BootOptions["vault"] => {
|
|
511
|
+
const names = [...new Set([...(base?.requiredEnv ?? []), ...registrySnapshot.requiredEnv])];
|
|
512
|
+
if (names.length === 0) return base;
|
|
513
|
+
return { ...(base ?? {}), requiredEnv: names };
|
|
514
|
+
};
|
|
504
515
|
// Stay `undefined` (never a defined-but-empty object) when neither an
|
|
505
516
|
// explicit `options.channel` nor a registered template exists — a defined
|
|
506
517
|
// object here would flip `resolveElementNeeds`'s `channel` need to `true`
|
|
@@ -519,14 +530,6 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
519
530
|
gate: options.gate,
|
|
520
531
|
env: options.env,
|
|
521
532
|
});
|
|
522
|
-
let authMaterialization: AuthHttpMaterialization | undefined;
|
|
523
|
-
if (gateConfig.auth?.http) {
|
|
524
|
-
authMaterialization = createAuthHttpBindings(gateConfig.auth, {
|
|
525
|
-
rateLimitEnabled: gateConfig.rateLimitEnabled,
|
|
526
|
-
sessions: gateConfig.auth.sessions,
|
|
527
|
-
});
|
|
528
|
-
adopted.push(...authMaterialization.bindings);
|
|
529
|
-
}
|
|
530
533
|
|
|
531
534
|
/** Retained for test harness / boot merges. */
|
|
532
535
|
const $options = options;
|
|
@@ -540,27 +543,26 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
540
543
|
/** Runtime route table — types accumulate on the returned {@link OkeApp}. */
|
|
541
544
|
const routes: RuntimeRouteMap = {};
|
|
542
545
|
|
|
543
|
-
|
|
546
|
+
let authMaterialization: AuthHttpMaterialization | undefined;
|
|
547
|
+
let wiredAuth: WiredGateAuth | undefined;
|
|
548
|
+
let authBinding: AppAuthBinding | undefined;
|
|
544
549
|
if (gateConfig.auth) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
secret: gateConfig.auth.secret,
|
|
549
|
-
accessTtlMs: gateConfig.auth.session.accessTtlMs,
|
|
550
|
-
refreshTtlMs: gateConfig.auth.session.refreshTtlMs,
|
|
551
|
-
session: {
|
|
552
|
-
accessTtlMs: gateConfig.auth.session.accessTtlMs,
|
|
553
|
-
refreshTtlMs: gateConfig.auth.session.refreshTtlMs,
|
|
554
|
-
idleTtlMs: gateConfig.auth.session.idleTtlMs,
|
|
555
|
-
absoluteTtlMs: gateConfig.auth.session.absoluteTtlMs,
|
|
556
|
-
singleSessionPerUser: gateConfig.auth.session.singleSessionPerUser,
|
|
557
|
-
},
|
|
558
|
-
password: gateConfig.auth.password,
|
|
559
|
-
passwordPolicy: gateConfig.auth.passwordPolicy,
|
|
560
|
-
breachCheck: gateConfig.auth.breachCheck,
|
|
561
|
-
}),
|
|
562
|
-
appPluginScope,
|
|
550
|
+
const { wireGateAuth } = requirePackageModule<typeof import("./app-auth.ts")>(
|
|
551
|
+
"kernel/app-auth",
|
|
552
|
+
"app-auth",
|
|
563
553
|
);
|
|
554
|
+
wiredAuth = wireGateAuth({
|
|
555
|
+
gateConfig: { ...gateConfig, auth: gateConfig.auth },
|
|
556
|
+
now: options.fx?.now,
|
|
557
|
+
});
|
|
558
|
+
authMaterialization = wiredAuth.materialization;
|
|
559
|
+
authBinding = wiredAuth.authBinding;
|
|
560
|
+
if (authMaterialization) {
|
|
561
|
+
adopted.push(...authMaterialization.bindings);
|
|
562
|
+
}
|
|
563
|
+
if (wiredAuth.authPlugin) {
|
|
564
|
+
applyPlugin(pluginRegistry, wiredAuth.authPlugin, appPluginScope);
|
|
565
|
+
}
|
|
564
566
|
}
|
|
565
567
|
|
|
566
568
|
const smart = createRouter<Binding>(options.router ?? "default");
|
|
@@ -655,30 +657,11 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
655
657
|
let bootResult: BootResult | undefined;
|
|
656
658
|
let bootPromise: Promise<BootResult> | undefined;
|
|
657
659
|
let readyState: ReadyState = "booting";
|
|
658
|
-
let bootEnv: BootOptions["env"] = options.env
|
|
659
|
-
let authBinding: AppAuthBinding | undefined =
|
|
660
|
-
gateConfig.auth !== undefined
|
|
661
|
-
? createAppAuthBinding({
|
|
662
|
-
secret: gateConfig.auth.secret,
|
|
663
|
-
sessions: authMaterialization?.ctx.sessions ?? gateConfig.auth.sessions,
|
|
664
|
-
now: gateConfig.auth.now ?? options.fx?.now,
|
|
665
|
-
})
|
|
666
|
-
: undefined;
|
|
667
|
-
if (authBinding) {
|
|
668
|
-
setActiveGateAuthContext({
|
|
669
|
-
secret: authBinding.secret,
|
|
670
|
-
sessions: authBinding.sessions,
|
|
671
|
-
now: authBinding.now,
|
|
672
|
-
passwordPolicy: gateConfig.auth?.passwordPolicy,
|
|
673
|
-
password: gateConfig.auth?.password,
|
|
674
|
-
breachCheck: gateConfig.auth?.breachCheck,
|
|
675
|
-
});
|
|
676
|
-
} else {
|
|
677
|
-
setActiveGateAuthContext(undefined);
|
|
678
|
-
}
|
|
660
|
+
let bootEnv: BootOptions["env"] = options.env;
|
|
679
661
|
// Fallback journal for pre-boot / `autoBoot: false` unit tests. A booted
|
|
680
662
|
// app replaces this with the bound `drivers.journal` store (postgres etc.).
|
|
681
|
-
|
|
663
|
+
// Constructed on first use so non-durable HTTP apps skip the alloc.
|
|
664
|
+
let fallbackJournalStore: JournalStore | undefined;
|
|
682
665
|
const fallbackJournalInstanceId = `app-${crypto.randomUUID()}`;
|
|
683
666
|
const sleepingRuns = new Map<
|
|
684
667
|
string,
|
|
@@ -700,6 +683,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
700
683
|
if (bound) {
|
|
701
684
|
return { store: bound.store, instanceId: bound.instanceId, leaseMs: bound.leaseMs };
|
|
702
685
|
}
|
|
686
|
+
fallbackJournalStore ??= createMemoryJournalStore();
|
|
703
687
|
return {
|
|
704
688
|
store: fallbackJournalStore,
|
|
705
689
|
instanceId: fallbackJournalInstanceId,
|
|
@@ -787,7 +771,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
787
771
|
}
|
|
788
772
|
}
|
|
789
773
|
|
|
790
|
-
/** Boot-time orphan scan — `running`/`sleeping`
|
|
774
|
+
/** Boot-time orphan scan — `running`/`sleeping`/`compensating` with no live lease. */
|
|
791
775
|
async function resumeOrphanedDurableRuns(): Promise<void> {
|
|
792
776
|
const { store } = activeJournal();
|
|
793
777
|
if (!hasJournalLease(store)) return;
|
|
@@ -807,7 +791,15 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
807
791
|
const { bootApplication, resolveElementNeeds } = await import("./boot.ts");
|
|
808
792
|
const { assertHttpGatePosture } = await import("../elements/gate/boot.ts");
|
|
809
793
|
const { assertPluginNeeds, buildAvailableNeedTokens } = await import("./plugin-needs.ts");
|
|
810
|
-
|
|
794
|
+
const dockerFlag =
|
|
795
|
+
overrides?.docker === true ||
|
|
796
|
+
(overrides?.docker !== false &&
|
|
797
|
+
(options.docker === true || (options.docker !== false && process.env.OKE_DOCKER === "1")));
|
|
798
|
+
// Mirror bootApplication: prod / compose-dev / else test (PGLite + memory).
|
|
799
|
+
bootEnv =
|
|
800
|
+
overrides?.env ??
|
|
801
|
+
options.env ??
|
|
802
|
+
(process.env.NODE_ENV === "production" ? "prod" : dockerFlag ? "dev" : "test");
|
|
811
803
|
|
|
812
804
|
// Prod without a real secret fails even if construction minted a dev secret.
|
|
813
805
|
if (gateConfig.auth?.secretMinted && bootEnv === "prod") {
|
|
@@ -830,7 +822,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
830
822
|
config: overrides?.config ?? options.config,
|
|
831
823
|
elements: overrides?.elements ?? options.elements,
|
|
832
824
|
secrets: overrides?.secrets ?? effectiveSecrets,
|
|
833
|
-
vault: overrides?.vault ?? options.vault,
|
|
825
|
+
vault: withRequiredEnv(overrides?.vault ?? options.vault),
|
|
834
826
|
gates: [...baseGates, ...authGates],
|
|
835
827
|
signals: overrides?.signals ?? effectiveSignals,
|
|
836
828
|
clocks: overrides?.clocks ?? options.clocks,
|
|
@@ -900,7 +892,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
900
892
|
rootDir: overrides?.rootDir ?? options.rootDir,
|
|
901
893
|
elements: overrides?.elements ?? options.elements,
|
|
902
894
|
secrets: [...baseSecrets, ...pluginSecrets],
|
|
903
|
-
vault: overrides?.vault ?? options.vault,
|
|
895
|
+
vault: withRequiredEnv(overrides?.vault ?? options.vault),
|
|
904
896
|
gates: [...baseGates, ...authGates, ...pluginGates],
|
|
905
897
|
unguardedHttp,
|
|
906
898
|
signals: [...baseSignals, ...pluginSignals],
|
|
@@ -955,12 +947,9 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
955
947
|
readyState = "ready";
|
|
956
948
|
}
|
|
957
949
|
// Prefer the booted clock for access-token expiry checks.
|
|
958
|
-
if (authBinding) {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
sessions: authBinding.sessions,
|
|
962
|
-
now: result.clock?.now.bind(result.clock) ?? authBinding.now,
|
|
963
|
-
});
|
|
950
|
+
if (wiredAuth && authBinding) {
|
|
951
|
+
const now = result.clock?.now.bind(result.clock) ?? authBinding.now;
|
|
952
|
+
authBinding = wiredAuth.rebind(now);
|
|
964
953
|
}
|
|
965
954
|
return result;
|
|
966
955
|
}
|
|
@@ -1013,7 +1002,7 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1013
1002
|
registerFlow(flowDef);
|
|
1014
1003
|
|
|
1015
1004
|
const i18nConfig = options.config?.i18n;
|
|
1016
|
-
const configuredLocales = i18nConfig?.locales ?? ["en"
|
|
1005
|
+
const configuredLocales = i18nConfig?.locales ?? ["en"];
|
|
1017
1006
|
const defaultLocale = i18nConfig?.default ?? "en";
|
|
1018
1007
|
const acceptLanguage = extras?.request?.headers.get("accept-language") ?? undefined;
|
|
1019
1008
|
const resolvedLocale = matchConfiguredLocale(
|
|
@@ -1121,21 +1110,24 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1121
1110
|
|
|
1122
1111
|
const binding = authBinding;
|
|
1123
1112
|
const cookieOpts = gateConfig.auth?.cookies;
|
|
1113
|
+
const verifyBearer = wiredAuth?.verifyBearerToken;
|
|
1114
|
+
const cookieToken = wiredAuth?.tokenFromCookieHeader;
|
|
1124
1115
|
const elementHooks = createElementPipelineHooks({
|
|
1125
1116
|
gates: booted.gate,
|
|
1126
1117
|
principals,
|
|
1127
1118
|
telemetry,
|
|
1128
1119
|
allowTestPrincipals: testMode,
|
|
1129
|
-
verifyBearer:
|
|
1120
|
+
verifyBearer:
|
|
1121
|
+
binding && verifyBearer ? async (token) => verifyBearer(binding, token) : undefined,
|
|
1130
1122
|
// Phase 1a: opt-in cookie → Bearer when Authorization is absent.
|
|
1131
1123
|
resolveToken:
|
|
1132
|
-
binding && cookieOpts?.enabled
|
|
1124
|
+
binding && cookieOpts?.enabled && cookieToken
|
|
1133
1125
|
? (request) => {
|
|
1134
1126
|
const header = request.headers.get("authorization");
|
|
1135
1127
|
if (header?.startsWith("Bearer ")) {
|
|
1136
1128
|
return header.slice("Bearer ".length).trim() || undefined;
|
|
1137
1129
|
}
|
|
1138
|
-
return
|
|
1130
|
+
return cookieToken(request.headers.get("cookie"), cookieOpts);
|
|
1139
1131
|
}
|
|
1140
1132
|
: undefined,
|
|
1141
1133
|
});
|
|
@@ -1281,30 +1273,13 @@ export function oke(options: OkeOptions): OkeApp {
|
|
|
1281
1273
|
});
|
|
1282
1274
|
} else if (!sleeping && isTerminalFailure(result)) {
|
|
1283
1275
|
const terminalErr = result.failure ?? result.ctx.error;
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
input: ctx.input as never,
|
|
1292
|
-
error: terminalErr,
|
|
1293
|
-
completedSteps,
|
|
1294
|
-
},
|
|
1295
|
-
fx,
|
|
1296
|
-
);
|
|
1297
|
-
} catch (compErr) {
|
|
1298
|
-
await journalSession.commit("failed", {
|
|
1299
|
-
error: `compensate:${failureCodeOf(compErr)}`,
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
if (journalSession.run.status === "running") {
|
|
1304
|
-
await journalSession.commit("failed", {
|
|
1305
|
-
error: failureCodeOf(terminalErr),
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1276
|
+
await runCompensationPhase({
|
|
1277
|
+
flow: flowDef,
|
|
1278
|
+
input: ctx.input,
|
|
1279
|
+
session: journalSession,
|
|
1280
|
+
fx,
|
|
1281
|
+
error: terminalErr,
|
|
1282
|
+
});
|
|
1308
1283
|
} else if (!sleeping) {
|
|
1309
1284
|
await journalSession.commit("completed", { output: result.output });
|
|
1310
1285
|
}
|
|
@@ -1699,14 +1674,6 @@ function isTerminalFailure(result: PipelineResult): boolean {
|
|
|
1699
1674
|
return result.ctx.error !== undefined && !isJournalSuspend(result.ctx.error);
|
|
1700
1675
|
}
|
|
1701
1676
|
|
|
1702
|
-
/** Machine-readable code for journal / Runs failure fields. */
|
|
1703
|
-
function failureCodeOf(err: unknown): string {
|
|
1704
|
-
if (isFlowFailure(err)) return err.error.code;
|
|
1705
|
-
if (err instanceof Error && err.name && err.name !== "Error") return err.name;
|
|
1706
|
-
if (err instanceof Error && err.message) return err.message.slice(0, 120);
|
|
1707
|
-
return "Error";
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
1677
|
/** Coerce a thrown value into a FlowFailure for Runs wide events. */
|
|
1711
1678
|
function failureFromUnknown(err: unknown): FlowFailure {
|
|
1712
1679
|
if (isFlowFailure(err)) return err;
|
|
@@ -40,7 +40,7 @@ describe("oke() auto-registry — stores/secrets/signals/channel.templates", ()
|
|
|
40
40
|
sends: ["before-note-created"],
|
|
41
41
|
},
|
|
42
42
|
do: async (_input, fx) => {
|
|
43
|
-
const secret = fx.vault(secretBefore);
|
|
43
|
+
const secret = await fx.vault.get(secretBefore);
|
|
44
44
|
await fx.emit(signalBefore, {});
|
|
45
45
|
await fx.send(templateBefore, { to: "you@localhost", data: {} });
|
|
46
46
|
return { secret };
|
|
@@ -78,7 +78,7 @@ describe("oke() auto-registry — stores/secrets/signals/channel.templates", ()
|
|
|
78
78
|
sends: ["after-note-created"],
|
|
79
79
|
},
|
|
80
80
|
do: async (_input, fx) => {
|
|
81
|
-
const secret = fx.vault(secretAfter);
|
|
81
|
+
const secret = await fx.vault.get(secretAfter);
|
|
82
82
|
await fx.emit(signalAfter, {});
|
|
83
83
|
await fx.send(templateAfter, { to: "you@localhost", data: {} });
|
|
84
84
|
return { secret };
|
|
@@ -16,15 +16,15 @@ describe("ai boot binder", () => {
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
test("resolveAiDriverId defaults to mock; honours config + OKE_AI_DRIVER in docker", () => {
|
|
19
|
-
expect(resolveAiDriverId({}, "
|
|
20
|
-
expect(resolveAiDriverId({ config: { drivers: { ai: {
|
|
19
|
+
expect(resolveAiDriverId({}, "test")).toBe("mock");
|
|
20
|
+
expect(resolveAiDriverId({ config: { drivers: { ai: { test: "ollama" } } } }, "test")).toBe(
|
|
21
21
|
"ollama",
|
|
22
22
|
);
|
|
23
23
|
|
|
24
24
|
const prev = process.env.OKE_AI_DRIVER;
|
|
25
25
|
process.env.OKE_AI_DRIVER = "ollama";
|
|
26
26
|
try {
|
|
27
|
-
expect(resolveAiDriverId({}, "
|
|
27
|
+
expect(resolveAiDriverId({}, "test", true)).toBe("ollama");
|
|
28
28
|
} finally {
|
|
29
29
|
if (prev === undefined) delete process.env.OKE_AI_DRIVER;
|
|
30
30
|
else process.env.OKE_AI_DRIVER = prev;
|
|
@@ -48,12 +48,12 @@ describe("ai boot binder", () => {
|
|
|
48
48
|
test("bindAi uses injected defaultDriver over config (tests stay on mock)", () => {
|
|
49
49
|
const runtime = bindAi(
|
|
50
50
|
{
|
|
51
|
-
config: { drivers: { ai: {
|
|
51
|
+
config: { drivers: { ai: { test: "ollama" } } },
|
|
52
52
|
ai: { defaultDriver: mockAiDriver },
|
|
53
53
|
},
|
|
54
54
|
undefined,
|
|
55
55
|
() => 0,
|
|
56
|
-
"
|
|
56
|
+
"test",
|
|
57
57
|
);
|
|
58
58
|
expect(runtime.autoCacheDisabled).toBe(true);
|
|
59
59
|
});
|
|
@@ -33,7 +33,7 @@ export function bindAi(
|
|
|
33
33
|
options: BootOptions,
|
|
34
34
|
gate: GateRuntime | undefined,
|
|
35
35
|
now: () => number,
|
|
36
|
-
env: ConfigEnv = "
|
|
36
|
+
env: ConfigEnv = "test",
|
|
37
37
|
docker = false,
|
|
38
38
|
): AiRuntime {
|
|
39
39
|
const id = resolveAiDriverId(options, env, docker);
|
|
@@ -115,6 +115,7 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
|
|
|
115
115
|
return {
|
|
116
116
|
...(apiKey ? { apiKey } : {}),
|
|
117
117
|
...(baseUrl ? { baseUrl } : {}),
|
|
118
|
+
...(process.env.OKE_AI_MODEL?.trim() ? { model: process.env.OKE_AI_MODEL.trim() } : {}),
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
return {};
|
|
@@ -58,23 +58,23 @@ describe("bindChannel driver resolution", () => {
|
|
|
58
58
|
config: {
|
|
59
59
|
drivers: {
|
|
60
60
|
channel: {
|
|
61
|
-
email: {
|
|
62
|
-
sms: {
|
|
61
|
+
email: { dev: "smtp", test: "console", prod: "sndr" },
|
|
62
|
+
sms: { dev: "taqnyat", test: "msegat", prod: "taqnyat" },
|
|
63
63
|
},
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
test("uses console locally and smtp in docker", () => {
|
|
69
|
-
expect(resolveEmailDriverId(options, "
|
|
70
|
-
expect(resolveEmailDriverId(options, "
|
|
69
|
+
expect(resolveEmailDriverId(options, "test", false)).toBe("console");
|
|
70
|
+
expect(resolveEmailDriverId(options, "dev", true)).toBe("smtp");
|
|
71
71
|
expect(resolveEmailDriverId(options, "prod", false)).toBe("sndr");
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test("resolves sms driver ids", () => {
|
|
75
|
-
expect(resolveSmsDriverId(options, "local")).toBe("console");
|
|
76
|
-
expect(resolveSmsDriverId(options, "docker")).toBe("taqnyat");
|
|
77
75
|
expect(resolveSmsDriverId(options, "test")).toBe("msegat");
|
|
76
|
+
expect(resolveSmsDriverId(options, "dev")).toBe("taqnyat");
|
|
77
|
+
expect(resolveSmsDriverId(options, "prod")).toBe("taqnyat");
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test("parses SMTP_URL and optional auth overrides", () => {
|
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
|
|
6
|
-
import {
|
|
7
|
-
CHANNEL_EMAIL_DEFAULTS,
|
|
8
|
-
CHANNEL_SMS_DEFAULTS,
|
|
9
|
-
dockerFlagDefaults,
|
|
10
|
-
} from "../../config/driver-defaults.ts";
|
|
6
|
+
import { CHANNEL_EMAIL_DEFAULTS, CHANNEL_SMS_DEFAULTS } from "../../config/driver-defaults.ts";
|
|
11
7
|
import { openConsoleChannel } from "../../drivers/channel-console.ts";
|
|
12
8
|
import { openMsegatChannel } from "../../drivers/channel-msegat.ts";
|
|
13
9
|
import { openResendChannel } from "../../drivers/channel-resend.ts";
|
|
@@ -86,12 +82,9 @@ export function resolveEmailDriverId(
|
|
|
86
82
|
env: ConfigEnv,
|
|
87
83
|
docker: boolean,
|
|
88
84
|
): string {
|
|
85
|
+
void docker;
|
|
89
86
|
// Defaults cover every ConfigEnv key, so this is never undefined.
|
|
90
|
-
return resolveDriverId(
|
|
91
|
-
options.config?.drivers?.channel?.email,
|
|
92
|
-
env,
|
|
93
|
-
dockerFlagDefaults(CHANNEL_EMAIL_DEFAULTS, docker),
|
|
94
|
-
)!;
|
|
87
|
+
return resolveDriverId(options.config?.drivers?.channel?.email, env, CHANNEL_EMAIL_DEFAULTS)!;
|
|
95
88
|
}
|
|
96
89
|
|
|
97
90
|
/**
|