@tokenbuddy/tokenbuddy 1.0.11 → 1.0.13

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 (135) hide show
  1. package/dist/src/buyer-store.d.ts +61 -0
  2. package/dist/src/buyer-store.d.ts.map +1 -1
  3. package/dist/src/buyer-store.js +12 -0
  4. package/dist/src/buyer-store.js.map +1 -1
  5. package/dist/src/cli.d.ts +47 -0
  6. package/dist/src/cli.d.ts.map +1 -1
  7. package/dist/src/cli.js +287 -63
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/credit-tracker.d.ts +26 -0
  10. package/dist/src/credit-tracker.d.ts.map +1 -1
  11. package/dist/src/credit-tracker.js +8 -0
  12. package/dist/src/credit-tracker.js.map +1 -1
  13. package/dist/src/daemon.d.ts +29 -3
  14. package/dist/src/daemon.d.ts.map +1 -1
  15. package/dist/src/daemon.js +292 -65
  16. package/dist/src/daemon.js.map +1 -1
  17. package/dist/src/doctor-clawtip-wallet.d.ts +25 -0
  18. package/dist/src/doctor-clawtip-wallet.d.ts.map +1 -1
  19. package/dist/src/doctor-clawtip-wallet.js +13 -0
  20. package/dist/src/doctor-clawtip-wallet.js.map +1 -1
  21. package/dist/src/doctor-diagnostics.d.ts +63 -0
  22. package/dist/src/doctor-diagnostics.d.ts.map +1 -1
  23. package/dist/src/doctor-diagnostics.js +39 -1
  24. package/dist/src/doctor-diagnostics.js.map +1 -1
  25. package/dist/src/index.d.ts +4 -0
  26. package/dist/src/index.d.ts.map +1 -1
  27. package/dist/src/index.js +4 -0
  28. package/dist/src/index.js.map +1 -1
  29. package/dist/src/init-clawtip-activation.d.ts +103 -0
  30. package/dist/src/init-clawtip-activation.d.ts.map +1 -1
  31. package/dist/src/init-clawtip-activation.js +60 -0
  32. package/dist/src/init-clawtip-activation.js.map +1 -1
  33. package/dist/src/init-payment-options.d.ts +124 -0
  34. package/dist/src/init-payment-options.d.ts.map +1 -1
  35. package/dist/src/init-payment-options.js +68 -0
  36. package/dist/src/init-payment-options.js.map +1 -1
  37. package/dist/src/model-index.d.ts +9 -0
  38. package/dist/src/model-index.d.ts.map +1 -1
  39. package/dist/src/model-index.js.map +1 -1
  40. package/dist/src/prewarm-cache.d.ts +89 -0
  41. package/dist/src/prewarm-cache.d.ts.map +1 -1
  42. package/dist/src/prewarm-cache.js +14 -1
  43. package/dist/src/prewarm-cache.js.map +1 -1
  44. package/dist/src/prewarm-scheduler.d.ts +62 -3
  45. package/dist/src/prewarm-scheduler.d.ts.map +1 -1
  46. package/dist/src/prewarm-scheduler.js +39 -8
  47. package/dist/src/prewarm-scheduler.js.map +1 -1
  48. package/dist/src/provider-install.d.ts +89 -3
  49. package/dist/src/provider-install.d.ts.map +1 -1
  50. package/dist/src/provider-install.js +77 -17
  51. package/dist/src/provider-install.js.map +1 -1
  52. package/dist/src/route-failover.d.ts +48 -0
  53. package/dist/src/route-failover.d.ts.map +1 -1
  54. package/dist/src/route-failover.js.map +1 -1
  55. package/dist/src/seller-catalog.d.ts +158 -10
  56. package/dist/src/seller-catalog.d.ts.map +1 -1
  57. package/dist/src/seller-catalog.js +79 -5
  58. package/dist/src/seller-catalog.js.map +1 -1
  59. package/dist/src/seller-metadata-cache.d.ts +29 -0
  60. package/dist/src/seller-metadata-cache.d.ts.map +1 -0
  61. package/dist/src/seller-metadata-cache.js +71 -0
  62. package/dist/src/seller-metadata-cache.js.map +1 -0
  63. package/dist/src/seller-pool.d.ts +71 -0
  64. package/dist/src/seller-pool.d.ts.map +1 -1
  65. package/dist/src/seller-pool.js +6 -1
  66. package/dist/src/seller-pool.js.map +1 -1
  67. package/dist/src/seller-route-planner.d.ts +118 -0
  68. package/dist/src/seller-route-planner.d.ts.map +1 -0
  69. package/dist/src/seller-route-planner.js +160 -0
  70. package/dist/src/seller-route-planner.js.map +1 -0
  71. package/dist/src/seller-routing-config.d.ts +69 -0
  72. package/dist/src/seller-routing-config.d.ts.map +1 -0
  73. package/dist/src/seller-routing-config.js +164 -0
  74. package/dist/src/seller-routing-config.js.map +1 -0
  75. package/dist/src/seller-routing-strategy.d.ts +118 -0
  76. package/dist/src/seller-routing-strategy.d.ts.map +1 -0
  77. package/dist/src/seller-routing-strategy.js +183 -0
  78. package/dist/src/seller-routing-strategy.js.map +1 -0
  79. package/dist/src/stream-failover.d.ts +23 -0
  80. package/dist/src/stream-failover.d.ts.map +1 -1
  81. package/dist/src/stream-failover.js +4 -0
  82. package/dist/src/stream-failover.js.map +1 -1
  83. package/dist/src/tb-proxyd.js +7 -21
  84. package/dist/src/tb-proxyd.js.map +1 -1
  85. package/dist/src/terminal-detect.d.ts +51 -0
  86. package/dist/src/terminal-detect.d.ts.map +1 -1
  87. package/dist/src/terminal-detect.js +42 -0
  88. package/dist/src/terminal-detect.js.map +1 -1
  89. package/dist/src/terminal-image.d.ts +41 -0
  90. package/dist/src/terminal-image.d.ts.map +1 -1
  91. package/dist/src/terminal-image.js +15 -0
  92. package/dist/src/terminal-image.js.map +1 -1
  93. package/package.json +1 -1
  94. package/src/buyer-store.ts +61 -0
  95. package/src/cli.ts +330 -68
  96. package/src/credit-tracker.ts +26 -0
  97. package/src/daemon.ts +363 -72
  98. package/src/doctor-clawtip-wallet.ts +25 -0
  99. package/src/doctor-diagnostics.ts +63 -1
  100. package/src/index.ts +4 -0
  101. package/src/init-clawtip-activation.ts +103 -0
  102. package/src/init-payment-options.ts +124 -0
  103. package/src/model-index.ts +9 -0
  104. package/src/prewarm-cache.ts +99 -1
  105. package/src/prewarm-scheduler.ts +97 -12
  106. package/src/provider-install.ts +125 -25
  107. package/src/route-failover.ts +48 -0
  108. package/src/seller-catalog.ts +158 -12
  109. package/src/seller-metadata-cache.ts +91 -0
  110. package/src/seller-pool.ts +77 -1
  111. package/src/seller-route-planner.ts +323 -0
  112. package/src/seller-routing-config.ts +198 -0
  113. package/src/seller-routing-strategy.ts +316 -0
  114. package/src/stream-failover.ts +23 -0
  115. package/src/tb-proxyd.ts +7 -23
  116. package/src/terminal-detect.ts +51 -0
  117. package/src/terminal-image.ts +41 -0
  118. package/tests/cli-routing.test.ts +287 -0
  119. package/tests/daemon-classify.test.ts +431 -0
  120. package/tests/daemon-roles.test.ts +92 -0
  121. package/tests/seller-catalog-utilities.test.ts +70 -0
  122. package/tests/seller-metadata-cache.test.ts +89 -0
  123. package/tests/seller-route-planner.test.ts +150 -0
  124. package/tests/seller-routing-config.test.ts +111 -0
  125. package/tests/seller-routing-strategy.test.ts +166 -0
  126. package/tests/tokenbuddy.test.ts +447 -33
  127. /package/{src → tests}/credit-tracker.test.ts +0 -0
  128. /package/{src → tests}/model-index.test.ts +0 -0
  129. /package/{src → tests}/prewarm-cache.test.ts +0 -0
  130. /package/{src → tests}/prewarm-scheduler.test.ts +0 -0
  131. /package/{src → tests}/route-failover.test.ts +0 -0
  132. /package/{src → tests}/seller-catalog-413.test.ts +0 -0
  133. /package/{src → tests}/seller-pool.test.ts +0 -0
  134. /package/{src → tests}/stream-failover.test.ts +0 -0
  135. /package/{src → tests}/thousand-seller.test.ts +0 -0
@@ -4,14 +4,26 @@ import {
4
4
  type ClawtipWalletStatus,
5
5
  } from "./init-payment-options.js";
6
6
 
7
+ /**
8
+ * `tb doctor` 视角的 ClawTip 钱包就绪摘要:把 readiness 拍平成 boolean + 关键路径。
9
+ * `printDoctorClawtipWallet()` 负责渲染它。
10
+ */
7
11
  export interface DoctorClawtipWalletSummary {
12
+ /** 综合就绪状态(同 `inspectClawtipWalletReadiness().status`) */
8
13
  status: ClawtipWalletStatus;
14
+ /** `status === "ready"` 的便利布尔,UI 据此选择图标 */
9
15
  ready: boolean;
16
+ /** buyer-store 里是否保存了 ClawTip 支付 metadata */
10
17
  paymentMetadataPresent: boolean;
18
+ /** `~/.openclaw/configs/config.json` 是否存在 */
11
19
  walletConfigPresent: boolean;
20
+ /** `~/.openclaw/configs/` 目录是否存在 */
12
21
  configsDirExists: boolean;
22
+ /** 期望的 wallet config 绝对路径 */
13
23
  expectedPath: string;
24
+ /** 同目录里其它文件列表(按字典序) */
14
25
  alternatePaths: string[];
26
+ /** 人类可读说明(来自 readiness.message) */
15
27
  message: string;
16
28
  }
17
29
 
@@ -28,6 +40,12 @@ function clawtipWalletStatusIcon(status: ClawtipWalletStatus): string {
28
40
  return "🔘";
29
41
  }
30
42
 
43
+ /**
44
+ * 从 `BuyerStore` 读取当前 ClawTip 支付 metadata,再叠加 wallet config 探查结果,
45
+ * 生成 `tb doctor` 面板用的就绪摘要。
46
+ *
47
+ * @returns doctor 面板用的就绪摘要
48
+ */
31
49
  export function readDoctorClawtipWallet(): DoctorClawtipWalletSummary {
32
50
  const store = new BuyerStore();
33
51
  try {
@@ -47,6 +65,13 @@ export function readDoctorClawtipWallet(): DoctorClawtipWalletSummary {
47
65
  }
48
66
  }
49
67
 
68
+ /**
69
+ * 把 doctor 摘要渲染成多行人类可读输出,包含状态图标、metadata/config 缺失标记、
70
+ * 期望路径、同目录其它文件,以及按状态分支的"接下来怎么做"提示。
71
+ *
72
+ * @param wallet `readDoctorClawtipWallet()` 的输出
73
+ * @param writeLine 注入的单行写入函数(默认 `console.log`)
74
+ */
50
75
  export function printDoctorClawtipWallet(
51
76
  wallet: DoctorClawtipWalletSummary,
52
77
  writeLine: (line: string) => void,
@@ -19,11 +19,21 @@ import {
19
19
  type DoctorClawtipWalletSummary,
20
20
  } from "./doctor-clawtip-wallet.js";
21
21
 
22
+ /**
23
+ * `tb doctor` 输出里 provider 行的形状。
24
+ * 在 `ProviderCandidate` 之上叠加 buyer 端持久化的 runtime config(如果存在)。
25
+ */
22
26
  export interface DoctorProviderView extends ProviderCandidate {
27
+ /** 持久化的 provider runtime config(opencode 的 `defaultModel` 等) */
23
28
  runtimeConfig?: ProviderRuntimeConfig;
29
+ /** runtime config 的最近更新时间 */
24
30
  runtimeConfigUpdatedAt?: string;
25
31
  }
26
32
 
33
+ /**
34
+ * `tb doctor` 输出里 seller 行的形状。
35
+ * 字段命名与 `RegistrySeller` 对齐,但额外包含状态、错误信息、manifest sellerId 等。
36
+ */
27
37
  export interface DoctorSellerEntry {
28
38
  id: string;
29
39
  name?: string;
@@ -37,11 +47,19 @@ export interface DoctorSellerEntry {
37
47
  errorMessage?: string;
38
48
  }
39
49
 
50
+ /**
51
+ * `tb doctor` 完整诊断结果,由 `collectDoctorDiagnostics` 返回。
52
+ */
40
53
  export interface DoctorDiagnostics {
54
+ /** 控制面 / 代理端点可达性 */
41
55
  access: DoctorAccessSummary;
56
+ /** Clawtip 钱包信息 */
42
57
  clawtipWallet: DoctorClawtipWalletSummary;
58
+ /** 模型目录汇总 */
43
59
  models: DoctorModelsSummary;
60
+ /** provider 安装 / 配置状态 */
44
61
  providers: DoctorProviderView[];
62
+ /** seller 注册表 + 探测结果 */
45
63
  sellers: DoctorSellersSummary;
46
64
  }
47
65
 
@@ -90,6 +108,9 @@ interface DoctorModelsResponse {
90
108
  sellers?: DoctorSellerEntry[];
91
109
  }
92
110
 
111
+ /**
112
+ * `tb doctor` 模型表的单行:按 model id 聚合 seller 的折扣 / 价格区间。
113
+ */
93
114
  export interface DoctorModelSummaryEntry {
94
115
  id: string;
95
116
  sellerCount: number;
@@ -163,6 +184,9 @@ type DoctorSellersSummary = {
163
184
  error?: string;
164
185
  };
165
186
 
187
+ /**
188
+ * `tb doctor` 模型目录汇总:原始 `data` + 聚合后的 `grouped`(按 model id 分组)。
189
+ */
166
190
  export type DoctorModelsSummary = {
167
191
  available: boolean;
168
192
  count: number;
@@ -235,7 +259,6 @@ function providerRuntimeSummary(runtimeConfig?: ProviderRuntimeConfig): string |
235
259
  return [
236
260
  runtimeConfig.protocolPreference,
237
261
  runtimeConfig.defaultModel,
238
- runtimeConfig.sellerId ? `seller=${runtimeConfig.sellerId}` : undefined,
239
262
  ].filter(Boolean).join(" · ");
240
263
  }
241
264
 
@@ -647,6 +670,12 @@ function printDoctorSellers(sellers: DoctorSellersSummary, writeLine: (line: str
647
670
  }
648
671
  }
649
672
 
673
+ /**
674
+ * 打印模型目录汇总(写到 `writeLine`,默认 stdout)。
675
+ *
676
+ * @param models 模型汇总
677
+ * @param writeLine 自定义输出函数(测试可注入)
678
+ */
650
679
  export function printDoctorModelsSummary(
651
680
  models: DoctorModelsSummary,
652
681
  writeLine: (line: string) => void = defaultWriter,
@@ -676,6 +705,11 @@ export function printDoctorModelsSummary(
676
705
  writeLine(table.toString());
677
706
  }
678
707
 
708
+ /**
709
+ * 读取 buyer 端已知的 provider 列表,把 runtime config(如果有)合并到 `DoctorProviderView`。
710
+ *
711
+ * @returns 每个 provider 的 view(数组)
712
+ */
679
713
  export function readDoctorProviders(): DoctorProviderView[] {
680
714
  const store = new BuyerStore();
681
715
  try {
@@ -703,6 +737,12 @@ export function readDoctorProviders(): DoctorProviderView[] {
703
737
  }
704
738
  }
705
739
 
740
+ /**
741
+ * 打印 provider 列表("Programming Terminals" 段)。
742
+ *
743
+ * @param providers provider view 列表
744
+ * @param writeLine 自定义输出函数
745
+ */
706
746
  export function printDoctorProviders(
707
747
  providers: DoctorProviderView[],
708
748
  writeLine: (line: string) => void = defaultWriter,
@@ -725,6 +765,15 @@ export function printDoctorProviders(
725
765
  }
726
766
  }
727
767
 
768
+ /**
769
+ * 收集 `tb doctor` 需要的全部诊断信息。
770
+ * 1. 并发拉取 daemon health / sellers / models / proxy models / registry 五个端点。
771
+ * 2. 合并 registry 配置 + 探测结果 + catalog 拉取结果。
772
+ * 3. 返回完整 `DoctorDiagnostics`(供 JSON 消费者或自定义渲染使用)。
773
+ *
774
+ * @param options 收集选项(端口 / daemon 状态 / registry URL / providers)
775
+ * @returns 完整诊断结果
776
+ */
728
777
  export async function collectDoctorDiagnostics(options: DoctorCollectOptions): Promise<DoctorDiagnostics> {
729
778
  const fetches = startDoctorFetches(
730
779
  options.controlPort,
@@ -762,6 +811,13 @@ export async function collectDoctorDiagnostics(options: DoctorCollectOptions): P
762
811
  };
763
812
  }
764
813
 
814
+ /**
815
+ * 单独收集 `tb models` 需要的模型汇总(不包含 providers)。
816
+ * daemon 拉到的数据缺价格时,回退用 `discoverSellerBackedModels` 重新拉 catalog。
817
+ *
818
+ * @param options 同 `collectDoctorDiagnostics`(去掉 providers)
819
+ * @returns 模型汇总
820
+ */
765
821
  export async function collectDoctorModelsSummary(options: Omit<DoctorCollectOptions, "providers">): Promise<DoctorModelsSummary> {
766
822
  const diagnostics = await collectDoctorDiagnostics({
767
823
  ...options,
@@ -789,6 +845,12 @@ export async function collectDoctorModelsSummary(options: Omit<DoctorCollectOpti
789
845
 
790
846
  }
791
847
 
848
+ /**
849
+ * 渐进式渲染 `tb doctor`:分阶段打印(clawtip → access → sellers → models),边等边输出,避免用户长时间看不到输出。
850
+ *
851
+ * @param options 渲染选项(含写入函数)
852
+ * @returns 完整 `DoctorDiagnostics`
853
+ */
792
854
  export async function renderDoctorDiagnosticsProgressively(options: DoctorRenderOptions): Promise<DoctorDiagnostics> {
793
855
  const writeLine = options.writeLine || defaultWriter;
794
856
  const clawtipWallet = readDoctorClawtipWallet();
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { buildCli } from "./cli.js";
2
2
 
3
+ /**
4
+ * CLI 入口:构造 commander program、解析 `process.argv`、异步执行。
5
+ * 异常通过 `process.exitCode = 1` 透传,避免在 daemon 未启动等情况下刷红 stderr(已通过 `code` 字段分类)。
6
+ */
3
7
  export function run() {
4
8
  const program = buildCli();
5
9
  program.parseAsync(process.argv).catch((error: unknown) => {
@@ -13,43 +13,86 @@ const SLEEP_SLICE_MS = 200;
13
13
  const CLAWTIP_MIN_CLI_VERSION = "1.0.4";
14
14
  const CLAWTIP_MIN_SKILL_VERSION = "1.0.12";
15
15
 
16
+ /**
17
+ * 一次 ClawTip 引导支付的输入参数(由上游 service / wallet-bootstrap 解析后传入)。
18
+ * 用来写本地 order 文件并触发 `npx @clawtip/clawtip-cli pay` 流程。
19
+ */
16
20
  export interface ClawtipBootstrapPayment {
21
+ /** 订单号(也是 order JSON 文件名) */
17
22
  orderNo: string;
23
+ /** 金额(分) */
18
24
  amountFen: number;
25
+ /** 收款方标识,可选 */
19
26
  payTo?: string;
27
+ /** 上游加密负载,可选 */
20
28
  encryptedData?: string;
29
+ /** 上游分配的 indicator(用于切到正确目录) */
21
30
  indicator: string;
31
+ /** 上游 slug,可选 */
22
32
  slug?: string;
33
+ /** skill ID(写入 order 的 `skill-id` 字段) */
23
34
  skillId?: string;
35
+ /** 订单描述,可选 */
24
36
  description?: string;
37
+ /** 资源 URL(写入 order 的 `resource_url` 字段) */
25
38
  resourceUrl?: string;
26
39
  }
27
40
 
41
+ /**
42
+ * `parseClawtipCliOutput()` 的输出:从 ClawTip CLI 文本输出中提取的结构化字段。
43
+ * 字段都为"尽力推断":缺失时为 undefined,由调用方决定如何处理。
44
+ */
28
45
  export interface ParsedClawtipOutput {
46
+ /** 鉴权 URL(可被扫码或浏览器打开) */
29
47
  authUrl?: string;
48
+ /** 设备 ID(用于 check-register 轮询) */
30
49
  clawtipId?: string;
50
+ /** QR 图片本地路径 */
31
51
  mediaPath?: string;
52
+ /** 下游/上游的失败信息(已 sanitize 过的中文提示) */
32
53
  failureMessage?: string;
54
+ /** 是否需要用户做钱包授权(含 QR、authUrl、扫码关键字等) */
33
55
  requiresWalletAuth: boolean;
56
+ /** 钱包是否已就绪(register success / tokeninfo / config.json 等关键字) */
34
57
  walletReady: boolean;
35
58
  }
36
59
 
60
+ /**
61
+ * `waitForClawtipActivationConfirmation()` 的可注入依赖与可调参数。
62
+ * 默认实现使用 `@clack/prompts` 的 `cancel`、本机 `~/.openclaw/configs/` 探查和 npx `check-register`。
63
+ */
37
64
  export interface WaitForClawtipActivationOptions {
65
+ /** wallet config 探查函数(默认 `inspectOpenClawWalletConfig`) */
38
66
  inspectWalletConfig?: () => OpenClawWalletConfigState;
67
+ /** 外部取消信号(与 SIGINT 监听器互不冲突) */
39
68
  isCancelled?: () => boolean;
69
+ /** check-register 用的 ClawTip 设备 ID(必填才会触发轮询) */
40
70
  clawtipId?: string;
71
+ /** check-register 命令执行函数(默认 `npx @clawtip/clawtip-cli check-register`) */
41
72
  checkRegister?: (clawtipId: string) => Promise<void>;
73
+ /** 取消时的回调(默认 `@clack/prompts.cancel`) */
42
74
  cancel?: (message?: string) => void;
75
+ /** 轮询间隔(毫秒),默认 2000 */
43
76
  pollIntervalMs?: number;
77
+ /** sleep 实现(测试可注入) */
44
78
  sleep?: (ms: number) => Promise<void>;
45
79
  }
46
80
 
81
+ /**
82
+ * `startClawtipWalletBootstrap()` 的可注入依赖。
83
+ */
47
84
  export interface StartClawtipWalletBootstrapOptions {
85
+ /** 注入的 home 目录(默认 `process.env.HOME` / `os.homedir()`) */
48
86
  home?: string;
87
+ /** 注入的 ClawTip CLI 执行函数(默认 `npx @clawtip/clawtip-cli`) */
49
88
  runClawtipCommand?: (args: string[]) => Promise<string>;
50
89
  }
51
90
 
91
+ /**
92
+ * `checkOpenClawRuntime()` 的可注入依赖。
93
+ */
52
94
  export interface CheckOpenClawRuntimeOptions {
95
+ /** 注入的 OpenClaw CLI 执行函数(默认 `openclaw --version`) */
53
96
  runOpenClawCommand?: (args: string[]) => Promise<string>;
54
97
  }
55
98
 
@@ -286,6 +329,13 @@ function extractClawtipIdFromUrl(value: string): string | undefined {
286
329
  return undefined;
287
330
  }
288
331
 
332
+ /**
333
+ * 从 ClawTip CLI 的 stdout/stderr 合并文本中提取关键信息。
334
+ * 同时识别 authUrl / clawtipId / mediaPath / 失败关键字 / 是否需要钱包授权 / 钱包是否就绪。
335
+ *
336
+ * @param output 合并后的 ClawTip CLI 输出
337
+ * @returns 结构化解析结果
338
+ */
289
339
  export function parseClawtipCliOutput(output: string): ParsedClawtipOutput {
290
340
  const authUrl = findValueAfterKeys(output, ["authUrl", "auth_url", "auth url"]) || findUrlInOutput(output);
291
341
  const mediaPath = findMediaPathInOutput(output);
@@ -314,6 +364,15 @@ export function parseClawtipCliOutput(output: string): ParsedClawtipOutput {
314
364
  };
315
365
  }
316
366
 
367
+ /**
368
+ * 取得 ClawTip 引导流程中要展示给用户的 QR 图片本地路径:
369
+ * 优先用 `parsedOutput.mediaPath`;缺省时抛错(因为首次绑定必须先走 wallet QR 流程)。
370
+ *
371
+ * @param parsedOutput `parseClawtipCliOutput()` 的结果
372
+ * @param orderFile 当前订单的 JSON 文件路径(用于错误消息)
373
+ * @returns QR 图片绝对路径
374
+ * @throws 当 `parsedOutput.mediaPath` 缺失时
375
+ */
317
376
  export function resolveClawtipQrMediaPath(parsedOutput: ParsedClawtipOutput, orderFile: string): string {
318
377
  if (parsedOutput.mediaPath) {
319
378
  return parsedOutput.mediaPath;
@@ -327,6 +386,13 @@ export function resolveClawtipQrMediaPath(parsedOutput: ParsedClawtipOutput, ord
327
386
  );
328
387
  }
329
388
 
389
+ /**
390
+ * 读 order JSON 里的 `payCredential`(snake/camel 兼容)。
391
+ * 用于在 `startClawtipWalletBootstrap()` 之后回传给上游 service。
392
+ *
393
+ * @param orderFile order JSON 的绝对路径
394
+ * @returns 非空 `payCredential` 字符串;文件不存在 / 字段缺失时返回 `undefined`
395
+ */
330
396
  export function readClawtipPayCredential(orderFile: string): string | undefined {
331
397
  if (!fs.existsSync(orderFile)) {
332
398
  return undefined;
@@ -336,6 +402,15 @@ export function readClawtipPayCredential(orderFile: string): string | undefined
336
402
  return typeof credential === "string" && credential.trim() ? credential : undefined;
337
403
  }
338
404
 
405
+ /**
406
+ * 把 ClawTip 引导支付所需的所有字段写成本地 order JSON:
407
+ * `~/.openclaw/skills/orders/<indicator>/<orderNo>.json`。
408
+ * 自动创建父目录。
409
+ *
410
+ * @param payment 引导支付参数
411
+ * @param home 用户 home 目录(默认 `process.env.HOME` / `os.homedir()`)
412
+ * @returns 写入的 order JSON 绝对路径
413
+ */
339
414
  export function writeClawtipOrderFile(
340
415
  payment: ClawtipBootstrapPayment,
341
416
  home: string = defaultHomeDir(),
@@ -357,6 +432,19 @@ export function writeClawtipOrderFile(
357
432
  return orderFile;
358
433
  }
359
434
 
435
+ /**
436
+ * 启动 ClawTip 钱包引导流程:
437
+ * 1. 写本地 order JSON
438
+ * 2. 调用 `npx @clawtip/clawtip-cli pay` 完成下单
439
+ * 3. 解析输出;若无 `mediaPath` 会在 `~/.openclaw/workspace/clawtip/qrcode/` 里
440
+ * 找最近生成的 QR 图作为兜底
441
+ * 4. 返回 order 文件路径 + 解析结果 + 回读到的 `payCredential`
442
+ *
443
+ * @param payment 引导支付参数
444
+ * @param options 可注入的 home / runClawtipCommand
445
+ * @returns 引导结果
446
+ * @throws 当 `parsedOutput.failureMessage` 非空时(即 ClawTip 下单失败)
447
+ */
360
448
  export async function startClawtipWalletBootstrap(
361
449
  payment: ClawtipBootstrapPayment,
362
450
  options: StartClawtipWalletBootstrapOptions = {},
@@ -394,6 +482,13 @@ export async function startClawtipWalletBootstrap(
394
482
  };
395
483
  }
396
484
 
485
+ /**
486
+ * 校验本机 `openclaw` CLI 可用:执行 `openclaw --version` 并返回去空白的版本字符串。
487
+ *
488
+ * @param options 可注入的 runOpenClawCommand
489
+ * @returns OpenClaw 版本字符串
490
+ * @throws 当 CLI 缺失或退出码非 0 时
491
+ */
397
492
  export async function checkOpenClawRuntime(
398
493
  options: CheckOpenClawRuntimeOptions = {},
399
494
  ): Promise<string> {
@@ -439,6 +534,14 @@ async function sleepUntilNextPoll(
439
534
  return !isCancelled();
440
535
  }
441
536
 
537
+ /**
538
+ * 阻塞等待 ClawTip 钱包激活完成:轮询 `~/.openclaw/configs/config.json` 是否出现;
539
+ * 拿到 `clawtipId` 时还会调 `check-register` 主动推一次。
540
+ * 监听 `SIGINT` 与外部 `isCancelled` 触发取消。
541
+ *
542
+ * @param options 可注入的 inspect / checkRegister / cancel / sleep / pollInterval
543
+ * @returns 钱包就绪返回 `true`;被取消返回 `false`
544
+ */
442
545
  export async function waitForClawtipActivationConfirmation(
443
546
  options: WaitForClawtipActivationOptions = {},
444
547
  ): Promise<boolean> {
@@ -5,63 +5,121 @@ import * as path from "path";
5
5
  import type { PaymentConfig } from "./buyer-store.js";
6
6
  import type { ProviderCandidate } from "./provider-install.js";
7
7
 
8
+ /** 当前 init 流程已支持的支付方式。当前仅 JD ClawTip 扫码支付。 */
8
9
  export type InitPaymentMethod = "clawtip";
9
10
 
11
+ /**
12
+ * `INIT_PAYMENT_OPTIONS` 里的单条目,喂给 `@clack/prompts` 的 select 组件。
13
+ */
10
14
  export interface InitPaymentOption {
15
+ /** 选项值,对应 `InitPaymentMethod` */
11
16
  value: InitPaymentMethod;
17
+ /** 展示给用户的标签 */
12
18
  label: string;
19
+ /** 选段下方的小字提示,可选 */
13
20
  hint?: string;
14
21
  }
15
22
 
23
+ /**
24
+ * `INIT_COMING_SOON_PAYMENT_OPTIONS` 里的单条目:未上线的支付方式占位。
25
+ * 仅用于"更多支付方式"提示框展示,不可被选中。
26
+ */
16
27
  export interface ComingSoonPaymentOption {
28
+ /** 选项 ID(仅作展示和未来上线时引用) */
17
29
  id: string;
30
+ /** 人类可读名称 */
18
31
  label: string;
19
32
  }
20
33
 
34
+ /**
35
+ * buyer-store 里 ClawTip 支付的已有绑定(持久化的关键字段)。
36
+ * `resourceUrl` / `orderNo` 至少存在一个才视为"可复用绑定"。
37
+ */
21
38
  export interface ExistingClawtipBinding {
39
+ /** 原始 config 对象(透传回 buyer-store 时用) */
22
40
  config: Record<string, unknown>;
41
+ /** 资源 URL(可选) */
23
42
  resourceUrl?: string;
43
+ /** 订单号(可选) */
24
44
  orderNo?: string;
25
45
  }
26
46
 
47
+ /**
48
+ * `buildInitTerminalSelectionState()` 的输出单条,喂给 `@clack/prompts` 的多选组件。
49
+ */
27
50
  export interface InitTerminalOption {
51
+ /** 选项值(通常是 `provider.id` 或 `"other"`) */
28
52
  value: string;
53
+ /** 展示给用户的标签 */
29
54
  label: string;
55
+ /** 选段下方的小字提示,可选 */
30
56
  hint?: string;
31
57
  }
32
58
 
59
+ /**
60
+ * `buildInitTerminalSelectionState()` 的输出:已配置 / 待配置 两个分组。
61
+ * `options` 末尾会追加 `OTHER_TERMINAL_OPTION`,给"我自己接"留口。
62
+ */
33
63
  export interface InitTerminalSelectionState {
64
+ /** 已配置完成的 provider(`status === "configured"`) */
34
65
  installed: InitTerminalOption[];
66
+ /** 待配置的 provider,末尾追加 `OTHER_TERMINAL_OPTION` */
35
67
  options: InitTerminalOption[];
36
68
  }
37
69
 
70
+ /**
71
+ * `inspectOpenClawWalletConfig()` 的输出:本地 OpenClaw 钱包目录的探查结果。
72
+ * `alternatePaths` 是 `~/.openclaw/configs/` 里除 `config.json` 之外的文件,
73
+ * 用于在 missing 状态下提示用户"目录里其实有别的东西"。
74
+ */
38
75
  export interface OpenClawWalletConfigState {
76
+ /** `~/.openclaw/configs/config.json` 的预期路径 */
39
77
  expectedPath: string;
78
+ /** `~/.openclaw/configs/` 目录是否存在 */
40
79
  configsDirExists: boolean;
80
+ /** `expectedPath` 是否真实存在 */
41
81
  exists: boolean;
82
+ /** 同目录里其它文件路径(按文件名字典序) */
42
83
  alternatePaths: string[];
43
84
  }
44
85
 
86
+ /**
87
+ * ClawTip 钱包就绪状态四象限:
88
+ * - `ready`:saved binding + 本地 wallet config 同时存在
89
+ * - `metadata_missing_wallet`:只 saved binding,wallet 缺
90
+ * - `wallet_missing_metadata`:只 wallet config,metadata 缺
91
+ * - `missing`:都缺
92
+ */
45
93
  export type ClawtipWalletStatus =
46
94
  | "ready"
47
95
  | "metadata_missing_wallet"
48
96
  | "wallet_missing_metadata"
49
97
  | "missing";
50
98
 
99
+ /**
100
+ * `inspectClawtipWalletReadiness()` 的输出:状态枚举 + 物证 + 给用户看的说明。
101
+ */
51
102
  export interface ClawtipWalletReadiness {
103
+ /** 综合状态 */
52
104
  status: ClawtipWalletStatus;
105
+ /** 本地 OpenClaw wallet config 探查结果 */
53
106
  walletConfig: OpenClawWalletConfigState;
107
+ /** buyer-store 里的 saved binding(如果有) */
54
108
  savedBinding?: ExistingClawtipBinding;
109
+ /** 既 saved 又本地存在的可复用 binding(如果有) */
55
110
  reusableBinding?: ExistingClawtipBinding;
111
+ /** 面向 init 面板 / doctor 的人类可读说明 */
56
112
  message: string;
57
113
  }
58
114
 
115
+ /** init 流程里"我自己接"的兜底选项 */
59
116
  export const OTHER_TERMINAL_OPTION: InitTerminalOption = {
60
117
  value: "other",
61
118
  label: "Other",
62
119
  hint: "I will configure my own terminal using the TokenBuddy proxy endpoints."
63
120
  };
64
121
 
122
+ /** init 流程已支持的可选支付方式(喂给 `@clack/prompts` 的 select 组件) */
65
123
  export const INIT_PAYMENT_OPTIONS: InitPaymentOption[] = [
66
124
  {
67
125
  value: "clawtip",
@@ -70,6 +128,7 @@ export const INIT_PAYMENT_OPTIONS: InitPaymentOption[] = [
70
128
  }
71
129
  ];
72
130
 
131
+ /** init 流程"更多支付方式"占位列表(仅展示用,不可选) */
73
132
  export const INIT_COMING_SOON_PAYMENT_OPTIONS = [
74
133
  {
75
134
  id: "wechat-pay",
@@ -85,6 +144,11 @@ export const INIT_COMING_SOON_PAYMENT_OPTIONS = [
85
144
  }
86
145
  ] satisfies ReadonlyArray<ComingSoonPaymentOption>;
87
146
 
147
+ /**
148
+ * 在 init 流程的支付选择面板下追加"更多支付方式(接入中)"提示。
149
+ *
150
+ * @param note 注入的 note 函数(默认 `@clack/prompts.note`,测试可替换)
151
+ */
88
152
  export function noteInitComingSoonPayments(
89
153
  note: (message?: string, title?: string) => void = p.note
90
154
  ): void {
@@ -94,6 +158,13 @@ export function noteInitComingSoonPayments(
94
158
  );
95
159
  }
96
160
 
161
+ /**
162
+ * 把 provider-install 阶段的探测结果转成 `InitTerminalSelectionState`:
163
+ * 已配置完成的归 `installed`;已检测到但未配置的归 `options`,末尾追加 `OTHER_TERMINAL_OPTION`。
164
+ *
165
+ * @param providers provider-install 的探测结果
166
+ * @returns init 流程的多选状态
167
+ */
97
168
  export function buildInitTerminalSelectionState(providers: ProviderCandidate[]): InitTerminalSelectionState {
98
169
  const installed = providers
99
170
  .filter((provider) => provider.detected && provider.status === "configured")
@@ -117,6 +188,12 @@ export function buildInitTerminalSelectionState(providers: ProviderCandidate[]):
117
188
  };
118
189
  }
119
190
 
191
+ /**
192
+ * 校验 init 多选面板的返回值:必须至少选一个 terminal 或选择 `Other`。
193
+ *
194
+ * @param selected `@clack/prompts` 多选返回值
195
+ * @returns 错误消息字符串;合法时返回 `undefined`
196
+ */
120
197
  export function validateInitTerminalSelection(selected: string[] | undefined): string | undefined {
121
198
  if (!selected || selected.length === 0) {
122
199
  return "Select at least one terminal or choose Other.";
@@ -124,6 +201,12 @@ export function validateInitTerminalSelection(selected: string[] | undefined): s
124
201
  return undefined;
125
202
  }
126
203
 
204
+ /**
205
+ * 把"已配置完成的 terminal"列表渲染成 init 面板顶部的摘要文案。
206
+ *
207
+ * @param installed 已配置完成的 terminal 列表
208
+ * @returns 多行 markdown 摘要;空列表时返回 `undefined`
209
+ */
127
210
  export function buildInstalledTerminalMessage(installed: InitTerminalOption[]): string | undefined {
128
211
  if (installed.length === 0) {
129
212
  return undefined;
@@ -137,6 +220,15 @@ function defaultHomeDir(): string {
137
220
  return process.env.HOME || os.homedir();
138
221
  }
139
222
 
223
+ /**
224
+ * 探查 `~/.openclaw/configs/` 目录及其默认 wallet 配置:
225
+ * - 是否存在 configs 目录
226
+ * - `config.json` 是否存在
227
+ * - 同目录里其它文件列表(用于 missing 状态下的提示)
228
+ *
229
+ * @param home 用户 home 目录(默认读 `process.env.HOME` 或 `os.homedir()`)
230
+ * @returns 探查结果
231
+ */
140
232
  export function inspectOpenClawWalletConfig(home: string = defaultHomeDir()): OpenClawWalletConfigState {
141
233
  const configsDir = path.join(home, ".openclaw", "configs");
142
234
  const expectedPath = path.join(configsDir, "config.json");
@@ -168,6 +260,13 @@ function stringField(value: unknown): string | undefined {
168
260
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
169
261
  }
170
262
 
263
+ /**
264
+ * 从 `PaymentConfig` 里识别 ClawTip 已保存的关键字段(`resourceUrl` / `orderNo`)。
265
+ * 两者都没有时返回 `undefined`。
266
+ *
267
+ * @param payment buyer-store 读到的 payment 配置
268
+ * @returns 识别出的 binding;无关键字段时返回 `undefined`
269
+ */
171
270
  export function detectExistingClawtipBinding(payment: PaymentConfig | undefined): ExistingClawtipBinding | undefined {
172
271
  if (!payment || payment.method !== "clawtip" || !payment.config || typeof payment.config !== "object") {
173
272
  return undefined;
@@ -185,6 +284,14 @@ export function detectExistingClawtipBinding(payment: PaymentConfig | undefined)
185
284
  };
186
285
  }
187
286
 
287
+ /**
288
+ * 判断已保存的 ClawTip binding 能否被本地 OpenClaw wallet config 复用:
289
+ * wallet config 必须存在并且 saved binding 至少有一个关键字段。
290
+ *
291
+ * @param payment buyer-store 读到的 payment 配置
292
+ * @param walletConfig `inspectOpenClawWalletConfig()` 的结果
293
+ * @returns 可复用 binding;不可复用时返回 `undefined`
294
+ */
188
295
  export function detectReusableClawtipBinding(
189
296
  payment: PaymentConfig | undefined,
190
297
  walletConfig: OpenClawWalletConfigState
@@ -195,6 +302,14 @@ export function detectReusableClawtipBinding(
195
302
  return detectExistingClawtipBinding(payment);
196
303
  }
197
304
 
305
+ /**
306
+ * 综合判断 ClawTip 钱包的就绪状态:把 saved binding、wallet config 和 reuse 关系
307
+ * 合并成 `ClawtipWalletReadiness`,供 init 面板、doctor 和激活流程共同消费。
308
+ *
309
+ * @param payment buyer-store 读到的 payment 配置
310
+ * @param walletConfig 本地 wallet 探查结果(默认 `inspectOpenClawWalletConfig()`)
311
+ * @returns 就绪状态 + 物证 + 人类可读说明
312
+ */
198
313
  export function inspectClawtipWalletReadiness(
199
314
  payment: PaymentConfig | undefined,
200
315
  walletConfig: OpenClawWalletConfigState = inspectOpenClawWalletConfig(),
@@ -236,6 +351,15 @@ export function inspectClawtipWalletReadiness(
236
351
  };
237
352
  }
238
353
 
354
+ /**
355
+ * 把 init 流程的若干条摘要拼成"成功"提示:
356
+ * - 顶部一行 emoji + 成功消息
357
+ * - 中间每条摘要 `- ` 开头
358
+ * - 末尾附 `tb doctor` 提示
359
+ *
360
+ * @param summaryLines 摘要行(会先 trim,空行会被跳过)
361
+ * @returns 多行 markdown 摘要
362
+ */
239
363
  export function buildInitSuccessMessage(summaryLines: string[] = []): string {
240
364
  const lines = ["✅ TokenBuddy setup completed successfully."];
241
365
  for (const line of summaryLines) {
@@ -21,10 +21,19 @@ function normalizeModelId(modelId: string): string {
21
21
  * - `sellers` is a snapshot array of the matching registry entries. Order
22
22
  * follows the registry's `defaultSeller` preference then declaration order.
23
23
  */
24
+ /**
25
+ * `ModelIndex.resolve()` 的返回结果。
26
+ * - `empty` 等价于 `matched = false && sellers = []`,路由层应当视为"无可用 seller"。
27
+ * - `sellers` 是按 `defaultSeller` 优先 + 声明顺序排序后的快照数组。
28
+ */
24
29
  export interface ModelIndexResolution {
30
+ /** 原始请求的模型 ID(未归一化) */
25
31
  modelId: string;
32
+ /** 索引里是否至少有一个 seller 命中(已归一化匹配) */
26
33
  matched: boolean;
34
+ /** 命中的 seller 列表(已按 default + 声明顺序排序的快照) */
27
35
  sellers: RegistrySeller[];
36
+ /** 当前索引里 `models` 字段缺失的 seller 数(用于诊断和告警) */
28
37
  missingModelsFlag: number;
29
38
  }
30
39