@velum-labs/routekit-daemon 0.16.0 → 0.16.2
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/index.js +37 -39
- package/dist/leaderboard.d.ts +1 -0
- package/dist/leaderboard.js +5 -0
- package/dist/test/daemon.test.js +97 -19
- package/dist/test/leaderboard.test.js +7 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -8,20 +8,19 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
10
10
|
import { basename, dirname, join } from "node:path";
|
|
11
|
-
import { AccountActivityCoordinator, CLIPROXY_API_KEY_ENV, CLIPROXY_BASE_URL_ENV,
|
|
11
|
+
import { AccountActivityCoordinator, accountStoreEntries, CLIPROXY_API_KEY_ENV, CLIPROXY_BASE_URL_ENV, cliproxyAccountEntries, cliproxyAccountMatchesKind, cliproxyApiKey, cliproxyAuthDirectory, cliproxyBaseUrl, cliproxyCredentialValid, defaultSubscriptionAccountDirectory, RateLimitTracker, removeCliproxyAccount, removeSubscriptionAccount, renameSubscriptionAccount, sanitizeSubscriptionLabel, subscriptionAccountIdentity } from "@velum-labs/routekit-accounts";
|
|
12
12
|
import { configuredProviderIds, globalRouterConfigPath, parseRouterConfigDocument, routekitHome, writeRouterConfig } from "@velum-labs/routekit-config";
|
|
13
13
|
import { createRouteKitControlHandler, ROUTEKIT_CONTROL_CAPABILITY } from "@velum-labs/routekit-control";
|
|
14
|
-
import { startSwitchingGatewayProxy } from "@velum-labs/routekit-gateway";
|
|
15
|
-
import {
|
|
16
|
-
import { PROVIDERS, accountKindForCliproxyAuthType, resolveAccountConnector } from "@velum-labs/routekit-registry";
|
|
14
|
+
import { resolveLeaderboardConfig, startSwitchingGatewayProxy } from "@velum-labs/routekit-gateway";
|
|
15
|
+
import { accountKindForCliproxyAuthType, PROVIDERS, resolveAccountConnector } from "@velum-labs/routekit-registry";
|
|
17
16
|
import { startRouter } from "@velum-labs/routekit-router";
|
|
18
17
|
import { acquireLifecycleLock, CONTROL_PROTOCOL_VERSION, ControlClient, ControlError, createPortlessSession, createServiceRecordStore, createTokenStore, encodeJoinCredential, extendCleanupGrace, generateControlToken, nextServiceGeneration, processIdentity, registerCleanup, SERVICE_HOME_MODE, startControlServer, supervisorFromEnv, writeFileAtomic } from "@velum-labs/routekit-runtime";
|
|
19
18
|
import { createConsentManager } from "@velum-labs/routekit-telemetry-core";
|
|
20
19
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
21
|
-
import { createCliproxySidecar } from "./cliproxy-sidecar.js";
|
|
22
20
|
import { cleanupAccountTransaction, markAccountTransactionCommitted, prepareAccountTransaction, recoverAccountTransactions, rollbackAccountTransaction } from "./account-transaction.js";
|
|
23
21
|
import { CallAttributionStore, callInspection } from "./call-attribution-store.js";
|
|
24
|
-
import {
|
|
22
|
+
import { createCliproxySidecar } from "./cliproxy-sidecar.js";
|
|
23
|
+
import { aggregateInspections, buildLeaderboardResult, defaultLeaderboardWindow, LeaderboardRollupStore } from "./leaderboard.js";
|
|
25
24
|
export const ROUTEKIT_DAEMON_KIND = "daemon";
|
|
26
25
|
export const ROUTEKIT_PRODUCT = "routekit";
|
|
27
26
|
function dataTokenPath(home) {
|
|
@@ -111,9 +110,7 @@ function readRevisions(home) {
|
|
|
111
110
|
accounts: typeof parsed.accounts === "number" && Number.isSafeInteger(parsed.accounts)
|
|
112
111
|
? parsed.accounts
|
|
113
112
|
: 0,
|
|
114
|
-
daemon: typeof parsed.daemon === "number" && Number.isSafeInteger(parsed.daemon)
|
|
115
|
-
? parsed.daemon
|
|
116
|
-
: 0
|
|
113
|
+
daemon: typeof parsed.daemon === "number" && Number.isSafeInteger(parsed.daemon) ? parsed.daemon : 0
|
|
117
114
|
};
|
|
118
115
|
}
|
|
119
116
|
catch {
|
|
@@ -170,7 +167,7 @@ function providerCredentialAvailable(provider, accounts, env) {
|
|
|
170
167
|
return accounts.some((entry) => entry.subscriptionKind === provider);
|
|
171
168
|
}
|
|
172
169
|
if (provider === "cliproxy") {
|
|
173
|
-
return (
|
|
170
|
+
return (env[CLIPROXY_API_KEY_ENV] ?? "").length > 0 || cliproxyApiKey(env) !== undefined;
|
|
174
171
|
}
|
|
175
172
|
const info = PROVIDERS[provider];
|
|
176
173
|
if (info?.keyEnv === undefined)
|
|
@@ -203,7 +200,7 @@ function safeCliproxyCredentialBlob(kind, value) {
|
|
|
203
200
|
const type = typeof record.type === "string" ? record.type : undefined;
|
|
204
201
|
const classified = type === undefined
|
|
205
202
|
? undefined
|
|
206
|
-
: accountKindForCliproxyAuthType(type) ?? resolveAccountConnector(type)?.kind;
|
|
203
|
+
: (accountKindForCliproxyAuthType(type) ?? resolveAccountConnector(type)?.kind);
|
|
207
204
|
if (classified !== kind || !cliproxyCredentialValid(record, type)) {
|
|
208
205
|
throw new ControlError({
|
|
209
206
|
code: "bad_request",
|
|
@@ -396,10 +393,12 @@ export async function startRouteKitDaemon(options) {
|
|
|
396
393
|
};
|
|
397
394
|
}
|
|
398
395
|
});
|
|
399
|
-
portless = await createPortlessSession(options.portless ?? env.ROUTEKIT_PORTLESS !== "0", {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
:
|
|
396
|
+
portless = await createPortlessSession(options.portless ?? env.ROUTEKIT_PORTLESS !== "0", {
|
|
397
|
+
project: "routekit",
|
|
398
|
+
ownerLabel: "routekit-daemon",
|
|
399
|
+
bareNames: []
|
|
400
|
+
});
|
|
401
|
+
const dataUrl = portless.enabled ? portless.register("gateway", proxy.port()) : proxy.url();
|
|
403
402
|
const replaceRouter = async (nextConfig, nextDocument, input) => {
|
|
404
403
|
// Sidecar reconcile runs before the generation commits; any failure
|
|
405
404
|
// below must put the sidecar back to the still-live currentConfig.
|
|
@@ -641,7 +640,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
641
640
|
const by = params.by ?? "principal";
|
|
642
641
|
const sort = params.sort ?? "cost";
|
|
643
642
|
const limit = params.limit ?? 20;
|
|
644
|
-
const window = params.window ??
|
|
643
|
+
const window = params.window ?? defaultLeaderboardWindow(leaderboardConfig);
|
|
645
644
|
const nowIso = new Date().toISOString();
|
|
646
645
|
if (window === "live") {
|
|
647
646
|
const inspections = callAttributions.list();
|
|
@@ -708,6 +707,9 @@ export async function startRouteKitDaemon(options) {
|
|
|
708
707
|
inFlight: 0,
|
|
709
708
|
lastSelected: false,
|
|
710
709
|
active: false,
|
|
710
|
+
...(entry.credentialValid
|
|
711
|
+
? {}
|
|
712
|
+
: { readinessReasons: [{ code: "credential_invalid" }] }),
|
|
711
713
|
models: []
|
|
712
714
|
};
|
|
713
715
|
}
|
|
@@ -730,6 +732,11 @@ export async function startRouteKitDaemon(options) {
|
|
|
730
732
|
: {}),
|
|
731
733
|
lastSelected: member?.lastSelected ?? false,
|
|
732
734
|
active: member?.lastSelected ?? false,
|
|
735
|
+
...(member?.readinessReasons !== undefined
|
|
736
|
+
? { readinessReasons: member.readinessReasons }
|
|
737
|
+
: member === undefined
|
|
738
|
+
? { readinessReasons: [{ code: "credential_invalid" }] }
|
|
739
|
+
: {}),
|
|
733
740
|
models: member?.models ?? [],
|
|
734
741
|
...(member?.limits !== undefined ? { limits: member.limits } : {})
|
|
735
742
|
};
|
|
@@ -797,8 +804,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
797
804
|
const label = connector === "native"
|
|
798
805
|
? sanitizeSubscriptionLabel(account.label)
|
|
799
806
|
: safeCliproxyLabel(account.label);
|
|
800
|
-
if (label !== account.label ||
|
|
801
|
-
(connector === "native" && label.startsWith("."))) {
|
|
807
|
+
if (label !== account.label || (connector === "native" && label.startsWith("."))) {
|
|
802
808
|
throw new ControlError({
|
|
803
809
|
code: "bad_request",
|
|
804
810
|
message: "account label must already be normalized"
|
|
@@ -887,8 +893,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
887
893
|
});
|
|
888
894
|
}
|
|
889
895
|
}
|
|
890
|
-
const unchanged = prepared.every((entry) => existsSync(entry.path) &&
|
|
891
|
-
readFileSync(entry.path, "utf8") === entry.content);
|
|
896
|
+
const unchanged = prepared.every((entry) => existsSync(entry.path) && readFileSync(entry.path, "utf8") === entry.content);
|
|
892
897
|
if (unchanged &&
|
|
893
898
|
currentConfig.providers[provider] !== undefined) {
|
|
894
899
|
return;
|
|
@@ -1023,8 +1028,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1023
1028
|
!Array.isArray(raw.providers)
|
|
1024
1029
|
? { ...raw.providers }
|
|
1025
1030
|
: {};
|
|
1026
|
-
const disableProvider = !hasRemainingAccount &&
|
|
1027
|
-
currentConfig.providers[nativeKind] !== undefined;
|
|
1031
|
+
const disableProvider = !hasRemainingAccount && currentConfig.providers[nativeKind] !== undefined;
|
|
1028
1032
|
if (disableProvider) {
|
|
1029
1033
|
for (const providerKey of nativeKind === "claude-code"
|
|
1030
1034
|
? ["claude-code", "claudeCode", "claude"]
|
|
@@ -1037,12 +1041,8 @@ export async function startRouteKitDaemon(options) {
|
|
|
1037
1041
|
delete raw.defaultModel;
|
|
1038
1042
|
}
|
|
1039
1043
|
}
|
|
1040
|
-
const nextDocument = disableProvider
|
|
1041
|
-
|
|
1042
|
-
: currentDocument;
|
|
1043
|
-
const nextConfig = disableProvider
|
|
1044
|
-
? parseConfigDocument(nextDocument)
|
|
1045
|
-
: currentConfig;
|
|
1044
|
+
const nextDocument = disableProvider ? stringifyYaml(raw) : currentDocument;
|
|
1045
|
+
const nextConfig = disableProvider ? parseConfigDocument(nextDocument) : currentConfig;
|
|
1046
1046
|
const activityPath = join(home, "usage", "account-activity.v1.json");
|
|
1047
1047
|
const transaction = prepareAccountTransaction({
|
|
1048
1048
|
home,
|
|
@@ -1054,7 +1054,9 @@ export async function startRouteKitDaemon(options) {
|
|
|
1054
1054
|
labels: [params.label]
|
|
1055
1055
|
});
|
|
1056
1056
|
try {
|
|
1057
|
-
const result = removeSubscriptionAccount(nativeKind, params.label, {
|
|
1057
|
+
const result = removeSubscriptionAccount(nativeKind, params.label, {
|
|
1058
|
+
accountsDirectory: activeNativeDirectory
|
|
1059
|
+
});
|
|
1058
1060
|
removed = result.removed;
|
|
1059
1061
|
if (!result.removed) {
|
|
1060
1062
|
cleanupAccountTransaction(transaction);
|
|
@@ -1252,9 +1254,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1252
1254
|
label: result.label,
|
|
1253
1255
|
redeemRequestId: result.redeemRequestId,
|
|
1254
1256
|
...(result.creditId !== undefined ? { creditId: result.creditId } : {}),
|
|
1255
|
-
...(result.windowsReset !== undefined
|
|
1256
|
-
? { windowsReset: result.windowsReset }
|
|
1257
|
-
: {}),
|
|
1257
|
+
...(result.windowsReset !== undefined ? { windowsReset: result.windowsReset } : {}),
|
|
1258
1258
|
usage: result.usage
|
|
1259
1259
|
};
|
|
1260
1260
|
}
|
|
@@ -1289,9 +1289,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1289
1289
|
const missingProviders = [
|
|
1290
1290
|
...new Set(accounts
|
|
1291
1291
|
.filter((entry) => {
|
|
1292
|
-
const provider = entry.connector === "cliproxy"
|
|
1293
|
-
? "cliproxy"
|
|
1294
|
-
: entry.subscriptionKind;
|
|
1292
|
+
const provider = entry.connector === "cliproxy" ? "cliproxy" : entry.subscriptionKind;
|
|
1295
1293
|
return currentConfig.providers[provider] === undefined;
|
|
1296
1294
|
})
|
|
1297
1295
|
.map((entry) => entry.subscriptionKind))
|
|
@@ -1364,7 +1362,9 @@ export async function startRouteKitDaemon(options) {
|
|
|
1364
1362
|
if (model === undefined || !listed.models.some((entry) => entry.id === model)) {
|
|
1365
1363
|
throw new ControlError({
|
|
1366
1364
|
code: "not_found",
|
|
1367
|
-
message: params.model === undefined
|
|
1365
|
+
message: params.model === undefined
|
|
1366
|
+
? "no model is available"
|
|
1367
|
+
: `unknown model: ${params.model}`
|
|
1368
1368
|
});
|
|
1369
1369
|
}
|
|
1370
1370
|
return {
|
|
@@ -1381,9 +1381,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1381
1381
|
label: params.label,
|
|
1382
1382
|
plane: params.plane,
|
|
1383
1383
|
role: "admin",
|
|
1384
|
-
createdBy: params.createdBy ??
|
|
1385
|
-
context.principal?.label ??
|
|
1386
|
-
"control"
|
|
1384
|
+
createdBy: params.createdBy ?? context.principal?.label ?? "control"
|
|
1387
1385
|
});
|
|
1388
1386
|
if (issued.plane === "data") {
|
|
1389
1387
|
dataTokenCache.set(issued.label, issued.token);
|
package/dist/leaderboard.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type LeaderboardSort = RouteKitLeaderboard["sort"];
|
|
|
5
5
|
export type LeaderboardWindow = NonNullable<RouteKitControlParams["calls.leaderboard"]["window"]>;
|
|
6
6
|
export declare const LEADERBOARD_ROLLUP_VERSION: 1;
|
|
7
7
|
export declare const LEADERBOARD_ROLLUP_RELATIVE_PATH: string;
|
|
8
|
+
export declare function defaultLeaderboardWindow(config: Pick<LeaderboardConfig, "durable" | "durableRetentionDays">): LeaderboardWindow;
|
|
8
9
|
export declare function aggregateInspections(inspections: readonly RouteKitCallInspection[], options: {
|
|
9
10
|
by: LeaderboardDimension;
|
|
10
11
|
sort: LeaderboardSort;
|
package/dist/leaderboard.js
CHANGED
|
@@ -7,6 +7,11 @@ import { dirname, join } from "node:path";
|
|
|
7
7
|
import { writeFileAtomic } from "@velum-labs/routekit-runtime";
|
|
8
8
|
export const LEADERBOARD_ROLLUP_VERSION = 1;
|
|
9
9
|
export const LEADERBOARD_ROLLUP_RELATIVE_PATH = join("usage", "leaderboard-rollups.v1.json");
|
|
10
|
+
export function defaultLeaderboardWindow(config) {
|
|
11
|
+
if (!config.durable)
|
|
12
|
+
return "live";
|
|
13
|
+
return config.durableRetentionDays >= 7 ? "7d" : "24h";
|
|
14
|
+
}
|
|
10
15
|
const WINDOW_MS = {
|
|
11
16
|
"1h": 60 * 60 * 1_000,
|
|
12
17
|
"24h": 24 * 60 * 60 * 1_000,
|
package/dist/test/daemon.test.js
CHANGED
|
@@ -8,8 +8,8 @@ import { CLIPROXY_PINNED_VERSION } from "@velum-labs/routekit-accounts";
|
|
|
8
8
|
import { RouteKitControlClient } from "@velum-labs/routekit-control";
|
|
9
9
|
import { ControlClient, ControlError, createServiceRecordStore } from "@velum-labs/routekit-runtime";
|
|
10
10
|
import { parse as parseYaml } from "yaml";
|
|
11
|
-
import { startRouteKitDaemon } from "../index.js";
|
|
12
11
|
import { prepareAccountTransaction } from "../account-transaction.js";
|
|
12
|
+
import { startRouteKitDaemon } from "../index.js";
|
|
13
13
|
async function mockProvider() {
|
|
14
14
|
const server = createServer((req, res) => {
|
|
15
15
|
if (req.url === "/v1/models") {
|
|
@@ -58,7 +58,9 @@ async function withMockAnthropicDiscovery(run) {
|
|
|
58
58
|
globalThis.fetch = async (input, init) => {
|
|
59
59
|
const url = new URL(input instanceof Request ? input.url : input.toString());
|
|
60
60
|
if (url.hostname === "api.anthropic.com" && url.pathname === "/v1/models") {
|
|
61
|
-
return new Response(JSON.stringify({ data: [{ id: "claude-test-model", type: "model" }] }), {
|
|
61
|
+
return new Response(JSON.stringify({ data: [{ id: "claude-test-model", type: "model" }] }), {
|
|
62
|
+
headers: { "content-type": "application/json" }
|
|
63
|
+
});
|
|
62
64
|
}
|
|
63
65
|
return await originalFetch(input, init);
|
|
64
66
|
};
|
|
@@ -69,7 +71,7 @@ async function withMockAnthropicDiscovery(run) {
|
|
|
69
71
|
globalThis.fetch = originalFetch;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
async function withMockNativeDiscovery(kind, run) {
|
|
74
|
+
async function withMockNativeDiscovery(kind, run, options = {}) {
|
|
73
75
|
if (kind === "claude-code")
|
|
74
76
|
return await withMockAnthropicDiscovery(run);
|
|
75
77
|
const originalFetch = globalThis.fetch;
|
|
@@ -80,6 +82,16 @@ async function withMockNativeDiscovery(kind, run) {
|
|
|
80
82
|
url.pathname.endsWith("/models")) {
|
|
81
83
|
return Response.json({ models: [{ slug: "gpt-test-model" }] });
|
|
82
84
|
}
|
|
85
|
+
if (options.healthyUsage === true &&
|
|
86
|
+
url.hostname === "chatgpt.com" &&
|
|
87
|
+
url.pathname === "/backend-api/wham/usage") {
|
|
88
|
+
return Response.json({
|
|
89
|
+
plan_type: "plus",
|
|
90
|
+
rate_limit: {
|
|
91
|
+
primary_window: { used_percent: 10, reset_at: Date.now() / 1000 + 3_600 }
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
83
95
|
return await originalFetch(input, init);
|
|
84
96
|
};
|
|
85
97
|
try {
|
|
@@ -473,6 +485,69 @@ test("daemon account activity persists last selection independently of leaderboa
|
|
|
473
485
|
rmSync(root, { recursive: true, force: true });
|
|
474
486
|
}
|
|
475
487
|
});
|
|
488
|
+
test("cleared persisted cooldown remains absent and eligible after daemon reload", async () => {
|
|
489
|
+
const root = mkdtempSync(join(tmpdir(), "routekit-daemon-cooldown-reload-"));
|
|
490
|
+
const stateHome = join(root, "state");
|
|
491
|
+
const configPath = join(root, "router.yaml");
|
|
492
|
+
const accountsDirectory = join(stateHome, "subscriptions", "codex");
|
|
493
|
+
const statePath = join(accountsDirectory, ".state.json");
|
|
494
|
+
const coolingUntil = Date.now() / 1000 + 3_600;
|
|
495
|
+
mkdirSync(accountsDirectory, { recursive: true });
|
|
496
|
+
writeFileSync(join(accountsDirectory, "work.json"), `${JSON.stringify(nativeCredential("codex"))}\n`);
|
|
497
|
+
writeFileSync(statePath, JSON.stringify({ members: [{ id: "work", coolingUntil }] }));
|
|
498
|
+
writeFileSync(configPath, "providers:\n codex: {}\ndefaultModel: codex/gpt-test-model\n");
|
|
499
|
+
try {
|
|
500
|
+
await withMockNativeDiscovery("codex", async () => {
|
|
501
|
+
const daemon = await startRouteKitDaemon({
|
|
502
|
+
packageVersion: "1.2.3",
|
|
503
|
+
stateHome,
|
|
504
|
+
configPath,
|
|
505
|
+
port: 0,
|
|
506
|
+
portless: false,
|
|
507
|
+
env: {
|
|
508
|
+
HOME: root,
|
|
509
|
+
ROUTEKIT_HOME: stateHome,
|
|
510
|
+
ROUTEKIT_PORTLESS: "0"
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
try {
|
|
514
|
+
const client = new RouteKitControlClient({
|
|
515
|
+
url: daemon.record.url,
|
|
516
|
+
token: daemon.record.controlToken
|
|
517
|
+
});
|
|
518
|
+
const before = await client.call("accounts.status", {});
|
|
519
|
+
assert.equal(before.accounts[0]?.relayOpen, false);
|
|
520
|
+
assert.deepEqual(before.accounts[0]?.readinessReasons, [
|
|
521
|
+
{ code: "cooldown_active", until: coolingUntil }
|
|
522
|
+
]);
|
|
523
|
+
const usage = await client.call("accounts.usage", {});
|
|
524
|
+
const recovered = usage.accountSets[0]?.members[0];
|
|
525
|
+
assert.equal(recovered?.coolingUntil, undefined);
|
|
526
|
+
assert.equal(recovered?.poolEligible, true);
|
|
527
|
+
assert.equal(recovered?.relayReady, true);
|
|
528
|
+
assert.deepEqual(recovered?.readinessReasons, []);
|
|
529
|
+
const persisted = JSON.parse(readFileSync(statePath, "utf8"));
|
|
530
|
+
assert.equal(persisted.members[0]?.coolingUntil, undefined);
|
|
531
|
+
assert.equal(persisted.members[0]?.cooldownRevision, 2);
|
|
532
|
+
const daemonStatus = await client.call("daemon.status", {});
|
|
533
|
+
await client.call("daemon.reload", { expectedRevision: daemonStatus.configRevision });
|
|
534
|
+
const afterReload = await client.call("accounts.status", {});
|
|
535
|
+
assert.equal(afterReload.accounts[0]?.relayOpen, true);
|
|
536
|
+
assert.deepEqual(afterReload.accounts[0]?.readinessReasons, []);
|
|
537
|
+
const afterUsage = await client.call("accounts.usage", {});
|
|
538
|
+
assert.equal(afterUsage.accountSets[0]?.members[0]?.coolingUntil, undefined);
|
|
539
|
+
assert.equal(afterUsage.accountSets[0]?.members[0]?.poolEligible, true);
|
|
540
|
+
assert.equal(JSON.parse(readFileSync(statePath, "utf8")).members[0]?.coolingUntil, undefined);
|
|
541
|
+
}
|
|
542
|
+
finally {
|
|
543
|
+
await daemon.close();
|
|
544
|
+
}
|
|
545
|
+
}, { healthyUsage: true });
|
|
546
|
+
}
|
|
547
|
+
finally {
|
|
548
|
+
rmSync(root, { recursive: true, force: true });
|
|
549
|
+
}
|
|
550
|
+
});
|
|
476
551
|
for (const kind of ["claude-code", "codex"]) {
|
|
477
552
|
test(`native ${kind} account rename preserves routing and usage state`, async () => {
|
|
478
553
|
const root = mkdtempSync(join(tmpdir(), `routekit-daemon-rename-${kind}-`));
|
|
@@ -763,12 +838,7 @@ test("native removal failure before deletion cleans its prepared transaction", a
|
|
|
763
838
|
mode: 0o600
|
|
764
839
|
});
|
|
765
840
|
symlinkSync(externalPath, accountPath);
|
|
766
|
-
writeFileSync(configPath, [
|
|
767
|
-
"providers:",
|
|
768
|
-
" openai: {}",
|
|
769
|
-
"defaultModel: openai/mock-model",
|
|
770
|
-
""
|
|
771
|
-
].join("\n"));
|
|
841
|
+
writeFileSync(configPath, ["providers:", " openai: {}", "defaultModel: openai/mock-model", ""].join("\n"));
|
|
772
842
|
const upstream = await mockProvider();
|
|
773
843
|
const daemon = await startRouteKitDaemon({
|
|
774
844
|
packageVersion: "1.2.3",
|
|
@@ -1019,11 +1089,10 @@ test("daemon owns the cliproxy sidecar: spawn, restart, account routing, shutdow
|
|
|
1019
1089
|
assert.ok(await waitFor(() => readFileSync(markerPath, "utf8").trim().split("\n").length === 3, 10_000), "accounts.sync did not restart the managed sidecar");
|
|
1020
1090
|
assert.equal(processAlive(respawnedPid), false);
|
|
1021
1091
|
const refreshedStatus = await client.call("accounts.status", {});
|
|
1022
|
-
assert.equal(refreshedStatus.accounts.find((entry) => entry.label === "antigravity-user@example.com")
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
assert.equal(refreshedStatus.accounts.find((entry) => entry.label === "broken-account")
|
|
1026
|
-
?.credentialValid, false);
|
|
1092
|
+
assert.equal(refreshedStatus.accounts.find((entry) => entry.label === "antigravity-user@example.com")
|
|
1093
|
+
?.credentialValid, true);
|
|
1094
|
+
assert.equal(refreshedStatus.accounts.find((entry) => entry.label === "kimi-invalid")?.credentialValid, false);
|
|
1095
|
+
assert.equal(refreshedStatus.accounts.find((entry) => entry.label === "broken-account")?.credentialValid, false);
|
|
1027
1096
|
const syncedPid = Number(readFileSync(markerPath, "utf8").trim().split("\n")[2]);
|
|
1028
1097
|
// Unclassified/corrupt auth files remain removable using the kind shown
|
|
1029
1098
|
// by accounts.list rather than becoming stuck in the store.
|
|
@@ -1072,7 +1141,9 @@ test("daemon owns the cliproxy sidecar: spawn, restart, account routing, shutdow
|
|
|
1072
1141
|
}
|
|
1073
1142
|
]
|
|
1074
1143
|
};
|
|
1075
|
-
const activated = await client.call("accounts.enrollActivate", activationParams, {
|
|
1144
|
+
const activated = await client.call("accounts.enrollActivate", activationParams, {
|
|
1145
|
+
idempotencyKey: "activate-grok"
|
|
1146
|
+
});
|
|
1076
1147
|
assert.equal(activated.activated, true);
|
|
1077
1148
|
assert.equal(activated.configRevision, beforeActivation.configRevision + 1);
|
|
1078
1149
|
assert.equal(activated.accountRevision, beforeActivation.accountRevision + 1);
|
|
@@ -1081,11 +1152,14 @@ test("daemon owns the cliproxy sidecar: spawn, restart, account routing, shutdow
|
|
|
1081
1152
|
assert.equal(existsSync(join(stateHome, "account-transactions")), false);
|
|
1082
1153
|
// A fresh transport retry converges on the committed state without
|
|
1083
1154
|
// incrementing either revision again.
|
|
1084
|
-
const replayed = await client.call("accounts.enrollActivate", activationParams, {
|
|
1155
|
+
const replayed = await client.call("accounts.enrollActivate", activationParams, {
|
|
1156
|
+
idempotencyKey: "activate-grok-retry"
|
|
1157
|
+
});
|
|
1085
1158
|
assert.equal(replayed.configRevision, activated.configRevision);
|
|
1086
1159
|
assert.equal(replayed.accountRevision, activated.accountRevision);
|
|
1087
1160
|
const activatedStatus = await client.call("accounts.status", {});
|
|
1088
|
-
assert.equal(activatedStatus.accounts.find((entry) => entry.label === "xai-transaction@example.com")
|
|
1161
|
+
assert.equal(activatedStatus.accounts.find((entry) => entry.label === "xai-transaction@example.com")
|
|
1162
|
+
?.configured, true);
|
|
1089
1163
|
const beforeClaude = await client.call("daemon.status", {});
|
|
1090
1164
|
const claudeActivation = {
|
|
1091
1165
|
kind: "claude-code",
|
|
@@ -1113,14 +1187,18 @@ test("daemon owns the cliproxy sidecar: spawn, restart, account routing, shutdow
|
|
|
1113
1187
|
assert.equal((await client.call("daemon.status", {})).configRevision, beforeClaude.configRevision);
|
|
1114
1188
|
assert.equal((await client.call("daemon.status", {})).accountRevision, beforeClaude.accountRevision);
|
|
1115
1189
|
await withMockAnthropicDiscovery(async () => {
|
|
1116
|
-
const claudeActivated = await client.call("accounts.enrollActivate", claudeActivation, {
|
|
1190
|
+
const claudeActivated = await client.call("accounts.enrollActivate", claudeActivation, {
|
|
1191
|
+
idempotencyKey: "activate-claude"
|
|
1192
|
+
});
|
|
1117
1193
|
assert.equal(claudeActivated.activated, true);
|
|
1118
1194
|
assert.equal(claudeActivated.configRevision, beforeClaude.configRevision + 1);
|
|
1119
1195
|
assert.equal(claudeActivated.accountRevision, beforeClaude.accountRevision + 1);
|
|
1120
1196
|
assert.equal(existsSync(claudePath), true);
|
|
1121
1197
|
assert.match(readFileSync(configPath, "utf8"), /claude-code:/);
|
|
1122
1198
|
assert.doesNotMatch(JSON.stringify(claudeActivated), /claude-transaction-access|claude-transaction-refresh/);
|
|
1123
|
-
const claudeReplay = await client.call("accounts.enrollActivate", claudeActivation, {
|
|
1199
|
+
const claudeReplay = await client.call("accounts.enrollActivate", claudeActivation, {
|
|
1200
|
+
idempotencyKey: "activate-claude-retry"
|
|
1201
|
+
});
|
|
1124
1202
|
assert.equal(claudeReplay.configRevision, claudeActivated.configRevision);
|
|
1125
1203
|
assert.equal(claudeReplay.accountRevision, claudeActivated.accountRevision);
|
|
1126
1204
|
assert.equal((await client.call("accounts.status", {})).accounts.find((entry) => entry.subscriptionKind === "claude-code" && entry.label === "claude-work")?.configured, true);
|
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
import { AccountActivityCoordinator } from "@velum-labs/routekit-accounts";
|
|
7
|
-
import { aggregateInspections, LeaderboardRollupStore } from "../leaderboard.js";
|
|
7
|
+
import { aggregateInspections, defaultLeaderboardWindow, LeaderboardRollupStore } from "../leaderboard.js";
|
|
8
8
|
function inspection(input) {
|
|
9
9
|
return {
|
|
10
10
|
callId: input.callId,
|
|
@@ -31,6 +31,12 @@ function inspection(input) {
|
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
test("defaultLeaderboardWindow uses the longest supported durable window", () => {
|
|
35
|
+
assert.equal(defaultLeaderboardWindow({ durable: false, durableRetentionDays: 14 }), "live");
|
|
36
|
+
assert.equal(defaultLeaderboardWindow({ durable: true, durableRetentionDays: 1 }), "24h");
|
|
37
|
+
assert.equal(defaultLeaderboardWindow({ durable: true, durableRetentionDays: 6 }), "24h");
|
|
38
|
+
assert.equal(defaultLeaderboardWindow({ durable: true, durableRetentionDays: 7 }), "7d");
|
|
39
|
+
});
|
|
34
40
|
test("aggregateInspections ranks principals by cost and keeps unknown cost visible", () => {
|
|
35
41
|
const result = aggregateInspections([
|
|
36
42
|
inspection({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velum-labs/routekit-daemon",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.16.
|
|
4
|
+
"version": "0.16.2",
|
|
5
5
|
"description": "Singleton RouteKit control daemon and stable model gateway.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"yaml": "2.9.0",
|
|
37
|
-
"@velum-labs/routekit-accounts": "0.16.
|
|
38
|
-
"@velum-labs/routekit-config": "0.16.
|
|
39
|
-
"@velum-labs/routekit-control": "0.16.
|
|
40
|
-
"@velum-labs/routekit-gateway": "0.16.
|
|
41
|
-
"@velum-labs/routekit-registry": "0.16.
|
|
42
|
-
"@velum-labs/routekit-router": "0.16.
|
|
43
|
-
"@velum-labs/routekit-runtime": "0.16.
|
|
44
|
-
"@velum-labs/routekit-telemetry-core": "0.16.
|
|
37
|
+
"@velum-labs/routekit-accounts": "0.16.2",
|
|
38
|
+
"@velum-labs/routekit-config": "0.16.2",
|
|
39
|
+
"@velum-labs/routekit-control": "0.16.2",
|
|
40
|
+
"@velum-labs/routekit-gateway": "0.16.2",
|
|
41
|
+
"@velum-labs/routekit-registry": "0.16.2",
|
|
42
|
+
"@velum-labs/routekit-router": "0.16.2",
|
|
43
|
+
"@velum-labs/routekit-runtime": "0.16.2",
|
|
44
|
+
"@velum-labs/routekit-telemetry-core": "0.16.2"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|