opc-agent 4.1.1 → 4.1.3

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