openmeld 0.3.205 → 0.3.206
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/dist/{add-me-membership-jhCNTfdl.js → add-me-membership-BkHWMxE4.js} +2 -2
- package/dist/{add-me-membership-jhCNTfdl.js.map → add-me-membership-BkHWMxE4.js.map} +1 -1
- package/dist/{agents-9krfNX_L.js → agents-BOTlKiwS.js} +901 -499
- package/dist/agents-BOTlKiwS.js.map +1 -0
- package/dist/{api-client-foundation-BguumKMv.js → api-client-foundation-6ms5gSu8.js} +1 -1
- package/dist/{api-client-foundation-BMPOo4bG.js → api-client-foundation-DpgDs0rw.js} +17 -7
- package/dist/api-client-foundation-DpgDs0rw.js.map +1 -0
- package/dist/{auth-session-wmefguut.js → auth-session-BjSqMLTz.js} +187 -56
- package/dist/auth-session-BjSqMLTz.js.map +1 -0
- package/dist/auth-session-ahQ5VXiF.js +4 -0
- package/dist/{base-url-jWXPssF_.js → base-url-9A557X2K.js} +2 -2
- package/dist/{base-url-jWXPssF_.js.map → base-url-9A557X2K.js.map} +1 -1
- package/dist/{base-url-C89TOpPV.js → base-url-CRAYNW-4.js} +1 -1
- package/dist/{command-DKM3wO3V.js → command-CkbzbvXy.js} +10 -10
- package/dist/{command-DKM3wO3V.js.map → command-CkbzbvXy.js.map} +1 -1
- package/dist/{computer-setup-continuation-Dad0lLP9.js → computer-setup-continuation-Cd9nhLQ-.js} +4 -4
- package/dist/{computer-setup-continuation-Dad0lLP9.js.map → computer-setup-continuation-Cd9nhLQ-.js.map} +1 -1
- package/dist/dist-0boZRalP.js +4 -0
- package/dist/{dist-CkixulB-.js → dist-CxadJClH.js} +3692 -3557
- package/dist/dist-CxadJClH.js.map +1 -0
- package/dist/{elapsed-time-CjAPqg-B.js → elapsed-time-C7P-nWIO.js} +3 -3
- package/dist/{elapsed-time-CjAPqg-B.js.map → elapsed-time-C7P-nWIO.js.map} +1 -1
- package/dist/{installer-CZyyvNZr.js → installation-inventory-P49sTjhe.js} +359 -359
- package/dist/installation-inventory-P49sTjhe.js.map +1 -0
- package/dist/{openmeld-DVThrV4O.js → openmeld-ChOHuaqH.js} +20 -16
- package/dist/openmeld-ChOHuaqH.js.map +1 -0
- package/dist/openmeld-dev.js +1 -1
- package/dist/{openmeld-maintenance-worker-Cxp6fwB5.js → openmeld-maintenance-worker-C7WykyDt.js} +5 -5
- package/dist/{openmeld-maintenance-worker-Cxp6fwB5.js.map → openmeld-maintenance-worker-C7WykyDt.js.map} +1 -1
- package/dist/{openmeld-tools-update-coordinator-DJ65uI3O.js → openmeld-tools-update-coordinator-BBZhGo1A.js} +12 -12
- package/dist/{openmeld-tools-update-coordinator-DJ65uI3O.js.map → openmeld-tools-update-coordinator-BBZhGo1A.js.map} +1 -1
- package/dist/openmeld.js +3 -3
- package/dist/{prepare-session-executor-aE1YARgn.js → prepare-session-executor-CLmDoHom.js} +9 -9
- package/dist/{prepare-session-executor-aE1YARgn.js.map → prepare-session-executor-CLmDoHom.js.map} +1 -1
- package/dist/{profiles-Dx3gaXgQ.js → profiles-BCecj4Pj.js} +397 -397
- package/dist/profiles-BCecj4Pj.js.map +1 -0
- package/dist/upgrade-BKEAZKre.js +4 -0
- package/dist/{upgrade-Bk8t5oav.js → upgrade-l_5Tge_z.js} +4 -4
- package/dist/{upgrade-Bk8t5oav.js.map → upgrade-l_5Tge_z.js.map} +1 -1
- package/dist/{urls-4D7CaXe3.js → urls-CY2uzH2-.js} +2 -1
- package/dist/urls-CY2uzH2-.js.map +1 -0
- package/package.json +1 -1
- package/dist/agents-9krfNX_L.js.map +0 -1
- package/dist/api-client-foundation-BMPOo4bG.js.map +0 -1
- package/dist/auth-session-CpPqG8Xz.js +0 -4
- package/dist/auth-session-wmefguut.js.map +0 -1
- package/dist/dist-BFbC-9BX.js +0 -4
- package/dist/dist-CkixulB-.js.map +0 -1
- package/dist/installer-CZyyvNZr.js.map +0 -1
- package/dist/openmeld-DVThrV4O.js.map +0 -1
- package/dist/profiles-Dx3gaXgQ.js.map +0 -1
- package/dist/upgrade-BFGKy5He.js +0 -4
- package/dist/urls-4D7CaXe3.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { ht as skillsDir, lt as resolveOpenMeldEnvPreset, mt as runtimeLogsDir, tt as openMeldRootDir } from "./lock-CvBnBcFK.js";
|
|
4
|
-
import { An as resolveSkillsAgentRegistry, Ko as compareSemver, On as canManageRegistryAgentSkillTargets, _o as OPENMELD_VERSION, kn as detectInstalledSkillsAgents } from "./openmeld-tools-update-coordinator-
|
|
5
|
-
import {
|
|
6
|
-
import { T as updateConfiguredSkillsConfig, g as getConfiguredSkillsConfig, y as resolveRuntimeSkillsCustomTargetId } from "./base-url-
|
|
4
|
+
import { An as resolveSkillsAgentRegistry, Ko as compareSemver, On as canManageRegistryAgentSkillTargets, _o as OPENMELD_VERSION, kn as detectInstalledSkillsAgents } from "./openmeld-tools-update-coordinator-BBZhGo1A.js";
|
|
5
|
+
import { S as isBinaryDistribution } from "./auth-session-BjSqMLTz.js";
|
|
6
|
+
import { T as updateConfiguredSkillsConfig, g as getConfiguredSkillsConfig, y as resolveRuntimeSkillsCustomTargetId } from "./base-url-9A557X2K.js";
|
|
7
7
|
import { createHash, randomUUID, verify } from "node:crypto";
|
|
8
8
|
import path, { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
9
9
|
import { cp, lstat, mkdir, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
@@ -42,160 +42,6 @@ const officialCliSkillChannelSchema = z$1.object({
|
|
|
42
42
|
releases: z$1.array(officialCliSkillReleaseSchema).min(1).max(16)
|
|
43
43
|
}).strict();
|
|
44
44
|
//#endregion
|
|
45
|
-
//#region src/skills/bundled.ts
|
|
46
|
-
const CLI_PACKAGE_JSON = "package.json";
|
|
47
|
-
/**
|
|
48
|
-
* Locate the on-disk bundled skills directory for the npm distribution by
|
|
49
|
-
* walking up from this module to the CLI package root (a directory that has
|
|
50
|
-
* both package.json and skills/). Returns null under the binary distribution,
|
|
51
|
-
* where import.meta.url is a virtual path and the skills are embedded instead —
|
|
52
|
-
* callers in that mode go through bundled-assets.ts, not this function.
|
|
53
|
-
*/
|
|
54
|
-
function resolveBundledSkillsLocation() {
|
|
55
|
-
const cliRootDir = resolveCliRootDir(path.dirname(fileURLToPath(import.meta.url)));
|
|
56
|
-
if (!cliRootDir) return null;
|
|
57
|
-
return {
|
|
58
|
-
cliRootDir,
|
|
59
|
-
skillsRootDir: path.join(cliRootDir, "skills")
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function resolveCliRootDir(fromDir) {
|
|
63
|
-
let current = fromDir;
|
|
64
|
-
for (;;) {
|
|
65
|
-
const hasPackageJson = existsSync(path.join(current, CLI_PACKAGE_JSON));
|
|
66
|
-
const hasSkillsDir = existsSync(path.join(current, "skills"));
|
|
67
|
-
if (hasPackageJson && hasSkillsDir) return current;
|
|
68
|
-
const parent = path.dirname(current);
|
|
69
|
-
if (parent === current) return null;
|
|
70
|
-
current = parent;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/skills/bundled-assets-fs.ts
|
|
75
|
-
/**
|
|
76
|
-
* npm-distribution source: read the bundled skills from the on-disk `skills/`
|
|
77
|
-
* directory next to the CLI package. Assets are sorted by relative path so the
|
|
78
|
-
* order (and therefore any content hash computed from them) is stable and
|
|
79
|
-
* matches the embedded distribution.
|
|
80
|
-
*/
|
|
81
|
-
async function loadFsBundledSkillAssets() {
|
|
82
|
-
const location = resolveBundledSkillsLocation();
|
|
83
|
-
if (!location) throw new Error("unable to resolve bundled OpenMeld skills directory");
|
|
84
|
-
return sortBundledSkillAssets(await collectSkillAssets(location.skillsRootDir, location.skillsRootDir));
|
|
85
|
-
}
|
|
86
|
-
function sortBundledSkillAssets(assets) {
|
|
87
|
-
return [...assets].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
88
|
-
}
|
|
89
|
-
async function collectSkillAssets(dir, rootDir) {
|
|
90
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
91
|
-
const assets = [];
|
|
92
|
-
for (const entry of entries) {
|
|
93
|
-
const absolutePath = join(dir, entry.name);
|
|
94
|
-
if (entry.isDirectory()) {
|
|
95
|
-
assets.push(...await collectSkillAssets(absolutePath, rootDir));
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (!entry.isFile()) continue;
|
|
99
|
-
assets.push({
|
|
100
|
-
relativePath: toPosixRelativePath(relative(rootDir, absolutePath)),
|
|
101
|
-
content: await readFile(absolutePath)
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return assets;
|
|
105
|
-
}
|
|
106
|
-
function toPosixRelativePath(value) {
|
|
107
|
-
return value.split("\\").join("/");
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
110
|
-
//#region src/skills/bundled-assets.ts
|
|
111
|
-
/**
|
|
112
|
-
* Load the OpenMeld-managed skill files that ship with this CLI build.
|
|
113
|
-
*
|
|
114
|
-
* - npm distribution: read from the on-disk `skills/` directory.
|
|
115
|
-
* - binary distribution: read from files embedded into the single binary at
|
|
116
|
-
* compile time. The embedded module is a Bun-only island resolved only by
|
|
117
|
-
* `bun build --compile`; the npm build never reaches this branch and marks
|
|
118
|
-
* the module external, so its `with { type: "file" }` imports are never
|
|
119
|
-
* parsed by tsdown.
|
|
120
|
-
*/
|
|
121
|
-
async function loadBundledSkillAssets() {
|
|
122
|
-
if (isBinaryDistribution()) return (await import("./skills/bundled-assets-embedded")).loadEmbeddedBundledSkillAssets();
|
|
123
|
-
return loadFsBundledSkillAssets();
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Content hash over an ordered list of skill assets. Byte-for-byte compatible
|
|
127
|
-
* with `computeDirectoryContentHash` (same `file:<relativePath>\n<content>\n`
|
|
128
|
-
* framing) so the embedded and on-disk sources produce identical hashes as long
|
|
129
|
-
* as `assets` is sorted by relative path (as `loadBundledSkillAssets` returns
|
|
130
|
-
* it). This is the single skills-hash algorithm for both distribution forms.
|
|
131
|
-
*/
|
|
132
|
-
function hashBundledSkillAssets(assets) {
|
|
133
|
-
const hash = createHash("sha256");
|
|
134
|
-
for (const asset of assets) {
|
|
135
|
-
hash.update(`file:${asset.relativePath}\n`);
|
|
136
|
-
hash.update(asset.content);
|
|
137
|
-
hash.update("\n");
|
|
138
|
-
}
|
|
139
|
-
return hash.digest("hex");
|
|
140
|
-
}
|
|
141
|
-
//#endregion
|
|
142
|
-
//#region src/skills/content-hash.ts
|
|
143
|
-
async function computeDirectoryContentHash(input) {
|
|
144
|
-
const ignoredNames = new Set(input.ignoredNames ?? []);
|
|
145
|
-
const files = await collectFiles({
|
|
146
|
-
dir: input.rootDir,
|
|
147
|
-
rootDir: input.rootDir,
|
|
148
|
-
ignoredNames
|
|
149
|
-
});
|
|
150
|
-
const hash = createHash("sha256");
|
|
151
|
-
for (const file of files) {
|
|
152
|
-
hash.update(`file:${file.relativePath}\n`);
|
|
153
|
-
hash.update(file.content);
|
|
154
|
-
hash.update("\n");
|
|
155
|
-
}
|
|
156
|
-
return hash.digest("hex");
|
|
157
|
-
}
|
|
158
|
-
async function collectFiles(input) {
|
|
159
|
-
const entries = await readdir(input.dir, { withFileTypes: true }).catch((error) => {
|
|
160
|
-
if (getErrorCode$1(error) === "ENOENT") return [];
|
|
161
|
-
warnDirectoryReadError(input.dir, error);
|
|
162
|
-
throw error;
|
|
163
|
-
});
|
|
164
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
165
|
-
const files = [];
|
|
166
|
-
for (const entry of entries) {
|
|
167
|
-
if (input.ignoredNames.has(entry.name)) continue;
|
|
168
|
-
const absolutePath = join(input.dir, entry.name);
|
|
169
|
-
if (entry.isDirectory()) {
|
|
170
|
-
files.push(...await collectFiles({
|
|
171
|
-
dir: absolutePath,
|
|
172
|
-
rootDir: input.rootDir,
|
|
173
|
-
ignoredNames: input.ignoredNames
|
|
174
|
-
}));
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
if (!entry.isFile()) continue;
|
|
178
|
-
const content = await readFile(absolutePath);
|
|
179
|
-
files.push({
|
|
180
|
-
relativePath: normalizeRelativePath(relative(input.rootDir, absolutePath)),
|
|
181
|
-
content
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return files;
|
|
185
|
-
}
|
|
186
|
-
function normalizeRelativePath(value) {
|
|
187
|
-
return value.split("\\").join("/");
|
|
188
|
-
}
|
|
189
|
-
function getErrorCode$1(error) {
|
|
190
|
-
if (!error || typeof error !== "object") return;
|
|
191
|
-
const code = error.code;
|
|
192
|
-
return typeof code === "string" ? code : void 0;
|
|
193
|
-
}
|
|
194
|
-
function warnDirectoryReadError(dir, error) {
|
|
195
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
196
|
-
process.stderr.write(`[openmeld] failed to read directory while hashing skills content (${dir}): ${reason}\n`);
|
|
197
|
-
}
|
|
198
|
-
//#endregion
|
|
199
45
|
//#region src/skills/install-lock.ts
|
|
200
46
|
const heldLock = new AsyncLocalStorage();
|
|
201
47
|
const pendingLocks = /* @__PURE__ */ new Map();
|
|
@@ -227,7 +73,7 @@ async function acquireSkillsInstallLock(run) {
|
|
|
227
73
|
acquired = true;
|
|
228
74
|
break;
|
|
229
75
|
} catch (error) {
|
|
230
|
-
if (getErrorCode(error) !== "EEXIST") throw error;
|
|
76
|
+
if (getErrorCode$1(error) !== "EEXIST") throw error;
|
|
231
77
|
if (!await recoverStaleSkillsInstallLock(lockPath)) throw new Error("another skills installation is already running; retry after it finishes");
|
|
232
78
|
try {
|
|
233
79
|
await writeFile(lockPath, lockContents, {
|
|
@@ -237,7 +83,7 @@ async function acquireSkillsInstallLock(run) {
|
|
|
237
83
|
acquired = true;
|
|
238
84
|
break;
|
|
239
85
|
} catch (retryError) {
|
|
240
|
-
if (getErrorCode(retryError) !== "EEXIST") throw retryError;
|
|
86
|
+
if (getErrorCode$1(retryError) !== "EEXIST") throw retryError;
|
|
241
87
|
}
|
|
242
88
|
}
|
|
243
89
|
if (!acquired) throw new Error("another skills installation is already running; retry after it finishes");
|
|
@@ -249,7 +95,7 @@ async function acquireSkillsInstallLock(run) {
|
|
|
249
95
|
}
|
|
250
96
|
async function recoverStaleSkillsInstallLock(lockPath) {
|
|
251
97
|
const lockRaw = await readFile(lockPath, "utf8").catch((error) => {
|
|
252
|
-
if (getErrorCode(error) === "ENOENT") return "";
|
|
98
|
+
if (getErrorCode$1(error) === "ENOENT") return "";
|
|
253
99
|
throw error;
|
|
254
100
|
});
|
|
255
101
|
if (!lockRaw) return true;
|
|
@@ -260,7 +106,7 @@ async function recoverStaleSkillsInstallLock(lockPath) {
|
|
|
260
106
|
}
|
|
261
107
|
if (pid && isPidAlive(pid)) return false;
|
|
262
108
|
const latestLockRaw = await readFile(lockPath, "utf8").catch((error) => {
|
|
263
|
-
if (getErrorCode(error) === "ENOENT") return "";
|
|
109
|
+
if (getErrorCode$1(error) === "ENOENT") return "";
|
|
264
110
|
throw error;
|
|
265
111
|
});
|
|
266
112
|
if (!latestLockRaw) return true;
|
|
@@ -271,7 +117,7 @@ async function recoverStaleSkillsInstallLock(lockPath) {
|
|
|
271
117
|
afterPid: resolveLockPid(parseLockMeta(latestLockRaw))
|
|
272
118
|
})) return false;
|
|
273
119
|
await rm(lockPath, { force: true }).catch((error) => {
|
|
274
|
-
if (getErrorCode(error) === "ENOENT") return;
|
|
120
|
+
if (getErrorCode$1(error) === "ENOENT") return;
|
|
275
121
|
throw error;
|
|
276
122
|
});
|
|
277
123
|
return true;
|
|
@@ -293,10 +139,10 @@ function isPidAlive(pid) {
|
|
|
293
139
|
process.kill(pid, 0);
|
|
294
140
|
return true;
|
|
295
141
|
} catch (error) {
|
|
296
|
-
return getErrorCode(error) !== "ESRCH";
|
|
142
|
+
return getErrorCode$1(error) !== "ESRCH";
|
|
297
143
|
}
|
|
298
144
|
}
|
|
299
|
-
function getErrorCode(error) {
|
|
145
|
+
function getErrorCode$1(error) {
|
|
300
146
|
if (!error || typeof error !== "object") return;
|
|
301
147
|
const code = error.code;
|
|
302
148
|
return typeof code === "string" ? code : void 0;
|
|
@@ -306,6 +152,219 @@ function isSameLockSnapshot(input) {
|
|
|
306
152
|
return input.beforePid === input.afterPid;
|
|
307
153
|
}
|
|
308
154
|
//#endregion
|
|
155
|
+
//#region src/skills/replace-directory.ts
|
|
156
|
+
async function recoverSkillReplacement(target) {
|
|
157
|
+
const previous = join(dirname(target), `.${basename(target)}.openmeld-previous`);
|
|
158
|
+
if (!await exists(previous)) return;
|
|
159
|
+
if (!await isManagedReplacement(previous, basename(target))) throw new Error("OpenMeld Skill backup conflicts with an unmanaged path");
|
|
160
|
+
if (await exists(target)) {
|
|
161
|
+
if (!await isManagedReplacement(target, basename(target))) throw new Error("OpenMeld Skill recovery would affect an unmanaged path");
|
|
162
|
+
await rm(previous, {
|
|
163
|
+
recursive: true,
|
|
164
|
+
force: true
|
|
165
|
+
});
|
|
166
|
+
} else await rename(previous, target);
|
|
167
|
+
}
|
|
168
|
+
async function replaceSkillDirectory(target, prepare) {
|
|
169
|
+
await recoverSkillReplacement(target);
|
|
170
|
+
const staged = join(dirname(target), `.${basename(target)}.openmeld-staged-${randomUUID()}`);
|
|
171
|
+
const previous = join(dirname(target), `.${basename(target)}.openmeld-previous`);
|
|
172
|
+
try {
|
|
173
|
+
await prepare(staged);
|
|
174
|
+
const hadPrevious = await exists(target);
|
|
175
|
+
if (hadPrevious) await rename(target, previous);
|
|
176
|
+
try {
|
|
177
|
+
await rename(staged, target);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (hadPrevious) await rename(previous, target);
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
await rm(previous, {
|
|
183
|
+
recursive: true,
|
|
184
|
+
force: true
|
|
185
|
+
}).catch(() => void 0);
|
|
186
|
+
} finally {
|
|
187
|
+
await rm(staged, {
|
|
188
|
+
recursive: true,
|
|
189
|
+
force: true
|
|
190
|
+
}).catch(() => void 0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function isManagedReplacement(path, skillId) {
|
|
194
|
+
const raw = await readFile(join(path, ".openmeld-skill.json"), "utf8").catch(() => null);
|
|
195
|
+
if (raw) try {
|
|
196
|
+
const marker = JSON.parse(raw);
|
|
197
|
+
return marker.managedBy === "openmeld" && marker.skillId === skillId;
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
if ((await lstat(path)).isSymbolicLink()) {
|
|
202
|
+
const destination = resolve(dirname(path), await readlink(path));
|
|
203
|
+
return basename(destination) === skillId && !await exists(destination);
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
function exists(path) {
|
|
208
|
+
return lstat(path).then(() => true, (error) => {
|
|
209
|
+
if (error.code === "ENOENT") return false;
|
|
210
|
+
throw error;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/skills/content-hash.ts
|
|
215
|
+
async function computeDirectoryContentHash(input) {
|
|
216
|
+
const ignoredNames = new Set(input.ignoredNames ?? []);
|
|
217
|
+
const files = await collectFiles({
|
|
218
|
+
dir: input.rootDir,
|
|
219
|
+
rootDir: input.rootDir,
|
|
220
|
+
ignoredNames
|
|
221
|
+
});
|
|
222
|
+
const hash = createHash("sha256");
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
hash.update(`file:${file.relativePath}\n`);
|
|
225
|
+
hash.update(file.content);
|
|
226
|
+
hash.update("\n");
|
|
227
|
+
}
|
|
228
|
+
return hash.digest("hex");
|
|
229
|
+
}
|
|
230
|
+
async function collectFiles(input) {
|
|
231
|
+
const entries = await readdir(input.dir, { withFileTypes: true }).catch((error) => {
|
|
232
|
+
if (getErrorCode(error) === "ENOENT") return [];
|
|
233
|
+
warnDirectoryReadError(input.dir, error);
|
|
234
|
+
throw error;
|
|
235
|
+
});
|
|
236
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
237
|
+
const files = [];
|
|
238
|
+
for (const entry of entries) {
|
|
239
|
+
if (input.ignoredNames.has(entry.name)) continue;
|
|
240
|
+
const absolutePath = join(input.dir, entry.name);
|
|
241
|
+
if (entry.isDirectory()) {
|
|
242
|
+
files.push(...await collectFiles({
|
|
243
|
+
dir: absolutePath,
|
|
244
|
+
rootDir: input.rootDir,
|
|
245
|
+
ignoredNames: input.ignoredNames
|
|
246
|
+
}));
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (!entry.isFile()) continue;
|
|
250
|
+
const content = await readFile(absolutePath);
|
|
251
|
+
files.push({
|
|
252
|
+
relativePath: normalizeRelativePath(relative(input.rootDir, absolutePath)),
|
|
253
|
+
content
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return files;
|
|
257
|
+
}
|
|
258
|
+
function normalizeRelativePath(value) {
|
|
259
|
+
return value.split("\\").join("/");
|
|
260
|
+
}
|
|
261
|
+
function getErrorCode(error) {
|
|
262
|
+
if (!error || typeof error !== "object") return;
|
|
263
|
+
const code = error.code;
|
|
264
|
+
return typeof code === "string" ? code : void 0;
|
|
265
|
+
}
|
|
266
|
+
function warnDirectoryReadError(dir, error) {
|
|
267
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
268
|
+
process.stderr.write(`[openmeld] failed to read directory while hashing skills content (${dir}): ${reason}\n`);
|
|
269
|
+
}
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/skills/bundled.ts
|
|
272
|
+
const CLI_PACKAGE_JSON = "package.json";
|
|
273
|
+
/**
|
|
274
|
+
* Locate the on-disk bundled skills directory for the npm distribution by
|
|
275
|
+
* walking up from this module to the CLI package root (a directory that has
|
|
276
|
+
* both package.json and skills/). Returns null under the binary distribution,
|
|
277
|
+
* where import.meta.url is a virtual path and the skills are embedded instead —
|
|
278
|
+
* callers in that mode go through bundled-assets.ts, not this function.
|
|
279
|
+
*/
|
|
280
|
+
function resolveBundledSkillsLocation() {
|
|
281
|
+
const cliRootDir = resolveCliRootDir(path.dirname(fileURLToPath(import.meta.url)));
|
|
282
|
+
if (!cliRootDir) return null;
|
|
283
|
+
return {
|
|
284
|
+
cliRootDir,
|
|
285
|
+
skillsRootDir: path.join(cliRootDir, "skills")
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function resolveCliRootDir(fromDir) {
|
|
289
|
+
let current = fromDir;
|
|
290
|
+
for (;;) {
|
|
291
|
+
const hasPackageJson = existsSync(path.join(current, CLI_PACKAGE_JSON));
|
|
292
|
+
const hasSkillsDir = existsSync(path.join(current, "skills"));
|
|
293
|
+
if (hasPackageJson && hasSkillsDir) return current;
|
|
294
|
+
const parent = path.dirname(current);
|
|
295
|
+
if (parent === current) return null;
|
|
296
|
+
current = parent;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/skills/bundled-assets-fs.ts
|
|
301
|
+
/**
|
|
302
|
+
* npm-distribution source: read the bundled skills from the on-disk `skills/`
|
|
303
|
+
* directory next to the CLI package. Assets are sorted by relative path so the
|
|
304
|
+
* order (and therefore any content hash computed from them) is stable and
|
|
305
|
+
* matches the embedded distribution.
|
|
306
|
+
*/
|
|
307
|
+
async function loadFsBundledSkillAssets() {
|
|
308
|
+
const location = resolveBundledSkillsLocation();
|
|
309
|
+
if (!location) throw new Error("unable to resolve bundled OpenMeld skills directory");
|
|
310
|
+
return sortBundledSkillAssets(await collectSkillAssets(location.skillsRootDir, location.skillsRootDir));
|
|
311
|
+
}
|
|
312
|
+
function sortBundledSkillAssets(assets) {
|
|
313
|
+
return [...assets].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
314
|
+
}
|
|
315
|
+
async function collectSkillAssets(dir, rootDir) {
|
|
316
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
317
|
+
const assets = [];
|
|
318
|
+
for (const entry of entries) {
|
|
319
|
+
const absolutePath = join(dir, entry.name);
|
|
320
|
+
if (entry.isDirectory()) {
|
|
321
|
+
assets.push(...await collectSkillAssets(absolutePath, rootDir));
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (!entry.isFile()) continue;
|
|
325
|
+
assets.push({
|
|
326
|
+
relativePath: toPosixRelativePath(relative(rootDir, absolutePath)),
|
|
327
|
+
content: await readFile(absolutePath)
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return assets;
|
|
331
|
+
}
|
|
332
|
+
function toPosixRelativePath(value) {
|
|
333
|
+
return value.split("\\").join("/");
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/skills/bundled-assets.ts
|
|
337
|
+
/**
|
|
338
|
+
* Load the OpenMeld-managed skill files that ship with this CLI build.
|
|
339
|
+
*
|
|
340
|
+
* - npm distribution: read from the on-disk `skills/` directory.
|
|
341
|
+
* - binary distribution: read from files embedded into the single binary at
|
|
342
|
+
* compile time. The embedded module is a Bun-only island resolved only by
|
|
343
|
+
* `bun build --compile`; the npm build never reaches this branch and marks
|
|
344
|
+
* the module external, so its `with { type: "file" }` imports are never
|
|
345
|
+
* parsed by tsdown.
|
|
346
|
+
*/
|
|
347
|
+
async function loadBundledSkillAssets() {
|
|
348
|
+
if (isBinaryDistribution()) return (await import("./skills/bundled-assets-embedded")).loadEmbeddedBundledSkillAssets();
|
|
349
|
+
return loadFsBundledSkillAssets();
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Content hash over an ordered list of skill assets. Byte-for-byte compatible
|
|
353
|
+
* with `computeDirectoryContentHash` (same `file:<relativePath>\n<content>\n`
|
|
354
|
+
* framing) so the embedded and on-disk sources produce identical hashes as long
|
|
355
|
+
* as `assets` is sorted by relative path (as `loadBundledSkillAssets` returns
|
|
356
|
+
* it). This is the single skills-hash algorithm for both distribution forms.
|
|
357
|
+
*/
|
|
358
|
+
function hashBundledSkillAssets(assets) {
|
|
359
|
+
const hash = createHash("sha256");
|
|
360
|
+
for (const asset of assets) {
|
|
361
|
+
hash.update(`file:${asset.relativePath}\n`);
|
|
362
|
+
hash.update(asset.content);
|
|
363
|
+
hash.update("\n");
|
|
364
|
+
}
|
|
365
|
+
return hash.digest("hex");
|
|
366
|
+
}
|
|
367
|
+
//#endregion
|
|
309
368
|
//#region src/skills/official-release.ts
|
|
310
369
|
const CACHE_FILE = "official-cli-skill.json";
|
|
311
370
|
function verifyOfficialSkillRelease(value) {
|
|
@@ -390,83 +449,24 @@ async function downloadOfficialSkillUpdate() {
|
|
|
390
449
|
if (size > 1048576) throw new Error("OpenMeld Skill download exceeds the size limit");
|
|
391
450
|
chunks.push(chunk);
|
|
392
451
|
}
|
|
393
|
-
const selected = selectOfficialSkillRelease(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
394
|
-
if (!selected) return "incompatible";
|
|
395
|
-
return withSkillsInstallLock(async () => {
|
|
396
|
-
const cached = await readCachedRelease();
|
|
397
|
-
if (cached && cached.payload.revision >= selected.payload.revision) return "current";
|
|
398
|
-
await mkdir(openMeldRootDir(), { recursive: true });
|
|
399
|
-
const cache = join(openMeldRootDir(), CACHE_FILE);
|
|
400
|
-
const staged = `${cache}.${randomUUID()}.tmp`;
|
|
401
|
-
try {
|
|
402
|
-
await writeFile(staged, JSON.stringify(selected.release), {
|
|
403
|
-
flag: "wx",
|
|
404
|
-
mode: 384
|
|
405
|
-
});
|
|
406
|
-
await rename(staged, cache);
|
|
407
|
-
} finally {
|
|
408
|
-
await rm(staged, { force: true });
|
|
409
|
-
}
|
|
410
|
-
return "updated";
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
//#endregion
|
|
414
|
-
//#region src/skills/replace-directory.ts
|
|
415
|
-
async function recoverSkillReplacement(target) {
|
|
416
|
-
const previous = join(dirname(target), `.${basename(target)}.openmeld-previous`);
|
|
417
|
-
if (!await exists(previous)) return;
|
|
418
|
-
if (!await isManagedReplacement(previous, basename(target))) throw new Error("OpenMeld Skill backup conflicts with an unmanaged path");
|
|
419
|
-
if (await exists(target)) {
|
|
420
|
-
if (!await isManagedReplacement(target, basename(target))) throw new Error("OpenMeld Skill recovery would affect an unmanaged path");
|
|
421
|
-
await rm(previous, {
|
|
422
|
-
recursive: true,
|
|
423
|
-
force: true
|
|
424
|
-
});
|
|
425
|
-
} else await rename(previous, target);
|
|
426
|
-
}
|
|
427
|
-
async function replaceSkillDirectory(target, prepare) {
|
|
428
|
-
await recoverSkillReplacement(target);
|
|
429
|
-
const staged = join(dirname(target), `.${basename(target)}.openmeld-staged-${randomUUID()}`);
|
|
430
|
-
const previous = join(dirname(target), `.${basename(target)}.openmeld-previous`);
|
|
431
|
-
try {
|
|
432
|
-
await prepare(staged);
|
|
433
|
-
const hadPrevious = await exists(target);
|
|
434
|
-
if (hadPrevious) await rename(target, previous);
|
|
435
|
-
try {
|
|
436
|
-
await rename(staged, target);
|
|
437
|
-
} catch (error) {
|
|
438
|
-
if (hadPrevious) await rename(previous, target);
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
441
|
-
await rm(previous, {
|
|
442
|
-
recursive: true,
|
|
443
|
-
force: true
|
|
444
|
-
}).catch(() => void 0);
|
|
445
|
-
} finally {
|
|
446
|
-
await rm(staged, {
|
|
447
|
-
recursive: true,
|
|
448
|
-
force: true
|
|
449
|
-
}).catch(() => void 0);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
async function isManagedReplacement(path, skillId) {
|
|
453
|
-
const raw = await readFile(join(path, ".openmeld-skill.json"), "utf8").catch(() => null);
|
|
454
|
-
if (raw) try {
|
|
455
|
-
const marker = JSON.parse(raw);
|
|
456
|
-
return marker.managedBy === "openmeld" && marker.skillId === skillId;
|
|
457
|
-
} catch {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
if ((await lstat(path)).isSymbolicLink()) {
|
|
461
|
-
const destination = resolve(dirname(path), await readlink(path));
|
|
462
|
-
return basename(destination) === skillId && !await exists(destination);
|
|
463
|
-
}
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
function exists(path) {
|
|
467
|
-
return lstat(path).then(() => true, (error) => {
|
|
468
|
-
if (error.code === "ENOENT") return false;
|
|
469
|
-
throw error;
|
|
452
|
+
const selected = selectOfficialSkillRelease(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
453
|
+
if (!selected) return "incompatible";
|
|
454
|
+
return withSkillsInstallLock(async () => {
|
|
455
|
+
const cached = await readCachedRelease();
|
|
456
|
+
if (cached && cached.payload.revision >= selected.payload.revision) return "current";
|
|
457
|
+
await mkdir(openMeldRootDir(), { recursive: true });
|
|
458
|
+
const cache = join(openMeldRootDir(), CACHE_FILE);
|
|
459
|
+
const staged = `${cache}.${randomUUID()}.tmp`;
|
|
460
|
+
try {
|
|
461
|
+
await writeFile(staged, JSON.stringify(selected.release), {
|
|
462
|
+
flag: "wx",
|
|
463
|
+
mode: 384
|
|
464
|
+
});
|
|
465
|
+
await rename(staged, cache);
|
|
466
|
+
} finally {
|
|
467
|
+
await rm(staged, { force: true });
|
|
468
|
+
}
|
|
469
|
+
return "updated";
|
|
470
470
|
});
|
|
471
471
|
}
|
|
472
472
|
//#endregion
|
|
@@ -746,6 +746,122 @@ function pathsEqual(left, right) {
|
|
|
746
746
|
return resolve(left) === resolve(right);
|
|
747
747
|
}
|
|
748
748
|
//#endregion
|
|
749
|
+
//#region src/skills/installer.ts
|
|
750
|
+
function installSkillsToTargets(input) {
|
|
751
|
+
return withSkillsInstallLock(() => installSkillsToTargetsLocked(input));
|
|
752
|
+
}
|
|
753
|
+
async function installSkillsToTargetsLocked(input) {
|
|
754
|
+
const results = [];
|
|
755
|
+
for (const target of input.targets) for (const skill of input.skills) {
|
|
756
|
+
const result = await installOneSkillToTarget({
|
|
757
|
+
skill,
|
|
758
|
+
target
|
|
759
|
+
});
|
|
760
|
+
results.push(result);
|
|
761
|
+
}
|
|
762
|
+
return results;
|
|
763
|
+
}
|
|
764
|
+
async function installOneSkillToTarget(input) {
|
|
765
|
+
const targetDir = input.target.dir;
|
|
766
|
+
const failInstall = (reason) => ({
|
|
767
|
+
targetId: input.target.id,
|
|
768
|
+
targetDir,
|
|
769
|
+
targetType: input.target.type,
|
|
770
|
+
skillId: input.skill.id,
|
|
771
|
+
status: "failed",
|
|
772
|
+
mode: "unknown",
|
|
773
|
+
degraded: false,
|
|
774
|
+
reason
|
|
775
|
+
});
|
|
776
|
+
if (!isAbsolute(targetDir)) return failInstall("target dir must be an absolute path");
|
|
777
|
+
const skillId = input.skill.id;
|
|
778
|
+
if (skillId.trim().length === 0 || isAbsolute(skillId) || skillId === "." || skillId === ".." || skillId.includes("/") || skillId.includes("\\")) return failInstall("skill id would escape target dir");
|
|
779
|
+
const resolvedTargetDir = resolve(targetDir);
|
|
780
|
+
const resolvedSkillDir = resolve(resolvedTargetDir, skillId);
|
|
781
|
+
const skillRelativePath = relative(resolvedTargetDir, resolvedSkillDir);
|
|
782
|
+
if (skillRelativePath === ".." || skillRelativePath.startsWith("../") || skillRelativePath.startsWith("..\\")) return failInstall("skill id would escape target dir");
|
|
783
|
+
const skillTargetDir = resolvedSkillDir;
|
|
784
|
+
await mkdir(targetDir, { recursive: true });
|
|
785
|
+
await recoverSkillReplacement(skillTargetDir);
|
|
786
|
+
const state = await resolveSkillInstallStateForTarget({
|
|
787
|
+
skill: input.skill,
|
|
788
|
+
target: input.target
|
|
789
|
+
});
|
|
790
|
+
if (state.state === "external_conflict") return {
|
|
791
|
+
targetId: input.target.id,
|
|
792
|
+
targetDir,
|
|
793
|
+
targetType: input.target.type,
|
|
794
|
+
skillId: input.skill.id,
|
|
795
|
+
status: "skipped",
|
|
796
|
+
mode: resolveSkippedInstallMode(state),
|
|
797
|
+
degraded: false,
|
|
798
|
+
reason: state.reason
|
|
799
|
+
};
|
|
800
|
+
if (state.state === "up_to_date") return {
|
|
801
|
+
targetId: input.target.id,
|
|
802
|
+
targetDir,
|
|
803
|
+
targetType: input.target.type,
|
|
804
|
+
skillId: input.skill.id,
|
|
805
|
+
status: "skipped",
|
|
806
|
+
mode: resolveSkippedInstallMode(state),
|
|
807
|
+
degraded: false,
|
|
808
|
+
reason: state.reason
|
|
809
|
+
};
|
|
810
|
+
const installation = { mode: "symlink" };
|
|
811
|
+
try {
|
|
812
|
+
await readFile(join(input.skill.absolutePath, "SKILL.md"), "utf8");
|
|
813
|
+
await replaceSkillDirectory(skillTargetDir, async (stagedDir) => {
|
|
814
|
+
if (await tryCreateSymlink({
|
|
815
|
+
sourceDir: input.skill.absolutePath,
|
|
816
|
+
targetDir: stagedDir
|
|
817
|
+
})) return;
|
|
818
|
+
installation.mode = "copy";
|
|
819
|
+
await cp(input.skill.absolutePath, stagedDir, {
|
|
820
|
+
recursive: true,
|
|
821
|
+
errorOnExist: true,
|
|
822
|
+
force: false
|
|
823
|
+
});
|
|
824
|
+
await writeOpenMeldSkillMarker({
|
|
825
|
+
skillDir: stagedDir,
|
|
826
|
+
skillId: input.skill.id,
|
|
827
|
+
mode: "copy",
|
|
828
|
+
...input.skill.contentHash ? { contentHash: input.skill.contentHash } : {},
|
|
829
|
+
...input.skill.version ? { version: input.skill.version } : {}
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
return {
|
|
833
|
+
targetId: input.target.id,
|
|
834
|
+
targetDir,
|
|
835
|
+
targetType: input.target.type,
|
|
836
|
+
skillId: input.skill.id,
|
|
837
|
+
status: "installed",
|
|
838
|
+
mode: installation.mode,
|
|
839
|
+
degraded: installation.mode === "copy",
|
|
840
|
+
...installation.mode === "copy" ? { reason: "symlink failed; fell back to copy" } : {}
|
|
841
|
+
};
|
|
842
|
+
} catch (error) {
|
|
843
|
+
return failInstall(error instanceof Error ? error.message : "failed to install skill to target");
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function resolveSkippedInstallMode(state) {
|
|
847
|
+
if (state.targetMode === "copy") return "copy";
|
|
848
|
+
if (state.targetMode === "symlink") return "symlink";
|
|
849
|
+
return "unknown";
|
|
850
|
+
}
|
|
851
|
+
async function tryCreateSymlink(input) {
|
|
852
|
+
try {
|
|
853
|
+
await mkdir(dirname(input.targetDir), { recursive: true });
|
|
854
|
+
const targetParentDir = dirname(input.targetDir);
|
|
855
|
+
const resolvedTargetParentDir = await realpath(targetParentDir).catch(() => targetParentDir);
|
|
856
|
+
const resolvedSourceDir = await realpath(input.sourceDir).catch(() => input.sourceDir);
|
|
857
|
+
const relativeTarget = relative(resolvedTargetParentDir, resolvedSourceDir);
|
|
858
|
+
await symlink(process$1.platform === "win32" ? resolvedSourceDir : relativeTarget, input.targetDir, process$1.platform === "win32" ? "junction" : void 0);
|
|
859
|
+
return true;
|
|
860
|
+
} catch {
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
//#endregion
|
|
749
865
|
//#region src/skills/installation-inventory.ts
|
|
750
866
|
async function collectSkillsReadinessSnapshot() {
|
|
751
867
|
const skillsConfig = await getConfiguredSkillsConfig();
|
|
@@ -979,122 +1095,6 @@ function normalizeInstallRecordForCompare(input) {
|
|
|
979
1095
|
};
|
|
980
1096
|
}
|
|
981
1097
|
//#endregion
|
|
982
|
-
|
|
983
|
-
function installSkillsToTargets(input) {
|
|
984
|
-
return withSkillsInstallLock(() => installSkillsToTargetsLocked(input));
|
|
985
|
-
}
|
|
986
|
-
async function installSkillsToTargetsLocked(input) {
|
|
987
|
-
const results = [];
|
|
988
|
-
for (const target of input.targets) for (const skill of input.skills) {
|
|
989
|
-
const result = await installOneSkillToTarget({
|
|
990
|
-
skill,
|
|
991
|
-
target
|
|
992
|
-
});
|
|
993
|
-
results.push(result);
|
|
994
|
-
}
|
|
995
|
-
return results;
|
|
996
|
-
}
|
|
997
|
-
async function installOneSkillToTarget(input) {
|
|
998
|
-
const targetDir = input.target.dir;
|
|
999
|
-
const failInstall = (reason) => ({
|
|
1000
|
-
targetId: input.target.id,
|
|
1001
|
-
targetDir,
|
|
1002
|
-
targetType: input.target.type,
|
|
1003
|
-
skillId: input.skill.id,
|
|
1004
|
-
status: "failed",
|
|
1005
|
-
mode: "unknown",
|
|
1006
|
-
degraded: false,
|
|
1007
|
-
reason
|
|
1008
|
-
});
|
|
1009
|
-
if (!isAbsolute(targetDir)) return failInstall("target dir must be an absolute path");
|
|
1010
|
-
const skillId = input.skill.id;
|
|
1011
|
-
if (skillId.trim().length === 0 || isAbsolute(skillId) || skillId === "." || skillId === ".." || skillId.includes("/") || skillId.includes("\\")) return failInstall("skill id would escape target dir");
|
|
1012
|
-
const resolvedTargetDir = resolve(targetDir);
|
|
1013
|
-
const resolvedSkillDir = resolve(resolvedTargetDir, skillId);
|
|
1014
|
-
const skillRelativePath = relative(resolvedTargetDir, resolvedSkillDir);
|
|
1015
|
-
if (skillRelativePath === ".." || skillRelativePath.startsWith("../") || skillRelativePath.startsWith("..\\")) return failInstall("skill id would escape target dir");
|
|
1016
|
-
const skillTargetDir = resolvedSkillDir;
|
|
1017
|
-
await mkdir(targetDir, { recursive: true });
|
|
1018
|
-
await recoverSkillReplacement(skillTargetDir);
|
|
1019
|
-
const state = await resolveSkillInstallStateForTarget({
|
|
1020
|
-
skill: input.skill,
|
|
1021
|
-
target: input.target
|
|
1022
|
-
});
|
|
1023
|
-
if (state.state === "external_conflict") return {
|
|
1024
|
-
targetId: input.target.id,
|
|
1025
|
-
targetDir,
|
|
1026
|
-
targetType: input.target.type,
|
|
1027
|
-
skillId: input.skill.id,
|
|
1028
|
-
status: "skipped",
|
|
1029
|
-
mode: resolveSkippedInstallMode(state),
|
|
1030
|
-
degraded: false,
|
|
1031
|
-
reason: state.reason
|
|
1032
|
-
};
|
|
1033
|
-
if (state.state === "up_to_date") return {
|
|
1034
|
-
targetId: input.target.id,
|
|
1035
|
-
targetDir,
|
|
1036
|
-
targetType: input.target.type,
|
|
1037
|
-
skillId: input.skill.id,
|
|
1038
|
-
status: "skipped",
|
|
1039
|
-
mode: resolveSkippedInstallMode(state),
|
|
1040
|
-
degraded: false,
|
|
1041
|
-
reason: state.reason
|
|
1042
|
-
};
|
|
1043
|
-
const installation = { mode: "symlink" };
|
|
1044
|
-
try {
|
|
1045
|
-
await readFile(join(input.skill.absolutePath, "SKILL.md"), "utf8");
|
|
1046
|
-
await replaceSkillDirectory(skillTargetDir, async (stagedDir) => {
|
|
1047
|
-
if (await tryCreateSymlink({
|
|
1048
|
-
sourceDir: input.skill.absolutePath,
|
|
1049
|
-
targetDir: stagedDir
|
|
1050
|
-
})) return;
|
|
1051
|
-
installation.mode = "copy";
|
|
1052
|
-
await cp(input.skill.absolutePath, stagedDir, {
|
|
1053
|
-
recursive: true,
|
|
1054
|
-
errorOnExist: true,
|
|
1055
|
-
force: false
|
|
1056
|
-
});
|
|
1057
|
-
await writeOpenMeldSkillMarker({
|
|
1058
|
-
skillDir: stagedDir,
|
|
1059
|
-
skillId: input.skill.id,
|
|
1060
|
-
mode: "copy",
|
|
1061
|
-
...input.skill.contentHash ? { contentHash: input.skill.contentHash } : {},
|
|
1062
|
-
...input.skill.version ? { version: input.skill.version } : {}
|
|
1063
|
-
});
|
|
1064
|
-
});
|
|
1065
|
-
return {
|
|
1066
|
-
targetId: input.target.id,
|
|
1067
|
-
targetDir,
|
|
1068
|
-
targetType: input.target.type,
|
|
1069
|
-
skillId: input.skill.id,
|
|
1070
|
-
status: "installed",
|
|
1071
|
-
mode: installation.mode,
|
|
1072
|
-
degraded: installation.mode === "copy",
|
|
1073
|
-
...installation.mode === "copy" ? { reason: "symlink failed; fell back to copy" } : {}
|
|
1074
|
-
};
|
|
1075
|
-
} catch (error) {
|
|
1076
|
-
return failInstall(error instanceof Error ? error.message : "failed to install skill to target");
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
function resolveSkippedInstallMode(state) {
|
|
1080
|
-
if (state.targetMode === "copy") return "copy";
|
|
1081
|
-
if (state.targetMode === "symlink") return "symlink";
|
|
1082
|
-
return "unknown";
|
|
1083
|
-
}
|
|
1084
|
-
async function tryCreateSymlink(input) {
|
|
1085
|
-
try {
|
|
1086
|
-
await mkdir(dirname(input.targetDir), { recursive: true });
|
|
1087
|
-
const targetParentDir = dirname(input.targetDir);
|
|
1088
|
-
const resolvedTargetParentDir = await realpath(targetParentDir).catch(() => targetParentDir);
|
|
1089
|
-
const resolvedSourceDir = await realpath(input.sourceDir).catch(() => input.sourceDir);
|
|
1090
|
-
const relativeTarget = relative(resolvedTargetParentDir, resolvedSourceDir);
|
|
1091
|
-
await symlink(process$1.platform === "win32" ? resolvedSourceDir : relativeTarget, input.targetDir, process$1.platform === "win32" ? "junction" : void 0);
|
|
1092
|
-
return true;
|
|
1093
|
-
} catch {
|
|
1094
|
-
return false;
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
//#endregion
|
|
1098
|
-
export { readSelectedSkillDocument as _, filterTargetsWithInstalledOpenMeldSkills as a, hashBundledSkillAssets as b, resolveTrackedInstallTargets as c, resolveSkillsInstallStates as d, ensureLocalSkills as f, loadSelectedSkillAssets as g, downloadOfficialSkillUpdate as h, compareInstallRecords as i, summarizeSkillState as l, readOpenMeldSkillMarker as m, buildAgentTargets as n, mergeTargetsByDir as o, isOpenMeldManagedSkillDir as p, collectSkillsReadinessSnapshot as r, persistDetectedInstallStateFromCheck as s, installSkillsToTargets as t, resolveSkillInstallStateForTarget as u, withSkillsInstallLock as v, OFFICIAL_CLI_SKILL_URL as x, computeDirectoryContentHash as y };
|
|
1098
|
+
export { readSelectedSkillDocument as _, mergeTargetsByDir as a, withSkillsInstallLock as b, summarizeSkillState as c, resolveSkillsInstallStates as d, ensureLocalSkills as f, loadSelectedSkillAssets as g, downloadOfficialSkillUpdate as h, filterTargetsWithInstalledOpenMeldSkills as i, installSkillsToTargets as l, readOpenMeldSkillMarker as m, collectSkillsReadinessSnapshot as n, persistDetectedInstallStateFromCheck as o, isOpenMeldManagedSkillDir as p, compareInstallRecords as r, resolveTrackedInstallTargets as s, buildAgentTargets as t, resolveSkillInstallStateForTarget as u, hashBundledSkillAssets as v, OFFICIAL_CLI_SKILL_URL as x, computeDirectoryContentHash as y };
|
|
1099
1099
|
|
|
1100
|
-
//# sourceMappingURL=
|
|
1100
|
+
//# sourceMappingURL=installation-inventory-P49sTjhe.js.map
|