opc-agent 4.1.0 → 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/deploy/index.js +56 -56
- 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/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/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/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 -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/deploy/index.ts
CHANGED
|
@@ -1,255 +1,255 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentDeployer - Deploy agents to Docker, Railway, Fly.io
|
|
3
|
-
*/
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import { execSync } from 'child_process';
|
|
7
|
-
|
|
8
|
-
export interface DeployOptions {
|
|
9
|
-
port?: number;
|
|
10
|
-
env?: Record<string, string>;
|
|
11
|
-
platform?: 'docker' | 'railway' | 'fly' | 'render';
|
|
12
|
-
replicas?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface DeployResult {
|
|
16
|
-
platform: string;
|
|
17
|
-
success: boolean;
|
|
18
|
-
url?: string;
|
|
19
|
-
message: string;
|
|
20
|
-
files?: string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class AgentDeployer {
|
|
24
|
-
/**
|
|
25
|
-
* Generate Dockerfile for the agent
|
|
26
|
-
*/
|
|
27
|
-
async generateDockerfile(agentDir: string, options?: DeployOptions): Promise<string> {
|
|
28
|
-
const port = options?.port || 3000;
|
|
29
|
-
const pkgPath = path.join(agentDir, 'package.json');
|
|
30
|
-
let startCmd = 'node src/index.js';
|
|
31
|
-
|
|
32
|
-
if (fs.existsSync(pkgPath)) {
|
|
33
|
-
try {
|
|
34
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
35
|
-
if (pkg.scripts?.start) {
|
|
36
|
-
startCmd = pkg.scripts.start;
|
|
37
|
-
}
|
|
38
|
-
if (pkg.main) {
|
|
39
|
-
startCmd = `node ${pkg.main}`;
|
|
40
|
-
}
|
|
41
|
-
} catch {}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return `FROM node:22-slim
|
|
45
|
-
WORKDIR /app
|
|
46
|
-
|
|
47
|
-
# Install dependencies
|
|
48
|
-
COPY package*.json ./
|
|
49
|
-
RUN npm ci --production
|
|
50
|
-
|
|
51
|
-
# Copy source
|
|
52
|
-
COPY . .
|
|
53
|
-
|
|
54
|
-
# Build if needed
|
|
55
|
-
RUN if [ -f "tsconfig.json" ]; then npx tsc || true; fi
|
|
56
|
-
|
|
57
|
-
EXPOSE ${port}
|
|
58
|
-
|
|
59
|
-
ENV NODE_ENV=production
|
|
60
|
-
ENV PORT=${port}
|
|
61
|
-
|
|
62
|
-
CMD ["${startCmd.split(' ')[0]}", ${startCmd.split(' ').slice(1).map(s => `"${s}"`).join(', ')}]
|
|
63
|
-
`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Generate docker-compose.yml
|
|
68
|
-
*/
|
|
69
|
-
async generateCompose(agentDir: string, options?: DeployOptions): Promise<string> {
|
|
70
|
-
const port = options?.port || 3000;
|
|
71
|
-
const replicas = options?.replicas || 1;
|
|
72
|
-
const agentName = path.basename(agentDir).toLowerCase().replace(/[^a-z0-9-]/g, '-') || 'opc-agent';
|
|
73
|
-
|
|
74
|
-
const envLines = options?.env
|
|
75
|
-
? Object.entries(options.env).map(([k, v]) => ` - ${k}=${v}`).join('\n')
|
|
76
|
-
: '';
|
|
77
|
-
|
|
78
|
-
return `version: "3.8"
|
|
79
|
-
|
|
80
|
-
services:
|
|
81
|
-
${agentName}:
|
|
82
|
-
build: .
|
|
83
|
-
ports:
|
|
84
|
-
- "${port}:${port}"
|
|
85
|
-
environment:
|
|
86
|
-
- NODE_ENV=production
|
|
87
|
-
- PORT=${port}
|
|
88
|
-
${envLines ? envLines + '\n' : ''} restart: unless-stopped
|
|
89
|
-
${replicas > 1 ? ` deploy:\n replicas: ${replicas}` : ''}
|
|
90
|
-
volumes:
|
|
91
|
-
- agent-data:/app/data
|
|
92
|
-
|
|
93
|
-
volumes:
|
|
94
|
-
agent-data:
|
|
95
|
-
`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Deploy to Railway (via CLI)
|
|
100
|
-
*/
|
|
101
|
-
async deployRailway(agentDir: string): Promise<DeployResult> {
|
|
102
|
-
try {
|
|
103
|
-
execSync('railway version', { stdio: 'pipe' });
|
|
104
|
-
} catch {
|
|
105
|
-
return { platform: 'railway', success: false, message: 'Railway CLI not installed. Run: npm i -g @railway/cli' };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
// Ensure Dockerfile exists
|
|
110
|
-
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
111
|
-
if (!fs.existsSync(dockerfilePath)) {
|
|
112
|
-
const content = await this.generateDockerfile(agentDir);
|
|
113
|
-
fs.writeFileSync(dockerfilePath, content);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const output = execSync('railway up --detach', { cwd: agentDir, encoding: 'utf-8' });
|
|
117
|
-
const urlMatch = output.match(/(https:\/\/[^\s]+)/);
|
|
118
|
-
return {
|
|
119
|
-
platform: 'railway',
|
|
120
|
-
success: true,
|
|
121
|
-
url: urlMatch?.[1],
|
|
122
|
-
message: 'Deployed to Railway successfully'
|
|
123
|
-
};
|
|
124
|
-
} catch (e: any) {
|
|
125
|
-
return { platform: 'railway', success: false, message: e.message };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Deploy to Fly.io
|
|
131
|
-
*/
|
|
132
|
-
async deployFly(agentDir: string): Promise<DeployResult> {
|
|
133
|
-
try {
|
|
134
|
-
execSync('fly version', { stdio: 'pipe' });
|
|
135
|
-
} catch {
|
|
136
|
-
return { platform: 'fly', success: false, message: 'Fly CLI not installed. Run: curl -L https://fly.io/install.sh | sh' };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const agentName = path.basename(agentDir).toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
141
|
-
|
|
142
|
-
// Generate fly.toml if not exists
|
|
143
|
-
const flyTomlPath = path.join(agentDir, 'fly.toml');
|
|
144
|
-
if (!fs.existsSync(flyTomlPath)) {
|
|
145
|
-
fs.writeFileSync(flyTomlPath, `app = "${agentName}"
|
|
146
|
-
primary_region = "sjc"
|
|
147
|
-
|
|
148
|
-
[build]
|
|
149
|
-
|
|
150
|
-
[http_service]
|
|
151
|
-
internal_port = 3000
|
|
152
|
-
force_https = true
|
|
153
|
-
auto_stop_machines = true
|
|
154
|
-
auto_start_machines = true
|
|
155
|
-
|
|
156
|
-
[checks]
|
|
157
|
-
[checks.alive]
|
|
158
|
-
type = "tcp"
|
|
159
|
-
port = 3000
|
|
160
|
-
`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Ensure Dockerfile exists
|
|
164
|
-
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
165
|
-
if (!fs.existsSync(dockerfilePath)) {
|
|
166
|
-
const content = await this.generateDockerfile(agentDir);
|
|
167
|
-
fs.writeFileSync(dockerfilePath, content);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const output = execSync('fly deploy', { cwd: agentDir, encoding: 'utf-8' });
|
|
171
|
-
return {
|
|
172
|
-
platform: 'fly',
|
|
173
|
-
success: true,
|
|
174
|
-
url: `https://${agentName}.fly.dev`,
|
|
175
|
-
message: 'Deployed to Fly.io successfully'
|
|
176
|
-
};
|
|
177
|
-
} catch (e: any) {
|
|
178
|
-
return { platform: 'fly', success: false, message: e.message };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Deploy locally via Docker
|
|
184
|
-
*/
|
|
185
|
-
async deployLocal(agentDir: string, options?: DeployOptions): Promise<DeployResult> {
|
|
186
|
-
try {
|
|
187
|
-
execSync('docker version', { stdio: 'pipe' });
|
|
188
|
-
} catch {
|
|
189
|
-
return { platform: 'docker', success: false, message: 'Docker not installed or not running' };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const files: string[] = [];
|
|
193
|
-
|
|
194
|
-
// Generate Dockerfile
|
|
195
|
-
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
196
|
-
if (!fs.existsSync(dockerfilePath)) {
|
|
197
|
-
fs.writeFileSync(dockerfilePath, await this.generateDockerfile(agentDir, options));
|
|
198
|
-
files.push('Dockerfile');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Generate docker-compose.yml
|
|
202
|
-
const composePath = path.join(agentDir, 'docker-compose.yml');
|
|
203
|
-
if (!fs.existsSync(composePath)) {
|
|
204
|
-
fs.writeFileSync(composePath, await this.generateCompose(agentDir, options));
|
|
205
|
-
files.push('docker-compose.yml');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
execSync('docker compose up -d --build', { cwd: agentDir, stdio: 'inherit' });
|
|
210
|
-
const port = options?.port || 3000;
|
|
211
|
-
return {
|
|
212
|
-
platform: 'docker',
|
|
213
|
-
success: true,
|
|
214
|
-
url: `http://localhost:${port}`,
|
|
215
|
-
message: 'Running locally via Docker',
|
|
216
|
-
files
|
|
217
|
-
};
|
|
218
|
-
} catch (e: any) {
|
|
219
|
-
return { platform: 'docker', success: false, message: e.message, files };
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Generate deployment files without deploying
|
|
225
|
-
*/
|
|
226
|
-
async generateFiles(agentDir: string, options?: DeployOptions): Promise<DeployResult> {
|
|
227
|
-
const files: string[] = [];
|
|
228
|
-
const port = options?.port || 3000;
|
|
229
|
-
|
|
230
|
-
const dockerfile = await this.generateDockerfile(agentDir, options);
|
|
231
|
-
fs.writeFileSync(path.join(agentDir, 'Dockerfile'), dockerfile);
|
|
232
|
-
files.push('Dockerfile');
|
|
233
|
-
|
|
234
|
-
const compose = await this.generateCompose(agentDir, options);
|
|
235
|
-
fs.writeFileSync(path.join(agentDir, 'docker-compose.yml'), compose);
|
|
236
|
-
files.push('docker-compose.yml');
|
|
237
|
-
|
|
238
|
-
// Generate .dockerignore
|
|
239
|
-
const dockerignore = `node_modules
|
|
240
|
-
.git
|
|
241
|
-
.env
|
|
242
|
-
*.log
|
|
243
|
-
dist
|
|
244
|
-
`;
|
|
245
|
-
fs.writeFileSync(path.join(agentDir, '.dockerignore'), dockerignore);
|
|
246
|
-
files.push('.dockerignore');
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
platform: 'docker',
|
|
250
|
-
success: true,
|
|
251
|
-
message: `Generated ${files.length} deployment files`,
|
|
252
|
-
files,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentDeployer - Deploy agents to Docker, Railway, Fly.io
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
|
|
8
|
+
export interface DeployOptions {
|
|
9
|
+
port?: number;
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
platform?: 'docker' | 'railway' | 'fly' | 'render';
|
|
12
|
+
replicas?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DeployResult {
|
|
16
|
+
platform: string;
|
|
17
|
+
success: boolean;
|
|
18
|
+
url?: string;
|
|
19
|
+
message: string;
|
|
20
|
+
files?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AgentDeployer {
|
|
24
|
+
/**
|
|
25
|
+
* Generate Dockerfile for the agent
|
|
26
|
+
*/
|
|
27
|
+
async generateDockerfile(agentDir: string, options?: DeployOptions): Promise<string> {
|
|
28
|
+
const port = options?.port || 3000;
|
|
29
|
+
const pkgPath = path.join(agentDir, 'package.json');
|
|
30
|
+
let startCmd = 'node src/index.js';
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(pkgPath)) {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
35
|
+
if (pkg.scripts?.start) {
|
|
36
|
+
startCmd = pkg.scripts.start;
|
|
37
|
+
}
|
|
38
|
+
if (pkg.main) {
|
|
39
|
+
startCmd = `node ${pkg.main}`;
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `FROM node:22-slim
|
|
45
|
+
WORKDIR /app
|
|
46
|
+
|
|
47
|
+
# Install dependencies
|
|
48
|
+
COPY package*.json ./
|
|
49
|
+
RUN npm ci --production
|
|
50
|
+
|
|
51
|
+
# Copy source
|
|
52
|
+
COPY . .
|
|
53
|
+
|
|
54
|
+
# Build if needed
|
|
55
|
+
RUN if [ -f "tsconfig.json" ]; then npx tsc || true; fi
|
|
56
|
+
|
|
57
|
+
EXPOSE ${port}
|
|
58
|
+
|
|
59
|
+
ENV NODE_ENV=production
|
|
60
|
+
ENV PORT=${port}
|
|
61
|
+
|
|
62
|
+
CMD ["${startCmd.split(' ')[0]}", ${startCmd.split(' ').slice(1).map(s => `"${s}"`).join(', ')}]
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate docker-compose.yml
|
|
68
|
+
*/
|
|
69
|
+
async generateCompose(agentDir: string, options?: DeployOptions): Promise<string> {
|
|
70
|
+
const port = options?.port || 3000;
|
|
71
|
+
const replicas = options?.replicas || 1;
|
|
72
|
+
const agentName = path.basename(agentDir).toLowerCase().replace(/[^a-z0-9-]/g, '-') || 'opc-agent';
|
|
73
|
+
|
|
74
|
+
const envLines = options?.env
|
|
75
|
+
? Object.entries(options.env).map(([k, v]) => ` - ${k}=${v}`).join('\n')
|
|
76
|
+
: '';
|
|
77
|
+
|
|
78
|
+
return `version: "3.8"
|
|
79
|
+
|
|
80
|
+
services:
|
|
81
|
+
${agentName}:
|
|
82
|
+
build: .
|
|
83
|
+
ports:
|
|
84
|
+
- "${port}:${port}"
|
|
85
|
+
environment:
|
|
86
|
+
- NODE_ENV=production
|
|
87
|
+
- PORT=${port}
|
|
88
|
+
${envLines ? envLines + '\n' : ''} restart: unless-stopped
|
|
89
|
+
${replicas > 1 ? ` deploy:\n replicas: ${replicas}` : ''}
|
|
90
|
+
volumes:
|
|
91
|
+
- agent-data:/app/data
|
|
92
|
+
|
|
93
|
+
volumes:
|
|
94
|
+
agent-data:
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Deploy to Railway (via CLI)
|
|
100
|
+
*/
|
|
101
|
+
async deployRailway(agentDir: string): Promise<DeployResult> {
|
|
102
|
+
try {
|
|
103
|
+
execSync('railway version', { stdio: 'pipe' });
|
|
104
|
+
} catch {
|
|
105
|
+
return { platform: 'railway', success: false, message: 'Railway CLI not installed. Run: npm i -g @railway/cli' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// Ensure Dockerfile exists
|
|
110
|
+
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
111
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
112
|
+
const content = await this.generateDockerfile(agentDir);
|
|
113
|
+
fs.writeFileSync(dockerfilePath, content);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const output = execSync('railway up --detach', { cwd: agentDir, encoding: 'utf-8' });
|
|
117
|
+
const urlMatch = output.match(/(https:\/\/[^\s]+)/);
|
|
118
|
+
return {
|
|
119
|
+
platform: 'railway',
|
|
120
|
+
success: true,
|
|
121
|
+
url: urlMatch?.[1],
|
|
122
|
+
message: 'Deployed to Railway successfully'
|
|
123
|
+
};
|
|
124
|
+
} catch (e: any) {
|
|
125
|
+
return { platform: 'railway', success: false, message: e.message };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Deploy to Fly.io
|
|
131
|
+
*/
|
|
132
|
+
async deployFly(agentDir: string): Promise<DeployResult> {
|
|
133
|
+
try {
|
|
134
|
+
execSync('fly version', { stdio: 'pipe' });
|
|
135
|
+
} catch {
|
|
136
|
+
return { platform: 'fly', success: false, message: 'Fly CLI not installed. Run: curl -L https://fly.io/install.sh | sh' };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const agentName = path.basename(agentDir).toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
141
|
+
|
|
142
|
+
// Generate fly.toml if not exists
|
|
143
|
+
const flyTomlPath = path.join(agentDir, 'fly.toml');
|
|
144
|
+
if (!fs.existsSync(flyTomlPath)) {
|
|
145
|
+
fs.writeFileSync(flyTomlPath, `app = "${agentName}"
|
|
146
|
+
primary_region = "sjc"
|
|
147
|
+
|
|
148
|
+
[build]
|
|
149
|
+
|
|
150
|
+
[http_service]
|
|
151
|
+
internal_port = 3000
|
|
152
|
+
force_https = true
|
|
153
|
+
auto_stop_machines = true
|
|
154
|
+
auto_start_machines = true
|
|
155
|
+
|
|
156
|
+
[checks]
|
|
157
|
+
[checks.alive]
|
|
158
|
+
type = "tcp"
|
|
159
|
+
port = 3000
|
|
160
|
+
`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Ensure Dockerfile exists
|
|
164
|
+
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
165
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
166
|
+
const content = await this.generateDockerfile(agentDir);
|
|
167
|
+
fs.writeFileSync(dockerfilePath, content);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const output = execSync('fly deploy', { cwd: agentDir, encoding: 'utf-8' });
|
|
171
|
+
return {
|
|
172
|
+
platform: 'fly',
|
|
173
|
+
success: true,
|
|
174
|
+
url: `https://${agentName}.fly.dev`,
|
|
175
|
+
message: 'Deployed to Fly.io successfully'
|
|
176
|
+
};
|
|
177
|
+
} catch (e: any) {
|
|
178
|
+
return { platform: 'fly', success: false, message: e.message };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Deploy locally via Docker
|
|
184
|
+
*/
|
|
185
|
+
async deployLocal(agentDir: string, options?: DeployOptions): Promise<DeployResult> {
|
|
186
|
+
try {
|
|
187
|
+
execSync('docker version', { stdio: 'pipe' });
|
|
188
|
+
} catch {
|
|
189
|
+
return { platform: 'docker', success: false, message: 'Docker not installed or not running' };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const files: string[] = [];
|
|
193
|
+
|
|
194
|
+
// Generate Dockerfile
|
|
195
|
+
const dockerfilePath = path.join(agentDir, 'Dockerfile');
|
|
196
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
197
|
+
fs.writeFileSync(dockerfilePath, await this.generateDockerfile(agentDir, options));
|
|
198
|
+
files.push('Dockerfile');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Generate docker-compose.yml
|
|
202
|
+
const composePath = path.join(agentDir, 'docker-compose.yml');
|
|
203
|
+
if (!fs.existsSync(composePath)) {
|
|
204
|
+
fs.writeFileSync(composePath, await this.generateCompose(agentDir, options));
|
|
205
|
+
files.push('docker-compose.yml');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
execSync('docker compose up -d --build', { cwd: agentDir, stdio: 'inherit' });
|
|
210
|
+
const port = options?.port || 3000;
|
|
211
|
+
return {
|
|
212
|
+
platform: 'docker',
|
|
213
|
+
success: true,
|
|
214
|
+
url: `http://localhost:${port}`,
|
|
215
|
+
message: 'Running locally via Docker',
|
|
216
|
+
files
|
|
217
|
+
};
|
|
218
|
+
} catch (e: any) {
|
|
219
|
+
return { platform: 'docker', success: false, message: e.message, files };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Generate deployment files without deploying
|
|
225
|
+
*/
|
|
226
|
+
async generateFiles(agentDir: string, options?: DeployOptions): Promise<DeployResult> {
|
|
227
|
+
const files: string[] = [];
|
|
228
|
+
const port = options?.port || 3000;
|
|
229
|
+
|
|
230
|
+
const dockerfile = await this.generateDockerfile(agentDir, options);
|
|
231
|
+
fs.writeFileSync(path.join(agentDir, 'Dockerfile'), dockerfile);
|
|
232
|
+
files.push('Dockerfile');
|
|
233
|
+
|
|
234
|
+
const compose = await this.generateCompose(agentDir, options);
|
|
235
|
+
fs.writeFileSync(path.join(agentDir, 'docker-compose.yml'), compose);
|
|
236
|
+
files.push('docker-compose.yml');
|
|
237
|
+
|
|
238
|
+
// Generate .dockerignore
|
|
239
|
+
const dockerignore = `node_modules
|
|
240
|
+
.git
|
|
241
|
+
.env
|
|
242
|
+
*.log
|
|
243
|
+
dist
|
|
244
|
+
`;
|
|
245
|
+
fs.writeFileSync(path.join(agentDir, '.dockerignore'), dockerignore);
|
|
246
|
+
files.push('.dockerignore');
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
platform: 'docker',
|
|
250
|
+
success: true,
|
|
251
|
+
message: `Generated ${files.length} deployment files`,
|
|
252
|
+
files,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|