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.
Files changed (53) hide show
  1. package/dist/{add-me-membership-jhCNTfdl.js → add-me-membership-BkHWMxE4.js} +2 -2
  2. package/dist/{add-me-membership-jhCNTfdl.js.map → add-me-membership-BkHWMxE4.js.map} +1 -1
  3. package/dist/{agents-9krfNX_L.js → agents-BOTlKiwS.js} +901 -499
  4. package/dist/agents-BOTlKiwS.js.map +1 -0
  5. package/dist/{api-client-foundation-BguumKMv.js → api-client-foundation-6ms5gSu8.js} +1 -1
  6. package/dist/{api-client-foundation-BMPOo4bG.js → api-client-foundation-DpgDs0rw.js} +17 -7
  7. package/dist/api-client-foundation-DpgDs0rw.js.map +1 -0
  8. package/dist/{auth-session-wmefguut.js → auth-session-BjSqMLTz.js} +187 -56
  9. package/dist/auth-session-BjSqMLTz.js.map +1 -0
  10. package/dist/auth-session-ahQ5VXiF.js +4 -0
  11. package/dist/{base-url-jWXPssF_.js → base-url-9A557X2K.js} +2 -2
  12. package/dist/{base-url-jWXPssF_.js.map → base-url-9A557X2K.js.map} +1 -1
  13. package/dist/{base-url-C89TOpPV.js → base-url-CRAYNW-4.js} +1 -1
  14. package/dist/{command-DKM3wO3V.js → command-CkbzbvXy.js} +10 -10
  15. package/dist/{command-DKM3wO3V.js.map → command-CkbzbvXy.js.map} +1 -1
  16. package/dist/{computer-setup-continuation-Dad0lLP9.js → computer-setup-continuation-Cd9nhLQ-.js} +4 -4
  17. package/dist/{computer-setup-continuation-Dad0lLP9.js.map → computer-setup-continuation-Cd9nhLQ-.js.map} +1 -1
  18. package/dist/dist-0boZRalP.js +4 -0
  19. package/dist/{dist-CkixulB-.js → dist-CxadJClH.js} +3692 -3557
  20. package/dist/dist-CxadJClH.js.map +1 -0
  21. package/dist/{elapsed-time-CjAPqg-B.js → elapsed-time-C7P-nWIO.js} +3 -3
  22. package/dist/{elapsed-time-CjAPqg-B.js.map → elapsed-time-C7P-nWIO.js.map} +1 -1
  23. package/dist/{installer-CZyyvNZr.js → installation-inventory-P49sTjhe.js} +359 -359
  24. package/dist/installation-inventory-P49sTjhe.js.map +1 -0
  25. package/dist/{openmeld-DVThrV4O.js → openmeld-ChOHuaqH.js} +20 -16
  26. package/dist/openmeld-ChOHuaqH.js.map +1 -0
  27. package/dist/openmeld-dev.js +1 -1
  28. package/dist/{openmeld-maintenance-worker-Cxp6fwB5.js → openmeld-maintenance-worker-C7WykyDt.js} +5 -5
  29. package/dist/{openmeld-maintenance-worker-Cxp6fwB5.js.map → openmeld-maintenance-worker-C7WykyDt.js.map} +1 -1
  30. package/dist/{openmeld-tools-update-coordinator-DJ65uI3O.js → openmeld-tools-update-coordinator-BBZhGo1A.js} +12 -12
  31. package/dist/{openmeld-tools-update-coordinator-DJ65uI3O.js.map → openmeld-tools-update-coordinator-BBZhGo1A.js.map} +1 -1
  32. package/dist/openmeld.js +3 -3
  33. package/dist/{prepare-session-executor-aE1YARgn.js → prepare-session-executor-CLmDoHom.js} +9 -9
  34. package/dist/{prepare-session-executor-aE1YARgn.js.map → prepare-session-executor-CLmDoHom.js.map} +1 -1
  35. package/dist/{profiles-Dx3gaXgQ.js → profiles-BCecj4Pj.js} +397 -397
  36. package/dist/profiles-BCecj4Pj.js.map +1 -0
  37. package/dist/upgrade-BKEAZKre.js +4 -0
  38. package/dist/{upgrade-Bk8t5oav.js → upgrade-l_5Tge_z.js} +4 -4
  39. package/dist/{upgrade-Bk8t5oav.js.map → upgrade-l_5Tge_z.js.map} +1 -1
  40. package/dist/{urls-4D7CaXe3.js → urls-CY2uzH2-.js} +2 -1
  41. package/dist/urls-CY2uzH2-.js.map +1 -0
  42. package/package.json +1 -1
  43. package/dist/agents-9krfNX_L.js.map +0 -1
  44. package/dist/api-client-foundation-BMPOo4bG.js.map +0 -1
  45. package/dist/auth-session-CpPqG8Xz.js +0 -4
  46. package/dist/auth-session-wmefguut.js.map +0 -1
  47. package/dist/dist-BFbC-9BX.js +0 -4
  48. package/dist/dist-CkixulB-.js.map +0 -1
  49. package/dist/installer-CZyyvNZr.js.map +0 -1
  50. package/dist/openmeld-DVThrV4O.js.map +0 -1
  51. package/dist/profiles-Dx3gaXgQ.js.map +0 -1
  52. package/dist/upgrade-BFGKy5He.js +0 -4
  53. 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-DJ65uI3O.js";
5
- import { b as isBinaryDistribution } from "./auth-session-wmefguut.js";
6
- import { T as updateConfiguredSkillsConfig, g as getConfiguredSkillsConfig, y as resolveRuntimeSkillsCustomTargetId } from "./base-url-jWXPssF_.js";
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
- //#region src/skills/installer.ts
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=installer-CZyyvNZr.js.map
1100
+ //# sourceMappingURL=installation-inventory-P49sTjhe.js.map