@skaile/workspaces 1.4.0 → 1.6.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 +326 -0
- package/dist/app-sdk/index.js +241 -55
- package/dist/app-sdk/index.js.map +1 -1
- package/dist/app-sdk/src/app.d.ts +55 -4
- package/dist/app-sdk/src/app.d.ts.map +1 -1
- package/dist/app-sdk/src/connection-params.d.ts +117 -0
- package/dist/app-sdk/src/connection-params.d.ts.map +1 -0
- package/dist/app-sdk/src/index.d.ts +3 -2
- package/dist/app-sdk/src/index.d.ts.map +1 -1
- package/dist/app-sdk/src/transport.d.ts +9 -0
- package/dist/app-sdk/src/transport.d.ts.map +1 -1
- package/dist/app-sdk/src/ws-transport.d.ts +18 -2
- package/dist/app-sdk/src/ws-transport.d.ts.map +1 -1
- package/dist/{asset-feeds-NG5TKB3V.js → asset-feeds-T35ELX2O.js} +9 -9
- package/dist/{asset-feeds-NG5TKB3V.js.map → asset-feeds-T35ELX2O.js.map} +1 -1
- package/dist/asset-manager/index.js +11 -6
- package/dist/asset-manager/installer.js +5 -5
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/{chunk-BE5KTS2L.js → chunk-2DWXGUZA.js} +2 -2
- package/dist/{chunk-BE5KTS2L.js.map → chunk-2DWXGUZA.js.map} +1 -1
- package/dist/{chunk-XBG2YDQQ.js → chunk-2ZMO4CFG.js} +3 -3
- package/dist/{chunk-XBG2YDQQ.js.map → chunk-2ZMO4CFG.js.map} +1 -1
- package/dist/{chunk-X57AMTAH.js → chunk-3CHARYD4.js} +16 -7
- package/dist/chunk-3CHARYD4.js.map +1 -0
- package/dist/{chunk-LCTRCWBK.js → chunk-6NR2BEIY.js} +4 -4
- package/dist/{chunk-LCTRCWBK.js.map → chunk-6NR2BEIY.js.map} +1 -1
- package/dist/{chunk-JZPEEMIM.js → chunk-BEO6KN7P.js} +5 -5
- package/dist/{chunk-JZPEEMIM.js.map → chunk-BEO6KN7P.js.map} +1 -1
- package/dist/{chunk-AAGD2J4H.js → chunk-CBA2XIBF.js} +33 -15
- package/dist/chunk-CBA2XIBF.js.map +1 -0
- package/dist/{chunk-GWDZW44L.js → chunk-DMG6NJH7.js} +4 -4
- package/dist/{chunk-GWDZW44L.js.map → chunk-DMG6NJH7.js.map} +1 -1
- package/dist/{chunk-HWTQ6HPO.js → chunk-FDE7XRQA.js} +56 -8
- package/dist/chunk-FDE7XRQA.js.map +1 -0
- package/dist/{chunk-26YCXWCU.js → chunk-HY33RMEA.js} +3 -3
- package/dist/{chunk-26YCXWCU.js.map → chunk-HY33RMEA.js.map} +1 -1
- package/dist/chunk-J5TI55E7.js +203 -0
- package/dist/chunk-J5TI55E7.js.map +1 -0
- package/dist/chunk-JIDKX7GC.js +122 -0
- package/dist/chunk-JIDKX7GC.js.map +1 -0
- package/dist/{chunk-BDUANIAR.js → chunk-KEQ4GLVM.js} +3 -3
- package/dist/{chunk-BDUANIAR.js.map → chunk-KEQ4GLVM.js.map} +1 -1
- package/dist/{chunk-DNFIKRO6.js → chunk-KMY7HVQ6.js} +3 -3
- package/dist/{chunk-DNFIKRO6.js.map → chunk-KMY7HVQ6.js.map} +1 -1
- package/dist/{chunk-B6SSXVF2.js → chunk-MZV6WCF3.js} +81 -49
- package/dist/chunk-MZV6WCF3.js.map +1 -0
- package/dist/{chunk-DZCK7GRW.js → chunk-P4VHQA4N.js} +22 -6
- package/dist/chunk-P4VHQA4N.js.map +1 -0
- package/dist/{chunk-OSCJRUKM.js → chunk-RDYOU6N6.js} +2 -2
- package/dist/{chunk-OSCJRUKM.js.map → chunk-RDYOU6N6.js.map} +1 -1
- package/dist/{chunk-77OXU44H.js → chunk-RSYV7XEN.js} +13 -7
- package/dist/chunk-RSYV7XEN.js.map +1 -0
- package/dist/{chunk-OUWNTFDG.js → chunk-UQKYT4V6.js} +38 -6
- package/dist/chunk-UQKYT4V6.js.map +1 -0
- package/dist/chunk-WYGXYGJH.js +688 -0
- package/dist/chunk-WYGXYGJH.js.map +1 -0
- package/dist/{chunk-TJRWGMD2.js → chunk-ZBZQC5R7.js} +3 -3
- package/dist/{chunk-TJRWGMD2.js.map → chunk-ZBZQC5R7.js.map} +1 -1
- package/dist/cli/index.js +259 -192
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/catalog.d.ts +18 -0
- package/dist/cli/src/commands/catalog.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts +2 -2
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-publish.d.ts +53 -0
- package/dist/cli/src/commands/library-publish.d.ts.map +1 -0
- package/dist/cli/src/commands/preset-cmd.d.ts +6 -0
- package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/preset-export.d.ts +10 -1
- package/dist/cli/src/commands/preset-export.d.ts.map +1 -1
- package/dist/cli/src/commands/project.d.ts.map +1 -1
- package/dist/cli/src/commands/publish-core.d.ts +75 -0
- package/dist/cli/src/commands/publish-core.d.ts.map +1 -0
- package/dist/cli/src/commands/run.d.ts.map +1 -1
- package/dist/cli/src/commands/source-manifest-publish.d.ts +82 -0
- package/dist/cli/src/commands/source-manifest-publish.d.ts.map +1 -0
- package/dist/cli/src/commands/source-manifest.d.ts +65 -6
- package/dist/cli/src/commands/source-manifest.d.ts.map +1 -1
- package/dist/cli/src/commands/source.d.ts +13 -0
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/store.d.ts.map +1 -1
- package/dist/cli/src/driver-error.d.ts +12 -0
- package/dist/cli/src/driver-error.d.ts.map +1 -0
- package/dist/cli/src/open-library.d.ts +4 -0
- package/dist/cli/src/open-library.d.ts.map +1 -1
- package/dist/cli/src/store-auth.d.ts +42 -0
- package/dist/cli/src/store-auth.d.ts.map +1 -0
- package/dist/cli/src/store-client.d.ts +20 -22
- package/dist/cli/src/store-client.d.ts.map +1 -1
- package/dist/cli/src/store-publisher.d.ts +91 -0
- package/dist/cli/src/store-publisher.d.ts.map +1 -0
- package/dist/connectors/config.js +5 -5
- package/dist/connectors/index.js +6 -6
- package/dist/core/index.js +4 -4
- package/dist/core/publish-manifest.js +1 -1
- package/dist/core/runtime-assets.js +3 -3
- package/dist/core/src/cache-paths.d.ts +17 -0
- package/dist/core/src/cache-paths.d.ts.map +1 -1
- package/dist/core/src/index.d.ts +2 -2
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/publish-manifest.d.ts.map +1 -1
- package/dist/core/src/repo-manager.d.ts +13 -2
- package/dist/core/src/repo-manager.d.ts.map +1 -1
- package/dist/core/src/walker.d.ts +10 -0
- package/dist/core/src/walker.d.ts.map +1 -1
- package/dist/core/workspace-config.js +2 -2
- package/dist/deploy/index.js +4 -4
- package/dist/discovery/index.js +2 -2
- package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
- package/dist/discovery/src/discover.d.ts +17 -4
- package/dist/discovery/src/discover.d.ts.map +1 -1
- package/dist/discovery/src/index.d.ts +1 -1
- package/dist/discovery/src/index.d.ts.map +1 -1
- package/dist/discovery/src/reference-lint.d.ts +7 -0
- package/dist/discovery/src/reference-lint.d.ts.map +1 -1
- package/dist/discovery/src/source-config.d.ts +28 -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-CHC7X7AJ.js → ensure-sources-QDVFUIUP.js} +14 -9
- package/dist/ensure-sources-QDVFUIUP.js.map +1 -0
- package/dist/factory-assets/connectors/box.js +6 -6
- package/dist/factory-assets/connectors/deploy.js +6 -6
- package/dist/factory-assets/connectors/devserver.js +6 -6
- package/dist/factory-assets/connectors/flow/adapter.js +6 -6
- package/dist/factory-assets/connectors/flow/engine.js +4 -4
- package/dist/factory-assets/connectors/flow/run-flow.js +7 -7
- package/dist/factory-assets/connectors/flow.js +6 -6
- package/dist/factory-assets/connectors/git.js +6 -6
- package/dist/factory-assets/connectors/gmail.js +6 -6
- package/dist/factory-assets/connectors/googledrive.js +6 -6
- package/dist/factory-assets/connectors/local.js +6 -6
- package/dist/factory-assets/connectors/mattermost.js +6 -6
- package/dist/factory-assets/connectors/memory.js +6 -6
- package/dist/factory-assets/connectors/minio.js +6 -6
- package/dist/factory-assets/connectors/postgres.js +6 -6
- package/dist/factory-assets/connectors/s3.js +6 -6
- package/dist/factory-assets/connectors/sharepoint.js +6 -6
- package/dist/factory-assets/connectors/sqlite.js +6 -6
- package/dist/factory-assets/connectors/static-server.js +6 -6
- package/dist/factory-assets/connectors/tunnel.js +6 -6
- package/dist/factory-assets/connectors/webdav.js +6 -6
- package/dist/factory-assets/connectors/xstate-store.js +6 -6
- package/dist/factory-assets/connectors/xstate.js +6 -6
- package/dist/{flows-CGOCUNO6.js → flows-MRVXTYWC.js} +3 -3
- package/dist/{flows-CGOCUNO6.js.map → flows-MRVXTYWC.js.map} +1 -1
- package/dist/{helpers-GWG4CYYC.js → helpers-TF7GCBRQ.js} +14 -9
- package/dist/{helpers-GWG4CYYC.js.map → helpers-TF7GCBRQ.js.map} +1 -1
- package/dist/library/index.js +6 -6
- package/dist/library/install.js +4 -4
- package/dist/library/src/index.d.ts +2 -2
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/local/local-catalog-source.d.ts +12 -1
- package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
- package/dist/library/src/sync/manifest-writeback.d.ts +36 -0
- package/dist/library/src/sync/manifest-writeback.d.ts.map +1 -1
- package/dist/library-publish-K5IBE44D.js +249 -0
- package/dist/library-publish-K5IBE44D.js.map +1 -0
- package/dist/{open-library-37PXN6MK.js → open-library-4HMIDK3N.js} +13 -13
- package/dist/{open-library-37PXN6MK.js.map → open-library-4HMIDK3N.js.map} +1 -1
- package/dist/{paths-UVOHWUPY.js → paths-P3DWFUPS.js} +7 -7
- package/dist/{paths-UVOHWUPY.js.map → paths-P3DWFUPS.js.map} +1 -1
- package/dist/{plugin-store-EIMVEH3O.js → plugin-store-S7OMGI7N.js} +6 -6
- package/dist/{plugin-store-EIMVEH3O.js.map → plugin-store-S7OMGI7N.js.map} +1 -1
- package/dist/runner/index.js +8 -8
- package/dist/runner/src/agent.d.ts.map +1 -1
- package/dist/runner/src/capability-registry.d.ts +21 -2
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +11 -6
- package/dist/sdk/core.js +4 -4
- package/dist/sdk/flow.js +4 -4
- package/dist/sdk/index.js +8 -8
- package/dist/sdk/runner.js +8 -8
- package/dist/{setup-DINVR6ZQ.js → setup-2CCS63L6.js} +6 -6
- package/dist/{setup-DINVR6ZQ.js.map → setup-2CCS63L6.js.map} +1 -1
- package/dist/source-LOAM7IIQ.js +30 -0
- package/dist/{source-RXAHIGMD.js.map → source-LOAM7IIQ.js.map} +1 -1
- package/dist/source-manifest-publish-TGVZ6KMC.js +228 -0
- package/dist/source-manifest-publish-TGVZ6KMC.js.map +1 -0
- package/dist/tui/index.js +8 -8
- package/dist/workspace-plugin/adapters/mcp.js +1 -1
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-77OXU44H.js.map +0 -1
- package/dist/chunk-AAGD2J4H.js.map +0 -1
- package/dist/chunk-B6SSXVF2.js.map +0 -1
- package/dist/chunk-BJHZHNKR.js +0 -93
- package/dist/chunk-BJHZHNKR.js.map +0 -1
- package/dist/chunk-DZCK7GRW.js.map +0 -1
- package/dist/chunk-HWTQ6HPO.js.map +0 -1
- package/dist/chunk-OUWNTFDG.js.map +0 -1
- package/dist/chunk-QM2TT2PJ.js +0 -385
- package/dist/chunk-QM2TT2PJ.js.map +0 -1
- package/dist/chunk-X57AMTAH.js.map +0 -1
- package/dist/ensure-sources-CHC7X7AJ.js.map +0 -1
- package/dist/source-RXAHIGMD.js +0 -30
- package/dist/store-client-T3F663YG.js +0 -15
- package/dist/store-client-T3F663YG.js.map +0 -1
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
import { openLibraryManager, createFullRegistry } from './chunk-RSYV7XEN.js';
|
|
2
|
+
import { logErr, logInfo, logWarn, logOk, S } from './chunk-KMY7HVQ6.js';
|
|
3
|
+
import { getStoreManifestsDir, LocalCatalogSource, writeCuratedManifest } from './chunk-FDE7XRQA.js';
|
|
4
|
+
import { computeDeterministicHash, discoverAssetsInTree } from './chunk-MZV6WCF3.js';
|
|
5
|
+
import { WorkspaceYamlEditor } from './chunk-HY33RMEA.js';
|
|
6
|
+
import { findWorkspaceRoot, workspaceConfigFilename, sourceShortName, sourceCacheKey, storeManifestSlug, getGlobalCacheDir, resolveSourcePublisher } from './chunk-UQKYT4V6.js';
|
|
7
|
+
import { decodeSkaileManifest, encodeSkaileManifest } from './chunk-P4VHQA4N.js';
|
|
8
|
+
import { parseAssetRef } from './chunk-GEEW6SY2.js';
|
|
9
|
+
import { spawnSync } from 'child_process';
|
|
10
|
+
import { existsSync, rmSync, renameSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as p from '@clack/prompts';
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
|
|
15
|
+
var NOT_WIRED_MSG = "`skaile source manifest *` is scaffolded but not yet wired. Server-side endpoint (`POST /sources/<id>/manifest`) lands in a follow-up PR.";
|
|
16
|
+
function notWired(verb) {
|
|
17
|
+
logErr(`${NOT_WIRED_MSG} (verb: source manifest ${verb})`);
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
function resolveSourceArg(arg, projectDir) {
|
|
21
|
+
const root = findWorkspaceRoot(projectDir);
|
|
22
|
+
const registered = root ? readProjectSources(path.join(root, workspaceConfigFilename())) : [];
|
|
23
|
+
const match = registered.find((e) => sourceMatchesKey(e, arg));
|
|
24
|
+
const bySlug = !match ? resolveByStoreSlug(arg) : void 0;
|
|
25
|
+
const url = match ? match.url : bySlug?.url ?? normalizeSourceUrl(arg);
|
|
26
|
+
const slug = sourceShortName(url);
|
|
27
|
+
const dest = sourceClonePath(sourceCacheKey(url));
|
|
28
|
+
return { url, slug, dest, manifestFile: bySlug?.manifestFile ?? storeManifestPath(url) };
|
|
29
|
+
}
|
|
30
|
+
function resolveByStoreSlug(arg) {
|
|
31
|
+
if (!/^[\w.-]+__[\w.-]+/.test(arg) || /[/:\\]/.test(arg)) return void 0;
|
|
32
|
+
const manifestFile = path.join(getStoreManifestsDir(), `${arg}.yaml`);
|
|
33
|
+
if (!existsSync(manifestFile)) return void 0;
|
|
34
|
+
const [host, owner, repo] = arg.split("__");
|
|
35
|
+
const url = host && owner && repo ? `https://${host}/${owner}/${repo}` : arg;
|
|
36
|
+
return { url, manifestFile };
|
|
37
|
+
}
|
|
38
|
+
function storeManifestPath(url) {
|
|
39
|
+
return path.join(getStoreManifestsDir(), `${storeManifestSlug(url)}.yaml`);
|
|
40
|
+
}
|
|
41
|
+
var TEST_FIXTURE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
42
|
+
"tests",
|
|
43
|
+
"test",
|
|
44
|
+
"__tests__",
|
|
45
|
+
"testdata",
|
|
46
|
+
"fixtures",
|
|
47
|
+
"__fixtures__"
|
|
48
|
+
]);
|
|
49
|
+
function isTestFixtureAsset(relativePath) {
|
|
50
|
+
const segments = relativePath.replace(/\\/g, "/").split("/");
|
|
51
|
+
return segments.some((s) => TEST_FIXTURE_SEGMENTS.has(s));
|
|
52
|
+
}
|
|
53
|
+
async function runInit(arg, opts) {
|
|
54
|
+
const projectDir = path.resolve(opts.projectDir);
|
|
55
|
+
const { url, dest, manifestFile: target } = resolveSourceArg(arg, projectDir);
|
|
56
|
+
if (existsSync(target) && !opts.force) {
|
|
57
|
+
logWarn(
|
|
58
|
+
`A curated manifest already exists at ${S.dim(target)}. Edit it with \`skaile source manifest edit\`, refresh it after an upstream change with \`skaile source manifest rebuild\`, or pass --force to regenerate.`
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const entry = { url };
|
|
63
|
+
ensureClone(entry);
|
|
64
|
+
const { assets, errors } = discoverCuratableAssets(dest, opts.dev ?? false);
|
|
65
|
+
const publisher = opts.publisher ?? resolveSourcePublisher(dest) ?? sourceShortName(url);
|
|
66
|
+
const written = writeCuratedManifest({
|
|
67
|
+
manifestFile: target,
|
|
68
|
+
publisher,
|
|
69
|
+
assets,
|
|
70
|
+
force: opts.force,
|
|
71
|
+
// Init: every asset inherits the top-level repo `publisher`. Do NOT stamp a
|
|
72
|
+
// per-asset publisher — on the glob path each DiscoveredAsset.publisher is
|
|
73
|
+
// the bare first path segment (`scripts`/`skills`/…), which would clobber
|
|
74
|
+
// the intended org/`--publisher` default with junk per-asset overrides.
|
|
75
|
+
inheritPublisher: true
|
|
76
|
+
});
|
|
77
|
+
if (!written.wrote) {
|
|
78
|
+
logWarn(
|
|
79
|
+
`Curated manifest already exists at ${S.dim(target)} \u2014 not overwritten. Pass --force to regenerate.`
|
|
80
|
+
);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
logOk(`Wrote curated manifest with ${written.assetCount} assets to ${S.dim(target)}.`);
|
|
84
|
+
logInfo(
|
|
85
|
+
`Edit it with \`skaile source manifest edit ${arg}\`, then \`skaile source add ${arg}\` to consume it.`
|
|
86
|
+
);
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
logWarn(`${errors.length} discovery error(s) \u2014 inspect the manifest before consuming.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function discoverCuratableAssets(dest, includeDev) {
|
|
92
|
+
const result = discoverAssetsInTree(dest, {
|
|
93
|
+
registry: createFullRegistry(),
|
|
94
|
+
includeDev
|
|
95
|
+
});
|
|
96
|
+
const assets = result.assets.filter((a) => !isTestFixtureAsset(a.relativePath));
|
|
97
|
+
return { assets, errors: result.errors.map((e) => `${e.path}: ${e.error}`) };
|
|
98
|
+
}
|
|
99
|
+
function pullCloneFfOnly(dest) {
|
|
100
|
+
const res = spawnSync("git", ["-C", dest, "pull", "--ff-only"], { stdio: "pipe" });
|
|
101
|
+
if (res.status !== 0) {
|
|
102
|
+
logWarn(
|
|
103
|
+
"git pull --ff-only failed \u2014 continuing with the existing clone (it may be behind upstream)."
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function entryRootKey(entry) {
|
|
108
|
+
if (entry.root) return entry.root.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
109
|
+
const files = entry.files;
|
|
110
|
+
if (files && files.length > 0) {
|
|
111
|
+
const first = typeof files[0] === "string" ? files[0] : files[0].path;
|
|
112
|
+
const dir = path.posix.dirname(first.replace(/\\/g, "/"));
|
|
113
|
+
return dir === "." ? entry.name : dir;
|
|
114
|
+
}
|
|
115
|
+
return entry.name;
|
|
116
|
+
}
|
|
117
|
+
function discoveredRootKey(relativePath) {
|
|
118
|
+
const dir = path.posix.dirname(relativePath.replace(/\\/g, "/"));
|
|
119
|
+
return dir === "." ? path.posix.basename(relativePath.replace(/\\/g, "/")) : dir;
|
|
120
|
+
}
|
|
121
|
+
function entryRef(entry) {
|
|
122
|
+
return `${entry.kind}:${entry.name}`;
|
|
123
|
+
}
|
|
124
|
+
async function runRebuild(arg, opts) {
|
|
125
|
+
const projectDir = path.resolve(opts.projectDir);
|
|
126
|
+
const { url, dest, slug, manifestFile: target } = resolveSourceArg(arg, projectDir);
|
|
127
|
+
if (!existsSync(target)) {
|
|
128
|
+
logErr(
|
|
129
|
+
`No curated manifest for "${slug}" at ${S.dim(target)} \u2014 run \`skaile source manifest init\` first.`
|
|
130
|
+
);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const entry = { url };
|
|
134
|
+
const cloned = ensureClone(entry);
|
|
135
|
+
if (!cloned) pullCloneFfOnly(dest);
|
|
136
|
+
const { assets: freshAssets, errors } = discoverCuratableAssets(dest, opts.dev ?? false);
|
|
137
|
+
if (opts.overwrite) {
|
|
138
|
+
logWarn(
|
|
139
|
+
"--overwrite: replacing the curated manifest with a full fresh inventory. All hand-curation (dropped assets, per-asset edits) is discarded."
|
|
140
|
+
);
|
|
141
|
+
const publisher = opts.publisher ?? resolveSourcePublisher(dest) ?? sourceShortName(url);
|
|
142
|
+
const written = writeCuratedManifest({
|
|
143
|
+
manifestFile: target,
|
|
144
|
+
publisher,
|
|
145
|
+
assets: freshAssets,
|
|
146
|
+
force: true,
|
|
147
|
+
inheritPublisher: true
|
|
148
|
+
});
|
|
149
|
+
logOk(
|
|
150
|
+
`Rebuilt curated manifest with ${written.assetCount} assets to ${S.dim(target)} (overwrite).`
|
|
151
|
+
);
|
|
152
|
+
if (errors.length > 0) {
|
|
153
|
+
logWarn(`${errors.length} discovery error(s) \u2014 inspect the manifest before consuming.`);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (opts.publisher) {
|
|
158
|
+
logWarn(
|
|
159
|
+
"--publisher is ignored in merge mode (the curated publisher is preserved). Use --overwrite to re-derive the publisher from a full re-scan."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const { manifest: existing } = decodeSkaileManifest(readFileSync(target, "utf-8"));
|
|
163
|
+
const existingEntries = existing.assets ?? [];
|
|
164
|
+
const freshByRoot = /* @__PURE__ */ new Map();
|
|
165
|
+
for (const a of freshAssets) freshByRoot.set(discoveredRootKey(a.relativePath), a);
|
|
166
|
+
const kept = [];
|
|
167
|
+
const dropped = [];
|
|
168
|
+
for (const e of existingEntries) {
|
|
169
|
+
if (freshByRoot.has(entryRootKey(e))) kept.push(e);
|
|
170
|
+
else dropped.push(entryRef(e));
|
|
171
|
+
}
|
|
172
|
+
const existingRoots = new Set(existingEntries.map(entryRootKey));
|
|
173
|
+
const newlyAvailable = [];
|
|
174
|
+
for (const a of freshAssets) {
|
|
175
|
+
if (!existingRoots.has(discoveredRootKey(a.relativePath))) {
|
|
176
|
+
newlyAvailable.push(`${a.kind}:${a.name}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const changed = dropped.length > 0;
|
|
180
|
+
if (dropped.length > 0) {
|
|
181
|
+
logWarn(`removed (gone upstream): ${dropped.sort().join(", ")}`);
|
|
182
|
+
}
|
|
183
|
+
if (newlyAvailable.length > 0) {
|
|
184
|
+
logInfo(
|
|
185
|
+
`${newlyAvailable.length} new upstream asset(s) available (not added): ${newlyAvailable.sort().join(", ")} \u2014 add with \`skaile source manifest edit ${arg}\` if wanted`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (!changed) {
|
|
189
|
+
logOk("already up to date");
|
|
190
|
+
if (errors.length > 0) {
|
|
191
|
+
logWarn(`${errors.length} discovery error(s) \u2014 inspect the source before re-consuming.`);
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const merged = { ...existing, assets: kept };
|
|
196
|
+
writeFileSync(target, encodeSkaileManifest(merged), "utf-8");
|
|
197
|
+
logOk(
|
|
198
|
+
`Rebuilt curated manifest: kept ${kept.length}, dropped ${dropped.length} \u2192 ${S.dim(target)}.`
|
|
199
|
+
);
|
|
200
|
+
if (errors.length > 0) {
|
|
201
|
+
logWarn(`${errors.length} discovery error(s) \u2014 inspect the source before re-consuming.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function runVerify(arg, opts) {
|
|
205
|
+
const projectDir = path.resolve(opts.projectDir);
|
|
206
|
+
const { url, dest, slug, manifestFile: target } = resolveSourceArg(arg, projectDir);
|
|
207
|
+
if (!existsSync(target)) {
|
|
208
|
+
logErr(
|
|
209
|
+
`No curated manifest for "${slug}" at ${S.dim(target)} \u2014 run \`skaile source manifest init\` first.`
|
|
210
|
+
);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
ensureClone({ url });
|
|
214
|
+
const text = readFileSync(target, "utf-8");
|
|
215
|
+
const { manifest, diagnostics } = decodeSkaileManifest(text);
|
|
216
|
+
const errors = [];
|
|
217
|
+
const warnings = [];
|
|
218
|
+
for (const d of diagnostics) {
|
|
219
|
+
const line = `${d.code}: ${d.message}`;
|
|
220
|
+
if (d.severity === "error") errors.push(line);
|
|
221
|
+
else warnings.push(line);
|
|
222
|
+
}
|
|
223
|
+
const assets = manifest.assets ?? [];
|
|
224
|
+
for (const entry of assets) {
|
|
225
|
+
const ref = entryRef(entry);
|
|
226
|
+
const relPaths = [];
|
|
227
|
+
if (entry.root) relPaths.push(entry.root);
|
|
228
|
+
if (entry.files) {
|
|
229
|
+
for (const f of entry.files) relPaths.push(typeof f === "string" ? f : f.path);
|
|
230
|
+
}
|
|
231
|
+
if (relPaths.length === 0) relPaths.push(entry.name);
|
|
232
|
+
for (const rel of relPaths) {
|
|
233
|
+
if (!existsSync(path.join(dest, rel))) {
|
|
234
|
+
errors.push(`${ref}: path not found upstream: ${rel}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (entry.sha256) {
|
|
238
|
+
if (entry.files && entry.files.length > 0) {
|
|
239
|
+
const hashFiles = entry.files.map(
|
|
240
|
+
(f) => (typeof f === "string" ? f : f.path).replace(/\\/g, "/")
|
|
241
|
+
);
|
|
242
|
+
const actual = computeDeterministicHash(dest, hashFiles);
|
|
243
|
+
if (actual !== entry.sha256) {
|
|
244
|
+
errors.push(
|
|
245
|
+
`${ref}: sha256 mismatch (declared ${entry.sha256.slice(0, 12)}\u2026, got ${actual.slice(0, 12)}\u2026)`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
warnings.push(
|
|
250
|
+
`${ref}: declared sha256 on a root:-mode entry is not verifiable (discovery hashes only the per-kind filtered set) \u2014 skipped`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
for (const dep of entry.dependencies ?? []) {
|
|
255
|
+
try {
|
|
256
|
+
parseAssetRef(dep);
|
|
257
|
+
} catch (err) {
|
|
258
|
+
warnings.push(
|
|
259
|
+
`${ref}: malformed dependency "${dep}": ${err instanceof Error ? err.message : String(err)}`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
for (const w of warnings) logWarn(w);
|
|
265
|
+
for (const e of errors) logErr(e);
|
|
266
|
+
if (errors.length === 0) {
|
|
267
|
+
logOk(`verified ${assets.length} asset(s).`);
|
|
268
|
+
} else {
|
|
269
|
+
logErr(`${errors.length} issue(s).`);
|
|
270
|
+
}
|
|
271
|
+
const failStrict = opts.strict && warnings.length > 0;
|
|
272
|
+
if (errors.length > 0 || failStrict) process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
function resolveEditorCommand(editorEnv, target) {
|
|
275
|
+
const parts = (editorEnv ?? "").trim().split(/\s+/).filter(Boolean);
|
|
276
|
+
const [command, ...rest] = parts.length > 0 ? parts : ["vi"];
|
|
277
|
+
return { command, args: [...rest, target] };
|
|
278
|
+
}
|
|
279
|
+
function runEdit(arg, opts, spawn = spawnSync) {
|
|
280
|
+
const projectDir = path.resolve(opts.projectDir);
|
|
281
|
+
const { slug, manifestFile: target } = resolveSourceArg(arg, projectDir);
|
|
282
|
+
if (!existsSync(target)) {
|
|
283
|
+
logErr(
|
|
284
|
+
`No curated manifest for "${slug}" at ${target}. Run \`skaile source manifest init\` first.`
|
|
285
|
+
);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const { command, args } = resolveEditorCommand(process.env.VISUAL || process.env.EDITOR, target);
|
|
289
|
+
const res = spawn(command, args, { stdio: "inherit" });
|
|
290
|
+
if (res.status !== 0 && res.status !== null) {
|
|
291
|
+
logErr(`Editor exited with status ${res.status}.`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
const { diagnostics } = decodeSkaileManifest(readFileSync(target, "utf-8"));
|
|
295
|
+
const errors = diagnostics.filter((d) => d.severity === "error");
|
|
296
|
+
const warnings = diagnostics.filter((d) => d.severity !== "error");
|
|
297
|
+
if (errors.length > 0) {
|
|
298
|
+
for (const d of errors) logErr(`${d.code}: ${d.message}`);
|
|
299
|
+
logWarn("Manifest has errors \u2014 fix them before `skaile source add` will consume it.");
|
|
300
|
+
} else if (warnings.length > 0) {
|
|
301
|
+
for (const d of warnings) logWarn(`${d.code}: ${d.message}`);
|
|
302
|
+
logOk("Saved (with warnings).");
|
|
303
|
+
} else {
|
|
304
|
+
logOk("Saved.");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function addSourceManifestCommands(src) {
|
|
308
|
+
const manifest = src.command("manifest").description("Curated manifest lifecycle for a source").enablePositionalOptions();
|
|
309
|
+
manifest.command("init <src>").description("Generate a curated manifest for a source (stored in the local store catalog)").option("-C, --project-dir <dir>", "Project directory", process.cwd()).option("--publisher <name>", "Override the publisher (default: source's GitHub org)").option("--dev", "Include assets under the source's dev_paths", false).option("--force", "Overwrite an existing curated manifest", false).action((arg, opts) => runInit(arg, opts));
|
|
310
|
+
manifest.command("edit <src>").description("Open the curated manifest in $EDITOR and re-validate on save").option("-C, --project-dir <dir>", "Project directory", process.cwd()).action((arg, opts) => runEdit(arg, opts));
|
|
311
|
+
manifest.command("rebuild <src>").description(
|
|
312
|
+
"Refresh a curated manifest after an upstream change (merge by default: keeps curation, drops assets gone upstream, reports new ones without adding)"
|
|
313
|
+
).option("-C, --project-dir <dir>", "Project directory", process.cwd()).option("--publisher <name>", "Override the publisher (--overwrite only)").option("--dev", "Include assets under the source's dev_paths", false).option("--overwrite", "Discard curation and re-scan the full inventory", false).action((arg, opts) => runRebuild(arg, opts));
|
|
314
|
+
manifest.command("verify <src>").description("Verify hashes, deps, and schema (read-only)").option("-C, --project-dir <dir>", "Project directory", process.cwd()).option("--strict", "Fail on advisory checks (warnings force a nonzero exit)", false).action((arg, opts) => runVerify(arg, opts));
|
|
315
|
+
manifest.command("publish <src>").description("Push the curated sidecar manifest and publish it to the store").option("-C, --project-dir <dir>", "Project directory", process.cwd()).option("--as <namespace>", "Publisher namespace (default: your store username)").option("--sidecar-repo <url>", "Public GitHub repo you own to hold the curated manifest").option("--ref <gitref>", "Foreign source ref to ingest (branch/sha; default: main)").option("--dry-run", "Print the publish plan without pushing or calling the store", false).action(async (arg, opts) => {
|
|
316
|
+
const { runPublish } = await import('./source-manifest-publish-TGVZ6KMC.js');
|
|
317
|
+
await runPublish(arg, opts);
|
|
318
|
+
});
|
|
319
|
+
manifest.command("propose <src>").description("Open a PR to upstream with the sidecar manifest").action(() => notWired("propose"));
|
|
320
|
+
manifest.command("yank <src>").description("Retract the source's published assets from the store").option("-C, --project-dir <dir>", "Project directory", process.cwd()).option("--asset <ref>", "Yank only the asset matching this ref/name").action(async (arg, opts) => {
|
|
321
|
+
const { runYank } = await import('./source-manifest-publish-TGVZ6KMC.js');
|
|
322
|
+
await runYank(arg, opts);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// cli/src/commands/source.ts
|
|
327
|
+
function sourceClonePath(key) {
|
|
328
|
+
return path.join(getGlobalCacheDir(), key);
|
|
329
|
+
}
|
|
330
|
+
var SKAILE_MANIFEST_FILENAME = "skaile.manifest.yaml";
|
|
331
|
+
var LEGACY_SOURCE_CONFIG_FILENAME = ".skaile-source.yaml";
|
|
332
|
+
function indexingHadFailures(result) {
|
|
333
|
+
return result.errors.length > 0;
|
|
334
|
+
}
|
|
335
|
+
function legacySourceConfigHint(dest) {
|
|
336
|
+
const hasLegacy = existsSync(path.join(dest, LEGACY_SOURCE_CONFIG_FILENAME));
|
|
337
|
+
const hasManifest = existsSync(path.join(dest, SKAILE_MANIFEST_FILENAME));
|
|
338
|
+
if (hasLegacy && !hasManifest) {
|
|
339
|
+
return `This source ships a legacy ${LEGACY_SOURCE_CONFIG_FILENAME} but no ${SKAILE_MANIFEST_FILENAME}; its declared publisher is ignored and assets are published under their first path segment. Add a ${SKAILE_MANIFEST_FILENAME} (\`skaile source manifest init\`) to set the publisher and asset inventory.`;
|
|
340
|
+
}
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
function normalizeSourceUrl(input) {
|
|
344
|
+
const s = input.trim();
|
|
345
|
+
if (/^(?:https?|git|ssh|file):\/\//.test(s) || /^git@/.test(s)) return s;
|
|
346
|
+
const repo = s.replace(/\.git$/, "");
|
|
347
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(repo)) return `git@github.com:${repo}.git`;
|
|
348
|
+
if (/^[\w.-]+$/.test(repo)) return `git@github.com:skaile-ai/${repo}.git`;
|
|
349
|
+
return s;
|
|
350
|
+
}
|
|
351
|
+
function sourceMatchesKey(entry, key) {
|
|
352
|
+
if (entry.url === key) return true;
|
|
353
|
+
if (sourceCacheKey(entry.url) === key || sourceShortName(entry.url) === key) return true;
|
|
354
|
+
return entry.url === normalizeSourceUrl(key);
|
|
355
|
+
}
|
|
356
|
+
function findCacheKeyCollision(sources, url) {
|
|
357
|
+
const key = sourceCacheKey(url);
|
|
358
|
+
return sources.find((s) => s.url !== url && sourceCacheKey(s.url) === key);
|
|
359
|
+
}
|
|
360
|
+
function requireProjectYamlPath() {
|
|
361
|
+
const root = findWorkspaceRoot(process.cwd());
|
|
362
|
+
if (!root) {
|
|
363
|
+
logErr(
|
|
364
|
+
"No skaile.yaml found in the current directory or any parent. Run `skaile init` first."
|
|
365
|
+
);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
return path.join(root, workspaceConfigFilename());
|
|
369
|
+
}
|
|
370
|
+
function readProjectSources(yamlPath) {
|
|
371
|
+
if (!existsSync(yamlPath)) return [];
|
|
372
|
+
try {
|
|
373
|
+
return WorkspaceYamlEditor.load(yamlPath).getSources();
|
|
374
|
+
} catch (err) {
|
|
375
|
+
logErr(`Could not read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function ensureClone(entry, opts = {}) {
|
|
380
|
+
const dest = sourceClonePath(sourceCacheKey(entry.url));
|
|
381
|
+
let backup;
|
|
382
|
+
if (existsSync(dest)) {
|
|
383
|
+
if (!opts.force) return false;
|
|
384
|
+
backup = `${dest}.bak`;
|
|
385
|
+
rmSync(backup, { recursive: true, force: true });
|
|
386
|
+
renameSync(dest, backup);
|
|
387
|
+
}
|
|
388
|
+
mkdirSync(path.dirname(dest), { recursive: true });
|
|
389
|
+
const args = ["clone", entry.url, dest];
|
|
390
|
+
if (entry.pin) args.splice(1, 0, "--branch", entry.pin);
|
|
391
|
+
const clone = spawnSync("git", args, { stdio: "inherit" });
|
|
392
|
+
if (clone.status !== 0) {
|
|
393
|
+
if (backup) {
|
|
394
|
+
rmSync(dest, { recursive: true, force: true });
|
|
395
|
+
renameSync(backup, dest);
|
|
396
|
+
}
|
|
397
|
+
logErr(`git clone failed for ${entry.url}`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
if (backup) rmSync(backup, { recursive: true, force: true });
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
async function refreshManifestCache(entry, includeDev = false) {
|
|
404
|
+
const { manager, library, close } = await openLibraryManager();
|
|
405
|
+
try {
|
|
406
|
+
const name = sourceShortName(entry.url);
|
|
407
|
+
const dest = sourceClonePath(sourceCacheKey(entry.url));
|
|
408
|
+
const existing = (await manager.listLibraries()).find((l) => l.name === name);
|
|
409
|
+
const lib = existing ?? await manager.addLibrary({
|
|
410
|
+
name,
|
|
411
|
+
path: dest,
|
|
412
|
+
backend: "git",
|
|
413
|
+
backendConfig: { url: entry.url, branch: entry.pin ?? "main", authHint: "ssh" },
|
|
414
|
+
ownership: "reader"
|
|
415
|
+
});
|
|
416
|
+
const overlayFile = path.join(getStoreManifestsDir(), `${storeManifestSlug(entry.url)}.yaml`);
|
|
417
|
+
const catalog = new LocalCatalogSource(library, lib.id, dest, createFullRegistry(), {
|
|
418
|
+
sidecarManifestFile: existsSync(overlayFile) ? overlayFile : void 0
|
|
419
|
+
});
|
|
420
|
+
const result = await catalog.sync({ includeDev });
|
|
421
|
+
await library.syncSource(lib.id);
|
|
422
|
+
return result;
|
|
423
|
+
} finally {
|
|
424
|
+
close();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
async function autoSyncForSearch(projectDir) {
|
|
428
|
+
const root = findWorkspaceRoot(projectDir);
|
|
429
|
+
if (!root) return false;
|
|
430
|
+
if (readProjectSources(path.join(root, workspaceConfigFilename())).length === 0) return false;
|
|
431
|
+
logInfo("sources not synced \u2014 syncing now\u2026");
|
|
432
|
+
const { synced } = await syncProjectSources(projectDir);
|
|
433
|
+
return synced > 0;
|
|
434
|
+
}
|
|
435
|
+
async function syncProjectSources(projectDir, opts = {}) {
|
|
436
|
+
const root = findWorkspaceRoot(projectDir) ?? projectDir;
|
|
437
|
+
const sources = readProjectSources(path.join(root, workspaceConfigFilename()));
|
|
438
|
+
let synced = 0;
|
|
439
|
+
let failed = 0;
|
|
440
|
+
for (const entry of sources) {
|
|
441
|
+
const slug = sourceShortName(entry.url);
|
|
442
|
+
const dest = sourceClonePath(sourceCacheKey(entry.url));
|
|
443
|
+
try {
|
|
444
|
+
if (!existsSync(dest)) {
|
|
445
|
+
mkdirSync(path.dirname(dest), { recursive: true });
|
|
446
|
+
const args = ["clone", entry.url, dest];
|
|
447
|
+
if (entry.pin) args.splice(1, 0, "--branch", entry.pin);
|
|
448
|
+
const clone = spawnSync("git", args, { stdio: "ignore" });
|
|
449
|
+
if (clone.status !== 0) {
|
|
450
|
+
logWarn(`${slug}: git clone failed, skipping.`);
|
|
451
|
+
failed++;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
const pull = spawnSync("git", ["pull", "--ff-only"], { cwd: dest, stdio: "ignore" });
|
|
456
|
+
if (pull.status !== 0) {
|
|
457
|
+
logWarn(`${slug}: git pull failed, skipping manifest refresh.`);
|
|
458
|
+
failed++;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const result = await refreshManifestCache(entry, opts.dev);
|
|
463
|
+
for (const err of result.errors) logWarn(`${slug}: ${err}`);
|
|
464
|
+
for (const advisory of result.advisories) logInfo(`${slug}: ${advisory}`);
|
|
465
|
+
synced++;
|
|
466
|
+
} catch (err) {
|
|
467
|
+
logWarn(`${slug}: sync failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
468
|
+
failed++;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return { total: sources.length, synced, failed };
|
|
472
|
+
}
|
|
473
|
+
async function dropManifestCache(url, purgeClone) {
|
|
474
|
+
const name = sourceShortName(url);
|
|
475
|
+
const { manager, close } = await openLibraryManager();
|
|
476
|
+
try {
|
|
477
|
+
const existing = (await manager.listLibraries()).find((l) => l.name === name);
|
|
478
|
+
if (existing) {
|
|
479
|
+
await manager.removeLibrary(name, { purge: false });
|
|
480
|
+
}
|
|
481
|
+
} finally {
|
|
482
|
+
close();
|
|
483
|
+
}
|
|
484
|
+
if (purgeClone) {
|
|
485
|
+
const dest = sourceClonePath(sourceCacheKey(url));
|
|
486
|
+
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function makeSourceCommand() {
|
|
490
|
+
const cmd = new Command("source").description(
|
|
491
|
+
"Manage github sources for the current project (recorded in skaile.yaml)"
|
|
492
|
+
);
|
|
493
|
+
cmd.command("add <source>").description(
|
|
494
|
+
"Clone a github repo and register it in this project's skaile.yaml. <source> may be a full git URL, an owner/repo shorthand, or a bare name (\u2192 git@github.com:skaile-ai/<name>.git)."
|
|
495
|
+
).option("--name <slug>", "[deprecated] no-op \u2014 the source entry stores only the URL").option("--pin <ref>", "Pin a branch, tag, or 40-char SHA").option("--branch <branch>", "[deprecated] alias for --pin").option("--force", "Remove an existing clone and re-clone", false).option("--dev", "Also index dev-only assets under the source's dev_paths", false).action(
|
|
496
|
+
async (source, opts) => {
|
|
497
|
+
const url = normalizeSourceUrl(source);
|
|
498
|
+
const yamlPath = requireProjectYamlPath();
|
|
499
|
+
const collision = findCacheKeyCollision(readProjectSources(yamlPath), url);
|
|
500
|
+
if (collision) {
|
|
501
|
+
logErr(
|
|
502
|
+
`"${url}" maps to the same cache key (${sourceCacheKey(url)}) as already-registered "${collision.url}". Remove that source first (skaile source remove) or re-use its exact URL to update it.`
|
|
503
|
+
);
|
|
504
|
+
process.exitCode = 1;
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (opts.name) {
|
|
508
|
+
logWarn(
|
|
509
|
+
"the --name flag is deprecated; the source entry now stores only the URL (the cache slug is derived from it)."
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
const pin = opts.pin ?? opts.branch;
|
|
513
|
+
const slug = sourceShortName(url);
|
|
514
|
+
const entry = { url };
|
|
515
|
+
if (pin) entry.pin = pin;
|
|
516
|
+
const dest = sourceClonePath(sourceCacheKey(url));
|
|
517
|
+
const preexisting = existsSync(dest);
|
|
518
|
+
const cloned = ensureClone(entry, { force: opts.force });
|
|
519
|
+
if (!cloned) {
|
|
520
|
+
logOk(`Reusing cached clone at ${dest}`);
|
|
521
|
+
} else if (preexisting) {
|
|
522
|
+
logOk(`Re-cloned ${url} \u2192 ${dest}`);
|
|
523
|
+
} else {
|
|
524
|
+
logOk(`Cloned ${url} \u2192 ${dest}`);
|
|
525
|
+
}
|
|
526
|
+
const editor = WorkspaceYamlEditor.load(yamlPath);
|
|
527
|
+
editor.setSource(entry);
|
|
528
|
+
editor.save();
|
|
529
|
+
const result = await refreshManifestCache(entry, opts.dev);
|
|
530
|
+
const failed = indexingHadFailures(result);
|
|
531
|
+
const indexedMsg = `Indexed ${result.assetsUpdated} of ${result.assetsFound} asset(s) from "${slug}".`;
|
|
532
|
+
if (failed) {
|
|
533
|
+
logWarn(indexedMsg);
|
|
534
|
+
} else {
|
|
535
|
+
logOk(indexedMsg);
|
|
536
|
+
}
|
|
537
|
+
for (const err of result.errors) logWarn(err);
|
|
538
|
+
for (const advisory of result.advisories) logInfo(advisory);
|
|
539
|
+
const legacyHint = legacySourceConfigHint(dest);
|
|
540
|
+
if (legacyHint) logWarn(legacyHint);
|
|
541
|
+
const where = path.relative(process.cwd(), yamlPath);
|
|
542
|
+
if (failed) {
|
|
543
|
+
logWarn(
|
|
544
|
+
`Source "${slug}" registered in ${where}, but ${result.errors.length} asset(s) failed to index (see above). Fix the cause, then re-run \`skaile source sync ${slug}\`.`
|
|
545
|
+
);
|
|
546
|
+
process.exitCode = 1;
|
|
547
|
+
} else {
|
|
548
|
+
logOk(`Source "${slug}" registered in ${where}.`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
cmd.command("list").description("List sources registered in this project's skaile.yaml").option("--json", "Output as JSON").action(async (opts) => {
|
|
553
|
+
const yamlPath = requireProjectYamlPath();
|
|
554
|
+
const sources = readProjectSources(yamlPath);
|
|
555
|
+
if (opts.json) return console.log(JSON.stringify(sources, null, 2));
|
|
556
|
+
if (sources.length === 0) {
|
|
557
|
+
logInfo("No sources in skaile.yaml. Run `skaile source add <git-url>` to register one.");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
console.log();
|
|
561
|
+
console.log(S.heading(" Sources"));
|
|
562
|
+
for (const s of sources) {
|
|
563
|
+
const slug = sourceShortName(s.url);
|
|
564
|
+
const cached = existsSync(sourceClonePath(sourceCacheKey(s.url))) ? "" : S.warn(" (not cached)");
|
|
565
|
+
console.log(` ${S.cmd(slug.padEnd(24))} ${S.dim(s.url)}${cached}`);
|
|
566
|
+
}
|
|
567
|
+
console.log(`
|
|
568
|
+
${S.dim(`${sources.length} source(s)`)}
|
|
569
|
+
`);
|
|
570
|
+
});
|
|
571
|
+
cmd.command("show <name-or-url>").description("Show details for a source (matched by URL or derived slug)").action(async (key) => {
|
|
572
|
+
const yamlPath = requireProjectYamlPath();
|
|
573
|
+
const entry = readProjectSources(yamlPath).find((s) => sourceMatchesKey(s, key));
|
|
574
|
+
if (!entry) {
|
|
575
|
+
logErr(`No source matching "${key}" in ${yamlPath}.`);
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
const slug = sourceShortName(entry.url);
|
|
579
|
+
const clonePath = sourceClonePath(sourceCacheKey(entry.url));
|
|
580
|
+
console.log(
|
|
581
|
+
JSON.stringify(
|
|
582
|
+
{
|
|
583
|
+
...entry,
|
|
584
|
+
slug,
|
|
585
|
+
clonePath,
|
|
586
|
+
cached: existsSync(clonePath)
|
|
587
|
+
},
|
|
588
|
+
null,
|
|
589
|
+
2
|
|
590
|
+
)
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
cmd.command("remove <name-or-url>").description("Remove a source from this project's skaile.yaml (matched by URL or slug)").option("--purge", "Also delete the cached clone from ~/.skaile/cache/sources/", false).action(async (key, opts) => {
|
|
594
|
+
const yamlPath = requireProjectYamlPath();
|
|
595
|
+
const editor = WorkspaceYamlEditor.load(yamlPath);
|
|
596
|
+
const match = editor.getSources().find((s) => sourceMatchesKey(s, key));
|
|
597
|
+
if (!match) {
|
|
598
|
+
logErr(`No source matching "${key}" in ${yamlPath}.`);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
const slug = sourceShortName(match.url);
|
|
602
|
+
editor.removeSource(match.url);
|
|
603
|
+
editor.save();
|
|
604
|
+
await dropManifestCache(match.url, opts.purge);
|
|
605
|
+
logOk(
|
|
606
|
+
opts.purge ? `Removed source "${slug}" and purged its clone.` : `Removed source "${slug}" from skaile.yaml (clone left in ~/.skaile/cache/sources/ \u2014 pass --purge to remove it, or run \`skaile cache clean\`).`
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
cmd.command("sync [name]").description("Pull upstream changes and refresh the manifest cache").option("--dev", "Also index dev-only assets under each source's dev_paths", false).option("--all", "Sync every source without prompting", false).action(async (key, opts) => {
|
|
610
|
+
const yamlPath = requireProjectYamlPath();
|
|
611
|
+
const sources = readProjectSources(yamlPath);
|
|
612
|
+
if (sources.length === 0) {
|
|
613
|
+
logInfo("No sources in skaile.yaml.");
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
let targets;
|
|
617
|
+
if (key) {
|
|
618
|
+
targets = sources.filter((s) => sourceMatchesKey(s, key));
|
|
619
|
+
if (targets.length === 0) {
|
|
620
|
+
logInfo(`No source matching "${key}".`);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
} else if (opts.all || sources.length === 1 || !process.stdin.isTTY) {
|
|
624
|
+
targets = sources;
|
|
625
|
+
} else {
|
|
626
|
+
const selected = await p.multiselect({
|
|
627
|
+
message: "Select sources to sync",
|
|
628
|
+
options: sources.map((s) => ({
|
|
629
|
+
value: s.url,
|
|
630
|
+
label: sourceShortName(s.url),
|
|
631
|
+
hint: s.url
|
|
632
|
+
})),
|
|
633
|
+
required: true
|
|
634
|
+
});
|
|
635
|
+
if (p.isCancel(selected)) {
|
|
636
|
+
p.cancel("Cancelled.");
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const picked = new Set(selected);
|
|
640
|
+
targets = sources.filter((s) => picked.has(s.url));
|
|
641
|
+
}
|
|
642
|
+
for (const entry of targets) {
|
|
643
|
+
const slug = sourceShortName(entry.url);
|
|
644
|
+
const dest = sourceClonePath(sourceCacheKey(entry.url));
|
|
645
|
+
if (!existsSync(dest)) {
|
|
646
|
+
ensureClone(entry);
|
|
647
|
+
logOk(`${slug}: cloned to ${dest}.`);
|
|
648
|
+
} else {
|
|
649
|
+
const pull = spawnSync("git", ["pull", "--ff-only"], { cwd: dest, stdio: "inherit" });
|
|
650
|
+
if (pull.status !== 0) {
|
|
651
|
+
logWarn(`${slug}: git pull failed, skipping manifest refresh.`);
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const result = await refreshManifestCache(entry, opts.dev);
|
|
656
|
+
const failed = indexingHadFailures(result);
|
|
657
|
+
const indexedMsg = `${slug}: indexed ${result.assetsUpdated} of ${result.assetsFound} asset(s).`;
|
|
658
|
+
if (failed) {
|
|
659
|
+
logWarn(indexedMsg);
|
|
660
|
+
process.exitCode = 1;
|
|
661
|
+
} else {
|
|
662
|
+
logOk(indexedMsg);
|
|
663
|
+
}
|
|
664
|
+
for (const err of result.errors) logWarn(`${slug}: ${err}`);
|
|
665
|
+
for (const advisory of result.advisories) logInfo(`${slug}: ${advisory}`);
|
|
666
|
+
const legacyHint = legacySourceConfigHint(dest);
|
|
667
|
+
if (legacyHint) logWarn(`${slug}: ${legacyHint}`);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
cmd.command("patch <ref>").description("Extract an asset for editing against the source").action((_ref) => {
|
|
671
|
+
logErr(
|
|
672
|
+
"`skaile source patch` is scaffolded but not yet wired. The patch flow lands in a follow-up PR."
|
|
673
|
+
);
|
|
674
|
+
process.exit(2);
|
|
675
|
+
});
|
|
676
|
+
cmd.command("propose <ref>").description("Open a PR with the patch against the source upstream").action((_ref) => {
|
|
677
|
+
logErr(
|
|
678
|
+
"`skaile source propose` is scaffolded but not yet wired. The propose flow lands in a follow-up PR."
|
|
679
|
+
);
|
|
680
|
+
process.exit(2);
|
|
681
|
+
});
|
|
682
|
+
addSourceManifestCommands(cmd);
|
|
683
|
+
return cmd;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export { autoSyncForSearch, ensureClone, findCacheKeyCollision, indexingHadFailures, legacySourceConfigHint, makeSourceCommand, normalizeSourceUrl, readProjectSources, resolveSourceArg, sourceClonePath, sourceMatchesKey, syncProjectSources };
|
|
687
|
+
//# sourceMappingURL=chunk-WYGXYGJH.js.map
|
|
688
|
+
//# sourceMappingURL=chunk-WYGXYGJH.js.map
|