@unbrained/pm-cli 2026.10.31 → 2026.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +27 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +1 -1
- package/dist/cli/migration-gates.js +1 -1
- package/dist/cli/register-operations.d.ts +3 -3
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/{chunk-GMBDZID5.js → chunk-3A4NVHYS.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-VC7TA67O.js → chunk-3ZQQVJT2.js} +19 -19
- package/dist/cli-bundle/chunks/{chunk-XDDPSWIW.js → chunk-4HW253W2.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-4OELCF3O.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-6PSDQ7T7.js → chunk-533L6D2N.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-35Q2B2GJ.js → chunk-ABFFRQRO.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-BFZUJ5QP.js +115 -0
- package/dist/cli-bundle/chunks/{chunk-SBVX5CMV.js → chunk-D52QQ7LP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-P3LNMWL2.js → chunk-DAZFCSDV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-43R7UTWV.js → chunk-EN27ZQSV.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-MRUA2Z43.js → chunk-GK2CHAT3.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-BS2KXT24.js → chunk-HOGKWJU5.js} +42 -42
- package/dist/cli-bundle/chunks/{chunk-WX25MU4N.js → chunk-J3JM36EH.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-V6EWBTNH.js → chunk-JRXD7SIL.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-U7JXZMU5.js → chunk-K46GDAKX.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PT7TS6E7.js → chunk-KJLGBLR3.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-2AHSPMKJ.js → chunk-NGRPD3ZO.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-OJR2YGXF.js +31 -0
- package/dist/cli-bundle/chunks/chunk-QHHFXNSQ.js +3 -0
- package/dist/cli-bundle/chunks/{chunk-TL3O5GZA.js → chunk-RGHLQGEX.js} +4 -4
- package/dist/cli-bundle/chunks/{chunk-24J6TNYE.js → chunk-VEOFWE32.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-ZNWDYDFF.js +129 -0
- package/dist/cli-bundle/chunks/{indexed-receipt-blobs-KGMJVFH2.js → indexed-receipt-blobs-IEJ2QOW3.js} +2 -2
- package/dist/cli-bundle/chunks/item-transaction-JHPBH6N7.js +2 -0
- package/dist/cli-bundle/chunks/{mutation-admission-I4RHIT3Q.js → mutation-admission-QUNQZCZ5.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-MVDL5EXG.js → register-list-query-NZTK3AXA.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-GAF7LXRB.js → register-mutation-2OFAMOVH.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-RFMM5KZ6.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-MZN6ONB3.js → register-setup-6B6Q7M5L.js} +2 -2
- package/dist/cli-bundle/chunks/{settlement-hook-YWSWFOZY.js → settlement-hook-UTCEZBFT.js} +2 -2
- package/dist/cli-bundle/chunks/{stage-2ZORTOF6.js → stage-VLI4OBVX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-4QPGJO2G.js → chunk-3UD6F2KQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-EA65HVBO.js → chunk-4NVPDZLX.js} +12 -12
- package/dist/cli-bundle/focused-chunks/{chunk-KKRIFBSL.js → chunk-4UUE3YXZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-NRBUMDLF.js → chunk-5UDDRPYR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SFQKCA5K.js → chunk-745IDMTD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-QFMJGQGO.js → chunk-B6OOKKOX.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-5MCGYBOQ.js → chunk-EJJWOFP5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-FUGYXUUL.js +80 -0
- package/dist/cli-bundle/focused-chunks/{chunk-6F6APBCX.js → chunk-HMXVTWIV.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-HSAEEWSI.js → chunk-I4Z2SCV5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JLN5CFYM.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-K4FMZB4Y.js +23 -0
- package/dist/cli-bundle/focused-chunks/{chunk-DFUTCKUF.js → chunk-MQVBWFJF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-2J7T2UCH.js → chunk-NJFMG3IK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HPWT3RBG.js → chunk-PCCZ72C3.js} +54 -54
- package/dist/cli-bundle/focused-chunks/chunk-PF553UVP.js +44 -0
- package/dist/cli-bundle/focused-chunks/{chunk-MW77Q3AQ.js → chunk-Q23K6WLV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-Y6UYRMVG.js → chunk-QNXXOFLQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-JLXGRDTG.js → chunk-QVFE5LIX.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-KH5QJXY7.js → chunk-RUCG6UWN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-JNKRUCVZ.js → chunk-SGWYNU7T.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-TGS5RXBY.js +26 -0
- package/dist/cli-bundle/focused-chunks/{chunk-PC3R5CJV.js → chunk-WD43E7RU.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-PFBQOZPK.js → chunk-YM4AMP7H.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{indexed-receipt-blobs-KHWT44EK.js → indexed-receipt-blobs-FT3KF6VJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{item-transaction-LU75EG7D.js → item-transaction-F4SN7TXJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{mutation-admission-KDJ7SCPA.js → mutation-admission-7R2FXJ5O.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{settlement-hook-SJV52ZTV.js → settlement-hook-KMIBD4F2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{stage-R4KEBO37.js → stage-6B3O2GZ6.js} +2 -2
- package/dist/cli-bundle/main.js +24 -24
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +4 -4
- package/dist/cli-bundle/sdk-core.js +1 -1
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +57 -57
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +9 -9
- package/dist/core/extensions/extension-types.d.ts +5 -0
- package/dist/core/extensions/extension-types.js +1 -1
- package/dist/core/item/parse.js +2 -2
- package/dist/core/lock/lock-confinement.d.ts +55 -0
- package/dist/core/lock/lock-confinement.js +2 -0
- package/dist/core/lock/lock-gc.js +1 -1
- package/dist/core/lock/lock.js +2 -2
- package/dist/core/output/mutation-projection.js +1 -1
- package/dist/core/output/output.js +2 -2
- package/dist/core/shared/errors.d.ts +6 -0
- package/dist/core/shared/errors.js +1 -1
- package/dist/core/store/settings-sections.d.ts +61 -0
- package/dist/core/store/settings-sections.js +3 -0
- package/dist/core/store/settings-validator.d.ts +4 -0
- package/dist/core/store/settings-validator.js +1 -1
- package/dist/core/store/settings.d.ts +29 -2
- package/dist/core/store/settings.js +4 -3
- package/dist/core/telemetry/consent.js +2 -2
- package/dist/core/telemetry/runtime.js +2 -2
- package/dist/core/test/background-runs.js +3 -3
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/config.js +1 -1
- package/dist/sdk/context-intent-contracts.d.ts +17 -0
- package/dist/sdk/context-intent-contracts.js +1 -1
- package/dist/sdk/core.d.ts +1 -1
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/extension-command-context.js +2 -2
- package/dist/sdk/generated/error-code-catalog-rows.d.ts +39 -0
- package/dist/sdk/generated/error-code-catalog-rows.js +2 -0
- package/dist/sdk/generated/generated-error-code-catalog-part-1.d.ts +3 -3
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.d.ts +3 -3
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/generated-error-code-catalog.js +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/init-agent-guidance.js +2 -2
- package/dist/sdk/lifecycle/claim.d.ts +27 -0
- package/dist/sdk/lifecycle/claim.js +1 -1
- package/dist/sdk/lifecycle/mcp-actions.d.ts +1 -1
- package/dist/sdk/lifecycle/mcp-actions.js +1 -1
- package/dist/sdk/lifecycle/task-composition.js +1 -1
- package/dist/sdk/mcp/discovery.js +1 -1
- package/dist/sdk/output/list-envelope.d.ts +8 -0
- package/dist/sdk/output/list-envelope.js +1 -1
- package/dist/sdk/output.d.ts +1 -1
- package/dist/sdk/output.js +2 -2
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/context.js +2 -2
- package/dist/sdk/runtime/actions.js +1 -1
- package/dist/sdk/runtime-input.js +1 -1
- package/dist/sdk/runtime-primitives.d.ts +2 -1
- package/dist/sdk/runtime-primitives.js +1 -1
- package/dist/sdk/runtime-public-contracts.d.ts +2 -0
- package/dist/sdk/runtime.d.ts +2 -2
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/start-task-status.d.ts +1 -1
- package/dist/sdk/start-task-status.js +1 -1
- package/docs/AGENT_EVIDENCE_CONSISTENCY.md +10 -0
- package/docs/AGENT_GUIDE.md +3 -3
- package/docs/COMMANDS.md +1 -0
- package/docs/COMMAND_NAMESPACES.md +1 -1
- package/docs/CONFIGURATION.md +9 -0
- package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +9 -6
- package/docs/LIFECYCLE_COMMANDS.md +19 -4
- package/docs/RELEASING.md +22 -2
- package/docs/SDK.md +37 -2
- package/docs/SDK_ACTION_CONFORMANCE.md +21 -0
- package/docs/SDK_CONFIGURATION_SAFETY.md +55 -0
- package/docs/TESTING.md +2 -1
- package/docs/agent-task-token-baseline.json +1 -1
- package/docs/agent-task-transcripts.json +1 -1
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +12 -3
- package/marketplace.json +1 -1
- package/package.json +9 -3
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-HSBTITO4.js +0 -3
- package/dist/cli-bundle/chunks/chunk-WNF6FOL7.js +0 -31
- package/dist/cli-bundle/chunks/chunk-WRXQAW7Q.js +0 -114
- package/dist/cli-bundle/chunks/chunk-YT5D5HNG.js +0 -2
- package/dist/cli-bundle/chunks/chunk-ZTJVOBDU.js +0 -128
- package/dist/cli-bundle/chunks/item-transaction-UWV6UNSV.js +0 -2
- package/dist/cli-bundle/chunks/register-operations-JLFRL36Q.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-57HPXGOQ.js +0 -79
- package/dist/cli-bundle/focused-chunks/chunk-AFVN2KCP.js +0 -44
- package/dist/cli-bundle/focused-chunks/chunk-GP2WU43K.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-NOKG4RFN.js +0 -22
- package/dist/cli-bundle/focused-chunks/chunk-WHIE2C6Q.js +0 -2
|
@@ -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]="
|
|
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
|
package/dist/core/item/parse.js
CHANGED
|
@@ -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]="
|
|
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]="
|
|
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
|
package/dist/core/lock/lock.js
CHANGED
|
@@ -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]="
|
|
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&¤tLockInfo.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){
|
|
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&¤tLockInfo.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,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]="
|
|
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]="ea19c980-44aa-54eb-b853-d829335e520e")}catch{}})();import{unicodeSafePrefix}from"../shared/text-normalization.js";const CHANGED_FIELDS_KEY="changed_fields",CHANGED_FIELD_COUNT_KEY="changed_field_count",ROWS_KEY="rows",UPDATE_MANY_MUTATION_MODES=new Set(["apply","rollback"]),DELETE_OUTCOMES=new Set(["deleted","would_delete"]),COMPACT_RECURRENCE_TEXT_LIMIT=256,COMPOUND_MUTATION_STEPS={start_task:["claim","update"],pause_task:["update","release"],close_task:["close","release"]};function projectCompoundMutation(result,options){if(!isPlainObject(result)||typeof result.action!="string"||typeof result.id!="string"||result.id.length===0)return null;const steps=Object.hasOwn(COMPOUND_MUTATION_STEPS,result.action)?COMPOUND_MUTATION_STEPS[result.action]:void 0;if(!steps||!steps.every(step=>{const value=result[step];return isPlainObject(value)&&isPlainObject(value.item)&&value.item.id===result.id&&Array.isArray(value.changed_fields)&&value.changed_fields.every(field=>typeof field=="string")}))return null;const final=result[steps.at(-1)],identity=projectIdOnlyResult(final);return options.idOnly?identity:!options.compactEnvelope&&options.changedFields!=="compact"?null:{...result,...options.compactEnvelope?{...identity,changed_field_count:new Set(steps.flatMap(step=>result[step].changed_fields)).size}:{},...Object.fromEntries(steps.map(step=>[step,projectMutationResult(result[step],options)]))}}function truncateCompactRecurrenceText(value){return typeof value!="string"||value.length<=COMPACT_RECURRENCE_TEXT_LIMIT?value:`${unicodeSafePrefix(value,COMPACT_RECURRENCE_TEXT_LIMIT-1)}\u2026`}function projectCompactRecurrence(value){if(!isPlainObject(value))return null;const recurrence={...value};return recurrence.reason=truncateCompactRecurrenceText(recurrence.reason),isPlainObject(recurrence.previous_terminal)&&(recurrence.previous_terminal=Object.fromEntries(Object.entries(recurrence.previous_terminal).map(([key,entry])=>[key,truncateCompactRecurrenceText(entry)]))),recurrence}function isPlainObject(value){if(typeof value!="object"||value===null||Array.isArray(value))return!1;const proto=Object.getPrototypeOf(value);return proto===null||proto===Object.prototype}function compactOwnChangedFields(envelope){const changedFields=envelope[CHANGED_FIELDS_KEY];if(!Array.isArray(changedFields))return null;const projected=Object.getPrototypeOf(envelope)===null?Object.create(null):{};for(const[key,value]of Object.entries(envelope))key!==CHANGED_FIELDS_KEY&&(projected[key]=value);return projected[CHANGED_FIELD_COUNT_KEY]=changedFields.length,projected}function isMutationEnvelope(value){return"item"in value&&Array.isArray(value[CHANGED_FIELDS_KEY])}function isUpdateManyMutationEnvelope(value){return typeof value.mode=="string"&&UPDATE_MANY_MUTATION_MODES.has(value.mode)&&Array.isArray(value[ROWS_KEY])}function replaceRows(envelope,rows){const projected=Object.getPrototypeOf(envelope)===null?Object.create(null):{};return Object.assign(projected,envelope),projected[ROWS_KEY]=rows,projected}function projectIdOnlyResult(result){if(!isPlainObject(result))return null;const subject=isPlainObject(result.item)?result.item:isPlainObject(result.plan)?result.plan:null;if(subject===null)return null;const id=typeof subject.id=="string"?subject.id:void 0,status=typeof result.outcome=="string"&&DELETE_OUTCOMES.has(result.outcome)?result.outcome:typeof subject.status=="string"?subject.status:void 0;return id?status?{id,status,...typeof result.deleted=="boolean"?{deleted:result.deleted}:{}}:{id}:null}function projectCompactMutationEnvelope(result,includeItem){if(!isPlainObject(result))return null;if(isPlainObject(result.plan)&&isPlainObject(result.mutation_receipt)&&result.mutation_receipt.kind==="plan_mutation"&&result.mutation_receipt.id===result.plan.id&&result.mutation_receipt.action===result.action)return result.mutation_receipt;if(!includeItem||!isPlainObject(result.item)||typeof result.item.id!="string")return null;const changedFields=result[CHANGED_FIELDS_KEY];if(!Array.isArray(changedFields))return null;const compact={id:result.item.id,...typeof result.outcome=="string"&&DELETE_OUTCOMES.has(result.outcome)?{status:result.outcome}:typeof result.item.status=="string"?{status:result.item.status}:{},...typeof result.deleted=="boolean"?{deleted:result.deleted}:{},...typeof result.previous_status=="string"?{previous_status:result.previous_status}:{},changed_field_count:changedFields.length};return appendCompactMutationEvidence(result,result.item,compact),compact}function appendCompactMutationEvidence(result,item,compact){const closeReason=typeof result.close_reason=="string"?result.close_reason:typeof item.close_reason=="string"?item.close_reason:void 0;closeReason!==void 0&&(compact.close_reason=closeReason);const recurrence=projectCompactRecurrence(result.recurrence);recurrence!==null&&(compact.recurrence=recurrence),Array.isArray(result.warnings)&&result.warnings.length>0&&(compact.warnings=result.warnings);for(const key of["recovery","follow_up","handoff","completion","start"])isPlainObject(result[key])&&(compact[key]=result[key]);if(typeof result.claimed_by=="string"||typeof result.released_by=="string")for(const key of["claimed_by","released_by","previous_assignee","forced","skipped","available","attempts","suggestions"])Object.hasOwn(result,key)&&(compact[key]=result[key])}function compactUpdateManyRows(envelope){const rows=isUpdateManyMutationEnvelope(envelope)?envelope[ROWS_KEY]:void 0;if(!Array.isArray(rows))return{projected:envelope,changed:!1};let rowsChanged=!1;const nextRows=rows.map(row=>{if(!isPlainObject(row))return row;const compactedRow=compactOwnChangedFields(row);return compactedRow?(rowsChanged=!0,compactedRow):row});return rowsChanged?{projected:replaceRows(envelope,nextRows),changed:!0}:{projected:envelope,changed:!1}}function projectMutationResult(result,options={}){const compound=projectCompoundMutation(result,options);if(compound!==null)return compound;if(options.idOnly===!0){const idOnly=projectIdOnlyResult(result);if(idOnly!==null)return idOnly}if(options.compactEnvelope===!0||options.changedFields==="compact"){const compact=projectCompactMutationEnvelope(result,options.compactEnvelope===!0);if(compact!==null)return compact}if((options.changedFields??"full")==="full"||!isPlainObject(result))return result;let changed=!1,projected=result;const compactedTop=isMutationEnvelope(result)?compactOwnChangedFields(result):null;compactedTop!==null&&(projected=compactedTop,changed=!0);const rowProjection=compactUpdateManyRows(projected);return rowProjection.changed&&(projected=rowProjection.projected,changed=!0),changed?projected:result}export{projectMutationResult};
|
|
2
2
|
//# sourceMappingURL=mutation-projection.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
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]="
|
|
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]="d47af602-51bb-592c-8dce-3de5c8a96636")}catch{}})();import{getActiveCommandContext,runActiveCommandOverride,runActiveRendererOverride,runActiveServiceOverrideSync,setActiveCommandResult}from"../extensions/index.js";import{EXIT_CODE}from"../shared/constants.js";import{isHostOutputSuppressed}from"./output-control.js";import{projectMutationResult}from"./mutation-projection.js";import{attachReadOutputContracts}from"../../sdk/context-intent-contracts.js";import{attachOutputTokenAccounting}from"../../sdk/output-token-accounting.js";import{propagateContextUsageServingReceipt}from"../../sdk/context-usage.js";import{isReadOutputBudgetExceeded,resolveReadOutputEncoding}from"../../sdk/read-output-contracts.js";import{encode as encodeToon}from"@toon-format/toon";import{encodePmTableRows}from"../../sdk/output/table-rows.js";import{projectPmBriefListEnvelope,projectPmBriefSearchEnvelope}from"../../sdk/output/list-envelope.js";import{prepareWellFormedOutputText}from"../shared/output-unicode.js";const DECLARED_PROCESS_EXIT_CODES=new Set(Object.values(EXIT_CODE)),COLLECTION_COUNT_ALIASES=new Map([["notes_count","notes"],["tests_count","tests"]]),NATIVE_OUTPUT_MARKER="__pm_native_output";let streamErrorHandlersInstalled=!1;function isPlainObject(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function setActiveCommandResultOmitted(source){const omitted={read_output:{result_omitted:!0}};propagateContextUsageServingReceipt(source,omitted),setActiveCommandResult(omitted)}function setActiveCommandDeliveryResult(result,receiptSource,structuredDeliveryPreserved){if(structuredDeliveryPreserved){setActiveCommandResult(result);return}setActiveCommandResultOmitted(receiptSource)}function stripNativeOutputMarker(result){if(!isPlainObject(result)||result[NATIVE_OUTPUT_MARKER]!==!0)return result;const{[NATIVE_OUTPUT_MARKER]:_marker,...rest}=result;return rest}function isBrokenPipeError(error){return typeof error=="object"&&error!==null&&error.code==="EPIPE"}function markStdoutBrokenPipeExitCode(){(process.exitCode===void 0||process.exitCode===EXIT_CODE.SUCCESS)&&(process.exitCode=EXIT_CODE.SUCCESS)}function markStderrBrokenPipeExitCode(){(process.exitCode===void 0||process.exitCode===EXIT_CODE.SUCCESS)&&(process.exitCode=EXIT_CODE.GENERIC_FAILURE)}function markBrokenPipeExitCode(target){if(target==="stdout"){markStdoutBrokenPipeExitCode();return}markStderrBrokenPipeExitCode()}function handleUnhandledStreamError(error){const unhandled=error instanceof Error?error:new Error(String(error));setImmediate(()=>{throw unhandled})}function installStreamErrorHandlers(){streamErrorHandlersInstalled||(streamErrorHandlersInstalled=!0,process.stdout.on("error",error=>{if(isBrokenPipeError(error)){markBrokenPipeExitCode("stdout");return}handleUnhandledStreamError(error)}),process.stderr.on("error",error=>{if(isBrokenPipeError(error)){markBrokenPipeExitCode("stderr");return}handleUnhandledStreamError(error)}))}function writeToStream(target,text){installStreamErrorHandlers();try{return target==="stdout"?process.stdout.write(text):process.stderr.write(text),!0}catch(error){if(isBrokenPipeError(error))return markBrokenPipeExitCode(target),!1;throw error}}function writeStdout(text){return writeToStream("stdout",text)}function writeStderr(text){return writeToStream("stderr",text)}function renderScalar(value){return typeof value=="string"?JSON.stringify(value):typeof value=="number"||typeof value=="boolean"?String(value):value===null?"null":JSON.stringify(value)}function indentToon(encoded,depth){const indent=" ".repeat(depth);return encoded.split(`
|
|
2
2
|
`).map(line=>`${indent}${line}`).join(`
|
|
3
3
|
`)}function isNestedTableCandidateBounded(rows){const columns=new Set;let presentCells=0;for(const row of rows){const keys=Object.keys(row);presentCells+=keys.length;for(const key of keys)columns.add(key)}return rows.length*columns.size<=presentCells*2}function renderToonObjectEntry(key,entry,depth,allowNestedTable){const indent=" ".repeat(depth);if(!isPlainObject(entry)&&!Array.isArray(entry))return`${indent}${key}: ${renderScalar(entry)}`;if(Array.isArray(entry)){if(entry.length===0)return`${indent}${key}: []`;const expanded=indentToon(encodeToon({[key]:entry}),depth);return allowNestedTable&&entry.length>=1&&entry.every(isPlainObject)&&isNestedTableCandidateBounded(entry)?[!1,!0].map(compact=>{const table=encodePmTableRows(entry,{compact});return indentToon(encodeToon({[key]:table.rows,[`${key}_encoding`]:table.encoding}),depth)}).reduce((smallest,candidate)=>Buffer.byteLength(candidate)<Buffer.byteLength(smallest)?candidate:smallest,expanded):expanded}return isPlainObject(entry)&&Object.keys(entry).length===0?`${indent}${key}: {}`:`${indent}${key}:
|
|
4
4
|
${renderToonValue(entry,depth+1)}`}function compactToonArray(values,policy,preserveEmpty){const compactedEntries=values.map(entry=>compactToonValue(entry,policy,preserveEmpty||policy.preserveRowFacts&&isPlainObject(entry))).filter(entry=>entry!==void 0);return compactedEntries.length>0||preserveEmpty?compactedEntries:void 0}function compactToonValue(value,policy={omitDuplicateCountAliases:!0,preserveRowFacts:!0},preserveEmpty=!1){if(value===null)return preserveEmpty?null:void 0;if(Array.isArray(value))return compactToonArray(value,policy,preserveEmpty);if(isPlainObject(value)){const compacted=[],entries=Object.entries(value).filter(([key,entry])=>{const canonicalCount=COLLECTION_COUNT_ALIASES.get(key);return!policy.omitDuplicateCountAliases||canonicalCount===void 0||!isPlainObject(value.collection_counts)||entry!==value.collection_counts[canonicalCount]});for(const[key,entry]of entries){if(key==="omitted_field_groups"&&Array.isArray(entry)&&entry.length===0){compacted.push([key,[]]);continue}const compactedEntry=compactToonValue(entry,policy,preserveEmpty);compactedEntry!==void 0&&compacted.push([key,compactedEntry])}return compacted.length>0||preserveEmpty?Object.fromEntries(compacted):void 0}return value}function renderToonValue(value,depth){const indent=" ".repeat(depth);if(Array.isArray(value))return value.length===0?"[]":indentToon(encodeToon(value),depth);if(isPlainObject(value)){const entries=Object.entries(value);return entries.length===0?"{}":entries.map(([key,entry])=>renderToonObjectEntry(key,entry,depth,!Object.hasOwn(value,`${key}_encoding`)&&!(key==="checks"&&isPlainObject(value.verdict)&&value.verdict.authority==="ok"))).join(`
|
|
@@ -7,7 +7,7 @@ ${renderToonValue(entry,depth+1)}`}function compactToonArray(values,policy,prese
|
|
|
7
7
|
`;for(const event of value.events){if(!isPlainObject(event))continue;const kind=typeof event.kind=="string"?event.kind:"event",title=typeof event.item_title=="string"?event.item_title:"",itemId=typeof event.item_id=="string"?event.item_id:"",reminderText=typeof event.reminder_text=="string"&&event.reminder_text.length>0?` ${event.reminder_text}`:"";lines.push(`- [${kind}] ${itemId} ${title}${reminderText}`.trim())}return`${lines.join(`
|
|
8
8
|
`)}
|
|
9
9
|
`}const LEAN_READ_ENVELOPE_ECHO_KEYS=new Set(["filters","now","projection","sorting"]);function projectLeanJsonValue(value){const compacted=compactToonValue(value,{omitDuplicateCountAliases:!1,preserveRowFacts:!1});if(!isPlainObject(compacted)||!Array.isArray(compacted.items))return compacted??null;const projected=Object.fromEntries(Object.entries(compacted).filter(([key])=>!LEAN_READ_ENVELOPE_ECHO_KEYS.has(key)));return projected.has_more!==!0&&delete projected.next_cursor,projected}function projectLinkedTestEvidence(value,lean){if(!isPlainObject(value)||!Array.isArray(value.run_results)||!value.run_results.every(isPlainObject))return value;const rows=value.run_results,sharedContext=resolveSharedExecutionContext(rows),runResults=rows.map(row=>{const{execution_context:_executionContext,stdout,stderr,...rest}=row;return sharedContext===void 0?row:!lean||row.status!=="passed"?{...rest,stdout,stderr}:rest});return lean&&isQuietLinkedTestSuccess(value,sharedContext,rows)?projectQuietLinkedTestSuccess(value,sharedContext,runResults):{...value,...sharedContext===void 0?{}:{execution_context:sharedContext},run_results:runResults}}function resolveSharedExecutionContext(rows){const contexts=rows.map(row=>row.execution_context).filter(context=>context!==void 0);if(contexts.length!==rows.length||contexts.length===0)return;const first=JSON.stringify(contexts[0]);return contexts.every(context=>JSON.stringify(context)===first)?contexts[0]:void 0}function isQuietLinkedTestSuccess(value,sharedContext,rows){if(value.ok!==!0||!isPlainObject(sharedContext)||sharedContext.mismatch_detected!==!1||!rows.every(row=>row.status==="passed"))return!1;const categories=value.failure_categories;return!(isPlainObject(categories)&&Object.values(categories).some(count=>count!==0))}function projectQuietLinkedTestSuccess(value,context,runResults){const{failure_categories:_zeroCategories,...rest}=value,trust=isPlainObject(context.trust)?context.trust:{};return{...rest,execution_context:{pm_context_mode:context.pm_context_mode,workspace_context_mode:context.workspace_context_mode,mismatch_detected:!1,...typeof trust.trusted=="boolean"?{trusted:trust.trusted}:{},...trust.reason===void 0?{}:{trust_reason:trust.reason},restore_with:"not retained; a run without --lean reports the full context"},run_results:runResults}}function projectEmptyCheckDetails(value){return!isPlainObject(value)||value.ok!==!0||!isPlainObject(value.verdict)||value.verdict.authority!=="ok"||!Array.isArray(value.checks)?value:{...value,checks:value.checks.map(omitEmptySuccessfulCheckDetails)}}function omitEmptySuccessfulCheckDetails(check){if(!isPlainObject(check)||check.ok!==!0||!isPlainObject(check.details)||Object.keys(check.details).length!==0)return check;const{details:_details,...rest}=check;return rest}function formatBuiltInOutput(result,format,lean=!1){if(result=prepareWellFormedOutputText(result),format==="json"){const value=lean?projectLeanJsonValue(result):result,briefList=isPlainObject(result)&&isPlainObject(result.projection)&&result.projection.mode==="brief"&&Array.isArray(result.items);return`${JSON.stringify(value,null,briefList?void 0:2)}
|
|
10
|
-
`}const compactedToon=compactToonValue(projectEmptyCheckDetails(projectPmBriefListEnvelope(result)),{omitDuplicateCountAliases:!0,preserveRowFacts:!0});return compactedToon===void 0?`{}
|
|
10
|
+
`}const compactedToon=compactToonValue(projectEmptyCheckDetails(projectPmBriefSearchEnvelope(projectPmBriefListEnvelope(result))),{omitDuplicateCountAliases:!0,preserveRowFacts:!0});return compactedToon===void 0?`{}
|
|
11
11
|
`:`${renderToonValue(compactedToon,0)}
|
|
12
12
|
`}function resolveOutputService(effectiveResult,nativeOutput,options,format){const serviceOverride=nativeOutput?{handled:!1,result:effectiveResult,warnings:[]}:runActiveServiceOverrideSync("output_format",{command:options.command,args:options.commandArgs,command_options:options.commandOptions,global:{...options},pm_root:options.pmRoot,format,options:{...options},result:effectiveResult}),legacyPayloadEchoWarning=serviceOverride.warnings.find(warning=>warning.startsWith("extension_output_format_payload_echo_deprecated:"));return legacyPayloadEchoWarning!==void 0&&writeStderr(`Warning: ${legacyPayloadEchoWarning}. Returning context.payload now declines safely for compatibility; migrate the override to declineServiceOverride() or { handled: false }.
|
|
13
13
|
`),serviceOverride.handled&&typeof serviceOverride.result=="string"?{result:serviceOverride.result,rendered:serviceOverride.result.endsWith(`
|
|
@@ -299,6 +299,12 @@ created_at:string;
|
|
|
299
299
|
/** Stale-lock lifetime of the current lock generation. */
|
|
300
300
|
ttl_seconds:number;
|
|
301
301
|
} | null;
|
|
302
|
+
/** Tracker-relative lock path component that a refused `lock_path_unsafe` acquisition could not prove confined. */
|
|
303
|
+
lock_component?:string;
|
|
304
|
+
/** Revision a refused compare-and-set write expected, or null when it required absence. */
|
|
305
|
+
expected_revision?:string | null;
|
|
306
|
+
/** Revision found at a refused compare-and-set write, or null when the target was absent. */
|
|
307
|
+
current_revision?:string | null;
|
|
302
308
|
/** Value that configures or reports recovery for this contract. */
|
|
303
309
|
recovery?:PmCliErrorRecoveryPayload;
|
|
304
310
|
}
|
|
@@ -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]="
|
|
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]="59ace77e-9dc1-59e8-aa2e-ee7364e04773")}catch{}})();class PmCliError extends Error{exitCode;code;context;constructor(message,exitCode,context={}){super(message),this.name="PmCliError",this.exitCode=exitCode,this.context=context,this.code=context.code}}export{PmCliError};
|
|
2
2
|
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Validate and normalize a stored section value; throw to refuse it. */
|
|
2
|
+
export type SettingsSectionValidator<T> = (raw:unknown) => T;
|
|
3
|
+
/** Options for reading one extension-owned settings section. */
|
|
4
|
+
export interface ReadSettingsSectionOptions<T> {
|
|
5
|
+
/** Validate the stored value; a throw becomes a typed `settings_section_invalid` refusal. */
|
|
6
|
+
validate?:SettingsSectionValidator<T>;
|
|
7
|
+
/** Value returned, unvalidated, when the section is absent. */
|
|
8
|
+
defaultValue?:T;
|
|
9
|
+
}
|
|
10
|
+
/** One extension-owned settings section as the host resolved it. */
|
|
11
|
+
export interface SettingsSectionReadResult<T> {
|
|
12
|
+
/** Top-level settings key that holds the section. */
|
|
13
|
+
section:string;
|
|
14
|
+
/** `stored` when settings.json holds the section, `default` when it is absent. */
|
|
15
|
+
source:"stored" | "default";
|
|
16
|
+
/** Validated stored value, or the default (undefined when none was given). */
|
|
17
|
+
value:T | undefined;
|
|
18
|
+
/** Content revision of the stored section (`sha256:<hex>`), or null when absent. */
|
|
19
|
+
revision:string | null;
|
|
20
|
+
}
|
|
21
|
+
/** Options for a revision-safe write of one extension-owned settings section. */
|
|
22
|
+
export interface WriteSettingsSectionOptions<T> {
|
|
23
|
+
/** Refuse unless the stored section still has this revision; null requires it to be absent. */
|
|
24
|
+
expectedRevision?:string | null;
|
|
25
|
+
/** Validate and normalize the value before it is stored. */
|
|
26
|
+
validate?:SettingsSectionValidator<T>;
|
|
27
|
+
/**
|
|
28
|
+
* Mutation author for the history event; omitted, the workspace default and
|
|
29
|
+
* detected identity apply. The host-bound `context.sdk` writer passes the
|
|
30
|
+
* invocation author.
|
|
31
|
+
*/
|
|
32
|
+
author?:string;
|
|
33
|
+
}
|
|
34
|
+
/** Receipt of a settings-section write. */
|
|
35
|
+
export interface SettingsSectionWriteResult {
|
|
36
|
+
/** Top-level settings key that holds the section. */
|
|
37
|
+
section:string;
|
|
38
|
+
/** Whether settings.json changed. */
|
|
39
|
+
changed:boolean;
|
|
40
|
+
/** Revision before the write, or null when the section was absent. */
|
|
41
|
+
previous_revision:string | null;
|
|
42
|
+
/** Revision after the write, or null when the section was removed. */
|
|
43
|
+
revision:string | null;
|
|
44
|
+
}
|
|
45
|
+
/** Content revision of a stored section value. */
|
|
46
|
+
export declare function settingsSectionRevision(value:unknown):string;
|
|
47
|
+
/**
|
|
48
|
+
* Read one extension-owned top-level section of the tracker's settings through
|
|
49
|
+
* the host. Absent sections resolve to the default; malformed documents and
|
|
50
|
+
* values the validator refuses become typed refusals instead of thrown parse
|
|
51
|
+
* errors.
|
|
52
|
+
*/
|
|
53
|
+
export declare function readSettingsSection<T = unknown>(pmRoot:string,section:string,options?:ReadSettingsSectionOptions<T>):Promise<SettingsSectionReadResult<T>>;
|
|
54
|
+
/**
|
|
55
|
+
* Replace (or, with `undefined`, remove) one extension-owned settings section
|
|
56
|
+
* under the workspace-history lock. Every other key of settings.json is kept
|
|
57
|
+
* byte-for-byte in value. `expectedRevision` makes the write compare-and-set:
|
|
58
|
+
* a section changed since it was read refuses with
|
|
59
|
+
* `settings_section_revision_conflict` and nothing is written.
|
|
60
|
+
*/
|
|
61
|
+
export declare function writeSettingsSection<T>(pmRoot:string,section:string,value:T | undefined,options?:WriteSettingsSectionOptions<T>):Promise<SettingsSectionWriteResult>;
|
|
@@ -0,0 +1,3 @@
|
|
|
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]="7cbbb50d-e9bc-5940-9ae6-6270e9c2fcbf")}catch{}})();import{createHash}from"node:crypto";import{readFileIfExists}from"../fs/fs-utils.js";import{runActiveOnReadHooks,runActiveOnWriteHooks}from"../extensions/index.js";import{mutateWorkspaceJsonWithHistory}from"../history/workspace-history.js";import{resolveAuthor}from"../shared/author.js";import{EXIT_CODE}from"../shared/constants.js";import{PmCliError}from"../shared/errors.js";import{toErrorMessage}from"../shared/primitives.js";import{stableValueEquals}from"../shared/serialization.js";import{getSettingsPath}from"./paths.js";import{readSettings}from"./settings.js";import{clearSettingsReadCache}from"./settings-read-cache.js";import{CORE_SETTINGS_SECTION_KEYS}from"./settings-validator.js";const SETTINGS_SECTION_NAME_PATTERN=/^[a-z][a-z0-9_-]{0,63}$/u,RESERVED_SECTION_NAMES=new Set(["constructor","prototype"]),SETTINGS_SECTION_WRITE_OP="settings:section:write",MAX_VALIDATION_MESSAGE_LENGTH=240;function settingsSectionRevision(value){return`sha256:${createHash("sha256").update(canonicalSectionJson(value)).digest("hex")}`}function canonicalSectionJson(value){return Array.isArray(value)?`[${value.map(entry=>canonicalSectionJson(entry)).join(",")}]`:typeof value=="object"&&value!==null?`{${Object.keys(value).sort().map(key=>`${JSON.stringify(key)}:${canonicalSectionJson(Object.getOwnPropertyDescriptor(value,key)?.value)}`).join(",")}}`:String(JSON.stringify(value))}function assertSettingsSectionName(section){if(!SETTINGS_SECTION_NAME_PATTERN.test(section)||RESERVED_SECTION_NAMES.has(section))throw new PmCliError(`Invalid settings section name "${section.slice(0,80)}". Use 1-64 lowercase letters, digits, underscores or hyphens, starting with a letter.`,EXIT_CODE.USAGE,{code:"settings_section_name_invalid",field:"section",required:"Name the section after the package, for example lint or jev."});if(CORE_SETTINGS_SECTION_KEYS.includes(section))throw new PmCliError(`Settings section "${section}" is owned by core settings; extension sections must use another name.`,EXIT_CODE.USAGE,{code:"settings_section_reserved",field:"section",value:section,required:"Read core settings with readSettings and change them with writeSettings or pm config."})}function parseSettingsDocument(raw){let parsed;try{parsed=JSON.parse(raw)}catch{parsed=void 0}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))throw new PmCliError("settings.json is not a JSON object, so no settings section can be read or written.",EXIT_CODE.USAGE,{code:"settings_section_document_malformed",reason:parsed===void 0?"settings_json_malformed":"settings_json_not_object",nextSteps:["Repair .agents/pm/settings.json so it is a JSON object, then retry.","Run pm health to confirm the settings warning has cleared."]});return parsed}function validateSectionValue(section,raw,validate){let value;try{value=validate===void 0?raw:validate(raw)}catch(error){throw new PmCliError(`Settings section "${section}" is invalid: ${toErrorMessage(error).slice(0,MAX_VALIDATION_MESSAGE_LENGTH)}`,EXIT_CODE.USAGE,{code:"settings_section_invalid",field:section,required:`Fix the "${section}" section of .agents/pm/settings.json so the owning package accepts it, then retry.`})}return value}function hasJsonHook(value){return typeof Reflect.get(value,"toJSON")=="function"}function describeJsonPath(segments){return segments.length===0?"the value":segments.join(".")}function hasOnlyDataProperties(value,requireEnumerable){return Object.values(Object.getOwnPropertyDescriptors(value)).every(descriptor=>"value"in descriptor&&(!requireEnumerable||descriptor.enumerable===!0))}function isPlainJsonArray(value){if(Object.getPrototypeOf(value)!==Array.prototype||!hasOnlyDataProperties(value,!1))return!1;const keys=Object.keys(value);return keys.length===value.length&&keys.every((key,index)=>key===String(index))&&Object.getOwnPropertyNames(value).length===value.length+1&&Object.getOwnPropertySymbols(value).length===0&&!hasJsonHook(value)}function isPlainJsonObject(value){const prototype=Object.getPrototypeOf(value);return(prototype===Object.prototype||prototype===null)&&hasOnlyDataProperties(value,!0)&&Object.getOwnPropertySymbols(value).length===0&&!hasJsonHook(value)}function findScalarDrift(value,segments){if(value===null||typeof value=="string"||typeof value=="boolean")return null;const where=describeJsonPath(segments);return typeof value=="number"?Number.isFinite(value)?null:{detail:`${where} is a non-finite number`,representable:!0}:{detail:`${where} is ${typeof value}`,representable:typeof value!="bigint"&&segments.length>0}}function findJsonDrift(value,segments=[],ancestors=new Set){if(typeof value!="object"||value===null)return findScalarDrift(value,segments);const where=describeJsonPath(segments);if(ancestors.has(value))return{detail:`${where} is a circular reference`,representable:!1};if(!(Array.isArray(value)?isPlainJsonArray(value):isPlainJsonObject(value)))return{detail:`${where} is not a plain JSON ${Array.isArray(value)?"array":"object"}`,representable:!0};ancestors.add(value);for(const[key,entry]of Object.entries(value)){const drift=findJsonDrift(entry,[...segments,key],ancestors);if(drift!==null)return drift}return ancestors.delete(value),null}function findReservedJsonKey(value,segments=[]){if(typeof value!="object"||value===null)return null;for(const[key,entry]of Object.entries(value)){const keyPath=[...segments,key];if(key==="__proto__"||key==="prototype"&&segments.at(-1)==="constructor")return describeJsonPath(keyPath);const nested=findReservedJsonKey(entry,keyPath);if(nested!==null)return nested}return null}function refuseSectionValue(section,value,reason){return validateSectionValue(section,value,()=>{throw new Error(reason)})}function toStoredSectionValue(section,value){const drift=findJsonDrift(value);if(drift!==null){const reason=drift.representable?`the value would change when stored as JSON: ${drift.detail}`:`the value is not representable as JSON: ${drift.detail}`;return refuseSectionValue(section,value,reason)}const reserved=findReservedJsonKey(value);return reserved!==null?refuseSectionValue(section,value,`${reserved} is a reserved key that settings history cannot record`):JSON.parse(JSON.stringify(value))}function readSectionEntry(document,section){return Object.hasOwn(document,section)?document[section]:void 0}async function readSettingsSection(pmRoot,section,options={}){assertSettingsSectionName(section);const settingsPath=getSettingsPath(pmRoot);await runActiveOnReadHooks({path:settingsPath,scope:"project"});const raw=await readFileIfExists(settingsPath),stored=raw===null?void 0:readSectionEntry(parseSettingsDocument(raw),section);if(stored===void 0)return{section,source:"default",value:options.defaultValue,revision:null};const revision=settingsSectionRevision(stored);return{section,source:"stored",value:validateSectionValue(section,structuredClone(stored),options.validate),revision}}async function writeSettingsSection(pmRoot,section,value,options={}){assertSettingsSectionName(section);const next=value===void 0?void 0:toStoredSectionValue(section,validateSectionValue(section,value,options.validate)),settingsPath=getSettingsPath(pmRoot),settings=await readSettings(pmRoot);try{const mutation=await mutateWorkspaceJsonWithHistory({pmRoot,filePath:settingsPath,op:SETTINGS_SECTION_WRITE_OP,author:resolveAuthor(options.author,settings.author_default),lockTtlSeconds:settings.locks.ttl_seconds,lockWaitMs:settings.locks.wait_ms,recordCreation:!1,mutate:beforeRaw=>{if(beforeRaw===null)throw new PmCliError("settings.json does not exist, so no settings section can be written.",EXIT_CODE.NOT_FOUND,{code:"settings_section_document_missing",required:"Initialize the tracker with pm init, then retry."});const document=parseSettingsDocument(beforeRaw),current=readSectionEntry(document,section),previousRevision=current===void 0?null:settingsSectionRevision(current);if(options.expectedRevision!==void 0&&options.expectedRevision!==previousRevision)throw new PmCliError(`Settings section "${section}" changed since it was read; read it again and reapply the change.`,EXIT_CODE.CONFLICT,{code:"settings_section_revision_conflict",field:section,expected_revision:options.expectedRevision,current_revision:previousRevision,required:"Read the section again with readSettingsSection, then retry with its current revision."});const receipt={section,changed:!1,previous_revision:previousRevision,revision:next===void 0?null:settingsSectionRevision(next)};return stableValueEquals(current,next)?{raw:beforeRaw,result:receipt}:(next===void 0?delete document[section]:document[section]=next,{raw:`${JSON.stringify(document,null,2)}
|
|
2
|
+
`,result:{...receipt,changed:!0}})}});return mutation.changed&&await runActiveOnWriteHooks({path:settingsPath,scope:"project",op:SETTINGS_SECTION_WRITE_OP}),mutation.result}finally{clearSettingsReadCache(pmRoot)}}export{readSettingsSection,settingsSectionRevision,writeSettingsSection};
|
|
3
|
+
//# sourceMappingURL=settings-sections.js.map
|
|
@@ -172,6 +172,10 @@ success:true;
|
|
|
172
172
|
data:ParsedSettings;
|
|
173
173
|
} | {
|
|
174
174
|
success:false;
|
|
175
|
+
/** Dotted path of the first invalid setting; empty when the document root is not an object. */
|
|
176
|
+
path:string;
|
|
175
177
|
};
|
|
178
|
+
/** Top-level settings keys owned and validated by core; extension sections must use other names. */
|
|
179
|
+
export declare const CORE_SETTINGS_SECTION_KEYS:readonly string[];
|
|
176
180
|
/** Validate raw settings, returning stripped, type-checked data or failure (matching the legacy zod safeParse). */
|
|
177
181
|
export declare function validateSettings(raw:unknown):SettingsValidationResult;
|
|
@@ -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]="405e7bd1-25c0-5e04-b313-d9871efce4f5")}catch{}})();import{GOVERNANCE_PRESET_VALUES,RUNTIME_FIELD_COMMAND_VALUES,RUNTIME_FIELD_TYPE_VALUES,RUNTIME_STATUS_ROLE_VALUES,RUNTIME_UNKNOWN_FIELD_POLICY_VALUES}from"../../types.js";const FAIL={ok:!1},vString=input=>typeof input=="string"?{ok:!0,value:input}:FAIL,vBoolean=input=>typeof input=="boolean"?{ok:!0,value:input}:FAIL;function vNumber(options={}){return input=>typeof input!="number"||!Number.isFinite(input)||options.int&&(!Number.isInteger(input)||!Number.isSafeInteger(input))||options.positive&&input<=0||typeof options.min=="number"&&input<options.min||typeof options.max=="number"&&input>options.max?FAIL:{ok:!0,value:input}}function vLiteral(...allowed){return input=>typeof input=="string"&&allowed.includes(input)?{ok:!0,value:input}:FAIL}function vArray(item,maxLength){return input=>{if(!Array.isArray(input)||maxLength!==void 0&&input.length>maxLength)return FAIL;const value=[];for(const element of input){const result=item(element);if(!result.ok)return FAIL;value.push(result.value)}return{ok:!0,value}}}function vBoundedNormalizedString(maxLength,pattern){return input=>typeof input=="string"&&input.length>0&&input.length<=maxLength&&input===input.trim()&&(pattern===void 0||pattern.test(input))?{ok:!0,value:input}:FAIL}function vOptional(inner){return input=>input===void 0?{ok:!0,value:void 0}:inner(input)}function vRecordOf(valueCheck){return input=>{if(typeof input!="object"||input===null||Array.isArray(input))return FAIL;const value={};for(const[key,raw]of Object.entries(input)){const result=valueCheck(raw);if(!result.ok)return FAIL;value[key]=result.value}return{ok:!0,value}}}function vObject(shape){return input=>{if(typeof input!="object"||input===null||Array.isArray(input))return FAIL;const record=input,value={};for(const[key,check]of Object.entries(shape)){const result=check(record[key]);if(!result.ok)return FAIL;result.value!==void 0&&(value[key]=result.value)}return{ok:!0,value}}}const itemTypeOption=vObject({key:vString,values:vArray(vString),required:vOptional(vBoolean),aliases:vOptional(vArray(vString)),description:vOptional(vString)}),itemTypeCommandOptionPolicy=vObject({command:vLiteral("create","update"),option:vString,required:vOptional(vBoolean),visible:vOptional(vBoolean),enabled:vOptional(vBoolean)}),itemTypeDefinition=vObject({name:vString,description:vOptional(vString),default_status:vOptional(vString),execution_role:vOptional(vLiteral("agent","human","gate")),folder:vOptional(vString),aliases:vOptional(vArray(vString)),required_create_fields:vOptional(vArray(vString)),required_create_repeatables:vOptional(vArray(vString)),options:vOptional(vArray(itemTypeOption)),command_option_policies:vOptional(vArray(itemTypeCommandOptionPolicy))}),runtimeStatusDefinition=vObject({id:vString,aliases:vOptional(vArray(vString)),roles:vOptional(vArray(vLiteral(...RUNTIME_STATUS_ROLE_VALUES))),description:vOptional(vString),order:vOptional(vNumber())}),runtimeFieldDefinition=vObject({key:vString,metadata_key:vOptional(vString),front_matter_key:vOptional(vString),cli_flag:vOptional(vString),cli_aliases:vOptional(vArray(vString)),description:vOptional(vString),type:vOptional(vLiteral(...RUNTIME_FIELD_TYPE_VALUES)),commands:vOptional(vArray(vLiteral(...RUNTIME_FIELD_COMMAND_VALUES))),repeatable:vOptional(vBoolean),required:vOptional(vBoolean),required_on_create:vOptional(vBoolean),required_types:vOptional(vArray(vString)),allow_unset:vOptional(vBoolean)}),statusTransitionPair=input=>{if(!Array.isArray(input)||input.length!==2)return FAIL;const[from,to]=input;return typeof from!="string"||typeof to!="string"?FAIL:{ok:!0,value:[from,to]}},typeWorkflowDefinition=vObject({type:vString,allowed_transitions:vArray(statusTransitionPair)}),runtimeSchemaSettings=vOptional(vObject({version:vOptional(vNumber({int:!0})),files:vOptional(vObject({types:vOptional(vString),statuses:vOptional(vString),fields:vOptional(vString),workflows:vOptional(vString)})),statuses:vOptional(vArray(runtimeStatusDefinition)),fields:vOptional(vArray(runtimeFieldDefinition)),workflow:vOptional(vObject({draft_status:vOptional(vString),open_status:vOptional(vString),in_progress_status:vOptional(vString),blocked_status:vOptional(vString),close_status:vOptional(vString),canceled_status:vOptional(vString)})),type_workflows:vOptional(vArray(typeWorkflowDefinition)),unknown_field_policy:vOptional(vLiteral(...RUNTIME_UNKNOWN_FIELD_POLICY_VALUES))})),governanceSettings=vOptional(vObject({preset:vOptional(vLiteral(...GOVERNANCE_PRESET_VALUES)),ownership_enforcement:vOptional(vLiteral("none","warn","strict")),create_mode_default:vOptional(vLiteral("progressive","strict")),close_validation_default:vOptional(vLiteral("off","warn","strict")),require_close_reason:vOptional(vBoolean),parent_reference:vOptional(vLiteral("warn","strict_error")),metadata_profile:vOptional(vLiteral("core","strict","custom")),force_required_for_stale_lock:vOptional(vBoolean),create_default_type:vOptional(vString),workflow_enforcement:vOptional(vLiteral("off","warn","strict")),duplicate_detection_mode:vOptional(vLiteral("off","advisory","strict")),duplicate_detection_threshold:vOptional(vNumber({min:0,max:1})),duplicate_detection_limit:vOptional(vNumber({int:!0,min:0,max:20}))})),extensionPolicyOverride=vObject({name:vString,disabled:vOptional(vBoolean),require_trusted:vOptional(vBoolean),require_provenance:vOptional(vBoolean),sandbox_profile:vOptional(vLiteral("none","restricted","strict")),allowed_capabilities:vOptional(vArray(vString)),blocked_capabilities:vOptional(vArray(vString)),allowed_surfaces:vOptional(vArray(vString)),blocked_surfaces:vOptional(vArray(vString)),allowed_commands:vOptional(vArray(vString)),blocked_commands:vOptional(vArray(vString)),allowed_actions:vOptional(vArray(vString)),blocked_actions:vOptional(vArray(vString)),allowed_services:vOptional(vArray(vString)),blocked_services:vOptional(vArray(vString))}),pmMaxVersionExceededModeLiteral=vLiteral("block","warn"),pmMaxVersionExceededMode=input=>typeof input=="string"?pmMaxVersionExceededModeLiteral(input):vObject({global:vOptional(pmMaxVersionExceededModeLiteral),project:vOptional(pmMaxVersionExceededModeLiteral)})(input),extensionPolicy=vOptional(vObject({mode:vOptional(vLiteral("off","warn","enforce")),trust_mode:vOptional(vLiteral("off","warn","enforce")),pm_max_version_exceeded_mode:vOptional(pmMaxVersionExceededMode),require_provenance:vOptional(vBoolean),trusted_extensions:vOptional(vArray(vString)),default_sandbox_profile:vOptional(vLiteral("none","restricted","strict")),allowed_extensions:vOptional(vArray(vString)),blocked_extensions:vOptional(vArray(vString)),allowed_capabilities:vOptional(vArray(vString)),blocked_capabilities:vOptional(vArray(vString)),allowed_surfaces:vOptional(vArray(vString)),blocked_surfaces:vOptional(vArray(vString)),allowed_commands:vOptional(vArray(vString)),blocked_commands:vOptional(vArray(vString)),allowed_actions:vOptional(vArray(vString)),blocked_actions:vOptional(vArray(vString)),allowed_services:vOptional(vArray(vString)),blocked_services:vOptional(vArray(vString)),extension_overrides:vOptional(vArray(extensionPolicyOverride))})),harnessSignalLiteral=vBoundedNormalizedString(128),harnessSignalDescriptor=vObject({harness:vBoundedNormalizedString(128,/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),environment_keys:vOptional(vArray(harnessSignalLiteral,64)),model_environment_keys:vOptional(vArray(harnessSignalLiteral,64)),session_environment_keys:vOptional(vArray(harnessSignalLiteral,64)),provenance_environment_keys:vOptional(vRecordOf(vArray(harnessSignalLiteral,64))),provenance_resolvers:vOptional(vRecordOf(vLiteral("ai_agent_version","claude_session_file"))),provenance_unavailable_dimensions:vOptional(vArray(harnessSignalLiteral,64)),argv_markers:vOptional(vArray(harnessSignalLiteral,64)),client_names:vOptional(vArray(harnessSignalLiteral,64))}),settingsCheck=vObject({version:vNumber({int:!0}),id_prefix:vString,ids:vOptional(vObject({token_length:vOptional(vNumber({int:!0,min:4,max:12}))})),author_default:vString,agent_identity:vOptional(vObject({probes_enabled:vOptional(vBoolean),identity_vocabulary:vOptional(vObject({version:vNumber({int:!0,positive:!0}),aliases:vRecordOf(vBoundedNormalizedString(128,/^[a-z0-9]+(?:-[a-z0-9]+)*$/u))})),harness_signals:vArray(harnessSignalDescriptor)})),mutation_guard:vOptional(vObject({require_attributed_author:vOptional(vBoolean),secret_guard:vOptional(vLiteral("off","advise","block")),stale_in_progress_hours:vOptional(vNumber({int:!0,positive:!0}))})),item_format:vOptional(vLiteral("toon","json_markdown")),locks:vObject({ttl_seconds:vNumber({int:!0}),wait_ms:vOptional(vNumber({int:!0,min:0}))}),checkpoints:vOptional(vObject({retention_days:vOptional(vNumber({int:!0,positive:!0}))})),output:vObject({default_format:vLiteral("toon","json")}),ux:vOptional(vObject({deprecation_hints:vOptional(vBoolean)})),history:vOptional(vObject({missing_stream:vLiteral("auto_create","strict_error"),compact_policy:vOptional(vObject({enabled:vOptional(vBoolean),max_entries:vOptional(vNumber({int:!0,positive:!0})),trigger:vOptional(vLiteral("health_warn","auto"))}))})),validation:vOptional(vObject({sprint_release_format:vLiteral("warn","strict_error"),parent_reference:vOptional(vLiteral("warn","strict_error")),metadata_profile:vOptional(vLiteral("core","strict","custom")),metadata_required_fields:vOptional(vArray(vString)),lifecycle_stale_blocker_reason_patterns:vOptional(vArray(vString)),lifecycle_closure_like_blocked_reason_patterns:vOptional(vArray(vString)),lifecycle_closure_like_resolution_patterns:vOptional(vArray(vString)),lifecycle_closure_like_actual_result_patterns:vOptional(vArray(vString)),estimate_defaults_by_type:vOptional(vRecordOf(vNumber({int:!0,positive:!0})))})),governance:governanceSettings,workflow:vOptional(vObject({definition_of_done:vArray(vString)})),testing:vOptional(vObject({record_results_to_items:vBoolean,allow_untrusted_linked_tests:vOptional(vBoolean)})),telemetry:vOptional(vObject({enabled:vBoolean,first_run_prompt_completed:vOptional(vBoolean),capture_level:vOptional(vLiteral("minimal","redacted","max")),endpoint:vOptional(vString),installation_id:vOptional(vString),retention_days:vOptional(vNumber({int:!0,positive:!0}))})),agent_guidance:vOptional(vObject({prompt_completed:vOptional(vBoolean),declined:vOptional(vBoolean),declined_at:vOptional(vString),template_version:vOptional(vNumber({int:!0,positive:!0})),last_checked_files:vOptional(vArray(vString))})),item_types:vOptional(vObject({definitions:vArray(itemTypeDefinition)})),schema:runtimeSchemaSettings,context:vOptional(vObject({default_depth:vOptional(vLiteral("brief","standard","deep")),activity_limit:vOptional(vNumber({int:!0,positive:!0})),stale_threshold_days:vOptional(vNumber({int:!0,positive:!0})),next_ranking_policy:vOptional(vLiteral("priority_floor","weighted")),sections:vOptional(vObject({hierarchy:vOptional(vBoolean),activity:vOptional(vBoolean),progress:vOptional(vBoolean),recently_created:vOptional(vBoolean),unparented:vOptional(vBoolean),blockers:vOptional(vBoolean),files:vOptional(vBoolean),workload:vOptional(vBoolean),staleness:vOptional(vBoolean),tests:vOptional(vBoolean)}))})),extensions:vObject({enabled:vArray(vString),disabled:vArray(vString),policy:extensionPolicy}),search:vObject({score_threshold:vNumber(),hybrid_semantic_weight:vOptional(vNumber()),max_results:vNumber({int:!0}),embedding_model:vString,embedding_corpus_max_characters:vOptional(vNumber()),embedding_batch_size:vNumber({int:!0}),embedding_timeout_ms:vOptional(vNumber({int:!0})),scanner_max_batch_retries:vNumber({int:!0}),provider:vOptional(vString),default_mode:vOptional(vLiteral("auto","keyword","semantic","hybrid")),corpus_fields:vOptional(vArray(vString)),mutation_refresh_policy:vOptional(vLiteral("cache_only","semantic_configured","semantic_auto")),query_expansion:vOptional(vObject({enabled:vOptional(vBoolean),provider:vOptional(vString)})),rerank:vOptional(vObject({enabled:vOptional(vBoolean),model:vOptional(vString),top_k:vOptional(vNumber({int:!0,positive:!0}))})),bm25:vOptional(vObject({k1:vOptional(vNumber()),b:vOptional(vNumber())}))}),providers:vObject({openai:vObject({base_url:vString,api_key:vString,model:vString}),ollama:vObject({base_url:vString,model:vString})}),vector_store:vObject({adapter:vOptional(vString),collection_name:vOptional(vString),qdrant:vObject({url:vString,api_key:vString}),lancedb:vObject({path:vString})})});function validateSettings(raw){const result=settingsCheck(raw);return result.ok?{success:!0,data:result.value}:{success:!1}}export{validateSettings};
|
|
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]="c7f9d249-e2c2-5edd-afbd-b14ac1c12e81")}catch{}})();import{GOVERNANCE_PRESET_VALUES,RUNTIME_FIELD_COMMAND_VALUES,RUNTIME_FIELD_TYPE_VALUES,RUNTIME_STATUS_ROLE_VALUES,RUNTIME_UNKNOWN_FIELD_POLICY_VALUES}from"../../types.js";const FAIL={ok:!1,path:[]};function failAt(segment,failure){return{ok:!1,path:[segment,...failure.path]}}const vString=input=>typeof input=="string"?{ok:!0,value:input}:FAIL,vBoolean=input=>typeof input=="boolean"?{ok:!0,value:input}:FAIL;function vNumber(options={}){return input=>typeof input!="number"||!Number.isFinite(input)||options.int&&(!Number.isInteger(input)||!Number.isSafeInteger(input))||options.positive&&input<=0||typeof options.min=="number"&&input<options.min||typeof options.max=="number"&&input>options.max?FAIL:{ok:!0,value:input}}function vLiteral(...allowed){return input=>typeof input=="string"&&allowed.includes(input)?{ok:!0,value:input}:FAIL}function vArray(item,maxLength){return input=>{if(!Array.isArray(input)||maxLength!==void 0&&input.length>maxLength)return FAIL;const value=[];for(const[index,element]of input.entries()){const result=item(element);if(!result.ok)return failAt(String(index),result);value.push(result.value)}return{ok:!0,value}}}function vBoundedNormalizedString(maxLength,pattern){return input=>typeof input=="string"&&input.length>0&&input.length<=maxLength&&input===input.trim()&&(pattern===void 0||pattern.test(input))?{ok:!0,value:input}:FAIL}function vOptional(inner){return input=>input===void 0?{ok:!0,value:void 0}:inner(input)}function vRecordOf(valueCheck){return input=>{if(typeof input!="object"||input===null||Array.isArray(input))return FAIL;const value={};for(const[key,raw]of Object.entries(input)){const result=valueCheck(raw);if(!result.ok)return failAt(key,result);value[key]=result.value}return{ok:!0,value}}}function vObject(shape){return input=>{if(typeof input!="object"||input===null||Array.isArray(input))return FAIL;const record=input,value={};for(const[key,check]of Object.entries(shape)){const result=check(record[key]);if(!result.ok)return failAt(key,result);result.value!==void 0&&(value[key]=result.value)}return{ok:!0,value}}}const itemTypeOption=vObject({key:vString,values:vArray(vString),required:vOptional(vBoolean),aliases:vOptional(vArray(vString)),description:vOptional(vString)}),itemTypeCommandOptionPolicy=vObject({command:vLiteral("create","update"),option:vString,required:vOptional(vBoolean),visible:vOptional(vBoolean),enabled:vOptional(vBoolean)}),itemTypeDefinition=vObject({name:vString,description:vOptional(vString),default_status:vOptional(vString),execution_role:vOptional(vLiteral("agent","human","gate")),folder:vOptional(vString),aliases:vOptional(vArray(vString)),required_create_fields:vOptional(vArray(vString)),required_create_repeatables:vOptional(vArray(vString)),options:vOptional(vArray(itemTypeOption)),command_option_policies:vOptional(vArray(itemTypeCommandOptionPolicy))}),runtimeStatusDefinition=vObject({id:vString,aliases:vOptional(vArray(vString)),roles:vOptional(vArray(vLiteral(...RUNTIME_STATUS_ROLE_VALUES))),description:vOptional(vString),order:vOptional(vNumber())}),runtimeFieldDefinition=vObject({key:vString,metadata_key:vOptional(vString),front_matter_key:vOptional(vString),cli_flag:vOptional(vString),cli_aliases:vOptional(vArray(vString)),description:vOptional(vString),type:vOptional(vLiteral(...RUNTIME_FIELD_TYPE_VALUES)),commands:vOptional(vArray(vLiteral(...RUNTIME_FIELD_COMMAND_VALUES))),repeatable:vOptional(vBoolean),required:vOptional(vBoolean),required_on_create:vOptional(vBoolean),required_types:vOptional(vArray(vString)),allow_unset:vOptional(vBoolean)}),statusTransitionPair=input=>{if(!Array.isArray(input)||input.length!==2)return FAIL;const[from,to]=input;return typeof from!="string"||typeof to!="string"?FAIL:{ok:!0,value:[from,to]}},typeWorkflowDefinition=vObject({type:vString,allowed_transitions:vArray(statusTransitionPair)}),runtimeSchemaSettings=vOptional(vObject({version:vOptional(vNumber({int:!0})),files:vOptional(vObject({types:vOptional(vString),statuses:vOptional(vString),fields:vOptional(vString),workflows:vOptional(vString)})),statuses:vOptional(vArray(runtimeStatusDefinition)),fields:vOptional(vArray(runtimeFieldDefinition)),workflow:vOptional(vObject({draft_status:vOptional(vString),open_status:vOptional(vString),in_progress_status:vOptional(vString),blocked_status:vOptional(vString),close_status:vOptional(vString),canceled_status:vOptional(vString)})),type_workflows:vOptional(vArray(typeWorkflowDefinition)),unknown_field_policy:vOptional(vLiteral(...RUNTIME_UNKNOWN_FIELD_POLICY_VALUES))})),governanceSettings=vOptional(vObject({preset:vOptional(vLiteral(...GOVERNANCE_PRESET_VALUES)),ownership_enforcement:vOptional(vLiteral("none","warn","strict")),create_mode_default:vOptional(vLiteral("progressive","strict")),close_validation_default:vOptional(vLiteral("off","warn","strict")),require_close_reason:vOptional(vBoolean),parent_reference:vOptional(vLiteral("warn","strict_error")),metadata_profile:vOptional(vLiteral("core","strict","custom")),force_required_for_stale_lock:vOptional(vBoolean),create_default_type:vOptional(vString),workflow_enforcement:vOptional(vLiteral("off","warn","strict")),duplicate_detection_mode:vOptional(vLiteral("off","advisory","strict")),duplicate_detection_threshold:vOptional(vNumber({min:0,max:1})),duplicate_detection_limit:vOptional(vNumber({int:!0,min:0,max:20}))})),extensionPolicyOverride=vObject({name:vString,disabled:vOptional(vBoolean),require_trusted:vOptional(vBoolean),require_provenance:vOptional(vBoolean),sandbox_profile:vOptional(vLiteral("none","restricted","strict")),allowed_capabilities:vOptional(vArray(vString)),blocked_capabilities:vOptional(vArray(vString)),allowed_surfaces:vOptional(vArray(vString)),blocked_surfaces:vOptional(vArray(vString)),allowed_commands:vOptional(vArray(vString)),blocked_commands:vOptional(vArray(vString)),allowed_actions:vOptional(vArray(vString)),blocked_actions:vOptional(vArray(vString)),allowed_services:vOptional(vArray(vString)),blocked_services:vOptional(vArray(vString))}),pmMaxVersionExceededModeLiteral=vLiteral("block","warn"),pmMaxVersionExceededMode=input=>typeof input=="string"?pmMaxVersionExceededModeLiteral(input):vObject({global:vOptional(pmMaxVersionExceededModeLiteral),project:vOptional(pmMaxVersionExceededModeLiteral)})(input),extensionPolicy=vOptional(vObject({mode:vOptional(vLiteral("off","warn","enforce")),trust_mode:vOptional(vLiteral("off","warn","enforce")),pm_max_version_exceeded_mode:vOptional(pmMaxVersionExceededMode),require_provenance:vOptional(vBoolean),trusted_extensions:vOptional(vArray(vString)),default_sandbox_profile:vOptional(vLiteral("none","restricted","strict")),allowed_extensions:vOptional(vArray(vString)),blocked_extensions:vOptional(vArray(vString)),allowed_capabilities:vOptional(vArray(vString)),blocked_capabilities:vOptional(vArray(vString)),allowed_surfaces:vOptional(vArray(vString)),blocked_surfaces:vOptional(vArray(vString)),allowed_commands:vOptional(vArray(vString)),blocked_commands:vOptional(vArray(vString)),allowed_actions:vOptional(vArray(vString)),blocked_actions:vOptional(vArray(vString)),allowed_services:vOptional(vArray(vString)),blocked_services:vOptional(vArray(vString)),extension_overrides:vOptional(vArray(extensionPolicyOverride))})),harnessSignalLiteral=vBoundedNormalizedString(128),harnessSignalDescriptor=vObject({harness:vBoundedNormalizedString(128,/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),environment_keys:vOptional(vArray(harnessSignalLiteral,64)),model_environment_keys:vOptional(vArray(harnessSignalLiteral,64)),session_environment_keys:vOptional(vArray(harnessSignalLiteral,64)),provenance_environment_keys:vOptional(vRecordOf(vArray(harnessSignalLiteral,64))),provenance_resolvers:vOptional(vRecordOf(vLiteral("ai_agent_version","claude_session_file"))),provenance_unavailable_dimensions:vOptional(vArray(harnessSignalLiteral,64)),argv_markers:vOptional(vArray(harnessSignalLiteral,64)),client_names:vOptional(vArray(harnessSignalLiteral,64))}),SETTINGS_SHAPE={version:vNumber({int:!0}),id_prefix:vString,ids:vOptional(vObject({token_length:vOptional(vNumber({int:!0,min:4,max:12}))})),author_default:vString,agent_identity:vOptional(vObject({probes_enabled:vOptional(vBoolean),identity_vocabulary:vOptional(vObject({version:vNumber({int:!0,positive:!0}),aliases:vRecordOf(vBoundedNormalizedString(128,/^[a-z0-9]+(?:-[a-z0-9]+)*$/u))})),harness_signals:vArray(harnessSignalDescriptor)})),mutation_guard:vOptional(vObject({require_attributed_author:vOptional(vBoolean),secret_guard:vOptional(vLiteral("off","advise","block")),stale_in_progress_hours:vOptional(vNumber({int:!0,positive:!0}))})),item_format:vOptional(vLiteral("toon","json_markdown")),locks:vObject({ttl_seconds:vNumber({int:!0}),wait_ms:vOptional(vNumber({int:!0,min:0}))}),checkpoints:vOptional(vObject({retention_days:vOptional(vNumber({int:!0,positive:!0}))})),output:vObject({default_format:vLiteral("toon","json")}),ux:vOptional(vObject({deprecation_hints:vOptional(vBoolean)})),history:vOptional(vObject({missing_stream:vLiteral("auto_create","strict_error"),compact_policy:vOptional(vObject({enabled:vOptional(vBoolean),max_entries:vOptional(vNumber({int:!0,positive:!0})),trigger:vOptional(vLiteral("health_warn","auto"))}))})),validation:vOptional(vObject({sprint_release_format:vLiteral("warn","strict_error"),parent_reference:vOptional(vLiteral("warn","strict_error")),metadata_profile:vOptional(vLiteral("core","strict","custom")),metadata_required_fields:vOptional(vArray(vString)),lifecycle_stale_blocker_reason_patterns:vOptional(vArray(vString)),lifecycle_closure_like_blocked_reason_patterns:vOptional(vArray(vString)),lifecycle_closure_like_resolution_patterns:vOptional(vArray(vString)),lifecycle_closure_like_actual_result_patterns:vOptional(vArray(vString)),estimate_defaults_by_type:vOptional(vRecordOf(vNumber({int:!0,positive:!0})))})),governance:governanceSettings,workflow:vOptional(vObject({definition_of_done:vArray(vString)})),testing:vOptional(vObject({record_results_to_items:vBoolean,allow_untrusted_linked_tests:vOptional(vBoolean)})),telemetry:vOptional(vObject({enabled:vBoolean,first_run_prompt_completed:vOptional(vBoolean),capture_level:vOptional(vLiteral("minimal","redacted","max")),endpoint:vOptional(vString),installation_id:vOptional(vString),retention_days:vOptional(vNumber({int:!0,positive:!0}))})),agent_guidance:vOptional(vObject({prompt_completed:vOptional(vBoolean),declined:vOptional(vBoolean),declined_at:vOptional(vString),template_version:vOptional(vNumber({int:!0,positive:!0})),last_checked_files:vOptional(vArray(vString))})),item_types:vOptional(vObject({definitions:vArray(itemTypeDefinition)})),schema:runtimeSchemaSettings,context:vOptional(vObject({default_depth:vOptional(vLiteral("brief","standard","deep")),activity_limit:vOptional(vNumber({int:!0,positive:!0})),stale_threshold_days:vOptional(vNumber({int:!0,positive:!0})),next_ranking_policy:vOptional(vLiteral("priority_floor","weighted")),sections:vOptional(vObject({hierarchy:vOptional(vBoolean),activity:vOptional(vBoolean),progress:vOptional(vBoolean),recently_created:vOptional(vBoolean),unparented:vOptional(vBoolean),blockers:vOptional(vBoolean),files:vOptional(vBoolean),workload:vOptional(vBoolean),staleness:vOptional(vBoolean),tests:vOptional(vBoolean)}))})),extensions:vObject({enabled:vArray(vString),disabled:vArray(vString),policy:extensionPolicy}),search:vObject({score_threshold:vNumber(),hybrid_semantic_weight:vOptional(vNumber()),max_results:vNumber({int:!0}),embedding_model:vString,embedding_corpus_max_characters:vOptional(vNumber()),embedding_batch_size:vNumber({int:!0}),embedding_timeout_ms:vOptional(vNumber({int:!0})),scanner_max_batch_retries:vNumber({int:!0}),provider:vOptional(vString),default_mode:vOptional(vLiteral("auto","keyword","semantic","hybrid")),corpus_fields:vOptional(vArray(vString)),mutation_refresh_policy:vOptional(vLiteral("cache_only","semantic_configured","semantic_auto")),query_expansion:vOptional(vObject({enabled:vOptional(vBoolean),provider:vOptional(vString)})),rerank:vOptional(vObject({enabled:vOptional(vBoolean),model:vOptional(vString),top_k:vOptional(vNumber({int:!0,positive:!0}))})),bm25:vOptional(vObject({k1:vOptional(vNumber()),b:vOptional(vNumber())}))}),providers:vObject({openai:vObject({base_url:vString,api_key:vString,model:vString}),ollama:vObject({base_url:vString,model:vString})}),vector_store:vObject({adapter:vOptional(vString),collection_name:vOptional(vString),qdrant:vObject({url:vString,api_key:vString}),lancedb:vObject({path:vString})})},settingsCheck=vObject(SETTINGS_SHAPE),CORE_SETTINGS_SECTION_KEYS=Object.freeze(Object.keys(SETTINGS_SHAPE));function validateSettings(raw){const result=settingsCheck(raw);return result.ok?{success:!0,data:result.value}:{success:!1,path:result.path.join(".")}}export{CORE_SETTINGS_SECTION_KEYS,validateSettings};
|
|
2
2
|
//# sourceMappingURL=settings-validator.js.map
|