okengine 0.10.2 → 0.11.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 +76 -10
- package/site/content/docs/ai/mcp.mdx +0 -1
- 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 -13
- package/site/content/docs/elements/channel.mdx +9 -13
- package/site/content/docs/elements/clock.mdx +8 -14
- package/site/content/docs/elements/flow.mdx +46 -54
- package/site/content/docs/elements/gate.mdx +6 -11
- package/site/content/docs/elements/signal.mdx +2 -8
- package/site/content/docs/elements/store.mdx +79 -93
- package/site/content/docs/elements/vault.mdx +89 -76
- package/site/content/docs/get-started/basic-usage.mdx +8 -14
- 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 +3 -4
- package/site/content/docs/index.mdx +0 -1
- package/site/content/docs/meta.json +0 -1
- 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 +14 -7
- 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 +30 -16
- 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 +8 -9
- 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 +246 -114
- package/src/cli/dev.ts +153 -205
- 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 +142 -9
- 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 +44 -10
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +35 -29
- 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 +29 -11
- 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/extract.ts +12 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +7 -5
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler/generate-adopt.ts +27 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +115 -0
- package/src/config/driver-defaults.ts +156 -0
- package/src/config/index.ts +246 -131
- 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 +163 -0
- 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/constants.ts +18 -0
- 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.ts +40 -42
- 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 +5 -4
- package/src/kernel/boot-bind/clock.ts +5 -5
- package/src/kernel/boot-bind/gate.ts +6 -7
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +5 -5
- package/src/kernel/boot-bind/signal.ts +3 -1
- package/src/kernel/boot-bind/store.test.ts +55 -37
- package/src/kernel/boot-bind/store.ts +24 -34
- package/src/kernel/boot-bind/vault.ts +19 -4
- package/src/kernel/boot.test.ts +12 -4
- 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 +58 -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/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/console/access.mdx +0 -29
- package/site/content/docs/console/ai.mdx +0 -35
- package/site/content/docs/console/architecture.mdx +0 -35
- package/site/content/docs/console/channels.mdx +0 -37
- package/site/content/docs/console/clock.mdx +0 -51
- package/site/content/docs/console/flows.mdx +0 -31
- package/site/content/docs/console/gates.mdx +0 -73
- package/site/content/docs/console/index.mdx +0 -54
- package/site/content/docs/console/manifest-diff.mdx +0 -34
- package/site/content/docs/console/meta.json +0 -24
- package/site/content/docs/console/overview.mdx +0 -40
- package/site/content/docs/console/plugins.mdx +0 -41
- package/site/content/docs/console/privacy.mdx +0 -32
- package/site/content/docs/console/runs.mdx +0 -50
- package/site/content/docs/console/signals.mdx +0 -40
- package/site/content/docs/console/store.mdx +0 -32
- package/site/content/docs/console/tenancy.mdx +0 -32
- package/site/content/docs/console/traces.mdx +0 -38
- package/site/content/docs/console/vault.mdx +0 -41
- 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
package/src/cli/doctor.ts
CHANGED
|
@@ -3,17 +3,76 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
|
+
import type {
|
|
7
|
+
ConfigEnv,
|
|
8
|
+
DriverRef,
|
|
9
|
+
DriversConfig,
|
|
10
|
+
EnvDriverMap,
|
|
11
|
+
VaultElementConfig,
|
|
12
|
+
} from "../config/index.ts";
|
|
13
|
+
import { resolveEffectiveDrivers, type EffectiveDriversConfig } from "../config/driver-defaults.ts";
|
|
6
14
|
import type { Manifest } from "../manifest/types.ts";
|
|
7
15
|
import { APP_PORT, CONSOLE_PORT, MCP_PORT } from "../runtime/types.ts";
|
|
8
16
|
import { hasFlag, wantsJson } from "./args.ts";
|
|
9
17
|
import { checkManifestPiiAsks } from "./doctor-pii.ts";
|
|
10
18
|
import { EXIT_OK, EXIT_RUNTIME } from "./exit.ts";
|
|
11
|
-
import { loadManifest } from "./load-config.ts";
|
|
19
|
+
import { loadManifest, loadOkeConfig } from "./load-config.ts";
|
|
12
20
|
import { isPortInUse } from "./ports.ts";
|
|
13
21
|
import { schemaFingerprint, readSchemaFingerprint } from "./schema.ts";
|
|
14
22
|
|
|
15
23
|
export { isPortInUse } from "./ports.ts";
|
|
16
24
|
|
|
25
|
+
/**
|
|
26
|
+
* `label → merged EnvDriverMap` pairs, in the fixed order shown by
|
|
27
|
+
* {@link formatDriversSummary} / the `drivers` JSON key.
|
|
28
|
+
*
|
|
29
|
+
* @param drivers - Fully-resolved drivers config
|
|
30
|
+
*/
|
|
31
|
+
function flattenEffectiveDrivers(
|
|
32
|
+
drivers: EffectiveDriversConfig,
|
|
33
|
+
): ReadonlyArray<readonly [string, EnvDriverMap]> {
|
|
34
|
+
return [
|
|
35
|
+
["store.sql", drivers.store.sql],
|
|
36
|
+
["store.kv", drivers.store.kv],
|
|
37
|
+
["store.files", drivers.store.files],
|
|
38
|
+
["signal", drivers.signal],
|
|
39
|
+
["clock", drivers.clock],
|
|
40
|
+
["journal", drivers.journal],
|
|
41
|
+
["vault", drivers.vault],
|
|
42
|
+
["channel.email", drivers.channel.email],
|
|
43
|
+
["channel.sms", drivers.channel.sms],
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param ref - Driver ref (string id, rich `{ driver }` object, or unset)
|
|
49
|
+
*/
|
|
50
|
+
function driverRefId(ref: DriverRef | undefined): string {
|
|
51
|
+
if (ref === undefined) return "—";
|
|
52
|
+
return typeof ref === "string" ? ref : ref.driver;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Render the fully-resolved drivers config as aligned text lines — every
|
|
57
|
+
* default plus every override, per env, for a developer whose own
|
|
58
|
+
* `oke.config.ts` only pins a handful of keys.
|
|
59
|
+
*
|
|
60
|
+
* @param drivers - Fully-resolved drivers config
|
|
61
|
+
* @param configEnv - Active env (marked in the active-driver column)
|
|
62
|
+
*/
|
|
63
|
+
function formatDriversSummary(drivers: EffectiveDriversConfig, configEnv: ConfigEnv): string {
|
|
64
|
+
const rows = flattenEffectiveDrivers(drivers);
|
|
65
|
+
const nameWidth = Math.max(...rows.map(([name]) => name.length));
|
|
66
|
+
const activeWidth = Math.max(...rows.map(([, map]) => driverRefId(map[configEnv]).length));
|
|
67
|
+
const lines = [`drivers (env=${configEnv}):`];
|
|
68
|
+
for (const [name, map] of rows) {
|
|
69
|
+
const active = driverRefId(map[configEnv]);
|
|
70
|
+
const full = `dev=${driverRefId(map.dev)} test=${driverRefId(map.test)} prod=${driverRefId(map.prod)}`;
|
|
71
|
+
lines.push(` ${name.padEnd(nameWidth)} ${active.padEnd(activeWidth)} { ${full} }`);
|
|
72
|
+
}
|
|
73
|
+
return lines.join("\n");
|
|
74
|
+
}
|
|
75
|
+
|
|
17
76
|
/** One doctor finding. */
|
|
18
77
|
export interface DoctorFinding {
|
|
19
78
|
readonly code:
|
|
@@ -23,7 +82,8 @@ export interface DoctorFinding {
|
|
|
23
82
|
| "db_drift"
|
|
24
83
|
| "tenancy"
|
|
25
84
|
| "driver"
|
|
26
|
-
| "pii_ask"
|
|
85
|
+
| "pii_ask"
|
|
86
|
+
| "vault_master_key";
|
|
27
87
|
readonly severity: "error" | "warn";
|
|
28
88
|
readonly message: string;
|
|
29
89
|
}
|
|
@@ -55,6 +115,23 @@ export interface DoctorOptions {
|
|
|
55
115
|
}>;
|
|
56
116
|
/** Skip live DB drift probe (default false). */
|
|
57
117
|
readonly skipDbDrift?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Inject the `drivers` block directly (tests) — skips loading
|
|
120
|
+
* `oke.config.ts` from disk. When omitted, `oke doctor` best-effort loads
|
|
121
|
+
* `oke.config.ts` from {@link cwd}; a project without one just gets the
|
|
122
|
+
* real, untouched defaults in the drivers summary.
|
|
123
|
+
*/
|
|
124
|
+
readonly driversConfig?: DriversConfig;
|
|
125
|
+
/**
|
|
126
|
+
* Inject the `vault` block directly (tests). When omitted, it is read
|
|
127
|
+
* from the same best-effort `oke.config.ts` load as {@link driversConfig}.
|
|
128
|
+
*/
|
|
129
|
+
readonly vaultConfig?: VaultElementConfig;
|
|
130
|
+
/**
|
|
131
|
+
* Active {@link ConfigEnv} for the drivers summary. Default: `dev` when
|
|
132
|
+
* `OKE_DOCKER=1` (via {@link env}), else `test`.
|
|
133
|
+
*/
|
|
134
|
+
readonly configEnv?: ConfigEnv;
|
|
58
135
|
readonly write?: (text: string) => void;
|
|
59
136
|
/** Write hints / progress (defaults to stderr). */
|
|
60
137
|
readonly writeErr?: (text: string) => void;
|
|
@@ -92,6 +169,25 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
92
169
|
const env = options.env ?? ((k) => Bun.env[k] ?? process.env[k]);
|
|
93
170
|
const secretNames = options.secrets ?? (manifest?.vault ? Object.keys(manifest.vault) : []);
|
|
94
171
|
|
|
172
|
+
// Fully-resolved `drivers.*` — real defaults + every override merged, so
|
|
173
|
+
// the picture is complete even when `oke.config.ts` only pins one key.
|
|
174
|
+
let driversConfig = options.driversConfig;
|
|
175
|
+
let vaultConfig = options.vaultConfig;
|
|
176
|
+
if (driversConfig === undefined || vaultConfig === undefined) {
|
|
177
|
+
try {
|
|
178
|
+
const loaded = (await loadOkeConfig(cwd)).config;
|
|
179
|
+
driversConfig ??= loaded.drivers;
|
|
180
|
+
vaultConfig ??= loaded.vault;
|
|
181
|
+
} catch {
|
|
182
|
+
// No oke.config.ts (or it failed to load) — show real defaults only.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Match `bootApplication`: production NODE_ENV → prod; compose → dev; else test.
|
|
186
|
+
const configEnv: ConfigEnv =
|
|
187
|
+
options.configEnv ??
|
|
188
|
+
(env("NODE_ENV") === "production" ? "prod" : env("OKE_DOCKER") === "1" ? "dev" : "test");
|
|
189
|
+
const effectiveDrivers = resolveEffectiveDrivers(driversConfig);
|
|
190
|
+
|
|
95
191
|
for (const name of secretNames) {
|
|
96
192
|
const value = env(name);
|
|
97
193
|
if (value === undefined || value.length === 0) {
|
|
@@ -104,6 +200,25 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
104
200
|
}
|
|
105
201
|
}
|
|
106
202
|
|
|
203
|
+
// An env-var master key is only as protected as the process environment:
|
|
204
|
+
// it lands in shell history, orchestrator manifests, and crash dumps.
|
|
205
|
+
const usesBuiltinVault =
|
|
206
|
+
vaultConfig !== undefined || driverRefId(effectiveDrivers.vault[configEnv]) === "vault";
|
|
207
|
+
if (
|
|
208
|
+
configEnv === "prod" &&
|
|
209
|
+
usesBuiltinVault &&
|
|
210
|
+
(vaultConfig?.encryption?.masterKey?.kind ?? "env") === "env"
|
|
211
|
+
) {
|
|
212
|
+
findings.push({
|
|
213
|
+
code: "vault_master_key",
|
|
214
|
+
// Fail closed in prod: an env-sourced master key collapses envelope
|
|
215
|
+
// protection the moment DB + env are stolen together.
|
|
216
|
+
severity: "error",
|
|
217
|
+
message:
|
|
218
|
+
'vault master key comes from an environment variable in prod — set vault.encryption.masterKey = { kind: "kms", keyId } (or use managed aws-secrets-manager) so the key is never readable from the process environment',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
107
222
|
const ports = options.ports ?? [APP_PORT, CONSOLE_PORT, MCP_PORT];
|
|
108
223
|
const probe = options.isPortInUse ?? isPortInUse;
|
|
109
224
|
for (const port of ports) {
|
|
@@ -133,13 +248,13 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
133
248
|
code: "schema_drift",
|
|
134
249
|
severity: "error",
|
|
135
250
|
message:
|
|
136
|
-
"core stub schema drift — run `oke schema generate` (schema/oke.ts; not domain migrations)",
|
|
251
|
+
"core stub schema drift — run `oke schema generate` (.oke/schema/oke.ts; not domain migrations)",
|
|
137
252
|
});
|
|
138
253
|
} else if (expected !== null && current === null) {
|
|
139
254
|
findings.push({
|
|
140
255
|
code: "schema_drift",
|
|
141
256
|
severity: "error",
|
|
142
|
-
message: "schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
|
|
257
|
+
message: ".oke/schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
|
|
143
258
|
});
|
|
144
259
|
}
|
|
145
260
|
}
|
|
@@ -157,7 +272,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
157
272
|
severity: "error",
|
|
158
273
|
message:
|
|
159
274
|
drift.detail ??
|
|
160
|
-
"domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in
|
|
275
|
+
"domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in dev)",
|
|
161
276
|
});
|
|
162
277
|
}
|
|
163
278
|
}
|
|
@@ -177,7 +292,9 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
177
292
|
const code = ok ? EXIT_OK : EXIT_RUNTIME;
|
|
178
293
|
|
|
179
294
|
if (json) {
|
|
180
|
-
write(
|
|
295
|
+
write(
|
|
296
|
+
`${JSON.stringify({ ok, findings, drivers: { env: configEnv, resolved: effectiveDrivers } }, null, 2)}\n`,
|
|
297
|
+
);
|
|
181
298
|
if (!ok) {
|
|
182
299
|
writeErr("Hint: fix error-severity findings, then re-run oke doctor.\n");
|
|
183
300
|
}
|
|
@@ -186,6 +303,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
186
303
|
|
|
187
304
|
if (findings.length === 0) {
|
|
188
305
|
write("oke doctor: ok\n");
|
|
306
|
+
write(`${formatDriversSummary(effectiveDrivers, configEnv)}\n`);
|
|
189
307
|
return { code: EXIT_OK, findings };
|
|
190
308
|
}
|
|
191
309
|
|
|
@@ -193,6 +311,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
|
|
|
193
311
|
for (const f of findings) {
|
|
194
312
|
write(` [${f.severity}] ${f.code}: ${f.message}\n`);
|
|
195
313
|
}
|
|
314
|
+
write(`${formatDriversSummary(effectiveDrivers, configEnv)}\n`);
|
|
196
315
|
return { code, findings };
|
|
197
316
|
}
|
|
198
317
|
|
|
@@ -208,16 +327,30 @@ export async function doctorCli(args: readonly string[]): Promise<number> {
|
|
|
208
327
|
}
|
|
209
328
|
|
|
210
329
|
let manifestPath: string | undefined;
|
|
330
|
+
let configEnv: ConfigEnv | undefined;
|
|
211
331
|
const json = wantsJson(args);
|
|
212
332
|
for (let i = 0; i < args.length; i++) {
|
|
213
333
|
const a = args[i]!;
|
|
214
334
|
if (a === "--manifest" || a === "-m") manifestPath = args[++i];
|
|
215
|
-
else if (a === "--
|
|
216
|
-
|
|
335
|
+
else if (a === "--env" || a === "-e") {
|
|
336
|
+
const value = args[++i];
|
|
337
|
+
if (value === "dev" || value === "test" || value === "prod") {
|
|
338
|
+
configEnv = value;
|
|
339
|
+
} else if (value === "local") {
|
|
340
|
+
configEnv = "test";
|
|
341
|
+
} else if (value === "docker") {
|
|
342
|
+
configEnv = "dev";
|
|
343
|
+
}
|
|
344
|
+
} else if (a === "--help" || a === "-h") {
|
|
345
|
+
console.log(`oke doctor [--manifest|-m path] [--env|-e dev|test|prod] [--json|-j]
|
|
217
346
|
oke doctor --diff|-d [--before|-b <path> --after|-a <path>] [--base|-B <branch>]
|
|
218
347
|
|
|
219
348
|
Verify secrets, ports, schema drift, and PII→model egress before serving.
|
|
349
|
+
Also prints drivers.* fully resolved (every default + every override merged)
|
|
350
|
+
for the active env, from oke.config.ts.
|
|
220
351
|
|
|
352
|
+
--env Env to resolve drivers for (default: prod when NODE_ENV=production,
|
|
353
|
+
dev when OKE_DOCKER=1 / \`oke dev\`, else test).
|
|
221
354
|
--diff CI gate: block undeclared contract breaks (Manifest Diff).
|
|
222
355
|
Default baseline is git merge-base (main/master) vs the working
|
|
223
356
|
tree; pass --before/--after for an explicit comparison.
|
|
@@ -227,6 +360,6 @@ Verify secrets, ports, schema drift, and PII→model egress before serving.
|
|
|
227
360
|
return EXIT_OK;
|
|
228
361
|
}
|
|
229
362
|
}
|
|
230
|
-
const { code } = await runDoctor({ manifestPath, json });
|
|
363
|
+
const { code } = await runDoctor({ manifestPath, configEnv, json });
|
|
231
364
|
return code;
|
|
232
365
|
}
|
|
@@ -24,22 +24,20 @@ async function tempDir(): Promise<string> {
|
|
|
24
24
|
const dualConfig = {
|
|
25
25
|
drivers: {
|
|
26
26
|
store: {
|
|
27
|
-
sql: {
|
|
27
|
+
sql: { dev: "postgres", test: "pglite", prod: "postgres" },
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
} as unknown as OkeConfig;
|
|
31
31
|
|
|
32
32
|
describe("resolveDrizzleKitEnv", () => {
|
|
33
|
-
test("
|
|
33
|
+
test("test env → postgresql dialect (pglite)", async () => {
|
|
34
34
|
const dir = await tempDir();
|
|
35
|
-
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "
|
|
36
|
-
expect(ctx.dialect).toBe("
|
|
37
|
-
expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("
|
|
38
|
-
expect(ctx.overlay.OKE_SQLITE_URL).toContain(".oke/app.sqlite");
|
|
39
|
-
expect(ctx.overlay.DATABASE_URL).toBeUndefined();
|
|
35
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "test");
|
|
36
|
+
expect(ctx.dialect).toBe("postgresql");
|
|
37
|
+
expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
|
|
40
38
|
});
|
|
41
39
|
|
|
42
|
-
test("
|
|
40
|
+
test("dev env → postgresql dialect + DATABASE_URL from compose env", async () => {
|
|
43
41
|
const dir = await tempDir();
|
|
44
42
|
await mkdir(join(dir, "docker"), { recursive: true });
|
|
45
43
|
await writeFile(
|
|
@@ -49,7 +47,7 @@ describe("resolveDrizzleKitEnv", () => {
|
|
|
49
47
|
const prevDb = process.env["DATABASE_URL"];
|
|
50
48
|
delete process.env["DATABASE_URL"];
|
|
51
49
|
try {
|
|
52
|
-
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "
|
|
50
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
|
|
53
51
|
expect(ctx.dialect).toBe("postgresql");
|
|
54
52
|
expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
|
|
55
53
|
expect(ctx.overlay.DATABASE_URL).toBe("postgres://u:p@127.0.0.1:5432/oke");
|
|
@@ -60,16 +58,16 @@ describe("resolveDrizzleKitEnv", () => {
|
|
|
60
58
|
}
|
|
61
59
|
});
|
|
62
60
|
|
|
63
|
-
test("
|
|
61
|
+
test("dev dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
|
|
64
62
|
const dir = await tempDir();
|
|
65
|
-
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "
|
|
63
|
+
const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
|
|
66
64
|
expect(ctx.dialect).toBe("postgresql");
|
|
67
65
|
});
|
|
68
66
|
|
|
69
67
|
test("unsupported driver id errors clearly", async () => {
|
|
70
68
|
const dir = await tempDir();
|
|
71
|
-
const bad = { drivers: { store: { sql: {
|
|
72
|
-
await expect(resolveDrizzleKitEnv(dir, bad, "
|
|
69
|
+
const bad = { drivers: { store: { sql: { test: "memory" } } } } as unknown as OkeConfig;
|
|
70
|
+
await expect(resolveDrizzleKitEnv(dir, bad, "test")).rejects.toThrow(/not supported/);
|
|
73
71
|
});
|
|
74
72
|
});
|
|
75
73
|
|
package/src/cli/drizzle-env.ts
CHANGED
|
@@ -64,36 +64,43 @@ export async function applyComposeEnvToProcess(cwd: string): Promise<readonly st
|
|
|
64
64
|
* Dialect comes from the configured driver via the exhaustive
|
|
65
65
|
* `SQL_DRIVER_TO_DRIZZLE_DIALECT` map — a missing/unsupported driver id is a
|
|
66
66
|
* compile-time gap there, and a clear runtime error here (never a silent
|
|
67
|
-
*
|
|
67
|
+
* fallback).
|
|
68
68
|
*
|
|
69
69
|
* @param cwd - Project root
|
|
70
|
-
* @param config - Loaded `oke.config.ts` (null →
|
|
71
|
-
* @param env - Active
|
|
70
|
+
* @param config - Loaded `oke.config.ts` (null → postgres/dev defaults)
|
|
71
|
+
* @param env - Active {@link ConfigEnv}
|
|
72
72
|
*/
|
|
73
73
|
export async function resolveDrizzleKitEnv(
|
|
74
74
|
cwd: string,
|
|
75
75
|
config: OkeConfig | null | undefined,
|
|
76
76
|
env: ConfigEnv,
|
|
77
77
|
): Promise<DrizzleKitEnvContext> {
|
|
78
|
-
const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "
|
|
79
|
-
if (driverId !== "
|
|
78
|
+
const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "postgres";
|
|
79
|
+
if (driverId !== "postgres" && driverId !== "pglite") {
|
|
80
80
|
throw new Error(
|
|
81
|
-
`oke db: store.sql driver "${driverId}" is not supported by drizzle-kit (
|
|
81
|
+
`oke db: store.sql driver "${driverId}" is not supported by drizzle-kit (postgres | pglite)`,
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
const dialect = drizzleDialectFromSqlDriver(driverId);
|
|
85
85
|
|
|
86
|
-
const compose = env === "
|
|
86
|
+
const compose = env === "dev" ? await readComposeEnv(cwd) : new Map<string, string>();
|
|
87
87
|
|
|
88
88
|
const overlay: Record<string, string> = { OKE_DRIZZLE_DIALECT: dialect };
|
|
89
|
-
if (
|
|
89
|
+
if (driverId === "pglite") {
|
|
90
90
|
const url =
|
|
91
|
+
process.env.OKE_PGLITE_URL ??
|
|
92
|
+
compose.get("OKE_PGLITE_URL") ??
|
|
93
|
+
(env === "test" ? "memory://" : ".oke/pgdata");
|
|
94
|
+
overlay.OKE_PGLITE_URL = url;
|
|
95
|
+
// drizzle-kit still wants a postgres-shaped URL for dialect postgresql when
|
|
96
|
+
// not using PGlite kit plugins — prefer DATABASE_URL from compose for push.
|
|
97
|
+
const pgUrl =
|
|
91
98
|
process.env.DATABASE_URL ?? compose.get("DATABASE_URL") ?? compose.get("OKE_STORE_SQL_URL");
|
|
92
|
-
if (
|
|
99
|
+
if (pgUrl !== undefined) overlay.DATABASE_URL = pgUrl;
|
|
93
100
|
} else {
|
|
94
101
|
const url =
|
|
95
|
-
process.env.
|
|
96
|
-
overlay.
|
|
102
|
+
process.env.DATABASE_URL ?? compose.get("DATABASE_URL") ?? compose.get("OKE_STORE_SQL_URL");
|
|
103
|
+
if (url !== undefined) overlay.DATABASE_URL = url;
|
|
97
104
|
}
|
|
98
105
|
return { env, dialect, overlay };
|
|
99
106
|
}
|
|
@@ -5,32 +5,29 @@
|
|
|
5
5
|
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/** PostgreSQL-only template (matches create-oke templates' drizzle.config.ts). */
|
|
9
9
|
const DRIZZLE_CONFIG_TEMPLATE = `import { defineConfig } from "drizzle-kit";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Domain schema sync for \`oke db push|generate|migrate\`.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* for the active dialect as a pre-step of \`oke db\` / \`oke dev\`.
|
|
14
|
+
* SQL is PostgreSQL-only (postgres / pglite). Domain tables come from
|
|
15
|
+
* \`src/db/schema.decl.ts\` → \`src/db/schema.drizzle.ts\` (emitted as a
|
|
16
|
+
* pre-step of \`oke db\` / \`oke dev\`).
|
|
18
17
|
*
|
|
19
|
-
*
|
|
18
|
+
* System / auth / plugin stubs are separate: \`oke schema generate\` →
|
|
19
|
+
* \`.oke/schema/oke.ts\` (gitignored with \`.oke/\`). Re-run after adding plugins.
|
|
20
|
+
*
|
|
21
|
+
* Versioned prod migrations land in \`./src/db/migrations\`.
|
|
20
22
|
*/
|
|
21
|
-
const dialect = (process.env.OKE_DRIZZLE_DIALECT ?? "sqlite") as "sqlite" | "postgresql";
|
|
22
|
-
|
|
23
23
|
export default defineConfig({
|
|
24
|
-
dialect,
|
|
25
|
-
schema: "./src/db/schema.
|
|
24
|
+
dialect: "postgresql",
|
|
25
|
+
schema: "./src/db/schema.drizzle.ts",
|
|
26
26
|
out: "./src/db/migrations",
|
|
27
27
|
// Core runtime tables (oke_crons, …) are created by drivers — not domain schema.
|
|
28
28
|
tablesFilter: ["!oke_*"],
|
|
29
29
|
dbCredentials: {
|
|
30
|
-
url:
|
|
31
|
-
dialect === "postgresql"
|
|
32
|
-
? (process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL!)
|
|
33
|
-
: (process.env.OKE_SQLITE_URL ?? "file:.oke/app.sqlite"),
|
|
30
|
+
url: process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL!,
|
|
34
31
|
},
|
|
35
32
|
});
|
|
36
33
|
`;
|
|
@@ -15,23 +15,23 @@ import {
|
|
|
15
15
|
const SAMPLE: OkeConfig = {
|
|
16
16
|
drivers: {
|
|
17
17
|
store: {
|
|
18
|
-
sql: {
|
|
19
|
-
kv: {
|
|
18
|
+
sql: { dev: "postgres", test: "pglite", prod: "postgres" },
|
|
19
|
+
kv: { dev: "redis", test: "memory", prod: "redis" },
|
|
20
20
|
},
|
|
21
|
-
signal: {
|
|
22
|
-
clock: {
|
|
23
|
-
vault: {
|
|
21
|
+
signal: { dev: "redis", test: "memory", prod: "redis" },
|
|
22
|
+
clock: { dev: "file", test: "memory", prod: "file" },
|
|
23
|
+
vault: { dev: "vault", test: "env", prod: "vault" },
|
|
24
24
|
channel: {
|
|
25
|
-
email: {
|
|
25
|
+
email: { dev: "smtp", test: "console", prod: "smtp" },
|
|
26
26
|
},
|
|
27
|
-
ai: {
|
|
27
|
+
ai: { dev: "mock", test: "mock" },
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
describe("hero-meta", () => {
|
|
32
|
-
test("resolveDevProfile maps docker
|
|
33
|
-
expect(resolveDevProfile({ docker: false })).toBe("
|
|
34
|
-
expect(resolveDevProfile({ docker: true })).toBe("
|
|
32
|
+
test("resolveDevProfile maps NODE_ENV (docker flag ignored)", () => {
|
|
33
|
+
expect(resolveDevProfile({ docker: false })).toBe("dev");
|
|
34
|
+
expect(resolveDevProfile({ docker: true })).toBe("dev");
|
|
35
35
|
expect(resolveDevProfile({ docker: false, nodeEnv: "test" })).toBe("test");
|
|
36
36
|
expect(resolveDevProfile({ docker: false, nodeEnv: "production" })).toBe("production");
|
|
37
37
|
});
|
|
@@ -48,12 +48,12 @@ describe("hero-meta", () => {
|
|
|
48
48
|
"channel",
|
|
49
49
|
"ai",
|
|
50
50
|
]);
|
|
51
|
-
expect(rows.find((r) => r.element === "store")?.detail).toContain("
|
|
51
|
+
expect(rows.find((r) => r.element === "store")?.detail).toContain("pglite");
|
|
52
52
|
expect(rows.find((r) => r.element === "flow")?.detail).toBe("");
|
|
53
53
|
expect(rows.find((r) => r.element === "flow")?.status).toBe("ready");
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
test("
|
|
56
|
+
test("dev (compose) profile applies to every element", () => {
|
|
57
57
|
const rows = resolveHeroElements(SAMPLE, {
|
|
58
58
|
docker: true,
|
|
59
59
|
sqlDriver: "postgres",
|
|
@@ -62,7 +62,7 @@ describe("hero-meta", () => {
|
|
|
62
62
|
const store = rows.find((r) => r.element === "store")?.detail ?? "";
|
|
63
63
|
expect(store).toContain("postgres");
|
|
64
64
|
expect(store).toContain("redis");
|
|
65
|
-
expect(store).not.toContain("
|
|
65
|
+
expect(store).not.toContain("pglite");
|
|
66
66
|
expect(rows.find((r) => r.element === "signal")?.detail).toBe("redis");
|
|
67
67
|
expect(rows.find((r) => r.element === "clock")?.detail).toBe("file");
|
|
68
68
|
expect(rows.find((r) => r.element === "gate")?.detail).toBe("redis");
|
|
@@ -78,8 +78,8 @@ describe("hero-meta", () => {
|
|
|
78
78
|
version: "0.2.4",
|
|
79
79
|
nodeEnv: "development",
|
|
80
80
|
});
|
|
81
|
-
expect(snap.profile).toBe("
|
|
82
|
-
expect(snap.runtimeEnv).toBe("
|
|
81
|
+
expect(snap.profile).toBe("dev");
|
|
82
|
+
expect(snap.runtimeEnv).toBe("dev");
|
|
83
83
|
expect(decodeHeroSnapshot(encodeHeroSnapshot(snap))).toEqual(snap);
|
|
84
84
|
});
|
|
85
85
|
|
package/src/cli/hero-meta.ts
CHANGED
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
} from "../config/index.ts";
|
|
12
12
|
import type { DevStatus } from "../term.ts";
|
|
13
13
|
|
|
14
|
-
/** How the process is running (
|
|
15
|
-
export type DevRuntimeProfile = "
|
|
14
|
+
/** How the process is running (dev Compose → production). */
|
|
15
|
+
export type DevRuntimeProfile = "dev" | "test" | "production";
|
|
16
16
|
|
|
17
|
-
/** Data plane: still on a laptop vs
|
|
18
|
-
export type DevRuntimeEnv = "
|
|
17
|
+
/** Data plane: still on a laptop vs production deploy. */
|
|
18
|
+
export type DevRuntimeEnv = "dev" | "production";
|
|
19
19
|
|
|
20
20
|
/** One of the eight elements with its active driver summary. */
|
|
21
21
|
export type HeroElementRow = {
|
|
@@ -69,16 +69,17 @@ export function resolveDevProfile(options: {
|
|
|
69
69
|
}): DevRuntimeProfile {
|
|
70
70
|
if (options.nodeEnv === "test") return "test";
|
|
71
71
|
if (options.nodeEnv === "production") return "production";
|
|
72
|
-
|
|
72
|
+
void options.docker;
|
|
73
|
+
return "dev";
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Data-plane environment (
|
|
77
|
+
* Data-plane environment (dev machine vs production deploy).
|
|
77
78
|
*
|
|
78
79
|
* @param profile - Runtime profile
|
|
79
80
|
*/
|
|
80
81
|
export function resolveDevRuntimeEnv(profile: DevRuntimeProfile): DevRuntimeEnv {
|
|
81
|
-
return profile === "production" ? "production" : "
|
|
82
|
+
return profile === "production" ? "production" : "dev";
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/**
|
|
@@ -155,13 +156,13 @@ export function resolveHeroElements(
|
|
|
155
156
|
readonly aiModel?: string;
|
|
156
157
|
/** AI readiness when known (model loading / ready). */
|
|
157
158
|
readonly aiStatus?: DevStatus;
|
|
158
|
-
/** Which driver map column to read (default `
|
|
159
|
+
/** Which driver map column to read (default `test` unless `docker`). */
|
|
159
160
|
readonly configEnv?: ConfigEnv;
|
|
160
161
|
},
|
|
161
162
|
): readonly HeroElementRow[] {
|
|
162
163
|
const drivers = config?.drivers;
|
|
163
164
|
// `-d` reads the `docker` profile for every element (not a store-only override).
|
|
164
|
-
const configEnv: ConfigEnv = options.configEnv ?? (options.docker ? "
|
|
165
|
+
const configEnv: ConfigEnv = options.configEnv ?? (options.docker ? "dev" : "test");
|
|
165
166
|
|
|
166
167
|
// Gate rates share `drivers.store.kv` (no separate drivers.gate).
|
|
167
168
|
const gateKv =
|
package/src/cli/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
3
|
* `oke` CLI entry.
|
|
4
|
+
*
|
|
5
|
+
* Bare `oke` (TTY) → interactive Ink TUI. `oke <command>` runs directly.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import { aiCli } from "./ai.ts";
|
|
@@ -23,11 +25,32 @@ import { replayCli } from "./replay.ts";
|
|
|
23
25
|
import { schemaCli } from "./schema.ts";
|
|
24
26
|
import { stackCli } from "./stack.ts";
|
|
25
27
|
import { startCli } from "./start.ts";
|
|
28
|
+
import { testCli } from "./test.ts";
|
|
26
29
|
import { upgradeCli } from "./upgrade.ts";
|
|
27
30
|
import { vaultCli } from "./vault-cmd.ts";
|
|
28
31
|
|
|
29
|
-
const
|
|
32
|
+
const rawArgv = process.argv.slice(2);
|
|
33
|
+
const wantTui = rawArgv.includes("--tui");
|
|
34
|
+
const argv = rawArgv.filter((a) => a !== "--tui");
|
|
30
35
|
const [cmd, sub, ...rest] = argv;
|
|
36
|
+
/** Nested TUI / slash child — never re-enter the interactive shell. */
|
|
37
|
+
const noTui = process.env["OKE_NO_TUI"] === "1";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Launch interactive TUI when allowed; otherwise print help (non-TTY).
|
|
41
|
+
*/
|
|
42
|
+
async function maybeLaunchTui(): Promise<never> {
|
|
43
|
+
const { canRenderTui, launchTui } = await import("./tui/launch.ts");
|
|
44
|
+
if (noTui || !canRenderTui(process.stdout)) {
|
|
45
|
+
console.log(formatOkeHelp());
|
|
46
|
+
process.exit(EXIT_USAGE);
|
|
47
|
+
}
|
|
48
|
+
process.exit(await launchTui(process.cwd()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (wantTui && !noTui) {
|
|
52
|
+
await maybeLaunchTui();
|
|
53
|
+
}
|
|
31
54
|
|
|
32
55
|
if (cmd === "dev") {
|
|
33
56
|
process.exit(await devCli(sub ? [sub, ...rest] : rest));
|
|
@@ -37,6 +60,10 @@ if (cmd === "mode") {
|
|
|
37
60
|
process.exit(await modeCli(sub ? [sub, ...rest] : rest));
|
|
38
61
|
}
|
|
39
62
|
|
|
63
|
+
if (cmd === "test") {
|
|
64
|
+
process.exit(await testCli(sub ? [sub, ...rest] : rest));
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
if (cmd === "start") {
|
|
41
68
|
process.exit(await startCli(sub ? [sub, ...rest] : rest));
|
|
42
69
|
}
|
|
@@ -109,11 +136,14 @@ if (cmd === "completion") {
|
|
|
109
136
|
process.exit(completionCli(sub ? [sub, ...rest] : rest));
|
|
110
137
|
}
|
|
111
138
|
|
|
112
|
-
if (cmd ===
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
140
|
+
console.log(`${formatOkeHelp()}${EXIT_CODE_HELP}`);
|
|
141
|
+
process.exit(EXIT_OK);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (cmd === undefined) {
|
|
145
|
+
// Bare `oke` — TUI on TTY; help + EXIT_USAGE when piped / CI / OKE_NO_TUI.
|
|
146
|
+
await maybeLaunchTui();
|
|
117
147
|
}
|
|
118
148
|
|
|
119
149
|
console.error(`Unknown command: ${cmd}${sub ? ` ${sub}` : ""}`);
|