okengine 0.6.1 → 0.8.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.
Files changed (167) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +73 -181
  3. package/package.json +3 -3
  4. package/site/content/docs/console/clock.mdx +25 -5
  5. package/site/content/docs/console/signals.mdx +13 -4
  6. package/site/content/docs/elements/ai.mdx +29 -2
  7. package/site/content/docs/elements/channel.mdx +42 -25
  8. package/site/content/docs/elements/clock.mdx +59 -25
  9. package/site/content/docs/elements/flow.mdx +8 -2
  10. package/site/content/docs/elements/gate.mdx +8 -1
  11. package/site/content/docs/elements/signal.mdx +173 -29
  12. package/site/content/docs/elements/store.mdx +595 -73
  13. package/site/content/docs/elements/vault.mdx +15 -5
  14. package/site/content/docs/get-started/basic-usage.mdx +13 -6
  15. package/site/content/docs/get-started/installation.mdx +52 -14
  16. package/site/content/docs/get-started/introduction.mdx +1 -1
  17. package/site/content/docs/plugins/magic-link.mdx +42 -0
  18. package/site/content/docs/plugins/phone-number.mdx +78 -17
  19. package/site/content/docs/plugins/two-factor.mdx +1 -0
  20. package/site/content/docs/reference/cli.md +32 -2
  21. package/site/content/docs/reference/configuration.mdx +15 -13
  22. package/site/content/docs/reference/environment-variables.mdx +32 -18
  23. package/site/content/docs/reference/errors.mdx +12 -11
  24. package/site/content/docs/reference/fx.mdx +15 -13
  25. package/site/content/docs/reference/security.md +3 -1
  26. package/src/cli/ai-setup/ai-setup.test.ts +144 -0
  27. package/src/cli/ai-setup/apply.ts +214 -0
  28. package/src/cli/ai-setup/catalog.ts +263 -0
  29. package/src/cli/ai-setup/detect-ollama.ts +166 -0
  30. package/src/cli/ai-setup/index.ts +228 -0
  31. package/src/cli/ai-setup/prompts.ts +649 -0
  32. package/src/cli/ai-setup/recommend.test.ts +100 -0
  33. package/src/cli/ai-setup/recommend.ts +203 -0
  34. package/src/cli/ai.ts +38 -0
  35. package/src/cli/db-auto-push.test.ts +6 -1
  36. package/src/cli/db-auto-push.ts +8 -3
  37. package/src/cli/db-seed.ts +359 -0
  38. package/src/cli/db.test.ts +341 -3
  39. package/src/cli/db.ts +75 -8
  40. package/src/cli/dev-db-push.test.ts +59 -0
  41. package/src/cli/dev.test.ts +40 -0
  42. package/src/cli/dev.ts +55 -1
  43. package/src/cli/docker-clean.test.ts +179 -0
  44. package/src/cli/docker-clean.ts +406 -0
  45. package/src/cli/docker.ts +16 -4
  46. package/src/cli/ensure-drizzle-config.ts +1 -1
  47. package/src/cli/hero-meta.test.ts +6 -5
  48. package/src/cli/hero-meta.ts +7 -1
  49. package/src/cli/index.ts +5 -0
  50. package/src/cli/load-config.images.test.ts +22 -0
  51. package/src/cli/load-config.ts +7 -2
  52. package/src/cli/openbao-bootstrap.test.ts +1 -0
  53. package/src/cli/openbao-bootstrap.ts +9 -2
  54. package/src/cli/registry.ts +116 -2
  55. package/src/client/live-gap.test.ts +35 -0
  56. package/src/client/transport.test.ts +21 -0
  57. package/src/client/transport.ts +27 -5
  58. package/src/compiler/effects-infer.ts +1 -0
  59. package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
  60. package/src/config/define-config.test.ts +6 -6
  61. package/src/config/index.ts +5 -1
  62. package/src/console/server/console.test.ts +45 -0
  63. package/src/console/server/flows.ts +39 -9
  64. package/src/console/server/i18n.ts +41 -0
  65. package/src/console/server/vault.ts +12 -38
  66. package/src/console/ui/dist/assets/index-ClX0ZCe5.js +10 -0
  67. package/src/console/ui/dist/assets/{panel-access-BGv45snf.js → panel-access-ri1X9Otj.js} +1 -1
  68. package/src/console/ui/dist/assets/{panel-ai-B2S7LEii.js → panel-ai-DLHjRoJk.js} +1 -1
  69. package/src/console/ui/dist/assets/{panel-architecture-D7UJh91v.js → panel-architecture-DI3bQF3J.js} +1 -1
  70. package/src/console/ui/dist/assets/{panel-channels-9T3ybqRu.js → panel-channels-BDYWX29p.js} +1 -1
  71. package/src/console/ui/dist/assets/{panel-clock-Cb1UXGRQ.js → panel-clock-i-JpTjVB.js} +1 -1
  72. package/src/console/ui/dist/assets/{panel-diff-DmYbKWmN.js → panel-diff-CFuWxiXy.js} +1 -1
  73. package/src/console/ui/dist/assets/{panel-flows-PiHwT55z.js → panel-flows-B8TRhcoe.js} +1 -1
  74. package/src/console/ui/dist/assets/{panel-gates-BQGYXvjT.js → panel-gates-CfkBgco7.js} +1 -1
  75. package/src/console/ui/dist/assets/{panel-overview-BBnRO18l.js → panel-overview-DONDzBd2.js} +1 -1
  76. package/src/console/ui/dist/assets/{panel-plugins-D0PsmVw2.js → panel-plugins-DDJwTehL.js} +1 -1
  77. package/src/console/ui/dist/assets/{panel-runs-CWuRDe0r.js → panel-runs-D5zf-D9c.js} +1 -1
  78. package/src/console/ui/dist/assets/{panel-signals-Bbg4ewpP.js → panel-signals-Dsxdu_AR.js} +1 -1
  79. package/src/console/ui/dist/assets/{panel-store-CPCbsDRa.js → panel-store-hy7O8HOs.js} +1 -1
  80. package/src/console/ui/dist/assets/{panel-traces-DVAzuA_S.js → panel-traces-CBiAe5go.js} +1 -1
  81. package/src/console/ui/dist/assets/{panel-vault-D1_MvOmo.js → panel-vault-Za1GyJfM.js} +1 -1
  82. package/src/console/ui/dist/index.html +1 -1
  83. package/src/console/ui/shell/components/ui.tsx +5 -1
  84. package/src/console/ui/shell/setup/Wizard.tsx +26 -5
  85. package/src/docker/cleanup.test.ts +193 -0
  86. package/src/docker/cleanup.ts +355 -0
  87. package/src/docker/docker.test.ts +4 -0
  88. package/src/docker/index.ts +19 -0
  89. package/src/docker/recipes/ollama.ts +10 -5
  90. package/src/drivers/ai-ollama.ts +45 -2
  91. package/src/drivers/ai-providers.test.ts +31 -0
  92. package/src/drivers/channel-sently.test.ts +8 -0
  93. package/src/drivers/channel-taqnyat-mail.ts +34 -0
  94. package/src/drivers/channel-types.ts +71 -0
  95. package/src/drivers/clock-postgres.test.ts +258 -0
  96. package/src/drivers/clock-postgres.ts +410 -0
  97. package/src/drivers/index.ts +21 -0
  98. package/src/drivers/journal-postgres.test.ts +175 -0
  99. package/src/drivers/journal-postgres.ts +492 -0
  100. package/src/drivers/signal-engine.ts +98 -25
  101. package/src/drivers/signal-nats.ts +4 -4
  102. package/src/drivers/signal-postgres.ts +158 -64
  103. package/src/drivers/signal-redis.ts +4 -4
  104. package/src/drivers/signal-types.ts +61 -2
  105. package/src/drivers/vault-driver-removal.test.ts +5 -1
  106. package/src/elements/channel/runtime.ts +51 -0
  107. package/src/elements/channel.test.ts +71 -0
  108. package/src/elements/clock/chaos-child.ts +389 -0
  109. package/src/elements/clock/chaos.test.ts +204 -0
  110. package/src/elements/clock/durable.ts +7 -0
  111. package/src/elements/clock/reconcile.ts +110 -0
  112. package/src/elements/clock/runtime.ts +13 -0
  113. package/src/elements/clock.test.ts +164 -2
  114. package/src/elements/clock.ts +2 -1
  115. package/src/elements/gate/runtime.ts +6 -0
  116. package/src/elements/signal/chaos-child.ts +41 -4
  117. package/src/elements/signal/declare.ts +4 -1
  118. package/src/elements/signal/delivery-modes.test.ts +179 -0
  119. package/src/elements/signal/key-ordering.test.ts +306 -0
  120. package/src/elements/signal/lease-reclaim.test.ts +123 -0
  121. package/src/elements/signal/optional-emit.test.ts +76 -0
  122. package/src/elements/signal/order-lifecycle.test.ts +130 -0
  123. package/src/elements/signal/orphan-messages.test.ts +152 -0
  124. package/src/elements/signal/runtime.ts +11 -4
  125. package/src/elements/signal/schema-emit.test.ts +107 -0
  126. package/src/elements/signal.test.ts +89 -5
  127. package/src/elements/store/files-fx.ts +104 -0
  128. package/src/elements/store/files-image.test.ts +251 -0
  129. package/src/elements/store/files-image.ts +494 -0
  130. package/src/elements/store/runtime.ts +60 -0
  131. package/src/elements/store/seed.test.ts +27 -0
  132. package/src/elements/store/seed.ts +68 -0
  133. package/src/elements/store/sql-session.test.ts +39 -0
  134. package/src/elements/store/sql-session.ts +55 -0
  135. package/src/elements/store/upsert-app.test.ts +103 -0
  136. package/src/elements/store.ts +29 -0
  137. package/src/elements/vault/boot-chain.ts +150 -0
  138. package/src/elements/vault/runtime.ts +8 -0
  139. package/src/index.ts +15 -0
  140. package/src/kernel/app.ts +168 -15
  141. package/src/kernel/boot-bind/channel.test.ts +16 -0
  142. package/src/kernel/boot-bind/channel.ts +13 -0
  143. package/src/kernel/boot-bind/clock.ts +63 -7
  144. package/src/kernel/boot-bind/gate.ts +71 -3
  145. package/src/kernel/boot-bind/honor-config.test.ts +348 -0
  146. package/src/kernel/boot-bind/journal.ts +89 -0
  147. package/src/kernel/boot-bind/signal.ts +64 -7
  148. package/src/kernel/boot-bind/vault.ts +27 -8
  149. package/src/kernel/boot.test.ts +6 -4
  150. package/src/kernel/boot.ts +63 -15
  151. package/src/kernel/concurrency.ts +1 -1
  152. package/src/kernel/errors.ts +8 -2
  153. package/src/kernel/fx.test.ts +19 -0
  154. package/src/kernel/fx.ts +153 -9
  155. package/src/kernel/index.ts +6 -0
  156. package/src/kernel/journal-boot.test.ts +397 -0
  157. package/src/kernel/journal-suspend.ts +35 -0
  158. package/src/kernel/journal.test.ts +142 -0
  159. package/src/kernel/journal.ts +202 -27
  160. package/src/plugins/auth-methods.security.test.ts +10 -7
  161. package/src/plugins/phone-number.ts +67 -10
  162. package/src/plugins/taqnyat.live.test.ts +174 -0
  163. package/src/runtime/dev-request-log.test.ts +20 -1
  164. package/src/runtime/dev-request-log.ts +38 -0
  165. package/src/term.test.ts +15 -0
  166. package/src/term.ts +8 -3
  167. package/src/console/ui/dist/assets/index-CjxwRGVv.js +0 -10
@@ -41,6 +41,7 @@ export type {
41
41
  SignalDriver,
42
42
  SignalBus,
43
43
  SignalMessage,
44
+ SignalEmitOptions,
44
45
  SignalFailureReason,
45
46
  DeadLetter,
46
47
  SignalTransaction,
@@ -55,6 +56,8 @@ export type {
55
56
  SignalDiscardOptions,
56
57
  } from "./signal-types.ts";
57
58
 
59
+ export { SIGNAL_DEFAULT_LEASE_MS } from "./signal-types.ts";
60
+
58
61
  export {
59
62
  memorySqlDriver,
60
63
  memoryKvDriver,
@@ -103,6 +106,23 @@ export {
103
106
  type PostgresSignalSql,
104
107
  } from "./signal-postgres.ts";
105
108
 
109
+ export {
110
+ createPostgresCronStore,
111
+ createPostgresCronFake,
112
+ type PostgresCronSql,
113
+ type CreatePostgresCronStoreOptions,
114
+ type BunCronClient,
115
+ } from "./clock-postgres.ts";
116
+
117
+ export {
118
+ createPostgresJournalStore,
119
+ createPostgresJournalFake,
120
+ type PostgresJournalSql,
121
+ type PostgresJournalStore,
122
+ type CreatePostgresJournalStoreOptions,
123
+ type BunJournalClient,
124
+ } from "./journal-postgres.ts";
125
+
106
126
  export {
107
127
  redisSignalDriver,
108
128
  openRedisSignal,
@@ -141,6 +161,7 @@ export { smtpChannelDriver, openSmtpChannel } from "./channel-smtp.ts";
141
161
  export { resendChannelDriver, openResendChannel } from "./channel-resend.ts";
142
162
  export { sndrChannelDriver, openSndrChannel } from "./channel-sndr.ts";
143
163
  export { taqnyatChannelDriver, openTaqnyatChannel } from "./channel-taqnyat.ts";
164
+ export { taqnyatMailChannelDriver, openTaqnyatMailChannel } from "./channel-taqnyat-mail.ts";
144
165
  export { msegatChannelDriver, openMsegatChannel } from "./channel-msegat.ts";
145
166
  export { unifonicChannelDriver, openUnifonicChannel } from "./channel-unifonic.ts";
146
167
  export { waCloudChannelDriver, openWaCloudChannel } from "./channel-wa-cloud.ts";
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Postgres JournalStore — unit (fake). Live multi-process boot chaos lives in
3
+ * `src/elements/clock/journal-boot.test.ts` (same LIVE_URL gate as clock).
4
+ */
5
+
6
+ import { describe, expect, test } from "bun:test";
7
+
8
+ import type { JournalRun } from "../kernel/journal.ts";
9
+ import { createPostgresJournalFake, createPostgresJournalStore } from "./journal-postgres.ts";
10
+
11
+ function seedRun(patch: Partial<JournalRun> & { id: string }): JournalRun {
12
+ return {
13
+ flow: "charge",
14
+ input: undefined,
15
+ status: "running",
16
+ entries: [],
17
+ createdAt: 1,
18
+ updatedAt: 1,
19
+ ...patch,
20
+ };
21
+ }
22
+
23
+ describe("postgres JournalStore (fake)", () => {
24
+ test("put / get / list round-trip incl. lease + sleep fields", async () => {
25
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
26
+ await store.put(
27
+ seedRun({
28
+ id: "r1",
29
+ input: { orderId: "o1" },
30
+ entries: [
31
+ { kind: "step", name: "create-intent", value: { id: "pi_1" }, at: 2 },
32
+ { kind: "sleep", label: "verify", duration: "7d", wakeAt: 1000, at: 3 },
33
+ ],
34
+ status: "sleeping",
35
+ wakeAt: 1000,
36
+ lockedBy: "inst-a",
37
+ leaseExpiresAt: 500,
38
+ output: { ok: true },
39
+ }),
40
+ );
41
+ const row = await store.get("r1");
42
+ expect(row?.flow).toBe("charge");
43
+ expect(row?.input).toEqual({ orderId: "o1" });
44
+ expect(row?.entries).toHaveLength(2);
45
+ expect(row?.status).toBe("sleeping");
46
+ expect(row?.wakeAt).toBe(1000);
47
+ expect(row?.lockedBy).toBe("inst-a");
48
+ expect(row?.leaseExpiresAt).toBe(500);
49
+ expect(row?.output).toEqual({ ok: true });
50
+ expect(await store.list()).toHaveLength(1);
51
+ await store.close();
52
+ });
53
+
54
+ test("acquireLease: exactly one of two racing claimants wins", async () => {
55
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
56
+ await store.put(seedRun({ id: "race" }));
57
+
58
+ const a = store.acquireLease("race", "a", 1_000, 500);
59
+ const b = store.acquireLease("race", "b", 1_000, 500);
60
+ const [wa, wb] = await Promise.all([a, b]);
61
+ expect([wa, wb].filter(Boolean)).toHaveLength(1);
62
+
63
+ const row = await store.get("race");
64
+ expect(row?.lockedBy).toBe(wa ? "a" : "b");
65
+ expect(row?.leaseExpiresAt).toBe(1_500);
66
+ await store.close();
67
+ });
68
+
69
+ test("acquireLease: expired lease is reclaimed lazily (no sweeper)", async () => {
70
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
71
+ await store.put(seedRun({ id: "reclaim", lockedBy: "dead", leaseExpiresAt: 100 }));
72
+
73
+ expect(await store.acquireLease("reclaim", "survivor", 100, 50)).toBe(true);
74
+ const row = await store.get("reclaim");
75
+ expect(row?.lockedBy).toBe("survivor");
76
+ expect(row?.leaseExpiresAt).toBe(150);
77
+ await store.close();
78
+ });
79
+
80
+ test("acquireLease: live lease blocks other instance; same holder renews", async () => {
81
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
82
+ await store.put(seedRun({ id: "held" }));
83
+ expect(await store.acquireLease("held", "leader", 0, 1_000)).toBe(true);
84
+ expect(await store.acquireLease("held", "other", 100, 1_000)).toBe(false);
85
+ expect(await store.acquireLease("held", "leader", 100, 1_000)).toBe(true);
86
+ await store.close();
87
+ });
88
+
89
+ test("claimDueSleep: claims due sleep, skips future + live-leased", async () => {
90
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
91
+ await store.put(seedRun({ id: "future", status: "sleeping", wakeAt: 10_000 }));
92
+ await store.put(
93
+ seedRun({
94
+ id: "leased",
95
+ status: "sleeping",
96
+ wakeAt: 100,
97
+ lockedBy: "other",
98
+ leaseExpiresAt: 9_000,
99
+ }),
100
+ );
101
+ await store.put(seedRun({ id: "due", status: "sleeping", wakeAt: 100 }));
102
+ await store.put(seedRun({ id: "done", status: "completed" }));
103
+
104
+ const claimed = await store.claimDueSleep("me", 1_000, 500);
105
+ expect(claimed?.id).toBe("due");
106
+ const row = await store.get("due");
107
+ expect(row?.lockedBy).toBe("me");
108
+ expect(row?.leaseExpiresAt).toBe(1_500);
109
+
110
+ // Nothing else claimable.
111
+ expect(await store.claimDueSleep("me", 1_000, 500)).toBeUndefined();
112
+ await store.close();
113
+ });
114
+
115
+ test("claimDueSleep: two racing claims hand the run to exactly one instance", async () => {
116
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
117
+ await store.put(seedRun({ id: "due", status: "sleeping", wakeAt: 100 }));
118
+
119
+ const [a, b] = await Promise.all([
120
+ store.claimDueSleep("a", 1_000, 500),
121
+ store.claimDueSleep("b", 1_000, 500),
122
+ ]);
123
+ const winners = [a, b].filter((r) => r?.id === "due");
124
+ expect(winners).toHaveLength(1);
125
+ await store.close();
126
+ });
127
+
128
+ test("claimDueSleep: expired lease on a sleeping run is reclaimable", async () => {
129
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
130
+ await store.put(
131
+ seedRun({
132
+ id: "stale",
133
+ status: "sleeping",
134
+ wakeAt: 100,
135
+ lockedBy: "dead",
136
+ leaseExpiresAt: 50,
137
+ }),
138
+ );
139
+ const claimed = await store.claimDueSleep("survivor", 1_000, 500);
140
+ expect(claimed?.id).toBe("stale");
141
+ expect((await store.get("stale"))?.lockedBy).toBe("survivor");
142
+ await store.close();
143
+ });
144
+
145
+ test("listOrphans: running/sleeping without a live lease; never completed", async () => {
146
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
147
+ await store.put(seedRun({ id: "running-unlocked" }));
148
+ await store.put(seedRun({ id: "running-expired", lockedBy: "dead", leaseExpiresAt: 10 }));
149
+ await store.put(seedRun({ id: "running-live", lockedBy: "alive", leaseExpiresAt: 9_000 }));
150
+ await store.put(seedRun({ id: "sleeping-future", status: "sleeping", wakeAt: 99_000 }));
151
+ await store.put(seedRun({ id: "done", status: "completed" }));
152
+
153
+ const orphans = (await store.listOrphans(1_000)).map((r) => r.id);
154
+ expect(orphans).toContain("running-unlocked");
155
+ expect(orphans).toContain("running-expired");
156
+ expect(orphans).toContain("sleeping-future");
157
+ expect(orphans).not.toContain("running-live");
158
+ expect(orphans).not.toContain("done");
159
+ await store.close();
160
+ });
161
+
162
+ test("releaseLease: holder releases; other holder cannot", async () => {
163
+ const store = await createPostgresJournalStore({ sql: createPostgresJournalFake() });
164
+ await store.put(seedRun({ id: "r", lockedBy: "a", leaseExpiresAt: 500 }));
165
+
166
+ await store.releaseLease("r", "b");
167
+ expect((await store.get("r"))?.lockedBy).toBe("a");
168
+
169
+ await store.releaseLease("r", "a");
170
+ const row = await store.get("r");
171
+ expect(row?.lockedBy).toBeUndefined();
172
+ expect(row?.leaseExpiresAt).toBeUndefined();
173
+ await store.close();
174
+ });
175
+ });