omnigateway 0.1.1 → 0.1.3

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/bin/omni.js CHANGED
@@ -608,6 +608,7 @@ async function* decodeAnthropic(messages) {
608
608
  let cacheWriteTokens = 0;
609
609
  let outputTokens = 0;
610
610
  let stopReason = "endTurn";
611
+ let terminal = false;
611
612
  for await (const msg of messages) {
612
613
  const d = json(msg.data);
613
614
  if (d === null)
@@ -673,6 +674,7 @@ async function* decodeAnthropic(messages) {
673
674
  break;
674
675
  }
675
676
  case "message_stop":
677
+ terminal = true;
676
678
  yield {
677
679
  type: "end",
678
680
  stopReason,
@@ -680,6 +682,7 @@ async function* decodeAnthropic(messages) {
680
682
  };
681
683
  break;
682
684
  case "error": {
685
+ terminal = true;
683
686
  const code = ERROR_TYPE[String(d.error?.type)] ?? "UPSTREAM";
684
687
  yield {
685
688
  type: "error",
@@ -693,6 +696,14 @@ async function* decodeAnthropic(messages) {
693
696
  break;
694
697
  }
695
698
  }
699
+ if (!terminal) {
700
+ yield {
701
+ type: "error",
702
+ code: "UPSTREAM",
703
+ message: "upstream stream ended before message_stop",
704
+ retryable: RETRYABLE.UPSTREAM
705
+ };
706
+ }
696
707
  }
697
708
 
698
709
  // packages/providers/src/anthropic/wire.ts
@@ -805,9 +816,10 @@ var anthropicAdapter = {
805
816
  ["anthropic-version", API_VERSION],
806
817
  ["Accept", req.request.stream ? "text/event-stream" : "application/json"]
807
818
  ];
819
+ const betas = new Set(req.request.betas ?? []);
808
820
  if (oauth) {
809
821
  protocol.push(["Authorization", `Bearer ${req.credentials.accessToken}`]);
810
- protocol.push(["anthropic-beta", OAUTH_BETA]);
822
+ betas.add(OAUTH_BETA);
811
823
  } else if (req.credentials.apiKey !== null) {
812
824
  protocol.push(["x-api-key", req.credentials.apiKey]);
813
825
  } else {
@@ -815,6 +827,8 @@ var anthropicAdapter = {
815
827
  provider: "anthropic"
816
828
  });
817
829
  }
830
+ if (betas.size > 0)
831
+ protocol.push(["anthropic-beta", [...betas].join(",")]);
818
832
  const profile = PROFILES.anthropic;
819
833
  const headers = orderHeaders(mergeHeaders(profile.headers, protocol), profile.order);
820
834
  const bodyString = signAnthropicBody(JSON.stringify(orderFields(withSystem, BODY_ORDER.anthropic)));
@@ -1032,18 +1046,16 @@ async function* decodeChat(messages) {
1032
1046
  let started = false;
1033
1047
  let textOpen = false;
1034
1048
  let textIndex;
1035
- let ended = false;
1049
+ let done = false;
1036
1050
  let stopReason = "endTurn";
1037
1051
  let usage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
1038
1052
  const toolIndex = new Map;
1039
1053
  let nextIndex = 0;
1040
- const emitEnd = () => {
1041
- ended = true;
1042
- return { type: "end", stopReason, usage };
1043
- };
1044
1054
  for await (const msg of messages) {
1045
- if (msg.data === "[DONE]")
1055
+ if (msg.data === "[DONE]") {
1056
+ done = true;
1046
1057
  break;
1058
+ }
1047
1059
  const d = json2(msg.data);
1048
1060
  if (d === null)
1049
1061
  continue;
@@ -1098,19 +1110,21 @@ async function* decodeChat(messages) {
1098
1110
  }
1099
1111
  if (typeof choice.finish_reason === "string") {
1100
1112
  stopReason = FINISH[choice.finish_reason] ?? "endTurn";
1101
- if (textOpen)
1102
- yield { type: "blockEnd", index: textIndex ?? 0 };
1103
- for (const index of toolIndex.values())
1104
- yield { type: "blockEnd", index };
1105
- yield emitEnd();
1106
1113
  }
1107
1114
  }
1108
- if (!ended) {
1115
+ if (done) {
1109
1116
  if (textOpen)
1110
1117
  yield { type: "blockEnd", index: textIndex ?? 0 };
1111
1118
  for (const index of toolIndex.values())
1112
1119
  yield { type: "blockEnd", index };
1113
- yield emitEnd();
1120
+ yield { type: "end", stopReason, usage };
1121
+ } else {
1122
+ yield {
1123
+ type: "error",
1124
+ code: "UPSTREAM",
1125
+ message: "upstream stream ended before [DONE]",
1126
+ retryable: RETRYABLE.UPSTREAM
1127
+ };
1114
1128
  }
1115
1129
  }
1116
1130
 
@@ -1265,6 +1279,7 @@ async function* decodeResponses(messages) {
1265
1279
  return assigned;
1266
1280
  };
1267
1281
  let sawToolCall = false;
1282
+ let terminal = false;
1268
1283
  const ownsBlock = new Set;
1269
1284
  for await (const msg of messages) {
1270
1285
  const d = json3(msg.data);
@@ -1340,6 +1355,7 @@ async function* decodeResponses(messages) {
1340
1355
  }
1341
1356
  case "response.completed":
1342
1357
  case "response.incomplete": {
1358
+ terminal = true;
1343
1359
  const r = d.response ?? {};
1344
1360
  const reason = r.incomplete_details?.reason;
1345
1361
  let stopReason = sawToolCall ? "toolUse" : "endTurn";
@@ -1361,6 +1377,7 @@ async function* decodeResponses(messages) {
1361
1377
  }
1362
1378
  case "response.failed":
1363
1379
  case "error": {
1380
+ terminal = true;
1364
1381
  const err = d.response?.error ?? d.error ?? {};
1365
1382
  const code = ERROR_CODE[String(err.code ?? err.type)] ?? "UPSTREAM";
1366
1383
  yield {
@@ -1375,6 +1392,14 @@ async function* decodeResponses(messages) {
1375
1392
  break;
1376
1393
  }
1377
1394
  }
1395
+ if (!terminal) {
1396
+ yield {
1397
+ type: "error",
1398
+ code: "UPSTREAM",
1399
+ message: "upstream stream ended before response completion",
1400
+ retryable: RETRYABLE.UPSTREAM
1401
+ };
1402
+ }
1378
1403
  }
1379
1404
 
1380
1405
  // packages/providers/src/openai/wire.ts
@@ -16415,25 +16440,104 @@ function requireDimension(grain, dimension) {
16415
16440
  }
16416
16441
 
16417
16442
  // packages/control/src/credentials.ts
16443
+ function summarizeCredential(credential) {
16444
+ return {
16445
+ id: credential.id,
16446
+ provider: credential.provider,
16447
+ label: credential.label,
16448
+ authType: credential.authType,
16449
+ enabled: credential.enabled,
16450
+ tier: credential.tier,
16451
+ weight: credential.weight,
16452
+ expiresAt: credential.expiresAt,
16453
+ accountEmail: credential.accountEmail,
16454
+ providerData: credential.providerData,
16455
+ disabledReason: credential.disabledReason,
16456
+ disabledAt: credential.disabledAt,
16457
+ hasRefreshToken: credential.hasRefreshToken,
16458
+ createdAt: credential.createdAt,
16459
+ updatedAt: credential.updatedAt
16460
+ };
16461
+ }
16418
16462
  async function listCredentials(store) {
16419
- const credentials = await store.credentials.list();
16420
- return credentials.map((c) => ({
16421
- id: c.id,
16422
- provider: c.provider,
16423
- label: c.label,
16424
- authType: c.authType,
16425
- enabled: c.enabled,
16426
- tier: c.tier,
16427
- weight: c.weight,
16428
- expiresAt: c.expiresAt,
16429
- accountEmail: c.accountEmail,
16430
- providerData: c.providerData,
16431
- disabledReason: c.disabledReason,
16432
- disabledAt: c.disabledAt,
16433
- hasRefreshToken: c.hasRefreshToken,
16434
- createdAt: c.createdAt,
16435
- updatedAt: c.updatedAt
16436
- }));
16463
+ return (await store.credentials.list()).map(summarizeCredential);
16464
+ }
16465
+ async function getCredential(store, id) {
16466
+ const credential = await store.credentials.get(id);
16467
+ if (credential === null)
16468
+ throw new GatewayError("BAD_REQUEST", "no such credential");
16469
+ return summarizeCredential(credential);
16470
+ }
16471
+ async function createApiKeyCredential(store, input) {
16472
+ const provider = parseOrThrow(providerIdSchema, input.provider);
16473
+ if (typeof input.apiKey !== "string" || input.apiKey.trim().length === 0) {
16474
+ throw new GatewayError("BAD_REQUEST", "apiKey: must not be empty");
16475
+ }
16476
+ if (input.label !== undefined && typeof input.label !== "string") {
16477
+ throw new GatewayError("BAD_REQUEST", "label: must be a string");
16478
+ }
16479
+ const label = input.label?.trim() || `${provider} api key`;
16480
+ const created = await store.credentials.create({
16481
+ id: crypto.randomUUID(),
16482
+ provider,
16483
+ label,
16484
+ authType: "apiKey",
16485
+ enabled: true,
16486
+ tier: 1,
16487
+ weight: 1,
16488
+ expiresAt: null,
16489
+ accountEmail: null,
16490
+ providerData: {},
16491
+ disabledReason: null,
16492
+ disabledAt: null,
16493
+ accessToken: null,
16494
+ refreshToken: null,
16495
+ apiKey: input.apiKey,
16496
+ idToken: null
16497
+ });
16498
+ return summarizeCredential(created);
16499
+ }
16500
+ async function refreshCredential(deps, id) {
16501
+ const credential = await deps.store.credentials.get(id);
16502
+ if (credential === null)
16503
+ throw new GatewayError("BAD_REQUEST", "no such credential");
16504
+ if (credential.authType !== "oauth") {
16505
+ throw new GatewayError("BAD_REQUEST", `credential "${id}" is an api key and has nothing to refresh`);
16506
+ }
16507
+ await deps.refresh(credential);
16508
+ return getCredential(deps.store, id);
16509
+ }
16510
+ async function credentialHealth(store) {
16511
+ const [health, quota] = await Promise.all([
16512
+ store.credentials.listHealth(),
16513
+ store.credentials.listQuota()
16514
+ ]);
16515
+ return { health, quota };
16516
+ }
16517
+ async function credentialStatus(store, options) {
16518
+ const [credentials, quota, adminConfigured] = await Promise.all([
16519
+ listCredentials(store),
16520
+ store.credentials.listQuota(),
16521
+ createAdminAuth(store, { now: options.now, sessionTtlMs: 0 }).isConfigured()
16522
+ ]);
16523
+ const byCredential = new Map;
16524
+ for (const row of quota) {
16525
+ const rows = byCredential.get(row.credentialId);
16526
+ if (rows === undefined)
16527
+ byCredential.set(row.credentialId, [row]);
16528
+ else
16529
+ rows.push(row);
16530
+ }
16531
+ return {
16532
+ adminConfigured,
16533
+ credentials: credentials.map(({ id, provider, label, enabled }) => ({
16534
+ id,
16535
+ provider,
16536
+ label,
16537
+ enabled,
16538
+ quota: byCredential.get(id) ?? []
16539
+ }))
16540
+ };
16437
16541
  }
16438
16542
  async function patchCredential(deps, id, input) {
16439
16543
  const patch = parseOrThrow(credentialPatchSchema, input);
@@ -16461,7 +16565,7 @@ function healthKey(credentialId, model) {
16461
16565
  }
16462
16566
  async function buildSnapshot(store, now) {
16463
16567
  const [credentials, healthRows, quotaRows, models, settings] = await Promise.all([
16464
- store.credentials.list(),
16568
+ store.credentials.listRouting(),
16465
16569
  store.credentials.listHealth(),
16466
16570
  store.credentials.listQuota(),
16467
16571
  store.config.listModels(),
@@ -16775,7 +16879,7 @@ var DEFAULT_SETTINGS = {
16775
16879
  // packages/store/src/sqlite/config.ts
16776
16880
  var SETTINGS_KEY = "settings";
16777
16881
  var ADMIN_HASH_KEY = "adminPasswordHash";
16778
- function createConfigRepo(db) {
16882
+ function createConfigRepo(db, emit2 = () => {}) {
16779
16883
  const readRaw = (key) => db.query("SELECT value FROM settings WHERE key = ?").get(key)?.value ?? null;
16780
16884
  const writeRaw = (key, value) => {
16781
16885
  db.run("INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value", [key, value]);
@@ -16795,9 +16899,11 @@ function createConfigRepo(db) {
16795
16899
  targets = excluded.targets,
16796
16900
  strategy = excluded.strategy,
16797
16901
  is_alias = excluded.is_alias`, [model.id, JSON.stringify(model.targets), model.strategy, model.isAlias ? 1 : 0]);
16902
+ emit2({ type: "modelsChanged" });
16798
16903
  },
16799
16904
  async removeModel(id) {
16800
16905
  db.run("DELETE FROM virtual_models WHERE id = ?", [id]);
16906
+ emit2({ type: "modelsChanged" });
16801
16907
  },
16802
16908
  async getSettings() {
16803
16909
  const raw = readRaw(SETTINGS_KEY);
@@ -16818,6 +16924,7 @@ function createConfigRepo(db) {
16818
16924
  weights: { ...current.weights, ...patch.weights }
16819
16925
  };
16820
16926
  writeRaw(SETTINGS_KEY, JSON.stringify(next));
16927
+ emit2({ type: "settingsChanged" });
16821
16928
  return next;
16822
16929
  },
16823
16930
  async getAdminPasswordHash() {
@@ -16833,8 +16940,40 @@ function createConfigRepo(db) {
16833
16940
  };
16834
16941
  }
16835
16942
  // packages/store/src/sqlite/credentials.ts
16836
- function createCredentialRepo(db, key) {
16837
- const view = (row) => ({
16943
+ function createCredentialRepo(db, key, emit2 = () => {}) {
16944
+ const open = async (v) => v === null ? null : decrypt(key, v);
16945
+ const secretsFrom = async (row) => ({
16946
+ accessToken: await open(row.access_token),
16947
+ refreshToken: await open(row.refresh_token),
16948
+ apiKey: await open(row.api_key),
16949
+ idToken: await open(row.id_token)
16950
+ });
16951
+ const requiredRow = (row, id) => {
16952
+ if (row === null)
16953
+ throw new Error(`credential ${id} no longer exists`);
16954
+ return row;
16955
+ };
16956
+ const currentSecrets = async (id) => {
16957
+ const row = requiredRow(db.query("SELECT access_token, refresh_token, api_key, id_token FROM credentials WHERE id = ?").get(id), id);
16958
+ return secretsFrom(row);
16959
+ };
16960
+ const currentInferenceSecrets = async (id, authType) => {
16961
+ if (authType === "oauth") {
16962
+ const row2 = requiredRow(db.query("SELECT access_token FROM credentials WHERE id = ?").get(id), id);
16963
+ return { accessToken: await open(row2.access_token), apiKey: null };
16964
+ }
16965
+ const row = requiredRow(db.query("SELECT api_key FROM credentials WHERE id = ?").get(id), id);
16966
+ return { accessToken: null, apiKey: await open(row.api_key) };
16967
+ };
16968
+ const currentRefreshSecrets = async (id) => {
16969
+ const row = requiredRow(db.query("SELECT refresh_token FROM credentials WHERE id = ?").get(id), id);
16970
+ return { refreshToken: await open(row.refresh_token) };
16971
+ };
16972
+ const currentUsageSecrets = async (id) => {
16973
+ const row = requiredRow(db.query("SELECT access_token FROM credentials WHERE id = ?").get(id), id);
16974
+ return { accessToken: await open(row.access_token) };
16975
+ };
16976
+ const view = (row, loadCurrentSecrets = false) => ({
16838
16977
  id: row.id,
16839
16978
  provider: row.provider,
16840
16979
  label: row.label,
@@ -16850,18 +16989,31 @@ function createCredentialRepo(db, key) {
16850
16989
  hasRefreshToken: row.refresh_token !== null,
16851
16990
  createdAt: row.created_at,
16852
16991
  updatedAt: row.updated_at,
16853
- secrets: async () => ({
16854
- accessToken: await open(row.access_token),
16855
- refreshToken: await open(row.refresh_token),
16856
- apiKey: await open(row.api_key),
16857
- idToken: await open(row.id_token)
16858
- })
16992
+ secrets: () => loadCurrentSecrets ? currentSecrets(row.id) : secretsFrom(row),
16993
+ openForInference: async () => {
16994
+ if (loadCurrentSecrets)
16995
+ return currentInferenceSecrets(row.id, row.auth_type);
16996
+ if (row.auth_type === "oauth") {
16997
+ return { accessToken: await open(row.access_token), apiKey: null };
16998
+ }
16999
+ return { accessToken: null, apiKey: await open(row.api_key) };
17000
+ },
17001
+ openForRefresh: async () => loadCurrentSecrets ? currentRefreshSecrets(row.id) : { refreshToken: await open(row.refresh_token) },
17002
+ openForUsage: async () => loadCurrentSecrets ? currentUsageSecrets(row.id) : { accessToken: await open(row.access_token) }
16859
17003
  });
16860
- const open = async (v) => v === null ? null : decrypt(key, v);
16861
17004
  const seal = async (v) => v === null || v === undefined ? null : encrypt(key, v);
16862
17005
  return {
16863
17006
  async list() {
16864
- return db.query("SELECT * FROM credentials ORDER BY tier, label").all().map(view);
17007
+ return db.query("SELECT * FROM credentials ORDER BY tier, label").all().map((row) => view(row));
17008
+ },
17009
+ async listRouting() {
17010
+ return db.query(`SELECT id, provider, label, auth_type, enabled, tier, weight, expires_at,
17011
+ account_email, provider_data, disabled_reason, disabled_at,
17012
+ NULL AS access_token,
17013
+ CASE WHEN refresh_token IS NULL THEN NULL ELSE 'present' END AS refresh_token,
17014
+ NULL AS api_key, NULL AS id_token, created_at, updated_at
17015
+ FROM credentials
17016
+ ORDER BY tier, label`).all().map((row) => view(row, true));
16865
17017
  },
16866
17018
  async get(id) {
16867
17019
  const row = db.query("SELECT * FROM credentials WHERE id = ?").get(id);
@@ -16894,6 +17046,7 @@ function createCredentialRepo(db, key) {
16894
17046
  now
16895
17047
  ]);
16896
17048
  const { accessToken, refreshToken, apiKey, idToken, ...meta3 } = input;
17049
+ emit2({ type: "credentialsChanged" });
16897
17050
  return {
16898
17051
  ...meta3,
16899
17052
  hasRefreshToken: refreshToken != null,
@@ -16930,6 +17083,7 @@ function createCredentialRepo(db, key) {
16930
17083
  return;
16931
17084
  put("updated_at", Date.now());
16932
17085
  db.run(`UPDATE credentials SET ${sets.join(", ")} WHERE id = ?`, [...vals, id]);
17086
+ emit2({ type: "credentialsChanged" });
16933
17087
  },
16934
17088
  async updateSecrets(id, secrets, expiresAt) {
16935
17089
  const sets = [];
@@ -16953,9 +17107,11 @@ function createCredentialRepo(db, key) {
16953
17107
  sets.push("expires_at = ?", "updated_at = ?");
16954
17108
  vals.push(expiresAt, Date.now());
16955
17109
  db.run(`UPDATE credentials SET ${sets.join(", ")} WHERE id = ?`, [...vals, id]);
17110
+ emit2({ type: "credentialsChanged" });
16956
17111
  },
16957
17112
  async remove(id) {
16958
17113
  db.run("DELETE FROM credentials WHERE id = ?", [id]);
17114
+ emit2({ type: "credentialsChanged" });
16959
17115
  },
16960
17116
  async listHealth() {
16961
17117
  return db.query("SELECT * FROM credential_health").all().map((r) => ({
@@ -16986,6 +17142,7 @@ function createCredentialRepo(db, key) {
16986
17142
  stmt.run(r.credentialId, r.model, r.breakerState, r.consecutiveFailures, r.openedAt, r.rateLimitedUntil, r.ewmaTtftMs, r.lastUsedAt);
16987
17143
  }
16988
17144
  })();
17145
+ emit2({ type: "healthSaved", rows });
16989
17146
  },
16990
17147
  async listQuota() {
16991
17148
  return db.query("SELECT * FROM quota_windows").all().map((r) => ({
@@ -17025,6 +17182,7 @@ function createCredentialRepo(db, key) {
17025
17182
  prune.run(credentialId, JSON.stringify(types2));
17026
17183
  }
17027
17184
  })();
17185
+ emit2({ type: "quotaSaved", rows });
17028
17186
  }
17029
17187
  };
17030
17188
  }
@@ -17497,11 +17655,26 @@ function createUsageRepo(db) {
17497
17655
  // packages/store/src/sqlite/store.ts
17498
17656
  async function createStore(opts) {
17499
17657
  const db = openDb(opts.path);
17658
+ const listeners = new Set;
17659
+ const emit2 = (change) => {
17660
+ for (const listener of listeners) {
17661
+ try {
17662
+ listener(change);
17663
+ } catch {}
17664
+ }
17665
+ };
17500
17666
  return {
17501
- credentials: createCredentialRepo(db, opts.encryptionKey),
17502
- config: createConfigRepo(db),
17667
+ credentials: createCredentialRepo(db, opts.encryptionKey, emit2),
17668
+ config: createConfigRepo(db, emit2),
17503
17669
  keys: createKeyRepo(db),
17504
17670
  usage: createUsageRepo(db),
17671
+ routing: {
17672
+ version: () => db.query("PRAGMA data_version").get()?.data_version ?? 0,
17673
+ subscribe(listener) {
17674
+ listeners.add(listener);
17675
+ return () => listeners.delete(listener);
17676
+ }
17677
+ },
17505
17678
  close: () => db.close()
17506
17679
  };
17507
17680
  }
@@ -17829,7 +18002,7 @@ var OAUTH_PROVIDERS = {
17829
18002
  function createRefresher(deps) {
17830
18003
  const inFlight = new Map;
17831
18004
  async function run(credential) {
17832
- const secrets = await credential.secrets();
18005
+ const secrets = await credential.openForRefresh();
17833
18006
  if (secrets.refreshToken === null) {
17834
18007
  throw new GatewayError("AUTH", `credential ${credential.id} has no refresh token`);
17835
18008
  }
@@ -18166,10 +18339,7 @@ var credentialsList = {
18166
18339
  }
18167
18340
  };
18168
18341
  async function findCredential(ctx, id) {
18169
- const credential = await (await ctx.store()).credentials.get(id) ?? null;
18170
- if (credential === null)
18171
- throw new CliError(`no credential "${id}"`);
18172
- return credential;
18342
+ return getCredential(await ctx.store(), id);
18173
18343
  }
18174
18344
  var credentialsShow = {
18175
18345
  usage: "credentials show <id>",
@@ -18268,12 +18438,7 @@ var credentialsRefresh = {
18268
18438
  async run(args, { ctx, writer }) {
18269
18439
  const id = requirePositional(args, 0, "credential id");
18270
18440
  const store = await ctx.store();
18271
- const credential = await store.credentials.get(id);
18272
- if (credential === null)
18273
- throw new CliError(`no credential "${id}"`);
18274
- if (credential.authType !== "oauth") {
18275
- throw new CliError(`credential "${id}" is an api key and has nothing to refresh`);
18276
- }
18441
+ const credential = await findCredential(ctx, id);
18277
18442
  const refresh = createRefresher({
18278
18443
  store,
18279
18444
  providers: OAUTH_PROVIDERS,
@@ -18281,9 +18446,8 @@ var credentialsRefresh = {
18281
18446
  now: ctx.now
18282
18447
  });
18283
18448
  note(ctx, writer, `refreshing ${credential.provider} credential ${id}\u2026`);
18284
- await refresh(credential);
18285
- const updated = await store.credentials.get(id);
18286
- emit(ctx, writer, { id, expiresAt: updated?.expiresAt ?? null }, () => `${id} refreshed; expires ${formatTime(updated?.expiresAt ?? null)}`);
18449
+ const updated = await refreshCredential({ store, refresh }, id);
18450
+ emit(ctx, writer, { id, expiresAt: updated.expiresAt }, () => `${id} refreshed; expires ${formatTime(updated.expiresAt)}`);
18287
18451
  }
18288
18452
  };
18289
18453
  var credentialsAddKey = {
@@ -18298,27 +18462,12 @@ var credentialsAddKey = {
18298
18462
  const key = await prompt.secret(`${providerId} API key: `);
18299
18463
  if (key.length === 0)
18300
18464
  throw new CliError("no API key given");
18301
- const store = await ctx.store();
18302
- const id = crypto.randomUUID();
18303
- await store.credentials.create({
18304
- id,
18465
+ const created = await createApiKeyCredential(await ctx.store(), {
18305
18466
  provider: providerId,
18306
- label: stringFlag(args.values, "label") ?? `${providerId} api key`,
18307
- authType: "apiKey",
18308
- enabled: true,
18309
- tier: 1,
18310
- weight: 1,
18311
- expiresAt: null,
18312
- accountEmail: null,
18313
- providerData: {},
18314
- disabledReason: null,
18315
- disabledAt: null,
18316
- accessToken: null,
18317
- refreshToken: null,
18318
18467
  apiKey: key,
18319
- idToken: null
18468
+ label: stringFlag(args.values, "label")
18320
18469
  });
18321
- emit(ctx, writer, { id, provider: providerId }, () => `stored ${providerId} api key as ${id}`);
18470
+ emit(ctx, writer, { id: created.id, provider: created.provider }, () => `stored ${created.provider} api key as ${created.id}`);
18322
18471
  }
18323
18472
  };
18324
18473
  var credentialsHealth = {
@@ -18327,8 +18476,8 @@ var credentialsHealth = {
18327
18476
  options: { all: { type: "boolean" } },
18328
18477
  async run(args, { ctx, writer }) {
18329
18478
  const store = await ctx.store();
18330
- const [rows, credentials] = await Promise.all([
18331
- store.credentials.listHealth(),
18479
+ const [{ health: rows }, credentials] = await Promise.all([
18480
+ credentialHealth(store),
18332
18481
  listCredentials(store)
18333
18482
  ]);
18334
18483
  const labels = new Map(credentials.map((c) => [c.id, c.label]));
@@ -19167,30 +19316,11 @@ var status2 = {
19167
19316
  } catch (error51) {
19168
19317
  storeError = error51 instanceof Error ? error51.message : "could not open the database";
19169
19318
  }
19170
- const credentials = store === null ? [] : await store.credentials.list();
19171
- const quotaRows = store === null ? [] : await store.credentials.listQuota();
19172
- const configured = store === null ? false : await createAdminAuth(store, {
19173
- now: ctx.now,
19174
- sessionTtlMs: 0
19175
- }).isConfigured();
19176
- const byCredential = new Map;
19177
- for (const row of quotaRows) {
19178
- const list = byCredential.get(row.credentialId);
19179
- if (list === undefined)
19180
- byCredential.set(row.credentialId, [row]);
19181
- else
19182
- list.push(row);
19183
- }
19319
+ const persistent = store === null ? { adminConfigured: false, credentials: [] } : await credentialStatus(store, { now: ctx.now });
19320
+ const { adminConfigured: configured, credentials } = persistent;
19184
19321
  const data = {
19185
19322
  process: process3,
19186
- adminConfigured: configured,
19187
- credentials: credentials.map((credential) => ({
19188
- id: credential.id,
19189
- provider: credential.provider,
19190
- label: credential.label,
19191
- enabled: credential.enabled,
19192
- quota: byCredential.get(credential.id) ?? []
19193
- })),
19323
+ ...persistent,
19194
19324
  storeError
19195
19325
  };
19196
19326
  emit(ctx, writer, data, () => {
@@ -19212,15 +19342,12 @@ ${state(ctx, false, storeError)}`;
19212
19342
 
19213
19343
  no credentials; add one with: omni connect <provider>`;
19214
19344
  }
19215
- const rows = credentials.map((credential) => {
19216
- const windows = byCredential.get(credential.id) ?? [];
19217
- return [
19218
- credential.label,
19219
- provider(ctx, credential.provider),
19220
- state(ctx, credential.enabled, credential.enabled ? "enabled" : "disabled"),
19221
- quotaCell(ctx, windows, ctx.now())
19222
- ];
19223
- });
19345
+ const rows = credentials.map((credential) => [
19346
+ credential.label,
19347
+ provider(ctx, credential.provider),
19348
+ state(ctx, credential.enabled, credential.enabled ? "enabled" : "disabled"),
19349
+ quotaCell(ctx, credential.quota, ctx.now())
19350
+ ]);
19224
19351
  return `${header}
19225
19352
 
19226
19353
  ${table([{ header: "ACCOUNT" }, { header: "PROVIDER" }, { header: "STATE" }, { header: "QUOTA" }], rows)}`;