opc-agent 0.3.0 → 0.5.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 (53) hide show
  1. package/README.md +20 -0
  2. package/dist/channels/voice.d.ts +43 -0
  3. package/dist/channels/voice.js +67 -0
  4. package/dist/channels/webhook.d.ts +40 -0
  5. package/dist/channels/webhook.js +193 -0
  6. package/dist/cli.js +143 -13
  7. package/dist/core/a2a.d.ts +46 -0
  8. package/dist/core/a2a.js +99 -0
  9. package/dist/core/hitl.d.ts +41 -0
  10. package/dist/core/hitl.js +100 -0
  11. package/dist/core/performance.d.ts +50 -0
  12. package/dist/core/performance.js +148 -0
  13. package/dist/core/versioning.d.ts +29 -0
  14. package/dist/core/versioning.js +114 -0
  15. package/dist/core/workflow.d.ts +59 -0
  16. package/dist/core/workflow.js +174 -0
  17. package/dist/deploy/openclaw.d.ts +14 -0
  18. package/dist/deploy/openclaw.js +208 -0
  19. package/dist/index.d.ts +13 -0
  20. package/dist/index.js +18 -1
  21. package/dist/schema/oad.d.ts +352 -15
  22. package/dist/schema/oad.js +41 -2
  23. package/dist/templates/executive-assistant.d.ts +20 -0
  24. package/dist/templates/executive-assistant.js +70 -0
  25. package/dist/templates/financial-advisor.d.ts +15 -0
  26. package/dist/templates/financial-advisor.js +60 -0
  27. package/dist/templates/legal-assistant.d.ts +15 -0
  28. package/dist/templates/legal-assistant.js +70 -0
  29. package/examples/customer-service-demo/README.md +90 -0
  30. package/examples/customer-service-demo/oad.yaml +107 -0
  31. package/package.json +46 -46
  32. package/src/channels/voice.ts +106 -0
  33. package/src/channels/webhook.ts +199 -0
  34. package/src/cli.ts +524 -384
  35. package/src/core/a2a.ts +143 -0
  36. package/src/core/hitl.ts +138 -0
  37. package/src/core/performance.ts +187 -0
  38. package/src/core/versioning.ts +106 -0
  39. package/src/core/workflow.ts +235 -0
  40. package/src/deploy/openclaw.ts +200 -0
  41. package/src/index.ts +15 -0
  42. package/src/schema/oad.ts +45 -1
  43. package/src/templates/executive-assistant.ts +71 -0
  44. package/src/templates/financial-advisor.ts +60 -0
  45. package/src/templates/legal-assistant.ts +71 -0
  46. package/tests/a2a.test.ts +66 -0
  47. package/tests/hitl.test.ts +71 -0
  48. package/tests/performance.test.ts +115 -0
  49. package/tests/templates.test.ts +77 -0
  50. package/tests/versioning.test.ts +75 -0
  51. package/tests/voice.test.ts +61 -0
  52. package/tests/webhook.test.ts +29 -0
  53. package/tests/workflow.test.ts +143 -0
package/README.md CHANGED
@@ -42,6 +42,25 @@ opc run
42
42
  | `knowledge-base` | RAG with DeepBrain semantic search |
43
43
  | `code-reviewer` | Bug detection + style checking |
44
44
 
45
+ ## 🚀 Deploy to OpenClaw
46
+
47
+ OPC Agent is a **development framework**. [OpenClaw](https://github.com/nicepkg/openclaw) is the **runtime**. Design your agent with OPC, deploy it to OpenClaw, and it runs on Telegram, Discord, or any channel OpenClaw supports.
48
+
49
+ ```bash
50
+ # Generate OpenClaw workspace from your OAD
51
+ opc deploy --target openclaw --output ./my-agent-workspace
52
+
53
+ # Or deploy AND auto-register in OpenClaw
54
+ opc deploy --target openclaw --install
55
+
56
+ # Then restart OpenClaw to pick it up
57
+ openclaw gateway restart
58
+ ```
59
+
60
+ This generates `IDENTITY.md`, `SOUL.md`, `AGENTS.md`, `USER.md`, and `MEMORY.md` — everything OpenClaw needs to run your agent.
61
+
62
+ See [`examples/customer-service-demo/`](examples/customer-service-demo/) for a complete walkthrough.
63
+
45
64
  ## CLI Commands
46
65
 
47
66
  | Command | Description |
@@ -53,6 +72,7 @@ opc run
53
72
  | `opc test` | Run in sandbox mode |
54
73
  | `opc run` | Start agent with channels |
55
74
  | `opc dev` | Hot-reload development mode |
75
+ | `opc deploy` | **Deploy to OpenClaw runtime** |
56
76
  | `opc publish` | Validate and generate manifest |
57
77
  | `opc search <query>` | Search OPC Registry (coming soon) |
58
78
 
@@ -0,0 +1,43 @@
1
+ import { BaseChannel } from './index';
2
+ export interface STTProvider {
3
+ name: string;
4
+ transcribe(audio: Buffer, options?: STTOptions): Promise<string>;
5
+ }
6
+ export interface TTSProvider {
7
+ name: string;
8
+ synthesize(text: string, options?: TTSOptions): Promise<Buffer>;
9
+ }
10
+ export interface STTOptions {
11
+ language?: string;
12
+ model?: string;
13
+ }
14
+ export interface TTSOptions {
15
+ voice?: string;
16
+ speed?: number;
17
+ language?: string;
18
+ }
19
+ export interface VoiceChannelConfig {
20
+ sttProvider?: STTProvider;
21
+ ttsProvider?: TTSProvider;
22
+ sampleRate?: number;
23
+ language?: string;
24
+ }
25
+ export declare class VoiceChannel extends BaseChannel {
26
+ type: string;
27
+ private config;
28
+ private logger;
29
+ private running;
30
+ constructor(config?: VoiceChannelConfig);
31
+ start(): Promise<void>;
32
+ stop(): Promise<void>;
33
+ isRunning(): boolean;
34
+ /** Process audio input: STT → Agent → TTS */
35
+ processAudio(audio: Buffer): Promise<{
36
+ text: string;
37
+ response: string;
38
+ audioResponse?: Buffer;
39
+ }>;
40
+ setSTTProvider(provider: STTProvider): void;
41
+ setTTSProvider(provider: TTSProvider): void;
42
+ }
43
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VoiceChannel = void 0;
4
+ const index_1 = require("./index");
5
+ const logger_1 = require("../core/logger");
6
+ // ── Voice Channel ───────────────────────────────────────────
7
+ class VoiceChannel extends index_1.BaseChannel {
8
+ type = 'voice';
9
+ config;
10
+ logger = new logger_1.Logger('voice-channel');
11
+ running = false;
12
+ constructor(config) {
13
+ super();
14
+ this.config = config ?? {};
15
+ }
16
+ async start() {
17
+ this.running = true;
18
+ this.logger.info('Voice channel started', {
19
+ stt: this.config.sttProvider?.name ?? 'none',
20
+ tts: this.config.ttsProvider?.name ?? 'none',
21
+ });
22
+ }
23
+ async stop() {
24
+ this.running = false;
25
+ this.logger.info('Voice channel stopped');
26
+ }
27
+ isRunning() {
28
+ return this.running;
29
+ }
30
+ /** Process audio input: STT → Agent → TTS */
31
+ async processAudio(audio) {
32
+ if (!this.handler)
33
+ throw new Error('No message handler set');
34
+ // STT
35
+ let text;
36
+ if (this.config.sttProvider) {
37
+ text = await this.config.sttProvider.transcribe(audio, { language: this.config.language });
38
+ }
39
+ else {
40
+ text = audio.toString('utf-8'); // Fallback: treat as text
41
+ }
42
+ this.logger.debug('STT result', { text });
43
+ // Create message and send to agent
44
+ const message = {
45
+ id: `voice_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
46
+ role: 'user',
47
+ content: text,
48
+ timestamp: Date.now(),
49
+ metadata: { channel: 'voice' },
50
+ };
51
+ const response = await this.handler(message);
52
+ // TTS
53
+ let audioResponse;
54
+ if (this.config.ttsProvider) {
55
+ audioResponse = await this.config.ttsProvider.synthesize(response.content, { language: this.config.language });
56
+ }
57
+ return { text, response: response.content, audioResponse };
58
+ }
59
+ setSTTProvider(provider) {
60
+ this.config.sttProvider = provider;
61
+ }
62
+ setTTSProvider(provider) {
63
+ this.config.ttsProvider = provider;
64
+ }
65
+ }
66
+ exports.VoiceChannel = VoiceChannel;
67
+ //# sourceMappingURL=voice.js.map
@@ -0,0 +1,40 @@
1
+ import { BaseChannel } from './index';
2
+ export interface WebhookConfig {
3
+ port?: number;
4
+ path?: string;
5
+ secret?: string;
6
+ retryAttempts?: number;
7
+ retryDelayMs?: number;
8
+ outgoing?: WebhookOutgoing[];
9
+ }
10
+ export interface WebhookOutgoing {
11
+ name: string;
12
+ url: string;
13
+ secret?: string;
14
+ events: string[];
15
+ }
16
+ export interface WebhookPayload {
17
+ event: string;
18
+ data: unknown;
19
+ timestamp: number;
20
+ id: string;
21
+ }
22
+ export declare class WebhookChannel extends BaseChannel {
23
+ type: string;
24
+ private config;
25
+ private server;
26
+ private logger;
27
+ private outgoing;
28
+ constructor(config?: WebhookConfig);
29
+ start(): Promise<void>;
30
+ stop(): Promise<void>;
31
+ /** Send a webhook to outgoing endpoints */
32
+ send(event: string, data: unknown): Promise<void>;
33
+ private sendWithRetry;
34
+ private httpPost;
35
+ private readBody;
36
+ verifySignature(body: string, signature: string, secret: string): boolean;
37
+ createSignature(body: string, secret: string): string;
38
+ addOutgoing(outgoing: WebhookOutgoing): void;
39
+ }
40
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WebhookChannel = void 0;
37
+ const index_1 = require("./index");
38
+ const logger_1 = require("../core/logger");
39
+ const http = __importStar(require("http"));
40
+ const crypto = __importStar(require("crypto"));
41
+ // ── Webhook Channel ─────────────────────────────────────────
42
+ class WebhookChannel extends index_1.BaseChannel {
43
+ type = 'webhook';
44
+ config;
45
+ server = null;
46
+ logger = new logger_1.Logger('webhook-channel');
47
+ outgoing = [];
48
+ constructor(config) {
49
+ super();
50
+ this.config = config ?? {};
51
+ this.outgoing = config?.outgoing ?? [];
52
+ }
53
+ async start() {
54
+ const port = this.config.port ?? 3100;
55
+ const path = this.config.path ?? '/webhook';
56
+ this.server = http.createServer(async (req, res) => {
57
+ if (req.url !== path || req.method !== 'POST') {
58
+ res.writeHead(404);
59
+ res.end('Not found');
60
+ return;
61
+ }
62
+ try {
63
+ const body = await this.readBody(req);
64
+ // Verify signature if secret configured
65
+ if (this.config.secret) {
66
+ const signature = req.headers['x-webhook-signature'];
67
+ if (!this.verifySignature(body, signature, this.config.secret)) {
68
+ res.writeHead(401);
69
+ res.end('Invalid signature');
70
+ return;
71
+ }
72
+ }
73
+ const payload = JSON.parse(body);
74
+ const message = {
75
+ id: payload.id ?? `wh_${Date.now()}`,
76
+ role: 'user',
77
+ content: typeof payload.data === 'string' ? payload.data : JSON.stringify(payload.data),
78
+ timestamp: payload.timestamp ?? Date.now(),
79
+ metadata: { channel: 'webhook', event: payload.event },
80
+ };
81
+ if (this.handler) {
82
+ const response = await this.handler(message);
83
+ res.writeHead(200, { 'Content-Type': 'application/json' });
84
+ res.end(JSON.stringify({ status: 'ok', response: response.content }));
85
+ }
86
+ else {
87
+ res.writeHead(200, { 'Content-Type': 'application/json' });
88
+ res.end(JSON.stringify({ status: 'ok' }));
89
+ }
90
+ }
91
+ catch (err) {
92
+ this.logger.error('Webhook error', { error: err.message });
93
+ res.writeHead(500);
94
+ res.end('Internal error');
95
+ }
96
+ });
97
+ return new Promise((resolve) => {
98
+ this.server.listen(port, () => {
99
+ this.logger.info('Webhook channel started', { port, path });
100
+ resolve();
101
+ });
102
+ });
103
+ }
104
+ async stop() {
105
+ return new Promise((resolve) => {
106
+ if (this.server) {
107
+ this.server.close(() => {
108
+ this.logger.info('Webhook channel stopped');
109
+ resolve();
110
+ });
111
+ }
112
+ else {
113
+ resolve();
114
+ }
115
+ });
116
+ }
117
+ /** Send a webhook to outgoing endpoints */
118
+ async send(event, data) {
119
+ const targets = this.outgoing.filter(o => o.events.includes(event) || o.events.includes('*'));
120
+ for (const target of targets) {
121
+ const payload = {
122
+ event,
123
+ data,
124
+ timestamp: Date.now(),
125
+ id: `wh_out_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
126
+ };
127
+ await this.sendWithRetry(target, payload);
128
+ }
129
+ }
130
+ async sendWithRetry(target, payload) {
131
+ const maxRetries = this.config.retryAttempts ?? 3;
132
+ const retryDelay = this.config.retryDelayMs ?? 1000;
133
+ const body = JSON.stringify(payload);
134
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
135
+ try {
136
+ await this.httpPost(target.url, body, target.secret);
137
+ return;
138
+ }
139
+ catch (err) {
140
+ if (attempt === maxRetries) {
141
+ this.logger.error('Webhook delivery failed', { target: target.name, error: err.message });
142
+ throw err;
143
+ }
144
+ await new Promise(r => setTimeout(r, retryDelay * Math.pow(2, attempt)));
145
+ }
146
+ }
147
+ }
148
+ httpPost(url, body, secret) {
149
+ return new Promise((resolve, reject) => {
150
+ const urlObj = new URL(url);
151
+ const headers = { 'Content-Type': 'application/json' };
152
+ if (secret) {
153
+ headers['x-webhook-signature'] = this.createSignature(body, secret);
154
+ }
155
+ const req = http.request({ hostname: urlObj.hostname, port: urlObj.port, path: urlObj.pathname, method: 'POST', headers }, (res) => {
156
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
157
+ resolve();
158
+ }
159
+ else {
160
+ reject(new Error(`HTTP ${res.statusCode}`));
161
+ }
162
+ res.resume();
163
+ });
164
+ req.on('error', reject);
165
+ req.write(body);
166
+ req.end();
167
+ });
168
+ }
169
+ readBody(req) {
170
+ return new Promise((resolve, reject) => {
171
+ const chunks = [];
172
+ req.on('data', (c) => chunks.push(c));
173
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
174
+ req.on('error', reject);
175
+ });
176
+ }
177
+ verifySignature(body, signature, secret) {
178
+ if (!signature)
179
+ return false;
180
+ const expected = this.createSignature(body, secret);
181
+ if (signature.length !== expected.length)
182
+ return false;
183
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
184
+ }
185
+ createSignature(body, secret) {
186
+ return crypto.createHmac('sha256', secret).update(body).digest('hex');
187
+ }
188
+ addOutgoing(outgoing) {
189
+ this.outgoing.push(outgoing);
190
+ }
191
+ }
192
+ exports.WebhookChannel = WebhookChannel;
193
+ //# sourceMappingURL=webhook.js.map
package/dist/cli.js CHANGED
@@ -47,8 +47,14 @@ const code_reviewer_1 = require("./templates/code-reviewer");
47
47
  const hr_recruiter_1 = require("./templates/hr-recruiter");
48
48
  const project_manager_1 = require("./templates/project-manager");
49
49
  const content_writer_1 = require("./templates/content-writer");
50
+ const legal_assistant_1 = require("./templates/legal-assistant");
51
+ const financial_advisor_1 = require("./templates/financial-advisor");
52
+ const executive_assistant_1 = require("./templates/executive-assistant");
50
53
  const customer_service_2 = require("./templates/customer-service");
51
54
  const analytics_1 = require("./analytics");
55
+ const openclaw_1 = require("./deploy/openclaw");
56
+ const workflow_1 = require("./core/workflow");
57
+ const versioning_1 = require("./core/versioning");
52
58
  const program = new commander_1.Command();
53
59
  const color = {
54
60
  green: (s) => `\x1b[32m${s}\x1b[0m`,
@@ -71,13 +77,16 @@ const icon = {
71
77
  file: '📄',
72
78
  };
73
79
  const TEMPLATES = {
74
- 'customer-service': { label: 'Customer Service FAQ + human handoff', factory: customer_service_1.createCustomerServiceConfig },
75
- 'sales-assistant': { label: 'Sales Assistant product Q&A + lead capture', factory: sales_assistant_1.createSalesAssistantConfig },
76
- 'knowledge-base': { label: 'Knowledge Base RAG with DeepBrain', factory: knowledge_base_1.createKnowledgeBaseConfig },
77
- 'code-reviewer': { label: 'Code Reviewer bug detection + style checks', factory: code_reviewer_1.createCodeReviewerConfig },
78
- 'hr-recruiter': { label: 'HR Recruiter resume screening + interview scheduling', factory: hr_recruiter_1.createHRRecruiterConfig },
79
- 'project-manager': { label: 'Project Manager task tracking + meeting notes', factory: project_manager_1.createProjectManagerConfig },
80
- 'content-writer': { label: 'Content Writer blog posts + social media + SEO', factory: content_writer_1.createContentWriterConfig },
80
+ 'customer-service': { label: 'Customer Service - FAQ + human handoff', factory: customer_service_1.createCustomerServiceConfig },
81
+ 'sales-assistant': { label: 'Sales Assistant - product Q&A + lead capture', factory: sales_assistant_1.createSalesAssistantConfig },
82
+ 'knowledge-base': { label: 'Knowledge Base - RAG with DeepBrain', factory: knowledge_base_1.createKnowledgeBaseConfig },
83
+ 'code-reviewer': { label: 'Code Reviewer - bug detection + style checks', factory: code_reviewer_1.createCodeReviewerConfig },
84
+ 'hr-recruiter': { label: 'HR Recruiter - resume screening + interview scheduling', factory: hr_recruiter_1.createHRRecruiterConfig },
85
+ 'project-manager': { label: 'Project Manager - task tracking + meeting notes', factory: project_manager_1.createProjectManagerConfig },
86
+ 'content-writer': { label: 'Content Writer - blog posts + social media + SEO', factory: content_writer_1.createContentWriterConfig },
87
+ 'legal-assistant': { label: 'Legal Assistant - contract review + compliance + legal research', factory: legal_assistant_1.createLegalAssistantConfig },
88
+ 'financial-advisor': { label: 'Financial Advisor - budget analysis + expense tracking + planning', factory: financial_advisor_1.createFinancialAdvisorConfig },
89
+ 'executive-assistant': { label: 'Executive Assistant - calendar + email drafting + meeting prep', factory: executive_assistant_1.createExecutiveAssistantConfig },
81
90
  };
82
91
  async function prompt(question, defaultValue) {
83
92
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -100,8 +109,8 @@ async function select(question, options) {
100
109
  }
101
110
  program
102
111
  .name('opc')
103
- .description('OPC Agent Open Agent Framework for business workstations')
104
- .version('0.3.0');
112
+ .description('OPC Agent - Open Agent Framework for business workstations')
113
+ .version('0.5.0');
105
114
  program
106
115
  .command('init')
107
116
  .description('Initialize a new OPC agent project (interactive)')
@@ -109,7 +118,7 @@ program
109
118
  .option('-t, --template <template>', 'Template to use')
110
119
  .option('-y, --yes', 'Skip prompts, use defaults')
111
120
  .action(async (nameArg, opts) => {
112
- console.log(`\n${icon.rocket} ${color.bold('OPC Agent Create New Project')}\n`);
121
+ console.log(`\n${icon.rocket} ${color.bold('OPC Agent - Create New Project')}\n`);
113
122
  const name = opts.yes ? (nameArg ?? 'my-agent') : (nameArg ?? await prompt('Project name', 'my-agent'));
114
123
  const template = opts.yes
115
124
  ? (opts.template ?? 'customer-service')
@@ -126,7 +135,7 @@ program
126
135
  fs.writeFileSync(path.join(dir, 'oad.yaml'), yaml.dump(config, { lineWidth: 120 }));
127
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`);
128
137
  console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')}`);
129
- console.log(` ${icon.file} oad.yaml Agent definition`);
138
+ console.log(` ${icon.file} oad.yaml - Agent definition`);
130
139
  console.log(` ${icon.file} README.md`);
131
140
  console.log(`\n Template: ${color.cyan(template)}`);
132
141
  console.log(`\n${color.dim('Next:')} cd ${name} && opc run\n`);
@@ -238,7 +247,7 @@ program
238
247
  .description('Hot-reload development mode')
239
248
  .option('-f, --file <file>', 'OAD file', 'oad.yaml')
240
249
  .action(async (opts) => {
241
- console.log(`\n${icon.gear} ${color.bold('Development mode')} watching for changes...\n`);
250
+ console.log(`\n${icon.gear} ${color.bold('Development mode')} - watching for changes...\n`);
242
251
  let runtime = null;
243
252
  const startAgent = async () => {
244
253
  try {
@@ -263,7 +272,7 @@ program
263
272
  if (fs.existsSync(watchPath)) {
264
273
  const isDir = fs.statSync(watchPath).isDirectory();
265
274
  fs.watch(watchPath, { recursive: isDir }, async (_event, filename) => {
266
- console.log(`\n${icon.info} ${color.dim(`Change detected: ${filename}`)} restarting...`);
275
+ console.log(`\n${icon.info} ${color.dim(`Change detected: ${filename}`)} - restarting...`);
267
276
  await startAgent();
268
277
  });
269
278
  }
@@ -324,6 +333,53 @@ program
324
333
  console.log(` Browse templates with: ${color.cyan('opc init --template <name>')}`);
325
334
  console.log(`\n Available templates: ${Object.keys(TEMPLATES).map(t => color.cyan(t)).join(', ')}\n`);
326
335
  });
336
+ // ── Deploy command ────────────────────────────────────────────
337
+ program
338
+ .command('deploy')
339
+ .description('Deploy agent to a target runtime')
340
+ .option('-f, --file <file>', 'OAD file', 'oad.yaml')
341
+ .option('-t, --target <target>', 'Deploy target', 'openclaw')
342
+ .option('-o, --output <dir>', 'Output directory')
343
+ .option('--install', 'Also register in OpenClaw config')
344
+ .action(async (opts) => {
345
+ if (opts.target !== 'openclaw') {
346
+ console.error(`${icon.error} Unknown target: ${color.bold(opts.target)}. Supported: openclaw`);
347
+ process.exit(1);
348
+ }
349
+ try {
350
+ const runtime = new runtime_1.AgentRuntime();
351
+ const config = await runtime.loadConfig(opts.file);
352
+ const agentId = config.metadata.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
353
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
354
+ const defaultOutput = path.join(homeDir, '.openclaw', 'agents', agentId, 'workspace');
355
+ const outputDir = path.resolve(opts.output ?? defaultOutput);
356
+ 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
+ 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
+ }
365
+ 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`);
376
+ }
377
+ }
378
+ catch (err) {
379
+ console.error(`${icon.error} Deploy failed:`, err instanceof Error ? err.message : err);
380
+ process.exit(1);
381
+ }
382
+ });
327
383
  // ── Tool commands ────────────────────────────────────────────
328
384
  const toolCmd = program.command('tool').description('Manage MCP tools');
329
385
  toolCmd
@@ -376,5 +432,79 @@ program
376
432
  console.log(` Uptime: ${Math.round(snap.uptime / 1000)}s`);
377
433
  console.log(`\n ${color.dim('Run an agent to collect analytics data.')}\n`);
378
434
  });
435
+ // 🔄 Workflow commands ────────────────────────────────────────
436
+ const workflowCmd = program.command('workflow').description('Manage workflows');
437
+ workflowCmd
438
+ .command('run')
439
+ .description('Run a workflow defined in OAD')
440
+ .argument('<name>', 'Workflow name')
441
+ .option('-f, --file <file>', 'OAD file', 'oad.yaml')
442
+ .action(async (name, opts) => {
443
+ console.log(`\n${icon.gear} Running workflow "${color.bold(name)}"...`);
444
+ const runtime = new runtime_1.AgentRuntime();
445
+ 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.`);
450
+ process.exit(1);
451
+ }
452
+ 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`);
456
+ });
457
+ workflowCmd
458
+ .command('list')
459
+ .description('List workflows in OAD')
460
+ .option('-f, --file <file>', 'OAD file', 'oad.yaml')
461
+ .action(async (opts) => {
462
+ const runtime = new runtime_1.AgentRuntime();
463
+ 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();
474
+ }
475
+ });
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(() => {
482
+ const vm = new versioning_1.VersionManager();
483
+ const versions = vm.list();
484
+ console.log(`\n${icon.gear} ${color.bold('Agent Versions')}\n`);
485
+ 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();
493
+ }
494
+ });
495
+ versionCmd
496
+ .command('rollback')
497
+ .description('Rollback to a saved version')
498
+ .argument('<version>', 'Version to rollback to')
499
+ .action((version) => {
500
+ const vm = new versioning_1.VersionManager();
501
+ const oad = vm.rollback(version);
502
+ if (!oad) {
503
+ console.error(`${icon.error} Version "${version}" not found.`);
504
+ process.exit(1);
505
+ }
506
+ fs.writeFileSync('oad.yaml', oad);
507
+ console.log(`${icon.success} Rolled back to version ${color.bold(version)}.`);
508
+ });
379
509
  program.parse();
380
510
  //# sourceMappingURL=cli.js.map
@@ -0,0 +1,46 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Room } from './room';
3
+ import type { IAgent } from './types';
4
+ export interface AgentCapability {
5
+ name: string;
6
+ description: string;
7
+ inputSchema?: Record<string, unknown>;
8
+ outputSchema?: Record<string, unknown>;
9
+ }
10
+ export interface AgentRegistration {
11
+ agentName: string;
12
+ capabilities: AgentCapability[];
13
+ endpoint?: string;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+ export interface A2ARequest {
17
+ id: string;
18
+ from: string;
19
+ to: string;
20
+ capability: string;
21
+ payload: string;
22
+ timestamp: number;
23
+ timeout?: number;
24
+ }
25
+ export interface A2AResponse {
26
+ requestId: string;
27
+ from: string;
28
+ status: 'success' | 'error' | 'timeout';
29
+ payload?: string;
30
+ error?: string;
31
+ timestamp: number;
32
+ }
33
+ export declare class AgentRegistry extends EventEmitter {
34
+ private registrations;
35
+ private agents;
36
+ private room;
37
+ private logger;
38
+ constructor(room?: Room);
39
+ register(agent: IAgent, capabilities: AgentCapability[]): void;
40
+ unregister(name: string): void;
41
+ discover(capability?: string): AgentRegistration[];
42
+ getAgent(name: string): IAgent | undefined;
43
+ request(req: A2ARequest): Promise<A2AResponse>;
44
+ call(from: string, to: string, capability: string, payload: string): Promise<A2AResponse>;
45
+ }
46
+ //# sourceMappingURL=a2a.d.ts.map