@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.
- package/README.md +76 -0
- package/dist/config-Bn2pbORi.js +34 -0
- package/dist/config-Bn2pbORi.js.map +1 -0
- package/dist/converse-C_PB7-JH.js +142 -0
- package/dist/converse-C_PB7-JH.js.map +1 -0
- package/dist/doctor-98gPl743.js +122 -0
- package/dist/doctor-98gPl743.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2268 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-override-BIQl0V6H.js +445 -0
- package/dist/llm-override-BIQl0V6H.js.map +1 -0
- package/dist/reset-DT8SBgFS.js +87 -0
- package/dist/reset-DT8SBgFS.js.map +1 -0
- package/dist/simulate-BKv62GJc.js +144 -0
- package/dist/simulate-BKv62GJc.js.map +1 -0
- package/dist/status-D6LIZvQa.js +82 -0
- package/dist/status-D6LIZvQa.js.map +1 -0
- package/dist/test-DYjkxbtK.js +177 -0
- package/dist/test-DYjkxbtK.js.map +1 -0
- package/dist/test-suite-D8H_5uKs.js +209 -0
- package/dist/test-suite-D8H_5uKs.js.map +1 -0
- package/dist/utils-BuV4q7f6.js +11 -0
- package/dist/utils-BuV4q7f6.js.map +1 -0
- package/dist/vibe-Bl_js3Jo.js +395 -0
- package/dist/vibe-Bl_js3Jo.js.map +1 -0
- package/package.json +43 -0
- package/src/commands/analytics.ts +408 -0
- package/src/commands/chat.ts +310 -0
- package/src/commands/config.ts +34 -0
- package/src/commands/converse.ts +182 -0
- package/src/commands/doctor.ts +154 -0
- package/src/commands/history.ts +60 -0
- package/src/commands/init.ts +163 -0
- package/src/commands/kb.ts +429 -0
- package/src/commands/llm-override.ts +480 -0
- package/src/commands/reset.ts +72 -0
- package/src/commands/simulate.ts +187 -0
- package/src/commands/status.ts +112 -0
- package/src/commands/test-suite.ts +247 -0
- package/src/commands/test.ts +177 -0
- package/src/commands/vibe.ts +478 -0
- package/src/config.ts +127 -0
- package/src/index.ts +190 -0
- package/src/log-timestamps.ts +26 -0
- package/src/setup.ts +712 -0
- package/src/start.ts +573 -0
- package/src/utils.ts +6 -0
- package/templates/agents/_defaults/SOUL.md +20 -0
- package/templates/agents/_defaults/USER.md +16 -0
- package/templates/agents/customer-support/IDENTITY.md +6 -0
- package/templates/agents/customer-support/INSTRUCTIONS.md +79 -0
- package/templates/agents/customer-support/SOUL.md +26 -0
- package/templates/agents/faq-bot/IDENTITY.md +6 -0
- package/templates/agents/faq-bot/INSTRUCTIONS.md +53 -0
- package/templates/agents/faq-bot/SOUL.md +19 -0
- package/templates/agents/sales/IDENTITY.md +6 -0
- package/templates/agents/sales/INSTRUCTIONS.md +67 -0
- package/templates/agents/sales/SOUL.md +20 -0
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +13 -0
- 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);
|