@velum-labs/routekit-daemon 1.3.2 → 1.5.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 (172) hide show
  1. package/dist/account-enroll-service.d.ts +1 -3
  2. package/dist/account-enroll-service.js +262 -262
  3. package/dist/account-mutation-service.d.ts +1 -3
  4. package/dist/account-mutation-service.js +302 -283
  5. package/dist/account-query-service.d.ts +1 -3
  6. package/dist/account-query-service.js +88 -86
  7. package/dist/call-attribution-store.js +544 -1
  8. package/dist/control-dispatch.js +9 -15
  9. package/dist/daemon-bootstrap-preflight.js +1 -1
  10. package/dist/daemon-control-handlers.d.ts +1 -1
  11. package/dist/daemon-control-handlers.js +24 -27
  12. package/dist/daemon-generations.d.ts +14 -28
  13. package/dist/daemon-generations.js +114 -53
  14. package/dist/daemon-lifecycle-service.d.ts +1 -3
  15. package/dist/daemon-lifecycle-service.js +88 -94
  16. package/dist/daemon-maintenance.d.ts +27 -49
  17. package/dist/daemon-maintenance.js +5 -1
  18. package/dist/daemon-options.d.ts +3 -2
  19. package/dist/daemon-policy-context.d.ts +1 -0
  20. package/dist/daemon-policy-context.js +1 -0
  21. package/dist/daemon-runtime-state.d.ts +1 -1
  22. package/dist/daemon-runtime-state.js +2 -3
  23. package/dist/daemon-state-context.d.ts +1 -1
  24. package/dist/daemon-state-context.js +1 -38
  25. package/dist/doctor-service.d.ts +1 -3
  26. package/dist/doctor-service.js +87 -91
  27. package/dist/effect/daemon-live.d.ts +7 -6
  28. package/dist/effect/daemon-live.js +57 -135
  29. package/dist/eval-routing-service.d.ts +1 -3
  30. package/dist/eval-routing-service.js +45 -69
  31. package/dist/eval-session-execution-store.d.ts +24 -0
  32. package/dist/eval-session-execution-store.js +253 -0
  33. package/dist/host-protocol.d.ts +1 -0
  34. package/dist/host.js +3 -0
  35. package/dist/internal-providers/provider-anthropic/index.d.ts +23 -0
  36. package/dist/internal-providers/provider-anthropic/index.js +42 -0
  37. package/dist/internal-providers/provider-anthropic/test/provider.test.js +33 -0
  38. package/dist/internal-providers/provider-azure/index.d.ts +25 -0
  39. package/dist/internal-providers/provider-azure/index.js +101 -0
  40. package/dist/internal-providers/provider-azure/test/provider.test.js +83 -0
  41. package/dist/internal-providers/provider-bedrock/codec.d.ts +11 -0
  42. package/dist/internal-providers/provider-bedrock/codec.js +484 -0
  43. package/dist/internal-providers/provider-bedrock/connection-settings.d.ts +6 -0
  44. package/dist/internal-providers/provider-bedrock/connection-settings.js +41 -0
  45. package/dist/internal-providers/provider-bedrock/connection.d.ts +17 -0
  46. package/dist/internal-providers/provider-bedrock/connection.js +143 -0
  47. package/dist/internal-providers/provider-bedrock/index.d.ts +27 -0
  48. package/dist/internal-providers/provider-bedrock/index.js +47 -0
  49. package/dist/internal-providers/provider-bedrock/source.d.ts +39 -0
  50. package/dist/internal-providers/provider-bedrock/source.js +454 -0
  51. package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.d.ts +1 -0
  52. package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.js +786 -0
  53. package/dist/internal-providers/provider-bedrock/test/connection.test.d.ts +1 -0
  54. package/dist/internal-providers/provider-bedrock/test/connection.test.js +249 -0
  55. package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.d.ts +1 -0
  56. package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.js +29 -0
  57. package/dist/internal-providers/provider-bedrock/workload-credentials.d.ts +5 -0
  58. package/dist/internal-providers/provider-bedrock/workload-credentials.js +14 -0
  59. package/dist/internal-providers/provider-claude-code/index.d.ts +6 -0
  60. package/dist/internal-providers/provider-claude-code/index.js +4 -0
  61. package/dist/internal-providers/provider-claude-code/module-api.d.ts +25 -0
  62. package/dist/internal-providers/provider-claude-code/module-api.js +31 -0
  63. package/dist/internal-providers/provider-claude-code/relay.d.ts +20 -0
  64. package/dist/internal-providers/provider-claude-code/relay.js +115 -0
  65. package/dist/internal-providers/provider-claude-code/subscription.d.ts +2 -0
  66. package/dist/internal-providers/provider-claude-code/subscription.js +194 -0
  67. package/dist/internal-providers/provider-claude-code/test/http-submission.test.d.ts +1 -0
  68. package/dist/internal-providers/provider-claude-code/test/http-submission.test.js +140 -0
  69. package/dist/internal-providers/provider-claude-code/test/relay.test.d.ts +1 -0
  70. package/dist/internal-providers/provider-claude-code/test/relay.test.js +379 -0
  71. package/dist/internal-providers/provider-claude-code/test/subscription.test.d.ts +1 -0
  72. package/dist/internal-providers/provider-claude-code/test/subscription.test.js +256 -0
  73. package/dist/internal-providers/provider-cliproxy/index.d.ts +9 -0
  74. package/dist/internal-providers/provider-cliproxy/index.js +15 -0
  75. package/dist/internal-providers/provider-codex/index.d.ts +6 -0
  76. package/dist/internal-providers/provider-codex/index.js +7 -0
  77. package/dist/internal-providers/provider-codex/module-api.d.ts +25 -0
  78. package/dist/internal-providers/provider-codex/module-api.js +31 -0
  79. package/dist/internal-providers/provider-codex/relay.d.ts +123 -0
  80. package/dist/internal-providers/provider-codex/relay.js +261 -0
  81. package/dist/internal-providers/provider-codex/subscription.d.ts +2 -0
  82. package/dist/internal-providers/provider-codex/subscription.js +555 -0
  83. package/dist/internal-providers/provider-codex/test/http-submission.test.d.ts +1 -0
  84. package/dist/internal-providers/provider-codex/test/http-submission.test.js +142 -0
  85. package/dist/internal-providers/provider-codex/test/relay.test.d.ts +1 -0
  86. package/dist/internal-providers/provider-codex/test/relay.test.js +600 -0
  87. package/dist/internal-providers/provider-codex/test/subscription.test.d.ts +1 -0
  88. package/dist/internal-providers/provider-codex/test/subscription.test.js +70 -0
  89. package/dist/internal-providers/provider-codex/test/translated-content.test.d.ts +1 -0
  90. package/dist/internal-providers/provider-codex/test/translated-content.test.js +91 -0
  91. package/dist/internal-providers/provider-custom/index.d.ts +9 -0
  92. package/dist/internal-providers/provider-custom/index.js +15 -0
  93. package/dist/internal-providers/provider-google/index.d.ts +23 -0
  94. package/dist/internal-providers/provider-google/index.js +43 -0
  95. package/dist/internal-providers/provider-google/test/provider.test.d.ts +1 -0
  96. package/dist/internal-providers/provider-google/test/provider.test.js +34 -0
  97. package/dist/internal-providers/provider-google-vertex/index.d.ts +28 -0
  98. package/dist/internal-providers/provider-google-vertex/index.js +137 -0
  99. package/dist/internal-providers/provider-google-vertex/test/provider.test.d.ts +1 -0
  100. package/dist/internal-providers/provider-google-vertex/test/provider.test.js +85 -0
  101. package/dist/internal-providers/provider-openai/documented-reasoning.d.ts +9 -0
  102. package/dist/internal-providers/provider-openai/documented-reasoning.js +26 -0
  103. package/dist/internal-providers/provider-openai/index.d.ts +23 -0
  104. package/dist/internal-providers/provider-openai/index.js +74 -0
  105. package/dist/internal-providers/provider-openai/test/provider.test.d.ts +1 -0
  106. package/dist/internal-providers/provider-openai/test/provider.test.js +268 -0
  107. package/dist/internal-providers/provider-openai-compatible/index.d.ts +9 -0
  108. package/dist/internal-providers/provider-openai-compatible/index.js +15 -0
  109. package/dist/internal-providers/provider-openai-compatible/test/provider.test.d.ts +1 -0
  110. package/dist/internal-providers/provider-openai-compatible/test/provider.test.js +40 -0
  111. package/dist/internal-providers/provider-openrouter/connection.d.ts +5 -0
  112. package/dist/internal-providers/provider-openrouter/connection.js +143 -0
  113. package/dist/internal-providers/provider-openrouter/index.d.ts +28 -0
  114. package/dist/internal-providers/provider-openrouter/index.js +42 -0
  115. package/dist/internal-providers/provider-openrouter/pricing.d.ts +4 -0
  116. package/dist/internal-providers/provider-openrouter/pricing.js +108 -0
  117. package/dist/internal-providers/provider-openrouter/test/connection.test.d.ts +1 -0
  118. package/dist/internal-providers/provider-openrouter/test/connection.test.js +71 -0
  119. package/dist/internal-providers/provider-openrouter/test/pricing.test.d.ts +1 -0
  120. package/dist/internal-providers/provider-openrouter/test/pricing.test.js +45 -0
  121. package/dist/internal-providers/provider-openrouter/test/provider.test.d.ts +1 -0
  122. package/dist/internal-providers/provider-openrouter/test/provider.test.js +141 -0
  123. package/dist/internal-providers/provider-openrouter/test/validation.test.d.ts +1 -0
  124. package/dist/internal-providers/provider-openrouter/test/validation.test.js +225 -0
  125. package/dist/launcher-service.d.ts +3 -7
  126. package/dist/launcher-service.js +144 -133
  127. package/dist/provider-modules.d.ts +6 -0
  128. package/dist/provider-modules.js +55 -0
  129. package/dist/provider-query-service.d.ts +1 -3
  130. package/dist/provider-query-service.js +140 -143
  131. package/dist/router-generation-service.d.ts +1 -3
  132. package/dist/router-generation-service.js +83 -78
  133. package/dist/services/eval-session/service.d.ts +20 -8
  134. package/dist/services/eval-session/service.js +410 -40
  135. package/dist/services/gateway-generation/service.d.ts +7 -3
  136. package/dist/services/gateway-generation/service.js +126 -63
  137. package/dist/services/generations/service.d.ts +3 -3
  138. package/dist/services/generations/service.js +5 -1
  139. package/dist/telemetry-application-service.d.ts +1 -3
  140. package/dist/telemetry-application-service.js +41 -45
  141. package/dist/test/call-attribution-store.test.js +186 -0
  142. package/dist/test/daemon-accounts.test.js +2 -2
  143. package/dist/test/daemon-data-plane.test.js +24 -45
  144. package/dist/test/daemon-fixtures.js +1 -1
  145. package/dist/test/daemon-generation-transaction.test.js +66 -2
  146. package/dist/test/daemon-runtime-state.test.js +28 -1
  147. package/dist/test/effect-daemon.test.js +38 -0
  148. package/dist/test/eval-routing-application-service.test.js +6 -6
  149. package/dist/test/eval-session-execution.test.d.ts +1 -0
  150. package/dist/test/eval-session-execution.test.js +356 -0
  151. package/dist/test/eval-session-service.test.js +70 -1
  152. package/dist/test/host-worker-session.test.js +1 -0
  153. package/dist/test/launcher-session.test.d.ts +1 -0
  154. package/dist/test/launcher-session.test.js +87 -0
  155. package/dist/test/leaderboard.test.js +25 -1
  156. package/dist/test/provider-modules.test.d.ts +1 -0
  157. package/dist/test/provider-modules.test.js +65 -0
  158. package/dist/test/routekit-upstream-sources.test.d.ts +1 -0
  159. package/dist/test/routekit-upstream-sources.test.js +40 -0
  160. package/dist/test/runtime-composition-types.test.js +11 -0
  161. package/dist/token-application-service.d.ts +1 -3
  162. package/dist/token-application-service.js +38 -44
  163. package/dist/worker.js +5 -3
  164. package/package.json +35 -11
  165. package/dist/application-services.d.ts +0 -10
  166. package/dist/application-services.js +0 -22
  167. package/dist/daemon-lifecycle.d.ts +0 -54
  168. package/dist/daemon-lifecycle.js +0 -177
  169. package/dist/test/application-services.test.js +0 -135
  170. package/dist/test/daemon-lifecycle.test.js +0 -182
  171. /package/dist/{test/application-services.test.d.ts → internal-providers/provider-anthropic/test/provider.test.d.ts} +0 -0
  172. /package/dist/{test/daemon-lifecycle.test.d.ts → internal-providers/provider-azure/test/provider.test.d.ts} +0 -0
@@ -150,6 +150,7 @@ test("singleton daemon exposes authenticated control and a stable reloadable dat
150
150
  const upstream = await mockProvider();
151
151
  const daemon = await startRouteKitDaemon({
152
152
  packageVersion: "1.2.3",
153
+ sourceBuildId: "checkout-build",
153
154
  stateHome,
154
155
  configPath,
155
156
  port: 0,
@@ -174,6 +175,7 @@ test("singleton daemon exposes authenticated control and a stable reloadable dat
174
175
  assert.equal(record.dataUrl, daemon.dataUrl);
175
176
  assert.equal(record.protocolVersion, "control.v2");
176
177
  assert.equal(record.generation, 1);
178
+ assert.equal(record.sourceBuildId, "checkout-build");
177
179
  assert.equal(statSync(join(stateHome, "services", "daemon.json")).mode & 0o777, 0o600);
178
180
  assert.ok(record.authTokenFile !== undefined);
179
181
  const dataToken = readFileSync(record.authTokenFile, "utf8").trim();
@@ -431,53 +433,31 @@ test("singleton daemon exposes authenticated control and a stable reloadable dat
431
433
  rmSync(root, { recursive: true, force: true });
432
434
  }
433
435
  });
434
- test("local Codex preparation ranks an incompatible OpenAI default by native recency", async () => {
436
+ test("local Codex preparation ranks Responses-compatible OpenAI models by native recency", async () => {
435
437
  const root = mkdtempSync(join(tmpdir(), "routekit-daemon-codex-ranking-"));
436
438
  const stateHome = join(root, "state");
437
439
  const configPath = join(root, "router.yaml");
438
440
  writeFileSync(configPath, "providers:\n openai: {}\ndefaultModel: openai/text-embedding-test\n");
439
441
  const upstream = await mockProvider([
440
- { id: "text-embedding-test", object: "model", created: 50 },
441
- { id: "older-generation", object: "model", created: 100 },
442
- { id: "newer-generation", object: "model", created: 200 }
443
- ]);
444
- const originalFetch = globalThis.fetch;
445
- let openRouterFetches = 0;
446
- globalThis.fetch = async (input, init) => {
447
- const url = new URL(input instanceof Request ? input.url : input.toString());
448
- if (url.hostname === "openrouter.ai") {
449
- openRouterFetches += 1;
450
- if (url.pathname === "/api/v1/models") {
451
- return Response.json({
452
- data: [
453
- {
454
- id: "openai/older-generation",
455
- created: 900,
456
- architecture: {
457
- input_modalities: ["text"],
458
- output_modalities: ["text"]
459
- },
460
- supported_parameters: ["tools"]
461
- },
462
- {
463
- id: "openai/newer-generation",
464
- created: 800,
465
- architecture: {
466
- input_modalities: ["text"],
467
- output_modalities: ["text"]
468
- },
469
- supported_parameters: ["tools"]
470
- }
471
- ]
472
- });
473
- }
474
- if (url.pathname === "/api/v1/embeddings/models") {
475
- return Response.json({ data: [{ id: "openai/text-embedding-test" }] });
476
- }
477
- return Response.json({ data: [] });
442
+ {
443
+ id: "text-embedding-test",
444
+ object: "model",
445
+ created: 50,
446
+ capabilities: { responses: "unsupported" }
447
+ },
448
+ {
449
+ id: "older-generation",
450
+ object: "model",
451
+ created: 100,
452
+ capabilities: { responses: "supported" }
453
+ },
454
+ {
455
+ id: "newer-generation",
456
+ object: "model",
457
+ created: 200,
458
+ capabilities: { responses: "supported" }
478
459
  }
479
- return await originalFetch(input, init);
480
- };
460
+ ]);
481
461
  let daemon;
482
462
  try {
483
463
  daemon = await startRouteKitDaemon({
@@ -504,17 +484,16 @@ test("local Codex preparation ranks an incompatible OpenAI default by native rec
504
484
  model: "openai/older-generation"
505
485
  }));
506
486
  assert.equal(explicit.model, "openai/older-generation");
507
- assert.equal(openRouterFetches, 0);
508
487
  const implicit = await runRouteKitEffect(client.call("launcher.prepare", { tool: "codex" }));
509
488
  assert.equal(implicit.model, "openai/newer-generation");
510
- assert.equal(openRouterFetches, 4);
511
489
  assert.equal(implicit.codexSelection?.models.find((model) => model.id === "openai/older-generation")
512
- ?.createdAt, 100, "native OpenAI creation time wins over OpenRouter");
490
+ ?.createdAt, 100, "native OpenAI creation time remains authoritative");
513
491
  assert.equal(implicit.codexSelection?.models.find((model) => model.id === "openai/newer-generation")
514
492
  ?.createdAt, 200);
493
+ assert.equal(implicit.codexSelection?.models.find((model) => model.id === "openai/newer-generation")
494
+ ?.capabilities?.responses, "supported");
515
495
  }
516
496
  finally {
517
- globalThis.fetch = originalFetch;
518
497
  if (daemon !== undefined)
519
498
  await daemon.close();
520
499
  await upstream.close();
@@ -268,7 +268,7 @@ for (const kind of ["claude-code", "codex"]) {
268
268
  assert.equal(after.configRevision, before.configRevision + 1);
269
269
  assert.equal(after.accountRevision, before.accountRevision + 1);
270
270
  const config = parseYaml((await runRouteKitEffect(client.call("config.get", {}))).document);
271
- assert.deepEqual(Object.keys(config.providers), []);
271
+ assert.deepEqual(Object.keys(config.sources), []);
272
272
  assert.equal(config.defaultModel, undefined);
273
273
  assert.deepEqual((await runRouteKitEffect(client.call("providers.status", {}))).providers, []);
274
274
  const listed = await runRouteKitEffect(client.call("models.list", {}));
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import test from "node:test";
@@ -7,7 +7,7 @@ import { RouteKitControlClient } from "@velum-labs/routekit-control";
7
7
  import { runRouteKitEffect } from "@velum-labs/routekit-runtime/effect";
8
8
  import { readDaemonRevisions } from "../daemon-state.js";
9
9
  import { startRouteKitDaemon } from "../index.js";
10
- import { mockProvider } from "./daemon-fixtures.js";
10
+ import { mockProvider, nativeCredential, withMockNativeDiscovery } from "./daemon-fixtures.js";
11
11
  const INITIAL_DOCUMENT = "providers:\n openai:\n strategy: sticky\ndefaultModel: openai/mock-model\n";
12
12
  const NEXT_DOCUMENT = "providers:\n openai:\n strategy: round_robin\ndefaultModel: openai/other-model\n";
13
13
  test("generation transaction rolls back every pre-publication stage and keeps retirement best-effort", async () => {
@@ -89,3 +89,67 @@ test("generation transaction rolls back every pre-publication stage and keeps re
89
89
  rmSync(root, { recursive: true, force: true });
90
90
  }
91
91
  });
92
+ test("commit rollback restores the previous native credential auth fingerprint and state", async () => {
93
+ const root = mkdtempSync(join(tmpdir(), "routekit-daemon-generation-auth-rollback-"));
94
+ const stateHome = join(root, "state");
95
+ const configPath = join(root, "router.yaml");
96
+ const accountsDirectory = join(stateHome, "subscriptions", "codex");
97
+ const accountPath = join(accountsDirectory, "work.json");
98
+ const authStatePath = join(stateHome, "subscriptions", "account-auth.v1.json");
99
+ mkdirSync(accountsDirectory, { recursive: true });
100
+ writeFileSync(accountPath, `${JSON.stringify(nativeCredential("codex"))}\n`);
101
+ writeFileSync(configPath, "providers:\n codex: {}\ndefaultModel: codex/gpt-test-model\n");
102
+ let failCommit = false;
103
+ try {
104
+ await withMockNativeDiscovery("codex", async () => {
105
+ const daemon = await startRouteKitDaemon({
106
+ packageVersion: "1.2.3",
107
+ stateHome,
108
+ configPath,
109
+ port: 0,
110
+ portless: false,
111
+ onGenerationStage: (stage) => {
112
+ if (failCommit && stage === "commit") {
113
+ failCommit = false;
114
+ throw new Error("injected native credential commit failure");
115
+ }
116
+ },
117
+ env: {
118
+ HOME: root,
119
+ ROUTEKIT_HOME: stateHome,
120
+ ROUTEKIT_PORTLESS: "0"
121
+ }
122
+ });
123
+ try {
124
+ const client = new RouteKitControlClient({
125
+ url: daemon.record.url,
126
+ token: daemon.record.controlToken
127
+ });
128
+ const before = await runRouteKitEffect(client.call("accounts.status", {}));
129
+ assert.equal(before.accounts[0]?.upstreamAuthState, "accepted");
130
+ const previousAuthState = readFileSync(authStatePath, "utf8");
131
+ const replacement = nativeCredential("codex");
132
+ replacement.tokens.access_token =
133
+ "eyJhbGciOiJub25lIn0.eyJleHAiOjk5OTk5OTk5OTl9.replacement";
134
+ replacement.tokens.refresh_token = "replacement-refresh";
135
+ writeFileSync(accountPath, `${JSON.stringify(replacement)}\n`);
136
+ const daemonStatus = await runRouteKitEffect(client.call("daemon.status", {}));
137
+ failCommit = true;
138
+ await assert.rejects(runRouteKitEffect(client.call("daemon.reload", {
139
+ expectedRevision: daemonStatus.configRevision
140
+ })));
141
+ const after = await runRouteKitEffect(client.call("accounts.status", {}));
142
+ assert.equal(after.accounts[0]?.upstreamAuthState, "accepted");
143
+ assert.equal(after.accounts[0]?.relayOpen, true);
144
+ assert.deepEqual(after.accounts[0]?.readinessReasons, []);
145
+ assert.equal(readFileSync(authStatePath, "utf8"), previousAuthState);
146
+ }
147
+ finally {
148
+ await daemon.close();
149
+ }
150
+ });
151
+ }
152
+ finally {
153
+ rmSync(root, { recursive: true, force: true });
154
+ }
155
+ });
@@ -3,8 +3,35 @@ import test from "node:test";
3
3
  import { parseRouterConfig } from "@velum-labs/routekit-config";
4
4
  import { ControlError } from "@velum-labs/routekit-runtime/control";
5
5
  import { runRouteKitEffect } from "@velum-labs/routekit-runtime/effect";
6
- import { Effect } from "effect";
6
+ import { Deferred, Effect, Fiber } from "effect";
7
7
  import { DaemonRuntimeState } from "../daemon-runtime-state.js";
8
+ import { DaemonState } from "../daemon-state-context.js";
9
+ test("daemon state service shares its owner and waits for queued mutations", async () => {
10
+ const state = new DaemonRuntimeState({
11
+ config: parseRouterConfig({ providers: {} }),
12
+ document: "providers: {}\n",
13
+ revisions: { config: 0, accounts: 0, daemon: 0 }
14
+ });
15
+ await runRouteKitEffect(Effect.gen(function* () {
16
+ const service = yield* DaemonState;
17
+ assert.strictEqual(service, state);
18
+ const started = yield* Deferred.make();
19
+ const release = yield* Deferred.make();
20
+ const mutation = yield* service
21
+ .serializeEffect(Effect.gen(function* () {
22
+ yield* Deferred.succeed(started, undefined);
23
+ yield* Deferred.await(release);
24
+ }))
25
+ .pipe(Effect.forkChild);
26
+ yield* Deferred.await(started);
27
+ const waiting = yield* service.awaitMutations.pipe(Effect.forkChild);
28
+ const beforeRelease = yield* Effect.race(Fiber.join(waiting).pipe(Effect.as("completed")), Effect.sleep("1 millis").pipe(Effect.as("pending")));
29
+ assert.equal(beforeRelease, "pending");
30
+ yield* Deferred.succeed(release, undefined);
31
+ yield* Fiber.join(mutation);
32
+ yield* Fiber.join(waiting);
33
+ }).pipe(Effect.provide(DaemonState.layer(state))));
34
+ });
8
35
  test("daemon runtime state serializes mutations and exposes a stable snapshot", async () => {
9
36
  const state = new DaemonRuntimeState({
10
37
  config: parseRouterConfig({ providers: {} }),
@@ -1,12 +1,50 @@
1
1
  import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
2
5
  import { test } from "node:test";
3
6
  import { parseRouterConfig } from "@velum-labs/routekit-config";
4
7
  import { ControlError } from "@velum-labs/routekit-runtime/control";
5
8
  import { runRouteKitEffect } from "@velum-labs/routekit-runtime/effect";
6
9
  import { Effect } from "effect";
7
10
  import { createCliproxySidecar } from "../cliproxy-sidecar.js";
11
+ import { makeRouteKitDaemonRuntime, runningRouteKitDaemon } from "../daemon-bootstrap.js";
12
+ import { DaemonPolicy } from "../daemon-policy-context.js";
13
+ import { DataPlane } from "../data-plane-context.js";
8
14
  import { DaemonRuntimeState } from "../daemon-runtime-state.js";
9
15
  const config = parseRouterConfig({ providers: {} });
16
+ test("published daemon layer provides the data-plane and policy services", async () => {
17
+ const root = mkdtempSync(join(tmpdir(), "routekit-daemon-compat-services-"));
18
+ const home = join(root, "state");
19
+ const configPath = join(root, "router.yaml");
20
+ writeFileSync(configPath, "providers: {}\n");
21
+ const runtime = makeRouteKitDaemonRuntime({
22
+ packageVersion: "0.0.0",
23
+ stateHome: home,
24
+ configPath,
25
+ port: 0,
26
+ controlPort: 0,
27
+ portless: false,
28
+ env: { HOME: root, ROUTEKIT_HOME: home, ROUTEKIT_PORTLESS: "0" }
29
+ });
30
+ try {
31
+ const daemon = await runningRouteKitDaemon(runtime);
32
+ try {
33
+ const plane = await runtime.runPromise(DataPlane);
34
+ const policy = await runtime.runPromise(DaemonPolicy);
35
+ assert.equal(plane.path, daemon.record.authTokenFile);
36
+ assert.equal(plane.token.length > 0, true);
37
+ assert.equal(policy.wantsCliproxySidecar(config), false);
38
+ }
39
+ finally {
40
+ await daemon.close();
41
+ }
42
+ }
43
+ finally {
44
+ await runtime.dispose();
45
+ rmSync(root, { recursive: true, force: true });
46
+ }
47
+ });
10
48
  test("daemon runtime-state mutations serialize on the shared tail", async () => {
11
49
  const state = new DaemonRuntimeState({
12
50
  config,
@@ -11,7 +11,7 @@ import { DaemonRuntimeState } from "../daemon-runtime-state.js";
11
11
  import { ActiveGateway } from "../services/active-gateway/service.js";
12
12
  import { DaemonEnv } from "../daemon-env-context.js";
13
13
  import { DaemonState } from "../daemon-state-context.js";
14
- import { EvalRoutingApplicationService } from "../eval-routing-service.js";
14
+ import { evalRoutingHandlers } from "../eval-routing-service.js";
15
15
  test("legacy V2 publication is rejected because V2 is rollback-only", async () => {
16
16
  const home = mkdtempSync(join(tmpdir(), "routekit-routing-activation-"));
17
17
  const daemonEnv = Layer.succeed(DaemonEnv, DaemonEnv.of({
@@ -24,11 +24,11 @@ test("legacy V2 publication is rejected because V2 is rollback-only", async () =
24
24
  hosted: undefined
25
25
  }));
26
26
  const runtimeState = new DaemonRuntimeState({
27
- config: {
28
- providers: { openai: {} },
27
+ config: parseRouterConfig({
28
+ sources: { openai: { use: "openai" } },
29
29
  defaultModel: "openai/model-a",
30
30
  classifierModel: "openai/classifier"
31
- },
31
+ }),
32
32
  document: "",
33
33
  revisions: { daemon: 1, config: 1, accounts: 1 }
34
34
  });
@@ -46,7 +46,7 @@ test("legacy V2 publication is rejected because V2 is rollback-only", async () =
46
46
  });
47
47
  })());
48
48
  const services = Layer.mergeAll(daemonEnv, DaemonState.layer(runtimeState), activeGateway);
49
- const handlers = new EvalRoutingApplicationService().handlers();
49
+ const handlers = evalRoutingHandlers;
50
50
  const run = (effect) => runRouteKitEffect(effect.pipe(Effect.provide(services)));
51
51
  try {
52
52
  assert.deepEqual(await run(handlers["evalRouting.status"]({}, undefined)), {
@@ -123,7 +123,7 @@ test("V3 authority installs when its pinned models match the running gateway", a
123
123
  });
124
124
  })());
125
125
  const services = Layer.mergeAll(daemonEnv, DaemonState.layer(runtimeState), activeGateway);
126
- const handler = new EvalRoutingApplicationService().handlers()["evalRouting.installAuthoritative"];
126
+ const handler = evalRoutingHandlers["evalRouting.installAuthoritative"];
127
127
  const run = (effect) => runRouteKitEffect(effect.pipe(Effect.provide(services)));
128
128
  const activationPath = join(repositoryRoot, ".routekit", "routing", "activations", `${activationV3.activationDigest}.json`);
129
129
  mkdirSync(join(repositoryRoot, ".routekit", "routing", "activations"), {
@@ -0,0 +1 @@
1
+ export {};