@tokenbuddy/tb-admin 1.0.13 → 1.0.15

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.
Files changed (45) hide show
  1. package/dist/src/bootstrap-registry.d.ts +1 -0
  2. package/dist/src/bootstrap-registry.d.ts.map +1 -1
  3. package/dist/src/bootstrap-registry.js.map +1 -1
  4. package/dist/src/cli.d.ts.map +1 -1
  5. package/dist/src/cli.js +30 -16
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/server-cmd.d.ts +27 -2
  8. package/dist/src/server-cmd.d.ts.map +1 -1
  9. package/dist/src/server-cmd.js +131 -26
  10. package/dist/src/server-cmd.js.map +1 -1
  11. package/dist/src/ui-actions.d.ts +88 -0
  12. package/dist/src/ui-actions.d.ts.map +1 -0
  13. package/dist/src/ui-actions.js +763 -0
  14. package/dist/src/ui-actions.js.map +1 -0
  15. package/dist/src/ui-command.d.ts +4 -0
  16. package/dist/src/ui-command.d.ts.map +1 -0
  17. package/dist/src/ui-command.js +37 -0
  18. package/dist/src/ui-command.js.map +1 -0
  19. package/dist/src/ui-server.d.ts +23 -0
  20. package/dist/src/ui-server.d.ts.map +1 -0
  21. package/dist/src/ui-server.js +245 -0
  22. package/dist/src/ui-server.js.map +1 -0
  23. package/dist/src/ui-state.d.ts +134 -0
  24. package/dist/src/ui-state.d.ts.map +1 -0
  25. package/dist/src/ui-state.js +407 -0
  26. package/dist/src/ui-state.js.map +1 -0
  27. package/dist/src/ui-static.d.ts +2 -0
  28. package/dist/src/ui-static.d.ts.map +1 -0
  29. package/dist/src/ui-static.js +144 -0
  30. package/dist/src/ui-static.js.map +1 -0
  31. package/dist/src/upstream-balance-probe.d.ts +41 -0
  32. package/dist/src/upstream-balance-probe.d.ts.map +1 -0
  33. package/dist/src/upstream-balance-probe.js +379 -0
  34. package/dist/src/upstream-balance-probe.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/bootstrap-registry.ts +1 -0
  37. package/src/cli.ts +32 -16
  38. package/src/server-cmd.ts +163 -29
  39. package/src/ui-actions.ts +901 -0
  40. package/src/ui-command.ts +39 -0
  41. package/src/ui-server.ts +308 -0
  42. package/src/ui-state.ts +575 -0
  43. package/src/ui-static.ts +144 -0
  44. package/src/upstream-balance-probe.ts +505 -0
  45. package/tests/admin.test.ts +893 -4
@@ -0,0 +1,379 @@
1
+ const DEFAULT_TIMEOUT_MS = 7000;
2
+ const FAILURE_CACHE_TTL_MS = 30000;
3
+ const DEFAULT_CNY_USD_RATE = 0.14;
4
+ export class BalanceProbeCache {
5
+ entries = new Map();
6
+ get(key, now) {
7
+ const entry = this.entries.get(key);
8
+ if (!entry || entry.expiresAt <= now) {
9
+ if (entry) {
10
+ this.entries.delete(key);
11
+ }
12
+ return undefined;
13
+ }
14
+ return entry.snapshot;
15
+ }
16
+ setFailure(key, snapshot, now) {
17
+ this.entries.set(key, {
18
+ snapshot,
19
+ expiresAt: now + FAILURE_CACHE_TTL_MS
20
+ });
21
+ }
22
+ }
23
+ export const defaultBalanceProbeCache = new BalanceProbeCache();
24
+ export async function probeUpstreamBalance(config, options = {}) {
25
+ const now = options.now || Date.now;
26
+ const fetchedAt = now();
27
+ const key = cacheKey(config);
28
+ const cache = options.cache || defaultBalanceProbeCache;
29
+ const cached = cache.get(key, fetchedAt);
30
+ if (cached) {
31
+ return cached;
32
+ }
33
+ const plan = requestPlan(config);
34
+ if (!plan) {
35
+ const snapshot = failure("unknown", fetchedAt, 0, "unsupported upstream - configure a balance parser");
36
+ cache.setFailure(key, snapshot, fetchedAt);
37
+ return snapshot;
38
+ }
39
+ if (!config.upstreamApiKey) {
40
+ const snapshot = failure(plan.source, fetchedAt, 0, "missing upstreamApiKey for balance probe");
41
+ cache.setFailure(key, snapshot, fetchedAt);
42
+ return snapshot;
43
+ }
44
+ const upstreamUserId = stringValue(config.upstreamBalanceProbe?.userId) || stringValue(config.upstreamUserId);
45
+ if (plan.requiresUserId && !upstreamUserId) {
46
+ const snapshot = failure(plan.source, fetchedAt, 0, "missing upstreamUserId for newapi upstream");
47
+ cache.setFailure(key, snapshot, fetchedAt);
48
+ return snapshot;
49
+ }
50
+ try {
51
+ const response = await fetchWithTimeout(plan.url, {
52
+ method: "GET",
53
+ headers: requestHeaders(config.upstreamApiKey, plan.requiresUserId ? upstreamUserId : undefined)
54
+ }, options.fetch || fetch, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
55
+ if (!response.ok) {
56
+ const snapshot = failure(plan.source, fetchedAt, response.status, httpErrorMessage(response.status));
57
+ cache.setFailure(key, snapshot, fetchedAt);
58
+ return snapshot;
59
+ }
60
+ const payload = await response.json();
61
+ const snapshot = parseBalancePayload(payload, plan, fetchedAt, cnyUsdRate(options.cnyUsdRate));
62
+ if (snapshot.error) {
63
+ cache.setFailure(key, snapshot, fetchedAt);
64
+ }
65
+ return snapshot;
66
+ }
67
+ catch (err) {
68
+ const snapshot = failure(plan.source, fetchedAt, 0, `network: ${err instanceof Error ? err.message : String(err)}`);
69
+ cache.setFailure(key, snapshot, fetchedAt);
70
+ return snapshot;
71
+ }
72
+ }
73
+ function requestPlan(config) {
74
+ const template = balanceProbeTemplateValue(config.upstreamBalanceProbe?.template);
75
+ if (template === "none") {
76
+ return undefined;
77
+ }
78
+ const balanceUrl = stringValue(config.upstreamBalanceProbe?.url) || stringValue(config.upstreamBalanceUrl);
79
+ const upstreamUrl = stringValue(config.upstreamUrl);
80
+ if (template && template !== "auto") {
81
+ return explicitRequestPlan(template, balanceUrl, upstreamUrl);
82
+ }
83
+ const host = hostName(balanceUrl || upstreamUrl);
84
+ if (host === "api.deepseek.com") {
85
+ return { source: "deepseek", url: "https://api.deepseek.com/user/balance", currency: "CNY", requiresUserId: false };
86
+ }
87
+ if (host === "api.stepfun.ai" || host === "api.stepfun.com") {
88
+ return { source: "stepfun", url: "https://api.stepfun.com/v1/accounts", currency: "CNY", requiresUserId: false };
89
+ }
90
+ if (host === "api.siliconflow.cn" || host === "api.siliconflow.com") {
91
+ const currency = host.endsWith(".cn") ? "CNY" : "USD";
92
+ const url = host.endsWith(".cn") ? "https://api.siliconflow.cn/v1/user/info" : "https://api.siliconflow.com/v1/user/info";
93
+ return { source: "siliconflow", url, currency, requiresUserId: false };
94
+ }
95
+ if (host === "openrouter.ai") {
96
+ return { source: "openrouter", url: "https://openrouter.ai/api/v1/credits", currency: "USD", requiresUserId: false };
97
+ }
98
+ if (host === "api.novita.ai") {
99
+ return { source: "novita", url: "https://api.novita.ai/v3/user/balance", currency: "USD", requiresUserId: false };
100
+ }
101
+ if (balanceUrl && isUsageEndpoint(balanceUrl)) {
102
+ return { source: "usage_generic", url: balanceUrl, currency: "USD", requiresUserId: false };
103
+ }
104
+ if (balanceUrl) {
105
+ return { source: "newapi_generic", url: balanceUrl, currency: "USD", requiresUserId: true };
106
+ }
107
+ const genericUsageUrl = upstreamUrl ? usageUrl(upstreamUrl) : undefined;
108
+ if (genericUsageUrl) {
109
+ return { source: "usage_generic", url: genericUsageUrl, currency: "USD", requiresUserId: false };
110
+ }
111
+ return undefined;
112
+ }
113
+ function explicitRequestPlan(template, balanceUrl, upstreamUrl) {
114
+ if (template === "deepseek") {
115
+ return { source: "deepseek", url: balanceUrl || "https://api.deepseek.com/user/balance", currency: "CNY", requiresUserId: false };
116
+ }
117
+ if (template === "stepfun") {
118
+ return { source: "stepfun", url: balanceUrl || "https://api.stepfun.com/v1/accounts", currency: "CNY", requiresUserId: false };
119
+ }
120
+ if (template === "siliconflow") {
121
+ const host = hostName(balanceUrl || upstreamUrl);
122
+ const currency = host.endsWith(".com") ? "USD" : "CNY";
123
+ const url = balanceUrl || (currency === "USD" ? "https://api.siliconflow.com/v1/user/info" : "https://api.siliconflow.cn/v1/user/info");
124
+ return { source: "siliconflow", url, currency, requiresUserId: false };
125
+ }
126
+ if (template === "openrouter") {
127
+ return { source: "openrouter", url: balanceUrl || "https://openrouter.ai/api/v1/credits", currency: "USD", requiresUserId: false };
128
+ }
129
+ if (template === "novita") {
130
+ return { source: "novita", url: balanceUrl || "https://api.novita.ai/v3/user/balance", currency: "USD", requiresUserId: false };
131
+ }
132
+ if (template === "newapi_generic") {
133
+ return balanceUrl ? { source: "newapi_generic", url: balanceUrl, currency: "USD", requiresUserId: true } : undefined;
134
+ }
135
+ if (template === "usage_generic") {
136
+ const url = balanceUrl || (upstreamUrl ? usageUrl(upstreamUrl) : undefined);
137
+ return url ? { source: "usage_generic", url, currency: "USD", requiresUserId: false } : undefined;
138
+ }
139
+ return undefined;
140
+ }
141
+ function requestHeaders(upstreamApiKey, upstreamUserId) {
142
+ const headers = {
143
+ "Authorization": `Bearer ${upstreamApiKey}`
144
+ };
145
+ if (upstreamUserId) {
146
+ headers["New-Api-User"] = upstreamUserId;
147
+ }
148
+ return headers;
149
+ }
150
+ async function fetchWithTimeout(url, init, fetchFn, timeoutMs) {
151
+ const controller = new AbortController();
152
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
153
+ try {
154
+ return await fetchFn(url, { ...init, signal: controller.signal });
155
+ }
156
+ finally {
157
+ clearTimeout(timer);
158
+ }
159
+ }
160
+ function parseBalancePayload(payload, plan, fetchedAt, cnyRate) {
161
+ const object = objectValue(payload);
162
+ if (!object) {
163
+ return failure(plan.source, fetchedAt, 200, "balance response must be an object");
164
+ }
165
+ if (plan.source === "deepseek") {
166
+ return parseDeepSeek(object, fetchedAt, cnyRate);
167
+ }
168
+ if (plan.source === "stepfun") {
169
+ return amountSnapshot(numberFrom(object.balance), "CNY", "stepfun", fetchedAt, cnyRate);
170
+ }
171
+ if (plan.source === "siliconflow") {
172
+ return parseSiliconFlow(object, plan.currency, fetchedAt, cnyRate);
173
+ }
174
+ if (plan.source === "openrouter") {
175
+ return parseOpenRouter(object, fetchedAt, cnyRate);
176
+ }
177
+ if (plan.source === "novita") {
178
+ const availableBalance = numberFrom(object.availableBalance);
179
+ return amountSnapshot(availableBalance === null ? null : availableBalance / 10000, "USD", "novita", fetchedAt, cnyRate);
180
+ }
181
+ if (plan.source === "newapi_generic") {
182
+ return parseNewApi(object, fetchedAt, cnyRate);
183
+ }
184
+ if (plan.source === "usage_generic") {
185
+ return parseUsageGeneric(object, fetchedAt, cnyRate);
186
+ }
187
+ return failure("unknown", fetchedAt, 0, "unsupported upstream - configure a balance parser");
188
+ }
189
+ function parseDeepSeek(payload, fetchedAt, cnyRate) {
190
+ const infos = Array.isArray(payload.balance_infos) ? payload.balance_infos : [];
191
+ const first = objectValue(infos[0]);
192
+ const currency = currencyValue(first?.currency) || "CNY";
193
+ const rawAmount = numberFrom(first?.total_balance);
194
+ if (payload.is_available === false) {
195
+ return {
196
+ ...amountSnapshot(rawAmount, currency, "deepseek", fetchedAt, cnyRate),
197
+ error: { httpStatus: 200, message: "Insufficient balance" }
198
+ };
199
+ }
200
+ if (!first) {
201
+ return amountSnapshot(null, currency, "deepseek", fetchedAt, cnyRate);
202
+ }
203
+ if (rawAmount === null) {
204
+ return failure("deepseek", fetchedAt, 200, "missing field: balance_infos[].total_balance", currency);
205
+ }
206
+ return amountSnapshot(rawAmount, currency, "deepseek", fetchedAt, cnyRate);
207
+ }
208
+ function parseSiliconFlow(payload, currency, fetchedAt, cnyRate) {
209
+ const code = numberFrom(payload.code);
210
+ if (code !== null && code !== 20000) {
211
+ return failure("siliconflow", fetchedAt, 200, `upstream code: ${code}`, currency);
212
+ }
213
+ const data = objectValue(payload.data);
214
+ if (!data) {
215
+ return failure("siliconflow", fetchedAt, 200, "missing field: data", currency);
216
+ }
217
+ const snapshot = amountSnapshot(numberFrom(data.totalBalance), currency, "siliconflow", fetchedAt, cnyRate);
218
+ const status = stringValue(data.status);
219
+ if (status && status !== "ok") {
220
+ return {
221
+ ...snapshot,
222
+ error: { httpStatus: 200, message: `upstream status: ${status}` }
223
+ };
224
+ }
225
+ return snapshot;
226
+ }
227
+ function parseOpenRouter(payload, fetchedAt, cnyRate) {
228
+ const data = objectValue(payload.data) || payload;
229
+ const totalCredits = numberFrom(data.total_credits);
230
+ const totalUsage = numberFrom(data.total_usage);
231
+ if (totalCredits === null || totalUsage === null) {
232
+ return failure("openrouter", fetchedAt, 200, "missing field: data.total_credits or data.total_usage", "USD");
233
+ }
234
+ return amountSnapshot(Math.max(0, totalCredits - totalUsage), "USD", "openrouter", fetchedAt, cnyRate);
235
+ }
236
+ function parseNewApi(payload, fetchedAt, cnyRate) {
237
+ const data = objectValue(payload.data);
238
+ if (!data) {
239
+ return failure("newapi_generic", fetchedAt, 200, "missing field: data", "USD");
240
+ }
241
+ const quota = numberFrom(data.quota);
242
+ const usedQuota = numberFrom(data.used_quota);
243
+ if (quota === null || usedQuota === null) {
244
+ return failure("newapi_generic", fetchedAt, 200, "missing field: data.quota or data.used_quota", "USD");
245
+ }
246
+ return amountSnapshot((quota - usedQuota) / 500000, "USD", "newapi_generic", fetchedAt, cnyRate);
247
+ }
248
+ function parseUsageGeneric(payload, fetchedAt, cnyRate) {
249
+ const quota = objectValue(payload.quota);
250
+ const rawAmount = numberFrom(payload.remaining) ?? numberFrom(quota?.remaining) ?? numberFrom(payload.balance);
251
+ const currency = currencyValue(payload.unit) || currencyValue(quota?.unit) || "USD";
252
+ if (rawAmount === null) {
253
+ return failure("usage_generic", fetchedAt, 200, "missing field: remaining, quota.remaining, or balance", currency);
254
+ }
255
+ const snapshot = amountSnapshot(rawAmount, currency, "usage_generic", fetchedAt, cnyRate);
256
+ const isValid = booleanValue(payload.is_active) ?? booleanValue(payload.isValid) ?? true;
257
+ if (!isValid) {
258
+ return {
259
+ ...snapshot,
260
+ error: { httpStatus: 200, message: "upstream key is not active" }
261
+ };
262
+ }
263
+ return snapshot;
264
+ }
265
+ function amountSnapshot(rawAmount, currency, source, fetchedAt, cnyRate) {
266
+ return {
267
+ rawAmount,
268
+ amountUsdMicros: rawAmount === null ? null : Math.round(rawAmount * (currency === "CNY" ? cnyRate : 1) * 1000000),
269
+ currency,
270
+ source,
271
+ fetchedAt
272
+ };
273
+ }
274
+ function failure(source, fetchedAt, httpStatus, message, currency = null) {
275
+ return {
276
+ rawAmount: null,
277
+ amountUsdMicros: null,
278
+ currency,
279
+ source,
280
+ fetchedAt,
281
+ error: { httpStatus, message }
282
+ };
283
+ }
284
+ function httpErrorMessage(status) {
285
+ if (status === 401) {
286
+ return "unauthorized: check upstreamApiKey";
287
+ }
288
+ if (status === 403) {
289
+ return "forbidden: upstream rejected the credentials";
290
+ }
291
+ if (status === 429) {
292
+ return "rate limited";
293
+ }
294
+ if (status >= 500) {
295
+ return "upstream 5xx";
296
+ }
297
+ return `upstream http ${status}`;
298
+ }
299
+ function cnyUsdRate(value) {
300
+ if (value !== undefined && Number.isFinite(value) && value > 0) {
301
+ return value;
302
+ }
303
+ const envValue = Number(process.env.TB_CNY_USD_RATE);
304
+ return Number.isFinite(envValue) && envValue > 0 ? envValue : DEFAULT_CNY_USD_RATE;
305
+ }
306
+ function cacheKey(config) {
307
+ return [
308
+ stringValue(config.upstreamBalanceProbe?.template),
309
+ stringValue(config.upstreamBalanceProbe?.url),
310
+ stringValue(config.upstreamBalanceProbe?.userId),
311
+ stringValue(config.upstreamBalanceUrl),
312
+ stringValue(config.upstreamUrl),
313
+ stringValue(config.upstreamApiKey),
314
+ stringValue(config.upstreamUserId)
315
+ ].join("|");
316
+ }
317
+ function balanceProbeTemplateValue(value) {
318
+ return value === "auto" ||
319
+ value === "deepseek" ||
320
+ value === "stepfun" ||
321
+ value === "siliconflow" ||
322
+ value === "openrouter" ||
323
+ value === "novita" ||
324
+ value === "newapi_generic" ||
325
+ value === "usage_generic" ||
326
+ value === "none"
327
+ ? value
328
+ : undefined;
329
+ }
330
+ function hostName(value) {
331
+ if (!value) {
332
+ return "";
333
+ }
334
+ try {
335
+ return new URL(value).hostname.replace(/^www\./, "");
336
+ }
337
+ catch {
338
+ return "";
339
+ }
340
+ }
341
+ function usageUrl(value) {
342
+ try {
343
+ const url = new URL(value);
344
+ url.pathname = "/v1/usage";
345
+ url.search = "";
346
+ url.hash = "";
347
+ return url.toString();
348
+ }
349
+ catch {
350
+ return undefined;
351
+ }
352
+ }
353
+ function isUsageEndpoint(value) {
354
+ try {
355
+ return new URL(value).pathname.replace(/\/+$/, "") === "/v1/usage";
356
+ }
357
+ catch {
358
+ return false;
359
+ }
360
+ }
361
+ function objectValue(value) {
362
+ return value && typeof value === "object" && !Array.isArray(value)
363
+ ? value
364
+ : undefined;
365
+ }
366
+ function stringValue(value) {
367
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
368
+ }
369
+ function numberFrom(value) {
370
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
371
+ return Number.isFinite(parsed) ? parsed : null;
372
+ }
373
+ function booleanValue(value) {
374
+ return typeof value === "boolean" ? value : undefined;
375
+ }
376
+ function currencyValue(value) {
377
+ return value === "USD" || value === "CNY" ? value : undefined;
378
+ }
379
+ //# sourceMappingURL=upstream-balance-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-balance-probe.js","sourceRoot":"","sources":["../../src/upstream-balance-probe.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenbuddy/tb-admin",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Remote admin CLI for TokenBuddy seller apps",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -14,6 +14,7 @@ export interface SellerRegistryEntry {
14
14
  region?: string;
15
15
  modelsCount?: number;
16
16
  sampleModels?: string[];
17
+ models?: string[];
17
18
  supportedProtocols: string[];
18
19
  paymentMethods: string[];
19
20
  recommendedFor?: string[];
package/src/cli.ts CHANGED
@@ -2,6 +2,7 @@ import { Command } from "commander";
2
2
  import { ConfigManager } from "./config.js";
3
3
  import { AdminClient } from "./client.js";
4
4
  import { FlyProvider } from "./server-cmd.js";
5
+ import { bindAdminUiCommand } from "./ui-command.js";
5
6
  import {
6
7
  loadRegistryFile,
7
8
  SellerRegistryDocument,
@@ -142,6 +143,13 @@ function loadYamlOrJsonFile(filePath: string): any {
142
143
  return YAML.load(fs.readFileSync(filePath, "utf8"));
143
144
  }
144
145
 
146
+ function requireUpstreamModels(data: any): any[] {
147
+ if (!Array.isArray(data?.models)) {
148
+ throw new Error("operator upstream summary must contain top-level models array");
149
+ }
150
+ return data.models;
151
+ }
152
+
145
153
  /**
146
154
  * 构造 admin commander program,绑定所有 `tb-admin` 子命令(profile / registry / seller / config / backup 等)。
147
155
  * 顶层选项支持 `--url` / `--token` / `--profile` / `--config`,与 `ConfigManager` 协同解析。
@@ -160,6 +168,8 @@ export function buildAdminCli(configManager: ConfigManager): Command {
160
168
  .option("--profile <profile>", "Use custom profile instead of default")
161
169
  .option("--config <path>", "Use custom config file path");
162
170
 
171
+ bindAdminUiCommand(program, configManager);
172
+
163
173
  // Helper to resolve client
164
174
  function getClient(): AdminClient {
165
175
  const opts = program.opts();
@@ -350,26 +360,31 @@ export function buildAdminCli(configManager: ConfigManager): Command {
350
360
  program
351
361
  .command("models")
352
362
  .description("List available upstream models")
353
- .action(async () => {
363
+ .option("--json", "Print current upstream model summary as JSON")
364
+ .action(async (options) => {
354
365
  try {
355
366
  const client = getClient();
356
367
  const data = await client.get("/operator/admin/upstreams");
368
+ const models = requireUpstreamModels(data);
369
+ if (options.json) {
370
+ console.log(JSON.stringify({ models }, null, 2));
371
+ return;
372
+ }
357
373
  const table = new Table({ head: ["Model ID", "Input Price/1M", "Output Price/1M", "Streaming"] });
358
374
 
359
- for (const ups of data.upstreams || []) {
360
- for (const model of ups.models || []) {
361
- table.push([
362
- model.id,
363
- `${model.inputPriceMicrosPer1m} micros`,
364
- `${model.outputPriceMicrosPer1m} micros`,
365
- model.streaming ? "Yes" : "No"
366
- ]);
367
- }
375
+ for (const model of models) {
376
+ table.push([
377
+ model.id,
378
+ `${model.inputPriceMicrosPer1m ?? "unknown"} micros`,
379
+ `${model.outputPriceMicrosPer1m ?? "unknown"} micros`,
380
+ model.streaming ? "Yes" : "No"
381
+ ]);
368
382
  }
369
383
  console.log("=== Upstream Model Configurations ===");
370
384
  console.log(table.toString());
371
385
  } catch (err: any) {
372
386
  console.error("Error:", err.message);
387
+ process.exit(1);
373
388
  }
374
389
  });
375
390
 
@@ -419,9 +434,6 @@ export function buildAdminCli(configManager: ConfigManager): Command {
419
434
  console.log("Auto-refreshing upstream model catalog (upstream URL or key changed)...");
420
435
  const refreshResp = await client.post("/operator/admin/upstreams/refresh", { autoModels: true });
421
436
  console.log(` refreshed: ${refreshResp.refreshedModels ?? 0} models from upstream`);
422
- if (refreshResp.upstreams && refreshResp.upstreams[0]?.error) {
423
- throw new Error(`upstream refresh failed: ${refreshResp.upstreams[0].error}`);
424
- }
425
437
  }
426
438
 
427
439
  const document = await getSellerConfig(client);
@@ -764,6 +776,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
764
776
  console.log(out);
765
777
  } catch (err: any) {
766
778
  console.error("Error:", err.message);
779
+ process.exit(1);
767
780
  }
768
781
  });
769
782
 
@@ -778,6 +791,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
778
791
  .option("--volume-size-gb <gb>", "Persistent volume size in GB", (v) => parseInt(v, 10))
779
792
  .option("--volume-id <id>", "Attach existing volume by ID (skips volume creation)")
780
793
  .option("--volume-snapshot-retention-days <days>", "Volume snapshot retention days", (v) => parseInt(v, 10))
794
+ .option("--initial-config <path>", "Initial seller YAML config to inject as TOKENBUDDY_SELLER_CONFIG_B64")
781
795
  .requiredOption("--operator-secret <secret>", "Operator secret to configure")
782
796
  .option("--dry-run", "Dry run display without actual execution")
783
797
  .action((name, options) => {
@@ -792,32 +806,33 @@ export function buildAdminCli(configManager: ConfigManager): Command {
792
806
  volumeSizeGb: options.volumeSizeGb,
793
807
  volumeId: options.volumeId,
794
808
  volumeSnapshotRetentionDays: options.volumeSnapshotRetentionDays,
809
+ initialConfigPath: options.initialConfig,
795
810
  operatorSecret: options.operatorSecret,
796
811
  dryRun: options.dryRun
797
812
  });
798
813
  console.log(res);
799
814
  } catch (err: any) {
800
815
  console.error("Error:", err.message);
816
+ process.exit(1);
801
817
  }
802
818
  });
803
819
 
804
820
  sellerCmd
805
821
  .command("deploy <app>")
806
- .description("Redeploy an existing seller app on Fly.io using an explicit image and config")
807
- .requiredOption("--fly-config <path>", "Fly.io config file path, for example deploy/fly.io/fly.tb-seller.toml")
822
+ .description("Update an existing seller app's Machines to an explicit image without changing Fly.io config or volumes")
808
823
  .requiredOption("--image <image>", "Published Docker image, for example registry.fly.io/tb-seller:<v>")
809
824
  .option("--dry-run", "Dry run")
810
825
  .action((app, options) => {
811
826
  try {
812
827
  const res = flyProvider.deploySeller({
813
828
  app,
814
- config: options.flyConfig,
815
829
  image: options.image,
816
830
  dryRun: options.dryRun
817
831
  });
818
832
  console.log(res);
819
833
  } catch (err: any) {
820
834
  console.error("Error:", err.message);
835
+ process.exit(1);
821
836
  }
822
837
  });
823
838
 
@@ -849,6 +864,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
849
864
  }
850
865
  } catch (err: any) {
851
866
  console.error("Error:", err.message);
867
+ process.exit(1);
852
868
  }
853
869
  });
854
870