opc-agent 4.1.0 → 4.1.2
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/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- package/CHANGELOG.md +48 -48
- package/CONTRIBUTING.md +36 -36
- package/README.zh-CN.md +497 -497
- package/USABILITY-ISSUES.md +73 -0
- package/dist/channels/web.js +8 -2
- package/dist/channels/wechat.js +6 -6
- package/dist/cli.js +200 -85
- package/dist/core/runtime.js +37 -15
- package/dist/deploy/index.js +56 -56
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +105 -10
- package/dist/memory/deepbrain.d.ts +1 -1
- package/dist/memory/deepbrain.js +95 -4
- package/dist/scheduler/cron-engine.js +3 -36
- package/dist/studio/server.js +30 -1
- package/dist/studio-ui/index.html +230 -10
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/fix-sidebar.mjs +188 -188
- package/install.ps1 +154 -154
- package/install.sh +164 -164
- package/package.json +1 -1
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/serve-studio.js +13 -13
- package/serve-test.js +25 -25
- package/src/channels/dingtalk.ts +46 -46
- package/src/channels/email.ts +351 -351
- package/src/channels/feishu.ts +349 -349
- package/src/channels/googlechat.ts +42 -42
- package/src/channels/imessage.ts +31 -31
- package/src/channels/irc.ts +82 -82
- package/src/channels/line.ts +32 -32
- package/src/channels/matrix.ts +33 -33
- package/src/channels/mattermost.ts +57 -57
- package/src/channels/msteams.ts +32 -32
- package/src/channels/nostr.ts +32 -32
- package/src/channels/qq.ts +33 -33
- package/src/channels/signal.ts +32 -32
- package/src/channels/sms.ts +33 -33
- package/src/channels/telegram.ts +616 -616
- package/src/channels/twitch.ts +65 -65
- package/src/channels/voice-call.ts +100 -100
- package/src/channels/web.ts +8 -2
- package/src/channels/websocket.ts +399 -399
- package/src/channels/wechat.ts +329 -329
- package/src/channels/whatsapp.ts +32 -32
- package/src/cli/chat.ts +99 -99
- package/src/cli/setup.ts +314 -314
- package/src/cli.ts +195 -92
- package/src/core/agent.ts +476 -476
- package/src/core/api-server.ts +277 -277
- package/src/core/audio.ts +98 -98
- package/src/core/collaboration.ts +275 -275
- package/src/core/context-discovery.ts +85 -85
- package/src/core/context-refs.ts +140 -140
- package/src/core/gateway.ts +106 -106
- package/src/core/heartbeat.ts +51 -51
- package/src/core/hooks.ts +105 -105
- package/src/core/ide-bridge.ts +133 -133
- package/src/core/node-network.ts +86 -86
- package/src/core/profiles.ts +122 -122
- package/src/core/runtime.ts +25 -0
- package/src/core/scheduler.ts +187 -187
- package/src/core/session-manager.ts +137 -137
- package/src/core/subagent.ts +98 -98
- package/src/core/vision.ts +180 -180
- package/src/core/workflow-graph.ts +365 -365
- package/src/daemon.ts +96 -96
- package/src/deploy/index.ts +255 -255
- package/src/doctor.ts +98 -11
- package/src/eval/index.ts +211 -211
- package/src/eval/suites/basic.json +16 -16
- package/src/eval/suites/memory.json +12 -12
- package/src/eval/suites/safety.json +14 -14
- package/src/hub/brain-seed.ts +54 -54
- package/src/hub/client.ts +60 -60
- package/src/mcp/servers/calculator-mcp.ts +65 -65
- package/src/mcp/servers/crypto-mcp.ts +73 -73
- package/src/mcp/servers/database-mcp.ts +72 -72
- package/src/mcp/servers/datetime-mcp.ts +69 -69
- package/src/mcp/servers/filesystem.ts +66 -66
- package/src/mcp/servers/github-mcp.ts +58 -58
- package/src/mcp/servers/index.ts +63 -63
- package/src/mcp/servers/json-mcp.ts +102 -102
- package/src/mcp/servers/memory-mcp.ts +56 -56
- package/src/mcp/servers/regex-mcp.ts +53 -53
- package/src/mcp/servers/web-mcp.ts +49 -49
- package/src/memory/context-compressor.ts +189 -189
- package/src/memory/deepbrain.ts +99 -5
- package/src/memory/seed-loader.ts +212 -212
- package/src/memory/user-profiler.ts +215 -215
- package/src/plugins/content-filter.ts +23 -23
- package/src/plugins/logger.ts +18 -18
- package/src/plugins/rate-limiter.ts +38 -38
- package/src/protocols/a2a/client.ts +132 -132
- package/src/protocols/a2a/index.ts +8 -8
- package/src/protocols/a2a/server.ts +333 -333
- package/src/protocols/a2a/types.ts +88 -88
- package/src/protocols/a2a/utils.ts +50 -50
- package/src/protocols/agui/client.ts +83 -83
- package/src/protocols/agui/index.ts +4 -4
- package/src/protocols/agui/server.ts +218 -218
- package/src/protocols/agui/types.ts +153 -153
- package/src/protocols/index.ts +2 -2
- package/src/protocols/mcp/agent-tools.ts +134 -134
- package/src/protocols/mcp/index.ts +8 -8
- package/src/protocols/mcp/server.ts +262 -262
- package/src/protocols/mcp/types.ts +69 -69
- package/src/providers/index.ts +632 -632
- package/src/publish/index.ts +376 -376
- package/src/scheduler/cron-engine.ts +191 -191
- package/src/scheduler/index.ts +2 -2
- package/src/schema/oad.ts +217 -217
- package/src/security/approval.ts +131 -131
- package/src/security/approvals.ts +143 -143
- package/src/security/elevated.ts +105 -105
- package/src/security/guardrails.ts +248 -248
- package/src/security/index.ts +9 -9
- package/src/security/keys.ts +87 -87
- package/src/security/secrets.ts +129 -129
- package/src/skills/builtin/index.ts +408 -408
- package/src/skills/marketplace.ts +113 -113
- package/src/skills/types.ts +42 -42
- package/src/studio/server.ts +31 -1
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +230 -10
- package/src/telemetry/index.ts +324 -324
- package/src/tools/builtin/browser.ts +299 -299
- package/src/tools/builtin/datetime.ts +41 -41
- package/src/tools/builtin/file.ts +107 -107
- package/src/tools/builtin/home-assistant.ts +116 -116
- package/src/tools/builtin/rl-tools.ts +243 -243
- package/src/tools/builtin/shell.ts +43 -43
- package/src/tools/builtin/vision.ts +64 -64
- package/src/tools/builtin/web-search.ts +126 -126
- package/src/tools/builtin/web.ts +35 -35
- package/src/tools/document-processor.ts +213 -213
- package/src/tools/image-generator.ts +150 -150
- package/src/tools/integrations/calendar.ts +73 -73
- package/src/tools/integrations/code-exec.ts +39 -39
- package/src/tools/integrations/csv-analyzer.ts +92 -92
- package/src/tools/integrations/database.ts +44 -44
- package/src/tools/integrations/email-send.ts +76 -76
- package/src/tools/integrations/git-tool.ts +42 -42
- package/src/tools/integrations/github-tool.ts +76 -76
- package/src/tools/integrations/image-gen.ts +56 -56
- package/src/tools/integrations/index.ts +92 -92
- package/src/tools/integrations/jira.ts +83 -83
- package/src/tools/integrations/notion.ts +71 -71
- package/src/tools/integrations/npm-tool.ts +48 -48
- package/src/tools/integrations/pdf-reader.ts +58 -58
- package/src/tools/integrations/slack.ts +65 -65
- package/src/tools/integrations/summarizer.ts +49 -49
- package/src/tools/integrations/translator.ts +48 -48
- package/src/tools/integrations/trello.ts +60 -60
- package/src/tools/integrations/vector-search.ts +42 -42
- package/src/tools/integrations/web-scraper.ts +47 -47
- package/src/tools/integrations/web-search.ts +58 -58
- package/src/tools/integrations/webhook.ts +38 -38
- package/src/tools/mcp-client.ts +131 -131
- package/src/tools/web-scraper.ts +179 -179
- package/src/tools/web-search.ts +180 -180
- package/src/ui/components.ts +127 -127
- package/srv-out.txt +1 -1
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -9
- package/test-agent/README.md +50 -50
- package/test-agent/agent.yaml +23 -23
- package/test-agent/docker-compose.yml +11 -11
- package/test-agent/oad.yaml +31 -31
- package/test-agent/package-lock.json +1492 -1492
- package/test-agent/package.json +17 -17
- package/test-agent/src/index.ts +24 -24
- package/test-agent/src/skills/echo.ts +15 -15
- package/test-agent/tsconfig.json +24 -24
- package/test-full.js +43 -43
- package/test-sidebar.js +22 -22
- package/test-studio3.js +75 -75
- package/test-studio4.js +41 -41
- package/tests/a2a-protocol.test.ts +285 -285
- package/tests/agui-protocol.test.ts +246 -246
- package/tests/api-server.test.ts +148 -148
- package/tests/approvals.test.ts +89 -89
- package/tests/audio.test.ts +40 -40
- package/tests/brain-seed-extended.test.ts +490 -490
- package/tests/brain-seed.test.ts +239 -239
- package/tests/browser.test.ts +179 -179
- package/tests/channels/discord.test.ts +79 -79
- package/tests/channels/email.test.ts +148 -148
- package/tests/channels/feishu.test.ts +123 -123
- package/tests/channels/telegram.test.ts +129 -129
- package/tests/channels/websocket.test.ts +53 -53
- package/tests/channels/wechat.test.ts +170 -170
- package/tests/channels-extra.test.ts +45 -45
- package/tests/chat-cli.test.ts +160 -160
- package/tests/cli.test.ts +46 -46
- package/tests/context-compressor.test.ts +172 -172
- package/tests/context-refs.test.ts +121 -121
- package/tests/cron-engine.test.ts +101 -101
- package/tests/daemon.test.ts +135 -135
- package/tests/deepbrain-wire.test.ts +234 -234
- package/tests/deploy-and-dag.test.ts +196 -196
- package/tests/doctor.test.ts +38 -38
- package/tests/document-processor.test.ts +69 -69
- package/tests/e2e-nocode.test.ts +442 -442
- package/tests/elevated.test.ts +69 -69
- package/tests/eval.test.ts +173 -173
- package/tests/gateway.test.ts +63 -63
- package/tests/guardrails.test.ts +177 -177
- package/tests/home-assistant.test.ts +40 -40
- package/tests/hooks.test.ts +79 -79
- package/tests/ide-bridge.test.ts +38 -38
- package/tests/image-generator.test.ts +84 -84
- package/tests/init-role.test.ts +124 -124
- package/tests/integrations.test.ts +249 -249
- package/tests/mcp-client.test.ts +92 -92
- package/tests/mcp-server.test.ts +178 -178
- package/tests/mcp-servers.test.ts +260 -260
- package/tests/node-network.test.ts +74 -74
- package/tests/plugin-a2a-enhanced.test.ts +230 -230
- package/tests/profiles.test.ts +61 -61
- package/tests/publish.test.ts +231 -231
- package/tests/rl-tools.test.ts +93 -93
- package/tests/sandbox-manager.test.ts +46 -46
- package/tests/scheduler.test.ts +200 -200
- package/tests/secrets.test.ts +107 -107
- package/tests/security-enhanced.test.ts +233 -233
- package/tests/settings-api.test.ts +148 -148
- package/tests/setup.test.ts +73 -73
- package/tests/subagent.test.ts +193 -193
- package/tests/telegram-discord.test.ts +60 -60
- package/tests/telemetry.test.ts +186 -186
- package/tests/user-profiler.test.ts +169 -169
- package/tests/v090-features.test.ts +254 -254
- package/tests/vision.test.ts +61 -61
- package/tests/voice-call.test.ts +47 -47
- package/tests/voice-enhanced.test.ts +169 -169
- package/tests/voice-interaction.test.ts +38 -38
- package/tests/web-search.test.ts +155 -155
- package/tests/workflow-graph.test.ts +279 -279
- package/tutorial/customer-service-agent/README.md +612 -612
- package/tutorial/customer-service-agent/SOUL.md +26 -26
- package/tutorial/customer-service-agent/agent.yaml +63 -63
- package/tutorial/customer-service-agent/package.json +19 -19
- package/tutorial/customer-service-agent/src/index.ts +69 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
- package/tutorial/customer-service-agent/tsconfig.json +14 -14
package/src/core/heartbeat.ts
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
export interface HeartbeatConfig {
|
|
2
|
-
interval: number;
|
|
3
|
-
checkFn: () => Promise<string>;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export class HeartbeatManager {
|
|
7
|
-
private config: HeartbeatConfig;
|
|
8
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
9
|
-
private callbacks: ((status: string) => void)[] = [];
|
|
10
|
-
private lastBeat: { timestamp: number; status: string } | null = null;
|
|
11
|
-
|
|
12
|
-
constructor(config: HeartbeatConfig) {
|
|
13
|
-
if (!config.interval || config.interval < 100) {
|
|
14
|
-
throw new Error('HeartbeatManager requires interval >= 100ms');
|
|
15
|
-
}
|
|
16
|
-
if (typeof config.checkFn !== 'function') {
|
|
17
|
-
throw new Error('HeartbeatManager requires checkFn');
|
|
18
|
-
}
|
|
19
|
-
this.config = config;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
start(): void {
|
|
23
|
-
if (this.timer) return;
|
|
24
|
-
this.timer = setInterval(async () => {
|
|
25
|
-
try {
|
|
26
|
-
const status = await this.config.checkFn();
|
|
27
|
-
this.lastBeat = { timestamp: Date.now(), status };
|
|
28
|
-
for (const cb of this.callbacks) cb(status);
|
|
29
|
-
} catch (err: any) {
|
|
30
|
-
const status = `error: ${err.message}`;
|
|
31
|
-
this.lastBeat = { timestamp: Date.now(), status };
|
|
32
|
-
for (const cb of this.callbacks) cb(status);
|
|
33
|
-
}
|
|
34
|
-
}, this.config.interval);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
stop(): void {
|
|
38
|
-
if (this.timer) {
|
|
39
|
-
clearInterval(this.timer);
|
|
40
|
-
this.timer = null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
onBeat(callback: (status: string) => void): void {
|
|
45
|
-
this.callbacks.push(callback);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getLastBeat(): { timestamp: number; status: string } | null {
|
|
49
|
-
return this.lastBeat;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
export interface HeartbeatConfig {
|
|
2
|
+
interval: number;
|
|
3
|
+
checkFn: () => Promise<string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class HeartbeatManager {
|
|
7
|
+
private config: HeartbeatConfig;
|
|
8
|
+
private timer: ReturnType<typeof setInterval> | null = null;
|
|
9
|
+
private callbacks: ((status: string) => void)[] = [];
|
|
10
|
+
private lastBeat: { timestamp: number; status: string } | null = null;
|
|
11
|
+
|
|
12
|
+
constructor(config: HeartbeatConfig) {
|
|
13
|
+
if (!config.interval || config.interval < 100) {
|
|
14
|
+
throw new Error('HeartbeatManager requires interval >= 100ms');
|
|
15
|
+
}
|
|
16
|
+
if (typeof config.checkFn !== 'function') {
|
|
17
|
+
throw new Error('HeartbeatManager requires checkFn');
|
|
18
|
+
}
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
start(): void {
|
|
23
|
+
if (this.timer) return;
|
|
24
|
+
this.timer = setInterval(async () => {
|
|
25
|
+
try {
|
|
26
|
+
const status = await this.config.checkFn();
|
|
27
|
+
this.lastBeat = { timestamp: Date.now(), status };
|
|
28
|
+
for (const cb of this.callbacks) cb(status);
|
|
29
|
+
} catch (err: any) {
|
|
30
|
+
const status = `error: ${err.message}`;
|
|
31
|
+
this.lastBeat = { timestamp: Date.now(), status };
|
|
32
|
+
for (const cb of this.callbacks) cb(status);
|
|
33
|
+
}
|
|
34
|
+
}, this.config.interval);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
stop(): void {
|
|
38
|
+
if (this.timer) {
|
|
39
|
+
clearInterval(this.timer);
|
|
40
|
+
this.timer = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onBeat(callback: (status: string) => void): void {
|
|
45
|
+
this.callbacks.push(callback);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getLastBeat(): { timestamp: number; status: string } | null {
|
|
49
|
+
return this.lastBeat;
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/core/hooks.ts
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hooks Module - v1.0.0
|
|
3
|
-
* Event hook system with priority ordering and context modification.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export type HookEvent =
|
|
7
|
-
| 'before:message' | 'after:message'
|
|
8
|
-
| 'before:tool' | 'after:tool'
|
|
9
|
-
| 'before:llm' | 'after:llm'
|
|
10
|
-
| 'before:send' | 'after:send'
|
|
11
|
-
| 'before:learn' | 'after:learn'
|
|
12
|
-
| 'before:recall' | 'after:recall'
|
|
13
|
-
| 'on:error' | 'on:start' | 'on:stop';
|
|
14
|
-
|
|
15
|
-
export const ALL_HOOK_EVENTS: HookEvent[] = [
|
|
16
|
-
'before:message', 'after:message',
|
|
17
|
-
'before:tool', 'after:tool',
|
|
18
|
-
'before:llm', 'after:llm',
|
|
19
|
-
'before:send', 'after:send',
|
|
20
|
-
'before:learn', 'after:learn',
|
|
21
|
-
'before:recall', 'after:recall',
|
|
22
|
-
'on:error', 'on:start', 'on:stop',
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
export interface HookContext {
|
|
26
|
-
[key: string]: unknown;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type HookHandler = (ctx: HookContext) => HookContext | void | Promise<HookContext | void>;
|
|
30
|
-
|
|
31
|
-
interface RegisteredHook {
|
|
32
|
-
id: string;
|
|
33
|
-
event: HookEvent;
|
|
34
|
-
handler: HookHandler;
|
|
35
|
-
priority: number;
|
|
36
|
-
name?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let hookIdCounter = 0;
|
|
40
|
-
|
|
41
|
-
export class HookManager {
|
|
42
|
-
private hooks: Map<HookEvent, RegisteredHook[]> = new Map();
|
|
43
|
-
|
|
44
|
-
register(event: HookEvent, handler: HookHandler, options?: { priority?: number; name?: string }): string {
|
|
45
|
-
const id = `hook_${++hookIdCounter}`;
|
|
46
|
-
const entry: RegisteredHook = {
|
|
47
|
-
id,
|
|
48
|
-
event,
|
|
49
|
-
handler,
|
|
50
|
-
priority: options?.priority ?? 100,
|
|
51
|
-
name: options?.name,
|
|
52
|
-
};
|
|
53
|
-
if (!this.hooks.has(event)) this.hooks.set(event, []);
|
|
54
|
-
const list = this.hooks.get(event)!;
|
|
55
|
-
list.push(entry);
|
|
56
|
-
list.sort((a, b) => a.priority - b.priority);
|
|
57
|
-
return id;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
unregister(id: string): boolean {
|
|
61
|
-
for (const [event, list] of this.hooks) {
|
|
62
|
-
const idx = list.findIndex(h => h.id === id);
|
|
63
|
-
if (idx !== -1) {
|
|
64
|
-
list.splice(idx, 1);
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async run(event: HookEvent, ctx: HookContext = {}): Promise<HookContext> {
|
|
72
|
-
const list = this.hooks.get(event);
|
|
73
|
-
if (!list || list.length === 0) return ctx;
|
|
74
|
-
let current = { ...ctx };
|
|
75
|
-
for (const hook of list) {
|
|
76
|
-
const result = await hook.handler(current);
|
|
77
|
-
if (result) current = { ...current, ...result };
|
|
78
|
-
}
|
|
79
|
-
return current;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getRegistered(event?: HookEvent): { id: string; event: HookEvent; priority: number; name?: string }[] {
|
|
83
|
-
const results: { id: string; event: HookEvent; priority: number; name?: string }[] = [];
|
|
84
|
-
const events = event ? [event] : ALL_HOOK_EVENTS;
|
|
85
|
-
for (const e of events) {
|
|
86
|
-
const list = this.hooks.get(e) ?? [];
|
|
87
|
-
for (const h of list) {
|
|
88
|
-
results.push({ id: h.id, event: h.event, priority: h.priority, name: h.name });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return results;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
clear(event?: HookEvent): void {
|
|
95
|
-
if (event) {
|
|
96
|
-
this.hooks.delete(event);
|
|
97
|
-
} else {
|
|
98
|
-
this.hooks.clear();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
hasHooks(event: HookEvent): boolean {
|
|
103
|
-
return (this.hooks.get(event)?.length ?? 0) > 0;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Hooks Module - v1.0.0
|
|
3
|
+
* Event hook system with priority ordering and context modification.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type HookEvent =
|
|
7
|
+
| 'before:message' | 'after:message'
|
|
8
|
+
| 'before:tool' | 'after:tool'
|
|
9
|
+
| 'before:llm' | 'after:llm'
|
|
10
|
+
| 'before:send' | 'after:send'
|
|
11
|
+
| 'before:learn' | 'after:learn'
|
|
12
|
+
| 'before:recall' | 'after:recall'
|
|
13
|
+
| 'on:error' | 'on:start' | 'on:stop';
|
|
14
|
+
|
|
15
|
+
export const ALL_HOOK_EVENTS: HookEvent[] = [
|
|
16
|
+
'before:message', 'after:message',
|
|
17
|
+
'before:tool', 'after:tool',
|
|
18
|
+
'before:llm', 'after:llm',
|
|
19
|
+
'before:send', 'after:send',
|
|
20
|
+
'before:learn', 'after:learn',
|
|
21
|
+
'before:recall', 'after:recall',
|
|
22
|
+
'on:error', 'on:start', 'on:stop',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export interface HookContext {
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type HookHandler = (ctx: HookContext) => HookContext | void | Promise<HookContext | void>;
|
|
30
|
+
|
|
31
|
+
interface RegisteredHook {
|
|
32
|
+
id: string;
|
|
33
|
+
event: HookEvent;
|
|
34
|
+
handler: HookHandler;
|
|
35
|
+
priority: number;
|
|
36
|
+
name?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let hookIdCounter = 0;
|
|
40
|
+
|
|
41
|
+
export class HookManager {
|
|
42
|
+
private hooks: Map<HookEvent, RegisteredHook[]> = new Map();
|
|
43
|
+
|
|
44
|
+
register(event: HookEvent, handler: HookHandler, options?: { priority?: number; name?: string }): string {
|
|
45
|
+
const id = `hook_${++hookIdCounter}`;
|
|
46
|
+
const entry: RegisteredHook = {
|
|
47
|
+
id,
|
|
48
|
+
event,
|
|
49
|
+
handler,
|
|
50
|
+
priority: options?.priority ?? 100,
|
|
51
|
+
name: options?.name,
|
|
52
|
+
};
|
|
53
|
+
if (!this.hooks.has(event)) this.hooks.set(event, []);
|
|
54
|
+
const list = this.hooks.get(event)!;
|
|
55
|
+
list.push(entry);
|
|
56
|
+
list.sort((a, b) => a.priority - b.priority);
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
unregister(id: string): boolean {
|
|
61
|
+
for (const [event, list] of this.hooks) {
|
|
62
|
+
const idx = list.findIndex(h => h.id === id);
|
|
63
|
+
if (idx !== -1) {
|
|
64
|
+
list.splice(idx, 1);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async run(event: HookEvent, ctx: HookContext = {}): Promise<HookContext> {
|
|
72
|
+
const list = this.hooks.get(event);
|
|
73
|
+
if (!list || list.length === 0) return ctx;
|
|
74
|
+
let current = { ...ctx };
|
|
75
|
+
for (const hook of list) {
|
|
76
|
+
const result = await hook.handler(current);
|
|
77
|
+
if (result) current = { ...current, ...result };
|
|
78
|
+
}
|
|
79
|
+
return current;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getRegistered(event?: HookEvent): { id: string; event: HookEvent; priority: number; name?: string }[] {
|
|
83
|
+
const results: { id: string; event: HookEvent; priority: number; name?: string }[] = [];
|
|
84
|
+
const events = event ? [event] : ALL_HOOK_EVENTS;
|
|
85
|
+
for (const e of events) {
|
|
86
|
+
const list = this.hooks.get(e) ?? [];
|
|
87
|
+
for (const h of list) {
|
|
88
|
+
results.push({ id: h.id, event: h.event, priority: h.priority, name: h.name });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clear(event?: HookEvent): void {
|
|
95
|
+
if (event) {
|
|
96
|
+
this.hooks.delete(event);
|
|
97
|
+
} else {
|
|
98
|
+
this.hooks.clear();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
hasHooks(event: HookEvent): boolean {
|
|
103
|
+
return (this.hooks.get(event)?.length ?? 0) > 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
package/src/core/ide-bridge.ts
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
|
|
3
|
-
export interface IDEConfig {
|
|
4
|
-
editor: 'vscode' | 'jetbrains' | 'zed' | 'cursor';
|
|
5
|
-
workspacePath?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface Diagnostic {
|
|
9
|
-
path: string;
|
|
10
|
-
line: number;
|
|
11
|
-
column: number;
|
|
12
|
-
severity: 'error' | 'warning' | 'info';
|
|
13
|
-
message: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface TextEdit {
|
|
17
|
-
range: Range;
|
|
18
|
-
newText: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Range {
|
|
22
|
-
startLine: number;
|
|
23
|
-
startColumn: number;
|
|
24
|
-
endLine: number;
|
|
25
|
-
endColumn: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface SearchOptions {
|
|
29
|
-
caseSensitive?: boolean;
|
|
30
|
-
regex?: boolean;
|
|
31
|
-
include?: string;
|
|
32
|
-
exclude?: string;
|
|
33
|
-
maxResults?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface SearchResult {
|
|
37
|
-
path: string;
|
|
38
|
-
line: number;
|
|
39
|
-
column: number;
|
|
40
|
-
text: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class IDEBridge {
|
|
44
|
-
private config: IDEConfig;
|
|
45
|
-
|
|
46
|
-
constructor(config: IDEConfig) {
|
|
47
|
-
this.config = config;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private getCliCommand(): string {
|
|
51
|
-
switch (this.config.editor) {
|
|
52
|
-
case 'vscode': case 'cursor': return this.config.editor === 'cursor' ? 'cursor' : 'code';
|
|
53
|
-
case 'jetbrains': return 'idea';
|
|
54
|
-
case 'zed': return 'zed';
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private exec(cmd: string): string {
|
|
59
|
-
try {
|
|
60
|
-
return execSync(cmd, { encoding: 'utf-8', timeout: 10000, cwd: this.config.workspacePath }).trim();
|
|
61
|
-
} catch (e: any) {
|
|
62
|
-
throw new Error(`IDE command failed: ${e.message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async openFile(path: string, line?: number): Promise<void> {
|
|
67
|
-
const cli = this.getCliCommand();
|
|
68
|
-
const target = line ? `${path}:${line}` : path;
|
|
69
|
-
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
70
|
-
this.exec(`${cli} --goto "${target}"`);
|
|
71
|
-
} else if (this.config.editor === 'zed') {
|
|
72
|
-
this.exec(`zed "${target}"`);
|
|
73
|
-
} else {
|
|
74
|
-
this.exec(`${cli} --line ${line || 1} "${path}"`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async getDiagnostics(path?: string): Promise<Diagnostic[]> {
|
|
79
|
-
// VS Code doesn't expose diagnostics via CLI; return empty as stub
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async runCommand(command: string): Promise<string> {
|
|
84
|
-
const cli = this.getCliCommand();
|
|
85
|
-
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
86
|
-
return this.exec(`${cli} --command "${command}"`);
|
|
87
|
-
}
|
|
88
|
-
throw new Error(`runCommand not supported for ${this.config.editor}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async getOpenFiles(): Promise<string[]> {
|
|
92
|
-
// Stub — no standard CLI to get open files
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async applyEdit(path: string, edits: TextEdit[]): Promise<void> {
|
|
97
|
-
// Stub — would use editor's API/extension
|
|
98
|
-
if (edits.length === 0) return;
|
|
99
|
-
throw new Error('applyEdit requires an IDE extension to be installed. Use file system edits as fallback.');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
|
|
103
|
-
const cli = this.getCliCommand();
|
|
104
|
-
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
105
|
-
try {
|
|
106
|
-
const args = [`--search "${query}"`];
|
|
107
|
-
if (options?.include) args.push(`--include "${options.include}"`);
|
|
108
|
-
const output = this.exec(`${cli} ${args.join(' ')}`);
|
|
109
|
-
// Parse output lines
|
|
110
|
-
return output.split('\n').filter(Boolean).map(line => {
|
|
111
|
-
const match = line.match(/^(.+):(\d+):(\d+):(.*)$/);
|
|
112
|
-
if (!match) return { path: '', line: 0, column: 0, text: line };
|
|
113
|
-
return { path: match[1], line: parseInt(match[2]), column: parseInt(match[3]), text: match[4] };
|
|
114
|
-
});
|
|
115
|
-
} catch { return []; }
|
|
116
|
-
}
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async getSelection(): Promise<{ path: string; text: string; range: Range } | null> {
|
|
121
|
-
// Stub — requires editor extension
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async installExtension(extensionId: string): Promise<void> {
|
|
126
|
-
const cli = this.getCliCommand();
|
|
127
|
-
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
128
|
-
this.exec(`${cli} --install-extension ${extensionId}`);
|
|
129
|
-
} else {
|
|
130
|
-
throw new Error(`installExtension not supported for ${this.config.editor}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
export interface IDEConfig {
|
|
4
|
+
editor: 'vscode' | 'jetbrains' | 'zed' | 'cursor';
|
|
5
|
+
workspacePath?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Diagnostic {
|
|
9
|
+
path: string;
|
|
10
|
+
line: number;
|
|
11
|
+
column: number;
|
|
12
|
+
severity: 'error' | 'warning' | 'info';
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TextEdit {
|
|
17
|
+
range: Range;
|
|
18
|
+
newText: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Range {
|
|
22
|
+
startLine: number;
|
|
23
|
+
startColumn: number;
|
|
24
|
+
endLine: number;
|
|
25
|
+
endColumn: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SearchOptions {
|
|
29
|
+
caseSensitive?: boolean;
|
|
30
|
+
regex?: boolean;
|
|
31
|
+
include?: string;
|
|
32
|
+
exclude?: string;
|
|
33
|
+
maxResults?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SearchResult {
|
|
37
|
+
path: string;
|
|
38
|
+
line: number;
|
|
39
|
+
column: number;
|
|
40
|
+
text: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class IDEBridge {
|
|
44
|
+
private config: IDEConfig;
|
|
45
|
+
|
|
46
|
+
constructor(config: IDEConfig) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private getCliCommand(): string {
|
|
51
|
+
switch (this.config.editor) {
|
|
52
|
+
case 'vscode': case 'cursor': return this.config.editor === 'cursor' ? 'cursor' : 'code';
|
|
53
|
+
case 'jetbrains': return 'idea';
|
|
54
|
+
case 'zed': return 'zed';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private exec(cmd: string): string {
|
|
59
|
+
try {
|
|
60
|
+
return execSync(cmd, { encoding: 'utf-8', timeout: 10000, cwd: this.config.workspacePath }).trim();
|
|
61
|
+
} catch (e: any) {
|
|
62
|
+
throw new Error(`IDE command failed: ${e.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async openFile(path: string, line?: number): Promise<void> {
|
|
67
|
+
const cli = this.getCliCommand();
|
|
68
|
+
const target = line ? `${path}:${line}` : path;
|
|
69
|
+
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
70
|
+
this.exec(`${cli} --goto "${target}"`);
|
|
71
|
+
} else if (this.config.editor === 'zed') {
|
|
72
|
+
this.exec(`zed "${target}"`);
|
|
73
|
+
} else {
|
|
74
|
+
this.exec(`${cli} --line ${line || 1} "${path}"`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getDiagnostics(path?: string): Promise<Diagnostic[]> {
|
|
79
|
+
// VS Code doesn't expose diagnostics via CLI; return empty as stub
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async runCommand(command: string): Promise<string> {
|
|
84
|
+
const cli = this.getCliCommand();
|
|
85
|
+
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
86
|
+
return this.exec(`${cli} --command "${command}"`);
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`runCommand not supported for ${this.config.editor}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getOpenFiles(): Promise<string[]> {
|
|
92
|
+
// Stub — no standard CLI to get open files
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async applyEdit(path: string, edits: TextEdit[]): Promise<void> {
|
|
97
|
+
// Stub — would use editor's API/extension
|
|
98
|
+
if (edits.length === 0) return;
|
|
99
|
+
throw new Error('applyEdit requires an IDE extension to be installed. Use file system edits as fallback.');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async search(query: string, options?: SearchOptions): Promise<SearchResult[]> {
|
|
103
|
+
const cli = this.getCliCommand();
|
|
104
|
+
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
105
|
+
try {
|
|
106
|
+
const args = [`--search "${query}"`];
|
|
107
|
+
if (options?.include) args.push(`--include "${options.include}"`);
|
|
108
|
+
const output = this.exec(`${cli} ${args.join(' ')}`);
|
|
109
|
+
// Parse output lines
|
|
110
|
+
return output.split('\n').filter(Boolean).map(line => {
|
|
111
|
+
const match = line.match(/^(.+):(\d+):(\d+):(.*)$/);
|
|
112
|
+
if (!match) return { path: '', line: 0, column: 0, text: line };
|
|
113
|
+
return { path: match[1], line: parseInt(match[2]), column: parseInt(match[3]), text: match[4] };
|
|
114
|
+
});
|
|
115
|
+
} catch { return []; }
|
|
116
|
+
}
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async getSelection(): Promise<{ path: string; text: string; range: Range } | null> {
|
|
121
|
+
// Stub — requires editor extension
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async installExtension(extensionId: string): Promise<void> {
|
|
126
|
+
const cli = this.getCliCommand();
|
|
127
|
+
if (this.config.editor === 'vscode' || this.config.editor === 'cursor') {
|
|
128
|
+
this.exec(`${cli} --install-extension ${extensionId}`);
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`installExtension not supported for ${this.config.editor}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|