@velum-labs/routekit-daemon 0.11.0 → 0.13.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/call-attribution-store.js +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +146 -13
- package/package.json +9 -9
|
@@ -31,6 +31,9 @@ export function callInspection(modelCall) {
|
|
|
31
31
|
}
|
|
32
32
|
const account = record(attribution?.account);
|
|
33
33
|
const accountSeat = string(account?.seat);
|
|
34
|
+
const principal = record(attribution?.principal);
|
|
35
|
+
const principalTokenId = string(principal?.token_id);
|
|
36
|
+
const principalLabel = string(principal?.label);
|
|
34
37
|
const nativeModel = string(attribution?.native_model);
|
|
35
38
|
const estimateUsd = number(metadata?.cost_estimate_usd);
|
|
36
39
|
const attempts = number(attribution?.attempts) ?? 1;
|
|
@@ -44,6 +47,14 @@ export function callInspection(modelCall) {
|
|
|
44
47
|
provider,
|
|
45
48
|
billingMode,
|
|
46
49
|
...(accountSeat !== undefined ? { account: { seat: accountSeat } } : {}),
|
|
50
|
+
...(principalTokenId !== undefined
|
|
51
|
+
? {
|
|
52
|
+
principal: {
|
|
53
|
+
tokenId: principalTokenId,
|
|
54
|
+
...(principalLabel !== undefined ? { label: principalLabel } : {})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
47
58
|
retries: {
|
|
48
59
|
attempts,
|
|
49
60
|
total: retries,
|
package/dist/index.d.ts
CHANGED
|
@@ -23,4 +23,15 @@ export type RunningRouteKitDaemon = {
|
|
|
23
23
|
close(): Promise<void>;
|
|
24
24
|
reload(): Promise<void>;
|
|
25
25
|
};
|
|
26
|
+
export type DaemonPublicRecord = {
|
|
27
|
+
product: string;
|
|
28
|
+
kind: string;
|
|
29
|
+
url: string;
|
|
30
|
+
port: number;
|
|
31
|
+
generation: number;
|
|
32
|
+
protocolVersion: string;
|
|
33
|
+
dataUrl?: string;
|
|
34
|
+
dataPort?: number;
|
|
35
|
+
startedAt: string;
|
|
36
|
+
};
|
|
26
37
|
export declare function startRouteKitDaemon(options: RouteKitDaemonOptions): Promise<RunningRouteKitDaemon>;
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createRouteKitControlHandler, ROUTEKIT_CONTROL_CAPABILITY } from "@velu
|
|
|
14
14
|
import { startSwitchingGatewayProxy } from "@velum-labs/routekit-gateway";
|
|
15
15
|
import { PROVIDERS, accountKindForCliproxyAuthType, resolveAccountConnector } from "@velum-labs/routekit-registry";
|
|
16
16
|
import { startRouter } from "@velum-labs/routekit-router";
|
|
17
|
-
import { acquireLifecycleLock, CONTROL_PROTOCOL_VERSION, ControlClient, ControlError, createPortlessSession, createServiceRecordStore, extendCleanupGrace, generateControlToken, nextServiceGeneration, processIdentity, registerCleanup, startControlServer, supervisorFromEnv, writeFileAtomic } from "@velum-labs/routekit-runtime";
|
|
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";
|
|
18
18
|
import { createConsentManager } from "@velum-labs/routekit-telemetry-core";
|
|
19
19
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
20
20
|
import { createCliproxySidecar } from "./cliproxy-sidecar.js";
|
|
@@ -42,16 +42,59 @@ function redactedProcessArgs(args) {
|
|
|
42
42
|
}
|
|
43
43
|
return result;
|
|
44
44
|
}
|
|
45
|
-
function resolveDataToken(home, input) {
|
|
45
|
+
function resolveDataToken(home, input, tokens) {
|
|
46
46
|
const path = input.authTokenFile ?? dataTokenPath(home);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
const ensured = tokens.ensureOwnerDataToken({
|
|
48
|
+
...(input.authToken !== undefined ? { plaintext: input.authToken } : {}),
|
|
49
|
+
legacyPath: path
|
|
50
|
+
});
|
|
51
|
+
if (ensured.token.length === 0)
|
|
51
52
|
throw new Error("RouteKit data-plane token is empty");
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return { token: ensured.token, path };
|
|
54
|
+
}
|
|
55
|
+
function daemonPublicRecordPath(home) {
|
|
56
|
+
return join(home, "services", "daemon.public.json");
|
|
57
|
+
}
|
|
58
|
+
/** Publish a secret-free discovery file peers can read across OS accounts. */
|
|
59
|
+
function writeDaemonPublicRecord(home, record) {
|
|
60
|
+
const servicesDir = join(home, "services");
|
|
61
|
+
mkdirSync(home, { recursive: true, mode: SERVICE_HOME_MODE });
|
|
62
|
+
chmodSync(home, SERVICE_HOME_MODE);
|
|
63
|
+
mkdirSync(servicesDir, { recursive: true, mode: SERVICE_HOME_MODE });
|
|
64
|
+
chmodSync(servicesDir, SERVICE_HOME_MODE);
|
|
65
|
+
const path = daemonPublicRecordPath(home);
|
|
66
|
+
writeFileAtomic(path, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o644 });
|
|
67
|
+
chmodSync(path, 0o644);
|
|
68
|
+
}
|
|
69
|
+
function removeDaemonPublicRecord(home) {
|
|
70
|
+
rmSync(daemonPublicRecordPath(home), { force: true });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve (or mint) a data-plane token for the calling control principal so
|
|
74
|
+
* tool launchers attribute usage to that admin rather than the owner token.
|
|
75
|
+
* Plaintext is cached in-memory for the daemon lifetime.
|
|
76
|
+
*/
|
|
77
|
+
function dataTokenForPrincipal(tokens, cache, ownerToken, principal) {
|
|
78
|
+
if (principal === undefined || principal.role === "ephemeral" || principal.role === "owner") {
|
|
79
|
+
return ownerToken;
|
|
80
|
+
}
|
|
81
|
+
const label = `${principal.label}-data`;
|
|
82
|
+
const cached = cache.get(label);
|
|
83
|
+
if (cached !== undefined)
|
|
84
|
+
return cached;
|
|
85
|
+
const existing = tokens.findByLabel(label, "data");
|
|
86
|
+
if (existing !== undefined) {
|
|
87
|
+
// Registry has a hash but plaintext is gone after restart; rotate.
|
|
88
|
+
tokens.revoke(existing.id);
|
|
89
|
+
}
|
|
90
|
+
const issued = tokens.issue({
|
|
91
|
+
label,
|
|
92
|
+
plane: "data",
|
|
93
|
+
role: "admin",
|
|
94
|
+
createdBy: principal.label
|
|
95
|
+
});
|
|
96
|
+
cache.set(label, issued.token);
|
|
97
|
+
return issued.token;
|
|
55
98
|
}
|
|
56
99
|
function revisionPath(home) {
|
|
57
100
|
return join(home, "daemon-revisions.json");
|
|
@@ -200,7 +243,10 @@ export async function startRouteKitDaemon(options) {
|
|
|
200
243
|
const home = options.stateHome ?? routekitHome(env);
|
|
201
244
|
const configPath = options.configPath ?? globalRouterConfigPath();
|
|
202
245
|
const drainGraceMs = options.drainGraceMs ?? 30_000;
|
|
203
|
-
const
|
|
246
|
+
const tokens = createTokenStore(home);
|
|
247
|
+
const dataTokenCache = new Map();
|
|
248
|
+
const dataAuth = resolveDataToken(home, options, tokens);
|
|
249
|
+
dataTokenCache.set("default", dataAuth.token);
|
|
204
250
|
const store = createServiceRecordStore({ home, product: ROUTEKIT_PRODUCT });
|
|
205
251
|
// Held for the daemon's whole lifetime. Lifecycle clients use daemon.lock
|
|
206
252
|
// while this authority lock prevents any second daemon from becoming live.
|
|
@@ -302,7 +348,17 @@ export async function startRouteKitDaemon(options) {
|
|
|
302
348
|
target: activeRouter.url,
|
|
303
349
|
host: options.host ?? "127.0.0.1",
|
|
304
350
|
port: options.port ?? 8080,
|
|
305
|
-
authToken: dataAuth.token
|
|
351
|
+
authToken: dataAuth.token,
|
|
352
|
+
resolveDataPrincipal: (presented) => {
|
|
353
|
+
const principal = tokens.resolve(presented, "data");
|
|
354
|
+
if (principal === undefined)
|
|
355
|
+
return undefined;
|
|
356
|
+
return {
|
|
357
|
+
id: principal.id,
|
|
358
|
+
label: principal.label,
|
|
359
|
+
role: principal.role
|
|
360
|
+
};
|
|
361
|
+
}
|
|
306
362
|
});
|
|
307
363
|
portless = await createPortlessSession(options.portless ?? env.ROUTEKIT_PORTLESS !== "0", { project: "routekit", ownerLabel: "routekit-daemon", bareNames: [] });
|
|
308
364
|
const dataUrl = portless.enabled
|
|
@@ -1165,7 +1221,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1165
1221
|
]
|
|
1166
1222
|
};
|
|
1167
1223
|
},
|
|
1168
|
-
"launcher.prepare": async (params) => {
|
|
1224
|
+
"launcher.prepare": async (params, context) => {
|
|
1169
1225
|
const listed = await handlers["models.list"]({}, {
|
|
1170
1226
|
signal: new AbortController().signal,
|
|
1171
1227
|
requestId: "internal"
|
|
@@ -1181,9 +1237,63 @@ export async function startRouteKitDaemon(options) {
|
|
|
1181
1237
|
tool: params.tool,
|
|
1182
1238
|
model,
|
|
1183
1239
|
gatewayUrl: dataUrl,
|
|
1184
|
-
authToken: dataAuth.token,
|
|
1240
|
+
authToken: dataTokenForPrincipal(tokens, dataTokenCache, dataAuth.token, context.principal),
|
|
1185
1241
|
env: {}
|
|
1186
1242
|
};
|
|
1243
|
+
},
|
|
1244
|
+
"tokens.issue": async (params, context) => {
|
|
1245
|
+
try {
|
|
1246
|
+
const issued = tokens.issue({
|
|
1247
|
+
label: params.label,
|
|
1248
|
+
plane: params.plane,
|
|
1249
|
+
role: "admin",
|
|
1250
|
+
createdBy: params.createdBy ??
|
|
1251
|
+
context.principal?.label ??
|
|
1252
|
+
"control"
|
|
1253
|
+
});
|
|
1254
|
+
if (issued.plane === "data") {
|
|
1255
|
+
dataTokenCache.set(issued.label, issued.token);
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
id: issued.id,
|
|
1259
|
+
label: issued.label,
|
|
1260
|
+
plane: issued.plane,
|
|
1261
|
+
role: issued.role,
|
|
1262
|
+
token: issued.token,
|
|
1263
|
+
...(issued.plane === "control"
|
|
1264
|
+
? {
|
|
1265
|
+
joinToken: encodeJoinCredential({
|
|
1266
|
+
publicRecordPath: daemonPublicRecordPath(home),
|
|
1267
|
+
token: issued.token
|
|
1268
|
+
})
|
|
1269
|
+
}
|
|
1270
|
+
: {})
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
catch (error) {
|
|
1274
|
+
throw new ControlError({
|
|
1275
|
+
code: "bad_request",
|
|
1276
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
"tokens.list": async (params) => ({
|
|
1281
|
+
tokens: tokens.list(params.plane)
|
|
1282
|
+
}),
|
|
1283
|
+
"tokens.revoke": async (params) => {
|
|
1284
|
+
try {
|
|
1285
|
+
const revoked = tokens.revoke(params.id);
|
|
1286
|
+
if (revoked.plane === "data")
|
|
1287
|
+
dataTokenCache.delete(revoked.label);
|
|
1288
|
+
return revoked;
|
|
1289
|
+
}
|
|
1290
|
+
catch (error) {
|
|
1291
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1292
|
+
throw new ControlError({
|
|
1293
|
+
code: message.startsWith("unknown token") ? "not_found" : "bad_request",
|
|
1294
|
+
message
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1187
1297
|
}
|
|
1188
1298
|
};
|
|
1189
1299
|
control = await startControlServer({
|
|
@@ -1192,6 +1302,16 @@ export async function startRouteKitDaemon(options) {
|
|
|
1192
1302
|
product: ROUTEKIT_PRODUCT,
|
|
1193
1303
|
packageVersion: options.packageVersion,
|
|
1194
1304
|
capabilities: [ROUTEKIT_CONTROL_CAPABILITY],
|
|
1305
|
+
authorize: (presented) => {
|
|
1306
|
+
const principal = tokens.resolve(presented, "control");
|
|
1307
|
+
if (principal === undefined)
|
|
1308
|
+
return undefined;
|
|
1309
|
+
return {
|
|
1310
|
+
id: principal.id,
|
|
1311
|
+
label: principal.label,
|
|
1312
|
+
role: principal.role
|
|
1313
|
+
};
|
|
1314
|
+
},
|
|
1195
1315
|
onError: (error, context) => {
|
|
1196
1316
|
const operation = context.method ?? "control transport";
|
|
1197
1317
|
console.error(`RouteKit ${operation} failed (request ${context.requestId}):`, error);
|
|
@@ -1221,6 +1341,17 @@ export async function startRouteKitDaemon(options) {
|
|
|
1221
1341
|
args: redactedProcessArgs(process.argv.slice(2)),
|
|
1222
1342
|
cwd: process.cwd()
|
|
1223
1343
|
});
|
|
1344
|
+
writeDaemonPublicRecord(home, {
|
|
1345
|
+
product: ROUTEKIT_PRODUCT,
|
|
1346
|
+
kind: ROUTEKIT_DAEMON_KIND,
|
|
1347
|
+
url: control.url,
|
|
1348
|
+
port: control.port,
|
|
1349
|
+
generation,
|
|
1350
|
+
protocolVersion: CONTROL_PROTOCOL_VERSION,
|
|
1351
|
+
dataUrl,
|
|
1352
|
+
dataPort: proxy.port(),
|
|
1353
|
+
startedAt
|
|
1354
|
+
});
|
|
1224
1355
|
extendCleanupGrace(drainGraceMs + 10_000);
|
|
1225
1356
|
let closeRun;
|
|
1226
1357
|
const close = () => {
|
|
@@ -1238,6 +1369,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1238
1369
|
if (portless?.enabled)
|
|
1239
1370
|
portless.unregister("gateway");
|
|
1240
1371
|
store.remove(ROUTEKIT_DAEMON_KIND, { ifPid: process.pid });
|
|
1372
|
+
removeDaemonPublicRecord(home);
|
|
1241
1373
|
authority.release();
|
|
1242
1374
|
lifecycle = "closed";
|
|
1243
1375
|
})();
|
|
@@ -1274,6 +1406,7 @@ export async function startRouteKitDaemon(options) {
|
|
|
1274
1406
|
portless.unregister("gateway");
|
|
1275
1407
|
if (record !== undefined)
|
|
1276
1408
|
store.remove(ROUTEKIT_DAEMON_KIND, { ifPid: process.pid });
|
|
1409
|
+
removeDaemonPublicRecord(home);
|
|
1277
1410
|
authority.release();
|
|
1278
1411
|
throw error;
|
|
1279
1412
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velum-labs/routekit-daemon",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
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-
|
|
38
|
-
"@velum-labs/routekit-gateway": "0.
|
|
39
|
-
"@velum-labs/routekit-
|
|
40
|
-
"@velum-labs/routekit-
|
|
41
|
-
"@velum-labs/routekit-
|
|
42
|
-
"@velum-labs/routekit-
|
|
43
|
-
"@velum-labs/routekit-
|
|
44
|
-
"@velum-labs/routekit-
|
|
37
|
+
"@velum-labs/routekit-control": "0.13.0",
|
|
38
|
+
"@velum-labs/routekit-gateway": "0.13.0",
|
|
39
|
+
"@velum-labs/routekit-registry": "0.13.0",
|
|
40
|
+
"@velum-labs/routekit-router": "0.13.0",
|
|
41
|
+
"@velum-labs/routekit-config": "0.13.0",
|
|
42
|
+
"@velum-labs/routekit-accounts": "0.13.0",
|
|
43
|
+
"@velum-labs/routekit-telemetry-core": "0.13.0",
|
|
44
|
+
"@velum-labs/routekit-runtime": "0.13.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|