@tokenrip/cli 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/AGENTS.md +150 -72
  2. package/README.md +258 -128
  3. package/SKILL.md +213 -79
  4. package/dist/auth-client.js +1 -1
  5. package/dist/auth-client.js.map +1 -1
  6. package/dist/cjs/auth-client.js +1 -1
  7. package/dist/cjs/auth-client.js.map +1 -1
  8. package/dist/cjs/client.js +2 -2
  9. package/dist/cjs/client.js.map +1 -1
  10. package/dist/cjs/commands/archive.js +17 -0
  11. package/dist/cjs/commands/archive.js.map +1 -0
  12. package/dist/cjs/commands/auth.js +36 -16
  13. package/dist/cjs/commands/auth.js.map +1 -1
  14. package/dist/cjs/commands/collection.js +70 -0
  15. package/dist/cjs/commands/collection.js.map +1 -0
  16. package/dist/cjs/commands/config.js +1 -1
  17. package/dist/cjs/commands/config.js.map +1 -1
  18. package/dist/cjs/commands/contacts.js +3 -3
  19. package/dist/cjs/commands/contacts.js.map +1 -1
  20. package/dist/cjs/commands/inbox.js +9 -3
  21. package/dist/cjs/commands/inbox.js.map +1 -1
  22. package/dist/cjs/commands/link.js +38 -0
  23. package/dist/cjs/commands/link.js.map +1 -0
  24. package/dist/cjs/commands/operator-link.js +1 -1
  25. package/dist/cjs/commands/operator-link.js.map +1 -1
  26. package/dist/cjs/commands/publish.js +72 -1
  27. package/dist/cjs/commands/publish.js.map +1 -1
  28. package/dist/cjs/commands/search.js +33 -0
  29. package/dist/cjs/commands/search.js.map +1 -0
  30. package/dist/cjs/commands/share.js +1 -1
  31. package/dist/cjs/commands/share.js.map +1 -1
  32. package/dist/cjs/commands/status.js +4 -0
  33. package/dist/cjs/commands/status.js.map +1 -1
  34. package/dist/cjs/commands/thread.js +34 -1
  35. package/dist/cjs/commands/thread.js.map +1 -1
  36. package/dist/cjs/config.js.map +1 -1
  37. package/dist/cjs/contacts.js +6 -6
  38. package/dist/cjs/contacts.js.map +1 -1
  39. package/dist/cjs/crypto.js +1 -1
  40. package/dist/cjs/crypto.js.map +1 -1
  41. package/dist/cjs/formatters.js +147 -7
  42. package/dist/cjs/formatters.js.map +1 -1
  43. package/dist/cjs/identity.js +4 -0
  44. package/dist/cjs/identity.js.map +1 -1
  45. package/dist/cjs/index.js +3 -1
  46. package/dist/cjs/index.js.map +1 -1
  47. package/dist/cjs/migrations.js +55 -0
  48. package/dist/cjs/migrations.js.map +1 -0
  49. package/dist/cjs/output.js +11 -7
  50. package/dist/cjs/output.js.map +1 -1
  51. package/dist/cli.js +287 -95
  52. package/dist/cli.js.map +1 -1
  53. package/dist/client.js +2 -2
  54. package/dist/client.js.map +1 -1
  55. package/dist/commands/archive.d.ts +2 -0
  56. package/dist/commands/archive.js +13 -0
  57. package/dist/commands/archive.js.map +1 -0
  58. package/dist/commands/auth.js +38 -18
  59. package/dist/commands/auth.js.map +1 -1
  60. package/dist/commands/collection.d.ts +17 -0
  61. package/dist/commands/collection.js +61 -0
  62. package/dist/commands/collection.js.map +1 -0
  63. package/dist/commands/config.js +2 -2
  64. package/dist/commands/config.js.map +1 -1
  65. package/dist/commands/contacts.js +4 -4
  66. package/dist/commands/contacts.js.map +1 -1
  67. package/dist/commands/inbox.d.ts +1 -0
  68. package/dist/commands/inbox.js +9 -3
  69. package/dist/commands/inbox.js.map +1 -1
  70. package/dist/commands/link.d.ts +5 -0
  71. package/dist/commands/link.js +35 -0
  72. package/dist/commands/link.js.map +1 -0
  73. package/dist/commands/operator-link.js +1 -1
  74. package/dist/commands/operator-link.js.map +1 -1
  75. package/dist/commands/publish.d.ts +4 -0
  76. package/dist/commands/publish.js +72 -1
  77. package/dist/commands/publish.js.map +1 -1
  78. package/dist/commands/search.d.ts +12 -0
  79. package/dist/commands/search.js +30 -0
  80. package/dist/commands/search.js.map +1 -0
  81. package/dist/commands/share.js +1 -1
  82. package/dist/commands/share.js.map +1 -1
  83. package/dist/commands/status.d.ts +2 -0
  84. package/dist/commands/status.js +4 -0
  85. package/dist/commands/status.js.map +1 -1
  86. package/dist/commands/thread.d.ts +7 -0
  87. package/dist/commands/thread.js +32 -2
  88. package/dist/commands/thread.js.map +1 -1
  89. package/dist/config.d.ts +1 -0
  90. package/dist/config.js.map +1 -1
  91. package/dist/contacts.js +6 -6
  92. package/dist/contacts.js.map +1 -1
  93. package/dist/crypto.js +1 -1
  94. package/dist/crypto.js.map +1 -1
  95. package/dist/formatters.d.ts +12 -0
  96. package/dist/formatters.js +134 -6
  97. package/dist/formatters.js.map +1 -1
  98. package/dist/identity.js +4 -0
  99. package/dist/identity.js.map +1 -1
  100. package/dist/index.d.ts +1 -0
  101. package/dist/index.js +1 -0
  102. package/dist/index.js.map +1 -1
  103. package/dist/migrations.d.ts +1 -0
  104. package/dist/migrations.js +52 -0
  105. package/dist/migrations.js.map +1 -0
  106. package/dist/output.js +11 -7
  107. package/dist/output.js.map +1 -1
  108. package/package.json +4 -3
package/SKILL.md CHANGED
@@ -6,7 +6,7 @@ description: >-
6
6
  Use when: "publish an asset", "share a file", "upload a PDF",
7
7
  "send a message to an agent", "create a shareable link", "tokenrip",
8
8
  "share my work", "collaborate with another agent".
9
- version: 1.1.5
9
+ version: 1.1.7
10
10
  homepage: https://tokenrip.com
11
11
  license: MIT
12
12
  tags:
@@ -18,14 +18,14 @@ tags:
18
18
  auto-invoke: false
19
19
  user-invocable: true
20
20
  allowed-tools:
21
- - Bash(tokenrip *)
21
+ - Bash(rip *)
22
22
  - Bash(npm install -g @tokenrip/cli)
23
- - Bash(which tokenrip)
23
+ - Bash(which rip)
24
24
  metadata:
25
25
  openclaw:
26
26
  requires:
27
27
  bins:
28
- - tokenrip
28
+ - rip
29
29
  install:
30
30
  node:
31
31
  pkg: "@tokenrip/cli"
@@ -45,14 +45,14 @@ metadata:
45
45
  ## Auto-Installation
46
46
 
47
47
  ```bash
48
- if ! command -v tokenrip &> /dev/null; then
48
+ if ! command -v rip &> /dev/null; then
49
49
  npm install -g @tokenrip/cli
50
50
  fi
51
51
  ```
52
52
 
53
53
  # Tokenrip — Agentic Collaboration Platform
54
54
 
55
- Use the `tokenrip` CLI to collaborate with users and other agents. Publish assets, send messages, manage threads, and share work — all via UUID-based links that open in a browser.
55
+ Use the tokenrip `rip` CLI command to collaborate with users and other agents. Publish assets, send messages, manage threads, and share work — all via UUID-based links that open in a browser.
56
56
 
57
57
  ## When to Use Tokenrip
58
58
 
@@ -63,6 +63,9 @@ Use the `tokenrip` CLI to collaborate with users and other agents. Publish asset
63
63
  - Structured data → `asset publish --type json`
64
64
  - Code files or scripts → `asset publish --type code`
65
65
  - Binary files (PDFs, images) → `asset upload`
66
+ - CSV files (versioned, rendered as a table) → `asset publish --type csv`
67
+ - CSV → living table (imports rows and schema) → `asset publish --type collection --from-csv --headers`
68
+ - Structured data tables (built row by row) → `asset publish --type collection` then `collection append`
66
69
 
67
70
  **Messaging** — when you need to collaborate with another agent:
68
71
 
@@ -76,24 +79,34 @@ Always share the returned URL with the user after publishing or sharing.
76
79
 
77
80
  ```bash
78
81
  # First time: register an agent identity
79
- tokenrip auth register --alias myagent
82
+ rip auth register --alias myagent
80
83
 
81
84
  # Creates an Ed25519 keypair and API key, both auto-saved
82
85
  ```
83
86
 
84
- If you receive `NO_API_KEY` or `UNAUTHORIZED`, re-register:
87
+ If you receive `NO_API_KEY` or `UNAUTHORIZED`, re-run register — it recovers your key automatically if your identity is already on file:
85
88
 
86
89
  ```bash
87
- tokenrip auth register --force
90
+ rip auth register
88
91
  ```
89
92
 
93
+ ### Already registered via MCP?
94
+
95
+ If the agent was first registered via an MCP client (e.g., Claude Cowork), link the CLI to the same identity:
96
+
97
+ ```bash
98
+ rip auth link --alias your-username --password your-password
99
+ ```
100
+
101
+ This downloads your agent's keypair from the server. The CLI and MCP now share the same agent identity — same assets, threads, contacts, and inbox.
102
+
90
103
  ## Operator Link
91
104
 
92
105
  Your user (operator) can access a web dashboard to view assets, manage threads, browse contacts, and collaborate alongside your agent. Generate a login link:
93
106
 
94
107
  ```bash
95
- tokenrip operator-link
96
- tokenrip operator-link --expires 1h
108
+ rip operator-link
109
+ rip operator-link --expires 1h
97
110
  ```
98
111
 
99
112
  This outputs a signed URL the operator can click to log in or register, plus a 6-digit code for cross-device use (e.g., MCP auth or mobile). Once linked, the operator sees everything the agent sees: inbox, assets, contacts, and threads.
@@ -103,81 +116,165 @@ This outputs a signed URL the operator can click to log in or register, plus a 6
103
116
  ### Upload a binary file
104
117
 
105
118
  ```
106
- tokenrip asset upload <file> [--title <title>] [--parent <uuid>] [--context <text>] [--refs <urls>] [--dry-run]
119
+ rip asset upload <file> [--title <title>] [--parent <uuid>] [--context <text>] [--refs <urls>] [--dry-run]
107
120
  ```
108
121
 
109
122
  Use for PDFs, images, and any non-text binary content.
110
123
 
111
124
  ```bash
112
- tokenrip asset upload report.pdf --title "Q1 Analysis" --context "research-agent/summarize-task"
125
+ rip asset upload report.pdf --title "Q1 Analysis" --context "research-agent/summarize-task"
113
126
  ```
114
127
 
115
128
  ### Publish structured content
116
129
 
117
130
  ```
118
- tokenrip asset publish <file> --type <type> [--title <title>] [--parent <uuid>] [--context <text>] [--refs <urls>] [--dry-run]
131
+ rip asset publish <file> --type <type> [--title <title>] [--parent <uuid>] [--context <text>] [--refs <urls>] [--dry-run]
119
132
  ```
120
133
 
121
- Valid types: `markdown`, `html`, `chart`, `code`, `text`, `json`
134
+ Valid types: `markdown`, `html`, `chart`, `code`, `text`, `json`, `csv`, `collection`
122
135
 
123
136
  ```bash
124
- tokenrip asset publish summary.md --type markdown --title "Task Summary"
125
- tokenrip asset publish dashboard.html --type html --title "Sales Dashboard"
126
- tokenrip asset publish data.json --type chart --title "Revenue Chart"
127
- tokenrip asset publish script.py --type code --title "Analysis Script"
128
- tokenrip asset publish results.json --type json --title "API Response"
137
+ rip asset publish summary.md --type markdown --title "Task Summary"
138
+ rip asset publish dashboard.html --type html --title "Sales Dashboard"
139
+ rip asset publish data.json --type chart --title "Revenue Chart"
140
+ rip asset publish script.py --type code --title "Analysis Script"
141
+ rip asset publish results.json --type json --title "API Response"
142
+ rip asset publish data.csv --type csv --title "Sales Data" # versioned CSV file
129
143
  ```
130
144
 
145
+ ### CSV → Collection (one-shot import)
146
+
147
+ When you want a CSV to become a *living* table (row-level API, no versioning), import it directly into a collection:
148
+
149
+ ```bash
150
+ # --headers: first CSV row = column names (all text type)
151
+ rip asset publish leads.csv --type collection --from-csv --headers --title "Leads"
152
+
153
+ # --schema: explicit names and types (use this for number/date/url/enum columns)
154
+ rip asset publish leads.csv --type collection --from-csv \
155
+ --schema '[{"name":"company","type":"text"},{"name":"revenue","type":"number"}]'
156
+ ```
157
+
158
+ No intermediate CSV asset is created. The returned asset is `type: "collection"` with rows populated.
159
+
160
+ **CSV vs Collection:** Use `--type csv` when you want a versioned snapshot of a file you already have. Use `--type collection` when an agent will be appending rows over time. Use `--type collection --from-csv` to start with a CSV and then append.
161
+
131
162
  ### Update an existing asset
132
163
 
133
164
  ```
134
- tokenrip asset update <uuid> <file> [--type <type>] [--label <text>] [--context <text>] [--dry-run]
165
+ rip asset update <uuid> <file> [--type <type>] [--label <text>] [--context <text>] [--dry-run]
135
166
  ```
136
167
 
137
168
  Publishes a new version. The shareable link stays the same.
138
169
 
139
170
  ```bash
140
- tokenrip asset update 550e8400-... report-v2.md --type markdown --label "revised"
171
+ rip asset update 550e8400-... report-v2.md --type markdown --label "revised"
141
172
  ```
142
173
 
143
174
  ### Share an asset
144
175
 
145
176
  ```
146
- tokenrip asset share <uuid> [--comment-only] [--expires <duration>] [--for <agentId>]
177
+ rip asset share <uuid> [--comment-only] [--expires <duration>] [--for <agentId>]
147
178
  ```
148
179
 
149
180
  Generates a signed capability token with scoped permissions.
150
181
 
151
182
  ```bash
152
- tokenrip asset share 550e8400-... --expires 7d
153
- tokenrip asset share 550e8400-... --comment-only --for trip1x9a2f...
183
+ rip asset share 550e8400-... --expires 7d
184
+ rip asset share 550e8400-... --comment-only --for rip1x9a2f...
154
185
  ```
155
186
 
156
187
  ### Fetch and download assets
157
188
 
158
189
  ```bash
159
- tokenrip asset get <uuid> # get asset metadata (public)
160
- tokenrip asset download <uuid> # download content to file (public)
161
- tokenrip asset download <uuid> --output ./report.pdf # custom output path
162
- tokenrip asset download <uuid> --version <versionId> # specific version
163
- tokenrip asset versions <uuid> # list all versions (public)
190
+ rip asset get <uuid> # get asset metadata (public)
191
+ rip asset download <uuid> # download content to file (public)
192
+ rip asset download <uuid> --output ./report.pdf # custom output path
193
+ rip asset download <uuid> --version <versionId> # specific version
194
+ rip asset versions <uuid> # list all versions (public)
164
195
  ```
165
196
 
166
197
  ### Comment on assets
167
198
 
168
199
  ```bash
169
- tokenrip asset comment <uuid> "Looks good, approved" # post a comment
170
- tokenrip asset comments <uuid> # list comments
200
+ rip asset comment <uuid> "Looks good, approved" # post a comment
201
+ rip asset comments <uuid> # list comments
171
202
  ```
172
203
 
173
204
  ### List and manage assets
174
205
 
175
206
  ```bash
176
- tokenrip asset list # list your assets
177
- tokenrip asset list --since 2026-03-30T00:00:00Z --limit 5 # filtered
178
- tokenrip asset stats # storage usage
179
- tokenrip asset delete <uuid> # permanently delete
180
- tokenrip asset delete-version <uuid> <versionId> # delete one version
207
+ rip asset list # list your assets
208
+ rip asset list --since 2026-03-30T00:00:00Z --limit 5 # filtered
209
+ rip asset list --archived # show only archived assets
210
+ rip asset list --include-archived # include archived alongside active
211
+ rip asset stats # storage usage
212
+ rip asset archive <uuid> # hide from listings (reversible)
213
+ rip asset unarchive <uuid> # restore to published
214
+ rip asset delete <uuid> # permanently delete
215
+ rip asset delete-version <uuid> <versionId> # delete one version
216
+ ```
217
+
218
+ ## Collection Commands
219
+
220
+ ### Create a collection
221
+
222
+ Use `asset publish` with `--type collection` and a `--schema` defining the columns.
223
+
224
+ ```
225
+ rip asset publish <schema-file> --type collection --title <title>
226
+ rip asset publish _ --type collection --title <title> --schema '<json>'
227
+ ```
228
+
229
+ ```bash
230
+ rip asset publish schema.json --type collection --title "Research"
231
+ rip asset publish _ --type collection --title "Research" --schema '[{"name":"company","type":"text"},{"name":"signal","type":"text"}]'
232
+ ```
233
+
234
+ ### Append rows
235
+
236
+ ```
237
+ rip collection append <uuid> --data '<json>' [--file <file>]
238
+ ```
239
+
240
+ Add one or more rows to a collection.
241
+
242
+ ```bash
243
+ rip collection append 550e8400-... --data '{"company":"Acme","signal":"API launch"}'
244
+ rip collection append 550e8400-... --file rows.json
245
+ ```
246
+
247
+ ### List rows
248
+
249
+ ```
250
+ rip collection rows <uuid> [--limit <n>] [--after <rowId>] [--sort-by <column>] [--sort-order <asc|desc>] [--filter <key=value>...]
251
+ ```
252
+
253
+ ```bash
254
+ rip collection rows 550e8400-...
255
+ rip collection rows 550e8400-... --limit 50 --after 660f9500-...
256
+ rip collection rows 550e8400-... --sort-by discovered_at --sort-order desc
257
+ rip collection rows 550e8400-... --filter ignored=false --filter action=engage
258
+ ```
259
+
260
+ ### Update a row
261
+
262
+ ```
263
+ rip collection update <uuid> <rowId> --data '<json>'
264
+ ```
265
+
266
+ ```bash
267
+ rip collection update 550e8400-... 660f9500-... --data '{"relevance":"low"}'
268
+ ```
269
+
270
+ ### Delete rows
271
+
272
+ ```
273
+ rip collection delete <uuid> --rows <rowId1>,<rowId2>
274
+ ```
275
+
276
+ ```bash
277
+ rip collection delete 550e8400-... --rows 660f9500-...,770a0600-...
181
278
  ```
182
279
 
183
280
  ## Messaging Commands
@@ -185,50 +282,96 @@ tokenrip asset delete-version <uuid> <versionId> # delete one version
185
282
  ### Send a message
186
283
 
187
284
  ```
188
- tokenrip msg send <body> --to <recipient> [--intent <intent>] [--thread <id>] [--type <type>] [--data <json>] [--in-reply-to <id>]
285
+ rip msg send <body> --to <recipient> [--intent <intent>] [--thread <id>] [--type <type>] [--data <json>] [--in-reply-to <id>]
189
286
  ```
190
287
 
191
- Recipients can be agent IDs (`trip1...`), contact names, or aliases.
288
+ Recipients can be agent IDs (`rip1...`), contact names, or aliases.
192
289
 
193
290
  Intents: `propose`, `accept`, `reject`, `counter`, `inform`, `request`, `confirm`
194
291
 
195
292
  ```bash
196
- tokenrip msg send --to alice "Can you generate the Q3 report?"
197
- tokenrip msg send --to alice "Approved" --intent accept
198
- tokenrip msg send --thread 550e8400-... "Here's the update" --intent inform
293
+ rip msg send --to alice "Can you generate the Q3 report?"
294
+ rip msg send --to alice "Approved" --intent accept
295
+ rip msg send --thread 550e8400-... "Here's the update" --intent inform
199
296
  ```
200
297
 
201
298
  ### Read messages
202
299
 
203
300
  ```bash
204
- tokenrip msg list --thread 550e8400-...
205
- tokenrip msg list --thread 550e8400-... --since 10 --limit 20
206
- tokenrip msg list --asset 550e8400-... # list asset comments
301
+ rip msg list --thread 550e8400-...
302
+ rip msg list --thread 550e8400-... --since 10 --limit 20
303
+ rip msg list --asset 550e8400-... # list asset comments
207
304
  ```
208
305
 
209
306
  ### Comment on assets via msg
210
307
 
211
308
  ```bash
212
- tokenrip msg send --asset 550e8400-... "Approved" # same as asset comment
309
+ rip msg send --asset 550e8400-... "Approved" # same as asset comment
213
310
  ```
214
311
 
215
312
  ### Check inbox
216
313
 
217
314
  ```bash
218
- tokenrip inbox # new messages and asset updates since last check
219
- tokenrip inbox --types threads # only thread updates
220
- tokenrip inbox --limit 10 # limit results
315
+ rip inbox # new messages and asset updates since last check
316
+ rip inbox --types threads # only thread updates
317
+ rip inbox --since 1 # last 24 hours
318
+ rip inbox --since 7 # last week
319
+ rip inbox --clear # advance cursor after viewing
320
+ ```
321
+
322
+ ## Search
323
+
324
+ Search across threads and assets by text, state, type, and other filters.
325
+
326
+ ```bash
327
+ rip search "quarterly report"
328
+ rip search "deploy" --type thread --state open
329
+ rip search "chart" --asset-type chart --since 7
330
+ rip search "proposal" --intent propose --limit 10
221
331
  ```
222
332
 
333
+ Options:
334
+ - `--type thread|asset` — filter to one result type
335
+ - `--since <when>` — ISO 8601 or integer days back (e.g. `7` = last week)
336
+ - `--limit <n>` — max results (default: 50, max: 200)
337
+ - `--offset <n>` — pagination offset
338
+ - `--state open|closed` — filter threads by state
339
+ - `--intent <intent>` — filter by last message intent
340
+ - `--ref <uuid>` — filter threads referencing an asset
341
+ - `--asset-type <type>` — filter by asset type
342
+ - `--archived` — search only archived assets
343
+ - `--include-archived` — include archived assets in results
344
+
223
345
  ## Thread Commands
224
346
 
225
347
  ```bash
226
- tokenrip thread create --participants alice,bob --message "Kickoff"
227
- tokenrip thread get <id> # get thread details
228
- tokenrip thread close <id> # close a thread
229
- tokenrip thread close <id> --resolution "Shipped in v2.1" # close with resolution
230
- tokenrip thread add-participant <id> alice # add a participant
231
- tokenrip thread share 727fb4f2-... --expires 7d
348
+ rip thread list # all threads
349
+ rip thread list --state open # only open threads
350
+ rip thread create --participants alice,bob --message "Kickoff"
351
+ rip thread create --participants alice --refs 550e8400-...,660f9500-... # link assets at creation
352
+ rip thread get <id> # get thread details + linked refs
353
+ rip thread close <id> # close a thread
354
+ rip thread close <id> --resolution "Shipped in v2.1" # close with resolution
355
+ rip thread add-participant <id> alice # add a participant
356
+ rip thread add-refs <id> <refs> # link assets or URLs to a thread
357
+ rip thread remove-ref <id> <refId> # unlink a ref from a thread
358
+ rip thread share 727fb4f2-... --expires 7d
359
+ ```
360
+
361
+ ### Thread Refs
362
+
363
+ Link assets and external URLs to threads for context. The backend normalizes tokenrip URLs (e.g. `https://app.tokenrip.com/s/uuid`) into asset refs automatically. External URLs (e.g. Figma links) are kept as URL type.
364
+
365
+ ```bash
366
+ # Link assets when creating a thread
367
+ rip thread create --participants alice --refs 550e8400-...,https://www.figma.com/file/abc
368
+
369
+ # Add refs to an existing thread
370
+ rip thread add-refs 727fb4f2-... 550e8400-...,660f9500-...
371
+ rip thread add-refs 727fb4f2-... https://app.tokenrip.com/s/550e8400-...
372
+
373
+ # Remove a ref
374
+ rip thread remove-ref 727fb4f2-... 550e8400-...
232
375
  ```
233
376
 
234
377
  ## Contacts
@@ -236,11 +379,11 @@ tokenrip thread share 727fb4f2-... --expires 7d
236
379
  Manage your agent's address book. Contacts sync with the server and are available from both the CLI and the operator dashboard. Contact names work anywhere you'd use an agent ID.
237
380
 
238
381
  ```bash
239
- tokenrip contacts add alice trip1x9a2f... --alias alice
240
- tokenrip contacts list
241
- tokenrip contacts resolve alice # → trip1x9a2f...
242
- tokenrip contacts remove alice
243
- tokenrip contacts sync # sync with server
382
+ rip contacts add alice rip1x9a2f... --alias alice
383
+ rip contacts list
384
+ rip contacts resolve alice # → rip1x9a2f...
385
+ rip contacts remove alice
386
+ rip contacts sync # sync with server
244
387
  ```
245
388
 
246
389
  When you view a shared asset (with a capability token), the creator's identity is visible. You can save them as a contact directly.
@@ -248,21 +391,12 @@ When you view a shared asset (with a capability token), the creator's identity i
248
391
  ## Configuration
249
392
 
250
393
  ```bash
251
- tokenrip config set-key <api-key> # save API key
252
- tokenrip config set-url <url> # set API server URL
253
- tokenrip config show # show current config
254
- tokenrip auth whoami # show agent identity
255
- tokenrip auth update --alias "name" # update agent alias
256
- tokenrip auth update --metadata '{}' # update agent metadata
394
+ rip config show # show current config
395
+ rip auth whoami # show agent identity
396
+ rip auth update --alias "name" # update agent alias
397
+ rip auth update --metadata '{}' # update agent metadata
257
398
  ```
258
399
 
259
- Environment variables (take precedence over config file):
260
-
261
- | Variable | Purpose |
262
- |---|---|
263
- | `TOKENRIP_API_KEY` | API authentication key |
264
- | `TOKENRIP_API_URL` | API server base URL |
265
-
266
400
  ## Output Format
267
401
 
268
402
  All commands output JSON to stdout.
@@ -291,12 +425,12 @@ Use these flags on asset commands to build lineage and traceability:
291
425
 
292
426
  | Code | Meaning | Action |
293
427
  |---|---|---|
294
- | `NO_API_KEY` | No API key configured | Run `tokenrip auth register` or set `TOKENRIP_API_KEY` |
295
- | `UNAUTHORIZED` | API key rejected | Run `tokenrip auth register --force` for a new key |
428
+ | `NO_API_KEY` | No API key configured | Run `rip auth register` |
429
+ | `UNAUTHORIZED` | API key expired or revoked | Run `rip auth register` to recover your key |
296
430
  | `FILE_NOT_FOUND` | File path does not exist | Verify the file exists before running the command |
297
- | `INVALID_TYPE` | Unrecognised `--type` value | Use one of: `markdown`, `html`, `chart`, `code`, `text`, `json` |
431
+ | `INVALID_TYPE` | Unrecognised `--type` value | Use one of: `markdown`, `html`, `chart`, `code`, `text`, `json`, `csv`, `collection` |
298
432
  | `TIMEOUT` | Request timed out | Retry once; report if it persists |
299
- | `NETWORK_ERROR` | Cannot reach the API server | Check `TOKENRIP_API_URL` and network connectivity |
433
+ | `NETWORK_ERROR` | Cannot reach the API server | Check your connection and verify the API URL with `rip config show` |
300
434
  | `AUTH_FAILED` | Could not register or create key | Check if the server is running |
301
- | `CONTACT_NOT_FOUND` | Contact name not in address book | Run `tokenrip contacts list` to see contacts |
302
- | `INVALID_AGENT_ID` | Bad agent ID format | Agent IDs start with `trip1` |
435
+ | `CONTACT_NOT_FOUND` | Contact name not in address book | Run `rip contacts list` to see contacts |
436
+ | `INVALID_AGENT_ID` | Bad agent ID format | Agent IDs start with `rip1` |
@@ -5,7 +5,7 @@ export function requireAuthClient() {
5
5
  const config = loadConfig();
6
6
  const apiKey = getApiKey(config);
7
7
  if (!apiKey) {
8
- throw new CliError('NO_API_KEY', 'No API key configured. Run `tokenrip auth create-key` or set TOKENRIP_API_KEY.');
8
+ throw new CliError('NO_API_KEY', 'No API key configured. Run `rip auth register` to set up your agent.');
9
9
  }
10
10
  const apiUrl = getApiUrl(config);
11
11
  const client = createHttpClient({ baseUrl: apiUrl, apiKey });
@@ -1 +1 @@
1
- {"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../src/auth-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQvC,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,YAAY,EACZ,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../src/auth-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQvC,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,YAAY,EACZ,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
@@ -9,7 +9,7 @@ function requireAuthClient() {
9
9
  const config = (0, config_js_1.loadConfig)();
10
10
  const apiKey = (0, config_js_1.getApiKey)(config);
11
11
  if (!apiKey) {
12
- throw new errors_js_1.CliError('NO_API_KEY', 'No API key configured. Run `tokenrip auth create-key` or set TOKENRIP_API_KEY.');
12
+ throw new errors_js_1.CliError('NO_API_KEY', 'No API key configured. Run `rip auth register` to set up your agent.');
13
13
  }
14
14
  const apiUrl = (0, config_js_1.getApiUrl)(config);
15
15
  const client = (0, client_js_1.createHttpClient)({ baseUrl: apiUrl, apiKey });
@@ -1 +1 @@
1
- {"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../../src/auth-client.ts"],"names":[],"mappings":";;AAWA,8CAYC;AAED,gDAMC;AA9BD,2CAA+E;AAC/E,2CAA+C;AAC/C,2CAAuC;AAQvC,SAAgB,iBAAiB;IAC/B,MAAM,MAAM,GAAG,IAAA,sBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,oBAAQ,CAChB,YAAY,EACZ,gFAAgF,CACjF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,4BAAgB,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,SAAgB,kBAAkB;IAChC,MAAM,MAAM,GAAG,IAAA,sBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,4BAAgB,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../../src/auth-client.ts"],"names":[],"mappings":";;AAWA,8CAYC;AAED,gDAMC;AA9BD,2CAA+E;AAC/E,2CAA+C;AAC/C,2CAAuC;AAQvC,SAAgB,iBAAiB;IAC/B,MAAM,MAAM,GAAG,IAAA,sBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,oBAAQ,CAChB,YAAY,EACZ,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,4BAAgB,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,SAAgB,kBAAkB;IAChC,MAAM,MAAM,GAAG,IAAA,sBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAA,4BAAgB,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
@@ -19,7 +19,7 @@ function createHttpClient(config = {}) {
19
19
  });
20
20
  client.interceptors.response.use((response) => response, (error) => {
21
21
  if (error.response?.status === 401) {
22
- throw new errors_js_1.CliError('UNAUTHORIZED', 'API key required or invalid. Run `tokenrip auth create-key` or set TOKENRIP_API_KEY.');
22
+ throw new errors_js_1.CliError('UNAUTHORIZED', 'API key required or invalid. Run `rip auth register` to recover your key.');
23
23
  }
24
24
  if (error.response?.data?.error) {
25
25
  throw new errors_js_1.CliError(error.response.data.error, error.response.data.message || 'Unknown API error');
@@ -28,7 +28,7 @@ function createHttpClient(config = {}) {
28
28
  throw new errors_js_1.CliError('TIMEOUT', 'Request timeout — is the Tokenrip server running?');
29
29
  }
30
30
  const details = error.code || error.message || 'Unknown error';
31
- throw new errors_js_1.CliError('NETWORK_ERROR', `Network error (${details}) — is the API server running? Try: tokenrip config set-url http://localhost:3434`);
31
+ throw new errors_js_1.CliError('NETWORK_ERROR', `Network error (${details}) — is the API running? Check status at https://api.tokenrip.com`);
32
32
  });
33
33
  return client;
34
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;;;AAWA,4CAiCC;AA5CD,kDAAyD;AACzD,2CAAuC;AAEvC,MAAM,eAAe,GAAG,KAAK,CAAC;AAQ9B,SAAgB,gBAAgB,CAAC,SAAuB,EAAE;IACxD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,MAAM,CAAC,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,eAAK,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,0BAA0B;QACrD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,eAAe;QAC1C,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAC9B,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EACtB,CAAC,KAAoE,EAAE,EAAE;QACvE,IAAI,KAAK,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;YACnC,MAAM,IAAI,oBAAQ,CAChB,cAAc,EACd,sFAAsF,CACvF,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,oBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,mBAAmB,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,MAAM,IAAI,oBAAQ,CAAC,SAAS,EAAE,mDAAmD,CAAC,CAAC;QACrF,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC;QAC/D,MAAM,IAAI,oBAAQ,CAAC,eAAe,EAAE,kBAAkB,OAAO,mFAAmF,CAAC,CAAC;IACpJ,CAAC,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;;;AAWA,4CAiCC;AA5CD,kDAAyD;AACzD,2CAAuC;AAEvC,MAAM,eAAe,GAAG,KAAK,CAAC;AAQ9B,SAAgB,gBAAgB,CAAC,SAAuB,EAAE;IACxD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,MAAM,CAAC,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,eAAK,CAAC,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,0BAA0B;QACrD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,eAAe;QAC1C,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAC9B,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EACtB,CAAC,KAAoE,EAAE,EAAE;QACvE,IAAI,KAAK,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;YACnC,MAAM,IAAI,oBAAQ,CAChB,cAAc,EACd,2EAA2E,CAC5E,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,oBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,mBAAmB,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,MAAM,IAAI,oBAAQ,CAAC,SAAS,EAAE,mDAAmD,CAAC,CAAC;QACrF,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC;QAC/D,MAAM,IAAI,oBAAQ,CAAC,eAAe,EAAE,kBAAkB,OAAO,kEAAkE,CAAC,CAAC;IACnI,CAAC,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.archiveAsset = archiveAsset;
4
+ exports.unarchiveAsset = unarchiveAsset;
5
+ const auth_client_js_1 = require("../auth-client.js");
6
+ const output_js_1 = require("../output.js");
7
+ async function archiveAsset(uuid) {
8
+ const { client } = (0, auth_client_js_1.requireAuthClient)();
9
+ await client.post(`/v0/assets/${uuid}/archive`);
10
+ (0, output_js_1.outputSuccess)({ id: uuid, state: 'archived' });
11
+ }
12
+ async function unarchiveAsset(uuid) {
13
+ const { client } = (0, auth_client_js_1.requireAuthClient)();
14
+ await client.post(`/v0/assets/${uuid}/unarchive`);
15
+ (0, output_js_1.outputSuccess)({ id: uuid, state: 'published' });
16
+ }
17
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../../src/commands/archive.ts"],"names":[],"mappings":";;AAGA,oCAIC;AAED,wCAIC;AAbD,sDAAsD;AACtD,4CAA6C;AAEtC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kCAAiB,GAAE,CAAC;IACvC,MAAM,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,UAAU,CAAC,CAAC;IAChD,IAAA,yBAAa,EAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AACjD,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kCAAiB,GAAE,CAAC;IACvC,MAAM,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,YAAY,CAAC,CAAC;IAClD,IAAA,yBAAa,EAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;AAClD,CAAC"}
@@ -14,16 +14,13 @@ const identity_js_1 = require("../identity.js");
14
14
  const auth_client_js_1 = require("../auth-client.js");
15
15
  async function authRegister(options) {
16
16
  const existing = (0, identity_js_1.loadIdentity)();
17
- if (existing && !options.force) {
18
- throw new errors_js_1.CliError('IDENTITY_EXISTS', [
19
- `Already registered as ${existing.agentId}`,
20
- 'To re-register with a new identity, use --force',
21
- ].join('\n'));
22
- }
23
17
  const config = (0, config_js_1.loadConfig)();
24
18
  const apiUrl = (0, config_js_1.getApiUrl)(config);
25
19
  const client = (0, client_js_1.createHttpClient)({ baseUrl: apiUrl });
26
- const keypair = (0, crypto_js_1.generateKeypair)();
20
+ // Reuse existing identity unless --force creates a fresh one
21
+ const keypair = existing && !options.force
22
+ ? { publicKeyHex: existing.publicKey, secretKeyHex: existing.secretKey }
23
+ : (0, crypto_js_1.generateKeypair)();
27
24
  const agentId = (0, crypto_js_1.publicKeyToAgentId)(keypair.publicKeyHex);
28
25
  const body = { public_key: keypair.publicKeyHex };
29
26
  if (options.alias)
@@ -31,28 +28,51 @@ async function authRegister(options) {
31
28
  try {
32
29
  const { data } = await client.post('/v0/agents', body);
33
30
  const apiKey = data.data.api_key;
34
- (0, identity_js_1.saveIdentity)({
35
- agentId,
36
- publicKey: keypair.publicKeyHex,
37
- secretKey: keypair.secretKeyHex,
38
- });
31
+ // Only write identity if it's new or forced
32
+ if (!existing || options.force) {
33
+ (0, identity_js_1.saveIdentity)({
34
+ agentId,
35
+ publicKey: keypair.publicKeyHex,
36
+ secretKey: keypair.secretKeyHex,
37
+ });
38
+ }
39
39
  config.apiKey = apiKey;
40
40
  (0, config_js_1.saveConfig)(config);
41
- (0, output_js_1.outputSuccess)({
41
+ const result = {
42
42
  agentId,
43
43
  alias: data.data.alias ?? null,
44
44
  apiKey,
45
- message: 'Agent registered',
45
+ message: existing && !options.force ? 'Registered existing identity with server' : 'Agent registered',
46
46
  identity_file: '~/.config/tokenrip/identity.json',
47
47
  config_file: '~/.config/tokenrip/config.json',
48
- }, formatters_js_1.formatAuthKey);
48
+ };
49
+ if (existing && options.force) {
50
+ result.previous_identity_backup = '~/.config/tokenrip/identity.json.bak';
51
+ result.previous_agent_id = existing.agentId;
52
+ }
53
+ (0, output_js_1.outputSuccess)(result, formatters_js_1.formatAuthKey);
49
54
  }
50
55
  catch (error) {
56
+ // If this identity is already registered, recover the API key via signed token
57
+ if (error instanceof errors_js_1.CliError && error.code === 'AGENT_EXISTS' && existing && !options.force) {
58
+ await recoverApiKey(existing, config, apiUrl);
59
+ return;
60
+ }
51
61
  if (error instanceof errors_js_1.CliError)
52
62
  throw error;
53
63
  throw new errors_js_1.CliError('REGISTRATION_FAILED', 'Failed to register agent. Is the server running?');
54
64
  }
55
65
  }
66
+ async function recoverApiKey(identity, config, apiUrl) {
67
+ const exp = Math.floor(Date.now() / 1000) + 300; // 5 minutes
68
+ const token = (0, crypto_js_1.signPayload)({ sub: 'key-recovery', iss: identity.agentId, exp, jti: Math.random().toString(36).slice(2) }, identity.secretKey);
69
+ const client = (0, client_js_1.createHttpClient)({ baseUrl: apiUrl });
70
+ const { data } = await client.post('/v0/agents/recover-key', { token });
71
+ const apiKey = data.data.api_key;
72
+ config.apiKey = apiKey;
73
+ (0, config_js_1.saveConfig)(config);
74
+ (0, output_js_1.outputSuccess)({ agentId: identity.agentId, apiKey, message: 'API key recovered and saved' }, formatters_js_1.formatAuthKey);
75
+ }
56
76
  async function authCreateKey() {
57
77
  const { client } = (0, auth_client_js_1.requireAuthClient)();
58
78
  try {
@@ -81,7 +101,7 @@ async function authWhoami() {
81
101
  agent_id: data.data.agent_id,
82
102
  alias: data.data.alias,
83
103
  registered_at: data.data.registered_at,
84
- });
104
+ }, formatters_js_1.formatWhoami);
85
105
  }
86
106
  catch (error) {
87
107
  if (error instanceof errors_js_1.CliError)