agentengine-sdk-python 0.6.2__py3-none-any.whl → 0.6.9__py3-none-any.whl

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 (490) hide show
  1. agentengine_sdk_python-0.6.9.dist-info/METADATA +179 -0
  2. agentengine_sdk_python-0.6.9.dist-info/RECORD +487 -0
  3. {agentengine_sdk_python-0.6.2.dist-info → agentengine_sdk_python-0.6.9.dist-info}/WHEEL +1 -1
  4. agentengine_sdk_python-0.6.9.dist-info/entry_points.txt +3 -0
  5. agentengine_sdk_python-0.6.9.dist-info/licenses/LICENSE +175 -0
  6. agentengine_sdk_python-0.6.9.dist-info/top_level.txt +2 -0
  7. ksadk/__init__.py +13 -0
  8. ksadk/__main__.py +4 -0
  9. ksadk/a2a/__init__.py +15 -0
  10. ksadk/a2a/card_builder.py +64 -0
  11. ksadk/a2a/client.py +309 -0
  12. ksadk/a2a/executor.py +201 -0
  13. ksadk/a2a/server.py +79 -0
  14. ksadk/agents/__init__.py +20 -0
  15. ksadk/agents/base.py +370 -0
  16. ksadk/agents/context.py +38 -0
  17. ksadk/agents/event.py +35 -0
  18. ksadk/agents/loop_agent.py +71 -0
  19. ksadk/agents/parallel_agent.py +109 -0
  20. ksadk/agents/runner_adapter.py +24 -0
  21. ksadk/agents/sequential_agent.py +34 -0
  22. ksadk/api/__init__.py +3 -0
  23. ksadk/api/client.py +2227 -0
  24. ksadk/builders/__init__.py +20 -0
  25. ksadk/builders/base.py +66 -0
  26. ksadk/builders/code_builder.py +1990 -0
  27. ksadk/builders/container_builder.py +786 -0
  28. ksadk/builders/framework_requirements.py +59 -0
  29. ksadk/builders/ks3_uploader.py +389 -0
  30. ksadk/builders/mcp_builder.py +476 -0
  31. ksadk/builders/requirements_utils.py +75 -0
  32. ksadk/cli/__init__.py +531 -0
  33. ksadk/cli/__main__.py +4 -0
  34. ksadk/cli/agent_ref.py +204 -0
  35. ksadk/cli/cmd_a2a.py +167 -0
  36. ksadk/cli/cmd_agent.py +201 -0
  37. ksadk/cli/cmd_build.py +270 -0
  38. ksadk/cli/cmd_completion.py +304 -0
  39. ksadk/cli/cmd_config.py +758 -0
  40. ksadk/cli/cmd_create.py +2277 -0
  41. ksadk/cli/cmd_dashboard.py +900 -0
  42. ksadk/cli/cmd_deploy.py +704 -0
  43. ksadk/cli/cmd_destroy.py +345 -0
  44. ksadk/cli/cmd_files.py +1227 -0
  45. ksadk/cli/cmd_hermes.py +1259 -0
  46. ksadk/cli/cmd_invoke.py +1336 -0
  47. ksadk/cli/cmd_launch.py +564 -0
  48. ksadk/cli/cmd_mcp.py +1174 -0
  49. ksadk/cli/cmd_model.py +302 -0
  50. ksadk/cli/cmd_openclaw.py +3918 -0
  51. ksadk/cli/cmd_run.py +232 -0
  52. ksadk/cli/cmd_status.py +588 -0
  53. ksadk/cli/cmd_version.py +522 -0
  54. ksadk/cli/cmd_web.py +288 -0
  55. ksadk/cli/deploy_utils.py +87 -0
  56. ksadk/cli/dry_run.py +214 -0
  57. ksadk/cli/env_options.py +82 -0
  58. ksadk/cli/error_utils.py +525 -0
  59. ksadk/cli/global_options.py +31 -0
  60. ksadk/cli/local_runtime.py +97 -0
  61. ksadk/cli/model_catalog.py +136 -0
  62. ksadk/cli/network_options.py +410 -0
  63. ksadk/cli/resource_common.py +621 -0
  64. ksadk/cli/storage.py +84 -0
  65. ksadk/cli/ui.py +431 -0
  66. ksadk/cli/workflow_common.py +552 -0
  67. ksadk/common/__init__.py +14 -0
  68. ksadk/common/aicp_env.py +128 -0
  69. ksadk/common/auth.py +190 -0
  70. ksadk/common/constants.py +84 -0
  71. ksadk/common/llm_utils.py +61 -0
  72. ksadk/configs/__init__.py +61 -0
  73. ksadk/configs/env_registry.py +342 -0
  74. ksadk/configs/global_config.py +200 -0
  75. ksadk/configs/settings.py +706 -0
  76. ksadk/conversations/__init__.py +95 -0
  77. ksadk/conversations/attachment_storage.py +379 -0
  78. ksadk/conversations/attachments.py +774 -0
  79. ksadk/conversations/compaction_pipeline.py +387 -0
  80. ksadk/conversations/compaction_prompt.py +128 -0
  81. ksadk/conversations/context.py +303 -0
  82. ksadk/conversations/model_context.py +360 -0
  83. ksadk/conversations/model_options.py +136 -0
  84. ksadk/conversations/normalize.py +446 -0
  85. ksadk/conversations/reasoning_markup.py +91 -0
  86. ksadk/conversations/run_kinds.py +61 -0
  87. ksadk/conversations/run_status.py +53 -0
  88. ksadk/conversations/runtime.py +5208 -0
  89. ksadk/conversations/semantic_summary.py +359 -0
  90. ksadk/conversations/session_title.py +254 -0
  91. ksadk/deployment/__init__.py +83 -0
  92. ksadk/deployment/agent_access.py +226 -0
  93. ksadk/deployment/base.py +229 -0
  94. ksadk/deployment/manager.py +332 -0
  95. ksadk/deployment/providers/__init__.py +10 -0
  96. ksadk/deployment/providers/serverless.py +1105 -0
  97. ksadk/deployment/registry.py +120 -0
  98. ksadk/deployment/state.py +78 -0
  99. ksadk/deployment/ui_config.py +21 -0
  100. ksadk/detection/__init__.py +7 -0
  101. ksadk/detection/detector.py +431 -0
  102. ksadk/detection/mcp_detector.py +250 -0
  103. ksadk/hermes_terminal.py +467 -0
  104. ksadk/identity/__init__.py +17 -0
  105. ksadk/identity/resolver.py +385 -0
  106. ksadk/knowledge_base/__init__.py +62 -0
  107. ksadk/knowledge_base/adk_tool.py +47 -0
  108. ksadk/knowledge_base/client.py +297 -0
  109. ksadk/knowledge_base/langchain_tool.py +55 -0
  110. ksadk/knowledge_base/service.py +66 -0
  111. ksadk/knowledge_base/tool.py +50 -0
  112. ksadk/markdown.py +169 -0
  113. ksadk/mcp_runtime/__init__.py +231 -0
  114. ksadk/memory/__init__.py +57 -0
  115. ksadk/memory/adk/__init__.py +51 -0
  116. ksadk/memory/adk/backends/__init__.py +5 -0
  117. ksadk/memory/adk/backends/base_ltm_backend.py +52 -0
  118. ksadk/memory/adk/backends/http_ltm_backend.py +183 -0
  119. ksadk/memory/adk/backends/inmemory_ltm_backend.py +96 -0
  120. ksadk/memory/adk/backends/sdk_ltm_backend.py +456 -0
  121. ksadk/memory/adk/long_term_memory.py +273 -0
  122. ksadk/memory/adk/short_term_memory.py +289 -0
  123. ksadk/memory/adk_tool.py +31 -0
  124. ksadk/memory/backends/__init__.py +6 -0
  125. ksadk/memory/backends/base.py +103 -0
  126. ksadk/memory/backends/memory.py +112 -0
  127. ksadk/memory/backends/redis.py +146 -0
  128. ksadk/memory/langchain_tool.py +39 -0
  129. ksadk/memory/ltm_backend_factory.py +24 -0
  130. ksadk/memory/manager.py +215 -0
  131. ksadk/memory/service.py +172 -0
  132. ksadk/memory/tool.py +96 -0
  133. ksadk/model_policy.py +223 -0
  134. ksadk/openclaw_gateway.py +378 -0
  135. ksadk/runners/__init__.py +8 -0
  136. ksadk/runners/adk_runner.py +1135 -0
  137. ksadk/runners/base_runner.py +305 -0
  138. ksadk/runners/deepagents_runner.py +13 -0
  139. ksadk/runners/factory.py +73 -0
  140. ksadk/runners/langchain_runner.py +539 -0
  141. ksadk/runners/langgraph_runner.py +852 -0
  142. ksadk/runners/patch_langchain.py +187 -0
  143. ksadk/runners/remote_runner.py +667 -0
  144. ksadk/runners/usage_accumulator.py +38 -0
  145. ksadk/runners/utils/__init__.py +19 -0
  146. ksadk/runners/utils/langfuse.py +230 -0
  147. ksadk/runners/utils/loader.py +62 -0
  148. ksadk/runtime_context.py +166 -0
  149. ksadk/runtime_state.py +25 -0
  150. ksadk/sandbox/__init__.py +28 -0
  151. ksadk/sandbox/backends/__init__.py +9 -0
  152. ksadk/sandbox/backends/e2b.py +154 -0
  153. ksadk/sandbox/backends/local_process.py +131 -0
  154. ksadk/sandbox/base.py +101 -0
  155. ksadk/sandbox/factory.py +53 -0
  156. ksadk/sandbox/registry.py +187 -0
  157. ksadk/server/__init__.py +7 -0
  158. ksadk/server/api_models.py +73 -0
  159. ksadk/server/app.py +3789 -0
  160. ksadk/server/static/assets/ArtifactsPanel-C0meuOdi.js +1 -0
  161. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +9 -0
  162. ksadk/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  163. ksadk/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  164. ksadk/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  165. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  166. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  167. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  168. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  169. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  170. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  171. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  172. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  173. ksadk/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  174. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  175. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  176. ksadk/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  177. ksadk/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  178. ksadk/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  179. ksadk/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  180. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  181. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  182. ksadk/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  183. ksadk/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  184. ksadk/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  185. ksadk/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  186. ksadk/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  187. ksadk/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  188. ksadk/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  189. ksadk/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  190. ksadk/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  191. ksadk/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  192. ksadk/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  193. ksadk/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  194. ksadk/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  195. ksadk/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  196. ksadk/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  197. ksadk/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  198. ksadk/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  199. ksadk/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  200. ksadk/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  201. ksadk/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  202. ksadk/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  203. ksadk/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  204. ksadk/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  205. ksadk/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  206. ksadk/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  207. ksadk/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  208. ksadk/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  209. ksadk/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  210. ksadk/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  211. ksadk/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  212. ksadk/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  213. ksadk/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  214. ksadk/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  215. ksadk/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  216. ksadk/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  217. ksadk/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  218. ksadk/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  219. ksadk/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  220. ksadk/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  221. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +8 -0
  222. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +1 -0
  223. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +303 -0
  224. ksadk/server/static/assets/NativeTerminalPanel-CHOd7Rch.css +1 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +1 -0
  226. ksadk/server/static/assets/addon-fit-DthTIhi3.js +1 -0
  227. ksadk/server/static/assets/apl-CnwPGSsG.js +1 -0
  228. ksadk/server/static/assets/arc-bj2rZnP5.js +1 -0
  229. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +1 -0
  230. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-2u1ltDIj.js +36 -0
  231. ksadk/server/static/assets/asciiarmor-qTkVPQu6.js +1 -0
  232. ksadk/server/static/assets/asn1-Dr8qZg38.js +1 -0
  233. ksadk/server/static/assets/asterisk-Bja0s6e1.js +1 -0
  234. ksadk/server/static/assets/blockDiagram-GPEHLZMM-D6fvdK20.js +132 -0
  235. ksadk/server/static/assets/brainfuck-D5EjA2JK.js +1 -0
  236. ksadk/server/static/assets/c4Diagram-AAUBKEIU-BK82L5n5.js +10 -0
  237. ksadk/server/static/assets/channel-BSSOwFIb.js +1 -0
  238. ksadk/server/static/assets/chunk-2J33WTMH-DLVdkeyQ.js +1 -0
  239. ksadk/server/static/assets/chunk-4BX2VUAB-BbwsYcbW.js +1 -0
  240. ksadk/server/static/assets/chunk-55IACEB6-DOairxzw.js +1 -0
  241. ksadk/server/static/assets/chunk-727SXJPM-hCO487gs.js +206 -0
  242. ksadk/server/static/assets/chunk-AQP2D5EJ-XU8vDOuG.js +231 -0
  243. ksadk/server/static/assets/chunk-FMBD7UC4-PrapyfMu.js +15 -0
  244. ksadk/server/static/assets/chunk-ND2GUHAM-CboKhTZ3.js +1 -0
  245. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +1 -0
  246. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +1 -0
  247. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +1 -0
  248. ksadk/server/static/assets/clike-DHH8Ad3s.js +1 -0
  249. ksadk/server/static/assets/clojure-BflJGmDX.js +1 -0
  250. ksadk/server/static/assets/cmake-CyVbuzPu.js +1 -0
  251. ksadk/server/static/assets/cobol-B_OZ4V-R.js +1 -0
  252. ksadk/server/static/assets/coffeescript-B2pKbFk8.js +1 -0
  253. ksadk/server/static/assets/commonlisp-CcllspGY.js +1 -0
  254. ksadk/server/static/assets/cose-bilkent-S5V4N54A-_N8bJ-EU.js +1 -0
  255. ksadk/server/static/assets/crystal-M5-qzICs.js +1 -0
  256. ksadk/server/static/assets/css-CYpP4FRV.js +1 -0
  257. ksadk/server/static/assets/cypher-p9eYesGn.js +1 -0
  258. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +321 -0
  259. ksadk/server/static/assets/d-BhMjBjQP.js +1 -0
  260. ksadk/server/static/assets/dagre-BM42HDAG-FoDlo5J2.js +4 -0
  261. ksadk/server/static/assets/dagre-Bx709z4p.js +1 -0
  262. ksadk/server/static/assets/defaultLocale-C8Fc0cco.js +1 -0
  263. ksadk/server/static/assets/diagram-2AECGRRQ-BBp6ebBw.js +43 -0
  264. ksadk/server/static/assets/diagram-5GNKFQAL-CqOvG1PR.js +10 -0
  265. ksadk/server/static/assets/diagram-KO2AKTUF-O9T3t6ba.js +3 -0
  266. ksadk/server/static/assets/diagram-LMA3HP47-CGG8ekBO.js +24 -0
  267. ksadk/server/static/assets/diagram-OG6HWLK6-4BmH1EEg.js +24 -0
  268. ksadk/server/static/assets/diff-ChtP43wD.js +1 -0
  269. ksadk/server/static/assets/dist-6kSZvCZh.js +1 -0
  270. ksadk/server/static/assets/dist-B67uF2uK.js +1 -0
  271. ksadk/server/static/assets/dist-BMKM66B4.js +1 -0
  272. ksadk/server/static/assets/dist-BVzcWa6D.js +1 -0
  273. ksadk/server/static/assets/dist-Bk1l05Ni.js +1 -0
  274. ksadk/server/static/assets/dist-BkJjNih3.js +1 -0
  275. ksadk/server/static/assets/dist-BmuTWcm7.js +9 -0
  276. ksadk/server/static/assets/dist-C2DKRFVK.js +1 -0
  277. ksadk/server/static/assets/dist-CWiTDN_V.js +6 -0
  278. ksadk/server/static/assets/dist-CZ6C3x8B.js +23 -0
  279. ksadk/server/static/assets/dist-CiZHB6ZW.js +1 -0
  280. ksadk/server/static/assets/dist-Cp1_jKwf.js +1 -0
  281. ksadk/server/static/assets/dist-Cwhk4lwI.js +2 -0
  282. ksadk/server/static/assets/dist-D5JGcxBx.js +11 -0
  283. ksadk/server/static/assets/dist-D99wf6_X.js +1 -0
  284. ksadk/server/static/assets/dist-DCpZrjaK.js +1 -0
  285. ksadk/server/static/assets/dist-DI-oVAXw.js +1 -0
  286. ksadk/server/static/assets/dist-DLMjL01k.js +1 -0
  287. ksadk/server/static/assets/dist-DLeM1OCK.js +1 -0
  288. ksadk/server/static/assets/dist-DM0rEQPk.js +1 -0
  289. ksadk/server/static/assets/dist-DbaH3p5h.js +1 -0
  290. ksadk/server/static/assets/dist-S4klkK5W.js +1 -0
  291. ksadk/server/static/assets/dockerfile-BLkNEvjs.js +1 -0
  292. ksadk/server/static/assets/dtd-DTF72YNO.js +1 -0
  293. ksadk/server/static/assets/dylan-C6jEEEk-.js +1 -0
  294. ksadk/server/static/assets/ebnf-D4c0_ac3.js +1 -0
  295. ksadk/server/static/assets/ecl-BSZaIHnp.js +1 -0
  296. ksadk/server/static/assets/eiffel-BEjRio4Q.js +1 -0
  297. ksadk/server/static/assets/elm-Cshzl8qu.js +1 -0
  298. ksadk/server/static/assets/erDiagram-TEJ5UH35-oJhaci7S.js +85 -0
  299. ksadk/server/static/assets/erlang-DaB2Rkuy.js +1 -0
  300. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +1 -0
  301. ksadk/server/static/assets/factor-D7LVTn2l.js +1 -0
  302. ksadk/server/static/assets/fcl-ClOhbFR7.js +1 -0
  303. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DtTmUan-.js +162 -0
  304. ksadk/server/static/assets/forth-Cezjo90N.js +1 -0
  305. ksadk/server/static/assets/fortran-Bt6PBEDR.js +1 -0
  306. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-BSsb_eij.js +292 -0
  307. ksadk/server/static/assets/gas-BHEdbvp9.js +1 -0
  308. ksadk/server/static/assets/gherkin-oBAE_ms0.js +1 -0
  309. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +1 -0
  310. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-BR5s6Pws.js +106 -0
  311. ksadk/server/static/assets/graphlib-B8gBHxth.js +1 -0
  312. ksadk/server/static/assets/groovy-BC3IOsC8.js +1 -0
  313. ksadk/server/static/assets/haskell-BRsoo5mP.js +1 -0
  314. ksadk/server/static/assets/haxe-DIz0ZZqd.js +1 -0
  315. ksadk/server/static/assets/http-CLVgA2GD.js +1 -0
  316. ksadk/server/static/assets/idl-BUZw3wgd.js +1 -0
  317. ksadk/server/static/assets/index-BH7-pC0B.js +165 -0
  318. ksadk/server/static/assets/index-DAURbCsP.css +1 -0
  319. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +1 -0
  320. ksadk/server/static/assets/infoDiagram-5YYISTIA-DYPc5cvh.js +2 -0
  321. ksadk/server/static/assets/init-D6jRqBbL.js +1 -0
  322. ksadk/server/static/assets/ishikawaDiagram-YF4QCWOH-MoBdALDg.js +70 -0
  323. ksadk/server/static/assets/javascript-9Tg8ixDm.js +1 -0
  324. ksadk/server/static/assets/journeyDiagram-JHISSGLW-DgLiQT8l.js +139 -0
  325. ksadk/server/static/assets/julia-CQfgDRfP.js +1 -0
  326. ksadk/server/static/assets/kanban-definition-UN3LZRKU-CsFx112F.js +89 -0
  327. ksadk/server/static/assets/katex-DRsiwpM2.js +265 -0
  328. ksadk/server/static/assets/linear-TNRQ5Orv.js +1 -0
  329. ksadk/server/static/assets/livescript-BXxG0Yva.js +1 -0
  330. ksadk/server/static/assets/lua-CiA1ziua.js +1 -0
  331. ksadk/server/static/assets/mathematica-u8YMmbU0.js +1 -0
  332. ksadk/server/static/assets/mbox-BEMVcqjs.js +1 -0
  333. ksadk/server/static/assets/mermaid-parser.core-D2eMVOgo.js +161 -0
  334. ksadk/server/static/assets/mindmap-definition-RKZ34NQL-BtByYFhL.js +96 -0
  335. ksadk/server/static/assets/mirc-C9zpzAZU.js +1 -0
  336. ksadk/server/static/assets/mllike-B45xgp2S.js +1 -0
  337. ksadk/server/static/assets/modelica-CqHI_q-D.js +1 -0
  338. ksadk/server/static/assets/mscgen-qgSLujhx.js +1 -0
  339. ksadk/server/static/assets/mumps-CW_TBmxz.js +1 -0
  340. ksadk/server/static/assets/nginx-BMaDbqW3.js +1 -0
  341. ksadk/server/static/assets/nsis-AQ_alPln.js +1 -0
  342. ksadk/server/static/assets/ntriples-d28e9m0E.js +1 -0
  343. ksadk/server/static/assets/octave-D2Q8cUp1.js +1 -0
  344. ksadk/server/static/assets/ordinal-hYBb2elL.js +1 -0
  345. ksadk/server/static/assets/oz-BwTct_5T.js +1 -0
  346. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +1 -0
  347. ksadk/server/static/assets/pascal-Dp_KdFgX.js +1 -0
  348. ksadk/server/static/assets/perl-CdEHsPfU.js +1 -0
  349. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +1 -0
  350. ksadk/server/static/assets/pieDiagram-4H26LBE5-DPnuCB8c.js +30 -0
  351. ksadk/server/static/assets/pig-AF4Hwhju.js +1 -0
  352. ksadk/server/static/assets/powershell-CzG71I4L.js +1 -0
  353. ksadk/server/static/assets/properties-C357ku5U.js +1 -0
  354. ksadk/server/static/assets/protobuf-D1ij_kNL.js +1 -0
  355. ksadk/server/static/assets/pug-Bugr-47h.js +1 -0
  356. ksadk/server/static/assets/puppet-B4hC2X4m.js +1 -0
  357. ksadk/server/static/assets/python-BqYw9LYJ.js +1 -0
  358. ksadk/server/static/assets/q-B9NhS7L_.js +1 -0
  359. ksadk/server/static/assets/quadrantDiagram-W4KKPZXB-miFpCPv4.js +7 -0
  360. ksadk/server/static/assets/r-DADKenSm.js +1 -0
  361. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +1 -0
  362. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-mjB_yYkW.js +84 -0
  363. ksadk/server/static/assets/rpm-gpDK5sbt.js +1 -0
  364. ksadk/server/static/assets/ruby-DsYpTuWg.js +1 -0
  365. ksadk/server/static/assets/sankeyDiagram-5OEKKPKP-D7hSlZZZ.js +40 -0
  366. ksadk/server/static/assets/sas-C2OyD2Y6.js +1 -0
  367. ksadk/server/static/assets/scheme-CyDdOh7e.js +1 -0
  368. ksadk/server/static/assets/sequenceDiagram-3UESZ5HK-D9Cx6hC3.js +162 -0
  369. ksadk/server/static/assets/shell-Du5Qg9im.js +1 -0
  370. ksadk/server/static/assets/sieve-BAxa3IjF.js +1 -0
  371. ksadk/server/static/assets/simple-mode-DRpGK0lJ.js +1 -0
  372. ksadk/server/static/assets/smalltalk-BLp5-jwC.js +1 -0
  373. ksadk/server/static/assets/solr-BBopId9w.js +1 -0
  374. ksadk/server/static/assets/sparql-Dz-mlCAC.js +1 -0
  375. ksadk/server/static/assets/spreadsheet-CTIncYxj.js +1 -0
  376. ksadk/server/static/assets/sql-DcCJ9jop.js +1 -0
  377. ksadk/server/static/assets/stateDiagram-AJRCARHV-D_zrg47o.js +1 -0
  378. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +1 -0
  379. ksadk/server/static/assets/stex-sDrdk2BW.js +1 -0
  380. ksadk/server/static/assets/stylus-DLLZ8dgm.js +1 -0
  381. ksadk/server/static/assets/swift-DKB6_1j6.js +1 -0
  382. ksadk/server/static/assets/tcl-BGfruO1u.js +1 -0
  383. ksadk/server/static/assets/textile-DWBqthVk.js +1 -0
  384. ksadk/server/static/assets/tiddlywiki-VlUiK86J.js +1 -0
  385. ksadk/server/static/assets/tiki-CxVlf5KZ.js +1 -0
  386. ksadk/server/static/assets/timeline-definition-PNZ67QCA-CwSec0Wa.js +120 -0
  387. ksadk/server/static/assets/toml-LvoKBwCs.js +1 -0
  388. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +1 -0
  389. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +1 -0
  390. ksadk/server/static/assets/troff-C2dAgh7P.js +1 -0
  391. ksadk/server/static/assets/ttcn-BePYh-hG.js +1 -0
  392. ksadk/server/static/assets/ttcn-cfg-CY0aot7Q.js +1 -0
  393. ksadk/server/static/assets/turtle-BDLWE2cD.js +1 -0
  394. ksadk/server/static/assets/vb-BthdM_4N.js +1 -0
  395. ksadk/server/static/assets/vbscript-D9f6rSsU.js +1 -0
  396. ksadk/server/static/assets/velocity-CqftRsjg.js +1 -0
  397. ksadk/server/static/assets/vennDiagram-CIIHVFJN-agCVobiF.js +34 -0
  398. ksadk/server/static/assets/verilog-D-x6ne0Z.js +1 -0
  399. ksadk/server/static/assets/vhdl-CMrOq2q1.js +1 -0
  400. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +1 -0
  401. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-TmrlhWNt.js +78 -0
  402. ksadk/server/static/assets/webidl-D5jiJvOU.js +1 -0
  403. ksadk/server/static/assets/xquery-fj_R-kMw.js +1 -0
  404. ksadk/server/static/assets/xterm-DooSxjI5.js +36 -0
  405. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-DV71V-ah.js +7 -0
  406. ksadk/server/static/assets/yacas-CXXgtw0p.js +1 -0
  407. ksadk/server/static/assets/z80-CL1naaMV.js +1 -0
  408. ksadk/server/static/favicon.svg +1 -0
  409. ksadk/server/static/icons.svg +24 -0
  410. ksadk/server/static/index.html +14 -0
  411. ksadk/server/terminal_sessions.py +551 -0
  412. ksadk/sessions/__init__.py +283 -0
  413. ksadk/sessions/base.py +354 -0
  414. ksadk/sessions/continuity.py +313 -0
  415. ksadk/sessions/errors.py +7 -0
  416. ksadk/sessions/in_memory.py +273 -0
  417. ksadk/sessions/local_service.py +949 -0
  418. ksadk/sessions/postgres_service.py +838 -0
  419. ksadk/sessions/sqlite_service.py +13 -0
  420. ksadk/skills/__init__.py +9 -0
  421. ksadk/skills/loader.py +46 -0
  422. ksadk/skills/models.py +120 -0
  423. ksadk/skills/package_store.py +104 -0
  424. ksadk/skills/runtime/__init__.py +17 -0
  425. ksadk/skills/runtime/agent.py +183 -0
  426. ksadk/skills/runtime/artifacts.py +39 -0
  427. ksadk/skills/runtime/backends/__init__.py +1 -0
  428. ksadk/skills/runtime/backends/disabled.py +8 -0
  429. ksadk/skills/runtime/backends/e2b.py +168 -0
  430. ksadk/skills/runtime/backends/local.py +95 -0
  431. ksadk/skills/runtime/base.py +103 -0
  432. ksadk/skills/runtime/executor.py +219 -0
  433. ksadk/skills/runtime/factory.py +37 -0
  434. ksadk/skills/runtime/loader.py +131 -0
  435. ksadk/skills/runtime/registry.py +150 -0
  436. ksadk/skills/runtime/request.py +67 -0
  437. ksadk/skills/service_client.py +239 -0
  438. ksadk/skills/service_env.py +58 -0
  439. ksadk/skills/tool_defs.py +221 -0
  440. ksadk/terminal_client.py +69 -0
  441. ksadk/terminal_exec_policy.py +229 -0
  442. ksadk/tools/__init__.py +17 -0
  443. ksadk/tools/gateway.py +230 -0
  444. ksadk/tools/result_budget.py +154 -0
  445. ksadk/toolsets/__init__.py +852 -0
  446. ksadk/toolsets/_langchain.py +11 -0
  447. ksadk/toolsets/platform.py +93 -0
  448. ksadk/toolsets/sandbox.py +310 -0
  449. ksadk/toolsets/skills.py +217 -0
  450. ksadk/toolsets/web.py +266 -0
  451. ksadk/toolsets/workspace.py +878 -0
  452. ksadk/toolsets/workspace_state.py +44 -0
  453. ksadk/tracing/__init__.py +37 -0
  454. ksadk/tracing/exporters/__init__.py +1 -0
  455. ksadk/tracing/exporters/inmemory_exporter.py +116 -0
  456. ksadk/tracing/exporters/langfuse_exporter.py +445 -0
  457. ksadk/tracing/setup.py +829 -0
  458. ksadk/tracing/span_utils.py +114 -0
  459. ksadk/tui/__init__.py +12 -0
  460. ksadk/tui/app.py +400 -0
  461. ksadk/tui/clipboard.py +100 -0
  462. ksadk/tui/widgets/__init__.py +22 -0
  463. ksadk/tui/widgets/assistant.py +89 -0
  464. ksadk/tui/widgets/base.py +54 -0
  465. ksadk/tui/widgets/chat_input.py +246 -0
  466. ksadk/tui/widgets/history.py +156 -0
  467. ksadk/tui/widgets/system.py +87 -0
  468. ksadk/tui/widgets/thinking.py +110 -0
  469. ksadk/tui/widgets/tool_call.py +161 -0
  470. ksadk/tui/widgets/user.py +37 -0
  471. ksadk/ui_config.py +168 -0
  472. ksadk/version.py +4 -0
  473. ksadk_runtime_common/__init__.py +20 -0
  474. ksadk_runtime_common/memory_backend/__init__.py +29 -0
  475. ksadk_runtime_common/memory_backend/manifest.py +79 -0
  476. ksadk_runtime_common/memory_backend/providers/__init__.py +6 -0
  477. ksadk_runtime_common/memory_backend/providers/lancedb.py +151 -0
  478. ksadk_runtime_common/memory_backend/providers/mem0.py +74 -0
  479. ksadk_runtime_common/memory_backend/registry.py +56 -0
  480. ksadk_runtime_common/memory_backend/render.py +71 -0
  481. ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -0
  482. ksadk_runtime_common/workspace_files/__init__.py +29 -0
  483. ksadk_runtime_common/workspace_files/bootstrap.py +56 -0
  484. ksadk_runtime_common/workspace_files/constants.py +9 -0
  485. ksadk_runtime_common/workspace_files/path_utils.py +71 -0
  486. ksadk_runtime_common/workspace_files/preview.py +96 -0
  487. ksadk_runtime_common/workspace_files/router.py +254 -0
  488. agentengine_sdk_python-0.6.2.dist-info/METADATA +0 -46
  489. agentengine_sdk_python-0.6.2.dist-info/RECORD +0 -4
  490. agentengine_sdk_python-0.6.2.dist-info/top_level.txt +0 -1
@@ -0,0 +1,1259 @@
1
+ """agentengine hermes - Hermes resource management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any, Callable, Optional
9
+
10
+ import click
11
+ from click.core import ParameterSource
12
+
13
+ from ksadk.api import AgentEngineClient
14
+ from ksadk.cli.agent_ref import merge_agent_inputs, resolve_agent_ref
15
+ from ksadk.cli.cmd_dashboard import _open_dashboard
16
+ from ksadk.cli.dry_run import dry_run_option, effective_dry_run, run_async_with_dry_run
17
+ from ksadk.cli.error_utils import remote_error, resolution_error
18
+ from ksadk.cli.network_options import build_network_payload, network_cli_kwargs, network_options
19
+ from ksadk.cli.storage import build_storage_config
20
+ from ksadk.cli.resource_common import (
21
+ CONTEXT_SETTINGS,
22
+ ResourceActionSet,
23
+ ResourceDescriptor,
24
+ ResourceListSchema,
25
+ ResourceStatusSchema,
26
+ build_dry_run_envelope,
27
+ build_result_envelope,
28
+ confirm_destructive,
29
+ confirm_options,
30
+ pagination_options,
31
+ print_next_action_hint,
32
+ render_descriptor_list,
33
+ render_descriptor_status,
34
+ )
35
+ from ksadk.cli.ui import (
36
+ emit_json,
37
+ is_json_output,
38
+ output_option as cli_output_option,
39
+ print_info,
40
+ print_kv,
41
+ print_rule,
42
+ print_success,
43
+ print_title,
44
+ print_warn,
45
+ status_rich_style,
46
+ )
47
+ from ksadk.deployment.agent_access import (
48
+ get_latest_agent_access,
49
+ is_agent_not_found_error,
50
+ normalize_deployment_status,
51
+ )
52
+ from ksadk.deployment.state import clear_state, load_state, save_state
53
+ from ksadk.cli.model_catalog import fetch_provider_model_metadata
54
+ from ksadk.model_policy import build_runtime_model_policy_env
55
+ from ksadk.hermes_terminal import (
56
+ run_hermes_terminal_session,
57
+ validate_hermes_exec_argv,
58
+ validate_hermes_pairing_argv,
59
+ )
60
+
61
+
62
+ DEFAULT_HERMES_IMAGE = "ghcr.io/kingsoftcloud/hermes-agent:2026.5.29.2-ksadk-v1"
63
+ DEFAULT_HERMES_CONTEXT_LENGTHS = (
64
+ ("glm-5.1", "200000"),
65
+ )
66
+ DEFAULT_HERMES_MODEL_NAME = "glm-5.2"
67
+ DEFAULT_HERMES_PUBLIC_BASE_URL = "https://kspmas.ksyun.com/v1/"
68
+ DEFAULT_HERMES_RUNTIME_BASE_URL = DEFAULT_HERMES_PUBLIC_BASE_URL
69
+ KSPMAS_PUBLIC_BASES = (
70
+ "http://kspmas.ksyun.com",
71
+ "https://kspmas.ksyun.com",
72
+ )
73
+ KSPMAS_INTERNAL_BASE = DEFAULT_HERMES_PUBLIC_BASE_URL.rstrip("/")
74
+ _HERMES_GLOBAL_ENV_CACHE: dict[str, str] | None = None
75
+
76
+ HERMES_RESOURCE = ResourceDescriptor(
77
+ name="Hermes",
78
+ summary="Hermes Agent 资源管理。",
79
+ resource_key="hermes",
80
+ actions=ResourceActionSet(
81
+ deploy="agentengine hermes deploy",
82
+ list="agentengine hermes list",
83
+ status="agentengine hermes status [agent_ref]",
84
+ delete="agentengine hermes delete <agent_ref...>",
85
+ open="agentengine hermes open [agent_ref]",
86
+ extra=("connect", "exec", "pairing"),
87
+ ),
88
+ list_schema=ResourceListSchema(
89
+ title="Hermes 实例列表",
90
+ noun="Hermes 实例",
91
+ columns=(
92
+ {"header": "ID", "key": "id", "style": "#58a6ff", "no_wrap": True},
93
+ {"header": "名称", "key": "name", "no_wrap": True},
94
+ {"header": "状态", "key": "status", "no_wrap": True},
95
+ {"header": "Endpoint", "key": "endpoint", "overflow": "fold"},
96
+ {"header": "区域", "key": "region", "no_wrap": True},
97
+ ),
98
+ empty_message="没有找到 Hermes 实例",
99
+ ),
100
+ status_schema=ResourceStatusSchema(
101
+ title="Hermes 状态",
102
+ next_steps=(
103
+ "agentengine invoke <agent> # 原生 Hermes TUI",
104
+ "agentengine hermes connect <agent> # 远端配置 Feishu/Weixin,gateway 由容器托管",
105
+ "agentengine hermes open <agent> --chat",
106
+ "agentengine hermes exec <agent> -- status",
107
+ "agentengine hermes pairing <agent> -- list",
108
+ ),
109
+ ),
110
+ examples=(
111
+ "agentengine hermes deploy --name demo-hermes",
112
+ "agentengine hermes list",
113
+ "agentengine hermes status ar-xxxx",
114
+ "agentengine hermes connect ar-xxxx",
115
+ "agentengine hermes open ar-xxxx --manage",
116
+ "agentengine hermes open ar-xxxx --chat",
117
+ "agentengine hermes exec ar-xxxx -- status",
118
+ "agentengine hermes pairing ar-xxxx -- list",
119
+ "agentengine hermes pairing ar-xxxx -- approve wpsxiezuo <code>",
120
+ "agentengine hermes delete ar-xxxx",
121
+ ),
122
+ missing_ref_message="未找到 Hermes Agent,请指定 Agent(--agent 或位置参数)",
123
+ resolution_commands=("agentengine hermes list",),
124
+ )
125
+
126
+
127
+ def _option_was_explicit(ctx: click.Context | None, name: str) -> bool:
128
+ if ctx is None:
129
+ return False
130
+ try:
131
+ return ctx.get_parameter_source(name) != ParameterSource.DEFAULT
132
+ except Exception:
133
+ return False
134
+
135
+
136
+ def _build_hermes_update_payload(
137
+ *,
138
+ payload: dict[str, Any],
139
+ storage_config: dict[str, Any] | None,
140
+ network_payload: dict[str, Any] | None,
141
+ include_env: bool,
142
+ include_storage: bool,
143
+ ) -> dict[str, Any]:
144
+ """构建已有 Hermes 的最小更新请求,避免镜像更新覆盖用户配置。"""
145
+ update_payload: dict[str, Any] = {
146
+ "name": payload["name"],
147
+ "description": payload["description"],
148
+ "framework": payload["framework"],
149
+ "artifact_type": payload["artifact_type"],
150
+ "artifact_path": payload["artifact_path"],
151
+ "region": payload["region"],
152
+ "resources": payload["resources"],
153
+ "scaling": payload["scaling"],
154
+ "ui_config": payload["ui_config"],
155
+ }
156
+ if include_env:
157
+ update_payload["env_vars"] = payload["env_vars"]
158
+ if include_storage and storage_config:
159
+ update_payload["storage"] = storage_config
160
+ if network_payload:
161
+ update_payload["network"] = network_payload
162
+ return update_payload
163
+
164
+
165
+ @click.group("hermes", context_settings=CONTEXT_SETTINGS)
166
+ def hermes():
167
+ """Hermes Agent 资源管理。"""
168
+
169
+
170
+ def _load_dotenv_into_env(project_dir: Path) -> None:
171
+ env_path = project_dir / ".env"
172
+ if not env_path.exists():
173
+ return
174
+ try:
175
+ from dotenv import dotenv_values
176
+ except ImportError:
177
+ return
178
+ for key, value in dotenv_values(env_path).items():
179
+ if key and value is not None and os.getenv(key) is None:
180
+ os.environ[key] = str(value)
181
+
182
+
183
+ def _get_hermes_global_env() -> dict[str, str]:
184
+ global _HERMES_GLOBAL_ENV_CACHE
185
+ if _HERMES_GLOBAL_ENV_CACHE is not None:
186
+ return _HERMES_GLOBAL_ENV_CACHE
187
+ try:
188
+ from ksadk.configs.global_config import get_env_from_global_config
189
+
190
+ _HERMES_GLOBAL_ENV_CACHE = {
191
+ str(key): str(value).strip()
192
+ for key, value in get_env_from_global_config().items()
193
+ if key and value is not None and str(value).strip()
194
+ }
195
+ except Exception:
196
+ _HERMES_GLOBAL_ENV_CACHE = {}
197
+ return _HERMES_GLOBAL_ENV_CACHE
198
+
199
+
200
+ def _env_value(*names: str) -> str:
201
+ for name in names:
202
+ value = os.getenv(name)
203
+ if value:
204
+ return value
205
+ global_env = _get_hermes_global_env()
206
+ for name in names:
207
+ value = global_env.get(name)
208
+ if value:
209
+ return value
210
+ return ""
211
+
212
+
213
+ def _normalize_hermes_ui_locale(raw: Optional[str]) -> str:
214
+ """标准化 Hermes UI 语言代码,当前 upstream 只支持 en / zh。"""
215
+ text = str(raw or "").strip()
216
+ if not text:
217
+ return "zh"
218
+
219
+ base = text.split(".", 1)[0].replace("_", "-").strip().lower()
220
+ if base in {"c", "c-utf-8", "c.utf-8", "posix"}:
221
+ return "zh"
222
+ if base.startswith("en"):
223
+ return "en"
224
+ if base.startswith("zh"):
225
+ return "zh"
226
+ return "zh"
227
+
228
+
229
+ async def _fetch_hermes_bootstrap_config(region: str) -> dict[str, Any] | None:
230
+ """从服务端获取 Hermes 客户端启动配置。失败时返回 None。"""
231
+ from ksadk.version import VERSION as CLI_VERSION
232
+
233
+ try:
234
+ async with AgentEngineClient(region=region) as client:
235
+ return await client.get_client_bootstrap_config(
236
+ product="hermes",
237
+ framework="hermes",
238
+ region=region,
239
+ client_type="cli",
240
+ client_version=CLI_VERSION,
241
+ locale=_env_value("LANG", "LC_ALL"),
242
+ ignore_dry_run=True,
243
+ )
244
+ except Exception as e:
245
+ print_warn(f"拉取 Hermes 服务端默认配置失败,回退本地默认镜像: {e}")
246
+ return None
247
+
248
+
249
+ def _extract_hermes_bootstrap_image(bootstrap_cfg: dict[str, Any] | None) -> str:
250
+ if not isinstance(bootstrap_cfg, dict):
251
+ return ""
252
+ configs = bootstrap_cfg.get("configs") or bootstrap_cfg.get("Configs")
253
+ if not isinstance(configs, dict):
254
+ return ""
255
+ value = configs.get("bootstrap.default_image") or configs.get("hermes.default_image")
256
+ return str(value or "").strip()
257
+
258
+
259
+ def _default_context_length_for_model(model: str | None) -> str:
260
+ normalized = str(model or "").strip().lower()
261
+ if not normalized:
262
+ return ""
263
+ for model_fragment, context_length in DEFAULT_HERMES_CONTEXT_LENGTHS:
264
+ if model_fragment in normalized:
265
+ return context_length
266
+ return ""
267
+
268
+
269
+ def _build_hermes_env_vars(
270
+ *,
271
+ model_base_url: str | None = None,
272
+ model_api_key: str | None = None,
273
+ default_model: str | None = None,
274
+ model_metadata: dict[str, Any] | None = None,
275
+ ) -> list[dict[str, Any]]:
276
+ raw_model_base_url = model_base_url or _env_value("OPENAI_BASE_URL")
277
+ resolved_model_base_url = (
278
+ _normalize_hermes_runtime_base_url(raw_model_base_url)
279
+ if raw_model_base_url
280
+ else DEFAULT_HERMES_RUNTIME_BASE_URL
281
+ )
282
+ resolved_default_model = default_model or _env_value("OPENAI_MODEL_NAME") or DEFAULT_HERMES_MODEL_NAME
283
+ metadata_context_length = ""
284
+ if isinstance(model_metadata, dict):
285
+ metadata_context_length = str(model_metadata.get("context_window_tokens") or "").strip()
286
+ context_length = (
287
+ _env_value("HERMES_CONTEXT_LENGTH", "OPENAI_CONTEXT_LENGTH", "MODEL_CONTEXT_LENGTH")
288
+ or metadata_context_length
289
+ or _default_context_length_for_model(resolved_default_model)
290
+ )
291
+ ui_locale = _normalize_hermes_ui_locale(_env_value("HERMES_UI_LOCALE", "LANG", "LC_ALL"))
292
+ raw = {
293
+ "OPENAI_API_KEY": model_api_key or _env_value("OPENAI_API_KEY"),
294
+ "OPENAI_BASE_URL": resolved_model_base_url,
295
+ "OPENAI_MODEL_NAME": resolved_default_model,
296
+ "API_SERVER_ENABLED": "true",
297
+ "API_SERVER_HOST": "127.0.0.1",
298
+ "API_SERVER_PORT": "8642",
299
+ "HERMES_DASHBOARD_HOST": "127.0.0.1",
300
+ "HERMES_DASHBOARD_PORT": "9119",
301
+ "KSADK_RUNTIME_PORT": _env_value("PORT") or "8080",
302
+ "HERMES_UI_LOCALE": ui_locale,
303
+ }
304
+ if context_length:
305
+ raw["HERMES_CONTEXT_LENGTH"] = context_length
306
+ fallback_model = _env_value("HERMES_FALLBACK_MODEL", "OPENAI_FALLBACK_MODEL_NAME")
307
+ if fallback_model:
308
+ raw["HERMES_FALLBACK_PROVIDER"] = _env_value("HERMES_FALLBACK_PROVIDER") or "custom"
309
+ raw["HERMES_FALLBACK_MODEL"] = fallback_model
310
+ raw["HERMES_FALLBACK_BASE_URL"] = _env_value("HERMES_FALLBACK_BASE_URL") or resolved_model_base_url
311
+ api_server_key = _env_value("API_SERVER_KEY", "HERMES_API_SERVER_KEY")
312
+ if api_server_key:
313
+ raw["API_SERVER_KEY"] = api_server_key
314
+ langfuse_public_key = _env_value("HERMES_LANGFUSE_PUBLIC_KEY", "LANGFUSE_PUBLIC_KEY")
315
+ langfuse_secret_key = _env_value("HERMES_LANGFUSE_SECRET_KEY", "LANGFUSE_SECRET_KEY")
316
+ if langfuse_public_key and langfuse_secret_key:
317
+ raw["HERMES_LANGFUSE_PUBLIC_KEY"] = langfuse_public_key
318
+ raw["HERMES_LANGFUSE_SECRET_KEY"] = langfuse_secret_key
319
+ langfuse_base_url = _env_value("HERMES_LANGFUSE_BASE_URL", "LANGFUSE_BASE_URL", "LANGFUSE_HOST")
320
+ if langfuse_base_url:
321
+ raw["HERMES_LANGFUSE_BASE_URL"] = langfuse_base_url
322
+ for target_key, source_keys in {
323
+ "HERMES_LANGFUSE_ENV": ("HERMES_LANGFUSE_ENV", "LANGFUSE_ENV"),
324
+ "HERMES_LANGFUSE_RELEASE": ("HERMES_LANGFUSE_RELEASE", "LANGFUSE_RELEASE"),
325
+ "HERMES_LANGFUSE_SAMPLE_RATE": ("HERMES_LANGFUSE_SAMPLE_RATE",),
326
+ "HERMES_LANGFUSE_MAX_CHARS": ("HERMES_LANGFUSE_MAX_CHARS",),
327
+ "HERMES_LANGFUSE_DEBUG": ("HERMES_LANGFUSE_DEBUG",),
328
+ }.items():
329
+ value = _env_value(*source_keys)
330
+ if value:
331
+ raw[target_key] = value
332
+ for key in (
333
+ "WPSXIEZUO_APP_ID",
334
+ "WPSXIEZUO_APP_KEY",
335
+ "WPSXIEZUO_API_BASE",
336
+ "WPSXIEZUO_WS_ENDPOINT",
337
+ "WPSXIEZUO_GROUP_AT_ONLY",
338
+ "WPSXIEZUO_ALLOWED_USERS",
339
+ "WPSXIEZUO_ALLOW_ALL_USERS",
340
+ "WPSXIEZUO_HOME_CHANNEL",
341
+ ):
342
+ value = _env_value(key)
343
+ if value:
344
+ raw[key] = value
345
+ raw = build_runtime_model_policy_env(raw, runtime="hermes")
346
+ if raw.get("HERMES_FALLBACK_MODEL"):
347
+ raw.setdefault("HERMES_FALLBACK_PROVIDER", _env_value("HERMES_FALLBACK_PROVIDER") or "custom")
348
+ raw.setdefault("HERMES_FALLBACK_BASE_URL", _env_value("HERMES_FALLBACK_BASE_URL") or resolved_model_base_url)
349
+ return [
350
+ {"Key": key, "Value": str(value), "IsSensitive": any(token in key for token in ("KEY", "TOKEN", "SECRET"))}
351
+ for key, value in raw.items()
352
+ if value is not None and str(value).strip() != ""
353
+ ]
354
+
355
+
356
+ def _validate_hermes_model_config(
357
+ *,
358
+ model_base_url: str | None = None,
359
+ model_api_key: str | None = None,
360
+ default_model: str | None = None,
361
+ ) -> None:
362
+ _ = model_api_key
363
+ resolved_model_base_url = model_base_url or _env_value("OPENAI_BASE_URL")
364
+ resolved_default_model = default_model or _env_value("OPENAI_MODEL_NAME")
365
+ if not resolved_model_base_url:
366
+ print_info(f"未配置 OPENAI_BASE_URL,默认使用: {DEFAULT_HERMES_PUBLIC_BASE_URL}")
367
+ if not resolved_default_model:
368
+ print_info(f"未配置 OPENAI_MODEL_NAME,默认使用: {DEFAULT_HERMES_MODEL_NAME}")
369
+ if not (model_api_key or _env_value("OPENAI_API_KEY")):
370
+ print_info("未配置 OPENAI_API_KEY,将由服务端在需要时自动创建。")
371
+
372
+
373
+ def _normalize_hermes_runtime_base_url(base_url: str | None) -> str:
374
+ normalized = str(base_url or "").strip()
375
+ return normalized
376
+
377
+
378
+ _FAILURE_STATUSES = {"FAILED", "ERROR", "TERMINATED"}
379
+
380
+
381
+ def _diagnostic_field_style(status_value: str) -> str:
382
+ """非 RUNNING 诊断行样式:失败用红色,其余用黄色。"""
383
+ return "bold #f85149" if status_value in _FAILURE_STATUSES else "bold #d29922"
384
+
385
+
386
+ def _flatten_agent_detail(agent: dict[str, Any]) -> dict[str, Any]:
387
+ basic = agent.get("basic") if isinstance(agent.get("basic"), dict) else {}
388
+ deployment = agent.get("deployment") if isinstance(agent.get("deployment"), dict) else {}
389
+ quick = agent.get("quick_access") if isinstance(agent.get("quick_access"), dict) else {}
390
+ return {
391
+ "agent_id": basic.get("agent_id") or agent.get("agent_id"),
392
+ "name": basic.get("name") or agent.get("name"),
393
+ "status": basic.get("status") or agent.get("status") or "UNKNOWN",
394
+ "phase": basic.get("phase") or "",
395
+ "message": basic.get("message") or "",
396
+ "replicas": basic.get("replicas"),
397
+ "ready_replicas": basic.get("ready_replicas"),
398
+ "framework": deployment.get("framework") or basic.get("framework") or agent.get("framework"),
399
+ "region": basic.get("region") or agent.get("region"),
400
+ "endpoint": quick.get("public_endpoint") or quick.get("private_endpoint") or agent.get("endpoint"),
401
+ "api_key": quick.get("api_key") or agent.get("api_key"),
402
+ "artifact_path": deployment.get("artifact_path") or agent.get("artifact_path"),
403
+ "langfuse_url": (agent.get("advanced") or {}).get("observability_url") or agent.get("langfuse_trace_url") or "",
404
+ }
405
+
406
+
407
+ def _resolve_hermes_ref(agent_ref: str | None) -> str:
408
+ resolved = resolve_agent_ref(agent_ref, cwd=Path(".").resolve(), include_state=True, include_project_config=True)
409
+ if not resolved:
410
+ raise resolution_error(
411
+ HERMES_RESOURCE.missing_ref_message or "请指定 Hermes Agent。",
412
+ hints=list(HERMES_RESOURCE.resolution_commands),
413
+ )
414
+ if resolved.source != "cli":
415
+ print_info(f"未显式指定 Hermes,使用 {resolved.source_text}: {resolved.value}")
416
+ return resolved.value
417
+
418
+
419
+ async def _get_hermes_detail_with_client(
420
+ client: AgentEngineClient,
421
+ agent_ref: str,
422
+ *,
423
+ include_api_key: bool = False,
424
+ ) -> dict[str, Any]:
425
+ if str(agent_ref).startswith("ar-"):
426
+ agent = await client.get_agent(agent_id=agent_ref, include_api_key=include_api_key)
427
+ else:
428
+ agent = await client.get_agent(name=agent_ref, include_api_key=include_api_key)
429
+ detail = _flatten_agent_detail(agent)
430
+ framework = str(detail.get("framework") or "").strip().lower()
431
+ if framework and framework != "hermes":
432
+ raise resolution_error(f"目标 Agent 不是 Hermes: {agent_ref}", hints=["agentengine hermes list"])
433
+ return detail
434
+
435
+
436
+ async def _get_hermes_detail(region: str, agent_ref: str, *, include_api_key: bool = False) -> dict[str, Any]:
437
+ async with AgentEngineClient(region=region) as client:
438
+ return await _get_hermes_detail_with_client(client, agent_ref, include_api_key=include_api_key)
439
+
440
+
441
+ def _resolve_hermes_access(
442
+ *,
443
+ agent_ref: str | None,
444
+ region: str,
445
+ endpoint: str | None = None,
446
+ api_key: str | None = None,
447
+ ) -> dict[str, str | None]:
448
+ if endpoint:
449
+ return {"endpoint": endpoint, "api_key": api_key}
450
+ resolved = _resolve_hermes_ref(agent_ref)
451
+ detail = asyncio.run(_get_hermes_detail(region, resolved, include_api_key=True))
452
+ endpoint_value = str(detail.get("endpoint") or "").strip()
453
+ if not endpoint_value:
454
+ raise resolution_error("目标 Hermes Agent 未返回 Endpoint", hints=["agentengine hermes status <agent>"])
455
+ return {
456
+ "endpoint": endpoint_value,
457
+ "api_key": api_key or detail.get("api_key"),
458
+ "agent_id": detail.get("agent_id"),
459
+ "name": detail.get("name"),
460
+ }
461
+
462
+
463
+ def _split_terminal_agent_ref_and_argv(
464
+ argv: tuple[str, ...],
465
+ *,
466
+ validator: Callable[[tuple[str, ...] | list[str]], list[str]],
467
+ ) -> tuple[str | None, list[str]]:
468
+ raw = [str(item) for item in argv]
469
+ try:
470
+ return None, validator(raw)
471
+ except ValueError as direct_error:
472
+ if len(raw) >= 2:
473
+ try:
474
+ return raw[0], validator(raw[1:])
475
+ except ValueError:
476
+ pass
477
+ raise direct_error
478
+
479
+
480
+ def _render_hermes_dry_run(action: str, request: dict[str, Any], hints: tuple[str, ...] = ()) -> None:
481
+ if is_json_output():
482
+ emit_json(
483
+ build_dry_run_envelope(
484
+ resource="hermes",
485
+ action=action,
486
+ request=request,
487
+ hints=list(hints),
488
+ )
489
+ )
490
+ return
491
+ print_title("Hermes Dry Run", f"action: {action}")
492
+ for key, value in request.items():
493
+ if isinstance(value, (list, tuple)):
494
+ rendered = " ".join(str(item) for item in value) or "-"
495
+ else:
496
+ rendered = str(value if value is not None else "-")
497
+ print_kv(key, rendered)
498
+ for hint in hints:
499
+ print_info(hint)
500
+
501
+
502
+ @hermes.command("deploy", context_settings=CONTEXT_SETTINGS)
503
+ @click.option("--name", "-n", default=None, help="Hermes Agent 名称")
504
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
505
+ @click.option("--image", default=None, help="Hermes runtime 镜像地址")
506
+ @click.option("--model-base-url", default=None, help="模型 Base URL (默认 OPENAI_BASE_URL)")
507
+ @click.option("--model-api-key", default=None, help="模型 API Key (默认 OPENAI_API_KEY)")
508
+ @click.option("--default-model", default=None, help="默认模型名 (默认 OPENAI_MODEL_NAME)")
509
+ @click.option("--cpu", default="2", help="CPU 规格")
510
+ @click.option("--memory", default="4Gi", help="内存规格")
511
+ @click.option("--storage-size-gi", type=int, default=20, show_default=True, help="PVC 容量(Gi)")
512
+ @click.option("--storage-mount-path", default=None, help="PVC 挂载目录(默认: /home/node/.hermes)")
513
+ @click.option("--no-storage", is_flag=True, help="禁用默认 PVC 挂载")
514
+ @network_options
515
+ @dry_run_option()
516
+ @cli_output_option()
517
+ def deploy(
518
+ name: Optional[str],
519
+ region: str,
520
+ image: Optional[str],
521
+ model_base_url: Optional[str],
522
+ model_api_key: Optional[str],
523
+ default_model: Optional[str],
524
+ cpu: str,
525
+ memory: str,
526
+ storage_size_gi: int,
527
+ storage_mount_path: Optional[str],
528
+ no_storage: bool,
529
+ enable_public_access: Optional[bool],
530
+ enable_vpc_access: bool,
531
+ vpc_id: Optional[str],
532
+ subnet_id: Optional[str],
533
+ security_group_id: Optional[str],
534
+ availability_zone: Optional[str],
535
+ dry_run: bool,
536
+ output_mode: str | None,
537
+ ):
538
+ """部署 Hermes runtime 到云端。"""
539
+ _ = output_mode
540
+ dry_run = effective_dry_run(dry_run)
541
+ ctx = click.get_current_context(silent=True)
542
+ include_env_on_update = any(
543
+ (
544
+ _option_was_explicit(ctx, "model_base_url"),
545
+ _option_was_explicit(ctx, "model_api_key"),
546
+ _option_was_explicit(ctx, "default_model"),
547
+ )
548
+ )
549
+ include_storage_on_update = any(
550
+ (
551
+ _option_was_explicit(ctx, "storage_size_gi"),
552
+ _option_was_explicit(ctx, "storage_mount_path"),
553
+ _option_was_explicit(ctx, "no_storage"),
554
+ )
555
+ )
556
+ run_async_with_dry_run(
557
+ _deploy_hermes(
558
+ name=name,
559
+ region=region,
560
+ image=image,
561
+ model_base_url=model_base_url,
562
+ model_api_key=model_api_key,
563
+ default_model=default_model,
564
+ cpu=cpu,
565
+ memory=memory,
566
+ storage_size_gi=storage_size_gi,
567
+ storage_mount_path=storage_mount_path,
568
+ no_storage=no_storage,
569
+ include_env_on_update=include_env_on_update,
570
+ include_storage_on_update=include_storage_on_update,
571
+ **network_cli_kwargs(
572
+ enable_public_access=enable_public_access,
573
+ enable_vpc_access=enable_vpc_access,
574
+ vpc_id=vpc_id,
575
+ subnet_id=subnet_id,
576
+ security_group_id=security_group_id,
577
+ availability_zone=availability_zone,
578
+ ),
579
+ dry_run=dry_run,
580
+ ),
581
+ dry_run=dry_run,
582
+ dry_run_resource="hermes",
583
+ dry_run_action="deploy",
584
+ )
585
+
586
+
587
+ async def _deploy_hermes(
588
+ *,
589
+ name: str | None,
590
+ region: str,
591
+ image: str | None,
592
+ model_base_url: str | None,
593
+ model_api_key: str | None,
594
+ default_model: str | None,
595
+ cpu: str,
596
+ memory: str,
597
+ storage_size_gi: int,
598
+ storage_mount_path: str | None,
599
+ no_storage: bool,
600
+ include_env_on_update: bool,
601
+ include_storage_on_update: bool,
602
+ enable_public_access: bool | None,
603
+ enable_vpc_access: bool,
604
+ vpc_id: str | None,
605
+ subnet_id: str | None,
606
+ security_group_id: str | None,
607
+ availability_zone: str | None,
608
+ dry_run: bool,
609
+ ) -> None:
610
+ project_dir = Path(".").resolve()
611
+ _load_dotenv_into_env(project_dir)
612
+ state = load_state(project_dir)
613
+ existing_agent_id = None
614
+ if str(state.get("type") or state.get("framework") or "").strip().lower() == "hermes":
615
+ existing_agent_id = str(state.get("agent_id") or "").strip() or None
616
+ agent_name = name or state.get("name") or project_dir.name.replace("-", "_")
617
+ image_ref = image or _env_value("HERMES_IMAGE", "HERMES_DOCKER_IMAGE")
618
+ if not image_ref:
619
+ bootstrap_cfg = await _fetch_hermes_bootstrap_config(region)
620
+ image_ref = _extract_hermes_bootstrap_image(bootstrap_cfg)
621
+ if image_ref:
622
+ print_info(f"未指定镜像,使用服务端默认镜像: {image_ref}")
623
+ image_ref = image_ref or DEFAULT_HERMES_IMAGE
624
+ _validate_hermes_model_config(
625
+ model_base_url=model_base_url,
626
+ model_api_key=model_api_key,
627
+ default_model=default_model,
628
+ )
629
+ resolved_default_model = default_model or _env_value("OPENAI_MODEL_NAME") or DEFAULT_HERMES_MODEL_NAME
630
+ model_metadata = await fetch_provider_model_metadata(
631
+ api_base=model_base_url or _env_value("OPENAI_BASE_URL"),
632
+ api_key=model_api_key or _env_value("OPENAI_API_KEY"),
633
+ model=resolved_default_model,
634
+ )
635
+ env_vars = _build_hermes_env_vars(
636
+ model_base_url=model_base_url,
637
+ model_api_key=model_api_key,
638
+ default_model=default_model,
639
+ model_metadata=model_metadata,
640
+ )
641
+ payload = {
642
+ "name": agent_name,
643
+ "description": "Hermes Agent (managed by AgentEngine)",
644
+ "framework": "hermes",
645
+ "artifact_type": "Container",
646
+ "artifact_path": image_ref,
647
+ "region": region,
648
+ "resources": {"cpu": cpu, "memory": memory},
649
+ "scaling": {"min_replicas": 1, "max_replicas": 1, "concurrency": 1000},
650
+ "env_vars": env_vars,
651
+ "ui_config": {"profile": "hermes", "path": "/", "url": None},
652
+ }
653
+ storage_config = build_storage_config(
654
+ "hermes",
655
+ no_storage=no_storage,
656
+ mount_path=storage_mount_path,
657
+ size_gi=storage_size_gi,
658
+ )
659
+ if storage_config:
660
+ payload["storage"] = storage_config
661
+ if existing_agent_id and include_storage_on_update and no_storage:
662
+ print_warn("更新已有 Hermes 时 `--no-storage` 不会删除服务端既有挂盘配置;默认保留已有配置。")
663
+ network_payload = build_network_payload(
664
+ enable_public_access=enable_public_access,
665
+ enable_vpc_access=enable_vpc_access,
666
+ vpc_id=vpc_id,
667
+ subnet_id=subnet_id,
668
+ security_group_id=security_group_id,
669
+ availability_zone=availability_zone,
670
+ region=region,
671
+ dry_run=dry_run,
672
+ )
673
+ if network_payload:
674
+ payload["network"] = network_payload
675
+
676
+ print_title("Hermes 云端部署", f"region: {region}")
677
+ print_kv("名称", agent_name)
678
+ print_kv("镜像", image_ref)
679
+
680
+ async with AgentEngineClient(region=region, dry_run=dry_run) as client:
681
+ if existing_agent_id:
682
+ update_payload = _build_hermes_update_payload(
683
+ payload=payload,
684
+ storage_config=storage_config,
685
+ network_payload=network_payload,
686
+ include_env=include_env_on_update,
687
+ include_storage=include_storage_on_update,
688
+ )
689
+ try:
690
+ res = await client.update_agent(existing_agent_id, update_payload)
691
+ except Exception as update_err:
692
+ if not is_agent_not_found_error(update_err):
693
+ raise
694
+ # 本地 state 缓存的 agent_id 在服务端已不存在(已删除),
695
+ # 清掉失效 state 后回退为新建,避免 404 卡住用户。
696
+ print_warn(
697
+ f"本地状态失效 ({existing_agent_id}),将自动回退为新建: {update_err}"
698
+ )
699
+ cleared = clear_state(project_dir, key=existing_agent_id)
700
+ if cleared:
701
+ print_info("已清理失效的 .agentengine.state")
702
+ existing_agent_id = None
703
+ res = None
704
+ else:
705
+ if res is None:
706
+ res = {}
707
+ res.setdefault("agent_id", existing_agent_id)
708
+ res.setdefault("endpoint", state.get("endpoint"))
709
+ res.setdefault("api_key", state.get("api_key"))
710
+
711
+ if not existing_agent_id:
712
+ res = await client.create_agent(payload)
713
+ if isinstance(res, dict):
714
+ if res.get("order_id") and not res.get("agent_id"):
715
+ print_info(f"订单已创建: {res.get('order_id')},等待 Hermes 实例创建...")
716
+ latest = await get_latest_agent_access(
717
+ client,
718
+ agent_name=agent_name,
719
+ attempts=12,
720
+ interval_seconds=5,
721
+ include_api_key=True,
722
+ detail_fetcher=lambda agent_ref, include_api_key: _get_hermes_detail_with_client(
723
+ client,
724
+ agent_ref,
725
+ include_api_key=include_api_key,
726
+ ),
727
+ suppress_transient_not_found_log=True,
728
+ )
729
+ if latest:
730
+ res = {
731
+ **res,
732
+ "agent_id": latest.get("agent_id"),
733
+ "name": latest.get("name") or agent_name,
734
+ "endpoint": latest.get("endpoint"),
735
+ "api_key": latest.get("api_key"),
736
+ "status": latest.get("status") or res.get("status"),
737
+ }
738
+ else:
739
+ print_warn("实例仍在创建中,稍后使用 `agentengine hermes status` 查看")
740
+ elif res.get("agent_id") and (
741
+ not str(res.get("endpoint") or "").strip()
742
+ or not str(res.get("api_key") or "").strip()
743
+ ):
744
+ latest = await get_latest_agent_access(
745
+ client,
746
+ agent_id=str(res.get("agent_id") or "").strip() or None,
747
+ attempts=5,
748
+ interval_seconds=1,
749
+ initial_delay_seconds=2,
750
+ require_complete_access=True,
751
+ include_api_key=True,
752
+ detail_fetcher=lambda agent_ref, include_api_key: _get_hermes_detail_with_client(
753
+ client,
754
+ agent_ref,
755
+ include_api_key=include_api_key,
756
+ ),
757
+ suppress_transient_not_found_log=True,
758
+ )
759
+ if latest:
760
+ res = {
761
+ **res,
762
+ "agent_id": latest.get("agent_id") or res.get("agent_id"),
763
+ "name": latest.get("name") or res.get("name") or agent_name,
764
+ "endpoint": latest.get("endpoint") or res.get("endpoint"),
765
+ "api_key": latest.get("api_key") or res.get("api_key"),
766
+ "status": latest.get("status") or res.get("status"),
767
+ }
768
+ if dry_run:
769
+ return
770
+
771
+ agent_id = res.get("agent_id")
772
+ endpoint = res.get("endpoint")
773
+ api_key = res.get("api_key")
774
+ deployment_status = normalize_deployment_status(res.get("status") or res.get("phase"))
775
+ save_state(
776
+ project_dir,
777
+ {
778
+ "type": "hermes",
779
+ "framework": "hermes",
780
+ "agent_id": agent_id,
781
+ "name": res.get("name") or agent_name,
782
+ "region": region,
783
+ "endpoint": endpoint,
784
+ "api_key": api_key,
785
+ "image": image_ref,
786
+ "ui_profile": "hermes",
787
+ "ui_path": "/",
788
+ },
789
+ )
790
+ if is_json_output():
791
+ emit_json(
792
+ build_result_envelope(
793
+ resource="hermes",
794
+ action="deploy",
795
+ result={
796
+ "id": str(agent_id or ""),
797
+ "agent_id": str(agent_id or ""),
798
+ "name": str(res.get("name") or agent_name),
799
+ "status": deployment_status,
800
+ "framework": "hermes",
801
+ "region": region,
802
+ "endpoint": str(endpoint or ""),
803
+ "image": image_ref,
804
+ "ui_profile": "hermes",
805
+ "ui_path": "/",
806
+ },
807
+ hints=list(HERMES_RESOURCE.status_schema.next_steps),
808
+ )
809
+ )
810
+ return
811
+ print_success("Hermes 已提交部署")
812
+ print_kv("Agent ID", str(agent_id or "(创建中)"))
813
+ print_kv("当前状态", deployment_status, value_style=status_rich_style(deployment_status))
814
+ if endpoint:
815
+ print_kv("Endpoint", str(endpoint), value_style="#58a6ff")
816
+ print_info("已保存状态到 .agentengine.state")
817
+ print_next_action_hint(
818
+ "agentengine hermes status",
819
+ "agentengine hermes open --chat",
820
+ "agentengine hermes connect",
821
+ )
822
+
823
+
824
+ @hermes.command("list", context_settings=CONTEXT_SETTINGS)
825
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
826
+ @pagination_options(default_page=1, default_size=20)
827
+ @dry_run_option()
828
+ @cli_output_option()
829
+ def list_hermes(region: str, page: int, size: int, dry_run: bool, output_mode: str | None):
830
+ """列出 Hermes Agent。"""
831
+ _ = output_mode
832
+ dry_run = effective_dry_run(dry_run)
833
+
834
+ async def _list():
835
+ async with AgentEngineClient(region=region, dry_run=dry_run) as client:
836
+ resp = await client.list_agents(region=region, framework="hermes", page=page, page_size=size)
837
+ agents = resp.get("agents", []) or []
838
+ rows = []
839
+ items = []
840
+ for agent in agents:
841
+ detail = _flatten_agent_detail(agent)
842
+ status = str(detail.get("status") or "UNKNOWN").upper()
843
+ row = (
844
+ str(detail.get("agent_id") or "-"),
845
+ str(detail.get("name") or "-"),
846
+ f"[{status_rich_style(status)}]{status}[/]",
847
+ str(detail.get("endpoint") or "-"),
848
+ str(detail.get("region") or region),
849
+ )
850
+ rows.append(row)
851
+ items.append(
852
+ {
853
+ "id": row[0],
854
+ "name": row[1],
855
+ "status": row[2],
856
+ "endpoint": row[3],
857
+ "region": row[4],
858
+ }
859
+ )
860
+ render_descriptor_list(
861
+ HERMES_RESOURCE,
862
+ rows=rows,
863
+ total=int(resp.get("total") or len(agents)),
864
+ page=page,
865
+ size=size,
866
+ items=items,
867
+ )
868
+
869
+ run_async_with_dry_run(_list(), dry_run=dry_run, dry_run_resource="hermes", dry_run_action="list")
870
+
871
+
872
+ @hermes.command("status", context_settings=CONTEXT_SETTINGS)
873
+ @click.argument("agent_ref", required=False)
874
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
875
+ @dry_run_option()
876
+ @cli_output_option()
877
+ def status(agent_ref: Optional[str], region: str, dry_run: bool, output_mode: str | None):
878
+ """查看 Hermes Agent 状态。"""
879
+ _ = output_mode
880
+ dry_run = effective_dry_run(dry_run)
881
+ resolved = _resolve_hermes_ref(agent_ref)
882
+
883
+ async def _status():
884
+ async with AgentEngineClient(region=region, dry_run=dry_run) as client:
885
+ detail = await _get_hermes_detail_with_client(client, resolved)
886
+ status_value = str(detail.get("status") or "UNKNOWN").upper()
887
+ fields: list[tuple[str, str, str | None]] = [
888
+ ("ID", str(detail.get("agent_id") or "-"), "#58a6ff"),
889
+ ("状态", status_value, status_rich_style(status_value)),
890
+ ]
891
+ message = str(detail.get("message") or "").strip()
892
+ if status_value != "RUNNING":
893
+ replicas = detail.get("replicas")
894
+ ready = detail.get("ready_replicas")
895
+ if replicas is not None or ready is not None:
896
+ replicas_text = f"{ready if ready is not None else '-'}/{replicas if replicas is not None else '-'}"
897
+ replica_style = _diagnostic_field_style(status_value)
898
+ fields.append(("副本", replicas_text, replica_style))
899
+ if message:
900
+ message_style = _diagnostic_field_style(status_value)
901
+ fields.append(("消息", message, message_style))
902
+ fields.extend([
903
+ ("框架", str(detail.get("framework") or "-"), None),
904
+ ("区域", str(detail.get("region") or region), None),
905
+ ("Endpoint", str(detail.get("endpoint") or "-"), "#58a6ff"),
906
+ ("Langfuse", str(detail.get("langfuse_url") or "-"), "#58a6ff" if detail.get("langfuse_url") else None),
907
+ ("镜像", str(detail.get("artifact_path") or "-"), None),
908
+ ])
909
+ render_descriptor_status(
910
+ HERMES_RESOURCE,
911
+ subtitle=str(detail.get("name") or resolved),
912
+ fields=fields,
913
+ item={
914
+ "id": str(detail.get("agent_id") or "-"),
915
+ "name": str(detail.get("name") or resolved),
916
+ "status": status_value,
917
+ "framework": str(detail.get("framework") or "-"),
918
+ "region": str(detail.get("region") or region),
919
+ "endpoint": str(detail.get("endpoint") or "-"),
920
+ "langfuse_url": str(detail.get("langfuse_url") or ""),
921
+ "image": str(detail.get("artifact_path") or "-"),
922
+ "message": message,
923
+ "phase": str(detail.get("phase") or ""),
924
+ "replicas": detail.get("replicas"),
925
+ "ready_replicas": detail.get("ready_replicas"),
926
+ },
927
+ )
928
+
929
+ run_async_with_dry_run(_status(), dry_run=dry_run, dry_run_resource="hermes", dry_run_action="status")
930
+
931
+
932
+ @hermes.command("open", context_settings=CONTEXT_SETTINGS)
933
+ @click.argument("agent_ref", required=False)
934
+ @click.option("--agent", "--agent-id", "agent_option", "-a", help="Hermes Agent 名称或 ID")
935
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
936
+ @click.option("--manage", is_flag=True, help="打开 Hermes 管理 UI (/)")
937
+ @click.option("--chat", is_flag=True, help="打开统一 hosted chat (/chat)")
938
+ @click.option("--path", "ui_path", default=None, help="目标 UI 路径")
939
+ @click.option("--share", is_flag=True, help="创建可分享链接")
940
+ @click.option("--expires-seconds", default=None, type=str, help="链接有效期(秒)")
941
+ @click.option("--force-new", is_flag=True, help="强制新建链接(跳过复用)")
942
+ @click.option("--no-open", is_flag=True, help="仅打印 URL,不自动打开浏览器")
943
+ @click.option("--direct", is_flag=True, help="直接打开 endpoint/path(跳过短链接创建)")
944
+ @dry_run_option()
945
+ @cli_output_option()
946
+ def open_hermes(
947
+ agent_ref: Optional[str],
948
+ agent_option: Optional[str],
949
+ region: str,
950
+ manage: bool,
951
+ chat: bool,
952
+ ui_path: Optional[str],
953
+ share: bool,
954
+ expires_seconds: Optional[str],
955
+ force_new: bool,
956
+ no_open: bool,
957
+ direct: bool,
958
+ dry_run: bool,
959
+ output_mode: str | None,
960
+ ):
961
+ """打开 Hermes 管理 UI,或使用 --chat 打开统一聊天页。"""
962
+ _ = output_mode
963
+ ctx = click.get_current_context(silent=True)
964
+ region_source = (
965
+ ctx.get_parameter_source("region").name.lower()
966
+ if ctx is not None and ctx.get_parameter_source("region") is not None
967
+ else ""
968
+ )
969
+ if manage and chat:
970
+ raise click.ClickException("--manage 与 --chat 不能同时使用")
971
+ try:
972
+ positional_agent = merge_agent_inputs(agent_option=agent_option, positional_agent=agent_ref)
973
+ except ValueError as e:
974
+ raise click.ClickException(str(e)) from e
975
+ dry_run = effective_dry_run(dry_run)
976
+ target_path = ui_path or ("/chat" if chat else "/")
977
+ parsed_expires = int(expires_seconds) if expires_seconds is not None and expires_seconds not in {"never", "forever"} else 0 if expires_seconds else None
978
+ if dry_run:
979
+ _render_hermes_dry_run(
980
+ "open",
981
+ {
982
+ "agent_ref": positional_agent,
983
+ "path": target_path,
984
+ "share": share,
985
+ "expires_seconds": parsed_expires,
986
+ "force_new": force_new,
987
+ "no_open": no_open,
988
+ "direct": direct,
989
+ },
990
+ hints=("dry-run 未解析远端 Agent,也未打开浏览器。",),
991
+ )
992
+ return
993
+ verified_ref = _resolve_hermes_ref(positional_agent)
994
+ detail = asyncio.run(_get_hermes_detail(region, verified_ref, include_api_key=False))
995
+ positional_agent = str(detail.get("agent_id") or verified_ref)
996
+ _open_dashboard(
997
+ positional_agent=positional_agent,
998
+ agent_option=None,
999
+ region=region,
1000
+ region_source=region_source,
1001
+ ui_path=target_path,
1002
+ share=share,
1003
+ expires_seconds=parsed_expires,
1004
+ force_new=force_new,
1005
+ no_open=no_open,
1006
+ direct=direct,
1007
+ )
1008
+
1009
+
1010
+ @hermes.command("exec", context_settings=CONTEXT_SETTINGS)
1011
+ @click.argument("argv", nargs=-1, required=True)
1012
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
1013
+ @click.option("--endpoint", "-e", default=None, help="Agent Endpoint URL (覆盖自动获取)")
1014
+ @click.option("--api-key", default=None, help="AgentEngine API Key (覆盖自动获取)")
1015
+ @click.option("--session", "-s", default=None, help="Session ID")
1016
+ @click.option("--insecure", "-k", is_flag=True, help="跳过 SSL 证书验证")
1017
+ @dry_run_option()
1018
+ @cli_output_option()
1019
+ def exec_hermes(
1020
+ argv: tuple[str, ...],
1021
+ region: str,
1022
+ endpoint: Optional[str],
1023
+ api_key: Optional[str],
1024
+ session: Optional[str],
1025
+ insecure: bool,
1026
+ dry_run: bool,
1027
+ output_mode: str | None,
1028
+ ):
1029
+ """透传受限 Hermes 只读运维子命令。"""
1030
+ _ = output_mode
1031
+ try:
1032
+ agent_ref, validated_argv = _split_terminal_agent_ref_and_argv(
1033
+ argv,
1034
+ validator=validate_hermes_exec_argv,
1035
+ )
1036
+ dry_run = effective_dry_run(dry_run)
1037
+ if dry_run:
1038
+ _render_hermes_dry_run(
1039
+ "exec",
1040
+ {
1041
+ "agent_ref": agent_ref,
1042
+ "endpoint": endpoint,
1043
+ "mode": "exec",
1044
+ "argv": validated_argv,
1045
+ "session": session,
1046
+ "insecure": insecure,
1047
+ },
1048
+ hints=("dry-run 未解析远端 Agent,也未建立 websocket。",),
1049
+ )
1050
+ return
1051
+ access = _resolve_hermes_access(agent_ref=agent_ref, region=region, endpoint=endpoint, api_key=api_key)
1052
+ exit_code = asyncio.run(
1053
+ run_hermes_terminal_session(
1054
+ endpoint=str(access["endpoint"]),
1055
+ api_key=access.get("api_key"),
1056
+ session_id=session,
1057
+ insecure=insecure,
1058
+ mode="exec",
1059
+ argv=validated_argv,
1060
+ )
1061
+ )
1062
+ except (KeyboardInterrupt, asyncio.CancelledError):
1063
+ raise SystemExit(130)
1064
+ except ValueError as e:
1065
+ raise click.ClickException(f"不允许的 Hermes 子命令: {e}") from e
1066
+ if exit_code:
1067
+ raise SystemExit(exit_code)
1068
+
1069
+
1070
+ @hermes.command("pairing", context_settings=CONTEXT_SETTINGS)
1071
+ @click.argument("argv", nargs=-1, required=True)
1072
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
1073
+ @click.option("--endpoint", "-e", default=None, help="Agent Endpoint URL (覆盖自动获取)")
1074
+ @click.option("--api-key", default=None, help="AgentEngine API Key (覆盖自动获取)")
1075
+ @click.option("--session", "-s", default=None, help="Session ID")
1076
+ @click.option("--insecure", "-k", is_flag=True, help="跳过 SSL 证书验证")
1077
+ @dry_run_option()
1078
+ @cli_output_option()
1079
+ def pairing_hermes(
1080
+ argv: tuple[str, ...],
1081
+ region: str,
1082
+ endpoint: Optional[str],
1083
+ api_key: Optional[str],
1084
+ session: Optional[str],
1085
+ insecure: bool,
1086
+ dry_run: bool,
1087
+ output_mode: str | None,
1088
+ ):
1089
+ """透传 Hermes pairing 审批子命令。
1090
+
1091
+ WPS 协作配对码来自未授权用户私聊机器人时 Hermes 返回的 pairing code,
1092
+ 审批示例:agentengine hermes pairing <agent> -- approve wpsxiezuo <code>
1093
+ """
1094
+ _ = output_mode
1095
+ try:
1096
+ agent_ref, validated_argv = _split_terminal_agent_ref_and_argv(
1097
+ argv,
1098
+ validator=validate_hermes_pairing_argv,
1099
+ )
1100
+ dry_run = effective_dry_run(dry_run)
1101
+ if dry_run:
1102
+ _render_hermes_dry_run(
1103
+ "pairing",
1104
+ {
1105
+ "agent_ref": agent_ref,
1106
+ "endpoint": endpoint,
1107
+ "mode": "pairing",
1108
+ "argv": validated_argv,
1109
+ "session": session,
1110
+ "insecure": insecure,
1111
+ },
1112
+ hints=("dry-run 未解析远端 Agent,也未建立 websocket。",),
1113
+ )
1114
+ return
1115
+ access = _resolve_hermes_access(agent_ref=agent_ref, region=region, endpoint=endpoint, api_key=api_key)
1116
+ exit_code = asyncio.run(
1117
+ run_hermes_terminal_session(
1118
+ endpoint=str(access["endpoint"]),
1119
+ api_key=access.get("api_key"),
1120
+ session_id=session,
1121
+ insecure=insecure,
1122
+ mode="pairing",
1123
+ argv=validated_argv,
1124
+ )
1125
+ )
1126
+ except (KeyboardInterrupt, asyncio.CancelledError):
1127
+ raise SystemExit(130)
1128
+ except ValueError as e:
1129
+ raise click.ClickException(f"不允许的 Hermes pairing 子命令: {e}") from e
1130
+ if exit_code:
1131
+ raise SystemExit(exit_code)
1132
+
1133
+
1134
+ @hermes.command("connect", context_settings=CONTEXT_SETTINGS)
1135
+ @click.argument("agent_ref", required=False)
1136
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
1137
+ @click.option("--endpoint", "-e", default=None, help="Agent Endpoint URL (覆盖自动获取)")
1138
+ @click.option("--api-key", default=None, help="AgentEngine API Key (覆盖自动获取)")
1139
+ @click.option("--session", "-s", default=None, help="Session ID")
1140
+ @click.option("--insecure", "-k", is_flag=True, help="跳过 SSL 证书验证")
1141
+ @dry_run_option()
1142
+ @cli_output_option()
1143
+ def connect_hermes(
1144
+ agent_ref: Optional[str],
1145
+ region: str,
1146
+ endpoint: Optional[str],
1147
+ api_key: Optional[str],
1148
+ session: Optional[str],
1149
+ insecure: bool,
1150
+ dry_run: bool,
1151
+ output_mode: str | None,
1152
+ ):
1153
+ """进入远端 Hermes gateway setup 向导,执行扫码连接。"""
1154
+ _ = output_mode
1155
+ dry_run = effective_dry_run(dry_run)
1156
+ if dry_run:
1157
+ _render_hermes_dry_run(
1158
+ "connect",
1159
+ {
1160
+ "agent_ref": agent_ref,
1161
+ "endpoint": endpoint,
1162
+ "mode": "connect",
1163
+ "session": session,
1164
+ "insecure": insecure,
1165
+ },
1166
+ hints=("dry-run 未解析远端 Agent,也未建立 websocket。",),
1167
+ )
1168
+ return
1169
+
1170
+ try:
1171
+ access = _resolve_hermes_access(agent_ref=agent_ref, region=region, endpoint=endpoint, api_key=api_key)
1172
+ exit_code = asyncio.run(
1173
+ run_hermes_terminal_session(
1174
+ endpoint=str(access["endpoint"]),
1175
+ api_key=access.get("api_key"),
1176
+ session_id=session,
1177
+ insecure=insecure,
1178
+ mode="connect",
1179
+ )
1180
+ )
1181
+ except (KeyboardInterrupt, asyncio.CancelledError):
1182
+ raise SystemExit(130)
1183
+ if exit_code:
1184
+ raise SystemExit(exit_code)
1185
+
1186
+
1187
+ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run: bool):
1188
+ dry_run = effective_dry_run(dry_run)
1189
+ if not confirm_destructive(
1190
+ assume_yes=assume_yes,
1191
+ dry_run=dry_run,
1192
+ prompt=f"确定要删除这 {len(agent_refs)} 个 Hermes Agent 吗?",
1193
+ ):
1194
+ return
1195
+
1196
+ async def _delete():
1197
+ deleted = []
1198
+ failed = []
1199
+ async with AgentEngineClient(region=region, dry_run=dry_run) as client:
1200
+ for agent_ref in agent_refs:
1201
+ detail = await _get_hermes_detail_with_client(client, agent_ref)
1202
+ agent_id = str(detail.get("agent_id") or "").strip()
1203
+ if not agent_id:
1204
+ failed.append(agent_ref)
1205
+ continue
1206
+ ok = await client.delete_agent(agent_id)
1207
+ if ok:
1208
+ deleted.append(agent_id)
1209
+ clear_state(Path(".").resolve(), key=agent_id)
1210
+ else:
1211
+ failed.append(agent_ref)
1212
+ if failed:
1213
+ raise remote_error(f"以下 Hermes 删除失败: {', '.join(failed)}")
1214
+ return {"targets": list(agent_refs), "deleted": deleted, "failed": failed}
1215
+
1216
+ result = run_async_with_dry_run(_delete(), dry_run=dry_run, dry_run_resource="hermes", dry_run_action="delete")
1217
+ if result is not None:
1218
+ deleted_text = ", ".join(result["deleted"]) or "-"
1219
+ failed_text = ", ".join(result["failed"]) or "-"
1220
+ render_descriptor_status(
1221
+ HERMES_RESOURCE,
1222
+ title="Hermes 删除结果",
1223
+ subtitle=", ".join(result["targets"]) if result["targets"] else "-",
1224
+ fields=[
1225
+ ("目标数量", str(len(result["targets"])), None),
1226
+ ("已删除", deleted_text, "ok" if result["deleted"] else "muted"),
1227
+ ("失败", failed_text, "err" if result["failed"] else "muted"),
1228
+ ],
1229
+ next_steps=(
1230
+ "agentengine hermes list",
1231
+ "agentengine hermes deploy",
1232
+ ),
1233
+ action="delete",
1234
+ item=result,
1235
+ )
1236
+
1237
+
1238
+ @hermes.command("delete", context_settings=CONTEXT_SETTINGS)
1239
+ @click.argument("agent_refs", nargs=-1, required=True)
1240
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
1241
+ @confirm_options()
1242
+ @dry_run_option()
1243
+ @cli_output_option()
1244
+ def delete(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run: bool, output_mode: str | None):
1245
+ """删除 Hermes Agent。"""
1246
+ _ = output_mode
1247
+ _delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)
1248
+
1249
+
1250
+ @hermes.command("destroy", context_settings=CONTEXT_SETTINGS, hidden=True)
1251
+ @click.argument("agent_refs", nargs=-1, required=True)
1252
+ @click.option("--region", "-r", default="cn-beijing-6", envvar="KSYUN_REGION", help="区域")
1253
+ @confirm_options()
1254
+ @dry_run_option()
1255
+ @cli_output_option()
1256
+ def destroy(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run: bool, output_mode: str | None):
1257
+ """删除 Hermes Agent。"""
1258
+ _ = output_mode
1259
+ _delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)