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
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for hidden / stdin master-key helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { EventEmitter } from "node:events";
|
|
7
|
+
import { promptHidden, readStdinSecure } from "./vault-secure-input.ts";
|
|
8
|
+
|
|
9
|
+
describe("readStdinSecure", () => {
|
|
10
|
+
test("trims a full stdin payload", async () => {
|
|
11
|
+
const stdin = new EventEmitter() as EventEmitter & {
|
|
12
|
+
setEncoding?: (encoding: BufferEncoding) => void;
|
|
13
|
+
};
|
|
14
|
+
stdin.setEncoding = () => undefined;
|
|
15
|
+
const pending = readStdinSecure(stdin);
|
|
16
|
+
stdin.emit("data", " abc123\n");
|
|
17
|
+
stdin.emit("end");
|
|
18
|
+
expect(await pending).toBe("abc123");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("promptHidden", () => {
|
|
23
|
+
test("masks characters and resolves on Enter", async () => {
|
|
24
|
+
const stdin = new EventEmitter() as EventEmitter & {
|
|
25
|
+
setRawMode?: (mode: boolean) => void;
|
|
26
|
+
off?: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
27
|
+
};
|
|
28
|
+
let raw = false;
|
|
29
|
+
stdin.setRawMode = (mode) => {
|
|
30
|
+
raw = mode;
|
|
31
|
+
};
|
|
32
|
+
stdin.off = (event: string | symbol, listener: (...args: unknown[]) => void) => {
|
|
33
|
+
stdin.removeListener(event, listener);
|
|
34
|
+
return stdin;
|
|
35
|
+
};
|
|
36
|
+
let written = "";
|
|
37
|
+
const pending = promptHidden("key: ", {
|
|
38
|
+
stdin,
|
|
39
|
+
write: (t) => {
|
|
40
|
+
written += t;
|
|
41
|
+
},
|
|
42
|
+
exit: () => {
|
|
43
|
+
throw new Error("exit should not run");
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
// Allow the listener to attach.
|
|
47
|
+
await Promise.resolve();
|
|
48
|
+
stdin.emit("data", "ab");
|
|
49
|
+
stdin.emit("data", "\r");
|
|
50
|
+
expect(await pending).toBe("ab");
|
|
51
|
+
expect(written).toContain("key: ");
|
|
52
|
+
expect(written).toContain("**");
|
|
53
|
+
expect(written).not.toContain("ab");
|
|
54
|
+
expect(raw).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure master-key input for `oke vault` — avoid shell history.
|
|
3
|
+
*
|
|
4
|
+
* Prefer `--key -` (stdin) or an interactive hidden prompt over putting
|
|
5
|
+
* base64 keys on the argv in shared environments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Minimal readable stream used by {@link readStdinSecure}. */
|
|
9
|
+
export interface SecureStdin {
|
|
10
|
+
setEncoding?(encoding: BufferEncoding): void;
|
|
11
|
+
on(event: "data", listener: (chunk: string | Buffer) => void): unknown;
|
|
12
|
+
on(event: "end", listener: () => void): unknown;
|
|
13
|
+
on(event: "error", listener: (error: Error) => void): unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Minimal TTY-ish stdin used by {@link promptHidden}. */
|
|
17
|
+
export interface SecurePromptStdin {
|
|
18
|
+
setRawMode?(mode: boolean): void;
|
|
19
|
+
on(event: "data", listener: (chunk: string | Buffer) => void): unknown;
|
|
20
|
+
removeListener?(event: "data", listener: (chunk: string | Buffer) => void): unknown;
|
|
21
|
+
off?(event: "data", listener: (chunk: string | Buffer) => void): unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** IO sinks for {@link promptHidden}. */
|
|
25
|
+
export interface PromptHiddenOptions {
|
|
26
|
+
/** Prompt destination. Defaults to `process.stdout.write`. */
|
|
27
|
+
readonly write?: (text: string) => void;
|
|
28
|
+
/** Character stream (TTY). Defaults to `process.stdin`. */
|
|
29
|
+
readonly stdin?: SecurePromptStdin;
|
|
30
|
+
/** Exit handler for Ctrl+C. Defaults to `process.exit`. */
|
|
31
|
+
readonly exit?: (code: number) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read a trimmed secret from stdin (for `--key -` / `--new-key -`).
|
|
36
|
+
*
|
|
37
|
+
* @param stdin - Readable stream; defaults to `process.stdin`
|
|
38
|
+
*/
|
|
39
|
+
export async function readStdinSecure(stdin: SecureStdin = process.stdin): Promise<string> {
|
|
40
|
+
stdin.setEncoding?.("utf8");
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
let data = "";
|
|
43
|
+
stdin.on("data", (chunk) => {
|
|
44
|
+
data += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
45
|
+
});
|
|
46
|
+
stdin.on("end", () => resolve(data.trim()));
|
|
47
|
+
stdin.on("error", reject);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prompt for a secret on a TTY without echoing the cleartext.
|
|
53
|
+
*
|
|
54
|
+
* Characters are masked with `*`. Backspace edits; Enter submits; Ctrl+C
|
|
55
|
+
* exits the process (or the injected `exit` handler).
|
|
56
|
+
*
|
|
57
|
+
* @param question - Prompt text written before input
|
|
58
|
+
* @param options - Injectable IO for tests
|
|
59
|
+
*/
|
|
60
|
+
export async function promptHidden(
|
|
61
|
+
question: string,
|
|
62
|
+
options: PromptHiddenOptions = {},
|
|
63
|
+
): Promise<string> {
|
|
64
|
+
const write = options.write ?? ((t: string) => process.stdout.write(t));
|
|
65
|
+
const stdin = options.stdin ?? process.stdin;
|
|
66
|
+
const exit = options.exit ?? ((code: number) => process.exit(code));
|
|
67
|
+
|
|
68
|
+
write(question);
|
|
69
|
+
stdin.setRawMode?.(true);
|
|
70
|
+
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
let input = "";
|
|
73
|
+
const onData = (char: string | Buffer): void => {
|
|
74
|
+
const text = typeof char === "string" ? char : char.toString("utf8");
|
|
75
|
+
for (const ch of text) {
|
|
76
|
+
if (ch === "\n" || ch === "\r" || ch === "\u0004") {
|
|
77
|
+
stdin.setRawMode?.(false);
|
|
78
|
+
write("\n");
|
|
79
|
+
removeDataListener(stdin, onData);
|
|
80
|
+
resolve(input);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (ch === "\u0003") {
|
|
84
|
+
stdin.setRawMode?.(false);
|
|
85
|
+
write("\n");
|
|
86
|
+
removeDataListener(stdin, onData);
|
|
87
|
+
exit(1);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (ch === "\u007f" || ch === "\b") {
|
|
91
|
+
if (input.length > 0) {
|
|
92
|
+
input = input.slice(0, -1);
|
|
93
|
+
write("\b \b");
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// Ignore other control characters.
|
|
98
|
+
if (ch < " ") continue;
|
|
99
|
+
input += ch;
|
|
100
|
+
write("*");
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
stdin.on("data", onData);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Detach the raw-mode data listener when the host supports it.
|
|
109
|
+
*
|
|
110
|
+
* @param stdin - Prompt stdin
|
|
111
|
+
* @param listener - Handler to remove
|
|
112
|
+
*/
|
|
113
|
+
function removeDataListener(
|
|
114
|
+
stdin: SecurePromptStdin,
|
|
115
|
+
listener: (chunk: string | Buffer) => void,
|
|
116
|
+
): void {
|
|
117
|
+
if (typeof stdin.off === "function") {
|
|
118
|
+
stdin.off("data", listener);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (typeof stdin.removeListener === "function") {
|
|
122
|
+
stdin.removeListener("data", listener);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -120,6 +120,9 @@ const WRITE_METHODS = new Set([
|
|
|
120
120
|
"putImage",
|
|
121
121
|
]);
|
|
122
122
|
|
|
123
|
+
/** `fx.vault.*` methods whose first argument is a secret path. */
|
|
124
|
+
const VAULT_PATH_METHODS = new Set(["get", "set", "rotate", "delete"]);
|
|
125
|
+
|
|
123
126
|
/** Methods whose first argument is a table / collection identifier. */
|
|
124
127
|
const TABLE_ARG_METHODS = new Set([
|
|
125
128
|
"insert",
|
|
@@ -189,6 +192,10 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
|
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
if (chain.rootMethod === "vault" && call === chain.rootCall) {
|
|
195
|
+
// `fx.vault.list()` / `fx.vault.status()` take no path — nothing to
|
|
196
|
+
// declare. Every other method's first argument is the secret.
|
|
197
|
+
const method = chain.methods[1] ?? "get";
|
|
198
|
+
if (!VAULT_PATH_METHODS.has(method)) continue;
|
|
192
199
|
const ref = resolveNamed(call.arguments[0], options.bindings, "secret");
|
|
193
200
|
if (ref) secrets.add(ref);
|
|
194
201
|
continue;
|
|
@@ -2,15 +2,15 @@ import { defineConfig } from "okengine/config";
|
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
drivers: {
|
|
5
|
-
prod: ["postgres", "redis", "s3", "smtp", "
|
|
5
|
+
prod: ["postgres", "redis", "s3", "smtp", "vault", "anthropic", "pgvector"],
|
|
6
6
|
store: {
|
|
7
|
-
sql: {
|
|
7
|
+
sql: { dev: { driver: "postgres" }, test: "pglite", prod: { driver: "postgres" } },
|
|
8
8
|
kv: { prod: "redis" },
|
|
9
9
|
files: { prod: "s3" },
|
|
10
10
|
index: { prod: "pgvector" },
|
|
11
11
|
},
|
|
12
12
|
signal: { prod: "redis" },
|
|
13
|
-
vault: { prod: "
|
|
13
|
+
vault: { prod: "vault" },
|
|
14
14
|
channel: { email: { prod: "smtp" } },
|
|
15
15
|
ai: { prod: { driver: "anthropic" } },
|
|
16
16
|
},
|
|
@@ -7,7 +7,7 @@ export const chargeBooking = flow("payments.chargeBooking", {
|
|
|
7
7
|
// Journal only serializable step values — a Redacted is not journaled.
|
|
8
8
|
const intent = await fx.step("create-intent", async () => ({
|
|
9
9
|
id: `pi_${orderId}`,
|
|
10
|
-
key: fx.vault(stripeKey).reveal(),
|
|
10
|
+
key: (await fx.vault.get(stripeKey)).reveal(),
|
|
11
11
|
}));
|
|
12
12
|
await fx.clock.sleep("verify-window", "2m");
|
|
13
13
|
return fx.step("confirm", () => intent);
|
|
@@ -1,82 +1,146 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `defineConfig` fills missing `
|
|
2
|
+
* `defineConfig` fills missing `dev` pins from `prod` and normalizes legacy keys.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import { defineConfig,
|
|
6
|
+
import { defineConfig, fillDevFromProd, normalizeEnvDriverMap } from "./index.ts";
|
|
7
7
|
|
|
8
|
-
describe("
|
|
9
|
-
test("copies prod onto
|
|
10
|
-
const filled =
|
|
11
|
-
|
|
8
|
+
describe("fillDevFromProd / defineConfig", () => {
|
|
9
|
+
test("copies prod onto dev when dev is omitted", () => {
|
|
10
|
+
const filled = fillDevFromProd({
|
|
11
|
+
test: "pglite",
|
|
12
12
|
prod: "postgres",
|
|
13
13
|
});
|
|
14
|
-
expect(filled?.
|
|
15
|
-
expect(filled?.
|
|
14
|
+
expect(filled?.dev).toBe("postgres");
|
|
15
|
+
expect(filled?.test).toBe("pglite");
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
test("does not overwrite an explicit
|
|
18
|
+
test("does not overwrite an explicit dev pin", () => {
|
|
19
19
|
expect(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
docker: "memory",
|
|
20
|
+
fillDevFromProd({
|
|
21
|
+
dev: "memory",
|
|
23
22
|
prod: "postgres",
|
|
24
|
-
})?.
|
|
23
|
+
})?.dev,
|
|
25
24
|
).toBe("memory");
|
|
26
25
|
});
|
|
27
26
|
|
|
28
|
-
test("vault prod →
|
|
29
|
-
expect(
|
|
27
|
+
test("vault prod → dev copies the prod driver", () => {
|
|
28
|
+
expect(fillDevFromProd({ prod: "vault" })?.dev).toBe("vault");
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
test("defineConfig fills every element from prod", () => {
|
|
33
32
|
const cfg = defineConfig({
|
|
34
33
|
drivers: {
|
|
35
34
|
store: {
|
|
36
|
-
sql: {
|
|
37
|
-
kv: {
|
|
38
|
-
files: {
|
|
35
|
+
sql: { test: "pglite", prod: "postgres" },
|
|
36
|
+
kv: { test: "memory", prod: "redis" },
|
|
37
|
+
files: { test: "memory", prod: "s3" },
|
|
39
38
|
},
|
|
40
|
-
signal: {
|
|
41
|
-
clock: {
|
|
42
|
-
vault: {
|
|
39
|
+
signal: { test: "memory", prod: "redis" },
|
|
40
|
+
clock: { test: "frozen", prod: "file" },
|
|
41
|
+
vault: { test: "memory", prod: "vault" },
|
|
43
42
|
channel: {
|
|
44
|
-
email: {
|
|
43
|
+
email: { test: "console", prod: "smtp" },
|
|
45
44
|
},
|
|
46
|
-
ai: {
|
|
45
|
+
ai: { test: "mock", prod: "anthropic" },
|
|
47
46
|
},
|
|
48
47
|
});
|
|
49
|
-
expect(cfg.drivers?.store?.sql
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
expect(cfg.drivers?.
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
expect(cfg.drivers?.store?.sql).toMatchObject({
|
|
49
|
+
dev: "postgres",
|
|
50
|
+
test: "pglite",
|
|
51
|
+
prod: "postgres",
|
|
52
|
+
});
|
|
53
|
+
expect(cfg.drivers?.store?.kv).toMatchObject({
|
|
54
|
+
dev: "redis",
|
|
55
|
+
test: "memory",
|
|
56
|
+
prod: "redis",
|
|
57
|
+
});
|
|
58
|
+
expect(cfg.drivers?.store?.files).toMatchObject({
|
|
59
|
+
dev: "s3",
|
|
60
|
+
test: "memory",
|
|
61
|
+
prod: "s3",
|
|
62
|
+
});
|
|
63
|
+
expect(cfg.drivers?.signal).toMatchObject({
|
|
64
|
+
dev: "redis",
|
|
65
|
+
test: "memory",
|
|
66
|
+
prod: "redis",
|
|
67
|
+
});
|
|
68
|
+
expect(cfg.drivers?.clock).toMatchObject({
|
|
69
|
+
dev: "file",
|
|
70
|
+
test: "frozen",
|
|
71
|
+
prod: "file",
|
|
72
|
+
});
|
|
73
|
+
expect(cfg.drivers?.vault).toMatchObject({
|
|
74
|
+
dev: "vault",
|
|
75
|
+
test: "memory",
|
|
76
|
+
prod: "vault",
|
|
77
|
+
});
|
|
78
|
+
expect(cfg.drivers?.channel?.email).toMatchObject({
|
|
79
|
+
dev: "smtp",
|
|
80
|
+
test: "console",
|
|
81
|
+
prod: "smtp",
|
|
82
|
+
});
|
|
83
|
+
expect(cfg.drivers?.ai).toMatchObject({
|
|
84
|
+
dev: "anthropic",
|
|
85
|
+
test: "mock",
|
|
86
|
+
prod: "anthropic",
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("normalizeEnvDriverMap expands a bare string to all envs", () => {
|
|
91
|
+
expect(normalizeEnvDriverMap("redis")).toEqual({
|
|
92
|
+
dev: "redis",
|
|
93
|
+
test: "redis",
|
|
94
|
+
prod: "redis",
|
|
95
|
+
});
|
|
57
96
|
});
|
|
58
97
|
|
|
59
|
-
test("normalizeEnvDriverMap
|
|
98
|
+
test("normalizeEnvDriverMap keeps only dev/test/prod keys", () => {
|
|
60
99
|
const normalized = normalizeEnvDriverMap({
|
|
61
|
-
dev: "
|
|
62
|
-
|
|
100
|
+
dev: "postgres",
|
|
101
|
+
test: "pglite",
|
|
102
|
+
prod: "postgres",
|
|
103
|
+
});
|
|
104
|
+
expect(normalized).toEqual({
|
|
105
|
+
dev: "postgres",
|
|
106
|
+
test: "pglite",
|
|
63
107
|
prod: "postgres",
|
|
64
108
|
});
|
|
65
|
-
expect(normalized?.local).toBe("sqlite");
|
|
66
|
-
expect(normalized?.docker).toBe("postgres");
|
|
67
|
-
expect((normalized as { dev?: string } | undefined)?.dev).toBeUndefined();
|
|
68
|
-
expect((normalized as { stack?: string } | undefined)?.stack).toBeUndefined();
|
|
69
109
|
});
|
|
70
110
|
|
|
71
|
-
test("defineConfig
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
111
|
+
test("defineConfig rejects sqlite", () => {
|
|
112
|
+
expect(() =>
|
|
113
|
+
defineConfig({
|
|
114
|
+
drivers: {
|
|
115
|
+
store: {
|
|
116
|
+
sql: { dev: "sqlite", test: "pglite", prod: "postgres" },
|
|
117
|
+
},
|
|
76
118
|
},
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
119
|
+
}),
|
|
120
|
+
).toThrow(/sqlite/);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("defineConfig rejects libsql", () => {
|
|
124
|
+
expect(() =>
|
|
125
|
+
defineConfig({
|
|
126
|
+
drivers: {
|
|
127
|
+
store: {
|
|
128
|
+
sql: { dev: "libsql", test: "pglite", prod: "postgres" },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
).toThrow(/libsql/);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("defineConfig rejects non-pglite test SQL", () => {
|
|
136
|
+
expect(() =>
|
|
137
|
+
defineConfig({
|
|
138
|
+
drivers: {
|
|
139
|
+
store: {
|
|
140
|
+
sql: { dev: "postgres", test: "memory", prod: "postgres" },
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
).toThrow(/pglite/);
|
|
81
145
|
});
|
|
82
146
|
});
|
|
@@ -20,11 +20,10 @@ import {
|
|
|
20
20
|
|
|
21
21
|
describe("mergeEnvDriverMap", () => {
|
|
22
22
|
test("an unset key keeps the real default, not a sibling key's value", () => {
|
|
23
|
-
const merged = mergeEnvDriverMap({
|
|
23
|
+
const merged = mergeEnvDriverMap({ test: "pglite" }, STORE_SQL_DEFAULTS);
|
|
24
24
|
expect(merged).toEqual({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
test: "memory",
|
|
25
|
+
dev: "postgres",
|
|
26
|
+
test: "pglite",
|
|
28
27
|
prod: "postgres",
|
|
29
28
|
});
|
|
30
29
|
});
|
|
@@ -34,28 +33,27 @@ describe("mergeEnvDriverMap", () => {
|
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
test("a fully-specified override wins on every key", () => {
|
|
37
|
-
const override = {
|
|
36
|
+
const override = { dev: "a", test: "b", prod: "c" };
|
|
38
37
|
expect(mergeEnvDriverMap(override, STORE_SQL_DEFAULTS)).toEqual(override);
|
|
39
38
|
});
|
|
40
39
|
});
|
|
41
40
|
|
|
42
|
-
describe("resolveEffectiveDrivers
|
|
43
|
-
test("`{ store: { sql: {
|
|
41
|
+
describe("resolveEffectiveDrivers", () => {
|
|
42
|
+
test("`{ store: { sql: { test: 'pglite' } } }` keeps postgres on dev/prod", () => {
|
|
44
43
|
const effective = resolveEffectiveDrivers({
|
|
45
|
-
store: { sql: {
|
|
44
|
+
store: { sql: { test: "pglite" } },
|
|
46
45
|
});
|
|
47
46
|
|
|
48
47
|
expect(effective.store.sql).toEqual({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
test: "memory",
|
|
48
|
+
dev: "postgres",
|
|
49
|
+
test: "pglite",
|
|
52
50
|
prod: "postgres",
|
|
53
51
|
});
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
test("every other driver stays at its full, real, untouched default map", () => {
|
|
57
55
|
const effective = resolveEffectiveDrivers({
|
|
58
|
-
store: { sql: {
|
|
56
|
+
store: { sql: { test: "pglite" } },
|
|
59
57
|
});
|
|
60
58
|
|
|
61
59
|
expect(effective.store.kv).toEqual(STORE_KV_DEFAULTS);
|
|
@@ -70,34 +68,29 @@ describe("resolveEffectiveDrivers — the drivers.store.sql.local discussion sce
|
|
|
70
68
|
|
|
71
69
|
test("overriding one driver's map has zero effect on a sibling driver's defaults", () => {
|
|
72
70
|
const effective = resolveEffectiveDrivers({
|
|
73
|
-
store: { kv: {
|
|
71
|
+
store: { kv: { dev: "redis" } },
|
|
74
72
|
});
|
|
75
73
|
|
|
76
|
-
// The overridden driver (kv) reflects the override on its one set key.
|
|
77
74
|
expect(effective.store.kv).toEqual({
|
|
78
|
-
|
|
79
|
-
docker: "redis",
|
|
75
|
+
dev: "redis",
|
|
80
76
|
test: "memory",
|
|
81
77
|
prod: "redis",
|
|
82
78
|
});
|
|
83
|
-
// sql / files (same element, different facet) stay fully at real defaults.
|
|
84
79
|
expect(effective.store.sql).toEqual(STORE_SQL_DEFAULTS);
|
|
85
80
|
expect(effective.store.files).toEqual(STORE_FILES_DEFAULTS);
|
|
86
|
-
// Unrelated elements stay fully at real defaults too.
|
|
87
81
|
expect(effective.signal).toEqual(SIGNAL_DEFAULTS);
|
|
88
82
|
expect(effective.vault).toEqual(VAULT_DEFAULTS);
|
|
89
83
|
});
|
|
90
84
|
|
|
91
|
-
test("overriding one env key leaves the other
|
|
85
|
+
test("overriding one env key leaves the other two at real defaults for that driver", () => {
|
|
92
86
|
const effective = resolveEffectiveDrivers({
|
|
93
|
-
vault: {
|
|
87
|
+
vault: { dev: "keychain" },
|
|
94
88
|
});
|
|
95
89
|
|
|
96
90
|
expect(effective.vault).toEqual({
|
|
97
|
-
|
|
98
|
-
docker: "env",
|
|
91
|
+
dev: "keychain",
|
|
99
92
|
test: "memory",
|
|
100
|
-
prod: "
|
|
93
|
+
prod: "vault",
|
|
101
94
|
});
|
|
102
95
|
});
|
|
103
96
|
|