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