@heyamiko/openclaw-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +200 -0
  2. package/contracts/channel-config.schema.json +87 -0
  3. package/contracts/platform-ack.schema.json +25 -0
  4. package/contracts/platform-events.schema.json +87 -0
  5. package/contracts/platform-outbound.schema.json +47 -0
  6. package/dist/index.d.ts +20 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +61 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/src/accounts.d.ts +30 -0
  11. package/dist/src/accounts.d.ts.map +1 -0
  12. package/dist/src/accounts.js +115 -0
  13. package/dist/src/accounts.js.map +1 -0
  14. package/dist/src/api.d.ts +13 -0
  15. package/dist/src/api.d.ts.map +1 -0
  16. package/dist/src/api.js +45 -0
  17. package/dist/src/api.js.map +1 -0
  18. package/dist/src/channel.d.ts +174 -0
  19. package/dist/src/channel.d.ts.map +1 -0
  20. package/dist/src/channel.js +140 -0
  21. package/dist/src/channel.js.map +1 -0
  22. package/dist/src/config-schema.d.ts +92 -0
  23. package/dist/src/config-schema.d.ts.map +1 -0
  24. package/dist/src/config-schema.js +17 -0
  25. package/dist/src/config-schema.js.map +1 -0
  26. package/dist/src/monitor.d.ts +19 -0
  27. package/dist/src/monitor.d.ts.map +1 -0
  28. package/dist/src/monitor.js +432 -0
  29. package/dist/src/monitor.js.map +1 -0
  30. package/dist/src/reply-prefix.d.ts +12 -0
  31. package/dist/src/reply-prefix.d.ts.map +1 -0
  32. package/dist/src/reply-prefix.js +8 -0
  33. package/dist/src/reply-prefix.js.map +1 -0
  34. package/dist/src/runtime.d.ts +57 -0
  35. package/dist/src/runtime.d.ts.map +1 -0
  36. package/dist/src/runtime.js +28 -0
  37. package/dist/src/runtime.js.map +1 -0
  38. package/dist/src/send.d.ts +8 -0
  39. package/dist/src/send.d.ts.map +1 -0
  40. package/dist/src/send.js +51 -0
  41. package/dist/src/send.js.map +1 -0
  42. package/dist/src/status.d.ts +19 -0
  43. package/dist/src/status.d.ts.map +1 -0
  44. package/dist/src/status.js +51 -0
  45. package/dist/src/status.js.map +1 -0
  46. package/dist/src/types.d.ts +79 -0
  47. package/dist/src/types.d.ts.map +1 -0
  48. package/dist/src/types.js +2 -0
  49. package/dist/src/types.js.map +1 -0
  50. package/openclaw.plugin.json +51 -0
  51. package/package.json +73 -0
  52. package/skills/amiko/SKILL.md +287 -0
  53. package/skills/amiko/cli.js +521 -0
  54. package/skills/amiko/lib.js +634 -0
  55. package/skills/composio/SKILL.md +102 -0
@@ -0,0 +1,521 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ setAccountId,
5
+ detectCurrentAccountId,
6
+ getConfig,
7
+ listConfiguredAccounts,
8
+ getTwinInfo,
9
+ updateTwin,
10
+ listDocs,
11
+ getDoc,
12
+ createDoc,
13
+ createDocUploadUrl,
14
+ uploadDocFile,
15
+ uploadDocFromFile,
16
+ checkDocsProcessing,
17
+ deleteDoc,
18
+ getVoice,
19
+ updateVoice,
20
+ designVoice,
21
+ createVoice,
22
+ cloneVoiceFromFile,
23
+ resetVoice,
24
+ updateAvatar,
25
+ searchFriends,
26
+ listFriends,
27
+ listFriendRequests,
28
+ sendFriendRequest,
29
+ acceptFriendRequest,
30
+ declineFriendRequest,
31
+ removeFriendship,
32
+ getFeed,
33
+ getPost,
34
+ commentOnPost,
35
+ connectComposioApp,
36
+ listComposioConnections,
37
+ disconnectComposioConnection,
38
+ } from "./lib.js";
39
+
40
+ const args = process.argv.slice(2);
41
+
42
+ function parseArgs(argv) {
43
+ const result = { _: [] };
44
+ let index = 0;
45
+
46
+ while (index < argv.length) {
47
+ const part = argv[index];
48
+ if (part.startsWith("--")) {
49
+ const key = part.slice(2);
50
+ const next = argv[index + 1];
51
+ const hasValue = next && !next.startsWith("--");
52
+ result[key] = hasValue ? next : true;
53
+ index += hasValue ? 2 : 1;
54
+ continue;
55
+ }
56
+
57
+ result._.push(part);
58
+ index += 1;
59
+ }
60
+
61
+ return result;
62
+ }
63
+
64
+ function parseNumber(value, fallback) {
65
+ if (value === undefined || value === true) return fallback;
66
+ const parsed = Number.parseInt(String(value), 10);
67
+ return Number.isFinite(parsed) ? parsed : fallback;
68
+ }
69
+
70
+ function parseCsv(value) {
71
+ if (!value || value === true) return [];
72
+ return String(value)
73
+ .split(",")
74
+ .map((item) => item.trim())
75
+ .filter(Boolean);
76
+ }
77
+
78
+ function printUsage() {
79
+ console.log(`
80
+ Amiko Skill CLI
81
+
82
+ Config source:
83
+ 1. $OPENCLAW_CONFIG_PATH
84
+ 2. $OPENCLAW_STATE_DIR/openclaw.json
85
+ 3. /data/.openclaw/openclaw.json
86
+ 4. ~/.openclaw/openclaw.json
87
+ Then read channels.amiko from that file.
88
+
89
+ CLI path:
90
+ Installed plugin: /openclaw/extensions/amiko/skills/amiko/cli.js
91
+ Source checkout: ./skills/amiko/cli.js
92
+
93
+ Global options:
94
+ --account <agentId> Select an account from channels.amiko.accounts
95
+ If omitted, the CLI tries OPENCLAW_AGENT_ID or the current workspace path.
96
+
97
+ Local commands:
98
+ accounts List configured account IDs from openclaw.json
99
+
100
+ API commands:
101
+ info
102
+ twin:update --name <name> --description <text> --avatar-url <url> --voice-description <text> --public --private
103
+
104
+ docs [--limit <n>] [--offset <n>] [--search <text>]
105
+ docs:get --id <docId>
106
+ docs:create --title <title> --filename <name> --file-url <url> --file-type <mime>
107
+ [--description <text>] [--doc-type <type>] [--relationship <value>] [--stance <value>]
108
+ docs:upload --file <path> [--title <title>] [--description <text>] [--doc-type <type>]
109
+ [--relationship <value>] [--stance <value>]
110
+ docs:upload-file --file <path>
111
+ docs:presign --filename <name> [--content-type <mime>]
112
+ docs:check --id <docId[,docId2]>
113
+ docs:delete --id <docId>
114
+
115
+ voice
116
+ voice:update [--voice-id <id>] [--status <status>] [--description <text>]
117
+ voice:design --description <text>
118
+ voice:create --generated-voice-id <id> [--description <text>]
119
+ voice:clone --file <path>
120
+ voice:reset
121
+
122
+ avatar:update --file <path>
123
+
124
+ friends [--type <user>] [--favorites]
125
+ friends:search --query <text>
126
+ friends:requests
127
+ friends:add --id <userId>
128
+ friends:accept --id <friendshipId>
129
+ friends:decline --id <friendshipId>
130
+ friends:remove --id <friendshipId>
131
+
132
+ feed [--page <n>] [--limit <n>]
133
+ post --id <postId>
134
+ post:comment --id <postId> --comment <text> [--media-urls <url1,url2>] [--twin-id <twinId>]
135
+
136
+ composio:connections
137
+ composio:connect --app <name> [--redirect-url <url>]
138
+ composio:disconnect --id <connectionId>
139
+
140
+ help
141
+
142
+ Examples:
143
+ node /openclaw/extensions/amiko/skills/amiko/cli.js accounts
144
+ node /openclaw/extensions/amiko/skills/amiko/cli.js --account main info
145
+ node ./skills/amiko/cli.js docs --search handbook
146
+ node ./skills/amiko/cli.js docs:upload --file ./notes.md --title "Notes"
147
+ node ./skills/amiko/cli.js voice:design --description "A calm and natural speaking voice with warm tone"
148
+ node ./skills/amiko/cli.js friends:add --id <userId>
149
+ node ./skills/amiko/cli.js post:comment --id <postId> --comment "Nice post"
150
+ `);
151
+ }
152
+
153
+ async function main() {
154
+ const parsed = parseArgs(args);
155
+ const command = parsed._[0];
156
+
157
+ if (!command || command === "help" || command === "-h" || command === "--help") {
158
+ printUsage();
159
+ process.exit(0);
160
+ }
161
+
162
+ if (parsed.account && parsed.account !== true) {
163
+ setAccountId(parsed.account);
164
+ }
165
+
166
+ if (command === "accounts") {
167
+ console.log(JSON.stringify({ accounts: listConfiguredAccounts() }, null, 2));
168
+ return;
169
+ }
170
+
171
+ try {
172
+ getConfig();
173
+ } catch (error) {
174
+ let available = [];
175
+ try {
176
+ available = listConfiguredAccounts();
177
+ } catch {}
178
+ const detected = detectCurrentAccountId();
179
+ console.error(`Error: ${error.message}`);
180
+ if (detected) {
181
+ console.error(`Detected current agent/account: ${detected}`);
182
+ }
183
+ if (available.length > 0) {
184
+ console.error(`Configured accounts: ${available.join(", ")}`);
185
+ }
186
+ process.exit(1);
187
+ }
188
+
189
+ switch (command) {
190
+ case "info":
191
+ console.log(JSON.stringify(await getTwinInfo(), null, 2));
192
+ return;
193
+
194
+ case "twin:update": {
195
+ const data = {};
196
+ if (parsed.name && parsed.name !== true) data.name = parsed.name;
197
+ if (parsed.description && parsed.description !== true) {
198
+ data.description = parsed.description;
199
+ }
200
+ if (parsed["avatar-url"] && parsed["avatar-url"] !== true) {
201
+ data.avatar_url = parsed["avatar-url"];
202
+ }
203
+ if (parsed["voice-description"] && parsed["voice-description"] !== true) {
204
+ data.voice_description = parsed["voice-description"];
205
+ }
206
+ if (parsed.public) data.is_public = true;
207
+ if (parsed.private) data.is_public = false;
208
+ if (Object.keys(data).length === 0) {
209
+ throw new Error("No update fields provided");
210
+ }
211
+ console.log(JSON.stringify(await updateTwin(data), null, 2));
212
+ return;
213
+ }
214
+
215
+ case "docs":
216
+ console.log(
217
+ JSON.stringify(
218
+ await listDocs({
219
+ limit: parseNumber(parsed.limit, 50),
220
+ offset: parseNumber(parsed.offset, 0),
221
+ search: parsed.search && parsed.search !== true ? parsed.search : undefined,
222
+ }),
223
+ null,
224
+ 2,
225
+ ),
226
+ );
227
+ return;
228
+
229
+ case "docs:get":
230
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
231
+ console.log(JSON.stringify(await getDoc(parsed.id), null, 2));
232
+ return;
233
+
234
+ case "docs:create": {
235
+ if (!parsed.title || parsed.title === true) throw new Error("--title is required");
236
+ if (!parsed.filename || parsed.filename === true) {
237
+ throw new Error("--filename is required");
238
+ }
239
+ if (!parsed["file-url"] || parsed["file-url"] === true) {
240
+ throw new Error("--file-url is required");
241
+ }
242
+ if (!parsed["file-type"] || parsed["file-type"] === true) {
243
+ throw new Error("--file-type is required");
244
+ }
245
+
246
+ console.log(
247
+ JSON.stringify(
248
+ await createDoc({
249
+ title: parsed.title,
250
+ filename: parsed.filename,
251
+ fileUrl: parsed["file-url"],
252
+ fileType: parsed["file-type"],
253
+ description:
254
+ parsed.description && parsed.description !== true
255
+ ? parsed.description
256
+ : undefined,
257
+ doc_type:
258
+ parsed["doc-type"] && parsed["doc-type"] !== true
259
+ ? parsed["doc-type"]
260
+ : "other",
261
+ relationship:
262
+ parsed.relationship && parsed.relationship !== true
263
+ ? parsed.relationship
264
+ : null,
265
+ stance: parsed.stance && parsed.stance !== true ? parsed.stance : null,
266
+ }),
267
+ null,
268
+ 2,
269
+ ),
270
+ );
271
+ return;
272
+ }
273
+
274
+ case "docs:upload":
275
+ if (!parsed.file || parsed.file === true) throw new Error("--file is required");
276
+ console.log(
277
+ JSON.stringify(
278
+ await uploadDocFromFile(parsed.file, {
279
+ title: parsed.title && parsed.title !== true ? parsed.title : undefined,
280
+ description:
281
+ parsed.description && parsed.description !== true
282
+ ? parsed.description
283
+ : undefined,
284
+ docType:
285
+ parsed["doc-type"] && parsed["doc-type"] !== true
286
+ ? parsed["doc-type"]
287
+ : "other",
288
+ relationship:
289
+ parsed.relationship && parsed.relationship !== true
290
+ ? parsed.relationship
291
+ : null,
292
+ stance: parsed.stance && parsed.stance !== true ? parsed.stance : null,
293
+ }),
294
+ null,
295
+ 2,
296
+ ),
297
+ );
298
+ return;
299
+
300
+ case "docs:upload-file":
301
+ if (!parsed.file || parsed.file === true) throw new Error("--file is required");
302
+ console.log(JSON.stringify(await uploadDocFile(parsed.file), null, 2));
303
+ return;
304
+
305
+ case "docs:presign":
306
+ if (!parsed.filename || parsed.filename === true) {
307
+ throw new Error("--filename is required");
308
+ }
309
+ console.log(
310
+ JSON.stringify(
311
+ await createDocUploadUrl(
312
+ parsed.filename,
313
+ parsed["content-type"] && parsed["content-type"] !== true
314
+ ? parsed["content-type"]
315
+ : undefined,
316
+ ),
317
+ null,
318
+ 2,
319
+ ),
320
+ );
321
+ return;
322
+
323
+ case "docs:check":
324
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
325
+ console.log(
326
+ JSON.stringify(await checkDocsProcessing(parseCsv(parsed.id)), null, 2),
327
+ );
328
+ return;
329
+
330
+ case "docs:delete":
331
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
332
+ console.log(JSON.stringify(await deleteDoc(parsed.id), null, 2));
333
+ return;
334
+
335
+ case "voice":
336
+ console.log(JSON.stringify(await getVoice(), null, 2));
337
+ return;
338
+
339
+ case "voice:update": {
340
+ const data = {};
341
+ if (parsed["voice-id"] && parsed["voice-id"] !== true) {
342
+ data.voice_id = parsed["voice-id"];
343
+ }
344
+ if (parsed.status && parsed.status !== true) {
345
+ data.voice_status = parsed.status;
346
+ }
347
+ if (parsed.description && parsed.description !== true) {
348
+ data.voice_description = parsed.description;
349
+ }
350
+ if (Object.keys(data).length === 0) {
351
+ throw new Error("At least one voice field is required");
352
+ }
353
+ console.log(JSON.stringify(await updateVoice(data), null, 2));
354
+ return;
355
+ }
356
+
357
+ case "voice:design": {
358
+ const description =
359
+ parsed.description && parsed.description !== true
360
+ ? parsed.description
361
+ : parsed._.slice(1).join(" ");
362
+ if (!description) throw new Error("--description is required");
363
+ console.log(JSON.stringify(await designVoice(description), null, 2));
364
+ return;
365
+ }
366
+
367
+ case "voice:create":
368
+ if (!parsed["generated-voice-id"] || parsed["generated-voice-id"] === true) {
369
+ throw new Error("--generated-voice-id is required");
370
+ }
371
+ console.log(
372
+ JSON.stringify(
373
+ await createVoice({
374
+ generatedVoiceId: parsed["generated-voice-id"],
375
+ voiceDescription:
376
+ parsed.description && parsed.description !== true
377
+ ? parsed.description
378
+ : undefined,
379
+ }),
380
+ null,
381
+ 2,
382
+ ),
383
+ );
384
+ return;
385
+
386
+ case "voice:clone":
387
+ if (!parsed.file || parsed.file === true) throw new Error("--file is required");
388
+ console.log(JSON.stringify(await cloneVoiceFromFile(parsed.file), null, 2));
389
+ return;
390
+
391
+ case "voice:reset":
392
+ console.log(JSON.stringify(await resetVoice(), null, 2));
393
+ return;
394
+
395
+ case "avatar:update":
396
+ if (!parsed.file || parsed.file === true) throw new Error("--file is required");
397
+ console.log(JSON.stringify(await updateAvatar(parsed.file), null, 2));
398
+ return;
399
+
400
+ case "friends":
401
+ console.log(
402
+ JSON.stringify(
403
+ await listFriends({
404
+ type: parsed.type && parsed.type !== true ? parsed.type : undefined,
405
+ favoritesOnly: Boolean(parsed.favorites),
406
+ }),
407
+ null,
408
+ 2,
409
+ ),
410
+ );
411
+ return;
412
+
413
+ case "friends:search":
414
+ if (!parsed.query || parsed.query === true) throw new Error("--query is required");
415
+ console.log(JSON.stringify(await searchFriends(parsed.query), null, 2));
416
+ return;
417
+
418
+ case "friends:requests":
419
+ console.log(JSON.stringify(await listFriendRequests(), null, 2));
420
+ return;
421
+
422
+ case "friends:add":
423
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
424
+ console.log(JSON.stringify(await sendFriendRequest(parsed.id), null, 2));
425
+ return;
426
+
427
+ case "friends:accept":
428
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
429
+ console.log(JSON.stringify(await acceptFriendRequest(parsed.id), null, 2));
430
+ return;
431
+
432
+ case "friends:decline":
433
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
434
+ console.log(JSON.stringify(await declineFriendRequest(parsed.id), null, 2));
435
+ return;
436
+
437
+ case "friends:remove":
438
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
439
+ console.log(JSON.stringify(await removeFriendship(parsed.id), null, 2));
440
+ return;
441
+
442
+ case "feed":
443
+ console.log(
444
+ JSON.stringify(
445
+ await getFeed({
446
+ page: parseNumber(parsed.page, 1),
447
+ limit: parseNumber(parsed.limit, 20),
448
+ }),
449
+ null,
450
+ 2,
451
+ ),
452
+ );
453
+ return;
454
+
455
+ case "post":
456
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
457
+ console.log(JSON.stringify(await getPost(parsed.id), null, 2));
458
+ return;
459
+
460
+ case "post:comment":
461
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
462
+ if (!parsed.comment || parsed.comment === true) {
463
+ throw new Error("--comment is required");
464
+ }
465
+ console.log(
466
+ JSON.stringify(
467
+ await commentOnPost(parsed.id, parsed.comment, {
468
+ mediaUrls: parseCsv(parsed["media-urls"]),
469
+ twinId:
470
+ parsed["twin-id"] && parsed["twin-id"] !== true
471
+ ? parsed["twin-id"]
472
+ : undefined,
473
+ }),
474
+ null,
475
+ 2,
476
+ ),
477
+ );
478
+ return;
479
+
480
+ case "composio:connections":
481
+ console.log(JSON.stringify(await listComposioConnections(), null, 2));
482
+ return;
483
+
484
+ case "composio:connect":
485
+ if (!parsed.app || parsed.app === true) throw new Error("--app is required");
486
+ console.log(
487
+ JSON.stringify(
488
+ await connectComposioApp(
489
+ parsed.app,
490
+ parsed["redirect-url"] && parsed["redirect-url"] !== true
491
+ ? parsed["redirect-url"]
492
+ : undefined,
493
+ ),
494
+ null,
495
+ 2,
496
+ ),
497
+ );
498
+ return;
499
+
500
+ case "composio:disconnect":
501
+ if (!parsed.id || parsed.id === true) throw new Error("--id is required");
502
+ console.log(
503
+ JSON.stringify(
504
+ await disconnectComposioConnection(parsed.id),
505
+ null,
506
+ 2,
507
+ ),
508
+ );
509
+ return;
510
+
511
+ default:
512
+ console.error(`Unknown command: ${command}`);
513
+ printUsage();
514
+ process.exit(1);
515
+ }
516
+ }
517
+
518
+ main().catch((error) => {
519
+ console.error(`Error: ${error.message}`);
520
+ process.exit(1);
521
+ });