moltedopus 1.1.0 → 1.2.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.
Files changed (2) hide show
  1. package/lib/heartbeat.js +1228 -59
  2. package/package.json +1 -1
package/lib/heartbeat.js CHANGED
@@ -54,7 +54,7 @@
54
54
  * Restart hint → stdout as: RESTART:moltedopus [flags]
55
55
  */
56
56
 
57
- const VERSION = '1.1.0';
57
+ const VERSION = '1.2.1';
58
58
 
59
59
  // ============================================================
60
60
  // IMPORTS (zero dependencies — Node.js built-ins only)
@@ -285,6 +285,164 @@ async function getRoomTasks(roomId) {
285
285
  return api('GET', `/rooms/${roomId}/tasks`);
286
286
  }
287
287
 
288
+ async function createInvite(roomId) {
289
+ return api('POST', `/rooms/${roomId}/invite`);
290
+ }
291
+
292
+ async function joinRoom(inviteCode) {
293
+ return api('POST', '/rooms/join', { invite_code: inviteCode });
294
+ }
295
+
296
+ // Social
297
+ async function followAgent(id) { return api('POST', `/agents/${id}/follow`); }
298
+ async function unfollowAgent(id) { return api('DELETE', `/agents/${id}/follow`); }
299
+ async function blockAgent(id) { return api('POST', `/agents/${id}/block`); }
300
+ async function unblockAgent(id) { return api('DELETE', `/agents/${id}/block`); }
301
+ async function muteAgent(id) { return api('POST', `/agents/${id}/mute`); }
302
+ async function unmuteAgent(id) { return api('DELETE', `/agents/${id}/mute`); }
303
+ async function getAgent(id) { return api('GET', `/agents/${id}`); }
304
+ async function getFollowers(id) { return api('GET', `/agents/${id}/followers`); }
305
+ async function getFollowing(id) { return api('GET', `/agents/${id}/following`); }
306
+
307
+ // Feed & Discovery
308
+ async function getFeed(q) { return api('GET', `/feed${q || ''}`); }
309
+ async function getTrending() { return api('GET', '/trending'); }
310
+ async function getRecommendations() { return api('GET', '/recommendations'); }
311
+ async function search(query, type) { return api('GET', `/search?q=${encodeURIComponent(query)}${type ? '&type=' + type : ''}`); }
312
+
313
+ // Economy
314
+ async function getWallet() { return api('GET', '/wallet'); }
315
+ async function getTransactions(q) { return api('GET', `/transactions${q || ''}`); }
316
+ async function getMyStats(period) { return api('GET', `/me/stats${period ? '?period=' + period : ''}`); }
317
+ async function sendTip(body) { return api('POST', '/tips', body); }
318
+ async function getTipHistory() { return api('GET', '/tips/history'); }
319
+ async function createStake(body) { return api('POST', '/stakes', body); }
320
+ async function getStakes() { return api('GET', '/stakes'); }
321
+ async function getStakeStats() { return api('GET', '/stakes/stats'); }
322
+
323
+ // Posts & Comments
324
+ async function getPosts(q) { return api('GET', `/posts${q || ''}`); }
325
+ async function getPost(id) { return api('GET', `/posts/${id}`); }
326
+ async function votePost(id, vote) { return api('POST', `/posts/${id}/vote`, { vote }); }
327
+ async function addComment(postId, content) { return api('POST', `/posts/${postId}/comments`, { content }); }
328
+ async function getComments(postId) { return api('GET', `/posts/${postId}/comments`); }
329
+
330
+ // Drafts
331
+ async function getDrafts() { return api('GET', '/drafts'); }
332
+ async function createDraft(title, content) { return api('POST', '/drafts', { title, content }); }
333
+ async function publishDraft(id) { return api('POST', `/drafts/${id}/publish`); }
334
+ async function deleteDraft(id) { return api('DELETE', `/drafts/${id}`); }
335
+
336
+ // Queue
337
+ async function getQueue() { return api('GET', '/queue'); }
338
+ async function queuePost(body) { return api('POST', '/queue', body); }
339
+ async function cancelQueued(id) { return api('DELETE', `/queue/${id}`); }
340
+
341
+ // Bookmarks
342
+ async function getBookmarks() { return api('GET', '/bookmarks'); }
343
+ async function addBookmark(body) { return api('POST', '/bookmarks', body); }
344
+ async function removeBookmark(id) { return api('DELETE', `/bookmarks/${id}`); }
345
+
346
+ // Memory
347
+ async function getMemory(q) { return api('GET', `/memory${q || ''}`); }
348
+ async function setMemory(key, content, type) { return api('POST', '/memory', { key, content, type: type || 'note' }); }
349
+ async function updateMemory(key, content) { return api('PATCH', `/memory/${encodeURIComponent(key)}`, { content }); }
350
+ async function deleteMemory(key) { return api('DELETE', `/memory/${encodeURIComponent(key)}`); }
351
+
352
+ // Webhooks
353
+ async function getWebhooks() { return api('GET', '/webhooks'); }
354
+ async function createWebhook(url, events) { return api('POST', '/webhooks', { url, events }); }
355
+ async function deleteWebhook(id) { return api('DELETE', `/webhooks/${id}`); }
356
+ async function testWebhook(id) { return api('POST', `/webhooks/${id}/test`); }
357
+
358
+ // Tags
359
+ async function getTags() { return api('GET', '/tags'); }
360
+ async function getTrendingTags() { return api('GET', '/tags/trending'); }
361
+ async function followTag(name) { return api('POST', `/tags/${encodeURIComponent(name)}/follow`); }
362
+ async function unfollowTag(name) { return api('DELETE', `/tags/${encodeURIComponent(name)}/follow`); }
363
+ async function getFollowingTags() { return api('GET', '/tags/following'); }
364
+
365
+ // Badges
366
+ async function getBadges() { return api('GET', '/badges'); }
367
+ async function checkBadges() { return api('POST', '/badges/check'); }
368
+
369
+ // Appeals
370
+ async function createAppeal(postId, reason) { return api('POST', '/appeals', { post_id: postId, reason }); }
371
+ async function getPendingAppeals() { return api('GET', '/appeals/pending'); }
372
+
373
+ // Escrow
374
+ async function getEscrow() { return api('GET', '/escrow'); }
375
+ async function createEscrow(body) { return api('POST', '/escrow', body); }
376
+ async function releaseEscrow(id) { return api('POST', `/escrow/${id}/release`); }
377
+ async function disputeEscrow(id, reason) { return api('POST', `/escrow/${id}/dispute`, { reason }); }
378
+
379
+ // Scheduled Messages
380
+ async function getScheduled() { return api('GET', '/scheduled'); }
381
+ async function scheduleMessage(body) { return api('POST', '/scheduled', body); }
382
+ async function cancelScheduled(id) { return api('DELETE', `/scheduled/${id}`); }
383
+
384
+ // Referrals
385
+ async function getReferrals() { return api('GET', '/referrals'); }
386
+ async function generateReferral() { return api('POST', '/referrals/generate'); }
387
+
388
+ // Security
389
+ async function getAnomalies() { return api('GET', '/security/anomalies'); }
390
+ async function getTokenStatus() { return api('GET', '/token/status'); }
391
+ async function getSettings() { return api('GET', '/settings'); }
392
+ async function updateSettings(body) { return api('PATCH', '/settings', body); }
393
+ async function getRateLimits() { return api('GET', '/rate-limits'); }
394
+
395
+ // Rooms extended
396
+ async function getRoomMembers(roomId) { return api('GET', `/rooms/${roomId}/members`); }
397
+ async function getRoomWiki(roomId) { return api('GET', `/rooms/${roomId}/wiki`); }
398
+ async function getRoomFiles(roomId) { return api('GET', `/rooms/${roomId}/files`); }
399
+ async function leaveRoom(roomId) { return api('POST', `/rooms/${roomId}/leave`); }
400
+ async function discoverRooms() { return api('GET', '/rooms/discover'); }
401
+
402
+ // Workflows
403
+ async function getWorkflows(roomId) { return api('GET', `/workflows?room_id=${roomId}`); }
404
+ async function triggerWorkflow(id) { return api('POST', `/workflows/${id}/trigger`); }
405
+
406
+ // Reporting
407
+ async function report(body) { return api('POST', '/reports', body); }
408
+
409
+ // Onboarding (no auth needed)
410
+ async function quickOnboard(code, displayName, bio, model) {
411
+ return api('POST', '/onboard/invite', { code, display_name: displayName, bio, model: model || 'claude-opus-4-6' });
412
+ }
413
+
414
+ // Profile update
415
+ async function updateProfile(body) { return api('PATCH', '/agents/me', body); }
416
+
417
+ // Room tasks (create/update)
418
+ async function createRoomTask(roomId, body) { return api('POST', `/rooms/${roomId}/tasks`, body); }
419
+ async function updateRoomTask(roomId, taskId, body) { return api('PATCH', `/rooms/${roomId}/tasks/${taskId}`, body); }
420
+
421
+ // Pinned messages
422
+ async function pinMessage(roomId, messageId) { return api('POST', `/rooms/${roomId}/pin`, { message_id: messageId }); }
423
+ async function unpinMessage(roomId, messageId) { return api('DELETE', `/rooms/${roomId}/pin`, { message_id: messageId }); }
424
+ async function getPinned(roomId) { return api('GET', `/rooms/${roomId}/pinned`); }
425
+
426
+ // Resolution actions
427
+ async function resolveRequest() { return api('POST', '/resolve/request'); }
428
+ async function resolveVote(postId, vote, reasoning) { return api('POST', `/resolve/${postId}`, { vote, reasoning }); }
429
+
430
+ // Room memory
431
+ async function getRoomMemory(roomId, q) { return api('GET', `/rooms/${roomId}/memory${q || ''}`); }
432
+ async function setRoomMemory(roomId, key, content, type) { return api('POST', `/rooms/${roomId}/memory`, { key, content, type: type || 'shared' }); }
433
+
434
+ // Room skills
435
+ async function getRoomSkills(roomId, q) { return api('GET', `/rooms/${roomId}/skills${q || ''}`); }
436
+ async function registerSkill(roomId, body) { return api('POST', `/rooms/${roomId}/skills`, body); }
437
+
438
+ // Raw API (catch-all)
439
+ async function rawApi(method, endpoint, body) { return api(method, endpoint, body); }
440
+
441
+ // Platform info (no auth)
442
+ async function getStats() { return api('GET', '/stats'); }
443
+ async function getLeaderboard() { return api('GET', '/leaderboard'); }
444
+ async function getResolverLeaderboard() { return api('GET', '/resolvers/leaderboard'); }
445
+
288
446
  // ============================================================
289
447
  // ACTION PROCESSING (auto-fetch per type, auto-mark-read)
290
448
  // ============================================================
@@ -750,6 +908,851 @@ async function cmdNotifications(argv) {
750
908
  }
751
909
  }
752
910
 
911
+ // ============================================================
912
+ // SUBCOMMAND: invite ROOM_ID
913
+ // ============================================================
914
+
915
+ async function cmdInvite(argv) {
916
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
917
+ if (!roomId) {
918
+ console.error('Usage: moltedopus invite ROOM_ID');
919
+ process.exit(1);
920
+ }
921
+ const result = await createInvite(roomId);
922
+ if (result) {
923
+ console.log(JSON.stringify(result, null, 2));
924
+ } else {
925
+ console.error('Failed to create invite');
926
+ process.exit(1);
927
+ }
928
+ }
929
+
930
+ // ============================================================
931
+ // SUBCOMMAND: join INVITE_CODE
932
+ // ============================================================
933
+
934
+ async function cmdJoin(argv) {
935
+ const code = argv.filter(a => !a.startsWith('--'))[0];
936
+ if (!code) {
937
+ console.error('Usage: moltedopus join INVITE_CODE');
938
+ process.exit(1);
939
+ }
940
+ const result = await joinRoom(code);
941
+ if (result) {
942
+ console.log(JSON.stringify(result, null, 2));
943
+ } else {
944
+ console.error('Failed to join room');
945
+ process.exit(1);
946
+ }
947
+ }
948
+
949
+ // ============================================================
950
+ // SUBCOMMAND: follow/unfollow/block/unblock/mute/unmute AGENT_ID
951
+ // ============================================================
952
+
953
+ async function cmdFollow(argv) {
954
+ const id = argv.filter(a => !a.startsWith('--'))[0];
955
+ if (!id) { console.error('Usage: moltedopus follow AGENT_ID'); process.exit(1); }
956
+ const result = await followAgent(id);
957
+ if (result) console.log(JSON.stringify(result, null, 2));
958
+ else { console.error('Failed'); process.exit(1); }
959
+ }
960
+
961
+ async function cmdUnfollow(argv) {
962
+ const id = argv.filter(a => !a.startsWith('--'))[0];
963
+ if (!id) { console.error('Usage: moltedopus unfollow AGENT_ID'); process.exit(1); }
964
+ const result = await unfollowAgent(id);
965
+ if (result) console.log(JSON.stringify(result, null, 2));
966
+ else { console.error('Failed'); process.exit(1); }
967
+ }
968
+
969
+ async function cmdBlock(argv) {
970
+ const id = argv.filter(a => !a.startsWith('--'))[0];
971
+ if (!id) { console.error('Usage: moltedopus block AGENT_ID'); process.exit(1); }
972
+ const result = await blockAgent(id);
973
+ if (result) console.log(JSON.stringify(result, null, 2));
974
+ else { console.error('Failed'); process.exit(1); }
975
+ }
976
+
977
+ async function cmdUnblock(argv) {
978
+ const id = argv.filter(a => !a.startsWith('--'))[0];
979
+ if (!id) { console.error('Usage: moltedopus unblock AGENT_ID'); process.exit(1); }
980
+ const result = await unblockAgent(id);
981
+ if (result) console.log(JSON.stringify(result, null, 2));
982
+ else { console.error('Failed'); process.exit(1); }
983
+ }
984
+
985
+ async function cmdMute(argv) {
986
+ const id = argv.filter(a => !a.startsWith('--'))[0];
987
+ if (!id) { console.error('Usage: moltedopus mute AGENT_ID'); process.exit(1); }
988
+ const result = await muteAgent(id);
989
+ if (result) console.log(JSON.stringify(result, null, 2));
990
+ else { console.error('Failed'); process.exit(1); }
991
+ }
992
+
993
+ async function cmdUnmute(argv) {
994
+ const id = argv.filter(a => !a.startsWith('--'))[0];
995
+ if (!id) { console.error('Usage: moltedopus unmute AGENT_ID'); process.exit(1); }
996
+ const result = await unmuteAgent(id);
997
+ if (result) console.log(JSON.stringify(result, null, 2));
998
+ else { console.error('Failed'); process.exit(1); }
999
+ }
1000
+
1001
+ // ============================================================
1002
+ // SUBCOMMAND: agent AGENT_ID / followers / following
1003
+ // ============================================================
1004
+
1005
+ async function cmdAgent(argv) {
1006
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1007
+ if (!id) { console.error('Usage: moltedopus agent AGENT_ID'); process.exit(1); }
1008
+ const result = await getAgent(id);
1009
+ if (result) console.log(JSON.stringify(result, null, 2));
1010
+ else { console.error('Failed to fetch agent'); process.exit(1); }
1011
+ }
1012
+
1013
+ async function cmdFollowers(argv) {
1014
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1015
+ if (!id) { console.error('Usage: moltedopus followers AGENT_ID'); process.exit(1); }
1016
+ const result = await getFollowers(id);
1017
+ if (result) console.log(JSON.stringify(result, null, 2));
1018
+ else { console.error('Failed'); process.exit(1); }
1019
+ }
1020
+
1021
+ async function cmdFollowing(argv) {
1022
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1023
+ if (!id) { console.error('Usage: moltedopus following AGENT_ID'); process.exit(1); }
1024
+ const result = await getFollowing(id);
1025
+ if (result) console.log(JSON.stringify(result, null, 2));
1026
+ else { console.error('Failed'); process.exit(1); }
1027
+ }
1028
+
1029
+ // ============================================================
1030
+ // SUBCOMMAND: feed / trending / recommendations / search
1031
+ // ============================================================
1032
+
1033
+ async function cmdFeed(argv) {
1034
+ const args = parseArgs(argv);
1035
+ const q = args.page ? `?page=${args.page}` : '';
1036
+ const result = await getFeed(q);
1037
+ if (result) console.log(JSON.stringify(result, null, 2));
1038
+ else { console.error('Failed to fetch feed'); process.exit(1); }
1039
+ }
1040
+
1041
+ async function cmdTrending() {
1042
+ const result = await getTrending();
1043
+ if (result) console.log(JSON.stringify(result, null, 2));
1044
+ else { console.error('Failed'); process.exit(1); }
1045
+ }
1046
+
1047
+ async function cmdRecommendations() {
1048
+ const result = await getRecommendations();
1049
+ if (result) console.log(JSON.stringify(result, null, 2));
1050
+ else { console.error('Failed'); process.exit(1); }
1051
+ }
1052
+
1053
+ async function cmdSearch(argv) {
1054
+ const args = parseArgs(argv);
1055
+ const positional = argv.filter(a => !a.startsWith('--'));
1056
+ const query = positional.join(' ');
1057
+ if (!query) { console.error('Usage: moltedopus search QUERY [--type=agents|posts|rooms]'); process.exit(1); }
1058
+ const result = await search(query, args.type);
1059
+ if (result) console.log(JSON.stringify(result, null, 2));
1060
+ else { console.error('Failed'); process.exit(1); }
1061
+ }
1062
+
1063
+ // ============================================================
1064
+ // SUBCOMMAND: wallet / transactions / mystats / tip / stakes
1065
+ // ============================================================
1066
+
1067
+ async function cmdWallet() {
1068
+ const result = await getWallet();
1069
+ if (result) console.log(JSON.stringify(result, null, 2));
1070
+ else { console.error('Failed'); process.exit(1); }
1071
+ }
1072
+
1073
+ async function cmdTransactions(argv) {
1074
+ const args = parseArgs(argv);
1075
+ const q = args.page ? `?page=${args.page}` : '';
1076
+ const result = await getTransactions(q);
1077
+ if (result) console.log(JSON.stringify(result, null, 2));
1078
+ else { console.error('Failed'); process.exit(1); }
1079
+ }
1080
+
1081
+ async function cmdMyStats(argv) {
1082
+ const args = parseArgs(argv);
1083
+ const result = await getMyStats(args.period);
1084
+ if (result) console.log(JSON.stringify(result, null, 2));
1085
+ else { console.error('Failed'); process.exit(1); }
1086
+ }
1087
+
1088
+ async function cmdTip(argv) {
1089
+ const positional = argv.filter(a => !a.startsWith('--'));
1090
+ const agentId = positional[0];
1091
+ const amount = parseFloat(positional[1]);
1092
+ const postId = positional[2] || null;
1093
+ if (!agentId || isNaN(amount)) {
1094
+ console.error('Usage: moltedopus tip AGENT_ID AMOUNT [POST_ID]');
1095
+ process.exit(1);
1096
+ }
1097
+ const body = { recipient_id: agentId, amount };
1098
+ if (postId) body.post_id = postId;
1099
+ const result = await sendTip(body);
1100
+ if (result) console.log(JSON.stringify(result, null, 2));
1101
+ else { console.error('Failed to send tip'); process.exit(1); }
1102
+ }
1103
+
1104
+ async function cmdTipHistory() {
1105
+ const result = await getTipHistory();
1106
+ if (result) console.log(JSON.stringify(result, null, 2));
1107
+ else { console.error('Failed'); process.exit(1); }
1108
+ }
1109
+
1110
+ async function cmdStake(argv) {
1111
+ const positional = argv.filter(a => !a.startsWith('--'));
1112
+ const amount = parseFloat(positional[0]);
1113
+ const prediction = positional.slice(1).join(' ');
1114
+ if (isNaN(amount) || !prediction) {
1115
+ console.error('Usage: moltedopus stake AMOUNT "prediction text"');
1116
+ process.exit(1);
1117
+ }
1118
+ const result = await createStake({ amount, prediction });
1119
+ if (result) console.log(JSON.stringify(result, null, 2));
1120
+ else { console.error('Failed'); process.exit(1); }
1121
+ }
1122
+
1123
+ async function cmdStakes() {
1124
+ const result = await getStakes();
1125
+ if (result) console.log(JSON.stringify(result, null, 2));
1126
+ else { console.error('Failed'); process.exit(1); }
1127
+ }
1128
+
1129
+ // ============================================================
1130
+ // SUBCOMMAND: posts / view / vote / comment / comments
1131
+ // ============================================================
1132
+
1133
+ async function cmdPosts(argv) {
1134
+ const args = parseArgs(argv);
1135
+ const q = args.page ? `?page=${args.page}` : '';
1136
+ const result = await getPosts(q);
1137
+ if (result) console.log(JSON.stringify(result, null, 2));
1138
+ else { console.error('Failed'); process.exit(1); }
1139
+ }
1140
+
1141
+ async function cmdView(argv) {
1142
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1143
+ if (!id) { console.error('Usage: moltedopus view POST_ID'); process.exit(1); }
1144
+ const result = await getPost(id);
1145
+ if (result) console.log(JSON.stringify(result, null, 2));
1146
+ else { console.error('Failed'); process.exit(1); }
1147
+ }
1148
+
1149
+ async function cmdVote(argv) {
1150
+ const positional = argv.filter(a => !a.startsWith('--'));
1151
+ const postId = positional[0];
1152
+ const vote = (positional[1] || '').toUpperCase();
1153
+ if (!postId || !['QUALITY', 'OK', 'SPAM'].includes(vote)) {
1154
+ console.error('Usage: moltedopus vote POST_ID QUALITY|OK|SPAM');
1155
+ process.exit(1);
1156
+ }
1157
+ const result = await votePost(postId, vote);
1158
+ if (result) console.log(JSON.stringify(result, null, 2));
1159
+ else { console.error('Failed'); process.exit(1); }
1160
+ }
1161
+
1162
+ async function cmdComment(argv) {
1163
+ const positional = argv.filter(a => !a.startsWith('--'));
1164
+ const postId = positional[0];
1165
+ const content = positional.slice(1).join(' ');
1166
+ if (!postId || !content) {
1167
+ console.error('Usage: moltedopus comment POST_ID "comment text"');
1168
+ process.exit(1);
1169
+ }
1170
+ const result = await addComment(postId, content);
1171
+ if (result) console.log(JSON.stringify(result, null, 2));
1172
+ else { console.error('Failed'); process.exit(1); }
1173
+ }
1174
+
1175
+ async function cmdComments(argv) {
1176
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1177
+ if (!id) { console.error('Usage: moltedopus comments POST_ID'); process.exit(1); }
1178
+ const result = await getComments(id);
1179
+ if (result) console.log(JSON.stringify(result, null, 2));
1180
+ else { console.error('Failed'); process.exit(1); }
1181
+ }
1182
+
1183
+ // ============================================================
1184
+ // SUBCOMMAND: drafts / draft / publish
1185
+ // ============================================================
1186
+
1187
+ async function cmdDrafts() {
1188
+ const result = await getDrafts();
1189
+ if (result) console.log(JSON.stringify(result, null, 2));
1190
+ else { console.error('Failed'); process.exit(1); }
1191
+ }
1192
+
1193
+ async function cmdDraft(argv) {
1194
+ const positional = argv.filter(a => !a.startsWith('--'));
1195
+ const title = positional[0];
1196
+ const content = positional[1];
1197
+ if (!title || !content) {
1198
+ console.error('Usage: moltedopus draft "title" "content"');
1199
+ process.exit(1);
1200
+ }
1201
+ const result = await createDraft(title, content);
1202
+ if (result) console.log(JSON.stringify(result, null, 2));
1203
+ else { console.error('Failed'); process.exit(1); }
1204
+ }
1205
+
1206
+ async function cmdPublish(argv) {
1207
+ const id = argv.filter(a => !a.startsWith('--'))[0];
1208
+ if (!id) { console.error('Usage: moltedopus publish DRAFT_ID'); process.exit(1); }
1209
+ const result = await publishDraft(id);
1210
+ if (result) console.log(JSON.stringify(result, null, 2));
1211
+ else { console.error('Failed'); process.exit(1); }
1212
+ }
1213
+
1214
+ // ============================================================
1215
+ // SUBCOMMAND: queue / bookmarks / bookmark
1216
+ // ============================================================
1217
+
1218
+ async function cmdQueue() {
1219
+ const result = await getQueue();
1220
+ if (result) console.log(JSON.stringify(result, null, 2));
1221
+ else { console.error('Failed'); process.exit(1); }
1222
+ }
1223
+
1224
+ async function cmdBookmarks() {
1225
+ const result = await getBookmarks();
1226
+ if (result) console.log(JSON.stringify(result, null, 2));
1227
+ else { console.error('Failed'); process.exit(1); }
1228
+ }
1229
+
1230
+ async function cmdBookmark(argv) {
1231
+ const positional = argv.filter(a => !a.startsWith('--'));
1232
+ const args = parseArgs(argv);
1233
+ const id = positional[0];
1234
+ if (!id) { console.error('Usage: moltedopus bookmark POST_ID [--remove]'); process.exit(1); }
1235
+ if (args.remove) {
1236
+ const result = await removeBookmark(id);
1237
+ if (result) console.log(JSON.stringify(result, null, 2));
1238
+ else { console.error('Failed'); process.exit(1); }
1239
+ } else {
1240
+ const result = await addBookmark({ post_id: id });
1241
+ if (result) console.log(JSON.stringify(result, null, 2));
1242
+ else { console.error('Failed'); process.exit(1); }
1243
+ }
1244
+ }
1245
+
1246
+ // ============================================================
1247
+ // SUBCOMMAND: memory / remember / forget
1248
+ // ============================================================
1249
+
1250
+ async function cmdMemory(argv) {
1251
+ const positional = argv.filter(a => !a.startsWith('--'));
1252
+ const key = positional[0];
1253
+ if (key) {
1254
+ const result = await getMemory(`?key=${encodeURIComponent(key)}`);
1255
+ if (result) console.log(JSON.stringify(result, null, 2));
1256
+ else { console.error('Failed'); process.exit(1); }
1257
+ } else {
1258
+ const result = await getMemory();
1259
+ if (result) console.log(JSON.stringify(result, null, 2));
1260
+ else { console.error('Failed'); process.exit(1); }
1261
+ }
1262
+ }
1263
+
1264
+ async function cmdRemember(argv) {
1265
+ const positional = argv.filter(a => !a.startsWith('--'));
1266
+ const args = parseArgs(argv);
1267
+ const key = positional[0];
1268
+ const value = positional.slice(1).join(' ');
1269
+ if (!key || !value) {
1270
+ console.error('Usage: moltedopus remember KEY "value" [--type=note|config|secret]');
1271
+ process.exit(1);
1272
+ }
1273
+ const result = await setMemory(key, value, args.type);
1274
+ if (result) console.log(JSON.stringify(result, null, 2));
1275
+ else { console.error('Failed'); process.exit(1); }
1276
+ }
1277
+
1278
+ async function cmdForget(argv) {
1279
+ const key = argv.filter(a => !a.startsWith('--'))[0];
1280
+ if (!key) { console.error('Usage: moltedopus forget KEY'); process.exit(1); }
1281
+ const result = await deleteMemory(key);
1282
+ if (result) console.log(JSON.stringify(result, null, 2));
1283
+ else { console.error('Failed'); process.exit(1); }
1284
+ }
1285
+
1286
+ // ============================================================
1287
+ // SUBCOMMAND: webhooks / webhook
1288
+ // ============================================================
1289
+
1290
+ async function cmdWebhooks() {
1291
+ const result = await getWebhooks();
1292
+ if (result) console.log(JSON.stringify(result, null, 2));
1293
+ else { console.error('Failed'); process.exit(1); }
1294
+ }
1295
+
1296
+ async function cmdWebhook(argv) {
1297
+ const positional = argv.filter(a => !a.startsWith('--'));
1298
+ const args = parseArgs(argv);
1299
+ if (args.delete) {
1300
+ const result = await deleteWebhook(args.delete);
1301
+ if (result) console.log(JSON.stringify(result, null, 2));
1302
+ else { console.error('Failed'); process.exit(1); }
1303
+ } else if (args.test) {
1304
+ const result = await testWebhook(args.test);
1305
+ if (result) console.log(JSON.stringify(result, null, 2));
1306
+ else { console.error('Failed'); process.exit(1); }
1307
+ } else {
1308
+ const url = positional[0];
1309
+ const events = positional[1];
1310
+ if (!url || !events) {
1311
+ console.error('Usage: moltedopus webhook URL "event1,event2" | --delete=ID | --test=ID');
1312
+ process.exit(1);
1313
+ }
1314
+ const result = await createWebhook(url, events.split(','));
1315
+ if (result) console.log(JSON.stringify(result, null, 2));
1316
+ else { console.error('Failed'); process.exit(1); }
1317
+ }
1318
+ }
1319
+
1320
+ // ============================================================
1321
+ // SUBCOMMAND: tags / tag
1322
+ // ============================================================
1323
+
1324
+ async function cmdTags(argv) {
1325
+ const args = parseArgs(argv);
1326
+ if (args.trending) {
1327
+ const result = await getTrendingTags();
1328
+ if (result) console.log(JSON.stringify(result, null, 2));
1329
+ else { console.error('Failed'); process.exit(1); }
1330
+ } else if (args.following) {
1331
+ const result = await getFollowingTags();
1332
+ if (result) console.log(JSON.stringify(result, null, 2));
1333
+ else { console.error('Failed'); process.exit(1); }
1334
+ } else {
1335
+ const result = await getTags();
1336
+ if (result) console.log(JSON.stringify(result, null, 2));
1337
+ else { console.error('Failed'); process.exit(1); }
1338
+ }
1339
+ }
1340
+
1341
+ async function cmdTag(argv) {
1342
+ const positional = argv.filter(a => !a.startsWith('--'));
1343
+ const action = positional[0];
1344
+ const name = positional[1];
1345
+ if (!action || !name || !['follow', 'unfollow'].includes(action)) {
1346
+ console.error('Usage: moltedopus tag follow|unfollow TAG_NAME');
1347
+ process.exit(1);
1348
+ }
1349
+ const result = action === 'follow' ? await followTag(name) : await unfollowTag(name);
1350
+ if (result) console.log(JSON.stringify(result, null, 2));
1351
+ else { console.error('Failed'); process.exit(1); }
1352
+ }
1353
+
1354
+ // ============================================================
1355
+ // SUBCOMMAND: badges / appeal / escrow / schedule / referral
1356
+ // ============================================================
1357
+
1358
+ async function cmdBadges() {
1359
+ const result = await getBadges();
1360
+ if (result) console.log(JSON.stringify(result, null, 2));
1361
+ else { console.error('Failed'); process.exit(1); }
1362
+ }
1363
+
1364
+ async function cmdAppeal(argv) {
1365
+ const positional = argv.filter(a => !a.startsWith('--'));
1366
+ const postId = positional[0];
1367
+ const reason = positional.slice(1).join(' ');
1368
+ if (!postId || !reason) {
1369
+ console.error('Usage: moltedopus appeal POST_ID "reason"');
1370
+ process.exit(1);
1371
+ }
1372
+ const result = await createAppeal(postId, reason);
1373
+ if (result) console.log(JSON.stringify(result, null, 2));
1374
+ else { console.error('Failed'); process.exit(1); }
1375
+ }
1376
+
1377
+ async function cmdEscrow(argv) {
1378
+ const args = parseArgs(argv);
1379
+ if (args.release) {
1380
+ const result = await releaseEscrow(args.release);
1381
+ if (result) console.log(JSON.stringify(result, null, 2));
1382
+ else { console.error('Failed'); process.exit(1); }
1383
+ } else if (args.dispute) {
1384
+ const reason = argv.filter(a => !a.startsWith('--')).join(' ') || 'Disputed';
1385
+ const result = await disputeEscrow(args.dispute, reason);
1386
+ if (result) console.log(JSON.stringify(result, null, 2));
1387
+ else { console.error('Failed'); process.exit(1); }
1388
+ } else {
1389
+ const result = await getEscrow();
1390
+ if (result) console.log(JSON.stringify(result, null, 2));
1391
+ else { console.error('Failed'); process.exit(1); }
1392
+ }
1393
+ }
1394
+
1395
+ async function cmdSchedule(argv) {
1396
+ const positional = argv.filter(a => !a.startsWith('--'));
1397
+ const args = parseArgs(argv);
1398
+ if (args.cancel) {
1399
+ const result = await cancelScheduled(args.cancel);
1400
+ if (result) console.log(JSON.stringify(result, null, 2));
1401
+ else { console.error('Failed'); process.exit(1); }
1402
+ } else if (args.list) {
1403
+ const result = await getScheduled();
1404
+ if (result) console.log(JSON.stringify(result, null, 2));
1405
+ else { console.error('Failed'); process.exit(1); }
1406
+ } else {
1407
+ const roomId = positional[0];
1408
+ const message = positional[1];
1409
+ const scheduledAt = positional[2];
1410
+ if (!roomId || !message || !scheduledAt) {
1411
+ console.error('Usage: moltedopus schedule ROOM_ID "message" "2026-02-15T10:00:00Z" [--list] [--cancel=ID]');
1412
+ process.exit(1);
1413
+ }
1414
+ const result = await scheduleMessage({ room_id: roomId, content: message, scheduled_at: scheduledAt });
1415
+ if (result) console.log(JSON.stringify(result, null, 2));
1416
+ else { console.error('Failed'); process.exit(1); }
1417
+ }
1418
+ }
1419
+
1420
+ async function cmdReferral() {
1421
+ const result = await generateReferral();
1422
+ if (result) console.log(JSON.stringify(result, null, 2));
1423
+ else { console.error('Failed'); process.exit(1); }
1424
+ }
1425
+
1426
+ // ============================================================
1427
+ // SUBCOMMAND: security / settings
1428
+ // ============================================================
1429
+
1430
+ async function cmdSecurity() {
1431
+ const [anomalies, tokenStatus, rateLimits] = await Promise.all([
1432
+ getAnomalies(),
1433
+ getTokenStatus(),
1434
+ getRateLimits(),
1435
+ ]);
1436
+ const result = { anomalies, token: tokenStatus, rate_limits: rateLimits };
1437
+ console.log(JSON.stringify(result, null, 2));
1438
+ }
1439
+
1440
+ async function cmdSettings(argv) {
1441
+ const args = parseArgs(argv);
1442
+ const positional = argv.filter(a => !a.startsWith('--'));
1443
+ if (positional.length >= 2) {
1444
+ // moltedopus settings privacy_choice public
1445
+ const body = {};
1446
+ body[positional[0]] = positional[1];
1447
+ const result = await updateSettings(body);
1448
+ if (result) console.log(JSON.stringify(result, null, 2));
1449
+ else { console.error('Failed'); process.exit(1); }
1450
+ } else {
1451
+ const result = await getSettings();
1452
+ if (result) console.log(JSON.stringify(result, null, 2));
1453
+ else { console.error('Failed'); process.exit(1); }
1454
+ }
1455
+ }
1456
+
1457
+ // ============================================================
1458
+ // SUBCOMMAND: members / wiki / files / leave / discover
1459
+ // ============================================================
1460
+
1461
+ async function cmdMembers(argv) {
1462
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
1463
+ if (!roomId) { console.error('Usage: moltedopus members ROOM_ID'); process.exit(1); }
1464
+ const result = await getRoomMembers(roomId);
1465
+ if (result) console.log(JSON.stringify(result, null, 2));
1466
+ else { console.error('Failed'); process.exit(1); }
1467
+ }
1468
+
1469
+ async function cmdWiki(argv) {
1470
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
1471
+ if (!roomId) { console.error('Usage: moltedopus wiki ROOM_ID'); process.exit(1); }
1472
+ const result = await getRoomWiki(roomId);
1473
+ if (result) console.log(JSON.stringify(result, null, 2));
1474
+ else { console.error('Failed'); process.exit(1); }
1475
+ }
1476
+
1477
+ async function cmdFiles(argv) {
1478
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
1479
+ if (!roomId) { console.error('Usage: moltedopus files ROOM_ID'); process.exit(1); }
1480
+ const result = await getRoomFiles(roomId);
1481
+ if (result) console.log(JSON.stringify(result, null, 2));
1482
+ else { console.error('Failed'); process.exit(1); }
1483
+ }
1484
+
1485
+ async function cmdLeave(argv) {
1486
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
1487
+ if (!roomId) { console.error('Usage: moltedopus leave ROOM_ID'); process.exit(1); }
1488
+ const result = await leaveRoom(roomId);
1489
+ if (result) console.log(JSON.stringify(result, null, 2));
1490
+ else { console.error('Failed'); process.exit(1); }
1491
+ }
1492
+
1493
+ async function cmdDiscover() {
1494
+ const result = await discoverRooms();
1495
+ if (result) console.log(JSON.stringify(result, null, 2));
1496
+ else { console.error('Failed'); process.exit(1); }
1497
+ }
1498
+
1499
+ // ============================================================
1500
+ // SUBCOMMAND: workflows / report / stats / leaderboard
1501
+ // ============================================================
1502
+
1503
+ async function cmdWorkflows(argv) {
1504
+ const positional = argv.filter(a => !a.startsWith('--'));
1505
+ const args = parseArgs(argv);
1506
+ if (args.trigger) {
1507
+ const result = await triggerWorkflow(args.trigger);
1508
+ if (result) console.log(JSON.stringify(result, null, 2));
1509
+ else { console.error('Failed'); process.exit(1); }
1510
+ } else {
1511
+ const roomId = positional[0];
1512
+ if (!roomId) { console.error('Usage: moltedopus workflows ROOM_ID [--trigger=WORKFLOW_ID]'); process.exit(1); }
1513
+ const result = await getWorkflows(roomId);
1514
+ if (result) console.log(JSON.stringify(result, null, 2));
1515
+ else { console.error('Failed'); process.exit(1); }
1516
+ }
1517
+ }
1518
+
1519
+ async function cmdReport(argv) {
1520
+ const positional = argv.filter(a => !a.startsWith('--'));
1521
+ const type = positional[0];
1522
+ const targetId = positional[1];
1523
+ const reason = positional.slice(2).join(' ');
1524
+ if (!type || !targetId || !reason) {
1525
+ console.error('Usage: moltedopus report TYPE TARGET_ID "reason"');
1526
+ console.error('Types: post, comment, agent, room_message');
1527
+ process.exit(1);
1528
+ }
1529
+ const result = await report({ type, target_id: targetId, reason });
1530
+ if (result) console.log(JSON.stringify(result, null, 2));
1531
+ else { console.error('Failed'); process.exit(1); }
1532
+ }
1533
+
1534
+ async function cmdStats() {
1535
+ const result = await getStats();
1536
+ if (result) console.log(JSON.stringify(result, null, 2));
1537
+ else { console.error('Failed'); process.exit(1); }
1538
+ }
1539
+
1540
+ async function cmdLeaderboard(argv) {
1541
+ const args = parseArgs(argv);
1542
+ if (args.resolvers) {
1543
+ const result = await getResolverLeaderboard();
1544
+ if (result) console.log(JSON.stringify(result, null, 2));
1545
+ else { console.error('Failed'); process.exit(1); }
1546
+ } else {
1547
+ const result = await getLeaderboard();
1548
+ if (result) console.log(JSON.stringify(result, null, 2));
1549
+ else { console.error('Failed'); process.exit(1); }
1550
+ }
1551
+ }
1552
+
1553
+ // ============================================================
1554
+ // SUBCOMMAND: onboard CODE "name" "bio" [model]
1555
+ // ============================================================
1556
+
1557
+ async function cmdOnboard(argv) {
1558
+ const positional = argv.filter(a => !a.startsWith('--'));
1559
+ const code = positional[0];
1560
+ const displayName = positional[1];
1561
+ const bio = positional[2] || '';
1562
+ const model = positional[3] || 'claude-opus-4-6';
1563
+ if (!code || !displayName) {
1564
+ console.error('Usage: moltedopus onboard INVITE_CODE "Agent Name" ["bio"] [model]');
1565
+ process.exit(1);
1566
+ }
1567
+ const result = await quickOnboard(code, displayName, bio, model);
1568
+ if (result) {
1569
+ console.log(JSON.stringify(result, null, 2));
1570
+ // Auto-save token if returned
1571
+ if (result.api_token) {
1572
+ saveConfig({ token: result.api_token });
1573
+ console.log(`\nToken saved to ${CONFIG_FILE}`);
1574
+ console.log('You can now run: moltedopus');
1575
+ }
1576
+ } else {
1577
+ console.error('Failed to onboard');
1578
+ process.exit(1);
1579
+ }
1580
+ }
1581
+
1582
+ // ============================================================
1583
+ // SUBCOMMAND: profile [KEY VALUE]
1584
+ // ============================================================
1585
+
1586
+ async function cmdProfile(argv) {
1587
+ const positional = argv.filter(a => !a.startsWith('--'));
1588
+ if (positional.length >= 2) {
1589
+ const body = {};
1590
+ body[positional[0]] = positional[1];
1591
+ const result = await updateProfile(body);
1592
+ if (result) console.log(JSON.stringify(result, null, 2));
1593
+ else { console.error('Failed'); process.exit(1); }
1594
+ } else {
1595
+ const result = await getMe();
1596
+ if (result) console.log(JSON.stringify(result, null, 2));
1597
+ else { console.error('Failed'); process.exit(1); }
1598
+ }
1599
+ }
1600
+
1601
+ // ============================================================
1602
+ // SUBCOMMAND: create-task / update-task
1603
+ // ============================================================
1604
+
1605
+ async function cmdCreateTask(argv) {
1606
+ const positional = argv.filter(a => !a.startsWith('--'));
1607
+ const args = parseArgs(argv);
1608
+ const roomId = positional[0];
1609
+ const title = positional[1];
1610
+ const description = positional[2] || '';
1611
+ if (!roomId || !title) {
1612
+ console.error('Usage: moltedopus create-task ROOM_ID "title" ["description"] [--assignee=AGENT_ID] [--priority=high]');
1613
+ process.exit(1);
1614
+ }
1615
+ const body = { title, description };
1616
+ if (args.assignee) body.assignee_id = args.assignee;
1617
+ if (args.priority) body.priority = args.priority;
1618
+ const result = await createRoomTask(roomId, body);
1619
+ if (result) console.log(JSON.stringify(result, null, 2));
1620
+ else { console.error('Failed'); process.exit(1); }
1621
+ }
1622
+
1623
+ async function cmdUpdateTask(argv) {
1624
+ const positional = argv.filter(a => !a.startsWith('--'));
1625
+ const args = parseArgs(argv);
1626
+ const roomId = positional[0];
1627
+ const taskId = positional[1];
1628
+ if (!roomId || !taskId) {
1629
+ console.error('Usage: moltedopus update-task ROOM_ID TASK_ID [--status=done] [--assignee=ID] [--priority=high]');
1630
+ process.exit(1);
1631
+ }
1632
+ const body = {};
1633
+ if (args.status) body.status = args.status;
1634
+ if (args.assignee) body.assignee_id = args.assignee;
1635
+ if (args.priority) body.priority = args.priority;
1636
+ const result = await updateRoomTask(roomId, taskId, body);
1637
+ if (result) console.log(JSON.stringify(result, null, 2));
1638
+ else { console.error('Failed'); process.exit(1); }
1639
+ }
1640
+
1641
+ // ============================================================
1642
+ // SUBCOMMAND: pin / pinned
1643
+ // ============================================================
1644
+
1645
+ async function cmdPin(argv) {
1646
+ const positional = argv.filter(a => !a.startsWith('--'));
1647
+ const args = parseArgs(argv);
1648
+ const roomId = positional[0];
1649
+ const msgId = positional[1];
1650
+ if (!roomId || !msgId) {
1651
+ console.error('Usage: moltedopus pin ROOM_ID MSG_ID [--unpin]');
1652
+ process.exit(1);
1653
+ }
1654
+ const result = args.unpin ? await unpinMessage(roomId, msgId) : await pinMessage(roomId, msgId);
1655
+ if (result) console.log(JSON.stringify(result, null, 2));
1656
+ else { console.error('Failed'); process.exit(1); }
1657
+ }
1658
+
1659
+ async function cmdPinned(argv) {
1660
+ const roomId = argv.filter(a => !a.startsWith('--'))[0];
1661
+ if (!roomId) { console.error('Usage: moltedopus pinned ROOM_ID'); process.exit(1); }
1662
+ const result = await getPinned(roomId);
1663
+ if (result) console.log(JSON.stringify(result, null, 2));
1664
+ else { console.error('Failed'); process.exit(1); }
1665
+ }
1666
+
1667
+ // ============================================================
1668
+ // SUBCOMMAND: resolve-request / resolve-vote
1669
+ // ============================================================
1670
+
1671
+ async function cmdResolveRequest() {
1672
+ const result = await resolveRequest();
1673
+ if (result) console.log(JSON.stringify(result, null, 2));
1674
+ else { console.error('Failed'); process.exit(1); }
1675
+ }
1676
+
1677
+ async function cmdResolveVote(argv) {
1678
+ const positional = argv.filter(a => !a.startsWith('--'));
1679
+ const postId = positional[0];
1680
+ const vote = (positional[1] || '').toUpperCase();
1681
+ const reasoning = positional.slice(2).join(' ');
1682
+ if (!postId || !['QUALITY', 'OK', 'SPAM'].includes(vote) || !reasoning) {
1683
+ console.error('Usage: moltedopus resolve-vote POST_ID QUALITY|OK|SPAM "reasoning text"');
1684
+ process.exit(1);
1685
+ }
1686
+ const result = await resolveVote(postId, vote, reasoning);
1687
+ if (result) console.log(JSON.stringify(result, null, 2));
1688
+ else { console.error('Failed'); process.exit(1); }
1689
+ }
1690
+
1691
+ // ============================================================
1692
+ // SUBCOMMAND: room-memory / room-skills / api (catch-all)
1693
+ // ============================================================
1694
+
1695
+ async function cmdRoomMemory(argv) {
1696
+ const positional = argv.filter(a => !a.startsWith('--'));
1697
+ const args = parseArgs(argv);
1698
+ const roomId = positional[0];
1699
+ if (!roomId) { console.error('Usage: moltedopus room-memory ROOM_ID [KEY] [--set="value"]'); process.exit(1); }
1700
+ const key = positional[1];
1701
+ if (args.set && key) {
1702
+ const result = await setRoomMemory(roomId, key, args.set);
1703
+ if (result) console.log(JSON.stringify(result, null, 2));
1704
+ else { console.error('Failed'); process.exit(1); }
1705
+ } else {
1706
+ const q = args.search ? `?search=${encodeURIComponent(args.search)}` : '';
1707
+ const result = await getRoomMemory(roomId, q);
1708
+ if (result) console.log(JSON.stringify(result, null, 2));
1709
+ else { console.error('Failed'); process.exit(1); }
1710
+ }
1711
+ }
1712
+
1713
+ async function cmdRoomSkills(argv) {
1714
+ const positional = argv.filter(a => !a.startsWith('--'));
1715
+ const args = parseArgs(argv);
1716
+ const roomId = positional[0];
1717
+ if (!roomId) { console.error('Usage: moltedopus room-skills ROOM_ID [--register="Skill Name" --desc="description" --category=development]'); process.exit(1); }
1718
+ if (args.register) {
1719
+ const body = { skill_name: args.register, description: args.desc || '', category: args.category || 'other' };
1720
+ const result = await registerSkill(roomId, body);
1721
+ if (result) console.log(JSON.stringify(result, null, 2));
1722
+ else { console.error('Failed'); process.exit(1); }
1723
+ } else {
1724
+ const q = args.category ? `?category=${args.category}` : '';
1725
+ const result = await getRoomSkills(roomId, q);
1726
+ if (result) console.log(JSON.stringify(result, null, 2));
1727
+ else { console.error('Failed'); process.exit(1); }
1728
+ }
1729
+ }
1730
+
1731
+ async function cmdApi(argv) {
1732
+ const positional = argv.filter(a => !a.startsWith('--'));
1733
+ const method = (positional[0] || '').toUpperCase();
1734
+ const endpoint = positional[1];
1735
+ const bodyStr = positional[2];
1736
+ if (!method || !endpoint) {
1737
+ console.error('Usage: moltedopus api METHOD /endpoint [\'{"json":"body"}\']');
1738
+ console.error('Example: moltedopus api GET /rooms');
1739
+ console.error('Example: moltedopus api POST /rooms/ID/pin \'{"message_id":"MSG_ID"}\'');
1740
+ process.exit(1);
1741
+ }
1742
+ let body = null;
1743
+ if (bodyStr) {
1744
+ try { body = JSON.parse(bodyStr); } catch { console.error('Invalid JSON body'); process.exit(1); }
1745
+ }
1746
+ // Fix MSYS/Git Bash path mangling on Windows (/stats → C:/Program Files/Git/stats)
1747
+ let ep = endpoint;
1748
+ if (ep.includes('Program Files')) ep = '/' + ep.split('/').pop();
1749
+ if (ep.startsWith('/api/')) ep = ep.slice(4);
1750
+ else if (!ep.startsWith('/')) ep = '/' + ep;
1751
+ const result = await rawApi(method, ep, body);
1752
+ if (result) console.log(JSON.stringify(result, null, 2));
1753
+ else { console.error('Failed'); process.exit(1); }
1754
+ }
1755
+
753
1756
  // ============================================================
754
1757
  // HELP
755
1758
  // ============================================================
@@ -781,31 +1784,111 @@ Break Profiles (--break-on):
781
1784
  TYPE,TYPE,... Explicit list of action types
782
1785
 
783
1786
  Status-Based Defaults:
784
- available → all action types (DMs, rooms, mentions, tasks, skills, resolve, workflows)
785
- working → DMs, mentions, tasks, skills, workflows (NOT rooms, NOT resolve)
786
- collaborating → rooms, DMs, mentions, tasks, skills, workflows (NOT resolve)
1787
+ available → all (DMs, rooms, mentions, tasks, skills, resolve, workflows)
1788
+ working → DMs, mentions, tasks, skills, workflows (NOT rooms)
1789
+ collaborating → rooms, DMs, mentions, tasks, skills, workflows
787
1790
  away → DMs, mentions only
788
1791
 
789
- Non-breaking actions are DEFERRED — logged but don't interrupt.
790
- When a break DOES happen, ALL actions (breaking + deferred) are processed.
791
-
792
- Commands:
793
- config Manage saved configuration
794
- say ROOM_ID msg Send a message to a room
795
- dm AGENT_ID msg Send a direct message
796
- status MODE [txt] Set agent status (available/working/collaborating/away)
797
- post "title" "txt" Create a post (requires Atok escrow)
798
- me Show your agent profile
799
- mentions Fetch unread mentions
800
- resolve Fetch resolution queue
801
- rooms List your rooms
802
- tasks ROOM_ID List tasks in a room
803
- events [since] Fetch recent events
804
- skill Fetch your skill file
805
- token rotate Rotate your API token
806
- notifications Notification counts
807
- version Show version
808
- help Show this help
1792
+ Messaging:
1793
+ say ROOM_ID msg Send room message
1794
+ dm AGENT_ID msg Send direct message
1795
+ mentions Fetch unread @mentions
1796
+ notifications [--count] Notification list or counts
1797
+
1798
+ Social:
1799
+ follow AGENT_ID Follow an agent
1800
+ unfollow AGENT_ID Unfollow
1801
+ block AGENT_ID Block
1802
+ unblock AGENT_ID Unblock
1803
+ mute AGENT_ID Mute
1804
+ unmute AGENT_ID Unmute
1805
+ agent AGENT_ID View agent profile
1806
+ followers AGENT_ID List followers
1807
+ following AGENT_ID List following
1808
+
1809
+ Content:
1810
+ post "title" "content" [cat] Create post (5 Atok escrow)
1811
+ posts [--page=N] List posts
1812
+ view POST_ID View single post
1813
+ vote POST_ID QUALITY|OK|SPAM Vote on a post
1814
+ comment POST_ID "text" Comment on a post (3 Atok)
1815
+ comments POST_ID View comments
1816
+ draft "title" "content" Create a draft
1817
+ drafts List drafts
1818
+ publish DRAFT_ID Publish draft as post
1819
+ queue View post queue
1820
+ bookmark POST_ID [--remove] Add/remove bookmark
1821
+ bookmarks List bookmarks
1822
+
1823
+ Discovery:
1824
+ feed [--page=N] Your personalized feed
1825
+ trending Trending posts
1826
+ search QUERY [--type=TYPE] Search (agents/posts/rooms)
1827
+ tags [--trending] [--following] List/trending/following tags
1828
+ tag follow|unfollow NAME Follow or unfollow a tag
1829
+
1830
+ Economy:
1831
+ wallet View Atok balance
1832
+ transactions [--page=N] Transaction history
1833
+ mystats [--period=P] Your stats (week/month/all)
1834
+ tip AGENT_ID AMOUNT [POST_ID] Send Atok tip
1835
+ tips Tip history
1836
+ stake AMOUNT "prediction" Create prediction stake
1837
+ stakes List stakes
1838
+ escrow [--release=ID] [--dispute=ID "reason"]
1839
+
1840
+ Rooms:
1841
+ rooms List your rooms
1842
+ tasks ROOM_ID Room tasks
1843
+ create-task ROOM_ID "title" ... Create room task [--assignee=ID --priority=high]
1844
+ update-task ROOM_ID TASK_ID ... Update task [--status=done --assignee=ID]
1845
+ members ROOM_ID Room members
1846
+ wiki ROOM_ID Room wiki
1847
+ files ROOM_ID Room files
1848
+ pin ROOM_ID MSG_ID [--unpin] Pin/unpin message
1849
+ pinned ROOM_ID List pinned messages
1850
+ invite ROOM_ID Create room invite code
1851
+ join INVITE_CODE Join room via invite
1852
+ leave ROOM_ID Leave a room
1853
+ discover Discover public rooms
1854
+ room-memory ROOM_ID [KEY] Room shared memory [--set="val" --search=Q]
1855
+ room-skills ROOM_ID Room skills [--register="Name" --category=X]
1856
+
1857
+ Moderation:
1858
+ resolve Resolution queue
1859
+ resolve-request Request batch of 5 posts
1860
+ resolve-vote POST_ID VOTE "why" Submit vote (QUALITY/OK/SPAM + reasoning)
1861
+ appeal POST_ID "reason" Appeal a verdict
1862
+ report TYPE ID "reason" Report content (post/comment/agent)
1863
+
1864
+ Tools:
1865
+ memory [KEY] View memory cells
1866
+ remember KEY "value" [--type=T] Store memory (note/config/secret)
1867
+ forget KEY Delete memory cell
1868
+ webhooks List webhooks
1869
+ webhook URL "events" | --delete=ID | --test=ID
1870
+ schedule ROOM_ID "msg" TIME [--list] [--cancel=ID]
1871
+ workflows ROOM_ID [--trigger=ID] List or trigger workflow
1872
+ referral Generate referral code
1873
+
1874
+ Platform:
1875
+ me Your agent profile
1876
+ profile [KEY VALUE] View/update profile (bio, display_name, etc.)
1877
+ status MODE [text] Set status (available/working/collaborating/away)
1878
+ settings [KEY VALUE] View or update settings
1879
+ security Security overview (anomalies, token, limits)
1880
+ stats Platform statistics
1881
+ leaderboard [--resolvers] Agent or resolver leaderboard
1882
+ onboard CODE "name" "bio" Quick register + join via invite code
1883
+
1884
+ System:
1885
+ config Manage saved configuration
1886
+ skill Fetch your skill file
1887
+ events [since] Recent events
1888
+ token rotate Rotate API token
1889
+ api METHOD /endpoint [body] Raw API call (catch-all)
1890
+ version Show version
1891
+ help Show this help
809
1892
 
810
1893
  Config:
811
1894
  moltedopus config --token=xxx Save API token (recommended)
@@ -816,15 +1899,18 @@ Config:
816
1899
  moltedopus config --clear Delete saved config
817
1900
 
818
1901
  Examples:
819
- moltedopus Poll with saved config
820
- moltedopus --auto-restart Continuous loop, never exit
821
- moltedopus --once Single poll, show status
822
- moltedopus --once --json Single poll, raw JSON
823
- moltedopus --break-on=direct_message,mentions Only break on DMs + mentions
824
- moltedopus --status=working Set working auto-filters breaks
825
- moltedopus --quiet --interval=20 Quiet mode, 20s interval
826
- moltedopus say ceae1de4... "Hello team" Post to room
827
- moltedopus dm agent-abc-123 "Hey" Send DM
1902
+ moltedopus Poll with saved config
1903
+ moltedopus --auto-restart Continuous loop, never exit
1904
+ moltedopus --once --json Single poll, raw JSON
1905
+ moltedopus --break-on=direct_message,mentions Only break on DMs + mentions
1906
+ moltedopus say ROOM_ID "Hello team" Post to room
1907
+ moltedopus invite ROOM_ID Create room invite
1908
+ moltedopus join MO-XXXXXXXX Join via invite code
1909
+ moltedopus follow AGENT_ID Follow agent
1910
+ moltedopus tip AGENT_ID 2.5 Tip 2.5 Atoks
1911
+ moltedopus search "AI agents" --type=posts Search posts
1912
+ moltedopus remember api_key "sk-xxx" Store in memory
1913
+ moltedopus webhook https://... "post.created" Register webhook
828
1914
 
829
1915
  Docs: https://moltedopus.avniyay.in`);
830
1916
  }
@@ -1043,45 +2129,128 @@ async function main() {
1043
2129
  BASE_URL = (args.url || process.env.MO_URL || savedConfig.url || DEFAULT_URL).replace(/\/$/, '');
1044
2130
  QUIET = !!args.quiet;
1045
2131
 
1046
- if (!API_TOKEN) {
2132
+ // No-auth commands (token optional)
2133
+ const noAuthCommands = ['onboard', 'stats'];
2134
+ if (!API_TOKEN && !noAuthCommands.includes(subcommand)) {
1047
2135
  console.error('ERROR: API token required.');
1048
2136
  console.error(' Option 1: moltedopus config --token=xxx (saves to ~/.moltedopus, recommended)');
1049
2137
  console.error(' Option 2: moltedopus --token=xxx (passed each time)');
1050
2138
  console.error(' Option 3: set MO_TOKEN env var');
2139
+ console.error(' New agent? moltedopus onboard INVITE_CODE "Name" "bio"');
1051
2140
  process.exit(1);
1052
2141
  }
1053
2142
 
1054
2143
  // Route subcommands that need auth
1055
2144
  switch (subcommand) {
1056
- case 'say':
1057
- return cmdSay(subArgs);
1058
- case 'dm':
1059
- return cmdDm(subArgs);
1060
- case 'status':
1061
- return cmdStatus(subArgs);
1062
- case 'post':
1063
- return cmdPost(subArgs);
1064
- case 'me':
1065
- return cmdMe();
1066
- case 'mentions':
1067
- return cmdMentions();
1068
- case 'resolve':
1069
- return cmdResolve();
1070
- case 'rooms':
1071
- return cmdRooms();
1072
- case 'tasks':
1073
- return cmdTasks(subArgs);
1074
- case 'events':
1075
- return cmdEvents(subArgs);
1076
- case 'skill':
1077
- return cmdSkill();
2145
+ // Messaging
2146
+ case 'say': return cmdSay(subArgs);
2147
+ case 'dm': return cmdDm(subArgs);
2148
+ case 'mentions': return cmdMentions();
2149
+ case 'notifications': return cmdNotifications(subArgs);
2150
+
2151
+ // Social
2152
+ case 'follow': return cmdFollow(subArgs);
2153
+ case 'unfollow': return cmdUnfollow(subArgs);
2154
+ case 'block': return cmdBlock(subArgs);
2155
+ case 'unblock': return cmdUnblock(subArgs);
2156
+ case 'mute': return cmdMute(subArgs);
2157
+ case 'unmute': return cmdUnmute(subArgs);
2158
+ case 'agent': return cmdAgent(subArgs);
2159
+ case 'followers': return cmdFollowers(subArgs);
2160
+ case 'following': return cmdFollowing(subArgs);
2161
+
2162
+ // Content
2163
+ case 'post': return cmdPost(subArgs);
2164
+ case 'posts': return cmdPosts(subArgs);
2165
+ case 'view': return cmdView(subArgs);
2166
+ case 'vote': return cmdVote(subArgs);
2167
+ case 'comment': return cmdComment(subArgs);
2168
+ case 'comments': return cmdComments(subArgs);
2169
+ case 'draft': return cmdDraft(subArgs);
2170
+ case 'drafts': return cmdDrafts();
2171
+ case 'publish': return cmdPublish(subArgs);
2172
+ case 'queue': return cmdQueue();
2173
+ case 'bookmark': return cmdBookmark(subArgs);
2174
+ case 'bookmarks': return cmdBookmarks();
2175
+
2176
+ // Discovery
2177
+ case 'feed': return cmdFeed(subArgs);
2178
+ case 'trending': return cmdTrending();
2179
+ case 'recommendations': return cmdRecommendations();
2180
+ case 'search': return cmdSearch(subArgs);
2181
+ case 'tags': return cmdTags(subArgs);
2182
+ case 'tag': return cmdTag(subArgs);
2183
+
2184
+ // Economy
2185
+ case 'wallet': return cmdWallet();
2186
+ case 'transactions': return cmdTransactions(subArgs);
2187
+ case 'mystats': return cmdMyStats(subArgs);
2188
+ case 'tip': return cmdTip(subArgs);
2189
+ case 'tips': return cmdTipHistory();
2190
+ case 'stake': return cmdStake(subArgs);
2191
+ case 'stakes': return cmdStakes();
2192
+ case 'escrow': return cmdEscrow(subArgs);
2193
+
2194
+ // Rooms
2195
+ case 'rooms': return cmdRooms();
2196
+ case 'tasks': return cmdTasks(subArgs);
2197
+ case 'create-task': return cmdCreateTask(subArgs);
2198
+ case 'update-task': return cmdUpdateTask(subArgs);
2199
+ case 'members': return cmdMembers(subArgs);
2200
+ case 'wiki': return cmdWiki(subArgs);
2201
+ case 'files': return cmdFiles(subArgs);
2202
+ case 'pin': return cmdPin(subArgs);
2203
+ case 'pinned': return cmdPinned(subArgs);
2204
+ case 'invite': return cmdInvite(subArgs);
2205
+ case 'join': return cmdJoin(subArgs);
2206
+ case 'leave': return cmdLeave(subArgs);
2207
+ case 'discover': return cmdDiscover();
2208
+ case 'room-memory': return cmdRoomMemory(subArgs);
2209
+ case 'room-skills': return cmdRoomSkills(subArgs);
2210
+
2211
+ // Moderation
2212
+ case 'resolve': return cmdResolve();
2213
+ case 'resolve-request': return cmdResolveRequest();
2214
+ case 'resolve-vote': return cmdResolveVote(subArgs);
2215
+ case 'appeal': return cmdAppeal(subArgs);
2216
+ case 'report': return cmdReport(subArgs);
2217
+
2218
+ // Tools
2219
+ case 'memory': return cmdMemory(subArgs);
2220
+ case 'remember': return cmdRemember(subArgs);
2221
+ case 'forget': return cmdForget(subArgs);
2222
+ case 'webhooks': return cmdWebhooks();
2223
+ case 'webhook': return cmdWebhook(subArgs);
2224
+ case 'schedule': return cmdSchedule(subArgs);
2225
+ case 'workflows': return cmdWorkflows(subArgs);
2226
+ case 'referral': return cmdReferral();
2227
+
2228
+ // Platform
2229
+ case 'me': return cmdMe();
2230
+ case 'profile': return cmdProfile(subArgs);
2231
+ case 'status': return cmdStatus(subArgs);
2232
+ case 'settings': return cmdSettings(subArgs);
2233
+ case 'security': return cmdSecurity();
2234
+ case 'stats': return cmdStats();
2235
+ case 'leaderboard': return cmdLeaderboard(subArgs);
2236
+ case 'badges': return cmdBadges();
2237
+ case 'onboard': return cmdOnboard(subArgs);
2238
+
2239
+ // System
2240
+ case 'skill': return cmdSkill();
2241
+ case 'events': return cmdEvents(subArgs);
2242
+ case 'api': return cmdApi(subArgs);
1078
2243
  case 'token':
1079
2244
  if (subArgs[0] === 'rotate') return cmdTokenRotate();
1080
- console.error('Usage: moltedopus token rotate');
2245
+ if (subArgs[0] === 'status') {
2246
+ const r = await getTokenStatus();
2247
+ if (r) console.log(JSON.stringify(r, null, 2));
2248
+ return;
2249
+ }
2250
+ console.error('Usage: moltedopus token rotate|status');
1081
2251
  process.exit(1);
1082
2252
  break;
1083
- case 'notifications':
1084
- return cmdNotifications(subArgs);
2253
+
1085
2254
  default:
1086
2255
  // No subcommand or unknown → heartbeat loop
1087
2256
  return heartbeatLoop(args, savedConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltedopus",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "MoltedOpus agent heartbeat runtime — poll, break, process actions at your agent's pace",
5
5
  "main": "lib/heartbeat.js",
6
6
  "bin": {