release-skill 0.1.4 → 0.1.5

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.
@@ -19947,7 +19947,7 @@ async function writePlanImmutable(planPath, plan) {
19947
19947
  }
19948
19948
  return { planPath, planDigest };
19949
19949
  }
19950
- function validatePlanActionCompleteness(plan) {
19950
+ function validatePlanActionCompleteness(plan, options = {}) {
19951
19951
  const failures = [];
19952
19952
  if (!plan || typeof plan !== "object") {
19953
19953
  return { passed: false, details: { failures: ["plan is null or not an object"], expectedCount: 0, actualCount: 0 } };
@@ -20254,6 +20254,26 @@ function validatePlanActionCompleteness(plan) {
20254
20254
  _checkRequired(action, "parameters.ref", action.parameters?.ref, expectedTag, unitId, failures);
20255
20255
  _checkRequired(action, "parameters.manifestDigest", action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
20256
20256
  }
20257
+ {
20258
+ const raw = action.parameters?.timeoutMs;
20259
+ if (raw === void 0) {
20260
+ if (!options.legacyCompatibility) {
20261
+ failures.push(
20262
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs is missing, expected a valid timeout (30000-900000)`
20263
+ );
20264
+ }
20265
+ } else {
20266
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
20267
+ failures.push(
20268
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
20269
+ );
20270
+ } else if (raw < 3e4 || raw > 9e5) {
20271
+ failures.push(
20272
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be between 30000 and 900000, got: ${raw}`
20273
+ );
20274
+ }
20275
+ }
20276
+ }
20257
20277
  _checkRequired(action, "expected.installed", action.expected?.installed, true, unitId, failures);
20258
20278
  _checkRequired(action, "expected.plugin", action.expected?.plugin, plugin, unitId, failures);
20259
20279
  _checkRequired(action, "expected.marketplace", action.expected?.marketplace, marketplace, unitId, failures);
@@ -20322,6 +20342,26 @@ function validatePlanActionCompleteness(plan) {
20322
20342
  _checkRequired(action, "parameters.ref", action.parameters?.ref, expectedTag, unitId, failures);
20323
20343
  _checkRequired(action, "parameters.manifestDigest", action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
20324
20344
  }
20345
+ {
20346
+ const raw = action.parameters?.timeoutMs;
20347
+ if (raw === void 0) {
20348
+ if (!options.legacyCompatibility) {
20349
+ failures.push(
20350
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs is missing, expected a valid timeout (30000-900000)`
20351
+ );
20352
+ }
20353
+ } else {
20354
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
20355
+ failures.push(
20356
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
20357
+ );
20358
+ } else if (raw < 3e4 || raw > 9e5) {
20359
+ failures.push(
20360
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be between 30000 and 900000, got: ${raw}`
20361
+ );
20362
+ }
20363
+ }
20364
+ }
20325
20365
  _checkRequired(action, "expected.installed", action.expected?.installed, true, unitId, failures);
20326
20366
  _checkRequired(action, "expected.plugin", action.expected?.plugin, plugin, unitId, failures);
20327
20367
  _checkRequired(action, "expected.marketplace", action.expected?.marketplace, marketplace, unitId, failures);
@@ -68159,6 +68199,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68159
68199
  const claudeDist = (unit.distributions ?? []).find((d) => d.type === "claude-plugin");
68160
68200
  if (claudeDist) {
68161
68201
  const identity = marketplaceIdentity(claudeDist);
68202
+ const claudeTimeoutMs = Number.isInteger(claudeDist.timeoutMs) ? claudeDist.timeoutMs : 3e5;
68162
68203
  actions.push({
68163
68204
  id: `claude-marketplace-install-${unit.id}`,
68164
68205
  type: "claude-marketplace-install",
@@ -68170,7 +68211,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68170
68211
  marketplace: identity.marketplace,
68171
68212
  repo: unit.publicRepo,
68172
68213
  version,
68173
- entrySkill: identity.entrySkill
68214
+ entrySkill: identity.entrySkill,
68215
+ timeoutMs: claudeTimeoutMs
68174
68216
  },
68175
68217
  expected: {
68176
68218
  installed: true,
@@ -68185,6 +68227,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68185
68227
  const codexDist = (unit.distributions ?? []).find((d) => d.type === "codex-plugin");
68186
68228
  if (codexDist) {
68187
68229
  const identity = marketplaceIdentity(codexDist);
68230
+ const codexTimeoutMs = Number.isInteger(codexDist.timeoutMs) ? codexDist.timeoutMs : 3e5;
68188
68231
  actions.push({
68189
68232
  id: `codex-marketplace-install-${unit.id}`,
68190
68233
  type: "codex-marketplace-install",
@@ -68196,7 +68239,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68196
68239
  marketplace: identity.marketplace,
68197
68240
  repo: unit.publicRepo,
68198
68241
  version,
68199
- entrySkill: identity.entrySkill
68242
+ entrySkill: identity.entrySkill,
68243
+ timeoutMs: codexTimeoutMs
68200
68244
  },
68201
68245
  expected: {
68202
68246
  installed: true,
@@ -68336,6 +68380,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68336
68380
  const claudeDist = (unit.distributions ?? []).find((d) => d.type === "claude-plugin");
68337
68381
  if (claudeDist) {
68338
68382
  const identity = marketplaceIdentity(claudeDist);
68383
+ const claudeTimeoutMs = Number.isInteger(claudeDist.timeoutMs) ? claudeDist.timeoutMs : 3e5;
68339
68384
  actions.push({
68340
68385
  id: `claude-marketplace-install-${unit.id}`,
68341
68386
  type: "claude-marketplace-install",
@@ -68350,7 +68395,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68350
68395
  version: unitVersion,
68351
68396
  entrySkill: identity.entrySkill,
68352
68397
  snapshotPath: asset.snapshotPath,
68353
- manifestDigest: asset.manifestDigest
68398
+ manifestDigest: asset.manifestDigest,
68399
+ timeoutMs: claudeTimeoutMs
68354
68400
  },
68355
68401
  expected: {
68356
68402
  installed: true,
@@ -68370,6 +68416,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68370
68416
  const codexDist = (unit.distributions ?? []).find((d) => d.type === "codex-plugin");
68371
68417
  if (codexDist) {
68372
68418
  const identity = marketplaceIdentity(codexDist);
68419
+ const codexTimeoutMs = Number.isInteger(codexDist.timeoutMs) ? codexDist.timeoutMs : 3e5;
68373
68420
  actions.push({
68374
68421
  id: `codex-marketplace-install-${unit.id}`,
68375
68422
  type: "codex-marketplace-install",
@@ -68384,7 +68431,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68384
68431
  version: unitVersion,
68385
68432
  entrySkill: identity.entrySkill,
68386
68433
  snapshotPath: asset.snapshotPath,
68387
- manifestDigest: asset.manifestDigest
68434
+ manifestDigest: asset.manifestDigest,
68435
+ timeoutMs: codexTimeoutMs
68388
68436
  },
68389
68437
  expected: {
68390
68438
  installed: true,
@@ -69526,7 +69574,7 @@ async function reconcileRelease(options) {
69526
69574
  }
69527
69575
  await evidence.append({ phase: "safety-gate", gate: "plan-digest", status: "passed" });
69528
69576
  await evidence.append({ phase: "safety-gate", gate: "action-completeness", status: "started" });
69529
- const completenessResult = validatePlanActionCompleteness(plan);
69577
+ const completenessResult = validatePlanActionCompleteness(plan, { legacyCompatibility: true });
69530
69578
  if (!completenessResult.passed) {
69531
69579
  await evidence.append({
69532
69580
  phase: "safety-gate",
@@ -71194,6 +71242,23 @@ function validateMarketplaceParams(params) {
71194
71242
  }
71195
71243
  return { valid: true, error: null };
71196
71244
  }
71245
+ function resolveTimeoutMs(action) {
71246
+ const raw = action?.timeoutMs;
71247
+ if (raw === void 0) {
71248
+ return 3e5;
71249
+ }
71250
+ if (raw === null || typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
71251
+ throw new Error(
71252
+ `action.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
71253
+ );
71254
+ }
71255
+ if (raw < 3e4 || raw > 9e5) {
71256
+ throw new Error(
71257
+ `action.timeoutMs must be between 30000 and 900000, got: ${raw}`
71258
+ );
71259
+ }
71260
+ return raw;
71261
+ }
71197
71262
  async function run4(cmd, args2, options = {}) {
71198
71263
  return execFile9(cmd, args2, {
71199
71264
  shell: false,
@@ -71663,11 +71728,21 @@ function createPluginMarketplaceAdapter(deps = {}) {
71663
71728
  ...baseEnv,
71664
71729
  ...consumer === "claude" ? { HOME: isolatedHome, CLAUDE_CONFIG_DIR: resolve18(isolatedHome, ".claude") } : { HOME: isolatedHome, CODEX_HOME: isolatedHome }
71665
71730
  };
71731
+ let frozenTimeoutMs;
71732
+ try {
71733
+ frozenTimeoutMs = resolveTimeoutMs(action);
71734
+ } catch (timeoutErr) {
71735
+ return createResult({
71736
+ actionType,
71737
+ status: ActionStatus.EXECUTE_FAILED,
71738
+ error: timeoutErr.message
71739
+ });
71740
+ }
71666
71741
  const ref = action.ref ?? `v${action.version}`;
71667
71742
  let addOutput;
71668
71743
  const marketplaceArgs = consumer === "claude" ? ["plugin", "marketplace", "add", `${action.repo}@${ref}`] : ["plugin", "marketplace", "add", action.repo, "--ref", ref, "--json"];
71669
71744
  try {
71670
- const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root });
71745
+ const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71671
71746
  if (consumer === "codex") {
71672
71747
  try {
71673
71748
  addOutput = JSON.parse(addResult.stdout);
@@ -71703,7 +71778,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
71703
71778
  let installOutput;
71704
71779
  const installArgs = consumer === "claude" ? ["plugin", "install", `${action.plugin}@${action.marketplace}`] : ["plugin", "add", `${action.plugin}@${action.marketplace}`, "--json"];
71705
71780
  try {
71706
- const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root });
71781
+ const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71707
71782
  if (consumer === "codex") {
71708
71783
  try {
71709
71784
  installOutput = JSON.parse(installResult.stdout);
@@ -71893,6 +71968,17 @@ function createPluginMarketplaceAdapter(deps = {}) {
71893
71968
  ...baseEnv,
71894
71969
  ...consumer === "claude" ? { HOME: isolatedHome, CLAUDE_CONFIG_DIR: resolve18(isolatedHome, ".claude") } : { HOME: isolatedHome, CODEX_HOME: isolatedHome }
71895
71970
  };
71971
+ let frozenTimeoutMs;
71972
+ try {
71973
+ frozenTimeoutMs = resolveTimeoutMs(action);
71974
+ } catch (timeoutErr) {
71975
+ return createResult({
71976
+ actionType,
71977
+ status: ActionStatus.OBSERVED,
71978
+ observation: { installed: false, error: timeoutErr.message },
71979
+ error: timeoutErr.message
71980
+ });
71981
+ }
71896
71982
  let evidence = null;
71897
71983
  try {
71898
71984
  const evidenceRaw = await readFile16(resolve18(runDir, "evidence", `${consumer}-${action.plugin}`, "release-skill-install-evidence.json"), "utf8");
@@ -71920,7 +72006,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
71920
72006
  const listArgs = consumer === "claude" ? ["plugin", "list", "--json"] : ["plugin", "list", "--json"];
71921
72007
  let listOutput;
71922
72008
  try {
71923
- const result = await exec(cliCmd, listArgs, { env, cwd: context.root });
72009
+ const result = await exec(cliCmd, listArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71924
72010
  listOutput = JSON.parse(result.stdout);
71925
72011
  } catch (listErr) {
71926
72012
  return createResult({
@@ -72248,6 +72334,7 @@ var init_plugin_marketplace = __esm({
72248
72334
  STRICT_SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
72249
72335
  __name(validateSafeRef, "validateSafeRef");
72250
72336
  __name(validateMarketplaceParams, "validateMarketplaceParams");
72337
+ __name(resolveTimeoutMs, "resolveTimeoutMs");
72251
72338
  __name(run4, "run");
72252
72339
  __name(validateManifestFile, "validateManifestFile");
72253
72340
  __name(checkRequiredFiles, "checkRequiredFiles");
@@ -72668,7 +72755,7 @@ async function verifyRelease(options) {
72668
72755
  }
72669
72756
  validateApproval(plan, approval, { clock: clockFn, requireUnexpired: false });
72670
72757
  }
72671
- const completenessResult = validatePlanActionCompleteness(plan);
72758
+ const completenessResult = validatePlanActionCompleteness(plan, { legacyCompatibility: true });
72672
72759
  if (!completenessResult.passed) {
72673
72760
  throw new ReleaseError(
72674
72761
  GATE_FAILED,
@@ -200,6 +200,12 @@
200
200
  },
201
201
  "smokeExpectedJson": {
202
202
  "type": "object"
203
+ },
204
+ "timeoutMs": {
205
+ "type": "integer",
206
+ "minimum": 30000,
207
+ "maximum": 900000,
208
+ "description": "Timeout in milliseconds for plugin marketplace install commands. Only valid for claude-plugin and codex-plugin distributions."
203
209
  }
204
210
  },
205
211
  "allOf": [
@@ -219,6 +225,23 @@
219
225
  ]
220
226
  }
221
227
  },
228
+ {
229
+ "if": {
230
+ "required": [
231
+ "timeoutMs"
232
+ ]
233
+ },
234
+ "then": {
235
+ "properties": {
236
+ "type": {
237
+ "enum": [
238
+ "claude-plugin",
239
+ "codex-plugin"
240
+ ]
241
+ }
242
+ }
243
+ }
244
+ },
222
245
  {
223
246
  "if": {
224
247
  "properties": {
@@ -19947,7 +19947,7 @@ async function writePlanImmutable(planPath, plan) {
19947
19947
  }
19948
19948
  return { planPath, planDigest };
19949
19949
  }
19950
- function validatePlanActionCompleteness(plan) {
19950
+ function validatePlanActionCompleteness(plan, options = {}) {
19951
19951
  const failures = [];
19952
19952
  if (!plan || typeof plan !== "object") {
19953
19953
  return { passed: false, details: { failures: ["plan is null or not an object"], expectedCount: 0, actualCount: 0 } };
@@ -20254,6 +20254,26 @@ function validatePlanActionCompleteness(plan) {
20254
20254
  _checkRequired(action, "parameters.ref", action.parameters?.ref, expectedTag, unitId, failures);
20255
20255
  _checkRequired(action, "parameters.manifestDigest", action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
20256
20256
  }
20257
+ {
20258
+ const raw = action.parameters?.timeoutMs;
20259
+ if (raw === void 0) {
20260
+ if (!options.legacyCompatibility) {
20261
+ failures.push(
20262
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs is missing, expected a valid timeout (30000-900000)`
20263
+ );
20264
+ }
20265
+ } else {
20266
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
20267
+ failures.push(
20268
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
20269
+ );
20270
+ } else if (raw < 3e4 || raw > 9e5) {
20271
+ failures.push(
20272
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be between 30000 and 900000, got: ${raw}`
20273
+ );
20274
+ }
20275
+ }
20276
+ }
20257
20277
  _checkRequired(action, "expected.installed", action.expected?.installed, true, unitId, failures);
20258
20278
  _checkRequired(action, "expected.plugin", action.expected?.plugin, plugin, unitId, failures);
20259
20279
  _checkRequired(action, "expected.marketplace", action.expected?.marketplace, marketplace, unitId, failures);
@@ -20322,6 +20342,26 @@ function validatePlanActionCompleteness(plan) {
20322
20342
  _checkRequired(action, "parameters.ref", action.parameters?.ref, expectedTag, unitId, failures);
20323
20343
  _checkRequired(action, "parameters.manifestDigest", action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
20324
20344
  }
20345
+ {
20346
+ const raw = action.parameters?.timeoutMs;
20347
+ if (raw === void 0) {
20348
+ if (!options.legacyCompatibility) {
20349
+ failures.push(
20350
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs is missing, expected a valid timeout (30000-900000)`
20351
+ );
20352
+ }
20353
+ } else {
20354
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
20355
+ failures.push(
20356
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
20357
+ );
20358
+ } else if (raw < 3e4 || raw > 9e5) {
20359
+ failures.push(
20360
+ `unit "${unitId}", action "${action.id}": parameters.timeoutMs must be between 30000 and 900000, got: ${raw}`
20361
+ );
20362
+ }
20363
+ }
20364
+ }
20325
20365
  _checkRequired(action, "expected.installed", action.expected?.installed, true, unitId, failures);
20326
20366
  _checkRequired(action, "expected.plugin", action.expected?.plugin, plugin, unitId, failures);
20327
20367
  _checkRequired(action, "expected.marketplace", action.expected?.marketplace, marketplace, unitId, failures);
@@ -68159,6 +68199,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68159
68199
  const claudeDist = (unit.distributions ?? []).find((d) => d.type === "claude-plugin");
68160
68200
  if (claudeDist) {
68161
68201
  const identity = marketplaceIdentity(claudeDist);
68202
+ const claudeTimeoutMs = Number.isInteger(claudeDist.timeoutMs) ? claudeDist.timeoutMs : 3e5;
68162
68203
  actions.push({
68163
68204
  id: `claude-marketplace-install-${unit.id}`,
68164
68205
  type: "claude-marketplace-install",
@@ -68170,7 +68211,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68170
68211
  marketplace: identity.marketplace,
68171
68212
  repo: unit.publicRepo,
68172
68213
  version,
68173
- entrySkill: identity.entrySkill
68214
+ entrySkill: identity.entrySkill,
68215
+ timeoutMs: claudeTimeoutMs
68174
68216
  },
68175
68217
  expected: {
68176
68218
  installed: true,
@@ -68185,6 +68227,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68185
68227
  const codexDist = (unit.distributions ?? []).find((d) => d.type === "codex-plugin");
68186
68228
  if (codexDist) {
68187
68229
  const identity = marketplaceIdentity(codexDist);
68230
+ const codexTimeoutMs = Number.isInteger(codexDist.timeoutMs) ? codexDist.timeoutMs : 3e5;
68188
68231
  actions.push({
68189
68232
  id: `codex-marketplace-install-${unit.id}`,
68190
68233
  type: "codex-marketplace-install",
@@ -68196,7 +68239,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68196
68239
  marketplace: identity.marketplace,
68197
68240
  repo: unit.publicRepo,
68198
68241
  version,
68199
- entrySkill: identity.entrySkill
68242
+ entrySkill: identity.entrySkill,
68243
+ timeoutMs: codexTimeoutMs
68200
68244
  },
68201
68245
  expected: {
68202
68246
  installed: true,
@@ -68336,6 +68380,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68336
68380
  const claudeDist = (unit.distributions ?? []).find((d) => d.type === "claude-plugin");
68337
68381
  if (claudeDist) {
68338
68382
  const identity = marketplaceIdentity(claudeDist);
68383
+ const claudeTimeoutMs = Number.isInteger(claudeDist.timeoutMs) ? claudeDist.timeoutMs : 3e5;
68339
68384
  actions.push({
68340
68385
  id: `claude-marketplace-install-${unit.id}`,
68341
68386
  type: "claude-marketplace-install",
@@ -68350,7 +68395,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68350
68395
  version: unitVersion,
68351
68396
  entrySkill: identity.entrySkill,
68352
68397
  snapshotPath: asset.snapshotPath,
68353
- manifestDigest: asset.manifestDigest
68398
+ manifestDigest: asset.manifestDigest,
68399
+ timeoutMs: claudeTimeoutMs
68354
68400
  },
68355
68401
  expected: {
68356
68402
  installed: true,
@@ -68370,6 +68416,7 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68370
68416
  const codexDist = (unit.distributions ?? []).find((d) => d.type === "codex-plugin");
68371
68417
  if (codexDist) {
68372
68418
  const identity = marketplaceIdentity(codexDist);
68419
+ const codexTimeoutMs = Number.isInteger(codexDist.timeoutMs) ? codexDist.timeoutMs : 3e5;
68373
68420
  actions.push({
68374
68421
  id: `codex-marketplace-install-${unit.id}`,
68375
68422
  type: "codex-marketplace-install",
@@ -68384,7 +68431,8 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
68384
68431
  version: unitVersion,
68385
68432
  entrySkill: identity.entrySkill,
68386
68433
  snapshotPath: asset.snapshotPath,
68387
- manifestDigest: asset.manifestDigest
68434
+ manifestDigest: asset.manifestDigest,
68435
+ timeoutMs: codexTimeoutMs
68388
68436
  },
68389
68437
  expected: {
68390
68438
  installed: true,
@@ -69526,7 +69574,7 @@ async function reconcileRelease(options) {
69526
69574
  }
69527
69575
  await evidence.append({ phase: "safety-gate", gate: "plan-digest", status: "passed" });
69528
69576
  await evidence.append({ phase: "safety-gate", gate: "action-completeness", status: "started" });
69529
- const completenessResult = validatePlanActionCompleteness(plan);
69577
+ const completenessResult = validatePlanActionCompleteness(plan, { legacyCompatibility: true });
69530
69578
  if (!completenessResult.passed) {
69531
69579
  await evidence.append({
69532
69580
  phase: "safety-gate",
@@ -71194,6 +71242,23 @@ function validateMarketplaceParams(params) {
71194
71242
  }
71195
71243
  return { valid: true, error: null };
71196
71244
  }
71245
+ function resolveTimeoutMs(action) {
71246
+ const raw = action?.timeoutMs;
71247
+ if (raw === void 0) {
71248
+ return 3e5;
71249
+ }
71250
+ if (raw === null || typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
71251
+ throw new Error(
71252
+ `action.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`
71253
+ );
71254
+ }
71255
+ if (raw < 3e4 || raw > 9e5) {
71256
+ throw new Error(
71257
+ `action.timeoutMs must be between 30000 and 900000, got: ${raw}`
71258
+ );
71259
+ }
71260
+ return raw;
71261
+ }
71197
71262
  async function run4(cmd, args2, options = {}) {
71198
71263
  return execFile9(cmd, args2, {
71199
71264
  shell: false,
@@ -71663,11 +71728,21 @@ function createPluginMarketplaceAdapter(deps = {}) {
71663
71728
  ...baseEnv,
71664
71729
  ...consumer === "claude" ? { HOME: isolatedHome, CLAUDE_CONFIG_DIR: resolve18(isolatedHome, ".claude") } : { HOME: isolatedHome, CODEX_HOME: isolatedHome }
71665
71730
  };
71731
+ let frozenTimeoutMs;
71732
+ try {
71733
+ frozenTimeoutMs = resolveTimeoutMs(action);
71734
+ } catch (timeoutErr) {
71735
+ return createResult({
71736
+ actionType,
71737
+ status: ActionStatus.EXECUTE_FAILED,
71738
+ error: timeoutErr.message
71739
+ });
71740
+ }
71666
71741
  const ref = action.ref ?? `v${action.version}`;
71667
71742
  let addOutput;
71668
71743
  const marketplaceArgs = consumer === "claude" ? ["plugin", "marketplace", "add", `${action.repo}@${ref}`] : ["plugin", "marketplace", "add", action.repo, "--ref", ref, "--json"];
71669
71744
  try {
71670
- const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root });
71745
+ const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71671
71746
  if (consumer === "codex") {
71672
71747
  try {
71673
71748
  addOutput = JSON.parse(addResult.stdout);
@@ -71703,7 +71778,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
71703
71778
  let installOutput;
71704
71779
  const installArgs = consumer === "claude" ? ["plugin", "install", `${action.plugin}@${action.marketplace}`] : ["plugin", "add", `${action.plugin}@${action.marketplace}`, "--json"];
71705
71780
  try {
71706
- const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root });
71781
+ const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71707
71782
  if (consumer === "codex") {
71708
71783
  try {
71709
71784
  installOutput = JSON.parse(installResult.stdout);
@@ -71893,6 +71968,17 @@ function createPluginMarketplaceAdapter(deps = {}) {
71893
71968
  ...baseEnv,
71894
71969
  ...consumer === "claude" ? { HOME: isolatedHome, CLAUDE_CONFIG_DIR: resolve18(isolatedHome, ".claude") } : { HOME: isolatedHome, CODEX_HOME: isolatedHome }
71895
71970
  };
71971
+ let frozenTimeoutMs;
71972
+ try {
71973
+ frozenTimeoutMs = resolveTimeoutMs(action);
71974
+ } catch (timeoutErr) {
71975
+ return createResult({
71976
+ actionType,
71977
+ status: ActionStatus.OBSERVED,
71978
+ observation: { installed: false, error: timeoutErr.message },
71979
+ error: timeoutErr.message
71980
+ });
71981
+ }
71896
71982
  let evidence = null;
71897
71983
  try {
71898
71984
  const evidenceRaw = await readFile16(resolve18(runDir, "evidence", `${consumer}-${action.plugin}`, "release-skill-install-evidence.json"), "utf8");
@@ -71920,7 +72006,7 @@ function createPluginMarketplaceAdapter(deps = {}) {
71920
72006
  const listArgs = consumer === "claude" ? ["plugin", "list", "--json"] : ["plugin", "list", "--json"];
71921
72007
  let listOutput;
71922
72008
  try {
71923
- const result = await exec(cliCmd, listArgs, { env, cwd: context.root });
72009
+ const result = await exec(cliCmd, listArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
71924
72010
  listOutput = JSON.parse(result.stdout);
71925
72011
  } catch (listErr) {
71926
72012
  return createResult({
@@ -72248,6 +72334,7 @@ var init_plugin_marketplace = __esm({
72248
72334
  STRICT_SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
72249
72335
  __name(validateSafeRef, "validateSafeRef");
72250
72336
  __name(validateMarketplaceParams, "validateMarketplaceParams");
72337
+ __name(resolveTimeoutMs, "resolveTimeoutMs");
72251
72338
  __name(run4, "run");
72252
72339
  __name(validateManifestFile, "validateManifestFile");
72253
72340
  __name(checkRequiredFiles, "checkRequiredFiles");
@@ -72668,7 +72755,7 @@ async function verifyRelease(options) {
72668
72755
  }
72669
72756
  validateApproval(plan, approval, { clock: clockFn, requireUnexpired: false });
72670
72757
  }
72671
- const completenessResult = validatePlanActionCompleteness(plan);
72758
+ const completenessResult = validatePlanActionCompleteness(plan, { legacyCompatibility: true });
72672
72759
  if (!completenessResult.passed) {
72673
72760
  throw new ReleaseError(
72674
72761
  GATE_FAILED,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "repository": {
6
6
  "type": "git",
@@ -200,6 +200,12 @@
200
200
  },
201
201
  "smokeExpectedJson": {
202
202
  "type": "object"
203
+ },
204
+ "timeoutMs": {
205
+ "type": "integer",
206
+ "minimum": 30000,
207
+ "maximum": 900000,
208
+ "description": "Timeout in milliseconds for plugin marketplace install commands. Only valid for claude-plugin and codex-plugin distributions."
203
209
  }
204
210
  },
205
211
  "allOf": [
@@ -219,6 +225,23 @@
219
225
  ]
220
226
  }
221
227
  },
228
+ {
229
+ "if": {
230
+ "required": [
231
+ "timeoutMs"
232
+ ]
233
+ },
234
+ "then": {
235
+ "properties": {
236
+ "type": {
237
+ "enum": [
238
+ "claude-plugin",
239
+ "codex-plugin"
240
+ ]
241
+ }
242
+ }
243
+ }
244
+ },
222
245
  {
223
246
  "if": {
224
247
  "properties": {
@@ -185,6 +185,42 @@ function validateMarketplaceParams(params) {
185
185
  }
186
186
 
187
187
 
188
+ /**
189
+ * Resolve and validate the frozen timeoutMs from the expanded adapter action.
190
+ *
191
+ * The publish/reconcile/verify call path expands plan actions as
192
+ * `{ actionType, ...action.parameters }`, so `parameters.timeoutMs` in the
193
+ * plan becomes `action.timeoutMs` at the adapter level. This function reads
194
+ * from the top-level action, not from a nested `parameters` sub-object.
195
+ *
196
+ * Rules:
197
+ * - Missing field (undefined): returns 300000 default (legacy compatibility).
198
+ * - Present but null/invalid (null, string, NaN, Infinity, non-integer,
199
+ * out of range): fail-closed, throws.
200
+ * - Valid integer in [30000, 900000]: returns the value as-is.
201
+ *
202
+ * @param {object} action - The expanded adapter action (top-level).
203
+ * @returns {number} Validated timeout in milliseconds.
204
+ * @throws {Error} If the value is present but invalid.
205
+ */
206
+ function resolveTimeoutMs(action) {
207
+ const raw = action?.timeoutMs;
208
+ if (raw === undefined) {
209
+ return 300000;
210
+ }
211
+ if (raw === null || typeof raw !== 'number' || !Number.isFinite(raw) || !Number.isInteger(raw)) {
212
+ throw new Error(
213
+ `action.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`,
214
+ );
215
+ }
216
+ if (raw < 30000 || raw > 900000) {
217
+ throw new Error(
218
+ `action.timeoutMs must be between 30000 and 900000, got: ${raw}`,
219
+ );
220
+ }
221
+ return raw;
222
+ }
223
+
188
224
  /**
189
225
  * Run a CLI command using execFile (never shell: true).
190
226
  */
@@ -792,6 +828,23 @@ export function createPluginMarketplaceAdapter(deps = {}) {
792
828
  };
793
829
  // Ensure real HOME/CODEX_HOME don't leak back (already overridden above)
794
830
 
831
+ // Resolve frozen timeoutMs from the expanded action (top-level,
832
+ // not action.parameters -- the publish/reconcile/verify call path
833
+ // expands plan action as { actionType, ...action.parameters }).
834
+ // Default to 300000 for old plans that lack the field.
835
+ // Fail closed on invalid values (null, non-integer, non-finite,
836
+ // out of range).
837
+ let frozenTimeoutMs;
838
+ try {
839
+ frozenTimeoutMs = resolveTimeoutMs(action);
840
+ } catch (timeoutErr) {
841
+ return createResult({
842
+ actionType,
843
+ status: ActionStatus.EXECUTE_FAILED,
844
+ error: timeoutErr.message,
845
+ });
846
+ }
847
+
795
848
  // Step 1: Add marketplace
796
849
  const ref = action.ref ?? `v${action.version}`;
797
850
  let addOutput;
@@ -799,7 +852,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
799
852
  ? ['plugin', 'marketplace', 'add', `${action.repo}@${ref}`]
800
853
  : ['plugin', 'marketplace', 'add', action.repo, '--ref', ref, '--json'];
801
854
  try {
802
- const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root });
855
+ const addResult = await exec(cliCmd, marketplaceArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
803
856
  if (consumer === 'codex') {
804
857
  try {
805
858
  addOutput = JSON.parse(addResult.stdout);
@@ -839,7 +892,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
839
892
  ? ['plugin', 'install', `${action.plugin}@${action.marketplace}`]
840
893
  : ['plugin', 'add', `${action.plugin}@${action.marketplace}`, '--json'];
841
894
  try {
842
- const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root });
895
+ const installResult = await exec(cliCmd, installArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
843
896
  if (consumer === 'codex') {
844
897
  try {
845
898
  installOutput = JSON.parse(installResult.stdout);
@@ -1053,6 +1106,20 @@ export function createPluginMarketplaceAdapter(deps = {}) {
1053
1106
  : { HOME: isolatedHome, CODEX_HOME: isolatedHome }),
1054
1107
  };
1055
1108
 
1109
+ // Resolve frozen timeoutMs from the expanded action (top-level).
1110
+ // Default to 300000 for old plans. Fail closed on invalid values.
1111
+ let frozenTimeoutMs;
1112
+ try {
1113
+ frozenTimeoutMs = resolveTimeoutMs(action);
1114
+ } catch (timeoutErr) {
1115
+ return createResult({
1116
+ actionType,
1117
+ status: ActionStatus.OBSERVED,
1118
+ observation: { installed: false, error: timeoutErr.message },
1119
+ error: timeoutErr.message,
1120
+ });
1121
+ }
1122
+
1056
1123
  // Read execute evidence — mandatory for observe validation
1057
1124
  let evidence = null;
1058
1125
  try {
@@ -1095,7 +1162,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
1095
1162
 
1096
1163
  let listOutput;
1097
1164
  try {
1098
- const result = await exec(cliCmd, listArgs, { env, cwd: context.root });
1165
+ const result = await exec(cliCmd, listArgs, { env, cwd: context.root, timeout: frozenTimeoutMs });
1099
1166
  listOutput = JSON.parse(result.stdout);
1100
1167
  } catch (listErr) {
1101
1168
  return createResult({