@echoclaw/echo-0g 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +1175 -0
  2. package/dist/0g-compute/account.d.ts +36 -0
  3. package/dist/0g-compute/account.d.ts.map +1 -0
  4. package/dist/0g-compute/account.js +85 -0
  5. package/dist/0g-compute/account.js.map +1 -0
  6. package/dist/0g-compute/bridge.d.ts +16 -0
  7. package/dist/0g-compute/bridge.d.ts.map +1 -0
  8. package/dist/0g-compute/bridge.js +40 -0
  9. package/dist/0g-compute/bridge.js.map +1 -0
  10. package/dist/0g-compute/broker-factory.d.ts +19 -0
  11. package/dist/0g-compute/broker-factory.d.ts.map +1 -0
  12. package/dist/0g-compute/broker-factory.js +65 -0
  13. package/dist/0g-compute/broker-factory.js.map +1 -0
  14. package/dist/0g-compute/constants.d.ts +10 -0
  15. package/dist/0g-compute/constants.d.ts.map +1 -0
  16. package/dist/0g-compute/constants.js +12 -0
  17. package/dist/0g-compute/constants.js.map +1 -0
  18. package/dist/0g-compute/monitor.d.ts +43 -0
  19. package/dist/0g-compute/monitor.d.ts.map +1 -0
  20. package/dist/0g-compute/monitor.js +302 -0
  21. package/dist/0g-compute/monitor.js.map +1 -0
  22. package/dist/0g-compute/pricing.d.ts +43 -0
  23. package/dist/0g-compute/pricing.d.ts.map +1 -0
  24. package/dist/0g-compute/pricing.js +53 -0
  25. package/dist/0g-compute/pricing.js.map +1 -0
  26. package/dist/0g-compute/sdk-bridge.cjs +17 -0
  27. package/dist/0g-compute/sdk-bridge.cjs.map +1 -0
  28. package/dist/0g-compute/sdk-bridge.d.cts +9 -0
  29. package/dist/0g-compute/sdk-bridge.d.cts.map +1 -0
  30. package/dist/0g-compute/smoke-test.d.ts +11 -0
  31. package/dist/0g-compute/smoke-test.d.ts.map +1 -0
  32. package/dist/0g-compute/smoke-test.js +172 -0
  33. package/dist/0g-compute/smoke-test.js.map +1 -0
  34. package/dist/bot/daemon.d.ts +34 -0
  35. package/dist/bot/daemon.d.ts.map +1 -0
  36. package/dist/bot/daemon.js +386 -0
  37. package/dist/bot/daemon.js.map +1 -0
  38. package/dist/bot/executor.d.ts +14238 -0
  39. package/dist/bot/executor.d.ts.map +1 -0
  40. package/dist/bot/executor.js +183 -0
  41. package/dist/bot/executor.js.map +1 -0
  42. package/dist/bot/nonce-queue.d.ts +20 -0
  43. package/dist/bot/nonce-queue.d.ts.map +1 -0
  44. package/dist/bot/nonce-queue.js +41 -0
  45. package/dist/bot/nonce-queue.js.map +1 -0
  46. package/dist/bot/notify.d.ts +15 -0
  47. package/dist/bot/notify.d.ts.map +1 -0
  48. package/dist/bot/notify.js +98 -0
  49. package/dist/bot/notify.js.map +1 -0
  50. package/dist/bot/orders.d.ts +30 -0
  51. package/dist/bot/orders.d.ts.map +1 -0
  52. package/dist/bot/orders.js +172 -0
  53. package/dist/bot/orders.js.map +1 -0
  54. package/dist/bot/state.d.ts +14 -0
  55. package/dist/bot/state.d.ts.map +1 -0
  56. package/dist/bot/state.js +109 -0
  57. package/dist/bot/state.js.map +1 -0
  58. package/dist/bot/stream.d.ts +28 -0
  59. package/dist/bot/stream.d.ts.map +1 -0
  60. package/dist/bot/stream.js +96 -0
  61. package/dist/bot/stream.js.map +1 -0
  62. package/dist/bot/triggers.d.ts +17 -0
  63. package/dist/bot/triggers.d.ts.map +1 -0
  64. package/dist/bot/triggers.js +95 -0
  65. package/dist/bot/triggers.js.map +1 -0
  66. package/dist/bot/types.d.ts +199 -0
  67. package/dist/bot/types.d.ts.map +1 -0
  68. package/dist/bot/types.js +12 -0
  69. package/dist/bot/types.js.map +1 -0
  70. package/dist/chainscan/client.d.ts +28 -0
  71. package/dist/chainscan/client.d.ts.map +1 -0
  72. package/dist/chainscan/client.js +361 -0
  73. package/dist/chainscan/client.js.map +1 -0
  74. package/dist/chainscan/constants.d.ts +15 -0
  75. package/dist/chainscan/constants.d.ts.map +1 -0
  76. package/dist/chainscan/constants.js +15 -0
  77. package/dist/chainscan/constants.js.map +1 -0
  78. package/dist/chainscan/types.d.ts +148 -0
  79. package/dist/chainscan/types.d.ts.map +1 -0
  80. package/dist/chainscan/types.js +2 -0
  81. package/dist/chainscan/types.js.map +1 -0
  82. package/dist/chainscan/validation.d.ts +35 -0
  83. package/dist/chainscan/validation.d.ts.map +1 -0
  84. package/dist/chainscan/validation.js +97 -0
  85. package/dist/chainscan/validation.js.map +1 -0
  86. package/dist/cli.d.ts +7 -0
  87. package/dist/cli.d.ts.map +1 -0
  88. package/dist/cli.js +328 -0
  89. package/dist/cli.js.map +1 -0
  90. package/dist/commands/0g-compute.d.ts +21 -0
  91. package/dist/commands/0g-compute.d.ts.map +1 -0
  92. package/dist/commands/0g-compute.js +850 -0
  93. package/dist/commands/0g-compute.js.map +1 -0
  94. package/dist/commands/chainscan.d.ts +17 -0
  95. package/dist/commands/chainscan.d.ts.map +1 -0
  96. package/dist/commands/chainscan.js +605 -0
  97. package/dist/commands/chainscan.js.map +1 -0
  98. package/dist/commands/config.d.ts +3 -0
  99. package/dist/commands/config.d.ts.map +1 -0
  100. package/dist/commands/config.js +251 -0
  101. package/dist/commands/config.js.map +1 -0
  102. package/dist/commands/echobook.d.ts +17 -0
  103. package/dist/commands/echobook.d.ts.map +1 -0
  104. package/dist/commands/echobook.js +905 -0
  105. package/dist/commands/echobook.js.map +1 -0
  106. package/dist/commands/jaine-subgraph.d.ts +3 -0
  107. package/dist/commands/jaine-subgraph.d.ts.map +1 -0
  108. package/dist/commands/jaine-subgraph.js +565 -0
  109. package/dist/commands/jaine-subgraph.js.map +1 -0
  110. package/dist/commands/jaine.d.ts +3 -0
  111. package/dist/commands/jaine.d.ts.map +1 -0
  112. package/dist/commands/jaine.js +1415 -0
  113. package/dist/commands/jaine.js.map +1 -0
  114. package/dist/commands/marketmaker.d.ts +6 -0
  115. package/dist/commands/marketmaker.d.ts.map +1 -0
  116. package/dist/commands/marketmaker.js +451 -0
  117. package/dist/commands/marketmaker.js.map +1 -0
  118. package/dist/commands/send.d.ts +3 -0
  119. package/dist/commands/send.d.ts.map +1 -0
  120. package/dist/commands/send.js +229 -0
  121. package/dist/commands/send.js.map +1 -0
  122. package/dist/commands/setup.d.ts +3 -0
  123. package/dist/commands/setup.d.ts.map +1 -0
  124. package/dist/commands/setup.js +263 -0
  125. package/dist/commands/setup.js.map +1 -0
  126. package/dist/commands/slop-app.d.ts +9 -0
  127. package/dist/commands/slop-app.d.ts.map +1 -0
  128. package/dist/commands/slop-app.js +708 -0
  129. package/dist/commands/slop-app.js.map +1 -0
  130. package/dist/commands/slop-stream.d.ts +9 -0
  131. package/dist/commands/slop-stream.d.ts.map +1 -0
  132. package/dist/commands/slop-stream.js +99 -0
  133. package/dist/commands/slop-stream.js.map +1 -0
  134. package/dist/commands/slop.d.ts +3 -0
  135. package/dist/commands/slop.d.ts.map +1 -0
  136. package/dist/commands/slop.js +1053 -0
  137. package/dist/commands/slop.js.map +1 -0
  138. package/dist/commands/wallet.d.ts +13 -0
  139. package/dist/commands/wallet.d.ts.map +1 -0
  140. package/dist/commands/wallet.js +748 -0
  141. package/dist/commands/wallet.js.map +1 -0
  142. package/dist/config/paths.d.ts +13 -0
  143. package/dist/config/paths.d.ts.map +1 -0
  144. package/dist/config/paths.js +33 -0
  145. package/dist/config/paths.js.map +1 -0
  146. package/dist/config/store.d.ts +48 -0
  147. package/dist/config/store.d.ts.map +1 -0
  148. package/dist/config/store.js +113 -0
  149. package/dist/config/store.js.map +1 -0
  150. package/dist/constants/chain.d.ts +57 -0
  151. package/dist/constants/chain.d.ts.map +1 -0
  152. package/dist/constants/chain.js +51 -0
  153. package/dist/constants/chain.js.map +1 -0
  154. package/dist/echobook/api.d.ts +38 -0
  155. package/dist/echobook/api.d.ts.map +1 -0
  156. package/dist/echobook/api.js +86 -0
  157. package/dist/echobook/api.js.map +1 -0
  158. package/dist/echobook/auth.d.ts +31 -0
  159. package/dist/echobook/auth.d.ts.map +1 -0
  160. package/dist/echobook/auth.js +93 -0
  161. package/dist/echobook/auth.js.map +1 -0
  162. package/dist/echobook/comments.d.ts +26 -0
  163. package/dist/echobook/comments.d.ts.map +1 -0
  164. package/dist/echobook/comments.js +20 -0
  165. package/dist/echobook/comments.js.map +1 -0
  166. package/dist/echobook/follows.d.ts +19 -0
  167. package/dist/echobook/follows.d.ts.map +1 -0
  168. package/dist/echobook/follows.js +21 -0
  169. package/dist/echobook/follows.js.map +1 -0
  170. package/dist/echobook/jwtCache.d.ts +15 -0
  171. package/dist/echobook/jwtCache.d.ts.map +1 -0
  172. package/dist/echobook/jwtCache.js +63 -0
  173. package/dist/echobook/jwtCache.js.map +1 -0
  174. package/dist/echobook/notifications.d.ts +30 -0
  175. package/dist/echobook/notifications.d.ts.map +1 -0
  176. package/dist/echobook/notifications.js +26 -0
  177. package/dist/echobook/notifications.js.map +1 -0
  178. package/dist/echobook/points.d.ts +35 -0
  179. package/dist/echobook/points.d.ts.map +1 -0
  180. package/dist/echobook/points.js +20 -0
  181. package/dist/echobook/points.js.map +1 -0
  182. package/dist/echobook/posts.d.ts +46 -0
  183. package/dist/echobook/posts.d.ts.map +1 -0
  184. package/dist/echobook/posts.js +43 -0
  185. package/dist/echobook/posts.js.map +1 -0
  186. package/dist/echobook/profile.d.ts +29 -0
  187. package/dist/echobook/profile.d.ts.map +1 -0
  188. package/dist/echobook/profile.js +14 -0
  189. package/dist/echobook/profile.js.map +1 -0
  190. package/dist/echobook/submolts.d.ts +22 -0
  191. package/dist/echobook/submolts.d.ts.map +1 -0
  192. package/dist/echobook/submolts.js +24 -0
  193. package/dist/echobook/submolts.js.map +1 -0
  194. package/dist/echobook/tradeProof.d.ts +21 -0
  195. package/dist/echobook/tradeProof.d.ts.map +1 -0
  196. package/dist/echobook/tradeProof.js +14 -0
  197. package/dist/echobook/tradeProof.js.map +1 -0
  198. package/dist/echobook/votes.d.ts +17 -0
  199. package/dist/echobook/votes.d.ts.map +1 -0
  200. package/dist/echobook/votes.js +20 -0
  201. package/dist/echobook/votes.js.map +1 -0
  202. package/dist/errors.d.ts +125 -0
  203. package/dist/errors.d.ts.map +1 -0
  204. package/dist/errors.js +147 -0
  205. package/dist/errors.js.map +1 -0
  206. package/dist/intents/store.d.ts +22 -0
  207. package/dist/intents/store.d.ts.map +1 -0
  208. package/dist/intents/store.js +76 -0
  209. package/dist/intents/store.js.map +1 -0
  210. package/dist/intents/types.d.ts +21 -0
  211. package/dist/intents/types.d.ts.map +1 -0
  212. package/dist/intents/types.js +2 -0
  213. package/dist/intents/types.js.map +1 -0
  214. package/dist/jaine/abi/erc20.d.ts +90 -0
  215. package/dist/jaine/abi/erc20.d.ts.map +1 -0
  216. package/dist/jaine/abi/erc20.js +65 -0
  217. package/dist/jaine/abi/erc20.js.map +1 -0
  218. package/dist/jaine/abi/factory.d.ts +38 -0
  219. package/dist/jaine/abi/factory.d.ts.map +1 -0
  220. package/dist/jaine/abi/factory.js +26 -0
  221. package/dist/jaine/abi/factory.js.map +1 -0
  222. package/dist/jaine/abi/index.d.ts +11 -0
  223. package/dist/jaine/abi/index.d.ts.map +1 -0
  224. package/dist/jaine/abi/index.js +11 -0
  225. package/dist/jaine/abi/index.js.map +1 -0
  226. package/dist/jaine/abi/nftManager.d.ts +282 -0
  227. package/dist/jaine/abi/nftManager.d.ts.map +1 -0
  228. package/dist/jaine/abi/nftManager.js +182 -0
  229. package/dist/jaine/abi/nftManager.js.map +1 -0
  230. package/dist/jaine/abi/pool.d.ts +77 -0
  231. package/dist/jaine/abi/pool.d.ts.map +1 -0
  232. package/dist/jaine/abi/pool.js +56 -0
  233. package/dist/jaine/abi/pool.js.map +1 -0
  234. package/dist/jaine/abi/quoter.d.ts +84 -0
  235. package/dist/jaine/abi/quoter.d.ts.map +1 -0
  236. package/dist/jaine/abi/quoter.js +53 -0
  237. package/dist/jaine/abi/quoter.js.map +1 -0
  238. package/dist/jaine/abi/router.d.ts +135 -0
  239. package/dist/jaine/abi/router.d.ts.map +1 -0
  240. package/dist/jaine/abi/router.js +88 -0
  241. package/dist/jaine/abi/router.js.map +1 -0
  242. package/dist/jaine/abi/w0g.d.ts +41 -0
  243. package/dist/jaine/abi/w0g.d.ts.map +1 -0
  244. package/dist/jaine/abi/w0g.js +34 -0
  245. package/dist/jaine/abi/w0g.js.map +1 -0
  246. package/dist/jaine/allowance.d.ts +48 -0
  247. package/dist/jaine/allowance.d.ts.map +1 -0
  248. package/dist/jaine/allowance.js +192 -0
  249. package/dist/jaine/allowance.js.map +1 -0
  250. package/dist/jaine/coreTokens.d.ts +32 -0
  251. package/dist/jaine/coreTokens.d.ts.map +1 -0
  252. package/dist/jaine/coreTokens.js +91 -0
  253. package/dist/jaine/coreTokens.js.map +1 -0
  254. package/dist/jaine/pathEncoding.d.ts +39 -0
  255. package/dist/jaine/pathEncoding.d.ts.map +1 -0
  256. package/dist/jaine/pathEncoding.js +98 -0
  257. package/dist/jaine/pathEncoding.js.map +1 -0
  258. package/dist/jaine/paths.d.ts +11 -0
  259. package/dist/jaine/paths.d.ts.map +1 -0
  260. package/dist/jaine/paths.js +20 -0
  261. package/dist/jaine/paths.js.map +1 -0
  262. package/dist/jaine/poolCache.d.ts +47 -0
  263. package/dist/jaine/poolCache.d.ts.map +1 -0
  264. package/dist/jaine/poolCache.js +195 -0
  265. package/dist/jaine/poolCache.js.map +1 -0
  266. package/dist/jaine/routing.d.ts +41 -0
  267. package/dist/jaine/routing.d.ts.map +1 -0
  268. package/dist/jaine/routing.js +247 -0
  269. package/dist/jaine/routing.js.map +1 -0
  270. package/dist/jaine/subgraph/client.d.ts +26 -0
  271. package/dist/jaine/subgraph/client.d.ts.map +1 -0
  272. package/dist/jaine/subgraph/client.js +201 -0
  273. package/dist/jaine/subgraph/client.js.map +1 -0
  274. package/dist/jaine/subgraph/constants.d.ts +9 -0
  275. package/dist/jaine/subgraph/constants.d.ts.map +1 -0
  276. package/dist/jaine/subgraph/constants.js +9 -0
  277. package/dist/jaine/subgraph/constants.js.map +1 -0
  278. package/dist/jaine/subgraph/queries.d.ts +21 -0
  279. package/dist/jaine/subgraph/queries.d.ts.map +1 -0
  280. package/dist/jaine/subgraph/queries.js +304 -0
  281. package/dist/jaine/subgraph/queries.js.map +1 -0
  282. package/dist/jaine/subgraph/types.d.ts +209 -0
  283. package/dist/jaine/subgraph/types.d.ts.map +1 -0
  284. package/dist/jaine/subgraph/types.js +7 -0
  285. package/dist/jaine/subgraph/types.js.map +1 -0
  286. package/dist/jaine/userTokens.d.ts +27 -0
  287. package/dist/jaine/userTokens.d.ts.map +1 -0
  288. package/dist/jaine/userTokens.js +89 -0
  289. package/dist/jaine/userTokens.js.map +1 -0
  290. package/dist/openclaw/config.d.ts +43 -0
  291. package/dist/openclaw/config.d.ts.map +1 -0
  292. package/dist/openclaw/config.js +231 -0
  293. package/dist/openclaw/config.js.map +1 -0
  294. package/dist/openclaw/hooks-client.d.ts +24 -0
  295. package/dist/openclaw/hooks-client.d.ts.map +1 -0
  296. package/dist/openclaw/hooks-client.js +119 -0
  297. package/dist/openclaw/hooks-client.js.map +1 -0
  298. package/dist/slop/abi/factory.d.ts +128 -0
  299. package/dist/slop/abi/factory.d.ts.map +1 -0
  300. package/dist/slop/abi/factory.js +70 -0
  301. package/dist/slop/abi/factory.js.map +1 -0
  302. package/dist/slop/abi/feeCollector.d.ts +95 -0
  303. package/dist/slop/abi/feeCollector.d.ts.map +1 -0
  304. package/dist/slop/abi/feeCollector.js +71 -0
  305. package/dist/slop/abi/feeCollector.js.map +1 -0
  306. package/dist/slop/abi/index.d.ts +5 -0
  307. package/dist/slop/abi/index.d.ts.map +1 -0
  308. package/dist/slop/abi/index.js +5 -0
  309. package/dist/slop/abi/index.js.map +1 -0
  310. package/dist/slop/abi/registry.d.ts +135 -0
  311. package/dist/slop/abi/registry.d.ts.map +1 -0
  312. package/dist/slop/abi/registry.js +90 -0
  313. package/dist/slop/abi/registry.js.map +1 -0
  314. package/dist/slop/abi/token.d.ts +320 -0
  315. package/dist/slop/abi/token.d.ts.map +1 -0
  316. package/dist/slop/abi/token.js +251 -0
  317. package/dist/slop/abi/token.js.map +1 -0
  318. package/dist/slop/auth.d.ts +19 -0
  319. package/dist/slop/auth.d.ts.map +1 -0
  320. package/dist/slop/auth.js +92 -0
  321. package/dist/slop/auth.js.map +1 -0
  322. package/dist/slop/jwtCache.d.ts +27 -0
  323. package/dist/slop/jwtCache.d.ts.map +1 -0
  324. package/dist/slop/jwtCache.js +91 -0
  325. package/dist/slop/jwtCache.js.map +1 -0
  326. package/dist/slop/quote.d.ts +80 -0
  327. package/dist/slop/quote.d.ts.map +1 -0
  328. package/dist/slop/quote.js +174 -0
  329. package/dist/slop/quote.js.map +1 -0
  330. package/dist/utils/canonicalJson.d.ts +8 -0
  331. package/dist/utils/canonicalJson.d.ts.map +1 -0
  332. package/dist/utils/canonicalJson.js +20 -0
  333. package/dist/utils/canonicalJson.js.map +1 -0
  334. package/dist/utils/env.d.ts +11 -0
  335. package/dist/utils/env.d.ts.map +1 -0
  336. package/dist/utils/env.js +20 -0
  337. package/dist/utils/env.js.map +1 -0
  338. package/dist/utils/http.d.ts +19 -0
  339. package/dist/utils/http.d.ts.map +1 -0
  340. package/dist/utils/http.js +61 -0
  341. package/dist/utils/http.js.map +1 -0
  342. package/dist/utils/logger.d.ts +4 -0
  343. package/dist/utils/logger.d.ts.map +1 -0
  344. package/dist/utils/logger.js +21 -0
  345. package/dist/utils/logger.js.map +1 -0
  346. package/dist/utils/output.d.ts +29 -0
  347. package/dist/utils/output.d.ts.map +1 -0
  348. package/dist/utils/output.js +51 -0
  349. package/dist/utils/output.js.map +1 -0
  350. package/dist/utils/rateLimit.d.ts +22 -0
  351. package/dist/utils/rateLimit.d.ts.map +1 -0
  352. package/dist/utils/rateLimit.js +58 -0
  353. package/dist/utils/rateLimit.js.map +1 -0
  354. package/dist/utils/respond.d.ts +19 -0
  355. package/dist/utils/respond.d.ts.map +1 -0
  356. package/dist/utils/respond.js +25 -0
  357. package/dist/utils/respond.js.map +1 -0
  358. package/dist/utils/ui.d.ts +38 -0
  359. package/dist/utils/ui.d.ts.map +1 -0
  360. package/dist/utils/ui.js +126 -0
  361. package/dist/utils/ui.js.map +1 -0
  362. package/dist/wallet/client.d.ts +4 -0
  363. package/dist/wallet/client.d.ts.map +1 -0
  364. package/dist/wallet/client.js +53 -0
  365. package/dist/wallet/client.js.map +1 -0
  366. package/dist/wallet/keystore.d.ts +22 -0
  367. package/dist/wallet/keystore.d.ts.map +1 -0
  368. package/dist/wallet/keystore.js +111 -0
  369. package/dist/wallet/keystore.js.map +1 -0
  370. package/package.json +63 -0
  371. package/skills/echo/SKILL.md +1121 -0
@@ -0,0 +1,850 @@
1
+ /**
2
+ * `echo 0g-compute` (alias: `echo 0g`) — 0G Compute Network integration.
3
+ *
4
+ * Commands:
5
+ * setup [--json] Read-only readiness check
6
+ * providers [--detailed] [--json] List available services
7
+ * ledger status [--json] Ledger + sub-account balances
8
+ * ledger deposit <amount> --yes [--json] Deposit to ledger
9
+ * ledger fund --provider <addr> --amount <0G> --yes [--json] Transfer to sub-account
10
+ * provider <addr> info [--json] Provider metadata + ack status
11
+ * provider <addr> ack --yes [--json] Acknowledge provider signer
12
+ * provider <addr> verify [--json] Verify TEE
13
+ * api-key create --provider <addr> --token-id <n> [--json] Create persistent API key
14
+ * api-key revoke --provider <addr> --token-id <n> --yes Revoke API key
15
+ * api-key revoke-all --provider <addr> --yes Revoke all API keys
16
+ * openclaw use --provider <addr> --token-id <n> --yes Patch openclaw.json
17
+ * monitor start/stop/status Balance monitor daemon
18
+ */
19
+ import { Command } from "commander";
20
+ import { isAddress, getAddress, formatUnits, parseUnits } from "viem";
21
+ import { EchoError, ErrorCodes } from "../errors.js";
22
+ import { isHeadless, writeJsonSuccess } from "../utils/output.js";
23
+ import { respond } from "../utils/respond.js";
24
+ import { getAuthenticatedBroker } from "../0g-compute/broker-factory.js";
25
+ import { withSuppressedConsole } from "../0g-compute/bridge.js";
26
+ import { normalizeSubAccount, normalizeInferTuple, serializeSubAccount } from "../0g-compute/account.js";
27
+ import { calculateProviderPricing, formatPricePerMTokens } from "../0g-compute/pricing.js";
28
+ import { patchOpenclawConfig } from "../openclaw/config.js";
29
+ import logger from "../utils/logger.js";
30
+ // ── Helpers ──────────────────────────────────────────────────────────
31
+ function requireAddress(raw, label) {
32
+ if (!isAddress(raw)) {
33
+ throw new EchoError(ErrorCodes.INVALID_ADDRESS, `Invalid ${label} address: ${raw}`);
34
+ }
35
+ return getAddress(raw);
36
+ }
37
+ function requirePositiveNumber(raw, label) {
38
+ const n = Number(raw);
39
+ if (!Number.isFinite(n) || n <= 0) {
40
+ throw new EchoError(ErrorCodes.INVALID_AMOUNT, `Invalid ${label}: ${raw} (must be > 0)`);
41
+ }
42
+ return n;
43
+ }
44
+ function requireTokenId(raw) {
45
+ const n = Number(raw);
46
+ if (!Number.isInteger(n) || n < 0 || n > 254) {
47
+ throw new EchoError(ErrorCodes.INVALID_AMOUNT, `Invalid token-id: ${raw} (must be 0-254)`);
48
+ }
49
+ return n;
50
+ }
51
+ function requireYes(yes, action) {
52
+ if (!yes) {
53
+ throw new EchoError(ErrorCodes.CONFIRMATION_REQUIRED, `On-chain action requires confirmation: ${action}`, "Add --yes to confirm.");
54
+ }
55
+ }
56
+ function redactToken(token) {
57
+ if (token.startsWith("app-sk-"))
58
+ return "app-sk-***";
59
+ return "***";
60
+ }
61
+ /** Serialize bigint fields in SDK responses for JSON output. */
62
+ function serializeBigInts(obj) {
63
+ if (typeof obj === "bigint")
64
+ return obj.toString();
65
+ if (Array.isArray(obj))
66
+ return obj.map(serializeBigInts);
67
+ if (obj !== null && typeof obj === "object") {
68
+ const result = {};
69
+ for (const [k, v] of Object.entries(obj)) {
70
+ result[k] = serializeBigInts(v);
71
+ }
72
+ return result;
73
+ }
74
+ return obj;
75
+ }
76
+ // ── Command tree ─────────────────────────────────────────────────────
77
+ export function create0gComputeCommand() {
78
+ const root = new Command("0g-compute")
79
+ .alias("0g")
80
+ .description("0G Compute Network: inference, funding, and provider management");
81
+ // ── setup ────────────────────────────────────────────────────────
82
+ root
83
+ .command("setup")
84
+ .description("Readiness check (read-only, no transactions)")
85
+ .option("--json", "JSON output")
86
+ .action(async () => {
87
+ const { requireWalletAndKeystore } = await import("../bot/executor.js");
88
+ const checks = {};
89
+ // 1. Wallet
90
+ let walletAddress = null;
91
+ let walletHint;
92
+ try {
93
+ const { address } = requireWalletAndKeystore();
94
+ walletAddress = address;
95
+ checks.wallet = { ok: true, address };
96
+ }
97
+ catch (err) {
98
+ walletHint = err instanceof EchoError ? err.hint : undefined;
99
+ checks.wallet = { ok: false, error: err instanceof EchoError ? err.message : String(err) };
100
+ }
101
+ if (!walletAddress) {
102
+ respond({
103
+ data: { checks },
104
+ ui: { type: "warn", title: "0G Compute Setup", body: walletHint ?? "Wallet not configured. Run: echo wallet create" },
105
+ });
106
+ return;
107
+ }
108
+ // 2. Wallet 0G balance
109
+ try {
110
+ const { getPublicClient } = await import("../wallet/client.js");
111
+ const client = getPublicClient();
112
+ const balance = await client.getBalance({ address: walletAddress });
113
+ checks.walletBalance = { ok: true, balance: formatUnits(balance, 18) + " 0G" };
114
+ }
115
+ catch (err) {
116
+ checks.walletBalance = { ok: false, error: err instanceof Error ? err.message : String(err) };
117
+ }
118
+ // 3. Broker init
119
+ try {
120
+ const broker = await getAuthenticatedBroker();
121
+ checks.broker = { ok: true };
122
+ // 4. Ledger
123
+ try {
124
+ const ledger = await withSuppressedConsole(() => broker.ledger.getLedger());
125
+ checks.ledger = { ok: true, info: serializeBigInts(ledger) };
126
+ }
127
+ catch {
128
+ checks.ledger = { ok: false, hint: "Run: echo 0g-compute ledger deposit <amount> --yes" };
129
+ }
130
+ // 5. Services available
131
+ const services = await withSuppressedConsole(() => broker.inference.listService());
132
+ checks.services = { ok: true, count: services.length };
133
+ }
134
+ catch (err) {
135
+ checks.broker = { ok: false, error: err instanceof Error ? err.message : String(err) };
136
+ }
137
+ const allOk = Object.values(checks).every((c) => typeof c === "object" && c !== null && c.ok === true);
138
+ respond({
139
+ data: { ready: allOk, checks },
140
+ ui: {
141
+ type: allOk ? "success" : "warn",
142
+ title: "0G Compute Setup",
143
+ body: allOk
144
+ ? "All checks passed. Ready to use 0G Compute."
145
+ : "Some checks failed. Review the output above.",
146
+ },
147
+ });
148
+ });
149
+ // ── providers ────────────────────────────────────────────────────
150
+ root
151
+ .command("providers")
152
+ .description("List available 0G Compute services (requires wallet)")
153
+ .option("--detailed", "Include detailed info (TEE status, health, pricing)")
154
+ .option("--with-balances", "Include sub-account balances per provider (requires --detailed)")
155
+ .option("--json", "JSON output")
156
+ .action(async (options) => {
157
+ if (options.withBalances && !options.detailed) {
158
+ throw new EchoError(ErrorCodes.INVALID_AMOUNT, "--with-balances requires --detailed", "Use: echo 0g providers --detailed --with-balances");
159
+ }
160
+ const broker = await getAuthenticatedBroker();
161
+ if (options.detailed) {
162
+ const services = await withSuppressedConsole(() => broker.inference.listServiceWithDetail());
163
+ // Build enriched provider data
164
+ const enriched = [];
165
+ for (const svc of services) {
166
+ const inputPrice = svc.inputPrice;
167
+ const outputPrice = svc.outputPrice;
168
+ const pricing = calculateProviderPricing(inputPrice, outputPrice);
169
+ const entry = {
170
+ ...serializeBigInts(svc),
171
+ inputPricePerMTokens: formatPricePerMTokens(inputPrice),
172
+ outputPricePerMTokens: formatPricePerMTokens(outputPrice),
173
+ recommendedMinLockedOg: pricing.recommendedMinLockedOg,
174
+ recommendedAlertLockedOg: pricing.recommendedAlertLockedOg,
175
+ };
176
+ // Optionally fetch sub-account balance
177
+ if (options.withBalances) {
178
+ try {
179
+ const account = await withSuppressedConsole(() => broker.inference.getAccount(svc.provider));
180
+ const normalized = normalizeSubAccount(account);
181
+ const needsTopUp = normalized.lockedOg < pricing.recommendedMinLockedOg;
182
+ entry.totalOg = normalized.totalOg;
183
+ entry.pendingRefundOg = normalized.pendingRefundOg;
184
+ entry.lockedOg = normalized.lockedOg;
185
+ entry.needsTopUp = needsTopUp;
186
+ entry.suggestedTopUpOg = needsTopUp
187
+ ? Math.max(0, pricing.recommendedMinLockedOg - normalized.lockedOg)
188
+ : 0;
189
+ }
190
+ catch {
191
+ entry.totalOg = null;
192
+ entry.pendingRefundOg = null;
193
+ entry.lockedOg = null;
194
+ entry.needsTopUp = null;
195
+ entry.suggestedTopUpOg = null;
196
+ }
197
+ }
198
+ enriched.push(entry);
199
+ }
200
+ if (isHeadless()) {
201
+ writeJsonSuccess({ providers: enriched });
202
+ }
203
+ else {
204
+ if (services.length === 0) {
205
+ process.stderr.write("No providers found.\n");
206
+ return;
207
+ }
208
+ process.stderr.write(`Found ${services.length} provider(s):\n\n`);
209
+ for (let i = 0; i < services.length; i++) {
210
+ const svc = services[i];
211
+ const e = enriched[i];
212
+ const inputPrice = svc.inputPrice;
213
+ const outputPrice = svc.outputPrice;
214
+ process.stderr.write(` Provider: ${svc.provider}\n`);
215
+ process.stderr.write(` Model: ${svc.model}\n`);
216
+ process.stderr.write(` Type: ${svc.serviceType}\n`);
217
+ process.stderr.write(` URL: ${svc.url}\n`);
218
+ process.stderr.write(` Input: ${formatPricePerMTokens(inputPrice)} 0G/M tokens\n`);
219
+ process.stderr.write(` Output: ${formatPricePerMTokens(outputPrice)} 0G/M tokens\n`);
220
+ process.stderr.write(` TEE ack: ${svc.teeSignerAcknowledged}\n`);
221
+ process.stderr.write(` Recommended locked: ~${e.recommendedMinLockedOg.toFixed(3)} 0G` +
222
+ ` (alert when < ${e.recommendedAlertLockedOg.toFixed(3)} 0G)\n`);
223
+ if (options.withBalances && e.lockedOg != null) {
224
+ const locked = e.lockedOg;
225
+ const recMin = e.recommendedMinLockedOg;
226
+ const ok = locked >= recMin;
227
+ const status = ok
228
+ ? "OK"
229
+ : `TOP UP ~${e.suggestedTopUpOg.toFixed(3)} 0G`;
230
+ process.stderr.write(` Locked: ${locked.toFixed(4)} 0G (recommended: ${recMin.toFixed(3)} 0G) — ${status}\n`);
231
+ }
232
+ else if (options.withBalances) {
233
+ process.stderr.write(" Locked: (no sub-account)\n");
234
+ }
235
+ process.stderr.write("\n");
236
+ }
237
+ }
238
+ }
239
+ else {
240
+ const services = await withSuppressedConsole(() => broker.inference.listService());
241
+ if (isHeadless()) {
242
+ writeJsonSuccess({ providers: services.map((s) => serializeBigInts(s)) });
243
+ }
244
+ else {
245
+ if (services.length === 0) {
246
+ process.stderr.write("No providers found.\n");
247
+ return;
248
+ }
249
+ process.stderr.write(`Provider | Model | URL\n`);
250
+ process.stderr.write("-".repeat(110) + "\n");
251
+ for (const svc of services) {
252
+ const provider = String(svc[0]).slice(0, 42).padEnd(42);
253
+ const model = String(svc[6]).padEnd(30);
254
+ const url = String(svc[2]);
255
+ process.stderr.write(`${provider} | ${model} | ${url}\n`);
256
+ }
257
+ process.stderr.write(`\nTotal: ${services.length}\n`);
258
+ }
259
+ }
260
+ });
261
+ // ── ledger ───────────────────────────────────────────────────────
262
+ const ledger = root.command("ledger").description("Manage 0G Compute ledger");
263
+ ledger
264
+ .command("status")
265
+ .description("Show ledger status and sub-account balances")
266
+ .option("--json", "JSON output")
267
+ .action(async () => {
268
+ const broker = await getAuthenticatedBroker();
269
+ // Try getLedgerWithDetail (returns sub-accounts without extra RPC)
270
+ let ledgerInfo;
271
+ let subAccounts = [];
272
+ try {
273
+ const detail = await withSuppressedConsole(() => broker.ledger
274
+ .getLedgerWithDetail());
275
+ ledgerInfo = detail.ledgerInfo;
276
+ if (Array.isArray(detail.infers)) {
277
+ subAccounts = detail.infers.map((t) => normalizeInferTuple(t));
278
+ }
279
+ }
280
+ catch {
281
+ // Fallback to plain getLedger
282
+ try {
283
+ ledgerInfo = await withSuppressedConsole(() => broker.ledger.getLedger());
284
+ }
285
+ catch {
286
+ throw new EchoError(ErrorCodes.ZG_LEDGER_NOT_FOUND, "No ledger found for this wallet.", "Create one with: echo 0g-compute ledger deposit <amount> --yes");
287
+ }
288
+ }
289
+ if (isHeadless()) {
290
+ writeJsonSuccess({
291
+ ledger: serializeBigInts(ledgerInfo),
292
+ subAccounts: subAccounts.map((sa) => ({
293
+ provider: sa.provider,
294
+ ...serializeSubAccount(sa),
295
+ })),
296
+ });
297
+ }
298
+ else {
299
+ process.stderr.write("Ledger:\n");
300
+ process.stderr.write(JSON.stringify(serializeBigInts(ledgerInfo), null, 2) + "\n");
301
+ if (subAccounts.length > 0) {
302
+ process.stderr.write("\nSub-accounts:\n");
303
+ process.stderr.write(" Provider | Total | Pending | Locked\n");
304
+ process.stderr.write(" " + "-".repeat(95) + "\n");
305
+ for (const sa of subAccounts) {
306
+ const addr = sa.provider.slice(0, 42).padEnd(42);
307
+ const total = sa.totalOg.toFixed(4).padStart(10);
308
+ const pending = sa.pendingRefundOg.toFixed(4).padStart(10);
309
+ const locked = sa.lockedOg.toFixed(4).padStart(10);
310
+ process.stderr.write(` ${addr} | ${total} 0G | ${pending} 0G | ${locked} 0G\n`);
311
+ }
312
+ }
313
+ }
314
+ });
315
+ ledger
316
+ .command("deposit <amount>")
317
+ .description("Deposit 0G to the compute ledger (creates if needed)")
318
+ .option("--yes", "Confirm on-chain transaction")
319
+ .option("--json", "JSON output")
320
+ .action(async (amountStr, options) => {
321
+ requireYes(options.yes, "ledger deposit");
322
+ const amount = requirePositiveNumber(amountStr, "deposit amount");
323
+ const broker = await getAuthenticatedBroker();
324
+ // Try depositFund first; if no ledger exists, use addLedger
325
+ try {
326
+ await withSuppressedConsole(() => broker.ledger.getLedger());
327
+ logger.info(`[0G Compute] Depositing ${amount} 0G to existing ledger...`);
328
+ await withSuppressedConsole(() => broker.ledger.depositFund(amount));
329
+ }
330
+ catch {
331
+ logger.info(`[0G Compute] No ledger found, creating with ${amount} 0G...`);
332
+ await withSuppressedConsole(() => broker.ledger.addLedger(amount));
333
+ }
334
+ respond({
335
+ data: { deposited: amount, unit: "0G" },
336
+ ui: { type: "success", title: "Ledger Deposit", body: `Deposited ${amount} 0G to compute ledger.` },
337
+ });
338
+ });
339
+ ledger
340
+ .command("fund")
341
+ .description("Transfer 0G from ledger to a provider sub-account")
342
+ .requiredOption("--provider <addr>", "Provider address")
343
+ .requiredOption("--amount <0G>", "Amount in 0G")
344
+ .option("--yes", "Confirm on-chain transaction")
345
+ .option("--json", "JSON output")
346
+ .action(async (options) => {
347
+ requireYes(options.yes, "ledger fund transfer");
348
+ const provider = requireAddress(options.provider, "provider");
349
+ const amount = requirePositiveNumber(options.amount, "fund amount");
350
+ const amountWei = parseUnits(options.amount, 18);
351
+ const broker = await getAuthenticatedBroker();
352
+ try {
353
+ await withSuppressedConsole(() => broker.ledger.transferFund(provider, "inference", amountWei));
354
+ }
355
+ catch (err) {
356
+ throw new EchoError(ErrorCodes.ZG_TRANSFER_FAILED, `Transfer failed: ${err instanceof Error ? err.message : String(err)}`, "Check ledger balance with: echo 0g-compute ledger status");
357
+ }
358
+ respond({
359
+ data: { transferred: amount, unit: "0G", provider },
360
+ ui: {
361
+ type: "success",
362
+ title: "Ledger Fund",
363
+ body: `Transferred ${amount} 0G to provider sub-account ${provider.slice(0, 10)}...`,
364
+ },
365
+ });
366
+ });
367
+ // ── provider ─────────────────────────────────────────────────────
368
+ const providerCmd = root.command("provider <address>").description("Provider-specific operations");
369
+ providerCmd
370
+ .command("info")
371
+ .description("Show provider metadata, ack status, and sub-account balance")
372
+ .option("--json", "JSON output")
373
+ .action(async (_options, cmd) => {
374
+ const providerAddress = requireAddress(cmd.parent.args[0], "provider");
375
+ const broker = await getAuthenticatedBroker();
376
+ const [metadata, userAcked] = await withSuppressedConsole(() => Promise.all([
377
+ broker.inference.getServiceMetadata(providerAddress),
378
+ broker.inference.acknowledged(providerAddress),
379
+ ]));
380
+ let normalizedAccount = null;
381
+ try {
382
+ const rawAccount = await withSuppressedConsole(() => broker.inference.getAccount(providerAddress));
383
+ normalizedAccount = normalizeSubAccount(rawAccount);
384
+ }
385
+ catch {
386
+ // No sub-account yet
387
+ }
388
+ const result = {
389
+ provider: providerAddress,
390
+ ...metadata,
391
+ userAcknowledged: userAcked,
392
+ subAccount: normalizedAccount ? serializeSubAccount(normalizedAccount) : null,
393
+ };
394
+ const balanceLine = normalizedAccount
395
+ ? `Balance: Total=${normalizedAccount.totalOg.toFixed(4)} | Pending=${normalizedAccount.pendingRefundOg.toFixed(4)} | Locked=${normalizedAccount.lockedOg.toFixed(4)} 0G`
396
+ : "Sub-account: none";
397
+ respond({
398
+ data: result,
399
+ ui: {
400
+ type: "info",
401
+ title: `Provider ${providerAddress.slice(0, 10)}...`,
402
+ body: [
403
+ `Model: ${metadata.model}`,
404
+ `Endpoint: ${metadata.endpoint}`,
405
+ `User ACK: ${userAcked}`,
406
+ balanceLine,
407
+ ].join("\n"),
408
+ },
409
+ });
410
+ });
411
+ providerCmd
412
+ .command("ack")
413
+ .description("Acknowledge provider signer (user-level, on-chain)")
414
+ .option("--yes", "Confirm on-chain transaction")
415
+ .option("--json", "JSON output")
416
+ .action(async (options, cmd) => {
417
+ requireYes(options.yes, "acknowledge provider signer");
418
+ const providerAddress = requireAddress(cmd.parent.args[0], "provider");
419
+ const broker = await getAuthenticatedBroker();
420
+ try {
421
+ await withSuppressedConsole(() => broker.inference.acknowledgeProviderSigner(providerAddress));
422
+ }
423
+ catch (err) {
424
+ throw new EchoError(ErrorCodes.ZG_ACKNOWLEDGE_FAILED, `Acknowledge failed: ${err instanceof Error ? err.message : String(err)}`);
425
+ }
426
+ respond({
427
+ data: { acknowledged: true, provider: providerAddress },
428
+ ui: {
429
+ type: "success",
430
+ title: "Provider Acknowledged",
431
+ body: `Provider ${providerAddress.slice(0, 10)}... signer acknowledged.`,
432
+ },
433
+ });
434
+ });
435
+ providerCmd
436
+ .command("verify")
437
+ .description("Verify provider TEE attestation")
438
+ .option("--json", "JSON output")
439
+ .action(async (_options, cmd) => {
440
+ const providerAddress = requireAddress(cmd.parent.args[0], "provider");
441
+ const broker = await getAuthenticatedBroker();
442
+ const result = await withSuppressedConsole(() => broker.inference.verifyService(providerAddress));
443
+ if (isHeadless()) {
444
+ writeJsonSuccess({ verification: serializeBigInts(result) });
445
+ }
446
+ else {
447
+ process.stderr.write(`TEE Verification for ${providerAddress.slice(0, 10)}...:\n`);
448
+ process.stderr.write(JSON.stringify(serializeBigInts(result), null, 2) + "\n");
449
+ }
450
+ });
451
+ // ── api-key ──────────────────────────────────────────────────────
452
+ const apiKey = root.command("api-key").description("Manage persistent API keys");
453
+ apiKey
454
+ .command("create")
455
+ .description("Create a persistent API key for a provider")
456
+ .requiredOption("--provider <addr>", "Provider address")
457
+ .requiredOption("--token-id <n>", "Token ID (0-254)")
458
+ .option("--expires <sec>", "Expiry in seconds (0 = never)", "0")
459
+ .option("--yes", "Confirm on-chain transaction")
460
+ .option("--json", "JSON output")
461
+ .action(async (options) => {
462
+ requireYes(options.yes, "create API key");
463
+ const provider = requireAddress(options.provider, "provider");
464
+ const tokenId = requireTokenId(options.tokenId);
465
+ const expiresIn = Number(options.expires) * 1000; // SDK expects ms
466
+ const broker = await getAuthenticatedBroker();
467
+ let apiKeyInfo;
468
+ try {
469
+ apiKeyInfo = await withSuppressedConsole(() => broker.inference.requestProcessor.createApiKey(provider, { tokenId, expiresIn }));
470
+ }
471
+ catch (err) {
472
+ throw new EchoError(ErrorCodes.ZG_API_KEY_FAILED, `API key creation failed: ${err instanceof Error ? err.message : String(err)}`);
473
+ }
474
+ respond({
475
+ data: {
476
+ tokenId: apiKeyInfo.tokenId,
477
+ createdAt: apiKeyInfo.createdAt,
478
+ expiresAt: apiKeyInfo.expiresAt,
479
+ token: redactToken(apiKeyInfo.rawToken),
480
+ provider,
481
+ },
482
+ ui: {
483
+ type: "success",
484
+ title: "API Key Created",
485
+ body: `Token ID: ${apiKeyInfo.tokenId}\nProvider: ${provider.slice(0, 10)}...\nToken: ${redactToken(apiKeyInfo.rawToken)}`,
486
+ },
487
+ });
488
+ });
489
+ apiKey
490
+ .command("revoke")
491
+ .description("Revoke a specific API key")
492
+ .requiredOption("--provider <addr>", "Provider address")
493
+ .requiredOption("--token-id <n>", "Token ID to revoke")
494
+ .option("--yes", "Confirm on-chain transaction")
495
+ .option("--json", "JSON output")
496
+ .action(async (options) => {
497
+ requireYes(options.yes, "revoke API key");
498
+ const provider = requireAddress(options.provider, "provider");
499
+ const tokenId = requireTokenId(options.tokenId);
500
+ const broker = await getAuthenticatedBroker();
501
+ await withSuppressedConsole(() => broker.inference.revokeApiKey(provider, tokenId));
502
+ respond({
503
+ data: { revoked: true, tokenId, provider },
504
+ ui: {
505
+ type: "success",
506
+ title: "API Key Revoked",
507
+ body: `Token ID ${tokenId} for provider ${provider.slice(0, 10)}... revoked.`,
508
+ },
509
+ });
510
+ });
511
+ apiKey
512
+ .command("revoke-all")
513
+ .description("Revoke all API keys for a provider")
514
+ .requiredOption("--provider <addr>", "Provider address")
515
+ .option("--yes", "Confirm on-chain transaction")
516
+ .option("--json", "JSON output")
517
+ .action(async (options) => {
518
+ requireYes(options.yes, "revoke all API keys");
519
+ const provider = requireAddress(options.provider, "provider");
520
+ const broker = await getAuthenticatedBroker();
521
+ await withSuppressedConsole(() => broker.inference.revokeAllTokens(provider));
522
+ respond({
523
+ data: { revokedAll: true, provider },
524
+ ui: {
525
+ type: "success",
526
+ title: "All API Keys Revoked",
527
+ body: `All API keys for provider ${provider.slice(0, 10)}... revoked.`,
528
+ },
529
+ });
530
+ });
531
+ // ── openclaw ─────────────────────────────────────────────────────
532
+ const openclaw = root.command("openclaw").description("OpenClaw AI gateway integration");
533
+ openclaw
534
+ .command("use")
535
+ .description("Create API key and patch openclaw.json for a 0G provider")
536
+ .requiredOption("--provider <addr>", "Provider address")
537
+ .requiredOption("--token-id <n>", "Token ID for persistent API key (0-254)")
538
+ .option("--set-default", "Set as default model in agents.defaults.model")
539
+ .option("--fallback <ref>", "Fallback model reference (e.g., anthropic/claude-sonnet-4-5)")
540
+ .option("--force", "Overwrite existing openclaw.json provider config")
541
+ .option("--yes", "Confirm (required — creates API key on-chain)")
542
+ .option("--json", "JSON output")
543
+ .action(async (options) => {
544
+ requireYes(options.yes, "openclaw use (creates API key on-chain)");
545
+ const provider = requireAddress(options.provider, "provider");
546
+ const tokenId = requireTokenId(options.tokenId);
547
+ const broker = await getAuthenticatedBroker();
548
+ // 1. Validate provider exists
549
+ const metadata = await withSuppressedConsole(() => broker.inference.getServiceMetadata(provider));
550
+ logger.info(`[0G Compute] Provider: ${metadata.model} at ${metadata.endpoint}`);
551
+ // 2. Acknowledge provider signer (idempotent)
552
+ await withSuppressedConsole(() => broker.inference.acknowledgeProviderSigner(provider));
553
+ logger.info("[0G Compute] Provider signer acknowledged");
554
+ // 3. Create API key
555
+ let apiKeyInfo;
556
+ try {
557
+ apiKeyInfo = await withSuppressedConsole(() => broker.inference.requestProcessor.createApiKey(provider, { tokenId, expiresIn: 0 }));
558
+ }
559
+ catch (err) {
560
+ throw new EchoError(ErrorCodes.ZG_API_KEY_FAILED, `API key creation failed: ${err instanceof Error ? err.message : String(err)}`, "Check if tokenId is already in use. Try a different --token-id.");
561
+ }
562
+ logger.info(`[0G Compute] API key created: tokenId=${apiKeyInfo.tokenId}, token=${redactToken(apiKeyInfo.rawToken)}`);
563
+ // 4. Patch openclaw.json — models.providers.zg
564
+ const providerConfig = {
565
+ baseUrl: metadata.endpoint,
566
+ apiKey: apiKeyInfo.rawToken,
567
+ api: "openai-completions",
568
+ models: [
569
+ {
570
+ id: metadata.model,
571
+ name: `${metadata.model} (0G Compute)`,
572
+ contextWindow: 128000,
573
+ maxTokens: 8192,
574
+ },
575
+ ],
576
+ };
577
+ const providerPatch = patchOpenclawConfig("models.providers.zg", providerConfig, { force: options.force ?? false });
578
+ // Ensure models.mode = "merge"
579
+ patchOpenclawConfig("models.mode", "merge", { force: false });
580
+ // 5. Optionally set as default model
581
+ let defaultPatch;
582
+ if (options.setDefault) {
583
+ const defaultModel = {
584
+ primary: `zg/${metadata.model}`,
585
+ };
586
+ if (options.fallback) {
587
+ defaultModel.fallbacks = [options.fallback];
588
+ }
589
+ defaultPatch = patchOpenclawConfig("agents.defaults.model", defaultModel, { force: options.force ?? false });
590
+ }
591
+ const resultData = {
592
+ provider,
593
+ model: metadata.model,
594
+ endpoint: metadata.endpoint,
595
+ apiKey: { tokenId: apiKeyInfo.tokenId, token: redactToken(apiKeyInfo.rawToken) },
596
+ openclawConfig: {
597
+ providerPatch: {
598
+ status: providerPatch.status,
599
+ path: providerPatch.path,
600
+ keysSkipped: providerPatch.keysSkipped,
601
+ },
602
+ defaultPatch: defaultPatch
603
+ ? { status: defaultPatch.status }
604
+ : null,
605
+ },
606
+ };
607
+ const hasSkipped = providerPatch.keysSkipped.length > 0;
608
+ const bodyLines = [
609
+ `Model: ${metadata.model} (0G Compute)`,
610
+ `Config: ${providerPatch.path} (${providerPatch.status})`,
611
+ ];
612
+ if (hasSkipped) {
613
+ bodyLines.push("");
614
+ bodyLines.push(`Skipped existing keys: ${providerPatch.keysSkipped.join(", ")}`);
615
+ bodyLines.push("Use --force to overwrite.");
616
+ }
617
+ bodyLines.push("");
618
+ bodyLines.push("Next steps:");
619
+ bodyLines.push(" 1. Restart OpenClaw gateway");
620
+ bodyLines.push(" 2. Run /reset in your agent session");
621
+ bodyLines.push(options.setDefault
622
+ ? ` 3. Default model set to zg/${metadata.model}`
623
+ : ` 3. (Optional) Set default: --set-default`);
624
+ respond({
625
+ data: resultData,
626
+ ui: {
627
+ type: hasSkipped ? "warn" : "success",
628
+ title: "OpenClaw Configured",
629
+ body: bodyLines.join("\n"),
630
+ },
631
+ });
632
+ });
633
+ // ── monitor ──────────────────────────────────────────────────────
634
+ const monitor = root.command("monitor").description("Balance monitor daemon");
635
+ monitor
636
+ .command("start")
637
+ .description("Start balance monitor (foreground or --daemon)")
638
+ .requiredOption("--providers <addrs>", "Comma-separated provider addresses")
639
+ .option("--mode <mode>", "Monitor mode: fixed | recommended", "fixed")
640
+ .option("--threshold <0G>", "Alert threshold in 0G (required for --mode fixed)")
641
+ .option("--buffer <0G>", "Extra buffer above recommended min (default 0, recommended mode)", "0")
642
+ .option("--ratio <n>", "Alert ratio multiplier (default 1.2, recommended mode)", "1.2")
643
+ .option("--interval <sec>", "Polling interval in seconds", "300")
644
+ .option("--daemon", "Run detached as background process")
645
+ .option("--json", "JSON output")
646
+ .action(async (options) => {
647
+ const providers = options.providers.split(",").map((a) => requireAddress(a.trim(), "provider"));
648
+ const mode = options.mode;
649
+ const interval = Math.max(60, Number(options.interval) || 300);
650
+ const buffer = Number(options.buffer) || 0;
651
+ const alertRatio = Number(options.ratio) || 1.2;
652
+ if (mode !== "fixed" && mode !== "recommended") {
653
+ throw new EchoError(ErrorCodes.INVALID_AMOUNT, `Invalid mode: ${options.mode}`, "Use: fixed | recommended");
654
+ }
655
+ let threshold;
656
+ if (mode === "fixed") {
657
+ if (!options.threshold) {
658
+ throw new EchoError(ErrorCodes.INVALID_AMOUNT, "--threshold is required for --mode fixed", "Use: --threshold <0G> or switch to --mode recommended");
659
+ }
660
+ threshold = requirePositiveNumber(options.threshold, "threshold");
661
+ }
662
+ if (options.daemon) {
663
+ // Spawn detached child process
664
+ const { spawn } = await import("node:child_process");
665
+ const { existsSync: fsExists, openSync, mkdirSync, closeSync } = await import("node:fs");
666
+ const { fileURLToPath } = await import("node:url");
667
+ const { ZG_COMPUTE_DIR, ZG_MONITOR_LOG_FILE } = await import("../0g-compute/constants.js");
668
+ // Ensure log directory exists
669
+ if (!fsExists(ZG_COMPUTE_DIR)) {
670
+ mkdirSync(ZG_COMPUTE_DIR, { recursive: true });
671
+ }
672
+ // Build args for the child — replay the same command without --daemon
673
+ const childArgs = [
674
+ "0g-compute", "monitor", "start",
675
+ "--providers", options.providers,
676
+ "--mode", mode,
677
+ "--interval", String(interval),
678
+ "--buffer", String(buffer),
679
+ "--ratio", String(alertRatio),
680
+ ];
681
+ if (threshold != null) {
682
+ childArgs.push("--threshold", String(threshold));
683
+ }
684
+ const cliPath = fileURLToPath(new URL("../cli.js", import.meta.url));
685
+ const logFd = openSync(ZG_MONITOR_LOG_FILE, "a");
686
+ const child = spawn(process.execPath, [cliPath, ...childArgs], {
687
+ detached: true,
688
+ stdio: ["ignore", logFd, logFd],
689
+ });
690
+ child.unref();
691
+ closeSync(logFd);
692
+ respond({
693
+ data: { daemon: true, pid: child.pid, logFile: ZG_MONITOR_LOG_FILE },
694
+ ui: {
695
+ type: "success",
696
+ title: "Monitor Daemon",
697
+ body: `Started (PID ${child.pid})\nLog: ${ZG_MONITOR_LOG_FILE}`,
698
+ },
699
+ });
700
+ return;
701
+ }
702
+ const { BalanceMonitor } = await import("../0g-compute/monitor.js");
703
+ const monitorInstance = new BalanceMonitor({
704
+ providers,
705
+ mode,
706
+ threshold,
707
+ buffer,
708
+ alertRatio,
709
+ intervalSec: interval,
710
+ });
711
+ await monitorInstance.start();
712
+ // Keep alive
713
+ await new Promise(() => { });
714
+ });
715
+ monitor
716
+ .command("stop")
717
+ .description("Stop the running balance monitor")
718
+ .option("--json", "JSON output")
719
+ .action(async () => {
720
+ const { existsSync, readFileSync, writeFileSync, unlinkSync } = await import("node:fs");
721
+ const { ZG_MONITOR_PID_FILE, ZG_MONITOR_SHUTDOWN_FILE } = await import("../0g-compute/constants.js");
722
+ if (!existsSync(ZG_MONITOR_PID_FILE)) {
723
+ throw new EchoError(ErrorCodes.ZG_MONITOR_NOT_RUNNING, "Balance monitor is not running (no pidfile)");
724
+ }
725
+ const pid = parseInt(readFileSync(ZG_MONITOR_PID_FILE, "utf-8").trim(), 10);
726
+ let alive = false;
727
+ try {
728
+ process.kill(pid, 0);
729
+ alive = true;
730
+ }
731
+ catch {
732
+ unlinkSync(ZG_MONITOR_PID_FILE);
733
+ throw new EchoError(ErrorCodes.ZG_MONITOR_NOT_RUNNING, `Monitor not running (stale PID ${pid})`);
734
+ }
735
+ // SIGTERM
736
+ try {
737
+ process.kill(pid, "SIGTERM");
738
+ }
739
+ catch { /* ignore */ }
740
+ // Wait up to 5s
741
+ const deadline = Date.now() + 5000;
742
+ while (Date.now() < deadline) {
743
+ await new Promise((r) => setTimeout(r, 500));
744
+ try {
745
+ process.kill(pid, 0);
746
+ }
747
+ catch {
748
+ respond({
749
+ data: { stopped: true, pid },
750
+ ui: { type: "success", title: "Monitor Stopped", body: `Balance monitor stopped (PID ${pid})` },
751
+ });
752
+ return;
753
+ }
754
+ }
755
+ // Fallback: shutdown file
756
+ writeFileSync(ZG_MONITOR_SHUTDOWN_FILE, String(Date.now()), "utf-8");
757
+ const deadline2 = Date.now() + 10000;
758
+ while (Date.now() < deadline2) {
759
+ await new Promise((r) => setTimeout(r, 1000));
760
+ try {
761
+ process.kill(pid, 0);
762
+ }
763
+ catch {
764
+ respond({
765
+ data: { stopped: true, pid, method: "shutdown-file" },
766
+ ui: { type: "success", title: "Monitor Stopped", body: `Monitor stopped via shutdown file (PID ${pid})` },
767
+ });
768
+ return;
769
+ }
770
+ }
771
+ // SIGKILL
772
+ try {
773
+ process.kill(pid, "SIGKILL");
774
+ }
775
+ catch { /* ignore */ }
776
+ try {
777
+ if (existsSync(ZG_MONITOR_PID_FILE))
778
+ unlinkSync(ZG_MONITOR_PID_FILE);
779
+ }
780
+ catch { /* ignore */ }
781
+ try {
782
+ if (existsSync(ZG_MONITOR_SHUTDOWN_FILE))
783
+ unlinkSync(ZG_MONITOR_SHUTDOWN_FILE);
784
+ }
785
+ catch { /* ignore */ }
786
+ respond({
787
+ data: { stopped: true, pid, method: "SIGKILL" },
788
+ ui: { type: "warn", title: "Monitor Killed", body: `Monitor force-killed (PID ${pid})` },
789
+ });
790
+ });
791
+ monitor
792
+ .command("status")
793
+ .description("Show balance monitor status")
794
+ .option("--json", "JSON output")
795
+ .action(async () => {
796
+ const { existsSync, readFileSync } = await import("node:fs");
797
+ const { ZG_MONITOR_PID_FILE, ZG_MONITOR_STATE_FILE, ZG_MONITOR_LOG_FILE } = await import("../0g-compute/constants.js");
798
+ let running = false;
799
+ let pid;
800
+ if (existsSync(ZG_MONITOR_PID_FILE)) {
801
+ pid = parseInt(readFileSync(ZG_MONITOR_PID_FILE, "utf-8").trim(), 10);
802
+ try {
803
+ process.kill(pid, 0);
804
+ running = true;
805
+ }
806
+ catch {
807
+ running = false;
808
+ }
809
+ }
810
+ let state = {};
811
+ if (existsSync(ZG_MONITOR_STATE_FILE)) {
812
+ try {
813
+ state = JSON.parse(readFileSync(ZG_MONITOR_STATE_FILE, "utf-8"));
814
+ }
815
+ catch { /* ignore corrupt state */ }
816
+ }
817
+ const logFileExists = existsSync(ZG_MONITOR_LOG_FILE);
818
+ if (isHeadless()) {
819
+ writeJsonSuccess({ running, pid, logFile: ZG_MONITOR_LOG_FILE, logFileExists, ...state });
820
+ }
821
+ else {
822
+ if (!running) {
823
+ process.stderr.write("Not running\n");
824
+ return;
825
+ }
826
+ const lines = [`Running (PID ${pid})`];
827
+ if (state.mode)
828
+ lines.push(`Mode: ${state.mode}`);
829
+ if (state.threshold != null)
830
+ lines.push(`Threshold: ${state.threshold} 0G`);
831
+ if (state.intervalSec != null)
832
+ lines.push(`Interval: ${state.intervalSec}s`);
833
+ if (state.lastCheckAt) {
834
+ lines.push(`Last check: ${new Date(state.lastCheckAt).toISOString()}`);
835
+ }
836
+ if (state.providerThresholds) {
837
+ lines.push("Provider thresholds:");
838
+ for (const [addr, pt] of Object.entries(state.providerThresholds)) {
839
+ lines.push(` ${addr.slice(0, 10)}... threshold=${pt.threshold.toFixed(4)} recommendedMin=${pt.recommendedMin.toFixed(4)}`);
840
+ }
841
+ }
842
+ if (logFileExists) {
843
+ lines.push(`Log: ${ZG_MONITOR_LOG_FILE}`);
844
+ }
845
+ process.stderr.write(lines.join("\n") + "\n");
846
+ }
847
+ });
848
+ return root;
849
+ }
850
+ //# sourceMappingURL=0g-compute.js.map