opc-agent 0.2.0 → 0.4.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/dist/analytics/index.d.ts +31 -0
- package/dist/analytics/index.js +52 -0
- package/dist/channels/voice.d.ts +43 -0
- package/dist/channels/voice.js +67 -0
- package/dist/channels/webhook.d.ts +40 -0
- package/dist/channels/webhook.js +193 -0
- package/dist/cli.js +157 -13
- package/dist/core/a2a.d.ts +46 -0
- package/dist/core/a2a.js +99 -0
- package/dist/core/hitl.d.ts +41 -0
- package/dist/core/hitl.js +100 -0
- package/dist/core/performance.d.ts +50 -0
- package/dist/core/performance.js +148 -0
- package/dist/core/room.d.ts +24 -0
- package/dist/core/room.js +97 -0
- package/dist/core/sandbox.d.ts +28 -0
- package/dist/core/sandbox.js +118 -0
- package/dist/core/versioning.d.ts +29 -0
- package/dist/core/versioning.js +114 -0
- package/dist/core/workflow.d.ts +59 -0
- package/dist/core/workflow.js +174 -0
- package/dist/i18n/index.d.ts +13 -0
- package/dist/i18n/index.js +73 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +36 -1
- package/dist/plugins/index.d.ts +47 -0
- package/dist/plugins/index.js +59 -0
- package/dist/schema/oad.d.ts +483 -15
- package/dist/schema/oad.js +53 -2
- package/dist/templates/content-writer.d.ts +36 -0
- package/dist/templates/content-writer.js +52 -0
- package/dist/templates/executive-assistant.d.ts +20 -0
- package/dist/templates/executive-assistant.js +70 -0
- package/dist/templates/financial-advisor.d.ts +15 -0
- package/dist/templates/financial-advisor.js +60 -0
- package/dist/templates/hr-recruiter.d.ts +36 -0
- package/dist/templates/hr-recruiter.js +52 -0
- package/dist/templates/legal-assistant.d.ts +15 -0
- package/dist/templates/legal-assistant.js +70 -0
- package/dist/templates/project-manager.d.ts +36 -0
- package/dist/templates/project-manager.js +52 -0
- package/dist/tools/mcp.d.ts +32 -0
- package/dist/tools/mcp.js +49 -0
- package/package.json +46 -46
- package/src/analytics/index.ts +66 -0
- package/src/channels/voice.ts +106 -0
- package/src/channels/webhook.ts +199 -0
- package/src/cli.ts +173 -16
- package/src/core/a2a.ts +143 -0
- package/src/core/hitl.ts +138 -0
- package/src/core/performance.ts +187 -0
- package/src/core/room.ts +109 -0
- package/src/core/sandbox.ts +101 -0
- package/src/core/versioning.ts +106 -0
- package/src/core/workflow.ts +235 -0
- package/src/i18n/index.ts +79 -0
- package/src/index.ts +29 -0
- package/src/plugins/index.ts +87 -0
- package/src/schema/oad.ts +59 -1
- package/src/templates/content-writer.ts +58 -0
- package/src/templates/executive-assistant.ts +71 -0
- package/src/templates/financial-advisor.ts +60 -0
- package/src/templates/hr-recruiter.ts +58 -0
- package/src/templates/legal-assistant.ts +71 -0
- package/src/templates/project-manager.ts +58 -0
- package/src/tools/mcp.ts +76 -0
- package/tests/a2a.test.ts +66 -0
- package/tests/analytics.test.ts +50 -0
- package/tests/hitl.test.ts +71 -0
- package/tests/i18n.test.ts +41 -0
- package/tests/mcp.test.ts +54 -0
- package/tests/performance.test.ts +115 -0
- package/tests/plugin.test.ts +74 -0
- package/tests/room.test.ts +106 -0
- package/tests/sandbox.test.ts +46 -0
- package/tests/templates.test.ts +77 -0
- package/tests/versioning.test.ts +75 -0
- package/tests/voice.test.ts +61 -0
- package/tests/webhook.test.ts +29 -0
- package/tests/workflow.test.ts +143 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Analytics — track messages, response times, skill usage, errors, tokens.
|
|
3
|
+
*/
|
|
4
|
+
export interface AnalyticsSnapshot {
|
|
5
|
+
messagesProcessed: number;
|
|
6
|
+
avgResponseTimeMs: number;
|
|
7
|
+
skillUsage: Record<string, number>;
|
|
8
|
+
errorCount: number;
|
|
9
|
+
tokenUsage: {
|
|
10
|
+
input: number;
|
|
11
|
+
output: number;
|
|
12
|
+
total: number;
|
|
13
|
+
};
|
|
14
|
+
uptime: number;
|
|
15
|
+
startedAt: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class Analytics {
|
|
18
|
+
private messagesProcessed;
|
|
19
|
+
private totalResponseTimeMs;
|
|
20
|
+
private skillUsage;
|
|
21
|
+
private errorCount;
|
|
22
|
+
private tokenUsage;
|
|
23
|
+
private startedAt;
|
|
24
|
+
recordMessage(responseTimeMs: number): void;
|
|
25
|
+
recordSkillUsage(skillName: string): void;
|
|
26
|
+
recordError(): void;
|
|
27
|
+
recordTokens(input: number, output: number): void;
|
|
28
|
+
getSnapshot(): AnalyticsSnapshot;
|
|
29
|
+
reset(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Analytics = void 0;
|
|
4
|
+
class Analytics {
|
|
5
|
+
messagesProcessed = 0;
|
|
6
|
+
totalResponseTimeMs = 0;
|
|
7
|
+
skillUsage = {};
|
|
8
|
+
errorCount = 0;
|
|
9
|
+
tokenUsage = { input: 0, output: 0 };
|
|
10
|
+
startedAt = Date.now();
|
|
11
|
+
recordMessage(responseTimeMs) {
|
|
12
|
+
this.messagesProcessed++;
|
|
13
|
+
this.totalResponseTimeMs += responseTimeMs;
|
|
14
|
+
}
|
|
15
|
+
recordSkillUsage(skillName) {
|
|
16
|
+
this.skillUsage[skillName] = (this.skillUsage[skillName] ?? 0) + 1;
|
|
17
|
+
}
|
|
18
|
+
recordError() {
|
|
19
|
+
this.errorCount++;
|
|
20
|
+
}
|
|
21
|
+
recordTokens(input, output) {
|
|
22
|
+
this.tokenUsage.input += input;
|
|
23
|
+
this.tokenUsage.output += output;
|
|
24
|
+
}
|
|
25
|
+
getSnapshot() {
|
|
26
|
+
return {
|
|
27
|
+
messagesProcessed: this.messagesProcessed,
|
|
28
|
+
avgResponseTimeMs: this.messagesProcessed > 0
|
|
29
|
+
? Math.round(this.totalResponseTimeMs / this.messagesProcessed)
|
|
30
|
+
: 0,
|
|
31
|
+
skillUsage: { ...this.skillUsage },
|
|
32
|
+
errorCount: this.errorCount,
|
|
33
|
+
tokenUsage: {
|
|
34
|
+
input: this.tokenUsage.input,
|
|
35
|
+
output: this.tokenUsage.output,
|
|
36
|
+
total: this.tokenUsage.input + this.tokenUsage.output,
|
|
37
|
+
},
|
|
38
|
+
uptime: Date.now() - this.startedAt,
|
|
39
|
+
startedAt: this.startedAt,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
reset() {
|
|
43
|
+
this.messagesProcessed = 0;
|
|
44
|
+
this.totalResponseTimeMs = 0;
|
|
45
|
+
this.skillUsage = {};
|
|
46
|
+
this.errorCount = 0;
|
|
47
|
+
this.tokenUsage = { input: 0, output: 0 };
|
|
48
|
+
this.startedAt = Date.now();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.Analytics = Analytics;
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -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
|
@@ -44,9 +44,17 @@ const customer_service_1 = require("./templates/customer-service");
|
|
|
44
44
|
const sales_assistant_1 = require("./templates/sales-assistant");
|
|
45
45
|
const knowledge_base_1 = require("./templates/knowledge-base");
|
|
46
46
|
const code_reviewer_1 = require("./templates/code-reviewer");
|
|
47
|
+
const hr_recruiter_1 = require("./templates/hr-recruiter");
|
|
48
|
+
const project_manager_1 = require("./templates/project-manager");
|
|
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");
|
|
47
53
|
const customer_service_2 = require("./templates/customer-service");
|
|
54
|
+
const analytics_1 = require("./analytics");
|
|
55
|
+
const workflow_1 = require("./core/workflow");
|
|
56
|
+
const versioning_1 = require("./core/versioning");
|
|
48
57
|
const program = new commander_1.Command();
|
|
49
|
-
// ── Colorful output helpers ──────────────────────────────────
|
|
50
58
|
const color = {
|
|
51
59
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
52
60
|
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
@@ -68,10 +76,16 @@ const icon = {
|
|
|
68
76
|
file: '📄',
|
|
69
77
|
};
|
|
70
78
|
const TEMPLATES = {
|
|
71
|
-
'customer-service': { label: 'Customer Service
|
|
72
|
-
'sales-assistant': { label: 'Sales Assistant
|
|
73
|
-
'knowledge-base': { label: 'Knowledge Base
|
|
74
|
-
'code-reviewer': { label: 'Code Reviewer
|
|
79
|
+
'customer-service': { label: 'Customer Service - FAQ + human handoff', factory: customer_service_1.createCustomerServiceConfig },
|
|
80
|
+
'sales-assistant': { label: 'Sales Assistant - product Q&A + lead capture', factory: sales_assistant_1.createSalesAssistantConfig },
|
|
81
|
+
'knowledge-base': { label: 'Knowledge Base - RAG with DeepBrain', factory: knowledge_base_1.createKnowledgeBaseConfig },
|
|
82
|
+
'code-reviewer': { label: 'Code Reviewer - bug detection + style checks', factory: code_reviewer_1.createCodeReviewerConfig },
|
|
83
|
+
'hr-recruiter': { label: 'HR Recruiter - resume screening + interview scheduling', factory: hr_recruiter_1.createHRRecruiterConfig },
|
|
84
|
+
'project-manager': { label: 'Project Manager - task tracking + meeting notes', factory: project_manager_1.createProjectManagerConfig },
|
|
85
|
+
'content-writer': { label: 'Content Writer - blog posts + social media + SEO', factory: content_writer_1.createContentWriterConfig },
|
|
86
|
+
'legal-assistant': { label: 'Legal Assistant - contract review + compliance + legal research', factory: legal_assistant_1.createLegalAssistantConfig },
|
|
87
|
+
'financial-advisor': { label: 'Financial Advisor - budget analysis + expense tracking + planning', factory: financial_advisor_1.createFinancialAdvisorConfig },
|
|
88
|
+
'executive-assistant': { label: 'Executive Assistant - calendar + email drafting + meeting prep', factory: executive_assistant_1.createExecutiveAssistantConfig },
|
|
75
89
|
};
|
|
76
90
|
async function prompt(question, defaultValue) {
|
|
77
91
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -94,8 +108,8 @@ async function select(question, options) {
|
|
|
94
108
|
}
|
|
95
109
|
program
|
|
96
110
|
.name('opc')
|
|
97
|
-
.description('OPC Agent
|
|
98
|
-
.version('0.
|
|
111
|
+
.description('OPC Agent - Open Agent Framework for business workstations')
|
|
112
|
+
.version('0.4.0');
|
|
99
113
|
program
|
|
100
114
|
.command('init')
|
|
101
115
|
.description('Initialize a new OPC agent project (interactive)')
|
|
@@ -103,7 +117,7 @@ program
|
|
|
103
117
|
.option('-t, --template <template>', 'Template to use')
|
|
104
118
|
.option('-y, --yes', 'Skip prompts, use defaults')
|
|
105
119
|
.action(async (nameArg, opts) => {
|
|
106
|
-
console.log(`\n${icon.rocket} ${color.bold('OPC Agent
|
|
120
|
+
console.log(`\n${icon.rocket} ${color.bold('OPC Agent - Create New Project')}\n`);
|
|
107
121
|
const name = opts.yes ? (nameArg ?? 'my-agent') : (nameArg ?? await prompt('Project name', 'my-agent'));
|
|
108
122
|
const template = opts.yes
|
|
109
123
|
? (opts.template ?? 'customer-service')
|
|
@@ -120,7 +134,7 @@ program
|
|
|
120
134
|
fs.writeFileSync(path.join(dir, 'oad.yaml'), yaml.dump(config, { lineWidth: 120 }));
|
|
121
135
|
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`);
|
|
122
136
|
console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')}`);
|
|
123
|
-
console.log(` ${icon.file} oad.yaml
|
|
137
|
+
console.log(` ${icon.file} oad.yaml - Agent definition`);
|
|
124
138
|
console.log(` ${icon.file} README.md`);
|
|
125
139
|
console.log(`\n Template: ${color.cyan(template)}`);
|
|
126
140
|
console.log(`\n${color.dim('Next:')} cd ${name} && opc run\n`);
|
|
@@ -159,6 +173,11 @@ program
|
|
|
159
173
|
console.log(` Channels: ${s.channels.map(c => c.type).join(', ') || color.dim('(none)')}`);
|
|
160
174
|
console.log(` Skills: ${s.skills.map(sk => sk.name).join(', ') || color.dim('(none)')}`);
|
|
161
175
|
console.log(` Trust: ${s.dtv?.trust?.level ?? 'sandbox'}`);
|
|
176
|
+
console.log(` Streaming: ${s.streaming ? 'enabled' : 'disabled'}`);
|
|
177
|
+
console.log(` Locale: ${s.locale ?? 'en'}`);
|
|
178
|
+
if (s.room) {
|
|
179
|
+
console.log(` Room: ${s.room.name}`);
|
|
180
|
+
}
|
|
162
181
|
if (m.marketplace) {
|
|
163
182
|
console.log(` Category: ${m.marketplace.category ?? color.dim('(none)')}`);
|
|
164
183
|
console.log(` Pricing: ${m.marketplace.pricing ?? 'free'}`);
|
|
@@ -227,7 +246,7 @@ program
|
|
|
227
246
|
.description('Hot-reload development mode')
|
|
228
247
|
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
229
248
|
.action(async (opts) => {
|
|
230
|
-
console.log(`\n${icon.gear} ${color.bold('Development mode')}
|
|
249
|
+
console.log(`\n${icon.gear} ${color.bold('Development mode')} - watching for changes...\n`);
|
|
231
250
|
let runtime = null;
|
|
232
251
|
const startAgent = async () => {
|
|
233
252
|
try {
|
|
@@ -247,13 +266,12 @@ program
|
|
|
247
266
|
}
|
|
248
267
|
};
|
|
249
268
|
await startAgent();
|
|
250
|
-
// Watch for file changes
|
|
251
269
|
const watchPaths = [opts.file, 'src'];
|
|
252
270
|
for (const watchPath of watchPaths) {
|
|
253
271
|
if (fs.existsSync(watchPath)) {
|
|
254
272
|
const isDir = fs.statSync(watchPath).isDirectory();
|
|
255
|
-
fs.watch(watchPath, { recursive: isDir }, async (
|
|
256
|
-
console.log(`\n${icon.info} ${color.dim(`Change detected: ${filename}`)}
|
|
273
|
+
fs.watch(watchPath, { recursive: isDir }, async (_event, filename) => {
|
|
274
|
+
console.log(`\n${icon.info} ${color.dim(`Change detected: ${filename}`)} - restarting...`);
|
|
257
275
|
await startAgent();
|
|
258
276
|
});
|
|
259
277
|
}
|
|
@@ -314,5 +332,131 @@ program
|
|
|
314
332
|
console.log(` Browse templates with: ${color.cyan('opc init --template <name>')}`);
|
|
315
333
|
console.log(`\n Available templates: ${Object.keys(TEMPLATES).map(t => color.cyan(t)).join(', ')}\n`);
|
|
316
334
|
});
|
|
335
|
+
// ── Tool commands ────────────────────────────────────────────
|
|
336
|
+
const toolCmd = program.command('tool').description('Manage MCP tools');
|
|
337
|
+
toolCmd
|
|
338
|
+
.command('list')
|
|
339
|
+
.description('List available MCP tools')
|
|
340
|
+
.action(() => {
|
|
341
|
+
console.log(`\n${icon.gear} ${color.bold('MCP Tools')}\n`);
|
|
342
|
+
console.log(` ${color.dim('No tools installed yet.')}`);
|
|
343
|
+
console.log(`\n Add tools with: ${color.cyan('opc tool add <name>')}\n`);
|
|
344
|
+
});
|
|
345
|
+
toolCmd
|
|
346
|
+
.command('add')
|
|
347
|
+
.description('Add an MCP tool from registry')
|
|
348
|
+
.argument('<name>', 'Tool name')
|
|
349
|
+
.action((name) => {
|
|
350
|
+
console.log(`\n🚧 Tool registry coming soon!`);
|
|
351
|
+
console.log(` Would add tool: ${color.cyan(name)}\n`);
|
|
352
|
+
});
|
|
353
|
+
// ── Plugin commands ──────────────────────────────────────────
|
|
354
|
+
const pluginCmd = program.command('plugin').description('Manage plugins');
|
|
355
|
+
pluginCmd
|
|
356
|
+
.command('list')
|
|
357
|
+
.description('List installed plugins')
|
|
358
|
+
.action(() => {
|
|
359
|
+
console.log(`\n${icon.gear} ${color.bold('Installed Plugins')}\n`);
|
|
360
|
+
console.log(` ${color.dim('No plugins installed yet.')}`);
|
|
361
|
+
console.log(`\n Add plugins with: ${color.cyan('opc plugin add <name>')}\n`);
|
|
362
|
+
});
|
|
363
|
+
pluginCmd
|
|
364
|
+
.command('add')
|
|
365
|
+
.description('Add a plugin')
|
|
366
|
+
.argument('<name>', 'Plugin name')
|
|
367
|
+
.action((name) => {
|
|
368
|
+
console.log(`\n🚧 Plugin registry coming soon!`);
|
|
369
|
+
console.log(` Would add plugin: ${color.cyan(name)}\n`);
|
|
370
|
+
});
|
|
371
|
+
// ── Stats command ────────────────────────────────────────────
|
|
372
|
+
program
|
|
373
|
+
.command('stats')
|
|
374
|
+
.description('Show agent analytics')
|
|
375
|
+
.action(() => {
|
|
376
|
+
const analytics = new analytics_1.Analytics();
|
|
377
|
+
// Show demo stats
|
|
378
|
+
const snap = analytics.getSnapshot();
|
|
379
|
+
console.log(`\n${icon.gear} ${color.bold('Agent Analytics')}\n`);
|
|
380
|
+
console.log(` Messages Processed: ${snap.messagesProcessed}`);
|
|
381
|
+
console.log(` Avg Response Time: ${snap.avgResponseTimeMs}ms`);
|
|
382
|
+
console.log(` Error Count: ${snap.errorCount}`);
|
|
383
|
+
console.log(` Token Usage: ${snap.tokenUsage.total} (in: ${snap.tokenUsage.input}, out: ${snap.tokenUsage.output})`);
|
|
384
|
+
console.log(` Uptime: ${Math.round(snap.uptime / 1000)}s`);
|
|
385
|
+
console.log(`\n ${color.dim('Run an agent to collect analytics data.')}\n`);
|
|
386
|
+
});
|
|
387
|
+
// 🔄 Workflow commands ────────────────────────────────────────
|
|
388
|
+
const workflowCmd = program.command('workflow').description('Manage workflows');
|
|
389
|
+
workflowCmd
|
|
390
|
+
.command('run')
|
|
391
|
+
.description('Run a workflow defined in OAD')
|
|
392
|
+
.argument('<name>', 'Workflow name')
|
|
393
|
+
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
394
|
+
.action(async (name, opts) => {
|
|
395
|
+
console.log(`\n${icon.gear} Running workflow "${color.bold(name)}"...`);
|
|
396
|
+
const runtime = new runtime_1.AgentRuntime();
|
|
397
|
+
const config = await runtime.loadConfig(opts.file);
|
|
398
|
+
const workflows = config.spec.workflows ?? [];
|
|
399
|
+
const wfDef = workflows.find((w) => w.name === name);
|
|
400
|
+
if (!wfDef) {
|
|
401
|
+
console.error(`${icon.error} Workflow "${name}" not found in OAD.`);
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
const engine = new workflow_1.WorkflowEngine();
|
|
405
|
+
engine.registerWorkflow(wfDef);
|
|
406
|
+
console.log(`${icon.success} Workflow "${name}" loaded with ${wfDef.steps?.length ?? 0} steps.`);
|
|
407
|
+
console.log(`${color.dim(' Workflow execution requires a running agent context.')}\n`);
|
|
408
|
+
});
|
|
409
|
+
workflowCmd
|
|
410
|
+
.command('list')
|
|
411
|
+
.description('List workflows in OAD')
|
|
412
|
+
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
413
|
+
.action(async (opts) => {
|
|
414
|
+
const runtime = new runtime_1.AgentRuntime();
|
|
415
|
+
const config = await runtime.loadConfig(opts.file);
|
|
416
|
+
const workflows = config.spec.workflows ?? [];
|
|
417
|
+
console.log(`\n${icon.gear} ${color.bold('Workflows')}\n`);
|
|
418
|
+
if (workflows.length === 0) {
|
|
419
|
+
console.log(` ${color.dim('No workflows defined.')}\n`);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
for (const wf of workflows) {
|
|
423
|
+
console.log(` ${color.cyan(wf.name)} - ${wf.description ?? color.dim('(no description)')}`);
|
|
424
|
+
}
|
|
425
|
+
console.log();
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
// 📦 Version commands ─────────────────────────────────────────
|
|
429
|
+
const versionCmd = program.command('version').description('Manage agent versions');
|
|
430
|
+
versionCmd
|
|
431
|
+
.command('list')
|
|
432
|
+
.description('List saved versions')
|
|
433
|
+
.action(() => {
|
|
434
|
+
const vm = new versioning_1.VersionManager();
|
|
435
|
+
const versions = vm.list();
|
|
436
|
+
console.log(`\n${icon.gear} ${color.bold('Agent Versions')}\n`);
|
|
437
|
+
if (versions.length === 0) {
|
|
438
|
+
console.log(` ${color.dim('No versions saved.')}\n`);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
for (const v of versions) {
|
|
442
|
+
console.log(` ${color.cyan(v.version)} - ${new Date(v.timestamp).toISOString()} ${v.description ?? ''}`);
|
|
443
|
+
}
|
|
444
|
+
console.log();
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
versionCmd
|
|
448
|
+
.command('rollback')
|
|
449
|
+
.description('Rollback to a saved version')
|
|
450
|
+
.argument('<version>', 'Version to rollback to')
|
|
451
|
+
.action((version) => {
|
|
452
|
+
const vm = new versioning_1.VersionManager();
|
|
453
|
+
const oad = vm.rollback(version);
|
|
454
|
+
if (!oad) {
|
|
455
|
+
console.error(`${icon.error} Version "${version}" not found.`);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
fs.writeFileSync('oad.yaml', oad);
|
|
459
|
+
console.log(`${icon.success} Rolled back to version ${color.bold(version)}.`);
|
|
460
|
+
});
|
|
317
461
|
program.parse();
|
|
318
462
|
//# sourceMappingURL=cli.js.map
|