opc-agent 3.0.1 → 4.0.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/README.md +404 -74
- package/README.zh-CN.md +82 -0
- package/dist/channels/dingtalk.d.ts +17 -0
- package/dist/channels/dingtalk.js +38 -0
- package/dist/channels/googlechat.d.ts +14 -0
- package/dist/channels/googlechat.js +37 -0
- package/dist/channels/imessage.d.ts +13 -0
- package/dist/channels/imessage.js +28 -0
- package/dist/channels/irc.d.ts +20 -0
- package/dist/channels/irc.js +71 -0
- package/dist/channels/line.d.ts +14 -0
- package/dist/channels/line.js +28 -0
- package/dist/channels/matrix.d.ts +15 -0
- package/dist/channels/matrix.js +28 -0
- package/dist/channels/mattermost.d.ts +18 -0
- package/dist/channels/mattermost.js +49 -0
- package/dist/channels/msteams.d.ts +14 -0
- package/dist/channels/msteams.js +28 -0
- package/dist/channels/nostr.d.ts +14 -0
- package/dist/channels/nostr.js +28 -0
- package/dist/channels/qq.d.ts +15 -0
- package/dist/channels/qq.js +28 -0
- package/dist/channels/signal.d.ts +14 -0
- package/dist/channels/signal.js +28 -0
- package/dist/channels/sms.d.ts +15 -0
- package/dist/channels/sms.js +28 -0
- package/dist/channels/twitch.d.ts +17 -0
- package/dist/channels/twitch.js +59 -0
- package/dist/channels/voice-call.d.ts +27 -0
- package/dist/channels/voice-call.js +82 -0
- package/dist/channels/whatsapp.d.ts +14 -0
- package/dist/channels/whatsapp.js +28 -0
- package/dist/cli/chat.d.ts +2 -0
- package/dist/cli/chat.js +134 -0
- package/dist/cli/setup.d.ts +4 -0
- package/dist/cli/setup.js +303 -0
- package/dist/cli.js +142 -6
- package/dist/core/api-server.d.ts +25 -0
- package/dist/core/api-server.js +286 -0
- package/dist/core/audio.d.ts +50 -0
- package/dist/core/audio.js +68 -0
- package/dist/core/context-discovery.d.ts +16 -0
- package/dist/core/context-discovery.js +107 -0
- package/dist/core/context-refs.d.ts +29 -0
- package/dist/core/context-refs.js +162 -0
- package/dist/core/gateway.d.ts +53 -0
- package/dist/core/gateway.js +80 -0
- package/dist/core/heartbeat.d.ts +19 -0
- package/dist/core/heartbeat.js +50 -0
- package/dist/core/hooks.d.ts +28 -0
- package/dist/core/hooks.js +82 -0
- package/dist/core/ide-bridge.d.ts +53 -0
- package/dist/core/ide-bridge.js +97 -0
- package/dist/core/node-network.d.ts +23 -0
- package/dist/core/node-network.js +77 -0
- package/dist/core/profiles.d.ts +27 -0
- package/dist/core/profiles.js +131 -0
- package/dist/core/sandbox.d.ts +25 -0
- package/dist/core/sandbox.js +84 -1
- package/dist/core/session-manager.d.ts +33 -0
- package/dist/core/session-manager.js +157 -0
- package/dist/core/vision.d.ts +45 -0
- package/dist/core/vision.js +177 -0
- package/dist/hub/brain-seed.d.ts +14 -0
- package/dist/hub/brain-seed.js +77 -0
- package/dist/hub/client.d.ts +25 -0
- package/dist/hub/client.js +44 -0
- package/dist/index.d.ts +66 -1
- package/dist/index.js +95 -3
- package/dist/memory/context-compressor.d.ts +43 -0
- package/dist/memory/context-compressor.js +167 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +5 -1
- package/dist/memory/user-profiler.d.ts +50 -0
- package/dist/memory/user-profiler.js +201 -0
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +54 -1
- package/dist/scheduler/cron-engine.d.ts +41 -0
- package/dist/scheduler/cron-engine.js +200 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +7 -0
- package/dist/schema/oad.d.ts +12 -12
- package/dist/security/approvals.d.ts +53 -0
- package/dist/security/approvals.js +115 -0
- package/dist/security/elevated.d.ts +41 -0
- package/dist/security/elevated.js +89 -0
- package/dist/security/index.d.ts +6 -0
- package/dist/security/index.js +7 -1
- package/dist/security/secrets.d.ts +34 -0
- package/dist/security/secrets.js +115 -0
- package/dist/skills/builtin/index.d.ts +6 -0
- package/dist/skills/builtin/index.js +402 -0
- package/dist/skills/marketplace.d.ts +30 -0
- package/dist/skills/marketplace.js +142 -0
- package/dist/skills/types.d.ts +34 -0
- package/dist/skills/types.js +16 -0
- package/dist/studio/server.d.ts +25 -0
- package/dist/studio/server.js +780 -0
- package/dist/studio/templates-data.d.ts +21 -0
- package/dist/studio/templates-data.js +148 -0
- package/dist/studio-ui/index.html +2502 -1073
- package/dist/tools/builtin/browser.d.ts +47 -0
- package/dist/tools/builtin/browser.js +284 -0
- package/dist/tools/builtin/home-assistant.d.ts +12 -0
- package/dist/tools/builtin/home-assistant.js +126 -0
- package/dist/tools/builtin/index.d.ts +7 -1
- package/dist/tools/builtin/index.js +23 -2
- package/dist/tools/builtin/rl-tools.d.ts +13 -0
- package/dist/tools/builtin/rl-tools.js +228 -0
- package/dist/tools/builtin/vision.d.ts +6 -0
- package/dist/tools/builtin/vision.js +61 -0
- package/dist/tools/builtin/web-search.d.ts +9 -0
- package/dist/tools/builtin/web-search.js +150 -0
- package/dist/tools/document-processor.d.ts +39 -0
- package/dist/tools/document-processor.js +188 -0
- package/dist/tools/image-generator.d.ts +42 -0
- package/dist/tools/image-generator.js +136 -0
- package/dist/tools/web-scraper.d.ts +20 -0
- package/dist/tools/web-scraper.js +148 -0
- package/dist/tools/web-search.d.ts +51 -0
- package/dist/tools/web-search.js +152 -0
- package/install.ps1 +154 -0
- package/install.sh +164 -0
- package/package.json +63 -52
- package/src/channels/dingtalk.ts +46 -0
- package/src/channels/googlechat.ts +42 -0
- package/src/channels/imessage.ts +32 -0
- package/src/channels/irc.ts +82 -0
- package/src/channels/line.ts +33 -0
- package/src/channels/matrix.ts +34 -0
- package/src/channels/mattermost.ts +57 -0
- package/src/channels/msteams.ts +33 -0
- package/src/channels/nostr.ts +33 -0
- package/src/channels/qq.ts +34 -0
- package/src/channels/signal.ts +33 -0
- package/src/channels/sms.ts +34 -0
- package/src/channels/twitch.ts +65 -0
- package/src/channels/voice-call.ts +100 -0
- package/src/channels/whatsapp.ts +33 -0
- package/src/cli/chat.ts +99 -0
- package/src/cli/setup.ts +314 -0
- package/src/cli.ts +148 -6
- package/src/core/api-server.ts +277 -0
- package/src/core/audio.ts +98 -0
- package/src/core/context-discovery.ts +85 -0
- package/src/core/context-refs.ts +140 -0
- package/src/core/gateway.ts +106 -0
- package/src/core/heartbeat.ts +51 -0
- package/src/core/hooks.ts +105 -0
- package/src/core/ide-bridge.ts +133 -0
- package/src/core/node-network.ts +86 -0
- package/src/core/profiles.ts +122 -0
- package/src/core/sandbox.ts +100 -0
- package/src/core/session-manager.ts +137 -0
- package/src/core/vision.ts +180 -0
- package/src/hub/brain-seed.ts +54 -0
- package/src/hub/client.ts +60 -0
- package/src/index.ts +86 -1
- package/src/memory/context-compressor.ts +189 -0
- package/src/memory/index.ts +4 -0
- package/src/memory/user-profiler.ts +215 -0
- package/src/providers/index.ts +64 -1
- package/src/scheduler/cron-engine.ts +191 -0
- package/src/scheduler/index.ts +2 -0
- package/src/security/approvals.ts +143 -0
- package/src/security/elevated.ts +105 -0
- package/src/security/index.ts +6 -0
- package/src/security/secrets.ts +129 -0
- package/src/skills/builtin/index.ts +408 -0
- package/src/skills/marketplace.ts +113 -0
- package/src/skills/types.ts +42 -0
- package/src/studio/server.ts +1591 -791
- package/src/studio/templates-data.ts +178 -0
- package/src/studio-ui/index.html +2502 -1073
- package/src/tools/builtin/browser.ts +299 -0
- package/src/tools/builtin/home-assistant.ts +116 -0
- package/src/tools/builtin/index.ts +37 -28
- package/src/tools/builtin/rl-tools.ts +243 -0
- package/src/tools/builtin/vision.ts +64 -0
- package/src/tools/builtin/web-search.ts +126 -0
- package/src/tools/document-processor.ts +213 -0
- package/src/tools/image-generator.ts +150 -0
- package/src/tools/web-scraper.ts +179 -0
- package/src/tools/web-search.ts +180 -0
- package/tests/api-server.test.ts +148 -0
- package/tests/approvals.test.ts +89 -0
- package/tests/audio.test.ts +40 -0
- package/tests/browser.test.ts +179 -0
- package/tests/builtin-tools.test.ts +83 -83
- package/tests/channels-extra.test.ts +45 -0
- package/tests/context-compressor.test.ts +172 -0
- package/tests/context-refs.test.ts +121 -0
- package/tests/cron-engine.test.ts +101 -0
- package/tests/document-processor.test.ts +69 -0
- package/tests/e2e-nocode.test.ts +442 -0
- package/tests/elevated.test.ts +69 -0
- package/tests/gateway.test.ts +63 -71
- package/tests/home-assistant.test.ts +40 -0
- package/tests/hooks.test.ts +79 -0
- package/tests/ide-bridge.test.ts +38 -0
- package/tests/image-generator.test.ts +84 -0
- package/tests/node-network.test.ts +74 -0
- package/tests/profiles.test.ts +61 -0
- package/tests/rl-tools.test.ts +93 -0
- package/tests/sandbox-manager.test.ts +46 -0
- package/tests/secrets.test.ts +107 -0
- package/tests/settings-api.test.ts +148 -0
- package/tests/setup.test.ts +73 -0
- package/tests/studio.test.ts +402 -229
- package/tests/tools/builtin-extended.test.ts +138 -138
- package/tests/user-profiler.test.ts +169 -0
- package/tests/v090-features.test.ts +254 -0
- package/tests/vision.test.ts +61 -0
- package/tests/voice-call.test.ts +47 -0
- package/tests/voice-interaction.test.ts +38 -0
- package/tests/web-search.test.ts +155 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
import type { Message } from './types';
|
|
5
|
+
|
|
6
|
+
export interface Session {
|
|
7
|
+
id: string;
|
|
8
|
+
agentId: string;
|
|
9
|
+
channel: string;
|
|
10
|
+
messages: Message[];
|
|
11
|
+
metadata: Record<string, any>;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
lastActivity: number;
|
|
14
|
+
parentId?: string;
|
|
15
|
+
compactedAt?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class SessionManager {
|
|
19
|
+
private sessions: Map<string, Session> = new Map();
|
|
20
|
+
private storageDir: string;
|
|
21
|
+
|
|
22
|
+
constructor(storageDir?: string) {
|
|
23
|
+
this.storageDir = storageDir || path.join(process.env.HOME || process.env.USERPROFILE || '~', '.opc', 'sessions');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
create(agentId: string, channel: string, parentId?: string): Session {
|
|
27
|
+
const session: Session = {
|
|
28
|
+
id: crypto.randomUUID(),
|
|
29
|
+
agentId,
|
|
30
|
+
channel,
|
|
31
|
+
messages: [],
|
|
32
|
+
metadata: {},
|
|
33
|
+
createdAt: Date.now(),
|
|
34
|
+
lastActivity: Date.now(),
|
|
35
|
+
parentId,
|
|
36
|
+
};
|
|
37
|
+
this.sessions.set(session.id, session);
|
|
38
|
+
return session;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get(id: string): Session | null {
|
|
42
|
+
return this.sessions.get(id) || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
list(filter?: { agentId?: string; channel?: string; active?: boolean }): Session[] {
|
|
46
|
+
let result = Array.from(this.sessions.values());
|
|
47
|
+
if (filter?.agentId) result = result.filter(s => s.agentId === filter.agentId);
|
|
48
|
+
if (filter?.channel) result = result.filter(s => s.channel === filter.channel);
|
|
49
|
+
if (filter?.active !== undefined) {
|
|
50
|
+
const cutoff = Date.now() - 30 * 60 * 1000; // 30 min
|
|
51
|
+
result = result.filter(s => filter.active ? s.lastActivity > cutoff : s.lastActivity <= cutoff);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
addMessage(sessionId: string, message: Message): void {
|
|
57
|
+
const session = this.sessions.get(sessionId);
|
|
58
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
59
|
+
session.messages.push(message);
|
|
60
|
+
session.lastActivity = Date.now();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async compact(sessionId: string, brain?: any): Promise<void> {
|
|
64
|
+
const session = this.sessions.get(sessionId);
|
|
65
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
66
|
+
if (brain && typeof brain.compress === 'function') {
|
|
67
|
+
const compressed = await brain.compress(session.messages);
|
|
68
|
+
session.messages = [{ id: 'compacted', role: 'system', content: compressed, timestamp: Date.now() }];
|
|
69
|
+
} else {
|
|
70
|
+
// Simple: keep first and last 5 messages
|
|
71
|
+
if (session.messages.length > 10) {
|
|
72
|
+
const first = session.messages.slice(0, 2);
|
|
73
|
+
const last = session.messages.slice(-5);
|
|
74
|
+
session.messages = [...first, { id: 'compacted', role: 'system', content: `[${session.messages.length - 7} messages compacted]`, timestamp: Date.now() }, ...last];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
session.compactedAt = Date.now();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
prune(maxAge: number): number {
|
|
81
|
+
const cutoff = Date.now() - maxAge;
|
|
82
|
+
let pruned = 0;
|
|
83
|
+
for (const [id, session] of this.sessions) {
|
|
84
|
+
if (session.lastActivity < cutoff) {
|
|
85
|
+
this.sessions.delete(id);
|
|
86
|
+
pruned++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return pruned;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getLineage(sessionId: string): Session[] {
|
|
93
|
+
const lineage: Session[] = [];
|
|
94
|
+
let current = this.sessions.get(sessionId);
|
|
95
|
+
while (current) {
|
|
96
|
+
lineage.unshift(current);
|
|
97
|
+
current = current.parentId ? this.sessions.get(current.parentId) || undefined : undefined;
|
|
98
|
+
}
|
|
99
|
+
return lineage;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fork(sessionId: string): Session {
|
|
103
|
+
const parent = this.sessions.get(sessionId);
|
|
104
|
+
if (!parent) throw new Error(`Session ${sessionId} not found`);
|
|
105
|
+
return this.create(parent.agentId, parent.channel, parent.id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export(sessionId: string): string {
|
|
109
|
+
const session = this.sessions.get(sessionId);
|
|
110
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
111
|
+
const lines = [`# Session ${session.id}`, `Agent: ${session.agentId} | Channel: ${session.channel}`, `Created: ${new Date(session.createdAt).toISOString()}`, ''];
|
|
112
|
+
for (const msg of session.messages) {
|
|
113
|
+
lines.push(`**${msg.role}** (${new Date(msg.timestamp).toISOString()}):`);
|
|
114
|
+
lines.push(msg.content);
|
|
115
|
+
lines.push('');
|
|
116
|
+
}
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
save(): void {
|
|
121
|
+
if (!fs.existsSync(this.storageDir)) {
|
|
122
|
+
fs.mkdirSync(this.storageDir, { recursive: true });
|
|
123
|
+
}
|
|
124
|
+
for (const [id, session] of this.sessions) {
|
|
125
|
+
fs.writeFileSync(path.join(this.storageDir, `${id}.json`), JSON.stringify(session, null, 2));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
load(): void {
|
|
130
|
+
if (!fs.existsSync(this.storageDir)) return;
|
|
131
|
+
const files = fs.readdirSync(this.storageDir).filter(f => f.endsWith('.json'));
|
|
132
|
+
for (const file of files) {
|
|
133
|
+
const data = JSON.parse(fs.readFileSync(path.join(this.storageDir, file), 'utf-8'));
|
|
134
|
+
this.sessions.set(data.id, data);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
// ─── Types ───────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface ImageInput {
|
|
7
|
+
type: 'base64' | 'url' | 'file';
|
|
8
|
+
data: string;
|
|
9
|
+
mimeType?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface VisionResult {
|
|
13
|
+
description: string;
|
|
14
|
+
text_content?: string;
|
|
15
|
+
objects?: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ─── MIME detection from magic bytes ─────────────────────────
|
|
19
|
+
|
|
20
|
+
const MAGIC_BYTES: Array<{ bytes: number[]; mime: string }> = [
|
|
21
|
+
{ bytes: [0x89, 0x50, 0x4E, 0x47], mime: 'image/png' },
|
|
22
|
+
{ bytes: [0xFF, 0xD8, 0xFF], mime: 'image/jpeg' },
|
|
23
|
+
{ bytes: [0x47, 0x49, 0x46, 0x38], mime: 'image/gif' },
|
|
24
|
+
{ bytes: [0x52, 0x49, 0x46, 0x46], mime: 'image/webp' }, // RIFF header (WebP)
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export function detectMimeType(buffer: Buffer): string {
|
|
28
|
+
for (const { bytes, mime } of MAGIC_BYTES) {
|
|
29
|
+
if (bytes.every((b, i) => buffer[i] === b)) {
|
|
30
|
+
return mime;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return 'application/octet-stream';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ─── VisionManager ───────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export interface VisionManagerConfig {
|
|
39
|
+
model?: string;
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
baseURL?: string;
|
|
42
|
+
maxImageSize?: number; // bytes, default 20MB
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class VisionManager {
|
|
46
|
+
private config: VisionManagerConfig;
|
|
47
|
+
|
|
48
|
+
constructor(config: VisionManagerConfig = {}) {
|
|
49
|
+
this.config = {
|
|
50
|
+
model: config.model ?? 'gpt-4o',
|
|
51
|
+
apiKey: config.apiKey ?? process.env.OPENAI_API_KEY ?? '',
|
|
52
|
+
baseURL: config.baseURL ?? 'https://api.openai.com/v1',
|
|
53
|
+
maxImageSize: config.maxImageSize ?? 20 * 1024 * 1024,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Load image data as base64, detecting MIME type.
|
|
59
|
+
*/
|
|
60
|
+
async loadImage(input: ImageInput): Promise<{ base64: string; mimeType: string }> {
|
|
61
|
+
if (input.type === 'base64') {
|
|
62
|
+
const buf = Buffer.from(input.data, 'base64');
|
|
63
|
+
return { base64: input.data, mimeType: input.mimeType ?? detectMimeType(buf) };
|
|
64
|
+
}
|
|
65
|
+
if (input.type === 'file') {
|
|
66
|
+
const buf = fs.readFileSync(input.data);
|
|
67
|
+
if (buf.length > this.config.maxImageSize!) {
|
|
68
|
+
throw new Error(`Image exceeds max size: ${buf.length} > ${this.config.maxImageSize}`);
|
|
69
|
+
}
|
|
70
|
+
return { base64: buf.toString('base64'), mimeType: input.mimeType ?? detectMimeType(buf) };
|
|
71
|
+
}
|
|
72
|
+
// URL — return as-is for API
|
|
73
|
+
return { base64: '', mimeType: input.mimeType ?? 'image/jpeg' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Prepare messages in OpenAI multimodal format.
|
|
78
|
+
*/
|
|
79
|
+
prepareMessage(images: ImageInput[], text: string): Array<Record<string, unknown>> {
|
|
80
|
+
const content: Array<Record<string, unknown>> = [];
|
|
81
|
+
if (text) {
|
|
82
|
+
content.push({ type: 'text', text });
|
|
83
|
+
}
|
|
84
|
+
for (const img of images) {
|
|
85
|
+
if (img.type === 'url') {
|
|
86
|
+
content.push({
|
|
87
|
+
type: 'image_url',
|
|
88
|
+
image_url: { url: img.data },
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
// base64 or file — will need to be loaded first
|
|
92
|
+
const mime = img.mimeType ?? 'image/jpeg';
|
|
93
|
+
const data = img.type === 'base64' ? img.data : fs.readFileSync(img.data).toString('base64');
|
|
94
|
+
content.push({
|
|
95
|
+
type: 'image_url',
|
|
96
|
+
image_url: { url: `data:${mime};base64,${data}` },
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return [{ role: 'user', content }];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Analyze an image with optional prompt.
|
|
105
|
+
*/
|
|
106
|
+
async analyze(image: ImageInput, prompt?: string): Promise<VisionResult> {
|
|
107
|
+
const messages = this.prepareMessage([image], prompt ?? 'Describe this image in detail.');
|
|
108
|
+
|
|
109
|
+
if (!this.config.apiKey) {
|
|
110
|
+
throw new Error('Vision API key not configured. Set OPENAI_API_KEY or pass apiKey in config.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
118
|
+
},
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
model: this.config.model,
|
|
121
|
+
messages,
|
|
122
|
+
max_tokens: 1000,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
throw new Error(`Vision API error: ${response.status} ${response.statusText}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const data = await response.json() as any;
|
|
131
|
+
const text = data.choices?.[0]?.message?.content ?? '';
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
description: text,
|
|
135
|
+
text_content: undefined,
|
|
136
|
+
objects: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Extract text (OCR) from an image.
|
|
142
|
+
*/
|
|
143
|
+
async extractText(image: ImageInput): Promise<string> {
|
|
144
|
+
const result = await this.analyze(image, 'Extract all visible text from this image. Return only the text, nothing else.');
|
|
145
|
+
return result.description;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Compare multiple images.
|
|
150
|
+
*/
|
|
151
|
+
async compareImages(images: ImageInput[], prompt?: string): Promise<string> {
|
|
152
|
+
if (images.length < 2) throw new Error('Need at least 2 images to compare');
|
|
153
|
+
|
|
154
|
+
const messages = this.prepareMessage(images, prompt ?? 'Compare these images and describe the differences.');
|
|
155
|
+
|
|
156
|
+
if (!this.config.apiKey) {
|
|
157
|
+
throw new Error('Vision API key not configured.');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: {
|
|
163
|
+
'Content-Type': 'application/json',
|
|
164
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
165
|
+
},
|
|
166
|
+
body: JSON.stringify({
|
|
167
|
+
model: this.config.model,
|
|
168
|
+
messages,
|
|
169
|
+
max_tokens: 1000,
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
throw new Error(`Vision API error: ${response.status} ${response.statusText}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const data = await response.json() as any;
|
|
178
|
+
return data.choices?.[0]?.message?.content ?? '';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brain-seed downloader and auto-learner.
|
|
3
|
+
* Downloads brain-seed files from Hub and optionally imports into DeepBrain.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import type { HubBrainSeed } from './client';
|
|
9
|
+
|
|
10
|
+
export interface BrainSeedResult {
|
|
11
|
+
savedFiles: string[];
|
|
12
|
+
learnedCount: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Save brain-seed files to disk and optionally auto-learn into DeepBrain.
|
|
17
|
+
*/
|
|
18
|
+
export async function downloadAndLearnBrainSeeds(
|
|
19
|
+
projectDir: string,
|
|
20
|
+
seeds: HubBrainSeed[],
|
|
21
|
+
): Promise<BrainSeedResult> {
|
|
22
|
+
if (!seeds || seeds.length === 0) {
|
|
23
|
+
return { savedFiles: [], learnedCount: 0 };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const seedDir = path.join(projectDir, 'brain-seed');
|
|
27
|
+
fs.mkdirSync(seedDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const savedFiles: string[] = [];
|
|
30
|
+
for (const seed of seeds) {
|
|
31
|
+
const filePath = path.join(seedDir, seed.filename);
|
|
32
|
+
fs.writeFileSync(filePath, seed.content, 'utf-8');
|
|
33
|
+
savedFiles.push(seed.filename);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try auto-learn into DeepBrain (optional dependency)
|
|
37
|
+
let learnedCount = 0;
|
|
38
|
+
try {
|
|
39
|
+
const { Brain } = require('deepbrain');
|
|
40
|
+
const brain = new Brain({ database: path.join(projectDir, 'data', 'brain.db') });
|
|
41
|
+
for (const seed of seeds) {
|
|
42
|
+
await brain.learn(seed.content, {
|
|
43
|
+
slug: `brain-seed/${seed.filename.replace(/\.md$/, '')}`,
|
|
44
|
+
title: `Brain Seed: ${seed.tier}`,
|
|
45
|
+
namespace: `seed/${seed.tier}`,
|
|
46
|
+
});
|
|
47
|
+
learnedCount++;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// deepbrain not installed — that's fine, files are saved
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { savedFiles, learnedCount };
|
|
54
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workstation Hub API client
|
|
3
|
+
* Fetches agent templates and brain-seed knowledge from the Hub.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const HUB_URL = process.env.OPC_HUB_URL || 'https://hub.deepleaper.com';
|
|
7
|
+
const HUB_TIMEOUT = 5000;
|
|
8
|
+
|
|
9
|
+
export interface HubTemplate {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
category: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface HubTemplateDetail extends HubTemplate {
|
|
18
|
+
files: Record<string, string>;
|
|
19
|
+
brainSeeds: HubBrainSeed[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface HubBrainSeed {
|
|
23
|
+
filename: string;
|
|
24
|
+
content: string;
|
|
25
|
+
tier: 'industry' | 'job' | 'workstation';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function hubFetch<T>(path: string): Promise<T> {
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), HUB_TIMEOUT);
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(`${HUB_URL}${path}`, {
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
headers: { 'Accept': 'application/json', 'User-Agent': 'opc-agent/2.0' },
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok) throw new Error(`Hub API ${res.status} ${res.statusText}`);
|
|
37
|
+
return (await res.json()) as T;
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function fetchTemplates(query?: string): Promise<HubTemplate[]> {
|
|
44
|
+
const qs = query ? `?q=${encodeURIComponent(query)}` : '';
|
|
45
|
+
return hubFetch<HubTemplate[]>(`/api/templates${qs}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function fetchTemplate(id: string): Promise<HubTemplateDetail> {
|
|
49
|
+
return hubFetch<HubTemplateDetail>(`/api/templates/${encodeURIComponent(id)}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function fetchBrainSeeds(templateId: string): Promise<HubBrainSeed[]> {
|
|
53
|
+
return hubFetch<HubBrainSeed[]>(`/api/templates/${encodeURIComponent(templateId)}/brain-seeds`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isHubAvailable(): Promise<boolean> {
|
|
57
|
+
return hubFetch<{ ok: boolean }>('/api/health')
|
|
58
|
+
.then(() => true)
|
|
59
|
+
.catch(() => false);
|
|
60
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -78,6 +78,26 @@ export { TextAnalysisTool } from './tools/text-analysis';
|
|
|
78
78
|
export { HttpSkill } from './skills/http';
|
|
79
79
|
export { WebhookTriggerSkill } from './skills/webhook-trigger';
|
|
80
80
|
export type { WebhookTarget } from './skills/webhook-trigger';
|
|
81
|
+
|
|
82
|
+
// v0.9.0 channels
|
|
83
|
+
export { DingTalkChannel } from './channels/dingtalk';
|
|
84
|
+
export type { DingTalkChannelConfig } from './channels/dingtalk';
|
|
85
|
+
export { MattermostChannel } from './channels/mattermost';
|
|
86
|
+
export type { MattermostChannelConfig } from './channels/mattermost';
|
|
87
|
+
export { GoogleChatChannel } from './channels/googlechat';
|
|
88
|
+
export type { GoogleChatChannelConfig } from './channels/googlechat';
|
|
89
|
+
export { TwitchChannel } from './channels/twitch';
|
|
90
|
+
export type { TwitchChannelConfig } from './channels/twitch';
|
|
91
|
+
export { IRCChannel } from './channels/irc';
|
|
92
|
+
export type { IRCChannelConfig } from './channels/irc';
|
|
93
|
+
|
|
94
|
+
// v0.9.0 core
|
|
95
|
+
export { ContextDiscovery } from './core/context-discovery';
|
|
96
|
+
export type { ContextFile } from './core/context-discovery';
|
|
97
|
+
export { SessionManager } from './core/session-manager';
|
|
98
|
+
export type { Session } from './core/session-manager';
|
|
99
|
+
export { HeartbeatManager } from './core/heartbeat';
|
|
100
|
+
export type { HeartbeatConfig } from './core/heartbeat';
|
|
81
101
|
export { SchedulerSkill } from './skills/scheduler';
|
|
82
102
|
export type { ScheduledTask } from './skills/scheduler';
|
|
83
103
|
export { DocumentSkill } from './skills/document';
|
|
@@ -111,6 +131,20 @@ export { contentFilterPlugin, createContentFilterPlugin } from './plugins/conten
|
|
|
111
131
|
export { GuardrailManager, createGuardrailsFromConfig } from './security/guardrails';
|
|
112
132
|
export type { GuardrailConfig, GuardrailRule, GuardrailResult, GuardrailViolation } from './security/guardrails';
|
|
113
133
|
|
|
134
|
+
// v3.1.0 — Exec Approvals + Elevated + Secrets
|
|
135
|
+
export { ExecApprovalManager } from './security/approvals';
|
|
136
|
+
export type { ExecApprovalPolicy, ExecApprovalRequest, ExecApprovalHistory, ApprovalRequestCallback } from './security/approvals';
|
|
137
|
+
export { ElevatedManager } from './security/elevated';
|
|
138
|
+
export type { ElevationMode, ElevationAuditEntry } from './security/elevated';
|
|
139
|
+
export { SecretsManager } from './security/secrets';
|
|
140
|
+
export type { SecretsStore } from './security/secrets';
|
|
141
|
+
|
|
142
|
+
// v3.1.0 — Hooks + Audio
|
|
143
|
+
export { HookManager, ALL_HOOK_EVENTS } from './core/hooks';
|
|
144
|
+
export type { HookEvent, HookContext, HookHandler } from './core/hooks';
|
|
145
|
+
export { AudioProcessor } from './core/audio';
|
|
146
|
+
export type { AudioFormat, TranscribeOptions, SynthesizeOptions, TranscribeResult, SynthesizeResult } from './core/audio';
|
|
147
|
+
|
|
114
148
|
// v1.1.0 modules
|
|
115
149
|
export { FeishuChannel } from './channels/feishu';
|
|
116
150
|
export type { FeishuChannelConfig } from './channels/feishu';
|
|
@@ -142,7 +176,9 @@ export { Scheduler, parseCron, cronMatches } from './core/scheduler';
|
|
|
142
176
|
export type { CronJob, JobHandler } from './core/scheduler';
|
|
143
177
|
|
|
144
178
|
// v1.5.0 — built-in tools + MCP client
|
|
145
|
-
export { getBuiltinTools, getBuiltinToolsByName } from './tools/builtin';
|
|
179
|
+
export { getBuiltinTools, getBuiltinToolsByName, rlTools, homeAssistantTools, configureHomeAssistant, webSearchTools, webSearchTool, webReadTool } from './tools/builtin';
|
|
180
|
+
export { webSearch, parseDuckDuckGoHTML, type SearchResult as WebSearchResult, type WebSearchConfig, type SearchEngine } from './tools/web-search';
|
|
181
|
+
export { scrapeUrl, extractReadableContent, type ScrapedContent } from './tools/web-scraper';
|
|
146
182
|
export { MCPClient } from './tools/mcp-client';
|
|
147
183
|
export type { MCPServerConfig } from './tools/mcp-client';
|
|
148
184
|
|
|
@@ -186,6 +222,55 @@ export type {
|
|
|
186
222
|
StepStartedEvent, StepFinishedEvent, CustomEvent,
|
|
187
223
|
} from './protocols/agui';
|
|
188
224
|
|
|
225
|
+
// v2.1.0 — API Server + Context References
|
|
226
|
+
export { APIServer } from './core/api-server';
|
|
227
|
+
export type { APIServerConfig } from './core/api-server';
|
|
228
|
+
export { ContextRefResolver } from './core/context-refs';
|
|
229
|
+
export type { ContextRef, RefType } from './core/context-refs';
|
|
230
|
+
|
|
231
|
+
// v2.2.0 — Vision
|
|
232
|
+
export { VisionManager, detectMimeType } from './core/vision';
|
|
233
|
+
export type { ImageInput, VisionResult, VisionManagerConfig } from './core/vision';
|
|
234
|
+
export { visionTools, visionAnalyzeTool, visionExtractTextTool, visionCompareTool } from './tools/builtin/vision';
|
|
235
|
+
|
|
236
|
+
// v2.2.0 — Additional channels
|
|
237
|
+
export { WhatsAppChannel } from './channels/whatsapp';
|
|
238
|
+
export type { WhatsAppChannelConfig } from './channels/whatsapp';
|
|
239
|
+
export { SignalChannel } from './channels/signal';
|
|
240
|
+
export type { SignalChannelConfig } from './channels/signal';
|
|
241
|
+
export { MatrixChannel } from './channels/matrix';
|
|
242
|
+
export type { MatrixChannelConfig } from './channels/matrix';
|
|
243
|
+
export { IMessageChannel } from './channels/imessage';
|
|
244
|
+
export type { IMessageChannelConfig } from './channels/imessage';
|
|
245
|
+
export { LINEChannel } from './channels/line';
|
|
246
|
+
export type { LINEChannelConfig } from './channels/line';
|
|
247
|
+
export { MSTeamsChannel } from './channels/msteams';
|
|
248
|
+
export type { MSTeamsChannelConfig } from './channels/msteams';
|
|
249
|
+
export { QQChannel } from './channels/qq';
|
|
250
|
+
export type { QQChannelConfig } from './channels/qq';
|
|
251
|
+
export { NostrChannel } from './channels/nostr';
|
|
252
|
+
export type { NostrChannelConfig } from './channels/nostr';
|
|
253
|
+
export { SMSChannel } from './channels/sms';
|
|
254
|
+
export type { SMSChannelConfig } from './channels/sms';
|
|
255
|
+
|
|
256
|
+
// v2.3.0 — Voice Calls + IDE + Node Network + Gateway
|
|
257
|
+
export { VoiceCallManager } from './channels/voice-call';
|
|
258
|
+
export type { VoiceCallConfig } from './channels/voice-call';
|
|
259
|
+
export { IDEBridge } from './core/ide-bridge';
|
|
260
|
+
export type { IDEConfig, Diagnostic, TextEdit, Range, SearchOptions, SearchResult as IDESearchResult } from './core/ide-bridge';
|
|
261
|
+
export { NodeNetwork } from './core/node-network';
|
|
262
|
+
export type { RemoteNode } from './core/node-network';
|
|
263
|
+
export { Gateway } from './core/gateway';
|
|
264
|
+
export type { AgentConfig as GatewayAgentConfig, ChannelConfig as GatewayChannelConfig, GatewayConfig } from './core/gateway';
|
|
265
|
+
|
|
266
|
+
// v2.2.0 — Remote Sandbox
|
|
267
|
+
export { SandboxManager } from './core/sandbox';
|
|
268
|
+
export type { RemoteSandboxConfig, ExecResult } from './core/sandbox';
|
|
269
|
+
|
|
270
|
+
// v2.2.0 — Profiles
|
|
271
|
+
export { ProfileManager } from './core/profiles';
|
|
272
|
+
export type { Profile, ProfileConfig } from './core/profiles';
|
|
273
|
+
|
|
189
274
|
// v2.0.0 - Pre-built tool integrations (20 tools)
|
|
190
275
|
export {
|
|
191
276
|
SlackTool, EmailSendTool, WebhookTool,
|