@tokenbuddy/tokenbuddy 1.0.37 → 1.0.39

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.
@@ -251,7 +251,7 @@ export interface BuyerStoreOptions {
251
251
  }
252
252
  /**
253
253
  * 解析 buyer store 的 DB 路径。
254
- * 优先级:`dbPath` → `TOKENBUDDY_BUYER_STORE` env → `~/.tokenbuddy-store/buyer-store.db`。
254
+ * 优先级:`dbPath` → `TOKENBUDDY_BUYER_STORE` env → `TB_WORKDIR` env → `~/.config/tokenbuddy/buyer-store.db`。
255
255
  *
256
256
  * @param options 解析选项
257
257
  * @returns DB 文件绝对路径
@@ -2,9 +2,9 @@
2
2
  import { DatabaseSync } from "node:sqlite";
3
3
  import * as crypto from "crypto";
4
4
  import * as fs from "fs";
5
- import * as os from "os";
6
5
  import * as path from "path";
7
6
  import { createModuleLogger } from "@tokenbuddy/logging";
7
+ import { resolveTokenBuddyWorkdir } from "./workdir.js";
8
8
  const logger = createModuleLogger("tb-proxyd");
9
9
  function nowIso() {
10
10
  return new Date().toISOString();
@@ -26,7 +26,7 @@ function ensureDirForFile(filePath) {
26
26
  }
27
27
  /**
28
28
  * 解析 buyer store 的 DB 路径。
29
- * 优先级:`dbPath` → `TOKENBUDDY_BUYER_STORE` env → `~/.tokenbuddy-store/buyer-store.db`。
29
+ * 优先级:`dbPath` → `TOKENBUDDY_BUYER_STORE` env → `TB_WORKDIR` env → `~/.config/tokenbuddy/buyer-store.db`。
30
30
  *
31
31
  * @param options 解析选项
32
32
  * @returns DB 文件绝对路径
@@ -35,7 +35,7 @@ export function resolveBuyerStorePath(options = {}) {
35
35
  if (options.dbPath) {
36
36
  return options.dbPath;
37
37
  }
38
- const root = options.root || process.env.TOKENBUDDY_BUYER_STORE || path.join(os.homedir(), ".tokenbuddy-store");
38
+ const root = options.root || process.env.TOKENBUDDY_BUYER_STORE || resolveTokenBuddyWorkdir();
39
39
  return path.join(root, "buyer-store.db");
40
40
  }
41
41
  /**
package/dist/src/cli.d.ts CHANGED
@@ -34,6 +34,7 @@ export interface LaunchdPlistOptions {
34
34
  proxyPort: number;
35
35
  sellerRegistryUrl: string;
36
36
  pathEnv?: string;
37
+ workdir?: string;
37
38
  clawtipProofCommand?: string;
38
39
  clawtipProofTimeoutMs?: number;
39
40
  }
package/dist/src/cli.js CHANGED
@@ -19,6 +19,7 @@ import { DEFAULT_CLAWTIP_BOOTSTRAP_URL, fetchClawtipBootstrap, } from "./clawtip
19
19
  import { displayTerminalImage } from "./terminal-image.js";
20
20
  import { checkPackageUpdate, readInstalledPackageManifest, runPackageUpdate, } from "./package-update.js";
21
21
  import { DEFAULT_SELLER_REGISTRY_URL } from "./registry-trust.js";
22
+ import { resolveTokenBuddyWorkdir, resolveTokenBuddyWorkdirPath, TB_WORKDIR_ENV } from "./workdir.js";
22
23
  // @ts-ignore
23
24
  import qrcode from "qrcode-terminal";
24
25
  const CONTROL_PORT = 17820;
@@ -119,7 +120,7 @@ function launchControlUi(controlPort, pathname = "/") {
119
120
  return url;
120
121
  }
121
122
  function defaultProxydLogPath(kind) {
122
- const logDir = path.join(os.homedir(), ".tokenbuddy-store");
123
+ const logDir = resolveTokenBuddyWorkdir();
123
124
  fs.mkdirSync(logDir, { recursive: true });
124
125
  return path.join(logDir, `tb-proxyd.${kind}.log`);
125
126
  }
@@ -164,6 +165,9 @@ export function buildLaunchdPlistContent(options) {
164
165
  if (options.clawtipProofCommand?.trim()) {
165
166
  env.TB_PROXYD_CLAWTIP_PROOF_COMMAND = options.clawtipProofCommand.trim();
166
167
  }
168
+ if (options.workdir?.trim()) {
169
+ env[TB_WORKDIR_ENV] = options.workdir.trim();
170
+ }
167
171
  if (options.clawtipProofTimeoutMs !== undefined) {
168
172
  env.TB_PROXYD_CLAWTIP_PROOF_TIMEOUT_MS = String(options.clawtipProofTimeoutMs);
169
173
  }
@@ -383,8 +387,9 @@ export async function runWebInitLauncher(deps = {}) {
383
387
  if (platform === "darwin") {
384
388
  const plistDir = path.join(home, "Library", "LaunchAgents");
385
389
  const plistPath = path.join(plistDir, `${TOKENBUDDY_LAUNCHD_LABEL}.plist`);
386
- const stdoutPath = deps.stdoutPath ?? path.join(home, ".tokenbuddy-store", "tb-proxyd.stdout.log");
387
- const stderrPath = deps.stderrPath ?? path.join(home, ".tokenbuddy-store", "tb-proxyd.stderr.log");
390
+ const workdir = resolveTokenBuddyWorkdir({ homeDir: home });
391
+ const stdoutPath = deps.stdoutPath ?? resolveTokenBuddyWorkdirPath("tb-proxyd.stdout.log", { homeDir: home });
392
+ const stderrPath = deps.stderrPath ?? resolveTokenBuddyWorkdirPath("tb-proxyd.stderr.log", { homeDir: home });
388
393
  try {
389
394
  (deps.mkdirSync ?? fs.mkdirSync)(plistDir, { recursive: true });
390
395
  (deps.mkdirSync ?? fs.mkdirSync)(path.dirname(stdoutPath), { recursive: true });
@@ -394,6 +399,7 @@ export async function runWebInitLauncher(deps = {}) {
394
399
  nodePath: deps.nodePath ?? process.execPath,
395
400
  scriptPath: deps.scriptPath ?? tbProxydScriptPath(),
396
401
  pathEnv: deps.pathEnv,
402
+ workdir,
397
403
  stdoutPath,
398
404
  stderrPath,
399
405
  controlPort,
@@ -690,6 +696,33 @@ function stableModelChoices(models) {
690
696
  };
691
697
  });
692
698
  }
699
+ const OPENCODE_SUPPORTED_PROTOCOLS = ["chat_completions", "responses", "messages"];
700
+ function uniqueModelIds(models) {
701
+ const seen = new Set();
702
+ const output = [];
703
+ for (const model of models) {
704
+ const trimmed = model.trim();
705
+ if (!trimmed || seen.has(trimmed)) {
706
+ continue;
707
+ }
708
+ seen.add(trimmed);
709
+ output.push(trimmed);
710
+ }
711
+ return output;
712
+ }
713
+ function availableModelsByProtocol(models, protocols) {
714
+ const result = {};
715
+ for (const protocol of protocols) {
716
+ const modelIds = uniqueModelIds(filterCatalogByProtocol(models, protocol).map((entry) => entry.id));
717
+ if (modelIds.length > 0) {
718
+ result[protocol] = modelIds;
719
+ }
720
+ }
721
+ return result;
722
+ }
723
+ function filterCatalogByAnyProtocol(models, protocols) {
724
+ return models.filter((entry) => protocols.some((protocol) => entry.supportedProtocols.includes(protocol)));
725
+ }
693
726
  async function promptSellerRoutingPreference(catalog) {
694
727
  const healthySellers = catalog.sellers.filter((seller) => seller.status === "ok");
695
728
  const mode = await p.select({
@@ -854,6 +887,28 @@ async function promptSingleModelSelection(providerId, models) {
854
887
  selectionKind: "single-model",
855
888
  protocolPreference,
856
889
  defaultModel: selectedModel,
890
+ ...(protocolPreference ? {
891
+ availableModelsByProtocol: availableModelsByProtocol(models, [protocolPreference]),
892
+ } : {}),
893
+ };
894
+ }
895
+ async function promptOpenCodeModelSelection(models) {
896
+ const compatibleModels = filterCatalogByAnyProtocol(models, OPENCODE_SUPPORTED_PROTOCOLS);
897
+ const choices = stableModelChoices(compatibleModels);
898
+ if (choices.length === 0) {
899
+ throw new Error("no compatible models available for OpenCode");
900
+ }
901
+ const selectedModel = await p.select({
902
+ message: "Choose the default model for OpenCode:",
903
+ options: choices,
904
+ });
905
+ if (typeof selectedModel !== "string") {
906
+ throw new Error("default model selection was cancelled for OpenCode");
907
+ }
908
+ return {
909
+ selectionKind: "single-model",
910
+ defaultModel: selectedModel,
911
+ availableModelsByProtocol: availableModelsByProtocol(models, OPENCODE_SUPPORTED_PROTOCOLS),
857
912
  };
858
913
  }
859
914
  function defaultClaudeDisplayName(modelId) {
@@ -952,6 +1007,10 @@ async function promptProviderSelections(providerIds, catalog, sellerRouting) {
952
1007
  : catalog.models;
953
1008
  const selections = {};
954
1009
  for (const providerId of providerIds) {
1010
+ if (providerId === "opencode") {
1011
+ selections[providerId] = await promptOpenCodeModelSelection(baseModels);
1012
+ continue;
1013
+ }
955
1014
  const selectionKind = getProviderModelSelectionKind(providerId);
956
1015
  if (selectionKind === "claude-role-mapping") {
957
1016
  selections[providerId] = await promptClaudeCodeModelSelection(baseModels);
@@ -1799,8 +1858,9 @@ export function buildCli() {
1799
1858
  const sellerRegistryUrl = sellerRegistryUrlForInit();
1800
1859
  const nodePath = process.execPath;
1801
1860
  const scriptPath = tbProxydScriptPath();
1802
- const stdoutPath = path.join(home, ".tokenbuddy-store", "tb-proxyd.stdout.log");
1803
- const stderrPath = path.join(home, ".tokenbuddy-store", "tb-proxyd.stderr.log");
1861
+ const workdir = resolveTokenBuddyWorkdir({ homeDir: home });
1862
+ const stdoutPath = resolveTokenBuddyWorkdirPath("tb-proxyd.stdout.log", { homeDir: home });
1863
+ const stderrPath = resolveTokenBuddyWorkdirPath("tb-proxyd.stderr.log", { homeDir: home });
1804
1864
  fs.mkdirSync(path.dirname(stdoutPath), { recursive: true });
1805
1865
  fs.mkdirSync(path.dirname(stderrPath), { recursive: true });
1806
1866
  const plistContent = buildLaunchdPlistContent({
@@ -1813,6 +1873,7 @@ export function buildCli() {
1813
1873
  proxyPort,
1814
1874
  sellerRegistryUrl,
1815
1875
  pathEnv: process.env.PATH,
1876
+ workdir,
1816
1877
  clawtipProofCommand: defaultClawtipProofCommand(),
1817
1878
  clawtipProofTimeoutMs: process.env.TB_PROXYD_CLAWTIP_PROOF_TIMEOUT_MS
1818
1879
  ? Number(process.env.TB_PROXYD_CLAWTIP_PROOF_TIMEOUT_MS)
@@ -24,11 +24,7 @@ export interface DaemonConfig {
24
24
  /** test-only override for the existing tb init ClawTip activation runner. */
25
25
  clawtipWalletBootstrapStarter?: (payment: ClawtipBootstrapPayment, options?: {
26
26
  home?: string;
27
- }) => Promise<{
28
- orderFile: string;
29
- parsedOutput: ParsedClawtipOutput;
30
- payCredential?: string;
31
- }>;
27
+ }) => Promise<ClawtipActivationResult>;
32
28
  /** test-only override for waiting on the existing tb init ClawTip registration loop. */
33
29
  clawtipActivationWaiter?: (options?: WaitForClawtipActivationOptions) => Promise<boolean>;
34
30
  /** test-only override for bundled ClawTip static assets; false disables bundled assets. */
@@ -93,6 +89,7 @@ export declare class TokenbuddyDaemon {
93
89
  private ensureClawtipStaticAssets;
94
90
  private copyClawtipQrToStatic;
95
91
  private startClawtipActivationQr;
92
+ private startClawtipActivationWithRebindFallback;
96
93
  private scheduleClawtipActivationWait;
97
94
  private clawtipRechargeQr;
98
95
  private lastRegistrySnapshot;
@@ -130,6 +127,21 @@ export declare class TokenbuddyDaemon {
130
127
  private applyAutoProviderRoutingConfig;
131
128
  private autoProviderCanRoute;
132
129
  private providerModePayload;
130
+ private isReconnectProviderId;
131
+ private uniqueModelIds;
132
+ private reconnectProtocolForProvider;
133
+ private reconnectProtocolsForProvider;
134
+ private catalogModelSupportsProtocol;
135
+ private catalogModelsByProtocol;
136
+ private previousProviderDefaultModel;
137
+ private pickReconnectDefaultModel;
138
+ private reconnectProviderSelection;
139
+ private reconnectProviderSelections;
140
+ private manualProviderModelCatalog;
141
+ private discoverSellerModelCatalog;
142
+ private refreshEnabledManualProviderModels;
143
+ private filterAutoEffectiveCatalog;
144
+ private effectiveModelCatalog;
133
145
  private clientToolsSummary;
134
146
  private initRepairStatus;
135
147
  private initStateSnapshot;
@@ -270,4 +282,10 @@ export declare class TokenbuddyDaemon {
270
282
  */
271
283
  setLastRegistrySnapshotForTest(snapshot: SellerRegistryDocument | null): void;
272
284
  }
285
+ interface ClawtipActivationResult {
286
+ orderFile: string;
287
+ parsedOutput: ParsedClawtipOutput;
288
+ payCredential?: string;
289
+ }
290
+ export {};
273
291
  //# sourceMappingURL=daemon.d.ts.map