@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.
- package/README.md +1175 -0
- package/dist/0g-compute/account.d.ts +36 -0
- package/dist/0g-compute/account.d.ts.map +1 -0
- package/dist/0g-compute/account.js +85 -0
- package/dist/0g-compute/account.js.map +1 -0
- package/dist/0g-compute/bridge.d.ts +16 -0
- package/dist/0g-compute/bridge.d.ts.map +1 -0
- package/dist/0g-compute/bridge.js +40 -0
- package/dist/0g-compute/bridge.js.map +1 -0
- package/dist/0g-compute/broker-factory.d.ts +19 -0
- package/dist/0g-compute/broker-factory.d.ts.map +1 -0
- package/dist/0g-compute/broker-factory.js +65 -0
- package/dist/0g-compute/broker-factory.js.map +1 -0
- package/dist/0g-compute/constants.d.ts +10 -0
- package/dist/0g-compute/constants.d.ts.map +1 -0
- package/dist/0g-compute/constants.js +12 -0
- package/dist/0g-compute/constants.js.map +1 -0
- package/dist/0g-compute/monitor.d.ts +43 -0
- package/dist/0g-compute/monitor.d.ts.map +1 -0
- package/dist/0g-compute/monitor.js +302 -0
- package/dist/0g-compute/monitor.js.map +1 -0
- package/dist/0g-compute/pricing.d.ts +43 -0
- package/dist/0g-compute/pricing.d.ts.map +1 -0
- package/dist/0g-compute/pricing.js +53 -0
- package/dist/0g-compute/pricing.js.map +1 -0
- package/dist/0g-compute/sdk-bridge.cjs +17 -0
- package/dist/0g-compute/sdk-bridge.cjs.map +1 -0
- package/dist/0g-compute/sdk-bridge.d.cts +9 -0
- package/dist/0g-compute/sdk-bridge.d.cts.map +1 -0
- package/dist/0g-compute/smoke-test.d.ts +11 -0
- package/dist/0g-compute/smoke-test.d.ts.map +1 -0
- package/dist/0g-compute/smoke-test.js +172 -0
- package/dist/0g-compute/smoke-test.js.map +1 -0
- package/dist/bot/daemon.d.ts +34 -0
- package/dist/bot/daemon.d.ts.map +1 -0
- package/dist/bot/daemon.js +386 -0
- package/dist/bot/daemon.js.map +1 -0
- package/dist/bot/executor.d.ts +14238 -0
- package/dist/bot/executor.d.ts.map +1 -0
- package/dist/bot/executor.js +183 -0
- package/dist/bot/executor.js.map +1 -0
- package/dist/bot/nonce-queue.d.ts +20 -0
- package/dist/bot/nonce-queue.d.ts.map +1 -0
- package/dist/bot/nonce-queue.js +41 -0
- package/dist/bot/nonce-queue.js.map +1 -0
- package/dist/bot/notify.d.ts +15 -0
- package/dist/bot/notify.d.ts.map +1 -0
- package/dist/bot/notify.js +98 -0
- package/dist/bot/notify.js.map +1 -0
- package/dist/bot/orders.d.ts +30 -0
- package/dist/bot/orders.d.ts.map +1 -0
- package/dist/bot/orders.js +172 -0
- package/dist/bot/orders.js.map +1 -0
- package/dist/bot/state.d.ts +14 -0
- package/dist/bot/state.d.ts.map +1 -0
- package/dist/bot/state.js +109 -0
- package/dist/bot/state.js.map +1 -0
- package/dist/bot/stream.d.ts +28 -0
- package/dist/bot/stream.d.ts.map +1 -0
- package/dist/bot/stream.js +96 -0
- package/dist/bot/stream.js.map +1 -0
- package/dist/bot/triggers.d.ts +17 -0
- package/dist/bot/triggers.d.ts.map +1 -0
- package/dist/bot/triggers.js +95 -0
- package/dist/bot/triggers.js.map +1 -0
- package/dist/bot/types.d.ts +199 -0
- package/dist/bot/types.d.ts.map +1 -0
- package/dist/bot/types.js +12 -0
- package/dist/bot/types.js.map +1 -0
- package/dist/chainscan/client.d.ts +28 -0
- package/dist/chainscan/client.d.ts.map +1 -0
- package/dist/chainscan/client.js +361 -0
- package/dist/chainscan/client.js.map +1 -0
- package/dist/chainscan/constants.d.ts +15 -0
- package/dist/chainscan/constants.d.ts.map +1 -0
- package/dist/chainscan/constants.js +15 -0
- package/dist/chainscan/constants.js.map +1 -0
- package/dist/chainscan/types.d.ts +148 -0
- package/dist/chainscan/types.d.ts.map +1 -0
- package/dist/chainscan/types.js +2 -0
- package/dist/chainscan/types.js.map +1 -0
- package/dist/chainscan/validation.d.ts +35 -0
- package/dist/chainscan/validation.d.ts.map +1 -0
- package/dist/chainscan/validation.js +97 -0
- package/dist/chainscan/validation.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +328 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/0g-compute.d.ts +21 -0
- package/dist/commands/0g-compute.d.ts.map +1 -0
- package/dist/commands/0g-compute.js +850 -0
- package/dist/commands/0g-compute.js.map +1 -0
- package/dist/commands/chainscan.d.ts +17 -0
- package/dist/commands/chainscan.d.ts.map +1 -0
- package/dist/commands/chainscan.js +605 -0
- package/dist/commands/chainscan.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +251 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/echobook.d.ts +17 -0
- package/dist/commands/echobook.d.ts.map +1 -0
- package/dist/commands/echobook.js +905 -0
- package/dist/commands/echobook.js.map +1 -0
- package/dist/commands/jaine-subgraph.d.ts +3 -0
- package/dist/commands/jaine-subgraph.d.ts.map +1 -0
- package/dist/commands/jaine-subgraph.js +565 -0
- package/dist/commands/jaine-subgraph.js.map +1 -0
- package/dist/commands/jaine.d.ts +3 -0
- package/dist/commands/jaine.d.ts.map +1 -0
- package/dist/commands/jaine.js +1415 -0
- package/dist/commands/jaine.js.map +1 -0
- package/dist/commands/marketmaker.d.ts +6 -0
- package/dist/commands/marketmaker.d.ts.map +1 -0
- package/dist/commands/marketmaker.js +451 -0
- package/dist/commands/marketmaker.js.map +1 -0
- package/dist/commands/send.d.ts +3 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +229 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +263 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/slop-app.d.ts +9 -0
- package/dist/commands/slop-app.d.ts.map +1 -0
- package/dist/commands/slop-app.js +708 -0
- package/dist/commands/slop-app.js.map +1 -0
- package/dist/commands/slop-stream.d.ts +9 -0
- package/dist/commands/slop-stream.d.ts.map +1 -0
- package/dist/commands/slop-stream.js +99 -0
- package/dist/commands/slop-stream.js.map +1 -0
- package/dist/commands/slop.d.ts +3 -0
- package/dist/commands/slop.d.ts.map +1 -0
- package/dist/commands/slop.js +1053 -0
- package/dist/commands/slop.js.map +1 -0
- package/dist/commands/wallet.d.ts +13 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +748 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/config/paths.d.ts +13 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +33 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/store.d.ts +48 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +113 -0
- package/dist/config/store.js.map +1 -0
- package/dist/constants/chain.d.ts +57 -0
- package/dist/constants/chain.d.ts.map +1 -0
- package/dist/constants/chain.js +51 -0
- package/dist/constants/chain.js.map +1 -0
- package/dist/echobook/api.d.ts +38 -0
- package/dist/echobook/api.d.ts.map +1 -0
- package/dist/echobook/api.js +86 -0
- package/dist/echobook/api.js.map +1 -0
- package/dist/echobook/auth.d.ts +31 -0
- package/dist/echobook/auth.d.ts.map +1 -0
- package/dist/echobook/auth.js +93 -0
- package/dist/echobook/auth.js.map +1 -0
- package/dist/echobook/comments.d.ts +26 -0
- package/dist/echobook/comments.d.ts.map +1 -0
- package/dist/echobook/comments.js +20 -0
- package/dist/echobook/comments.js.map +1 -0
- package/dist/echobook/follows.d.ts +19 -0
- package/dist/echobook/follows.d.ts.map +1 -0
- package/dist/echobook/follows.js +21 -0
- package/dist/echobook/follows.js.map +1 -0
- package/dist/echobook/jwtCache.d.ts +15 -0
- package/dist/echobook/jwtCache.d.ts.map +1 -0
- package/dist/echobook/jwtCache.js +63 -0
- package/dist/echobook/jwtCache.js.map +1 -0
- package/dist/echobook/notifications.d.ts +30 -0
- package/dist/echobook/notifications.d.ts.map +1 -0
- package/dist/echobook/notifications.js +26 -0
- package/dist/echobook/notifications.js.map +1 -0
- package/dist/echobook/points.d.ts +35 -0
- package/dist/echobook/points.d.ts.map +1 -0
- package/dist/echobook/points.js +20 -0
- package/dist/echobook/points.js.map +1 -0
- package/dist/echobook/posts.d.ts +46 -0
- package/dist/echobook/posts.d.ts.map +1 -0
- package/dist/echobook/posts.js +43 -0
- package/dist/echobook/posts.js.map +1 -0
- package/dist/echobook/profile.d.ts +29 -0
- package/dist/echobook/profile.d.ts.map +1 -0
- package/dist/echobook/profile.js +14 -0
- package/dist/echobook/profile.js.map +1 -0
- package/dist/echobook/submolts.d.ts +22 -0
- package/dist/echobook/submolts.d.ts.map +1 -0
- package/dist/echobook/submolts.js +24 -0
- package/dist/echobook/submolts.js.map +1 -0
- package/dist/echobook/tradeProof.d.ts +21 -0
- package/dist/echobook/tradeProof.d.ts.map +1 -0
- package/dist/echobook/tradeProof.js +14 -0
- package/dist/echobook/tradeProof.js.map +1 -0
- package/dist/echobook/votes.d.ts +17 -0
- package/dist/echobook/votes.d.ts.map +1 -0
- package/dist/echobook/votes.js +20 -0
- package/dist/echobook/votes.js.map +1 -0
- package/dist/errors.d.ts +125 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +147 -0
- package/dist/errors.js.map +1 -0
- package/dist/intents/store.d.ts +22 -0
- package/dist/intents/store.d.ts.map +1 -0
- package/dist/intents/store.js +76 -0
- package/dist/intents/store.js.map +1 -0
- package/dist/intents/types.d.ts +21 -0
- package/dist/intents/types.d.ts.map +1 -0
- package/dist/intents/types.js +2 -0
- package/dist/intents/types.js.map +1 -0
- package/dist/jaine/abi/erc20.d.ts +90 -0
- package/dist/jaine/abi/erc20.d.ts.map +1 -0
- package/dist/jaine/abi/erc20.js +65 -0
- package/dist/jaine/abi/erc20.js.map +1 -0
- package/dist/jaine/abi/factory.d.ts +38 -0
- package/dist/jaine/abi/factory.d.ts.map +1 -0
- package/dist/jaine/abi/factory.js +26 -0
- package/dist/jaine/abi/factory.js.map +1 -0
- package/dist/jaine/abi/index.d.ts +11 -0
- package/dist/jaine/abi/index.d.ts.map +1 -0
- package/dist/jaine/abi/index.js +11 -0
- package/dist/jaine/abi/index.js.map +1 -0
- package/dist/jaine/abi/nftManager.d.ts +282 -0
- package/dist/jaine/abi/nftManager.d.ts.map +1 -0
- package/dist/jaine/abi/nftManager.js +182 -0
- package/dist/jaine/abi/nftManager.js.map +1 -0
- package/dist/jaine/abi/pool.d.ts +77 -0
- package/dist/jaine/abi/pool.d.ts.map +1 -0
- package/dist/jaine/abi/pool.js +56 -0
- package/dist/jaine/abi/pool.js.map +1 -0
- package/dist/jaine/abi/quoter.d.ts +84 -0
- package/dist/jaine/abi/quoter.d.ts.map +1 -0
- package/dist/jaine/abi/quoter.js +53 -0
- package/dist/jaine/abi/quoter.js.map +1 -0
- package/dist/jaine/abi/router.d.ts +135 -0
- package/dist/jaine/abi/router.d.ts.map +1 -0
- package/dist/jaine/abi/router.js +88 -0
- package/dist/jaine/abi/router.js.map +1 -0
- package/dist/jaine/abi/w0g.d.ts +41 -0
- package/dist/jaine/abi/w0g.d.ts.map +1 -0
- package/dist/jaine/abi/w0g.js +34 -0
- package/dist/jaine/abi/w0g.js.map +1 -0
- package/dist/jaine/allowance.d.ts +48 -0
- package/dist/jaine/allowance.d.ts.map +1 -0
- package/dist/jaine/allowance.js +192 -0
- package/dist/jaine/allowance.js.map +1 -0
- package/dist/jaine/coreTokens.d.ts +32 -0
- package/dist/jaine/coreTokens.d.ts.map +1 -0
- package/dist/jaine/coreTokens.js +91 -0
- package/dist/jaine/coreTokens.js.map +1 -0
- package/dist/jaine/pathEncoding.d.ts +39 -0
- package/dist/jaine/pathEncoding.d.ts.map +1 -0
- package/dist/jaine/pathEncoding.js +98 -0
- package/dist/jaine/pathEncoding.js.map +1 -0
- package/dist/jaine/paths.d.ts +11 -0
- package/dist/jaine/paths.d.ts.map +1 -0
- package/dist/jaine/paths.js +20 -0
- package/dist/jaine/paths.js.map +1 -0
- package/dist/jaine/poolCache.d.ts +47 -0
- package/dist/jaine/poolCache.d.ts.map +1 -0
- package/dist/jaine/poolCache.js +195 -0
- package/dist/jaine/poolCache.js.map +1 -0
- package/dist/jaine/routing.d.ts +41 -0
- package/dist/jaine/routing.d.ts.map +1 -0
- package/dist/jaine/routing.js +247 -0
- package/dist/jaine/routing.js.map +1 -0
- package/dist/jaine/subgraph/client.d.ts +26 -0
- package/dist/jaine/subgraph/client.d.ts.map +1 -0
- package/dist/jaine/subgraph/client.js +201 -0
- package/dist/jaine/subgraph/client.js.map +1 -0
- package/dist/jaine/subgraph/constants.d.ts +9 -0
- package/dist/jaine/subgraph/constants.d.ts.map +1 -0
- package/dist/jaine/subgraph/constants.js +9 -0
- package/dist/jaine/subgraph/constants.js.map +1 -0
- package/dist/jaine/subgraph/queries.d.ts +21 -0
- package/dist/jaine/subgraph/queries.d.ts.map +1 -0
- package/dist/jaine/subgraph/queries.js +304 -0
- package/dist/jaine/subgraph/queries.js.map +1 -0
- package/dist/jaine/subgraph/types.d.ts +209 -0
- package/dist/jaine/subgraph/types.d.ts.map +1 -0
- package/dist/jaine/subgraph/types.js +7 -0
- package/dist/jaine/subgraph/types.js.map +1 -0
- package/dist/jaine/userTokens.d.ts +27 -0
- package/dist/jaine/userTokens.d.ts.map +1 -0
- package/dist/jaine/userTokens.js +89 -0
- package/dist/jaine/userTokens.js.map +1 -0
- package/dist/openclaw/config.d.ts +43 -0
- package/dist/openclaw/config.d.ts.map +1 -0
- package/dist/openclaw/config.js +231 -0
- package/dist/openclaw/config.js.map +1 -0
- package/dist/openclaw/hooks-client.d.ts +24 -0
- package/dist/openclaw/hooks-client.d.ts.map +1 -0
- package/dist/openclaw/hooks-client.js +119 -0
- package/dist/openclaw/hooks-client.js.map +1 -0
- package/dist/slop/abi/factory.d.ts +128 -0
- package/dist/slop/abi/factory.d.ts.map +1 -0
- package/dist/slop/abi/factory.js +70 -0
- package/dist/slop/abi/factory.js.map +1 -0
- package/dist/slop/abi/feeCollector.d.ts +95 -0
- package/dist/slop/abi/feeCollector.d.ts.map +1 -0
- package/dist/slop/abi/feeCollector.js +71 -0
- package/dist/slop/abi/feeCollector.js.map +1 -0
- package/dist/slop/abi/index.d.ts +5 -0
- package/dist/slop/abi/index.d.ts.map +1 -0
- package/dist/slop/abi/index.js +5 -0
- package/dist/slop/abi/index.js.map +1 -0
- package/dist/slop/abi/registry.d.ts +135 -0
- package/dist/slop/abi/registry.d.ts.map +1 -0
- package/dist/slop/abi/registry.js +90 -0
- package/dist/slop/abi/registry.js.map +1 -0
- package/dist/slop/abi/token.d.ts +320 -0
- package/dist/slop/abi/token.d.ts.map +1 -0
- package/dist/slop/abi/token.js +251 -0
- package/dist/slop/abi/token.js.map +1 -0
- package/dist/slop/auth.d.ts +19 -0
- package/dist/slop/auth.d.ts.map +1 -0
- package/dist/slop/auth.js +92 -0
- package/dist/slop/auth.js.map +1 -0
- package/dist/slop/jwtCache.d.ts +27 -0
- package/dist/slop/jwtCache.d.ts.map +1 -0
- package/dist/slop/jwtCache.js +91 -0
- package/dist/slop/jwtCache.js.map +1 -0
- package/dist/slop/quote.d.ts +80 -0
- package/dist/slop/quote.d.ts.map +1 -0
- package/dist/slop/quote.js +174 -0
- package/dist/slop/quote.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +8 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -0
- package/dist/utils/canonicalJson.js +20 -0
- package/dist/utils/canonicalJson.js.map +1 -0
- package/dist/utils/env.d.ts +11 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +20 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/http.d.ts +19 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +61 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/output.d.ts +29 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +51 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/rateLimit.d.ts +22 -0
- package/dist/utils/rateLimit.d.ts.map +1 -0
- package/dist/utils/rateLimit.js +58 -0
- package/dist/utils/rateLimit.js.map +1 -0
- package/dist/utils/respond.d.ts +19 -0
- package/dist/utils/respond.d.ts.map +1 -0
- package/dist/utils/respond.js +25 -0
- package/dist/utils/respond.js.map +1 -0
- package/dist/utils/ui.d.ts +38 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +126 -0
- package/dist/utils/ui.js.map +1 -0
- package/dist/wallet/client.d.ts +4 -0
- package/dist/wallet/client.d.ts.map +1 -0
- package/dist/wallet/client.js +53 -0
- package/dist/wallet/client.js.map +1 -0
- package/dist/wallet/keystore.d.ts +22 -0
- package/dist/wallet/keystore.d.ts.map +1 -0
- package/dist/wallet/keystore.js +111 -0
- package/dist/wallet/keystore.js.map +1 -0
- package/package.json +63 -0
- 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
|