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
|
@@ -8,7 +8,12 @@ import { tmpdir } from "node:os";
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { composeProjectName, type DockerRunner } from "../docker/cleanup.ts";
|
|
10
10
|
import { dockerCli } from "./docker.ts";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
dockerCleanHelp,
|
|
13
|
+
parseDockerCleanArgs,
|
|
14
|
+
runDockerClean,
|
|
15
|
+
selectionEntries,
|
|
16
|
+
} from "./docker-clean.ts";
|
|
12
17
|
import { EXIT_OK, EXIT_USAGE } from "./exit.ts";
|
|
13
18
|
import { formatOkeHelp, flagTokensFor, OKE_COMMANDS } from "./registry.ts";
|
|
14
19
|
|
|
@@ -176,4 +181,31 @@ describe("oke docker clean", () => {
|
|
|
176
181
|
expect(flagTokensFor(docker, "clean")).toContain("--all");
|
|
177
182
|
expect(formatOkeHelp()).toContain("clean");
|
|
178
183
|
});
|
|
184
|
+
|
|
185
|
+
test("selectionEntries is one row per project (not per container)", () => {
|
|
186
|
+
const entries = selectionEntries([
|
|
187
|
+
{
|
|
188
|
+
project: "oke-dev-aaaaaa",
|
|
189
|
+
containers: [
|
|
190
|
+
{
|
|
191
|
+
id: "1",
|
|
192
|
+
name: "oke-dev-aaaaaa-redis-1",
|
|
193
|
+
service: "redis",
|
|
194
|
+
state: "running",
|
|
195
|
+
project: "oke-dev-aaaaaa",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: "2",
|
|
199
|
+
name: "oke-dev-aaaaaa-postgres-1",
|
|
200
|
+
service: "postgres",
|
|
201
|
+
state: "running",
|
|
202
|
+
project: "oke-dev-aaaaaa",
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
]);
|
|
207
|
+
expect(entries).toHaveLength(1);
|
|
208
|
+
expect(entries[0]?.label).toBe("oke-dev-aaaaaa");
|
|
209
|
+
expect(entries[0]?.hint).toContain("2 container");
|
|
210
|
+
});
|
|
179
211
|
});
|
package/src/cli/docker-clean.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
resolveSelection,
|
|
13
13
|
selectionKey,
|
|
14
14
|
type DockerRunner,
|
|
15
|
-
type OkeContainer,
|
|
16
15
|
type OkeStack,
|
|
17
16
|
} from "../docker/cleanup.ts";
|
|
18
17
|
import { EXIT_OK, EXIT_RUNTIME, EXIT_USAGE } from "./exit.ts";
|
|
@@ -49,8 +48,8 @@ export function dockerCleanHelp(): string {
|
|
|
49
48
|
|
|
50
49
|
Remove leftover OKE docker compose stacks (containers, networks, volumes).
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
Selection is by compose project name (oke-dev-<id>) — never per-service.
|
|
52
|
+
Inside a project: focuses the current stack; optionally include others.
|
|
54
53
|
Outside a project: list every oke-dev-* stack and pick what to remove.
|
|
55
54
|
|
|
56
55
|
--yes, -y Non-interactive; required when stdin is not a TTY
|
|
@@ -235,10 +234,10 @@ async function runInteractive(options: {
|
|
|
235
234
|
if (current.length === 0) {
|
|
236
235
|
write(`No stack for this project (${currentProject}).\n`);
|
|
237
236
|
} else {
|
|
238
|
-
const keys = await
|
|
237
|
+
const keys = await pickProjectKeys({
|
|
239
238
|
stacks: current,
|
|
240
239
|
scope: "current",
|
|
241
|
-
message: `
|
|
240
|
+
message: `Stack for this project (${currentProject})`,
|
|
242
241
|
initialAll: true,
|
|
243
242
|
pickKeys,
|
|
244
243
|
});
|
|
@@ -256,7 +255,7 @@ async function runInteractive(options: {
|
|
|
256
255
|
return EXIT_OK;
|
|
257
256
|
}
|
|
258
257
|
if (include) {
|
|
259
|
-
const keys = await
|
|
258
|
+
const keys = await pickProjectKeys({
|
|
260
259
|
stacks: others,
|
|
261
260
|
scope: "other",
|
|
262
261
|
message: "Other OKE stacks on this machine",
|
|
@@ -271,7 +270,7 @@ async function runInteractive(options: {
|
|
|
271
270
|
}
|
|
272
271
|
}
|
|
273
272
|
} else {
|
|
274
|
-
const keys = await
|
|
273
|
+
const keys = await pickProjectKeys({
|
|
275
274
|
stacks,
|
|
276
275
|
scope: "all",
|
|
277
276
|
message: "OKE docker stacks on this machine",
|
|
@@ -330,7 +329,10 @@ async function askIncludeOthersPrompt(): Promise<boolean | null> {
|
|
|
330
329
|
return value;
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Multiselect compose projects (one row per `oke-dev-*` name).
|
|
334
|
+
*/
|
|
335
|
+
async function pickProjectKeys(options: {
|
|
334
336
|
readonly stacks: readonly OkeStack[];
|
|
335
337
|
readonly scope: "current" | "other" | "all";
|
|
336
338
|
readonly message: string;
|
|
@@ -344,7 +346,7 @@ async function pickContainerKeys(options: {
|
|
|
344
346
|
if (entries.length === 0) return [];
|
|
345
347
|
|
|
346
348
|
const value = await multiselect({
|
|
347
|
-
message: `${message} —
|
|
349
|
+
message: `${message} — removes the whole stack (containers, networks, volumes)`,
|
|
348
350
|
options: entries.map((e) => ({
|
|
349
351
|
value: e.key,
|
|
350
352
|
label: e.label,
|
|
@@ -357,34 +359,27 @@ async function pickContainerKeys(options: {
|
|
|
357
359
|
return value;
|
|
358
360
|
}
|
|
359
361
|
|
|
360
|
-
|
|
362
|
+
/**
|
|
363
|
+
* One selectable row per compose project.
|
|
364
|
+
*
|
|
365
|
+
* @param stacks - Discovered stacks
|
|
366
|
+
*/
|
|
367
|
+
export function selectionEntries(stacks: readonly OkeStack[]): readonly {
|
|
361
368
|
readonly key: string;
|
|
362
369
|
readonly label: string;
|
|
363
370
|
readonly hint?: string;
|
|
364
371
|
}[] {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
out.push({
|
|
377
|
-
key: selectionKey(stack.project, c.name),
|
|
378
|
-
label: `${stack.project} · ${displayContainer(c)}`,
|
|
379
|
-
hint: c.state || undefined,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return out;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function displayContainer(c: OkeContainer): string {
|
|
387
|
-
return c.service && c.service !== c.name ? `${c.service} (${c.name})` : c.name;
|
|
372
|
+
return stacks.map((stack) => {
|
|
373
|
+
const n = stack.containers.length;
|
|
374
|
+
return {
|
|
375
|
+
key: selectionKey(stack.project),
|
|
376
|
+
label: stack.project,
|
|
377
|
+
hint:
|
|
378
|
+
n === 0
|
|
379
|
+
? "no containers · volumes/networks may remain"
|
|
380
|
+
: `${n} container${n === 1 ? "" : "s"}`,
|
|
381
|
+
};
|
|
382
|
+
});
|
|
388
383
|
}
|
|
389
384
|
|
|
390
385
|
async function tearDownProjects(
|
|
@@ -39,11 +39,12 @@ describe("oke docker CLI", () => {
|
|
|
39
39
|
expect(code).toBe(0);
|
|
40
40
|
expect(result).toBeDefined();
|
|
41
41
|
expect(await Bun.file(join(dir, "docker/Dockerfile")).exists()).toBe(true);
|
|
42
|
-
expect(await Bun.file(join(dir, "docker/compose.
|
|
43
|
-
const yml = await Bun.file(join(dir, "docker/compose.
|
|
42
|
+
expect(await Bun.file(join(dir, "docker/docker-compose.yml")).exists()).toBe(true);
|
|
43
|
+
const yml = await Bun.file(join(dir, "docker/docker-compose.yml")).text();
|
|
44
44
|
expect(yml).not.toContain("test-password-not-in-yaml");
|
|
45
45
|
expect(yml).toContain(".env.docker");
|
|
46
|
-
expect(
|
|
46
|
+
expect(yml).toContain("/_/ready");
|
|
47
|
+
expect(logs.join("")).toContain("docker-compose.override.yml");
|
|
47
48
|
});
|
|
48
49
|
});
|
|
49
50
|
|
|
@@ -136,7 +137,7 @@ describe("oke images list", () => {
|
|
|
136
137
|
});
|
|
137
138
|
|
|
138
139
|
describe("oke schema generate", () => {
|
|
139
|
-
test("emits schema/oke.ts and --check catches drift", async () => {
|
|
140
|
+
test("emits .oke/schema/oke.ts and --check catches drift", async () => {
|
|
140
141
|
const dir = await mkdtemp(join(tmpdir(), "oke-cli-schema-"));
|
|
141
142
|
const code = await runSchemaGenerate({
|
|
142
143
|
cwd: dir,
|
|
@@ -144,7 +145,7 @@ describe("oke schema generate", () => {
|
|
|
144
145
|
write: () => {},
|
|
145
146
|
});
|
|
146
147
|
expect(code).toBe(0);
|
|
147
|
-
const src = await Bun.file(join(dir, "schema/oke.ts")).text();
|
|
148
|
+
const src = await Bun.file(join(dir, ".oke/schema/oke.ts")).text();
|
|
148
149
|
expect(src).toContain("bookings");
|
|
149
150
|
expect(src).toContain("oke_roles");
|
|
150
151
|
|
|
@@ -156,7 +157,7 @@ describe("oke schema generate", () => {
|
|
|
156
157
|
});
|
|
157
158
|
expect(checkOk).toBe(0);
|
|
158
159
|
|
|
159
|
-
await Bun.write(join(dir, "schema/oke.ts"), emitSchemaSource(["other"]));
|
|
160
|
+
await Bun.write(join(dir, ".oke/schema/oke.ts"), emitSchemaSource(["other"]));
|
|
160
161
|
const checkFail = await runSchemaGenerate({
|
|
161
162
|
cwd: dir,
|
|
162
163
|
extraTables: ["bookings"],
|
|
@@ -167,7 +168,7 @@ describe("oke schema generate", () => {
|
|
|
167
168
|
});
|
|
168
169
|
});
|
|
169
170
|
|
|
170
|
-
describe("oke dev
|
|
171
|
+
describe("oke dev compose plan", () => {
|
|
171
172
|
test("plans docker with postgres and writes nothing on dryRun", async () => {
|
|
172
173
|
const dir = await mkdtemp(join(tmpdir(), "oke-cli-dev-dry-"));
|
|
173
174
|
await Bun.write(join(dir, "src/app.ts"), "export {}\n");
|
|
@@ -182,11 +183,11 @@ describe("oke dev --docker", () => {
|
|
|
182
183
|
});
|
|
183
184
|
expect(code).toBe(0);
|
|
184
185
|
expect(plan?.stackRoles).toEqual(["store.sql"]);
|
|
185
|
-
expect(plan?.composeFiles
|
|
186
|
-
expect(plan?.composeFiles).
|
|
186
|
+
expect(plan?.composeFiles).toContain("docker-compose.yml");
|
|
187
|
+
expect(plan?.composeFiles?.some((f) => f.includes("store.sql"))).toBe(false);
|
|
187
188
|
expect(plan?.stackEnv?.DATABASE_URL).toContain("postgres://");
|
|
188
|
-
expect(await Bun.file(join(dir, "compose.yml")).exists()).toBe(false);
|
|
189
|
-
expect(await Bun.file(join(dir, "docker/compose.yml")).exists()).toBe(false);
|
|
189
|
+
expect(await Bun.file(join(dir, "docker-compose.yml")).exists()).toBe(false);
|
|
190
|
+
expect(await Bun.file(join(dir, "docker/docker-compose.yml")).exists()).toBe(false);
|
|
190
191
|
});
|
|
191
192
|
});
|
|
192
193
|
|
package/src/cli/docker.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `oke docker` — Dockerfile +
|
|
2
|
+
* `oke docker` — Dockerfile + compose artefacts · `clean` leftover stacks.
|
|
3
|
+
*
|
|
4
|
+
* Default layout is a single production-grade `docker-compose.yml`.
|
|
5
|
+
* Opt into `--split` (per-role files) or `--stack` (`docker-stack.yml`).
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
8
|
import { resolve } from "node:path";
|
|
6
9
|
import {
|
|
7
10
|
DEFAULT_DOCKER_DIR,
|
|
11
|
+
DEFAULT_SERVER_BUDGET,
|
|
8
12
|
deriveInfrastructure,
|
|
9
13
|
writeDerivedFiles,
|
|
14
|
+
type ComposeLayout,
|
|
10
15
|
type DeriveOptions,
|
|
11
16
|
type DeriveResult,
|
|
12
17
|
} from "../docker/index.ts";
|
|
@@ -18,7 +23,14 @@ import { loadOkeConfig, loadManifest, resolveImages } from "./load-config.ts";
|
|
|
18
23
|
export interface DockerCliOptions {
|
|
19
24
|
readonly cwd?: string;
|
|
20
25
|
readonly outDir?: string;
|
|
26
|
+
/** Production overlays + resource budget (default true for `oke docker`). */
|
|
21
27
|
readonly prod?: boolean;
|
|
28
|
+
/** Compose layout (default `single`). */
|
|
29
|
+
readonly layout?: ComposeLayout;
|
|
30
|
+
/** Host CPUs for resource apportioning (default 4). */
|
|
31
|
+
readonly serverCpus?: number;
|
|
32
|
+
/** Host RAM GiB for resource apportioning (default 8). */
|
|
33
|
+
readonly serverMemoryGb?: number;
|
|
22
34
|
readonly configPath?: string;
|
|
23
35
|
readonly manifestPath?: string;
|
|
24
36
|
readonly write?: (text: string) => void;
|
|
@@ -75,11 +87,19 @@ export async function runDockerDerive(
|
|
|
75
87
|
return { code: 1 };
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
const layout = options.layout ?? "single";
|
|
91
|
+
const prod = options.prod ?? true;
|
|
92
|
+
const serverCpus = options.serverCpus ?? DEFAULT_SERVER_BUDGET.cpus;
|
|
93
|
+
const serverMemoryGb = options.serverMemoryGb ?? DEFAULT_SERVER_BUDGET.memoryGb;
|
|
94
|
+
|
|
78
95
|
try {
|
|
79
96
|
const result = deriveInfrastructure({
|
|
80
97
|
images,
|
|
81
98
|
app,
|
|
82
|
-
prod
|
|
99
|
+
prod,
|
|
100
|
+
layout,
|
|
101
|
+
serverCpus,
|
|
102
|
+
serverMemoryGb,
|
|
83
103
|
outDir,
|
|
84
104
|
composeDir,
|
|
85
105
|
includeApp: true,
|
|
@@ -90,25 +110,22 @@ export async function runDockerDerive(
|
|
|
90
110
|
await writeDerivedFiles(result, outDir, { writeStackEnv: false });
|
|
91
111
|
}
|
|
92
112
|
|
|
93
|
-
const vaultSpec = result.specs.find((s) => s.role === "vault");
|
|
94
|
-
if (vaultSpec) {
|
|
95
|
-
const rel = composeDir === "." ? ".oke/openbao/" : `${composeDir}/../.oke/openbao/`;
|
|
96
|
-
write(
|
|
97
|
-
`oke docker: OpenBao first boot — bring the stack up (e.g. \`oke dev -d\`), and oke ` +
|
|
98
|
-
`will initialize + unseal it once. Root/unseal material stays on the host at ${rel} ` +
|
|
99
|
-
`(0600, gitignored) — back it up; losing it loses every secret permanently.\n`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
113
|
write(`oke docker: wrote ${result.files.length} file(s) → ${composeDir}/\n`);
|
|
104
114
|
for (const f of result.files) write(` ${composeDir}/${f.path}\n`);
|
|
105
115
|
write(
|
|
106
|
-
`
|
|
116
|
+
`layout: ${layout} · server budget: ${serverCpus} CPU / ${serverMemoryGb} GiB` +
|
|
117
|
+
(prod ? " · production-grade\n" : "\n"),
|
|
107
118
|
);
|
|
108
119
|
write(
|
|
109
|
-
`(${composeDir}/
|
|
110
|
-
`(${composeDir}/compose.all.yml is layers 1–3 merged — single-file alternative for Swarm / one \`-c\`)\n`,
|
|
120
|
+
`compose merge order (cwd ${composeDir}/):\n${result.composeFiles.map((f) => ` -f ${f}`).join("\n")}\n`,
|
|
111
121
|
);
|
|
122
|
+
if (layout === "single") {
|
|
123
|
+
write(`(${composeDir}/docker-compose.override.yml is user-owned — never written by oke)\n`);
|
|
124
|
+
} else if (layout === "split") {
|
|
125
|
+
write(`(${composeDir}/compose.override.yml is user-owned — never written by oke)\n`);
|
|
126
|
+
} else {
|
|
127
|
+
write(`(Swarm: docker stack deploy -c ${composeDir}/docker-stack.yml <name>)\n`);
|
|
128
|
+
}
|
|
112
129
|
return { code: 0, result };
|
|
113
130
|
} catch (err) {
|
|
114
131
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -127,21 +144,61 @@ export async function dockerCli(args: readonly string[]): Promise<number> {
|
|
|
127
144
|
return dockerCleanCli(rest);
|
|
128
145
|
}
|
|
129
146
|
|
|
130
|
-
let prod
|
|
147
|
+
let prod: boolean | undefined;
|
|
148
|
+
let layout: ComposeLayout | undefined;
|
|
149
|
+
let serverCpus: number | undefined;
|
|
150
|
+
let serverMemoryGb: number | undefined;
|
|
131
151
|
let outDir: string | undefined;
|
|
132
152
|
let configPath: string | undefined;
|
|
133
153
|
let manifestPath: string | undefined;
|
|
134
154
|
for (let i = 0; i < args.length; i++) {
|
|
135
155
|
const a = args[i]!;
|
|
136
156
|
if (a === "--prod" || a === "-p") prod = true;
|
|
137
|
-
else if (a === "--
|
|
157
|
+
else if (a === "--no-prod") prod = false;
|
|
158
|
+
else if (a === "--split") layout = "split";
|
|
159
|
+
else if (a === "--stack") layout = "stack";
|
|
160
|
+
else if (a === "--single") layout = "single";
|
|
161
|
+
else if (a === "--cpus" || a === "--cpu") {
|
|
162
|
+
const raw = args[++i];
|
|
163
|
+
const n = Number(raw);
|
|
164
|
+
if (!raw || !(n > 0)) {
|
|
165
|
+
console.error("oke docker: --cpus requires a positive number");
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
serverCpus = n;
|
|
169
|
+
} else if (a === "--memory" || a === "--mem" || a === "--memory-gb") {
|
|
170
|
+
const raw = args[++i];
|
|
171
|
+
const n = Number(raw);
|
|
172
|
+
if (!raw || !(n > 0)) {
|
|
173
|
+
console.error("oke docker: --memory requires a positive GiB number");
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
serverMemoryGb = n;
|
|
177
|
+
} else if (a === "--out" || a === "-o") outDir = args[++i];
|
|
138
178
|
else if (a === "--config" || a === "-c") configPath = args[++i];
|
|
139
179
|
else if (a === "--manifest" || a === "-m") manifestPath = args[++i];
|
|
140
180
|
else if (a === "--help" || a === "-h") {
|
|
141
|
-
console.log(`oke docker [
|
|
181
|
+
console.log(`oke docker [options]
|
|
142
182
|
|
|
143
|
-
Derive
|
|
144
|
-
|
|
183
|
+
Derive production-grade Docker artefacts under docker/ (default).
|
|
184
|
+
|
|
185
|
+
Layouts (mutually exclusive; default --single):
|
|
186
|
+
--single docker-compose.yml + Dockerfile (+ .env.docker via oke dev)
|
|
187
|
+
--split compose.yml + compose.<role>.yml (+ compose.prod.yml)
|
|
188
|
+
--stack docker-stack.yml for docker stack deploy
|
|
189
|
+
|
|
190
|
+
Server budget (sizes deploy.resources; default 4 CPU / 8 GiB):
|
|
191
|
+
--cpus <n> host CPUs
|
|
192
|
+
--memory <gib> host RAM in GiB
|
|
193
|
+
|
|
194
|
+
Other:
|
|
195
|
+
--prod / -p production overlays (default on)
|
|
196
|
+
--no-prod skip readiness / deploy / resource budget
|
|
197
|
+
--out / -o output directory (default docker)
|
|
198
|
+
--config / -c oke.config.ts path
|
|
199
|
+
--manifest / -m Manifest path
|
|
200
|
+
|
|
201
|
+
Credentials are never written into YAML. PgDog configs land in docker/pgdog/.
|
|
145
202
|
|
|
146
203
|
${dockerCleanHelp()}`);
|
|
147
204
|
return EXIT_OK;
|
|
@@ -149,10 +206,28 @@ ${dockerCleanHelp()}`);
|
|
|
149
206
|
console.error(`oke docker: unknown subcommand ${a}`);
|
|
150
207
|
console.error("Run `oke docker --help` for usage.");
|
|
151
208
|
return 1;
|
|
209
|
+
} else {
|
|
210
|
+
console.error(`oke docker: unknown flag ${a}`);
|
|
211
|
+
console.error("Run `oke docker --help` for usage.");
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
{
|
|
216
|
+
const layoutFlags = [
|
|
217
|
+
args.includes("--split"),
|
|
218
|
+
args.includes("--stack"),
|
|
219
|
+
args.includes("--single"),
|
|
220
|
+
].filter(Boolean).length;
|
|
221
|
+
if (layoutFlags > 1) {
|
|
222
|
+
console.error("oke docker: use only one of --single, --split, --stack");
|
|
223
|
+
return 1;
|
|
152
224
|
}
|
|
153
225
|
}
|
|
154
226
|
const { code } = await runDockerDerive({
|
|
155
227
|
prod,
|
|
228
|
+
layout,
|
|
229
|
+
serverCpus,
|
|
230
|
+
serverMemoryGb,
|
|
156
231
|
outDir,
|
|
157
232
|
configPath,
|
|
158
233
|
manifestPath,
|
package/src/cli/doctor.test.ts
CHANGED
|
@@ -84,6 +84,46 @@ describe("oke doctor", () => {
|
|
|
84
84
|
expect(findings).toHaveLength(0);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
test("fails closed in prod when builtin vault master key is env-sourced", async () => {
|
|
88
|
+
const { code, findings } = await runDoctor({
|
|
89
|
+
secrets: [],
|
|
90
|
+
ports: [],
|
|
91
|
+
isPortInUse: async () => false,
|
|
92
|
+
skipDbDrift: true,
|
|
93
|
+
configEnv: "prod",
|
|
94
|
+
driversConfig: {
|
|
95
|
+
vault: { dev: "vault", test: "memory", prod: "vault" },
|
|
96
|
+
},
|
|
97
|
+
vaultConfig: {
|
|
98
|
+
encryption: { masterKey: { kind: "env", name: "OKE_VAULT_MASTER_KEY" } },
|
|
99
|
+
},
|
|
100
|
+
write: () => {},
|
|
101
|
+
});
|
|
102
|
+
expect(code).toBe(2);
|
|
103
|
+
const finding = findings.find((f) => f.code === "vault_master_key");
|
|
104
|
+
expect(finding?.severity).toBe("error");
|
|
105
|
+
expect(finding?.message).toContain("environment variable");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("NODE_ENV=production selects prod env for the vault master-key gate", async () => {
|
|
109
|
+
const { code, findings } = await runDoctor({
|
|
110
|
+
secrets: [],
|
|
111
|
+
ports: [],
|
|
112
|
+
isPortInUse: async () => false,
|
|
113
|
+
skipDbDrift: true,
|
|
114
|
+
env: (k) => (k === "NODE_ENV" ? "production" : undefined),
|
|
115
|
+
driversConfig: {
|
|
116
|
+
vault: { dev: "vault", test: "memory", prod: "vault" },
|
|
117
|
+
},
|
|
118
|
+
vaultConfig: {
|
|
119
|
+
encryption: { masterKey: { kind: "env", name: "OKE_VAULT_MASTER_KEY" } },
|
|
120
|
+
},
|
|
121
|
+
write: () => {},
|
|
122
|
+
});
|
|
123
|
+
expect(code).toBe(2);
|
|
124
|
+
expect(findings.some((f) => f.code === "vault_master_key")).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
87
127
|
test("fails with flow name when PII reaches unguarded ask", async () => {
|
|
88
128
|
const manifest: Manifest = {
|
|
89
129
|
oke: "1.0",
|