@skaile/workspaces 1.3.0 → 1.4.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 +122 -0
- package/README.md +1 -1
- package/dist/app-sdk/index.d.ts +2 -0
- package/dist/app-sdk/index.js +295 -0
- package/dist/app-sdk/index.js.map +1 -0
- package/dist/app-sdk/src/app.d.ts +101 -0
- package/dist/app-sdk/src/app.d.ts.map +1 -0
- package/dist/app-sdk/src/define-app-capability.d.ts +139 -0
- package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -0
- package/dist/app-sdk/src/index.d.ts +16 -0
- package/dist/app-sdk/src/index.d.ts.map +1 -0
- package/dist/app-sdk/src/logger.d.ts +26 -0
- package/dist/app-sdk/src/logger.d.ts.map +1 -0
- package/dist/app-sdk/src/transport.d.ts +84 -0
- package/dist/app-sdk/src/transport.d.ts.map +1 -0
- package/dist/app-sdk/src/ws-transport.d.ts +63 -0
- package/dist/app-sdk/src/ws-transport.d.ts.map +1 -0
- package/dist/{asset-feeds-NEZKG6YS.js → asset-feeds-NG5TKB3V.js} +8 -8
- package/dist/{asset-feeds-NEZKG6YS.js.map → asset-feeds-NG5TKB3V.js.map} +1 -1
- package/dist/asset-manager/index.js +7 -7
- package/dist/asset-manager/installer.js +6 -6
- package/dist/asset-manager/renderers.js +2 -2
- package/dist/asset-manager/src/index.d.ts +42 -6
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/bridge/drivers/claude-sdk.js +2 -2
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +3 -3
- package/dist/{chunk-AZAS3FKN.js → chunk-26YCXWCU.js} +48 -2
- package/dist/chunk-26YCXWCU.js.map +1 -0
- package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
- package/dist/chunk-4N5XR5DQ.js.map +1 -0
- package/dist/{chunk-RHMQPLIK.js → chunk-77OXU44H.js} +6 -6
- package/dist/{chunk-RHMQPLIK.js.map → chunk-77OXU44H.js.map} +1 -1
- package/dist/{chunk-7CNIRF42.js → chunk-AAGD2J4H.js} +20 -11
- package/dist/chunk-AAGD2J4H.js.map +1 -0
- package/dist/{chunk-BZMTR5WW.js → chunk-B6SSXVF2.js} +4 -4
- package/dist/chunk-B6SSXVF2.js.map +1 -0
- package/dist/{chunk-YOPSHV7V.js → chunk-BE5KTS2L.js} +3 -3
- package/dist/{chunk-YOPSHV7V.js.map → chunk-BE5KTS2L.js.map} +1 -1
- package/dist/{chunk-JQMIG7D3.js → chunk-BJHZHNKR.js} +3 -3
- package/dist/{chunk-JQMIG7D3.js.map → chunk-BJHZHNKR.js.map} +1 -1
- package/dist/{chunk-5XS3BFJS.js → chunk-DNFIKRO6.js} +3 -3
- package/dist/{chunk-5XS3BFJS.js.map → chunk-DNFIKRO6.js.map} +1 -1
- package/dist/{chunk-PSMW5HLX.js → chunk-GWDZW44L.js} +4 -4
- package/dist/{chunk-PSMW5HLX.js.map → chunk-GWDZW44L.js.map} +1 -1
- package/dist/{chunk-K5SD7FSF.js → chunk-HKX5VSWQ.js} +3 -3
- package/dist/{chunk-K5SD7FSF.js.map → chunk-HKX5VSWQ.js.map} +1 -1
- package/dist/{chunk-P3MJYPZE.js → chunk-HWTQ6HPO.js} +4 -4
- package/dist/{chunk-P3MJYPZE.js.map → chunk-HWTQ6HPO.js.map} +1 -1
- package/dist/{chunk-XKARS6EE.js → chunk-IEX5YV4R.js} +3 -3
- package/dist/{chunk-XKARS6EE.js.map → chunk-IEX5YV4R.js.map} +1 -1
- package/dist/{chunk-GB4SEEK4.js → chunk-IRORTHGA.js} +10 -4
- package/dist/chunk-IRORTHGA.js.map +1 -0
- package/dist/{chunk-YXEPZ7LP.js → chunk-JZPEEMIM.js} +5 -5
- package/dist/{chunk-YXEPZ7LP.js.map → chunk-JZPEEMIM.js.map} +1 -1
- package/dist/{chunk-WFEPI5LA.js → chunk-LCTRCWBK.js} +5 -5
- package/dist/{chunk-WFEPI5LA.js.map → chunk-LCTRCWBK.js.map} +1 -1
- package/dist/{chunk-QK5JDAFF.js → chunk-NKTFEV7Q.js} +4 -4
- package/dist/{chunk-QK5JDAFF.js.map → chunk-NKTFEV7Q.js.map} +1 -1
- package/dist/{chunk-ETYDLXI5.js → chunk-OSCJRUKM.js} +2 -2
- package/dist/{chunk-ETYDLXI5.js.map → chunk-OSCJRUKM.js.map} +1 -1
- package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
- package/dist/chunk-PK7Q7MC7.js.map +1 -0
- package/dist/{chunk-RYRJEVQ4.js → chunk-QM2TT2PJ.js} +19 -11
- package/dist/chunk-QM2TT2PJ.js.map +1 -0
- package/dist/{chunk-SUOGVHGJ.js → chunk-TNSCHS4I.js} +2 -2
- package/dist/{chunk-SUOGVHGJ.js.map → chunk-TNSCHS4I.js.map} +1 -1
- package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
- package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
- package/dist/{chunk-RJ3HUSBC.js → chunk-UQ5FK2AB.js} +3 -3
- package/dist/{chunk-RJ3HUSBC.js.map → chunk-UQ5FK2AB.js.map} +1 -1
- package/dist/{chunk-OBSSORCE.js → chunk-X57AMTAH.js} +72 -9
- package/dist/chunk-X57AMTAH.js.map +1 -0
- package/dist/cli/index.js +766 -185
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/preset-apply.d.ts +59 -0
- package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
- package/dist/cli/src/commands/preset-cmd.d.ts +2 -2
- package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/preset-export.d.ts +93 -0
- package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/why.d.ts +18 -0
- package/dist/cli/src/commands/why.d.ts.map +1 -1
- package/dist/connectors/config.js +2 -2
- package/dist/connectors/index.js +4 -4
- package/dist/core/index.js +2 -2
- package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
- package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
- package/dist/deploy/index.js +2 -2
- package/dist/discovery/index.js +2 -2
- package/dist/{ensure-sources-4BAJGJRK.js → ensure-sources-CHC7X7AJ.js} +10 -10
- package/dist/{ensure-sources-4BAJGJRK.js.map → ensure-sources-CHC7X7AJ.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +4 -4
- package/dist/factory-assets/connectors/deploy.js +4 -4
- package/dist/factory-assets/connectors/devserver.js +4 -4
- package/dist/factory-assets/connectors/flow/adapter.js +4 -4
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +3 -3
- package/dist/factory-assets/connectors/flow/run-flow.js +5 -5
- package/dist/factory-assets/connectors/flow.js +4 -4
- package/dist/factory-assets/connectors/git.js +4 -4
- package/dist/factory-assets/connectors/gmail.js +4 -4
- package/dist/factory-assets/connectors/googledrive.js +4 -4
- package/dist/factory-assets/connectors/local.js +4 -4
- package/dist/factory-assets/connectors/mattermost.js +4 -4
- package/dist/factory-assets/connectors/memory.js +4 -4
- package/dist/factory-assets/connectors/minio.js +4 -4
- package/dist/factory-assets/connectors/postgres.js +4 -4
- package/dist/factory-assets/connectors/s3.js +4 -4
- package/dist/factory-assets/connectors/sharepoint.js +4 -4
- package/dist/factory-assets/connectors/sqlite.js +4 -4
- package/dist/factory-assets/connectors/static-server.js +4 -4
- package/dist/factory-assets/connectors/tunnel.js +4 -4
- package/dist/factory-assets/connectors/webdav.js +4 -4
- package/dist/factory-assets/connectors/xstate-store.js +4 -4
- package/dist/factory-assets/connectors/xstate.js +4 -4
- package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
- package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
- package/dist/{flows-UFCOGAAM.js → flows-CGOCUNO6.js} +4 -4
- package/dist/{flows-UFCOGAAM.js.map → flows-CGOCUNO6.js.map} +1 -1
- package/dist/{helpers-2OL4FRFB.js → helpers-GWG4CYYC.js} +10 -10
- package/dist/{helpers-2OL4FRFB.js.map → helpers-GWG4CYYC.js.map} +1 -1
- package/dist/library/index.js +6 -6
- package/dist/library/install.js +2 -2
- package/dist/{open-library-RPV2LIKL.js → open-library-37PXN6MK.js} +15 -15
- package/dist/{open-library-RPV2LIKL.js.map → open-library-37PXN6MK.js.map} +1 -1
- package/dist/{paths-ML33B5SB.js → paths-UVOHWUPY.js} +4 -4
- package/dist/{paths-ML33B5SB.js.map → paths-UVOHWUPY.js.map} +1 -1
- package/dist/{plugin-store-G4HKWCTU.js → plugin-store-EIMVEH3O.js} +4 -4
- package/dist/{plugin-store-G4HKWCTU.js.map → plugin-store-EIMVEH3O.js.map} +1 -1
- package/dist/runner/index.js +10 -10
- package/dist/runner/src/capability-registry.d.ts +15 -4
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +7 -7
- package/dist/sdk/bridge.js +3 -3
- package/dist/sdk/core.js +2 -2
- package/dist/sdk/flow.js +3 -3
- package/dist/sdk/index.js +10 -10
- package/dist/sdk/runner.js +10 -10
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-WS6PF6JQ.js → setup-DINVR6ZQ.js} +7 -7
- package/dist/{setup-WS6PF6JQ.js.map → setup-DINVR6ZQ.js.map} +1 -1
- package/dist/{source-22U5NOY4.js → source-RXAHIGMD.js} +16 -16
- package/dist/{source-22U5NOY4.js.map → source-RXAHIGMD.js.map} +1 -1
- package/dist/{store-client-YLMBH5MF.js → store-client-T3F663YG.js} +5 -5
- package/dist/{store-client-YLMBH5MF.js.map → store-client-T3F663YG.js.map} +1 -1
- package/dist/tui/index.js +10 -10
- package/dist/types/index.js +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/capabilities.d.ts +8 -2
- package/dist/types/src/capabilities.d.ts.map +1 -1
- package/dist/types/src/logging.d.ts +3 -2
- package/dist/types/src/logging.d.ts.map +1 -1
- package/dist/types/src/manifests/_shared.d.ts +4 -4
- package/dist/types/src/manifests/index.d.ts +2 -2
- package/dist/types/src/manifests/index.d.ts.map +1 -1
- package/dist/types/src/manifests/preset.d.ts +10 -0
- package/dist/types/src/manifests/preset.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +11 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +2 -2
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +7 -1
- package/dist/chunk-7CNIRF42.js.map +0 -1
- package/dist/chunk-AZAS3FKN.js.map +0 -1
- package/dist/chunk-BZMTR5WW.js.map +0 -1
- package/dist/chunk-EVZKWY5J.js.map +0 -1
- package/dist/chunk-GB4SEEK4.js.map +0 -1
- package/dist/chunk-OBSSORCE.js.map +0 -1
- package/dist/chunk-RYRJEVQ4.js.map +0 -1
- package/dist/chunk-SKADPHAM.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { makeSourceCommand, syncProjectSources } from '../chunk-RYRJEVQ4.js';
|
|
3
|
-
import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-RHMQPLIK.js';
|
|
4
|
-
import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-5XS3BFJS.js';
|
|
5
|
-
import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-JQMIG7D3.js';
|
|
6
2
|
import { AI_RESOURCES, aiResourceRoots } from '../chunk-XBG2YDQQ.js';
|
|
3
|
+
import { makeSourceCommand, syncProjectSources } from '../chunk-QM2TT2PJ.js';
|
|
4
|
+
import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-77OXU44H.js';
|
|
5
|
+
import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-DNFIKRO6.js';
|
|
6
|
+
import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-BJHZHNKR.js';
|
|
7
7
|
import { LocalSecretsProvider } from '../chunk-6BEGKO2Q.js';
|
|
8
|
-
import { resolveLibraryDir } from '../chunk-
|
|
9
|
-
import '../chunk-
|
|
8
|
+
import { resolveLibraryDir, getDefaultValue, isSecretPlaceholder } from '../chunk-HWTQ6HPO.js';
|
|
9
|
+
import '../chunk-B6SSXVF2.js';
|
|
10
|
+
import '../chunk-JXT46QFC.js';
|
|
10
11
|
import '../chunk-UKJMQZV6.js';
|
|
11
12
|
import '../chunk-R7FOF242.js';
|
|
12
|
-
import '../chunk-
|
|
13
|
-
import { runFlow, resumeFlow } from '../chunk-YXEPZ7LP.js';
|
|
13
|
+
import { runFlow, resumeFlow } from '../chunk-JZPEEMIM.js';
|
|
14
14
|
import '../chunk-4R6DROGN.js';
|
|
15
15
|
import { validateFlowVersions, parseSkillFrontmatter } from '../chunk-GXC3TYMQ.js';
|
|
16
|
-
import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-
|
|
17
|
-
import { buildClaudePluginFiles } from '../chunk-
|
|
16
|
+
import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-AAGD2J4H.js';
|
|
17
|
+
import { buildClaudePluginFiles } from '../chunk-OSCJRUKM.js';
|
|
18
18
|
import '../chunk-X5YPJV4N.js';
|
|
19
19
|
import '../chunk-O7SG5PC2.js';
|
|
20
20
|
import '../chunk-7QBNJTTQ.js';
|
|
@@ -22,38 +22,38 @@ import '../chunk-GTS2FODO.js';
|
|
|
22
22
|
import '../chunk-WQ7DE5UC.js';
|
|
23
23
|
import '../chunk-32NA4TVC.js';
|
|
24
24
|
import '../chunk-W3UDISS2.js';
|
|
25
|
-
import '../chunk-
|
|
26
|
-
import '../chunk-K5SD7FSF.js';
|
|
25
|
+
import '../chunk-HKX5VSWQ.js';
|
|
27
26
|
import '../chunk-ISQWMBKD.js';
|
|
27
|
+
import '../chunk-RRVQAE5D.js';
|
|
28
28
|
import '../chunk-BMQKMOQY.js';
|
|
29
29
|
import '../chunk-GAH4YRCI.js';
|
|
30
|
-
import '../chunk-
|
|
31
|
-
import '../chunk-SUOGVHGJ.js';
|
|
30
|
+
import '../chunk-TNSCHS4I.js';
|
|
32
31
|
import '../chunk-LV2HPH3C.js';
|
|
33
|
-
import '../chunk-
|
|
32
|
+
import '../chunk-GWDZW44L.js';
|
|
34
33
|
import '../chunk-BDUANIAR.js';
|
|
35
34
|
import '../chunk-6E6PKKAD.js';
|
|
36
|
-
import { loadFlow, loadAllFlows } from '../chunk-
|
|
35
|
+
import { loadFlow, loadAllFlows } from '../chunk-4N5XR5DQ.js';
|
|
37
36
|
import '../chunk-DEYYRFQU.js';
|
|
38
37
|
import '../chunk-IFGTRL2I.js';
|
|
39
|
-
import
|
|
40
|
-
import '../chunk-
|
|
41
|
-
import
|
|
38
|
+
import '../chunk-PK7Q7MC7.js';
|
|
39
|
+
import { AssetManager, NON_DEPLOYABLE_KINDS, MissingManifestError, normalizeRepoUrl, FACTORY_SOURCE_URL } from '../chunk-X57AMTAH.js';
|
|
40
|
+
import '../chunk-LCTRCWBK.js';
|
|
41
|
+
import '../chunk-CGYEHQOX.js';
|
|
42
|
+
import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor, checkAgentSkillConformance } from '../chunk-26YCXWCU.js';
|
|
43
|
+
import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
|
|
42
44
|
import '../chunk-TJRWGMD2.js';
|
|
43
|
-
import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
|
|
45
|
+
import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, loadSkWorkspaceConfig, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
|
|
44
46
|
import { SKAILE_MANIFEST_FILENAME, decodeSkaileManifest, loadSkPublishManifest, saveSkPublishManifest } from '../chunk-DZCK7GRW.js';
|
|
45
47
|
import { parseFrontmatter } from '../chunk-BQCGFPHW.js';
|
|
46
48
|
import { parseAssetRef, ASSET_KINDS, isValidAssetName, slugifyAssetName, ASSET_NAME_HINT, assetRefToStr } from '../chunk-GEEW6SY2.js';
|
|
47
49
|
import '../chunk-F3HNW354.js';
|
|
48
50
|
import { SUPPORTED_DRIVER_TARGETS } from '../chunk-MENCWIHW.js';
|
|
49
51
|
import { DRIVER_DEFAULTS } from '../chunk-K5GBV4SA.js';
|
|
50
|
-
import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
|
|
51
52
|
import { openSqlite } from '../chunk-JB35MVBN.js';
|
|
52
53
|
import '../chunk-KTBKW2FI.js';
|
|
53
54
|
import '../chunk-UQ6LFBPZ.js';
|
|
54
|
-
import '../chunk-
|
|
55
|
-
import '../chunk-
|
|
56
|
-
import '../chunk-CGYEHQOX.js';
|
|
55
|
+
import '../chunk-IEX5YV4R.js';
|
|
56
|
+
import { validatePreset } from '../chunk-IRORTHGA.js';
|
|
57
57
|
import '../chunk-NSBPE2FW.js';
|
|
58
58
|
import * as fs12 from 'fs';
|
|
59
59
|
import fs12__default, { readFileSync, existsSync, mkdirSync, writeFileSync, lstatSync, rmSync, readdirSync, statSync } from 'fs';
|
|
@@ -71,6 +71,7 @@ import { createInterface } from 'readline';
|
|
|
71
71
|
import fs6 from 'fs/promises';
|
|
72
72
|
import { parse as parse$1, stringify as stringify$1 } from 'smol-toml';
|
|
73
73
|
import { execSync } from 'child_process';
|
|
74
|
+
import * as readline2 from 'readline/promises';
|
|
74
75
|
import { createTRPCUntypedClient, httpLink, httpSubscriptionLink } from '@trpc/client';
|
|
75
76
|
|
|
76
77
|
async function runSearch(kind, query, opts) {
|
|
@@ -93,7 +94,7 @@ async function searchLocal(query, kind, opts, showStore, airGapped) {
|
|
|
93
94
|
const projectDir = path14__default.resolve(opts.projectDir);
|
|
94
95
|
let entries = new AssetManager({ projectDir }).search(query, kind);
|
|
95
96
|
if (entries.length === 0 && !query && !kind && !airGapped) {
|
|
96
|
-
const { autoSyncForSearch } = await import('../source-
|
|
97
|
+
const { autoSyncForSearch } = await import('../source-RXAHIGMD.js');
|
|
97
98
|
if (await autoSyncForSearch(projectDir)) {
|
|
98
99
|
entries = new AssetManager({ projectDir }).search(query, kind);
|
|
99
100
|
}
|
|
@@ -146,7 +147,7 @@ async function searchStore(query, kind, opts, airGapped) {
|
|
|
146
147
|
return;
|
|
147
148
|
}
|
|
148
149
|
try {
|
|
149
|
-
const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-
|
|
150
|
+
const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-T3F663YG.js');
|
|
150
151
|
const config = getStoreConfig2();
|
|
151
152
|
const params = {};
|
|
152
153
|
if (query) params.q = query;
|
|
@@ -1006,7 +1007,7 @@ function makeCatalogCommand() {
|
|
|
1006
1007
|
return catalog;
|
|
1007
1008
|
}
|
|
1008
1009
|
async function runLocalCatalogTest(projectDir) {
|
|
1009
|
-
const { resolveCatalogSource } = await import('../open-library-
|
|
1010
|
+
const { resolveCatalogSource } = await import('../open-library-37PXN6MK.js');
|
|
1010
1011
|
let resolved;
|
|
1011
1012
|
try {
|
|
1012
1013
|
resolved = await resolveCatalogSource({ projectDir });
|
|
@@ -1996,7 +1997,7 @@ async function ensureTargetsRegistered(projectDir) {
|
|
|
1996
1997
|
const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
|
|
1997
1998
|
const config = resolveSkWorkspaceConfig2(projectDir);
|
|
1998
1999
|
if (config.plugins && config.plugins.length > 0) {
|
|
1999
|
-
const { ensurePluginsLoaded } = await import('../plugin-store-
|
|
2000
|
+
const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
|
|
2000
2001
|
const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
|
|
2001
2002
|
for (const f of result.failed) logWarn(f.error);
|
|
2002
2003
|
}
|
|
@@ -3712,7 +3713,7 @@ async function previewSoftDepsUnderCursor() {
|
|
|
3712
3713
|
}
|
|
3713
3714
|
async function loadAssets() {
|
|
3714
3715
|
softDepPreviewCache.clear();
|
|
3715
|
-
const { gatherAssetFeeds } = await import('../asset-feeds-
|
|
3716
|
+
const { gatherAssetFeeds } = await import('../asset-feeds-NG5TKB3V.js');
|
|
3716
3717
|
const { entries, domains, notes } = await gatherAssetFeeds(am, am.projectDir);
|
|
3717
3718
|
if (notes.length > 0) {
|
|
3718
3719
|
state.message = notes.map((n) => `[${n.feed}] ${n.message}`).join(" \u2022 ");
|
|
@@ -3879,7 +3880,7 @@ function reconcileCollapsedSets(rows) {
|
|
|
3879
3880
|
async function loadSourcesAndLibraries() {
|
|
3880
3881
|
try {
|
|
3881
3882
|
const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir }, { getGlobalCacheDir: getGlobalCacheDir2 }] = await Promise.all([
|
|
3882
|
-
import('../open-library-
|
|
3883
|
+
import('../open-library-37PXN6MK.js'),
|
|
3883
3884
|
import('../library/index.js'),
|
|
3884
3885
|
import('../core/index.js')
|
|
3885
3886
|
]);
|
|
@@ -4965,7 +4966,7 @@ function action2(fn) {
|
|
|
4965
4966
|
};
|
|
4966
4967
|
}
|
|
4967
4968
|
async function reconcileAndReport(projectDir, plugins) {
|
|
4968
|
-
const { reconcilePlugins } = await import('../plugin-store-
|
|
4969
|
+
const { reconcilePlugins } = await import('../plugin-store-EIMVEH3O.js');
|
|
4969
4970
|
if (plugins.length === 0) {
|
|
4970
4971
|
logInfo("No plugins declared \u2014 nothing to reconcile.");
|
|
4971
4972
|
return;
|
|
@@ -4981,7 +4982,7 @@ async function reconcileAndReport(projectDir, plugins) {
|
|
|
4981
4982
|
async function runInstall2(spec, opts) {
|
|
4982
4983
|
const projectDir = path14__default.resolve(opts.projectDir);
|
|
4983
4984
|
const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
|
|
4984
|
-
const { specName } = await import('../plugin-store-
|
|
4985
|
+
const { specName } = await import('../plugin-store-EIMVEH3O.js');
|
|
4985
4986
|
const yamlPath = path14__default.join(projectDir, "skaile.yaml");
|
|
4986
4987
|
const editor = WorkspaceYamlEditor2.load(yamlPath);
|
|
4987
4988
|
const replaced = editor.addPlugin(spec, specName);
|
|
@@ -4993,7 +4994,7 @@ async function runInstall2(spec, opts) {
|
|
|
4993
4994
|
async function runRemove(name, opts) {
|
|
4994
4995
|
const projectDir = path14__default.resolve(opts.projectDir);
|
|
4995
4996
|
const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
|
|
4996
|
-
const { specName } = await import('../plugin-store-
|
|
4997
|
+
const { specName } = await import('../plugin-store-EIMVEH3O.js');
|
|
4997
4998
|
const yamlPath = path14__default.join(projectDir, "skaile.yaml");
|
|
4998
4999
|
const editor = WorkspaceYamlEditor2.load(yamlPath);
|
|
4999
5000
|
const removed = editor.removePlugin(name, specName);
|
|
@@ -5021,7 +5022,7 @@ async function runList(opts) {
|
|
|
5021
5022
|
const registry = createPluginRegistry();
|
|
5022
5023
|
if (declared.length > 0) {
|
|
5023
5024
|
try {
|
|
5024
|
-
const { loadPlugins } = await import('../plugin-store-
|
|
5025
|
+
const { loadPlugins } = await import('../plugin-store-EIMVEH3O.js');
|
|
5025
5026
|
const result = await loadPlugins(projectDir, declared, registry);
|
|
5026
5027
|
for (const f of result.failed) logWarn(f.error);
|
|
5027
5028
|
} catch (err) {
|
|
@@ -5055,150 +5056,93 @@ function makePluginCommand() {
|
|
|
5055
5056
|
cmd.action(() => cmd.help());
|
|
5056
5057
|
return cmd;
|
|
5057
5058
|
}
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
async function loadPresetForValidation(resolved) {
|
|
5082
|
-
if (!existsSync(resolved)) {
|
|
5083
|
-
logErr(`File not found: ${resolved}`);
|
|
5084
|
-
process.exit(1);
|
|
5085
|
-
}
|
|
5086
|
-
const content = readFileSync(resolved, "utf-8");
|
|
5087
|
-
const { parsePresetYaml } = await import('../library/index.js');
|
|
5088
|
-
const parseResult = parsePresetYaml(content);
|
|
5089
|
-
if (!parseResult.ok) {
|
|
5090
|
-
logErr("Preset validation failed (parse errors):");
|
|
5091
|
-
for (const err of parseResult.errors) {
|
|
5092
|
-
console.log(` ${S.err("\u2717")} ${err}`);
|
|
5093
|
-
}
|
|
5094
|
-
process.exit(1);
|
|
5095
|
-
}
|
|
5096
|
-
if (!parseResult.preset) {
|
|
5097
|
-
logErr("Preset validation failed: parser returned no manifest");
|
|
5098
|
-
process.exit(1);
|
|
5099
|
-
}
|
|
5100
|
-
return parseResult.preset;
|
|
5101
|
-
}
|
|
5102
|
-
async function reportNestedPresetRefs(preset) {
|
|
5103
|
-
const { detectNestedPresetRefs } = await import('../library/index.js');
|
|
5104
|
-
const nestedRefs = detectNestedPresetRefs(preset);
|
|
5105
|
-
if (nestedRefs.length > 0) {
|
|
5106
|
-
logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
|
|
5107
|
-
}
|
|
5108
|
-
}
|
|
5109
|
-
async function checkPresetCycles(preset, resolved) {
|
|
5110
|
-
const { detectCycles } = await import('../discovery/index.js');
|
|
5111
|
-
const presetRef = path14__default.basename(resolved, ".preset.yaml");
|
|
5112
|
-
const edges = [];
|
|
5059
|
+
|
|
5060
|
+
// cli/src/commands/preset-apply.ts
|
|
5061
|
+
var TOKEN_RE = /\$\{(INPUT|SECRET):([^}]+)\}/g;
|
|
5062
|
+
var NESTED_PRESET_UNSUPPORTED = "nested presets are not supported by `preset apply` yet";
|
|
5063
|
+
function resolvePlaceholderValue(placeholder, providedValue) {
|
|
5064
|
+
if (isSecretPlaceholder(placeholder)) {
|
|
5065
|
+
return { envRef: `env:${placeholder.key}` };
|
|
5066
|
+
}
|
|
5067
|
+
const fallback = getDefaultValue(placeholder);
|
|
5068
|
+
const resolved = providedValue ?? (fallback !== void 0 ? String(fallback) : void 0);
|
|
5069
|
+
if (resolved === void 0) {
|
|
5070
|
+
if (placeholder.required !== false) {
|
|
5071
|
+
throw new Error(`Placeholder "${placeholder.key}" is required but no value was provided`);
|
|
5072
|
+
}
|
|
5073
|
+
return { value: "" };
|
|
5074
|
+
}
|
|
5075
|
+
return { value: resolved };
|
|
5076
|
+
}
|
|
5077
|
+
function presetItemsToWorkspace(preset, values) {
|
|
5078
|
+
const dependencies = [];
|
|
5079
|
+
const connectors = [];
|
|
5080
|
+
const mcpServers = [];
|
|
5081
|
+
const envVarsToSet = /* @__PURE__ */ new Set();
|
|
5113
5082
|
for (const item of preset.items) {
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5083
|
+
const ref = item.ref;
|
|
5084
|
+
if (ref.startsWith("preset:")) throw new Error(NESTED_PRESET_UNSUPPORTED);
|
|
5085
|
+
if (item.id && ref.startsWith("connector:")) {
|
|
5086
|
+
const config = resolveItemConfig(item, preset, values, envVarsToSet);
|
|
5087
|
+
connectors.push({
|
|
5088
|
+
id: item.id,
|
|
5089
|
+
driver: ref.slice("connector:".length),
|
|
5090
|
+
...config
|
|
5091
|
+
});
|
|
5092
|
+
} else if (item.id && ref.startsWith("mcp:")) {
|
|
5093
|
+
const config = resolveItemConfig(item, preset, values, envVarsToSet);
|
|
5094
|
+
mcpServers.push({ id: item.id, ...config });
|
|
5095
|
+
} else {
|
|
5096
|
+
dependencies.push(ref);
|
|
5124
5097
|
}
|
|
5125
|
-
process.exit(1);
|
|
5126
5098
|
}
|
|
5099
|
+
const sources = (preset.sources ?? []).map(
|
|
5100
|
+
(s) => s.pin ? { url: s.url, pin: s.pin } : { url: s.url }
|
|
5101
|
+
);
|
|
5102
|
+
return { dependencies, connectors, mcpServers, sources, envVarsToSet: [...envVarsToSet] };
|
|
5127
5103
|
}
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
const
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5104
|
+
function resolveItemConfig(item, preset, values, envVarsToSet) {
|
|
5105
|
+
const byKey = placeholderIndex(item, preset);
|
|
5106
|
+
const resolve5 = (key) => {
|
|
5107
|
+
const ph = byKey.get(key) ?? { key, type: "text", required: true };
|
|
5108
|
+
return resolvePlaceholderValue(ph, values[key]);
|
|
5109
|
+
};
|
|
5110
|
+
return substituteNode(item.config ?? {}, resolve5, envVarsToSet);
|
|
5111
|
+
}
|
|
5112
|
+
function placeholderIndex(item, preset) {
|
|
5113
|
+
const map = /* @__PURE__ */ new Map();
|
|
5114
|
+
for (const input of preset.inputs ?? []) map.set(input.key, input);
|
|
5115
|
+
for (const ph of item.placeholders ?? []) map.set(ph.key, ph);
|
|
5116
|
+
return map;
|
|
5117
|
+
}
|
|
5118
|
+
function substituteNode(node, resolve5, envVarsToSet) {
|
|
5119
|
+
if (typeof node === "string") return substituteString(node, resolve5, envVarsToSet);
|
|
5120
|
+
if (Array.isArray(node)) return node.map((el) => substituteNode(el, resolve5, envVarsToSet));
|
|
5121
|
+
if (node && typeof node === "object") {
|
|
5122
|
+
const out = {};
|
|
5123
|
+
for (const [k, v] of Object.entries(node)) out[k] = substituteNode(v, resolve5, envVarsToSet);
|
|
5124
|
+
return out;
|
|
5125
|
+
}
|
|
5126
|
+
return node;
|
|
5127
|
+
}
|
|
5128
|
+
function substituteString(value, resolve5, envVarsToSet) {
|
|
5129
|
+
const tokens = [...value.matchAll(TOKEN_RE)];
|
|
5130
|
+
if (tokens.length === 0) return value;
|
|
5131
|
+
const secret = tokens.find(([, ns]) => ns === "SECRET") ?? tokens.find(([, , key]) => "envRef" in resolve5(key));
|
|
5132
|
+
if (secret) {
|
|
5133
|
+
const [full, , key] = secret;
|
|
5134
|
+
if (tokens.length > 1 || full !== value) {
|
|
5135
|
+
throw new Error(
|
|
5136
|
+
`Secret "${key}" must be the only token in its value \u2014 remove the surrounding tokens/text`
|
|
5137
|
+
);
|
|
5136
5138
|
}
|
|
5139
|
+
envVarsToSet.add(key);
|
|
5140
|
+
return `env:${key}`;
|
|
5137
5141
|
}
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
}
|
|
5143
|
-
return true;
|
|
5144
|
-
}
|
|
5145
|
-
function printPresetSummary(preset, placeholderCount, hasWarnings) {
|
|
5146
|
-
console.log();
|
|
5147
|
-
console.log(S.heading(" Preset Validation"));
|
|
5148
|
-
console.log(` ${S.rule(40)}`);
|
|
5149
|
-
console.log(` ${S.label("Name")} ${preset.name}`);
|
|
5150
|
-
console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
|
|
5151
|
-
console.log(` ${S.label("Items")} ${preset.items.length}`);
|
|
5152
|
-
console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
|
|
5153
|
-
console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
|
|
5154
|
-
console.log();
|
|
5155
|
-
if (hasWarnings) {
|
|
5156
|
-
logWarn("Validation passed with warnings");
|
|
5157
|
-
} else {
|
|
5158
|
-
logOk("Preset is valid");
|
|
5159
|
-
}
|
|
5160
|
-
}
|
|
5161
|
-
async function runPresetValidate(filePath) {
|
|
5162
|
-
const resolved = path14__default.resolve(filePath);
|
|
5163
|
-
const preset = await loadPresetForValidation(resolved);
|
|
5164
|
-
await reportNestedPresetRefs(preset);
|
|
5165
|
-
await checkPresetCycles(preset, resolved);
|
|
5166
|
-
const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
|
|
5167
|
-
const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
|
|
5168
|
-
printPresetSummary(preset, allPlaceholders.length, hasWarnings);
|
|
5169
|
-
}
|
|
5170
|
-
function generatePresetTemplate(name) {
|
|
5171
|
-
return `# ${name}.preset.yaml
|
|
5172
|
-
# Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
|
|
5173
|
-
# See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
|
|
5174
|
-
|
|
5175
|
-
name: "${name}"
|
|
5176
|
-
version: "1.0.0"
|
|
5177
|
-
description: "TODO: describe what this preset configures"
|
|
5178
|
-
|
|
5179
|
-
# Placeholders \u2014 values the user provides at apply-time.
|
|
5180
|
-
# Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
|
|
5181
|
-
placeholders:
|
|
5182
|
-
- key: EXAMPLE_KEY
|
|
5183
|
-
type: text
|
|
5184
|
-
label: "Example placeholder"
|
|
5185
|
-
required: true
|
|
5186
|
-
# default: "some-value"
|
|
5187
|
-
# hint: "Help text shown to the user"
|
|
5188
|
-
|
|
5189
|
-
# Items \u2014 each becomes a Library Instance when the preset is applied.
|
|
5190
|
-
# Ref types:
|
|
5191
|
-
# - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
|
|
5192
|
-
# - Library Instance ref (#uuid) \u2192 reuses existing Instance
|
|
5193
|
-
# - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
|
|
5194
|
-
items:
|
|
5195
|
-
- id: example-item
|
|
5196
|
-
ref: "skill:@skaile/example-skill@^1.0"
|
|
5197
|
-
config:
|
|
5198
|
-
# Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
|
|
5199
|
-
setting: "\${INPUT:EXAMPLE_KEY}"
|
|
5200
|
-
# credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
|
|
5201
|
-
`;
|
|
5142
|
+
return value.replace(TOKEN_RE, (_m, _ns, key) => {
|
|
5143
|
+
const resolved = resolve5(key);
|
|
5144
|
+
return "envRef" in resolved ? resolved.envRef : resolved.value;
|
|
5145
|
+
});
|
|
5202
5146
|
}
|
|
5203
5147
|
var REF_HINT = "kind:@<publisher>/<name>#<version> (e.g. skill:@skaile/use-exa#1.0.0)";
|
|
5204
5148
|
function isValidAssetRef(ref) {
|
|
@@ -5275,7 +5219,7 @@ async function runRefInstall(ref, opts) {
|
|
|
5275
5219
|
const spinner5 = p6.spinner();
|
|
5276
5220
|
spinner5.start(`Installing ${ref}`);
|
|
5277
5221
|
try {
|
|
5278
|
-
const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-
|
|
5222
|
+
const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-37PXN6MK.js');
|
|
5279
5223
|
const catalog = await openCatalogSource2({ projectDir: path14__default.resolve(opts.projectDir) });
|
|
5280
5224
|
if (!supportsInstallManifest(catalog)) {
|
|
5281
5225
|
throw new Error(
|
|
@@ -5312,7 +5256,7 @@ async function runYamlInstall(opts) {
|
|
|
5312
5256
|
return;
|
|
5313
5257
|
}
|
|
5314
5258
|
try {
|
|
5315
|
-
const { ensureSourcesCloned } = await import('../ensure-sources-
|
|
5259
|
+
const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
|
|
5316
5260
|
const hydrate = ensureSourcesCloned(projectDir, { quiet: true });
|
|
5317
5261
|
if (hydrate.cloned.length > 0) {
|
|
5318
5262
|
logOk(`Cloned source(s): ${hydrate.cloned.join(", ")}`);
|
|
@@ -5468,6 +5412,612 @@ function makeCleanCommand() {
|
|
|
5468
5412
|
}
|
|
5469
5413
|
});
|
|
5470
5414
|
}
|
|
5415
|
+
|
|
5416
|
+
// cli/src/commands/preset-export.ts
|
|
5417
|
+
function buildPresetFromWorkspace(config, name, sources = []) {
|
|
5418
|
+
const items = [];
|
|
5419
|
+
for (const ref of config.dependencies ?? []) {
|
|
5420
|
+
items.push({ ref });
|
|
5421
|
+
}
|
|
5422
|
+
for (const connector of config.connectors ?? []) {
|
|
5423
|
+
const config_ = compact({
|
|
5424
|
+
access: connector.access,
|
|
5425
|
+
auth: connector.auth,
|
|
5426
|
+
options: connector.options,
|
|
5427
|
+
version: connector.version,
|
|
5428
|
+
tags: connector.tags
|
|
5429
|
+
});
|
|
5430
|
+
items.push({
|
|
5431
|
+
id: connector.id,
|
|
5432
|
+
ref: `connector:${connector.driver}`,
|
|
5433
|
+
...config_ ? { config: config_ } : {}
|
|
5434
|
+
});
|
|
5435
|
+
}
|
|
5436
|
+
for (const mcp of config.mcp_servers ?? []) {
|
|
5437
|
+
const config_ = compact({
|
|
5438
|
+
transport: mcp.transport,
|
|
5439
|
+
command: mcp.command,
|
|
5440
|
+
args: mcp.args,
|
|
5441
|
+
env: mcp.env,
|
|
5442
|
+
url: mcp.url,
|
|
5443
|
+
headers: mcp.headers
|
|
5444
|
+
});
|
|
5445
|
+
items.push({
|
|
5446
|
+
id: mcp.id,
|
|
5447
|
+
ref: `mcp:${mcp.id}`,
|
|
5448
|
+
...config_ ? { config: config_ } : {}
|
|
5449
|
+
});
|
|
5450
|
+
}
|
|
5451
|
+
const project = config.name ?? name;
|
|
5452
|
+
return {
|
|
5453
|
+
name,
|
|
5454
|
+
version: "1.0.0",
|
|
5455
|
+
description: `Exported from ${project} workspace`,
|
|
5456
|
+
items,
|
|
5457
|
+
// Never let a credential-bearing source URL reach a shareable preset.
|
|
5458
|
+
...sources.length > 0 ? { sources: sources.map((s) => ({ ...s, url: stripUrlCredentials(s.url) })) } : {}
|
|
5459
|
+
};
|
|
5460
|
+
}
|
|
5461
|
+
function neededSources(config, lock) {
|
|
5462
|
+
const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
|
|
5463
|
+
const nonFactory = (config.sources ?? []).filter((s) => normalizeRepoUrl(s.url) !== factoryUrl);
|
|
5464
|
+
if (!lock) return nonFactory;
|
|
5465
|
+
const inClosure = new Set(lock.sources.map((s) => normalizeRepoUrl(s.url)));
|
|
5466
|
+
return nonFactory.filter((s) => inClosure.has(normalizeRepoUrl(s.url)));
|
|
5467
|
+
}
|
|
5468
|
+
function compact(obj) {
|
|
5469
|
+
const out = {};
|
|
5470
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
5471
|
+
if (v !== void 0 && v !== null) out[k] = v;
|
|
5472
|
+
}
|
|
5473
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
5474
|
+
}
|
|
5475
|
+
var SECRET_REFERENCE_PREFIXES = ["env", "forge", "secret", "op", "kp", "oauth", "pat"];
|
|
5476
|
+
var SECRET_REFERENCE_RE = new RegExp(`^(${SECRET_REFERENCE_PREFIXES.join("|")}):`);
|
|
5477
|
+
function isSecretReference(value) {
|
|
5478
|
+
return SECRET_REFERENCE_RE.test(value);
|
|
5479
|
+
}
|
|
5480
|
+
var PLACEHOLDER_RE = /^\$\{(INPUT|SECRET):[^}]+\}$/;
|
|
5481
|
+
var SUSPICIOUS_KEY_RE = /(?:^|[_-])(?:token|password|passwd|secret|apikey|key|dsn|credential|auth|authorization|bearer)(?:[_-]|$)/i;
|
|
5482
|
+
var PROTO_CREDENTIAL_URL_RE = /\w+:\/\/[^/@\s]+:[^/@\s]+@/;
|
|
5483
|
+
function urlHasCredentials(url) {
|
|
5484
|
+
return PROTO_CREDENTIAL_URL_RE.test(url);
|
|
5485
|
+
}
|
|
5486
|
+
function stripUrlCredentials(url) {
|
|
5487
|
+
return url.replace(/^(\w+:\/\/)[^/@]*:[^/@]*@/, "$1");
|
|
5488
|
+
}
|
|
5489
|
+
function isLongEntropic(value) {
|
|
5490
|
+
if (value.length < 20) return false;
|
|
5491
|
+
if (/^\w+:\/\//.test(value) || value.startsWith("/")) return false;
|
|
5492
|
+
const classes = [/[a-z]/, /[A-Z]/, /[0-9]/].filter((re) => re.test(value)).length;
|
|
5493
|
+
return classes >= 2;
|
|
5494
|
+
}
|
|
5495
|
+
function scanInlineSecrets(preset) {
|
|
5496
|
+
const findings = [];
|
|
5497
|
+
for (const item of preset.items) {
|
|
5498
|
+
const itemId = typeof item.id === "string" ? item.id : "";
|
|
5499
|
+
if (item.config) scanNode(item.config, [], false, itemId, findings);
|
|
5500
|
+
}
|
|
5501
|
+
return findings;
|
|
5502
|
+
}
|
|
5503
|
+
function scanNode(node, segments, inCredentialField, itemId, findings) {
|
|
5504
|
+
if (typeof node === "string") {
|
|
5505
|
+
if (isInlineSecret(node, segments.at(-1) ?? "", inCredentialField)) {
|
|
5506
|
+
findings.push({ itemId, path: segments.join("."), maskedValue: maskSecret(node) });
|
|
5507
|
+
}
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
if (Array.isArray(node)) {
|
|
5511
|
+
node.forEach(
|
|
5512
|
+
(el, i) => scanNode(el, [...segments, String(i)], inCredentialField, itemId, findings)
|
|
5513
|
+
);
|
|
5514
|
+
return;
|
|
5515
|
+
}
|
|
5516
|
+
if (node && typeof node === "object") {
|
|
5517
|
+
for (const [k, v] of Object.entries(node)) {
|
|
5518
|
+
const credField = inCredentialField || k === "auth";
|
|
5519
|
+
scanNode(v, [...segments, k], credField, itemId, findings);
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
function isInlineSecret(value, key, inCredentialField) {
|
|
5524
|
+
if (isSecretReference(value) || PLACEHOLDER_RE.test(value)) return false;
|
|
5525
|
+
if (inCredentialField) return true;
|
|
5526
|
+
if (SUSPICIOUS_KEY_RE.test(key)) return true;
|
|
5527
|
+
if (PROTO_CREDENTIAL_URL_RE.test(value)) return true;
|
|
5528
|
+
return isLongEntropic(value);
|
|
5529
|
+
}
|
|
5530
|
+
function maskSecret(value) {
|
|
5531
|
+
const proto = value.match(/^(\w+):\/\/[^/@\s]+:[^/@\s]+@/);
|
|
5532
|
+
if (proto) return `${proto[1]}://\u2026:****@\u2026`;
|
|
5533
|
+
return "****";
|
|
5534
|
+
}
|
|
5535
|
+
function redactSecrets(preset, findings) {
|
|
5536
|
+
const next = structuredClone(preset);
|
|
5537
|
+
const keys = [];
|
|
5538
|
+
const used = /* @__PURE__ */ new Set();
|
|
5539
|
+
for (const finding of findings) {
|
|
5540
|
+
const item = next.items.find((it) => it.id === finding.itemId);
|
|
5541
|
+
if (!item?.config) continue;
|
|
5542
|
+
const base = placeholderKey(finding.itemId, finding.path);
|
|
5543
|
+
let key = base;
|
|
5544
|
+
for (let n = 2; used.has(key); n++) key = `${base}_${n}`;
|
|
5545
|
+
if (!setAtPath(item.config, finding.path.split("."), `\${INPUT:${key}}`)) continue;
|
|
5546
|
+
used.add(key);
|
|
5547
|
+
const placeholder = {
|
|
5548
|
+
key,
|
|
5549
|
+
type: "secret",
|
|
5550
|
+
required: true,
|
|
5551
|
+
prompt: `Secret value for ${finding.itemId}.${finding.path}`
|
|
5552
|
+
};
|
|
5553
|
+
item.placeholders = [...item.placeholders ?? [], placeholder];
|
|
5554
|
+
keys.push(key);
|
|
5555
|
+
}
|
|
5556
|
+
return { preset: next, keys };
|
|
5557
|
+
}
|
|
5558
|
+
function placeholderKey(itemId, path36) {
|
|
5559
|
+
return `${itemId}_${path36}`.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
|
|
5560
|
+
}
|
|
5561
|
+
function setAtPath(root, segments, value) {
|
|
5562
|
+
let node = root;
|
|
5563
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
5564
|
+
if (!node || typeof node !== "object") return false;
|
|
5565
|
+
node = node[segments[i]];
|
|
5566
|
+
}
|
|
5567
|
+
if (!node || typeof node !== "object") return false;
|
|
5568
|
+
node[segments.at(-1)] = value;
|
|
5569
|
+
return true;
|
|
5570
|
+
}
|
|
5571
|
+
var INLINE_SECRETS_HEADER = "# \u26A0\uFE0F CONTAINS INLINE SECRETS \u2014 replace with placeholders before sharing or publishing";
|
|
5572
|
+
function secretHeaderComment(findings) {
|
|
5573
|
+
return findings.length > 0 ? INLINE_SECRETS_HEADER : null;
|
|
5574
|
+
}
|
|
5575
|
+
|
|
5576
|
+
// cli/src/commands/preset-cmd.ts
|
|
5577
|
+
function makePresetCommand() {
|
|
5578
|
+
const cmd = new Command("preset").description("Scaffold and validate preset YAML files");
|
|
5579
|
+
cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
|
|
5580
|
+
const presetName = name ?? "my-preset";
|
|
5581
|
+
const filename = `${presetName}.preset.yaml`;
|
|
5582
|
+
const outPath = path14__default.join(path14__default.resolve(opts.dir), filename);
|
|
5583
|
+
if (existsSync(outPath)) {
|
|
5584
|
+
logErr(`File already exists: ${outPath}`);
|
|
5585
|
+
logInfo("Choose a different name or remove the existing file.");
|
|
5586
|
+
process.exit(1);
|
|
5587
|
+
}
|
|
5588
|
+
const template = generatePresetTemplate(presetName);
|
|
5589
|
+
writeFileSync(outPath, template, "utf-8");
|
|
5590
|
+
logOk(`Created ${S.brand(filename)}`);
|
|
5591
|
+
logInfo(`Path: ${outPath}`);
|
|
5592
|
+
logInfo("Edit the file to define your preset items, then validate with:");
|
|
5593
|
+
logInfo(` skaile preset validate ${filename}`);
|
|
5594
|
+
});
|
|
5595
|
+
cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
|
|
5596
|
+
await runPresetValidate(filePath);
|
|
5597
|
+
});
|
|
5598
|
+
cmd.command("export [name]").description("Snapshot the workspace's skaile.yaml into a shareable .preset.yaml").option("--project-dir <path>", "Project directory to read skaile.yaml from", process.cwd()).option("--out <file>", "Explicit output file path").option("--dir <path>", "Output directory (default: cwd)", process.cwd()).option("--redact", "Replace every detected inline secret with a placeholder").option(
|
|
5599
|
+
"--allow-inline-secrets",
|
|
5600
|
+
"Exit 0 even when inline secrets remain unredacted (opt-in; unsafe for CI)"
|
|
5601
|
+
).option("--force", "Overwrite the output file if it already exists").action(async (name, opts) => {
|
|
5602
|
+
await runPresetExport(name, opts);
|
|
5603
|
+
});
|
|
5604
|
+
cmd.command("apply <path>").description("Apply a .preset.yaml additively into the workspace's skaile.yaml").option("--project-dir <path>", "Project directory holding skaile.yaml", process.cwd()).option(
|
|
5605
|
+
"--input <KEY=VAL>",
|
|
5606
|
+
"Provide a placeholder value (repeatable)",
|
|
5607
|
+
collectInput,
|
|
5608
|
+
[]
|
|
5609
|
+
).option("--yes", "Skip interactive prompts; use defaults where available").option("--no-deploy", "Skip the final `skaile install` step").action(async (filePath, opts) => {
|
|
5610
|
+
await runPresetApply(filePath, opts);
|
|
5611
|
+
});
|
|
5612
|
+
return cmd;
|
|
5613
|
+
}
|
|
5614
|
+
async function runPresetExport(name, opts) {
|
|
5615
|
+
const projectDir = path14__default.resolve(opts.projectDir);
|
|
5616
|
+
const file = loadSkWorkspaceConfig(projectDir);
|
|
5617
|
+
if (!file) {
|
|
5618
|
+
logErr(`No skaile.yaml found in ${projectDir}`);
|
|
5619
|
+
logInfo("Run this from a project directory or pass --project-dir <path>.");
|
|
5620
|
+
process.exit(1);
|
|
5621
|
+
}
|
|
5622
|
+
const presetName = name ?? file.config.name ?? "my-preset";
|
|
5623
|
+
let lock = null;
|
|
5624
|
+
try {
|
|
5625
|
+
lock = readLock(path14__default.join(projectDir, "skaile.lock.yaml"));
|
|
5626
|
+
} catch {
|
|
5627
|
+
lock = null;
|
|
5628
|
+
}
|
|
5629
|
+
const sources = neededSources(file.config, lock);
|
|
5630
|
+
if (lock === null && sources.length > 0) {
|
|
5631
|
+
logInfo("No lock file \u2014 exporting all non-factory sources conservatively.");
|
|
5632
|
+
}
|
|
5633
|
+
const strippedCreds = sources.filter((s) => urlHasCredentials(s.url)).length;
|
|
5634
|
+
if (strippedCreds > 0) {
|
|
5635
|
+
logWarn(`Stripped credentials from ${strippedCreds} source URL(s) \u2014 resolved via git auth on apply.`);
|
|
5636
|
+
}
|
|
5637
|
+
let preset = buildPresetFromWorkspace(file.config, presetName, sources);
|
|
5638
|
+
if (preset.items.length === 0) {
|
|
5639
|
+
logErr("Nothing to export \u2014 skaile.yaml has no dependencies, connectors, or mcp_servers.");
|
|
5640
|
+
process.exit(1);
|
|
5641
|
+
}
|
|
5642
|
+
const result = validatePreset(preset);
|
|
5643
|
+
if (!result.ok) {
|
|
5644
|
+
logErr("Internal error: generated preset failed validation:");
|
|
5645
|
+
for (const err of result.errors) console.error(` ${S.err("\u2717")} ${err.path}: ${err.message}`);
|
|
5646
|
+
process.exit(1);
|
|
5647
|
+
}
|
|
5648
|
+
const findings = scanInlineSecrets(preset);
|
|
5649
|
+
reportFindings(findings);
|
|
5650
|
+
const toRedact = await resolveRedactions(findings, opts.redact === true);
|
|
5651
|
+
if (toRedact.length > 0) preset = redactSecrets(preset, toRedact).preset;
|
|
5652
|
+
const unresolved = scanInlineSecrets(preset);
|
|
5653
|
+
const header = secretHeaderComment(unresolved);
|
|
5654
|
+
const body = stringify(preset);
|
|
5655
|
+
const contents = header ? `${header}
|
|
5656
|
+
${body}` : body;
|
|
5657
|
+
if (opts.out && opts.dir !== process.cwd()) logWarn("--dir is ignored when --out is set.");
|
|
5658
|
+
const outPath = opts.out ? path14__default.resolve(opts.out) : path14__default.join(path14__default.resolve(opts.dir), `${presetName}.preset.yaml`);
|
|
5659
|
+
if (existsSync(outPath) && !opts.force) {
|
|
5660
|
+
logErr(`File already exists: ${outPath}`);
|
|
5661
|
+
logInfo("Pass --force to overwrite, or choose a different --out/--dir.");
|
|
5662
|
+
process.exit(1);
|
|
5663
|
+
}
|
|
5664
|
+
try {
|
|
5665
|
+
writeFileSync(outPath, contents, "utf-8");
|
|
5666
|
+
} catch (e) {
|
|
5667
|
+
logErr(`Failed to write ${outPath}: ${e.message}`);
|
|
5668
|
+
process.exit(1);
|
|
5669
|
+
}
|
|
5670
|
+
logOk(`Exported ${S.brand(path14__default.basename(outPath))}`);
|
|
5671
|
+
logInfo(`Path: ${outPath}`);
|
|
5672
|
+
if (unresolved.length > 0) {
|
|
5673
|
+
console.error(S.err("This file is NOT safe to share."));
|
|
5674
|
+
if (!opts.allowInlineSecrets) {
|
|
5675
|
+
logErr(`${unresolved.length} inline secret(s) left unredacted.`);
|
|
5676
|
+
logInfo(
|
|
5677
|
+
"Re-run with --redact to placeholder them, or --allow-inline-secrets to accept the risk."
|
|
5678
|
+
);
|
|
5679
|
+
process.exit(1);
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
function reportFindings(findings) {
|
|
5684
|
+
if (findings.length === 0) return;
|
|
5685
|
+
console.error(S.err(`\u26A0\uFE0F ${findings.length} potential INLINE secret(s) found`));
|
|
5686
|
+
for (const f of findings) {
|
|
5687
|
+
console.error(` ${S.err("\u2022")} ${f.itemId} ${S.dim(f.path)} ${f.maskedValue}`);
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
async function resolveRedactions(findings, redactAll) {
|
|
5691
|
+
if (findings.length === 0) return [];
|
|
5692
|
+
if (redactAll) return findings;
|
|
5693
|
+
if (!process.stdin.isTTY) return [];
|
|
5694
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
5695
|
+
try {
|
|
5696
|
+
const chosen = [];
|
|
5697
|
+
for (const f of findings) {
|
|
5698
|
+
const answer = await rl.question(`Redact ${f.itemId}.${f.path}? [y/N] `);
|
|
5699
|
+
if (/^y(es)?$/i.test(answer.trim())) chosen.push(f);
|
|
5700
|
+
}
|
|
5701
|
+
return chosen;
|
|
5702
|
+
} finally {
|
|
5703
|
+
rl.close();
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
function collectInput(value, acc) {
|
|
5707
|
+
acc.push(value);
|
|
5708
|
+
return acc;
|
|
5709
|
+
}
|
|
5710
|
+
async function runPresetApply(filePath, opts) {
|
|
5711
|
+
const projectDir = path14__default.resolve(opts.projectDir);
|
|
5712
|
+
const file = loadSkWorkspaceConfig(projectDir);
|
|
5713
|
+
if (!file) {
|
|
5714
|
+
logErr("No skaile.yaml \u2014 run `skaile init` first");
|
|
5715
|
+
process.exit(1);
|
|
5716
|
+
}
|
|
5717
|
+
const resolved = path14__default.resolve(filePath);
|
|
5718
|
+
if (!existsSync(resolved)) {
|
|
5719
|
+
logErr(`File not found: ${resolved}`);
|
|
5720
|
+
process.exit(1);
|
|
5721
|
+
}
|
|
5722
|
+
const { parsePresetYaml } = await import('../library/index.js');
|
|
5723
|
+
const parseResult = parsePresetYaml(readFileSync(resolved, "utf-8"));
|
|
5724
|
+
if (!parseResult.ok || !parseResult.preset) {
|
|
5725
|
+
logErr("Preset validation failed:");
|
|
5726
|
+
for (const err of parseResult.errors) console.error(` ${S.err("\u2717")} ${err}`);
|
|
5727
|
+
process.exit(1);
|
|
5728
|
+
}
|
|
5729
|
+
const preset = parseResult.preset;
|
|
5730
|
+
if (preset.items.some((it) => it.ref.startsWith("preset:"))) {
|
|
5731
|
+
logErr(NESTED_PRESET_UNSUPPORTED);
|
|
5732
|
+
logInfo("Remove the nested `preset:` item(s) and re-run.");
|
|
5733
|
+
process.exit(1);
|
|
5734
|
+
}
|
|
5735
|
+
const values = parseInputs(opts.input);
|
|
5736
|
+
await promptMissingInputs(preset.items, preset.inputs, values, opts.yes === true);
|
|
5737
|
+
let items;
|
|
5738
|
+
try {
|
|
5739
|
+
items = presetItemsToWorkspace(preset, values);
|
|
5740
|
+
} catch (err) {
|
|
5741
|
+
logErr(err.message);
|
|
5742
|
+
process.exit(1);
|
|
5743
|
+
}
|
|
5744
|
+
const editor = WorkspaceYamlEditor.load(file.path);
|
|
5745
|
+
const summary = applyItems(editor, file.config, items);
|
|
5746
|
+
const changed = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
|
|
5747
|
+
if (changed > 0) editor.save();
|
|
5748
|
+
printApplySummary(summary, items.envVarsToSet);
|
|
5749
|
+
if (opts.deploy) {
|
|
5750
|
+
await runPresetInstall(projectDir);
|
|
5751
|
+
} else {
|
|
5752
|
+
logInfo("Skipped deploy (--no-deploy). Run `skaile install` to deploy the new dependencies.");
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
function parseInputs(pairs) {
|
|
5756
|
+
const out = {};
|
|
5757
|
+
for (const pair of pairs) {
|
|
5758
|
+
const eq = pair.indexOf("=");
|
|
5759
|
+
if (eq <= 0) {
|
|
5760
|
+
logErr(`Invalid --input "${pair}" \u2014 expected KEY=VALUE.`);
|
|
5761
|
+
process.exit(1);
|
|
5762
|
+
}
|
|
5763
|
+
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
5764
|
+
}
|
|
5765
|
+
return out;
|
|
5766
|
+
}
|
|
5767
|
+
async function promptMissingInputs(presetItems, presetInputs, values, yes) {
|
|
5768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5769
|
+
const toPrompt = [];
|
|
5770
|
+
const collect = (ph) => {
|
|
5771
|
+
if (seen.has(ph.key) || isSecretPlaceholder(ph)) return;
|
|
5772
|
+
seen.add(ph.key);
|
|
5773
|
+
if (values[ph.key] === void 0) toPrompt.push(ph);
|
|
5774
|
+
};
|
|
5775
|
+
for (const input of presetInputs ?? []) collect(input);
|
|
5776
|
+
for (const item of presetItems) for (const ph of item.placeholders ?? []) collect(ph);
|
|
5777
|
+
if (toPrompt.length === 0 || yes || !process.stdin.isTTY) return;
|
|
5778
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
5779
|
+
try {
|
|
5780
|
+
for (const ph of toPrompt) {
|
|
5781
|
+
const def = getDefaultValue(ph);
|
|
5782
|
+
const suffix = def !== void 0 ? ` [${String(def)}]` : "";
|
|
5783
|
+
const answer = (await rl.question(`${ph.prompt ?? ph.key}${suffix}: `)).trim();
|
|
5784
|
+
if (answer) values[ph.key] = answer;
|
|
5785
|
+
else if (def !== void 0) values[ph.key] = String(def);
|
|
5786
|
+
}
|
|
5787
|
+
} finally {
|
|
5788
|
+
rl.close();
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
function applyItems(editor, config, items) {
|
|
5792
|
+
const s = {
|
|
5793
|
+
addedDeps: [],
|
|
5794
|
+
skippedDeps: [],
|
|
5795
|
+
addedConnectors: [],
|
|
5796
|
+
skippedConnectors: [],
|
|
5797
|
+
unchangedConnectors: [],
|
|
5798
|
+
addedMcp: [],
|
|
5799
|
+
skippedMcp: [],
|
|
5800
|
+
unchangedMcp: [],
|
|
5801
|
+
addedSources: [],
|
|
5802
|
+
skippedSources: [],
|
|
5803
|
+
unchangedSources: []
|
|
5804
|
+
};
|
|
5805
|
+
const existingSourceUrls = new Map(
|
|
5806
|
+
editor.getSources().map((src) => [normalizeRepoUrl(src.url), src])
|
|
5807
|
+
);
|
|
5808
|
+
for (const src of items.sources) {
|
|
5809
|
+
const key = normalizeRepoUrl(src.url);
|
|
5810
|
+
const existing = existingSourceUrls.get(key);
|
|
5811
|
+
if (!existing) {
|
|
5812
|
+
editor.setSource(src);
|
|
5813
|
+
existingSourceUrls.set(key, src);
|
|
5814
|
+
s.addedSources.push(src.url);
|
|
5815
|
+
} else if ((existing.pin ?? void 0) !== (src.pin ?? void 0)) {
|
|
5816
|
+
s.skippedSources.push({ url: src.url, keptPin: existing.pin, presetPin: src.pin });
|
|
5817
|
+
} else {
|
|
5818
|
+
s.unchangedSources.push(src.url);
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
for (const ref of items.dependencies) {
|
|
5822
|
+
if (editor.addDependency(ref)) s.addedDeps.push(ref);
|
|
5823
|
+
else s.skippedDeps.push(ref);
|
|
5824
|
+
}
|
|
5825
|
+
const existingConnectors = new Map((config.connectors ?? []).map((c) => [c.id, c]));
|
|
5826
|
+
for (const conn of items.connectors) {
|
|
5827
|
+
const existing = existingConnectors.get(conn.id);
|
|
5828
|
+
if (existing) {
|
|
5829
|
+
(stableEqual(existing, conn) ? s.unchangedConnectors : s.skippedConnectors).push(conn.id);
|
|
5830
|
+
continue;
|
|
5831
|
+
}
|
|
5832
|
+
editor.setConnector(conn);
|
|
5833
|
+
s.addedConnectors.push(conn.id);
|
|
5834
|
+
}
|
|
5835
|
+
const existingMcp = new Map((config.mcp_servers ?? []).map((m) => [m.id, m]));
|
|
5836
|
+
for (const mcp of items.mcpServers) {
|
|
5837
|
+
const existing = existingMcp.get(mcp.id);
|
|
5838
|
+
if (existing) {
|
|
5839
|
+
(stableEqual(existing, mcp) ? s.unchangedMcp : s.skippedMcp).push(mcp.id);
|
|
5840
|
+
continue;
|
|
5841
|
+
}
|
|
5842
|
+
editor.setMcpServer(mcp);
|
|
5843
|
+
s.addedMcp.push(mcp.id);
|
|
5844
|
+
}
|
|
5845
|
+
return s;
|
|
5846
|
+
}
|
|
5847
|
+
function stableEqual(a, b) {
|
|
5848
|
+
return canonicalJson(a) === canonicalJson(b);
|
|
5849
|
+
}
|
|
5850
|
+
function canonicalJson(value) {
|
|
5851
|
+
return JSON.stringify(
|
|
5852
|
+
value,
|
|
5853
|
+
(_k, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
|
|
5854
|
+
Object.entries(v).sort(([x], [y]) => x.localeCompare(y))
|
|
5855
|
+
) : v
|
|
5856
|
+
);
|
|
5857
|
+
}
|
|
5858
|
+
function printApplySummary(summary, envVarsToSet) {
|
|
5859
|
+
const added = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
|
|
5860
|
+
if (added === 0) {
|
|
5861
|
+
logInfo("Nothing to apply \u2014 everything was already present.");
|
|
5862
|
+
} else {
|
|
5863
|
+
if (summary.addedSources.length > 0) {
|
|
5864
|
+
logOk(`Registered ${summary.addedSources.length} source(s).`);
|
|
5865
|
+
}
|
|
5866
|
+
logOk(
|
|
5867
|
+
`Applied ${summary.addedDeps.length} dependency(ies), ${summary.addedConnectors.length} connector(s), ${summary.addedMcp.length} mcp server(s).`
|
|
5868
|
+
);
|
|
5869
|
+
}
|
|
5870
|
+
const skipped = [
|
|
5871
|
+
...summary.skippedSources.map(
|
|
5872
|
+
(s) => `source ${s.url} (kept pin '${s.keptPin ?? "none"}', preset had '${s.presetPin ?? "none"}')`
|
|
5873
|
+
),
|
|
5874
|
+
...summary.skippedDeps.map((d) => `dependency ${d} (already present)`),
|
|
5875
|
+
...summary.skippedConnectors.map((c) => `connector ${c} (id exists, differs)`),
|
|
5876
|
+
...summary.skippedMcp.map((m) => `mcp ${m} (id exists, differs)`)
|
|
5877
|
+
];
|
|
5878
|
+
for (const line of skipped) logWarn(`Skipped ${line}`);
|
|
5879
|
+
const unchanged = summary.unchangedConnectors.length + summary.unchangedMcp.length + summary.unchangedSources.length;
|
|
5880
|
+
if (unchanged > 0) logInfo(`${unchanged} source/connector/mcp entry(ies) already matched \u2014 left as-is.`);
|
|
5881
|
+
if (envVarsToSet.length > 0) {
|
|
5882
|
+
logWarn(`Set these env vars before running: ${envVarsToSet.join(", ")}`);
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
async function runPresetInstall(projectDir) {
|
|
5886
|
+
try {
|
|
5887
|
+
const { AssetManager: AssetManager2 } = await import('../asset-manager/index.js');
|
|
5888
|
+
const am2 = new AssetManager2({ projectDir });
|
|
5889
|
+
logInfo("Installing new dependencies\u2026");
|
|
5890
|
+
const result = await am2.install({});
|
|
5891
|
+
for (const d of result.deployed) logOk(d);
|
|
5892
|
+
for (const m of result.missing) logErr(`Missing: ${m}`);
|
|
5893
|
+
await reportInstalledSoftDeps(am2);
|
|
5894
|
+
} catch (err) {
|
|
5895
|
+
logErr(`Install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5896
|
+
logInfo("Run `skaile install` to deploy the new dependencies.");
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
async function loadPresetForValidation(resolved) {
|
|
5900
|
+
if (!existsSync(resolved)) {
|
|
5901
|
+
logErr(`File not found: ${resolved}`);
|
|
5902
|
+
process.exit(1);
|
|
5903
|
+
}
|
|
5904
|
+
const content = readFileSync(resolved, "utf-8");
|
|
5905
|
+
const { parsePresetYaml } = await import('../library/index.js');
|
|
5906
|
+
const parseResult = parsePresetYaml(content);
|
|
5907
|
+
if (!parseResult.ok) {
|
|
5908
|
+
logErr("Preset validation failed (parse errors):");
|
|
5909
|
+
for (const err of parseResult.errors) {
|
|
5910
|
+
console.log(` ${S.err("\u2717")} ${err}`);
|
|
5911
|
+
}
|
|
5912
|
+
process.exit(1);
|
|
5913
|
+
}
|
|
5914
|
+
if (!parseResult.preset) {
|
|
5915
|
+
logErr("Preset validation failed: parser returned no manifest");
|
|
5916
|
+
process.exit(1);
|
|
5917
|
+
}
|
|
5918
|
+
return parseResult.preset;
|
|
5919
|
+
}
|
|
5920
|
+
async function reportNestedPresetRefs(preset) {
|
|
5921
|
+
const { detectNestedPresetRefs } = await import('../library/index.js');
|
|
5922
|
+
const nestedRefs = detectNestedPresetRefs(preset);
|
|
5923
|
+
if (nestedRefs.length > 0) {
|
|
5924
|
+
logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
async function checkPresetCycles(preset, resolved) {
|
|
5928
|
+
const { detectCycles } = await import('../discovery/index.js');
|
|
5929
|
+
const presetRef = path14__default.basename(resolved, ".preset.yaml");
|
|
5930
|
+
const edges = [];
|
|
5931
|
+
for (const item of preset.items) {
|
|
5932
|
+
if (item.ref) {
|
|
5933
|
+
edges.push({ from: presetRef, to: item.ref, field: "items[].ref" });
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
if (edges.length === 0) return;
|
|
5937
|
+
const cycleResult = detectCycles(edges);
|
|
5938
|
+
if (!cycleResult.acyclic) {
|
|
5939
|
+
logErr("Cycle detected in preset references:");
|
|
5940
|
+
if (cycleResult.cyclePath) {
|
|
5941
|
+
console.log(` ${S.err("\u2717")} ${cycleResult.cyclePath.join(" \u2192 ")}`);
|
|
5942
|
+
}
|
|
5943
|
+
process.exit(1);
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
async function checkPresetPlaceholders(allPlaceholders) {
|
|
5947
|
+
if (allPlaceholders.length === 0) return false;
|
|
5948
|
+
const { validatePlaceholder } = await import('../library/index.js');
|
|
5949
|
+
const invalidEntries = [];
|
|
5950
|
+
for (const ph of allPlaceholders) {
|
|
5951
|
+
const result = validatePlaceholder(ph, ph.default);
|
|
5952
|
+
if (!result.valid) {
|
|
5953
|
+
invalidEntries.push({ key: ph.key, errors: result.errors });
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
if (invalidEntries.length === 0) return false;
|
|
5957
|
+
logWarn("Placeholder issues:");
|
|
5958
|
+
for (const r of invalidEntries) {
|
|
5959
|
+
console.log(` ${S.warn("!")} ${r.key}: ${r.errors.join(", ")}`);
|
|
5960
|
+
}
|
|
5961
|
+
return true;
|
|
5962
|
+
}
|
|
5963
|
+
function printPresetSummary(preset, placeholderCount, hasWarnings) {
|
|
5964
|
+
console.log();
|
|
5965
|
+
console.log(S.heading(" Preset Validation"));
|
|
5966
|
+
console.log(` ${S.rule(40)}`);
|
|
5967
|
+
console.log(` ${S.label("Name")} ${preset.name}`);
|
|
5968
|
+
console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
|
|
5969
|
+
console.log(` ${S.label("Items")} ${preset.items.length}`);
|
|
5970
|
+
console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
|
|
5971
|
+
console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
|
|
5972
|
+
console.log();
|
|
5973
|
+
if (hasWarnings) {
|
|
5974
|
+
logWarn("Validation passed with warnings");
|
|
5975
|
+
} else {
|
|
5976
|
+
logOk("Preset is valid");
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
async function runPresetValidate(filePath) {
|
|
5980
|
+
const resolved = path14__default.resolve(filePath);
|
|
5981
|
+
const preset = await loadPresetForValidation(resolved);
|
|
5982
|
+
await reportNestedPresetRefs(preset);
|
|
5983
|
+
await checkPresetCycles(preset, resolved);
|
|
5984
|
+
const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
|
|
5985
|
+
const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
|
|
5986
|
+
printPresetSummary(preset, allPlaceholders.length, hasWarnings);
|
|
5987
|
+
}
|
|
5988
|
+
function generatePresetTemplate(name) {
|
|
5989
|
+
return `# ${name}.preset.yaml
|
|
5990
|
+
# Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
|
|
5991
|
+
# See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
|
|
5992
|
+
|
|
5993
|
+
name: "${name}"
|
|
5994
|
+
version: "1.0.0"
|
|
5995
|
+
description: "TODO: describe what this preset configures"
|
|
5996
|
+
|
|
5997
|
+
# Placeholders \u2014 values the user provides at apply-time.
|
|
5998
|
+
# Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
|
|
5999
|
+
placeholders:
|
|
6000
|
+
- key: EXAMPLE_KEY
|
|
6001
|
+
type: text
|
|
6002
|
+
label: "Example placeholder"
|
|
6003
|
+
required: true
|
|
6004
|
+
# default: "some-value"
|
|
6005
|
+
# hint: "Help text shown to the user"
|
|
6006
|
+
|
|
6007
|
+
# Items \u2014 each becomes a Library Instance when the preset is applied.
|
|
6008
|
+
# Ref types:
|
|
6009
|
+
# - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
|
|
6010
|
+
# - Library Instance ref (#uuid) \u2192 reuses existing Instance
|
|
6011
|
+
# - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
|
|
6012
|
+
items:
|
|
6013
|
+
- id: example-item
|
|
6014
|
+
ref: "skill:@skaile/example-skill@^1.0"
|
|
6015
|
+
config:
|
|
6016
|
+
# Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
|
|
6017
|
+
setting: "\${INPUT:EXAMPLE_KEY}"
|
|
6018
|
+
# credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
|
|
6019
|
+
`;
|
|
6020
|
+
}
|
|
5471
6021
|
function makeRebuildCommand() {
|
|
5472
6022
|
return new Command("rebuild").description("Re-snapshot inline-snapshot composition items and bump prompt version").argument("[agent]", "Agent name or path (defaults to current project agent)").option("--project-dir <dir>", "Project directory", process.cwd()).action(async (agentArg, opts) => {
|
|
5473
6023
|
const projectDir = path14__default.resolve(opts.projectDir);
|
|
@@ -5786,12 +6336,12 @@ function makeServeCommand() {
|
|
|
5786
6336
|
);
|
|
5787
6337
|
}
|
|
5788
6338
|
async function ensurePluginsLoadedForServe(projectDir) {
|
|
5789
|
-
const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-
|
|
6339
|
+
const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-GWG4CYYC.js');
|
|
5790
6340
|
try {
|
|
5791
6341
|
const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
|
|
5792
6342
|
const config = resolveSkWorkspaceConfig2(projectDir);
|
|
5793
6343
|
if (!config.plugins || config.plugins.length === 0) return;
|
|
5794
|
-
const { ensurePluginsLoaded } = await import('../plugin-store-
|
|
6344
|
+
const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
|
|
5795
6345
|
const { pluginRegistry } = await import('../plugin-registry/index.js');
|
|
5796
6346
|
const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
|
|
5797
6347
|
if (result.loaded.length > 0) {
|
|
@@ -5805,14 +6355,14 @@ async function ensurePluginsLoadedForServe(projectDir) {
|
|
|
5805
6355
|
}
|
|
5806
6356
|
}
|
|
5807
6357
|
async function runCompileTest(opts) {
|
|
5808
|
-
const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-
|
|
6358
|
+
const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-GWG4CYYC.js');
|
|
5809
6359
|
const { tmpdir } = await import('os');
|
|
5810
6360
|
const { dirname: dirname4, join: join5, resolve: resolve5 } = await import('path');
|
|
5811
6361
|
const { fileURLToPath: fileURLToPath2 } = await import('url');
|
|
5812
6362
|
const { unlinkSync, existsSync: existsSync13 } = await import('fs');
|
|
5813
6363
|
const { spawn } = await import('child_process');
|
|
5814
6364
|
const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
|
|
5815
|
-
const { MONOREPO_ROOT } = await import('../paths-
|
|
6365
|
+
const { MONOREPO_ROOT } = await import('../paths-UVOHWUPY.js');
|
|
5816
6366
|
if (MONOREPO_ROOT === null) {
|
|
5817
6367
|
logErr2("--compile-test is only available inside the skaile-dev workspace.");
|
|
5818
6368
|
logInfo2("Run it from a checkout of skaile-ai/skaile-dev (the monorepo shell).");
|
|
@@ -6630,8 +7180,8 @@ function makeStoreCommand() {
|
|
|
6630
7180
|
store.command("login").description("Authenticate with the store").option("--email <email>", "Login with email instead of GitHub").action(async (opts) => {
|
|
6631
7181
|
const config = getStoreConfig();
|
|
6632
7182
|
if (opts.email) {
|
|
6633
|
-
const
|
|
6634
|
-
const rl =
|
|
7183
|
+
const readline3 = await import('readline');
|
|
7184
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
6635
7185
|
const password = await new Promise((resolve5) => {
|
|
6636
7186
|
rl.question("Password: ", (answer) => {
|
|
6637
7187
|
rl.close();
|
|
@@ -7448,12 +7998,43 @@ function makeVerifyCommand() {
|
|
|
7448
7998
|
}
|
|
7449
7999
|
});
|
|
7450
8000
|
}
|
|
8001
|
+
function refKindName(ref) {
|
|
8002
|
+
const trimmed = ref.trim();
|
|
8003
|
+
const colon = trimmed.indexOf(":");
|
|
8004
|
+
let kind = colon === -1 ? void 0 : trimmed.slice(0, colon);
|
|
8005
|
+
if (kind === "mcp") kind = "mcp-server";
|
|
8006
|
+
let rest = colon === -1 ? trimmed : trimmed.slice(colon + 1);
|
|
8007
|
+
const hash = rest.indexOf("#");
|
|
8008
|
+
if (hash !== -1) rest = rest.slice(0, hash);
|
|
8009
|
+
if (rest.startsWith("@")) {
|
|
8010
|
+
const slash = rest.indexOf("/");
|
|
8011
|
+
if (slash !== -1) rest = rest.slice(slash + 1);
|
|
8012
|
+
}
|
|
8013
|
+
return { kind, name: rest };
|
|
8014
|
+
}
|
|
8015
|
+
function whyEmptyMessage(ref, declared) {
|
|
8016
|
+
const { kind, name } = refKindName(ref);
|
|
8017
|
+
const isDeclared = declared.some(
|
|
8018
|
+
(d) => d.name === name && (kind === void 0 || d.kind === kind)
|
|
8019
|
+
);
|
|
8020
|
+
if (isDeclared) {
|
|
8021
|
+
return `${ref} not found in lock file. Run \`skaile install\` first.`;
|
|
8022
|
+
}
|
|
8023
|
+
return `${ref} is not a dependency of this project \u2014 nothing in skaile.yaml requires it. Run \`skaile list\` to see declared dependencies.`;
|
|
8024
|
+
}
|
|
7451
8025
|
function makeWhyCommand() {
|
|
7452
8026
|
return new Command("why").description("Show why an asset is installed (dependency chain)").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action(async (ref, opts) => {
|
|
7453
8027
|
const am2 = new AssetManager({ projectDir: path14__default.resolve(opts.projectDir) });
|
|
7454
8028
|
const chain = am2.why(ref);
|
|
7455
8029
|
if (chain.length === 0) {
|
|
7456
|
-
|
|
8030
|
+
let declared;
|
|
8031
|
+
try {
|
|
8032
|
+
declared = am2.listDeclared();
|
|
8033
|
+
} catch {
|
|
8034
|
+
logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
|
|
8035
|
+
return;
|
|
8036
|
+
}
|
|
8037
|
+
logErr(whyEmptyMessage(ref, declared));
|
|
7457
8038
|
return;
|
|
7458
8039
|
}
|
|
7459
8040
|
console.log();
|
|
@@ -7599,7 +8180,7 @@ program.command("init [project-dir]").description("Initialize a project director
|
|
|
7599
8180
|
);
|
|
7600
8181
|
async function runInit(projectDir, opts) {
|
|
7601
8182
|
const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync4 } = await import('fs');
|
|
7602
|
-
const { stringify } = await import('yaml');
|
|
8183
|
+
const { stringify: stringify2 } = await import('yaml');
|
|
7603
8184
|
const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
|
|
7604
8185
|
const backend = opts.backend;
|
|
7605
8186
|
if (!SUPPORTED_DRIVER_TARGETS2.includes(backend)) {
|
|
@@ -7627,7 +8208,7 @@ async function runInit(projectDir, opts) {
|
|
|
7627
8208
|
}
|
|
7628
8209
|
const wsConfigPath = path14__default.join(resolved, "skaile.yaml");
|
|
7629
8210
|
if (!existsSync13(wsConfigPath)) {
|
|
7630
|
-
writeFileSync4(wsConfigPath,
|
|
8211
|
+
writeFileSync4(wsConfigPath, stringify2(buildInitWorkspaceConfig(projectName, backend)));
|
|
7631
8212
|
created.push("skaile.yaml");
|
|
7632
8213
|
}
|
|
7633
8214
|
for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
|
|
@@ -7718,7 +8299,7 @@ async function bootstrapUserConfig() {
|
|
|
7718
8299
|
}
|
|
7719
8300
|
async function hydrateInitSources(resolved, created) {
|
|
7720
8301
|
try {
|
|
7721
|
-
const { ensureSourcesCloned } = await import('../ensure-sources-
|
|
8302
|
+
const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
|
|
7722
8303
|
const hydrate = ensureSourcesCloned(resolved, { quiet: true });
|
|
7723
8304
|
for (const n of hydrate.cloned) created.push(`~/.skaile/cache/sources/${n}/`);
|
|
7724
8305
|
if (hydrate.failed.length > 0) {
|
|
@@ -7754,7 +8335,7 @@ function printInitSummary(resolved, projectDir, created) {
|
|
|
7754
8335
|
console.log();
|
|
7755
8336
|
}
|
|
7756
8337
|
program.command("setup").description("Interactive provider setup wizard").action(async () => {
|
|
7757
|
-
const { cmdSetup } = await import('../setup-
|
|
8338
|
+
const { cmdSetup } = await import('../setup-DINVR6ZQ.js');
|
|
7758
8339
|
await cmdSetup([], { projectDir: process.cwd() });
|
|
7759
8340
|
});
|
|
7760
8341
|
program.addCommand(makeInstallCommand());
|