agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__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.
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
- agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +180 -11
- ksadk/a2a/bootstrap.py +332 -0
- ksadk/a2a/card.py +15 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +1056 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +361 -137
- ksadk/a2a/external_transport.py +516 -0
- ksadk/a2a/identity.py +304 -0
- ksadk/a2a/ids.py +22 -0
- ksadk/a2a/langgraph.py +285 -0
- ksadk/a2a/resume_store.py +201 -0
- ksadk/a2a/routes.py +112 -0
- ksadk/a2a/server.py +102 -62
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +1122 -0
- ksadk/a2a/task_adapter.py +423 -0
- ksadk/a2a/task_event_dispatcher.py +167 -0
- ksadk/a2a/task_event_outbox.py +326 -0
- ksadk/a2a/task_store.py +206 -0
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/__init__.py +27 -0
- ksadk/a2ui/core.py +186 -0
- ksadk/a2ui/models.py +166 -0
- ksadk/a2ui/renderer.py +198 -0
- ksadk/agui/__init__.py +9 -0
- ksadk/agui/a2ui_projection.py +92 -0
- ksadk/agui/agent.py +872 -0
- ksadk/agui/config.py +92 -0
- ksadk/agui/routes.py +47 -0
- ksadk/api/client.py +238 -204
- ksadk/api/environment.py +27 -0
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +375 -270
- ksadk/builders/container_builder.py +240 -200
- ksadk/builders/framework_requirements.py +31 -12
- ksadk/builders/ks3_uploader.py +60 -29
- ksadk/builders/managed_runtime_builder.py +173 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +97 -171
- ksadk/cli/cmd_a2a.py +255 -84
- ksadk/cli/cmd_agent.py +19 -5
- ksadk/cli/cmd_build.py +158 -29
- ksadk/cli/cmd_completion.py +47 -35
- ksadk/cli/cmd_config.py +267 -198
- ksadk/cli/cmd_create.py +507 -740
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +135 -59
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_eval.py +386 -0
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +313 -135
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +76 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +564 -268
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +58 -73
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_studio.py +110 -0
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +95 -18
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +7 -4
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +4 -3
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +41 -19
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +11 -3
- ksadk/cli/workflow_common.py +52 -18
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +1042 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +940 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/kop_client.py +232 -0
- ksadk/common/llm_utils.py +17 -13
- ksadk/compat/__init__.py +5 -0
- ksadk/compat/adk_compat.py +200 -0
- ksadk/compat/copilotkit_a2ui.py +330 -0
- ksadk/configs/__init__.py +12 -14
- ksadk/configs/env_registry.py +661 -116
- ksadk/configs/global_config.py +3 -2
- ksadk/configs/settings.py +133 -209
- ksadk/conversations/__init__.py +85 -92
- ksadk/conversations/attachment_storage.py +16 -11
- ksadk/conversations/attachments.py +44 -36
- ksadk/conversations/compaction_pipeline.py +12 -12
- ksadk/conversations/compaction_prompt.py +9 -5
- ksadk/conversations/context.py +221 -6
- ksadk/conversations/message_projection.py +438 -51
- ksadk/conversations/model_context.py +12 -6
- ksadk/conversations/model_options.py +0 -1
- ksadk/conversations/normalize.py +20 -16
- ksadk/conversations/reasoning_markup.py +1 -1
- ksadk/conversations/runtime.py +339 -5255
- ksadk/conversations/runtime_compaction.py +333 -0
- ksadk/conversations/runtime_constants.py +27 -0
- ksadk/conversations/runtime_governance.py +185 -0
- ksadk/conversations/runtime_input.py +660 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +367 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +426 -0
- ksadk/conversations/runtime_resume.py +836 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +700 -0
- ksadk/conversations/semantic_summary.py +17 -5
- ksadk/conversations/session_title.py +6 -3
- ksadk/deployment/__init__.py +15 -16
- ksadk/deployment/agent_access.py +12 -23
- ksadk/deployment/base.py +51 -38
- ksadk/deployment/managed_runtime.py +33 -0
- ksadk/deployment/manager.py +85 -71
- ksadk/deployment/providers/serverless.py +379 -210
- ksadk/deployment/registry.py +38 -32
- ksadk/deployment/state.py +11 -11
- ksadk/deployment/ui_config.py +1 -1
- ksadk/detection/__init__.py +1 -1
- ksadk/detection/detector.py +146 -83
- ksadk/detection/mcp_detector.py +9 -6
- ksadk/evaluation/__init__.py +84 -0
- ksadk/evaluation/a2a_adapter.py +444 -0
- ksadk/evaluation/adapters.py +54 -0
- ksadk/evaluation/contracts.py +409 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +461 -0
- ksadk/evaluation/executor.py +118 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/target.py +73 -0
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +273 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +35 -0
- ksadk/harness/app.py +269 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runtime.py +364 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +28 -12
- ksadk/identity/resolver.py +15 -9
- ksadk/ids.py +32 -0
- ksadk/knowledge_base/adk_tool.py +1 -1
- ksadk/knowledge_base/client.py +38 -33
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +195 -0
- ksadk/markdown.py +5 -2
- ksadk/mcp_runtime/__init__.py +9 -6
- ksadk/memory/__init__.py +1 -1
- ksadk/memory/adk/__init__.py +2 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
- ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
- ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
- ksadk/memory/adk/long_term_memory.py +34 -37
- ksadk/memory/adk/resilient_session_service.py +4 -5
- ksadk/memory/adk/short_term_memory.py +12 -21
- ksadk/memory/adk_tool.py +3 -2
- ksadk/memory/backends/base.py +19 -19
- ksadk/memory/backends/memory.py +24 -25
- ksadk/memory/backends/redis.py +31 -30
- ksadk/memory/langchain_tool.py +2 -1
- ksadk/memory/manager.py +52 -47
- ksadk/memory/service.py +16 -6
- ksadk/memory/tool.py +4 -0
- ksadk/model_policy.py +15 -7
- ksadk/model_proxy/__init__.py +51 -0
- ksadk/model_proxy/bootstrap.py +80 -0
- ksadk/model_proxy/cache.py +165 -0
- ksadk/model_proxy/config.py +66 -0
- ksadk/model_proxy/demo_webui.py +115 -0
- ksadk/model_proxy/detect.py +145 -0
- ksadk/model_proxy/gate.py +68 -0
- ksadk/model_proxy/namespace.py +201 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +443 -0
- ksadk/model_proxy/transform.py +735 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +354 -146
- ksadk/runners/base_runner.py +71 -29
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +18 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +417 -97
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +23 -7
- ksadk/runtime/__init__.py +67 -0
- ksadk/runtime/adapter.py +412 -0
- ksadk/runtime/conversation_execution.py +553 -0
- ksadk/runtime/executor.py +251 -0
- ksadk/runtime/factory.py +156 -0
- ksadk/runtime/framework_adapters.py +118 -0
- ksadk/runtime/launch.py +48 -0
- ksadk/runtime/preprocessing.py +131 -0
- ksadk/runtime/runner_adapter.py +1032 -0
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime_context.py +18 -3
- ksadk/runtime_state.py +0 -1
- ksadk/sandbox/__init__.py +4 -1
- ksadk/sandbox/backends/__init__.py +4 -1
- ksadk/sandbox/backends/e2b.py +13 -5
- ksadk/sandbox/backends/local_process.py +11 -3
- ksadk/sandbox/base.py +10 -15
- ksadk/sandbox/factory.py +14 -4
- ksadk/sandbox/registry.py +57 -6
- ksadk/server/__init__.py +39 -4
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +24 -3928
- ksadk/server/composition.py +47 -0
- ksadk/server/factory.py +622 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +682 -0
- ksadk/server/routes/control.py +331 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +425 -0
- ksadk/server/routes/models.py +451 -0
- ksadk/server/routes/openai_compat.py +229 -0
- ksadk/server/routes/projection.py +848 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +519 -0
- ksadk/server/routes/sessions.py +644 -0
- ksadk/server/routes/streaming.py +265 -0
- ksadk/server/routes/workspace.py +414 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
- ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
- ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
- ksadk/server/static/assets/index-R52pJJvg.js +177 -0
- ksadk/server/static/assets/index-jJr3_955.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +111 -58
- ksadk/sessions/__init__.py +69 -19
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +2 -3
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +143 -25
- ksadk/sessions/resilient.py +36 -8
- ksadk/skills/models.py +17 -5
- ksadk/skills/package_store.py +5 -2
- ksadk/skills/runtime/agent.py +20 -5
- ksadk/skills/runtime/artifacts.py +1 -5
- ksadk/skills/runtime/backends/e2b.py +10 -4
- ksadk/skills/runtime/backends/local.py +15 -3
- ksadk/skills/runtime/base.py +1 -2
- ksadk/skills/runtime/executor.py +10 -3
- ksadk/skills/runtime/factory.py +4 -2
- ksadk/skills/runtime/loader.py +1 -2
- ksadk/skills/runtime/registry.py +15 -10
- ksadk/skills/runtime/request.py +3 -1
- ksadk/skills/service_client.py +47 -16
- ksadk/skills/tool_defs.py +15 -10
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1046 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +177 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/authoring.py +524 -0
- ksadk/studio/authoring_coordinator.py +285 -0
- ksadk/studio/builder.py +320 -0
- ksadk/studio/capabilities.py +351 -0
- ksadk/studio/cloud.py +278 -0
- ksadk/studio/codex_agent_service.py +583 -0
- ksadk/studio/codex_builder.py +324 -0
- ksadk/studio/codex_manifest.py +278 -0
- ksadk/studio/codex_run.py +352 -0
- ksadk/studio/compiler.py +189 -0
- ksadk/studio/contracts.py +644 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/evaluation.py +192 -0
- ksadk/studio/event_store.py +284 -0
- ksadk/studio/framework_run.py +114 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +543 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +243 -0
- ksadk/studio/otel_trace.py +754 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +306 -0
- ksadk/studio/resource_catalog.py +1217 -0
- ksadk/studio/run_service.py +707 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +339 -0
- ksadk/studio/service.py +1141 -0
- ksadk/studio/shared_web.py +994 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
- ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +630 -0
- ksadk/studio/validator.py +246 -0
- ksadk/studio/workspace.py +137 -0
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +101 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +161 -37
- ksadk/toolsets/a2a.py +426 -0
- ksadk/toolsets/platform.py +21 -5
- ksadk/toolsets/sandbox.py +82 -16
- ksadk/toolsets/skills.py +200 -39
- ksadk/toolsets/web.py +123 -26
- ksadk/toolsets/workspace.py +212 -101
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/setup.py +135 -506
- ksadk/tracing/span_utils.py +0 -41
- ksadk/tui/loop.py +78 -30
- ksadk/tui/stream_render.py +1 -0
- ksadk/ui_config.py +5 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
- ksadk/server/static/assets/channel-CTuydARX.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
- ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
- ksadk/tracing/exporters/langfuse_exporter.py +0 -445
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.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,28 @@ import time
|
|
|
26
26
|
import webbrowser
|
|
27
27
|
from datetime import datetime, timedelta, timezone
|
|
28
28
|
from pathlib import Path
|
|
29
|
-
from typing import
|
|
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,
|
|
43
|
+
from ksadk.cli.dry_run import dry_run_option, effective_dry_run, run_async_with_dry_run
|
|
44
|
+
from ksadk.cli.env_options import env_options, parse_env_pairs
|
|
39
45
|
from ksadk.cli.error_utils import abort_with_cli_error, remote_error, resolution_error
|
|
46
|
+
from ksadk.cli.model_catalog import fetch_provider_model_catalog, find_model_in_catalog
|
|
40
47
|
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
48
|
from ksadk.cli.resource_common import (
|
|
44
49
|
CONTEXT_SETTINGS,
|
|
45
|
-
|
|
50
|
+
ResourceActionDescriptor,
|
|
46
51
|
ResourceDescriptor,
|
|
47
52
|
ResourceListSchema,
|
|
48
53
|
ResourceStatusSchema,
|
|
@@ -51,18 +56,17 @@ from ksadk.cli.resource_common import (
|
|
|
51
56
|
confirm_destructive,
|
|
52
57
|
confirm_options,
|
|
53
58
|
pagination_options,
|
|
54
|
-
|
|
59
|
+
region_option,
|
|
55
60
|
render_descriptor_list,
|
|
56
61
|
render_descriptor_status,
|
|
57
|
-
region_option,
|
|
58
62
|
)
|
|
63
|
+
from ksadk.cli.storage import build_storage_config
|
|
59
64
|
from ksadk.cli.ui import (
|
|
60
65
|
emit_json,
|
|
61
66
|
get_console,
|
|
62
67
|
is_json_output,
|
|
63
68
|
is_stdout_tty,
|
|
64
69
|
json_dumps,
|
|
65
|
-
output_option as cli_output_option,
|
|
66
70
|
print_info,
|
|
67
71
|
print_kv,
|
|
68
72
|
print_rule,
|
|
@@ -71,17 +75,20 @@ from ksadk.cli.ui import (
|
|
|
71
75
|
print_warn,
|
|
72
76
|
status_rich_style,
|
|
73
77
|
)
|
|
74
|
-
from ksadk.
|
|
75
|
-
|
|
78
|
+
from ksadk.cli.ui import (
|
|
79
|
+
output_option as cli_output_option,
|
|
80
|
+
)
|
|
81
|
+
from ksadk.configs.env_registry import is_sensitive_env_var
|
|
76
82
|
from ksadk.conversations.model_context import normalize_model_metadata
|
|
83
|
+
from ksadk.deployment.agent_access import get_latest_agent_access
|
|
77
84
|
from ksadk.model_policy import build_runtime_model_policy_env
|
|
78
|
-
from ksadk.
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
from ksadk.openclaw_gateway import (
|
|
86
|
+
OpenClawGatewayClient,
|
|
87
|
+
OpenClawGatewayError,
|
|
88
|
+
OpenClawGatewayRequestError,
|
|
81
89
|
)
|
|
82
|
-
from ksadk.openclaw_gateway import OpenClawGatewayClient, OpenClawGatewayError, OpenClawGatewayRequestError
|
|
83
|
-
from ksadk.terminal_exec_policy import OPENCLAW_TERMINAL_EXEC_POLICY
|
|
84
90
|
from ksadk.terminal_client import run_terminal_session
|
|
91
|
+
from ksadk.terminal_exec_policy import OPENCLAW_TERMINAL_EXEC_POLICY
|
|
85
92
|
|
|
86
93
|
console = get_console()
|
|
87
94
|
# 默认 OpenClaw 镜像。
|
|
@@ -102,7 +109,6 @@ DEFAULT_TRUSTED_PROXY_CIDRS = [
|
|
|
102
109
|
_GLOBAL_ENV_CACHE: Optional[Dict[str, str]] = None
|
|
103
110
|
OPENCLAW_SECURITY_PROFILES = ("relaxed", "strict", "strictest")
|
|
104
111
|
OPENCLAW_CHANNELS = ("weixin", "feishu", "wps-xiezuo")
|
|
105
|
-
OPENCLAW_ENV_KEY_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
106
112
|
WEIXIN_PLUGIN_ID = "openclaw-weixin"
|
|
107
113
|
FEISHU_PLUGIN_ID = "openclaw-lark"
|
|
108
114
|
FEISHU_CHANNEL_KEY = "feishu"
|
|
@@ -136,7 +142,8 @@ OPENCLAW_CHANNEL_CONNECT_HELP = """连接指定 channel。
|
|
|
136
142
|
飞书:启动官方 onboarding 流程。
|
|
137
143
|
agentengine openclaw channel connect <id> --channel feishu
|
|
138
144
|
WPS 协作:写入开放平台 appId/appSecret 并启动长连接。
|
|
139
|
-
agentengine openclaw channel connect <id> --channel wps-xiezuo
|
|
145
|
+
agentengine openclaw channel connect <id> --channel wps-xiezuo \
|
|
146
|
+
--app-id <appId> --app-secret <appSecret>
|
|
140
147
|
|
|
141
148
|
\b
|
|
142
149
|
WPS 协作说明:
|
|
@@ -347,6 +354,7 @@ def _get_global_env() -> Dict[str, str]:
|
|
|
347
354
|
|
|
348
355
|
try:
|
|
349
356
|
from ksadk.configs.global_config import get_env_from_global_config
|
|
357
|
+
|
|
350
358
|
_GLOBAL_ENV_CACHE = {
|
|
351
359
|
str(k): str(v).strip()
|
|
352
360
|
for k, v in get_env_from_global_config().items()
|
|
@@ -395,6 +403,7 @@ def _resolve_model_base_url(cli_value: Optional[str]) -> Optional[str]:
|
|
|
395
403
|
|
|
396
404
|
try:
|
|
397
405
|
from ksadk.configs.settings import settings
|
|
406
|
+
|
|
398
407
|
api_base = settings.model.api_base
|
|
399
408
|
if api_base and str(api_base).strip():
|
|
400
409
|
return str(api_base).strip()
|
|
@@ -434,8 +443,9 @@ def _summarize_openclaw_region(agents: list[Dict[str, Any]], fallback_region: Op
|
|
|
434
443
|
|
|
435
444
|
def _print_openclaw_list_summary(table: RichTable, summary_text: str) -> None:
|
|
436
445
|
"""将摘要贴在表格下方;宽度不足时退化成普通单行。"""
|
|
437
|
-
|
|
438
|
-
|
|
446
|
+
rich_console = cast(Console, console)
|
|
447
|
+
table_width = Measurement.get(rich_console, rich_console.options, table).maximum
|
|
448
|
+
summary_width = Measurement.get(rich_console, rich_console.options, summary_text).maximum
|
|
439
449
|
if table_width >= summary_width:
|
|
440
450
|
summary_grid = RichTable.grid(expand=False)
|
|
441
451
|
summary_grid.add_column(justify="right", width=table_width)
|
|
@@ -456,7 +466,12 @@ def _normalize_ui_locale(raw: Optional[str]) -> str:
|
|
|
456
466
|
|
|
457
467
|
if low in {"c", "c-utf-8", "c.utf-8", "posix"}:
|
|
458
468
|
return "zh-CN"
|
|
459
|
-
if
|
|
469
|
+
if (
|
|
470
|
+
low.startswith("zh-tw")
|
|
471
|
+
or low.startswith("zh-hk")
|
|
472
|
+
or low.startswith("zh-mo")
|
|
473
|
+
or low.startswith("zh-hant")
|
|
474
|
+
):
|
|
460
475
|
return "zh-TW"
|
|
461
476
|
if low.startswith("zh"):
|
|
462
477
|
return "zh-CN"
|
|
@@ -531,7 +546,9 @@ def _strip_provider_prefix(provider_id: str, model_id: str) -> str:
|
|
|
531
546
|
|
|
532
547
|
|
|
533
548
|
def _default_openclaw_model_inputs(provider_id: str, model_id: str) -> list[str]:
|
|
534
|
-
if str(provider_id or "").strip().lower() == "ksyun" and str(
|
|
549
|
+
if str(provider_id or "").strip().lower() == "ksyun" and str(
|
|
550
|
+
model_id or ""
|
|
551
|
+
).strip().lower() in {"glm-5.1", "glm-5.2"}:
|
|
535
552
|
return ["text"]
|
|
536
553
|
return ["text", "image"]
|
|
537
554
|
|
|
@@ -597,9 +614,11 @@ def _openclaw_catalog_item_from_metadata(
|
|
|
597
614
|
"api": str(raw_model.get("api") or provider_api or "openai-completions"),
|
|
598
615
|
"reasoning": bool(raw_model.get("reasoning", True)),
|
|
599
616
|
"input": inputs or _default_openclaw_model_inputs(provider_id, model_id),
|
|
600
|
-
"cost":
|
|
601
|
-
|
|
602
|
-
|
|
617
|
+
"cost": (
|
|
618
|
+
raw_model.get("cost")
|
|
619
|
+
if isinstance(raw_model.get("cost"), dict)
|
|
620
|
+
else {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}
|
|
621
|
+
),
|
|
603
622
|
"contextWindow": int(metadata.get("context_window_tokens") or 200_000),
|
|
604
623
|
"maxTokens": int(metadata.get("max_output_tokens") or 20_000),
|
|
605
624
|
}
|
|
@@ -613,7 +632,9 @@ def _apply_openclaw_provider_model_catalog(
|
|
|
613
632
|
return False
|
|
614
633
|
|
|
615
634
|
provider_id = str(env.get("OPENCLAW_MODEL_PROVIDER_ID") or "ksyun").strip() or "ksyun"
|
|
616
|
-
provider_api =
|
|
635
|
+
provider_api = (
|
|
636
|
+
str(env.get("OPENCLAW_MODEL_API") or "openai-completions").strip() or "openai-completions"
|
|
637
|
+
)
|
|
617
638
|
raw_catalog = str(env.get("OPENCLAW_MODEL_CATALOG_JSON") or "").strip()
|
|
618
639
|
catalog: list[Dict[str, Any]] = []
|
|
619
640
|
if raw_catalog:
|
|
@@ -632,16 +653,17 @@ def _apply_openclaw_provider_model_catalog(
|
|
|
632
653
|
|
|
633
654
|
changed = False
|
|
634
655
|
for raw_model in raw_models:
|
|
635
|
-
|
|
636
|
-
raw_model
|
|
637
|
-
|
|
638
|
-
|
|
656
|
+
raw_model_dict: Dict[str, Any] = (
|
|
657
|
+
dict(raw_model) if isinstance(raw_model, dict) else {"id": str(raw_model or "").strip()}
|
|
658
|
+
)
|
|
659
|
+
catalog_item = _openclaw_catalog_item_from_metadata(
|
|
660
|
+
raw_model_dict,
|
|
639
661
|
provider_id=provider_id,
|
|
640
662
|
provider_api=provider_api,
|
|
641
663
|
)
|
|
642
|
-
if not
|
|
664
|
+
if not catalog_item:
|
|
643
665
|
continue
|
|
644
|
-
model_key = str(
|
|
666
|
+
model_key = str(catalog_item["id"])
|
|
645
667
|
existing_index = next(
|
|
646
668
|
(
|
|
647
669
|
index
|
|
@@ -651,13 +673,13 @@ def _apply_openclaw_provider_model_catalog(
|
|
|
651
673
|
None,
|
|
652
674
|
)
|
|
653
675
|
if existing_index is not None:
|
|
654
|
-
merged = {**catalog[existing_index], **
|
|
676
|
+
merged = {**catalog[existing_index], **catalog_item}
|
|
655
677
|
if merged != catalog[existing_index]:
|
|
656
678
|
catalog[existing_index] = merged
|
|
657
679
|
changed = True
|
|
658
680
|
continue
|
|
659
681
|
seen.add(model_key)
|
|
660
|
-
catalog.append(
|
|
682
|
+
catalog.append(catalog_item)
|
|
661
683
|
changed = True
|
|
662
684
|
|
|
663
685
|
if not catalog or not changed:
|
|
@@ -679,20 +701,12 @@ def _apply_openclaw_provider_model_metadata(
|
|
|
679
701
|
|
|
680
702
|
def _openclaw_requested_model_ids(env: Dict[str, str]) -> list[str]:
|
|
681
703
|
raw_allowlist = str(
|
|
682
|
-
env.get("OPENCLAW_MODEL_ALLOWLIST")
|
|
683
|
-
or env.get("AGENTENGINE_MODEL_ALLOWLIST")
|
|
684
|
-
or ""
|
|
704
|
+
env.get("OPENCLAW_MODEL_ALLOWLIST") or env.get("AGENTENGINE_MODEL_ALLOWLIST") or ""
|
|
685
705
|
).strip()
|
|
686
706
|
if raw_allowlist:
|
|
687
|
-
return [
|
|
688
|
-
item.strip()
|
|
689
|
-
for item in raw_allowlist.replace(";", ",").split(",")
|
|
690
|
-
if item.strip()
|
|
691
|
-
]
|
|
707
|
+
return [item.strip() for item in raw_allowlist.replace(";", ",").split(",") if item.strip()]
|
|
692
708
|
primary_model = str(
|
|
693
|
-
env.get("OPENCLAW_DEFAULT_MODEL")
|
|
694
|
-
or env.get("OPENAI_MODEL_NAME")
|
|
695
|
-
or ""
|
|
709
|
+
env.get("OPENCLAW_DEFAULT_MODEL") or env.get("OPENAI_MODEL_NAME") or ""
|
|
696
710
|
).strip()
|
|
697
711
|
return [primary_model] if primary_model else []
|
|
698
712
|
|
|
@@ -705,9 +719,7 @@ def _filter_openclaw_provider_catalog(
|
|
|
705
719
|
if not requested_models:
|
|
706
720
|
return []
|
|
707
721
|
raw_models = [
|
|
708
|
-
item.get("_provider_raw_model") or item
|
|
709
|
-
if isinstance(item, dict)
|
|
710
|
-
else item
|
|
722
|
+
item.get("_provider_raw_model") or item if isinstance(item, dict) else item
|
|
711
723
|
for item in provider_catalog
|
|
712
724
|
]
|
|
713
725
|
selected: list[Any] = []
|
|
@@ -716,7 +728,9 @@ def _filter_openclaw_provider_catalog(
|
|
|
716
728
|
match = find_model_in_catalog(raw_models, model_id)
|
|
717
729
|
if match is None:
|
|
718
730
|
continue
|
|
719
|
-
identities = sorted(
|
|
731
|
+
identities = sorted(
|
|
732
|
+
str(value).strip().lower() for value in (model_id, str(match)) if str(value).strip()
|
|
733
|
+
)
|
|
720
734
|
dedupe_key = identities[0] if identities else str(model_id).lower()
|
|
721
735
|
if dedupe_key in seen:
|
|
722
736
|
continue
|
|
@@ -747,14 +761,12 @@ def _build_openclaw_env_vars(
|
|
|
747
761
|
openclaw_explicit_model = default_model or _resolve_env("OPENCLAW_DEFAULT_MODEL")
|
|
748
762
|
generic_model_preference = _resolve_env("OPENAI_MODEL_NAME", "MODEL_NAME", "LLM_MODEL")
|
|
749
763
|
model_preference = openclaw_explicit_model or generic_model_preference
|
|
750
|
-
explicit_base_url = (
|
|
751
|
-
|
|
752
|
-
or _resolve_env("OPENCLAW_MODEL_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE")
|
|
764
|
+
explicit_base_url = model_base_url or _resolve_env(
|
|
765
|
+
"OPENCLAW_MODEL_BASE_URL", "OPENAI_BASE_URL", "OPENAI_API_BASE"
|
|
753
766
|
)
|
|
754
767
|
base_url = _resolve_model_base_url(explicit_base_url)
|
|
755
|
-
api_key = (
|
|
756
|
-
|
|
757
|
-
or _resolve_env("OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY")
|
|
768
|
+
api_key = model_api_key or _resolve_env(
|
|
769
|
+
"OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY"
|
|
758
770
|
)
|
|
759
771
|
model = model_preference or "glm-5.2"
|
|
760
772
|
explicit_provider_id = model_provider_id or _resolve_env("OPENCLAW_MODEL_PROVIDER_ID")
|
|
@@ -762,34 +774,33 @@ def _build_openclaw_env_vars(
|
|
|
762
774
|
if not inferred_provider_id and model and "/" in model:
|
|
763
775
|
inferred_provider_id = model.split("/", 1)[0].strip()
|
|
764
776
|
provider_id = inferred_provider_id or default_provider_id
|
|
765
|
-
resolved_gateway_port = (
|
|
766
|
-
|
|
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
|
-
)
|
|
777
|
+
resolved_gateway_port = gateway_port or _resolve_env("OPENCLAW_GATEWAY_PORT", "PORT") or "8080"
|
|
778
|
+
resolved_public_port = public_port or _resolve_env("OPENCLAW_PUBLIC_PORT") or "80"
|
|
775
779
|
explicit_model_api = _resolve_env("OPENCLAW_MODEL_API")
|
|
776
780
|
model_api = explicit_model_api or default_model_api
|
|
777
781
|
trusted_proxy_user_header = (
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
782
|
+
(
|
|
783
|
+
_resolve_env(
|
|
784
|
+
"OPENCLAW_TRUSTED_PROXY_USER_HEADER",
|
|
785
|
+
"OPENCLAW_GATEWAY_TRUSTED_PROXY_USER_HEADER",
|
|
786
|
+
)
|
|
787
|
+
or DEFAULT_TRUSTED_PROXY_USER_HEADER
|
|
781
788
|
)
|
|
782
|
-
|
|
783
|
-
|
|
789
|
+
.strip()
|
|
790
|
+
.lower()
|
|
791
|
+
)
|
|
784
792
|
internal_trusted_proxy_user = (
|
|
785
|
-
_resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER")
|
|
786
|
-
or "openclaw-backend"
|
|
793
|
+
_resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER") or "openclaw-backend"
|
|
787
794
|
)
|
|
788
795
|
internal_trusted_proxy_user_header = (
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
796
|
+
(
|
|
797
|
+
_resolve_env("OPENCLAW_INTERNAL_TRUSTED_PROXY_USER_HEADER")
|
|
798
|
+
or trusted_proxy_user_header
|
|
799
|
+
or DEFAULT_TRUSTED_PROXY_USER_HEADER
|
|
800
|
+
)
|
|
801
|
+
.strip()
|
|
802
|
+
.lower()
|
|
803
|
+
)
|
|
793
804
|
trusted_proxies = _normalize_csv_list(
|
|
794
805
|
_resolve_env("OPENCLAW_TRUSTED_PROXIES") or "",
|
|
795
806
|
default_items=DEFAULT_TRUSTED_PROXY_CIDRS,
|
|
@@ -797,7 +808,9 @@ def _build_openclaw_env_vars(
|
|
|
797
808
|
browser_enabled = _resolve_env("OPENCLAW_BROWSER_ENABLED")
|
|
798
809
|
browser_no_sandbox = _resolve_env("OPENCLAW_BROWSER_NO_SANDBOX") or "true"
|
|
799
810
|
browser_headless = _resolve_env("OPENCLAW_BROWSER_HEADLESS") or "true"
|
|
800
|
-
browser_executable = _resolve_env(
|
|
811
|
+
browser_executable = _resolve_env(
|
|
812
|
+
"OPENCLAW_BROWSER_EXECUTABLE_PATH", "OPENCLAW_BROWSER_EXECUTABLE"
|
|
813
|
+
)
|
|
801
814
|
ui_locale = _normalize_ui_locale(_resolve_env("OPENCLAW_UI_LOCALE", "LANG", "LC_ALL"))
|
|
802
815
|
exec_strict_mode_raw = (
|
|
803
816
|
exec_profile_overrides.get("OPENCLAW_EXEC_STRICT_MODE")
|
|
@@ -806,13 +819,21 @@ def _build_openclaw_env_vars(
|
|
|
806
819
|
)
|
|
807
820
|
exec_strict_mode = _is_truthy(exec_strict_mode_raw)
|
|
808
821
|
|
|
809
|
-
exec_host =
|
|
822
|
+
exec_host = (
|
|
823
|
+
exec_profile_overrides.get("OPENCLAW_EXEC_HOST")
|
|
824
|
+
or _resolve_env("OPENCLAW_EXEC_HOST")
|
|
825
|
+
or "gateway"
|
|
826
|
+
)
|
|
810
827
|
exec_security = (
|
|
811
828
|
exec_profile_overrides.get("OPENCLAW_EXEC_SECURITY")
|
|
812
829
|
or _resolve_env("OPENCLAW_EXEC_SECURITY")
|
|
813
830
|
or ("allowlist" if exec_strict_mode else "full")
|
|
814
831
|
)
|
|
815
|
-
exec_ask =
|
|
832
|
+
exec_ask = (
|
|
833
|
+
exec_profile_overrides.get("OPENCLAW_EXEC_ASK")
|
|
834
|
+
or _resolve_env("OPENCLAW_EXEC_ASK")
|
|
835
|
+
or "off"
|
|
836
|
+
)
|
|
816
837
|
exec_ask_fallback = (
|
|
817
838
|
exec_profile_overrides.get("OPENCLAW_EXEC_ASK_FALLBACK")
|
|
818
839
|
or _resolve_env("OPENCLAW_EXEC_ASK_FALLBACK")
|
|
@@ -831,9 +852,7 @@ def _build_openclaw_env_vars(
|
|
|
831
852
|
exec_default_allowlist_enabled = (
|
|
832
853
|
exec_profile_overrides.get("OPENCLAW_EXEC_DEFAULT_ALLOWLIST_ENABLED")
|
|
833
854
|
or _resolve_env("OPENCLAW_EXEC_DEFAULT_ALLOWLIST_ENABLED")
|
|
834
|
-
or (
|
|
835
|
-
"true" if exec_strict_mode else "false"
|
|
836
|
-
)
|
|
855
|
+
or ("true" if exec_strict_mode else "false")
|
|
837
856
|
)
|
|
838
857
|
exec_allowlist = _resolve_env("OPENCLAW_EXEC_ALLOWLIST")
|
|
839
858
|
fs_workspace_only = (
|
|
@@ -850,10 +869,14 @@ def _build_openclaw_env_vars(
|
|
|
850
869
|
env["OPENCLAW_GATEWAY_BIND"] = "lan"
|
|
851
870
|
if gateway_auth_mode:
|
|
852
871
|
env["OPENCLAW_GATEWAY_AUTH_MODE"] = gateway_auth_mode
|
|
853
|
-
env["OPENCLAW_TRUSTED_PROXY_USER_HEADER"] =
|
|
872
|
+
env["OPENCLAW_TRUSTED_PROXY_USER_HEADER"] = (
|
|
873
|
+
trusted_proxy_user_header or DEFAULT_TRUSTED_PROXY_USER_HEADER
|
|
874
|
+
)
|
|
854
875
|
env["OPENCLAW_INTERNAL_TRUSTED_PROXY_USER"] = internal_trusted_proxy_user
|
|
855
876
|
env["OPENCLAW_INTERNAL_TRUSTED_PROXY_USER_HEADER"] = (
|
|
856
|
-
internal_trusted_proxy_user_header
|
|
877
|
+
internal_trusted_proxy_user_header
|
|
878
|
+
or trusted_proxy_user_header
|
|
879
|
+
or DEFAULT_TRUSTED_PROXY_USER_HEADER
|
|
857
880
|
)
|
|
858
881
|
env["OPENCLAW_TRUSTED_PROXIES"] = trusted_proxies
|
|
859
882
|
env["OPENCLAW_GATEWAY_PORT"] = str(resolved_gateway_port)
|
|
@@ -899,8 +922,9 @@ def _build_openclaw_env_vars(
|
|
|
899
922
|
_, catalog_model_id = normalized_model.split("/", 1)
|
|
900
923
|
resolved_model = normalized_model
|
|
901
924
|
else:
|
|
902
|
-
|
|
903
|
-
|
|
925
|
+
resolved_model = (
|
|
926
|
+
f"{provider_id}/{normalized_model}" if provider_id else normalized_model
|
|
927
|
+
)
|
|
904
928
|
if openclaw_explicit_model:
|
|
905
929
|
env["OPENCLAW_DEFAULT_MODEL"] = resolved_model
|
|
906
930
|
elif generic_model_preference:
|
|
@@ -1005,11 +1029,14 @@ def _normalize_openclaw_gateway_auth_env(env: dict[str, str]) -> dict[str, str]:
|
|
|
1005
1029
|
auth_mode = raw_mode or ("token" if raw_token or raw_password else "trusted-proxy")
|
|
1006
1030
|
if auth_mode == "token":
|
|
1007
1031
|
if raw_token and raw_password and raw_token != raw_password:
|
|
1008
|
-
raise ValueError(
|
|
1032
|
+
raise ValueError(
|
|
1033
|
+
"OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致"
|
|
1034
|
+
)
|
|
1009
1035
|
shared_secret = raw_token or raw_password
|
|
1010
1036
|
if not shared_secret:
|
|
1011
1037
|
raise ValueError(
|
|
1012
|
-
"OPENCLAW_GATEWAY_AUTH_MODE=token 时必须提供
|
|
1038
|
+
"OPENCLAW_GATEWAY_AUTH_MODE=token 时必须提供 "
|
|
1039
|
+
"OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
|
|
1013
1040
|
)
|
|
1014
1041
|
normalized_env["OPENCLAW_GATEWAY_AUTH_MODE"] = "token"
|
|
1015
1042
|
normalized_env["OPENCLAW_GATEWAY_TOKEN"] = shared_secret
|
|
@@ -1018,7 +1045,8 @@ def _normalize_openclaw_gateway_auth_env(env: dict[str, str]) -> dict[str, str]:
|
|
|
1018
1045
|
|
|
1019
1046
|
if raw_token or raw_password:
|
|
1020
1047
|
raise ValueError(
|
|
1021
|
-
"仅在 OPENCLAW_GATEWAY_AUTH_MODE=token 时支持
|
|
1048
|
+
"仅在 OPENCLAW_GATEWAY_AUTH_MODE=token 时支持 "
|
|
1049
|
+
"OPENCLAW_GATEWAY_TOKEN 或 OPENCLAW_GATEWAY_PASSWORD"
|
|
1022
1050
|
)
|
|
1023
1051
|
|
|
1024
1052
|
normalized_env["OPENCLAW_GATEWAY_AUTH_MODE"] = auth_mode
|
|
@@ -1028,22 +1056,13 @@ def _normalize_openclaw_gateway_auth_env(env: dict[str, str]) -> dict[str, str]:
|
|
|
1028
1056
|
|
|
1029
1057
|
|
|
1030
1058
|
def _parse_extra_openclaw_env_pairs(items: tuple[str, ...] | list[str] | None) -> dict[str, str]:
|
|
1031
|
-
"""解析 deploy --env
|
|
1032
|
-
parsed
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
if not
|
|
1036
|
-
raise ValueError(
|
|
1037
|
-
|
|
1038
|
-
key = key.strip()
|
|
1039
|
-
if not OPENCLAW_ENV_KEY_PATTERN.fullmatch(key):
|
|
1040
|
-
raise ValueError(f"自定义环境变量名不合法: {key!r},请使用合法的环境变量名")
|
|
1041
|
-
if key == "OPENCLAW_GATEWAY_AUTH_MODE":
|
|
1042
|
-
normalized = value.strip().lower()
|
|
1043
|
-
if normalized not in {"trusted-proxy", "token", "none"}:
|
|
1044
|
-
raise ValueError("OPENCLAW_GATEWAY_AUTH_MODE 仅支持 trusted-proxy、token 或 none")
|
|
1045
|
-
value = normalized
|
|
1046
|
-
parsed[key] = value
|
|
1059
|
+
"""解析 deploy --env 传入的自定义环境变量,并对 gateway 鉴权模式做早期归一化。"""
|
|
1060
|
+
parsed = parse_env_pairs(items)
|
|
1061
|
+
if "OPENCLAW_GATEWAY_AUTH_MODE" in parsed:
|
|
1062
|
+
normalized = parsed["OPENCLAW_GATEWAY_AUTH_MODE"].strip().lower()
|
|
1063
|
+
if normalized not in {"trusted-proxy", "token", "none"}:
|
|
1064
|
+
raise ValueError("OPENCLAW_GATEWAY_AUTH_MODE 仅支持 trusted-proxy、token 或 none")
|
|
1065
|
+
parsed["OPENCLAW_GATEWAY_AUTH_MODE"] = normalized
|
|
1047
1066
|
return parsed
|
|
1048
1067
|
|
|
1049
1068
|
|
|
@@ -1174,7 +1193,7 @@ async def _fetch_bootstrap_config(region: str) -> Optional[Dict[str, Any]]:
|
|
|
1174
1193
|
|
|
1175
1194
|
try:
|
|
1176
1195
|
async with AgentEngineClient(region=region) as client:
|
|
1177
|
-
|
|
1196
|
+
result = await client.get_client_bootstrap_config(
|
|
1178
1197
|
product="openclaw",
|
|
1179
1198
|
framework="openclaw",
|
|
1180
1199
|
region=region,
|
|
@@ -1183,6 +1202,7 @@ async def _fetch_bootstrap_config(region: str) -> Optional[Dict[str, Any]]:
|
|
|
1183
1202
|
locale=_resolve_env("OPENCLAW_UI_LOCALE", "LANG", "LC_ALL"),
|
|
1184
1203
|
ignore_dry_run=True,
|
|
1185
1204
|
)
|
|
1205
|
+
return dict(result) if isinstance(result, dict) else None
|
|
1186
1206
|
except Exception as e:
|
|
1187
1207
|
print_warn(f"拉取服务端默认配置失败,回退本地默认镜像: {e}")
|
|
1188
1208
|
return None
|
|
@@ -1253,14 +1273,22 @@ def _flatten_agent_detail(agent: dict) -> dict:
|
|
|
1253
1273
|
"agent_id": basic.get("agent_id") or agent.get("agent_id") or "",
|
|
1254
1274
|
"name": basic.get("name") or agent.get("name") or "",
|
|
1255
1275
|
"status": (basic.get("status") or agent.get("status") or "UNKNOWN").upper(),
|
|
1256
|
-
"framework": basic.get("framework")
|
|
1276
|
+
"framework": basic.get("framework")
|
|
1277
|
+
or deploy.get("framework")
|
|
1278
|
+
or agent.get("framework")
|
|
1279
|
+
or "",
|
|
1257
1280
|
"region": basic.get("region") or deploy.get("region") or agent.get("region") or "",
|
|
1258
|
-
"endpoint": quick.get("public_endpoint")
|
|
1281
|
+
"endpoint": quick.get("public_endpoint")
|
|
1282
|
+
or quick.get("private_endpoint")
|
|
1283
|
+
or agent.get("endpoint")
|
|
1284
|
+
or "",
|
|
1259
1285
|
"artifact_path": deploy.get("artifact_path") or agent.get("artifact_path") or "",
|
|
1260
1286
|
"created_at": basic.get("created_at") or agent.get("created_at") or "",
|
|
1261
1287
|
"updated_at": basic.get("updated_at") or agent.get("updated_at") or "",
|
|
1262
1288
|
"api_key": quick.get("api_key") or agent.get("api_key"),
|
|
1263
|
-
"langfuse_url": (agent.get("advanced") or {}).get("observability_url")
|
|
1289
|
+
"langfuse_url": (agent.get("advanced") or {}).get("observability_url")
|
|
1290
|
+
or agent.get("langfuse_trace_url")
|
|
1291
|
+
or "",
|
|
1264
1292
|
}
|
|
1265
1293
|
|
|
1266
1294
|
|
|
@@ -1277,7 +1305,9 @@ async def _get_openclaw_detail_with_client(
|
|
|
1277
1305
|
detail = _flatten_agent_detail(agent)
|
|
1278
1306
|
framework = str(detail.get("framework") or "").strip().lower()
|
|
1279
1307
|
if framework and framework != "openclaw":
|
|
1280
|
-
raise resolution_error(
|
|
1308
|
+
raise resolution_error(
|
|
1309
|
+
f"目标 Agent 不是 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"]
|
|
1310
|
+
)
|
|
1281
1311
|
return detail
|
|
1282
1312
|
|
|
1283
1313
|
|
|
@@ -1303,10 +1333,7 @@ def _resolve_region(
|
|
|
1303
1333
|
) -> str:
|
|
1304
1334
|
"""解析 region: 显式参数 > state > 环境变量 > 默认值。"""
|
|
1305
1335
|
return (
|
|
1306
|
-
cli_region
|
|
1307
|
-
or (state or {}).get("region")
|
|
1308
|
-
or _resolve_env("KSYUN_REGION")
|
|
1309
|
-
or "cn-beijing-6"
|
|
1336
|
+
cli_region or (state or {}).get("region") or _resolve_env("KSYUN_REGION") or "cn-beijing-6"
|
|
1310
1337
|
)
|
|
1311
1338
|
|
|
1312
1339
|
|
|
@@ -1331,7 +1358,9 @@ async def _resolve_openclaw_detail_or_raise(
|
|
|
1331
1358
|
detail = await _get_openclaw_detail_with_client(client, resolved.value)
|
|
1332
1359
|
|
|
1333
1360
|
if not detail:
|
|
1334
|
-
raise resolution_error(
|
|
1361
|
+
raise resolution_error(
|
|
1362
|
+
f"未找到 OpenClaw: {resolved.value}", hints=["agentengine openclaw list"]
|
|
1363
|
+
)
|
|
1335
1364
|
return resolved_region, detail
|
|
1336
1365
|
|
|
1337
1366
|
|
|
@@ -1378,7 +1407,9 @@ async def _ensure_openclaw_gateway_available(
|
|
|
1378
1407
|
await _wait_for_gateway_ready(region, detail, timeout_seconds=timeout_seconds)
|
|
1379
1408
|
|
|
1380
1409
|
|
|
1381
|
-
def _emit_data_payload(
|
|
1410
|
+
def _emit_data_payload(
|
|
1411
|
+
title: str, payload: dict[str, Any], *, subtitle: Optional[str] = None
|
|
1412
|
+
) -> None:
|
|
1382
1413
|
if is_json_output():
|
|
1383
1414
|
emit_json(payload)
|
|
1384
1415
|
return
|
|
@@ -1386,7 +1417,9 @@ def _emit_data_payload(title: str, payload: dict[str, Any], *, subtitle: Optiona
|
|
|
1386
1417
|
console.print(json_dumps(payload), markup=False)
|
|
1387
1418
|
|
|
1388
1419
|
|
|
1389
|
-
def _render_openclaw_dry_run(
|
|
1420
|
+
def _render_openclaw_dry_run(
|
|
1421
|
+
action: str, request: dict[str, Any], hints: tuple[str, ...] = ()
|
|
1422
|
+
) -> None:
|
|
1390
1423
|
if is_json_output():
|
|
1391
1424
|
emit_json(
|
|
1392
1425
|
build_dry_run_envelope(
|
|
@@ -1456,7 +1489,9 @@ def _select_openclaw_gateway_secret(
|
|
|
1456
1489
|
or ""
|
|
1457
1490
|
).strip()
|
|
1458
1491
|
if token and password and token != password:
|
|
1459
|
-
raise click.ClickException(
|
|
1492
|
+
raise click.ClickException(
|
|
1493
|
+
"OPENCLAW_GATEWAY_TOKEN 与 OPENCLAW_GATEWAY_PASSWORD 同时提供时必须一致"
|
|
1494
|
+
)
|
|
1460
1495
|
if token:
|
|
1461
1496
|
return "token", token
|
|
1462
1497
|
if password:
|
|
@@ -1473,14 +1508,20 @@ def _mask_openclaw_secret(secret: str | None) -> str:
|
|
|
1473
1508
|
return f"{text[:4]}****"
|
|
1474
1509
|
|
|
1475
1510
|
|
|
1476
|
-
def _openclaw_auth_mode_from_sources(
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1511
|
+
def _openclaw_auth_mode_from_sources(
|
|
1512
|
+
detail: dict[str, Any], state: dict[str, Any] | None = None
|
|
1513
|
+
) -> str:
|
|
1514
|
+
return (
|
|
1515
|
+
str(
|
|
1516
|
+
detail.get("openclaw_auth_mode")
|
|
1517
|
+
or detail.get("gateway_auth_mode")
|
|
1518
|
+
or (state or {}).get("openclaw_auth_mode")
|
|
1519
|
+
or (state or {}).get("gateway_auth_mode")
|
|
1520
|
+
or ""
|
|
1521
|
+
)
|
|
1522
|
+
.strip()
|
|
1523
|
+
.lower()
|
|
1524
|
+
)
|
|
1484
1525
|
|
|
1485
1526
|
|
|
1486
1527
|
def _openclaw_state_gateway_token(env_vars: dict[str, str]) -> str | None:
|
|
@@ -1488,9 +1529,7 @@ def _openclaw_state_gateway_token(env_vars: dict[str, str]) -> str | None:
|
|
|
1488
1529
|
if str(env_vars.get("OPENCLAW_GATEWAY_AUTH_MODE") or "").strip().lower() != "token":
|
|
1489
1530
|
return None
|
|
1490
1531
|
token = str(
|
|
1491
|
-
env_vars.get("OPENCLAW_GATEWAY_TOKEN")
|
|
1492
|
-
or env_vars.get("OPENCLAW_GATEWAY_PASSWORD")
|
|
1493
|
-
or ""
|
|
1532
|
+
env_vars.get("OPENCLAW_GATEWAY_TOKEN") or env_vars.get("OPENCLAW_GATEWAY_PASSWORD") or ""
|
|
1494
1533
|
).strip()
|
|
1495
1534
|
return token or None
|
|
1496
1535
|
|
|
@@ -1523,7 +1562,9 @@ async def _run_weixin_remote_cli_login(
|
|
|
1523
1562
|
if not endpoint:
|
|
1524
1563
|
raise OpenClawGatewayError("OpenClaw runtime endpoint 为空,无法通过远端 CLI 执行微信登录")
|
|
1525
1564
|
|
|
1526
|
-
print_info(
|
|
1565
|
+
print_info(
|
|
1566
|
+
f"当前 OpenClaw 未暴露微信 web login RPC,改用远端 OpenClaw CLI 登录流程({reason})"
|
|
1567
|
+
)
|
|
1527
1568
|
exit_code = await run_terminal_session(
|
|
1528
1569
|
endpoint=endpoint,
|
|
1529
1570
|
api_key=_openclaw_terminal_api_key(detail),
|
|
@@ -1592,9 +1633,8 @@ async def _wait_for_gateway_reload_after_config_apply(
|
|
|
1592
1633
|
|
|
1593
1634
|
def _is_gateway_reload_disconnect_error(exc: Exception) -> bool:
|
|
1594
1635
|
message = str(exc).lower()
|
|
1595
|
-
return (
|
|
1596
|
-
"
|
|
1597
|
-
and ("1011" in message or "bad gateway" in message or "going away" in message)
|
|
1636
|
+
return "websocket receive failed" in message and (
|
|
1637
|
+
"1011" in message or "bad gateway" in message or "going away" in message
|
|
1598
1638
|
)
|
|
1599
1639
|
|
|
1600
1640
|
|
|
@@ -1684,9 +1724,15 @@ def _is_channel_configured(
|
|
|
1684
1724
|
accounts = channel_cfg.get("accounts")
|
|
1685
1725
|
return isinstance(accounts, dict) and any(str(key).strip() for key in accounts.keys())
|
|
1686
1726
|
if channel == "feishu":
|
|
1687
|
-
return bool(
|
|
1727
|
+
return bool(
|
|
1728
|
+
str(channel_cfg.get("appId") or "").strip()
|
|
1729
|
+
and str(channel_cfg.get("appSecret") or "").strip()
|
|
1730
|
+
)
|
|
1688
1731
|
if channel == "wps-xiezuo":
|
|
1689
|
-
return bool(
|
|
1732
|
+
return bool(
|
|
1733
|
+
str(channel_cfg.get("appId") or "").strip()
|
|
1734
|
+
and str(channel_cfg.get("appSecret") or "").strip()
|
|
1735
|
+
)
|
|
1690
1736
|
return bool(channel_cfg)
|
|
1691
1737
|
|
|
1692
1738
|
|
|
@@ -1791,7 +1837,9 @@ def _ensure_local_node_tools() -> dict[str, str]:
|
|
|
1791
1837
|
if not node_path or not npx_path:
|
|
1792
1838
|
raise resolution_error(
|
|
1793
1839
|
"本地缺少 `node` 或 `npx`,飞书接入依赖官方 onboarding 工具",
|
|
1794
|
-
hints=[
|
|
1840
|
+
hints=[
|
|
1841
|
+
"先安装 Node.js,然后重试 `agentengine openclaw channel connect --channel feishu`"
|
|
1842
|
+
],
|
|
1795
1843
|
)
|
|
1796
1844
|
return {"node": node_path, "npx": npx_path}
|
|
1797
1845
|
|
|
@@ -1822,14 +1870,11 @@ async def _resolve_npx_cached_package_file(package_spec: str, relative_path: str
|
|
|
1822
1870
|
text=True,
|
|
1823
1871
|
)
|
|
1824
1872
|
if completed.returncode != 0:
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
)
|
|
1873
|
+
npm_error = completed.stderr.strip() or completed.stdout.strip() or package_spec
|
|
1874
|
+
raise OpenClawGatewayError(f"无法准备官方 npm 包缓存: {npm_error}")
|
|
1828
1875
|
|
|
1829
1876
|
npm_cache_root = Path(
|
|
1830
|
-
os.getenv("NPM_CONFIG_CACHE")
|
|
1831
|
-
or os.getenv("npm_config_cache")
|
|
1832
|
-
or str(Path.home() / ".npm")
|
|
1877
|
+
os.getenv("NPM_CONFIG_CACHE") or os.getenv("npm_config_cache") or str(Path.home() / ".npm")
|
|
1833
1878
|
)
|
|
1834
1879
|
pattern = f"_npx/**/node_modules/{package_name}/{relative_path}"
|
|
1835
1880
|
matches = list(npm_cache_root.glob(pattern))
|
|
@@ -1847,8 +1892,7 @@ async def _run_feishu_onboarding(existing_app_id: Optional[str]) -> dict[str, An
|
|
|
1847
1892
|
"@larksuite/openclaw-lark-tools@latest",
|
|
1848
1893
|
"dist/utils/install-prompts.js",
|
|
1849
1894
|
)
|
|
1850
|
-
script_body = textwrap.dedent(
|
|
1851
|
-
"""
|
|
1895
|
+
script_body = textwrap.dedent("""
|
|
1852
1896
|
const fs = require("fs");
|
|
1853
1897
|
const { runInstallAuthFlow } = require(__INSTALL_PROMPTS_PATH__);
|
|
1854
1898
|
|
|
@@ -1860,19 +1904,26 @@ async def _run_feishu_onboarding(existing_app_id: Optional[str]) -> dict[str, An
|
|
|
1860
1904
|
{},
|
|
1861
1905
|
false,
|
|
1862
1906
|
);
|
|
1863
|
-
fs.writeFileSync(
|
|
1907
|
+
fs.writeFileSync(
|
|
1908
|
+
process.env.KSADK_FEISHU_RESULT_PATH,
|
|
1909
|
+
JSON.stringify(result),
|
|
1910
|
+
"utf8",
|
|
1911
|
+
);
|
|
1864
1912
|
} catch (error) {
|
|
1865
1913
|
console.error(error);
|
|
1866
1914
|
process.exit(1);
|
|
1867
1915
|
}
|
|
1868
1916
|
})();
|
|
1869
|
-
"""
|
|
1870
|
-
|
|
1871
|
-
|
|
1917
|
+
""").replace("__INSTALL_PROMPTS_PATH__", json.dumps(str(install_prompts_path)))
|
|
1918
|
+
with tempfile.NamedTemporaryFile(
|
|
1919
|
+
"w", suffix=".cjs", delete=False, encoding="utf-8"
|
|
1920
|
+
) as script_file:
|
|
1872
1921
|
script_file.write(script_body.strip())
|
|
1873
1922
|
script_path = script_file.name
|
|
1874
1923
|
|
|
1875
|
-
with tempfile.NamedTemporaryFile(
|
|
1924
|
+
with tempfile.NamedTemporaryFile(
|
|
1925
|
+
"w", suffix=".json", delete=False, encoding="utf-8"
|
|
1926
|
+
) as result_file:
|
|
1876
1927
|
result_path = result_file.name
|
|
1877
1928
|
|
|
1878
1929
|
env = os.environ.copy()
|
|
@@ -1945,7 +1996,9 @@ def _resolve_weixin_account_id(
|
|
|
1945
1996
|
raise click.ClickException("检测到多个微信账号,请显式传入 --account-id")
|
|
1946
1997
|
if create_if_missing:
|
|
1947
1998
|
return "default"
|
|
1948
|
-
raise click.ClickException(
|
|
1999
|
+
raise click.ClickException(
|
|
2000
|
+
"尚未检测到微信账号,请先执行 `agentengine openclaw channel connect --channel weixin`"
|
|
2001
|
+
)
|
|
1949
2002
|
|
|
1950
2003
|
|
|
1951
2004
|
def _mutate_weixin_account_enabled(
|
|
@@ -2011,7 +2064,8 @@ def _mutate_feishu_connect_config(config: dict[str, Any], onboarding: dict[str,
|
|
|
2011
2064
|
feishu_cfg[key] = value
|
|
2012
2065
|
changed = True
|
|
2013
2066
|
|
|
2014
|
-
|
|
2067
|
+
user_info_value = onboarding.get("userInfo")
|
|
2068
|
+
user_info: dict[str, Any] = dict(user_info_value) if isinstance(user_info_value, dict) else {}
|
|
2015
2069
|
open_id = str(user_info.get("openId") or "").strip()
|
|
2016
2070
|
if open_id:
|
|
2017
2071
|
if feishu_cfg.get("dmPolicy") != "allowlist":
|
|
@@ -2105,8 +2159,12 @@ def _mutate_wps_xiezuo_connect_config(
|
|
|
2105
2159
|
bindings = config.setdefault("bindings", [])
|
|
2106
2160
|
if isinstance(bindings, list):
|
|
2107
2161
|
next_bindings = [
|
|
2108
|
-
item
|
|
2109
|
-
|
|
2162
|
+
item
|
|
2163
|
+
for item in bindings
|
|
2164
|
+
if not (
|
|
2165
|
+
isinstance(item, dict)
|
|
2166
|
+
and item.get("match", {}).get("channel") == WPS_XIEZUO_CHANNEL_KEY
|
|
2167
|
+
)
|
|
2110
2168
|
]
|
|
2111
2169
|
desired_binding = {
|
|
2112
2170
|
"type": "route",
|
|
@@ -2130,7 +2188,9 @@ def _mutate_wps_xiezuo_enabled(
|
|
|
2130
2188
|
) -> bool:
|
|
2131
2189
|
normalized_account = str(account_id or WPS_XIEZUO_DEFAULT_ACCOUNT_ID).strip()
|
|
2132
2190
|
if normalized_account and normalized_account != WPS_XIEZUO_DEFAULT_ACCOUNT_ID:
|
|
2133
|
-
raise click.ClickException(
|
|
2191
|
+
raise click.ClickException(
|
|
2192
|
+
"WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default"
|
|
2193
|
+
)
|
|
2134
2194
|
changed = _ensure_plugin_enabled(config, WPS_XIEZUO_PLUGIN_ID) if enabled else False
|
|
2135
2195
|
channels = config.setdefault("channels", {})
|
|
2136
2196
|
channel_cfg = channels.setdefault(WPS_XIEZUO_CHANNEL_KEY, {})
|
|
@@ -2152,7 +2212,9 @@ def _check_wps_xiezuo_local_deps() -> dict[str, Any]:
|
|
|
2152
2212
|
}
|
|
2153
2213
|
|
|
2154
2214
|
|
|
2155
|
-
@click.group(
|
|
2215
|
+
@click.group(
|
|
2216
|
+
"openclaw", context_settings=CONTEXT_SETTINGS, help=build_resource_group_help(OPENCLAW_RESOURCE)
|
|
2217
|
+
)
|
|
2156
2218
|
def openclaw():
|
|
2157
2219
|
pass
|
|
2158
2220
|
|
|
@@ -2211,8 +2273,15 @@ async def _run_openclaw_repair_action(
|
|
|
2211
2273
|
@click.option("--session", "-s", default=None, help="OpenClaw Session key")
|
|
2212
2274
|
@click.option("--message", "-m", default=None, help="连接后发送的初始消息")
|
|
2213
2275
|
@click.option("--thinking", default=None, help="Thinking level override")
|
|
2214
|
-
@click.option(
|
|
2215
|
-
|
|
2276
|
+
@click.option(
|
|
2277
|
+
"--history-limit",
|
|
2278
|
+
type=click.IntRange(1, 10000),
|
|
2279
|
+
default=None,
|
|
2280
|
+
help="历史条数 (默认使用 OpenClaw 默认值)",
|
|
2281
|
+
)
|
|
2282
|
+
@click.option(
|
|
2283
|
+
"--timeout-ms", type=click.IntRange(1, 86400000), default=None, help="Agent timeout ms"
|
|
2284
|
+
)
|
|
2216
2285
|
@click.option("--deliver", is_flag=True, help="Deliver assistant replies")
|
|
2217
2286
|
@click.option("--insecure", "-k", is_flag=True, help="跳过 SSL 证书验证")
|
|
2218
2287
|
@dry_run_option()
|
|
@@ -2279,10 +2348,14 @@ def tui_openclaw(
|
|
|
2279
2348
|
elif not agent_ref and str(state.get("endpoint") or "").strip():
|
|
2280
2349
|
resolved_endpoint = str(state.get("endpoint") or "").strip()
|
|
2281
2350
|
else:
|
|
2282
|
-
resolved_region, detail = asyncio.run(
|
|
2351
|
+
resolved_region, detail = asyncio.run(
|
|
2352
|
+
_resolve_openclaw_detail_or_raise(agent_ref, region=region)
|
|
2353
|
+
)
|
|
2283
2354
|
resolved_endpoint = str(detail.get("endpoint") or "").strip()
|
|
2284
2355
|
if not resolved_endpoint:
|
|
2285
|
-
raise click.ClickException(
|
|
2356
|
+
raise click.ClickException(
|
|
2357
|
+
"未解析到 OpenClaw Endpoint,请传入 --endpoint 或指定 OpenClaw ID/名称"
|
|
2358
|
+
)
|
|
2286
2359
|
|
|
2287
2360
|
secret_kind, gateway_secret = _select_openclaw_gateway_secret(
|
|
2288
2361
|
gateway_token=gateway_token,
|
|
@@ -2293,18 +2366,26 @@ def tui_openclaw(
|
|
|
2293
2366
|
auth_mode = _openclaw_auth_mode_from_sources(detail, state)
|
|
2294
2367
|
if auth_mode in {"token", "password"} and not gateway_secret:
|
|
2295
2368
|
raise click.ClickException(
|
|
2296
|
-
"当前 OpenClaw Gateway 为 token/password 模式,
|
|
2369
|
+
"当前 OpenClaw Gateway 为 token/password 模式,"
|
|
2370
|
+
"agentengine openclaw tui 需要 OpenClaw Gateway token/password。\n"
|
|
2297
2371
|
"请使用: agentengine openclaw tui --gateway-token <token>\n"
|
|
2298
2372
|
"或设置: OPENCLAW_GATEWAY_TOKEN=<token> agentengine openclaw tui\n"
|
|
2299
|
-
"如果部署时传过 OPENCLAW_GATEWAY_TOKEN,请重新运行部署让本地
|
|
2373
|
+
"如果部署时传过 OPENCLAW_GATEWAY_TOKEN,请重新运行部署让本地 "
|
|
2374
|
+
".agentengine.state 记录该 token。\n"
|
|
2300
2375
|
"注意:这里不是 AgentEngine API Key(ak-*)。"
|
|
2301
2376
|
)
|
|
2302
2377
|
|
|
2303
|
-
terminal_api_key =
|
|
2378
|
+
terminal_api_key = (
|
|
2379
|
+
gateway_secret
|
|
2380
|
+
or api_key
|
|
2381
|
+
or str(detail.get("api_key") or state.get("api_key") or "").strip()
|
|
2382
|
+
or None
|
|
2383
|
+
)
|
|
2304
2384
|
click.secho("🖥️ OpenClaw Native Remote TUI", fg="blue", bold=True)
|
|
2305
2385
|
click.echo(f" Endpoint: {resolved_endpoint}")
|
|
2306
2386
|
if gateway_secret:
|
|
2307
|
-
|
|
2387
|
+
masked_gateway_secret = _mask_openclaw_secret(gateway_secret)
|
|
2388
|
+
click.echo(f" Runtime Auth: OpenClaw Gateway {secret_kind} {masked_gateway_secret}")
|
|
2308
2389
|
elif terminal_api_key:
|
|
2309
2390
|
click.echo(f" Runtime Auth: Bearer {_mask_openclaw_secret(terminal_api_key)}")
|
|
2310
2391
|
else:
|
|
@@ -2337,7 +2418,9 @@ def tui_openclaw(
|
|
|
2337
2418
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2338
2419
|
@click.option("--no-open", is_flag=True, help="仅打印 URL,不自动打开浏览器")
|
|
2339
2420
|
@cli_output_option()
|
|
2340
|
-
def gateway_open(
|
|
2421
|
+
def gateway_open(
|
|
2422
|
+
agent_ref: Optional[str], region: Optional[str], no_open: bool, output_mode: str | None
|
|
2423
|
+
):
|
|
2341
2424
|
"""打开 OpenClaw gateway Dashboard。"""
|
|
2342
2425
|
_ = output_mode
|
|
2343
2426
|
no_open = no_open or is_json_output()
|
|
@@ -2408,19 +2491,29 @@ def gateway_ws_url(agent_ref: Optional[str], region: Optional[str], output_mode:
|
|
|
2408
2491
|
"auth_mode": "cookie-session",
|
|
2409
2492
|
"note": "该 ws-url 依赖短链 cookie session,不承诺长期复用。",
|
|
2410
2493
|
}
|
|
2411
|
-
_emit_data_payload(
|
|
2494
|
+
_emit_data_payload(
|
|
2495
|
+
"OpenClaw Gateway 连接信息", payload, subtitle=str(detail.get("name") or "-")
|
|
2496
|
+
)
|
|
2412
2497
|
|
|
2413
2498
|
try:
|
|
2414
2499
|
asyncio.run(_run())
|
|
2415
2500
|
except Exception as e:
|
|
2416
|
-
_abort_openclaw_error(
|
|
2501
|
+
_abort_openclaw_error(
|
|
2502
|
+
e, context="获取 gateway ws-url 失败", argv=["openclaw", "gateway", "ws-url"]
|
|
2503
|
+
)
|
|
2417
2504
|
|
|
2418
2505
|
|
|
2419
2506
|
@openclaw_gateway.command("logs", context_settings=CONTEXT_SETTINGS)
|
|
2420
2507
|
@click.argument("agent_ref", required=False)
|
|
2421
2508
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2422
2509
|
@click.option("--instance", default=None, help="实例名;不填则查询全部实例")
|
|
2423
|
-
@click.option(
|
|
2510
|
+
@click.option(
|
|
2511
|
+
"--log-type",
|
|
2512
|
+
type=click.Choice(["stdout", "log"], case_sensitive=False),
|
|
2513
|
+
default="stdout",
|
|
2514
|
+
show_default=True,
|
|
2515
|
+
help="日志类型",
|
|
2516
|
+
)
|
|
2424
2517
|
@click.option("--start-time", default=None, help="开始时间,支持 Unix 毫秒或 ISO-8601")
|
|
2425
2518
|
@click.option("--end-time", default=None, help="结束时间,支持 Unix 毫秒或 ISO-8601")
|
|
2426
2519
|
@cli_output_option()
|
|
@@ -2465,7 +2558,9 @@ def gateway_logs(
|
|
|
2465
2558
|
emit_json(payload)
|
|
2466
2559
|
return
|
|
2467
2560
|
|
|
2468
|
-
print_title(
|
|
2561
|
+
print_title(
|
|
2562
|
+
"OpenClaw Gateway 日志", str(detail.get("name") or detail.get("agent_id") or "-")
|
|
2563
|
+
)
|
|
2469
2564
|
print_kv("实例", str(resp.get("instance") or "all"))
|
|
2470
2565
|
print_kv("日志类型", str(resp.get("log_type") or log_type))
|
|
2471
2566
|
print_kv("日志条数", str(resp.get("total") or 0))
|
|
@@ -2479,13 +2574,20 @@ def gateway_logs(
|
|
|
2479
2574
|
try:
|
|
2480
2575
|
asyncio.run(_run())
|
|
2481
2576
|
except Exception as e:
|
|
2482
|
-
_abort_openclaw_error(
|
|
2577
|
+
_abort_openclaw_error(
|
|
2578
|
+
e, context="获取 gateway 日志失败", argv=["openclaw", "gateway", "logs"]
|
|
2579
|
+
)
|
|
2483
2580
|
|
|
2484
2581
|
|
|
2485
2582
|
@openclaw_gateway.command("doctor", context_settings=CONTEXT_SETTINGS)
|
|
2486
2583
|
@click.argument("agent_ref", required=False)
|
|
2487
2584
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2488
|
-
@click.option(
|
|
2585
|
+
@click.option(
|
|
2586
|
+
"--fix",
|
|
2587
|
+
"do_fix",
|
|
2588
|
+
is_flag=True,
|
|
2589
|
+
help="不走 gateway 内诊断,改为通过控制面执行 openclaw doctor --fix",
|
|
2590
|
+
)
|
|
2489
2591
|
@cli_output_option()
|
|
2490
2592
|
def gateway_doctor(
|
|
2491
2593
|
agent_ref: Optional[str],
|
|
@@ -2519,7 +2621,7 @@ def gateway_doctor(
|
|
|
2519
2621
|
"ws_url": info.ws_url,
|
|
2520
2622
|
}
|
|
2521
2623
|
)
|
|
2522
|
-
|
|
2624
|
+
await gateway.connect()
|
|
2523
2625
|
checks.append(
|
|
2524
2626
|
{
|
|
2525
2627
|
"name": "cookie_ws_handshake",
|
|
@@ -2606,7 +2708,12 @@ def openclaw_channel():
|
|
|
2606
2708
|
@openclaw_channel.command("status", context_settings=CONTEXT_SETTINGS)
|
|
2607
2709
|
@click.argument("agent_ref", required=False)
|
|
2608
2710
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2609
|
-
@click.option(
|
|
2711
|
+
@click.option(
|
|
2712
|
+
"--channel",
|
|
2713
|
+
type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
|
|
2714
|
+
default=None,
|
|
2715
|
+
help="指定 channel",
|
|
2716
|
+
)
|
|
2610
2717
|
@click.option("--probe", is_flag=True, help="触发远端 probe 刷新 channel 快照")
|
|
2611
2718
|
@cli_output_option()
|
|
2612
2719
|
def channel_status(
|
|
@@ -2626,7 +2733,9 @@ def channel_status(
|
|
|
2626
2733
|
gateway = _build_gateway_client(resolved_region, detail)
|
|
2627
2734
|
try:
|
|
2628
2735
|
await gateway.connect()
|
|
2629
|
-
snapshot = await gateway.channels_status(
|
|
2736
|
+
snapshot = await gateway.channels_status(
|
|
2737
|
+
probe=probe, timeout_ms=8_000 if probe else None
|
|
2738
|
+
)
|
|
2630
2739
|
finally:
|
|
2631
2740
|
await gateway.close()
|
|
2632
2741
|
|
|
@@ -2640,26 +2749,67 @@ def channel_status(
|
|
|
2640
2749
|
"snapshot": snapshot,
|
|
2641
2750
|
"selected": _extract_channel_snapshot(snapshot, normalized_channel),
|
|
2642
2751
|
}
|
|
2643
|
-
_emit_data_payload(
|
|
2752
|
+
_emit_data_payload(
|
|
2753
|
+
"OpenClaw Channel 状态", payload, subtitle=str(detail.get("name") or "-")
|
|
2754
|
+
)
|
|
2644
2755
|
|
|
2645
2756
|
try:
|
|
2646
2757
|
asyncio.run(_run())
|
|
2647
2758
|
except Exception as e:
|
|
2648
|
-
_abort_openclaw_error(
|
|
2759
|
+
_abort_openclaw_error(
|
|
2760
|
+
e, context="获取 channel 状态失败", argv=["openclaw", "channel", "status"]
|
|
2761
|
+
)
|
|
2649
2762
|
|
|
2650
2763
|
|
|
2651
|
-
@openclaw_channel.command(
|
|
2764
|
+
@openclaw_channel.command(
|
|
2765
|
+
"connect", context_settings=CONTEXT_SETTINGS, help=OPENCLAW_CHANNEL_CONNECT_HELP
|
|
2766
|
+
)
|
|
2652
2767
|
@click.argument("agent_ref", required=False)
|
|
2653
2768
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2654
|
-
@click.option(
|
|
2769
|
+
@click.option(
|
|
2770
|
+
"--channel",
|
|
2771
|
+
"channel_name",
|
|
2772
|
+
type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
|
|
2773
|
+
required=True,
|
|
2774
|
+
help="目标 channel",
|
|
2775
|
+
)
|
|
2655
2776
|
@click.option("--open-qr", is_flag=True, help="仅微信:在本地浏览器额外打开二维码链接")
|
|
2656
2777
|
@click.option("--app-id", "wps_xiezuo_app_id", default=None, help="仅 WPS 协作:开放平台应用 ID")
|
|
2657
|
-
@click.option(
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
@click.option(
|
|
2661
|
-
|
|
2662
|
-
|
|
2778
|
+
@click.option(
|
|
2779
|
+
"--app-secret", "wps_xiezuo_app_secret", default=None, help="仅 WPS 协作:开放平台应用密钥"
|
|
2780
|
+
)
|
|
2781
|
+
@click.option(
|
|
2782
|
+
"--account-id",
|
|
2783
|
+
"wps_xiezuo_account_id",
|
|
2784
|
+
default="default",
|
|
2785
|
+
help="仅 WPS 协作:账号 ID;当前只支持 default",
|
|
2786
|
+
)
|
|
2787
|
+
@click.option(
|
|
2788
|
+
"--agent-id",
|
|
2789
|
+
"wps_xiezuo_agent_id",
|
|
2790
|
+
default="main",
|
|
2791
|
+
help="仅 WPS 协作:消息路由到的 OpenClaw agentId",
|
|
2792
|
+
)
|
|
2793
|
+
@click.option(
|
|
2794
|
+
"--dm-policy",
|
|
2795
|
+
"wps_xiezuo_dm_policy",
|
|
2796
|
+
type=click.Choice(("disabled", "open", "pairing", "allowlist")),
|
|
2797
|
+
default="pairing",
|
|
2798
|
+
help="仅 WPS 协作:私聊策略,默认 pairing",
|
|
2799
|
+
)
|
|
2800
|
+
@click.option(
|
|
2801
|
+
"--group-policy",
|
|
2802
|
+
"wps_xiezuo_group_policy",
|
|
2803
|
+
type=click.Choice(("open", "allowlist")),
|
|
2804
|
+
default="open",
|
|
2805
|
+
help="仅 WPS 协作:群聊策略,默认 open",
|
|
2806
|
+
)
|
|
2807
|
+
@click.option(
|
|
2808
|
+
"--base-url",
|
|
2809
|
+
"wps_xiezuo_base_url",
|
|
2810
|
+
default="https://openapi.wps.cn",
|
|
2811
|
+
help="仅 WPS 协作:WPS OpenAPI 基础地址",
|
|
2812
|
+
)
|
|
2663
2813
|
def channel_connect(
|
|
2664
2814
|
agent_ref: Optional[str],
|
|
2665
2815
|
region: Optional[str],
|
|
@@ -2779,13 +2929,18 @@ def channel_connect(
|
|
|
2779
2929
|
if normalized_channel == "wps-xiezuo":
|
|
2780
2930
|
app_id = str(wps_xiezuo_app_id or "").strip()
|
|
2781
2931
|
app_secret = str(wps_xiezuo_app_secret or "").strip()
|
|
2782
|
-
account_id =
|
|
2932
|
+
account_id = (
|
|
2933
|
+
str(wps_xiezuo_account_id or WPS_XIEZUO_DEFAULT_ACCOUNT_ID).strip()
|
|
2934
|
+
or WPS_XIEZUO_DEFAULT_ACCOUNT_ID
|
|
2935
|
+
)
|
|
2783
2936
|
if not app_id:
|
|
2784
2937
|
raise click.ClickException("连接 WPS 协作 channel 必须提供 --app-id")
|
|
2785
2938
|
if not app_secret:
|
|
2786
2939
|
raise click.ClickException("连接 WPS 协作 channel 必须提供 --app-secret")
|
|
2787
2940
|
if account_id != WPS_XIEZUO_DEFAULT_ACCOUNT_ID:
|
|
2788
|
-
raise click.ClickException(
|
|
2941
|
+
raise click.ClickException(
|
|
2942
|
+
"WPS 协作插件使用扁平 channel 配置,`--account-id` 仅支持 default"
|
|
2943
|
+
)
|
|
2789
2944
|
apply_gateway = _build_gateway_client(resolved_region, detail)
|
|
2790
2945
|
changed = False
|
|
2791
2946
|
try:
|
|
@@ -2800,7 +2955,8 @@ def channel_connect(
|
|
|
2800
2955
|
agent_id=str(wps_xiezuo_agent_id or "main").strip() or "main",
|
|
2801
2956
|
dm_policy=wps_xiezuo_dm_policy,
|
|
2802
2957
|
group_policy=wps_xiezuo_group_policy,
|
|
2803
|
-
base_url=str(wps_xiezuo_base_url or "https://openapi.wps.cn").strip()
|
|
2958
|
+
base_url=str(wps_xiezuo_base_url or "https://openapi.wps.cn").strip()
|
|
2959
|
+
or "https://openapi.wps.cn",
|
|
2804
2960
|
)
|
|
2805
2961
|
if changed:
|
|
2806
2962
|
await _config_apply_and_wait_for_reload(
|
|
@@ -2842,9 +2998,12 @@ def channel_connect(
|
|
|
2842
2998
|
await bootstrap_gateway.close()
|
|
2843
2999
|
|
|
2844
3000
|
config, _ = _extract_config_state(cfg_snapshot)
|
|
2845
|
-
existing_app_id =
|
|
2846
|
-
(
|
|
2847
|
-
|
|
3001
|
+
existing_app_id = (
|
|
3002
|
+
str(
|
|
3003
|
+
((config.get("channels") or {}).get(FEISHU_CHANNEL_KEY) or {}).get("appId") or ""
|
|
3004
|
+
).strip()
|
|
3005
|
+
or None
|
|
3006
|
+
)
|
|
2848
3007
|
onboarding = await _run_feishu_onboarding(existing_app_id)
|
|
2849
3008
|
changed_config = copy.deepcopy(config)
|
|
2850
3009
|
changed = _mutate_feishu_connect_config(changed_config, onboarding)
|
|
@@ -2885,7 +3044,9 @@ def channel_connect(
|
|
|
2885
3044
|
try:
|
|
2886
3045
|
asyncio.run(_run())
|
|
2887
3046
|
except Exception as e:
|
|
2888
|
-
_abort_openclaw_error(
|
|
3047
|
+
_abort_openclaw_error(
|
|
3048
|
+
e, context="channel connect 失败", argv=["openclaw", "channel", "connect"]
|
|
3049
|
+
)
|
|
2889
3050
|
|
|
2890
3051
|
|
|
2891
3052
|
def _run_channel_toggle_command(
|
|
@@ -2954,18 +3115,28 @@ def _run_channel_toggle_command(
|
|
|
2954
3115
|
"enabled": enabled,
|
|
2955
3116
|
"status": _extract_channel_snapshot(snapshot, normalized_channel),
|
|
2956
3117
|
}
|
|
2957
|
-
_emit_data_payload(
|
|
3118
|
+
_emit_data_payload(
|
|
3119
|
+
f"OpenClaw Channel {action.title()}", payload, subtitle=normalized_channel
|
|
3120
|
+
)
|
|
2958
3121
|
|
|
2959
3122
|
try:
|
|
2960
3123
|
asyncio.run(_run())
|
|
2961
3124
|
except Exception as e:
|
|
2962
|
-
_abort_openclaw_error(
|
|
3125
|
+
_abort_openclaw_error(
|
|
3126
|
+
e, context=f"channel {action} 失败", argv=["openclaw", "channel", action]
|
|
3127
|
+
)
|
|
2963
3128
|
|
|
2964
3129
|
|
|
2965
3130
|
@openclaw_channel.command("enable", context_settings=CONTEXT_SETTINGS)
|
|
2966
3131
|
@click.argument("agent_ref", required=False)
|
|
2967
3132
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2968
|
-
@click.option(
|
|
3133
|
+
@click.option(
|
|
3134
|
+
"--channel",
|
|
3135
|
+
"channel_name",
|
|
3136
|
+
type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
|
|
3137
|
+
required=True,
|
|
3138
|
+
help="目标 channel",
|
|
3139
|
+
)
|
|
2969
3140
|
@click.option("--account-id", default=None, help="账号 ID;飞书 V1 仅支持 default")
|
|
2970
3141
|
def channel_enable(
|
|
2971
3142
|
agent_ref: Optional[str],
|
|
@@ -2987,7 +3158,13 @@ def channel_enable(
|
|
|
2987
3158
|
@openclaw_channel.command("disable", context_settings=CONTEXT_SETTINGS)
|
|
2988
3159
|
@click.argument("agent_ref", required=False)
|
|
2989
3160
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
2990
|
-
@click.option(
|
|
3161
|
+
@click.option(
|
|
3162
|
+
"--channel",
|
|
3163
|
+
"channel_name",
|
|
3164
|
+
type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
|
|
3165
|
+
required=True,
|
|
3166
|
+
help="目标 channel",
|
|
3167
|
+
)
|
|
2991
3168
|
@click.option("--account-id", default=None, help="账号 ID;飞书 V1 仅支持 default")
|
|
2992
3169
|
def channel_disable(
|
|
2993
3170
|
agent_ref: Optional[str],
|
|
@@ -3009,9 +3186,16 @@ def channel_disable(
|
|
|
3009
3186
|
@openclaw_channel.command("doctor", context_settings=CONTEXT_SETTINGS)
|
|
3010
3187
|
@click.argument("agent_ref", required=False)
|
|
3011
3188
|
@region_option(default=None, envvar=None, help_text="区域 (默认优先读取 .agentengine.state)")
|
|
3012
|
-
@click.option(
|
|
3189
|
+
@click.option(
|
|
3190
|
+
"--channel",
|
|
3191
|
+
type=click.Choice(OPENCLAW_CHANNELS, case_sensitive=False),
|
|
3192
|
+
default=None,
|
|
3193
|
+
help="指定 channel",
|
|
3194
|
+
)
|
|
3013
3195
|
@cli_output_option()
|
|
3014
|
-
def channel_doctor(
|
|
3196
|
+
def channel_doctor(
|
|
3197
|
+
agent_ref: Optional[str], region: Optional[str], channel: Optional[str], output_mode: str | None
|
|
3198
|
+
):
|
|
3015
3199
|
"""检查 channel 接入前置条件。"""
|
|
3016
3200
|
_ = output_mode
|
|
3017
3201
|
normalized_channel = str(channel).lower() if channel else None
|
|
@@ -3028,20 +3212,28 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3028
3212
|
gateway = _build_gateway_client(resolved_region, detail)
|
|
3029
3213
|
try:
|
|
3030
3214
|
info = await gateway.build_access_info()
|
|
3031
|
-
checks.append(
|
|
3215
|
+
checks.append(
|
|
3216
|
+
{"name": "dashboard_short_link", "ok": True, "dashboard_url": info.access_url}
|
|
3217
|
+
)
|
|
3032
3218
|
await gateway.connect()
|
|
3033
3219
|
methods = gateway.methods
|
|
3034
3220
|
checks.append({"name": "cookie_ws_handshake", "ok": True, "methods": len(methods)})
|
|
3035
3221
|
snapshot = await gateway.channels_status(probe=False)
|
|
3036
3222
|
config_snapshot = await gateway.config_get()
|
|
3037
|
-
config =
|
|
3223
|
+
config = (
|
|
3224
|
+
config_snapshot.get("config")
|
|
3225
|
+
if isinstance(config_snapshot.get("config"), dict)
|
|
3226
|
+
else {}
|
|
3227
|
+
)
|
|
3038
3228
|
except Exception as exc:
|
|
3039
3229
|
checks.append({"name": "gateway_connectivity", "ok": False, "error": str(exc)})
|
|
3040
3230
|
finally:
|
|
3041
3231
|
await gateway.close()
|
|
3042
3232
|
|
|
3043
3233
|
channels_to_check = [normalized_channel] if normalized_channel else list(OPENCLAW_CHANNELS)
|
|
3044
|
-
plugin_entries = (
|
|
3234
|
+
plugin_entries = (
|
|
3235
|
+
((config.get("plugins") or {}).get("entries") or {}) if isinstance(config, dict) else {}
|
|
3236
|
+
)
|
|
3045
3237
|
for item in channels_to_check:
|
|
3046
3238
|
selected_snapshot = _extract_channel_snapshot(snapshot, item)
|
|
3047
3239
|
configured = _is_channel_configured(item, config=config, snapshot=selected_snapshot)
|
|
@@ -3059,7 +3251,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3059
3251
|
name="weixin_status_snapshot",
|
|
3060
3252
|
available=selected_snapshot is not None,
|
|
3061
3253
|
configured=configured,
|
|
3062
|
-
connect_required_message=
|
|
3254
|
+
connect_required_message=(
|
|
3255
|
+
"首次连接前微信状态快照可能为空," "执行 channel connect 后会自动补齐"
|
|
3256
|
+
),
|
|
3063
3257
|
)
|
|
3064
3258
|
)
|
|
3065
3259
|
checks.append(
|
|
@@ -3067,7 +3261,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3067
3261
|
name="weixin_qr_rpc",
|
|
3068
3262
|
available="web.login.start" in methods and "web.login.wait" in methods,
|
|
3069
3263
|
configured=configured,
|
|
3070
|
-
connect_required_message=
|
|
3264
|
+
connect_required_message=(
|
|
3265
|
+
"首次连接会先自动启用 bundled weixin plugin," "然后再暴露扫码 RPC"
|
|
3266
|
+
),
|
|
3071
3267
|
connect_required_ok=False,
|
|
3072
3268
|
required_methods=["web.login.start", "web.login.wait"],
|
|
3073
3269
|
)
|
|
@@ -3085,7 +3281,10 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3085
3281
|
name="feishu_status_snapshot",
|
|
3086
3282
|
available=selected_snapshot is not None,
|
|
3087
3283
|
configured=configured,
|
|
3088
|
-
connect_required_message=
|
|
3284
|
+
connect_required_message=(
|
|
3285
|
+
"飞书尚未完成 connect/onboarding,"
|
|
3286
|
+
"首次接入前不会出现在 channel snapshot 中"
|
|
3287
|
+
),
|
|
3089
3288
|
)
|
|
3090
3289
|
)
|
|
3091
3290
|
node_path = shutil.which("node")
|
|
@@ -3111,7 +3310,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3111
3310
|
name="wps_xiezuo_status_snapshot",
|
|
3112
3311
|
available=selected_snapshot is not None,
|
|
3113
3312
|
configured=configured,
|
|
3114
|
-
connect_required_message=
|
|
3313
|
+
connect_required_message=(
|
|
3314
|
+
"WPS 协作尚未完成配置," "首次 connect 前不会出现在 channel snapshot 中"
|
|
3315
|
+
),
|
|
3115
3316
|
)
|
|
3116
3317
|
)
|
|
3117
3318
|
dep_check = _check_wps_xiezuo_local_deps()
|
|
@@ -3134,15 +3335,20 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3134
3335
|
|
|
3135
3336
|
try:
|
|
3136
3337
|
payload = asyncio.run(_run())
|
|
3137
|
-
_emit_data_payload(
|
|
3338
|
+
_emit_data_payload(
|
|
3339
|
+
"OpenClaw Channel Doctor", payload, subtitle=str(payload.get("name") or "-")
|
|
3340
|
+
)
|
|
3138
3341
|
except Exception as e:
|
|
3139
|
-
_abort_openclaw_error(
|
|
3342
|
+
_abort_openclaw_error(
|
|
3343
|
+
e, context="channel doctor 执行失败", argv=["openclaw", "channel", "doctor"]
|
|
3344
|
+
)
|
|
3140
3345
|
|
|
3141
3346
|
|
|
3142
3347
|
@openclaw.command("deploy", context_settings=CONTEXT_SETTINGS)
|
|
3143
3348
|
@click.option("--name", "-n", default=None, help="OpenClaw 名称 (默认: openclaw-gateway)")
|
|
3144
3349
|
@click.option(
|
|
3145
|
-
"--region",
|
|
3350
|
+
"--region",
|
|
3351
|
+
"-r",
|
|
3146
3352
|
default="cn-beijing-6",
|
|
3147
3353
|
envvar="KSYUN_REGION",
|
|
3148
3354
|
help="部署区域 (默认: cn-beijing-6)",
|
|
@@ -3154,7 +3360,9 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3154
3360
|
help="安全预设: relaxed | strict | strictest (安全测试建议 strictest)",
|
|
3155
3361
|
)
|
|
3156
3362
|
@click.option("--strict-mode", "security_profile", flag_value="strict", help="快捷开启严格模式")
|
|
3157
|
-
@click.option(
|
|
3363
|
+
@click.option(
|
|
3364
|
+
"--strictest", "security_profile", flag_value="strictest", help="快捷开启最严格安全模式"
|
|
3365
|
+
)
|
|
3158
3366
|
@click.option(
|
|
3159
3367
|
"--image",
|
|
3160
3368
|
default=None,
|
|
@@ -3172,15 +3380,21 @@ def channel_doctor(agent_ref: Optional[str], region: Optional[str], channel: Opt
|
|
|
3172
3380
|
@click.option("--mem0-instance-id", default=None, help="mem0 实例 ID")
|
|
3173
3381
|
@click.option("--mem0-instance-name", default=None, help="mem0 实例名称(可选)")
|
|
3174
3382
|
@click.option("--mem0-region", default=None, help="mem0 实例区域(可选)")
|
|
3383
|
+
@env_options
|
|
3384
|
+
@click.option("--storage-size-gi", type=int, default=20, show_default=True, help="PVC 容量(Gi)")
|
|
3175
3385
|
@click.option(
|
|
3176
|
-
"--
|
|
3177
|
-
"extra_env",
|
|
3178
|
-
multiple=True,
|
|
3179
|
-
help="额外透传自定义环境变量,格式 KEY=VALUE,可重复传入",
|
|
3386
|
+
"--storage-mount-path", default=None, help="PVC 挂载目录(默认: /home/node/.openclaw)"
|
|
3180
3387
|
)
|
|
3181
|
-
@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)")
|
|
3183
3388
|
@click.option("--no-storage", is_flag=True, help="禁用默认 PVC 挂载")
|
|
3389
|
+
@click.option(
|
|
3390
|
+
"--agent-id",
|
|
3391
|
+
"agent_id_opt",
|
|
3392
|
+
default=None,
|
|
3393
|
+
help=(
|
|
3394
|
+
"指定要更新的已有 Agent ID;当前凭证有权限时会自动回填 "
|
|
3395
|
+
".agentengine.state 并走热更新(用于本地状态丢失后重新关联)"
|
|
3396
|
+
),
|
|
3397
|
+
)
|
|
3184
3398
|
@network_options
|
|
3185
3399
|
@dry_run_option("仅显示请求,不实际部署")
|
|
3186
3400
|
def deploy(
|
|
@@ -3196,9 +3410,11 @@ def deploy(
|
|
|
3196
3410
|
mem0_instance_name: Optional[str],
|
|
3197
3411
|
mem0_region: Optional[str],
|
|
3198
3412
|
extra_env: tuple[str, ...],
|
|
3413
|
+
env_file: Optional[str],
|
|
3199
3414
|
storage_size_gi: int,
|
|
3200
3415
|
storage_mount_path: Optional[str],
|
|
3201
3416
|
no_storage: bool,
|
|
3417
|
+
agent_id_opt: Optional[str],
|
|
3202
3418
|
enable_public_access: Optional[bool],
|
|
3203
3419
|
enable_vpc_access: bool,
|
|
3204
3420
|
vpc_id: Optional[str],
|
|
@@ -3222,7 +3438,8 @@ def deploy(
|
|
|
3222
3438
|
# 一键创建最严格实例(适合安全测试)
|
|
3223
3439
|
agentengine openclaw deploy --security-profile strictest
|
|
3224
3440
|
# 显式指定模型
|
|
3225
|
-
agentengine openclaw deploy --model-base-url https://api.example.com/v1
|
|
3441
|
+
agentengine openclaw deploy --model-base-url https://api.example.com/v1 \
|
|
3442
|
+
--model-api-key sk-xxx
|
|
3226
3443
|
# 使用自定义镜像
|
|
3227
3444
|
agentengine openclaw deploy --image ghcr.io/my-org/openclaw:v2
|
|
3228
3445
|
# 透传业务自定义环境变量
|
|
@@ -3237,6 +3454,7 @@ def deploy(
|
|
|
3237
3454
|
_option_was_explicit(ctx, "model_api_key"),
|
|
3238
3455
|
_option_was_explicit(ctx, "default_model"),
|
|
3239
3456
|
bool(extra_env),
|
|
3457
|
+
_option_was_explicit(ctx, "env_file"),
|
|
3240
3458
|
)
|
|
3241
3459
|
)
|
|
3242
3460
|
include_storage_on_update = any(
|
|
@@ -3267,9 +3485,11 @@ def deploy(
|
|
|
3267
3485
|
mem0_instance_name=mem0_instance_name,
|
|
3268
3486
|
mem0_region=mem0_region,
|
|
3269
3487
|
extra_env=extra_env,
|
|
3488
|
+
env_file=env_file,
|
|
3270
3489
|
storage_size_gi=storage_size_gi,
|
|
3271
3490
|
storage_mount_path=storage_mount_path,
|
|
3272
3491
|
no_storage=no_storage,
|
|
3492
|
+
agent_id_opt=agent_id_opt,
|
|
3273
3493
|
include_env_on_update=include_env_on_update,
|
|
3274
3494
|
include_storage_on_update=include_storage_on_update,
|
|
3275
3495
|
**network_cli_kwargs(
|
|
@@ -3302,9 +3522,11 @@ async def _deploy_openclaw(
|
|
|
3302
3522
|
mem0_instance_name: Optional[str],
|
|
3303
3523
|
mem0_region: Optional[str],
|
|
3304
3524
|
extra_env: tuple[str, ...] = (),
|
|
3525
|
+
env_file: Optional[str] = None,
|
|
3305
3526
|
storage_size_gi: int = 20,
|
|
3306
3527
|
storage_mount_path: Optional[str] = None,
|
|
3307
3528
|
no_storage: bool = False,
|
|
3529
|
+
agent_id_opt: Optional[str] = None,
|
|
3308
3530
|
include_env_on_update: bool = False,
|
|
3309
3531
|
include_storage_on_update: bool = False,
|
|
3310
3532
|
enable_public_access: Optional[bool] = None,
|
|
@@ -3317,25 +3539,25 @@ async def _deploy_openclaw(
|
|
|
3317
3539
|
):
|
|
3318
3540
|
"""异步部署 OpenClaw"""
|
|
3319
3541
|
from ksadk.api import AgentEngineClient
|
|
3320
|
-
from ksadk.
|
|
3321
|
-
|
|
3542
|
+
from ksadk.cli.env_options import (
|
|
3543
|
+
apply_explicit_env_with_shell_priority,
|
|
3544
|
+
inject_env_to_environ,
|
|
3545
|
+
resolve_runtime_env_overrides,
|
|
3546
|
+
)
|
|
3547
|
+
from ksadk.deployment.state import clear_state, load_state, save_state
|
|
3322
3548
|
|
|
3323
|
-
# 自动加载当前目录 .env
|
|
3549
|
+
# 自动加载当前目录 .env 或 --env-file 指定文件(优先级: --env > --env-file > shell > .env)
|
|
3324
3550
|
project_dir = Path(".").resolve()
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
if loaded:
|
|
3336
|
-
print_info(f"已从 .env 注入环境变量: {loaded} 项")
|
|
3337
|
-
except Exception as e:
|
|
3338
|
-
print_warn(f"读取 .env 失败,将继续使用当前 shell 环境: {e}")
|
|
3551
|
+
cli_env, auto_dotenv, shell_keys, env_source = resolve_runtime_env_overrides(
|
|
3552
|
+
env_file=env_file,
|
|
3553
|
+
extra_env=extra_env,
|
|
3554
|
+
base_dir=project_dir,
|
|
3555
|
+
)
|
|
3556
|
+
loaded = inject_env_to_environ(cli_env, auto_dotenv, shell_keys)
|
|
3557
|
+
if loaded:
|
|
3558
|
+
print_info(f"已从 {env_source or '--env'} 注入环境变量: {loaded} 项")
|
|
3559
|
+
if cli_env or auto_dotenv:
|
|
3560
|
+
include_env_on_update = True
|
|
3339
3561
|
|
|
3340
3562
|
# 读取本地状态 (判断创建 vs 更新)
|
|
3341
3563
|
state = load_state(project_dir)
|
|
@@ -3345,6 +3567,14 @@ async def _deploy_openclaw(
|
|
|
3345
3567
|
if state_kind == "openclaw":
|
|
3346
3568
|
existing_agent_id = state.get("agent_id")
|
|
3347
3569
|
state_name = str(state.get("name") or "").strip() or None
|
|
3570
|
+
explicit_agent_id = (agent_id_opt or "").strip() or None
|
|
3571
|
+
if explicit_agent_id:
|
|
3572
|
+
if existing_agent_id and existing_agent_id != explicit_agent_id:
|
|
3573
|
+
print_info(
|
|
3574
|
+
f"--agent-id ({explicit_agent_id}) 与本地状态 "
|
|
3575
|
+
f"({existing_agent_id}) 不一致,以 --agent-id 为准"
|
|
3576
|
+
)
|
|
3577
|
+
existing_agent_id = explicit_agent_id
|
|
3348
3578
|
|
|
3349
3579
|
if name:
|
|
3350
3580
|
openclaw_name = name
|
|
@@ -3372,18 +3602,19 @@ async def _deploy_openclaw(
|
|
|
3372
3602
|
default_model=default_model,
|
|
3373
3603
|
security_profile=security_profile,
|
|
3374
3604
|
)
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
env_vars.update(custom_env_vars)
|
|
3605
|
+
if cli_env or auto_dotenv:
|
|
3606
|
+
apply_explicit_env_with_shell_priority(env_vars, cli_env, auto_dotenv, shell_keys)
|
|
3378
3607
|
env_vars = _normalize_openclaw_gateway_auth_env(env_vars)
|
|
3379
3608
|
if not str(env_vars.get("OPENCLAW_MODEL_CATALOG_JSON") or "").strip():
|
|
3380
|
-
catalog_api_base = (
|
|
3381
|
-
|
|
3382
|
-
|
|
3609
|
+
catalog_api_base = model_base_url or _resolve_env(
|
|
3610
|
+
"OPENCLAW_MODEL_BASE_URL",
|
|
3611
|
+
"OPENAI_BASE_URL",
|
|
3612
|
+
"OPENAI_API_BASE",
|
|
3613
|
+
"LLM_API_BASE",
|
|
3614
|
+
"MODEL_API_BASE",
|
|
3383
3615
|
)
|
|
3384
|
-
catalog_api_key = (
|
|
3385
|
-
|
|
3386
|
-
or _resolve_env("OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY")
|
|
3616
|
+
catalog_api_key = model_api_key or _resolve_env(
|
|
3617
|
+
"OPENCLAW_MODEL_API_KEY", "OPENAI_API_KEY", "LLM_API_KEY", "MODEL_API_KEY"
|
|
3387
3618
|
)
|
|
3388
3619
|
provider_catalog = await fetch_provider_model_catalog(
|
|
3389
3620
|
api_base=catalog_api_base,
|
|
@@ -3391,7 +3622,9 @@ async def _deploy_openclaw(
|
|
|
3391
3622
|
)
|
|
3392
3623
|
selected_catalog = _filter_openclaw_provider_catalog(env_vars, provider_catalog)
|
|
3393
3624
|
if _apply_openclaw_provider_model_catalog(env_vars, selected_catalog):
|
|
3394
|
-
print_info(
|
|
3625
|
+
print_info(
|
|
3626
|
+
f"已从模型服务 /v1/models 同步 OpenClaw 模型元数据: {len(selected_catalog)} 个"
|
|
3627
|
+
)
|
|
3395
3628
|
memory_config = _build_openclaw_memory_config(
|
|
3396
3629
|
memory_system=memory_system,
|
|
3397
3630
|
mem0_instance_id=mem0_instance_id,
|
|
@@ -3412,7 +3645,7 @@ async def _deploy_openclaw(
|
|
|
3412
3645
|
|
|
3413
3646
|
# 构建环境变量列表
|
|
3414
3647
|
env_list = [
|
|
3415
|
-
{"Key": k, "Value": v, "IsSensitive":
|
|
3648
|
+
{"Key": k, "Value": v, "IsSensitive": is_sensitive_env_var(k)}
|
|
3416
3649
|
for k, v in env_vars.items()
|
|
3417
3650
|
]
|
|
3418
3651
|
# 资源规格(支持通过环境变量覆盖)
|
|
@@ -3445,7 +3678,9 @@ async def _deploy_openclaw(
|
|
|
3445
3678
|
if storage_config:
|
|
3446
3679
|
request_data["storage"] = storage_config
|
|
3447
3680
|
if existing_agent_id and include_storage_on_update and no_storage:
|
|
3448
|
-
print_warn(
|
|
3681
|
+
print_warn(
|
|
3682
|
+
"更新已有 OpenClaw 时 `--no-storage` 不会删除服务端既有挂盘配置;默认保留已有配置。"
|
|
3683
|
+
)
|
|
3449
3684
|
network_payload = build_network_payload(
|
|
3450
3685
|
enable_public_access=enable_public_access,
|
|
3451
3686
|
enable_vpc_access=enable_vpc_access,
|
|
@@ -3456,7 +3691,7 @@ async def _deploy_openclaw(
|
|
|
3456
3691
|
region=region,
|
|
3457
3692
|
dry_run=dry_run,
|
|
3458
3693
|
)
|
|
3459
|
-
#
|
|
3694
|
+
# Create 默认开公网;update 使用原始 payload,None 表示保留现有配置。
|
|
3460
3695
|
create_network_payload = dict(network_payload) if network_payload is not None else {}
|
|
3461
3696
|
if "enable_public_access" not in create_network_payload:
|
|
3462
3697
|
create_network_payload["enable_public_access"] = True
|
|
@@ -3477,12 +3712,15 @@ async def _deploy_openclaw(
|
|
|
3477
3712
|
request_data["image_credential"] = image_credential
|
|
3478
3713
|
elif kcr_password and not kcr_username and registry_kind != "personal_kcr":
|
|
3479
3714
|
print_warn(
|
|
3480
|
-
|
|
3715
|
+
"检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,已忽略"
|
|
3716
|
+
f"{registry_kind_label(registry_kind)}镜像凭证;"
|
|
3481
3717
|
"企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD"
|
|
3482
3718
|
)
|
|
3483
3719
|
elif "/agentengine-public/" not in image_ref:
|
|
3484
3720
|
if registry_kind == "personal_kcr":
|
|
3485
|
-
print_warn(
|
|
3721
|
+
print_warn(
|
|
3722
|
+
"未配置个人版 KCR 镜像凭证 (KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取"
|
|
3723
|
+
)
|
|
3486
3724
|
else:
|
|
3487
3725
|
print_warn(
|
|
3488
3726
|
f"未配置{registry_kind_label(registry_kind)}镜像凭证 "
|
|
@@ -3515,6 +3753,37 @@ async def _deploy_openclaw(
|
|
|
3515
3753
|
latest_status = None
|
|
3516
3754
|
updated_existing_agent = False
|
|
3517
3755
|
async with AgentEngineClient(region=region) as client:
|
|
3756
|
+
if explicit_agent_id:
|
|
3757
|
+
try:
|
|
3758
|
+
detail = await client.get_agent(
|
|
3759
|
+
explicit_agent_id, include_api_key=True
|
|
3760
|
+
)
|
|
3761
|
+
except Exception as e:
|
|
3762
|
+
raise click.ClickException(
|
|
3763
|
+
f"指定的 Agent ID '{explicit_agent_id}' 不存在,或当前凭证无权限访问。\n"
|
|
3764
|
+
f" 详情: {e}\n"
|
|
3765
|
+
" 👉 请确认 agent_id 正确,且当前 AK/SK / 账号有该 Agent 的权限。"
|
|
3766
|
+
) from e
|
|
3767
|
+
qa = detail.get("quick_access", {}) or {}
|
|
3768
|
+
basic = detail.get("basic", {}) or {}
|
|
3769
|
+
recovered_state = state.copy()
|
|
3770
|
+
recovered_state.update(
|
|
3771
|
+
{
|
|
3772
|
+
"agent_id": explicit_agent_id,
|
|
3773
|
+
"name": basic.get("name") or openclaw_name,
|
|
3774
|
+
"type": "openclaw",
|
|
3775
|
+
"region": region,
|
|
3776
|
+
"endpoint": qa.get("public_endpoint"),
|
|
3777
|
+
}
|
|
3778
|
+
)
|
|
3779
|
+
if qa.get("api_key"):
|
|
3780
|
+
recovered_state["api_key"] = qa["api_key"]
|
|
3781
|
+
recovered_state = {
|
|
3782
|
+
k: v for k, v in recovered_state.items() if v is not None
|
|
3783
|
+
}
|
|
3784
|
+
save_state(project_dir, recovered_state)
|
|
3785
|
+
state = recovered_state
|
|
3786
|
+
print_info(f"已通过 --agent-id 关联已有 Agent: {explicit_agent_id}")
|
|
3518
3787
|
if existing_agent_id:
|
|
3519
3788
|
print_info(f"检测到本地状态: {existing_agent_id},执行更新...")
|
|
3520
3789
|
try:
|
|
@@ -3538,12 +3807,13 @@ async def _deploy_openclaw(
|
|
|
3538
3807
|
except Exception as update_err:
|
|
3539
3808
|
err_msg = str(update_err)
|
|
3540
3809
|
not_found = (
|
|
3541
|
-
"code: 404" in err_msg.lower()
|
|
3542
|
-
or "agent not found" in err_msg.lower()
|
|
3810
|
+
"code: 404" in err_msg.lower() or "agent not found" in err_msg.lower()
|
|
3543
3811
|
)
|
|
3544
3812
|
if not not_found:
|
|
3545
3813
|
raise
|
|
3546
|
-
print_warn(
|
|
3814
|
+
print_warn(
|
|
3815
|
+
f"本地状态失效 ({existing_agent_id}),将自动回退为新建: {update_err}"
|
|
3816
|
+
)
|
|
3547
3817
|
cleared = clear_state(project_dir, key=existing_agent_id)
|
|
3548
3818
|
if cleared:
|
|
3549
3819
|
print_info("已清理失效的 .agentengine.state")
|
|
@@ -3568,10 +3838,12 @@ async def _deploy_openclaw(
|
|
|
3568
3838
|
attempts=12,
|
|
3569
3839
|
interval_seconds=5,
|
|
3570
3840
|
include_api_key=True,
|
|
3571
|
-
detail_fetcher=lambda agent_ref, include_api_key:
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3841
|
+
detail_fetcher=lambda agent_ref, include_api_key: (
|
|
3842
|
+
_get_openclaw_detail_with_client(
|
|
3843
|
+
client,
|
|
3844
|
+
agent_ref,
|
|
3845
|
+
include_api_key=include_api_key,
|
|
3846
|
+
)
|
|
3575
3847
|
),
|
|
3576
3848
|
suppress_transient_not_found_log=True,
|
|
3577
3849
|
)
|
|
@@ -3583,8 +3855,7 @@ async def _deploy_openclaw(
|
|
|
3583
3855
|
else:
|
|
3584
3856
|
print_warn("实例创建中,稍后使用 'agentengine openclaw list' 查看")
|
|
3585
3857
|
elif agent_id and (
|
|
3586
|
-
not str(endpoint or "").strip()
|
|
3587
|
-
or not str(api_key or "").strip()
|
|
3858
|
+
not str(endpoint or "").strip() or not str(api_key or "").strip()
|
|
3588
3859
|
):
|
|
3589
3860
|
latest = await get_latest_agent_access(
|
|
3590
3861
|
client,
|
|
@@ -3594,10 +3865,12 @@ async def _deploy_openclaw(
|
|
|
3594
3865
|
initial_delay_seconds=2,
|
|
3595
3866
|
require_complete_access=True,
|
|
3596
3867
|
include_api_key=True,
|
|
3597
|
-
detail_fetcher=lambda agent_ref, include_api_key:
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3868
|
+
detail_fetcher=lambda agent_ref, include_api_key: (
|
|
3869
|
+
_get_openclaw_detail_with_client(
|
|
3870
|
+
client,
|
|
3871
|
+
agent_ref,
|
|
3872
|
+
include_api_key=include_api_key,
|
|
3873
|
+
)
|
|
3601
3874
|
),
|
|
3602
3875
|
suppress_transient_not_found_log=True,
|
|
3603
3876
|
)
|
|
@@ -3627,11 +3900,13 @@ async def _deploy_openclaw(
|
|
|
3627
3900
|
state_payload["openclaw_gateway_token"] = state_gateway_token
|
|
3628
3901
|
save_state(project_dir, state_payload)
|
|
3629
3902
|
|
|
3630
|
-
#
|
|
3903
|
+
# 更新已有实例才回读;新建后立即查询可能早于底层落库。
|
|
3631
3904
|
if updated_existing_agent and agent_id:
|
|
3632
3905
|
try:
|
|
3633
3906
|
latest = await client.get_agent(agent_id=agent_id, include_api_key=False)
|
|
3634
|
-
latest_status =
|
|
3907
|
+
latest_status = (
|
|
3908
|
+
str(((latest.get("basic") or {}).get("status") or "")).upper() or None
|
|
3909
|
+
)
|
|
3635
3910
|
except Exception:
|
|
3636
3911
|
latest_status = None
|
|
3637
3912
|
|
|
@@ -3672,7 +3947,9 @@ def list_openclaws(region: str, page: int, size: int, dry_run: bool, output_mode
|
|
|
3672
3947
|
|
|
3673
3948
|
async def _list():
|
|
3674
3949
|
async with AgentEngineClient(region=region, dry_run=dry_run) as client:
|
|
3675
|
-
resp = await client.list_agents(
|
|
3950
|
+
resp = await client.list_agents(
|
|
3951
|
+
region=region, framework="openclaw", page=page, page_size=size
|
|
3952
|
+
)
|
|
3676
3953
|
agents = resp.get("agents", []) or []
|
|
3677
3954
|
total = int(resp.get("total") or len(agents))
|
|
3678
3955
|
rows = []
|
|
@@ -3765,7 +4042,9 @@ def status(agent_ref: Optional[str], region: Optional[str], dry_run: bool, outpu
|
|
|
3765
4042
|
agent = await client.get_agent(name=agent_ref)
|
|
3766
4043
|
|
|
3767
4044
|
if not agent:
|
|
3768
|
-
raise resolution_error(
|
|
4045
|
+
raise resolution_error(
|
|
4046
|
+
f"未找到 OpenClaw: {agent_ref}", hints=["agentengine openclaw list"]
|
|
4047
|
+
)
|
|
3769
4048
|
|
|
3770
4049
|
detail = _flatten_agent_detail(agent)
|
|
3771
4050
|
status_val = detail.get("status", "UNKNOWN")
|
|
@@ -3781,7 +4060,11 @@ def status(agent_ref: Optional[str], region: Optional[str], dry_run: bool, outpu
|
|
|
3781
4060
|
("框架", str(detail.get("framework") or "-"), None),
|
|
3782
4061
|
("区域", str(detail.get("region") or region), None),
|
|
3783
4062
|
("Endpoint", str(detail.get("endpoint") or "N/A"), "#58a6ff"),
|
|
3784
|
-
(
|
|
4063
|
+
(
|
|
4064
|
+
"Langfuse",
|
|
4065
|
+
str(detail.get("langfuse_url") or "-"),
|
|
4066
|
+
"#58a6ff" if detail.get("langfuse_url") else None,
|
|
4067
|
+
),
|
|
3785
4068
|
("镜像", str(detail.get("artifact_path") or "-"), None),
|
|
3786
4069
|
("创建时间", created_at_display, None),
|
|
3787
4070
|
("更新时间", updated_at_display, None),
|
|
@@ -3844,6 +4127,7 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
|
|
|
3844
4127
|
|
|
3845
4128
|
# 清理本地状态
|
|
3846
4129
|
from ksadk.deployment.state import clear_state
|
|
4130
|
+
|
|
3847
4131
|
try:
|
|
3848
4132
|
removed = clear_state(Path("."), key=agent_ref)
|
|
3849
4133
|
if removed:
|
|
@@ -3866,7 +4150,7 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
|
|
|
3866
4150
|
"failed": failed_refs,
|
|
3867
4151
|
}
|
|
3868
4152
|
|
|
3869
|
-
dry_run_kwargs = {"dry_run": dry_run}
|
|
4153
|
+
dry_run_kwargs: dict[str, Any] = {"dry_run": dry_run}
|
|
3870
4154
|
if is_json_output():
|
|
3871
4155
|
dry_run_kwargs.update(
|
|
3872
4156
|
dry_run_resource="openclaw",
|
|
@@ -3904,7 +4188,13 @@ def _delete_impl(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry
|
|
|
3904
4188
|
@confirm_options()
|
|
3905
4189
|
@dry_run_option()
|
|
3906
4190
|
@cli_output_option()
|
|
3907
|
-
def delete(
|
|
4191
|
+
def delete(
|
|
4192
|
+
agent_refs: tuple[str, ...],
|
|
4193
|
+
region: str,
|
|
4194
|
+
assume_yes: bool,
|
|
4195
|
+
dry_run: bool,
|
|
4196
|
+
output_mode: str | None,
|
|
4197
|
+
):
|
|
3908
4198
|
"""删除 OpenClaw 实例。"""
|
|
3909
4199
|
_ = output_mode
|
|
3910
4200
|
_delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)
|
|
@@ -3916,7 +4206,13 @@ def delete(agent_refs: tuple[str, ...], region: str, assume_yes: bool, dry_run:
|
|
|
3916
4206
|
@confirm_options()
|
|
3917
4207
|
@dry_run_option()
|
|
3918
4208
|
@cli_output_option()
|
|
3919
|
-
def destroy(
|
|
4209
|
+
def destroy(
|
|
4210
|
+
agent_refs: tuple[str, ...],
|
|
4211
|
+
region: str,
|
|
4212
|
+
assume_yes: bool,
|
|
4213
|
+
dry_run: bool,
|
|
4214
|
+
output_mode: str | None,
|
|
4215
|
+
):
|
|
3920
4216
|
"""删除 OpenClaw 实例。"""
|
|
3921
4217
|
_ = output_mode
|
|
3922
4218
|
_delete_impl(agent_refs=agent_refs, region=region, assume_yes=assume_yes, dry_run=dry_run)
|