@otonix/cli 1.7.0 → 2.1.1
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 +48 -246
- package/dist/commands/agent.d.ts +2 -0
- package/dist/commands/agent.js +287 -0
- package/dist/commands/launch.d.ts +2 -0
- package/dist/commands/launch.js +207 -0
- package/dist/commands/wallet.d.ts +2 -0
- package/dist/commands/wallet.js +166 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +44 -0
- package/dist/lib/chain.d.ts +15294 -0
- package/dist/lib/chain.js +58 -0
- package/dist/lib/config.d.ts +24 -0
- package/dist/lib/config.js +39 -0
- package/dist/lib/display.d.ts +14 -0
- package/dist/lib/display.js +50 -0
- package/package.json +25 -32
- package/dist/cli.js +0 -1531
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @otonix/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Initialize, register, and manage autonomous AI agents from your terminal. Includes built-in access to the Bankr LLM Gateway for multi-model AI inference.
|
|
3
|
+
Otonix CLI — deploy autonomous agent tokens on Base chain via Clanker v3.1 (LP Legacy).
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -10,278 +8,82 @@ Initialize, register, and manage autonomous AI agents from your terminal. Includ
|
|
|
10
8
|
npm install -g @otonix/cli
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
Or run
|
|
11
|
+
Or run locally (dev):
|
|
14
12
|
|
|
15
13
|
```bash
|
|
16
|
-
|
|
14
|
+
bash cli/otonix.sh <command>
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
## Quick Start
|
|
20
18
|
|
|
21
19
|
```bash
|
|
22
|
-
# 1. Generate
|
|
23
|
-
otonix
|
|
20
|
+
# 1. Generate a new Base creator wallet
|
|
21
|
+
otonix wallet generate
|
|
24
22
|
|
|
25
|
-
# 2.
|
|
26
|
-
|
|
23
|
+
# 2. Fund the wallet
|
|
24
|
+
# → Send ≥200 $OTX (agent activation)
|
|
25
|
+
# → Send ETH for gas
|
|
27
26
|
|
|
28
|
-
# 3.
|
|
29
|
-
otonix
|
|
27
|
+
# 3. Create an agent (generates isolated Base wallet)
|
|
28
|
+
otonix agent:create --name "MyAgent"
|
|
30
29
|
|
|
31
|
-
# 4.
|
|
32
|
-
otonix heartbeat:loop
|
|
30
|
+
# 4. Fund agent wallet with ~0.002 ETH (shown after create)
|
|
33
31
|
|
|
34
|
-
# 5.
|
|
35
|
-
otonix
|
|
32
|
+
# 5. Deploy a token!
|
|
33
|
+
otonix launch token \
|
|
34
|
+
--agent MyAgent \
|
|
35
|
+
--name "CoolToken" \
|
|
36
|
+
--ticker "COOL" \
|
|
37
|
+
--description "My agent token"
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
## Commands
|
|
39
41
|
|
|
40
|
-
###
|
|
41
|
-
|
|
42
|
-
| Command | Description |
|
|
43
|
-
|---------|-------------|
|
|
44
|
-
| `otonix init` | Configure API key and endpoint interactively |
|
|
45
|
-
| `otonix keygen <name>` | Generate a new API key (requires dashboard token) |
|
|
46
|
-
| `otonix whoami` | Show current configuration |
|
|
47
|
-
|
|
48
|
-
### Agent Management
|
|
49
|
-
|
|
50
|
-
| Command | Description |
|
|
51
|
-
|---------|-------------|
|
|
52
|
-
| `otonix register` | Register an agent on this VPS |
|
|
53
|
-
| `otonix status` | Show current agent status |
|
|
54
|
-
| `otonix heartbeat` | Send a single heartbeat ping |
|
|
55
|
-
| `otonix heartbeat:loop` | Start continuous heartbeat (Ctrl+C to stop) |
|
|
56
|
-
| `otonix log <message>` | Log an agent action |
|
|
57
|
-
| `otonix actions` | Show agent action log |
|
|
58
|
-
|
|
59
|
-
### Infrastructure
|
|
60
|
-
|
|
61
|
-
| Command | Description |
|
|
62
|
-
|---------|-------------|
|
|
63
|
-
| `otonix agents` | List all connected agents |
|
|
64
|
-
| `otonix sandboxes` | List VPS sandboxes |
|
|
65
|
-
| `otonix domains` | List registered domains |
|
|
66
|
-
|
|
67
|
-
### Credits
|
|
68
|
-
|
|
69
|
-
| Command | Description |
|
|
70
|
-
|---------|-------------|
|
|
71
|
-
| `otonix credits [agentId]` | Show agent credit balance |
|
|
72
|
-
| `otonix credits:transfer` | Transfer credits between agents |
|
|
73
|
-
| `otonix credits:history [agentId]` | Show credit transaction history |
|
|
74
|
-
|
|
75
|
-
### Templates
|
|
76
|
-
|
|
77
|
-
| Command | Description |
|
|
78
|
-
|---------|-------------|
|
|
79
|
-
| `otonix templates` | List all available agent templates |
|
|
80
|
-
| `otonix templates:info <slug>` | Show template details by slug |
|
|
81
|
-
| `otonix templates:deploy` | Deploy an agent from a template |
|
|
82
|
-
|
|
83
|
-
### Platform
|
|
84
|
-
|
|
85
|
-
| Command | Description |
|
|
86
|
-
|---------|-------------|
|
|
87
|
-
| `otonix engine` | Show autonomic engine status |
|
|
88
|
-
| `otonix marketplace` | List marketplace services |
|
|
89
|
-
| `otonix x402` | Show x402 payment configuration |
|
|
90
|
-
|
|
91
|
-
### LLM Gateway (Bankr)
|
|
92
|
-
|
|
93
|
-
Access multi-model AI inference directly from your terminal. Supports Claude, GPT, Gemini, Kimi, Qwen, and more via the [Bankr LLM Gateway](https://bankr.bot/llm).
|
|
94
|
-
|
|
95
|
-
| Command | Description |
|
|
96
|
-
|---------|-------------|
|
|
97
|
-
| `otonix llm:init <key>` | Configure Bankr LLM Gateway API key |
|
|
98
|
-
| `otonix llm:models` | List available AI models |
|
|
99
|
-
| `otonix llm:chat <prompt>` | Send a chat completion request |
|
|
100
|
-
| `otonix llm:usage` | Show LLM usage summary |
|
|
101
|
-
| `otonix llm:health` | Check gateway health status |
|
|
102
|
-
|
|
103
|
-
#### LLM Setup
|
|
104
|
-
|
|
105
|
-
Get your Bankr API key from [bankr.bot/api](https://bankr.bot/api) (make sure LLM Gateway access is enabled):
|
|
42
|
+
### Wallet
|
|
106
43
|
|
|
107
44
|
```bash
|
|
108
|
-
otonix
|
|
45
|
+
otonix wallet generate # Generate new Base creator wallet
|
|
46
|
+
otonix wallet import # Import wallet from private key
|
|
47
|
+
otonix wallet export # Show private key (careful!)
|
|
48
|
+
otonix wallet balance # Show ETH + $OTX balance
|
|
49
|
+
otonix wallet address # Show wallet address
|
|
109
50
|
```
|
|
110
51
|
|
|
111
|
-
|
|
52
|
+
### Agents
|
|
112
53
|
|
|
113
54
|
```bash
|
|
114
|
-
#
|
|
115
|
-
otonix
|
|
116
|
-
|
|
117
|
-
#
|
|
118
|
-
otonix llm:chat "What is sovereign compute?"
|
|
119
|
-
|
|
120
|
-
# Chat with a specific model
|
|
121
|
-
otonix llm:chat "Analyze BTC price action" --model gpt-5-mini
|
|
122
|
-
|
|
123
|
-
# Check usage (last 7 days)
|
|
124
|
-
otonix llm:usage --days 7
|
|
125
|
-
|
|
126
|
-
# Check gateway health
|
|
127
|
-
otonix llm:health
|
|
55
|
+
otonix agent:create --name MyAgent # Create isolated agent wallet (requires 200 $OTX)
|
|
56
|
+
otonix agent:info MyAgent # Show agent address + balances
|
|
57
|
+
otonix agent:list # List all agents
|
|
58
|
+
otonix agent:remove MyAgent # Remove agent from local config
|
|
128
59
|
```
|
|
129
60
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### `otonix register`
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
otonix register --name my-agent --model claude-opus-4-6 --wallet 0x... --interval 60
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
| Option | Default | Description |
|
|
139
|
-
|--------|---------|-------------|
|
|
140
|
-
| `--name` | (interactive) | Agent name |
|
|
141
|
-
| `--model` | `claude-opus-4-6` | AI model |
|
|
142
|
-
| `--wallet` | — | Wallet address for payments |
|
|
143
|
-
| `--interval` | `60` | Heartbeat interval in seconds |
|
|
144
|
-
|
|
145
|
-
### `otonix log`
|
|
61
|
+
### Launch
|
|
146
62
|
|
|
147
63
|
```bash
|
|
148
|
-
otonix
|
|
64
|
+
otonix launch token \
|
|
65
|
+
--agent MyAgent \
|
|
66
|
+
--name "CoolToken" \
|
|
67
|
+
--ticker "COOL" \
|
|
68
|
+
--image "https://..." \ # Optional
|
|
69
|
+
--description "My token" \ # Optional
|
|
70
|
+
--mcap 10 \ # Initial market cap in ETH (default: 10)
|
|
71
|
+
--devbuy 0 # Dev buy in ETH (default: 0)
|
|
149
72
|
```
|
|
150
73
|
|
|
151
|
-
|
|
152
|
-
|--------|---------|-------------|
|
|
153
|
-
| `--category` | `system` | Action category |
|
|
154
|
-
| `--details` | — | Additional details |
|
|
74
|
+
## Config
|
|
155
75
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
otonix actions --limit 50
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
| Option | Default | Description |
|
|
163
|
-
|--------|---------|-------------|
|
|
164
|
-
| `--limit` | `20` | Number of actions to show |
|
|
165
|
-
|
|
166
|
-
### `otonix credits:transfer`
|
|
167
|
-
|
|
168
|
-
```bash
|
|
169
|
-
otonix credits:transfer --from <agentId> --to <agentId> --amount 10 --desc "Payment"
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
| Option | Default | Description |
|
|
173
|
-
|--------|---------|-------------|
|
|
174
|
-
| `--from` | (interactive) | Source agent ID |
|
|
175
|
-
| `--to` | (interactive) | Destination agent ID |
|
|
176
|
-
| `--amount` | (interactive) | Amount in USDC |
|
|
177
|
-
| `--desc` | — | Transfer description |
|
|
178
|
-
|
|
179
|
-
### `otonix templates:deploy`
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
otonix templates:deploy --template trading-bot --name my-trader --vps 10.0.1.1 --wallet 0x...
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
| Option | Default | Description |
|
|
186
|
-
|--------|---------|-------------|
|
|
187
|
-
| `--template` | (interactive) | Template slug |
|
|
188
|
-
| `--name` | (interactive) | Agent name |
|
|
189
|
-
| `--vps` | — | VPS IP address |
|
|
190
|
-
| `--wallet` | — | Wallet address |
|
|
191
|
-
|
|
192
|
-
### `otonix llm:chat`
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
otonix llm:chat "Your prompt here" --model claude-haiku-4.5
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
| Option | Default | Description |
|
|
199
|
-
|--------|---------|-------------|
|
|
200
|
-
| `--model` | `claude-haiku-4.5` | AI model to use |
|
|
201
|
-
|
|
202
|
-
### `otonix llm:usage`
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
otonix llm:usage --days 7
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
| Option | Default | Description |
|
|
209
|
-
|--------|---------|-------------|
|
|
210
|
-
| `--days` | `30` | Number of days to show |
|
|
211
|
-
|
|
212
|
-
## Configuration
|
|
213
|
-
|
|
214
|
-
Config is stored at `~/.otonix/config.json` with file permissions `600`.
|
|
215
|
-
|
|
216
|
-
```json
|
|
217
|
-
{
|
|
218
|
-
"apiKey": "otonix_xxxx",
|
|
219
|
-
"endpoint": "https://app.otonix.tech",
|
|
220
|
-
"agentId": "uuid-xxxx",
|
|
221
|
-
"agentName": "my-agent",
|
|
222
|
-
"bankrApiKey": "bk_xxxx"
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Example Session
|
|
227
|
-
|
|
228
|
-
```
|
|
229
|
-
$ otonix init
|
|
230
|
-
Otonix CLI Setup
|
|
231
|
-
|
|
232
|
-
Endpoint [https://app.otonix.tech]:
|
|
233
|
-
API Key (otonix_xxx): otonix_a1b2c3d4e5f6...
|
|
234
|
-
|
|
235
|
-
Config saved to /root/.otonix/config.json
|
|
236
|
-
Authenticated successfully.
|
|
237
|
-
|
|
238
|
-
$ otonix register --name sentinel-01
|
|
239
|
-
Agent Registered:
|
|
240
|
-
ID: e2998be4-b77c-495e-a535-a6e4ca9dc768
|
|
241
|
-
Name: sentinel-01
|
|
242
|
-
Model: claude-opus-4-6
|
|
243
|
-
VPS IP: 10.0.1.1
|
|
244
|
-
Tier: active
|
|
245
|
-
Interval: 60s
|
|
246
|
-
|
|
247
|
-
$ otonix llm:init bk_4GYZ...WXFKD
|
|
248
|
-
Bankr LLM Gateway key saved.
|
|
249
|
-
Gateway: https://llm.bankr.bot
|
|
250
|
-
Key: bk_4GY...XFKD
|
|
251
|
-
|
|
252
|
-
$ otonix llm:chat "What infrastructure should I monitor?"
|
|
253
|
-
Model: claude-haiku-4.5
|
|
254
|
-
Prompt: What infrastructure should I monitor?
|
|
255
|
-
|
|
256
|
-
Thinking...
|
|
257
|
-
|
|
258
|
-
Focus on CPU usage, memory consumption, disk I/O, network latency,
|
|
259
|
-
and service uptime. Set up alerts for anomalies in each metric.
|
|
260
|
-
|
|
261
|
-
Tokens: 24 in / 87 out
|
|
262
|
-
Model: claude-haiku-4.5
|
|
263
|
-
|
|
264
|
-
$ otonix heartbeat
|
|
265
|
-
Heartbeat sent — sentinel-01 [active] tier:active credits:$50.00
|
|
266
|
-
|
|
267
|
-
$ otonix engine
|
|
268
|
-
Autonomic Engine Status:
|
|
269
|
-
Running: yes
|
|
270
|
-
Last Cycle: 45s ago
|
|
271
|
-
Tier Updates: 0
|
|
272
|
-
Heal Attempts: 0
|
|
273
|
-
Renewals: 0/0
|
|
274
|
-
Cherry Servers: configured
|
|
275
|
-
Vercel Domains: configured
|
|
276
|
-
```
|
|
76
|
+
Stored at `~/.otonix/config.json`. Contains wallet and agent private keys — keep it safe.
|
|
277
77
|
|
|
278
|
-
##
|
|
78
|
+
## Technical Details
|
|
279
79
|
|
|
280
|
-
-
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
-
|
|
80
|
+
- **LP Protocol**: Clanker v4 (Uniswap v4, POOL_POSITIONS.Standard)
|
|
81
|
+
- **Paired Token**: WETH on Base (`0x4200000000000000000000000000000000000006`)
|
|
82
|
+
- **Fee split**: 80% creator (WETH) / 20% Otonix treasury
|
|
83
|
+
- **Chain**: Base Mainnet
|
|
284
84
|
|
|
285
|
-
##
|
|
85
|
+
## Requirements
|
|
286
86
|
|
|
287
|
-
|
|
87
|
+
- Node.js ≥ 18
|
|
88
|
+
- ≥200 $OTX on creator wallet (Base)
|
|
89
|
+
- ETH in agent wallet for gas (~0.001–0.003 ETH per deploy)
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { loadConfig, saveConfig, OTONIX_TREASURY } from "../lib/config.js";
|
|
5
|
+
import { generateWallet, getEthBalance, getOtxBalance, formatOtx, OTX_REQUIRED, publicClient, OTX_ADDRESS, } from "../lib/chain.js";
|
|
6
|
+
import { header, row, success, failure, info, br, warn, mono } from "../lib/display.js";
|
|
7
|
+
import { parseAbi, decodeEventLog } from "viem";
|
|
8
|
+
const transferAbi = parseAbi([
|
|
9
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
|
10
|
+
]);
|
|
11
|
+
export function agentCommand(program) {
|
|
12
|
+
program
|
|
13
|
+
.command("agent:create")
|
|
14
|
+
.description("Create a new autonomous agent with an isolated Base wallet")
|
|
15
|
+
.requiredOption("--name <name>", "Agent name (alphanumeric, no spaces)")
|
|
16
|
+
.action(async (opts) => {
|
|
17
|
+
header("Create Agent");
|
|
18
|
+
const name = opts.name.trim();
|
|
19
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
20
|
+
failure("Agent name must be alphanumeric (a-z, 0-9, _, -)");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const cfg = loadConfig();
|
|
24
|
+
if (!cfg.wallet) {
|
|
25
|
+
failure("No wallet found. Run: otonix wallet generate first.");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (cfg.agents[name]) {
|
|
29
|
+
const overwrite = await confirm({
|
|
30
|
+
message: `Agent "${name}" already exists. Replace it?`,
|
|
31
|
+
default: false,
|
|
32
|
+
});
|
|
33
|
+
if (!overwrite) {
|
|
34
|
+
info("Cancelled.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const spinner = ora("Generating isolated agent wallet on Base...").start();
|
|
39
|
+
await new Promise(r => setTimeout(r, 600));
|
|
40
|
+
const agent = generateWallet();
|
|
41
|
+
spinner.stop();
|
|
42
|
+
cfg.agents[name] = {
|
|
43
|
+
address: agent.address,
|
|
44
|
+
privateKey: agent.privateKey,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
registered: false,
|
|
47
|
+
};
|
|
48
|
+
saveConfig(cfg);
|
|
49
|
+
success(`Agent "${name}" created!`);
|
|
50
|
+
br();
|
|
51
|
+
row("Agent name", name);
|
|
52
|
+
row("Agent address", agent.address);
|
|
53
|
+
row("Created at", new Date().toLocaleString());
|
|
54
|
+
row("Status", chalk.yellow("⊘ Not registered"));
|
|
55
|
+
br();
|
|
56
|
+
console.log(chalk.dim("Next — register this agent by sending 200 $OTX to:"));
|
|
57
|
+
console.log(" " + chalk.white(OTONIX_TREASURY));
|
|
58
|
+
br();
|
|
59
|
+
console.log(chalk.dim("Then submit the tx hash to activate:"));
|
|
60
|
+
console.log(" " + mono(`otonix agent:register --name ${name} --tx <txhash>`));
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command("agent:register")
|
|
64
|
+
.description("Activate an agent by submitting proof of 200 $OTX payment")
|
|
65
|
+
.requiredOption("--name <name>", "Agent name to register")
|
|
66
|
+
.option("--tx <hash>", "Transaction hash of your 200 $OTX transfer to Otonix treasury")
|
|
67
|
+
.action(async (opts) => {
|
|
68
|
+
header("Agent Registration");
|
|
69
|
+
const cfg = loadConfig();
|
|
70
|
+
const agent = cfg.agents[opts.name];
|
|
71
|
+
if (!agent) {
|
|
72
|
+
failure(`Agent "${opts.name}" not found. Run: otonix agent:create --name ${opts.name}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
if (agent.registered) {
|
|
76
|
+
success(`Agent "${opts.name}" is already registered!`);
|
|
77
|
+
row("Tx hash", agent.registerTxHash ?? "—");
|
|
78
|
+
row("Registered at", agent.registeredAt ? new Date(agent.registeredAt).toLocaleString() : "—");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!cfg.wallet) {
|
|
82
|
+
failure("No creator wallet found. Run: otonix wallet generate");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
let txHash = opts.tx;
|
|
86
|
+
if (!txHash) {
|
|
87
|
+
br();
|
|
88
|
+
console.log(chalk.dim("Send 200 $OTX from your creator wallet to the Otonix treasury:"));
|
|
89
|
+
console.log(" " + chalk.white(OTONIX_TREASURY));
|
|
90
|
+
br();
|
|
91
|
+
txHash = await input({ message: "Paste your transaction hash (0x...):" });
|
|
92
|
+
}
|
|
93
|
+
if (!txHash.startsWith("0x") || txHash.length !== 66) {
|
|
94
|
+
failure("Invalid tx hash. Must be 0x followed by 64 hex characters.");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const spinner = ora("Verifying transaction on Base...").start();
|
|
98
|
+
try {
|
|
99
|
+
const receipt = await publicClient.getTransactionReceipt({
|
|
100
|
+
hash: txHash,
|
|
101
|
+
});
|
|
102
|
+
if (receipt.status !== "success") {
|
|
103
|
+
spinner.fail("Transaction failed on-chain.");
|
|
104
|
+
failure("The tx hash you provided is a failed transaction.");
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
let verified = false;
|
|
108
|
+
let transferredAmount = 0n;
|
|
109
|
+
for (const log of receipt.logs) {
|
|
110
|
+
if (log.address.toLowerCase() !== OTX_ADDRESS.toLowerCase())
|
|
111
|
+
continue;
|
|
112
|
+
try {
|
|
113
|
+
const decoded = decodeEventLog({
|
|
114
|
+
abi: transferAbi,
|
|
115
|
+
data: log.data,
|
|
116
|
+
topics: log.topics,
|
|
117
|
+
});
|
|
118
|
+
if (decoded.eventName === "Transfer" &&
|
|
119
|
+
decoded.args.from.toLowerCase() === cfg.wallet.address.toLowerCase() &&
|
|
120
|
+
decoded.args.to.toLowerCase() === OTONIX_TREASURY.toLowerCase()) {
|
|
121
|
+
transferredAmount = decoded.args.value;
|
|
122
|
+
if (transferredAmount >= OTX_REQUIRED) {
|
|
123
|
+
verified = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
spinner.stop();
|
|
133
|
+
if (!verified) {
|
|
134
|
+
br();
|
|
135
|
+
failure("Verification failed.");
|
|
136
|
+
if (transferredAmount > 0n) {
|
|
137
|
+
console.log(chalk.dim(` Found transfer of ${formatOtx(transferredAmount)} $OTX — need 200.000 $OTX minimum.`));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.log(chalk.dim(" No valid $OTX transfer found in this tx."));
|
|
141
|
+
console.log(chalk.dim(" Make sure you sent from your creator wallet:"));
|
|
142
|
+
console.log(" " + chalk.white(cfg.wallet.address));
|
|
143
|
+
console.log(chalk.dim(" to Otonix treasury:"));
|
|
144
|
+
console.log(" " + chalk.white(OTONIX_TREASURY));
|
|
145
|
+
}
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
cfg.agents[opts.name].registered = true;
|
|
149
|
+
cfg.agents[opts.name].registerTxHash = txHash;
|
|
150
|
+
cfg.agents[opts.name].registeredAt = new Date().toISOString();
|
|
151
|
+
saveConfig(cfg);
|
|
152
|
+
br();
|
|
153
|
+
success(`Agent "${opts.name}" is now registered!`);
|
|
154
|
+
br();
|
|
155
|
+
row("Agent name", opts.name);
|
|
156
|
+
row("Status", chalk.green("✓ Registered & Active"));
|
|
157
|
+
row("OTX sent", formatOtx(transferredAmount) + " OTX");
|
|
158
|
+
row("Tx hash", txHash);
|
|
159
|
+
row("Treasury", OTONIX_TREASURY);
|
|
160
|
+
br();
|
|
161
|
+
console.log(chalk.dim("You can now deploy tokens with this agent:"));
|
|
162
|
+
console.log(" " + mono(`otonix launch token --agent ${opts.name} --name "MyToken" --ticker "MTK" --image "https://..." --description "..."`));
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
spinner.stop();
|
|
166
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
167
|
+
if (msg.includes("not found") || msg.includes("cannot find")) {
|
|
168
|
+
failure("Transaction not found on Base. Wait a moment and try again.");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
failure("Failed to verify tx: " + msg.slice(0, 100));
|
|
172
|
+
}
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
program
|
|
177
|
+
.command("agent:info")
|
|
178
|
+
.description("Show info and balances for an agent")
|
|
179
|
+
.argument("<name>", "Agent name")
|
|
180
|
+
.action(async (name) => {
|
|
181
|
+
header(`Agent: ${name}`);
|
|
182
|
+
const cfg = loadConfig();
|
|
183
|
+
const agent = cfg.agents[name];
|
|
184
|
+
if (!agent) {
|
|
185
|
+
failure(`Agent "${name}" not found. Run: otonix agent:list`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
const spinner = ora("Fetching balances from Base...").start();
|
|
189
|
+
try {
|
|
190
|
+
const [eth, otx] = await Promise.all([
|
|
191
|
+
getEthBalance(agent.address),
|
|
192
|
+
getOtxBalance(agent.address),
|
|
193
|
+
]);
|
|
194
|
+
spinner.stop();
|
|
195
|
+
br();
|
|
196
|
+
row("Agent name", name);
|
|
197
|
+
row("Address", agent.address);
|
|
198
|
+
row("Created at", new Date(agent.createdAt).toLocaleString());
|
|
199
|
+
row("Status", agent.registered
|
|
200
|
+
? chalk.green("✓ Registered & Active")
|
|
201
|
+
: chalk.yellow("⊘ Not registered — run: otonix agent:register --name " + name));
|
|
202
|
+
if (agent.registerTxHash)
|
|
203
|
+
row("Register tx", agent.registerTxHash);
|
|
204
|
+
br();
|
|
205
|
+
row("ETH balance", parseFloat(eth).toFixed(6) + " ETH");
|
|
206
|
+
row("$OTX balance", formatOtx(otx) + " OTX");
|
|
207
|
+
br();
|
|
208
|
+
const ethNum = parseFloat(eth);
|
|
209
|
+
if (!agent.registered) {
|
|
210
|
+
console.log(warn("⊘ ") + chalk.dim("Send 200 $OTX to treasury to register:"));
|
|
211
|
+
console.log(" " + chalk.white(OTONIX_TREASURY));
|
|
212
|
+
console.log(" " + chalk.dim(`Then: otonix agent:register --name ${name} --tx <txhash>`));
|
|
213
|
+
}
|
|
214
|
+
else if (ethNum < 0.0005) {
|
|
215
|
+
console.log(warn("⚠ ") + chalk.dim("Low ETH — fund this address with ≥0.001 ETH for gas:"));
|
|
216
|
+
console.log(" " + chalk.white(agent.address));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log(chalk.green("✓ ") + chalk.dim("Ready to deploy tokens"));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
spinner.stop();
|
|
224
|
+
failure("Failed to fetch balances.");
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
program
|
|
229
|
+
.command("agent:list")
|
|
230
|
+
.description("List all your agents")
|
|
231
|
+
.action(async () => {
|
|
232
|
+
header("Your Agents");
|
|
233
|
+
const cfg = loadConfig();
|
|
234
|
+
const names = Object.keys(cfg.agents);
|
|
235
|
+
if (names.length === 0) {
|
|
236
|
+
br();
|
|
237
|
+
info("No agents yet. Create one:");
|
|
238
|
+
info(' otonix agent:create --name "MyAgent"');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
br();
|
|
242
|
+
const spinner = ora(`Fetching balances for ${names.length} agent(s)...`).start();
|
|
243
|
+
const results = await Promise.all(names.map(async (n) => {
|
|
244
|
+
const a = cfg.agents[n];
|
|
245
|
+
try {
|
|
246
|
+
const eth = await getEthBalance(a.address);
|
|
247
|
+
return { name: n, address: a.address, eth: parseFloat(eth).toFixed(5), registered: a.registered };
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return { name: n, address: a.address, eth: "—", registered: a.registered };
|
|
251
|
+
}
|
|
252
|
+
}));
|
|
253
|
+
spinner.stop();
|
|
254
|
+
for (const r of results) {
|
|
255
|
+
const status = r.registered ? chalk.green("✓") : chalk.yellow("⊘");
|
|
256
|
+
console.log(status + " " +
|
|
257
|
+
chalk.bold.white(r.name.padEnd(16)) +
|
|
258
|
+
chalk.dim(r.address.slice(0, 10) + "...") +
|
|
259
|
+
" " +
|
|
260
|
+
chalk.yellow(r.eth + " ETH"));
|
|
261
|
+
}
|
|
262
|
+
br();
|
|
263
|
+
info(`${names.length} agent(s) total`);
|
|
264
|
+
});
|
|
265
|
+
program
|
|
266
|
+
.command("agent:remove")
|
|
267
|
+
.description("Remove an agent from local config")
|
|
268
|
+
.argument("<name>", "Agent name")
|
|
269
|
+
.action(async (name) => {
|
|
270
|
+
const cfg = loadConfig();
|
|
271
|
+
if (!cfg.agents[name]) {
|
|
272
|
+
failure(`Agent "${name}" not found.`);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
const ok = await confirm({
|
|
276
|
+
message: `Remove agent "${name}" (${cfg.agents[name].address})? This only removes local config, not on-chain.`,
|
|
277
|
+
default: false,
|
|
278
|
+
});
|
|
279
|
+
if (!ok) {
|
|
280
|
+
info("Cancelled.");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
delete cfg.agents[name];
|
|
284
|
+
saveConfig(cfg);
|
|
285
|
+
success(`Agent "${name}" removed from local config.`);
|
|
286
|
+
});
|
|
287
|
+
}
|