@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.
- package/dist/account-enroll-service.d.ts +1 -3
- package/dist/account-enroll-service.js +262 -262
- package/dist/account-mutation-service.d.ts +1 -3
- package/dist/account-mutation-service.js +302 -283
- package/dist/account-query-service.d.ts +1 -3
- package/dist/account-query-service.js +88 -86
- package/dist/call-attribution-store.js +544 -1
- package/dist/control-dispatch.js +9 -15
- package/dist/daemon-bootstrap-preflight.js +1 -1
- package/dist/daemon-control-handlers.d.ts +1 -1
- package/dist/daemon-control-handlers.js +24 -27
- package/dist/daemon-generations.d.ts +14 -28
- package/dist/daemon-generations.js +114 -53
- package/dist/daemon-lifecycle-service.d.ts +1 -3
- package/dist/daemon-lifecycle-service.js +88 -94
- package/dist/daemon-maintenance.d.ts +27 -49
- package/dist/daemon-maintenance.js +5 -1
- package/dist/daemon-options.d.ts +3 -2
- package/dist/daemon-policy-context.d.ts +1 -0
- package/dist/daemon-policy-context.js +1 -0
- package/dist/daemon-runtime-state.d.ts +1 -1
- package/dist/daemon-runtime-state.js +2 -3
- package/dist/daemon-state-context.d.ts +1 -1
- package/dist/daemon-state-context.js +1 -38
- package/dist/doctor-service.d.ts +1 -3
- package/dist/doctor-service.js +87 -91
- package/dist/effect/daemon-live.d.ts +7 -6
- package/dist/effect/daemon-live.js +57 -135
- package/dist/eval-routing-service.d.ts +1 -3
- package/dist/eval-routing-service.js +45 -69
- package/dist/eval-session-execution-store.d.ts +24 -0
- package/dist/eval-session-execution-store.js +253 -0
- package/dist/host-protocol.d.ts +1 -0
- package/dist/host.js +3 -0
- package/dist/internal-providers/provider-anthropic/index.d.ts +23 -0
- package/dist/internal-providers/provider-anthropic/index.js +42 -0
- package/dist/internal-providers/provider-anthropic/test/provider.test.js +33 -0
- package/dist/internal-providers/provider-azure/index.d.ts +25 -0
- package/dist/internal-providers/provider-azure/index.js +101 -0
- package/dist/internal-providers/provider-azure/test/provider.test.js +83 -0
- package/dist/internal-providers/provider-bedrock/codec.d.ts +11 -0
- package/dist/internal-providers/provider-bedrock/codec.js +484 -0
- package/dist/internal-providers/provider-bedrock/connection-settings.d.ts +6 -0
- package/dist/internal-providers/provider-bedrock/connection-settings.js +41 -0
- package/dist/internal-providers/provider-bedrock/connection.d.ts +17 -0
- package/dist/internal-providers/provider-bedrock/connection.js +143 -0
- package/dist/internal-providers/provider-bedrock/index.d.ts +27 -0
- package/dist/internal-providers/provider-bedrock/index.js +47 -0
- package/dist/internal-providers/provider-bedrock/source.d.ts +39 -0
- package/dist/internal-providers/provider-bedrock/source.js +454 -0
- package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.js +786 -0
- package/dist/internal-providers/provider-bedrock/test/connection.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/connection.test.js +249 -0
- package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.d.ts +1 -0
- package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.js +29 -0
- package/dist/internal-providers/provider-bedrock/workload-credentials.d.ts +5 -0
- package/dist/internal-providers/provider-bedrock/workload-credentials.js +14 -0
- package/dist/internal-providers/provider-claude-code/index.d.ts +6 -0
- package/dist/internal-providers/provider-claude-code/index.js +4 -0
- package/dist/internal-providers/provider-claude-code/module-api.d.ts +25 -0
- package/dist/internal-providers/provider-claude-code/module-api.js +31 -0
- package/dist/internal-providers/provider-claude-code/relay.d.ts +20 -0
- package/dist/internal-providers/provider-claude-code/relay.js +115 -0
- package/dist/internal-providers/provider-claude-code/subscription.d.ts +2 -0
- package/dist/internal-providers/provider-claude-code/subscription.js +194 -0
- package/dist/internal-providers/provider-claude-code/test/http-submission.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/http-submission.test.js +140 -0
- package/dist/internal-providers/provider-claude-code/test/relay.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/relay.test.js +379 -0
- package/dist/internal-providers/provider-claude-code/test/subscription.test.d.ts +1 -0
- package/dist/internal-providers/provider-claude-code/test/subscription.test.js +256 -0
- package/dist/internal-providers/provider-cliproxy/index.d.ts +9 -0
- package/dist/internal-providers/provider-cliproxy/index.js +15 -0
- package/dist/internal-providers/provider-codex/index.d.ts +6 -0
- package/dist/internal-providers/provider-codex/index.js +7 -0
- package/dist/internal-providers/provider-codex/module-api.d.ts +25 -0
- package/dist/internal-providers/provider-codex/module-api.js +31 -0
- package/dist/internal-providers/provider-codex/relay.d.ts +123 -0
- package/dist/internal-providers/provider-codex/relay.js +261 -0
- package/dist/internal-providers/provider-codex/subscription.d.ts +2 -0
- package/dist/internal-providers/provider-codex/subscription.js +555 -0
- package/dist/internal-providers/provider-codex/test/http-submission.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/http-submission.test.js +142 -0
- package/dist/internal-providers/provider-codex/test/relay.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/relay.test.js +600 -0
- package/dist/internal-providers/provider-codex/test/subscription.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/subscription.test.js +70 -0
- package/dist/internal-providers/provider-codex/test/translated-content.test.d.ts +1 -0
- package/dist/internal-providers/provider-codex/test/translated-content.test.js +91 -0
- package/dist/internal-providers/provider-custom/index.d.ts +9 -0
- package/dist/internal-providers/provider-custom/index.js +15 -0
- package/dist/internal-providers/provider-google/index.d.ts +23 -0
- package/dist/internal-providers/provider-google/index.js +43 -0
- package/dist/internal-providers/provider-google/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-google/test/provider.test.js +34 -0
- package/dist/internal-providers/provider-google-vertex/index.d.ts +28 -0
- package/dist/internal-providers/provider-google-vertex/index.js +137 -0
- package/dist/internal-providers/provider-google-vertex/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-google-vertex/test/provider.test.js +85 -0
- package/dist/internal-providers/provider-openai/documented-reasoning.d.ts +9 -0
- package/dist/internal-providers/provider-openai/documented-reasoning.js +26 -0
- package/dist/internal-providers/provider-openai/index.d.ts +23 -0
- package/dist/internal-providers/provider-openai/index.js +74 -0
- package/dist/internal-providers/provider-openai/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openai/test/provider.test.js +268 -0
- package/dist/internal-providers/provider-openai-compatible/index.d.ts +9 -0
- package/dist/internal-providers/provider-openai-compatible/index.js +15 -0
- package/dist/internal-providers/provider-openai-compatible/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openai-compatible/test/provider.test.js +40 -0
- package/dist/internal-providers/provider-openrouter/connection.d.ts +5 -0
- package/dist/internal-providers/provider-openrouter/connection.js +143 -0
- package/dist/internal-providers/provider-openrouter/index.d.ts +28 -0
- package/dist/internal-providers/provider-openrouter/index.js +42 -0
- package/dist/internal-providers/provider-openrouter/pricing.d.ts +4 -0
- package/dist/internal-providers/provider-openrouter/pricing.js +108 -0
- package/dist/internal-providers/provider-openrouter/test/connection.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/connection.test.js +71 -0
- package/dist/internal-providers/provider-openrouter/test/pricing.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/pricing.test.js +45 -0
- package/dist/internal-providers/provider-openrouter/test/provider.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/provider.test.js +141 -0
- package/dist/internal-providers/provider-openrouter/test/validation.test.d.ts +1 -0
- package/dist/internal-providers/provider-openrouter/test/validation.test.js +225 -0
- package/dist/launcher-service.d.ts +3 -7
- package/dist/launcher-service.js +144 -133
- package/dist/provider-modules.d.ts +6 -0
- package/dist/provider-modules.js +55 -0
- package/dist/provider-query-service.d.ts +1 -3
- package/dist/provider-query-service.js +140 -143
- package/dist/router-generation-service.d.ts +1 -3
- package/dist/router-generation-service.js +83 -78
- package/dist/services/eval-session/service.d.ts +20 -8
- package/dist/services/eval-session/service.js +410 -40
- package/dist/services/gateway-generation/service.d.ts +7 -3
- package/dist/services/gateway-generation/service.js +126 -63
- package/dist/services/generations/service.d.ts +3 -3
- package/dist/services/generations/service.js +5 -1
- package/dist/telemetry-application-service.d.ts +1 -3
- package/dist/telemetry-application-service.js +41 -45
- package/dist/test/call-attribution-store.test.js +186 -0
- package/dist/test/daemon-accounts.test.js +2 -2
- package/dist/test/daemon-data-plane.test.js +24 -45
- package/dist/test/daemon-fixtures.js +1 -1
- package/dist/test/daemon-generation-transaction.test.js +66 -2
- package/dist/test/daemon-runtime-state.test.js +28 -1
- package/dist/test/effect-daemon.test.js +38 -0
- package/dist/test/eval-routing-application-service.test.js +6 -6
- package/dist/test/eval-session-execution.test.d.ts +1 -0
- package/dist/test/eval-session-execution.test.js +356 -0
- package/dist/test/eval-session-service.test.js +70 -1
- package/dist/test/host-worker-session.test.js +1 -0
- package/dist/test/launcher-session.test.d.ts +1 -0
- package/dist/test/launcher-session.test.js +87 -0
- package/dist/test/leaderboard.test.js +25 -1
- package/dist/test/provider-modules.test.d.ts +1 -0
- package/dist/test/provider-modules.test.js +65 -0
- package/dist/test/routekit-upstream-sources.test.d.ts +1 -0
- package/dist/test/routekit-upstream-sources.test.js +40 -0
- package/dist/test/runtime-composition-types.test.js +11 -0
- package/dist/token-application-service.d.ts +1 -3
- package/dist/token-application-service.js +38 -44
- package/dist/worker.js +5 -3
- package/package.json +35 -11
- package/dist/application-services.d.ts +0 -10
- package/dist/application-services.js +0 -22
- package/dist/daemon-lifecycle.d.ts +0 -54
- package/dist/daemon-lifecycle.js +0 -177
- package/dist/test/application-services.test.js +0 -135
- package/dist/test/daemon-lifecycle.test.js +0 -182
- /package/dist/{test/application-services.test.d.ts → internal-providers/provider-anthropic/test/provider.test.d.ts} +0 -0
- /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
|
|
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
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 {};
|