@gguf/claw 2026.2.9 → 2026.2.13
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.
- package/CHANGELOG.md +159 -0
- package/LICENSE +1 -1
- package/dist/{accounts-MyAvfCVH.js → accounts-54zZMYCo.js} +5 -2
- package/dist/{accounts-DbzMEfKN.js → accounts-Bvh0DFxS.js} +5 -2
- package/dist/{acp-cli-MZ3h1E1n.js → acp-cli-BslcOPdx.js} +146 -25
- package/dist/{acp-cli-DKJRTfwB.js → acp-cli-D6rk5cOh.js} +145 -24
- package/dist/{agent-whSJT2Lk.js → agent-C0yL70cy.js} +26 -20
- package/dist/{agent-c1QNeDmV.js → agent-DjZxytiC.js} +26 -20
- package/dist/{agent-scope-D3me2AZa.js → agent-scope-Bkr9fZtl.js} +31 -14
- package/dist/{agent-scope-Dp8sREli.js → agent-scope-DASgjz2_.js} +199 -14
- package/dist/{agent-scope-DnyDZ5RH.js → agent-scope-GYIs5dyU.js} +30 -13
- package/dist/{agent-scope-Dpav7C-i.js → agent-scope-okUOVjE5.js} +32 -11
- package/dist/audio-preflight-B0kLz-Ma.js +60 -0
- package/dist/audio-preflight-BCs_J33s.js +60 -0
- package/dist/audio-preflight-CTl2RCyF.js +71 -0
- package/dist/audio-preflight-MhF6YlAY.js +74 -0
- package/dist/{audit-BFYy1qSw.js → audit-BYfhZ7LA.js} +454 -31
- package/dist/{audit-Dn2cBl2x.js → audit-CfPZ_5Id.js} +452 -29
- package/dist/auth-9nTeB2Je.js +602 -0
- package/dist/auth-CLhyWwAU.js +593 -0
- package/dist/{auth-health-Cx5exPMV.js → auth-health-CWiLyzSr.js} +1 -1
- package/dist/{auth-health-DjT4fUpw.js → auth-health-qD4RND47.js} +1 -1
- package/dist/{auth-profiles-FJ3VY25a.js → auth-profiles-Cp9MtUdM.js} +353 -33
- package/dist/build-info.json +2 -2
- package/dist/bundled/boot-md/handler.js +33 -25
- package/dist/bundled/session-memory/handler.js +33 -22
- package/dist/{call-CD2IZCHT.js → call-CjEdFGAf.js} +7 -7
- package/dist/{call-CM25qgxz.js → call-DAfkvtVq.js} +6 -6
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +7 -1
- package/dist/{channel-options-CremuJyh.js → channel-options-B8dPzlyO.js} +4 -4
- package/dist/{channel-options-D-JnJ4Ft.js → channel-options-Bq5IC5Tv.js} +12 -7
- package/dist/{channel-selection-DAHCVAX4.js → channel-selection-BaW1xXEa.js} +2 -2
- package/dist/{channel-selection-DPV9hvY8.js → channel-selection-dR0jCgTn.js} +2 -2
- package/dist/{channels-cli-6deHFr9t.js → channels-cli-hPo28hWS.js} +61 -56
- package/dist/{channels-cli-D3tKmhlt.js → channels-cli-zi3rO0jq.js} +62 -57
- package/dist/{channels-status-issues-BN1ICfdy.js → channels-status-issues-kb-M2Fi0.js} +1 -1
- package/dist/{channels-status-issues-DFhI_u0p.js → channels-status-issues-ketdwZun.js} +1 -1
- package/dist/{chrome-B2UjqY-9.js → chrome--Fe8F5Kf.js} +24 -12
- package/dist/{chrome-COabMr6f.js → chrome-BWeMtFGf.js} +24 -12
- package/dist/{chrome-CQd_MVOA.js → chrome-Bx24uq7B.js} +27 -15
- package/dist/{chrome-CxRJz4ZD.js → chrome-n_3rtK2c.js} +22 -11
- package/dist/{clack-prompter-BkNZ4Xdw.js → clack-prompter-B-tJmODa.js} +5 -5
- package/dist/{clack-prompter-DuBVnTKy.js → clack-prompter-DpuKn_Uy.js} +5 -5
- package/dist/cli/daemon-cli.js +8 -1
- package/dist/cli-9lwO6Ttx.js +94 -0
- package/dist/cli-CNNdyxPO.js +91 -0
- package/dist/{client-DMloFP_O.js → client-BhZjzrH2.js} +73 -9
- package/dist/{client-C0gQ7hrj.js → client-DyAxKXKY.js} +73 -9
- package/dist/{command-format-ayFsmwwz.js → command-format-Bxe0mWee.js} +1 -1
- package/dist/{command-options-BQdH6qnK.js → command-options-BDV7Xgs-.js} +9 -4
- package/dist/{commands-BWHYcc83.js → commands-gOiRcfoU.js} +4 -4
- package/dist/{tui-formatters-BDP_71Xt.js → commands-registry-6NUFrejL.js} +6 -114
- package/dist/{tui-formatters-CIx4sCQO.js → commands-registry-DGgkLQ7A.js} +6 -114
- package/dist/{completion-cli-DEJia0V1.js → completion-cli-B1kHKJZX.js} +30 -30
- package/dist/{completion-cli-D_0fx2O6.js → completion-cli-Drks7xRK.js} +3 -3
- package/dist/{config-CQt4vGxI.js → config-7NCznPmF.js} +336 -97
- package/dist/{config-fCnPoWjU.js → config-B8v0zg48.js} +295 -99
- package/dist/{config-Bj2eDa02.js → config-CeWMHOiQ.js} +295 -99
- package/dist/{config-ethqi73X.js → config-D8pgDSNo.js} +358 -99
- package/dist/{config-guard-BJuqQvng.js → config-guard-RbHxYc9j.js} +212 -63
- package/dist/{configure-skrLiSwW.js → configure-DLp2Xz7L.js} +59 -40
- package/dist/{configure-C-pYuYg_.js → configure-Su1S0gi-.js} +58 -39
- package/dist/control-auth-BlWU-jBl.js +54 -0
- package/dist/control-auth-C8rIqEdA.js +54 -0
- package/dist/{control-service-BDgF-FZ0.js → control-service-BNDthc1N.js} +11 -5
- package/dist/{control-service-Djd_WI3_.js → control-service-COF59GQe.js} +10 -4
- package/dist/control-ui/assets/{index-CnB9IO4a.js → index-BECn2L1T.js} +369 -368
- package/dist/control-ui/assets/index-BECn2L1T.js.map +1 -0
- package/dist/control-ui/assets/index-DRPcd1Z4.css +1 -0
- package/dist/control-ui/index.html +2 -2
- package/dist/{cron-cli-CB6CufAb.js → cron-cli-CSy4-JGS.js} +20 -20
- package/dist/{cron-cli-Db6fardJ.js → cron-cli-Db3uCDIT.js} +21 -21
- package/dist/{daemon-cli-Xe22v7lZ.js → daemon-cli-BLbzcTuD.js} +61 -22
- package/dist/{daemon-cli-BlHK0ly2.js → daemon-cli-DR0D35MO.js} +60 -21
- package/dist/{daemon-runtime-CMqH8BUE.js → daemon-runtime-ZWXvLDxx.js} +3 -3
- package/dist/{daemon-runtime-DwQFvDXZ.js → daemon-runtime-pVcZ2KDE.js} +3 -3
- package/dist/{deliver-CD7-BhYD.js → deliver-BHNoC9Yk.js} +396 -290
- package/dist/{deliver-BdGjIcTC.js → deliver-C_5eGQrX.js} +392 -286
- package/dist/{deliver-nTKaXF--.js → deliver-DPHZlWgr.js} +392 -287
- package/dist/{deliver-CDMGxRoW.js → deliver-geVWJ52I.js} +394 -288
- package/dist/{deps-BDQ_K8zf.js → deps-CP0dcOgD.js} +2 -2
- package/dist/{deps-D60FbgTP.js → deps-DW5r2IEk.js} +2 -2
- package/dist/{devices-cli-N559801X.js → devices-cli-BViqX5pl.js} +15 -15
- package/dist/{devices-cli-IxmPLIk8.js → devices-cli-DpYaY-iM.js} +14 -14
- package/dist/{directory-cli-Caq-OYk8.js → directory-cli-BWD1DdKf.js} +16 -16
- package/dist/{directory-cli-ClrdmQL-.js → directory-cli-BcvZfkfo.js} +17 -17
- package/dist/{dispatcher-BfXtm4Dl.js → dispatcher-4Qn951N3.js} +5 -3
- package/dist/{dns-cli-DgVO0Pkw.js → dns-cli-_Ych2tu9.js} +12 -12
- package/dist/{dns-cli-BTNZkWHs.js → dns-cli-gQCxUXgU.js} +13 -13
- package/dist/{docs-cli-9Xan7C6D.js → docs-cli-Bseiau7J.js} +7 -7
- package/dist/{docs-cli-DZULc91f.js → docs-cli-DzBTlWQE.js} +8 -8
- package/dist/{doctor-D39rZvNH.js → doctor-BNkYYahD.js} +37 -36
- package/dist/{doctor-Dq1YeYdH.js → doctor-DzIgdPx1.js} +37 -36
- package/dist/entry.js +77 -21
- package/dist/{env-B5YXooWp.js → env-BUuSkE19.js} +1 -1
- package/dist/{exec-DFOtZbI0.js → exec-BPQSKwGa.js} +5 -3
- package/dist/{exec-B8JKbXKW.js → exec-DqZFMawz.js} +5 -3
- package/dist/{exec-Bas1hoSJ.js → exec-EKUaAU91.js} +57 -18
- package/dist/{exec-CiH_vkWn.js → exec-_PSUrMP8.js} +528 -19
- package/dist/{exec-approvals-DGPTjO0N.js → exec-approvals-Bqk-tIxY.js} +134 -51
- package/dist/{exec-approvals-C9InMoAB.js → exec-approvals-C67V-ljH.js} +134 -51
- package/dist/{exec-approvals-cli-EASbqFd-.js → exec-approvals-cli-D6vfSqQu.js} +22 -22
- package/dist/{exec-approvals-cli-DPHItoxG.js → exec-approvals-cli-DAdoki_R.js} +21 -21
- package/dist/extensionAPI.js +8518 -9140
- package/dist/fetch-Bz1WxfzV.js +285 -0
- package/dist/fetch-D2O8s8I1.js +285 -0
- package/dist/fetch-Dm-nCwa_.js +285 -0
- package/dist/fetch-wuOZDzdT.js +285 -0
- package/dist/{gateway-cli-BFqUIif8.js → gateway-cli-C-k7JPlr.js} +1868 -1072
- package/dist/{gateway-cli-v4kSPsLE.js → gateway-cli-DIIJ9Z0Y.js} +1870 -1074
- package/dist/{gateway-rpc-D6LrkcSA.js → gateway-rpc-D6jLh81b.js} +3 -3
- package/dist/{gateway-rpc-dHFK02Kk.js → gateway-rpc-aqysUyf5.js} +3 -3
- package/dist/{github-copilot-auth-CQIWc0hC.js → github-copilot-auth-BUqfX7hG.js} +316 -52
- package/dist/{github-copilot-auth-D2jfnapd.js → github-copilot-auth-By-nyRb6.js} +316 -52
- package/dist/{github-copilot-token-SLWintYd.js → github-copilot-token-C9W4SY9o.js} +7 -4
- package/dist/{github-copilot-token-BW-SEg7E.js → github-copilot-token-CiF5Iyi2.js} +6 -3
- package/dist/{github-copilot-token-C9IJh2Pn.js → github-copilot-token-DatTe1w-.js} +6 -3
- package/dist/{github-copilot-token-wCk9Fg_E.js → github-copilot-token-c9Igt3ZH.js} +6 -3
- package/dist/{gmail-setup-utils-CVNgLkXL.js → gmail-setup-utils-HvKMdooP.js} +4 -4
- package/dist/{gmail-setup-utils-CAM1vbUS.js → gmail-setup-utils-c-iF00aL.js} +3 -3
- package/dist/{health-format-C77hrjEQ.js → health-format-BORnJOeS.js} +106 -44
- package/dist/{health-format-DDYtlkB9.js → health-format-Nd0jcoqM.js} +105 -43
- package/dist/{help-format-CUnac_bT.js → help-format-Cd5PLwXe.js} +1 -1
- package/dist/{help-format-aiW76js8.js → help-format-DYBEvMOX.js} +1 -1
- package/dist/{hooks-cli-DsflBRxX.js → hooks-cli-3KdsbdRi.js} +53 -47
- package/dist/{hooks-cli-C7kctMuZ.js → hooks-cli-BThja6wK.js} +53 -47
- package/dist/{hooks-status-DRAVHSPg.js → hooks-status-BbIz0zmm.js} +6 -5
- package/dist/{hooks-status-lHWrY64E.js → hooks-status-DPJORMB6.js} +6 -5
- package/dist/{image--gbzucyh.js → image-BaJKrmCs.js} +12 -8
- package/dist/{image-ORs4LLwg.js → image-D-5pUELC.js} +13 -9
- package/dist/{image-DMnjYGdA.js → image-TvL5YI_W.js} +13 -9
- package/dist/{image-BVNytEIn.js → image-bodq5cUH.js} +13 -9
- package/dist/index.js +261 -109
- package/dist/{installs-CXGV291R.js → installs-BrOMqREO.js} +7 -6
- package/dist/{installs-89zeUsVn.js → installs-z69au9Te.js} +7 -6
- package/dist/{links-Dg90NTyF.js → links-AVB88xxH.js} +1 -1
- package/dist/{links-7M-j83As.js → links-DpxpaKe1.js} +1 -1
- package/dist/llm-slug-generator.js +18 -19
- package/dist/{loader-BnzQyT31.js → loader-CS-5lMQa.js} +3694 -4531
- package/dist/{logging-DuK6YXuK.js → logging-B3KnAryz.js} +2 -2
- package/dist/{logging-CNq0UUgf.js → logging-DEPo2hji.js} +1 -1
- package/dist/{login-qr-CJ__cE3-.js → login-qr--28WL1TN.js} +11 -5
- package/dist/{login-qr-BVeOFfNW.js → login-qr-4o2aC2UE.js} +9 -4
- package/dist/{login-qr-BJChByHH.js → login-qr-BUdeu1Sl.js} +8 -2
- package/dist/{login-qr-KUOtNJaQ.js → login-qr-BsYM2E1y.js} +12 -6
- package/dist/{logs-cli-Cm7AiarR.js → logs-cli-DDMD5w5_.js} +38 -22
- package/dist/{logs-cli-BWmtAsjp.js → logs-cli-DvPoVKCN.js} +38 -22
- package/dist/{manager-C-jXr9ks.js → manager-CXo1uqmO.js} +102 -86
- package/dist/{manager-CMFBuvVd.js → manager-ChW0jk7T.js} +101 -85
- package/dist/{manager-D2Ndphg3.js → manager-DUOe7ud6.js} +100 -85
- package/dist/{manager-BsdlwsL5.js → manager-PoxUqdN_.js} +98 -82
- package/dist/{manifest-registry-D5SiA3xq.js → manifest-registry-CVsqjgX0.js} +40 -2
- package/dist/{manifest-registry-DyMRD3rY.js → manifest-registry-jeAPx6AW.js} +40 -2
- package/dist/{message-channel-CHRYQtAM.js → message-channel-CTtrEkmW.js} +1 -1
- package/dist/{message-channel-BlgPSDAh.js → message-channel-DWcu72r7.js} +1 -1
- package/dist/{model-auth-BqjMkNFs.js → model-auth-BvODRbV0.js} +362 -35
- package/dist/{model-selection-DbsbOAoh.js → model-selection-B53OvWCf.js} +353 -33
- package/dist/{model-selection-DlV6wnTr.js → model-selection-vC82fEiP.js} +331 -30
- package/dist/{models-cli-DIFBrK4W.js → models-cli-DqsKsOgd.js} +66 -55
- package/dist/{models-cli-0XhQQbMW.js → models-cli-NV0bnh8l.js} +66 -55
- package/dist/{node-cli-BMUfVCSq.js → node-cli-C7YleuBk.js} +54 -44
- package/dist/{node-cli-DY4lzhDA.js → node-cli-CxwoHnZ6.js} +54 -44
- package/dist/{node-service-DQ-tiSie.js → node-service-C7f_uvx9.js} +2 -2
- package/dist/{node-service-u8g85nD3.js → node-service-De_WkxJe.js} +2 -2
- package/dist/{nodes-cli-BX6oWnLC.js → nodes-cli-BxrMVI9V.js} +25 -23
- package/dist/{nodes-cli-CVHzcQo2.js → nodes-cli-Clb0ocwB.js} +24 -22
- package/dist/{nodes-screen-DGlNPbk4.js → nodes-screen-CVL9363A.js} +48 -6
- package/dist/{nodes-screen-lykd2cny.js → nodes-screen-DsHJIN2I.js} +47 -5
- package/dist/{note-Ci08TSbV.js → note-Duiadw1g.js} +1 -1
- package/dist/{note-DVO1KLaW.js → note-uC6iDp4y.js} +2 -2
- package/dist/{onboard-channels-DTkFFbzS.js → onboard-channels-C5Iaafwb.js} +10 -10
- package/dist/{onboard-channels-CtDnwaF5.js → onboard-channels-C5uL3i8d.js} +11 -11
- package/dist/{onboard-skills-BnAcpzfX.js → onboard-skills-BFxdI1Y1.js} +1143 -112
- package/dist/{onboard-skills-DuoDzEmI.js → onboard-skills-DUG8Y0se.js} +1142 -111
- package/dist/{onboarding-DvhiiHh0.js → onboarding-ClzElK4D.js} +56 -48
- package/dist/{openclaw-root-93W6UrUK.js → openclaw-root-BKsZvO6K.js} +6 -2
- package/dist/{openclaw-root-9ILYSmJ9.js → openclaw-root-CEnmuBUN.js} +6 -2
- package/dist/{pairing-cli-BKJHBxwT.js → pairing-cli-BWWFZF7Q.js} +16 -16
- package/dist/{pairing-cli-DJHjPBwu.js → pairing-cli-BrFLxnug.js} +16 -16
- package/dist/{pairing-labels-xImhiJax.js → pairing-labels-C8KULWNH.js} +1 -1
- package/dist/{pairing-labels-CHxlh3tT.js → pairing-labels-Dt2vXyI7.js} +1 -1
- package/dist/{pairing-store-CO6umWFP.js → pairing-store-Dz-ArTQS.js} +3 -3
- package/dist/{pairing-store-BpPUNzmB.js → pairing-store-gQdv7Ruh.js} +2 -2
- package/dist/{path-env-Nq83EHH9.js → path-env-BRKerjt1.js} +2 -2
- package/dist/{path-env-CXWUFfFv.js → path-env-OJAyUeWW.js} +1 -1
- package/dist/paths-BZK4Ct0I.js +81 -0
- package/dist/paths-DWYi0R_2.js +78 -0
- package/dist/{paths-Bkhd_qY8.js → paths-DdKf4lHp.js} +35 -5
- package/dist/paths-SFzVNGbc.js +78 -0
- package/dist/pi-auth-json-D7hGObyW.js +12 -0
- package/dist/pi-auth-json-DgvHjfJy.js +8 -0
- package/dist/pi-auth-json-la6lnAzY.js +79 -0
- package/dist/pi-auth-json-p3vsMR7W.js +79 -0
- package/dist/{pi-embedded-C1qKCgDT.js → pi-embedded-De6SeAPs.js} +9518 -9968
- package/dist/{pi-embedded-helpers-DtPn5RC8.js → pi-embedded-helpers-BrUBxrE2.js} +70 -10
- package/dist/{pi-embedded-helpers-DhEkdWB1.js → pi-embedded-helpers-D0mqOwwq.js} +821 -128
- package/dist/{pi-embedded-helpers-7AjuNiiJ.js → pi-embedded-helpers-DpJb0kUk.js} +69 -9
- package/dist/{pi-embedded-helpers-BTkXgwJ7.js → pi-embedded-helpers-ZI1UCSRM.js} +927 -136
- package/dist/{pi-tools.policy-gG96mWwA.js → pi-tools.policy-z5Wd_2WN.js} +4 -4
- package/dist/{plugin-auto-enable-D5ye7QnB.js → plugin-auto-enable-B8mX3rX3.js} +14 -5
- package/dist/{plugin-auto-enable-BROgMZcf.js → plugin-auto-enable-OO0eDINB.js} +14 -5
- package/dist/plugin-sdk/agents/apply-patch-update.d.ts +3 -1
- package/dist/plugin-sdk/agents/apply-patch.d.ts +11 -3
- package/dist/plugin-sdk/agents/auth-profiles/profiles.d.ts +5 -0
- package/dist/plugin-sdk/agents/auth-profiles.d.ts +1 -1
- package/dist/plugin-sdk/agents/bash-process-registry.d.ts +1 -0
- package/dist/plugin-sdk/agents/bash-tools.exec.d.ts +26 -0
- package/dist/plugin-sdk/agents/current-time.d.ts +17 -0
- package/dist/plugin-sdk/agents/huggingface-models.d.ts +17 -0
- package/dist/plugin-sdk/agents/models-config.providers.d.ts +10 -0
- package/dist/plugin-sdk/agents/openclaw-tools.d.ts +2 -0
- package/dist/plugin-sdk/agents/pi-auth-json.d.ts +14 -0
- package/dist/plugin-sdk/agents/pi-embedded-helpers/errors.d.ts +5 -1
- package/dist/plugin-sdk/agents/pi-embedded-helpers.d.ts +1 -1
- package/dist/plugin-sdk/agents/pi-embedded-runner/google.d.ts +1 -0
- package/dist/plugin-sdk/agents/pi-embedded-runner/run/images.d.ts +9 -4
- package/dist/plugin-sdk/agents/pi-embedded-runner/run/params.d.ts +2 -0
- package/dist/plugin-sdk/agents/pi-embedded-runner/run/payloads.d.ts +1 -0
- package/dist/plugin-sdk/agents/pi-embedded-runner/run/types.d.ts +2 -0
- package/dist/plugin-sdk/agents/pi-embedded-runner/types.d.ts +15 -0
- package/dist/plugin-sdk/agents/pi-embedded-subscribe.handlers.tools.d.ts +1 -1
- package/dist/plugin-sdk/agents/pi-embedded-subscribe.handlers.types.d.ts +2 -0
- package/dist/plugin-sdk/agents/pi-embedded-subscribe.types.d.ts +2 -0
- package/dist/plugin-sdk/agents/pi-tools.read.d.ts +8 -3
- package/dist/plugin-sdk/agents/sandbox/constants.d.ts +1 -1
- package/dist/plugin-sdk/agents/sandbox/docker.d.ts +14 -3
- package/dist/plugin-sdk/agents/sandbox/fs-bridge.d.ts +56 -0
- package/dist/plugin-sdk/agents/sandbox/types.d.ts +2 -0
- package/dist/plugin-sdk/agents/session-tool-result-guard-wrapper.d.ts +2 -0
- package/dist/plugin-sdk/agents/session-tool-result-guard.d.ts +4 -0
- package/dist/plugin-sdk/agents/subagent-registry.d.ts +3 -1
- package/dist/plugin-sdk/agents/tools/agent-step.d.ts +3 -0
- package/dist/plugin-sdk/agents/tools/browser-tool.schema.d.ts +2 -2
- package/dist/plugin-sdk/agents/tools/common.d.ts +4 -0
- package/dist/plugin-sdk/agents/tools/image-tool.d.ts +9 -1
- package/dist/plugin-sdk/agents/tools/web-search.d.ts +10 -1
- package/dist/plugin-sdk/agents/usage.d.ts +1 -0
- package/dist/plugin-sdk/auto-reply/reply/commands-status.d.ts +1 -0
- package/dist/plugin-sdk/auto-reply/reply/get-reply-directives.d.ts +1 -0
- package/dist/plugin-sdk/auto-reply/reply/memory-flush.d.ts +2 -2
- package/dist/plugin-sdk/auto-reply/reply/mentions.d.ts +1 -0
- package/dist/plugin-sdk/auto-reply/reply/model-selection.d.ts +3 -0
- package/dist/plugin-sdk/auto-reply/reply/reply-reference.d.ts +1 -1
- package/dist/plugin-sdk/auto-reply/reply/session-run-accounting.d.ts +11 -0
- package/dist/plugin-sdk/auto-reply/reply/session-usage.d.ts +8 -0
- package/dist/plugin-sdk/auto-reply/status.d.ts +2 -0
- package/dist/plugin-sdk/auto-reply/templating.d.ts +3 -0
- package/dist/plugin-sdk/auto-reply/thinking.d.ts +1 -1
- package/dist/plugin-sdk/auto-reply/types.d.ts +2 -0
- package/dist/plugin-sdk/browser/cdp.helpers.d.ts +2 -1
- package/dist/plugin-sdk/browser/client-actions-core.d.ts +1 -0
- package/dist/plugin-sdk/browser/control-auth.d.ts +13 -0
- package/dist/plugin-sdk/browser/pw-ai.d.ts +1 -1
- package/dist/plugin-sdk/browser/pw-session.d.ts +25 -0
- package/dist/plugin-sdk/browser/pw-tools-core.interactions.d.ts +2 -0
- package/dist/plugin-sdk/browser/routes/dispatcher.d.ts +1 -0
- package/dist/plugin-sdk/browser/routes/types.d.ts +5 -0
- package/dist/plugin-sdk/channels/plugins/onboarding/signal.d.ts +1 -0
- package/dist/plugin-sdk/channels/registry.d.ts +2 -2
- package/dist/plugin-sdk/cli/nodes-camera.d.ts +8 -2
- package/dist/plugin-sdk/cli/prompt.d.ts +1 -0
- package/dist/plugin-sdk/commands/agent/types.d.ts +2 -0
- package/dist/plugin-sdk/commands/onboard-helpers.d.ts +1 -0
- package/dist/plugin-sdk/commands/onboard-types.d.ts +9 -1
- package/dist/plugin-sdk/commands/signal-install.d.ts +20 -0
- package/dist/plugin-sdk/config/config.d.ts +1 -1
- package/dist/plugin-sdk/config/group-policy.d.ts +3 -0
- package/dist/plugin-sdk/config/merge-patch.d.ts +1 -0
- package/dist/plugin-sdk/config/sessions/paths.d.ts +14 -4
- package/dist/plugin-sdk/config/sessions/store.d.ts +8 -0
- package/dist/plugin-sdk/config/sessions/types.d.ts +8 -0
- package/dist/plugin-sdk/config/types.agents.d.ts +2 -0
- package/dist/plugin-sdk/config/types.channels.d.ts +2 -0
- package/dist/plugin-sdk/config/types.d.ts +1 -0
- package/dist/plugin-sdk/config/types.discord.d.ts +5 -0
- package/dist/plugin-sdk/config/types.gateway.d.ts +35 -0
- package/dist/plugin-sdk/config/types.hooks.d.ts +23 -1
- package/dist/plugin-sdk/config/types.irc.d.ts +96 -0
- package/dist/plugin-sdk/config/types.memory.d.ts +2 -0
- package/dist/plugin-sdk/config/types.openclaw.d.ts +6 -0
- package/dist/plugin-sdk/config/types.queue.d.ts +1 -0
- package/dist/plugin-sdk/config/types.slack.d.ts +2 -0
- package/dist/plugin-sdk/config/types.telegram.d.ts +2 -0
- package/dist/plugin-sdk/config/validation.d.ts +20 -0
- package/dist/plugin-sdk/config/zod-schema.agents.d.ts +1 -0
- package/dist/plugin-sdk/config/zod-schema.core.d.ts +2 -0
- package/dist/plugin-sdk/config/zod-schema.d.ts +193 -2
- package/dist/plugin-sdk/config/zod-schema.hooks.d.ts +3 -2
- package/dist/plugin-sdk/config/zod-schema.providers-core.d.ts +378 -0
- package/dist/plugin-sdk/config/zod-schema.providers.d.ts +176 -0
- package/dist/plugin-sdk/config/zod-schema.sensitive.d.ts +2 -0
- package/dist/plugin-sdk/config/zod-schema.session.d.ts +1 -0
- package/dist/plugin-sdk/cron/service/jobs.d.ts +8 -0
- package/dist/plugin-sdk/cron/service/state.d.ts +1 -0
- package/dist/plugin-sdk/cron/types.d.ts +2 -0
- package/dist/plugin-sdk/discord/monitor/allow-list.d.ts +15 -0
- package/dist/plugin-sdk/discord/send.types.d.ts +5 -0
- package/dist/plugin-sdk/gateway/auth-rate-limit.d.ts +59 -0
- package/dist/plugin-sdk/gateway/auth.d.ts +47 -0
- package/dist/plugin-sdk/gateway/net.d.ts +5 -0
- package/dist/plugin-sdk/gateway/protocol/index.d.ts +7 -7
- package/dist/plugin-sdk/gateway/protocol/schema/agent.d.ts +7 -1
- package/dist/plugin-sdk/gateway/protocol/schema/channels.d.ts +21 -0
- package/dist/plugin-sdk/gateway/protocol/schema/types.d.ts +3 -1
- package/dist/plugin-sdk/gateway/session-utils.fs.d.ts +3 -1
- package/dist/plugin-sdk/gateway/session-utils.types.d.ts +1 -0
- package/dist/plugin-sdk/imessage/send.d.ts +12 -0
- package/dist/plugin-sdk/index.js +2147 -900
- package/dist/plugin-sdk/infra/binaries.d.ts +3 -0
- package/dist/plugin-sdk/infra/brew.d.ts +8 -0
- package/dist/plugin-sdk/infra/heartbeat-active-hours.d.ts +5 -0
- package/dist/plugin-sdk/infra/heartbeat-runner.d.ts +1 -0
- package/dist/plugin-sdk/infra/heartbeat-wake.d.ts +8 -1
- package/dist/plugin-sdk/infra/net/fetch-guard.d.ts +1 -0
- package/dist/plugin-sdk/infra/net/ssrf.d.ts +1 -0
- package/dist/plugin-sdk/infra/outbound/message.d.ts +2 -0
- package/dist/plugin-sdk/infra/outbound/outbound-send-service.d.ts +2 -0
- package/dist/plugin-sdk/infra/session-cost-usage.d.ts +3 -0
- package/dist/plugin-sdk/infra/tailscale.d.ts +34 -0
- package/dist/plugin-sdk/infra/tmp-openclaw-dir.d.ts +10 -0
- package/dist/plugin-sdk/logging/console.d.ts +4 -0
- package/dist/plugin-sdk/logging/logger.d.ts +1 -1
- package/dist/plugin-sdk/logging/state.d.ts +1 -0
- package/dist/plugin-sdk/logging.d.ts +2 -2
- package/dist/plugin-sdk/markdown/ir.d.ts +1 -1
- package/dist/plugin-sdk/markdown/whatsapp.d.ts +14 -0
- package/dist/plugin-sdk/media/input-files.d.ts +5 -0
- package/dist/plugin-sdk/media/store.d.ts +10 -0
- package/dist/plugin-sdk/media-understanding/audio-preflight.d.ts +16 -0
- package/dist/plugin-sdk/media-understanding/types.d.ts +1 -0
- package/dist/plugin-sdk/memory/backend-config.d.ts +2 -1
- package/dist/plugin-sdk/memory/embedding-chunk-limits.d.ts +3 -0
- package/dist/plugin-sdk/memory/embedding-input-limits.d.ts +2 -0
- package/dist/plugin-sdk/memory/embedding-model-limits.d.ts +2 -0
- package/dist/plugin-sdk/memory/embeddings.d.ts +1 -0
- package/dist/plugin-sdk/memory/internal.d.ts +11 -0
- package/dist/plugin-sdk/memory/manager.d.ts +0 -6
- package/dist/plugin-sdk/memory/qmd-manager.d.ts +2 -0
- package/dist/plugin-sdk/memory/qmd-query-parser.d.ts +8 -0
- package/dist/plugin-sdk/memory/session-files.d.ts +2 -0
- package/dist/plugin-sdk/process/command-queue.d.ts +16 -0
- package/dist/plugin-sdk/providers/github-copilot-token.d.ts +3 -0
- package/dist/plugin-sdk/routing/resolve-route.d.ts +3 -1
- package/dist/plugin-sdk/security/external-content.d.ts +1 -1
- package/dist/plugin-sdk/security/secret-equal.d.ts +1 -0
- package/dist/plugin-sdk/sessions/input-provenance.d.ts +16 -0
- package/dist/plugin-sdk/signal/monitor/event-handler.types.d.ts +8 -0
- package/dist/plugin-sdk/signal/monitor/mentions.d.ts +2 -0
- package/dist/plugin-sdk/slack/monitor/commands.d.ts +5 -0
- package/dist/plugin-sdk/slack/monitor/media.d.ts +21 -0
- package/dist/plugin-sdk/slack/types.d.ts +1 -0
- package/dist/plugin-sdk/telegram/bot-message-context.d.ts +2 -1
- package/dist/plugin-sdk/telegram/fetch.d.ts +1 -0
- package/dist/plugin-sdk/telegram/monitor.d.ts +1 -0
- package/dist/plugin-sdk/telegram/send.d.ts +3 -0
- package/dist/plugin-sdk/tts/tts.d.ts +2 -2
- package/dist/plugin-sdk/utils/fetch-timeout.d.ts +2 -0
- package/dist/plugin-sdk/web/media.d.ts +12 -2
- package/dist/{plugins-CQw3z3Nw.js → plugins-CTjLu-z-.js} +4 -4
- package/dist/{plugins-B7F0Ly9G.js → plugins-CxrdL_IZ.js} +3 -3
- package/dist/{plugins-cli-CJ74eHvr.js → plugins-cli-CbX97Kvt.js} +259 -49
- package/dist/{plugins-cli-ubDwUAzK.js → plugins-cli-Dn9OeO53.js} +257 -47
- package/dist/{ports-kYsTYQdA.js → ports-C8YYHVlc.js} +2 -2
- package/dist/{program-1bQ15ivo.js → program-D-mNC0It.js} +86 -83
- package/dist/{progress-Da1ehW-x.js → progress-COHv-uNT.js} +1 -1
- package/dist/{progress-COzt9PNY.js → progress-DZb6yPcJ.js} +1 -1
- package/dist/{prompt-style-Dc0C5HC9.js → prompt-style-Cf1r1L6k.js} +1 -1
- package/dist/{prompt-style-DjZDxcFg.js → prompt-style-lSlXMhsd.js} +1 -1
- package/dist/{pw-ai-CQ4-gUNR.js → pw-ai-6GzTgK5g.js} +205 -32
- package/dist/{pw-ai-1NN0FrJb.js → pw-ai-C8YhJRaI.js} +207 -32
- package/dist/{pw-ai-qEMUq5Mt.js → pw-ai-CKGenizV.js} +203 -29
- package/dist/{pw-ai-IOqEXO1O.js → pw-ai-D7devT89.js} +206 -32
- package/dist/{qmd-manager-CEwp3el1.js → qmd-manager-CQzWovq-.js} +71 -90
- package/dist/{qmd-manager-D6N3qvQ5.js → qmd-manager-Cs8RJVQp.js} +73 -90
- package/dist/{qmd-manager-C48QzrRe.js → qmd-manager-DdgrQ2kc.js} +71 -88
- package/dist/{qmd-manager-DaUqCKB_.js → qmd-manager-dyIoOvKl.js} +73 -90
- package/dist/{register.subclis-Cm-VJ5nP.js → register.subclis-ifHtmF3e.js} +29 -29
- package/dist/{reply-CBs4e9Rm.js → reply-VIHqsQ-k.js} +7906 -8743
- package/dist/{routes-9ygR0GOk.js → routes-CaCvio4Q.js} +36 -15
- package/dist/{routes-BrWrBk2R.js → routes-Cpfxk96k.js} +36 -14
- package/dist/{rpc-Cjuz2Gv1.js → rpc-BhB01Bhj.js} +3 -3
- package/dist/{rpc-DhkLVY5H.js → rpc-C5WsS_Ne.js} +3 -3
- package/dist/{run-main-BlZ5l-X9.js → run-main-DVy6KJTe.js} +88 -85
- package/dist/runner-B7CKBC80.js +1800 -0
- package/dist/runner-BEy5ZGFv.js +1901 -0
- package/dist/runner-Bv0BmJPF.js +1800 -0
- package/dist/runner-ChqVEgPx.js +1901 -0
- package/dist/{sandbox-qt49csTr.js → sandbox-BAChxjC5.js} +627 -157
- package/dist/{sandbox-CPZiaKcS.js → sandbox-DNHDwHw8.js} +628 -158
- package/dist/{sandbox-cli-C6_iNuqO.js → sandbox-cli-9oq67QEg.js} +22 -22
- package/dist/{sandbox-cli-C_wK-KAE.js → sandbox-cli-BiNq9yUe.js} +22 -22
- package/dist/{security-cli-CTTD1vms.js → security-cli-CRg03hvq.js} +28 -28
- package/dist/{security-cli-DRpGF2Yc.js → security-cli-LmBBHnmh.js} +28 -28
- package/dist/{server-context-lyNcqJYD.js → server-context-FwqBRH3K.js} +10 -10
- package/dist/{server-context-39mkstUs.js → server-context-RY7lRaxl.js} +9 -9
- package/dist/{server-node-events-V_G9BRRw.js → server-node-events-BbHOZX3O.js} +48 -43
- package/dist/{server-node-events-o9G18PaE.js → server-node-events-CngNLVL-.js} +50 -45
- package/dist/{service-DOlJdIqe.js → service-BnqdBTAK.js} +8 -4
- package/dist/{service-DDPRbf8a.js → service-DZN7KRok.js} +8 -4
- package/dist/{service-audit-VDRrWefh.js → service-audit-0Eil3ISN.js} +4 -4
- package/dist/{service-audit-CVy00Ze_.js → service-audit-B8KIOe8A.js} +4 -4
- package/dist/{session-cost-usage-CcCEQNuc.js → session-cost-usage-B-tyjp76.js} +14 -14
- package/dist/{session-cost-usage-PvyVZz-g.js → session-cost-usage-BYUb7fov.js} +14 -14
- package/dist/{shared-BnpC3wMU.js → shared-BCdNboU1.js} +3 -3
- package/dist/{shared-CagUDdmp.js → shared-CsAwU6-q.js} +3 -3
- package/dist/{shared-BDk_zC9p.js → shared-Csn6DLBA.js} +5 -5
- package/dist/{shared-C92wo-6f.js → shared-DEanAgja.js} +4 -4
- package/dist/{skill-scanner-C_fQzVDu.js → skill-scanner-BrGkh5K7.js} +1 -1
- package/dist/{skill-scanner-DrVEHfC6.js → skill-scanner-CucvxYhu.js} +1 -1
- package/dist/{skills-Ccsv3IQq.js → skills-CE7by2IF.js} +151 -8
- package/dist/{skills-_eKGrw9z.js → skills-Dz15dAM4.js} +152 -9
- package/dist/{skills-cli-DqvLjooh.js → skills-cli-B5b75pDK.js} +13 -13
- package/dist/{skills-cli-DUncybht.js → skills-cli-CbCDrYwp.js} +13 -13
- package/dist/{skills-status-Cp2ZFhIx.js → skills-status-B99Us6yS.js} +2 -2
- package/dist/{skills-status-Ck0CCFZG.js → skills-status-ChM7JE47.js} +3 -3
- package/dist/{sqlite-DODNHWJb.js → sqlite-2UsPaJz5.js} +97 -2
- package/dist/{sqlite-cSdsHVEw.js → sqlite-CASnHrgX.js} +97 -1
- package/dist/{sqlite-Bwo2rASR.js → sqlite-CVWiMkGu.js} +97 -1
- package/dist/{sqlite-CpqIbY4-.js → sqlite-CcIWkGaM.js} +97 -1
- package/dist/{status-Bmx9_1C7.js → status-CKuX1-zb.js} +3 -3
- package/dist/{status-CBGgwlTW.js → status-Cm4q6o-I.js} +57 -49
- package/dist/{status-DkJgtvSz.js → status-DD2iqGc9.js} +4 -4
- package/dist/{subsystem-DPnkvS73.js → subsystem-DHfJG4gk.js} +73 -20
- package/dist/{system-cli-9fQ1uLiz.js → system-cli-BVJDR474.js} +87 -15
- package/dist/{system-cli-Gq8OWHFg.js → system-cli-C3Y_9VpI.js} +88 -16
- package/dist/{systemd-Pa7LURHB.js → systemd-DxddcFsa.js} +3 -3
- package/dist/{systemd-hints-zi4ohCOY.js → systemd-hints-BVLopJ9O.js} +1 -1
- package/dist/{systemd-linger-CDo2UbHM.js → systemd-linger-BThjV1Sr.js} +2 -2
- package/dist/{systemd-linger-6_naJcJp.js → systemd-linger-D3Va1Cv7.js} +2 -2
- package/dist/{systemd-BEWwfwn0.js → systemd-s3S2HVog.js} +3 -3
- package/dist/{table-Bb9gAVIp.js → table-BIk8Aan_.js} +2 -2
- package/dist/{table-cCoGqLsk.js → table-Bvka_vkc.js} +1 -1
- package/dist/{tool-display-DUVhO36P.js → tool-display-DbdMQFZx.js} +2 -2
- package/dist/{tool-display-DNOVCI6J.js → tool-display-kpW5Hg2z.js} +2 -2
- package/dist/{tui-DDVqLwqT.js → tui-B40Z2jMa.js} +120 -14
- package/dist/{tui-cli-CurbazQf.js → tui-cli-Bwa6K7xR.js} +28 -28
- package/dist/{tui-cli-BeN2K38I.js → tui-cli-DD6g7uZb.js} +27 -27
- package/dist/{tui-B9zLJxf6.js → tui-lFMZUnx6.js} +121 -13
- package/dist/{update-Ct9sqJC_.js → update-Bos8PPCG.js} +3 -3
- package/dist/{update--i077azM.js → update-Cg8MtrEr.js} +3 -3
- package/dist/{update-cli-CT5W0kpw.js → update-cli-CC-wTeje.js} +92 -73
- package/dist/{update-cli-C87lNK1S.js → update-cli-CULnXFL_.js} +91 -72
- package/dist/{update-runner-BIttRDyV.js → update-runner-BaLsla0c.js} +11 -11
- package/dist/{update-runner-xbeVkAD9.js → update-runner-Dbsdl5AU.js} +10 -10
- package/dist/{utils-Dk86IbEs.js → utils-BLJAc3ZV.js} +1 -1
- package/dist/{utils-BTaR--Ln.js → utils-BtIMES3N.js} +1 -1
- package/dist/{webhooks-cli-Db3zyJaw.js → webhooks-cli-ClHLUu_j.js} +21 -13
- package/dist/{webhooks-cli-DUUa8gVY.js → webhooks-cli-DVXr2uyN.js} +21 -13
- package/dist/{widearea-dns-BgYasW6m.js → widearea-dns-C4RnIR9O.js} +3 -3
- package/dist/{widearea-dns-CMIG6-74.js → widearea-dns-Ypwgjpsr.js} +3 -3
- package/dist/{ws-C0k_dhCP.js → ws-BcJt4pcg.js} +24 -2
- package/dist/{ws-DtDKpbLR.js → ws-MC-rTJLe.js} +24 -2
- package/dist/{ws-log-cMNgAyLy.js → ws-log-WrJ4QYu7.js} +1 -1
- package/dist/{ws-log-C6vm_XMA.js → ws-log-lip4ETlm.js} +2 -2
- package/dist/{wsl-rfIr_Sde.js → wsl-BvTIzy-8.js} +5 -3
- package/docs/assets/install-script.svg +1 -0
- package/docs/automation/hooks.md +1 -38
- package/docs/automation/webhook.md +52 -2
- package/docs/channels/discord.md +389 -381
- package/docs/channels/grammy.md +1 -1
- package/docs/channels/imessage.md +229 -218
- package/docs/channels/index.md +1 -0
- package/docs/channels/irc.md +234 -0
- package/docs/channels/msteams.md +2 -0
- package/docs/channels/pairing.md +1 -1
- package/docs/channels/slack.md +295 -415
- package/docs/channels/telegram.md +397 -460
- package/docs/channels/whatsapp.md +338 -310
- package/docs/ci.md +0 -12
- package/docs/cli/hooks.md +1 -14
- package/docs/cli/index.md +6 -1
- package/docs/cli/logs.md +4 -0
- package/docs/cli/onboard.md +33 -0
- package/docs/cli/plugins.md +20 -1
- package/docs/cli/security.md +2 -0
- package/docs/concepts/architecture.md +0 -16
- package/docs/concepts/memory.md +7 -4
- package/docs/concepts/model-providers.md +27 -0
- package/docs/concepts/session-tool.md +1 -0
- package/docs/concepts/system-prompt.md +13 -0
- package/docs/docs.json +18 -12
- package/docs/experiments/plans/browser-evaluate-cdp-refactor.md +229 -0
- package/docs/gateway/configuration-examples.md +9 -2
- package/docs/gateway/configuration-reference.md +2345 -0
- package/docs/gateway/configuration.md +338 -3297
- package/docs/gateway/index.md +162 -238
- package/docs/gateway/openai-http-api.md +1 -0
- package/docs/gateway/openresponses-http-api.md +16 -0
- package/docs/gateway/remote-gateway-readme.md +0 -16
- package/docs/gateway/security/index.md +4 -16
- package/docs/gateway/tools-invoke-http-api.md +26 -1
- package/docs/help/faq.md +9 -0
- package/docs/help/testing.md +11 -0
- package/docs/install/docker.md +18 -0
- package/docs/install/hetzner.md +21 -0
- package/docs/install/installer.md +20 -0
- package/docs/nodes/audio.md +19 -0
- package/docs/platforms/mac/release.md +7 -7
- package/docs/providers/glm.md +3 -3
- package/docs/providers/huggingface.md +209 -0
- package/docs/providers/index.md +3 -0
- package/docs/providers/litellm.md +153 -0
- package/docs/providers/together.md +2 -2
- package/docs/providers/vllm.md +92 -0
- package/docs/providers/zai.md +2 -2
- package/docs/reference/credits.md +4 -28
- package/docs/reference/test.md +2 -1
- package/docs/reference/token-use.md +1 -1
- package/docs/reference/transcript-hygiene.md +18 -0
- package/docs/start/getting-started.md +5 -0
- package/docs/start/onboarding-overview.md +51 -0
- package/docs/start/onboarding.md +1 -0
- package/docs/start/openclaw.md +0 -16
- package/docs/start/wizard-cli-automation.md +17 -0
- package/docs/start/wizard-cli-reference.md +12 -0
- package/docs/start/wizard.md +3 -1
- package/docs/tools/browser.md +6 -0
- package/docs/zh-CN/automation/hooks.md +1 -38
- package/docs/zh-CN/cli/hooks.md +1 -14
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/bluebubbles/src/monitor.test.ts +40 -28
- package/extensions/bluebubbles/src/monitor.ts +0 -4
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +10 -10
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +2 -5
- package/extensions/feishu/src/bot.checkBotMentioned.test.ts +64 -0
- package/extensions/feishu/src/bot.test.ts +265 -0
- package/extensions/feishu/src/bot.ts +73 -18
- package/extensions/feishu/src/channel.test.ts +48 -0
- package/extensions/feishu/src/channel.ts +1 -3
- package/extensions/feishu/src/config-schema.ts +6 -0
- package/extensions/feishu/src/docx.ts +14 -4
- package/extensions/feishu/src/media.test.ts +151 -0
- package/extensions/feishu/src/media.ts +27 -13
- package/extensions/feishu/src/reply-dispatcher.test.ts +116 -0
- package/extensions/feishu/src/reply-dispatcher.ts +124 -67
- package/extensions/feishu/src/streaming-card.ts +223 -0
- package/extensions/feishu/src/targets.test.ts +16 -0
- package/extensions/feishu/src/targets.ts +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/oauth.test.ts +4 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/googlechat/src/channel.ts +3 -20
- package/extensions/googlechat/src/resolve-target.test.ts +138 -0
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/index.ts +17 -0
- package/extensions/irc/openclaw.plugin.json +9 -0
- package/extensions/irc/package.json +14 -0
- package/extensions/irc/src/accounts.ts +268 -0
- package/extensions/irc/src/channel.ts +367 -0
- package/extensions/irc/src/client.test.ts +43 -0
- package/extensions/irc/src/client.ts +439 -0
- package/extensions/irc/src/config-schema.test.ts +27 -0
- package/extensions/irc/src/config-schema.ts +97 -0
- package/extensions/irc/src/control-chars.ts +22 -0
- package/extensions/irc/src/inbound.ts +334 -0
- package/extensions/irc/src/monitor.test.ts +43 -0
- package/extensions/irc/src/monitor.ts +158 -0
- package/extensions/irc/src/normalize.test.ts +46 -0
- package/extensions/irc/src/normalize.ts +117 -0
- package/extensions/irc/src/onboarding.test.ts +118 -0
- package/extensions/irc/src/onboarding.ts +479 -0
- package/extensions/irc/src/policy.test.ts +132 -0
- package/extensions/irc/src/policy.ts +157 -0
- package/extensions/irc/src/probe.ts +64 -0
- package/extensions/irc/src/protocol.test.ts +44 -0
- package/extensions/irc/src/protocol.ts +169 -0
- package/extensions/irc/src/runtime.ts +14 -0
- package/extensions/irc/src/send.ts +99 -0
- package/extensions/irc/src/types.ts +94 -0
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +6 -0
- package/extensions/matrix/node_modules/.bin/markdown-it +2 -2
- package/extensions/matrix/node_modules/.bin/markdown-it.CMD +2 -2
- package/extensions/matrix/node_modules/.bin/markdown-it.ps1 +2 -2
- package/extensions/matrix/package.json +2 -2
- package/extensions/matrix/src/matrix/monitor/media.ts +4 -2
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/index.ts +6 -2
- package/extensions/memory-lancedb/node_modules/.bin/openai +2 -2
- package/extensions/memory-lancedb/node_modules/.bin/openai.CMD +2 -2
- package/extensions/memory-lancedb/node_modules/.bin/openai.ps1 +2 -2
- package/extensions/memory-lancedb/package.json +2 -2
- package/extensions/minimax-portal-auth/index.ts +7 -5
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +6 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/msteams/src/media-helpers.test.ts +9 -0
- package/extensions/msteams/src/media-helpers.ts +15 -1
- package/extensions/msteams/src/mentions.test.ts +235 -0
- package/extensions/msteams/src/mentions.ts +114 -0
- package/extensions/msteams/src/messenger.test.ts +81 -1
- package/extensions/msteams/src/messenger.ts +11 -2
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +6 -0
- package/extensions/nostr/package.json +2 -2
- package/extensions/open-prose/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/telegram/src/channel.ts +1 -0
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +6 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/twitch/src/onboarding.test.ts +5 -0
- package/extensions/twitch/src/outbound.test.ts +17 -6
- package/extensions/twitch/src/outbound.ts +12 -10
- package/extensions/voice-call/CHANGELOG.md +6 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/voice-call/src/media-stream.ts +7 -1
- package/extensions/voice-call/src/providers/twilio.test.ts +5 -3
- package/extensions/voice-call/src/providers/twilio.ts +12 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/whatsapp/src/channel.ts +6 -16
- package/extensions/whatsapp/src/resolve-target.test.ts +154 -0
- package/extensions/zalo/CHANGELOG.md +6 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +6 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +26 -22
- package/dist/auth-BcNHFK-i.js +0 -184
- package/dist/auth-DkjJ3pm-.js +0 -184
- package/dist/boolean-M-esQJt6.js +0 -30
- package/dist/bundled/soul-evil/HOOK.md +0 -71
- package/dist/bundled/soul-evil/handler.js +0 -194
- package/dist/cli-B631__JU.js +0 -89
- package/dist/cli-DVhCVZZ6.js +0 -86
- package/dist/config-CI7EpvlP.js +0 -15
- package/dist/constants-DuoCkWRh.js +0 -65
- package/dist/control-ui/assets/index-CnB9IO4a.js.map +0 -1
- package/dist/control-ui/assets/index-DWhx-9JL.css +0 -1
- package/dist/date-time-c6HTX6IW.js +0 -187
- package/dist/frontmatter-xwTm0734.js +0 -105
- package/dist/parse-DqAvJRIf.js +0 -23
- package/dist/parse-duration-De_tAQSe.js +0 -24
- package/dist/parse-timeout-DV8NQQWk.js +0 -16
- package/dist/paths-IivnSNkP.js +0 -51
- package/dist/paths-MnZaxqPw.js +0 -48
- package/dist/paths-uoGO2aiO.js +0 -48
- package/dist/pi-model-discovery-DzFOAbQt.js +0 -20
- package/dist/plugin-sdk/tui/tui-formatters.d.ts +0 -31
- package/dist/session-key-nXYQSv-a.js +0 -167
- package/dist/tailscale-DU6DgqVy.js +0 -225
- package/dist/tailscale-DzJUWmKf.js +0 -252
- package/dist/utils-dp_OM900.js +0 -476
- package/docs/hooks/soul-evil.md +0 -69
- package/docs/zh-CN/hooks/soul-evil.md +0 -72
- package/skills/local-places/SERVER_README.md +0 -101
- package/skills/local-places/SKILL.md +0 -102
- package/skills/local-places/pyproject.toml +0 -21
- package/skills/local-places/src/local_places/__init__.py +0 -2
- package/skills/local-places/src/local_places/google_places.py +0 -314
- package/skills/local-places/src/local_places/main.py +0 -65
- package/skills/local-places/src/local_places/schemas.py +0 -107
- /package/dist/{archive-CXhvR9nU.js → archive-aSMUcOc6.js} +0 -0
- /package/dist/{archive-D0z3LZDK.js → archive-beaSfAzA.js} +0 -0
- /package/dist/{brew-BIrWdDps.js → brew-DlQQMJ3n.js} +0 -0
- /package/dist/{brew-B7YK4ZoL.js → brew-ROHf0-Xp.js} +0 -0
- /package/dist/{cli-utils-PlLcDZlM.js → cli-utils-CRhVAaLV.js} +0 -0
- /package/dist/{cli-utils-R-ECs5cY.js → cli-utils-CodyYLHe.js} +0 -0
- /package/dist/{command-format-BUxhT1xL.js → command-format-qUVxzqYm.js} +0 -0
- /package/dist/{constants-CNTiY-ZN.js → constants-BvQ6S8j5.js} +0 -0
- /package/dist/{errors-D3tYRJWG.js → errors-B91HIDPD.js} +0 -0
- /package/dist/{errors-B0eT3jVv.js → errors-Bv81hF2P.js} +0 -0
- /package/dist/{errors-x4NYs-1P.js → errors-Cojm0Kl7.js} +0 -0
- /package/dist/{format-CaxeRcue.js → format-CL8VOhxX.js} +0 -0
- /package/dist/{format-DLOJPZmo.js → format-DcfK-dwd.js} +0 -0
- /package/dist/{format-duration-CEmFWLyX.js → format-duration--hQihAvf.js} +0 -0
- /package/dist/{format-duration-DCXJx2ba.js → format-duration-84n6_DgO.js} +0 -0
- /package/dist/{format-relative-79_Y1n2Y.js → format-relative-Cywx6ldk.js} +0 -0
- /package/dist/{format-relative-Db7eqEu8.js → format-relative-cegC_FF5.js} +0 -0
- /package/dist/{helpers-CQI-5xS9.js → helpers-8O7IVGO-.js} +0 -0
- /package/dist/{helpers-DdwqKAAS.js → helpers-ByYj2Aq5.js} +0 -0
- /package/dist/{helpers-CRzoyyXS.js → helpers-CUVSCDJV.js} +0 -0
- /package/dist/{helpers-C89IG08W.js → helpers-HyeZXsnu.js} +0 -0
- /package/dist/{is-main-qJ675wPV.js → is-main-B9A8S9YC.js} +0 -0
- /package/dist/{is-main-WWuz28Ip.js → is-main-BWoXGz7p.js} +0 -0
- /package/dist/{logging-BzvBIA3Y.js → logging-D-Jq2wIo.js} +0 -0
- /package/dist/{logging-CfEk_PnX.js → logging-fywhKCmE.js} +0 -0
- /package/dist/{parse-Cjiudy6x.js → parse-Bw0oH-rT.js} +0 -0
- /package/dist/{parse-log-line-CUrpqe1w.js → parse-log-line-BuRiE-Ij.js} +0 -0
- /package/dist/{parse-log-line-D2UGw0wR.js → parse-log-line-CfVgwy6x.js} +0 -0
- /package/dist/{parse-timeout-DFSPLxpY.js → parse-timeout-D1XX_zN_.js} +0 -0
- /package/dist/{pi-model-discovery-CV2V1HHz.js → pi-model-discovery-DqgqUyAv.js} +0 -0
- /package/dist/{pi-model-discovery-DzEIEgHL.js → pi-model-discovery-EwKVHlZB.js} +0 -0
- /package/dist/{prompts--d-6l5Ln.js → prompts-Bg96reub.js} +0 -0
- /package/dist/{prompts-CXLLIBwP.js → prompts-Dszjy1n_.js} +0 -0
- /package/dist/{redact-BRmQPYDR.js → redact-BIMJ3ntQ.js} +0 -0
- /package/dist/{redact-BHmk44DI.js → redact-BRsnXqwD.js} +0 -0
- /package/dist/{redact-DAKeu7PA.js → redact-UvkXqguc.js} +0 -0
- /package/dist/{status-Cv36yYdi.js → status-C_dMhoE0.js} +0 -0
- /package/dist/{status-Drziap9H.js → status-DCkF_L3U.js} +0 -0
- /package/dist/{systemd-hints-CH4pbCFD.js → systemd-hints-CXNtLw9Q.js} +0 -0
- /package/dist/{tailnet-CL5GtL7t.js → tailnet-DATIFSsY.js} +0 -0
- /package/dist/{tailnet-DGRSvYuQ.js → tailnet-uoFvUSsw.js} +0 -0
- /package/dist/{transcript-events-BlIONGVn.js → transcript-events-BHS7QoRl.js} +0 -0
- /package/dist/{transcript-events-C1hdue6u.js → transcript-events-Bp7fGnwv.js} +0 -0
- /package/dist/{transcript-events-CZ8CG4ht.js → transcript-events-Ch7wLX-j.js} +0 -0
- /package/dist/{usage-format-6Uar63S0.js → usage-format-Bhl_WCWP.js} +0 -0
- /package/dist/{usage-format-hd37en6b.js → usage-format-CpORtVCG.js} +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/claw +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/claw.CMD +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/claw.ps1 +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/moltbot +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/moltbot.CMD +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/moltbot.ps1 +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/pigbot +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/pigbot.CMD +0 -0
- /package/extensions/{feishu → irc}/node_modules/.bin/pigbot.ps1 +0 -0
|
@@ -1,94 +1,97 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
3
|
-
import { t as formatCliCommand } from "./command-format-
|
|
1
|
+
import { $ as CHANNEL_IDS, A as theme, G as resolveGatewayPort, I as CONFIG_PATH, M as getLogger, N as getResolvedLoggerSettings, O as colorize, R as STATE_DIR, V as resolveConfigPath, W as resolveGatewayLockDir, Z as resolveStateDir, j as getChildLogger, k as isRich, l as setConsoleSubsystemFilter, lt as getActivePluginRegistry, n as isTruthyEnvValue, o as createSubsystemLogger, p as defaultRuntime, pt as expandHomePrefix, r as logAcceptedEnvOption, s as runtimeForLogger, tt as DEFAULT_CHAT_CHANNEL, u as setConsoleTimestampPrefix, w as setVerbose, z as isNixMode } from "./entry.js";
|
|
2
|
+
import { At as DEFAULT_MODEL, D as getModelRefStatus, F as resolveDefaultModelForAgent, I as resolveHooksGmailModel, M as resolveAllowedModelRef, O as isCliProvider, P as resolveConfiguredModelRef, R as resolveThinkingDefault, jt as DEFAULT_PROVIDER, kt as DEFAULT_CONTEXT_TOKENS, mt as normalizeSecretInput } from "./auth-profiles-Cp9MtUdM.js";
|
|
3
|
+
import { t as formatCliCommand } from "./command-format-Bxe0mWee.js";
|
|
4
4
|
import { _ as isCronRunSessionKey, d as resolveAgentIdFromSessionKey, i as buildAgentMainSessionKey, l as normalizeAgentId, m as toAgentRequestSessionKey, n as DEFAULT_AGENT_ID, t as DEFAULT_ACCOUNT_ID, u as normalizeMainKey, v as isSubagentSessionKey, y as parseAgentSessionKey } from "./session-key-DVvxnFKg.js";
|
|
5
|
-
import { E as truncateUtf16Safe, S as shortenHomePath, n as clamp, s as ensureDir, t as CONFIG_DIR, u as isPlainObject, y as resolveUserPath } from "./utils-
|
|
6
|
-
import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-
|
|
7
|
-
import { t as resolveOpenClawPackageRoot } from "./openclaw-root-
|
|
8
|
-
import { T as resolveWorkspaceTemplateDir, _ as DEFAULT_MEMORY_FILENAME, b as DEFAULT_USER_FILENAME, c as resolveDefaultAgentId, d as DEFAULT_AGENTS_FILENAME, g as DEFAULT_MEMORY_ALT_FILENAME, h as DEFAULT_IDENTITY_FILENAME, i as resolveAgentModelFallbacksOverride, l as resolveSessionAgentId, m as DEFAULT_HEARTBEAT_FILENAME, n as resolveAgentConfig, p as DEFAULT_BOOTSTRAP_FILENAME, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, v as DEFAULT_SOUL_FILENAME, w as resolveDefaultAgentWorkspaceDir, x as ensureAgentWorkspace, y as DEFAULT_TOOLS_FILENAME } from "./agent-scope-
|
|
9
|
-
import "./github-copilot-token-
|
|
10
|
-
import "./pi-model-discovery-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { n as movePathToTrash } from "./server-context-
|
|
14
|
-
import { n as pickPrimaryTailnetIPv6, t as pickPrimaryTailnetIPv4 } from "./tailnet-
|
|
15
|
-
import {
|
|
16
|
-
import { c as ensurePortAvailable, d as formatPortDiagnostics, l as inspectPortUsage } from "./chrome
|
|
17
|
-
import { a as
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { $ as validateNodePairRequestParams, A as validateCronStatusParams,
|
|
23
|
-
import { n as callGateway, o as loadGatewayTlsRuntime$1 } from "./call-
|
|
24
|
-
import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, h as GATEWAY_CLIENT_NAMES, i as isGatewayMessageChannel, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-
|
|
25
|
-
import { t as formatDocsLink } from "./links-
|
|
26
|
-
import { r as buildChannelUiCatalog, t as applyPluginAutoEnable } from "./plugin-auto-enable-
|
|
27
|
-
import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-
|
|
28
|
-
import "./logging-
|
|
29
|
-
import "./accounts-
|
|
30
|
-
import { $ as
|
|
31
|
-
import { n as withProgress } from "./progress-
|
|
32
|
-
import "./prompt-style-
|
|
33
|
-
import "./note-
|
|
34
|
-
import { t as WizardCancelledError } from "./prompts-
|
|
35
|
-
import { t as resolveChannelDefaultAccountId } from "./helpers-
|
|
36
|
-
import "./onboard-channels-
|
|
37
|
-
import "./archive-
|
|
38
|
-
import "./skill-scanner-
|
|
39
|
-
import "./installs-
|
|
40
|
-
import "./
|
|
41
|
-
import
|
|
42
|
-
import "./
|
|
43
|
-
import
|
|
44
|
-
import
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import "./
|
|
48
|
-
import {
|
|
49
|
-
import
|
|
50
|
-
import "./
|
|
51
|
-
import {
|
|
52
|
-
import "./
|
|
53
|
-
import "./
|
|
54
|
-
import
|
|
55
|
-
import
|
|
56
|
-
import {
|
|
57
|
-
import { n as
|
|
58
|
-
import {
|
|
59
|
-
import "./
|
|
60
|
-
import "./
|
|
61
|
-
import {
|
|
62
|
-
import "./pairing-
|
|
63
|
-
import
|
|
64
|
-
import "./
|
|
65
|
-
import "./
|
|
66
|
-
import "./
|
|
67
|
-
import
|
|
68
|
-
import "./
|
|
69
|
-
import "./
|
|
70
|
-
import "./
|
|
71
|
-
import "./
|
|
72
|
-
import
|
|
73
|
-
import "./
|
|
74
|
-
import "./
|
|
75
|
-
import { n as
|
|
76
|
-
import
|
|
77
|
-
import
|
|
78
|
-
import {
|
|
79
|
-
import { a as
|
|
80
|
-
import {
|
|
81
|
-
import "./
|
|
82
|
-
import "./
|
|
83
|
-
import {
|
|
84
|
-
import
|
|
85
|
-
import
|
|
86
|
-
import "./
|
|
87
|
-
import {
|
|
88
|
-
import {
|
|
89
|
-
import
|
|
90
|
-
import "./
|
|
91
|
-
import {
|
|
5
|
+
import { E as truncateUtf16Safe, S as shortenHomePath, n as clamp, s as ensureDir, t as CONFIG_DIR, u as isPlainObject, y as resolveUserPath } from "./utils-BLJAc3ZV.js";
|
|
6
|
+
import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-DqZFMawz.js";
|
|
7
|
+
import { t as resolveOpenClawPackageRoot } from "./openclaw-root-BKsZvO6K.js";
|
|
8
|
+
import { T as resolveWorkspaceTemplateDir, _ as DEFAULT_MEMORY_FILENAME, b as DEFAULT_USER_FILENAME, c as resolveDefaultAgentId, d as DEFAULT_AGENTS_FILENAME, g as DEFAULT_MEMORY_ALT_FILENAME, h as DEFAULT_IDENTITY_FILENAME, i as resolveAgentModelFallbacksOverride, l as resolveSessionAgentId, m as DEFAULT_HEARTBEAT_FILENAME, n as resolveAgentConfig, p as DEFAULT_BOOTSTRAP_FILENAME, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, v as DEFAULT_SOUL_FILENAME, w as resolveDefaultAgentWorkspaceDir, x as ensureAgentWorkspace, y as DEFAULT_TOOLS_FILENAME } from "./agent-scope-Bkr9fZtl.js";
|
|
9
|
+
import "./github-copilot-token-C9W4SY9o.js";
|
|
10
|
+
import "./pi-model-discovery-EhM2JAQo.js";
|
|
11
|
+
import { D as applyLegacyMigrations, E as applyMergePatch, M as resolveAgentMaxConcurrent, N as resolveSubagentMaxConcurrent, P as VERSION, a as parseConfigJson5, c as writeConfigFile, g as parseDurationMs, h as sensitive, i as loadConfig, l as validateConfigObjectWithPlugins, n as migrateLegacyConfig, o as readConfigFileSnapshot, r as createConfigIO, s as resolveConfigSnapshotHash, u as OpenClawSchema } from "./config-B8v0zg48.js";
|
|
12
|
+
import { c as isTestDefaultMemorySlotDisabled } from "./manifest-registry-CVsqjgX0.js";
|
|
13
|
+
import { n as movePathToTrash } from "./server-context-FwqBRH3K.js";
|
|
14
|
+
import { n as pickPrimaryTailnetIPv6, t as pickPrimaryTailnetIPv4 } from "./tailnet-uoFvUSsw.js";
|
|
15
|
+
import { a as isTrustedProxyAddress, d as resolveGatewayListenHosts, i as isPrivateOrLoopbackAddress, l as resolveGatewayBindHost, n as isLoopbackAddress, r as isLoopbackHost, t as rawDataToString, u as resolveGatewayClientIp } from "./ws-MC-rTJLe.js";
|
|
16
|
+
import { c as ensurePortAvailable, d as formatPortDiagnostics, l as inspectPortUsage } from "./chrome--Fe8F5Kf.js";
|
|
17
|
+
import { a as AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN, c as safeEqualSecret, d as enableTailscaleFunnel, f as enableTailscaleServe, i as resolveGatewayAuth, l as disableTailscaleFunnel, m as getTailnetHostname, n as authorizeGatewayConnect, o as AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, r as isLocalDirectRequest, s as createAuthRateLimiter, t as assertGatewayAuthConfigured, u as disableTailscaleServe } from "./auth-CLhyWwAU.js";
|
|
18
|
+
import "./control-auth-BlWU-jBl.js";
|
|
19
|
+
import { a as isErrno, n as formatErrorMessage } from "./errors-Cojm0Kl7.js";
|
|
20
|
+
import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-COF59GQe.js";
|
|
21
|
+
import { t as ensureOpenClawCliOnPath } from "./path-env-OJAyUeWW.js";
|
|
22
|
+
import { $ as validateNodePairRequestParams, A as validateCronStatusParams, B as validateExecApprovalsNodeGetParams, Bt as verifyDeviceSignature, C as validateConfigSetParams, Ct as validateWizardCancelParams, D as validateCronRemoveParams, Dt as PROTOCOL_VERSION, E as validateCronListParams, Et as validateWizardStatusParams, F as validateDeviceTokenRevokeParams, Ft as buildDeviceAuthPayload, G as validateNodeDescribeParams, H as validateExecApprovalsSetParams, I as validateDeviceTokenRotateParams, J as validateNodeInvokeResultParams, K as validateNodeEventParams, L as validateExecApprovalRequestParams, Lt as deriveDeviceIdFromPublicKey, M as validateDevicePairApproveParams, N as validateDevicePairListParams, O as validateCronRunParams, Ot as ErrorCodes, P as validateDevicePairRejectParams, Pt as normalizeInputProvenance, Q as validateNodePairRejectParams, R as validateExecApprovalResolveParams, S as validateConfigSchemaParams, St as validateWebLoginWaitParams, T as validateCronAddParams, Tt as validateWizardStartParams, U as validateLogsTailParams, V as validateExecApprovalsNodeSetParams, W as validateModelsListParams, X as validateNodePairApproveParams, Y as validateNodeListParams, Z as validateNodePairListParams, _ as validateChatInjectParams, _t as validateTalkConfigParams, a as validateAgentWaitParams, at as validateSessionsCompactParams, b as validateConfigGetParams, bt as validateWakeParams, c as validateAgentsFilesGetParams, ct as validateSessionsPatchParams, d as validateAgentsListParams, dt as validateSessionsResolveParams, et as validateNodePairVerifyParams, f as validateAgentsUpdateParams, ft as validateSessionsUsageParams, g as validateChatHistoryParams, gt as validateSkillsUpdateParams, h as validateChatAbortParams, ht as validateSkillsStatusParams, i as validateAgentParams, it as validateSendParams, j as validateCronUpdateParams, jt as parseSessionLabel, k as validateCronRunsParams, kt as errorShape, l as validateAgentsFilesListParams, lt as validateSessionsPreviewParams, m as validateChannelsStatusParams, mt as validateSkillsInstallParams, n as formatValidationErrors, nt as validatePollParams, o as validateAgentsCreateParams, ot as validateSessionsDeleteParams, p as validateChannelsLogoutParams, pt as validateSkillsBinsParams, q as validateNodeInvokeParams, r as validateAgentIdentityParams, rt as validateRequestFrame, s as validateAgentsDeleteParams, st as validateSessionsListParams, tt as validateNodeRenameParams, u as validateAgentsFilesSetParams, ut as validateSessionsResetParams, v as validateChatSendParams, vt as validateTalkModeParams, w as validateConnectParams, wt as validateWizardNextParams, x as validateConfigPatchParams, xt as validateWebLoginStartParams, y as validateConfigApplyParams, yt as validateUpdateRunParams, z as validateExecApprovalsGetParams, zt as normalizeDevicePublicKeyBase64Url } from "./client-BhZjzrH2.js";
|
|
23
|
+
import { n as callGateway, o as loadGatewayTlsRuntime$1 } from "./call-CjEdFGAf.js";
|
|
24
|
+
import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, h as GATEWAY_CLIENT_NAMES, i as isGatewayMessageChannel, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-DWcu72r7.js";
|
|
25
|
+
import { t as formatDocsLink } from "./links-DpxpaKe1.js";
|
|
26
|
+
import { r as buildChannelUiCatalog, t as applyPluginAutoEnable } from "./plugin-auto-enable-OO0eDINB.js";
|
|
27
|
+
import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-CxrdL_IZ.js";
|
|
28
|
+
import "./logging-D-Jq2wIo.js";
|
|
29
|
+
import "./accounts-Bvh0DFxS.js";
|
|
30
|
+
import { $ as recordRemoteNodeInfo, $n as isSystemEventContextChanged, $t as loadCombinedSessionStoreForGateway, A as buildHistoryContextFromEntries, An as setGatewaySigusr1RestartPolicy, Ar as stripHeartbeatToken, At as stopSubagentsForRequester, B as getChannelActivity, Bn as inferLegacyName, Bt as getAgentRunContext, C as CONTROL_UI_AVATAR_PREFIX, Cr as startDiagnosticHeartbeat, Ct as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, Dn as consumeGatewaySigusr1RestartAuthorization, Dt as normalizeMimeList, E as resolveAssistantAvatarUrl, En as authorizeGatewaySigusr1Restart, Er as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, Et as extractImageContentFromSource, Fn as summarizeRestartSentinel, Ft as createOpenClawTools, G as getCliSessionId, Gn as migrateLegacyCronPayload, H as dispatchInboundMessage, Hn as normalizeOptionalText, Hr as createInternalHookEvent, Ht as registerAgentRunContext, Ir as ToolInputError, It as initSubagentRegistry, Jn as buildSafeExternalPrompt, K as setCliSessionId, Kt as applyModelOverrideToSessionEntry, L as resolveCronStyleNow, Ln as writeRestartSentinel, Lr as getMemorySearchManager, Lt as resolveAgentTimeoutMs, Mn as formatDoctorNonInteractiveHint, Mr as normalizePollInput, Mt as abortEmbeddedPiRun, N as getLastHeartbeatEvent, Nn as formatRestartSentinelMessage, Nr as resolveUserTimezone, Nt as waitForEmbeddedPiRunEnd, On as isGatewaySigusr1RestartExternallyAllowed, Ot as formatZonedTimestamp, P as onHeartbeatEvent, Q as primeRemoteSkillsCache, Qn as enqueueSystemEvent, Qt as listSessionsFromStore, Rn as normalizeCronJobCreate, Rr as resolveMemoryBackendConfig, Rt as clearAgentRunContext, Sn as resetDirectoryCache, Sr as CommandLane, St as DEFAULT_INPUT_PDF_MAX_PIXELS, T as normalizeControlUiBasePath, Tr as isDiagnosticsEnabled, Tt as extractFileContentFromSource, U as createReplyDispatcher, Un as normalizePayloadToSystemText, Ur as registerInternalHook, Ut as runSubagentAnnounceFlow, Vn as normalizeOptionalAgentId, Vr as clearInternalHooks, Vt as onAgentEvent, Wn as normalizeRequiredName, Wr as triggerInternalHook, Wt as resolveAnnounceTargetFromKey, X as resolveSendPolicy, Xn as getHookType, Y as normalizeSendPolicy, Yn as detectSuspiciousPatterns, Z as getRemoteSkillEligibility, Zn as isExternalHookSession, Zt as listAgentsForGateway, _t as DEFAULT_INPUT_FILE_MIMES, an as readSessionMessages, ar as OPENAI_TTS_VOICES, at as rejectNodePairing, bn as resolveSessionDeliveryTarget, br as setCommandLaneConcurrency, bt as DEFAULT_INPUT_MAX_REDIRECTS, cn as stripEnvelopeFromMessages, cr as isTtsProviderConfigured, ct as updatePairedNodeMetadata, d as handleReset, dn as clearSessionQueues, dr as resolveTtsConfig, dt as registerSkillsChangeListener, en as loadSessionEntry, et as refreshRemoteBinsForConnectedNodes, fn as normalizeGroupActivation, fr as resolveTtsPrefsPath, gr as textToSpeech, gt as DEFAULT_INPUT_FILE_MAX_CHARS, hn as resolveOutboundSessionRoute, hr as setTtsProvider, ht as DEFAULT_INPUT_FILE_MAX_BYTES, in as capArrayByJsonBytes, ir as OPENAI_TTS_MODELS, it as listNodePairing, j as resolveHeartbeatVisibility, jn as consumeRestartSentinel, jt as runEmbeddedPiAgent, k as createReplyPrefixOptions, kn as scheduleGatewaySigusr1Restart, kt as isAbortTrigger, lr as resolveTtsApiKey, lt as verifyNodeToken, mn as ensureOutboundSessionEntry, mr as setTtsEnabled, mt as parseVerboseOverride, n as handleSlackHttpRequest, nn as resolveSessionModelRef, nt as setSkillsRemoteRegistry, on as readSessionPreviewItemsFromTranscript, or as getTtsProvider, ot as renamePairedNode, pr as resolveTtsProviderOrder, pt as applyVerboseOverride, q as runCliAgent, qt as loadProviderUsageSummary, rn as archiveFileOnDisk, rr as getPluginToolMeta, rt as approveNodePairing, sn as resolveSessionTranscriptCandidates, sr as isTtsEnabled, st as requestNodePairing, t as loadOpenClawPlugins, tn as resolveGatewaySessionStoreTarget, tr as requestHeartbeatNow, tt as refreshRemoteNodeBins, un as lookupContextTokens, ur as resolveTtsAutoMode, ut as getSkillsSnapshotVersion, vr as getActiveTaskCount, vt as DEFAULT_INPUT_IMAGE_MAX_BYTES, w as buildControlUiAvatarUrl, wn as runWithModelFallback, wr as stopDiagnosticHeartbeat, wt as DEFAULT_INPUT_TIMEOUT_MS, xr as waitForActiveTasks, xt as DEFAULT_INPUT_PDF_MAX_PAGES, yn as resolveOutboundTarget, yt as DEFAULT_INPUT_IMAGE_MIMES, zn as normalizeCronJobPatch, zr as resolveAgentIdentity, zt as emitAgentEvent } from "./loader-CS-5lMQa.js";
|
|
31
|
+
import { n as withProgress } from "./progress-COHv-uNT.js";
|
|
32
|
+
import "./prompt-style-Cf1r1L6k.js";
|
|
33
|
+
import "./note-Duiadw1g.js";
|
|
34
|
+
import { t as WizardCancelledError } from "./prompts-Dszjy1n_.js";
|
|
35
|
+
import { t as resolveChannelDefaultAccountId } from "./helpers-ByYj2Aq5.js";
|
|
36
|
+
import "./onboard-channels-C5uL3i8d.js";
|
|
37
|
+
import "./archive-aSMUcOc6.js";
|
|
38
|
+
import "./skill-scanner-BrGkh5K7.js";
|
|
39
|
+
import "./installs-z69au9Te.js";
|
|
40
|
+
import { J as getGlobalHookRunner, o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads } from "./deliver-BHNoC9Yk.js";
|
|
41
|
+
import "./manager-ChW0jk7T.js";
|
|
42
|
+
import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-BZK4Ct0I.js";
|
|
43
|
+
import "./sqlite-2UsPaJz5.js";
|
|
44
|
+
import "./redact-UvkXqguc.js";
|
|
45
|
+
import { p as detectMime, r as saveMediaBuffer } from "./routes-CaCvio4Q.js";
|
|
46
|
+
import { B as normalizeElevatedLevel, H as normalizeThinkLevel, I as formatThinkingLevels, K as supportsXHighThinking, L as formatXHighModelHint, U as normalizeUsageDisplay, V as normalizeReasoningLevel, W as normalizeVerboseLevel } from "./pi-embedded-helpers-DpJb0kUk.js";
|
|
47
|
+
import "./fetch-wuOZDzdT.js";
|
|
48
|
+
import { $ as normalizeToolName, I as resolveAgentMainSessionKey, L as resolveExplicitAgentSessionKey, R as resolveMainSessionKey, S as mergeDeliveryContext, V as snapshotSessionOrigin, X as collectExplicitAllowlist, Y as buildPluginToolGroups, Z as expandPolicyWithPluginGroups, b as deliveryContextFromSession, d as loadSessionStore, et as resolveToolProfilePolicy, g as updateSessionStore, tt as stripPluginOnlyAllowlist, w as normalizeSessionDeliveryFields, z as resolveMainSessionKeyFromConfig } from "./sandbox-DNHDwHw8.js";
|
|
49
|
+
import "./commands-registry-6NUFrejL.js";
|
|
50
|
+
import { i as getMachineDisplayName, r as createBrowserRouteDispatcher } from "./wsl-BvTIzy-8.js";
|
|
51
|
+
import { h as hasBinary, i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot } from "./skills-CE7by2IF.js";
|
|
52
|
+
import "./image-D-5pUELC.js";
|
|
53
|
+
import { c as normalizeExecApprovals, g as saveExecApprovals, l as readExecApprovalsSnapshot, m as resolveExecApprovalsSocketPath, r as ensureExecApprovals } from "./exec-approvals-C67V-ljH.js";
|
|
54
|
+
import "./nodes-screen-CVL9363A.js";
|
|
55
|
+
import "./tool-display-DbdMQFZx.js";
|
|
56
|
+
import { t as parseAbsoluteTimeMs } from "./parse-Bw0oH-rT.js";
|
|
57
|
+
import { n as resolveMessageChannelSelection } from "./channel-selection-BaW1xXEa.js";
|
|
58
|
+
import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-B-tyjp76.js";
|
|
59
|
+
import { n as formatTokenCount, r as formatUsd } from "./usage-format-Bhl_WCWP.js";
|
|
60
|
+
import { _ as loadModelCatalog, h as registerUnhandledRejectionHandler } from "./runner-BEy5ZGFv.js";
|
|
61
|
+
import { c as resolveSubagentToolPolicy, i as filterToolsByPolicy, o as resolveEffectiveToolPolicy, s as resolveGroupToolPolicy } from "./commands-gOiRcfoU.js";
|
|
62
|
+
import "./pairing-store-Dz-ArTQS.js";
|
|
63
|
+
import "./login-qr-BsYM2E1y.js";
|
|
64
|
+
import { n as runCommandWithRuntime } from "./cli-utils-CodyYLHe.js";
|
|
65
|
+
import "./pairing-labels-Dt2vXyI7.js";
|
|
66
|
+
import { t as buildChannelAccountSnapshot } from "./status-DCkF_L3U.js";
|
|
67
|
+
import "./channels-status-issues-kb-M2Fi0.js";
|
|
68
|
+
import "./register.subclis-ifHtmF3e.js";
|
|
69
|
+
import "./completion-cli-Drks7xRK.js";
|
|
70
|
+
import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-CP0dcOgD.js";
|
|
71
|
+
import "./daemon-runtime-ZWXvLDxx.js";
|
|
72
|
+
import "./service-DZN7KRok.js";
|
|
73
|
+
import "./systemd-s3S2HVog.js";
|
|
74
|
+
import "./shared-Csn6DLBA.js";
|
|
75
|
+
import { a as runDaemonStop, i as runDaemonStart, n as runDaemonStatus, o as runDaemonUninstall, r as runDaemonRestart, s as runDaemonInstall } from "./daemon-cli-BLbzcTuD.js";
|
|
76
|
+
import "./service-audit-B8KIOe8A.js";
|
|
77
|
+
import "./table-Bvka_vkc.js";
|
|
78
|
+
import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-C4RnIR9O.js";
|
|
79
|
+
import { a as toOptionString, i as parsePort$1, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-CsAwU6-q.js";
|
|
80
|
+
import { a as resolveNodeCommandAllowlist, i as isNodeCommandAllowed, o as probeGateway } from "./audit-BYfhZ7LA.js";
|
|
81
|
+
import { S as discoverGatewayBeacons, n as installSkill } from "./onboard-skills-BFxdI1Y1.js";
|
|
82
|
+
import { a as resolveControlUiRootOverrideSync, c as getHealthSnapshot, d as runHeartbeatOnce, f as setHeartbeatsEnabled, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync, p as startHeartbeatRunner, s as formatHealthChannelLines } from "./health-format-Nd0jcoqM.js";
|
|
83
|
+
import { S as normalizeUpdateChannel, _ as resolveNpmChannelTag, h as compareSemverStrings, m as checkUpdateStatus, t as runGatewayUpdate, y as DEFAULT_PACKAGE_CHANNEL } from "./update-runner-BaLsla0c.js";
|
|
84
|
+
import "./github-copilot-auth-BUqfX7hG.js";
|
|
85
|
+
import "./logging-B3KnAryz.js";
|
|
86
|
+
import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-BbIz0zmm.js";
|
|
87
|
+
import { a as findAgentEntryIndex, c as pruneAgentConfig, f as runOnboardingWizard, n as getStatusSummary, o as listAgentEntries, r as applyAgentConfig, s as loadAgentIdentity, u as loadAgentIdentityFromWorkspace } from "./status-Cm4q6o-I.js";
|
|
88
|
+
import { t as buildWorkspaceSkillStatus } from "./skills-status-B99Us6yS.js";
|
|
89
|
+
import "./tui-B40Z2jMa.js";
|
|
90
|
+
import { i as setGatewayWsLogStyle, n as logWs, r as summarizeAgentEventForWsLog, t as formatForLog } from "./ws-log-WrJ4QYu7.js";
|
|
91
|
+
import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-HvKMdooP.js";
|
|
92
|
+
import { a as createOutboundSendDeps$1, i as resolveAgentOutboundTarget, r as resolveAgentDeliveryPlan, t as agentCommand } from "./agent-DjZxytiC.js";
|
|
93
|
+
import "./node-service-C7f_uvx9.js";
|
|
94
|
+
import { n as forceFreePortAndWait } from "./ports-C8YYHVlc.js";
|
|
92
95
|
import { spawn, spawnSync } from "node:child_process";
|
|
93
96
|
import path from "node:path";
|
|
94
97
|
import os from "node:os";
|
|
@@ -862,7 +865,7 @@ function truncateCloseReason(reason, maxBytes = CLOSE_REASON_MAX_BYTES) {
|
|
|
862
865
|
|
|
863
866
|
//#endregion
|
|
864
867
|
//#region src/infra/exec-approval-forwarder.ts
|
|
865
|
-
const log$
|
|
868
|
+
const log$5 = createSubsystemLogger("gateway/exec-approvals");
|
|
866
869
|
const DEFAULT_MODE = "session";
|
|
867
870
|
function normalizeMode(mode) {
|
|
868
871
|
return mode ?? DEFAULT_MODE;
|
|
@@ -977,7 +980,7 @@ async function deliverToTargets(params) {
|
|
|
977
980
|
payloads: [{ text: params.text }]
|
|
978
981
|
});
|
|
979
982
|
} catch (err) {
|
|
980
|
-
log$
|
|
983
|
+
log$5.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
|
|
981
984
|
}
|
|
982
985
|
});
|
|
983
986
|
await Promise.allSettled(deliveries);
|
|
@@ -1204,6 +1207,10 @@ const BASE_RELOAD_RULES = [
|
|
|
1204
1207
|
}
|
|
1205
1208
|
];
|
|
1206
1209
|
const BASE_RELOAD_RULES_TAIL = [
|
|
1210
|
+
{
|
|
1211
|
+
prefix: "meta",
|
|
1212
|
+
kind: "none"
|
|
1213
|
+
},
|
|
1207
1214
|
{
|
|
1208
1215
|
prefix: "identity",
|
|
1209
1216
|
kind: "none"
|
|
@@ -2100,10 +2107,6 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
|
|
|
2100
2107
|
const last = agentRunSeq.get(evt.runId) ?? 0;
|
|
2101
2108
|
const isToolEvent = evt.stream === "tool";
|
|
2102
2109
|
const toolVerbose = isToolEvent ? resolveToolVerboseLevel(evt.runId, sessionKey) : "off";
|
|
2103
|
-
if (isToolEvent && toolVerbose === "off") {
|
|
2104
|
-
agentRunSeq.set(evt.runId, evt.seq);
|
|
2105
|
-
return;
|
|
2106
|
-
}
|
|
2107
2110
|
const toolPayload = isToolEvent && toolVerbose !== "full" ? (() => {
|
|
2108
2111
|
const data = evt.data ? { ...evt.data } : {};
|
|
2109
2112
|
delete data.result;
|
|
@@ -2135,7 +2138,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
|
|
|
2135
2138
|
} else broadcast("agent", agentPayload);
|
|
2136
2139
|
const lifecyclePhase = evt.stream === "lifecycle" && typeof evt.data?.phase === "string" ? evt.data.phase : null;
|
|
2137
2140
|
if (sessionKey) {
|
|
2138
|
-
nodeSendToSession(sessionKey, "agent", isToolEvent ? toolPayload : agentPayload);
|
|
2141
|
+
if (!isToolEvent || toolVerbose !== "off") nodeSendToSession(sessionKey, "agent", isToolEvent ? toolPayload : agentPayload);
|
|
2139
2142
|
if (!isAborted && evt.stream === "assistant" && typeof evt.data?.text === "string") emitChatDelta(sessionKey, clientRunId, evt.seq, evt.data.text);
|
|
2140
2143
|
else if (!isAborted && (lifecyclePhase === "end" || lifecyclePhase === "error")) if (chatLink) {
|
|
2141
2144
|
const finished = chatRunState.registry.shift(evt.runId);
|
|
@@ -2168,7 +2171,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
|
|
|
2168
2171
|
* Automatically starts `gog gmail watch serve` when the gateway starts,
|
|
2169
2172
|
* if hooks.gmail is configured with an account.
|
|
2170
2173
|
*/
|
|
2171
|
-
const log$
|
|
2174
|
+
const log$4 = createSubsystemLogger("gmail-watcher");
|
|
2172
2175
|
const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
|
|
2173
2176
|
function isAddressInUseError(line) {
|
|
2174
2177
|
return ADDRESS_IN_USE_RE.test(line);
|
|
@@ -2192,13 +2195,13 @@ async function startGmailWatch(cfg) {
|
|
|
2192
2195
|
const result = await runCommandWithTimeout(args, { timeoutMs: 12e4 });
|
|
2193
2196
|
if (result.code !== 0) {
|
|
2194
2197
|
const message = result.stderr || result.stdout || "gog watch start failed";
|
|
2195
|
-
log$
|
|
2198
|
+
log$4.error(`watch start failed: ${message}`);
|
|
2196
2199
|
return false;
|
|
2197
2200
|
}
|
|
2198
|
-
log$
|
|
2201
|
+
log$4.info(`watch started for ${cfg.account}`);
|
|
2199
2202
|
return true;
|
|
2200
2203
|
} catch (err) {
|
|
2201
|
-
log$
|
|
2204
|
+
log$4.error(`watch start error: ${String(err)}`);
|
|
2202
2205
|
return false;
|
|
2203
2206
|
}
|
|
2204
2207
|
}
|
|
@@ -2207,7 +2210,7 @@ async function startGmailWatch(cfg) {
|
|
|
2207
2210
|
*/
|
|
2208
2211
|
function spawnGogServe(cfg) {
|
|
2209
2212
|
const args = buildGogWatchServeArgs(cfg);
|
|
2210
|
-
log$
|
|
2213
|
+
log$4.info(`starting gog ${args.join(" ")}`);
|
|
2211
2214
|
let addressInUse = false;
|
|
2212
2215
|
const child = spawn("gog", args, {
|
|
2213
2216
|
stdio: [
|
|
@@ -2219,25 +2222,25 @@ function spawnGogServe(cfg) {
|
|
|
2219
2222
|
});
|
|
2220
2223
|
child.stdout?.on("data", (data) => {
|
|
2221
2224
|
const line = data.toString().trim();
|
|
2222
|
-
if (line) log$
|
|
2225
|
+
if (line) log$4.info(`[gog] ${line}`);
|
|
2223
2226
|
});
|
|
2224
2227
|
child.stderr?.on("data", (data) => {
|
|
2225
2228
|
const line = data.toString().trim();
|
|
2226
2229
|
if (!line) return;
|
|
2227
2230
|
if (isAddressInUseError(line)) addressInUse = true;
|
|
2228
|
-
log$
|
|
2231
|
+
log$4.warn(`[gog] ${line}`);
|
|
2229
2232
|
});
|
|
2230
2233
|
child.on("error", (err) => {
|
|
2231
|
-
log$
|
|
2234
|
+
log$4.error(`gog process error: ${String(err)}`);
|
|
2232
2235
|
});
|
|
2233
2236
|
child.on("exit", (code, signal) => {
|
|
2234
2237
|
if (shuttingDown) return;
|
|
2235
2238
|
if (addressInUse) {
|
|
2236
|
-
log$
|
|
2239
|
+
log$4.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set OPENCLAW_SKIP_GMAIL_WATCHER=1 or stop the other process.");
|
|
2237
2240
|
watcherProcess = null;
|
|
2238
2241
|
return;
|
|
2239
2242
|
}
|
|
2240
|
-
log$
|
|
2243
|
+
log$4.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
|
|
2241
2244
|
watcherProcess = null;
|
|
2242
2245
|
setTimeout(() => {
|
|
2243
2246
|
if (shuttingDown || !currentConfig) return;
|
|
@@ -2277,15 +2280,15 @@ async function startGmailWatcher(cfg) {
|
|
|
2277
2280
|
port: runtimeConfig.serve.port,
|
|
2278
2281
|
target: runtimeConfig.tailscale.target
|
|
2279
2282
|
});
|
|
2280
|
-
log$
|
|
2283
|
+
log$4.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
|
|
2281
2284
|
} catch (err) {
|
|
2282
|
-
log$
|
|
2285
|
+
log$4.error(`tailscale setup failed: ${String(err)}`);
|
|
2283
2286
|
return {
|
|
2284
2287
|
started: false,
|
|
2285
2288
|
reason: `tailscale setup failed: ${String(err)}`
|
|
2286
2289
|
};
|
|
2287
2290
|
}
|
|
2288
|
-
if (!await startGmailWatch(runtimeConfig)) log$
|
|
2291
|
+
if (!await startGmailWatch(runtimeConfig)) log$4.warn("gmail watch start failed, but continuing with serve");
|
|
2289
2292
|
shuttingDown = false;
|
|
2290
2293
|
watcherProcess = spawnGogServe(runtimeConfig);
|
|
2291
2294
|
const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
|
|
@@ -2293,7 +2296,7 @@ async function startGmailWatcher(cfg) {
|
|
|
2293
2296
|
if (shuttingDown) return;
|
|
2294
2297
|
startGmailWatch(runtimeConfig);
|
|
2295
2298
|
}, renewMs);
|
|
2296
|
-
log$
|
|
2299
|
+
log$4.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
|
|
2297
2300
|
return { started: true };
|
|
2298
2301
|
}
|
|
2299
2302
|
/**
|
|
@@ -2306,7 +2309,7 @@ async function stopGmailWatcher() {
|
|
|
2306
2309
|
renewInterval = null;
|
|
2307
2310
|
}
|
|
2308
2311
|
if (watcherProcess) {
|
|
2309
|
-
log$
|
|
2312
|
+
log$4.info("stopping gmail watcher");
|
|
2310
2313
|
watcherProcess.kill("SIGTERM");
|
|
2311
2314
|
await new Promise((resolve) => {
|
|
2312
2315
|
const timeout = setTimeout(() => {
|
|
@@ -2321,7 +2324,7 @@ async function stopGmailWatcher() {
|
|
|
2321
2324
|
watcherProcess = null;
|
|
2322
2325
|
}
|
|
2323
2326
|
currentConfig = null;
|
|
2324
|
-
log$
|
|
2327
|
+
log$4.info("gmail watcher stopped");
|
|
2325
2328
|
}
|
|
2326
2329
|
|
|
2327
2330
|
//#endregion
|
|
@@ -2551,6 +2554,8 @@ function resolveCronSession(params) {
|
|
|
2551
2554
|
thinkingLevel: entry?.thinkingLevel,
|
|
2552
2555
|
verboseLevel: entry?.verboseLevel,
|
|
2553
2556
|
model: entry?.model,
|
|
2557
|
+
modelOverride: entry?.modelOverride,
|
|
2558
|
+
providerOverride: entry?.providerOverride,
|
|
2554
2559
|
contextTokens: entry?.contextTokens,
|
|
2555
2560
|
sendPolicy: entry?.sendPolicy,
|
|
2556
2561
|
lastChannel: entry?.lastChannel,
|
|
@@ -2584,9 +2589,8 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2584
2589
|
const defaultAgentId = resolveDefaultAgentId(params.cfg);
|
|
2585
2590
|
const requestedAgentId = typeof params.agentId === "string" && params.agentId.trim() ? params.agentId : typeof params.job.agentId === "string" && params.job.agentId.trim() ? params.job.agentId : void 0;
|
|
2586
2591
|
const normalizedRequested = requestedAgentId ? normalizeAgentId(requestedAgentId) : void 0;
|
|
2587
|
-
const
|
|
2588
|
-
const
|
|
2589
|
-
const agentId = agentConfigOverride ? normalizedRequested ?? defaultAgentId : defaultAgentId;
|
|
2592
|
+
const { model: overrideModel, ...agentOverrideRest } = (normalizedRequested ? resolveAgentConfig(params.cfg, normalizedRequested) : void 0) ?? {};
|
|
2593
|
+
const agentId = normalizedRequested ?? defaultAgentId;
|
|
2590
2594
|
const agentCfg = Object.assign({}, params.cfg.agents?.defaults, agentOverrideRest);
|
|
2591
2595
|
if (typeof overrideModel === "string") agentCfg.model = { primary: overrideModel };
|
|
2592
2596
|
else if (overrideModel) agentCfg.model = overrideModel;
|
|
@@ -2618,6 +2622,7 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2618
2622
|
return catalog;
|
|
2619
2623
|
};
|
|
2620
2624
|
const isGmailHook = baseSessionKey.startsWith("hook:gmail:");
|
|
2625
|
+
let hooksGmailModelApplied = false;
|
|
2621
2626
|
const hooksGmailModelRef = isGmailHook ? resolveHooksGmailModel({
|
|
2622
2627
|
cfg: params.cfg,
|
|
2623
2628
|
defaultProvider: DEFAULT_PROVIDER
|
|
@@ -2632,6 +2637,7 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2632
2637
|
}).allowed) {
|
|
2633
2638
|
provider = hooksGmailModelRef.provider;
|
|
2634
2639
|
model = hooksGmailModelRef.model;
|
|
2640
|
+
hooksGmailModelApplied = true;
|
|
2635
2641
|
}
|
|
2636
2642
|
}
|
|
2637
2643
|
const modelOverrideRaw = params.job.payload.kind === "agentTurn" ? params.job.payload.model : void 0;
|
|
@@ -2677,6 +2683,23 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2677
2683
|
const labelSuffix = typeof params.job.name === "string" && params.job.name.trim() ? params.job.name.trim() : params.job.id;
|
|
2678
2684
|
cronSession.sessionEntry.label = `Cron: ${labelSuffix}`;
|
|
2679
2685
|
}
|
|
2686
|
+
if (!modelOverride && !hooksGmailModelApplied) {
|
|
2687
|
+
const sessionModelOverride = cronSession.sessionEntry.modelOverride?.trim();
|
|
2688
|
+
if (sessionModelOverride) {
|
|
2689
|
+
const sessionProviderOverride = cronSession.sessionEntry.providerOverride?.trim() || resolvedDefault.provider;
|
|
2690
|
+
const resolvedSessionOverride = resolveAllowedModelRef({
|
|
2691
|
+
cfg: cfgWithAgentDefaults,
|
|
2692
|
+
catalog: await loadCatalog(),
|
|
2693
|
+
raw: `${sessionProviderOverride}/${sessionModelOverride}`,
|
|
2694
|
+
defaultProvider: resolvedDefault.provider,
|
|
2695
|
+
defaultModel: resolvedDefault.model
|
|
2696
|
+
});
|
|
2697
|
+
if (!("error" in resolvedSessionOverride)) {
|
|
2698
|
+
provider = resolvedSessionOverride.ref.provider;
|
|
2699
|
+
model = resolvedSessionOverride.ref.model;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2680
2703
|
const hooksGmailThinking = isGmailHook ? normalizeThinkLevel(params.cfg.hooks?.gmail?.thinking) : void 0;
|
|
2681
2704
|
const thinkOverride = normalizeThinkLevel(agentCfg?.thinkingDefault);
|
|
2682
2705
|
let thinkLevel = normalizeThinkLevel((params.job.payload.kind === "agentTurn" ? params.job.payload.thinking : void 0) ?? void 0) ?? hooksGmailThinking ?? thinkOverride;
|
|
@@ -2701,10 +2724,7 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2701
2724
|
channel: deliveryPlan.channel ?? "last",
|
|
2702
2725
|
to: deliveryPlan.to
|
|
2703
2726
|
});
|
|
2704
|
-
const
|
|
2705
|
-
const userTimeFormat = resolveUserTimeFormat(params.cfg.agents?.defaults?.timeFormat);
|
|
2706
|
-
const formattedTime = formatUserTime(new Date(now), userTimezone, userTimeFormat) ?? new Date(now).toISOString();
|
|
2707
|
-
const timeLine = `Current time: ${formattedTime} (${userTimezone})`;
|
|
2727
|
+
const { formattedTime, timeLine } = resolveCronStyleNow(params.cfg, now);
|
|
2708
2728
|
const base = `[cron:${params.job.id} ${params.job.name}] ${params.message}`.trim();
|
|
2709
2729
|
const isExternalHook = isExternalHookSession(baseSessionKey);
|
|
2710
2730
|
const allowUnsafeExternalContent = agentPayload?.allowUnsafeExternalContent === true || isGmailHook && params.cfg.hooks?.gmail?.allowUnsafeExternalContent === true;
|
|
@@ -2817,6 +2837,7 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2817
2837
|
const payloads = runResult.payloads ?? [];
|
|
2818
2838
|
{
|
|
2819
2839
|
const usage = runResult.meta.agentMeta?.usage;
|
|
2840
|
+
const promptTokens = runResult.meta.agentMeta?.promptTokens;
|
|
2820
2841
|
const modelUsed = runResult.meta.agentMeta?.model ?? fallbackModel ?? model;
|
|
2821
2842
|
const providerUsed = runResult.meta.agentMeta?.provider ?? fallbackProvider ?? provider;
|
|
2822
2843
|
const contextTokens = agentCfg?.contextTokens ?? lookupContextTokens(modelUsed) ?? DEFAULT_CONTEXT_TOKENS;
|
|
@@ -2830,12 +2851,15 @@ async function runCronIsolatedAgentTurn(params) {
|
|
|
2830
2851
|
if (hasNonzeroUsage(usage)) {
|
|
2831
2852
|
const input = usage.input ?? 0;
|
|
2832
2853
|
const output = usage.output ?? 0;
|
|
2833
|
-
|
|
2834
|
-
cronSession.sessionEntry.outputTokens = output;
|
|
2835
|
-
cronSession.sessionEntry.totalTokens = deriveSessionTotalTokens({
|
|
2854
|
+
const totalTokens = deriveSessionTotalTokens({
|
|
2836
2855
|
usage,
|
|
2837
|
-
contextTokens
|
|
2856
|
+
contextTokens,
|
|
2857
|
+
promptTokens
|
|
2838
2858
|
}) ?? input;
|
|
2859
|
+
cronSession.sessionEntry.inputTokens = input;
|
|
2860
|
+
cronSession.sessionEntry.outputTokens = output;
|
|
2861
|
+
cronSession.sessionEntry.totalTokens = totalTokens;
|
|
2862
|
+
cronSession.sessionEntry.totalTokensFresh = true;
|
|
2839
2863
|
}
|
|
2840
2864
|
await persistSessionEntry();
|
|
2841
2865
|
}
|
|
@@ -3052,10 +3076,10 @@ function computeNextRunAtMs(schedule, nowMs) {
|
|
|
3052
3076
|
catch: false
|
|
3053
3077
|
});
|
|
3054
3078
|
const nowSecondMs = Math.floor(nowMs / 1e3) * 1e3;
|
|
3055
|
-
const next = cron.nextRun(
|
|
3079
|
+
const next = cron.nextRun(new Date(nowSecondMs));
|
|
3056
3080
|
if (!next) return;
|
|
3057
3081
|
const nextMs = next.getTime();
|
|
3058
|
-
return Number.isFinite(nextMs) && nextMs
|
|
3082
|
+
return Number.isFinite(nextMs) && nextMs > nowSecondMs ? nextMs : void 0;
|
|
3059
3083
|
}
|
|
3060
3084
|
|
|
3061
3085
|
//#endregion
|
|
@@ -3098,6 +3122,8 @@ function computeJobNextRunAtMs(job, nowMs) {
|
|
|
3098
3122
|
}
|
|
3099
3123
|
return computeNextRunAtMs(job.schedule, nowMs);
|
|
3100
3124
|
}
|
|
3125
|
+
/** Maximum consecutive schedule errors before auto-disabling a job. */
|
|
3126
|
+
const MAX_SCHEDULE_ERRORS = 3;
|
|
3101
3127
|
function recomputeNextRuns(state) {
|
|
3102
3128
|
if (!state.store) return false;
|
|
3103
3129
|
let changed = false;
|
|
@@ -3128,12 +3154,82 @@ function recomputeNextRuns(state) {
|
|
|
3128
3154
|
changed = true;
|
|
3129
3155
|
}
|
|
3130
3156
|
const nextRun = job.state.nextRunAtMs;
|
|
3131
|
-
if (nextRun === void 0 || now >= nextRun) {
|
|
3157
|
+
if (nextRun === void 0 || now >= nextRun) try {
|
|
3132
3158
|
const newNext = computeJobNextRunAtMs(job, now);
|
|
3133
3159
|
if (job.state.nextRunAtMs !== newNext) {
|
|
3134
3160
|
job.state.nextRunAtMs = newNext;
|
|
3135
3161
|
changed = true;
|
|
3136
3162
|
}
|
|
3163
|
+
if (job.state.scheduleErrorCount) {
|
|
3164
|
+
job.state.scheduleErrorCount = void 0;
|
|
3165
|
+
changed = true;
|
|
3166
|
+
}
|
|
3167
|
+
} catch (err) {
|
|
3168
|
+
const errorCount = (job.state.scheduleErrorCount ?? 0) + 1;
|
|
3169
|
+
job.state.scheduleErrorCount = errorCount;
|
|
3170
|
+
job.state.nextRunAtMs = void 0;
|
|
3171
|
+
job.state.lastError = `schedule error: ${String(err)}`;
|
|
3172
|
+
changed = true;
|
|
3173
|
+
if (errorCount >= MAX_SCHEDULE_ERRORS) {
|
|
3174
|
+
job.enabled = false;
|
|
3175
|
+
state.deps.log.error({
|
|
3176
|
+
jobId: job.id,
|
|
3177
|
+
name: job.name,
|
|
3178
|
+
errorCount,
|
|
3179
|
+
err: String(err)
|
|
3180
|
+
}, "cron: auto-disabled job after repeated schedule errors");
|
|
3181
|
+
} else state.deps.log.warn({
|
|
3182
|
+
jobId: job.id,
|
|
3183
|
+
name: job.name,
|
|
3184
|
+
errorCount,
|
|
3185
|
+
err: String(err)
|
|
3186
|
+
}, "cron: failed to compute next run for job (skipping)");
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
return changed;
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Maintenance-only version of recomputeNextRuns that handles disabled jobs
|
|
3193
|
+
* and stuck markers, but does NOT recompute nextRunAtMs for enabled jobs
|
|
3194
|
+
* with existing values. Used during timer ticks when no due jobs were found
|
|
3195
|
+
* to prevent silently advancing past-due nextRunAtMs values without execution
|
|
3196
|
+
* (see #13992).
|
|
3197
|
+
*/
|
|
3198
|
+
function recomputeNextRunsForMaintenance(state) {
|
|
3199
|
+
if (!state.store) return false;
|
|
3200
|
+
let changed = false;
|
|
3201
|
+
const now = state.deps.nowMs();
|
|
3202
|
+
for (const job of state.store.jobs) {
|
|
3203
|
+
if (!job.state) {
|
|
3204
|
+
job.state = {};
|
|
3205
|
+
changed = true;
|
|
3206
|
+
}
|
|
3207
|
+
if (!job.enabled) {
|
|
3208
|
+
if (job.state.nextRunAtMs !== void 0) {
|
|
3209
|
+
job.state.nextRunAtMs = void 0;
|
|
3210
|
+
changed = true;
|
|
3211
|
+
}
|
|
3212
|
+
if (job.state.runningAtMs !== void 0) {
|
|
3213
|
+
job.state.runningAtMs = void 0;
|
|
3214
|
+
changed = true;
|
|
3215
|
+
}
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
const runningAt = job.state.runningAtMs;
|
|
3219
|
+
if (typeof runningAt === "number" && now - runningAt > STUCK_RUN_MS) {
|
|
3220
|
+
state.deps.log.warn({
|
|
3221
|
+
jobId: job.id,
|
|
3222
|
+
runningAtMs: runningAt
|
|
3223
|
+
}, "cron: clearing stuck running marker");
|
|
3224
|
+
job.state.runningAtMs = void 0;
|
|
3225
|
+
changed = true;
|
|
3226
|
+
}
|
|
3227
|
+
if (job.state.nextRunAtMs === void 0) {
|
|
3228
|
+
const newNext = computeJobNextRunAtMs(job, now);
|
|
3229
|
+
if (newNext !== void 0) {
|
|
3230
|
+
job.state.nextRunAtMs = newNext;
|
|
3231
|
+
changed = true;
|
|
3232
|
+
}
|
|
3137
3233
|
}
|
|
3138
3234
|
}
|
|
3139
3235
|
return changed;
|
|
@@ -3793,7 +3889,7 @@ function applyJobResult(state, job, result) {
|
|
|
3793
3889
|
job.updatedAtMs = result.endedAt;
|
|
3794
3890
|
if (result.status === "error") job.state.consecutiveErrors = (job.state.consecutiveErrors ?? 0) + 1;
|
|
3795
3891
|
else job.state.consecutiveErrors = 0;
|
|
3796
|
-
const shouldDelete = job.schedule.kind === "at" &&
|
|
3892
|
+
const shouldDelete = job.schedule.kind === "at" && job.deleteAfterRun === true && result.status === "ok";
|
|
3797
3893
|
if (!shouldDelete) if (job.schedule.kind === "at") {
|
|
3798
3894
|
job.enabled = false;
|
|
3799
3895
|
job.state.nextRunAtMs = void 0;
|
|
@@ -3854,7 +3950,17 @@ function armTimer(state) {
|
|
|
3854
3950
|
}, "cron: timer armed");
|
|
3855
3951
|
}
|
|
3856
3952
|
async function onTimer(state) {
|
|
3857
|
-
if (state.running)
|
|
3953
|
+
if (state.running) {
|
|
3954
|
+
if (state.timer) clearTimeout(state.timer);
|
|
3955
|
+
state.timer = setTimeout(async () => {
|
|
3956
|
+
try {
|
|
3957
|
+
await onTimer(state);
|
|
3958
|
+
} catch (err) {
|
|
3959
|
+
state.deps.log.error({ err: String(err) }, "cron: timer tick failed");
|
|
3960
|
+
}
|
|
3961
|
+
}, MAX_TIMER_DELAY_MS);
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3858
3964
|
state.running = true;
|
|
3859
3965
|
try {
|
|
3860
3966
|
const dueJobs = await locked(state, async () => {
|
|
@@ -3864,7 +3970,7 @@ async function onTimer(state) {
|
|
|
3864
3970
|
});
|
|
3865
3971
|
const due = findDueJobs(state);
|
|
3866
3972
|
if (due.length === 0) {
|
|
3867
|
-
if (
|
|
3973
|
+
if (recomputeNextRunsForMaintenance(state)) await persist(state);
|
|
3868
3974
|
return [];
|
|
3869
3975
|
}
|
|
3870
3976
|
const now = state.deps.nowMs();
|
|
@@ -4000,7 +4106,7 @@ async function runMissedJobs(state) {
|
|
|
4000
4106
|
if (!j.enabled) return false;
|
|
4001
4107
|
if (typeof j.state.runningAtMs === "number") return false;
|
|
4002
4108
|
const next = j.state.nextRunAtMs;
|
|
4003
|
-
if (j.schedule.kind === "at" && j.state.lastStatus
|
|
4109
|
+
if (j.schedule.kind === "at" && j.state.lastStatus) return false;
|
|
4004
4110
|
return typeof next === "number" && now >= next;
|
|
4005
4111
|
});
|
|
4006
4112
|
if (missed.length > 0) {
|
|
@@ -4026,7 +4132,10 @@ async function executeJobCore(state, job) {
|
|
|
4026
4132
|
const waitStartedAt = state.deps.nowMs();
|
|
4027
4133
|
let heartbeatResult;
|
|
4028
4134
|
for (;;) {
|
|
4029
|
-
heartbeatResult = await state.deps.runHeartbeatOnce({
|
|
4135
|
+
heartbeatResult = await state.deps.runHeartbeatOnce({
|
|
4136
|
+
reason,
|
|
4137
|
+
agentId: job.agentId
|
|
4138
|
+
});
|
|
4030
4139
|
if (heartbeatResult.status !== "skipped" || heartbeatResult.reason !== "requests-in-flight") break;
|
|
4031
4140
|
if (state.deps.nowMs() - waitStartedAt > maxWaitMs) {
|
|
4032
4141
|
state.deps.requestHeartbeatNow({ reason });
|
|
@@ -4405,9 +4514,12 @@ function buildGatewayCronService(params) {
|
|
|
4405
4514
|
},
|
|
4406
4515
|
requestHeartbeatNow,
|
|
4407
4516
|
runHeartbeatOnce: async (opts) => {
|
|
4517
|
+
const runtimeConfig = loadConfig();
|
|
4518
|
+
const agentId = opts?.agentId ? resolveCronAgent(opts.agentId).agentId : void 0;
|
|
4408
4519
|
return await runHeartbeatOnce({
|
|
4409
|
-
cfg:
|
|
4520
|
+
cfg: runtimeConfig,
|
|
4410
4521
|
reason: opts?.reason,
|
|
4522
|
+
agentId,
|
|
4411
4523
|
deps: {
|
|
4412
4524
|
...params.deps,
|
|
4413
4525
|
runtime: defaultRuntime
|
|
@@ -4788,8 +4900,8 @@ function abortChatRunsForSessionKey(ops, params) {
|
|
|
4788
4900
|
|
|
4789
4901
|
//#endregion
|
|
4790
4902
|
//#region src/gateway/server-constants.ts
|
|
4791
|
-
const MAX_PAYLOAD_BYTES =
|
|
4792
|
-
const MAX_BUFFERED_BYTES =
|
|
4903
|
+
const MAX_PAYLOAD_BYTES = 8 * 1024 * 1024;
|
|
4904
|
+
const MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
|
|
4793
4905
|
const DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES = 6 * 1024 * 1024;
|
|
4794
4906
|
let maxChatHistoryMessagesBytes = DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES;
|
|
4795
4907
|
const getMaxChatHistoryMessagesBytes = () => maxChatHistoryMessagesBytes;
|
|
@@ -5241,6 +5353,7 @@ const BASE_METHODS = [
|
|
|
5241
5353
|
"wizard.next",
|
|
5242
5354
|
"wizard.cancel",
|
|
5243
5355
|
"wizard.status",
|
|
5356
|
+
"talk.config",
|
|
5244
5357
|
"talk.mode",
|
|
5245
5358
|
"models.list",
|
|
5246
5359
|
"agents.list",
|
|
@@ -5502,7 +5615,7 @@ function ensureAgentRunListener() {
|
|
|
5502
5615
|
agentRunStarts.delete(evt.runId);
|
|
5503
5616
|
recordAgentRunSnapshot({
|
|
5504
5617
|
runId: evt.runId,
|
|
5505
|
-
status: phase === "error" ? "error" : "ok",
|
|
5618
|
+
status: phase === "error" ? "error" : evt.data?.aborted ? "timeout" : "ok",
|
|
5506
5619
|
startedAt,
|
|
5507
5620
|
endedAt,
|
|
5508
5621
|
error,
|
|
@@ -5544,7 +5657,7 @@ async function waitForAgentJob(params) {
|
|
|
5544
5657
|
const error = typeof evt.data?.error === "string" ? evt.data.error : void 0;
|
|
5545
5658
|
const snapshot = {
|
|
5546
5659
|
runId: evt.runId,
|
|
5547
|
-
status: phase === "error" ? "error" : "ok",
|
|
5660
|
+
status: phase === "error" ? "error" : evt.data?.aborted ? "timeout" : "ok",
|
|
5548
5661
|
startedAt,
|
|
5549
5662
|
endedAt,
|
|
5550
5663
|
error,
|
|
@@ -5629,6 +5742,7 @@ const agentHandlers = {
|
|
|
5629
5742
|
let resolvedGroupChannel = groupChannelRaw || void 0;
|
|
5630
5743
|
let resolvedGroupSpace = groupSpaceRaw || void 0;
|
|
5631
5744
|
let spawnedByValue = typeof request.spawnedBy === "string" ? request.spawnedBy.trim() : void 0;
|
|
5745
|
+
const inputProvenance = normalizeInputProvenance(request.inputProvenance);
|
|
5632
5746
|
const cached = context.dedupe.get(`agent:${idem}`);
|
|
5633
5747
|
if (cached) {
|
|
5634
5748
|
respond(cached.ok, cached.payload, cached.error, { cached: true });
|
|
@@ -5765,7 +5879,10 @@ const agentHandlers = {
|
|
|
5765
5879
|
const runId = idem;
|
|
5766
5880
|
const connId = typeof client?.connId === "string" ? client.connId : void 0;
|
|
5767
5881
|
const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
|
|
5768
|
-
if (connId && wantsToolEvents)
|
|
5882
|
+
if (connId && wantsToolEvents) {
|
|
5883
|
+
context.registerToolEventRecipient(runId, connId);
|
|
5884
|
+
for (const [activeRunId, active] of context.chatAbortControllers) if (activeRunId !== runId && active.sessionKey === requestedSessionKey) context.registerToolEventRecipient(activeRunId, connId);
|
|
5885
|
+
}
|
|
5769
5886
|
const wantsDelivery = request.deliver === true;
|
|
5770
5887
|
const explicitTo = typeof request.replyTo === "string" && request.replyTo.trim() ? request.replyTo.trim() : typeof request.to === "string" && request.to.trim() ? request.to.trim() : void 0;
|
|
5771
5888
|
const explicitThreadId = typeof request.threadId === "string" && request.threadId.trim() ? request.threadId.trim() : void 0;
|
|
@@ -5832,7 +5949,8 @@ const agentHandlers = {
|
|
|
5832
5949
|
messageChannel: resolvedChannel,
|
|
5833
5950
|
runId,
|
|
5834
5951
|
lane: request.lane,
|
|
5835
|
-
extraSystemPrompt: request.extraSystemPrompt
|
|
5952
|
+
extraSystemPrompt: request.extraSystemPrompt,
|
|
5953
|
+
inputProvenance
|
|
5836
5954
|
}, defaultRuntime, context.deps).then((result) => {
|
|
5837
5955
|
const payload = {
|
|
5838
5956
|
runId,
|
|
@@ -6246,143 +6364,6 @@ const agentsHandlers = {
|
|
|
6246
6364
|
}
|
|
6247
6365
|
};
|
|
6248
6366
|
|
|
6249
|
-
//#endregion
|
|
6250
|
-
//#region src/gateway/node-command-policy.ts
|
|
6251
|
-
const CANVAS_COMMANDS = [
|
|
6252
|
-
"canvas.present",
|
|
6253
|
-
"canvas.hide",
|
|
6254
|
-
"canvas.navigate",
|
|
6255
|
-
"canvas.eval",
|
|
6256
|
-
"canvas.snapshot",
|
|
6257
|
-
"canvas.a2ui.push",
|
|
6258
|
-
"canvas.a2ui.pushJSONL",
|
|
6259
|
-
"canvas.a2ui.reset"
|
|
6260
|
-
];
|
|
6261
|
-
const CAMERA_COMMANDS = ["camera.list"];
|
|
6262
|
-
const CAMERA_DANGEROUS_COMMANDS = ["camera.snap", "camera.clip"];
|
|
6263
|
-
const SCREEN_DANGEROUS_COMMANDS = ["screen.record"];
|
|
6264
|
-
const LOCATION_COMMANDS = ["location.get"];
|
|
6265
|
-
const DEVICE_COMMANDS = ["device.info", "device.status"];
|
|
6266
|
-
const CONTACTS_COMMANDS = ["contacts.search"];
|
|
6267
|
-
const CONTACTS_DANGEROUS_COMMANDS = ["contacts.add"];
|
|
6268
|
-
const CALENDAR_COMMANDS = ["calendar.events"];
|
|
6269
|
-
const CALENDAR_DANGEROUS_COMMANDS = ["calendar.add"];
|
|
6270
|
-
const REMINDERS_COMMANDS = ["reminders.list"];
|
|
6271
|
-
const REMINDERS_DANGEROUS_COMMANDS = ["reminders.add"];
|
|
6272
|
-
const PHOTOS_COMMANDS = ["photos.latest"];
|
|
6273
|
-
const MOTION_COMMANDS = ["motion.activity", "motion.pedometer"];
|
|
6274
|
-
const SMS_DANGEROUS_COMMANDS = ["sms.send"];
|
|
6275
|
-
const IOS_SYSTEM_COMMANDS = ["system.notify"];
|
|
6276
|
-
const SYSTEM_COMMANDS = [
|
|
6277
|
-
"system.run",
|
|
6278
|
-
"system.which",
|
|
6279
|
-
"system.notify",
|
|
6280
|
-
"system.execApprovals.get",
|
|
6281
|
-
"system.execApprovals.set",
|
|
6282
|
-
"browser.proxy"
|
|
6283
|
-
];
|
|
6284
|
-
const DEFAULT_DANGEROUS_NODE_COMMANDS = [
|
|
6285
|
-
...CAMERA_DANGEROUS_COMMANDS,
|
|
6286
|
-
...SCREEN_DANGEROUS_COMMANDS,
|
|
6287
|
-
...CONTACTS_DANGEROUS_COMMANDS,
|
|
6288
|
-
...CALENDAR_DANGEROUS_COMMANDS,
|
|
6289
|
-
...REMINDERS_DANGEROUS_COMMANDS,
|
|
6290
|
-
...SMS_DANGEROUS_COMMANDS
|
|
6291
|
-
];
|
|
6292
|
-
const PLATFORM_DEFAULTS = {
|
|
6293
|
-
ios: [
|
|
6294
|
-
...CANVAS_COMMANDS,
|
|
6295
|
-
...CAMERA_COMMANDS,
|
|
6296
|
-
...LOCATION_COMMANDS,
|
|
6297
|
-
...DEVICE_COMMANDS,
|
|
6298
|
-
...CONTACTS_COMMANDS,
|
|
6299
|
-
...CALENDAR_COMMANDS,
|
|
6300
|
-
...REMINDERS_COMMANDS,
|
|
6301
|
-
...PHOTOS_COMMANDS,
|
|
6302
|
-
...MOTION_COMMANDS,
|
|
6303
|
-
...IOS_SYSTEM_COMMANDS
|
|
6304
|
-
],
|
|
6305
|
-
android: [
|
|
6306
|
-
...CANVAS_COMMANDS,
|
|
6307
|
-
...CAMERA_COMMANDS,
|
|
6308
|
-
...LOCATION_COMMANDS,
|
|
6309
|
-
...DEVICE_COMMANDS,
|
|
6310
|
-
...CONTACTS_COMMANDS,
|
|
6311
|
-
...CALENDAR_COMMANDS,
|
|
6312
|
-
...REMINDERS_COMMANDS,
|
|
6313
|
-
...PHOTOS_COMMANDS,
|
|
6314
|
-
...MOTION_COMMANDS
|
|
6315
|
-
],
|
|
6316
|
-
macos: [
|
|
6317
|
-
...CANVAS_COMMANDS,
|
|
6318
|
-
...CAMERA_COMMANDS,
|
|
6319
|
-
...LOCATION_COMMANDS,
|
|
6320
|
-
...DEVICE_COMMANDS,
|
|
6321
|
-
...CONTACTS_COMMANDS,
|
|
6322
|
-
...CALENDAR_COMMANDS,
|
|
6323
|
-
...REMINDERS_COMMANDS,
|
|
6324
|
-
...PHOTOS_COMMANDS,
|
|
6325
|
-
...MOTION_COMMANDS,
|
|
6326
|
-
...SYSTEM_COMMANDS
|
|
6327
|
-
],
|
|
6328
|
-
linux: [...SYSTEM_COMMANDS],
|
|
6329
|
-
windows: [...SYSTEM_COMMANDS],
|
|
6330
|
-
unknown: [
|
|
6331
|
-
...CANVAS_COMMANDS,
|
|
6332
|
-
...CAMERA_COMMANDS,
|
|
6333
|
-
...LOCATION_COMMANDS,
|
|
6334
|
-
...SYSTEM_COMMANDS
|
|
6335
|
-
]
|
|
6336
|
-
};
|
|
6337
|
-
function normalizePlatformId(platform, deviceFamily) {
|
|
6338
|
-
const raw = (platform ?? "").trim().toLowerCase();
|
|
6339
|
-
if (raw.startsWith("ios")) return "ios";
|
|
6340
|
-
if (raw.startsWith("android")) return "android";
|
|
6341
|
-
if (raw.startsWith("mac")) return "macos";
|
|
6342
|
-
if (raw.startsWith("darwin")) return "macos";
|
|
6343
|
-
if (raw.startsWith("win")) return "windows";
|
|
6344
|
-
if (raw.startsWith("linux")) return "linux";
|
|
6345
|
-
const family = (deviceFamily ?? "").trim().toLowerCase();
|
|
6346
|
-
if (family.includes("iphone") || family.includes("ipad") || family.includes("ios")) return "ios";
|
|
6347
|
-
if (family.includes("android")) return "android";
|
|
6348
|
-
if (family.includes("mac")) return "macos";
|
|
6349
|
-
if (family.includes("windows")) return "windows";
|
|
6350
|
-
if (family.includes("linux")) return "linux";
|
|
6351
|
-
return "unknown";
|
|
6352
|
-
}
|
|
6353
|
-
function resolveNodeCommandAllowlist(cfg, node) {
|
|
6354
|
-
const base = PLATFORM_DEFAULTS[normalizePlatformId(node?.platform, node?.deviceFamily)] ?? PLATFORM_DEFAULTS.unknown;
|
|
6355
|
-
const extra = cfg.gateway?.nodes?.allowCommands ?? [];
|
|
6356
|
-
const deny = new Set(cfg.gateway?.nodes?.denyCommands ?? []);
|
|
6357
|
-
const allow = new Set([...base, ...extra].map((cmd) => cmd.trim()).filter(Boolean));
|
|
6358
|
-
for (const blocked of deny) {
|
|
6359
|
-
const trimmed = blocked.trim();
|
|
6360
|
-
if (trimmed) allow.delete(trimmed);
|
|
6361
|
-
}
|
|
6362
|
-
return allow;
|
|
6363
|
-
}
|
|
6364
|
-
function isNodeCommandAllowed(params) {
|
|
6365
|
-
const command = params.command.trim();
|
|
6366
|
-
if (!command) return {
|
|
6367
|
-
ok: false,
|
|
6368
|
-
reason: "command required"
|
|
6369
|
-
};
|
|
6370
|
-
if (!params.allowlist.has(command)) return {
|
|
6371
|
-
ok: false,
|
|
6372
|
-
reason: "command not allowlisted"
|
|
6373
|
-
};
|
|
6374
|
-
if (Array.isArray(params.declaredCommands) && params.declaredCommands.length > 0) {
|
|
6375
|
-
if (!params.declaredCommands.includes(command)) return {
|
|
6376
|
-
ok: false,
|
|
6377
|
-
reason: "command not declared by node"
|
|
6378
|
-
};
|
|
6379
|
-
} else return {
|
|
6380
|
-
ok: false,
|
|
6381
|
-
reason: "node did not declare commands"
|
|
6382
|
-
};
|
|
6383
|
-
return { ok: true };
|
|
6384
|
-
}
|
|
6385
|
-
|
|
6386
6367
|
//#endregion
|
|
6387
6368
|
//#region src/gateway/server-methods/nodes.helpers.ts
|
|
6388
6369
|
function respondInvalidParams(params) {
|
|
@@ -6753,9 +6734,13 @@ const channelsHandlers = {
|
|
|
6753
6734
|
//#region src/gateway/server-methods/chat.ts
|
|
6754
6735
|
function resolveTranscriptPath(params) {
|
|
6755
6736
|
const { sessionId, storePath, sessionFile } = params;
|
|
6756
|
-
if (sessionFile) return
|
|
6757
|
-
|
|
6758
|
-
|
|
6737
|
+
if (!storePath && !sessionFile) return null;
|
|
6738
|
+
try {
|
|
6739
|
+
const sessionsDir = storePath ? path.dirname(storePath) : void 0;
|
|
6740
|
+
return resolveSessionFilePath(sessionId, sessionFile ? { sessionFile } : void 0, sessionsDir ? { sessionsDir } : void 0);
|
|
6741
|
+
} catch {
|
|
6742
|
+
return null;
|
|
6743
|
+
}
|
|
6759
6744
|
}
|
|
6760
6745
|
function ensureTranscriptFile(params) {
|
|
6761
6746
|
if (fs.existsSync(params.transcriptPath)) return { ok: true };
|
|
@@ -7119,7 +7104,10 @@ const chatHandlers = {
|
|
|
7119
7104
|
agentRunStarted = true;
|
|
7120
7105
|
const connId = typeof client?.connId === "string" ? client.connId : void 0;
|
|
7121
7106
|
const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
|
|
7122
|
-
if (connId && wantsToolEvents)
|
|
7107
|
+
if (connId && wantsToolEvents) {
|
|
7108
|
+
context.registerToolEventRecipient(runId, connId);
|
|
7109
|
+
for (const [activeRunId, active] of context.chatAbortControllers) if (activeRunId !== runId && active.sessionKey === p.sessionKey) context.registerToolEventRecipient(activeRunId, connId);
|
|
7110
|
+
}
|
|
7123
7111
|
},
|
|
7124
7112
|
onModelSelected
|
|
7125
7113
|
}
|
|
@@ -7252,491 +7240,30 @@ const chatHandlers = {
|
|
|
7252
7240
|
};
|
|
7253
7241
|
|
|
7254
7242
|
//#endregion
|
|
7255
|
-
//#region src/config/
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
const baseValue = result[key];
|
|
7266
|
-
result[key] = applyMergePatch(isPlainObject(baseValue) ? baseValue : {}, value);
|
|
7267
|
-
continue;
|
|
7268
|
-
}
|
|
7269
|
-
result[key] = value;
|
|
7270
|
-
}
|
|
7271
|
-
return result;
|
|
7272
|
-
}
|
|
7273
|
-
|
|
7274
|
-
//#endregion
|
|
7275
|
-
//#region src/config/redact-snapshot.ts
|
|
7276
|
-
/**
|
|
7277
|
-
* Sentinel value used to replace sensitive config fields in gateway responses.
|
|
7278
|
-
* Write-side handlers (config.set, config.apply, config.patch) detect this
|
|
7279
|
-
* sentinel and restore the original value from the on-disk config, so a
|
|
7280
|
-
* round-trip through the Web UI does not corrupt credentials.
|
|
7281
|
-
*/
|
|
7282
|
-
const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
|
|
7283
|
-
/**
|
|
7284
|
-
* Patterns that identify sensitive config field names.
|
|
7285
|
-
* Aligned with the UI-hint logic in schema.ts.
|
|
7286
|
-
*/
|
|
7287
|
-
const SENSITIVE_KEY_PATTERNS = [
|
|
7288
|
-
/token/i,
|
|
7289
|
-
/password/i,
|
|
7290
|
-
/secret/i,
|
|
7291
|
-
/api.?key/i
|
|
7292
|
-
];
|
|
7293
|
-
function isSensitiveKey(key) {
|
|
7294
|
-
return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(key));
|
|
7295
|
-
}
|
|
7296
|
-
/**
|
|
7297
|
-
* Deep-walk an object and replace values whose key matches a sensitive pattern
|
|
7298
|
-
* with the redaction sentinel.
|
|
7299
|
-
*/
|
|
7300
|
-
function redactObject(obj) {
|
|
7301
|
-
if (obj === null || obj === void 0) return obj;
|
|
7302
|
-
if (typeof obj !== "object") return obj;
|
|
7303
|
-
if (Array.isArray(obj)) return obj.map(redactObject);
|
|
7304
|
-
const result = {};
|
|
7305
|
-
for (const [key, value] of Object.entries(obj)) if (isSensitiveKey(key) && value !== null && value !== void 0) result[key] = REDACTED_SENTINEL;
|
|
7306
|
-
else if (typeof value === "object" && value !== null) result[key] = redactObject(value);
|
|
7307
|
-
else result[key] = value;
|
|
7308
|
-
return result;
|
|
7309
|
-
}
|
|
7310
|
-
function redactConfigObject(value) {
|
|
7311
|
-
return redactObject(value);
|
|
7312
|
-
}
|
|
7313
|
-
/**
|
|
7314
|
-
* Collect all sensitive string values from a config object.
|
|
7315
|
-
* Used for text-based redaction of the raw JSON5 source.
|
|
7316
|
-
*/
|
|
7317
|
-
function collectSensitiveValues(obj) {
|
|
7318
|
-
const values = [];
|
|
7319
|
-
if (obj === null || obj === void 0 || typeof obj !== "object") return values;
|
|
7320
|
-
if (Array.isArray(obj)) {
|
|
7321
|
-
for (const item of obj) values.push(...collectSensitiveValues(item));
|
|
7322
|
-
return values;
|
|
7323
|
-
}
|
|
7324
|
-
for (const [key, value] of Object.entries(obj)) if (isSensitiveKey(key) && typeof value === "string" && value.length > 0) values.push(value);
|
|
7325
|
-
else if (typeof value === "object" && value !== null) values.push(...collectSensitiveValues(value));
|
|
7326
|
-
return values;
|
|
7327
|
-
}
|
|
7328
|
-
/**
|
|
7329
|
-
* Replace known sensitive values in a raw JSON5 string with the sentinel.
|
|
7330
|
-
* Values are replaced longest-first to avoid partial matches.
|
|
7331
|
-
*/
|
|
7332
|
-
function redactRawText(raw, config) {
|
|
7333
|
-
const sensitiveValues = collectSensitiveValues(config);
|
|
7334
|
-
sensitiveValues.sort((a, b) => b.length - a.length);
|
|
7335
|
-
let result = raw;
|
|
7336
|
-
for (const value of sensitiveValues) {
|
|
7337
|
-
const escaped = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7338
|
-
result = result.replace(new RegExp(escaped, "g"), REDACTED_SENTINEL);
|
|
7339
|
-
}
|
|
7340
|
-
result = result.replace(/(^|[{\s,])((["'])([^"']+)\3|([A-Za-z0-9_$.-]+))(\s*:\s*)(["'])([^"']*)\7/g, (match, prefix, keyExpr, _keyQuote, keyQuoted, keyBare, sep, valQuote, val) => {
|
|
7341
|
-
const key = keyQuoted ?? keyBare;
|
|
7342
|
-
if (!key || !isSensitiveKey(key)) return match;
|
|
7343
|
-
if (val === REDACTED_SENTINEL) return match;
|
|
7344
|
-
return `${prefix}${keyExpr}${sep}${valQuote}${REDACTED_SENTINEL}${valQuote}`;
|
|
7345
|
-
});
|
|
7346
|
-
return result;
|
|
7347
|
-
}
|
|
7348
|
-
/**
|
|
7349
|
-
* Returns a copy of the config snapshot with all sensitive fields
|
|
7350
|
-
* replaced by {@link REDACTED_SENTINEL}. The `hash` is preserved
|
|
7351
|
-
* (it tracks config identity, not content).
|
|
7352
|
-
*
|
|
7353
|
-
* Both `config` (the parsed object) and `raw` (the JSON5 source) are scrubbed
|
|
7354
|
-
* so no credential can leak through either path.
|
|
7355
|
-
*/
|
|
7356
|
-
function redactConfigSnapshot(snapshot) {
|
|
7357
|
-
const redactedConfig = redactConfigObject(snapshot.config);
|
|
7358
|
-
const redactedRaw = snapshot.raw ? redactRawText(snapshot.raw, snapshot.config) : null;
|
|
7359
|
-
const redactedParsed = snapshot.parsed ? redactConfigObject(snapshot.parsed) : snapshot.parsed;
|
|
7360
|
-
return {
|
|
7361
|
-
...snapshot,
|
|
7362
|
-
config: redactedConfig,
|
|
7363
|
-
raw: redactedRaw,
|
|
7364
|
-
parsed: redactedParsed
|
|
7365
|
-
};
|
|
7366
|
-
}
|
|
7367
|
-
/**
|
|
7368
|
-
* Deep-walk `incoming` and replace any {@link REDACTED_SENTINEL} values
|
|
7369
|
-
* (on sensitive keys) with the corresponding value from `original`.
|
|
7370
|
-
*
|
|
7371
|
-
* This is called by config.set / config.apply / config.patch before writing,
|
|
7372
|
-
* so that credentials survive a Web UI round-trip unmodified.
|
|
7373
|
-
*/
|
|
7374
|
-
function restoreRedactedValues(incoming, original) {
|
|
7375
|
-
if (incoming === null || incoming === void 0) return incoming;
|
|
7376
|
-
if (typeof incoming !== "object") return incoming;
|
|
7377
|
-
if (Array.isArray(incoming)) {
|
|
7378
|
-
const origArr = Array.isArray(original) ? original : [];
|
|
7379
|
-
return incoming.map((item, i) => restoreRedactedValues(item, origArr[i]));
|
|
7380
|
-
}
|
|
7381
|
-
const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
|
|
7382
|
-
const result = {};
|
|
7383
|
-
for (const [key, value] of Object.entries(incoming)) if (isSensitiveKey(key) && value === REDACTED_SENTINEL) {
|
|
7384
|
-
if (!(key in orig)) throw new Error(`config write rejected: "${key}" is redacted; set an explicit value instead of ${REDACTED_SENTINEL}`);
|
|
7385
|
-
result[key] = orig[key];
|
|
7386
|
-
} else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValues(value, orig[key]);
|
|
7387
|
-
else result[key] = value;
|
|
7388
|
-
return result;
|
|
7389
|
-
}
|
|
7390
|
-
|
|
7391
|
-
//#endregion
|
|
7392
|
-
//#region src/config/schema.ts
|
|
7393
|
-
const GROUP_LABELS = {
|
|
7394
|
-
wizard: "Wizard",
|
|
7395
|
-
update: "Update",
|
|
7396
|
-
diagnostics: "Diagnostics",
|
|
7397
|
-
logging: "Logging",
|
|
7398
|
-
gateway: "Gateway",
|
|
7399
|
-
nodeHost: "Node Host",
|
|
7400
|
-
agents: "Agents",
|
|
7401
|
-
tools: "Tools",
|
|
7402
|
-
bindings: "Bindings",
|
|
7403
|
-
audio: "Audio",
|
|
7404
|
-
models: "Models",
|
|
7405
|
-
messages: "Messages",
|
|
7406
|
-
commands: "Commands",
|
|
7407
|
-
session: "Session",
|
|
7408
|
-
cron: "Cron",
|
|
7409
|
-
hooks: "Hooks",
|
|
7410
|
-
ui: "UI",
|
|
7411
|
-
browser: "Browser",
|
|
7412
|
-
talk: "Talk",
|
|
7413
|
-
channels: "Messaging Channels",
|
|
7414
|
-
skills: "Skills",
|
|
7415
|
-
plugins: "Plugins",
|
|
7416
|
-
discovery: "Discovery",
|
|
7417
|
-
presence: "Presence",
|
|
7418
|
-
voicewake: "Voice Wake"
|
|
7243
|
+
//#region src/config/schema.irc.ts
|
|
7244
|
+
const IRC_FIELD_LABELS = {
|
|
7245
|
+
"channels.irc": "IRC",
|
|
7246
|
+
"channels.irc.dmPolicy": "IRC DM Policy",
|
|
7247
|
+
"channels.irc.nickserv.enabled": "IRC NickServ Enabled",
|
|
7248
|
+
"channels.irc.nickserv.service": "IRC NickServ Service",
|
|
7249
|
+
"channels.irc.nickserv.password": "IRC NickServ Password",
|
|
7250
|
+
"channels.irc.nickserv.passwordFile": "IRC NickServ Password File",
|
|
7251
|
+
"channels.irc.nickserv.register": "IRC NickServ Register",
|
|
7252
|
+
"channels.irc.nickserv.registerEmail": "IRC NickServ Register Email"
|
|
7419
7253
|
};
|
|
7420
|
-
const
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
audio: 60,
|
|
7430
|
-
models: 70,
|
|
7431
|
-
messages: 80,
|
|
7432
|
-
commands: 85,
|
|
7433
|
-
session: 90,
|
|
7434
|
-
cron: 100,
|
|
7435
|
-
hooks: 110,
|
|
7436
|
-
ui: 120,
|
|
7437
|
-
browser: 130,
|
|
7438
|
-
talk: 140,
|
|
7439
|
-
channels: 150,
|
|
7440
|
-
skills: 200,
|
|
7441
|
-
plugins: 205,
|
|
7442
|
-
discovery: 210,
|
|
7443
|
-
presence: 220,
|
|
7444
|
-
voicewake: 230,
|
|
7445
|
-
logging: 900
|
|
7446
|
-
};
|
|
7447
|
-
const FIELD_LABELS = {
|
|
7448
|
-
"meta.lastTouchedVersion": "Config Last Touched Version",
|
|
7449
|
-
"meta.lastTouchedAt": "Config Last Touched At",
|
|
7450
|
-
"update.channel": "Update Channel",
|
|
7451
|
-
"update.checkOnStart": "Update Check on Start",
|
|
7452
|
-
"diagnostics.enabled": "Diagnostics Enabled",
|
|
7453
|
-
"diagnostics.flags": "Diagnostics Flags",
|
|
7454
|
-
"diagnostics.otel.enabled": "OpenTelemetry Enabled",
|
|
7455
|
-
"diagnostics.otel.endpoint": "OpenTelemetry Endpoint",
|
|
7456
|
-
"diagnostics.otel.protocol": "OpenTelemetry Protocol",
|
|
7457
|
-
"diagnostics.otel.headers": "OpenTelemetry Headers",
|
|
7458
|
-
"diagnostics.otel.serviceName": "OpenTelemetry Service Name",
|
|
7459
|
-
"diagnostics.otel.traces": "OpenTelemetry Traces Enabled",
|
|
7460
|
-
"diagnostics.otel.metrics": "OpenTelemetry Metrics Enabled",
|
|
7461
|
-
"diagnostics.otel.logs": "OpenTelemetry Logs Enabled",
|
|
7462
|
-
"diagnostics.otel.sampleRate": "OpenTelemetry Trace Sample Rate",
|
|
7463
|
-
"diagnostics.otel.flushIntervalMs": "OpenTelemetry Flush Interval (ms)",
|
|
7464
|
-
"diagnostics.cacheTrace.enabled": "Cache Trace Enabled",
|
|
7465
|
-
"diagnostics.cacheTrace.filePath": "Cache Trace File Path",
|
|
7466
|
-
"diagnostics.cacheTrace.includeMessages": "Cache Trace Include Messages",
|
|
7467
|
-
"diagnostics.cacheTrace.includePrompt": "Cache Trace Include Prompt",
|
|
7468
|
-
"diagnostics.cacheTrace.includeSystem": "Cache Trace Include System",
|
|
7469
|
-
"agents.list.*.identity.avatar": "Identity Avatar",
|
|
7470
|
-
"agents.list.*.skills": "Agent Skill Filter",
|
|
7471
|
-
"gateway.remote.url": "Remote Gateway URL",
|
|
7472
|
-
"gateway.remote.sshTarget": "Remote Gateway SSH Target",
|
|
7473
|
-
"gateway.remote.sshIdentity": "Remote Gateway SSH Identity",
|
|
7474
|
-
"gateway.remote.token": "Remote Gateway Token",
|
|
7475
|
-
"gateway.remote.password": "Remote Gateway Password",
|
|
7476
|
-
"gateway.remote.tlsFingerprint": "Remote Gateway TLS Fingerprint",
|
|
7477
|
-
"gateway.auth.token": "Gateway Token",
|
|
7478
|
-
"gateway.auth.password": "Gateway Password",
|
|
7479
|
-
"tools.media.image.enabled": "Enable Image Understanding",
|
|
7480
|
-
"tools.media.image.maxBytes": "Image Understanding Max Bytes",
|
|
7481
|
-
"tools.media.image.maxChars": "Image Understanding Max Chars",
|
|
7482
|
-
"tools.media.image.prompt": "Image Understanding Prompt",
|
|
7483
|
-
"tools.media.image.timeoutSeconds": "Image Understanding Timeout (sec)",
|
|
7484
|
-
"tools.media.image.attachments": "Image Understanding Attachment Policy",
|
|
7485
|
-
"tools.media.image.models": "Image Understanding Models",
|
|
7486
|
-
"tools.media.image.scope": "Image Understanding Scope",
|
|
7487
|
-
"tools.media.models": "Media Understanding Shared Models",
|
|
7488
|
-
"tools.media.concurrency": "Media Understanding Concurrency",
|
|
7489
|
-
"tools.media.audio.enabled": "Enable Audio Understanding",
|
|
7490
|
-
"tools.media.audio.maxBytes": "Audio Understanding Max Bytes",
|
|
7491
|
-
"tools.media.audio.maxChars": "Audio Understanding Max Chars",
|
|
7492
|
-
"tools.media.audio.prompt": "Audio Understanding Prompt",
|
|
7493
|
-
"tools.media.audio.timeoutSeconds": "Audio Understanding Timeout (sec)",
|
|
7494
|
-
"tools.media.audio.language": "Audio Understanding Language",
|
|
7495
|
-
"tools.media.audio.attachments": "Audio Understanding Attachment Policy",
|
|
7496
|
-
"tools.media.audio.models": "Audio Understanding Models",
|
|
7497
|
-
"tools.media.audio.scope": "Audio Understanding Scope",
|
|
7498
|
-
"tools.media.video.enabled": "Enable Video Understanding",
|
|
7499
|
-
"tools.media.video.maxBytes": "Video Understanding Max Bytes",
|
|
7500
|
-
"tools.media.video.maxChars": "Video Understanding Max Chars",
|
|
7501
|
-
"tools.media.video.prompt": "Video Understanding Prompt",
|
|
7502
|
-
"tools.media.video.timeoutSeconds": "Video Understanding Timeout (sec)",
|
|
7503
|
-
"tools.media.video.attachments": "Video Understanding Attachment Policy",
|
|
7504
|
-
"tools.media.video.models": "Video Understanding Models",
|
|
7505
|
-
"tools.media.video.scope": "Video Understanding Scope",
|
|
7506
|
-
"tools.links.enabled": "Enable Link Understanding",
|
|
7507
|
-
"tools.links.maxLinks": "Link Understanding Max Links",
|
|
7508
|
-
"tools.links.timeoutSeconds": "Link Understanding Timeout (sec)",
|
|
7509
|
-
"tools.links.models": "Link Understanding Models",
|
|
7510
|
-
"tools.links.scope": "Link Understanding Scope",
|
|
7511
|
-
"tools.profile": "Tool Profile",
|
|
7512
|
-
"tools.alsoAllow": "Tool Allowlist Additions",
|
|
7513
|
-
"agents.list[].tools.profile": "Agent Tool Profile",
|
|
7514
|
-
"agents.list[].tools.alsoAllow": "Agent Tool Allowlist Additions",
|
|
7515
|
-
"tools.byProvider": "Tool Policy by Provider",
|
|
7516
|
-
"agents.list[].tools.byProvider": "Agent Tool Policy by Provider",
|
|
7517
|
-
"tools.exec.applyPatch.enabled": "Enable apply_patch",
|
|
7518
|
-
"tools.exec.applyPatch.allowModels": "apply_patch Model Allowlist",
|
|
7519
|
-
"tools.exec.notifyOnExit": "Exec Notify On Exit",
|
|
7520
|
-
"tools.exec.approvalRunningNoticeMs": "Exec Approval Running Notice (ms)",
|
|
7521
|
-
"tools.exec.host": "Exec Host",
|
|
7522
|
-
"tools.exec.security": "Exec Security",
|
|
7523
|
-
"tools.exec.ask": "Exec Ask",
|
|
7524
|
-
"tools.exec.node": "Exec Node Binding",
|
|
7525
|
-
"tools.exec.pathPrepend": "Exec PATH Prepend",
|
|
7526
|
-
"tools.exec.safeBins": "Exec Safe Bins",
|
|
7527
|
-
"tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
|
|
7528
|
-
"tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
|
|
7529
|
-
"tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
|
|
7530
|
-
"tools.message.crossContext.marker.enabled": "Cross-Context Marker",
|
|
7531
|
-
"tools.message.crossContext.marker.prefix": "Cross-Context Marker Prefix",
|
|
7532
|
-
"tools.message.crossContext.marker.suffix": "Cross-Context Marker Suffix",
|
|
7533
|
-
"tools.message.broadcast.enabled": "Enable Message Broadcast",
|
|
7534
|
-
"tools.web.search.enabled": "Enable Web Search Tool",
|
|
7535
|
-
"tools.web.search.provider": "Web Search Provider",
|
|
7536
|
-
"tools.web.search.apiKey": "Brave Search API Key",
|
|
7537
|
-
"tools.web.search.maxResults": "Web Search Max Results",
|
|
7538
|
-
"tools.web.search.timeoutSeconds": "Web Search Timeout (sec)",
|
|
7539
|
-
"tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)",
|
|
7540
|
-
"tools.web.fetch.enabled": "Enable Web Fetch Tool",
|
|
7541
|
-
"tools.web.fetch.maxChars": "Web Fetch Max Chars",
|
|
7542
|
-
"tools.web.fetch.timeoutSeconds": "Web Fetch Timeout (sec)",
|
|
7543
|
-
"tools.web.fetch.cacheTtlMinutes": "Web Fetch Cache TTL (min)",
|
|
7544
|
-
"tools.web.fetch.maxRedirects": "Web Fetch Max Redirects",
|
|
7545
|
-
"tools.web.fetch.userAgent": "Web Fetch User-Agent",
|
|
7546
|
-
"gateway.controlUi.basePath": "Control UI Base Path",
|
|
7547
|
-
"gateway.controlUi.root": "Control UI Assets Root",
|
|
7548
|
-
"gateway.controlUi.allowedOrigins": "Control UI Allowed Origins",
|
|
7549
|
-
"gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth",
|
|
7550
|
-
"gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth",
|
|
7551
|
-
"gateway.http.endpoints.chatCompletions.enabled": "OpenAI Chat Completions Endpoint",
|
|
7552
|
-
"gateway.reload.mode": "Config Reload Mode",
|
|
7553
|
-
"gateway.reload.debounceMs": "Config Reload Debounce (ms)",
|
|
7554
|
-
"gateway.nodes.browser.mode": "Gateway Node Browser Mode",
|
|
7555
|
-
"gateway.nodes.browser.node": "Gateway Node Browser Pin",
|
|
7556
|
-
"gateway.nodes.allowCommands": "Gateway Node Allowlist (Extra Commands)",
|
|
7557
|
-
"gateway.nodes.denyCommands": "Gateway Node Denylist",
|
|
7558
|
-
"nodeHost.browserProxy.enabled": "Node Browser Proxy Enabled",
|
|
7559
|
-
"nodeHost.browserProxy.allowProfiles": "Node Browser Proxy Allowed Profiles",
|
|
7560
|
-
"skills.load.watch": "Watch Skills",
|
|
7561
|
-
"skills.load.watchDebounceMs": "Skills Watch Debounce (ms)",
|
|
7562
|
-
"agents.defaults.workspace": "Workspace",
|
|
7563
|
-
"agents.defaults.repoRoot": "Repo Root",
|
|
7564
|
-
"agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars",
|
|
7565
|
-
"agents.defaults.envelopeTimezone": "Envelope Timezone",
|
|
7566
|
-
"agents.defaults.envelopeTimestamp": "Envelope Timestamp",
|
|
7567
|
-
"agents.defaults.envelopeElapsed": "Envelope Elapsed",
|
|
7568
|
-
"agents.defaults.memorySearch": "Memory Search",
|
|
7569
|
-
"agents.defaults.memorySearch.enabled": "Enable Memory Search",
|
|
7570
|
-
"agents.defaults.memorySearch.sources": "Memory Search Sources",
|
|
7571
|
-
"agents.defaults.memorySearch.extraPaths": "Extra Memory Paths",
|
|
7572
|
-
"agents.defaults.memorySearch.experimental.sessionMemory": "Memory Search Session Index (Experimental)",
|
|
7573
|
-
"agents.defaults.memorySearch.provider": "Memory Search Provider",
|
|
7574
|
-
"agents.defaults.memorySearch.remote.baseUrl": "Remote Embedding Base URL",
|
|
7575
|
-
"agents.defaults.memorySearch.remote.apiKey": "Remote Embedding API Key",
|
|
7576
|
-
"agents.defaults.memorySearch.remote.headers": "Remote Embedding Headers",
|
|
7577
|
-
"agents.defaults.memorySearch.remote.batch.concurrency": "Remote Batch Concurrency",
|
|
7578
|
-
"agents.defaults.memorySearch.model": "Memory Search Model",
|
|
7579
|
-
"agents.defaults.memorySearch.fallback": "Memory Search Fallback",
|
|
7580
|
-
"agents.defaults.memorySearch.local.modelPath": "Local Embedding Model Path",
|
|
7581
|
-
"agents.defaults.memorySearch.store.path": "Memory Search Index Path",
|
|
7582
|
-
"agents.defaults.memorySearch.store.vector.enabled": "Memory Search Vector Index",
|
|
7583
|
-
"agents.defaults.memorySearch.store.vector.extensionPath": "Memory Search Vector Extension Path",
|
|
7584
|
-
"agents.defaults.memorySearch.chunking.tokens": "Memory Chunk Tokens",
|
|
7585
|
-
"agents.defaults.memorySearch.chunking.overlap": "Memory Chunk Overlap Tokens",
|
|
7586
|
-
"agents.defaults.memorySearch.sync.onSessionStart": "Index on Session Start",
|
|
7587
|
-
"agents.defaults.memorySearch.sync.onSearch": "Index on Search (Lazy)",
|
|
7588
|
-
"agents.defaults.memorySearch.sync.watch": "Watch Memory Files",
|
|
7589
|
-
"agents.defaults.memorySearch.sync.watchDebounceMs": "Memory Watch Debounce (ms)",
|
|
7590
|
-
"agents.defaults.memorySearch.sync.sessions.deltaBytes": "Session Delta Bytes",
|
|
7591
|
-
"agents.defaults.memorySearch.sync.sessions.deltaMessages": "Session Delta Messages",
|
|
7592
|
-
"agents.defaults.memorySearch.query.maxResults": "Memory Search Max Results",
|
|
7593
|
-
"agents.defaults.memorySearch.query.minScore": "Memory Search Min Score",
|
|
7594
|
-
"agents.defaults.memorySearch.query.hybrid.enabled": "Memory Search Hybrid",
|
|
7595
|
-
"agents.defaults.memorySearch.query.hybrid.vectorWeight": "Memory Search Vector Weight",
|
|
7596
|
-
"agents.defaults.memorySearch.query.hybrid.textWeight": "Memory Search Text Weight",
|
|
7597
|
-
"agents.defaults.memorySearch.query.hybrid.candidateMultiplier": "Memory Search Hybrid Candidate Multiplier",
|
|
7598
|
-
"agents.defaults.memorySearch.cache.enabled": "Memory Search Embedding Cache",
|
|
7599
|
-
"agents.defaults.memorySearch.cache.maxEntries": "Memory Search Embedding Cache Max Entries",
|
|
7600
|
-
memory: "Memory",
|
|
7601
|
-
"memory.backend": "Memory Backend",
|
|
7602
|
-
"memory.citations": "Memory Citations Mode",
|
|
7603
|
-
"memory.qmd.command": "QMD Binary",
|
|
7604
|
-
"memory.qmd.includeDefaultMemory": "QMD Include Default Memory",
|
|
7605
|
-
"memory.qmd.paths": "QMD Extra Paths",
|
|
7606
|
-
"memory.qmd.paths.path": "QMD Path",
|
|
7607
|
-
"memory.qmd.paths.pattern": "QMD Path Pattern",
|
|
7608
|
-
"memory.qmd.paths.name": "QMD Path Name",
|
|
7609
|
-
"memory.qmd.sessions.enabled": "QMD Session Indexing",
|
|
7610
|
-
"memory.qmd.sessions.exportDir": "QMD Session Export Directory",
|
|
7611
|
-
"memory.qmd.sessions.retentionDays": "QMD Session Retention (days)",
|
|
7612
|
-
"memory.qmd.update.interval": "QMD Update Interval",
|
|
7613
|
-
"memory.qmd.update.debounceMs": "QMD Update Debounce (ms)",
|
|
7614
|
-
"memory.qmd.update.onBoot": "QMD Update on Startup",
|
|
7615
|
-
"memory.qmd.update.waitForBootSync": "QMD Wait for Boot Sync",
|
|
7616
|
-
"memory.qmd.update.embedInterval": "QMD Embed Interval",
|
|
7617
|
-
"memory.qmd.update.commandTimeoutMs": "QMD Command Timeout (ms)",
|
|
7618
|
-
"memory.qmd.update.updateTimeoutMs": "QMD Update Timeout (ms)",
|
|
7619
|
-
"memory.qmd.update.embedTimeoutMs": "QMD Embed Timeout (ms)",
|
|
7620
|
-
"memory.qmd.limits.maxResults": "QMD Max Results",
|
|
7621
|
-
"memory.qmd.limits.maxSnippetChars": "QMD Max Snippet Chars",
|
|
7622
|
-
"memory.qmd.limits.maxInjectedChars": "QMD Max Injected Chars",
|
|
7623
|
-
"memory.qmd.limits.timeoutMs": "QMD Search Timeout (ms)",
|
|
7624
|
-
"memory.qmd.scope": "QMD Surface Scope",
|
|
7625
|
-
"auth.profiles": "Auth Profiles",
|
|
7626
|
-
"auth.order": "Auth Profile Order",
|
|
7627
|
-
"auth.cooldowns.billingBackoffHours": "Billing Backoff (hours)",
|
|
7628
|
-
"auth.cooldowns.billingBackoffHoursByProvider": "Billing Backoff Overrides",
|
|
7629
|
-
"auth.cooldowns.billingMaxHours": "Billing Backoff Cap (hours)",
|
|
7630
|
-
"auth.cooldowns.failureWindowHours": "Failover Window (hours)",
|
|
7631
|
-
"agents.defaults.models": "Models",
|
|
7632
|
-
"agents.defaults.model.primary": "Primary Model",
|
|
7633
|
-
"agents.defaults.model.fallbacks": "Model Fallbacks",
|
|
7634
|
-
"agents.defaults.imageModel.primary": "Image Model",
|
|
7635
|
-
"agents.defaults.imageModel.fallbacks": "Image Model Fallbacks",
|
|
7636
|
-
"agents.defaults.humanDelay.mode": "Human Delay Mode",
|
|
7637
|
-
"agents.defaults.humanDelay.minMs": "Human Delay Min (ms)",
|
|
7638
|
-
"agents.defaults.humanDelay.maxMs": "Human Delay Max (ms)",
|
|
7639
|
-
"agents.defaults.cliBackends": "CLI Backends",
|
|
7640
|
-
"commands.native": "Native Commands",
|
|
7641
|
-
"commands.nativeSkills": "Native Skill Commands",
|
|
7642
|
-
"commands.text": "Text Commands",
|
|
7643
|
-
"commands.bash": "Allow Bash Chat Command",
|
|
7644
|
-
"commands.bashForegroundMs": "Bash Foreground Window (ms)",
|
|
7645
|
-
"commands.config": "Allow /config",
|
|
7646
|
-
"commands.debug": "Allow /debug",
|
|
7647
|
-
"commands.restart": "Allow Restart",
|
|
7648
|
-
"commands.useAccessGroups": "Use Access Groups",
|
|
7649
|
-
"commands.ownerAllowFrom": "Command Owners",
|
|
7650
|
-
"commands.allowFrom": "Command Access Allowlist",
|
|
7651
|
-
"ui.seamColor": "Accent Color",
|
|
7652
|
-
"ui.assistant.name": "Assistant Name",
|
|
7653
|
-
"ui.assistant.avatar": "Assistant Avatar",
|
|
7654
|
-
"browser.evaluateEnabled": "Browser Evaluate Enabled",
|
|
7655
|
-
"browser.snapshotDefaults": "Browser Snapshot Defaults",
|
|
7656
|
-
"browser.snapshotDefaults.mode": "Browser Snapshot Mode",
|
|
7657
|
-
"browser.remoteCdpTimeoutMs": "Remote CDP Timeout (ms)",
|
|
7658
|
-
"browser.remoteCdpHandshakeTimeoutMs": "Remote CDP Handshake Timeout (ms)",
|
|
7659
|
-
"session.dmScope": "DM Session Scope",
|
|
7660
|
-
"session.agentToAgent.maxPingPongTurns": "Agent-to-Agent Ping-Pong Turns",
|
|
7661
|
-
"messages.ackReaction": "Ack Reaction Emoji",
|
|
7662
|
-
"messages.ackReactionScope": "Ack Reaction Scope",
|
|
7663
|
-
"messages.inbound.debounceMs": "Inbound Message Debounce (ms)",
|
|
7664
|
-
"talk.apiKey": "Talk API Key",
|
|
7665
|
-
"channels.whatsapp": "WhatsApp",
|
|
7666
|
-
"channels.telegram": "Telegram",
|
|
7667
|
-
"channels.telegram.customCommands": "Telegram Custom Commands",
|
|
7668
|
-
"channels.discord": "Discord",
|
|
7669
|
-
"channels.slack": "Slack",
|
|
7670
|
-
"channels.mattermost": "Mattermost",
|
|
7671
|
-
"channels.signal": "Signal",
|
|
7672
|
-
"channels.imessage": "iMessage",
|
|
7673
|
-
"channels.bluebubbles": "BlueBubbles",
|
|
7674
|
-
"channels.msteams": "MS Teams",
|
|
7675
|
-
"channels.telegram.botToken": "Telegram Bot Token",
|
|
7676
|
-
"channels.telegram.dmPolicy": "Telegram DM Policy",
|
|
7677
|
-
"channels.telegram.streamMode": "Telegram Draft Stream Mode",
|
|
7678
|
-
"channels.telegram.draftChunk.minChars": "Telegram Draft Chunk Min Chars",
|
|
7679
|
-
"channels.telegram.draftChunk.maxChars": "Telegram Draft Chunk Max Chars",
|
|
7680
|
-
"channels.telegram.draftChunk.breakPreference": "Telegram Draft Chunk Break Preference",
|
|
7681
|
-
"channels.telegram.retry.attempts": "Telegram Retry Attempts",
|
|
7682
|
-
"channels.telegram.retry.minDelayMs": "Telegram Retry Min Delay (ms)",
|
|
7683
|
-
"channels.telegram.retry.maxDelayMs": "Telegram Retry Max Delay (ms)",
|
|
7684
|
-
"channels.telegram.retry.jitter": "Telegram Retry Jitter",
|
|
7685
|
-
"channels.telegram.network.autoSelectFamily": "Telegram autoSelectFamily",
|
|
7686
|
-
"channels.telegram.timeoutSeconds": "Telegram API Timeout (seconds)",
|
|
7687
|
-
"channels.telegram.capabilities.inlineButtons": "Telegram Inline Buttons",
|
|
7688
|
-
"channels.whatsapp.dmPolicy": "WhatsApp DM Policy",
|
|
7689
|
-
"channels.whatsapp.selfChatMode": "WhatsApp Self-Phone Mode",
|
|
7690
|
-
"channels.whatsapp.debounceMs": "WhatsApp Message Debounce (ms)",
|
|
7691
|
-
"channels.signal.dmPolicy": "Signal DM Policy",
|
|
7692
|
-
"channels.imessage.dmPolicy": "iMessage DM Policy",
|
|
7693
|
-
"channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
|
|
7694
|
-
"channels.discord.dm.policy": "Discord DM Policy",
|
|
7695
|
-
"channels.discord.retry.attempts": "Discord Retry Attempts",
|
|
7696
|
-
"channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
|
|
7697
|
-
"channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
|
|
7698
|
-
"channels.discord.retry.jitter": "Discord Retry Jitter",
|
|
7699
|
-
"channels.discord.maxLinesPerMessage": "Discord Max Lines Per Message",
|
|
7700
|
-
"channels.discord.intents.presence": "Discord Presence Intent",
|
|
7701
|
-
"channels.discord.intents.guildMembers": "Discord Guild Members Intent",
|
|
7702
|
-
"channels.discord.pluralkit.enabled": "Discord PluralKit Enabled",
|
|
7703
|
-
"channels.discord.pluralkit.token": "Discord PluralKit Token",
|
|
7704
|
-
"channels.slack.dm.policy": "Slack DM Policy",
|
|
7705
|
-
"channels.slack.allowBots": "Slack Allow Bot Messages",
|
|
7706
|
-
"channels.discord.token": "Discord Bot Token",
|
|
7707
|
-
"channels.slack.botToken": "Slack Bot Token",
|
|
7708
|
-
"channels.slack.appToken": "Slack App Token",
|
|
7709
|
-
"channels.slack.userToken": "Slack User Token",
|
|
7710
|
-
"channels.slack.userTokenReadOnly": "Slack User Token Read Only",
|
|
7711
|
-
"channels.slack.thread.historyScope": "Slack Thread History Scope",
|
|
7712
|
-
"channels.slack.thread.inheritParent": "Slack Thread Parent Inheritance",
|
|
7713
|
-
"channels.mattermost.botToken": "Mattermost Bot Token",
|
|
7714
|
-
"channels.mattermost.baseUrl": "Mattermost Base URL",
|
|
7715
|
-
"channels.mattermost.chatmode": "Mattermost Chat Mode",
|
|
7716
|
-
"channels.mattermost.oncharPrefixes": "Mattermost Onchar Prefixes",
|
|
7717
|
-
"channels.mattermost.requireMention": "Mattermost Require Mention",
|
|
7718
|
-
"channels.signal.account": "Signal Account",
|
|
7719
|
-
"channels.imessage.cliPath": "iMessage CLI Path",
|
|
7720
|
-
"agents.list[].skills": "Agent Skill Filter",
|
|
7721
|
-
"agents.list[].identity.avatar": "Agent Avatar",
|
|
7722
|
-
"discovery.mdns.mode": "mDNS Discovery Mode",
|
|
7723
|
-
"plugins.enabled": "Enable Plugins",
|
|
7724
|
-
"plugins.allow": "Plugin Allowlist",
|
|
7725
|
-
"plugins.deny": "Plugin Denylist",
|
|
7726
|
-
"plugins.load.paths": "Plugin Load Paths",
|
|
7727
|
-
"plugins.slots": "Plugin Slots",
|
|
7728
|
-
"plugins.slots.memory": "Memory Plugin",
|
|
7729
|
-
"plugins.entries": "Plugin Entries",
|
|
7730
|
-
"plugins.entries.*.enabled": "Plugin Enabled",
|
|
7731
|
-
"plugins.entries.*.config": "Plugin Config",
|
|
7732
|
-
"plugins.installs": "Plugin Install Records",
|
|
7733
|
-
"plugins.installs.*.source": "Plugin Install Source",
|
|
7734
|
-
"plugins.installs.*.spec": "Plugin Install Spec",
|
|
7735
|
-
"plugins.installs.*.sourcePath": "Plugin Install Source Path",
|
|
7736
|
-
"plugins.installs.*.installPath": "Plugin Install Path",
|
|
7737
|
-
"plugins.installs.*.version": "Plugin Install Version",
|
|
7738
|
-
"plugins.installs.*.installedAt": "Plugin Install Time"
|
|
7254
|
+
const IRC_FIELD_HELP = {
|
|
7255
|
+
"channels.irc.configWrites": "Allow IRC to write config in response to channel events/commands (default: true).",
|
|
7256
|
+
"channels.irc.dmPolicy": "Direct message access control (\"pairing\" recommended). \"open\" requires channels.irc.allowFrom=[\"*\"].",
|
|
7257
|
+
"channels.irc.nickserv.enabled": "Enable NickServ identify/register after connect (defaults to enabled when password is configured).",
|
|
7258
|
+
"channels.irc.nickserv.service": "NickServ service nick (default: NickServ).",
|
|
7259
|
+
"channels.irc.nickserv.password": "NickServ password used for IDENTIFY/REGISTER (sensitive).",
|
|
7260
|
+
"channels.irc.nickserv.passwordFile": "Optional file path containing NickServ password.",
|
|
7261
|
+
"channels.irc.nickserv.register": "If true, send NickServ REGISTER on every connect. Use once for initial registration, then disable.",
|
|
7262
|
+
"channels.irc.nickserv.registerEmail": "Email used with NickServ REGISTER (required when register=true)."
|
|
7739
7263
|
};
|
|
7264
|
+
|
|
7265
|
+
//#endregion
|
|
7266
|
+
//#region src/config/schema.help.ts
|
|
7740
7267
|
const FIELD_HELP = {
|
|
7741
7268
|
"meta.lastTouchedVersion": "Auto-set when OpenClaw writes the config.",
|
|
7742
7269
|
"meta.lastTouchedAt": "ISO timestamp of the last config write (auto-set).",
|
|
@@ -7810,6 +7337,7 @@ const FIELD_HELP = {
|
|
|
7810
7337
|
"channels.slack.allowBots": "Allow bot-authored messages to trigger Slack replies (default: false).",
|
|
7811
7338
|
"channels.slack.thread.historyScope": "Scope for Slack thread history context (\"thread\" isolates per thread; \"channel\" reuses channel history).",
|
|
7812
7339
|
"channels.slack.thread.inheritParent": "If true, Slack thread sessions inherit the parent channel transcript (default: false).",
|
|
7340
|
+
"channels.slack.thread.initialHistoryLimit": "Maximum number of existing Slack thread messages to fetch when starting a new thread session (default: 20, set to 0 to disable).",
|
|
7813
7341
|
"channels.mattermost.botToken": "Bot token from Mattermost System Console -> Integrations -> Bot Accounts.",
|
|
7814
7342
|
"channels.mattermost.baseUrl": "Base URL for your Mattermost server (e.g., https://chat.example.com).",
|
|
7815
7343
|
"channels.mattermost.chatmode": "Reply to channel messages on mention (\"oncall\"), on trigger chars (\">\" or \"!\") (\"onchar\"), or on every message (\"onmessage\").",
|
|
@@ -7835,7 +7363,7 @@ const FIELD_HELP = {
|
|
|
7835
7363
|
"agents.defaults.memorySearch.remote.baseUrl": "Custom base URL for remote embeddings (OpenAI-compatible proxies or Gemini overrides).",
|
|
7836
7364
|
"agents.defaults.memorySearch.remote.apiKey": "Custom API key for the remote embedding provider.",
|
|
7837
7365
|
"agents.defaults.memorySearch.remote.headers": "Extra headers for remote embeddings (merged; remote overrides OpenAI headers).",
|
|
7838
|
-
"agents.defaults.memorySearch.remote.batch.enabled": "Enable batch API for memory embeddings (OpenAI/Gemini
|
|
7366
|
+
"agents.defaults.memorySearch.remote.batch.enabled": "Enable batch API for memory embeddings (OpenAI/Gemini; default: true).",
|
|
7839
7367
|
"agents.defaults.memorySearch.remote.batch.wait": "Wait for batch completion when indexing (default: true).",
|
|
7840
7368
|
"agents.defaults.memorySearch.remote.batch.concurrency": "Max concurrent embedding batch jobs for memory indexing (default: 2).",
|
|
7841
7369
|
"agents.defaults.memorySearch.remote.batch.pollIntervalMs": "Polling interval in ms for batch status (default: 2000).",
|
|
@@ -7915,7 +7443,6 @@ const FIELD_HELP = {
|
|
|
7915
7443
|
"commands.restart": "Allow /restart and gateway restart tool actions (default: false).",
|
|
7916
7444
|
"commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
|
|
7917
7445
|
"commands.ownerAllowFrom": "Explicit owner allowlist for owner-only tools/commands. Use channel-native IDs (optionally prefixed like \"whatsapp:+15551234567\"). '*' is ignored.",
|
|
7918
|
-
"commands.allowFrom": "Per-provider allowlist restricting who can use slash commands. If set, overrides the channel's allowFrom for command authorization. Use '*' key for global default; provider-specific keys (e.g. 'discord') override the global. Example: { \"*\": [\"user1\"], \"discord\": [\"user:123\"] }.",
|
|
7919
7446
|
"session.dmScope": "DM session scoping: \"main\" keeps continuity; \"per-peer\", \"per-channel-peer\", or \"per-account-channel-peer\" isolates DM history (recommended for shared inboxes/multi-account).",
|
|
7920
7447
|
"session.identityLinks": "Map canonical identities to provider-prefixed peer IDs for DM session linking (example: telegram:123456).",
|
|
7921
7448
|
"channels.telegram.configWrites": "Allow Telegram to write config in response to channel events/commands (default: true).",
|
|
@@ -7926,6 +7453,7 @@ const FIELD_HELP = {
|
|
|
7926
7453
|
"channels.signal.configWrites": "Allow Signal to write config in response to channel events/commands (default: true).",
|
|
7927
7454
|
"channels.imessage.configWrites": "Allow iMessage to write config in response to channel events/commands (default: true).",
|
|
7928
7455
|
"channels.msteams.configWrites": "Allow Microsoft Teams to write config in response to channel events/commands (default: true).",
|
|
7456
|
+
...IRC_FIELD_HELP,
|
|
7929
7457
|
"channels.discord.commands.native": "Override native commands for Discord (bool or \"auto\").",
|
|
7930
7458
|
"channels.discord.commands.nativeSkills": "Override native skill commands for Discord (bool or \"auto\").",
|
|
7931
7459
|
"channels.telegram.commands.native": "Override native commands for Telegram (bool or \"auto\").",
|
|
@@ -7966,6 +7494,361 @@ const FIELD_HELP = {
|
|
|
7966
7494
|
"channels.discord.pluralkit.token": "Optional PluralKit token for resolving private systems or members.",
|
|
7967
7495
|
"channels.slack.dm.policy": "Direct message access control (\"pairing\" recommended). \"open\" requires channels.slack.dm.allowFrom=[\"*\"]."
|
|
7968
7496
|
};
|
|
7497
|
+
|
|
7498
|
+
//#endregion
|
|
7499
|
+
//#region src/config/schema.labels.ts
|
|
7500
|
+
const FIELD_LABELS = {
|
|
7501
|
+
"meta.lastTouchedVersion": "Config Last Touched Version",
|
|
7502
|
+
"meta.lastTouchedAt": "Config Last Touched At",
|
|
7503
|
+
"update.channel": "Update Channel",
|
|
7504
|
+
"update.checkOnStart": "Update Check on Start",
|
|
7505
|
+
"diagnostics.enabled": "Diagnostics Enabled",
|
|
7506
|
+
"diagnostics.flags": "Diagnostics Flags",
|
|
7507
|
+
"diagnostics.otel.enabled": "OpenTelemetry Enabled",
|
|
7508
|
+
"diagnostics.otel.endpoint": "OpenTelemetry Endpoint",
|
|
7509
|
+
"diagnostics.otel.protocol": "OpenTelemetry Protocol",
|
|
7510
|
+
"diagnostics.otel.headers": "OpenTelemetry Headers",
|
|
7511
|
+
"diagnostics.otel.serviceName": "OpenTelemetry Service Name",
|
|
7512
|
+
"diagnostics.otel.traces": "OpenTelemetry Traces Enabled",
|
|
7513
|
+
"diagnostics.otel.metrics": "OpenTelemetry Metrics Enabled",
|
|
7514
|
+
"diagnostics.otel.logs": "OpenTelemetry Logs Enabled",
|
|
7515
|
+
"diagnostics.otel.sampleRate": "OpenTelemetry Trace Sample Rate",
|
|
7516
|
+
"diagnostics.otel.flushIntervalMs": "OpenTelemetry Flush Interval (ms)",
|
|
7517
|
+
"diagnostics.cacheTrace.enabled": "Cache Trace Enabled",
|
|
7518
|
+
"diagnostics.cacheTrace.filePath": "Cache Trace File Path",
|
|
7519
|
+
"diagnostics.cacheTrace.includeMessages": "Cache Trace Include Messages",
|
|
7520
|
+
"diagnostics.cacheTrace.includePrompt": "Cache Trace Include Prompt",
|
|
7521
|
+
"diagnostics.cacheTrace.includeSystem": "Cache Trace Include System",
|
|
7522
|
+
"agents.list.*.identity.avatar": "Identity Avatar",
|
|
7523
|
+
"agents.list.*.skills": "Agent Skill Filter",
|
|
7524
|
+
"gateway.remote.url": "Remote Gateway URL",
|
|
7525
|
+
"gateway.remote.sshTarget": "Remote Gateway SSH Target",
|
|
7526
|
+
"gateway.remote.sshIdentity": "Remote Gateway SSH Identity",
|
|
7527
|
+
"gateway.remote.token": "Remote Gateway Token",
|
|
7528
|
+
"gateway.remote.password": "Remote Gateway Password",
|
|
7529
|
+
"gateway.remote.tlsFingerprint": "Remote Gateway TLS Fingerprint",
|
|
7530
|
+
"gateway.auth.token": "Gateway Token",
|
|
7531
|
+
"gateway.auth.password": "Gateway Password",
|
|
7532
|
+
"tools.media.image.enabled": "Enable Image Understanding",
|
|
7533
|
+
"tools.media.image.maxBytes": "Image Understanding Max Bytes",
|
|
7534
|
+
"tools.media.image.maxChars": "Image Understanding Max Chars",
|
|
7535
|
+
"tools.media.image.prompt": "Image Understanding Prompt",
|
|
7536
|
+
"tools.media.image.timeoutSeconds": "Image Understanding Timeout (sec)",
|
|
7537
|
+
"tools.media.image.attachments": "Image Understanding Attachment Policy",
|
|
7538
|
+
"tools.media.image.models": "Image Understanding Models",
|
|
7539
|
+
"tools.media.image.scope": "Image Understanding Scope",
|
|
7540
|
+
"tools.media.models": "Media Understanding Shared Models",
|
|
7541
|
+
"tools.media.concurrency": "Media Understanding Concurrency",
|
|
7542
|
+
"tools.media.audio.enabled": "Enable Audio Understanding",
|
|
7543
|
+
"tools.media.audio.maxBytes": "Audio Understanding Max Bytes",
|
|
7544
|
+
"tools.media.audio.maxChars": "Audio Understanding Max Chars",
|
|
7545
|
+
"tools.media.audio.prompt": "Audio Understanding Prompt",
|
|
7546
|
+
"tools.media.audio.timeoutSeconds": "Audio Understanding Timeout (sec)",
|
|
7547
|
+
"tools.media.audio.language": "Audio Understanding Language",
|
|
7548
|
+
"tools.media.audio.attachments": "Audio Understanding Attachment Policy",
|
|
7549
|
+
"tools.media.audio.models": "Audio Understanding Models",
|
|
7550
|
+
"tools.media.audio.scope": "Audio Understanding Scope",
|
|
7551
|
+
"tools.media.video.enabled": "Enable Video Understanding",
|
|
7552
|
+
"tools.media.video.maxBytes": "Video Understanding Max Bytes",
|
|
7553
|
+
"tools.media.video.maxChars": "Video Understanding Max Chars",
|
|
7554
|
+
"tools.media.video.prompt": "Video Understanding Prompt",
|
|
7555
|
+
"tools.media.video.timeoutSeconds": "Video Understanding Timeout (sec)",
|
|
7556
|
+
"tools.media.video.attachments": "Video Understanding Attachment Policy",
|
|
7557
|
+
"tools.media.video.models": "Video Understanding Models",
|
|
7558
|
+
"tools.media.video.scope": "Video Understanding Scope",
|
|
7559
|
+
"tools.links.enabled": "Enable Link Understanding",
|
|
7560
|
+
"tools.links.maxLinks": "Link Understanding Max Links",
|
|
7561
|
+
"tools.links.timeoutSeconds": "Link Understanding Timeout (sec)",
|
|
7562
|
+
"tools.links.models": "Link Understanding Models",
|
|
7563
|
+
"tools.links.scope": "Link Understanding Scope",
|
|
7564
|
+
"tools.profile": "Tool Profile",
|
|
7565
|
+
"tools.alsoAllow": "Tool Allowlist Additions",
|
|
7566
|
+
"agents.list[].tools.profile": "Agent Tool Profile",
|
|
7567
|
+
"agents.list[].tools.alsoAllow": "Agent Tool Allowlist Additions",
|
|
7568
|
+
"tools.byProvider": "Tool Policy by Provider",
|
|
7569
|
+
"agents.list[].tools.byProvider": "Agent Tool Policy by Provider",
|
|
7570
|
+
"tools.exec.applyPatch.enabled": "Enable apply_patch",
|
|
7571
|
+
"tools.exec.applyPatch.allowModels": "apply_patch Model Allowlist",
|
|
7572
|
+
"tools.exec.notifyOnExit": "Exec Notify On Exit",
|
|
7573
|
+
"tools.exec.approvalRunningNoticeMs": "Exec Approval Running Notice (ms)",
|
|
7574
|
+
"tools.exec.host": "Exec Host",
|
|
7575
|
+
"tools.exec.security": "Exec Security",
|
|
7576
|
+
"tools.exec.ask": "Exec Ask",
|
|
7577
|
+
"tools.exec.node": "Exec Node Binding",
|
|
7578
|
+
"tools.exec.pathPrepend": "Exec PATH Prepend",
|
|
7579
|
+
"tools.exec.safeBins": "Exec Safe Bins",
|
|
7580
|
+
"tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
|
|
7581
|
+
"tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
|
|
7582
|
+
"tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
|
|
7583
|
+
"tools.message.crossContext.marker.enabled": "Cross-Context Marker",
|
|
7584
|
+
"tools.message.crossContext.marker.prefix": "Cross-Context Marker Prefix",
|
|
7585
|
+
"tools.message.crossContext.marker.suffix": "Cross-Context Marker Suffix",
|
|
7586
|
+
"tools.message.broadcast.enabled": "Enable Message Broadcast",
|
|
7587
|
+
"tools.web.search.enabled": "Enable Web Search Tool",
|
|
7588
|
+
"tools.web.search.provider": "Web Search Provider",
|
|
7589
|
+
"tools.web.search.apiKey": "Brave Search API Key",
|
|
7590
|
+
"tools.web.search.maxResults": "Web Search Max Results",
|
|
7591
|
+
"tools.web.search.timeoutSeconds": "Web Search Timeout (sec)",
|
|
7592
|
+
"tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)",
|
|
7593
|
+
"tools.web.fetch.enabled": "Enable Web Fetch Tool",
|
|
7594
|
+
"tools.web.fetch.maxChars": "Web Fetch Max Chars",
|
|
7595
|
+
"tools.web.fetch.timeoutSeconds": "Web Fetch Timeout (sec)",
|
|
7596
|
+
"tools.web.fetch.cacheTtlMinutes": "Web Fetch Cache TTL (min)",
|
|
7597
|
+
"tools.web.fetch.maxRedirects": "Web Fetch Max Redirects",
|
|
7598
|
+
"tools.web.fetch.userAgent": "Web Fetch User-Agent",
|
|
7599
|
+
"gateway.controlUi.basePath": "Control UI Base Path",
|
|
7600
|
+
"gateway.controlUi.root": "Control UI Assets Root",
|
|
7601
|
+
"gateway.controlUi.allowedOrigins": "Control UI Allowed Origins",
|
|
7602
|
+
"gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth",
|
|
7603
|
+
"gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth",
|
|
7604
|
+
"gateway.http.endpoints.chatCompletions.enabled": "OpenAI Chat Completions Endpoint",
|
|
7605
|
+
"gateway.reload.mode": "Config Reload Mode",
|
|
7606
|
+
"gateway.reload.debounceMs": "Config Reload Debounce (ms)",
|
|
7607
|
+
"gateway.nodes.browser.mode": "Gateway Node Browser Mode",
|
|
7608
|
+
"gateway.nodes.browser.node": "Gateway Node Browser Pin",
|
|
7609
|
+
"gateway.nodes.allowCommands": "Gateway Node Allowlist (Extra Commands)",
|
|
7610
|
+
"gateway.nodes.denyCommands": "Gateway Node Denylist",
|
|
7611
|
+
"nodeHost.browserProxy.enabled": "Node Browser Proxy Enabled",
|
|
7612
|
+
"nodeHost.browserProxy.allowProfiles": "Node Browser Proxy Allowed Profiles",
|
|
7613
|
+
"skills.load.watch": "Watch Skills",
|
|
7614
|
+
"skills.load.watchDebounceMs": "Skills Watch Debounce (ms)",
|
|
7615
|
+
"agents.defaults.workspace": "Workspace",
|
|
7616
|
+
"agents.defaults.repoRoot": "Repo Root",
|
|
7617
|
+
"agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars",
|
|
7618
|
+
"agents.defaults.envelopeTimezone": "Envelope Timezone",
|
|
7619
|
+
"agents.defaults.envelopeTimestamp": "Envelope Timestamp",
|
|
7620
|
+
"agents.defaults.envelopeElapsed": "Envelope Elapsed",
|
|
7621
|
+
"agents.defaults.memorySearch": "Memory Search",
|
|
7622
|
+
"agents.defaults.memorySearch.enabled": "Enable Memory Search",
|
|
7623
|
+
"agents.defaults.memorySearch.sources": "Memory Search Sources",
|
|
7624
|
+
"agents.defaults.memorySearch.extraPaths": "Extra Memory Paths",
|
|
7625
|
+
"agents.defaults.memorySearch.experimental.sessionMemory": "Memory Search Session Index (Experimental)",
|
|
7626
|
+
"agents.defaults.memorySearch.provider": "Memory Search Provider",
|
|
7627
|
+
"agents.defaults.memorySearch.remote.baseUrl": "Remote Embedding Base URL",
|
|
7628
|
+
"agents.defaults.memorySearch.remote.apiKey": "Remote Embedding API Key",
|
|
7629
|
+
"agents.defaults.memorySearch.remote.headers": "Remote Embedding Headers",
|
|
7630
|
+
"agents.defaults.memorySearch.remote.batch.concurrency": "Remote Batch Concurrency",
|
|
7631
|
+
"agents.defaults.memorySearch.model": "Memory Search Model",
|
|
7632
|
+
"agents.defaults.memorySearch.fallback": "Memory Search Fallback",
|
|
7633
|
+
"agents.defaults.memorySearch.local.modelPath": "Local Embedding Model Path",
|
|
7634
|
+
"agents.defaults.memorySearch.store.path": "Memory Search Index Path",
|
|
7635
|
+
"agents.defaults.memorySearch.store.vector.enabled": "Memory Search Vector Index",
|
|
7636
|
+
"agents.defaults.memorySearch.store.vector.extensionPath": "Memory Search Vector Extension Path",
|
|
7637
|
+
"agents.defaults.memorySearch.chunking.tokens": "Memory Chunk Tokens",
|
|
7638
|
+
"agents.defaults.memorySearch.chunking.overlap": "Memory Chunk Overlap Tokens",
|
|
7639
|
+
"agents.defaults.memorySearch.sync.onSessionStart": "Index on Session Start",
|
|
7640
|
+
"agents.defaults.memorySearch.sync.onSearch": "Index on Search (Lazy)",
|
|
7641
|
+
"agents.defaults.memorySearch.sync.watch": "Watch Memory Files",
|
|
7642
|
+
"agents.defaults.memorySearch.sync.watchDebounceMs": "Memory Watch Debounce (ms)",
|
|
7643
|
+
"agents.defaults.memorySearch.sync.sessions.deltaBytes": "Session Delta Bytes",
|
|
7644
|
+
"agents.defaults.memorySearch.sync.sessions.deltaMessages": "Session Delta Messages",
|
|
7645
|
+
"agents.defaults.memorySearch.query.maxResults": "Memory Search Max Results",
|
|
7646
|
+
"agents.defaults.memorySearch.query.minScore": "Memory Search Min Score",
|
|
7647
|
+
"agents.defaults.memorySearch.query.hybrid.enabled": "Memory Search Hybrid",
|
|
7648
|
+
"agents.defaults.memorySearch.query.hybrid.vectorWeight": "Memory Search Vector Weight",
|
|
7649
|
+
"agents.defaults.memorySearch.query.hybrid.textWeight": "Memory Search Text Weight",
|
|
7650
|
+
"agents.defaults.memorySearch.query.hybrid.candidateMultiplier": "Memory Search Hybrid Candidate Multiplier",
|
|
7651
|
+
"agents.defaults.memorySearch.cache.enabled": "Memory Search Embedding Cache",
|
|
7652
|
+
"agents.defaults.memorySearch.cache.maxEntries": "Memory Search Embedding Cache Max Entries",
|
|
7653
|
+
memory: "Memory",
|
|
7654
|
+
"memory.backend": "Memory Backend",
|
|
7655
|
+
"memory.citations": "Memory Citations Mode",
|
|
7656
|
+
"memory.qmd.command": "QMD Binary",
|
|
7657
|
+
"memory.qmd.includeDefaultMemory": "QMD Include Default Memory",
|
|
7658
|
+
"memory.qmd.paths": "QMD Extra Paths",
|
|
7659
|
+
"memory.qmd.paths.path": "QMD Path",
|
|
7660
|
+
"memory.qmd.paths.pattern": "QMD Path Pattern",
|
|
7661
|
+
"memory.qmd.paths.name": "QMD Path Name",
|
|
7662
|
+
"memory.qmd.sessions.enabled": "QMD Session Indexing",
|
|
7663
|
+
"memory.qmd.sessions.exportDir": "QMD Session Export Directory",
|
|
7664
|
+
"memory.qmd.sessions.retentionDays": "QMD Session Retention (days)",
|
|
7665
|
+
"memory.qmd.update.interval": "QMD Update Interval",
|
|
7666
|
+
"memory.qmd.update.debounceMs": "QMD Update Debounce (ms)",
|
|
7667
|
+
"memory.qmd.update.onBoot": "QMD Update on Startup",
|
|
7668
|
+
"memory.qmd.update.waitForBootSync": "QMD Wait for Boot Sync",
|
|
7669
|
+
"memory.qmd.update.embedInterval": "QMD Embed Interval",
|
|
7670
|
+
"memory.qmd.update.commandTimeoutMs": "QMD Command Timeout (ms)",
|
|
7671
|
+
"memory.qmd.update.updateTimeoutMs": "QMD Update Timeout (ms)",
|
|
7672
|
+
"memory.qmd.update.embedTimeoutMs": "QMD Embed Timeout (ms)",
|
|
7673
|
+
"memory.qmd.limits.maxResults": "QMD Max Results",
|
|
7674
|
+
"memory.qmd.limits.maxSnippetChars": "QMD Max Snippet Chars",
|
|
7675
|
+
"memory.qmd.limits.maxInjectedChars": "QMD Max Injected Chars",
|
|
7676
|
+
"memory.qmd.limits.timeoutMs": "QMD Search Timeout (ms)",
|
|
7677
|
+
"memory.qmd.scope": "QMD Surface Scope",
|
|
7678
|
+
"auth.profiles": "Auth Profiles",
|
|
7679
|
+
"auth.order": "Auth Profile Order",
|
|
7680
|
+
"auth.cooldowns.billingBackoffHours": "Billing Backoff (hours)",
|
|
7681
|
+
"auth.cooldowns.billingBackoffHoursByProvider": "Billing Backoff Overrides",
|
|
7682
|
+
"auth.cooldowns.billingMaxHours": "Billing Backoff Cap (hours)",
|
|
7683
|
+
"auth.cooldowns.failureWindowHours": "Failover Window (hours)",
|
|
7684
|
+
"agents.defaults.models": "Models",
|
|
7685
|
+
"agents.defaults.model.primary": "Primary Model",
|
|
7686
|
+
"agents.defaults.model.fallbacks": "Model Fallbacks",
|
|
7687
|
+
"agents.defaults.imageModel.primary": "Image Model",
|
|
7688
|
+
"agents.defaults.imageModel.fallbacks": "Image Model Fallbacks",
|
|
7689
|
+
"agents.defaults.humanDelay.mode": "Human Delay Mode",
|
|
7690
|
+
"agents.defaults.humanDelay.minMs": "Human Delay Min (ms)",
|
|
7691
|
+
"agents.defaults.humanDelay.maxMs": "Human Delay Max (ms)",
|
|
7692
|
+
"agents.defaults.cliBackends": "CLI Backends",
|
|
7693
|
+
"commands.native": "Native Commands",
|
|
7694
|
+
"commands.nativeSkills": "Native Skill Commands",
|
|
7695
|
+
"commands.text": "Text Commands",
|
|
7696
|
+
"commands.bash": "Allow Bash Chat Command",
|
|
7697
|
+
"commands.bashForegroundMs": "Bash Foreground Window (ms)",
|
|
7698
|
+
"commands.config": "Allow /config",
|
|
7699
|
+
"commands.debug": "Allow /debug",
|
|
7700
|
+
"commands.restart": "Allow Restart",
|
|
7701
|
+
"commands.useAccessGroups": "Use Access Groups",
|
|
7702
|
+
"commands.ownerAllowFrom": "Command Owners",
|
|
7703
|
+
"ui.seamColor": "Accent Color",
|
|
7704
|
+
"ui.assistant.name": "Assistant Name",
|
|
7705
|
+
"ui.assistant.avatar": "Assistant Avatar",
|
|
7706
|
+
"browser.evaluateEnabled": "Browser Evaluate Enabled",
|
|
7707
|
+
"browser.snapshotDefaults": "Browser Snapshot Defaults",
|
|
7708
|
+
"browser.snapshotDefaults.mode": "Browser Snapshot Mode",
|
|
7709
|
+
"browser.remoteCdpTimeoutMs": "Remote CDP Timeout (ms)",
|
|
7710
|
+
"browser.remoteCdpHandshakeTimeoutMs": "Remote CDP Handshake Timeout (ms)",
|
|
7711
|
+
"session.dmScope": "DM Session Scope",
|
|
7712
|
+
"session.agentToAgent.maxPingPongTurns": "Agent-to-Agent Ping-Pong Turns",
|
|
7713
|
+
"messages.ackReaction": "Ack Reaction Emoji",
|
|
7714
|
+
"messages.ackReactionScope": "Ack Reaction Scope",
|
|
7715
|
+
"messages.inbound.debounceMs": "Inbound Message Debounce (ms)",
|
|
7716
|
+
"talk.apiKey": "Talk API Key",
|
|
7717
|
+
"channels.whatsapp": "WhatsApp",
|
|
7718
|
+
"channels.telegram": "Telegram",
|
|
7719
|
+
"channels.telegram.customCommands": "Telegram Custom Commands",
|
|
7720
|
+
"channels.discord": "Discord",
|
|
7721
|
+
"channels.slack": "Slack",
|
|
7722
|
+
"channels.mattermost": "Mattermost",
|
|
7723
|
+
"channels.signal": "Signal",
|
|
7724
|
+
"channels.imessage": "iMessage",
|
|
7725
|
+
"channels.bluebubbles": "BlueBubbles",
|
|
7726
|
+
"channels.msteams": "MS Teams",
|
|
7727
|
+
...IRC_FIELD_LABELS,
|
|
7728
|
+
"channels.telegram.botToken": "Telegram Bot Token",
|
|
7729
|
+
"channels.telegram.dmPolicy": "Telegram DM Policy",
|
|
7730
|
+
"channels.telegram.streamMode": "Telegram Draft Stream Mode",
|
|
7731
|
+
"channels.telegram.draftChunk.minChars": "Telegram Draft Chunk Min Chars",
|
|
7732
|
+
"channels.telegram.draftChunk.maxChars": "Telegram Draft Chunk Max Chars",
|
|
7733
|
+
"channels.telegram.draftChunk.breakPreference": "Telegram Draft Chunk Break Preference",
|
|
7734
|
+
"channels.telegram.retry.attempts": "Telegram Retry Attempts",
|
|
7735
|
+
"channels.telegram.retry.minDelayMs": "Telegram Retry Min Delay (ms)",
|
|
7736
|
+
"channels.telegram.retry.maxDelayMs": "Telegram Retry Max Delay (ms)",
|
|
7737
|
+
"channels.telegram.retry.jitter": "Telegram Retry Jitter",
|
|
7738
|
+
"channels.telegram.network.autoSelectFamily": "Telegram autoSelectFamily",
|
|
7739
|
+
"channels.telegram.timeoutSeconds": "Telegram API Timeout (seconds)",
|
|
7740
|
+
"channels.telegram.capabilities.inlineButtons": "Telegram Inline Buttons",
|
|
7741
|
+
"channels.whatsapp.dmPolicy": "WhatsApp DM Policy",
|
|
7742
|
+
"channels.whatsapp.selfChatMode": "WhatsApp Self-Phone Mode",
|
|
7743
|
+
"channels.whatsapp.debounceMs": "WhatsApp Message Debounce (ms)",
|
|
7744
|
+
"channels.signal.dmPolicy": "Signal DM Policy",
|
|
7745
|
+
"channels.imessage.dmPolicy": "iMessage DM Policy",
|
|
7746
|
+
"channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
|
|
7747
|
+
"channels.discord.dm.policy": "Discord DM Policy",
|
|
7748
|
+
"channels.discord.retry.attempts": "Discord Retry Attempts",
|
|
7749
|
+
"channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
|
|
7750
|
+
"channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
|
|
7751
|
+
"channels.discord.retry.jitter": "Discord Retry Jitter",
|
|
7752
|
+
"channels.discord.maxLinesPerMessage": "Discord Max Lines Per Message",
|
|
7753
|
+
"channels.discord.intents.presence": "Discord Presence Intent",
|
|
7754
|
+
"channels.discord.intents.guildMembers": "Discord Guild Members Intent",
|
|
7755
|
+
"channels.discord.pluralkit.enabled": "Discord PluralKit Enabled",
|
|
7756
|
+
"channels.discord.pluralkit.token": "Discord PluralKit Token",
|
|
7757
|
+
"channels.slack.dm.policy": "Slack DM Policy",
|
|
7758
|
+
"channels.slack.allowBots": "Slack Allow Bot Messages",
|
|
7759
|
+
"channels.discord.token": "Discord Bot Token",
|
|
7760
|
+
"channels.slack.botToken": "Slack Bot Token",
|
|
7761
|
+
"channels.slack.appToken": "Slack App Token",
|
|
7762
|
+
"channels.slack.userToken": "Slack User Token",
|
|
7763
|
+
"channels.slack.userTokenReadOnly": "Slack User Token Read Only",
|
|
7764
|
+
"channels.slack.thread.historyScope": "Slack Thread History Scope",
|
|
7765
|
+
"channels.slack.thread.inheritParent": "Slack Thread Parent Inheritance",
|
|
7766
|
+
"channels.slack.thread.initialHistoryLimit": "Slack Thread Initial History Limit",
|
|
7767
|
+
"channels.mattermost.botToken": "Mattermost Bot Token",
|
|
7768
|
+
"channels.mattermost.baseUrl": "Mattermost Base URL",
|
|
7769
|
+
"channels.mattermost.chatmode": "Mattermost Chat Mode",
|
|
7770
|
+
"channels.mattermost.oncharPrefixes": "Mattermost Onchar Prefixes",
|
|
7771
|
+
"channels.mattermost.requireMention": "Mattermost Require Mention",
|
|
7772
|
+
"channels.signal.account": "Signal Account",
|
|
7773
|
+
"channels.imessage.cliPath": "iMessage CLI Path",
|
|
7774
|
+
"agents.list[].skills": "Agent Skill Filter",
|
|
7775
|
+
"agents.list[].identity.avatar": "Agent Avatar",
|
|
7776
|
+
"discovery.mdns.mode": "mDNS Discovery Mode",
|
|
7777
|
+
"plugins.enabled": "Enable Plugins",
|
|
7778
|
+
"plugins.allow": "Plugin Allowlist",
|
|
7779
|
+
"plugins.deny": "Plugin Denylist",
|
|
7780
|
+
"plugins.load.paths": "Plugin Load Paths",
|
|
7781
|
+
"plugins.slots": "Plugin Slots",
|
|
7782
|
+
"plugins.slots.memory": "Memory Plugin",
|
|
7783
|
+
"plugins.entries": "Plugin Entries",
|
|
7784
|
+
"plugins.entries.*.enabled": "Plugin Enabled",
|
|
7785
|
+
"plugins.entries.*.config": "Plugin Config",
|
|
7786
|
+
"plugins.installs": "Plugin Install Records",
|
|
7787
|
+
"plugins.installs.*.source": "Plugin Install Source",
|
|
7788
|
+
"plugins.installs.*.spec": "Plugin Install Spec",
|
|
7789
|
+
"plugins.installs.*.sourcePath": "Plugin Install Source Path",
|
|
7790
|
+
"plugins.installs.*.installPath": "Plugin Install Path",
|
|
7791
|
+
"plugins.installs.*.version": "Plugin Install Version",
|
|
7792
|
+
"plugins.installs.*.installedAt": "Plugin Install Time"
|
|
7793
|
+
};
|
|
7794
|
+
|
|
7795
|
+
//#endregion
|
|
7796
|
+
//#region src/config/schema.hints.ts
|
|
7797
|
+
const log$3 = createSubsystemLogger("config/schema");
|
|
7798
|
+
const GROUP_LABELS = {
|
|
7799
|
+
wizard: "Wizard",
|
|
7800
|
+
update: "Update",
|
|
7801
|
+
diagnostics: "Diagnostics",
|
|
7802
|
+
logging: "Logging",
|
|
7803
|
+
gateway: "Gateway",
|
|
7804
|
+
nodeHost: "Node Host",
|
|
7805
|
+
agents: "Agents",
|
|
7806
|
+
tools: "Tools",
|
|
7807
|
+
bindings: "Bindings",
|
|
7808
|
+
audio: "Audio",
|
|
7809
|
+
models: "Models",
|
|
7810
|
+
messages: "Messages",
|
|
7811
|
+
commands: "Commands",
|
|
7812
|
+
session: "Session",
|
|
7813
|
+
cron: "Cron",
|
|
7814
|
+
hooks: "Hooks",
|
|
7815
|
+
ui: "UI",
|
|
7816
|
+
browser: "Browser",
|
|
7817
|
+
talk: "Talk",
|
|
7818
|
+
channels: "Messaging Channels",
|
|
7819
|
+
skills: "Skills",
|
|
7820
|
+
plugins: "Plugins",
|
|
7821
|
+
discovery: "Discovery",
|
|
7822
|
+
presence: "Presence",
|
|
7823
|
+
voicewake: "Voice Wake"
|
|
7824
|
+
};
|
|
7825
|
+
const GROUP_ORDER = {
|
|
7826
|
+
wizard: 20,
|
|
7827
|
+
update: 25,
|
|
7828
|
+
diagnostics: 27,
|
|
7829
|
+
gateway: 30,
|
|
7830
|
+
nodeHost: 35,
|
|
7831
|
+
agents: 40,
|
|
7832
|
+
tools: 50,
|
|
7833
|
+
bindings: 55,
|
|
7834
|
+
audio: 60,
|
|
7835
|
+
models: 70,
|
|
7836
|
+
messages: 80,
|
|
7837
|
+
commands: 85,
|
|
7838
|
+
session: 90,
|
|
7839
|
+
cron: 100,
|
|
7840
|
+
hooks: 110,
|
|
7841
|
+
ui: 120,
|
|
7842
|
+
browser: 130,
|
|
7843
|
+
talk: 140,
|
|
7844
|
+
channels: 150,
|
|
7845
|
+
skills: 200,
|
|
7846
|
+
plugins: 205,
|
|
7847
|
+
discovery: 210,
|
|
7848
|
+
presence: 220,
|
|
7849
|
+
voicewake: 230,
|
|
7850
|
+
logging: 900
|
|
7851
|
+
};
|
|
7969
7852
|
const FIELD_PLACEHOLDERS = {
|
|
7970
7853
|
"gateway.remote.url": "ws://host:18789",
|
|
7971
7854
|
"gateway.remote.tlsFingerprint": "sha256:ab12cd34…",
|
|
@@ -7976,15 +7859,430 @@ const FIELD_PLACEHOLDERS = {
|
|
|
7976
7859
|
"channels.mattermost.baseUrl": "https://chat.example.com",
|
|
7977
7860
|
"agents.list[].identity.avatar": "avatars/openclaw.png"
|
|
7978
7861
|
};
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7862
|
+
/**
|
|
7863
|
+
* Non-sensitive field names that happen to match sensitive patterns.
|
|
7864
|
+
* These are explicitly excluded from redaction (plugin config) and
|
|
7865
|
+
* warnings about not being marked sensitive (base config).
|
|
7866
|
+
*/
|
|
7867
|
+
const SENSITIVE_KEY_WHITELIST = new Set([
|
|
7868
|
+
"maxtokens",
|
|
7869
|
+
"maxoutputtokens",
|
|
7870
|
+
"maxinputtokens",
|
|
7871
|
+
"maxcompletiontokens",
|
|
7872
|
+
"contexttokens",
|
|
7873
|
+
"totaltokens",
|
|
7874
|
+
"tokencount",
|
|
7875
|
+
"tokenlimit",
|
|
7876
|
+
"tokenbudget",
|
|
7877
|
+
"passwordFile"
|
|
7878
|
+
]);
|
|
7879
|
+
const SENSITIVE_PATTERNS = [
|
|
7880
|
+
/token$/i,
|
|
7881
|
+
/password/i,
|
|
7882
|
+
/secret/i,
|
|
7883
|
+
/api.?key/i
|
|
7884
|
+
];
|
|
7885
|
+
function isSensitiveConfigPath(path) {
|
|
7886
|
+
return !Array.from(SENSITIVE_KEY_WHITELIST).some((suffix) => path.endsWith(suffix)) && SENSITIVE_PATTERNS.some((pattern) => pattern.test(path));
|
|
7887
|
+
}
|
|
7888
|
+
function buildBaseHints() {
|
|
7889
|
+
const hints = {};
|
|
7890
|
+
for (const [group, label] of Object.entries(GROUP_LABELS)) hints[group] = {
|
|
7891
|
+
label,
|
|
7892
|
+
group: label,
|
|
7893
|
+
order: GROUP_ORDER[group]
|
|
7894
|
+
};
|
|
7895
|
+
for (const [path, label] of Object.entries(FIELD_LABELS)) {
|
|
7896
|
+
const current = hints[path];
|
|
7897
|
+
hints[path] = current ? {
|
|
7898
|
+
...current,
|
|
7899
|
+
label
|
|
7900
|
+
} : { label };
|
|
7901
|
+
}
|
|
7902
|
+
for (const [path, help] of Object.entries(FIELD_HELP)) {
|
|
7903
|
+
const current = hints[path];
|
|
7904
|
+
hints[path] = current ? {
|
|
7905
|
+
...current,
|
|
7906
|
+
help
|
|
7907
|
+
} : { help };
|
|
7908
|
+
}
|
|
7909
|
+
for (const [path, placeholder] of Object.entries(FIELD_PLACEHOLDERS)) {
|
|
7910
|
+
const current = hints[path];
|
|
7911
|
+
hints[path] = current ? {
|
|
7912
|
+
...current,
|
|
7913
|
+
placeholder
|
|
7914
|
+
} : { placeholder };
|
|
7915
|
+
}
|
|
7916
|
+
return hints;
|
|
7917
|
+
}
|
|
7918
|
+
function applySensitiveHints(hints, allowedKeys) {
|
|
7919
|
+
const next = { ...hints };
|
|
7920
|
+
for (const key of Object.keys(next)) {
|
|
7921
|
+
if (allowedKeys && !allowedKeys.has(key)) continue;
|
|
7922
|
+
if (next[key]?.sensitive !== void 0) continue;
|
|
7923
|
+
if (isSensitiveConfigPath(key)) next[key] = {
|
|
7924
|
+
...next[key],
|
|
7925
|
+
sensitive: true
|
|
7926
|
+
};
|
|
7927
|
+
}
|
|
7928
|
+
return next;
|
|
7929
|
+
}
|
|
7930
|
+
function isUnwrappable(object) {
|
|
7931
|
+
return !!object && typeof object === "object" && "unwrap" in object && typeof object.unwrap === "function" && !(object instanceof z.ZodArray);
|
|
7932
|
+
}
|
|
7933
|
+
function mapSensitivePaths(schema, path, hints) {
|
|
7934
|
+
let next = { ...hints };
|
|
7935
|
+
let currentSchema = schema;
|
|
7936
|
+
let isSensitive = sensitive.has(currentSchema);
|
|
7937
|
+
while (isUnwrappable(currentSchema)) {
|
|
7938
|
+
currentSchema = currentSchema.unwrap();
|
|
7939
|
+
isSensitive ||= sensitive.has(currentSchema);
|
|
7940
|
+
}
|
|
7941
|
+
if (isSensitive) next[path] = {
|
|
7942
|
+
...next[path],
|
|
7943
|
+
sensitive: true
|
|
7944
|
+
};
|
|
7945
|
+
else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$3.warn(`possibly sensitive key found: (${path})`);
|
|
7946
|
+
if (currentSchema instanceof z.ZodObject) {
|
|
7947
|
+
const shape = currentSchema.shape;
|
|
7948
|
+
for (const key in shape) {
|
|
7949
|
+
const nextPath = path ? `${path}.${key}` : key;
|
|
7950
|
+
next = mapSensitivePaths(shape[key], nextPath, next);
|
|
7951
|
+
}
|
|
7952
|
+
} else if (currentSchema instanceof z.ZodArray) {
|
|
7953
|
+
const nextPath = path ? `${path}[]` : "[]";
|
|
7954
|
+
next = mapSensitivePaths(currentSchema.element, nextPath, next);
|
|
7955
|
+
} else if (currentSchema instanceof z.ZodRecord) {
|
|
7956
|
+
const nextPath = path ? `${path}.*` : "*";
|
|
7957
|
+
next = mapSensitivePaths(currentSchema._def.valueType, nextPath, next);
|
|
7958
|
+
} else if (currentSchema instanceof z.ZodUnion || currentSchema instanceof z.ZodDiscriminatedUnion) for (const option of currentSchema.options) next = mapSensitivePaths(option, path, next);
|
|
7959
|
+
else if (currentSchema instanceof z.ZodIntersection) {
|
|
7960
|
+
next = mapSensitivePaths(currentSchema._def.left, path, next);
|
|
7961
|
+
next = mapSensitivePaths(currentSchema._def.right, path, next);
|
|
7962
|
+
}
|
|
7963
|
+
return next;
|
|
7964
|
+
}
|
|
7965
|
+
|
|
7966
|
+
//#endregion
|
|
7967
|
+
//#region src/config/redact-snapshot.ts
|
|
7968
|
+
const log$2 = createSubsystemLogger("config/redaction");
|
|
7969
|
+
const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
|
|
7970
|
+
function isSensitivePath(path) {
|
|
7971
|
+
if (path.endsWith("[]")) return isSensitiveConfigPath(path.slice(0, -2));
|
|
7972
|
+
else return isSensitiveConfigPath(path);
|
|
7973
|
+
}
|
|
7974
|
+
function isEnvVarPlaceholder(value) {
|
|
7975
|
+
return ENV_VAR_PLACEHOLDER_PATTERN.test(value.trim());
|
|
7976
|
+
}
|
|
7977
|
+
function isExtensionPath(path) {
|
|
7978
|
+
return path === "plugins" || path.startsWith("plugins.") || path === "channels" || path.startsWith("channels.");
|
|
7979
|
+
}
|
|
7980
|
+
function isExplicitlyNonSensitivePath(hints, paths) {
|
|
7981
|
+
if (!hints) return false;
|
|
7982
|
+
return paths.some((path) => hints[path]?.sensitive === false);
|
|
7983
|
+
}
|
|
7984
|
+
/**
|
|
7985
|
+
* Sentinel value used to replace sensitive config fields in gateway responses.
|
|
7986
|
+
* Write-side handlers (config.set, config.apply, config.patch) detect this
|
|
7987
|
+
* sentinel and restore the original value from the on-disk config, so a
|
|
7988
|
+
* round-trip through the Web UI does not corrupt credentials.
|
|
7989
|
+
*/
|
|
7990
|
+
const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
|
|
7991
|
+
function buildRedactionLookup(hints) {
|
|
7992
|
+
let result = /* @__PURE__ */ new Set();
|
|
7993
|
+
for (const [path, hint] of Object.entries(hints)) {
|
|
7994
|
+
if (!hint.sensitive) continue;
|
|
7995
|
+
const parts = path.split(".");
|
|
7996
|
+
let joinedPath = parts.shift() ?? "";
|
|
7997
|
+
result.add(joinedPath);
|
|
7998
|
+
if (joinedPath.endsWith("[]")) result.add(joinedPath.slice(0, -2));
|
|
7999
|
+
for (const part of parts) {
|
|
8000
|
+
if (part.endsWith("[]")) result.add(`${joinedPath}.${part.slice(0, -2)}`);
|
|
8001
|
+
joinedPath = `${joinedPath}.${part}`;
|
|
8002
|
+
result.add(joinedPath);
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
if (result.size !== 0) result.add("");
|
|
8006
|
+
return result;
|
|
8007
|
+
}
|
|
8008
|
+
/**
|
|
8009
|
+
* Deep-walk an object and replace string values at sensitive paths
|
|
8010
|
+
* with the redaction sentinel.
|
|
8011
|
+
*/
|
|
8012
|
+
function redactObject(obj, hints) {
|
|
8013
|
+
if (hints) {
|
|
8014
|
+
const lookup = buildRedactionLookup(hints);
|
|
8015
|
+
return lookup.has("") ? redactObjectWithLookup(obj, lookup, "", [], hints) : redactObjectGuessing(obj, "", [], hints);
|
|
8016
|
+
} else return redactObjectGuessing(obj, "", []);
|
|
8017
|
+
}
|
|
8018
|
+
/**
|
|
8019
|
+
* Collect all sensitive string values from a config object.
|
|
8020
|
+
* Used for text-based redaction of the raw JSON5 source.
|
|
8021
|
+
*/
|
|
8022
|
+
function collectSensitiveValues(obj, hints) {
|
|
8023
|
+
const result = [];
|
|
8024
|
+
if (hints) {
|
|
8025
|
+
const lookup = buildRedactionLookup(hints);
|
|
8026
|
+
if (lookup.has("")) redactObjectWithLookup(obj, lookup, "", result, hints);
|
|
8027
|
+
else redactObjectGuessing(obj, "", result, hints);
|
|
8028
|
+
} else redactObjectGuessing(obj, "", result);
|
|
8029
|
+
return result;
|
|
8030
|
+
}
|
|
8031
|
+
/**
|
|
8032
|
+
* Worker for redactObject() and collectSensitiveValues().
|
|
8033
|
+
* Used when there are ConfigUiHints available.
|
|
8034
|
+
*/
|
|
8035
|
+
function redactObjectWithLookup(obj, lookup, prefix, values, hints) {
|
|
8036
|
+
if (obj === null || obj === void 0) return obj;
|
|
8037
|
+
if (Array.isArray(obj)) {
|
|
8038
|
+
const path = `${prefix}[]`;
|
|
8039
|
+
if (!lookup.has(path)) {
|
|
8040
|
+
if (!isExtensionPath(prefix)) return obj;
|
|
8041
|
+
return redactObjectGuessing(obj, prefix, values, hints);
|
|
8042
|
+
}
|
|
8043
|
+
return obj.map((item) => {
|
|
8044
|
+
if (typeof item === "string" && !isEnvVarPlaceholder(item)) {
|
|
8045
|
+
values.push(item);
|
|
8046
|
+
return REDACTED_SENTINEL;
|
|
8047
|
+
}
|
|
8048
|
+
return redactObjectWithLookup(item, lookup, path, values, hints);
|
|
8049
|
+
});
|
|
8050
|
+
}
|
|
8051
|
+
if (typeof obj === "object") {
|
|
8052
|
+
const result = {};
|
|
8053
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
8054
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
8055
|
+
const wildcardPath = prefix ? `${prefix}.*` : "*";
|
|
8056
|
+
let matched = false;
|
|
8057
|
+
for (const candidate of [path, wildcardPath]) {
|
|
8058
|
+
result[key] = value;
|
|
8059
|
+
if (lookup.has(candidate)) {
|
|
8060
|
+
matched = true;
|
|
8061
|
+
if (typeof value === "string" && !isEnvVarPlaceholder(value)) {
|
|
8062
|
+
result[key] = REDACTED_SENTINEL;
|
|
8063
|
+
values.push(value);
|
|
8064
|
+
} else if (typeof value === "object" && value !== null) result[key] = redactObjectWithLookup(value, lookup, candidate, values, hints);
|
|
8065
|
+
break;
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
if (!matched && isExtensionPath(path)) {
|
|
8069
|
+
const markedNonSensitive = isExplicitlyNonSensitivePath(hints, [path, wildcardPath]);
|
|
8070
|
+
if (typeof value === "string" && !markedNonSensitive && isSensitivePath(path) && !isEnvVarPlaceholder(value)) {
|
|
8071
|
+
result[key] = REDACTED_SENTINEL;
|
|
8072
|
+
values.push(value);
|
|
8073
|
+
} else if (typeof value === "object" && value !== null) result[key] = redactObjectGuessing(value, path, values, hints);
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
return result;
|
|
8077
|
+
}
|
|
8078
|
+
return obj;
|
|
8079
|
+
}
|
|
8080
|
+
/**
|
|
8081
|
+
* Worker for redactObject() and collectSensitiveValues().
|
|
8082
|
+
* Used when ConfigUiHints are NOT available.
|
|
8083
|
+
*/
|
|
8084
|
+
function redactObjectGuessing(obj, prefix, values, hints) {
|
|
8085
|
+
if (obj === null || obj === void 0) return obj;
|
|
8086
|
+
if (Array.isArray(obj)) return obj.map((item) => {
|
|
8087
|
+
const path = `${prefix}[]`;
|
|
8088
|
+
if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && typeof item === "string" && !isEnvVarPlaceholder(item)) {
|
|
8089
|
+
values.push(item);
|
|
8090
|
+
return REDACTED_SENTINEL;
|
|
8091
|
+
}
|
|
8092
|
+
return redactObjectGuessing(item, path, values, hints);
|
|
8093
|
+
});
|
|
8094
|
+
if (typeof obj === "object") {
|
|
8095
|
+
const result = {};
|
|
8096
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
8097
|
+
const dotPath = prefix ? `${prefix}.${key}` : key;
|
|
8098
|
+
if (!isExplicitlyNonSensitivePath(hints, [dotPath, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(dotPath) && typeof value === "string" && !isEnvVarPlaceholder(value)) {
|
|
8099
|
+
result[key] = REDACTED_SENTINEL;
|
|
8100
|
+
values.push(value);
|
|
8101
|
+
} else if (typeof value === "object" && value !== null) result[key] = redactObjectGuessing(value, dotPath, values, hints);
|
|
8102
|
+
else result[key] = value;
|
|
8103
|
+
}
|
|
8104
|
+
return result;
|
|
8105
|
+
}
|
|
8106
|
+
return obj;
|
|
8107
|
+
}
|
|
8108
|
+
/**
|
|
8109
|
+
* Replace known sensitive values in a raw JSON5 string with the sentinel.
|
|
8110
|
+
* Values are replaced longest-first to avoid partial matches.
|
|
8111
|
+
*/
|
|
8112
|
+
function redactRawText(raw, config, hints) {
|
|
8113
|
+
const sensitiveValues = collectSensitiveValues(config, hints);
|
|
8114
|
+
sensitiveValues.sort((a, b) => b.length - a.length);
|
|
8115
|
+
let result = raw;
|
|
8116
|
+
for (const value of sensitiveValues) result = result.replaceAll(value, REDACTED_SENTINEL);
|
|
8117
|
+
return result;
|
|
8118
|
+
}
|
|
8119
|
+
/**
|
|
8120
|
+
* Returns a copy of the config snapshot with all sensitive fields
|
|
8121
|
+
* replaced by {@link REDACTED_SENTINEL}. The `hash` is preserved
|
|
8122
|
+
* (it tracks config identity, not content).
|
|
8123
|
+
*
|
|
8124
|
+
* Both `config` (the parsed object) and `raw` (the JSON5 source) are scrubbed
|
|
8125
|
+
* so no credential can leak through either path.
|
|
8126
|
+
*
|
|
8127
|
+
* When `uiHints` are provided, sensitivity is determined from the schema hints.
|
|
8128
|
+
* Without hints, falls back to regex-based detection via `isSensitivePath()`.
|
|
8129
|
+
*/
|
|
8130
|
+
/**
|
|
8131
|
+
* Redact sensitive fields from a plain config object (not a full snapshot).
|
|
8132
|
+
* Used by write endpoints (config.set, config.patch, config.apply) to avoid
|
|
8133
|
+
* leaking credentials in their responses.
|
|
8134
|
+
*/
|
|
8135
|
+
function redactConfigObject(value, uiHints) {
|
|
8136
|
+
return redactObject(value, uiHints);
|
|
8137
|
+
}
|
|
8138
|
+
function redactConfigSnapshot(snapshot, uiHints) {
|
|
8139
|
+
if (!snapshot.valid) return {
|
|
8140
|
+
...snapshot,
|
|
8141
|
+
config: {},
|
|
8142
|
+
raw: null,
|
|
8143
|
+
parsed: null,
|
|
8144
|
+
resolved: {}
|
|
8145
|
+
};
|
|
8146
|
+
const redactedConfig = redactObject(snapshot.config, uiHints);
|
|
8147
|
+
const redactedRaw = snapshot.raw ? redactRawText(snapshot.raw, snapshot.config, uiHints) : null;
|
|
8148
|
+
const redactedParsed = snapshot.parsed ? redactObject(snapshot.parsed, uiHints) : snapshot.parsed;
|
|
8149
|
+
const redactedResolved = redactConfigObject(snapshot.resolved);
|
|
8150
|
+
return {
|
|
8151
|
+
...snapshot,
|
|
8152
|
+
config: redactedConfig,
|
|
8153
|
+
raw: redactedRaw,
|
|
8154
|
+
parsed: redactedParsed,
|
|
8155
|
+
resolved: redactedResolved
|
|
8156
|
+
};
|
|
8157
|
+
}
|
|
8158
|
+
/**
|
|
8159
|
+
* Deep-walk `incoming` and replace any {@link REDACTED_SENTINEL} values
|
|
8160
|
+
* (on sensitive paths) with the corresponding value from `original`.
|
|
8161
|
+
*
|
|
8162
|
+
* This is called by config.set / config.apply / config.patch before writing,
|
|
8163
|
+
* so that credentials survive a Web UI round-trip unmodified.
|
|
8164
|
+
*/
|
|
8165
|
+
function restoreRedactedValues(incoming, original, hints) {
|
|
8166
|
+
if (incoming === null || incoming === void 0) return {
|
|
8167
|
+
ok: false,
|
|
8168
|
+
error: "no input"
|
|
8169
|
+
};
|
|
8170
|
+
if (typeof incoming !== "object") return {
|
|
8171
|
+
ok: false,
|
|
8172
|
+
error: "input not an object"
|
|
8173
|
+
};
|
|
8174
|
+
try {
|
|
8175
|
+
if (hints) {
|
|
8176
|
+
const lookup = buildRedactionLookup(hints);
|
|
8177
|
+
if (lookup.has("")) return {
|
|
8178
|
+
ok: true,
|
|
8179
|
+
result: restoreRedactedValuesWithLookup(incoming, original, lookup, "", hints)
|
|
8180
|
+
};
|
|
8181
|
+
else return {
|
|
8182
|
+
ok: true,
|
|
8183
|
+
result: restoreRedactedValuesGuessing(incoming, original, "", hints)
|
|
8184
|
+
};
|
|
8185
|
+
} else return {
|
|
8186
|
+
ok: true,
|
|
8187
|
+
result: restoreRedactedValuesGuessing(incoming, original, "")
|
|
8188
|
+
};
|
|
8189
|
+
} catch (err) {
|
|
8190
|
+
if (err instanceof RedactionError) return {
|
|
8191
|
+
ok: false,
|
|
8192
|
+
humanReadableMessage: `Sentinel value "${REDACTED_SENTINEL}" in key ${err.key} is not valid as real data`
|
|
8193
|
+
};
|
|
8194
|
+
throw err;
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
8197
|
+
var RedactionError = class RedactionError extends Error {
|
|
8198
|
+
constructor(key) {
|
|
8199
|
+
super("internal error class---should never escape");
|
|
8200
|
+
this.key = key;
|
|
8201
|
+
this.name = "RedactionError";
|
|
8202
|
+
Object.setPrototypeOf(this, RedactionError.prototype);
|
|
8203
|
+
}
|
|
8204
|
+
};
|
|
8205
|
+
/**
|
|
8206
|
+
* Worker for restoreRedactedValues().
|
|
8207
|
+
* Used when there are ConfigUiHints available.
|
|
8208
|
+
*/
|
|
8209
|
+
function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hints) {
|
|
8210
|
+
if (incoming === null || incoming === void 0) return incoming;
|
|
8211
|
+
if (typeof incoming !== "object") return incoming;
|
|
8212
|
+
if (Array.isArray(incoming)) {
|
|
8213
|
+
const path = `${prefix}[]`;
|
|
8214
|
+
if (!lookup.has(path)) {
|
|
8215
|
+
if (!isExtensionPath(prefix)) return incoming;
|
|
8216
|
+
return restoreRedactedValuesGuessing(incoming, original, prefix, hints);
|
|
8217
|
+
}
|
|
8218
|
+
const origArr = Array.isArray(original) ? original : [];
|
|
8219
|
+
if (incoming.length < origArr.length) log$2.warn(`Redacted config array key ${path} has been truncated`);
|
|
8220
|
+
return incoming.map((item, i) => {
|
|
8221
|
+
if (item === REDACTED_SENTINEL) return origArr[i];
|
|
8222
|
+
return restoreRedactedValuesWithLookup(item, origArr[i], lookup, path, hints);
|
|
8223
|
+
});
|
|
8224
|
+
}
|
|
8225
|
+
const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
|
|
8226
|
+
const result = {};
|
|
8227
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
8228
|
+
result[key] = value;
|
|
8229
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
8230
|
+
const wildcardPath = prefix ? `${prefix}.*` : "*";
|
|
8231
|
+
let matched = false;
|
|
8232
|
+
for (const candidate of [path, wildcardPath]) if (lookup.has(candidate)) {
|
|
8233
|
+
matched = true;
|
|
8234
|
+
if (value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
8235
|
+
else {
|
|
8236
|
+
log$2.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
|
|
8237
|
+
throw new RedactionError(candidate);
|
|
8238
|
+
}
|
|
8239
|
+
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesWithLookup(value, orig[key], lookup, candidate, hints);
|
|
8240
|
+
break;
|
|
8241
|
+
}
|
|
8242
|
+
if (!matched && isExtensionPath(path)) {
|
|
8243
|
+
if (!isExplicitlyNonSensitivePath(hints, [path, wildcardPath]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
8244
|
+
else {
|
|
8245
|
+
log$2.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
|
|
8246
|
+
throw new RedactionError(path);
|
|
8247
|
+
}
|
|
8248
|
+
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
8251
|
+
return result;
|
|
8252
|
+
}
|
|
8253
|
+
/**
|
|
8254
|
+
* Worker for restoreRedactedValues().
|
|
8255
|
+
* Used when ConfigUiHints are NOT available.
|
|
8256
|
+
*/
|
|
8257
|
+
function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
|
|
8258
|
+
if (incoming === null || incoming === void 0) return incoming;
|
|
8259
|
+
if (typeof incoming !== "object") return incoming;
|
|
8260
|
+
if (Array.isArray(incoming)) {
|
|
8261
|
+
const origArr = Array.isArray(original) ? original : [];
|
|
8262
|
+
return incoming.map((item, i) => {
|
|
8263
|
+
const path = `${prefix}[]`;
|
|
8264
|
+
if (incoming.length < origArr.length) log$2.warn(`Redacted config array key ${path} has been truncated`);
|
|
8265
|
+
if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && item === REDACTED_SENTINEL) return origArr[i];
|
|
8266
|
+
return restoreRedactedValuesGuessing(item, origArr[i], path, hints);
|
|
8267
|
+
});
|
|
8268
|
+
}
|
|
8269
|
+
const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
|
|
8270
|
+
const result = {};
|
|
8271
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
8272
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
8273
|
+
if (!isExplicitlyNonSensitivePath(hints, [path, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
8274
|
+
else {
|
|
8275
|
+
log$2.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
|
|
8276
|
+
throw new RedactionError(path);
|
|
8277
|
+
}
|
|
8278
|
+
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
|
|
8279
|
+
else result[key] = value;
|
|
8280
|
+
}
|
|
8281
|
+
return result;
|
|
7987
8282
|
}
|
|
8283
|
+
|
|
8284
|
+
//#endregion
|
|
8285
|
+
//#region src/config/schema.ts
|
|
7988
8286
|
function cloneSchema(value) {
|
|
7989
8287
|
if (typeof structuredClone === "function") return structuredClone(value);
|
|
7990
8288
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -8014,43 +8312,11 @@ function mergeObjectSchema(base, extension) {
|
|
|
8014
8312
|
if (additional !== void 0) merged.additionalProperties = additional;
|
|
8015
8313
|
return merged;
|
|
8016
8314
|
}
|
|
8017
|
-
function
|
|
8018
|
-
const
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
order: GROUP_ORDER[group]
|
|
8023
|
-
};
|
|
8024
|
-
for (const [path, label] of Object.entries(FIELD_LABELS)) {
|
|
8025
|
-
const current = hints[path];
|
|
8026
|
-
hints[path] = current ? {
|
|
8027
|
-
...current,
|
|
8028
|
-
label
|
|
8029
|
-
} : { label };
|
|
8030
|
-
}
|
|
8031
|
-
for (const [path, help] of Object.entries(FIELD_HELP)) {
|
|
8032
|
-
const current = hints[path];
|
|
8033
|
-
hints[path] = current ? {
|
|
8034
|
-
...current,
|
|
8035
|
-
help
|
|
8036
|
-
} : { help };
|
|
8037
|
-
}
|
|
8038
|
-
for (const [path, placeholder] of Object.entries(FIELD_PLACEHOLDERS)) {
|
|
8039
|
-
const current = hints[path];
|
|
8040
|
-
hints[path] = current ? {
|
|
8041
|
-
...current,
|
|
8042
|
-
placeholder
|
|
8043
|
-
} : { placeholder };
|
|
8044
|
-
}
|
|
8045
|
-
return hints;
|
|
8046
|
-
}
|
|
8047
|
-
function applySensitiveHints(hints) {
|
|
8048
|
-
const next = { ...hints };
|
|
8049
|
-
for (const key of Object.keys(next)) if (isSensitivePath(key)) next[key] = {
|
|
8050
|
-
...next[key],
|
|
8051
|
-
sensitive: true
|
|
8052
|
-
};
|
|
8053
|
-
return next;
|
|
8315
|
+
function collectExtensionHintKeys(hints, plugins, channels) {
|
|
8316
|
+
const pluginPrefixes = plugins.map((plugin) => plugin.id.trim()).filter(Boolean).map((id) => `plugins.entries.${id}`);
|
|
8317
|
+
const channelPrefixes = channels.map((channel) => channel.id.trim()).filter(Boolean).map((id) => `channels.${id}`);
|
|
8318
|
+
const prefixes = [...pluginPrefixes, ...channelPrefixes];
|
|
8319
|
+
return new Set(Object.keys(hints).filter((key) => prefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}.`))));
|
|
8054
8320
|
}
|
|
8055
8321
|
function applyPluginHints(hints, plugins) {
|
|
8056
8322
|
const next = { ...hints };
|
|
@@ -8200,7 +8466,7 @@ function buildBaseConfigSchema() {
|
|
|
8200
8466
|
unrepresentable: "any"
|
|
8201
8467
|
});
|
|
8202
8468
|
schema.title = "OpenClawConfig";
|
|
8203
|
-
const hints =
|
|
8469
|
+
const hints = mapSensitivePaths(OpenClawSchema, "", buildBaseHints());
|
|
8204
8470
|
const next = {
|
|
8205
8471
|
schema: stripChannelSchema(schema),
|
|
8206
8472
|
uiHints: hints,
|
|
@@ -8215,7 +8481,8 @@ function buildConfigSchema(params) {
|
|
|
8215
8481
|
const plugins = params?.plugins ?? [];
|
|
8216
8482
|
const channels = params?.channels ?? [];
|
|
8217
8483
|
if (plugins.length === 0 && channels.length === 0) return base;
|
|
8218
|
-
const
|
|
8484
|
+
const mergedWithoutSensitiveHints = applyHeartbeatTargetHints(applyChannelHints(applyPluginHints(base.uiHints, plugins), channels), channels);
|
|
8485
|
+
const mergedHints = applySensitiveHints(mergedWithoutSensitiveHints, collectExtensionHintKeys(mergedWithoutSensitiveHints, plugins, channels));
|
|
8219
8486
|
const mergedSchema = applyChannelSchemas(applyPluginSchemas(base.schema, plugins), channels);
|
|
8220
8487
|
return {
|
|
8221
8488
|
...base,
|
|
@@ -8250,45 +8517,49 @@ function requireConfigBaseHash(params, snapshot, respond) {
|
|
|
8250
8517
|
}
|
|
8251
8518
|
return true;
|
|
8252
8519
|
}
|
|
8520
|
+
function loadSchemaWithPlugins() {
|
|
8521
|
+
const cfg = loadConfig();
|
|
8522
|
+
return buildConfigSchema({
|
|
8523
|
+
plugins: loadOpenClawPlugins({
|
|
8524
|
+
config: cfg,
|
|
8525
|
+
cache: true,
|
|
8526
|
+
workspaceDir: resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)),
|
|
8527
|
+
logger: {
|
|
8528
|
+
info: () => {},
|
|
8529
|
+
warn: () => {},
|
|
8530
|
+
error: () => {},
|
|
8531
|
+
debug: () => {}
|
|
8532
|
+
}
|
|
8533
|
+
}).plugins.map((plugin) => ({
|
|
8534
|
+
id: plugin.id,
|
|
8535
|
+
name: plugin.name,
|
|
8536
|
+
description: plugin.description,
|
|
8537
|
+
configUiHints: plugin.configUiHints,
|
|
8538
|
+
configSchema: plugin.configJsonSchema
|
|
8539
|
+
})),
|
|
8540
|
+
channels: listChannelPlugins().map((entry) => ({
|
|
8541
|
+
id: entry.id,
|
|
8542
|
+
label: entry.meta.label,
|
|
8543
|
+
description: entry.meta.blurb,
|
|
8544
|
+
configSchema: entry.configSchema?.schema,
|
|
8545
|
+
configUiHints: entry.configSchema?.uiHints
|
|
8546
|
+
}))
|
|
8547
|
+
});
|
|
8548
|
+
}
|
|
8253
8549
|
const configHandlers = {
|
|
8254
8550
|
"config.get": async ({ params, respond }) => {
|
|
8255
8551
|
if (!validateConfigGetParams(params)) {
|
|
8256
8552
|
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid config.get params: ${formatValidationErrors(validateConfigGetParams.errors)}`));
|
|
8257
8553
|
return;
|
|
8258
8554
|
}
|
|
8259
|
-
respond(true, redactConfigSnapshot(await readConfigFileSnapshot()), void 0);
|
|
8555
|
+
respond(true, redactConfigSnapshot(await readConfigFileSnapshot(), loadSchemaWithPlugins().uiHints), void 0);
|
|
8260
8556
|
},
|
|
8261
8557
|
"config.schema": ({ params, respond }) => {
|
|
8262
8558
|
if (!validateConfigSchemaParams(params)) {
|
|
8263
8559
|
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid config.schema params: ${formatValidationErrors(validateConfigSchemaParams.errors)}`));
|
|
8264
8560
|
return;
|
|
8265
8561
|
}
|
|
8266
|
-
|
|
8267
|
-
respond(true, buildConfigSchema({
|
|
8268
|
-
plugins: loadOpenClawPlugins({
|
|
8269
|
-
config: cfg,
|
|
8270
|
-
workspaceDir: resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)),
|
|
8271
|
-
logger: {
|
|
8272
|
-
info: () => {},
|
|
8273
|
-
warn: () => {},
|
|
8274
|
-
error: () => {},
|
|
8275
|
-
debug: () => {}
|
|
8276
|
-
}
|
|
8277
|
-
}).plugins.map((plugin) => ({
|
|
8278
|
-
id: plugin.id,
|
|
8279
|
-
name: plugin.name,
|
|
8280
|
-
description: plugin.description,
|
|
8281
|
-
configUiHints: plugin.configUiHints,
|
|
8282
|
-
configSchema: plugin.configJsonSchema
|
|
8283
|
-
})),
|
|
8284
|
-
channels: listChannelPlugins().map((entry) => ({
|
|
8285
|
-
id: entry.id,
|
|
8286
|
-
label: entry.meta.label,
|
|
8287
|
-
description: entry.meta.blurb,
|
|
8288
|
-
configSchema: entry.configSchema?.schema,
|
|
8289
|
-
configUiHints: entry.configSchema?.uiHints
|
|
8290
|
-
}))
|
|
8291
|
-
}), void 0);
|
|
8562
|
+
respond(true, loadSchemaWithPlugins(), void 0);
|
|
8292
8563
|
},
|
|
8293
8564
|
"config.set": async ({ params, respond }) => {
|
|
8294
8565
|
if (!validateConfigSetParams(params)) {
|
|
@@ -8307,23 +8578,22 @@ const configHandlers = {
|
|
|
8307
8578
|
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, parsedRes.error));
|
|
8308
8579
|
return;
|
|
8309
8580
|
}
|
|
8310
|
-
const
|
|
8311
|
-
|
|
8312
|
-
|
|
8581
|
+
const schemaSet = loadSchemaWithPlugins();
|
|
8582
|
+
const restored = restoreRedactedValues(parsedRes.parsed, snapshot.config, schemaSet.uiHints);
|
|
8583
|
+
if (!restored.ok) {
|
|
8584
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restored.humanReadableMessage ?? "invalid config"));
|
|
8313
8585
|
return;
|
|
8314
8586
|
}
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
} catch (err) {
|
|
8319
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
|
|
8587
|
+
const validated = validateConfigObjectWithPlugins(restored.result);
|
|
8588
|
+
if (!validated.ok) {
|
|
8589
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
|
|
8320
8590
|
return;
|
|
8321
8591
|
}
|
|
8322
|
-
await writeConfigFile(
|
|
8592
|
+
await writeConfigFile(validated.config);
|
|
8323
8593
|
respond(true, {
|
|
8324
8594
|
ok: true,
|
|
8325
8595
|
path: CONFIG_PATH,
|
|
8326
|
-
config: redactConfigObject(
|
|
8596
|
+
config: redactConfigObject(validated.config, schemaSet.uiHints)
|
|
8327
8597
|
}, void 0);
|
|
8328
8598
|
},
|
|
8329
8599
|
"config.patch": async ({ params, respond }) => {
|
|
@@ -8352,14 +8622,13 @@ const configHandlers = {
|
|
|
8352
8622
|
return;
|
|
8353
8623
|
}
|
|
8354
8624
|
const merged = applyMergePatch(snapshot.config, parsedRes.parsed);
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
|
|
8625
|
+
const schemaPatch = loadSchemaWithPlugins();
|
|
8626
|
+
const restoredMerge = restoreRedactedValues(merged, snapshot.config, schemaPatch.uiHints);
|
|
8627
|
+
if (!restoredMerge.ok) {
|
|
8628
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restoredMerge.humanReadableMessage ?? "invalid config"));
|
|
8360
8629
|
return;
|
|
8361
8630
|
}
|
|
8362
|
-
const validated = validateConfigObjectWithPlugins(applyLegacyMigrations(restoredMerge).next ?? restoredMerge);
|
|
8631
|
+
const validated = validateConfigObjectWithPlugins(applyLegacyMigrations(restoredMerge.result).next ?? restoredMerge.result);
|
|
8363
8632
|
if (!validated.ok) {
|
|
8364
8633
|
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
|
|
8365
8634
|
return;
|
|
@@ -8394,7 +8663,7 @@ const configHandlers = {
|
|
|
8394
8663
|
respond(true, {
|
|
8395
8664
|
ok: true,
|
|
8396
8665
|
path: CONFIG_PATH,
|
|
8397
|
-
config: redactConfigObject(validated.config),
|
|
8666
|
+
config: redactConfigObject(validated.config, schemaPatch.uiHints),
|
|
8398
8667
|
restart,
|
|
8399
8668
|
sentinel: {
|
|
8400
8669
|
path: sentinelPath,
|
|
@@ -8419,19 +8688,18 @@ const configHandlers = {
|
|
|
8419
8688
|
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, parsedRes.error));
|
|
8420
8689
|
return;
|
|
8421
8690
|
}
|
|
8422
|
-
const
|
|
8423
|
-
|
|
8424
|
-
|
|
8691
|
+
const schemaApply = loadSchemaWithPlugins();
|
|
8692
|
+
const restored = restoreRedactedValues(parsedRes.parsed, snapshot.config, schemaApply.uiHints);
|
|
8693
|
+
if (!restored.ok) {
|
|
8694
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restored.humanReadableMessage ?? "invalid config"));
|
|
8425
8695
|
return;
|
|
8426
8696
|
}
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
} catch (err) {
|
|
8431
|
-
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
|
|
8697
|
+
const validated = validateConfigObjectWithPlugins(restored.result);
|
|
8698
|
+
if (!validated.ok) {
|
|
8699
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
|
|
8432
8700
|
return;
|
|
8433
8701
|
}
|
|
8434
|
-
await writeConfigFile(
|
|
8702
|
+
await writeConfigFile(validated.config);
|
|
8435
8703
|
const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey?.trim() || void 0 : void 0;
|
|
8436
8704
|
const note = typeof params.note === "string" ? params.note?.trim() || void 0 : void 0;
|
|
8437
8705
|
const restartDelayMsRaw = params.restartDelayMs;
|
|
@@ -8461,7 +8729,7 @@ const configHandlers = {
|
|
|
8461
8729
|
respond(true, {
|
|
8462
8730
|
ok: true,
|
|
8463
8731
|
path: CONFIG_PATH,
|
|
8464
|
-
config: redactConfigObject(
|
|
8732
|
+
config: redactConfigObject(validated.config, schemaApply.uiHints),
|
|
8465
8733
|
restart,
|
|
8466
8734
|
sentinel: {
|
|
8467
8735
|
path: sentinelPath,
|
|
@@ -8906,7 +9174,7 @@ async function verifyDeviceToken(params) {
|
|
|
8906
9174
|
ok: false,
|
|
8907
9175
|
reason: "token-revoked"
|
|
8908
9176
|
};
|
|
8909
|
-
if (
|
|
9177
|
+
if (!safeEqualSecret(params.token, entry.token)) return {
|
|
8910
9178
|
ok: false,
|
|
8911
9179
|
reason: "token-mismatch"
|
|
8912
9180
|
};
|
|
@@ -9758,7 +10026,7 @@ const nodeHandlers = {
|
|
|
9758
10026
|
const p = params;
|
|
9759
10027
|
const payloadJSON = typeof p.payloadJSON === "string" ? p.payloadJSON : p.payload !== void 0 ? JSON.stringify(p.payload) : null;
|
|
9760
10028
|
await respondUnavailableOnThrow(respond, async () => {
|
|
9761
|
-
const { handleNodeEvent } = await import("./server-node-events-
|
|
10029
|
+
const { handleNodeEvent } = await import("./server-node-events-BbHOZX3O.js");
|
|
9762
10030
|
const nodeId = client?.connect?.device?.id ?? client?.connect?.client?.id ?? "node";
|
|
9763
10031
|
await handleNodeEvent({
|
|
9764
10032
|
deps: context.deps,
|
|
@@ -9826,8 +10094,13 @@ const sendHandlers = {
|
|
|
9826
10094
|
return;
|
|
9827
10095
|
}
|
|
9828
10096
|
const to = request.to.trim();
|
|
9829
|
-
const message = request.message.trim();
|
|
9830
|
-
const
|
|
10097
|
+
const message = typeof request.message === "string" ? request.message.trim() : "";
|
|
10098
|
+
const mediaUrl = typeof request.mediaUrl === "string" && request.mediaUrl.trim().length > 0 ? request.mediaUrl.trim() : void 0;
|
|
10099
|
+
const mediaUrls = Array.isArray(request.mediaUrls) ? request.mediaUrls.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0) : void 0;
|
|
10100
|
+
if (!message && !mediaUrl && (mediaUrls?.length ?? 0) === 0) {
|
|
10101
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid send params: text or media is required"));
|
|
10102
|
+
return;
|
|
10103
|
+
}
|
|
9831
10104
|
const channelInput = typeof request.channel === "string" ? request.channel : void 0;
|
|
9832
10105
|
const normalizedChannel = channelInput ? normalizeChannelId(channelInput) : null;
|
|
9833
10106
|
if (channelInput && !normalizedChannel) {
|
|
@@ -9859,7 +10132,7 @@ const sendHandlers = {
|
|
|
9859
10132
|
const outboundDeps = context.deps ? createOutboundSendDeps(context.deps) : void 0;
|
|
9860
10133
|
const mirrorPayloads = normalizeReplyPayloadsForDelivery([{
|
|
9861
10134
|
text: message,
|
|
9862
|
-
mediaUrl
|
|
10135
|
+
mediaUrl,
|
|
9863
10136
|
mediaUrls
|
|
9864
10137
|
}]);
|
|
9865
10138
|
const mirrorText = mirrorPayloads.map((payload) => payload.text).filter(Boolean).join("\n");
|
|
@@ -9887,7 +10160,7 @@ const sendHandlers = {
|
|
|
9887
10160
|
accountId,
|
|
9888
10161
|
payloads: [{
|
|
9889
10162
|
text: message,
|
|
9890
|
-
mediaUrl
|
|
10163
|
+
mediaUrl,
|
|
9891
10164
|
mediaUrls
|
|
9892
10165
|
}],
|
|
9893
10166
|
gifPlayback: request.gifPlayback,
|
|
@@ -10528,7 +10801,8 @@ const sessionsHandlers = {
|
|
|
10528
10801
|
skillsSnapshot: entry?.skillsSnapshot,
|
|
10529
10802
|
inputTokens: 0,
|
|
10530
10803
|
outputTokens: 0,
|
|
10531
|
-
totalTokens: 0
|
|
10804
|
+
totalTokens: 0,
|
|
10805
|
+
totalTokensFresh: true
|
|
10532
10806
|
};
|
|
10533
10807
|
store[primaryKey] = nextEntry;
|
|
10534
10808
|
return nextEntry;
|
|
@@ -10672,6 +10946,7 @@ const sessionsHandlers = {
|
|
|
10672
10946
|
delete entryToUpdate.inputTokens;
|
|
10673
10947
|
delete entryToUpdate.outputTokens;
|
|
10674
10948
|
delete entryToUpdate.totalTokens;
|
|
10949
|
+
delete entryToUpdate.totalTokensFresh;
|
|
10675
10950
|
entryToUpdate.updatedAt = Date.now();
|
|
10676
10951
|
});
|
|
10677
10952
|
respond(true, {
|
|
@@ -10909,23 +11184,70 @@ const systemHandlers = {
|
|
|
10909
11184
|
|
|
10910
11185
|
//#endregion
|
|
10911
11186
|
//#region src/gateway/server-methods/talk.ts
|
|
10912
|
-
const
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
11187
|
+
const ADMIN_SCOPE$2 = "operator.admin";
|
|
11188
|
+
const TALK_SECRETS_SCOPE = "operator.talk.secrets";
|
|
11189
|
+
function canReadTalkSecrets(client) {
|
|
11190
|
+
const scopes = Array.isArray(client?.connect?.scopes) ? client.connect.scopes : [];
|
|
11191
|
+
return scopes.includes(ADMIN_SCOPE$2) || scopes.includes(TALK_SECRETS_SCOPE);
|
|
11192
|
+
}
|
|
11193
|
+
function normalizeTalkConfigSection(value) {
|
|
11194
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
11195
|
+
const source = value;
|
|
11196
|
+
const talk = {};
|
|
11197
|
+
if (typeof source.voiceId === "string") talk.voiceId = source.voiceId;
|
|
11198
|
+
if (source.voiceAliases && typeof source.voiceAliases === "object" && !Array.isArray(source.voiceAliases)) {
|
|
11199
|
+
const aliases = {};
|
|
11200
|
+
for (const [alias, id] of Object.entries(source.voiceAliases)) {
|
|
11201
|
+
if (typeof id !== "string") continue;
|
|
11202
|
+
aliases[alias] = id;
|
|
11203
|
+
}
|
|
11204
|
+
if (Object.keys(aliases).length > 0) talk.voiceAliases = aliases;
|
|
11205
|
+
}
|
|
11206
|
+
if (typeof source.modelId === "string") talk.modelId = source.modelId;
|
|
11207
|
+
if (typeof source.outputFormat === "string") talk.outputFormat = source.outputFormat;
|
|
11208
|
+
if (typeof source.apiKey === "string") talk.apiKey = source.apiKey;
|
|
11209
|
+
if (typeof source.interruptOnSpeech === "boolean") talk.interruptOnSpeech = source.interruptOnSpeech;
|
|
11210
|
+
return Object.keys(talk).length > 0 ? talk : void 0;
|
|
11211
|
+
}
|
|
11212
|
+
const talkHandlers = {
|
|
11213
|
+
"talk.config": async ({ params, respond, client }) => {
|
|
11214
|
+
if (!validateTalkConfigParams(params)) {
|
|
11215
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid talk.config params: ${formatValidationErrors(validateTalkConfigParams.errors)}`));
|
|
11216
|
+
return;
|
|
11217
|
+
}
|
|
11218
|
+
const includeSecrets = Boolean(params.includeSecrets);
|
|
11219
|
+
if (includeSecrets && !canReadTalkSecrets(client)) {
|
|
11220
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `missing scope: ${TALK_SECRETS_SCOPE}`));
|
|
11221
|
+
return;
|
|
11222
|
+
}
|
|
11223
|
+
const snapshot = await readConfigFileSnapshot();
|
|
11224
|
+
const configPayload = {};
|
|
11225
|
+
const talk = normalizeTalkConfigSection(includeSecrets ? snapshot.config.talk : redactConfigObject(snapshot.config.talk));
|
|
11226
|
+
if (talk) configPayload.talk = talk;
|
|
11227
|
+
const sessionMainKey = snapshot.config.session?.mainKey;
|
|
11228
|
+
if (typeof sessionMainKey === "string") configPayload.session = { mainKey: sessionMainKey };
|
|
11229
|
+
const seamColor = snapshot.config.ui?.seamColor;
|
|
11230
|
+
if (typeof seamColor === "string") configPayload.ui = { seamColor };
|
|
11231
|
+
respond(true, { config: configPayload }, void 0);
|
|
11232
|
+
},
|
|
11233
|
+
"talk.mode": ({ params, respond, context, client, isWebchatConnect }) => {
|
|
11234
|
+
if (client && isWebchatConnect(client.connect) && !context.hasConnectedMobileNode()) {
|
|
11235
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "talk disabled: no connected iOS/Android nodes"));
|
|
11236
|
+
return;
|
|
11237
|
+
}
|
|
11238
|
+
if (!validateTalkModeParams(params)) {
|
|
11239
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid talk.mode params: ${formatValidationErrors(validateTalkModeParams.errors)}`));
|
|
11240
|
+
return;
|
|
11241
|
+
}
|
|
11242
|
+
const payload = {
|
|
11243
|
+
enabled: params.enabled,
|
|
11244
|
+
phase: params.phase ?? null,
|
|
11245
|
+
ts: Date.now()
|
|
11246
|
+
};
|
|
11247
|
+
context.broadcast("talk.mode", payload, { dropIfSlow: true });
|
|
11248
|
+
respond(true, payload, void 0);
|
|
10920
11249
|
}
|
|
10921
|
-
|
|
10922
|
-
enabled: params.enabled,
|
|
10923
|
-
phase: params.phase ?? null,
|
|
10924
|
-
ts: Date.now()
|
|
10925
|
-
};
|
|
10926
|
-
context.broadcast("talk.mode", payload, { dropIfSlow: true });
|
|
10927
|
-
respond(true, payload, void 0);
|
|
10928
|
-
} };
|
|
11250
|
+
};
|
|
10929
11251
|
|
|
10930
11252
|
//#endregion
|
|
10931
11253
|
//#region src/gateway/server-methods/tts.ts
|
|
@@ -11258,7 +11580,7 @@ const usageHandlers = {
|
|
|
11258
11580
|
const limit = typeof p.limit === "number" && Number.isFinite(p.limit) ? p.limit : 50;
|
|
11259
11581
|
const includeContextWeight = p.includeContextWeight ?? false;
|
|
11260
11582
|
const specificKey = typeof p.key === "string" ? p.key.trim() : null;
|
|
11261
|
-
const { store } = loadCombinedSessionStoreForGateway(config);
|
|
11583
|
+
const { storePath, store } = loadCombinedSessionStoreForGateway(config);
|
|
11262
11584
|
const now = Date.now();
|
|
11263
11585
|
const mergedEntries = [];
|
|
11264
11586
|
if (specificKey) {
|
|
@@ -11278,7 +11600,16 @@ const usageHandlers = {
|
|
|
11278
11600
|
const resolvedStoreKey = storeMatch?.key ?? storeByIdMatch?.key ?? specificKey;
|
|
11279
11601
|
const storeEntry = storeMatch?.entry ?? storeByIdMatch?.entry;
|
|
11280
11602
|
const sessionId = storeEntry?.sessionId ?? keyRest;
|
|
11281
|
-
|
|
11603
|
+
let sessionFile;
|
|
11604
|
+
try {
|
|
11605
|
+
sessionFile = resolveSessionFilePath(sessionId, storeEntry, resolveSessionFilePathOptions({
|
|
11606
|
+
storePath: storePath !== "(multiple)" ? storePath : void 0,
|
|
11607
|
+
agentId: agentIdFromKey
|
|
11608
|
+
}));
|
|
11609
|
+
} catch {
|
|
11610
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session reference: ${specificKey}`));
|
|
11611
|
+
return;
|
|
11612
|
+
}
|
|
11282
11613
|
try {
|
|
11283
11614
|
const stats = fs.statSync(sessionFile);
|
|
11284
11615
|
if (stats.isFile()) mergedEntries.push({
|
|
@@ -11386,11 +11717,13 @@ const usageHandlers = {
|
|
|
11386
11717
|
target.missingCostEntries += source.missingCostEntries;
|
|
11387
11718
|
};
|
|
11388
11719
|
for (const merged of limitedEntries) {
|
|
11720
|
+
const agentId = parseAgentSessionKey(merged.key)?.agentId;
|
|
11389
11721
|
const usage = await loadSessionCostSummary({
|
|
11390
11722
|
sessionId: merged.sessionId,
|
|
11391
11723
|
sessionEntry: merged.storeEntry,
|
|
11392
11724
|
sessionFile: merged.sessionFile,
|
|
11393
11725
|
config,
|
|
11726
|
+
agentId,
|
|
11394
11727
|
startMs,
|
|
11395
11728
|
endMs
|
|
11396
11729
|
});
|
|
@@ -11407,7 +11740,6 @@ const usageHandlers = {
|
|
|
11407
11740
|
aggregateTotals.cacheWriteCost += usage.cacheWriteCost;
|
|
11408
11741
|
aggregateTotals.missingCostEntries += usage.missingCostEntries;
|
|
11409
11742
|
}
|
|
11410
|
-
const agentId = parseAgentSessionKey(merged.key)?.agentId;
|
|
11411
11743
|
const channel = merged.storeEntry?.channel ?? merged.storeEntry?.origin?.provider;
|
|
11412
11744
|
const chatType = merged.storeEntry?.chatType ?? merged.storeEntry?.origin?.chatType;
|
|
11413
11745
|
if (usage) {
|
|
@@ -11604,15 +11936,27 @@ const usageHandlers = {
|
|
|
11604
11936
|
return;
|
|
11605
11937
|
}
|
|
11606
11938
|
const config = loadConfig();
|
|
11607
|
-
const { entry } = loadSessionEntry(key);
|
|
11939
|
+
const { entry, storePath } = loadSessionEntry(key);
|
|
11608
11940
|
const parsed = parseAgentSessionKey(key);
|
|
11609
11941
|
const agentId = parsed?.agentId;
|
|
11610
11942
|
const rawSessionId = parsed?.rest ?? key;
|
|
11943
|
+
const sessionId = entry?.sessionId ?? rawSessionId;
|
|
11944
|
+
let sessionFile;
|
|
11945
|
+
try {
|
|
11946
|
+
sessionFile = resolveSessionFilePath(sessionId, entry, resolveSessionFilePathOptions({
|
|
11947
|
+
storePath,
|
|
11948
|
+
agentId
|
|
11949
|
+
}));
|
|
11950
|
+
} catch {
|
|
11951
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
|
|
11952
|
+
return;
|
|
11953
|
+
}
|
|
11611
11954
|
const timeseries = await loadSessionUsageTimeSeries({
|
|
11612
|
-
sessionId
|
|
11955
|
+
sessionId,
|
|
11613
11956
|
sessionEntry: entry,
|
|
11614
|
-
sessionFile
|
|
11957
|
+
sessionFile,
|
|
11615
11958
|
config,
|
|
11959
|
+
agentId,
|
|
11616
11960
|
maxPoints: 200
|
|
11617
11961
|
});
|
|
11618
11962
|
if (!timeseries) {
|
|
@@ -11629,18 +11973,28 @@ const usageHandlers = {
|
|
|
11629
11973
|
}
|
|
11630
11974
|
const limit = typeof params?.limit === "number" && Number.isFinite(params.limit) ? Math.min(params.limit, 1e3) : 200;
|
|
11631
11975
|
const config = loadConfig();
|
|
11632
|
-
const { entry } = loadSessionEntry(key);
|
|
11976
|
+
const { entry, storePath } = loadSessionEntry(key);
|
|
11633
11977
|
const parsed = parseAgentSessionKey(key);
|
|
11634
11978
|
const agentId = parsed?.agentId;
|
|
11635
11979
|
const rawSessionId = parsed?.rest ?? key;
|
|
11636
11980
|
const sessionId = entry?.sessionId ?? rawSessionId;
|
|
11637
|
-
|
|
11638
|
-
|
|
11981
|
+
let sessionFile;
|
|
11982
|
+
try {
|
|
11983
|
+
sessionFile = resolveSessionFilePath(sessionId, entry, resolveSessionFilePathOptions({
|
|
11984
|
+
storePath,
|
|
11985
|
+
agentId
|
|
11986
|
+
}));
|
|
11987
|
+
} catch {
|
|
11988
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
|
|
11989
|
+
return;
|
|
11990
|
+
}
|
|
11991
|
+
const { loadSessionLogs } = await import("./session-cost-usage-B-tyjp76.js").then((n) => n.a);
|
|
11639
11992
|
respond(true, { logs: await loadSessionLogs({
|
|
11640
11993
|
sessionId,
|
|
11641
11994
|
sessionEntry: entry,
|
|
11642
11995
|
sessionFile,
|
|
11643
11996
|
config,
|
|
11997
|
+
agentId,
|
|
11644
11998
|
limit
|
|
11645
11999
|
}) ?? [] }, void 0);
|
|
11646
12000
|
}
|
|
@@ -12069,7 +12423,9 @@ const READ_METHODS = new Set([
|
|
|
12069
12423
|
"last-heartbeat",
|
|
12070
12424
|
"node.list",
|
|
12071
12425
|
"node.describe",
|
|
12072
|
-
"chat.history"
|
|
12426
|
+
"chat.history",
|
|
12427
|
+
"config.get",
|
|
12428
|
+
"talk.config"
|
|
12073
12429
|
]);
|
|
12074
12430
|
const WRITE_METHODS = new Set([
|
|
12075
12431
|
"send",
|
|
@@ -12457,6 +12813,7 @@ function normalizeHookMapping(mapping, index, transformsDir) {
|
|
|
12457
12813
|
action,
|
|
12458
12814
|
wakeMode,
|
|
12459
12815
|
name: mapping.name,
|
|
12816
|
+
agentId: mapping.agentId?.trim() || void 0,
|
|
12460
12817
|
sessionKey: mapping.sessionKey,
|
|
12461
12818
|
messageTemplate: mapping.messageTemplate,
|
|
12462
12819
|
textTemplate: mapping.textTemplate,
|
|
@@ -12495,6 +12852,7 @@ function buildActionFromMapping(mapping, ctx) {
|
|
|
12495
12852
|
kind: "agent",
|
|
12496
12853
|
message: renderTemplate(mapping.messageTemplate ?? "", ctx),
|
|
12497
12854
|
name: renderOptional(mapping.name, ctx),
|
|
12855
|
+
agentId: mapping.agentId,
|
|
12498
12856
|
wakeMode: mapping.wakeMode ?? "now",
|
|
12499
12857
|
sessionKey: renderOptional(mapping.sessionKey, ctx),
|
|
12500
12858
|
deliver: mapping.deliver,
|
|
@@ -12523,6 +12881,7 @@ function mergeAction(base, override, defaultAction) {
|
|
|
12523
12881
|
message: typeof override.message === "string" ? override.message : baseAgent?.message ?? "",
|
|
12524
12882
|
wakeMode: override.wakeMode === "next-heartbeat" ? "next-heartbeat" : baseAgent?.wakeMode ?? "now",
|
|
12525
12883
|
name: override.name ?? baseAgent?.name,
|
|
12884
|
+
agentId: override.agentId ?? baseAgent?.agentId,
|
|
12526
12885
|
sessionKey: override.sessionKey ?? baseAgent?.sessionKey,
|
|
12527
12886
|
deliver: typeof override.deliver === "boolean" ? override.deliver : baseAgent?.deliver,
|
|
12528
12887
|
allowUnsafeExternalContent: typeof override.allowUnsafeExternalContent === "boolean" ? override.allowUnsafeExternalContent : baseAgent?.allowUnsafeExternalContent,
|
|
@@ -12635,13 +12994,76 @@ function resolveHooksConfig(cfg) {
|
|
|
12635
12994
|
const withSlash = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
12636
12995
|
const trimmed = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") : withSlash;
|
|
12637
12996
|
if (trimmed === "/") throw new Error("hooks.path may not be '/'");
|
|
12997
|
+
const maxBodyBytes = cfg.hooks?.maxBodyBytes && cfg.hooks.maxBodyBytes > 0 ? cfg.hooks.maxBodyBytes : DEFAULT_HOOKS_MAX_BODY_BYTES;
|
|
12998
|
+
const mappings = resolveHookMappings(cfg.hooks);
|
|
12999
|
+
const defaultAgentId = resolveDefaultAgentId(cfg);
|
|
13000
|
+
const knownAgentIds = resolveKnownAgentIds(cfg, defaultAgentId);
|
|
13001
|
+
const allowedAgentIds = resolveAllowedAgentIds(cfg.hooks?.allowedAgentIds);
|
|
13002
|
+
const defaultSessionKey = resolveSessionKey$1(cfg.hooks?.defaultSessionKey);
|
|
13003
|
+
const allowedSessionKeyPrefixes = resolveAllowedSessionKeyPrefixes(cfg.hooks?.allowedSessionKeyPrefixes);
|
|
13004
|
+
if (defaultSessionKey && allowedSessionKeyPrefixes && !isSessionKeyAllowedByPrefix(defaultSessionKey, allowedSessionKeyPrefixes)) throw new Error("hooks.defaultSessionKey must match hooks.allowedSessionKeyPrefixes");
|
|
13005
|
+
if (!defaultSessionKey && allowedSessionKeyPrefixes && !isSessionKeyAllowedByPrefix("hook:example", allowedSessionKeyPrefixes)) throw new Error("hooks.allowedSessionKeyPrefixes must include 'hook:' when hooks.defaultSessionKey is unset");
|
|
12638
13006
|
return {
|
|
12639
13007
|
basePath: trimmed,
|
|
12640
13008
|
token,
|
|
12641
|
-
maxBodyBytes
|
|
12642
|
-
mappings
|
|
13009
|
+
maxBodyBytes,
|
|
13010
|
+
mappings,
|
|
13011
|
+
agentPolicy: {
|
|
13012
|
+
defaultAgentId,
|
|
13013
|
+
knownAgentIds,
|
|
13014
|
+
allowedAgentIds
|
|
13015
|
+
},
|
|
13016
|
+
sessionPolicy: {
|
|
13017
|
+
defaultSessionKey,
|
|
13018
|
+
allowRequestSessionKey: cfg.hooks?.allowRequestSessionKey === true,
|
|
13019
|
+
allowedSessionKeyPrefixes
|
|
13020
|
+
}
|
|
12643
13021
|
};
|
|
12644
13022
|
}
|
|
13023
|
+
function resolveKnownAgentIds(cfg, defaultAgentId) {
|
|
13024
|
+
const known = new Set(listAgentIds(cfg));
|
|
13025
|
+
known.add(defaultAgentId);
|
|
13026
|
+
return known;
|
|
13027
|
+
}
|
|
13028
|
+
function resolveAllowedAgentIds(raw) {
|
|
13029
|
+
if (!Array.isArray(raw)) return;
|
|
13030
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
13031
|
+
let hasWildcard = false;
|
|
13032
|
+
for (const entry of raw) {
|
|
13033
|
+
const trimmed = entry.trim();
|
|
13034
|
+
if (!trimmed) continue;
|
|
13035
|
+
if (trimmed === "*") {
|
|
13036
|
+
hasWildcard = true;
|
|
13037
|
+
break;
|
|
13038
|
+
}
|
|
13039
|
+
allowed.add(normalizeAgentId(trimmed));
|
|
13040
|
+
}
|
|
13041
|
+
if (hasWildcard) return;
|
|
13042
|
+
return allowed;
|
|
13043
|
+
}
|
|
13044
|
+
function resolveSessionKey$1(raw) {
|
|
13045
|
+
const value = raw?.trim();
|
|
13046
|
+
return value ? value : void 0;
|
|
13047
|
+
}
|
|
13048
|
+
function normalizeSessionKeyPrefix(raw) {
|
|
13049
|
+
const value = raw.trim().toLowerCase();
|
|
13050
|
+
return value ? value : void 0;
|
|
13051
|
+
}
|
|
13052
|
+
function resolveAllowedSessionKeyPrefixes(raw) {
|
|
13053
|
+
if (!Array.isArray(raw)) return;
|
|
13054
|
+
const set = /* @__PURE__ */ new Set();
|
|
13055
|
+
for (const prefix of raw) {
|
|
13056
|
+
const normalized = normalizeSessionKeyPrefix(prefix);
|
|
13057
|
+
if (!normalized) continue;
|
|
13058
|
+
set.add(normalized);
|
|
13059
|
+
}
|
|
13060
|
+
return set.size > 0 ? Array.from(set) : void 0;
|
|
13061
|
+
}
|
|
13062
|
+
function isSessionKeyAllowedByPrefix(sessionKey, prefixes) {
|
|
13063
|
+
const normalized = sessionKey.trim().toLowerCase();
|
|
13064
|
+
if (!normalized) return false;
|
|
13065
|
+
return prefixes.some((prefix) => normalized.startsWith(prefix));
|
|
13066
|
+
}
|
|
12645
13067
|
function extractHookToken(req) {
|
|
12646
13068
|
const auth = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
|
|
12647
13069
|
if (auth.toLowerCase().startsWith("bearer ")) {
|
|
@@ -12723,20 +13145,71 @@ function normalizeWakePayload(payload) {
|
|
|
12723
13145
|
}
|
|
12724
13146
|
};
|
|
12725
13147
|
}
|
|
12726
|
-
const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin) => plugin.id)];
|
|
12727
|
-
const getHookChannelSet = () => new Set(listHookChannelValues());
|
|
12728
|
-
const getHookChannelError = () => `channel must be ${listHookChannelValues().join("|")}`;
|
|
12729
|
-
function resolveHookChannel(raw) {
|
|
12730
|
-
if (raw === void 0) return "last";
|
|
12731
|
-
if (typeof raw !== "string") return null;
|
|
12732
|
-
const normalized = normalizeMessageChannel(raw);
|
|
12733
|
-
if (!normalized || !getHookChannelSet().has(normalized)) return null;
|
|
12734
|
-
return normalized;
|
|
12735
|
-
}
|
|
12736
|
-
function resolveHookDeliver(raw) {
|
|
12737
|
-
return raw !== false;
|
|
12738
|
-
}
|
|
12739
|
-
function
|
|
13148
|
+
const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin) => plugin.id)];
|
|
13149
|
+
const getHookChannelSet = () => new Set(listHookChannelValues());
|
|
13150
|
+
const getHookChannelError = () => `channel must be ${listHookChannelValues().join("|")}`;
|
|
13151
|
+
function resolveHookChannel(raw) {
|
|
13152
|
+
if (raw === void 0) return "last";
|
|
13153
|
+
if (typeof raw !== "string") return null;
|
|
13154
|
+
const normalized = normalizeMessageChannel(raw);
|
|
13155
|
+
if (!normalized || !getHookChannelSet().has(normalized)) return null;
|
|
13156
|
+
return normalized;
|
|
13157
|
+
}
|
|
13158
|
+
function resolveHookDeliver(raw) {
|
|
13159
|
+
return raw !== false;
|
|
13160
|
+
}
|
|
13161
|
+
function resolveHookTargetAgentId(hooksConfig, agentId) {
|
|
13162
|
+
const raw = agentId?.trim();
|
|
13163
|
+
if (!raw) return;
|
|
13164
|
+
const normalized = normalizeAgentId(raw);
|
|
13165
|
+
if (hooksConfig.agentPolicy.knownAgentIds.has(normalized)) return normalized;
|
|
13166
|
+
return hooksConfig.agentPolicy.defaultAgentId;
|
|
13167
|
+
}
|
|
13168
|
+
function isHookAgentAllowed(hooksConfig, agentId) {
|
|
13169
|
+
const raw = agentId?.trim();
|
|
13170
|
+
if (!raw) return true;
|
|
13171
|
+
const allowed = hooksConfig.agentPolicy.allowedAgentIds;
|
|
13172
|
+
if (allowed === void 0) return true;
|
|
13173
|
+
const resolved = resolveHookTargetAgentId(hooksConfig, raw);
|
|
13174
|
+
return resolved ? allowed.has(resolved) : false;
|
|
13175
|
+
}
|
|
13176
|
+
const getHookAgentPolicyError = () => "agentId is not allowed by hooks.allowedAgentIds";
|
|
13177
|
+
const getHookSessionKeyRequestPolicyError = () => "sessionKey is disabled for external /hooks/agent payloads; set hooks.allowRequestSessionKey=true to enable";
|
|
13178
|
+
const getHookSessionKeyPrefixError = (prefixes) => `sessionKey must start with one of: ${prefixes.join(", ")}`;
|
|
13179
|
+
function resolveHookSessionKey(params) {
|
|
13180
|
+
const requested = resolveSessionKey$1(params.sessionKey);
|
|
13181
|
+
if (requested) {
|
|
13182
|
+
if (params.source === "request" && !params.hooksConfig.sessionPolicy.allowRequestSessionKey) return {
|
|
13183
|
+
ok: false,
|
|
13184
|
+
error: getHookSessionKeyRequestPolicyError()
|
|
13185
|
+
};
|
|
13186
|
+
const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
|
|
13187
|
+
if (allowedPrefixes && !isSessionKeyAllowedByPrefix(requested, allowedPrefixes)) return {
|
|
13188
|
+
ok: false,
|
|
13189
|
+
error: getHookSessionKeyPrefixError(allowedPrefixes)
|
|
13190
|
+
};
|
|
13191
|
+
return {
|
|
13192
|
+
ok: true,
|
|
13193
|
+
value: requested
|
|
13194
|
+
};
|
|
13195
|
+
}
|
|
13196
|
+
const defaultSessionKey = params.hooksConfig.sessionPolicy.defaultSessionKey;
|
|
13197
|
+
if (defaultSessionKey) return {
|
|
13198
|
+
ok: true,
|
|
13199
|
+
value: defaultSessionKey
|
|
13200
|
+
};
|
|
13201
|
+
const generated = `hook:${(params.idFactory ?? randomUUID)()}`;
|
|
13202
|
+
const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
|
|
13203
|
+
if (allowedPrefixes && !isSessionKeyAllowedByPrefix(generated, allowedPrefixes)) return {
|
|
13204
|
+
ok: false,
|
|
13205
|
+
error: getHookSessionKeyPrefixError(allowedPrefixes)
|
|
13206
|
+
};
|
|
13207
|
+
return {
|
|
13208
|
+
ok: true,
|
|
13209
|
+
value: generated
|
|
13210
|
+
};
|
|
13211
|
+
}
|
|
13212
|
+
function normalizeAgentPayload(payload) {
|
|
12740
13213
|
const message = typeof payload.message === "string" ? payload.message.trim() : "";
|
|
12741
13214
|
if (!message) return {
|
|
12742
13215
|
ok: false,
|
|
@@ -12744,10 +13217,11 @@ function normalizeAgentPayload(payload, opts) {
|
|
|
12744
13217
|
};
|
|
12745
13218
|
const nameRaw = payload.name;
|
|
12746
13219
|
const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : "Hook";
|
|
13220
|
+
const agentIdRaw = payload.agentId;
|
|
13221
|
+
const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : void 0;
|
|
12747
13222
|
const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now";
|
|
12748
13223
|
const sessionKeyRaw = payload.sessionKey;
|
|
12749
|
-
const
|
|
12750
|
-
const sessionKey = typeof sessionKeyRaw === "string" && sessionKeyRaw.trim() ? sessionKeyRaw.trim() : `hook:${idFactory()}`;
|
|
13224
|
+
const sessionKey = typeof sessionKeyRaw === "string" && sessionKeyRaw.trim() ? sessionKeyRaw.trim() : void 0;
|
|
12751
13225
|
const channel = resolveHookChannel(payload.channel);
|
|
12752
13226
|
if (!channel) return {
|
|
12753
13227
|
ok: false,
|
|
@@ -12770,6 +13244,7 @@ function normalizeAgentPayload(payload, opts) {
|
|
|
12770
13244
|
value: {
|
|
12771
13245
|
message,
|
|
12772
13246
|
name,
|
|
13247
|
+
agentId,
|
|
12773
13248
|
wakeMode,
|
|
12774
13249
|
sessionKey,
|
|
12775
13250
|
deliver,
|
|
@@ -12787,7 +13262,7 @@ function normalizeAgentPayload(payload, opts) {
|
|
|
12787
13262
|
async function startBrowserControlServerIfEnabled() {
|
|
12788
13263
|
if (isTruthyEnvValue(process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER)) return null;
|
|
12789
13264
|
const override = process.env.OPENCLAW_BROWSER_CONTROL_MODULE?.trim();
|
|
12790
|
-
const mod = override ? await import(override) : await import("./control-service-
|
|
13265
|
+
const mod = override ? await import(override) : await import("./control-service-COF59GQe.js").then((n) => n.t);
|
|
12791
13266
|
const start = typeof mod.startBrowserControlServiceFromConfig === "function" ? mod.startBrowserControlServiceFromConfig : mod.startBrowserControlServerFromConfig;
|
|
12792
13267
|
const stop = typeof mod.stopBrowserControlService === "function" ? mod.stopBrowserControlService : mod.stopBrowserControlServer;
|
|
12793
13268
|
if (!start) return null;
|
|
@@ -12931,6 +13406,62 @@ async function resolveGatewayRuntimeConfig(params) {
|
|
|
12931
13406
|
};
|
|
12932
13407
|
}
|
|
12933
13408
|
|
|
13409
|
+
//#endregion
|
|
13410
|
+
//#region src/infra/fs-safe.ts
|
|
13411
|
+
var SafeOpenError = class extends Error {
|
|
13412
|
+
constructor(code, message) {
|
|
13413
|
+
super(message);
|
|
13414
|
+
this.code = code;
|
|
13415
|
+
this.name = "SafeOpenError";
|
|
13416
|
+
}
|
|
13417
|
+
};
|
|
13418
|
+
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
13419
|
+
const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
|
|
13420
|
+
const isNodeError = (err) => Boolean(err && typeof err === "object" && "code" in err);
|
|
13421
|
+
const isNotFoundError = (err) => isNodeError(err) && typeof err.code === "string" && NOT_FOUND_CODES.has(err.code);
|
|
13422
|
+
const isSymlinkOpenError = (err) => isNodeError(err) && (err.code === "ELOOP" || err.code === "EINVAL" || err.code === "ENOTSUP");
|
|
13423
|
+
async function openFileWithinRoot(params) {
|
|
13424
|
+
let rootReal;
|
|
13425
|
+
try {
|
|
13426
|
+
rootReal = await fs$1.realpath(params.rootDir);
|
|
13427
|
+
} catch (err) {
|
|
13428
|
+
if (isNotFoundError(err)) throw new SafeOpenError("not-found", "root dir not found");
|
|
13429
|
+
throw err;
|
|
13430
|
+
}
|
|
13431
|
+
const rootWithSep = ensureTrailingSep(rootReal);
|
|
13432
|
+
const resolved = path.resolve(rootWithSep, params.relativePath);
|
|
13433
|
+
if (!resolved.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
|
|
13434
|
+
const supportsNoFollow = process.platform !== "win32" && "O_NOFOLLOW" in constants;
|
|
13435
|
+
const flags = constants.O_RDONLY | (supportsNoFollow ? constants.O_NOFOLLOW : 0);
|
|
13436
|
+
let handle;
|
|
13437
|
+
try {
|
|
13438
|
+
handle = await fs$1.open(resolved, flags);
|
|
13439
|
+
} catch (err) {
|
|
13440
|
+
if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
|
|
13441
|
+
if (isSymlinkOpenError(err)) throw new SafeOpenError("invalid-path", "symlink open blocked");
|
|
13442
|
+
throw err;
|
|
13443
|
+
}
|
|
13444
|
+
try {
|
|
13445
|
+
if ((await fs$1.lstat(resolved).catch(() => null))?.isSymbolicLink()) throw new SafeOpenError("invalid-path", "symlink not allowed");
|
|
13446
|
+
const realPath = await fs$1.realpath(resolved);
|
|
13447
|
+
if (!realPath.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
|
|
13448
|
+
const stat = await handle.stat();
|
|
13449
|
+
if (!stat.isFile()) throw new SafeOpenError("invalid-path", "not a file");
|
|
13450
|
+
const realStat = await fs$1.stat(realPath);
|
|
13451
|
+
if (stat.ino !== realStat.ino || stat.dev !== realStat.dev) throw new SafeOpenError("invalid-path", "path mismatch");
|
|
13452
|
+
return {
|
|
13453
|
+
handle,
|
|
13454
|
+
realPath,
|
|
13455
|
+
stat
|
|
13456
|
+
};
|
|
13457
|
+
} catch (err) {
|
|
13458
|
+
await handle.close().catch(() => {});
|
|
13459
|
+
if (err instanceof SafeOpenError) throw err;
|
|
13460
|
+
if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
|
|
13461
|
+
throw err;
|
|
13462
|
+
}
|
|
13463
|
+
}
|
|
13464
|
+
|
|
12934
13465
|
//#endregion
|
|
12935
13466
|
//#region src/canvas-host/a2ui.ts
|
|
12936
13467
|
const A2UI_PATH = "/__openclaw__/a2ui";
|
|
@@ -12970,24 +13501,29 @@ function normalizeUrlPath$1(rawPath) {
|
|
|
12970
13501
|
const normalized = path.posix.normalize(decoded);
|
|
12971
13502
|
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
12972
13503
|
}
|
|
12973
|
-
async function
|
|
13504
|
+
async function resolveA2uiFile(rootReal, urlPath) {
|
|
12974
13505
|
const normalized = normalizeUrlPath$1(urlPath);
|
|
12975
13506
|
const rel = normalized.replace(/^\/+/, "");
|
|
12976
13507
|
if (rel.split("/").some((p) => p === "..")) return null;
|
|
12977
|
-
|
|
12978
|
-
|
|
13508
|
+
const tryOpen = async (relative) => {
|
|
13509
|
+
try {
|
|
13510
|
+
return await openFileWithinRoot({
|
|
13511
|
+
rootDir: rootReal,
|
|
13512
|
+
relativePath: relative
|
|
13513
|
+
});
|
|
13514
|
+
} catch (err) {
|
|
13515
|
+
if (err instanceof SafeOpenError) return null;
|
|
13516
|
+
throw err;
|
|
13517
|
+
}
|
|
13518
|
+
};
|
|
13519
|
+
if (normalized.endsWith("/")) return await tryOpen(path.posix.join(rel, "index.html"));
|
|
13520
|
+
const candidate = path.join(rootReal, rel);
|
|
12979
13521
|
try {
|
|
12980
|
-
|
|
13522
|
+
const st = await fs$1.lstat(candidate);
|
|
13523
|
+
if (st.isSymbolicLink()) return null;
|
|
13524
|
+
if (st.isDirectory()) return await tryOpen(path.posix.join(rel, "index.html"));
|
|
12981
13525
|
} catch {}
|
|
12982
|
-
|
|
12983
|
-
try {
|
|
12984
|
-
if ((await fs$1.lstat(candidate)).isSymbolicLink()) return null;
|
|
12985
|
-
const real = await fs$1.realpath(candidate);
|
|
12986
|
-
if (!real.startsWith(rootPrefix)) return null;
|
|
12987
|
-
return real;
|
|
12988
|
-
} catch {
|
|
12989
|
-
return null;
|
|
12990
|
-
}
|
|
13526
|
+
return await tryOpen(rel);
|
|
12991
13527
|
}
|
|
12992
13528
|
function injectCanvasLiveReload(html) {
|
|
12993
13529
|
const snippet = `
|
|
@@ -13063,80 +13599,33 @@ async function handleA2uiHttpRequest(req, res) {
|
|
|
13063
13599
|
res.end("A2UI assets not found");
|
|
13064
13600
|
return true;
|
|
13065
13601
|
}
|
|
13066
|
-
const
|
|
13067
|
-
if (!
|
|
13602
|
+
const result = await resolveA2uiFile(a2uiRootReal, url.pathname.slice(basePath.length) || "/");
|
|
13603
|
+
if (!result) {
|
|
13068
13604
|
res.statusCode = 404;
|
|
13069
13605
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
13070
13606
|
res.end("not found");
|
|
13071
13607
|
return true;
|
|
13072
13608
|
}
|
|
13073
|
-
const lower = filePath.toLowerCase();
|
|
13074
|
-
const mime = lower.endsWith(".html") || lower.endsWith(".htm") ? "text/html" : await detectMime({ filePath }) ?? "application/octet-stream";
|
|
13075
|
-
res.setHeader("Cache-Control", "no-store");
|
|
13076
|
-
if (mime === "text/html") {
|
|
13077
|
-
const html = await fs$1.readFile(filePath, "utf8");
|
|
13078
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13079
|
-
res.end(injectCanvasLiveReload(html));
|
|
13080
|
-
return true;
|
|
13081
|
-
}
|
|
13082
|
-
res.setHeader("Content-Type", mime);
|
|
13083
|
-
res.end(await fs$1.readFile(filePath));
|
|
13084
|
-
return true;
|
|
13085
|
-
}
|
|
13086
|
-
|
|
13087
|
-
//#endregion
|
|
13088
|
-
//#region src/infra/fs-safe.ts
|
|
13089
|
-
var SafeOpenError = class extends Error {
|
|
13090
|
-
constructor(code, message) {
|
|
13091
|
-
super(message);
|
|
13092
|
-
this.code = code;
|
|
13093
|
-
this.name = "SafeOpenError";
|
|
13094
|
-
}
|
|
13095
|
-
};
|
|
13096
|
-
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
13097
|
-
const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
|
|
13098
|
-
const isNodeError = (err) => Boolean(err && typeof err === "object" && "code" in err);
|
|
13099
|
-
const isNotFoundError = (err) => isNodeError(err) && typeof err.code === "string" && NOT_FOUND_CODES.has(err.code);
|
|
13100
|
-
const isSymlinkOpenError = (err) => isNodeError(err) && (err.code === "ELOOP" || err.code === "EINVAL" || err.code === "ENOTSUP");
|
|
13101
|
-
async function openFileWithinRoot(params) {
|
|
13102
|
-
let rootReal;
|
|
13103
|
-
try {
|
|
13104
|
-
rootReal = await fs$1.realpath(params.rootDir);
|
|
13105
|
-
} catch (err) {
|
|
13106
|
-
if (isNotFoundError(err)) throw new SafeOpenError("not-found", "root dir not found");
|
|
13107
|
-
throw err;
|
|
13108
|
-
}
|
|
13109
|
-
const rootWithSep = ensureTrailingSep(rootReal);
|
|
13110
|
-
const resolved = path.resolve(rootWithSep, params.relativePath);
|
|
13111
|
-
if (!resolved.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
|
|
13112
|
-
const supportsNoFollow = process.platform !== "win32" && "O_NOFOLLOW" in constants;
|
|
13113
|
-
const flags = constants.O_RDONLY | (supportsNoFollow ? constants.O_NOFOLLOW : 0);
|
|
13114
|
-
let handle;
|
|
13115
|
-
try {
|
|
13116
|
-
handle = await fs$1.open(resolved, flags);
|
|
13117
|
-
} catch (err) {
|
|
13118
|
-
if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
|
|
13119
|
-
if (isSymlinkOpenError(err)) throw new SafeOpenError("invalid-path", "symlink open blocked");
|
|
13120
|
-
throw err;
|
|
13121
|
-
}
|
|
13122
13609
|
try {
|
|
13123
|
-
|
|
13124
|
-
const
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
13610
|
+
const lower = result.realPath.toLowerCase();
|
|
13611
|
+
const mime = lower.endsWith(".html") || lower.endsWith(".htm") ? "text/html" : await detectMime({ filePath: result.realPath }) ?? "application/octet-stream";
|
|
13612
|
+
res.setHeader("Cache-Control", "no-store");
|
|
13613
|
+
if (req.method === "HEAD") {
|
|
13614
|
+
res.setHeader("Content-Type", mime === "text/html" ? "text/html; charset=utf-8" : mime);
|
|
13615
|
+
res.end();
|
|
13616
|
+
return true;
|
|
13617
|
+
}
|
|
13618
|
+
if (mime === "text/html") {
|
|
13619
|
+
const buf = await result.handle.readFile({ encoding: "utf8" });
|
|
13620
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13621
|
+
res.end(injectCanvasLiveReload(buf));
|
|
13622
|
+
return true;
|
|
13623
|
+
}
|
|
13624
|
+
res.setHeader("Content-Type", mime);
|
|
13625
|
+
res.end(await result.handle.readFile());
|
|
13626
|
+
return true;
|
|
13627
|
+
} finally {
|
|
13628
|
+
await result.handle.close().catch(() => {});
|
|
13140
13629
|
}
|
|
13141
13630
|
}
|
|
13142
13631
|
|
|
@@ -13289,7 +13778,7 @@ async function prepareCanvasRoot(rootDir) {
|
|
|
13289
13778
|
return rootReal;
|
|
13290
13779
|
}
|
|
13291
13780
|
function resolveDefaultCanvasRoot() {
|
|
13292
|
-
const candidates = [path.join(
|
|
13781
|
+
const candidates = [path.join(resolveStateDir(), "canvas")];
|
|
13293
13782
|
return candidates.find((dir) => {
|
|
13294
13783
|
try {
|
|
13295
13784
|
return fsSync.statSync(dir).isDirectory();
|
|
@@ -13833,6 +14322,20 @@ function sendUnauthorized(res) {
|
|
|
13833
14322
|
type: "unauthorized"
|
|
13834
14323
|
} });
|
|
13835
14324
|
}
|
|
14325
|
+
function sendRateLimited(res, retryAfterMs) {
|
|
14326
|
+
if (retryAfterMs && retryAfterMs > 0) res.setHeader("Retry-After", String(Math.ceil(retryAfterMs / 1e3)));
|
|
14327
|
+
sendJson$1(res, 429, { error: {
|
|
14328
|
+
message: "Too many failed authentication attempts. Please try again later.",
|
|
14329
|
+
type: "rate_limited"
|
|
14330
|
+
} });
|
|
14331
|
+
}
|
|
14332
|
+
function sendGatewayAuthFailure(res, authResult) {
|
|
14333
|
+
if (authResult.rateLimited) {
|
|
14334
|
+
sendRateLimited(res, authResult.retryAfterMs);
|
|
14335
|
+
return;
|
|
14336
|
+
}
|
|
14337
|
+
sendUnauthorized(res);
|
|
14338
|
+
}
|
|
13836
14339
|
function sendInvalidRequest(res, message) {
|
|
13837
14340
|
sendJson$1(res, 400, { error: {
|
|
13838
14341
|
message,
|
|
@@ -13992,16 +14495,18 @@ async function handleOpenAiHttpRequest(req, res, opts) {
|
|
|
13992
14495
|
return true;
|
|
13993
14496
|
}
|
|
13994
14497
|
const token = getBearerToken(req);
|
|
13995
|
-
|
|
14498
|
+
const authResult = await authorizeGatewayConnect({
|
|
13996
14499
|
auth: opts.auth,
|
|
13997
14500
|
connectAuth: {
|
|
13998
14501
|
token,
|
|
13999
14502
|
password: token
|
|
14000
14503
|
},
|
|
14001
14504
|
req,
|
|
14002
|
-
trustedProxies: opts.trustedProxies
|
|
14003
|
-
|
|
14004
|
-
|
|
14505
|
+
trustedProxies: opts.trustedProxies,
|
|
14506
|
+
rateLimiter: opts.rateLimiter
|
|
14507
|
+
});
|
|
14508
|
+
if (!authResult.ok) {
|
|
14509
|
+
sendGatewayAuthFailure(res, authResult);
|
|
14005
14510
|
return true;
|
|
14006
14511
|
}
|
|
14007
14512
|
const body = await readJsonBodyOrError(req, res, opts.maxBodyBytes ?? 1024 * 1024);
|
|
@@ -14060,8 +14565,9 @@ async function handleOpenAiHttpRequest(req, res, opts) {
|
|
|
14060
14565
|
}
|
|
14061
14566
|
});
|
|
14062
14567
|
} catch (err) {
|
|
14568
|
+
logWarn(`openai-compat: chat completion failed: ${String(err)}`);
|
|
14063
14569
|
sendJson$1(res, 500, { error: {
|
|
14064
|
-
message:
|
|
14570
|
+
message: "internal error",
|
|
14065
14571
|
type: "api_error"
|
|
14066
14572
|
} });
|
|
14067
14573
|
}
|
|
@@ -14162,6 +14668,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
|
|
|
14162
14668
|
});
|
|
14163
14669
|
}
|
|
14164
14670
|
} catch (err) {
|
|
14671
|
+
logWarn(`openai-compat: streaming chat completion failed: ${String(err)}`);
|
|
14165
14672
|
if (closed) return;
|
|
14166
14673
|
writeSse(res, {
|
|
14167
14674
|
id: runId,
|
|
@@ -14170,7 +14677,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
|
|
|
14170
14677
|
model,
|
|
14171
14678
|
choices: [{
|
|
14172
14679
|
index: 0,
|
|
14173
|
-
delta: { content:
|
|
14680
|
+
delta: { content: "Error: internal error" },
|
|
14174
14681
|
finish_reason: "stop"
|
|
14175
14682
|
}]
|
|
14176
14683
|
});
|
|
@@ -14438,6 +14945,7 @@ const OutputTextDoneEventSchema = z.object({
|
|
|
14438
14945
|
//#endregion
|
|
14439
14946
|
//#region src/gateway/openresponses-http.ts
|
|
14440
14947
|
const DEFAULT_BODY_BYTES$1 = 20 * 1024 * 1024;
|
|
14948
|
+
const DEFAULT_MAX_URL_PARTS = 8;
|
|
14441
14949
|
function writeSseEvent(res, event) {
|
|
14442
14950
|
res.write(`event: ${event.type}\n`);
|
|
14443
14951
|
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
@@ -14450,13 +14958,20 @@ function extractTextContent(content) {
|
|
|
14450
14958
|
return "";
|
|
14451
14959
|
}).filter(Boolean).join("\n");
|
|
14452
14960
|
}
|
|
14961
|
+
function normalizeHostnameAllowlist(values) {
|
|
14962
|
+
if (!values || values.length === 0) return;
|
|
14963
|
+
const normalized = values.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
14964
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
14965
|
+
}
|
|
14453
14966
|
function resolveResponsesLimits(config) {
|
|
14454
14967
|
const files = config?.files;
|
|
14455
14968
|
const images = config?.images;
|
|
14456
14969
|
return {
|
|
14457
14970
|
maxBodyBytes: config?.maxBodyBytes ?? DEFAULT_BODY_BYTES$1,
|
|
14971
|
+
maxUrlParts: typeof config?.maxUrlParts === "number" ? Math.max(0, Math.floor(config.maxUrlParts)) : DEFAULT_MAX_URL_PARTS,
|
|
14458
14972
|
files: {
|
|
14459
14973
|
allowUrl: files?.allowUrl ?? true,
|
|
14974
|
+
urlAllowlist: normalizeHostnameAllowlist(files?.urlAllowlist),
|
|
14460
14975
|
allowedMimes: normalizeMimeList(files?.allowedMimes, DEFAULT_INPUT_FILE_MIMES),
|
|
14461
14976
|
maxBytes: files?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES,
|
|
14462
14977
|
maxChars: files?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS,
|
|
@@ -14470,6 +14985,7 @@ function resolveResponsesLimits(config) {
|
|
|
14470
14985
|
},
|
|
14471
14986
|
images: {
|
|
14472
14987
|
allowUrl: images?.allowUrl ?? true,
|
|
14988
|
+
urlAllowlist: normalizeHostnameAllowlist(images?.urlAllowlist),
|
|
14473
14989
|
allowedMimes: normalizeMimeList(images?.allowedMimes, DEFAULT_INPUT_IMAGE_MIMES),
|
|
14474
14990
|
maxBytes: images?.maxBytes ?? DEFAULT_INPUT_IMAGE_MAX_BYTES,
|
|
14475
14991
|
maxRedirects: images?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS,
|
|
@@ -14621,16 +15137,18 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14621
15137
|
return true;
|
|
14622
15138
|
}
|
|
14623
15139
|
const token = getBearerToken(req);
|
|
14624
|
-
|
|
15140
|
+
const authResult = await authorizeGatewayConnect({
|
|
14625
15141
|
auth: opts.auth,
|
|
14626
15142
|
connectAuth: {
|
|
14627
15143
|
token,
|
|
14628
15144
|
password: token
|
|
14629
15145
|
},
|
|
14630
15146
|
req,
|
|
14631
|
-
trustedProxies: opts.trustedProxies
|
|
14632
|
-
|
|
14633
|
-
|
|
15147
|
+
trustedProxies: opts.trustedProxies,
|
|
15148
|
+
rateLimiter: opts.rateLimiter
|
|
15149
|
+
});
|
|
15150
|
+
if (!authResult.ok) {
|
|
15151
|
+
sendGatewayAuthFailure(res, authResult);
|
|
14634
15152
|
return true;
|
|
14635
15153
|
}
|
|
14636
15154
|
const limits = resolveResponsesLimits(opts.config);
|
|
@@ -14651,6 +15169,11 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14651
15169
|
const user = payload.user;
|
|
14652
15170
|
let images = [];
|
|
14653
15171
|
let fileContexts = [];
|
|
15172
|
+
let urlParts = 0;
|
|
15173
|
+
const markUrlPart = () => {
|
|
15174
|
+
urlParts += 1;
|
|
15175
|
+
if (urlParts > limits.maxUrlParts) throw new Error(`Too many URL-based input sources: ${urlParts} (limit: ${limits.maxUrlParts})`);
|
|
15176
|
+
};
|
|
14654
15177
|
try {
|
|
14655
15178
|
if (Array.isArray(payload.input)) {
|
|
14656
15179
|
for (const item of payload.input) if (item.type === "message" && typeof item.content !== "string") for (const part of item.content) {
|
|
@@ -14658,6 +15181,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14658
15181
|
const source = part.source;
|
|
14659
15182
|
const sourceType = source.type === "base64" || source.type === "url" ? source.type : void 0;
|
|
14660
15183
|
if (!sourceType) throw new Error("input_image must have 'source.url' or 'source.data'");
|
|
15184
|
+
if (sourceType === "url") markUrlPart();
|
|
14661
15185
|
const image = await extractImageContentFromSource({
|
|
14662
15186
|
type: sourceType,
|
|
14663
15187
|
url: source.url,
|
|
@@ -14671,6 +15195,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14671
15195
|
const source = part.source;
|
|
14672
15196
|
const sourceType = source.type === "base64" || source.type === "url" ? source.type : void 0;
|
|
14673
15197
|
if (!sourceType) throw new Error("input_file must have 'source.url' or 'source.data'");
|
|
15198
|
+
if (sourceType === "url") markUrlPart();
|
|
14674
15199
|
const file = await extractFileContentFromSource({
|
|
14675
15200
|
source: {
|
|
14676
15201
|
type: sourceType,
|
|
@@ -14688,8 +15213,9 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14688
15213
|
}
|
|
14689
15214
|
}
|
|
14690
15215
|
} catch (err) {
|
|
15216
|
+
logWarn(`openresponses: request parsing failed: ${String(err)}`);
|
|
14691
15217
|
sendJson$1(res, 400, { error: {
|
|
14692
|
-
message:
|
|
15218
|
+
message: "invalid request",
|
|
14693
15219
|
type: "invalid_request_error"
|
|
14694
15220
|
} });
|
|
14695
15221
|
return true;
|
|
@@ -14705,8 +15231,9 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14705
15231
|
resolvedClientTools = toolChoiceResult.tools;
|
|
14706
15232
|
toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
|
|
14707
15233
|
} catch (err) {
|
|
15234
|
+
logWarn(`openresponses: tool configuration failed: ${String(err)}`);
|
|
14708
15235
|
sendJson$1(res, 400, { error: {
|
|
14709
|
-
message:
|
|
15236
|
+
message: "invalid tool configuration",
|
|
14710
15237
|
type: "invalid_request_error"
|
|
14711
15238
|
} });
|
|
14712
15239
|
return true;
|
|
@@ -14787,6 +15314,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14787
15314
|
usage
|
|
14788
15315
|
}));
|
|
14789
15316
|
} catch (err) {
|
|
15317
|
+
logWarn(`openresponses: non-stream response failed: ${String(err)}`);
|
|
14790
15318
|
sendJson$1(res, 500, createResponseResource({
|
|
14791
15319
|
id: responseId,
|
|
14792
15320
|
model,
|
|
@@ -14794,7 +15322,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
14794
15322
|
output: [],
|
|
14795
15323
|
error: {
|
|
14796
15324
|
code: "api_error",
|
|
14797
|
-
message:
|
|
15325
|
+
message: "internal error"
|
|
14798
15326
|
}
|
|
14799
15327
|
}));
|
|
14800
15328
|
}
|
|
@@ -15025,6 +15553,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
15025
15553
|
});
|
|
15026
15554
|
}
|
|
15027
15555
|
} catch (err) {
|
|
15556
|
+
logWarn(`openresponses: streaming response failed: ${String(err)}`);
|
|
15028
15557
|
if (closed) return;
|
|
15029
15558
|
finalUsage = finalUsage ?? createEmptyUsage();
|
|
15030
15559
|
writeSseEvent(res, {
|
|
@@ -15036,7 +15565,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
15036
15565
|
output: [],
|
|
15037
15566
|
error: {
|
|
15038
15567
|
code: "api_error",
|
|
15039
|
-
message:
|
|
15568
|
+
message: "internal error"
|
|
15040
15569
|
},
|
|
15041
15570
|
usage: finalUsage
|
|
15042
15571
|
})
|
|
@@ -15061,6 +15590,17 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
|
|
|
15061
15590
|
//#region src/gateway/tools-invoke-http.ts
|
|
15062
15591
|
const DEFAULT_BODY_BYTES = 2 * 1024 * 1024;
|
|
15063
15592
|
const MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]);
|
|
15593
|
+
/**
|
|
15594
|
+
* Tools denied via HTTP /tools/invoke regardless of session policy.
|
|
15595
|
+
* Prevents RCE and privilege escalation from HTTP API surface.
|
|
15596
|
+
* Configurable via gateway.tools.{deny,allow} in openclaw.json.
|
|
15597
|
+
*/
|
|
15598
|
+
const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [
|
|
15599
|
+
"sessions_spawn",
|
|
15600
|
+
"sessions_send",
|
|
15601
|
+
"gateway",
|
|
15602
|
+
"whatsapp_login"
|
|
15603
|
+
];
|
|
15064
15604
|
function resolveSessionKeyFromBody(body) {
|
|
15065
15605
|
if (typeof body.sessionKey === "string" && body.sessionKey.trim()) return body.sessionKey.trim();
|
|
15066
15606
|
}
|
|
@@ -15088,6 +15628,15 @@ function mergeActionIntoArgsIfSupported(params) {
|
|
|
15088
15628
|
action
|
|
15089
15629
|
};
|
|
15090
15630
|
}
|
|
15631
|
+
function getErrorMessage(err) {
|
|
15632
|
+
if (err instanceof Error) return err.message || String(err);
|
|
15633
|
+
if (typeof err === "string") return err;
|
|
15634
|
+
return String(err);
|
|
15635
|
+
}
|
|
15636
|
+
function isToolInputError(err) {
|
|
15637
|
+
if (err instanceof ToolInputError) return true;
|
|
15638
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "ToolInputError";
|
|
15639
|
+
}
|
|
15091
15640
|
async function handleToolsInvokeHttpRequest(req, res, opts) {
|
|
15092
15641
|
if (new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`).pathname !== "/tools/invoke") return false;
|
|
15093
15642
|
if (req.method !== "POST") {
|
|
@@ -15096,16 +15645,18 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
|
|
|
15096
15645
|
}
|
|
15097
15646
|
const cfg = loadConfig();
|
|
15098
15647
|
const token = getBearerToken(req);
|
|
15099
|
-
|
|
15648
|
+
const authResult = await authorizeGatewayConnect({
|
|
15100
15649
|
auth: opts.auth,
|
|
15101
15650
|
connectAuth: token ? {
|
|
15102
15651
|
token,
|
|
15103
15652
|
password: token
|
|
15104
15653
|
} : null,
|
|
15105
15654
|
req,
|
|
15106
|
-
trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies
|
|
15107
|
-
|
|
15108
|
-
|
|
15655
|
+
trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies,
|
|
15656
|
+
rateLimiter: opts.rateLimiter
|
|
15657
|
+
});
|
|
15658
|
+
if (!authResult.ok) {
|
|
15659
|
+
sendGatewayAuthFailure(res, authResult);
|
|
15109
15660
|
return true;
|
|
15110
15661
|
}
|
|
15111
15662
|
const bodyUnknown = await readJsonBodyOrError(req, res, opts.maxBodyBytes ?? DEFAULT_BODY_BYTES);
|
|
@@ -15199,7 +15750,11 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
|
|
|
15199
15750
|
const agentFiltered = agentPolicyExpanded ? filterToolsByPolicy(globalProviderFiltered, agentPolicyExpanded) : globalProviderFiltered;
|
|
15200
15751
|
const agentProviderFiltered = agentProviderExpanded ? filterToolsByPolicy(agentFiltered, agentProviderExpanded) : agentFiltered;
|
|
15201
15752
|
const groupFiltered = groupPolicyExpanded ? filterToolsByPolicy(agentProviderFiltered, groupPolicyExpanded) : agentProviderFiltered;
|
|
15202
|
-
const
|
|
15753
|
+
const subagentFiltered = subagentPolicyExpanded ? filterToolsByPolicy(groupFiltered, subagentPolicyExpanded) : groupFiltered;
|
|
15754
|
+
const gatewayToolsCfg = cfg.gateway?.tools;
|
|
15755
|
+
const gatewayDenyNames = DEFAULT_GATEWAY_HTTP_TOOL_DENY.filter((name) => !gatewayToolsCfg?.allow?.includes(name)).concat(Array.isArray(gatewayToolsCfg?.deny) ? gatewayToolsCfg.deny : []);
|
|
15756
|
+
const gatewayDenySet = new Set(gatewayDenyNames);
|
|
15757
|
+
const tool = subagentFiltered.filter((t) => !gatewayDenySet.has(t.name)).find((t) => t.name === toolName);
|
|
15203
15758
|
if (!tool) {
|
|
15204
15759
|
sendJson$1(res, 404, {
|
|
15205
15760
|
ok: false,
|
|
@@ -15221,11 +15776,22 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
|
|
|
15221
15776
|
result: await tool.execute?.(`http-${Date.now()}`, toolArgs)
|
|
15222
15777
|
});
|
|
15223
15778
|
} catch (err) {
|
|
15224
|
-
|
|
15779
|
+
if (isToolInputError(err)) {
|
|
15780
|
+
sendJson$1(res, 400, {
|
|
15781
|
+
ok: false,
|
|
15782
|
+
error: {
|
|
15783
|
+
type: "tool_error",
|
|
15784
|
+
message: getErrorMessage(err) || "invalid tool arguments"
|
|
15785
|
+
}
|
|
15786
|
+
});
|
|
15787
|
+
return true;
|
|
15788
|
+
}
|
|
15789
|
+
logWarn(`tools-invoke: tool execution failed: ${String(err)}`);
|
|
15790
|
+
sendJson$1(res, 500, {
|
|
15225
15791
|
ok: false,
|
|
15226
15792
|
error: {
|
|
15227
15793
|
type: "tool_error",
|
|
15228
|
-
message:
|
|
15794
|
+
message: "tool execution failed"
|
|
15229
15795
|
}
|
|
15230
15796
|
});
|
|
15231
15797
|
}
|
|
@@ -15234,6 +15800,9 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
|
|
|
15234
15800
|
|
|
15235
15801
|
//#endregion
|
|
15236
15802
|
//#region src/gateway/server-http.ts
|
|
15803
|
+
const HOOK_AUTH_FAILURE_LIMIT = 20;
|
|
15804
|
+
const HOOK_AUTH_FAILURE_WINDOW_MS = 6e4;
|
|
15805
|
+
const HOOK_AUTH_FAILURE_TRACK_MAX = 2048;
|
|
15237
15806
|
function sendJson(res, status, body) {
|
|
15238
15807
|
res.statusCode = status;
|
|
15239
15808
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
@@ -15247,11 +15816,12 @@ function hasAuthorizedWsClientForIp(clients, clientIp) {
|
|
|
15247
15816
|
return false;
|
|
15248
15817
|
}
|
|
15249
15818
|
async function authorizeCanvasRequest(params) {
|
|
15250
|
-
const { req, auth, trustedProxies, clients } = params;
|
|
15251
|
-
if (isLocalDirectRequest(req, trustedProxies)) return true;
|
|
15819
|
+
const { req, auth, trustedProxies, clients, rateLimiter } = params;
|
|
15820
|
+
if (isLocalDirectRequest(req, trustedProxies)) return { ok: true };
|
|
15821
|
+
let lastAuthFailure = null;
|
|
15252
15822
|
const token = getBearerToken(req);
|
|
15253
15823
|
if (token) {
|
|
15254
|
-
|
|
15824
|
+
const authResult = await authorizeGatewayConnect({
|
|
15255
15825
|
auth: {
|
|
15256
15826
|
...auth,
|
|
15257
15827
|
allowTailscale: false
|
|
@@ -15261,8 +15831,11 @@ async function authorizeCanvasRequest(params) {
|
|
|
15261
15831
|
password: token
|
|
15262
15832
|
},
|
|
15263
15833
|
req,
|
|
15264
|
-
trustedProxies
|
|
15265
|
-
|
|
15834
|
+
trustedProxies,
|
|
15835
|
+
rateLimiter
|
|
15836
|
+
});
|
|
15837
|
+
if (authResult.ok) return authResult;
|
|
15838
|
+
lastAuthFailure = authResult;
|
|
15266
15839
|
}
|
|
15267
15840
|
const clientIp = resolveGatewayClientIp({
|
|
15268
15841
|
remoteAddr: req.socket?.remoteAddress ?? "",
|
|
@@ -15270,11 +15843,65 @@ async function authorizeCanvasRequest(params) {
|
|
|
15270
15843
|
realIp: getHeader(req, "x-real-ip"),
|
|
15271
15844
|
trustedProxies
|
|
15272
15845
|
});
|
|
15273
|
-
if (!clientIp) return
|
|
15274
|
-
|
|
15846
|
+
if (!clientIp) return lastAuthFailure ?? {
|
|
15847
|
+
ok: false,
|
|
15848
|
+
reason: "unauthorized"
|
|
15849
|
+
};
|
|
15850
|
+
if (!isPrivateOrLoopbackAddress(clientIp)) return lastAuthFailure ?? {
|
|
15851
|
+
ok: false,
|
|
15852
|
+
reason: "unauthorized"
|
|
15853
|
+
};
|
|
15854
|
+
if (hasAuthorizedWsClientForIp(clients, clientIp)) return { ok: true };
|
|
15855
|
+
return lastAuthFailure ?? {
|
|
15856
|
+
ok: false,
|
|
15857
|
+
reason: "unauthorized"
|
|
15858
|
+
};
|
|
15859
|
+
}
|
|
15860
|
+
function writeUpgradeAuthFailure(socket, auth) {
|
|
15861
|
+
if (auth.rateLimited) {
|
|
15862
|
+
const retryAfterSeconds = auth.retryAfterMs && auth.retryAfterMs > 0 ? Math.ceil(auth.retryAfterMs / 1e3) : void 0;
|
|
15863
|
+
socket.write([
|
|
15864
|
+
"HTTP/1.1 429 Too Many Requests",
|
|
15865
|
+
retryAfterSeconds ? `Retry-After: ${retryAfterSeconds}` : void 0,
|
|
15866
|
+
"Content-Type: application/json; charset=utf-8",
|
|
15867
|
+
"Connection: close",
|
|
15868
|
+
"",
|
|
15869
|
+
JSON.stringify({ error: {
|
|
15870
|
+
message: "Too many failed authentication attempts. Please try again later.",
|
|
15871
|
+
type: "rate_limited"
|
|
15872
|
+
} })
|
|
15873
|
+
].filter(Boolean).join("\r\n"));
|
|
15874
|
+
return;
|
|
15875
|
+
}
|
|
15876
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
15275
15877
|
}
|
|
15276
15878
|
function createHooksRequestHandler(opts) {
|
|
15277
15879
|
const { getHooksConfig, bindHost, port, logHooks, dispatchAgentHook, dispatchWakeHook } = opts;
|
|
15880
|
+
const hookAuthFailures = /* @__PURE__ */ new Map();
|
|
15881
|
+
const resolveHookClientKey = (req) => {
|
|
15882
|
+
return req.socket?.remoteAddress?.trim() || "unknown";
|
|
15883
|
+
};
|
|
15884
|
+
const recordHookAuthFailure = (clientKey, nowMs) => {
|
|
15885
|
+
if (!hookAuthFailures.has(clientKey) && hookAuthFailures.size >= HOOK_AUTH_FAILURE_TRACK_MAX) hookAuthFailures.clear();
|
|
15886
|
+
const current = hookAuthFailures.get(clientKey);
|
|
15887
|
+
const next = !current || nowMs - current.windowStartedAtMs >= HOOK_AUTH_FAILURE_WINDOW_MS ? {
|
|
15888
|
+
count: 1,
|
|
15889
|
+
windowStartedAtMs: nowMs
|
|
15890
|
+
} : {
|
|
15891
|
+
count: current.count + 1,
|
|
15892
|
+
windowStartedAtMs: current.windowStartedAtMs
|
|
15893
|
+
};
|
|
15894
|
+
hookAuthFailures.set(clientKey, next);
|
|
15895
|
+
if (next.count <= HOOK_AUTH_FAILURE_LIMIT) return { throttled: false };
|
|
15896
|
+
const retryAfterMs = Math.max(1, next.windowStartedAtMs + HOOK_AUTH_FAILURE_WINDOW_MS - nowMs);
|
|
15897
|
+
return {
|
|
15898
|
+
throttled: true,
|
|
15899
|
+
retryAfterSeconds: Math.ceil(retryAfterMs / 1e3)
|
|
15900
|
+
};
|
|
15901
|
+
};
|
|
15902
|
+
const clearHookAuthFailure = (clientKey) => {
|
|
15903
|
+
hookAuthFailures.delete(clientKey);
|
|
15904
|
+
};
|
|
15278
15905
|
return async (req, res) => {
|
|
15279
15906
|
const hooksConfig = getHooksConfig();
|
|
15280
15907
|
if (!hooksConfig) return false;
|
|
@@ -15288,12 +15915,24 @@ function createHooksRequestHandler(opts) {
|
|
|
15288
15915
|
return true;
|
|
15289
15916
|
}
|
|
15290
15917
|
const token = extractHookToken(req);
|
|
15291
|
-
|
|
15918
|
+
const clientKey = resolveHookClientKey(req);
|
|
15919
|
+
if (!safeEqualSecret(token, hooksConfig.token)) {
|
|
15920
|
+
const throttle = recordHookAuthFailure(clientKey, Date.now());
|
|
15921
|
+
if (throttle.throttled) {
|
|
15922
|
+
const retryAfter = throttle.retryAfterSeconds ?? 1;
|
|
15923
|
+
res.statusCode = 429;
|
|
15924
|
+
res.setHeader("Retry-After", String(retryAfter));
|
|
15925
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
15926
|
+
res.end("Too Many Requests");
|
|
15927
|
+
logHooks.warn(`hook auth throttled for ${clientKey}; retry-after=${retryAfter}s`);
|
|
15928
|
+
return true;
|
|
15929
|
+
}
|
|
15292
15930
|
res.statusCode = 401;
|
|
15293
15931
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
15294
15932
|
res.end("Unauthorized");
|
|
15295
15933
|
return true;
|
|
15296
15934
|
}
|
|
15935
|
+
clearHookAuthFailure(clientKey);
|
|
15297
15936
|
if (req.method !== "POST") {
|
|
15298
15937
|
res.statusCode = 405;
|
|
15299
15938
|
res.setHeader("Allow", "POST");
|
|
@@ -15343,9 +15982,32 @@ function createHooksRequestHandler(opts) {
|
|
|
15343
15982
|
});
|
|
15344
15983
|
return true;
|
|
15345
15984
|
}
|
|
15985
|
+
if (!isHookAgentAllowed(hooksConfig, normalized.value.agentId)) {
|
|
15986
|
+
sendJson(res, 400, {
|
|
15987
|
+
ok: false,
|
|
15988
|
+
error: getHookAgentPolicyError()
|
|
15989
|
+
});
|
|
15990
|
+
return true;
|
|
15991
|
+
}
|
|
15992
|
+
const sessionKey = resolveHookSessionKey({
|
|
15993
|
+
hooksConfig,
|
|
15994
|
+
source: "request",
|
|
15995
|
+
sessionKey: normalized.value.sessionKey
|
|
15996
|
+
});
|
|
15997
|
+
if (!sessionKey.ok) {
|
|
15998
|
+
sendJson(res, 400, {
|
|
15999
|
+
ok: false,
|
|
16000
|
+
error: sessionKey.error
|
|
16001
|
+
});
|
|
16002
|
+
return true;
|
|
16003
|
+
}
|
|
15346
16004
|
sendJson(res, 202, {
|
|
15347
16005
|
ok: true,
|
|
15348
|
-
runId: dispatchAgentHook(
|
|
16006
|
+
runId: dispatchAgentHook({
|
|
16007
|
+
...normalized.value,
|
|
16008
|
+
sessionKey: sessionKey.value,
|
|
16009
|
+
agentId: resolveHookTargetAgentId(hooksConfig, normalized.value.agentId)
|
|
16010
|
+
})
|
|
15349
16011
|
});
|
|
15350
16012
|
return true;
|
|
15351
16013
|
}
|
|
@@ -15388,13 +16050,33 @@ function createHooksRequestHandler(opts) {
|
|
|
15388
16050
|
});
|
|
15389
16051
|
return true;
|
|
15390
16052
|
}
|
|
16053
|
+
if (!isHookAgentAllowed(hooksConfig, mapped.action.agentId)) {
|
|
16054
|
+
sendJson(res, 400, {
|
|
16055
|
+
ok: false,
|
|
16056
|
+
error: getHookAgentPolicyError()
|
|
16057
|
+
});
|
|
16058
|
+
return true;
|
|
16059
|
+
}
|
|
16060
|
+
const sessionKey = resolveHookSessionKey({
|
|
16061
|
+
hooksConfig,
|
|
16062
|
+
source: "mapping",
|
|
16063
|
+
sessionKey: mapped.action.sessionKey
|
|
16064
|
+
});
|
|
16065
|
+
if (!sessionKey.ok) {
|
|
16066
|
+
sendJson(res, 400, {
|
|
16067
|
+
ok: false,
|
|
16068
|
+
error: sessionKey.error
|
|
16069
|
+
});
|
|
16070
|
+
return true;
|
|
16071
|
+
}
|
|
15391
16072
|
sendJson(res, 202, {
|
|
15392
16073
|
ok: true,
|
|
15393
16074
|
runId: dispatchAgentHook({
|
|
15394
16075
|
message: mapped.action.message,
|
|
15395
16076
|
name: mapped.action.name ?? "Hook",
|
|
16077
|
+
agentId: resolveHookTargetAgentId(hooksConfig, mapped.action.agentId),
|
|
15396
16078
|
wakeMode: mapped.action.wakeMode,
|
|
15397
|
-
sessionKey:
|
|
16079
|
+
sessionKey: sessionKey.value,
|
|
15398
16080
|
deliver: resolveHookDeliver(mapped.action.deliver),
|
|
15399
16081
|
channel,
|
|
15400
16082
|
to: mapped.action.to,
|
|
@@ -15421,7 +16103,7 @@ function createHooksRequestHandler(opts) {
|
|
|
15421
16103
|
};
|
|
15422
16104
|
}
|
|
15423
16105
|
function createGatewayHttpServer(opts) {
|
|
15424
|
-
const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth } = opts;
|
|
16106
|
+
const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth, rateLimiter } = opts;
|
|
15425
16107
|
const httpServer = opts.tlsOptions ? createServer$1(opts.tlsOptions, (req, res) => {
|
|
15426
16108
|
handleRequest(req, res);
|
|
15427
16109
|
}) : createServer((req, res) => {
|
|
@@ -15432,35 +16114,60 @@ function createGatewayHttpServer(opts) {
|
|
|
15432
16114
|
try {
|
|
15433
16115
|
const configSnapshot = loadConfig();
|
|
15434
16116
|
const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
|
|
16117
|
+
const requestPath = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
15435
16118
|
if (await handleHooksRequest(req, res)) return;
|
|
15436
16119
|
if (await handleToolsInvokeHttpRequest(req, res, {
|
|
15437
16120
|
auth: resolvedAuth,
|
|
15438
|
-
trustedProxies
|
|
16121
|
+
trustedProxies,
|
|
16122
|
+
rateLimiter
|
|
15439
16123
|
})) return;
|
|
15440
16124
|
if (await handleSlackHttpRequest(req, res)) return;
|
|
15441
|
-
if (handlePluginRequest
|
|
16125
|
+
if (handlePluginRequest) {
|
|
16126
|
+
if (requestPath.startsWith("/api/channels/")) {
|
|
16127
|
+
const token = getBearerToken(req);
|
|
16128
|
+
const authResult = await authorizeGatewayConnect({
|
|
16129
|
+
auth: resolvedAuth,
|
|
16130
|
+
connectAuth: token ? {
|
|
16131
|
+
token,
|
|
16132
|
+
password: token
|
|
16133
|
+
} : null,
|
|
16134
|
+
req,
|
|
16135
|
+
trustedProxies,
|
|
16136
|
+
rateLimiter
|
|
16137
|
+
});
|
|
16138
|
+
if (!authResult.ok) {
|
|
16139
|
+
sendGatewayAuthFailure(res, authResult);
|
|
16140
|
+
return;
|
|
16141
|
+
}
|
|
16142
|
+
}
|
|
16143
|
+
if (await handlePluginRequest(req, res)) return;
|
|
16144
|
+
}
|
|
15442
16145
|
if (openResponsesEnabled) {
|
|
15443
16146
|
if (await handleOpenResponsesHttpRequest(req, res, {
|
|
15444
16147
|
auth: resolvedAuth,
|
|
15445
16148
|
config: openResponsesConfig,
|
|
15446
|
-
trustedProxies
|
|
16149
|
+
trustedProxies,
|
|
16150
|
+
rateLimiter
|
|
15447
16151
|
})) return;
|
|
15448
16152
|
}
|
|
15449
16153
|
if (openAiChatCompletionsEnabled) {
|
|
15450
16154
|
if (await handleOpenAiHttpRequest(req, res, {
|
|
15451
16155
|
auth: resolvedAuth,
|
|
15452
|
-
trustedProxies
|
|
16156
|
+
trustedProxies,
|
|
16157
|
+
rateLimiter
|
|
15453
16158
|
})) return;
|
|
15454
16159
|
}
|
|
15455
16160
|
if (canvasHost) {
|
|
15456
|
-
if (isCanvasPath(
|
|
15457
|
-
|
|
16161
|
+
if (isCanvasPath(requestPath)) {
|
|
16162
|
+
const ok = await authorizeCanvasRequest({
|
|
15458
16163
|
req,
|
|
15459
16164
|
auth: resolvedAuth,
|
|
15460
16165
|
trustedProxies,
|
|
15461
|
-
clients
|
|
15462
|
-
|
|
15463
|
-
|
|
16166
|
+
clients,
|
|
16167
|
+
rateLimiter
|
|
16168
|
+
});
|
|
16169
|
+
if (!ok.ok) {
|
|
16170
|
+
sendGatewayAuthFailure(res, ok);
|
|
15464
16171
|
return;
|
|
15465
16172
|
}
|
|
15466
16173
|
}
|
|
@@ -15490,18 +16197,20 @@ function createGatewayHttpServer(opts) {
|
|
|
15490
16197
|
return httpServer;
|
|
15491
16198
|
}
|
|
15492
16199
|
function attachGatewayUpgradeHandler(opts) {
|
|
15493
|
-
const { httpServer, wss, canvasHost, clients, resolvedAuth } = opts;
|
|
16200
|
+
const { httpServer, wss, canvasHost, clients, resolvedAuth, rateLimiter } = opts;
|
|
15494
16201
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
15495
16202
|
(async () => {
|
|
15496
16203
|
if (canvasHost) {
|
|
15497
16204
|
if (new URL(req.url ?? "/", "http://localhost").pathname === CANVAS_WS_PATH) {
|
|
15498
|
-
|
|
16205
|
+
const ok = await authorizeCanvasRequest({
|
|
15499
16206
|
req,
|
|
15500
16207
|
auth: resolvedAuth,
|
|
15501
16208
|
trustedProxies: loadConfig().gateway?.trustedProxies ?? [],
|
|
15502
|
-
clients
|
|
15503
|
-
|
|
15504
|
-
|
|
16209
|
+
clients,
|
|
16210
|
+
rateLimiter
|
|
16211
|
+
});
|
|
16212
|
+
if (!ok.ok) {
|
|
16213
|
+
writeUpgradeAuthFailure(socket, ok);
|
|
15505
16214
|
socket.destroy();
|
|
15506
16215
|
return;
|
|
15507
16216
|
}
|
|
@@ -15527,12 +16236,13 @@ function createGatewayHooksRequestHandler(params) {
|
|
|
15527
16236
|
if (value.mode === "now") requestHeartbeatNow({ reason: "hook:wake" });
|
|
15528
16237
|
};
|
|
15529
16238
|
const dispatchAgentHook = (value) => {
|
|
15530
|
-
const sessionKey = value.sessionKey.trim()
|
|
16239
|
+
const sessionKey = value.sessionKey.trim();
|
|
15531
16240
|
const mainSessionKey = resolveMainSessionKeyFromConfig();
|
|
15532
16241
|
const jobId = randomUUID();
|
|
15533
16242
|
const now = Date.now();
|
|
15534
16243
|
const job = {
|
|
15535
16244
|
id: jobId,
|
|
16245
|
+
agentId: value.agentId,
|
|
15536
16246
|
name: value.name,
|
|
15537
16247
|
enabled: true,
|
|
15538
16248
|
createdAtMs: now,
|
|
@@ -15860,6 +16570,7 @@ async function createGatewayRuntimeState(params) {
|
|
|
15860
16570
|
handleHooksRequest,
|
|
15861
16571
|
handlePluginRequest,
|
|
15862
16572
|
resolvedAuth: params.resolvedAuth,
|
|
16573
|
+
rateLimiter: params.rateLimiter,
|
|
15863
16574
|
tlsOptions: params.gatewayTls?.enabled ? params.gatewayTls.tlsOptions : void 0
|
|
15864
16575
|
});
|
|
15865
16576
|
try {
|
|
@@ -15886,7 +16597,8 @@ async function createGatewayRuntimeState(params) {
|
|
|
15886
16597
|
wss,
|
|
15887
16598
|
canvasHost,
|
|
15888
16599
|
clients,
|
|
15889
|
-
resolvedAuth: params.resolvedAuth
|
|
16600
|
+
resolvedAuth: params.resolvedAuth,
|
|
16601
|
+
rateLimiter: params.rateLimiter
|
|
15890
16602
|
});
|
|
15891
16603
|
const agentRunSeq = /* @__PURE__ */ new Map();
|
|
15892
16604
|
const dedupe = /* @__PURE__ */ new Map();
|
|
@@ -15980,11 +16692,15 @@ function logGatewayStartup(params) {
|
|
|
15980
16692
|
* console.log(`Loaded ${count} hook handlers`);
|
|
15981
16693
|
* ```
|
|
15982
16694
|
*/
|
|
15983
|
-
async function loadInternalHooks(cfg, workspaceDir) {
|
|
16695
|
+
async function loadInternalHooks(cfg, workspaceDir, opts) {
|
|
15984
16696
|
if (!cfg.hooks?.internal?.enabled) return 0;
|
|
15985
16697
|
let loadedCount = 0;
|
|
15986
16698
|
try {
|
|
15987
|
-
const eligible = loadWorkspaceHookEntries(workspaceDir, {
|
|
16699
|
+
const eligible = loadWorkspaceHookEntries(workspaceDir, {
|
|
16700
|
+
config: cfg,
|
|
16701
|
+
managedHooksDir: opts?.managedHooksDir,
|
|
16702
|
+
bundledHooksDir: opts?.bundledHooksDir
|
|
16703
|
+
}).filter((entry) => shouldIncludeHook({
|
|
15988
16704
|
entry,
|
|
15989
16705
|
config: cfg
|
|
15990
16706
|
}));
|
|
@@ -16408,6 +17124,8 @@ function formatGatewayAuthFailureMessage(params) {
|
|
|
16408
17124
|
case "tailscale_proxy_missing": return "unauthorized: tailscale proxy headers missing (use Tailscale Serve or gateway token/password)";
|
|
16409
17125
|
case "tailscale_whois_failed": return "unauthorized: tailscale identity check failed (use Tailscale Serve auth or gateway token/password)";
|
|
16410
17126
|
case "tailscale_user_mismatch": return "unauthorized: tailscale identity mismatch (use Tailscale Serve auth or gateway token/password)";
|
|
17127
|
+
case "rate_limited": return "unauthorized: too many failed authentication attempts (retry later)";
|
|
17128
|
+
case "device_token_mismatch": return "unauthorized: device token mismatch (rotate/reissue device token)";
|
|
16411
17129
|
default: break;
|
|
16412
17130
|
}
|
|
16413
17131
|
if (authMode === "token" && authProvided === "none") return `unauthorized: gateway token missing (${tokenHint})`;
|
|
@@ -16415,7 +17133,7 @@ function formatGatewayAuthFailureMessage(params) {
|
|
|
16415
17133
|
return "unauthorized";
|
|
16416
17134
|
}
|
|
16417
17135
|
function attachGatewayWsMessageHandler(params) {
|
|
16418
|
-
const { socket, upgradeReq, connId, remoteAddr, forwardedFor, realIp, requestHost, requestOrigin, requestUserAgent, canvasHostUrl, connectNonce, resolvedAuth, gatewayMethods, events, extraHandlers, buildRequestContext, send, close, isClosed, clearHandshakeTimer, getClient, setClient, setHandshakeState, setCloseCause, setLastFrameMeta, logGateway, logHealth, logWsControl } = params;
|
|
17136
|
+
const { socket, upgradeReq, connId, remoteAddr, forwardedFor, realIp, requestHost, requestOrigin, requestUserAgent, canvasHostUrl, connectNonce, resolvedAuth, rateLimiter, gatewayMethods, events, extraHandlers, buildRequestContext, send, close, isClosed, clearHandshakeTimer, getClient, setClient, setHandshakeState, setCloseCause, setLastFrameMeta, logGateway, logHealth, logWsControl } = params;
|
|
16419
17137
|
const configSnapshot = loadConfig();
|
|
16420
17138
|
const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
|
|
16421
17139
|
const clientIp = resolveGatewayClientIp({
|
|
@@ -16518,8 +17236,7 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16518
17236
|
close(1008, "invalid role");
|
|
16519
17237
|
return;
|
|
16520
17238
|
}
|
|
16521
|
-
const
|
|
16522
|
-
const scopes = requestedScopes.length > 0 ? requestedScopes : role === "operator" ? ["operator.admin"] : [];
|
|
17239
|
+
const scopes = Array.isArray(connectParams.scopes) ? connectParams.scopes : [];
|
|
16523
17240
|
connectParams.role = role;
|
|
16524
17241
|
connectParams.scopes = scopes;
|
|
16525
17242
|
const isControlUi = connectParams.client.id === GATEWAY_CLIENT_IDS.CONTROL_UI;
|
|
@@ -16561,12 +17278,26 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16561
17278
|
const disableControlUiDeviceAuth = isControlUi && configSnapshot.gateway?.controlUi?.dangerouslyDisableDeviceAuth === true;
|
|
16562
17279
|
const allowControlUiBypass = allowInsecureControlUi || disableControlUiDeviceAuth;
|
|
16563
17280
|
const device = disableControlUiDeviceAuth ? null : deviceRaw;
|
|
16564
|
-
const
|
|
17281
|
+
const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
|
|
17282
|
+
let authResult = await authorizeGatewayConnect({
|
|
16565
17283
|
auth: resolvedAuth,
|
|
16566
17284
|
connectAuth: connectParams.auth,
|
|
16567
17285
|
req: upgradeReq,
|
|
16568
|
-
trustedProxies
|
|
17286
|
+
trustedProxies,
|
|
17287
|
+
rateLimiter: hasDeviceTokenCandidate ? void 0 : rateLimiter,
|
|
17288
|
+
clientIp,
|
|
17289
|
+
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET
|
|
16569
17290
|
});
|
|
17291
|
+
if (hasDeviceTokenCandidate && authResult.ok && rateLimiter && (authResult.method === "token" || authResult.method === "password")) {
|
|
17292
|
+
const sharedRateCheck = rateLimiter.check(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
|
|
17293
|
+
if (!sharedRateCheck.allowed) authResult = {
|
|
17294
|
+
ok: false,
|
|
17295
|
+
reason: "rate_limited",
|
|
17296
|
+
rateLimited: true,
|
|
17297
|
+
retryAfterMs: sharedRateCheck.retryAfterMs
|
|
17298
|
+
};
|
|
17299
|
+
else rateLimiter.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
|
|
17300
|
+
}
|
|
16570
17301
|
let authOk = authResult.ok;
|
|
16571
17302
|
let authMethod = authResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
|
|
16572
17303
|
const sharedAuthResult = hasSharedAuth ? await authorizeGatewayConnect({
|
|
@@ -16576,23 +17307,24 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16576
17307
|
},
|
|
16577
17308
|
connectAuth: connectParams.auth,
|
|
16578
17309
|
req: upgradeReq,
|
|
16579
|
-
trustedProxies
|
|
17310
|
+
trustedProxies,
|
|
17311
|
+
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET
|
|
16580
17312
|
}) : null;
|
|
16581
17313
|
const sharedAuthOk = sharedAuthResult?.ok === true && (sharedAuthResult.method === "token" || sharedAuthResult.method === "password");
|
|
16582
|
-
const rejectUnauthorized = () => {
|
|
17314
|
+
const rejectUnauthorized = (failedAuth) => {
|
|
16583
17315
|
setHandshakeState("failed");
|
|
16584
|
-
logWsControl.warn(`unauthorized conn=${connId} remote=${remoteAddr ?? "?"} client=${clientLabel} ${connectParams.client.mode} v${connectParams.client.version} reason=${
|
|
17316
|
+
logWsControl.warn(`unauthorized conn=${connId} remote=${remoteAddr ?? "?"} client=${clientLabel} ${connectParams.client.mode} v${connectParams.client.version} reason=${failedAuth.reason ?? "unknown"}`);
|
|
16585
17317
|
const authProvided = connectParams.auth?.token ? "token" : connectParams.auth?.password ? "password" : "none";
|
|
16586
17318
|
const authMessage = formatGatewayAuthFailureMessage({
|
|
16587
17319
|
authMode: resolvedAuth.mode,
|
|
16588
17320
|
authProvided,
|
|
16589
|
-
reason:
|
|
17321
|
+
reason: failedAuth.reason,
|
|
16590
17322
|
client: connectParams.client
|
|
16591
17323
|
});
|
|
16592
17324
|
setCloseCause("unauthorized", {
|
|
16593
17325
|
authMode: resolvedAuth.mode,
|
|
16594
17326
|
authProvided,
|
|
16595
|
-
authReason:
|
|
17327
|
+
authReason: failedAuth.reason,
|
|
16596
17328
|
allowTailscale: resolvedAuth.allowTailscale,
|
|
16597
17329
|
client: connectParams.client.id,
|
|
16598
17330
|
clientDisplayName: connectParams.client.displayName,
|
|
@@ -16629,7 +17361,7 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16629
17361
|
}
|
|
16630
17362
|
if (!canSkipDevice) {
|
|
16631
17363
|
if (!authOk && hasSharedAuth) {
|
|
16632
|
-
rejectUnauthorized();
|
|
17364
|
+
rejectUnauthorized(authResult);
|
|
16633
17365
|
return;
|
|
16634
17366
|
}
|
|
16635
17367
|
setHandshakeState("failed");
|
|
@@ -16723,7 +17455,7 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16723
17455
|
clientId: connectParams.client.id,
|
|
16724
17456
|
clientMode: connectParams.client.mode,
|
|
16725
17457
|
role,
|
|
16726
|
-
scopes
|
|
17458
|
+
scopes,
|
|
16727
17459
|
signedAtMs: signedAt,
|
|
16728
17460
|
token: connectParams.auth?.token ?? null,
|
|
16729
17461
|
nonce: providedNonce || void 0,
|
|
@@ -16736,7 +17468,7 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16736
17468
|
clientId: connectParams.client.id,
|
|
16737
17469
|
clientMode: connectParams.client.mode,
|
|
16738
17470
|
role,
|
|
16739
|
-
scopes
|
|
17471
|
+
scopes,
|
|
16740
17472
|
signedAtMs: signedAt,
|
|
16741
17473
|
token: connectParams.auth?.token ?? null,
|
|
16742
17474
|
version: "v1"
|
|
@@ -16792,7 +17524,16 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16792
17524
|
}
|
|
16793
17525
|
}
|
|
16794
17526
|
if (!authOk && connectParams.auth?.token && device) {
|
|
16795
|
-
if (
|
|
17527
|
+
if (rateLimiter) {
|
|
17528
|
+
const deviceRateCheck = rateLimiter.check(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
|
|
17529
|
+
if (!deviceRateCheck.allowed) authResult = {
|
|
17530
|
+
ok: false,
|
|
17531
|
+
reason: "rate_limited",
|
|
17532
|
+
rateLimited: true,
|
|
17533
|
+
retryAfterMs: deviceRateCheck.retryAfterMs
|
|
17534
|
+
};
|
|
17535
|
+
}
|
|
17536
|
+
if (!authResult.rateLimited) if ((await verifyDeviceToken({
|
|
16796
17537
|
deviceId: device.id,
|
|
16797
17538
|
token: connectParams.auth.token,
|
|
16798
17539
|
role,
|
|
@@ -16800,10 +17541,17 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
16800
17541
|
})).ok) {
|
|
16801
17542
|
authOk = true;
|
|
16802
17543
|
authMethod = "device-token";
|
|
17544
|
+
rateLimiter?.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
|
|
17545
|
+
} else {
|
|
17546
|
+
authResult = {
|
|
17547
|
+
ok: false,
|
|
17548
|
+
reason: "device_token_mismatch"
|
|
17549
|
+
};
|
|
17550
|
+
rateLimiter?.recordFailure(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
|
|
16803
17551
|
}
|
|
16804
17552
|
}
|
|
16805
17553
|
if (!authOk) {
|
|
16806
|
-
rejectUnauthorized();
|
|
17554
|
+
rejectUnauthorized(authResult);
|
|
16807
17555
|
return;
|
|
16808
17556
|
}
|
|
16809
17557
|
if (device && devicePublicKey && !(allowControlUiBypass && sharedAuthOk)) {
|
|
@@ -17070,8 +17818,18 @@ function attachGatewayWsMessageHandler(params) {
|
|
|
17070
17818
|
|
|
17071
17819
|
//#endregion
|
|
17072
17820
|
//#region src/gateway/server/ws-connection.ts
|
|
17821
|
+
const LOG_HEADER_MAX_LEN = 300;
|
|
17822
|
+
const LOG_HEADER_CONTROL_REGEX = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
17823
|
+
const LOG_HEADER_FORMAT_REGEX = /\p{Cf}/gu;
|
|
17824
|
+
const sanitizeLogValue = (value) => {
|
|
17825
|
+
if (!value) return;
|
|
17826
|
+
const cleaned = value.replace(LOG_HEADER_CONTROL_REGEX, " ").replace(LOG_HEADER_FORMAT_REGEX, " ").replace(/\s+/g, " ").trim();
|
|
17827
|
+
if (!cleaned) return;
|
|
17828
|
+
if (cleaned.length <= LOG_HEADER_MAX_LEN) return cleaned;
|
|
17829
|
+
return truncateUtf16Safe(cleaned, LOG_HEADER_MAX_LEN);
|
|
17830
|
+
};
|
|
17073
17831
|
function attachGatewayWsConnectionHandler(params) {
|
|
17074
|
-
const { wss, clients, port, gatewayHost, canvasHostEnabled, canvasHostServerPort, resolvedAuth, gatewayMethods, events, logGateway, logHealth, logWsControl, extraHandlers, broadcast, buildRequestContext } = params;
|
|
17832
|
+
const { wss, clients, port, gatewayHost, canvasHostEnabled, canvasHostServerPort, resolvedAuth, rateLimiter, gatewayMethods, events, logGateway, logHealth, logWsControl, extraHandlers, broadcast, buildRequestContext } = params;
|
|
17075
17833
|
wss.on("connection", (socket, upgradeReq) => {
|
|
17076
17834
|
let client = null;
|
|
17077
17835
|
let closed = false;
|
|
@@ -17145,6 +17903,11 @@ function attachGatewayWsConnectionHandler(params) {
|
|
|
17145
17903
|
const isNoisySwiftPmHelperClose = (userAgent, remote) => Boolean(userAgent?.toLowerCase().includes("swiftpm-testing-helper") && isLoopbackAddress(remote));
|
|
17146
17904
|
socket.once("close", (code, reason) => {
|
|
17147
17905
|
const durationMs = Date.now() - openedAt;
|
|
17906
|
+
const logForwardedFor = sanitizeLogValue(forwardedFor);
|
|
17907
|
+
const logOrigin = sanitizeLogValue(requestOrigin);
|
|
17908
|
+
const logHost = sanitizeLogValue(requestHost);
|
|
17909
|
+
const logUserAgent = sanitizeLogValue(requestUserAgent);
|
|
17910
|
+
const logReason = sanitizeLogValue(reason?.toString());
|
|
17148
17911
|
const closeContext = {
|
|
17149
17912
|
cause: closeCause,
|
|
17150
17913
|
handshake: handshakeState,
|
|
@@ -17152,14 +17915,14 @@ function attachGatewayWsConnectionHandler(params) {
|
|
|
17152
17915
|
lastFrameType,
|
|
17153
17916
|
lastFrameMethod,
|
|
17154
17917
|
lastFrameId,
|
|
17155
|
-
host:
|
|
17156
|
-
origin:
|
|
17157
|
-
userAgent:
|
|
17158
|
-
forwardedFor,
|
|
17918
|
+
host: logHost,
|
|
17919
|
+
origin: logOrigin,
|
|
17920
|
+
userAgent: logUserAgent,
|
|
17921
|
+
forwardedFor: logForwardedFor,
|
|
17159
17922
|
...closeMeta
|
|
17160
17923
|
};
|
|
17161
|
-
if (!client) (isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) ? logWsControl.debug : logWsControl.warn)(`closed before connect conn=${connId} remote=${remoteAddr ?? "?"} fwd=${
|
|
17162
|
-
if (client && isWebchatClient(client.connect.client)) logWsControl.info(`webchat disconnected code=${code} reason=${
|
|
17924
|
+
if (!client) (isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) ? logWsControl.debug : logWsControl.warn)(`closed before connect conn=${connId} remote=${remoteAddr ?? "?"} fwd=${logForwardedFor || "n/a"} origin=${logOrigin || "n/a"} host=${logHost || "n/a"} ua=${logUserAgent || "n/a"} code=${code ?? "n/a"} reason=${logReason || "n/a"}`, closeContext);
|
|
17925
|
+
if (client && isWebchatClient(client.connect.client)) logWsControl.info(`webchat disconnected code=${code} reason=${logReason || "n/a"} conn=${connId}`);
|
|
17163
17926
|
if (client?.presenceKey) {
|
|
17164
17927
|
upsertPresence(client.presenceKey, { reason: "disconnect" });
|
|
17165
17928
|
incrementPresenceVersion();
|
|
@@ -17179,7 +17942,7 @@ function attachGatewayWsConnectionHandler(params) {
|
|
|
17179
17942
|
logWs("out", "close", {
|
|
17180
17943
|
connId,
|
|
17181
17944
|
code,
|
|
17182
|
-
reason:
|
|
17945
|
+
reason: logReason,
|
|
17183
17946
|
durationMs,
|
|
17184
17947
|
cause: closeCause,
|
|
17185
17948
|
handshake: handshakeState,
|
|
@@ -17211,6 +17974,7 @@ function attachGatewayWsConnectionHandler(params) {
|
|
|
17211
17974
|
canvasHostUrl,
|
|
17212
17975
|
connectNonce,
|
|
17213
17976
|
resolvedAuth,
|
|
17977
|
+
rateLimiter,
|
|
17214
17978
|
gatewayMethods,
|
|
17215
17979
|
events,
|
|
17216
17980
|
extraHandlers,
|
|
@@ -17247,6 +18011,7 @@ function attachGatewayWsHandlers(params) {
|
|
|
17247
18011
|
canvasHostEnabled: params.canvasHostEnabled,
|
|
17248
18012
|
canvasHostServerPort: params.canvasHostServerPort,
|
|
17249
18013
|
resolvedAuth: params.resolvedAuth,
|
|
18014
|
+
rateLimiter: params.rateLimiter,
|
|
17250
18015
|
gatewayMethods: params.gatewayMethods,
|
|
17251
18016
|
events: params.events,
|
|
17252
18017
|
logGateway: params.logGateway,
|
|
@@ -17346,6 +18111,8 @@ async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
17346
18111
|
const { bindHost, controlUiEnabled, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, controlUiBasePath, controlUiRoot: controlUiRootOverride, resolvedAuth, tailscaleConfig, tailscaleMode } = runtimeConfig;
|
|
17347
18112
|
let hooksConfig = runtimeConfig.hooksConfig;
|
|
17348
18113
|
const canvasHostEnabled = runtimeConfig.canvasHostEnabled;
|
|
18114
|
+
const rateLimitConfig = cfgAtStart.gateway?.auth?.rateLimit;
|
|
18115
|
+
const authRateLimiter = rateLimitConfig ? createAuthRateLimiter(rateLimitConfig) : void 0;
|
|
17349
18116
|
let controlUiRootState;
|
|
17350
18117
|
if (controlUiRootOverride) {
|
|
17351
18118
|
const resolvedOverride = resolveControlUiRootOverrideSync(controlUiRootOverride);
|
|
@@ -17395,6 +18162,7 @@ async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
17395
18162
|
openResponsesEnabled,
|
|
17396
18163
|
openResponsesConfig,
|
|
17397
18164
|
resolvedAuth,
|
|
18165
|
+
rateLimiter: authRateLimiter,
|
|
17398
18166
|
gatewayTls,
|
|
17399
18167
|
hooksConfig: () => hooksConfig,
|
|
17400
18168
|
pluginRegistry,
|
|
@@ -17502,6 +18270,7 @@ async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
17502
18270
|
canvasHostEnabled: Boolean(canvasHost),
|
|
17503
18271
|
canvasHostServerPort,
|
|
17504
18272
|
resolvedAuth,
|
|
18273
|
+
rateLimiter: authRateLimiter,
|
|
17505
18274
|
gatewayMethods,
|
|
17506
18275
|
events: GATEWAY_EVENTS,
|
|
17507
18276
|
logGateway: log,
|
|
@@ -17585,6 +18354,12 @@ async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
17585
18354
|
logChannels,
|
|
17586
18355
|
logBrowser
|
|
17587
18356
|
}));
|
|
18357
|
+
{
|
|
18358
|
+
const hookRunner = getGlobalHookRunner();
|
|
18359
|
+
if (hookRunner?.hasHooks("gateway_start")) hookRunner.runGatewayStart({ port }, { port }).catch((err) => {
|
|
18360
|
+
log.warn(`gateway_start hook failed: ${String(err)}`);
|
|
18361
|
+
});
|
|
18362
|
+
}
|
|
17588
18363
|
const { applyHotReload, requestGatewayRestart } = createGatewayReloadHandlers({
|
|
17589
18364
|
deps,
|
|
17590
18365
|
broadcast,
|
|
@@ -17647,12 +18422,21 @@ async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
17647
18422
|
httpServers
|
|
17648
18423
|
});
|
|
17649
18424
|
return { close: async (opts) => {
|
|
18425
|
+
{
|
|
18426
|
+
const hookRunner = getGlobalHookRunner();
|
|
18427
|
+
if (hookRunner?.hasHooks("gateway_stop")) try {
|
|
18428
|
+
await hookRunner.runGatewayStop({ reason: opts?.reason ?? "gateway stopping" }, { port });
|
|
18429
|
+
} catch (err) {
|
|
18430
|
+
log.warn(`gateway_stop hook failed: ${String(err)}`);
|
|
18431
|
+
}
|
|
18432
|
+
}
|
|
17650
18433
|
if (diagnosticsEnabled) stopDiagnosticHeartbeat();
|
|
17651
18434
|
if (skillsRefreshTimer) {
|
|
17652
18435
|
clearTimeout(skillsRefreshTimer);
|
|
17653
18436
|
skillsRefreshTimer = null;
|
|
17654
18437
|
}
|
|
17655
18438
|
skillsChangeUnsub();
|
|
18439
|
+
authRateLimiter?.dispose();
|
|
17656
18440
|
await close(opts);
|
|
17657
18441
|
} };
|
|
17658
18442
|
}
|
|
@@ -17752,6 +18536,8 @@ async function runGatewayLoop(params) {
|
|
|
17752
18536
|
process.removeListener("SIGINT", onSigint);
|
|
17753
18537
|
process.removeListener("SIGUSR1", onSigusr1);
|
|
17754
18538
|
};
|
|
18539
|
+
const DRAIN_TIMEOUT_MS = 3e4;
|
|
18540
|
+
const SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
17755
18541
|
const request = (action, signal) => {
|
|
17756
18542
|
if (shuttingDown) {
|
|
17757
18543
|
gatewayLog$1.info(`received ${signal} during shutdown; ignoring`);
|
|
@@ -17760,13 +18546,23 @@ async function runGatewayLoop(params) {
|
|
|
17760
18546
|
shuttingDown = true;
|
|
17761
18547
|
const isRestart = action === "restart";
|
|
17762
18548
|
gatewayLog$1.info(`received ${signal}; ${isRestart ? "restarting" : "shutting down"}`);
|
|
18549
|
+
const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
|
|
17763
18550
|
const forceExitTimer = setTimeout(() => {
|
|
17764
18551
|
gatewayLog$1.error("shutdown timed out; exiting without full cleanup");
|
|
17765
18552
|
cleanupSignals();
|
|
17766
18553
|
params.runtime.exit(0);
|
|
17767
|
-
},
|
|
18554
|
+
}, forceExitMs);
|
|
17768
18555
|
(async () => {
|
|
17769
18556
|
try {
|
|
18557
|
+
if (isRestart) {
|
|
18558
|
+
const activeTasks = getActiveTaskCount();
|
|
18559
|
+
if (activeTasks > 0) {
|
|
18560
|
+
gatewayLog$1.info(`draining ${activeTasks} active task(s) before restart (timeout ${DRAIN_TIMEOUT_MS}ms)`);
|
|
18561
|
+
const { drained } = await waitForActiveTasks(DRAIN_TIMEOUT_MS);
|
|
18562
|
+
if (drained) gatewayLog$1.info("all active tasks drained");
|
|
18563
|
+
else gatewayLog$1.warn("drain timeout reached; proceeding with restart");
|
|
18564
|
+
}
|
|
18565
|
+
}
|
|
17770
18566
|
await server?.close({
|
|
17771
18567
|
reason: isRestart ? "gateway restarting" : "gateway stopping",
|
|
17772
18568
|
restartExpectedMs: isRestart ? 1500 : null
|