@towns-labs/wallet 7.1.0 → 7.3.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 +307 -196
- package/dist/cli.js +433 -353
- package/dist/session-daemon.js +16 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,468 +1,579 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tw — Towns Wallet CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your keys. Your account. No signup. No login. Just run it.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Run with bunx
|
|
5
|
+
`tw` is a local-first CLI for managing smart accounts, session keys, and on-chain permissions on Towns Protocol. It works for humans at the terminal and for agents over `--json` or `--mcp`.
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
bunx @towns-labs/wallet --help
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
---
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
## Quick Start
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Create an account and send USDC in under a minute:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
```bash
|
|
18
|
+
# 1. Create a local account (interactive password prompt)
|
|
19
|
+
tw account create --profile main
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- Default: human-readable text
|
|
21
|
+
# 2. Fund it
|
|
22
|
+
tw address --qr --amount 100
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
# 3. Send USDC
|
|
25
|
+
tw account send 10 vitalik.eth
|
|
26
|
+
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
# Show all commands
|
|
29
|
-
tw --help
|
|
28
|
+
No custody service. No API key. Everything runs locally with encrypted keystores.
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
tw account create --help
|
|
30
|
+
---
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
tw --llms
|
|
32
|
+
## How It Works
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
tw --mcp
|
|
34
|
+
`tw` manages a local keystore that holds your root key and session keys. Your root key creates and controls your on-chain smart account. Session keys are scoped signers — they can send transactions through the Towns relayer without exposing your root key.
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
tw
|
|
36
|
+
```text
|
|
37
|
+
~/.config/towns/tw/profiles/<env>/<profile>/default.keystore.json
|
|
38
|
+
~/.config/towns/tw/profiles/<env>/<profile>/sessions/<session-name>.json
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
For agents, the session daemon keeps decrypted keys in memory for a bounded duration so automated workflows can sign without prompting for a password on every call.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
### Account
|
|
45
48
|
|
|
46
|
-
Create a new account
|
|
49
|
+
#### `account create` — Create a new account
|
|
47
50
|
|
|
48
51
|
```bash
|
|
49
|
-
tw account create
|
|
52
|
+
tw account create --profile agent
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
Resume a previously interrupted flow:
|
|
53
56
|
|
|
54
57
|
```bash
|
|
55
|
-
tw account create --
|
|
58
|
+
tw account create --resume --profile agent
|
|
56
59
|
```
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
Non-interactive (stdin password + JSON):
|
|
59
62
|
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
~/.config/towns/tw/profiles/<env>/<profile>/sessions/<session-name>.json
|
|
63
|
+
```bash
|
|
64
|
+
echo "my-password" | tw account create --password-stdin --json
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
Use an explicit keystore path:
|
|
66
68
|
|
|
67
69
|
```bash
|
|
68
|
-
tw account create --
|
|
70
|
+
tw account create --keystore-path ~/.config/towns/tw/profiles/prod/team/default.keystore.json
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
#### `account status` — Check readiness
|
|
72
74
|
|
|
73
75
|
```bash
|
|
74
|
-
|
|
76
|
+
tw account status --profile agent --json
|
|
75
77
|
```
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
#### `account balance` — Check USDC balance
|
|
78
80
|
|
|
79
81
|
```bash
|
|
80
|
-
tw account
|
|
82
|
+
tw account balance --profile agent
|
|
83
|
+
tw account balance --profile agent --chain polygon
|
|
81
84
|
```
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Export metadata only (safe default):
|
|
86
|
+
#### `account address` — Print root address
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
|
-
tw account
|
|
89
|
+
tw account address --profile agent
|
|
89
90
|
```
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
#### `account send` — Send USDC
|
|
92
93
|
|
|
93
94
|
```bash
|
|
94
|
-
tw account
|
|
95
|
+
tw account send 1 0x1111111111111111111111111111111111111111
|
|
96
|
+
tw account send 2.5 vitalik.eth --chain base
|
|
97
|
+
tw account send 10 treasury --chain polygon --json
|
|
95
98
|
```
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
Use a specific local session (without changing active session):
|
|
98
101
|
|
|
99
102
|
```bash
|
|
100
|
-
tw account
|
|
103
|
+
tw account send 1 0x... --profile agent --session worker-2
|
|
101
104
|
```
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Print the main/root account address:
|
|
106
|
+
Use a portable session file (no root keystore required):
|
|
106
107
|
|
|
107
108
|
```bash
|
|
108
|
-
tw account
|
|
109
|
+
tw account send 1 0x... --chain base --session-file ./worker-1.session.json
|
|
109
110
|
```
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
`--session` and `--session-file` are mutually exclusive.
|
|
112
113
|
|
|
113
|
-
|
|
114
|
+
#### `account swap` — Swap tokens on the same chain
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
Powered by [relay.link](https://relay.link). Interactive confirmation by default.
|
|
116
117
|
|
|
117
118
|
```bash
|
|
118
|
-
tw
|
|
119
|
+
tw account swap --from ETH --to USDC --amount 0.1 --chain base
|
|
120
|
+
tw account swap --from USDC --to ETH --amount 50 --chain base --yes --json
|
|
119
121
|
```
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
#### `account bridge` — Bridge tokens cross-chain
|
|
122
124
|
|
|
123
125
|
```bash
|
|
124
|
-
tw
|
|
126
|
+
tw account bridge --token USDC --amount 100 --to-chain polygon
|
|
127
|
+
tw account bridge --token ETH --amount 0.5 --to-chain base --recipient 0x... --yes --json
|
|
125
128
|
```
|
|
126
129
|
|
|
127
|
-
|
|
130
|
+
#### `account delegate` — Delegate on additional chains
|
|
131
|
+
|
|
132
|
+
If your account was created on Base and you need it on Polygon:
|
|
128
133
|
|
|
129
134
|
```bash
|
|
130
|
-
tw
|
|
135
|
+
echo "my-password" | tw account delegate --chain polygon --profile agent --password-stdin
|
|
131
136
|
```
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
#### `account history` — Relayer call history
|
|
134
139
|
|
|
135
140
|
```bash
|
|
136
|
-
tw
|
|
141
|
+
tw account history --profile agent
|
|
142
|
+
tw account history --address 0x... --limit 10 --offset 20
|
|
143
|
+
tw account history --chain base,polygon --json
|
|
137
144
|
```
|
|
138
145
|
|
|
139
|
-
|
|
146
|
+
`--limit` defaults to 20 (max 100). `offset + limit` must be <= 1000.
|
|
147
|
+
|
|
148
|
+
#### `account export` — Export metadata or private keys
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
tw account export --profile agent --json
|
|
152
|
+
tw account export --profile agent --show-private
|
|
153
|
+
```
|
|
140
154
|
|
|
141
|
-
|
|
155
|
+
#### `account nonce` — Read account nonce
|
|
142
156
|
|
|
143
157
|
```bash
|
|
144
|
-
tw account
|
|
158
|
+
tw account nonce --profile agent
|
|
145
159
|
```
|
|
146
160
|
|
|
147
|
-
|
|
161
|
+
#### `account update password` — Rotate keystore password
|
|
148
162
|
|
|
149
163
|
```bash
|
|
150
|
-
tw account
|
|
164
|
+
tw account update password --profile agent
|
|
165
|
+
printf "old\nnew\n" | tw account update password --current-password-stdin --new-password-stdin --json
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
---
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
### Address
|
|
156
171
|
|
|
157
|
-
|
|
172
|
+
`tw address` is a top-level shortcut for showing your funding address with optional payment helpers.
|
|
158
173
|
|
|
159
174
|
```bash
|
|
160
|
-
tw
|
|
175
|
+
tw address
|
|
176
|
+
tw address --link --amount 100
|
|
177
|
+
tw address --qr --amount 100
|
|
161
178
|
```
|
|
162
179
|
|
|
163
|
-
|
|
180
|
+
Custom token:
|
|
164
181
|
|
|
165
182
|
```bash
|
|
166
|
-
tw
|
|
183
|
+
tw address --token 0x... --amount 1 --decimals 18 --link
|
|
167
184
|
```
|
|
168
185
|
|
|
169
|
-
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### Session Keys
|
|
189
|
+
|
|
190
|
+
Session keys are scoped signers that can act on behalf of your account without exposing the root key.
|
|
191
|
+
|
|
192
|
+
#### `session create` — Create and authorize a session key
|
|
170
193
|
|
|
171
194
|
```bash
|
|
172
|
-
|
|
195
|
+
echo "my-password" | tw session create worker-1 --profile agent --password-stdin --json
|
|
173
196
|
```
|
|
174
197
|
|
|
175
|
-
|
|
198
|
+
Create and activate immediately:
|
|
176
199
|
|
|
177
200
|
```bash
|
|
178
|
-
tw
|
|
201
|
+
echo "my-password" | tw session create worker-2 --profile agent --activate --password-stdin
|
|
179
202
|
```
|
|
180
203
|
|
|
181
|
-
|
|
204
|
+
Grant full wildcard access:
|
|
182
205
|
|
|
183
206
|
```bash
|
|
184
|
-
tw
|
|
207
|
+
echo "my-password" | tw session create worker-admin --profile agent --full-access --password-stdin
|
|
185
208
|
```
|
|
186
209
|
|
|
187
|
-
|
|
210
|
+
`--full-access` is mutually exclusive with `--target`, `--selector`, `--spend-limit`, `--spend-limit-raw`, and `--spend-period`. Omitting both `--target` and `--selector` uses wildcard call permissions by default.
|
|
188
211
|
|
|
189
|
-
|
|
212
|
+
Resume an interrupted flow:
|
|
190
213
|
|
|
191
|
-
```
|
|
192
|
-
|
|
214
|
+
```bash
|
|
215
|
+
echo "my-password" | tw session create worker-1 --profile agent --resume --password-stdin --json
|
|
193
216
|
```
|
|
194
217
|
|
|
195
|
-
|
|
218
|
+
#### `session list` — List local sessions
|
|
196
219
|
|
|
197
220
|
```bash
|
|
198
|
-
tw
|
|
199
|
-
tw
|
|
221
|
+
tw session list --profile agent --json
|
|
222
|
+
tw session list --profile agent --on-chain --json
|
|
200
223
|
```
|
|
201
224
|
|
|
202
|
-
|
|
225
|
+
#### `session rotate` — Rotate the active session
|
|
203
226
|
|
|
204
227
|
```bash
|
|
205
|
-
tw
|
|
206
|
-
tw
|
|
228
|
+
echo "my-password" | tw session rotate --profile agent --password-stdin --json
|
|
229
|
+
echo "my-password" | tw session rotate --profile agent --new-name worker-3 --password-stdin
|
|
230
|
+
echo "my-password" | tw session rotate --profile agent --resume --password-stdin --json
|
|
207
231
|
```
|
|
208
232
|
|
|
209
|
-
|
|
233
|
+
#### `session revoke` — Revoke a session on-chain
|
|
210
234
|
|
|
211
235
|
```bash
|
|
212
|
-
tw
|
|
213
|
-
tw
|
|
214
|
-
tw
|
|
236
|
+
echo "my-password" | tw session revoke worker-1 --profile agent --password-stdin --json
|
|
237
|
+
echo "my-password" | tw session revoke worker-2 --profile agent --force --password-stdin
|
|
238
|
+
echo "my-password" | tw session revoke worker-2 --profile agent --resume --password-stdin --json
|
|
215
239
|
```
|
|
216
240
|
|
|
217
|
-
Export
|
|
241
|
+
#### `session export` — Export as portable file
|
|
218
242
|
|
|
219
243
|
```bash
|
|
220
|
-
|
|
221
|
-
tw
|
|
222
|
-
tw contacts import ./contacts.json --force --json
|
|
244
|
+
TW_PASSWORD="my-password" TW_EXPORT_PASSWORD="export-password" \
|
|
245
|
+
tw session export worker-1 --profile agent --output ./worker-1.session.json
|
|
223
246
|
```
|
|
224
247
|
|
|
225
|
-
|
|
248
|
+
Or via stdin:
|
|
226
249
|
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
|
|
250
|
+
```bash
|
|
251
|
+
echo "export-password" | tw session export worker-1 --profile agent \
|
|
252
|
+
--output ./worker-1.session.json --export-password-stdin
|
|
253
|
+
```
|
|
231
254
|
|
|
232
|
-
|
|
255
|
+
#### `session import` — Import a portable session
|
|
233
256
|
|
|
234
257
|
```bash
|
|
235
|
-
tw
|
|
258
|
+
tw session import ./worker-1.session.json --profile worker-1
|
|
236
259
|
```
|
|
237
260
|
|
|
238
|
-
|
|
261
|
+
Creates a session-only profile that can execute `account send` but cannot run manager commands requiring a root keystore.
|
|
239
262
|
|
|
240
|
-
|
|
241
|
-
- If ENS re-resolution succeeds but points to a different address than stored, send aborts and requires explicit `tw contacts update`.
|
|
263
|
+
---
|
|
242
264
|
|
|
243
|
-
|
|
265
|
+
### Session Daemon
|
|
266
|
+
|
|
267
|
+
The daemon holds decrypted session keys in memory so automated workflows can sign without re-entering passwords.
|
|
268
|
+
|
|
269
|
+
#### `session start` — Start the daemon
|
|
244
270
|
|
|
245
271
|
```bash
|
|
246
|
-
tw
|
|
247
|
-
|
|
272
|
+
tw session start --json
|
|
273
|
+
tw session start --foreground --json
|
|
248
274
|
```
|
|
249
275
|
|
|
250
|
-
|
|
276
|
+
Idempotent — if already running, returns the active pid/socket.
|
|
251
277
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Show compact readiness status:
|
|
278
|
+
#### `session unlock` — Load a key into daemon memory
|
|
255
279
|
|
|
256
280
|
```bash
|
|
257
|
-
tw
|
|
281
|
+
echo "my-password" | tw session unlock worker-1 --profile agent --password-stdin --duration 15m --json
|
|
258
282
|
```
|
|
259
283
|
|
|
260
|
-
|
|
284
|
+
Key material stays in daemon memory only; encrypted files remain unchanged.
|
|
285
|
+
|
|
286
|
+
#### `session lock` — Remove a key from daemon memory
|
|
261
287
|
|
|
262
288
|
```bash
|
|
263
|
-
tw
|
|
289
|
+
tw session lock worker-1 --json
|
|
264
290
|
```
|
|
265
291
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Rotate local keystore encryption password (interactive):
|
|
292
|
+
#### `session status` — Check daemon health
|
|
269
293
|
|
|
270
294
|
```bash
|
|
271
|
-
tw
|
|
295
|
+
tw session status --json
|
|
272
296
|
```
|
|
273
297
|
|
|
274
|
-
|
|
298
|
+
#### `session stop` — Shut down the daemon
|
|
275
299
|
|
|
276
300
|
```bash
|
|
277
|
-
|
|
301
|
+
tw session stop --json
|
|
278
302
|
```
|
|
279
303
|
|
|
280
|
-
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### Permissions
|
|
307
|
+
|
|
308
|
+
Fine-grained on-chain permission rules for session keys.
|
|
281
309
|
|
|
282
|
-
|
|
310
|
+
#### `permissions list` — List keys and their permissions
|
|
283
311
|
|
|
284
312
|
```bash
|
|
285
|
-
|
|
313
|
+
tw permissions list --profile agent --json
|
|
286
314
|
```
|
|
287
315
|
|
|
288
|
-
|
|
316
|
+
#### `permissions show` — Show rules for a specific key
|
|
289
317
|
|
|
290
318
|
```bash
|
|
291
|
-
|
|
319
|
+
tw permissions show agent-key --profile agent --json
|
|
320
|
+
tw permissions show --key-hash 0xaaa... --profile agent --json
|
|
292
321
|
```
|
|
293
322
|
|
|
294
|
-
|
|
323
|
+
#### `permissions grant` — Grant a permission rule
|
|
324
|
+
|
|
325
|
+
Wildcard call permission:
|
|
295
326
|
|
|
296
327
|
```bash
|
|
297
|
-
echo "my-password" | tw
|
|
328
|
+
echo "my-password" | tw permissions grant agent-key --type call --target any --selector any --profile agent --password-stdin --json
|
|
298
329
|
```
|
|
299
330
|
|
|
300
|
-
|
|
331
|
+
Daily USDC spend limit:
|
|
301
332
|
|
|
302
333
|
```bash
|
|
303
|
-
echo "my-password" | tw
|
|
334
|
+
echo "my-password" | tw permissions grant agent-key --type spend --token USDC --spend-limit 100 --period day --profile agent --password-stdin --json
|
|
304
335
|
```
|
|
305
336
|
|
|
306
|
-
`--
|
|
307
|
-
Omitting both `--target` and `--selector` uses wildcard call permissions by default; you do not need to pass magic wildcard values.
|
|
308
|
-
`--spend-limit` accepts human USDC amounts (for example `10` = `10 USDC`). Use `--spend-limit-raw` for base units.
|
|
337
|
+
Use `--spend-limit-raw` for base units instead of human amounts.
|
|
309
338
|
|
|
310
|
-
|
|
339
|
+
#### `permissions revoke` — Revoke permission rules
|
|
311
340
|
|
|
312
|
-
|
|
341
|
+
Revoke a single rule:
|
|
313
342
|
|
|
314
343
|
```bash
|
|
315
|
-
tw
|
|
344
|
+
echo "my-password" | tw permissions revoke agent-key --rule call:0x...:0xa9059cbb --profile agent --password-stdin --json
|
|
316
345
|
```
|
|
317
346
|
|
|
318
|
-
|
|
347
|
+
Revoke all rules for a key:
|
|
319
348
|
|
|
320
349
|
```bash
|
|
321
|
-
tw
|
|
350
|
+
echo "my-password" | tw permissions revoke agent-key --all --profile agent --password-stdin --json
|
|
322
351
|
```
|
|
323
352
|
|
|
324
|
-
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
### Escrow
|
|
356
|
+
|
|
357
|
+
USDC escrow: lock funds as buyer with a seller and oracle; settle (oracle signs) or refund after deadline.
|
|
325
358
|
|
|
326
|
-
|
|
359
|
+
#### `escrow create` — Create a new USDC escrow
|
|
327
360
|
|
|
328
361
|
```bash
|
|
329
|
-
|
|
330
|
-
|
|
362
|
+
tw escrow create 50 0x1111111111111111111111111111111111111111 \
|
|
363
|
+
--oracle 0x2222222222222222222222222222222222222222 --deadline 24h
|
|
364
|
+
tw escrow create 100 0x... --oracle 0x... --deadline 2d --chain base --env prod --json
|
|
331
365
|
```
|
|
332
366
|
|
|
333
|
-
|
|
367
|
+
- **Amount** and **seller** are positional; **oracle** and **deadline** are required options.
|
|
368
|
+
- **Deadline**: relative (`1h`, `2d`, `30m`, `1w`) or unix timestamp. After deadline, anyone can call `escrow refund`.
|
|
369
|
+
- Optional **salt** (hex, up to 12 bytes) for unique escrow IDs on repeated orders.
|
|
370
|
+
- Uses session key (daemon or direct) to sign; supports `--session-file` like `account send`.
|
|
371
|
+
|
|
372
|
+
#### `escrow status` — Check on-chain status
|
|
334
373
|
|
|
335
374
|
```bash
|
|
336
|
-
|
|
337
|
-
|
|
375
|
+
tw escrow status 0x<64 hex chars> --env prod
|
|
376
|
+
tw escrow status 0x... --chain base --json
|
|
338
377
|
```
|
|
339
378
|
|
|
340
|
-
|
|
379
|
+
Escrow ID must be the full 32-byte hex (0x + 64 hex chars), e.g. from `escrow create` output.
|
|
341
380
|
|
|
342
|
-
|
|
381
|
+
#### `escrow settle` — Settle (release USDC to seller)
|
|
382
|
+
|
|
383
|
+
Oracle signs a settlement; any account can submit the signed settlement via relayer.
|
|
343
384
|
|
|
344
385
|
```bash
|
|
345
|
-
tw
|
|
386
|
+
TW_ORACLE_PRIVATE_KEY=0x... tw escrow settle 0x<escrowId> \
|
|
387
|
+
--settlement-id 0x<orderId> --oracle 0x2222... --profile agent
|
|
346
388
|
```
|
|
347
389
|
|
|
348
|
-
|
|
390
|
+
Or with pre-signed signature (oracle signs offline):
|
|
349
391
|
|
|
350
392
|
```bash
|
|
351
|
-
|
|
393
|
+
tw escrow settle 0x<escrowId> --settlement-id 0x... --oracle 0x... --signature 0x... --profile agent
|
|
352
394
|
```
|
|
353
395
|
|
|
354
|
-
|
|
396
|
+
Settlement ID is the bytes32 order ID used at creation (see create output or `escrow status`).
|
|
355
397
|
|
|
356
|
-
|
|
398
|
+
#### `escrow refund` — Refund to buyer after deadline
|
|
357
399
|
|
|
358
|
-
|
|
400
|
+
Permissionless: after the escrow deadline, anyone can trigger refund to the buyer.
|
|
359
401
|
|
|
360
402
|
```bash
|
|
361
|
-
|
|
403
|
+
tw escrow refund 0x<escrowId> --profile agent
|
|
404
|
+
tw escrow refund 0x<escrowId> --env prod --json
|
|
362
405
|
```
|
|
363
406
|
|
|
364
|
-
|
|
407
|
+
---
|
|
365
408
|
|
|
366
|
-
|
|
367
|
-
|
|
409
|
+
### Contacts
|
|
410
|
+
|
|
411
|
+
Store recipient aliases so you never have to copy-paste addresses.
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
~/.config/towns/tw/contacts.json
|
|
368
415
|
```
|
|
369
416
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
417
|
+
```bash
|
|
418
|
+
tw contacts add vitalik vitalik.eth
|
|
419
|
+
tw contacts add treasury 0x1111111111111111111111111111111111111111
|
|
420
|
+
tw contacts list --json
|
|
421
|
+
tw contacts show vitalik --json
|
|
422
|
+
tw contacts update vitalik 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
423
|
+
tw contacts rename vitalik vitalik-main
|
|
424
|
+
tw contacts remove vitalik-main
|
|
425
|
+
```
|
|
373
426
|
|
|
374
|
-
|
|
427
|
+
Export and import:
|
|
375
428
|
|
|
376
429
|
```bash
|
|
377
|
-
|
|
430
|
+
tw contacts export --json
|
|
431
|
+
tw contacts import ./contacts.json --json
|
|
432
|
+
tw contacts import ./contacts.json --force --json
|
|
378
433
|
```
|
|
379
434
|
|
|
380
|
-
|
|
435
|
+
Import returns `importedCount`, `skippedInvalidCount`, `skippedConflictCount`, and `overwrittenCount`.
|
|
381
436
|
|
|
382
|
-
|
|
437
|
+
Send via alias:
|
|
383
438
|
|
|
384
439
|
```bash
|
|
385
|
-
|
|
440
|
+
tw account send 10 vitalik
|
|
386
441
|
```
|
|
387
442
|
|
|
388
|
-
|
|
443
|
+
ENS safety: if ENS re-resolves to a different address than stored, send aborts and requires `tw contacts update`.
|
|
389
444
|
|
|
390
|
-
|
|
391
|
-
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### Agents
|
|
448
|
+
|
|
449
|
+
Agents are session-key-backed Towns identities with their own encryption device and named channel bindings — built for autonomous agent-to-agent communication.
|
|
450
|
+
|
|
451
|
+
```text
|
|
452
|
+
~/.config/towns/tw/profiles/<env>/<profile>/sessions/agent-<name>.json
|
|
453
|
+
~/.config/towns/tw/profiles/<env>/<profile>/agent-channels.json
|
|
392
454
|
```
|
|
393
455
|
|
|
394
|
-
|
|
456
|
+
#### `agent create` / `agent list` / `agent remove`
|
|
395
457
|
|
|
396
458
|
```bash
|
|
397
|
-
|
|
459
|
+
TW_PASSWORD="my-password" tw agent create alice --profile agent
|
|
460
|
+
TW_PASSWORD="my-password" tw agent create bob --profile agent
|
|
461
|
+
TW_PASSWORD="my-password" tw agent list --profile agent --json
|
|
462
|
+
TW_PASSWORD="my-password" tw agent remove bob --profile agent
|
|
398
463
|
```
|
|
399
464
|
|
|
400
|
-
|
|
465
|
+
#### `agent connect` — Create or bind a named channel
|
|
401
466
|
|
|
402
|
-
|
|
467
|
+
First side creates the channel and returns the shared secret:
|
|
403
468
|
|
|
404
469
|
```bash
|
|
405
|
-
tw
|
|
470
|
+
TW_PASSWORD="my-password" tw agent connect --from alice --channel art --to bob --profile agent
|
|
406
471
|
```
|
|
407
472
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
Show full permission rules for a key using positional `<key-ref>`:
|
|
473
|
+
Second side binds using that secret:
|
|
411
474
|
|
|
412
475
|
```bash
|
|
413
|
-
tw
|
|
476
|
+
TW_PASSWORD="my-password" tw agent connect --from bob --channel art --secret "<shared-secret>" --to alice --profile agent
|
|
414
477
|
```
|
|
415
478
|
|
|
416
|
-
|
|
479
|
+
#### `agent send` — Send a message
|
|
480
|
+
|
|
481
|
+
To a named channel:
|
|
417
482
|
|
|
418
483
|
```bash
|
|
419
|
-
tw
|
|
484
|
+
TW_PASSWORD="my-password" tw agent send --from alice --channel art "hello from alice" --profile agent
|
|
420
485
|
```
|
|
421
486
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
Grant wildcard call permission:
|
|
487
|
+
To a raw stream ID:
|
|
425
488
|
|
|
426
489
|
```bash
|
|
427
|
-
|
|
490
|
+
TW_PASSWORD="my-password" tw agent send --from alice 77aaa... "debug message" --profile agent
|
|
428
491
|
```
|
|
429
492
|
|
|
430
|
-
|
|
493
|
+
#### `agent listen` — Listen for messages
|
|
431
494
|
|
|
432
495
|
```bash
|
|
433
|
-
|
|
496
|
+
TW_PASSWORD="my-password" tw agent listen --from bob --channel art --profile agent
|
|
497
|
+
TW_PASSWORD="my-password" tw agent listen --from bob --stream 77aaa... --profile agent
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Messages arrive as NDJSON:
|
|
501
|
+
|
|
502
|
+
```json
|
|
503
|
+
{
|
|
504
|
+
"type": "message",
|
|
505
|
+
"streamId": "77...",
|
|
506
|
+
"senderId": "0x...",
|
|
507
|
+
"eventId": "0x...",
|
|
508
|
+
"timestamp": 1709654400,
|
|
509
|
+
"content": "hello from alice"
|
|
510
|
+
}
|
|
434
511
|
```
|
|
435
512
|
|
|
436
|
-
|
|
513
|
+
#### `agent channels` — Inspect bindings
|
|
437
514
|
|
|
438
515
|
```bash
|
|
439
|
-
|
|
516
|
+
TW_PASSWORD="my-password" tw agent channels --from alice --profile agent --json
|
|
440
517
|
```
|
|
441
518
|
|
|
442
|
-
|
|
519
|
+
#### Agent Quickstart
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
# Create profile + two agents
|
|
523
|
+
tw account create --profile agent
|
|
524
|
+
TW_PASSWORD="pw" tw agent create alice --profile agent
|
|
525
|
+
TW_PASSWORD="pw" tw agent create bob --profile agent
|
|
526
|
+
|
|
527
|
+
# Alice creates a channel → copy the returned secret
|
|
528
|
+
TW_PASSWORD="pw" tw agent connect --from alice --channel art --to bob --profile agent
|
|
529
|
+
|
|
530
|
+
# Bob binds with that secret
|
|
531
|
+
TW_PASSWORD="pw" tw agent connect --from bob --channel art --secret "<secret>" --to alice --profile agent
|
|
443
532
|
|
|
444
|
-
|
|
533
|
+
# Terminal 1: listen
|
|
534
|
+
TW_PASSWORD="pw" tw agent listen --from bob --channel art --profile agent
|
|
535
|
+
|
|
536
|
+
# Terminal 2: send
|
|
537
|
+
TW_PASSWORD="pw" tw agent send --from alice --channel art "hello" --profile agent
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
Smoke test:
|
|
445
541
|
|
|
446
542
|
```bash
|
|
447
|
-
|
|
543
|
+
cd packages/wallet
|
|
544
|
+
TW_PASSWORD="my-password" bun run smoke:agent
|
|
448
545
|
```
|
|
449
546
|
|
|
450
|
-
|
|
547
|
+
---
|
|
548
|
+
|
|
549
|
+
## Agent & Automation Integration
|
|
550
|
+
|
|
551
|
+
Every command supports `--json` for machine-readable output. Treat JSON output schemas as the stable contract.
|
|
451
552
|
|
|
452
553
|
```bash
|
|
453
|
-
|
|
554
|
+
tw <command> --json
|
|
454
555
|
```
|
|
455
556
|
|
|
456
|
-
|
|
557
|
+
### Password Automation
|
|
457
558
|
|
|
458
|
-
Use `TW_PASSWORD`
|
|
559
|
+
Use `TW_PASSWORD` to skip interactive prompts:
|
|
459
560
|
|
|
460
561
|
```bash
|
|
461
562
|
TW_PASSWORD="my-password" tw session list --profile agent --json
|
|
462
563
|
```
|
|
463
564
|
|
|
464
|
-
|
|
565
|
+
Or pipe via stdin for commands that accept `--password-stdin`:
|
|
465
566
|
|
|
466
567
|
```bash
|
|
467
568
|
echo "my-password" | tw session rotate --profile agent --password-stdin --json
|
|
468
569
|
```
|
|
570
|
+
|
|
571
|
+
### Discovery
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
tw --help # All commands
|
|
575
|
+
tw account send --help # Command-specific help
|
|
576
|
+
tw --llms # Machine-readable command manifest
|
|
577
|
+
tw --mcp # Run as MCP server
|
|
578
|
+
tw --version # Version
|
|
579
|
+
```
|