@heyamiko/amiko-cli 0.14.0-beta.9 → 0.14.2
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 +162 -18
- package/dist/index.js +2093 -225
- package/package.json +1 -1
- package/skills/SKILL.md +126 -16
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.14.0-beta.
|
|
1
|
+
# @heyamiko/amiko-cli (v0.14.0-beta.24)
|
|
2
2
|
|
|
3
3
|
Manage wallets, credits, swaps, MPP marketplace services, and your Amiko twin (identity, documents, voice, avatar, friends, feed) from the terminal. Works for both human users and AI agents running on OpenClaw.
|
|
4
4
|
|
|
@@ -120,16 +120,21 @@ Image generation typically takes 20–60s. In non-TTY environments (agents, pipe
|
|
|
120
120
|
|
|
121
121
|
## Create Studio
|
|
122
122
|
|
|
123
|
-
The CLI half of the platform's Create Studio. `amiko create` generates media through Amiko's own authenticated endpoints (not the raw MPP pay-per-call surface `amiko markets` uses). Generation runs **asynchronously** and is **charged on success** from your twin's wallet — a failed or timed-out generation is never billed, and there's no up-front payment.
|
|
123
|
+
The CLI half of the platform's Create Studio. `amiko create` generates media through Amiko's own authenticated endpoints (not the raw MPP pay-per-call surface `amiko markets` uses). Generation runs **asynchronously** and is **charged on success** from your twin's wallet — a failed or timed-out generation is never billed, and there's no up-front payment. On unified-billing accounts, `--pay credits` pays from your Amiko account credits instead: the quote is reserved at submit, captured only on success, and released on failure (legacy accounts get a clear server error — omit the flag to keep the twin-wallet default).
|
|
124
124
|
|
|
125
125
|
```bash
|
|
126
126
|
amiko create image "a sunset over mountains" # default nano-banana-2, 1:1
|
|
127
127
|
amiko create image "logo" --aspect 16:9 --model gpt-image-2 # widescreen, OpenAI
|
|
128
|
+
amiko create image "a sunset" --pay credits --yes # pay with Amiko account credits (unified accounts)
|
|
128
129
|
amiko create video "a whale in space" --resolution 768P --seconds 6
|
|
129
130
|
amiko create video "animate cover" --first-frame https://.../cover.png --yes
|
|
130
131
|
amiko create video "dance clip" --model dreamina-seedance-2-0-fast-260128 \
|
|
131
132
|
--reference-image https://.../ref1.png --reference-audio https://.../beat.mp3 --generate-audio --yes
|
|
132
133
|
amiko create tts "Hello world" --voice 21m00Tcm4TlvDq8ikWAM
|
|
134
|
+
amiko create tts "Hello world" --provider minimax --model speech-2.8-turbo
|
|
135
|
+
amiko create tts "Hello" --provider minimax --voice English_expressive_narrator --yes
|
|
136
|
+
amiko voice clone ./me.mp3 --provider minimax
|
|
137
|
+
amiko voice clone ./me.mp3 --provider elevenlabs
|
|
133
138
|
amiko create music "lo-fi chill beat" --duration 30000
|
|
134
139
|
amiko create music --lyrics "..." --instrumental # instrumental
|
|
135
140
|
amiko create sfx "thunder and heavy rain" --duration 5
|
|
@@ -142,7 +147,8 @@ Modes: `image`, `video`, `tts`, `music`, `sfx` (mirrors the platform Create Stud
|
|
|
142
147
|
|
|
143
148
|
| Flag | Description |
|
|
144
149
|
|------|-------------|
|
|
145
|
-
| `--
|
|
150
|
+
| `--pay <method>` | Payment method: `wallet` (twin wallet, default) or `credits` (Amiko account credits — unified accounts only) |
|
|
151
|
+
| `--token <symbol>` | Wallet charge token: `AMIKO`, `USDC`, `USDT`, `SOL` (default: auto, AMIKO-first); ignored with `--pay credits` |
|
|
146
152
|
| `--yes` | Skip the pre-spend confirmation (required in non-interactive shells) |
|
|
147
153
|
| `--raw` | Output the raw job result as JSON |
|
|
148
154
|
|
|
@@ -157,7 +163,7 @@ Modes: `image`, `video`, `tts`, `music`, `sfx` (mirrors the platform Create Stud
|
|
|
157
163
|
|
|
158
164
|
| Flag | Default | Description |
|
|
159
165
|
|------|---------|-------------|
|
|
160
|
-
| `--model <model>` |
|
|
166
|
+
| `--model <model>` | smart | Video model (`MiniMax-Hailuo-*`, `dreamina-seedance-2-0-fast-260128`, …). Default: `MiniMax-Hailuo-02` for prompt-only (T2V), `MiniMax-Hailuo-2.3-Fast` when `--first-frame` is given (I2V) |
|
|
161
167
|
| `--resolution <res>` | `768P` | `512P`, `720P`, `768P`, `1080P` |
|
|
162
168
|
| `--seconds <n>` | `6` | Clip length: `6` or `10` |
|
|
163
169
|
| `--aspect <ratio>` | — | Aspect ratio (provider-dependent) |
|
|
@@ -176,8 +182,20 @@ Reference flags accept URLs or data URIs (not local paths). Typical I2V flow: `c
|
|
|
176
182
|
|
|
177
183
|
| Flag | Default | Description |
|
|
178
184
|
|------|---------|-------------|
|
|
179
|
-
| `--
|
|
180
|
-
| `--
|
|
185
|
+
| `--provider <name>` | inferred / `elevenlabs` | `minimax` or `elevenlabs` — sets model/voice defaults when those flags are omitted |
|
|
186
|
+
| `--model <model>` | EL: `eleven_multilingual_v2`; MiniMax: `speech-2.8-turbo` | MiniMax: `speech-2.8-turbo\|hd`, `speech-2.6-turbo\|hd`. ElevenLabs: `eleven_*` |
|
|
187
|
+
| `--voice <voiceId>` | EL default hash; MiniMax: `English_expressive_narrator` | MiniMax system or cloned `voice_id`; ElevenLabs voice id |
|
|
188
|
+
|
|
189
|
+
Examples: `amiko create tts "Hi" --provider minimax --yes` · `amiko create tts "Hi" --provider minimax --model speech-2.8-turbo --voice <clonedId> --yes`
|
|
190
|
+
|
|
191
|
+
**`amiko voice clone <source>`** — audio path, https URL, or `-` (stdin)
|
|
192
|
+
|
|
193
|
+
| Flag | Default | Description |
|
|
194
|
+
|------|---------|-------------|
|
|
195
|
+
| `--provider <name>` | `minimax` | `minimax` (mpp rapid clone + ownership registry) or `elevenlabs` (legacy IVC) |
|
|
196
|
+
| `--voice-id <id>` | auto | **MiniMax only** — optional custom voice_id (ignored for elevenlabs) |
|
|
197
|
+
| `--model <model>` | `speech-2.8-turbo` | **MiniMax only** — preview speech model (ignored for elevenlabs) |
|
|
198
|
+
| `--preview-text <text>` | provider default | **MiniMax only** — preview line; empty string skips preview (ignored for elevenlabs) |
|
|
181
199
|
|
|
182
200
|
**`amiko create music [prompt]`** — pass a prompt and/or `--lyrics`
|
|
183
201
|
|
|
@@ -231,20 +249,37 @@ Your conversations — **DMs and group chats** — acting **as you (the owner)**
|
|
|
231
249
|
```bash
|
|
232
250
|
amiko chat list # all conversations (DM + group): id, peer/title, last msg, unread
|
|
233
251
|
amiko chat list --limit 50 --archived # include archived
|
|
252
|
+
amiko chat list --mentions # only chats with an unread @mention of you or a reply to you
|
|
234
253
|
amiko chat read @sophie # recent messages with a user (by @handle)
|
|
235
254
|
amiko chat read <conversationId> --limit 40 # by conversation id (from `chat list`) — works for groups too
|
|
236
255
|
amiko chat send @sophie "on my way!" --yes # send as the owner
|
|
237
256
|
amiko chat send <userId> "hey!" --yes # user id works too — the DM is opened automatically
|
|
238
257
|
amiko chat send Mars "hey!" --yes # or a name (friends first, then people search)
|
|
239
258
|
amiko chat send <groupConversationId> "hi all" --yes # send to a group chat
|
|
259
|
+
amiko chat send "Trip planning" "standup in 5 @all" --yes # @all — notifies every group member
|
|
260
|
+
amiko chat send "Trip planning" "standup in 5" --all --yes # same, via the explicit flag
|
|
240
261
|
amiko chat send @sophie "look 👀" --image ./cat.png --yes # attach a local image
|
|
241
262
|
amiko chat send @sophie "made this 🎵" --audio <generated-url> --yes # attach audio (e.g. from `amiko create`)
|
|
263
|
+
amiko chat send @sophie --gif "happy dance" --yes # send the top Klipy GIF for a search (message optional)
|
|
264
|
+
amiko chat gifs "happy dance" # browse GIFs first, then send a specific one:
|
|
265
|
+
amiko chat send @sophie "we did it!" --gif <gifUrl> --yes # attach an exact GIF by its Klipy URL
|
|
266
|
+
amiko chat receipts <messageId> # who read your message + when (id from `chat read`)
|
|
267
|
+
amiko chat pin <messageId> --yes # pin for everyone (id from `chat read`; groups: admins only)
|
|
268
|
+
amiko chat unpin <messageId> --yes # remove a pin for everyone
|
|
269
|
+
amiko chat pinned "Trip planning" # list a conversation's pinned messages, oldest first
|
|
270
|
+
amiko chat mark-read --all --yes # mark EVERY conversation as read (clears all unread badges)
|
|
242
271
|
```
|
|
243
272
|
|
|
244
273
|
- **`<target>`** for `read`/`send` is a **conversation id** (from `chat list` — use this for groups), a **user id**, an **`@handle`**, or a **name**. Names resolve against your **friends first**, then people search; if ambiguous, the CLI lists candidates instead of guessing. A cuid that isn't one of your conversations is retried as a user id, so pasting a user id "just works".
|
|
245
274
|
- `send` to a person you have no DM with yet **creates the DM automatically** (find-or-create, after the confirmation). `read` never creates one — it errors if no DM exists yet.
|
|
246
|
-
- **
|
|
275
|
+
- **Unread mentions & replies**: rows whose unread messages **concern you directly** get an `@you` marker next to the unread count, and `chat list --mentions` filters to just those. The flag is server-computed over your unread messages and covers three cases: a direct **@mention** of you, a permitted **@all** in a group, and a **reply to one of your messages**. Composes with `--limit`/`--archived`/`--raw` (the raw payload is filtered too). Requires the amiko-web `has_unread_mention` deploy. On an older server the field is absent and reads as **false for every conversation**: plain `chat list` still shows unread counts, just without `@you`, and `chat list --mentions` returns no matches.
|
|
276
|
+
- **Inline media**: `--image <pathOrUrl>` (jpg/png/webp/gif, local ≤10MB or an Amiko URL), `--audio <pathOrUrl>` (local ≤25MB or an Amiko URL → sent as a voice note), and `--gif <queryOrUrl>` (search words send the **top Klipy result**; or pass an exact Klipy URL from `amiko chat gifs` — non-Klipy URLs are refused; to send another image, use `--image` with a local file or an Amiko URL). One media block per message (image *or* audio *or* gif). **General video files aren't supported in chat yet.** A local file is uploaded; an Amiko URL (e.g. a generated asset from `amiko create`) attaches directly with no re-upload. (A local *audio* file is sent as a voice note, with your text as a separate message.)
|
|
277
|
+
- **GIFs**: `amiko chat gifs [query]` lists Klipy GIFs (trending with no query; `--page`/`--limit`/`--raw`) — read-only and ungated, powered by KLIPY. The message text is optional with `--gif` (a captionless GIF sends as just the GIF); a caption renders under the GIF on every client.
|
|
278
|
+
- **@all in groups**: `--all` prepends an @all mention, and a standalone `@all` word typed in the message converts too — either way **every member** is notified. Group **admins/owners** can always use it; everyone else only after an admin runs `amiko chat group mention-all <group> on`. Without permission, `--all` fails with the fix named, while a typed `@all` is delivered as plain text (with a note). Incoming mention markup renders as plain `@name`/`@all` in `chat read` and `chat list`.
|
|
247
279
|
- `chat send` messages a real person, so it's **gated on `--yes`** in non-interactive shells. Delivery is real-time. `--raw` prints raw JSON on any subcommand.
|
|
280
|
+
- **`chat receipts <messageId>`** shows read receipts for **your own** messages (server-enforced; 403 otherwise): sent time, who read it with each person's first-read time, and who it was delivered to but hasn't read. Reads from before receipts tracking show as "exact time unknown". Read-only, ungated.
|
|
281
|
+
- **Pinned messages**: `chat pin <messageId>` pins for the **whole conversation** (the chat sees an "X pinned a message" announcement) and `chat unpin <messageId>` removes it for everyone — both gated on `--yes`. In **groups** only admins can pin/unpin (403 otherwise); in DMs either side can. Max 20 pins per conversation. `chat pinned <target>` (same targets as `read`; never creates a DM) lists them oldest-first with each message's id and pinner — read-only, ungated.
|
|
282
|
+
- **Mark all read**: `chat mark-read --all` marks every conversation as read in one call — unread badges clear on all your devices, your chat/mention notifications clear, and senders see read ticks on their messages. Irreversible (there is no "mark unread"), so it's gated on `--yes` in non-interactive shells. `--all` is required (mirrors `notifications read --all`); `--raw` prints the server payload.
|
|
248
283
|
|
|
249
284
|
### Group chats
|
|
250
285
|
|
|
@@ -258,13 +293,32 @@ amiko chat group rename <groupIdOrTitle> "New name" --yes
|
|
|
258
293
|
amiko chat group add <groupIdOrTitle> --member @sophie --yes
|
|
259
294
|
amiko chat group remove <groupIdOrTitle> --member Mars --yes
|
|
260
295
|
amiko chat group promote <groupIdOrTitle> --member Sophie --yes # make a member an admin
|
|
296
|
+
amiko chat group mention-all <groupIdOrTitle> on --yes # let everyone use @all (off = admins only, the default)
|
|
261
297
|
amiko chat group leave <groupIdOrTitle> --yes # alias: delete — see caveat below
|
|
262
298
|
```
|
|
263
299
|
|
|
264
300
|
- **`--member`** is repeatable and accepts a plain name, `@handle`, or user id. Plain names resolve against your **friends** first (then people search); an ambiguous name errors listing candidates instead of guessing, and one unresolvable member aborts the whole command **before anything is changed**.
|
|
265
301
|
- **Groups address by id or title** — titles match case-insensitively (exact first, then substring) across your most recent 100 conversations; ambiguous titles error with candidates. `chat send "<group title>" "hi" --yes` also works when exactly one group title matches.
|
|
266
|
-
- **`leave` (alias `delete`) only hides the group for you.** Other members keep the conversation and its history — the platform has no delete-for-everyone. Rename, add, remove-others, and
|
|
267
|
-
-
|
|
302
|
+
- **`leave` (alias `delete`) only hides the group for you.** Other members keep the conversation and its history — the platform has no delete-for-everyone. Rename, add, remove-others, promote, and the @all toggle are **admin-only** (the creator is admin); the server enforces this.
|
|
303
|
+
- **`mention-all <group> <on|off>`** controls who may @all in the group: `on` lets every member, `off` (the default) restricts it to admins. The current setting shows in `amiko chat group info` as `@all mentions: everyone | admins only`.
|
|
304
|
+
- `create`/`add` are outward social actions and `rename`/`remove`/`leave`/`promote`/`mention-all` are destructive, so all are **gated on `--yes`** in non-interactive shells. `--raw` prints JSON everywhere.
|
|
305
|
+
|
|
306
|
+
### Chat lists
|
|
307
|
+
|
|
308
|
+
Your **chat lists** — private folders of conversations (the same lists as the app sidebar). Only you ever see them; putting a chat in a list notifies nobody and changes nothing about the conversation itself.
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
amiko chat lists # every list with its conversations (names resolved)
|
|
312
|
+
amiko chat lists create "Work" --conversation "Trip planning" --conversation @sophie --yes
|
|
313
|
+
amiko chat lists rename "Work" "Focus" --yes
|
|
314
|
+
amiko chat lists add "Focus" --conversation <conversationId> --yes
|
|
315
|
+
amiko chat lists remove "Focus" --conversation @sophie --yes
|
|
316
|
+
amiko chat lists delete "Focus" --yes # the conversations themselves are untouched
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
- **`<list>`** is a list id or name (case-insensitive; exact beats substring; ambiguity errors listing candidates). **`--conversation`** is repeatable and takes the same targets as `chat send` — conversation id, user id, `@handle`, group title, or name — except a person must already have a DM with you: organizing lists **never creates conversations**.
|
|
320
|
+
- The server stores a list as a plain conversation-id array and `PATCH` replaces it wholesale, so `add`/`remove` read-modify-write the full array — existing entries always survive an `add`, and one unresolvable `--conversation` aborts the whole command before anything is written.
|
|
321
|
+
- Lists are private, but mutations still take the `--yes` gate in non-interactive shells (they reshape your chat sidebar on every device). The bare listing is an ungated read; `--raw` prints JSON everywhere.
|
|
268
322
|
|
|
269
323
|
## Twin Cards
|
|
270
324
|
|
|
@@ -343,8 +397,11 @@ amiko markets service call POST /v1/sfx '{"text":"Thunder with heavy rain","dura
|
|
|
343
397
|
# Music generation ($0.10) — returns { url, format, service }
|
|
344
398
|
amiko markets service call POST /v1/music '{"prompt":"Lo-fi chill beat","music_length_ms":30000}'
|
|
345
399
|
|
|
346
|
-
# Text-to-speech
|
|
400
|
+
# Text-to-speech — ElevenLabs voice id (default)
|
|
347
401
|
amiko markets service tts 21m00Tcm4TlvDq8ikWAM "Hello world"
|
|
402
|
+
# MiniMax system or cloned voice_id
|
|
403
|
+
amiko markets service tts English_expressive_narrator "Hello" --provider minimax --yes
|
|
404
|
+
amiko markets service tts <clonedId> "Hello" --provider minimax --model speech-2.8-turbo --yes
|
|
348
405
|
|
|
349
406
|
# Composition plan ($0.02)
|
|
350
407
|
amiko markets service call POST /v1/music/plan '{"prompt":"Epic orchestral piece"}'
|
|
@@ -476,8 +533,8 @@ src/
|
|
|
476
533
|
│ ├── voice.ts # voice design, voice create, voice clone, voice reset
|
|
477
534
|
│ ├── avatar.ts # avatar update
|
|
478
535
|
│ ├── friends.ts # friends list, requests, add, accept, remove, matches, reports
|
|
479
|
-
│ ├── users.ts # users search, profile
|
|
480
|
-
│ └── feed.ts # feed, post create, post comment
|
|
536
|
+
│ ├── users.ts # users search, profile, follow/unfollow/followers/following
|
|
537
|
+
│ └── feed.ts # feed, post create/drafts/publish, post comment
|
|
481
538
|
└── lib/
|
|
482
539
|
├── config.ts # defaults + resolved auth wrapper
|
|
483
540
|
├── agent-config.ts # .amiko.json reader + auth resolution
|
|
@@ -519,32 +576,49 @@ amiko voice reset --yes
|
|
|
519
576
|
amiko avatar update --file ./portrait.png --yes
|
|
520
577
|
|
|
521
578
|
# Friends
|
|
522
|
-
amiko friends list # list
|
|
579
|
+
amiko friends list # ENTIRE friend list + exact total, in one call
|
|
523
580
|
amiko friends requests # pending requests (incoming + outgoing)
|
|
524
581
|
amiko friends add --id <userId>
|
|
525
582
|
amiko friends accept <friendshipId>
|
|
526
583
|
amiko friends remove <friendshipId> --yes
|
|
584
|
+
amiko friends nickname # every private nickname you've set
|
|
585
|
+
amiko friends nickname set Sophie "Soph" --yes # set/change (name, @handle, user id, or friendship id)
|
|
586
|
+
amiko friends nickname remove Sophie --yes # clear it
|
|
527
587
|
amiko friends matches # personality match candidates
|
|
528
588
|
amiko friends reports list # friend matching reports
|
|
529
589
|
amiko friends reports view <reportId>
|
|
530
590
|
|
|
531
591
|
# Users
|
|
532
|
-
amiko users search <query> # find users by name/handle
|
|
592
|
+
amiko users search <query> # find users by name/handle (one page, --limit default 10)
|
|
593
|
+
amiko users search <query> --all # every match, for "how many people match X"
|
|
533
594
|
amiko users profile <handle> # public profile
|
|
595
|
+
amiko users follow <handleOrId> # follow (one-way, no approval); unfollow to undo
|
|
596
|
+
amiko users follow-status <handleOrId> # do I follow them / do they follow me
|
|
597
|
+
amiko users followers <handleOrId> # who follows them (--limit 1-50, --cursor)
|
|
598
|
+
amiko users following <handleOrId> # who they follow
|
|
534
599
|
|
|
535
|
-
# Feed & posts
|
|
600
|
+
# Feed & posts (a.k.a. notes)
|
|
536
601
|
amiko feed # friends feed (default)
|
|
537
|
-
amiko feed --type
|
|
538
|
-
amiko
|
|
602
|
+
amiko feed --type all --limit 20 # the "All" tab (a.k.a. for_you)
|
|
603
|
+
amiko feed --type following # posts from accounts you follow
|
|
604
|
+
amiko feed --type media --kind image # site-wide public media (hits /api/media/feed)
|
|
605
|
+
amiko post create --content "hello from the CLI" # public post — prints the canonical share URL (post_url in --json); share that verbatim, never hand-build one from the id
|
|
606
|
+
amiko post create --title "Kyoto, 6am" --media ./shot.webp # image-only note — --content is optional when media/docs are attached
|
|
607
|
+
amiko post create --title "Q3 report" --doc ./q3.pdf # document note — rendered as a file card
|
|
539
608
|
amiko post create --content "private note" --visibility private
|
|
540
609
|
amiko post create --content "look" --media https://...jpg
|
|
610
|
+
amiko post create --title "the track" --media drive:<docId> # media already in the Drive (id from `amiko drive list`) — works for audio/video too
|
|
611
|
+
amiko post create --content "wip idea" --draft # save a draft — no share URL until published
|
|
612
|
+
amiko post drafts # list draft posts (--limit/--offset/--json)
|
|
613
|
+
amiko post likers <postId> # who liked one of YOUR posts (403 on someone else's)
|
|
614
|
+
amiko post publish <postId> # publish a draft (fresh timestamp, prints the share URL)
|
|
541
615
|
amiko post comment --id <postId> --comment "great post"
|
|
542
616
|
amiko post comment --id <postId> --comment "nice!" --media https://...jpg
|
|
543
617
|
```
|
|
544
618
|
|
|
545
619
|
Every new command accepts `--json` for structured output and `--twin <id>` where a target twin is needed.
|
|
546
620
|
|
|
547
|
-
**Destructive or identity-changing commands** (`docs delete`, `friends remove`, `voice reset`, `avatar update`, `twin update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
|
|
621
|
+
**Destructive or identity-changing commands** (`docs delete`, `friends remove`, `friends nickname set/remove`, `voice reset`, `avatar update`, `twin update --public`) prompt for confirmation in a TTY and require `--yes` in non-interactive shells.
|
|
548
622
|
|
|
549
623
|
## API Endpoints
|
|
550
624
|
|
|
@@ -562,6 +636,76 @@ npm publish
|
|
|
562
636
|
|
|
563
637
|
## Changelog
|
|
564
638
|
|
|
639
|
+
### 0.14.2
|
|
640
|
+
|
|
641
|
+
- **`--media drive:<docId>`** (new source for `post create` and `post comment`): attach a media file already in the twin's Drive — an upload, or a Create Studio generation, which `saveCreationToDrive` mirrors into the "Create Studio Files" folder. The id comes from `amiko drive list --json`. This closes the gap where the only way to post an *audio or video* file the twin already owned was to have kept the original `amiko create` URL: `--media` takes local paths for images only, and a Drive signed URL expires.
|
|
642
|
+
- **No amiko-web change was needed, and this is why.** A Drive doc's `file_url` is stored in *public* form (`…/storage/v1/object/public/docs/<path>`) by all three write paths (`uploadDocumentToSupabase`, `createDocSignedUpload`, `saveCreationToDrive`) even though the `docs` bucket is private. `/api/posts` accepts it (`isAllowedPostMediaUrl` checks host + `/storage/v1/object/` only), then `ensurePostMediaUrl` sees a non-`post-media` bucket and does a **service-role server-side copy into `post-media`**, returning a durable URL — so the post never links the private object and never expires. If that copy fails, the fallback reachability probe on a private-bucket URL 4xxs and the route rejects the post rather than storing a dead link. This mirrors what amiko-web's own `DriveMediaPicker` already does (it forwards the same `file_url` under the same MIME allowlist), so the CLI is matching shipped behaviour, not inventing a path.
|
|
643
|
+
- **The drive ref honours the command's `--twin`.** Docs are scoped by `twin_id`; `resolveMediaUrls` now takes the caller's `--twin` instead of re-resolving the default twin, so `post comment --twin <other> --media drive:<id>` no longer 404s on an id `drive list --twin <other>` had just printed.
|
|
644
|
+
- **Two guards on the resolved row.** A `file_url` that isn't Amiko public storage is refused locally — only `source`-tagged Doc rows are pinned to the twin's storage namespace server-side, so an ordinary row can hold any URL, and letting it through produced a `/api/posts` 400 naming neither the ref nor the doc. And the image/audio/video MIME check falls back to the **filename extension** when `file_type` is `application/octet-stream`: Create Studio stores `assetMime || "application/octet-stream"` and the job's `mime_type` is nullable, so a real mp3 can arrive typeless — refusing it with "attach it with `--doc`" would be wrong advice about an attachable file.
|
|
645
|
+
- **SKILL.md**: `--media` now documents its three sources (local image path / Amiko-hosted URL / `drive:<docId>`), says a Drive **signed URL** and a `/d/<slug>` share link are both rejected, and notes that a `drive:` ref pointing at a pdf belongs on `--doc`. New Examples row for "post the audio/image from my Drive".
|
|
646
|
+
|
|
647
|
+
### 0.14.1
|
|
648
|
+
|
|
649
|
+
- **`amiko post likers <postId>`** (new): lists everyone who liked a post, paging `GET /api/posts/[id]/likes` to completeness so "who / which friends liked this" is answered from the whole set. Cross-reference the printed user ids against `amiko friends list` for the "which *friends*" variant. The route is **owner-only** (`post.user_id !== user.id` → 403), so the command says so in `--help` and turns the server's generic 403 into a message naming the rule — an agent shouldn't read it as a broken login and retry.
|
|
650
|
+
- **`amiko users search --all`** (new flag). Search stays a one-page lookup by default and `--limit` works again; `--all` is what pages through every match, for "how many people match X". Full paging is capped at 200 (10 requests at the server's 20-row page limit) rather than the shared 2000, because search is ranked recall — nobody needs the 2000th match, and a broad query shouldn't cost a hundred round trips.
|
|
651
|
+
- **`amiko friends list` reports the server's exact total.** `/api/friends` without `limit` already returns the whole list *and* an authoritative `pagination.total` in one request; the header count now comes from `total` instead of the row count. An earlier attempt to paginate this command was reverted — it added ~19 requests, capped the set at 2000, and replaced an exact server-side number with an approximation.
|
|
652
|
+
- **New `src/lib/paginate.ts`**: `fetchAllCursor` follows `hasMore`/`nextCursor` to completeness behind a safety cap, returning `{ items, partial }`. Exhaustion is checked *before* the cap so a set that ends exactly on the cap reports complete — flagging it `partial` would be the same lie about a count, inverted. When the cap really is hit, callers print `⚠ Partial results` and JSON carries `"partial": true` with an `N+` count.
|
|
653
|
+
- **SKILL.md**: new top-level **"Counts & full lists — never answer from the first page"** section (promoted out of Friends, since two of its three commands aren't friends commands) with a question → command → where-the-number-comes-from table; `post likers`'s owner-only rule; the `users search` row in the "Who should I meet?" table now notes one-page-by-default vs `--all`; Examples table gains "谁给我点赞了" and "我有多少好友".
|
|
654
|
+
|
|
655
|
+
### 0.14.0-beta.25
|
|
656
|
+
|
|
657
|
+
- **Notes parity: `amiko post create --title` and `--doc`.** amiko-web's Notes composer (the 小红书-style card grid — same `Post` rows, new name) writes fields the CLI had no way to set. `--title <text>` sets the card heading (max 150 chars, counted in **grapheme clusters** to match the server's `Intl.Segmenter`, validated before any request; omitted from the body when unset so older amiko-web deploys still accept the payload). `--doc <pathOrUrl...>` attaches non-media documents that render as downloadable file cards: local paths upload to the public `docs` bucket via `POST /api/upload/post-doc` (≤4 MB) or presigned `POST /api/upload/post-doc/sign` + direct PUT (>4 MB, 50 MB cap — the multipart route dies at the ~4.5 MB serverless body limit), https URLs pass through, and media MIME types are refused with a pointer to `--media`. Max 8 documents — the server silently truncates past that, so the CLI errors instead. `--content` is no longer a `requiredOption`: with `--media` or `--doc` attached the body may be empty (an image-only note is normal), but a post with nothing in it — including `--title` alone — is still rejected client-side, mirroring the server's rule. `feed` and `post drafts` now render titles and a document count.
|
|
658
|
+
- **Follows: `amiko users follow / unfollow / follow-status / followers / following`.** Wraps `POST|DELETE|GET /api/users/<handleOrId>/follow` and the two list routes (`--limit` 1–50, `--cursor`; the CLI rejects a larger limit rather than letting the server silently clamp). Every slot takes a **handle or a user id**. Following is one-way and takes effect immediately — distinct from `amiko friends add`, which is a mutual request the other side must accept; the SKILL now carries a comparison table so agents stop conflating them.
|
|
659
|
+
- **`amiko feed --type` now mirrors the app's tabs: `all` / `following` / `media`** (plus `friends`, and `humans` / `amikos`). `all` is the UI's name for the API's `for_you`, so both spellings are accepted and normalized on the way out. The server silently degrades an unrecognized type to `for_you`, which reads as "the filter worked" when it didn't — the CLI validates up front and exits with the allowed list rather than issuing a request. `media` is the trap that rule was written for: `/api/feed?type=media` degrades to `for_you`, because the Media tab is served by a **different endpoint** (`/api/media/feed`, site-wide public creations with a different response shape) — so `--type media` routes there instead, with `--kind image|audio|video` for that endpoint's own filter (named `--kind` because its wire param is also called `type`).
|
|
660
|
+
- **SKILL.md**: notes/posts are the same surface; a note is a card so lead with `--title`; image- and document-only notes need no body; documents go through `--doc`, never `amiko drive upload`; **@-mentions must be written `@[Name](userId)`** — a bare `@handle` produces no mention and no notification; quoting a post means pasting its canonical URL into `--content`; follow-vs-friend routing table; which `--type` answers which question.
|
|
661
|
+
|
|
662
|
+
### 0.14.0-beta.24
|
|
663
|
+
|
|
664
|
+
- **Private friend nicknames: `amiko friends nickname`.** Manage the owner's private nicknames for friends against amiko-web `/api/friends/{friendshipId}/nickname`: bare `nickname` (or `nickname list`) pages the whole friends list and shows every nickname set, `set <friend> "<nickname>"` adds or changes one (max 50 characters, validated client-side before any request), `remove <friend>` clears it (soft no-op when none is set). `<friend>` resolves against accepted user friends only — by name, `@handle`, user id, friendship id, or the current nickname — with ambiguity reported, never guessed. Mutations are `--yes`-gated (private resource, plain confirm); `amiko friends list` now also renders a NICKNAME column. **Requires the companion amiko-web deploy (`feat/friend-nicknames`)** — on an older server, reads work but `set`/`remove` return 403 (the CLI prints the deploy hint).
|
|
665
|
+
|
|
666
|
+
### 0.14.0-beta.23
|
|
667
|
+
|
|
668
|
+
- **Chat lists: `amiko chat lists`.** View and manage the owner's private conversation folders against amiko-web `/api/chat-lists`: bare `lists` renders every list with conversation names resolved, plus `create` / `rename` / `add` / `remove` / `delete` subcommands (all `--yes`-gated). The server stores a wholesale `conversation_ids` array, so `add`/`remove` read the current array, mutate locally, and PATCH it back — existing entries always survive an `add`. `--conversation` targets resolve like `chat send` (id / user id / `@handle` / group title / name) but map people to their **existing** DM only — list organization never creates a conversation.
|
|
669
|
+
- **Unread mentions in `chat list`: `@you` marker + `--mentions` filter.** Conversations whose unread messages contain a direct @mention of the owner, a permitted @all, or a **reply to one of the owner's messages** now render `(N unread, @you)`, and `chat list --mentions` keeps only those rows (the `--raw` payload is filtered too, and the flag is ignored on fully read conversations). Driven by the server-computed `has_unread_mention` field; requires the companion amiko-web deploy (`leandrogavidia/global-quick-fixes`) — on an older server the field is absent and reads as false for every conversation, so plain `chat list` still shows unread counts without `@you` and `--mentions` returns no matches. Also moves `resolveConversation`/`findDmByUserId` from `chat.ts` into `lib/conversations.ts` so `chat lists` can reuse them.
|
|
670
|
+
|
|
671
|
+
### 0.14.0-beta.22
|
|
672
|
+
|
|
673
|
+
- **Mark all conversations read: `amiko chat mark-read --all`.** Hits `POST /api/conversations/read-all` as the owner — bulk monotonic `last_read_at` update across every active membership, clears the owner's unread chat/mention notifications (count echoed as "N chat notifications cleared"), busts the per-user conversation cache, and broadcasts read ticks to peers in up to 50 most-recently-active unread conversations. Requires `--all` explicitly (mirrors `notifications read --all`) and sits behind the destructive `--yes` gate — irreversible, and senders see read ticks. `--raw` prints the server payload; success copy never counts conversations (the response's id list is capped at 50). Requires the companion amiko-web read-all deploy (`leandrogavidia/global-quick-fixes`).
|
|
674
|
+
|
|
675
|
+
### 0.14.0-beta.21
|
|
676
|
+
|
|
677
|
+
- **Chat GIFs: `amiko chat gifs [query]` + `amiko chat send --gif <queryOrUrl>`.** `chat gifs` browses Klipy GIFs via the platform proxy (`GET /api/gifs`; trending with no query, `--page`/`--limit`/`--raw`, "Powered by KLIPY") — read-only and ungated. `chat send --gif` attaches a GIF to a message: search words send the top result, a Klipy URL (from `chat gifs`) attaches exactly, and non-Klipy URLs are refused. Sends use the cross-client GIF contract — Klipy mp4/webm → `message_type: "video"` + `metadata.videos`, `.gif` → `"image"` + `metadata.images`, each record `{url, name, contentType, kind: "gif", width?, height?}` — so web/desktop/mobile render a looping watermarked GIF. The `<message>` positional is now optional when `--gif` is present (`content` falls back to `"GIF"`); captions ride as `metadata.image_caption`, which the `--image` path now also sets so image captions actually display in bubbles. One media block per message (`--image`/`--audio`/`--gif` are mutually exclusive). Requires the companion amiko-web `feat/chat-gif-picker` deploy (`GET /api/gifs`) — the twin token already authenticates against it, so no other backend work is needed.
|
|
678
|
+
|
|
679
|
+
### 0.14.0-beta.20
|
|
680
|
+
|
|
681
|
+
- **Draft posts: `amiko post create --draft`, `amiko post drafts`, `amiko post publish <postId>`.** `--draft` saves via `POST /api/posts { status: "draft" }` — no side effects fire and no share URL is printed (drafts 404 by id until published; success copy says "Draft saved" with a publish hint, and `post_url` is omitted from `--json`). `drafts` lists the owner's draft posts (`GET /api/posts?status=draft`, offset pagination via `--limit`/`--offset`; owner-wide across all the owner's twins). `publish` PATCHes `{ status: "published" }`, stamps a fresh timestamp, fires mention/hashtag notifications, and prints the canonical share URL; a 404 points at `amiko post drafts` since drafts can't be fetched by id. Requires the amiko-web draft-posts API (`feat/draft-posts`).
|
|
682
|
+
|
|
683
|
+
### 0.14.0-beta.19
|
|
684
|
+
|
|
685
|
+
- **Pinned messages: `amiko chat pin <messageId>`, `amiko chat unpin <messageId>`, `amiko chat pinned <target>`.** Pins are conversation-wide: `pin` posts an "X pinned a message" announcement to the whole chat (POST `/api/messages/{id}/pin`), so it sits behind the outward-action `--yes` gate with a content preview; `unpin` (DELETE, silent but removes shared state for everyone) sits behind the destructive gate. Groups are admin-gated server-side — a 403 names the rule and the `chat group info` check; DMs let either side pin. 409 at the 20-pin cap and 400 for unpinnable (system/in-flight) messages get status-specific copy. `pinned <target>` is an ungated read using the same target resolution as `chat read` (conversation id, user id, `@handle`, or name; never creates a DM), listing pins oldest-first with sender, message id, and pinner name; `--raw` on all three. Requires the companion amiko-web `feat/chat-pinned-messages` deploy.
|
|
686
|
+
|
|
687
|
+
### 0.14.0-beta.18
|
|
688
|
+
|
|
689
|
+
- **Read receipts: `amiko chat receipts <messageId>`.** Shows when the message was sent, who has read it (per-person first-read time), and who it was delivered to but hasn't read yet. Sender-only, enforced server-side — works for messages sent as the owner (`chat send`); anything else 403s with a clear message. Reader names resolve from the conversation's participant list (short-id fallback). Read-only and ungated, like `chat reactions`; `--raw` prints the API payload. Rows read before receipts tracking existed render as "exact time unknown". Requires the companion amiko-web `feat/chat-read-receipts` deploy (`GET /api/messages/{id}/receipts`).
|
|
690
|
+
|
|
691
|
+
### 0.14.0-beta.17
|
|
692
|
+
|
|
693
|
+
- **@all mentions in group chats + `amiko chat group mention-all <group> <on|off>`.** `chat send` gains `--all`, and a standalone literal `@all` word in the message converts too — both emit the canonical sentinel `@[all](all:all)` (kept in sync with amiko-web `src/utils/mentions.ts`), which amiko-chat expands into a mention notification for every group member. The CLI pre-checks permission client-side, mirroring the web gate plus the server-honored `owner` role (`admin`/`owner`, or the conversation's `allow_everyone_mention_all` flag; `left_at` participants excluded): a denied `--all` exits 1 naming the fix, before the approval gate and before any DM could be created, while a denied literal `@all` degrades to plain text with a note — the server treats an unauthorized token as inert either way. The new `chat group mention-all` toggle PATCHes `/api/conversations/{id}/mention-all-permission` (admin-gated server-side, 403 → admin tip) behind `confirmDestructive`, and the current setting shows in `chat group info` (`@all mentions: everyone | admins only`). Incoming mention markup (`@[Name](id:user|agent)` / `@[all](all:all)`) now renders as plain `@Name`/`@all` in `chat read`, `chat list`, and `chat group list` previews (new `src/lib/mentions.ts`). Requires the companion amiko-web `feat/group-mention-all` deploy.
|
|
694
|
+
|
|
695
|
+
### 0.14.0-beta.15
|
|
696
|
+
|
|
697
|
+
- Create Studio defaults to `--pay credits` (account credits) instead of twin wallet; pass `--pay wallet` for on-chain.
|
|
698
|
+
|
|
699
|
+
### 0.14.0-beta.14
|
|
700
|
+
|
|
701
|
+
- `credits balance` prefers `AMIKO_API_KEY` (`amk_`) → `billing.heyamiko.com/api/credits` directly; falls back to twin-token platform proxy. Override base with `AMIKO_BILLING_URL`.
|
|
702
|
+
|
|
703
|
+
### 0.14.0-beta.13
|
|
704
|
+
|
|
705
|
+
- **Create Studio TTS + voice clone: MiniMax.** `amiko create tts` accepts `--provider minimax|elevenlabs` (infers from `speech-*` / `eleven_*` models when omitted), with provider-specific defaults (`speech-2.8-turbo` + `English_expressive_narrator` vs `eleven_multilingual_v2` + Rachel). Quote body includes `voiceId`. Cloned-voice examples require `--provider minimax` when using a MiniMax `voice_id`.
|
|
706
|
+
- **`amiko voice clone`** defaults to MiniMax (mpp rapid clone + ownership registry); `--provider elevenlabs` keeps legacy IVC. MiniMax-only flags: `--voice-id`, `--model`, `--preview-text`.
|
|
707
|
+
- **`amiko markets service tts`** gains `--provider minimax|elevenlabs` and `--model` for MiniMax speech routing on `/v1/tts/:voiceId`.
|
|
708
|
+
|
|
565
709
|
### 0.14.0-beta.7
|
|
566
710
|
|
|
567
711
|
- **`amiko chat group invite <group>` — share a group's invite/join link + QR.** New read-only subcommand that fetches `GET /api/conversations/{id}/invite` (twin token) and prints the join link plus a scannable QR image URL. Admins-only: SKILL.md instructs the agent to confirm the asker holds `admin`/`owner` (via `amiko chat group info`) before sharing, and the endpoint is admin-gated server-side (403 → admin tip). Fails loudly if the response is missing its link/QR instead of printing a placeholder. Requires the companion amiko-web change (adds `qrUrl` to the invite response + a public `GET /api/conversations/join/[token]/qr` PNG route).
|