agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.8.0__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 (384) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +252 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +533 -726
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +173 -90
  54. ksadk/cli/cmd_invoke.py +388 -159
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +103 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +488 -228
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +44 -14
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +38 -16
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +501 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +656 -0
  97. ksadk/conversations/model_context.py +22 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5187
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +52 -39
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +326 -209
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +267 -0
  156. ksadk/memory/adk/short_term_memory.py +33 -27
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +1248 -164
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -530
  183. ksadk/runners/langgraph_runner.py +490 -95
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +264 -38
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3743
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +86 -40
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +28 -5
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +216 -35
  313. ksadk/sessions/resilience.py +52 -0
  314. ksadk/sessions/resilient.py +388 -0
  315. ksadk/skills/models.py +17 -5
  316. ksadk/skills/package_store.py +5 -2
  317. ksadk/skills/runtime/agent.py +20 -5
  318. ksadk/skills/runtime/artifacts.py +1 -5
  319. ksadk/skills/runtime/backends/e2b.py +10 -4
  320. ksadk/skills/runtime/backends/local.py +15 -3
  321. ksadk/skills/runtime/base.py +1 -2
  322. ksadk/skills/runtime/executor.py +10 -3
  323. ksadk/skills/runtime/factory.py +4 -2
  324. ksadk/skills/runtime/loader.py +1 -2
  325. ksadk/skills/runtime/registry.py +15 -10
  326. ksadk/skills/runtime/request.py +3 -1
  327. ksadk/skills/service_client.py +38 -17
  328. ksadk/skills/tool_defs.py +15 -10
  329. ksadk/terminal_exec_policy.py +3 -4
  330. ksadk/tools/gateway.py +92 -15
  331. ksadk/tools/result_budget.py +12 -4
  332. ksadk/toolsets/__init__.py +136 -36
  333. ksadk/toolsets/platform.py +21 -5
  334. ksadk/toolsets/sandbox.py +82 -16
  335. ksadk/toolsets/skills.py +200 -39
  336. ksadk/toolsets/web.py +123 -26
  337. ksadk/toolsets/workspace.py +264 -114
  338. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  339. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  340. ksadk/tracing/setup.py +132 -53
  341. ksadk/tracing/span_utils.py +5 -2
  342. ksadk/tui/__init__.py +22 -7
  343. ksadk/tui/loop.py +1925 -0
  344. ksadk/tui/stream_render.py +69 -0
  345. ksadk/ui_config.py +5 -3
  346. ksadk/version.py +1 -1
  347. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  348. agentengine_sdk_python-0.6.9.dist-info/RECORD +0 -487
  349. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
  350. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
  351. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
  352. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
  353. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  354. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  355. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  356. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  357. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  358. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  359. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  360. ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
  361. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  362. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  363. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  364. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  365. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  366. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  367. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  368. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  369. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  370. ksadk/tui/app.py +0 -400
  371. ksadk/tui/clipboard.py +0 -100
  372. ksadk/tui/widgets/__init__.py +0 -22
  373. ksadk/tui/widgets/assistant.py +0 -89
  374. ksadk/tui/widgets/base.py +0 -54
  375. ksadk/tui/widgets/chat_input.py +0 -246
  376. ksadk/tui/widgets/history.py +0 -156
  377. ksadk/tui/widgets/system.py +0 -87
  378. ksadk/tui/widgets/thinking.py +0 -110
  379. ksadk/tui/widgets/tool_call.py +0 -161
  380. ksadk/tui/widgets/user.py +0 -37
  381. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  382. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  383. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  384. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
ksadk/cli/cmd_openclaw.py CHANGED
@@ -10,11 +10,11 @@ agentengine openclaw - OpenClaw 资源管理
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import asyncio
13
14
  import copy
14
15
  import io
15
- import os
16
- import asyncio
17
16
  import json
17
+ import os
18
18
  import re
19
19
  import secrets
20
20
  import shutil
@@ -26,23 +26,27 @@ import time
26
26
  import webbrowser
27
27
  from datetime import datetime, timedelta, timezone
28
28
  from pathlib import Path
29
- from typing import Optional, Dict, Any
29
+ from typing import Any, Dict, Optional, cast
30
30
 
31
31
  import click
32
32
  from click.core import ParameterSource
33
+ from rich.console import Console
33
34
  from rich.measure import Measurement
34
35
  from rich.table import Table as RichTable
35
36
 
36
37
  from ksadk.api.client import DryRunExit
38
+ from ksadk.builders.container_builder import (
39
+ registry_kind_label,
40
+ resolve_registry_credentials,
41
+ )
37
42
  from ksadk.cli.agent_ref import resolve_openclaw_ref
38
- from ksadk.cli.dry_run import dry_run_option, run_async_with_dry_run, effective_dry_run
43
+ from ksadk.cli.dry_run import dry_run_option, effective_dry_run, run_async_with_dry_run
39
44
  from ksadk.cli.error_utils import abort_with_cli_error, remote_error, resolution_error
45
+ from ksadk.cli.model_catalog import fetch_provider_model_catalog, find_model_in_catalog
40
46
  from ksadk.cli.network_options import build_network_payload, network_cli_kwargs, network_options
41
- from ksadk.cli.storage import build_storage_config
42
- from ksadk.cli.resource_common import ResourceActionDescriptor
43
47
  from ksadk.cli.resource_common import (
44
48
  CONTEXT_SETTINGS,
45
- ResourceActionSet,
49
+ ResourceActionDescriptor,
46
50
  ResourceDescriptor,
47
51
  ResourceListSchema,
48
52
  ResourceStatusSchema,
@@ -51,18 +55,17 @@ from ksadk.cli.resource_common import (
51
55
  confirm_destructive,
52
56
  confirm_options,
53
57
  pagination_options,
54
- print_next_action_hint,
58
+ region_option,
55
59
  render_descriptor_list,
56
60
  render_descriptor_status,
57
- region_option,
58
61
  )
62
+ from ksadk.cli.storage import build_storage_config
59
63
  from ksadk.cli.ui import (
60
64
  emit_json,
61
65
  get_console,
62
66
  is_json_output,
63
67
  is_stdout_tty,
64
68
  json_dumps,
65
- output_option as cli_output_option,
66
69
  print_info,
67
70
  print_kv,
68
71
  print_rule,
@@ -71,17 +74,19 @@ from ksadk.cli.ui import (
71
74
  print_warn,
72
75
  status_rich_style,
73
76
  )
74
- from ksadk.deployment.agent_access import get_latest_agent_access
75
- from ksadk.cli.model_catalog import fetch_provider_model_catalog, find_model_in_catalog
77
+ from ksadk.cli.ui import (
78
+ output_option as cli_output_option,
79
+ )
76
80
  from ksadk.conversations.model_context import normalize_model_metadata
81
+ from ksadk.deployment.agent_access import get_latest_agent_access
77
82
  from ksadk.model_policy import build_runtime_model_policy_env
78
- from ksadk.builders.container_builder import (
79
- registry_kind_label,
80
- resolve_registry_credentials,
83
+ from ksadk.openclaw_gateway import (
84
+ OpenClawGatewayClient,
85
+ OpenClawGatewayError,
86
+ OpenClawGatewayRequestError,
81
87
  )
82
- from ksadk.openclaw_gateway import OpenClawGatewayClient, OpenClawGatewayError, OpenClawGatewayRequestError
83
- from ksadk.terminal_exec_policy import OPENCLAW_TERMINAL_EXEC_POLICY
84
88
  from ksadk.terminal_client import run_terminal_session
89
+ from ksadk.terminal_exec_policy import OPENCLAW_TERMINAL_EXEC_POLICY
85
90
 
86
91
  console = get_console()
87
92
  # 默认 OpenClaw 镜像。
@@ -136,7 +141,8 @@ OPENCLAW_CHANNEL_CONNECT_HELP = """连接指定 channel。
136
141
  飞书:启动官方 onboarding 流程。
137
142
  agentengine openclaw channel connect <id> --channel feishu
138
143
  WPS 协作:写入开放平台 appId/appSecret 并启动长连接。
139
- agentengine openclaw channel connect <id> --channel wps-xiezuo --app-id <appId> --app-secret <appSecret>
144
+ agentengine openclaw channel connect <id> --channel wps-xiezuo \
145
+ --app-id <appId> --app-secret <appSecret>
140
146
 
141
147
  \b
142
148
  WPS 协作说明:
@@ -347,6 +353,7 @@ def _get_global_env() -> Dict[str, str]:
347
353
 
348
354
  try:
349
355
  from ksadk.configs.global_config import get_env_from_global_config
356
+
350
357
  _GLOBAL_ENV_CACHE = {
351
358
  str(k): str(v).strip()
352
359
  for k, v in get_env_from_global_config().items()
@@ -395,6 +402,7 @@ def _resolve_model_base_url(cli_value: Optional[str]) -> Optional[str]:
395
402
 
396
403
  try:
397
404
  from ksadk.configs.settings import settings
405
+
398
406
  api_base = settings.model.api_base
399
407
  if api_base and str(api_base).strip():
400
408
  return str(api_base).strip()
@@ -434,8 +442,9 @@ def _summarize_openclaw_region(agents: list[Dict[str, Any]], fallback_region: Op
434
442
 
435
443
  def _print_openclaw_list_summary(table: RichTable, summary_text: str) -> None:
436
444
  """将摘要贴在表格下方;宽度不足时退化成普通单行。"""
437
- table_width = Measurement.get(console, console.options, table).maximum
438
- summary_width = Measurement.get(console, console.options, summary_text).maximum
445
+ rich_console = cast(Console, console)
446
+ table_width = Measurement.get(rich_console, rich_console.options, table).maximum
447
+ summary_width = Measurement.get(rich_console, rich_console.options, summary_text).maximum
439
448
  if table_width >= summary_width:
440
449
  summary_grid = RichTable.grid(expand=False)
441
450
  summary_grid.add_column(justify="right", width=table_width)
@@ -456,7 +465,12 @@ def _normalize_ui_locale(raw: Optional[str]) -> str:
456
465
 
457
466
  if low in {"c", "c-utf-8", "c.utf-8", "posix"}:
458
467
  return "zh-CN"
459
- if low.startswith("zh-tw") or low.startswith("zh-hk") or low.startswith("zh-mo") or low.startswith("zh-hant"):
468
+ if (
469
+ low.startswith("zh-tw")
470
+ or low.startswith("zh-hk")
471
+ or low.startswith("zh-mo")
472
+ or low.startswith("zh-hant")
473
+ ):
460
474
  return "zh-TW"
461
475
  if low.startswith("zh"):
462
476
  return "zh-CN"
@@ -531,7 +545,9 @@ def _strip_provider_prefix(provider_id: str, model_id: str) -> str:
531
545
 
532
546
 
533
547
  def _default_openclaw_model_inputs(provider_id: str, model_id: str) -> list[str]:
534
- if str(provider_id or "").strip().lower() == "ksyun" and str(model_id or "").strip().lower() in {"glm-5.1", "glm-5.2"}:
548
+ if str(provider_id or "").strip().lower() == "ksyun" and str(
549
+ model_id or ""
550
+ ).strip().lower() in {"glm-5.1", "glm-5.2"}:
535
551
  return ["text"]
536
552
  return ["text", "image"]
537
553
 
@@ -597,9 +613,11 @@ def _openclaw_catalog_item_from_metadata(
597
613
  "api": str(raw_model.get("api") or provider_api or "openai-completions"),
598
614
  "reasoning": bool(raw_model.get("reasoning", True)),
599
615
  "input": inputs or _default_openclaw_model_inputs(provider_id, model_id),
600
- "cost": raw_model.get("cost")
601
- if isinstance(raw_model.get("cost"), dict)
602
- else {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
616
+ "cost": (
617
+ raw_model.get("cost")
618
+ if isinstance(raw_model.get("cost"), dict)
619
+ else {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}
620
+ ),
603
621
  "contextWindow": int(metadata.get("context_window_tokens") or 200_000),
604
622
  "maxTokens": int(metadata.get("max_output_tokens") or 20_000),
605
623
  }
@@ -613,7 +631,9 @@ def _apply_openclaw_provider_model_catalog(
613
631
  return False
614
632
 
615
633
  provider_id = str(env.get("OPENCLAW_MODEL_PROVIDER_ID") or "ksyun").strip() or "ksyun"
616
- provider_api = str(env.get("OPENCLAW_MODEL_API") or "openai-completions").strip() or "openai-completions"
634
+ provider_api = (
635
+ str(env.get("OPENCLAW_MODEL_API") or "openai-completions").strip() or "openai-completions"
636
+ )
617
637
  raw_catalog = str(env.get("OPENCLAW_MODEL_CATALOG_JSON") or "").strip()
618
638
  catalog: list[Dict[str, Any]] = []
619
639
  if raw_catalog:
@@ -632,16 +652,17 @@ def _apply_openclaw_provider_model_catalog(
632
652
 
633
653
  changed = False
634
654
  for raw_model in raw_models:
635
- if not isinstance(raw_model, dict):
636
- raw_model = {"id": str(raw_model or "").strip()}
637
- item = _openclaw_catalog_item_from_metadata(
638
- raw_model,
655
+ raw_model_dict: Dict[str, Any] = (
656
+ dict(raw_model) if isinstance(raw_model, dict) else {"id": str(raw_model or "").strip()}
657
+ )
658
+ catalog_item = _openclaw_catalog_item_from_metadata(
659
+ raw_model_dict,
639
660
  provider_id=provider_id,
640
661
  provider_api=provider_api,
641
662
  )
642
- if not item:
663
+ if not catalog_item:
643
664
  continue
644
- model_key = str(item["id"])
665
+ model_key = str(catalog_item["id"])
645
666
  existing_index = next(
646
667
  (
647
668
  index
@@ -651,13 +672,13 @@ def _apply_openclaw_provider_model_catalog(
651
672
  None,
652
673
  )
653
674
  if existing_index is not None:
654
- merged = {**catalog[existing_index], **item}
675
+ merged = {**catalog[existing_index], **catalog_item}
655
676
  if merged != catalog[existing_index]:
656
677
  catalog[existing_index] = merged
657
678
  changed = True
658
679
  continue
659
680
  seen.add(model_key)
660
- catalog.append(item)
681
+ catalog.append(catalog_item)
661
682
  changed = True
662
683
 
663
684
  if not catalog or not changed:
@@ -679,20 +700,12 @@ def _apply_openclaw_provider_model_metadata(
679
700
 
680
701
  def _openclaw_requested_model_ids(env: Dict[str, str]) -> list[str]:
681
702
  raw_allowlist = str(
682
- env.get("OPENCLAW_MODEL_ALLOWLIST")
683
- or env.get("AGENTENGINE_MODEL_ALLOWLIST")
684
- or ""
703
+ env.get("OPENCLAW_MODEL_ALLOWLIST") or env.get("AGENTENGINE_MODEL_ALLOWLIST") or ""
685
704
  ).strip()
686
705
  if raw_allowlist:
687
- return [
688
- item.strip()
689
- for item in raw_allowlist.replace(";", ",").split(",")
690
- if item.strip()
691
- ]
706
+ return [item.strip() for item in raw_allowlist.replace(";", ",").split(",") if item.strip()]
692
707
  primary_model = str(
693
- env.get("OPENCLAW_DEFAULT_MODEL")
694
- or env.get("OPENAI_MODEL_NAME")
695
- or ""
708
+ env.get("OPENCLAW_DEFAULT_MODEL") or env.get("OPENAI_MODEL_NAME") or ""
696
709
  ).strip()
697
710
  return [primary_model] if primary_model else []
698
711
 
@@ -705,9 +718,7 @@ def _filter_openclaw_provider_catalog(
705
718
  if not requested_models:
706
719
  return []
707
720
  raw_models = [
708
- item.get("_provider_raw_model") or item
709
- if isinstance(item, dict)
710
- else item
721
+ item.get("_provider_raw_model") or item if isinstance(item, dict) else item
711
722
  for item in provider_catalog
712
723
  ]
713
724
  selected: list[Any] = []
@@ -716,7 +727,9 @@ def _filter_openclaw_provider_catalog(
716
727
  match = find_model_in_catalog(raw_models, model_id)
717
728
  if match is None:
718
729
  continue
719
- identities = sorted(str(value).strip().lower() for value in (model_id, str(match)) if str(value).strip())
730
+ identities = sorted(
731
+ str(value).strip().lower() for value in (model_id, str(match)) if str(value).strip()
732
+ )
720
733
  dedupe_key = identities[0] if identities else str(model_id).lower()
721
734
  if dedupe_key in seen:
722
735
  continue
@@ -747,14 +760,12 @@ def _build_openclaw_env_vars(
747
760
  openclaw_explicit_model = default_model or _resolve_env("OPENCLAW_DEFAULT_MODEL")
748
761
  generic_model_preference = _resolve_env("OPENAI_MODEL_NAME", "MODEL_NAME", "LLM_MODEL")
749
762
  model_preference = openclaw_explicit_model or generic_model_preference
750
- explicit_base_url = (
751
- model_base_url
752
- or _resolve_env("OPENCLAW_MODEL_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE")
763
+ explicit_base_url = model_base_url or _resolve_env(
764
+ "OPENCLAW_MODEL_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE"
753
765
  )
754
766
  base_url = _resolve_model_base_url(explicit_base_url)
755
- api_key = (
756
- model_api_key
757
- or _resolve_env("OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY")
767
+ api_key = model_api_key or _resolve_env(
768
+ "OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY"
758
769
  )
759
770
  model = model_preference or "glm-5.2"
760
771
  explicit_provider_id = model_provider_id or _resolve_env("OPENCLAW_MODEL_PROVIDER_ID")
@@ -762,34 +773,33 @@ def _build_openclaw_env_vars(
762
773
  if not inferred_provider_id and model and "/" in model:
763
774
  inferred_provider_id = model.split("/", 1)[0].strip()
764
775
  provider_id = inferred_provider_id or default_provider_id
765
- resolved_gateway_port = (
766
- gateway_port
767
- or _resolve_env("OPENCLAW_GATEWAY_PORT", "PORT")
768
- or "8080"
769
- )
770
- resolved_public_port = (
771
- public_port
772
- or _resolve_env("OPENCLAW_PUBLIC_PORT")
773
- or "80"
774
- )
776
+ resolved_gateway_port = gateway_port or _resolve_env("OPENCLAW_GATEWAY_PORT", "PORT") or "8080"
777
+ resolved_public_port = public_port or _resolve_env("OPENCLAW_PUBLIC_PORT") or "80"
775
778
  explicit_model_api = _resolve_env("OPENCLAW_MODEL_API")
776
779
  model_api = explicit_model_api or default_model_api
777
780
  trusted_proxy_user_header = (
778
- _resolve_env(
779
- "OPENCLAW_TRUSTED_PROXY_USER_HEADER",
780
- "OPENCLAW_GATEWAY_TRUSTED_PROXY_USER_HEADER",
781
+ (
782
+ _resolve_env(
783
+ "OPENCLAW_TRUSTED_PROXY_USER_HEADER",
784
+ "OPENCLAW_GATEWAY_TRUSTED_PROXY_USER_HEADER",
785
+ )
786
+ or DEFAULT_TRUSTED_PROXY_USER_HEADER
781
787
  )
782
- or DEFAULT_TRUSTED_PROXY_USER_HEADER
783
- ).strip().lower()
788
+ .strip()
789
+ .lower()
790
+ )
784
791
  internal_trusted_proxy_user = (
785
- _resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER")
786
- or "openclaw-backend"
792
+ _resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER") or "openclaw-backend"
787
793
  )
788
794
  internal_trusted_proxy_user_header = (
789
- _resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER_HEADER")
790
- or trusted_proxy_user_header
791
- or DEFAULT_TRUSTED_PROXY_USER_HEADER
792
- ).strip().lower()
795
+ (
796
+ _resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER_HEADER")
797
+ or trusted_proxy_user_header
798
+ or DEFAULT_TRUSTED_PROXY_USER_HEADER
799
+ )
800
+ .strip()
801
+ .lower()
802
+ )
793
803
  trusted_proxies = _normalize_csv_list(
794
804
  _resolve_env("OPENCLAW_TRUSTED_PROXIES") or "",
795
805
  default_items=DEFAULT_TRUSTED_PROXY_CIDRS,
@@ -797,7 +807,9 @@ def _build_openclaw_env_vars(
797
807
  browser_enabled = _resolve_env("OPENCLAW_BROWSER_ENABLED")
798
808
  browser_no_sandbox = _resolve_env("OPENCLAW_BROWSER_NO_SANDBOX") or "true"
799
809
  browser_headless = _resolve_env("OPENCLAW_BROWSER_HEADLESS") or "true"
800
- browser_executable = _resolve_env("OPENCLAW_BROWSER_EXECUTABLE_PATH", "OPENCLAW_BROWSER_EXECUTABLE")
810
+ browser_executable = _resolve_env(
811
+ "OPENCLAW_BROWSER_EXECUTABLE_PATH", "OPENCLAW_BROWSER_EXECUTABLE"
812
+ )
801
813
  ui_locale = _normalize_ui_locale(_resolve_env("OPENCLAW_UI_LOCALE", "LANG", "LC_ALL"))
802
814
  exec_strict_mode_raw = (
803
815
  exec_profile_overrides.get("OPENCLAW_EXEC_STRICT_MODE")
@@ -806,13 +818,21 @@ def _build_openclaw_env_vars(
806
818
  )
807
819
  exec_strict_mode = _is_truthy(exec_strict_mode_raw)
808
820
 
809
- exec_host = exec_profile_overrides.get("OPENCLAW_EXEC_HOST") or _resolve_env("OPENCLAW_EXEC_HOST") or "gateway"
821
+ exec_host = (
822
+ exec_profile_overrides.get("OPENCLAW_EXEC_HOST")
823
+ or _resolve_env("OPENCLAW_EXEC_HOST")
824
+ or "gateway"
825
+ )
810
826
  exec_security = (
811
827
  exec_profile_overrides.get("OPENCLAW_EXEC_SECURITY")
812
828
  or _resolve_env("OPENCLAW_EXEC_SECURITY")
813
829
  or ("allowlist" if exec_strict_mode else "full")
814
830
  )
815
- exec_ask = exec_profile_overrides.get("OPENCLAW_EXEC_ASK") or _resolve_env("OPENCLAW_EXEC_ASK") or "off"
831
+ exec_ask = (
832
+ exec_profile_overrides.get("OPENCLAW_EXEC_ASK")
833
+ or _resolve_env("OPENCLAW_EXEC_ASK")
834
+ or "off"
835
+ )
816
836
  exec_ask_fallback = (
817
837
  exec_profile_overrides.get("OPENCLAW_EXEC_ASK_FALLBACK")
818
838
  or _resolve_env("OPENCLAW_EXEC_ASK_FALLBACK")
@@ -831,9 +851,7 @@ def _build_openclaw_env_vars(
831
851
  exec_default_allowlist_enabled = (
832
852
  exec_profile_overrides.get("OPENCLAW_EXEC_DEFAULT_ALLOWLIST_ENABLED")
833
853
  or _resolve_env("OPENCLAW_EXEC_DEFAULT_ALLOWLIST_ENABLED")
834
- or (
835
- "true" if exec_strict_mode else "false"
836
- )
854
+ or ("true" if exec_strict_mode else "false")
837
855
  )
838
856
  exec_allowlist = _resolve_env("OPENCLAW_EXEC_ALLOWLIST")
839
857
  fs_workspace_only = (
@@ -850,10 +868,14 @@ def _build_openclaw_env_vars(
850
868
  env["OPENCLAW_GATEWAY_BIND"] = "lan"
851
869
  if gateway_auth_mode:
852
870
  env["OPENCLAW_GATEWAY_AUTH_MODE"] = gateway_auth_mode
853
- env["OPENCLAW_TRUSTED_PROXY_USER_HEADER"] = trusted_proxy_user_header or DEFAULT_TRUSTED_PROXY_USER_HEADER
871
+ env["OPENCLAW_TRUSTED_PROXY_USER_HEADER"] = (
872
+ trusted_proxy_user_header or DEFAULT_TRUSTED_PROXY_USER_HEADER
873
+ )
854
874
  env["OPENCLAW_INTERNAL_TRUSTED_PROXY_USER"] = internal_trusted_proxy_user
855
875
  env["OPENCLAW_INTERNAL_TRUSTED_PROXY_USER_HEADER"] = (
856
- internal_trusted_proxy_user_header or trusted_proxy_user_header or DEFAULT_TRUSTED_PROXY_USER_HEADER
876
+ internal_trusted_proxy_user_header
877
+ or trusted_proxy_user_header
878
+ or DEFAULT_TRUSTED_PROXY_USER_HEADER
857
879
  )
858
880
  env["OPENCLAW_TRUSTED_PROXIES"] = trusted_proxies
859
881
  env["OPENCLAW_GATEWAY_PORT"] = str(resolved_gateway_port)
@@ -899,8 +921,9 @@ def _build_openclaw_env_vars(
899
921
  _, catalog_model_id = normalized_model.split("/", 1)
900
922
  resolved_model = normalized_model
901
923
  else:
902
- catalog_model_id = normalized_model
903
- resolved_model = f"{provider_id}/{normalized_model}" if provider_id else normalized_model
924
+ resolved_model = (
925
+ f"{provider_id}/{normalized_model}" if provider_id else normalized_model
926
+ )
904
927
  if openclaw_explicit_model:
905
928
  env["OPENCLAW_DEFAULT_MODEL"] = resolved_model
906
929
  elif generic_model_preference:
@@ -1005,11 +1028,14 @@ def _normalize_openclaw_gateway_auth_env(env: dict[str, str]) -> dict[str, str]:
1005
1028
  auth_mode = raw_mode or ("token" if raw_token or raw_password else "trusted-proxy")
1006
1029
  if auth_mode == "token":
1007
1030
  if raw_token and raw_password and raw_token != raw_password:
1008
- raise ValueError("OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致")
1031
+ raise ValueError(
1032
+ "OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致"
1033
+ )
1009
1034
  shared_secret = raw_token or raw_password
1010
1035
  if not shared_secret:
1011
1036
  raise ValueError(
1012
- "OPENCLAW_GATEWAY_AUTH_MODE=token 时必须提供 OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
1037
+ "OPENCLAW_GATEWAY_AUTH_MODE=token 时必须提供 "
1038
+ "OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
1013
1039
  )
1014
1040
  normalized_env["OPENCLAW_GATEWAY_AUTH_MODE"] = "token"
1015
1041
  normalized_env["OPENCLAW_GATEWAY_TOKEN"] = shared_secret
@@ -1018,7 +1044,8 @@ def _normalize_openclaw_gateway_auth_env(env: dict[str, str]) -> dict[str, str]:
1018
1044
 
1019
1045
  if raw_token or raw_password:
1020
1046
  raise ValueError(
1021
- "仅在 OPENCLAW_GATEWAY_AUTH_MODE=token 时支持 OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
1047
+ "仅在 OPENCLAW_GATEWAY_AUTH_MODE=token 时支持 "
1048
+ "OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
1022
1049
  )
1023
1050
 
1024
1051
  normalized_env["OPENCLAW_GATEWAY_AUTH_MODE"] = auth_mode
@@ -1174,7 +1201,7 @@ async def _fetch_bootstrap_config(region: str) -> Optional[Dict[str, Any]]:
1174
1201
 
1175
1202
  try:
1176
1203
  async with AgentEngineClient(region=region) as client:
1177
- return await client.get_client_bootstrap_config(
1204
+ result = await client.get_client_bootstrap_config(
1178
1205
  product="openclaw",
1179
1206
  framework="openclaw",
1180
1207
  region=region,
@@ -1183,6 +1210,7 @@ async def _fetch_bootstrap_config(region: str) -> Optional[Dict[str, Any]]:
1183
1210
  locale=_resolve_env("OPENCLAW_UI_LOCALE", "LANG", "LC_ALL"),
1184
1211
  ignore_dry_run=True,
1185
1212
  )
1213
+ return dict(result) if isinstance(result, dict) else None
1186
1214
  except Exception as e:
1187
1215
  print_warn(f"拉取服务端默认配置失败,回退本地默认镜像: {e}")
1188
1216
  return None
@@ -1253,14 +1281,22 @@ def _flatten_agent_detail(agent: dict) -> dict:
1253
1281
  "agent_id": basic.get("agent_id") or agent.get("agent_id") or "",
1254
1282
  "name": basic.get("name") or agent.get("name") or "",
1255
1283
  "status": (basic.get("status") or agent.get("status") or "UNKNOWN").upper(),
1256
- "framework": basic.get("framework") or deploy.get("framework") or agent.get("framework") or "",
1284
+ "framework": basic.get("framework")
1285
+ or deploy.get("framework")
1286
+ or agent.get("framework")
1287
+ or "",
1257
1288
  "region": basic.get("region") or deploy.get("region") or agent.get("region") or "",
1258
- "endpoint": quick.get("public_endpoint") or quick.get("private_endpoint") or agent.get("endpoint") or "",
1289
+ "endpoint": quick.get("public_endpoint")
1290
+ or quick.get("private_endpoint")
1291
+ or agent.get("endpoint")
1292
+ or "",
1259
1293
  "artifact_path": deploy.get("artifact_path") or agent.get("artifact_path") or "",
1260
1294
  "created_at": basic.get("created_at") or agent.get("created_at") or "",
1261
1295
  "updated_at": basic.get("updated_at") or agent.get("updated_at") or "",
1262
1296
  "api_key": quick.get("api_key") or agent.get("api_key"),
1263
- "langfuse_url": (agent.get("advanced") or {}).get("observability_url") or agent.get("langfuse_trace_url") or "",
1297
+ "langfuse_url": (agent.get("advanced") or {}).get("observability_url")
1298
+ or agent.get("langfuse_trace_url")
1299
+ or "",
1264
1300
  }
1265
1301
 
1266
1302
 
@@ -1277,7 +1313,9 @@ async def _get_openclaw_detail_with_client(
1277
1313
  detail = _flatten_agent_detail(agent)
1278
1314
  framework = str(detail.get("framework") or "").strip().lower()
1279
1315
  if framework and framework != "openclaw":
1280
- raise resolution_error(f"目标 Agent 不是 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"])
1316
+ raise resolution_error(
1317
+ f"目标 Agent 不是 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"]
1318
+ )
1281
1319
  return detail
1282
1320
 
1283
1321
 
@@ -1303,10 +1341,7 @@ def _resolve_region(
1303
1341
  ) -> str:
1304
1342
  """解析 region: 显式参数 > state > 环境变量 > 默认值。"""
1305
1343
  return (
1306
- cli_region
1307
- or (state or {}).get("region")
1308
- or _resolve_env("KSYUN_REGION")
1309
- or "cn-beijing-6"
1344
+ cli_region or (state or {}).get("region") or _resolve_env("KSYUN_REGION") or "cn-beijing-6"
1310
1345
  )
1311
1346
 
1312
1347
 
@@ -1331,7 +1366,9 @@ async def _resolve_openclaw_detail_or_raise(
1331
1366
  detail = await _get_openclaw_detail_with_client(client, resolved.value)
1332
1367
 
1333
1368
  if not detail:
1334
- raise resolution_error(f"未找到 OpenClaw: {resolved.value}", hints=["agentengine openclaw list"])
1369
+ raise resolution_error(
1370
+ f"未找到 OpenClaw: {resolved.value}", hints=["agentengine openclaw list"]
1371
+ )
1335
1372
  return resolved_region, detail
1336
1373
 
1337
1374
 
@@ -1378,7 +1415,9 @@ async def _ensure_openclaw_gateway_available(
1378
1415
  await _wait_for_gateway_ready(region, detail, timeout_seconds=timeout_seconds)
1379
1416
 
1380
1417
 
1381
- def _emit_data_payload(title: str, payload: dict[str, Any], *, subtitle: Optional[str] = None) -> None:
1418
+ def _emit_data_payload(
1419
+ title: str, payload: dict[str, Any], *, subtitle: Optional[str] = None
1420
+ ) -> None:
1382
1421
  if is_json_output():
1383
1422
  emit_json(payload)
1384
1423
  return
@@ -1386,7 +1425,9 @@ def _emit_data_payload(title: str, payload: dict[str, Any], *, subtitle: Optiona
1386
1425
  console.print(json_dumps(payload), markup=False)
1387
1426
 
1388
1427
 
1389
- def _render_openclaw_dry_run(action: str, request: dict[str, Any], hints: tuple[str, ...] = ()) -> None:
1428
+ def _render_openclaw_dry_run(
1429
+ action: str, request: dict[str, Any], hints: tuple[str, ...] = ()
1430
+ ) -> None:
1390
1431
  if is_json_output():
1391
1432
  emit_json(
1392
1433
  build_dry_run_envelope(
@@ -1456,7 +1497,9 @@ def _select_openclaw_gateway_secret(
1456
1497
  or ""
1457
1498
  ).strip()
1458
1499
  if token and password and token != password:
1459
- raise click.ClickException("OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致")
1500
+ raise click.ClickException(
1501
+ "OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致"
1502
+ )
1460
1503
  if token:
1461
1504
  return "token", token
1462
1505
  if password:
@@ -1473,14 +1516,20 @@ def _mask_openclaw_secret(secret: str | None) -> str:
1473
1516
  return f"{text[:4]}****"
1474
1517
 
1475
1518
 
1476
- def _openclaw_auth_mode_from_sources(detail: dict[str, Any], state: dict[str, Any] | None = None) -> str:
1477
- return str(
1478
- detail.get("openclaw_auth_mode")
1479
- or detail.get("gateway_auth_mode")
1480
- or (state or {}).get("openclaw_auth_mode")
1481
- or (state or {}).get("gateway_auth_mode")
1482
- or ""
1483
- ).strip().lower()
1519
+ def _openclaw_auth_mode_from_sources(
1520
+ detail: dict[str, Any], state: dict[str, Any] | None = None
1521
+ ) -> str:
1522
+ return (
1523
+ str(
1524
+ detail.get("openclaw_auth_mode")
1525
+ or detail.get("gateway_auth_mode")
1526
+ or (state or {}).get("openclaw_auth_mode")
1527
+ or (state or {}).get("gateway_auth_mode")
1528
+ or ""
1529
+ )
1530
+ .strip()
1531
+ .lower()
1532
+ )
1484
1533
 
1485
1534
 
1486
1535
  def _openclaw_state_gateway_token(env_vars: dict[str, str]) -> str | None:
@@ -1488,9 +1537,7 @@ def _openclaw_state_gateway_token(env_vars: dict[str, str]) -> str | None:
1488
1537
  if str(env_vars.get("OPENCLAW_GATEWAY_AUTH_MODE") or "").strip().lower() != "token":
1489
1538
  return None
1490
1539
  token = str(
1491
- env_vars.get("OPENCLAW_GATEWAY_TOKEN")
1492
- or env_vars.get("OPENCLAW_GATEWAY_PASSWORD")
1493
- or ""
1540
+ env_vars.get("OPENCLAW_GATEWAY_TOKEN") or env_vars.get("OPENCLAW_GATEWAY_PASSWORD") or ""
1494
1541
  ).strip()
1495
1542
  return token or None
1496
1543
 
@@ -1523,7 +1570,9 @@ async def _run_weixin_remote_cli_login(
1523
1570
  if not endpoint:
1524
1571
  raise OpenClawGatewayError("OpenClaw runtime endpoint 为空,无法通过远端 CLI 执行微信登录")
1525
1572
 
1526
- print_info(f"当前 OpenClaw 未暴露微信 web login RPC,改用远端 OpenClaw CLI 登录流程({reason})")
1573
+ print_info(
1574
+ f"当前 OpenClaw 未暴露微信 web login RPC,改用远端 OpenClaw CLI 登录流程({reason})"
1575
+ )
1527
1576
  exit_code = await run_terminal_session(
1528
1577
  endpoint=endpoint,
1529
1578
  api_key=_openclaw_terminal_api_key(detail),
@@ -1592,9 +1641,8 @@ async def _wait_for_gateway_reload_after_config_apply(
1592
1641
 
1593
1642
  def _is_gateway_reload_disconnect_error(exc: Exception) -> bool:
1594
1643
  message = str(exc).lower()
1595
- return (
1596
- "websocket receive failed" in message
1597
- and ("1011" in message or "bad gateway" in message or "going away" in message)
1644
+ return "websocket receive failed" in message and (
1645
+ "1011" in message or "bad gateway" in message or "going away" in message
1598
1646
  )
1599
1647
 
1600
1648
 
@@ -1684,9 +1732,15 @@ def _is_channel_configured(
1684
1732
  accounts = channel_cfg.get("accounts")
1685
1733
  return isinstance(accounts, dict) and any(str(key).strip() for key in accounts.keys())
1686
1734
  if channel == "feishu":
1687
- return bool(str(channel_cfg.get("appId") or "").strip() and str(channel_cfg.get("appSecret") or "").strip())
1735
+ return bool(
1736
+ str(channel_cfg.get("appId") or "").strip()
1737
+ and str(channel_cfg.get("appSecret") or "").strip()
1738
+ )
1688
1739
  if channel == "wps-xiezuo":
1689
- return bool(str(channel_cfg.get("appId") or "").strip() and str(channel_cfg.get("appSecret") or "").strip())
1740
+ return bool(
1741
+ str(channel_cfg.get("appId") or "").strip()
1742
+ and str(channel_cfg.get("appSecret") or "").strip()
1743
+ )
1690
1744
  return bool(channel_cfg)
1691
1745
 
1692
1746
 
@@ -1791,7 +1845,9 @@ def _ensure_local_node_tools() -> dict[str, str]:
1791
1845
  if not node_path or not npx_path:
1792
1846
  raise resolution_error(
1793
1847
  "本地缺少 `node` 或 `npx`,飞书接入依赖官方 onboarding 工具",
1794
- hints=["先安装 Node.js,然后重试 `agentengine openclaw channel connect --channel feishu`"],
1848
+ hints=[
1849
+ "先安装 Node.js,然后重试 `agentengine openclaw channel connect --channel feishu`"
1850
+ ],
1795
1851
  )
1796
1852
  return {"node": node_path, "npx": npx_path}
1797
1853
 
@@ -1822,14 +1878,11 @@ async def _resolve_npx_cached_package_file(package_spec: str, relative_path: str
1822
1878
  text=True,
1823
1879
  )
1824
1880
  if completed.returncode != 0:
1825
- raise OpenClawGatewayError(
1826
- f"无法准备官方 npm 包缓存: {completed.stderr.strip() or completed.stdout.strip() or package_spec}"
1827
- )
1881
+ npm_error = completed.stderr.strip() or completed.stdout.strip() or package_spec
1882
+ raise OpenClawGatewayError(f"无法准备官方 npm 包缓存: {npm_error}")
1828
1883
 
1829
1884
  npm_cache_root = Path(
1830
- os.getenv("NPM_CONFIG_CACHE")
1831
- or os.getenv("npm_config_cache")
1832
- or str(Path.home() / ".npm")
1885
+ os.getenv("NPM_CONFIG_CACHE") or os.getenv("npm_config_cache") or str(Path.home() / ".npm")
1833
1886
  )
1834
1887
  pattern = f"_npx/**/node_modules/{package_name}/{relative_path}"
1835
1888
  matches = list(npm_cache_root.glob(pattern))
@@ -1847,8 +1900,7 @@ async def _run_feishu_onboarding(existing_app_id: Optional[str]) -> dict[str, An
1847
1900
  "@larksuite/openclaw-lark-tools@latest",
1848
1901
  "dist/utils/install-prompts.js",
1849
1902
  )
1850
- script_body = textwrap.dedent(
1851
- """
1903
+ script_body = textwrap.dedent("""
1852
1904
  const fs = require("fs");
1853
1905
  const { runInstallAuthFlow } = require(__INSTALL_PROMPTS_PATH__);
1854
1906
 
@@ -1860,19 +1912,26 @@ async def _run_feishu_onboarding(existing_app_id: Optional[str]) -> dict[str, An
1860
1912
  {},
1861
1913
  false,
1862
1914
  );
1863
- fs.writeFileSync(process.env.KSADK_FEISHU_RESULT_PATH, JSON.stringify(result), "utf8");
1915
+ fs.writeFileSync(
1916
+ process.env.KSADK_FEISHU_RESULT_PATH,
1917
+ JSON.stringify(result),
1918
+ "utf8",
1919
+ );
1864
1920
  } catch (error) {
1865
1921
  console.error(error);
1866
1922
  process.exit(1);
1867
1923
  }
1868
1924
  })();
1869
- """
1870
- ).replace("__INSTALL_PROMPTS_PATH__", json.dumps(str(install_prompts_path)))
1871
- with tempfile.NamedTemporaryFile("w", suffix=".cjs", delete=False, encoding="utf-8") as script_file:
1925
+ """).replace("__INSTALL_PROMPTS_PATH__", json.dumps(str(install_prompts_path)))
1926
+ with tempfile.NamedTemporaryFile(
1927
+ "w", suffix=".cjs", delete=False, encoding="utf-8"
1928
+ ) as script_file:
1872
1929
  script_file.write(script_body.strip())
1873
1930
  script_path = script_file.name
1874
1931
 
1875
- with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as result_file:
1932
+ with tempfile.NamedTemporaryFile(
1933
+ "w", suffix=".json", delete=False, encoding="utf-8"
1934
+ ) as result_file:
1876
1935
  result_path = result_file.name
1877
1936
 
1878
1937
  env = os.environ.copy()
@@ -1945,7 +2004,9 @@ def _resolve_weixin_account_id(
1945
2004
  raise click.ClickException("检测到多个微信账号,请显式传入 --account-id")
1946
2005
  if create_if_missing:
1947
2006
  return "default"
1948
- raise click.ClickException("尚未检测到微信账号,请先执行 `agentengine openclaw channel connect --channel weixin`")
2007
+ raise click.ClickException(
2008
+ "尚未检测到微信账号,请先执行 `agentengine openclaw channel connect --channel weixin`"
2009
+ )
1949
2010
 
1950
2011
 
1951
2012
  def _mutate_weixin_account_enabled(
@@ -2011,7 +2072,8 @@ def _mutate_feishu_connect_config(config: dict[str, Any], onboarding: dict[str,
2011
2072
  feishu_cfg[key] = value
2012
2073
  changed = True
2013
2074
 
2014
- user_info = onboarding.get("userInfo") if isinstance(onboarding.get("userInfo"), dict) else {}
2075
+ user_info_value = onboarding.get("userInfo")
2076
+ user_info: dict[str, Any] = dict(user_info_value) if isinstance(user_info_value, dict) else {}
2015
2077
  open_id = str(user_info.get("openId") or "").strip()
2016
2078
  if open_id:
2017
2079
  if feishu_cfg.get("dmPolicy") != "allowlist":
@@ -2105,8 +2167,12 @@ def _mutate_wps_xiezuo_connect_config(
2105
2167
  bindings = config.setdefault("bindings", [])
2106
2168
  if isinstance(bindings, list):
2107
2169
  next_bindings = [
2108
- item for item in bindings
2109
- if not (isinstance(item, dict) and item.get("match", {}).get("channel") == WPS_XIEZUO_CHANNEL_KEY)
2170
+ item
2171
+ for item in bindings
2172
+ if not (
2173
+ isinstance(item, dict)
2174
+ and item.get("match", {}).get("channel") == WPS_XIEZUO_CHANNEL_KEY
2175
+ )
2110
2176
  ]
2111
2177
  desired_binding = {
2112
2178
  "type": "route",
@@ -2130,7 +2196,9 @@ def _mutate_wps_xiezuo_enabled(
2130
2196
  ) -> bool:
2131
2197
  normalized_account = str(account_id or WPS_XIEZUO_DEFAULT_ACCOUNT_ID).strip()
2132
2198
  if normalized_account and normalized_account != WPS_XIEZUO_DEFAULT_ACCOUNT_ID:
2133
- raise click.ClickException("WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default")
2199
+ raise click.ClickException(
2200
+ "WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default"
2201
+ )
2134
2202
  changed = _ensure_plugin_enabled(config, WPS_XIEZUO_PLUGIN_ID) if enabled else False
2135
2203
  channels = config.setdefault("channels", {})
2136
2204
  channel_cfg = channels.setdefault(WPS_XIEZUO_CHANNEL_KEY, {})
@@ -2152,7 +2220,9 @@ def _check_wps_xiezuo_local_deps() -> dict[str, Any]:
2152
2220
  }
2153
2221
 
2154
2222
 
2155
- @click.group("openclaw", context_settings=CONTEXT_SETTINGS, help=build_resource_group_help(OPENCLAW_RESOURCE))
2223
+ @click.group(
2224
+ "openclaw", context_settings=CONTEXT_SETTINGS, help=build_resource_group_help(OPENCLAW_RESOURCE)
2225
+ )
2156
2226
  def openclaw():
2157
2227
  pass
2158
2228
 
@@ -2211,8 +2281,15 @@ async def _run_openclaw_repair_action(
2211
2281
  @click.option("--session", "-s", default=None, help="OpenClaw Session key")
2212
2282
  @click.option("--message", "-m", default=None, help="连接后发送的初始消息")
2213
2283
  @click.option("--thinking", default=None, help="Thinking level override")
2214
- @click.option("--history-limit", type=click.IntRange(1, 10000), default=None, help="历史条数 (默认使用 OpenClaw 默认值)")
2215
- @click.option("--timeout-ms", type=click.IntRange(1, 86400000), default=None, help="Agent timeout ms")
2284
+ @click.option(
2285
+ "--history-limit",
2286
+ type=click.IntRange(1, 10000),
2287
+ default=None,
2288
+ help="历史条数 (默认使用 OpenClaw 默认值)",
2289
+ )
2290
+ @click.option(
2291
+ "--timeout-ms", type=click.IntRange(1, 86400000), default=None, help="Agent timeout ms"
2292
+ )
2216
2293
  @click.option("--deliver", is_flag=True, help="Deliver assistant replies")
2217
2294
  @click.option("--insecure", "-k", is_flag=True, help="跳过 SSL 证书验证")
2218
2295
  @dry_run_option()
@@ -2279,10 +2356,14 @@ def tui_openclaw(
2279
2356
  elif not agent_ref and str(state.get("endpoint") or "").strip():
2280
2357
  resolved_endpoint = str(state.get("endpoint") or "").strip()
2281
2358
  else:
2282
- resolved_region, detail = asyncio.run(_resolve_openclaw_detail_or_raise(agent_ref, region=region))
2359
+ resolved_region, detail = asyncio.run(
2360
+ _resolve_openclaw_detail_or_raise(agent_ref, region=region)
2361
+ )
2283
2362
  resolved_endpoint = str(detail.get("endpoint") or "").strip()
2284
2363
  if not resolved_endpoint:
2285
- raise click.ClickException("未解析到 OpenClaw Endpoint,请传入 --endpoint 或指定 OpenClaw ID/名称")
2364
+ raise click.ClickException(
2365
+ "未解析到 OpenClaw Endpoint,请传入 --endpoint 或指定 OpenClaw ID/名称"
2366
+ )
2286
2367
 
2287
2368
  secret_kind, gateway_secret = _select_openclaw_gateway_secret(
2288
2369
  gateway_token=gateway_token,
@@ -2293,18 +2374,26 @@ def tui_openclaw(
2293
2374
  auth_mode = _openclaw_auth_mode_from_sources(detail, state)
2294
2375
  if auth_mode in {"token", "password"} and not gateway_secret:
2295
2376
  raise click.ClickException(
2296
- "当前 OpenClaw Gateway 为 token/password 模式,agentengine openclaw tui 需要 OpenClaw Gateway token/password。\n"
2377
+ "当前 OpenClaw Gateway 为 token/password 模式,"
2378
+ "agentengine openclaw tui 需要 OpenClaw Gateway token/password。\n"
2297
2379
  "请使用: agentengine openclaw tui --gateway-token <token>\n"
2298
2380
  "或设置: OPENCLAW_GATEWAY_TOKEN=<token> agentengine openclaw tui\n"
2299
- "如果部署时传过 OPENCLAW_GATEWAY_TOKEN,请重新运行部署让本地 .agentengine.state 记录该 token。\n"
2381
+ "如果部署时传过 OPENCLAW_GATEWAY_TOKEN,请重新运行部署让本地 "
2382
+ ".agentengine.state 记录该 token。\n"
2300
2383
  "注意:这里不是 AgentEngine API Key(ak-*)。"
2301
2384
  )
2302
2385
 
2303
- terminal_api_key = gateway_secret or api_key or str(detail.get("api_key") or state.get("api_key") or "").strip() or None
2386
+ terminal_api_key = (
2387
+ gateway_secret
2388
+ or api_key
2389
+ or str(detail.get("api_key") or state.get("api_key") or "").strip()
2390
+ or None
2391
+ )
2304
2392
  click.secho("🖥️ OpenClaw Native Remote TUI", fg="blue", bold=True)
2305
2393
  click.echo(f" Endpoint: {resolved_endpoint}")
2306
2394
  if gateway_secret:
2307
- click.echo(f" Runtime Auth: OpenClaw Gateway {secret_kind} {_mask_openclaw_secret(gateway_secret)}")
2395
+ masked_gateway_secret = _mask_openclaw_secret(gateway_secret)
2396
+ click.echo(f" Runtime Auth: OpenClaw Gateway {secret_kind} {masked_gateway_secret}")
2308
2397
  elif terminal_api_key:
2309
2398
  click.echo(f" Runtime Auth: Bearer {_mask_openclaw_secret(terminal_api_key)}")
2310
2399
  else:
@@ -2337,7 +2426,9 @@ def tui_openclaw(
2337
2426
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2338
2427
  @click.option("--no-open", is_flag=True, help="仅打印 URL,不自动打开浏览器")
2339
2428
  @cli_output_option()
2340
- def gateway_open(agent_ref: Optional[str], region: Optional[str], no_open: bool, output_mode: str | None):
2429
+ def gateway_open(
2430
+ agent_ref: Optional[str], region: Optional[str], no_open: bool, output_mode: str | None
2431
+ ):
2341
2432
  """打开 OpenClaw gateway Dashboard。"""
2342
2433
  _ = output_mode
2343
2434
  no_open = no_open or is_json_output()
@@ -2408,19 +2499,29 @@ def gateway_ws_url(agent_ref: Optional[str], region: Optional[str], output_mode:
2408
2499
  "auth_mode": "cookie-session",
2409
2500
  "note": "该 ws-url 依赖短链 cookie session,不承诺长期复用。",
2410
2501
  }
2411
- _emit_data_payload("OpenClaw Gateway 连接信息", payload, subtitle=str(detail.get("name") or "-"))
2502
+ _emit_data_payload(
2503
+ "OpenClaw Gateway 连接信息", payload, subtitle=str(detail.get("name") or "-")
2504
+ )
2412
2505
 
2413
2506
  try:
2414
2507
  asyncio.run(_run())
2415
2508
  except Exception as e:
2416
- _abort_openclaw_error(e, context="获取 gateway ws-url 失败", argv=["openclaw", "gateway", "ws-url"])
2509
+ _abort_openclaw_error(
2510
+ e, context="获取 gateway ws-url 失败", argv=["openclaw", "gateway", "ws-url"]
2511
+ )
2417
2512
 
2418
2513
 
2419
2514
  @openclaw_gateway.command("logs", context_settings=CONTEXT_SETTINGS)
2420
2515
  @click.argument("agent_ref", required=False)
2421
2516
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2422
2517
  @click.option("--instance", default=None, help="实例名;不填则查询全部实例")
2423
- @click.option("--log-type", type=click.Choice(["stdout", "log"], case_sensitive=False), default="stdout", show_default=True, help="日志类型")
2518
+ @click.option(
2519
+ "--log-type",
2520
+ type=click.Choice(["stdout", "log"], case_sensitive=False),
2521
+ default="stdout",
2522
+ show_default=True,
2523
+ help="日志类型",
2524
+ )
2424
2525
  @click.option("--start-time", default=None, help="开始时间,支持 Unix 毫秒或 ISO-8601")
2425
2526
  @click.option("--end-time", default=None, help="结束时间,支持 Unix 毫秒或 ISO-8601")
2426
2527
  @cli_output_option()
@@ -2465,7 +2566,9 @@ def gateway_logs(
2465
2566
  emit_json(payload)
2466
2567
  return
2467
2568
 
2468
- print_title("OpenClaw Gateway 日志", str(detail.get("name") or detail.get("agent_id") or "-"))
2569
+ print_title(
2570
+ "OpenClaw Gateway 日志", str(detail.get("name") or detail.get("agent_id") or "-")
2571
+ )
2469
2572
  print_kv("实例", str(resp.get("instance") or "all"))
2470
2573
  print_kv("日志类型", str(resp.get("log_type") or log_type))
2471
2574
  print_kv("日志条数", str(resp.get("total") or 0))
@@ -2479,13 +2582,20 @@ def gateway_logs(
2479
2582
  try:
2480
2583
  asyncio.run(_run())
2481
2584
  except Exception as e:
2482
- _abort_openclaw_error(e, context="获取 gateway 日志失败", argv=["openclaw", "gateway", "logs"])
2585
+ _abort_openclaw_error(
2586
+ e, context="获取 gateway 日志失败", argv=["openclaw", "gateway", "logs"]
2587
+ )
2483
2588
 
2484
2589
 
2485
2590
  @openclaw_gateway.command("doctor", context_settings=CONTEXT_SETTINGS)
2486
2591
  @click.argument("agent_ref", required=False)
2487
2592
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2488
- @click.option("--fix", "do_fix", is_flag=True, help="不走 gateway 内诊断,改为通过控制面执行 openclaw doctor --fix")
2593
+ @click.option(
2594
+ "--fix",
2595
+ "do_fix",
2596
+ is_flag=True,
2597
+ help="不走 gateway 内诊断,改为通过控制面执行 openclaw doctor --fix",
2598
+ )
2489
2599
  @cli_output_option()
2490
2600
  def gateway_doctor(
2491
2601
  agent_ref: Optional[str],
@@ -2519,7 +2629,7 @@ def gateway_doctor(
2519
2629
  "ws_url": info.ws_url,
2520
2630
  }
2521
2631
  )
2522
- hello = await gateway.connect()
2632
+ await gateway.connect()
2523
2633
  checks.append(
2524
2634
  {
2525
2635
  "name": "cookie_ws_handshake",
@@ -2606,7 +2716,12 @@ def openclaw_channel():
2606
2716
  @openclaw_channel.command("status", context_settings=CONTEXT_SETTINGS)
2607
2717
  @click.argument("agent_ref", required=False)
2608
2718
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2609
- @click.option("--channel", type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False), default=None, help="指定 channel")
2719
+ @click.option(
2720
+ "--channel",
2721
+ type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
2722
+ default=None,
2723
+ help="指定 channel",
2724
+ )
2610
2725
  @click.option("--probe", is_flag=True, help="触发远端 probe 刷新 channel 快照")
2611
2726
  @cli_output_option()
2612
2727
  def channel_status(
@@ -2626,7 +2741,9 @@ def channel_status(
2626
2741
  gateway = _build_gateway_client(resolved_region, detail)
2627
2742
  try:
2628
2743
  await gateway.connect()
2629
- snapshot = await gateway.channels_status(probe=probe, timeout_ms=8_000 if probe else None)
2744
+ snapshot = await gateway.channels_status(
2745
+ probe=probe, timeout_ms=8_000 if probe else None
2746
+ )
2630
2747
  finally:
2631
2748
  await gateway.close()
2632
2749
 
@@ -2640,26 +2757,67 @@ def channel_status(
2640
2757
  "snapshot": snapshot,
2641
2758
  "selected": _extract_channel_snapshot(snapshot, normalized_channel),
2642
2759
  }
2643
- _emit_data_payload("OpenClaw Channel 状态", payload, subtitle=str(detail.get("name") or "-"))
2760
+ _emit_data_payload(
2761
+ "OpenClaw Channel 状态", payload, subtitle=str(detail.get("name") or "-")
2762
+ )
2644
2763
 
2645
2764
  try:
2646
2765
  asyncio.run(_run())
2647
2766
  except Exception as e:
2648
- _abort_openclaw_error(e, context="获取 channel 状态失败", argv=["openclaw", "channel", "status"])
2767
+ _abort_openclaw_error(
2768
+ e, context="获取 channel 状态失败", argv=["openclaw", "channel", "status"]
2769
+ )
2649
2770
 
2650
2771
 
2651
- @openclaw_channel.command("connect", context_settings=CONTEXT_SETTINGS, help=OPENCLAW_CHANNEL_CONNECT_HELP)
2772
+ @openclaw_channel.command(
2773
+ "connect", context_settings=CONTEXT_SETTINGS, help=OPENCLAW_CHANNEL_CONNECT_HELP
2774
+ )
2652
2775
  @click.argument("agent_ref", required=False)
2653
2776
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2654
- @click.option("--channel", "channel_name", type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False), required=True, help="目标 channel")
2777
+ @click.option(
2778
+ "--channel",
2779
+ "channel_name",
2780
+ type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
2781
+ required=True,
2782
+ help="目标 channel",
2783
+ )
2655
2784
  @click.option("--open-qr", is_flag=True, help="仅微信:在本地浏览器额外打开二维码链接")
2656
2785
  @click.option("--app-id", "wps_xiezuo_app_id", default=None, help="仅 WPS 协作:开放平台应用 ID")
2657
- @click.option("--app-secret", "wps_xiezuo_app_secret", default=None, help="仅 WPS 协作:开放平台应用密钥")
2658
- @click.option("--account-id", "wps_xiezuo_account_id", default="default", help="仅 WPS 协作:账号 ID;当前只支持 default")
2659
- @click.option("--agent-id", "wps_xiezuo_agent_id", default="main", help="仅 WPS 协作:消息路由到的 OpenClaw agentId")
2660
- @click.option("--dm-policy", "wps_xiezuo_dm_policy", type=click.Choice(("disabled", "open", "pairing", "allowlist")), default="pairing", help="仅 WPS 协作:私聊策略,默认 pairing")
2661
- @click.option("--group-policy", "wps_xiezuo_group_policy", type=click.Choice(("open", "allowlist")), default="open", help="仅 WPS 协作:群聊策略,默认 open")
2662
- @click.option("--base-url", "wps_xiezuo_base_url", default="https://openapi.wps.cn", help="仅 WPS 协作:WPS OpenAPI 基础地址")
2786
+ @click.option(
2787
+ "--app-secret", "wps_xiezuo_app_secret", default=None, help="仅 WPS 协作:开放平台应用密钥"
2788
+ )
2789
+ @click.option(
2790
+ "--account-id",
2791
+ "wps_xiezuo_account_id",
2792
+ default="default",
2793
+ help="仅 WPS 协作:账号 ID;当前只支持 default",
2794
+ )
2795
+ @click.option(
2796
+ "--agent-id",
2797
+ "wps_xiezuo_agent_id",
2798
+ default="main",
2799
+ help="仅 WPS 协作:消息路由到的 OpenClaw agentId",
2800
+ )
2801
+ @click.option(
2802
+ "--dm-policy",
2803
+ "wps_xiezuo_dm_policy",
2804
+ type=click.Choice(("disabled", "open", "pairing", "allowlist")),
2805
+ default="pairing",
2806
+ help="仅 WPS 协作:私聊策略,默认 pairing",
2807
+ )
2808
+ @click.option(
2809
+ "--group-policy",
2810
+ "wps_xiezuo_group_policy",
2811
+ type=click.Choice(("open", "allowlist")),
2812
+ default="open",
2813
+ help="仅 WPS 协作:群聊策略,默认 open",
2814
+ )
2815
+ @click.option(
2816
+ "--base-url",
2817
+ "wps_xiezuo_base_url",
2818
+ default="https://openapi.wps.cn",
2819
+ help="仅 WPS 协作:WPS OpenAPI 基础地址",
2820
+ )
2663
2821
  def channel_connect(
2664
2822
  agent_ref: Optional[str],
2665
2823
  region: Optional[str],
@@ -2779,13 +2937,18 @@ def channel_connect(
2779
2937
  if normalized_channel == "wps-xiezuo":
2780
2938
  app_id = str(wps_xiezuo_app_id or "").strip()
2781
2939
  app_secret = str(wps_xiezuo_app_secret or "").strip()
2782
- account_id = str(wps_xiezuo_account_id or WPS_XIEZUO_DEFAULT_ACCOUNT_ID).strip() or WPS_XIEZUO_DEFAULT_ACCOUNT_ID
2940
+ account_id = (
2941
+ str(wps_xiezuo_account_id or WPS_XIEZUO_DEFAULT_ACCOUNT_ID).strip()
2942
+ or WPS_XIEZUO_DEFAULT_ACCOUNT_ID
2943
+ )
2783
2944
  if not app_id:
2784
2945
  raise click.ClickException("连接 WPS 协作 channel 必须提供 --app-id")
2785
2946
  if not app_secret:
2786
2947
  raise click.ClickException("连接 WPS 协作 channel 必须提供 --app-secret")
2787
2948
  if account_id != WPS_XIEZUO_DEFAULT_ACCOUNT_ID:
2788
- raise click.ClickException("WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default")
2949
+ raise click.ClickException(
2950
+ "WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default"
2951
+ )
2789
2952
  apply_gateway = _build_gateway_client(resolved_region, detail)
2790
2953
  changed = False
2791
2954
  try:
@@ -2800,7 +2963,8 @@ def channel_connect(
2800
2963
  agent_id=str(wps_xiezuo_agent_id or "main").strip() or "main",
2801
2964
  dm_policy=wps_xiezuo_dm_policy,
2802
2965
  group_policy=wps_xiezuo_group_policy,
2803
- base_url=str(wps_xiezuo_base_url or "https://openapi.wps.cn").strip() or "https://openapi.wps.cn",
2966
+ base_url=str(wps_xiezuo_base_url or "https://openapi.wps.cn").strip()
2967
+ or "https://openapi.wps.cn",
2804
2968
  )
2805
2969
  if changed:
2806
2970
  await _config_apply_and_wait_for_reload(
@@ -2842,9 +3006,12 @@ def channel_connect(
2842
3006
  await bootstrap_gateway.close()
2843
3007
 
2844
3008
  config, _ = _extract_config_state(cfg_snapshot)
2845
- existing_app_id = str(
2846
- ((config.get("channels") or {}).get(FEISHU_CHANNEL_KEY) or {}).get("appId") or ""
2847
- ).strip() or None
3009
+ existing_app_id = (
3010
+ str(
3011
+ ((config.get("channels") or {}).get(FEISHU_CHANNEL_KEY) or {}).get("appId") or ""
3012
+ ).strip()
3013
+ or None
3014
+ )
2848
3015
  onboarding = await _run_feishu_onboarding(existing_app_id)
2849
3016
  changed_config = copy.deepcopy(config)
2850
3017
  changed = _mutate_feishu_connect_config(changed_config, onboarding)
@@ -2885,7 +3052,9 @@ def channel_connect(
2885
3052
  try:
2886
3053
  asyncio.run(_run())
2887
3054
  except Exception as e:
2888
- _abort_openclaw_error(e, context="channel connect 失败", argv=["openclaw", "channel", "connect"])
3055
+ _abort_openclaw_error(
3056
+ e, context="channel connect 失败", argv=["openclaw", "channel", "connect"]
3057
+ )
2889
3058
 
2890
3059
 
2891
3060
  def _run_channel_toggle_command(
@@ -2954,18 +3123,28 @@ def _run_channel_toggle_command(
2954
3123
  "enabled": enabled,
2955
3124
  "status": _extract_channel_snapshot(snapshot, normalized_channel),
2956
3125
  }
2957
- _emit_data_payload(f"OpenClaw Channel {action.title()}", payload, subtitle=normalized_channel)
3126
+ _emit_data_payload(
3127
+ f"OpenClaw Channel {action.title()}", payload, subtitle=normalized_channel
3128
+ )
2958
3129
 
2959
3130
  try:
2960
3131
  asyncio.run(_run())
2961
3132
  except Exception as e:
2962
- _abort_openclaw_error(e, context=f"channel {action} 失败", argv=["openclaw", "channel", action])
3133
+ _abort_openclaw_error(
3134
+ e, context=f"channel {action} 失败", argv=["openclaw", "channel", action]
3135
+ )
2963
3136
 
2964
3137
 
2965
3138
  @openclaw_channel.command("enable", context_settings=CONTEXT_SETTINGS)
2966
3139
  @click.argument("agent_ref", required=False)
2967
3140
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2968
- @click.option("--channel", "channel_name", type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False), required=True, help="目标 channel")
3141
+ @click.option(
3142
+ "--channel",
3143
+ "channel_name",
3144
+ type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
3145
+ required=True,
3146
+ help="目标 channel",
3147
+ )
2969
3148
  @click.option("--account-id", default=None, help="账号 ID;飞书 V1 仅支持 default")
2970
3149
  def channel_enable(
2971
3150
  agent_ref: Optional[str],
@@ -2987,7 +3166,13 @@ def channel_enable(
2987
3166
  @openclaw_channel.command("disable", context_settings=CONTEXT_SETTINGS)
2988
3167
  @click.argument("agent_ref", required=False)
2989
3168
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
2990
- @click.option("--channel", "channel_name", type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False), required=True, help="目标 channel")
3169
+ @click.option(
3170
+ "--channel",
3171
+ "channel_name",
3172
+ type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
3173
+ required=True,
3174
+ help="目标 channel",
3175
+ )
2991
3176
  @click.option("--account-id", default=None, help="账号 ID;飞书 V1 仅支持 default")
2992
3177
  def channel_disable(
2993
3178
  agent_ref: Optional[str],
@@ -3009,9 +3194,16 @@ def channel_disable(
3009
3194
  @openclaw_channel.command("doctor", context_settings=CONTEXT_SETTINGS)
3010
3195
  @click.argument("agent_ref", required=False)
3011
3196
  @region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
3012
- @click.option("--channel", type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False), default=None, help="指定 channel")
3197
+ @click.option(
3198
+ "--channel",
3199
+ type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
3200
+ default=None,
3201
+ help="指定 channel",
3202
+ )
3013
3203
  @cli_output_option()
3014
- def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Optional[str], output_mode: str | None):
3204
+ def channel_doctor(
3205
+ agent_ref: Optional[str], region: Optional[str], channel: Optional[str], output_mode: str | None
3206
+ ):
3015
3207
  """检查 channel 接入前置条件。"""
3016
3208
  _ = output_mode
3017
3209
  normalized_channel = str(channel).lower() if channel else None
@@ -3028,20 +3220,28 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3028
3220
  gateway = _build_gateway_client(resolved_region, detail)
3029
3221
  try:
3030
3222
  info = await gateway.build_access_info()
3031
- checks.append({"name": "dashboard_short_link", "ok": True, "dashboard_url": info.access_url})
3223
+ checks.append(
3224
+ {"name": "dashboard_short_link", "ok": True, "dashboard_url": info.access_url}
3225
+ )
3032
3226
  await gateway.connect()
3033
3227
  methods = gateway.methods
3034
3228
  checks.append({"name": "cookie_ws_handshake", "ok": True, "methods": len(methods)})
3035
3229
  snapshot = await gateway.channels_status(probe=False)
3036
3230
  config_snapshot = await gateway.config_get()
3037
- config = config_snapshot.get("config") if isinstance(config_snapshot.get("config"), dict) else {}
3231
+ config = (
3232
+ config_snapshot.get("config")
3233
+ if isinstance(config_snapshot.get("config"), dict)
3234
+ else {}
3235
+ )
3038
3236
  except Exception as exc:
3039
3237
  checks.append({"name": "gateway_connectivity", "ok": False, "error": str(exc)})
3040
3238
  finally:
3041
3239
  await gateway.close()
3042
3240
 
3043
3241
  channels_to_check = [normalized_channel] if normalized_channel else list(OPENCLAW_CHANNELS)
3044
- plugin_entries = ((config.get("plugins") or {}).get("entries") or {}) if isinstance(config, dict) else {}
3242
+ plugin_entries = (
3243
+ ((config.get("plugins") or {}).get("entries") or {}) if isinstance(config, dict) else {}
3244
+ )
3045
3245
  for item in channels_to_check:
3046
3246
  selected_snapshot = _extract_channel_snapshot(snapshot, item)
3047
3247
  configured = _is_channel_configured(item, config=config, snapshot=selected_snapshot)
@@ -3059,7 +3259,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3059
3259
  name="weixin_status_snapshot",
3060
3260
  available=selected_snapshot is not None,
3061
3261
  configured=configured,
3062
- connect_required_message="首次连接前微信状态快照可能为空,执行 channel connect 后会自动补齐",
3262
+ connect_required_message=(
3263
+ "首次连接前微信状态快照可能为空," "执行 channel connect 后会自动补齐"
3264
+ ),
3063
3265
  )
3064
3266
  )
3065
3267
  checks.append(
@@ -3067,7 +3269,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3067
3269
  name="weixin_qr_rpc",
3068
3270
  available="web.login.start" in methods and "web.login.wait" in methods,
3069
3271
  configured=configured,
3070
- connect_required_message="首次连接会先自动启用 bundled weixin plugin,然后再暴露扫码 RPC",
3272
+ connect_required_message=(
3273
+ "首次连接会先自动启用 bundled weixin plugin," "然后再暴露扫码 RPC"
3274
+ ),
3071
3275
  connect_required_ok=False,
3072
3276
  required_methods=["web.login.start", "web.login.wait"],
3073
3277
  )
@@ -3085,7 +3289,10 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3085
3289
  name="feishu_status_snapshot",
3086
3290
  available=selected_snapshot is not None,
3087
3291
  configured=configured,
3088
- connect_required_message="飞书尚未完成 connect/onboarding,首次接入前不会出现在 channel snapshot 中",
3292
+ connect_required_message=(
3293
+ "飞书尚未完成 connect/onboarding,"
3294
+ "首次接入前不会出现在 channel snapshot 中"
3295
+ ),
3089
3296
  )
3090
3297
  )
3091
3298
  node_path = shutil.which("node")
@@ -3111,7 +3318,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3111
3318
  name="wps_xiezuo_status_snapshot",
3112
3319
  available=selected_snapshot is not None,
3113
3320
  configured=configured,
3114
- connect_required_message="WPS 协作尚未完成配置,首次 connect 前不会出现在 channel snapshot 中",
3321
+ connect_required_message=(
3322
+ "WPS 协作尚未完成配置," "首次 connect 前不会出现在 channel snapshot 中"
3323
+ ),
3115
3324
  )
3116
3325
  )
3117
3326
  dep_check = _check_wps_xiezuo_local_deps()
@@ -3134,15 +3343,20 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3134
3343
 
3135
3344
  try:
3136
3345
  payload = asyncio.run(_run())
3137
- _emit_data_payload("OpenClaw Channel Doctor", payload, subtitle=str(payload.get("name") or "-"))
3346
+ _emit_data_payload(
3347
+ "OpenClaw Channel Doctor", payload, subtitle=str(payload.get("name") or "-")
3348
+ )
3138
3349
  except Exception as e:
3139
- _abort_openclaw_error(e, context="channel doctor 执行失败", argv=["openclaw", "channel", "doctor"])
3350
+ _abort_openclaw_error(
3351
+ e, context="channel doctor 执行失败", argv=["openclaw", "channel", "doctor"]
3352
+ )
3140
3353
 
3141
3354
 
3142
3355
  @openclaw.command("deploy", context_settings=CONTEXT_SETTINGS)
3143
3356
  @click.option("--name", "-n", default=None, help="OpenClaw 名称 (默认: openclaw-gateway)")
3144
3357
  @click.option(
3145
- "--region", "-r",
3358
+ "--region",
3359
+ "-r",
3146
3360
  default="cn-beijing-6",
3147
3361
  envvar="KSYUN_REGION",
3148
3362
  help="部署区域 (默认: cn-beijing-6)",
@@ -3154,7 +3368,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3154
3368
  help="安全预设: relaxed | strict | strictest (安全测试建议 strictest)",
3155
3369
  )
3156
3370
  @click.option("--strict-mode", "security_profile", flag_value="strict", help="快捷开启严格模式")
3157
- @click.option("--strictest", "security_profile", flag_value="strictest", help="快捷开启最严格安全模式")
3371
+ @click.option(
3372
+ "--strictest", "security_profile", flag_value="strictest", help="快捷开启最严格安全模式"
3373
+ )
3158
3374
  @click.option(
3159
3375
  "--image",
3160
3376
  default=None,
@@ -3179,7 +3395,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
3179
3395
  help="额外透传自定义环境变量,格式 KEY=VALUE,可重复传入",
3180
3396
  )
3181
3397
  @click.option("--storage-size-gi", type=int, default=20, show_default=True, help="PVC 容量(Gi)")
3182
- @click.option("--storage-mount-path", default=None, help="PVC 挂载目录(默认: /home/node/.openclaw)")
3398
+ @click.option(
3399
+ "--storage-mount-path", default=None, help="PVC 挂载目录(默认: /home/node/.openclaw)"
3400
+ )
3183
3401
  @click.option("--no-storage", is_flag=True, help="禁用默认 PVC 挂载")
3184
3402
  @network_options
3185
3403
  @dry_run_option("仅显示请求,不实际部署")
@@ -3222,7 +3440,8 @@ def deploy(
3222
3440
  # 一键创建最严格实例(适合安全测试)
3223
3441
  agentengine openclaw deploy --security-profile strictest
3224
3442
  # 显式指定模型
3225
- agentengine openclaw deploy --model-base-url https://api.example.com/v1 --model-api-key sk-xxx
3443
+ agentengine openclaw deploy --model-base-url https://api.example.com/v1 \
3444
+ --model-api-key sk-xxx
3226
3445
  # 使用自定义镜像
3227
3446
  agentengine openclaw deploy --image ghcr.io/my-org/openclaw:v2
3228
3447
  # 透传业务自定义环境变量
@@ -3316,10 +3535,11 @@ async def _deploy_openclaw(
3316
3535
  dry_run: bool,
3317
3536
  ):
3318
3537
  """异步部署 OpenClaw"""
3319
- from ksadk.api import AgentEngineClient
3320
- from ksadk.deployment.state import load_state, save_state, clear_state
3321
3538
  from dotenv import dotenv_values
3322
3539
 
3540
+ from ksadk.api import AgentEngineClient
3541
+ from ksadk.deployment.state import clear_state, load_state, save_state
3542
+
3323
3543
  # 自动加载当前目录 .env(仅补充未导出的变量,不覆盖已导出的 shell 环境)
3324
3544
  project_dir = Path(".").resolve()
3325
3545
  env_file = project_dir / ".env"
@@ -3377,13 +3597,15 @@ async def _deploy_openclaw(
3377
3597
  env_vars.update(custom_env_vars)
3378
3598
  env_vars = _normalize_openclaw_gateway_auth_env(env_vars)
3379
3599
  if not str(env_vars.get("OPENCLAW_MODEL_CATALOG_JSON") or "").strip():
3380
- catalog_api_base = (
3381
- model_base_url
3382
- or _resolve_env("OPENCLAW_MODEL_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE", "LLM_API_BASE", "MODEL_API_BASE")
3600
+ catalog_api_base = model_base_url or _resolve_env(
3601
+ "OPENCLAW_MODEL_BASE_URL",
3602
+ "OPENAI_BASE_URL",
3603
+ "OPENAI_API_BASE",
3604
+ "LLM_API_BASE",
3605
+ "MODEL_API_BASE",
3383
3606
  )
3384
- catalog_api_key = (
3385
- model_api_key
3386
- or _resolve_env("OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY")
3607
+ catalog_api_key = model_api_key or _resolve_env(
3608
+ "OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY"
3387
3609
  )
3388
3610
  provider_catalog = await fetch_provider_model_catalog(
3389
3611
  api_base=catalog_api_base,
@@ -3391,7 +3613,9 @@ async def _deploy_openclaw(
3391
3613
  )
3392
3614
  selected_catalog = _filter_openclaw_provider_catalog(env_vars, provider_catalog)
3393
3615
  if _apply_openclaw_provider_model_catalog(env_vars, selected_catalog):
3394
- print_info(f"已从模型服务 /v1/models 同步 OpenClaw 模型元数据: {len(selected_catalog)} 个")
3616
+ print_info(
3617
+ f"已从模型服务 /v1/models 同步 OpenClaw 模型元数据: {len(selected_catalog)} 个"
3618
+ )
3395
3619
  memory_config = _build_openclaw_memory_config(
3396
3620
  memory_system=memory_system,
3397
3621
  mem0_instance_id=mem0_instance_id,
@@ -3445,7 +3669,9 @@ async def _deploy_openclaw(
3445
3669
  if storage_config:
3446
3670
  request_data["storage"] = storage_config
3447
3671
  if existing_agent_id and include_storage_on_update and no_storage:
3448
- print_warn("更新已有 OpenClaw 时 `--no-storage` 不会删除服务端既有挂盘配置;默认保留已有配置。")
3672
+ print_warn(
3673
+ "更新已有 OpenClaw 时 `--no-storage` 不会删除服务端既有挂盘配置;默认保留已有配置。"
3674
+ )
3449
3675
  network_payload = build_network_payload(
3450
3676
  enable_public_access=enable_public_access,
3451
3677
  enable_vpc_access=enable_vpc_access,
@@ -3456,8 +3682,12 @@ async def _deploy_openclaw(
3456
3682
  region=region,
3457
3683
  dry_run=dry_run,
3458
3684
  )
3459
- if network_payload:
3460
- request_data["network"] = network_payload
3685
+ # Create 默认开公网;update 使用原始 payload,None 表示保留现有配置。
3686
+ create_network_payload = dict(network_payload) if network_payload is not None else {}
3687
+ if "enable_public_access" not in create_network_payload:
3688
+ create_network_payload["enable_public_access"] = True
3689
+ if create_network_payload:
3690
+ request_data["network"] = create_network_payload
3461
3691
 
3462
3692
  # 镜像凭证:按目标镜像地址判断仓库类型,避免企业版/第三方误用 KSYUN_ACCOUNT_ID。
3463
3693
  image_credential = None
@@ -3473,12 +3703,15 @@ async def _deploy_openclaw(
3473
3703
  request_data["image_credential"] = image_credential
3474
3704
  elif kcr_password and not kcr_username and registry_kind != "personal_kcr":
3475
3705
  print_warn(
3476
- f"检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,已忽略{registry_kind_label(registry_kind)}镜像凭证;"
3706
+ "检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,已忽略"
3707
+ f"{registry_kind_label(registry_kind)}镜像凭证;"
3477
3708
  "企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD"
3478
3709
  )
3479
3710
  elif "/agentengine-public/" not in image_ref:
3480
3711
  if registry_kind == "personal_kcr":
3481
- print_warn("未配置个人版 KCR 镜像凭证 (KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取")
3712
+ print_warn(
3713
+ "未配置个人版 KCR 镜像凭证 (KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取"
3714
+ )
3482
3715
  else:
3483
3716
  print_warn(
3484
3717
  f"未配置{registry_kind_label(registry_kind)}镜像凭证 "
@@ -3534,12 +3767,13 @@ async def _deploy_openclaw(
3534
3767
  except Exception as update_err:
3535
3768
  err_msg = str(update_err)
3536
3769
  not_found = (
3537
- "code: 404" in err_msg.lower()
3538
- or "agent not found" in err_msg.lower()
3770
+ "code: 404" in err_msg.lower() or "agent not found" in err_msg.lower()
3539
3771
  )
3540
3772
  if not not_found:
3541
3773
  raise
3542
- print_warn(f"本地状态失效 ({existing_agent_id}),将自动回退为新建: {update_err}")
3774
+ print_warn(
3775
+ f"本地状态失效 ({existing_agent_id}),将自动回退为新建: {update_err}"
3776
+ )
3543
3777
  cleared = clear_state(project_dir, key=existing_agent_id)
3544
3778
  if cleared:
3545
3779
  print_info("已清理失效的 .agentengine.state")
@@ -3564,10 +3798,12 @@ async def _deploy_openclaw(
3564
3798
  attempts=12,
3565
3799
  interval_seconds=5,
3566
3800
  include_api_key=True,
3567
- detail_fetcher=lambda agent_ref, include_api_key: _get_openclaw_detail_with_client(
3568
- client,
3569
- agent_ref,
3570
- include_api_key=include_api_key,
3801
+ detail_fetcher=lambda agent_ref, include_api_key: (
3802
+ _get_openclaw_detail_with_client(
3803
+ client,
3804
+ agent_ref,
3805
+ include_api_key=include_api_key,
3806
+ )
3571
3807
  ),
3572
3808
  suppress_transient_not_found_log=True,
3573
3809
  )
@@ -3579,8 +3815,7 @@ async def _deploy_openclaw(
3579
3815
  else:
3580
3816
  print_warn("实例创建中,稍后使用 'agentengine openclaw list' 查看")
3581
3817
  elif agent_id and (
3582
- not str(endpoint or "").strip()
3583
- or not str(api_key or "").strip()
3818
+ not str(endpoint or "").strip() or not str(api_key or "").strip()
3584
3819
  ):
3585
3820
  latest = await get_latest_agent_access(
3586
3821
  client,
@@ -3590,10 +3825,12 @@ async def _deploy_openclaw(
3590
3825
  initial_delay_seconds=2,
3591
3826
  require_complete_access=True,
3592
3827
  include_api_key=True,
3593
- detail_fetcher=lambda agent_ref, include_api_key: _get_openclaw_detail_with_client(
3594
- client,
3595
- agent_ref,
3596
- include_api_key=include_api_key,
3828
+ detail_fetcher=lambda agent_ref, include_api_key: (
3829
+ _get_openclaw_detail_with_client(
3830
+ client,
3831
+ agent_ref,
3832
+ include_api_key=include_api_key,
3833
+ )
3597
3834
  ),
3598
3835
  suppress_transient_not_found_log=True,
3599
3836
  )
@@ -3623,11 +3860,13 @@ async def _deploy_openclaw(
3623
3860
  state_payload["openclaw_gateway_token"] = state_gateway_token
3624
3861
  save_state(project_dir, state_payload)
3625
3862
 
3626
- # 仅在更新已有实例时回读一次状态;新建时底层可能尚未落库,立即按 ID 查询会产生误导性报错。
3863
+ # 更新已有实例才回读;新建后立即查询可能早于底层落库。
3627
3864
  if updated_existing_agent and agent_id:
3628
3865
  try:
3629
3866
  latest = await client.get_agent(agent_id=agent_id, include_api_key=False)
3630
- latest_status = str(((latest.get("basic") or {}).get("status") or "")).upper() or None
3867
+ latest_status = (
3868
+ str(((latest.get("basic") or {}).get("status") or "")).upper() or None
3869
+ )
3631
3870
  except Exception:
3632
3871
  latest_status = None
3633
3872
 
@@ -3668,7 +3907,9 @@ def list_openclaws(region: str, page: int, size: int, dry_run: bool, output_mode
3668
3907
 
3669
3908
  async def _list():
3670
3909
  async with AgentEngineClient(region=region, dry_run=dry_run) as client:
3671
- resp = await client.list_agents(region=region, framework="openclaw", page=page, page_size=size)
3910
+ resp = await client.list_agents(
3911
+ region=region, framework="openclaw", page=page, page_size=size
3912
+ )
3672
3913
  agents = resp.get("agents", []) or []
3673
3914
  total = int(resp.get("total") or len(agents))
3674
3915
  rows = []
@@ -3761,7 +4002,9 @@ def status(agent_ref: Optional[str], region: Optional[str], dry_run: bool, outpu
3761
4002
  agent = await client.get_agent(name=agent_ref)
3762
4003
 
3763
4004
  if not agent:
3764
- raise resolution_error(f"未找到 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"])
4005
+ raise resolution_error(
4006
+ f"未找到 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"]
4007
+ )
3765
4008
 
3766
4009
  detail = _flatten_agent_detail(agent)
3767
4010
  status_val = detail.get("status", "UNKNOWN")
@@ -3777,7 +4020,11 @@ def status(agent_ref: Optional[str], region: Optional[str], dry_run: bool, outpu
3777
4020
  ("框架", str(detail.get("framework") or "-"), None),
3778
4021
  ("区域", str(detail.get("region") or region), None),
3779
4022
  ("Endpoint", str(detail.get("endpoint") or "N/A"), "#58a6ff"),
3780
- ("Langfuse", str(detail.get("langfuse_url") or "-"), "#58a6ff" if detail.get("langfuse_url") else None),
4023
+ (
4024
+ "Langfuse",
4025
+ str(detail.get("langfuse_url") or "-"),
4026
+ "#58a6ff" if detail.get("langfuse_url") else None,
4027
+ ),
3781
4028
  ("镜像", str(detail.get("artifact_path") or "-"), None),
3782
4029
  ("创建时间", created_at_display, None),
3783
4030
  ("更新时间", updated_at_display, None),
@@ -3840,6 +4087,7 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
3840
4087
 
3841
4088
  # 清理本地状态
3842
4089
  from ksadk.deployment.state import clear_state
4090
+
3843
4091
  try:
3844
4092
  removed = clear_state(Path("."), key=agent_ref)
3845
4093
  if removed:
@@ -3862,7 +4110,7 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
3862
4110
  "failed": failed_refs,
3863
4111
  }
3864
4112
 
3865
- dry_run_kwargs = {"dry_run": dry_run}
4113
+ dry_run_kwargs: dict[str, Any] = {"dry_run": dry_run}
3866
4114
  if is_json_output():
3867
4115
  dry_run_kwargs.update(
3868
4116
  dry_run_resource="openclaw",
@@ -3900,7 +4148,13 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
3900
4148
  @confirm_options()
3901
4149
  @dry_run_option()
3902
4150
  @cli_output_option()
3903
- def delete(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run: bool, output_mode: str | None):
4151
+ def delete(
4152
+ agent_refs: tuple[str, ...],
4153
+ region: str,
4154
+ assume_yes: bool,
4155
+ dry_run: bool,
4156
+ output_mode: str | None,
4157
+ ):
3904
4158
  """删除 OpenClaw 实例。"""
3905
4159
  _ = output_mode
3906
4160
  _delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)
@@ -3912,7 +4166,13 @@ def delete(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run:
3912
4166
  @confirm_options()
3913
4167
  @dry_run_option()
3914
4168
  @cli_output_option()
3915
- def destroy(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run: bool, output_mode: str | None):
4169
+ def destroy(
4170
+ agent_refs: tuple[str, ...],
4171
+ region: str,
4172
+ assume_yes: bool,
4173
+ dry_run: bool,
4174
+ output_mode: str | None,
4175
+ ):
3916
4176
  """删除 OpenClaw 实例。"""
3917
4177
  _ = output_mode
3918
4178
  _delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)