@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 +18 -1
- package/dist/index.js +162 -0
- package/package.json +1 -1
- package/skills/SKILL.md +14 -5
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @heyamiko/amiko-cli (v0.
|
|
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
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
|
|
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
|
-
> **
|
|
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 /
|
|
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
|
|