@skaile/workspaces 5.6.1 → 5.8.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 +176 -0
- package/README.md +4 -2
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-LU7NHOAP.js → asset-feeds-EYRZR4K5.js} +55 -31
- package/dist/asset-feeds-EYRZR4K5.js.map +1 -0
- package/dist/asset-manager/catalog-deployer.js +10 -10
- package/dist/asset-manager/index.js +16 -16
- package/dist/asset-manager/installer.js +12 -12
- package/dist/asset-manager/renderers.js +11 -11
- package/dist/asset-manager/src/index.d.ts +364 -30
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/asset-manager/src/install-agent.d.ts +11 -0
- package/dist/asset-manager/src/install-agent.d.ts.map +1 -1
- package/dist/asset-manager/src/installer.d.ts +6 -3
- package/dist/asset-manager/src/installer.d.ts.map +1 -1
- package/dist/bridge/drivers/claude-sdk.js +148 -212
- package/dist/bridge/drivers/claude-sdk.js.map +1 -1
- package/dist/bridge/drivers/codex.js +91 -67
- package/dist/bridge/drivers/codex.js.map +1 -1
- package/dist/bridge/drivers/continue.js +16 -17
- package/dist/bridge/drivers/continue.js.map +1 -1
- package/dist/bridge/drivers/echo.js +4 -4
- package/dist/bridge/drivers/gemini.js +8 -8
- package/dist/bridge/drivers/goose.js +16 -17
- package/dist/bridge/drivers/goose.js.map +1 -1
- package/dist/bridge/drivers/omp.js +68 -104
- package/dist/bridge/drivers/omp.js.map +1 -1
- package/dist/bridge/drivers/opencode.js +16 -17
- package/dist/bridge/drivers/opencode.js.map +1 -1
- package/dist/bridge/drivers/qwen.js +8 -8
- package/dist/bridge/index.js +17 -18
- package/dist/bridge/src/codex/managed-driver.d.ts +13 -10
- package/dist/bridge/src/codex/managed-driver.d.ts.map +1 -1
- package/dist/bridge/src/drivers/_subprocess-cli.d.ts +12 -2
- package/dist/bridge/src/drivers/_subprocess-cli.d.ts.map +1 -1
- package/dist/bridge/src/drivers/claude-sdk.d.ts +37 -62
- package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
- package/dist/bridge/src/drivers/codex.d.ts +26 -10
- package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
- package/dist/bridge/src/drivers/continue.d.ts +2 -2
- package/dist/bridge/src/drivers/continue.d.ts.map +1 -1
- package/dist/bridge/src/drivers/gemini.d.ts +2 -2
- package/dist/bridge/src/drivers/gemini.d.ts.map +1 -1
- package/dist/bridge/src/drivers/goose.d.ts +2 -2
- package/dist/bridge/src/drivers/goose.d.ts.map +1 -1
- package/dist/bridge/src/drivers/omp.d.ts +24 -35
- package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
- package/dist/bridge/src/drivers/opencode.d.ts +2 -2
- package/dist/bridge/src/drivers/opencode.d.ts.map +1 -1
- package/dist/bridge/src/drivers/recovery-policy.d.ts +79 -0
- package/dist/bridge/src/drivers/recovery-policy.d.ts.map +1 -0
- package/dist/bridge/src/index.d.ts +1 -0
- package/dist/bridge/src/index.d.ts.map +1 -1
- package/dist/bridge/src/reconfigure-ledger.d.ts +117 -0
- package/dist/bridge/src/reconfigure-ledger.d.ts.map +1 -0
- package/dist/bridge/src/types.d.ts +57 -6
- package/dist/bridge/src/types.d.ts.map +1 -1
- package/dist/{chunk-OLLAQLFJ.js → chunk-2FRUUDZY.js} +5 -5
- package/dist/{chunk-OLLAQLFJ.js.map → chunk-2FRUUDZY.js.map} +1 -1
- package/dist/{chunk-SDFROXSA.js → chunk-2LWYNQK7.js} +3 -3
- package/dist/{chunk-SDFROXSA.js.map → chunk-2LWYNQK7.js.map} +1 -1
- package/dist/{chunk-TZCQ7ZWQ.js → chunk-36AFURKZ.js} +10 -11
- package/dist/chunk-36AFURKZ.js.map +1 -0
- package/dist/{chunk-MX3UQXLU.js → chunk-4MQS7CAF.js} +256 -92
- package/dist/chunk-4MQS7CAF.js.map +1 -0
- package/dist/{chunk-45VFGT7A.js → chunk-4PTSR4UU.js} +3 -3
- package/dist/{chunk-45VFGT7A.js.map → chunk-4PTSR4UU.js.map} +1 -1
- package/dist/{chunk-VX3INFBA.js → chunk-6GRX5L6A.js} +5 -5
- package/dist/{chunk-VX3INFBA.js.map → chunk-6GRX5L6A.js.map} +1 -1
- package/dist/{chunk-NFGRW3L7.js → chunk-7RGTAEG6.js} +3 -3
- package/dist/{chunk-NFGRW3L7.js.map → chunk-7RGTAEG6.js.map} +1 -1
- package/dist/{chunk-5B5IZVEP.js → chunk-AWQYXZVG.js} +15 -6
- package/dist/chunk-AWQYXZVG.js.map +1 -0
- package/dist/{chunk-XBSQ4KMC.js → chunk-B4X2N7JQ.js} +205 -3
- package/dist/chunk-B4X2N7JQ.js.map +1 -0
- package/dist/{chunk-FRHE7FFB.js → chunk-B6GIW55K.js} +5 -3
- package/dist/chunk-B6GIW55K.js.map +1 -0
- package/dist/{chunk-LLKKLKZJ.js → chunk-BJNUNQAW.js} +3 -3
- package/dist/{chunk-LLKKLKZJ.js.map → chunk-BJNUNQAW.js.map} +1 -1
- package/dist/{chunk-7ZHLYXTI.js → chunk-BTE2V4IW.js} +3 -3
- package/dist/{chunk-7ZHLYXTI.js.map → chunk-BTE2V4IW.js.map} +1 -1
- package/dist/{chunk-ZBBM6O6P.js → chunk-CGT45WN7.js} +3 -3
- package/dist/{chunk-ZBBM6O6P.js.map → chunk-CGT45WN7.js.map} +1 -1
- package/dist/{chunk-47ZD34KB.js → chunk-CIFS5K6T.js} +3 -3
- package/dist/{chunk-47ZD34KB.js.map → chunk-CIFS5K6T.js.map} +1 -1
- package/dist/{chunk-BTISGSK4.js → chunk-CKQEIY43.js} +4 -4
- package/dist/{chunk-BTISGSK4.js.map → chunk-CKQEIY43.js.map} +1 -1
- package/dist/{chunk-KXTO7HON.js → chunk-EAZECHPM.js} +3 -3
- package/dist/{chunk-KXTO7HON.js.map → chunk-EAZECHPM.js.map} +1 -1
- package/dist/{chunk-VKSXST23.js → chunk-EF7NPDDU.js} +4 -4
- package/dist/{chunk-VKSXST23.js.map → chunk-EF7NPDDU.js.map} +1 -1
- package/dist/chunk-EPBKISS5.js +84 -0
- package/dist/chunk-EPBKISS5.js.map +1 -0
- package/dist/{chunk-VJENKWXF.js → chunk-F2EKONM7.js} +3 -3
- package/dist/{chunk-VJENKWXF.js.map → chunk-F2EKONM7.js.map} +1 -1
- package/dist/{chunk-DMLDYFDS.js → chunk-F3RDEH5U.js} +7 -4
- package/dist/chunk-F3RDEH5U.js.map +1 -0
- package/dist/{chunk-UAP2XHRF.js → chunk-FTJJO3EM.js} +39 -29
- package/dist/chunk-FTJJO3EM.js.map +1 -0
- package/dist/{chunk-2GRB5ITE.js → chunk-FVJK4QWM.js} +4 -4
- package/dist/{chunk-2GRB5ITE.js.map → chunk-FVJK4QWM.js.map} +1 -1
- package/dist/{chunk-AOAZCSDI.js → chunk-FZNG4V4J.js} +16 -30
- package/dist/chunk-FZNG4V4J.js.map +1 -0
- package/dist/{chunk-BF33SCLY.js → chunk-GB7WDMH2.js} +4 -4
- package/dist/{chunk-BF33SCLY.js.map → chunk-GB7WDMH2.js.map} +1 -1
- package/dist/{chunk-NARITJS2.js → chunk-GCICGXDD.js} +3 -3
- package/dist/{chunk-NARITJS2.js.map → chunk-GCICGXDD.js.map} +1 -1
- package/dist/{chunk-PA7WLID3.js → chunk-GTILM2CI.js} +56 -5
- package/dist/chunk-GTILM2CI.js.map +1 -0
- package/dist/{chunk-WIHQWVWL.js → chunk-GXXFLBUQ.js} +26 -27
- package/dist/chunk-GXXFLBUQ.js.map +1 -0
- package/dist/{chunk-PTO2YDDG.js → chunk-H7QUDDKH.js} +1337 -168
- package/dist/chunk-H7QUDDKH.js.map +1 -0
- package/dist/{chunk-I4QBZLOR.js → chunk-HG34RNTK.js} +7 -13
- package/dist/chunk-HG34RNTK.js.map +1 -0
- package/dist/{chunk-65EYRFZJ.js → chunk-HYDRDNKN.js} +10 -6
- package/dist/chunk-HYDRDNKN.js.map +1 -0
- package/dist/{chunk-4GNJELPH.js → chunk-I6NLLJHZ.js} +2 -2
- package/dist/{chunk-4GNJELPH.js.map → chunk-I6NLLJHZ.js.map} +1 -1
- package/dist/{chunk-VSQFGU5D.js → chunk-ILFGVSTJ.js} +3 -3
- package/dist/{chunk-VSQFGU5D.js.map → chunk-ILFGVSTJ.js.map} +1 -1
- package/dist/{chunk-2XFUXGV6.js → chunk-IONAUNWB.js} +3 -3
- package/dist/{chunk-2XFUXGV6.js.map → chunk-IONAUNWB.js.map} +1 -1
- package/dist/{chunk-CT6NQSA4.js → chunk-ISHJITXJ.js} +3 -3
- package/dist/{chunk-CT6NQSA4.js.map → chunk-ISHJITXJ.js.map} +1 -1
- package/dist/chunk-IZCR27MU.js +60 -0
- package/dist/chunk-IZCR27MU.js.map +1 -0
- package/dist/{chunk-2W4LBLIN.js → chunk-JAKOMFC6.js} +68 -20
- package/dist/chunk-JAKOMFC6.js.map +1 -0
- package/dist/{chunk-MFHKHXDE.js → chunk-L5VSIPQD.js} +5 -5
- package/dist/chunk-L5VSIPQD.js.map +1 -0
- package/dist/{chunk-RGXVQZOV.js → chunk-LEMHVDET.js} +89 -67
- package/dist/chunk-LEMHVDET.js.map +1 -0
- package/dist/{chunk-37FTLYFP.js → chunk-LNFKL23K.js} +3 -3
- package/dist/{chunk-37FTLYFP.js.map → chunk-LNFKL23K.js.map} +1 -1
- package/dist/{chunk-6NPINANG.js → chunk-LSXHED6I.js} +6 -6
- package/dist/{chunk-6NPINANG.js.map → chunk-LSXHED6I.js.map} +1 -1
- package/dist/{chunk-XUUYAS3Z.js → chunk-M3WPBTGJ.js} +3 -3
- package/dist/{chunk-XUUYAS3Z.js.map → chunk-M3WPBTGJ.js.map} +1 -1
- package/dist/{chunk-QZLWJETK.js → chunk-MGGFBZWA.js} +3 -3
- package/dist/{chunk-QZLWJETK.js.map → chunk-MGGFBZWA.js.map} +1 -1
- package/dist/{chunk-QD772CGQ.js → chunk-NPLPCRW5.js} +5 -5
- package/dist/{chunk-QD772CGQ.js.map → chunk-NPLPCRW5.js.map} +1 -1
- package/dist/{chunk-QCI6D7E4.js → chunk-NUELZWCF.js} +266 -170
- package/dist/chunk-NUELZWCF.js.map +1 -0
- package/dist/{chunk-GP6ZL7PE.js → chunk-OFBIWVCR.js} +3 -3
- package/dist/{chunk-GP6ZL7PE.js.map → chunk-OFBIWVCR.js.map} +1 -1
- package/dist/{chunk-D2V42LUE.js → chunk-P23M65LB.js} +93 -52
- package/dist/chunk-P23M65LB.js.map +1 -0
- package/dist/{chunk-46OVMT76.js → chunk-PMECEZK3.js} +16 -10
- package/dist/chunk-PMECEZK3.js.map +1 -0
- package/dist/{chunk-27HS2DHB.js → chunk-PTSGWKHR.js} +4 -4
- package/dist/{chunk-27HS2DHB.js.map → chunk-PTSGWKHR.js.map} +1 -1
- package/dist/{chunk-OKGUDNDT.js → chunk-Q6E6FM7P.js} +35 -33
- package/dist/chunk-Q6E6FM7P.js.map +1 -0
- package/dist/{chunk-PJS2YWGG.js → chunk-QOP6KDFR.js} +8 -9
- package/dist/chunk-QOP6KDFR.js.map +1 -0
- package/dist/{chunk-AO7VJ6G6.js → chunk-QVNRCDTV.js} +29 -8
- package/dist/chunk-QVNRCDTV.js.map +1 -0
- package/dist/{chunk-VBBBZDI7.js → chunk-S53LOR7B.js} +3 -3
- package/dist/{chunk-VBBBZDI7.js.map → chunk-S53LOR7B.js.map} +1 -1
- package/dist/{chunk-KUFNKVBM.js → chunk-SH5D7U7N.js} +20 -7
- package/dist/{chunk-KUFNKVBM.js.map → chunk-SH5D7U7N.js.map} +1 -1
- package/dist/{chunk-SX4UIDKT.js → chunk-UAROCOXF.js} +3 -3
- package/dist/{chunk-SX4UIDKT.js.map → chunk-UAROCOXF.js.map} +1 -1
- package/dist/{chunk-JMKB7IB4.js → chunk-UMVKJM4Q.js} +4 -4
- package/dist/chunk-UMVKJM4Q.js.map +1 -0
- package/dist/{chunk-HCYS7GFP.js → chunk-V2DXZYBB.js} +3 -3
- package/dist/{chunk-HCYS7GFP.js.map → chunk-V2DXZYBB.js.map} +1 -1
- package/dist/{chunk-KYXOGHY6.js → chunk-W5DWWE4Y.js} +4 -4
- package/dist/{chunk-KYXOGHY6.js.map → chunk-W5DWWE4Y.js.map} +1 -1
- package/dist/{chunk-FK6HPLQA.js → chunk-WTQ3TATB.js} +3 -3
- package/dist/{chunk-FK6HPLQA.js.map → chunk-WTQ3TATB.js.map} +1 -1
- package/dist/{chunk-OGLDAJFL.js → chunk-Y4N2NUZD.js} +4 -4
- package/dist/{chunk-OGLDAJFL.js.map → chunk-Y4N2NUZD.js.map} +1 -1
- package/dist/{chunk-XOGG7VDZ.js → chunk-Y7QA2OLD.js} +17 -25
- package/dist/chunk-Y7QA2OLD.js.map +1 -0
- package/dist/{chunk-2KOKBTR4.js → chunk-YSHVQGUS.js} +123 -28
- package/dist/chunk-YSHVQGUS.js.map +1 -0
- package/dist/chunk-ZAOGFXDO.js +112 -0
- package/dist/chunk-ZAOGFXDO.js.map +1 -0
- package/dist/{chunk-7KLC4LZO.js → chunk-ZKSJMZKD.js} +17 -13
- package/dist/chunk-ZKSJMZKD.js.map +1 -0
- package/dist/cli/index.js +1169 -672
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/asset-feeds.d.ts +5 -2
- package/dist/cli/src/asset-feeds.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 +27 -5
- package/dist/cli/src/commands/catalog.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 +49 -4
- package/dist/cli/src/commands/library-index.d.ts.map +1 -1
- package/dist/cli/src/commands/library-publish.d.ts +9 -0
- package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
- package/dist/cli/src/commands/manage.d.ts +8 -2
- package/dist/cli/src/commands/manage.d.ts.map +1 -1
- package/dist/cli/src/commands/outdated.d.ts +10 -4
- package/dist/cli/src/commands/outdated.d.ts.map +1 -1
- package/dist/cli/src/commands/project.d.ts +15 -4
- package/dist/cli/src/commands/project.d.ts.map +1 -1
- package/dist/cli/src/commands/publish-core.d.ts +10 -1
- package/dist/cli/src/commands/publish-core.d.ts.map +1 -1
- package/dist/cli/src/commands/session.d.ts.map +1 -1
- package/dist/cli/src/commands/source.d.ts +18 -3
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/tree.d.ts.map +1 -1
- package/dist/cli/src/commands/update.d.ts +1 -1
- package/dist/cli/src/commands/update.d.ts.map +1 -1
- package/dist/cli/src/commands/validate.d.ts.map +1 -1
- package/dist/cli/src/flow-discovery.d.ts +8 -5
- package/dist/cli/src/flow-discovery.d.ts.map +1 -1
- package/dist/cli/src/open-library.d.ts +0 -11
- package/dist/cli/src/open-library.d.ts.map +1 -1
- package/dist/cli/src/skill-checks.d.ts +30 -10
- package/dist/cli/src/skill-checks.d.ts.map +1 -1
- package/dist/connectors/config.js +6 -6
- package/dist/connectors/index.js +26 -27
- package/dist/connectors/src/access-policy.d.ts +33 -0
- package/dist/connectors/src/access-policy.d.ts.map +1 -0
- package/dist/connectors/src/connector-manager.d.ts.map +1 -1
- package/dist/connectors/src/index.d.ts +2 -0
- package/dist/connectors/src/index.d.ts.map +1 -1
- package/dist/connectors-O6Y6VESP.js +44 -0
- package/dist/{connectors-HWENZFKW.js.map → connectors-O6Y6VESP.js.map} +1 -1
- package/dist/connectors-shared/index.js +2 -2
- package/dist/core/index.js +9 -9
- package/dist/core/logging.js +1 -1
- package/dist/core/manifest.js +2 -2
- package/dist/core/models.js +1 -1
- package/dist/core/publish-manifest.js +2 -2
- package/dist/core/ref.js +1 -1
- package/dist/core/runtime-assets.js +6 -6
- package/dist/core/src/cache-paths.d.ts.map +1 -1
- package/dist/core/src/index.d.ts +4 -4
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/lock.d.ts +3 -3
- package/dist/core/src/lock.d.ts.map +1 -1
- package/dist/core/src/logging/config.d.ts +9 -1
- package/dist/core/src/logging/config.d.ts.map +1 -1
- package/dist/core/src/manifest.d.ts +17 -2
- package/dist/core/src/manifest.d.ts.map +1 -1
- package/dist/core/src/models.d.ts +22 -6
- package/dist/core/src/models.d.ts.map +1 -1
- package/dist/core/src/repo-manager.d.ts +41 -6
- package/dist/core/src/repo-manager.d.ts.map +1 -1
- package/dist/core/src/store.d.ts +16 -2
- package/dist/core/src/store.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +10 -1
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/store.js +2 -2
- package/dist/core/workspace-config.js +5 -5
- package/dist/deploy/index.js +9 -9
- package/dist/discovery/index.js +5 -5
- package/dist/discovery/src/index.d.ts +1 -1
- package/dist/discovery/src/index.d.ts.map +1 -1
- package/dist/discovery/src/source-config.d.ts +12 -0
- package/dist/discovery/src/source-config.d.ts.map +1 -1
- package/dist/discovery/src/tree-entries.d.ts.map +1 -1
- package/dist/{ensure-sources-DSTSXJWK.js → ensure-sources-SKLNM4UZ.js} +19 -19
- package/dist/{ensure-sources-DSTSXJWK.js.map → ensure-sources-SKLNM4UZ.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +26 -27
- package/dist/factory-assets/connectors/deploy.js +26 -27
- package/dist/factory-assets/connectors/devserver.js +26 -27
- package/dist/factory-assets/connectors/flow/adapter.d.ts +6 -0
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +26 -27
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts +3 -0
- package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/output-fields.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/contract/output-fields.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +4 -4
- package/dist/factory-assets/connectors/flow/engine/flow-output.d.ts +4 -3
- package/dist/factory-assets/connectors/flow/engine/flow-output.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts +6 -1
- package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +13 -13
- package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/prompt-fragments.js +3 -3
- package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +28 -29
- package/dist/factory-assets/connectors/flow.js +26 -27
- package/dist/factory-assets/connectors/git/config.d.ts.map +1 -1
- package/dist/factory-assets/connectors/git.js +26 -27
- package/dist/factory-assets/connectors/gmail.js +26 -27
- package/dist/factory-assets/connectors/googledrive.js +26 -27
- package/dist/factory-assets/connectors/local.js +26 -27
- package/dist/factory-assets/connectors/mattermost.js +26 -27
- package/dist/factory-assets/connectors/memory.js +26 -27
- package/dist/factory-assets/connectors/minio.js +26 -27
- package/dist/factory-assets/connectors/postgres.js +26 -27
- package/dist/factory-assets/connectors/s3.js +26 -27
- package/dist/factory-assets/connectors/sharepoint.js +26 -27
- package/dist/factory-assets/connectors/sqlite.js +26 -27
- package/dist/factory-assets/connectors/static-server.js +26 -27
- package/dist/factory-assets/connectors/tunnel.js +26 -27
- package/dist/factory-assets/connectors/webdav.js +26 -27
- package/dist/factory-assets/connectors/xstate-store.js +26 -27
- package/dist/factory-assets/connectors/xstate.js +26 -27
- package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +10 -7
- package/dist/flows-CBYRSAGS.js +26 -0
- package/dist/{flows-ENQY2DV7.js.map → flows-CBYRSAGS.js.map} +1 -1
- package/dist/{helpers-R44RUTDY.js → helpers-MWZDRBLD.js} +19 -19
- package/dist/{helpers-R44RUTDY.js.map → helpers-MWZDRBLD.js.map} +1 -1
- package/dist/library/index.js +13 -13
- package/dist/library/install.js +3 -3
- package/dist/library/src/index.d.ts +5 -3
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/local/library-manager.d.ts +6 -0
- package/dist/library/src/local/library-manager.d.ts.map +1 -1
- package/dist/library/src/local/local-catalog-source.d.ts +5 -2
- package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
- package/dist/library/src/local/scan-authoring-libraries.d.ts +43 -0
- package/dist/library/src/local/scan-authoring-libraries.d.ts.map +1 -0
- package/dist/library/src/manifest-writer.d.ts +46 -1
- package/dist/library/src/manifest-writer.d.ts.map +1 -1
- package/dist/library/src/sync/git-driver.d.ts +12 -0
- package/dist/library/src/sync/git-driver.d.ts.map +1 -1
- package/dist/{library-publish-LFOJJVMK.js → library-publish-PICMDSB2.js} +129 -49
- package/dist/library-publish-PICMDSB2.js.map +1 -0
- package/dist/open-library-OG457THY.js +40 -0
- package/dist/{open-library-4WUMQHES.js.map → open-library-OG457THY.js.map} +1 -1
- package/dist/open-registry-ZUZJ7MS7.js +32 -0
- package/dist/{open-registry-NVPCQPND.js.map → open-registry-ZUZJ7MS7.js.map} +1 -1
- package/dist/paths-FQ64OLJG.js +22 -0
- package/dist/{paths-YUCP7K6U.js.map → paths-FQ64OLJG.js.map} +1 -1
- package/dist/{plugin-store-66QFBNEO.js → plugin-store-MPAMD5OT.js} +11 -11
- package/dist/{plugin-store-66QFBNEO.js.map → plugin-store-MPAMD5OT.js.map} +1 -1
- package/dist/project-2GFT5LEZ.js +41 -0
- package/dist/{project-VMOHUXBD.js.map → project-2GFT5LEZ.js.map} +1 -1
- package/dist/resolver/index.js +10 -10
- package/dist/runner/index.js +41 -42
- package/dist/runner/src/logging-bootstrap.d.ts +6 -0
- package/dist/runner/src/logging-bootstrap.d.ts.map +1 -1
- package/dist/runner/src/reconfigure-handler.d.ts +38 -17
- package/dist/runner/src/reconfigure-handler.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/runner/src/session-capability-wiring.d.ts +0 -2
- package/dist/runner/src/session-capability-wiring.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +16 -16
- package/dist/sdk/bridge.js +17 -18
- package/dist/sdk/core.js +9 -9
- package/dist/sdk/flow.js +13 -13
- package/dist/sdk/index.js +69 -46
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +10 -10
- package/dist/sdk/runner.js +41 -42
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/src/agent-session.d.ts +8 -0
- package/dist/sdk/src/agent-session.d.ts.map +1 -1
- package/dist/sdk/store.js +2 -1
- package/dist/sdk/types.js +1 -1
- package/dist/secrets/index.js +2 -2
- package/dist/session/index.js +2 -2
- package/dist/{setup-PYY23HGZ.js → setup-WHMCSIF2.js} +27 -28
- package/dist/setup-WHMCSIF2.js.map +1 -0
- package/dist/skills-TVO26Y4V.js +26 -0
- package/dist/{skills-CS7NE5TT.js.map → skills-TVO26Y4V.js.map} +1 -1
- package/dist/source-LKOFTOEK.js +41 -0
- package/dist/{source-JCF6KSQN.js.map → source-LKOFTOEK.js.map} +1 -1
- package/dist/{source-manifest-publish-O6P7G2M7.js → source-manifest-publish-L5J3QXER.js} +28 -28
- package/dist/{source-manifest-publish-O6P7G2M7.js.map → source-manifest-publish-L5J3QXER.js.map} +1 -1
- package/dist/store/index.js +2 -1
- package/dist/store/src/store.d.ts +8 -5
- package/dist/store/src/store.d.ts.map +1 -1
- package/dist/tui/index.js +41 -42
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/ask-slots.d.ts +68 -0
- package/dist/types/src/ask-slots.d.ts.map +1 -0
- package/dist/types/src/delivery.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +29 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.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/preset.d.ts +4 -0
- package/dist/types/src/manifests/preset.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +12 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +33 -34
- package/dist/workspace-plugin/adapters/omp.js +14 -14
- package/dist/workspace-plugin/index.js +2 -2
- package/package.json +1 -1
- package/dist/asset-feeds-LU7NHOAP.js.map +0 -1
- package/dist/chunk-2KOKBTR4.js.map +0 -1
- package/dist/chunk-2NYXQYHN.js +0 -22
- package/dist/chunk-2NYXQYHN.js.map +0 -1
- package/dist/chunk-2W4LBLIN.js.map +0 -1
- package/dist/chunk-46OVMT76.js.map +0 -1
- package/dist/chunk-5B5IZVEP.js.map +0 -1
- package/dist/chunk-65EYRFZJ.js.map +0 -1
- package/dist/chunk-7KLC4LZO.js.map +0 -1
- package/dist/chunk-AO7VJ6G6.js.map +0 -1
- package/dist/chunk-AOAZCSDI.js.map +0 -1
- package/dist/chunk-D2V42LUE.js.map +0 -1
- package/dist/chunk-DMLDYFDS.js.map +0 -1
- package/dist/chunk-EINLFFP6.js +0 -59
- package/dist/chunk-EINLFFP6.js.map +0 -1
- package/dist/chunk-FRHE7FFB.js.map +0 -1
- package/dist/chunk-I4QBZLOR.js.map +0 -1
- package/dist/chunk-JMKB7IB4.js.map +0 -1
- package/dist/chunk-MFHKHXDE.js.map +0 -1
- package/dist/chunk-MX3UQXLU.js.map +0 -1
- package/dist/chunk-OKGUDNDT.js.map +0 -1
- package/dist/chunk-PA7WLID3.js.map +0 -1
- package/dist/chunk-PJS2YWGG.js.map +0 -1
- package/dist/chunk-PTO2YDDG.js.map +0 -1
- package/dist/chunk-QCI6D7E4.js.map +0 -1
- package/dist/chunk-RGXVQZOV.js.map +0 -1
- package/dist/chunk-TZCQ7ZWQ.js.map +0 -1
- package/dist/chunk-UAP2XHRF.js.map +0 -1
- package/dist/chunk-WIHQWVWL.js.map +0 -1
- package/dist/chunk-XBSQ4KMC.js.map +0 -1
- package/dist/chunk-XOGG7VDZ.js.map +0 -1
- package/dist/connectors-HWENZFKW.js +0 -45
- package/dist/flows-ENQY2DV7.js +0 -26
- package/dist/library-publish-LFOJJVMK.js.map +0 -1
- package/dist/open-library-4WUMQHES.js +0 -40
- package/dist/open-registry-NVPCQPND.js +0 -32
- package/dist/paths-YUCP7K6U.js +0 -22
- package/dist/project-VMOHUXBD.js +0 -41
- package/dist/setup-PYY23HGZ.js.map +0 -1
- package/dist/skills-CS7NE5TT.js +0 -26
- package/dist/source-JCF6KSQN.js +0 -41
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { getStoreManifestsDir } from './chunk-
|
|
2
|
-
import { readManifestSourceConfig, discoverAssetsInTree } from './chunk-
|
|
3
|
-
import { installAgent, installTargetOf, deployAll, removeAsset,
|
|
1
|
+
import { getStoreManifestsDir } from './chunk-4MQS7CAF.js';
|
|
2
|
+
import { readManifestSourceConfig, isManifestMode, discoverAssetsInTree } from './chunk-Y7QA2OLD.js';
|
|
3
|
+
import { installAgent, installTargetOf, canonicalEntryRef, deployAll, removeAsset, createScaffold } from './chunk-YSHVQGUS.js';
|
|
4
4
|
import { appendHistory, getRecentHistory, clearHistory } from './chunk-KTBKW2FI.js';
|
|
5
5
|
import { commitChanges } from './chunk-UQ6LFBPZ.js';
|
|
6
|
-
import { driverTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-
|
|
7
|
-
import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries,
|
|
8
|
-
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-
|
|
9
|
-
import { deployedBase, deployedDir, isDeployed } from './chunk-
|
|
10
|
-
import { resolveSkWorkspaceConfig, effectiveAgentInstances, resolveAgentDefinitionDir, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin,
|
|
11
|
-
import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-
|
|
6
|
+
import { driverTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-EF7NPDDU.js';
|
|
7
|
+
import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, buildLockFile, writeLock, readLock, verifyLock, mergeLockFile, extractForPatch, generatePatch, savePatch, applyPatch } from './chunk-36AFURKZ.js';
|
|
8
|
+
import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-GB7WDMH2.js';
|
|
9
|
+
import { deployedBase, deployedDir, isDeployed, deployedPaths } from './chunk-PMECEZK3.js';
|
|
10
|
+
import { resolveSkWorkspaceConfig, effectiveAgentInstances, resolveAgentDefinitionDir, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, resolveAll, buildProvenanceIndex, ensureRepo, sourceShortName, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, coerceVersion, resolveAsset, readLinks, MAX_PRESET_DEPTH, PresetDepthError, storeManifestSlug } from './chunk-JAKOMFC6.js';
|
|
11
|
+
import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-L5VSIPQD.js';
|
|
12
12
|
import { DRIVER_TARGETS } from './chunk-CPSIPIX7.js';
|
|
13
|
-
import { scanDirectory, parseRequires } from './chunk-
|
|
14
|
-
import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName } from './chunk-
|
|
13
|
+
import { scanDirectory, parseRequires } from './chunk-GTILM2CI.js';
|
|
14
|
+
import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr } from './chunk-QVNRCDTV.js';
|
|
15
15
|
import { spawnSync } from 'child_process';
|
|
16
16
|
import { existsSync, rmSync, readdirSync, lstatSync, unlinkSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
17
|
-
import path, { join, resolve, dirname, sep, posix } from 'path';
|
|
17
|
+
import path, { join, resolve, dirname, sep, relative, posix } from 'path';
|
|
18
18
|
import { gt } from 'semver';
|
|
19
19
|
import { stringify, parse } from 'yaml';
|
|
20
20
|
import fs from 'fs/promises';
|
|
@@ -336,6 +336,25 @@ function indexLockByKindName(lock) {
|
|
|
336
336
|
}
|
|
337
337
|
return lockByKindName;
|
|
338
338
|
}
|
|
339
|
+
function commitsBetween(repoDir, from, to, cache) {
|
|
340
|
+
const key = `${from}..${to}`;
|
|
341
|
+
const hit = cache.get(key);
|
|
342
|
+
if (hit !== void 0) return hit;
|
|
343
|
+
let count = 1;
|
|
344
|
+
if (COMMIT_ID.test(from) && COMMIT_ID.test(to)) {
|
|
345
|
+
const r = spawnSync("git", ["-C", repoDir, "rev-list", "--count", key], {
|
|
346
|
+
encoding: "utf8",
|
|
347
|
+
stdio: "pipe"
|
|
348
|
+
});
|
|
349
|
+
if (r.status === 0) {
|
|
350
|
+
const n = Number.parseInt(r.stdout.trim(), 10);
|
|
351
|
+
if (Number.isFinite(n) && n >= 0) count = n;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
cache.set(key, count);
|
|
355
|
+
return count;
|
|
356
|
+
}
|
|
357
|
+
var COMMIT_ID = /^[0-9a-f]{7,64}$/;
|
|
339
358
|
function libraryDiskHashes(libPath, cache, registry) {
|
|
340
359
|
let hashes = cache.get(libPath);
|
|
341
360
|
if (!hashes) {
|
|
@@ -356,6 +375,26 @@ function libraryDiskHashes(libPath, cache, registry) {
|
|
|
356
375
|
}
|
|
357
376
|
return hashes;
|
|
358
377
|
}
|
|
378
|
+
function withLibraryFiles(entry, libPath, cache, registry) {
|
|
379
|
+
let byManifest = cache.get(libPath);
|
|
380
|
+
if (!byManifest) {
|
|
381
|
+
byManifest = /* @__PURE__ */ new Map();
|
|
382
|
+
const sourceConfig = readManifestSourceConfig(libPath, "library");
|
|
383
|
+
if (sourceConfig && isManifestMode(sourceConfig)) {
|
|
384
|
+
for (const a of discoverAssetsInTree(libPath, { sourceConfig, registry }).assets) {
|
|
385
|
+
const { assetRoot, files: files2 } = a.fileFilter;
|
|
386
|
+
byManifest.set(
|
|
387
|
+
resolve(a.manifestPath),
|
|
388
|
+
filesUnderManifestDir(files2, relative(assetRoot, a.manifestPath).split(sep).join("/"))
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
cache.set(libPath, byManifest);
|
|
393
|
+
}
|
|
394
|
+
const files = byManifest.get(resolve(entry.source));
|
|
395
|
+
if (files) entry.files = files;
|
|
396
|
+
return entry;
|
|
397
|
+
}
|
|
359
398
|
function newestLibraryDef(snap, libraryName, id) {
|
|
360
399
|
const lib = snap?.libraries.find((l) => l.name === libraryName);
|
|
361
400
|
if (!snap || !lib) return void 0;
|
|
@@ -367,6 +406,50 @@ function newestLibraryDef(snap, libraryName, id) {
|
|
|
367
406
|
}
|
|
368
407
|
return best;
|
|
369
408
|
}
|
|
409
|
+
function libraryPreference(isDefaultLib) {
|
|
410
|
+
return (a, b) => {
|
|
411
|
+
const va = coerceVersion(a.entry.version ?? "");
|
|
412
|
+
const vb = coerceVersion(b.entry.version ?? "");
|
|
413
|
+
if (gt(va, vb)) return -1;
|
|
414
|
+
if (gt(vb, va)) return 1;
|
|
415
|
+
const da = isDefaultLib.get(a.library) ? 0 : 1;
|
|
416
|
+
const db = isDefaultLib.get(b.library) ? 0 : 1;
|
|
417
|
+
return da - db || a.library.localeCompare(b.library);
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function qualifyDepRef(ref, fallback) {
|
|
421
|
+
const hash = ref.indexOf("#");
|
|
422
|
+
const head = hash === -1 ? ref : ref.slice(0, hash);
|
|
423
|
+
if (head.includes("@") || !fallback) return ref;
|
|
424
|
+
const pin = hash === -1 ? void 0 : ref.slice(hash + 1) || void 0;
|
|
425
|
+
const colon = head.indexOf(":");
|
|
426
|
+
return assetRefToStr({
|
|
427
|
+
kind: colon === -1 ? "skill" : head.slice(0, colon),
|
|
428
|
+
name: colon === -1 ? head : head.slice(colon + 1),
|
|
429
|
+
publisher: fallback,
|
|
430
|
+
pin
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function libraryEntryDepRefs(e) {
|
|
434
|
+
const publisher = e.publisher ?? "";
|
|
435
|
+
const refs = /* @__PURE__ */ new Set();
|
|
436
|
+
for (const d of e.dependencies ?? []) {
|
|
437
|
+
if (d) refs.add(qualifyDepRef(d, publisher));
|
|
438
|
+
}
|
|
439
|
+
for (const r of e.requires ?? []) {
|
|
440
|
+
const p = r.publisher ?? publisher;
|
|
441
|
+
if (p) refs.add(assetRefToStr({ kind: r.kind, name: r.name, publisher: p, pin: r.pin }));
|
|
442
|
+
}
|
|
443
|
+
return [...refs];
|
|
444
|
+
}
|
|
445
|
+
function refIdentity(ref) {
|
|
446
|
+
try {
|
|
447
|
+
const p = parseAssetRef(ref);
|
|
448
|
+
return p.publisher ? `${p.publisher}/${p.kind}:${p.name}` : null;
|
|
449
|
+
} catch {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
370
453
|
function computeSyncStatus(repoStatus) {
|
|
371
454
|
if (!repoStatus) return "unknown";
|
|
372
455
|
if (repoStatus.kind === "local") return "local";
|
|
@@ -437,6 +520,56 @@ function parseRefTolerant(ref) {
|
|
|
437
520
|
return { kind, name, publisher: void 0 };
|
|
438
521
|
}
|
|
439
522
|
}
|
|
523
|
+
function keptLockEntries(prev, missing) {
|
|
524
|
+
const out = { assets: {}, sources: [], refs: /* @__PURE__ */ new Set() };
|
|
525
|
+
if (!prev) return out;
|
|
526
|
+
for (const ref of missing) {
|
|
527
|
+
const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
|
|
528
|
+
if (!want) continue;
|
|
529
|
+
for (const [key, entry] of Object.entries(prev.assets)) {
|
|
530
|
+
if (entry.library?.installed) continue;
|
|
531
|
+
const id = parseCanonicalLockKey(key);
|
|
532
|
+
if (id.kind !== want.kind || id.name !== want.name) continue;
|
|
533
|
+
if (want.publisher && id.publisher !== want.publisher) continue;
|
|
534
|
+
out.assets[key] = entry;
|
|
535
|
+
out.refs.add(ref);
|
|
536
|
+
const src = entry.source ? prev.sources.find((s) => s.url === entry.source?.url) : void 0;
|
|
537
|
+
if (src) out.sources.push(src);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return out;
|
|
541
|
+
}
|
|
542
|
+
function withKeptLockEntries(lock, kept) {
|
|
543
|
+
if (Object.keys(kept.assets).length === 0) return lock;
|
|
544
|
+
const sources = new Map(lock.sources.map((s) => [s.url, s]));
|
|
545
|
+
for (const s of kept.sources) if (!sources.has(s.url)) sources.set(s.url, s);
|
|
546
|
+
return {
|
|
547
|
+
...lock,
|
|
548
|
+
// v4 once a library-origin entry is present (see buildLockFile).
|
|
549
|
+
schema_version: lock.schema_version === 4 || Object.values(kept.assets).some((e) => e.library) ? 4 : 3,
|
|
550
|
+
assets: { ...lock.assets, ...kept.assets },
|
|
551
|
+
sources: [...sources.values()].sort((a, b) => a.url.localeCompare(b.url))
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function relockedRefs(prev, next) {
|
|
555
|
+
if (!prev) return [];
|
|
556
|
+
const fingerprint = (e) => [e.library?.version ?? "", e.library?.sha256 ?? e.sha256, e.source?.commit ?? ""].join("\0");
|
|
557
|
+
const out = [];
|
|
558
|
+
for (const [key, entry] of Object.entries(next.assets)) {
|
|
559
|
+
if (entry.library?.installed) continue;
|
|
560
|
+
const id = parseCanonicalLockKey(key);
|
|
561
|
+
const before = prev.assets[key] ? [key, prev.assets[key]] : Object.entries(prev.assets).find(([k]) => {
|
|
562
|
+
const o = parseCanonicalLockKey(k);
|
|
563
|
+
return o.kind === id.kind && o.name === id.name && o.publisher === id.publisher;
|
|
564
|
+
});
|
|
565
|
+
if (!before) continue;
|
|
566
|
+
if (before[0] !== key || fingerprint(before[1]) !== fingerprint(entry)) out.push(key);
|
|
567
|
+
}
|
|
568
|
+
return out;
|
|
569
|
+
}
|
|
570
|
+
function unregisteredLibraryFix(library, ref) {
|
|
571
|
+
return `register library "${library}" again with \`skaile library init ${library} --path <dir>\`, or drop the dependency with \`skaile remove ${ref}\``;
|
|
572
|
+
}
|
|
440
573
|
function memberIdentity(ref) {
|
|
441
574
|
const parsed = parseRefTolerant(ref.trim());
|
|
442
575
|
if (!parsed) return null;
|
|
@@ -517,6 +650,8 @@ var AssetManager = class {
|
|
|
517
650
|
indexPathOverride;
|
|
518
651
|
/** Sink for cross-library collision warnings; else `console.warn`. */
|
|
519
652
|
onLibraryCollision;
|
|
653
|
+
/** Store URLs whose failed version lookup was already reported (one warning per URL). */
|
|
654
|
+
warnedStoreVersionLookups = /* @__PURE__ */ new Set();
|
|
520
655
|
constructor(opts) {
|
|
521
656
|
this.projectDir = resolve(opts.projectDir);
|
|
522
657
|
this.global = opts.global ?? false;
|
|
@@ -765,13 +900,55 @@ var AssetManager = class {
|
|
|
765
900
|
* content. The returned `libraryOf` map names the library each entry came from
|
|
766
901
|
* so callers can lock it as library-origin (see {@link libraryLockEntries}).
|
|
767
902
|
*
|
|
903
|
+
* @param opts.quiet - Skip the cross-library collision warning (a lookup that only
|
|
904
|
+
* builds an error hint must not warn).
|
|
768
905
|
* @returns the matched deploy entries, the set of `missing` refs covered, and
|
|
769
906
|
* each entry's originating library name.
|
|
770
907
|
*/
|
|
771
|
-
async resolveLibraryEntries(missing) {
|
|
908
|
+
async resolveLibraryEntries(missing, opts = {}) {
|
|
772
909
|
const resolved = /* @__PURE__ */ new Set();
|
|
773
910
|
const libraryOf = /* @__PURE__ */ new Map();
|
|
774
911
|
if (missing.length === 0) return { entries: [], resolved, libraryOf };
|
|
912
|
+
const scan = await this.scanLibraries();
|
|
913
|
+
if (!scan) return { entries: [], resolved, libraryOf };
|
|
914
|
+
const { byKey, isDefaultLib, libraryDir } = scan;
|
|
915
|
+
const entries = [];
|
|
916
|
+
const fileSets = /* @__PURE__ */ new Map();
|
|
917
|
+
const allCandidates = [...byKey.values()].flat();
|
|
918
|
+
for (const ref of missing) {
|
|
919
|
+
const parsed = parseRefTolerant(ref);
|
|
920
|
+
if (!parsed) continue;
|
|
921
|
+
const hash = ref.indexOf("#");
|
|
922
|
+
const pin = parsed.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
|
|
923
|
+
const key = parsed.publisher ? `${parsed.kind}:@${parsed.publisher}/${parsed.name}` : void 0;
|
|
924
|
+
const pool = key ? byKey.get(key) ?? [] : allCandidates.filter((c) => c.entry.kind === parsed.kind && c.entry.name === parsed.name);
|
|
925
|
+
const candidates = pool.filter((c) => !pin || matchPin(pin, c.entry.version ?? ""));
|
|
926
|
+
if (candidates.length === 0) continue;
|
|
927
|
+
candidates.sort(libraryPreference(isDefaultLib));
|
|
928
|
+
const hit = candidates[0];
|
|
929
|
+
if (!opts.quiet) {
|
|
930
|
+
this.warnLibraryCollision(
|
|
931
|
+
`${hit.entry.kind}:@${hit.entry.publisher}/${hit.entry.name}`,
|
|
932
|
+
candidates,
|
|
933
|
+
hit
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
withLibraryFiles(hit.entry, libraryDir.get(hit.library), fileSets, this.registry);
|
|
937
|
+
entries.push(hit.entry);
|
|
938
|
+
libraryOf.set(hit.entry, hit.library);
|
|
939
|
+
resolved.add(ref);
|
|
940
|
+
}
|
|
941
|
+
return { entries, resolved, libraryOf };
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Scan every candidate library (the set {@link resolveLibraryEntries} resolves from)
|
|
945
|
+
* into `kind:@publisher/name` → every holder, across all libraries and versions.
|
|
946
|
+
*
|
|
947
|
+
* @param minRoots - Return `null` without scanning when fewer candidate libraries
|
|
948
|
+
* exist (a cross-library question needs at least two).
|
|
949
|
+
* @returns `null` when there is no candidate library or none holds an asset.
|
|
950
|
+
*/
|
|
951
|
+
async scanLibraries(minRoots = 1) {
|
|
775
952
|
const roots = [];
|
|
776
953
|
for (const l of (await this.loadLibraryIndex())?.libraries ?? []) {
|
|
777
954
|
if (l.name === GLOBAL_INSTALL_DIR_NAME) continue;
|
|
@@ -793,8 +970,9 @@ var AssetManager = class {
|
|
|
793
970
|
}
|
|
794
971
|
} catch {
|
|
795
972
|
}
|
|
796
|
-
if (roots.length === 0
|
|
973
|
+
if (roots.length === 0 || roots.length < minRoots) return null;
|
|
797
974
|
const byKey = /* @__PURE__ */ new Map();
|
|
975
|
+
const libraryDir = new Map(roots.map((r) => [r.name, r.dir]));
|
|
798
976
|
const isDefaultLib = new Map(roots.map((r) => [r.name, r.isDefault]));
|
|
799
977
|
for (const { name, dir } of roots) {
|
|
800
978
|
const publisher = readDeclaredPublisher(dir) ?? slugifyAssetName(name);
|
|
@@ -805,37 +983,142 @@ var AssetManager = class {
|
|
|
805
983
|
else byKey.set(key, [{ entry, library: name }]);
|
|
806
984
|
}
|
|
807
985
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
986
|
+
return byKey.size === 0 ? null : { byKey, isDefaultLib, libraryDir };
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Every ref that more than one registered library holds, with one row per holder
|
|
990
|
+
* (all libraries, all versions) in the order `add` prefers them: the first row is
|
|
991
|
+
* the one `add <ref>` deploys. The local index keeps a single row per ref and
|
|
992
|
+
* version, so `search` reads this to show equal-version copies in both libraries.
|
|
993
|
+
*
|
|
994
|
+
* @returns `kind:@publisher/name` → holders; refs held by one library are absent.
|
|
995
|
+
*/
|
|
996
|
+
async libraryHolders() {
|
|
997
|
+
const out = /* @__PURE__ */ new Map();
|
|
998
|
+
const scan = await this.scanLibraries(2);
|
|
999
|
+
if (!scan) return out;
|
|
1000
|
+
for (const [key, candidates] of scan.byKey) {
|
|
1001
|
+
if (new Set(candidates.map((c) => c.library)).size < 2) continue;
|
|
1002
|
+
const sorted = [...candidates].sort(libraryPreference(scan.isDefaultLib));
|
|
1003
|
+
out.set(
|
|
1004
|
+
key,
|
|
1005
|
+
sorted.map((c, i) => ({
|
|
1006
|
+
library: c.library,
|
|
1007
|
+
version: c.entry.version ?? "",
|
|
1008
|
+
description: c.entry.description ?? "",
|
|
1009
|
+
picked: i === 0
|
|
1010
|
+
}))
|
|
821
1011
|
);
|
|
822
|
-
if (candidates.length === 0) continue;
|
|
823
|
-
candidates.sort((a, b) => {
|
|
824
|
-
const va = coerceVersion(a.entry.version ?? "");
|
|
825
|
-
const vb = coerceVersion(b.entry.version ?? "");
|
|
826
|
-
if (gt(va, vb)) return -1;
|
|
827
|
-
if (gt(vb, va)) return 1;
|
|
828
|
-
const da = isDefaultLib.get(a.library) ? 0 : 1;
|
|
829
|
-
const db = isDefaultLib.get(b.library) ? 0 : 1;
|
|
830
|
-
return da - db || a.library.localeCompare(b.library);
|
|
831
|
-
});
|
|
832
|
-
const hit = candidates[0];
|
|
833
|
-
this.warnLibraryCollision(key, candidates, hit);
|
|
834
|
-
entries.push(hit.entry);
|
|
835
|
-
libraryOf.set(hit.entry, hit.library);
|
|
836
|
-
resolved.add(ref);
|
|
837
1012
|
}
|
|
838
|
-
return
|
|
1013
|
+
return out;
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Where each missed publisher-qualified ref went when its publisher changed, for every
|
|
1017
|
+
* ref at once (one library scan, one lock read):
|
|
1018
|
+
* - `moved` when a registered library holds the same kind + name under another publisher;
|
|
1019
|
+
* - `lockedAs` when the lock holds the same kind + name under another publisher, records
|
|
1020
|
+
* the library it came from, and that library now holds the asset under yet another
|
|
1021
|
+
* publisher than the locked one. An unrelated asset that merely shares kind + name does
|
|
1022
|
+
* not qualify.
|
|
1023
|
+
*
|
|
1024
|
+
* @param refs - Missed refs; a trailing `(reason)` after the first space is ignored.
|
|
1025
|
+
* @returns one finding per ref that has one. Never warns.
|
|
1026
|
+
*/
|
|
1027
|
+
async publisherChangeFindings(refs) {
|
|
1028
|
+
const out = /* @__PURE__ */ new Map();
|
|
1029
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
1030
|
+
for (const ref of refs) {
|
|
1031
|
+
const parsed = parseRefTolerant(ref.split(" ")[0] ?? ref);
|
|
1032
|
+
if (parsed?.publisher) {
|
|
1033
|
+
wanted.set(ref, { kind: parsed.kind, name: parsed.name, publisher: parsed.publisher });
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (wanted.size === 0) return out;
|
|
1037
|
+
const lockEntries = Object.entries(this.tryReadLock()?.assets ?? {});
|
|
1038
|
+
const bare = [...new Set([...wanted.values()].map((w) => `${w.kind}:${w.name}`))];
|
|
1039
|
+
const lib = await this.resolveLibraryEntries(bare, { quiet: true });
|
|
1040
|
+
for (const [ref, w] of wanted) {
|
|
1041
|
+
const moved = lib.entries.find((e) => e.kind === w.kind && e.name === w.name);
|
|
1042
|
+
const movedLibrary = moved ? lib.libraryOf.get(moved) : void 0;
|
|
1043
|
+
const finding = {};
|
|
1044
|
+
for (const [key, entry] of lockEntries) {
|
|
1045
|
+
const id = parseCanonicalLockKey(key);
|
|
1046
|
+
if (id.kind === w.kind && id.name === w.name && id.publisher && id.publisher !== w.publisher && entry.library && moved && movedLibrary === entry.library.name && moved.publisher !== id.publisher) {
|
|
1047
|
+
finding.lockedAs = `${id.kind}:@${id.publisher}/${id.name}`;
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (moved && moved.publisher !== w.publisher) {
|
|
1052
|
+
finding.moved = {
|
|
1053
|
+
ref: `${moved.kind}:@${moved.publisher}/${moved.name}`,
|
|
1054
|
+
...movedLibrary ? { library: movedLibrary } : {}
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
if (finding.lockedAs || finding.moved) out.set(ref, finding);
|
|
1058
|
+
}
|
|
1059
|
+
return out;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Explain misses caused by a publisher change, as parenthesized hint text per ref:
|
|
1063
|
+
* `did you mean <ref>?` when a registered library holds the same kind + name under
|
|
1064
|
+
* another publisher, and `locked as <lockRef>; run skaile install to re-resolve` when
|
|
1065
|
+
* the lock holds it under the library's previous publisher (see
|
|
1066
|
+
* {@link AssetManager.publisherChangeFindings}).
|
|
1067
|
+
*
|
|
1068
|
+
* @param refs - Missed refs; a trailing `(reason)` after the first space is ignored.
|
|
1069
|
+
* @returns hint text per ref, only for refs that have one. Never warns.
|
|
1070
|
+
*/
|
|
1071
|
+
async publisherChangeHints(refs) {
|
|
1072
|
+
const out = {};
|
|
1073
|
+
for (const [ref, f] of await this.publisherChangeFindings(refs)) {
|
|
1074
|
+
const hints = [];
|
|
1075
|
+
if (f.lockedAs) hints.push(`locked as ${f.lockedAs}; run skaile install to re-resolve`);
|
|
1076
|
+
if (f.moved) hints.push(`did you mean ${f.moved.ref}?`);
|
|
1077
|
+
out[ref] = hints.map((h) => ` (${h})`).join("");
|
|
1078
|
+
}
|
|
1079
|
+
return out;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* What the user has to change for each ref that is locked from a library but resolves
|
|
1083
|
+
* nowhere any more, where `skaile install` cannot help: the library is not registered
|
|
1084
|
+
* (renamed away or removed), the library now publishes the asset under another
|
|
1085
|
+
* publisher, or the library no longer holds it. A ref the lock does not tie to a
|
|
1086
|
+
* library has no entry.
|
|
1087
|
+
*
|
|
1088
|
+
* @param refs - Declared or locked refs, `kind:@publisher/name` (a trailing `(reason)`
|
|
1089
|
+
* after the first space is ignored).
|
|
1090
|
+
* @returns a phrase per ref that follows the ref name, without parentheses.
|
|
1091
|
+
*/
|
|
1092
|
+
async unresolvedFixes(refs) {
|
|
1093
|
+
const out = /* @__PURE__ */ new Map();
|
|
1094
|
+
if (refs.length === 0) return out;
|
|
1095
|
+
const lockEntries = Object.entries(this.tryReadLock()?.assets ?? {});
|
|
1096
|
+
const snap = await this.loadLibraryIndex();
|
|
1097
|
+
const findings = await this.publisherChangeFindings(refs);
|
|
1098
|
+
for (const ref of refs) {
|
|
1099
|
+
const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
|
|
1100
|
+
if (!want) continue;
|
|
1101
|
+
const origin = lockEntries.map(([key, e]) => ({ id: parseCanonicalLockKey(key), library: e.library })).find(
|
|
1102
|
+
({ id, library }) => library && !library.installed && id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
|
|
1103
|
+
)?.library;
|
|
1104
|
+
if (!origin) continue;
|
|
1105
|
+
const plain = want.publisher ? `${want.kind}:@${want.publisher}/${want.name}` : `${want.kind}:${want.name}`;
|
|
1106
|
+
const moved = findings.get(ref)?.moved;
|
|
1107
|
+
if (moved?.library && want.publisher) {
|
|
1108
|
+
out.set(
|
|
1109
|
+
ref,
|
|
1110
|
+
`did you mean ${moved.ref}? Change the ref in skaile.yaml to it, or keep the old ref with \`skaile library set ${moved.library} publisher=${want.publisher}\``
|
|
1111
|
+
);
|
|
1112
|
+
} else if (!this.libraryPath(origin.name, snap)) {
|
|
1113
|
+
out.set(ref, unregisteredLibraryFix(origin.name, plain));
|
|
1114
|
+
} else {
|
|
1115
|
+
out.set(
|
|
1116
|
+
ref,
|
|
1117
|
+
`library "${origin.name}" no longer holds it \u2014 restore it there, or drop the dependency with \`skaile remove ${plain}\``
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return out;
|
|
839
1122
|
}
|
|
840
1123
|
/** Collision messages already printed by this instance (one warning per ref + library set). */
|
|
841
1124
|
warnedLibraryCollisions = /* @__PURE__ */ new Set();
|
|
@@ -853,6 +1136,56 @@ var AssetManager = class {
|
|
|
853
1136
|
if (this.onLibraryCollision) this.onLibraryCollision(msg);
|
|
854
1137
|
else console.warn(`skaile: ${msg}`);
|
|
855
1138
|
}
|
|
1139
|
+
/** Kind, name and bare publisher of a deploy entry; the publisher comes from its canonical ref. */
|
|
1140
|
+
entryIdentity(entry) {
|
|
1141
|
+
const ref = parseAssetRefLoose(canonicalEntryRef(entry));
|
|
1142
|
+
return {
|
|
1143
|
+
kind: entry.kind,
|
|
1144
|
+
name: entry.name,
|
|
1145
|
+
publisher: ref?.publisher ?? entry.publisher?.replace(/^@/, "")
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Refuse assets of one kind and name from different publishers: they deploy into the
|
|
1150
|
+
* same folder, so the later one would silently overwrite the earlier. Throws one line
|
|
1151
|
+
* naming the folder and the asset that holds it.
|
|
1152
|
+
*
|
|
1153
|
+
* @param incoming - Assets about to be deployed. Two of them clashing is refused.
|
|
1154
|
+
* @param held - Assets the project already declares or has locked; one of them
|
|
1155
|
+
* clashing with an incoming asset is refused when its folder is on disk.
|
|
1156
|
+
*/
|
|
1157
|
+
assertNoFolderCollision(incoming, held = []) {
|
|
1158
|
+
const refOf = (a) => assetRefToStr({ kind: a.kind, name: a.name, publisher: a.publisher });
|
|
1159
|
+
const differs = (a, b) => !!a.publisher && !!b.publisher && a.publisher !== b.publisher;
|
|
1160
|
+
const shown = (dir) => this.global ? dir : relative(this.projectDir, dir);
|
|
1161
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1162
|
+
for (const a of incoming) {
|
|
1163
|
+
if (!a.kind || !a.name) continue;
|
|
1164
|
+
const component = installTargetOf(this.registry, a.kind);
|
|
1165
|
+
if (!component) continue;
|
|
1166
|
+
const slot = `${component}/${a.name}`;
|
|
1167
|
+
const prior = seen.get(slot);
|
|
1168
|
+
if (prior && differs(prior, a)) {
|
|
1169
|
+
const dir2 = deployedDir(component, a.name, this.driverTarget, this.global, this.projectDir);
|
|
1170
|
+
throw new Error(
|
|
1171
|
+
`${refOf(prior)} and ${refOf(a)} both deploy to ${shown(dir2)} \u2014 keep only one of them`
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
if (!prior) seen.set(slot, a);
|
|
1175
|
+
const holder = held.find(
|
|
1176
|
+
(h) => !!h.kind && h.name === a.name && installTargetOf(this.registry, h.kind) === component && differs(h, a)
|
|
1177
|
+
);
|
|
1178
|
+
if (!holder) continue;
|
|
1179
|
+
const target = this.targetHarnesses().find(
|
|
1180
|
+
(t) => isDeployed(component, a.name, t, this.global, this.projectDir)
|
|
1181
|
+
);
|
|
1182
|
+
if (!target) continue;
|
|
1183
|
+
const dir = deployedDir(component, a.name, target, this.global, this.projectDir);
|
|
1184
|
+
throw new Error(
|
|
1185
|
+
`${refOf(a)} would overwrite ${shown(dir)}, which ${refOf(holder)} already holds \u2014 run skaile remove ${refOf(holder)} first`
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
856
1189
|
/**
|
|
857
1190
|
* Open the local index and snapshot its libraries + asset definitions.
|
|
858
1191
|
* Best-effort: no index file (never indexed), or any open/guard failure,
|
|
@@ -875,10 +1208,12 @@ var AssetManager = class {
|
|
|
875
1208
|
}
|
|
876
1209
|
/**
|
|
877
1210
|
* Library-origin lock entries for the assets {@link resolveLibraryEntries}
|
|
878
|
-
* matched. The locked version
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
1211
|
+
* matched. The locked version is the one actually deployed: the on-disk
|
|
1212
|
+
* entry's version, falling back to the library index only when the disk entry
|
|
1213
|
+
* carries none — the index lags until the next `skaile library index`, so
|
|
1214
|
+
* preferring it would lock a stale version after an in-place bump. The hash is
|
|
1215
|
+
* computed from the files on disk, so it matches what `install --locked`
|
|
1216
|
+
* re-hashes.
|
|
882
1217
|
*/
|
|
883
1218
|
async libraryLockEntries(lib) {
|
|
884
1219
|
if (lib.entries.length === 0) return {};
|
|
@@ -895,7 +1230,7 @@ var AssetManager = class {
|
|
|
895
1230
|
kind: e.kind,
|
|
896
1231
|
publisher,
|
|
897
1232
|
name: e.name,
|
|
898
|
-
library: { name, version:
|
|
1233
|
+
library: { name, version: e.version || def?.version || "0.0.0", sha256 }
|
|
899
1234
|
};
|
|
900
1235
|
})
|
|
901
1236
|
);
|
|
@@ -915,6 +1250,7 @@ var AssetManager = class {
|
|
|
915
1250
|
if (locked.length === 0) return { entries, missing };
|
|
916
1251
|
const snap = await this.loadLibraryIndex();
|
|
917
1252
|
const diskHashes = /* @__PURE__ */ new Map();
|
|
1253
|
+
const fileSets = /* @__PURE__ */ new Map();
|
|
918
1254
|
for (const [ref, e] of locked) {
|
|
919
1255
|
const origin = e.library;
|
|
920
1256
|
const { publisher, kind, name } = parseCanonicalLockKey(ref);
|
|
@@ -925,7 +1261,7 @@ var AssetManager = class {
|
|
|
925
1261
|
const libPath = this.libraryPath(origin.name, snap);
|
|
926
1262
|
if (!libPath) {
|
|
927
1263
|
missing.push(
|
|
928
|
-
`${ref} (library "${origin.name}" is not registered \u2014 renamed or removed?
|
|
1264
|
+
`${ref} (library "${origin.name}" is not registered \u2014 renamed or removed? ${unregisteredLibraryFix(origin.name, `${kind}:@${publisher}/${name}`)}; \`skaile install\` re-locks it when another library holds it now)`
|
|
929
1265
|
);
|
|
930
1266
|
continue;
|
|
931
1267
|
}
|
|
@@ -937,7 +1273,7 @@ var AssetManager = class {
|
|
|
937
1273
|
continue;
|
|
938
1274
|
}
|
|
939
1275
|
const def = newestLibraryDef(snap, origin.name, { kind, publisher, name });
|
|
940
|
-
const current =
|
|
1276
|
+
const current = found.version || def?.version || "0.0.0";
|
|
941
1277
|
if (current !== origin.version) {
|
|
942
1278
|
missing.push(
|
|
943
1279
|
`${ref} (library "${origin.name}" is at ${current}, locked ${origin.version})`
|
|
@@ -953,7 +1289,7 @@ var AssetManager = class {
|
|
|
953
1289
|
continue;
|
|
954
1290
|
}
|
|
955
1291
|
}
|
|
956
|
-
entries.push(found);
|
|
1292
|
+
entries.push(withLibraryFiles(found, libPath, fileSets, this.registry));
|
|
957
1293
|
}
|
|
958
1294
|
return { entries, missing };
|
|
959
1295
|
}
|
|
@@ -992,7 +1328,8 @@ var AssetManager = class {
|
|
|
992
1328
|
stores: opts.stores,
|
|
993
1329
|
storeFetcher: this.storeFetcherFor(opts.stores),
|
|
994
1330
|
projectDir: this.projectDir,
|
|
995
|
-
knownKinds: this.knownKinds
|
|
1331
|
+
knownKinds: this.knownKinds,
|
|
1332
|
+
declaredRefs: opts.declaredRefs
|
|
996
1333
|
});
|
|
997
1334
|
if (first.missing.length === 0) return first;
|
|
998
1335
|
const fallback = await this.globalStoreFallback(opts.stores);
|
|
@@ -1005,13 +1342,135 @@ var AssetManager = class {
|
|
|
1005
1342
|
stores,
|
|
1006
1343
|
storeFetcher: this.storeFetcherFor(stores),
|
|
1007
1344
|
projectDir: this.projectDir,
|
|
1008
|
-
knownKinds: this.knownKinds
|
|
1345
|
+
knownKinds: this.knownKinds,
|
|
1346
|
+
declaredRefs: opts.declaredRefs
|
|
1009
1347
|
});
|
|
1010
1348
|
return retry.missing.length < first.missing.length ? retry : first;
|
|
1011
1349
|
} catch {
|
|
1012
1350
|
return first;
|
|
1013
1351
|
}
|
|
1014
1352
|
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Resolve `deps` library-first, then walk the dependencies of every library
|
|
1355
|
+
* asset it picks.
|
|
1356
|
+
*
|
|
1357
|
+
* A publisher-qualified ref that no project source declares is looked up in the
|
|
1358
|
+
* registered **libraries** before any store is asked, so a locally authored
|
|
1359
|
+
* asset resolves (and deploys) with the store unreachable and without a
|
|
1360
|
+
* version-discovery round trip. Only refs the libraries did not satisfy go to
|
|
1361
|
+
* {@link resolveWithStores}; its misses get one more library try (a ref a source
|
|
1362
|
+
* knows only under another version). A `#version` pin still selects through
|
|
1363
|
+
* `matchPin` inside {@link resolveLibraryEntries}.
|
|
1364
|
+
*
|
|
1365
|
+
* Library assets declare `dependencies` / `requires`. Those are fed back through
|
|
1366
|
+
* the same resolution with a `seen` identity set (mirrors `resolveAll`), so deps
|
|
1367
|
+
* deploy, get lock entries, and a dependency cycle terminates.
|
|
1368
|
+
*
|
|
1369
|
+
* @returns source/store candidates, still-unresolved refs, applied overrides,
|
|
1370
|
+
* expanded presets, and the library leg (entries + originating library per entry).
|
|
1371
|
+
*/
|
|
1372
|
+
async resolveLibraryFirst(deps, opts) {
|
|
1373
|
+
const resolved = [];
|
|
1374
|
+
const overridesApplied = /* @__PURE__ */ new Set();
|
|
1375
|
+
const presets = [];
|
|
1376
|
+
const lib = { entries: [], resolved: /* @__PURE__ */ new Set(), libraryOf: /* @__PURE__ */ new Map() };
|
|
1377
|
+
const missing = /* @__PURE__ */ new Set();
|
|
1378
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1379
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1380
|
+
const declaredById = /* @__PURE__ */ new Map();
|
|
1381
|
+
for (const d of opts.declaredRefs ?? []) {
|
|
1382
|
+
const id = refIdentity(d);
|
|
1383
|
+
if (id !== null) declaredById.set(id, d);
|
|
1384
|
+
}
|
|
1385
|
+
const presetParent = /* @__PURE__ */ new Map();
|
|
1386
|
+
const absorb = (hit) => {
|
|
1387
|
+
const next = [];
|
|
1388
|
+
for (const e of hit.entries) {
|
|
1389
|
+
const origin = hit.libraryOf.get(e);
|
|
1390
|
+
const id = `${e.publisher}/${e.kind}:${e.name}`;
|
|
1391
|
+
seen.add(id);
|
|
1392
|
+
covered.add(id);
|
|
1393
|
+
const deps2 = libraryEntryDepRefs(e);
|
|
1394
|
+
if (e.kind === "preset") {
|
|
1395
|
+
const chain = [id];
|
|
1396
|
+
for (let up = presetParent.get(id); up !== void 0; up = presetParent.get(up)) {
|
|
1397
|
+
chain.unshift(up);
|
|
1398
|
+
}
|
|
1399
|
+
if (chain.length > MAX_PRESET_DEPTH) {
|
|
1400
|
+
throw new PresetDepthError("depth", canonicalEntryRef(e), chain);
|
|
1401
|
+
}
|
|
1402
|
+
for (const dep of deps2) {
|
|
1403
|
+
const depId = refIdentity(dep);
|
|
1404
|
+
if (depId === null || !depId.includes("/preset:")) continue;
|
|
1405
|
+
if (chain.includes(depId)) throw new PresetDepthError("cycle", dep, [...chain, depId]);
|
|
1406
|
+
if (!presetParent.has(depId)) presetParent.set(depId, id);
|
|
1407
|
+
}
|
|
1408
|
+
presets.push({
|
|
1409
|
+
ref: canonicalEntryRef(e),
|
|
1410
|
+
assets: deps2.filter((d) => !d.startsWith("preset:")).length,
|
|
1411
|
+
skipped: e.skippedItems ?? 0,
|
|
1412
|
+
...origin ? { library: origin } : {}
|
|
1413
|
+
});
|
|
1414
|
+
} else {
|
|
1415
|
+
lib.entries.push(e);
|
|
1416
|
+
if (origin) lib.libraryOf.set(e, origin);
|
|
1417
|
+
}
|
|
1418
|
+
next.push(
|
|
1419
|
+
...deps2.map((dep) => {
|
|
1420
|
+
const depId = refIdentity(dep);
|
|
1421
|
+
return depId !== null && declaredById.get(depId) || dep;
|
|
1422
|
+
})
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
for (const r of hit.resolved) lib.resolved.add(r);
|
|
1426
|
+
return next;
|
|
1427
|
+
};
|
|
1428
|
+
let wave = deps;
|
|
1429
|
+
while (wave.length > 0) {
|
|
1430
|
+
const fresh = [];
|
|
1431
|
+
for (const ref of wave) {
|
|
1432
|
+
const id = refIdentity(ref);
|
|
1433
|
+
if (id !== null) {
|
|
1434
|
+
if (seen.has(id)) continue;
|
|
1435
|
+
seen.add(id);
|
|
1436
|
+
}
|
|
1437
|
+
fresh.push(ref);
|
|
1438
|
+
}
|
|
1439
|
+
if (fresh.length === 0) break;
|
|
1440
|
+
const libraryTried = new Set(
|
|
1441
|
+
fresh.filter((ref) => {
|
|
1442
|
+
const id = refIdentity(ref);
|
|
1443
|
+
return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0;
|
|
1444
|
+
})
|
|
1445
|
+
);
|
|
1446
|
+
const hit = await this.resolveLibraryEntries([...libraryTried]);
|
|
1447
|
+
const next = absorb(hit);
|
|
1448
|
+
const rest = fresh.filter((ref) => !hit.resolved.has(ref));
|
|
1449
|
+
if (rest.length > 0) {
|
|
1450
|
+
const r = await this.resolveWithStores(rest, opts);
|
|
1451
|
+
for (const c of r.resolved) {
|
|
1452
|
+
const id = `${c.publisher}/${c.kind}:${c.name}`;
|
|
1453
|
+
if (covered.has(id)) continue;
|
|
1454
|
+
seen.add(id);
|
|
1455
|
+
covered.add(id);
|
|
1456
|
+
resolved.push(c);
|
|
1457
|
+
}
|
|
1458
|
+
for (const o of r.overridesApplied) overridesApplied.add(o);
|
|
1459
|
+
presets.push(...r.presets);
|
|
1460
|
+
const hit2 = await this.resolveLibraryEntries(
|
|
1461
|
+
r.missing.filter((m) => !libraryTried.has(m))
|
|
1462
|
+
);
|
|
1463
|
+
next.push(...absorb(hit2));
|
|
1464
|
+
for (const m of r.missing) if (!hit2.resolved.has(m)) missing.add(m);
|
|
1465
|
+
}
|
|
1466
|
+
wave = next;
|
|
1467
|
+
}
|
|
1468
|
+
const stillMissing = [...missing].filter((m) => {
|
|
1469
|
+
const id = refIdentity(m);
|
|
1470
|
+
return !lib.resolved.has(m) && !(id !== null && covered.has(id));
|
|
1471
|
+
});
|
|
1472
|
+
return { resolved, missing: stillMissing, overridesApplied, lib, presets };
|
|
1473
|
+
}
|
|
1015
1474
|
/** The catalog fetcher to use: an injected one, else built from `stores`. */
|
|
1016
1475
|
storeFetcherFor(stores) {
|
|
1017
1476
|
return this.injectedStoreFetcher ?? this.buildStoreFetcher(stores);
|
|
@@ -1177,7 +1636,7 @@ var AssetManager = class {
|
|
|
1177
1636
|
async install(opts) {
|
|
1178
1637
|
const { repositories, sources, stores, dependencies, overrides, patches } = this.loadConfig();
|
|
1179
1638
|
if (opts?.locked) {
|
|
1180
|
-
return this.installLocked(repositories, patches);
|
|
1639
|
+
return this.installLocked(repositories, patches, dependencies);
|
|
1181
1640
|
}
|
|
1182
1641
|
const cloneError = this.ensureSourceClones(repositories, sources);
|
|
1183
1642
|
if (cloneError) return cloneError;
|
|
@@ -1185,18 +1644,32 @@ var AssetManager = class {
|
|
|
1185
1644
|
projectDir: this.projectDir,
|
|
1186
1645
|
knownKinds: this.knownKinds
|
|
1187
1646
|
});
|
|
1188
|
-
const { resolved, missing, overridesApplied, presets } = await this.
|
|
1647
|
+
const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
|
|
1189
1648
|
dependencies,
|
|
1190
|
-
{
|
|
1191
|
-
provenanceIndex,
|
|
1192
|
-
overrides,
|
|
1193
|
-
stores
|
|
1194
|
-
}
|
|
1649
|
+
{ provenanceIndex, overrides, stores, declaredRefs: dependencies }
|
|
1195
1650
|
);
|
|
1196
|
-
|
|
1651
|
+
this.assertNoFolderCollision([...resolved, ...lib.entries.map((e) => this.entryIdentity(e))]);
|
|
1652
|
+
const stillMissing = missing.filter((m) => !lib.resolved.has(m));
|
|
1653
|
+
const missingHints = await this.publisherChangeHints(stillMissing);
|
|
1654
|
+
const prevLock = this.tryReadLock();
|
|
1655
|
+
const kept = keptLockEntries(prevLock, stillMissing);
|
|
1656
|
+
const fixes = await this.unresolvedFixes(stillMissing);
|
|
1657
|
+
for (const ref of /* @__PURE__ */ new Set([...kept.refs, ...fixes.keys()])) {
|
|
1658
|
+
const fix = fixes.get(ref);
|
|
1659
|
+
const notes = [
|
|
1660
|
+
kept.refs.has(ref) ? "the deployed copy and its lock entry are kept" : void 0,
|
|
1661
|
+
fix
|
|
1662
|
+
].filter((n) => n !== void 0);
|
|
1663
|
+
missingHints[ref] = `${fix ? "" : missingHints[ref] ?? ""} (${notes.join("; ")})`;
|
|
1664
|
+
}
|
|
1197
1665
|
const directLock = this.directInstallLockEntries();
|
|
1198
1666
|
const libraryLock = { ...directLock, ...await this.libraryLockEntries(lib) };
|
|
1199
|
-
const
|
|
1667
|
+
const freshLock = buildLockFile(resolved, overridesApplied, libraryLock);
|
|
1668
|
+
await this.keepRequiredByKept(prevLock, kept, Object.keys(freshLock.assets));
|
|
1669
|
+
const removed = this.reconcileRemovedAssets(
|
|
1670
|
+
resolved,
|
|
1671
|
+
/* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
|
|
1672
|
+
);
|
|
1200
1673
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
1201
1674
|
const deployed = [
|
|
1202
1675
|
...new Set(
|
|
@@ -1208,16 +1681,20 @@ var AssetManager = class {
|
|
|
1208
1681
|
await this.renderDriverMcpConfig();
|
|
1209
1682
|
const agentRender = this.renderAgentInstances();
|
|
1210
1683
|
const lockWritten = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
|
|
1684
|
+
let relocked = [];
|
|
1211
1685
|
if (lockWritten) {
|
|
1212
|
-
|
|
1686
|
+
const lockData = withKeptLockEntries(freshLock, kept);
|
|
1687
|
+
relocked = relockedRefs(prevLock, lockData);
|
|
1688
|
+
writeLock(this.lockPath, lockData);
|
|
1213
1689
|
}
|
|
1214
1690
|
this.recordInstallHistory(deployed, removed);
|
|
1215
1691
|
const { npmInstalled, npmFailed } = await this.installRuntimeAssetDeps();
|
|
1216
|
-
const stillMissing = missing.filter((m) => !lib.resolved.has(m));
|
|
1217
1692
|
return {
|
|
1218
1693
|
deployed,
|
|
1219
1694
|
removed,
|
|
1220
1695
|
missing: stillMissing,
|
|
1696
|
+
...Object.keys(missingHints).length > 0 ? { missingHints } : {},
|
|
1697
|
+
...relocked.length > 0 ? { relocked } : {},
|
|
1221
1698
|
lockWritten,
|
|
1222
1699
|
npmInstalled,
|
|
1223
1700
|
npmFailed,
|
|
@@ -1225,6 +1702,49 @@ var AssetManager = class {
|
|
|
1225
1702
|
...agentRender
|
|
1226
1703
|
};
|
|
1227
1704
|
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Extend `kept` (see {@link keptLockEntries}) with every previous lock entry a kept asset
|
|
1707
|
+
* requires, transitively, that the new lock (`freshKeys`) does not already carry. The lock
|
|
1708
|
+
* records no edges, so they are read from the kept asset's deployed copy, where the
|
|
1709
|
+
* requirement is publisher-qualified the same way a library scan does it. A copy that
|
|
1710
|
+
* cannot be read has no known requirements.
|
|
1711
|
+
*/
|
|
1712
|
+
async keepRequiredByKept(prev, kept, freshKeys) {
|
|
1713
|
+
if (!prev) return;
|
|
1714
|
+
const fresh = freshKeys.map(parseCanonicalLockKey);
|
|
1715
|
+
const queue = Object.keys(kept.assets);
|
|
1716
|
+
for (const key of queue) {
|
|
1717
|
+
for (const dep of await this.deployedRequirements(key)) {
|
|
1718
|
+
const want = memberIdentity(dep);
|
|
1719
|
+
if (!want) continue;
|
|
1720
|
+
const matches = (id) => id.kind === want.kind && id.name === want.name && (!want.publisher || !id.publisher || id.publisher === want.publisher);
|
|
1721
|
+
if (fresh.some(matches)) continue;
|
|
1722
|
+
for (const [depKey, entry] of Object.entries(prev.assets)) {
|
|
1723
|
+
if (depKey in kept.assets || entry.library?.installed) continue;
|
|
1724
|
+
if (!matches(parseCanonicalLockKey(depKey))) continue;
|
|
1725
|
+
kept.assets[depKey] = entry;
|
|
1726
|
+
queue.push(depKey);
|
|
1727
|
+
const src = entry.source ? prev.sources.find((s) => s.url === entry.source?.url) : void 0;
|
|
1728
|
+
if (src) kept.sources.push(src);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
/** Publisher-qualified refs the deployed copy of the asset behind lock `key` requires. */
|
|
1734
|
+
async deployedRequirements(key) {
|
|
1735
|
+
const { kind, name, publisher } = parseCanonicalLockKey(key);
|
|
1736
|
+
const component = kind ? this.installTarget(kind) : void 0;
|
|
1737
|
+
if (!kind || !name || !component) return [];
|
|
1738
|
+
const dir = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
|
|
1739
|
+
if (!existsSync(dir)) return [];
|
|
1740
|
+
try {
|
|
1741
|
+
const scanned = await scanDirectory(dir, publisher ?? "", { knownKinds: this.knownKinds });
|
|
1742
|
+
const own = scanned.find((e) => e.kind === kind && e.name === name);
|
|
1743
|
+
return own ? libraryEntryDepRefs(own) : [];
|
|
1744
|
+
} catch {
|
|
1745
|
+
return [];
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1228
1748
|
/**
|
|
1229
1749
|
* Ensure all source clones are present/up to date. Returns an early
|
|
1230
1750
|
* {@link InstallResult} describing the failed source on the first error, or
|
|
@@ -1289,6 +1809,7 @@ var AssetManager = class {
|
|
|
1289
1809
|
for (const ref of Object.keys(oldLock.assets)) {
|
|
1290
1810
|
if (!resolvedRefs.has(ref)) {
|
|
1291
1811
|
const { kind, name } = parseCanonicalLockKey(ref);
|
|
1812
|
+
if (kind === "preset") continue;
|
|
1292
1813
|
if (kind && name) {
|
|
1293
1814
|
for (const t of this.targetHarnesses()) removeAsset(kind, name, this.deployOptsFor(t));
|
|
1294
1815
|
removed.push(ref);
|
|
@@ -1374,7 +1895,10 @@ var AssetManager = class {
|
|
|
1374
1895
|
try {
|
|
1375
1896
|
return await src.listVersions(ref);
|
|
1376
1897
|
} catch (err) {
|
|
1377
|
-
|
|
1898
|
+
if (!this.warnedStoreVersionLookups.has(storeUrl)) {
|
|
1899
|
+
this.warnedStoreVersionLookups.add(storeUrl);
|
|
1900
|
+
console.warn(`skaile: store ${storeUrl} version lookup for ${ref} failed \u2014 ${err}`);
|
|
1901
|
+
}
|
|
1378
1902
|
return [];
|
|
1379
1903
|
}
|
|
1380
1904
|
}
|
|
@@ -1411,11 +1935,12 @@ var AssetManager = class {
|
|
|
1411
1935
|
}
|
|
1412
1936
|
return { npmInstalled, npmFailed };
|
|
1413
1937
|
}
|
|
1414
|
-
async installLocked(repositories, patches) {
|
|
1938
|
+
async installLocked(repositories, patches, dependencies) {
|
|
1415
1939
|
const lock = this.tryReadLock();
|
|
1416
1940
|
if (!lock) {
|
|
1417
1941
|
throw new Error("No lock file found. Run `skaile install` first.");
|
|
1418
1942
|
}
|
|
1943
|
+
this.assertNoFolderCollision(Object.keys(lock.assets).map(parseCanonicalLockKey));
|
|
1419
1944
|
const declBySlug = new Map(Object.entries(repositories));
|
|
1420
1945
|
const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
|
|
1421
1946
|
const factoryDir = Object.values(repositories).find((d) => d.factory)?.path ?? null;
|
|
@@ -1440,6 +1965,16 @@ var AssetManager = class {
|
|
|
1440
1965
|
throw new Error(`Lock file verification failed:
|
|
1441
1966
|
${issues.join("\n ")}`);
|
|
1442
1967
|
}
|
|
1968
|
+
const presetGaps = await this.declaredPresetGaps(dependencies, lock);
|
|
1969
|
+
if (presetGaps.size > 0) {
|
|
1970
|
+
const lines = [...presetGaps].flatMap(
|
|
1971
|
+
([preset, gaps2]) => gaps2.map((g) => `${preset} item ${g.ref}`)
|
|
1972
|
+
);
|
|
1973
|
+
throw new Error(
|
|
1974
|
+
`Lock file does not pin every item of the declared presets (not resolved by \`skaile install\`):
|
|
1975
|
+
${lines.join("\n ")}`
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1443
1978
|
const resolved = [];
|
|
1444
1979
|
for (const [ref, entry] of Object.entries(lock.assets)) {
|
|
1445
1980
|
if (!entry.source) continue;
|
|
@@ -1478,10 +2013,20 @@ var AssetManager = class {
|
|
|
1478
2013
|
];
|
|
1479
2014
|
await this.renderDriverMcpConfig();
|
|
1480
2015
|
const agentRender = this.renderAgentInstances();
|
|
2016
|
+
const alreadyMissing = new Set(
|
|
2017
|
+
library.missing.map((m) => {
|
|
2018
|
+
const p = parseRefTolerant(m.split(" ")[0] ?? m);
|
|
2019
|
+
return p ? `${p.kind}:${p.name}` : m;
|
|
2020
|
+
})
|
|
2021
|
+
);
|
|
2022
|
+
const gaps = (await this.lockGaps()).filter((g) => {
|
|
2023
|
+
const p = parseRefTolerant(g.ref);
|
|
2024
|
+
return !(p && alreadyMissing.has(`${p.kind}:${p.name}`));
|
|
2025
|
+
}).map((g) => g.message);
|
|
1481
2026
|
return {
|
|
1482
2027
|
deployed,
|
|
1483
2028
|
removed: [],
|
|
1484
|
-
missing: library.missing,
|
|
2029
|
+
missing: [...library.missing, ...gaps],
|
|
1485
2030
|
lockWritten: false,
|
|
1486
2031
|
...agentRender
|
|
1487
2032
|
};
|
|
@@ -1499,26 +2044,44 @@ var AssetManager = class {
|
|
|
1499
2044
|
* store/factory sources. Defaults to copy.
|
|
1500
2045
|
* @param opts.onLibraryOrigin - Called once per deployed library-origin asset with its
|
|
1501
2046
|
* canonical ref and the name of the library it was resolved from.
|
|
2047
|
+
* @param opts.onPresets - Called once with the presets expanded into this add (the
|
|
2048
|
+
* added preset and any it nests); a library-held preset names its library.
|
|
1502
2049
|
* @returns Array of asset refs that were deployed in this call.
|
|
1503
|
-
* @throws When the asset cannot be found in any configured repository
|
|
2050
|
+
* @throws When the asset cannot be found in any configured repository, or when a
|
|
2051
|
+
* preset's items do not all resolve (nothing is deployed or recorded then).
|
|
1504
2052
|
*/
|
|
1505
2053
|
async add(ref, opts) {
|
|
1506
|
-
const { repositories, sources, stores, overrides, patches } = this.loadConfig();
|
|
1507
|
-
parseAssetRef(ref);
|
|
2054
|
+
const { repositories, sources, stores, dependencies, overrides, patches } = this.loadConfig();
|
|
2055
|
+
const parsed = parseAssetRef(ref);
|
|
1508
2056
|
this.ensureSourceClonesQuiet(repositories, sources);
|
|
1509
2057
|
const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
|
|
1510
2058
|
projectDir: this.projectDir,
|
|
1511
2059
|
knownKinds: this.knownKinds
|
|
1512
2060
|
});
|
|
1513
|
-
const { resolved, missing, overridesApplied } = await this.
|
|
1514
|
-
|
|
1515
|
-
overrides,
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
const lib = await this.resolveLibraryEntries(missing);
|
|
1519
|
-
if (resolved.length === 0 && lib.entries.length === 0) {
|
|
2061
|
+
const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
|
|
2062
|
+
[ref],
|
|
2063
|
+
{ provenanceIndex, overrides, stores, declaredRefs: dependencies }
|
|
2064
|
+
);
|
|
2065
|
+
if (resolved.length === 0 && lib.entries.length === 0 && presets.length === 0) {
|
|
1520
2066
|
throw new Error(
|
|
1521
|
-
`Asset not found: ${ref}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}`
|
|
2067
|
+
`Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}`
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
if (parsed.kind === "preset" && missing.length > 0) {
|
|
2071
|
+
throw new Error(
|
|
2072
|
+
`Preset ${ref} has items that do not resolve: ${missing.join(", ")} \u2014 nothing was added`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
this.assertNoFolderCollision(
|
|
2076
|
+
[...resolved, ...lib.entries.map((e) => this.entryIdentity(e))],
|
|
2077
|
+
[
|
|
2078
|
+
...Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey),
|
|
2079
|
+
...dependencies.map((d) => parseAssetRefLoose(d) ?? {})
|
|
2080
|
+
]
|
|
2081
|
+
);
|
|
2082
|
+
if (missing.length > 0) {
|
|
2083
|
+
console.warn(
|
|
2084
|
+
`skaile: ${ref} was added without unresolved dependencies: ${missing.join(", ")}`
|
|
1522
2085
|
);
|
|
1523
2086
|
}
|
|
1524
2087
|
const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
|
|
@@ -1536,6 +2099,7 @@ var AssetManager = class {
|
|
|
1536
2099
|
if (origin && deployed.includes(deployedRef)) opts.onLibraryOrigin(deployedRef, origin);
|
|
1537
2100
|
}
|
|
1538
2101
|
}
|
|
2102
|
+
if (presets.length > 0) opts?.onPresets?.(presets);
|
|
1539
2103
|
this.addDependencyToConfig(ref);
|
|
1540
2104
|
await this.renderDriverMcpConfig();
|
|
1541
2105
|
this.renderAgentInstances();
|
|
@@ -1604,7 +2168,7 @@ var AssetManager = class {
|
|
|
1604
2168
|
const out = [];
|
|
1605
2169
|
const seen = /* @__PURE__ */ new Set();
|
|
1606
2170
|
for (const p of await this.search(void 0, "preset")) {
|
|
1607
|
-
const members = [...p.dependencies, ...p.requires.map(
|
|
2171
|
+
const members = [...p.dependencies, ...p.requires.map(depToStr)];
|
|
1608
2172
|
if (!members.some((m) => sameAsset(memberIdentity(m), target))) continue;
|
|
1609
2173
|
const publisher = p.publisher?.replace(/^@/, "");
|
|
1610
2174
|
const presetId = { kind: "preset", name: p.name, publisher };
|
|
@@ -1784,7 +2348,14 @@ var AssetManager = class {
|
|
|
1784
2348
|
return refs;
|
|
1785
2349
|
}
|
|
1786
2350
|
/**
|
|
1787
|
-
* Remove a deployed asset and unregister it from `skaile.yaml
|
|
2351
|
+
* Remove a deployed asset and unregister it from `skaile.yaml` and the lock.
|
|
2352
|
+
*
|
|
2353
|
+
* A declared asset is always unregistered, even when nothing is deployed any
|
|
2354
|
+
* more (e.g. its library file was deleted): the declaration and lock entry
|
|
2355
|
+
* would otherwise be stuck behind a "not deployed" refusal.
|
|
2356
|
+
*
|
|
2357
|
+
* Removes only `ref` itself; use {@link removeWithDependencies} to also drop the
|
|
2358
|
+
* dependencies it leaves orphaned.
|
|
1788
2359
|
*
|
|
1789
2360
|
* Deliberately does NOT re-render the driver target's MCP config: the render
|
|
1790
2361
|
* step is async (the declaration resolver is), and making this synchronous
|
|
@@ -1792,38 +2363,153 @@ var AssetManager = class {
|
|
|
1792
2363
|
* generated entry is dropped by the next {@link AssetManager.install} — see
|
|
1793
2364
|
* {@link renderMcpServerConfig}.
|
|
1794
2365
|
*
|
|
1795
|
-
* @param ref - Asset ref
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
2366
|
+
* @param ref - Asset ref: `kind:@publisher/name`, or the short `kind:name` /
|
|
2367
|
+
* `name` form when exactly one declared, locked or deployed asset matches it.
|
|
2368
|
+
* @returns `true` if anything changed (files undeployed, declaration or lock
|
|
2369
|
+
* entry removed), `false` if the asset was neither deployed nor declared.
|
|
2370
|
+
* A `preset:` ref has no deployed files: only its declaration goes — its
|
|
2371
|
+
* expanded items leave with the next `skaile install`.
|
|
2372
|
+
* @throws When `ref` is malformed, or a short ref matches several assets (the
|
|
2373
|
+
* message lists them).
|
|
1799
2374
|
*/
|
|
1800
2375
|
remove(ref) {
|
|
1801
|
-
const
|
|
1802
|
-
|
|
2376
|
+
const target = this.resolveRemoveTarget(ref);
|
|
2377
|
+
return target ? this.removeResolved(target) : false;
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* {@link remove} `ref`, then drop the dependencies that removal left orphaned:
|
|
2381
|
+
* locked assets `ref` required (directly or through one another) that
|
|
2382
|
+
* `skaile.yaml` no longer declares and no remaining locked asset requires. A
|
|
2383
|
+
* preset's items count as its requirements. They are undeployed and unlocked;
|
|
2384
|
+
* a dependency still declared or required elsewhere stays.
|
|
2385
|
+
*
|
|
2386
|
+
* @returns `changed` as {@link remove}; `pruned` the canonical refs
|
|
2387
|
+
* (`kind:@publisher/name`) of the dependencies dropped as well.
|
|
2388
|
+
*/
|
|
2389
|
+
async removeWithDependencies(ref) {
|
|
2390
|
+
const target = this.resolveRemoveTarget(ref);
|
|
2391
|
+
if (!target) return { changed: false, pruned: [] };
|
|
2392
|
+
const asked = target.publisher ? assetRefToStr({ kind: target.kind, name: target.name, publisher: target.publisher }) : `${target.kind}:${target.name}`;
|
|
2393
|
+
let entry = null;
|
|
2394
|
+
try {
|
|
2395
|
+
entry = await this.info(asked);
|
|
2396
|
+
} catch {
|
|
2397
|
+
}
|
|
2398
|
+
const requiredRefs = entry ? libraryEntryDepRefs(entry) : [];
|
|
2399
|
+
const changed = this.removeResolved(target);
|
|
2400
|
+
if (!changed || requiredRefs.length === 0) return { changed, pruned: [] };
|
|
2401
|
+
return { changed, pruned: await this.pruneOrphans(requiredRefs) };
|
|
2402
|
+
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Resolve the identity {@link remove} acts on. A publisher-qualified ref is taken
|
|
2405
|
+
* as written; a short ref (`kind:name`, or just `name`) is matched on kind + name
|
|
2406
|
+
* against the declared dependencies, the lock and the deployed assets, like
|
|
2407
|
+
* `why` / `diff` do.
|
|
2408
|
+
*
|
|
2409
|
+
* @returns the identity, or `null` when a name-only ref matches nothing.
|
|
2410
|
+
* @throws On a malformed ref, or when a short ref matches more than one asset.
|
|
2411
|
+
*/
|
|
2412
|
+
resolveRemoveTarget(ref) {
|
|
2413
|
+
const trimmed = ref.trim();
|
|
2414
|
+
try {
|
|
2415
|
+
return parseAssetRef(trimmed);
|
|
2416
|
+
} catch (err) {
|
|
2417
|
+
if (!(err instanceof Error) || !err.message.startsWith("publisher required")) throw err;
|
|
2418
|
+
const colon = trimmed.indexOf(":");
|
|
2419
|
+
const rawKind = colon === -1 ? void 0 : trimmed.slice(0, colon).trim();
|
|
2420
|
+
const kind = rawKind === "mcp" ? "mcp-server" : rawKind;
|
|
2421
|
+
const hash = trimmed.indexOf("#", colon + 1);
|
|
2422
|
+
const name = trimmed.slice(colon + 1, hash === -1 ? void 0 : hash).trim();
|
|
2423
|
+
if (!name || /[@/\s]/.test(name)) throw err;
|
|
2424
|
+
const found = [];
|
|
2425
|
+
const note = (id) => {
|
|
2426
|
+
if (!id?.kind || id.name !== name || kind && id.kind !== kind) return;
|
|
2427
|
+
found.push({ kind: id.kind, name: id.name, publisher: id.publisher });
|
|
2428
|
+
};
|
|
2429
|
+
try {
|
|
2430
|
+
for (const dep of this.loadConfig().dependencies) note(memberIdentity(dep));
|
|
2431
|
+
} catch {
|
|
2432
|
+
}
|
|
2433
|
+
for (const key of Object.keys(this.tryReadLock()?.assets ?? {})) {
|
|
2434
|
+
note(parseCanonicalLockKey(key));
|
|
2435
|
+
}
|
|
2436
|
+
for (const e of this.listDeployed()) note(e);
|
|
2437
|
+
const qualified = found.filter((f) => f.publisher);
|
|
2438
|
+
const distinct = /* @__PURE__ */ new Map();
|
|
2439
|
+
for (const f of found) {
|
|
2440
|
+
if (!f.publisher && qualified.some((q) => q.kind === f.kind)) continue;
|
|
2441
|
+
distinct.set(`${f.kind}:${f.publisher ?? ""}/${f.name}`, f);
|
|
2442
|
+
}
|
|
2443
|
+
const matches = [...distinct.values()];
|
|
2444
|
+
if (matches.length > 1) {
|
|
2445
|
+
const list = matches.map((m) => m.publisher ? `${m.kind}:@${m.publisher}/${m.name}` : `${m.kind}:${m.name}`).join(", ");
|
|
2446
|
+
throw new Error(`"${ref}" matches several assets (${list}) \u2014 name one with its full ref.`);
|
|
2447
|
+
}
|
|
2448
|
+
if (matches.length === 1) return matches[0];
|
|
2449
|
+
return kind ? { kind, name } : null;
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
/** Undeploy `parsed` from every target harness and drop its declaration and lock entry. */
|
|
2453
|
+
removeResolved(parsed) {
|
|
2454
|
+
let undeployed = false;
|
|
1803
2455
|
for (const t of this.targetHarnesses()) {
|
|
1804
|
-
if (removeAsset(parsed.kind, parsed.name, this.deployOptsFor(t)))
|
|
2456
|
+
if (removeAsset(parsed.kind, parsed.name, this.deployOptsFor(t))) undeployed = true;
|
|
1805
2457
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
2458
|
+
const undeclared = this.removeDependencyFromConfig(parsed);
|
|
2459
|
+
const unlocked = undeployed || undeclared ? this.pruneLockEntry(parsed) : false;
|
|
2460
|
+
const changed = undeployed || undeclared || unlocked;
|
|
2461
|
+
if (changed) {
|
|
1810
2462
|
appendHistory(this.projectDir, {
|
|
1811
|
-
ref:
|
|
2463
|
+
ref: `${parsed.kind}:${parsed.name}`,
|
|
1812
2464
|
action: "remove",
|
|
1813
2465
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1814
2466
|
context: this.global ? "global" : "project"
|
|
1815
2467
|
});
|
|
1816
2468
|
}
|
|
1817
|
-
return
|
|
2469
|
+
return changed;
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Drop the locked assets among `requiredRefs` (and what they require in turn) that
|
|
2473
|
+
* nothing needs any more: not declared in `skaile.yaml`, not required by a locked
|
|
2474
|
+
* asset outside that set. Call after the requiring asset is gone.
|
|
2475
|
+
*
|
|
2476
|
+
* @returns the canonical refs of the assets dropped.
|
|
2477
|
+
*/
|
|
2478
|
+
async pruneOrphans(requiredRefs) {
|
|
2479
|
+
const lock = this.tryReadLock();
|
|
2480
|
+
if (!lock) return [];
|
|
2481
|
+
const { requires, roots, findKey } = await this.lockedGraph(lock);
|
|
2482
|
+
const closure = (seeds) => {
|
|
2483
|
+
const out = new Set(seeds);
|
|
2484
|
+
for (const node of out) for (const dep of requires.get(node) ?? []) out.add(dep);
|
|
2485
|
+
return out;
|
|
2486
|
+
};
|
|
2487
|
+
const candidates = closure(requiredRefs.map(findKey).filter((k) => !!k));
|
|
2488
|
+
const needed = closure([
|
|
2489
|
+
...roots,
|
|
2490
|
+
...Object.keys(lock.assets).filter((k) => !candidates.has(k))
|
|
2491
|
+
]);
|
|
2492
|
+
const pruned = [];
|
|
2493
|
+
for (const key of candidates) {
|
|
2494
|
+
if (needed.has(key) || !lock.assets[key]) continue;
|
|
2495
|
+
const { kind, name, publisher } = parseCanonicalLockKey(key);
|
|
2496
|
+
if (!kind || !name) continue;
|
|
2497
|
+
this.removeResolved({ kind, name, publisher });
|
|
2498
|
+
this.pruneLockEntry({ kind, name, publisher });
|
|
2499
|
+
pruned.push(publisher ? assetRefToStr({ kind, name, publisher }) : `${kind}:${name}`);
|
|
2500
|
+
}
|
|
2501
|
+
return pruned;
|
|
1818
2502
|
}
|
|
1819
2503
|
/**
|
|
1820
2504
|
* Drop every lock entry matching a removed asset's identity, version-agnostic so
|
|
1821
2505
|
* a content-sha pseudo-version key is pruned too. Symmetric with {@link add}'s
|
|
1822
2506
|
* lock-merge — stops a stale entry reporting a phantom "removed" on next install.
|
|
2507
|
+
*
|
|
2508
|
+
* @returns `true` when at least one lock entry was dropped.
|
|
1823
2509
|
*/
|
|
1824
2510
|
pruneLockEntry(parsed) {
|
|
1825
2511
|
const lock = this.tryReadLock();
|
|
1826
|
-
if (!lock) return;
|
|
2512
|
+
if (!lock) return false;
|
|
1827
2513
|
let changed = false;
|
|
1828
2514
|
for (const key of Object.keys(lock.assets)) {
|
|
1829
2515
|
const id = parseCanonicalLockKey(key);
|
|
@@ -1833,6 +2519,7 @@ var AssetManager = class {
|
|
|
1833
2519
|
}
|
|
1834
2520
|
}
|
|
1835
2521
|
if (changed) writeLock(this.lockPath, lock);
|
|
2522
|
+
return changed;
|
|
1836
2523
|
}
|
|
1837
2524
|
// ── Query ──────────────────────────────────────────────────────────────
|
|
1838
2525
|
/**
|
|
@@ -1993,9 +2680,15 @@ var AssetManager = class {
|
|
|
1993
2680
|
for (const { kind, name, component: k } of tracked.values()) {
|
|
1994
2681
|
const legacy = deployedDir(k, name, "claude-code", false, this.projectDir);
|
|
1995
2682
|
if (legacy === deployedDir(k, name, this.driverTarget, false, this.projectDir)) continue;
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2683
|
+
const found = deployedPaths(k, name, "claude-code", false, this.projectDir).find((p) => {
|
|
2684
|
+
try {
|
|
2685
|
+
lstatSync(p);
|
|
2686
|
+
return true;
|
|
2687
|
+
} catch {
|
|
2688
|
+
return false;
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
if (found) out.push({ kind, name, path: found });
|
|
1999
2692
|
}
|
|
2000
2693
|
return out.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
|
|
2001
2694
|
}
|
|
@@ -2029,6 +2722,7 @@ var AssetManager = class {
|
|
|
2029
2722
|
const dependencies = resolveSkWorkspaceConfig(this.projectDir).dependencies ?? [];
|
|
2030
2723
|
const lock = this.tryReadLock();
|
|
2031
2724
|
const lockKeys = lock ? Object.keys(lock.assets).map(parseCanonicalLockKey) : [];
|
|
2725
|
+
const presetGaps = await this.declaredPresetGaps(dependencies, lock);
|
|
2032
2726
|
let wiredMcps;
|
|
2033
2727
|
const wiredMcpNames = async () => {
|
|
2034
2728
|
if (wiredMcps === void 0) {
|
|
@@ -2051,7 +2745,7 @@ var AssetManager = class {
|
|
|
2051
2745
|
continue;
|
|
2052
2746
|
}
|
|
2053
2747
|
if (kind && parsed.kind !== kind) continue;
|
|
2054
|
-
const locked = parsed.kind === "preset" ? lock !== null : lockKeys.some(
|
|
2748
|
+
const locked = parsed.kind === "preset" ? lock !== null && !presetGaps.get(ref)?.length : lockKeys.some(
|
|
2055
2749
|
(k) => k.kind === parsed.kind && k.name === parsed.name && (!parsed.publisher || k.publisher === parsed.publisher)
|
|
2056
2750
|
);
|
|
2057
2751
|
let state;
|
|
@@ -2079,6 +2773,38 @@ var AssetManager = class {
|
|
|
2079
2773
|
}
|
|
2080
2774
|
return out;
|
|
2081
2775
|
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Library-locked assets whose deployed directory lacks files the deploy writes —
|
|
2778
|
+
* a deleted `build.py` / `scripts/` helper next to an intact `SKILL.md`. The
|
|
2779
|
+
* expected set is the library entry's file set (every file under its manifest
|
|
2780
|
+
* `root:`), the same bytes the lock hashes; only presence is checked, so a
|
|
2781
|
+
* local edit never counts. An asset with no deployed directory is not listed
|
|
2782
|
+
* (`listDeclared` reports it as drift); an agent is rendered, not copied, so it
|
|
2783
|
+
* has no file mirror to compare. `skaile install` restores what is listed.
|
|
2784
|
+
*
|
|
2785
|
+
* @returns One {@link IncompleteDeploy} per affected asset; empty when healthy
|
|
2786
|
+
* or when the project has no lock.
|
|
2787
|
+
*/
|
|
2788
|
+
async incompleteDeploys() {
|
|
2789
|
+
const lock = this.tryReadLock();
|
|
2790
|
+
if (!lock) return [];
|
|
2791
|
+
const locked = Object.entries(lock.assets).filter(([, e]) => e.library !== void 0 && !e.library.installed).map(([key]) => key);
|
|
2792
|
+
if (locked.length === 0) return [];
|
|
2793
|
+
const lib = await this.resolveLibraryEntries(locked, { quiet: true });
|
|
2794
|
+
const out = [];
|
|
2795
|
+
for (const entry of lib.entries) {
|
|
2796
|
+
const component = this.installTarget(entry.kind);
|
|
2797
|
+
if (!entry.files || !component || component === "agent") continue;
|
|
2798
|
+
const dest = join(
|
|
2799
|
+
deployedBase(component, this.driverTarget, this.global, this.projectDir),
|
|
2800
|
+
entry.name
|
|
2801
|
+
);
|
|
2802
|
+
if (!existsSync(dest)) continue;
|
|
2803
|
+
const missing = entry.files.filter((rel) => !existsSync(join(dest, rel)));
|
|
2804
|
+
if (missing.length > 0) out.push({ ref: canonicalEntryRef(entry), missing });
|
|
2805
|
+
}
|
|
2806
|
+
return out;
|
|
2807
|
+
}
|
|
2082
2808
|
/**
|
|
2083
2809
|
* Per-harness reconciliation of the **global** declared deps
|
|
2084
2810
|
* (`~/.skaile/skaile.yaml`) against each global harness's deploy dir. The
|
|
@@ -2176,70 +2902,197 @@ var AssetManager = class {
|
|
|
2176
2902
|
}
|
|
2177
2903
|
return repoStatuses;
|
|
2178
2904
|
}
|
|
2905
|
+
/**
|
|
2906
|
+
* Requirement graph of the locked assets, for {@link tree} / {@link why} / removal pruning.
|
|
2907
|
+
*
|
|
2908
|
+
* The lock records no edges, so they are rebuilt from each locked asset's own
|
|
2909
|
+
* declaration (`requires`, manifest `dependencies`, a preset's items) — the same
|
|
2910
|
+
* source the install walk reads, via {@link info}. An asset that no source or
|
|
2911
|
+
* library can be found for has no known requirements. Nodes are lock keys, plus
|
|
2912
|
+
* each declared `preset:` that the lock does not carry (a preset is expanded at
|
|
2913
|
+
* install, never locked), keyed by its declared ref.
|
|
2914
|
+
*
|
|
2915
|
+
* @returns `requires` (node → the lock keys it requires), `roots` (nodes
|
|
2916
|
+
* `skaile.yaml` declares) and `findKey` (a ref → its lock key, version-agnostic).
|
|
2917
|
+
*/
|
|
2918
|
+
async lockedGraph(lock) {
|
|
2919
|
+
const locked = Object.keys(lock.assets).map((key) => ({ key, ...parseCanonicalLockKey(key) }));
|
|
2920
|
+
const findKey = (ref) => {
|
|
2921
|
+
const id = memberIdentity(ref);
|
|
2922
|
+
if (!id) return void 0;
|
|
2923
|
+
return locked.find(
|
|
2924
|
+
(l) => l.kind === id.kind && l.name === id.name && (!id.publisher || !l.publisher || l.publisher === id.publisher)
|
|
2925
|
+
)?.key;
|
|
2926
|
+
};
|
|
2927
|
+
const requires = /* @__PURE__ */ new Map();
|
|
2928
|
+
const link = async (node, ref) => {
|
|
2929
|
+
const out = [];
|
|
2930
|
+
try {
|
|
2931
|
+
const entry = await this.info(ref);
|
|
2932
|
+
for (const dep of entry ? libraryEntryDepRefs(entry) : []) {
|
|
2933
|
+
const key = findKey(dep);
|
|
2934
|
+
if (key && key !== node && !out.includes(key)) out.push(key);
|
|
2935
|
+
}
|
|
2936
|
+
} catch {
|
|
2937
|
+
}
|
|
2938
|
+
requires.set(node, out);
|
|
2939
|
+
};
|
|
2940
|
+
for (const l of locked) {
|
|
2941
|
+
await link(
|
|
2942
|
+
l.key,
|
|
2943
|
+
l.publisher ? `${l.kind}:@${l.publisher}/${l.name}` : `${l.kind}:${l.name}`
|
|
2944
|
+
);
|
|
2945
|
+
}
|
|
2946
|
+
const roots = [];
|
|
2947
|
+
let declared = [];
|
|
2948
|
+
try {
|
|
2949
|
+
declared = this.loadConfig().dependencies;
|
|
2950
|
+
} catch {
|
|
2951
|
+
}
|
|
2952
|
+
for (const dep of declared) {
|
|
2953
|
+
const key = findKey(dep);
|
|
2954
|
+
if (key) {
|
|
2955
|
+
if (!roots.includes(key)) roots.push(key);
|
|
2956
|
+
} else if (memberIdentity(dep)?.kind === "preset" && !roots.includes(dep)) {
|
|
2957
|
+
await link(dep, dep);
|
|
2958
|
+
roots.push(dep);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
return { requires, roots, findKey };
|
|
2962
|
+
}
|
|
2179
2963
|
/**
|
|
2180
2964
|
* Build the resolved dependency tree from the lock file.
|
|
2181
2965
|
*
|
|
2966
|
+
* Required assets nest under the asset that requires them (see {@link lockedGraph});
|
|
2967
|
+
* an asset required by several parents appears under each, and a dependency cycle
|
|
2968
|
+
* is cut where it would repeat.
|
|
2969
|
+
*
|
|
2182
2970
|
* @returns Root {@link DependencyNode} with `ref === "project"` whose children
|
|
2183
|
-
* are the directly-declared
|
|
2971
|
+
* are the directly-declared assets, followed by locked assets nothing requires,
|
|
2972
|
+
* each with the assets it requires as children.
|
|
2184
2973
|
* Returns a single node with `ref === "(no lock file)"` when no lock exists.
|
|
2185
2974
|
*/
|
|
2186
|
-
tree() {
|
|
2975
|
+
async tree() {
|
|
2187
2976
|
const lock = this.tryReadLock();
|
|
2188
2977
|
if (!lock) return { ref: "(no lock file)", children: [] };
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2978
|
+
const { requires, roots } = await this.lockedGraph(lock);
|
|
2979
|
+
const required = new Set([...requires.values()].flat());
|
|
2980
|
+
const build = (ref, path2) => ({
|
|
2981
|
+
ref,
|
|
2982
|
+
children: (requires.get(ref) ?? []).filter((child) => !path2.has(child)).map((child) => build(child, /* @__PURE__ */ new Set([...path2, child])))
|
|
2983
|
+
});
|
|
2984
|
+
const shown = /* @__PURE__ */ new Set();
|
|
2985
|
+
const top = [];
|
|
2986
|
+
const addTop = (ref) => {
|
|
2987
|
+
const node = build(ref, /* @__PURE__ */ new Set([ref]));
|
|
2988
|
+
top.push(node);
|
|
2989
|
+
const walk = (n) => {
|
|
2990
|
+
shown.add(n.ref);
|
|
2991
|
+
for (const c of n.children) walk(c);
|
|
2992
|
+
};
|
|
2993
|
+
walk(node);
|
|
2192
2994
|
};
|
|
2995
|
+
const lockKeys = Object.keys(lock.assets);
|
|
2996
|
+
for (const ref of roots) addTop(ref);
|
|
2997
|
+
for (const ref of lockKeys) if (!roots.includes(ref) && !required.has(ref)) addTop(ref);
|
|
2998
|
+
for (const ref of lockKeys) if (!shown.has(ref)) addTop(ref);
|
|
2999
|
+
return { ref: "project", children: top };
|
|
2193
3000
|
}
|
|
2194
3001
|
/**
|
|
2195
|
-
* Explain why an asset is installed by tracing its dependency chain
|
|
3002
|
+
* Explain why an asset is installed by tracing its dependency chain.
|
|
3003
|
+
*
|
|
3004
|
+
* The chain is read from the requirement graph ({@link lockedGraph}): the asset,
|
|
3005
|
+
* then the asset that requires it, and so on up to a dependency `skaile.yaml`
|
|
3006
|
+
* declares (the shortest such path; cycles are not followed twice).
|
|
2196
3007
|
*
|
|
2197
3008
|
* @param ref - Asset ref in `kind:name` form.
|
|
2198
|
-
* @returns Chain of refs from the asset back to `"direct (skaile.yaml)"
|
|
2199
|
-
*
|
|
3009
|
+
* @returns Chain of refs from the asset back to `"direct (skaile.yaml)"`; an asset
|
|
3010
|
+
* no declared dependency reaches ends in `"not required by a declared dependency"`
|
|
3011
|
+
* instead. `[]` when the asset is not in the lock file.
|
|
2200
3012
|
*/
|
|
2201
|
-
why(ref) {
|
|
3013
|
+
async why(ref) {
|
|
2202
3014
|
const lock = this.tryReadLock();
|
|
2203
3015
|
if (!lock) return [];
|
|
2204
3016
|
const bare = (s) => s.includes("#") ? s.slice(0, s.indexOf("#")) : s;
|
|
2205
|
-
const
|
|
3017
|
+
const asked = parseRefTolerant(ref);
|
|
3018
|
+
const matchesIdentity = (k) => {
|
|
3019
|
+
if (bare(k) === bare(ref)) return true;
|
|
3020
|
+
if (!asked || asked.publisher) return false;
|
|
3021
|
+
const locked = parseCanonicalLockKey(k);
|
|
3022
|
+
return locked.kind === asked.kind && locked.name === asked.name;
|
|
3023
|
+
};
|
|
3024
|
+
const key = lock.assets[ref] ? ref : Object.keys(lock.assets).find(matchesIdentity);
|
|
2206
3025
|
if (!key) return [];
|
|
2207
|
-
|
|
3026
|
+
const { requires, roots } = await this.lockedGraph(lock);
|
|
3027
|
+
if (roots.includes(key)) return [key, "direct (skaile.yaml)"];
|
|
3028
|
+
const requirers = /* @__PURE__ */ new Map();
|
|
3029
|
+
for (const [node, deps] of requires) {
|
|
3030
|
+
for (const dep of deps) requirers.set(dep, [...requirers.get(dep) ?? [], node]);
|
|
3031
|
+
}
|
|
3032
|
+
const child = /* @__PURE__ */ new Map();
|
|
3033
|
+
const seen = /* @__PURE__ */ new Set([key]);
|
|
3034
|
+
const queue = [key];
|
|
3035
|
+
for (let node = queue.shift(); node !== void 0; node = queue.shift()) {
|
|
3036
|
+
for (const parent of requirers.get(node) ?? []) {
|
|
3037
|
+
if (seen.has(parent)) continue;
|
|
3038
|
+
seen.add(parent);
|
|
3039
|
+
child.set(parent, node);
|
|
3040
|
+
if (roots.includes(parent)) {
|
|
3041
|
+
const chain = [];
|
|
3042
|
+
for (let c = parent; c !== void 0; c = child.get(c)) chain.push(c);
|
|
3043
|
+
return [...chain.reverse(), "direct (skaile.yaml)"];
|
|
3044
|
+
}
|
|
3045
|
+
queue.push(parent);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
return [key, "not required by a declared dependency"];
|
|
2208
3049
|
}
|
|
2209
3050
|
/**
|
|
2210
3051
|
* List installed assets that have a newer upstream available.
|
|
2211
3052
|
*
|
|
2212
3053
|
* Two kinds of staleness are reported:
|
|
2213
3054
|
* - **Source assets** — only remote repositories (not local paths) are checked;
|
|
2214
|
-
* `behind`
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
*
|
|
3055
|
+
* `behind` counts the commits the clone is missing from the remote plus the
|
|
3056
|
+
* commits the clone moved past the locked commit (`source sync` after `add`).
|
|
3057
|
+
* - **Library assets** — a library-origin lock entry whose library files on disk
|
|
3058
|
+
* (not the index, which lags until `skaile library index`) hold a newer version
|
|
3059
|
+
* than the locked one (`latestVersion`, `library` set), or the same version with
|
|
3060
|
+
* different bytes (`contentChanged`). `behind` is 0. An unregistered library is a
|
|
3061
|
+
* `problem` row.
|
|
3062
|
+
* - **Lock gaps** — every {@link AssetManager.lockGaps} gap (a declared dependency or a
|
|
3063
|
+
* deployed asset's `requires` with no lock entry, a lock that contradicts a
|
|
3064
|
+
* `skaile.yaml` pin) as a `problem` row, so `outdated` agrees with `check`.
|
|
2218
3065
|
*
|
|
2219
3066
|
* @returns Array of {@link OutdatedEntry} objects.
|
|
2220
3067
|
*/
|
|
2221
3068
|
async outdated() {
|
|
2222
3069
|
const { repositories } = this.loadConfig();
|
|
2223
3070
|
const lock = this.tryReadLock();
|
|
2224
|
-
if (!lock) return [];
|
|
3071
|
+
if (!lock) return this.addLockGapRows([]);
|
|
2225
3072
|
const result = [];
|
|
2226
3073
|
const reported = /* @__PURE__ */ new Set();
|
|
3074
|
+
const commitCounts = /* @__PURE__ */ new Map();
|
|
2227
3075
|
for (const src of lock.sources) {
|
|
2228
3076
|
const slug = sourceCacheKey(src.url);
|
|
2229
3077
|
const decl = repositories[slug];
|
|
2230
3078
|
if (!decl) continue;
|
|
2231
3079
|
const status = checkRepoStatus(decl, slug, this.reposDir);
|
|
2232
|
-
if (status.
|
|
3080
|
+
if (status.kind !== "remote" || !status.localHead) continue;
|
|
3081
|
+
const clone = decl.path ?? join(this.reposDir, slug);
|
|
2233
3082
|
for (const [ref, entry] of Object.entries(lock.assets)) {
|
|
2234
3083
|
if (entry.source?.url !== src.url) continue;
|
|
2235
3084
|
const { publisher, kind, name, version } = parseCanonicalLockKey(ref);
|
|
2236
3085
|
if (!kind || !name) continue;
|
|
3086
|
+
const lockedCommit = entry.source.commit || src.commit;
|
|
3087
|
+
const moved = lockedCommit && !/^0+$/.test(lockedCommit) && lockedCommit !== status.localHead ? commitsBetween(clone, lockedCommit, status.localHead, commitCounts) : 0;
|
|
3088
|
+
const behind = status.behind + moved;
|
|
3089
|
+
if (behind <= 0) continue;
|
|
2237
3090
|
result.push({
|
|
2238
3091
|
kind,
|
|
2239
3092
|
name,
|
|
2240
3093
|
publisher: publisher ?? sourceShortName(src.url),
|
|
2241
3094
|
currentVersion: version ?? "",
|
|
2242
|
-
behind
|
|
3095
|
+
behind
|
|
2243
3096
|
});
|
|
2244
3097
|
reported.add(ref);
|
|
2245
3098
|
}
|
|
@@ -2249,11 +3102,17 @@ var AssetManager = class {
|
|
|
2249
3102
|
);
|
|
2250
3103
|
if (libraryAssets.length > 0) {
|
|
2251
3104
|
const snap = await this.loadLibraryIndex();
|
|
3105
|
+
const diskHashes = /* @__PURE__ */ new Map();
|
|
3106
|
+
const scans = /* @__PURE__ */ new Map();
|
|
2252
3107
|
for (const [ref, entry] of libraryAssets) {
|
|
2253
3108
|
const origin = entry.library;
|
|
2254
3109
|
const { publisher, kind, name } = parseCanonicalLockKey(ref);
|
|
2255
3110
|
if (!publisher || !kind || !name) continue;
|
|
2256
|
-
|
|
3111
|
+
const libPath = this.libraryPath(origin.name, snap);
|
|
3112
|
+
if (!libPath) {
|
|
3113
|
+
const canonical = `${kind}:@${publisher}/${name}`;
|
|
3114
|
+
const heldElsewhere = (await this.resolveLibraryEntries([canonical], { quiet: true })).resolved.size > 0;
|
|
3115
|
+
const fix = heldElsewhere ? void 0 : (await this.unresolvedFixes([canonical])).get(canonical) ?? unregisteredLibraryFix(origin.name, canonical);
|
|
2257
3116
|
result.push({
|
|
2258
3117
|
kind,
|
|
2259
3118
|
name,
|
|
@@ -2261,23 +3120,55 @@ var AssetManager = class {
|
|
|
2261
3120
|
currentVersion: origin.version,
|
|
2262
3121
|
behind: 0,
|
|
2263
3122
|
library: origin.name,
|
|
2264
|
-
|
|
3123
|
+
...fix === void 0 ? {
|
|
3124
|
+
problem: `library "${origin.name}" is not registered (renamed?) \u2014 run \`skaile install\` to re-lock it against the library that holds it now`
|
|
3125
|
+
} : {
|
|
3126
|
+
problem: `library "${origin.name}" is not registered (renamed or removed?) \u2014 ${fix}`,
|
|
3127
|
+
manualFix: true
|
|
3128
|
+
}
|
|
2265
3129
|
});
|
|
2266
3130
|
reported.add(ref);
|
|
2267
3131
|
continue;
|
|
2268
3132
|
}
|
|
2269
|
-
const
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
3133
|
+
const scanKey = `${libPath}\0${publisher}`;
|
|
3134
|
+
let scanned = scans.get(scanKey);
|
|
3135
|
+
if (!scanned) {
|
|
3136
|
+
scanned = await scanDirectory(libPath, publisher, { knownKinds: this.knownKinds });
|
|
3137
|
+
scans.set(scanKey, scanned);
|
|
3138
|
+
}
|
|
3139
|
+
const found = scanned.find((x) => x.kind === kind && x.name === name);
|
|
3140
|
+
if (!found) continue;
|
|
3141
|
+
const diskVersion = found.version || newestLibraryDef(snap, origin.name, { kind, publisher, name })?.version;
|
|
3142
|
+
if (diskVersion && gt(coerceVersion(diskVersion), coerceVersion(origin.version))) {
|
|
3143
|
+
result.push({
|
|
3144
|
+
kind,
|
|
3145
|
+
name,
|
|
3146
|
+
publisher,
|
|
3147
|
+
currentVersion: origin.version,
|
|
3148
|
+
behind: 0,
|
|
3149
|
+
latestVersion: diskVersion,
|
|
3150
|
+
library: origin.name
|
|
3151
|
+
});
|
|
3152
|
+
reported.add(ref);
|
|
3153
|
+
continue;
|
|
3154
|
+
}
|
|
3155
|
+
if (origin.sha256) {
|
|
3156
|
+
const onDisk = libraryDiskHashes(libPath, diskHashes, this.registry).get(
|
|
3157
|
+
`${kind}:${name}`
|
|
3158
|
+
);
|
|
3159
|
+
if (onDisk !== void 0 && onDisk !== origin.sha256) {
|
|
3160
|
+
result.push({
|
|
3161
|
+
kind,
|
|
3162
|
+
name,
|
|
3163
|
+
publisher,
|
|
3164
|
+
currentVersion: origin.version,
|
|
3165
|
+
behind: 0,
|
|
3166
|
+
library: origin.name,
|
|
3167
|
+
contentChanged: true
|
|
3168
|
+
});
|
|
3169
|
+
reported.add(ref);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
2281
3172
|
}
|
|
2282
3173
|
}
|
|
2283
3174
|
for (const [ref, entry] of Object.entries(lock.assets)) {
|
|
@@ -2298,7 +3189,30 @@ var AssetManager = class {
|
|
|
2298
3189
|
problem: "deployed copy missing"
|
|
2299
3190
|
});
|
|
2300
3191
|
}
|
|
2301
|
-
return result;
|
|
3192
|
+
return this.addLockGapRows(result);
|
|
3193
|
+
}
|
|
3194
|
+
/**
|
|
3195
|
+
* Append one `problem` row per {@link AssetManager.lockGaps} gap whose asset is not
|
|
3196
|
+
* listed yet, so `outdated` reports the same set `check` and `install --locked` fail on.
|
|
3197
|
+
*/
|
|
3198
|
+
async addLockGapRows(rows) {
|
|
3199
|
+
const listed = new Set(rows.map((r) => `${r.kind}:${r.name}`));
|
|
3200
|
+
for (const gap of await this.lockGaps()) {
|
|
3201
|
+
const parsed = parseRefTolerant(gap.ref);
|
|
3202
|
+
const id = parsed ? `${parsed.kind}:${parsed.name}` : gap.ref;
|
|
3203
|
+
if (listed.has(id)) continue;
|
|
3204
|
+
listed.add(id);
|
|
3205
|
+
rows.push({
|
|
3206
|
+
kind: parsed?.kind ?? "",
|
|
3207
|
+
name: parsed?.name ?? gap.ref,
|
|
3208
|
+
publisher: parsed?.publisher ?? "",
|
|
3209
|
+
currentVersion: "",
|
|
3210
|
+
behind: 0,
|
|
3211
|
+
problem: gap.reason,
|
|
3212
|
+
...gap.manualFix ? { manualFix: true } : {}
|
|
3213
|
+
});
|
|
3214
|
+
}
|
|
3215
|
+
return rows;
|
|
2302
3216
|
}
|
|
2303
3217
|
/**
|
|
2304
3218
|
* Produce a unified diff from the deployed asset to its catalog source: `-` lines
|
|
@@ -2315,7 +3229,8 @@ var AssetManager = class {
|
|
|
2315
3229
|
* @throws When `diff(1)` itself fails (exit status other than 0/1).
|
|
2316
3230
|
*/
|
|
2317
3231
|
async diff(ref) {
|
|
2318
|
-
const parsed =
|
|
3232
|
+
const parsed = parseRefTolerant(ref);
|
|
3233
|
+
if (!parsed) return { status: "not-found" };
|
|
2319
3234
|
const entry = await this.info(ref);
|
|
2320
3235
|
if (!entry || !existsSync(entry.source)) return { status: "not-found" };
|
|
2321
3236
|
const component = this.installTarget(parsed.kind);
|
|
@@ -2325,10 +3240,20 @@ var AssetManager = class {
|
|
|
2325
3240
|
parsed.name
|
|
2326
3241
|
);
|
|
2327
3242
|
if (!existsSync(deployedPath)) return { status: "not-deployed" };
|
|
2328
|
-
const
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
3243
|
+
const pairs = entry.files ? entry.files.map((rel) => [join(deployedPath, rel), join(dirname(entry.source), rel)]) : [[deployedPath, entry.source]];
|
|
3244
|
+
const diffs = [];
|
|
3245
|
+
for (const [from, to] of pairs) {
|
|
3246
|
+
const r = spawnSync("diff", [entry.files ? "-uN" : "-ruN", from, to], {
|
|
3247
|
+
encoding: "utf8"
|
|
3248
|
+
});
|
|
3249
|
+
if (r.status === 1) diffs.push(r.stdout);
|
|
3250
|
+
else if (r.status !== 0) {
|
|
3251
|
+
throw new Error(
|
|
3252
|
+
`diff failed for ${ref}: ${r.stderr || r.error?.message || "unknown error"}`
|
|
3253
|
+
);
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
return diffs.length > 0 ? { status: "differs", diff: diffs.join("") } : { status: "identical" };
|
|
2332
3257
|
}
|
|
2333
3258
|
// ── Lock ───────────────────────────────────────────────────────────────
|
|
2334
3259
|
/**
|
|
@@ -2346,12 +3271,13 @@ var AssetManager = class {
|
|
|
2346
3271
|
projectDir: this.projectDir,
|
|
2347
3272
|
knownKinds: this.knownKinds
|
|
2348
3273
|
});
|
|
2349
|
-
const { resolved,
|
|
3274
|
+
const { resolved, overridesApplied, lib } = await this.resolveLibraryFirst(dependencies, {
|
|
2350
3275
|
provenanceIndex,
|
|
2351
3276
|
overrides,
|
|
2352
|
-
stores
|
|
3277
|
+
stores,
|
|
3278
|
+
declaredRefs: dependencies
|
|
2353
3279
|
});
|
|
2354
|
-
const libraryLock = await this.libraryLockEntries(
|
|
3280
|
+
const libraryLock = await this.libraryLockEntries(lib);
|
|
2355
3281
|
const lockData = buildLockFile(resolved, overridesApplied, libraryLock);
|
|
2356
3282
|
writeLock(this.lockPath, lockData);
|
|
2357
3283
|
return lockData;
|
|
@@ -2445,37 +3371,245 @@ var AssetManager = class {
|
|
|
2445
3371
|
*
|
|
2446
3372
|
* For each deployed asset, checks that all entries in its `requires` list are
|
|
2447
3373
|
* also deployed. Returns a list of issues with a flag indicating whether the
|
|
2448
|
-
* missing dependency can be resolved from a configured repository.
|
|
3374
|
+
* missing dependency can be resolved from a configured repository or library.
|
|
3375
|
+
*
|
|
3376
|
+
* A deployed asset is looked up by its publisher-qualified identity taken from
|
|
3377
|
+
* the lock key (`kind:@pub/name`), so library and source assets resolve alike.
|
|
3378
|
+
* A lock-managed asset that no source or library can be found for is reported
|
|
3379
|
+
* as an issue with `unresolved: true` (its `requires` cannot be verified);
|
|
3380
|
+
* unmanaged deployed files carry no declared origin and are skipped.
|
|
2449
3381
|
*
|
|
2450
3382
|
* @returns Array of {@link DepIssue} objects. An empty array means the workspace is healthy.
|
|
2451
3383
|
*/
|
|
2452
3384
|
async doctor() {
|
|
2453
|
-
const { repositories } = this.loadConfig();
|
|
3385
|
+
const { repositories, dependencies } = this.loadConfig();
|
|
2454
3386
|
const deployed = this.listDeployed();
|
|
2455
3387
|
const issues = [];
|
|
3388
|
+
const lock = this.tryReadLock();
|
|
3389
|
+
const lockedPublisher = {};
|
|
3390
|
+
for (const key of Object.keys(lock?.assets ?? {})) {
|
|
3391
|
+
const { kind, name, publisher } = parseCanonicalLockKey(key);
|
|
3392
|
+
if (kind && name && publisher) lockedPublisher[`${kind}:${name}`] = publisher;
|
|
3393
|
+
}
|
|
2456
3394
|
for (const entry of deployed) {
|
|
2457
|
-
const
|
|
2458
|
-
|
|
3395
|
+
const publisher = lockedPublisher[`${entry.kind}:${entry.name}`];
|
|
3396
|
+
const full = await this.info(
|
|
3397
|
+
publisher ? `${entry.kind}:@${publisher}/${entry.name}` : `${entry.kind}:${entry.name}`
|
|
3398
|
+
);
|
|
3399
|
+
if (!full) {
|
|
3400
|
+
if (publisher) {
|
|
3401
|
+
const canonical = `${entry.kind}:@${publisher}/${entry.name}`;
|
|
3402
|
+
const fix = (await this.unresolvedFixes([canonical])).get(canonical);
|
|
3403
|
+
issues.push({
|
|
3404
|
+
assetKind: entry.kind,
|
|
3405
|
+
assetName: entry.name,
|
|
3406
|
+
depKind: entry.kind,
|
|
3407
|
+
depName: entry.name,
|
|
3408
|
+
inRepos: false,
|
|
3409
|
+
unresolved: true,
|
|
3410
|
+
...fix ? { fix } : {}
|
|
3411
|
+
});
|
|
3412
|
+
}
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
const gaps = [];
|
|
2459
3416
|
for (const req of full.requires) {
|
|
2460
|
-
const dep = memberIdentity(
|
|
3417
|
+
const dep = memberIdentity(depToStr(req));
|
|
2461
3418
|
if (!dep) continue;
|
|
2462
3419
|
const depComponent = this.installTarget(dep.kind);
|
|
2463
3420
|
if (depComponent && !isDeployed(depComponent, dep.name, this.driverTarget, this.global, this.projectDir)) {
|
|
2464
3421
|
const inRepos = await resolveAsset({ kind: dep.kind, name: dep.name }, repositories, this.reposDir, {
|
|
2465
3422
|
knownKinds: this.knownKinds
|
|
2466
3423
|
}) !== null;
|
|
2467
|
-
|
|
3424
|
+
const issue = {
|
|
2468
3425
|
assetKind: entry.kind,
|
|
2469
3426
|
assetName: entry.name,
|
|
2470
3427
|
depKind: req.kind,
|
|
2471
|
-
depName: req.name,
|
|
3428
|
+
depName: req.publisher ? `@${req.publisher}/${req.name}` : req.name,
|
|
2472
3429
|
inRepos
|
|
2473
|
-
}
|
|
3430
|
+
};
|
|
3431
|
+
issues.push(issue);
|
|
3432
|
+
if (!inRepos) gaps.push({ issue, ref: depToStr(req) });
|
|
2474
3433
|
}
|
|
2475
3434
|
}
|
|
3435
|
+
const inLibrary = await this.resolveLibraryEntries(gaps.map((g) => g.ref));
|
|
3436
|
+
for (const g of gaps) g.issue.inRepos = inLibrary.resolved.has(g.ref);
|
|
3437
|
+
}
|
|
3438
|
+
for (const [presetRef, gaps] of await this.declaredPresetGaps(dependencies, lock)) {
|
|
3439
|
+
const preset = parseRefTolerant(presetRef);
|
|
3440
|
+
if (!preset) continue;
|
|
3441
|
+
for (const g of gaps) {
|
|
3442
|
+
const item = parseRefTolerant(g.ref);
|
|
3443
|
+
if (!item) continue;
|
|
3444
|
+
issues.push({
|
|
3445
|
+
assetKind: "preset",
|
|
3446
|
+
assetName: preset.name,
|
|
3447
|
+
depKind: item.kind,
|
|
3448
|
+
depName: item.publisher ? `@${item.publisher}/${item.name}` : item.name,
|
|
3449
|
+
inRepos: g.resolvable
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
2476
3452
|
}
|
|
2477
3453
|
return issues;
|
|
2478
3454
|
}
|
|
3455
|
+
/**
|
|
3456
|
+
* Everything the lock does not cover or contradicts, in one list: the set
|
|
3457
|
+
* `skaile check`, `skaile install --locked` and `skaile outdated` all report.
|
|
3458
|
+
*
|
|
3459
|
+
* - `declared` — a `skaile.yaml` dependency with no lock entry (library or source gone,
|
|
3460
|
+
* unresolvable ref, never installed);
|
|
3461
|
+
* - `invalid` — a `dependencies:` entry that is not a parseable ref;
|
|
3462
|
+
* - `drift` — declared + locked but its deployed files are missing;
|
|
3463
|
+
* - `pin` — a declared `#pin` that no lock entry for that asset satisfies;
|
|
3464
|
+
* - `requires` — a deployed asset's `requires` that is not deployed (so not locked either);
|
|
3465
|
+
* - `unresolved` — a lock-managed asset that no source or library holds any more.
|
|
3466
|
+
*
|
|
3467
|
+
* Project mode only: a global manager (`global: true`) returns `[]`. A project without
|
|
3468
|
+
* `skaile.yaml` has no declared set, so only the `requires` / `unresolved` gaps of
|
|
3469
|
+
* deployed assets can appear.
|
|
3470
|
+
*
|
|
3471
|
+
* @returns One {@link LockGap} per gap; empty when the lock covers everything declared.
|
|
3472
|
+
*/
|
|
3473
|
+
async lockGaps() {
|
|
3474
|
+
if (this.global) return [];
|
|
3475
|
+
const gaps = [];
|
|
3476
|
+
const issues = await this.doctor();
|
|
3477
|
+
for (const i of issues) {
|
|
3478
|
+
if (i.unresolved) {
|
|
3479
|
+
const ref = `${i.assetKind}:${i.assetName}`;
|
|
3480
|
+
const reason = `is locked but no source or library provides it any more \u2014 its requirements cannot be checked${i.fix ? ` (${i.fix})` : ""}`;
|
|
3481
|
+
gaps.push({
|
|
3482
|
+
type: "unresolved",
|
|
3483
|
+
ref,
|
|
3484
|
+
reason,
|
|
3485
|
+
message: `${ref} ${reason}`,
|
|
3486
|
+
...i.fix ? { manualFix: true } : {}
|
|
3487
|
+
});
|
|
3488
|
+
continue;
|
|
3489
|
+
}
|
|
3490
|
+
const asset = `${i.assetKind}:${i.assetName}`;
|
|
3491
|
+
const dep = `${i.depKind}:${i.depName}`;
|
|
3492
|
+
const fix = i.inRepos ? "fixable" : "not found";
|
|
3493
|
+
gaps.push({
|
|
3494
|
+
type: "requires",
|
|
3495
|
+
ref: dep,
|
|
3496
|
+
reason: `is required by ${asset} but neither deployed nor locked (${fix})`,
|
|
3497
|
+
message: `${asset} requires ${dep} (${fix})`
|
|
3498
|
+
});
|
|
3499
|
+
}
|
|
3500
|
+
let declared = [];
|
|
3501
|
+
try {
|
|
3502
|
+
declared = await this.listDeclared();
|
|
3503
|
+
} catch {
|
|
3504
|
+
return gaps;
|
|
3505
|
+
}
|
|
3506
|
+
const unlocked = declared.filter((d) => d.state === "declared").map((d) => d.ref);
|
|
3507
|
+
const hints = await this.publisherChangeHints(unlocked);
|
|
3508
|
+
const lockKeys = Object.keys(this.tryReadLock()?.assets ?? {});
|
|
3509
|
+
for (const d of declared) {
|
|
3510
|
+
if (d.kind === "preset" && issues.some((i) => i.assetKind === "preset" && i.assetName === d.name)) {
|
|
3511
|
+
continue;
|
|
3512
|
+
}
|
|
3513
|
+
let gap;
|
|
3514
|
+
if (d.state === "invalid") {
|
|
3515
|
+
gap = {
|
|
3516
|
+
type: "invalid",
|
|
3517
|
+
reason: "is not a valid asset ref (expected kind:@<publisher>/<name>#<version>)"
|
|
3518
|
+
};
|
|
3519
|
+
} else if (d.state === "drift") {
|
|
3520
|
+
gap = {
|
|
3521
|
+
type: "drift",
|
|
3522
|
+
reason: "is declared and locked but not materialized \u2014 run `skaile install`"
|
|
3523
|
+
};
|
|
3524
|
+
} else if (d.state === "declared") {
|
|
3525
|
+
const hint = hints[d.ref];
|
|
3526
|
+
gap = hint ? { type: "declared", reason: `is declared but does not resolve${hint}`, manualFix: true } : {
|
|
3527
|
+
type: "declared",
|
|
3528
|
+
reason: "is declared but does not resolve \u2014 run `skaile install`"
|
|
3529
|
+
};
|
|
3530
|
+
} else {
|
|
3531
|
+
const want = parseAssetRef(d.ref);
|
|
3532
|
+
if (!want.pin || want.kind === "preset") continue;
|
|
3533
|
+
const held = lockKeys.map((key) => ({ key, id: parseCanonicalLockKey(key) })).filter(
|
|
3534
|
+
({ id }) => id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
|
|
3535
|
+
);
|
|
3536
|
+
if (held.length > 0 && !held.some(({ id }) => !id.version || matchPin(want.pin, id.version))) {
|
|
3537
|
+
gap = {
|
|
3538
|
+
type: "pin",
|
|
3539
|
+
reason: `is pinned in skaile.yaml but the lock holds ${held.map((h) => h.key).join(", ")} \u2014 run \`skaile install\``
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
if (gap) gaps.push({ ...gap, ref: d.ref, message: `${d.ref} ${gap.reason}` });
|
|
3544
|
+
}
|
|
3545
|
+
return gaps;
|
|
3546
|
+
}
|
|
3547
|
+
/**
|
|
3548
|
+
* Items of each declared `preset:` dependency that the lock does not cover.
|
|
3549
|
+
*
|
|
3550
|
+
* A preset is never locked itself; the lock lists only its expanded items, so an
|
|
3551
|
+
* item missing from the lock never resolved (or was declared after the last
|
|
3552
|
+
* install). Presets are read from the local sources and libraries (nested presets
|
|
3553
|
+
* followed); one only a store holds cannot be read without the network and
|
|
3554
|
+
* reports no gaps. Without a lock nothing is reported: `list` and `check` already
|
|
3555
|
+
* show a never-installed declaration as `declared`.
|
|
3556
|
+
*
|
|
3557
|
+
* @param declared - The `dependencies:` refs as written in `skaile.yaml`.
|
|
3558
|
+
* @param lock - The current lock, or `null` when none exists.
|
|
3559
|
+
* @returns Declared preset ref → its uncovered items; `resolvable` is `true` when a
|
|
3560
|
+
* local source or library offers the item (so `skaile install` can fix it).
|
|
3561
|
+
* Presets without gaps are absent.
|
|
3562
|
+
*/
|
|
3563
|
+
async declaredPresetGaps(declared, lock) {
|
|
3564
|
+
const gaps = /* @__PURE__ */ new Map();
|
|
3565
|
+
if (!lock) return gaps;
|
|
3566
|
+
const presetRefs = declared.filter((d) => parseRefTolerant(d)?.kind === "preset");
|
|
3567
|
+
if (presetRefs.length === 0) return gaps;
|
|
3568
|
+
const { repositories } = this.loadConfig();
|
|
3569
|
+
const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
|
|
3570
|
+
projectDir: this.projectDir,
|
|
3571
|
+
knownKinds: this.knownKinds
|
|
3572
|
+
});
|
|
3573
|
+
const lockKeys = Object.keys(lock.assets).map(parseCanonicalLockKey);
|
|
3574
|
+
const sourceCandidate = (ref) => {
|
|
3575
|
+
const p = parseRefTolerant(ref);
|
|
3576
|
+
if (!p?.publisher) return void 0;
|
|
3577
|
+
const hash = ref.indexOf("#");
|
|
3578
|
+
const pin = p.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
|
|
3579
|
+
return (provenanceIndex.get(`${p.publisher}/${p.kind}:${p.name}`) ?? []).filter((c) => matchPin(pin, c.version)).sort((a, b) => {
|
|
3580
|
+
const va = coerceVersion(a.version);
|
|
3581
|
+
const vb = coerceVersion(b.version);
|
|
3582
|
+
return gt(va, vb) ? -1 : gt(vb, va) ? 1 : 0;
|
|
3583
|
+
})[0];
|
|
3584
|
+
};
|
|
3585
|
+
const itemsOf = async (ref) => {
|
|
3586
|
+
const src = sourceCandidate(ref);
|
|
3587
|
+
if (src) return (src.deps ?? []).map((d) => qualifyDepRef(d, src.publisher));
|
|
3588
|
+
const entry = (await this.resolveLibraryEntries([ref], { quiet: true })).entries[0];
|
|
3589
|
+
return entry ? libraryEntryDepRefs(entry) : null;
|
|
3590
|
+
};
|
|
3591
|
+
const resolvableLocally = async (ref) => sourceCandidate(ref) !== void 0 || (await this.resolveLibraryEntries([ref], { quiet: true })).resolved.has(ref);
|
|
3592
|
+
const isLocked = (item) => lockKeys.some(
|
|
3593
|
+
(k) => k.kind === item.kind && k.name === item.name && (!item.publisher || k.publisher === item.publisher)
|
|
3594
|
+
);
|
|
3595
|
+
for (const presetRef of presetRefs) {
|
|
3596
|
+
const out = [];
|
|
3597
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3598
|
+
const walk = async (ref) => {
|
|
3599
|
+
if (visited.has(ref)) return;
|
|
3600
|
+
visited.add(ref);
|
|
3601
|
+
for (const item of await itemsOf(ref) ?? []) {
|
|
3602
|
+
const p = parseRefTolerant(item);
|
|
3603
|
+
if (!p) continue;
|
|
3604
|
+
if (p.kind === "preset") await walk(item);
|
|
3605
|
+
else if (!isLocked(p)) out.push({ ref: item, resolvable: await resolvableLocally(item) });
|
|
3606
|
+
}
|
|
3607
|
+
};
|
|
3608
|
+
await walk(presetRef);
|
|
3609
|
+
if (out.length > 0) gaps.set(presetRef, out);
|
|
3610
|
+
}
|
|
3611
|
+
return gaps;
|
|
3612
|
+
}
|
|
2479
3613
|
// ── Scaffold ───────────────────────────────────────────────────────────
|
|
2480
3614
|
/**
|
|
2481
3615
|
* Create a minimal asset scaffold directory (delegates to {@link createScaffold}).
|
|
@@ -2485,7 +3619,7 @@ var AssetManager = class {
|
|
|
2485
3619
|
* @param destDir - Parent directory to create the asset in.
|
|
2486
3620
|
* @param opts - Optional scaffold content (`description`).
|
|
2487
3621
|
* @returns `{ ok: true, path }` on success, `{ ok: false, path: errorMessage }` if the
|
|
2488
|
-
* directory already exists.
|
|
3622
|
+
* directory already exists or `kind` has no scaffold template.
|
|
2489
3623
|
*/
|
|
2490
3624
|
create(name, kind, destDir, opts) {
|
|
2491
3625
|
return createScaffold(name, kind, destDir, opts);
|
|
@@ -2544,15 +3678,21 @@ var AssetManager = class {
|
|
|
2544
3678
|
const { kind, name } = parseCanonicalLockKey(ref);
|
|
2545
3679
|
const component = kind ? this.installTarget(kind) : void 0;
|
|
2546
3680
|
if (!kind || !name || !component) continue;
|
|
2547
|
-
const dest
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
3681
|
+
for (const dest of deployedPaths(
|
|
3682
|
+
component,
|
|
3683
|
+
name,
|
|
3684
|
+
this.driverTarget,
|
|
3685
|
+
this.global,
|
|
3686
|
+
this.projectDir
|
|
3687
|
+
)) {
|
|
3688
|
+
try {
|
|
3689
|
+
const stat = lstatSync(dest);
|
|
3690
|
+
if (stat.isSymbolicLink()) unlinkSync(dest);
|
|
3691
|
+
else rmSync(dest, { recursive: true, force: true });
|
|
3692
|
+
} catch {
|
|
3693
|
+
}
|
|
2555
3694
|
}
|
|
3695
|
+
removed.push(ref);
|
|
2556
3696
|
}
|
|
2557
3697
|
return removed;
|
|
2558
3698
|
}
|
|
@@ -2613,15 +3753,44 @@ var AssetManager = class {
|
|
|
2613
3753
|
return;
|
|
2614
3754
|
}
|
|
2615
3755
|
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
2616
|
-
if (!raw.dependencies) raw.dependencies = [];
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
3756
|
+
if (!Array.isArray(raw.dependencies)) raw.dependencies = [];
|
|
3757
|
+
const deps = raw.dependencies;
|
|
3758
|
+
if (deps.includes(ref) && deps.filter((d) => this.sameDeclaredAsset(d, ref)).length === 1) {
|
|
3759
|
+
return;
|
|
3760
|
+
}
|
|
3761
|
+
const next = [];
|
|
3762
|
+
let placed = false;
|
|
3763
|
+
for (const dep of deps) {
|
|
3764
|
+
if (!this.sameDeclaredAsset(dep, ref)) {
|
|
3765
|
+
next.push(dep);
|
|
3766
|
+
} else if (!placed) {
|
|
3767
|
+
next.push(ref);
|
|
3768
|
+
placed = true;
|
|
3769
|
+
}
|
|
2620
3770
|
}
|
|
3771
|
+
if (!placed) next.push(ref);
|
|
3772
|
+
raw.dependencies = next;
|
|
3773
|
+
writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
3774
|
+
}
|
|
3775
|
+
/**
|
|
3776
|
+
* Whether the declared `dep` names the same asset (kind, publisher, name) as
|
|
3777
|
+
* `ref`, ignoring the version pin. A publisher must agree on both sides —
|
|
3778
|
+
* unlike {@link removeDependencyFromConfig}, a bare `kind:name` never replaces a
|
|
3779
|
+
* publisher-qualified line. Unparseable entries only match on string equality.
|
|
3780
|
+
*/
|
|
3781
|
+
sameDeclaredAsset(dep, ref) {
|
|
3782
|
+
if (typeof dep !== "string") return false;
|
|
3783
|
+
if (dep === ref) return true;
|
|
3784
|
+
const a = parseAssetRefLoose(dep);
|
|
3785
|
+
const b = parseAssetRefLoose(ref);
|
|
3786
|
+
if (!a || !b) return false;
|
|
3787
|
+
return a.kind === b.kind && a.name === b.name && a.publisher?.replace(/^@/, "") === b.publisher?.replace(/^@/, "");
|
|
2621
3788
|
}
|
|
2622
3789
|
/**
|
|
2623
3790
|
* Remove a dependency ref from the manifest `dependencies:`. Targets the same
|
|
2624
3791
|
* file as {@link addDependencyToConfig} (`~/.skaile/skaile.yaml` in global mode).
|
|
3792
|
+
*
|
|
3793
|
+
* @returns `true` when a declaration was removed.
|
|
2625
3794
|
*/
|
|
2626
3795
|
removeDependencyFromConfig(target) {
|
|
2627
3796
|
const configPath = join(this.stateRoot, "skaile.yaml");
|
|
@@ -2629,15 +3798,15 @@ var AssetManager = class {
|
|
|
2629
3798
|
const raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
2630
3799
|
if (!Array.isArray(raw.dependencies)) return false;
|
|
2631
3800
|
const wantPub = target.publisher?.replace(/^@/, "");
|
|
2632
|
-
const
|
|
2633
|
-
|
|
3801
|
+
const before = raw.dependencies.length;
|
|
3802
|
+
raw.dependencies = raw.dependencies.filter((dep) => {
|
|
3803
|
+
if (typeof dep !== "string") return true;
|
|
2634
3804
|
const p = parseAssetRefLoose(dep);
|
|
2635
|
-
if (!p || p.kind !== target.kind || p.name !== target.name) return
|
|
3805
|
+
if (!p || p.kind !== target.kind || p.name !== target.name) return true;
|
|
2636
3806
|
const havePub = p.publisher?.replace(/^@/, "");
|
|
2637
|
-
return wantPub === void 0 || havePub === void 0 || havePub === wantPub;
|
|
3807
|
+
return !(wantPub === void 0 || havePub === void 0 || havePub === wantPub);
|
|
2638
3808
|
});
|
|
2639
|
-
if (
|
|
2640
|
-
raw.dependencies.splice(idx, 1);
|
|
3809
|
+
if (raw.dependencies.length === before) return false;
|
|
2641
3810
|
writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
|
|
2642
3811
|
return true;
|
|
2643
3812
|
}
|
|
@@ -2659,5 +3828,5 @@ var AssetManager = class {
|
|
|
2659
3828
|
};
|
|
2660
3829
|
|
|
2661
3830
|
export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, installAgentInstances, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName };
|
|
2662
|
-
//# sourceMappingURL=chunk-
|
|
2663
|
-
//# sourceMappingURL=chunk-
|
|
3831
|
+
//# sourceMappingURL=chunk-H7QUDDKH.js.map
|
|
3832
|
+
//# sourceMappingURL=chunk-H7QUDDKH.js.map
|