@skaile/workspaces 6.0.0 → 6.2.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 +98 -0
- package/dist/{asset-feeds-PXUWCXVI.js → asset-feeds-KPKCE64E.js} +31 -23
- package/dist/asset-feeds-KPKCE64E.js.map +1 -0
- package/dist/asset-manager/catalog-deployer.js +10 -10
- package/dist/asset-manager/fragments.js +1 -1
- package/dist/asset-manager/index.js +19 -19
- package/dist/asset-manager/installer.js +13 -13
- package/dist/asset-manager/renderers.js +11 -11
- package/dist/asset-manager/src/index.d.ts +339 -19
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/asset-manager/src/installer.d.ts +40 -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/asset-manager/src/renderers.d.ts +7 -0
- package/dist/asset-manager/src/renderers.d.ts.map +1 -1
- package/dist/bridge/harnesses/claude-sdk.js +18 -22
- package/dist/bridge/harnesses/claude-sdk.js.map +1 -1
- package/dist/bridge/harnesses/codex.js +24 -17
- package/dist/bridge/harnesses/codex.js.map +1 -1
- package/dist/bridge/harnesses/continue.js +14 -14
- package/dist/bridge/harnesses/echo.js +4 -4
- package/dist/bridge/harnesses/gemini.js +10 -10
- package/dist/bridge/harnesses/goose.js +14 -14
- package/dist/bridge/harnesses/omp.js +15 -8
- package/dist/bridge/harnesses/omp.js.map +1 -1
- package/dist/bridge/harnesses/opencode.js +14 -14
- package/dist/bridge/harnesses/qwen.js +10 -10
- package/dist/bridge/index.js +19 -19
- 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 +30 -0
- package/dist/bridge/src/harness-model-check.d.ts.map +1 -1
- package/dist/bridge/src/harnesses/_subprocess-cli.d.ts.map +1 -1
- package/dist/bridge/src/harnesses/claude-sdk.d.ts.map +1 -1
- package/dist/bridge/src/harnesses/codex.d.ts.map +1 -1
- package/dist/bridge/src/harnesses/omp.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/bridge/src/runner-private-env.d.ts +7 -0
- package/dist/bridge/src/runner-private-env.d.ts.map +1 -1
- package/dist/{chunk-HK7HMEPT.js → chunk-27SXN33H.js} +13 -5
- package/dist/chunk-27SXN33H.js.map +1 -0
- package/dist/{chunk-TKHJXPSB.js → chunk-2XM3ZH36.js} +183 -33
- package/dist/chunk-2XM3ZH36.js.map +1 -0
- package/dist/{chunk-E3GPGTW3.js → chunk-3HACK2Z7.js} +142 -28
- package/dist/chunk-3HACK2Z7.js.map +1 -0
- package/dist/{chunk-T2BDID2J.js → chunk-5JPW27BI.js} +10 -9
- package/dist/chunk-5JPW27BI.js.map +1 -0
- package/dist/{chunk-2UUKCROR.js → chunk-5LQ62LNT.js} +4 -4
- package/dist/{chunk-2UUKCROR.js.map → chunk-5LQ62LNT.js.map} +1 -1
- package/dist/{chunk-UL22SDGR.js → chunk-6CYKTHNT.js} +194 -127
- package/dist/chunk-6CYKTHNT.js.map +1 -0
- package/dist/{chunk-XFIEVEU7.js → chunk-6OHZUUR7.js} +5 -4
- package/dist/chunk-6OHZUUR7.js.map +1 -0
- package/dist/{chunk-E572OHGY.js → chunk-76FLBODK.js} +4 -3
- package/dist/chunk-76FLBODK.js.map +1 -0
- package/dist/{chunk-GN4VVSAA.js → chunk-7DRFKAJZ.js} +7 -7
- package/dist/{chunk-GN4VVSAA.js.map → chunk-7DRFKAJZ.js.map} +1 -1
- package/dist/chunk-7H6XRCPG.js +31 -0
- package/dist/chunk-7H6XRCPG.js.map +1 -0
- package/dist/{chunk-MCUJTNY5.js → chunk-7Y6S4XLL.js} +3 -3
- package/dist/{chunk-MCUJTNY5.js.map → chunk-7Y6S4XLL.js.map} +1 -1
- package/dist/{chunk-DHYSUJ3H.js → chunk-BEYNH2LS.js} +4 -4
- package/dist/{chunk-DHYSUJ3H.js.map → chunk-BEYNH2LS.js.map} +1 -1
- package/dist/{chunk-GEQSUC6U.js → chunk-BOT64P3U.js} +4 -4
- package/dist/{chunk-GEQSUC6U.js.map → chunk-BOT64P3U.js.map} +1 -1
- package/dist/{chunk-L4YKW5AY.js → chunk-BRK5QZKI.js} +121 -20
- package/dist/chunk-BRK5QZKI.js.map +1 -0
- package/dist/{chunk-YGRZNFRO.js → chunk-BY5WIRCU.js} +58 -5
- package/dist/chunk-BY5WIRCU.js.map +1 -0
- package/dist/{chunk-Z7ZEZZB6.js → chunk-C5HFK34M.js} +3 -3
- package/dist/chunk-C5HFK34M.js.map +1 -0
- package/dist/{chunk-DUMTLYSO.js → chunk-COAFAYBS.js} +31 -7
- package/dist/chunk-COAFAYBS.js.map +1 -0
- package/dist/{chunk-7XSAW5OC.js → chunk-E5NQRHEB.js} +30 -7
- package/dist/chunk-E5NQRHEB.js.map +1 -0
- package/dist/{chunk-BFMYYNDT.js → chunk-F3D25RGM.js} +11 -3
- package/dist/chunk-F3D25RGM.js.map +1 -0
- package/dist/{chunk-VBI7HGHB.js → chunk-GF5JXQIZ.js} +3 -3
- package/dist/{chunk-VBI7HGHB.js.map → chunk-GF5JXQIZ.js.map} +1 -1
- package/dist/{chunk-ZFUZ5AKM.js → chunk-I7LC5TDY.js} +3 -3
- package/dist/{chunk-ZFUZ5AKM.js.map → chunk-I7LC5TDY.js.map} +1 -1
- package/dist/{chunk-3IXIEHUB.js → chunk-ICSGNT5P.js} +7 -7
- package/dist/{chunk-3IXIEHUB.js.map → chunk-ICSGNT5P.js.map} +1 -1
- package/dist/{chunk-WEH33JSG.js → chunk-ITFCWMOY.js} +93 -29
- package/dist/chunk-ITFCWMOY.js.map +1 -0
- package/dist/{chunk-WDXZA7QW.js → chunk-IZQOO6MN.js} +2 -2
- package/dist/{chunk-WDXZA7QW.js.map → chunk-IZQOO6MN.js.map} +1 -1
- package/dist/{chunk-WDAIWDLK.js → chunk-JFLQICIU.js} +5 -5
- package/dist/{chunk-WDAIWDLK.js.map → chunk-JFLQICIU.js.map} +1 -1
- package/dist/{chunk-ARWKK325.js → chunk-JJJ2G4K2.js} +5 -5
- package/dist/{chunk-ARWKK325.js.map → chunk-JJJ2G4K2.js.map} +1 -1
- package/dist/{chunk-GOCWM7FN.js → chunk-JMIKGAUA.js} +43 -4
- package/dist/chunk-JMIKGAUA.js.map +1 -0
- package/dist/{chunk-X3RMRJNR.js → chunk-JRN5QF4C.js} +8 -3
- package/dist/chunk-JRN5QF4C.js.map +1 -0
- package/dist/{chunk-SNEE6UGY.js → chunk-K3R2UKG7.js} +18 -12
- package/dist/chunk-K3R2UKG7.js.map +1 -0
- package/dist/{chunk-VXEYX73S.js → chunk-KB56WBQ3.js} +4 -4
- package/dist/{chunk-VXEYX73S.js.map → chunk-KB56WBQ3.js.map} +1 -1
- package/dist/{chunk-OHNKYHRR.js → chunk-KJSC2ZD6.js} +264 -18
- package/dist/chunk-KJSC2ZD6.js.map +1 -0
- package/dist/{chunk-UQ33Y4DM.js → chunk-KPKGWXUJ.js} +4 -4
- package/dist/{chunk-UQ33Y4DM.js.map → chunk-KPKGWXUJ.js.map} +1 -1
- package/dist/{chunk-AF432MS3.js → chunk-KXJMQTOU.js} +3 -3
- package/dist/{chunk-AF432MS3.js.map → chunk-KXJMQTOU.js.map} +1 -1
- package/dist/chunk-LRBP3234.js +302 -0
- package/dist/chunk-LRBP3234.js.map +1 -0
- package/dist/{chunk-UVVVR4XO.js → chunk-M4OG2YN4.js} +4 -4
- package/dist/{chunk-UVVVR4XO.js.map → chunk-M4OG2YN4.js.map} +1 -1
- package/dist/{chunk-QOM5Z62G.js → chunk-MAZ3UXJK.js} +17 -24
- package/dist/chunk-MAZ3UXJK.js.map +1 -0
- package/dist/{chunk-Y2TO2U7W.js → chunk-MM4YO3EL.js} +3 -3
- package/dist/{chunk-Y2TO2U7W.js.map → chunk-MM4YO3EL.js.map} +1 -1
- package/dist/{chunk-OULSJRXV.js → chunk-OCQNUV4T.js} +7 -7
- package/dist/{chunk-OULSJRXV.js.map → chunk-OCQNUV4T.js.map} +1 -1
- package/dist/{chunk-SZXGRHQQ.js → chunk-PJJSBZZP.js} +4 -4
- package/dist/{chunk-SZXGRHQQ.js.map → chunk-PJJSBZZP.js.map} +1 -1
- package/dist/{chunk-OAHVKSQJ.js → chunk-PKBLJI23.js} +1471 -126
- package/dist/chunk-PKBLJI23.js.map +1 -0
- package/dist/{chunk-NOA6EJXE.js → chunk-PKE3PXOK.js} +3 -3
- package/dist/{chunk-NOA6EJXE.js.map → chunk-PKE3PXOK.js.map} +1 -1
- package/dist/{chunk-WYHQUCHR.js → chunk-PMWRM4EJ.js} +109 -48
- package/dist/chunk-PMWRM4EJ.js.map +1 -0
- package/dist/{chunk-H6UVIKQ2.js → chunk-PNUM7GUY.js} +3 -3
- package/dist/{chunk-H6UVIKQ2.js.map → chunk-PNUM7GUY.js.map} +1 -1
- package/dist/{chunk-SRODW6M2.js → chunk-Q5RN4PWV.js} +3 -3
- package/dist/{chunk-SRODW6M2.js.map → chunk-Q5RN4PWV.js.map} +1 -1
- package/dist/{chunk-LN6QB7XI.js → chunk-QIHL3YPL.js} +4 -4
- package/dist/{chunk-LN6QB7XI.js.map → chunk-QIHL3YPL.js.map} +1 -1
- package/dist/{chunk-U56GGCC2.js → chunk-QK7AG5AV.js} +4 -4
- package/dist/{chunk-U56GGCC2.js.map → chunk-QK7AG5AV.js.map} +1 -1
- package/dist/{chunk-OTIB4D2K.js → chunk-QPWR3YMY.js} +15 -5
- package/dist/chunk-QPWR3YMY.js.map +1 -0
- package/dist/{chunk-4DWBSTYA.js → chunk-QXKUFROW.js} +22 -5
- package/dist/chunk-QXKUFROW.js.map +1 -0
- package/dist/{chunk-4XWLB7JB.js → chunk-RQMIZONH.js} +95 -35
- package/dist/chunk-RQMIZONH.js.map +1 -0
- package/dist/{chunk-LSI2IVFT.js → chunk-RXGPFAMN.js} +4 -3
- package/dist/chunk-RXGPFAMN.js.map +1 -0
- package/dist/{chunk-3JKGY2YC.js → chunk-S2H3RBK7.js} +182 -33
- package/dist/chunk-S2H3RBK7.js.map +1 -0
- package/dist/{chunk-VJRTDKXS.js → chunk-SJNZ4WPA.js} +165 -106
- package/dist/chunk-SJNZ4WPA.js.map +1 -0
- package/dist/{chunk-KX2PT5Q4.js → chunk-T2VBPVWZ.js} +77 -18
- package/dist/chunk-T2VBPVWZ.js.map +1 -0
- package/dist/{chunk-6Z5KTFJT.js → chunk-TSTRH4SM.js} +3 -3
- package/dist/{chunk-6Z5KTFJT.js.map → chunk-TSTRH4SM.js.map} +1 -1
- package/dist/{chunk-7CV3HFWJ.js → chunk-TZERN27L.js} +5 -5
- package/dist/{chunk-7CV3HFWJ.js.map → chunk-TZERN27L.js.map} +1 -1
- package/dist/{chunk-JDR6ILCW.js → chunk-U673DB5U.js} +4 -4
- package/dist/{chunk-JDR6ILCW.js.map → chunk-U673DB5U.js.map} +1 -1
- package/dist/{chunk-FKU57Q5Q.js → chunk-URCJIV7T.js} +3 -3
- package/dist/{chunk-FKU57Q5Q.js.map → chunk-URCJIV7T.js.map} +1 -1
- package/dist/{chunk-ADPM6AYF.js → chunk-VSX2G7LA.js} +3 -3
- package/dist/{chunk-ADPM6AYF.js.map → chunk-VSX2G7LA.js.map} +1 -1
- package/dist/{chunk-F2GOZMRA.js → chunk-X37JUNEW.js} +49 -49
- package/dist/chunk-X37JUNEW.js.map +1 -0
- package/dist/{chunk-NMI2TWMH.js → chunk-Z3IRZBXB.js} +158 -344
- package/dist/chunk-Z3IRZBXB.js.map +1 -0
- package/dist/{chunk-7TPYPVAH.js → chunk-ZWJCRHP3.js} +3 -3
- package/dist/{chunk-7TPYPVAH.js.map → chunk-ZWJCRHP3.js.map} +1 -1
- package/dist/{claude-code-VJGYBXQR.js → claude-code-BBMNVH2J.js} +5 -5
- package/dist/{claude-code-VJGYBXQR.js.map → claude-code-BBMNVH2J.js.map} +1 -1
- package/dist/cli/index.js +964 -501
- 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/asset-cmd.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 +28 -3
- 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/outdated.d.ts.map +1 -1
- package/dist/cli/src/commands/plugin-registry-cmd.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/rebuild.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 +31 -1
- package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -1
- package/dist/cli/src/commands/serve.d.ts +10 -0
- package/dist/cli/src/commands/serve.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-manifest-publish.d.ts.map +1 -1
- package/dist/cli/src/commands/source-manifest.d.ts +19 -7
- package/dist/cli/src/commands/source-manifest.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-instances.d.ts.map +1 -1
- package/dist/cli/src/commands/validate.d.ts.map +1 -1
- package/dist/cli/src/commands/why.d.ts.map +1 -1
- package/dist/cli/src/config-checks.d.ts +55 -0
- package/dist/cli/src/config-checks.d.ts.map +1 -0
- package/dist/cli/src/harness-error.d.ts +12 -1
- package/dist/cli/src/harness-error.d.ts.map +1 -1
- package/dist/cli/src/helpers.d.ts +18 -8
- package/dist/cli/src/helpers.d.ts.map +1 -1
- package/dist/cli/src/plugin-store/index.d.ts +10 -1
- package/dist/cli/src/plugin-store/index.d.ts.map +1 -1
- package/dist/cli/src/plugin-store/spec.d.ts +8 -0
- package/dist/cli/src/plugin-store/spec.d.ts.map +1 -1
- 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 +23 -23
- package/dist/connectors/src/connector-manager.d.ts.map +1 -1
- package/dist/connectors-47EM6C2P.js +48 -0
- package/dist/{connectors-CXZLXAFZ.js.map → connectors-47EM6C2P.js.map} +1 -1
- package/dist/core/framework.js +1 -1
- package/dist/core/index.js +9 -9
- package/dist/core/manifest.js +2 -2
- package/dist/core/models.js +1 -1
- package/dist/core/provider-registry-credentials.js +2 -2
- package/dist/core/provider-registry.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/framework.d.ts +5 -3
- package/dist/core/src/framework.d.ts.map +1 -1
- package/dist/core/src/index.d.ts +3 -2
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/lock.d.ts +12 -0
- package/dist/core/src/lock.d.ts.map +1 -1
- package/dist/core/src/manifest.d.ts +25 -0
- package/dist/core/src/manifest.d.ts.map +1 -1
- package/dist/core/src/models.d.ts +15 -0
- package/dist/core/src/models.d.ts.map +1 -1
- package/dist/core/src/provider-registry.d.ts +6 -0
- package/dist/core/src/provider-registry.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +18 -1
- 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 +9 -9
- package/dist/discovery/index.js +5 -5
- 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-K5EFNC3L.js} +36 -22
- package/dist/ensure-sources-K5EFNC3L.js.map +1 -0
- package/dist/factory-assets/connectors/box.js +23 -23
- package/dist/factory-assets/connectors/deploy.js +23 -23
- package/dist/factory-assets/connectors/devserver.js +23 -23
- package/dist/factory-assets/connectors/flow/adapter.js +23 -23
- 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 +11 -11
- package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +25 -25
- package/dist/factory-assets/connectors/flow.js +23 -23
- package/dist/factory-assets/connectors/git.js +23 -23
- package/dist/factory-assets/connectors/gmail.js +23 -23
- package/dist/factory-assets/connectors/googledrive.js +23 -23
- package/dist/factory-assets/connectors/local.js +23 -23
- package/dist/factory-assets/connectors/mattermost.js +23 -23
- package/dist/factory-assets/connectors/memory.js +23 -23
- package/dist/factory-assets/connectors/minio.js +23 -23
- package/dist/factory-assets/connectors/postgres.js +23 -23
- package/dist/factory-assets/connectors/s3.js +23 -23
- package/dist/factory-assets/connectors/sharepoint.js +23 -23
- package/dist/factory-assets/connectors/sqlite.js +23 -23
- package/dist/factory-assets/connectors/static-server.js +23 -23
- package/dist/factory-assets/connectors/tunnel.js +23 -23
- package/dist/factory-assets/connectors/webdav.js +23 -23
- package/dist/factory-assets/connectors/xstate-store.js +23 -23
- package/dist/factory-assets/connectors/xstate.js +23 -23
- 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-KLBNSKLO.js +26 -0
- package/dist/{flows-TJQIMGUX.js.map → flows-KLBNSKLO.js.map} +1 -1
- package/dist/helpers-LUSV2ICC.js +52 -0
- package/dist/{helpers-DE53KG3E.js.map → helpers-LUSV2ICC.js.map} +1 -1
- package/dist/library/index.js +14 -14
- package/dist/library/install.js +5 -5
- package/dist/library/src/index.d.ts +2 -2
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/install/index.d.ts +1 -1
- package/dist/library/src/install/index.d.ts.map +1 -1
- package/dist/library/src/install/install-from-manifest.d.ts +13 -0
- package/dist/library/src/install/install-from-manifest.d.ts.map +1 -1
- package/dist/library/src/install/source-fetch-github.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/manifest-writer.d.ts.map +1 -1
- package/dist/library/src/remote/remote-catalog-source.d.ts +8 -0
- package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
- package/dist/library/src/remote/rest-catalog-source.d.ts +3 -2
- package/dist/library/src/remote/rest-catalog-source.d.ts.map +1 -1
- package/dist/{library-publish-KKQ2ULLX.js → library-publish-XMRCKRNR.js} +59 -58
- package/dist/library-publish-XMRCKRNR.js.map +1 -0
- package/dist/model-endpoint/index.js +12 -12
- package/dist/model-endpoint/src/openai-chat.d.ts +7 -2
- package/dist/model-endpoint/src/openai-chat.d.ts.map +1 -1
- package/dist/model-endpoint/src/openai-shared.d.ts +8 -0
- package/dist/model-endpoint/src/openai-shared.d.ts.map +1 -1
- package/dist/model-endpoint/src/server.d.ts.map +1 -1
- package/dist/model-endpoint/src/types.d.ts +1 -1
- package/dist/model-endpoint/src/types.d.ts.map +1 -1
- package/dist/model-endpoint/src/usage-cost.d.ts +23 -0
- package/dist/model-endpoint/src/usage-cost.d.ts.map +1 -0
- package/dist/open-library-XBR46DJL.js +56 -0
- package/dist/{open-library-RJGJIW5H.js.map → open-library-XBR46DJL.js.map} +1 -1
- package/dist/open-registry-W562DCDL.js +32 -0
- package/dist/{open-registry-3QS3J7KO.js.map → open-registry-W562DCDL.js.map} +1 -1
- package/dist/paths-2AZXB3QL.js +22 -0
- package/dist/{paths-QAV4C3KX.js.map → paths-2AZXB3QL.js.map} +1 -1
- package/dist/plugin-store-36SIL4TV.js +23 -0
- package/dist/{plugin-store-MSH6GK6T.js.map → plugin-store-36SIL4TV.js.map} +1 -1
- package/dist/project-D7GN7WLS.js +86 -0
- package/dist/{project-HZW2MZPA.js.map → project-D7GN7WLS.js.map} +1 -1
- package/dist/resolver/index.js +10 -10
- package/dist/runner/index.js +40 -40
- package/dist/runner/src/capability-roundtrip.d.ts +7 -6
- package/dist/runner/src/capability-roundtrip.d.ts.map +1 -1
- 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 +20 -7
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +19 -19
- package/dist/sdk/bridge.js +19 -19
- package/dist/sdk/core.js +9 -9
- package/dist/sdk/flow.js +11 -11
- package/dist/sdk/index.js +40 -40
- package/dist/sdk/resolver.js +10 -10
- package/dist/sdk/runner.js +40 -40
- package/dist/sdk/transport/ws/server.js +10 -10
- package/dist/sdk/transport/ws.js +10 -10
- package/dist/sdk/transport.js +10 -10
- package/dist/serve-preflight-CGA5G3AE.js +135 -0
- package/dist/serve-preflight-CGA5G3AE.js.map +1 -0
- package/dist/{setup-AGYB4PKR.js → setup-IBDNJAOU.js} +32 -32
- package/dist/{setup-AGYB4PKR.js.map → setup-IBDNJAOU.js.map} +1 -1
- package/dist/skills-CTTO7SZK.js +26 -0
- package/dist/{skills-2LMQ2IAD.js.map → skills-CTTO7SZK.js.map} +1 -1
- package/dist/source-2DJ5KNOI.js +85 -0
- package/dist/{source-GBMYD3RO.js.map → source-2DJ5KNOI.js.map} +1 -1
- package/dist/source-fetch-github-RWHV4I5Z.js +4 -0
- package/dist/{source-fetch-github-ST4MFL3D.js.map → source-fetch-github-RWHV4I5Z.js.map} +1 -1
- package/dist/{source-manifest-publish-FK22TVQT.js → source-manifest-publish-IYWUMUCY.js} +55 -57
- package/dist/source-manifest-publish-IYWUMUCY.js.map +1 -0
- package/dist/transport/index.js +10 -10
- package/dist/transport/ws/server.js +10 -10
- package/dist/transport/ws.js +10 -10
- package/dist/tui/index.js +48 -41
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/src/repl.d.ts.map +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/install-manifest.d.ts +1 -1
- package/dist/types/src/install-manifest.d.ts.map +1 -1
- package/dist/types/src/manifests/index.d.ts +1 -1
- package/dist/types/src/manifests/index.d.ts.map +1 -1
- package/dist/types/src/manifests/scaffold-body.d.ts +2 -0
- package/dist/types/src/manifests/scaffold-body.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +30 -30
- package/dist/workspace-plugin/adapters/omp.js +15 -15
- package/dist/workspace-plugin/index.js +2 -2
- 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-7XSAW5OC.js.map +0 -1
- package/dist/chunk-BFMYYNDT.js.map +0 -1
- package/dist/chunk-DUMTLYSO.js.map +0 -1
- package/dist/chunk-E3GPGTW3.js.map +0 -1
- package/dist/chunk-E572OHGY.js.map +0 -1
- package/dist/chunk-F2GOZMRA.js.map +0 -1
- package/dist/chunk-GOCWM7FN.js.map +0 -1
- package/dist/chunk-HK7HMEPT.js.map +0 -1
- package/dist/chunk-KX2PT5Q4.js.map +0 -1
- package/dist/chunk-L4YKW5AY.js.map +0 -1
- package/dist/chunk-LSI2IVFT.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-OTIB4D2K.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-SNEE6UGY.js.map +0 -1
- package/dist/chunk-T2BDID2J.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/chunk-XFIEVEU7.js.map +0 -1
- package/dist/chunk-YGRZNFRO.js.map +0 -1
- package/dist/chunk-Z7ZEZZB6.js.map +0 -1
- package/dist/connectors-CXZLXAFZ.js +0 -48
- package/dist/ensure-sources-UPTHZKGF.js.map +0 -1
- package/dist/flows-TJQIMGUX.js +0 -26
- package/dist/helpers-DE53KG3E.js +0 -38
- package/dist/library-publish-KKQ2ULLX.js.map +0 -1
- package/dist/open-library-RJGJIW5H.js +0 -42
- package/dist/open-registry-3QS3J7KO.js +0 -32
- package/dist/paths-QAV4C3KX.js +0 -22
- package/dist/plugin-store-MSH6GK6T.js +0 -23
- package/dist/project-HZW2MZPA.js +0 -84
- package/dist/serve-preflight-L4POEHNQ.js +0 -108
- package/dist/serve-preflight-L4POEHNQ.js.map +0 -1
- package/dist/skills-2LMQ2IAD.js +0 -26
- package/dist/source-GBMYD3RO.js +0 -85
- package/dist/source-fetch-github-ST4MFL3D.js +0 -4
- package/dist/source-manifest-publish-FK22TVQT.js.map +0 -1
|
@@ -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, lastLibraryName, filesEntryPaths } from './chunk-6CYKTHNT.js';
|
|
2
|
+
import { SEMVER_RE, invalidSemverMessage, flowFileNameError, computeDeterministicHash, readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-MAZ3UXJK.js';
|
|
3
|
+
import { parseFlowDefinition } from './chunk-JRN5QF4C.js';
|
|
4
|
+
import { installTargetOf, readKeptLegacyCopies, writeKeptLegacyCopies, canonicalEntryRef, diffDeployedAgent, deployedAssetDrift, assertNoLibrarySymlinks, deployAll, removeAsset, composedDriftRefs, sourceComposedFiles, cachedComposedFiles, createScaffold, installAgent } from './chunk-S2H3RBK7.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-
|
|
8
|
-
import { isScaffoldBody, isScaffoldDescription } 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-
|
|
10
|
-
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-
|
|
7
|
+
import { harnessTargetSupportsAgents, CODEX_IDENTITY_DIR, agentRenderFiles } from './chunk-COAFAYBS.js';
|
|
8
|
+
import { isScaffoldBody, isScaffoldDescription } from './chunk-76FLBODK.js';
|
|
9
|
+
import { resolveConfiguredHarnessName, FACTORY_SOURCE_URL, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, computeHash, buildLockFile, readLock, assertLockHashes, verifyLock, mergeLockFile, resyncLockSources, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-BRK5QZKI.js';
|
|
10
|
+
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-QPWR3YMY.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-KJSC2ZD6.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-KPKGWXUJ.js';
|
|
16
|
+
import { scanDirectory, parseFrontmatter, parseRequires, dedupeRequirements, readAssetBody } from './chunk-JMIKGAUA.js';
|
|
17
|
+
import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-IZQOO6MN.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,14 @@ function manifestFileHasAssetsArray(libPath) {
|
|
|
847
898
|
return false;
|
|
848
899
|
}
|
|
849
900
|
}
|
|
901
|
+
var libraryAssetCheck;
|
|
902
|
+
function setLibraryAssetCheck(check) {
|
|
903
|
+
libraryAssetCheck = check;
|
|
904
|
+
}
|
|
905
|
+
var renamedEntryLookup;
|
|
906
|
+
function setRenamedEntryLookup(lookup) {
|
|
907
|
+
renamedEntryLookup = lookup;
|
|
908
|
+
}
|
|
850
909
|
function isUnlistedLibraryAsset(entry, libPath) {
|
|
851
910
|
const manifest = loadSkPublishManifest(libPath);
|
|
852
911
|
const listed = manifest?.assets ?? [];
|
|
@@ -1045,6 +1104,13 @@ function parseCanonicalLockKey(key) {
|
|
|
1045
1104
|
};
|
|
1046
1105
|
}
|
|
1047
1106
|
}
|
|
1107
|
+
function labelDiffHeaders(diff, from, to) {
|
|
1108
|
+
const stamp = "\\t(?:\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)? [+-]\\d{4}|[A-Z][a-z]{2} [A-Z][a-z]{2} [ \\d]\\d \\d{2}:\\d{2}:\\d{2} \\d{4})";
|
|
1109
|
+
return diff.replace(
|
|
1110
|
+
new RegExp(`^(---|\\+\\+\\+) (.*?)${stamp}$`, "gm"),
|
|
1111
|
+
(_m, mark, file) => `${mark} ${file} (${mark === "---" ? from : to})`
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1048
1114
|
function softDepName(ref) {
|
|
1049
1115
|
const at = ref.lastIndexOf("@");
|
|
1050
1116
|
const base = at > 0 ? ref.slice(0, at) : ref;
|
|
@@ -1117,7 +1183,9 @@ function relockedRefs(prev, next) {
|
|
|
1117
1183
|
e.library?.version ?? "",
|
|
1118
1184
|
e.library && !e.library.installed ? e.library.name : "",
|
|
1119
1185
|
e.library?.sha256 ?? e.sha256,
|
|
1120
|
-
key.includes("#0.0.0-sha.") ? "" : e.source?.commit ?? ""
|
|
1186
|
+
key.includes("#0.0.0-sha.") ? "" : e.source?.commit ?? "",
|
|
1187
|
+
// The `../` compose folders are part of the lock: `update` moving them is a re-lock.
|
|
1188
|
+
(e.composed ?? []).map((f) => `${f.path}:${f.sha256}`).join("\n")
|
|
1121
1189
|
].join("\0");
|
|
1122
1190
|
const out = [];
|
|
1123
1191
|
for (const [key, entry] of Object.entries(next.assets)) {
|
|
@@ -1133,6 +1201,9 @@ function relockedRefs(prev, next) {
|
|
|
1133
1201
|
}
|
|
1134
1202
|
return out;
|
|
1135
1203
|
}
|
|
1204
|
+
function planPaths(folders, renders, files) {
|
|
1205
|
+
return renders.length > 0 ? { folders: [], files: [], renders } : { folders, files, renders: [] };
|
|
1206
|
+
}
|
|
1136
1207
|
function libraryMoves(prev, next) {
|
|
1137
1208
|
if (!prev) return [];
|
|
1138
1209
|
const out = [];
|
|
@@ -1152,9 +1223,13 @@ function libraryMoves(prev, next) {
|
|
|
1152
1223
|
}
|
|
1153
1224
|
return out;
|
|
1154
1225
|
}
|
|
1226
|
+
function unregisteredLibraryName(origin) {
|
|
1227
|
+
return lastLibraryName(origin.name) ?? origin.name;
|
|
1228
|
+
}
|
|
1155
1229
|
function unregisteredLibraryFix(origin, ref) {
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1230
|
+
const name = unregisteredLibraryName(origin);
|
|
1231
|
+
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>\``;
|
|
1232
|
+
return `register library "${name}" again with ${register}, or drop the dependency with \`skaile remove ${ref}\``;
|
|
1158
1233
|
}
|
|
1159
1234
|
function declaredNoHolderReason(ref) {
|
|
1160
1235
|
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 +1255,7 @@ function libraryCollisionSummary(all) {
|
|
|
1180
1255
|
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
1256
|
}
|
|
1182
1257
|
function lockedLibraryProblem(lookup, origin, ref) {
|
|
1183
|
-
return lookup.state === "dir-missing" ? missingLibraryDirMessage({ name: origin.name, path: lookup.path }) : `library "${origin
|
|
1258
|
+
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
1259
|
}
|
|
1185
1260
|
function gapIdentity(ref) {
|
|
1186
1261
|
const p = parseRefTolerant(ref);
|
|
@@ -1242,7 +1317,6 @@ function isSymlink(p) {
|
|
|
1242
1317
|
return false;
|
|
1243
1318
|
}
|
|
1244
1319
|
}
|
|
1245
|
-
var FACTORY_SOURCE_URL = "https://github.com/skaile-ai/ai-assets";
|
|
1246
1320
|
function resolveFactoryAssetsDir() {
|
|
1247
1321
|
for (const dir of factoryAssetRoots()) {
|
|
1248
1322
|
if (existsSync(join(dir, "skaile.manifest.yaml"))) return dir;
|
|
@@ -1429,6 +1503,43 @@ var AssetManager = class {
|
|
|
1429
1503
|
return !recorded || recorded.includes(target);
|
|
1430
1504
|
});
|
|
1431
1505
|
}
|
|
1506
|
+
/**
|
|
1507
|
+
* A 5.12.0 global record has no `global_asset_harnesses:` key, yet `add --global --harness`
|
|
1508
|
+
* wrote one copy only. On the first global `install`, an entry with exactly one existing copy
|
|
1509
|
+
* among several default harnesses is that harness's subset (OQ6): record it, so the install
|
|
1510
|
+
* does not fan the asset out to harnesses the user never asked for (p1442). A copy under the
|
|
1511
|
+
* 5.12.0 omp folder (`~/.omp/skills`) counts as an omp copy. Nothing is inferred once the key
|
|
1512
|
+
* exists (an entry without a row then means "every harness"), with one default harness, or
|
|
1513
|
+
* with no copy yet. Global mode only; returns the rows written.
|
|
1514
|
+
*/
|
|
1515
|
+
inferLegacyHarnessSubsets(entries) {
|
|
1516
|
+
if (!this.global) return [];
|
|
1517
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
1518
|
+
if (!existsSync(configPath)) return [];
|
|
1519
|
+
let raw;
|
|
1520
|
+
try {
|
|
1521
|
+
raw = parse(readFileSync(configPath, "utf8"));
|
|
1522
|
+
} catch {
|
|
1523
|
+
return [];
|
|
1524
|
+
}
|
|
1525
|
+
if (!raw || typeof raw !== "object" || ENTRY_HARNESSES_KEY in raw) return [];
|
|
1526
|
+
const all = this.defaultGlobalHarnesses();
|
|
1527
|
+
if (all.length < 2) return [];
|
|
1528
|
+
const rows = [];
|
|
1529
|
+
for (const e of entries) {
|
|
1530
|
+
const component = this.installTarget(e.kind);
|
|
1531
|
+
if (!component || !e.name) continue;
|
|
1532
|
+
const holding = all.filter(
|
|
1533
|
+
(h) => isDeployed(component, e.name, h, true) || h === "omp" && legacyOmpPaths(component, e.name).length > 0
|
|
1534
|
+
);
|
|
1535
|
+
if (holding.length !== 1) continue;
|
|
1536
|
+
const publisher = e.publisher?.replace(/^@/, "");
|
|
1537
|
+
const ref = `${e.kind}:${publisher ? `@${publisher}/` : ""}${e.name}`;
|
|
1538
|
+
if (this.setEntryHarnessRow(raw, ref, holding)) rows.push({ ref, harnesses: holding });
|
|
1539
|
+
}
|
|
1540
|
+
if (rows.length > 0) writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
1541
|
+
return rows;
|
|
1542
|
+
}
|
|
1432
1543
|
/**
|
|
1433
1544
|
* Set (or, with `harnesses` undefined, clear) the harness row of `ref` in the parsed
|
|
1434
1545
|
* `~/.skaile/skaile.yaml` `raw`. Returns whether `raw` changed.
|
|
@@ -1456,6 +1567,78 @@ var AssetManager = class {
|
|
|
1456
1567
|
else raw[ENTRY_HARNESSES_KEY] = rows;
|
|
1457
1568
|
return true;
|
|
1458
1569
|
}
|
|
1570
|
+
/** The harness row of `kind:name` (publisher must agree when both sides have one) in the parsed `raw`. */
|
|
1571
|
+
harnessRowIn(raw, id) {
|
|
1572
|
+
const wantPub = id.publisher?.replace(/^@/, "");
|
|
1573
|
+
for (const [key, harnesses] of Object.entries(decodeEntryHarnesses(raw[ENTRY_HARNESSES_KEY]))) {
|
|
1574
|
+
const other = parseAssetRefLoose(key);
|
|
1575
|
+
if (!other || other.kind !== id.kind || other.name !== id.name) continue;
|
|
1576
|
+
const havePub = other.publisher?.replace(/^@/, "");
|
|
1577
|
+
if (wantPub === void 0 || havePub === void 0 || havePub === wantPub) return harnesses;
|
|
1578
|
+
}
|
|
1579
|
+
return void 0;
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* The harness row `add --global --harness <requested>` leaves for `ref`: the recorded set plus
|
|
1583
|
+
* `requested`. An asset declared with no row goes to every global harness, so its set is that
|
|
1584
|
+
* default; `undefined` (no row) when `requested` is already part of it. A new asset gets
|
|
1585
|
+
* `requested` alone.
|
|
1586
|
+
*/
|
|
1587
|
+
widenedHarnessRow(raw, ref, requested, declared) {
|
|
1588
|
+
const id = parseAssetRefLoose(ref);
|
|
1589
|
+
if (!id) return [...requested];
|
|
1590
|
+
const recorded = this.harnessRowIn(raw, id);
|
|
1591
|
+
if (!recorded && !declared) return [...requested];
|
|
1592
|
+
const current = recorded ?? this.defaultGlobalHarnesses();
|
|
1593
|
+
const union = [.../* @__PURE__ */ new Set([...current, ...requested])];
|
|
1594
|
+
if (!recorded && union.length === current.length) return void 0;
|
|
1595
|
+
return union;
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* After `add --global --harness X`: widen the row of every other entry of the add that has one
|
|
1599
|
+
* (a requirement declared on its own with `--harness`), so the copy now deployed into X stays
|
|
1600
|
+
* managed. Entries with no row already go to every harness.
|
|
1601
|
+
*/
|
|
1602
|
+
widenDependencyRows(entries, requested) {
|
|
1603
|
+
const named = this.harnessesOverride;
|
|
1604
|
+
if (!this.global || !named?.length) return;
|
|
1605
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
1606
|
+
if (!existsSync(configPath)) return;
|
|
1607
|
+
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
1608
|
+
let changed = false;
|
|
1609
|
+
for (const e of entries) {
|
|
1610
|
+
if (!e.name || e.kind === requested.kind && e.name === requested.name) continue;
|
|
1611
|
+
const recorded = this.harnessRowIn(raw, e);
|
|
1612
|
+
if (!recorded) continue;
|
|
1613
|
+
const union = [.../* @__PURE__ */ new Set([...recorded, ...named])];
|
|
1614
|
+
if (union.length === recorded.length) continue;
|
|
1615
|
+
const publisher = e.publisher?.replace(/^@/, "");
|
|
1616
|
+
const ref = `${e.kind}:${publisher ? `@${publisher}/` : ""}${e.name}`;
|
|
1617
|
+
if (this.setEntryHarnessRow(raw, ref, union)) changed = true;
|
|
1618
|
+
}
|
|
1619
|
+
if (changed) writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* `add --global --harness X --only`: name each harness that leaves the asset's set but still
|
|
1623
|
+
* holds a copy. The copy stays on disk; `remove --global --harness <h>` deletes it.
|
|
1624
|
+
*/
|
|
1625
|
+
reportDroppedCopies(target, onDropped) {
|
|
1626
|
+
const named = this.harnessesOverride;
|
|
1627
|
+
const component = this.installTarget(target.kind);
|
|
1628
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
1629
|
+
if (!named?.length || !component || !existsSync(configPath)) return;
|
|
1630
|
+
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
1631
|
+
const publisher = target.publisher?.replace(/^@/, "");
|
|
1632
|
+
const ref = `${target.kind}:${publisher ? `@${publisher}/` : ""}${target.name}`;
|
|
1633
|
+
const declared = Array.isArray(raw.dependencies) && raw.dependencies.some((d) => this.sameDeclaredAsset(d, ref));
|
|
1634
|
+
if (!declared) return;
|
|
1635
|
+
const current = this.harnessRowIn(raw, target) ?? this.defaultGlobalHarnesses();
|
|
1636
|
+
for (const h of current) {
|
|
1637
|
+
if (named.includes(h) || !isDeployed(component, target.name, h, true)) continue;
|
|
1638
|
+
const where = deployedPaths(component, target.name, h, true)[0];
|
|
1639
|
+
onDropped(ref, h, where ?? deployedDir(component, target.name, h, true));
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1459
1642
|
/**
|
|
1460
1643
|
* Delete the copies a 5.12.0 global install left in `~/.omp/skills`, `~/.omp/agents`,
|
|
1461
1644
|
* `~/.omp/prompts` and `~/.omp/contracts` (omp reads `~/.omp/agent/<dir>`) for the assets just
|
|
@@ -1556,16 +1739,36 @@ var AssetManager = class {
|
|
|
1556
1739
|
}
|
|
1557
1740
|
/** Deploy options for one harness. In global mode, `harness` is the
|
|
1558
1741
|
* fan-out target (not the constructor's `harness`, which is project-only). */
|
|
1559
|
-
deployOptsFor(target, link, onSecretFile) {
|
|
1742
|
+
deployOptsFor(target, link, onSecretFile, composed) {
|
|
1743
|
+
const records = this.global ? void 0 : this.composedRecords();
|
|
1560
1744
|
return {
|
|
1561
1745
|
registry: this.registry,
|
|
1562
1746
|
harness: target,
|
|
1563
1747
|
global: this.global,
|
|
1564
1748
|
cwd: this.projectDir,
|
|
1565
1749
|
link: link === true,
|
|
1566
|
-
...onSecretFile ? { onSecretFile: (entry, p) => onSecretFile(canonicalEntryRef(entry), p) } : {}
|
|
1750
|
+
...onSecretFile ? { onSecretFile: (entry, p) => onSecretFile(canonicalEntryRef(entry), p) } : {},
|
|
1751
|
+
...records ? { lockedComposed: (agent) => records.get(agent) } : {},
|
|
1752
|
+
...composed ? {
|
|
1753
|
+
refreshComposed: composed.refresh,
|
|
1754
|
+
onComposedDrift: (entry, ref) => composed.onDrift(canonicalEntryRef(entry), ref)
|
|
1755
|
+
} : {}
|
|
1567
1756
|
};
|
|
1568
1757
|
}
|
|
1758
|
+
/** Agent name → the `../` compose folder files its lock entry records (`LockEntry.composed`). */
|
|
1759
|
+
composedRecords() {
|
|
1760
|
+
const out = /* @__PURE__ */ new Map();
|
|
1761
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
1762
|
+
if (!entry.composed?.length) continue;
|
|
1763
|
+
const { name } = parseCanonicalLockKey(key);
|
|
1764
|
+
if (name) out.set(name, entry.composed);
|
|
1765
|
+
}
|
|
1766
|
+
return out;
|
|
1767
|
+
}
|
|
1768
|
+
/** `install`'s `Missing:` line for a `../` compose folder the library moved past the lock. */
|
|
1769
|
+
composedDriftLine(agentRef, composeRef) {
|
|
1770
|
+
return `${agentRef} (composes ${composeRef}: the library's copy changed since the lock; \`skaile update\` moves the lock to it)`;
|
|
1771
|
+
}
|
|
1569
1772
|
/**
|
|
1570
1773
|
* Names of the `mcp-server` assets the lock holds (the declaration ids, unscoped); a missing
|
|
1571
1774
|
* `.mcp.json` entry of one counts as drift even before skaile owned it.
|
|
@@ -1766,7 +1969,16 @@ var AssetManager = class {
|
|
|
1766
1969
|
for (const [slug, decl] of Object.entries(repositories)) {
|
|
1767
1970
|
const localPath = this.resolveRepoDir(decl, slug);
|
|
1768
1971
|
if (!localPath || !existsSync(localPath)) continue;
|
|
1769
|
-
|
|
1972
|
+
if (decl.url && normalizeRepoUrl(decl.url) !== normalizeRepoUrl(FACTORY_SOURCE_URL) && !existsSync(join(localPath, ".git"))) {
|
|
1973
|
+
this.reportSourceError(
|
|
1974
|
+
{ localPath, sourceUrl: decl.url, commit: "" },
|
|
1975
|
+
new Error(
|
|
1976
|
+
`${decl.url}: its clone ${localPath} is not a git repository, so git pull fails - run \`skaile source sync\` to clone it again`
|
|
1977
|
+
)
|
|
1978
|
+
);
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const commit = decl.factory ? "0".repeat(40) : getRepoCommit(localPath) ?? "0".repeat(40);
|
|
1770
1982
|
clones.push({
|
|
1771
1983
|
localPath,
|
|
1772
1984
|
sourceUrl: decl.url ?? localPath,
|
|
@@ -2089,6 +2301,33 @@ var AssetManager = class {
|
|
|
2089
2301
|
if (!scan) return [];
|
|
2090
2302
|
return [...scan.byKey.values()].flat();
|
|
2091
2303
|
}
|
|
2304
|
+
/**
|
|
2305
|
+
* The version `add` deploys for each asset of a source clone registered as a `reader`
|
|
2306
|
+
* library, when its file declares none: the repo commit pseudo-version (p855). `search`
|
|
2307
|
+
* reads this where no project is in scope, so it lists what `add` locks, not the index
|
|
2308
|
+
* default.
|
|
2309
|
+
*
|
|
2310
|
+
* @returns `<library>\0<kind>:@<publisher>/<name>` -> version; only assets that declare none.
|
|
2311
|
+
*/
|
|
2312
|
+
async sourceAssetVersions() {
|
|
2313
|
+
const out = /* @__PURE__ */ new Map();
|
|
2314
|
+
const scan = await this.scanLibraries();
|
|
2315
|
+
if (!scan) return out;
|
|
2316
|
+
const snap = await this.loadLibraryIndex();
|
|
2317
|
+
for (const [key, candidates] of scan.byKey) {
|
|
2318
|
+
for (const c of candidates) {
|
|
2319
|
+
if (c.entry.version) continue;
|
|
2320
|
+
const lib = snap?.libraries.find((l) => l.name === c.library);
|
|
2321
|
+
if (lib?.ownership !== "reader" || !("url" in lib.backendConfig)) continue;
|
|
2322
|
+
const manifestVersion = repoManifestVersion(lib.path);
|
|
2323
|
+
const commit = getRepoCommit(lib.path);
|
|
2324
|
+
if (manifestVersion) out.set(`${c.library}\0${key}`, manifestVersion);
|
|
2325
|
+
else if (commit)
|
|
2326
|
+
out.set(`${c.library}\0${key}`, `${PSEUDO_VERSION_PREFIX}${commit.slice(0, 7)}`);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
return out;
|
|
2330
|
+
}
|
|
2092
2331
|
/**
|
|
2093
2332
|
* Every origin that holds `ref` (`kind:@publisher/name`) with the version it holds, in
|
|
2094
2333
|
* the order `add <ref>` prefers them, so the first row is the one `add` deploys: the
|
|
@@ -2598,7 +2837,14 @@ var AssetManager = class {
|
|
|
2598
2837
|
if (!existsSync(indexPath)) return null;
|
|
2599
2838
|
const index = new LocalIndex({ indexPath });
|
|
2600
2839
|
try {
|
|
2601
|
-
|
|
2840
|
+
const libraries = await index.listSources();
|
|
2841
|
+
const defs = await index.listAssetDefs();
|
|
2842
|
+
const hidden = this.undeclaredSourceClones(libraries);
|
|
2843
|
+
if (hidden.size === 0) return { libraries, defs };
|
|
2844
|
+
return {
|
|
2845
|
+
libraries: libraries.filter((l) => !hidden.has(l.id)),
|
|
2846
|
+
defs: defs.filter((d) => !d.libraryId || !hidden.has(d.libraryId))
|
|
2847
|
+
};
|
|
2602
2848
|
} finally {
|
|
2603
2849
|
index.close();
|
|
2604
2850
|
}
|
|
@@ -2606,6 +2852,32 @@ var AssetManager = class {
|
|
|
2606
2852
|
return null;
|
|
2607
2853
|
}
|
|
2608
2854
|
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Ids of the registered libraries that are the clone of a source this project does not
|
|
2857
|
+
* declare. `source add` registers the clone as a machine-wide reader library so `search`
|
|
2858
|
+
* and the indexer can read it, but a `sources:` clone belongs to the projects that declare
|
|
2859
|
+
* it (OQ12, p321): another project neither resolves it, nor ties with it by name, nor has
|
|
2860
|
+
* `update` re-pin its locked assets to it. A reader library the user registered by hand
|
|
2861
|
+
* (`library init --reader --git`) lives outside the sources cache and is never hidden.
|
|
2862
|
+
*/
|
|
2863
|
+
undeclaredSourceClones(libraries) {
|
|
2864
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
2865
|
+
if (!this.global && !findWorkspaceRoot(this.projectDir)) return hidden;
|
|
2866
|
+
let declared;
|
|
2867
|
+
try {
|
|
2868
|
+
declared = new Set(this.loadConfig().sources.map((s) => sourceCacheKey(s.url)));
|
|
2869
|
+
} catch {
|
|
2870
|
+
return hidden;
|
|
2871
|
+
}
|
|
2872
|
+
const cloneRoots = [join(globalCacheRoot(), "sources"), this.reposDir].map((d) => resolve(d));
|
|
2873
|
+
for (const l of libraries) {
|
|
2874
|
+
if (l.ownership !== "reader" || !("url" in l.backendConfig)) continue;
|
|
2875
|
+
const at = resolve(l.path);
|
|
2876
|
+
if (!cloneRoots.some((root) => at.startsWith(root + sep))) continue;
|
|
2877
|
+
if (!declared.has(sourceCacheKey(l.backendConfig.url))) hidden.add(l.id);
|
|
2878
|
+
}
|
|
2879
|
+
return hidden;
|
|
2880
|
+
}
|
|
2609
2881
|
/**
|
|
2610
2882
|
* Library-origin lock entries for the assets {@link resolveLibraryEntries}
|
|
2611
2883
|
* matched. The locked version is the one actually deployed: the on-disk
|
|
@@ -2749,14 +3021,19 @@ var AssetManager = class {
|
|
|
2749
3021
|
* `install --locked`. An entry is restored only when its library is still
|
|
2750
3022
|
* registered/on disk, still holds the asset, and still offers the locked
|
|
2751
3023
|
* version; anything else is reported in `missing` with the reason.
|
|
3024
|
+
*
|
|
3025
|
+
* @param opts.storeFallback - `install --locked` only: an entry whose library is not registered
|
|
3026
|
+
* here is restored from a store that serves the locked sha256 (OQ15, p1490). Reads such as
|
|
3027
|
+
* the drift check never ask a store.
|
|
2752
3028
|
*/
|
|
2753
|
-
async restoreLibraryLockEntries(lock) {
|
|
3029
|
+
async restoreLibraryLockEntries(lock, opts) {
|
|
2754
3030
|
const entries = [];
|
|
2755
3031
|
const missing = [];
|
|
3032
|
+
const viaStore = [];
|
|
2756
3033
|
const locked = Object.entries(lock.assets).filter(
|
|
2757
3034
|
([, e]) => e.library !== void 0 && !e.library.installed
|
|
2758
3035
|
);
|
|
2759
|
-
if (locked.length === 0) return { entries, missing };
|
|
3036
|
+
if (locked.length === 0) return { entries, missing, viaStore };
|
|
2760
3037
|
const snap = await this.loadLibraryIndex();
|
|
2761
3038
|
const diskHashes = /* @__PURE__ */ new Map();
|
|
2762
3039
|
const fileSets = /* @__PURE__ */ new Map();
|
|
@@ -2769,6 +3046,12 @@ var AssetManager = class {
|
|
|
2769
3046
|
}
|
|
2770
3047
|
const lookup = this.lockedLibrary(origin, snap, { kind, publisher, name });
|
|
2771
3048
|
if (lookup.state !== "ok") {
|
|
3049
|
+
const fallback = lookup.state === "unregistered" && opts?.storeFallback ? await this.storeEntryForLockedLibraryAsset(publisher, kind, name, origin) : null;
|
|
3050
|
+
if (fallback) {
|
|
3051
|
+
entries.push(fallback);
|
|
3052
|
+
viaStore.push(ref);
|
|
3053
|
+
continue;
|
|
3054
|
+
}
|
|
2772
3055
|
missing.push(
|
|
2773
3056
|
`${ref} (${lockedLibraryProblem(lookup, origin, `${kind}:@${publisher}/${name}`)})`
|
|
2774
3057
|
);
|
|
@@ -2811,22 +3094,94 @@ var AssetManager = class {
|
|
|
2811
3094
|
found.version = sameVersion ? origin.version : current;
|
|
2812
3095
|
entries.push(withLibraryFiles(found, libPath, fileSets, this.registry));
|
|
2813
3096
|
}
|
|
2814
|
-
return { entries, missing };
|
|
3097
|
+
return { entries, missing, viaStore };
|
|
3098
|
+
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Whether a library-origin lock entry was restored from its lock url: its library is not
|
|
3101
|
+
* registered here, and the project's store cache holds the locked version (`install --locked`
|
|
3102
|
+
* fetched and SHA-verified it there against the sha256 the lock recorded). Such an entry is
|
|
3103
|
+
* as restorable as a source- or store-origin one: `check` and `doctor` read its requirements
|
|
3104
|
+
* from the deployed copy and do not ask for the library (OQ8, p1655). An unregistered library
|
|
3105
|
+
* that never restored anything here stays a gap.
|
|
3106
|
+
*/
|
|
3107
|
+
async restoredFromLockUrl(key, entry, snap) {
|
|
3108
|
+
const origin = entry.library;
|
|
3109
|
+
if (!origin || origin.installed || !origin.sha256 || !origin.version) return false;
|
|
3110
|
+
if (isPseudoVersion(origin.version)) return false;
|
|
3111
|
+
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
3112
|
+
if (!publisher || !kind || !name) return false;
|
|
3113
|
+
if (this.lockedLibrary(origin, snap, { kind, publisher, name }).state !== "unregistered") {
|
|
3114
|
+
return false;
|
|
3115
|
+
}
|
|
3116
|
+
try {
|
|
3117
|
+
return readdirSync(join(this.storeCacheDir, publisher, kind, name, origin.version)).length > 0;
|
|
3118
|
+
} catch {
|
|
3119
|
+
return false;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
/**
|
|
3123
|
+
* The store's copy of a library-origin lock entry whose library is not registered here: a
|
|
3124
|
+
* store (project `stores:` or the global catalog) that serves `kind:@publisher/name#version`
|
|
3125
|
+
* under the very sha256 the lock recorded holds the same bytes. The entry is fetched and
|
|
3126
|
+
* verified like any store asset; the lock stays as it is. `null` when the lock has no hash,
|
|
3127
|
+
* no store answers, or the hash differs (OQ15, p1490).
|
|
3128
|
+
*/
|
|
3129
|
+
async storeEntryForLockedLibraryAsset(publisher, kind, name, origin) {
|
|
3130
|
+
if (!origin.sha256 || !origin.version || isPseudoVersion(origin.version)) return null;
|
|
3131
|
+
const { repositories, stores: projectStores } = this.loadConfig();
|
|
3132
|
+
const stores = [...projectStores, ...await this.globalStoreFallback(projectStores)];
|
|
3133
|
+
const fetcher = this.storeFetcherFor(stores);
|
|
3134
|
+
if (!fetcher) return null;
|
|
3135
|
+
for (const store of stores) {
|
|
3136
|
+
try {
|
|
3137
|
+
const m = await fetcher.getInstallManifest(
|
|
3138
|
+
store.url,
|
|
3139
|
+
`${kind}:@${publisher}/${name}#${origin.version}`
|
|
3140
|
+
);
|
|
3141
|
+
if (!m || m.sha256 !== origin.sha256) continue;
|
|
3142
|
+
const [entry] = await this.buildCatalogEntries(
|
|
3143
|
+
[
|
|
3144
|
+
{
|
|
3145
|
+
publisher,
|
|
3146
|
+
kind,
|
|
3147
|
+
name,
|
|
3148
|
+
version: origin.version,
|
|
3149
|
+
sourceUrl: m.sourceUrl,
|
|
3150
|
+
commit: m.commit,
|
|
3151
|
+
files: m.files,
|
|
3152
|
+
sha256: m.sha256,
|
|
3153
|
+
...m.deps?.length ? { deps: m.deps, storeRequires: m.deps } : {}
|
|
3154
|
+
}
|
|
3155
|
+
],
|
|
3156
|
+
repositories
|
|
3157
|
+
);
|
|
3158
|
+
if (entry) return entry;
|
|
3159
|
+
} catch (err) {
|
|
3160
|
+
this.noteStoreFailure(store.url, err);
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
return null;
|
|
2815
3164
|
}
|
|
2816
3165
|
/**
|
|
2817
3166
|
* Whether a deployed copy exists for the lock entries `install` kept for the unresolved
|
|
2818
|
-
* `ref`. A kind with no install target has
|
|
3167
|
+
* `ref`. A kind with no install target has none, except an `mcp-server`: its copy is the
|
|
3168
|
+
* `.mcp.json` entry skaile wrote and the payload that entry points into (p792).
|
|
2819
3169
|
*/
|
|
2820
|
-
keptCopyDeployed(kept, ref) {
|
|
3170
|
+
async keptCopyDeployed(kept, ref) {
|
|
2821
3171
|
const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
|
|
2822
3172
|
if (!want) return false;
|
|
2823
|
-
|
|
3173
|
+
for (const key of Object.keys(kept.assets)) {
|
|
2824
3174
|
const id = parseCanonicalLockKey(key);
|
|
2825
|
-
if (id.kind !== want.kind || id.name !== want.name)
|
|
2826
|
-
if (want.publisher && id.publisher !== want.publisher)
|
|
3175
|
+
if (id.kind !== want.kind || id.name !== want.name) continue;
|
|
3176
|
+
if (want.publisher && id.publisher !== want.publisher) continue;
|
|
2827
3177
|
const component = this.installTarget(id.kind);
|
|
2828
|
-
|
|
2829
|
-
|
|
3178
|
+
if (component !== void 0) {
|
|
3179
|
+
if (isDeployed(component, id.name, this.harness, this.global, this.projectDir)) return true;
|
|
3180
|
+
} else if (id.kind === "mcp-server" && !this.global && await mcpCommittedRenderIntact(this.projectDir, this.harness, id.name)) {
|
|
3181
|
+
return true;
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
return false;
|
|
2830
3185
|
}
|
|
2831
3186
|
/** Whether the deployed copy of a library asset is on disk; a kind with no install target has none to check. */
|
|
2832
3187
|
lockedCopyDeployed(kind, name) {
|
|
@@ -2949,6 +3304,24 @@ var AssetManager = class {
|
|
|
2949
3304
|
lockedRefsNamed(kind, name) {
|
|
2950
3305
|
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
3306
|
}
|
|
3307
|
+
/**
|
|
3308
|
+
* The library a library-origin lock entry of `kind` and `name` (and `publisher`, when given)
|
|
3309
|
+
* resolves to on this machine, or `undefined` when the lock holds no such entry, it is not
|
|
3310
|
+
* from a library, or its library is not here. `info` prints it next to `source:`.
|
|
3311
|
+
*/
|
|
3312
|
+
async lockedLibraryOf(kind, name, publisher) {
|
|
3313
|
+
const bare = publisher?.replace(/^@/, "");
|
|
3314
|
+
const names = await this.lockedLibraryNames();
|
|
3315
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3316
|
+
if (!entry.library || entry.library.installed) continue;
|
|
3317
|
+
const id = parseCanonicalLockKey(key);
|
|
3318
|
+
if (id.kind !== kind || id.name !== name || !id.publisher) continue;
|
|
3319
|
+
if (bare && id.publisher !== bare) continue;
|
|
3320
|
+
const lib = names.get(`${kind}:${id.publisher}/${name}`);
|
|
3321
|
+
if (lib) return lib;
|
|
3322
|
+
}
|
|
3323
|
+
return void 0;
|
|
3324
|
+
}
|
|
2952
3325
|
/**
|
|
2953
3326
|
* Where each library-origin lock entry of this project resolves on this machine: identity
|
|
2954
3327
|
* `kind:publisher/name` (no scope sigil, no version) → the name the locked library is
|
|
@@ -2991,6 +3364,88 @@ var AssetManager = class {
|
|
|
2991
3364
|
}
|
|
2992
3365
|
return { libraries: await this.lockedLibraryNames(), shas };
|
|
2993
3366
|
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Canonical refs of the locked source assets that share a source URL with `resolved` but
|
|
3369
|
+
* are locked at another commit than the one `resolved` carries for it. Assets in `resolved`
|
|
3370
|
+
* and library-origin entries are skipped.
|
|
3371
|
+
*/
|
|
3372
|
+
staleSourceSiblingRefs(resolved, declared = []) {
|
|
3373
|
+
const pinOf = /* @__PURE__ */ new Map();
|
|
3374
|
+
for (const d of declared) {
|
|
3375
|
+
const id = refIdentity(d);
|
|
3376
|
+
const pin = parseAssetRefLoose(d)?.pin;
|
|
3377
|
+
if (id !== null && pin) pinOf.set(id.replace(/^@/, ""), pin);
|
|
3378
|
+
}
|
|
3379
|
+
const commitOf = /* @__PURE__ */ new Map();
|
|
3380
|
+
const held = /* @__PURE__ */ new Set();
|
|
3381
|
+
for (const c of resolved) {
|
|
3382
|
+
if (!c.sourceUrl || !c.commit) continue;
|
|
3383
|
+
commitOf.set(normalizeRepoUrl(c.sourceUrl), c.commit);
|
|
3384
|
+
held.add(`${c.publisher.replace(/^@/, "")}/${c.kind}:${c.name}`);
|
|
3385
|
+
}
|
|
3386
|
+
const out = [];
|
|
3387
|
+
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3388
|
+
const src = entry.source;
|
|
3389
|
+
if (!src?.url || entry.library) continue;
|
|
3390
|
+
const commit = commitOf.get(normalizeRepoUrl(src.url));
|
|
3391
|
+
if (!commit || src.commit === commit) continue;
|
|
3392
|
+
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
3393
|
+
if (!publisher || !kind || !name || held.has(`${publisher}/${kind}:${name}`)) continue;
|
|
3394
|
+
const pin = pinOf.get(`${publisher}/${kind}:${name}`);
|
|
3395
|
+
out.push(`${kind}:@${publisher}/${name}${pin ? `#${pin}` : ""}`);
|
|
3396
|
+
}
|
|
3397
|
+
return out;
|
|
3398
|
+
}
|
|
3399
|
+
/**
|
|
3400
|
+
* The lock entries that came from a source or the store (a `source:`, no `library:`), by
|
|
3401
|
+
* identity `<publisher>/<kind>:<name>` -> locked version. The lock's origin
|
|
3402
|
+
* wins: a library registered later that holds the same ref does not take such an asset
|
|
3403
|
+
* (`update`, `install`, `add`; OQ15, p1490).
|
|
3404
|
+
*/
|
|
3405
|
+
lockedOriginVersions() {
|
|
3406
|
+
const out = /* @__PURE__ */ new Map();
|
|
3407
|
+
for (const [key, e] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
3408
|
+
if (!e.source || e.library) continue;
|
|
3409
|
+
const { publisher, kind, name, version } = parseCanonicalLockKey(key);
|
|
3410
|
+
if (publisher && kind && name) out.set(`${publisher}/${kind}:${name}`, version ?? "");
|
|
3411
|
+
}
|
|
3412
|
+
return out;
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Say which registered library holds a ref the lock keeps on its source or the store: the
|
|
3416
|
+
* candidate that lost, with the way to switch (p1490). Printed once per `add`.
|
|
3417
|
+
*/
|
|
3418
|
+
async noteLosingLibraries(refs) {
|
|
3419
|
+
const lock = this.tryReadLock();
|
|
3420
|
+
for (const ref of refs) {
|
|
3421
|
+
const plain = ref.split("#")[0] ?? ref;
|
|
3422
|
+
let hit;
|
|
3423
|
+
try {
|
|
3424
|
+
hit = await this.resolveLibraryEntries([plain], { quiet: true });
|
|
3425
|
+
} catch {
|
|
3426
|
+
continue;
|
|
3427
|
+
}
|
|
3428
|
+
const entry = hit.entries[0];
|
|
3429
|
+
const library = entry ? hit.libraryOf.get(entry) : void 0;
|
|
3430
|
+
if (!entry || !library) continue;
|
|
3431
|
+
const canonical = canonicalEntryRef(entry);
|
|
3432
|
+
const plainCanonical = canonical.split("#")[0] ?? canonical;
|
|
3433
|
+
const lockedUrl = Object.entries(lock?.assets ?? {}).find(([k, e]) => {
|
|
3434
|
+
const id = parseCanonicalLockKey(k);
|
|
3435
|
+
return e.source && !e.library && `${id.kind}:@${id.publisher}/${id.name}` === plainCanonical;
|
|
3436
|
+
})?.[1].source?.url;
|
|
3437
|
+
if (lockedUrl) {
|
|
3438
|
+
const ownClone = (await this.loadLibraryIndex())?.libraries.some(
|
|
3439
|
+
(l) => l.name === library && l.ownership === "reader" && "url" in l.backendConfig && sourceCacheKey(l.backendConfig.url) === sourceCacheKey(lockedUrl)
|
|
3440
|
+
);
|
|
3441
|
+
if (ownClone) continue;
|
|
3442
|
+
}
|
|
3443
|
+
this.emitLibraryCollision(
|
|
3444
|
+
`${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}\`.`,
|
|
3445
|
+
[plainCanonical]
|
|
3446
|
+
);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
2994
3449
|
// ── Store fetch-then-deploy ────────────────────────────────────────────
|
|
2995
3450
|
/** A candidate's stable identity key (`<publisher>/<kind>:<name>@<version>`). */
|
|
2996
3451
|
candidateKey(c) {
|
|
@@ -3150,10 +3605,19 @@ var AssetManager = class {
|
|
|
3150
3605
|
fresh.push(ref);
|
|
3151
3606
|
}
|
|
3152
3607
|
if (fresh.length === 0) break;
|
|
3608
|
+
const keepsOrigin = (ref) => {
|
|
3609
|
+
const id = refIdentity(ref);
|
|
3610
|
+
const locked = id !== null ? opts.lockedOrigin?.get(id) : void 0;
|
|
3611
|
+
if (locked === void 0) return false;
|
|
3612
|
+
const pin = ref.includes("#") ? ref.slice(ref.indexOf("#") + 1).trim() : void 0;
|
|
3613
|
+
return !pin || locked === "" || matchPin(pin, locked);
|
|
3614
|
+
};
|
|
3615
|
+
const originKept = fresh.filter(keepsOrigin);
|
|
3616
|
+
if (opts.warnCollisions && originKept.length > 0) await this.noteLosingLibraries(originKept);
|
|
3153
3617
|
const libraryTried = new Set(
|
|
3154
3618
|
fresh.filter((ref) => {
|
|
3155
3619
|
const id = refIdentity(ref);
|
|
3156
|
-
return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0;
|
|
3620
|
+
return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0 && !keepsOrigin(ref);
|
|
3157
3621
|
})
|
|
3158
3622
|
);
|
|
3159
3623
|
const hit = await this.resolveLibraryEntries([...libraryTried], {
|
|
@@ -3181,7 +3645,7 @@ var AssetManager = class {
|
|
|
3181
3645
|
}
|
|
3182
3646
|
presets.push(...r.presets);
|
|
3183
3647
|
const hit2 = await this.resolveLibraryEntries(
|
|
3184
|
-
r.missing.filter((m) => !libraryTried.has(m)),
|
|
3648
|
+
r.missing.filter((m) => !libraryTried.has(m) && !keepsOrigin(m)),
|
|
3185
3649
|
{
|
|
3186
3650
|
quiet: !opts.warnCollisions,
|
|
3187
3651
|
lockedLibraries: opts.lockedLibraries,
|
|
@@ -3324,6 +3788,13 @@ var AssetManager = class {
|
|
|
3324
3788
|
}
|
|
3325
3789
|
});
|
|
3326
3790
|
if (intact) {
|
|
3791
|
+
const { restoreScriptModes } = await import('./library/index.js');
|
|
3792
|
+
restoreScriptModes(
|
|
3793
|
+
targetDir,
|
|
3794
|
+
c.files.map(
|
|
3795
|
+
(f) => root && f.path.startsWith(`${root}/`) ? f.path.slice(root.length + 1) : f.path
|
|
3796
|
+
)
|
|
3797
|
+
);
|
|
3327
3798
|
staged.set(this.candidateKey(c), join(targetDir, this.stagedManifestRel(c)));
|
|
3328
3799
|
continue;
|
|
3329
3800
|
}
|
|
@@ -3484,7 +3955,8 @@ var AssetManager = class {
|
|
|
3484
3955
|
};
|
|
3485
3956
|
}
|
|
3486
3957
|
const orphans = await this.orphanedSourceGaps(
|
|
3487
|
-
await this.declaredLockKeys(lockForLibraries, dependencies)
|
|
3958
|
+
await this.declaredLockKeys(lockForLibraries, dependencies),
|
|
3959
|
+
true
|
|
3488
3960
|
);
|
|
3489
3961
|
if (orphans.size > 0) {
|
|
3490
3962
|
return {
|
|
@@ -3527,6 +3999,7 @@ var AssetManager = class {
|
|
|
3527
3999
|
// library holding the same ref does not move a locked asset. `update` re-resolves.
|
|
3528
4000
|
...opts?.moveLibraries ? { preferLocked: await this.lockedPreference() } : { lockedLibraries: await this.lockedLibraryNames() },
|
|
3529
4001
|
...keepLocked ? { keepLocked } : {},
|
|
4002
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
3530
4003
|
readStoreRequires: true
|
|
3531
4004
|
});
|
|
3532
4005
|
this.assertNoFolderCollision([...resolved, ...lib.entries.map((e) => this.entryIdentity(e))]);
|
|
@@ -3570,7 +4043,7 @@ var AssetManager = class {
|
|
|
3570
4043
|
const missingHints = await this.publisherChangeHints(stillMissing);
|
|
3571
4044
|
const prevLock = this.tryReadLock();
|
|
3572
4045
|
const kept = keptLockEntries(prevLock, stillMissing);
|
|
3573
|
-
const storeNotChecked = this.storeNotCheckedRefs(
|
|
4046
|
+
const storeNotChecked = await this.storeNotCheckedRefs(
|
|
3574
4047
|
kept,
|
|
3575
4048
|
sources.map((s) => s.url)
|
|
3576
4049
|
);
|
|
@@ -3578,7 +4051,7 @@ var AssetManager = class {
|
|
|
3578
4051
|
for (const ref of /* @__PURE__ */ new Set([...kept.refs, ...fixes.keys()])) {
|
|
3579
4052
|
const fix = fixes.get(ref);
|
|
3580
4053
|
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,
|
|
4054
|
+
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
4055
|
fix
|
|
3583
4056
|
].filter((n) => n !== void 0);
|
|
3584
4057
|
missingHints[ref] = `${fix ? "" : missingHints[ref] ?? ""} (${notes.join("; ")})`;
|
|
@@ -3597,12 +4070,32 @@ var AssetManager = class {
|
|
|
3597
4070
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
3598
4071
|
assertFlowsLoad(entries, "installed");
|
|
3599
4072
|
assertNoLibrarySymlinks(entries, this.registry, repositories, opts?.link === true);
|
|
4073
|
+
const forceCommand = opts?.moveLibraries === true ? "skaile update --force" : "skaile install --force";
|
|
3600
4074
|
if (!opts?.force) {
|
|
3601
4075
|
await this.assertNoHandEditsPruned(
|
|
3602
4076
|
resolved,
|
|
3603
|
-
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
|
|
4077
|
+
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)]),
|
|
4078
|
+
forceCommand
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
const relockRemoved = await this.relockDeletions(
|
|
4082
|
+
entries,
|
|
4083
|
+
prevLock,
|
|
4084
|
+
withKeptLockEntries(freshLock, kept),
|
|
4085
|
+
opts?.force === true,
|
|
4086
|
+
forceCommand,
|
|
4087
|
+
lib.libraryOf
|
|
4088
|
+
);
|
|
4089
|
+
if (!opts?.force) this.assertNoUnrecordedAgentOverwrite(entries, prevLock, forceCommand);
|
|
4090
|
+
if (!opts?.force) {
|
|
4091
|
+
await this.assertNoHandEditsOverwritten(
|
|
4092
|
+
entries,
|
|
4093
|
+
prevLock,
|
|
4094
|
+
withKeptLockEntries(freshLock, kept),
|
|
4095
|
+
forceCommand
|
|
3604
4096
|
);
|
|
3605
4097
|
}
|
|
4098
|
+
const inferredHarnesses = this.inferLegacyHarnessSubsets(entries);
|
|
3606
4099
|
const { removed, replaced } = this.reconcileRemovedAssets(
|
|
3607
4100
|
resolved,
|
|
3608
4101
|
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
|
|
@@ -3610,6 +4103,7 @@ var AssetManager = class {
|
|
|
3610
4103
|
const copyDrift = await this.lockedCopyDrift();
|
|
3611
4104
|
const missingCopies = await this.incompleteDeploys();
|
|
3612
4105
|
const unrecordedAgents = this.unrecordedAgentFiles(prevLock);
|
|
4106
|
+
const composedDrift = /* @__PURE__ */ new Set();
|
|
3613
4107
|
const deployed = [
|
|
3614
4108
|
...new Set(
|
|
3615
4109
|
this.targetHarnesses().flatMap(
|
|
@@ -3617,7 +4111,10 @@ var AssetManager = class {
|
|
|
3617
4111
|
this.entriesForHarness(entries, t),
|
|
3618
4112
|
repositories,
|
|
3619
4113
|
patches,
|
|
3620
|
-
this.deployOptsFor(t, opts?.link
|
|
4114
|
+
this.deployOptsFor(t, opts?.link, void 0, {
|
|
4115
|
+
refresh: opts?.moveLibraries === true,
|
|
4116
|
+
onDrift: (ref, composeRef) => composedDrift.add(this.composedDriftLine(ref, composeRef))
|
|
4117
|
+
})
|
|
3621
4118
|
)
|
|
3622
4119
|
)
|
|
3623
4120
|
)
|
|
@@ -3625,14 +4122,24 @@ var AssetManager = class {
|
|
|
3625
4122
|
this.sweepLegacyOmpCopies(entries);
|
|
3626
4123
|
const mcpRestored = await this.renderHarnessMcpConfig();
|
|
3627
4124
|
const agentRender = await this.renderAgentInstances();
|
|
3628
|
-
const restored = [
|
|
4125
|
+
const restored = [
|
|
4126
|
+
...this.restoredCopies(copyDrift, deployed, missingCopies),
|
|
4127
|
+
...relockRemoved,
|
|
4128
|
+
...mcpRestored
|
|
4129
|
+
];
|
|
3629
4130
|
const rewritten = this.rewrittenAgentFiles(unrecordedAgents);
|
|
3630
4131
|
const lockWanted = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
|
|
3631
4132
|
let lockWritten = false;
|
|
3632
4133
|
let relocked = [];
|
|
3633
4134
|
let moved = [];
|
|
3634
4135
|
if (lockWanted) {
|
|
3635
|
-
const lockData = this.
|
|
4136
|
+
const lockData = this.withCopiedFiles(
|
|
4137
|
+
this.withRenderedAgents(
|
|
4138
|
+
withKeptLockEntries(freshLock, kept),
|
|
4139
|
+
opts?.moveLibraries ? void 0 : prevLock
|
|
4140
|
+
),
|
|
4141
|
+
prevLock
|
|
4142
|
+
);
|
|
3636
4143
|
moved = libraryMoves(prevLock, libraryLock);
|
|
3637
4144
|
const movedRefs = new Set(moved.map((m) => m.ref));
|
|
3638
4145
|
relocked = relockedRefs(prevLock, lockData).filter((k) => !movedRefs.has(k.split("#")[0]));
|
|
@@ -3648,12 +4155,17 @@ var AssetManager = class {
|
|
|
3648
4155
|
return {
|
|
3649
4156
|
deployed,
|
|
3650
4157
|
removed,
|
|
3651
|
-
missing: [
|
|
4158
|
+
missing: [
|
|
4159
|
+
...reportedMissing,
|
|
4160
|
+
...versionBlocked.filter((b) => !reportedMissing.includes(b)),
|
|
4161
|
+
...composedDrift
|
|
4162
|
+
],
|
|
3652
4163
|
...placeholderBodies.length > 0 ? { placeholderBodies } : {},
|
|
3653
4164
|
...placeholderDescriptions.length > 0 ? { placeholderDescriptions } : {},
|
|
3654
4165
|
...storeNotChecked.length > 0 ? { storeNotChecked } : {},
|
|
3655
4166
|
...Object.keys(missingHints).length > 0 ? { missingHints } : {},
|
|
3656
4167
|
...relocked.length > 0 ? { relocked } : {},
|
|
4168
|
+
...inferredHarnesses.length > 0 ? { inferredHarnesses } : {},
|
|
3657
4169
|
...moved.length > 0 ? { moved } : {},
|
|
3658
4170
|
...replaced.length > 0 ? { replaced } : {},
|
|
3659
4171
|
...restored.length > 0 ? { restored } : {},
|
|
@@ -3674,7 +4186,7 @@ var AssetManager = class {
|
|
|
3674
4186
|
* @param declaredSourceUrls - The `sources:` URLs of `skaile.yaml`; a lock entry from one of
|
|
3675
4187
|
* them is that source's, not the store's.
|
|
3676
4188
|
*/
|
|
3677
|
-
storeNotCheckedRefs(kept, declaredSourceUrls) {
|
|
4189
|
+
async storeNotCheckedRefs(kept, declaredSourceUrls) {
|
|
3678
4190
|
const skipped = this.catalogUrlOverride === "";
|
|
3679
4191
|
if (!skipped && this.storeFailures.size === 0) return [];
|
|
3680
4192
|
const declared = new Set(declaredSourceUrls.map(normalizeRepoUrl));
|
|
@@ -3685,7 +4197,7 @@ var AssetManager = class {
|
|
|
3685
4197
|
const entry = kept.assets[key];
|
|
3686
4198
|
return entry !== void 0 && !entry.library && entry.source !== void 0 && !declared.has(normalizeRepoUrl(entry.source.url));
|
|
3687
4199
|
});
|
|
3688
|
-
if (!fromStore || !this.keptCopyDeployed(kept, ref)) continue;
|
|
4200
|
+
if (!fromStore || !await this.keptCopyDeployed(kept, ref)) continue;
|
|
3689
4201
|
if (skipped && this.storeFailures.size === 0) {
|
|
3690
4202
|
out.push({ ref, reason: "skipped", detail: "--no-catalog: the store was not asked" });
|
|
3691
4203
|
continue;
|
|
@@ -3831,7 +4343,7 @@ var AssetManager = class {
|
|
|
3831
4343
|
* other version taking its place) whose deployed folder holds hand edits: the files that differ
|
|
3832
4344
|
* from the lock are named, nothing is removed, and `--force` prunes anyway (OQ6).
|
|
3833
4345
|
*/
|
|
3834
|
-
async assertNoHandEditsPruned(resolved, keep) {
|
|
4346
|
+
async assertNoHandEditsPruned(resolved, keep, forceCommand) {
|
|
3835
4347
|
const oldLock = this.tryReadLock();
|
|
3836
4348
|
if (!oldLock) return;
|
|
3837
4349
|
const resolvedRefs = [
|
|
@@ -3863,7 +4375,202 @@ var AssetManager = class {
|
|
|
3863
4375
|
throw new Error(
|
|
3864
4376
|
`Not pruned: skaile.yaml no longer declares ${lines.length === 1 ? "an asset" : "assets"} whose deployed copy holds your edits:
|
|
3865
4377
|
${lines.join("\n ")}
|
|
3866
|
-
Nothing was changed. Declare it again to keep it, or run \`
|
|
4378
|
+
Nothing was changed. Declare it again to keep it, or run \`${forceCommand}\` to prune it with its edits.`
|
|
4379
|
+
);
|
|
4380
|
+
}
|
|
4381
|
+
/**
|
|
4382
|
+
* Report (and, without `force`, guard) the files a re-lock deletes from a deployed copy
|
|
4383
|
+
* (OQ5, p1441): the asset is locked already and its library or source now holds other content
|
|
4384
|
+
* (`next` re-locks it), and the deployed folder holds files the new content does not ship. The
|
|
4385
|
+
* deploy clears them. Two cases:
|
|
4386
|
+
* - the deployed copy still equals what `prevLock` recorded (per-file sha256 for a source
|
|
4387
|
+
* or store asset, the library hash for a library asset): the new version dropped those
|
|
4388
|
+
* files, nobody edited them, so they are deleted and come back as `removed` rows (an
|
|
4389
|
+
* `install`/`update` prints `Removed <file> (not part of <ref>)`), without a refusal;
|
|
4390
|
+
* - the copy diverged (a file the previous content did not ship, an edited file, or no hash
|
|
4391
|
+
* to compare): without `force` this throws one error naming the files and changes nothing.
|
|
4392
|
+
* A source or store asset names only the files the previous content did not ship (or that
|
|
4393
|
+
* were edited); a library asset records no file list, so it names every file to delete.
|
|
4394
|
+
*
|
|
4395
|
+
* @param entries - The catalog entries about to be deployed.
|
|
4396
|
+
* @param next - The lock the run is about to write (or merge).
|
|
4397
|
+
* @param libraryOf - The library each library-origin entry came from.
|
|
4398
|
+
*/
|
|
4399
|
+
async relockDeletions(entries, prevLock, next, force, forceCommand, libraryOf) {
|
|
4400
|
+
if (!prevLock) return [];
|
|
4401
|
+
const relocked = relockedRefs(prevLock, next).map((key) => ({
|
|
4402
|
+
key,
|
|
4403
|
+
id: parseCanonicalLockKey(key)
|
|
4404
|
+
}));
|
|
4405
|
+
if (relocked.length === 0) return [];
|
|
4406
|
+
const shown = (p) => (this.global ? p : relative(this.projectDir, p)).split(sep).join("/");
|
|
4407
|
+
let snap;
|
|
4408
|
+
const rows = [];
|
|
4409
|
+
const refused = [];
|
|
4410
|
+
for (const e of entries) {
|
|
4411
|
+
const hit = relocked.find(({ id }) => id.kind === e.kind && id.name === e.name);
|
|
4412
|
+
const component = hit ? this.installTarget(e.kind) : void 0;
|
|
4413
|
+
if (!hit || !component) continue;
|
|
4414
|
+
const prevEntry = Object.entries(prevLock.assets).find(([k]) => {
|
|
4415
|
+
const o = parseCanonicalLockKey(k);
|
|
4416
|
+
return o.kind === hit.id.kind && o.name === hit.id.name && o.publisher === hit.id.publisher;
|
|
4417
|
+
})?.[1];
|
|
4418
|
+
const removed = [];
|
|
4419
|
+
const blocked = [];
|
|
4420
|
+
for (const t of this.targetHarnesses()) {
|
|
4421
|
+
const drift = deployedAssetDrift(e, this.deployOptsFor(t));
|
|
4422
|
+
if (!drift || drift.extra.length === 0) continue;
|
|
4423
|
+
const dir = deployedDir(component, e.name, t, this.global, this.projectDir);
|
|
4424
|
+
const files = drift.extra;
|
|
4425
|
+
let untouched;
|
|
4426
|
+
if (prevEntry && prevEntry.files.length > 0) {
|
|
4427
|
+
const locked = new Map(
|
|
4428
|
+
lockedAssetFiles(e.kind, prevEntry.files).map((f) => [f.rel, f.sha256])
|
|
4429
|
+
);
|
|
4430
|
+
untouched = files.filter((rel) => {
|
|
4431
|
+
const want = locked.get(rel);
|
|
4432
|
+
if (want === void 0) return false;
|
|
4433
|
+
try {
|
|
4434
|
+
return computeHash(join(dir, rel)) === want;
|
|
4435
|
+
} catch {
|
|
4436
|
+
return false;
|
|
4437
|
+
}
|
|
4438
|
+
});
|
|
4439
|
+
} else if (prevEntry?.library?.sha256 && !prevEntry.library.installed) {
|
|
4440
|
+
const libName = libraryOf.get(e);
|
|
4441
|
+
snap ??= await this.loadLibraryIndex();
|
|
4442
|
+
const libPath = libName ? this.libraryPath({ name: libName }, snap) : null;
|
|
4443
|
+
if (libPath) {
|
|
4444
|
+
const assetRoot = statSync(e.source).isDirectory() ? e.source : dirname(e.source);
|
|
4445
|
+
const hash = computeDeterministicHash(
|
|
4446
|
+
dir,
|
|
4447
|
+
listAssetFiles(dir),
|
|
4448
|
+
posix.normalize(relative(libPath, assetRoot).split(sep).join("/"))
|
|
4449
|
+
);
|
|
4450
|
+
if (hash === prevEntry.library.sha256) untouched = [...files];
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
const own = new Set(untouched ?? []);
|
|
4454
|
+
for (const f of files) (own.has(f) ? removed : blocked).push(shown(join(dir, f)));
|
|
4455
|
+
}
|
|
4456
|
+
if (removed.length > 0) rows.push({ ref: hit.key, restored: [], removed });
|
|
4457
|
+
if (blocked.length > 0) refused.push({ ref: hit.key, restored: [], removed: blocked });
|
|
4458
|
+
}
|
|
4459
|
+
if (refused.length === 0 || force) return [...rows, ...refused];
|
|
4460
|
+
const lines = refused.map((r) => `${r.ref} (${r.removed.join(", ")})`);
|
|
4461
|
+
throw new Error(
|
|
4462
|
+
`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:
|
|
4463
|
+
${lines.join("\n ")}
|
|
4464
|
+
Nothing was changed. Copy what you need, or run \`${forceCommand}\` to delete them.`
|
|
4465
|
+
);
|
|
4466
|
+
}
|
|
4467
|
+
/**
|
|
4468
|
+
* Stop `install` from overwriting a hand-edited rendered agent file that no lock entry
|
|
4469
|
+
* records (OQ5, p1440): the lock is gone (or never held the agent), so nothing tells an edit
|
|
4470
|
+
* from a stale render, and the deploy would replace the file without a word. Checks the file
|
|
4471
|
+
* named after each agent about to be deployed and the file of each agent instance whose bound
|
|
4472
|
+
* agent has no lock entry. Throws one error naming each file that exists and differs from its
|
|
4473
|
+
* render and changes nothing; `--force` (the caller skips this check) overwrites. A file that
|
|
4474
|
+
* equals its render is not an edit. Project mode only.
|
|
4475
|
+
*/
|
|
4476
|
+
assertNoUnrecordedAgentOverwrite(entries, prevLock, forceCommand) {
|
|
4477
|
+
if (this.global) return;
|
|
4478
|
+
const lockedAgents = new Set(
|
|
4479
|
+
Object.keys(prevLock?.assets ?? {}).map(parseCanonicalLockKey).filter((k) => k.kind && this.installTarget(k.kind) === "agent").map((k) => k.name)
|
|
4480
|
+
);
|
|
4481
|
+
const opts = this.deployOptsFor(this.harness);
|
|
4482
|
+
const lines = /* @__PURE__ */ new Set();
|
|
4483
|
+
const collect = (diff, what) => {
|
|
4484
|
+
if (!diff) return;
|
|
4485
|
+
for (const m of diff.matchAll(/^--- (.+?)\t/gm)) {
|
|
4486
|
+
const abs = m[1];
|
|
4487
|
+
if (!existsSync(abs)) continue;
|
|
4488
|
+
lines.add(
|
|
4489
|
+
`${relative(this.projectDir, abs).split(sep).join("/")} (${what} differs from its render)`
|
|
4490
|
+
);
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4493
|
+
for (const e of entries) {
|
|
4494
|
+
if (this.installTarget(e.kind) !== "agent" || lockedAgents.has(e.name)) continue;
|
|
4495
|
+
try {
|
|
4496
|
+
collect(diffDeployedAgent(e, opts), `agent ${e.name}`);
|
|
4497
|
+
} catch {
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
let instances = {};
|
|
4501
|
+
try {
|
|
4502
|
+
instances = effectiveAgentInstances(resolveSkWorkspaceConfig(this.projectDir));
|
|
4503
|
+
} catch {
|
|
4504
|
+
}
|
|
4505
|
+
for (const [id, config] of Object.entries(instances)) {
|
|
4506
|
+
if (!config.ref.startsWith("agent:")) continue;
|
|
4507
|
+
const bound = parseRefTolerant(config.ref);
|
|
4508
|
+
if (bound?.kind === "agent" && lockedAgents.has(bound.name)) continue;
|
|
4509
|
+
try {
|
|
4510
|
+
const srcDir = resolveAgentDefinitionDir(this.projectDir, config.ref);
|
|
4511
|
+
if (srcDir === void 0) continue;
|
|
4512
|
+
const entry = {
|
|
4513
|
+
kind: "agent",
|
|
4514
|
+
name: id,
|
|
4515
|
+
description: "",
|
|
4516
|
+
source: srcDir,
|
|
4517
|
+
version: "",
|
|
4518
|
+
requires: [],
|
|
4519
|
+
dependencies: []
|
|
4520
|
+
};
|
|
4521
|
+
collect(diffDeployedAgent(entry, opts, { id, srcDir, config }), `agent instance ${id}`);
|
|
4522
|
+
} catch {
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
if (lines.size === 0) return;
|
|
4526
|
+
throw new Error(
|
|
4527
|
+
`Not deployed: the deploy would overwrite ${lines.size === 1 ? "a rendered agent file" : "rendered agent files"} that no lock entry records:
|
|
4528
|
+
${[...lines].join("\n ")}
|
|
4529
|
+
Nothing was changed. Move your edits away, or run \`${forceCommand}\` to overwrite the file.`
|
|
4530
|
+
);
|
|
4531
|
+
}
|
|
4532
|
+
/**
|
|
4533
|
+
* Stop a re-lock from replacing a hand edit of a deployed copy (OQ6, p1641): the asset is locked
|
|
4534
|
+
* already, its library or source now holds other content (`next` re-locks it), and a deployed
|
|
4535
|
+
* file differs from what the lock covers and from the new content. The deploy would replace
|
|
4536
|
+
* the file without a word. Throws one error naming each file and changes nothing; `--force`
|
|
4537
|
+
* (the caller skips this check) replaces them. An agent's rendered files are compared with the
|
|
4538
|
+
* hashes the lock records. A file the new content brings back byte for byte loses nothing.
|
|
4539
|
+
* Project mode only.
|
|
4540
|
+
*/
|
|
4541
|
+
async assertNoHandEditsOverwritten(entries, prevLock, next, forceCommand) {
|
|
4542
|
+
if (this.global || !prevLock) return;
|
|
4543
|
+
const relocked = relockedRefs(prevLock, next).map((k) => parseCanonicalLockKey(k));
|
|
4544
|
+
if (relocked.length === 0) return;
|
|
4545
|
+
const opts = this.deployOptsFor(this.harness);
|
|
4546
|
+
const lines = [];
|
|
4547
|
+
for (const d of await this.lockedCopyDrift()) {
|
|
4548
|
+
const id = parseCanonicalLockKey(d.key);
|
|
4549
|
+
if (!relocked.some(
|
|
4550
|
+
(r) => r.kind === id.kind && r.name === id.name && r.publisher === id.publisher
|
|
4551
|
+
)) {
|
|
4552
|
+
continue;
|
|
4553
|
+
}
|
|
4554
|
+
let files = d.modified;
|
|
4555
|
+
if (!d.rendered) {
|
|
4556
|
+
const component = id.kind ? this.installTarget(id.kind) : void 0;
|
|
4557
|
+
if (!id.kind || !id.name || !component) continue;
|
|
4558
|
+
const entry = entries.find((e) => e.kind === id.kind && e.name === id.name);
|
|
4559
|
+
const differs = entry ? deployedAssetDrift(entry, opts)?.modified : void 0;
|
|
4560
|
+
if (differs) files = files.filter((f) => differs.includes(f));
|
|
4561
|
+
const dir = relative(
|
|
4562
|
+
this.projectDir,
|
|
4563
|
+
deployedDir(component, id.name, this.harness, this.global, this.projectDir)
|
|
4564
|
+
).split(sep).join("/");
|
|
4565
|
+
files = files.map((f) => `${dir}/${f}`);
|
|
4566
|
+
}
|
|
4567
|
+
if (files.length > 0) lines.push(`${d.ref} (${files.join(", ")} was edited)`);
|
|
4568
|
+
}
|
|
4569
|
+
if (lines.length === 0) return;
|
|
4570
|
+
throw new Error(
|
|
4571
|
+
`Not re-locked: the new content would replace ${lines.length === 1 ? "a file" : "files"} you edited in the deployed copy:
|
|
4572
|
+
${lines.join("\n ")}
|
|
4573
|
+
Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace them.`
|
|
3867
4574
|
);
|
|
3868
4575
|
}
|
|
3869
4576
|
/**
|
|
@@ -4091,7 +4798,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4091
4798
|
}
|
|
4092
4799
|
assertLockHashes(lock);
|
|
4093
4800
|
this.assertNoFolderCollision(Object.keys(lock.assets).map(parseCanonicalLockKey));
|
|
4094
|
-
const orphaned = [...(await this.orphanedSourceGaps()).values()];
|
|
4801
|
+
const orphaned = [...(await this.orphanedSourceGaps(void 0, true)).values()];
|
|
4095
4802
|
if (orphaned.length > 0) {
|
|
4096
4803
|
throw new Error(
|
|
4097
4804
|
`Lock file verification failed:
|
|
@@ -4166,20 +4873,47 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4166
4873
|
throw new Error(`Lock file verification failed:
|
|
4167
4874
|
${reason}`);
|
|
4168
4875
|
}
|
|
4169
|
-
const library = await this.restoreLibraryLockEntries(lock);
|
|
4876
|
+
const library = await this.restoreLibraryLockEntries(lock, { storeFallback: true });
|
|
4170
4877
|
assertFlowsLoad([...resolved, ...library.entries], "installed");
|
|
4171
4878
|
assertNoLibrarySymlinks([...resolved, ...library.entries], this.registry, repositories);
|
|
4172
|
-
const
|
|
4173
|
-
const
|
|
4174
|
-
|
|
4175
|
-
|
|
4879
|
+
const inferredHarnesses = this.inferLegacyHarnessSubsets([...resolved, ...library.entries]);
|
|
4880
|
+
const composedPre = /* @__PURE__ */ new Set();
|
|
4881
|
+
if (!this.global) {
|
|
4882
|
+
for (const e of [...resolved, ...library.entries]) {
|
|
4883
|
+
if (this.installTarget(e.kind) !== "agent") continue;
|
|
4884
|
+
try {
|
|
4885
|
+
for (const composeRef of composedDriftRefs(e, this.deployOptsFor(this.harness))) {
|
|
4886
|
+
composedPre.add(this.composedDriftLine(canonicalEntryRef(e), composeRef));
|
|
4887
|
+
}
|
|
4888
|
+
} catch {
|
|
4889
|
+
}
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
if (library.missing.length > 0 || composedPre.size > 0) {
|
|
4893
|
+
return {
|
|
4894
|
+
deployed: [],
|
|
4895
|
+
removed: [],
|
|
4896
|
+
missing: [...library.missing, ...composedPre],
|
|
4897
|
+
lockWritten: false,
|
|
4898
|
+
npmInstalled: [],
|
|
4899
|
+
npmFailed: []
|
|
4900
|
+
};
|
|
4901
|
+
}
|
|
4902
|
+
const copyDrift = await this.lockedCopyDrift();
|
|
4903
|
+
const missingCopies = await this.incompleteDeploys();
|
|
4904
|
+
const unrecordedAgents = this.unrecordedAgentFiles(lock);
|
|
4905
|
+
const composedDrift = /* @__PURE__ */ new Set();
|
|
4906
|
+
const deployed = [
|
|
4176
4907
|
...new Set(
|
|
4177
4908
|
this.targetHarnesses().flatMap(
|
|
4178
4909
|
(t) => deployAll(
|
|
4179
4910
|
this.entriesForHarness([...resolved, ...library.entries], t),
|
|
4180
4911
|
repositories,
|
|
4181
4912
|
patches,
|
|
4182
|
-
this.deployOptsFor(t
|
|
4913
|
+
this.deployOptsFor(t, void 0, void 0, {
|
|
4914
|
+
refresh: false,
|
|
4915
|
+
onDrift: (ref, composeRef) => composedDrift.add(this.composedDriftLine(ref, composeRef))
|
|
4916
|
+
})
|
|
4183
4917
|
)
|
|
4184
4918
|
)
|
|
4185
4919
|
)
|
|
@@ -4195,25 +4929,53 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4195
4929
|
return p ? `${p.kind}:${p.name}` : m;
|
|
4196
4930
|
})
|
|
4197
4931
|
);
|
|
4932
|
+
const viaStoreIds = new Set(library.viaStore.map(gapIdentity));
|
|
4198
4933
|
const gaps = (await this.projectState()).gaps.filter((g) => {
|
|
4199
4934
|
if (g.type === "agent") return false;
|
|
4935
|
+
if (g.unreadableSource) return false;
|
|
4936
|
+
if ((g.type === "library" || g.type === "unresolved") && viaStoreIds.has(gapIdentity(g.ref))) {
|
|
4937
|
+
return false;
|
|
4938
|
+
}
|
|
4200
4939
|
const p = parseRefTolerant(g.ref);
|
|
4201
4940
|
return !(p && alreadyMissing.has(`${p.kind}:${p.name}`));
|
|
4202
4941
|
}).map((g) => g.message);
|
|
4942
|
+
const libraryNotices = this.lockUrlRestoreNotices(lock, library.viaStore);
|
|
4203
4943
|
const placeholderBodies = scaffoldBodyRefs(library.entries, deployed);
|
|
4204
4944
|
const placeholderDescriptions = scaffoldDescriptionRefs(library.entries, deployed);
|
|
4205
4945
|
return {
|
|
4206
4946
|
deployed,
|
|
4207
4947
|
removed: [],
|
|
4208
|
-
missing: [...library.missing, ...gaps],
|
|
4948
|
+
missing: [...library.missing, ...gaps, ...composedDrift],
|
|
4209
4949
|
...placeholderBodies.length > 0 ? { placeholderBodies } : {},
|
|
4210
4950
|
...placeholderDescriptions.length > 0 ? { placeholderDescriptions } : {},
|
|
4211
4951
|
lockWritten: false,
|
|
4952
|
+
...inferredHarnesses.length > 0 ? { inferredHarnesses } : {},
|
|
4212
4953
|
...restored.length > 0 ? { restored } : {},
|
|
4213
4954
|
...rewritten.length > 0 ? { rewritten } : {},
|
|
4955
|
+
...libraryNotices.length > 0 ? { libraryNotices } : {},
|
|
4214
4956
|
...agentRender
|
|
4215
4957
|
};
|
|
4216
4958
|
}
|
|
4959
|
+
/**
|
|
4960
|
+
* One notice line per locked library that is not registered here and whose assets
|
|
4961
|
+
* `install --locked` restored from the lock url: names the library, how many assets came from
|
|
4962
|
+
* the url, and how to register the library to move the lock later (OQ8, p1655).
|
|
4963
|
+
*/
|
|
4964
|
+
lockUrlRestoreNotices(lock, viaStore) {
|
|
4965
|
+
const byLibrary = /* @__PURE__ */ new Map();
|
|
4966
|
+
for (const key of viaStore) {
|
|
4967
|
+
const origin = lock.assets[key]?.library;
|
|
4968
|
+
if (!origin) continue;
|
|
4969
|
+
const name = unregisteredLibraryName(origin);
|
|
4970
|
+
const row = byLibrary.get(name) ?? { ...origin.url ? { url: origin.url } : {}, refs: [] };
|
|
4971
|
+
row.refs.push(key);
|
|
4972
|
+
byLibrary.set(name, row);
|
|
4973
|
+
}
|
|
4974
|
+
return [...byLibrary].map(([name, { url, refs }]) => {
|
|
4975
|
+
const register = url ? `skaile library init ${name} --git ${url}` : `skaile library init ${name} --path <dir>`;
|
|
4976
|
+
return `Library "${name}" is not registered here: restored ${refs.length} asset${refs.length === 1 ? "" : "s"} from the lock url${url ? ` ${url}` : ""}. Register it with \`${register}\` to move the lock later.`;
|
|
4977
|
+
});
|
|
4978
|
+
}
|
|
4217
4979
|
/**
|
|
4218
4980
|
* The versions of `parsed`'s name that a source, a library or a store holds, newest first,
|
|
4219
4981
|
* for the `Asset not found` of a pinned ref. Empty for an unpinned ref (nothing was ruled
|
|
@@ -4236,6 +4998,120 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4236
4998
|
}
|
|
4237
4999
|
return [...found].sort((a, b) => gt(coerceVersion(a), coerceVersion(b)) ? -1 : 1);
|
|
4238
5000
|
}
|
|
5001
|
+
/**
|
|
5002
|
+
* The refs of the other kinds that a source or a library holds under the same publisher and
|
|
5003
|
+
* name as `parsed` (`agent:@pub/captain` for a `@pub/captain` that is no skill), for the
|
|
5004
|
+
* `Did you mean` of an `add` that missed.
|
|
5005
|
+
*/
|
|
5006
|
+
async otherKindRefs(parsed) {
|
|
5007
|
+
if (!parsed.publisher) return [];
|
|
5008
|
+
const publisher = parsed.publisher.replace(/^@/, "");
|
|
5009
|
+
const found = [];
|
|
5010
|
+
for (const kind of [...this.knownKinds].sort()) {
|
|
5011
|
+
if (kind === parsed.kind) continue;
|
|
5012
|
+
const candidate = `${kind}:@${publisher}/${parsed.name}`;
|
|
5013
|
+
try {
|
|
5014
|
+
if (await this.info(candidate) !== null) found.push(candidate);
|
|
5015
|
+
} catch {
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
return found;
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* The `Did you mean agent:@pub/name?` sentence for a ref written without a kind (`@pub/name`,
|
|
5022
|
+
* which means `skill`) that names no asset: the refs of other kinds under the same name that
|
|
5023
|
+
* a source, a library, the lock or `skaile.yaml` holds (p1425). `undefined` when the ref has a
|
|
5024
|
+
* kind or nothing of another kind answers to it. `add`, `diff`, `update`, `info`, `remove` and
|
|
5025
|
+
* `why` end their not-found line with it.
|
|
5026
|
+
*/
|
|
5027
|
+
async otherKindHint(ref) {
|
|
5028
|
+
if (/^[^@/#\s]+:/.test(ref.trim())) return void 0;
|
|
5029
|
+
const parsed = parseRefTolerant(ref);
|
|
5030
|
+
if (!parsed) return void 0;
|
|
5031
|
+
const wantPublisher = parsed.publisher?.replace(/^@/, "");
|
|
5032
|
+
const found = new Set(await this.otherKindRefs(parsed));
|
|
5033
|
+
const note = (id) => {
|
|
5034
|
+
if (!id.kind || !id.name || id.kind === parsed.kind || id.name !== parsed.name) return;
|
|
5035
|
+
const publisher = id.publisher?.replace(/^@/, "");
|
|
5036
|
+
if (wantPublisher && publisher !== wantPublisher) return;
|
|
5037
|
+
found.add(
|
|
5038
|
+
assetRefToStr({ kind: id.kind, name: id.name, ...publisher ? { publisher } : {} })
|
|
5039
|
+
);
|
|
5040
|
+
};
|
|
5041
|
+
for (const key of Object.keys(this.tryReadLock()?.assets ?? {})) {
|
|
5042
|
+
note(parseCanonicalLockKey(key));
|
|
5043
|
+
}
|
|
5044
|
+
try {
|
|
5045
|
+
for (const dep of resolveSkWorkspaceConfig(this.projectDir).dependencies ?? []) {
|
|
5046
|
+
if (typeof dep === "string") note(parseRefTolerant(dep) ?? {});
|
|
5047
|
+
}
|
|
5048
|
+
} catch {
|
|
5049
|
+
}
|
|
5050
|
+
const refs = [...found].sort();
|
|
5051
|
+
if (refs.length === 0) return void 0;
|
|
5052
|
+
return refs.length === 1 ? `Did you mean ${refs[0]}?` : `Did you mean one of: ${refs.join(", ")}?`;
|
|
5053
|
+
}
|
|
5054
|
+
/**
|
|
5055
|
+
* What `add` would deploy for the resolved candidates and library entries, without touching
|
|
5056
|
+
* the project: one item per asset with its origin, version, deploy folders and files.
|
|
5057
|
+
*/
|
|
5058
|
+
planAddItems(resolved, lib, stores, parsed) {
|
|
5059
|
+
const storeUrls = new Set(stores.map((s) => normalizeRepoUrl(s.url)));
|
|
5060
|
+
const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
|
|
5061
|
+
const shownPath = (dir) => {
|
|
5062
|
+
const rel = relative(this.projectDir, dir);
|
|
5063
|
+
return this.global || rel.startsWith("..") ? dir.replace(homedir(), "~") : `${rel}${dir.endsWith(sep) ? sep : ""}`;
|
|
5064
|
+
};
|
|
5065
|
+
const folders = (kind, name) => {
|
|
5066
|
+
const component = this.installTarget(kind);
|
|
5067
|
+
if (!component) return [];
|
|
5068
|
+
return this.targetHarnesses().map(
|
|
5069
|
+
(t) => shownPath(deployedDir(component, name, t, this.global, this.projectDir))
|
|
5070
|
+
);
|
|
5071
|
+
};
|
|
5072
|
+
const renders = (kind, name) => {
|
|
5073
|
+
if (this.installTarget(kind) !== "agent") return [];
|
|
5074
|
+
return this.targetHarnesses().flatMap(
|
|
5075
|
+
(t) => agentRenderFiles(
|
|
5076
|
+
t,
|
|
5077
|
+
dirname(deployedDir("agent", name, t, this.global, this.projectDir)),
|
|
5078
|
+
name
|
|
5079
|
+
).map(shownPath)
|
|
5080
|
+
);
|
|
5081
|
+
};
|
|
5082
|
+
const asked = (kind, name, publisher) => kind === parsed.kind && name === parsed.name && (!parsed.publisher || (publisher ?? "").replace(/^@/, "") === parsed.publisher.replace(/^@/, ""));
|
|
5083
|
+
const items = [];
|
|
5084
|
+
for (const c of resolved) {
|
|
5085
|
+
const url = normalizeRepoUrl(c.sourceUrl);
|
|
5086
|
+
items.push({
|
|
5087
|
+
ref: assetRefToStr({
|
|
5088
|
+
kind: c.kind,
|
|
5089
|
+
name: c.name,
|
|
5090
|
+
publisher: c.publisher.replace(/^@/, "")
|
|
5091
|
+
}),
|
|
5092
|
+
version: c.version,
|
|
5093
|
+
origin: storeUrls.has(url) ? `store ${c.sourceUrl}` : url === factoryUrl ? "factory" : `source ${sourceShortName(c.sourceUrl)}`,
|
|
5094
|
+
requested: asked(c.kind, c.name, c.publisher),
|
|
5095
|
+
...planPaths(
|
|
5096
|
+
folders(c.kind, c.name),
|
|
5097
|
+
renders(c.kind, c.name),
|
|
5098
|
+
c.files.map((f) => f.path)
|
|
5099
|
+
)
|
|
5100
|
+
});
|
|
5101
|
+
}
|
|
5102
|
+
for (const e of lib.entries) {
|
|
5103
|
+
const id = this.entryIdentity(e);
|
|
5104
|
+
items.push({
|
|
5105
|
+
// The ref is printed once: `canonicalEntryRef` carries the version as a pin, `version` has it.
|
|
5106
|
+
ref: canonicalEntryRef(e).replace(/#.*$/, ""),
|
|
5107
|
+
version: e.version ?? "",
|
|
5108
|
+
origin: `library ${lib.libraryOf.get(e) ?? ""}`.trim(),
|
|
5109
|
+
requested: asked(e.kind, e.name, id.publisher),
|
|
5110
|
+
...planPaths(folders(e.kind, e.name), renders(e.kind, e.name), [...e.files ?? []])
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
5113
|
+
return items.sort((a, b) => Number(b.requested) - Number(a.requested));
|
|
5114
|
+
}
|
|
4239
5115
|
/**
|
|
4240
5116
|
* Parse the ref `add` was given. A ref the grammar rejects fails with the parser's message
|
|
4241
5117
|
* minus its manifest-migration advice (it points at a skill that is not for `add`), and:
|
|
@@ -4253,7 +5129,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4253
5129
|
const plain = err.message.endsWith(advice) ? err.message.slice(0, -advice.length) : err.message;
|
|
4254
5130
|
if (plain.startsWith("publisher required")) {
|
|
4255
5131
|
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(", ")}?`;
|
|
5132
|
+
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
5133
|
throw new Error(`${plain}${hint}`);
|
|
4258
5134
|
}
|
|
4259
5135
|
const pinned = /^(.*\/[^@#/]+)@([^@#/]+)$/.exec(ref.trim());
|
|
@@ -4334,6 +5210,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4334
5210
|
stores,
|
|
4335
5211
|
declaredRefs: dependencies,
|
|
4336
5212
|
warnCollisions: true,
|
|
5213
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
4337
5214
|
readStoreRequires: true,
|
|
4338
5215
|
...opts?.preferLockedLibrary ? { preferLocked: await this.lockedPreference() } : {}
|
|
4339
5216
|
});
|
|
@@ -4370,6 +5247,25 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4370
5247
|
);
|
|
4371
5248
|
}
|
|
4372
5249
|
}
|
|
5250
|
+
if (renamedEntryLookup) {
|
|
5251
|
+
for (const l of snap?.libraries ?? []) {
|
|
5252
|
+
const libPath = this.libraryPath({ name: l.name }, snap);
|
|
5253
|
+
if (!libPath) continue;
|
|
5254
|
+
const renamed = renamedEntryLookup(libPath).find(
|
|
5255
|
+
(r) => r.kind === parsed.kind && r.name === parsed.name && (!parsed.publisher || bare(r.publisher) === bare(parsed.publisher))
|
|
5256
|
+
);
|
|
5257
|
+
if (renamed) {
|
|
5258
|
+
const shown = renamed.publisher ? assetRefToStr({
|
|
5259
|
+
kind: renamed.kind,
|
|
5260
|
+
name: renamed.name,
|
|
5261
|
+
publisher: bare(renamed.publisher)
|
|
5262
|
+
}) : `${renamed.kind}:${renamed.name}`;
|
|
5263
|
+
throw new Error(
|
|
5264
|
+
`${shown}: ${renamed.file} now names "${renamed.now}", so the library entry is a ref \`add\` cannot resolve. Run \`skaile library index ${l.name}\` to rename the entry, or restore the name.`
|
|
5265
|
+
);
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
4373
5269
|
const unreachable = [...this.storeFailures.values()];
|
|
4374
5270
|
if (unreachable.length > 0) {
|
|
4375
5271
|
throw new Error(
|
|
@@ -4377,8 +5273,13 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4377
5273
|
);
|
|
4378
5274
|
}
|
|
4379
5275
|
const available = await this.availableVersions(parsed, stores);
|
|
5276
|
+
const otherKind = await this.otherKindHint(ref);
|
|
5277
|
+
const others = missing.filter((m) => {
|
|
5278
|
+
const id = parseRefTolerant(m);
|
|
5279
|
+
return !(id && id.kind === parsed.kind && id.name === parsed.name && (id.publisher ?? "") === (parsed.publisher ?? ""));
|
|
5280
|
+
});
|
|
4380
5281
|
throw new Error(
|
|
4381
|
-
`Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${
|
|
5282
|
+
`Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${others.length ? ` (missing: ${others.join(", ")})` : ""}${available.length ? `; ${parsed.name} has version(s): ${available.join(", ")}` : ""}` + (otherKind ? `. ${otherKind}` : "")
|
|
4382
5283
|
);
|
|
4383
5284
|
}
|
|
4384
5285
|
if (parsed.kind === "preset" && missing.length > 0) {
|
|
@@ -4401,6 +5302,62 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4401
5302
|
)
|
|
4402
5303
|
);
|
|
4403
5304
|
}
|
|
5305
|
+
const problems = origin && libPath && !(e.kind === "flow" && flowLoadError(e) !== void 0) ? libraryAssetCheck?.({ kind: e.kind, name: e.name, source: e.source }, libPath) ?? [] : [];
|
|
5306
|
+
if (problems.length > 0) {
|
|
5307
|
+
throw new Error(
|
|
5308
|
+
`${canonicalEntryRef(e)} in library "${origin}" fails the checks of \`skaile validate\` - nothing was added:
|
|
5309
|
+
${problems.map((l) => ` ${l}`).join("\n")}
|
|
5310
|
+
Fix the asset, then run \`skaile library index ${origin}\` and add it again.`
|
|
5311
|
+
);
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
if (libraryAssetCheck) {
|
|
5316
|
+
for (const c of resolved) {
|
|
5317
|
+
const decl = Object.entries(repositories).find(([, d]) => d.url === c.sourceUrl);
|
|
5318
|
+
const cloneDir = decl ? this.resolveRepoDir(decl[1], decl[0]) : null;
|
|
5319
|
+
const entry = cloneDir ? this.candidateToCatalogEntry(c, repositories) : null;
|
|
5320
|
+
if (!cloneDir || !entry) continue;
|
|
5321
|
+
if (entry.kind === "flow" && flowLoadError(entry) !== void 0) continue;
|
|
5322
|
+
const problems = libraryAssetCheck(
|
|
5323
|
+
{ kind: entry.kind, name: entry.name, source: entry.source },
|
|
5324
|
+
cloneDir
|
|
5325
|
+
);
|
|
5326
|
+
if (problems.length > 0) {
|
|
5327
|
+
throw new Error(
|
|
5328
|
+
`${canonicalEntryRef(entry)} from source ${c.sourceUrl} fails the checks of \`skaile validate\` - nothing was added:
|
|
5329
|
+
${problems.map((l) => ` ${l}`).join("\n")}
|
|
5330
|
+
Fix the asset in the source repo, then run \`skaile source sync\` and add it again.`
|
|
5331
|
+
);
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
const staleSiblings = this.staleSourceSiblingRefs(resolved, dependencies);
|
|
5336
|
+
if (staleSiblings.length > 0) {
|
|
5337
|
+
const siblings = await this.resolveLibraryFirst(staleSiblings, {
|
|
5338
|
+
provenanceIndex,
|
|
5339
|
+
overrides,
|
|
5340
|
+
stores,
|
|
5341
|
+
declaredRefs: dependencies,
|
|
5342
|
+
lockedOrigin: this.lockedOriginVersions(),
|
|
5343
|
+
readStoreRequires: true
|
|
5344
|
+
});
|
|
5345
|
+
for (const sib of staleSiblings) {
|
|
5346
|
+
const pin = parseAssetRefLoose(sib)?.pin;
|
|
5347
|
+
if (!pin) continue;
|
|
5348
|
+
const id = refIdentity(sib.replace(/#.*$/, ""));
|
|
5349
|
+
const kept = siblings.resolved.some(
|
|
5350
|
+
(c) => `${c.publisher.replace(/^@/, "")}/${c.kind}:${c.name}` === id?.replace(/^@/, "") && matchPin(pin, c.version ?? "")
|
|
5351
|
+
);
|
|
5352
|
+
if (!kept) {
|
|
5353
|
+
throw new Error(
|
|
5354
|
+
`${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.`
|
|
5355
|
+
);
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
const held = new Set(resolved.map((c) => this.candidateKey(c)));
|
|
5359
|
+
for (const c of siblings.resolved) {
|
|
5360
|
+
if (!held.has(this.candidateKey(c))) resolved.push(c);
|
|
4404
5361
|
}
|
|
4405
5362
|
}
|
|
4406
5363
|
this.assertSourceCommitsPublished(resolved, repositories);
|
|
@@ -4437,9 +5394,22 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4437
5394
|
`${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
5395
|
);
|
|
4439
5396
|
}
|
|
5397
|
+
if (opts?.dryRun) {
|
|
5398
|
+
const plan = this.planAddItems(resolved, lib, stores, parsed);
|
|
5399
|
+
opts.onPlan?.(plan);
|
|
5400
|
+
return [];
|
|
5401
|
+
}
|
|
4440
5402
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
4441
5403
|
assertFlowsLoad(entries, "added");
|
|
4442
5404
|
assertNoLibrarySymlinks(entries, this.registry, repositories, opts?.link === true);
|
|
5405
|
+
if (!opts?.force && !this.global && (resolved.length > 0 || lib.entries.length > 0)) {
|
|
5406
|
+
await this.assertNoHandEditsOverwritten(
|
|
5407
|
+
entries,
|
|
5408
|
+
this.tryReadLock(),
|
|
5409
|
+
buildLockFile(resolved, overridesApplied, await this.libraryLockEntries(lib)),
|
|
5410
|
+
opts?.preferLockedLibrary ? "skaile update --force" : `skaile add ${ref} --force`
|
|
5411
|
+
);
|
|
5412
|
+
}
|
|
4443
5413
|
if (parsed.kind === "mcp-server" && !this.global) {
|
|
4444
5414
|
const decls = await loadMcpServerDeclarations(this.projectDir, { alsoDeclared: [ref] });
|
|
4445
5415
|
const mine = decls.find(
|
|
@@ -4452,14 +5422,21 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4452
5422
|
);
|
|
4453
5423
|
}
|
|
4454
5424
|
}
|
|
5425
|
+
const harnessNamed = this.global && (this.harnessesOverride?.length ?? 0) > 0;
|
|
5426
|
+
if (harnessNamed && opts?.only && opts.onDroppedCopy)
|
|
5427
|
+
this.reportDroppedCopies(parsed, opts.onDroppedCopy);
|
|
4455
5428
|
const deployed = [
|
|
4456
5429
|
...new Set(
|
|
4457
5430
|
this.targetHarnesses().flatMap(
|
|
4458
5431
|
(t) => deployAll(
|
|
4459
|
-
this.entriesForHarness(entries, t),
|
|
5432
|
+
harnessNamed ? entries : this.entriesForHarness(entries, t),
|
|
4460
5433
|
repositories,
|
|
4461
5434
|
patches,
|
|
4462
|
-
this.deployOptsFor(t, opts?.link, opts?.onSecretFile
|
|
5435
|
+
this.deployOptsFor(t, opts?.link, opts?.onSecretFile, {
|
|
5436
|
+
refresh: true,
|
|
5437
|
+
onDrift: () => {
|
|
5438
|
+
}
|
|
5439
|
+
})
|
|
4463
5440
|
)
|
|
4464
5441
|
)
|
|
4465
5442
|
)
|
|
@@ -4479,7 +5456,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4479
5456
|
for (const placeholder of scaffoldDescriptionRefs(lib.entries, deployed)) {
|
|
4480
5457
|
opts?.onPlaceholderDescription?.(placeholder);
|
|
4481
5458
|
}
|
|
4482
|
-
this.addDependencyToConfig(ref);
|
|
5459
|
+
this.addDependencyToConfig(ref, opts?.only === true);
|
|
5460
|
+
this.widenDependencyRows(entries, parsed);
|
|
4483
5461
|
await this.renderHarnessMcpConfig();
|
|
4484
5462
|
await this.renderAgentInstances();
|
|
4485
5463
|
if (resolved.length > 0 || lib.entries.length > 0) {
|
|
@@ -4494,7 +5472,9 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4494
5472
|
}
|
|
4495
5473
|
writeLock(
|
|
4496
5474
|
this.lockPath,
|
|
4497
|
-
this.withInstanceRenderings(
|
|
5475
|
+
this.withInstanceRenderings(
|
|
5476
|
+
mergeLockFile(prevLock, this.withCopiedFiles(this.withRenderedAgents(fresh), prevLock))
|
|
5477
|
+
)
|
|
4498
5478
|
);
|
|
4499
5479
|
}
|
|
4500
5480
|
for (const d of deployed) {
|
|
@@ -4816,10 +5796,18 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4816
5796
|
}
|
|
4817
5797
|
const kept = [];
|
|
4818
5798
|
const ownedBy = [];
|
|
5799
|
+
const mcpPublishers = target.kind === "mcp-server" && !this.global ? [
|
|
5800
|
+
.../* @__PURE__ */ new Set([
|
|
5801
|
+
...this.lockHolders("mcp-server", target.name).map((h) => h.publisher),
|
|
5802
|
+
target.publisher?.replace(/^@/, "")
|
|
5803
|
+
])
|
|
5804
|
+
].filter((p) => !!p) : [];
|
|
4819
5805
|
const changed = this.removeResolved(target, { force: opts?.force, kept, ownedBy });
|
|
5806
|
+
const droppedSettings = changed && mcpPublishers.length > 0 ? this.dropRemovedMcpServerSettings(target.name, mcpPublishers) : [];
|
|
4820
5807
|
const keptNote = {
|
|
4821
5808
|
...kept.length > 0 ? { kept } : {},
|
|
4822
|
-
...ownedBy.length > 0 ? { ownedBy } : {}
|
|
5809
|
+
...ownedBy.length > 0 ? { ownedBy } : {},
|
|
5810
|
+
...droppedSettings.length > 0 ? { droppedSettings } : {}
|
|
4823
5811
|
};
|
|
4824
5812
|
const pruned = changed && requiredRefs.length > 0 ? await this.pruneOrphans(requiredRefs) : [];
|
|
4825
5813
|
if (changed && (target.kind === "mcp-server" || pruned.some((r) => r.startsWith("mcp-server:")))) {
|
|
@@ -4876,6 +5864,25 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4876
5864
|
return kind ? { kind, name } : null;
|
|
4877
5865
|
}
|
|
4878
5866
|
}
|
|
5867
|
+
/**
|
|
5868
|
+
* The refs among `refs` (`skill:@publisher/name`, `skill:name`, with or without `#version`)
|
|
5869
|
+
* that are not deployed for this manager's harnesses and scope, judged as `check` judges a
|
|
5870
|
+
* requirement. A ref that does not parse or names a kind this build does not install is
|
|
5871
|
+
* never reported. `run <flow> --dry-run` names the node skills a flow still lacks (p1757).
|
|
5872
|
+
*/
|
|
5873
|
+
undeployedSkills(refs) {
|
|
5874
|
+
const out = [];
|
|
5875
|
+
for (const ref of refs) {
|
|
5876
|
+
const parsed = parseRefTolerant(ref);
|
|
5877
|
+
if (!parsed) continue;
|
|
5878
|
+
const component = this.installTarget(parsed.kind);
|
|
5879
|
+
if (!component) continue;
|
|
5880
|
+
if (!isDeployed(component, parsed.name, this.harness, this.global, this.projectDir)) {
|
|
5881
|
+
out.push(ref);
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
return out;
|
|
5885
|
+
}
|
|
4879
5886
|
/**
|
|
4880
5887
|
* The reverse guard of {@link removeWithDependencies}: which other locked assets (and
|
|
4881
5888
|
* declared presets) require `target`. Their requirements are read the way {@link lockedGraph}
|
|
@@ -4987,6 +5994,40 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
4987
5994
|
}
|
|
4988
5995
|
return changed;
|
|
4989
5996
|
}
|
|
5997
|
+
/**
|
|
5998
|
+
* After an `mcp-server` asset left the project: drop what would keep it rendered. Its staged
|
|
5999
|
+
* store bytes under `.skaile/cache/store/<publisher>/mcp-server/<name>/` go (an `mcp_servers:`
|
|
6000
|
+
* entry of the same id would otherwise keep loading them), and so does an `mcp_servers:` entry
|
|
6001
|
+
* that only sets values for it: `env` and the like, with no `command`, `url` or recipe of its
|
|
6002
|
+
* own. An entry that can run alone is the user's own server and stays.
|
|
6003
|
+
*
|
|
6004
|
+
* @returns The dropped `mcp_servers` entries, as `mcp_servers entry "<id>"`.
|
|
6005
|
+
*/
|
|
6006
|
+
dropRemovedMcpServerSettings(name, publishers) {
|
|
6007
|
+
for (const publisher of publishers) {
|
|
6008
|
+
rmSync(join(this.storeCacheDir, publisher, "mcp-server", name), {
|
|
6009
|
+
recursive: true,
|
|
6010
|
+
force: true
|
|
6011
|
+
});
|
|
6012
|
+
}
|
|
6013
|
+
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
6014
|
+
if (!existsSync(configPath)) return [];
|
|
6015
|
+
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
6016
|
+
if (!Array.isArray(raw.mcp_servers)) return [];
|
|
6017
|
+
const dropped = [];
|
|
6018
|
+
const standalone = ["command", "url", "recipe", "launcher"];
|
|
6019
|
+
raw.mcp_servers = raw.mcp_servers.filter((entry) => {
|
|
6020
|
+
const e = entry;
|
|
6021
|
+
if (!e || typeof e.id !== "string" || unscopedName(e.id) !== name) return true;
|
|
6022
|
+
if (standalone.some((k) => e[k] !== void 0 && e[k] !== null)) return true;
|
|
6023
|
+
dropped.push(`mcp_servers entry "${e.id}"`);
|
|
6024
|
+
return false;
|
|
6025
|
+
});
|
|
6026
|
+
if (dropped.length === 0) return [];
|
|
6027
|
+
if (raw.mcp_servers.length === 0) delete raw.mcp_servers;
|
|
6028
|
+
writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
6029
|
+
return dropped;
|
|
6030
|
+
}
|
|
4990
6031
|
/**
|
|
4991
6032
|
* After `remove --global --harness X`: the harnesses outside `X` that still hold a copy of the
|
|
4992
6033
|
* asset, or `undefined` when none does (or this is not a harness-limited global remove).
|
|
@@ -5158,7 +6199,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5158
6199
|
}
|
|
5159
6200
|
}
|
|
5160
6201
|
if (best) return best;
|
|
5161
|
-
const
|
|
6202
|
+
const lockedLibraries = await this.lockedLibraryNames();
|
|
6203
|
+
const lib = await this.resolveLibraryEntries([ref], {
|
|
6204
|
+
quiet: true,
|
|
6205
|
+
...lockedLibraries.size > 0 ? { lockedLibraries } : {}
|
|
6206
|
+
});
|
|
5162
6207
|
return lib.entries[0] ?? null;
|
|
5163
6208
|
}
|
|
5164
6209
|
/**
|
|
@@ -5211,18 +6256,18 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5211
6256
|
}
|
|
5212
6257
|
/**
|
|
5213
6258
|
* Tracked assets (`skaile.lock.yaml` or declared in `skaile.yaml`) whose copy
|
|
5214
|
-
* still sits in the project
|
|
5215
|
-
*
|
|
5216
|
-
* project
|
|
5217
|
-
*
|
|
6259
|
+
* still sits in the project dirs of another harness (`.claude/…`, `.omp/…`,
|
|
6260
|
+
* `.codex/…`) although this manager deploys to {@link AssetManager.harness}.
|
|
6261
|
+
* A project that moved with `skaile harness set` holds orphans there that
|
|
6262
|
+
* `remove` / `clean` / `list` no longer look at.
|
|
5218
6263
|
*
|
|
5219
|
-
* Read-only: nothing is deleted. Empty for
|
|
5220
|
-
*
|
|
6264
|
+
* Read-only: nothing is deleted. Empty for global mode and for kinds whose
|
|
6265
|
+
* dir is shared with the active target (flows).
|
|
5221
6266
|
*
|
|
5222
|
-
* @returns One {@link
|
|
6267
|
+
* @returns One {@link StaleHarnessCopy} per stale copy, sorted by harness, kind, then name.
|
|
5223
6268
|
*/
|
|
5224
|
-
|
|
5225
|
-
if (this.global
|
|
6269
|
+
listStaleHarnessCopies() {
|
|
6270
|
+
if (this.global) return [];
|
|
5226
6271
|
const tracked = /* @__PURE__ */ new Map();
|
|
5227
6272
|
const track = (kind, name) => {
|
|
5228
6273
|
const component = kind ? this.installTarget(kind) : void 0;
|
|
@@ -5248,20 +6293,34 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5248
6293
|
} catch {
|
|
5249
6294
|
}
|
|
5250
6295
|
const out = [];
|
|
5251
|
-
for (const
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
lstatSync(p);
|
|
5257
|
-
return true;
|
|
5258
|
-
} catch {
|
|
5259
|
-
return false;
|
|
6296
|
+
for (const other of SUPPORTED_HARNESSES) {
|
|
6297
|
+
if (other === this.harness) continue;
|
|
6298
|
+
for (const { kind, name, component: k } of tracked.values()) {
|
|
6299
|
+
if (deployedDir(k, name, other, false, this.projectDir) === deployedDir(k, name, this.harness, false, this.projectDir)) {
|
|
6300
|
+
continue;
|
|
5260
6301
|
}
|
|
5261
|
-
|
|
5262
|
-
|
|
6302
|
+
const found = deployedPaths(k, name, other, false, this.projectDir).find((p) => {
|
|
6303
|
+
try {
|
|
6304
|
+
lstatSync(p);
|
|
6305
|
+
return true;
|
|
6306
|
+
} catch {
|
|
6307
|
+
return false;
|
|
6308
|
+
}
|
|
6309
|
+
});
|
|
6310
|
+
if (found) out.push({ harness: other, kind, name, path: found });
|
|
6311
|
+
}
|
|
5263
6312
|
}
|
|
5264
|
-
return out.sort(
|
|
6313
|
+
return out.sort(
|
|
6314
|
+
(a, b) => a.harness.localeCompare(b.harness) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
|
|
6315
|
+
);
|
|
6316
|
+
}
|
|
6317
|
+
/**
|
|
6318
|
+
* @deprecated Use {@link AssetManager.listStaleHarnessCopies}, which covers every
|
|
6319
|
+
* other harness folder. This returns only its `.claude/` rows (the old default
|
|
6320
|
+
* deploy dir), in the old shape.
|
|
6321
|
+
*/
|
|
6322
|
+
listLegacyClaudeDeployed() {
|
|
6323
|
+
return this.listStaleHarnessCopies().filter((c) => c.harness === "claude-code").map(({ kind, name, path: path2 }) => ({ kind, name, path: path2 }));
|
|
5265
6324
|
}
|
|
5266
6325
|
/**
|
|
5267
6326
|
* List the **declared** top-level dependencies from `skaile.yaml`, each tagged
|
|
@@ -5325,7 +6384,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5325
6384
|
state = "declared";
|
|
5326
6385
|
} else if (parsed.kind === "mcp-server") {
|
|
5327
6386
|
const wired = await wiredMcpNames();
|
|
5328
|
-
const isWired = wired.has(parsed.name) || wired.has(unscopedName(parsed.name))
|
|
6387
|
+
const isWired = wired.has(parsed.name) || wired.has(unscopedName(parsed.name)) || !this.global && await mcpCommittedRenderIntact(
|
|
6388
|
+
this.projectDir,
|
|
6389
|
+
this.harness,
|
|
6390
|
+
unscopedName(parsed.name)
|
|
6391
|
+
);
|
|
5329
6392
|
const skippedReason = Object.entries(
|
|
5330
6393
|
await readSkippedMcpServers(this.projectDir, this.harness)
|
|
5331
6394
|
).find(([id]) => id === parsed.name || unscopedName(id) === parsed.name)?.[1];
|
|
@@ -5670,11 +6733,12 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5670
6733
|
if (!lock) return [];
|
|
5671
6734
|
const bare = (s) => s.includes("#") ? s.slice(0, s.indexOf("#")) : s;
|
|
5672
6735
|
const asked = parseRefTolerant(ref);
|
|
6736
|
+
const askedPublisher = asked?.publisher?.replace(/^@/, "");
|
|
5673
6737
|
const matchesIdentity = (k) => {
|
|
5674
6738
|
if (bare(k) === bare(ref)) return true;
|
|
5675
|
-
if (!asked
|
|
6739
|
+
if (!asked) return false;
|
|
5676
6740
|
const locked = parseCanonicalLockKey(k);
|
|
5677
|
-
return locked.kind === asked.kind && locked.name === asked.name;
|
|
6741
|
+
return locked.kind === asked.kind && locked.name === asked.name && (!askedPublisher || locked.publisher === askedPublisher);
|
|
5678
6742
|
};
|
|
5679
6743
|
const key = lock.assets[ref] ? ref : Object.keys(lock.assets).find(matchesIdentity);
|
|
5680
6744
|
if (!key) return asked?.kind === "preset" ? this.whyPreset(lock, asked) : [];
|
|
@@ -5810,6 +6874,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5810
6874
|
const libraryAssets = Object.entries(lock.assets).filter(
|
|
5811
6875
|
([, e]) => e.library && !e.library.installed
|
|
5812
6876
|
);
|
|
6877
|
+
const declaredPins = this.declaredPinsByIdentity();
|
|
5813
6878
|
if (libraryAssets.length > 0) {
|
|
5814
6879
|
const snap = await this.loadLibraryIndex();
|
|
5815
6880
|
const diskHashes = /* @__PURE__ */ new Map();
|
|
@@ -5835,6 +6900,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5835
6900
|
{ kind, publisher, name },
|
|
5836
6901
|
found.version
|
|
5837
6902
|
);
|
|
6903
|
+
const declaredPin = declaredPins.get(`${kind}:${publisher}/${name}`);
|
|
6904
|
+
if (declaredPin && diskVersion && !matchPin(declaredPin, diskVersion)) continue;
|
|
5838
6905
|
if (diskVersion && !isPseudoVersion(diskVersion) && gt(coerceVersion(diskVersion), coerceVersion(origin.version))) {
|
|
5839
6906
|
result.push({
|
|
5840
6907
|
kind,
|
|
@@ -5873,6 +6940,26 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5873
6940
|
library: lookup.name,
|
|
5874
6941
|
contentChanged: true
|
|
5875
6942
|
});
|
|
6943
|
+
continue;
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
if (entry.composed && kind === "agent") {
|
|
6947
|
+
const srcDir = statSync(found.source).isDirectory() ? found.source : dirname(found.source);
|
|
6948
|
+
const now = new Map(
|
|
6949
|
+
sourceComposedFiles(srcDir, name, this.projectDir).map((f) => [f.path, f.sha256])
|
|
6950
|
+
);
|
|
6951
|
+
const locked = new Map(entry.composed.map((f) => [f.path, f.sha256]));
|
|
6952
|
+
const same = now.size === locked.size && [...now].every(([f, h]) => locked.get(f) === h);
|
|
6953
|
+
if (!same) {
|
|
6954
|
+
result.push({
|
|
6955
|
+
kind,
|
|
6956
|
+
name,
|
|
6957
|
+
publisher,
|
|
6958
|
+
currentVersion: origin.version,
|
|
6959
|
+
behind: 0,
|
|
6960
|
+
library: lookup.name,
|
|
6961
|
+
contentChanged: true
|
|
6962
|
+
});
|
|
5876
6963
|
}
|
|
5877
6964
|
}
|
|
5878
6965
|
}
|
|
@@ -5882,6 +6969,23 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5882
6969
|
await this.addPinnedRows(lock, result);
|
|
5883
6970
|
return this.addLockGapRows(result);
|
|
5884
6971
|
}
|
|
6972
|
+
/**
|
|
6973
|
+
* The `#pin` of every publisher-qualified `skaile.yaml` dependency, keyed `kind:publisher/name`
|
|
6974
|
+
* (no scope sigil). `update` honours it, so `outdated` must not list a move it keeps.
|
|
6975
|
+
*/
|
|
6976
|
+
declaredPinsByIdentity() {
|
|
6977
|
+
const pins = /* @__PURE__ */ new Map();
|
|
6978
|
+
for (const dep of this.loadConfig().dependencies) {
|
|
6979
|
+
try {
|
|
6980
|
+
const p = parseAssetRef(dep);
|
|
6981
|
+
if (p.pin && p.publisher) {
|
|
6982
|
+
pins.set(`${p.kind}:${p.publisher.replace(/^@/, "")}/${p.name}`, p.pin);
|
|
6983
|
+
}
|
|
6984
|
+
} catch {
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
return pins;
|
|
6988
|
+
}
|
|
5885
6989
|
/**
|
|
5886
6990
|
* List a locked asset whose `skaile.yaml` ref carries a `#version` pin while a source, a
|
|
5887
6991
|
* library or a store holds a newer one: `locked 0.7.0 -> 0.9.0 (pinned)`. The pin is the
|
|
@@ -5986,20 +7090,20 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
5986
7090
|
async addLibraryMoves(libraryAssets, rows) {
|
|
5987
7091
|
const refs = [];
|
|
5988
7092
|
const lockedIn = /* @__PURE__ */ new Map();
|
|
7093
|
+
const declaredPins = this.declaredPinsByIdentity();
|
|
5989
7094
|
for (const [key, entry] of libraryAssets) {
|
|
5990
7095
|
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
5991
7096
|
if (!publisher || !kind || !name || !entry.library) continue;
|
|
5992
7097
|
const ref = `${kind}:@${publisher}/${name}`;
|
|
5993
|
-
|
|
7098
|
+
const pin = declaredPins.get(`${kind}:${publisher}/${name}`);
|
|
7099
|
+
refs.push(pin ? `${ref}#${pin}` : ref);
|
|
5994
7100
|
lockedIn.set(ref, { name: entry.library.name, key });
|
|
5995
7101
|
}
|
|
5996
7102
|
if (refs.length === 0) return;
|
|
5997
7103
|
let res;
|
|
7104
|
+
const preferLocked = await this.lockedPreference();
|
|
5998
7105
|
try {
|
|
5999
|
-
res = await this.resolveLibraryEntries(refs, {
|
|
6000
|
-
quiet: true,
|
|
6001
|
-
preferLocked: await this.lockedPreference()
|
|
6002
|
-
});
|
|
7106
|
+
res = await this.resolveLibraryEntries(refs, { quiet: true, preferLocked });
|
|
6003
7107
|
} catch {
|
|
6004
7108
|
return;
|
|
6005
7109
|
}
|
|
@@ -6009,9 +7113,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6009
7113
|
const ref = `${e.kind}:@${publisher}/${e.name}`;
|
|
6010
7114
|
const from = lockedIn.get(ref);
|
|
6011
7115
|
if (!to || !from || from.name === to) continue;
|
|
7116
|
+
const renamedOnly = preferLocked.libraries.get(`${e.kind}:${publisher}/${e.name}`) === to;
|
|
6012
7117
|
const row = rows.find(
|
|
6013
7118
|
(r) => r.kind === e.kind && r.name === e.name && r.publisher.replace(/^@/, "") === publisher
|
|
6014
7119
|
);
|
|
7120
|
+
if (renamedOnly && !row) continue;
|
|
6015
7121
|
if (row) {
|
|
6016
7122
|
row.library = from.name;
|
|
6017
7123
|
row.movesTo = to;
|
|
@@ -6143,7 +7249,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6143
7249
|
(r) => unscopedName(r.drift.id) === unscopedName(parsed.name)
|
|
6144
7250
|
);
|
|
6145
7251
|
if (!row) return { status: "identical" };
|
|
6146
|
-
const fileSetDiffers = row.drift.extra.length > 0 || row.drift.missing.
|
|
7252
|
+
const fileSetDiffers = row.drift.extra.length > 0 || row.drift.missing.length > 0;
|
|
6147
7253
|
return {
|
|
6148
7254
|
status: "differs",
|
|
6149
7255
|
diff: mcpDriftDiff(row.drift),
|
|
@@ -6151,8 +7257,10 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6151
7257
|
};
|
|
6152
7258
|
}
|
|
6153
7259
|
}
|
|
6154
|
-
const
|
|
7260
|
+
const libraryEntry = await this.lockedLibraryEntry(parsed);
|
|
7261
|
+
const entry = libraryEntry ?? this.lockedSourceEntry(parsed) ?? await this.info(ref);
|
|
6155
7262
|
if (!entry || !existsSync(entry.source)) return { status: "not-found" };
|
|
7263
|
+
const toLabel = libraryEntry ? "library" : "source";
|
|
6156
7264
|
const wantPublisher = parsed.publisher?.replace(/^@/, "");
|
|
6157
7265
|
if (wantPublisher) {
|
|
6158
7266
|
const lockedHere = Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey).filter((id) => id.kind === parsed.kind && id.name === parsed.name);
|
|
@@ -6170,7 +7278,11 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6170
7278
|
parts.push(diffDeployedAgent(entry, opts, inst));
|
|
6171
7279
|
}
|
|
6172
7280
|
if (parts.every((d) => d === null)) return { status: "not-deployed" };
|
|
6173
|
-
const diff =
|
|
7281
|
+
const diff = labelDiffHeaders(
|
|
7282
|
+
parts.filter((d) => d !== null).join(""),
|
|
7283
|
+
"deployed",
|
|
7284
|
+
"rendered"
|
|
7285
|
+
);
|
|
6174
7286
|
return diff === "" ? { status: "identical" } : { status: "differs", diff };
|
|
6175
7287
|
}
|
|
6176
7288
|
const deployedPath = join(
|
|
@@ -6191,7 +7303,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6191
7303
|
);
|
|
6192
7304
|
}
|
|
6193
7305
|
}
|
|
6194
|
-
return diffs.length > 0 ? { status: "differs", diff: diffs.join("") } : { status: "identical" };
|
|
7306
|
+
return diffs.length > 0 ? { status: "differs", diff: labelDiffHeaders(diffs.join(""), "deployed", toLabel) } : { status: "identical" };
|
|
6195
7307
|
}
|
|
6196
7308
|
/**
|
|
6197
7309
|
* The agent instances of `skaile.yaml` whose `ref` names `want` (an `agent:` ref, matched on
|
|
@@ -6241,7 +7353,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6241
7353
|
provenanceIndex,
|
|
6242
7354
|
overrides,
|
|
6243
7355
|
stores,
|
|
6244
|
-
declaredRefs: dependencies
|
|
7356
|
+
declaredRefs: dependencies,
|
|
7357
|
+
lockedOrigin: this.lockedOriginVersions()
|
|
6245
7358
|
});
|
|
6246
7359
|
this.assertSourceCommitsPublished(resolved, repositories);
|
|
6247
7360
|
const libraryLock = await this.libraryLockEntries(lib);
|
|
@@ -6250,6 +7363,12 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6250
7363
|
for (const [key, entry] of Object.entries(lockData.assets)) {
|
|
6251
7364
|
const rendered = prior?.assets[key]?.rendered;
|
|
6252
7365
|
if (rendered) entry.rendered = rendered;
|
|
7366
|
+
const copied = prior?.assets[key]?.copied;
|
|
7367
|
+
if (copied && prior?.assets[key]?.library?.sha256 === entry.library?.sha256) {
|
|
7368
|
+
entry.copied = copied;
|
|
7369
|
+
}
|
|
7370
|
+
const composed = prior?.assets[key]?.composed;
|
|
7371
|
+
if (composed) entry.composed = composed;
|
|
6253
7372
|
}
|
|
6254
7373
|
writeLock(this.lockPath, lockData);
|
|
6255
7374
|
return lockData;
|
|
@@ -6363,15 +7482,30 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6363
7482
|
const { kind, name, publisher } = parseCanonicalLockKey(key);
|
|
6364
7483
|
if (kind && name && publisher) lockedPublisher[`${kind}:${name}`] = publisher;
|
|
6365
7484
|
}
|
|
7485
|
+
let librarySnap;
|
|
6366
7486
|
for (const entry of deployed) {
|
|
6367
7487
|
const publisher = lockedPublisher[`${entry.kind}:${entry.name}`];
|
|
6368
7488
|
const full = await this.info(
|
|
6369
7489
|
publisher ? `${entry.kind}:@${publisher}/${entry.name}` : `${entry.kind}:${entry.name}`
|
|
6370
7490
|
);
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
7491
|
+
let restorable = false;
|
|
7492
|
+
if (!full && publisher !== void 0) {
|
|
7493
|
+
for (const [key, e] of Object.entries(lock?.assets ?? {})) {
|
|
7494
|
+
const id = parseCanonicalLockKey(key);
|
|
7495
|
+
if (id.kind !== entry.kind || id.name !== entry.name || id.publisher !== publisher) {
|
|
7496
|
+
continue;
|
|
7497
|
+
}
|
|
7498
|
+
if (e.source !== void 0) {
|
|
7499
|
+
restorable = true;
|
|
7500
|
+
break;
|
|
7501
|
+
}
|
|
7502
|
+
librarySnap ??= { snap: await this.loadLibraryIndex() };
|
|
7503
|
+
if (await this.restoredFromLockUrl(key, e, librarySnap.snap)) {
|
|
7504
|
+
restorable = true;
|
|
7505
|
+
break;
|
|
7506
|
+
}
|
|
7507
|
+
}
|
|
7508
|
+
}
|
|
6375
7509
|
if (!full && !restorable) {
|
|
6376
7510
|
if (publisher) {
|
|
6377
7511
|
const canonical = `${entry.kind}:@${publisher}/${entry.name}`;
|
|
@@ -6554,7 +7688,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6554
7688
|
return want !== null && gaps.some((g) => g.type === "library" && sameAsset(g.ref, want));
|
|
6555
7689
|
};
|
|
6556
7690
|
gaps = gaps.filter((g) => !(g.type === "requires" && libraryHeld(g.ref)));
|
|
6557
|
-
for (const gap of this.undeployedLockGaps(lock, named)) {
|
|
7691
|
+
for (const gap of await this.undeployedLockGaps(lock, named)) {
|
|
6558
7692
|
gaps.push(gap);
|
|
6559
7693
|
named.add(gapIdentity(gap.ref));
|
|
6560
7694
|
}
|
|
@@ -6576,7 +7710,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6576
7710
|
}),
|
|
6577
7711
|
...d.extra.map((f) => `${f} is not part of the asset`)
|
|
6578
7712
|
];
|
|
6579
|
-
const reason = `is locked, but its deployed copy differs from the lock (${parts.join("; ")}) \u2014 run \`skaile install --locked\` to restore it`;
|
|
7713
|
+
const reason = d.libraryChanged ? `is locked, but its deployed copy differs from the lock (${parts.join("; ")}) \u2014 the library holds other content now: copy your edit away, then run \`skaile update --force\` to replace it` : `is locked, but its deployed copy differs from the lock (${parts.join("; ")}) \u2014 run \`skaile install --locked\` to restore it`;
|
|
6580
7714
|
gaps.push({ type: "modified", ref: d.ref, reason, message: `${d.ref} ${reason}` });
|
|
6581
7715
|
named.add(gapIdentity(d.ref));
|
|
6582
7716
|
}
|
|
@@ -6609,7 +7743,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6609
7743
|
ref: s.url,
|
|
6610
7744
|
reason,
|
|
6611
7745
|
message: `source ${s.url} ${reason}`,
|
|
6612
|
-
manualFix: true
|
|
7746
|
+
manualFix: true,
|
|
7747
|
+
unreadableSource: true
|
|
6613
7748
|
});
|
|
6614
7749
|
}
|
|
6615
7750
|
for (const u of unboundAgentInstances(this.projectDir, this.harness)) {
|
|
@@ -6629,6 +7764,22 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6629
7764
|
)
|
|
6630
7765
|
};
|
|
6631
7766
|
}
|
|
7767
|
+
/**
|
|
7768
|
+
* The first `../` compose folder of the locked library agent `kind:name` whose library copy
|
|
7769
|
+
* changed since the lock, or `undefined` (project scope only). `install --locked` cannot
|
|
7770
|
+
* restore such an agent; `update` moves the lock to the library's copy.
|
|
7771
|
+
*/
|
|
7772
|
+
async lockedComposeDrift(lock, kind, name) {
|
|
7773
|
+
if (this.global) return void 0;
|
|
7774
|
+
const { entries } = await this.restoreLibraryLockEntries(lock);
|
|
7775
|
+
const entry = entries.find((x) => x.kind === kind && x.name === name);
|
|
7776
|
+
if (!entry) return void 0;
|
|
7777
|
+
try {
|
|
7778
|
+
return composedDriftRefs(entry, this.deployOptsFor(this.harness))[0];
|
|
7779
|
+
} catch {
|
|
7780
|
+
return void 0;
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
6632
7783
|
/**
|
|
6633
7784
|
* Why the `agent:` ref of agent instance `id` binds to nothing, as the phrase that follows
|
|
6634
7785
|
* the ref in a {@link LockGap}; see {@link AssetManager.unboundAgentAdvice}.
|
|
@@ -6652,6 +7803,15 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6652
7803
|
return k.kind === "agent" && k.name === parsedRef?.name && (!publisher || !k.publisher || k.publisher === publisher);
|
|
6653
7804
|
});
|
|
6654
7805
|
if (lockedAgentKey !== void 0) {
|
|
7806
|
+
const lock = this.tryReadLock();
|
|
7807
|
+
const locked = parseCanonicalLockKey(lockedAgentKey);
|
|
7808
|
+
const composeRef = lock && locked.kind && locked.name ? await this.lockedComposeDrift(lock, locked.kind, locked.name) : void 0;
|
|
7809
|
+
if (composeRef) {
|
|
7810
|
+
return {
|
|
7811
|
+
lead: `its agent composes ${composeRef}, whose library copy changed since the lock`,
|
|
7812
|
+
advice: `run \`skaile update\` (it moves the lock to the library's copy), ${drop}`
|
|
7813
|
+
};
|
|
7814
|
+
}
|
|
6655
7815
|
const libraryFix = await this.lockedAgentLibraryHolds(lockedAgentKey) ? void 0 : (await this.unresolvedFixes([ref])).get(ref);
|
|
6656
7816
|
if (libraryFix) {
|
|
6657
7817
|
const register = libraryFix.replace(
|
|
@@ -6835,7 +7995,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6835
7995
|
continue;
|
|
6836
7996
|
}
|
|
6837
7997
|
if (lookup.state === "unregistered") {
|
|
6838
|
-
|
|
7998
|
+
if (await this.restoredFromLockUrl(key, entry, snap)) continue;
|
|
7999
|
+
const reason2 = `is locked, but library "${unregisteredLibraryName(origin)}" is not registered (renamed or removed?) \u2014 ${unregisteredLibraryFix(origin, ref)}`;
|
|
6839
8000
|
gaps.push({ type: "library", ref, reason: reason2, message: `${ref} ${reason2}`, manualFix: true });
|
|
6840
8001
|
continue;
|
|
6841
8002
|
}
|
|
@@ -6855,12 +8016,15 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6855
8016
|
* The locked source assets whose source is no longer in `skaile.yaml` `sources:` (`source
|
|
6856
8017
|
* remove`, a hand edit), keyed by lock key. ONE sentence for it: `check`, `doctor`, `outdated`,
|
|
6857
8018
|
* `install`, `install --locked` and `update` all say it (p1062), and none re-clones the source.
|
|
6858
|
-
* A
|
|
6859
|
-
*
|
|
8019
|
+
* A GitHub lock entry that no source declares counts as store-supplied only when a store
|
|
8020
|
+
* lists the ref (OQ3): a configured store alone proves nothing, as it may never have held
|
|
8021
|
+
* the asset. `check`, `doctor` and `outdated` read the store's cached catalog and never touch
|
|
8022
|
+
* the network; `install` and `update` (`online`) ask the store itself.
|
|
6860
8023
|
*
|
|
6861
8024
|
* @param inScope - Only these lock keys count (`install` prunes an undeclared entry as usual).
|
|
8025
|
+
* @param online - Ask the stores when the cached catalog does not list the ref.
|
|
6862
8026
|
*/
|
|
6863
|
-
async orphanedSourceGaps(inScope) {
|
|
8027
|
+
async orphanedSourceGaps(inScope, online = false) {
|
|
6864
8028
|
const out = /* @__PURE__ */ new Map();
|
|
6865
8029
|
let config;
|
|
6866
8030
|
try {
|
|
@@ -6868,23 +8032,53 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6868
8032
|
} catch {
|
|
6869
8033
|
return out;
|
|
6870
8034
|
}
|
|
6871
|
-
const
|
|
8035
|
+
const stores = [...config.stores, ...await this.globalStoreFallback(config.stores, false)];
|
|
6872
8036
|
const registered = new Set(config.sources.map((s) => sourceCacheKey(s.url)));
|
|
6873
8037
|
for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
|
|
6874
8038
|
const url = entry.source?.url;
|
|
6875
8039
|
if (!url || entry.library || !GIT_URL.test(url)) continue;
|
|
6876
8040
|
if (inScope && !inScope.has(key)) continue;
|
|
6877
8041
|
if (normalizeRepoUrl(url) === normalizeRepoUrl(FACTORY_SOURCE_URL)) continue;
|
|
6878
|
-
const { publisher, kind, name } = parseCanonicalLockKey(key);
|
|
8042
|
+
const { publisher, kind, name, version } = parseCanonicalLockKey(key);
|
|
6879
8043
|
if (!publisher || !kind || !name) continue;
|
|
6880
8044
|
if (registered.has(sourceCacheKey(url))) continue;
|
|
6881
|
-
if (storeBacked && GITHUB_REPO_URL.test(url)) continue;
|
|
6882
8045
|
const ref = `${kind}:@${publisher}/${name}`;
|
|
8046
|
+
const locked = version ? `${ref}#${version}` : void 0;
|
|
8047
|
+
if (GITHUB_REPO_URL.test(url) && await this.storeListsRef(stores, ref, online, locked))
|
|
8048
|
+
continue;
|
|
6883
8049
|
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
8050
|
out.set(key, { type: "source", ref, reason, message: `${ref} ${reason}`, manualFix: true });
|
|
6885
8051
|
}
|
|
6886
8052
|
return out;
|
|
6887
8053
|
}
|
|
8054
|
+
/**
|
|
8055
|
+
* `true` when one of `stores` lists `ref` (a canonical lock key): its cached catalog holds it
|
|
8056
|
+
* or, with `online`, the store answers for it. An injected store fetcher is the store client
|
|
8057
|
+
* itself, so it is asked either way. A store an online install cannot ask (unreachable,
|
|
8058
|
+
* air-gapped, `--no-catalog`) may hold the ref; `check` never asks, so a store lists only what
|
|
8059
|
+
* its cached catalog holds.
|
|
8060
|
+
*/
|
|
8061
|
+
async storeListsRef(stores, ref, online, lockedRef) {
|
|
8062
|
+
for (const store of stores) {
|
|
8063
|
+
try {
|
|
8064
|
+
if (this.injectedStoreFetcher) {
|
|
8065
|
+
if (await this.injectedStoreFetcher.getInstallManifest(store.url, ref)) return true;
|
|
8066
|
+
continue;
|
|
8067
|
+
}
|
|
8068
|
+
const remote = this.remoteStore(store.url);
|
|
8069
|
+
if (remote.holdsCached(ref)) return true;
|
|
8070
|
+
if (!online) {
|
|
8071
|
+
if (lockedRef && remote.holdsCached(lockedRef)) return true;
|
|
8072
|
+
continue;
|
|
8073
|
+
}
|
|
8074
|
+
if (this.catalogUrlOverride === "") return true;
|
|
8075
|
+
if (await (lockedRef ? remote.revalidate(lockedRef) : remote.resolve(ref))) return true;
|
|
8076
|
+
} catch {
|
|
8077
|
+
if (online) return true;
|
|
8078
|
+
}
|
|
8079
|
+
}
|
|
8080
|
+
return false;
|
|
8081
|
+
}
|
|
6888
8082
|
/**
|
|
6889
8083
|
* Locked source assets whose source cannot supply them any more: the source is no longer in
|
|
6890
8084
|
* `skaile.yaml` `sources:` (`source remove`, a hand edit), or it is there but now publishes
|
|
@@ -6936,13 +8130,55 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6936
8130
|
}
|
|
6937
8131
|
return out;
|
|
6938
8132
|
}
|
|
8133
|
+
/**
|
|
8134
|
+
* Record, on every library-origin entry of `lock` that is copied into a harness folder, the
|
|
8135
|
+
* files of the deployed copy and their sha256 (`LockEntry.copied`), so a hand edit is named
|
|
8136
|
+
* even after the library moved past the lock (p1641). Call it after the deploy. An entry that
|
|
8137
|
+
* is unchanged since `prev` keeps its record, or stays without one: the deployed bytes may
|
|
8138
|
+
* carry an edit the deploy did not touch. Project mode only; a `--link` deploy and a patched
|
|
8139
|
+
* asset are not recorded.
|
|
8140
|
+
*/
|
|
8141
|
+
withCopiedFiles(lock, prev) {
|
|
8142
|
+
if (this.global) return lock;
|
|
8143
|
+
const { patches } = this.loadConfig();
|
|
8144
|
+
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
8145
|
+
if (!entry.library || entry.library.installed) continue;
|
|
8146
|
+
const before = prev?.assets[key];
|
|
8147
|
+
if (before && before.library?.sha256 === entry.library.sha256) {
|
|
8148
|
+
if (before.copied) entry.copied = before.copied;
|
|
8149
|
+
else delete entry.copied;
|
|
8150
|
+
continue;
|
|
8151
|
+
}
|
|
8152
|
+
delete entry.copied;
|
|
8153
|
+
const { kind, name } = parseCanonicalLockKey(key);
|
|
8154
|
+
const component = kind ? this.installTarget(kind) : void 0;
|
|
8155
|
+
if (!kind || !name || !component || component === "agent") continue;
|
|
8156
|
+
if (patches[`${kind}:${name}`]) continue;
|
|
8157
|
+
const dest = deployedDir(component, name, this.harness, this.global, this.projectDir);
|
|
8158
|
+
try {
|
|
8159
|
+
const st = lstatSync(dest);
|
|
8160
|
+
if (st.isSymbolicLink() || !st.isDirectory()) continue;
|
|
8161
|
+
const rows = listAssetFiles(dest).map((path2) => ({
|
|
8162
|
+
path: path2,
|
|
8163
|
+
sha256: computeHash(join(dest, path2))
|
|
8164
|
+
}));
|
|
8165
|
+
if (rows.length > 0) entry.copied = rows.sort((a, b) => a.path.localeCompare(b.path));
|
|
8166
|
+
} catch {
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
8169
|
+
return lock;
|
|
8170
|
+
}
|
|
6939
8171
|
/**
|
|
6940
8172
|
* Record, on every agent entry of `lock` that has none, the files the agent's harness
|
|
6941
8173
|
* rendering occupies and their sha256. Call it after the deploy, so the bytes hashed are the
|
|
6942
8174
|
* ones `install` just wrote; {@link lockedCopyDrift} compares later edits with them.
|
|
6943
8175
|
* Entries that carry a record (kept from an earlier lock) are left as they are.
|
|
8176
|
+
*
|
|
8177
|
+
* @param keepComposedOf - The lock an `install` read: an agent whose `../` compose folder is
|
|
8178
|
+
* not in the cache keeps that lock's `composed:` record. Omitted by `update` and `add`,
|
|
8179
|
+
* which copy the library's current bytes.
|
|
6944
8180
|
*/
|
|
6945
|
-
withRenderedAgents(lock) {
|
|
8181
|
+
withRenderedAgents(lock, keepComposedOf) {
|
|
6946
8182
|
if (this.global) return lock;
|
|
6947
8183
|
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
6948
8184
|
if (entry.rendered || entry.library?.installed) continue;
|
|
@@ -6951,6 +8187,18 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
6951
8187
|
const rendered = this.renderedAgentFiles(name);
|
|
6952
8188
|
if (rendered.length > 0) entry.rendered = rendered;
|
|
6953
8189
|
}
|
|
8190
|
+
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
8191
|
+
if (entry.library?.installed) continue;
|
|
8192
|
+
const { kind, name } = parseCanonicalLockKey(key);
|
|
8193
|
+
if (!kind || !name || this.installTarget(kind) !== "agent") continue;
|
|
8194
|
+
const composed = cachedComposedFiles(name, this.projectDir);
|
|
8195
|
+
if (composed.length > 0) entry.composed = composed;
|
|
8196
|
+
else {
|
|
8197
|
+
const earlier = keepComposedOf?.assets[key]?.composed;
|
|
8198
|
+
if (earlier?.length) entry.composed = earlier;
|
|
8199
|
+
else delete entry.composed;
|
|
8200
|
+
}
|
|
8201
|
+
}
|
|
6954
8202
|
return this.withInstanceRenderings(lock);
|
|
6955
8203
|
}
|
|
6956
8204
|
/**
|
|
@@ -7129,6 +8377,73 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7129
8377
|
}
|
|
7130
8378
|
return out;
|
|
7131
8379
|
}
|
|
8380
|
+
/**
|
|
8381
|
+
* Hand edits in the deployed copy of a library asset whose library moved past the lock (a
|
|
8382
|
+
* version bump, or an in-place edit of the same version): {@link restoreLibraryLockEntries}
|
|
8383
|
+
* drops such an entry, so {@link lockedCopyDrift} would not see the edit and `update` would
|
|
8384
|
+
* replace the file without a word (p1641). `covered` holds the `kind:@publisher/name` of the
|
|
8385
|
+
* entries that were compared already. The lock's per-file record (`copied:`) names the edited
|
|
8386
|
+
* files; a lock without one (written before it existed) compares the deployed folder with the
|
|
8387
|
+
* one library hash and names the files that also differ from the library's new content.
|
|
8388
|
+
*/
|
|
8389
|
+
async changedLibraryCopyEdits(lock, covered, patches) {
|
|
8390
|
+
const out = [];
|
|
8391
|
+
let snap;
|
|
8392
|
+
const fileSets = /* @__PURE__ */ new Map();
|
|
8393
|
+
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
8394
|
+
const origin = entry.library;
|
|
8395
|
+
if (!origin || origin.installed) continue;
|
|
8396
|
+
const { kind, name, publisher } = parseCanonicalLockKey(key);
|
|
8397
|
+
if (!kind || !name || !publisher) continue;
|
|
8398
|
+
const ref = `${kind}:@${publisher}/${name}`;
|
|
8399
|
+
if (covered.has(ref) || patches[`${kind}:${name}`]) continue;
|
|
8400
|
+
const component = this.installTarget(kind);
|
|
8401
|
+
if (!component || component === "agent") continue;
|
|
8402
|
+
const dest = deployedDir(component, name, this.harness, this.global, this.projectDir);
|
|
8403
|
+
try {
|
|
8404
|
+
const st = lstatSync(dest);
|
|
8405
|
+
if (st.isSymbolicLink() || !st.isDirectory()) continue;
|
|
8406
|
+
} catch {
|
|
8407
|
+
continue;
|
|
8408
|
+
}
|
|
8409
|
+
let modified = [];
|
|
8410
|
+
if (entry.copied && entry.copied.length > 0) {
|
|
8411
|
+
for (const f of entry.copied) {
|
|
8412
|
+
const file = join(dest, f.path);
|
|
8413
|
+
if (!existsSync(file)) continue;
|
|
8414
|
+
try {
|
|
8415
|
+
if (computeHash(file) !== f.sha256) modified.push(f.path);
|
|
8416
|
+
} catch {
|
|
8417
|
+
modified.push(f.path);
|
|
8418
|
+
}
|
|
8419
|
+
}
|
|
8420
|
+
} else if (origin.sha256) {
|
|
8421
|
+
snap ??= await this.loadLibraryIndex();
|
|
8422
|
+
const lookup = this.lockedLibrary(origin, snap, { kind, publisher, name });
|
|
8423
|
+
if (lookup.state !== "ok") continue;
|
|
8424
|
+
const found = (await scanDirectory(lookup.path, publisher, { knownKinds: this.knownKinds })).find((x) => x.kind === kind && x.name === name);
|
|
8425
|
+
if (!found) continue;
|
|
8426
|
+
const current = withLibraryFiles(found, lookup.path, fileSets, this.registry);
|
|
8427
|
+
const assetRoot = statSync(current.source).isDirectory() ? current.source : dirname(current.source);
|
|
8428
|
+
const hash = computeDeterministicHash(
|
|
8429
|
+
dest,
|
|
8430
|
+
listAssetFiles(dest),
|
|
8431
|
+
posix.normalize(relative(lookup.path, assetRoot).split(sep).join("/"))
|
|
8432
|
+
);
|
|
8433
|
+
if (hash === origin.sha256) continue;
|
|
8434
|
+
modified = deployedAssetDrift(current, {
|
|
8435
|
+
registry: this.registry,
|
|
8436
|
+
harness: this.harness,
|
|
8437
|
+
global: this.global,
|
|
8438
|
+
cwd: this.projectDir
|
|
8439
|
+
})?.modified ?? [];
|
|
8440
|
+
}
|
|
8441
|
+
if (modified.length > 0) {
|
|
8442
|
+
out.push({ ref, key, modified, extra: [], library: true, libraryChanged: true });
|
|
8443
|
+
}
|
|
8444
|
+
}
|
|
8445
|
+
return out;
|
|
8446
|
+
}
|
|
7132
8447
|
/**
|
|
7133
8448
|
* Locked assets whose deployed copy differs from the bytes the lock covers: a file edited by
|
|
7134
8449
|
* hand, or a file the asset does not ship. One row per asset, `ref` being `kind:@publisher/name`
|
|
@@ -7172,6 +8487,15 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7172
8487
|
const ref = `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`;
|
|
7173
8488
|
out.push({ ref, key: keyOf.get(ref) ?? ref, ...drift, library: true });
|
|
7174
8489
|
}
|
|
8490
|
+
out.push(
|
|
8491
|
+
...await this.changedLibraryCopyEdits(
|
|
8492
|
+
lock,
|
|
8493
|
+
new Set(
|
|
8494
|
+
entries.map((e) => `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`)
|
|
8495
|
+
),
|
|
8496
|
+
patches
|
|
8497
|
+
)
|
|
8498
|
+
);
|
|
7175
8499
|
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
7176
8500
|
if (!entry.source || entry.files.length === 0) continue;
|
|
7177
8501
|
const { kind, name } = parseCanonicalLockKey(key);
|
|
@@ -7206,12 +8530,13 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7206
8530
|
out.push({ ref: refOf(key), key, modified, extra, library: false });
|
|
7207
8531
|
}
|
|
7208
8532
|
for (const [key, entry] of Object.entries(lock.assets)) {
|
|
7209
|
-
|
|
8533
|
+
const recorded = [...entry.rendered ?? [], ...entry.composed ?? []];
|
|
8534
|
+
if (recorded.length === 0 || entry.library?.installed) continue;
|
|
7210
8535
|
const { kind, name } = parseCanonicalLockKey(key);
|
|
7211
8536
|
if (!kind || !name || this.installTarget(kind) !== "agent") continue;
|
|
7212
8537
|
if (patches[`${kind}:${name}`]) continue;
|
|
7213
8538
|
const modified = [];
|
|
7214
|
-
for (const f of
|
|
8539
|
+
for (const f of recorded) {
|
|
7215
8540
|
const file = join(this.projectDir, f.path);
|
|
7216
8541
|
if (!existsSync(file)) continue;
|
|
7217
8542
|
try {
|
|
@@ -7232,7 +8557,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7232
8557
|
extra: [],
|
|
7233
8558
|
library: false,
|
|
7234
8559
|
rendered: true,
|
|
7235
|
-
lockedSha: new Map(
|
|
8560
|
+
lockedSha: new Map(recorded.map((f) => [f.path, f.sha256])),
|
|
7236
8561
|
instanceOf
|
|
7237
8562
|
});
|
|
7238
8563
|
}
|
|
@@ -7245,7 +8570,7 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7245
8570
|
*
|
|
7246
8571
|
* @param named - `kind:name` identities already reported; they are skipped.
|
|
7247
8572
|
*/
|
|
7248
|
-
undeployedLockGaps(lock, named) {
|
|
8573
|
+
async undeployedLockGaps(lock, named) {
|
|
7249
8574
|
const gaps = [];
|
|
7250
8575
|
for (const [key, entry] of Object.entries(lock?.assets ?? {})) {
|
|
7251
8576
|
if (entry.library?.installed) continue;
|
|
@@ -7264,7 +8589,8 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7264
8589
|
this.global,
|
|
7265
8590
|
this.projectDir
|
|
7266
8591
|
).find((p) => isSymlink(p) && !existsSync(p));
|
|
7267
|
-
const
|
|
8592
|
+
const composeDrift = component === "agent" && !dangling && lock ? await this.lockedComposeDrift(lock, kind, name) : void 0;
|
|
8593
|
+
const reason = composeDrift ? `(composes ${composeDrift}: the library's copy changed since the lock; \`skaile update\` moves the lock to it)` : dangling ? `is locked but its deployed copy is a symlink to nothing (${relative(this.projectDir, dangling)}) \u2014 run \`skaile install --locked\`` : "is locked but its deployed copy is missing \u2014 run `skaile install --locked`";
|
|
7268
8594
|
gaps.push({ type: "drift", ref, reason, message: `${ref} ${reason}` });
|
|
7269
8595
|
}
|
|
7270
8596
|
return gaps;
|
|
@@ -7345,10 +8671,23 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7345
8671
|
({ id }) => id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
|
|
7346
8672
|
);
|
|
7347
8673
|
if (held.length > 0 && !held.some(({ id }) => !id.version || matchPin(want.pin, id.version))) {
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
8674
|
+
const lockHolds = held.map((h) => h.key).join(", ");
|
|
8675
|
+
const pinned = await this.info(d.ref);
|
|
8676
|
+
const pinHeld = pinned !== null && matchPin(want.pin, pinned.version ?? "");
|
|
8677
|
+
if (!pinHeld && this.loadConfig().stores.length === 0) {
|
|
8678
|
+
const plainRef = d.ref.split("#")[0] ?? d.ref;
|
|
8679
|
+
const other = pinned ?? await this.info(plainRef);
|
|
8680
|
+
gap = {
|
|
8681
|
+
type: "pin",
|
|
8682
|
+
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`,
|
|
8683
|
+
manualFix: true
|
|
8684
|
+
};
|
|
8685
|
+
} else {
|
|
8686
|
+
gap = {
|
|
8687
|
+
type: "pin",
|
|
8688
|
+
reason: `is pinned in skaile.yaml but the lock holds ${lockHolds} \u2014 run \`skaile install\``
|
|
8689
|
+
};
|
|
8690
|
+
}
|
|
7352
8691
|
}
|
|
7353
8692
|
}
|
|
7354
8693
|
if (gap) gaps.push({ ...gap, ref: d.ref, message: `${d.ref} ${gap.reason}` });
|
|
@@ -7641,20 +8980,26 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7641
8980
|
* user-scope SSOT `~/.skaile/skaile.yaml`, **bootstrapping** the file (and its
|
|
7642
8981
|
* parent dir) when absent so `skaile add --global` works on a fresh machine.
|
|
7643
8982
|
*/
|
|
7644
|
-
addDependencyToConfig(ref) {
|
|
8983
|
+
addDependencyToConfig(ref, only = false) {
|
|
7645
8984
|
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
7646
|
-
const
|
|
8985
|
+
const requested = this.global && this.harnessesOverride?.length ? this.harnessesOverride : void 0;
|
|
7647
8986
|
if (!existsSync(configPath)) {
|
|
7648
8987
|
if (!this.global) return;
|
|
7649
8988
|
mkdirSync(this.stateRoot, { recursive: true });
|
|
7650
8989
|
const fresh = { dependencies: [ref] };
|
|
7651
|
-
if (
|
|
8990
|
+
if (requested) this.setEntryHarnessRow(fresh, ref, requested);
|
|
7652
8991
|
writeFileSync(configPath, stringify(fresh, { lineWidth: 120 }));
|
|
7653
8992
|
return;
|
|
7654
8993
|
}
|
|
7655
8994
|
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
7656
8995
|
if (!Array.isArray(raw.dependencies)) raw.dependencies = [];
|
|
7657
8996
|
const deps = raw.dependencies;
|
|
8997
|
+
const limit = requested ? only ? requested : this.widenedHarnessRow(
|
|
8998
|
+
raw,
|
|
8999
|
+
ref,
|
|
9000
|
+
requested,
|
|
9001
|
+
deps.some((d) => this.sameDeclaredAsset(d, ref))
|
|
9002
|
+
) : void 0;
|
|
7658
9003
|
const limitChanged = this.global ? this.setEntryHarnessRow(raw, ref, limit) : false;
|
|
7659
9004
|
if (deps.includes(ref) && deps.filter((d) => this.sameDeclaredAsset(d, ref)).length === 1) {
|
|
7660
9005
|
if (limitChanged) writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
@@ -7821,6 +9166,6 @@ Nothing was changed. Declare it again to keep it, or run \`skaile install --forc
|
|
|
7821
9166
|
}
|
|
7822
9167
|
};
|
|
7823
9168
|
|
|
7824
|
-
export { AssetManager,
|
|
7825
|
-
//# sourceMappingURL=chunk-
|
|
7826
|
-
//# sourceMappingURL=chunk-
|
|
9169
|
+
export { AssetManager, LibraryEntryError, MissingManifestError, installAgentInstances, labelDiffHeaders, mcpNotRunnableReason, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, setLibraryAssetCheck, setRenamedEntryLookup, softDepName };
|
|
9170
|
+
//# sourceMappingURL=chunk-PKBLJI23.js.map
|
|
9171
|
+
//# sourceMappingURL=chunk-PKBLJI23.js.map
|