@crewx/cli 0.8.0-rc.66 → 0.8.0-rc.82

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 (281) hide show
  1. package/bin/crewx +2 -0
  2. package/dist/bootstrap/crewx-cli.d.ts +11 -0
  3. package/dist/bootstrap/crewx-cli.js +31 -0
  4. package/dist/builtin.d.ts +15 -0
  5. package/dist/{cli/builtin.handler.js → builtin.js} +21 -7
  6. package/dist/commands/agent.d.ts +12 -0
  7. package/dist/commands/agent.js +246 -0
  8. package/dist/commands/doctor.d.ts +12 -0
  9. package/dist/commands/doctor.js +190 -0
  10. package/dist/commands/execute.d.ts +21 -0
  11. package/dist/commands/execute.js +117 -0
  12. package/dist/commands/hook/install.d.ts +21 -0
  13. package/dist/commands/hook/install.js +175 -0
  14. package/dist/commands/hook/paths.d.ts +19 -0
  15. package/dist/commands/hook/paths.js +94 -0
  16. package/dist/commands/hook/status.d.ts +7 -0
  17. package/dist/commands/hook/status.js +86 -0
  18. package/dist/commands/hook/uninstall.d.ts +8 -0
  19. package/dist/commands/hook/uninstall.js +71 -0
  20. package/dist/commands/hook-dispatch.d.ts +15 -0
  21. package/dist/commands/hook-dispatch.js +180 -0
  22. package/dist/commands/init.d.ts +24 -0
  23. package/dist/commands/init.js +133 -0
  24. package/dist/commands/kill.d.ts +12 -0
  25. package/dist/commands/kill.js +49 -0
  26. package/dist/commands/log.d.ts +13 -0
  27. package/dist/commands/log.js +97 -0
  28. package/dist/commands/parse-agent-message.d.ts +31 -0
  29. package/dist/commands/parse-agent-message.js +52 -0
  30. package/dist/commands/parse-common-flags.d.ts +36 -0
  31. package/dist/commands/parse-common-flags.js +105 -0
  32. package/dist/commands/ps.d.ts +12 -0
  33. package/dist/commands/ps.js +71 -0
  34. package/dist/commands/query.d.ts +21 -0
  35. package/dist/commands/query.js +117 -0
  36. package/dist/commands/result.d.ts +13 -0
  37. package/dist/commands/result.js +73 -0
  38. package/dist/commands/slack.d.ts +12 -0
  39. package/dist/commands/slack.js +559 -0
  40. package/dist/commands/task-db.d.ts +33 -0
  41. package/dist/commands/task-db.js +107 -0
  42. package/dist/examples/deny-secrets-plugin.d.ts +22 -0
  43. package/dist/examples/deny-secrets-plugin.js +40 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.js +5 -0
  46. package/dist/logging.d.ts +21 -0
  47. package/dist/logging.js +86 -0
  48. package/dist/main.d.ts +10 -0
  49. package/dist/main.js +246 -256
  50. package/dist/plugins/examples/echo-hook.d.ts +24 -0
  51. package/dist/plugins/examples/echo-hook.js +60 -0
  52. package/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
  53. package/dist/plugins/examples/verify-echo-hook.js +47 -0
  54. package/dist/plugins/sqlite-tracing.d.ts +13 -0
  55. package/dist/plugins/sqlite-tracing.js +20 -0
  56. package/dist/register-builtin-tools.d.ts +5 -0
  57. package/dist/register-builtin-tools.js +9 -0
  58. package/dist/slack/file-download.d.ts +17 -0
  59. package/dist/slack/file-download.js +134 -0
  60. package/dist/slack/markdown.d.ts +5 -0
  61. package/dist/slack/markdown.js +33 -0
  62. package/dist/utils/env-defaults.d.ts +5 -0
  63. package/dist/utils/env-defaults.js +10 -0
  64. package/dist/utils/version.d.ts +1 -0
  65. package/dist/utils/version.js +28 -0
  66. package/package.json +32 -115
  67. package/README.md +0 -663
  68. package/dist/ai-provider.service.d.ts +0 -36
  69. package/dist/ai-provider.service.js +0 -315
  70. package/dist/ai-provider.service.js.map +0 -1
  71. package/dist/ai.service.d.ts +0 -17
  72. package/dist/ai.service.js +0 -51
  73. package/dist/ai.service.js.map +0 -1
  74. package/dist/app.module.d.ts +0 -5
  75. package/dist/app.module.js +0 -165
  76. package/dist/app.module.js.map +0 -1
  77. package/dist/cli/agent.handler.d.ts +0 -2
  78. package/dist/cli/agent.handler.js +0 -186
  79. package/dist/cli/agent.handler.js.map +0 -1
  80. package/dist/cli/builtin.handler.d.ts +0 -3
  81. package/dist/cli/builtin.handler.js.map +0 -1
  82. package/dist/cli/chat.handler.d.ts +0 -20
  83. package/dist/cli/chat.handler.js +0 -446
  84. package/dist/cli/chat.handler.js.map +0 -1
  85. package/dist/cli/cli.handler.d.ts +0 -4
  86. package/dist/cli/cli.handler.js +0 -119
  87. package/dist/cli/cli.handler.js.map +0 -1
  88. package/dist/cli/doctor.handler.d.ts +0 -38
  89. package/dist/cli/doctor.handler.js +0 -495
  90. package/dist/cli/doctor.handler.js.map +0 -1
  91. package/dist/cli/execute.handler.d.ts +0 -2
  92. package/dist/cli/execute.handler.js +0 -376
  93. package/dist/cli/execute.handler.js.map +0 -1
  94. package/dist/cli/help.handler.d.ts +0 -2
  95. package/dist/cli/help.handler.js +0 -10
  96. package/dist/cli/help.handler.js.map +0 -1
  97. package/dist/cli/init.handler.d.ts +0 -26
  98. package/dist/cli/init.handler.js +0 -450
  99. package/dist/cli/init.handler.js.map +0 -1
  100. package/dist/cli/log.handler.d.ts +0 -2
  101. package/dist/cli/log.handler.js +0 -69
  102. package/dist/cli/log.handler.js.map +0 -1
  103. package/dist/cli/mcp.handler.d.ts +0 -3
  104. package/dist/cli/mcp.handler.js +0 -121
  105. package/dist/cli/mcp.handler.js.map +0 -1
  106. package/dist/cli/query.handler.d.ts +0 -2
  107. package/dist/cli/query.handler.js +0 -392
  108. package/dist/cli/query.handler.js.map +0 -1
  109. package/dist/cli/skill.handler.d.ts +0 -2
  110. package/dist/cli/skill.handler.js +0 -252
  111. package/dist/cli/skill.handler.js.map +0 -1
  112. package/dist/cli/slack-files.handler.d.ts +0 -2
  113. package/dist/cli/slack-files.handler.js +0 -291
  114. package/dist/cli/slack-files.handler.js.map +0 -1
  115. package/dist/cli/template.handler.d.ts +0 -2
  116. package/dist/cli/template.handler.js +0 -188
  117. package/dist/cli/template.handler.js.map +0 -1
  118. package/dist/cli/templates.handler.d.ts +0 -2
  119. package/dist/cli/templates.handler.js +0 -100
  120. package/dist/cli/templates.handler.js.map +0 -1
  121. package/dist/cli-options.d.ts +0 -40
  122. package/dist/cli-options.js +0 -371
  123. package/dist/cli-options.js.map +0 -1
  124. package/dist/config/timeout.config.d.ts +0 -14
  125. package/dist/config/timeout.config.js +0 -34
  126. package/dist/config/timeout.config.js.map +0 -1
  127. package/dist/conversation/base-conversation-history.provider.d.ts +0 -12
  128. package/dist/conversation/base-conversation-history.provider.js +0 -45
  129. package/dist/conversation/base-conversation-history.provider.js.map +0 -1
  130. package/dist/conversation/cli-box-reader.adapter.d.ts +0 -6
  131. package/dist/conversation/cli-box-reader.adapter.js +0 -10
  132. package/dist/conversation/cli-box-reader.adapter.js.map +0 -1
  133. package/dist/conversation/cli-conversation-history.provider.d.ts +0 -16
  134. package/dist/conversation/cli-conversation-history.provider.js +0 -112
  135. package/dist/conversation/cli-conversation-history.provider.js.map +0 -1
  136. package/dist/conversation/cli-task-reader.adapter.d.ts +0 -6
  137. package/dist/conversation/cli-task-reader.adapter.js +0 -25
  138. package/dist/conversation/cli-task-reader.adapter.js.map +0 -1
  139. package/dist/conversation/conversation-provider.factory.d.ts +0 -10
  140. package/dist/conversation/conversation-provider.factory.js +0 -50
  141. package/dist/conversation/conversation-provider.factory.js.map +0 -1
  142. package/dist/conversation/index.d.ts +0 -8
  143. package/dist/conversation/index.js +0 -29
  144. package/dist/conversation/index.js.map +0 -1
  145. package/dist/conversation/slack-conversation-history.provider.d.ts +0 -29
  146. package/dist/conversation/slack-conversation-history.provider.js +0 -302
  147. package/dist/conversation/slack-conversation-history.provider.js.map +0 -1
  148. package/dist/crewx.tool.d.ts +0 -360
  149. package/dist/crewx.tool.js +0 -2531
  150. package/dist/crewx.tool.js.map +0 -1
  151. package/dist/crewx.tool.spec.d.ts +0 -1
  152. package/dist/crewx.tool.spec.js +0 -222
  153. package/dist/crewx.tool.spec.js.map +0 -1
  154. package/dist/guards/bearer-auth.guard.d.ts +0 -7
  155. package/dist/guards/bearer-auth.guard.js +0 -44
  156. package/dist/guards/bearer-auth.guard.js.map +0 -1
  157. package/dist/health.controller.d.ts +0 -6
  158. package/dist/health.controller.js +0 -32
  159. package/dist/health.controller.js.map +0 -1
  160. package/dist/main.js.map +0 -1
  161. package/dist/mcp.controller.d.ts +0 -8
  162. package/dist/mcp.controller.js +0 -62
  163. package/dist/mcp.controller.js.map +0 -1
  164. package/dist/package.json +0 -3
  165. package/dist/providers/dynamic-provider.factory.d.ts +0 -17
  166. package/dist/providers/dynamic-provider.factory.js +0 -138
  167. package/dist/providers/dynamic-provider.factory.js.map +0 -1
  168. package/dist/providers/logger.adapter.d.ts +0 -7
  169. package/dist/providers/logger.adapter.js +0 -107
  170. package/dist/providers/logger.adapter.js.map +0 -1
  171. package/dist/services/agent-loader.service.d.ts +0 -35
  172. package/dist/services/agent-loader.service.js +0 -623
  173. package/dist/services/agent-loader.service.js.map +0 -1
  174. package/dist/services/auth.service.d.ts +0 -9
  175. package/dist/services/auth.service.js +0 -47
  176. package/dist/services/auth.service.js.map +0 -1
  177. package/dist/services/config-validator.service.d.ts +0 -29
  178. package/dist/services/config-validator.service.js +0 -483
  179. package/dist/services/config-validator.service.js.map +0 -1
  180. package/dist/services/config.service.d.ts +0 -45
  181. package/dist/services/config.service.js +0 -352
  182. package/dist/services/config.service.js.map +0 -1
  183. package/dist/services/document-loader.service.d.ts +0 -26
  184. package/dist/services/document-loader.service.js +0 -186
  185. package/dist/services/document-loader.service.js.map +0 -1
  186. package/dist/services/help.service.d.ts +0 -5
  187. package/dist/services/help.service.js +0 -139
  188. package/dist/services/help.service.js.map +0 -1
  189. package/dist/services/intelligent-compression.service.d.ts +0 -20
  190. package/dist/services/intelligent-compression.service.js +0 -179
  191. package/dist/services/intelligent-compression.service.js.map +0 -1
  192. package/dist/services/mcp-client.service.d.ts +0 -26
  193. package/dist/services/mcp-client.service.js +0 -81
  194. package/dist/services/mcp-client.service.js.map +0 -1
  195. package/dist/services/parallel-processing.service.d.ts +0 -108
  196. package/dist/services/parallel-processing.service.js +0 -333
  197. package/dist/services/parallel-processing.service.js.map +0 -1
  198. package/dist/services/provider-bridge.service.d.ts +0 -35
  199. package/dist/services/provider-bridge.service.js +0 -224
  200. package/dist/services/provider-bridge.service.js.map +0 -1
  201. package/dist/services/remote-agent.service.d.ts +0 -50
  202. package/dist/services/remote-agent.service.js +0 -171
  203. package/dist/services/remote-agent.service.js.map +0 -1
  204. package/dist/services/result-formatter.service.d.ts +0 -27
  205. package/dist/services/result-formatter.service.js +0 -126
  206. package/dist/services/result-formatter.service.js.map +0 -1
  207. package/dist/services/skill-loader.service.d.ts +0 -15
  208. package/dist/services/skill-loader.service.js +0 -278
  209. package/dist/services/skill-loader.service.js.map +0 -1
  210. package/dist/services/skill.service.d.ts +0 -69
  211. package/dist/services/skill.service.js +0 -779
  212. package/dist/services/skill.service.js.map +0 -1
  213. package/dist/services/skill.service.spec.d.ts +0 -1
  214. package/dist/services/skill.service.spec.js +0 -168
  215. package/dist/services/skill.service.spec.js.map +0 -1
  216. package/dist/services/task-management.service.d.ts +0 -71
  217. package/dist/services/task-management.service.js +0 -324
  218. package/dist/services/task-management.service.js.map +0 -1
  219. package/dist/services/template.service.d.ts +0 -61
  220. package/dist/services/template.service.js +0 -416
  221. package/dist/services/template.service.js.map +0 -1
  222. package/dist/services/tool-call.service.d.ts +0 -16
  223. package/dist/services/tool-call.service.js +0 -302
  224. package/dist/services/tool-call.service.js.map +0 -1
  225. package/dist/services/tracing.service.d.ts +0 -197
  226. package/dist/services/tracing.service.js +0 -1267
  227. package/dist/services/tracing.service.js.map +0 -1
  228. package/dist/slack/formatters/message.formatter.d.ts +0 -43
  229. package/dist/slack/formatters/message.formatter.js +0 -505
  230. package/dist/slack/formatters/message.formatter.js.map +0 -1
  231. package/dist/slack/services/slack-file-download.service.d.ts +0 -58
  232. package/dist/slack/services/slack-file-download.service.js +0 -558
  233. package/dist/slack/services/slack-file-download.service.js.map +0 -1
  234. package/dist/slack/slack-bot.d.ts +0 -33
  235. package/dist/slack/slack-bot.js +0 -568
  236. package/dist/slack/slack-bot.js.map +0 -1
  237. package/dist/stderr.logger.d.ts +0 -8
  238. package/dist/stderr.logger.js +0 -26
  239. package/dist/stderr.logger.js.map +0 -1
  240. package/dist/types/usage.types.d.ts +0 -107
  241. package/dist/types/usage.types.js +0 -3
  242. package/dist/types/usage.types.js.map +0 -1
  243. package/dist/utils/config-utils.d.ts +0 -15
  244. package/dist/utils/config-utils.js +0 -69
  245. package/dist/utils/config-utils.js.map +0 -1
  246. package/dist/utils/extract-text.d.ts +0 -1
  247. package/dist/utils/extract-text.js +0 -15
  248. package/dist/utils/extract-text.js.map +0 -1
  249. package/dist/utils/mcp-installer.d.ts +0 -20
  250. package/dist/utils/mcp-installer.js +0 -199
  251. package/dist/utils/mcp-installer.js.map +0 -1
  252. package/dist/utils/project-hash.d.ts +0 -6
  253. package/dist/utils/project-hash.js +0 -70
  254. package/dist/utils/project-hash.js.map +0 -1
  255. package/dist/utils/simple-security.d.ts +0 -3
  256. package/dist/utils/simple-security.js +0 -20
  257. package/dist/utils/simple-security.js.map +0 -1
  258. package/dist/utils/stdin-utils.d.ts +0 -6
  259. package/dist/utils/stdin-utils.js +0 -109
  260. package/dist/utils/stdin-utils.js.map +0 -1
  261. package/dist/utils/template-processor.d.ts +0 -27
  262. package/dist/utils/template-processor.js +0 -395
  263. package/dist/utils/template-processor.js.map +0 -1
  264. package/dist/utils/terminal-message-formatter.d.ts +0 -23
  265. package/dist/utils/terminal-message-formatter.js +0 -136
  266. package/dist/utils/terminal-message-formatter.js.map +0 -1
  267. package/dist/version.d.ts +0 -1
  268. package/dist/version.js +0 -17
  269. package/dist/version.js.map +0 -1
  270. package/dist/workspace.service.d.ts +0 -44
  271. package/dist/workspace.service.js +0 -299
  272. package/dist/workspace.service.js.map +0 -1
  273. package/scripts/backfill-tokens.js +0 -218
  274. package/scripts/postbuild-cli.mjs +0 -88
  275. package/scripts/postinstall-cli.mjs +0 -30
  276. package/templates/agents/default.yaml +0 -490
  277. package/templates/agents/minimal.yaml +0 -16
  278. package/templates/documents/conversation-history-default.hbs +0 -17
  279. package/templates/documents/crewx-manual.md +0 -2278
  280. package/templates/documents/crewx-quick-guide.md +0 -147
  281. package/templates/versions.json +0 -19
@@ -0,0 +1,559 @@
1
+ "use strict";
2
+ /**
3
+ * Slack files command — crewx slack:files [--thread <thread_ts>] [--list] [--clean]
4
+ *
5
+ * Ported 1:1 from packages/cli-bak/src/cli/slack-files.handler.ts.
6
+ * Implements download/list/clean operations without NestJS or @slack/web-api.
7
+ * Uses Node.js built-in https and fetch for Slack API calls.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.handleSlack = handleSlack;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const https = __importStar(require("https"));
47
+ const adapter_slack_1 = require("@crewx/adapter-slack");
48
+ const crewx_cli_1 = require("../bootstrap/crewx-cli");
49
+ const markdown_1 = require("../slack/markdown");
50
+ const file_download_1 = require("../slack/file-download");
51
+ // ─── Entry point ──────────────────────────────────────────────────────────
52
+ /**
53
+ * Handle `crewx slack` / `crewx slack:files` commands.
54
+ * Subcommand is determined by flags: --list → list, --clean → clean, default → download.
55
+ */
56
+ async function handleSlack(args) {
57
+ if (args.includes('--mode') || args.includes('--agent')) {
58
+ return await handleBotMode(args);
59
+ }
60
+ try {
61
+ const subCommand = getSubcommand(args);
62
+ const threadId = getThreadOption(args);
63
+ const botToken = process.env.SLACK_BOT_TOKEN;
64
+ if (!botToken && subCommand === 'download') {
65
+ console.error('\n❌ Error: SLACK_BOT_TOKEN environment variable not found\n');
66
+ console.log('Set your Slack bot token:');
67
+ console.log(' export SLACK_BOT_TOKEN=xoxb-your-token\n');
68
+ process.exit(1);
69
+ }
70
+ switch (subCommand) {
71
+ case 'list':
72
+ await handleListFiles(threadId);
73
+ break;
74
+ case 'clean':
75
+ await handleCleanFiles(threadId);
76
+ break;
77
+ case 'download':
78
+ default:
79
+ await handleDownloadFiles(botToken, threadId);
80
+ break;
81
+ }
82
+ }
83
+ catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+ console.error(`\n❌ Slack files command failed:\n${errorMessage}\n`);
86
+ process.exit(1);
87
+ }
88
+ }
89
+ // ─── Subcommand resolution ───────────────────────────────────────────────
90
+ function getSubcommand(args) {
91
+ if (args.includes('--list') || args.includes('-l'))
92
+ return 'list';
93
+ if (args.includes('--clean'))
94
+ return 'clean';
95
+ return 'download';
96
+ }
97
+ function getThreadOption(args) {
98
+ const idx = args.findIndex(a => a === '--thread' || a === '-t');
99
+ if (idx !== -1 && idx + 1 < args.length) {
100
+ return args[idx + 1];
101
+ }
102
+ return undefined;
103
+ }
104
+ // ─── Download ────────────────────────────────────────────────────────────
105
+ async function handleDownloadFiles(botToken, threadId) {
106
+ if (!threadId) {
107
+ console.error('\n❌ Error: --thread option is required for download\n');
108
+ console.log('Usage: crewx slack:files --thread <thread_ts>\n');
109
+ console.log('Example:');
110
+ console.log(' crewx slack:files --thread 1234567890.123456\n');
111
+ process.exit(1);
112
+ return;
113
+ }
114
+ const validThreadId = threadId;
115
+ console.log(`\n📥 Downloading files from thread: ${validThreadId}\n`);
116
+ const channelId = validThreadId.includes('.')
117
+ ? (validThreadId.split('.')[0] || validThreadId)
118
+ : validThreadId;
119
+ // Fetch conversation history from Slack API
120
+ const messages = await slackConversationsHistory(botToken, channelId, validThreadId);
121
+ if (!messages || messages.length === 0) {
122
+ console.log('⚠️ No messages found in thread\n');
123
+ return;
124
+ }
125
+ // Collect files from messages
126
+ const files = [];
127
+ for (const message of messages) {
128
+ if (message.files) {
129
+ for (const file of message.files) {
130
+ files.push({
131
+ id: file.id,
132
+ name: file.name,
133
+ userId: message.user || 'unknown',
134
+ });
135
+ }
136
+ }
137
+ }
138
+ if (files.length === 0) {
139
+ console.log('ℹ️ No files found in thread\n');
140
+ return;
141
+ }
142
+ console.log(`Found ${files.length} file${files.length > 1 ? 's' : ''} to download:\n`);
143
+ let downloadedCount = 0;
144
+ let skippedCount = 0;
145
+ let failedCount = 0;
146
+ for (const file of files) {
147
+ try {
148
+ const metadata = await downloadSlackFile(botToken, file.id, file.name, validThreadId, channelId, file.userId);
149
+ const wasSkipped = fs.existsSync(metadata.filePath) &&
150
+ fs.statSync(metadata.filePath).mtime < metadata.downloadedAt;
151
+ if (wasSkipped) {
152
+ console.log(` ⏭️ ${file.name} (already exists)`);
153
+ skippedCount++;
154
+ }
155
+ else {
156
+ console.log(` ✅ ${file.name} (${formatFileSize(metadata.fileSize)})`);
157
+ downloadedCount++;
158
+ }
159
+ }
160
+ catch (error) {
161
+ const errorMessage = error instanceof Error ? error.message : String(error);
162
+ console.log(` ❌ ${file.name} - ${errorMessage}`);
163
+ failedCount++;
164
+ }
165
+ }
166
+ console.log(`\n📊 Summary:`);
167
+ if (downloadedCount > 0)
168
+ console.log(` Downloaded: ${downloadedCount} file${downloadedCount > 1 ? 's' : ''}`);
169
+ if (skippedCount > 0)
170
+ console.log(` Skipped: ${skippedCount} file${skippedCount > 1 ? 's' : ''} (already exists)`);
171
+ if (failedCount > 0)
172
+ console.log(` Failed: ${failedCount} file${failedCount > 1 ? 's' : ''}`);
173
+ console.log('');
174
+ }
175
+ // ─── List ────────────────────────────────────────────────────────────────
176
+ async function handleListFiles(threadId) {
177
+ const downloadDir = path.join(process.cwd(), '.crewx', 'slack-files');
178
+ if (threadId) {
179
+ console.log(`\n📋 Files in thread ${threadId}:\n`);
180
+ const files = getThreadFilesMetadata(threadId, downloadDir);
181
+ if (files.length === 0) {
182
+ console.log(' No files downloaded for this thread\n');
183
+ return;
184
+ }
185
+ for (const file of files) {
186
+ console.log(` 📎 ${file.fileName}`);
187
+ console.log(` Size: ${formatFileSize(file.fileSize)}`);
188
+ console.log(` Path: ${file.filePath}`);
189
+ console.log(` Downloaded: ${file.downloadedAt.toLocaleString()}`);
190
+ console.log('');
191
+ }
192
+ console.log(`Total: ${files.length} file${files.length > 1 ? 's' : ''}\n`);
193
+ }
194
+ else {
195
+ console.log('\n📋 All downloaded Slack files:\n');
196
+ if (!fs.existsSync(downloadDir)) {
197
+ console.log(' No files downloaded yet\n');
198
+ return;
199
+ }
200
+ const threadDirs = fs.readdirSync(downloadDir).filter(name => fs.statSync(path.join(downloadDir, name)).isDirectory());
201
+ if (threadDirs.length === 0) {
202
+ console.log(' No files downloaded yet\n');
203
+ return;
204
+ }
205
+ let totalFiles = 0;
206
+ for (const threadDir of threadDirs) {
207
+ const files = getThreadFilesMetadata(threadDir, downloadDir);
208
+ if (files.length > 0) {
209
+ console.log(` Thread: ${threadDir}`);
210
+ for (const file of files) {
211
+ console.log(` 📎 ${file.fileName} (${formatFileSize(file.fileSize)})`);
212
+ totalFiles++;
213
+ }
214
+ console.log('');
215
+ }
216
+ }
217
+ console.log(`Total: ${totalFiles} file${totalFiles > 1 ? 's' : ''} in ${threadDirs.length} thread${threadDirs.length > 1 ? 's' : ''}\n`);
218
+ }
219
+ }
220
+ // ─── Clean ───────────────────────────────────────────────────────────────
221
+ async function handleCleanFiles(threadId) {
222
+ const downloadDir = path.join(process.cwd(), '.crewx', 'slack-files');
223
+ if (threadId) {
224
+ console.log(`\n🧹 Cleaning files for thread ${threadId}...\n`);
225
+ const safeThreadDir = threadId.replace(/:/g, '_');
226
+ const threadDir = path.join(downloadDir, safeThreadDir);
227
+ if (!fs.existsSync(threadDir)) {
228
+ console.log(' No files to clean\n');
229
+ return;
230
+ }
231
+ const files = fs.readdirSync(threadDir);
232
+ for (const file of files) {
233
+ fs.unlinkSync(path.join(threadDir, file));
234
+ }
235
+ fs.rmdirSync(threadDir);
236
+ console.log(`✅ Cleaned ${files.length} file${files.length > 1 ? 's' : ''}\n`);
237
+ }
238
+ else {
239
+ console.log('\n🧹 Cleaning all downloaded files...\n');
240
+ if (!fs.existsSync(downloadDir)) {
241
+ console.log(' No files to clean\n');
242
+ return;
243
+ }
244
+ let totalFiles = 0;
245
+ let totalThreads = 0;
246
+ const threadDirs = fs.readdirSync(downloadDir).filter(name => fs.statSync(path.join(downloadDir, name)).isDirectory());
247
+ for (const threadDir of threadDirs) {
248
+ const threadPath = path.join(downloadDir, threadDir);
249
+ const files = fs.readdirSync(threadPath);
250
+ for (const file of files) {
251
+ fs.unlinkSync(path.join(threadPath, file));
252
+ totalFiles++;
253
+ }
254
+ fs.rmdirSync(threadPath);
255
+ totalThreads++;
256
+ }
257
+ console.log(`✅ Cleaned ${totalFiles} file${totalFiles > 1 ? 's' : ''} from ${totalThreads} thread${totalThreads > 1 ? 's' : ''}\n`);
258
+ }
259
+ }
260
+ // ─── Slack API helpers ───────────────────────────────────────────────────
261
+ /**
262
+ * Call Slack conversations.history API using Node.js https.
263
+ * Returns array of message objects.
264
+ */
265
+ async function slackConversationsHistory(token, channel, latest) {
266
+ const params = new URLSearchParams({
267
+ channel,
268
+ latest,
269
+ inclusive: 'true',
270
+ limit: '100',
271
+ });
272
+ const data = await slackApiGet(token, `conversations.history?${params.toString()}`);
273
+ if (!data.ok) {
274
+ throw new Error(`Slack API error: ${data.error ?? 'unknown_error'}`);
275
+ }
276
+ return data.messages ?? [];
277
+ }
278
+ /**
279
+ * Download a single Slack file and save it to disk.
280
+ */
281
+ async function downloadSlackFile(botToken, fileId, fileName, threadId, channelId, userId) {
282
+ // Get file info
283
+ const infoData = await slackApiGet(botToken, `files.info?file=${fileId}`);
284
+ if (!infoData.ok) {
285
+ throw new Error(`Failed to get file info: ${infoData.error ?? 'unknown_error'}`);
286
+ }
287
+ const fileInfo = infoData.file;
288
+ if (!fileInfo?.url_private) {
289
+ throw new Error('No private download URL available for this file');
290
+ }
291
+ // Build save path
292
+ const sanitized = sanitizeFileName(fileName);
293
+ const uniqueName = `${fileId}${path.extname(sanitized)}`;
294
+ const safeThreadDir = threadId.replace(/:/g, '_');
295
+ const downloadDir = path.join(process.cwd(), '.crewx', 'slack-files');
296
+ const savePath = path.join(downloadDir, safeThreadDir, uniqueName);
297
+ // Duplicate check
298
+ if (fs.existsSync(savePath)) {
299
+ const stats = fs.statSync(savePath);
300
+ return {
301
+ fileId,
302
+ fileName: sanitized,
303
+ filePath: savePath,
304
+ fileSize: stats.size,
305
+ mimeType: fileInfo.mimetype ?? 'application/octet-stream',
306
+ uploadedBy: userId,
307
+ uploadedAt: new Date((fileInfo.timestamp ?? 0) * 1000),
308
+ threadId,
309
+ channelId,
310
+ downloadedAt: new Date(stats.mtime),
311
+ };
312
+ }
313
+ // Fetch file content from Slack private URL
314
+ const fileBuffer = await fetchPrivateUrl(botToken, fileInfo.url_private);
315
+ // Save to disk
316
+ await fs.promises.mkdir(path.dirname(savePath), { recursive: true });
317
+ await fs.promises.writeFile(savePath, fileBuffer);
318
+ return {
319
+ fileId,
320
+ fileName: sanitized,
321
+ filePath: savePath,
322
+ fileSize: fileBuffer.length,
323
+ mimeType: fileInfo.mimetype ?? 'application/octet-stream',
324
+ uploadedBy: userId,
325
+ uploadedAt: new Date((fileInfo.timestamp ?? 0) * 1000),
326
+ threadId,
327
+ channelId,
328
+ downloadedAt: new Date(),
329
+ };
330
+ }
331
+ /**
332
+ * Make a GET request to a Slack API endpoint.
333
+ */
334
+ async function slackApiGet(token, endpoint) {
335
+ return new Promise((resolve, reject) => {
336
+ const options = {
337
+ hostname: 'slack.com',
338
+ path: `/api/${endpoint}`,
339
+ method: 'GET',
340
+ headers: {
341
+ Authorization: `Bearer ${token}`,
342
+ 'Content-Type': 'application/json',
343
+ },
344
+ };
345
+ const req = https.request(options, res => {
346
+ let body = '';
347
+ res.on('data', chunk => { body += chunk; });
348
+ res.on('end', () => {
349
+ try {
350
+ resolve(JSON.parse(body));
351
+ }
352
+ catch {
353
+ reject(new Error(`Failed to parse Slack API response: ${body}`));
354
+ }
355
+ });
356
+ });
357
+ req.on('error', reject);
358
+ req.setTimeout(30000, () => {
359
+ req.destroy();
360
+ reject(new Error('Slack API request timed out'));
361
+ });
362
+ req.end();
363
+ });
364
+ }
365
+ /**
366
+ * Download content from a Slack private URL using Bearer auth.
367
+ */
368
+ async function fetchPrivateUrl(token, url) {
369
+ const parsed = new URL(url);
370
+ return new Promise((resolve, reject) => {
371
+ const options = {
372
+ hostname: parsed.hostname,
373
+ path: parsed.pathname + parsed.search,
374
+ method: 'GET',
375
+ headers: {
376
+ Authorization: `Bearer ${token}`,
377
+ },
378
+ };
379
+ const req = https.request(options, res => {
380
+ if (res.statusCode === 302 || res.statusCode === 301) {
381
+ // Follow redirect
382
+ const location = res.headers.location;
383
+ if (!location) {
384
+ reject(new Error('Redirect without location header'));
385
+ return;
386
+ }
387
+ fetchPrivateUrl(token, location).then(resolve).catch(reject);
388
+ return;
389
+ }
390
+ if (res.statusCode !== 200) {
391
+ reject(new Error(`HTTP ${res.statusCode} while downloading file`));
392
+ return;
393
+ }
394
+ const chunks = [];
395
+ res.on('data', (chunk) => chunks.push(chunk));
396
+ res.on('end', () => resolve(Buffer.concat(chunks)));
397
+ res.on('error', reject);
398
+ });
399
+ req.on('error', reject);
400
+ req.setTimeout(30000, () => {
401
+ req.destroy();
402
+ reject(new Error('File download timed out'));
403
+ });
404
+ req.end();
405
+ });
406
+ }
407
+ // ─── Local file helpers ───────────────────────────────────────────────────
408
+ function getThreadFilesMetadata(threadId, downloadDir) {
409
+ const safeThreadDir = threadId.replace(/:/g, '_');
410
+ const threadDir = path.join(downloadDir, safeThreadDir);
411
+ if (!fs.existsSync(threadDir))
412
+ return [];
413
+ const files = fs.readdirSync(threadDir);
414
+ const metadata = [];
415
+ for (const file of files) {
416
+ try {
417
+ const filePath = path.join(threadDir, file);
418
+ const stats = fs.statSync(filePath);
419
+ metadata.push({
420
+ fileId: '',
421
+ fileName: file,
422
+ filePath,
423
+ fileSize: stats.size,
424
+ mimeType: guessMimeType(file),
425
+ uploadedBy: '',
426
+ uploadedAt: new Date(stats.birthtime),
427
+ threadId,
428
+ channelId: '',
429
+ downloadedAt: new Date(stats.mtime),
430
+ });
431
+ }
432
+ catch {
433
+ // ignore unreadable files
434
+ }
435
+ }
436
+ return metadata;
437
+ }
438
+ function sanitizeFileName(fileName) {
439
+ let safeName = fileName.replace(/[/\\]/g, '_');
440
+ safeName = safeName.replace(/\.\./g, '_');
441
+ safeName = safeName.replace(/[\x00-\x1f\x80-\x9f<>:"|?*]/g, '_');
442
+ safeName = safeName.replace(/^\.+|\.+$/g, '');
443
+ safeName = safeName.trim();
444
+ if (!safeName)
445
+ safeName = 'unnamed_file';
446
+ return safeName;
447
+ }
448
+ function guessMimeType(fileName) {
449
+ const ext = path.extname(fileName).toLowerCase();
450
+ const mimeTypes = {
451
+ '.pdf': 'application/pdf',
452
+ '.png': 'image/png',
453
+ '.jpg': 'image/jpeg',
454
+ '.jpeg': 'image/jpeg',
455
+ '.gif': 'image/gif',
456
+ '.txt': 'text/plain',
457
+ '.md': 'text/markdown',
458
+ '.csv': 'text/csv',
459
+ '.json': 'application/json',
460
+ '.xml': 'application/xml',
461
+ '.zip': 'application/zip',
462
+ '.doc': 'application/msword',
463
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
464
+ '.xls': 'application/vnd.ms-excel',
465
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
466
+ };
467
+ return mimeTypes[ext] ?? 'application/octet-stream';
468
+ }
469
+ function formatFileSize(bytes) {
470
+ if (bytes < 1024)
471
+ return `${bytes}B`;
472
+ if (bytes < 1024 * 1024)
473
+ return `${(bytes / 1024).toFixed(1)}KB`;
474
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
475
+ }
476
+ // ─── Bot mode ────────────────────────────────────────────────────────────
477
+ function parseFlag(args, flag) {
478
+ const idx = args.indexOf(flag);
479
+ if (idx !== -1 && idx + 1 < args.length) {
480
+ return args[idx + 1];
481
+ }
482
+ const prefix = `${flag}=`;
483
+ const found = args.find(a => a.startsWith(prefix));
484
+ return found ? found.slice(prefix.length) : undefined;
485
+ }
486
+ async function handleBotMode(args) {
487
+ const mode = parseFlag(args, '--mode') ?? 'query';
488
+ const agent = parseFlag(args, '--agent');
489
+ if (!agent) {
490
+ console.error('Error: --agent required for bot mode');
491
+ console.error('Usage: crewx slack --mode <query|execute> --agent <agent-id>');
492
+ process.exit(1);
493
+ }
494
+ if (mode !== 'query' && mode !== 'execute') {
495
+ console.error(`Error: invalid mode "${mode}" — must be query or execute`);
496
+ process.exit(1);
497
+ }
498
+ const botToken = process.env.SLACK_BOT_TOKEN;
499
+ const appToken = process.env.SLACK_APP_TOKEN;
500
+ if (!botToken || !appToken) {
501
+ console.error('Error: SLACK_BOT_TOKEN and SLACK_APP_TOKEN environment variables required');
502
+ process.exit(1);
503
+ }
504
+ const configPath = process.env.CREWX_CONFIG ?? './crewx.yaml';
505
+ let crewx;
506
+ try {
507
+ crewx = await (0, crewx_cli_1.createCliCrewx)(configPath);
508
+ }
509
+ catch (err) {
510
+ console.error(`Failed to load config: ${configPath}`);
511
+ console.error(err instanceof Error ? err.message : String(err));
512
+ process.exit(1);
513
+ }
514
+ const adapter = new adapter_slack_1.SlackAdapter();
515
+ await crewx.registerChannelAdapter({
516
+ adapter,
517
+ instanceId: `slack-${agent}`,
518
+ config: { mode: 'socket', botToken, appToken },
519
+ defaultMode: mode,
520
+ defaultAgent: agent,
521
+ onInbound: async (msg) => {
522
+ if (msg.attachments && msg.attachments.length > 0) {
523
+ try {
524
+ await (0, file_download_1.downloadInboundFiles)(botToken, msg.attachments.map(a => ({
525
+ url_private: a.url,
526
+ mimetype: a.mimeType ?? 'application/octet-stream',
527
+ name: a.name ?? 'unnamed',
528
+ })), msg.threadId);
529
+ }
530
+ catch (err) {
531
+ console.error('File download failed:', err instanceof Error ? err.message : String(err));
532
+ }
533
+ }
534
+ const fn = mode === 'execute' ? crewx.execute.bind(crewx) : crewx.query.bind(crewx);
535
+ const result = await fn(`@${agent}`, msg.text, { threadId: msg.threadId, platform: adapter.manifest.platform });
536
+ const rawOutput = result.ok
537
+ ? result.data
538
+ : (result.error?.message ?? 'Error');
539
+ return { accepted: true, output: (0, markdown_1.markdownToMrkdwn)(rawOutput) };
540
+ },
541
+ });
542
+ console.log(`Slack bot started (agent=${agent}, mode=${mode}, instance=slack-${agent})`);
543
+ console.log('Press Ctrl+C to shut down.');
544
+ await new Promise((resolve) => {
545
+ const shutdown = async () => {
546
+ console.log('\nShutting down...');
547
+ try {
548
+ await crewx.stopAllAdapters({ timeoutMs: 5000 });
549
+ }
550
+ catch (err) {
551
+ console.error('Shutdown error:', err instanceof Error ? err.message : String(err));
552
+ }
553
+ resolve();
554
+ };
555
+ process.once('SIGINT', shutdown);
556
+ process.once('SIGTERM', shutdown);
557
+ });
558
+ process.exit(0);
559
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Shared SQLite helper for ps/kill/result CLI commands.
3
+ * Reads from (or writes to) the global ~/.crewx/crewx.db
4
+ * populated by SqliteTracingPlugin.
5
+ */
6
+ import BetterSqlite3 from 'better-sqlite3';
7
+ export type TaskStatus = 'running' | 'success' | 'failed';
8
+ export interface TaskRow {
9
+ id: string;
10
+ agent_id: string;
11
+ prompt: string;
12
+ mode: string;
13
+ status: TaskStatus;
14
+ pid: number | null;
15
+ started_at: string;
16
+ completed_at: string | null;
17
+ result: string | null;
18
+ error: string | null;
19
+ duration_ms: number | null;
20
+ }
21
+ export declare function getDbPath(dbRoot?: string): string;
22
+ export declare function openDb(readonly?: boolean, dbRoot?: string): InstanceType<typeof BetterSqlite3> | null;
23
+ /** Return all tasks with status='running'. */
24
+ export declare function getRunningTasks(dbRoot?: string): TaskRow[];
25
+ /** Return all tasks ordered by started_at desc. */
26
+ export declare function getAllTasks(dbRoot?: string): TaskRow[];
27
+ /** Return a single task by id, or undefined if not found. */
28
+ export declare function getTask(id: string, dbRoot?: string): TaskRow | undefined;
29
+ /** Send SIGTERM to the task's pid and mark it failed. Returns ok/message. */
30
+ export declare function killTask(id: string, dbRoot?: string): {
31
+ ok: boolean;
32
+ message: string;
33
+ };