@closedloop-ai/cli 0.2.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 (70) hide show
  1. package/dist/__tests__/cli.test.js +37 -0
  2. package/dist/__tests__/cli.test.js.map +1 -0
  3. package/dist/__tests__/config.test.js +33 -0
  4. package/dist/__tests__/config.test.js.map +1 -0
  5. package/dist/__tests__/errors.test.js +109 -0
  6. package/dist/__tests__/errors.test.js.map +1 -0
  7. package/dist/__tests__/setup.js +20 -0
  8. package/dist/__tests__/setup.js.map +1 -0
  9. package/dist/__tests__/validation.test.js +124 -0
  10. package/dist/__tests__/validation.test.js.map +1 -0
  11. package/dist/api/auth.js +27 -0
  12. package/dist/api/auth.js.map +1 -0
  13. package/dist/api/feedback.js +70 -0
  14. package/dist/api/feedback.js.map +1 -0
  15. package/dist/api/input.js +60 -0
  16. package/dist/api/input.js.map +1 -0
  17. package/dist/api/usage.js +49 -0
  18. package/dist/api/usage.js.map +1 -0
  19. package/dist/cli.js +156 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/commands/config.js +186 -0
  22. package/dist/commands/config.js.map +1 -0
  23. package/dist/commands/feedback.js +234 -0
  24. package/dist/commands/feedback.js.map +1 -0
  25. package/dist/commands/input.js +370 -0
  26. package/dist/commands/input.js.map +1 -0
  27. package/dist/commands/list.js +81 -0
  28. package/dist/commands/list.js.map +1 -0
  29. package/dist/commands/status.js +93 -0
  30. package/dist/commands/status.js.map +1 -0
  31. package/dist/commands/submit.js +225 -0
  32. package/dist/commands/submit.js.map +1 -0
  33. package/dist/commands/usage.js +168 -0
  34. package/dist/commands/usage.js.map +1 -0
  35. package/dist/config/constants.js +37 -0
  36. package/dist/config/constants.js.map +1 -0
  37. package/dist/config/index.js +84 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/types/index.js +3 -0
  40. package/dist/types/index.js.map +1 -0
  41. package/dist/utils/config.js +55 -0
  42. package/dist/utils/config.js.map +1 -0
  43. package/dist/utils/errors.js +70 -0
  44. package/dist/utils/errors.js.map +1 -0
  45. package/dist/utils/validation.js +133 -0
  46. package/dist/utils/validation.js.map +1 -0
  47. package/jest.config.js +14 -0
  48. package/package.json +42 -0
  49. package/src/__tests__/cli.test.ts +44 -0
  50. package/src/__tests__/config.test.ts +38 -0
  51. package/src/__tests__/errors.test.ts +132 -0
  52. package/src/__tests__/setup.ts +21 -0
  53. package/src/__tests__/validation.test.ts +156 -0
  54. package/src/api/auth.ts +21 -0
  55. package/src/api/feedback.ts +64 -0
  56. package/src/api/input.ts +55 -0
  57. package/src/api/usage.ts +45 -0
  58. package/src/cli.ts +158 -0
  59. package/src/commands/config.ts +192 -0
  60. package/src/commands/feedback.ts +250 -0
  61. package/src/commands/input.ts +389 -0
  62. package/src/commands/list.ts +84 -0
  63. package/src/commands/status.ts +96 -0
  64. package/src/commands/submit.ts +214 -0
  65. package/src/config/constants.ts +36 -0
  66. package/src/config/index.ts +59 -0
  67. package/src/types/index.ts +119 -0
  68. package/src/utils/errors.ts +98 -0
  69. package/src/utils/validation.ts +166 -0
  70. package/tsconfig.json +25 -0
@@ -0,0 +1,84 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { table } from 'table';
4
+ import { listFeedbacks } from '../api/feedback';
5
+ import { CommandOptions, FeedbackData, ListResponse } from '../types';
6
+ import { CONFIG, MESSAGES } from '../config/constants';
7
+
8
+ export const listCommand = new Command('list')
9
+ .description('List customer inputs and generated AI feedback')
10
+ .option('-p, --page <number>', 'Page number to show', '1')
11
+ .option('-l, --limit <number>', 'How many items to show per page', '20')
12
+ .option('-s, --status <status>', 'Show only items with this status (processing, completed, failed)')
13
+ .option('--new', 'Show only inputs that are still being processed')
14
+ .option('--json', 'Output in JSON format')
15
+ .action(async (options: CommandOptions) => {
16
+ try {
17
+ const params: CommandOptions = {
18
+ page: options.page || 1,
19
+ limit: options.limit || CONFIG.DEFAULT_PAGE_SIZE
20
+ };
21
+
22
+ if (options.status) {
23
+ params.status = options.status;
24
+ }
25
+
26
+ const result = options.new
27
+ ? await listFeedbacks(params, true) // new feedbacks
28
+ : await listFeedbacks(params, false); // processed feedbacks
29
+
30
+ const feedbacks = result.feedbacks || result.data || [];
31
+ if (feedbacks.length === 0) {
32
+ if (options.json) {
33
+ console.log(JSON.stringify({ success: true, data: [], pagination: result.pagination || {} }, null, 2));
34
+ } else {
35
+ console.log(chalk.yellow('No customer inputs found.'));
36
+ }
37
+ return;
38
+ }
39
+
40
+ if (options.json) {
41
+ console.log(JSON.stringify({ success: true, data: feedbacks, pagination: result.pagination || {} }, null, 2));
42
+ return;
43
+ }
44
+
45
+ // Create table data
46
+ const tableData = [
47
+ ['ID', 'Title', 'Status', 'AI Feedback', 'Credits Used', 'Submitted']
48
+ ];
49
+
50
+ feedbacks.forEach((item: any) => {
51
+ const statusColor = item.status === 'completed' ? 'green' :
52
+ item.status === 'processing' ? 'yellow' :
53
+ item.status === 'pending_step2' ? 'yellow' :
54
+ item.status === 'failed' ? 'red' :
55
+ item.status === 'new' ? 'blue' :
56
+ item.status === 'in_review' ? 'yellow' :
57
+ item.status === 'addressed' ? 'green' :
58
+ item.status === 'archived' ? 'gray' : 'red';
59
+
60
+ tableData.push([
61
+ item.id,
62
+ item.title || 'Untitled',
63
+ chalk[statusColor](item.status || 'unknown'),
64
+ (item.feedbackCount || 0).toString(),
65
+ (item.creditsConsumed || 0).toString(),
66
+ item.timestamp ? new Date(item.timestamp).toLocaleString() : 'Unknown'
67
+ ]);
68
+ });
69
+
70
+ console.log(chalk.blue('\n📋 Customer Inputs & AI Feedback:'));
71
+ console.log(table(tableData));
72
+
73
+ // Pagination info
74
+ const pagination = result.pagination || {};
75
+ if (pagination.pages > 1) {
76
+ console.log(chalk.gray(`\nPage ${pagination.page} of ${pagination.pages} (${pagination.total} total items)`));
77
+ console.log(chalk.gray('Use --page <number> to see more'));
78
+ }
79
+
80
+ } catch (error: any) {
81
+ console.error(chalk.red('Error:'), error.message);
82
+ process.exit(1);
83
+ }
84
+ });
@@ -0,0 +1,96 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { getFeedbackStatus } from '../api/feedback';
5
+ import { CommandOptions, FeedbackData } from '../types';
6
+ import { CONFIG, MESSAGES } from '../config/constants';
7
+
8
+ export const statusCommand = new Command('status')
9
+ .description('Check processing status of a feedback submission')
10
+ .argument('<id>', 'Feedback submission ID')
11
+ .option('-w, --watch', 'Watch for status changes')
12
+ .option('--json', 'Output in JSON format')
13
+ .action(async (id, options: CommandOptions) => {
14
+ try {
15
+ if (options.watch) {
16
+ // Watch mode - check status every few seconds
17
+ const spinner = ora('Watching for status changes...').start();
18
+
19
+ const checkStatus = async () => {
20
+ try {
21
+ const result = await getFeedbackStatus(id);
22
+
23
+ if (result.status === 'completed') {
24
+ spinner.succeed(chalk.green(`✅ Processing completed! Generated ${result.feedbackCount} insights`));
25
+ process.exit(0);
26
+ } else if (result.status === 'failed') {
27
+ spinner.fail(chalk.red('❌ Processing failed'));
28
+ process.exit(1);
29
+ } else {
30
+ spinner.text = `Processing... (${result.status})`;
31
+ }
32
+ } catch (error: any) {
33
+ spinner.fail(chalk.red('Error checking status:') + ' ' + error.message);
34
+ process.exit(1);
35
+ }
36
+ };
37
+
38
+ // Check immediately
39
+ await checkStatus();
40
+
41
+ // Then check every few seconds
42
+ const interval = setInterval(checkStatus, CONFIG.POLL_INTERVAL_MS);
43
+
44
+ // Stop watching on Ctrl+C
45
+ process.on('SIGINT', () => {
46
+ clearInterval(interval);
47
+ spinner.stop();
48
+ console.log(chalk.yellow('\nStopped watching.'));
49
+ process.exit(0);
50
+ });
51
+
52
+ } else {
53
+ // Single check
54
+ const spinner = ora('Checking status...').start();
55
+
56
+ const result = await getFeedbackStatus(id);
57
+
58
+ spinner.stop();
59
+
60
+ if (options.json) {
61
+ console.log(JSON.stringify({ success: true, data: result }, null, 2));
62
+ return;
63
+ }
64
+
65
+ console.log(chalk.blue('\n📊 Status Information:'));
66
+ console.log(chalk.gray('─'.repeat(40)));
67
+ console.log(chalk.blue('ID:'), result.id);
68
+ console.log(chalk.blue('Title:'), result.signal_title || result.title);
69
+ console.log(chalk.blue('Status:'),
70
+ result.status === 'completed' ? chalk.green(result.status) :
71
+ result.status === 'processing' ? chalk.yellow(result.status) :
72
+ result.status === 'pending_step2' ? chalk.yellow('pending') :
73
+ result.status === 'failed' ? chalk.red(result.status) :
74
+ result.status === 'new' ? chalk.blue(result.status) :
75
+ result.status === 'in_review' ? chalk.yellow(result.status) :
76
+ result.status === 'addressed' ? chalk.green(result.status) :
77
+ result.status === 'archived' ? chalk.gray(result.status) :
78
+ chalk.red(result.status)
79
+ );
80
+ console.log(chalk.blue('Created:'), result.created_at ? new Date(result.created_at).toLocaleString() : 'Unknown');
81
+ console.log(chalk.blue('Updated:'), result.updated_at ? new Date(result.updated_at).toLocaleString() : 'Unknown');
82
+ console.log(chalk.blue('Credits Used:'), result.creditsConsumed || 'Unknown');
83
+ console.log(chalk.blue('AI Feedback Generated:'), result.feedbackCount || 0);
84
+
85
+ if (result.status === 'completed' && (result.feedbackCount || 0) > 0) {
86
+ console.log(chalk.green('\n🎯 Use "cl list" to see the generated AI feedback!'));
87
+ } else if (result.status === 'processing') {
88
+ console.log(chalk.yellow('\n⏳ Still processing... Use "cl status <id> --watch" to monitor'));
89
+ }
90
+ }
91
+
92
+ } catch (error: any) {
93
+ console.error(chalk.red('Error:'), error.message);
94
+ process.exit(1);
95
+ }
96
+ });
@@ -0,0 +1,214 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import inquirer from 'inquirer';
5
+ import { submitFeedback, getFeedbackStatus } from '../api/feedback';
6
+ import { CommandOptions, InputData } from '../types';
7
+ import { CONFIG, MESSAGES } from '../config/constants';
8
+
9
+ export const submitCommand = new Command('submit')
10
+ .description('Submit customer input for AI analysis and insight generation')
11
+ .argument('[content]', 'Customer input content to analyze')
12
+ .option('-t, --title <title>', 'Title for this customer input')
13
+ .option('-u, --url <url>', 'Where this input came from (support ticket, survey, etc.)')
14
+ .option('-c, --customer <id>', 'Customer identifier')
15
+ .option('-n, --name <name>', 'Name of person who provided the input')
16
+ .option('-e, --email <email>', 'Email of person who provided the input')
17
+ .option('--interactive', 'Fill in details step by step')
18
+ .option('-w, --wait', 'Wait until processing is completed')
19
+ .option('--json', 'Output in JSON format')
20
+ .action(async (content, options: CommandOptions) => {
21
+ try {
22
+ let feedbackData: Partial<InputData> = {};
23
+
24
+ if (options.interactive || !content) {
25
+ // Interactive mode
26
+ const answers = await inquirer.prompt([
27
+ {
28
+ type: 'input',
29
+ name: 'content',
30
+ message: 'What customer input would you like to analyze?',
31
+ validate: (input) => input.length >= 10 || 'Input must be at least 10 characters',
32
+ default: content
33
+ },
34
+ {
35
+ type: 'input',
36
+ name: 'title',
37
+ message: 'What should we call this input? (optional):',
38
+ default: options.title
39
+ },
40
+ {
41
+ type: 'input',
42
+ name: 'sourceUrl',
43
+ message: 'Where did this come from? (support ticket, survey, etc.) (optional):',
44
+ default: options.url
45
+ },
46
+ {
47
+ type: 'input',
48
+ name: 'customerId',
49
+ message: 'Customer identifier (optional):',
50
+ default: options.customer
51
+ },
52
+ {
53
+ type: 'input',
54
+ name: 'reporterName',
55
+ message: 'Who provided this input? (optional):',
56
+ default: options.name
57
+ },
58
+ {
59
+ type: 'input',
60
+ name: 'reporterEmail',
61
+ message: 'Their email address (optional):',
62
+ default: options.email
63
+ }
64
+ ]);
65
+
66
+ feedbackData = {
67
+ content: answers.content,
68
+ title: answers.title,
69
+ source_url: answers.sourceUrl,
70
+ customer_id: answers.customerId,
71
+ reporter_name: answers.reporterName,
72
+ reporter_email: answers.reporterEmail
73
+ };
74
+ } else {
75
+ // Command line mode
76
+ feedbackData = {
77
+ content,
78
+ title: options.title,
79
+ source_url: options.url,
80
+ customer_id: options.customer,
81
+ reporter_name: options.name,
82
+ reporter_email: options.email
83
+ };
84
+ }
85
+
86
+ const spinner = ora('Submitting customer input for AI analysis...').start();
87
+
88
+ const result = await submitFeedback(feedbackData);
89
+
90
+ if (options.wait) {
91
+ // Wait for processing to complete
92
+ if (options.json) {
93
+ // For JSON, we'll output the final result after waiting
94
+ } else {
95
+ console.log(chalk.green('✅ Customer input submitted successfully!'));
96
+ console.log(chalk.blue(`Submission ID: ${result.id}`));
97
+ }
98
+
99
+ const waitSpinner = ora('Processing...').start();
100
+ let attempts = 0;
101
+ const maxAttempts = CONFIG.MAX_RETRY_ATTEMPTS;
102
+
103
+ while (attempts < maxAttempts) {
104
+ await new Promise(resolve => setTimeout(resolve, CONFIG.POLL_INTERVAL_MS));
105
+
106
+ try {
107
+ const statusResult = await getFeedbackStatus(result.id);
108
+
109
+ if (statusResult.status === 'completed') {
110
+ waitSpinner.succeed('Processing completed!');
111
+
112
+ if (options.json) {
113
+ console.log(JSON.stringify({ success: true, data: statusResult }, null, 2));
114
+ } else {
115
+ console.log(chalk.blue(`Feedback generated: ${statusResult.feedbackCount || 0}`));
116
+ console.log(chalk.blue(`Credits used: ${statusResult.creditsConsumed || 0}`));
117
+ if ((statusResult.feedbackCount || 0) > 0) {
118
+ console.log(chalk.gray('Run "cl list" to see all feedback'));
119
+ }
120
+ }
121
+ break;
122
+ } else if (statusResult.status === 'failed') {
123
+ waitSpinner.fail('Processing failed');
124
+ if (options.json) {
125
+ console.log(JSON.stringify({ success: false, error: 'Processing failed', data: statusResult }, null, 2));
126
+ } else {
127
+ console.log(chalk.red('\n❌ Processing failed. Check the status for more details.'));
128
+ }
129
+ break;
130
+ } else {
131
+ // Still processing
132
+ waitSpinner.text = `Processing... (${statusResult.status})`;
133
+ }
134
+ } catch (error) {
135
+ // Continue waiting on API errors
136
+ waitSpinner.text = 'Processing... (checking status)';
137
+ }
138
+
139
+ attempts++;
140
+ }
141
+
142
+ if (attempts >= maxAttempts) {
143
+ waitSpinner.warn('Processing is taking longer than expected');
144
+ if (options.json) {
145
+ console.log(JSON.stringify({ success: false, error: 'Processing timeout', data: { id: result.id, message: 'Processing is taking longer than expected' } }, null, 2));
146
+ } else {
147
+ console.log(chalk.yellow('\n⏳ Processing is still in progress. You can check status manually:'));
148
+ console.log(chalk.gray(` cl status ${result.id}`));
149
+ }
150
+ }
151
+ } else {
152
+ if (options.json) {
153
+ console.log(JSON.stringify({ success: true, data: result }, null, 2));
154
+ } else {
155
+ console.log(chalk.green('✅ Customer input submitted successfully!'));
156
+ console.log(chalk.blue(`Submission ID: ${result.id}`));
157
+ console.log(chalk.gray('Run "cl status ' + result.id + '" to check progress'));
158
+ console.log(chalk.gray('Or use "cl submit --wait" to wait for completion'));
159
+ }
160
+ }
161
+
162
+ } catch (error: any) {
163
+ if (options.json) {
164
+ console.log(JSON.stringify({
165
+ success: false,
166
+ error: error.message,
167
+ details: error.response?.data || 'Unknown error occurred'
168
+ }, null, 2));
169
+ } else {
170
+ // User-friendly error messages
171
+ if (error.response?.status === 400) {
172
+ const errorData = error.response.data;
173
+ if (errorData?.code === 'VALIDATION_ERROR') {
174
+ console.error(chalk.red('❌ Validation Error:'), errorData.error);
175
+ console.log(chalk.gray('\n💡 Common issues:'));
176
+ console.log(chalk.gray(' • Content must be at least 10 characters'));
177
+ console.log(chalk.gray(' • Check that all required fields are provided'));
178
+ console.log(chalk.gray(' • Ensure API key is valid: cl config'));
179
+ } else if (errorData?.code === 'PROCESSING_ERROR') {
180
+ console.error(chalk.red('❌ Processing Error:'), errorData.error);
181
+ console.log(chalk.gray('\n💡 This usually means:'));
182
+ console.log(chalk.gray(' • The content could not be processed by our AI'));
183
+ console.log(chalk.gray(' • Try rephrasing your input'));
184
+ console.log(chalk.gray(' • Make sure the content is clear and meaningful'));
185
+ console.log(chalk.gray(' • Check API key: cl config'));
186
+ } else {
187
+ console.error(chalk.red('❌ Bad Request:'), errorData?.error || 'Invalid input provided');
188
+ console.log(chalk.gray('\n💡 Try:'));
189
+ console.log(chalk.gray(' • Check your input content'));
190
+ console.log(chalk.gray(' • Verify API key: cl config'));
191
+ console.log(chalk.gray(' • Get help: cl submit --help'));
192
+ }
193
+ } else if (error.response?.status === 401) {
194
+ console.error(chalk.red('❌ Authentication Error:'), 'Invalid API key');
195
+ console.log(chalk.gray('\n💡 To fix:'));
196
+ console.log(chalk.gray(' 1. Get your free API key at: https://closedloop.sh'));
197
+ console.log(chalk.gray(' 2. Run: cl config --set-api-key <your-key>'));
198
+ } else if (error.response?.status === 429) {
199
+ console.error(chalk.red('❌ Rate Limited:'), 'Too many requests');
200
+ console.log(chalk.gray('\n💡 Wait a moment and try again'));
201
+ } else if (error.response?.status >= 500) {
202
+ console.error(chalk.red('❌ Server Error:'), 'Our servers are having issues');
203
+ console.log(chalk.gray('\n💡 Try again in a few minutes'));
204
+ } else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
205
+ console.error(chalk.red('❌ Connection Error:'), 'Cannot connect to ClosedLoop servers');
206
+ console.log(chalk.gray('\n💡 Check your internet connection and try again'));
207
+ } else {
208
+ console.error(chalk.red('❌ Error:'), error.message);
209
+ console.log(chalk.gray('\n💡 Get help: cl --help'));
210
+ }
211
+ }
212
+ process.exit(1);
213
+ }
214
+ });
@@ -0,0 +1,36 @@
1
+ export const CONFIG = {
2
+ API_BASE_URL: process.env.CLOSEDLOOP_API_URL || 'https://mcp.closedloop.sh',
3
+ USER_AGENT: 'closedloop-cli/0.2.0',
4
+ MAX_RETRY_ATTEMPTS: 100,
5
+ POLL_INTERVAL_MS: 3000,
6
+ DEFAULT_PAGE_SIZE: 20,
7
+ MAX_PAGE_SIZE: 100,
8
+ MIN_API_KEY_LENGTH: 10,
9
+ MAX_CONTENT_LENGTH: 10000,
10
+ TIMEOUT_MS: 30000
11
+ } as const;
12
+
13
+ export const MESSAGES = {
14
+ NO_API_KEY: `🔑 No API key configured!
15
+
16
+ To get started:
17
+ 1. Go to https://closedloop.sh
18
+ 2. Sign up for a free account
19
+ 3. Generate your API key
20
+ 4. Run: cl config --set-api-key <your-key>
21
+
22
+ Get your free API key at: https://closedloop.sh`,
23
+
24
+ INVALID_API_KEY: '❌ Invalid API key. Get your free API key at https://closedloop.sh',
25
+ CONNECTION_ERROR: '❌ Connection Error: Cannot connect to ClosedLoop AI servers',
26
+ CHECK_INTERNET: 'Check your internet connection and try again',
27
+ GET_HELP: 'Get help: cl --help',
28
+ PROCESSING_ERROR: '❌ Processing Error: Unable to process feedback',
29
+ VALIDATION_ERROR: '❌ Validation Error:',
30
+ UNKNOWN_ERROR: 'Unknown error occurred'
31
+ } as const;
32
+
33
+ export const URLS = {
34
+ CLOSEDLOOP_HOME: 'https://closedloop.sh',
35
+ GET_API_KEY: 'https://closedloop.sh'
36
+ } as const;
@@ -0,0 +1,59 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+
5
+ interface Config {
6
+ apiKey?: string;
7
+ baseUrl?: string;
8
+ }
9
+
10
+ class ConfigManager {
11
+ private configPath: string;
12
+ private config: Config = {};
13
+
14
+ constructor() {
15
+ this.configPath = path.join(os.homedir(), '.closedloop', 'config.json');
16
+ this.load();
17
+ }
18
+
19
+ private load(): void {
20
+ try {
21
+ if (fs.existsSync(this.configPath)) {
22
+ const data = fs.readFileSync(this.configPath, 'utf8');
23
+ this.config = JSON.parse(data);
24
+ }
25
+ } catch (error) {
26
+ // Ignore config load errors
27
+ }
28
+ }
29
+
30
+ private save(): void {
31
+ try {
32
+ const dir = path.dirname(this.configPath);
33
+ if (!fs.existsSync(dir)) {
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ }
36
+ fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
37
+ } catch (error) {
38
+ throw new Error('Failed to save configuration');
39
+ }
40
+ }
41
+
42
+ get(key?: keyof Config): any {
43
+ if (key) {
44
+ return this.config[key];
45
+ }
46
+ return { ...this.config };
47
+ }
48
+
49
+ set(key: keyof Config, value: any): void {
50
+ this.config[key] = value;
51
+ this.save();
52
+ }
53
+
54
+ getConfigPath(): string {
55
+ return fs.existsSync(this.configPath) ? this.configPath : 'Not created yet';
56
+ }
57
+ }
58
+
59
+ export const config = new ConfigManager();
@@ -0,0 +1,119 @@
1
+ export interface CommandOptions {
2
+ page?: number;
3
+ limit?: number;
4
+ json?: boolean;
5
+ wait?: boolean;
6
+ interactive?: boolean;
7
+ // Config options
8
+ setApiKey?: string;
9
+ setTeam?: string;
10
+ apiKey?: string;
11
+ team?: string;
12
+ // Input options
13
+ title?: string;
14
+ url?: string;
15
+ customer?: string;
16
+ name?: string;
17
+ email?: string;
18
+ // Feedback options
19
+ status?: string;
20
+ search?: string;
21
+ new?: boolean;
22
+ watch?: boolean;
23
+ }
24
+
25
+ export interface ApiResponse<T> {
26
+ success: boolean;
27
+ data: T;
28
+ error?: string;
29
+ pagination?: {
30
+ page: number;
31
+ limit: number;
32
+ total: number;
33
+ pages: number;
34
+ };
35
+ }
36
+
37
+ export interface InputData {
38
+ id: string;
39
+ title?: string;
40
+ content: string;
41
+ source_url?: string;
42
+ source_id?: string;
43
+ source_timestamp?: string;
44
+ customer_id?: string;
45
+ reporter_name?: string;
46
+ reporter_email?: string;
47
+ language?: string;
48
+ metadata?: any;
49
+ tags?: string[];
50
+ status: string;
51
+ created_at: string;
52
+ updated_at: string;
53
+ claimed_at?: string;
54
+ retry_count?: number;
55
+ error_message?: string;
56
+ feedback_count: number;
57
+ credits_consumed: number;
58
+ }
59
+
60
+ export interface FeedbackData {
61
+ id: string;
62
+ title?: string;
63
+ clarity?: string;
64
+ deal_blocker: boolean;
65
+ pain_point?: string;
66
+ workaround?: string;
67
+ competitor_gap?: string;
68
+ willingness_to_pay?: string;
69
+ use_case?: string;
70
+ feature_area?: string;
71
+ source_url?: string;
72
+ timestamp: string;
73
+ input_id?: string;
74
+ severity?: string;
75
+ status?: string;
76
+ source?: string;
77
+ // For backward compatibility
78
+ signal_title?: string;
79
+ created_at?: string;
80
+ updated_at?: string;
81
+ feedbackCount?: number;
82
+ creditsConsumed?: number;
83
+ }
84
+
85
+ export interface UsageData {
86
+ id: string;
87
+ input_id: string;
88
+ title?: string;
89
+ credits_consumed: number;
90
+ feedback_count: number;
91
+ created_at: string;
92
+ updated_at: string;
93
+ }
94
+
95
+ export interface ConfigData {
96
+ apiKey?: string;
97
+ baseUrl?: string;
98
+ }
99
+
100
+ export interface PaginationData {
101
+ page: number;
102
+ limit: number;
103
+ total: number;
104
+ pages: number;
105
+ }
106
+
107
+ export interface ListResponse<T> {
108
+ data: T[];
109
+ pagination: PaginationData;
110
+ // For backward compatibility
111
+ inputs?: T[];
112
+ feedbacks?: T[];
113
+ }
114
+
115
+ export interface ErrorResponse {
116
+ success: false;
117
+ error: string;
118
+ details?: any;
119
+ }