@byok-sdk/keys 0.1.0 → 0.2.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/README.md +34 -15
- package/dist/bin/pi-provider-launcher.d.ts +2 -0
- package/dist/bin/pi-provider-launcher.js +1137 -0
- package/dist/bin/pi-provider-launcher.js.map +1 -0
- package/dist/errors.d.ts +4 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +340 -40
- package/dist/index.js.map +1 -1
- package/dist/pi-provider-launcher-core.d.ts +34 -0
- package/dist/pi-provider-projection.d.ts +11 -0
- package/dist/profile-store.d.ts +16 -16
- package/dist/registry.d.ts +1 -1
- package/dist/sqlite-profile-store.d.ts +11 -9
- package/dist/sqlite-support.d.ts +11 -3
- package/dist/truth-profile-store.d.ts +27 -0
- package/package.json +14 -7
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createHash } from 'crypto';
|
|
|
4
4
|
import { mkdirSync, existsSync, chmodSync } from 'fs';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
6
|
import { dirname } from 'path';
|
|
7
|
+
import { isCoreConflictError, contentHash } from '@byok-sdk/core';
|
|
7
8
|
|
|
8
9
|
// src/errors.ts
|
|
9
10
|
var ByokKeysError = class extends Error {
|
|
@@ -38,13 +39,16 @@ var BYOK_KEYS_ERROR_CODES = {
|
|
|
38
39
|
MODEL_RESPONSE_INVALID: "MODEL_RESPONSE_INVALID",
|
|
39
40
|
PROVIDER_NOT_CONFIGURED: "PROVIDER_NOT_CONFIGURED",
|
|
40
41
|
PROVIDER_PROFILE_INVALID: "PROVIDER_PROFILE_INVALID",
|
|
42
|
+
PROVIDER_PROFILE_CONFLICT: "PROVIDER_PROFILE_CONFLICT",
|
|
41
43
|
PROVIDER_REQUEST_TIMEOUT: "PROVIDER_REQUEST_TIMEOUT",
|
|
42
44
|
PROVIDER_RESPONSE_INVALID: "PROVIDER_RESPONSE_INVALID",
|
|
43
45
|
PROVIDER_RESPONSE_TOO_LARGE: "PROVIDER_RESPONSE_TOO_LARGE",
|
|
44
46
|
PROVIDER_SECRET_EMPTY: "PROVIDER_SECRET_EMPTY",
|
|
45
47
|
PROVIDER_SECRET_MISSING: "PROVIDER_SECRET_MISSING",
|
|
46
48
|
PROVIDER_SECRET_NOT_ALLOWED: "PROVIDER_SECRET_NOT_ALLOWED",
|
|
49
|
+
PROVIDER_SECRET_ROLLBACK_FAILED: "PROVIDER_SECRET_ROLLBACK_FAILED",
|
|
47
50
|
PROVIDER_STORE_UNAVAILABLE: "PROVIDER_STORE_UNAVAILABLE",
|
|
51
|
+
PROVIDER_TRUTH_INVALID: "PROVIDER_TRUTH_INVALID",
|
|
48
52
|
PROVIDER_URL_INVALID: "PROVIDER_URL_INVALID",
|
|
49
53
|
SECRET_ENVELOPE_INVALID: "SECRET_ENVELOPE_INVALID",
|
|
50
54
|
SECRET_NAME_INVALID: "SECRET_NAME_INVALID",
|
|
@@ -1135,24 +1139,24 @@ function providerNotConfigured(providerId) {
|
|
|
1135
1139
|
}
|
|
1136
1140
|
var InMemoryProviderProfileStore = class {
|
|
1137
1141
|
#profiles = /* @__PURE__ */ new Map();
|
|
1138
|
-
close() {
|
|
1142
|
+
async close() {
|
|
1139
1143
|
this.#profiles.clear();
|
|
1140
1144
|
}
|
|
1141
|
-
delete(providerId) {
|
|
1145
|
+
async delete(providerId) {
|
|
1142
1146
|
return this.#profiles.delete(providerId);
|
|
1143
1147
|
}
|
|
1144
|
-
get(providerId) {
|
|
1148
|
+
async get(providerId) {
|
|
1145
1149
|
return this.#profiles.get(providerId);
|
|
1146
1150
|
}
|
|
1147
|
-
getEnabled() {
|
|
1148
|
-
return this.
|
|
1151
|
+
async getEnabled() {
|
|
1152
|
+
return [...this.#profiles.values()].find((profile) => profile.enabled);
|
|
1149
1153
|
}
|
|
1150
|
-
list() {
|
|
1154
|
+
async list() {
|
|
1151
1155
|
return [...this.#profiles.values()].sort(
|
|
1152
1156
|
(left, right) => left.provider_id.localeCompare(right.provider_id)
|
|
1153
1157
|
);
|
|
1154
1158
|
}
|
|
1155
|
-
save(profile) {
|
|
1159
|
+
async save(profile) {
|
|
1156
1160
|
const validated = parseModelProviderProfile({
|
|
1157
1161
|
...profile,
|
|
1158
1162
|
created_at: this.#profiles.get(profile.provider_id)?.created_at ?? profile.created_at
|
|
@@ -1167,12 +1171,20 @@ var InMemoryProviderProfileStore = class {
|
|
|
1167
1171
|
this.#profiles.set(validated.provider_id, validated);
|
|
1168
1172
|
return validated;
|
|
1169
1173
|
}
|
|
1170
|
-
setEnabled(providerId) {
|
|
1174
|
+
async setEnabled(providerId) {
|
|
1171
1175
|
const existing = this.#profiles.get(providerId);
|
|
1172
1176
|
if (existing === void 0) throw providerNotConfigured(providerId);
|
|
1173
1177
|
return this.save({ ...existing, enabled: true });
|
|
1174
1178
|
}
|
|
1175
1179
|
};
|
|
1180
|
+
function closeSqliteDatabaseAfterInitializationFailure(database, initializationError, message, close = (handle) => handle.close()) {
|
|
1181
|
+
try {
|
|
1182
|
+
close(database);
|
|
1183
|
+
} catch (closeError) {
|
|
1184
|
+
throw new AggregateError([initializationError, closeError], message);
|
|
1185
|
+
}
|
|
1186
|
+
throw initializationError;
|
|
1187
|
+
}
|
|
1176
1188
|
var SECURE_DIR_MODE = 448;
|
|
1177
1189
|
var SECURE_FILE_MODE = 384;
|
|
1178
1190
|
var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
|
|
@@ -1195,20 +1207,33 @@ function isSqliteAvailable() {
|
|
|
1195
1207
|
return false;
|
|
1196
1208
|
}
|
|
1197
1209
|
}
|
|
1198
|
-
function openSqliteDatabase(path, options) {
|
|
1210
|
+
function openSqliteDatabase(path, options, faults) {
|
|
1199
1211
|
const { DatabaseSync } = loadSqliteModule();
|
|
1200
|
-
|
|
1212
|
+
const readOnly = options?.readOnly === true;
|
|
1213
|
+
if (path !== ":memory:" && !readOnly) {
|
|
1201
1214
|
mkdirSync(dirname(path), { mode: SECURE_DIR_MODE, recursive: true });
|
|
1202
1215
|
}
|
|
1203
1216
|
const database = new DatabaseSync(path, {
|
|
1204
1217
|
timeout: DEFAULT_BUSY_TIMEOUT_MS,
|
|
1205
1218
|
...options
|
|
1206
1219
|
});
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1220
|
+
try {
|
|
1221
|
+
faults?.onStep?.("after-open");
|
|
1222
|
+
if (path !== ":memory:" && !readOnly) {
|
|
1223
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
1224
|
+
faults?.onStep?.("after-wal");
|
|
1225
|
+
database.exec("PRAGMA synchronous = FULL");
|
|
1226
|
+
faults?.onStep?.("after-synchronous");
|
|
1227
|
+
}
|
|
1228
|
+
return database;
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
closeSqliteDatabaseAfterInitializationFailure(
|
|
1231
|
+
database,
|
|
1232
|
+
error,
|
|
1233
|
+
"provider profile SQLite open initialization failed and its native handle could not be closed",
|
|
1234
|
+
faults?.close
|
|
1235
|
+
);
|
|
1210
1236
|
}
|
|
1211
|
-
return database;
|
|
1212
1237
|
}
|
|
1213
1238
|
function secureSqliteFilePermissions(databasePath) {
|
|
1214
1239
|
if (databasePath === ":memory:") return;
|
|
@@ -1247,10 +1272,22 @@ var SqliteProviderProfileStore = class {
|
|
|
1247
1272
|
#database;
|
|
1248
1273
|
#closed = false;
|
|
1249
1274
|
constructor(options) {
|
|
1250
|
-
this.#database = openSqliteDatabase(options.path
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1275
|
+
this.#database = openSqliteDatabase(options.path, {
|
|
1276
|
+
readOnly: options.readOnly ?? false
|
|
1277
|
+
});
|
|
1278
|
+
if (!options.readOnly) {
|
|
1279
|
+
try {
|
|
1280
|
+
this.#database.exec(SCHEMA);
|
|
1281
|
+
this.#database.exec(ENABLED_INDEX);
|
|
1282
|
+
secureSqliteFilePermissions(options.path);
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
closeSqliteDatabaseAfterInitializationFailure(
|
|
1285
|
+
this.#database,
|
|
1286
|
+
error,
|
|
1287
|
+
"SqliteProviderProfileStore initialization failed and its native handle could not be closed"
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1254
1291
|
}
|
|
1255
1292
|
/**
|
|
1256
1293
|
* Idempotent, as {@link ProviderProfileStore.close} requires: `node:sqlite`
|
|
@@ -1258,31 +1295,32 @@ var SqliteProviderProfileStore = class {
|
|
|
1258
1295
|
* routinely closed both by the code that finished with it and by a test's
|
|
1259
1296
|
* teardown.
|
|
1260
1297
|
*/
|
|
1261
|
-
close() {
|
|
1298
|
+
async close() {
|
|
1262
1299
|
if (this.#closed) return;
|
|
1263
1300
|
this.#closed = true;
|
|
1264
1301
|
this.#database.close();
|
|
1265
1302
|
}
|
|
1266
|
-
delete(providerId) {
|
|
1303
|
+
async delete(providerId) {
|
|
1267
1304
|
const result = this.#database.prepare("DELETE FROM provider_profile WHERE provider_id = ?").run(providerId);
|
|
1268
1305
|
return Number(result.changes) === 1;
|
|
1269
1306
|
}
|
|
1270
|
-
get(providerId) {
|
|
1307
|
+
async get(providerId) {
|
|
1271
1308
|
const row = this.#database.prepare("SELECT * FROM provider_profile WHERE provider_id = ?").get(providerId);
|
|
1272
1309
|
return row === void 0 ? void 0 : parseRow(row);
|
|
1273
1310
|
}
|
|
1274
|
-
getEnabled() {
|
|
1311
|
+
async getEnabled() {
|
|
1275
1312
|
const row = this.#database.prepare("SELECT * FROM provider_profile WHERE enabled = 1").get();
|
|
1276
1313
|
return row === void 0 ? void 0 : parseRow(row);
|
|
1277
1314
|
}
|
|
1278
|
-
list() {
|
|
1315
|
+
async list() {
|
|
1279
1316
|
const rows = this.#database.prepare("SELECT * FROM provider_profile ORDER BY provider_id ASC").all();
|
|
1280
1317
|
return rows.map(parseRow);
|
|
1281
1318
|
}
|
|
1282
|
-
save(profile) {
|
|
1319
|
+
async save(profile) {
|
|
1320
|
+
const existing = await this.get(profile.provider_id);
|
|
1283
1321
|
const validated = parseModelProviderProfile({
|
|
1284
1322
|
...profile,
|
|
1285
|
-
created_at:
|
|
1323
|
+
created_at: existing?.created_at ?? profile.created_at
|
|
1286
1324
|
});
|
|
1287
1325
|
this.#transaction(() => {
|
|
1288
1326
|
if (validated.enabled) {
|
|
@@ -1316,10 +1354,10 @@ var SqliteProviderProfileStore = class {
|
|
|
1316
1354
|
validated.updated_at
|
|
1317
1355
|
);
|
|
1318
1356
|
});
|
|
1319
|
-
return this.get(validated.provider_id);
|
|
1357
|
+
return await this.get(validated.provider_id);
|
|
1320
1358
|
}
|
|
1321
|
-
setEnabled(providerId) {
|
|
1322
|
-
const existing = this.get(providerId);
|
|
1359
|
+
async setEnabled(providerId) {
|
|
1360
|
+
const existing = await this.get(providerId);
|
|
1323
1361
|
if (existing === void 0) throw providerNotConfigured(providerId);
|
|
1324
1362
|
return this.save({ ...existing, enabled: true });
|
|
1325
1363
|
}
|
|
@@ -1341,6 +1379,219 @@ function parseRow(row) {
|
|
|
1341
1379
|
enabled: row.enabled === 1
|
|
1342
1380
|
});
|
|
1343
1381
|
}
|
|
1382
|
+
var PROVIDER_PROFILE_TRUTH_RECORD_KEY = "byok-sdk.keys/model-provider-registry-v1";
|
|
1383
|
+
var PROFILE_KEYS = [
|
|
1384
|
+
"adapter",
|
|
1385
|
+
"auth_mode",
|
|
1386
|
+
"base_url",
|
|
1387
|
+
"created_at",
|
|
1388
|
+
"display_name",
|
|
1389
|
+
"enabled",
|
|
1390
|
+
"kind",
|
|
1391
|
+
"model",
|
|
1392
|
+
"provider_id",
|
|
1393
|
+
"updated_at"
|
|
1394
|
+
];
|
|
1395
|
+
var TruthStoreProviderProfileStore = class {
|
|
1396
|
+
#tenant;
|
|
1397
|
+
#truth;
|
|
1398
|
+
constructor(options) {
|
|
1399
|
+
this.#tenant = options.tenant;
|
|
1400
|
+
this.#truth = options.truthStore;
|
|
1401
|
+
}
|
|
1402
|
+
async close() {
|
|
1403
|
+
}
|
|
1404
|
+
async delete(providerId) {
|
|
1405
|
+
const current = await this.#load();
|
|
1406
|
+
if (!current.profiles.some((profile) => profile.provider_id === providerId)) {
|
|
1407
|
+
return false;
|
|
1408
|
+
}
|
|
1409
|
+
await this.#write(
|
|
1410
|
+
current.profiles.filter((profile) => profile.provider_id !== providerId),
|
|
1411
|
+
current.rev
|
|
1412
|
+
);
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
async get(providerId) {
|
|
1416
|
+
return (await this.#load()).profiles.find(
|
|
1417
|
+
(profile) => profile.provider_id === providerId
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
async getEnabled() {
|
|
1421
|
+
return (await this.#load()).profiles.find((profile) => profile.enabled);
|
|
1422
|
+
}
|
|
1423
|
+
async list() {
|
|
1424
|
+
return [...(await this.#load()).profiles];
|
|
1425
|
+
}
|
|
1426
|
+
async save(profile) {
|
|
1427
|
+
const current = await this.#load();
|
|
1428
|
+
const existing = current.profiles.find(
|
|
1429
|
+
(candidate) => candidate.provider_id === profile.provider_id
|
|
1430
|
+
);
|
|
1431
|
+
const validated = parseModelProviderProfile({
|
|
1432
|
+
...profile,
|
|
1433
|
+
created_at: existing?.created_at ?? profile.created_at
|
|
1434
|
+
});
|
|
1435
|
+
const next = current.profiles.filter((candidate) => candidate.provider_id !== validated.provider_id).map(
|
|
1436
|
+
(candidate) => validated.enabled && candidate.enabled ? { ...candidate, enabled: false } : candidate
|
|
1437
|
+
);
|
|
1438
|
+
next.push(validated);
|
|
1439
|
+
await this.#write(next, current.rev);
|
|
1440
|
+
return validated;
|
|
1441
|
+
}
|
|
1442
|
+
async setEnabled(providerId) {
|
|
1443
|
+
const current = await this.#load();
|
|
1444
|
+
const selected = current.profiles.find(
|
|
1445
|
+
(profile) => profile.provider_id === providerId
|
|
1446
|
+
);
|
|
1447
|
+
if (selected === void 0) throw providerNotConfigured(providerId);
|
|
1448
|
+
const next = current.profiles.map((profile) => ({
|
|
1449
|
+
...profile,
|
|
1450
|
+
enabled: profile.provider_id === providerId
|
|
1451
|
+
}));
|
|
1452
|
+
await this.#write(next, current.rev);
|
|
1453
|
+
return { ...selected, enabled: true };
|
|
1454
|
+
}
|
|
1455
|
+
async #load() {
|
|
1456
|
+
const record = await this.#truth.getRecord(this.#tenant, {
|
|
1457
|
+
kind: "profile",
|
|
1458
|
+
recordKey: PROVIDER_PROFILE_TRUTH_RECORD_KEY
|
|
1459
|
+
});
|
|
1460
|
+
if (record === void 0) return { profiles: [], rev: 0 };
|
|
1461
|
+
return decodeRegistryRecord(record, this.#tenant);
|
|
1462
|
+
}
|
|
1463
|
+
async #write(profiles, expectedRev) {
|
|
1464
|
+
const encoded = encodeRegistry(profiles);
|
|
1465
|
+
let written;
|
|
1466
|
+
try {
|
|
1467
|
+
written = await this.#truth.writeSnapshot(this.#tenant, {
|
|
1468
|
+
kind: "profile",
|
|
1469
|
+
recordKey: PROVIDER_PROFILE_TRUTH_RECORD_KEY,
|
|
1470
|
+
expectedRev,
|
|
1471
|
+
contentHash: hashBody(encoded),
|
|
1472
|
+
byteSize: BigInt(new TextEncoder().encode(encoded).byteLength),
|
|
1473
|
+
body: { kind: "inline", body: encoded },
|
|
1474
|
+
label: "BYOK model provider registry"
|
|
1475
|
+
});
|
|
1476
|
+
} catch (cause) {
|
|
1477
|
+
if (isCoreConflictError(cause, "truth_revision_conflict")) {
|
|
1478
|
+
throw new ByokKeysError(
|
|
1479
|
+
"PROVIDER_PROFILE_CONFLICT",
|
|
1480
|
+
"Provider profiles changed since this operation read them",
|
|
1481
|
+
{ cause }
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
throw cause;
|
|
1485
|
+
}
|
|
1486
|
+
if (written.body.kind !== "inline" || written.body.body !== encoded) {
|
|
1487
|
+
throw invalidTruth("TruthStore did not confirm the requested provider profile snapshot");
|
|
1488
|
+
}
|
|
1489
|
+
const confirmed = decodeRegistryRecord(written, this.#tenant);
|
|
1490
|
+
if (confirmed.rev !== expectedRev + 1) {
|
|
1491
|
+
throw invalidTruth("TruthStore confirmed an unexpected provider profile revision");
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
function encodeRegistry(profiles) {
|
|
1496
|
+
const normalized = profiles.map((profile) => parseModelProviderProfile(profile)).sort((left, right) => left.provider_id.localeCompare(right.provider_id));
|
|
1497
|
+
assertRegistryInvariants(normalized);
|
|
1498
|
+
const snapshot = {
|
|
1499
|
+
schema_version: 1,
|
|
1500
|
+
profiles: normalized.map((profile) => ({
|
|
1501
|
+
adapter: profile.adapter,
|
|
1502
|
+
auth_mode: profile.auth_mode,
|
|
1503
|
+
base_url: profile.base_url,
|
|
1504
|
+
created_at: profile.created_at,
|
|
1505
|
+
display_name: profile.display_name,
|
|
1506
|
+
enabled: profile.enabled,
|
|
1507
|
+
kind: profile.kind,
|
|
1508
|
+
model: profile.model,
|
|
1509
|
+
provider_id: profile.provider_id,
|
|
1510
|
+
updated_at: profile.updated_at
|
|
1511
|
+
}))
|
|
1512
|
+
};
|
|
1513
|
+
return JSON.stringify(snapshot);
|
|
1514
|
+
}
|
|
1515
|
+
function decodeRegistryRecord(record, tenant) {
|
|
1516
|
+
if (record.tenantId !== tenant || record.kind !== "profile" || record.recordKey !== PROVIDER_PROFILE_TRUTH_RECORD_KEY || !Number.isSafeInteger(record.rev) || record.rev < 1) {
|
|
1517
|
+
throw invalidTruth("TruthStore returned mismatched provider profile authority");
|
|
1518
|
+
}
|
|
1519
|
+
if (record.body.kind !== "inline") {
|
|
1520
|
+
throw invalidTruth("Provider profiles must use an inline TruthStore body");
|
|
1521
|
+
}
|
|
1522
|
+
const bytes = new TextEncoder().encode(record.body.body);
|
|
1523
|
+
if (record.byteSize !== BigInt(bytes.byteLength)) {
|
|
1524
|
+
throw invalidTruth("Provider profile TruthStore byte size does not match its body");
|
|
1525
|
+
}
|
|
1526
|
+
if (record.contentHash !== hashBody(record.body.body)) {
|
|
1527
|
+
throw invalidTruth("Provider profile TruthStore hash does not match its body");
|
|
1528
|
+
}
|
|
1529
|
+
let raw;
|
|
1530
|
+
try {
|
|
1531
|
+
raw = JSON.parse(record.body.body);
|
|
1532
|
+
} catch (cause) {
|
|
1533
|
+
throw invalidTruth("Provider profile TruthStore body is not valid JSON", cause);
|
|
1534
|
+
}
|
|
1535
|
+
if (!isPlainRecord(raw) || !hasExactKeys(raw, ["profiles", "schema_version"])) {
|
|
1536
|
+
throw invalidTruth("Provider profile TruthStore body has an unknown top-level field");
|
|
1537
|
+
}
|
|
1538
|
+
if (raw.schema_version !== 1 || !Array.isArray(raw.profiles)) {
|
|
1539
|
+
throw invalidTruth("Provider profile TruthStore body has an unsupported schema");
|
|
1540
|
+
}
|
|
1541
|
+
if (raw.profiles.length > MODEL_PROVIDER_IDS.length) {
|
|
1542
|
+
throw invalidTruth("Provider profile TruthStore body exceeds the provider registry bound");
|
|
1543
|
+
}
|
|
1544
|
+
let profiles;
|
|
1545
|
+
try {
|
|
1546
|
+
profiles = raw.profiles.map((candidate) => {
|
|
1547
|
+
if (!isPlainRecord(candidate) || !hasExactKeys(candidate, PROFILE_KEYS)) {
|
|
1548
|
+
throw invalidTruth("Provider profile TruthStore body contains an unknown profile field");
|
|
1549
|
+
}
|
|
1550
|
+
return parseModelProviderProfile(candidate);
|
|
1551
|
+
});
|
|
1552
|
+
} catch (cause) {
|
|
1553
|
+
if (cause instanceof ByokKeysError && cause.code === "PROVIDER_TRUTH_INVALID") {
|
|
1554
|
+
throw cause;
|
|
1555
|
+
}
|
|
1556
|
+
throw invalidTruth("Provider profile TruthStore body contains an invalid profile", cause);
|
|
1557
|
+
}
|
|
1558
|
+
assertRegistryInvariants(profiles);
|
|
1559
|
+
if (record.body.body !== encodeRegistry(profiles)) {
|
|
1560
|
+
throw invalidTruth("Provider profile TruthStore body is not in canonical form");
|
|
1561
|
+
}
|
|
1562
|
+
return { profiles, rev: record.rev };
|
|
1563
|
+
}
|
|
1564
|
+
function assertRegistryInvariants(profiles) {
|
|
1565
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1566
|
+
let enabled = 0;
|
|
1567
|
+
for (const profile of profiles) {
|
|
1568
|
+
if (seen.has(profile.provider_id)) {
|
|
1569
|
+
throw invalidTruth(`Provider profile ${profile.provider_id} appears more than once`);
|
|
1570
|
+
}
|
|
1571
|
+
seen.add(profile.provider_id);
|
|
1572
|
+
if (profile.enabled) enabled += 1;
|
|
1573
|
+
}
|
|
1574
|
+
if (enabled > 1) {
|
|
1575
|
+
throw invalidTruth("Provider profile TruthStore body enables more than one provider");
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
function hashBody(body) {
|
|
1579
|
+
const digest = createHash("sha256").update(body, "utf8").digest("hex");
|
|
1580
|
+
return contentHash(`sha256:${digest}`);
|
|
1581
|
+
}
|
|
1582
|
+
function isPlainRecord(value) {
|
|
1583
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1584
|
+
}
|
|
1585
|
+
function hasExactKeys(value, expected) {
|
|
1586
|
+
const keys = Object.keys(value).sort();
|
|
1587
|
+
const wanted = [...expected].sort();
|
|
1588
|
+
return keys.length === wanted.length && keys.every((key, index) => key === wanted[index]);
|
|
1589
|
+
}
|
|
1590
|
+
function invalidTruth(message, cause) {
|
|
1591
|
+
return new ByokKeysError("PROVIDER_TRUTH_INVALID", message, {
|
|
1592
|
+
...cause === void 0 ? {} : { cause }
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1344
1595
|
|
|
1345
1596
|
// src/registry.ts
|
|
1346
1597
|
var ProviderRegistry = class {
|
|
@@ -1354,8 +1605,8 @@ var ProviderRegistry = class {
|
|
|
1354
1605
|
this.#profiles = options.profileStore;
|
|
1355
1606
|
this.#secrets = options.secretStore;
|
|
1356
1607
|
}
|
|
1357
|
-
close() {
|
|
1358
|
-
this.#profiles.close();
|
|
1608
|
+
async close() {
|
|
1609
|
+
await this.#profiles.close();
|
|
1359
1610
|
}
|
|
1360
1611
|
/**
|
|
1361
1612
|
* Persist a provider's profile and, when supplied, its secret
|
|
@@ -1368,15 +1619,17 @@ var ProviderRegistry = class {
|
|
|
1368
1619
|
*/
|
|
1369
1620
|
async configure(configuration, secret) {
|
|
1370
1621
|
const timestamp = this.#now().toISOString();
|
|
1371
|
-
const previous = this.#profiles.get(configuration.provider_id);
|
|
1372
|
-
const profile = {
|
|
1622
|
+
const previous = await this.#profiles.get(configuration.provider_id);
|
|
1623
|
+
const profile = parseModelProviderProfile({
|
|
1373
1624
|
...configuration,
|
|
1374
1625
|
created_at: previous?.created_at ?? timestamp,
|
|
1375
1626
|
enabled: configuration.enabled ?? true,
|
|
1376
1627
|
kind: "model",
|
|
1377
1628
|
updated_at: timestamp
|
|
1378
|
-
};
|
|
1629
|
+
});
|
|
1379
1630
|
const secretName = modelProviderSecretName(configuration.provider_id);
|
|
1631
|
+
let previousSecret;
|
|
1632
|
+
let secretWritten = false;
|
|
1380
1633
|
if (configuration.auth_mode === "none" && secret !== void 0) {
|
|
1381
1634
|
throw new ByokKeysError(
|
|
1382
1635
|
"PROVIDER_SECRET_NOT_ALLOWED",
|
|
@@ -1390,7 +1643,9 @@ var ProviderRegistry = class {
|
|
|
1390
1643
|
"Provider secret cannot be empty"
|
|
1391
1644
|
);
|
|
1392
1645
|
}
|
|
1646
|
+
previousSecret = await this.#secrets.get(secretName);
|
|
1393
1647
|
await this.#secrets.set(secretName, secret);
|
|
1648
|
+
secretWritten = true;
|
|
1394
1649
|
}
|
|
1395
1650
|
if (configuration.auth_mode !== "none" && !await this.#secrets.has(secretName)) {
|
|
1396
1651
|
throw new ByokKeysError(
|
|
@@ -1398,7 +1653,27 @@ var ProviderRegistry = class {
|
|
|
1398
1653
|
"Provider authentication requires a secret in the operating-system credential store"
|
|
1399
1654
|
);
|
|
1400
1655
|
}
|
|
1401
|
-
|
|
1656
|
+
let saved;
|
|
1657
|
+
try {
|
|
1658
|
+
saved = await this.#profiles.save(profile);
|
|
1659
|
+
} catch (cause) {
|
|
1660
|
+
if (secretWritten) {
|
|
1661
|
+
try {
|
|
1662
|
+
if (previousSecret === void 0) {
|
|
1663
|
+
await this.#secrets.delete(secretName);
|
|
1664
|
+
} else {
|
|
1665
|
+
await this.#secrets.set(secretName, previousSecret);
|
|
1666
|
+
}
|
|
1667
|
+
} catch (rollbackCause) {
|
|
1668
|
+
throw new ByokKeysError(
|
|
1669
|
+
"PROVIDER_SECRET_ROLLBACK_FAILED",
|
|
1670
|
+
"Provider profile write failed and the previous secret could not be restored",
|
|
1671
|
+
{ cause: new AggregateError([cause, rollbackCause]) }
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
throw cause;
|
|
1676
|
+
}
|
|
1402
1677
|
if (saved.auth_mode === "none") {
|
|
1403
1678
|
await this.#secrets.delete(secretName);
|
|
1404
1679
|
}
|
|
@@ -1406,17 +1681,17 @@ var ProviderRegistry = class {
|
|
|
1406
1681
|
}
|
|
1407
1682
|
/** Remove a provider's profile and its secret together. */
|
|
1408
1683
|
async delete(providerId) {
|
|
1409
|
-
const removed = this.#profiles.delete(providerId);
|
|
1684
|
+
const removed = await this.#profiles.delete(providerId);
|
|
1410
1685
|
await this.#secrets.delete(modelProviderSecretName(providerId));
|
|
1411
1686
|
return removed;
|
|
1412
1687
|
}
|
|
1413
1688
|
async get(providerId) {
|
|
1414
|
-
const profile = this.#profiles.get(providerId);
|
|
1689
|
+
const profile = await this.#profiles.get(providerId);
|
|
1415
1690
|
return profile === void 0 ? void 0 : this.#status(profile);
|
|
1416
1691
|
}
|
|
1417
1692
|
async list() {
|
|
1418
1693
|
return Promise.all(
|
|
1419
|
-
this.#profiles.list().map((profile) => this.#status(profile))
|
|
1694
|
+
(await this.#profiles.list()).map((profile) => this.#status(profile))
|
|
1420
1695
|
);
|
|
1421
1696
|
}
|
|
1422
1697
|
/**
|
|
@@ -1427,7 +1702,7 @@ var ProviderRegistry = class {
|
|
|
1427
1702
|
* missing secret or an unusable profile is a fault, not an absence.
|
|
1428
1703
|
*/
|
|
1429
1704
|
async resolveDefaultModelProvider() {
|
|
1430
|
-
const profile = this.#profiles.getEnabled();
|
|
1705
|
+
const profile = await this.#profiles.getEnabled();
|
|
1431
1706
|
if (profile === void 0) return void 0;
|
|
1432
1707
|
const secret = await this.#secrets.get(
|
|
1433
1708
|
modelProviderSecretName(profile.provider_id)
|
|
@@ -1437,7 +1712,7 @@ var ProviderRegistry = class {
|
|
|
1437
1712
|
}
|
|
1438
1713
|
/** Switch which configured provider is the default. */
|
|
1439
1714
|
async setDefaultModelProvider(providerId) {
|
|
1440
|
-
return this.#status(this.#profiles.setEnabled(providerId));
|
|
1715
|
+
return this.#status(await this.#profiles.setEnabled(providerId));
|
|
1441
1716
|
}
|
|
1442
1717
|
async #status(profile) {
|
|
1443
1718
|
return {
|
|
@@ -1457,6 +1732,31 @@ var ProviderRegistry = class {
|
|
|
1457
1732
|
}
|
|
1458
1733
|
};
|
|
1459
1734
|
|
|
1460
|
-
|
|
1735
|
+
// src/pi-provider-projection.ts
|
|
1736
|
+
var PI_PROJECTED_KEY_ENV = "PI_PROVIDER_API_KEY";
|
|
1737
|
+
function piProjectionProviderId(profileProviderId) {
|
|
1738
|
+
return `byok-sdk-${profileProviderId}`;
|
|
1739
|
+
}
|
|
1740
|
+
function buildPiProviderProjection(profile) {
|
|
1741
|
+
const projectedProviderId = piProjectionProviderId(profile.provider_id);
|
|
1742
|
+
return {
|
|
1743
|
+
providers: {
|
|
1744
|
+
[projectedProviderId]: {
|
|
1745
|
+
baseUrl: profile.base_url,
|
|
1746
|
+
api: profile.adapter === "anthropic" ? "anthropic-messages" : "openai-completions",
|
|
1747
|
+
...profile.auth_mode === "none" ? {} : { apiKey: `$${PI_PROJECTED_KEY_ENV}` },
|
|
1748
|
+
...profile.auth_mode === "bearer" ? { authHeader: true } : {},
|
|
1749
|
+
models: [
|
|
1750
|
+
{
|
|
1751
|
+
id: profile.model,
|
|
1752
|
+
name: profile.display_name
|
|
1753
|
+
}
|
|
1754
|
+
]
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
export { AnthropicMessagesClient, BYOK_KEYS_ERROR_CODES, ByokKeysError, DEFAULT_KEYCHAIN_SECRET_STORAGE_PREFIX, DEFAULT_SECRET_ENVELOPE_PREFIX, DEFAULT_SECRET_SERVICE_PREFIX, EnvelopeScopedSecretStore, InMemoryProviderProfileStore, InMemorySecretStore, MODEL_PROVIDER_ADAPTERS, MODEL_PROVIDER_IDS, MODEL_PROVIDER_SECRET_NAMES, MacOsKeychainSecretStore, ModelProviderProfileSchema, OpenAiCompatibleChatClient, PI_PROJECTED_KEY_ENV, PROVIDER_AUTH_MODES, PROVIDER_PROFILE_TRUTH_RECORD_KEY, PROVIDER_RESPONSE_MAX_BYTES, PROVIDER_TIMEOUT_MS, ProviderRegistry, SECRET_NAMESPACE_PATTERN, SECRET_NAME_PATTERN, SqliteProviderProfileStore, TruthStoreProviderProfileStore, WindowsCredentialManagerSecretStore, anthropicMessageText, assertLiveModelResponse, assertSecretName, assertSecretNamespace, assertSharedSecretValue, buildPiProviderProjection, chatCompletionText, classifyModelProviderHttpError, decodeStrictBase64Utf8, fetchWithProviderGuards, isLoopbackHost, isLoopbackProviderUrl, isPrivateNetworkLiteral, isSqliteAvailable, loadSqliteModule, modelApiUrl, modelMessageText, modelProviderSecretName, normalizeProviderUrl, objectValue, openSqliteDatabase, parseBoundedJsonResponse, parseModelProviderProfile, providerHeaders, readModelProviderResponse, requiredProviderSecret, runCommand, scopeSecretStore, secretScopeId, secureSqliteFilePermissions };
|
|
1461
1761
|
//# sourceMappingURL=index.js.map
|
|
1462
1762
|
//# sourceMappingURL=index.js.map
|