opc-agent 0.5.0 → 0.5.1

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/dist/cli.js CHANGED
@@ -50,11 +50,11 @@ const content_writer_1 = require("./templates/content-writer");
50
50
  const legal_assistant_1 = require("./templates/legal-assistant");
51
51
  const financial_advisor_1 = require("./templates/financial-advisor");
52
52
  const executive_assistant_1 = require("./templates/executive-assistant");
53
- const customer_service_2 = require("./templates/customer-service");
54
53
  const analytics_1 = require("./analytics");
55
54
  const openclaw_1 = require("./deploy/openclaw");
56
55
  const workflow_1 = require("./core/workflow");
57
56
  const versioning_1 = require("./core/versioning");
57
+ const providers_1 = require("./providers");
58
58
  const program = new commander_1.Command();
59
59
  const color = {
60
60
  green: (s) => `\x1b[32m${s}\x1b[0m`,
@@ -88,7 +88,7 @@ const TEMPLATES = {
88
88
  'financial-advisor': { label: 'Financial Advisor - budget analysis + expense tracking + planning', factory: financial_advisor_1.createFinancialAdvisorConfig },
89
89
  'executive-assistant': { label: 'Executive Assistant - calendar + email drafting + meeting prep', factory: executive_assistant_1.createExecutiveAssistantConfig },
90
90
  };
91
- async function prompt(question, defaultValue) {
91
+ async function promptUser(question, defaultValue) {
92
92
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
93
93
  const suffix = defaultValue ? ` ${color.dim(`(${defaultValue})`)}` : '';
94
94
  return new Promise((resolve) => {
@@ -103,23 +103,24 @@ async function select(question, options) {
103
103
  options.forEach((opt, i) => {
104
104
  console.log(` ${color.cyan(`${i + 1})`)} ${opt.label}`);
105
105
  });
106
- const answer = await prompt(`\nChoose ${color.dim('(1-' + options.length + ')')}`, '1');
106
+ const answer = await promptUser(`\nChoose ${color.dim('(1-' + options.length + ')')}`, '1');
107
107
  const idx = parseInt(answer) - 1;
108
108
  return options[Math.max(0, Math.min(idx, options.length - 1))].value;
109
109
  }
110
110
  program
111
111
  .name('opc')
112
112
  .description('OPC Agent - Open Agent Framework for business workstations')
113
- .version('0.5.0');
113
+ .version('0.5.1');
114
+ // ── Init command ─────────────────────────────────────────────
114
115
  program
115
116
  .command('init')
116
- .description('Initialize a new OPC agent project (interactive)')
117
+ .description('Initialize a new OPC agent project')
117
118
  .argument('[name]', 'Project name')
118
119
  .option('-t, --template <template>', 'Template to use')
119
120
  .option('-y, --yes', 'Skip prompts, use defaults')
120
121
  .action(async (nameArg, opts) => {
121
122
  console.log(`\n${icon.rocket} ${color.bold('OPC Agent - Create New Project')}\n`);
122
- const name = opts.yes ? (nameArg ?? 'my-agent') : (nameArg ?? await prompt('Project name', 'my-agent'));
123
+ const name = opts.yes ? (nameArg ?? 'my-agent') : (nameArg ?? await promptUser('Project name', 'my-agent'));
123
124
  const template = opts.yes
124
125
  ? (opts.template ?? 'customer-service')
125
126
  : (opts.template ?? await select('Select a template:', Object.entries(TEMPLATES).map(([value, { label }]) => ({ value, label }))));
@@ -132,27 +133,184 @@ program
132
133
  const factory = TEMPLATES[template]?.factory ?? customer_service_1.createCustomerServiceConfig;
133
134
  const config = factory();
134
135
  config.metadata.name = name;
136
+ // Ensure web channel exists
137
+ if (!config.spec.channels.some((c) => c.type === 'web')) {
138
+ config.spec.channels.push({ type: 'web', port: 3000 });
139
+ }
135
140
  fs.writeFileSync(path.join(dir, 'oad.yaml'), yaml.dump(config, { lineWidth: 120 }));
136
- fs.writeFileSync(path.join(dir, 'README.md'), `# ${name}\n\nCreated with OPC Agent using the \`${template}\` template.\n\n## Run\n\n\`\`\`bash\nopc run\n\`\`\`\n`);
141
+ // .env.example
142
+ fs.writeFileSync(path.join(dir, '.env.example'), `# LLM API Configuration
143
+ OPC_LLM_API_KEY=your-api-key-here
144
+ OPC_LLM_BASE_URL=https://api.openai.com/v1
145
+ OPC_LLM_MODEL=gpt-4o-mini
146
+
147
+ # For DeepSeek:
148
+ # OPC_LLM_BASE_URL=https://api.deepseek.com/v1
149
+ # OPC_LLM_MODEL=deepseek-chat
150
+
151
+ # For local Ollama:
152
+ # OPC_LLM_BASE_URL=http://localhost:11434/v1
153
+ # OPC_LLM_MODEL=llama3
154
+ `);
155
+ // .env (copy of example)
156
+ fs.writeFileSync(path.join(dir, '.env'), `OPC_LLM_API_KEY=your-api-key-here
157
+ OPC_LLM_BASE_URL=https://api.openai.com/v1
158
+ OPC_LLM_MODEL=gpt-4o-mini
159
+ `);
160
+ // package.json
161
+ fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({
162
+ name,
163
+ version: '1.0.0',
164
+ private: true,
165
+ scripts: {
166
+ start: 'opc run',
167
+ chat: 'opc chat',
168
+ },
169
+ dependencies: {
170
+ 'opc-agent': '^0.5.0',
171
+ },
172
+ }, null, 2));
173
+ // .gitignore
174
+ fs.writeFileSync(path.join(dir, '.gitignore'), 'node_modules\n.env\n');
175
+ // README.md
176
+ fs.writeFileSync(path.join(dir, 'README.md'), `# ${name}
177
+
178
+ Created with [OPC Agent](https://github.com/Deepleaper/opc-agent) using the \`${template}\` template.
179
+
180
+ ## Quick Start
181
+
182
+ 1. **Set your API key:**
183
+ \`\`\`bash
184
+ # Edit .env and add your API key
185
+ cp .env.example .env
186
+ # Then edit .env with your actual key
187
+ \`\`\`
188
+
189
+ 2. **Install dependencies:**
190
+ \`\`\`bash
191
+ npm install
192
+ \`\`\`
193
+
194
+ 3. **Start the web server:**
195
+ \`\`\`bash
196
+ npx opc run
197
+ \`\`\`
198
+
199
+ 4. **Open browser:** [http://localhost:3000](http://localhost:3000)
200
+
201
+ ## CLI Chat
202
+
203
+ \`\`\`bash
204
+ npx opc chat
205
+ \`\`\`
206
+
207
+ ## Configuration
208
+
209
+ Edit \`oad.yaml\` to customize your agent's personality, skills, and behavior.
210
+ `);
137
211
  console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')}`);
138
- console.log(` ${icon.file} oad.yaml - Agent definition`);
212
+ console.log(` ${icon.file} oad.yaml - Agent definition`);
213
+ console.log(` ${icon.file} package.json - Dependencies`);
214
+ console.log(` ${icon.file} .env.example - Environment template`);
215
+ console.log(` ${icon.file} .env - Environment config (edit this!)`);
216
+ console.log(` ${icon.file} .gitignore`);
139
217
  console.log(` ${icon.file} README.md`);
140
218
  console.log(`\n Template: ${color.cyan(template)}`);
141
- console.log(`\n${color.dim('Next:')} cd ${name} && opc run\n`);
219
+ console.log(`\n${color.bold('Next steps:')}`);
220
+ console.log(` 1. cd ${name}`);
221
+ console.log(` 2. Edit .env — set your OPC_LLM_API_KEY`);
222
+ console.log(` 3. npm install`);
223
+ console.log(` 4. npx opc run`);
224
+ console.log(` 5. Open http://localhost:3000\n`);
142
225
  });
226
+ // ── Chat command ─────────────────────────────────────────────
143
227
  program
144
- .command('create')
145
- .description('Create an agent from a template')
146
- .argument('<name>', 'Agent name')
147
- .option('-t, --template <template>', 'Template', 'customer-service')
148
- .action(async (name, opts) => {
149
- const factory = TEMPLATES[opts.template]?.factory ?? customer_service_1.createCustomerServiceConfig;
150
- const config = factory();
151
- config.metadata.name = name;
152
- const outFile = `${name}-oad.yaml`;
153
- fs.writeFileSync(outFile, yaml.dump(config, { lineWidth: 120 }));
154
- console.log(`${icon.success} Created ${color.bold(outFile)}`);
228
+ .command('chat')
229
+ .description('Interactive CLI chat with the agent')
230
+ .option('-f, --file <file>', 'OAD file', 'oad.yaml')
231
+ .action(async (opts) => {
232
+ // Load .env if present
233
+ loadDotEnv();
234
+ let systemPrompt = 'You are a helpful AI agent.';
235
+ let model;
236
+ try {
237
+ const raw = fs.readFileSync(opts.file, 'utf-8');
238
+ const config = yaml.load(raw);
239
+ if (config?.spec?.systemPrompt)
240
+ systemPrompt = config.spec.systemPrompt;
241
+ if (config?.spec?.model)
242
+ model = config.spec.model;
243
+ console.log(`\n${icon.gear} Loaded agent: ${color.bold(config?.metadata?.name ?? 'unknown')}`);
244
+ }
245
+ catch {
246
+ console.log(`\n${icon.info} No oad.yaml found, using defaults.`);
247
+ }
248
+ const provider = (0, providers_1.createProvider)('openai', model);
249
+ const history = [];
250
+ console.log(`${color.dim('Type your message. Press Ctrl+C to exit.')}\n`);
251
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
252
+ const ask = () => {
253
+ rl.question(color.cyan('You: '), async (input) => {
254
+ const text = input.trim();
255
+ if (!text) {
256
+ ask();
257
+ return;
258
+ }
259
+ history.push({ role: 'user', content: text });
260
+ // Build messages for provider
261
+ const messages = history.map((m) => ({
262
+ id: 'x',
263
+ role: m.role,
264
+ content: m.content,
265
+ timestamp: Date.now(),
266
+ }));
267
+ process.stdout.write(color.green('Agent: '));
268
+ let full = '';
269
+ try {
270
+ for await (const chunk of provider.chatStream(messages, systemPrompt)) {
271
+ process.stdout.write(chunk);
272
+ full += chunk;
273
+ }
274
+ }
275
+ catch (err) {
276
+ const msg = err instanceof Error ? err.message : String(err);
277
+ process.stdout.write(color.red(`\n[Error: ${msg}]`));
278
+ full = `[Error: ${msg}]`;
279
+ }
280
+ console.log('\n');
281
+ history.push({ role: 'assistant', content: full });
282
+ // Trim history if too long (keep last 40 messages)
283
+ if (history.length > 40) {
284
+ history.splice(0, history.length - 40);
285
+ }
286
+ ask();
287
+ });
288
+ };
289
+ rl.on('close', () => {
290
+ console.log(`\n${color.dim('Goodbye!')}`);
291
+ process.exit(0);
292
+ });
293
+ ask();
294
+ });
295
+ // ── Run command ──────────────────────────────────────────────
296
+ program
297
+ .command('run')
298
+ .description('Start agent with web server')
299
+ .option('-f, --file <file>', 'OAD file', 'oad.yaml')
300
+ .option('-p, --port <port>', 'Port override')
301
+ .action(async (opts) => {
302
+ loadDotEnv();
303
+ const runtime = new runtime_1.AgentRuntime();
304
+ await runtime.loadConfig(opts.file);
305
+ await runtime.initialize();
306
+ await runtime.start();
307
+ const agent = runtime.getAgent();
308
+ console.log(`\n${icon.rocket} Agent "${color.bold(agent?.name ?? 'unknown')}" is running.`);
309
+ console.log(` ${color.dim('Web UI:')} http://localhost:3000`);
310
+ console.log(` ${color.dim('API:')} POST http://localhost:3000/api/chat`);
311
+ console.log(`\n ${color.dim('Press Ctrl+C to stop.')}\n`);
155
312
  });
313
+ // ── Info command ─────────────────────────────────────────────
156
314
  program
157
315
  .command('info')
158
316
  .description('Show agent info from OAD')
@@ -167,22 +325,9 @@ program
167
325
  console.log(` Name: ${color.cyan(m.name)}`);
168
326
  console.log(` Version: ${m.version}`);
169
327
  console.log(` Description: ${m.description ?? color.dim('(none)')}`);
170
- console.log(` Author: ${m.author ?? color.dim('(none)')}`);
171
- console.log(` License: ${m.license}`);
172
328
  console.log(` Model: ${s.model}`);
173
- console.log(` Provider: ${s.provider?.default ?? 'deepseek'}`);
174
- console.log(` Channels: ${s.channels.map(c => c.type).join(', ') || color.dim('(none)')}`);
175
- console.log(` Skills: ${s.skills.map(sk => sk.name).join(', ') || color.dim('(none)')}`);
176
- console.log(` Trust: ${s.dtv?.trust?.level ?? 'sandbox'}`);
177
- console.log(` Streaming: ${s.streaming ? 'enabled' : 'disabled'}`);
178
- console.log(` Locale: ${s.locale ?? 'en'}`);
179
- if (s.room) {
180
- console.log(` Room: ${s.room.name}`);
181
- }
182
- if (m.marketplace) {
183
- console.log(` Category: ${m.marketplace.category ?? color.dim('(none)')}`);
184
- console.log(` Pricing: ${m.marketplace.pricing ?? 'free'}`);
185
- }
329
+ console.log(` Channels: ${s.channels.map((c) => c.type).join(', ') || color.dim('(none)')}`);
330
+ console.log(` Skills: ${s.skills.map((sk) => sk.name).join(', ') || color.dim('(none)')}`);
186
331
  console.log();
187
332
  }
188
333
  catch (err) {
@@ -190,6 +335,7 @@ program
190
335
  process.exit(1);
191
336
  }
192
337
  });
338
+ // ── Build command ────────────────────────────────────────────
193
339
  program
194
340
  .command('build')
195
341
  .description('Validate OAD and compile')
@@ -205,48 +351,50 @@ program
205
351
  process.exit(1);
206
352
  }
207
353
  });
354
+ // ── Create command ───────────────────────────────────────────
355
+ program
356
+ .command('create')
357
+ .description('Create an agent from a template')
358
+ .argument('<name>', 'Agent name')
359
+ .option('-t, --template <template>', 'Template', 'customer-service')
360
+ .action(async (name, opts) => {
361
+ const factory = TEMPLATES[opts.template]?.factory ?? customer_service_1.createCustomerServiceConfig;
362
+ const config = factory();
363
+ config.metadata.name = name;
364
+ const outFile = `${name}-oad.yaml`;
365
+ fs.writeFileSync(outFile, yaml.dump(config, { lineWidth: 120 }));
366
+ console.log(`${icon.success} Created ${color.bold(outFile)}`);
367
+ });
368
+ // ── Test command ─────────────────────────────────────────────
208
369
  program
209
370
  .command('test')
210
371
  .description('Run agent in sandbox mode')
211
372
  .option('-f, --file <file>', 'OAD file', 'oad.yaml')
212
373
  .action(async (opts) => {
374
+ loadDotEnv();
213
375
  console.log(`\n${icon.gear} Running agent in sandbox mode...`);
214
376
  const runtime = new runtime_1.AgentRuntime();
215
377
  await runtime.loadConfig(opts.file);
216
378
  const agent = await runtime.initialize();
217
- runtime.registerSkill(new customer_service_2.FAQSkill());
218
- runtime.registerSkill(new customer_service_2.HandoffSkill());
219
379
  console.log(`${icon.success} Agent "${color.bold(agent.name)}" initialized in sandbox.`);
220
380
  console.log(` State: ${agent.state}`);
221
381
  console.log(` Sending test message...`);
222
382
  const response = await agent.handleMessage({
223
383
  id: 'test_1',
224
384
  role: 'user',
225
- content: 'What are your business hours?',
385
+ content: 'Hello! What can you help me with?',
226
386
  timestamp: Date.now(),
227
387
  });
228
- console.log(` Response: ${response.content}`);
388
+ console.log(` Response: ${response.content.slice(0, 200)}`);
229
389
  console.log(`${icon.success} Sandbox test passed.\n`);
230
390
  });
231
- program
232
- .command('run')
233
- .description('Start agent with channels')
234
- .option('-f, --file <file>', 'OAD file', 'oad.yaml')
235
- .action(async (opts) => {
236
- const runtime = new runtime_1.AgentRuntime();
237
- await runtime.loadConfig(opts.file);
238
- await runtime.initialize();
239
- runtime.registerSkill(new customer_service_2.FAQSkill());
240
- runtime.registerSkill(new customer_service_2.HandoffSkill());
241
- await runtime.start();
242
- const agent = runtime.getAgent();
243
- console.log(`\n${icon.rocket} Agent "${color.bold(agent?.name ?? 'unknown')}" is running.\n`);
244
- });
391
+ // ── Dev command ──────────────────────────────────────────────
245
392
  program
246
393
  .command('dev')
247
394
  .description('Hot-reload development mode')
248
395
  .option('-f, --file <file>', 'OAD file', 'oad.yaml')
249
396
  .action(async (opts) => {
397
+ loadDotEnv();
250
398
  console.log(`\n${icon.gear} ${color.bold('Development mode')} - watching for changes...\n`);
251
399
  let runtime = null;
252
400
  const startAgent = async () => {
@@ -256,8 +404,6 @@ program
256
404
  runtime = new runtime_1.AgentRuntime();
257
405
  await runtime.loadConfig(opts.file);
258
406
  await runtime.initialize();
259
- runtime.registerSkill(new customer_service_2.FAQSkill());
260
- runtime.registerSkill(new customer_service_2.HandoffSkill());
261
407
  await runtime.start();
262
408
  const agent = runtime.getAgent();
263
409
  console.log(`${icon.success} Agent "${color.bold(agent?.name ?? 'unknown')}" restarted.`);
@@ -284,6 +430,7 @@ program
284
430
  process.exit(0);
285
431
  });
286
432
  });
433
+ // ── Publish command ──────────────────────────────────────────
287
434
  program
288
435
  .command('publish')
289
436
  .description('Validate and package for OPC Registry')
@@ -295,10 +442,6 @@ program
295
442
  const trust = config.spec.dtv?.trust?.level ?? 'sandbox';
296
443
  console.log(`\n${icon.package} Publishing: ${color.bold(config.metadata.name)} v${config.metadata.version}`);
297
444
  console.log(` ${icon.success} OAD validation passed`);
298
- console.log(` 🔐 Trust level: ${trust}`);
299
- if (trust === 'sandbox') {
300
- console.log(` ${icon.warn} Trust level is 'sandbox'. Upgrade for marketplace listing.`);
301
- }
302
445
  const manifest = {
303
446
  name: config.metadata.name,
304
447
  version: config.metadata.version,
@@ -306,11 +449,8 @@ program
306
449
  author: config.metadata.author,
307
450
  license: config.metadata.license,
308
451
  trust,
309
- category: config.metadata.marketplace?.category,
310
- pricing: config.metadata.marketplace?.pricing ?? 'free',
311
- tags: config.metadata.marketplace?.tags ?? [],
312
- channels: config.spec.channels.map(c => c.type),
313
- skills: config.spec.skills.map(s => s.name),
452
+ channels: config.spec.channels.map((c) => c.type),
453
+ skills: config.spec.skills.map((s) => s.name),
314
454
  publishedAt: new Date().toISOString(),
315
455
  };
316
456
  fs.writeFileSync('opc-manifest.json', JSON.stringify(manifest, null, 2));
@@ -322,18 +462,7 @@ program
322
462
  process.exit(1);
323
463
  }
324
464
  });
325
- program
326
- .command('search')
327
- .description('Search OPC Registry for agents and skills')
328
- .argument('<query>', 'Search query')
329
- .action(async (query) => {
330
- console.log(`\n${icon.search} Searching OPC Registry for "${color.bold(query)}"...`);
331
- console.log(`\n🚧 OPC Registry coming soon!`);
332
- console.log(` The marketplace is under development.`);
333
- console.log(` Browse templates with: ${color.cyan('opc init --template <name>')}`);
334
- console.log(`\n Available templates: ${Object.keys(TEMPLATES).map(t => color.cyan(t)).join(', ')}\n`);
335
- });
336
- // ── Deploy command ────────────────────────────────────────────
465
+ // ── Deploy command ───────────────────────────────────────────
337
466
  program
338
467
  .command('deploy')
339
468
  .description('Deploy agent to a target runtime')
@@ -354,67 +483,27 @@ program
354
483
  const defaultOutput = path.join(homeDir, '.openclaw', 'agents', agentId, 'workspace');
355
484
  const outputDir = path.resolve(opts.output ?? defaultOutput);
356
485
  console.log(`\n${icon.rocket} ${color.bold('Deploy to OpenClaw')}\n`);
357
- console.log(` Agent: ${color.cyan(config.metadata.name)} v${config.metadata.version}`);
358
- console.log(` Target: ${color.cyan('OpenClaw')}`);
359
- console.log(` Output: ${color.dim(outputDir)}`);
360
486
  const result = (0, openclaw_1.deployToOpenClaw)({ oad: config, outputDir, install: opts.install });
361
- console.log(`\n${icon.success} Generated ${result.files.length} files:`);
362
- for (const f of result.files) {
363
- console.log(` ${icon.file} ${f}`);
364
- }
487
+ console.log(`\n${icon.success} Generated ${result.files.length} files.`);
365
488
  if (result.installed) {
366
- console.log(`\n${icon.success} Registered in OpenClaw config: ${color.dim(result.configPath)}`);
367
- console.log(`\n${color.dim('Next:')} Restart OpenClaw gateway to pick up the new agent.`);
368
- console.log(` ${color.cyan('openclaw gateway restart')}\n`);
369
- }
370
- else if (opts.install) {
371
- console.log(`\n${icon.warn} Could not auto-register. Add the agent manually to openclaw.json.`);
372
- }
373
- else {
374
- console.log(`\n${color.dim('Next:')} Copy the output to your OpenClaw agents directory, or re-run with ${color.cyan('--install')}`);
375
- console.log(` ${color.cyan(`opc deploy --target openclaw --install`)}\n`);
489
+ console.log(`${icon.success} Registered in OpenClaw config.`);
376
490
  }
491
+ console.log();
377
492
  }
378
493
  catch (err) {
379
494
  console.error(`${icon.error} Deploy failed:`, err instanceof Error ? err.message : err);
380
495
  process.exit(1);
381
496
  }
382
497
  });
383
- // ── Tool commands ────────────────────────────────────────────
384
- const toolCmd = program.command('tool').description('Manage MCP tools');
385
- toolCmd
386
- .command('list')
387
- .description('List available MCP tools')
388
- .action(() => {
389
- console.log(`\n${icon.gear} ${color.bold('MCP Tools')}\n`);
390
- console.log(` ${color.dim('No tools installed yet.')}`);
391
- console.log(`\n Add tools with: ${color.cyan('opc tool add <name>')}\n`);
392
- });
393
- toolCmd
394
- .command('add')
395
- .description('Add an MCP tool from registry')
396
- .argument('<name>', 'Tool name')
397
- .action((name) => {
398
- console.log(`\n🚧 Tool registry coming soon!`);
399
- console.log(` Would add tool: ${color.cyan(name)}\n`);
400
- });
401
- // ── Plugin commands ──────────────────────────────────────────
402
- const pluginCmd = program.command('plugin').description('Manage plugins');
403
- pluginCmd
404
- .command('list')
405
- .description('List installed plugins')
406
- .action(() => {
407
- console.log(`\n${icon.gear} ${color.bold('Installed Plugins')}\n`);
408
- console.log(` ${color.dim('No plugins installed yet.')}`);
409
- console.log(`\n Add plugins with: ${color.cyan('opc plugin add <name>')}\n`);
410
- });
411
- pluginCmd
412
- .command('add')
413
- .description('Add a plugin')
414
- .argument('<name>', 'Plugin name')
415
- .action((name) => {
416
- console.log(`\n🚧 Plugin registry coming soon!`);
417
- console.log(` Would add plugin: ${color.cyan(name)}\n`);
498
+ // ── Search command ───────────────────────────────────────────
499
+ program
500
+ .command('search')
501
+ .description('Search OPC Registry for agents and skills')
502
+ .argument('<query>', 'Search query')
503
+ .action(async (query) => {
504
+ console.log(`\n${icon.search} Searching OPC Registry for "${color.bold(query)}"...`);
505
+ console.log(`\n🚧 OPC Registry coming soon!`);
506
+ console.log(` Available templates: ${Object.keys(TEMPLATES).map(t => color.cyan(t)).join(', ')}\n`);
418
507
  });
419
508
  // ── Stats command ────────────────────────────────────────────
420
509
  program
@@ -422,89 +511,96 @@ program
422
511
  .description('Show agent analytics')
423
512
  .action(() => {
424
513
  const analytics = new analytics_1.Analytics();
425
- // Show demo stats
426
514
  const snap = analytics.getSnapshot();
427
515
  console.log(`\n${icon.gear} ${color.bold('Agent Analytics')}\n`);
428
- console.log(` Messages Processed: ${snap.messagesProcessed}`);
429
- console.log(` Avg Response Time: ${snap.avgResponseTimeMs}ms`);
430
- console.log(` Error Count: ${snap.errorCount}`);
431
- console.log(` Token Usage: ${snap.tokenUsage.total} (in: ${snap.tokenUsage.input}, out: ${snap.tokenUsage.output})`);
432
- console.log(` Uptime: ${Math.round(snap.uptime / 1000)}s`);
433
- console.log(`\n ${color.dim('Run an agent to collect analytics data.')}\n`);
516
+ console.log(` Messages: ${snap.messagesProcessed} | Errors: ${snap.errorCount} | Uptime: ${Math.round(snap.uptime / 1000)}s\n`);
517
+ });
518
+ // ── Tool commands ────────────────────────────────────────────
519
+ const toolCmd = program.command('tool').description('Manage MCP tools');
520
+ toolCmd.command('list').description('List MCP tools').action(() => {
521
+ console.log(`\n${icon.gear} No tools installed. Add with: ${color.cyan('opc tool add <name>')}\n`);
522
+ });
523
+ toolCmd.command('add').argument('<name>').action((name) => {
524
+ console.log(`🚧 Tool registry coming soon! Would add: ${color.cyan(name)}\n`);
434
525
  });
435
- // 🔄 Workflow commands ────────────────────────────────────────
526
+ // ── Workflow commands ────────────────────────────────────────
436
527
  const workflowCmd = program.command('workflow').description('Manage workflows');
437
528
  workflowCmd
438
529
  .command('run')
439
- .description('Run a workflow defined in OAD')
440
- .argument('<name>', 'Workflow name')
530
+ .argument('<name>')
441
531
  .option('-f, --file <file>', 'OAD file', 'oad.yaml')
442
532
  .action(async (name, opts) => {
443
- console.log(`\n${icon.gear} Running workflow "${color.bold(name)}"...`);
444
533
  const runtime = new runtime_1.AgentRuntime();
445
534
  const config = await runtime.loadConfig(opts.file);
446
- const workflows = config.spec.workflows ?? [];
447
- const wfDef = workflows.find((w) => w.name === name);
448
- if (!wfDef) {
449
- console.error(`${icon.error} Workflow "${name}" not found in OAD.`);
535
+ const wf = (config.spec.workflows ?? []).find((w) => w.name === name);
536
+ if (!wf) {
537
+ console.error(`Workflow "${name}" not found.`);
450
538
  process.exit(1);
451
539
  }
452
540
  const engine = new workflow_1.WorkflowEngine();
453
- engine.registerWorkflow(wfDef);
454
- console.log(`${icon.success} Workflow "${name}" loaded with ${wfDef.steps?.length ?? 0} steps.`);
455
- console.log(`${color.dim(' Workflow execution requires a running agent context.')}\n`);
541
+ engine.registerWorkflow(wf);
542
+ console.log(`${icon.success} Workflow "${name}" loaded.\n`);
456
543
  });
457
544
  workflowCmd
458
545
  .command('list')
459
- .description('List workflows in OAD')
460
546
  .option('-f, --file <file>', 'OAD file', 'oad.yaml')
461
547
  .action(async (opts) => {
462
548
  const runtime = new runtime_1.AgentRuntime();
463
549
  const config = await runtime.loadConfig(opts.file);
464
- const workflows = config.spec.workflows ?? [];
465
- console.log(`\n${icon.gear} ${color.bold('Workflows')}\n`);
466
- if (workflows.length === 0) {
467
- console.log(` ${color.dim('No workflows defined.')}\n`);
468
- }
469
- else {
470
- for (const wf of workflows) {
471
- console.log(` ${color.cyan(wf.name)} - ${wf.description ?? color.dim('(no description)')}`);
472
- }
473
- console.log();
550
+ const wfs = config.spec.workflows ?? [];
551
+ if (wfs.length === 0) {
552
+ console.log('No workflows defined.');
553
+ return;
474
554
  }
555
+ for (const wf of wfs)
556
+ console.log(` ${color.cyan(wf.name)}`);
475
557
  });
476
- // 📦 Version commands ─────────────────────────────────────────
477
- const versionCmd = program.command('version').description('Manage agent versions');
478
- versionCmd
479
- .command('list')
480
- .description('List saved versions')
481
- .action(() => {
558
+ // ── Version commands ─────────────────────────────────────────
559
+ const versionCmd = program.command('version-mgmt').description('Manage agent versions');
560
+ versionCmd.command('list').action(() => {
482
561
  const vm = new versioning_1.VersionManager();
483
562
  const versions = vm.list();
484
- console.log(`\n${icon.gear} ${color.bold('Agent Versions')}\n`);
485
563
  if (versions.length === 0) {
486
- console.log(` ${color.dim('No versions saved.')}\n`);
487
- }
488
- else {
489
- for (const v of versions) {
490
- console.log(` ${color.cyan(v.version)} - ${new Date(v.timestamp).toISOString()} ${v.description ?? ''}`);
491
- }
492
- console.log();
564
+ console.log('No versions saved.');
565
+ return;
493
566
  }
567
+ for (const v of versions)
568
+ console.log(` ${color.cyan(v.version)} - ${new Date(v.timestamp).toISOString()}`);
494
569
  });
495
- versionCmd
496
- .command('rollback')
497
- .description('Rollback to a saved version')
498
- .argument('<version>', 'Version to rollback to')
499
- .action((version) => {
570
+ versionCmd.command('rollback').argument('<version>').action((version) => {
500
571
  const vm = new versioning_1.VersionManager();
501
572
  const oad = vm.rollback(version);
502
573
  if (!oad) {
503
- console.error(`${icon.error} Version "${version}" not found.`);
574
+ console.error(`Version "${version}" not found.`);
504
575
  process.exit(1);
505
576
  }
506
577
  fs.writeFileSync('oad.yaml', oad);
507
- console.log(`${icon.success} Rolled back to version ${color.bold(version)}.`);
578
+ console.log(`${icon.success} Rolled back to ${version}.`);
508
579
  });
580
+ // ── Helpers ──────────────────────────────────────────────────
581
+ function loadDotEnv() {
582
+ const envPath = path.resolve('.env');
583
+ if (!fs.existsSync(envPath))
584
+ return;
585
+ try {
586
+ const content = fs.readFileSync(envPath, 'utf-8');
587
+ for (const line of content.split('\n')) {
588
+ const trimmed = line.trim();
589
+ if (!trimmed || trimmed.startsWith('#'))
590
+ continue;
591
+ const eqIdx = trimmed.indexOf('=');
592
+ if (eqIdx === -1)
593
+ continue;
594
+ const key = trimmed.slice(0, eqIdx).trim();
595
+ const value = trimmed.slice(eqIdx + 1).trim();
596
+ if (!process.env[key]) {
597
+ process.env[key] = value;
598
+ }
599
+ }
600
+ }
601
+ catch {
602
+ // ignore
603
+ }
604
+ }
509
605
  program.parse();
510
606
  //# sourceMappingURL=cli.js.map