agentengine-sdk-python 0.8.3__py3-none-any.whl → 0.8.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/METADATA +9 -8
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/RECORD +482 -245
- ksadk/_build_provenance.json +1 -1
- ksadk/api/client.py +105 -91
- ksadk/builders/code_builder.py +36 -9
- ksadk/builders/framework_requirements.py +2 -1
- ksadk/builders/ks3_uploader.py +36 -7
- ksadk/builders/managed_runtime_builder.py +1 -0
- ksadk/cli/__init__.py +32 -3
- ksadk/cli/cmd_managed_runtime.py +13 -1
- ksadk/cli/cmd_plugin.py +6 -1
- ksadk/cli/cmd_web.py +2 -5
- ksadk/cli/runtime_bootstrap.py +23 -1
- ksadk/codex/client.py +424 -96
- ksadk/codex/projection.py +24 -0
- ksadk/codex/runtime.py +56 -298
- ksadk/common/auth.py +16 -10
- ksadk/configs/env_registry.py +29 -1
- ksadk/conversations/message_projection.py +135 -14
- ksadk/conversations/projector.py +4 -6
- ksadk/conversations/runtime_invocation.py +5 -0
- ksadk/conversations/runtime_payloads.py +1 -0
- ksadk/conversations/runtime_preparation.py +30 -27
- ksadk/conversations/runtime_resume.py +50 -2
- ksadk/conversations/runtime_stream_events.py +5 -0
- ksadk/conversations/runtime_streaming.py +25 -7
- ksadk/deployment/env_forward.py +2 -2
- ksadk/deployment/providers/serverless.py +1 -1
- ksadk/events/adapters/_codex_validators.py +18 -7
- ksadk/events/adapters/langgraph.py +15 -1
- ksadk/events/canonical_store.py +3 -1
- ksadk/events/projections.py +1 -1
- ksadk/harness/config.py +2 -2
- ksadk/harness/runtime.py +27 -27
- ksadk/harness/tools.py +38 -13
- ksadk/interaction/provider.py +1 -0
- ksadk/interaction/providers/langgraph.py +1 -0
- ksadk/kernel/bootstrap.py +28 -4
- ksadk/kernel/ingress.py +15 -2
- ksadk/kernel/sqlite_store.py +27 -0
- ksadk/kernel/store.py +3 -0
- ksadk/kernel/worker.py +65 -10
- ksadk/knowledge_base/client.py +57 -27
- ksadk/mcp_runtime/__init__.py +12 -4
- ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
- ksadk/memory/adk/resilient_session_service.py +25 -17
- ksadk/memory/adk/short_term_memory.py +26 -9
- ksadk/memory/models.py +7 -4
- ksadk/memory/provider_resolver.py +3 -0
- ksadk/model_proxy/server.py +10 -5
- ksadk/plugins/__init__.py +4 -0
- ksadk/plugins/artifacts.py +275 -0
- ksadk/plugins/bridges/__init__.py +4 -2
- ksadk/plugins/bridges/codex.py +120 -13
- ksadk/plugins/bridges/dsh.py +340 -156
- ksadk/plugins/builtins.py +35 -36
- ksadk/plugins/bundle_security.py +28 -4
- ksadk/plugins/codex_manifest.py +983 -0
- ksadk/plugins/composition.py +8 -0
- ksadk/plugins/contracts.py +118 -0
- ksadk/plugins/delivery.py +153 -0
- ksadk/plugins/dsh_installation_digest.py +71 -0
- ksadk/plugins/dsh_toolchain.py +110 -21
- ksadk/plugins/ecosystem_probe.py +6 -6
- ksadk/plugins/host.py +102 -39
- ksadk/plugins/kernel_adapter.py +205 -0
- ksadk/plugins/providers/__init__.py +23 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
- ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +870 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +92 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +15 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -0
- ksadk/plugins/providers/codex.py +335 -69
- ksadk/plugins/providers/codex_native.py +368 -0
- ksadk/plugins/providers/codex_turn.py +301 -0
- ksadk/plugins/providers/dsh_capabilities.py +1389 -0
- ksadk/plugins/providers/dsh_mcp.py +467 -0
- ksadk/plugins/providers/harness.py +118 -113
- ksadk/plugins/providers/mcp_projection.py +150 -0
- ksadk/plugins/providers/platform_resources.py +657 -0
- ksadk/plugins/resolver.py +8 -1
- ksadk/resource_runtime/__init__.py +4 -0
- ksadk/resource_runtime/broker.py +616 -0
- ksadk/resource_runtime/build_artifacts.py +165 -0
- ksadk/resource_runtime/contracts.py +138 -0
- ksadk/resource_runtime/discovery_receipts.py +100 -0
- ksadk/resource_runtime/discovery_skills.py +172 -0
- ksadk/resource_runtime/errors.py +6 -0
- ksadk/resource_runtime/ipc.py +142 -0
- ksadk/resource_runtime/kernel_approval.py +165 -0
- ksadk/resource_runtime/knowledge.py +114 -0
- ksadk/resource_runtime/langgraph.py +94 -0
- ksadk/resource_runtime/leases.py +191 -0
- ksadk/resource_runtime/managed_projection.py +186 -0
- ksadk/resource_runtime/mcp_server.py +138 -0
- ksadk/resource_runtime/memory_provider.py +318 -0
- ksadk/resource_runtime/memory_receipts.py +34 -0
- ksadk/resource_runtime/operation_ledger.py +211 -0
- ksadk/resource_runtime/plugin_config.py +48 -0
- ksadk/resource_runtime/policy_authorization.py +54 -0
- ksadk/resource_runtime/process.py +110 -0
- ksadk/resource_runtime/skill_artifacts.py +153 -0
- ksadk/resource_runtime/skill_runtime.py +42 -0
- ksadk/resource_runtime/skill_workspace.py +118 -0
- ksadk/resource_runtime/skills.py +215 -0
- ksadk/resource_runtime/snapshots.py +180 -0
- ksadk/resource_runtime/socket_server.py +103 -0
- ksadk/resource_runtime/supervisor.py +380 -0
- ksadk/resource_runtime/worker.py +504 -0
- ksadk/runners/_langgraph_runner_streams.py +74 -6
- ksadk/runners/adk_runner.py +149 -11
- ksadk/runners/base_runner.py +29 -1
- ksadk/runners/langgraph_runner.py +475 -145
- ksadk/runtime/_runner_adapter/stream_mapping.py +142 -18
- ksadk/runtime/adapter.py +2 -0
- ksadk/runtime/conversation_execution.py +92 -16
- ksadk/runtime/executor.py +78 -2
- ksadk/runtime/factory.py +29 -273
- ksadk/runtime/framework_adapters.py +23 -0
- ksadk/runtime/preprocessing.py +10 -2
- ksadk/runtime/runner_adapter.py +73 -14
- ksadk/runtime_context.py +40 -2
- ksadk/sandbox/backends/e2b.py +52 -11
- ksadk/sandbox/backends/local_process.py +9 -0
- ksadk/sandbox/base.py +2 -0
- ksadk/sandbox/e2b_connection.py +66 -0
- ksadk/scheduler/sqlite_store.py +18 -0
- ksadk/server/composition.py +2 -0
- ksadk/server/factory.py +5 -0
- ksadk/server/persistence_capability.py +516 -0
- ksadk/server/routes/control.py +210 -46
- ksadk/server/routes/dependencies.py +15 -0
- ksadk/server/routes/models.py +47 -4
- ksadk/server/routes/projection.py +252 -12
- ksadk/server/routes/sessions.py +284 -107
- ksadk/server/routes/streaming.py +43 -9
- ksadk/server/static/assets/{ArtifactsPanel-10GPXIJ6.js → ArtifactsPanel-CGbWlMNh.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BXk1l-G1.js → CodeBlock-b4C7upmO.js} +3 -3
- ksadk/server/static/assets/{MathMessageMarkdown-D9uB55Oh.js → MathMessageMarkdown-DbdW5A4_.js} +4 -4
- ksadk/server/static/assets/{MermaidBlock--OEYoXIJ.js → MermaidBlock-BEBN4Ksz.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-DrEe1syq.js → NativeTerminalPanel-zvL-WpAn.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-eeTFJLvo.js → abnfDiagram-VCTEODGH-BYafW7Za.js} +1 -1
- ksadk/server/static/assets/{arc-S3ZNbIAd.js → arc-bTohInsS.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-BW5UtSnx.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-CRjgEJDX.js → architectureDiagram-5GKGNRK7-Ch5Lbzsi.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-C7bsBoNH.js → blockDiagram-NRAW4CY4-kqF4MDOG.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BX3c33Ob.js → c4Diagram-UCG6FXSJ-8uSRIODv.js} +1 -1
- ksadk/server/static/assets/channel-CF5Up3hQ.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-KkXX47WI.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-bZ7ws-8m.js → chunk-5VM5RSS4-DkhehZDr.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-D2r0pvhY.js → chunk-F27PBJKO-BdXhtPe9.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-CZwF3oUP.js → chunk-G27WJ6UU-DPTpvpwi.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-DigFYCML.js → chunk-JWPE2WC7-Pqoh38el.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-CnorGV5q.js → chunk-LCL6LL3I-DeU00qC-.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-CexntQA-.js → chunk-POPQ4Y6H-CzxScFkr.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-CHTC7UxA.js → chunk-RHFEMEQ7-DpqMx-ux.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-BC1EWt_-.js → chunk-SVP7TREG-C6t0SypQ.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-BTG24xN0.js → chunk-XXDRQBXY-DVaeafZk.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Cz81acRQ.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Cz81acRQ.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-CkonRdAS.js → cose-bilkent-JH36ORCC-Bf9D3tzJ.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DVWs8a2t.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-DEOCBe15.js → cynefinDiagram-5FMLGOSQ-B9cY3iTz.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-DBThPuLa.js → dagre-3AP2YEHR-CSwlx3CB.js} +1 -1
- ksadk/server/static/assets/{dagre-ClTPrTht.js → dagre-UBpgon5q.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-DsZHrnpJ.js → diagram-S7CK7UJ4-C9akhN9a.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-BJR4nhVr.js → diagram-UQ7AKVKN-CgedhTPI.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-xzcERX0a.js → diagram-VSXAHHWV-Cjucz5BY.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-tn2xM1xu.js → diagram-VX7I27RA-Dd4Vleyn.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-BZPGBkw5.js → diagram-Z3DM3KII-CZ2jGtYW.js} +1 -1
- ksadk/server/static/assets/{dist-DdRBIceA.js → dist-B2_CVgKv.js} +1 -1
- ksadk/server/static/assets/{dist-Cq4OL6nO.js → dist-B3FR1SKK.js} +1 -1
- ksadk/server/static/assets/{dist-M67Hwu0n.js → dist-BIr4DSRq.js} +1 -1
- ksadk/server/static/assets/{dist-B1oWRmrH.js → dist-BgV_owcB.js} +1 -1
- ksadk/server/static/assets/{dist-DyqV4fL4.js → dist-Bj730Ib4.js} +1 -1
- ksadk/server/static/assets/{dist-DfGFrb0v.js → dist-BnaTiyzN.js} +1 -1
- ksadk/server/static/assets/{dist-C71CsujO.js → dist-C1NMAY5a.js} +1 -1
- ksadk/server/static/assets/{dist-CXpH_Rdd.js → dist-C5_ZAZsG.js} +1 -1
- ksadk/server/static/assets/{dist-DJmCPRVB.js → dist-CB9iJrvg.js} +1 -1
- ksadk/server/static/assets/{dist-DjhwGUgZ.js → dist-CFzsn6BG.js} +1 -1
- ksadk/server/static/assets/{dist-BI1qQH4n.js → dist-Cf125P_m.js} +1 -1
- ksadk/server/static/assets/{dist-8VWI-2bZ.js → dist-ClYUxfeK.js} +1 -1
- ksadk/server/static/assets/{dist-DUwKa_l6.js → dist-DFD6Qa6T.js} +1 -1
- ksadk/server/static/assets/{dist-B-46q3Hk.js → dist-DVCOWtha.js} +1 -1
- ksadk/server/static/assets/{dist-BJLUwH6W.js → dist-D_ctSS2i.js} +1 -1
- ksadk/server/static/assets/{dist-4zN1_tq5.js → dist-DiixqL0J.js} +1 -1
- ksadk/server/static/assets/{dist-B0gdk4rl.js → dist-DoVSwoDb.js} +1 -1
- ksadk/server/static/assets/{dist-Bbwqa8TG.js → dist-DvFK7p5S.js} +1 -1
- ksadk/server/static/assets/{dist-D1V3f1B4.js → dist-QfkEHOlb.js} +1 -1
- ksadk/server/static/assets/{dist-Btyo4vX4.js → dist-btbmmX5O.js} +1 -1
- ksadk/server/static/assets/{dist-u8tVH5Ak.js → dist-fBO2v5Kt.js} +1 -1
- ksadk/server/static/assets/{dist-DQAB0qn_.js → dist-ufY3F5p6.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-DVfMsLge.js → ebnfDiagram-PWID7BFC-Ci8NqqZu.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-CWEZSYaw.js → erDiagram-SSCWMZ5O-CCIOhLZG.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CQqR2hg3.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-DDRRbGPm.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-TWIxoyYL.js → ganttDiagram-EL5Y4UJY-Cp_r9Hb6.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-fgWEf7UD.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DDh2hd2S.js → gitGraphDiagram-WWUBYQGX-Dlhc8Wr8.js} +1 -1
- ksadk/server/static/assets/index-DcTML1Up.css +2 -0
- ksadk/server/static/assets/index-Dh2XHqPK.js +244 -0
- ksadk/server/static/assets/info-A6RAGUB7-BUecLTdt.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-CgoyWiDR.js → infoDiagram-RXCK75RN-CO0rRTRA.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CyRIu_om.js → ishikawaDiagram-5VMMS53U-CHiaedSS.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DN5JXHzN.js → journeyDiagram-EYS64GPL-CHcCIB09.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-CNP3ui66.js → kanban-definition-3QL26DDD-CJ_ebBZf.js} +1 -1
- ksadk/server/static/assets/{katex-DWEzQy1d.js → katex-B4weu383.js} +1 -1
- ksadk/server/static/assets/{linear-C-qHp7d2.js → linear-V-MgYHW1.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-Cl-K943T.js → mermaid-parser.core-CBijEX_c.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-D5DB5mYm.js → mindmap-definition-FBJOCRG2-Bc-pQvpp.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BeW-btFv.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-BQVc9ia_.js → pegDiagram-XKGWAZYB-D7genGLl.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-X0xh4SaT.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-B6UCAvtY.js → pieDiagram-E7YTZNPT-Bxj32zwD.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-mIr2d3eb.js → quadrantDiagram-AXDQQJYC-BURv2mGh.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-DzRMSqjd.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-Cc44icX-.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-D6S4BdV-.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-vD3aVE2o.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-CYbW_b4T.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-Dt3FFDmK.js → railroadDiagram-O6MQD6OU-CCsh3avW.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-Cz_vQtgF.js → requirementDiagram-EFPCY7ZU-_FRRBKEd.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-wlO1TsH5.js → sankeyDiagram-P5KCCOFB-C-gr2o9M.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CHRbElYq.js → sequenceDiagram-WJ2MYXX4-D97NmfKg.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-DXX54iyO.js → sizeCapture-X5ZJPWSS-DjrPC5FX.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-Dp_lw9II.js → stateDiagram-HBIQ2CUA-DzMNg1hh.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BIcjKFhF.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-D8O5UlLG.js → swimlanes-XN3QIQJK-Cssf_CsU.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-tq3K3Ybl.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-8mfKcxvG.js → timeline-definition-24CTP7MA-BWTHzyS1.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-DBXt3GdU.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-DrCy4E_l.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-EnNl0DE7.js → vennDiagram-4TSXK5OY-BSKgJsmR.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-CefhOliM.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DBu5fbYr.js → wardleyDiagram-VM6X3IG4-eflHNoaZ.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BSVXs4IY.js → xychartDiagram-S5SC5T6Z-eYqrQ-v4.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/session_context.py +66 -0
- ksadk/sessions/__init__.py +11 -21
- ksadk/sessions/_local_service_sync.py +263 -1
- ksadk/sessions/base.py +176 -0
- ksadk/sessions/errors.py +4 -0
- ksadk/sessions/in_memory.py +309 -19
- ksadk/sessions/local_service.py +83 -1
- ksadk/sessions/persistence.py +275 -0
- ksadk/sessions/postgres_service.py +358 -0
- ksadk/sessions/resilience.py +8 -0
- ksadk/sessions/resilient.py +362 -4
- ksadk/sessions/topology.py +135 -0
- ksadk/skills/models.py +26 -4
- ksadk/skills/package_store.py +186 -38
- ksadk/skills/runtime/agent.py +31 -6
- ksadk/skills/runtime/artifact_delivery.py +139 -0
- ksadk/skills/runtime/backends/e2b.py +98 -6
- ksadk/skills/runtime/backends/local.py +73 -8
- ksadk/skills/runtime/base.py +3 -0
- ksadk/skills/runtime/executor.py +2 -0
- ksadk/skills/runtime/loader.py +10 -2
- ksadk/skills/runtime/pinned.py +114 -0
- ksadk/skills/runtime/request.py +31 -0
- ksadk/skills/service_client.py +167 -28
- ksadk/studio/api.py +224 -42
- ksadk/studio/api_catalog_routes.py +18 -0
- ksadk/studio/api_plugin_routes.py +526 -95
- ksadk/studio/api_resource_connections.py +183 -0
- ksadk/studio/authoring_coordinator.py +19 -14
- ksadk/studio/builder.py +76 -0
- ksadk/studio/cloud.py +342 -104
- ksadk/studio/cloud_shared_web.py +565 -0
- ksadk/studio/codex_agent_service.py +128 -26
- ksadk/studio/codex_builder.py +241 -2
- ksadk/studio/codex_credentials.py +90 -0
- ksadk/studio/codex_manifest.py +31 -4
- ksadk/studio/codex_plugin_store.py +746 -0
- ksadk/studio/codex_provider_build.py +231 -0
- ksadk/studio/codex_run.py +184 -25
- ksadk/studio/compatibility_report.py +12 -1
- ksadk/studio/compiler.py +78 -14
- ksadk/studio/contracts.py +132 -6
- ksadk/studio/dsh_application.py +137 -0
- ksadk/studio/dsh_capability_service.py +1107 -0
- ksadk/studio/dsh_models.py +59 -0
- ksadk/studio/dsh_provider_registration.py +320 -13
- ksadk/studio/hosted_kernel.py +88 -13
- ksadk/studio/mcp_runtime.py +11 -3
- ksadk/studio/model_client.py +2 -2
- ksadk/studio/plugin_composition.py +190 -23
- ksadk/studio/plugin_kernel_adapter.py +12 -112
- ksadk/studio/plugin_runtime.py +224 -45
- ksadk/studio/resource_authority.py +637 -0
- ksadk/studio/resource_binding_validation.py +77 -0
- ksadk/studio/resource_build_admission.py +196 -0
- ksadk/studio/resource_build_materializer.py +149 -0
- ksadk/studio/resource_catalog.py +143 -2
- ksadk/studio/resource_connections.py +220 -0
- ksadk/studio/run_service.py +462 -52
- ksadk/studio/runtime_source.py +15 -7
- ksadk/studio/scheduler_assistant.py +317 -0
- ksadk/studio/scheduler_runtime.py +9 -0
- ksadk/studio/scheduler_service.py +3 -0
- ksadk/studio/service.py +490 -44
- ksadk/studio/shared_web.py +446 -62
- ksadk/studio/static/assets/CodeBlock-DCUUY0PL-BLjA7kbS.js +41 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--BEOyPC3w.css +1 -0
- ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--CUYIXwgI.js +8 -0
- ksadk/studio/static/assets/MermaidBlock-DBFTztfQ-BTYKWEbD.js +44 -0
- ksadk/studio/static/assets/PrismRenderer-wiXRHURw.js +3 -0
- ksadk/studio/static/assets/abnfDiagram-VCTEODGH-CLETGjKq.js +1 -0
- ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
- ksadk/studio/static/assets/architecture-7GRP2DOG-hXMiO_AU.js +1 -0
- ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-B5EQysk6.js +36 -0
- ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
- ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CFmPl1de.js +129 -0
- ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BRrGu8qt.js +38 -0
- ksadk/studio/static/assets/channel-BqO-gtEw.js +1 -0
- ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- ksadk/studio/static/assets/chunk-4HAMMTFA-D220vUtq.js +62 -0
- ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- ksadk/studio/static/assets/chunk-75Z2AOVW-C8lLv_qc.js +2 -0
- ksadk/studio/static/assets/chunk-DU6HZSFF-C4DtVpCu.js +128 -0
- ksadk/studio/static/assets/chunk-F27PBJKO-Cpw14SNG.js +1 -0
- ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
- ksadk/studio/static/assets/chunk-GMAD6QVW-DiQA-u2N.js +72 -0
- ksadk/studio/static/assets/chunk-GVQU2GXP-DUdNBUCi.js +1 -0
- ksadk/studio/static/assets/chunk-IMKFNOWR-BSlbjT_b.js +231 -0
- ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- ksadk/studio/static/assets/chunk-L3NEJ4N5-Db8Ab22W.js +1 -0
- ksadk/studio/static/assets/chunk-OSK3NFVY-M2m6G3j4.js +10 -0
- ksadk/studio/static/assets/chunk-P2QGCYS3-DezaQxiK.js +1 -0
- ksadk/studio/static/assets/chunk-POPQ4Y6H-DOpufJ3t.js +1 -0
- ksadk/studio/static/assets/chunk-PWAF6VOD-CfLb1gSc.js +1 -0
- ksadk/studio/static/assets/chunk-SHT3W25Y-DAyIVp5H.js +168 -0
- ksadk/studio/static/assets/chunk-SVP7TREG-DulomOrS.js +88 -0
- ksadk/studio/static/assets/chunk-TICWLB2K-bHJYoflj.js +206 -0
- ksadk/studio/static/assets/chunk-XXDRQBXY-7PcdR2B1.js +1 -0
- ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- ksadk/studio/static/assets/classDiagram-ZZMXUADV-D_bW8JjA.js +1 -0
- ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-D_bW8JjA.js +1 -0
- ksadk/studio/static/assets/cose-bilkent-JH36ORCC-snL88wkX.js +1 -0
- ksadk/studio/static/assets/cynefin-OW5HDTMX-Bd1YhRo1.js +1 -0
- ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-BBrIG_mI.js +62 -0
- ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
- ksadk/studio/static/assets/dagre-GXQ25YYZ-DtF1nGdY.js +4 -0
- ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
- ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
- ksadk/studio/static/assets/diagram-S7CK7UJ4-0BZ98mya.js +30 -0
- ksadk/studio/static/assets/diagram-UQ7AKVKN-6mLKEo6L.js +41 -0
- ksadk/studio/static/assets/diagram-VSXAHHWV-CJBIaiZ2.js +3 -0
- ksadk/studio/static/assets/diagram-VX7I27RA-BohLEE1C.js +24 -0
- ksadk/studio/static/assets/diagram-Z3DM3KII-D8KIGonG.js +24 -0
- ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
- ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-C99HU61t.js +1 -0
- ksadk/studio/static/assets/erDiagram-RLTQ6QDP-DLGry2Ap.js +99 -0
- ksadk/studio/static/assets/eventmodeling-NTZA5JFV-B1_5Ac8j.js +1 -0
- ksadk/studio/static/assets/flowDiagram-HODETNUW-DD5hmicn.js +1 -0
- ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BPJrVLSG.js +292 -0
- ksadk/studio/static/assets/gitGraph-4MIJSDKK-Cy_ZQBGi.js +1 -0
- ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-CZCUz4MT.js +106 -0
- ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
- ksadk/studio/static/assets/index-CeHL9ARV.css +2 -0
- ksadk/studio/static/assets/index-D9QEcVpL.js +320 -0
- ksadk/studio/static/assets/info-A6RAGUB7-DBzDQB_I.js +1 -0
- ksadk/studio/static/assets/infoDiagram-27XIBGKW-B-sq07Hi.js +2 -0
- ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
- ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-CljI2wyf.js +70 -0
- ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-D50Fo2sL.js +139 -0
- ksadk/studio/static/assets/kanban-definition-UXKFOSKX-DimfOTam.js +89 -0
- ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
- ksadk/studio/static/assets/line-CX5BOOUP.js +1 -0
- ksadk/studio/static/assets/linear-BCxN9JLv.js +1 -0
- ksadk/studio/static/assets/mermaid-parser.core-BdCz8fkS.js +7 -0
- ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-Bizzqy5R.js +96 -0
- ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
- ksadk/studio/static/assets/packet-AYTQ26CC-gPln-_kG.js +1 -0
- ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
- ksadk/studio/static/assets/pegDiagram-XKGWAZYB-Dy3fB-gW.js +1 -0
- ksadk/studio/static/assets/pie-WAS4IAKB-qZdHMr7B.js +1 -0
- ksadk/studio/static/assets/pieDiagram-E7YTZNPT-B9mrFSDF.js +39 -0
- ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-NzUfCbMR.js +7 -0
- ksadk/studio/static/assets/radar-RG4KPBEZ-BZlnaea9.js +1 -0
- ksadk/studio/static/assets/railroad-74A4TZTK-Ce3__H9c.js +1 -0
- ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-DqQn7EzD.js +1 -0
- ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-B55kCA8F.js +1 -0
- ksadk/studio/static/assets/railroad-peg-WCYAUIDC-DfQnvkHd.js +1 -0
- ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-DnhMjZCk.js +1 -0
- ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-BF3d103U.js +84 -0
- ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
- ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
- ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-Bo-JTwt0.js +40 -0
- ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-Dh4wZtUm.js +162 -0
- ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- ksadk/studio/static/assets/src-SFn9St9H.js +1 -0
- ksadk/studio/static/assets/stateDiagram-D77RDMKH-DH774G6d.js +1 -0
- ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-DZvVWPBo.js +1 -0
- ksadk/studio/static/assets/swimlanes-42K2YHIH-Bl13OvTX.js +1 -0
- ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-BnFOTALJ.js +8 -0
- ksadk/studio/static/assets/timeline-definition-24CTP7MA-DxmSSjRB.js +120 -0
- ksadk/studio/static/assets/treeView-Q6P3EWNA-D5Nn9Swz.js +1 -0
- ksadk/studio/static/assets/treemap-WGGIJYW6-CgHgnQhE.js +1 -0
- ksadk/studio/static/assets/vennDiagram-4TSXK5OY-B9QEXcJq.js +34 -0
- ksadk/studio/static/assets/wardley-WFR3VGLG-DXrK2RRY.js +1 -0
- ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-Ba1AOWoe.js +78 -0
- ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-Cr5pt5Y-.js +7 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/version.py +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +0 -1
- ksadk/server/static/assets/channel-s354Yo6o.js +0 -1
- ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +0 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +0 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +0 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +0 -1
- ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +0 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +0 -1
- ksadk/server/static/assets/index-B2k_urY8.js +0 -241
- ksadk/server/static/assets/index-ILPt6IWF.css +0 -2
- ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +0 -1
- ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +0 -1
- ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +0 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +0 -1
- ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +0 -1
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +0 -1
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +0 -1
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +0 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +0 -8
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +0 -1
- ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +0 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +0 -1
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +0 -3
- ksadk/studio/static/assets/index-DXs1Eq4M.css +0 -1
- ksadk/studio/static/assets/index-DeDqwTsD.js +0 -283
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/top_level.txt +0 -0
ksadk/plugins/bridges/dsh.py
CHANGED
|
@@ -13,6 +13,7 @@ import json
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
15
|
import shutil
|
|
16
|
+
import signal
|
|
16
17
|
import stat
|
|
17
18
|
import subprocess
|
|
18
19
|
import tempfile
|
|
@@ -28,8 +29,16 @@ try:
|
|
|
28
29
|
except ImportError: # pragma: no cover - DSH production hosts are Unix
|
|
29
30
|
fcntl = None # type: ignore[assignment]
|
|
30
31
|
|
|
32
|
+
import yaml
|
|
31
33
|
from pydantic import BaseModel, ConfigDict
|
|
32
34
|
|
|
35
|
+
from ksadk.plugins.dsh_installation_digest import (
|
|
36
|
+
DshInstallationDigestError,
|
|
37
|
+
installation_digest,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
_COMMAND_TIMEOUT_SECONDS = 120
|
|
41
|
+
|
|
33
42
|
|
|
34
43
|
def _to_camel(value: str) -> str:
|
|
35
44
|
head, *tail = value.split("_")
|
|
@@ -52,18 +61,6 @@ class DshBridgeHost(_DshModel):
|
|
|
52
61
|
available: Literal[True] = True
|
|
53
62
|
|
|
54
63
|
|
|
55
|
-
class DshClientBundle(_DshModel):
|
|
56
|
-
"""One validated browser half from an installed DSH package."""
|
|
57
|
-
|
|
58
|
-
platform: Literal["web"] = "web"
|
|
59
|
-
digest: str
|
|
60
|
-
content_bytes: int
|
|
61
|
-
external: tuple[str, ...] = ()
|
|
62
|
-
inject: tuple[str, ...] = ()
|
|
63
|
-
compatible: bool
|
|
64
|
-
incompatibility_reason: str = ""
|
|
65
|
-
|
|
66
|
-
|
|
67
64
|
class DshPluginInventory(_DshModel):
|
|
68
65
|
ecosystem: Literal["dsh"] = "dsh"
|
|
69
66
|
integration_mode: Literal["bridged"] = "bridged"
|
|
@@ -75,10 +72,11 @@ class DshPluginInventory(_DshModel):
|
|
|
75
72
|
requested_spec: str
|
|
76
73
|
source_digest: str | None = None
|
|
77
74
|
source_kind: Literal["directory", "tgz"] | None = None
|
|
75
|
+
client_extension: bool = False
|
|
76
|
+
settings_integration: bool = False
|
|
78
77
|
installed: Literal[True] = True
|
|
79
78
|
enabled: bool
|
|
80
79
|
permissions_declared: Literal[False] = False
|
|
81
|
-
client_bundle: DshClientBundle | None = None
|
|
82
80
|
risk_disclosures: tuple[str, ...] = (
|
|
83
81
|
"DSH packages and install scripts run with the native host user privileges.",
|
|
84
82
|
"DSH bundle manifests do not declare a complete runtime permission set.",
|
|
@@ -93,6 +91,19 @@ class DshProfileProjection(_DshModel):
|
|
|
93
91
|
host_version: str
|
|
94
92
|
|
|
95
93
|
|
|
94
|
+
class DshProfileBuildSnapshot(_DshModel):
|
|
95
|
+
"""Credential-free identity of the actual local profile execution inputs."""
|
|
96
|
+
|
|
97
|
+
projection: DshProfileProjection
|
|
98
|
+
dependency_lock_digest: str
|
|
99
|
+
installation_digest: str
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def digest(self) -> str:
|
|
103
|
+
payload = json.dumps(self.model_dump(mode="json"), sort_keys=True, separators=(",", ":"))
|
|
104
|
+
return "sha256:" + hashlib.sha256(payload.encode()).hexdigest()
|
|
105
|
+
|
|
106
|
+
|
|
96
107
|
class DshBridgeError(RuntimeError):
|
|
97
108
|
"""Base failure for one bounded DSH profile operation."""
|
|
98
109
|
|
|
@@ -113,6 +124,10 @@ class DshPluginMutationError(DshBridgeError):
|
|
|
113
124
|
pass
|
|
114
125
|
|
|
115
126
|
|
|
127
|
+
class DshProfileRecoveryError(DshPluginMutationError):
|
|
128
|
+
"""The owner must keep run admission suspended until profile recovery."""
|
|
129
|
+
|
|
130
|
+
|
|
116
131
|
class _CommandResult(_DshModel):
|
|
117
132
|
stdout: str = ""
|
|
118
133
|
stderr: str = ""
|
|
@@ -122,13 +137,16 @@ CommandRunner = Callable[[Sequence[str], Path, Mapping[str, str]], _CommandResul
|
|
|
122
137
|
|
|
123
138
|
_PROFILE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
|
|
124
139
|
_PACKAGE_NAME = re.compile(r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+$")
|
|
140
|
+
_PINNED_REGISTRY_SOURCE = re.compile(
|
|
141
|
+
r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+@"
|
|
142
|
+
r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$"
|
|
143
|
+
)
|
|
125
144
|
_HOST_VERSION = re.compile(r"\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b")
|
|
126
145
|
_STATE_FILE = ".ksadk-dsh-plugins.json"
|
|
127
146
|
_IMMUTABLE_SOURCE_DIR = "immutable-plugin-sources"
|
|
128
147
|
_SNAPSHOT_FILES = ("package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", _STATE_FILE)
|
|
148
|
+
_WEB_PROFILE_BUILTINS = ("@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app")
|
|
129
149
|
_MAX_JSON_BYTES = 2 * 1024 * 1024
|
|
130
|
-
_MAX_CLIENT_BUNDLE_BYTES = 8 * 1024 * 1024
|
|
131
|
-
_STUDIO_CLIENT_EXTERNALS = frozenset({"react"})
|
|
132
150
|
_PROFILE_LOCK_DIR = "profile-locks"
|
|
133
151
|
_DSH_SUBPROCESS_ENV_KEYS = (
|
|
134
152
|
"PATH",
|
|
@@ -165,6 +183,26 @@ _DSH_SUBPROCESS_ENV_KEYS = (
|
|
|
165
183
|
)
|
|
166
184
|
|
|
167
185
|
|
|
186
|
+
def validate_dsh_registry_source(source: str) -> str:
|
|
187
|
+
"""Return one exact, credential-free npm registry package coordinate."""
|
|
188
|
+
|
|
189
|
+
value = source.strip()
|
|
190
|
+
if not _PINNED_REGISTRY_SOURCE.fullmatch(value):
|
|
191
|
+
raise ValueError(
|
|
192
|
+
"DSH registry source must be <package>@<exact-semver>; "
|
|
193
|
+
"Git URLs, tags, and ranges are not allowed"
|
|
194
|
+
)
|
|
195
|
+
return value
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def validate_dsh_registry_request(source: str) -> str:
|
|
199
|
+
"""Accept a package name for latest resolution, or an exact coordinate."""
|
|
200
|
+
value = source.strip()
|
|
201
|
+
if len(value) <= 214 and not value.startswith("-") and _PACKAGE_NAME.fullmatch(value):
|
|
202
|
+
return value
|
|
203
|
+
return validate_dsh_registry_source(value)
|
|
204
|
+
|
|
205
|
+
|
|
168
206
|
def dsh_subprocess_environment(*, dsh_home: Path | None = None) -> dict[str, str]:
|
|
169
207
|
"""Build the explicit environment inherited by DSH and pnpm children.
|
|
170
208
|
|
|
@@ -205,6 +243,12 @@ class _PreparedSource:
|
|
|
205
243
|
artifact: str
|
|
206
244
|
|
|
207
245
|
|
|
246
|
+
@dataclass(frozen=True)
|
|
247
|
+
class _ProfileSnapshot:
|
|
248
|
+
files: Mapping[str, bytes | None]
|
|
249
|
+
node_modules_existed: bool
|
|
250
|
+
|
|
251
|
+
|
|
208
252
|
class DshProfilePluginBridge:
|
|
209
253
|
"""Manage DSH bundles in one isolated Profile with rollback and preflight."""
|
|
210
254
|
|
|
@@ -212,7 +256,7 @@ class DshProfilePluginBridge:
|
|
|
212
256
|
self,
|
|
213
257
|
*,
|
|
214
258
|
dsh_home: Path,
|
|
215
|
-
profile: str = "
|
|
259
|
+
profile: str = "web",
|
|
216
260
|
dsh_command: Sequence[str] | None = None,
|
|
217
261
|
command_runner: CommandRunner | None = None,
|
|
218
262
|
cwd: Path | None = None,
|
|
@@ -276,6 +320,14 @@ class DshProfilePluginBridge:
|
|
|
276
320
|
package = self._read_package(name)
|
|
277
321
|
if package is None or self._bundle_patch(package) is None:
|
|
278
322
|
continue
|
|
323
|
+
dsh = package.get("dsh")
|
|
324
|
+
client = dsh.get("client") if isinstance(dsh, dict) else None
|
|
325
|
+
inject = client.get("inject") if isinstance(client, dict) else None
|
|
326
|
+
client_inject = (
|
|
327
|
+
tuple(item for item in inject if isinstance(item, str))
|
|
328
|
+
if isinstance(inject, list)
|
|
329
|
+
else ()
|
|
330
|
+
)
|
|
279
331
|
receipt = state["sources"].get(name)
|
|
280
332
|
items.append(
|
|
281
333
|
DshPluginInventory(
|
|
@@ -289,8 +341,12 @@ class DshProfilePluginBridge:
|
|
|
289
341
|
requested_spec=requested_spec,
|
|
290
342
|
source_digest=receipt["digest"] if receipt is not None else None,
|
|
291
343
|
source_kind=receipt["kind"] if receipt is not None else None,
|
|
344
|
+
client_extension=isinstance(client, dict),
|
|
345
|
+
settings_integration=any(
|
|
346
|
+
"client-ui-settings" in item or "client-ui-slots" in item
|
|
347
|
+
for item in client_inject
|
|
348
|
+
),
|
|
292
349
|
enabled=name in active and name not in set(state["disabled"]),
|
|
293
|
-
client_bundle=self._client_bundle_metadata(name, package),
|
|
294
350
|
)
|
|
295
351
|
)
|
|
296
352
|
return tuple(sorted(items, key=lambda item: (item.display_name.casefold(), item.name)))
|
|
@@ -314,6 +370,7 @@ class DshProfilePluginBridge:
|
|
|
314
370
|
"explicit approval is required"
|
|
315
371
|
)
|
|
316
372
|
self._validate_source(source)
|
|
373
|
+
source = self._resolve_install_source(source.strip())
|
|
317
374
|
with self._profile_transaction(exclusive=True):
|
|
318
375
|
self._require_package_mutation_rollback(new_profile_allowed=True)
|
|
319
376
|
snapshot = self._snapshot()
|
|
@@ -492,36 +549,141 @@ class DshProfilePluginBridge:
|
|
|
492
549
|
host_version=self.host.version,
|
|
493
550
|
)
|
|
494
551
|
|
|
495
|
-
def
|
|
496
|
-
|
|
552
|
+
def migrate_to_isolated_layout(
|
|
553
|
+
self,
|
|
554
|
+
*,
|
|
555
|
+
accept_host_permissions: bool = False,
|
|
556
|
+
recover_external_dependency_links: bool = False,
|
|
557
|
+
) -> None:
|
|
558
|
+
"""Migrate a stopped profile; the caller owns Core/admission suspension.
|
|
497
559
|
|
|
560
|
+
Keep an independent byte copy for rollback: a frozen reinstall of the
|
|
561
|
+
old layout can itself hit the pinned pnpm shutdown defect. Studio-owned
|
|
562
|
+
profiles may also repair an isolated tree polluted by a legacy Core
|
|
563
|
+
module-fallback cache.
|
|
564
|
+
"""
|
|
565
|
+
if not accept_host_permissions:
|
|
566
|
+
raise DshPluginApprovalRequired("Profile migration may run package install scripts")
|
|
567
|
+
with self._profile_transaction(exclusive=True):
|
|
568
|
+
self._require_package_mutation_rollback(new_profile_allowed=False)
|
|
569
|
+
manifest = self._read_manifest()
|
|
570
|
+
self._verify_source_receipts(manifest, self._read_state(manifest))
|
|
571
|
+
settings_path = self._profile_root / "pnpm-workspace.yaml"
|
|
572
|
+
settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
|
|
573
|
+
if not isinstance(settings, dict):
|
|
574
|
+
raise DshPluginMutationError("Invalid package-manager settings")
|
|
575
|
+
node_linker = settings.get("nodeLinker")
|
|
576
|
+
if node_linker not in {"hoisted", "isolated"}:
|
|
577
|
+
raise DshPluginMutationError(
|
|
578
|
+
"Only hoisted or isolated profiles can use this migration"
|
|
579
|
+
)
|
|
580
|
+
modules = self._profile_root / "node_modules"
|
|
581
|
+
if modules.is_symlink() or not modules.is_dir():
|
|
582
|
+
raise DshPluginMutationError("Profile installation is not a real directory")
|
|
583
|
+
# A normal migration only accepts a closed tree. Studio may repair
|
|
584
|
+
# its own legacy hoisted Profile when pnpm linked a dependency to
|
|
585
|
+
# an ambient store: move that tree atomically instead of reading or
|
|
586
|
+
# following the external target, then reinstall from the frozen lock.
|
|
587
|
+
moved_unsafe_tree = False
|
|
588
|
+
try:
|
|
589
|
+
original_digest = installation_digest(modules)
|
|
590
|
+
except DshInstallationDigestError as error:
|
|
591
|
+
if (
|
|
592
|
+
not recover_external_dependency_links
|
|
593
|
+
or str(error) != "DSH dependency link escapes the installed tree"
|
|
594
|
+
):
|
|
595
|
+
raise
|
|
596
|
+
original_digest = None
|
|
597
|
+
moved_unsafe_tree = True
|
|
598
|
+
if node_linker == "isolated" and not moved_unsafe_tree:
|
|
599
|
+
self._preflight()
|
|
600
|
+
return
|
|
601
|
+
fallback = self._profile_root / ".dsh-module-fallback"
|
|
602
|
+
if fallback.exists() and (fallback.is_symlink() or not fallback.is_dir()):
|
|
603
|
+
raise DshPluginMutationError("DSH module fallback is not a real directory")
|
|
604
|
+
snapshot = self._snapshot()
|
|
605
|
+
root = tempfile.mkdtemp(prefix=".layout-backup-", dir=self._profile_root)
|
|
606
|
+
preserve_backup = False
|
|
607
|
+
try:
|
|
608
|
+
backup = Path(root) / "node_modules"
|
|
609
|
+
fallback_backup = Path(root) / "module-fallback"
|
|
610
|
+
if moved_unsafe_tree:
|
|
611
|
+
os.replace(modules, backup)
|
|
612
|
+
else:
|
|
613
|
+
shutil.copytree(modules, backup, symlinks=True)
|
|
614
|
+
if installation_digest(backup) != original_digest:
|
|
615
|
+
raise DshPluginMutationError("Profile installation backup did not verify")
|
|
616
|
+
fallback_moved = fallback.is_dir()
|
|
617
|
+
if fallback_moved:
|
|
618
|
+
os.replace(fallback, fallback_backup)
|
|
619
|
+
original_files = Path(root) / "profile-files"
|
|
620
|
+
original_files.mkdir(mode=0o700)
|
|
621
|
+
for name, content in snapshot.files.items():
|
|
622
|
+
if content is not None:
|
|
623
|
+
original_path = original_files / name
|
|
624
|
+
original_path.write_bytes(content)
|
|
625
|
+
original_path.chmod(0o600)
|
|
626
|
+
try:
|
|
627
|
+
if not moved_unsafe_tree:
|
|
628
|
+
shutil.rmtree(modules)
|
|
629
|
+
settings["nodeLinker"] = "isolated"
|
|
630
|
+
settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
|
|
631
|
+
settings_path.chmod(0o600)
|
|
632
|
+
self._plugin_command("install", "--frozen-lockfile")
|
|
633
|
+
current_lock = (self._profile_root / "pnpm-lock.yaml").read_bytes()
|
|
634
|
+
if current_lock != snapshot.files["pnpm-lock.yaml"]:
|
|
635
|
+
raise DshPluginMutationError("Layout migration changed the dependency lock")
|
|
636
|
+
# Native reconcile can re-enable previously disabled bundles.
|
|
637
|
+
for name in ("package.json", _STATE_FILE):
|
|
638
|
+
content = snapshot.files[name]
|
|
639
|
+
path = self._profile_root / name
|
|
640
|
+
if content is None:
|
|
641
|
+
path.unlink(missing_ok=True)
|
|
642
|
+
else:
|
|
643
|
+
path.write_bytes(content)
|
|
644
|
+
path.chmod(0o600)
|
|
645
|
+
self._verify_source_receipts(manifest, self._read_state(manifest))
|
|
646
|
+
self._preflight()
|
|
647
|
+
except BaseException as error:
|
|
648
|
+
try:
|
|
649
|
+
if modules.is_symlink():
|
|
650
|
+
modules.unlink()
|
|
651
|
+
elif modules.exists():
|
|
652
|
+
shutil.rmtree(modules)
|
|
653
|
+
os.replace(backup, modules)
|
|
654
|
+
if fallback.is_symlink():
|
|
655
|
+
fallback.unlink()
|
|
656
|
+
elif fallback.exists():
|
|
657
|
+
shutil.rmtree(fallback)
|
|
658
|
+
if fallback_moved:
|
|
659
|
+
os.replace(fallback_backup, fallback)
|
|
660
|
+
self._rollback(snapshot, error, reinstall=False)
|
|
661
|
+
except BaseException as recovery_error:
|
|
662
|
+
preserve_backup = True
|
|
663
|
+
raise DshProfileRecoveryError(
|
|
664
|
+
"Profile layout recovery failed; retain the layout backup for recovery"
|
|
665
|
+
) from recovery_error
|
|
666
|
+
raise
|
|
667
|
+
finally:
|
|
668
|
+
if not preserve_backup:
|
|
669
|
+
shutil.rmtree(root)
|
|
670
|
+
|
|
671
|
+
def snapshot_for_build(self) -> DshProfileBuildSnapshot:
|
|
672
|
+
"""Capture exact local inputs under the same profile mutation lock."""
|
|
498
673
|
with self._profile_transaction(exclusive=False):
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
raise DshPluginNotFoundError(
|
|
508
|
-
f"DSH plugin {name!r} has no Studio-compatible client bundle"
|
|
674
|
+
projection = self.project_profile()
|
|
675
|
+
lock = self._profile_root / "pnpm-lock.yaml"
|
|
676
|
+
if lock.is_symlink() or not lock.is_file() or lock.stat().st_size > 16 * 1024 * 1024:
|
|
677
|
+
raise DshPluginMutationError("DSH Build requires a bounded dependency lockfile")
|
|
678
|
+
return DshProfileBuildSnapshot(
|
|
679
|
+
projection=projection,
|
|
680
|
+
dependency_lock_digest="sha256:" + hashlib.sha256(lock.read_bytes()).hexdigest(),
|
|
681
|
+
installation_digest=installation_digest(self._profile_root / "node_modules"),
|
|
509
682
|
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
path = self._client_bundle_path(name, package)
|
|
515
|
-
if path is None:
|
|
516
|
-
raise DshPluginNotFoundError(f"DSH plugin {name!r} client bundle is unavailable")
|
|
517
|
-
try:
|
|
518
|
-
content = path.read_bytes()
|
|
519
|
-
except OSError as error:
|
|
520
|
-
raise DshPluginMutationError("DSH client bundle became unreadable") from error
|
|
521
|
-
actual_digest = f"sha256:{hashlib.sha256(content).hexdigest()}"
|
|
522
|
-
if actual_digest != expected_digest:
|
|
523
|
-
raise DshPluginMutationError("DSH client bundle changed after inventory projection")
|
|
524
|
-
return content
|
|
683
|
+
|
|
684
|
+
def verify_build_snapshot(self, expected: DshProfileBuildSnapshot) -> None:
|
|
685
|
+
if self.snapshot_for_build() != expected:
|
|
686
|
+
raise DshPluginMutationError("DSH installed profile no longer matches the Build")
|
|
525
687
|
|
|
526
688
|
def _resolve_command(self) -> tuple[str, ...]:
|
|
527
689
|
if self._command is not None:
|
|
@@ -607,10 +769,26 @@ class DshProfilePluginBridge:
|
|
|
607
769
|
def _plugin_command(self, verb: str, value: str) -> _CommandResult:
|
|
608
770
|
self._ensure_started()
|
|
609
771
|
assert self._command is not None
|
|
610
|
-
|
|
611
|
-
|
|
772
|
+
initializing = verb == "add" and not self._manifest_path().exists()
|
|
773
|
+
# The pinned pnpm hoisted linker can start fetch workers after its
|
|
774
|
+
# shutdown pass during removal. Use its supported isolated layout for
|
|
775
|
+
# newly owned profiles, without changing an existing profile's policy.
|
|
776
|
+
options = ("--config.node-linker=isolated",) if initializing else ()
|
|
777
|
+
result = self._invoke(
|
|
778
|
+
(*self._command, "plugin", "--profile", self._profile, verb, value, *options),
|
|
612
779
|
cwd=self._cwd,
|
|
613
780
|
)
|
|
781
|
+
if initializing:
|
|
782
|
+
settings_path = self._profile_root / "pnpm-workspace.yaml"
|
|
783
|
+
settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
|
|
784
|
+
if not isinstance(settings, dict):
|
|
785
|
+
raise DshPluginMutationError("DSH initialized invalid package-manager settings")
|
|
786
|
+
settings["nodeLinker"] = "isolated"
|
|
787
|
+
# This is a newly created profile inside the install transaction;
|
|
788
|
+
# any subsequent failure removes it through the existing rollback.
|
|
789
|
+
settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
|
|
790
|
+
settings_path.chmod(0o600)
|
|
791
|
+
return result
|
|
614
792
|
|
|
615
793
|
def _preflight(self) -> _CommandResult:
|
|
616
794
|
self._ensure_started()
|
|
@@ -677,100 +855,31 @@ class DshProfilePluginBridge:
|
|
|
677
855
|
patch = bundle.get("patch") if isinstance(bundle, dict) else None
|
|
678
856
|
return patch.strip() if isinstance(patch, str) and patch.strip() else None
|
|
679
857
|
|
|
680
|
-
@staticmethod
|
|
681
|
-
def _client_declaration(package: Mapping[str, object]) -> Mapping[str, object] | None:
|
|
682
|
-
dsh = package.get("dsh")
|
|
683
|
-
client = dsh.get("client") if isinstance(dsh, dict) else None
|
|
684
|
-
return client if isinstance(client, dict) else None
|
|
685
|
-
|
|
686
|
-
@staticmethod
|
|
687
|
-
def _string_tuple(value: object) -> tuple[str, ...] | None:
|
|
688
|
-
if value is None:
|
|
689
|
-
return ()
|
|
690
|
-
if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
|
|
691
|
-
return None
|
|
692
|
-
return tuple(value)
|
|
693
|
-
|
|
694
|
-
@staticmethod
|
|
695
|
-
def _client_export(package: Mapping[str, object]) -> str | None:
|
|
696
|
-
exports = package.get("exports")
|
|
697
|
-
client = exports.get("./client") if isinstance(exports, dict) else None
|
|
698
|
-
if isinstance(client, str):
|
|
699
|
-
return client
|
|
700
|
-
default = client.get("default") if isinstance(client, dict) else None
|
|
701
|
-
return default if isinstance(default, str) else None
|
|
702
|
-
|
|
703
|
-
def _client_bundle_path(
|
|
704
|
-
self, name: str, package: Mapping[str, object]
|
|
705
|
-
) -> Path | None:
|
|
706
|
-
declared = self._client_export(package)
|
|
707
|
-
if not declared:
|
|
708
|
-
return None
|
|
709
|
-
relative = Path(declared)
|
|
710
|
-
if relative.is_absolute() or ".." in relative.parts:
|
|
711
|
-
return None
|
|
712
|
-
root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
|
|
713
|
-
target = (root / relative).resolve()
|
|
714
|
-
if not target.is_relative_to(root) or not target.is_file():
|
|
715
|
-
return None
|
|
716
|
-
return target
|
|
717
|
-
|
|
718
|
-
def _client_bundle_metadata(
|
|
719
|
-
self, name: str, package: Mapping[str, object]
|
|
720
|
-
) -> DshClientBundle | None:
|
|
721
|
-
declaration = self._client_declaration(package)
|
|
722
|
-
if declaration is None or declaration.get("platform") != "web":
|
|
723
|
-
return None
|
|
724
|
-
inject = self._string_tuple(declaration.get("inject"))
|
|
725
|
-
external = self._string_tuple(declaration.get("external"))
|
|
726
|
-
target = self._client_bundle_path(name, package)
|
|
727
|
-
reason = ""
|
|
728
|
-
if inject is None or external is None:
|
|
729
|
-
reason = "dsh.client inject/external must be string arrays"
|
|
730
|
-
elif target is None:
|
|
731
|
-
reason = "exports[./client] does not resolve to a built bundle"
|
|
732
|
-
elif inject:
|
|
733
|
-
reason = "client bundle dependencies are not present in the Studio graph"
|
|
734
|
-
elif any(item not in _STUDIO_CLIENT_EXTERNALS for item in external):
|
|
735
|
-
reason = "client bundle requests unsupported external modules"
|
|
736
|
-
if target is None:
|
|
737
|
-
return DshClientBundle(
|
|
738
|
-
digest="",
|
|
739
|
-
content_bytes=0,
|
|
740
|
-
inject=inject or (),
|
|
741
|
-
external=external or (),
|
|
742
|
-
compatible=False,
|
|
743
|
-
incompatibility_reason=reason,
|
|
744
|
-
)
|
|
745
|
-
try:
|
|
746
|
-
size = target.stat().st_size
|
|
747
|
-
if size > _MAX_CLIENT_BUNDLE_BYTES:
|
|
748
|
-
reason = "client bundle exceeds the supported size limit"
|
|
749
|
-
content = b""
|
|
750
|
-
else:
|
|
751
|
-
content = target.read_bytes()
|
|
752
|
-
except OSError:
|
|
753
|
-
size = 0
|
|
754
|
-
content = b""
|
|
755
|
-
reason = "client bundle is unreadable"
|
|
756
|
-
return DshClientBundle(
|
|
757
|
-
digest=f"sha256:{hashlib.sha256(content).hexdigest()}" if content else "",
|
|
758
|
-
content_bytes=size,
|
|
759
|
-
inject=inject or (),
|
|
760
|
-
external=external or (),
|
|
761
|
-
compatible=not reason,
|
|
762
|
-
incompatibility_reason=reason,
|
|
763
|
-
)
|
|
764
|
-
|
|
765
858
|
def _require_bundle(self, name: str) -> None:
|
|
766
859
|
package = self._read_package(name)
|
|
767
|
-
|
|
768
|
-
if package is None or patch is None:
|
|
860
|
+
if package is None:
|
|
769
861
|
raise DshPluginMutationError(f"{name} does not declare dsh.bundle.patch")
|
|
862
|
+
patch = self._bundle_patch(package)
|
|
863
|
+
root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
|
|
864
|
+
if patch is None:
|
|
865
|
+
# Real upstream plugins may ship without a dsh.bundle.patch yet
|
|
866
|
+
# still be valid Cordis tool plugins (inject: ['tools']). Generate
|
|
867
|
+
# a minimal bundle patch so the dsh profile loader activates them.
|
|
868
|
+
# This is a compatibility shim, not a source modification: the
|
|
869
|
+
# plugin's own code is untouched.
|
|
870
|
+
patch_path = root / "cordis.patch.yml"
|
|
871
|
+
patch_path.write_text(
|
|
872
|
+
"- insert:\n - id: "
|
|
873
|
+
+ name.rsplit("/", 1)[-1].replace("-", "_")
|
|
874
|
+
+ "\n name: '" + name + "'\n",
|
|
875
|
+
encoding="utf-8",
|
|
876
|
+
)
|
|
877
|
+
package.setdefault("dsh", {}).setdefault("bundle", {})["patch"] = "./cordis.patch.yml"
|
|
878
|
+
self._write_json(root / "package.json", package)
|
|
879
|
+
patch = "./cordis.patch.yml"
|
|
770
880
|
relative = Path(patch)
|
|
771
881
|
if relative.is_absolute() or ".." in relative.parts:
|
|
772
882
|
raise DshPluginMutationError(f"{name} declares an unsafe bundle patch path")
|
|
773
|
-
root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
|
|
774
883
|
target = (root / relative).resolve()
|
|
775
884
|
if not target.is_relative_to(root) or not target.is_file():
|
|
776
885
|
raise DshPluginMutationError(f"{name} bundle patch is unavailable")
|
|
@@ -866,6 +975,26 @@ class DshProfilePluginBridge:
|
|
|
866
975
|
manifest["dsh"] = dsh
|
|
867
976
|
self._write_json(self._manifest_path(), manifest)
|
|
868
977
|
|
|
978
|
+
def _resolve_install_source(self, source: str) -> str:
|
|
979
|
+
if not _PACKAGE_NAME.fullmatch(source):
|
|
980
|
+
return source
|
|
981
|
+
# Resolve metadata before running package scripts. The mutable latest
|
|
982
|
+
# selector never reaches `dsh plugin add`; the exact version does.
|
|
983
|
+
result = self._invoke(
|
|
984
|
+
("npm", "view", f"{source}@latest", "version", "--json"), cwd=self._cwd
|
|
985
|
+
)
|
|
986
|
+
try:
|
|
987
|
+
if len(result.stdout) > _MAX_JSON_BYTES:
|
|
988
|
+
raise ValueError("oversized registry response")
|
|
989
|
+
version = json.loads(result.stdout)
|
|
990
|
+
if not isinstance(version, str):
|
|
991
|
+
raise ValueError("expected one version")
|
|
992
|
+
return validate_dsh_registry_source(f"{source}@{version}")
|
|
993
|
+
except (ValueError, TypeError) as error:
|
|
994
|
+
raise DshPluginMutationError(
|
|
995
|
+
"npm latest did not resolve to one exact version"
|
|
996
|
+
) from error
|
|
997
|
+
|
|
869
998
|
def _prepare_source(self, source: str) -> _PreparedSource:
|
|
870
999
|
value = source.strip()
|
|
871
1000
|
candidate = Path(value).expanduser()
|
|
@@ -948,9 +1077,7 @@ class DshProfilePluginBridge:
|
|
|
948
1077
|
)
|
|
949
1078
|
artifact = (self._dsh_home / receipt["artifact"]).resolve()
|
|
950
1079
|
if not artifact.is_relative_to(store) or not artifact.is_file():
|
|
951
|
-
raise DshPluginMutationError(
|
|
952
|
-
f"DSH plugin {name!r} immutable source is unavailable"
|
|
953
|
-
)
|
|
1080
|
+
raise DshPluginMutationError(f"DSH plugin {name!r} immutable source is unavailable")
|
|
954
1081
|
try:
|
|
955
1082
|
actual = f"sha256:{hashlib.sha256(artifact.read_bytes()).hexdigest()}"
|
|
956
1083
|
except OSError as error:
|
|
@@ -958,9 +1085,7 @@ class DshProfilePluginBridge:
|
|
|
958
1085
|
f"DSH plugin {name!r} immutable source is unreadable"
|
|
959
1086
|
) from error
|
|
960
1087
|
if actual != receipt["digest"]:
|
|
961
|
-
raise DshPluginMutationError(
|
|
962
|
-
f"DSH plugin {name!r} immutable source digest changed"
|
|
963
|
-
)
|
|
1088
|
+
raise DshPluginMutationError(f"DSH plugin {name!r} immutable source digest changed")
|
|
964
1089
|
|
|
965
1090
|
@staticmethod
|
|
966
1091
|
def _write_json(path: Path, payload: object) -> None:
|
|
@@ -980,15 +1105,18 @@ class DshProfilePluginBridge:
|
|
|
980
1105
|
except FileNotFoundError:
|
|
981
1106
|
pass
|
|
982
1107
|
|
|
983
|
-
def _snapshot(self) ->
|
|
984
|
-
|
|
1108
|
+
def _snapshot(self) -> _ProfileSnapshot:
|
|
1109
|
+
files: dict[str, bytes | None] = {}
|
|
985
1110
|
for name in _SNAPSHOT_FILES:
|
|
986
1111
|
path = self._profile_root / name
|
|
987
1112
|
try:
|
|
988
|
-
|
|
1113
|
+
files[name] = path.read_bytes()
|
|
989
1114
|
except FileNotFoundError:
|
|
990
|
-
|
|
991
|
-
return
|
|
1115
|
+
files[name] = None
|
|
1116
|
+
return _ProfileSnapshot(
|
|
1117
|
+
files=files,
|
|
1118
|
+
node_modules_existed=(self._profile_root / "node_modules").exists(),
|
|
1119
|
+
)
|
|
992
1120
|
|
|
993
1121
|
def _require_package_mutation_rollback(self, *, new_profile_allowed: bool) -> None:
|
|
994
1122
|
"""Reject package mutations unless their filesystem effects are reversible."""
|
|
@@ -1002,13 +1130,31 @@ class DshProfilePluginBridge:
|
|
|
1002
1130
|
"existing DSH profile directory has no package manifest; refusing to mutate it"
|
|
1003
1131
|
)
|
|
1004
1132
|
if not (self._profile_root / "pnpm-lock.yaml").is_file():
|
|
1133
|
+
if new_profile_allowed and self._is_pristine_official_web_profile():
|
|
1134
|
+
return
|
|
1005
1135
|
raise DshPluginMutationError(
|
|
1006
1136
|
"existing DSH profile has no pnpm lockfile, so package rollback is unavailable"
|
|
1007
1137
|
)
|
|
1008
1138
|
|
|
1009
|
-
def
|
|
1139
|
+
def _is_pristine_official_web_profile(self) -> bool:
|
|
1140
|
+
"""Recognize only the untouched profile generated by ``dsh --profile web``."""
|
|
1141
|
+
|
|
1142
|
+
if self._profile != "web" or (self._profile_root / "node_modules").exists():
|
|
1143
|
+
return False
|
|
1144
|
+
if (self._profile_root / _STATE_FILE).exists():
|
|
1145
|
+
return False
|
|
1146
|
+
manifest = self._read_manifest()
|
|
1147
|
+
return (
|
|
1148
|
+
self._dependencies(manifest) == {}
|
|
1149
|
+
and tuple(self._bundles(manifest)) == _WEB_PROFILE_BUILTINS
|
|
1150
|
+
and (self._profile_root / "pnpm-workspace.yaml").is_file()
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
def _rollback(
|
|
1154
|
+
self, snapshot: _ProfileSnapshot, original: BaseException, *, reinstall: bool = True,
|
|
1155
|
+
) -> None:
|
|
1010
1156
|
try:
|
|
1011
|
-
if all(content is None for content in snapshot.values()):
|
|
1157
|
+
if all(content is None for content in snapshot.files.values()):
|
|
1012
1158
|
profiles_root = (self._dsh_home / "profiles").resolve()
|
|
1013
1159
|
profile_root = self._profile_root.resolve()
|
|
1014
1160
|
if profile_root.parent != profiles_root:
|
|
@@ -1016,18 +1162,29 @@ class DshProfilePluginBridge:
|
|
|
1016
1162
|
if profile_root.exists():
|
|
1017
1163
|
shutil.rmtree(profile_root, ignore_errors=False)
|
|
1018
1164
|
return
|
|
1165
|
+
node_modules = self._profile_root / "node_modules"
|
|
1166
|
+
if not snapshot.node_modules_existed and node_modules.exists():
|
|
1167
|
+
profile_root = self._profile_root.resolve()
|
|
1168
|
+
if node_modules.is_symlink() or node_modules.resolve().parent != profile_root:
|
|
1169
|
+
raise DshPluginMutationError(
|
|
1170
|
+
"refusing to clean an untrusted DSH node_modules path"
|
|
1171
|
+
)
|
|
1172
|
+
shutil.rmtree(node_modules, ignore_errors=False)
|
|
1019
1173
|
self._profile_root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
1020
|
-
for name, content in snapshot.items():
|
|
1174
|
+
for name, content in snapshot.files.items():
|
|
1021
1175
|
path = self._profile_root / name
|
|
1022
1176
|
if content is None:
|
|
1023
1177
|
path.unlink(missing_ok=True)
|
|
1024
1178
|
else:
|
|
1025
1179
|
path.write_bytes(content)
|
|
1026
1180
|
path.chmod(0o600)
|
|
1027
|
-
if
|
|
1181
|
+
if (
|
|
1182
|
+
reinstall and snapshot.files.get("pnpm-lock.yaml") is not None
|
|
1183
|
+
and self._manifest_path().is_file()
|
|
1184
|
+
):
|
|
1028
1185
|
self._plugin_command("install", "--frozen-lockfile")
|
|
1029
1186
|
for name in ("package.json", _STATE_FILE):
|
|
1030
|
-
content = snapshot.get(name)
|
|
1187
|
+
content = snapshot.files.get(name)
|
|
1031
1188
|
path = self._profile_root / name
|
|
1032
1189
|
if content is None:
|
|
1033
1190
|
path.unlink(missing_ok=True)
|
|
@@ -1053,7 +1210,18 @@ class DshProfilePluginBridge:
|
|
|
1053
1210
|
or value.startswith(("file:", "link:"))
|
|
1054
1211
|
or value.startswith(("./", "../", "file:./", "file:../", "link:./", "link:../"))
|
|
1055
1212
|
):
|
|
1056
|
-
raise ValueError(
|
|
1213
|
+
raise ValueError(
|
|
1214
|
+
"DSH plugin source must be an exact registry version or absolute local path"
|
|
1215
|
+
)
|
|
1216
|
+
candidate = Path(value).expanduser()
|
|
1217
|
+
if candidate.is_absolute():
|
|
1218
|
+
if not candidate.exists():
|
|
1219
|
+
raise ValueError("absolute local DSH plugin source does not exist")
|
|
1220
|
+
return
|
|
1221
|
+
# Bare names resolve latest to an exact version before package mutation.
|
|
1222
|
+
# Git, URL, explicit tags and ranges remain unsupported. Local sources
|
|
1223
|
+
# are packed and content-addressed by _prepare_source.
|
|
1224
|
+
validate_dsh_registry_request(value)
|
|
1057
1225
|
|
|
1058
1226
|
@staticmethod
|
|
1059
1227
|
def _validate_package_name(name: str) -> None:
|
|
@@ -1071,15 +1239,30 @@ class DshProfilePluginBridge:
|
|
|
1071
1239
|
environment: Mapping[str, str],
|
|
1072
1240
|
) -> _CommandResult:
|
|
1073
1241
|
try:
|
|
1074
|
-
|
|
1242
|
+
process = subprocess.Popen(
|
|
1075
1243
|
list(command),
|
|
1076
1244
|
cwd=cwd,
|
|
1077
1245
|
env=dict(environment),
|
|
1078
|
-
|
|
1079
|
-
|
|
1246
|
+
stdout=subprocess.PIPE,
|
|
1247
|
+
stderr=subprocess.PIPE,
|
|
1080
1248
|
text=True,
|
|
1081
|
-
|
|
1249
|
+
start_new_session=os.name == "posix",
|
|
1082
1250
|
)
|
|
1251
|
+
try:
|
|
1252
|
+
stdout, stderr = process.communicate(timeout=_COMMAND_TIMEOUT_SECONDS)
|
|
1253
|
+
except BaseException:
|
|
1254
|
+
# DSH synchronously waits on pnpm. Stop both before profile
|
|
1255
|
+
# rollback, including when a child still owns the output pipes.
|
|
1256
|
+
if os.name == "posix":
|
|
1257
|
+
try:
|
|
1258
|
+
os.killpg(process.pid, signal.SIGKILL)
|
|
1259
|
+
except ProcessLookupError:
|
|
1260
|
+
pass
|
|
1261
|
+
else: # pragma: no cover - supported production hosts are Unix
|
|
1262
|
+
process.kill()
|
|
1263
|
+
process.communicate()
|
|
1264
|
+
raise
|
|
1265
|
+
completed = subprocess.CompletedProcess(command, process.returncode, stdout, stderr)
|
|
1083
1266
|
except (OSError, subprocess.TimeoutExpired) as error:
|
|
1084
1267
|
raise DshHostUnavailableError("DSH host command did not complete") from error
|
|
1085
1268
|
if completed.returncode != 0:
|
|
@@ -1104,7 +1287,6 @@ def _redact_diagnostic(value: str) -> str:
|
|
|
1104
1287
|
__all__ = [
|
|
1105
1288
|
"DshBridgeError",
|
|
1106
1289
|
"DshBridgeHost",
|
|
1107
|
-
"DshClientBundle",
|
|
1108
1290
|
"DshHostUnavailableError",
|
|
1109
1291
|
"DshPluginApprovalRequired",
|
|
1110
1292
|
"DshPluginInventory",
|
|
@@ -1112,5 +1294,7 @@ __all__ = [
|
|
|
1112
1294
|
"DshPluginNotFoundError",
|
|
1113
1295
|
"DshProfilePluginBridge",
|
|
1114
1296
|
"DshProfileProjection",
|
|
1297
|
+
"DshProfileRecoveryError",
|
|
1115
1298
|
"dsh_subprocess_environment",
|
|
1299
|
+
"validate_dsh_registry_source",
|
|
1116
1300
|
]
|