@skaile/workspaces 6.0.0 → 6.1.0
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 +50 -0
- package/dist/{asset-feeds-PXUWCXVI.js → asset-feeds-HJFV7UPW.js} +26 -18
- package/dist/asset-feeds-HJFV7UPW.js.map +1 -0
- package/dist/asset-manager/catalog-deployer.js +7 -7
- package/dist/asset-manager/index.js +13 -13
- package/dist/asset-manager/installer.js +8 -8
- package/dist/asset-manager/renderers.js +7 -7
- package/dist/asset-manager/src/index.d.ts +192 -5
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/asset-manager/src/installer.d.ts +31 -0
- package/dist/asset-manager/src/installer.d.ts.map +1 -1
- package/dist/asset-manager/src/mcp-config.d.ts +21 -0
- package/dist/asset-manager/src/mcp-config.d.ts.map +1 -1
- package/dist/bridge/harnesses/claude-sdk.js +13 -17
- package/dist/bridge/harnesses/claude-sdk.js.map +1 -1
- package/dist/bridge/harnesses/codex.js +10 -10
- package/dist/bridge/harnesses/continue.js +10 -10
- package/dist/bridge/harnesses/echo.js +2 -2
- package/dist/bridge/harnesses/gemini.js +6 -6
- package/dist/bridge/harnesses/goose.js +10 -10
- package/dist/bridge/harnesses/omp.js +4 -4
- package/dist/bridge/harnesses/opencode.js +10 -10
- package/dist/bridge/harnesses/qwen.js +6 -6
- package/dist/bridge/index.js +14 -14
- package/dist/bridge/src/claude-sdk-surface.d.ts +17 -0
- package/dist/bridge/src/claude-sdk-surface.d.ts.map +1 -0
- package/dist/bridge/src/harness-model-check.d.ts +15 -0
- package/dist/bridge/src/harness-model-check.d.ts.map +1 -1
- package/dist/bridge/src/harnesses/claude-sdk.d.ts.map +1 -1
- package/dist/bridge/src/index.d.ts +2 -1
- package/dist/bridge/src/index.d.ts.map +1 -1
- package/dist/bridge/src/normalizer.d.ts +3 -1
- package/dist/bridge/src/normalizer.d.ts.map +1 -1
- package/dist/{chunk-VJRTDKXS.js → chunk-2LAPHLDH.js} +144 -94
- package/dist/chunk-2LAPHLDH.js.map +1 -0
- package/dist/{chunk-VXEYX73S.js → chunk-3GMOPFWH.js} +4 -4
- package/dist/{chunk-VXEYX73S.js.map → chunk-3GMOPFWH.js.map} +1 -1
- package/dist/{chunk-MCUJTNY5.js → chunk-4FVIT5DI.js} +3 -3
- package/dist/{chunk-MCUJTNY5.js.map → chunk-4FVIT5DI.js.map} +1 -1
- package/dist/{chunk-SZXGRHQQ.js → chunk-4MUDQCBP.js} +4 -4
- package/dist/{chunk-SZXGRHQQ.js.map → chunk-4MUDQCBP.js.map} +1 -1
- package/dist/{chunk-7CV3HFWJ.js → chunk-5J4NPGFM.js} +3 -3
- package/dist/{chunk-7CV3HFWJ.js.map → chunk-5J4NPGFM.js.map} +1 -1
- package/dist/{chunk-4XWLB7JB.js → chunk-5Y2KGB4H.js} +69 -34
- package/dist/chunk-5Y2KGB4H.js.map +1 -0
- package/dist/{chunk-OULSJRXV.js → chunk-6YR4ALVX.js} +6 -6
- package/dist/{chunk-OULSJRXV.js.map → chunk-6YR4ALVX.js.map} +1 -1
- package/dist/chunk-7HX6FNKL.js +317 -0
- package/dist/chunk-7HX6FNKL.js.map +1 -0
- package/dist/{chunk-WDAIWDLK.js → chunk-7LP7RCZI.js} +4 -4
- package/dist/{chunk-WDAIWDLK.js.map → chunk-7LP7RCZI.js.map} +1 -1
- package/dist/{chunk-2UUKCROR.js → chunk-AATHJRKL.js} +4 -4
- package/dist/{chunk-2UUKCROR.js.map → chunk-AATHJRKL.js.map} +1 -1
- package/dist/{chunk-DUMTLYSO.js → chunk-ABFPSVIS.js} +3 -3
- package/dist/{chunk-DUMTLYSO.js.map → chunk-ABFPSVIS.js.map} +1 -1
- package/dist/{chunk-TKHJXPSB.js → chunk-AITJ7ELM.js} +140 -26
- package/dist/chunk-AITJ7ELM.js.map +1 -0
- package/dist/{chunk-Y2TO2U7W.js → chunk-ASWTQQ7E.js} +3 -3
- package/dist/{chunk-Y2TO2U7W.js.map → chunk-ASWTQQ7E.js.map} +1 -1
- package/dist/{chunk-WYHQUCHR.js → chunk-AZ2H6R5I.js} +51 -25
- package/dist/chunk-AZ2H6R5I.js.map +1 -0
- package/dist/{chunk-LN6QB7XI.js → chunk-BBBCJSSU.js} +4 -4
- package/dist/{chunk-LN6QB7XI.js.map → chunk-BBBCJSSU.js.map} +1 -1
- package/dist/{chunk-WEH33JSG.js → chunk-CEKW3KXP.js} +87 -26
- package/dist/chunk-CEKW3KXP.js.map +1 -0
- package/dist/{chunk-ARWKK325.js → chunk-COYF6IG2.js} +5 -5
- package/dist/{chunk-ARWKK325.js.map → chunk-COYF6IG2.js.map} +1 -1
- package/dist/{chunk-SNEE6UGY.js → chunk-DGTI65OC.js} +11 -11
- package/dist/{chunk-SNEE6UGY.js.map → chunk-DGTI65OC.js.map} +1 -1
- package/dist/{chunk-HK7HMEPT.js → chunk-DIGFA7Z5.js} +3 -3
- package/dist/{chunk-HK7HMEPT.js.map → chunk-DIGFA7Z5.js.map} +1 -1
- package/dist/{chunk-AF432MS3.js → chunk-EJPMNDTP.js} +3 -3
- package/dist/{chunk-AF432MS3.js.map → chunk-EJPMNDTP.js.map} +1 -1
- package/dist/chunk-EX3GLAL6.js +31 -0
- package/dist/chunk-EX3GLAL6.js.map +1 -0
- package/dist/{chunk-UVVVR4XO.js → chunk-FKQNBCDW.js} +3 -3
- package/dist/{chunk-UVVVR4XO.js.map → chunk-FKQNBCDW.js.map} +1 -1
- package/dist/{chunk-GEQSUC6U.js → chunk-GNZBFSRB.js} +3 -3
- package/dist/{chunk-GEQSUC6U.js.map → chunk-GNZBFSRB.js.map} +1 -1
- package/dist/{chunk-U56GGCC2.js → chunk-HMISULVD.js} +4 -4
- package/dist/{chunk-U56GGCC2.js.map → chunk-HMISULVD.js.map} +1 -1
- package/dist/{chunk-GN4VVSAA.js → chunk-HPMQRAIF.js} +7 -7
- package/dist/{chunk-GN4VVSAA.js.map → chunk-HPMQRAIF.js.map} +1 -1
- package/dist/{chunk-OAHVKSQJ.js → chunk-HWNK3F3X.js} +913 -80
- package/dist/chunk-HWNK3F3X.js.map +1 -0
- package/dist/{chunk-NMI2TWMH.js → chunk-IHFGPHCT.js} +131 -341
- package/dist/chunk-IHFGPHCT.js.map +1 -0
- package/dist/{chunk-F2GOZMRA.js → chunk-ILLTBKDJ.js} +47 -47
- package/dist/chunk-ILLTBKDJ.js.map +1 -0
- package/dist/{chunk-FKU57Q5Q.js → chunk-IRYKRKVQ.js} +3 -3
- package/dist/{chunk-FKU57Q5Q.js.map → chunk-IRYKRKVQ.js.map} +1 -1
- package/dist/{chunk-T2BDID2J.js → chunk-J72MO2WA.js} +8 -8
- package/dist/{chunk-T2BDID2J.js.map → chunk-J72MO2WA.js.map} +1 -1
- package/dist/{chunk-X3RMRJNR.js → chunk-JRN5QF4C.js} +8 -3
- package/dist/chunk-JRN5QF4C.js.map +1 -0
- package/dist/{chunk-E3GPGTW3.js → chunk-KUQAJVYT.js} +5 -5
- package/dist/{chunk-E3GPGTW3.js.map → chunk-KUQAJVYT.js.map} +1 -1
- package/dist/{chunk-WDXZA7QW.js → chunk-LSSYC5IC.js} +2 -2
- package/dist/{chunk-WDXZA7QW.js.map → chunk-LSSYC5IC.js.map} +1 -1
- package/dist/{chunk-3IXIEHUB.js → chunk-NB7SYQYP.js} +5 -5
- package/dist/{chunk-3IXIEHUB.js.map → chunk-NB7SYQYP.js.map} +1 -1
- package/dist/{chunk-3JKGY2YC.js → chunk-NUA33BYO.js} +149 -27
- package/dist/chunk-NUA33BYO.js.map +1 -0
- package/dist/{chunk-VBI7HGHB.js → chunk-QAKUD6QN.js} +2 -2
- package/dist/{chunk-VBI7HGHB.js.map → chunk-QAKUD6QN.js.map} +1 -1
- package/dist/{chunk-KX2PT5Q4.js → chunk-QEULL4Q3.js} +4 -4
- package/dist/{chunk-KX2PT5Q4.js.map → chunk-QEULL4Q3.js.map} +1 -1
- package/dist/{chunk-YGRZNFRO.js → chunk-QSYQOTWN.js} +3 -3
- package/dist/{chunk-YGRZNFRO.js.map → chunk-QSYQOTWN.js.map} +1 -1
- package/dist/{chunk-DHYSUJ3H.js → chunk-RDTLPOPD.js} +4 -4
- package/dist/{chunk-DHYSUJ3H.js.map → chunk-RDTLPOPD.js.map} +1 -1
- package/dist/{chunk-GOCWM7FN.js → chunk-RHN5MRQO.js} +21 -4
- package/dist/chunk-RHN5MRQO.js.map +1 -0
- package/dist/{chunk-UL22SDGR.js → chunk-S2XHGWQC.js} +161 -123
- package/dist/chunk-S2XHGWQC.js.map +1 -0
- package/dist/{chunk-4DWBSTYA.js → chunk-SVTMNSPI.js} +22 -5
- package/dist/chunk-SVTMNSPI.js.map +1 -0
- package/dist/{chunk-QOM5Z62G.js → chunk-UI745XC3.js} +9 -22
- package/dist/chunk-UI745XC3.js.map +1 -0
- package/dist/{chunk-L4YKW5AY.js → chunk-VMVDAZLA.js} +20 -8
- package/dist/chunk-VMVDAZLA.js.map +1 -0
- package/dist/{chunk-H6UVIKQ2.js → chunk-VYVTEMAW.js} +3 -3
- package/dist/{chunk-H6UVIKQ2.js.map → chunk-VYVTEMAW.js.map} +1 -1
- package/dist/{chunk-SRODW6M2.js → chunk-X3YGUKCG.js} +3 -3
- package/dist/{chunk-SRODW6M2.js.map → chunk-X3YGUKCG.js.map} +1 -1
- package/dist/{chunk-JDR6ILCW.js → chunk-XGULGQA3.js} +3 -3
- package/dist/{chunk-JDR6ILCW.js.map → chunk-XGULGQA3.js.map} +1 -1
- package/dist/{chunk-7XSAW5OC.js → chunk-YLLVSR2U.js} +4 -4
- package/dist/{chunk-7XSAW5OC.js.map → chunk-YLLVSR2U.js.map} +1 -1
- package/dist/{chunk-6Z5KTFJT.js → chunk-Z3VKUWAX.js} +3 -3
- package/dist/{chunk-6Z5KTFJT.js.map → chunk-Z3VKUWAX.js.map} +1 -1
- package/dist/{chunk-7TPYPVAH.js → chunk-ZAELRYKS.js} +3 -3
- package/dist/{chunk-7TPYPVAH.js.map → chunk-ZAELRYKS.js.map} +1 -1
- package/dist/{chunk-UQ33Y4DM.js → chunk-ZBHZLWV3.js} +4 -4
- package/dist/{chunk-UQ33Y4DM.js.map → chunk-ZBHZLWV3.js.map} +1 -1
- package/dist/{chunk-OTIB4D2K.js → chunk-ZDLUQLI6.js} +4 -4
- package/dist/{chunk-OTIB4D2K.js.map → chunk-ZDLUQLI6.js.map} +1 -1
- package/dist/{chunk-OHNKYHRR.js → chunk-ZHGKAZJ7.js} +214 -16
- package/dist/chunk-ZHGKAZJ7.js.map +1 -0
- package/dist/{claude-code-VJGYBXQR.js → claude-code-RE46FJXI.js} +4 -4
- package/dist/{claude-code-VJGYBXQR.js.map → claude-code-RE46FJXI.js.map} +1 -1
- package/dist/cli/index.js +704 -448
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/agent-pins.d.ts +11 -0
- package/dist/cli/src/agent-pins.d.ts.map +1 -1
- package/dist/cli/src/commands/catalog.d.ts +1 -1
- package/dist/cli/src/commands/catalog.d.ts.map +1 -1
- package/dist/cli/src/commands/config.d.ts.map +1 -1
- package/dist/cli/src/commands/diff.d.ts.map +1 -1
- package/dist/cli/src/commands/harness.d.ts +24 -0
- package/dist/cli/src/commands/harness.d.ts.map +1 -0
- package/dist/cli/src/commands/integration.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-index.d.ts +20 -2
- package/dist/cli/src/commands/library-index.d.ts.map +1 -1
- package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
- package/dist/cli/src/commands/migrate-assets.d.ts.map +1 -1
- package/dist/cli/src/commands/project.d.ts +19 -1
- package/dist/cli/src/commands/project.d.ts.map +1 -1
- package/dist/cli/src/commands/run.d.ts.map +1 -1
- package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -1
- package/dist/cli/src/commands/session-logs/local-query.d.ts +2 -0
- package/dist/cli/src/commands/session-logs/local-query.d.ts.map +1 -1
- package/dist/cli/src/commands/source.d.ts +9 -2
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/store.d.ts.map +1 -1
- package/dist/cli/src/commands/update.d.ts.map +1 -1
- package/dist/cli/src/commands/validate.d.ts.map +1 -1
- package/dist/cli/src/config-checks.d.ts +37 -0
- package/dist/cli/src/config-checks.d.ts.map +1 -0
- package/dist/cli/src/skill-checks.d.ts +27 -0
- package/dist/cli/src/skill-checks.d.ts.map +1 -1
- package/dist/cli/src/store-addable.d.ts +11 -0
- package/dist/cli/src/store-addable.d.ts.map +1 -1
- package/dist/connectors/config.js +5 -5
- package/dist/connectors/index.js +18 -18
- package/dist/{connectors-CXZLXAFZ.js → connectors-7THEF7YA.js} +21 -21
- package/dist/{connectors-CXZLXAFZ.js.map → connectors-7THEF7YA.js.map} +1 -1
- package/dist/core/index.js +6 -6
- package/dist/core/manifest.js +2 -2
- package/dist/core/models.js +1 -1
- package/dist/core/publish-manifest.js +3 -3
- package/dist/core/ref.js +1 -1
- package/dist/core/runtime-assets.js +5 -5
- package/dist/core/src/agent-instances.d.ts.map +1 -1
- package/dist/core/src/config-key-diagnostics.d.ts +9 -0
- package/dist/core/src/config-key-diagnostics.d.ts.map +1 -0
- package/dist/core/src/index.d.ts +1 -1
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/manifest.d.ts +10 -0
- package/dist/core/src/manifest.d.ts.map +1 -1
- package/dist/core/src/models.d.ts +8 -0
- package/dist/core/src/models.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +8 -0
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/src/workspace-yaml-editor.d.ts +9 -2
- package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
- package/dist/core/workspace-config.js +4 -4
- package/dist/deploy/index.js +6 -6
- package/dist/discovery/index.js +4 -4
- package/dist/discovery/src/builtin-providers.d.ts.map +1 -1
- package/dist/discovery/src/parse-manifest.d.ts.map +1 -1
- package/dist/{ensure-sources-UPTHZKGF.js → ensure-sources-IH7CHSJF.js} +16 -16
- package/dist/{ensure-sources-UPTHZKGF.js.map → ensure-sources-IH7CHSJF.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +18 -18
- package/dist/factory-assets/connectors/deploy.js +18 -18
- package/dist/factory-assets/connectors/devserver.js +18 -18
- package/dist/factory-assets/connectors/flow/adapter.js +18 -18
- package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +8 -8
- package/dist/factory-assets/connectors/flow/run-flow.js +20 -20
- package/dist/factory-assets/connectors/flow.js +18 -18
- package/dist/factory-assets/connectors/git.js +18 -18
- package/dist/factory-assets/connectors/gmail.js +18 -18
- package/dist/factory-assets/connectors/googledrive.js +18 -18
- package/dist/factory-assets/connectors/local.js +18 -18
- package/dist/factory-assets/connectors/mattermost.js +18 -18
- package/dist/factory-assets/connectors/memory.js +18 -18
- package/dist/factory-assets/connectors/minio.js +18 -18
- package/dist/factory-assets/connectors/postgres.js +18 -18
- package/dist/factory-assets/connectors/s3.js +18 -18
- package/dist/factory-assets/connectors/sharepoint.js +18 -18
- package/dist/factory-assets/connectors/sqlite.js +18 -18
- package/dist/factory-assets/connectors/static-server.js +18 -18
- package/dist/factory-assets/connectors/tunnel.js +18 -18
- package/dist/factory-assets/connectors/webdav.js +18 -18
- package/dist/factory-assets/connectors/xstate-store.js +18 -18
- package/dist/factory-assets/connectors/xstate.js +18 -18
- package/dist/factory-assets/skills/authoring-skaile-assets/SKILL.md +1 -1
- package/dist/factory-assets/skills/authoring-skaile-manifests/SKILL.md +2 -2
- package/dist/factory-assets/skills/authoring-skaile-manifests/references/dependency-standard.md +3 -3
- package/dist/{flows-TJQIMGUX.js → flows-4JZQ3XFK.js} +11 -11
- package/dist/{flows-TJQIMGUX.js.map → flows-4JZQ3XFK.js.map} +1 -1
- package/dist/{helpers-DE53KG3E.js → helpers-JNMK3FC2.js} +16 -16
- package/dist/{helpers-DE53KG3E.js.map → helpers-JNMK3FC2.js.map} +1 -1
- package/dist/library/index.js +9 -9
- package/dist/library/install.js +4 -4
- package/dist/library/src/index.d.ts +1 -1
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/local/library-dir.d.ts +22 -5
- package/dist/library/src/local/library-dir.d.ts.map +1 -1
- package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
- package/dist/library/src/remote/remote-catalog-source.d.ts +6 -0
- package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
- package/dist/{library-publish-KKQ2ULLX.js → library-publish-I3UWXAHI.js} +52 -50
- package/dist/library-publish-I3UWXAHI.js.map +1 -0
- package/dist/model-endpoint/index.js +9 -9
- package/dist/{open-library-RJGJIW5H.js → open-library-NKKUJLWA.js} +19 -19
- package/dist/{open-library-RJGJIW5H.js.map → open-library-NKKUJLWA.js.map} +1 -1
- package/dist/{open-registry-3QS3J7KO.js → open-registry-BYAXSHUI.js} +14 -14
- package/dist/{open-registry-3QS3J7KO.js.map → open-registry-BYAXSHUI.js.map} +1 -1
- package/dist/{paths-QAV4C3KX.js → paths-UKWE35XS.js} +9 -9
- package/dist/{paths-QAV4C3KX.js.map → paths-UKWE35XS.js.map} +1 -1
- package/dist/{plugin-store-MSH6GK6T.js → plugin-store-ABE35NRX.js} +9 -9
- package/dist/{plugin-store-MSH6GK6T.js.map → plugin-store-ABE35NRX.js.map} +1 -1
- package/dist/project-BIPK47JK.js +86 -0
- package/dist/{project-HZW2MZPA.js.map → project-BIPK47JK.js.map} +1 -1
- package/dist/resolver/index.js +7 -7
- package/dist/runner/index.js +33 -33
- package/dist/runner/src/composition/content-source.d.ts.map +1 -1
- package/dist/runner/src/composition/errors.d.ts +16 -1
- package/dist/runner/src/composition/errors.d.ts.map +1 -1
- package/dist/runner/src/credential-notice.d.ts +3 -3
- package/dist/runner/src/credential-notice.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts +14 -2
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +13 -13
- package/dist/sdk/bridge.js +14 -14
- package/dist/sdk/core.js +6 -6
- package/dist/sdk/flow.js +8 -8
- package/dist/sdk/index.js +33 -33
- package/dist/sdk/resolver.js +7 -7
- package/dist/sdk/runner.js +33 -33
- package/dist/sdk/transport/ws/server.js +7 -7
- package/dist/sdk/transport/ws.js +7 -7
- package/dist/sdk/transport.js +7 -7
- package/dist/{serve-preflight-L4POEHNQ.js → serve-preflight-FLTIBTEX.js} +28 -29
- package/dist/serve-preflight-FLTIBTEX.js.map +1 -0
- package/dist/{setup-AGYB4PKR.js → setup-AG6NFHSN.js} +25 -25
- package/dist/{setup-AGYB4PKR.js.map → setup-AG6NFHSN.js.map} +1 -1
- package/dist/{skills-2LMQ2IAD.js → skills-UQYUHVKO.js} +12 -12
- package/dist/{skills-2LMQ2IAD.js.map → skills-UQYUHVKO.js.map} +1 -1
- package/dist/source-ASEHI2XS.js +85 -0
- package/dist/{source-GBMYD3RO.js.map → source-ASEHI2XS.js.map} +1 -1
- package/dist/{source-manifest-publish-FK22TVQT.js → source-manifest-publish-IEVV3W6J.js} +45 -45
- package/dist/{source-manifest-publish-FK22TVQT.js.map → source-manifest-publish-IEVV3W6J.js.map} +1 -1
- package/dist/transport/index.js +7 -7
- package/dist/transport/ws/server.js +7 -7
- package/dist/transport/ws.js +7 -7
- package/dist/tui/index.js +33 -33
- package/dist/types/src/install-manifest.d.ts +1 -1
- package/dist/types/src/install-manifest.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +25 -25
- package/dist/workspace-plugin/adapters/omp.js +14 -14
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/asset-feeds-PXUWCXVI.js.map +0 -1
- package/dist/chunk-3JKGY2YC.js.map +0 -1
- package/dist/chunk-4DWBSTYA.js.map +0 -1
- package/dist/chunk-4XWLB7JB.js.map +0 -1
- package/dist/chunk-F2GOZMRA.js.map +0 -1
- package/dist/chunk-GOCWM7FN.js.map +0 -1
- package/dist/chunk-L4YKW5AY.js.map +0 -1
- package/dist/chunk-NMI2TWMH.js.map +0 -1
- package/dist/chunk-OAHVKSQJ.js.map +0 -1
- package/dist/chunk-OHNKYHRR.js.map +0 -1
- package/dist/chunk-PUK7TQT4.js +0 -13
- package/dist/chunk-PUK7TQT4.js.map +0 -1
- package/dist/chunk-QOM5Z62G.js.map +0 -1
- package/dist/chunk-TKHJXPSB.js.map +0 -1
- package/dist/chunk-UL22SDGR.js.map +0 -1
- package/dist/chunk-VJRTDKXS.js.map +0 -1
- package/dist/chunk-WEH33JSG.js.map +0 -1
- package/dist/chunk-WYHQUCHR.js.map +0 -1
- package/dist/chunk-X3RMRJNR.js.map +0 -1
- package/dist/library-publish-KKQ2ULLX.js.map +0 -1
- package/dist/project-HZW2MZPA.js +0 -84
- package/dist/serve-preflight-L4POEHNQ.js.map +0 -1
- package/dist/source-GBMYD3RO.js +0 -85
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import { unregisteredLibraryNames, renamedLibraryName, resolveConfig, RemoteCatalogSource, OfflineError, describeCatalogError, CatalogHttpError, scanUnlistedAssets, missingRootAssets, storeManifestFile, missingLibraryDirMessage, filesEntryPaths } from './chunk-
|
|
2
|
-
import { SEMVER_RE, invalidSemverMessage, flowFileNameError, readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-
|
|
3
|
-
import { parseFlowDefinition } from './chunk-
|
|
4
|
-
import { installTargetOf, readKeptLegacyCopies, writeKeptLegacyCopies, canonicalEntryRef, diffDeployedAgent, deployedAssetDrift, assertNoLibrarySymlinks, deployAll, removeAsset, createScaffold, installAgent } from './chunk-
|
|
1
|
+
import { unregisteredLibraryNames, renamedLibraryName, resolveConfig, RemoteCatalogSource, OfflineError, describeCatalogError, CatalogHttpError, scanUnlistedAssets, missingRootAssets, storeManifestFile, missingLibraryDirMessage, filesEntryPaths, lastLibraryName } from './chunk-S2XHGWQC.js';
|
|
2
|
+
import { SEMVER_RE, invalidSemverMessage, flowFileNameError, computeDeterministicHash, readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-UI745XC3.js';
|
|
3
|
+
import { parseFlowDefinition } from './chunk-JRN5QF4C.js';
|
|
4
|
+
import { installTargetOf, readKeptLegacyCopies, writeKeptLegacyCopies, canonicalEntryRef, diffDeployedAgent, deployedAssetDrift, assertNoLibrarySymlinks, deployAll, removeAsset, sourceComposedFiles, cachedComposedFiles, createScaffold, installAgent } from './chunk-NUA33BYO.js';
|
|
5
5
|
import { appendHistory, getRecentHistory, clearHistory } from './chunk-KTBKW2FI.js';
|
|
6
6
|
import { commitChanges } from './chunk-UQ6LFBPZ.js';
|
|
7
|
-
import { harnessTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-
|
|
7
|
+
import { harnessTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-ABFPSVIS.js';
|
|
8
8
|
import { isScaffoldBody, isScaffoldDescription } from './chunk-E572OHGY.js';
|
|
9
|
-
import { resolveConfiguredHarnessName, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, computeHash, buildLockFile, readLock, assertLockHashes, verifyLock, mergeLockFile, resyncLockSources, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-
|
|
10
|
-
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-
|
|
9
|
+
import { resolveConfiguredHarnessName, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, computeHash, buildLockFile, readLock, assertLockHashes, verifyLock, mergeLockFile, resyncLockSources, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-VMVDAZLA.js';
|
|
10
|
+
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-ZDLUQLI6.js';
|
|
11
11
|
import { deployedBase, isDeployed, deployedPaths, deployedDir } from './chunk-5KRMNUL4.js';
|
|
12
|
-
import { resolveSkWorkspaceConfig, effectiveAgentInstances, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, readLinks, isCommitOnRemote, resolveAll, agentSourceCacheDir, ensureRepo, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus,
|
|
12
|
+
import { resolveSkWorkspaceConfig, effectiveAgentInstances, mcpDeclarationFromMdContent, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, readLinks, isCommitOnRemote, findWorkspaceRoot, resolveAll, agentSourceCacheDir, ensureRepo, resolveAgentDefinitionDir, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, MAX_PRESET_DEPTH, PresetDepthError, DEFAULT_AGENT_INSTANCE_ID } from './chunk-ZHGKAZJ7.js';
|
|
13
13
|
import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-L5VSIPQD.js';
|
|
14
14
|
import { HARNESS_TARGETS, SUPPORTED_HARNESSES } from './chunk-VTETQCR6.js';
|
|
15
|
-
import { loadSkPublishManifest,
|
|
16
|
-
import { scanDirectory, parseFrontmatter, parseRequires, dedupeRequirements, readAssetBody } from './chunk-
|
|
17
|
-
import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-
|
|
15
|
+
import { listAssetFiles, loadSkPublishManifest, decodeSkaileManifest, isNonAssetDir, isNonAssetFile } from './chunk-ZBHZLWV3.js';
|
|
16
|
+
import { scanDirectory, parseFrontmatter, parseRequires, dedupeRequirements, readAssetBody } from './chunk-RHN5MRQO.js';
|
|
17
|
+
import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-LSSYC5IC.js';
|
|
18
18
|
import { spawnSync } from 'child_process';
|
|
19
|
-
import { existsSync, rmSync, readFileSync, readdirSync, lstatSync,
|
|
19
|
+
import { existsSync, rmSync, readFileSync, writeFileSync, readdirSync, lstatSync, statSync, unlinkSync, mkdirSync } from 'fs';
|
|
20
|
+
import { homedir } from 'os';
|
|
20
21
|
import path, { resolve, dirname, sep, join, relative, posix } from 'path';
|
|
21
22
|
import { gt } from 'semver';
|
|
22
23
|
import { parse, stringify } from 'yaml';
|
|
@@ -595,6 +596,14 @@ function requiredFieldsAdvice(id, keys) {
|
|
|
595
596
|
const setting = keys.map((k) => `${k}: env:${k}`).join(", ");
|
|
596
597
|
return `needs ${keys.join(", ")} (required): set it with an mcp_servers entry (id: ${id}, env: ${setting}) in skaile.yaml and export the variable, then run skaile install`;
|
|
597
598
|
}
|
|
599
|
+
function mcpNotRunnableReason(mdContent, name) {
|
|
600
|
+
try {
|
|
601
|
+
const decl = mcpDeclarationFromMdContent(mdContent, name);
|
|
602
|
+
return decl ? skipReason(decl) : void 0;
|
|
603
|
+
} catch {
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
598
607
|
function mcpConfigFileOf(harnessTarget) {
|
|
599
608
|
return TARGET_MCP_CONFIG[harnessTarget];
|
|
600
609
|
}
|
|
@@ -616,6 +625,38 @@ async function mcpRenderedFootprint(projectDir, harnessTarget, name) {
|
|
|
616
625
|
...dir && hasPayload ? { payloadDir: `${path.relative(projectDir, dir).split(path.sep).join("/")}/` } : {}
|
|
617
626
|
};
|
|
618
627
|
}
|
|
628
|
+
async function mcpCommittedRenderIntact(projectDir, harnessTarget, name) {
|
|
629
|
+
const relConfig = TARGET_MCP_CONFIG[harnessTarget];
|
|
630
|
+
if (!relConfig) return false;
|
|
631
|
+
if ((await readNotedSkips(projectDir, harnessTarget))[name] !== void 0) return true;
|
|
632
|
+
let ledger;
|
|
633
|
+
let servers;
|
|
634
|
+
try {
|
|
635
|
+
ledger = await readManaged(projectDir, harnessTarget);
|
|
636
|
+
const file = await readJsonStrict(path.join(projectDir, relConfig));
|
|
637
|
+
servers = file?.mcpServers ?? {};
|
|
638
|
+
} catch {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
if (!Object.hasOwn(servers, name)) return false;
|
|
642
|
+
const entry = servers[name];
|
|
643
|
+
const recorded = ledger.ids.includes(name) ? ledger.rendered[name] : void 0;
|
|
644
|
+
if (recorded !== void 0 && entryHash(entry) !== recorded) return false;
|
|
645
|
+
const e = entry ?? {};
|
|
646
|
+
const strings = [e.command, ...Array.isArray(e.args) ? e.args : []].filter(
|
|
647
|
+
(v) => typeof v === "string"
|
|
648
|
+
);
|
|
649
|
+
for (const v of strings) {
|
|
650
|
+
const at = v.indexOf(".skaile/assets/");
|
|
651
|
+
if (at < 0) continue;
|
|
652
|
+
try {
|
|
653
|
+
await fs.stat(path.join(projectDir, v.slice(at)));
|
|
654
|
+
} catch {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
619
660
|
async function renderMcpServerConfig(projectDir, harnessTarget, opts) {
|
|
620
661
|
const warn = opts?.warn ?? ((m) => console.warn(`skaile: ${m}`));
|
|
621
662
|
const relConfig = TARGET_MCP_CONFIG[harnessTarget];
|
|
@@ -839,6 +880,16 @@ function libraryDiskHashes(libPath, cache, registry) {
|
|
|
839
880
|
}
|
|
840
881
|
return hashes;
|
|
841
882
|
}
|
|
883
|
+
function repoManifestVersion(repoDir) {
|
|
884
|
+
try {
|
|
885
|
+
const { manifest } = decodeSkaileManifest(
|
|
886
|
+
readFileSync(join(repoDir, "skaile.manifest.yaml"), "utf8")
|
|
887
|
+
);
|
|
888
|
+
return manifest.version || void 0;
|
|
889
|
+
} catch {
|
|
890
|
+
return void 0;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
842
893
|
function manifestFileHasAssetsArray(libPath) {
|
|
843
894
|
try {
|
|
844
895
|
const doc = parse(readFileSync(join(libPath, "skaile.manifest.yaml"), "utf8"));
|
|
@@ -847,6 +898,10 @@ function manifestFileHasAssetsArray(libPath) {
|
|
|
847
898
|
return false;
|
|
848
899
|
}
|
|
849
900
|
}
|
|
901
|
+
var libraryAssetCheck;
|
|
902
|
+
function setLibraryAssetCheck(check) {
|
|
903
|
+
libraryAssetCheck = check;
|
|
904
|
+
}
|
|
850
905
|
function isUnlistedLibraryAsset(entry, libPath) {
|
|
851
906
|
const manifest = loadSkPublishManifest(libPath);
|
|
852
907
|
const listed = manifest?.assets ?? [];
|
|
@@ -1117,7 +1172,9 @@ function relockedRefs(prev, next) {
|
|
|
1117
1172
|
e.library?.version ?? "",
|
|
1118
1173
|
e.library && !e.library.installed ? e.library.name : "",
|
|
1119
1174
|
e.library?.sha256 ?? e.sha256,
|
|
1120
|
-
key.includes("#0.0.0-sha.") ? "" : e.source?.commit ?? ""
|
|
1175
|
+
key.includes("#0.0.0-sha.") ? "" : e.source?.commit ?? "",
|
|
1176
|
+
// The `../` compose folders are part of the lock: `update` moving them is a re-lock.
|
|
1177
|
+
(e.composed ?? []).map((f) => `${f.path}:${f.sha256}`).join("\n")
|
|
1121
1178
|
].join("\0");
|
|
1122
1179
|
const out = [];
|
|
1123
1180
|
for (const [key, entry] of Object.entries(next.assets)) {
|
|
@@ -1152,9 +1209,13 @@ function libraryMoves(prev, next) {
|
|
|
1152
1209
|
}
|
|
1153
1210
|
return out;
|
|
1154
1211
|
}
|
|
1212
|
+
function unregisteredLibraryName(origin) {
|
|
1213
|
+
return lastLibraryName(origin.name) ?? origin.name;
|
|
1214
|
+
}
|
|
1155
1215
|
function unregisteredLibraryFix(origin, ref) {
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1216
|
+
const name = unregisteredLibraryName(origin);
|
|
1217
|
+
const register = origin.url ? `\`skaile library init ${name} --git ${origin.url}\` (no registered library has the remote ${origin.url} either)` : `\`skaile library init ${name} --path <dir>\``;
|
|
1218
|
+
return `register library "${name}" again with ${register}, or drop the dependency with \`skaile remove ${ref}\``;
|
|
1158
1219
|
}
|
|
1159
1220
|
function declaredNoHolderReason(ref) {
|
|
1160
1221
|
return `is declared but no library or source holds it \u2014 register the library that holds it with \`skaile library init <name> --path <dir>\`, or drop it with \`skaile remove ${ref.split("#")[0]}\``;
|
|
@@ -1180,7 +1241,7 @@ function libraryCollisionSummary(all) {
|
|
|
1180
1241
|
return `${all.length} assets exist in several libraries (${libraries.join(", ")}); using ${usedText.join(", ")}. Highest version wins; an equal version goes to the default library, then by name. ` + (differing.length > 0 ? `Same version, different bytes for: ${differing.join(", ")}. ` : "") + "Pin a version (<ref>#<version>) to choose another; `--verbose` lists each asset.";
|
|
1181
1242
|
}
|
|
1182
1243
|
function lockedLibraryProblem(lookup, origin, ref) {
|
|
1183
|
-
return lookup.state === "dir-missing" ? missingLibraryDirMessage({ name: origin.name, path: lookup.path }) : `library "${origin
|
|
1244
|
+
return lookup.state === "dir-missing" ? missingLibraryDirMessage({ name: origin.name, path: lookup.path }) : `library "${unregisteredLibraryName(origin)}" is not registered \u2014 renamed or removed? ${unregisteredLibraryFix(origin, ref)}`;
|
|
1184
1245
|
}
|
|
1185
1246
|
function gapIdentity(ref) {
|
|
1186
1247
|
const p = parseRefTolerant(ref);
|
|
@@ -1429,6 +1490,43 @@ var AssetManager = class {
|
|
|
1429
1490
|
return !recorded || recorded.includes(target);
|
|
1430
1491
|
});
|
|
1431
1492
|
}
|
|
1493
|
+
/**
|
|
1494
|
+
* A 5.12.0 global record has no `global_asset_harnesses:` key, yet `add --global --harness`
|
|
1495
|
+
* wrote one copy only. On the first global `install`, an entry with exactly one existing copy
|
|
1496
|
+
* among several default harnesses is that harness's subset (OQ6): record it, so the install
|
|
1497
|
+
* does not fan the asset out to harnesses the user never asked for (p1442). A copy under the
|
|
1498
|
+
* 5.12.0 omp folder (`~/.omp/skills`) counts as an omp copy. Nothing is inferred once the key
|
|
1499
|
+
* exists (an entry without a row then means "every harness"), with one default harness, or
|
|
1500
|
+
* with no copy yet. Global mode only; returns the rows written.
|
|
1501
|
+
*/
|
|
1502
|
+
inferLegacyHarnessSubsets(entries) {
|
|
1503
|
+
if (!this.global) return [];
|
|
1504
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
1505
|
+
if (!existsSync(configPath)) return [];
|
|
1506
|
+
let raw;
|
|
1507
|
+
try {
|
|
1508
|
+
raw = parse(readFileSync(configPath, "utf8"));
|
|
1509
|
+
} catch {
|
|
1510
|
+
return [];
|
|
1511
|
+
}
|
|
1512
|
+
if (!raw || typeof raw !== "object" || ENTRY_HARNESSES_KEY in raw) return [];
|
|
1513
|
+
const all = this.defaultGlobalHarnesses();
|
|
1514
|
+
if (all.length < 2) return [];
|
|
1515
|
+
const rows = [];
|
|
1516
|
+
for (const e of entries) {
|
|
1517
|
+
const component = this.installTarget(e.kind);
|
|
1518
|
+
if (!component || !e.name) continue;
|
|
1519
|
+
const holding = all.filter(
|
|
1520
|
+
(h) => isDeployed(component, e.name, h, true) || h === "omp" && legacyOmpPaths(component, e.name).length > 0
|
|
1521
|
+
);
|
|
1522
|
+
if (holding.length !== 1) continue;
|
|
1523
|
+
const publisher = e.publisher?.replace(/^@/, "");
|
|
1524
|
+
const ref = `${e.kind}:${publisher ? `@${publisher}/` : ""}${e.name}`;
|
|
1525
|
+
if (this.setEntryHarnessRow(raw, ref, holding)) rows.push({ ref, harnesses: holding });
|
|
1526
|
+
}
|
|
1527
|
+
if (rows.length > 0) writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
1528
|
+
return rows;
|
|
1529
|
+
}
|
|
1432
1530
|
/**
|
|
1433
1531
|
* Set (or, with `harnesses` undefined, clear) the harness row of `ref` in the parsed
|
|
1434
1532
|
* `~/.skaile/skaile.yaml` `raw`. Returns whether `raw` changed.
|
|
@@ -1556,16 +1654,36 @@ var AssetManager = class {
|
|
|
1556
1654
|
}
|
|
1557
1655
|
/** Deploy options for one harness. In global mode, `harness` is the
|
|
1558
1656
|
* fan-out target (not the constructor's `harness`, which is project-only). */
|
|
1559
|
-
deployOptsFor(target, link, onSecretFile) {
|
|
1657
|
+
deployOptsFor(target, link, onSecretFile, composed) {
|
|
1658
|
+
const records = this.global ? void 0 : this.composedRecords();
|
|
1560
1659
|
return {
|
|
1561
1660
|
registry: this.registry,
|
|
1562
1661
|
harness: target,
|
|
1563
1662
|
global: this.global,
|
|
1564
1663
|
cwd: this.projectDir,
|
|
1565
1664
|
link: link === true,
|
|
1566
|
-
...onSecretFile ? { onSecretFile: (entry, p) => onSecretFile(canonicalEntryRef(entry), p) } : {}
|
|
1665
|
+
...onSecretFile ? { onSecretFile: (entry, p) => onSecretFile(canonicalEntryRef(entry), p) } : {},
|
|
1666
|
+
...records ? { lockedComposed: (agent) => records.get(agent) } : {},
|
|
1667
|
+
...composed ? {
|
|
1668
|
+
refreshComposed: composed.refresh,
|
|
1669
|
+
onComposedDrift: (entry, ref) => composed.onDrift(canonicalEntryRef(entry), ref)
|
|
1670
|
+
} : {}
|
|
1567
1671
|
};
|
|
1568
1672
|
}
|
|
1673
|
+
/** Agent name → the `../` compose folder files its lock entry records (`LockEntry.composed`). */
|
|
1674
|
+
composedRecords() {
|
|
1675
|
+
const out = /* @__PURE__ */ new Map();
|
|
1676
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
1677
|
+
if (!entry.composed?.length) continue;
|
|
1678
|
+
const { name } = parseCanonicalLockKey(key);
|
|
1679
|
+
if (name) out.set(name, entry.composed);
|
|
1680
|
+
}
|
|
1681
|
+
return out;
|
|
1682
|
+
}
|
|
1683
|
+
/** `install`'s `Missing:` line for a `../` compose folder the library moved past the lock. */
|
|
1684
|
+
composedDriftLine(agentRef, composeRef) {
|
|
1685
|
+
return `${agentRef} (composes ${composeRef}: the library's copy changed since the lock; \`skaile update\` moves the lock to it)`;
|
|
1686
|
+
}
|
|
1569
1687
|
/**
|
|
1570
1688
|
* Names of the `mcp-server` assets the lock holds (the declaration ids, unscoped); a missing
|
|
1571
1689
|
* `.mcp.json` entry of one counts as drift even before skaile owned it.
|
|
@@ -1766,6 +1884,15 @@ var AssetManager = class {
|
|
|
1766
1884
|
for (const [slug, decl] of Object.entries(repositories)) {
|
|
1767
1885
|
const localPath = this.resolveRepoDir(decl, slug);
|
|
1768
1886
|
if (!localPath || !existsSync(localPath)) continue;
|
|
1887
|
+
if (decl.url && normalizeRepoUrl(decl.url) !== normalizeRepoUrl(FACTORY_SOURCE_URL) && !existsSync(join(localPath, ".git"))) {
|
|
1888
|
+
this.reportSourceError(
|
|
1889
|
+
{ localPath, sourceUrl: decl.url, commit: "" },
|
|
1890
|
+
new Error(
|
|
1891
|
+
`${decl.url}: its clone ${localPath} is not a git repository, so git pull fails - run \`skaile source sync\` to clone it again`
|
|
1892
|
+
)
|
|
1893
|
+
);
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1769
1896
|
const commit = getRepoCommit(localPath) ?? "0".repeat(40);
|
|
1770
1897
|
clones.push({
|
|
1771
1898
|
localPath,
|
|
@@ -2089,6 +2216,33 @@ var AssetManager = class {
|
|
|
2089
2216
|
if (!scan) return [];
|
|
2090
2217
|
return [...scan.byKey.values()].flat();
|
|
2091
2218
|
}
|
|
2219
|
+
/**
|
|
2220
|
+
* The version `add` deploys for each asset of a source clone registered as a `reader`
|
|
2221
|
+
* library, when its file declares none: the repo commit pseudo-version (p855). `search`
|
|
2222
|
+
* reads this where no project is in scope, so it lists what `add` locks, not the index
|
|
2223
|
+
* default.
|
|
2224
|
+
*
|
|
2225
|
+
* @returns `<library>\0<kind>:@<publisher>/<name>` -> version; only assets that declare none.
|
|
2226
|
+
*/
|
|
2227
|
+
async sourceAssetVersions() {
|
|
2228
|
+
const out = /* @__PURE__ */ new Map();
|
|
2229
|
+
const scan = await this.scanLibraries();
|
|
2230
|
+
if (!scan) return out;
|
|
2231
|
+
const snap = await this.loadLibraryIndex();
|
|
2232
|
+
for (const [key, candidates] of scan.byKey) {
|
|
2233
|
+
for (const c of candidates) {
|
|
2234
|
+
if (c.entry.version) continue;
|
|
2235
|
+
const lib = snap?.libraries.find((l) => l.name === c.library);
|
|
2236
|
+
if (lib?.ownership !== "reader" || !("url" in lib.backendConfig)) continue;
|
|
2237
|
+
const manifestVersion = repoManifestVersion(lib.path);
|
|
2238
|
+
const commit = getRepoCommit(lib.path);
|
|
2239
|
+
if (manifestVersion) out.set(`${c.library}\0${key}`, manifestVersion);
|
|
2240
|
+
else if (commit)
|
|
2241
|
+
out.set(`${c.library}\0${key}`, `${PSEUDO_VERSION_PREFIX}${commit.slice(0, 7)}`);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
return out;
|
|
2245
|
+
}
|
|
2092
2246
|
/**
|
|
2093
2247
|
* Every origin that holds `ref` (`kind:@publisher/name`) with the version it holds, in
|
|
2094
2248
|
* the order `add <ref>` prefers them, so the first row is the one `add` deploys: the
|
|
@@ -2598,7 +2752,14 @@ var AssetManager = class {
|
|
|
2598
2752
|
if (!existsSync(indexPath)) return null;
|
|
2599
2753
|
const index = new LocalIndex({ indexPath });
|
|
2600
2754
|
try {
|
|
2601
|
-
|
|
2755
|
+
const libraries = await index.listSources();
|
|
2756
|
+
const defs = await index.listAssetDefs();
|
|
2757
|
+
const hidden = this.undeclaredSourceClones(libraries);
|
|
2758
|
+
if (hidden.size === 0) return { libraries, defs };
|
|
2759
|
+
return {
|
|
2760
|
+
libraries: libraries.filter((l) => !hidden.has(l.id)),
|
|
2761
|
+
defs: defs.filter((d) => !d.libraryId || !hidden.has(d.libraryId))
|
|
2762
|
+
};
|
|
2602
2763
|
} finally {
|
|
2603
2764
|
index.close();
|
|
2604
2765
|
}
|
|
@@ -2606,6 +2767,32 @@ var AssetManager = class {
|
|
|
2606
2767
|
return null;
|
|
2607
2768
|
}
|
|
2608
2769
|
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Ids of the registered libraries that are the clone of a source this project does not
|
|
2772
|
+
* declare. `source add` registers the clone as a machine-wide reader library so `search`
|
|
2773
|
+
* and the indexer can read it, but a `sources:` clone belongs to the projects that declare
|
|
2774
|
+
* it (OQ12, p321): another project neither resolves it, nor ties with it by name, nor has
|
|
2775
|
+
* `update` re-pin its locked assets to it. A reader library the user registered by hand
|
|
2776
|
+
* (`library init --reader --git`) lives outside the sources cache and is never hidden.
|
|
2777
|
+
*/
|
|
2778
|
+
undeclaredSourceClones(libraries) {
|
|
2779
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
2780
|
+
if (!this.global && !findWorkspaceRoot(this.projectDir)) return hidden;
|
|
2781
|
+
let declared;
|
|
2782
|
+
try {
|
|
2783
|
+
declared = new Set(this.loadConfig().sources.map((s) => sourceCacheKey(s.url)));
|
|
2784
|
+
} catch {
|
|
2785
|
+
return hidden;
|
|
2786
|
+
}
|
|
2787
|
+
const cloneRoots = [join(globalCacheRoot(), "sources"), this.reposDir].map((d) => resolve(d));
|
|
2788
|
+
for (const l of libraries) {
|
|
2789
|
+
if (l.ownership !== "reader" || !("url" in l.backendConfig)) continue;
|
|
2790
|
+
const at = resolve(l.path);
|
|
2791
|
+
if (!cloneRoots.some((root) => at.startsWith(root + sep))) continue;
|
|
2792
|
+
if (!declared.has(sourceCacheKey(l.backendConfig.url))) hidden.add(l.id);
|
|
2793
|
+
}
|
|
2794
|
+
return hidden;
|
|
2795
|
+
}
|
|
2609
2796
|
/**
|
|
2610
2797
|
* Library-origin lock entries for the assets {@link resolveLibraryEntries}
|
|
2611
2798
|
* matched. The locked version is the one actually deployed: the on-disk
|
|
@@ -2749,14 +2936,19 @@ var AssetManager = class {
|
|
|
2749
2936
|
* `install --locked`. An entry is restored only when its library is still
|
|
2750
2937
|
* registered/on disk, still holds the asset, and still offers the locked
|
|
2751
2938
|
* version; anything else is reported in `missing` with the reason.
|
|
2939
|
+
*
|
|
2940
|
+
* @param opts.storeFallback - `install --locked` only: an entry whose library is not registered
|
|
2941
|
+
* here is restored from a store that serves the locked sha256 (OQ15, p1490). Reads such as
|
|
2942
|
+
* the drift check never ask a store.
|
|
2752
2943
|
*/
|
|
2753
|
-
async restoreLibraryLockEntries(lock) {
|
|
2944
|
+
async restoreLibraryLockEntries(lock, opts) {
|
|
2754
2945
|
const entries = [];
|
|
2755
2946
|
const missing = [];
|
|
2947
|
+
const viaStore = [];
|
|
2756
2948
|
const locked = Object.entries(lock.assets).filter(
|
|
2757
2949
|
([, e]) => e.library !== void 0 && !e.library.installed
|
|
2758
2950
|
);
|
|
2759
|
-
if (locked.length === 0) return { entries, missing };
|
|
2951
|
+
if (locked.length === 0) return { entries, missing, viaStore };
|
|
2760
2952
|
const snap = await this.loadLibraryIndex();
|
|
2761
2953
|
const diskHashes = /* @__PURE__ */ new Map();
|
|
2762
2954
|
const fileSets = /* @__PURE__ */ new Map();
|
|
@@ -2769,6 +2961,12 @@ var AssetManager = class {
|
|
|
2769
2961
|
}
|
|
2770
2962
|
const lookup = this.lockedLibrary(origin, snap, { kind, publisher, name });
|
|
2771
2963
|
if (lookup.state !== "ok") {
|
|
2964
|
+
const fallback = lookup.state === "unregistered" && opts?.storeFallback ? await this.storeEntryForLockedLibraryAsset(publisher, kind, name, origin) : null;
|
|
2965
|
+
if (fallback) {
|
|
2966
|
+
entries.push(fallback);
|
|
2967
|
+
viaStore.push(ref);
|
|
2968
|
+
continue;
|
|
2969
|
+
}
|
|
2772
2970
|
missing.push(
|
|
2773
2971
|
`${ref} (${lockedLibraryProblem(lookup, origin, `${kind}:@${publisher}/${name}`)})`
|
|
2774
2972
|
);
|
|
@@ -2811,22 +3009,71 @@ var AssetManager = class {
|
|
|
2811
3009
|
found.version = sameVersion ? origin.version : current;
|
|
2812
3010
|
entries.push(withLibraryFiles(found, libPath, fileSets, this.registry));
|
|
2813
3011
|
}
|
|
2814
|
-
return { entries, missing };
|
|
3012
|
+
return { entries, missing, viaStore };
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* The store's copy of a library-origin lock entry whose library is not registered here: a
|
|
3016
|
+
* store (project `stores:` or the global catalog) that serves `kind:@publisher/name#version`
|
|
3017
|
+
* under the very sha256 the lock recorded holds the same bytes. The entry is fetched and
|
|
3018
|
+
* verified like any store asset; the lock stays as it is. `null` when the lock has no hash,
|
|
3019
|
+
* no store answers, or the hash differs (OQ15, p1490).
|
|
3020
|
+
*/
|
|
3021
|
+
async storeEntryForLockedLibraryAsset(publisher, kind, name, origin) {
|
|
3022
|
+
if (!origin.sha256 || !origin.version || isPseudoVersion(origin.version)) return null;
|
|
3023
|
+
const { repositories, stores: projectStores } = this.loadConfig();
|
|
3024
|
+
const stores = [...projectStores, ...await this.globalStoreFallback(projectStores)];
|
|
3025
|
+
const fetcher = this.storeFetcherFor(stores);
|
|
3026
|
+
if (!fetcher) return null;
|
|
3027
|
+
for (const store of stores) {
|
|
3028
|
+
try {
|
|
3029
|
+
const m = await fetcher.getInstallManifest(
|
|
3030
|
+
store.url,
|
|
3031
|
+
`${kind}:@${publisher}/${name}#${origin.version}`
|
|
3032
|
+
);
|
|
3033
|
+
if (!m || m.sha256 !== origin.sha256) continue;
|
|
3034
|
+
const [entry] = await this.buildCatalogEntries(
|
|
3035
|
+
[
|
|
3036
|
+
{
|
|
3037
|
+
publisher,
|
|
3038
|
+
kind,
|
|
3039
|
+
name,
|
|
3040
|
+
version: origin.version,
|
|
3041
|
+
sourceUrl: m.sourceUrl,
|
|
3042
|
+
commit: m.commit,
|
|
3043
|
+
files: m.files,
|
|
3044
|
+
sha256: m.sha256,
|
|
3045
|
+
...m.deps?.length ? { deps: m.deps, storeRequires: m.deps } : {}
|
|
3046
|
+
}
|
|
3047
|
+
],
|
|
3048
|
+
repositories
|
|
3049
|
+
);
|
|
3050
|
+
if (entry) return entry;
|
|
3051
|
+
} catch (err) {
|
|
3052
|
+
this.noteStoreFailure(store.url, err);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
return null;
|
|
2815
3056
|
}
|
|
2816
3057
|
/**
|
|
2817
3058
|
* Whether a deployed copy exists for the lock entries `install` kept for the unresolved
|
|
2818
|
-
* `ref`. A kind with no install target has
|
|
3059
|
+
* `ref`. A kind with no install target has none, except an `mcp-server`: its copy is the
|
|
3060
|
+
* `.mcp.json` entry skaile wrote and the payload that entry points into (p792).
|
|
2819
3061
|
*/
|
|
2820
|
-
keptCopyDeployed(kept, ref) {
|
|
3062
|
+
async keptCopyDeployed(kept, ref) {
|
|
2821
3063
|
const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
|
|
2822
3064
|
if (!want) return false;
|
|
2823
|
-
|
|
3065
|
+
for (const key of Object.keys(kept.assets)) {
|
|
2824
3066
|
const id = parseCanonicalLockKey(key);
|
|
2825
|
-
if (id.kind !== want.kind || id.name !== want.name)
|
|
2826
|
-
if (want.publisher && id.publisher !== want.publisher)
|
|
3067
|
+
if (id.kind !== want.kind || id.name !== want.name) continue;
|
|
3068
|
+
if (want.publisher && id.publisher !== want.publisher) continue;
|
|
2827
3069
|
const component = this.installTarget(id.kind);
|
|
2828
|
-
|
|
2829
|
-
|
|
3070
|
+
if (component !== void 0) {
|
|
3071
|
+
if (isDeployed(component, id.name, this.harness, this.global, this.projectDir)) return true;
|
|
3072
|
+
} else if (id.kind === "mcp-server" && !this.global && await mcpCommittedRenderIntact(this.projectDir, this.harness, id.name)) {
|
|
3073
|
+
return true;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return false;
|
|
2830
3077
|
}
|
|
2831
3078
|
/** Whether the deployed copy of a library asset is on disk; a kind with no install target has none to check. */
|
|
2832
3079
|
lockedCopyDeployed(kind, name) {
|
|
@@ -2949,6 +3196,24 @@ var AssetManager = class {
|
|
|
2949
3196
|
lockedRefsNamed(kind, name) {
|
|
2950
3197
|
return Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey).filter((id) => id.kind === kind && id.name === name).map((id) => id.publisher ? `${kind}:@${id.publisher}/${name}` : `${kind}:${name}`);
|
|
2951
3198
|
}
|
|
3199
|
+
/**
|
|
3200
|
+
* The library a library-origin lock entry of `kind` and `name` (and `publisher`, when given)
|
|
3201
|
+
* resolves to on this machine, or `undefined` when the lock holds no such entry, it is not
|
|
3202
|
+
* from a library, or its library is not here. `info` prints it next to `source:`.
|
|
3203
|
+
*/
|
|
3204
|
+
async lockedLibraryOf(kind, name, publisher) {
|
|
3205
|
+
const bare = publisher?.replace(/^@/, "");
|
|
3206
|
+
const names = await this.lockedLibraryNames();
|
|
3207
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3208
|
+
if (!entry.library || entry.library.installed) continue;
|
|
3209
|
+
const id = parseCanonicalLockKey(key);
|
|
3210
|
+
if (id.kind !== kind || id.name !== name || !id.publisher) continue;
|
|
3211
|
+
if (bare && id.publisher !== bare) continue;
|
|
3212
|
+
const lib = names.get(`${kind}:${id.publisher}/${name}`);
|
|
3213
|
+
if (lib) return lib;
|
|
3214
|
+
}
|
|
3215
|
+
return void 0;
|
|
3216
|
+
}
|
|
2952
3217
|
/**
|
|
2953
3218
|
* Where each library-origin lock entry of this project resolves on this machine: identity
|
|
2954
3219
|
* `kind:publisher/name` (no scope sigil, no version) → the name the locked library is
|
|
@@ -2991,6 +3256,88 @@ var AssetManager = class {
|
|
|
2991
3256
|
}
|
|
2992
3257
|
return { libraries: await this.lockedLibraryNames(), shas };
|
|
2993
3258
|
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Canonical refs of the locked source assets that share a source URL with `resolved` but
|
|
3261
|
+
* are locked at another commit than the one `resolved` carries for it. Assets in `resolved`
|
|
3262
|
+
* and library-origin entries are skipped.
|
|
3263
|
+
*/
|
|
3264
|
+
staleSourceSiblingRefs(resolved, declared = []) {
|
|
3265
|
+
const pinOf = /* @__PURE__ */ new Map();
|
|
3266
|
+
for (const d of declared) {
|
|
3267
|
+
const id = refIdentity(d);
|
|
3268
|
+
const pin = parseAssetRefLoose(d)?.pin;
|
|
3269
|
+
if (id !== null && pin) pinOf.set(id.replace(/^@/, ""), pin);
|
|
3270
|
+
}
|
|
3271
|
+
const commitOf = /* @__PURE__ */ new Map();
|
|
3272
|
+
const held = /* @__PURE__ */ new Set();
|
|
3273
|
+
for (const c of resolved) {
|
|
3274
|
+
if (!c.sourceUrl || !c.commit) continue;
|
|
3275
|
+
commitOf.set(normalizeRepoUrl(c.sourceUrl), c.commit);
|
|
3276
|
+
held.add(`${c.publisher.replace(/^@/, "")}/${c.kind}:${c.name}`);
|
|
3277
|
+
}
|
|
3278
|
+
const out = [];
|
|
3279
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3280
|
+
const src = entry.source;
|
|
3281
|
+
if (!src?.url || entry.library) continue;
|
|
3282
|
+
const commit = commitOf.get(normalizeRepoUrl(src.url));
|
|
3283
|
+
if (!commit || src.commit === commit) continue;
|
|
3284
|
+
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
3285
|
+
if (!publisher || !kind || !name || held.has(`${publisher}/${kind}:${name}`)) continue;
|
|
3286
|
+
const pin = pinOf.get(`${publisher}/${kind}:${name}`);
|
|
3287
|
+
out.push(`${kind}:@${publisher}/${name}${pin ? `#${pin}` : ""}`);
|
|
3288
|
+
}
|
|
3289
|
+
return out;
|
|
3290
|
+
}
|
|
3291
|
+
/**
|
|
3292
|
+
* The lock entries that came from a source or the store (a `source:`, no `library:`), by
|
|
3293
|
+
* identity `<publisher>/<kind>:<name>` -> locked version. The lock's origin
|
|
3294
|
+
* wins: a library registered later that holds the same ref does not take such an asset
|
|
3295
|
+
* (`update`, `install`, `add`; OQ15, p1490).
|
|
3296
|
+
*/
|
|
3297
|
+
lockedOriginVersions() {
|
|
3298
|
+
const out = /* @__PURE__ */ new Map();
|
|
3299
|
+
for (const [key, e] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3300
|
+
if (!e.source || e.library) continue;
|
|
3301
|
+
const { publisher, kind, name, version } = parseCanonicalLockKey(key);
|
|
3302
|
+
if (publisher && kind && name) out.set(`${publisher}/${kind}:${name}`, version ?? "");
|
|
3303
|
+
}
|
|
3304
|
+
return out;
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Say which registered library holds a ref the lock keeps on its source or the store: the
|
|
3308
|
+
* candidate that lost, with the way to switch (p1490). Printed once per `add`.
|
|
3309
|
+
*/
|
|
3310
|
+
async noteLosingLibraries(refs) {
|
|
3311
|
+
const lock = this.tryReadLock();
|
|
3312
|
+
for (const ref of refs) {
|
|
3313
|
+
const plain = ref.split("#")[0] ?? ref;
|
|
3314
|
+
let hit;
|
|
3315
|
+
try {
|
|
3316
|
+
hit = await this.resolveLibraryEntries([plain], { quiet: true });
|
|
3317
|
+
} catch {
|
|
3318
|
+
continue;
|
|
3319
|
+
}
|
|
3320
|
+
const entry = hit.entries[0];
|
|
3321
|
+
const library = entry ? hit.libraryOf.get(entry) : void 0;
|
|
3322
|
+
if (!entry || !library) continue;
|
|
3323
|
+
const canonical = canonicalEntryRef(entry);
|
|
3324
|
+
const plainCanonical = canonical.split("#")[0] ?? canonical;
|
|
3325
|
+
const lockedUrl = Object.entries(lock?.assets ?? {}).find(([k, e]) => {
|
|
3326
|
+
const id = parseCanonicalLockKey(k);
|
|
3327
|
+
return e.source && !e.library && `${id.kind}:@${id.publisher}/${id.name}` === plainCanonical;
|
|
3328
|
+
})?.[1].source?.url;
|
|
3329
|
+
if (lockedUrl) {
|
|
3330
|
+
const ownClone = (await this.loadLibraryIndex())?.libraries.some(
|
|
3331
|
+
(l) => l.name === library && l.ownership === "reader" && "url" in l.backendConfig && sourceCacheKey(l.backendConfig.url) === sourceCacheKey(lockedUrl)
|
|
3332
|
+
);
|
|
3333
|
+
if (ownClone) continue;
|
|
3334
|
+
}
|
|
3335
|
+
this.emitLibraryCollision(
|
|
3336
|
+
`${plainCanonical} is locked from ${lockedUrl ?? "its source"}, which wins; library "${library}" also holds it${entry.version ? ` (${entry.version})` : ""} and is not used. To switch, run \`skaile remove ${plainCanonical}\`, then \`skaile add ${plainCanonical}\`.`,
|
|
3337
|
+
[plainCanonical]
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
2994
3341
|
// ── Store fetch-then-deploy ────────────────────────────────────────────
|
|
2995
3342
|
/** A candidate's stable identity key (`<publisher>/<kind>:<name>@<version>`). */
|
|
2996
3343
|
candidateKey(c) {
|
|
@@ -3150,10 +3497,19 @@ var AssetManager = class {
|
|
|
3150
3497
|
fresh.push(ref);
|
|
3151
3498
|
}
|
|
3152
3499
|
if (fresh.length === 0) break;
|
|
3500
|
+
const keepsOrigin = (ref) => {
|
|
3501
|
+
const id = refIdentity(ref);
|
|
3502
|
+
const locked = id !== null ? opts.lockedOrigin?.get(id) : void 0;
|
|
3503
|
+
if (locked === void 0) return false;
|
|
3504
|
+
const pin = ref.includes("#") ? ref.slice(ref.indexOf("#") + 1).trim() : void 0;
|
|
3505
|
+
return !pin || locked === "" || matchPin(pin, locked);
|
|
3506
|
+
};
|
|
3507
|
+
const originKept = fresh.filter(keepsOrigin);
|
|
3508
|
+
if (opts.warnCollisions && originKept.length > 0) await this.noteLosingLibraries(originKept);
|
|
3153
3509
|
const libraryTried = new Set(
|
|
3154
3510
|
fresh.filter((ref) => {
|
|
3155
3511
|
const id = refIdentity(ref);
|
|
3156
|
-
return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0;
|
|
3512
|
+
return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0 && !keepsOrigin(ref);
|
|
3157
3513
|
})
|
|
3158
3514
|
);
|
|
3159
3515
|
const hit = await this.resolveLibraryEntries([...libraryTried], {
|
|
@@ -3181,7 +3537,7 @@ var AssetManager = class {
|
|
|
3181
3537
|
}
|
|
3182
3538
|
presets.push(...r.presets);
|
|
3183
3539
|
const hit2 = await this.resolveLibraryEntries(
|
|
3184
|
-
r.missing.filter((m) => !libraryTried.has(m)),
|
|
3540
|
+
r.missing.filter((m) => !libraryTried.has(m) && !keepsOrigin(m)),
|
|
3185
3541
|
{
|
|
3186
3542
|
quiet: !opts.warnCollisions,
|
|
3187
3543
|
lockedLibraries: opts.lockedLibraries,
|
|
@@ -3484,7 +3840,8 @@ var AssetManager = class {
|
|
|
3484
3840
|
};
|
|
3485
3841
|
}
|
|
3486
3842
|
const orphans = await this.orphanedSourceGaps(
|
|
3487
|
-
await this.declaredLockKeys(lockForLibraries, dependencies)
|
|
3843
|
+
await this.declaredLockKeys(lockForLibraries, dependencies),
|
|
3844
|
+
true
|
|
3488
3845
|
);
|
|
3489
3846
|
if (orphans.size > 0) {
|
|
3490
3847
|
return {
|
|
@@ -3527,6 +3884,7 @@ var AssetManager = class {
|
|
|
3527
3884
|
// library holding the same ref does not move a locked asset. `update` re-resolves.
|
|
3528
3885
|
...opts?.moveLibraries ? { preferLocked: await this.lockedPreference() } : { lockedLibraries: await this.lockedLibraryNames() },
|
|
3529
3886
|
...keepLocked ? { keepLocked } : {},
|
|
3887
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
3530
3888
|
readStoreRequires: true
|
|
3531
3889
|
});
|
|
3532
3890
|
this.assertNoFolderCollision([...resolved, ...lib.entries.map((e) => this.entryIdentity(e))]);
|
|
@@ -3570,7 +3928,7 @@ var AssetManager = class {
|
|
|
3570
3928
|
const missingHints = await this.publisherChangeHints(stillMissing);
|
|
3571
3929
|
const prevLock = this.tryReadLock();
|
|
3572
3930
|
const kept = keptLockEntries(prevLock, stillMissing);
|
|
3573
|
-
const storeNotChecked = this.storeNotCheckedRefs(
|
|
3931
|
+
const storeNotChecked = await this.storeNotCheckedRefs(
|
|
3574
3932
|
kept,
|
|
3575
3933
|
sources.map((s) => s.url)
|
|
3576
3934
|
);
|
|
@@ -3578,7 +3936,7 @@ var AssetManager = class {
|
|
|
3578
3936
|
for (const ref of /* @__PURE__ */ new Set([...kept.refs, ...fixes.keys()])) {
|
|
3579
3937
|
const fix = fixes.get(ref);
|
|
3580
3938
|
const notes = [
|
|
3581
|
-
kept.refs.has(ref) ? this.keptCopyDeployed(kept, ref) ? "the deployed copy and its lock entry are kept" : "its lock entry is kept; no deployed copy exists" : void 0,
|
|
3939
|
+
kept.refs.has(ref) ? await this.keptCopyDeployed(kept, ref) ? "the deployed copy and its lock entry are kept" : "its lock entry is kept; no deployed copy exists" : void 0,
|
|
3582
3940
|
fix
|
|
3583
3941
|
].filter((n) => n !== void 0);
|
|
3584
3942
|
missingHints[ref] = `${fix ? "" : missingHints[ref] ?? ""} (${notes.join("; ")})`;
|
|
@@ -3597,12 +3955,24 @@ var AssetManager = class {
|
|
|
3597
3955
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
3598
3956
|
assertFlowsLoad(entries, "installed");
|
|
3599
3957
|
assertNoLibrarySymlinks(entries, this.registry, repositories, opts?.link === true);
|
|
3958
|
+
const forceCommand = opts?.moveLibraries === true ? "skaile update --force" : "skaile install --force";
|
|
3600
3959
|
if (!opts?.force) {
|
|
3601
3960
|
await this.assertNoHandEditsPruned(
|
|
3602
3961
|
resolved,
|
|
3603
|
-
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
|
|
3962
|
+
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)]),
|
|
3963
|
+
forceCommand
|
|
3604
3964
|
);
|
|
3605
3965
|
}
|
|
3966
|
+
const relockRemoved = await this.relockDeletions(
|
|
3967
|
+
entries,
|
|
3968
|
+
prevLock,
|
|
3969
|
+
withKeptLockEntries(freshLock, kept),
|
|
3970
|
+
opts?.force === true,
|
|
3971
|
+
forceCommand,
|
|
3972
|
+
lib.libraryOf
|
|
3973
|
+
);
|
|
3974
|
+
if (!opts?.force) this.assertNoUnrecordedAgentOverwrite(entries, prevLock, forceCommand);
|
|
3975
|
+
const inferredHarnesses = this.inferLegacyHarnessSubsets(entries);
|
|
3606
3976
|
const { removed, replaced } = this.reconcileRemovedAssets(
|
|
3607
3977
|
resolved,
|
|
3608
3978
|
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
|
|
@@ -3610,6 +3980,7 @@ var AssetManager = class {
|
|
|
3610
3980
|
const copyDrift = await this.lockedCopyDrift();
|
|
3611
3981
|
const missingCopies = await this.incompleteDeploys();
|
|
3612
3982
|
const unrecordedAgents = this.unrecordedAgentFiles(prevLock);
|
|
3983
|
+
const composedDrift = /* @__PURE__ */ new Set();
|
|
3613
3984
|
const deployed = [
|
|
3614
3985
|
...new Set(
|
|
3615
3986
|
this.targetHarnesses().flatMap(
|
|
@@ -3617,7 +3988,10 @@ var AssetManager = class {
|
|
|
3617
3988
|
this.entriesForHarness(entries, t),
|
|
3618
3989
|
repositories,
|
|
3619
3990
|
patches,
|
|
3620
|
-
this.deployOptsFor(t, opts?.link
|
|
3991
|
+
this.deployOptsFor(t, opts?.link, void 0, {
|
|
3992
|
+
refresh: opts?.moveLibraries === true,
|
|
3993
|
+
onDrift: (ref, composeRef) => composedDrift.add(this.composedDriftLine(ref, composeRef))
|
|
3994
|
+
})
|
|
3621
3995
|
)
|
|
3622
3996
|
)
|
|
3623
3997
|
)
|
|
@@ -3625,14 +3999,21 @@ var AssetManager = class {
|
|
|
3625
3999
|
this.sweepLegacyOmpCopies(entries);
|
|
3626
4000
|
const mcpRestored = await this.renderHarnessMcpConfig();
|
|
3627
4001
|
const agentRender = await this.renderAgentInstances();
|
|
3628
|
-
const restored = [
|
|
4002
|
+
const restored = [
|
|
4003
|
+
...this.restoredCopies(copyDrift, deployed, missingCopies),
|
|
4004
|
+
...relockRemoved,
|
|
4005
|
+
...mcpRestored
|
|
4006
|
+
];
|
|
3629
4007
|
const rewritten = this.rewrittenAgentFiles(unrecordedAgents);
|
|
3630
4008
|
const lockWanted = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
|
|
3631
4009
|
let lockWritten = false;
|
|
3632
4010
|
let relocked = [];
|
|
3633
4011
|
let moved = [];
|
|
3634
4012
|
if (lockWanted) {
|
|
3635
|
-
const lockData = this.withRenderedAgents(
|
|
4013
|
+
const lockData = this.withRenderedAgents(
|
|
4014
|
+
withKeptLockEntries(freshLock, kept),
|
|
4015
|
+
opts?.moveLibraries ? void 0 : prevLock
|
|
4016
|
+
);
|
|
3636
4017
|
moved = libraryMoves(prevLock, libraryLock);
|
|
3637
4018
|
const movedRefs = new Set(moved.map((m) => m.ref));
|
|
3638
4019
|
relocked = relockedRefs(prevLock, lockData).filter((k) => !movedRefs.has(k.split("#")[0]));
|
|
@@ -3648,12 +4029,17 @@ var AssetManager = class {
|
|
|
3648
4029
|
return {
|
|
3649
4030
|
deployed,
|
|
3650
4031
|
removed,
|
|
3651
|
-
missing: [
|
|
4032
|
+
missing: [
|
|
4033
|
+
...reportedMissing,
|
|
4034
|
+
...versionBlocked.filter((b) => !reportedMissing.includes(b)),
|
|
4035
|
+
...composedDrift
|
|
4036
|
+
],
|
|
3652
4037
|
...placeholderBodies.length > 0 ? { placeholderBodies } : {},
|
|
3653
4038
|
...placeholderDescriptions.length > 0 ? { placeholderDescriptions } : {},
|
|
3654
4039
|
...storeNotChecked.length > 0 ? { storeNotChecked } : {},
|
|
3655
4040
|
...Object.keys(missingHints).length > 0 ? { missingHints } : {},
|
|
3656
4041
|
...relocked.length > 0 ? { relocked } : {},
|
|
4042
|
+
...inferredHarnesses.length > 0 ? { inferredHarnesses } : {},
|
|
3657
4043
|
...moved.length > 0 ? { moved } : {},
|
|
3658
4044
|
...replaced.length > 0 ? { replaced } : {},
|
|
3659
4045
|
...restored.length > 0 ? { restored } : {},
|
|
@@ -3674,7 +4060,7 @@ var AssetManager = class {
|
|
|
3674
4060
|
* @param declaredSourceUrls - The `sources:` URLs of `skaile.yaml`; a lock entry from one of
|
|
3675
4061
|
* them is that source's, not the store's.
|
|
3676
4062
|
*/
|
|
3677
|
-
storeNotCheckedRefs(kept, declaredSourceUrls) {
|
|
4063
|
+
async storeNotCheckedRefs(kept, declaredSourceUrls) {
|
|
3678
4064
|
const skipped = this.catalogUrlOverride === "";
|
|
3679
4065
|
if (!skipped && this.storeFailures.size === 0) return [];
|
|
3680
4066
|
const declared = new Set(declaredSourceUrls.map(normalizeRepoUrl));
|
|
@@ -3685,7 +4071,7 @@ var AssetManager = class {
|
|
|
3685
4071
|
const entry = kept.assets[key];
|
|
3686
4072
|
return entry !== void 0 && !entry.library && entry.source !== void 0 && !declared.has(normalizeRepoUrl(entry.source.url));
|
|
3687
4073
|
});
|
|
3688
|
-
if (!fromStore || !this.keptCopyDeployed(kept, ref)) continue;
|
|
4074
|
+
if (!fromStore || !await this.keptCopyDeployed(kept, ref)) continue;
|
|
3689
4075
|
if (skipped && this.storeFailures.size === 0) {
|
|
3690
4076
|
out.push({ ref, reason: "skipped", detail: "--no-catalog: the store was not asked" });
|
|
3691
4077
|
continue;
|
|
@@ -3831,7 +4217,7 @@ var AssetManager = class {
|
|
|
3831
4217
|
* other version taking its place) whose deployed folder holds hand edits: the files that differ
|
|
3832
4218
|
* from the lock are named, nothing is removed, and `--force` prunes anyway (OQ6).
|
|
3833
4219
|
*/
|
|
3834
|
-
async assertNoHandEditsPruned(resolved, keep) {
|
|
4220
|
+
async assertNoHandEditsPruned(resolved, keep, forceCommand) {
|
|
3835
4221
|
const oldLock = this.tryReadLock();
|
|
3836
4222
|
if (!oldLock) return;
|
|
3837
4223
|
const resolvedRefs = [
|
|
@@ -3863,7 +4249,158 @@ var AssetManager = class {
|
|
|
3863
4249
|
throw new Error(
|
|
3864
4250
|
`Not pruned: skaile.yaml no longer declares ${lines.length === 1 ? "an asset" : "assets"} whose deployed copy holds your edits:
|
|
3865
4251
|
${lines.join("\n ")}
|
|
3866
|
-
Nothing was changed. Declare it again to keep it, or run \`
|
|
4252
|
+
Nothing was changed. Declare it again to keep it, or run \`${forceCommand}\` to prune it with its edits.`
|
|
4253
|
+
);
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Report (and, without `force`, guard) the files a re-lock deletes from a deployed copy
|
|
4257
|
+
* (OQ5, p1441): the asset is locked already and its library or source now holds other content
|
|
4258
|
+
* (`next` re-locks it), and the deployed folder holds files the new content does not ship. The
|
|
4259
|
+
* deploy clears them. Two cases:
|
|
4260
|
+
* - the deployed copy still equals what `prevLock` recorded (per-file sha256 for a source
|
|
4261
|
+
* or store asset, the library hash for a library asset): the new version dropped those
|
|
4262
|
+
* files, nobody edited them, so they are deleted and come back as `removed` rows (an
|
|
4263
|
+
* `install`/`update` prints `Removed <file> (not part of <ref>)`), without a refusal;
|
|
4264
|
+
* - the copy diverged (a file the previous content did not ship, an edited file, or no hash
|
|
4265
|
+
* to compare): without `force` this throws one error naming the files and changes nothing.
|
|
4266
|
+
* A source or store asset names only the files the previous content did not ship (or that
|
|
4267
|
+
* were edited); a library asset records no file list, so it names every file to delete.
|
|
4268
|
+
*
|
|
4269
|
+
* @param entries - The catalog entries about to be deployed.
|
|
4270
|
+
* @param next - The lock the run is about to write (or merge).
|
|
4271
|
+
* @param libraryOf - The library each library-origin entry came from.
|
|
4272
|
+
*/
|
|
4273
|
+
async relockDeletions(entries, prevLock, next, force, forceCommand, libraryOf) {
|
|
4274
|
+
if (!prevLock) return [];
|
|
4275
|
+
const relocked = relockedRefs(prevLock, next).map((key) => ({
|
|
4276
|
+
key,
|
|
4277
|
+
id: parseCanonicalLockKey(key)
|
|
4278
|
+
}));
|
|
4279
|
+
if (relocked.length === 0) return [];
|
|
4280
|
+
const shown = (p) => (this.global ? p : relative(this.projectDir, p)).split(sep).join("/");
|
|
4281
|
+
let snap;
|
|
4282
|
+
const rows = [];
|
|
4283
|
+
const refused = [];
|
|
4284
|
+
for (const e of entries) {
|
|
4285
|
+
const hit = relocked.find(({ id }) => id.kind === e.kind && id.name === e.name);
|
|
4286
|
+
const component = hit ? this.installTarget(e.kind) : void 0;
|
|
4287
|
+
if (!hit || !component) continue;
|
|
4288
|
+
const prevEntry = Object.entries(prevLock.assets).find(([k]) => {
|
|
4289
|
+
const o = parseCanonicalLockKey(k);
|
|
4290
|
+
return o.kind === hit.id.kind && o.name === hit.id.name && o.publisher === hit.id.publisher;
|
|
4291
|
+
})?.[1];
|
|
4292
|
+
const removed = [];
|
|
4293
|
+
const blocked = [];
|
|
4294
|
+
for (const t of this.targetHarnesses()) {
|
|
4295
|
+
const drift = deployedAssetDrift(e, this.deployOptsFor(t));
|
|
4296
|
+
if (!drift || drift.extra.length === 0) continue;
|
|
4297
|
+
const dir = deployedDir(component, e.name, t, this.global, this.projectDir);
|
|
4298
|
+
const files = drift.extra;
|
|
4299
|
+
let untouched;
|
|
4300
|
+
if (prevEntry && prevEntry.files.length > 0) {
|
|
4301
|
+
const locked = new Map(
|
|
4302
|
+
lockedAssetFiles(e.kind, prevEntry.files).map((f) => [f.rel, f.sha256])
|
|
4303
|
+
);
|
|
4304
|
+
untouched = files.filter((rel) => {
|
|
4305
|
+
const want = locked.get(rel);
|
|
4306
|
+
if (want === void 0) return false;
|
|
4307
|
+
try {
|
|
4308
|
+
return computeHash(join(dir, rel)) === want;
|
|
4309
|
+
} catch {
|
|
4310
|
+
return false;
|
|
4311
|
+
}
|
|
4312
|
+
});
|
|
4313
|
+
} else if (prevEntry?.library?.sha256 && !prevEntry.library.installed) {
|
|
4314
|
+
const libName = libraryOf.get(e);
|
|
4315
|
+
snap ??= await this.loadLibraryIndex();
|
|
4316
|
+
const libPath = libName ? this.libraryPath({ name: libName }, snap) : null;
|
|
4317
|
+
if (libPath) {
|
|
4318
|
+
const assetRoot = statSync(e.source).isDirectory() ? e.source : dirname(e.source);
|
|
4319
|
+
const hash = computeDeterministicHash(
|
|
4320
|
+
dir,
|
|
4321
|
+
listAssetFiles(dir),
|
|
4322
|
+
posix.normalize(relative(libPath, assetRoot).split(sep).join("/"))
|
|
4323
|
+
);
|
|
4324
|
+
if (hash === prevEntry.library.sha256) untouched = [...files];
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
const own = new Set(untouched ?? []);
|
|
4328
|
+
for (const f of files) (own.has(f) ? removed : blocked).push(shown(join(dir, f)));
|
|
4329
|
+
}
|
|
4330
|
+
if (removed.length > 0) rows.push({ ref: hit.key, restored: [], removed });
|
|
4331
|
+
if (blocked.length > 0) refused.push({ ref: hit.key, restored: [], removed: blocked });
|
|
4332
|
+
}
|
|
4333
|
+
if (refused.length === 0 || force) return [...rows, ...refused];
|
|
4334
|
+
const lines = refused.map((r) => `${r.ref} (${r.removed.join(", ")})`);
|
|
4335
|
+
throw new Error(
|
|
4336
|
+
`Not re-locked: the deployed copy of ${refused.length === 1 ? "an asset" : "assets"} holds files its locked content did not ship or that were edited, and the deploy would delete them:
|
|
4337
|
+
${lines.join("\n ")}
|
|
4338
|
+
Nothing was changed. Copy what you need, or run \`${forceCommand}\` to delete them.`
|
|
4339
|
+
);
|
|
4340
|
+
}
|
|
4341
|
+
/**
|
|
4342
|
+
* Stop `install` from overwriting a hand-edited rendered agent file that no lock entry
|
|
4343
|
+
* records (OQ5, p1440): the lock is gone (or never held the agent), so nothing tells an edit
|
|
4344
|
+
* from a stale render, and the deploy would replace the file without a word. Checks the file
|
|
4345
|
+
* named after each agent about to be deployed and the file of each agent instance whose bound
|
|
4346
|
+
* agent has no lock entry. Throws one error naming each file that exists and differs from its
|
|
4347
|
+
* render and changes nothing; `--force` (the caller skips this check) overwrites. A file that
|
|
4348
|
+
* equals its render is not an edit. Project mode only.
|
|
4349
|
+
*/
|
|
4350
|
+
assertNoUnrecordedAgentOverwrite(entries, prevLock, forceCommand) {
|
|
4351
|
+
if (this.global) return;
|
|
4352
|
+
const lockedAgents = new Set(
|
|
4353
|
+
Object.keys(prevLock?.assets ?? {}).map(parseCanonicalLockKey).filter((k) => k.kind && this.installTarget(k.kind) === "agent").map((k) => k.name)
|
|
4354
|
+
);
|
|
4355
|
+
const opts = this.deployOptsFor(this.harness);
|
|
4356
|
+
const lines = /* @__PURE__ */ new Set();
|
|
4357
|
+
const collect = (diff, what) => {
|
|
4358
|
+
if (!diff) return;
|
|
4359
|
+
for (const m of diff.matchAll(/^--- (.+?)\t/gm)) {
|
|
4360
|
+
const abs = m[1];
|
|
4361
|
+
if (!existsSync(abs)) continue;
|
|
4362
|
+
lines.add(
|
|
4363
|
+
`${relative(this.projectDir, abs).split(sep).join("/")} (${what} differs from its render)`
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4366
|
+
};
|
|
4367
|
+
for (const e of entries) {
|
|
4368
|
+
if (this.installTarget(e.kind) !== "agent" || lockedAgents.has(e.name)) continue;
|
|
4369
|
+
try {
|
|
4370
|
+
collect(diffDeployedAgent(e, opts), `agent ${e.name}`);
|
|
4371
|
+
} catch {
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
let instances = {};
|
|
4375
|
+
try {
|
|
4376
|
+
instances = effectiveAgentInstances(resolveSkWorkspaceConfig(this.projectDir));
|
|
4377
|
+
} catch {
|
|
4378
|
+
}
|
|
4379
|
+
for (const [id, config] of Object.entries(instances)) {
|
|
4380
|
+
if (!config.ref.startsWith("agent:")) continue;
|
|
4381
|
+
const bound = parseRefTolerant(config.ref);
|
|
4382
|
+
if (bound?.kind === "agent" && lockedAgents.has(bound.name)) continue;
|
|
4383
|
+
try {
|
|
4384
|
+
const srcDir = resolveAgentDefinitionDir(this.projectDir, config.ref);
|
|
4385
|
+
if (srcDir === void 0) continue;
|
|
4386
|
+
const entry = {
|
|
4387
|
+
kind: "agent",
|
|
4388
|
+
name: id,
|
|
4389
|
+
description: "",
|
|
4390
|
+
source: srcDir,
|
|
4391
|
+
version: "",
|
|
4392
|
+
requires: [],
|
|
4393
|
+
dependencies: []
|
|
4394
|
+
};
|
|
4395
|
+
collect(diffDeployedAgent(entry, opts, { id, srcDir, config }), `agent instance ${id}`);
|
|
4396
|
+
} catch {
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
if (lines.size === 0) return;
|
|
4400
|
+
throw new Error(
|
|
4401
|
+
`Not deployed: the deploy would overwrite ${lines.size === 1 ? "a rendered agent file" : "rendered agent files"} that no lock entry records:
|
|
4402
|
+
${[...lines].join("\n ")}
|
|
4403
|
+
Nothing was changed. Move your edits away, or run \`${forceCommand}\` to overwrite the file.`
|
|
3867
4404
|
);
|
|
3868
4405
|
}
|
|
3869
4406
|
/**
|
|
@@ -4091,7 +4628,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4091
4628
|
}
|
|
4092
4629
|
assertLockHashes(lock);
|
|
4093
4630
|
this.assertNoFolderCollision(Object.keys(lock.assets).map(parseCanonicalLockKey));
|
|
4094
|
-
const orphaned = [...(await this.orphanedSourceGaps()).values()];
|
|
4631
|
+
const orphaned = [...(await this.orphanedSourceGaps(void 0, true)).values()];
|
|
4095
4632
|
if (orphaned.length > 0) {
|
|
4096
4633
|
throw new Error(
|
|
4097
4634
|
`Lock file verification failed:
|
|
@@ -4166,12 +4703,14 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4166
4703
|
throw new Error(`Lock file verification failed:
|
|
4167
4704
|
${reason}`);
|
|
4168
4705
|
}
|
|
4169
|
-
const library = await this.restoreLibraryLockEntries(lock);
|
|
4706
|
+
const library = await this.restoreLibraryLockEntries(lock, { storeFallback: true });
|
|
4170
4707
|
assertFlowsLoad([...resolved, ...library.entries], "installed");
|
|
4171
4708
|
assertNoLibrarySymlinks([...resolved, ...library.entries], this.registry, repositories);
|
|
4709
|
+
const inferredHarnesses = this.inferLegacyHarnessSubsets([...resolved, ...library.entries]);
|
|
4172
4710
|
const copyDrift = await this.lockedCopyDrift();
|
|
4173
4711
|
const missingCopies = await this.incompleteDeploys();
|
|
4174
4712
|
const unrecordedAgents = this.unrecordedAgentFiles(lock);
|
|
4713
|
+
const composedDrift = /* @__PURE__ */ new Set();
|
|
4175
4714
|
const deployed = [
|
|
4176
4715
|
...new Set(
|
|
4177
4716
|
this.targetHarnesses().flatMap(
|
|
@@ -4179,7 +4718,10 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4179
4718
|
this.entriesForHarness([...resolved, ...library.entries], t),
|
|
4180
4719
|
repositories,
|
|
4181
4720
|
patches,
|
|
4182
|
-
this.deployOptsFor(t
|
|
4721
|
+
this.deployOptsFor(t, void 0, void 0, {
|
|
4722
|
+
refresh: false,
|
|
4723
|
+
onDrift: (ref, composeRef) => composedDrift.add(this.composedDriftLine(ref, composeRef))
|
|
4724
|
+
})
|
|
4183
4725
|
)
|
|
4184
4726
|
)
|
|
4185
4727
|
)
|
|
@@ -4197,6 +4739,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4197
4739
|
);
|
|
4198
4740
|
const gaps = (await this.projectState()).gaps.filter((g) => {
|
|
4199
4741
|
if (g.type === "agent") return false;
|
|
4742
|
+
if (g.unreadableSource) return false;
|
|
4743
|
+
if (g.type === "library" && library.viaStore.includes(g.ref)) return false;
|
|
4200
4744
|
const p = parseRefTolerant(g.ref);
|
|
4201
4745
|
return !(p && alreadyMissing.has(`${p.kind}:${p.name}`));
|
|
4202
4746
|
}).map((g) => g.message);
|
|
@@ -4205,10 +4749,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4205
4749
|
return {
|
|
4206
4750
|
deployed,
|
|
4207
4751
|
removed: [],
|
|
4208
|
-
missing: [...library.missing, ...gaps],
|
|
4752
|
+
missing: [...library.missing, ...gaps, ...composedDrift],
|
|
4209
4753
|
...placeholderBodies.length > 0 ? { placeholderBodies } : {},
|
|
4210
4754
|
...placeholderDescriptions.length > 0 ? { placeholderDescriptions } : {},
|
|
4211
4755
|
lockWritten: false,
|
|
4756
|
+
...inferredHarnesses.length > 0 ? { inferredHarnesses } : {},
|
|
4212
4757
|
...restored.length > 0 ? { restored } : {},
|
|
4213
4758
|
...rewritten.length > 0 ? { rewritten } : {},
|
|
4214
4759
|
...agentRender
|
|
@@ -4236,6 +4781,72 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4236
4781
|
}
|
|
4237
4782
|
return [...found].sort((a, b) => gt(coerceVersion(a), coerceVersion(b)) ? -1 : 1);
|
|
4238
4783
|
}
|
|
4784
|
+
/**
|
|
4785
|
+
* The refs of the other kinds that a source or a library holds under the same publisher and
|
|
4786
|
+
* name as `parsed` (`agent:@pub/captain` for a `@pub/captain` that is no skill), for the
|
|
4787
|
+
* `Did you mean` of an `add` that missed.
|
|
4788
|
+
*/
|
|
4789
|
+
async otherKindRefs(parsed) {
|
|
4790
|
+
if (!parsed.publisher) return [];
|
|
4791
|
+
const publisher = parsed.publisher.replace(/^@/, "");
|
|
4792
|
+
const found = [];
|
|
4793
|
+
for (const kind of [...this.knownKinds].sort()) {
|
|
4794
|
+
if (kind === parsed.kind) continue;
|
|
4795
|
+
const candidate = `${kind}:@${publisher}/${parsed.name}`;
|
|
4796
|
+
try {
|
|
4797
|
+
if (await this.info(candidate) !== null) found.push(candidate);
|
|
4798
|
+
} catch {
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
return found;
|
|
4802
|
+
}
|
|
4803
|
+
/**
|
|
4804
|
+
* What `add` would deploy for the resolved candidates and library entries, without touching
|
|
4805
|
+
* the project: one item per asset with its origin, version, deploy folders and files.
|
|
4806
|
+
*/
|
|
4807
|
+
planAddItems(resolved, lib, stores, parsed) {
|
|
4808
|
+
const storeUrls = new Set(stores.map((s) => normalizeRepoUrl(s.url)));
|
|
4809
|
+
const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
|
|
4810
|
+
const folders = (kind, name) => {
|
|
4811
|
+
const component = this.installTarget(kind);
|
|
4812
|
+
if (!component) return [];
|
|
4813
|
+
return this.targetHarnesses().map((t) => {
|
|
4814
|
+
const dir = deployedDir(component, name, t, this.global, this.projectDir);
|
|
4815
|
+
const rel = relative(this.projectDir, dir);
|
|
4816
|
+
const shown = this.global || rel.startsWith("..") ? dir.replace(homedir(), "~") : `${rel}${dir.endsWith(sep) ? sep : ""}`;
|
|
4817
|
+
return shown;
|
|
4818
|
+
});
|
|
4819
|
+
};
|
|
4820
|
+
const asked = (kind, name, publisher) => kind === parsed.kind && name === parsed.name && (!parsed.publisher || (publisher ?? "").replace(/^@/, "") === parsed.publisher.replace(/^@/, ""));
|
|
4821
|
+
const items = [];
|
|
4822
|
+
for (const c of resolved) {
|
|
4823
|
+
const url = normalizeRepoUrl(c.sourceUrl);
|
|
4824
|
+
items.push({
|
|
4825
|
+
ref: assetRefToStr({
|
|
4826
|
+
kind: c.kind,
|
|
4827
|
+
name: c.name,
|
|
4828
|
+
publisher: c.publisher.replace(/^@/, "")
|
|
4829
|
+
}),
|
|
4830
|
+
version: c.version,
|
|
4831
|
+
origin: storeUrls.has(url) ? `store ${c.sourceUrl}` : url === factoryUrl ? "factory" : `source ${sourceShortName(c.sourceUrl)}`,
|
|
4832
|
+
requested: asked(c.kind, c.name, c.publisher),
|
|
4833
|
+
folders: folders(c.kind, c.name),
|
|
4834
|
+
files: c.files.map((f) => f.path)
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
for (const e of lib.entries) {
|
|
4838
|
+
const id = this.entryIdentity(e);
|
|
4839
|
+
items.push({
|
|
4840
|
+
ref: canonicalEntryRef(e),
|
|
4841
|
+
version: e.version ?? "",
|
|
4842
|
+
origin: `library ${lib.libraryOf.get(e) ?? ""}`.trim(),
|
|
4843
|
+
requested: asked(e.kind, e.name, id.publisher),
|
|
4844
|
+
folders: folders(e.kind, e.name),
|
|
4845
|
+
files: [...e.files ?? []]
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
return items.sort((a, b) => Number(b.requested) - Number(a.requested));
|
|
4849
|
+
}
|
|
4239
4850
|
/**
|
|
4240
4851
|
* Parse the ref `add` was given. A ref the grammar rejects fails with the parser's message
|
|
4241
4852
|
* minus its manifest-migration advice (it points at a skill that is not for `add`), and:
|
|
@@ -4253,7 +4864,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4253
4864
|
const plain = err.message.endsWith(advice) ? err.message.slice(0, -advice.length) : err.message;
|
|
4254
4865
|
if (plain.startsWith("publisher required")) {
|
|
4255
4866
|
const matches = await this.resolvableMatches(ref, repositories, sources);
|
|
4256
|
-
const hint = matches.length === 0 ? "" : matches.length === 1 ? ` Did you mean ${matches[0]}?` : ` Did you mean one of: ${matches.join(", ")}?`;
|
|
4867
|
+
const hint = matches.length === 0 ? ` Find the publisher with \`skaile store search ${ref.trim().replace(/^[^:@/#\s]+:/, "").replace(/#.*$/, "")}\`.` : matches.length === 1 ? ` Did you mean ${matches[0]}?` : ` Did you mean one of: ${matches.join(", ")}?`;
|
|
4257
4868
|
throw new Error(`${plain}${hint}`);
|
|
4258
4869
|
}
|
|
4259
4870
|
const pinned = /^(.*\/[^@#/]+)@([^@#/]+)$/.exec(ref.trim());
|
|
@@ -4334,6 +4945,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4334
4945
|
stores,
|
|
4335
4946
|
declaredRefs: dependencies,
|
|
4336
4947
|
warnCollisions: true,
|
|
4948
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
4337
4949
|
readStoreRequires: true,
|
|
4338
4950
|
...opts?.preferLockedLibrary ? { preferLocked: await this.lockedPreference() } : {}
|
|
4339
4951
|
});
|
|
@@ -4377,8 +4989,9 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4377
4989
|
);
|
|
4378
4990
|
}
|
|
4379
4991
|
const available = await this.availableVersions(parsed, stores);
|
|
4992
|
+
const otherKinds = /^[^@/#\s]+:/.test(ref.trim()) ? [] : await this.otherKindRefs(parsed);
|
|
4380
4993
|
throw new Error(
|
|
4381
|
-
`Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}${available.length ? `; ${parsed.name} has version(s): ${available.join(", ")}` : ""}`
|
|
4994
|
+
`Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}${available.length ? `; ${parsed.name} has version(s): ${available.join(", ")}` : ""}` + (otherKinds.length === 0 ? "" : otherKinds.length === 1 ? `. Did you mean ${otherKinds[0]}?` : `. Did you mean one of: ${otherKinds.join(", ")}?`)
|
|
4382
4995
|
);
|
|
4383
4996
|
}
|
|
4384
4997
|
if (parsed.kind === "preset" && missing.length > 0) {
|
|
@@ -4401,6 +5014,62 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4401
5014
|
)
|
|
4402
5015
|
);
|
|
4403
5016
|
}
|
|
5017
|
+
const problems = origin && libPath && !(e.kind === "flow" && flowLoadError(e) !== void 0) ? libraryAssetCheck?.({ kind: e.kind, name: e.name, source: e.source }, libPath) ?? [] : [];
|
|
5018
|
+
if (problems.length > 0) {
|
|
5019
|
+
throw new Error(
|
|
5020
|
+
`${canonicalEntryRef(e)} in library "${origin}" fails the checks of \`skaile validate\` - nothing was added:
|
|
5021
|
+
${problems.map((l) => ` ${l}`).join("\n")}
|
|
5022
|
+
Fix the asset, then run \`skaile library index ${origin}\` and add it again.`
|
|
5023
|
+
);
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
if (libraryAssetCheck) {
|
|
5028
|
+
for (const c of resolved) {
|
|
5029
|
+
const decl = Object.entries(repositories).find(([, d]) => d.url === c.sourceUrl);
|
|
5030
|
+
const cloneDir = decl ? this.resolveRepoDir(decl[1], decl[0]) : null;
|
|
5031
|
+
const entry = cloneDir ? this.candidateToCatalogEntry(c, repositories) : null;
|
|
5032
|
+
if (!cloneDir || !entry) continue;
|
|
5033
|
+
if (entry.kind === "flow" && flowLoadError(entry) !== void 0) continue;
|
|
5034
|
+
const problems = libraryAssetCheck(
|
|
5035
|
+
{ kind: entry.kind, name: entry.name, source: entry.source },
|
|
5036
|
+
cloneDir
|
|
5037
|
+
);
|
|
5038
|
+
if (problems.length > 0) {
|
|
5039
|
+
throw new Error(
|
|
5040
|
+
`${canonicalEntryRef(entry)} from source ${c.sourceUrl} fails the checks of \`skaile validate\` - nothing was added:
|
|
5041
|
+
${problems.map((l) => ` ${l}`).join("\n")}
|
|
5042
|
+
Fix the asset in the source repo, then run \`skaile source sync\` and add it again.`
|
|
5043
|
+
);
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
const staleSiblings = this.staleSourceSiblingRefs(resolved, dependencies);
|
|
5048
|
+
if (staleSiblings.length > 0) {
|
|
5049
|
+
const siblings = await this.resolveLibraryFirst(staleSiblings, {
|
|
5050
|
+
provenanceIndex,
|
|
5051
|
+
overrides,
|
|
5052
|
+
stores,
|
|
5053
|
+
declaredRefs: dependencies,
|
|
5054
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
5055
|
+
readStoreRequires: true
|
|
5056
|
+
});
|
|
5057
|
+
for (const sib of staleSiblings) {
|
|
5058
|
+
const pin = parseAssetRefLoose(sib)?.pin;
|
|
5059
|
+
if (!pin) continue;
|
|
5060
|
+
const id = refIdentity(sib.replace(/#.*$/, ""));
|
|
5061
|
+
const kept = siblings.resolved.some(
|
|
5062
|
+
(c) => `${c.publisher.replace(/^@/, "")}/${c.kind}:${c.name}` === id?.replace(/^@/, "") && matchPin(pin, c.version ?? "")
|
|
5063
|
+
);
|
|
5064
|
+
if (!kept) {
|
|
5065
|
+
throw new Error(
|
|
5066
|
+
`${sib.replace(/#.*$/, "")} is pinned to ${pin} in skaile.yaml, but its source moved on and no longer holds ${pin}, so adding ${ref} would break the lock - nothing was added. Change the pin in skaile.yaml, or drop the pinned asset, then add again.`
|
|
5067
|
+
);
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
const held = new Set(resolved.map((c) => this.candidateKey(c)));
|
|
5071
|
+
for (const c of siblings.resolved) {
|
|
5072
|
+
if (!held.has(this.candidateKey(c))) resolved.push(c);
|
|
4404
5073
|
}
|
|
4405
5074
|
}
|
|
4406
5075
|
this.assertSourceCommitsPublished(resolved, repositories);
|
|
@@ -4437,6 +5106,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4437
5106
|
`${ref.split("#")[0]} requires ${missing.join(", ")} (not found \u2014 ${REGISTER_HOLDER}, or remove the requirement from the asset's manifest) \u2014 nothing was added`
|
|
4438
5107
|
);
|
|
4439
5108
|
}
|
|
5109
|
+
if (opts?.dryRun) {
|
|
5110
|
+
const plan = this.planAddItems(resolved, lib, stores, parsed);
|
|
5111
|
+
opts.onPlan?.(plan);
|
|
5112
|
+
return [];
|
|
5113
|
+
}
|
|
4440
5114
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
4441
5115
|
assertFlowsLoad(entries, "added");
|
|
4442
5116
|
assertNoLibrarySymlinks(entries, this.registry, repositories, opts?.link === true);
|
|
@@ -4459,7 +5133,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4459
5133
|
this.entriesForHarness(entries, t),
|
|
4460
5134
|
repositories,
|
|
4461
5135
|
patches,
|
|
4462
|
-
this.deployOptsFor(t, opts?.link, opts?.onSecretFile
|
|
5136
|
+
this.deployOptsFor(t, opts?.link, opts?.onSecretFile, {
|
|
5137
|
+
refresh: true,
|
|
5138
|
+
onDrift: () => {
|
|
5139
|
+
}
|
|
5140
|
+
})
|
|
4463
5141
|
)
|
|
4464
5142
|
)
|
|
4465
5143
|
)
|
|
@@ -4816,10 +5494,18 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4816
5494
|
}
|
|
4817
5495
|
const kept = [];
|
|
4818
5496
|
const ownedBy = [];
|
|
5497
|
+
const mcpPublishers = target.kind === "mcp-server" && !this.global ? [
|
|
5498
|
+
.../* @__PURE__ */ new Set([
|
|
5499
|
+
...this.lockHolders("mcp-server", target.name).map((h) => h.publisher),
|
|
5500
|
+
target.publisher?.replace(/^@/, "")
|
|
5501
|
+
])
|
|
5502
|
+
].filter((p) => !!p) : [];
|
|
4819
5503
|
const changed = this.removeResolved(target, { force: opts?.force, kept, ownedBy });
|
|
5504
|
+
const droppedSettings = changed && mcpPublishers.length > 0 ? this.dropRemovedMcpServerSettings(target.name, mcpPublishers) : [];
|
|
4820
5505
|
const keptNote = {
|
|
4821
5506
|
...kept.length > 0 ? { kept } : {},
|
|
4822
|
-
...ownedBy.length > 0 ? { ownedBy } : {}
|
|
5507
|
+
...ownedBy.length > 0 ? { ownedBy } : {},
|
|
5508
|
+
...droppedSettings.length > 0 ? { droppedSettings } : {}
|
|
4823
5509
|
};
|
|
4824
5510
|
const pruned = changed && requiredRefs.length > 0 ? await this.pruneOrphans(requiredRefs) : [];
|
|
4825
5511
|
if (changed && (target.kind === "mcp-server" || pruned.some((r) => r.startsWith("mcp-server:")))) {
|
|
@@ -4987,6 +5673,40 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4987
5673
|
}
|
|
4988
5674
|
return changed;
|
|
4989
5675
|
}
|
|
5676
|
+
/**
|
|
5677
|
+
* After an `mcp-server` asset left the project: drop what would keep it rendered. Its staged
|
|
5678
|
+
* store bytes under `.skaile/cache/store/<publisher>/mcp-server/<name>/` go (an `mcp_servers:`
|
|
5679
|
+
* entry of the same id would otherwise keep loading them), and so does an `mcp_servers:` entry
|
|
5680
|
+
* that only sets values for it: `env` and the like, with no `command`, `url` or recipe of its
|
|
5681
|
+
* own. An entry that can run alone is the user's own server and stays.
|
|
5682
|
+
*
|
|
5683
|
+
* @returns The dropped `mcp_servers` entries, as `mcp_servers entry "<id>"`.
|
|
5684
|
+
*/
|
|
5685
|
+
dropRemovedMcpServerSettings(name, publishers) {
|
|
5686
|
+
for (const publisher of publishers) {
|
|
5687
|
+
rmSync(join(this.storeCacheDir, publisher, "mcp-server", name), {
|
|
5688
|
+
recursive: true,
|
|
5689
|
+
force: true
|
|
5690
|
+
});
|
|
5691
|
+
}
|
|
5692
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
5693
|
+
if (!existsSync(configPath)) return [];
|
|
5694
|
+
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
5695
|
+
if (!Array.isArray(raw.mcp_servers)) return [];
|
|
5696
|
+
const dropped = [];
|
|
5697
|
+
const standalone = ["command", "url", "recipe", "launcher"];
|
|
5698
|
+
raw.mcp_servers = raw.mcp_servers.filter((entry) => {
|
|
5699
|
+
const e = entry;
|
|
5700
|
+
if (!e || typeof e.id !== "string" || unscopedName(e.id) !== name) return true;
|
|
5701
|
+
if (standalone.some((k) => e[k] !== void 0 && e[k] !== null)) return true;
|
|
5702
|
+
dropped.push(`mcp_servers entry "${e.id}"`);
|
|
5703
|
+
return false;
|
|
5704
|
+
});
|
|
5705
|
+
if (dropped.length === 0) return [];
|
|
5706
|
+
if (raw.mcp_servers.length === 0) delete raw.mcp_servers;
|
|
5707
|
+
writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
5708
|
+
return dropped;
|
|
5709
|
+
}
|
|
4990
5710
|
/**
|
|
4991
5711
|
* After `remove --global --harness X`: the harnesses outside `X` that still hold a copy of the
|
|
4992
5712
|
* asset, or `undefined` when none does (or this is not a harness-limited global remove).
|
|
@@ -5158,7 +5878,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5158
5878
|
}
|
|
5159
5879
|
}
|
|
5160
5880
|
if (best) return best;
|
|
5161
|
-
const
|
|
5881
|
+
const lockedLibraries = await this.lockedLibraryNames();
|
|
5882
|
+
const lib = await this.resolveLibraryEntries([ref], {
|
|
5883
|
+
quiet: true,
|
|
5884
|
+
...lockedLibraries.size > 0 ? { lockedLibraries } : {}
|
|
5885
|
+
});
|
|
5162
5886
|
return lib.entries[0] ?? null;
|
|
5163
5887
|
}
|
|
5164
5888
|
/**
|
|
@@ -5325,7 +6049,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5325
6049
|
state = "declared";
|
|
5326
6050
|
} else if (parsed.kind === "mcp-server") {
|
|
5327
6051
|
const wired = await wiredMcpNames();
|
|
5328
|
-
const isWired = wired.has(parsed.name) || wired.has(unscopedName(parsed.name))
|
|
6052
|
+
const isWired = wired.has(parsed.name) || wired.has(unscopedName(parsed.name)) || !this.global && await mcpCommittedRenderIntact(
|
|
6053
|
+
this.projectDir,
|
|
6054
|
+
this.harness,
|
|
6055
|
+
unscopedName(parsed.name)
|
|
6056
|
+
);
|
|
5329
6057
|
const skippedReason = Object.entries(
|
|
5330
6058
|
await readSkippedMcpServers(this.projectDir, this.harness)
|
|
5331
6059
|
).find(([id]) => id === parsed.name || unscopedName(id) === parsed.name)?.[1];
|
|
@@ -5670,11 +6398,12 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5670
6398
|
if (!lock) return [];
|
|
5671
6399
|
const bare = (s) => s.includes("#") ? s.slice(0, s.indexOf("#")) : s;
|
|
5672
6400
|
const asked = parseRefTolerant(ref);
|
|
6401
|
+
const askedPublisher = asked?.publisher?.replace(/^@/, "");
|
|
5673
6402
|
const matchesIdentity = (k) => {
|
|
5674
6403
|
if (bare(k) === bare(ref)) return true;
|
|
5675
|
-
if (!asked
|
|
6404
|
+
if (!asked) return false;
|
|
5676
6405
|
const locked = parseCanonicalLockKey(k);
|
|
5677
|
-
return locked.kind === asked.kind && locked.name === asked.name;
|
|
6406
|
+
return locked.kind === asked.kind && locked.name === asked.name && (!askedPublisher || locked.publisher === askedPublisher);
|
|
5678
6407
|
};
|
|
5679
6408
|
const key = lock.assets[ref] ? ref : Object.keys(lock.assets).find(matchesIdentity);
|
|
5680
6409
|
if (!key) return asked?.kind === "preset" ? this.whyPreset(lock, asked) : [];
|
|
@@ -5810,6 +6539,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5810
6539
|
const libraryAssets = Object.entries(lock.assets).filter(
|
|
5811
6540
|
([, e]) => e.library && !e.library.installed
|
|
5812
6541
|
);
|
|
6542
|
+
const declaredPins = this.declaredPinsByIdentity();
|
|
5813
6543
|
if (libraryAssets.length > 0) {
|
|
5814
6544
|
const snap = await this.loadLibraryIndex();
|
|
5815
6545
|
const diskHashes = /* @__PURE__ */ new Map();
|
|
@@ -5835,6 +6565,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5835
6565
|
{ kind, publisher, name },
|
|
5836
6566
|
found.version
|
|
5837
6567
|
);
|
|
6568
|
+
const declaredPin = declaredPins.get(`${kind}:${publisher}/${name}`);
|
|
6569
|
+
if (declaredPin && diskVersion && !matchPin(declaredPin, diskVersion)) continue;
|
|
5838
6570
|
if (diskVersion && !isPseudoVersion(diskVersion) && gt(coerceVersion(diskVersion), coerceVersion(origin.version))) {
|
|
5839
6571
|
result.push({
|
|
5840
6572
|
kind,
|
|
@@ -5873,6 +6605,26 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5873
6605
|
library: lookup.name,
|
|
5874
6606
|
contentChanged: true
|
|
5875
6607
|
});
|
|
6608
|
+
continue;
|
|
6609
|
+
}
|
|
6610
|
+
}
|
|
6611
|
+
if (entry.composed && kind === "agent") {
|
|
6612
|
+
const srcDir = statSync(found.source).isDirectory() ? found.source : dirname(found.source);
|
|
6613
|
+
const now = new Map(
|
|
6614
|
+
sourceComposedFiles(srcDir, name, this.projectDir).map((f) => [f.path, f.sha256])
|
|
6615
|
+
);
|
|
6616
|
+
const locked = new Map(entry.composed.map((f) => [f.path, f.sha256]));
|
|
6617
|
+
const same = now.size === locked.size && [...now].every(([f, h]) => locked.get(f) === h);
|
|
6618
|
+
if (!same) {
|
|
6619
|
+
result.push({
|
|
6620
|
+
kind,
|
|
6621
|
+
name,
|
|
6622
|
+
publisher,
|
|
6623
|
+
currentVersion: origin.version,
|
|
6624
|
+
behind: 0,
|
|
6625
|
+
library: lookup.name,
|
|
6626
|
+
contentChanged: true
|
|
6627
|
+
});
|
|
5876
6628
|
}
|
|
5877
6629
|
}
|
|
5878
6630
|
}
|
|
@@ -5882,6 +6634,23 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5882
6634
|
await this.addPinnedRows(lock, result);
|
|
5883
6635
|
return this.addLockGapRows(result);
|
|
5884
6636
|
}
|
|
6637
|
+
/**
|
|
6638
|
+
* The `#pin` of every publisher-qualified `skaile.yaml` dependency, keyed `kind:publisher/name`
|
|
6639
|
+
* (no scope sigil). `update` honours it, so `outdated` must not list a move it keeps.
|
|
6640
|
+
*/
|
|
6641
|
+
declaredPinsByIdentity() {
|
|
6642
|
+
const pins = /* @__PURE__ */ new Map();
|
|
6643
|
+
for (const dep of this.loadConfig().dependencies) {
|
|
6644
|
+
try {
|
|
6645
|
+
const p = parseAssetRef(dep);
|
|
6646
|
+
if (p.pin && p.publisher) {
|
|
6647
|
+
pins.set(`${p.kind}:${p.publisher.replace(/^@/, "")}/${p.name}`, p.pin);
|
|
6648
|
+
}
|
|
6649
|
+
} catch {
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
return pins;
|
|
6653
|
+
}
|
|
5885
6654
|
/**
|
|
5886
6655
|
* List a locked asset whose `skaile.yaml` ref carries a `#version` pin while a source, a
|
|
5887
6656
|
* library or a store holds a newer one: `locked 0.7.0 -> 0.9.0 (pinned)`. The pin is the
|
|
@@ -5986,20 +6755,20 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5986
6755
|
async addLibraryMoves(libraryAssets, rows) {
|
|
5987
6756
|
const refs = [];
|
|
5988
6757
|
const lockedIn = /* @__PURE__ */ new Map();
|
|
6758
|
+
const declaredPins = this.declaredPinsByIdentity();
|
|
5989
6759
|
for (const [key, entry] of libraryAssets) {
|
|
5990
6760
|
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
5991
6761
|
if (!publisher || !kind || !name || !entry.library) continue;
|
|
5992
6762
|
const ref = `${kind}:@${publisher}/${name}`;
|
|
5993
|
-
|
|
6763
|
+
const pin = declaredPins.get(`${kind}:${publisher}/${name}`);
|
|
6764
|
+
refs.push(pin ? `${ref}#${pin}` : ref);
|
|
5994
6765
|
lockedIn.set(ref, { name: entry.library.name, key });
|
|
5995
6766
|
}
|
|
5996
6767
|
if (refs.length === 0) return;
|
|
5997
6768
|
let res;
|
|
6769
|
+
const preferLocked = await this.lockedPreference();
|
|
5998
6770
|
try {
|
|
5999
|
-
res = await this.resolveLibraryEntries(refs, {
|
|
6000
|
-
quiet: true,
|
|
6001
|
-
preferLocked: await this.lockedPreference()
|
|
6002
|
-
});
|
|
6771
|
+
res = await this.resolveLibraryEntries(refs, { quiet: true, preferLocked });
|
|
6003
6772
|
} catch {
|
|
6004
6773
|
return;
|
|
6005
6774
|
}
|
|
@@ -6009,9 +6778,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6009
6778
|
const ref = `${e.kind}:@${publisher}/${e.name}`;
|
|
6010
6779
|
const from = lockedIn.get(ref);
|
|
6011
6780
|
if (!to || !from || from.name === to) continue;
|
|
6781
|
+
const renamedOnly = preferLocked.libraries.get(`${e.kind}:${publisher}/${e.name}`) === to;
|
|
6012
6782
|
const row = rows.find(
|
|
6013
6783
|
(r) => r.kind === e.kind && r.name === e.name && r.publisher.replace(/^@/, "") === publisher
|
|
6014
6784
|
);
|
|
6785
|
+
if (renamedOnly && !row) continue;
|
|
6015
6786
|
if (row) {
|
|
6016
6787
|
row.library = from.name;
|
|
6017
6788
|
row.movesTo = to;
|
|
@@ -6143,7 +6914,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6143
6914
|
(r) => unscopedName(r.drift.id) === unscopedName(parsed.name)
|
|
6144
6915
|
);
|
|
6145
6916
|
if (!row) return { status: "identical" };
|
|
6146
|
-
const fileSetDiffers = row.drift.extra.length > 0 || row.drift.missing.
|
|
6917
|
+
const fileSetDiffers = row.drift.extra.length > 0 || row.drift.missing.length > 0;
|
|
6147
6918
|
return {
|
|
6148
6919
|
status: "differs",
|
|
6149
6920
|
diff: mcpDriftDiff(row.drift),
|
|
@@ -6241,7 +7012,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6241
7012
|
provenanceIndex,
|
|
6242
7013
|
overrides,
|
|
6243
7014
|
stores,
|
|
6244
|
-
declaredRefs: dependencies
|
|
7015
|
+
declaredRefs: dependencies,
|
|
7016
|
+
lockedOrigin: this.lockedOriginVersions()
|
|
6245
7017
|
});
|
|
6246
7018
|
this.assertSourceCommitsPublished(resolved, repositories);
|
|
6247
7019
|
const libraryLock = await this.libraryLockEntries(lib);
|
|
@@ -6250,6 +7022,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6250
7022
|
for (const [key, entry] of Object.entries(lockData.assets)) {
|
|
6251
7023
|
const rendered = prior?.assets[key]?.rendered;
|
|
6252
7024
|
if (rendered) entry.rendered = rendered;
|
|
7025
|
+
const composed = prior?.assets[key]?.composed;
|
|
7026
|
+
if (composed) entry.composed = composed;
|
|
6253
7027
|
}
|
|
6254
7028
|
writeLock(this.lockPath, lockData);
|
|
6255
7029
|
return lockData;
|
|
@@ -6609,7 +7383,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6609
7383
|
ref: s.url,
|
|
6610
7384
|
reason,
|
|
6611
7385
|
message: `source ${s.url} ${reason}`,
|
|
6612
|
-
manualFix: true
|
|
7386
|
+
manualFix: true,
|
|
7387
|
+
unreadableSource: true
|
|
6613
7388
|
});
|
|
6614
7389
|
}
|
|
6615
7390
|
for (const u of unboundAgentInstances(this.projectDir, this.harness)) {
|
|
@@ -6835,7 +7610,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6835
7610
|
continue;
|
|
6836
7611
|
}
|
|
6837
7612
|
if (lookup.state === "unregistered") {
|
|
6838
|
-
const reason2 = `is locked, but library "${origin
|
|
7613
|
+
const reason2 = `is locked, but library "${unregisteredLibraryName(origin)}" is not registered (renamed or removed?) \u2014 ${unregisteredLibraryFix(origin, ref)}`;
|
|
6839
7614
|
gaps.push({ type: "library", ref, reason: reason2, message: `${ref} ${reason2}`, manualFix: true });
|
|
6840
7615
|
continue;
|
|
6841
7616
|
}
|
|
@@ -6855,12 +7630,15 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6855
7630
|
* The locked source assets whose source is no longer in `skaile.yaml` `sources:` (`source
|
|
6856
7631
|
* remove`, a hand edit), keyed by lock key. ONE sentence for it: `check`, `doctor`, `outdated`,
|
|
6857
7632
|
* `install`, `install --locked` and `update` all say it (p1062), and none re-clones the source.
|
|
6858
|
-
* A
|
|
6859
|
-
*
|
|
7633
|
+
* A GitHub lock entry that no source declares counts as store-supplied only when a store
|
|
7634
|
+
* lists the ref (OQ3): a configured store alone proves nothing, as it may never have held
|
|
7635
|
+
* the asset. `check`, `doctor` and `outdated` read the store's cached catalog and never touch
|
|
7636
|
+
* the network; `install` and `update` (`online`) ask the store itself.
|
|
6860
7637
|
*
|
|
6861
7638
|
* @param inScope - Only these lock keys count (`install` prunes an undeclared entry as usual).
|
|
7639
|
+
* @param online - Ask the stores when the cached catalog does not list the ref.
|
|
6862
7640
|
*/
|
|
6863
|
-
async orphanedSourceGaps(inScope) {
|
|
7641
|
+
async orphanedSourceGaps(inScope, online = false) {
|
|
6864
7642
|
const out = /* @__PURE__ */ new Map();
|
|
6865
7643
|
let config;
|
|
6866
7644
|
try {
|
|
@@ -6868,7 +7646,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6868
7646
|
} catch {
|
|
6869
7647
|
return out;
|
|
6870
7648
|
}
|
|
6871
|
-
const
|
|
7649
|
+
const stores = [...config.stores, ...await this.globalStoreFallback(config.stores, false)];
|
|
6872
7650
|
const registered = new Set(config.sources.map((s) => sourceCacheKey(s.url)));
|
|
6873
7651
|
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
6874
7652
|
const url = entry.source?.url;
|
|
@@ -6878,13 +7656,38 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6878
7656
|
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
6879
7657
|
if (!publisher || !kind || !name) continue;
|
|
6880
7658
|
if (registered.has(sourceCacheKey(url))) continue;
|
|
6881
|
-
if (storeBacked && GITHUB_REPO_URL.test(url)) continue;
|
|
6882
7659
|
const ref = `${kind}:@${publisher}/${name}`;
|
|
7660
|
+
if (GITHUB_REPO_URL.test(url) && await this.storeListsRef(stores, ref, online)) continue;
|
|
6883
7661
|
const reason = `is locked, but its source ${url} is not in skaile.yaml sources: any more \u2014 register it again with \`skaile source add ${url}\`, or drop the dependency with \`skaile remove ${ref}\``;
|
|
6884
7662
|
out.set(key, { type: "source", ref, reason, message: `${ref} ${reason}`, manualFix: true });
|
|
6885
7663
|
}
|
|
6886
7664
|
return out;
|
|
6887
7665
|
}
|
|
7666
|
+
/**
|
|
7667
|
+
* `true` when one of `stores` lists `ref` (a canonical lock key): its cached catalog holds it
|
|
7668
|
+
* or, with `online`, the store answers for it. An injected store fetcher is the store client
|
|
7669
|
+
* itself, so it is asked either way. A store an online install cannot ask (unreachable,
|
|
7670
|
+
* air-gapped, `--no-catalog`) may hold the ref; `check` never asks, so a store lists only what
|
|
7671
|
+
* its cached catalog holds.
|
|
7672
|
+
*/
|
|
7673
|
+
async storeListsRef(stores, ref, online) {
|
|
7674
|
+
for (const store of stores) {
|
|
7675
|
+
try {
|
|
7676
|
+
if (this.injectedStoreFetcher) {
|
|
7677
|
+
if (await this.injectedStoreFetcher.getInstallManifest(store.url, ref)) return true;
|
|
7678
|
+
continue;
|
|
7679
|
+
}
|
|
7680
|
+
const remote = this.remoteStore(store.url);
|
|
7681
|
+
if (remote.holdsCached(ref)) return true;
|
|
7682
|
+
if (!online) continue;
|
|
7683
|
+
if (this.catalogUrlOverride === "") return true;
|
|
7684
|
+
if (await remote.resolve(ref)) return true;
|
|
7685
|
+
} catch {
|
|
7686
|
+
if (online) return true;
|
|
7687
|
+
}
|
|
7688
|
+
}
|
|
7689
|
+
return false;
|
|
7690
|
+
}
|
|
6888
7691
|
/**
|
|
6889
7692
|
* Locked source assets whose source cannot supply them any more: the source is no longer in
|
|
6890
7693
|
* `skaile.yaml` `sources:` (`source remove`, a hand edit), or it is there but now publishes
|
|
@@ -6941,8 +7744,12 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6941
7744
|
* rendering occupies and their sha256. Call it after the deploy, so the bytes hashed are the
|
|
6942
7745
|
* ones `install` just wrote; {@link lockedCopyDrift} compares later edits with them.
|
|
6943
7746
|
* Entries that carry a record (kept from an earlier lock) are left as they are.
|
|
7747
|
+
*
|
|
7748
|
+
* @param keepComposedOf - The lock an `install` read: an agent whose `../` compose folder is
|
|
7749
|
+
* not in the cache keeps that lock's `composed:` record. Omitted by `update` and `add`,
|
|
7750
|
+
* which copy the library's current bytes.
|
|
6944
7751
|
*/
|
|
6945
|
-
withRenderedAgents(lock) {
|
|
7752
|
+
withRenderedAgents(lock, keepComposedOf) {
|
|
6946
7753
|
if (this.global) return lock;
|
|
6947
7754
|
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
6948
7755
|
if (entry.rendered || entry.library?.installed) continue;
|
|
@@ -6951,6 +7758,18 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6951
7758
|
const rendered = this.renderedAgentFiles(name);
|
|
6952
7759
|
if (rendered.length > 0) entry.rendered = rendered;
|
|
6953
7760
|
}
|
|
7761
|
+
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
7762
|
+
if (entry.library?.installed) continue;
|
|
7763
|
+
const { kind, name } = parseCanonicalLockKey(key);
|
|
7764
|
+
if (!kind || !name || this.installTarget(kind) !== "agent") continue;
|
|
7765
|
+
const composed = cachedComposedFiles(name, this.projectDir);
|
|
7766
|
+
if (composed.length > 0) entry.composed = composed;
|
|
7767
|
+
else {
|
|
7768
|
+
const earlier = keepComposedOf?.assets[key]?.composed;
|
|
7769
|
+
if (earlier?.length) entry.composed = earlier;
|
|
7770
|
+
else delete entry.composed;
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
6954
7773
|
return this.withInstanceRenderings(lock);
|
|
6955
7774
|
}
|
|
6956
7775
|
/**
|
|
@@ -7206,12 +8025,13 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7206
8025
|
out.push({ ref: refOf(key), key, modified, extra, library: false });
|
|
7207
8026
|
}
|
|
7208
8027
|
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
7209
|
-
|
|
8028
|
+
const recorded = [...entry.rendered ?? [], ...entry.composed ?? []];
|
|
8029
|
+
if (recorded.length === 0 || entry.library?.installed) continue;
|
|
7210
8030
|
const { kind, name } = parseCanonicalLockKey(key);
|
|
7211
8031
|
if (!kind || !name || this.installTarget(kind) !== "agent") continue;
|
|
7212
8032
|
if (patches[`${kind}:${name}`]) continue;
|
|
7213
8033
|
const modified = [];
|
|
7214
|
-
for (const f of
|
|
8034
|
+
for (const f of recorded) {
|
|
7215
8035
|
const file = join(this.projectDir, f.path);
|
|
7216
8036
|
if (!existsSync(file)) continue;
|
|
7217
8037
|
try {
|
|
@@ -7232,7 +8052,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7232
8052
|
extra: [],
|
|
7233
8053
|
library: false,
|
|
7234
8054
|
rendered: true,
|
|
7235
|
-
lockedSha: new Map(
|
|
8055
|
+
lockedSha: new Map(recorded.map((f) => [f.path, f.sha256])),
|
|
7236
8056
|
instanceOf
|
|
7237
8057
|
});
|
|
7238
8058
|
}
|
|
@@ -7345,10 +8165,23 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7345
8165
|
({ id }) => id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
|
|
7346
8166
|
);
|
|
7347
8167
|
if (held.length > 0 && !held.some(({ id }) => !id.version || matchPin(want.pin, id.version))) {
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
8168
|
+
const lockHolds = held.map((h) => h.key).join(", ");
|
|
8169
|
+
const pinned = await this.info(d.ref);
|
|
8170
|
+
const pinHeld = pinned !== null && matchPin(want.pin, pinned.version ?? "");
|
|
8171
|
+
if (!pinHeld && this.loadConfig().stores.length === 0) {
|
|
8172
|
+
const plainRef = d.ref.split("#")[0] ?? d.ref;
|
|
8173
|
+
const other = pinned ?? await this.info(plainRef);
|
|
8174
|
+
gap = {
|
|
8175
|
+
type: "pin",
|
|
8176
|
+
reason: `is pinned in skaile.yaml but the lock holds ${lockHolds}, and no library or source holds ${want.pin}${other?.version ? ` (${want.name} has version(s): ${other.version})` : ""} \u2014 change the pin in skaile.yaml`,
|
|
8177
|
+
manualFix: true
|
|
8178
|
+
};
|
|
8179
|
+
} else {
|
|
8180
|
+
gap = {
|
|
8181
|
+
type: "pin",
|
|
8182
|
+
reason: `is pinned in skaile.yaml but the lock holds ${lockHolds} \u2014 run \`skaile install\``
|
|
8183
|
+
};
|
|
8184
|
+
}
|
|
7352
8185
|
}
|
|
7353
8186
|
}
|
|
7354
8187
|
if (gap) gaps.push({ ...gap, ref: d.ref, message: `${d.ref} ${gap.reason}` });
|
|
@@ -7821,6 +8654,6 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7821
8654
|
}
|
|
7822
8655
|
};
|
|
7823
8656
|
|
|
7824
|
-
export { AssetManager, FACTORY_SOURCE_URL, LibraryEntryError, MissingManifestError, installAgentInstances, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName };
|
|
7825
|
-
//# sourceMappingURL=chunk-
|
|
7826
|
-
//# sourceMappingURL=chunk-
|
|
8657
|
+
export { AssetManager, FACTORY_SOURCE_URL, LibraryEntryError, MissingManifestError, installAgentInstances, mcpNotRunnableReason, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, setLibraryAssetCheck, softDepName };
|
|
8658
|
+
//# sourceMappingURL=chunk-HWNK3F3X.js.map
|
|
8659
|
+
//# sourceMappingURL=chunk-HWNK3F3X.js.map
|