opc-agent 4.0.44 → 4.1.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/.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/dist/channels/wechat.js +6 -6
- package/dist/cli.js +2 -2
- package/dist/core/runtime.js +18 -0
- package/dist/deploy/index.js +56 -56
- package/dist/providers/index.js +39 -13
- package/dist/studio/server.js +211 -20
- package/dist/studio-ui/index.html +279 -24
- 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/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 +2 -2
- 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 +18 -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 +156 -156
- 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/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 -608
- 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 +209 -22
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +279 -24
- 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/dist/channels/wechat.js
CHANGED
|
@@ -155,12 +155,12 @@ class WeChatChannel extends index_1.BaseChannel {
|
|
|
155
155
|
/** Format response as WeChat XML */
|
|
156
156
|
static formatXMLResponse(toUser, fromUser, content) {
|
|
157
157
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
158
|
-
return `<xml>
|
|
159
|
-
<ToUserName><![CDATA[${toUser}]]></ToUserName>
|
|
160
|
-
<FromUserName><![CDATA[${fromUser}]]></FromUserName>
|
|
161
|
-
<CreateTime>${timestamp}</CreateTime>
|
|
162
|
-
<MsgType><![CDATA[text]]></MsgType>
|
|
163
|
-
<Content><![CDATA[${content}]]></Content>
|
|
158
|
+
return `<xml>
|
|
159
|
+
<ToUserName><![CDATA[${toUser}]]></ToUserName>
|
|
160
|
+
<FromUserName><![CDATA[${fromUser}]]></FromUserName>
|
|
161
|
+
<CreateTime>${timestamp}</CreateTime>
|
|
162
|
+
<MsgType><![CDATA[text]]></MsgType>
|
|
163
|
+
<Content><![CDATA[${content}]]></Content>
|
|
164
164
|
</xml>`;
|
|
165
165
|
}
|
|
166
166
|
/** Handle incoming WeChat message */
|
package/dist/cli.js
CHANGED
|
@@ -680,7 +680,7 @@ program
|
|
|
680
680
|
let model;
|
|
681
681
|
let agentName = 'Agent';
|
|
682
682
|
let agentVersion = '1.0.0';
|
|
683
|
-
let providerName = '
|
|
683
|
+
let providerName = 'auto';
|
|
684
684
|
let skillNames = [];
|
|
685
685
|
// Try loading SOUL.md and CONTEXT.md for enriched system prompt
|
|
686
686
|
const soulPath = path.resolve('SOUL.md');
|
|
@@ -708,7 +708,7 @@ program
|
|
|
708
708
|
}
|
|
709
709
|
// Prepend SOUL.md and CONTEXT.md to system prompt
|
|
710
710
|
systemPrompt = [soulContent, contextContent, systemPrompt].filter(Boolean).join('\n\n');
|
|
711
|
-
const provider = (0, providers_1.createProvider)(
|
|
711
|
+
const provider = (0, providers_1.createProvider)(providerName, model);
|
|
712
712
|
const history = [];
|
|
713
713
|
// Print startup banner
|
|
714
714
|
const bannerLines = [
|
package/dist/core/runtime.js
CHANGED
|
@@ -73,6 +73,24 @@ class AgentRuntime {
|
|
|
73
73
|
agentBrain = null;
|
|
74
74
|
evolveScheduler = null;
|
|
75
75
|
async loadConfig(filePath) {
|
|
76
|
+
const fs = require('fs');
|
|
77
|
+
if (!fs.existsSync(filePath)) {
|
|
78
|
+
// Auto-create a minimal oad.yaml with auto-detect provider
|
|
79
|
+
const yaml = require('js-yaml');
|
|
80
|
+
const defaultOAD = {
|
|
81
|
+
apiVersion: 'opc/v1',
|
|
82
|
+
kind: 'Agent',
|
|
83
|
+
metadata: { name: 'my-agent', version: '1.0.0', description: 'OPC Agent' },
|
|
84
|
+
spec: {
|
|
85
|
+
model: 'auto',
|
|
86
|
+
provider: { default: 'auto' },
|
|
87
|
+
systemPrompt: 'You are a helpful AI assistant.',
|
|
88
|
+
channels: [{ type: 'web', config: { port: 3000 } }],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
fs.writeFileSync(filePath, yaml.dump(defaultOAD, { lineWidth: 120 }));
|
|
92
|
+
this.logger.info('Created default oad.yaml (no config file found)');
|
|
93
|
+
}
|
|
76
94
|
this.config = (0, config_1.loadOAD)(filePath);
|
|
77
95
|
this.logger.info('Config loaded', { name: this.config.metadata.name });
|
|
78
96
|
return this.config;
|
package/dist/deploy/index.js
CHANGED
|
@@ -60,25 +60,25 @@ class AgentDeployer {
|
|
|
60
60
|
}
|
|
61
61
|
catch { }
|
|
62
62
|
}
|
|
63
|
-
return `FROM node:22-slim
|
|
64
|
-
WORKDIR /app
|
|
65
|
-
|
|
66
|
-
# Install dependencies
|
|
67
|
-
COPY package*.json ./
|
|
68
|
-
RUN npm ci --production
|
|
69
|
-
|
|
70
|
-
# Copy source
|
|
71
|
-
COPY . .
|
|
72
|
-
|
|
73
|
-
# Build if needed
|
|
74
|
-
RUN if [ -f "tsconfig.json" ]; then npx tsc || true; fi
|
|
75
|
-
|
|
76
|
-
EXPOSE ${port}
|
|
77
|
-
|
|
78
|
-
ENV NODE_ENV=production
|
|
79
|
-
ENV PORT=${port}
|
|
80
|
-
|
|
81
|
-
CMD ["${startCmd.split(' ')[0]}", ${startCmd.split(' ').slice(1).map(s => `"${s}"`).join(', ')}]
|
|
63
|
+
return `FROM node:22-slim
|
|
64
|
+
WORKDIR /app
|
|
65
|
+
|
|
66
|
+
# Install dependencies
|
|
67
|
+
COPY package*.json ./
|
|
68
|
+
RUN npm ci --production
|
|
69
|
+
|
|
70
|
+
# Copy source
|
|
71
|
+
COPY . .
|
|
72
|
+
|
|
73
|
+
# Build if needed
|
|
74
|
+
RUN if [ -f "tsconfig.json" ]; then npx tsc || true; fi
|
|
75
|
+
|
|
76
|
+
EXPOSE ${port}
|
|
77
|
+
|
|
78
|
+
ENV NODE_ENV=production
|
|
79
|
+
ENV PORT=${port}
|
|
80
|
+
|
|
81
|
+
CMD ["${startCmd.split(' ')[0]}", ${startCmd.split(' ').slice(1).map(s => `"${s}"`).join(', ')}]
|
|
82
82
|
`;
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
@@ -91,23 +91,23 @@ CMD ["${startCmd.split(' ')[0]}", ${startCmd.split(' ').slice(1).map(s => `"${s}
|
|
|
91
91
|
const envLines = options?.env
|
|
92
92
|
? Object.entries(options.env).map(([k, v]) => ` - ${k}=${v}`).join('\n')
|
|
93
93
|
: '';
|
|
94
|
-
return `version: "3.8"
|
|
95
|
-
|
|
96
|
-
services:
|
|
97
|
-
${agentName}:
|
|
98
|
-
build: .
|
|
99
|
-
ports:
|
|
100
|
-
- "${port}:${port}"
|
|
101
|
-
environment:
|
|
102
|
-
- NODE_ENV=production
|
|
103
|
-
- PORT=${port}
|
|
104
|
-
${envLines ? envLines + '\n' : ''} restart: unless-stopped
|
|
105
|
-
${replicas > 1 ? ` deploy:\n replicas: ${replicas}` : ''}
|
|
106
|
-
volumes:
|
|
107
|
-
- agent-data:/app/data
|
|
108
|
-
|
|
109
|
-
volumes:
|
|
110
|
-
agent-data:
|
|
94
|
+
return `version: "3.8"
|
|
95
|
+
|
|
96
|
+
services:
|
|
97
|
+
${agentName}:
|
|
98
|
+
build: .
|
|
99
|
+
ports:
|
|
100
|
+
- "${port}:${port}"
|
|
101
|
+
environment:
|
|
102
|
+
- NODE_ENV=production
|
|
103
|
+
- PORT=${port}
|
|
104
|
+
${envLines ? envLines + '\n' : ''} restart: unless-stopped
|
|
105
|
+
${replicas > 1 ? ` deploy:\n replicas: ${replicas}` : ''}
|
|
106
|
+
volumes:
|
|
107
|
+
- agent-data:/app/data
|
|
108
|
+
|
|
109
|
+
volumes:
|
|
110
|
+
agent-data:
|
|
111
111
|
`;
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
@@ -155,21 +155,21 @@ volumes:
|
|
|
155
155
|
// Generate fly.toml if not exists
|
|
156
156
|
const flyTomlPath = path.join(agentDir, 'fly.toml');
|
|
157
157
|
if (!fs.existsSync(flyTomlPath)) {
|
|
158
|
-
fs.writeFileSync(flyTomlPath, `app = "${agentName}"
|
|
159
|
-
primary_region = "sjc"
|
|
160
|
-
|
|
161
|
-
[build]
|
|
162
|
-
|
|
163
|
-
[http_service]
|
|
164
|
-
internal_port = 3000
|
|
165
|
-
force_https = true
|
|
166
|
-
auto_stop_machines = true
|
|
167
|
-
auto_start_machines = true
|
|
168
|
-
|
|
169
|
-
[checks]
|
|
170
|
-
[checks.alive]
|
|
171
|
-
type = "tcp"
|
|
172
|
-
port = 3000
|
|
158
|
+
fs.writeFileSync(flyTomlPath, `app = "${agentName}"
|
|
159
|
+
primary_region = "sjc"
|
|
160
|
+
|
|
161
|
+
[build]
|
|
162
|
+
|
|
163
|
+
[http_service]
|
|
164
|
+
internal_port = 3000
|
|
165
|
+
force_https = true
|
|
166
|
+
auto_stop_machines = true
|
|
167
|
+
auto_start_machines = true
|
|
168
|
+
|
|
169
|
+
[checks]
|
|
170
|
+
[checks.alive]
|
|
171
|
+
type = "tcp"
|
|
172
|
+
port = 3000
|
|
173
173
|
`);
|
|
174
174
|
}
|
|
175
175
|
// Ensure Dockerfile exists
|
|
@@ -241,11 +241,11 @@ primary_region = "sjc"
|
|
|
241
241
|
fs.writeFileSync(path.join(agentDir, 'docker-compose.yml'), compose);
|
|
242
242
|
files.push('docker-compose.yml');
|
|
243
243
|
// Generate .dockerignore
|
|
244
|
-
const dockerignore = `node_modules
|
|
245
|
-
.git
|
|
246
|
-
.env
|
|
247
|
-
*.log
|
|
248
|
-
dist
|
|
244
|
+
const dockerignore = `node_modules
|
|
245
|
+
.git
|
|
246
|
+
.env
|
|
247
|
+
*.log
|
|
248
|
+
dist
|
|
249
249
|
`;
|
|
250
250
|
fs.writeFileSync(path.join(agentDir, '.dockerignore'), dockerignore);
|
|
251
251
|
files.push('.dockerignore');
|
package/dist/providers/index.js
CHANGED
|
@@ -328,7 +328,21 @@ class ClaudeCLIProvider {
|
|
|
328
328
|
name = 'claude-cli';
|
|
329
329
|
model;
|
|
330
330
|
constructor(model) {
|
|
331
|
-
|
|
331
|
+
// Claude CLI uses short model names; don't pass API-style model names
|
|
332
|
+
// Let Claude CLI use its default model unless explicitly set to a known CLI model
|
|
333
|
+
const cliModels = ['sonnet', 'opus', 'haiku', 'claude-sonnet-4-20250514', 'claude-opus-4-20250514'];
|
|
334
|
+
if (model && !cliModels.includes(model)) {
|
|
335
|
+
// Map common patterns
|
|
336
|
+
if (model.includes('opus'))
|
|
337
|
+
this.model = 'opus';
|
|
338
|
+
else if (model.includes('haiku'))
|
|
339
|
+
this.model = 'haiku';
|
|
340
|
+
else
|
|
341
|
+
this.model = ''; // let CLI choose default
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
this.model = model || '';
|
|
345
|
+
}
|
|
332
346
|
}
|
|
333
347
|
async chat(messages, systemPrompt, options) {
|
|
334
348
|
const { writeFileSync, unlinkSync, mkdtempSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
@@ -343,7 +357,7 @@ class ClaudeCLIProvider {
|
|
|
343
357
|
if (options?.tools && options.tools.length > 0) {
|
|
344
358
|
prompt += buildToolPrompt(options.tools);
|
|
345
359
|
}
|
|
346
|
-
const args = ['-p'
|
|
360
|
+
const args = ['-p'];
|
|
347
361
|
// Write system prompt to temp file to avoid shell escaping issues
|
|
348
362
|
let tmpFile;
|
|
349
363
|
if (systemPrompt) {
|
|
@@ -407,7 +421,7 @@ class ClaudeCLIProvider {
|
|
|
407
421
|
}
|
|
408
422
|
}
|
|
409
423
|
async *chatStream(messages, systemPrompt) {
|
|
410
|
-
const args = ['-p', '--
|
|
424
|
+
const args = ['-p', '--verbose', '--output-format', 'stream-json'];
|
|
411
425
|
if (this.model) {
|
|
412
426
|
args.push('--model', this.model);
|
|
413
427
|
}
|
|
@@ -442,14 +456,25 @@ class ClaudeCLIProvider {
|
|
|
442
456
|
continue;
|
|
443
457
|
try {
|
|
444
458
|
const event = JSON.parse(trimmed);
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
459
|
+
// Claude CLI stream-json format:
|
|
460
|
+
// {"type":"assistant","message":{"content":[{"type":"text","text":"..."}]}}
|
|
461
|
+
if (event.type === 'assistant' && event.message?.content) {
|
|
462
|
+
for (const block of event.message.content) {
|
|
463
|
+
if (block.type === 'text' && block.text) {
|
|
464
|
+
const newContent = block.text;
|
|
465
|
+
if (newContent.length > lastContent.length) {
|
|
466
|
+
yield newContent.slice(lastContent.length);
|
|
467
|
+
lastContent = newContent;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
451
470
|
}
|
|
452
471
|
}
|
|
472
|
+
// Also handle result type for final content
|
|
473
|
+
if (event.type === 'result' && event.result) {
|
|
474
|
+
const remaining = event.result.slice(lastContent.length);
|
|
475
|
+
if (remaining)
|
|
476
|
+
yield remaining;
|
|
477
|
+
}
|
|
453
478
|
// Handle assistant message with content array
|
|
454
479
|
if (event.type === 'assistant' && event.message?.content) {
|
|
455
480
|
for (const block of event.message.content) {
|
|
@@ -520,10 +545,11 @@ function detectClaudeCLI() {
|
|
|
520
545
|
}
|
|
521
546
|
function detectOllama() {
|
|
522
547
|
try {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
548
|
+
// Use node http instead of curl for Windows compatibility
|
|
549
|
+
const { execSync: es } = require('child_process');
|
|
550
|
+
// Quick check: try to connect to Ollama API via node
|
|
551
|
+
const result = es(`node -e "const h=require('http');const r=h.get('http://localhost:11434/api/tags',{timeout:2000},s=>{let d='';s.on('data',c=>d+=c);s.on('end',()=>{process.stdout.write(d.includes('models')?'1':'0')})});r.on('error',()=>process.stdout.write('0'));r.on('timeout',()=>{r.destroy();process.stdout.write('0')})"`, { stdio: 'pipe', timeout: 5000 });
|
|
552
|
+
return result.toString().trim() === '1';
|
|
527
553
|
}
|
|
528
554
|
catch {
|
|
529
555
|
return false;
|
package/dist/studio/server.js
CHANGED
|
@@ -166,12 +166,96 @@ class StudioServer {
|
|
|
166
166
|
const industry = url.searchParams.get('industry') || '';
|
|
167
167
|
const search = url.searchParams.get('q') || '';
|
|
168
168
|
data = this.getTemplates(industry, search);
|
|
169
|
+
// Merge with real workstation templates
|
|
170
|
+
try {
|
|
171
|
+
const ws = require('agent-workstation');
|
|
172
|
+
const categories = ws.getCategories();
|
|
173
|
+
const wsTemplates = [];
|
|
174
|
+
for (const cat of categories) {
|
|
175
|
+
for (const roleName of cat.roles) {
|
|
176
|
+
const role = ws.getRole(cat.name, roleName);
|
|
177
|
+
if (!role)
|
|
178
|
+
continue;
|
|
179
|
+
let oad = {};
|
|
180
|
+
try {
|
|
181
|
+
if (role.files?.['oad.yaml']) {
|
|
182
|
+
const yaml = require('js-yaml');
|
|
183
|
+
oad = yaml.load(role.files['oad.yaml']) || {};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch { }
|
|
187
|
+
const tpl = {
|
|
188
|
+
id: `ws-${cat.name}-${roleName}`,
|
|
189
|
+
name: oad.name || roleName.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
190
|
+
nameZh: oad.nameZh || '',
|
|
191
|
+
icon: oad.icon || '🤖',
|
|
192
|
+
description: oad.description || '',
|
|
193
|
+
descriptionZh: oad.descriptionZh || '',
|
|
194
|
+
industry: cat.name,
|
|
195
|
+
industryZh: cat.name,
|
|
196
|
+
tags: [cat.name, 'workstation'],
|
|
197
|
+
suggestedModel: 'auto',
|
|
198
|
+
systemPrompt: oad.systemPrompt || role.files?.['brain-seed.md'] || '',
|
|
199
|
+
source: 'workstation',
|
|
200
|
+
ego: oad.ego || null,
|
|
201
|
+
mission: oad.mission || null,
|
|
202
|
+
skills: oad.skills || [],
|
|
203
|
+
};
|
|
204
|
+
if (!search || tpl.name.toLowerCase().includes(search.toLowerCase()) || tpl.nameZh.includes(search)) {
|
|
205
|
+
if (!industry || tpl.industry === industry) {
|
|
206
|
+
wsTemplates.push(tpl);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
data.templates = [...data.templates, ...wsTemplates];
|
|
212
|
+
// Add workstation industries to list
|
|
213
|
+
const existingIds = new Set(data.industries.map((i) => i.id));
|
|
214
|
+
for (const cat of categories) {
|
|
215
|
+
if (!existingIds.has(cat.name)) {
|
|
216
|
+
data.industries.push({ id: cat.name, name: cat.name, nameZh: cat.name });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (wsErr) {
|
|
221
|
+
// workstation not available, use built-in templates only
|
|
222
|
+
}
|
|
169
223
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
170
224
|
res.end(JSON.stringify(data));
|
|
171
225
|
return;
|
|
172
226
|
}
|
|
173
227
|
if (route.match(/^templates\/[^/]+$/) && req.method === 'GET') {
|
|
174
228
|
const tplId = route.split('/')[1];
|
|
229
|
+
// Check workstation first
|
|
230
|
+
if (tplId.startsWith('ws-')) {
|
|
231
|
+
const parts = tplId.replace('ws-', '').split('-');
|
|
232
|
+
const catName = parts[0];
|
|
233
|
+
const roleName = parts.slice(1).join('-');
|
|
234
|
+
try {
|
|
235
|
+
const ws = require('agent-workstation');
|
|
236
|
+
const role = ws.getRole(catName, roleName);
|
|
237
|
+
if (role) {
|
|
238
|
+
let oad = {};
|
|
239
|
+
try {
|
|
240
|
+
if (role.files?.['oad.yaml']) {
|
|
241
|
+
const yaml = require('js-yaml');
|
|
242
|
+
oad = yaml.load(role.files['oad.yaml']) || {};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch { }
|
|
246
|
+
data = {
|
|
247
|
+
id: tplId, name: oad.name || roleName, source: 'workstation',
|
|
248
|
+
category: catName, role: roleName, files: role.files,
|
|
249
|
+
ego: oad.ego, mission: oad.mission, skills: oad.skills,
|
|
250
|
+
systemPrompt: oad.systemPrompt || role.files?.['brain-seed.md'] || '',
|
|
251
|
+
};
|
|
252
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
253
|
+
res.end(JSON.stringify(data));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch { }
|
|
258
|
+
}
|
|
175
259
|
data = this.getTemplateById(tplId);
|
|
176
260
|
res.writeHead(data.error ? 404 : 200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
177
261
|
res.end(JSON.stringify(data));
|
|
@@ -473,6 +557,64 @@ class StudioServer {
|
|
|
473
557
|
res.end(JSON.stringify(data));
|
|
474
558
|
return;
|
|
475
559
|
}
|
|
560
|
+
// === Global config API (reads/writes ~/.opc/config.json) ===
|
|
561
|
+
if (route === 'config' && req.method === 'GET') {
|
|
562
|
+
data = loadSettingsConfig();
|
|
563
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
564
|
+
res.end(JSON.stringify(data));
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (route === 'config' && req.method === 'PUT') {
|
|
568
|
+
const body = JSON.parse(await this.readBody(req));
|
|
569
|
+
const cfg = loadSettingsConfig();
|
|
570
|
+
Object.assign(cfg, body);
|
|
571
|
+
saveSettingsConfig(cfg);
|
|
572
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
573
|
+
res.end(JSON.stringify({ success: true, config: cfg }));
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
// === Models API (real agentkits integration) ===
|
|
577
|
+
if (route === 'models' && req.method === 'GET') {
|
|
578
|
+
try {
|
|
579
|
+
const ak = await Promise.resolve().then(() => __importStar(require('agentkits')));
|
|
580
|
+
const providers = ak.listLLMProviders();
|
|
581
|
+
data = { providers };
|
|
582
|
+
}
|
|
583
|
+
catch (e) {
|
|
584
|
+
data = { providers: [], error: 'agentkits not available: ' + e.message };
|
|
585
|
+
}
|
|
586
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
587
|
+
res.end(JSON.stringify(data));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
// === Memory stats API (real deepbrain integration) ===
|
|
591
|
+
if (route === 'memory/stats' && req.method === 'GET') {
|
|
592
|
+
try {
|
|
593
|
+
const { Brain } = require('deepbrain');
|
|
594
|
+
const oad = this.loadOAD();
|
|
595
|
+
const dbPath = oad?.spec?.memory?.longTerm?.database || './data/brain.db';
|
|
596
|
+
const brain = new Brain({ database: dbPath, embedding_provider: 'ollama' });
|
|
597
|
+
await brain.connect();
|
|
598
|
+
const stats = await brain.stats();
|
|
599
|
+
await brain.disconnect();
|
|
600
|
+
data = { connected: true, ...stats };
|
|
601
|
+
}
|
|
602
|
+
catch {
|
|
603
|
+
data = { connected: false, pages: 0, chunks: 0, error: 'DeepBrain not installed or not configured. Install with: npm i deepbrain' };
|
|
604
|
+
}
|
|
605
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
606
|
+
res.end(JSON.stringify(data));
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (route.match(/^memory\/[^/]+$/) && req.method === 'GET') {
|
|
610
|
+
const agentId = route.split('/')[1];
|
|
611
|
+
if (agentId !== 'stats' && agentId !== 'list' && agentId !== 'search') {
|
|
612
|
+
data = this.getAgentMemory(agentId);
|
|
613
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
614
|
+
res.end(JSON.stringify(data));
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
476
618
|
switch (route) {
|
|
477
619
|
case 'modules':
|
|
478
620
|
data = await this.getModulesStatus();
|
|
@@ -643,6 +785,7 @@ class StudioServer {
|
|
|
643
785
|
return agent;
|
|
644
786
|
}
|
|
645
787
|
listAgents() {
|
|
788
|
+
// 1. Load Studio-created agents from ~/.opc/agents/*.json
|
|
646
789
|
const dir = this.getAgentsDir();
|
|
647
790
|
const files = (0, fs_1.readdirSync)(dir).filter(f => f.endsWith('.json'));
|
|
648
791
|
const agents = files.map(f => {
|
|
@@ -652,7 +795,35 @@ class StudioServer {
|
|
|
652
795
|
catch {
|
|
653
796
|
return null;
|
|
654
797
|
}
|
|
655
|
-
}).filter(Boolean)
|
|
798
|
+
}).filter(Boolean);
|
|
799
|
+
// 2. Also detect current working directory agent (oad.yaml)
|
|
800
|
+
const seenIds = new Set(agents.map((a) => a.id));
|
|
801
|
+
const oadPath = (0, path_1.join)(this.config.agentDir, 'oad.yaml');
|
|
802
|
+
if ((0, fs_1.existsSync)(oadPath)) {
|
|
803
|
+
try {
|
|
804
|
+
const oadRaw = (0, fs_1.readFileSync)(oadPath, 'utf-8');
|
|
805
|
+
const yamlMod = require('js-yaml');
|
|
806
|
+
const oad = yamlMod.load(oadRaw);
|
|
807
|
+
const name = oad?.name || oad?.metadata?.name || 'My Agent';
|
|
808
|
+
const id = oad?.id || name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
809
|
+
if (!seenIds.has(id)) {
|
|
810
|
+
agents.push({
|
|
811
|
+
id,
|
|
812
|
+
name,
|
|
813
|
+
description: oad?.description || oad?.spec?.description || '',
|
|
814
|
+
icon: oad?.icon || '🤖',
|
|
815
|
+
emoji: oad?.icon || '🤖',
|
|
816
|
+
status: 'running',
|
|
817
|
+
source: 'oad.yaml',
|
|
818
|
+
model: oad?.spec?.model || oad?.spec?.provider?.model || 'auto',
|
|
819
|
+
created: new Date().toISOString(),
|
|
820
|
+
updated: new Date().toISOString(),
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
catch { /* ignore parse errors */ }
|
|
825
|
+
}
|
|
826
|
+
agents.sort((a, b) => new Date(b.updated).getTime() - new Date(a.updated).getTime());
|
|
656
827
|
return { agents };
|
|
657
828
|
}
|
|
658
829
|
getAgentById(id) {
|
|
@@ -710,8 +881,21 @@ class StudioServer {
|
|
|
710
881
|
return { entries, timeline: entries.map((e) => ({ date: e.timestamp, summary: e.summary || e.content?.slice(0, 100) })) };
|
|
711
882
|
}
|
|
712
883
|
async handleAgentChat(req, res, agentId) {
|
|
713
|
-
|
|
714
|
-
|
|
884
|
+
let body;
|
|
885
|
+
try {
|
|
886
|
+
body = JSON.parse(await this.readBody(req));
|
|
887
|
+
}
|
|
888
|
+
catch {
|
|
889
|
+
res.writeHead(400, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
890
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
// Accept both { messages: [...] } and { message: "...", history: [...] }
|
|
894
|
+
let messages = body.messages || [];
|
|
895
|
+
if (body.message) {
|
|
896
|
+
// Frontend sends { message, history }
|
|
897
|
+
messages = [...(body.history || []), { role: 'user', content: body.message }];
|
|
898
|
+
}
|
|
715
899
|
const agent = this.getAgentById(agentId);
|
|
716
900
|
if (agent.error) {
|
|
717
901
|
res.writeHead(404, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
@@ -722,8 +906,8 @@ class StudioServer {
|
|
|
722
906
|
agent.messageCount = (agent.messageCount || 0) + 1;
|
|
723
907
|
agent.lastActive = new Date().toISOString();
|
|
724
908
|
agent.updated = new Date().toISOString();
|
|
725
|
-
const
|
|
726
|
-
(0, fs_1.writeFileSync)(
|
|
909
|
+
const agentFilePath = (0, path_1.join)(this.getAgentsDir(), `${agentId}.json`);
|
|
910
|
+
(0, fs_1.writeFileSync)(agentFilePath, JSON.stringify(agent, null, 2));
|
|
727
911
|
// SSE streaming response
|
|
728
912
|
res.writeHead(200, {
|
|
729
913
|
'Content-Type': 'text/event-stream',
|
|
@@ -731,30 +915,31 @@ class StudioServer {
|
|
|
731
915
|
'Connection': 'keep-alive',
|
|
732
916
|
'Access-Control-Allow-Origin': '*',
|
|
733
917
|
});
|
|
734
|
-
const allMsgs = [{ role: 'system', content: agent.systemPrompt }, ...messages];
|
|
735
|
-
const lastMsg = allMsgs[allMsgs.length - 1]?.content || '';
|
|
736
918
|
// Use createProvider directly to call LLM
|
|
737
919
|
try {
|
|
738
920
|
const { createProvider } = require('../providers');
|
|
739
|
-
//
|
|
921
|
+
// Determine provider: agent config > OAD yaml > env > auto
|
|
740
922
|
let providerName = agent.provider || process.env.OPC_LLM_PROVIDER;
|
|
741
923
|
if (!providerName) {
|
|
742
|
-
// Try reading from oad.yaml
|
|
743
924
|
try {
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
925
|
+
for (const fname of ['oad.yaml', 'agent.yaml']) {
|
|
926
|
+
const oadPath = (0, path_1.join)(this.config.agentDir, fname);
|
|
927
|
+
if ((0, fs_1.existsSync)(oadPath)) {
|
|
928
|
+
const yaml = require('js-yaml');
|
|
929
|
+
const oad = yaml.load((0, fs_1.readFileSync)(oadPath, 'utf-8'));
|
|
930
|
+
providerName = oad?.spec?.provider?.default;
|
|
931
|
+
if (providerName)
|
|
932
|
+
break;
|
|
933
|
+
}
|
|
749
934
|
}
|
|
750
935
|
}
|
|
751
936
|
catch { }
|
|
752
937
|
}
|
|
753
|
-
providerName = providerName || '
|
|
938
|
+
providerName = providerName || 'auto';
|
|
754
939
|
const provider = createProvider(providerName, agent.model);
|
|
755
940
|
let fullText = '';
|
|
756
941
|
try {
|
|
757
|
-
for await (const chunk of provider.chatStream(
|
|
942
|
+
for await (const chunk of provider.chatStream(messages, agent.systemPrompt)) {
|
|
758
943
|
const sseData = JSON.stringify({
|
|
759
944
|
choices: [{ delta: { content: chunk }, index: 0 }],
|
|
760
945
|
});
|
|
@@ -764,8 +949,9 @@ class StudioServer {
|
|
|
764
949
|
}
|
|
765
950
|
catch (streamErr) {
|
|
766
951
|
if (!fullText) {
|
|
767
|
-
|
|
768
|
-
|
|
952
|
+
const errData = JSON.stringify({
|
|
953
|
+
choices: [{ delta: { content: `⚠️ LLM Error: ${streamErr.message}` }, index: 0 }],
|
|
954
|
+
});
|
|
769
955
|
res.write(`data: ${errData}\n\n`);
|
|
770
956
|
}
|
|
771
957
|
}
|
|
@@ -773,8 +959,13 @@ class StudioServer {
|
|
|
773
959
|
res.end();
|
|
774
960
|
}
|
|
775
961
|
catch (err) {
|
|
776
|
-
//
|
|
777
|
-
|
|
962
|
+
// Provider creation failed — send error as SSE so frontend can display it
|
|
963
|
+
const errData = JSON.stringify({
|
|
964
|
+
choices: [{ delta: { content: `⚠️ Provider error: ${err.message}\n\nTip: Install Claude CLI (npm i -g @anthropic-ai/claude-code) or set OPENAI_API_KEY.` }, index: 0 }],
|
|
965
|
+
});
|
|
966
|
+
res.write(`data: ${errData}\n\n`);
|
|
967
|
+
res.write('data: [DONE]\n\n');
|
|
968
|
+
res.end();
|
|
778
969
|
}
|
|
779
970
|
}
|
|
780
971
|
sendSimulatedResponse(res, lastMsg, agent) {
|