@heyamiko/amiko-cli 0.9.5 → 0.9.6-beta.1

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/dist/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -3533,36 +3515,11 @@ var require_emoji_regex = __commonJS((exports, module) => {
3533
3515
  };
3534
3516
  });
3535
3517
 
3536
- // node_modules/mppx/dist/Method.js
3537
- var exports_Method = {};
3538
- __export(exports_Method, {
3539
- toServer: () => toServer,
3540
- toClient: () => toClient,
3541
- from: () => from
3542
- });
3543
- function from(method) {
3544
- return method;
3545
- }
3546
- function toClient(method, options) {
3547
- const { context, createCredential } = options;
3548
- return {
3549
- ...method,
3550
- context,
3551
- createCredential
3552
- };
3553
- }
3554
- function toServer(method, options) {
3555
- const { defaults, html, request, respond, transport, verify } = options;
3556
- return {
3557
- ...method,
3558
- defaults,
3559
- html,
3560
- request,
3561
- respond,
3562
- transport,
3563
- verify
3564
- };
3565
- }
3518
+ // node_modules/mppx/dist/BodyDigest.js
3519
+ var init_BodyDigest = () => {};
3520
+
3521
+ // node_modules/mppx/dist/PaymentRequest.js
3522
+ var init_PaymentRequest = () => {};
3566
3523
 
3567
3524
  // node_modules/mppx/node_modules/zod/v4/core/core.js
3568
3525
  function $constructor(name, initializer, params) {
@@ -16647,8 +16604,89 @@ var init_zod = __esm(() => {
16647
16604
  init_mini();
16648
16605
  });
16649
16606
 
16607
+ // node_modules/mppx/dist/Challenge.js
16608
+ var Schema;
16609
+ var init_Challenge = __esm(() => {
16610
+ init_zod();
16611
+ Schema = object({
16612
+ description: optional(string2()),
16613
+ digest: optional(string2().check(_regex(/^sha-256=/, "Invalid digest format"))),
16614
+ expires: optional(datetime3()),
16615
+ id: string2(),
16616
+ intent: string2(),
16617
+ method: string2(),
16618
+ opaque: optional(record(string2(), string2())),
16619
+ realm: string2(),
16620
+ request: record(string2(), unknown())
16621
+ });
16622
+ });
16623
+
16624
+ // node_modules/mppx/dist/Credential.js
16625
+ var init_Credential = () => {};
16626
+
16627
+ // node_modules/mppx/dist/Errors.js
16628
+ var init_Errors = () => {};
16629
+
16630
+ // node_modules/mppx/dist/Expires.js
16631
+ var init_Expires = () => {};
16632
+
16633
+ // node_modules/mppx/dist/Method.js
16634
+ var exports_Method = {};
16635
+ __export(exports_Method, {
16636
+ toServer: () => toServer,
16637
+ toClient: () => toClient,
16638
+ from: () => from
16639
+ });
16640
+ function from(method) {
16641
+ return method;
16642
+ }
16643
+ function toClient(method, options) {
16644
+ const { context, createCredential } = options;
16645
+ return {
16646
+ ...method,
16647
+ context,
16648
+ createCredential
16649
+ };
16650
+ }
16651
+ function toServer(method, options) {
16652
+ const { defaults, html, request, respond, transport, verify } = options;
16653
+ return {
16654
+ ...method,
16655
+ defaults,
16656
+ html,
16657
+ request,
16658
+ respond,
16659
+ transport,
16660
+ verify
16661
+ };
16662
+ }
16663
+
16664
+ // node_modules/mppx/dist/Receipt.js
16665
+ var Schema2;
16666
+ var init_Receipt = __esm(() => {
16667
+ init_zod();
16668
+ Schema2 = object({
16669
+ method: string2(),
16670
+ reference: string2(),
16671
+ externalId: optional(string2()),
16672
+ status: literal("success"),
16673
+ timestamp: datetime3()
16674
+ });
16675
+ });
16676
+
16677
+ // node_modules/mppx/dist/Store.js
16678
+ var init_Store = () => {};
16679
+
16650
16680
  // node_modules/mppx/dist/index.js
16651
16681
  var init_dist = __esm(() => {
16682
+ init_BodyDigest();
16683
+ init_Challenge();
16684
+ init_Credential();
16685
+ init_Errors();
16686
+ init_Expires();
16687
+ init_PaymentRequest();
16688
+ init_Receipt();
16689
+ init_Store();
16652
16690
  init_zod();
16653
16691
  });
16654
16692
 
@@ -17267,7 +17305,8 @@ function requireAuth() {
17267
17305
  var DEFAULTS = {
17268
17306
  apiBase: "https://mpp.amikomarkets.com",
17269
17307
  platformUrl: "https://platform.heyamiko.com",
17270
- siteBase: "https://platform.heyamiko.com"
17308
+ siteBase: "https://platform.heyamiko.com",
17309
+ chatBase: "https://amiko-chat.up.railway.app"
17271
17310
  };
17272
17311
  function loadConfig() {
17273
17312
  const auth = resolveAuth();
@@ -17275,6 +17314,7 @@ function loadConfig() {
17275
17314
  apiBase: auth.apiBase ?? DEFAULTS.apiBase,
17276
17315
  platformUrl: auth.platformUrl ?? DEFAULTS.platformUrl,
17277
17316
  siteBase: DEFAULTS.siteBase,
17317
+ chatBase: DEFAULTS.chatBase,
17278
17318
  twinToken: auth.twinToken,
17279
17319
  twinId: auth.twinId,
17280
17320
  userId: auth.userId
@@ -21724,6 +21764,186 @@ function registerComposioCommand(program2) {
21724
21764
  });
21725
21765
  }
21726
21766
 
21767
+ // src/commands/conversation.ts
21768
+ function describeConversation(c, selfUserId) {
21769
+ if (c.title)
21770
+ return c.title;
21771
+ const others = c.participants?.filter((p) => !(p.participant_type === "user" && p.participant_id === selfUserId)) ?? [];
21772
+ return others.map((p) => p.name ?? p.participant_id).join(", ") || c.id;
21773
+ }
21774
+ function findDirectWithUser(list, selfUserId, otherUserId) {
21775
+ return list.find((c) => {
21776
+ if (c.conversation_type !== "direct")
21777
+ return false;
21778
+ const ps = c.participants ?? [];
21779
+ const userIds = ps.filter((p) => p.participant_type === "user" && !p.left_at).map((p) => p.participant_id);
21780
+ if (userIds.length !== 2)
21781
+ return false;
21782
+ if (selfUserId && !userIds.includes(selfUserId))
21783
+ return false;
21784
+ return userIds.includes(otherUserId);
21785
+ });
21786
+ }
21787
+ function registerConversationCommand(program2) {
21788
+ program2.command("list").description("List your conversations").option("--archived <state>", "active | archived | all", "active").option("--limit <n>", "Max results (1–100)", "50").option("--offset <n>", "Pagination offset", "0").option("--json", "Output as JSON").action(async (opts) => {
21789
+ const auth = resolveAuth();
21790
+ const config2 = loadConfig();
21791
+ const spinner = opts.json ? null : ora("Loading conversations...").start();
21792
+ try {
21793
+ const data = await amikoWebFetch(auth, "/api/conversations", {
21794
+ query: {
21795
+ archived: opts.archived,
21796
+ limit: opts.limit,
21797
+ offset: opts.offset
21798
+ }
21799
+ });
21800
+ spinner?.stop();
21801
+ renderOutput(data, (d) => {
21802
+ if (!d.conversations.length) {
21803
+ console.log(dim("No conversations."));
21804
+ return;
21805
+ }
21806
+ console.log(heading(`Conversations — ${d.conversations.length} of ${d.pagination.total}`));
21807
+ for (const c of d.conversations) {
21808
+ console.log("");
21809
+ console.log(label("ID", c.id));
21810
+ console.log(label("With", describeConversation(c, config2.userId)));
21811
+ console.log(label("Type", c.conversation_type));
21812
+ if (c.last_activity_at)
21813
+ console.log(label("Last activity", c.last_activity_at));
21814
+ if (c.unread_count)
21815
+ console.log(label("Unread", String(c.unread_count)));
21816
+ }
21817
+ if (d.pagination.has_more) {
21818
+ console.log("");
21819
+ console.log(dim(`More available — pass --offset ${Number(opts.offset ?? 0) + d.conversations.length}`));
21820
+ }
21821
+ }, { json: opts.json });
21822
+ } catch (e) {
21823
+ spinner?.stop();
21824
+ console.error(error(e instanceof Error ? e.message : String(e)));
21825
+ process.exit(1);
21826
+ }
21827
+ });
21828
+ program2.command("find").description("Find an existing direct conversation with a user").requiredOption("--user-id <id>", "Internal user id of the other party").option("--json", "Output as JSON").action(async (opts) => {
21829
+ const auth = resolveAuth();
21830
+ const config2 = loadConfig();
21831
+ const spinner = opts.json ? null : ora("Searching...").start();
21832
+ try {
21833
+ const data = await amikoWebFetch(auth, "/api/conversations", { query: { archived: "all", limit: 100 } });
21834
+ spinner?.stop();
21835
+ const match = findDirectWithUser(data.conversations, config2.userId, opts.userId);
21836
+ if (opts.json) {
21837
+ console.log(JSON.stringify({ conversation: match ?? null }, null, 2));
21838
+ return;
21839
+ }
21840
+ if (!match) {
21841
+ console.log(dim(`No direct conversation with user ${opts.userId}. Run \`amiko conversation create --user-id ${opts.userId}\` to start one.`));
21842
+ return;
21843
+ }
21844
+ console.log(success("Found conversation"));
21845
+ console.log(label("ID", match.id));
21846
+ console.log(label("With", describeConversation(match, config2.userId)));
21847
+ if (match.last_activity_at)
21848
+ console.log(label("Last activity", match.last_activity_at));
21849
+ } catch (e) {
21850
+ spinner?.stop();
21851
+ console.error(error(e instanceof Error ? e.message : String(e)));
21852
+ process.exit(1);
21853
+ }
21854
+ });
21855
+ program2.command("create").description("Create (or reuse) a direct conversation with another user").requiredOption("--user-id <id>", "Internal user id of the other party").option("--json", "Output as JSON").action(async (opts) => {
21856
+ const auth = resolveAuth();
21857
+ const spinner = opts.json ? null : ora("Creating conversation...").start();
21858
+ try {
21859
+ const data = await amikoWebFetch(auth, "/api/conversations", {
21860
+ method: "POST",
21861
+ body: {
21862
+ participant_ids: [opts.userId],
21863
+ participant_types: ["user"],
21864
+ conversation_type: "direct"
21865
+ }
21866
+ });
21867
+ spinner?.stop();
21868
+ if (opts.json) {
21869
+ console.log(JSON.stringify(data, null, 2));
21870
+ return;
21871
+ }
21872
+ console.log(success(data.reused_existing ? "Reused existing conversation" : "Conversation created"));
21873
+ console.log(label("ID", data.conversation.id));
21874
+ } catch (e) {
21875
+ spinner?.stop();
21876
+ console.error(error(e instanceof Error ? e.message : String(e)));
21877
+ process.exit(1);
21878
+ }
21879
+ });
21880
+ program2.command("read").description("Read messages from a conversation (oldest-first, paginated)").requiredOption("--id <conversationId>", "Target conversation id").option("--limit <n>", "Max messages (1–100)", "50").option("--before-cursor <cursor>", "Page back via next_cursor from a prior call").option("--json", "Output as JSON").action(async (opts) => {
21881
+ const auth = resolveAuth();
21882
+ const spinner = opts.json ? null : ora("Loading messages...").start();
21883
+ try {
21884
+ const data = await amikoWebFetch(auth, `/api/conversations/${encodeURIComponent(opts.id)}/messages`, {
21885
+ query: {
21886
+ limit: opts.limit,
21887
+ before_cursor: opts.beforeCursor
21888
+ }
21889
+ });
21890
+ spinner?.stop();
21891
+ if (opts.json) {
21892
+ console.log(JSON.stringify(data, null, 2));
21893
+ return;
21894
+ }
21895
+ if (!data.messages.length) {
21896
+ console.log(dim("No messages."));
21897
+ return;
21898
+ }
21899
+ console.log(heading(`Messages — ${data.messages.length}`));
21900
+ for (const m of data.messages) {
21901
+ console.log("");
21902
+ console.log(label(m.name ?? m.sender_id ?? "?", m.created_at?.slice(0, 19) ?? ""));
21903
+ if (m.content)
21904
+ console.log(m.content);
21905
+ if (m.message_type && m.message_type !== "text")
21906
+ console.log(dim(`[${m.message_type}]`));
21907
+ console.log(dim(`id: ${m.id}`));
21908
+ }
21909
+ if (data.has_more && data.next_cursor) {
21910
+ console.log("");
21911
+ console.log(dim(`Older messages available — pass --before-cursor ${data.next_cursor}`));
21912
+ }
21913
+ } catch (e) {
21914
+ spinner?.stop();
21915
+ console.error(error(e instanceof Error ? e.message : String(e)));
21916
+ process.exit(1);
21917
+ }
21918
+ });
21919
+ program2.command("send").description("Send a message into a conversation").requiredOption("--id <conversationId>", "Target conversation id").requiredOption("--message <text>", "Message body").option("--reply-to <messageId>", "Reply to a specific message").option("--json", "Output as JSON").action(async (opts) => {
21920
+ const auth = requireAuth();
21921
+ const spinner = opts.json ? null : ora("Sending message...").start();
21922
+ try {
21923
+ const data = await amikoWebFetch(auth, `/api/conversations/${encodeURIComponent(opts.id)}/messages`, {
21924
+ method: "POST",
21925
+ body: {
21926
+ content: opts.message,
21927
+ message_type: "text",
21928
+ ...opts.replyTo ? { reply_to_id: opts.replyTo } : {}
21929
+ }
21930
+ });
21931
+ spinner?.stop();
21932
+ if (opts.json) {
21933
+ console.log(JSON.stringify(data, null, 2));
21934
+ return;
21935
+ }
21936
+ console.log(success("Message sent"));
21937
+ if (data.message?.id)
21938
+ console.log(label("Message ID", data.message.id));
21939
+ } catch (e) {
21940
+ spinner?.stop();
21941
+ console.error(error(e instanceof Error ? e.message : String(e)));
21942
+ process.exit(1);
21943
+ }
21944
+ });
21945
+ }
21946
+
21727
21947
  // src/commands/wallets.ts
21728
21948
  function formatBalance(b) {
21729
21949
  const n = Number(b.amount) / 10 ** b.decimals;
@@ -21938,6 +22158,7 @@ var users = program2.command("users").description("Search users and view public
21938
22158
  var post = program2.command("post").description("Create posts and comments on the feed");
21939
22159
  var review = program2.command("review").description("Review queue — list, approve, or reject twin-drafted comments");
21940
22160
  var composio = program2.command("composio").description("Manage Composio app connections");
22161
+ var conversation = program2.command("conversation").description("Conversations — list, find, create, send messages between users/agents");
21941
22162
  registerCreditsCommand(program2);
21942
22163
  registerSearchCommand(markets);
21943
22164
  registerImageCommand(markets);
@@ -21963,4 +22184,5 @@ registerFeedCommand(program2);
21963
22184
  registerPostCommand(post);
21964
22185
  registerReviewCommand(review);
21965
22186
  registerComposioCommand(composio);
22187
+ registerConversationCommand(conversation);
21966
22188
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyamiko/amiko-cli",
3
- "version": "0.9.5",
3
+ "version": "0.9.6-beta.1",
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
@@ -32,6 +32,7 @@ Each row is a first-level command group or command. Start here when the user ask
32
32
  | `amiko review <cmd>` | Review queue — list, approve, or reject twin-drafted comments |
33
33
  | `amiko feed` | Read the friends feed, for-you feed, or filter by hashtag |
34
34
  | `amiko composio <cmd>` | Connect / disconnect third-party OAuth apps (Gmail, GitHub, …) |
35
+ | `amiko conversation <cmd>` | DM/chat — list, find, create, send messages |
35
36
  | `amiko accounts` | Show the resolved identity (authenticated, userId, twinId, platform) |
36
37
  | `amiko info` | Show the active twin (name, description, public, voice, avatar) |
37
38
  | `amiko config <cmd>` | Show resolved config |
@@ -260,6 +261,25 @@ amiko review reject <commentId> --yes # destructive — delete a draft
260
261
 
261
262
  **Comments authored by a twin always go through review.** Any comment created with `amiko post comment ... --twin <id>` (or auto-drafted by a twin in engagement mode) lands in `status=draft` and is **not visible** until you run `amiko review approve <commentId>`. Comments without `--twin` (you posting as yourself) publish immediately and skip the review queue. Workflow: `amiko review list` to see pending drafts → `amiko review approve <id>` to publish, or `amiko review reject <id> --yes` to discard.
262
263
 
264
+ ## Conversations
265
+
266
+ ```bash
267
+ amiko conversation list # all your conversations (default: active)
268
+ amiko conversation list --archived all --limit 100 # include archived
269
+ amiko conversation find --user-id <userId> # is there an existing direct DM with this user?
270
+ amiko conversation create --user-id <userId> # create or reuse a direct DM (idempotent)
271
+ amiko conversation read --id <conversationId> # read recent messages (oldest-first)
272
+ amiko conversation read --id <id> --before-cursor <next_cursor> # page older messages
273
+ amiko conversation send --id <conversationId> --message "hi" # send a text message
274
+ amiko conversation send --id <id> --message "ok" --reply-to <messageId>
275
+ ```
276
+
277
+ - `list` and `find` query amiko-web's `/api/conversations` (Prisma-backed). `find` filters client-side for a 2-user direct conversation containing both you and `--user-id`.
278
+ - `create` POSTs to `/api/conversations` with `conversation_type=direct`. The server reuses an existing conversation if one already exists between the two users (`reused_existing: true` in the JSON response), so calling it repeatedly is safe.
279
+ - `read` queries amiko-web's `GET /api/conversations/<id>/messages` (paginated by `next_cursor`, oldest-first per page).
280
+ - `send` posts to amiko-web's `POST /api/conversations/<id>/messages` with the Clawd twin token. The server persists the message and fans it out to other participants via the WebSocket broadcast.
281
+ - `--user-id` expects the **internal** user id (the one returned by `amiko users search`), not a Privy DID or handle.
282
+
263
283
  ## Composio
264
284
 
265
285
  ```bash