opc-agent 4.1.1 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/channels/dingtalk.d.ts.map +1 -0
- package/dist/channels/dingtalk.js.map +1 -0
- package/dist/channels/discord.d.ts.map +1 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/email.d.ts.map +1 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/feishu.d.ts.map +1 -0
- package/dist/channels/feishu.js.map +1 -0
- package/dist/channels/googlechat.d.ts.map +1 -0
- package/dist/channels/googlechat.js.map +1 -0
- package/dist/channels/imessage.d.ts.map +1 -0
- package/dist/channels/imessage.js.map +1 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/irc.d.ts.map +1 -0
- package/dist/channels/irc.js.map +1 -0
- package/dist/channels/line.d.ts.map +1 -0
- package/dist/channels/line.js.map +1 -0
- package/dist/channels/matrix.d.ts.map +1 -0
- package/dist/channels/matrix.js.map +1 -0
- package/dist/channels/mattermost.d.ts.map +1 -0
- package/dist/channels/mattermost.js.map +1 -0
- package/dist/channels/msteams.d.ts.map +1 -0
- package/dist/channels/msteams.js.map +1 -0
- package/dist/channels/nostr.d.ts.map +1 -0
- package/dist/channels/nostr.js.map +1 -0
- package/dist/channels/qq.d.ts.map +1 -0
- package/dist/channels/qq.js.map +1 -0
- package/dist/channels/signal.d.ts.map +1 -0
- package/dist/channels/signal.js.map +1 -0
- package/dist/channels/slack.d.ts.map +1 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/sms.d.ts.map +1 -0
- package/dist/channels/sms.js.map +1 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/twitch.d.ts.map +1 -0
- package/dist/channels/twitch.js.map +1 -0
- package/dist/channels/voice-call.d.ts.map +1 -0
- package/dist/channels/voice-call.js.map +1 -0
- package/dist/channels/voice.d.ts.map +1 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/channels/web.d.ts.map +1 -0
- package/dist/channels/web.js +8 -2
- package/dist/channels/web.js.map +1 -0
- package/dist/channels/webhook.d.ts.map +1 -0
- package/dist/channels/webhook.js.map +1 -0
- package/dist/channels/websocket.d.ts.map +1 -0
- package/dist/channels/websocket.js.map +1 -0
- package/dist/channels/wechat.d.ts.map +1 -0
- package/dist/channels/wechat.js.map +1 -0
- package/dist/channels/whatsapp.d.ts.map +1 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/cli/chat.d.ts.map +1 -0
- package/dist/cli/chat.js.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +261 -85
- package/dist/cli.js.map +1 -0
- package/dist/core/a2a.d.ts.map +1 -0
- package/dist/core/a2a.js.map +1 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/analytics-engine.d.ts.map +1 -0
- package/dist/core/analytics-engine.js.map +1 -0
- package/dist/core/api-server.d.ts.map +1 -0
- package/dist/core/api-server.js.map +1 -0
- package/dist/core/audio.d.ts.map +1 -0
- package/dist/core/audio.js.map +1 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/collaboration.d.ts.map +1 -0
- package/dist/core/collaboration.js.map +1 -0
- package/dist/core/compose.d.ts.map +1 -0
- package/dist/core/compose.js.map +1 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/context-discovery.d.ts.map +1 -0
- package/dist/core/context-discovery.js.map +1 -0
- package/dist/core/context-refs.d.ts.map +1 -0
- package/dist/core/context-refs.js.map +1 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/gateway.d.ts.map +1 -0
- package/dist/core/gateway.js.map +1 -0
- package/dist/core/heartbeat.d.ts.map +1 -0
- package/dist/core/heartbeat.js.map +1 -0
- package/dist/core/hitl.d.ts.map +1 -0
- package/dist/core/hitl.js.map +1 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/ide-bridge.d.ts.map +1 -0
- package/dist/core/ide-bridge.js.map +1 -0
- package/dist/core/knowledge.d.ts.map +1 -0
- package/dist/core/knowledge.js.map +1 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/node-network.d.ts.map +1 -0
- package/dist/core/node-network.js.map +1 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/performance.d.ts.map +1 -0
- package/dist/core/performance.js.map +1 -0
- package/dist/core/profiles.d.ts.map +1 -0
- package/dist/core/profiles.js.map +1 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/room.d.ts.map +1 -0
- package/dist/core/room.js.map +1 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +37 -15
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/security.d.ts.map +1 -0
- package/dist/core/security.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/streaming.d.ts.map +1 -0
- package/dist/core/streaming.js.map +1 -0
- package/dist/core/subagent.d.ts.map +1 -0
- package/dist/core/subagent.js.map +1 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/versioning.d.ts.map +1 -0
- package/dist/core/versioning.js.map +1 -0
- package/dist/core/vision.d.ts.map +1 -0
- package/dist/core/vision.js.map +1 -0
- package/dist/core/watch.d.ts.map +1 -0
- package/dist/core/watch.js.map +1 -0
- package/dist/core/workflow-graph.d.ts.map +1 -0
- package/dist/core/workflow-graph.js.map +1 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js.map +1 -0
- package/dist/deploy/hermes.d.ts.map +1 -0
- package/dist/deploy/hermes.js.map +1 -0
- package/dist/deploy/index.d.ts.map +1 -0
- package/dist/deploy/index.js.map +1 -0
- package/dist/deploy/openclaw.d.ts.map +1 -0
- package/dist/deploy/openclaw.js.map +1 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +105 -10
- package/dist/doctor.js.map +1 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/hub/brain-seed.d.ts.map +1 -0
- package/dist/hub/brain-seed.js.map +1 -0
- package/dist/hub/client.d.ts.map +1 -0
- package/dist/hub/client.js.map +1 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/servers/calculator-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/calculator-mcp.js.map +1 -0
- package/dist/mcp/servers/crypto-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/crypto-mcp.js.map +1 -0
- package/dist/mcp/servers/database-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/database-mcp.js.map +1 -0
- package/dist/mcp/servers/datetime-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/datetime-mcp.js.map +1 -0
- package/dist/mcp/servers/filesystem.d.ts.map +1 -0
- package/dist/mcp/servers/filesystem.js.map +1 -0
- package/dist/mcp/servers/github-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/github-mcp.js.map +1 -0
- package/dist/mcp/servers/index.d.ts.map +1 -0
- package/dist/mcp/servers/index.js.map +1 -0
- package/dist/mcp/servers/json-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/json-mcp.js.map +1 -0
- package/dist/mcp/servers/memory-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/memory-mcp.js.map +1 -0
- package/dist/mcp/servers/regex-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/regex-mcp.js.map +1 -0
- package/dist/mcp/servers/web-mcp.d.ts.map +1 -0
- package/dist/mcp/servers/web-mcp.js.map +1 -0
- package/dist/memory/context-compressor.d.ts.map +1 -0
- package/dist/memory/context-compressor.js.map +1 -0
- package/dist/memory/deepbrain.d.ts +1 -1
- package/dist/memory/deepbrain.d.ts.map +1 -0
- package/dist/memory/deepbrain.js +95 -4
- package/dist/memory/deepbrain.js.map +1 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/seed-loader.d.ts.map +1 -0
- package/dist/memory/seed-loader.js.map +1 -0
- package/dist/memory/user-profiler.d.ts.map +1 -0
- package/dist/memory/user-profiler.js.map +1 -0
- package/dist/plugins/content-filter.d.ts.map +1 -0
- package/dist/plugins/content-filter.js.map +1 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/logger.d.ts.map +1 -0
- package/dist/plugins/logger.js.map +1 -0
- package/dist/plugins/rate-limiter.d.ts.map +1 -0
- package/dist/plugins/rate-limiter.js.map +1 -0
- package/dist/protocols/a2a/client.d.ts.map +1 -0
- package/dist/protocols/a2a/client.js.map +1 -0
- package/dist/protocols/a2a/index.d.ts.map +1 -0
- package/dist/protocols/a2a/index.js.map +1 -0
- package/dist/protocols/a2a/server.d.ts.map +1 -0
- package/dist/protocols/a2a/server.js.map +1 -0
- package/dist/protocols/a2a/types.d.ts.map +1 -0
- package/dist/protocols/a2a/types.js.map +1 -0
- package/dist/protocols/a2a/utils.d.ts.map +1 -0
- package/dist/protocols/a2a/utils.js.map +1 -0
- package/dist/protocols/agui/client.d.ts.map +1 -0
- package/dist/protocols/agui/client.js.map +1 -0
- package/dist/protocols/agui/index.d.ts.map +1 -0
- package/dist/protocols/agui/index.js.map +1 -0
- package/dist/protocols/agui/server.d.ts.map +1 -0
- package/dist/protocols/agui/server.js.map +1 -0
- package/dist/protocols/agui/types.d.ts.map +1 -0
- package/dist/protocols/agui/types.js.map +1 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js.map +1 -0
- package/dist/protocols/mcp/agent-tools.d.ts.map +1 -0
- package/dist/protocols/mcp/agent-tools.js.map +1 -0
- package/dist/protocols/mcp/index.d.ts.map +1 -0
- package/dist/protocols/mcp/index.js.map +1 -0
- package/dist/protocols/mcp/server.d.ts.map +1 -0
- package/dist/protocols/mcp/server.js.map +1 -0
- package/dist/protocols/mcp/types.d.ts.map +1 -0
- package/dist/protocols/mcp/types.js.map +1 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/publish/index.d.ts.map +1 -0
- package/dist/publish/index.js.map +1 -0
- package/dist/scheduler/cron-engine.d.ts.map +1 -0
- package/dist/scheduler/cron-engine.js +3 -36
- package/dist/scheduler/cron-engine.js.map +1 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/schema/oad.d.ts.map +1 -0
- package/dist/schema/oad.js.map +1 -0
- package/dist/security/approval.d.ts.map +1 -0
- package/dist/security/approval.js.map +1 -0
- package/dist/security/approvals.d.ts.map +1 -0
- package/dist/security/approvals.js.map +1 -0
- package/dist/security/elevated.d.ts.map +1 -0
- package/dist/security/elevated.js.map +1 -0
- package/dist/security/guardrails.d.ts.map +1 -0
- package/dist/security/guardrails.js.map +1 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/keys.d.ts.map +1 -0
- package/dist/security/keys.js.map +1 -0
- package/dist/security/secrets.d.ts.map +1 -0
- package/dist/security/secrets.js.map +1 -0
- package/dist/skills/auto-learn.d.ts.map +1 -0
- package/dist/skills/auto-learn.js.map +1 -0
- package/dist/skills/base.d.ts.map +1 -0
- package/dist/skills/base.js.map +1 -0
- package/dist/skills/builtin/index.d.ts.map +1 -0
- package/dist/skills/builtin/index.js.map +1 -0
- package/dist/skills/document.d.ts.map +1 -0
- package/dist/skills/document.js.map +1 -0
- package/dist/skills/http.d.ts.map +1 -0
- package/dist/skills/http.js.map +1 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/marketplace.d.ts.map +1 -0
- package/dist/skills/marketplace.js.map +1 -0
- package/dist/skills/scheduler.d.ts.map +1 -0
- package/dist/skills/scheduler.js.map +1 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/skills/webhook-trigger.d.ts.map +1 -0
- package/dist/skills/webhook-trigger.js.map +1 -0
- package/dist/studio/server.d.ts.map +1 -0
- package/dist/studio/server.js.map +1 -0
- package/dist/studio/templates-data.d.ts.map +1 -0
- package/dist/studio/templates-data.js.map +1 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/templates/code-reviewer.d.ts.map +1 -0
- package/dist/templates/code-reviewer.js.map +1 -0
- package/dist/templates/content-writer.d.ts.map +1 -0
- package/dist/templates/content-writer.js.map +1 -0
- package/dist/templates/customer-service.d.ts.map +1 -0
- package/dist/templates/customer-service.js.map +1 -0
- package/dist/templates/data-analyst.d.ts.map +1 -0
- package/dist/templates/data-analyst.js.map +1 -0
- package/dist/templates/executive-assistant.d.ts.map +1 -0
- package/dist/templates/executive-assistant.js.map +1 -0
- package/dist/templates/financial-advisor.d.ts.map +1 -0
- package/dist/templates/financial-advisor.js.map +1 -0
- package/dist/templates/hr-recruiter.d.ts.map +1 -0
- package/dist/templates/hr-recruiter.js.map +1 -0
- package/dist/templates/knowledge-base.d.ts.map +1 -0
- package/dist/templates/knowledge-base.js.map +1 -0
- package/dist/templates/legal-assistant.d.ts.map +1 -0
- package/dist/templates/legal-assistant.js.map +1 -0
- package/dist/templates/project-manager.d.ts.map +1 -0
- package/dist/templates/project-manager.js.map +1 -0
- package/dist/templates/sales-assistant.d.ts.map +1 -0
- package/dist/templates/sales-assistant.js.map +1 -0
- package/dist/templates/teacher.d.ts.map +1 -0
- package/dist/templates/teacher.js.map +1 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tools/builtin/browser.d.ts.map +1 -0
- package/dist/tools/builtin/browser.js.map +1 -0
- package/dist/tools/builtin/datetime.d.ts.map +1 -0
- package/dist/tools/builtin/datetime.js.map +1 -0
- package/dist/tools/builtin/file.d.ts.map +1 -0
- package/dist/tools/builtin/file.js.map +1 -0
- package/dist/tools/builtin/home-assistant.d.ts.map +1 -0
- package/dist/tools/builtin/home-assistant.js.map +1 -0
- package/dist/tools/builtin/index.d.ts.map +1 -0
- package/dist/tools/builtin/index.js.map +1 -0
- package/dist/tools/builtin/rl-tools.d.ts.map +1 -0
- package/dist/tools/builtin/rl-tools.js.map +1 -0
- package/dist/tools/builtin/shell.d.ts.map +1 -0
- package/dist/tools/builtin/shell.js.map +1 -0
- package/dist/tools/builtin/vision.d.ts.map +1 -0
- package/dist/tools/builtin/vision.js.map +1 -0
- package/dist/tools/builtin/web-search.d.ts.map +1 -0
- package/dist/tools/builtin/web-search.js.map +1 -0
- package/dist/tools/builtin/web.d.ts.map +1 -0
- package/dist/tools/builtin/web.js.map +1 -0
- package/dist/tools/calculator.d.ts.map +1 -0
- package/dist/tools/calculator.js.map +1 -0
- package/dist/tools/datetime.d.ts.map +1 -0
- package/dist/tools/datetime.js.map +1 -0
- package/dist/tools/document-processor.d.ts.map +1 -0
- package/dist/tools/document-processor.js.map +1 -0
- package/dist/tools/gateway.d.ts.map +1 -0
- package/dist/tools/gateway.js.map +1 -0
- package/dist/tools/image-generator.d.ts.map +1 -0
- package/dist/tools/image-generator.js.map +1 -0
- package/dist/tools/integrations/calendar.d.ts.map +1 -0
- package/dist/tools/integrations/calendar.js.map +1 -0
- package/dist/tools/integrations/code-exec.d.ts.map +1 -0
- package/dist/tools/integrations/code-exec.js.map +1 -0
- package/dist/tools/integrations/csv-analyzer.d.ts.map +1 -0
- package/dist/tools/integrations/csv-analyzer.js.map +1 -0
- package/dist/tools/integrations/database.d.ts.map +1 -0
- package/dist/tools/integrations/database.js.map +1 -0
- package/dist/tools/integrations/email-send.d.ts.map +1 -0
- package/dist/tools/integrations/email-send.js.map +1 -0
- package/dist/tools/integrations/git-tool.d.ts.map +1 -0
- package/dist/tools/integrations/git-tool.js.map +1 -0
- package/dist/tools/integrations/github-tool.d.ts.map +1 -0
- package/dist/tools/integrations/github-tool.js.map +1 -0
- package/dist/tools/integrations/image-gen.d.ts.map +1 -0
- package/dist/tools/integrations/image-gen.js.map +1 -0
- package/dist/tools/integrations/index.d.ts.map +1 -0
- package/dist/tools/integrations/index.js.map +1 -0
- package/dist/tools/integrations/jira.d.ts.map +1 -0
- package/dist/tools/integrations/jira.js.map +1 -0
- package/dist/tools/integrations/notion.d.ts.map +1 -0
- package/dist/tools/integrations/notion.js.map +1 -0
- package/dist/tools/integrations/npm-tool.d.ts.map +1 -0
- package/dist/tools/integrations/npm-tool.js.map +1 -0
- package/dist/tools/integrations/pdf-reader.d.ts.map +1 -0
- package/dist/tools/integrations/pdf-reader.js.map +1 -0
- package/dist/tools/integrations/slack.d.ts.map +1 -0
- package/dist/tools/integrations/slack.js.map +1 -0
- package/dist/tools/integrations/summarizer.d.ts.map +1 -0
- package/dist/tools/integrations/summarizer.js.map +1 -0
- package/dist/tools/integrations/translator.d.ts.map +1 -0
- package/dist/tools/integrations/translator.js.map +1 -0
- package/dist/tools/integrations/trello.d.ts.map +1 -0
- package/dist/tools/integrations/trello.js.map +1 -0
- package/dist/tools/integrations/vector-search.d.ts.map +1 -0
- package/dist/tools/integrations/vector-search.js.map +1 -0
- package/dist/tools/integrations/web-scraper.d.ts.map +1 -0
- package/dist/tools/integrations/web-scraper.js.map +1 -0
- package/dist/tools/integrations/web-search.d.ts.map +1 -0
- package/dist/tools/integrations/web-search.js.map +1 -0
- package/dist/tools/integrations/webhook.d.ts.map +1 -0
- package/dist/tools/integrations/webhook.js.map +1 -0
- package/dist/tools/json-transform.d.ts.map +1 -0
- package/dist/tools/json-transform.js.map +1 -0
- package/dist/tools/mcp-client.d.ts.map +1 -0
- package/dist/tools/mcp-client.js.map +1 -0
- package/dist/tools/mcp.d.ts.map +1 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/text-analysis.d.ts.map +1 -0
- package/dist/tools/text-analysis.js.map +1 -0
- package/dist/tools/web-scraper.d.ts.map +1 -0
- package/dist/tools/web-scraper.js.map +1 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/traces/index.d.ts.map +1 -0
- package/dist/traces/index.js.map +1 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js.map +1 -0
- package/package.json +1 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -13
- package/.github/workflows/ci.yml +0 -24
- package/docs/.vitepress/config.ts +0 -103
- package/docs/api/cli.md +0 -48
- package/docs/api/oad-schema.md +0 -64
- package/docs/api/sdk.md +0 -80
- package/docs/guide/concepts.md +0 -51
- package/docs/guide/configuration.md +0 -79
- package/docs/guide/deployment.md +0 -42
- package/docs/guide/getting-started.md +0 -44
- package/docs/guide/templates.md +0 -28
- package/docs/guide/testing.md +0 -84
- package/docs/index.md +0 -27
- package/docs/zh/api/cli.md +0 -54
- package/docs/zh/api/oad-schema.md +0 -87
- package/docs/zh/api/sdk.md +0 -102
- package/docs/zh/guide/concepts.md +0 -104
- package/docs/zh/guide/configuration.md +0 -135
- package/docs/zh/guide/deployment.md +0 -81
- package/docs/zh/guide/getting-started.md +0 -82
- package/docs/zh/guide/templates.md +0 -84
- package/docs/zh/guide/testing.md +0 -88
- package/docs/zh/index.md +0 -27
- package/fix-sidebar.mjs +0 -188
- package/serve-studio.js +0 -13
- package/serve-test.js +0 -25
- package/src/analytics/index.ts +0 -66
- package/src/channels/dingtalk.ts +0 -46
- package/src/channels/discord.ts +0 -192
- package/src/channels/email.ts +0 -351
- package/src/channels/feishu.ts +0 -349
- package/src/channels/googlechat.ts +0 -42
- package/src/channels/imessage.ts +0 -32
- package/src/channels/index.ts +0 -15
- package/src/channels/irc.ts +0 -82
- package/src/channels/line.ts +0 -33
- package/src/channels/matrix.ts +0 -34
- package/src/channels/mattermost.ts +0 -57
- package/src/channels/msteams.ts +0 -33
- package/src/channels/nostr.ts +0 -33
- package/src/channels/qq.ts +0 -34
- package/src/channels/signal.ts +0 -33
- package/src/channels/slack.ts +0 -217
- package/src/channels/sms.ts +0 -34
- package/src/channels/telegram.ts +0 -616
- package/src/channels/twitch.ts +0 -65
- package/src/channels/voice-call.ts +0 -100
- package/src/channels/voice.ts +0 -471
- package/src/channels/web.ts +0 -632
- package/src/channels/webhook.ts +0 -199
- package/src/channels/websocket.ts +0 -399
- package/src/channels/wechat.ts +0 -329
- package/src/channels/whatsapp.ts +0 -33
- package/src/cli/chat.ts +0 -99
- package/src/cli/setup.ts +0 -314
- package/src/cli.ts +0 -2723
- package/src/core/a2a.ts +0 -203
- package/src/core/agent.ts +0 -476
- package/src/core/analytics-engine.ts +0 -186
- package/src/core/api-server.ts +0 -277
- package/src/core/audio.ts +0 -98
- package/src/core/auth.ts +0 -57
- package/src/core/cache.ts +0 -141
- package/src/core/collaboration.ts +0 -275
- package/src/core/compose.ts +0 -77
- package/src/core/config.ts +0 -14
- package/src/core/context-discovery.ts +0 -85
- package/src/core/context-refs.ts +0 -140
- package/src/core/errors.ts +0 -148
- package/src/core/gateway.ts +0 -106
- package/src/core/heartbeat.ts +0 -51
- package/src/core/hitl.ts +0 -138
- package/src/core/hooks.ts +0 -105
- package/src/core/ide-bridge.ts +0 -133
- package/src/core/knowledge.ts +0 -255
- package/src/core/logger.ts +0 -57
- package/src/core/node-network.ts +0 -86
- package/src/core/orchestrator.ts +0 -215
- package/src/core/performance.ts +0 -187
- package/src/core/profiles.ts +0 -122
- package/src/core/rate-limiter.ts +0 -128
- package/src/core/room.ts +0 -109
- package/src/core/runtime.ts +0 -410
- package/src/core/sandbox.ts +0 -344
- package/src/core/scheduler.ts +0 -187
- package/src/core/security.ts +0 -171
- package/src/core/session-manager.ts +0 -137
- package/src/core/streaming.ts +0 -195
- package/src/core/subagent.ts +0 -98
- package/src/core/types.ts +0 -68
- package/src/core/versioning.ts +0 -106
- package/src/core/vision.ts +0 -180
- package/src/core/watch.ts +0 -178
- package/src/core/workflow-graph.ts +0 -365
- package/src/core/workflow.ts +0 -235
- package/src/daemon.ts +0 -96
- package/src/deploy/hermes.ts +0 -156
- package/src/deploy/index.ts +0 -255
- package/src/deploy/openclaw.ts +0 -190
- package/src/doctor.ts +0 -156
- package/src/eval/index.ts +0 -211
- package/src/eval/suites/basic.json +0 -16
- package/src/eval/suites/memory.json +0 -12
- package/src/eval/suites/safety.json +0 -14
- package/src/hub/brain-seed.ts +0 -54
- package/src/hub/client.ts +0 -60
- package/src/i18n/index.ts +0 -216
- package/src/index.ts +0 -283
- package/src/mcp/servers/calculator-mcp.ts +0 -65
- package/src/mcp/servers/crypto-mcp.ts +0 -73
- package/src/mcp/servers/database-mcp.ts +0 -72
- package/src/mcp/servers/datetime-mcp.ts +0 -69
- package/src/mcp/servers/filesystem.ts +0 -66
- package/src/mcp/servers/github-mcp.ts +0 -58
- package/src/mcp/servers/index.ts +0 -63
- package/src/mcp/servers/json-mcp.ts +0 -102
- package/src/mcp/servers/memory-mcp.ts +0 -56
- package/src/mcp/servers/regex-mcp.ts +0 -53
- package/src/mcp/servers/web-mcp.ts +0 -49
- package/src/memory/context-compressor.ts +0 -189
- package/src/memory/deepbrain.ts +0 -108
- package/src/memory/index.ts +0 -41
- package/src/memory/seed-loader.ts +0 -212
- package/src/memory/user-profiler.ts +0 -215
- package/src/plugins/content-filter.ts +0 -23
- package/src/plugins/index.ts +0 -339
- package/src/plugins/logger.ts +0 -18
- package/src/plugins/rate-limiter.ts +0 -38
- package/src/protocols/a2a/client.ts +0 -132
- package/src/protocols/a2a/index.ts +0 -8
- package/src/protocols/a2a/server.ts +0 -333
- package/src/protocols/a2a/types.ts +0 -88
- package/src/protocols/a2a/utils.ts +0 -50
- package/src/protocols/agui/client.ts +0 -83
- package/src/protocols/agui/index.ts +0 -4
- package/src/protocols/agui/server.ts +0 -218
- package/src/protocols/agui/types.ts +0 -153
- package/src/protocols/index.ts +0 -2
- package/src/protocols/mcp/agent-tools.ts +0 -134
- package/src/protocols/mcp/index.ts +0 -8
- package/src/protocols/mcp/server.ts +0 -262
- package/src/protocols/mcp/types.ts +0 -69
- package/src/providers/index.ts +0 -632
- package/src/publish/index.ts +0 -376
- package/src/scheduler/cron-engine.ts +0 -191
- package/src/scheduler/index.ts +0 -2
- package/src/schema/oad.ts +0 -217
- package/src/security/approval.ts +0 -131
- package/src/security/approvals.ts +0 -143
- package/src/security/elevated.ts +0 -105
- package/src/security/guardrails.ts +0 -248
- package/src/security/index.ts +0 -9
- package/src/security/keys.ts +0 -87
- package/src/security/secrets.ts +0 -129
- package/src/skills/auto-learn.ts +0 -262
- package/src/skills/base.ts +0 -16
- package/src/skills/builtin/index.ts +0 -408
- package/src/skills/document.ts +0 -100
- package/src/skills/http.ts +0 -35
- package/src/skills/index.ts +0 -27
- package/src/skills/marketplace.ts +0 -113
- package/src/skills/scheduler.ts +0 -80
- package/src/skills/types.ts +0 -42
- package/src/skills/webhook-trigger.ts +0 -59
- package/src/studio/server.ts +0 -1791
- package/src/studio/templates-data.ts +0 -178
- package/src/studio-ui/index.html +0 -3076
- package/src/telemetry/index.ts +0 -324
- package/src/templates/code-reviewer.ts +0 -30
- package/src/templates/content-writer.ts +0 -58
- package/src/templates/customer-service.ts +0 -76
- package/src/templates/data-analyst.ts +0 -66
- package/src/templates/executive-assistant.ts +0 -71
- package/src/templates/financial-advisor.ts +0 -60
- package/src/templates/hr-recruiter.ts +0 -58
- package/src/templates/knowledge-base.ts +0 -27
- package/src/templates/legal-assistant.ts +0 -71
- package/src/templates/project-manager.ts +0 -58
- package/src/templates/sales-assistant.ts +0 -75
- package/src/templates/teacher.ts +0 -75
- package/src/testing/index.ts +0 -181
- package/src/tools/builtin/browser.ts +0 -299
- package/src/tools/builtin/datetime.ts +0 -41
- package/src/tools/builtin/file.ts +0 -107
- package/src/tools/builtin/home-assistant.ts +0 -116
- package/src/tools/builtin/index.ts +0 -37
- package/src/tools/builtin/rl-tools.ts +0 -243
- package/src/tools/builtin/shell.ts +0 -43
- package/src/tools/builtin/vision.ts +0 -64
- package/src/tools/builtin/web-search.ts +0 -126
- package/src/tools/builtin/web.ts +0 -35
- package/src/tools/calculator.ts +0 -73
- package/src/tools/datetime.ts +0 -149
- package/src/tools/document-processor.ts +0 -213
- package/src/tools/gateway.ts +0 -220
- package/src/tools/image-generator.ts +0 -150
- package/src/tools/integrations/calendar.ts +0 -73
- package/src/tools/integrations/code-exec.ts +0 -39
- package/src/tools/integrations/csv-analyzer.ts +0 -92
- package/src/tools/integrations/database.ts +0 -44
- package/src/tools/integrations/email-send.ts +0 -76
- package/src/tools/integrations/git-tool.ts +0 -42
- package/src/tools/integrations/github-tool.ts +0 -76
- package/src/tools/integrations/image-gen.ts +0 -56
- package/src/tools/integrations/index.ts +0 -92
- package/src/tools/integrations/jira.ts +0 -83
- package/src/tools/integrations/notion.ts +0 -71
- package/src/tools/integrations/npm-tool.ts +0 -48
- package/src/tools/integrations/pdf-reader.ts +0 -58
- package/src/tools/integrations/slack.ts +0 -65
- package/src/tools/integrations/summarizer.ts +0 -49
- package/src/tools/integrations/translator.ts +0 -48
- package/src/tools/integrations/trello.ts +0 -60
- package/src/tools/integrations/vector-search.ts +0 -42
- package/src/tools/integrations/web-scraper.ts +0 -47
- package/src/tools/integrations/web-search.ts +0 -58
- package/src/tools/integrations/webhook.ts +0 -38
- package/src/tools/json-transform.ts +0 -187
- package/src/tools/mcp-client.ts +0 -131
- package/src/tools/mcp.ts +0 -76
- package/src/tools/text-analysis.ts +0 -116
- package/src/tools/web-scraper.ts +0 -179
- package/src/tools/web-search.ts +0 -180
- package/src/traces/index.ts +0 -132
- package/src/types/agent-workstation.d.ts +0 -2
- package/src/ui/components.ts +0 -127
- package/srv-err.txt +0 -0
- package/srv-out.txt +0 -1
- package/test-agent/Dockerfile +0 -9
- package/test-agent/README.md +0 -50
- package/test-agent/agent.yaml +0 -23
- package/test-agent/docker-compose.yml +0 -11
- package/test-agent/oad.yaml +0 -31
- package/test-agent/package-lock.json +0 -1492
- package/test-agent/package.json +0 -18
- package/test-agent/src/index.ts +0 -24
- package/test-agent/src/skills/echo.ts +0 -15
- package/test-agent/tsconfig.json +0 -25
- package/test-full.js +0 -43
- package/test-sidebar.js +0 -22
- package/test-studio3.js +0 -75
- package/test-studio4.js +0 -41
- package/tests/a2a-protocol.test.ts +0 -285
- package/tests/a2a.test.ts +0 -66
- package/tests/agent.test.ts +0 -72
- package/tests/agui-protocol.test.ts +0 -246
- package/tests/analytics.test.ts +0 -50
- package/tests/api-server.test.ts +0 -148
- package/tests/approvals.test.ts +0 -89
- package/tests/audio.test.ts +0 -40
- package/tests/auto-learn.test.ts +0 -105
- package/tests/brain-seed-extended.test.ts +0 -490
- package/tests/brain-seed.test.ts +0 -239
- package/tests/browser.test.ts +0 -179
- package/tests/builtin-tools.test.ts +0 -83
- package/tests/channel.test.ts +0 -39
- package/tests/channels/discord.test.ts +0 -79
- package/tests/channels/email.test.ts +0 -148
- package/tests/channels/feishu.test.ts +0 -123
- package/tests/channels/telegram.test.ts +0 -129
- package/tests/channels/websocket.test.ts +0 -53
- package/tests/channels/wechat.test.ts +0 -170
- package/tests/channels-extra.test.ts +0 -45
- package/tests/chat-cli.test.ts +0 -160
- package/tests/cli.test.ts +0 -46
- package/tests/collaboration.test.ts +0 -319
- package/tests/context-compressor.test.ts +0 -172
- package/tests/context-refs.test.ts +0 -121
- package/tests/cron-engine.test.ts +0 -101
- package/tests/daemon.test.ts +0 -135
- package/tests/deepbrain-wire.test.ts +0 -234
- package/tests/deploy-and-dag.test.ts +0 -196
- package/tests/doctor.test.ts +0 -38
- package/tests/document-processor.test.ts +0 -69
- package/tests/e2e-nocode.test.ts +0 -442
- package/tests/e2e.test.ts +0 -134
- package/tests/elevated.test.ts +0 -69
- package/tests/errors.test.ts +0 -83
- package/tests/eval.test.ts +0 -173
- package/tests/gateway.test.ts +0 -63
- package/tests/guardrails.test.ts +0 -177
- package/tests/hitl.test.ts +0 -71
- package/tests/home-assistant.test.ts +0 -40
- package/tests/hooks.test.ts +0 -79
- package/tests/i18n.test.ts +0 -41
- package/tests/ide-bridge.test.ts +0 -38
- package/tests/image-generator.test.ts +0 -84
- package/tests/init-role.test.ts +0 -124
- package/tests/integrations.test.ts +0 -249
- package/tests/mcp-client.test.ts +0 -92
- package/tests/mcp-server.test.ts +0 -178
- package/tests/mcp-servers.test.ts +0 -260
- package/tests/mcp.test.ts +0 -54
- package/tests/node-network.test.ts +0 -74
- package/tests/oad.test.ts +0 -68
- package/tests/performance.test.ts +0 -115
- package/tests/plugin-a2a-enhanced.test.ts +0 -230
- package/tests/plugin.test.ts +0 -74
- package/tests/profiles.test.ts +0 -61
- package/tests/publish.test.ts +0 -231
- package/tests/rl-tools.test.ts +0 -93
- package/tests/room.test.ts +0 -106
- package/tests/runtime.test.ts +0 -42
- package/tests/sandbox-manager.test.ts +0 -46
- package/tests/sandbox.test.ts +0 -46
- package/tests/scheduler.test.ts +0 -200
- package/tests/secrets.test.ts +0 -107
- package/tests/security-enhanced.test.ts +0 -233
- package/tests/security.test.ts +0 -60
- package/tests/settings-api.test.ts +0 -148
- package/tests/setup.test.ts +0 -73
- package/tests/skill-learner.test.ts +0 -161
- package/tests/streaming.test.ts +0 -109
- package/tests/studio.test.ts +0 -402
- package/tests/subagent.test.ts +0 -193
- package/tests/telegram-discord.test.ts +0 -60
- package/tests/telemetry.test.ts +0 -186
- package/tests/templates.test.ts +0 -77
- package/tests/tools/builtin-extended.test.ts +0 -138
- package/tests/user-profiler.test.ts +0 -169
- package/tests/v070.test.ts +0 -76
- package/tests/v090-features.test.ts +0 -254
- package/tests/versioning.test.ts +0 -75
- package/tests/vision.test.ts +0 -61
- package/tests/voice-call.test.ts +0 -47
- package/tests/voice-enhanced.test.ts +0 -169
- package/tests/voice-interaction.test.ts +0 -38
- package/tests/voice.test.ts +0 -61
- package/tests/web-search.test.ts +0 -155
- package/tests/webhook.test.ts +0 -29
- package/tests/workflow-graph.test.ts +0 -279
- package/tests/workflow.test.ts +0 -143
- package/tmp-js-test.js +0 -1532
- package/tmp-sc.js +0 -1716
- package/tutorial/customer-service-agent/README.md +0 -612
- package/tutorial/customer-service-agent/SOUL.md +0 -26
- package/tutorial/customer-service-agent/agent.yaml +0 -63
- package/tutorial/customer-service-agent/package.json +0 -19
- package/tutorial/customer-service-agent/src/index.ts +0 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +0 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +0 -22
- package/tutorial/customer-service-agent/tsconfig.json +0 -14
- package/vitest.config.ts +0 -9
package/src/cli.ts
DELETED
|
@@ -1,2723 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as yaml from 'js-yaml';
|
|
6
|
-
import * as readline from 'readline';
|
|
7
|
-
import { AgentRuntime } from './core/runtime';
|
|
8
|
-
import { createCustomerServiceConfig } from './templates/customer-service';
|
|
9
|
-
import { createSalesAssistantConfig } from './templates/sales-assistant';
|
|
10
|
-
import { createKnowledgeBaseConfig } from './templates/knowledge-base';
|
|
11
|
-
import { createCodeReviewerConfig } from './templates/code-reviewer';
|
|
12
|
-
import { createHRRecruiterConfig } from './templates/hr-recruiter';
|
|
13
|
-
import { createProjectManagerConfig } from './templates/project-manager';
|
|
14
|
-
import { createContentWriterConfig } from './templates/content-writer';
|
|
15
|
-
import { createLegalAssistantConfig } from './templates/legal-assistant';
|
|
16
|
-
import { createFinancialAdvisorConfig } from './templates/financial-advisor';
|
|
17
|
-
import { createExecutiveAssistantConfig } from './templates/executive-assistant';
|
|
18
|
-
import { createDataAnalystConfig } from './templates/data-analyst';
|
|
19
|
-
import { createTeacherConfig } from './templates/teacher';
|
|
20
|
-
import { FAQSkill, HandoffSkill } from './templates/customer-service';
|
|
21
|
-
import { Analytics } from './analytics';
|
|
22
|
-
import { AnalyticsEngine } from './core/analytics-engine';
|
|
23
|
-
import { runTests, formatReport } from './testing';
|
|
24
|
-
import { deployToOpenClaw } from './deploy/openclaw';
|
|
25
|
-
import { deployToHermes } from './deploy/hermes';
|
|
26
|
-
import { AgentDeployer } from './deploy/index';
|
|
27
|
-
import { WorkflowEngine } from './core/workflow';
|
|
28
|
-
import { VersionManager } from './core/versioning';
|
|
29
|
-
import { createProvider } from './providers';
|
|
30
|
-
import { KnowledgeBase } from './core/knowledge';
|
|
31
|
-
|
|
32
|
-
import { PluginManager, createLoggingPlugin, createAnalyticsPlugin, createRateLimitPlugin } from './plugins';
|
|
33
|
-
import { runDoctor } from './doctor';
|
|
34
|
-
import { Scheduler } from './core/scheduler';
|
|
35
|
-
import type { CronJob } from './core/scheduler';
|
|
36
|
-
import type { Span } from './traces';
|
|
37
|
-
import { spawn } from 'child_process';
|
|
38
|
-
import { searchRoles, getPopularRoles, getRole, getCategories } from 'agent-workstation';
|
|
39
|
-
import { fetchTemplates as hubFetchTemplates, fetchTemplate as hubFetchTemplate, fetchBrainSeeds, isHubAvailable } from './hub/client';
|
|
40
|
-
import type { HubTemplate } from './hub/client';
|
|
41
|
-
import { downloadAndLearnBrainSeeds } from './hub/brain-seed';
|
|
42
|
-
|
|
43
|
-
const program = new Command();
|
|
44
|
-
|
|
45
|
-
const color = {
|
|
46
|
-
green: (s: string) => `\x1b[32m${s}\x1b[0m`,
|
|
47
|
-
red: (s: string) => `\x1b[31m${s}\x1b[0m`,
|
|
48
|
-
yellow: (s: string) => `\x1b[33m${s}\x1b[0m`,
|
|
49
|
-
blue: (s: string) => `\x1b[34m${s}\x1b[0m`,
|
|
50
|
-
cyan: (s: string) => `\x1b[36m${s}\x1b[0m`,
|
|
51
|
-
bold: (s: string) => `\x1b[1m${s}\x1b[0m`,
|
|
52
|
-
dim: (s: string) => `\x1b[2m${s}\x1b[0m`,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const icon = {
|
|
56
|
-
success: color.green('✔'),
|
|
57
|
-
error: color.red('✘'),
|
|
58
|
-
warn: color.yellow('⚠'),
|
|
59
|
-
info: color.blue('ℹ'),
|
|
60
|
-
rocket: '🚀',
|
|
61
|
-
package: '📦',
|
|
62
|
-
search: '🔍',
|
|
63
|
-
gear: '⚙️',
|
|
64
|
-
file: '📄',
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const TEMPLATES: Record<string, { label: string; factory: () => any }> = {
|
|
68
|
-
'customer-service': { label: 'Customer Service - FAQ + human handoff', factory: createCustomerServiceConfig },
|
|
69
|
-
'sales-assistant': { label: 'Sales Assistant - product Q&A + lead capture', factory: createSalesAssistantConfig },
|
|
70
|
-
'knowledge-base': { label: 'Knowledge Base - RAG with DeepBrain', factory: createKnowledgeBaseConfig },
|
|
71
|
-
'code-reviewer': { label: 'Code Reviewer - bug detection + style checks', factory: createCodeReviewerConfig },
|
|
72
|
-
'hr-recruiter': { label: 'HR Recruiter - resume screening + interview scheduling', factory: createHRRecruiterConfig },
|
|
73
|
-
'project-manager': { label: 'Project Manager - task tracking + meeting notes', factory: createProjectManagerConfig },
|
|
74
|
-
'content-writer': { label: 'Content Writer - blog posts + social media + SEO', factory: createContentWriterConfig },
|
|
75
|
-
'legal-assistant': { label: 'Legal Assistant - contract review + compliance + legal research', factory: createLegalAssistantConfig },
|
|
76
|
-
'financial-advisor': { label: 'Financial Advisor - budget analysis + expense tracking + planning', factory: createFinancialAdvisorConfig },
|
|
77
|
-
'executive-assistant': { label: 'Executive Assistant - calendar + email drafting + meeting prep', factory: createExecutiveAssistantConfig },
|
|
78
|
-
'data-analyst': { label: 'Data Analyst - data querying + visualization + insights', factory: createDataAnalystConfig },
|
|
79
|
-
'teacher': { label: 'Teacher - lesson planning + quizzes + concept explanation', factory: createTeacherConfig },
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
async function promptUser(question: string, defaultValue?: string): Promise<string> {
|
|
83
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
84
|
-
const suffix = defaultValue ? ` ${color.dim(`(${defaultValue})`)}` : '';
|
|
85
|
-
return new Promise((resolve) => {
|
|
86
|
-
rl.question(`${question}${suffix}: `, (answer) => {
|
|
87
|
-
rl.close();
|
|
88
|
-
resolve(answer.trim() || defaultValue || '');
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function select(question: string, options: { value: string; label: string }[]): Promise<string> {
|
|
94
|
-
console.log(`\n${question}`);
|
|
95
|
-
options.forEach((opt, i) => {
|
|
96
|
-
console.log(` ${color.cyan(`${i + 1})`)} ${opt.label}`);
|
|
97
|
-
});
|
|
98
|
-
const answer = await promptUser(`\nChoose ${color.dim('(1-' + options.length + ')')}`, '1');
|
|
99
|
-
const idx = parseInt(answer) - 1;
|
|
100
|
-
return options[Math.max(0, Math.min(idx, options.length - 1))].value;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
program
|
|
104
|
-
.name('opc')
|
|
105
|
-
.description('OPC Agent - Open Agent Framework for business workstations')
|
|
106
|
-
.version('2.0.0');
|
|
107
|
-
|
|
108
|
-
// ── Init command ─────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
program
|
|
111
|
-
.command('init')
|
|
112
|
-
.description('Initialize a new OPC agent project')
|
|
113
|
-
.argument('[name]', 'Project name')
|
|
114
|
-
.option('-t, --template <template>', 'Template to use')
|
|
115
|
-
.option('-y, --yes', 'Skip prompts, use defaults')
|
|
116
|
-
.option('-r, --role <role>', 'Use an agent-workstation role template')
|
|
117
|
-
.option('--list-roles', 'List available workstation roles')
|
|
118
|
-
.action(async (nameArg: string | undefined, opts: { template?: string; yes?: boolean; role?: string; listRoles?: boolean }) => {
|
|
119
|
-
console.log(`\n${icon.rocket} ${color.bold('OPC Agent - Create New Project')}\n`);
|
|
120
|
-
|
|
121
|
-
// Handle --list-roles
|
|
122
|
-
if (opts.listRoles) {
|
|
123
|
-
const roles = getPopularRoles();
|
|
124
|
-
console.log(`📋 ${color.bold('Available workstation roles:')}\n`);
|
|
125
|
-
for (const r of roles) {
|
|
126
|
-
const fullRole = getRole(r.category, r.role);
|
|
127
|
-
let roleName = r.role;
|
|
128
|
-
if (fullRole?.files?.['oad.yaml']) {
|
|
129
|
-
try {
|
|
130
|
-
const oadData = yaml.load(fullRole.files['oad.yaml']) as any;
|
|
131
|
-
if (oadData?.name) roleName = oadData.name;
|
|
132
|
-
} catch { /* ignore */ }
|
|
133
|
-
}
|
|
134
|
-
console.log(` ${color.cyan((r.category + '/' + r.role).padEnd(45))} ${roleName}`);
|
|
135
|
-
}
|
|
136
|
-
console.log(`\n Use: ${color.cyan('opc init my-agent --role <role-name>')}`);
|
|
137
|
-
console.log(` Example: ${color.cyan('opc init my-agent --role customer-service')}\n`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Handle --role: search and generate from workstation template
|
|
142
|
-
if (opts.role) {
|
|
143
|
-
const results = searchRoles(opts.role);
|
|
144
|
-
if (results.length === 0) {
|
|
145
|
-
console.error(`${icon.error} Role "${color.bold(opts.role)}" not found. Run '${color.cyan('opc init --list-roles')}' to see available roles.`);
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const matched = results[0];
|
|
150
|
-
const roleData = getRole(matched.category, matched.role);
|
|
151
|
-
if (!roleData || !roleData.files) {
|
|
152
|
-
console.error(`${icon.error} Could not load role data for ${matched.category}/${matched.role}.`);
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const name = nameArg ?? matched.role;
|
|
157
|
-
const dir = path.resolve(name);
|
|
158
|
-
if (fs.existsSync(dir)) {
|
|
159
|
-
console.error(`\n${icon.error} Directory ${color.bold(name)} already exists.`);
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Parse role metadata from oad.yaml
|
|
164
|
-
let roleMeta: any = {};
|
|
165
|
-
if (roleData.files['oad.yaml']) {
|
|
166
|
-
try { roleMeta = yaml.load(roleData.files['oad.yaml']) as any; } catch { /* ignore */ }
|
|
167
|
-
}
|
|
168
|
-
const roleDisplayName = roleMeta.name || matched.role;
|
|
169
|
-
const roleDescription = roleMeta.name_zh ? `${roleMeta.name} (${roleMeta.name_zh})` : (roleMeta.name || matched.role);
|
|
170
|
-
|
|
171
|
-
console.log(` ${icon.info} Matched role: ${color.cyan(matched.category + '/' + matched.role)} — ${roleDisplayName}`);
|
|
172
|
-
|
|
173
|
-
// Create directories
|
|
174
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
175
|
-
fs.mkdirSync(path.join(dir, 'src', 'skills'), { recursive: true });
|
|
176
|
-
fs.mkdirSync(path.join(dir, 'data'), { recursive: true });
|
|
177
|
-
fs.mkdirSync(path.join(dir, 'brain-seeds'), { recursive: true });
|
|
178
|
-
|
|
179
|
-
// Get system prompt content
|
|
180
|
-
const systemPromptContent = roleData.files['system-prompt.md'] || roleData.files['prompts/system.md'] || '';
|
|
181
|
-
|
|
182
|
-
// Generate brain-seeds/ files from role data
|
|
183
|
-
const brainSeedContent = roleData.files['brain-seed.md'] || '';
|
|
184
|
-
const industryMatch = brainSeedContent ? brainSeedContent.match(/# Industry Knowledge[\s\S]*?(?=# Job Knowledge|# Workstation Knowledge|$)/i) : null;
|
|
185
|
-
const jobMatch = brainSeedContent ? brainSeedContent.match(/# Job Knowledge[\s\S]*?(?=# Industry Knowledge|# Workstation Knowledge|$)/i) : null;
|
|
186
|
-
const workstationMatch = brainSeedContent ? brainSeedContent.match(/# Workstation Knowledge[\s\S]*?(?=# Industry Knowledge|# Job Knowledge|$)/i) : null;
|
|
187
|
-
|
|
188
|
-
fs.writeFileSync(path.join(dir, 'brain-seeds', 'industry.md'), industryMatch?.[0]?.trim() || `# Industry Knowledge\n\n## Overview\n\nAdd industry-specific knowledge for your domain.\n`);
|
|
189
|
-
fs.writeFileSync(path.join(dir, 'brain-seeds', 'job.md'), jobMatch?.[0]?.trim() || `# Job Knowledge\n\n## Core Skills\n\nAdd role-specific knowledge for ${roleDisplayName}.\n`);
|
|
190
|
-
// workstation.md: public workstation knowledge (tools, workflows, best practices)
|
|
191
|
-
// Company-specific knowledge belongs to Desk (closed-source), not here.
|
|
192
|
-
const workstationSeedFromRole = workstationMatch?.[0]?.trim() || '';
|
|
193
|
-
fs.writeFileSync(path.join(dir, 'brain-seeds', 'workstation.md'), workstationSeedFromRole || `# Workstation Knowledge\n\n## Tools & Environment\n\nCommon tools and setup for this workstation role.\n\n## Workflows\n\nStandard operating procedures and workflows.\n\n## Best Practices\n\nIndustry best practices for this role.\n`);
|
|
194
|
-
|
|
195
|
-
// agent.yaml with role system prompt and brain seeds
|
|
196
|
-
const firstLine = systemPromptContent.split('\n').find((l: string) => l.trim() && !l.startsWith('#'))?.trim() || 'You are a helpful AI assistant.';
|
|
197
|
-
fs.writeFileSync(
|
|
198
|
-
path.join(dir, 'agent.yaml'),
|
|
199
|
-
`apiVersion: opc/v1
|
|
200
|
-
kind: Agent
|
|
201
|
-
metadata:
|
|
202
|
-
name: ${name}
|
|
203
|
-
version: 1.0.0
|
|
204
|
-
description: ${roleDescription}
|
|
205
|
-
spec:
|
|
206
|
-
model: qwen2.5
|
|
207
|
-
provider:
|
|
208
|
-
default: ollama
|
|
209
|
-
systemPrompt: |
|
|
210
|
-
${systemPromptContent.split('\n').join('\n ')}
|
|
211
|
-
channels:
|
|
212
|
-
- type: web
|
|
213
|
-
port: 3000
|
|
214
|
-
memory:
|
|
215
|
-
shortTerm: true
|
|
216
|
-
longTerm:
|
|
217
|
-
provider: deepbrain
|
|
218
|
-
database: ./data/brain.db
|
|
219
|
-
brain:
|
|
220
|
-
seeds:
|
|
221
|
-
- brain-seeds/industry.md
|
|
222
|
-
- brain-seeds/job.md
|
|
223
|
-
- brain-seeds/workstation.md
|
|
224
|
-
autoSeed: true
|
|
225
|
-
evolve:
|
|
226
|
-
enabled: true
|
|
227
|
-
direction: bottom-up
|
|
228
|
-
skills: []
|
|
229
|
-
`,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// SOUL.md from system-prompt.md
|
|
233
|
-
fs.writeFileSync(path.join(dir, 'SOUL.md'), systemPromptContent);
|
|
234
|
-
|
|
235
|
-
// CONTEXT.md
|
|
236
|
-
const readmeContent = roleData.files['README.md'] || '';
|
|
237
|
-
fs.writeFileSync(
|
|
238
|
-
path.join(dir, 'CONTEXT.md'),
|
|
239
|
-
`# Project Context\n\n## Role: ${roleDisplayName}\n\n${readmeContent}\n`,
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// data/brain-seed.md if available
|
|
243
|
-
if (roleData.files['brain-seed.md']) {
|
|
244
|
-
fs.writeFileSync(path.join(dir, 'data', 'brain-seed.md'), roleData.files['brain-seed.md']);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// oad.yaml from role
|
|
248
|
-
if (roleData.files['oad.yaml']) {
|
|
249
|
-
fs.writeFileSync(path.join(dir, 'oad.yaml'), roleData.files['oad.yaml']);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// src/index.ts — entry point (same as generic)
|
|
253
|
-
fs.writeFileSync(
|
|
254
|
-
path.join(dir, 'src', 'index.ts'),
|
|
255
|
-
`import { AgentRuntime } from 'opc-agent';
|
|
256
|
-
import { EchoSkill } from './skills/echo';
|
|
257
|
-
import { readFileSync, existsSync } from 'fs';
|
|
258
|
-
|
|
259
|
-
async function main() {
|
|
260
|
-
const runtime = new AgentRuntime();
|
|
261
|
-
const config = await runtime.loadConfig('./agent.yaml');
|
|
262
|
-
|
|
263
|
-
const soul = existsSync('./SOUL.md') ? readFileSync('./SOUL.md', 'utf-8') : '';
|
|
264
|
-
const context = existsSync('./CONTEXT.md') ? readFileSync('./CONTEXT.md', 'utf-8') : '';
|
|
265
|
-
if (soul || context) {
|
|
266
|
-
const fullPrompt = [soul, context, config.spec.systemPrompt].filter(Boolean).join('\\n\\n');
|
|
267
|
-
config.spec.systemPrompt = fullPrompt;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const agent = await runtime.initialize(config);
|
|
271
|
-
runtime.registerSkill(new EchoSkill());
|
|
272
|
-
await runtime.start();
|
|
273
|
-
|
|
274
|
-
console.log('🤖 Agent is running!');
|
|
275
|
-
console.log(' Web UI: http://localhost:3000');
|
|
276
|
-
console.log(' Press Ctrl+C to stop');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
main().catch(console.error);
|
|
280
|
-
`,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
// src/skills/echo.ts
|
|
284
|
-
fs.writeFileSync(
|
|
285
|
-
path.join(dir, 'src', 'skills', 'echo.ts'),
|
|
286
|
-
`import { BaseSkill } from 'opc-agent';
|
|
287
|
-
import type { AgentContext, Message, SkillResult } from 'opc-agent';
|
|
288
|
-
|
|
289
|
-
export class EchoSkill extends BaseSkill {
|
|
290
|
-
name = 'echo';
|
|
291
|
-
description = 'Echo back the message (test skill)';
|
|
292
|
-
|
|
293
|
-
async execute(context: AgentContext, message: Message): Promise<SkillResult> {
|
|
294
|
-
if (message.content.toLowerCase().startsWith('/echo ')) {
|
|
295
|
-
const text = message.content.slice(6);
|
|
296
|
-
return this.match(\`🔊 Echo: \${text}\`);
|
|
297
|
-
}
|
|
298
|
-
return this.noMatch();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
`,
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// tsconfig.json
|
|
305
|
-
fs.writeFileSync(
|
|
306
|
-
path.join(dir, 'tsconfig.json'),
|
|
307
|
-
JSON.stringify(
|
|
308
|
-
{
|
|
309
|
-
compilerOptions: { target: 'ES2022', module: 'commonjs', lib: ['ES2022'], outDir: 'dist', rootDir: 'src', strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, resolveJsonModule: true, declaration: true, sourceMap: true },
|
|
310
|
-
include: ['src/**/*'],
|
|
311
|
-
exclude: ['node_modules', 'dist'],
|
|
312
|
-
},
|
|
313
|
-
null,
|
|
314
|
-
2,
|
|
315
|
-
),
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
// package.json
|
|
319
|
-
fs.writeFileSync(
|
|
320
|
-
path.join(dir, 'package.json'),
|
|
321
|
-
JSON.stringify(
|
|
322
|
-
{ name, version: '1.0.0', private: true, scripts: { start: 'opc run', dev: 'opc dev', chat: 'opc chat', build: 'tsc' }, dependencies: { 'opc-agent': '^1.3.0' }, devDependencies: { typescript: '^5.5.0', tsx: '^4.0.0' } },
|
|
323
|
-
null,
|
|
324
|
-
2,
|
|
325
|
-
),
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
// .gitignore, .env.example, .env
|
|
329
|
-
fs.writeFileSync(path.join(dir, '.gitignore'), 'node_modules\ndist\n.env\n.opc-knowledge.json\ndata/\n');
|
|
330
|
-
fs.writeFileSync(path.join(dir, '.env.example'), `# LLM API Configuration\nOPC_LLM_API_KEY=your-api-key-here\nOPC_LLM_BASE_URL=https://api.openai.com/v1\nOPC_LLM_MODEL=gpt-4o-mini\n`);
|
|
331
|
-
fs.writeFileSync(path.join(dir, '.env'), `OPC_LLM_API_KEY=your-api-key-here\nOPC_LLM_BASE_URL=https://api.openai.com/v1\nOPC_LLM_MODEL=gpt-4o-mini\n`);
|
|
332
|
-
|
|
333
|
-
// README.md
|
|
334
|
-
fs.writeFileSync(
|
|
335
|
-
path.join(dir, 'README.md'),
|
|
336
|
-
`# ${name}\n\nCreated with [OPC Agent](https://github.com/Deepleaper/opc-agent) using the \`${matched.category}/${matched.role}\` workstation role.\n\n## Quick Start\n\n\`\`\`bash\nnpm install\nollama pull qwen2.5\nnpx tsx src/index.ts\n\`\`\`\n\nOpen [http://localhost:3000](http://localhost:3000)\n`,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
// Dockerfile + docker-compose
|
|
340
|
-
fs.writeFileSync(path.join(dir, 'Dockerfile'), `FROM node:22-alpine\nWORKDIR /app\nCOPY package.json package-lock.json* ./\nRUN npm ci --production 2>/dev/null || npm install --production\nCOPY oad.yaml agent.yaml .env* ./\nCOPY src/ ./src/\nCOPY prompts/ ./prompts/ 2>/dev/null || true\nEXPOSE 3000\nCMD ["npx", "opc", "run"]\n`);
|
|
341
|
-
fs.writeFileSync(path.join(dir, 'docker-compose.yml'), `version: '3.8'\nservices:\n agent:\n build: .\n ports:\n - "3000:3000"\n env_file:\n - .env\n volumes:\n - ./agent.yaml:/app/agent.yaml:ro\n restart: unless-stopped\n`);
|
|
342
|
-
|
|
343
|
-
console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')} from role ${color.cyan(matched.category + '/' + matched.role)}`);
|
|
344
|
-
console.log(` ${icon.file} agent.yaml - Agent definition with role system prompt`);
|
|
345
|
-
console.log(` ${icon.file} SOUL.md - Role personality (${systemPromptContent.split('\n').length} lines)`);
|
|
346
|
-
console.log(` ${icon.file} CONTEXT.md - Role context & documentation`);
|
|
347
|
-
console.log(` ${icon.file} brain-seeds/ - 3-tier brain seed knowledge`);
|
|
348
|
-
console.log(` ${color.dim('├')} industry.md - Industry knowledge`);
|
|
349
|
-
console.log(` ${color.dim('├')} job.md - Job/role knowledge`);
|
|
350
|
-
console.log(` ${color.dim('└')} workstation.md - Workstation knowledge`);
|
|
351
|
-
if (roleData.files['brain-seed.md']) {
|
|
352
|
-
console.log(` ${icon.file} data/brain-seed.md - Role brain seed knowledge (legacy)`);
|
|
353
|
-
}
|
|
354
|
-
console.log(` ${icon.file} src/index.ts - Entry point`);
|
|
355
|
-
console.log(` ${icon.file} package.json - Dependencies`);
|
|
356
|
-
console.log(`\n${color.bold('Next steps:')}`);
|
|
357
|
-
console.log(` 1. cd ${name}`);
|
|
358
|
-
console.log(` 2. npm install`);
|
|
359
|
-
console.log(` 3. npx tsx src/index.ts\n`);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const name = opts.yes ? (nameArg ?? 'my-agent') : (nameArg ?? await promptUser('Project name', 'my-agent'));
|
|
364
|
-
|
|
365
|
-
// Try fetching templates from Hub API, fall back to bundled
|
|
366
|
-
let hubTemplates: HubTemplate[] = [];
|
|
367
|
-
let useHub = false;
|
|
368
|
-
try {
|
|
369
|
-
const hubAvailable = await isHubAvailable();
|
|
370
|
-
if (hubAvailable) {
|
|
371
|
-
hubTemplates = await hubFetchTemplates();
|
|
372
|
-
if (hubTemplates.length > 0) useHub = true;
|
|
373
|
-
}
|
|
374
|
-
} catch {
|
|
375
|
-
// Hub unreachable — fall back to bundled templates
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
let template: string;
|
|
379
|
-
let selectedHubTemplate: HubTemplate | undefined;
|
|
380
|
-
if (opts.yes) {
|
|
381
|
-
template = opts.template ?? 'customer-service';
|
|
382
|
-
} else if (opts.template) {
|
|
383
|
-
template = opts.template;
|
|
384
|
-
if (useHub) selectedHubTemplate = hubTemplates.find(t => t.id === template);
|
|
385
|
-
} else if (useHub) {
|
|
386
|
-
console.log(` ${icon.info} ${color.dim('Using templates from Workstation Hub')}`);
|
|
387
|
-
template = await select('Select a template:', hubTemplates.map(t => ({ value: t.id, label: `${t.name} - ${t.description}` })));
|
|
388
|
-
selectedHubTemplate = hubTemplates.find(t => t.id === template);
|
|
389
|
-
} else {
|
|
390
|
-
template = await select('Select a template:', Object.entries(TEMPLATES).map(([value, { label }]) => ({ value, label })));
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const dir = path.resolve(name);
|
|
394
|
-
if (fs.existsSync(dir)) {
|
|
395
|
-
console.error(`\n${icon.error} Directory ${color.bold(name)} already exists.`);
|
|
396
|
-
process.exit(1);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
400
|
-
fs.mkdirSync(path.join(dir, 'src', 'skills'), { recursive: true });
|
|
401
|
-
|
|
402
|
-
const factory = TEMPLATES[template]?.factory ?? createCustomerServiceConfig;
|
|
403
|
-
const config = factory();
|
|
404
|
-
config.metadata.name = name;
|
|
405
|
-
|
|
406
|
-
// Ensure web channel exists
|
|
407
|
-
if (!config.spec.channels.some((c: any) => c.type === 'web')) {
|
|
408
|
-
config.spec.channels.push({ type: 'web', port: 3000 });
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
fs.writeFileSync(path.join(dir, 'oad.yaml'), yaml.dump(config, { lineWidth: 120 }));
|
|
412
|
-
|
|
413
|
-
// agent.yaml — standalone OAD config for runtime usage
|
|
414
|
-
fs.writeFileSync(
|
|
415
|
-
path.join(dir, 'agent.yaml'),
|
|
416
|
-
`apiVersion: opc/v1
|
|
417
|
-
kind: Agent
|
|
418
|
-
metadata:
|
|
419
|
-
name: ${name}
|
|
420
|
-
version: 1.0.0
|
|
421
|
-
description: My AI Agent
|
|
422
|
-
spec:
|
|
423
|
-
model: qwen2.5
|
|
424
|
-
provider:
|
|
425
|
-
default: ollama
|
|
426
|
-
systemPrompt: |
|
|
427
|
-
You are a helpful AI assistant named ${name}.
|
|
428
|
-
Be concise, helpful, and friendly.
|
|
429
|
-
channels:
|
|
430
|
-
- type: web
|
|
431
|
-
port: 3000
|
|
432
|
-
memory:
|
|
433
|
-
shortTerm: true
|
|
434
|
-
longTerm:
|
|
435
|
-
provider: deepbrain
|
|
436
|
-
skills:
|
|
437
|
-
- name: echo
|
|
438
|
-
description: Echo test skill
|
|
439
|
-
`,
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
// src/index.ts — entry point
|
|
443
|
-
fs.writeFileSync(
|
|
444
|
-
path.join(dir, 'src', 'index.ts'),
|
|
445
|
-
`import { AgentRuntime } from 'opc-agent';
|
|
446
|
-
import { EchoSkill } from './skills/echo';
|
|
447
|
-
import { readFileSync, existsSync } from 'fs';
|
|
448
|
-
|
|
449
|
-
async function main() {
|
|
450
|
-
const runtime = new AgentRuntime();
|
|
451
|
-
|
|
452
|
-
// Load OAD config
|
|
453
|
-
const config = await runtime.loadConfig('./agent.yaml');
|
|
454
|
-
|
|
455
|
-
// Load personality and context files
|
|
456
|
-
const soul = existsSync('./SOUL.md') ? readFileSync('./SOUL.md', 'utf-8') : '';
|
|
457
|
-
const context = existsSync('./CONTEXT.md') ? readFileSync('./CONTEXT.md', 'utf-8') : '';
|
|
458
|
-
if (soul || context) {
|
|
459
|
-
const fullPrompt = [soul, context, config.spec.systemPrompt].filter(Boolean).join('\\n\\n');
|
|
460
|
-
config.spec.systemPrompt = fullPrompt;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Initialize agent with channels, memory, etc.
|
|
464
|
-
const agent = await runtime.initialize(config);
|
|
465
|
-
|
|
466
|
-
// Register custom skills
|
|
467
|
-
runtime.registerSkill(new EchoSkill());
|
|
468
|
-
|
|
469
|
-
// Start serving
|
|
470
|
-
await runtime.start();
|
|
471
|
-
|
|
472
|
-
console.log('🤖 Agent is running!');
|
|
473
|
-
console.log(' Web UI: http://localhost:3000');
|
|
474
|
-
console.log(' Press Ctrl+C to stop');
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
main().catch(console.error);
|
|
478
|
-
`,
|
|
479
|
-
);
|
|
480
|
-
|
|
481
|
-
// src/skills/echo.ts — example skill
|
|
482
|
-
fs.writeFileSync(
|
|
483
|
-
path.join(dir, 'src', 'skills', 'echo.ts'),
|
|
484
|
-
`import { BaseSkill } from 'opc-agent';
|
|
485
|
-
import type { AgentContext, Message, SkillResult } from 'opc-agent';
|
|
486
|
-
|
|
487
|
-
export class EchoSkill extends BaseSkill {
|
|
488
|
-
name = 'echo';
|
|
489
|
-
description = 'Echo back the message (test skill)';
|
|
490
|
-
|
|
491
|
-
async execute(context: AgentContext, message: Message): Promise<SkillResult> {
|
|
492
|
-
if (message.content.toLowerCase().startsWith('/echo ')) {
|
|
493
|
-
const text = message.content.slice(6);
|
|
494
|
-
return this.match(\`🔊 Echo: \${text}\`);
|
|
495
|
-
}
|
|
496
|
-
return this.noMatch();
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
`,
|
|
500
|
-
);
|
|
501
|
-
|
|
502
|
-
// tsconfig.json
|
|
503
|
-
fs.writeFileSync(
|
|
504
|
-
path.join(dir, 'tsconfig.json'),
|
|
505
|
-
JSON.stringify(
|
|
506
|
-
{
|
|
507
|
-
compilerOptions: {
|
|
508
|
-
target: 'ES2022',
|
|
509
|
-
module: 'commonjs',
|
|
510
|
-
lib: ['ES2022'],
|
|
511
|
-
outDir: 'dist',
|
|
512
|
-
rootDir: 'src',
|
|
513
|
-
strict: true,
|
|
514
|
-
esModuleInterop: true,
|
|
515
|
-
skipLibCheck: true,
|
|
516
|
-
forceConsistentCasingInFileNames: true,
|
|
517
|
-
resolveJsonModule: true,
|
|
518
|
-
declaration: true,
|
|
519
|
-
sourceMap: true,
|
|
520
|
-
},
|
|
521
|
-
include: ['src/**/*'],
|
|
522
|
-
exclude: ['node_modules', 'dist'],
|
|
523
|
-
},
|
|
524
|
-
null,
|
|
525
|
-
2,
|
|
526
|
-
),
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
// .env.example
|
|
530
|
-
fs.writeFileSync(
|
|
531
|
-
path.join(dir, '.env.example'),
|
|
532
|
-
`# LLM API Configuration
|
|
533
|
-
OPC_LLM_API_KEY=your-api-key-here
|
|
534
|
-
OPC_LLM_BASE_URL=https://api.openai.com/v1
|
|
535
|
-
OPC_LLM_MODEL=gpt-4o-mini
|
|
536
|
-
|
|
537
|
-
# For DeepSeek:
|
|
538
|
-
# OPC_LLM_BASE_URL=https://api.deepseek.com/v1
|
|
539
|
-
# OPC_LLM_MODEL=deepseek-chat
|
|
540
|
-
|
|
541
|
-
# For local Ollama (default in agent.yaml):
|
|
542
|
-
# OPC_LLM_BASE_URL=http://localhost:11434/v1
|
|
543
|
-
# OPC_LLM_MODEL=qwen2.5
|
|
544
|
-
`,
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
// .env (copy of example)
|
|
548
|
-
fs.writeFileSync(
|
|
549
|
-
path.join(dir, '.env'),
|
|
550
|
-
`OPC_LLM_API_KEY=your-api-key-here
|
|
551
|
-
OPC_LLM_BASE_URL=https://api.openai.com/v1
|
|
552
|
-
OPC_LLM_MODEL=gpt-4o-mini
|
|
553
|
-
`,
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
// package.json
|
|
557
|
-
fs.writeFileSync(
|
|
558
|
-
path.join(dir, 'package.json'),
|
|
559
|
-
JSON.stringify(
|
|
560
|
-
{
|
|
561
|
-
name,
|
|
562
|
-
version: '1.0.0',
|
|
563
|
-
private: true,
|
|
564
|
-
scripts: {
|
|
565
|
-
start: 'opc run',
|
|
566
|
-
dev: 'opc dev',
|
|
567
|
-
chat: 'opc chat',
|
|
568
|
-
build: 'tsc',
|
|
569
|
-
},
|
|
570
|
-
dependencies: {
|
|
571
|
-
'opc-agent': '^1.3.0',
|
|
572
|
-
},
|
|
573
|
-
devDependencies: {
|
|
574
|
-
typescript: '^5.5.0',
|
|
575
|
-
tsx: '^4.0.0',
|
|
576
|
-
},
|
|
577
|
-
},
|
|
578
|
-
null,
|
|
579
|
-
2,
|
|
580
|
-
),
|
|
581
|
-
);
|
|
582
|
-
|
|
583
|
-
// .gitignore
|
|
584
|
-
fs.writeFileSync(path.join(dir, '.gitignore'), 'node_modules\ndist\n.env\n.opc-knowledge.json\ndata/\n');
|
|
585
|
-
|
|
586
|
-
// Dockerfile
|
|
587
|
-
fs.writeFileSync(
|
|
588
|
-
path.join(dir, 'Dockerfile'),
|
|
589
|
-
`FROM node:22-alpine
|
|
590
|
-
WORKDIR /app
|
|
591
|
-
COPY package.json package-lock.json* ./
|
|
592
|
-
RUN npm ci --production 2>/dev/null || npm install --production
|
|
593
|
-
COPY oad.yaml agent.yaml .env* ./
|
|
594
|
-
COPY src/ ./src/
|
|
595
|
-
COPY prompts/ ./prompts/ 2>/dev/null || true
|
|
596
|
-
EXPOSE 3000
|
|
597
|
-
CMD ["npx", "opc", "run"]
|
|
598
|
-
`,
|
|
599
|
-
);
|
|
600
|
-
|
|
601
|
-
// docker-compose.yml
|
|
602
|
-
fs.writeFileSync(
|
|
603
|
-
path.join(dir, 'docker-compose.yml'),
|
|
604
|
-
`version: '3.8'
|
|
605
|
-
services:
|
|
606
|
-
agent:
|
|
607
|
-
build: .
|
|
608
|
-
ports:
|
|
609
|
-
- "3000:3000"
|
|
610
|
-
env_file:
|
|
611
|
-
- .env
|
|
612
|
-
volumes:
|
|
613
|
-
- ./agent.yaml:/app/agent.yaml:ro
|
|
614
|
-
restart: unless-stopped
|
|
615
|
-
`,
|
|
616
|
-
);
|
|
617
|
-
|
|
618
|
-
// README.md
|
|
619
|
-
fs.writeFileSync(
|
|
620
|
-
path.join(dir, 'README.md'),
|
|
621
|
-
`# ${name}
|
|
622
|
-
|
|
623
|
-
Created with [OPC Agent](https://github.com/Deepleaper/opc-agent) using the \`${template}\` template.
|
|
624
|
-
|
|
625
|
-
## Quick Start
|
|
626
|
-
|
|
627
|
-
1. **Install dependencies:**
|
|
628
|
-
\`\`\`bash
|
|
629
|
-
npm install
|
|
630
|
-
\`\`\`
|
|
631
|
-
|
|
632
|
-
2. **Run with Ollama (default):**
|
|
633
|
-
\`\`\`bash
|
|
634
|
-
# Make sure Ollama is running with qwen2.5 model
|
|
635
|
-
ollama pull qwen2.5
|
|
636
|
-
npx tsx src/index.ts
|
|
637
|
-
\`\`\`
|
|
638
|
-
|
|
639
|
-
3. **Or use OpenAI/other providers:**
|
|
640
|
-
\`\`\`bash
|
|
641
|
-
# Edit .env and set your API key
|
|
642
|
-
npx opc run
|
|
643
|
-
\`\`\`
|
|
644
|
-
|
|
645
|
-
4. **Open browser:** [http://localhost:3000](http://localhost:3000)
|
|
646
|
-
|
|
647
|
-
## Development
|
|
648
|
-
|
|
649
|
-
\`\`\`bash
|
|
650
|
-
npx opc dev # Hot-reload mode
|
|
651
|
-
npx opc chat # CLI chat
|
|
652
|
-
\`\`\`
|
|
653
|
-
|
|
654
|
-
## Project Structure
|
|
655
|
-
|
|
656
|
-
\`\`\`
|
|
657
|
-
${name}/
|
|
658
|
-
├── agent.yaml # OAD agent config (used by src/index.ts)
|
|
659
|
-
├── oad.yaml # OAD config (used by opc CLI)
|
|
660
|
-
├── src/
|
|
661
|
-
│ ├── index.ts # Entry point
|
|
662
|
-
│ └── skills/
|
|
663
|
-
│ └── echo.ts # Example skill
|
|
664
|
-
├── package.json
|
|
665
|
-
└── tsconfig.json
|
|
666
|
-
\`\`\`
|
|
667
|
-
|
|
668
|
-
## Configuration
|
|
669
|
-
|
|
670
|
-
Edit \`agent.yaml\` to customize your agent's personality, skills, and behavior.
|
|
671
|
-
`,
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
// SOUL.md — agent personality
|
|
675
|
-
const createdDate = new Date().toISOString().split('T')[0];
|
|
676
|
-
fs.writeFileSync(
|
|
677
|
-
path.join(dir, 'SOUL.md'),
|
|
678
|
-
`# ${name} Personality
|
|
679
|
-
|
|
680
|
-
## Identity
|
|
681
|
-
- Name: ${name}
|
|
682
|
-
- Role: AI Assistant
|
|
683
|
-
- Created: ${createdDate}
|
|
684
|
-
|
|
685
|
-
## Personality Traits
|
|
686
|
-
- Helpful and professional
|
|
687
|
-
- Concise but thorough
|
|
688
|
-
- Friendly tone
|
|
689
|
-
|
|
690
|
-
## Communication Style
|
|
691
|
-
- Use clear, simple language
|
|
692
|
-
- Be direct — answer the question first, then explain
|
|
693
|
-
- Use markdown formatting when helpful
|
|
694
|
-
|
|
695
|
-
## Rules
|
|
696
|
-
- Always be honest about limitations
|
|
697
|
-
- Ask for clarification when the request is ambiguous
|
|
698
|
-
- Never make up information
|
|
699
|
-
`,
|
|
700
|
-
);
|
|
701
|
-
|
|
702
|
-
// CONTEXT.md — project context
|
|
703
|
-
fs.writeFileSync(
|
|
704
|
-
path.join(dir, 'CONTEXT.md'),
|
|
705
|
-
`# Project Context
|
|
706
|
-
|
|
707
|
-
## About This Agent
|
|
708
|
-
${name} is an AI agent built with OPC Agent Framework.
|
|
709
|
-
|
|
710
|
-
## Knowledge Base
|
|
711
|
-
Add project-specific context here. The agent reads this file
|
|
712
|
-
on startup to understand the project context.
|
|
713
|
-
|
|
714
|
-
## Important Notes
|
|
715
|
-
- Add domain knowledge here
|
|
716
|
-
- Add FAQ items here
|
|
717
|
-
- Add company policies here
|
|
718
|
-
`,
|
|
719
|
-
);
|
|
720
|
-
|
|
721
|
-
console.log(`\n${icon.success} Created agent project: ${color.bold(name + '/')}`);
|
|
722
|
-
console.log(` ${icon.file} agent.yaml - Agent definition (OAD)`);
|
|
723
|
-
console.log(` ${icon.file} src/index.ts - Entry point`);
|
|
724
|
-
console.log(` ${icon.file} src/skills/echo.ts - Example skill`);
|
|
725
|
-
console.log(` ${icon.file} SOUL.md - Agent personality`);
|
|
726
|
-
console.log(` ${icon.file} CONTEXT.md - Project context`);
|
|
727
|
-
console.log(` ${icon.file} package.json - Dependencies`);
|
|
728
|
-
console.log(` ${icon.file} tsconfig.json - TypeScript config`);
|
|
729
|
-
console.log(` ${icon.file} .env.example - Environment template`);
|
|
730
|
-
console.log(` ${icon.file} .gitignore`);
|
|
731
|
-
console.log(` ${icon.file} Dockerfile`);
|
|
732
|
-
console.log(` ${icon.file} README.md`);
|
|
733
|
-
console.log(`\n Template: ${color.cyan(template)}`);
|
|
734
|
-
|
|
735
|
-
// Download brain-seed files from Hub if available
|
|
736
|
-
if (selectedHubTemplate) {
|
|
737
|
-
try {
|
|
738
|
-
const seeds = await fetchBrainSeeds(selectedHubTemplate.id);
|
|
739
|
-
if (seeds.length > 0) {
|
|
740
|
-
const result = await downloadAndLearnBrainSeeds(dir, seeds);
|
|
741
|
-
console.log(`\n 📚 Imported ${color.bold(String(result.savedFiles.length))} knowledge files into brain-seed/`);
|
|
742
|
-
if (result.learnedCount > 0) {
|
|
743
|
-
console.log(` 🧠 Auto-learned ${color.bold(String(result.learnedCount))} files into local DeepBrain`);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
} catch {
|
|
747
|
-
// Brain-seed download failed — non-fatal, project still usable
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
console.log(`\n${color.bold('Next steps:')}`);
|
|
751
|
-
console.log(` 1. cd ${name}`);
|
|
752
|
-
console.log(` 2. npm install`);
|
|
753
|
-
console.log(` 3. npx tsx src/index.ts ${color.dim('# or: npx opc run')}`);
|
|
754
|
-
console.log(` 4. Open http://localhost:3000\n`);
|
|
755
|
-
console.log(`${color.dim('💡 Tip: Use --role to start from a workstation template:')}`);
|
|
756
|
-
console.log(`${color.dim(' opc init my-agent --role customer-service')}`);
|
|
757
|
-
console.log(`${color.dim(' opc init --list-roles (see all roles)')}\n`);
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
// ── Chat command ─────────────────────────────────────────────
|
|
761
|
-
|
|
762
|
-
program
|
|
763
|
-
.command('chat')
|
|
764
|
-
.description('Interactive CLI chat with the agent')
|
|
765
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
766
|
-
.action(async (opts: { file: string }) => {
|
|
767
|
-
// Load .env if present
|
|
768
|
-
loadDotEnv();
|
|
769
|
-
|
|
770
|
-
let systemPrompt = 'You are a helpful AI agent.';
|
|
771
|
-
let model: string | undefined;
|
|
772
|
-
let agentName = 'Agent';
|
|
773
|
-
let agentVersion = '1.0.0';
|
|
774
|
-
let providerName = 'auto';
|
|
775
|
-
let skillNames: string[] = [];
|
|
776
|
-
|
|
777
|
-
// Try loading SOUL.md and CONTEXT.md for enriched system prompt
|
|
778
|
-
const soulPath = path.resolve('SOUL.md');
|
|
779
|
-
const contextPath = path.resolve('CONTEXT.md');
|
|
780
|
-
const soulContent = fs.existsSync(soulPath) ? fs.readFileSync(soulPath, 'utf-8') : '';
|
|
781
|
-
const contextContent = fs.existsSync(contextPath) ? fs.readFileSync(contextPath, 'utf-8') : '';
|
|
782
|
-
|
|
783
|
-
try {
|
|
784
|
-
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
785
|
-
const config = yaml.load(raw) as any;
|
|
786
|
-
if (config?.spec?.systemPrompt) systemPrompt = config.spec.systemPrompt;
|
|
787
|
-
if (config?.spec?.model) model = config.spec.model;
|
|
788
|
-
if (config?.metadata?.name) agentName = config.metadata.name;
|
|
789
|
-
if (config?.metadata?.version) agentVersion = config.metadata.version;
|
|
790
|
-
if (config?.spec?.provider?.default) providerName = config.spec.provider.default;
|
|
791
|
-
if (config?.spec?.skills) skillNames = config.spec.skills.map((s: any) => s.name);
|
|
792
|
-
} catch {
|
|
793
|
-
// No config file, use defaults
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// Prepend SOUL.md and CONTEXT.md to system prompt
|
|
797
|
-
systemPrompt = [soulContent, contextContent, systemPrompt].filter(Boolean).join('\n\n');
|
|
798
|
-
|
|
799
|
-
const provider = createProvider(providerName, model);
|
|
800
|
-
const history: { role: 'user' | 'assistant' | 'system'; content: string }[] = [];
|
|
801
|
-
|
|
802
|
-
// Print startup banner
|
|
803
|
-
const bannerLines = [
|
|
804
|
-
'╔══════════════════════════════════════╗',
|
|
805
|
-
'║ 🤖 OPC Agent — Interactive Chat ║',
|
|
806
|
-
`║ Agent: ${(agentName + ' v' + agentVersion).padEnd(27)}║`,
|
|
807
|
-
`║ Model: ${((providerName + '/' + (model ?? 'default')).slice(0, 27)).padEnd(27)}║`,
|
|
808
|
-
`║ Skills: ${(String(skillNames.length) + ' loaded').padEnd(26)}║`,
|
|
809
|
-
'║ Type /help for commands ║',
|
|
810
|
-
'╚══════════════════════════════════════╝',
|
|
811
|
-
];
|
|
812
|
-
console.log('\n' + color.cyan(bannerLines.join('\n')) + '\n');
|
|
813
|
-
|
|
814
|
-
if (soulContent) console.log(` ${icon.info} Loaded SOUL.md`);
|
|
815
|
-
if (contextContent) console.log(` ${icon.info} Loaded CONTEXT.md`);
|
|
816
|
-
if (soulContent || contextContent) console.log();
|
|
817
|
-
|
|
818
|
-
const rl = readline.createInterface({
|
|
819
|
-
input: process.stdin,
|
|
820
|
-
output: process.stdout,
|
|
821
|
-
historySize: 100,
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
const handleSlashCommand = (cmd: string): boolean => {
|
|
825
|
-
const lower = cmd.toLowerCase().trim();
|
|
826
|
-
if (lower === '/quit' || lower === '/exit') {
|
|
827
|
-
console.log(`\n${color.dim('Goodbye! 👋')}`);
|
|
828
|
-
process.exit(0);
|
|
829
|
-
}
|
|
830
|
-
if (lower === '/help') {
|
|
831
|
-
console.log(`\n ${color.bold('Available commands:')}`);
|
|
832
|
-
console.log(` ${color.cyan('/help')} — Show this help`);
|
|
833
|
-
console.log(` ${color.cyan('/quit')} — Exit chat (/exit also works)`);
|
|
834
|
-
console.log(` ${color.cyan('/clear')} — Clear conversation history`);
|
|
835
|
-
console.log(` ${color.cyan('/skills')} — List registered skills`);
|
|
836
|
-
console.log(` ${color.cyan('/memory')} — Show memory stats`);
|
|
837
|
-
console.log(` ${color.cyan('/info')} — Show agent info\n`);
|
|
838
|
-
return true;
|
|
839
|
-
}
|
|
840
|
-
if (lower === '/clear') {
|
|
841
|
-
history.length = 0;
|
|
842
|
-
console.log(`\n ${icon.success} Conversation history cleared.\n`);
|
|
843
|
-
return true;
|
|
844
|
-
}
|
|
845
|
-
if (lower === '/skills') {
|
|
846
|
-
if (skillNames.length === 0) {
|
|
847
|
-
console.log(`\n ${icon.info} No skills registered.\n`);
|
|
848
|
-
} else {
|
|
849
|
-
console.log(`\n ${color.bold('Registered skills:')}`);
|
|
850
|
-
skillNames.forEach((s) => console.log(` • ${color.cyan(s)}`));
|
|
851
|
-
console.log();
|
|
852
|
-
}
|
|
853
|
-
return true;
|
|
854
|
-
}
|
|
855
|
-
if (lower === '/memory') {
|
|
856
|
-
console.log(`\n ${color.bold('Memory stats:')}`);
|
|
857
|
-
console.log(` Messages in history: ${color.cyan(String(history.length))}`);
|
|
858
|
-
console.log(` Characters: ${color.cyan(String(history.reduce((a, m) => a + m.content.length, 0)))}\n`);
|
|
859
|
-
return true;
|
|
860
|
-
}
|
|
861
|
-
if (lower === '/info') {
|
|
862
|
-
console.log(`\n ${color.bold('Agent Info:')}`);
|
|
863
|
-
console.log(` Name: ${color.cyan(agentName)}`);
|
|
864
|
-
console.log(` Version: ${color.cyan(agentVersion)}`);
|
|
865
|
-
console.log(` Provider: ${color.cyan(providerName)}`);
|
|
866
|
-
console.log(` Model: ${color.cyan(model ?? 'default')}`);
|
|
867
|
-
console.log(` Skills: ${color.cyan(String(skillNames.length))}\n`);
|
|
868
|
-
return true;
|
|
869
|
-
}
|
|
870
|
-
return false;
|
|
871
|
-
};
|
|
872
|
-
|
|
873
|
-
const ask = (): void => {
|
|
874
|
-
rl.question(color.cyan('You: '), async (input) => {
|
|
875
|
-
const text = input.trim();
|
|
876
|
-
if (!text) { ask(); return; }
|
|
877
|
-
|
|
878
|
-
// Handle slash commands
|
|
879
|
-
if (text.startsWith('/') && handleSlashCommand(text)) {
|
|
880
|
-
ask();
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
history.push({ role: 'user', content: text });
|
|
885
|
-
|
|
886
|
-
// Build messages for provider
|
|
887
|
-
const messages = history.map((m) => ({
|
|
888
|
-
id: 'x',
|
|
889
|
-
role: m.role as any,
|
|
890
|
-
content: m.content,
|
|
891
|
-
timestamp: Date.now(),
|
|
892
|
-
}));
|
|
893
|
-
|
|
894
|
-
process.stdout.write(color.green('Agent: '));
|
|
895
|
-
let full = '';
|
|
896
|
-
try {
|
|
897
|
-
for await (const chunk of provider.chatStream(messages, systemPrompt)) {
|
|
898
|
-
process.stdout.write(chunk);
|
|
899
|
-
full += chunk;
|
|
900
|
-
}
|
|
901
|
-
} catch (err) {
|
|
902
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
903
|
-
process.stdout.write(color.red(`\n[Error: ${msg}]`));
|
|
904
|
-
full = `[Error: ${msg}]`;
|
|
905
|
-
}
|
|
906
|
-
console.log('\n');
|
|
907
|
-
|
|
908
|
-
history.push({ role: 'assistant', content: full });
|
|
909
|
-
|
|
910
|
-
// Trim history if too long (keep last 40 messages)
|
|
911
|
-
if (history.length > 40) {
|
|
912
|
-
history.splice(0, history.length - 40);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
ask();
|
|
916
|
-
});
|
|
917
|
-
};
|
|
918
|
-
|
|
919
|
-
rl.on('close', () => {
|
|
920
|
-
console.log(`\n${color.dim('Goodbye! 👋')}`);
|
|
921
|
-
process.exit(0);
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
ask();
|
|
925
|
-
});
|
|
926
|
-
|
|
927
|
-
// ── Run command ──────────────────────────────────────────────
|
|
928
|
-
|
|
929
|
-
program
|
|
930
|
-
.command('run')
|
|
931
|
-
.description('Start agent with web server')
|
|
932
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
933
|
-
.option('-p, --port <port>', 'Port override')
|
|
934
|
-
.action(async (opts: { file: string; port?: string }) => {
|
|
935
|
-
loadDotEnv();
|
|
936
|
-
|
|
937
|
-
const runtime = new AgentRuntime();
|
|
938
|
-
await runtime.loadConfig(opts.file);
|
|
939
|
-
await runtime.initialize();
|
|
940
|
-
await runtime.start();
|
|
941
|
-
const agent = runtime.getAgent();
|
|
942
|
-
|
|
943
|
-
// Auto-start Studio on port 4000
|
|
944
|
-
let studioUrl = '';
|
|
945
|
-
try {
|
|
946
|
-
const { StudioServer } = require('./studio/server');
|
|
947
|
-
const studioPort = parseInt(opts.port || '3000') === 4000 ? 4001 : 4000;
|
|
948
|
-
const studio = new StudioServer({ port: studioPort, agentDir: process.cwd() });
|
|
949
|
-
await studio.start();
|
|
950
|
-
studioUrl = `http://localhost:${studioPort}`;
|
|
951
|
-
} catch {}
|
|
952
|
-
|
|
953
|
-
console.log(`\n${icon.rocket} Agent "${color.bold(agent?.name ?? 'unknown')}" is running.`);
|
|
954
|
-
console.log(` ${color.dim('Chat:')} http://localhost:3000`);
|
|
955
|
-
if (studioUrl) console.log(` ${color.dim('Studio:')} ${studioUrl}`);
|
|
956
|
-
console.log(` ${color.dim('API:')} POST http://localhost:3000/api/chat`);
|
|
957
|
-
console.log(`\n ${color.dim('Press Ctrl+C to stop.')}\n`);
|
|
958
|
-
});
|
|
959
|
-
|
|
960
|
-
// ── Serve command (OpenAI-compatible API) ────────────────────
|
|
961
|
-
|
|
962
|
-
program
|
|
963
|
-
.command('serve')
|
|
964
|
-
.description('Start OpenAI-compatible API server')
|
|
965
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
966
|
-
.option('-p, --port <port>', 'Port', '8080')
|
|
967
|
-
.option('-H, --host <host>', 'Host', '0.0.0.0')
|
|
968
|
-
.option('-k, --api-key <key>', 'API key for auth')
|
|
969
|
-
.action(async (opts: { file: string; port: string; host: string; apiKey?: string }) => {
|
|
970
|
-
loadDotEnv();
|
|
971
|
-
const { APIServer } = require('./core/api-server');
|
|
972
|
-
const runtime = new AgentRuntime();
|
|
973
|
-
await runtime.loadConfig(opts.file);
|
|
974
|
-
await runtime.initialize();
|
|
975
|
-
await runtime.start();
|
|
976
|
-
const agent = runtime.getAgent();
|
|
977
|
-
|
|
978
|
-
const server = new APIServer({
|
|
979
|
-
port: parseInt(opts.port) || 8080,
|
|
980
|
-
host: opts.host,
|
|
981
|
-
apiKey: opts.apiKey ?? process.env.OPC_API_KEY,
|
|
982
|
-
agent,
|
|
983
|
-
});
|
|
984
|
-
await server.start();
|
|
985
|
-
|
|
986
|
-
const name = agent?.name ?? 'unknown';
|
|
987
|
-
console.log(`\n${icon.rocket} OpenAI-compatible API server running`);
|
|
988
|
-
console.log(` Agent: ${color.bold(name)}`);
|
|
989
|
-
console.log(` URL: ${color.cyan(`http://${opts.host}:${opts.port}`)}`);
|
|
990
|
-
console.log(` Auth: ${opts.apiKey ? color.green('enabled') : color.yellow('disabled')}`);
|
|
991
|
-
console.log(`\n Endpoints:`);
|
|
992
|
-
console.log(` POST /v1/chat/completions`);
|
|
993
|
-
console.log(` GET /v1/models`);
|
|
994
|
-
console.log(` POST /v1/embeddings`);
|
|
995
|
-
console.log(` GET /health`);
|
|
996
|
-
console.log(` GET /v1/agent/status`);
|
|
997
|
-
console.log(`\n ${color.dim('Press Ctrl+C to stop.')}\n`);
|
|
998
|
-
});
|
|
999
|
-
|
|
1000
|
-
// ── Info command ─────────────────────────────────────────────
|
|
1001
|
-
|
|
1002
|
-
program
|
|
1003
|
-
.command('info')
|
|
1004
|
-
.description('Show agent info from OAD')
|
|
1005
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1006
|
-
.action(async (opts: { file: string }) => {
|
|
1007
|
-
try {
|
|
1008
|
-
const runtime = new AgentRuntime();
|
|
1009
|
-
const config = await runtime.loadConfig(opts.file);
|
|
1010
|
-
const m = config.metadata;
|
|
1011
|
-
const s = config.spec;
|
|
1012
|
-
|
|
1013
|
-
console.log(`\n${icon.gear} ${color.bold('Agent Info')}\n`);
|
|
1014
|
-
console.log(` Name: ${color.cyan(m.name)}`);
|
|
1015
|
-
console.log(` Version: ${m.version}`);
|
|
1016
|
-
console.log(` Description: ${m.description ?? color.dim('(none)')}`);
|
|
1017
|
-
console.log(` Model: ${s.model}`);
|
|
1018
|
-
console.log(` Channels: ${s.channels.map((c: any) => c.type).join(', ') || color.dim('(none)')}`);
|
|
1019
|
-
console.log(` Skills: ${s.skills.map((sk: any) => sk.name).join(', ') || color.dim('(none)')}`);
|
|
1020
|
-
|
|
1021
|
-
// Memory info
|
|
1022
|
-
const memCfg = s.memory;
|
|
1023
|
-
const shortTermStatus = memCfg?.shortTerm !== false ? '✅' : '❌';
|
|
1024
|
-
console.log(`\n ${color.bold('Memory:')}`);
|
|
1025
|
-
console.log(` Short-term: ${shortTermStatus} InMemoryStore`);
|
|
1026
|
-
if (memCfg && typeof memCfg.longTerm === 'object' && memCfg.longTerm.provider === 'deepbrain') {
|
|
1027
|
-
const ltCfg = memCfg.longTerm.config as any ?? {};
|
|
1028
|
-
const dbPath = ltCfg.database || './data/brain.db';
|
|
1029
|
-
const autoLearn = ltCfg.autoLearn !== false ? '✅' : '❌';
|
|
1030
|
-
const autoRecall = ltCfg.autoRecall !== false ? '✅' : '❌';
|
|
1031
|
-
const evolveInterval = ltCfg.evolveInterval;
|
|
1032
|
-
console.log(` Long-term: ✅ DeepBrain (${dbPath})`);
|
|
1033
|
-
console.log(` Auto-learn: ${autoLearn}`);
|
|
1034
|
-
console.log(` Auto-recall: ${autoRecall}`);
|
|
1035
|
-
if (evolveInterval && evolveInterval > 0) {
|
|
1036
|
-
const hours = Math.floor(evolveInterval / 3600000);
|
|
1037
|
-
const mins = Math.floor((evolveInterval % 3600000) / 60000);
|
|
1038
|
-
const label = hours > 0 ? `every ${hours}h${mins > 0 ? ` ${mins}m` : ''}` : `every ${mins}m`;
|
|
1039
|
-
console.log(` Auto-evolve: ${label}`);
|
|
1040
|
-
} else {
|
|
1041
|
-
console.log(` Auto-evolve: ❌ disabled`);
|
|
1042
|
-
}
|
|
1043
|
-
} else {
|
|
1044
|
-
console.log(` Long-term: ❌ disabled`);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
console.log();
|
|
1048
|
-
} catch (err) {
|
|
1049
|
-
console.error(`${icon.error} Failed to read OAD:`, err instanceof Error ? err.message : err);
|
|
1050
|
-
process.exit(1);
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
// ── Build command ────────────────────────────────────────────
|
|
1055
|
-
|
|
1056
|
-
program
|
|
1057
|
-
.command('build')
|
|
1058
|
-
.description('Validate OAD and compile')
|
|
1059
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1060
|
-
.action(async (opts: { file: string }) => {
|
|
1061
|
-
try {
|
|
1062
|
-
const runtime = new AgentRuntime();
|
|
1063
|
-
const config = await runtime.loadConfig(opts.file);
|
|
1064
|
-
console.log(`${icon.success} Valid OAD: ${color.bold(config.metadata.name)} v${config.metadata.version}`);
|
|
1065
|
-
} catch (err) {
|
|
1066
|
-
console.error(`${icon.error} Invalid OAD:`, err instanceof Error ? err.message : err);
|
|
1067
|
-
process.exit(1);
|
|
1068
|
-
}
|
|
1069
|
-
});
|
|
1070
|
-
|
|
1071
|
-
// ── Create command ───────────────────────────────────────────
|
|
1072
|
-
|
|
1073
|
-
program
|
|
1074
|
-
.command('create')
|
|
1075
|
-
.description('Create an agent from a template')
|
|
1076
|
-
.argument('<name>', 'Agent name')
|
|
1077
|
-
.option('-t, --template <template>', 'Template', 'customer-service')
|
|
1078
|
-
.action(async (name: string, opts: { template: string }) => {
|
|
1079
|
-
const factory = TEMPLATES[opts.template]?.factory ?? createCustomerServiceConfig;
|
|
1080
|
-
const config = factory();
|
|
1081
|
-
config.metadata.name = name;
|
|
1082
|
-
const outFile = `${name}-oad.yaml`;
|
|
1083
|
-
fs.writeFileSync(outFile, yaml.dump(config, { lineWidth: 120 }));
|
|
1084
|
-
console.log(`${icon.success} Created ${color.bold(outFile)}`);
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
// ── Test command ─────────────────────────────────────────────
|
|
1088
|
-
|
|
1089
|
-
program
|
|
1090
|
-
.command('test')
|
|
1091
|
-
.description('Run agent tests defined in OAD or tests.yaml')
|
|
1092
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1093
|
-
.option('--json', 'Output as JSON')
|
|
1094
|
-
.action(async (opts: { file: string; json?: boolean }) => {
|
|
1095
|
-
loadDotEnv();
|
|
1096
|
-
console.log(`\n${icon.gear} Running agent tests...\n`);
|
|
1097
|
-
try {
|
|
1098
|
-
const report = await runTests(opts.file);
|
|
1099
|
-
if (opts.json) {
|
|
1100
|
-
console.log(JSON.stringify(report, null, 2));
|
|
1101
|
-
} else {
|
|
1102
|
-
console.log(formatReport(report));
|
|
1103
|
-
}
|
|
1104
|
-
process.exit(report.failed > 0 ? 1 : 0);
|
|
1105
|
-
} catch (err) {
|
|
1106
|
-
console.error(`${icon.error} Test failed:`, err instanceof Error ? err.message : err);
|
|
1107
|
-
process.exit(1);
|
|
1108
|
-
}
|
|
1109
|
-
});
|
|
1110
|
-
|
|
1111
|
-
// ── Analytics command ────────────────────────────────────────
|
|
1112
|
-
|
|
1113
|
-
program
|
|
1114
|
-
.command('analytics')
|
|
1115
|
-
.description('Show agent analytics and usage stats')
|
|
1116
|
-
.option('--json', 'Output as JSON')
|
|
1117
|
-
.option('--clear', 'Clear analytics data')
|
|
1118
|
-
.action(async (opts: { json?: boolean; clear?: boolean }) => {
|
|
1119
|
-
const engine = new AnalyticsEngine('.');
|
|
1120
|
-
if (opts.clear) {
|
|
1121
|
-
engine.clear();
|
|
1122
|
-
console.log(`${icon.success} Analytics data cleared.`);
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
const stats = engine.getStats();
|
|
1126
|
-
if (opts.json) {
|
|
1127
|
-
console.log(JSON.stringify(stats, null, 2));
|
|
1128
|
-
} else {
|
|
1129
|
-
console.log(AnalyticsEngine.formatStats(stats));
|
|
1130
|
-
}
|
|
1131
|
-
});
|
|
1132
|
-
|
|
1133
|
-
// ── Dev command ──────────────────────────────────────────────
|
|
1134
|
-
|
|
1135
|
-
program
|
|
1136
|
-
.command('dev')
|
|
1137
|
-
.description('Hot-reload development mode')
|
|
1138
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1139
|
-
.action(async (opts: { file: string }) => {
|
|
1140
|
-
loadDotEnv();
|
|
1141
|
-
console.log(`\n${icon.gear} ${color.bold('Development mode')} - watching for changes...\n`);
|
|
1142
|
-
|
|
1143
|
-
let runtime: AgentRuntime | null = null;
|
|
1144
|
-
|
|
1145
|
-
const startAgent = async () => {
|
|
1146
|
-
try {
|
|
1147
|
-
if (runtime) await runtime.stop();
|
|
1148
|
-
runtime = new AgentRuntime();
|
|
1149
|
-
await runtime.loadConfig(opts.file);
|
|
1150
|
-
await runtime.initialize();
|
|
1151
|
-
await runtime.start();
|
|
1152
|
-
const agent = runtime.getAgent();
|
|
1153
|
-
console.log(`${icon.success} Agent "${color.bold(agent?.name ?? 'unknown')}" restarted.`);
|
|
1154
|
-
} catch (err) {
|
|
1155
|
-
console.error(`${icon.error} Failed to start:`, err instanceof Error ? err.message : err);
|
|
1156
|
-
}
|
|
1157
|
-
};
|
|
1158
|
-
|
|
1159
|
-
await startAgent();
|
|
1160
|
-
|
|
1161
|
-
const watchPaths = [opts.file, 'src'];
|
|
1162
|
-
for (const watchPath of watchPaths) {
|
|
1163
|
-
if (fs.existsSync(watchPath)) {
|
|
1164
|
-
const isDir = fs.statSync(watchPath).isDirectory();
|
|
1165
|
-
fs.watch(watchPath, { recursive: isDir }, async (_event, filename) => {
|
|
1166
|
-
console.log(`\n${icon.info} ${color.dim(`Change detected: ${filename}`)} - restarting...`);
|
|
1167
|
-
await startAgent();
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
process.on('SIGINT', async () => {
|
|
1173
|
-
console.log(`\n${color.dim('Shutting down dev mode...')}`);
|
|
1174
|
-
if (runtime) await runtime.stop();
|
|
1175
|
-
process.exit(0);
|
|
1176
|
-
});
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
// (publish command moved to marketplace section below)
|
|
1180
|
-
|
|
1181
|
-
// ── Deploy command ───────────────────────────────────────────
|
|
1182
|
-
|
|
1183
|
-
program
|
|
1184
|
-
.command('deploy')
|
|
1185
|
-
.description('Deploy agent to a target runtime')
|
|
1186
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1187
|
-
.option('-t, --target <target>', 'Deploy target', 'openclaw')
|
|
1188
|
-
.option('-o, --output <dir>', 'Output directory')
|
|
1189
|
-
.option('--install', 'Also register in OpenClaw config')
|
|
1190
|
-
.option('--docker', 'Generate Dockerfile + docker-compose.yml')
|
|
1191
|
-
.option('--railway', 'Deploy to Railway')
|
|
1192
|
-
.option('--fly', 'Deploy to Fly.io')
|
|
1193
|
-
.option('--local', 'Deploy locally via Docker Compose')
|
|
1194
|
-
.option('-p, --port <port>', 'Port number', '3000')
|
|
1195
|
-
.option('--replicas <n>', 'Number of replicas', '1')
|
|
1196
|
-
.action(async (opts: { file: string; target: string; output?: string; install?: boolean; docker?: boolean; railway?: boolean; fly?: boolean; local?: boolean; port: string; replicas: string }) => {
|
|
1197
|
-
const deployer = new AgentDeployer();
|
|
1198
|
-
const agentDir = path.resolve(opts.output || '.');
|
|
1199
|
-
|
|
1200
|
-
// New deploy modes
|
|
1201
|
-
if (opts.docker) {
|
|
1202
|
-
console.log(`\n${icon.rocket} ${color.bold('Generating Docker deployment files')}\n`);
|
|
1203
|
-
const result = await deployer.generateFiles(agentDir, { port: parseInt(opts.port), replicas: parseInt(opts.replicas) });
|
|
1204
|
-
console.log(`${icon.success} ${result.message}`);
|
|
1205
|
-
for (const f of (result.files || [])) console.log(` ${icon.file} ${f}`);
|
|
1206
|
-
console.log();
|
|
1207
|
-
return;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
if (opts.railway) {
|
|
1211
|
-
console.log(`\n${icon.rocket} ${color.bold('Deploying to Railway')}\n`);
|
|
1212
|
-
const result = await deployer.deployRailway(agentDir);
|
|
1213
|
-
if (result.success) {
|
|
1214
|
-
console.log(`${icon.success} ${result.message}`);
|
|
1215
|
-
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1216
|
-
} else {
|
|
1217
|
-
console.error(`${icon.error} ${result.message}`);
|
|
1218
|
-
process.exit(1);
|
|
1219
|
-
}
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
if (opts.fly) {
|
|
1224
|
-
console.log(`\n${icon.rocket} ${color.bold('Deploying to Fly.io')}\n`);
|
|
1225
|
-
const result = await deployer.deployFly(agentDir);
|
|
1226
|
-
if (result.success) {
|
|
1227
|
-
console.log(`${icon.success} ${result.message}`);
|
|
1228
|
-
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1229
|
-
} else {
|
|
1230
|
-
console.error(`${icon.error} ${result.message}`);
|
|
1231
|
-
process.exit(1);
|
|
1232
|
-
}
|
|
1233
|
-
return;
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (opts.local) {
|
|
1237
|
-
console.log(`\n${icon.rocket} ${color.bold('Deploying locally via Docker')}\n`);
|
|
1238
|
-
const result = await deployer.deployLocal(agentDir, { port: parseInt(opts.port), replicas: parseInt(opts.replicas) });
|
|
1239
|
-
if (result.success) {
|
|
1240
|
-
console.log(`${icon.success} ${result.message}`);
|
|
1241
|
-
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1242
|
-
} else {
|
|
1243
|
-
console.error(`${icon.error} ${result.message}`);
|
|
1244
|
-
process.exit(1);
|
|
1245
|
-
}
|
|
1246
|
-
return;
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// Legacy deploy modes
|
|
1250
|
-
if (opts.target !== 'openclaw' && opts.target !== 'hermes') {
|
|
1251
|
-
console.error(`${icon.error} Unknown target: ${color.bold(opts.target)}. Supported: openclaw, hermes, --docker, --railway, --fly, --local`);
|
|
1252
|
-
process.exit(1);
|
|
1253
|
-
}
|
|
1254
|
-
try {
|
|
1255
|
-
const runtime = new AgentRuntime();
|
|
1256
|
-
const config = await runtime.loadConfig(opts.file);
|
|
1257
|
-
|
|
1258
|
-
if (opts.target === 'hermes') {
|
|
1259
|
-
const agentId = config.metadata.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
1260
|
-
const outputDir = path.resolve(opts.output ?? `hermes-${agentId}`);
|
|
1261
|
-
console.log(`\n${icon.rocket} ${color.bold('Deploy to Hermes')}\n`);
|
|
1262
|
-
const result = deployToHermes({ oad: config, outputDir });
|
|
1263
|
-
console.log(`${icon.success} Generated ${result.files.length} files in ${color.bold(outputDir)}`);
|
|
1264
|
-
for (const f of result.files) console.log(` ${icon.file} ${f}`);
|
|
1265
|
-
console.log();
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
const agentId = config.metadata.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
1270
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
1271
|
-
const defaultOutput = path.join(homeDir, '.openclaw', 'agents', agentId, 'workspace');
|
|
1272
|
-
const outputDir = path.resolve(opts.output ?? defaultOutput);
|
|
1273
|
-
|
|
1274
|
-
console.log(`\n${icon.rocket} ${color.bold('Deploy to OpenClaw')}\n`);
|
|
1275
|
-
const result = deployToOpenClaw({ oad: config, outputDir, install: opts.install });
|
|
1276
|
-
console.log(`\n${icon.success} Generated ${result.files.length} files.`);
|
|
1277
|
-
if (result.installed) {
|
|
1278
|
-
console.log(`${icon.success} Registered in OpenClaw config.`);
|
|
1279
|
-
}
|
|
1280
|
-
console.log();
|
|
1281
|
-
} catch (err) {
|
|
1282
|
-
console.error(`${icon.error} Deploy failed:`, err instanceof Error ? err.message : err);
|
|
1283
|
-
process.exit(1);
|
|
1284
|
-
}
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
// ── Search command ───────────────────────────────────────────
|
|
1288
|
-
|
|
1289
|
-
program
|
|
1290
|
-
.command('search')
|
|
1291
|
-
.description('Search OPC Registry for agents and skills')
|
|
1292
|
-
.argument('<query>', 'Search query')
|
|
1293
|
-
.action(async (query: string) => {
|
|
1294
|
-
console.log(`\n${icon.search} Searching OPC Registry for "${color.bold(query)}"...`);
|
|
1295
|
-
console.log(`\n🚧 OPC Registry coming soon!`);
|
|
1296
|
-
console.log(` Available templates: ${Object.keys(TEMPLATES).map(t => color.cyan(t)).join(', ')}\n`);
|
|
1297
|
-
});
|
|
1298
|
-
|
|
1299
|
-
// ── Stats command ────────────────────────────────────────────
|
|
1300
|
-
|
|
1301
|
-
program
|
|
1302
|
-
.command('stats')
|
|
1303
|
-
.description('Show agent analytics')
|
|
1304
|
-
.action(() => {
|
|
1305
|
-
const analytics = new Analytics();
|
|
1306
|
-
const snap = analytics.getSnapshot();
|
|
1307
|
-
console.log(`\n${icon.gear} ${color.bold('Agent Analytics')}\n`);
|
|
1308
|
-
console.log(` Messages: ${snap.messagesProcessed} | Errors: ${snap.errorCount} | Uptime: ${Math.round(snap.uptime / 1000)}s\n`);
|
|
1309
|
-
});
|
|
1310
|
-
|
|
1311
|
-
// ── Tool commands ────────────────────────────────────────────
|
|
1312
|
-
|
|
1313
|
-
const toolCmd = program.command('tool').description('Manage MCP tools');
|
|
1314
|
-
toolCmd.command('list').description('List MCP tools').action(() => {
|
|
1315
|
-
console.log(`\n${icon.gear} No tools installed. Add with: ${color.cyan('opc tool add <name>')}\n`);
|
|
1316
|
-
});
|
|
1317
|
-
toolCmd.command('add').argument('<name>').action((name: string) => {
|
|
1318
|
-
console.log(`🚧 Tool registry coming soon! Would add: ${color.cyan(name)}\n`);
|
|
1319
|
-
});
|
|
1320
|
-
|
|
1321
|
-
// ── Workflow commands ────────────────────────────────────────
|
|
1322
|
-
|
|
1323
|
-
const workflowCmd = program.command('workflow').description('Manage workflows');
|
|
1324
|
-
workflowCmd
|
|
1325
|
-
.command('run')
|
|
1326
|
-
.argument('<name>')
|
|
1327
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1328
|
-
.action(async (name: string, opts: { file: string }) => {
|
|
1329
|
-
const runtime = new AgentRuntime();
|
|
1330
|
-
const config = await runtime.loadConfig(opts.file);
|
|
1331
|
-
const wf = (config.spec.workflows ?? []).find((w: any) => w.name === name);
|
|
1332
|
-
if (!wf) { console.error(`Workflow "${name}" not found.`); process.exit(1); }
|
|
1333
|
-
const engine = new WorkflowEngine();
|
|
1334
|
-
engine.registerWorkflow(wf as any);
|
|
1335
|
-
console.log(`${icon.success} Workflow "${name}" loaded.\n`);
|
|
1336
|
-
});
|
|
1337
|
-
|
|
1338
|
-
workflowCmd
|
|
1339
|
-
.command('list')
|
|
1340
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1341
|
-
.action(async (opts: { file: string }) => {
|
|
1342
|
-
const runtime = new AgentRuntime();
|
|
1343
|
-
const config = await runtime.loadConfig(opts.file);
|
|
1344
|
-
const wfs = config.spec.workflows ?? [];
|
|
1345
|
-
if (wfs.length === 0) { console.log('No workflows defined.'); return; }
|
|
1346
|
-
for (const wf of wfs) console.log(` ${color.cyan((wf as any).name)}`);
|
|
1347
|
-
});
|
|
1348
|
-
|
|
1349
|
-
// ── Version commands ─────────────────────────────────────────
|
|
1350
|
-
|
|
1351
|
-
const versionCmd = program.command('version-mgmt').description('Manage agent versions');
|
|
1352
|
-
versionCmd.command('list').action(() => {
|
|
1353
|
-
const vm = new VersionManager();
|
|
1354
|
-
const versions = vm.list();
|
|
1355
|
-
if (versions.length === 0) { console.log('No versions saved.'); return; }
|
|
1356
|
-
for (const v of versions) console.log(` ${color.cyan(v.version)} - ${new Date(v.timestamp).toISOString()}`);
|
|
1357
|
-
});
|
|
1358
|
-
versionCmd.command('rollback').argument('<version>').action((version: string) => {
|
|
1359
|
-
const vm = new VersionManager();
|
|
1360
|
-
const oad = vm.rollback(version);
|
|
1361
|
-
if (!oad) { console.error(`Version "${version}" not found.`); process.exit(1); }
|
|
1362
|
-
fs.writeFileSync('oad.yaml', oad);
|
|
1363
|
-
console.log(`${icon.success} Rolled back to ${version}.`);
|
|
1364
|
-
});
|
|
1365
|
-
|
|
1366
|
-
// ── Helpers ──────────────────────────────────────────────────
|
|
1367
|
-
|
|
1368
|
-
function loadDotEnv(): void {
|
|
1369
|
-
const envPath = path.resolve('.env');
|
|
1370
|
-
if (!fs.existsSync(envPath)) return;
|
|
1371
|
-
try {
|
|
1372
|
-
const content = fs.readFileSync(envPath, 'utf-8');
|
|
1373
|
-
for (const line of content.split('\n')) {
|
|
1374
|
-
const trimmed = line.trim();
|
|
1375
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
1376
|
-
const eqIdx = trimmed.indexOf('=');
|
|
1377
|
-
if (eqIdx === -1) continue;
|
|
1378
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
1379
|
-
const value = trimmed.slice(eqIdx + 1).trim();
|
|
1380
|
-
if (!process.env[key]) {
|
|
1381
|
-
process.env[key] = value;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
} catch {
|
|
1385
|
-
// ignore
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
// 📚 Knowledge Base commands ────────────────────────────────
|
|
1390
|
-
|
|
1391
|
-
const kbCmd = program.command('kb').description('Manage knowledge base');
|
|
1392
|
-
kbCmd
|
|
1393
|
-
.command('add')
|
|
1394
|
-
.argument('<file>', 'File to index')
|
|
1395
|
-
.action(async (file: string) => {
|
|
1396
|
-
try {
|
|
1397
|
-
const kb = new KnowledgeBase('.');
|
|
1398
|
-
const result = await kb.addFile(file);
|
|
1399
|
-
console.log(`${icon.success} Indexed ${color.bold(file)} → ${result.chunks} chunks`);
|
|
1400
|
-
} catch (err) {
|
|
1401
|
-
console.error(`${icon.error}`, err instanceof Error ? err.message : err);
|
|
1402
|
-
process.exit(1);
|
|
1403
|
-
}
|
|
1404
|
-
});
|
|
1405
|
-
|
|
1406
|
-
kbCmd
|
|
1407
|
-
.command('search')
|
|
1408
|
-
.argument('<query>', 'Search query')
|
|
1409
|
-
.option('-k, --top-k <n>', 'Number of results', '5')
|
|
1410
|
-
.action(async (query: string, opts: { topK: string }) => {
|
|
1411
|
-
const kb = new KnowledgeBase('.');
|
|
1412
|
-
const results = await kb.search(query, parseInt(opts.topK));
|
|
1413
|
-
if (results.length === 0) {
|
|
1414
|
-
console.log(`${icon.info} No results found.`);
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
console.log(`\n${icon.search} Results for "${color.bold(query)}":\n`);
|
|
1418
|
-
for (const r of results) {
|
|
1419
|
-
console.log(` ${color.cyan(`[${(r.score * 100).toFixed(0)}%]`)} ${color.dim(`(${r.source})`)}`);
|
|
1420
|
-
console.log(` ${r.content.slice(0, 200)}${r.content.length > 200 ? '...' : ''}\n`);
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
|
|
1424
|
-
kbCmd.command('stats').action(() => {
|
|
1425
|
-
const kb = new KnowledgeBase('.');
|
|
1426
|
-
const stats = kb.getStats();
|
|
1427
|
-
console.log(`\n${icon.gear} Knowledge Base Stats\n`);
|
|
1428
|
-
console.log(` Entries: ${stats.totalEntries}`);
|
|
1429
|
-
console.log(` Sources: ${stats.sources.join(', ') || '(none)'}`);
|
|
1430
|
-
console.log(` Updated: ${stats.updatedAt}\n`);
|
|
1431
|
-
});
|
|
1432
|
-
|
|
1433
|
-
kbCmd.command('clear').action(() => {
|
|
1434
|
-
const kb = new KnowledgeBase('.');
|
|
1435
|
-
kb.clear();
|
|
1436
|
-
console.log(`${icon.success} Knowledge base cleared.`);
|
|
1437
|
-
});
|
|
1438
|
-
|
|
1439
|
-
// 📦 Package commands ───────────────────────────────────
|
|
1440
|
-
|
|
1441
|
-
import { AgentPackager, AgentPublisher, AgentInstaller } from './publish';
|
|
1442
|
-
|
|
1443
|
-
program
|
|
1444
|
-
.command('publish')
|
|
1445
|
-
.description('Validate, pack, and publish agent package')
|
|
1446
|
-
.option('--dry-run', 'Show what would be published without actually publishing')
|
|
1447
|
-
.option('--tag <tag>', 'Publish tag (default: latest)', 'latest')
|
|
1448
|
-
.option('--access <access>', 'Package access level (public or private)', 'public')
|
|
1449
|
-
.option('--registry <url>', 'Registry URL')
|
|
1450
|
-
.action(async (opts: { dryRun?: boolean; tag: string; access: string; registry?: string }) => {
|
|
1451
|
-
const dir = process.cwd();
|
|
1452
|
-
const packager = new AgentPackager();
|
|
1453
|
-
const publisher = new AgentPublisher();
|
|
1454
|
-
|
|
1455
|
-
// Validate first
|
|
1456
|
-
console.log(`\n${icon.gear} Validating agent project...`);
|
|
1457
|
-
const validation = await packager.validate(dir);
|
|
1458
|
-
for (const w of validation.warnings) console.log(` ${icon.warn} ${color.yellow(w)}`);
|
|
1459
|
-
if (!validation.valid) {
|
|
1460
|
-
for (const e of validation.errors) console.log(` ${icon.error} ${color.red(e)}`);
|
|
1461
|
-
console.log(`\n${icon.error} Validation failed. Fix errors above.\n`);
|
|
1462
|
-
process.exit(1);
|
|
1463
|
-
}
|
|
1464
|
-
console.log(` ${icon.success} Validation passed.`);
|
|
1465
|
-
|
|
1466
|
-
// Pack
|
|
1467
|
-
console.log(`\n${icon.package} Packing agent...`);
|
|
1468
|
-
const { path: pkgPath, manifest } = await packager.pack(dir);
|
|
1469
|
-
console.log(` ${icon.success} Created ${color.bold(path.basename(pkgPath))} (${manifest.files.length} files)`);
|
|
1470
|
-
console.log(` ${color.dim('Checksum:')} ${manifest.checksum}`);
|
|
1471
|
-
|
|
1472
|
-
// Publish
|
|
1473
|
-
await publisher.publish(pkgPath, manifest, {
|
|
1474
|
-
dryRun: opts.dryRun,
|
|
1475
|
-
tag: opts.tag,
|
|
1476
|
-
access: opts.access as 'public' | 'private',
|
|
1477
|
-
registry: opts.registry,
|
|
1478
|
-
});
|
|
1479
|
-
});
|
|
1480
|
-
|
|
1481
|
-
program
|
|
1482
|
-
.command('pack')
|
|
1483
|
-
.description('Create .opc.tgz package without publishing')
|
|
1484
|
-
.option('--list', 'List files that would be included (do not create archive)')
|
|
1485
|
-
.action(async (opts: { list?: boolean }) => {
|
|
1486
|
-
const dir = process.cwd();
|
|
1487
|
-
const packager = new AgentPackager();
|
|
1488
|
-
|
|
1489
|
-
if (opts.list) {
|
|
1490
|
-
const files = await packager.listFiles(dir);
|
|
1491
|
-
console.log(`\n${icon.package} ${color.bold('Files to include')} (${files.length}):\n`);
|
|
1492
|
-
for (const f of files) console.log(` ${f}`);
|
|
1493
|
-
console.log();
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
// Validate
|
|
1498
|
-
const validation = await packager.validate(dir);
|
|
1499
|
-
for (const w of validation.warnings) console.log(` ${icon.warn} ${color.yellow(w)}`);
|
|
1500
|
-
if (!validation.valid) {
|
|
1501
|
-
for (const e of validation.errors) console.log(` ${icon.error} ${color.red(e)}`);
|
|
1502
|
-
process.exit(1);
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
console.log(`\n${icon.package} Packing agent...`);
|
|
1506
|
-
const { path: pkgPath, manifest } = await packager.pack(dir);
|
|
1507
|
-
console.log(` ${icon.success} Created ${color.bold(path.basename(pkgPath))}`);
|
|
1508
|
-
console.log(` Files: ${manifest.files.length}`);
|
|
1509
|
-
console.log(` Checksum: ${manifest.checksum}\n`);
|
|
1510
|
-
});
|
|
1511
|
-
|
|
1512
|
-
program
|
|
1513
|
-
.command('install')
|
|
1514
|
-
.description('Install agent from .opc.tgz package or npm')
|
|
1515
|
-
.argument('<source>', 'Package file path, URL, or npm package name')
|
|
1516
|
-
.option('-d, --dir <dir>', 'Install directory', '.')
|
|
1517
|
-
.action(async (source: string, opts: { dir: string }) => {
|
|
1518
|
-
const installer = new AgentInstaller();
|
|
1519
|
-
console.log(`\n${icon.package} Installing from ${color.bold(source)}...`);
|
|
1520
|
-
await installer.install(source, path.resolve(opts.dir));
|
|
1521
|
-
console.log();
|
|
1522
|
-
});
|
|
1523
|
-
|
|
1524
|
-
// 🔌 Plugin commands ────────────────────────────────────────
|
|
1525
|
-
|
|
1526
|
-
const pluginCmd = program.command('plugin').description('Manage plugins');
|
|
1527
|
-
pluginCmd.command('list')
|
|
1528
|
-
.description('List available built-in plugins')
|
|
1529
|
-
.action(() => {
|
|
1530
|
-
const builtIn = [
|
|
1531
|
-
{ name: 'logging', description: 'Logs all messages and responses' },
|
|
1532
|
-
{ name: 'analytics', description: 'Tracks message counts and error rates' },
|
|
1533
|
-
{ name: 'rate-limit', description: 'Per-user rate limiting' },
|
|
1534
|
-
];
|
|
1535
|
-
console.log(`\n${icon.gear} ${color.bold('Available Plugins')}\n`);
|
|
1536
|
-
for (const p of builtIn) {
|
|
1537
|
-
console.log(` ${color.cyan(p.name.padEnd(16))} ${p.description}`);
|
|
1538
|
-
}
|
|
1539
|
-
console.log(`\n Add to oad.yaml: ${color.dim('plugins: [{ name: "logging" }]')}\n`);
|
|
1540
|
-
});
|
|
1541
|
-
|
|
1542
|
-
pluginCmd.command('add')
|
|
1543
|
-
.argument('<name>', 'Plugin name')
|
|
1544
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1545
|
-
.description('Add a plugin to your agent configuration')
|
|
1546
|
-
.action((name: string, opts: { file: string }) => {
|
|
1547
|
-
const validPlugins = ['logging', 'analytics', 'rate-limit'];
|
|
1548
|
-
if (!validPlugins.includes(name)) {
|
|
1549
|
-
console.error(`${icon.error} Unknown plugin: ${color.bold(name)}. Available: ${validPlugins.join(', ')}`);
|
|
1550
|
-
process.exit(1);
|
|
1551
|
-
}
|
|
1552
|
-
try {
|
|
1553
|
-
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
1554
|
-
const config = yaml.load(raw) as any;
|
|
1555
|
-
if (!config.spec.plugins) config.spec.plugins = [];
|
|
1556
|
-
if (config.spec.plugins.some((p: any) => p.name === name)) {
|
|
1557
|
-
console.log(`${icon.info} Plugin "${name}" already in config.`);
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
config.spec.plugins.push({ name });
|
|
1561
|
-
fs.writeFileSync(opts.file, yaml.dump(config, { lineWidth: 120 }));
|
|
1562
|
-
console.log(`${icon.success} Added plugin "${color.cyan(name)}" to ${opts.file}`);
|
|
1563
|
-
} catch (err) {
|
|
1564
|
-
console.error(`${icon.error} Failed:`, err instanceof Error ? err.message : err);
|
|
1565
|
-
process.exit(1);
|
|
1566
|
-
}
|
|
1567
|
-
});
|
|
1568
|
-
|
|
1569
|
-
// 🔌 Protocol commands ───────────────────────────────────────
|
|
1570
|
-
|
|
1571
|
-
const protocolCmd = program.command('protocol').description('Manage agent protocols (A2A, AG-UI)');
|
|
1572
|
-
|
|
1573
|
-
protocolCmd.command('list')
|
|
1574
|
-
.description('List supported protocols and their status')
|
|
1575
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1576
|
-
.action((opts: { file: string }) => {
|
|
1577
|
-
let config: any = {};
|
|
1578
|
-
try { config = yaml.load(fs.readFileSync(opts.file, 'utf-8')) as any; } catch { /* no file */ }
|
|
1579
|
-
const protocols = config?.spec?.protocols || {};
|
|
1580
|
-
const items = [
|
|
1581
|
-
{ name: 'a2a', description: 'Agent-to-Agent protocol', enabled: !!protocols.a2a?.enabled, detail: protocols.a2a?.port ? `port ${protocols.a2a.port}` : '' },
|
|
1582
|
-
{ name: 'agui', description: 'AG-UI — Agent-User Interaction (SSE)', enabled: !!protocols.agui?.enabled, detail: protocols.agui?.path || '/agui' },
|
|
1583
|
-
];
|
|
1584
|
-
console.log(`\n${icon.gear} ${color.bold('Protocols')}\n`);
|
|
1585
|
-
for (const p of items) {
|
|
1586
|
-
const status = p.enabled ? color.green('enabled') : color.dim('disabled');
|
|
1587
|
-
console.log(` ${color.cyan(p.name.padEnd(10))} ${status.padEnd(20)} ${p.description} ${p.detail ? color.dim(`(${p.detail})`) : ''}`);
|
|
1588
|
-
}
|
|
1589
|
-
console.log();
|
|
1590
|
-
});
|
|
1591
|
-
|
|
1592
|
-
protocolCmd.command('enable')
|
|
1593
|
-
.argument('<name>', 'Protocol name (a2a, agui)')
|
|
1594
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1595
|
-
.description('Enable a protocol')
|
|
1596
|
-
.action((name: string, opts: { file: string }) => {
|
|
1597
|
-
const validProtocols = ['a2a', 'agui'];
|
|
1598
|
-
if (!validProtocols.includes(name)) {
|
|
1599
|
-
console.error(`${icon.error} Unknown protocol: ${color.bold(name)}. Available: ${validProtocols.join(', ')}`);
|
|
1600
|
-
process.exit(1);
|
|
1601
|
-
}
|
|
1602
|
-
try {
|
|
1603
|
-
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
1604
|
-
const config = yaml.load(raw) as any;
|
|
1605
|
-
if (!config.spec.protocols) config.spec.protocols = {};
|
|
1606
|
-
if (!config.spec.protocols[name]) config.spec.protocols[name] = {};
|
|
1607
|
-
config.spec.protocols[name].enabled = true;
|
|
1608
|
-
if (name === 'agui' && !config.spec.protocols[name].path) {
|
|
1609
|
-
config.spec.protocols[name].path = '/agui';
|
|
1610
|
-
}
|
|
1611
|
-
fs.writeFileSync(opts.file, yaml.dump(config, { lineWidth: 120 }));
|
|
1612
|
-
console.log(`${icon.success} Enabled protocol "${color.cyan(name)}" in ${opts.file}`);
|
|
1613
|
-
} catch (err) {
|
|
1614
|
-
console.error(`${icon.error} Failed:`, err instanceof Error ? err.message : err);
|
|
1615
|
-
process.exit(1);
|
|
1616
|
-
}
|
|
1617
|
-
});
|
|
1618
|
-
|
|
1619
|
-
protocolCmd.command('disable')
|
|
1620
|
-
.argument('<name>', 'Protocol name (a2a, agui)')
|
|
1621
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1622
|
-
.description('Disable a protocol')
|
|
1623
|
-
.action((name: string, opts: { file: string }) => {
|
|
1624
|
-
try {
|
|
1625
|
-
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
1626
|
-
const config = yaml.load(raw) as any;
|
|
1627
|
-
if (config?.spec?.protocols?.[name]) {
|
|
1628
|
-
config.spec.protocols[name].enabled = false;
|
|
1629
|
-
fs.writeFileSync(opts.file, yaml.dump(config, { lineWidth: 120 }));
|
|
1630
|
-
console.log(`${icon.success} Disabled protocol "${color.cyan(name)}" in ${opts.file}`);
|
|
1631
|
-
} else {
|
|
1632
|
-
console.log(`${icon.info} Protocol "${name}" was not configured.`);
|
|
1633
|
-
}
|
|
1634
|
-
} catch (err) {
|
|
1635
|
-
console.error(`${icon.error} Failed:`, err instanceof Error ? err.message : err);
|
|
1636
|
-
process.exit(1);
|
|
1637
|
-
}
|
|
1638
|
-
});
|
|
1639
|
-
|
|
1640
|
-
// 🔄 Migrate command ────────────────────────────────────────
|
|
1641
|
-
|
|
1642
|
-
program
|
|
1643
|
-
.command('migrate')
|
|
1644
|
-
.description('Migrate OAD to latest schema version')
|
|
1645
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
1646
|
-
.option('--dry-run', 'Show changes without writing')
|
|
1647
|
-
.action(async (opts: { file: string; dryRun?: boolean }) => {
|
|
1648
|
-
try {
|
|
1649
|
-
const raw = fs.readFileSync(opts.file, 'utf-8');
|
|
1650
|
-
const config = yaml.load(raw) as any;
|
|
1651
|
-
let changed = false;
|
|
1652
|
-
|
|
1653
|
-
// Migration: add apiVersion if missing
|
|
1654
|
-
if (!config.apiVersion) { config.apiVersion = 'opc/v1'; changed = true; }
|
|
1655
|
-
// Migration: add kind if missing
|
|
1656
|
-
if (!config.kind) { config.kind = 'Agent'; changed = true; }
|
|
1657
|
-
// Migration: ensure metadata.version
|
|
1658
|
-
if (!config.metadata?.version) {
|
|
1659
|
-
if (!config.metadata) config.metadata = {};
|
|
1660
|
-
config.metadata.version = '1.0.0';
|
|
1661
|
-
changed = true;
|
|
1662
|
-
}
|
|
1663
|
-
// Migration: ensure spec.channels is array
|
|
1664
|
-
if (config.spec?.channels && !Array.isArray(config.spec.channels)) {
|
|
1665
|
-
config.spec.channels = [config.spec.channels];
|
|
1666
|
-
changed = true;
|
|
1667
|
-
}
|
|
1668
|
-
// Migration: ensure spec.skills is array
|
|
1669
|
-
if (config.spec?.skills && !Array.isArray(config.spec.skills)) {
|
|
1670
|
-
config.spec.skills = [config.spec.skills];
|
|
1671
|
-
changed = true;
|
|
1672
|
-
}
|
|
1673
|
-
// Migration: old model format
|
|
1674
|
-
if (config.spec?.llm?.model && !config.spec?.model) {
|
|
1675
|
-
config.spec.model = config.spec.llm.model;
|
|
1676
|
-
delete config.spec.llm;
|
|
1677
|
-
changed = true;
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
if (!changed) {
|
|
1681
|
-
console.log(`${icon.success} OAD is already up to date.`);
|
|
1682
|
-
return;
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
if (opts.dryRun) {
|
|
1686
|
-
console.log(`\n${icon.info} Would migrate:\n`);
|
|
1687
|
-
console.log(yaml.dump(config, { lineWidth: 120 }));
|
|
1688
|
-
} else {
|
|
1689
|
-
// Backup
|
|
1690
|
-
fs.writeFileSync(opts.file + '.bak', raw);
|
|
1691
|
-
fs.writeFileSync(opts.file, yaml.dump(config, { lineWidth: 120 }));
|
|
1692
|
-
console.log(`${icon.success} Migrated ${color.bold(opts.file)} (backup: ${opts.file}.bak)`);
|
|
1693
|
-
}
|
|
1694
|
-
} catch (err) {
|
|
1695
|
-
console.error(`${icon.error} Migration failed:`, err instanceof Error ? err.message : err);
|
|
1696
|
-
process.exit(1);
|
|
1697
|
-
}
|
|
1698
|
-
});
|
|
1699
|
-
|
|
1700
|
-
// ── Brain command ────────────────────────────────────────────
|
|
1701
|
-
|
|
1702
|
-
const brainCmd = program
|
|
1703
|
-
.command('brain')
|
|
1704
|
-
.description('Manage agent brain (memory, seeds, evolve)');
|
|
1705
|
-
|
|
1706
|
-
brainCmd
|
|
1707
|
-
.command('status')
|
|
1708
|
-
.description('Show brain stats (pages, tiers, last evolve)')
|
|
1709
|
-
.option('--url <url>', 'DeepBrain server URL', 'http://localhost:3333')
|
|
1710
|
-
.action(async (opts: { url: string }) => {
|
|
1711
|
-
console.log(`\n${icon.gear} ${color.bold('DeepBrain Status')} — ${color.dim(opts.url)}\n`);
|
|
1712
|
-
try {
|
|
1713
|
-
const res = await fetch(`${opts.url}/api/stats`);
|
|
1714
|
-
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
1715
|
-
const stats = (await res.json()) as Record<string, any>;
|
|
1716
|
-
const rows: [string, string][] = [
|
|
1717
|
-
['Total Pages', String(stats.totalPages ?? stats.pages ?? '-')],
|
|
1718
|
-
['Total Chunks', String(stats.totalChunks ?? stats.chunks ?? '-')],
|
|
1719
|
-
['Memory Tiers', String(stats.memoryTiers ?? stats.tiers ?? '-')],
|
|
1720
|
-
['Index Size', stats.indexSize ?? '-'],
|
|
1721
|
-
['Last Updated', stats.lastUpdated ?? stats.updatedAt ?? '-'],
|
|
1722
|
-
];
|
|
1723
|
-
const maxKey = Math.max(...rows.map(([k]) => k.length));
|
|
1724
|
-
for (const [key, val] of rows) {
|
|
1725
|
-
console.log(` ${color.cyan(key.padEnd(maxKey))} ${val}`);
|
|
1726
|
-
}
|
|
1727
|
-
console.log();
|
|
1728
|
-
} catch (err) {
|
|
1729
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1730
|
-
if (msg.includes('ECONNREFUSED') || msg.includes('fetch failed')) {
|
|
1731
|
-
console.log(` ${icon.warn} Cannot connect to DeepBrain at ${opts.url}`);
|
|
1732
|
-
console.log(` ${color.dim('Is the server running? Start with: deepbrain serve')}\n`);
|
|
1733
|
-
} else {
|
|
1734
|
-
console.error(` ${icon.error} ${msg}\n`);
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
brainCmd
|
|
1740
|
-
.command('seed')
|
|
1741
|
-
.description('Import brain seed files into memory')
|
|
1742
|
-
.option('-f, --file <file>', 'OAD file', 'agent.yaml')
|
|
1743
|
-
.option('--status', 'Check if seeds have been imported')
|
|
1744
|
-
.option('--reset', 'Re-import seeds (clear marker and re-seed)')
|
|
1745
|
-
.action(async (opts: { file: string; status?: boolean; reset?: boolean }) => {
|
|
1746
|
-
const { BrainSeedLoader } = require('./memory/seed-loader');
|
|
1747
|
-
let config: any = {};
|
|
1748
|
-
try { config = yaml.load(fs.readFileSync(opts.file, 'utf-8')) as any; } catch { /* ignore */ }
|
|
1749
|
-
const brainConfig = config?.spec?.brain;
|
|
1750
|
-
if (!brainConfig?.seeds?.length) {
|
|
1751
|
-
console.log(`${icon.info} No brain seeds configured in ${opts.file}.`);
|
|
1752
|
-
console.log(` Add spec.brain.seeds to your agent.yaml.`);
|
|
1753
|
-
return;
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
const loader = new BrainSeedLoader(process.cwd(), {
|
|
1757
|
-
seeds: brainConfig.seeds,
|
|
1758
|
-
autoSeed: brainConfig.autoSeed !== false,
|
|
1759
|
-
});
|
|
1760
|
-
|
|
1761
|
-
if (opts.status) {
|
|
1762
|
-
const seeded = await loader.isSeeded();
|
|
1763
|
-
console.log(`\n Brain seed status: ${seeded ? color.green('seeded ✔') : color.yellow('not seeded')}`);
|
|
1764
|
-
console.log(` Seeds configured: ${brainConfig.seeds.map((s: string) => color.cyan(s)).join(', ')}\n`);
|
|
1765
|
-
return;
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
if (opts.reset) {
|
|
1769
|
-
const markerPath = path.resolve(process.cwd(), '.brain-seeded');
|
|
1770
|
-
if (fs.existsSync(markerPath)) {
|
|
1771
|
-
fs.unlinkSync(markerPath);
|
|
1772
|
-
console.log(` ${icon.success} Cleared seed marker.`);
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
if (await loader.isSeeded() && !opts.reset) {
|
|
1777
|
-
console.log(`${icon.info} Brain already seeded. Use --reset to re-import.`);
|
|
1778
|
-
return;
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
console.log(`\n${icon.gear} Importing brain seeds...\n`);
|
|
1782
|
-
// Use a simple mock brain that logs imports (real usage would connect to DeepBrain)
|
|
1783
|
-
const pages: string[] = [];
|
|
1784
|
-
const mockBrain = {
|
|
1785
|
-
learn: async (content: string, meta: any) => { pages.push(meta?.slug || 'unknown'); },
|
|
1786
|
-
};
|
|
1787
|
-
const result = await loader.seedBrain(mockBrain);
|
|
1788
|
-
await loader.markSeeded();
|
|
1789
|
-
|
|
1790
|
-
console.log(` ${icon.success} Imported ${color.bold(String(result.imported))} pages from ${brainConfig.seeds.length} seed files.`);
|
|
1791
|
-
for (const p of result.pages) {
|
|
1792
|
-
console.log(` ${color.dim('•')} ${p}`);
|
|
1793
|
-
}
|
|
1794
|
-
console.log();
|
|
1795
|
-
});
|
|
1796
|
-
|
|
1797
|
-
brainCmd
|
|
1798
|
-
.command('evolve')
|
|
1799
|
-
.description('Trigger manual knowledge evolution cycle')
|
|
1800
|
-
.option('--dry-run', 'Show what would be promoted without doing it')
|
|
1801
|
-
.action(async (opts: { dryRun?: boolean }) => {
|
|
1802
|
-
const { KnowledgeEvolver } = require('./memory/seed-loader');
|
|
1803
|
-
const evolver = new KnowledgeEvolver();
|
|
1804
|
-
console.log(`\n${icon.gear} ${color.bold('Knowledge Evolution')}\n`);
|
|
1805
|
-
console.log(` ${icon.info} Checking for promotion candidates...`);
|
|
1806
|
-
// Would connect to real brain in production
|
|
1807
|
-
const result = await evolver.checkPromotion(null);
|
|
1808
|
-
if (result.candidates.length === 0) {
|
|
1809
|
-
console.log(` ${icon.info} No knowledge ready for promotion yet.\n`);
|
|
1810
|
-
} else {
|
|
1811
|
-
for (const c of result.candidates) {
|
|
1812
|
-
console.log(` ${color.cyan(c.slug)} → ${c.fromTier} → ${c.toTier} (confidence: ${(c.confidence * 100).toFixed(0)}%)`);
|
|
1813
|
-
}
|
|
1814
|
-
if (opts.dryRun) {
|
|
1815
|
-
console.log(`\n ${icon.info} Dry run — no changes made.\n`);
|
|
1816
|
-
} else {
|
|
1817
|
-
console.log(`\n ${icon.success} Promoted ${result.promoted} knowledge entries.\n`);
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
});
|
|
1821
|
-
|
|
1822
|
-
// ── Logs command ─────────────────────────────────────────────
|
|
1823
|
-
|
|
1824
|
-
program
|
|
1825
|
-
.command('logs')
|
|
1826
|
-
.description('Show recent agent traces')
|
|
1827
|
-
.option('-n, --limit <n>', 'Number of spans to show', '20')
|
|
1828
|
-
.option('-f, --follow', 'Keep watching for new spans')
|
|
1829
|
-
.action(async (opts: { limit: string; follow?: boolean }) => {
|
|
1830
|
-
const { TraceCollector } = await import('./traces');
|
|
1831
|
-
const collector = new TraceCollector();
|
|
1832
|
-
const limit = parseInt(opts.limit) || 20;
|
|
1833
|
-
|
|
1834
|
-
const printSpans = (spans: readonly Span[]) => {
|
|
1835
|
-
const slice = spans.slice(-limit);
|
|
1836
|
-
if (slice.length === 0) {
|
|
1837
|
-
console.log(` ${icon.info} No traces yet. Interact with the agent to generate traces.`);
|
|
1838
|
-
return;
|
|
1839
|
-
}
|
|
1840
|
-
for (const span of slice) {
|
|
1841
|
-
const duration = span.endTime
|
|
1842
|
-
? `${span.endTime.getTime() - span.startTime.getTime()}ms`
|
|
1843
|
-
: 'ongoing';
|
|
1844
|
-
const statusIcon = span.status === 'ok' ? icon.success : span.status === 'error' ? icon.error : color.dim('○');
|
|
1845
|
-
const time = span.startTime.toLocaleTimeString();
|
|
1846
|
-
console.log(` ${statusIcon} ${color.dim(time)} ${color.bold(span.name)} ${color.dim(duration)}`);
|
|
1847
|
-
}
|
|
1848
|
-
};
|
|
1849
|
-
|
|
1850
|
-
console.log(`\n${icon.gear} ${color.bold('Agent Traces')}\n`);
|
|
1851
|
-
const spans = collector.getBufferedSpans();
|
|
1852
|
-
printSpans(spans);
|
|
1853
|
-
|
|
1854
|
-
if (opts.follow) {
|
|
1855
|
-
console.log(`\n ${color.dim('Watching for new traces... (Ctrl+C to stop)')}\n`);
|
|
1856
|
-
let lastCount = spans.length;
|
|
1857
|
-
const interval = setInterval(() => {
|
|
1858
|
-
const current = collector.getBufferedSpans();
|
|
1859
|
-
if (current.length > lastCount) {
|
|
1860
|
-
const newSpans = current.slice(lastCount);
|
|
1861
|
-
printSpans(newSpans);
|
|
1862
|
-
lastCount = current.length;
|
|
1863
|
-
}
|
|
1864
|
-
}, 1000);
|
|
1865
|
-
process.on('SIGINT', () => { clearInterval(interval); process.exit(0); });
|
|
1866
|
-
} else {
|
|
1867
|
-
console.log();
|
|
1868
|
-
}
|
|
1869
|
-
});
|
|
1870
|
-
|
|
1871
|
-
// ── Score command ────────────────────────────────────────────
|
|
1872
|
-
|
|
1873
|
-
program
|
|
1874
|
-
.command('score')
|
|
1875
|
-
.description('Show agent performance score')
|
|
1876
|
-
.action(async () => {
|
|
1877
|
-
console.log(`\n${icon.gear} ${color.bold('Agent Performance Score')}\n`);
|
|
1878
|
-
try {
|
|
1879
|
-
const engine = new AnalyticsEngine('.');
|
|
1880
|
-
const stats = engine.getStats();
|
|
1881
|
-
if (!stats || stats.totalMessages === 0) {
|
|
1882
|
-
console.log(` ${icon.info} No score data yet. Run the agent first.\n`);
|
|
1883
|
-
return;
|
|
1884
|
-
}
|
|
1885
|
-
const errorRate = stats.totalMessages > 0 ? (stats.totalErrors / stats.totalMessages) : 0;
|
|
1886
|
-
const rows: [string, string][] = [
|
|
1887
|
-
['Total Messages', String(stats.totalMessages)],
|
|
1888
|
-
['Total LLM Calls', String(stats.totalLLMCalls)],
|
|
1889
|
-
['Total Tool Uses', String(stats.totalToolUses)],
|
|
1890
|
-
['Avg Response Time', `${stats.avgResponseTimeMs}ms`],
|
|
1891
|
-
['Error Rate', `${(errorRate * 100).toFixed(1)}%`],
|
|
1892
|
-
['Token Usage', `${stats.totalTokens.total} tokens (in: ${stats.totalTokens.input}, out: ${stats.totalTokens.output})`],
|
|
1893
|
-
];
|
|
1894
|
-
const maxKey = Math.max(...rows.map(([k]) => k.length));
|
|
1895
|
-
for (const [key, val] of rows) {
|
|
1896
|
-
console.log(` ${color.cyan(key.padEnd(maxKey))} ${val}`);
|
|
1897
|
-
}
|
|
1898
|
-
console.log();
|
|
1899
|
-
} catch {
|
|
1900
|
-
console.log(` ${icon.info} No score data yet. Run the agent first.\n`);
|
|
1901
|
-
}
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
// ── Daemon commands (start/stop/status) ─────────────────────
|
|
1905
|
-
|
|
1906
|
-
const OPC_DIR = path.resolve('.opc');
|
|
1907
|
-
|
|
1908
|
-
program
|
|
1909
|
-
.command('start')
|
|
1910
|
-
.description('Start agent as a background daemon')
|
|
1911
|
-
.option('-f, --file <file>', 'OAD file (agent.yaml or oad.yaml)')
|
|
1912
|
-
.action(async () => {
|
|
1913
|
-
if (!fs.existsSync(OPC_DIR)) fs.mkdirSync(OPC_DIR, { recursive: true });
|
|
1914
|
-
const pidFile = path.join(OPC_DIR, 'agent.pid');
|
|
1915
|
-
|
|
1916
|
-
// Check if already running
|
|
1917
|
-
if (fs.existsSync(pidFile)) {
|
|
1918
|
-
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
1919
|
-
try { process.kill(pid, 0); console.log(`${icon.warn} Agent already running (PID ${pid}).`); return; } catch { /* stale */ }
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
// Find daemon entry point
|
|
1923
|
-
const daemonScript = path.join(__dirname, 'daemon.js');
|
|
1924
|
-
if (!fs.existsSync(daemonScript)) {
|
|
1925
|
-
console.error(`${icon.error} Daemon script not found. Run ${color.cyan('npm run build')} first.`);
|
|
1926
|
-
process.exit(1);
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
const logFile = path.join(OPC_DIR, 'agent.log');
|
|
1930
|
-
const out = fs.openSync(logFile, 'a');
|
|
1931
|
-
const err = fs.openSync(logFile, 'a');
|
|
1932
|
-
|
|
1933
|
-
const child = spawn(process.execPath, [daemonScript], {
|
|
1934
|
-
detached: true,
|
|
1935
|
-
stdio: ['ignore', out, err],
|
|
1936
|
-
cwd: process.cwd(),
|
|
1937
|
-
env: process.env,
|
|
1938
|
-
});
|
|
1939
|
-
|
|
1940
|
-
child.unref();
|
|
1941
|
-
|
|
1942
|
-
// Wait briefly for PID file
|
|
1943
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
1944
|
-
|
|
1945
|
-
if (fs.existsSync(pidFile)) {
|
|
1946
|
-
const pid = fs.readFileSync(pidFile, 'utf-8').trim();
|
|
1947
|
-
console.log(`${icon.success} Agent started (PID ${pid})`);
|
|
1948
|
-
console.log(` ${color.dim('Logs:')} ${logFile}`);
|
|
1949
|
-
console.log(` ${color.dim('Stop:')} opc stop`);
|
|
1950
|
-
} else {
|
|
1951
|
-
console.log(`${icon.success} Agent starting... (PID ${child.pid})`);
|
|
1952
|
-
console.log(` ${color.dim('Logs:')} ${logFile}`);
|
|
1953
|
-
}
|
|
1954
|
-
});
|
|
1955
|
-
|
|
1956
|
-
program
|
|
1957
|
-
.command('stop')
|
|
1958
|
-
.description('Stop the background daemon')
|
|
1959
|
-
.action(() => {
|
|
1960
|
-
const pidFile = path.join(OPC_DIR, 'agent.pid');
|
|
1961
|
-
if (!fs.existsSync(pidFile)) {
|
|
1962
|
-
console.log(`${icon.info} No running agent found.`);
|
|
1963
|
-
return;
|
|
1964
|
-
}
|
|
1965
|
-
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
1966
|
-
try {
|
|
1967
|
-
// On Windows, process.kill with SIGTERM may not work; use taskkill
|
|
1968
|
-
if (process.platform === 'win32') {
|
|
1969
|
-
const { execSync } = require('child_process');
|
|
1970
|
-
try { execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' }); } catch { /* ignore */ }
|
|
1971
|
-
} else {
|
|
1972
|
-
process.kill(pid, 'SIGTERM');
|
|
1973
|
-
}
|
|
1974
|
-
console.log(`${icon.success} Sent stop signal to PID ${pid}`);
|
|
1975
|
-
} catch {
|
|
1976
|
-
console.log(`${icon.warn} Process ${pid} not found (may have already stopped).`);
|
|
1977
|
-
}
|
|
1978
|
-
try { fs.unlinkSync(pidFile); } catch { /* ignore */ }
|
|
1979
|
-
});
|
|
1980
|
-
|
|
1981
|
-
program
|
|
1982
|
-
.command('status')
|
|
1983
|
-
.description('Check daemon status')
|
|
1984
|
-
.action(() => {
|
|
1985
|
-
const pidFile = path.join(OPC_DIR, 'agent.pid');
|
|
1986
|
-
if (!fs.existsSync(pidFile)) {
|
|
1987
|
-
console.log(`\n Status: ${color.red('stopped')}\n`);
|
|
1988
|
-
return;
|
|
1989
|
-
}
|
|
1990
|
-
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
1991
|
-
let running = false;
|
|
1992
|
-
try { process.kill(pid, 0); running = true; } catch { /* not running */ }
|
|
1993
|
-
|
|
1994
|
-
if (!running) {
|
|
1995
|
-
console.log(`\n Status: ${color.red('stopped')} (stale PID file)`);
|
|
1996
|
-
try { fs.unlinkSync(pidFile); } catch { /* ignore */ }
|
|
1997
|
-
console.log();
|
|
1998
|
-
return;
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
// Uptime
|
|
2002
|
-
const startedFile = path.join(OPC_DIR, 'started');
|
|
2003
|
-
let uptime = '';
|
|
2004
|
-
if (fs.existsSync(startedFile)) {
|
|
2005
|
-
const startedMs = parseInt(fs.readFileSync(startedFile, 'utf-8').trim(), 10);
|
|
2006
|
-
const secs = Math.floor((Date.now() - startedMs) / 1000);
|
|
2007
|
-
const h = Math.floor(secs / 3600);
|
|
2008
|
-
const m = Math.floor((secs % 3600) / 60);
|
|
2009
|
-
const s = secs % 60;
|
|
2010
|
-
uptime = `${h}h ${m}m ${s}s`;
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
// Agent name from config
|
|
2014
|
-
let agentName = 'unknown';
|
|
2015
|
-
for (const f of ['agent.yaml', 'oad.yaml']) {
|
|
2016
|
-
if (fs.existsSync(f)) {
|
|
2017
|
-
try {
|
|
2018
|
-
const raw = fs.readFileSync(f, 'utf-8');
|
|
2019
|
-
const cfg = yaml.load(raw) as any;
|
|
2020
|
-
if (cfg?.metadata?.name) { agentName = cfg.metadata.name; break; }
|
|
2021
|
-
} catch { /* ignore */ }
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
console.log(`\n Status: ${color.green('running')}`);
|
|
2026
|
-
console.log(` PID: ${pid}`);
|
|
2027
|
-
console.log(` Agent: ${color.cyan(agentName)}`);
|
|
2028
|
-
if (uptime) console.log(` Uptime: ${uptime}`);
|
|
2029
|
-
console.log();
|
|
2030
|
-
});
|
|
2031
|
-
|
|
2032
|
-
// ── Jobs commands ────────────────────────────────────────────
|
|
2033
|
-
|
|
2034
|
-
const jobsCmd = program.command('jobs').description('Manage scheduled jobs');
|
|
2035
|
-
|
|
2036
|
-
jobsCmd
|
|
2037
|
-
.command('list', { isDefault: true })
|
|
2038
|
-
.description('List all scheduled jobs')
|
|
2039
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
2040
|
-
.action(async (opts: { file: string }) => {
|
|
2041
|
-
const jobs = loadJobsFromConfig(opts.file);
|
|
2042
|
-
if (jobs.length === 0) {
|
|
2043
|
-
console.log(`\n${icon.info} No scheduled jobs defined in config.\n`);
|
|
2044
|
-
return;
|
|
2045
|
-
}
|
|
2046
|
-
console.log(`\n${icon.gear} ${color.bold('Scheduled Jobs')}\n`);
|
|
2047
|
-
for (const job of jobs) {
|
|
2048
|
-
const status = job.enabled ? color.green('enabled') : color.dim('disabled');
|
|
2049
|
-
const next = job.nextRun ? job.nextRun.toLocaleString() : color.dim('N/A');
|
|
2050
|
-
console.log(` ${color.cyan(job.id.padEnd(20))} ${job.name}`);
|
|
2051
|
-
console.log(` ${''.padEnd(20)} Schedule: ${color.dim(job.schedule)} | Status: ${status} | Next: ${next}`);
|
|
2052
|
-
console.log();
|
|
2053
|
-
}
|
|
2054
|
-
});
|
|
2055
|
-
|
|
2056
|
-
jobsCmd
|
|
2057
|
-
.command('run')
|
|
2058
|
-
.argument('<id>', 'Job ID to run')
|
|
2059
|
-
.option('-f, --file <file>', 'OAD file', 'oad.yaml')
|
|
2060
|
-
.description('Manually trigger a scheduled job')
|
|
2061
|
-
.action(async (id: string, opts: { file: string }) => {
|
|
2062
|
-
const jobs = loadJobsFromConfig(opts.file);
|
|
2063
|
-
const job = jobs.find(j => j.id === id || j.name === id);
|
|
2064
|
-
if (!job) {
|
|
2065
|
-
console.error(`${icon.error} Job "${id}" not found. Available: ${jobs.map(j => j.id).join(', ')}`);
|
|
2066
|
-
process.exit(1);
|
|
2067
|
-
}
|
|
2068
|
-
console.log(`${icon.info} Running job "${color.bold(job.name)}"...`);
|
|
2069
|
-
console.log(` Task: ${color.dim(job.task)}`);
|
|
2070
|
-
console.log(`\n${icon.warn} Manual job execution requires a running daemon. Use ${color.cyan('opc start')} first.\n`);
|
|
2071
|
-
});
|
|
2072
|
-
|
|
2073
|
-
function loadJobsFromConfig(file: string): CronJob[] {
|
|
2074
|
-
try {
|
|
2075
|
-
const raw = fs.readFileSync(file, 'utf-8');
|
|
2076
|
-
const config = yaml.load(raw) as any;
|
|
2077
|
-
const jobConfigs = config?.spec?.scheduler?.jobs ?? [];
|
|
2078
|
-
const { parseCron } = require('./core/scheduler');
|
|
2079
|
-
return jobConfigs.map((j: any, i: number) => {
|
|
2080
|
-
const id = j.id || j.name?.toLowerCase().replace(/\s+/g, '-') || `job-${i}`;
|
|
2081
|
-
const parsed = parseCron(j.schedule);
|
|
2082
|
-
// Compute next run
|
|
2083
|
-
const now = new Date();
|
|
2084
|
-
let nextRun: Date | undefined;
|
|
2085
|
-
const d = new Date(now);
|
|
2086
|
-
d.setSeconds(0, 0);
|
|
2087
|
-
d.setMinutes(d.getMinutes() + 1);
|
|
2088
|
-
for (let k = 0; k < 48 * 60; k++) {
|
|
2089
|
-
const { cronMatches } = require('./core/scheduler');
|
|
2090
|
-
if (cronMatches(parsed, d)) { nextRun = new Date(d); break; }
|
|
2091
|
-
d.setMinutes(d.getMinutes() + 1);
|
|
2092
|
-
}
|
|
2093
|
-
return {
|
|
2094
|
-
id,
|
|
2095
|
-
name: j.name || id,
|
|
2096
|
-
schedule: j.schedule,
|
|
2097
|
-
task: j.task || '',
|
|
2098
|
-
enabled: j.enabled !== false,
|
|
2099
|
-
nextRun,
|
|
2100
|
-
} as CronJob;
|
|
2101
|
-
});
|
|
2102
|
-
} catch {
|
|
2103
|
-
return [];
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
// ── Skills commands ──────────────────────────────────────────
|
|
2108
|
-
|
|
2109
|
-
const skillsCmd = program.command('skills').description('Manage learned skills');
|
|
2110
|
-
|
|
2111
|
-
skillsCmd
|
|
2112
|
-
.command('list', { isDefault: true })
|
|
2113
|
-
.description('List all learned skills')
|
|
2114
|
-
.option('-d, --dir <dir>', 'Skills directory', '.opc/skills')
|
|
2115
|
-
.action(async (opts: { dir: string }) => {
|
|
2116
|
-
const { SkillLearner } = await import('./skills/auto-learn');
|
|
2117
|
-
const learner = new SkillLearner(opts.dir);
|
|
2118
|
-
const skills = await learner.loadLearnedSkills();
|
|
2119
|
-
if (skills.length === 0) {
|
|
2120
|
-
console.log(`\n${icon.info} No learned skills yet.\n`);
|
|
2121
|
-
console.log(` Skills are auto-created from conversations when learning is enabled.`);
|
|
2122
|
-
console.log(` Directory: ${color.dim(path.resolve(opts.dir))}\n`);
|
|
2123
|
-
return;
|
|
2124
|
-
}
|
|
2125
|
-
console.log(`\n${icon.gear} ${color.bold('Learned Skills')} (${skills.length})\n`);
|
|
2126
|
-
for (const skill of skills) {
|
|
2127
|
-
console.log(` ${color.cyan(skill.name.padEnd(24))} ${skill.description}`);
|
|
2128
|
-
console.log(` ${''.padEnd(24)} v${skill.version} | used ${skill.usageCount}x | trigger: ${color.dim(skill.trigger)}`);
|
|
2129
|
-
console.log();
|
|
2130
|
-
}
|
|
2131
|
-
});
|
|
2132
|
-
|
|
2133
|
-
skillsCmd
|
|
2134
|
-
.command('show')
|
|
2135
|
-
.argument('<name>', 'Skill name')
|
|
2136
|
-
.option('-d, --dir <dir>', 'Skills directory', '.opc/skills')
|
|
2137
|
-
.description('Show details of a learned skill')
|
|
2138
|
-
.action(async (name: string, opts: { dir: string }) => {
|
|
2139
|
-
const skillPath = path.join(opts.dir, `${name}.md`);
|
|
2140
|
-
if (!fs.existsSync(skillPath)) {
|
|
2141
|
-
console.error(`${icon.error} Skill "${name}" not found at ${skillPath}`);
|
|
2142
|
-
process.exit(1);
|
|
2143
|
-
}
|
|
2144
|
-
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
2145
|
-
console.log(`\n${content}`);
|
|
2146
|
-
});
|
|
2147
|
-
|
|
2148
|
-
skillsCmd
|
|
2149
|
-
.command('remove')
|
|
2150
|
-
.argument('<name>', 'Skill name')
|
|
2151
|
-
.option('-d, --dir <dir>', 'Skills directory', '.opc/skills')
|
|
2152
|
-
.description('Remove a learned skill')
|
|
2153
|
-
.action(async (name: string, opts: { dir: string }) => {
|
|
2154
|
-
const skillPath = path.join(opts.dir, `${name}.md`);
|
|
2155
|
-
if (!fs.existsSync(skillPath)) {
|
|
2156
|
-
console.error(`${icon.error} Skill "${name}" not found.`);
|
|
2157
|
-
process.exit(1);
|
|
2158
|
-
}
|
|
2159
|
-
fs.unlinkSync(skillPath);
|
|
2160
|
-
console.log(`${icon.success} Removed skill "${color.cyan(name)}".`);
|
|
2161
|
-
});
|
|
2162
|
-
|
|
2163
|
-
// ── Doctor command ───────────────────────────────────────────
|
|
2164
|
-
|
|
2165
|
-
program
|
|
2166
|
-
.command('studio')
|
|
2167
|
-
.description('Start OPC Studio web UI')
|
|
2168
|
-
.option('--port <port>', 'Port to listen on', '4000')
|
|
2169
|
-
.option('--no-open', 'Do not open browser automatically')
|
|
2170
|
-
.action(async (opts: any) => {
|
|
2171
|
-
const { StudioServer } = require('./studio/server');
|
|
2172
|
-
const net = require('net');
|
|
2173
|
-
const port = parseInt(opts.port, 10);
|
|
2174
|
-
|
|
2175
|
-
const checkPort = (p: number): Promise<boolean> => new Promise((resolve) => {
|
|
2176
|
-
const sock = new net.Socket();
|
|
2177
|
-
sock.setTimeout(400);
|
|
2178
|
-
sock.once('connect', () => { sock.destroy(); resolve(true); });
|
|
2179
|
-
sock.once('error', () => { sock.destroy(); resolve(false); });
|
|
2180
|
-
sock.once('timeout', () => { sock.destroy(); resolve(false); });
|
|
2181
|
-
sock.connect(p, 'localhost');
|
|
2182
|
-
});
|
|
2183
|
-
|
|
2184
|
-
const server = new StudioServer({ port, agentDir: process.cwd() });
|
|
2185
|
-
await server.start();
|
|
2186
|
-
|
|
2187
|
-
// Try to start sub-module UI servers with graceful fallback
|
|
2188
|
-
const subModules = [
|
|
2189
|
-
{ name: 'DeepBrain', icon: '🧠', pkg: 'deepbrain', port: 4001, serveMethod: 'serveUI' },
|
|
2190
|
-
{ name: 'AgentKits', icon: '📊', pkg: 'agent-kits', port: 4002, serveMethod: 'serveUI' },
|
|
2191
|
-
{ name: 'Workstation', icon: '👤', pkg: 'agent-workstation', port: 4003, serveMethod: 'serveUI' },
|
|
2192
|
-
];
|
|
2193
|
-
|
|
2194
|
-
const moduleStatuses: string[] = [];
|
|
2195
|
-
for (const mod of subModules) {
|
|
2196
|
-
try {
|
|
2197
|
-
const already = await checkPort(mod.port);
|
|
2198
|
-
if (already) {
|
|
2199
|
-
moduleStatuses.push(` ${icon.success} ${mod.icon} ${mod.name} already running on :${mod.port}`);
|
|
2200
|
-
continue;
|
|
2201
|
-
}
|
|
2202
|
-
const modExports = require(mod.pkg);
|
|
2203
|
-
if (typeof modExports[mod.serveMethod] === 'function') {
|
|
2204
|
-
modExports[mod.serveMethod]({ port: mod.port });
|
|
2205
|
-
await new Promise(r => setTimeout(r, 600));
|
|
2206
|
-
const started = await checkPort(mod.port);
|
|
2207
|
-
moduleStatuses.push(started
|
|
2208
|
-
? ` ${icon.success} ${mod.icon} ${mod.name} started on :${mod.port}`
|
|
2209
|
-
: ` ${icon.warn} ${mod.icon} ${mod.name} failed to start`);
|
|
2210
|
-
} else {
|
|
2211
|
-
moduleStatuses.push(` ${color.dim('○')} ${mod.icon} ${mod.name} no serve method`);
|
|
2212
|
-
}
|
|
2213
|
-
} catch {
|
|
2214
|
-
moduleStatuses.push(` ${color.dim('○')} ${mod.icon} ${mod.name} not installed`);
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
if (moduleStatuses.length > 0) {
|
|
2219
|
-
console.log('\nModules:');
|
|
2220
|
-
moduleStatuses.forEach(s => console.log(s));
|
|
2221
|
-
}
|
|
2222
|
-
|
|
2223
|
-
const url = `http://localhost:${port}`;
|
|
2224
|
-
console.log(`\n${icon.success} OPC Studio ready → ${color.cyan(url)}`);
|
|
2225
|
-
|
|
2226
|
-
if (opts.open !== false) {
|
|
2227
|
-
try {
|
|
2228
|
-
const { exec } = require('child_process');
|
|
2229
|
-
const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start ""' : 'xdg-open';
|
|
2230
|
-
exec(`${openCmd} ${url}`);
|
|
2231
|
-
} catch {}
|
|
2232
|
-
}
|
|
2233
|
-
|
|
2234
|
-
console.log(color.dim('Press Ctrl+C to stop'));
|
|
2235
|
-
});
|
|
2236
|
-
|
|
2237
|
-
program
|
|
2238
|
-
.command('doctor')
|
|
2239
|
-
.description('Check environment and diagnose common issues')
|
|
2240
|
-
.action(async () => {
|
|
2241
|
-
await runDoctor();
|
|
2242
|
-
});
|
|
2243
|
-
|
|
2244
|
-
// ─── Eval command ───────────────────────────────────────────────────────────
|
|
2245
|
-
import { AgentEvaluator } from './eval';
|
|
2246
|
-
|
|
2247
|
-
program
|
|
2248
|
-
.command('eval')
|
|
2249
|
-
.argument('[suite]', 'Built-in suite name (basic, safety, memory) or omit for all')
|
|
2250
|
-
.option('-f, --file <path>', 'Path to custom eval suite JSON file')
|
|
2251
|
-
.option('-o, --output <path>', 'Save report to JSON file')
|
|
2252
|
-
.option('-v, --verbose', 'Show per-case details')
|
|
2253
|
-
.description('Run agent evaluation suites')
|
|
2254
|
-
.action(async (suiteName: string | undefined, opts: { file?: string; output?: string; verbose?: boolean }) => {
|
|
2255
|
-
const suites: import('./eval').EvalSuite[] = [];
|
|
2256
|
-
|
|
2257
|
-
if (opts.file) {
|
|
2258
|
-
suites.push(AgentEvaluator.loadSuite(opts.file));
|
|
2259
|
-
} else if (suiteName) {
|
|
2260
|
-
suites.push(AgentEvaluator.loadBuiltinSuite(suiteName));
|
|
2261
|
-
} else {
|
|
2262
|
-
// All built-in suites
|
|
2263
|
-
for (const s of AgentEvaluator.builtinSuites()) {
|
|
2264
|
-
suites.push(AgentEvaluator.loadBuiltinSuite(s.name));
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
if (!suites.length) {
|
|
2269
|
-
console.log(`${icon.warn} No eval suites found.`);
|
|
2270
|
-
return;
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
// Create a minimal mock agent for eval (real usage would load from OAD)
|
|
2274
|
-
const oadPath = path.resolve('agent.yaml');
|
|
2275
|
-
let agent: any;
|
|
2276
|
-
if (fs.existsSync(oadPath)) {
|
|
2277
|
-
const runtime = new AgentRuntime();
|
|
2278
|
-
await runtime.loadConfig(oadPath);
|
|
2279
|
-
await runtime.start();
|
|
2280
|
-
agent = (runtime as any).agent;
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
if (!agent) {
|
|
2284
|
-
console.log(`${icon.warn} No agent.yaml found — running with dry-run mock agent.`);
|
|
2285
|
-
agent = { chat: async (input: string) => `[mock response to: ${input}]` };
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
const evaluator = new AgentEvaluator(agent);
|
|
2289
|
-
let allPassed = 0, allTotal = 0;
|
|
2290
|
-
|
|
2291
|
-
for (const suite of suites) {
|
|
2292
|
-
console.log(`\n${color.bold(`🧪 Suite: ${suite.name}`)} (${suite.cases.length} cases)`);
|
|
2293
|
-
const report = await evaluator.evalSuite(suite);
|
|
2294
|
-
allPassed += report.passed;
|
|
2295
|
-
allTotal += report.totalCases;
|
|
2296
|
-
|
|
2297
|
-
for (const r of report.results) {
|
|
2298
|
-
const status = r.passed ? color.green('PASS') : color.red('FAIL');
|
|
2299
|
-
console.log(` ${status} ${r.caseId}`);
|
|
2300
|
-
if (opts.verbose && !r.passed) {
|
|
2301
|
-
if (r.error) console.log(` ${color.dim('error: ' + r.error)}`);
|
|
2302
|
-
console.log(` ${color.dim('output: ' + r.output.slice(0, 120))}`);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
|
-
console.log(` ${color.dim(report.summary)}`);
|
|
2307
|
-
|
|
2308
|
-
if (opts.output) {
|
|
2309
|
-
const outPath = suites.length > 1
|
|
2310
|
-
? opts.output.replace('.json', `-${suite.name}.json`)
|
|
2311
|
-
: opts.output;
|
|
2312
|
-
AgentEvaluator.saveReport(report, outPath);
|
|
2313
|
-
console.log(` ${icon.success} Report saved to ${outPath}`);
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
console.log(`\n${color.bold('Summary:')} ${allPassed}/${allTotal} passed (${allTotal ? Math.round(allPassed / allTotal * 100) : 0}%)`);
|
|
2318
|
-
});
|
|
2319
|
-
|
|
2320
|
-
// ── Guardrails command ────────────────────────────────────────
|
|
2321
|
-
|
|
2322
|
-
const guardrailsCmd = program.command('guardrails').description('Guardrail utilities');
|
|
2323
|
-
|
|
2324
|
-
guardrailsCmd
|
|
2325
|
-
.command('test <message>')
|
|
2326
|
-
.description('Test guardrails against a message')
|
|
2327
|
-
.option('-c, --config <file>', 'OAD config file with guardrails')
|
|
2328
|
-
.action(async (message: string, opts: any) => {
|
|
2329
|
-
const { GuardrailManager, createGuardrailsFromConfig } = await import('./security/guardrails');
|
|
2330
|
-
|
|
2331
|
-
let manager: InstanceType<typeof GuardrailManager>;
|
|
2332
|
-
if (opts.config) {
|
|
2333
|
-
const raw = fs.readFileSync(opts.config, 'utf-8');
|
|
2334
|
-
const doc = yaml.load(raw) as any;
|
|
2335
|
-
manager = createGuardrailsFromConfig(doc.spec?.guardrails ?? {});
|
|
2336
|
-
} else {
|
|
2337
|
-
// Default: all built-in rules
|
|
2338
|
-
manager = new GuardrailManager({
|
|
2339
|
-
input: [
|
|
2340
|
-
{ name: 'pii-detector', type: 'regex', action: 'redact' },
|
|
2341
|
-
{ name: 'prompt-injection', type: 'keyword', action: 'block' },
|
|
2342
|
-
{ name: 'toxicity', type: 'keyword', action: 'block' },
|
|
2343
|
-
{ name: 'compliance-filter', type: 'keyword', action: 'block' },
|
|
2344
|
-
],
|
|
2345
|
-
output: [],
|
|
2346
|
-
});
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
console.log(color.bold('Testing guardrails against:'), message);
|
|
2350
|
-
console.log();
|
|
2351
|
-
|
|
2352
|
-
const result = await manager.checkInput(message);
|
|
2353
|
-
if (result.passed) {
|
|
2354
|
-
console.log(color.green('✓ PASSED — no violations'));
|
|
2355
|
-
} else {
|
|
2356
|
-
if (result.blocked) console.log(color.red('✗ BLOCKED'));
|
|
2357
|
-
if (result.warned) console.log(color.yellow('⚠ WARNING'));
|
|
2358
|
-
if (result.redacted) {
|
|
2359
|
-
console.log(color.yellow('✎ REDACTED'));
|
|
2360
|
-
console.log(' Redacted text:', result.redactedText);
|
|
2361
|
-
}
|
|
2362
|
-
for (const v of result.violations) {
|
|
2363
|
-
console.log(` [${v.action}] ${v.rule}: ${v.detail}`);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
});
|
|
2367
|
-
|
|
2368
|
-
// ── Voice command ─────────────────────────────────────────────
|
|
2369
|
-
|
|
2370
|
-
program
|
|
2371
|
-
.command('voice')
|
|
2372
|
-
.description('Voice conversation utilities')
|
|
2373
|
-
.command('start')
|
|
2374
|
-
.description('Start voice conversation (requires STT/TTS providers)')
|
|
2375
|
-
.option('--stt <provider>', 'STT provider: whisper, deepgram', 'whisper')
|
|
2376
|
-
.option('--tts <provider>', 'TTS provider: edge-tts, openai-tts, elevenlabs', 'edge-tts')
|
|
2377
|
-
.option('--voice <name>', 'Voice name/id')
|
|
2378
|
-
.option('--language <lang>', 'Language code', 'en')
|
|
2379
|
-
.action(async (opts: any) => {
|
|
2380
|
-
console.log(color.bold('🎤 Voice Conversation Mode'));
|
|
2381
|
-
console.log(` STT: ${opts.stt} | TTS: ${opts.tts} | Voice: ${opts.voice ?? 'default'} | Language: ${opts.language}`);
|
|
2382
|
-
console.log(color.dim(' (Voice conversation requires audio input integration — use as library)'));
|
|
2383
|
-
console.log();
|
|
2384
|
-
console.log('To use voice in your agent:');
|
|
2385
|
-
console.log(color.cyan(`
|
|
2386
|
-
import { VoiceChannel, createVoiceProviders } from 'opc-agent';
|
|
2387
|
-
|
|
2388
|
-
const { stt, tts } = createVoiceProviders({
|
|
2389
|
-
sttProvider: '${opts.stt}',
|
|
2390
|
-
ttsProvider: '${opts.tts}',
|
|
2391
|
-
voice: '${opts.voice ?? 'en-US-AriaNeural'}',
|
|
2392
|
-
language: '${opts.language}',
|
|
2393
|
-
});
|
|
2394
|
-
|
|
2395
|
-
const voice = new VoiceChannel({ sttProvider: stt, ttsProvider: tts });
|
|
2396
|
-
await voice.start();
|
|
2397
|
-
`));
|
|
2398
|
-
});
|
|
2399
|
-
|
|
2400
|
-
program.parse();
|
|
2401
|
-
|
|
2402
|
-
// ── Keys command ──────────────────────────────────────────────
|
|
2403
|
-
|
|
2404
|
-
import { KeyManager } from './security/keys';
|
|
2405
|
-
import { ApprovalManager } from './security/approval';
|
|
2406
|
-
|
|
2407
|
-
const keysCmd = program.command('keys').description('Manage API keys');
|
|
2408
|
-
|
|
2409
|
-
keysCmd
|
|
2410
|
-
.command('set')
|
|
2411
|
-
.argument('<name>', 'Key name')
|
|
2412
|
-
.description('Store an API key (encrypted)')
|
|
2413
|
-
.action(async (name: string) => {
|
|
2414
|
-
const value = await promptUser(`Enter value for ${color.bold(name)}`);
|
|
2415
|
-
if (!value) {
|
|
2416
|
-
console.log(`${icon.error} No value provided.`);
|
|
2417
|
-
return;
|
|
2418
|
-
}
|
|
2419
|
-
const km = new KeyManager();
|
|
2420
|
-
km.set(name, value);
|
|
2421
|
-
console.log(`${icon.success} Key ${color.bold(name)} saved.`);
|
|
2422
|
-
});
|
|
2423
|
-
|
|
2424
|
-
keysCmd
|
|
2425
|
-
.command('list')
|
|
2426
|
-
.description('List stored key names')
|
|
2427
|
-
.action(() => {
|
|
2428
|
-
const km = new KeyManager();
|
|
2429
|
-
const names = km.list();
|
|
2430
|
-
if (names.length === 0) {
|
|
2431
|
-
console.log(`${icon.info} No keys stored.`);
|
|
2432
|
-
return;
|
|
2433
|
-
}
|
|
2434
|
-
console.log(`\n${color.bold('Stored keys:')}`);
|
|
2435
|
-
names.forEach(n => console.log(` • ${n}`));
|
|
2436
|
-
});
|
|
2437
|
-
|
|
2438
|
-
keysCmd
|
|
2439
|
-
.command('delete')
|
|
2440
|
-
.argument('<name>', 'Key name')
|
|
2441
|
-
.description('Delete a stored key')
|
|
2442
|
-
.action((name: string) => {
|
|
2443
|
-
const km = new KeyManager();
|
|
2444
|
-
if (km.delete(name)) {
|
|
2445
|
-
console.log(`${icon.success} Key ${color.bold(name)} deleted.`);
|
|
2446
|
-
} else {
|
|
2447
|
-
console.log(`${icon.error} Key ${color.bold(name)} not found.`);
|
|
2448
|
-
}
|
|
2449
|
-
});
|
|
2450
|
-
|
|
2451
|
-
// ── Approve command ───────────────────────────────────────────
|
|
2452
|
-
|
|
2453
|
-
const approveCmd = program.command('approve').description('Manage command approvals');
|
|
2454
|
-
|
|
2455
|
-
// Singleton for CLI — in real usage this would be loaded from daemon state
|
|
2456
|
-
const approvalManager = new ApprovalManager();
|
|
2457
|
-
|
|
2458
|
-
approveCmd
|
|
2459
|
-
.command('list')
|
|
2460
|
-
.description('Show pending approval requests')
|
|
2461
|
-
.action(() => {
|
|
2462
|
-
const pending = approvalManager.getPending();
|
|
2463
|
-
if (pending.length === 0) {
|
|
2464
|
-
console.log(`${icon.info} No pending approvals.`);
|
|
2465
|
-
return;
|
|
2466
|
-
}
|
|
2467
|
-
console.log(`\n${color.bold('Pending approvals:')}`);
|
|
2468
|
-
pending.forEach(r => {
|
|
2469
|
-
console.log(` ${color.cyan(r.id.slice(0, 8))} [${r.type}] ${r.command}`);
|
|
2470
|
-
console.log(` ${color.dim(r.description)}`);
|
|
2471
|
-
});
|
|
2472
|
-
});
|
|
2473
|
-
|
|
2474
|
-
approveCmd
|
|
2475
|
-
.command('allow')
|
|
2476
|
-
.argument('<id>', 'Approval request ID (prefix match)')
|
|
2477
|
-
.description('Approve a pending request')
|
|
2478
|
-
.action((id: string) => {
|
|
2479
|
-
const pending = approvalManager.getPending();
|
|
2480
|
-
const match = pending.find(r => r.id.startsWith(id));
|
|
2481
|
-
if (!match) {
|
|
2482
|
-
console.log(`${icon.error} No pending request matching ${id}`);
|
|
2483
|
-
return;
|
|
2484
|
-
}
|
|
2485
|
-
approvalManager.approve(match.id, 'cli-user');
|
|
2486
|
-
console.log(`${icon.success} Approved: ${match.command}`);
|
|
2487
|
-
});
|
|
2488
|
-
|
|
2489
|
-
approveCmd
|
|
2490
|
-
.command('deny')
|
|
2491
|
-
.argument('<id>', 'Approval request ID (prefix match)')
|
|
2492
|
-
.description('Deny a pending request')
|
|
2493
|
-
.action((id: string) => {
|
|
2494
|
-
const pending = approvalManager.getPending();
|
|
2495
|
-
const match = pending.find(r => r.id.startsWith(id));
|
|
2496
|
-
if (!match) {
|
|
2497
|
-
console.log(`${icon.error} No pending request matching ${id}`);
|
|
2498
|
-
return;
|
|
2499
|
-
}
|
|
2500
|
-
approvalManager.deny(match.id, 'cli-user');
|
|
2501
|
-
console.log(`${icon.success} Denied: ${match.command}`);
|
|
2502
|
-
});
|
|
2503
|
-
|
|
2504
|
-
// ── Traces command ────────────────────────────────────────────
|
|
2505
|
-
|
|
2506
|
-
import { Tracer, FileExporter } from './telemetry';
|
|
2507
|
-
|
|
2508
|
-
program
|
|
2509
|
-
.command('traces')
|
|
2510
|
-
.option('-l, --limit <n>', 'Number of traces to show', '20')
|
|
2511
|
-
.option('-f, --file <path>', 'Read traces from file')
|
|
2512
|
-
.description('Show recent telemetry traces')
|
|
2513
|
-
.action(async (opts: { limit: string; file?: string }) => {
|
|
2514
|
-
const limit = parseInt(opts.limit) || 20;
|
|
2515
|
-
|
|
2516
|
-
if (opts.file) {
|
|
2517
|
-
// Read from NDJSON file
|
|
2518
|
-
const fs = require('fs');
|
|
2519
|
-
if (!fs.existsSync(opts.file)) {
|
|
2520
|
-
console.log(`${icon.error} File not found: ${opts.file}`);
|
|
2521
|
-
return;
|
|
2522
|
-
}
|
|
2523
|
-
const lines = fs.readFileSync(opts.file, 'utf-8').trim().split('\n');
|
|
2524
|
-
const spans = lines.slice(-limit).map((l: string) => {
|
|
2525
|
-
try { return JSON.parse(l); } catch { return null; }
|
|
2526
|
-
}).filter(Boolean);
|
|
2527
|
-
|
|
2528
|
-
printTraceTable(spans);
|
|
2529
|
-
} else {
|
|
2530
|
-
// Try to read from Studio API
|
|
2531
|
-
try {
|
|
2532
|
-
const oad = loadOADFile();
|
|
2533
|
-
const port = 4000; // default studio port
|
|
2534
|
-
const res = await fetch(`http://localhost:${port}/api/telemetry/traces?limit=${limit}`);
|
|
2535
|
-
const data = await res.json() as any;
|
|
2536
|
-
if (data.traces && data.traces.length > 0) {
|
|
2537
|
-
console.log(`\n${color.bold('Recent Traces')} (${data.traces.length})\n`);
|
|
2538
|
-
console.log(`${'Trace ID'.padEnd(12)} ${'Root Span'.padEnd(25)} ${'Time'.padEnd(22)} ${'Spans'.padEnd(7)} ${'Status'}`);
|
|
2539
|
-
console.log(`${'─'.repeat(12)} ${'─'.repeat(25)} ${'─'.repeat(22)} ${'─'.repeat(7)} ${'─'.repeat(8)}`);
|
|
2540
|
-
for (const t of data.traces) {
|
|
2541
|
-
const time = new Date(t.startTime).toISOString().slice(0, 19).replace('T', ' ');
|
|
2542
|
-
const statusColor = t.status === 'ok' ? color.green : t.status === 'error' ? color.red : color.dim;
|
|
2543
|
-
console.log(`${color.cyan(t.traceId.slice(0, 12))} ${t.rootSpan.padEnd(25).slice(0, 25)} ${time.padEnd(22)} ${String(t.spanCount).padEnd(7)} ${statusColor(t.status)}`);
|
|
2544
|
-
}
|
|
2545
|
-
} else {
|
|
2546
|
-
console.log(`${icon.info} No traces found. Enable telemetry in your OAD: spec.telemetry.enabled: true`);
|
|
2547
|
-
}
|
|
2548
|
-
} catch {
|
|
2549
|
-
console.log(`${icon.error} Could not connect to Studio. Is it running? (opc studio)`);
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
});
|
|
2553
|
-
|
|
2554
|
-
function printTraceTable(spans: any[]) {
|
|
2555
|
-
if (spans.length === 0) {
|
|
2556
|
-
console.log(`${icon.info} No traces found.`);
|
|
2557
|
-
return;
|
|
2558
|
-
}
|
|
2559
|
-
console.log(`\n${color.bold('Recent Spans')} (${spans.length})\n`);
|
|
2560
|
-
console.log(`${'Trace ID'.padEnd(12)} ${'Span'.padEnd(25)} ${'Duration'.padEnd(10)} ${'Status'}`);
|
|
2561
|
-
console.log(`${'─'.repeat(12)} ${'─'.repeat(25)} ${'─'.repeat(10)} ${'─'.repeat(8)}`);
|
|
2562
|
-
for (const s of spans) {
|
|
2563
|
-
const dur = s.endTime ? `${s.endTime - s.startTime}ms` : 'ongoing';
|
|
2564
|
-
const statusColor = s.status === 'ok' ? color.green : s.status === 'error' ? color.red : color.dim;
|
|
2565
|
-
console.log(`${color.cyan(s.traceId.slice(0, 12))} ${s.name.padEnd(25).slice(0, 25)} ${dur.padEnd(10)} ${statusColor(s.status)}`);
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
// ── A2A Protocol Commands ───────────────────────────────────
|
|
2570
|
-
const a2aCmd = program.command('a2a').description('Google A2A protocol commands');
|
|
2571
|
-
|
|
2572
|
-
a2aCmd
|
|
2573
|
-
.command('serve')
|
|
2574
|
-
.option('-p, --port <port>', 'Port for A2A server', '3001')
|
|
2575
|
-
.description('Start A2A server for this agent')
|
|
2576
|
-
.action(async (opts: { port: string }) => {
|
|
2577
|
-
const port = parseInt(opts.port) || 3001;
|
|
2578
|
-
const { A2AServer } = require('./protocols/a2a');
|
|
2579
|
-
const oad = loadOADFile();
|
|
2580
|
-
const server = new A2AServer(null, { oad, port });
|
|
2581
|
-
await server.start(port);
|
|
2582
|
-
console.log(`${icon.success} A2A server running on http://localhost:${port}`);
|
|
2583
|
-
console.log(`${icon.info} Agent card: http://localhost:${port}/.well-known/agent.json`);
|
|
2584
|
-
});
|
|
2585
|
-
|
|
2586
|
-
a2aCmd
|
|
2587
|
-
.command('card')
|
|
2588
|
-
.description('Print this agent\'s A2A card')
|
|
2589
|
-
.action(() => {
|
|
2590
|
-
const { oadToAgentCard } = require('./protocols/a2a');
|
|
2591
|
-
const oad = loadOADFile();
|
|
2592
|
-
if (!oad) { console.log(`${icon.error} No agent.yaml found`); return; }
|
|
2593
|
-
const card = oadToAgentCard(oad, 'http://localhost:3001');
|
|
2594
|
-
console.log(JSON.stringify(card, null, 2));
|
|
2595
|
-
});
|
|
2596
|
-
|
|
2597
|
-
a2aCmd
|
|
2598
|
-
.command('discover')
|
|
2599
|
-
.argument('<url>', 'Remote agent URL')
|
|
2600
|
-
.description('Fetch remote agent\'s A2A card')
|
|
2601
|
-
.action(async (url: string) => {
|
|
2602
|
-
const { A2AClient } = require('./protocols/a2a');
|
|
2603
|
-
const client = new A2AClient(url);
|
|
2604
|
-
try {
|
|
2605
|
-
const card = await client.getAgentCard();
|
|
2606
|
-
console.log(JSON.stringify(card, null, 2));
|
|
2607
|
-
} catch (err: any) {
|
|
2608
|
-
console.log(`${icon.error} Failed to discover agent: ${err.message}`);
|
|
2609
|
-
}
|
|
2610
|
-
});
|
|
2611
|
-
|
|
2612
|
-
a2aCmd
|
|
2613
|
-
.command('call')
|
|
2614
|
-
.argument('<url>', 'Remote agent URL')
|
|
2615
|
-
.argument('<message>', 'Message to send')
|
|
2616
|
-
.description('Call a remote A2A agent')
|
|
2617
|
-
.action(async (url: string, message: string) => {
|
|
2618
|
-
const { A2AClient } = require('./protocols/a2a');
|
|
2619
|
-
const client = new A2AClient(url);
|
|
2620
|
-
try {
|
|
2621
|
-
const response = await client.sendText(message);
|
|
2622
|
-
console.log(response);
|
|
2623
|
-
} catch (err: any) {
|
|
2624
|
-
console.log(`${icon.error} Call failed: ${err.message}`);
|
|
2625
|
-
}
|
|
2626
|
-
});
|
|
2627
|
-
|
|
2628
|
-
function loadOADFile(): any {
|
|
2629
|
-
const fs = require('fs');
|
|
2630
|
-
const yaml = require('js-yaml');
|
|
2631
|
-
for (const name of ['agent.yaml', 'agent.yml']) {
|
|
2632
|
-
if (fs.existsSync(name)) {
|
|
2633
|
-
return yaml.load(fs.readFileSync(name, 'utf-8'));
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
return null;
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
// ── MCP Server Commands ────────────────────────────────────
|
|
2640
|
-
const mcpCmd = program.command('mcp').description('MCP server commands — expose agent as MCP tools');
|
|
2641
|
-
|
|
2642
|
-
mcpCmd
|
|
2643
|
-
.command('serve')
|
|
2644
|
-
.option('--http <port>', 'Start HTTP+SSE mode on given port')
|
|
2645
|
-
.description('Start MCP server (stdio by default, --http for HTTP+SSE)')
|
|
2646
|
-
.action(async (opts: { http?: string }) => {
|
|
2647
|
-
const { MCPServer } = require('./protocols/mcp');
|
|
2648
|
-
const { agentToMCPTools, agentToMCPResources } = require('./protocols/mcp');
|
|
2649
|
-
const oad = loadOADFile();
|
|
2650
|
-
const agentName = oad?.metadata?.name || 'opc-agent';
|
|
2651
|
-
const server = new MCPServer({
|
|
2652
|
-
name: agentName,
|
|
2653
|
-
version: oad?.metadata?.version || '1.0.0',
|
|
2654
|
-
});
|
|
2655
|
-
// Register tools from OAD or defaults
|
|
2656
|
-
const { agentToMCPTools: toTools } = require('./protocols/mcp/agent-tools');
|
|
2657
|
-
const mockAgent = { name: agentName, config: { name: agentName } };
|
|
2658
|
-
const tools = toTools(mockAgent);
|
|
2659
|
-
for (const t of tools) server.addTool(t);
|
|
2660
|
-
|
|
2661
|
-
if (opts.http) {
|
|
2662
|
-
const port = parseInt(opts.http) || 3002;
|
|
2663
|
-
await server.serveHTTP(port);
|
|
2664
|
-
console.log(`${icon.success} MCP server (HTTP+SSE) running on http://localhost:${port}`);
|
|
2665
|
-
console.log(`${icon.info} SSE endpoint: http://localhost:${port}/sse`);
|
|
2666
|
-
console.log(`${icon.info} Message endpoint: http://localhost:${port}/message`);
|
|
2667
|
-
console.log(`${icon.info} Tools: ${server.getToolCount()}`);
|
|
2668
|
-
} else {
|
|
2669
|
-
console.error(`${icon.success} MCP server (stdio) started — ${server.getToolCount()} tools`);
|
|
2670
|
-
await server.serveStdio();
|
|
2671
|
-
}
|
|
2672
|
-
});
|
|
2673
|
-
|
|
2674
|
-
mcpCmd
|
|
2675
|
-
.command('tools')
|
|
2676
|
-
.description('List MCP tools that would be exposed')
|
|
2677
|
-
.action(() => {
|
|
2678
|
-
const { agentToMCPTools } = require('./protocols/mcp/agent-tools');
|
|
2679
|
-
const oad = loadOADFile();
|
|
2680
|
-
const agentName = oad?.metadata?.name || 'opc-agent';
|
|
2681
|
-
const tools = agentToMCPTools({ name: agentName });
|
|
2682
|
-
console.log(`\n${icon.gear} MCP Tools for ${color.cyan(agentName)}:\n`);
|
|
2683
|
-
for (const t of tools) {
|
|
2684
|
-
const required = t.inputSchema?.required?.join(', ') || 'none';
|
|
2685
|
-
console.log(` ${color.green(t.name.padEnd(20))} ${t.description}`);
|
|
2686
|
-
console.log(` ${' '.repeat(20)} Required: ${color.dim(required)}`);
|
|
2687
|
-
}
|
|
2688
|
-
console.log();
|
|
2689
|
-
});
|
|
2690
|
-
|
|
2691
|
-
mcpCmd
|
|
2692
|
-
.command('list')
|
|
2693
|
-
.description('List available pre-built MCP servers')
|
|
2694
|
-
.action(() => {
|
|
2695
|
-
const { listMCPServers } = require('./mcp/servers');
|
|
2696
|
-
const servers = listMCPServers();
|
|
2697
|
-
console.log(`\n${icon.gear} Available MCP Servers:\n`);
|
|
2698
|
-
for (const s of servers) {
|
|
2699
|
-
console.log(` ${color.green(s.name.padEnd(14))} ${s.description} ${color.dim(`(${s.toolCount} tools, v${s.version})`)}`);
|
|
2700
|
-
}
|
|
2701
|
-
console.log(`\n Total: ${servers.length} servers\n`);
|
|
2702
|
-
});
|
|
2703
|
-
|
|
2704
|
-
mcpCmd
|
|
2705
|
-
.command('start')
|
|
2706
|
-
.argument('<name>', 'Server name (e.g. filesystem, github, calculator)')
|
|
2707
|
-
.option('--port <port>', 'Start in HTTP+SSE mode on given port')
|
|
2708
|
-
.description('Start a pre-built MCP server (stdio by default)')
|
|
2709
|
-
.action(async (name: string, opts: { port?: string }) => {
|
|
2710
|
-
const { getMCPServer } = require('./mcp/servers');
|
|
2711
|
-
const { MCPServer } = require('./protocols/mcp');
|
|
2712
|
-
const config = getMCPServer(name);
|
|
2713
|
-
const server = new MCPServer(config);
|
|
2714
|
-
if (opts.port) {
|
|
2715
|
-
const port = parseInt(opts.port) || 3100;
|
|
2716
|
-
await server.serveHTTP(port);
|
|
2717
|
-
console.log(`${icon.success} MCP server ${color.cyan(name)} running on http://localhost:${port}`);
|
|
2718
|
-
console.log(`${icon.info} Tools: ${server.getToolCount()}`);
|
|
2719
|
-
} else {
|
|
2720
|
-
console.error(`${icon.success} MCP server ${color.cyan(name)} (stdio) — ${server.getToolCount()} tools`);
|
|
2721
|
-
await server.serveStdio();
|
|
2722
|
-
}
|
|
2723
|
-
});
|