release-skill 0.1.8 → 0.1.10

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.
@@ -9,7 +9,7 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
9
9
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
10
10
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
11
11
  // Package identity injected at build time — closure-independent --version probe.
12
- const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.8"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.10"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -17816,7 +17816,8 @@ var init_baseline = __esm({
17816
17816
  ".release-skill/lock",
17817
17817
  ".release-skill/lock-audit",
17818
17818
  ".release-skill/runs",
17819
- ".release-skill/transactions"
17819
+ ".release-skill/transactions",
17820
+ ".release-skill/kimi-attestations"
17820
17821
  ];
17821
17822
  RESERVED_CONTROL_PREFIXES = [
17822
17823
  ...CONTROL_PLANE_PREFIXES,
@@ -19013,14 +19014,30 @@ async function computeFrozenSnapshot(snapshotDir, { excludeRootEntries = [] } =
19013
19014
  throw frozenError("frozen snapshot root must be a real directory");
19014
19015
  }
19015
19016
  const entries = [];
19016
- const excluded = new Set(excludeRootEntries);
19017
+ const excludedRootNames = /* @__PURE__ */ new Set();
19018
+ const excludedPathPrefixes = [];
19019
+ for (const entry of excludeRootEntries) {
19020
+ if (typeof entry !== "string") {
19021
+ throw frozenError("frozen snapshot exclusion entries must be strings");
19022
+ }
19023
+ const segments = entry.split("/").filter((segment) => segment !== "" && segment !== ".");
19024
+ if (segments.length === 0 || entry.startsWith("/") || entry.includes("\\") || segments.some((segment) => segment === "..")) {
19025
+ throw frozenError(`frozen snapshot exclusion entry is not a safe relative path: ${JSON.stringify(entry)}`);
19026
+ }
19027
+ if (segments.length === 1) {
19028
+ excludedRootNames.add(segments[0]);
19029
+ } else {
19030
+ excludedPathPrefixes.push(segments.join("/"));
19031
+ }
19032
+ }
19017
19033
  async function walk(dir) {
19018
19034
  const children = await readdir3(dir, { withFileTypes: true });
19019
19035
  children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
19020
19036
  for (const child of children) {
19021
- if (dir === root && excluded.has(child.name)) continue;
19037
+ if (dir === root && excludedRootNames.has(child.name)) continue;
19022
19038
  const absolute = join3(dir, child.name);
19023
19039
  const rel = relative7(root, absolute).split("\\").join("/");
19040
+ if (excludedPathPrefixes.some((prefix) => rel === prefix || rel.startsWith(`${prefix}/`))) continue;
19024
19041
  const st = await lstat5(absolute);
19025
19042
  if (st.isSymbolicLink()) {
19026
19043
  throw frozenError(`frozen snapshot contains symlink: ${rel}`);
@@ -76930,6 +76947,50 @@ function transportPayload(entries) {
76930
76947
  contentDigest
76931
76948
  }));
76932
76949
  }
76950
+ function consumerTransportExclusions(consumer) {
76951
+ if (consumer === "claude") return [".in_use"];
76952
+ if (consumer === "codex") return [".git", ".codex-plugin/migrated-command-skills"];
76953
+ if (consumer === "kimi") return [".git"];
76954
+ return [];
76955
+ }
76956
+ function extractDeclaredPluginSource(consumer, entry) {
76957
+ const rawSource = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
76958
+ if (typeof rawSource !== "string" || rawSource.length === 0) {
76959
+ throw new Error(`marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`);
76960
+ }
76961
+ if (rawSource.startsWith("/") || rawSource.includes("..") || rawSource.includes("\\") || /^https?:\/\//i.test(rawSource)) {
76962
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76963
+ }
76964
+ const segments = rawSource.split("/").filter((segment) => segment !== "" && segment !== ".");
76965
+ if (segments.some((segment) => segment === "..")) {
76966
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76967
+ }
76968
+ return segments.length === 0 ? "." : segments.join("/");
76969
+ }
76970
+ async function resolveInstalledPayloadSubpath(snapshotDir, sourceEntries, action, consumer) {
76971
+ if (consumer === "kimi") return ".";
76972
+ const marketplaceRelative = consumer === "claude" ? ".claude-plugin/marketplace.json" : ".agents/plugins/marketplace.json";
76973
+ const anchored = sourceEntries.some((entry) => entry.type === "file" && entry.path === marketplaceRelative);
76974
+ if (!anchored) {
76975
+ throw new Error(`frozen snapshot is missing the marketplace manifest ${marketplaceRelative}`);
76976
+ }
76977
+ const result = await validateManifestFile(resolve19(snapshotDir, marketplaceRelative), ["name", "plugins"]);
76978
+ if (!result.valid) {
76979
+ throw new Error(`frozen snapshot ${marketplaceRelative} invalid: ${result.error}`);
76980
+ }
76981
+ if (result.manifest.name !== action.marketplace) {
76982
+ throw new Error(`marketplace manifest name "${result.manifest.name}" does not match action marketplace "${action.marketplace}"`);
76983
+ }
76984
+ const plugins = result.manifest.plugins;
76985
+ if (!Array.isArray(plugins)) {
76986
+ throw new Error(`${marketplaceRelative} must have a plugins[] array`);
76987
+ }
76988
+ const matches = plugins.filter((entry) => entry.name === action.plugin);
76989
+ if (matches.length !== 1) {
76990
+ throw new Error(`expected exactly one plugins[] entry with name "${action.plugin}", found ${matches.length}`);
76991
+ }
76992
+ return extractDeclaredPluginSource(consumer, matches[0]);
76993
+ }
76933
76994
  async function verifyInstalledMarketplacePayload(action, context, installPath, consumer) {
76934
76995
  const sourcePath = await resolveFrozenPath(
76935
76996
  context.root,
@@ -76940,10 +77001,21 @@ async function verifyInstalledMarketplacePayload(action, context, installPath, c
76940
77001
  if (sourceSnapshot.digest !== action.manifestDigest) {
76941
77002
  throw new Error("frozen marketplace snapshot digest no longer matches the plan");
76942
77003
  }
77004
+ const payloadSubpath = await resolveInstalledPayloadSubpath(
77005
+ sourcePath,
77006
+ sourceSnapshot.entries,
77007
+ action,
77008
+ consumer
77009
+ );
77010
+ const prefix = payloadSubpath === "." ? null : `${payloadSubpath}/`;
77011
+ const authorityEntries = prefix === null ? sourceSnapshot.entries : sourceSnapshot.entries.filter((entry) => entry.path.startsWith(prefix)).map((entry) => ({ ...entry, path: entry.path.slice(prefix.length) }));
77012
+ if (authorityEntries.length === 0) {
77013
+ throw new Error("frozen snapshot contains no payload under the declared marketplace source");
77014
+ }
76943
77015
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
76944
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
77016
+ excludeRootEntries: consumerTransportExclusions(consumer)
76945
77017
  });
76946
- if (JSON.stringify(transportPayload(sourceSnapshot.entries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
77018
+ if (JSON.stringify(transportPayload(authorityEntries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
76947
77019
  throw new Error("installed marketplace payload differs in path, bytes, size, or non-write mode bits");
76948
77020
  }
76949
77021
  return action.manifestDigest;
@@ -77643,19 +77715,14 @@ function createPluginMarketplaceAdapter(deps = {}) {
77643
77715
  });
77644
77716
  }
77645
77717
  const entry = pluginEntry[0];
77646
- const sourcePath = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
77647
- if (typeof sourcePath !== "string" || sourcePath.length === 0) {
77648
- return createResult({
77649
- actionType,
77650
- status: ActionStatus.PREFLIGHT_FAILED,
77651
- error: `marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`
77652
- });
77653
- }
77654
- if (sourcePath.startsWith("/") || sourcePath.includes("..") || sourcePath.includes("\\") || /^https?:\/\//i.test(sourcePath)) {
77718
+ let sourcePath;
77719
+ try {
77720
+ sourcePath = extractDeclaredPluginSource(consumer, entry);
77721
+ } catch (sourceErr) {
77655
77722
  return createResult({
77656
77723
  actionType,
77657
77724
  status: ActionStatus.PREFLIGHT_FAILED,
77658
- error: `marketplace plugin entry source "${sourcePath}" is not a safe relative path`
77725
+ error: sourceErr.message
77659
77726
  });
77660
77727
  }
77661
77728
  const sourceDirAbs = resolve19(snapshotDirReal, sourcePath);
@@ -78578,7 +78645,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
78578
78645
  } catch (digestErr) {
78579
78646
  try {
78580
78647
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
78581
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
78648
+ excludeRootEntries: consumerTransportExclusions(consumer)
78582
78649
  });
78583
78650
  manifestDigest = installedSnapshot.digest;
78584
78651
  } catch {
@@ -78744,6 +78811,9 @@ var init_plugin_marketplace = __esm({
78744
78811
  execFile9 = promisify10(execFileCb10);
78745
78812
  NAME4 = "plugin-marketplace";
78746
78813
  __name(transportPayload, "transportPayload");
78814
+ __name(consumerTransportExclusions, "consumerTransportExclusions");
78815
+ __name(extractDeclaredPluginSource, "extractDeclaredPluginSource");
78816
+ __name(resolveInstalledPayloadSubpath, "resolveInstalledPayloadSubpath");
78747
78817
  __name(verifyInstalledMarketplacePayload, "verifyInstalledMarketplacePayload");
78748
78818
  __name(writeEvidenceAtomic, "writeEvidenceAtomic");
78749
78819
  KIMI_REQUIREMENT_FILE = "release-skill-kimi-manual-install.json";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
@@ -9,7 +9,7 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
9
9
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
10
10
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
11
11
  // Package identity injected at build time — closure-independent --version probe.
12
- const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.8"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.10"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -17816,7 +17816,8 @@ var init_baseline = __esm({
17816
17816
  ".release-skill/lock",
17817
17817
  ".release-skill/lock-audit",
17818
17818
  ".release-skill/runs",
17819
- ".release-skill/transactions"
17819
+ ".release-skill/transactions",
17820
+ ".release-skill/kimi-attestations"
17820
17821
  ];
17821
17822
  RESERVED_CONTROL_PREFIXES = [
17822
17823
  ...CONTROL_PLANE_PREFIXES,
@@ -19013,14 +19014,30 @@ async function computeFrozenSnapshot(snapshotDir, { excludeRootEntries = [] } =
19013
19014
  throw frozenError("frozen snapshot root must be a real directory");
19014
19015
  }
19015
19016
  const entries = [];
19016
- const excluded = new Set(excludeRootEntries);
19017
+ const excludedRootNames = /* @__PURE__ */ new Set();
19018
+ const excludedPathPrefixes = [];
19019
+ for (const entry of excludeRootEntries) {
19020
+ if (typeof entry !== "string") {
19021
+ throw frozenError("frozen snapshot exclusion entries must be strings");
19022
+ }
19023
+ const segments = entry.split("/").filter((segment) => segment !== "" && segment !== ".");
19024
+ if (segments.length === 0 || entry.startsWith("/") || entry.includes("\\") || segments.some((segment) => segment === "..")) {
19025
+ throw frozenError(`frozen snapshot exclusion entry is not a safe relative path: ${JSON.stringify(entry)}`);
19026
+ }
19027
+ if (segments.length === 1) {
19028
+ excludedRootNames.add(segments[0]);
19029
+ } else {
19030
+ excludedPathPrefixes.push(segments.join("/"));
19031
+ }
19032
+ }
19017
19033
  async function walk(dir) {
19018
19034
  const children = await readdir3(dir, { withFileTypes: true });
19019
19035
  children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
19020
19036
  for (const child of children) {
19021
- if (dir === root && excluded.has(child.name)) continue;
19037
+ if (dir === root && excludedRootNames.has(child.name)) continue;
19022
19038
  const absolute = join3(dir, child.name);
19023
19039
  const rel = relative7(root, absolute).split("\\").join("/");
19040
+ if (excludedPathPrefixes.some((prefix) => rel === prefix || rel.startsWith(`${prefix}/`))) continue;
19024
19041
  const st = await lstat5(absolute);
19025
19042
  if (st.isSymbolicLink()) {
19026
19043
  throw frozenError(`frozen snapshot contains symlink: ${rel}`);
@@ -76930,6 +76947,50 @@ function transportPayload(entries) {
76930
76947
  contentDigest
76931
76948
  }));
76932
76949
  }
76950
+ function consumerTransportExclusions(consumer) {
76951
+ if (consumer === "claude") return [".in_use"];
76952
+ if (consumer === "codex") return [".git", ".codex-plugin/migrated-command-skills"];
76953
+ if (consumer === "kimi") return [".git"];
76954
+ return [];
76955
+ }
76956
+ function extractDeclaredPluginSource(consumer, entry) {
76957
+ const rawSource = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
76958
+ if (typeof rawSource !== "string" || rawSource.length === 0) {
76959
+ throw new Error(`marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`);
76960
+ }
76961
+ if (rawSource.startsWith("/") || rawSource.includes("..") || rawSource.includes("\\") || /^https?:\/\//i.test(rawSource)) {
76962
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76963
+ }
76964
+ const segments = rawSource.split("/").filter((segment) => segment !== "" && segment !== ".");
76965
+ if (segments.some((segment) => segment === "..")) {
76966
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76967
+ }
76968
+ return segments.length === 0 ? "." : segments.join("/");
76969
+ }
76970
+ async function resolveInstalledPayloadSubpath(snapshotDir, sourceEntries, action, consumer) {
76971
+ if (consumer === "kimi") return ".";
76972
+ const marketplaceRelative = consumer === "claude" ? ".claude-plugin/marketplace.json" : ".agents/plugins/marketplace.json";
76973
+ const anchored = sourceEntries.some((entry) => entry.type === "file" && entry.path === marketplaceRelative);
76974
+ if (!anchored) {
76975
+ throw new Error(`frozen snapshot is missing the marketplace manifest ${marketplaceRelative}`);
76976
+ }
76977
+ const result = await validateManifestFile(resolve19(snapshotDir, marketplaceRelative), ["name", "plugins"]);
76978
+ if (!result.valid) {
76979
+ throw new Error(`frozen snapshot ${marketplaceRelative} invalid: ${result.error}`);
76980
+ }
76981
+ if (result.manifest.name !== action.marketplace) {
76982
+ throw new Error(`marketplace manifest name "${result.manifest.name}" does not match action marketplace "${action.marketplace}"`);
76983
+ }
76984
+ const plugins = result.manifest.plugins;
76985
+ if (!Array.isArray(plugins)) {
76986
+ throw new Error(`${marketplaceRelative} must have a plugins[] array`);
76987
+ }
76988
+ const matches = plugins.filter((entry) => entry.name === action.plugin);
76989
+ if (matches.length !== 1) {
76990
+ throw new Error(`expected exactly one plugins[] entry with name "${action.plugin}", found ${matches.length}`);
76991
+ }
76992
+ return extractDeclaredPluginSource(consumer, matches[0]);
76993
+ }
76933
76994
  async function verifyInstalledMarketplacePayload(action, context, installPath, consumer) {
76934
76995
  const sourcePath = await resolveFrozenPath(
76935
76996
  context.root,
@@ -76940,10 +77001,21 @@ async function verifyInstalledMarketplacePayload(action, context, installPath, c
76940
77001
  if (sourceSnapshot.digest !== action.manifestDigest) {
76941
77002
  throw new Error("frozen marketplace snapshot digest no longer matches the plan");
76942
77003
  }
77004
+ const payloadSubpath = await resolveInstalledPayloadSubpath(
77005
+ sourcePath,
77006
+ sourceSnapshot.entries,
77007
+ action,
77008
+ consumer
77009
+ );
77010
+ const prefix = payloadSubpath === "." ? null : `${payloadSubpath}/`;
77011
+ const authorityEntries = prefix === null ? sourceSnapshot.entries : sourceSnapshot.entries.filter((entry) => entry.path.startsWith(prefix)).map((entry) => ({ ...entry, path: entry.path.slice(prefix.length) }));
77012
+ if (authorityEntries.length === 0) {
77013
+ throw new Error("frozen snapshot contains no payload under the declared marketplace source");
77014
+ }
76943
77015
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
76944
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
77016
+ excludeRootEntries: consumerTransportExclusions(consumer)
76945
77017
  });
76946
- if (JSON.stringify(transportPayload(sourceSnapshot.entries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
77018
+ if (JSON.stringify(transportPayload(authorityEntries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
76947
77019
  throw new Error("installed marketplace payload differs in path, bytes, size, or non-write mode bits");
76948
77020
  }
76949
77021
  return action.manifestDigest;
@@ -77643,19 +77715,14 @@ function createPluginMarketplaceAdapter(deps = {}) {
77643
77715
  });
77644
77716
  }
77645
77717
  const entry = pluginEntry[0];
77646
- const sourcePath = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
77647
- if (typeof sourcePath !== "string" || sourcePath.length === 0) {
77648
- return createResult({
77649
- actionType,
77650
- status: ActionStatus.PREFLIGHT_FAILED,
77651
- error: `marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`
77652
- });
77653
- }
77654
- if (sourcePath.startsWith("/") || sourcePath.includes("..") || sourcePath.includes("\\") || /^https?:\/\//i.test(sourcePath)) {
77718
+ let sourcePath;
77719
+ try {
77720
+ sourcePath = extractDeclaredPluginSource(consumer, entry);
77721
+ } catch (sourceErr) {
77655
77722
  return createResult({
77656
77723
  actionType,
77657
77724
  status: ActionStatus.PREFLIGHT_FAILED,
77658
- error: `marketplace plugin entry source "${sourcePath}" is not a safe relative path`
77725
+ error: sourceErr.message
77659
77726
  });
77660
77727
  }
77661
77728
  const sourceDirAbs = resolve19(snapshotDirReal, sourcePath);
@@ -78578,7 +78645,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
78578
78645
  } catch (digestErr) {
78579
78646
  try {
78580
78647
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
78581
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
78648
+ excludeRootEntries: consumerTransportExclusions(consumer)
78582
78649
  });
78583
78650
  manifestDigest = installedSnapshot.digest;
78584
78651
  } catch {
@@ -78744,6 +78811,9 @@ var init_plugin_marketplace = __esm({
78744
78811
  execFile9 = promisify10(execFileCb10);
78745
78812
  NAME4 = "plugin-marketplace";
78746
78813
  __name(transportPayload, "transportPayload");
78814
+ __name(consumerTransportExclusions, "consumerTransportExclusions");
78815
+ __name(extractDeclaredPluginSource, "extractDeclaredPluginSource");
78816
+ __name(resolveInstalledPayloadSubpath, "resolveInstalledPayloadSubpath");
78747
78817
  __name(verifyInstalledMarketplacePayload, "verifyInstalledMarketplacePayload");
78748
78818
  __name(writeEvidenceAtomic, "writeEvidenceAtomic");
78749
78819
  KIMI_REQUIREMENT_FILE = "release-skill-kimi-manual-install.json";
@@ -9,7 +9,7 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
9
9
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
10
10
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
11
11
  // Package identity injected at build time — closure-independent --version probe.
12
- const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.8"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.1.10"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -17816,7 +17816,8 @@ var init_baseline = __esm({
17816
17816
  ".release-skill/lock",
17817
17817
  ".release-skill/lock-audit",
17818
17818
  ".release-skill/runs",
17819
- ".release-skill/transactions"
17819
+ ".release-skill/transactions",
17820
+ ".release-skill/kimi-attestations"
17820
17821
  ];
17821
17822
  RESERVED_CONTROL_PREFIXES = [
17822
17823
  ...CONTROL_PLANE_PREFIXES,
@@ -19013,14 +19014,30 @@ async function computeFrozenSnapshot(snapshotDir, { excludeRootEntries = [] } =
19013
19014
  throw frozenError("frozen snapshot root must be a real directory");
19014
19015
  }
19015
19016
  const entries = [];
19016
- const excluded = new Set(excludeRootEntries);
19017
+ const excludedRootNames = /* @__PURE__ */ new Set();
19018
+ const excludedPathPrefixes = [];
19019
+ for (const entry of excludeRootEntries) {
19020
+ if (typeof entry !== "string") {
19021
+ throw frozenError("frozen snapshot exclusion entries must be strings");
19022
+ }
19023
+ const segments = entry.split("/").filter((segment) => segment !== "" && segment !== ".");
19024
+ if (segments.length === 0 || entry.startsWith("/") || entry.includes("\\") || segments.some((segment) => segment === "..")) {
19025
+ throw frozenError(`frozen snapshot exclusion entry is not a safe relative path: ${JSON.stringify(entry)}`);
19026
+ }
19027
+ if (segments.length === 1) {
19028
+ excludedRootNames.add(segments[0]);
19029
+ } else {
19030
+ excludedPathPrefixes.push(segments.join("/"));
19031
+ }
19032
+ }
19017
19033
  async function walk(dir) {
19018
19034
  const children = await readdir3(dir, { withFileTypes: true });
19019
19035
  children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
19020
19036
  for (const child of children) {
19021
- if (dir === root && excluded.has(child.name)) continue;
19037
+ if (dir === root && excludedRootNames.has(child.name)) continue;
19022
19038
  const absolute = join3(dir, child.name);
19023
19039
  const rel = relative7(root, absolute).split("\\").join("/");
19040
+ if (excludedPathPrefixes.some((prefix) => rel === prefix || rel.startsWith(`${prefix}/`))) continue;
19024
19041
  const st = await lstat5(absolute);
19025
19042
  if (st.isSymbolicLink()) {
19026
19043
  throw frozenError(`frozen snapshot contains symlink: ${rel}`);
@@ -76930,6 +76947,50 @@ function transportPayload(entries) {
76930
76947
  contentDigest
76931
76948
  }));
76932
76949
  }
76950
+ function consumerTransportExclusions(consumer) {
76951
+ if (consumer === "claude") return [".in_use"];
76952
+ if (consumer === "codex") return [".git", ".codex-plugin/migrated-command-skills"];
76953
+ if (consumer === "kimi") return [".git"];
76954
+ return [];
76955
+ }
76956
+ function extractDeclaredPluginSource(consumer, entry) {
76957
+ const rawSource = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
76958
+ if (typeof rawSource !== "string" || rawSource.length === 0) {
76959
+ throw new Error(`marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`);
76960
+ }
76961
+ if (rawSource.startsWith("/") || rawSource.includes("..") || rawSource.includes("\\") || /^https?:\/\//i.test(rawSource)) {
76962
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76963
+ }
76964
+ const segments = rawSource.split("/").filter((segment) => segment !== "" && segment !== ".");
76965
+ if (segments.some((segment) => segment === "..")) {
76966
+ throw new Error(`marketplace plugin entry source "${rawSource}" is not a safe relative path`);
76967
+ }
76968
+ return segments.length === 0 ? "." : segments.join("/");
76969
+ }
76970
+ async function resolveInstalledPayloadSubpath(snapshotDir, sourceEntries, action, consumer) {
76971
+ if (consumer === "kimi") return ".";
76972
+ const marketplaceRelative = consumer === "claude" ? ".claude-plugin/marketplace.json" : ".agents/plugins/marketplace.json";
76973
+ const anchored = sourceEntries.some((entry) => entry.type === "file" && entry.path === marketplaceRelative);
76974
+ if (!anchored) {
76975
+ throw new Error(`frozen snapshot is missing the marketplace manifest ${marketplaceRelative}`);
76976
+ }
76977
+ const result = await validateManifestFile(resolve19(snapshotDir, marketplaceRelative), ["name", "plugins"]);
76978
+ if (!result.valid) {
76979
+ throw new Error(`frozen snapshot ${marketplaceRelative} invalid: ${result.error}`);
76980
+ }
76981
+ if (result.manifest.name !== action.marketplace) {
76982
+ throw new Error(`marketplace manifest name "${result.manifest.name}" does not match action marketplace "${action.marketplace}"`);
76983
+ }
76984
+ const plugins = result.manifest.plugins;
76985
+ if (!Array.isArray(plugins)) {
76986
+ throw new Error(`${marketplaceRelative} must have a plugins[] array`);
76987
+ }
76988
+ const matches = plugins.filter((entry) => entry.name === action.plugin);
76989
+ if (matches.length !== 1) {
76990
+ throw new Error(`expected exactly one plugins[] entry with name "${action.plugin}", found ${matches.length}`);
76991
+ }
76992
+ return extractDeclaredPluginSource(consumer, matches[0]);
76993
+ }
76933
76994
  async function verifyInstalledMarketplacePayload(action, context, installPath, consumer) {
76934
76995
  const sourcePath = await resolveFrozenPath(
76935
76996
  context.root,
@@ -76940,10 +77001,21 @@ async function verifyInstalledMarketplacePayload(action, context, installPath, c
76940
77001
  if (sourceSnapshot.digest !== action.manifestDigest) {
76941
77002
  throw new Error("frozen marketplace snapshot digest no longer matches the plan");
76942
77003
  }
77004
+ const payloadSubpath = await resolveInstalledPayloadSubpath(
77005
+ sourcePath,
77006
+ sourceSnapshot.entries,
77007
+ action,
77008
+ consumer
77009
+ );
77010
+ const prefix = payloadSubpath === "." ? null : `${payloadSubpath}/`;
77011
+ const authorityEntries = prefix === null ? sourceSnapshot.entries : sourceSnapshot.entries.filter((entry) => entry.path.startsWith(prefix)).map((entry) => ({ ...entry, path: entry.path.slice(prefix.length) }));
77012
+ if (authorityEntries.length === 0) {
77013
+ throw new Error("frozen snapshot contains no payload under the declared marketplace source");
77014
+ }
76943
77015
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
76944
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
77016
+ excludeRootEntries: consumerTransportExclusions(consumer)
76945
77017
  });
76946
- if (JSON.stringify(transportPayload(sourceSnapshot.entries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
77018
+ if (JSON.stringify(transportPayload(authorityEntries)) !== JSON.stringify(transportPayload(installedSnapshot.entries))) {
76947
77019
  throw new Error("installed marketplace payload differs in path, bytes, size, or non-write mode bits");
76948
77020
  }
76949
77021
  return action.manifestDigest;
@@ -77643,19 +77715,14 @@ function createPluginMarketplaceAdapter(deps = {}) {
77643
77715
  });
77644
77716
  }
77645
77717
  const entry = pluginEntry[0];
77646
- const sourcePath = consumer === "claude" ? entry.source : entry.source?.source === "local" ? entry.source?.path : null;
77647
- if (typeof sourcePath !== "string" || sourcePath.length === 0) {
77648
- return createResult({
77649
- actionType,
77650
- status: ActionStatus.PREFLIGHT_FAILED,
77651
- error: `marketplace plugin entry source must be a non-empty relative path${consumer === "codex" ? ' (object with source:"local")' : ""}, got ${JSON.stringify(entry.source)}`
77652
- });
77653
- }
77654
- if (sourcePath.startsWith("/") || sourcePath.includes("..") || sourcePath.includes("\\") || /^https?:\/\//i.test(sourcePath)) {
77718
+ let sourcePath;
77719
+ try {
77720
+ sourcePath = extractDeclaredPluginSource(consumer, entry);
77721
+ } catch (sourceErr) {
77655
77722
  return createResult({
77656
77723
  actionType,
77657
77724
  status: ActionStatus.PREFLIGHT_FAILED,
77658
- error: `marketplace plugin entry source "${sourcePath}" is not a safe relative path`
77725
+ error: sourceErr.message
77659
77726
  });
77660
77727
  }
77661
77728
  const sourceDirAbs = resolve19(snapshotDirReal, sourcePath);
@@ -78578,7 +78645,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
78578
78645
  } catch (digestErr) {
78579
78646
  try {
78580
78647
  const installedSnapshot = await computeFrozenSnapshot(installPath, {
78581
- excludeRootEntries: consumer === "codex" || consumer === "kimi" ? [".git"] : []
78648
+ excludeRootEntries: consumerTransportExclusions(consumer)
78582
78649
  });
78583
78650
  manifestDigest = installedSnapshot.digest;
78584
78651
  } catch {
@@ -78744,6 +78811,9 @@ var init_plugin_marketplace = __esm({
78744
78811
  execFile9 = promisify10(execFileCb10);
78745
78812
  NAME4 = "plugin-marketplace";
78746
78813
  __name(transportPayload, "transportPayload");
78814
+ __name(consumerTransportExclusions, "consumerTransportExclusions");
78815
+ __name(extractDeclaredPluginSource, "extractDeclaredPluginSource");
78816
+ __name(resolveInstalledPayloadSubpath, "resolveInstalledPayloadSubpath");
78747
78817
  __name(verifyInstalledMarketplacePayload, "verifyInstalledMarketplacePayload");
78748
78818
  __name(writeEvidenceAtomic, "writeEvidenceAtomic");
78749
78819
  KIMI_REQUIREMENT_FILE = "release-skill-kimi-manual-install.json";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"