@agent-loom/loom 1.0.1 → 1.0.3
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/README.md +69 -0
- package/dist/acp/client.d.ts +182 -0
- package/dist/acp/client.d.ts.map +1 -0
- package/dist/acp/client.js +432 -0
- package/dist/acp/client.js.map +1 -0
- package/dist/acp/index.d.ts +5 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +3 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/run.d.ts +41 -0
- package/dist/acp/run.d.ts.map +1 -0
- package/dist/acp/run.js +32 -0
- package/dist/acp/run.js.map +1 -0
- package/dist/apply.d.ts +17 -6
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js +85 -47
- package/dist/apply.js.map +1 -1
- package/dist/chat/chat.d.ts +108 -0
- package/dist/chat/chat.d.ts.map +1 -0
- package/dist/chat/chat.js +221 -0
- package/dist/chat/chat.js.map +1 -0
- package/dist/chat/discovery.d.ts +30 -0
- package/dist/chat/discovery.d.ts.map +1 -0
- package/dist/chat/discovery.js +68 -0
- package/dist/chat/discovery.js.map +1 -0
- package/dist/chat/frontmatter.d.ts +12 -0
- package/dist/chat/frontmatter.d.ts.map +1 -0
- package/dist/chat/frontmatter.js +11 -0
- package/dist/chat/frontmatter.js.map +1 -0
- package/dist/chat/index.d.ts +16 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +11 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/registry.d.ts +73 -0
- package/dist/chat/registry.d.ts.map +1 -0
- package/dist/chat/registry.js +118 -0
- package/dist/chat/registry.js.map +1 -0
- package/dist/chat/resolve-agent.d.ts +39 -0
- package/dist/chat/resolve-agent.d.ts.map +1 -0
- package/dist/chat/resolve-agent.js +36 -0
- package/dist/chat/resolve-agent.js.map +1 -0
- package/dist/chat/suggest.d.ts +20 -0
- package/dist/chat/suggest.d.ts.map +1 -0
- package/dist/chat/suggest.js +55 -0
- package/dist/chat/suggest.js.map +1 -0
- package/dist/cli.js +628 -75
- package/dist/cli.js.map +1 -1
- package/dist/clone.d.ts +21 -3
- package/dist/clone.d.ts.map +1 -1
- package/dist/clone.js +240 -12
- package/dist/clone.js.map +1 -1
- package/dist/copilot/mcp.d.ts +48 -0
- package/dist/copilot/mcp.d.ts.map +1 -0
- package/dist/copilot/mcp.js +146 -0
- package/dist/copilot/mcp.js.map +1 -0
- package/dist/copilot/resolve.d.ts +33 -0
- package/dist/copilot/resolve.d.ts.map +1 -0
- package/dist/copilot/resolve.js +96 -0
- package/dist/copilot/resolve.js.map +1 -0
- package/dist/copilot/spawn.d.ts +51 -0
- package/dist/copilot/spawn.d.ts.map +1 -0
- package/dist/copilot/spawn.js +132 -0
- package/dist/copilot/spawn.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/launch/index.d.ts +10 -0
- package/dist/launch/index.d.ts.map +1 -0
- package/dist/launch/index.js +9 -0
- package/dist/launch/index.js.map +1 -0
- package/dist/launch/stage.d.ts +62 -0
- package/dist/launch/stage.d.ts.map +1 -0
- package/dist/launch/stage.js +108 -0
- package/dist/launch/stage.js.map +1 -0
- package/dist/manifest.d.ts +165 -18
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +980 -225
- package/dist/manifest.js.map +1 -1
- package/dist/renderers/claude.d.ts +5 -0
- package/dist/renderers/claude.d.ts.map +1 -1
- package/dist/renderers/claude.js +17 -3
- package/dist/renderers/claude.js.map +1 -1
- package/dist/renderers/copilot.d.ts +1 -1
- package/dist/renderers/copilot.d.ts.map +1 -1
- package/dist/renderers/copilot.js +205 -22
- package/dist/renderers/copilot.js.map +1 -1
- package/dist/repo-clone.js +17 -11
- package/dist/repo-clone.js.map +1 -1
- package/dist/resolve-template.d.ts +12 -4
- package/dist/resolve-template.d.ts.map +1 -1
- package/dist/resolve-template.js +39 -8
- package/dist/resolve-template.js.map +1 -1
- package/dist/run/index.d.ts +4 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +2 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run/run.d.ts +143 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +406 -0
- package/dist/run/run.js.map +1 -0
- package/dist/search-registry.d.ts +10 -3
- package/dist/search-registry.d.ts.map +1 -1
- package/dist/search-registry.js +16 -16
- package/dist/search-registry.js.map +1 -1
- package/dist/sessions/index.d.ts +16 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +15 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/store.d.ts +56 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +220 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/sessions/types.d.ts +62 -0
- package/dist/sessions/types.d.ts.map +1 -0
- package/dist/sessions/types.js +5 -0
- package/dist/sessions/types.js.map +1 -0
- package/dist/skill-fetcher.d.ts.map +1 -1
- package/dist/skill-fetcher.js +5 -6
- package/dist/skill-fetcher.js.map +1 -1
- package/dist/types.d.ts +123 -41
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -1
- package/dist/util/binary-cache.d.ts +53 -0
- package/dist/util/binary-cache.d.ts.map +1 -0
- package/dist/util/binary-cache.js +211 -0
- package/dist/util/binary-cache.js.map +1 -0
- package/dist/util/frontmatter.d.ts +53 -0
- package/dist/util/frontmatter.d.ts.map +1 -0
- package/dist/util/frontmatter.js +85 -0
- package/dist/util/frontmatter.js.map +1 -0
- package/dist/util/loom-home.d.ts +19 -0
- package/dist/util/loom-home.d.ts.map +1 -0
- package/dist/util/loom-home.js +37 -0
- package/dist/util/loom-home.js.map +1 -0
- package/dist/util/workspace-folder.d.ts +29 -0
- package/dist/util/workspace-folder.d.ts.map +1 -0
- package/dist/util/workspace-folder.js +43 -0
- package/dist/util/workspace-folder.js.map +1 -0
- package/dist/validate.d.ts +7 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +90 -17
- package/dist/validate.js.map +1 -1
- package/package.json +31 -2
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA8CH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,CAAC;AACxE,CAAC;AAOD,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,CAAC;AACxE,CAAC;AAQD,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,UAAU,IAAI,KAAK,CAAC;AAC5E,CAAC;AAkBD,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,UAAU,IAAI,KAAK;QACnB,KAAK,CAAC,OAAO,CAAE,KAA+B,CAAC,QAAQ,CAAC,CACzD,CAAC;AACJ,CAAC;AA2BD,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,UAAU,IAAI,KAAK;QACnB,KAAK,CAAC,OAAO,CAAE,KAA+B,CAAC,QAAQ,CAAC,CACzD,CAAC;AACJ,CAAC;AA6DD,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary cache for MCP servers declared via dobby's `binary:` block.
|
|
3
|
+
*
|
|
4
|
+
* Lazy-downloads a release asset from a GitHub release into
|
|
5
|
+
* ~/.loom/mcp-binaries/<owner>_<repo>/<version>/<filename> on first use.
|
|
6
|
+
* Subsequent launches reuse the cached file.
|
|
7
|
+
*
|
|
8
|
+
* Resolves the {os}, {arch}, {ext} variables in the pattern using the
|
|
9
|
+
* modern Go-style convention that GitHub Action release uploaders typically
|
|
10
|
+
* use (windows / linux / darwin x amd64 / arm64). If a manifest in the
|
|
11
|
+
* wild uses a different convention, we try the alternatives via the
|
|
12
|
+
* release-asset listing before giving up.
|
|
13
|
+
*
|
|
14
|
+
* Auth: when the spec declares auth.type='github' we attach a Bearer
|
|
15
|
+
* token from `gh auth token`. Public release assets work without auth.
|
|
16
|
+
*
|
|
17
|
+
* Security: filename is validated as a basename only (alphanumerics,
|
|
18
|
+
* dot, underscore, hyphen). No directory separators, no shell metachars.
|
|
19
|
+
*/
|
|
20
|
+
export interface BinarySpec {
|
|
21
|
+
source: 'github';
|
|
22
|
+
repo: string;
|
|
23
|
+
path: string;
|
|
24
|
+
pattern: string;
|
|
25
|
+
version: string;
|
|
26
|
+
auth?: {
|
|
27
|
+
type: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface BinaryResolveResult {
|
|
31
|
+
/** Absolute path to the cached binary file. */
|
|
32
|
+
cachedPath: string;
|
|
33
|
+
/** Whether the binary was downloaded this call (vs already cached). */
|
|
34
|
+
downloaded: boolean;
|
|
35
|
+
/** The resolved asset name on the GitHub release. */
|
|
36
|
+
assetName: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a `binary:` spec to a local cached path, downloading from the
|
|
40
|
+
* GitHub release on cache miss. Returns undefined if resolution fails for
|
|
41
|
+
* any reason (validation, network, missing asset). Errors are logged
|
|
42
|
+
* via the `log` callback so the caller can surface them in stderr.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveBinaryToCache(binary: BinarySpec, log?: (msg: string) => void): Promise<BinaryResolveResult | undefined>;
|
|
45
|
+
/**
|
|
46
|
+
* Expand {os}, {arch}, {ext} in a release-asset pattern into a small set
|
|
47
|
+
* of likely candidates. The canonical convention (windows/linux/darwin x
|
|
48
|
+
* amd64/arm64) is tried first; we fall back to a couple of legacy aliases
|
|
49
|
+
* (win, macos, x64) so manifests authored against older binaries still
|
|
50
|
+
* resolve.
|
|
51
|
+
*/
|
|
52
|
+
export declare function expandPatternVariants(pattern: string): string[];
|
|
53
|
+
//# sourceMappingURL=binary-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-cache.d.ts","sourceRoot":"","sources":["../../src/util/binary-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,UAAU,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,UAAU,EAClB,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAwF1C;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA2B/D"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary cache for MCP servers declared via dobby's `binary:` block.
|
|
3
|
+
*
|
|
4
|
+
* Lazy-downloads a release asset from a GitHub release into
|
|
5
|
+
* ~/.loom/mcp-binaries/<owner>_<repo>/<version>/<filename> on first use.
|
|
6
|
+
* Subsequent launches reuse the cached file.
|
|
7
|
+
*
|
|
8
|
+
* Resolves the {os}, {arch}, {ext} variables in the pattern using the
|
|
9
|
+
* modern Go-style convention that GitHub Action release uploaders typically
|
|
10
|
+
* use (windows / linux / darwin x amd64 / arm64). If a manifest in the
|
|
11
|
+
* wild uses a different convention, we try the alternatives via the
|
|
12
|
+
* release-asset listing before giving up.
|
|
13
|
+
*
|
|
14
|
+
* Auth: when the spec declares auth.type='github' we attach a Bearer
|
|
15
|
+
* token from `gh auth token`. Public release assets work without auth.
|
|
16
|
+
*
|
|
17
|
+
* Security: filename is validated as a basename only (alphanumerics,
|
|
18
|
+
* dot, underscore, hyphen). No directory separators, no shell metachars.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, statSync } from 'node:fs';
|
|
21
|
+
import { writeFile, chmod } from 'node:fs/promises';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { platform } from 'node:os';
|
|
24
|
+
import { execSync } from 'node:child_process';
|
|
25
|
+
import { loomHomeDir } from './loom-home.js';
|
|
26
|
+
const FILENAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a `binary:` spec to a local cached path, downloading from the
|
|
29
|
+
* GitHub release on cache miss. Returns undefined if resolution fails for
|
|
30
|
+
* any reason (validation, network, missing asset). Errors are logged
|
|
31
|
+
* via the `log` callback so the caller can surface them in stderr.
|
|
32
|
+
*/
|
|
33
|
+
export async function resolveBinaryToCache(binary, log = () => { }) {
|
|
34
|
+
// Try the canonical Go-style convention first; fall back to legacy
|
|
35
|
+
// alternatives if the resolved filename doesn't exist as a release asset.
|
|
36
|
+
const candidates = expandPatternVariants(binary.pattern);
|
|
37
|
+
// Validate every candidate as a safe basename before attempting any
|
|
38
|
+
// network call. This closes the same shell-injection / path-traversal
|
|
39
|
+
// attack surface as the synchronous resolver.
|
|
40
|
+
for (const filename of candidates) {
|
|
41
|
+
if (!FILENAME_RE.test(filename)) {
|
|
42
|
+
log(` Binary ${binary.repo}: rejecting filename "${filename}" -- pattern must yield a basename (alphanumerics, dot, underscore, hyphen)`);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const repoSlug = binary.repo.replace(/[/\\]/g, '_');
|
|
47
|
+
const cacheDir = join(loomHomeDir(), 'mcp-binaries', repoSlug, binary.version);
|
|
48
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
49
|
+
// Cache hit: any candidate filename present on disk wins.
|
|
50
|
+
for (const filename of candidates) {
|
|
51
|
+
const cachedPath = join(cacheDir, filename);
|
|
52
|
+
if (existsSync(cachedPath) && statSync(cachedPath).size > 0) {
|
|
53
|
+
log(` Binary ${binary.repo}: cache hit ${cachedPath}`);
|
|
54
|
+
return { cachedPath, downloaded: false, assetName: filename };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Build candidate URLs. The `binary:` spec describes WHERE the asset lives;
|
|
58
|
+
// in the wild we see two conventions:
|
|
59
|
+
// 1. GitHub releases — typical for repos that ship via tagged releases.
|
|
60
|
+
// URL: https://github.com/<repo>/releases/download/v<version>/<filename>
|
|
61
|
+
// 2. Source-tree raw checkout — used when the repo checks binaries into
|
|
62
|
+
// a path like `tools/<name>/current/`. The `path:` field in the spec
|
|
63
|
+
// points at that directory; the file lives at
|
|
64
|
+
// https://raw.githubusercontent.com/<repo>/<ref>/<path>/<filename>
|
|
65
|
+
//
|
|
66
|
+
// We try both, with a few likely refs for the raw form. First successful
|
|
67
|
+
// download (200 status, non-HTML body, valid magic-bytes for .exe) wins.
|
|
68
|
+
const tag = binary.version.startsWith('v') ? binary.version : `v${binary.version}`;
|
|
69
|
+
const refsToTry = Array.from(new Set([
|
|
70
|
+
tag, // 'v1.0.0' (release tag)
|
|
71
|
+
binary.version, // '1.0.0' (branch / tag without v)
|
|
72
|
+
'main', // default branch on most repos
|
|
73
|
+
'master', // older repos
|
|
74
|
+
]));
|
|
75
|
+
const urlCandidates = [];
|
|
76
|
+
for (const filename of candidates) {
|
|
77
|
+
// Releases URL: tag-style refs
|
|
78
|
+
urlCandidates.push({
|
|
79
|
+
url: `https://github.com/${binary.repo}/releases/download/${tag}/${filename}`,
|
|
80
|
+
kind: 'release',
|
|
81
|
+
});
|
|
82
|
+
// Raw-content URL: each ref + the spec's `path` subdir
|
|
83
|
+
if (binary.path) {
|
|
84
|
+
const pathPart = binary.path.replace(/^\/|\/$/g, '');
|
|
85
|
+
for (const ref of refsToTry) {
|
|
86
|
+
urlCandidates.push({
|
|
87
|
+
url: `https://raw.githubusercontent.com/${binary.repo}/${ref}/${pathPart}/${filename}`,
|
|
88
|
+
kind: 'raw',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Try each candidate URL until one downloads successfully. Each failure
|
|
94
|
+
// is logged so the user has visibility if every attempt misses.
|
|
95
|
+
for (const { url } of urlCandidates) {
|
|
96
|
+
// Filename to cache as: pull from the URL
|
|
97
|
+
const filename = url.substring(url.lastIndexOf('/') + 1);
|
|
98
|
+
const cachedPath = join(cacheDir, filename);
|
|
99
|
+
try {
|
|
100
|
+
await downloadFile(url, cachedPath, binary.auth?.type === 'github');
|
|
101
|
+
if (platform() !== 'win32') {
|
|
102
|
+
await chmod(cachedPath, 0o755);
|
|
103
|
+
}
|
|
104
|
+
log(` Binary ${binary.repo}: downloaded ${filename} -> ${cachedPath}`);
|
|
105
|
+
return { cachedPath, downloaded: true, assetName: filename };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
log(` Binary ${binary.repo}: ${url} -> ${err.message}`);
|
|
109
|
+
// Try next URL
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
log(` Binary ${binary.repo}: failed to download from any of ${urlCandidates.length} candidate URLs (tried release tag ${tag} and raw paths under refs ${refsToTry.join(', ')}).`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Expand {os}, {arch}, {ext} in a release-asset pattern into a small set
|
|
117
|
+
* of likely candidates. The canonical convention (windows/linux/darwin x
|
|
118
|
+
* amd64/arm64) is tried first; we fall back to a couple of legacy aliases
|
|
119
|
+
* (win, macos, x64) so manifests authored against older binaries still
|
|
120
|
+
* resolve.
|
|
121
|
+
*/
|
|
122
|
+
export function expandPatternVariants(pattern) {
|
|
123
|
+
const variants = [];
|
|
124
|
+
const seen = new Set();
|
|
125
|
+
// Canonical (Go release-uploader) convention
|
|
126
|
+
const canonicalOs = canonical(['win32', 'windows'], ['darwin', 'darwin'], ['linux', 'linux']);
|
|
127
|
+
const canonicalArch = process.arch === 'arm64' ? 'arm64' : 'amd64';
|
|
128
|
+
const ext = platform() === 'win32' ? '.exe' : '';
|
|
129
|
+
// Legacy aliases (older dobby/loom releases use these)
|
|
130
|
+
const legacyOs = canonical(['win32', 'win'], ['darwin', 'macos'], ['linux', 'linux']);
|
|
131
|
+
const legacyArch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
132
|
+
for (const os of [canonicalOs, legacyOs]) {
|
|
133
|
+
for (const arch of [canonicalArch, legacyArch]) {
|
|
134
|
+
const candidate = pattern
|
|
135
|
+
.replace(/\{os\}/g, os)
|
|
136
|
+
.replace(/\{arch\}/g, arch)
|
|
137
|
+
.replace(/\{ext\}/g, ext);
|
|
138
|
+
if (!seen.has(candidate)) {
|
|
139
|
+
seen.add(candidate);
|
|
140
|
+
variants.push(candidate);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return variants;
|
|
145
|
+
}
|
|
146
|
+
function canonical(...pairs) {
|
|
147
|
+
const p = platform();
|
|
148
|
+
for (const [match, value] of pairs) {
|
|
149
|
+
if (p === match)
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
return pairs[pairs.length - 1][1];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Download a URL to a file, optionally with a GitHub Bearer token. Uses
|
|
156
|
+
* Node's built-in fetch (Node 18+). Throws on non-2xx response or on
|
|
157
|
+
* network error.
|
|
158
|
+
*/
|
|
159
|
+
async function downloadFile(url, destPath, useGithubAuth) {
|
|
160
|
+
// Note: do NOT set Accept: application/octet-stream. GitHub's release
|
|
161
|
+
// download URL redirects to a CDN that returns 406 for that header. */* is
|
|
162
|
+
// safe and matches what curl/wget send by default.
|
|
163
|
+
const headers = {
|
|
164
|
+
Accept: '*/*',
|
|
165
|
+
'User-Agent': 'loom-cli',
|
|
166
|
+
};
|
|
167
|
+
if (useGithubAuth) {
|
|
168
|
+
try {
|
|
169
|
+
const token = execSync('gh auth token', { stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
|
|
170
|
+
if (token)
|
|
171
|
+
headers.Authorization = `Bearer ${token}`;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// gh CLI not installed or not signed in -- proceed without auth.
|
|
175
|
+
// Public releases will still work; private will fail with 404.
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const res = await fetch(url, { headers, redirect: 'follow' });
|
|
179
|
+
if (!res.ok) {
|
|
180
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
181
|
+
}
|
|
182
|
+
// Validate the response is an actual binary, not an HTML 404 / login wall.
|
|
183
|
+
// GitHub serves text/html with a 200 status code when the release tag or
|
|
184
|
+
// asset doesn't exist (e.g. the manifest pinned an outdated version).
|
|
185
|
+
// Caching that HTML as <name>.exe leaves the user with an 'invalid
|
|
186
|
+
// application' error at MCP launch time -- a slow, confusing failure.
|
|
187
|
+
// Fail loud here so the resolver can try the next candidate or surface
|
|
188
|
+
// a clear error.
|
|
189
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
190
|
+
if (/^text\/html\b/i.test(contentType)) {
|
|
191
|
+
throw new Error(`server returned text/html (likely 404 page disguised as 200) -- ` +
|
|
192
|
+
`release tag or asset name probably doesn't exist`);
|
|
193
|
+
}
|
|
194
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
195
|
+
// Sanity-check binary magic for the platform we're targeting. Catches
|
|
196
|
+
// the CDN-served-HTML case even if the server forgot the content-type
|
|
197
|
+
// header. Skipped for non-exe extensions (e.g. raw binary names on Linux).
|
|
198
|
+
if (destPath.toLowerCase().endsWith('.exe')) {
|
|
199
|
+
if (buf.length < 2 || buf[0] !== 0x4d || buf[1] !== 0x5a) {
|
|
200
|
+
throw new Error(`downloaded file is not a valid Windows PE executable (no MZ header). ` +
|
|
201
|
+
`Likely an HTML error page from GitHub.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Atomic write via .tmp + rename to avoid leaving a partial cache file
|
|
205
|
+
// if the process is killed mid-write.
|
|
206
|
+
const tmp = destPath + '.tmp';
|
|
207
|
+
await writeFile(tmp, buf);
|
|
208
|
+
const fs = await import('node:fs/promises');
|
|
209
|
+
await fs.rename(tmp, destPath);
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=binary-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-cache.js","sourceRoot":"","sources":["../../src/util/binary-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML frontmatter parsing for `.agent.md` (and other) markdown files.
|
|
3
|
+
*
|
|
4
|
+
* Lives under `src/util/` because frontmatter handling is needed by every
|
|
5
|
+
* layer (chat, run, renderers, manifest). Putting it under `src/chat/`
|
|
6
|
+
* forced renderers + util code to import a chat module — wrong dependency
|
|
7
|
+
* direction. `src/chat/frontmatter.ts` re-exports these for backwards
|
|
8
|
+
* compatibility with older imports.
|
|
9
|
+
*
|
|
10
|
+
* Minimal, tolerant parser: extracts the top-most `---` fenced block,
|
|
11
|
+
* hands the body to js-yaml, and returns a normalized view of the fields
|
|
12
|
+
* `loom <agent>` cares about. Files without frontmatter are not an error —
|
|
13
|
+
* they simply yield an empty metadata object.
|
|
14
|
+
*
|
|
15
|
+
* Tolerant of:
|
|
16
|
+
* - LF or CRLF line endings (Windows-authored agent files)
|
|
17
|
+
* - UTF-8 BOM at the start of the file (also common on Windows)
|
|
18
|
+
*
|
|
19
|
+
* Three exports cover every shape we need:
|
|
20
|
+
* - `parseAgentFrontmatter(path)` — read from disk, normalized view
|
|
21
|
+
* - `parseFrontmatterRaw(content)` — string in, raw dict out
|
|
22
|
+
* - `stripFrontmatter(content)` — body without the fenced block
|
|
23
|
+
*
|
|
24
|
+
* **Always use these instead of hand-rolling a `^---` regex** — the
|
|
25
|
+
* `repo-conventions.test.ts` lint enforces this.
|
|
26
|
+
*/
|
|
27
|
+
export interface AgentMetadata {
|
|
28
|
+
/** Human-readable agent name (frontmatter `name:` or the file's agent id) */
|
|
29
|
+
name?: string;
|
|
30
|
+
/** One-line description from `description:` */
|
|
31
|
+
description?: string;
|
|
32
|
+
/** Model hint, e.g. `claude-sonnet-4.5` (may be absent in CLI-shaped agents) */
|
|
33
|
+
model?: string;
|
|
34
|
+
/** Any other frontmatter fields, preserved for future use */
|
|
35
|
+
raw: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parse the top-most `---` fenced YAML block from `content` and return it
|
|
39
|
+
* as a plain object. Returns `{}` when there's no frontmatter or the YAML
|
|
40
|
+
* is malformed. Tolerant of CRLF and a leading UTF-8 BOM.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseFrontmatterRaw(content: string): Record<string, unknown>;
|
|
43
|
+
/**
|
|
44
|
+
* Return `content` with the leading `---` fenced YAML block removed.
|
|
45
|
+
* No-op when there is no frontmatter. Tolerant of CRLF and a leading BOM.
|
|
46
|
+
*/
|
|
47
|
+
export declare function stripFrontmatter(content: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Parse the frontmatter from an agent file. Returns an empty metadata object
|
|
50
|
+
* (with `raw: {}`) when no frontmatter is present.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseAgentFrontmatter(agentFilePath: string): AgentMetadata;
|
|
53
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/util/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAaD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAW5E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAe1E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML frontmatter parsing for `.agent.md` (and other) markdown files.
|
|
3
|
+
*
|
|
4
|
+
* Lives under `src/util/` because frontmatter handling is needed by every
|
|
5
|
+
* layer (chat, run, renderers, manifest). Putting it under `src/chat/`
|
|
6
|
+
* forced renderers + util code to import a chat module — wrong dependency
|
|
7
|
+
* direction. `src/chat/frontmatter.ts` re-exports these for backwards
|
|
8
|
+
* compatibility with older imports.
|
|
9
|
+
*
|
|
10
|
+
* Minimal, tolerant parser: extracts the top-most `---` fenced block,
|
|
11
|
+
* hands the body to js-yaml, and returns a normalized view of the fields
|
|
12
|
+
* `loom <agent>` cares about. Files without frontmatter are not an error —
|
|
13
|
+
* they simply yield an empty metadata object.
|
|
14
|
+
*
|
|
15
|
+
* Tolerant of:
|
|
16
|
+
* - LF or CRLF line endings (Windows-authored agent files)
|
|
17
|
+
* - UTF-8 BOM at the start of the file (also common on Windows)
|
|
18
|
+
*
|
|
19
|
+
* Three exports cover every shape we need:
|
|
20
|
+
* - `parseAgentFrontmatter(path)` — read from disk, normalized view
|
|
21
|
+
* - `parseFrontmatterRaw(content)` — string in, raw dict out
|
|
22
|
+
* - `stripFrontmatter(content)` — body without the fenced block
|
|
23
|
+
*
|
|
24
|
+
* **Always use these instead of hand-rolling a `^---` regex** — the
|
|
25
|
+
* `repo-conventions.test.ts` lint enforces this.
|
|
26
|
+
*/
|
|
27
|
+
import { readFileSync } from 'node:fs';
|
|
28
|
+
import yaml from 'js-yaml';
|
|
29
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
30
|
+
/**
|
|
31
|
+
* Strip a leading UTF-8 BOM (U+FEFF) if present. Windows editors and
|
|
32
|
+
* PowerShell `>` redirection often add one, which would otherwise prevent
|
|
33
|
+
* `^---` from matching the frontmatter fence.
|
|
34
|
+
*/
|
|
35
|
+
function stripBom(content) {
|
|
36
|
+
return content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parse the top-most `---` fenced YAML block from `content` and return it
|
|
40
|
+
* as a plain object. Returns `{}` when there's no frontmatter or the YAML
|
|
41
|
+
* is malformed. Tolerant of CRLF and a leading UTF-8 BOM.
|
|
42
|
+
*/
|
|
43
|
+
export function parseFrontmatterRaw(content) {
|
|
44
|
+
const match = FRONTMATTER_RE.exec(stripBom(content));
|
|
45
|
+
if (!match)
|
|
46
|
+
return {};
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = yaml.load(match[1]);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
55
|
+
return {};
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Return `content` with the leading `---` fenced YAML block removed.
|
|
60
|
+
* No-op when there is no frontmatter. Tolerant of CRLF and a leading BOM.
|
|
61
|
+
*/
|
|
62
|
+
export function stripFrontmatter(content) {
|
|
63
|
+
return stripBom(content).replace(FRONTMATTER_RE, '').trim();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse the frontmatter from an agent file. Returns an empty metadata object
|
|
67
|
+
* (with `raw: {}`) when no frontmatter is present.
|
|
68
|
+
*/
|
|
69
|
+
export function parseAgentFrontmatter(agentFilePath) {
|
|
70
|
+
let content;
|
|
71
|
+
try {
|
|
72
|
+
content = readFileSync(agentFilePath, 'utf-8');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { raw: {} };
|
|
76
|
+
}
|
|
77
|
+
const raw = parseFrontmatterRaw(content);
|
|
78
|
+
return {
|
|
79
|
+
name: typeof raw.name === 'string' ? raw.name : undefined,
|
|
80
|
+
description: typeof raw.description === 'string' ? raw.description : undefined,
|
|
81
|
+
model: typeof raw.model === 'string' ? raw.model : undefined,
|
|
82
|
+
raw,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/util/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,SAAS,CAAC;AAa3B,MAAM,cAAc,GAAG,mCAAmC,CAAC;AAE3D;;;;GAIG;AACH,SAAS,QAAQ,CAAC,OAAe;IAC/B,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9E,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,aAAqB;IACzD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,GAAG,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO;QACL,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QACzD,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QAC9E,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC5D,GAAG;KACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the loom home directory.
|
|
3
|
+
*
|
|
4
|
+
* Default: `~/.loom`. Overridable via the `LOOM_HOME` env var. The
|
|
5
|
+
* env var must be an absolute path; relative paths are rejected to
|
|
6
|
+
* prevent surprising behavior when callers set it once and then
|
|
7
|
+
* change cwd.
|
|
8
|
+
*
|
|
9
|
+
* Why a single helper: this path is the root for staged dirs, repo
|
|
10
|
+
* cache, MCP binaries, sessions, run artifacts, repos.yaml, and the
|
|
11
|
+
* top-level config.yaml. Hardcoding `~/.loom` in each module makes
|
|
12
|
+
* test sandboxing painful and forecloses on a future dobby-CLI rebrand
|
|
13
|
+
* (where the same code might write to `~/.dobby/`).
|
|
14
|
+
*
|
|
15
|
+
* The function is LAZY -- never cached -- because tests need to be
|
|
16
|
+
* able to set HOME / USERPROFILE / LOOM_HOME on a per-test basis.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loomHomeDir(): string;
|
|
19
|
+
//# sourceMappingURL=loom-home.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loom-home.d.ts","sourceRoot":"","sources":["../../src/util/loom-home.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,wBAAgB,WAAW,IAAI,MAAM,CAiBpC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the loom home directory.
|
|
3
|
+
*
|
|
4
|
+
* Default: `~/.loom`. Overridable via the `LOOM_HOME` env var. The
|
|
5
|
+
* env var must be an absolute path; relative paths are rejected to
|
|
6
|
+
* prevent surprising behavior when callers set it once and then
|
|
7
|
+
* change cwd.
|
|
8
|
+
*
|
|
9
|
+
* Why a single helper: this path is the root for staged dirs, repo
|
|
10
|
+
* cache, MCP binaries, sessions, run artifacts, repos.yaml, and the
|
|
11
|
+
* top-level config.yaml. Hardcoding `~/.loom` in each module makes
|
|
12
|
+
* test sandboxing painful and forecloses on a future dobby-CLI rebrand
|
|
13
|
+
* (where the same code might write to `~/.dobby/`).
|
|
14
|
+
*
|
|
15
|
+
* The function is LAZY -- never cached -- because tests need to be
|
|
16
|
+
* able to set HOME / USERPROFILE / LOOM_HOME on a per-test basis.
|
|
17
|
+
*/
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { join, isAbsolute } from 'node:path';
|
|
20
|
+
let warnedAboutRelativeOverride = false;
|
|
21
|
+
export function loomHomeDir() {
|
|
22
|
+
const override = process.env.LOOM_HOME;
|
|
23
|
+
if (override && override.length > 0) {
|
|
24
|
+
if (isAbsolute(override)) {
|
|
25
|
+
return override;
|
|
26
|
+
}
|
|
27
|
+
// Reject relative paths but don't crash the caller. The caller is
|
|
28
|
+
// typically a path getter and breaking it would surface as a
|
|
29
|
+
// confusing ENOENT downstream. Warn once, fall back to default.
|
|
30
|
+
if (!warnedAboutRelativeOverride) {
|
|
31
|
+
warnedAboutRelativeOverride = true;
|
|
32
|
+
process.stderr.write(` warning: LOOM_HOME='${override}' is not absolute; falling back to default ~/.loom.\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return join(homedir(), '.loom');
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=loom-home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loom-home.js","sourceRoot":"","sources":["../../src/util/loom-home.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE7C,IAAI,2BAA2B,GAAG,KAAK,CAAC;AAExC,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACvC,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,kEAAkE;QAClE,6DAA6D;QAC7D,gEAAgE;QAChE,IAAI,CAAC,2BAA2B,EAAE,CAAC;YACjC,2BAA2B,GAAG,IAAI,CAAC;YACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yBAAyB,QAAQ,uDAAuD,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper: expand `${workspaceFolder:X}` placeholders in MCP
|
|
3
|
+
* args and cwd. Used by apply.ts after manifest resolution so the
|
|
4
|
+
* placeholder semantics are identical regardless of the source
|
|
5
|
+
* manifest layout (canonical contents:-wrapped or flat).
|
|
6
|
+
*
|
|
7
|
+
* The placeholder is resolved against a repo-name -> dir-name map
|
|
8
|
+
* built from the manifest's repos: block. Both repo.name and repo.id
|
|
9
|
+
* are entries in the map (pointing to the dir loom will place the
|
|
10
|
+
* clone at), so manifests can use either form.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildRepoNameMap<T extends {
|
|
13
|
+
id?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
}>(repos: ReadonlyArray<T>,
|
|
16
|
+
/**
|
|
17
|
+
* Function that returns the directory name a repo will be placed at
|
|
18
|
+
* in the workspace. Concrete callers wire this to deriveRepoDir or
|
|
19
|
+
* an equivalent so the map matches what cloneManifestRepos / lazy-
|
|
20
|
+
* stage actually does.
|
|
21
|
+
*/
|
|
22
|
+
dirNameFor: (repo: T) => string): Map<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Replace every `${workspaceFolder:X}` in the input with
|
|
25
|
+
* `<workDir>/<resolved>` where resolved is the repoNameMap lookup
|
|
26
|
+
* (falling back to X verbatim).
|
|
27
|
+
*/
|
|
28
|
+
export declare function expandWorkspaceFolder(value: string, repoNameMap: Map<string, string>, workDir: string): string;
|
|
29
|
+
//# sourceMappingURL=workspace-folder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-folder.d.ts","sourceRoot":"","sources":["../../src/util/workspace-folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACvE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;AACvB;;;;;GAKG;AACH,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC9B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,GACd,MAAM,CAKR"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper: expand `${workspaceFolder:X}` placeholders in MCP
|
|
3
|
+
* args and cwd. Used by apply.ts after manifest resolution so the
|
|
4
|
+
* placeholder semantics are identical regardless of the source
|
|
5
|
+
* manifest layout (canonical contents:-wrapped or flat).
|
|
6
|
+
*
|
|
7
|
+
* The placeholder is resolved against a repo-name -> dir-name map
|
|
8
|
+
* built from the manifest's repos: block. Both repo.name and repo.id
|
|
9
|
+
* are entries in the map (pointing to the dir loom will place the
|
|
10
|
+
* clone at), so manifests can use either form.
|
|
11
|
+
*/
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
export function buildRepoNameMap(repos,
|
|
14
|
+
/**
|
|
15
|
+
* Function that returns the directory name a repo will be placed at
|
|
16
|
+
* in the workspace. Concrete callers wire this to deriveRepoDir or
|
|
17
|
+
* an equivalent so the map matches what cloneManifestRepos / lazy-
|
|
18
|
+
* stage actually does.
|
|
19
|
+
*/
|
|
20
|
+
dirNameFor) {
|
|
21
|
+
const map = new Map();
|
|
22
|
+
for (const r of repos) {
|
|
23
|
+
const dirName = dirNameFor(r);
|
|
24
|
+
if (r.name)
|
|
25
|
+
map.set(r.name, dirName);
|
|
26
|
+
if (r.id)
|
|
27
|
+
map.set(r.id, dirName);
|
|
28
|
+
map.set(dirName, dirName); // identity, in case the placeholder uses the dir name
|
|
29
|
+
}
|
|
30
|
+
return map;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Replace every `${workspaceFolder:X}` in the input with
|
|
34
|
+
* `<workDir>/<resolved>` where resolved is the repoNameMap lookup
|
|
35
|
+
* (falling back to X verbatim).
|
|
36
|
+
*/
|
|
37
|
+
export function expandWorkspaceFolder(value, repoNameMap, workDir) {
|
|
38
|
+
return value.replace(/\$\{workspaceFolder:([^}]+)\}/g, (_match, name) => {
|
|
39
|
+
const dirName = repoNameMap.get(name) ?? name;
|
|
40
|
+
return join(workDir, dirName);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=workspace-folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-folder.js","sourceRoot":"","sources":["../../src/util/workspace-folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,gBAAgB,CAC9B,KAAuB;AACvB;;;;;GAKG;AACH,UAA+B;IAE/B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC,IAAI;YAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,EAAE;YAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,sDAAsD;IACnF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAa,EACb,WAAgC,EAChC,OAAe;IAEf,OAAO,KAAK,CAAC,OAAO,CAAC,gCAAgC,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QAC9E,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC9C,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Manifest validation
|
|
2
|
+
* Manifest validation -- schema, reference, and registry checks.
|
|
3
|
+
*
|
|
4
|
+
* Per U6, this validator probes both `manifest.yaml` (canonical) and
|
|
5
|
+
* `dobby.yaml` (legacy alias) inside the template dir, and runs the
|
|
6
|
+
* raw YAML through `normalizeManifestAliases` so dobby-style field
|
|
7
|
+
* names (`mcpServers:`, `sparseCheckout:`, `team:`) and flat layout
|
|
8
|
+
* are validated against the same schema as canonical manifests.
|
|
3
9
|
*/
|
|
4
10
|
export interface ValidateResult {
|
|
5
11
|
templateId: string;
|
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,CAAC,CAgKzB;AAMD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC,CA4D3B"}
|