@heyamiko/amiko-cli 0.12.0-beta.3 → 0.12.0-beta.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # @heyamiko/amiko-cli (v0.11.0-beta.3)
1
+ # @heyamiko/amiko-cli (v0.12.0-beta.4)
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
 
@@ -210,6 +210,23 @@ amiko create media --service video --limit 5
210
210
 
211
211
  > `amiko markets image` still exists and is unchanged — it's the direct MPP pay-per-call path (pre-pay in AMIKO). Use `amiko create` for the Create Studio experience (async, charge-on-success); use `amiko markets` for raw MPP endpoints.
212
212
 
213
+ ## Chat
214
+
215
+ Your conversations — **DMs and group chats** — acting **as you (the owner)**. (This is distinct from the openhermit gateway's `session_*` tools, which act as the *agent* on the agent's own sessions.)
216
+
217
+ ```bash
218
+ amiko chat list # all conversations (DM + group): id, peer/title, last msg, unread
219
+ amiko chat list --limit 50 --archived # include archived
220
+ amiko chat read @sophie # recent messages with a user (by @handle)
221
+ amiko chat read <conversationId> --limit 40 # by conversation id (from `chat list`) — works for groups too
222
+ amiko chat send @sophie "on my way!" --yes # send as the owner
223
+ amiko chat send <groupConversationId> "hi all" --yes # send to a group chat
224
+ ```
225
+
226
+ - **`<target>`** for `read`/`send` is a **conversation id** (from `chat list` — use this for groups), an **`@handle`**, or a **name** (resolved via user search; if ambiguous, the CLI lists candidates instead of guessing).
227
+ - A DM target that doesn't exist yet is **created automatically** (find-or-create).
228
+ - `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.
229
+
213
230
  ## Amazon
214
231
 
215
232
  ```bash
package/dist/index.js CHANGED
@@ -28924,6 +28924,166 @@ More available — raise --limit to see more.`));
28924
28924
  });
28925
28925
  }
28926
28926
 
28927
+ // src/commands/chat.ts
28928
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
28929
+ function authOrExit() {
28930
+ try {
28931
+ return requireAuth();
28932
+ } catch (e5) {
28933
+ console.error(error(e5 instanceof Error ? e5.message : "auth failed"));
28934
+ process.exit(1);
28935
+ }
28936
+ }
28937
+ function convLabel(conv, selfUserId) {
28938
+ if (conv.title)
28939
+ return conv.title;
28940
+ const others = (conv.participants ?? []).filter((p) => !(p.participant_type === "user" && p.participant_id === selfUserId)).map((p) => p.user?.name ?? p.twin?.name ?? p.participant_id.slice(0, 8)).filter((n) => !!n);
28941
+ return others.length ? others.join(", ") : "(conversation)";
28942
+ }
28943
+ async function findOrCreateDm(auth, userId) {
28944
+ const data = await amikoWebFetch(auth, "/api/conversations", {
28945
+ method: "POST",
28946
+ body: {
28947
+ participant_ids: [userId],
28948
+ participant_types: ["user"],
28949
+ conversation_type: "direct"
28950
+ },
28951
+ timeoutMs: 20000
28952
+ });
28953
+ const id = data.conversation?.id;
28954
+ if (!id)
28955
+ throw new Error("Could not open a conversation with that user.");
28956
+ return id;
28957
+ }
28958
+ async function resolveConversation(auth, target) {
28959
+ const t = target.trim();
28960
+ if (UUID_RE.test(t))
28961
+ return t;
28962
+ if (t.startsWith("@")) {
28963
+ const handle = t.slice(1);
28964
+ const prof = await amikoWebFetch(auth, `/api/users/${encodeURIComponent(handle)}`, { timeoutMs: 15000 });
28965
+ if (!prof.user?.id)
28966
+ throw new Error(`No user @${handle}.`);
28967
+ return findOrCreateDm(auth, prof.user.id);
28968
+ }
28969
+ const res = await amikoWebFetch(auth, "/api/search", {
28970
+ query: { q: t, type: "people", limit: 8 },
28971
+ timeoutMs: 15000
28972
+ });
28973
+ const users = res.users ?? [];
28974
+ if (users.length === 0) {
28975
+ throw new Error(`No user matching "${t}". Use an exact @handle, or a conversation id from \`amiko chat list\`.`);
28976
+ }
28977
+ if (users.length > 1) {
28978
+ const names = users.map((u) => `${u.name ?? "?"} (@${u.handle ?? u.id.slice(0, 8)})`).join(", ");
28979
+ throw new Error(`"${t}" is ambiguous — matches: ${names}. Use an exact @handle or a conversation id.`);
28980
+ }
28981
+ return findOrCreateDm(auth, users[0].id);
28982
+ }
28983
+ function registerChatCommand(chat) {
28984
+ chat.command("list").description("List your conversations (DMs and group chats)").option("--limit <n>", "Max conversations", "30").option("--archived", "Include archived conversations").option("--raw", "Output raw JSON").action(async (opts) => {
28985
+ const auth = authOrExit();
28986
+ let data;
28987
+ try {
28988
+ data = await amikoWebFetch(auth, "/api/conversations", {
28989
+ query: {
28990
+ limit: opts.limit,
28991
+ ...opts.archived ? { archived: "all" } : {}
28992
+ },
28993
+ timeoutMs: 20000
28994
+ });
28995
+ } catch (e5) {
28996
+ console.error(error(e5 instanceof Error ? e5.message : "Failed to list conversations"));
28997
+ process.exit(1);
28998
+ }
28999
+ if (opts.raw) {
29000
+ console.log(JSON.stringify(data, null, 2));
29001
+ return;
29002
+ }
29003
+ const convs = data.conversations ?? [];
29004
+ if (convs.length === 0) {
29005
+ console.log(dim("No conversations."));
29006
+ return;
29007
+ }
29008
+ console.log(heading(`Conversations (${convs.length})
29009
+ `));
29010
+ for (const c of convs) {
29011
+ const kind = c.conversation_type === "direct" ? "DM" : c.conversation_type;
29012
+ const unread = c.unread_count ? ` (${c.unread_count} unread)` : "";
29013
+ console.log(label(`[${kind}] ${convLabel(c, auth.userId)}`, dim(c.id)));
29014
+ if (c.last_message?.content) {
29015
+ const who = c.last_message.name ? `${c.last_message.name}: ` : "";
29016
+ console.log(dim(` ${who}${c.last_message.content.slice(0, 80)}`));
29017
+ }
29018
+ if (unread)
29019
+ console.log(dim(` ${unread.trim()}`));
29020
+ }
29021
+ });
29022
+ chat.command("read <target>").description("Read recent messages in a conversation (conversation id, @handle, or name)").option("--limit <n>", "Max messages", "20").option("--raw", "Output raw JSON").action(async (target, opts) => {
29023
+ const auth = authOrExit();
29024
+ let convId;
29025
+ try {
29026
+ convId = await resolveConversation(auth, target);
29027
+ } catch (e5) {
29028
+ console.error(error(e5 instanceof Error ? e5.message : "Could not resolve target"));
29029
+ process.exit(1);
29030
+ }
29031
+ let data;
29032
+ try {
29033
+ data = await amikoWebFetch(auth, `/api/conversations/${encodeURIComponent(convId)}/messages`, { query: { limit: opts.limit }, timeoutMs: 20000 });
29034
+ } catch (e5) {
29035
+ console.error(error(e5 instanceof Error ? e5.message : "Failed to read messages"));
29036
+ process.exit(1);
29037
+ }
29038
+ if (opts.raw) {
29039
+ console.log(JSON.stringify(data, null, 2));
29040
+ return;
29041
+ }
29042
+ const msgs = (data.messages ?? []).slice().reverse();
29043
+ if (msgs.length === 0) {
29044
+ console.log(dim("No messages."));
29045
+ return;
29046
+ }
29047
+ console.log(heading(`Messages (${msgs.length})
29048
+ `));
29049
+ for (const m of msgs) {
29050
+ const who = m.sender?.name ?? m.name ?? (m.sender_id ? m.sender_id.slice(0, 8) : "?");
29051
+ const when = m.created_at ? m.created_at.slice(0, 19).replace("T", " ") : "";
29052
+ console.log(label(who, m.content));
29053
+ if (when)
29054
+ console.log(dim(` ${when}`));
29055
+ }
29056
+ });
29057
+ chat.command("send <target> <message>").description("Send a message as yourself (the owner) to a conversation (id, @handle, or name)").option("--yes", "Skip the confirmation (required in non-interactive shells)").option("--raw", "Output raw JSON").action(async (target, message, opts) => {
29058
+ const auth = authOrExit();
29059
+ let convId;
29060
+ try {
29061
+ convId = await resolveConversation(auth, target);
29062
+ } catch (e5) {
29063
+ console.error(error(e5 instanceof Error ? e5.message : "Could not resolve target"));
29064
+ process.exit(1);
29065
+ }
29066
+ await requireApproval({
29067
+ cost: "no charge — sends a message as you (the owner)",
29068
+ summary: `Send to ${target}: ${message.slice(0, 80)}${message.length > 80 ? "…" : ""}`,
29069
+ yes: opts.yes,
29070
+ commandExample: `amiko chat send ${JSON.stringify(target)} ${JSON.stringify(message)}`
29071
+ });
29072
+ let data;
29073
+ try {
29074
+ data = await amikoWebFetch(auth, `/api/conversations/${encodeURIComponent(convId)}/messages`, { method: "POST", body: { content: message }, timeoutMs: 20000 });
29075
+ } catch (e5) {
29076
+ console.error(error(e5 instanceof Error ? e5.message : "Send failed"));
29077
+ process.exit(1);
29078
+ }
29079
+ if (opts.raw) {
29080
+ console.log(JSON.stringify(data, null, 2));
29081
+ return;
29082
+ }
29083
+ console.log(success("Message sent") + dim(` (conversation ${convId.slice(0, 8)}…)`));
29084
+ });
29085
+ }
29086
+
28927
29087
  // src/commands/amazon.ts
28928
29088
  var TREASURY3 = "FPiZAf3jEnNfCwfvbv46jdPfHbmSepD4Q6iHiLJDQXvb";
28929
29089
  async function payAmiko(auth, wallet, amount2) {
@@ -31806,6 +31966,7 @@ var program2 = new Command;
31806
31966
  program2.name("amiko").description("Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace services").version(PKG_VERSION);
31807
31967
  var markets = program2.command("markets").description("MPP marketplace — discover and call paid AMIKO services");
31808
31968
  var create2 = program2.command("create").description("Create Studio — generate images, video, speech, music, and SFX (charged on success)");
31969
+ var chat = program2.command("chat").description("Your conversations (DMs + group chats) as the owner — list, read, send");
31809
31970
  var wallets = program2.command("wallets").description("Manage twin wallets — list, swap, bridge");
31810
31971
  var twin = program2.command("twin").description("Update twin identity (name, description, visibility)");
31811
31972
  var drive = program2.command("drive").alias("docs").description("Manage twin drive — upload/download/search files, organise into folders");
@@ -31820,6 +31981,7 @@ registerCreditsCommand(program2);
31820
31981
  registerSearchCommand(markets);
31821
31982
  registerImageCommand(markets);
31822
31983
  registerCreateCommand(create2);
31984
+ registerChatCommand(chat);
31823
31985
  registerAmazonCommand(markets);
31824
31986
  registerWalletsCreateCommand(wallets);
31825
31987
  registerWalletsListCommand(wallets);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.12.0-beta.3",
3
+ "version": "0.12.0-beta.4",
4
4
  "description": "Amiko CLI — swap tokens, manage credits, bridge cross-chain, and call marketplace agents",
5
5
  "type": "module",
6
6
  "bin": {
package/skills/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: amiko-cli
3
- description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — read platform notifications (friend requests, mentions, system alerts), search and write **cross-agent memories** about the owner (what other agents already know — preferences, decisions, facts), manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, generate media — images, video, speech, music, and SFX — via Create Studio (`amiko create`, async and charged on success), call paid MPP marketplace services (X/Twitter search, Amazon product search, TTS/STT, AI chat), manage the twin's identity and drive (files, folders, RAG), voice and avatar, and social graph (friends, posts, comments, feed). Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). DMs / chat sessions are NOT in this CLI — use the openhermit gateway's built-in session_list / session_read tools for both local sessions and platform DMs. Use this skill whenever the user asks about their Amiko notifications, drive, memory, social graph, wallets, credits, or marketplace services — anything that would show up in their Amiko account or cost AMIKO/credits.
3
+ description: The Amiko CLI lets an agent act on the Amiko platform end-to-end — read platform notifications (friend requests, mentions, system alerts), search and write **cross-agent memories** about the owner (what other agents already know — preferences, decisions, facts), manage Solana/Base wallets (create, swap via Jupiter, bridge USDC via Across, transfer tokens to external addresses), top up and spend credits, generate media — images, video, speech, music, and SFX — via Create Studio (`amiko create`, async and charged on success), call paid MPP marketplace services (X/Twitter search, Amazon product search, TTS/STT, AI chat), manage the twin's identity and drive (files, folders, RAG), voice and avatar, and social graph (friends, posts, comments, feed). Auth is automatic when run from the agent's workspace folder; payments are platform-custodied (no keys on disk). The owner's DMs and group chats are `amiko chat` (list / read / send AS the owner, 人对人); the agent's OWN sessions are the openhermit gateway's session_list / session_send (AS the agent) — different identities, different surfaces. Use this skill whenever the user asks about their Amiko notifications, chats/DMs, drive, memory, social graph, wallets, credits, or marketplace services — anything that would show up in their Amiko account or cost AMIKO/credits.
4
4
  homepage: https://platform.heyamiko.com
5
5
  metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["node"]}}}
6
6
  ---
@@ -41,13 +41,13 @@ The CLI is installed globally and is pre-authenticated when you're inside your w
41
41
 
42
42
  ## Command groups
43
43
 
44
- Discover everything via `amiko --help`. Groups: `markets` (paid MPP services), `create` (Create Studio — async media generation, charged on success), `wallets`, `credits`, `twin`, `drive` (files / folders / RAG; `docs` is an alias), `voice`, `avatar`, `friends`, `users`, `post`, `review`, `feed`, `notifications`, `memory`, plus the top-level `accounts`, `info`, `config`, `update`. All twin-scoped commands accept `--twin <id>`. Most commands support `--json`.
44
+ Discover everything via `amiko --help`. Groups: `markets` (paid MPP services), `create` (Create Studio — async media generation, charged on success), `chat` (owner's DMs + group chats), `wallets`, `credits`, `twin`, `drive` (files / folders / RAG; `docs` is an alias), `voice`, `avatar`, `friends`, `users`, `post`, `review`, `feed`, `notifications`, `memory`, plus the top-level `accounts`, `info`, `config`, `update`. All twin-scoped commands accept `--twin <id>`. Most commands support `--json`.
45
45
 
46
- > **DMs / chat sessions are NOT in this CLI.** Use the openhermit gateway's built-in `session_list` / `session_read` tools for both the agent's local sessions and platform DMs with other users. The previous `amiko conversation` namespace was removed in 0.10.1-beta.4.
46
+ > **Two chat surfaces — pick by identity.** `amiko chat` is the **owner's** DMs and group chats, acting **as the owner** (list / read / send). The openhermit gateway's `session_list` / `session_send` are the **agent's own** sessions, acting **as the agent**. "Send a message to Sophie for me" → `amiko chat send`; "have the twin reply as itself" → gateway. (The old `amiko conversation` namespace was removed in 0.10.1-beta.4; `amiko chat` is its owner-identity replacement.)
47
47
 
48
48
  ## Critical Rules
49
49
 
50
- 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI refuses to run `markets *`, `create *`, `credits topup`, `wallets swap send`, `wallets bridge send`, `wallets transfer` (and destructive ops like `twin update --public`, `drive delete`, `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `review reject`) unless `--yes` is passed. The refusal prints the cost and the re-run command. **Quote the cost, get explicit approval, THEN append `--yes`.**
50
+ 1. **Every paid / value-moving command is hard-gated on `--yes` in a non-interactive shell.** The CLI refuses to run `markets *`, `create *`, `chat send`, `credits topup`, `wallets swap send`, `wallets bridge send`, `wallets transfer` (and destructive ops like `twin update --public`, `drive delete`, `friends remove`, `friends reports request`, `avatar update`, `voice reset`, `review reject`) unless `--yes` is passed. The refusal prints the cost and the re-run command. **Quote the cost, get explicit approval, THEN append `--yes`.**
51
51
  2. **After every paid command, report the remaining balance.** The CLI prints a `Balance: N credits` line — include that figure in your reply.
52
52
  3. **Never retry a failed command.** Report and stop. Every paid call costs tokens even on failure.
53
53
  4. **Auth is automatic.** Never suggest `amiko login` / `amiko connect`. Run from the agent's workspace folder; if anything looks off, `amiko accounts` shows the resolved `userId` / `twinId`.
@@ -96,6 +96,15 @@ All paid markets commands auto-select the twin's active Solana wallet (see `--wa
96
96
 
97
97
  `amiko create <image|video|tts|music|sfx>` is the CLI half of the platform Create Studio. It generates through Amiko's own authenticated endpoints (not raw MPP), runs **async**, and is **charged on success** from the twin's custodial wallet — a failed or timed-out generation is **never billed**, and there is no pre-pay. **The command returns immediately** with a `jobId` and `status: PENDING` — it does NOT block for the whole generation (so you're not held for 60s–9min). It prints how to check + a rough ETA. **Poll for the result** with `amiko create status <jobId>` (re-query that job, ~24h) — wait roughly: image ~15–60s, video ~2–9min, music ~30–120s, tts/sfx ~5–20s. Or `amiko create media` (list recent generations; `--service`/`--limit`/`--raw`). Result is a permanent Supabase Storage URL. (Pass `--wait` only if you deliberately want the command to block until done.) `--token <AMIKO|USDC|USDT|SOL>` selects the charge token (default auto, AMIKO-first). Prefer `create` over `markets image` for media generation — it doesn't lose money on timeouts. `markets image` remains the raw MPP pre-pay path.
98
98
 
99
+ ## Chat — behavior notes
100
+
101
+ `amiko chat` is the **owner's** conversations, acting **as the owner** (人对人) — NOT the agent's own sessions (those are the gateway's `session_*`, as the agent).
102
+
103
+ - `amiko chat list` — all conversations, **DMs and group chats** (id, type, peer/title, last message, unread).
104
+ - `amiko chat read <target>` — recent messages. `<target>` = a conversation id (from `list`), an `@handle`, or a name.
105
+ - `amiko chat send <target> "message"` — send **as the owner**. Delivered in real time. `--yes` required in non-interactive shells (it's an outward message to a real person). For **group chats**, use the conversation id from `list`; for a **DM**, an `@handle`/name resolves + finds-or-creates the DM.
106
+ - Name resolution goes through user search; if ambiguous it lists candidates — don't blind-send. Server enforces who you're allowed to message (friend/participant rules); surface its error, don't retry.
107
+
99
108
  ## Drive (files & folders)
100
109
 
101
110
  The drive is **shared between the user and the agent** — anything either side uploads is visible to both. Uploads kick off an async RAG-parsing job (~seconds to minutes); list/search/rename/move/delete all work regardless of parsing status. `drive search` and `drive list --search` hit filename, title, description, and the parsed content body (once parsing completes). Prefer reading parsed content via the agent workspace sync over polling `drive status` from long-running tasks. `amiko docs` is kept as an alias for backward compat.
@@ -133,7 +142,7 @@ Workflow: start with `friends matches --limit 20 --json`; narrow with `--dimensi
133
142
 
134
143
  > **Default platform = Amiko.** When the owner asks about notifications **without naming a platform**, assume Amiko and answer with `amiko notifications`. Only ask if they explicitly mention a non-Amiko channel.
135
144
  >
136
- > **DMs / chat history are handled by the openhermit gateway**, not the CLI. For "did X message me?" / "check my chat with Y", use the built-in `session_list` / `session_read` tools — they cover both the agent's local sessions and platform DMs in the unified gateway view.
145
+ > **Owner DMs / group chats: `amiko chat`.** For "did X message me?" / "what did Sophie and I say?" / "message Y for me", use `amiko chat list` / `amiko chat read <who>` / `amiko chat send <who> "…"` — these act **as the owner** (人对人). The gateway's `session_*` tools are the **agent's own** sessions (as the agent), a separate surface (see Chat behavior notes).
137
146
 
138
147
  Platform notifications cover friend requests, mentions, system alerts, and post-related events.
139
148