@fluxra-ai/fluxra-cli 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 (265) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +274 -0
  3. package/bin/fluxra +6 -0
  4. package/dist/cli/command-context.d.ts +17 -0
  5. package/dist/cli/command-context.d.ts.map +1 -0
  6. package/dist/cli/command-context.js +26 -0
  7. package/dist/cli/command-context.js.map +1 -0
  8. package/dist/cli/commands/auth/index.d.ts +3 -0
  9. package/dist/cli/commands/auth/index.d.ts.map +1 -0
  10. package/dist/cli/commands/auth/index.js +165 -0
  11. package/dist/cli/commands/auth/index.js.map +1 -0
  12. package/dist/cli/commands/chat/index.d.ts +3 -0
  13. package/dist/cli/commands/chat/index.d.ts.map +1 -0
  14. package/dist/cli/commands/chat/index.js +1201 -0
  15. package/dist/cli/commands/chat/index.js.map +1 -0
  16. package/dist/cli/commands/config/index.d.ts +3 -0
  17. package/dist/cli/commands/config/index.d.ts.map +1 -0
  18. package/dist/cli/commands/config/index.js +66 -0
  19. package/dist/cli/commands/config/index.js.map +1 -0
  20. package/dist/cli/commands/help.d.ts +7 -0
  21. package/dist/cli/commands/help.d.ts.map +1 -0
  22. package/dist/cli/commands/help.js +106 -0
  23. package/dist/cli/commands/help.js.map +1 -0
  24. package/dist/cli/commands/local/doctor.d.ts +26 -0
  25. package/dist/cli/commands/local/doctor.d.ts.map +1 -0
  26. package/dist/cli/commands/local/doctor.js +265 -0
  27. package/dist/cli/commands/local/doctor.js.map +1 -0
  28. package/dist/cli/commands/local/export.d.ts +41 -0
  29. package/dist/cli/commands/local/export.d.ts.map +1 -0
  30. package/dist/cli/commands/local/export.js +83 -0
  31. package/dist/cli/commands/local/export.js.map +1 -0
  32. package/dist/cli/commands/local/index.d.ts +6 -0
  33. package/dist/cli/commands/local/index.d.ts.map +1 -0
  34. package/dist/cli/commands/local/index.js +116 -0
  35. package/dist/cli/commands/local/index.js.map +1 -0
  36. package/dist/cli/commands/local/inspect.d.ts +42 -0
  37. package/dist/cli/commands/local/inspect.d.ts.map +1 -0
  38. package/dist/cli/commands/local/inspect.js +125 -0
  39. package/dist/cli/commands/local/inspect.js.map +1 -0
  40. package/dist/cli/commands/mcp.d.ts +8 -0
  41. package/dist/cli/commands/mcp.d.ts.map +1 -0
  42. package/dist/cli/commands/mcp.js +253 -0
  43. package/dist/cli/commands/mcp.js.map +1 -0
  44. package/dist/cli/commands/profile/index.d.ts +3 -0
  45. package/dist/cli/commands/profile/index.d.ts.map +1 -0
  46. package/dist/cli/commands/profile/index.js +114 -0
  47. package/dist/cli/commands/profile/index.js.map +1 -0
  48. package/dist/cli/commands/schema.d.ts +7 -0
  49. package/dist/cli/commands/schema.d.ts.map +1 -0
  50. package/dist/cli/commands/schema.js +33 -0
  51. package/dist/cli/commands/schema.js.map +1 -0
  52. package/dist/cli/errors.d.ts +16 -0
  53. package/dist/cli/errors.d.ts.map +1 -0
  54. package/dist/cli/errors.js +15 -0
  55. package/dist/cli/errors.js.map +1 -0
  56. package/dist/cli/fluxra.d.ts +9 -0
  57. package/dist/cli/fluxra.d.ts.map +1 -0
  58. package/dist/cli/fluxra.js +55 -0
  59. package/dist/cli/fluxra.js.map +1 -0
  60. package/dist/cli/helpers.d.ts +13 -0
  61. package/dist/cli/helpers.d.ts.map +1 -0
  62. package/dist/cli/helpers.js +32 -0
  63. package/dist/cli/helpers.js.map +1 -0
  64. package/dist/cli/output.d.ts +14 -0
  65. package/dist/cli/output.d.ts.map +1 -0
  66. package/dist/cli/output.js +55 -0
  67. package/dist/cli/output.js.map +1 -0
  68. package/dist/cli/version.d.ts +6 -0
  69. package/dist/cli/version.d.ts.map +1 -0
  70. package/dist/cli/version.js +8 -0
  71. package/dist/cli/version.js.map +1 -0
  72. package/dist/core/auth/auth-service.d.ts +35 -0
  73. package/dist/core/auth/auth-service.d.ts.map +1 -0
  74. package/dist/core/auth/auth-service.js +116 -0
  75. package/dist/core/auth/auth-service.js.map +1 -0
  76. package/dist/core/config/global-config.d.ts +38 -0
  77. package/dist/core/config/global-config.d.ts.map +1 -0
  78. package/dist/core/config/global-config.js +75 -0
  79. package/dist/core/config/global-config.js.map +1 -0
  80. package/dist/core/errors/error-model.d.ts +48 -0
  81. package/dist/core/errors/error-model.d.ts.map +1 -0
  82. package/dist/core/errors/error-model.js +152 -0
  83. package/dist/core/errors/error-model.js.map +1 -0
  84. package/dist/core/filesystem/paths.d.ts +45 -0
  85. package/dist/core/filesystem/paths.d.ts.map +1 -0
  86. package/dist/core/filesystem/paths.js +77 -0
  87. package/dist/core/filesystem/paths.js.map +1 -0
  88. package/dist/core/http/auth-api.d.ts +71 -0
  89. package/dist/core/http/auth-api.d.ts.map +1 -0
  90. package/dist/core/http/auth-api.js +91 -0
  91. package/dist/core/http/auth-api.js.map +1 -0
  92. package/dist/core/http/block-api.d.ts +37 -0
  93. package/dist/core/http/block-api.d.ts.map +1 -0
  94. package/dist/core/http/block-api.js +36 -0
  95. package/dist/core/http/block-api.js.map +1 -0
  96. package/dist/core/http/chat-api.d.ts +41 -0
  97. package/dist/core/http/chat-api.d.ts.map +1 -0
  98. package/dist/core/http/chat-api.js +88 -0
  99. package/dist/core/http/chat-api.js.map +1 -0
  100. package/dist/core/http/conversation-management-api.d.ts +65 -0
  101. package/dist/core/http/conversation-management-api.d.ts.map +1 -0
  102. package/dist/core/http/conversation-management-api.js +59 -0
  103. package/dist/core/http/conversation-management-api.js.map +1 -0
  104. package/dist/core/http/directory-api.d.ts +32 -0
  105. package/dist/core/http/directory-api.d.ts.map +1 -0
  106. package/dist/core/http/directory-api.js +36 -0
  107. package/dist/core/http/directory-api.js.map +1 -0
  108. package/dist/core/http/directory-profile-api.d.ts +32 -0
  109. package/dist/core/http/directory-profile-api.d.ts.map +1 -0
  110. package/dist/core/http/directory-profile-api.js +39 -0
  111. package/dist/core/http/directory-profile-api.js.map +1 -0
  112. package/dist/core/http/http-client.d.ts +41 -0
  113. package/dist/core/http/http-client.d.ts.map +1 -0
  114. package/dist/core/http/http-client.js +127 -0
  115. package/dist/core/http/http-client.js.map +1 -0
  116. package/dist/core/http/message-api.d.ts +55 -0
  117. package/dist/core/http/message-api.d.ts.map +1 -0
  118. package/dist/core/http/message-api.js +64 -0
  119. package/dist/core/http/message-api.js.map +1 -0
  120. package/dist/core/http/rate-limit.d.ts +22 -0
  121. package/dist/core/http/rate-limit.d.ts.map +1 -0
  122. package/dist/core/http/rate-limit.js +66 -0
  123. package/dist/core/http/rate-limit.js.map +1 -0
  124. package/dist/core/http/token-lifecycle.d.ts +18 -0
  125. package/dist/core/http/token-lifecycle.d.ts.map +1 -0
  126. package/dist/core/http/token-lifecycle.js +73 -0
  127. package/dist/core/http/token-lifecycle.js.map +1 -0
  128. package/dist/core/locking/profile-lock.d.ts +32 -0
  129. package/dist/core/locking/profile-lock.d.ts.map +1 -0
  130. package/dist/core/locking/profile-lock.js +104 -0
  131. package/dist/core/locking/profile-lock.js.map +1 -0
  132. package/dist/core/profiles/profile-service.d.ts +35 -0
  133. package/dist/core/profiles/profile-service.d.ts.map +1 -0
  134. package/dist/core/profiles/profile-service.js +119 -0
  135. package/dist/core/profiles/profile-service.js.map +1 -0
  136. package/dist/core/profiles/profile-types.d.ts +28 -0
  137. package/dist/core/profiles/profile-types.d.ts.map +1 -0
  138. package/dist/core/profiles/profile-types.js +13 -0
  139. package/dist/core/profiles/profile-types.js.map +1 -0
  140. package/dist/core/secrets/secrets-service.d.ts +25 -0
  141. package/dist/core/secrets/secrets-service.d.ts.map +1 -0
  142. package/dist/core/secrets/secrets-service.js +67 -0
  143. package/dist/core/secrets/secrets-service.js.map +1 -0
  144. package/dist/core/secrets/secrets-types.d.ts +29 -0
  145. package/dist/core/secrets/secrets-types.d.ts.map +1 -0
  146. package/dist/core/secrets/secrets-types.js +33 -0
  147. package/dist/core/secrets/secrets-types.js.map +1 -0
  148. package/dist/core/sqlite/chat-schema.d.ts +14 -0
  149. package/dist/core/sqlite/chat-schema.d.ts.map +1 -0
  150. package/dist/core/sqlite/chat-schema.js +172 -0
  151. package/dist/core/sqlite/chat-schema.js.map +1 -0
  152. package/dist/core/sqlite/core-schema.d.ts +14 -0
  153. package/dist/core/sqlite/core-schema.d.ts.map +1 -0
  154. package/dist/core/sqlite/core-schema.js +54 -0
  155. package/dist/core/sqlite/core-schema.js.map +1 -0
  156. package/dist/core/sqlite/database.d.ts +40 -0
  157. package/dist/core/sqlite/database.d.ts.map +1 -0
  158. package/dist/core/sqlite/database.js +68 -0
  159. package/dist/core/sqlite/database.js.map +1 -0
  160. package/dist/core/sqlite/migrations.d.ts +22 -0
  161. package/dist/core/sqlite/migrations.d.ts.map +1 -0
  162. package/dist/core/sqlite/migrations.js +64 -0
  163. package/dist/core/sqlite/migrations.js.map +1 -0
  164. package/dist/modules/chat/inbox/conversation-service.d.ts +35 -0
  165. package/dist/modules/chat/inbox/conversation-service.d.ts.map +1 -0
  166. package/dist/modules/chat/inbox/conversation-service.js +54 -0
  167. package/dist/modules/chat/inbox/conversation-service.js.map +1 -0
  168. package/dist/modules/chat/inbox/history-service.d.ts +25 -0
  169. package/dist/modules/chat/inbox/history-service.d.ts.map +1 -0
  170. package/dist/modules/chat/inbox/history-service.js +57 -0
  171. package/dist/modules/chat/inbox/history-service.js.map +1 -0
  172. package/dist/modules/chat/inbox/index.d.ts +9 -0
  173. package/dist/modules/chat/inbox/index.d.ts.map +1 -0
  174. package/dist/modules/chat/inbox/index.js +9 -0
  175. package/dist/modules/chat/inbox/index.js.map +1 -0
  176. package/dist/modules/chat/inbox/read-service.d.ts +36 -0
  177. package/dist/modules/chat/inbox/read-service.d.ts.map +1 -0
  178. package/dist/modules/chat/inbox/read-service.js +91 -0
  179. package/dist/modules/chat/inbox/read-service.js.map +1 -0
  180. package/dist/modules/chat/inbox/search-service.d.ts +20 -0
  181. package/dist/modules/chat/inbox/search-service.d.ts.map +1 -0
  182. package/dist/modules/chat/inbox/search-service.js +23 -0
  183. package/dist/modules/chat/inbox/search-service.js.map +1 -0
  184. package/dist/modules/chat/inbox/unread-service.d.ts +38 -0
  185. package/dist/modules/chat/inbox/unread-service.d.ts.map +1 -0
  186. package/dist/modules/chat/inbox/unread-service.js +65 -0
  187. package/dist/modules/chat/inbox/unread-service.js.map +1 -0
  188. package/dist/modules/chat/render/message-render.d.ts +35 -0
  189. package/dist/modules/chat/render/message-render.d.ts.map +1 -0
  190. package/dist/modules/chat/render/message-render.js +129 -0
  191. package/dist/modules/chat/render/message-render.js.map +1 -0
  192. package/dist/modules/chat/send/conversation-service.d.ts +53 -0
  193. package/dist/modules/chat/send/conversation-service.d.ts.map +1 -0
  194. package/dist/modules/chat/send/conversation-service.js +110 -0
  195. package/dist/modules/chat/send/conversation-service.js.map +1 -0
  196. package/dist/modules/chat/send/directory-cache-service.d.ts +37 -0
  197. package/dist/modules/chat/send/directory-cache-service.d.ts.map +1 -0
  198. package/dist/modules/chat/send/directory-cache-service.js +49 -0
  199. package/dist/modules/chat/send/directory-cache-service.js.map +1 -0
  200. package/dist/modules/chat/send/send-service.d.ts +36 -0
  201. package/dist/modules/chat/send/send-service.d.ts.map +1 -0
  202. package/dist/modules/chat/send/send-service.js +113 -0
  203. package/dist/modules/chat/send/send-service.js.map +1 -0
  204. package/dist/modules/chat/store/conversation-repo.d.ts +53 -0
  205. package/dist/modules/chat/store/conversation-repo.d.ts.map +1 -0
  206. package/dist/modules/chat/store/conversation-repo.js +75 -0
  207. package/dist/modules/chat/store/conversation-repo.js.map +1 -0
  208. package/dist/modules/chat/store/directory-cache-repo.d.ts +41 -0
  209. package/dist/modules/chat/store/directory-cache-repo.d.ts.map +1 -0
  210. package/dist/modules/chat/store/directory-cache-repo.js +64 -0
  211. package/dist/modules/chat/store/directory-cache-repo.js.map +1 -0
  212. package/dist/modules/chat/store/job-repo.d.ts +72 -0
  213. package/dist/modules/chat/store/job-repo.d.ts.map +1 -0
  214. package/dist/modules/chat/store/job-repo.js +140 -0
  215. package/dist/modules/chat/store/job-repo.js.map +1 -0
  216. package/dist/modules/chat/store/message-repo.d.ts +98 -0
  217. package/dist/modules/chat/store/message-repo.d.ts.map +1 -0
  218. package/dist/modules/chat/store/message-repo.js +231 -0
  219. package/dist/modules/chat/store/message-repo.js.map +1 -0
  220. package/dist/modules/chat/store/outbox-repo.d.ts +73 -0
  221. package/dist/modules/chat/store/outbox-repo.d.ts.map +1 -0
  222. package/dist/modules/chat/store/outbox-repo.js +112 -0
  223. package/dist/modules/chat/store/outbox-repo.js.map +1 -0
  224. package/dist/modules/chat/store/read-state-repo.d.ts +83 -0
  225. package/dist/modules/chat/store/read-state-repo.d.ts.map +1 -0
  226. package/dist/modules/chat/store/read-state-repo.js +210 -0
  227. package/dist/modules/chat/store/read-state-repo.js.map +1 -0
  228. package/dist/modules/chat/store/sync-state-repo.d.ts +45 -0
  229. package/dist/modules/chat/store/sync-state-repo.d.ts.map +1 -0
  230. package/dist/modules/chat/store/sync-state-repo.js +67 -0
  231. package/dist/modules/chat/store/sync-state-repo.js.map +1 -0
  232. package/dist/modules/chat/sync/backfill.d.ts +13 -0
  233. package/dist/modules/chat/sync/backfill.d.ts.map +1 -0
  234. package/dist/modules/chat/sync/backfill.js +37 -0
  235. package/dist/modules/chat/sync/backfill.js.map +1 -0
  236. package/dist/modules/chat/sync/cron-manager.d.ts +50 -0
  237. package/dist/modules/chat/sync/cron-manager.d.ts.map +1 -0
  238. package/dist/modules/chat/sync/cron-manager.js +164 -0
  239. package/dist/modules/chat/sync/cron-manager.js.map +1 -0
  240. package/dist/modules/chat/sync/index.d.ts +8 -0
  241. package/dist/modules/chat/sync/index.d.ts.map +1 -0
  242. package/dist/modules/chat/sync/index.js +7 -0
  243. package/dist/modules/chat/sync/index.js.map +1 -0
  244. package/dist/modules/chat/sync/job-logger.d.ts +44 -0
  245. package/dist/modules/chat/sync/job-logger.d.ts.map +1 -0
  246. package/dist/modules/chat/sync/job-logger.js +139 -0
  247. package/dist/modules/chat/sync/job-logger.js.map +1 -0
  248. package/dist/modules/chat/sync/sync-service.d.ts +14 -0
  249. package/dist/modules/chat/sync/sync-service.d.ts.map +1 -0
  250. package/dist/modules/chat/sync/sync-service.js +174 -0
  251. package/dist/modules/chat/sync/sync-service.js.map +1 -0
  252. package/dist/modules/chat/sync/sync-status.d.ts +14 -0
  253. package/dist/modules/chat/sync/sync-status.d.ts.map +1 -0
  254. package/dist/modules/chat/sync/sync-status.js +77 -0
  255. package/dist/modules/chat/sync/sync-status.js.map +1 -0
  256. package/dist/modules/chat/sync/sync-types.d.ts +80 -0
  257. package/dist/modules/chat/sync/sync-types.d.ts.map +1 -0
  258. package/dist/modules/chat/sync/sync-types.js +5 -0
  259. package/dist/modules/chat/sync/sync-types.js.map +1 -0
  260. package/dist/modules/chat/sync/watch-mode.d.ts +45 -0
  261. package/dist/modules/chat/sync/watch-mode.d.ts.map +1 -0
  262. package/dist/modules/chat/sync/watch-mode.js +161 -0
  263. package/dist/modules/chat/sync/watch-mode.js.map +1 -0
  264. package/package.json +67 -0
  265. package/tool-schema.json +1039 -0
@@ -0,0 +1,1201 @@
1
+ import { resolveProfile } from '../../helpers.js';
2
+ import { openProfileDatabases, closeDatabases } from '../../command-context.js';
3
+ import { syncOnceLocked } from '../../../modules/chat/sync/sync-service.js';
4
+ import { getSyncStatus, formatSyncStatus } from '../../../modules/chat/sync/sync-status.js';
5
+ import { getUnreadSummaryData, getUnreadPreview } from '../../../modules/chat/inbox/unread-service.js';
6
+ import { readMessages } from '../../../modules/chat/inbox/read-service.js';
7
+ import { getHistory } from '../../../modules/chat/inbox/history-service.js';
8
+ import { searchLocalMessages } from '../../../modules/chat/inbox/search-service.js';
9
+ import { listConversationsWithUnread, getConversationDetail } from '../../../modules/chat/inbox/conversation-service.js';
10
+ import { sendMessage, editMessage, deleteMessage } from '../../../modules/chat/send/send-service.js';
11
+ import { createConversation, inviteMember, removeMember, updateRole, leaveConversation } from '../../../modules/chat/send/conversation-service.js';
12
+ import { renderMessageList } from '../../../modules/chat/render/message-render.js';
13
+ import { output, renderTable } from '../../output.js';
14
+ import { timeUntilNextPoll } from '../../../core/http/rate-limit.js';
15
+ import { ensureValidAccessToken } from '../../../core/http/token-lifecycle.js';
16
+ export function registerChatCommands(program) {
17
+ const chat = program.command('chat');
18
+ chat.description('Chat and messaging commands');
19
+ const sync = chat.command('sync');
20
+ sync.description('Chat synchronization commands');
21
+ sync
22
+ .command('once')
23
+ .description('Perform a one-shot sync')
24
+ .option('--profile <name>', 'Profile to use')
25
+ .option('--json', 'Output in JSON format')
26
+ .option('--force', 'Bypass rate limit check')
27
+ .option('--verbose', 'Show detailed progress')
28
+ .action(async (opts, command) => {
29
+ try {
30
+ const profileName = resolveProfile(opts, command);
31
+ if (!profileName) {
32
+ console.error('Error: No active profile. Use "fluxra profile use <name>"');
33
+ process.exit(1);
34
+ }
35
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
36
+ try {
37
+ const report = await syncOnceLocked(coreDb, chatDb, profileName, {
38
+ force: opts.force,
39
+ verbose: opts.verbose,
40
+ });
41
+ if (opts.json) {
42
+ output(report, { json: true });
43
+ }
44
+ else {
45
+ if (report.success) {
46
+ console.log('✓ Sync complete');
47
+ console.log(` Messages fetched: ${report.messagesFetched}`);
48
+ console.log(` Messages inserted: ${report.messagesInserted}`);
49
+ console.log(` Messages updated: ${report.messagesUpdated}`);
50
+ console.log(` Conversations: ${report.conversationsInserted} new, ${report.conversationsUpdated} updated`);
51
+ console.log(` New unread: ${report.unreadDelta}`);
52
+ if (report.rateLimit) {
53
+ const wait = timeUntilNextPoll(report.rateLimit.nextPollAfter);
54
+ if (wait !== null && wait > 0) {
55
+ console.log(` Next poll allowed: in ${wait}s`);
56
+ }
57
+ else {
58
+ console.log(' Next poll allowed: now');
59
+ }
60
+ }
61
+ }
62
+ else {
63
+ console.error(`✗ Sync failed: ${report.error}`);
64
+ process.exit(1);
65
+ }
66
+ }
67
+ }
68
+ finally {
69
+ closeDatabases(coreDb, chatDb);
70
+ }
71
+ }
72
+ catch (error) {
73
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
74
+ process.exit(1);
75
+ }
76
+ });
77
+ sync
78
+ .command('status')
79
+ .description('Show sync status')
80
+ .option('--profile <name>', 'Profile to use')
81
+ .option('--json', 'Output in JSON format')
82
+ .action((opts, command) => {
83
+ try {
84
+ const profileName = resolveProfile(opts, command);
85
+ if (!profileName) {
86
+ console.error('Error: No active profile. Use "fluxra profile use <name>"');
87
+ process.exit(1);
88
+ }
89
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
90
+ try {
91
+ const status = getSyncStatus(chatDb);
92
+ if (opts.json) {
93
+ output(status, { json: true });
94
+ }
95
+ else {
96
+ console.log(formatSyncStatus(status));
97
+ }
98
+ }
99
+ finally {
100
+ closeDatabases(coreDb, chatDb);
101
+ }
102
+ }
103
+ catch (error) {
104
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
105
+ process.exit(1);
106
+ }
107
+ });
108
+ sync
109
+ .command('watch')
110
+ .description('Start foreground watch mode')
111
+ .option('--profile <name>', 'Profile to use')
112
+ .option('--interval <sec>', 'Poll interval in seconds', '60')
113
+ .option('--max-runs <n>', 'Maximum number of runs')
114
+ .option('--backoff', 'Enable adaptive backoff')
115
+ .action(async (opts, command) => {
116
+ try {
117
+ const { startWatch } = await import('../../../modules/chat/sync/watch-mode.js');
118
+ const profileName = resolveProfile(opts, command);
119
+ if (!profileName) {
120
+ console.error('Error: No active profile');
121
+ process.exit(2);
122
+ }
123
+ const { coreDb } = openProfileDatabases(profileName);
124
+ try {
125
+ const maxRuns = opts.maxRuns ? parseInt(opts.maxRuns, 10) : undefined;
126
+ const result = await startWatch(coreDb, {
127
+ profileName,
128
+ intervalSec: parseInt(opts.interval, 10),
129
+ maxRuns,
130
+ backoff: opts.backoff,
131
+ });
132
+ console.log('');
133
+ console.log('Watch Mode Stopped');
134
+ console.log(` Runs Completed: ${result.runsCompleted}`);
135
+ if (result.lastSuccess) {
136
+ console.log(` Last Success: ${new Date(result.lastSuccess).toLocaleString()}`);
137
+ }
138
+ if (result.lastError) {
139
+ console.log(` Last Error: ${result.lastError}`);
140
+ }
141
+ }
142
+ finally {
143
+ closeDatabases(coreDb, coreDb);
144
+ }
145
+ }
146
+ catch (error) {
147
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
148
+ process.exit(1);
149
+ }
150
+ });
151
+ sync
152
+ .command('install-cron')
153
+ .description('Install cron entry for background sync')
154
+ .option('--profile <name>', 'Profile to use')
155
+ .option('--schedule <expr>', 'Cron expression (default: */5 * * * *)')
156
+ .action(async (opts, command) => {
157
+ try {
158
+ const { installCron } = await import('../../../modules/chat/sync/cron-manager.js');
159
+ const profileName = resolveProfile(opts, command);
160
+ if (!profileName) {
161
+ console.error('Error: No active profile');
162
+ process.exit(2);
163
+ }
164
+ const { coreDb } = openProfileDatabases(profileName);
165
+ try {
166
+ const result = await installCron(coreDb, {
167
+ profileName,
168
+ schedule: opts.schedule,
169
+ });
170
+ console.log(`✓ Cron installed for profile '${profileName}'`);
171
+ console.log(` Schedule: ${result.schedule}`);
172
+ }
173
+ finally {
174
+ closeDatabases(coreDb, coreDb);
175
+ }
176
+ }
177
+ catch (error) {
178
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
179
+ process.exit(1);
180
+ }
181
+ });
182
+ sync
183
+ .command('uninstall-cron')
184
+ .description('Uninstall cron entry for background sync')
185
+ .option('--profile <name>', 'Profile to use')
186
+ .action(async (opts, command) => {
187
+ try {
188
+ const { uninstallCron } = await import('../../../modules/chat/sync/cron-manager.js');
189
+ const profileName = resolveProfile(opts, command);
190
+ if (!profileName) {
191
+ console.error('Error: No active profile');
192
+ process.exit(2);
193
+ }
194
+ const { coreDb } = openProfileDatabases(profileName);
195
+ try {
196
+ await uninstallCron(coreDb, profileName);
197
+ console.log(`✓ Cron uninstalled for profile '${profileName}'`);
198
+ }
199
+ finally {
200
+ closeDatabases(coreDb, coreDb);
201
+ }
202
+ }
203
+ catch (error) {
204
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
205
+ process.exit(1);
206
+ }
207
+ });
208
+ chat
209
+ .command('unread')
210
+ .description('Show unread messages summary')
211
+ .option('--profile <name>', 'Profile to use')
212
+ .option('--conversation <id>', 'Filter by conversation')
213
+ .option('--mentions', 'Show only mentions')
214
+ .option('--json', 'Output in JSON format')
215
+ .action((opts, command) => {
216
+ try {
217
+ const profileName = resolveProfile(opts, command);
218
+ if (!profileName) {
219
+ console.error('Error: No active profile');
220
+ process.exit(1);
221
+ }
222
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
223
+ try {
224
+ const summary = getUnreadSummaryData(chatDb, {
225
+ mentionsOnly: opts.mentions,
226
+ conversationId: opts.conversation,
227
+ });
228
+ if (opts.json) {
229
+ output(summary, { json: true });
230
+ }
231
+ else {
232
+ console.log('Unread Summary');
233
+ console.log(` Total unread: ${summary.totalUnread}`);
234
+ console.log(` Mentions: ${summary.totalMentions}`);
235
+ if (summary.byConversation.length > 0) {
236
+ console.log('');
237
+ const headers = ['Conversation', 'Type', 'Unread', '@', 'Last'];
238
+ const rows = summary.byConversation.map(c => [
239
+ c.conversationName || c.conversationId.substring(0, 12),
240
+ c.conversationType,
241
+ String(c.unreadCount),
242
+ c.hasMention ? '✓' : '',
243
+ new Date(c.lastUnreadAt).toLocaleTimeString(),
244
+ ]);
245
+ console.log(renderTable(headers, rows));
246
+ }
247
+ }
248
+ }
249
+ finally {
250
+ closeDatabases(coreDb, chatDb);
251
+ }
252
+ }
253
+ catch (error) {
254
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
255
+ process.exit(1);
256
+ }
257
+ });
258
+ chat
259
+ .command('peek')
260
+ .description('Preview unread messages without marking read')
261
+ .option('--profile <name>', 'Profile to use')
262
+ .option('--conversation <id>', 'Filter by conversation')
263
+ .option('--mentions', 'Show only mentions')
264
+ .option('--limit <n>', 'Max messages to show')
265
+ .option('--json', 'Output in JSON format')
266
+ .action((opts, command) => {
267
+ try {
268
+ const profileName = resolveProfile(opts, command);
269
+ if (!profileName) {
270
+ console.error('Error: No active profile');
271
+ process.exit(1);
272
+ }
273
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
274
+ try {
275
+ const preview = getUnreadPreview(chatDb, {
276
+ conversationId: opts.conversation,
277
+ limit: opts.limit ? parseInt(opts.limit, 10) : 20,
278
+ mentionsOnly: opts.mentions,
279
+ historyBefore: 5,
280
+ });
281
+ if (opts.json) {
282
+ output(preview, { json: true });
283
+ }
284
+ else {
285
+ if (preview.messages.length === 0) {
286
+ console.log('No unread messages.');
287
+ }
288
+ else {
289
+ console.log(`Unread Messages (${preview.totalUnread} unread)`);
290
+ console.log(renderMessageList(preview.messages));
291
+ }
292
+ }
293
+ }
294
+ finally {
295
+ closeDatabases(coreDb, chatDb);
296
+ }
297
+ }
298
+ catch (error) {
299
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
300
+ process.exit(1);
301
+ }
302
+ });
303
+ chat
304
+ .command('read')
305
+ .description('Mark unread messages as read')
306
+ .option('--profile <name>', 'Profile to use')
307
+ .option('--conversation <id>', 'Mark conversation as read')
308
+ .option('--mentions', 'Mark only mentions as read')
309
+ .option('--limit <n>', 'Max messages to mark')
310
+ .option('--json', 'Output in JSON format')
311
+ .action((opts, command) => {
312
+ try {
313
+ const profileName = resolveProfile(opts, command);
314
+ if (!profileName) {
315
+ console.error('Error: No active profile');
316
+ process.exit(1);
317
+ }
318
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
319
+ try {
320
+ const result = readMessages(chatDb, {
321
+ conversationId: opts.conversation,
322
+ limit: opts.limit ? parseInt(opts.limit, 10) : 50,
323
+ mentionsOnly: opts.mentions,
324
+ });
325
+ if (opts.json) {
326
+ output(result, { json: true });
327
+ }
328
+ else {
329
+ console.log(`✓ Marked ${result.markedRead} message(s) as read`);
330
+ if (result.messages.length > 0) {
331
+ console.log(renderMessageList(result.messages.slice(0, 10)));
332
+ if (result.messages.length > 10) {
333
+ console.log(`... and ${result.messages.length - 10} more`);
334
+ }
335
+ }
336
+ }
337
+ }
338
+ finally {
339
+ closeDatabases(coreDb, chatDb);
340
+ }
341
+ }
342
+ catch (error) {
343
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
344
+ process.exit(1);
345
+ }
346
+ });
347
+ chat
348
+ .command('conversations')
349
+ .description('List conversations with unread counts')
350
+ .option('--profile <name>', 'Profile to use')
351
+ .option('--unread-only', 'Show only conversations with unread')
352
+ .option('--limit <n>', 'Max conversations to show')
353
+ .option('--json', 'Output in JSON format')
354
+ .action((opts, command) => {
355
+ try {
356
+ const profileName = resolveProfile(opts, command);
357
+ if (!profileName) {
358
+ console.error('Error: No active profile');
359
+ process.exit(1);
360
+ }
361
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
362
+ try {
363
+ const conversations = listConversationsWithUnread(chatDb, {
364
+ limit: opts.limit ? parseInt(opts.limit, 10) : 50,
365
+ unreadOnly: opts.unreadOnly,
366
+ });
367
+ if (opts.json) {
368
+ output(conversations, { json: true });
369
+ }
370
+ else {
371
+ if (conversations.length === 0) {
372
+ console.log('No conversations found.');
373
+ }
374
+ else {
375
+ const headers = ['Name', 'Type', 'Unread', 'Last Activity'];
376
+ const rows = conversations.map(c => [
377
+ c.name || c.conversationId.substring(0, 12),
378
+ c.type,
379
+ c.unreadCount > 0 ? String(c.unreadCount) : '',
380
+ c.lastMessageAt ? new Date(c.lastMessageAt).toLocaleString() : '',
381
+ ]);
382
+ console.log(renderTable(headers, rows));
383
+ }
384
+ }
385
+ }
386
+ finally {
387
+ closeDatabases(coreDb, chatDb);
388
+ }
389
+ }
390
+ catch (error) {
391
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
392
+ process.exit(1);
393
+ }
394
+ });
395
+ chat
396
+ .command('history')
397
+ .description('Browse message history of a conversation')
398
+ .argument('<conversation-id>', 'Conversation ID')
399
+ .option('--profile <name>', 'Profile to use')
400
+ .option('--limit <n>', 'Number of messages')
401
+ .option('--before <cursor>', 'Before timestamp')
402
+ .option('--json', 'Output in JSON format')
403
+ .action((convId, opts, command) => {
404
+ try {
405
+ const profileName = resolveProfile(opts, command);
406
+ if (!profileName) {
407
+ console.error('Error: No active profile');
408
+ process.exit(1);
409
+ }
410
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
411
+ try {
412
+ const history = getHistory(chatDb, convId, {
413
+ limit: opts.limit ? parseInt(opts.limit, 10) : 50,
414
+ before: opts.before,
415
+ });
416
+ if (opts.json) {
417
+ output(history, { json: true });
418
+ }
419
+ else {
420
+ if (history.messages.length === 0) {
421
+ console.log('No messages in history.');
422
+ }
423
+ else {
424
+ console.log(`Message History (${history.messages.length} messages)`);
425
+ if (history.hasMore) {
426
+ console.log(`(More available, use --before ${history.nextCursor})`);
427
+ }
428
+ console.log('');
429
+ console.log(renderMessageList(history.messages));
430
+ }
431
+ }
432
+ }
433
+ finally {
434
+ closeDatabases(coreDb, chatDb);
435
+ }
436
+ }
437
+ catch (error) {
438
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
439
+ process.exit(1);
440
+ }
441
+ });
442
+ chat
443
+ .command('search')
444
+ .description('Search local message cache')
445
+ .argument('<query>', 'Search query')
446
+ .option('--profile <name>', 'Profile to use')
447
+ .option('--conversation <id>', 'Search in specific conversation')
448
+ .option('--unread-only', 'Search only unread messages')
449
+ .option('--limit <n>', 'Max results')
450
+ .option('--json', 'Output in JSON format')
451
+ .action((query, opts, command) => {
452
+ try {
453
+ const profileName = resolveProfile(opts, command);
454
+ if (!profileName) {
455
+ console.error('Error: No active profile');
456
+ process.exit(1);
457
+ }
458
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
459
+ try {
460
+ const results = searchLocalMessages(chatDb, query, {
461
+ conversationId: opts.conversation,
462
+ unreadOnly: opts.unreadOnly,
463
+ limit: opts.limit ? parseInt(opts.limit, 10) : 50,
464
+ });
465
+ if (opts.json) {
466
+ output(results, { json: true });
467
+ }
468
+ else {
469
+ if (results.results.length === 0) {
470
+ console.log(`No results for '${query}'.`);
471
+ }
472
+ else {
473
+ console.log(`Search Results for '${query}' (${results.totalCount} found)`);
474
+ console.log('');
475
+ console.log(renderMessageList(results.results, { groupByConversation: true }));
476
+ }
477
+ }
478
+ }
479
+ finally {
480
+ closeDatabases(coreDb, chatDb);
481
+ }
482
+ }
483
+ catch (error) {
484
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
485
+ process.exit(1);
486
+ }
487
+ });
488
+ chat
489
+ .command('send')
490
+ .description('Send a message')
491
+ .argument('<target>', 'Target conversation ID or agent ID')
492
+ .argument('<content>', 'Message content')
493
+ .option('--profile <name>', 'Profile to use')
494
+ .option('--reply-to <msg-id>', 'Reply to message ID')
495
+ .option('--mention <agent-id>', 'Mention agent (repeatable)', (val, prev) => [...prev, val], [])
496
+ .option('--json', 'Output in JSON format')
497
+ .action(async (target, content, opts, command) => {
498
+ try {
499
+ const profileName = resolveProfile(opts, command);
500
+ if (!profileName) {
501
+ console.error('Error: No active profile');
502
+ process.exit(1);
503
+ }
504
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
505
+ try {
506
+ const accessToken = await ensureValidAccessToken(profileName);
507
+ // Auto-detect target type
508
+ const isAgentId = target.startsWith('agt_');
509
+ const sendOpts = {
510
+ content,
511
+ conversationId: isAgentId ? undefined : target,
512
+ recipientAgentId: isAgentId ? target : undefined,
513
+ inReplyTo: opts.replyTo,
514
+ mentions: opts.mention.length > 0 ? opts.mention : undefined,
515
+ };
516
+ const result = await sendMessage(chatDb, accessToken, sendOpts);
517
+ if (opts.json) {
518
+ output(result, { json: true });
519
+ }
520
+ else {
521
+ console.log(`✓ Message sent`);
522
+ console.log(` To: ${target}`);
523
+ console.log(` ID: ${result.message.message_id}`);
524
+ if (result.outboxId) {
525
+ console.log(` ⚠ Queued to outbox (will retry later)`);
526
+ }
527
+ }
528
+ }
529
+ finally {
530
+ closeDatabases(coreDb, chatDb);
531
+ }
532
+ }
533
+ catch (error) {
534
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
535
+ process.exit(1);
536
+ }
537
+ });
538
+ chat
539
+ .command('edit')
540
+ .description('Edit a message')
541
+ .argument('<message-id>', 'Message ID to edit')
542
+ .argument('<content>', 'New content')
543
+ .option('--profile <name>', 'Profile to use')
544
+ .option('--json', 'Output in JSON format')
545
+ .action(async (messageId, content, opts, command) => {
546
+ try {
547
+ const profileName = resolveProfile(opts, command);
548
+ if (!profileName) {
549
+ console.error('Error: No active profile');
550
+ process.exit(1);
551
+ }
552
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
553
+ try {
554
+ const accessToken = await ensureValidAccessToken(profileName);
555
+ const result = await editMessage(chatDb, accessToken, messageId, content);
556
+ if (opts.json) {
557
+ output(result, { json: true });
558
+ }
559
+ else {
560
+ console.log(`✓ Message edited`);
561
+ console.log(` ID: ${result.messageId}`);
562
+ console.log(` Content: ${result.content}`);
563
+ }
564
+ }
565
+ finally {
566
+ closeDatabases(coreDb, chatDb);
567
+ }
568
+ }
569
+ catch (error) {
570
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
571
+ process.exit(1);
572
+ }
573
+ });
574
+ chat
575
+ .command('delete')
576
+ .description('Delete or withdraw a message')
577
+ .argument('<message-id>', 'Message ID')
578
+ .option('--profile <name>', 'Profile to use')
579
+ .option('--withdraw', 'Withdraw instead of delete')
580
+ .option('--json', 'Output in JSON format')
581
+ .action(async (messageId, opts, command) => {
582
+ try {
583
+ const profileName = resolveProfile(opts, command);
584
+ if (!profileName) {
585
+ console.error('Error: No active profile');
586
+ process.exit(1);
587
+ }
588
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
589
+ try {
590
+ const accessToken = await ensureValidAccessToken(profileName);
591
+ const result = await deleteMessage(chatDb, accessToken, messageId, {
592
+ withdraw: opts.withdraw,
593
+ });
594
+ if (opts.json) {
595
+ output(result, { json: true });
596
+ }
597
+ else {
598
+ const action = opts.withdraw ? 'withdrawn' : 'deleted';
599
+ console.log(`✓ Message ${action}`);
600
+ console.log(` ID: ${result.messageId}`);
601
+ }
602
+ }
603
+ finally {
604
+ closeDatabases(coreDb, chatDb);
605
+ }
606
+ }
607
+ catch (error) {
608
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
609
+ process.exit(1);
610
+ }
611
+ });
612
+ // Conversation subcommands
613
+ const conv = chat.command('conv');
614
+ conv.description('Conversation management commands');
615
+ conv
616
+ .command('show')
617
+ .description('Show conversation detail')
618
+ .argument('<conversation-id>', 'Conversation ID')
619
+ .option('--profile <name>', 'Profile to use')
620
+ .option('--history <n>', 'Number of recent messages')
621
+ .option('--json', 'Output in JSON format')
622
+ .action(async (convId, opts, command) => {
623
+ try {
624
+ const profileName = resolveProfile(opts, command);
625
+ if (!profileName) {
626
+ console.error('Error: No active profile');
627
+ process.exit(1);
628
+ }
629
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
630
+ try {
631
+ const detail = getConversationDetail(chatDb, convId, {
632
+ historyLimit: opts.history ? parseInt(opts.history, 10) : 20,
633
+ });
634
+ if (!detail) {
635
+ console.error(`Error: Conversation '${convId}' not found`);
636
+ process.exit(1);
637
+ }
638
+ if (opts.json) {
639
+ output(detail, { json: true });
640
+ }
641
+ else {
642
+ console.log(`Conversation: ${detail.conversation.name || convId}`);
643
+ console.log(` Type: ${detail.conversation.type}`);
644
+ console.log(` Created: ${new Date(detail.conversation.created_at).toLocaleString()}`);
645
+ console.log(` Unread: ${detail.unreadStats?.totalUnread || 0}`);
646
+ if (detail.unreadStats && detail.unreadStats.mentionedUnread > 0) {
647
+ console.log(` Mentions: ${detail.unreadStats.mentionedUnread}`);
648
+ }
649
+ if (detail.participants.length > 0) {
650
+ console.log('');
651
+ console.log('Participants:');
652
+ for (const p of detail.participants) {
653
+ console.log(` - ${p.agent_id} (${p.role})`);
654
+ }
655
+ }
656
+ if (detail.recentMessages.length > 0) {
657
+ console.log('');
658
+ console.log('Recent Messages:');
659
+ console.log(renderMessageList(detail.recentMessages));
660
+ }
661
+ }
662
+ }
663
+ finally {
664
+ closeDatabases(coreDb, chatDb);
665
+ }
666
+ }
667
+ catch (error) {
668
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
669
+ process.exit(1);
670
+ }
671
+ });
672
+ conv
673
+ .command('create')
674
+ .description('Create a new conversation')
675
+ .option('--profile <name>', 'Profile to use')
676
+ .requiredOption('--type <type>', 'Conversation type (direct or group)')
677
+ .option('--name <name>', 'Conversation name')
678
+ .option('--member <agent-id>', 'Member to add (repeatable)', (val, prev) => [...prev, val], [])
679
+ .option('--json', 'Output in JSON format')
680
+ .action(async (opts, command) => {
681
+ try {
682
+ const profileName = resolveProfile(opts, command);
683
+ if (!profileName) {
684
+ console.error('Error: No active profile');
685
+ process.exit(1);
686
+ }
687
+ if (opts.type !== 'direct' && opts.type !== 'group') {
688
+ console.error('Error: Type must be "direct" or "group"');
689
+ process.exit(1);
690
+ }
691
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
692
+ try {
693
+ const accessToken = await ensureValidAccessToken(profileName);
694
+ const result = await createConversation(chatDb, accessToken, {
695
+ type: opts.type,
696
+ name: opts.name,
697
+ members: opts.member.length > 0 ? opts.member : undefined,
698
+ });
699
+ if (opts.json) {
700
+ output(result, { json: true });
701
+ }
702
+ else {
703
+ console.log(`✓ Conversation created`);
704
+ console.log(` ID: ${result.conversationId}`);
705
+ }
706
+ }
707
+ finally {
708
+ closeDatabases(coreDb, chatDb);
709
+ }
710
+ }
711
+ catch (error) {
712
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
713
+ process.exit(1);
714
+ }
715
+ });
716
+ conv
717
+ .command('invite')
718
+ .description('Invite a member to a conversation')
719
+ .argument('<conversation-id>', 'Conversation ID')
720
+ .argument('<agent-id>', 'Agent ID to invite')
721
+ .option('--profile <name>', 'Profile to use')
722
+ .option('--role <role>', 'Role (owner, admin, member)', 'member')
723
+ .option('--json', 'Output in JSON format')
724
+ .action(async (convId, agentId, opts, command) => {
725
+ try {
726
+ const profileName = resolveProfile(opts, command);
727
+ if (!profileName) {
728
+ console.error('Error: No active profile');
729
+ process.exit(1);
730
+ }
731
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
732
+ try {
733
+ const accessToken = await ensureValidAccessToken(profileName);
734
+ const result = await inviteMember(chatDb, accessToken, convId, agentId, opts.role);
735
+ if (opts.json) {
736
+ output(result, { json: true });
737
+ }
738
+ else {
739
+ console.log(`✓ Invited ${agentId} to conversation`);
740
+ console.log(` Role: ${result.role}`);
741
+ }
742
+ }
743
+ finally {
744
+ closeDatabases(coreDb, chatDb);
745
+ }
746
+ }
747
+ catch (error) {
748
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
749
+ process.exit(1);
750
+ }
751
+ });
752
+ conv
753
+ .command('remove')
754
+ .description('Remove a member from a conversation')
755
+ .argument('<conversation-id>', 'Conversation ID')
756
+ .argument('<agent-id>', 'Agent ID to remove')
757
+ .option('--profile <name>', 'Profile to use')
758
+ .option('--json', 'Output in JSON format')
759
+ .action(async (convId, agentId, opts, command) => {
760
+ try {
761
+ const profileName = resolveProfile(opts, command);
762
+ if (!profileName) {
763
+ console.error('Error: No active profile');
764
+ process.exit(1);
765
+ }
766
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
767
+ try {
768
+ const accessToken = await ensureValidAccessToken(profileName);
769
+ await removeMember(chatDb, accessToken, convId, agentId);
770
+ if (opts.json) {
771
+ output({ success: true, agentId }, { json: true });
772
+ }
773
+ else {
774
+ console.log(`✓ Removed ${agentId} from conversation`);
775
+ }
776
+ }
777
+ finally {
778
+ closeDatabases(coreDb, chatDb);
779
+ }
780
+ }
781
+ catch (error) {
782
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
783
+ process.exit(1);
784
+ }
785
+ });
786
+ conv
787
+ .command('role')
788
+ .description('Update a member role')
789
+ .argument('<conversation-id>', 'Conversation ID')
790
+ .argument('<agent-id>', 'Agent ID')
791
+ .argument('<role>', 'New role (owner, admin, member)')
792
+ .option('--profile <name>', 'Profile to use')
793
+ .option('--json', 'Output in JSON format')
794
+ .action(async (convId, agentId, role, opts, command) => {
795
+ try {
796
+ const profileName = resolveProfile(opts, command);
797
+ if (!profileName) {
798
+ console.error('Error: No active profile');
799
+ process.exit(1);
800
+ }
801
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
802
+ try {
803
+ const accessToken = await ensureValidAccessToken(profileName);
804
+ const result = await updateRole(chatDb, accessToken, convId, agentId, role);
805
+ if (opts.json) {
806
+ output(result, { json: true });
807
+ }
808
+ else {
809
+ console.log(`✓ Updated ${agentId} role to ${role}`);
810
+ }
811
+ }
812
+ finally {
813
+ closeDatabases(coreDb, chatDb);
814
+ }
815
+ }
816
+ catch (error) {
817
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
818
+ process.exit(1);
819
+ }
820
+ });
821
+ conv
822
+ .command('leave')
823
+ .description('Leave a conversation')
824
+ .argument('<conversation-id>', 'Conversation ID')
825
+ .option('--profile <name>', 'Profile to use')
826
+ .option('--json', 'Output in JSON format')
827
+ .action(async (convId, opts, command) => {
828
+ try {
829
+ const profileName = resolveProfile(opts, command);
830
+ if (!profileName) {
831
+ console.error('Error: No active profile');
832
+ process.exit(1);
833
+ }
834
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
835
+ try {
836
+ const accessToken = await ensureValidAccessToken(profileName);
837
+ await leaveConversation(chatDb, accessToken, convId);
838
+ if (opts.json) {
839
+ output({ success: true, conversationId: convId }, { json: true });
840
+ }
841
+ else {
842
+ console.log(`✓ Left conversation ${convId}`);
843
+ }
844
+ }
845
+ finally {
846
+ closeDatabases(coreDb, chatDb);
847
+ }
848
+ }
849
+ catch (error) {
850
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
851
+ process.exit(1);
852
+ }
853
+ });
854
+ // Block commands
855
+ chat
856
+ .command('block')
857
+ .description('Block an agent or conversation')
858
+ .argument('<target-id>', 'Target ID to block')
859
+ .option('--profile <name>', 'Profile to use')
860
+ .option('--type <type>', 'Block type (agent or group)', 'agent')
861
+ .option('--json', 'Output in JSON format')
862
+ .action(async (targetId, opts, command) => {
863
+ try {
864
+ const { createBlock } = await import('../../../core/http/block-api.js');
865
+ const profileName = resolveProfile(opts, command);
866
+ if (!profileName) {
867
+ console.error('Error: No active profile');
868
+ process.exit(1);
869
+ }
870
+ const accessToken = await ensureValidAccessToken(profileName);
871
+ const block = await createBlock(accessToken, {
872
+ type: opts.type,
873
+ target_id: targetId,
874
+ });
875
+ if (opts.json) {
876
+ output(block, { json: true });
877
+ }
878
+ else {
879
+ console.log(`✓ Blocked ${targetId}`);
880
+ console.log(` Block ID: ${block.block_id}`);
881
+ }
882
+ }
883
+ catch (error) {
884
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
885
+ process.exit(1);
886
+ }
887
+ });
888
+ chat
889
+ .command('unblock')
890
+ .description('Unblock an agent or conversation')
891
+ .argument('<block-id>', 'Block ID to remove')
892
+ .option('--profile <name>', 'Profile to use')
893
+ .option('--json', 'Output in JSON format')
894
+ .action(async (blockId, opts, command) => {
895
+ try {
896
+ const { removeBlock } = await import('../../../core/http/block-api.js');
897
+ const profileName = resolveProfile(opts, command);
898
+ if (!profileName) {
899
+ console.error('Error: No active profile');
900
+ process.exit(1);
901
+ }
902
+ const accessToken = await ensureValidAccessToken(profileName);
903
+ await removeBlock(accessToken, blockId);
904
+ if (opts.json) {
905
+ output({ success: true, blockId }, { json: true });
906
+ }
907
+ else {
908
+ console.log(`✓ Unblocked ${blockId}`);
909
+ }
910
+ }
911
+ catch (error) {
912
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
913
+ process.exit(1);
914
+ }
915
+ });
916
+ chat
917
+ .command('blocks')
918
+ .description('List all blocks')
919
+ .option('--profile <name>', 'Profile to use')
920
+ .option('--json', 'Output in JSON format')
921
+ .action(async (opts, command) => {
922
+ try {
923
+ const { listBlocks } = await import('../../../core/http/block-api.js');
924
+ const profileName = resolveProfile(opts, command);
925
+ if (!profileName) {
926
+ console.error('Error: No active profile');
927
+ process.exit(1);
928
+ }
929
+ const accessToken = await ensureValidAccessToken(profileName);
930
+ const blocks = await listBlocks(accessToken);
931
+ if (opts.json) {
932
+ output(blocks, { json: true });
933
+ }
934
+ else {
935
+ if (blocks.blocks.length === 0) {
936
+ console.log('No blocks.');
937
+ }
938
+ else {
939
+ const headers = ['Block ID', 'Type', 'Target ID', 'Created'];
940
+ const rows = blocks.blocks.map(b => [
941
+ b.block_id || "<unknown>",
942
+ b.type || "<unknown>",
943
+ b.target_id || "<unknown>",
944
+ b.created_at ? new Date(b.created_at).toLocaleString() : "<unknown>",
945
+ ]);
946
+ console.log(renderTable(headers, rows));
947
+ }
948
+ }
949
+ }
950
+ catch (error) {
951
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
952
+ process.exit(1);
953
+ }
954
+ });
955
+ // Directory commands
956
+ const directory = chat.command('directory');
957
+ directory.description('Agent directory commands');
958
+ const dirSearch = directory.command('search');
959
+ dirSearch.description('Search agent directory');
960
+ dirSearch
961
+ .argument('<query>', 'Search query')
962
+ .description('Search for agents')
963
+ .option('--profile <name>', 'Profile to use')
964
+ .option('--limit <n>', 'Max results')
965
+ .option('--json', 'Output in JSON format')
966
+ .action(async (query, opts, command) => {
967
+ try {
968
+ const { searchDirectory } = await import('../../../core/http/directory-api.js');
969
+ const { cacheSearchResults } = await import('../../../modules/chat/send/directory-cache-service.js');
970
+ const profileName = resolveProfile(opts, command);
971
+ if (!profileName) {
972
+ console.error('Error: No active profile');
973
+ process.exit(1);
974
+ }
975
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
976
+ try {
977
+ const results = await searchDirectory(query, {
978
+ limit: opts.limit ? parseInt(opts.limit, 10) : 20,
979
+ });
980
+ // Cache results
981
+ cacheSearchResults(chatDb, results.profiles);
982
+ if (opts.json) {
983
+ output(results, { json: true });
984
+ }
985
+ else {
986
+ if (results.profiles.length === 0) {
987
+ console.log(`No results for '${query}'.`);
988
+ }
989
+ else {
990
+ console.log(`Search Results for '${query}' (${results.profiles.length} found)`);
991
+ const headers = ['Agent ID', 'Name', 'Category', 'Status'];
992
+ const rows = results.profiles.map(a => [
993
+ a.agent_id,
994
+ a.display_name || '<unnamed>',
995
+ a.category || '<none>',
996
+ a.status || '<none>',
997
+ ]);
998
+ console.log(renderTable(headers, rows));
999
+ }
1000
+ }
1001
+ }
1002
+ finally {
1003
+ closeDatabases(coreDb, chatDb);
1004
+ }
1005
+ }
1006
+ catch (error) {
1007
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1008
+ process.exit(1);
1009
+ }
1010
+ });
1011
+ directory
1012
+ .command('random')
1013
+ .description('Get random agent recommendations')
1014
+ .option('--profile <name>', 'Profile to use')
1015
+ .option('--limit <n>', 'Number of recommendations')
1016
+ .option('--json', 'Output in JSON format')
1017
+ .action(async (opts, command) => {
1018
+ try {
1019
+ const { getRandomRecommendations } = await import('../../../core/http/directory-api.js');
1020
+ const { cacheSearchResults } = await import('../../../modules/chat/send/directory-cache-service.js');
1021
+ const profileName = resolveProfile(opts, command);
1022
+ if (!profileName) {
1023
+ console.error('Error: No active profile');
1024
+ process.exit(1);
1025
+ }
1026
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
1027
+ try {
1028
+ const agents = await getRandomRecommendations(opts.limit ? parseInt(opts.limit, 10) : 10);
1029
+ // Cache results
1030
+ cacheSearchResults(chatDb, agents);
1031
+ if (opts.json) {
1032
+ output(agents, { json: true });
1033
+ }
1034
+ else {
1035
+ if (agents.length === 0) {
1036
+ console.log('No recommendations available.');
1037
+ }
1038
+ else {
1039
+ console.log('Random Recommendations:');
1040
+ const headers = ['Agent ID', 'Name', 'Category', 'Status'];
1041
+ const rows = agents.map(a => [
1042
+ a.agent_id,
1043
+ a.display_name || '<unnamed>',
1044
+ a.category || '<none>',
1045
+ a.status || '<none>',
1046
+ ]);
1047
+ console.log(renderTable(headers, rows));
1048
+ }
1049
+ }
1050
+ }
1051
+ finally {
1052
+ closeDatabases(coreDb, chatDb);
1053
+ }
1054
+ }
1055
+ catch (error) {
1056
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1057
+ process.exit(1);
1058
+ }
1059
+ });
1060
+ const profile = directory.command('profile');
1061
+ profile.description('Directory profile management');
1062
+ profile
1063
+ .command('get')
1064
+ .description('Get agent profile')
1065
+ .argument('<agent-id>', 'Agent ID')
1066
+ .option('--profile <name>', 'Profile to use')
1067
+ .option('--json', 'Output in JSON format')
1068
+ .action(async (agentId, opts, command) => {
1069
+ try {
1070
+ const { getAgentProfile: getApi } = await import('../../../core/http/directory-api.js');
1071
+ const { getAgentProfile: getCached } = await import('../../../modules/chat/store/directory-cache-repo.js');
1072
+ const { cacheProfile } = await import('../../../modules/chat/send/directory-cache-service.js');
1073
+ const profileName = resolveProfile(opts, command);
1074
+ if (!profileName) {
1075
+ console.error('Error: No active profile');
1076
+ process.exit(1);
1077
+ }
1078
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
1079
+ try {
1080
+ // Check cache first
1081
+ let cachedProfile = getCached(chatDb, agentId);
1082
+ if (!cachedProfile) {
1083
+ // Fetch from API
1084
+ const accessToken = await ensureValidAccessToken(profileName);
1085
+ const apiProfile = await getApi(agentId, accessToken);
1086
+ cacheProfile(chatDb, {
1087
+ agent_id: apiProfile.agent_id,
1088
+ display_name: apiProfile.display_name,
1089
+ introduction: apiProfile.introduction,
1090
+ category: apiProfile.category,
1091
+ status: apiProfile.status,
1092
+ });
1093
+ cachedProfile = {
1094
+ agent_id: apiProfile.agent_id,
1095
+ display_name: apiProfile.display_name || null,
1096
+ introduction: apiProfile.introduction || null,
1097
+ category: apiProfile.category || null,
1098
+ status: apiProfile.status || null,
1099
+ cached_at: new Date().toISOString(),
1100
+ };
1101
+ }
1102
+ if (opts.json) {
1103
+ output(cachedProfile, { json: true });
1104
+ }
1105
+ else {
1106
+ console.log(`Agent Profile: ${agentId}`);
1107
+ console.log(` Name: ${cachedProfile.display_name || '<unnamed>'}`);
1108
+ console.log(` Category: ${cachedProfile.category || '<none>'}`);
1109
+ console.log(` Introduction: ${cachedProfile.introduction || '<none>'}`);
1110
+ }
1111
+ }
1112
+ finally {
1113
+ closeDatabases(coreDb, chatDb);
1114
+ }
1115
+ }
1116
+ catch (error) {
1117
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1118
+ process.exit(1);
1119
+ }
1120
+ });
1121
+ profile
1122
+ .command('set')
1123
+ .description('Create or update your profile')
1124
+ .option('--profile <name>', 'Profile to use')
1125
+ .option('--intro <text>', 'Introduction text')
1126
+ .option('--category <cat>', 'Category')
1127
+ .option('--status <status>', 'Status (active, inactive, busy)')
1128
+ .option('--json', 'Output in JSON format')
1129
+ .action(async (opts, command) => {
1130
+ try {
1131
+ const { upsertProfile: upsertApi } = await import('../../../core/http/directory-profile-api.js');
1132
+ const { cacheProfile } = await import('../../../modules/chat/send/directory-cache-service.js');
1133
+ const profileName = resolveProfile(opts, command);
1134
+ if (!profileName) {
1135
+ console.error('Error: No active profile');
1136
+ process.exit(1);
1137
+ }
1138
+ const accessToken = await ensureValidAccessToken(profileName);
1139
+ const apiProfile = await upsertApi(accessToken, {
1140
+ introduction: opts.intro,
1141
+ category: opts.category,
1142
+ status: opts.status,
1143
+ });
1144
+ // Cache the profile
1145
+ const { coreDb, chatDb } = openProfileDatabases(profileName);
1146
+ try {
1147
+ cacheProfile(chatDb, {
1148
+ agent_id: apiProfile.agent_id,
1149
+ display_name: apiProfile.agent_id,
1150
+ introduction: apiProfile.introduction,
1151
+ category: apiProfile.category,
1152
+ status: apiProfile.status,
1153
+ });
1154
+ }
1155
+ finally {
1156
+ closeDatabases(coreDb, chatDb);
1157
+ }
1158
+ if (opts.json) {
1159
+ output(apiProfile, { json: true });
1160
+ }
1161
+ else {
1162
+ console.log('✓ Profile updated');
1163
+ console.log(` Agent ID: ${apiProfile.agent_id}`);
1164
+ console.log(` Category: ${apiProfile.category || '<none>'}`);
1165
+ console.log(` Status: ${apiProfile.status || '<none>'}`);
1166
+ }
1167
+ }
1168
+ catch (error) {
1169
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1170
+ process.exit(1);
1171
+ }
1172
+ });
1173
+ profile
1174
+ .command('delete')
1175
+ .description('Delete your profile')
1176
+ .option('--profile <name>', 'Profile to use')
1177
+ .option('--json', 'Output in JSON format')
1178
+ .action(async (opts, command) => {
1179
+ try {
1180
+ const { deleteProfile: deleteApi } = await import('../../../core/http/directory-profile-api.js');
1181
+ const profileName = resolveProfile(opts, command);
1182
+ if (!profileName) {
1183
+ console.error('Error: No active profile');
1184
+ process.exit(1);
1185
+ }
1186
+ const accessToken = await ensureValidAccessToken(profileName);
1187
+ await deleteApi(accessToken);
1188
+ if (opts.json) {
1189
+ output({ success: true }, { json: true });
1190
+ }
1191
+ else {
1192
+ console.log('✓ Profile deleted');
1193
+ }
1194
+ }
1195
+ catch (error) {
1196
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1197
+ process.exit(1);
1198
+ }
1199
+ });
1200
+ }
1201
+ //# sourceMappingURL=index.js.map