@operor/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 (62) hide show
  1. package/README.md +76 -0
  2. package/dist/config-Bn2pbORi.js +34 -0
  3. package/dist/config-Bn2pbORi.js.map +1 -0
  4. package/dist/converse-C_PB7-JH.js +142 -0
  5. package/dist/converse-C_PB7-JH.js.map +1 -0
  6. package/dist/doctor-98gPl743.js +122 -0
  7. package/dist/doctor-98gPl743.js.map +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +2268 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/llm-override-BIQl0V6H.js +445 -0
  12. package/dist/llm-override-BIQl0V6H.js.map +1 -0
  13. package/dist/reset-DT8SBgFS.js +87 -0
  14. package/dist/reset-DT8SBgFS.js.map +1 -0
  15. package/dist/simulate-BKv62GJc.js +144 -0
  16. package/dist/simulate-BKv62GJc.js.map +1 -0
  17. package/dist/status-D6LIZvQa.js +82 -0
  18. package/dist/status-D6LIZvQa.js.map +1 -0
  19. package/dist/test-DYjkxbtK.js +177 -0
  20. package/dist/test-DYjkxbtK.js.map +1 -0
  21. package/dist/test-suite-D8H_5uKs.js +209 -0
  22. package/dist/test-suite-D8H_5uKs.js.map +1 -0
  23. package/dist/utils-BuV4q7f6.js +11 -0
  24. package/dist/utils-BuV4q7f6.js.map +1 -0
  25. package/dist/vibe-Bl_js3Jo.js +395 -0
  26. package/dist/vibe-Bl_js3Jo.js.map +1 -0
  27. package/package.json +43 -0
  28. package/src/commands/analytics.ts +408 -0
  29. package/src/commands/chat.ts +310 -0
  30. package/src/commands/config.ts +34 -0
  31. package/src/commands/converse.ts +182 -0
  32. package/src/commands/doctor.ts +154 -0
  33. package/src/commands/history.ts +60 -0
  34. package/src/commands/init.ts +163 -0
  35. package/src/commands/kb.ts +429 -0
  36. package/src/commands/llm-override.ts +480 -0
  37. package/src/commands/reset.ts +72 -0
  38. package/src/commands/simulate.ts +187 -0
  39. package/src/commands/status.ts +112 -0
  40. package/src/commands/test-suite.ts +247 -0
  41. package/src/commands/test.ts +177 -0
  42. package/src/commands/vibe.ts +478 -0
  43. package/src/config.ts +127 -0
  44. package/src/index.ts +190 -0
  45. package/src/log-timestamps.ts +26 -0
  46. package/src/setup.ts +712 -0
  47. package/src/start.ts +573 -0
  48. package/src/utils.ts +6 -0
  49. package/templates/agents/_defaults/SOUL.md +20 -0
  50. package/templates/agents/_defaults/USER.md +16 -0
  51. package/templates/agents/customer-support/IDENTITY.md +6 -0
  52. package/templates/agents/customer-support/INSTRUCTIONS.md +79 -0
  53. package/templates/agents/customer-support/SOUL.md +26 -0
  54. package/templates/agents/faq-bot/IDENTITY.md +6 -0
  55. package/templates/agents/faq-bot/INSTRUCTIONS.md +53 -0
  56. package/templates/agents/faq-bot/SOUL.md +19 -0
  57. package/templates/agents/sales/IDENTITY.md +6 -0
  58. package/templates/agents/sales/INSTRUCTIONS.md +67 -0
  59. package/templates/agents/sales/SOUL.md +20 -0
  60. package/tsconfig.json +9 -0
  61. package/tsdown.config.ts +13 -0
  62. package/vitest.config.ts +8 -0
package/src/config.ts ADDED
@@ -0,0 +1,127 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ const ENV_PATH = resolve(process.cwd(), '.env');
5
+
6
+ export interface AppConfig {
7
+ // LLM
8
+ LLM_PROVIDER?: string;
9
+ LLM_API_KEY?: string;
10
+ LLM_MODEL?: string;
11
+ LLM_BASE_URL?: string;
12
+ // Channel
13
+ CHANNEL?: string;
14
+ // Telegram
15
+ TELEGRAM_BOT_TOKEN?: string;
16
+ // Skills
17
+ SKILLS_ENABLED?: string;
18
+ // WATI
19
+ WATI_API_TOKEN?: string;
20
+ WATI_TENANT_ID?: string;
21
+ WATI_WEBHOOK_PORT?: string;
22
+ // Memory
23
+ MEMORY_TYPE?: string;
24
+ MEMORY_DB_PATH?: string;
25
+ // Intent
26
+ INTENT_CLASSIFIER?: string;
27
+ // Knowledge Base
28
+ KB_ENABLED?: string;
29
+ KB_DB_PATH?: string;
30
+ KB_EMBEDDING_PROVIDER?: string;
31
+ KB_EMBEDDING_MODEL?: string;
32
+ KB_EMBEDDING_API_KEY?: string;
33
+ KB_RENDER_JS?: string;
34
+ KB_CHUNK_SIZE?: string;
35
+ KB_CHUNK_OVERLAP?: string;
36
+ // Training Mode
37
+ TRAINING_MODE_ENABLED?: string;
38
+ TRAINING_MODE_WHITELIST?: string;
39
+ // Training Copilot
40
+ COPILOT_ENABLED?: string;
41
+ COPILOT_DB_PATH?: string;
42
+ COPILOT_TRACKING_THRESHOLD?: string;
43
+ COPILOT_CLUSTER_THRESHOLD?: string;
44
+ COPILOT_DIGEST_INTERVAL?: string;
45
+ COPILOT_DIGEST_MAX_ITEMS?: string;
46
+ COPILOT_AUTO_SUGGEST?: string;
47
+ // Analytics
48
+ ANALYTICS_ENABLED?: string;
49
+ ANALYTICS_DB_PATH?: string;
50
+ ANALYTICS_DIGEST_ENABLED?: string;
51
+ ANALYTICS_DIGEST_SCHEDULE?: string;
52
+ ANALYTICS_DIGEST_TIME?: string;
53
+ [key: string]: string | undefined;
54
+ }
55
+
56
+ export function configExists(): boolean {
57
+ return existsSync(ENV_PATH);
58
+ }
59
+
60
+ export function readConfig(): AppConfig {
61
+ if (!existsSync(ENV_PATH)) return {};
62
+ const content = readFileSync(ENV_PATH, 'utf-8');
63
+ const config: AppConfig = {};
64
+ for (const line of content.split('\n')) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed || trimmed.startsWith('#')) continue;
67
+ const eqIndex = trimmed.indexOf('=');
68
+ if (eqIndex === -1) continue;
69
+ const key = trimmed.slice(0, eqIndex).trim();
70
+ const value = trimmed.slice(eqIndex + 1).trim();
71
+ config[key] = value;
72
+ }
73
+ return config;
74
+ }
75
+
76
+ export function writeConfig(config: AppConfig): void {
77
+ const lines: string[] = [
78
+ '# Operor Configuration',
79
+ '# Generated by operor setup',
80
+ '',
81
+ ];
82
+
83
+ const sections: { header: string; keys: string[] }[] = [
84
+ { header: 'LLM', keys: ['LLM_PROVIDER', 'LLM_API_KEY', 'LLM_MODEL', 'LLM_BASE_URL'] },
85
+ { header: 'Channel', keys: ['CHANNEL'] },
86
+ { header: 'Telegram', keys: ['TELEGRAM_BOT_TOKEN'] },
87
+ { header: 'Skills', keys: ['SKILLS_ENABLED'] },
88
+ { header: 'WATI', keys: ['WATI_API_TOKEN', 'WATI_TENANT_ID', 'WATI_WEBHOOK_PORT'] },
89
+ { header: 'Memory', keys: ['MEMORY_TYPE', 'MEMORY_DB_PATH'] },
90
+ { header: 'Intent', keys: ['INTENT_CLASSIFIER'] },
91
+ { header: 'Knowledge Base', keys: ['KB_ENABLED', 'KB_DB_PATH', 'KB_EMBEDDING_PROVIDER', 'KB_EMBEDDING_MODEL', 'KB_EMBEDDING_API_KEY', 'KB_RENDER_JS', 'KB_CHUNK_SIZE', 'KB_CHUNK_OVERLAP'] },
92
+ { header: 'Training Mode', keys: ['TRAINING_MODE_ENABLED', 'TRAINING_MODE_WHITELIST'] },
93
+ { header: 'Training Copilot', keys: ['COPILOT_ENABLED', 'COPILOT_DB_PATH', 'COPILOT_TRACKING_THRESHOLD', 'COPILOT_CLUSTER_THRESHOLD', 'COPILOT_DIGEST_INTERVAL', 'COPILOT_DIGEST_MAX_ITEMS', 'COPILOT_AUTO_SUGGEST'] },
94
+ { header: 'Analytics', keys: ['ANALYTICS_ENABLED', 'ANALYTICS_DB_PATH', 'ANALYTICS_DIGEST_ENABLED', 'ANALYTICS_DIGEST_SCHEDULE', 'ANALYTICS_DIGEST_TIME'] },
95
+ ];
96
+
97
+ const knownKeys = new Set<string>();
98
+ for (const section of sections) {
99
+ const sectionLines: string[] = [];
100
+ for (const key of section.keys) {
101
+ knownKeys.add(key);
102
+ if (config[key]) {
103
+ sectionLines.push(`${key}=${config[key]}`);
104
+ }
105
+ }
106
+ if (sectionLines.length > 0) {
107
+ lines.push(`# ${section.header}`);
108
+ lines.push(...sectionLines);
109
+ lines.push('');
110
+ }
111
+ }
112
+
113
+ // Preserve any extra keys not covered by known sections
114
+ const extraLines: string[] = [];
115
+ for (const [key, value] of Object.entries(config)) {
116
+ if (value && !knownKeys.has(key)) {
117
+ extraLines.push(`${key}=${value}`);
118
+ }
119
+ }
120
+ if (extraLines.length > 0) {
121
+ lines.push('# Other');
122
+ lines.push(...extraLines);
123
+ lines.push('');
124
+ }
125
+
126
+ writeFileSync(ENV_PATH, lines.join('\n'));
127
+ }
package/src/index.ts ADDED
@@ -0,0 +1,190 @@
1
+ import './log-timestamps.js';
2
+ import { Command } from 'commander';
3
+ import { configExists } from './config.js';
4
+ import { runSetup, runQuickSetup } from './setup.js';
5
+ import { startOperor } from './start.js';
6
+
7
+ const program = new Command();
8
+
9
+ program
10
+ .name('operor')
11
+ .description('Operor - AI Agent Operating System')
12
+ .version('0.1.0');
13
+
14
+ program
15
+ .command('setup')
16
+ .description('Run the interactive setup wizard')
17
+ .option('--quick', 'Quick setup with sensible defaults (3 questions)')
18
+ .action(async (opts) => {
19
+ if (opts.quick) {
20
+ await runQuickSetup();
21
+ } else {
22
+ await runSetup();
23
+ }
24
+ await startOperor();
25
+ });
26
+
27
+ program
28
+ .command('start')
29
+ .description('Start Operor with existing configuration')
30
+ .action(async () => {
31
+ if (!configExists()) {
32
+ console.error('No configuration found. Run "operor setup" first.');
33
+ process.exit(1);
34
+ }
35
+ await startOperor();
36
+ });
37
+
38
+ program
39
+ .command('status')
40
+ .description('Show Operor configuration status')
41
+ .action(async () => {
42
+ const { runStatus } = await import('./commands/status.js');
43
+ await runStatus();
44
+ });
45
+
46
+ program
47
+ .command('doctor')
48
+ .description('Validate Operor configuration and test connections')
49
+ .action(async () => {
50
+ const { runDoctor } = await import('./commands/doctor.js');
51
+ await runDoctor();
52
+ });
53
+
54
+ program
55
+ .command('test')
56
+ .description('Run Operor test scenarios')
57
+ .option('--csv <path>', 'Load test cases from a CSV file')
58
+ .option('--tag <tag>', 'Filter test cases by tag')
59
+ .option('--report <path>', 'Save JSON results to file')
60
+ .option('--real', 'Use real skills instead of mocks')
61
+ .option('--allow-writes', 'Allow write operations with real skills')
62
+ .option('--dry-run', 'Real reads, mock writes')
63
+ .action(async (opts) => {
64
+ const { runTest } = await import('./commands/test.js');
65
+ await runTest(opts);
66
+ });
67
+
68
+ program
69
+ .command('test-suite <file>')
70
+ .description('Run a test suite from a CSV or JSON file')
71
+ .option('--strategy <type>', 'Evaluation strategy: exact, contains, semantic', 'contains')
72
+ .option('--timeout <ms>', 'Per-test timeout in milliseconds', parseInt)
73
+ .option('--parallel', 'Run tests in parallel')
74
+ .option('--verbose', 'Show detailed output for failed tests')
75
+ .option('--json', 'Output results as JSON')
76
+ .option('--real', 'Use real skills instead of mocks')
77
+ .option('--allow-writes', 'Allow write operations with real skills')
78
+ .option('--dry-run', 'Real reads, mock writes')
79
+ .option('--llm', 'Use LLM-based agent processing instead of pattern matcher')
80
+ .action(async (file, opts) => {
81
+ const { runTestSuite } = await import('./commands/test-suite.js');
82
+ await runTestSuite(file, opts);
83
+ });
84
+
85
+ program
86
+ .command('converse [scenario]')
87
+ .description('Run multi-turn conversation test scenarios')
88
+ .option('--file <path>', 'Load scenarios from a JSON file')
89
+ .option('--turns <n>', 'Override max turns per scenario', parseInt)
90
+ .option('--persona <style>', 'Override persona (polite|frustrated|confused|terse|verbose)')
91
+ .option('--verbose', 'Show full conversation transcripts')
92
+ .option('--json', 'Output results as JSON')
93
+ .option('--real', 'Use real skills instead of mocks')
94
+ .option('--allow-writes', 'Allow write operations with real skills')
95
+ .option('--dry-run', 'Real reads, mock writes')
96
+ .action(async (scenario, opts) => {
97
+ const { runConverse } = await import('./commands/converse.js');
98
+ await runConverse({ scenario, ...opts });
99
+ });
100
+
101
+ program
102
+ .command('simulate')
103
+ .description('Run pre-deployment simulation (test suites + conversation scenarios)')
104
+ .option('--tests <files...>', 'CSV/JSON test suite files to include')
105
+ .option('--scenarios <names...>', 'Scenario names to run (or "all" for built-in)')
106
+ .option('--conversations <n>', 'Number of conversations to run (default: 10)', parseInt)
107
+ .option('--strategy <type>', 'Evaluation strategy: exact, similarity, llm_judge', 'similarity')
108
+ .option('--real', 'Use real skills instead of mocks')
109
+ .option('--allow-writes', 'Allow write operations with real skills')
110
+ .option('--dry-run', 'Real reads, mock writes')
111
+ .option('--timeout <ms>', 'Per-test timeout in milliseconds', parseInt)
112
+ .option('--parallel', 'Run tests in parallel')
113
+ .option('--json', 'Output full report as JSON')
114
+ .option('--report <file>', 'Save detailed JSON report to file')
115
+ .action(async (opts) => {
116
+ const { runSimulate } = await import('./commands/simulate.js');
117
+ await runSimulate({ ...opts, output: opts.report });
118
+ });
119
+
120
+ // Knowledge Base commands
121
+ import { registerKBCommand } from './commands/kb.js';
122
+ registerKBCommand(program);
123
+
124
+ // Analytics commands
125
+ import { registerAnalyticsCommand } from './commands/analytics.js';
126
+ registerAnalyticsCommand(program);
127
+
128
+ // History commands
129
+ import { registerHistoryCommand } from './commands/history.js';
130
+ registerHistoryCommand(program);
131
+
132
+ // Chat command
133
+ import { registerChatCommand } from './commands/chat.js';
134
+ registerChatCommand(program);
135
+
136
+ // Init command
137
+ import { registerInitCommand } from './commands/init.js';
138
+ registerInitCommand(program);
139
+
140
+ program
141
+ .command('reset')
142
+ .description('Delete all data (KB, memory, analytics, copilot, skills, auth) to start fresh')
143
+ .option('--yes', 'Skip confirmation prompt')
144
+ .option('--keep-config', 'Keep .env and mcp.json, only delete databases')
145
+ .action(async (opts) => {
146
+ const { runReset } = await import('./commands/reset.js');
147
+ await runReset(opts);
148
+ });
149
+
150
+ program
151
+ .command('vibe [action]')
152
+ .description('AI-powered agent copilot for creating and customizing agents')
153
+ .option('--agent <name>', 'Target a specific agent')
154
+ .action(async (action, opts) => {
155
+ const { runVibe } = await import('./commands/vibe.js');
156
+ await runVibe({ action, agent: opts.agent });
157
+ });
158
+
159
+ const configCmd = program.command('config').description('View and modify configuration');
160
+ configCmd
161
+ .command('show')
162
+ .description('Show current config')
163
+ .action(async () => {
164
+ const { showConfig } = await import('./commands/config.js');
165
+ showConfig();
166
+ });
167
+ configCmd
168
+ .command('set <key> <value>')
169
+ .description('Set a config value')
170
+ .action(async (k, v) => {
171
+ const { setConfigValue } = await import('./commands/config.js');
172
+ setConfigValue(k, v);
173
+ });
174
+ configCmd
175
+ .command('unset <key>')
176
+ .description('Remove a config value')
177
+ .action(async (k) => {
178
+ const { unsetConfigValue } = await import('./commands/config.js');
179
+ unsetConfigValue(k);
180
+ });
181
+
182
+ // Default action: setup if no config, otherwise start
183
+ program.action(async () => {
184
+ if (!configExists()) {
185
+ await runSetup();
186
+ }
187
+ await startOperor();
188
+ });
189
+
190
+ program.parse();
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Global console override that prepends timestamps to all log output.
3
+ * Format: [YYYY-MM-DD HH:mm:ss.SSS] — similar to NLog/structured logging.
4
+ *
5
+ * Import this file once at the entry point (index.ts) for automatic timestamps.
6
+ */
7
+
8
+ const originalLog = console.log;
9
+ const originalWarn = console.warn;
10
+ const originalError = console.error;
11
+
12
+ function timestamp(): string {
13
+ const now = new Date();
14
+ const y = now.getFullYear();
15
+ const mo = String(now.getMonth() + 1).padStart(2, '0');
16
+ const d = String(now.getDate()).padStart(2, '0');
17
+ const h = String(now.getHours()).padStart(2, '0');
18
+ const mi = String(now.getMinutes()).padStart(2, '0');
19
+ const s = String(now.getSeconds()).padStart(2, '0');
20
+ const ms = String(now.getMilliseconds()).padStart(3, '0');
21
+ return `[${y}-${mo}-${d} ${h}:${mi}:${s}.${ms}]`;
22
+ }
23
+
24
+ console.log = (...args: any[]) => originalLog(timestamp(), ...args);
25
+ console.warn = (...args: any[]) => originalWarn(timestamp(), ...args);
26
+ console.error = (...args: any[]) => originalError(timestamp(), ...args);