@unbrained/pm-cli 2026.11.1 → 2026.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/CONTRIBUTING.md +1 -1
  4. package/dist/cli/error-guidance.d.ts +9 -0
  5. package/dist/cli/error-guidance.js +4 -3
  6. package/dist/cli/main.js +1 -1
  7. package/dist/cli/migration-gates.js +1 -1
  8. package/dist/cli/runtime/field-route-retry.d.ts +15 -0
  9. package/dist/cli/runtime/field-route-retry.js +2 -0
  10. package/dist/cli-bundle/bundle-manifest.json +1 -1
  11. package/dist/cli-bundle/chunks/{chunk-2GK2IVU5.js → chunk-2CBHQXYJ.js} +3 -3
  12. package/dist/cli-bundle/chunks/chunk-32BCU6HN.js +115 -0
  13. package/dist/cli-bundle/chunks/{chunk-MTRIGD7M.js → chunk-5IPXDZKU.js} +42 -42
  14. package/dist/cli-bundle/chunks/{chunk-O56O7NIE.js → chunk-5JZMF4M5.js} +2 -2
  15. package/dist/cli-bundle/chunks/{chunk-TYERATXH.js → chunk-7ZEFHFAU.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-B6UTLYLM.js +3 -0
  17. package/dist/cli-bundle/chunks/{chunk-N45IXPCA.js → chunk-DEJHTFEF.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-ICT6E4IW.js → chunk-HBKD5HZB.js} +2 -2
  19. package/dist/cli-bundle/chunks/{chunk-6PKWKVS6.js → chunk-JPKIUWLG.js} +2 -2
  20. package/dist/cli-bundle/chunks/{chunk-WFBYKDB2.js → chunk-L6LCLC37.js} +2 -2
  21. package/dist/cli-bundle/chunks/{chunk-Y7CKVU56.js → chunk-MQJXZX3Q.js} +2 -2
  22. package/dist/cli-bundle/chunks/chunk-NR4KABHC.js +31 -0
  23. package/dist/cli-bundle/chunks/{chunk-WX25MU4N.js → chunk-O4ZLEN4T.js} +3 -3
  24. package/dist/cli-bundle/chunks/{chunk-F3ECOBKA.js → chunk-PWD5RY2D.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-SBVX5CMV.js → chunk-RSAN2YUU.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-NILQ2ZMV.js → chunk-TZ3D3DPQ.js} +2 -2
  27. package/dist/cli-bundle/chunks/{chunk-MRUA2Z43.js → chunk-UJMQP2HP.js} +2 -2
  28. package/dist/cli-bundle/chunks/{chunk-TMPO4CTZ.js → chunk-UYNMKUGA.js} +2 -2
  29. package/dist/cli-bundle/chunks/{chunk-UUN3WNMQ.js → chunk-XRMYA4R7.js} +3 -3
  30. package/dist/cli-bundle/chunks/{chunk-UEEG47TU.js → chunk-Y7H55PCY.js} +2 -2
  31. package/dist/cli-bundle/chunks/{chunk-5KW4WUAK.js → chunk-YWS4XV4I.js} +4 -4
  32. package/dist/cli-bundle/chunks/chunk-ZOAJNPBL.js +129 -0
  33. package/dist/cli-bundle/chunks/{indexed-receipt-blobs-KGMJVFH2.js → indexed-receipt-blobs-I6RQGD6Y.js} +2 -2
  34. package/dist/cli-bundle/chunks/item-transaction-ZHLJTBD7.js +2 -0
  35. package/dist/cli-bundle/chunks/{mutation-admission-BOOCJPCU.js → mutation-admission-ZQMRMGIB.js} +2 -2
  36. package/dist/cli-bundle/chunks/{register-list-query-23OX36Q3.js → register-list-query-I6AZA3IX.js} +2 -2
  37. package/dist/cli-bundle/chunks/{register-mutation-JPJ5YNDX.js → register-mutation-3DAXCLXR.js} +2 -2
  38. package/dist/cli-bundle/chunks/{register-operations-IGR7ERY5.js → register-operations-G3QGGQ5Z.js} +2 -2
  39. package/dist/cli-bundle/chunks/{register-setup-BMIFKZZP.js → register-setup-LDXHMNOT.js} +2 -2
  40. package/dist/cli-bundle/chunks/{settlement-hook-YWSWFOZY.js → settlement-hook-JHXLUBEN.js} +2 -2
  41. package/dist/cli-bundle/chunks/{stage-ON7CYYPM.js → stage-7H36OTDJ.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-3XGKBVJ3.js +2 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-WX2RVS2Y.js → chunk-4HY6EGPB.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/{chunk-3NYWMUX3.js → chunk-D4XQOMG4.js} +3 -3
  45. package/dist/cli-bundle/focused-chunks/{chunk-UQIBGLDR.js → chunk-F3X6UWMI.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-JLXGRDTG.js → chunk-G6ERMFJ5.js} +3 -3
  47. package/dist/cli-bundle/focused-chunks/{chunk-VFQAZLKO.js → chunk-HJ27QLZV.js} +2 -2
  48. package/dist/cli-bundle/focused-chunks/{chunk-5TWS5KOM.js → chunk-HOXIIZEO.js} +2 -2
  49. package/dist/cli-bundle/focused-chunks/chunk-HUXZCPWT.js +26 -0
  50. package/dist/cli-bundle/focused-chunks/{chunk-LVA76N27.js → chunk-I5FODKYB.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/{chunk-DFUTCKUF.js → chunk-IDAYTYNN.js} +2 -2
  52. package/dist/cli-bundle/focused-chunks/{chunk-2Y77HWIX.js → chunk-IKXJ57XA.js} +2 -2
  53. package/dist/cli-bundle/focused-chunks/{chunk-RPUDOJVB.js → chunk-LKN2WGGB.js} +2 -2
  54. package/dist/cli-bundle/focused-chunks/chunk-MOY6KZGJ.js +44 -0
  55. package/dist/cli-bundle/focused-chunks/{chunk-PAT6QF63.js → chunk-PWUTFKSI.js} +2 -2
  56. package/dist/cli-bundle/focused-chunks/chunk-PZRB7IXH.js +91 -0
  57. package/dist/cli-bundle/focused-chunks/chunk-RY4QEMAE.js +2 -0
  58. package/dist/cli-bundle/focused-chunks/{chunk-6GMYW42K.js → chunk-TYHDZUL4.js} +2 -2
  59. package/dist/cli-bundle/focused-chunks/chunk-WSYPVBKS.js +80 -0
  60. package/dist/cli-bundle/focused-chunks/{chunk-Z7GIVH6B.js → chunk-WXLG7HZ5.js} +2 -2
  61. package/dist/cli-bundle/focused-chunks/{chunk-5WCQFH6D.js → chunk-WZDFCVLX.js} +2 -2
  62. package/dist/cli-bundle/focused-chunks/{chunk-VS2ZFPXD.js → chunk-YGO3FWA2.js} +2 -2
  63. package/dist/cli-bundle/focused-chunks/chunk-YVLAP3BL.js +23 -0
  64. package/dist/cli-bundle/focused-chunks/{chunk-6OKPYCLU.js → chunk-ZQRCVRGS.js} +54 -54
  65. package/dist/cli-bundle/focused-chunks/chunk-ZYXQACPB.js +2 -0
  66. package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-KHWT44EK.js → indexed-receipt-blobs-7XGVHYAO.js} +2 -2
  67. package/dist/cli-bundle/focused-chunks/{item-transaction-7VLPA2SU.js → item-transaction-PZ2FQ6B2.js} +2 -2
  68. package/dist/cli-bundle/focused-chunks/{mutation-admission-2DH27R5Y.js → mutation-admission-XOOK6XFH.js} +2 -2
  69. package/dist/cli-bundle/focused-chunks/{settlement-hook-SJV52ZTV.js → settlement-hook-H56537UV.js} +2 -2
  70. package/dist/cli-bundle/focused-chunks/{stage-FYOBWQA7.js → stage-4SQHVI3O.js} +2 -2
  71. package/dist/cli-bundle/main.js +30 -29
  72. package/dist/cli-bundle/sdk-authoring.js +1 -1
  73. package/dist/cli-bundle/sdk-contracts.js +1 -1
  74. package/dist/cli-bundle/sdk-core.js +1 -1
  75. package/dist/cli-bundle/sdk-governance.js +1 -1
  76. package/dist/cli-bundle/sdk-graph.js +1 -1
  77. package/dist/cli-bundle/sdk-merge.js +57 -57
  78. package/dist/cli-bundle/sdk-query.js +1 -1
  79. package/dist/cli-bundle/sdk-runtime.js +1 -1
  80. package/dist/cli-bundle/sdk-testing.js +1 -1
  81. package/dist/cli-bundle/sdk.js +11 -11
  82. package/dist/core/extensions/extension-hook-runtime.js +1 -1
  83. package/dist/core/extensions/extension-types.d.ts +5 -0
  84. package/dist/core/extensions/extension-types.js +1 -1
  85. package/dist/core/extensions/item-fields.d.ts +1 -1
  86. package/dist/core/extensions/item-fields.js +1 -1
  87. package/dist/core/item/parse.js +2 -2
  88. package/dist/core/lock/lock-confinement.d.ts +55 -0
  89. package/dist/core/lock/lock-confinement.js +2 -0
  90. package/dist/core/lock/lock-gc.js +1 -1
  91. package/dist/core/lock/lock.js +2 -2
  92. package/dist/core/schema/runtime-field-values.d.ts +30 -0
  93. package/dist/core/schema/runtime-field-values.js +1 -1
  94. package/dist/core/sentry/helpers.js +1 -1
  95. package/dist/core/sentry/instrument.js +1 -1
  96. package/dist/core/shared/error-details.d.ts +98 -0
  97. package/dist/core/shared/error-details.js +2 -0
  98. package/dist/core/shared/errors.d.ts +43 -0
  99. package/dist/core/shared/errors.js +1 -1
  100. package/dist/core/store/settings-sections.d.ts +61 -0
  101. package/dist/core/store/settings-sections.js +3 -0
  102. package/dist/core/store/settings-validator.d.ts +4 -0
  103. package/dist/core/store/settings-validator.js +1 -1
  104. package/dist/core/store/settings.d.ts +29 -2
  105. package/dist/core/store/settings.js +4 -3
  106. package/dist/core/telemetry/consent.js +2 -2
  107. package/dist/core/telemetry/runtime.js +2 -2
  108. package/dist/core/test/background-runs.js +3 -3
  109. package/dist/mcp/server.js +2 -2
  110. package/dist/sdk/config.js +1 -1
  111. package/dist/sdk/errors.d.ts +6 -2
  112. package/dist/sdk/errors.js +1 -1
  113. package/dist/sdk/extension-command-context.js +2 -2
  114. package/dist/sdk/generated/error-code-catalog-rows.d.ts +39 -0
  115. package/dist/sdk/generated/error-code-catalog-rows.js +2 -0
  116. package/dist/sdk/generated/generated-error-code-catalog-part-1.d.ts +3 -3
  117. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  118. package/dist/sdk/generated/generated-error-code-catalog-part-2.d.ts +3 -3
  119. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  120. package/dist/sdk/generated-error-code-catalog.js +1 -1
  121. package/dist/sdk/index.d.ts +1 -1
  122. package/dist/sdk/index.js +1 -1
  123. package/dist/sdk/lifecycle/core-field-recovery.d.ts +26 -0
  124. package/dist/sdk/lifecycle/core-field-recovery.js +1 -1
  125. package/dist/sdk/lifecycle/create.js +1 -1
  126. package/dist/sdk/lifecycle/update.js +2 -2
  127. package/dist/sdk/mcp/discovery.js +1 -1
  128. package/dist/sdk/public-surface.json +1 -1
  129. package/dist/sdk/runtime-primitives.d.ts +2 -1
  130. package/dist/sdk/runtime-primitives.js +1 -1
  131. package/dist/sdk/runtime.d.ts +1 -1
  132. package/dist/sdk/runtime.js +1 -1
  133. package/dist/sdk/test/entry.d.ts +5 -0
  134. package/dist/sdk/test/entry.js +2 -2
  135. package/dist/sdk/test/execution.d.ts +1 -2
  136. package/dist/sdk/test/execution.js +2 -2
  137. package/dist/sdk/test/linked-command-detection.d.ts +10 -0
  138. package/dist/sdk/test/linked-command-detection.js +1 -1
  139. package/dist/sdk/test/update.d.ts +2 -0
  140. package/dist/sdk/test/update.js +1 -1
  141. package/docs/AGENT_EVIDENCE_CONSISTENCY.md +10 -0
  142. package/docs/CONFIGURATION.md +9 -0
  143. package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +9 -6
  144. package/docs/EXTENSIONS.md +2 -2
  145. package/docs/LINKED_TEST_EDITS.md +38 -5
  146. package/docs/RELEASING.md +22 -2
  147. package/docs/SDK.md +103 -1
  148. package/docs/SDK_CONFIGURATION_SAFETY.md +55 -0
  149. package/docs/TESTING.md +2 -1
  150. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +15 -3
  151. package/marketplace.json +1 -1
  152. package/package.json +9 -3
  153. package/packages/pm-beads/package.json +1 -1
  154. package/packages/pm-calendar/package.json +1 -1
  155. package/packages/pm-command-kit/package.json +1 -1
  156. package/packages/pm-digital-twin/package.json +1 -1
  157. package/packages/pm-governance-audit/package.json +1 -1
  158. package/packages/pm-guide-shell/package.json +1 -1
  159. package/packages/pm-kanban/package.json +1 -1
  160. package/packages/pm-lifecycle-hooks/package.json +1 -1
  161. package/packages/pm-linked-test-adapters/package.json +1 -1
  162. package/packages/pm-search-advanced/package.json +1 -1
  163. package/packages/pm-templates/package.json +1 -1
  164. package/packages/pm-todos/package.json +1 -1
  165. package/packages/pm-vcs/package.json +1 -1
  166. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  167. package/plugins/pm-claude/package.json +1 -1
  168. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  169. package/plugins/pm-codex/package.json +1 -1
  170. package/plugins/pm-codex/plugin.json +1 -1
  171. package/runtime-dependencies.json +1 -1
  172. package/dist/cli-bundle/chunks/chunk-5WVE7XBS.js +0 -114
  173. package/dist/cli-bundle/chunks/chunk-E74BORFX.js +0 -128
  174. package/dist/cli-bundle/chunks/chunk-MP2DQJH2.js +0 -3
  175. package/dist/cli-bundle/chunks/chunk-O6TFUV6M.js +0 -31
  176. package/dist/cli-bundle/chunks/item-transaction-Q5S5YLEJ.js +0 -2
  177. package/dist/cli-bundle/focused-chunks/chunk-3Z53POXU.js +0 -2
  178. package/dist/cli-bundle/focused-chunks/chunk-7WJDRZOR.js +0 -22
  179. package/dist/cli-bundle/focused-chunks/chunk-AL7SOFUL.js +0 -2
  180. package/dist/cli-bundle/focused-chunks/chunk-CIQ6YWCW.js +0 -26
  181. package/dist/cli-bundle/focused-chunks/chunk-GE755ODZ.js +0 -2
  182. package/dist/cli-bundle/focused-chunks/chunk-JCR32IAO.js +0 -91
  183. package/dist/cli-bundle/focused-chunks/chunk-M22TI7LP.js +0 -79
  184. package/dist/cli-bundle/focused-chunks/chunk-SXZM4PSS.js +0 -44
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3a15b0ff-f638-5368-95a7-0d5d9abb3f21")}catch{}})();import{cloneCommandOptionsSnapshot,cloneContextSnapshot,cloneGlobalOptionsSnapshot,normalizeCommandName}from"./extension-runtime-helpers.js";import{stableValueEquals}from"../shared/serialization.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";const DEFAULT_MUTATION_GUARD_TIMEOUT_MS=5e3;async function executeRegisteredHooks(entries,hookName,context){const warnings=[];for(const entry of entries)try{await entry.run(cloneContextSnapshot(context))}catch{warnings.push(`extension_hook_failed:${entry.layer}:${entry.name}:${hookName}`)}return warnings}async function runBeforeCommandHooks(hooks,context){return executeRegisteredHooks(hooks.beforeCommand,"beforeCommand",context)}function enforceMutationGuardDecision(decision,entry,operation){if(decision===void 0)return;if(typeof decision!="object"||decision===null||!("allow"in decision)||decision.allow!==!0&&decision.allow!==!1)throw new PmCliError(`Extension mutation guard ${entry.name} returned an invalid decision.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_invalid_decision",reason:`${entry.layer}:${entry.name}`,nextSteps:["Return { allow: true } or a structured denial from the guard."]});if(decision.allow)return;const denial=decision,code=typeof denial.code=="string"?denial.code.trim():"",remediation=typeof denial.remediation=="string"?denial.remediation.trim():"";if(code.length===0||remediation.length===0)throw new PmCliError(`Extension mutation guard ${entry.name} returned an invalid denial.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_invalid_denial",reason:`${entry.layer}:${entry.name}`,nextSteps:["Return non-empty code and remediation fields from the guard."]});const message=typeof denial.message=="string"?denial.message.trim():"";throw new PmCliError(message||`Extension mutation guard ${entry.name} denied ${operation}.`,EXIT_CODE.CONFLICT,{code,reason:`${entry.layer}:${entry.name}`,nextSteps:[remediation]})}async function runBeforeMutationHooks(hooks,context){for(const entry of hooks.beforeMutation??[]){let decision,timeoutHandle;try{const guardPromise=Promise.resolve().then(()=>entry.run({pm_root:context.pm_root,operation:context.operation,before:context.before===null?null:cloneContextSnapshot(context.before),after:context.after===null?null:cloneContextSnapshot(context.after),changed_fields:[...context.changed_fields],sdk:context.sdk}));guardPromise.catch(()=>{}),decision=await Promise.race([guardPromise,new Promise((_,reject)=>{timeoutHandle=setTimeout(()=>{reject(new PmCliError(`Extension mutation guard ${entry.name} timed out before ${context.operation}.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_timed_out",reason:`${entry.layer}:${entry.name}`,nextSteps:["Fix or disable the stalled extension guard, then retry the mutation."]}))},DEFAULT_MUTATION_GUARD_TIMEOUT_MS),timeoutHandle.unref?.()})])}catch(error){throw error instanceof PmCliError?error:new PmCliError(`Extension mutation guard ${entry.name} failed before ${context.operation}.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_failed",reason:`${entry.layer}:${entry.name}`,nextSteps:["Fix or disable the failing extension guard, then retry the mutation."]})}finally{clearTimeout(timeoutHandle)}enforceMutationGuardDecision(decision,entry,context.operation)}}async function runAfterCommandHooks(hooks,context){return executeRegisteredHooks(hooks.afterCommand,"afterCommand",context)}async function runOnWriteHooks(hooks,context){return executeRegisteredHooks(hooks.onWrite,"onWrite",context)}async function runOnReadHooks(hooks,context){return executeRegisteredHooks(hooks.onRead,"onRead",context)}async function runOnIndexHooks(hooks,context){return executeRegisteredHooks(hooks.onIndex,"onIndex",context)}function describeHandlerError(error){let raw="";error instanceof Error&&typeof error.message=="string"?raw=error.message:typeof error=="string"?raw=error:typeof error=="object"&&error!==null&&typeof error.message=="string"&&(raw=error.message);const normalized=raw.replace(/\s+/gu," ").trim();if(normalized.length===0)return"";const maxLength=300;return normalized.length>maxLength?`${normalized.slice(0,maxLength-1)}\u2026`:normalized}function readHandlerStringField(value,field){if(typeof value!="object"||value===null||typeof value[field]!="string")return;const normalized=String(value[field]).replace(/\s+/gu," ").trim();return normalized.length>0?normalized.slice(0,300):void 0}function readHandlerResultExitCode(result){if(typeof result!="object"||result===null||!Object.hasOwn(result,"exit_code"))return;const exitCode=result.exit_code;if(typeof exitCode!="number"||!Number.isInteger(exitCode)||exitCode<1||exitCode>255)throw new TypeError("Extension command result exit_code must be an integer from 1 to 255.");return exitCode}async function runCommandHandler(commands,context){const command=normalizeCommandName(context.command);if(command.length===0)return{handled:!1,result:null,warnings:[]};const matched=[...commands.handlers].reverse().find(entry=>entry.command===command);if(!matched)return{handled:!1,result:null,warnings:[]};try{const result=await matched.run({command,args:cloneContextSnapshot(context.args),options:cloneContextSnapshot(context.options),global:cloneContextSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,...context.sdk===void 0?{}:{sdk:context.sdk}});return{handled:!0,result,warnings:[],...Object.fromEntries([["exitCode",readHandlerResultExitCode(result)],["errorCode",readHandlerStringField(result,"code")],["remediation",readHandlerStringField(result,"remediation")]].filter(entry=>entry[1]!==void 0))}}catch(error){const exitCode=typeof error=="object"&&error!==null&&"exitCode"in error?error.exitCode:void 0;if(typeof exitCode=="number"&&Number.isFinite(exitCode))throw error;return Object.defineProperty({handled:!1,result:null,warnings:[`extension_command_handler_failed:${matched.layer}:${matched.name}:${matched.command}`],errorMessage:describeHandlerError(error),...Object.fromEntries([["errorCode",readHandlerStringField(error,"code")],["remediation",readHandlerStringField(error,"remediation")]].filter(entry=>entry[1]!==void 0))},"cause",{value:error,configurable:!0})}}async function runParserOverride(parsers,context){const command=normalizeCommandName(context.command);if(command.length===0)return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]};const matched=[...parsers.overrides].reverse().find(entry=>entry.command===command);if(!matched)return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]};try{const delta=await Promise.resolve(matched.run({command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel}))??{},nextArgs=Array.isArray(delta.args)?cloneContextSnapshot(delta.args):cloneContextSnapshot(context.args),nextOptions=delta.options?cloneCommandOptionsSnapshot(delta.options):cloneCommandOptionsSnapshot(context.options),nextGlobal=delta.global?cloneGlobalOptionsSnapshot(delta.global):cloneGlobalOptionsSnapshot(context.global);return{overridden:!0,context:{command,args:nextArgs,options:nextOptions,global:nextGlobal,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]}}catch{return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[`extension_parser_override_failed:${matched.layer}:${matched.name}:${matched.command}`]}}}async function runPreflightOverride(preflight,context){const baseContext={command:normalizeCommandName(context.command),args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},baseDecision=cloneContextSnapshot(context.decision),matched=[...preflight.overrides].reverse().find(entry=>!entry.commands?.length||entry.commands.includes(baseContext.command));if(!matched)return{overridden:!1,context:baseContext,decision:baseDecision,warnings:[]};try{const delta=await Promise.resolve(matched.run({command:baseContext.command,args:cloneContextSnapshot(baseContext.args),options:cloneCommandOptionsSnapshot(baseContext.options),global:cloneGlobalOptionsSnapshot(baseContext.global),pm_root:baseContext.pm_root,source_workspace_root:baseContext.source_workspace_root,repo_root:baseContext.repo_root,pm_root_rel:baseContext.pm_root_rel,decision:cloneContextSnapshot(baseDecision)}))??{},nextContext={command:baseContext.command,args:Array.isArray(delta.args)?cloneContextSnapshot(delta.args):baseContext.args,options:delta.options?cloneCommandOptionsSnapshot(delta.options):baseContext.options,global:delta.global?cloneGlobalOptionsSnapshot(delta.global):baseContext.global,pm_root:baseContext.pm_root,source_workspace_root:baseContext.source_workspace_root,repo_root:baseContext.repo_root,pm_root_rel:baseContext.pm_root_rel},nextDecision={enforce_item_format_gate:typeof delta.enforce_item_format_gate=="boolean"?delta.enforce_item_format_gate:baseDecision.enforce_item_format_gate,run_preflight_item_format_sync:typeof delta.run_preflight_item_format_sync=="boolean"?delta.run_preflight_item_format_sync:baseDecision.run_preflight_item_format_sync,run_extension_migrations:typeof delta.run_extension_migrations=="boolean"?delta.run_extension_migrations:baseDecision.run_extension_migrations,enforce_mandatory_migration_gate:typeof delta.enforce_mandatory_migration_gate=="boolean"?delta.enforce_mandatory_migration_gate:baseDecision.enforce_mandatory_migration_gate};return{overridden:!0,context:nextContext,decision:nextDecision,warnings:[]}}catch{return{overridden:!1,context:baseContext,decision:baseDecision,warnings:[`extension_preflight_override_failed:${matched.layer}:${matched.name}`]}}}function resolveDefaultServiceResult(context){return{handled:!1,result:context.payload,warnings:[]}}function matchingServiceOverrides(services,service){return[...services.overrides].reverse().filter(entry=>entry.service===service)}function buildServiceOverrideContext(context){return{service:context.service,command:context.command?normalizeCommandName(context.command):void 0,args:context.args?cloneContextSnapshot(context.args):void 0,options:context.options?cloneCommandOptionsSnapshot(context.options):void 0,global:context.global?cloneGlobalOptionsSnapshot(context.global):void 0,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,payload:cloneContextSnapshot(context.payload)}}function isServiceOverrideDecision(value){if(typeof value!="object"||value===null||!Object.hasOwn(value,"handled"))return!1;const handled=value.handled;return handled===!0?Object.hasOwn(value,"result"):handled===!1&&Reflect.ownKeys(value).length===1}function resolveServiceOverrideValue(result,context){return isServiceOverrideDecision(result)?result.handled?{handled:!0,result:result.result}:{handled:!1,result:context.payload}:context.service==="output_format"&&result==null?{handled:!1,result:context.payload}:{handled:!0,result}}function isLegacyOutputPayloadEcho(result,context,serviceContext){return context.service==="output_format"&&typeof result=="object"&&result!==null&&stableValueEquals(result,serviceContext.payload)}function resolveMatchedServiceDecision(matched,decision,warnings){if(decision.handled){if(matched.passThrough===!0){warnings.push(`extension_service_pass_through_contract_violated:${matched.layer}:${matched.name}:${matched.service}`);return}return{handled:!0,result:decision.result,warnings}}}function runServiceOverrideSync(services,context){const matches=matchingServiceOverrides(services,context.service);if(matches.length===0)return resolveDefaultServiceResult(context);const warnings=[];for(const matched of matches)try{const serviceContext=buildServiceOverrideContext(context),result=matched.run(serviceContext);if(result instanceof Promise){warnings.push(`extension_service_override_async_unsupported:${matched.layer}:${matched.name}:${matched.service}`);continue}if(isLegacyOutputPayloadEcho(result,context,serviceContext)){warnings.push(`extension_output_format_payload_echo_deprecated:${matched.layer}:${matched.name}`);continue}const resolved=resolveMatchedServiceDecision(matched,resolveServiceOverrideValue(result,context),warnings);if(resolved)return resolved}catch{warnings.push(`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`)}return{handled:!1,result:context.payload,warnings}}async function runServiceOverride(services,context){const matches=matchingServiceOverrides(services,context.service);if(matches.length===0)return resolveDefaultServiceResult(context);const warnings=[];for(const matched of matches)try{const serviceContext=buildServiceOverrideContext(context),result=await Promise.resolve(matched.run(serviceContext));if(isLegacyOutputPayloadEcho(result,context,serviceContext)){warnings.push(`extension_output_format_payload_echo_deprecated:${matched.layer}:${matched.name}`);continue}const resolved=resolveMatchedServiceDecision(matched,resolveServiceOverrideValue(result,context),warnings);if(resolved)return resolved}catch{warnings.push(`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`)}return{handled:!1,result:context.payload,warnings}}function runCommandOverride(commands,context){const command=normalizeCommandName(context.command);if(command.length===0)return{overridden:!1,result:context.result,warnings:[]};const matched=[...commands.overrides].reverse().find(entry=>entry.command===command);if(!matched)return{overridden:!1,result:context.result,warnings:[]};try{const overrideOptions=cloneCommandOptionsSnapshot(context.options),overrideGlobal=cloneGlobalOptionsSnapshot(context.global),overrideResult=matched.run({command,args:cloneContextSnapshot(context.args),options:overrideOptions,global:overrideGlobal,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,result:cloneContextSnapshot(context.result)});return overrideResult instanceof Promise?{overridden:!1,result:context.result,warnings:[`extension_command_override_async_unsupported:${matched.layer}:${matched.name}:${matched.command}`]}:{overridden:!0,result:overrideResult,warnings:[]}}catch{return{overridden:!1,result:context.result,warnings:[`extension_command_override_failed:${matched.layer}:${matched.name}:${matched.command}`]}}}function rendererOwnsContext(entry,command,result){if((entry.commands?.length??0)>0&&!entry.commands?.includes(command))return!1;if(entry.resultDiscriminator===void 0)return!0;try{return entry.resultDiscriminator(cloneContextSnapshot(result))}catch{return!1}}function runRendererOverride(renderers,context){const rendererCommand=typeof context.command=="string"?normalizeCommandName(context.command):"",matched=[...renderers.overrides].reverse().find(entry=>entry.format===context.format&&rendererOwnsContext(entry,rendererCommand,context.result));if(!matched)return{overridden:!1,rendered:null,warnings:[]};try{const rendererArgs=Array.isArray(context.args)?cloneContextSnapshot(context.args):[],rendererOptions=cloneCommandOptionsSnapshot(context.options),rendererGlobal=cloneGlobalOptionsSnapshot(context.global),rendererPmRoot=typeof context.pm_root=="string"?context.pm_root:"",rendered=matched.run({format:context.format,command:rendererCommand,args:rendererArgs,options:rendererOptions,global:rendererGlobal,pm_root:rendererPmRoot,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,result:cloneContextSnapshot(context.result)});return rendered==null?{overridden:!1,rendered:null,warnings:[]}:typeof rendered!="string"?{overridden:!1,rendered:null,warnings:[`extension_renderer_invalid_result:${matched.layer}:${matched.name}:${matched.format}`]}:{overridden:!0,rendered,warnings:[]}}catch{return{overridden:!1,rendered:null,warnings:[`extension_renderer_failed:${matched.layer}:${matched.name}:${matched.format}`]}}}export{runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride,runServiceOverrideSync};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f49bad80-c246-59b3-950d-7143585fbdb5")}catch{}})();import{unicodeSafePrefix}from"../shared/text-normalization.js";import{cloneCommandOptionsSnapshot,cloneContextSnapshot,cloneGlobalOptionsSnapshot,normalizeCommandName}from"./extension-runtime-helpers.js";import{stableValueEquals}from"../shared/serialization.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";const DEFAULT_MUTATION_GUARD_TIMEOUT_MS=5e3;async function executeRegisteredHooks(entries,hookName,context){const warnings=[];for(const entry of entries)try{await entry.run(cloneContextSnapshot(context))}catch{warnings.push(`extension_hook_failed:${entry.layer}:${entry.name}:${hookName}`)}return warnings}async function runBeforeCommandHooks(hooks,context){return executeRegisteredHooks(hooks.beforeCommand,"beforeCommand",context)}function enforceMutationGuardDecision(decision,entry,operation){if(decision===void 0)return;if(typeof decision!="object"||decision===null||!("allow"in decision)||decision.allow!==!0&&decision.allow!==!1)throw new PmCliError(`Extension mutation guard ${entry.name} returned an invalid decision.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_invalid_decision",reason:`${entry.layer}:${entry.name}`,nextSteps:["Return { allow: true } or a structured denial from the guard."]});if(decision.allow)return;const denial=decision,code=typeof denial.code=="string"?denial.code.trim():"",remediation=typeof denial.remediation=="string"?denial.remediation.trim():"";if(code.length===0||remediation.length===0)throw new PmCliError(`Extension mutation guard ${entry.name} returned an invalid denial.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_invalid_denial",reason:`${entry.layer}:${entry.name}`,nextSteps:["Return non-empty code and remediation fields from the guard."]});const message=typeof denial.message=="string"?denial.message.trim():"";throw new PmCliError(message||`Extension mutation guard ${entry.name} denied ${operation}.`,EXIT_CODE.CONFLICT,{code,reason:`${entry.layer}:${entry.name}`,nextSteps:[remediation]})}async function runBeforeMutationHooks(hooks,context){for(const entry of hooks.beforeMutation??[]){let decision,timeoutHandle;try{const guardPromise=Promise.resolve().then(()=>entry.run({pm_root:context.pm_root,operation:context.operation,before:context.before===null?null:cloneContextSnapshot(context.before),after:context.after===null?null:cloneContextSnapshot(context.after),changed_fields:[...context.changed_fields],sdk:context.sdk}));guardPromise.catch(()=>{}),decision=await Promise.race([guardPromise,new Promise((_,reject)=>{timeoutHandle=setTimeout(()=>{reject(new PmCliError(`Extension mutation guard ${entry.name} timed out before ${context.operation}.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_timed_out",reason:`${entry.layer}:${entry.name}`,nextSteps:["Fix or disable the stalled extension guard, then retry the mutation."]}))},DEFAULT_MUTATION_GUARD_TIMEOUT_MS),timeoutHandle.unref?.()})])}catch(error){throw error instanceof PmCliError?error:new PmCliError(`Extension mutation guard ${entry.name} failed before ${context.operation}.`,EXIT_CODE.CONFLICT,{code:"extension_mutation_guard_failed",reason:`${entry.layer}:${entry.name}`,nextSteps:["Fix or disable the failing extension guard, then retry the mutation."]})}finally{clearTimeout(timeoutHandle)}enforceMutationGuardDecision(decision,entry,context.operation)}}async function runAfterCommandHooks(hooks,context){return executeRegisteredHooks(hooks.afterCommand,"afterCommand",context)}async function runOnWriteHooks(hooks,context){return executeRegisteredHooks(hooks.onWrite,"onWrite",context)}async function runOnReadHooks(hooks,context){return executeRegisteredHooks(hooks.onRead,"onRead",context)}async function runOnIndexHooks(hooks,context){return executeRegisteredHooks(hooks.onIndex,"onIndex",context)}function describeHandlerError(error){let raw="";error instanceof Error&&typeof error.message=="string"?raw=error.message:typeof error=="string"?raw=error:typeof error=="object"&&error!==null&&typeof error.message=="string"&&(raw=error.message);const normalized=raw.replace(/\s+/gu," ").trim();if(normalized.length===0)return"";const maxLength=300;return normalized.length>maxLength?`${unicodeSafePrefix(normalized,maxLength-1)}\u2026`:normalized}function readHandlerStringField(value,field){if(typeof value!="object"||value===null||typeof value[field]!="string")return;const normalized=String(value[field]).replace(/\s+/gu," ").trim();return normalized.length>0?unicodeSafePrefix(normalized,300):void 0}function readHandlerResultExitCode(result){if(typeof result!="object"||result===null||!Object.hasOwn(result,"exit_code"))return;const exitCode=result.exit_code;if(typeof exitCode!="number"||!Number.isInteger(exitCode)||exitCode<1||exitCode>255)throw new TypeError("Extension command result exit_code must be an integer from 1 to 255.");return exitCode}async function runCommandHandler(commands,context){const command=normalizeCommandName(context.command);if(command.length===0)return{handled:!1,result:null,warnings:[]};const matched=[...commands.handlers].reverse().find(entry=>entry.command===command);if(!matched)return{handled:!1,result:null,warnings:[]};try{const result=await matched.run({command,args:cloneContextSnapshot(context.args),options:cloneContextSnapshot(context.options),global:cloneContextSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,...context.sdk===void 0?{}:{sdk:context.sdk}});return{handled:!0,result,warnings:[],...Object.fromEntries([["exitCode",readHandlerResultExitCode(result)],["errorCode",readHandlerStringField(result,"code")],["remediation",readHandlerStringField(result,"remediation")]].filter(entry=>entry[1]!==void 0))}}catch(error){const exitCode=typeof error=="object"&&error!==null&&"exitCode"in error?error.exitCode:void 0;if(typeof exitCode=="number"&&Number.isFinite(exitCode))throw error;return Object.defineProperty({handled:!1,result:null,warnings:[`extension_command_handler_failed:${matched.layer}:${matched.name}:${matched.command}`],errorMessage:describeHandlerError(error),...Object.fromEntries([["errorCode",readHandlerStringField(error,"code")],["remediation",readHandlerStringField(error,"remediation")]].filter(entry=>entry[1]!==void 0))},"cause",{value:error,configurable:!0})}}async function runParserOverride(parsers,context){const command=normalizeCommandName(context.command);if(command.length===0)return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]};const matched=[...parsers.overrides].reverse().find(entry=>entry.command===command);if(!matched)return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]};try{const delta=await Promise.resolve(matched.run({command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel}))??{},nextArgs=Array.isArray(delta.args)?cloneContextSnapshot(delta.args):cloneContextSnapshot(context.args),nextOptions=delta.options?cloneCommandOptionsSnapshot(delta.options):cloneCommandOptionsSnapshot(context.options),nextGlobal=delta.global?cloneGlobalOptionsSnapshot(delta.global):cloneGlobalOptionsSnapshot(context.global);return{overridden:!0,context:{command,args:nextArgs,options:nextOptions,global:nextGlobal,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[]}}catch{return{overridden:!1,context:{command,args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},warnings:[`extension_parser_override_failed:${matched.layer}:${matched.name}:${matched.command}`]}}}async function runPreflightOverride(preflight,context){const baseContext={command:normalizeCommandName(context.command),args:cloneContextSnapshot(context.args),options:cloneCommandOptionsSnapshot(context.options),global:cloneGlobalOptionsSnapshot(context.global),pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel},baseDecision=cloneContextSnapshot(context.decision),matched=[...preflight.overrides].reverse().find(entry=>!entry.commands?.length||entry.commands.includes(baseContext.command));if(!matched)return{overridden:!1,context:baseContext,decision:baseDecision,warnings:[]};try{const delta=await Promise.resolve(matched.run({command:baseContext.command,args:cloneContextSnapshot(baseContext.args),options:cloneCommandOptionsSnapshot(baseContext.options),global:cloneGlobalOptionsSnapshot(baseContext.global),pm_root:baseContext.pm_root,source_workspace_root:baseContext.source_workspace_root,repo_root:baseContext.repo_root,pm_root_rel:baseContext.pm_root_rel,decision:cloneContextSnapshot(baseDecision)}))??{},nextContext={command:baseContext.command,args:Array.isArray(delta.args)?cloneContextSnapshot(delta.args):baseContext.args,options:delta.options?cloneCommandOptionsSnapshot(delta.options):baseContext.options,global:delta.global?cloneGlobalOptionsSnapshot(delta.global):baseContext.global,pm_root:baseContext.pm_root,source_workspace_root:baseContext.source_workspace_root,repo_root:baseContext.repo_root,pm_root_rel:baseContext.pm_root_rel},nextDecision={enforce_item_format_gate:typeof delta.enforce_item_format_gate=="boolean"?delta.enforce_item_format_gate:baseDecision.enforce_item_format_gate,run_preflight_item_format_sync:typeof delta.run_preflight_item_format_sync=="boolean"?delta.run_preflight_item_format_sync:baseDecision.run_preflight_item_format_sync,run_extension_migrations:typeof delta.run_extension_migrations=="boolean"?delta.run_extension_migrations:baseDecision.run_extension_migrations,enforce_mandatory_migration_gate:typeof delta.enforce_mandatory_migration_gate=="boolean"?delta.enforce_mandatory_migration_gate:baseDecision.enforce_mandatory_migration_gate};return{overridden:!0,context:nextContext,decision:nextDecision,warnings:[]}}catch{return{overridden:!1,context:baseContext,decision:baseDecision,warnings:[`extension_preflight_override_failed:${matched.layer}:${matched.name}`]}}}function resolveDefaultServiceResult(context){return{handled:!1,result:context.payload,warnings:[]}}function matchingServiceOverrides(services,service){return[...services.overrides].reverse().filter(entry=>entry.service===service)}function buildServiceOverrideContext(context){return{service:context.service,command:context.command?normalizeCommandName(context.command):void 0,args:context.args?cloneContextSnapshot(context.args):void 0,options:context.options?cloneCommandOptionsSnapshot(context.options):void 0,global:context.global?cloneGlobalOptionsSnapshot(context.global):void 0,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,payload:cloneContextSnapshot(context.payload)}}function isServiceOverrideDecision(value){if(typeof value!="object"||value===null||!Object.hasOwn(value,"handled"))return!1;const handled=value.handled;return handled===!0?Object.hasOwn(value,"result"):handled===!1&&Reflect.ownKeys(value).length===1}function resolveServiceOverrideValue(result,context){return isServiceOverrideDecision(result)?result.handled?{handled:!0,result:result.result}:{handled:!1,result:context.payload}:context.service==="output_format"&&result==null?{handled:!1,result:context.payload}:{handled:!0,result}}function isLegacyOutputPayloadEcho(result,context,serviceContext){return context.service==="output_format"&&typeof result=="object"&&result!==null&&stableValueEquals(result,serviceContext.payload)}function resolveMatchedServiceDecision(matched,decision,warnings){if(decision.handled){if(matched.passThrough===!0){warnings.push(`extension_service_pass_through_contract_violated:${matched.layer}:${matched.name}:${matched.service}`);return}return{handled:!0,result:decision.result,warnings}}}function runServiceOverrideSync(services,context){const matches=matchingServiceOverrides(services,context.service);if(matches.length===0)return resolveDefaultServiceResult(context);const warnings=[];for(const matched of matches)try{const serviceContext=buildServiceOverrideContext(context),result=matched.run(serviceContext);if(result instanceof Promise){warnings.push(`extension_service_override_async_unsupported:${matched.layer}:${matched.name}:${matched.service}`);continue}if(isLegacyOutputPayloadEcho(result,context,serviceContext)){warnings.push(`extension_output_format_payload_echo_deprecated:${matched.layer}:${matched.name}`);continue}const resolved=resolveMatchedServiceDecision(matched,resolveServiceOverrideValue(result,context),warnings);if(resolved)return resolved}catch{warnings.push(`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`)}return{handled:!1,result:context.payload,warnings}}async function runServiceOverride(services,context){const matches=matchingServiceOverrides(services,context.service);if(matches.length===0)return resolveDefaultServiceResult(context);const warnings=[];for(const matched of matches)try{const serviceContext=buildServiceOverrideContext(context),result=await Promise.resolve(matched.run(serviceContext));if(isLegacyOutputPayloadEcho(result,context,serviceContext)){warnings.push(`extension_output_format_payload_echo_deprecated:${matched.layer}:${matched.name}`);continue}const resolved=resolveMatchedServiceDecision(matched,resolveServiceOverrideValue(result,context),warnings);if(resolved)return resolved}catch{warnings.push(`extension_service_override_failed:${matched.layer}:${matched.name}:${matched.service}`)}return{handled:!1,result:context.payload,warnings}}function runCommandOverride(commands,context){const command=normalizeCommandName(context.command);if(command.length===0)return{overridden:!1,result:context.result,warnings:[]};const matched=[...commands.overrides].reverse().find(entry=>entry.command===command);if(!matched)return{overridden:!1,result:context.result,warnings:[]};try{const overrideOptions=cloneCommandOptionsSnapshot(context.options),overrideGlobal=cloneGlobalOptionsSnapshot(context.global),overrideResult=matched.run({command,args:cloneContextSnapshot(context.args),options:overrideOptions,global:overrideGlobal,pm_root:context.pm_root,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,result:cloneContextSnapshot(context.result)});return overrideResult instanceof Promise?{overridden:!1,result:context.result,warnings:[`extension_command_override_async_unsupported:${matched.layer}:${matched.name}:${matched.command}`]}:{overridden:!0,result:overrideResult,warnings:[]}}catch{return{overridden:!1,result:context.result,warnings:[`extension_command_override_failed:${matched.layer}:${matched.name}:${matched.command}`]}}}function rendererOwnsContext(entry,command,result){if((entry.commands?.length??0)>0&&!entry.commands?.includes(command))return!1;if(entry.resultDiscriminator===void 0)return!0;try{return entry.resultDiscriminator(cloneContextSnapshot(result))}catch{return!1}}function runRendererOverride(renderers,context){const rendererCommand=typeof context.command=="string"?normalizeCommandName(context.command):"",matched=[...renderers.overrides].reverse().find(entry=>entry.format===context.format&&rendererOwnsContext(entry,rendererCommand,context.result));if(!matched)return{overridden:!1,rendered:null,warnings:[]};try{const rendererArgs=Array.isArray(context.args)?cloneContextSnapshot(context.args):[],rendererOptions=cloneCommandOptionsSnapshot(context.options),rendererGlobal=cloneGlobalOptionsSnapshot(context.global),rendererPmRoot=typeof context.pm_root=="string"?context.pm_root:"",rendered=matched.run({format:context.format,command:rendererCommand,args:rendererArgs,options:rendererOptions,global:rendererGlobal,pm_root:rendererPmRoot,source_workspace_root:context.source_workspace_root,repo_root:context.repo_root,pm_root_rel:context.pm_root_rel,result:cloneContextSnapshot(context.result)});return rendered==null?{overridden:!1,rendered:null,warnings:[]}:typeof rendered!="string"?{overridden:!1,rendered:null,warnings:[`extension_renderer_invalid_result:${matched.layer}:${matched.name}:${matched.format}`]}:{overridden:!0,rendered,warnings:[]}}catch{return{overridden:!1,rendered:null,warnings:[`extension_renderer_failed:${matched.layer}:${matched.name}:${matched.format}`]}}}export{runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride,runServiceOverrideSync};
2
2
  //# sourceMappingURL=extension-hook-runtime.js.map
@@ -8,6 +8,7 @@ import type { GlobalOptions } from "../shared/command-types.js";
8
8
  import type { ProjectProfileDefinition,ProjectProfileRegistrationInput } from "../profile/profile-presets.js";
9
9
  import type { RelationshipEdge,RelationshipGraph,RelationshipKindDefinition,RelationshipQueryOptions } from "../../sdk/relationships.js";
10
10
  import type { RelationshipImpactAnalysis } from "../../sdk/relationship-analytics.js";
11
+ import type { ReadSettingsSectionOptions,SettingsSectionReadResult,SettingsSectionWriteResult,WriteSettingsSectionOptions } from "../store/settings-sections.js";
11
12
  import type { PmClient } from "../../sdk/runtime.js";
12
13
  import type { GetItemAtResult } from "../../sdk/history-read.js";
13
14
  import type { fitOutputEnvelope } from "../../sdk/output/fit-envelope.js";
@@ -581,6 +582,10 @@ client:PmClient;
581
582
  isItemNotFoundError(error:unknown):boolean;
582
583
  /** Reconstruct one item at a history version or timestamp. */
583
584
  getItemAt(id:string,target:string):Promise<GetItemAtResult>;
585
+ /** Read an extension-owned top-level settings section of this tracker through the host, never by settings.json path. */
586
+ readSettingsSection<T = unknown>(section:string,options?:ReadSettingsSectionOptions<T>):Promise<SettingsSectionReadResult<T>>;
587
+ /** Replace or remove (with `undefined`) an extension-owned settings section, optionally compare-and-set on its revision. */
588
+ writeSettingsSection<T>(section:string,value:T | undefined,options?:WriteSettingsSectionOptions<T>):Promise<SettingsSectionWriteResult>;
584
589
  /** Open a durable event store with package-contributed relationship semantics. */
585
590
  openRelationshipEventStore(options:{
586
591
  nodes:Iterable<string>;
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7474e879-83ca-56ff-981a-fdfede976378")}catch{}})();const KNOWN_EXTENSION_CAPABILITIES=["commands","renderers","hooks","schema","importers","search","parser","preflight","services"],EXTENSION_CAPABILITY_CONTRACT_VERSION=2,EXTENSION_CAPABILITY_LEGACY_ALIASES=Object.freeze({migration:"schema",validation:"schema"}),EXTENSION_CAPABILITY_CONTRACT=Object.freeze({version:2,capabilities:[...KNOWN_EXTENSION_CAPABILITIES],legacy_aliases:{...EXTENSION_CAPABILITY_LEGACY_ALIASES}}),KNOWN_EXTENSION_POLICY_MODES=["off","warn","enforce"],KNOWN_EXTENSION_TRUST_MODES=["off","warn","enforce"],KNOWN_EXTENSION_SANDBOX_PROFILES=["none","restricted","strict"],KNOWN_PM_MAX_VERSION_EXCEEDED_MODES=["block","warn"],KNOWN_EXTENSION_POLICY_SURFACES=["commands.override","commands.handler","hooks.beforecommand","hooks.beforemutation","hooks.aftercommand","hooks.onwrite","hooks.onread","hooks.onindex","schema.flags","schema.itemfields","schema.itemtypes","schema.relationshipkinds","schema.migrations","schema.profiles","parser.override","preflight.override","services.override","renderers.override","importers.importer","importers.exporter","search.provider","search.vectorstore","services.assuranceprovider"],KNOWN_EXTENSION_SERVICE_NAMES=["output_format","error_format","help_format","lock_acquire","lock_release","history_append","item_store_write","item_store_delete","context_relevance","command_result"];function createDefaultExtensionGovernancePolicy(){return{mode:"off",trust_mode:"off",require_provenance:!1,trusted_extensions:[],default_sandbox_profile:"none",allowed_extensions:[],blocked_extensions:[],allowed_capabilities:[],blocked_capabilities:[],allowed_surfaces:[],blocked_surfaces:[],allowed_commands:[],blocked_commands:[],allowed_actions:[],blocked_actions:[],allowed_services:[],blocked_services:[],extension_overrides:[]}}export{EXTENSION_CAPABILITY_CONTRACT,EXTENSION_CAPABILITY_CONTRACT_VERSION,EXTENSION_CAPABILITY_LEGACY_ALIASES,KNOWN_EXTENSION_CAPABILITIES,KNOWN_EXTENSION_POLICY_MODES,KNOWN_EXTENSION_POLICY_SURFACES,KNOWN_EXTENSION_SANDBOX_PROFILES,KNOWN_EXTENSION_SERVICE_NAMES,KNOWN_EXTENSION_TRUST_MODES,KNOWN_PM_MAX_VERSION_EXCEEDED_MODES,createDefaultExtensionGovernancePolicy};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="937c31e4-62a2-5815-8c7b-3640688b43b9")}catch{}})();const KNOWN_EXTENSION_CAPABILITIES=["commands","renderers","hooks","schema","importers","search","parser","preflight","services"],EXTENSION_CAPABILITY_CONTRACT_VERSION=2,EXTENSION_CAPABILITY_LEGACY_ALIASES=Object.freeze({migration:"schema",validation:"schema"}),EXTENSION_CAPABILITY_CONTRACT=Object.freeze({version:2,capabilities:[...KNOWN_EXTENSION_CAPABILITIES],legacy_aliases:{...EXTENSION_CAPABILITY_LEGACY_ALIASES}}),KNOWN_EXTENSION_POLICY_MODES=["off","warn","enforce"],KNOWN_EXTENSION_TRUST_MODES=["off","warn","enforce"],KNOWN_EXTENSION_SANDBOX_PROFILES=["none","restricted","strict"],KNOWN_PM_MAX_VERSION_EXCEEDED_MODES=["block","warn"],KNOWN_EXTENSION_POLICY_SURFACES=["commands.override","commands.handler","hooks.beforecommand","hooks.beforemutation","hooks.aftercommand","hooks.onwrite","hooks.onread","hooks.onindex","schema.flags","schema.itemfields","schema.itemtypes","schema.relationshipkinds","schema.migrations","schema.profiles","parser.override","preflight.override","services.override","renderers.override","importers.importer","importers.exporter","search.provider","search.vectorstore","services.assuranceprovider"],KNOWN_EXTENSION_SERVICE_NAMES=["output_format","error_format","help_format","lock_acquire","lock_release","history_append","item_store_write","item_store_delete","context_relevance","command_result"];function createDefaultExtensionGovernancePolicy(){return{mode:"off",trust_mode:"off",require_provenance:!1,trusted_extensions:[],default_sandbox_profile:"none",allowed_extensions:[],blocked_extensions:[],allowed_capabilities:[],blocked_capabilities:[],allowed_surfaces:[],blocked_surfaces:[],allowed_commands:[],blocked_commands:[],allowed_actions:[],blocked_actions:[],allowed_services:[],blocked_services:[],extension_overrides:[]}}export{EXTENSION_CAPABILITY_CONTRACT,EXTENSION_CAPABILITY_CONTRACT_VERSION,EXTENSION_CAPABILITY_LEGACY_ALIASES,KNOWN_EXTENSION_CAPABILITIES,KNOWN_EXTENSION_POLICY_MODES,KNOWN_EXTENSION_POLICY_SURFACES,KNOWN_EXTENSION_SANDBOX_PROFILES,KNOWN_EXTENSION_SERVICE_NAMES,KNOWN_EXTENSION_TRUST_MODES,KNOWN_PM_MAX_VERSION_EXCEEDED_MODES,createDefaultExtensionGovernancePolicy};
2
2
  //# sourceMappingURL=extension-types.js.map
@@ -15,7 +15,7 @@ export declare function collectRegisteredItemFieldNames(registrations:ExtensionR
15
15
  * rejected input separately from legal registered keys. Parsing never writes
16
16
  * tracker state, so a failed assignment cannot persist partial metadata.
17
17
  */
18
- export declare function parseRegisteredItemFieldAssignments(rawFields:string[] | undefined,registrations:ExtensionRegistrationRegistry | null,resolveCoreFieldRecovery?:(field:string) => Pick<PmCliErrorContext,"recovery" | "nextSteps">):Record<string,unknown>;
18
+ export declare function parseRegisteredItemFieldAssignments(rawFields:string[] | undefined,registrations:ExtensionRegistrationRegistry | null,resolveCoreFieldRecovery?:(field:string) => Pick<PmCliErrorContext,"recovery" | "nextSteps">,resolveSchemaFieldRecovery?:(field:string) => Pick<PmCliErrorContext,"recovery" | "nextSteps"> | undefined):Record<string,unknown>;
19
19
  /** Implements apply registered item field defaults and validation for the public runtime surface of this module. */
20
20
  export declare function applyRegisteredItemFieldDefaultsAndValidation(itemMetadata:Record<string,unknown>,registrations:ExtensionRegistrationRegistry | null,options?:{
21
21
  skipDefaultFields?:ReadonlySet<string>;
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="300b6741-40eb-5bb1-bb0a-bb144d15946e")}catch{}})();import{normalizeItemFieldType}from"./item-field-types.js";import{EXIT_CODE,ITEM_METADATA_KEY_ORDER}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";const reservedItemFieldNames=new Set(ITEM_METADATA_KEY_ORDER),RESERVED_ITEM_FIELD_NAMES=Object.freeze({get size(){return reservedItemFieldNames.size},has(value){return reservedItemFieldNames.has(value)},entries(){return reservedItemFieldNames.entries()},keys(){return reservedItemFieldNames.keys()},values(){return reservedItemFieldNames.values()},forEach(callback,thisArg){reservedItemFieldNames.forEach(value=>callback.call(thisArg,value,value,RESERVED_ITEM_FIELD_NAMES))},[Symbol.iterator](){return reservedItemFieldNames.values()}});function normalizeFieldName(value){if(typeof value!="string")return null;const normalized=value.trim();return normalized.length>0?normalized:null}function normalizeFieldType(value){return typeof value!="string"?null:normalizeItemFieldType(value)}function cloneFieldValue(value){try{return structuredClone(value)}catch{return value}}function parseFieldAssignment(raw){const trimmed=raw.trim(),separatorIndex=trimmed.indexOf("=");if(separatorIndex<=0)throw new PmCliError(`--field entries must use name=value syntax, received: ${raw}`,EXIT_CODE.USAGE);const key=trimmed.slice(0,separatorIndex).trim(),value=trimmed.slice(separatorIndex+1);return{key,value}}function parseJsonFieldValue(raw,fieldName,expectedType){try{const parsed=JSON.parse(raw);if(expectedType==="array"&&Array.isArray(parsed)||expectedType==="object"&&typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed))return parsed}catch{}throw new PmCliError(`--field ${fieldName}=... must be valid JSON ${expectedType}`,EXIT_CODE.USAGE)}function coerceRegisteredFieldValue(fieldName,fieldType,raw){if(fieldType==="string")return raw;if(fieldType==="number"){const trimmed=raw.trim();if(trimmed.length===0)throw new PmCliError(`--field ${fieldName}=... must be a number`,EXIT_CODE.USAGE);const parsed=Number(trimmed);if(!Number.isFinite(parsed))throw new PmCliError(`--field ${fieldName}=... must be a number`,EXIT_CODE.USAGE);return parsed}if(fieldType==="boolean"){const normalized=raw.trim().toLowerCase();if(normalized==="true"||normalized==="1"||normalized==="yes")return!0;if(normalized==="false"||normalized==="0"||normalized==="no")return!1;throw new PmCliError(`--field ${fieldName}=... must be one of true|false|1|0|yes|no`,EXIT_CODE.USAGE)}return fieldType==="string_array"?raw.split(/[\n,|]/u).map(value=>value.trim()).filter(value=>value.length>0):parseJsonFieldValue(raw,fieldName,fieldType)}function collectRegisteredFieldDefinitions(registrations){const definitions=new Map;if(!registrations)return definitions;for(const registration of registrations.item_fields)for(const definition of registration.fields){const fieldName=normalizeFieldName(definition.name),fieldType=normalizeFieldType(definition.type);if(!fieldName||!fieldType)continue;const existing=definitions.get(fieldName);if(existing&&existing.type!==fieldType)throw new PmCliError(`Extension item field "${fieldName}" is declared with conflicting types: ${existing.type}, ${fieldType}`,EXIT_CODE.USAGE,{code:"extension_item_field_type_conflict",nextSteps:["Make every active extension declaration for this field use the same type."]});definitions.set(fieldName,{name:fieldName,type:fieldType})}return definitions}function assertNotReservedItemFieldName(fieldName){if(RESERVED_ITEM_FIELD_NAMES.has(fieldName))throw new PmCliError(`Extension item field "${fieldName}" collides with built-in item metadata "${fieldName}"; rename the extension field with an extension-specific prefix. Reserved fields: ${[...RESERVED_ITEM_FIELD_NAMES].sort((left,right)=>left.localeCompare(right)).join(", ")}.`,EXIT_CODE.USAGE,{code:"extension_item_field_reserved",nextSteps:["Rename the extension item field, preferably with an extension-specific prefix."]})}function collectRegisteredItemFieldNames(registrations){return[...collectRegisteredFieldDefinitions(registrations).keys()].sort((left,right)=>left.localeCompare(right))}function parseRegisteredItemFieldAssignments(rawFields,registrations,resolveCoreFieldRecovery){if(!rawFields||rawFields.length===0)return{};const definitions=collectRegisteredFieldDefinitions(registrations),values={};for(const raw of rawFields){const{key,value}=parseFieldAssignment(raw),definition=definitions.get(key);if(!definition){if(RESERVED_ITEM_FIELD_NAMES.has(key)){const guidance=resolveCoreFieldRecovery?.(key)??{recovery:{provided_fields:[key]},nextSteps:["Core metadata has no generic --field mutation path. Use the owning lifecycle, evidence, or importer API; inspect pm update <id> --help for declared mutation flags."]};throw new PmCliError(`--field ${key} is a core item field, not an extension field`,EXIT_CODE.USAGE,{code:"core_item_field_requires_dedicated_flag",field:key,...guidance})}const known=[...definitions.keys()].sort((left,right)=>left.localeCompare(right));throw new PmCliError(`--field ${key} is not declared by an active extension item-field registration`,EXIT_CODE.USAGE,{code:"extension_item_field_unknown",field:key,recovery:{provided_fields:[key]},nextSteps:known.length>0?[`Use one of the declared fields: ${known.join(", ")}`]:["Activate an extension that calls registerItemFields before setting extension fields."]})}assertNotReservedItemFieldName(definition.name),values[definition.name]=coerceRegisteredFieldValue(definition.name,definition.type,value)}return values}function isValidFieldType(value,expectedType){return expectedType==="string"?typeof value=="string":expectedType==="number"?typeof value=="number"&&Number.isFinite(value):expectedType==="boolean"?typeof value=="boolean":expectedType==="array"?Array.isArray(value):expectedType==="string_array"?Array.isArray(value)&&value.every(entry=>typeof entry=="string"):typeof value=="object"&&value!==null&&!Array.isArray(value)}function isAllowedFieldValue(value,allowed){return!allowed||allowed.length===0?!0:allowed.some(candidate=>Object.is(candidate,value))}function applyRegisteredItemFieldDefaultsAndValidation(itemMetadata,registrations,options={}){if(registrations){collectRegisteredFieldDefinitions(registrations);for(const registration of registrations.item_fields)for(const definition of registration.fields){const fieldName=normalizeFieldName(definition.name);if(!fieldName)continue;assertNotReservedItemFieldName(fieldName),!(fieldName in itemMetadata)&&!options.skipDefaultFields?.has(fieldName)&&Object.prototype.hasOwnProperty.call(definition,"default")&&(itemMetadata[fieldName]=cloneFieldValue(definition.default));const currentValue=itemMetadata[fieldName];if(currentValue===void 0)continue;const expectedType=normalizeFieldType(definition.type);if(expectedType&&!isValidFieldType(currentValue,expectedType))throw new TypeError(`Item field "${fieldName}" must be of type ${expectedType}`);const allowedValues=Array.isArray(definition.values)?definition.values:void 0;if(!isAllowedFieldValue(currentValue,allowedValues))throw new TypeError(`Item field "${fieldName}" must match one of the configured allowed values`)}}}export{RESERVED_ITEM_FIELD_NAMES,applyRegisteredItemFieldDefaultsAndValidation,collectRegisteredItemFieldNames,parseRegisteredItemFieldAssignments};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4e02447f-3fb0-5f7c-9512-be2b348fd082")}catch{}})();import{normalizeItemFieldType}from"./item-field-types.js";import{EXIT_CODE,ITEM_METADATA_KEY_ORDER}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";const reservedItemFieldNames=new Set(ITEM_METADATA_KEY_ORDER),RESERVED_ITEM_FIELD_NAMES=Object.freeze({get size(){return reservedItemFieldNames.size},has(value){return reservedItemFieldNames.has(value)},entries(){return reservedItemFieldNames.entries()},keys(){return reservedItemFieldNames.keys()},values(){return reservedItemFieldNames.values()},forEach(callback,thisArg){reservedItemFieldNames.forEach(value=>callback.call(thisArg,value,value,RESERVED_ITEM_FIELD_NAMES))},[Symbol.iterator](){return reservedItemFieldNames.values()}});function normalizeFieldName(value){if(typeof value!="string")return null;const normalized=value.trim();return normalized.length>0?normalized:null}function normalizeFieldType(value){return typeof value!="string"?null:normalizeItemFieldType(value)}function cloneFieldValue(value){try{return structuredClone(value)}catch{return value}}function parseFieldAssignment(raw){const trimmed=raw.trim(),separatorIndex=trimmed.indexOf("=");if(separatorIndex<=0)throw new PmCliError(`--field entries must use name=value syntax, received: ${raw}`,EXIT_CODE.USAGE);const key=trimmed.slice(0,separatorIndex).trim(),value=trimmed.slice(separatorIndex+1);return{key,value}}function parseJsonFieldValue(raw,fieldName,expectedType){try{const parsed=JSON.parse(raw);if(expectedType==="array"&&Array.isArray(parsed)||expectedType==="object"&&typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed))return parsed}catch{}throw new PmCliError(`--field ${fieldName}=... must be valid JSON ${expectedType}`,EXIT_CODE.USAGE)}function coerceRegisteredFieldValue(fieldName,fieldType,raw){if(fieldType==="string")return raw;if(fieldType==="number"){const trimmed=raw.trim();if(trimmed.length===0)throw new PmCliError(`--field ${fieldName}=... must be a number`,EXIT_CODE.USAGE);const parsed=Number(trimmed);if(!Number.isFinite(parsed))throw new PmCliError(`--field ${fieldName}=... must be a number`,EXIT_CODE.USAGE);return parsed}if(fieldType==="boolean"){const normalized=raw.trim().toLowerCase();if(normalized==="true"||normalized==="1"||normalized==="yes")return!0;if(normalized==="false"||normalized==="0"||normalized==="no")return!1;throw new PmCliError(`--field ${fieldName}=... must be one of true|false|1|0|yes|no`,EXIT_CODE.USAGE)}return fieldType==="string_array"?raw.split(/[\n,|]/u).map(value=>value.trim()).filter(value=>value.length>0):parseJsonFieldValue(raw,fieldName,fieldType)}function collectRegisteredFieldDefinitions(registrations){const definitions=new Map;if(!registrations)return definitions;for(const registration of registrations.item_fields)for(const definition of registration.fields){const fieldName=normalizeFieldName(definition.name),fieldType=normalizeFieldType(definition.type);if(!fieldName||!fieldType)continue;const existing=definitions.get(fieldName);if(existing&&existing.type!==fieldType)throw new PmCliError(`Extension item field "${fieldName}" is declared with conflicting types: ${existing.type}, ${fieldType}`,EXIT_CODE.USAGE,{code:"extension_item_field_type_conflict",nextSteps:["Make every active extension declaration for this field use the same type."]});definitions.set(fieldName,{name:fieldName,type:fieldType})}return definitions}function assertNotReservedItemFieldName(fieldName){if(RESERVED_ITEM_FIELD_NAMES.has(fieldName))throw new PmCliError(`Extension item field "${fieldName}" collides with built-in item metadata "${fieldName}"; rename the extension field with an extension-specific prefix. Reserved fields: ${[...RESERVED_ITEM_FIELD_NAMES].sort((left,right)=>left.localeCompare(right)).join(", ")}.`,EXIT_CODE.USAGE,{code:"extension_item_field_reserved",nextSteps:["Rename the extension item field, preferably with an extension-specific prefix."]})}function collectRegisteredItemFieldNames(registrations){return[...collectRegisteredFieldDefinitions(registrations).keys()].sort((left,right)=>left.localeCompare(right))}function parseRegisteredItemFieldAssignments(rawFields,registrations,resolveCoreFieldRecovery,resolveSchemaFieldRecovery){if(!rawFields||rawFields.length===0)return{};const definitions=collectRegisteredFieldDefinitions(registrations),values={};for(const raw of rawFields){const{key,value}=parseFieldAssignment(raw),definition=definitions.get(key);if(!definition)throw unregisteredFieldRefusal(key,definitions,resolveCoreFieldRecovery,resolveSchemaFieldRecovery);assertNotReservedItemFieldName(definition.name),values[definition.name]=coerceRegisteredFieldValue(definition.name,definition.type,value)}return values}function unregisteredFieldRefusal(key,definitions,resolveCoreFieldRecovery,resolveSchemaFieldRecovery){if(RESERVED_ITEM_FIELD_NAMES.has(key)){const guidance=resolveCoreFieldRecovery?.(key)??{recovery:{provided_fields:[key]},nextSteps:["Core metadata has no generic --field mutation path. Use the owning lifecycle, evidence, or importer API; inspect pm update <id> --help for declared mutation flags."]};return new PmCliError(`--field ${key} is a core item field, not an extension field`,EXIT_CODE.USAGE,{code:"core_item_field_requires_dedicated_flag",field:key,...guidance})}const schemaGuidance=resolveSchemaFieldRecovery?.(key);if(schemaGuidance!==void 0){const flag=schemaGuidance.recovery?.suggested_flags?.[0];return new PmCliError(flag===void 0?`--field ${key} is a project-schema field, not an extension field`:`--field ${key} is a project-schema field; set it with its declared flag ${flag}`,EXIT_CODE.USAGE,{code:"project_schema_field_requires_declared_flag",field:key,changed:!1,...schemaGuidance})}const known=[...definitions.keys()].sort((left,right)=>left.localeCompare(right));return new PmCliError(`--field ${key} is not declared by an active extension item-field registration`,EXIT_CODE.USAGE,{code:"extension_item_field_unknown",field:key,recovery:{provided_fields:[key]},nextSteps:known.length>0?[`Use one of the declared fields: ${known.join(", ")}`]:["Activate an extension that calls registerItemFields before setting extension fields."]})}function isValidFieldType(value,expectedType){return expectedType==="string"?typeof value=="string":expectedType==="number"?typeof value=="number"&&Number.isFinite(value):expectedType==="boolean"?typeof value=="boolean":expectedType==="array"?Array.isArray(value):expectedType==="string_array"?Array.isArray(value)&&value.every(entry=>typeof entry=="string"):typeof value=="object"&&value!==null&&!Array.isArray(value)}function isAllowedFieldValue(value,allowed){return!allowed||allowed.length===0?!0:allowed.some(candidate=>Object.is(candidate,value))}function applyRegisteredItemFieldDefaultsAndValidation(itemMetadata,registrations,options={}){if(registrations){collectRegisteredFieldDefinitions(registrations);for(const registration of registrations.item_fields)for(const definition of registration.fields){const fieldName=normalizeFieldName(definition.name);if(!fieldName)continue;assertNotReservedItemFieldName(fieldName),!(fieldName in itemMetadata)&&!options.skipDefaultFields?.has(fieldName)&&Object.prototype.hasOwnProperty.call(definition,"default")&&(itemMetadata[fieldName]=cloneFieldValue(definition.default));const currentValue=itemMetadata[fieldName];if(currentValue===void 0)continue;const expectedType=normalizeFieldType(definition.type);if(expectedType&&!isValidFieldType(currentValue,expectedType))throw new TypeError(`Item field "${fieldName}" must be of type ${expectedType}`);const allowedValues=Array.isArray(definition.values)?definition.values:void 0;if(!isAllowedFieldValue(currentValue,allowedValues))throw new TypeError(`Item field "${fieldName}" must match one of the configured allowed values`)}}}export{RESERVED_ITEM_FIELD_NAMES,applyRegisteredItemFieldDefaultsAndValidation,collectRegisteredItemFieldNames,parseRegisteredItemFieldAssignments};
2
2
  //# sourceMappingURL=item-fields.js.map
@@ -1,9 +1,9 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b2ae2a38-678e-5f49-9f86-6e1ea4210b34")}catch{}})();import{PmCliError}from"../shared/errors.js";import{EXIT_CODE}from"../shared/constants.js";import{isCliMutationInput,preserveLiteralMutationInput,transferLiteralMutationInput}from"./literal-mutation-input.js";const STDIN_TOKEN="-",CONTINUABLE_VALUE_KEYS=new Set(["actual_result","body","cmd","command","customer_impact","description","environment","expected_result","impact","location","message","note","outcome","repro_steps","resolution","text","title","unblock_note","value","why_now"]);function looksLikeStructuredKeyValueEntry(raw,keys){if(raw.startsWith("```")||raw.includes(`
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e35626b8-0818-5b4b-8fc2-57426b51a2d1")}catch{}})();import{PmCliError}from"../shared/errors.js";import{EXIT_CODE}from"../shared/constants.js";import{isCliMutationInput,preserveLiteralMutationInput,transferLiteralMutationInput}from"./literal-mutation-input.js";const STDIN_TOKEN="-",CSV_KEY_PATTERN=/^[A-Za-z0-9_.-]+$/u,CONTINUABLE_VALUE_KEYS=new Set(["actual_result","body","cmd","command","customer_impact","description","environment","expected_result","impact","location","message","note","outcome","repro_steps","resolution","text","title","unblock_note","value","why_now"]);function looksLikeStructuredKeyValueEntry(raw,keys){if(raw.startsWith("```")||raw.includes(`
2
2
  `))return!0;const keyPattern=keys.map(key=>key.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|");return new RegExp(`^(?:[-*+]\\s+)?(?:${keyPattern})\\s*[:=]`,"i").test(raw)}function parseTags(raw){const trimmed=raw.trim();if(trimmed==="")return[];const tags=(coerceJsonTagArray(trimmed)??trimmed.split(",")).map(tag=>tag.trim()).filter(Boolean).map(tag=>tag.toLowerCase());return Array.from(new Set(tags)).sort((a,b)=>a.localeCompare(b))}function collectTagFlagValues(values){if(!Array.isArray(values)||values.length===0)return[];const collected=[];for(const raw of values)if(typeof raw=="string")for(const tag of parseTags(raw))collected.push(tag);return Array.from(new Set(collected)).sort((a,b)=>a.localeCompare(b))}function normalizeBaseTags(baseTags){return Array.isArray(baseTags)?baseTags.filter(tag=>typeof tag=="string").map(tag=>tag.trim().toLowerCase()).filter(Boolean):[]}function splitAcceptanceCriteria(value){return typeof value!="string"?[]:value.split(";").map(criterion=>criterion.trim()).filter(Boolean)}function applyAcceptanceCriteriaMutations(baseCriteria,add,remove){const criteria=baseCriteria.map(criterion=>criterion.trim()).filter(Boolean);for(const raw of add??[]){const criterion=raw.trim();if(criterion.includes(";"))throw new PmCliError('Acceptance criteria added with --add-ac cannot contain ";" because semicolons delimit stored criteria',EXIT_CODE.USAGE,{code:"acceptance_criteria_semicolon_forbidden",required:"Pass one semicolon-free criterion per --add-ac value",why:"A semicolon inside one criterion would be re-read as multiple criteria",nextSteps:["Split the text into repeated --add-ac values, or replace the semicolon with punctuation that is not the storage delimiter"]});criterion.length>0&&!criteria.includes(criterion)&&criteria.push(criterion)}const unmatchedRemovals=[];let remaining=criteria;for(const raw of remove??[]){const criterion=raw.trim();if(criterion.length!==0){if(!remaining.includes(criterion)){unmatchedRemovals.push(criterion);continue}remaining=remaining.filter(entry=>entry!==criterion)}}return{criteria:remaining,unmatchedRemovals}}function mergeAdditiveTags(baseTags,add){const normalizedBase=normalizeBaseTags(baseTags);if(!add||add.length===0)return Array.from(new Set(normalizedBase)).sort((a,b)=>a.localeCompare(b));const merged=new Set(normalizedBase);for(const tag of collectTagFlagValues(add))merged.add(tag);return Array.from(merged).sort((a,b)=>a.localeCompare(b))}function applyTagRemovals(baseTags,remove){const normalizedBase=Array.from(new Set(normalizeBaseTags(baseTags))).sort((a,b)=>a.localeCompare(b));if(!remove||remove.length===0)return normalizedBase;const removeSet=new Set(collectTagFlagValues(remove));return removeSet.size===0?normalizedBase:normalizedBase.filter(tag=>!removeSet.has(tag))}function coerceJsonTagArray(trimmed){if(!trimmed.startsWith("["))return null;let parsed;try{parsed=JSON.parse(trimmed)}catch{return null}return parsed.map(entry=>typeof entry=="string"||typeof entry=="number"||typeof entry=="boolean"?String(entry):"").filter(entry=>entry.length>0)}function normalizeLineEndings(value){return value.replace(/\r\n/g,`
3
3
  `)}function stripCodeFenceEnvelope(value){const normalized=normalizeLineEndings(value).trim();if(!normalized.startsWith("```"))return value;const lines=normalized.split(`
4
4
  `);return lines.length<2||lines.at(-1)?.trim()!=="```"?value:lines.slice(1,-1).join(`
5
5
  `)}function splitCsvSegments(raw){const segments=[];let current="",inQuotes=!1;for(let index=0;index<raw.length;index+=1){const char=raw[index];if(char==="\\"&&raw[index+1]===","){current+=",",index+=1;continue}if(char==="\\"&&raw[index+1]!==void 0){current+=`${char}${raw[index+1]}`,index+=1;continue}if(char==='"'){inQuotes=!inQuotes,current+=char;continue}if(char===","&&!inQuotes){segments.push(current),current="";continue}current+=char}return current.trim()&&segments.push(current),segments}function unquoteValue(value){const trimmed=value.trim();if(trimmed.startsWith('"')&&trimmed.endsWith('"')){try{const parsed=JSON.parse(trimmed);if(typeof parsed=="string")return parsed}catch{}return trimmed.slice(1,-1).replace(/\\"/g,'"')}return value}function findKeyValueDelimiter(segment){const equalsIndex=segment.indexOf("="),colonIndex=segment.indexOf(":"),hasEquals=equalsIndex>=0,hasColon=colonIndex>=0;return hasEquals&&hasColon?Math.min(equalsIndex,colonIndex):hasEquals?equalsIndex:hasColon?colonIndex:-1}function buildOptionSpecificKvGuidance(raw,optionName){if((optionName==="--add"||optionName==="--add-glob")&&(/[./\\]/.test(raw)||/\.[a-z]{1,6}$/i.test(raw.trim())))return"For file/doc paths use: path=<file-path>[,scope=project|global]. The scope field is optional and defaults to project (example: path=src/api.ts or path=README.md,scope=project). ";if(optionName!=="--event")return"";const lowered=raw.toLowerCase(),recurrenceHint=lowered.includes("recur_")||lowered.includes("recurrence")?' Recurrence list values must stay in one field and use "|" delimiters (for example recur_by_weekday=mon|wed or recur_by_month_day=1|15).':"",weekdayAliasHint=lowered.includes("recur_byweekday")?" Use recur_by_weekday (with underscores) for weekday recurrence filters.":"";return`${recurrenceHint}${weekdayAliasHint}`}function buildInvalidKvMessage(raw,optionName){const condensed=raw.replaceAll(/\s+/g," ").trim(),preview=condensed.length>160?`${condensed.slice(0,157)}...`:condensed,optionSpecificGuidance=buildOptionSpecificKvGuidance(raw,optionName);return`Invalid ${optionName} value "${preview}". Expected key=value entries separated by commas. `+optionSpecificGuidance+`Also accepts markdown-style key/value lines (for example "- path: README.md"). Use ${optionName} ${STDIN_TOKEN} to read piped stdin input.`}function parseMarkdownKeyValueLines(raw){const normalized=stripCodeFenceEnvelope(raw).trim();if(normalized.length===0||!normalized.includes(`
6
6
  `)&&!normalized.startsWith("-")&&!normalized.startsWith("*")&&!normalized.startsWith("+"))return null;const result={};let activeKey;const lines=normalizeLineEndings(normalized).split(`
7
7
  `).map(line=>line.trim()).filter(line=>line.length>0);for(const line of lines){const match=line.match(/^(?:[-*+]\s+)?([a-zA-Z0-9_.-]+)\s*(=|:)\s*(.*)$/);if(match){const key=match[1].trim();result[key]=unquoteValue(match[3].trim()),activeKey=key;continue}if(activeKey&&CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())){result[activeKey]=`${result[activeKey]}
8
- ${line}`;continue}return null}return result}function parseCsvKv(raw,optionName){const trimmed=stripCodeFenceEnvelope(raw).trim();if(!trimmed)throw new PmCliError(`${optionName} cannot be empty`,EXIT_CODE.USAGE);const markdownStyle=parseMarkdownKeyValueLines(trimmed);if(markdownStyle)return markdownStyle;const result={};let activeKey;const segments=splitCsvSegments(trimmed);for(const segment of segments){const delimiterIndex=findKeyValueDelimiter(segment);if(delimiterIndex>=0){const key=segment.slice(0,delimiterIndex).trim();if(!key)throw new PmCliError(buildInvalidKvMessage(raw,optionName),EXIT_CODE.USAGE);const value=unquoteValue(segment.slice(delimiterIndex+1).trim());result[key]=value,activeKey=key;continue}if(activeKey&&CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())){result[activeKey]=`${result[activeKey]},${segment}`;continue}throw new PmCliError(buildInvalidKvMessage(raw,optionName),EXIT_CODE.USAGE)}return result}const WINDOWS_ABSOLUTE_PATH_PATTERN=/^[A-Za-z]:[\\/]/,GENERIC_LEADING_KV_KEY_PATTERN=/^(?:[-*+]\s+)?[A-Za-z_][A-Za-z0-9_.-]*\s*=/;function looksLikeGenericKeyValueEntry(raw){const trimmed=raw.trim();return WINDOWS_ABSOLUTE_PATH_PATTERN.test(trimmed)?!1:GENERIC_LEADING_KV_KEY_PATTERN.test(trimmed)}function assertNoAmbiguousBareCommaEntry(raw,optionName,noun,structured){if(!(structured||!raw.includes(",")))throw new PmCliError(`${optionName} received an ambiguous comma-bearing bare ${noun}. Repeat ${optionName} for multiple values, or quote or escape literal commas in a structured key=<value> entry.`,EXIT_CODE.USAGE,{code:"bare_comma_entry_ambiguous",required:`Use one ${optionName} per bare ${noun}, or quote or escape literal commas in a structured key=<value> entry.`})}function assertNoUnknownCsvKeys(kv,optionName,allowedKeys){const allowed=new Set(allowedKeys.map(key=>key.toLowerCase())),unknownKeys=Object.keys(kv).filter(key=>!allowed.has(key.toLowerCase()));if(unknownKeys.length>0)throw new PmCliError(`${optionName} does not recognize key${unknownKeys.length>1?"s":""} ${unknownKeys.map(key=>`"${key}"`).join(", ")}. Allowed keys: ${allowedKeys.join(", ")}.`,EXIT_CODE.USAGE);for(const key of Object.keys(kv)){const normalizedKey=key.toLowerCase();if(normalizedKey!==key){if(Object.hasOwn(kv,normalizedKey))throw new PmCliError(`${optionName} provides key "${key}" more than once after case normalization.`,EXIT_CODE.USAGE);kv[normalizedKey]=kv[key],delete kv[key]}}}async function readStdinText(optionName){if(process.stdin.isTTY===!0)throw new PmCliError(`${optionName} value "${STDIN_TOKEN}" requires piped stdin input. Pipe content into the command, or end manual stdin with Ctrl+D (Unix/macOS) or Ctrl+Z then Enter (Windows).`,EXIT_CODE.USAGE);return process.stdin.setEncoding("utf8"),await new Promise((resolve,reject)=>{let input="";process.stdin.on("data",chunk=>{input+=chunk}),process.stdin.on("end",()=>{resolve(normalizeLineEndings(input))}),process.stdin.on("error",error=>{reject(error)})})}const ALL_MUTATION_STDIN_FIELDS=Symbol("all-mutation-stdin-fields"),literalStdinTokenFields=new WeakMap;function preserveSdkMutationInput(options){return isCliMutationInput(options)?options:preserveLiteralMutationInput(preserveMutationStdinTokenLiterals({...options}))}function preserveMutationStdinTokenLiterals(options){return literalStdinTokenFields.set(options,ALL_MUTATION_STDIN_FIELDS),options}function preserveMutationStdinTokenFields(options,fields){const current=literalStdinTokenFields.get(options);return current===ALL_MUTATION_STDIN_FIELDS||literalStdinTokenFields.set(options,new Set([...current??[],...fields])),options}function transferMutationStdinTokenPolicy(source,target){transferLiteralMutationInput(source,target);const policy=literalStdinTokenFields.get(source);return policy===void 0||literalStdinTokenFields.set(target,policy===ALL_MUTATION_STDIN_FIELDS?policy:new Set(policy)),target}function overlayMutationStdinTokenPolicy(source,target){const sourcePolicy=literalStdinTokenFields.get(source),targetPolicy=literalStdinTokenFields.get(target);return sourcePolicy===void 0||targetPolicy===ALL_MUTATION_STDIN_FIELDS?target:sourcePolicy===ALL_MUTATION_STDIN_FIELDS?(literalStdinTokenFields.set(target,sourcePolicy),target):(literalStdinTokenFields.set(target,new Set([...targetPolicy??[],...sourcePolicy])),target)}function shouldResolveMutationStdinTokens(options){return literalStdinTokenFields.get(options)!==ALL_MUTATION_STDIN_FIELDS}function shouldResolveMutationStdinTokenField(options,field){const literalFields=literalStdinTokenFields.get(options);return literalFields!==ALL_MUTATION_STDIN_FIELDS&&!literalFields?.has(field)}function assertSingleStdinTokenConsumer(candidates){let stdinConsumerOption;for(const candidate of candidates){if(candidate.resolveToken===!1||candidate.value===void 0)continue;const tokenCount=(Array.isArray(candidate.value)?candidate.value:[candidate.value]).filter(value=>value.trim()===STDIN_TOKEN).length;if(tokenCount>1)throw new PmCliError(`${candidate.optionName} accepts "${STDIN_TOKEN}" stdin token at most once per command invocation`,EXIT_CODE.USAGE);if(tokenCount!==0){if(stdinConsumerOption!==void 0&&stdinConsumerOption!==candidate.optionName)throw new PmCliError(`Only one option may use "${STDIN_TOKEN}" stdin token per command invocation. Already used by ${stdinConsumerOption}.`,EXIT_CODE.USAGE);stdinConsumerOption=candidate.optionName}}}async function resolveMutationStdinTokenFields(options,scalarFields,listFields){const source=options,definitions=[...scalarFields,...listFields];assertSingleStdinTokenConsumer(definitions.map(([field,optionName])=>({value:source[field],optionName,resolveToken:shouldResolveMutationStdinTokenField(options,field)})));const stdinResolver=createStdinTokenResolver(),resolved={...source};for(const[field,optionName]of scalarFields)resolved[field]=await stdinResolver.resolveValue(source[field],optionName,shouldResolveMutationStdinTokenField(options,field));for(const[field,optionName]of listFields)resolved[field]=await stdinResolver.resolveList(source[field],optionName,shouldResolveMutationStdinTokenField(options,field));return preserveMutationStdinTokenLiterals(transferMutationStdinTokenPolicy(options,resolved))}function createStdinTokenResolver(){let stdinValuePromise,stdinConsumerOption;const consumeStdin=async optionName=>{if(stdinConsumerOption&&stdinConsumerOption!==optionName)throw new PmCliError(`Only one option may use "${STDIN_TOKEN}" stdin token per command invocation. Already used by ${stdinConsumerOption}.`,EXIT_CODE.USAGE);return stdinConsumerOption=optionName,stdinValuePromise||(stdinValuePromise=readStdinText(optionName)),await stdinValuePromise};return{resolveValue:async(value,optionName,resolveToken=!0)=>{if(!resolveToken)return value;if(value!==void 0)return value.trim()!==STDIN_TOKEN?value:await consumeStdin(optionName)},resolveList:async(values,optionName,resolveToken=!0)=>{if(!resolveToken)return values;if(!values)return;const tokenIndexes=values.map((entry,index)=>entry.trim()===STDIN_TOKEN?index:-1).filter(index=>index>=0);if(tokenIndexes.length===0)return values;if(tokenIndexes.length>1)throw new PmCliError(`${optionName} accepts "${STDIN_TOKEN}" stdin token at most once per command invocation`,EXIT_CODE.USAGE);const stdinValue=await consumeStdin(optionName),next=[...values];return next[tokenIndexes[0]]=stdinValue,next}}}function parseOptionalNumber(raw,optionName){const value=Number(raw);if(!Number.isFinite(value))throw new PmCliError(`Invalid ${optionName} value "${raw}"`,EXIT_CODE.USAGE);return value}function parseOptionalNonNegativeInteger(raw,optionName){const trimmed=raw.trim(),value=Number(trimmed);if(trimmed===""||!Number.isInteger(value)||value<0)throw new PmCliError(`Invalid ${optionName} value "${raw}". Expected a non-negative integer.`,EXIT_CODE.USAGE);return value}const _testOnly={coerceJsonTagArray,stripCodeFenceEnvelope,parseMarkdownKeyValueLines};export{_testOnly,applyAcceptanceCriteriaMutations,applyTagRemovals,assertNoAmbiguousBareCommaEntry,assertNoUnknownCsvKeys,assertSingleStdinTokenConsumer,collectTagFlagValues,createStdinTokenResolver,looksLikeGenericKeyValueEntry,looksLikeStructuredKeyValueEntry,mergeAdditiveTags,overlayMutationStdinTokenPolicy,parseCsvKv,parseOptionalNonNegativeInteger,parseOptionalNumber,parseTags,preserveMutationStdinTokenFields,preserveMutationStdinTokenLiterals,preserveSdkMutationInput,resolveMutationStdinTokenFields,shouldResolveMutationStdinTokenField,shouldResolveMutationStdinTokens,splitAcceptanceCriteria,transferMutationStdinTokenPolicy};
8
+ ${line}`;continue}return null}return result}function parseCsvKv(raw,optionName){const trimmed=stripCodeFenceEnvelope(raw).trim();if(!trimmed)throw new PmCliError(`${optionName} cannot be empty`,EXIT_CODE.USAGE);const markdownStyle=parseMarkdownKeyValueLines(trimmed);if(markdownStyle)return markdownStyle;const result={};let activeKey;const segments=splitCsvSegments(trimmed);for(const segment of segments){const delimiterIndex=findKeyValueDelimiter(segment),candidateKey=segment.slice(0,Math.max(delimiterIndex,0)).trim();if(candidateKey!==""&&activeKey!==void 0&&CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())&&!CSV_KEY_PATTERN.test(candidateKey)){result[activeKey]=`${result[activeKey]},${segment}`;continue}if(delimiterIndex>=0){const key=segment.slice(0,delimiterIndex).trim();if(!key)throw new PmCliError(buildInvalidKvMessage(raw,optionName),EXIT_CODE.USAGE);const value=unquoteValue(segment.slice(delimiterIndex+1).trim());result[key]=value,activeKey=key;continue}if(activeKey&&CONTINUABLE_VALUE_KEYS.has(activeKey.toLowerCase())){result[activeKey]=`${result[activeKey]},${segment}`;continue}throw new PmCliError(buildInvalidKvMessage(raw,optionName),EXIT_CODE.USAGE)}return result}const WINDOWS_ABSOLUTE_PATH_PATTERN=/^[A-Za-z]:[\\/]/,GENERIC_LEADING_KV_KEY_PATTERN=/^(?:[-*+]\s+)?[A-Za-z_][A-Za-z0-9_.-]*\s*=/;function looksLikeGenericKeyValueEntry(raw){const trimmed=raw.trim();return WINDOWS_ABSOLUTE_PATH_PATTERN.test(trimmed)?!1:GENERIC_LEADING_KV_KEY_PATTERN.test(trimmed)}function assertNoAmbiguousBareCommaEntry(raw,optionName,noun,structured){if(!(structured||!raw.includes(",")))throw new PmCliError(`${optionName} received an ambiguous comma-bearing bare ${noun}. Repeat ${optionName} for multiple values, or quote or escape literal commas in a structured key=<value> entry.`,EXIT_CODE.USAGE,{code:"bare_comma_entry_ambiguous",required:`Use one ${optionName} per bare ${noun}, or quote or escape literal commas in a structured key=<value> entry.`})}function assertNoUnknownCsvKeys(kv,optionName,allowedKeys){const allowed=new Set(allowedKeys.map(key=>key.toLowerCase())),unknownKeys=Object.keys(kv).filter(key=>!allowed.has(key.toLowerCase()));if(unknownKeys.length>0)throw new PmCliError(`${optionName} does not recognize key${unknownKeys.length>1?"s":""} ${unknownKeys.map(key=>`"${key}"`).join(", ")}. Allowed keys: ${allowedKeys.join(", ")}.`,EXIT_CODE.USAGE);for(const key of Object.keys(kv)){const normalizedKey=key.toLowerCase();if(normalizedKey!==key){if(Object.hasOwn(kv,normalizedKey))throw new PmCliError(`${optionName} provides key "${key}" more than once after case normalization.`,EXIT_CODE.USAGE);kv[normalizedKey]=kv[key],delete kv[key]}}}async function readStdinText(optionName){if(process.stdin.isTTY===!0)throw new PmCliError(`${optionName} value "${STDIN_TOKEN}" requires piped stdin input. Pipe content into the command, or end manual stdin with Ctrl+D (Unix/macOS) or Ctrl+Z then Enter (Windows).`,EXIT_CODE.USAGE);return process.stdin.setEncoding("utf8"),await new Promise((resolve,reject)=>{let input="";process.stdin.on("data",chunk=>{input+=chunk}),process.stdin.on("end",()=>{resolve(normalizeLineEndings(input))}),process.stdin.on("error",error=>{reject(error)})})}const ALL_MUTATION_STDIN_FIELDS=Symbol("all-mutation-stdin-fields"),literalStdinTokenFields=new WeakMap;function preserveSdkMutationInput(options){return isCliMutationInput(options)?options:preserveLiteralMutationInput(preserveMutationStdinTokenLiterals({...options}))}function preserveMutationStdinTokenLiterals(options){return literalStdinTokenFields.set(options,ALL_MUTATION_STDIN_FIELDS),options}function preserveMutationStdinTokenFields(options,fields){const current=literalStdinTokenFields.get(options);return current===ALL_MUTATION_STDIN_FIELDS||literalStdinTokenFields.set(options,new Set([...current??[],...fields])),options}function transferMutationStdinTokenPolicy(source,target){transferLiteralMutationInput(source,target);const policy=literalStdinTokenFields.get(source);return policy===void 0||literalStdinTokenFields.set(target,policy===ALL_MUTATION_STDIN_FIELDS?policy:new Set(policy)),target}function overlayMutationStdinTokenPolicy(source,target){const sourcePolicy=literalStdinTokenFields.get(source),targetPolicy=literalStdinTokenFields.get(target);return sourcePolicy===void 0||targetPolicy===ALL_MUTATION_STDIN_FIELDS?target:sourcePolicy===ALL_MUTATION_STDIN_FIELDS?(literalStdinTokenFields.set(target,sourcePolicy),target):(literalStdinTokenFields.set(target,new Set([...targetPolicy??[],...sourcePolicy])),target)}function shouldResolveMutationStdinTokens(options){return literalStdinTokenFields.get(options)!==ALL_MUTATION_STDIN_FIELDS}function shouldResolveMutationStdinTokenField(options,field){const literalFields=literalStdinTokenFields.get(options);return literalFields!==ALL_MUTATION_STDIN_FIELDS&&!literalFields?.has(field)}function assertSingleStdinTokenConsumer(candidates){let stdinConsumerOption;for(const candidate of candidates){if(candidate.resolveToken===!1||candidate.value===void 0)continue;const tokenCount=(Array.isArray(candidate.value)?candidate.value:[candidate.value]).filter(value=>value.trim()===STDIN_TOKEN).length;if(tokenCount>1)throw new PmCliError(`${candidate.optionName} accepts "${STDIN_TOKEN}" stdin token at most once per command invocation`,EXIT_CODE.USAGE);if(tokenCount!==0){if(stdinConsumerOption!==void 0&&stdinConsumerOption!==candidate.optionName)throw new PmCliError(`Only one option may use "${STDIN_TOKEN}" stdin token per command invocation. Already used by ${stdinConsumerOption}.`,EXIT_CODE.USAGE);stdinConsumerOption=candidate.optionName}}}async function resolveMutationStdinTokenFields(options,scalarFields,listFields){const source=options,definitions=[...scalarFields,...listFields];assertSingleStdinTokenConsumer(definitions.map(([field,optionName])=>({value:source[field],optionName,resolveToken:shouldResolveMutationStdinTokenField(options,field)})));const stdinResolver=createStdinTokenResolver(),resolved={...source};for(const[field,optionName]of scalarFields)resolved[field]=await stdinResolver.resolveValue(source[field],optionName,shouldResolveMutationStdinTokenField(options,field));for(const[field,optionName]of listFields)resolved[field]=await stdinResolver.resolveList(source[field],optionName,shouldResolveMutationStdinTokenField(options,field));return preserveMutationStdinTokenLiterals(transferMutationStdinTokenPolicy(options,resolved))}function createStdinTokenResolver(){let stdinValuePromise,stdinConsumerOption;const consumeStdin=async optionName=>{if(stdinConsumerOption&&stdinConsumerOption!==optionName)throw new PmCliError(`Only one option may use "${STDIN_TOKEN}" stdin token per command invocation. Already used by ${stdinConsumerOption}.`,EXIT_CODE.USAGE);return stdinConsumerOption=optionName,stdinValuePromise||(stdinValuePromise=readStdinText(optionName)),await stdinValuePromise};return{resolveValue:async(value,optionName,resolveToken=!0)=>{if(!resolveToken)return value;if(value!==void 0)return value.trim()!==STDIN_TOKEN?value:await consumeStdin(optionName)},resolveList:async(values,optionName,resolveToken=!0)=>{if(!resolveToken)return values;if(!values)return;const tokenIndexes=values.map((entry,index)=>entry.trim()===STDIN_TOKEN?index:-1).filter(index=>index>=0);if(tokenIndexes.length===0)return values;if(tokenIndexes.length>1)throw new PmCliError(`${optionName} accepts "${STDIN_TOKEN}" stdin token at most once per command invocation`,EXIT_CODE.USAGE);const stdinValue=await consumeStdin(optionName),next=[...values];return next[tokenIndexes[0]]=stdinValue,next}}}function parseOptionalNumber(raw,optionName){const value=Number(raw);if(!Number.isFinite(value))throw new PmCliError(`Invalid ${optionName} value "${raw}"`,EXIT_CODE.USAGE);return value}function parseOptionalNonNegativeInteger(raw,optionName){const trimmed=raw.trim(),value=Number(trimmed);if(trimmed===""||!Number.isInteger(value)||value<0)throw new PmCliError(`Invalid ${optionName} value "${raw}". Expected a non-negative integer.`,EXIT_CODE.USAGE);return value}const _testOnly={coerceJsonTagArray,stripCodeFenceEnvelope,parseMarkdownKeyValueLines};export{_testOnly,applyAcceptanceCriteriaMutations,applyTagRemovals,assertNoAmbiguousBareCommaEntry,assertNoUnknownCsvKeys,assertSingleStdinTokenConsumer,collectTagFlagValues,createStdinTokenResolver,looksLikeGenericKeyValueEntry,looksLikeStructuredKeyValueEntry,mergeAdditiveTags,overlayMutationStdinTokenPolicy,parseCsvKv,parseOptionalNonNegativeInteger,parseOptionalNumber,parseTags,preserveMutationStdinTokenFields,preserveMutationStdinTokenLiterals,preserveSdkMutationInput,resolveMutationStdinTokenFields,shouldResolveMutationStdinTokenField,shouldResolveMutationStdinTokens,splitAcceptanceCriteria,transferMutationStdinTokenPolicy};
9
9
  //# sourceMappingURL=parse.js.map
@@ -0,0 +1,55 @@
1
+ import { type FileHandle } from "node:fs/promises";
2
+ import { PmCliError } from "../shared/errors.js";
3
+ /** Typed refusal code for a lock path that leaves, or could leave, the tracker. */
4
+ export declare const LOCK_PATH_UNSAFE_CODE = "lock_path_unsafe";
5
+ /**
6
+ * Device and inode identity of a confined lock directory or lock entry, as
7
+ * exact bigints: numeric stats lose precision above Number.MAX_SAFE_INTEGER
8
+ * (64-bit file ids on NTFS, ReFS and some network filesystems), where two
9
+ * distinct entries could compare equal.
10
+ */
11
+ export interface LockEntryIdentity {
12
+ /** Device that holds the entry. */
13
+ readonly dev:bigint;
14
+ /** Inode of the entry on that device. */
15
+ readonly ino:bigint;
16
+ }
17
+ /** Stable failure classes reported in the `reason` of a lock_path_unsafe refusal. */
18
+ export type LockPathUnsafeReason = "lock_path_outside_tracker" | "lock_directory_symlink" | "lock_directory_not_directory" | "lock_entry_symlink" | "lock_directory_replaced";
19
+ /** Build the typed refusal for a lock path that cannot be proven confined. */
20
+ export declare function buildLockPathUnsafeError(params:{
21
+ pmRoot:string;
22
+ lockPath:string;
23
+ id:string;
24
+ reason:LockPathUnsafeReason;
25
+ component?:string;
26
+ }):PmCliError;
27
+ /**
28
+ * Verify that every path component between the tracker root and the lock's
29
+ * parent directory is a real directory, never a symlink. Returns the parent's
30
+ * identity. A missing component returns null when `allowMissing` (the caller's
31
+ * own creation or absence handling then applies) and otherwise rethrows the
32
+ * native error. The tracker root itself may be a symlink; confinement is
33
+ * relative to it.
34
+ */
35
+ export declare function inspectConfinedLockDirectory(pmRoot:string,lockPath:string,id:string,allowMissing:false):Promise<LockEntryIdentity>;
36
+ /** Inspect the lock directory, returning null when a component is missing and absence is allowed. */
37
+ export declare function inspectConfinedLockDirectory(pmRoot:string,lockPath:string,id:string,allowMissing?:boolean):Promise<LockEntryIdentity | null>;
38
+ /** Refuse a lock entry that is itself a symlink instead of waiting on it as a peer's lock. */
39
+ export declare function assertLockEntryNotSymlink(pmRoot:string,lockPath:string,id:string):Promise<void>;
40
+ /**
41
+ * Prove that the lock just created through `handle` is the entry at `lockPath`
42
+ * inside the same confined directory observed before creation. A directory
43
+ * swapped between the check and the create means the lock landed elsewhere, so
44
+ * the acquisition refuses with lock_path_unsafe instead of returning a lease
45
+ * that excludes no one. The stray entry is left in place: Node has no
46
+ * descriptor-relative unlink, and a path-based unlink after the identity check
47
+ * could delete a same-name replacement outside the tracker.
48
+ */
49
+ export declare function verifyConfinedLockCreation(params:{
50
+ pmRoot:string;
51
+ lockPath:string;
52
+ id:string;
53
+ handle:FileHandle;
54
+ directory:LockEntryIdentity;
55
+ }):Promise<void>;
@@ -0,0 +1,2 @@
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f1eb25ad-08b8-5826-9ec1-7b0a7a22d277")}catch{}})();import fs from"node:fs/promises";import path from"node:path";import{isFileMissingError}from"../fs/fs-utils.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";const LOCK_PATH_UNSAFE_CODE="lock_path_unsafe";function toPortableRelative(pmRoot,target){return path.relative(pmRoot,target).split(path.sep).join("/")}function describeLockPathRecovery(reason,relativeComponent){return reason==="lock_entry_symlink"?`Remove the symlinked lock entry ${relativeComponent} (pm neither follows nor deletes it), then retry the same operation.`:reason==="lock_directory_replaced"?`Restore ${relativeComponent} as a real directory inside the tracker, then retry the same operation. The lock file created while it was replaced stays where the replacement pointed, because pm never deletes outside the tracker; remove it there if it is not wanted.`:"Replace the symlinked or replaced lock directory with a real directory inside the tracker (for example: remove the link, then let pm recreate locks/), then retry the same operation."}function buildLockPathUnsafeError(params){const component=params.component??path.dirname(params.lockPath),relativeComponent=toPortableRelative(params.pmRoot,component);return new PmCliError(`Refused to acquire lock ${params.id}: ${relativeComponent} is not a real directory inside the tracker (${params.reason}). Locks are never created through a symlinked or replaced lock directory.`,EXIT_CODE.CONFLICT,{code:"lock_path_unsafe",item_id:params.id,reason:params.reason,lock_component:relativeComponent,required:describeLockPathRecovery(params.reason,relativeComponent)})}function sameIdentity(left,right){return left.dev===right.dev&&left.ino===right.ino}async function inspectConfinedLockDirectory(pmRoot,lockPath,id,allowMissing=!0){const root=path.resolve(pmRoot),directory=path.dirname(path.resolve(lockPath)),relative=path.relative(root,directory);if(relative===".."||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative))throw buildLockPathUnsafeError({pmRoot:root,lockPath,id,reason:"lock_path_outside_tracker",component:directory});let current=root,identity=null;for(const segment of relative===""?[]:relative.split(path.sep)){current=path.join(current,segment);let stats;try{stats=await fs.lstat(current,{bigint:!0})}catch(error){if(allowMissing&&isFileMissingError(error))return null;throw error}if(stats.isSymbolicLink())throw buildLockPathUnsafeError({pmRoot:root,lockPath,id,reason:"lock_directory_symlink",component:current});if(!stats.isDirectory())throw buildLockPathUnsafeError({pmRoot:root,lockPath,id,reason:"lock_directory_not_directory",component:current});identity={dev:stats.dev,ino:stats.ino}}if(identity!==null)return identity;const rootStats=await fs.stat(root,{bigint:!0});return{dev:rootStats.dev,ino:rootStats.ino}}async function assertLockEntryNotSymlink(pmRoot,lockPath,id){let stats;try{stats=await fs.lstat(lockPath)}catch{return}if(stats.isSymbolicLink())throw buildLockPathUnsafeError({pmRoot,lockPath,id,reason:"lock_entry_symlink",component:lockPath})}async function verifyConfinedLockCreation(params){const createdStats=await params.handle.stat({bigint:!0}),created={dev:createdStats.dev,ino:createdStats.ino};let confined=!1;try{const directory=await inspectConfinedLockDirectory(params.pmRoot,params.lockPath,params.id,!1);if(sameIdentity(params.directory,directory)){const entry=await fs.lstat(params.lockPath,{bigint:!0});confined=!entry.isSymbolicLink()&&sameIdentity(created,entry)}}catch{confined=!1}if(!confined)throw buildLockPathUnsafeError({pmRoot:params.pmRoot,lockPath:params.lockPath,id:params.id,reason:"lock_directory_replaced"})}export{LOCK_PATH_UNSAFE_CODE,assertLockEntryNotSymlink,buildLockPathUnsafeError,inspectConfinedLockDirectory,verifyConfinedLockCreation};
2
+ //# sourceMappingURL=lock-confinement.js.map
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6b4ef85f-d844-53e3-ba32-08b95377584a")}catch{}})();import{inspectLockOwner}from"./lock-owner.js";import{acquireStaleCleanupGate,recordDeadLockRecovery}from"./lock.js";import{isFileMissingError}from"../fs/fs-utils.js";import fs from"node:fs/promises";import path from"node:path";function parseLock(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return null}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return null;const candidate=parsed,{id,owner,created_at,ttl_seconds}=candidate;return typeof id!="string"||typeof owner!="string"||typeof created_at!="string"||typeof ttl_seconds!="number"||!Number.isFinite(ttl_seconds)?null:{id,owner,created_at,ttl_seconds,pid:candidate.pid,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity}}function classifyLockContent(file,raw,nowMs){const lock=parseLock(raw);if(lock===null)return{detail:"unparseable_json",entry:{file,id:null,owner:null,created_at:null,ttl_seconds:null,age_seconds:null,stale:!1,reason:"unparseable"}};const createdAtMs=Date.parse(lock.created_at);if(!Number.isFinite(createdAtMs))return{detail:"invalid_timestamp",entry:{file,id:lock.id,owner:lock.owner,created_at:lock.created_at,ttl_seconds:lock.ttl_seconds,age_seconds:null,stale:!1,reason:"unparseable"}};const ageMs=nowMs-createdAtMs,age_seconds=Math.floor(ageMs/1e3),stale=ageMs>lock.ttl_seconds*1e3;return{detail:stale?"expired":"active",entry:{file,id:lock.id,owner:lock.owner,created_at:lock.created_at,ttl_seconds:lock.ttl_seconds,age_seconds,stale,reason:stale?"expired":"active"}}}async function classifyObservedLock(file,raw,nowMs){const classified=classifyLockContent(file,raw,nowMs);if(classified.detail!=="active")return classified;const lock=parseLock(raw),state=await inspectLockOwner(lock);return state==="unknown"?classified:{detail:state==="dead"?"owner_dead":"active",entry:{...classified.entry,owner_state:state,pid:lock.pid,...state==="dead"?{stale:!0,reason:"owner_dead"}:{}}}}async function removeObservedLock(lockPath,raw,ownerDead){const release=await acquireStaleCleanupGate(lockPath,"gc");if(release===null)return!1;try{let current;try{current=await fs.readFile(lockPath,"utf8")}catch(error){if(isFileMissingError(error))return!0;throw error}if(current!==raw)return!1;ownerDead&&await recordDeadLockRecovery(lockPath,raw);try{await fs.unlink(lockPath)}catch(error){if(!isFileMissingError(error))throw error}return!0}finally{await release()}}function addHealthObservation(scan,detail,entry){detail==="owner_dead"&&(scan.owner_dead_lock_samples??=[],scan.owner_dead_lock_samples.length<20&&scan.owner_dead_lock_samples.push(entry)),detail==="active"?scan.active_lock_count+=1:detail==="expired"||detail==="owner_dead"?scan.stale_lock_count+=1:scan.unparseable_lock_count+=1}async function scanLockHealth(pmRoot,now){const nowMs=now??Date.now(),locksDir=path.join(pmRoot,"locks"),scan={scanned:0,active_lock_count:0,stale_lock_count:0,unreadable_lock_count:0,unparseable_lock_count:0};let dirEntries;try{dirEntries=(await fs.readdir(locksDir)).filter(f=>f.endsWith(".lock")).sort()}catch(error){if(isFileMissingError(error))return scan;throw error}for(const file of dirEntries){let raw;try{raw=await fs.readFile(path.join(locksDir,file),"utf8")}catch(readError){if(isFileMissingError(readError))continue;scan.scanned+=1,scan.unreadable_lock_count+=1;continue}scan.scanned+=1;const{detail,entry}=await classifyObservedLock(file,raw,nowMs);addHealthObservation(scan,detail,entry)}return scan}async function removeOrPreviewLock(result,options,absPath,relPath,file,raw,entry){if(!options.dryRun){try{if(!await removeObservedLock(absPath,raw,entry.reason==="owner_dead")){result.warnings.push(`lock_changed_or_busy:${file}`),result.retained.push(relPath),result.entries.push(entry);return}}catch{result.warnings.push(`lock_remove_failed:${file}`),result.retained.push(relPath),result.entries.push(entry);return}if(options.hooks?.onWrite){const hookWarnings=await options.hooks.onWrite(absPath);result.warnings.push(...hookWarnings)}}result.removed.push(relPath),result.entries.push(entry)}async function runLockGc(pmRoot,options){const{hooks}=options,nowMs=options.now??Date.now(),locksDir=path.join(pmRoot,"locks"),result={scanned:0,removed:[],retained:[],warnings:[],entries:[]};let dirEntries;try{dirEntries=(await fs.readdir(locksDir)).filter(f=>f.endsWith(".lock")).sort()}catch(error){if(isFileMissingError(error))return result;throw error}for(const file of dirEntries){result.scanned+=1;const absPath=path.join(locksDir,file),relPath=`locks/${file}`;if(hooks?.onRead){const hookWarnings=await hooks.onRead(absPath);result.warnings.push(...hookWarnings)}let raw;try{raw=await fs.readFile(absPath,"utf8")}catch(readError){if(isFileMissingError(readError)){result.scanned-=1;continue}result.warnings.push(`lock_unreadable:${file}`),result.retained.push(relPath),result.entries.push({file,id:null,owner:null,created_at:null,ttl_seconds:null,age_seconds:null,stale:!1,reason:"unparseable"});continue}const classified=await classifyObservedLock(file,raw,nowMs);if(classified.detail==="unparseable_json"){result.warnings.push(`lock_unparseable:${file}`),result.retained.push(relPath),result.entries.push(classified.entry);continue}if(classified.detail==="invalid_timestamp"){result.warnings.push(`lock_invalid_timestamp:${file}`),result.retained.push(relPath),result.entries.push(classified.entry);continue}if(classified.detail==="active"){result.retained.push(relPath),result.entries.push(classified.entry);continue}await removeOrPreviewLock(result,options,absPath,relPath,file,raw,classified.entry)}return result}export{classifyLockContent,runLockGc,scanLockHealth};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b01042e0-77ae-5e2c-8118-6680f0002d60")}catch{}})();import{inspectConfinedLockDirectory}from"./lock-confinement.js";import{inspectLockOwner}from"./lock-owner.js";import{acquireStaleCleanupGate,recordDeadLockRecovery}from"./lock.js";import{isFileMissingError}from"../fs/fs-utils.js";import fs from"node:fs/promises";import path from"node:path";function parseLock(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return null}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return null;const candidate=parsed,{id,owner,created_at,ttl_seconds}=candidate;return typeof id!="string"||typeof owner!="string"||typeof created_at!="string"||typeof ttl_seconds!="number"||!Number.isFinite(ttl_seconds)?null:{id,owner,created_at,ttl_seconds,pid:candidate.pid,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity}}function classifyLockContent(file,raw,nowMs){const lock=parseLock(raw);if(lock===null)return{detail:"unparseable_json",entry:{file,id:null,owner:null,created_at:null,ttl_seconds:null,age_seconds:null,stale:!1,reason:"unparseable"}};const createdAtMs=Date.parse(lock.created_at);if(!Number.isFinite(createdAtMs))return{detail:"invalid_timestamp",entry:{file,id:lock.id,owner:lock.owner,created_at:lock.created_at,ttl_seconds:lock.ttl_seconds,age_seconds:null,stale:!1,reason:"unparseable"}};const ageMs=nowMs-createdAtMs,age_seconds=Math.floor(ageMs/1e3),stale=ageMs>lock.ttl_seconds*1e3;return{detail:stale?"expired":"active",entry:{file,id:lock.id,owner:lock.owner,created_at:lock.created_at,ttl_seconds:lock.ttl_seconds,age_seconds,stale,reason:stale?"expired":"active"}}}async function classifyObservedLock(file,raw,nowMs){const classified=classifyLockContent(file,raw,nowMs);if(classified.detail!=="active")return classified;const lock=parseLock(raw),state=await inspectLockOwner(lock);return state==="unknown"?classified:{detail:state==="dead"?"owner_dead":"active",entry:{...classified.entry,owner_state:state,pid:lock.pid,...state==="dead"?{stale:!0,reason:"owner_dead"}:{}}}}async function removeObservedLock(lockPath,raw,ownerDead){const release=await acquireStaleCleanupGate(lockPath,"gc");if(release===null)return!1;try{let current;try{current=await fs.readFile(lockPath,"utf8")}catch(error){if(isFileMissingError(error))return!0;throw error}if(current!==raw)return!1;ownerDead&&await recordDeadLockRecovery(lockPath,raw);try{await fs.unlink(lockPath)}catch(error){if(!isFileMissingError(error))throw error}return!0}finally{await release()}}function addHealthObservation(scan,detail,entry){detail==="owner_dead"&&(scan.owner_dead_lock_samples??=[],scan.owner_dead_lock_samples.length<20&&scan.owner_dead_lock_samples.push(entry)),detail==="active"?scan.active_lock_count+=1:detail==="expired"||detail==="owner_dead"?scan.stale_lock_count+=1:scan.unparseable_lock_count+=1}async function scanLockHealth(pmRoot,now){const nowMs=now??Date.now(),locksDir=path.join(pmRoot,"locks"),scan={scanned:0,active_lock_count:0,stale_lock_count:0,unreadable_lock_count:0,unparseable_lock_count:0};let dirEntries;try{dirEntries=(await fs.readdir(locksDir)).filter(f=>f.endsWith(".lock")).sort()}catch(error){if(isFileMissingError(error))return scan;throw error}for(const file of dirEntries){let raw;try{raw=await fs.readFile(path.join(locksDir,file),"utf8")}catch(readError){if(isFileMissingError(readError))continue;scan.scanned+=1,scan.unreadable_lock_count+=1;continue}scan.scanned+=1;const{detail,entry}=await classifyObservedLock(file,raw,nowMs);addHealthObservation(scan,detail,entry)}return scan}async function removeOrPreviewLock(result,options,absPath,relPath,file,raw,entry){if(!options.dryRun){try{if(!await removeObservedLock(absPath,raw,entry.reason==="owner_dead")){result.warnings.push(`lock_changed_or_busy:${file}`),result.retained.push(relPath),result.entries.push(entry);return}}catch{result.warnings.push(`lock_remove_failed:${file}`),result.retained.push(relPath),result.entries.push(entry);return}if(options.hooks?.onWrite){const hookWarnings=await options.hooks.onWrite(absPath);result.warnings.push(...hookWarnings)}}result.removed.push(relPath),result.entries.push(entry)}async function runLockGc(pmRoot,options){const{hooks}=options,nowMs=options.now??Date.now(),locksDir=path.join(pmRoot,"locks"),result={scanned:0,removed:[],retained:[],warnings:[],entries:[]};await inspectConfinedLockDirectory(pmRoot,path.join(locksDir,"gc.lock"),"gc");let dirEntries;try{dirEntries=(await fs.readdir(locksDir)).filter(f=>f.endsWith(".lock")).sort()}catch(error){if(isFileMissingError(error))return result;throw error}for(const file of dirEntries){result.scanned+=1;const absPath=path.join(locksDir,file),relPath=`locks/${file}`;if(hooks?.onRead){const hookWarnings=await hooks.onRead(absPath);result.warnings.push(...hookWarnings)}let raw;try{raw=await fs.readFile(absPath,"utf8")}catch(readError){if(isFileMissingError(readError)){result.scanned-=1;continue}result.warnings.push(`lock_unreadable:${file}`),result.retained.push(relPath),result.entries.push({file,id:null,owner:null,created_at:null,ttl_seconds:null,age_seconds:null,stale:!1,reason:"unparseable"});continue}const classified=await classifyObservedLock(file,raw,nowMs);if(classified.detail==="unparseable_json"){result.warnings.push(`lock_unparseable:${file}`),result.retained.push(relPath),result.entries.push(classified.entry);continue}if(classified.detail==="invalid_timestamp"){result.warnings.push(`lock_invalid_timestamp:${file}`),result.retained.push(relPath),result.entries.push(classified.entry);continue}if(classified.detail==="active"){result.retained.push(relPath),result.entries.push(classified.entry);continue}await removeOrPreviewLock(result,options,absPath,relPath,file,raw,classified.entry)}return result}export{classifyLockContent,runLockGc,scanLockHealth};
2
2
  //# sourceMappingURL=lock-gc.js.map
@@ -1,5 +1,5 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fa450dd8-119e-5a50-bdc7-e81c1dfdbd9b")}catch{}})();import{isFileMissingError}from"../fs/fs-utils.js";import{constants as fsConstants}from"node:fs";import fs from"node:fs/promises";import path from"node:path";import{createHash,randomUUID}from"node:crypto";import{runActiveOnReadHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{toErrorMessage}from"../shared/primitives.js";import{getLockPath}from"../store/paths.js";import{nowIso}from"../shared/time.js";import{captureProcessIdentity,captureLockOwnerIdentity,inspectLockOwner,isProcessIdentityAlive,parseLinuxProcessStartIdentity,readProcessStartIdentity}from"./lock-owner.js";import{captureProcessIdentity as captureProcessIdentity2,isProcessIdentityAlive as isProcessIdentityAlive2,readProcessStartIdentity as readProcessStartIdentity2}from"./lock-owner.js";function parseLockInfo(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return{info:null,warnings:["lock_info_invalid_json"]}}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return{info:null,warnings:["lock_info_invalid_shape"]};const candidate=parsed,id=candidate.id,pid=candidate.pid,owner=candidate.owner,token=candidate.token,createdAt=candidate.created_at,ttlSeconds=candidate.ttl_seconds;return typeof id!="string"||typeof pid!="number"||!Number.isFinite(pid)||typeof owner!="string"||token!==void 0&&(typeof token!="string"||token.trim().length===0)||typeof createdAt!="string"||typeof ttlSeconds!="number"||!Number.isFinite(ttlSeconds)?{info:null,warnings:["lock_info_invalid_shape"]}:{info:{id,pid,owner,...typeof token=="string"?{token}:{},created_at:createdAt,ttl_seconds:ttlSeconds,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity},warnings:[]}}async function readLockInfo(lockPath,emitReadHook=!0){try{const raw=await fs.readFile(lockPath,"utf8");return emitReadHook&&await runActiveOnReadHooks({path:lockPath,scope:"project"}),parseLockInfo(raw)}catch(error){return isFileMissingError(error)?{info:null,warnings:[]}:{info:null,warnings:["lock_info_read_failed"]}}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}async function isLockCreateContention(error,lockPath,platform=process.platform){if(isErrno(error,"EEXIST"))return!0;if(!isErrno(error,"EPERM")&&!isErrno(error,"EACCES")&&!isErrno(error,"EBUSY"))return!1;try{return await fs.lstat(lockPath),!0}catch(probeError){if(platform!=="win32"||!isFileMissingError(probeError)&&!isErrno(probeError,"EPERM")&&!isErrno(probeError,"EACCES")&&!isErrno(probeError,"EBUSY"))return!1}try{return await fs.access(path.dirname(lockPath),fsConstants.W_OK),!0}catch{return!1}}async function emitLockWriteHook(lockPath,op){await runActiveOnWriteHooks({path:lockPath,scope:"project",op})}async function buildLockPayload(id,owner,ttlSeconds,token=randomUUID()){return{...await captureLockOwnerIdentity(),id,pid:process.pid,owner,token,created_at:nowIso(),ttl_seconds:ttlSeconds}}async function createLockFile(lockPath,id,owner,ttlSeconds,token,createParentDirectories){createParentDirectories&&await fs.mkdir(path.dirname(lockPath),{recursive:!0});const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null)throw await fs.access(path.dirname(lockPath)),Object.assign(new Error("Lock initialization is contended"),{code:"EEXIST"});let createdAt;try{const handle=await fs.open(lockPath,"wx");try{try{const payload=await buildLockPayload(id,owner,ttlSeconds,token);createdAt=payload.created_at,await handle.writeFile(`${JSON.stringify(payload,null,2)}
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7f116073-a02b-568b-a408-17c0fcc8b4a4")}catch{}})();import{isFileMissingError}from"../fs/fs-utils.js";import{constants as fsConstants}from"node:fs";import fs from"node:fs/promises";import path from"node:path";import{createHash,randomUUID}from"node:crypto";import{runActiveOnReadHooks,runActiveOnWriteHooks,runActiveServiceOverride}from"../extensions/index.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{toErrorMessage}from"../shared/primitives.js";import{getLockPath}from"../store/paths.js";import{nowIso}from"../shared/time.js";import{LOCK_PATH_UNSAFE_CODE,assertLockEntryNotSymlink,inspectConfinedLockDirectory,verifyConfinedLockCreation}from"./lock-confinement.js";import{captureProcessIdentity,captureLockOwnerIdentity,inspectLockOwner,isProcessIdentityAlive,parseLinuxProcessStartIdentity,readProcessStartIdentity}from"./lock-owner.js";import{captureProcessIdentity as captureProcessIdentity2,isProcessIdentityAlive as isProcessIdentityAlive2,readProcessStartIdentity as readProcessStartIdentity2}from"./lock-owner.js";function parseLockInfo(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return{info:null,warnings:["lock_info_invalid_json"]}}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return{info:null,warnings:["lock_info_invalid_shape"]};const candidate=parsed,id=candidate.id,pid=candidate.pid,owner=candidate.owner,token=candidate.token,createdAt=candidate.created_at,ttlSeconds=candidate.ttl_seconds;return typeof id!="string"||typeof pid!="number"||!Number.isFinite(pid)||typeof owner!="string"||token!==void 0&&(typeof token!="string"||token.trim().length===0)||typeof createdAt!="string"||typeof ttlSeconds!="number"||!Number.isFinite(ttlSeconds)?{info:null,warnings:["lock_info_invalid_shape"]}:{info:{id,pid,owner,...typeof token=="string"?{token}:{},created_at:createdAt,ttl_seconds:ttlSeconds,host_identity:candidate.host_identity,process_start_identity:candidate.process_start_identity},warnings:[]}}async function readLockInfo(lockPath,emitReadHook=!0){try{const raw=await fs.readFile(lockPath,"utf8");return emitReadHook&&await runActiveOnReadHooks({path:lockPath,scope:"project"}),parseLockInfo(raw)}catch(error){return isFileMissingError(error)?{info:null,warnings:[]}:{info:null,warnings:["lock_info_read_failed"]}}}function isErrno(error,code){return typeof error=="object"&&error!==null&&"code"in error&&error.code===code}async function isLockCreateContention(error,lockPath,platform=process.platform){if(isErrno(error,"EEXIST"))return!0;if(!isErrno(error,"EPERM")&&!isErrno(error,"EACCES")&&!isErrno(error,"EBUSY"))return!1;try{return await fs.lstat(lockPath),!0}catch(probeError){if(platform!=="win32"||!isFileMissingError(probeError)&&!isErrno(probeError,"EPERM")&&!isErrno(probeError,"EACCES")&&!isErrno(probeError,"EBUSY"))return!1}try{return await fs.access(path.dirname(lockPath),fsConstants.W_OK),!0}catch{return!1}}async function emitLockWriteHook(lockPath,op){await runActiveOnWriteHooks({path:lockPath,scope:"project",op})}async function buildLockPayload(id,owner,ttlSeconds,token=randomUUID()){return{...await captureLockOwnerIdentity(),id,pid:process.pid,owner,token,created_at:nowIso(),ttl_seconds:ttlSeconds}}async function createLockFile(pmRoot,lockPath,id,owner,ttlSeconds,token,createParentDirectories){const existingDirectory=await inspectConfinedLockDirectory(pmRoot,lockPath,id);createParentDirectories&&existingDirectory===null&&await fs.mkdir(path.dirname(lockPath),{recursive:!0});const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null)throw await fs.access(path.dirname(lockPath)),Object.assign(new Error("Lock initialization is contended"),{code:"EEXIST"});let createdAt;try{const directory=await inspectConfinedLockDirectory(pmRoot,lockPath,id,!1),handle=await fs.open(lockPath,"wx");try{await verifyConfinedLockCreation({pmRoot,lockPath,id,handle,directory})}catch(error){throw await handle.close(),error}try{try{const payload=await buildLockPayload(id,owner,ttlSeconds,token);createdAt=payload.created_at,await handle.writeFile(`${JSON.stringify(payload,null,2)}
2
2
  `,"utf8")}finally{await handle.close()}}catch(error){throw await unlinkLockWithHook(lockPath,"lock:release"),error}}finally{await releaseCleanupGate()}return await emitLockWriteHook(lockPath,"lock:create"),createdAt}async function unlinkLockWithHook(lockPath,op){try{return await fs.unlink(lockPath),await emitLockWriteHook(lockPath,op),!0}catch{return!1}}async function releaseOwnedLock(lockPath,id,owner,token){const startedAtMs=performance.now();let backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;for(;;){const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate!==null){try{const current2=await readLockInfo(lockPath,!1);isOwnedLock(current2.info,id,owner,token)&&await unlinkLockWithHook(lockPath,"lock:release")}finally{await releaseCleanupGate()}return}const current=await readLockInfo(lockPath,!1);if(!isOwnedLock(current.info,id,owner,token))return;const elapsedMs=performance.now()-startedAtMs;if(elapsedMs>=LOCK_RELEASE_GATE_WAIT_MS)return;await sleepWithJitter(Math.min(backoffMs,LOCK_RELEASE_GATE_WAIT_MS-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}function isOwnedLock(info,id,owner,token){return info?.id===id&&info.pid===process.pid&&info.owner===owner&&info.token===token}function isStaleLock(info,ttlSeconds){const createdAtMs=info?.created_at?Date.parse(info.created_at):Number.NaN;return(Number.isFinite(createdAtMs)?Date.now()-createdAtMs:Number.POSITIVE_INFINITY)>ttlSeconds*1e3}async function isConfirmedDeadLock(info){return info!==null&&Number.isFinite(Date.parse(info.created_at))&&await inspectLockOwner(info)==="dead"}async function isReclaimableLock(info,ttlSeconds){return isStaleLock(info,ttlSeconds)||await isConfirmedDeadLock(info)}async function hasUnexpiredLockOwner(pmRoot,id,ownerPrefix){const{info}=await readLockInfo(getLockPath(pmRoot,id),!1);return info!==null&&info.owner.startsWith(ownerPrefix)&&!await isReclaimableLock(info,info.ttl_seconds)}function lockOwnerSuffix(info){return info?.owner?` (owner ${info.owner})`:""}const LOCK_WAIT_INITIAL_DELAY_MS=25,LOCK_WAIT_MAX_DELAY_MS=200,LOCK_CONFLICT_RETRY_HINT_MS=250,LOCK_PROGRESS_MAX_WAIT_MS=12e4,MAX_STALE_LOCK_REMOVALS=3,STALE_CLEANUP_GATE_SUFFIX=".stale-cleanup",STALE_CLEANUP_GATE_OWNER_FILE="owner.json",STALE_CLEANUP_GATE_STALE_MS=1e4,LOCK_RELEASE_GATE_WAIT_MS=STALE_CLEANUP_GATE_STALE_MS+LOCK_WAIT_MAX_DELAY_MS;function parseNonNegativeIntegerWaitMs(value){if(typeof value=="string"){const trimmed=value.trim();if(!/^\d+$/.test(trimmed))return;const parsed=Number(trimmed);return Number.isSafeInteger(parsed)?parsed:void 0}if(typeof value=="number"&&Number.isFinite(value)&&value>=0)return Math.trunc(value)}function resolveLockWaitMs(waitMs){const envRaw=process.env.PM_LOCK_WAIT_MS,envParsed=parseNonNegativeIntegerWaitMs(envRaw);if(envParsed!==void 0)return envParsed;const callerParsed=parseNonNegativeIntegerWaitMs(waitMs);return callerParsed!==void 0?callerParsed:0}function cappedLockWaitMs(waitMs,maxWaitMs){return Math.min(resolveLockWaitMs(waitMs),maxWaitMs??1/0)}function sleepWithJitter(baseDelayMs){const jitteredMs=Math.max(1,Math.round(baseDelayMs*(.5+Math.random())));return new Promise(resolve=>setTimeout(resolve,jitteredMs))}async function buildLockConflictError(id,info,waitedMs,ownerHandoffs=0){waitedMs=Math.floor(waitedMs);const ownerState=await inspectLockOwner(info),handoffSuffix=ownerHandoffs>0?` across ${ownerHandoffs} owner handoff(s)`:"",waitedSuffix=waitedMs>0?` after waiting ${waitedMs}ms${handoffSuffix}`:"";return new PmCliError(`Item ${id} is locked${lockOwnerSuffix(info)} (owner process ${ownerState})${waitedSuffix}`,EXIT_CODE.CONFLICT,{code:"lock_conflict",item_id:id,reason:`lock_owner_${ownerState}`,required:"Wait for the owner to release the lock, then retry the same operation. Set PM_LOCK_WAIT_MS to a larger bounded millisecond budget when contention is expected. Inspect stale receipts with pm gc --scope locks --dry-run.",recovery:{retry_after_ms:LOCK_CONFLICT_RETRY_HINT_MS,...waitedMs>0?{waited_ms:waitedMs,owner_handoffs:ownerHandoffs}:{}}})}function createLockWaitTracker(waitBudgetMs,totalCeilingMs){const startedAtMs=performance.now();let stallStartedAtMs=startedAtMs,observedGeneration,ownerHandoffs=0;return{startedAtMs,observe(info){const nowMs=performance.now(),generation=lockGenerationKey(info);observedGeneration!==void 0&&generation!==observedGeneration&&(ownerHandoffs+=1,stallStartedAtMs=nowMs),observedGeneration=generation;const elapsedMs=nowMs-startedAtMs,stalledMs=nowMs-stallStartedAtMs;return{exhausted:waitBudgetMs===0||stalledMs>=waitBudgetMs||elapsedMs>=totalCeilingMs,elapsedMs,remainingMs:Math.min(waitBudgetMs-stalledMs,totalCeilingMs-elapsedMs),ownerHandoffs}}}}function resolveLockWaitCeilingMs(waitBudgetMs,maxWaitMs,waitPolicy){return waitPolicy!=="progress"||maxWaitMs!==void 0?waitBudgetMs:Math.max(waitBudgetMs,LOCK_PROGRESS_MAX_WAIT_MS)}function lockGenerationKey(info){return info===null?"released":info.token??`${info.pid}:${info.owner}:${info.created_at}`}function describeLease(lease){const expiresAtMs=Date.parse(lease.acquiredAt)+lease.ttlSeconds*1e3;return{owner:lease.owner,acquired_at:lease.acquiredAt,ttl_seconds:lease.ttlSeconds,expires_at:new Date(expiresAtMs).toISOString()}}function describeLockLoss(id,lease,current,reason){const leaseLabel=`the lease acquired at ${lease.acquiredAt} (ttl ${lease.ttlSeconds}s)`;if(reason==="fence_gate_unavailable")return{summary:`Lock for ${id} could not be verified before commit: ${leaseLabel} could not enter its commit fence because the stale-cleanup gate stayed held.`,required:"Retry the same operation; this writer's change was not committed. Inspect pm gc --scope locks --dry-run if the stale-cleanup gate stays held."};const holder=current===null?"the lock no longer exists":`it is now held by ${current.owner} since ${current.created_at}`;return{summary:`Lock for ${id} was lost before commit: ${leaseLabel} is no longer owned by this writer, and ${holder}.`,required:"Re-read the item and apply the mutation again; this writer's change was not committed and the newer value was kept. Keep mutation work shorter than locks.ttl_seconds, or raise locks.ttl_seconds for long-running callbacks."}}function buildLockLostError(id,lease,current,reason){const{summary,required}=describeLockLoss(id,lease,current,reason);return new PmCliError(`${summary} Nothing was written.`,EXIT_CODE.CONFLICT,{code:"lock_lost",item_id:id,reason,lease:describeLease(lease),current_owner:current===null?null:{owner:current.owner,pid:current.pid,created_at:current.created_at,ttl_seconds:current.ttl_seconds},required,recovery:{retry_after_ms:LOCK_CONFLICT_RETRY_HINT_MS}})}async function waitForStaleCleanupGate(lockPath,id){const startedAtMs=performance.now();let backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;for(;;){const releaseCleanupGate=await acquireStaleCleanupGate(lockPath,id);if(releaseCleanupGate!==null)return releaseCleanupGate;const elapsedMs=performance.now()-startedAtMs;if(elapsedMs>=LOCK_RELEASE_GATE_WAIT_MS)return null;await sleepWithJitter(Math.min(backoffMs,LOCK_RELEASE_GATE_WAIT_MS-elapsedMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}async function fenceOwnedLock(lockPath,id,lease,commit){const releaseCleanupGate=await waitForStaleCleanupGate(lockPath,id);if(releaseCleanupGate===null){const current=await readLockInfo(lockPath,!1);throw buildLockLostError(id,lease,current.info,"fence_gate_unavailable")}try{const current=await readLockInfo(lockPath,!1);if(!isOwnedLock(current.info,id,lease.owner,lease.token))throw buildLockLostError(id,lease,current.info,current.info===null?"lease_released":"lease_taken_over");return await commit()}finally{await releaseCleanupGate()}}function resolveLockOverrideRelease(result){if(typeof result=="function")return result;if(typeof result=="object"&&result!==null&&"release"in result){const release=result.release;return typeof release=="function"?release:null}return null}function throwIfStaleLockNeedsForce(id,lockInfo,force,forceRequired){if(!force&&forceRequired){const warningSuffix=lockInfo.warnings.length>0?` (${lockInfo.warnings.join(",")})`:"";throw new PmCliError(`Item ${id} lock is stale${warningSuffix}; rerun with --force when supported for this command`,EXIT_CODE.CONFLICT)}}function parseStaleCleanupGateOwner(raw){let parsed;try{parsed=JSON.parse(raw)}catch{return null}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return null;const candidate=parsed,pid=candidate.pid,token=candidate.token,processStartIdentity=candidate.process_start_identity;return typeof pid!="number"||!Number.isInteger(pid)||pid<=0||typeof token!="string"||token.length===0||processStartIdentity!==void 0&&(typeof processStartIdentity!="string"||!/^\d+$/u.test(processStartIdentity))?null:{pid,token,host_identity:candidate.host_identity,...typeof processStartIdentity=="string"?{process_start_identity:processStartIdentity}:{}}}async function readStaleCleanupGateOwner(gatePath){try{return parseStaleCleanupGateOwner(await fs.readFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),"utf8"))}catch(error){return isFileMissingError(error)?null:void 0}}async function removeStaleCleanupGateDirectory(gatePath){try{return await fs.rm(gatePath,{recursive:!0,force:!0}),!0}catch{return!1}}async function releaseStaleCleanupGate(gatePath,token){let owner=await readStaleCleanupGateOwner(gatePath);for(let attempt=0;owner===void 0&&attempt<3;attempt+=1)await sleepWithJitter(LOCK_WAIT_INITIAL_DELAY_MS),owner=await readStaleCleanupGateOwner(gatePath);!owner||owner.token!==token||await removeStaleCleanupGateDirectory(gatePath)}async function tryCreateStaleCleanupGate(gatePath){try{await fs.mkdir(gatePath)}catch(error){return isErrno(error,"EEXIST"),null}const owner={...await captureProcessIdentity(),host_identity:(await captureLockOwnerIdentity()).host_identity,token:randomUUID()};try{await fs.writeFile(path.join(gatePath,STALE_CLEANUP_GATE_OWNER_FILE),`${JSON.stringify(owner)}
3
3
  `,"utf8")}catch{return await removeStaleCleanupGateDirectory(gatePath),null}return owner}async function retireCleanupGateGeneration(gatePath,token){const recoveries=path.join(path.dirname(gatePath),"recoveries"),generation=createHash("sha256").update(token).digest("hex");try{return await fs.mkdir(recoveries,{recursive:!0}),(await readStaleCleanupGateOwner(gatePath))?.token!==token?!1:(await fs.rename(gatePath,path.join(recoveries,`gate-${generation}`)),!0)}catch{return!1}}async function removeExpiredStaleCleanupGate(gatePath){let gateAgeMs;try{const stats=await fs.stat(gatePath);gateAgeMs=Date.now()-stats.mtimeMs}catch(error){return isFileMissingError(error)}const owner=await readStaleCleanupGateOwner(gatePath);if(owner===void 0)return!1;if(owner!==null){if(await inspectLockOwner(owner)==="dead")return retireCleanupGateGeneration(gatePath,owner.token);if(owner.host_identity!==void 0)return!1}return gateAgeMs<=STALE_CLEANUP_GATE_STALE_MS||owner!==null&&await isProcessIdentityAlive(owner,gateAgeMs,Number.POSITIVE_INFINITY)?!1:owner!==null?retireCleanupGateGeneration(gatePath,owner.token):removeStaleCleanupGateDirectory(gatePath)}async function acquireStaleCleanupGate(lockPath,_id){const gatePath=`${lockPath}${STALE_CLEANUP_GATE_SUFFIX}`,owner=await tryCreateStaleCleanupGate(gatePath);if(owner!==null)return async()=>releaseStaleCleanupGate(gatePath,owner.token);if(!await removeExpiredStaleCleanupGate(gatePath))return null;const recoveredOwner=await tryCreateStaleCleanupGate(gatePath);return recoveredOwner===null?null:async()=>releaseStaleCleanupGate(gatePath,recoveredOwner.token)}async function recordDeadLockRecovery(lockPath,raw){const directory=path.join(path.dirname(lockPath),"recoveries");await fs.mkdir(directory,{recursive:!0}),await fs.writeFile(path.join(directory,`${randomUUID()}.json`),`${JSON.stringify({event:"owner_dead_recovery_attempt",recorded_at:nowIso(),lock:JSON.parse(raw)})}
4
- `,{encoding:"utf8",flag:"wx",mode:384})}async function removeConfirmedStaleLock(params){const releaseCleanupGate=await acquireStaleCleanupGate(params.lockPath,params.id);if(releaseCleanupGate===null)return{lockInfo:params.fallbackLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};try{const currentLockInfo=await readLockInfo(params.lockPath);if(currentLockInfo.info===null&&currentLockInfo.warnings.length===0)return{lockInfo:currentLockInfo,shouldRetryCreate:!0,staleRemovalCounted:!1};const ownerDead=await isConfirmedDeadLock(currentLockInfo.info);if(!isStaleLock(currentLockInfo.info,params.ttlSeconds)&&!ownerDead)return{lockInfo:currentLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};if(throwIfStaleLockNeedsForce(params.id,currentLockInfo,params.force||ownerDead,params.forceRequiredForStaleLock),params.staleRemovals>=MAX_STALE_LOCK_REMOVALS)throw await buildLockConflictError(params.id,currentLockInfo.info,performance.now()-params.startedAtMs);return ownerDead&&await recordDeadLockRecovery(params.lockPath,JSON.stringify(currentLockInfo.info)),{lockInfo:currentLockInfo,shouldRetryCreate:await unlinkLockWithHook(params.lockPath,"lock:stale_remove"),staleRemovalCounted:!0}}finally{await releaseCleanupGate()}}const _testOnly={parseLockInfo,readLockInfo,isErrno,isLockCreateContention,buildLockPayload,isStaleLock,lockOwnerSuffix,resolveLockWaitMs,lockGenerationKey,parseLinuxProcessStartIdentity,readProcessStartIdentity,acquireStaleCleanupGate,removeConfirmedStaleLock};async function acquireLock(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){return(await acquireLockLease(pmRoot,id,ttlSeconds,owner,force,forceRequiredForStaleLock,waitMs,options)).release}async function acquireLockLease(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){const maxWaitMs=parseNonNegativeIntegerWaitMs(options.maxWaitMs),lockOverride=await runActiveServiceOverride("lock_acquire",{pm_root:pmRoot,id,ttl_seconds:ttlSeconds,owner,force,force_required_for_stale_lock:forceRequiredForStaleLock,...maxWaitMs===void 0?{}:{max_wait_ms:maxWaitMs}});if(lockOverride.handled){const release=resolveLockOverrideRelease(lockOverride.result);if(release)return{release:async()=>{await Promise.resolve(release()),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>commit()}}const lockPath=getLockPath(pmRoot,id),waitBudgetMs=cappedLockWaitMs(waitMs,maxWaitMs),totalCeilingMs=resolveLockWaitCeilingMs(waitBudgetMs,maxWaitMs,options.waitPolicy),waitTracker=createLockWaitTracker(waitBudgetMs,totalCeilingMs),startedAtMs=waitTracker.startedAtMs;let staleRemovals=0,backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;const token=randomUUID();for(;;)try{const acquiredAt=await createLockFile(lockPath,id,owner,ttlSeconds,token,options.createParentDirectories!==!1),lease={owner,token,acquiredAt,ttlSeconds};return{release:async()=>{await releaseOwnedLock(lockPath,id,owner,token),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>fenceOwnedLock(lockPath,id,lease,commit)}}catch(error){if(!await isLockCreateContention(error,lockPath))throw new PmCliError(`Failed to acquire lock for ${id}: ${toErrorMessage(error)}`,EXIT_CODE.GENERIC_FAILURE);let lockInfo=await readLockInfo(lockPath);if(await isReclaimableLock(lockInfo.info,ttlSeconds)){const staleRemoval=await removeConfirmedStaleLock({lockPath,id,ttlSeconds,force,forceRequiredForStaleLock,staleRemovals,waitBudgetMs,startedAtMs,fallbackLockInfo:lockInfo});if(lockInfo=staleRemoval.lockInfo,staleRemoval.staleRemovalCounted&&(staleRemovals+=1),staleRemoval.shouldRetryCreate)continue}const observation=waitTracker.observe(lockInfo.info);if(observation.exhausted)throw await buildLockConflictError(id,lockInfo.info,waitBudgetMs===0?0:observation.elapsedMs,observation.ownerHandoffs);await sleepWithJitter(Math.min(backoffMs,observation.remainingMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}export{_testOnly,acquireLock,acquireLockLease,acquireStaleCleanupGate,captureProcessIdentity2 as captureProcessIdentity,hasUnexpiredLockOwner,isProcessIdentityAlive2 as isProcessIdentityAlive,readProcessStartIdentity2 as readProcessStartIdentity,recordDeadLockRecovery,waitForStaleCleanupGate};
4
+ `,{encoding:"utf8",flag:"wx",mode:384})}async function removeConfirmedStaleLock(params){const releaseCleanupGate=await acquireStaleCleanupGate(params.lockPath,params.id);if(releaseCleanupGate===null)return{lockInfo:params.fallbackLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};try{const currentLockInfo=await readLockInfo(params.lockPath);if(currentLockInfo.info===null&&currentLockInfo.warnings.length===0)return{lockInfo:currentLockInfo,shouldRetryCreate:!0,staleRemovalCounted:!1};const ownerDead=await isConfirmedDeadLock(currentLockInfo.info);if(!isStaleLock(currentLockInfo.info,params.ttlSeconds)&&!ownerDead)return{lockInfo:currentLockInfo,shouldRetryCreate:!1,staleRemovalCounted:!1};if(throwIfStaleLockNeedsForce(params.id,currentLockInfo,params.force||ownerDead,params.forceRequiredForStaleLock),params.staleRemovals>=MAX_STALE_LOCK_REMOVALS)throw await buildLockConflictError(params.id,currentLockInfo.info,performance.now()-params.startedAtMs);return ownerDead&&await recordDeadLockRecovery(params.lockPath,JSON.stringify(currentLockInfo.info)),{lockInfo:currentLockInfo,shouldRetryCreate:await unlinkLockWithHook(params.lockPath,"lock:stale_remove"),staleRemovalCounted:!0}}finally{await releaseCleanupGate()}}async function assertLockCreateContention(error,pmRoot,lockPath,id){if(error instanceof PmCliError&&error.code===LOCK_PATH_UNSAFE_CODE)throw error;if(!await isLockCreateContention(error,lockPath))throw new PmCliError(`Failed to acquire lock for ${id}: ${toErrorMessage(error)}`,EXIT_CODE.GENERIC_FAILURE);await assertLockEntryNotSymlink(pmRoot,lockPath,id)}const _testOnly={parseLockInfo,readLockInfo,isErrno,isLockCreateContention,buildLockPayload,isStaleLock,lockOwnerSuffix,resolveLockWaitMs,lockGenerationKey,parseLinuxProcessStartIdentity,readProcessStartIdentity,acquireStaleCleanupGate,removeConfirmedStaleLock};async function acquireLock(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){return(await acquireLockLease(pmRoot,id,ttlSeconds,owner,force,forceRequiredForStaleLock,waitMs,options)).release}async function acquireLockLease(pmRoot,id,ttlSeconds,owner,force=!1,forceRequiredForStaleLock=!0,waitMs,options={}){const maxWaitMs=parseNonNegativeIntegerWaitMs(options.maxWaitMs),lockOverride=await runActiveServiceOverride("lock_acquire",{pm_root:pmRoot,id,ttl_seconds:ttlSeconds,owner,force,force_required_for_stale_lock:forceRequiredForStaleLock,...maxWaitMs===void 0?{}:{max_wait_ms:maxWaitMs}});if(lockOverride.handled){const release=resolveLockOverrideRelease(lockOverride.result);if(release)return{release:async()=>{await Promise.resolve(release()),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>commit()}}const lockPath=getLockPath(pmRoot,id),waitBudgetMs=cappedLockWaitMs(waitMs,maxWaitMs),totalCeilingMs=resolveLockWaitCeilingMs(waitBudgetMs,maxWaitMs,options.waitPolicy),waitTracker=createLockWaitTracker(waitBudgetMs,totalCeilingMs),startedAtMs=waitTracker.startedAtMs;let staleRemovals=0,backoffMs=LOCK_WAIT_INITIAL_DELAY_MS;const token=randomUUID();for(;;)try{const acquiredAt=await createLockFile(pmRoot,lockPath,id,owner,ttlSeconds,token,options.createParentDirectories!==!1),lease={owner,token,acquiredAt,ttlSeconds};return{release:async()=>{await releaseOwnedLock(lockPath,id,owner,token),await runActiveServiceOverride("lock_release",{pm_root:pmRoot,id,owner})},fence:commit=>fenceOwnedLock(lockPath,id,lease,commit)}}catch(error){await assertLockCreateContention(error,pmRoot,lockPath,id);let lockInfo=await readLockInfo(lockPath);if(await isReclaimableLock(lockInfo.info,ttlSeconds)){const staleRemoval=await removeConfirmedStaleLock({lockPath,id,ttlSeconds,force,forceRequiredForStaleLock,staleRemovals,waitBudgetMs,startedAtMs,fallbackLockInfo:lockInfo});if(lockInfo=staleRemoval.lockInfo,staleRemoval.staleRemovalCounted&&(staleRemovals+=1),staleRemoval.shouldRetryCreate)continue}const observation=waitTracker.observe(lockInfo.info);if(observation.exhausted)throw await buildLockConflictError(id,lockInfo.info,waitBudgetMs===0?0:observation.elapsedMs,observation.ownerHandoffs);await sleepWithJitter(Math.min(backoffMs,observation.remainingMs)),backoffMs=Math.min(backoffMs*2,LOCK_WAIT_MAX_DELAY_MS)}}export{_testOnly,acquireLock,acquireLockLease,acquireStaleCleanupGate,captureProcessIdentity2 as captureProcessIdentity,hasUnexpiredLockOwner,isProcessIdentityAlive2 as isProcessIdentityAlive,readProcessStartIdentity2 as readProcessStartIdentity,recordDeadLockRecovery,waitForStaleCleanupGate};
5
5
  //# sourceMappingURL=lock.js.map
@@ -1,4 +1,34 @@
1
1
  import { type RuntimeFieldDefinitionResolved,type RuntimeFieldRegistry } from "./runtime-schema.js";
2
+ import type { RuntimeFieldCommand } from "../../types/index.js";
3
+ /**
4
+ * The option name an SDK caller passes to set exactly this runtime field: the
5
+ * first name {@link readRuntimeFieldOptionValue} reads for it (preferring the
6
+ * camelCase of its CLI flag, as Commander derives it) that no other declared
7
+ * field also reads. Undefined when every name is shared, because following
8
+ * such a hint would also set the other field.
9
+ */
10
+ export declare function runtimeFieldSdkOptionName(definition:RuntimeFieldDefinitionResolved,registry:Pick<RuntimeFieldRegistry,"definitions">,reservedOptions?:ReadonlySet<string>,command?:RuntimeFieldCommand):string | undefined;
11
+ /** The command-line token for a declared spelling: a short alias keeps its single dash. */
12
+ export declare function runtimeFieldFlagToken(flag:string):string;
13
+ /**
14
+ * A declared spelling that sets exactly this runtime field: the declared flag
15
+ * or an alias that is not a host flag, is not a negation (its value would
16
+ * land under another key that this field never reads), whose parsed option
17
+ * name no other active field and no host option reads, and which no
18
+ * earlier-read key of this field can shadow. Undefined when no spelling
19
+ * qualifies. Render it with {@link runtimeFieldFlagToken}.
20
+ */
21
+ export declare function runtimeFieldCliFlagRoute(definition:RuntimeFieldDefinitionResolved,registry:Pick<RuntimeFieldRegistry,"definitions">,reserved:{
22
+ options:ReadonlySet<string>;
23
+ flags:ReadonlySet<string>;
24
+ },command?:RuntimeFieldCommand):string | undefined;
25
+ /**
26
+ * The option key Commander stores for a long flag (without dashes): it strips
27
+ * a negating `no-` prefix and camel-cases across hyphens only, so
28
+ * `--review-url` parses to `reviewUrl`, `--no-title` to `title`, and
29
+ * `--review.url` keeps the literal key `review.url`.
30
+ */
31
+ export declare function commanderOptionAttribute(flag:string):string;
2
32
  /** Implements read runtime field option value for the public runtime surface of this module. */
3
33
  export declare function readRuntimeFieldOptionValue(options:Record<string,unknown>,definition:RuntimeFieldDefinitionResolved):unknown;
4
34
  /** Validate a declared field; option arrays represent repeated flags, while metadata containers retain their exact shape. */
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4d5692ce-ec33-5eb4-bd9b-7497ffe25b0e")}catch{}})();import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{stableStringify}from"../shared/serialization.js";import{isRfc3339DateTime}from"../shared/time.js";import{runtimeFieldOptionTarget}from"./runtime-schema.js";const MAX_VALUE_SCHEMA_DEPTH=16;function toCamelToken(value){const segments=value.trim().replaceAll(/[^A-Za-z0-9]+/g," ").split(/\s+/).filter(segment=>segment.length>0).map(segment=>segment.toLowerCase());if(segments.length===0)return value;const[first,...rest]=segments;return`${first}${rest.map(segment=>`${segment.slice(0,1).toUpperCase()}${segment.slice(1)}`).join("")}`}function resolveCandidateOptionKeys(definition){return[...new Set([runtimeFieldOptionTarget(definition),toCamelToken(definition.key),toCamelToken(definition.cli_flag),...definition.cli_aliases.map(alias=>toCamelToken(alias))])]}function readRuntimeFieldOptionValue(options,definition){for(const candidateKey of resolveCandidateOptionKeys(definition)){if(!Object.hasOwn(options,candidateKey))continue;const value=options[candidateKey];if(value!==void 0)return value}}function normalizeStringArrayValue(value){return Array.isArray(value)?value.flatMap(entry=>normalizeStringArrayValue(entry)).map(entry=>entry.trim()).filter(entry=>entry.length>0):typeof value=="string"?value.split(/[,\n|]/).map(entry=>entry.trim()).filter(entry=>entry.length>0):value==null?[]:[String(value).trim()].filter(entry=>entry.length>0)}function parseBooleanValue(raw,label){if(typeof raw=="boolean")return raw;if(typeof raw=="number"){if(raw===1)return!0;if(raw===0)return!1}if(typeof raw=="string"){const normalized=raw.trim().toLowerCase();if(normalized==="true"||normalized==="1"||normalized==="yes")return!0;if(normalized==="false"||normalized==="0"||normalized==="no")return!1}throw new PmCliError(`${label} must be one of true|false|1|0|yes|no`,EXIT_CODE.USAGE)}function parseNumberValue(raw,label){if(typeof raw=="number"&&Number.isFinite(raw))return raw;const candidate=typeof raw=="string"?raw.trim():String(raw),parsed=Number(candidate);if(!Number.isFinite(parsed))throw new PmCliError(`${label} must be a number`,EXIT_CODE.USAGE);return parsed}function parseJsonContainerValue(raw,label,expectedType){const parsed=typeof raw=="string"?(()=>{try{return JSON.parse(raw)}catch{return}})():raw;if(expectedType==="array"&&Array.isArray(parsed)||expectedType==="object"&&typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed))return parsed;throw new PmCliError(`${label} must be valid JSON ${expectedType}`,EXIT_CODE.USAGE)}function valueMatchesSchemaType(value,type){return type===void 0?!0:type==="array"?Array.isArray(value):type==="object"?typeof value=="object"&&value!==null&&!Array.isArray(value):typeof value===type}function runtimeFieldPrimitiveSchemaError(value,schema,path){if(schema.const!==void 0&&stableStringify(value)!==stableStringify(schema.const))return`${path} must equal ${stableStringify(schema.const)}`;if(schema.enum!==void 0&&!schema.enum.some(candidate=>stableStringify(candidate)===stableStringify(value)))return`${path} must be one of ${schema.enum.map(entry=>stableStringify(entry)).join("|")}`;if(!valueMatchesSchemaType(value,schema.type))return`${path} must match configured type ${String(schema.type)}`;if(typeof value=="string"){if(schema.min_length!==void 0&&value.length<schema.min_length)return`${path} must contain at least ${schema.min_length} characters`;if(schema.format==="date-time"&&!isRfc3339DateTime(value))return`${path} must be an RFC 3339 date-time`}}function runtimeFieldArraySchemaError(value,schema,path,depth){if(Array.isArray(value)){if(schema.min_items!==void 0&&value.length<schema.min_items)return`${path} must contain at least ${schema.min_items} items`;if(schema.items)for(const[index,entry]of value.entries()){const error=runtimeFieldSchemaError(entry,schema.items,`${path}[${index}]`,depth+1);if(error)return error}}}function runtimeFieldObjectSchemaError(value,schema,path,depth){if(typeof value!="object"||value===null||Array.isArray(value))return;const record=value;for(const key of schema.required??[])if(!Object.hasOwn(record,key))return`${path}.${key} is required`;for(const[key,entry]of Object.entries(record)){const propertySchema=schema.properties?.[key];if(!propertySchema){if(schema.additional_properties===!1)return`${path}.${key} is not allowed`;continue}const error=runtimeFieldSchemaError(entry,propertySchema,`${path}.${key}`,depth+1);if(error)return error}}function runtimeFieldSchemaError(value,schema,path,depth=0){if(depth>MAX_VALUE_SCHEMA_DEPTH)return`${path} value schema exceeds maximum depth`;const directError=runtimeFieldPrimitiveSchemaError(value,schema,path);if(directError)return directError;const arrayError=runtimeFieldArraySchemaError(value,schema,path,depth);if(arrayError)return arrayError;const objectError=runtimeFieldObjectSchemaError(value,schema,path,depth);if(objectError)return objectError;if(!schema.one_of)return;let matches=0;for(const candidate of schema.one_of)if(runtimeFieldSchemaError(value,candidate,path,depth+1)===void 0&&(matches+=1,matches>1))break;return matches===1?void 0:`${path} must match exactly one configured schema variant`}function validateRuntimeFieldValue(definition,value,label){if(!definition.value_schema||value===void 0)return value;const error=runtimeFieldSchemaError(value,definition.value_schema,label);if(error)throw new PmCliError(error,EXIT_CODE.USAGE);return value}function coerceRuntimeContainerValue(definition,rawValue,label,inputKind){const raw=inputKind==="option"&&Array.isArray(rawValue)?rawValue[rawValue.length-1]:rawValue;if(raw!==void 0)return validateRuntimeFieldValue(definition,parseJsonContainerValue(raw,label,definition.type),label)}function coerceRuntimeFieldValue(definition,rawValue,labelOverride,inputKind="option"){const label=labelOverride??`--${definition.cli_flag}`;if(definition.type==="array"||definition.type==="object")return coerceRuntimeContainerValue(definition,rawValue,label,inputKind);if(definition.repeatable||definition.type==="string_array"){const values=normalizeStringArrayValue(rawValue);return definition.type==="number"?validateRuntimeFieldValue(definition,values.map(value=>parseNumberValue(value,label)),label):definition.type==="boolean"?validateRuntimeFieldValue(definition,values.map(value=>parseBooleanValue(value,label)),label):validateRuntimeFieldValue(definition,values,label)}const scalarRaw=Array.isArray(rawValue)?rawValue[rawValue.length-1]:rawValue;if(scalarRaw!==void 0)return definition.type==="number"?validateRuntimeFieldValue(definition,parseNumberValue(scalarRaw,label),label):definition.type==="boolean"?validateRuntimeFieldValue(definition,parseBooleanValue(scalarRaw,label),label):validateRuntimeFieldValue(definition,typeof scalarRaw=="string"?scalarRaw:String(scalarRaw),label)}function shouldRequireFieldOnCreate(definition,itemTypeName){return!definition.required&&!definition.required_on_create?!1:definition.required_types.length===0?!0:itemTypeName?definition.required_types.map(value=>value.toLowerCase()).includes(itemTypeName.trim().toLowerCase()):!1}function collectRuntimeCreateFieldValues(options,fieldRegistry,itemTypeName){const values={},missingRequiredFlags=[];for(const definition of fieldRegistry.command_to_fields.get("create")??[]){const rawValue=readRuntimeFieldOptionValue(options,definition);if(rawValue===void 0){shouldRequireFieldOnCreate(definition,itemTypeName)&&missingRequiredFlags.push(`--${definition.cli_flag}`);continue}values[definition.metadata_key]=coerceRuntimeFieldValue(definition,rawValue)}return{values,missing_required_flags:[...new Set(missingRequiredFlags)].sort((left,right)=>left.localeCompare(right))}}function collectRuntimeUpdateFieldValues(options,fieldRegistry,commands=["update"]){const values={},definitions=(commands??["update"]).flatMap(command=>fieldRegistry.command_to_fields.get(command)??[]),seen=new Set;for(const definition of definitions){const rawValue=readRuntimeFieldOptionValue(options,definition);rawValue!==void 0&&(seen.has(definition.metadata_key)||(seen.add(definition.metadata_key),values[definition.metadata_key]=coerceRuntimeFieldValue(definition,rawValue)))}return values}export{coerceRuntimeFieldValue,collectRuntimeCreateFieldValues,collectRuntimeUpdateFieldValues,readRuntimeFieldOptionValue};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6635e694-c788-5805-adb8-ed93cc931946")}catch{}})();import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{stableStringify}from"../shared/serialization.js";import{isRfc3339DateTime}from"../shared/time.js";import{runtimeFieldOptionTarget}from"./runtime-schema.js";const MAX_VALUE_SCHEMA_DEPTH=16;function toCamelToken(value){const segments=value.trim().replaceAll(/[^A-Za-z0-9]+/g," ").split(/\s+/).filter(segment=>segment.length>0).map(segment=>segment.toLowerCase());if(segments.length===0)return value;const[first,...rest]=segments;return`${first}${rest.map(segment=>`${segment.slice(0,1).toUpperCase()}${segment.slice(1)}`).join("")}`}function runtimeFieldSdkOptionName(definition,registry,reservedOptions=new Set,command){if(hasStorageTwin(definition,registry,command))return;const taken=foreignRuntimeFieldOptionNames(definition,registry,reservedOptions,command),readOrder=resolveCandidateOptionKeys(definition);return[toCamelToken(definition.cli_flag),...readOrder].find(name=>!taken.has(name)&&isUnshadowedOptionKey(name,readOrder,taken))}function isUnshadowedOptionKey(key,readOrder,taken){for(const earlier of readOrder){if(earlier===key)break;if(taken.has(earlier))return!1}return!0}function hasStorageTwin(definition,registry,command){return registry.definitions.some(candidate=>candidate!==definition&&candidate.metadata_key===definition.metadata_key&&(command===void 0||candidate.commands.includes(command)))}function foreignRuntimeFieldOptionNames(definition,registry,reservedOptions,command){return new Set([...reservedOptions,...registry.definitions.filter(candidate=>candidate!==definition&&(command===void 0||candidate.commands.includes(command))).flatMap(candidate=>resolveCandidateOptionKeys(candidate))])}function isNegatingFlag(flag){return flag.replace(/^-+/u,"").startsWith("no-")}function runtimeFieldFlagToken(flag){return flag.startsWith("-")?flag:`--${flag}`}function runtimeFieldCliFlagRoute(definition,registry,reserved,command){if(hasStorageTwin(definition,registry,command))return;const taken=foreignRuntimeFieldOptionNames(definition,registry,reserved.options,command),readOrder=resolveCandidateOptionKeys(definition);return[definition.cli_flag,...definition.cli_aliases].find(flag=>{const key=commanderOptionAttribute(flag);return isNegatingFlag(flag)||reserved.flags.has(runtimeFieldFlagToken(flag))||taken.has(key)?!1:isUnshadowedOptionKey(key,readOrder,taken)})}function commanderOptionAttribute(flag){return flag.replace(/^-+/u,"").replace(/^no-/u,"").split("-").reduce((name,word)=>`${name}${word.charAt(0).toUpperCase()}${word.slice(1)}`)}function resolveCandidateOptionKeys(definition){return[...new Set([runtimeFieldOptionTarget(definition),toCamelToken(definition.key),toCamelToken(definition.cli_flag),...definition.cli_aliases.map(alias=>toCamelToken(alias)),...[definition.cli_flag,...definition.cli_aliases].filter(flag=>!isNegatingFlag(flag)).map(flag=>commanderOptionAttribute(flag))])]}function readRuntimeFieldOptionValue(options,definition){for(const candidateKey of resolveCandidateOptionKeys(definition)){if(!Object.hasOwn(options,candidateKey))continue;const value=options[candidateKey];if(value!==void 0)return value}}function normalizeStringArrayValue(value){return Array.isArray(value)?value.flatMap(entry=>normalizeStringArrayValue(entry)).map(entry=>entry.trim()).filter(entry=>entry.length>0):typeof value=="string"?value.split(/[,\n|]/).map(entry=>entry.trim()).filter(entry=>entry.length>0):value==null?[]:[String(value).trim()].filter(entry=>entry.length>0)}function parseBooleanValue(raw,label){if(typeof raw=="boolean")return raw;if(typeof raw=="number"){if(raw===1)return!0;if(raw===0)return!1}if(typeof raw=="string"){const normalized=raw.trim().toLowerCase();if(normalized==="true"||normalized==="1"||normalized==="yes")return!0;if(normalized==="false"||normalized==="0"||normalized==="no")return!1}throw new PmCliError(`${label} must be one of true|false|1|0|yes|no`,EXIT_CODE.USAGE)}function parseNumberValue(raw,label){if(typeof raw=="number"&&Number.isFinite(raw))return raw;const candidate=typeof raw=="string"?raw.trim():String(raw),parsed=Number(candidate);if(!Number.isFinite(parsed))throw new PmCliError(`${label} must be a number`,EXIT_CODE.USAGE);return parsed}function parseJsonContainerValue(raw,label,expectedType){const parsed=typeof raw=="string"?(()=>{try{return JSON.parse(raw)}catch{return}})():raw;if(expectedType==="array"&&Array.isArray(parsed)||expectedType==="object"&&typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed))return parsed;throw new PmCliError(`${label} must be valid JSON ${expectedType}`,EXIT_CODE.USAGE)}function valueMatchesSchemaType(value,type){return type===void 0?!0:type==="array"?Array.isArray(value):type==="object"?typeof value=="object"&&value!==null&&!Array.isArray(value):typeof value===type}function runtimeFieldPrimitiveSchemaError(value,schema,path){if(schema.const!==void 0&&stableStringify(value)!==stableStringify(schema.const))return`${path} must equal ${stableStringify(schema.const)}`;if(schema.enum!==void 0&&!schema.enum.some(candidate=>stableStringify(candidate)===stableStringify(value)))return`${path} must be one of ${schema.enum.map(entry=>stableStringify(entry)).join("|")}`;if(!valueMatchesSchemaType(value,schema.type))return`${path} must match configured type ${String(schema.type)}`;if(typeof value=="string"){if(schema.min_length!==void 0&&value.length<schema.min_length)return`${path} must contain at least ${schema.min_length} characters`;if(schema.format==="date-time"&&!isRfc3339DateTime(value))return`${path} must be an RFC 3339 date-time`}}function runtimeFieldArraySchemaError(value,schema,path,depth){if(Array.isArray(value)){if(schema.min_items!==void 0&&value.length<schema.min_items)return`${path} must contain at least ${schema.min_items} items`;if(schema.items)for(const[index,entry]of value.entries()){const error=runtimeFieldSchemaError(entry,schema.items,`${path}[${index}]`,depth+1);if(error)return error}}}function runtimeFieldObjectSchemaError(value,schema,path,depth){if(typeof value!="object"||value===null||Array.isArray(value))return;const record=value;for(const key of schema.required??[])if(!Object.hasOwn(record,key))return`${path}.${key} is required`;for(const[key,entry]of Object.entries(record)){const propertySchema=schema.properties?.[key];if(!propertySchema){if(schema.additional_properties===!1)return`${path}.${key} is not allowed`;continue}const error=runtimeFieldSchemaError(entry,propertySchema,`${path}.${key}`,depth+1);if(error)return error}}function runtimeFieldSchemaError(value,schema,path,depth=0){if(depth>MAX_VALUE_SCHEMA_DEPTH)return`${path} value schema exceeds maximum depth`;const directError=runtimeFieldPrimitiveSchemaError(value,schema,path);if(directError)return directError;const arrayError=runtimeFieldArraySchemaError(value,schema,path,depth);if(arrayError)return arrayError;const objectError=runtimeFieldObjectSchemaError(value,schema,path,depth);if(objectError)return objectError;if(!schema.one_of)return;let matches=0;for(const candidate of schema.one_of)if(runtimeFieldSchemaError(value,candidate,path,depth+1)===void 0&&(matches+=1,matches>1))break;return matches===1?void 0:`${path} must match exactly one configured schema variant`}function validateRuntimeFieldValue(definition,value,label){if(!definition.value_schema||value===void 0)return value;const error=runtimeFieldSchemaError(value,definition.value_schema,label);if(error)throw new PmCliError(error,EXIT_CODE.USAGE);return value}function coerceRuntimeContainerValue(definition,rawValue,label,inputKind){const raw=inputKind==="option"&&Array.isArray(rawValue)?rawValue[rawValue.length-1]:rawValue;if(raw!==void 0)return validateRuntimeFieldValue(definition,parseJsonContainerValue(raw,label,definition.type),label)}function coerceRuntimeFieldValue(definition,rawValue,labelOverride,inputKind="option"){const label=labelOverride??`--${definition.cli_flag}`;if(definition.type==="array"||definition.type==="object")return coerceRuntimeContainerValue(definition,rawValue,label,inputKind);if(definition.repeatable||definition.type==="string_array"){const values=normalizeStringArrayValue(rawValue);return definition.type==="number"?validateRuntimeFieldValue(definition,values.map(value=>parseNumberValue(value,label)),label):definition.type==="boolean"?validateRuntimeFieldValue(definition,values.map(value=>parseBooleanValue(value,label)),label):validateRuntimeFieldValue(definition,values,label)}const scalarRaw=Array.isArray(rawValue)?rawValue[rawValue.length-1]:rawValue;if(scalarRaw!==void 0)return definition.type==="number"?validateRuntimeFieldValue(definition,parseNumberValue(scalarRaw,label),label):definition.type==="boolean"?validateRuntimeFieldValue(definition,parseBooleanValue(scalarRaw,label),label):validateRuntimeFieldValue(definition,typeof scalarRaw=="string"?scalarRaw:String(scalarRaw),label)}function shouldRequireFieldOnCreate(definition,itemTypeName){return!definition.required&&!definition.required_on_create?!1:definition.required_types.length===0?!0:itemTypeName?definition.required_types.map(value=>value.toLowerCase()).includes(itemTypeName.trim().toLowerCase()):!1}function collectRuntimeCreateFieldValues(options,fieldRegistry,itemTypeName){const values={},missingRequiredFlags=[];for(const definition of fieldRegistry.command_to_fields.get("create")??[]){const rawValue=readRuntimeFieldOptionValue(options,definition);if(rawValue===void 0){shouldRequireFieldOnCreate(definition,itemTypeName)&&missingRequiredFlags.push(`--${definition.cli_flag}`);continue}values[definition.metadata_key]=coerceRuntimeFieldValue(definition,rawValue)}return{values,missing_required_flags:[...new Set(missingRequiredFlags)].sort((left,right)=>left.localeCompare(right))}}function collectRuntimeUpdateFieldValues(options,fieldRegistry,commands=["update"]){const values={},definitions=(commands??["update"]).flatMap(command=>fieldRegistry.command_to_fields.get(command)??[]),seen=new Set;for(const definition of definitions){const rawValue=readRuntimeFieldOptionValue(options,definition);rawValue!==void 0&&(seen.has(definition.metadata_key)||(seen.add(definition.metadata_key),values[definition.metadata_key]=coerceRuntimeFieldValue(definition,rawValue)))}return values}export{coerceRuntimeFieldValue,collectRuntimeCreateFieldValues,collectRuntimeUpdateFieldValues,commanderOptionAttribute,readRuntimeFieldOptionValue,runtimeFieldCliFlagRoute,runtimeFieldFlagToken,runtimeFieldSdkOptionName};
2
2
  //# sourceMappingURL=runtime-field-values.js.map
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fc213d81-bf8a-50a2-b1a8-1b2a69721855")}catch{}})();import{getSentry}from"./instrument.js";import{PmCliError}from"../shared/errors.js";import{isPmCliExpectedError}from"../../sdk/errors.js";import{ExtensionCommandError}from"../../sdk/error-runtime.js";import{EXIT_CODE}from"../shared/constants.js";import{deriveTelemetryCommandResolution,deriveTelemetryCommandTaxonomy}from"../telemetry/observability.js";let activeCommandSpan;function setSpanAttribute(span,key,value){if(value===void 0)return;const attributeTarget=span;typeof attributeTarget.setAttribute=="function"&&attributeTarget.setAttribute(key,value)}function setSentryTagIfPresent(Sentry,key,value){typeof value=="string"&&value.trim().length>0&&Sentry.setTag(key,value)}function setSentryCommandFinishTags(Sentry,normalizedOk,metadata){Sentry.setTag("pm.ok",normalizedOk),Sentry.setTag("pm.command_ok",normalizedOk),typeof metadata?.exit_code=="number"&&(Sentry.setTag("pm.exit_code",String(metadata.exit_code)),Sentry.setTag("pm.command_exit_code",String(metadata.exit_code))),setSentryTagIfPresent(Sentry,"pm.error_code",metadata?.error_code),setSentryTagIfPresent(Sentry,"pm.error_category",metadata?.error_category),setSentryTagIfPresent(Sentry,"pm.command_resolution",metadata?.command_resolution),setSentryTagIfPresent(Sentry,"pm.resolution_stage",metadata?.resolution_stage)}function setCommandSpanFinishAttributes(span,normalizedOk,normalizedExitCode,metadata){setSpanAttribute(span,"pm.ok",normalizedOk),setSpanAttribute(span,"pm.command_ok",normalizedOk),setSpanAttribute(span,"pm.exit_code",normalizedExitCode),setSpanAttribute(span,"pm.command_exit_code",normalizedExitCode),setSpanAttribute(span,"pm.error_code",metadata?.error_code),setSpanAttribute(span,"pm.error_category",metadata?.error_category),setSpanAttribute(span,"pm.command_resolution",metadata?.command_resolution),setSpanAttribute(span,"pm.resolution_stage",metadata?.resolution_stage)}function sentrySetCommandContext(command,args,options,metadata){const Sentry=getSentry();if(!Sentry)return;const taxonomy=deriveTelemetryCommandTaxonomy(command);Sentry.setTag("pm.command",command),Sentry.setTag("pm.command_root",taxonomy.command_root),Sentry.setTag("pm.command_family",taxonomy.command_family),setSentryTagIfPresent(Sentry,"pm.version",metadata?.pm_version),setSentryTagIfPresent(Sentry,"pm.source_context_source",metadata?.source_context_source),Sentry.setTag("pm.extension.name",metadata?.extension?.name),Sentry.setTag("pm.extension.version",metadata?.extension?.version),Sentry.setTag("pm.extension.layer",metadata?.extension?.layer),typeof metadata?.source_context=="string"&&metadata.source_context.trim().length>0&&(Sentry.setTag("pm.source_context",metadata.source_context),Sentry.setAttribute("pm.source_context",metadata.source_context));const safeArgs=args.map(()=>"[redacted]");Sentry.setContext("pm.command",{name:command,root:taxonomy.command_root,leaf:taxonomy.command_leaf,family:taxonomy.command_family,args:safeArgs,option_keys:Object.keys(options).sort(),source_context:metadata?.source_context,source_context_source:metadata?.source_context_source}),Sentry.addBreadcrumb({category:"pm.command",message:`pm ${command}`,level:"info",data:{args:safeArgs,command_root:taxonomy.command_root,command_family:taxonomy.command_family,source_context:metadata?.source_context}})}function sentryStartCommandSpan(command){const Sentry=getSentry();if(!Sentry)return;const taxonomy=deriveTelemetryCommandTaxonomy(command);activeCommandSpan=Sentry.startInactiveSpan({op:"pm.command",name:`pm ${command}`,forceTransaction:!0,attributes:{"pm.command":command,"pm.command_root":taxonomy.command_root,"pm.command_family":taxonomy.command_family}})}function sentryDiscardCommandSpan(){activeCommandSpan=void 0}function sentryFinishCommandSpan(ok,error,metadata){if(!activeCommandSpan)return;const normalizedOk=ok?"true":"false",normalizedExitCode=typeof metadata?.exit_code=="number"?String(metadata.exit_code):void 0,Sentry=getSentry();Sentry&&setSentryCommandFinishTags(Sentry,normalizedOk,metadata),setCommandSpanFinishAttributes(activeCommandSpan,normalizedOk,normalizedExitCode,metadata),activeCommandSpan.setStatus(ok?{code:1}:{code:2,message:error??"command_failed"}),activeCommandSpan.end(),activeCommandSpan=void 0}const UNCLASSIFIED_CAPTURED_ERROR_CODE="unclassified_runtime_error";function readCapturedErrorContract(error){const candidate=typeof error=="object"&&error!==null?error:void 0,hasDeclaredExitCode=typeof candidate?.exitCode=="number",exitCode=hasDeclaredExitCode?Math.trunc(candidate?.exitCode):EXIT_CODE.GENERIC_FAILURE;return{error_code:typeof candidate?.code=="string"&&candidate.code.length>0&&hasDeclaredExitCode?candidate?.code:UNCLASSIFIED_CAPTURED_ERROR_CODE,exit_code:exitCode}}function buildCapturedErrorTags(error){const contract=readCapturedErrorContract(error);return{"pm.error_code":contract.error_code,"pm.exit_code":String(contract.exit_code)}}function sentryCaptureCliError(error){if(!shouldCaptureCliError(error))return;const Sentry=getSentry();if(!Sentry)return;const tags=buildCapturedErrorTags(error);if(error instanceof Error){const extras={};"exitCode"in error&&typeof error.exitCode=="number"&&(extras.exit_code=error.exitCode),"context"in error&&typeof error.context=="object"&&(extras.error_context=error.context),Sentry.captureException(error,{extra:extras,tags})}else Sentry.captureException(new Error(String(error)),{tags})}function sentryLogCliUsageError(params){const Sentry=getSentry();if(!Sentry)return;const resolvedCommandResolution=params.command_resolution??deriveTelemetryCommandResolution({ok:!1,errorCode:params.error_code,errorCategory:params.error_category}),resolvedResolutionStage=params.resolution_stage??"unknown",payload={"pm.command":params.command,"pm.error_code":params.error_code,"pm.error_category":params.error_category,"pm.exit_code":params.exit_code,"pm.error_message":params.error_message,"pm.command_resolution":resolvedCommandResolution,"pm.resolution_stage":resolvedResolutionStage,"pm.source_context":params.source_context??""},loggerCandidate=Sentry.logger;if(loggerCandidate&&typeof loggerCandidate.warn=="function"){loggerCandidate.warn("pm_cli_usage_error",payload);return}Sentry.captureMessage(`pm_cli_usage_error:${params.error_code}`,{level:"warning",tags:{"pm.command":params.command,"pm.error_code":params.error_code,"pm.error_category":params.error_category,"pm.exit_code":String(params.exit_code),"pm.command_resolution":resolvedCommandResolution,"pm.resolution_stage":resolvedResolutionStage,"pm.source_context":params.source_context??"unknown"},extra:payload})}function shouldCaptureCliError(error){if(error instanceof ExtensionCommandError)return!0;if(error instanceof PmCliError||isPmCliExpectedError(error)||error instanceof Error&&error.name==="AbortError"&&/(?:ctrl\+[cd]|sigint|user[- ]initiated)/i.test(error.message))return!1;if(typeof error=="object"&&error!==null&&"exitCode"in error&&typeof error.exitCode=="number"){if(error instanceof Error&&error.name==="CommandError")return!1;const exitCode=Math.trunc(error.exitCode);return!new Set([EXIT_CODE.SUCCESS,EXIT_CODE.USAGE,EXIT_CODE.NOT_FOUND,EXIT_CODE.CONFLICT]).has(exitCode)}return!0}async function sentryFlush(timeoutMs=3e3){const Sentry=getSentry();if(Sentry)try{await Sentry.flush(timeoutMs)}catch{}}export{UNCLASSIFIED_CAPTURED_ERROR_CODE,sentryCaptureCliError,sentryDiscardCommandSpan,sentryFinishCommandSpan,sentryFlush,sentryLogCliUsageError,sentrySetCommandContext,sentryStartCommandSpan,shouldCaptureCliError};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="10325dfb-128f-5d83-9163-061d8d8b29b6")}catch{}})();import{getSentry}from"./instrument.js";import{PmCliError}from"../shared/errors.js";import{crashReportErrorContext}from"../shared/error-details.js";import{isPmCliExpectedError}from"../../sdk/errors.js";import{ExtensionCommandError}from"../../sdk/error-runtime.js";import{EXIT_CODE}from"../shared/constants.js";import{deriveTelemetryCommandResolution,deriveTelemetryCommandTaxonomy}from"../telemetry/observability.js";let activeCommandSpan;function setSpanAttribute(span,key,value){if(value===void 0)return;const attributeTarget=span;typeof attributeTarget.setAttribute=="function"&&attributeTarget.setAttribute(key,value)}function setSentryTagIfPresent(Sentry,key,value){typeof value=="string"&&value.trim().length>0&&Sentry.setTag(key,value)}function setSentryCommandFinishTags(Sentry,normalizedOk,metadata){Sentry.setTag("pm.ok",normalizedOk),Sentry.setTag("pm.command_ok",normalizedOk),typeof metadata?.exit_code=="number"&&(Sentry.setTag("pm.exit_code",String(metadata.exit_code)),Sentry.setTag("pm.command_exit_code",String(metadata.exit_code))),setSentryTagIfPresent(Sentry,"pm.error_code",metadata?.error_code),setSentryTagIfPresent(Sentry,"pm.error_category",metadata?.error_category),setSentryTagIfPresent(Sentry,"pm.command_resolution",metadata?.command_resolution),setSentryTagIfPresent(Sentry,"pm.resolution_stage",metadata?.resolution_stage)}function setCommandSpanFinishAttributes(span,normalizedOk,normalizedExitCode,metadata){setSpanAttribute(span,"pm.ok",normalizedOk),setSpanAttribute(span,"pm.command_ok",normalizedOk),setSpanAttribute(span,"pm.exit_code",normalizedExitCode),setSpanAttribute(span,"pm.command_exit_code",normalizedExitCode),setSpanAttribute(span,"pm.error_code",metadata?.error_code),setSpanAttribute(span,"pm.error_category",metadata?.error_category),setSpanAttribute(span,"pm.command_resolution",metadata?.command_resolution),setSpanAttribute(span,"pm.resolution_stage",metadata?.resolution_stage)}function sentrySetCommandContext(command,args,options,metadata){const Sentry=getSentry();if(!Sentry)return;const taxonomy=deriveTelemetryCommandTaxonomy(command);Sentry.setTag("pm.command",command),Sentry.setTag("pm.command_root",taxonomy.command_root),Sentry.setTag("pm.command_family",taxonomy.command_family),setSentryTagIfPresent(Sentry,"pm.version",metadata?.pm_version),setSentryTagIfPresent(Sentry,"pm.source_context_source",metadata?.source_context_source),Sentry.setTag("pm.extension.name",metadata?.extension?.name),Sentry.setTag("pm.extension.version",metadata?.extension?.version),Sentry.setTag("pm.extension.layer",metadata?.extension?.layer),typeof metadata?.source_context=="string"&&metadata.source_context.trim().length>0&&(Sentry.setTag("pm.source_context",metadata.source_context),Sentry.setAttribute("pm.source_context",metadata.source_context));const safeArgs=args.map(()=>"[redacted]");Sentry.setContext("pm.command",{name:command,root:taxonomy.command_root,leaf:taxonomy.command_leaf,family:taxonomy.command_family,args:safeArgs,option_keys:Object.keys(options).sort(),source_context:metadata?.source_context,source_context_source:metadata?.source_context_source}),Sentry.addBreadcrumb({category:"pm.command",message:`pm ${command}`,level:"info",data:{args:safeArgs,command_root:taxonomy.command_root,command_family:taxonomy.command_family,source_context:metadata?.source_context}})}function sentryStartCommandSpan(command){const Sentry=getSentry();if(!Sentry)return;const taxonomy=deriveTelemetryCommandTaxonomy(command);activeCommandSpan=Sentry.startInactiveSpan({op:"pm.command",name:`pm ${command}`,forceTransaction:!0,attributes:{"pm.command":command,"pm.command_root":taxonomy.command_root,"pm.command_family":taxonomy.command_family}})}function sentryDiscardCommandSpan(){activeCommandSpan=void 0}function sentryFinishCommandSpan(ok,error,metadata){if(!activeCommandSpan)return;const normalizedOk=ok?"true":"false",normalizedExitCode=typeof metadata?.exit_code=="number"?String(metadata.exit_code):void 0,Sentry=getSentry();Sentry&&setSentryCommandFinishTags(Sentry,normalizedOk,metadata),setCommandSpanFinishAttributes(activeCommandSpan,normalizedOk,normalizedExitCode,metadata),activeCommandSpan.setStatus(ok?{code:1}:{code:2,message:error??"command_failed"}),activeCommandSpan.end(),activeCommandSpan=void 0}const UNCLASSIFIED_CAPTURED_ERROR_CODE="unclassified_runtime_error";function readCapturedErrorContract(error){const candidate=typeof error=="object"&&error!==null?error:void 0,hasDeclaredExitCode=typeof candidate?.exitCode=="number",exitCode=hasDeclaredExitCode?Math.trunc(candidate?.exitCode):EXIT_CODE.GENERIC_FAILURE;return{error_code:typeof candidate?.code=="string"&&candidate.code.length>0&&hasDeclaredExitCode?candidate?.code:UNCLASSIFIED_CAPTURED_ERROR_CODE,exit_code:exitCode}}function buildCapturedErrorTags(error){const contract=readCapturedErrorContract(error);return{"pm.error_code":contract.error_code,"pm.exit_code":String(contract.exit_code)}}function sentryCaptureCliError(error){if(!shouldCaptureCliError(error))return;const Sentry=getSentry();if(!Sentry)return;const tags=buildCapturedErrorTags(error);if(error instanceof Error){const extras={};"exitCode"in error&&typeof error.exitCode=="number"&&(extras.exit_code=error.exitCode),"context"in error&&typeof error.context=="object"&&(extras.error_context=crashReportErrorContext(error.context)),Sentry.captureException(error,{extra:extras,tags})}else Sentry.captureException(new Error(String(error)),{tags})}function sentryLogCliUsageError(params){const Sentry=getSentry();if(!Sentry)return;const resolvedCommandResolution=params.command_resolution??deriveTelemetryCommandResolution({ok:!1,errorCode:params.error_code,errorCategory:params.error_category}),resolvedResolutionStage=params.resolution_stage??"unknown",payload={"pm.command":params.command,"pm.error_code":params.error_code,"pm.error_category":params.error_category,"pm.exit_code":params.exit_code,"pm.error_message":params.error_message,"pm.command_resolution":resolvedCommandResolution,"pm.resolution_stage":resolvedResolutionStage,"pm.source_context":params.source_context??""},loggerCandidate=Sentry.logger;if(loggerCandidate&&typeof loggerCandidate.warn=="function"){loggerCandidate.warn("pm_cli_usage_error",payload);return}Sentry.captureMessage(`pm_cli_usage_error:${params.error_code}`,{level:"warning",tags:{"pm.command":params.command,"pm.error_code":params.error_code,"pm.error_category":params.error_category,"pm.exit_code":String(params.exit_code),"pm.command_resolution":resolvedCommandResolution,"pm.resolution_stage":resolvedResolutionStage,"pm.source_context":params.source_context??"unknown"},extra:payload})}function shouldCaptureCliError(error){if(error instanceof ExtensionCommandError)return!0;if(error instanceof PmCliError||isPmCliExpectedError(error)||error instanceof Error&&error.name==="AbortError"&&/(?:ctrl\+[cd]|sigint|user[- ]initiated)/i.test(error.message))return!1;if(typeof error=="object"&&error!==null&&"exitCode"in error&&typeof error.exitCode=="number"){if(error instanceof Error&&error.name==="CommandError")return!1;const exitCode=Math.trunc(error.exitCode);return!new Set([EXIT_CODE.SUCCESS,EXIT_CODE.USAGE,EXIT_CODE.NOT_FOUND,EXIT_CODE.CONFLICT]).has(exitCode)}return!0}async function sentryFlush(timeoutMs=3e3){const Sentry=getSentry();if(Sentry)try{await Sentry.flush(timeoutMs)}catch{}}export{UNCLASSIFIED_CAPTURED_ERROR_CODE,sentryCaptureCliError,sentryDiscardCommandSpan,sentryFinishCommandSpan,sentryFlush,sentryLogCliUsageError,sentrySetCommandContext,sentryStartCommandSpan,shouldCaptureCliError};
2
2
  //# sourceMappingURL=helpers.js.map