safeword 0.78.2 → 0.78.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks.json +5 -5
- package/codex-plugin/skills/bdd/references/SCENARIOS.md +3 -0
- package/codex-plugin/skills/bdd/references/TDD.md +11 -5
- package/codex-plugin/skills/closeout/SKILL.md +24 -16
- package/codex-plugin/skills/finish-review/SKILL.md +4 -3
- package/codex-plugin/skills/lint/SKILL.md +1 -1
- package/codex-plugin/skills/review-spec/SKILL.md +5 -1
- package/codex-plugin/skills/self-review/SKILL.md +2 -2
- package/codex-plugin/skills/ticket-system/SKILL.md +3 -0
- package/dist/{architecture-N5ZC4CDU.js → architecture-RQ377ZER.js} +6 -6
- package/dist/{architecture-document-UBA5UT4J.js → architecture-document-KS3CLSYN.js} +6 -6
- package/dist/{architecture-monorepo-E4JUYJGV.js → architecture-monorepo-NJWWU65G.js} +4 -4
- package/dist/{boundary-HLJILY7R.js → boundary-OPTC2RVJ.js} +6 -6
- package/dist/{chunk-NOTBZCKG.js → chunk-2XR7J5WL.js} +93 -42
- package/dist/chunk-2XR7J5WL.js.map +1 -0
- package/dist/{chunk-4CZS4YJV.js → chunk-3OKZ3X4O.js} +3 -3
- package/dist/{chunk-J7UDLLTK.js → chunk-76XHADUG.js} +39 -25
- package/dist/chunk-76XHADUG.js.map +1 -0
- package/dist/{chunk-4CKCRZDO.js → chunk-7ULZND56.js} +2 -2
- package/dist/{chunk-34NCOTP5.js → chunk-7XELS325.js} +2 -2
- package/dist/{chunk-USPVNZ63.js → chunk-BZWO3BVD.js} +11 -7
- package/dist/chunk-BZWO3BVD.js.map +1 -0
- package/dist/{chunk-GZ36UZJG.js → chunk-CFME47IY.js} +6 -6
- package/dist/{chunk-SME5SU3O.js → chunk-CNBZ3S5U.js} +255 -45
- package/dist/chunk-CNBZ3S5U.js.map +1 -0
- package/dist/{chunk-AV5LTEME.js → chunk-CRGPYC35.js} +8 -6
- package/dist/{chunk-AV5LTEME.js.map → chunk-CRGPYC35.js.map} +1 -1
- package/dist/{chunk-BQTC6YGN.js → chunk-D5ENZR6N.js} +2 -2
- package/dist/{chunk-V63TSN3C.js → chunk-FOYGZ4MK.js} +31 -10
- package/dist/chunk-FOYGZ4MK.js.map +1 -0
- package/dist/{chunk-ZRV7TOCX.js → chunk-G5AFQXXW.js} +2 -2
- package/dist/{chunk-S2T356DA.js → chunk-HF3DXZEA.js} +7 -10
- package/dist/chunk-HF3DXZEA.js.map +1 -0
- package/dist/{chunk-NSZGI7HK.js → chunk-HNCUIXDY.js} +9 -9
- package/dist/{chunk-NSZGI7HK.js.map → chunk-HNCUIXDY.js.map} +1 -1
- package/dist/{chunk-KXHVFCDT.js → chunk-IDRLACDS.js} +145 -19
- package/dist/chunk-IDRLACDS.js.map +1 -0
- package/dist/{chunk-BSOUMHMQ.js → chunk-IPTM5VE7.js} +101 -1094
- package/dist/chunk-IPTM5VE7.js.map +1 -0
- package/dist/chunk-JBOLNV2L.js +21 -0
- package/dist/chunk-JBOLNV2L.js.map +1 -0
- package/dist/chunk-JSR3HXFT.js +1231 -0
- package/dist/chunk-JSR3HXFT.js.map +1 -0
- package/dist/{chunk-I4KBHRZW.js → chunk-LH2FAQTI.js} +4 -4
- package/dist/{chunk-WVDRXD67.js → chunk-MLBZFB5A.js} +1 -1
- package/dist/chunk-MLBZFB5A.js.map +1 -0
- package/dist/{chunk-KFVEPQ7X.js → chunk-MVFHTUQ3.js} +2 -2
- package/dist/{chunk-BAVDDAN3.js → chunk-MZKTOX6X.js} +7 -7
- package/dist/{chunk-TJ627SKI.js → chunk-O3GZ77F7.js} +15 -1
- package/dist/chunk-O3GZ77F7.js.map +1 -0
- package/dist/{chunk-Q5KLE6ZF.js → chunk-OKPRQGFG.js} +2 -2
- package/dist/{chunk-QUDIOBHP.js → chunk-PD7ZXRHV.js} +139 -18
- package/dist/chunk-PD7ZXRHV.js.map +1 -0
- package/dist/chunk-QAHJSG53.js +30 -0
- package/dist/chunk-QAHJSG53.js.map +1 -0
- package/dist/{chunk-QTG2EXFT.js → chunk-QLOUSTQE.js} +2 -2
- package/dist/{chunk-QTG2EXFT.js.map → chunk-QLOUSTQE.js.map} +1 -1
- package/dist/{chunk-3SOJBMM6.js → chunk-R6BSRNU4.js} +6 -6
- package/dist/{chunk-3SOJBMM6.js.map → chunk-R6BSRNU4.js.map} +1 -1
- package/dist/{chunk-664YNAMR.js → chunk-RTFPKJJV.js} +5 -5
- package/dist/{chunk-GKR44BCH.js → chunk-RVDXBHUG.js} +2 -2
- package/dist/chunk-THLEVFEX.js +22 -0
- package/dist/chunk-THLEVFEX.js.map +1 -0
- package/dist/{chunk-A7Z27COA.js → chunk-VFWAZLEF.js} +3 -3
- package/dist/{chunk-3EJNDIAO.js → chunk-VZ5B2SIO.js} +2 -2
- package/dist/chunk-WAMQTZAH.js +19 -0
- package/dist/chunk-WAMQTZAH.js.map +1 -0
- package/dist/{chunk-FDHJ63FZ.js → chunk-WVA4Q6BX.js} +1 -1
- package/dist/{chunk-FDHJ63FZ.js.map → chunk-WVA4Q6BX.js.map} +1 -1
- package/dist/{cleanup-X556LNWM.js → cleanup-ELVHZPRC.js} +9 -9
- package/dist/{cleanup-command-RV2Q7G72.js → cleanup-command-5BY4FWAT.js} +12 -12
- package/dist/cli.js +143 -108
- package/dist/cli.js.map +1 -1
- package/dist/{clients-HP23HRN2.js → clients-WRPBRO6V.js} +2 -2
- package/dist/{codex-bootstrap-MGY2D6HU.js → codex-bootstrap-GYG7N4XU.js} +77 -34
- package/dist/codex-bootstrap-GYG7N4XU.js.map +1 -0
- package/dist/{codex-hook-IPX3PDXX.js → codex-hook-7ILIPLYM.js} +56 -48
- package/dist/codex-hook-7ILIPLYM.js.map +1 -0
- package/dist/{codify-J5ZLJ3HK.js → codify-HSDVRZFI.js} +11 -8
- package/dist/codify-HSDVRZFI.js.map +1 -0
- package/dist/{commands-L4AINARC.js → commands-X4KBCMHS.js} +115 -60
- package/dist/commands-X4KBCMHS.js.map +1 -0
- package/dist/{config-LUSJAK6B.js → config-TXJRCN3W.js} +2 -2
- package/dist/{configured-paths-SPQ5XWOM.js → configured-paths-4CRHLBIF.js} +4 -4
- package/dist/contract-B7NDKO5B.js +8 -0
- package/dist/{coordinator-SA75UB3C.js → coordinator-ZMD3467F.js} +231 -144
- package/dist/coordinator-ZMD3467F.js.map +1 -0
- package/dist/corpus-PUBXCCSN.js +15 -0
- package/dist/{doctor-DSC36R4U.js → doctor-RPXETRSJ.js} +18 -18
- package/dist/{feature-directories-VOHVV26X.js → feature-directories-ASNDEM7J.js} +6 -6
- package/dist/{finalization-7SMLMVLV.js → finalization-BGPOEBAL.js} +2 -2
- package/dist/{gh-cli-ZMO7D2KE.js → gh-cli-OTX7PORT.js} +2 -2
- package/dist/{github-rest-KGKS4V2H.js → github-rest-JCW32377.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{job-CDADLJDF.js → job-NXXG4U5K.js} +20 -21
- package/dist/job-NXXG4U5K.js.map +1 -0
- package/dist/{learning-sync-6BTHM6ZC.js → learning-sync-2WIAOTW3.js} +4 -4
- package/dist/{legacy-global-guidance-2JL4Y5EB.js → legacy-global-guidance-WTDI53H7.js} +5 -5
- package/dist/{lint-gherkin-COSHVS6Z.js → lint-gherkin-LS3CHW3J.js} +7 -7
- package/dist/{operations-XVRQSLJG.js → operations-OZKEQCS4.js} +12 -12
- package/dist/{output-3LB2OEHN.js → output-ZYTEW6SC.js} +2 -2
- package/dist/{packet-5WT5M3OS.js → packet-7S6CZJQR.js} +2 -2
- package/dist/presets/typescript/index.js +2 -2
- package/dist/{profile-UMKQ5JVO.js → profile-SDFSBKH5.js} +7 -7
- package/dist/{prompt-GWYDFMLO.js → prompt-LYPODDC3.js} +2 -2
- package/dist/remove-P5JQNS5P.js +23 -0
- package/dist/{retro-IFSVU5KS.js → retro-EQK2Z2Z6.js} +8 -8
- package/dist/{retro-draft-spool-4S3TCHWL.js → retro-draft-spool-MDPBVM6J.js} +2 -2
- package/dist/{retro-extract-OSBFAXLU.js → retro-extract-CZMDD4WB.js} +2 -2
- package/dist/{review-pr-6S2352CK.js → review-pr-CWRD446M.js} +2 -2
- package/dist/{review-pr-publication-CGHCRRUI.js → review-pr-publication-AIX2U3VM.js} +2 -2
- package/dist/{run-5JESKXUR.js → run-T45X6Z6C.js} +4 -4
- package/dist/{self-report-VPHBPPSJ.js → self-report-V7PHKTS6.js} +2 -2
- package/dist/status-F5TFEIZ6.js +21 -0
- package/dist/{status-EX4L3ELO.js → status-S24PDFXB.js} +18 -18
- package/dist/{sync-config-O7J7IDGE.js → sync-config-EUVBPUUG.js} +5 -5
- package/dist/{sync-tracker-U2GYOFUO.js → sync-tracker-5FSIIAMS.js} +6 -6
- package/dist/{test-execution-6LIPMBIH.js → test-execution-HASVO6EL.js} +8 -7
- package/dist/{test-execution-6LIPMBIH.js.map → test-execution-HASVO6EL.js.map} +1 -1
- package/dist/{test-plan-VASHZXJ7.js → test-plan-EM3PVGIX.js} +7 -6
- package/dist/{test-plan-VASHZXJ7.js.map → test-plan-EM3PVGIX.js.map} +1 -1
- package/dist/{ticket-new-3YXDIWPC.js → ticket-new-H6ZNIRS4.js} +11 -11
- package/dist/ticket-new-H6ZNIRS4.js.map +1 -0
- package/dist/{ticket-sync-LLUBXZ2R.js → ticket-sync-7QRH6PVT.js} +5 -5
- package/dist/{tracker-map-HR4MBYG5.js → tracker-map-7AGCTWBZ.js} +2 -2
- package/dist/{tracker-sync-YDICZN66.js → tracker-sync-MLVYGFQJ.js} +2 -2
- package/package.json +1 -1
- package/templates/SAFEWORD.md +2 -2
- package/templates/commands/lint.md +1 -1
- package/templates/scripts/closeout-cleanup.ts +127 -31
- package/templates/skills/bdd/SCENARIOS.md +3 -0
- package/templates/skills/bdd/TDD.md +11 -5
- package/templates/skills/closeout/SKILL.md +25 -17
- package/templates/skills/finish-review/SKILL.md +4 -3
- package/templates/skills/lint/SKILL.md +1 -1
- package/templates/skills/review-spec/SKILL.md +5 -1
- package/templates/skills/self-review/SKILL.md +2 -2
- package/templates/skills/ticket-system/SKILL.md +3 -0
- package/dist/chunk-6S7CTKGC.js +0 -128
- package/dist/chunk-6S7CTKGC.js.map +0 -1
- package/dist/chunk-BSOUMHMQ.js.map +0 -1
- package/dist/chunk-J7UDLLTK.js.map +0 -1
- package/dist/chunk-KXHVFCDT.js.map +0 -1
- package/dist/chunk-NOTBZCKG.js.map +0 -1
- package/dist/chunk-QUDIOBHP.js.map +0 -1
- package/dist/chunk-S2T356DA.js.map +0 -1
- package/dist/chunk-SME5SU3O.js.map +0 -1
- package/dist/chunk-TJ627SKI.js.map +0 -1
- package/dist/chunk-USPVNZ63.js.map +0 -1
- package/dist/chunk-V63TSN3C.js.map +0 -1
- package/dist/chunk-WRUXO3WA.js +0 -48
- package/dist/chunk-WRUXO3WA.js.map +0 -1
- package/dist/chunk-WVDRXD67.js.map +0 -1
- package/dist/codex-bootstrap-MGY2D6HU.js.map +0 -1
- package/dist/codex-hook-IPX3PDXX.js.map +0 -1
- package/dist/codify-J5ZLJ3HK.js.map +0 -1
- package/dist/commands-L4AINARC.js.map +0 -1
- package/dist/contract-KEZY6YC3.js +0 -8
- package/dist/coordinator-SA75UB3C.js.map +0 -1
- package/dist/corpus-PPOBTF4J.js +0 -15
- package/dist/job-CDADLJDF.js.map +0 -1
- package/dist/remove-E6HGNHOU.js +0 -22
- package/dist/status-7VIGNTUI.js +0 -21
- package/dist/ticket-new-3YXDIWPC.js.map +0 -1
- /package/dist/{architecture-N5ZC4CDU.js.map → architecture-RQ377ZER.js.map} +0 -0
- /package/dist/{architecture-document-UBA5UT4J.js.map → architecture-document-KS3CLSYN.js.map} +0 -0
- /package/dist/{architecture-monorepo-E4JUYJGV.js.map → architecture-monorepo-NJWWU65G.js.map} +0 -0
- /package/dist/{boundary-HLJILY7R.js.map → boundary-OPTC2RVJ.js.map} +0 -0
- /package/dist/{chunk-4CZS4YJV.js.map → chunk-3OKZ3X4O.js.map} +0 -0
- /package/dist/{chunk-4CKCRZDO.js.map → chunk-7ULZND56.js.map} +0 -0
- /package/dist/{chunk-34NCOTP5.js.map → chunk-7XELS325.js.map} +0 -0
- /package/dist/{chunk-GZ36UZJG.js.map → chunk-CFME47IY.js.map} +0 -0
- /package/dist/{chunk-BQTC6YGN.js.map → chunk-D5ENZR6N.js.map} +0 -0
- /package/dist/{chunk-ZRV7TOCX.js.map → chunk-G5AFQXXW.js.map} +0 -0
- /package/dist/{chunk-I4KBHRZW.js.map → chunk-LH2FAQTI.js.map} +0 -0
- /package/dist/{chunk-KFVEPQ7X.js.map → chunk-MVFHTUQ3.js.map} +0 -0
- /package/dist/{chunk-BAVDDAN3.js.map → chunk-MZKTOX6X.js.map} +0 -0
- /package/dist/{chunk-Q5KLE6ZF.js.map → chunk-OKPRQGFG.js.map} +0 -0
- /package/dist/{chunk-664YNAMR.js.map → chunk-RTFPKJJV.js.map} +0 -0
- /package/dist/{chunk-GKR44BCH.js.map → chunk-RVDXBHUG.js.map} +0 -0
- /package/dist/{chunk-A7Z27COA.js.map → chunk-VFWAZLEF.js.map} +0 -0
- /package/dist/{chunk-3EJNDIAO.js.map → chunk-VZ5B2SIO.js.map} +0 -0
- /package/dist/{cleanup-X556LNWM.js.map → cleanup-ELVHZPRC.js.map} +0 -0
- /package/dist/{cleanup-command-RV2Q7G72.js.map → cleanup-command-5BY4FWAT.js.map} +0 -0
- /package/dist/{clients-HP23HRN2.js.map → clients-WRPBRO6V.js.map} +0 -0
- /package/dist/{config-LUSJAK6B.js.map → config-TXJRCN3W.js.map} +0 -0
- /package/dist/{configured-paths-SPQ5XWOM.js.map → configured-paths-4CRHLBIF.js.map} +0 -0
- /package/dist/{contract-KEZY6YC3.js.map → contract-B7NDKO5B.js.map} +0 -0
- /package/dist/{corpus-PPOBTF4J.js.map → corpus-PUBXCCSN.js.map} +0 -0
- /package/dist/{doctor-DSC36R4U.js.map → doctor-RPXETRSJ.js.map} +0 -0
- /package/dist/{feature-directories-VOHVV26X.js.map → feature-directories-ASNDEM7J.js.map} +0 -0
- /package/dist/{finalization-7SMLMVLV.js.map → finalization-BGPOEBAL.js.map} +0 -0
- /package/dist/{gh-cli-ZMO7D2KE.js.map → gh-cli-OTX7PORT.js.map} +0 -0
- /package/dist/{github-rest-KGKS4V2H.js.map → github-rest-JCW32377.js.map} +0 -0
- /package/dist/{learning-sync-6BTHM6ZC.js.map → learning-sync-2WIAOTW3.js.map} +0 -0
- /package/dist/{legacy-global-guidance-2JL4Y5EB.js.map → legacy-global-guidance-WTDI53H7.js.map} +0 -0
- /package/dist/{lint-gherkin-COSHVS6Z.js.map → lint-gherkin-LS3CHW3J.js.map} +0 -0
- /package/dist/{operations-XVRQSLJG.js.map → operations-OZKEQCS4.js.map} +0 -0
- /package/dist/{output-3LB2OEHN.js.map → output-ZYTEW6SC.js.map} +0 -0
- /package/dist/{packet-5WT5M3OS.js.map → packet-7S6CZJQR.js.map} +0 -0
- /package/dist/{profile-UMKQ5JVO.js.map → profile-SDFSBKH5.js.map} +0 -0
- /package/dist/{prompt-GWYDFMLO.js.map → prompt-LYPODDC3.js.map} +0 -0
- /package/dist/{remove-E6HGNHOU.js.map → remove-P5JQNS5P.js.map} +0 -0
- /package/dist/{retro-IFSVU5KS.js.map → retro-EQK2Z2Z6.js.map} +0 -0
- /package/dist/{retro-draft-spool-4S3TCHWL.js.map → retro-draft-spool-MDPBVM6J.js.map} +0 -0
- /package/dist/{retro-extract-OSBFAXLU.js.map → retro-extract-CZMDD4WB.js.map} +0 -0
- /package/dist/{review-pr-6S2352CK.js.map → review-pr-CWRD446M.js.map} +0 -0
- /package/dist/{review-pr-publication-CGHCRRUI.js.map → review-pr-publication-AIX2U3VM.js.map} +0 -0
- /package/dist/{run-5JESKXUR.js.map → run-T45X6Z6C.js.map} +0 -0
- /package/dist/{self-report-VPHBPPSJ.js.map → self-report-V7PHKTS6.js.map} +0 -0
- /package/dist/{status-7VIGNTUI.js.map → status-F5TFEIZ6.js.map} +0 -0
- /package/dist/{status-EX4L3ELO.js.map → status-S24PDFXB.js.map} +0 -0
- /package/dist/{sync-config-O7J7IDGE.js.map → sync-config-EUVBPUUG.js.map} +0 -0
- /package/dist/{sync-tracker-U2GYOFUO.js.map → sync-tracker-5FSIIAMS.js.map} +0 -0
- /package/dist/{ticket-sync-LLUBXZ2R.js.map → ticket-sync-7QRH6PVT.js.map} +0 -0
- /package/dist/{tracker-map-HR4MBYG5.js.map → tracker-map-7AGCTWBZ.js.map} +0 -0
- /package/dist/{tracker-sync-YDICZN66.js.map → tracker-sync-MLVYGFQJ.js.map} +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WORKSPACE_ROOTS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OKPRQGFG.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveConfiguredLaneDirectory,
|
|
6
6
|
resolveTicketsDirectory,
|
|
7
7
|
toRepoDirectory,
|
|
8
8
|
toRepoPath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RVDXBHUG.js";
|
|
10
10
|
import {
|
|
11
11
|
readFileSafe
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HF3DXZEA.js";
|
|
13
13
|
|
|
14
14
|
// src/utils/feature-source.ts
|
|
15
15
|
import { existsSync, readdirSync } from "fs";
|
|
@@ -39,9 +39,12 @@ function slugForTicket(cwd, ticketFolder) {
|
|
|
39
39
|
const dashIndex = ticketFolder.indexOf("-");
|
|
40
40
|
return dashIndex === -1 ? ticketFolder : ticketFolder.slice(dashIndex + 1);
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
function featureSourceFileName(cwd, ticketFolder) {
|
|
43
|
+
return `${slugForTicket(cwd, ticketFolder)}.feature`;
|
|
44
|
+
}
|
|
45
|
+
function findFeatureSourcePath(cwd, ticketFolder, featureFiles = collectExecutableFeatureFiles(cwd)) {
|
|
46
|
+
const fileName = featureSourceFileName(cwd, ticketFolder);
|
|
47
|
+
return featureFiles.find((path) => nodePath.basename(path) === fileName);
|
|
45
48
|
}
|
|
46
49
|
function createPhaseAnchorEnvironment(cwd, configuredFeatures) {
|
|
47
50
|
const featureRoots = ["features"];
|
|
@@ -124,6 +127,7 @@ function isMatchingFeatureFile(entryName, fileName) {
|
|
|
124
127
|
|
|
125
128
|
export {
|
|
126
129
|
readFrontmatterScalar,
|
|
130
|
+
featureSourceFileName,
|
|
127
131
|
findFeatureSourcePath,
|
|
128
132
|
createPhaseAnchorEnvironment,
|
|
129
133
|
createPhaseAnchorScope,
|
|
@@ -131,4 +135,4 @@ export {
|
|
|
131
135
|
hasDefaultExecutableFeatureFiles,
|
|
132
136
|
collectExecutableFeatureDirectories
|
|
133
137
|
};
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
138
|
+
//# sourceMappingURL=chunk-BZWO3BVD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/feature-source.ts","../src/utils/frontmatter.ts"],"sourcesContent":["import { existsSync, readdirSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport type { PhaseAnchorScope } from '../../templates/hooks/lib/phase-provenance.js';\nimport { resolveConfiguredLaneDirectory, resolveTicketsDirectory } from './configured-paths.js';\nimport { readFrontmatterScalar } from './frontmatter.js';\nimport { readFileSafe } from './fs.js';\nimport { toRepoDirectory, toRepoPath } from './repo-path.js';\nimport { WORKSPACE_ROOTS } from './workspaces.js';\n\n/** Read the authoritative ticket slug, falling back for legacy folders. */\nfunction slugForTicket(cwd: string, ticketFolder: string): string {\n const content = readFileSafe(\n nodePath.join(resolveTicketsDirectory(cwd), ticketFolder, 'ticket.md'),\n );\n const slug = readFrontmatterScalar(content, 'slug');\n if (slug !== undefined && slug !== '') return slug;\n\n const dashIndex = ticketFolder.indexOf('-');\n return dashIndex === -1 ? ticketFolder : ticketFolder.slice(dashIndex + 1);\n}\n\n/**\n * Find the executable `.feature` file for a ticket by slug. The search mirrors\n * the Cucumber lane: all feature files under root `features/`, then under each\n * direct workspace package's `features/` directory in stable order.\n */\nexport function featureSourceFileName(cwd: string, ticketFolder: string): string {\n return `${slugForTicket(cwd, ticketFolder)}.feature`;\n}\n\nexport function findFeatureSourcePath(\n cwd: string,\n ticketFolder: string,\n featureFiles: readonly string[] = collectExecutableFeatureFiles(cwd),\n): string | undefined {\n const fileName = featureSourceFileName(cwd, ticketFolder);\n return featureFiles.find(path => nodePath.basename(path) === fileName);\n}\n\n/** Build the ticket-invariant feature ownership policy once per tree/config. */\nexport function createPhaseAnchorEnvironment(\n cwd: string,\n configuredFeatures?: string,\n): Omit<PhaseAnchorScope, 'ticketPath'> {\n const featureRoots = ['features'];\n if (configuredFeatures !== undefined) {\n const configuredRoot = toRepoDirectory(cwd, configuredFeatures);\n if (configuredRoot !== undefined && !featureRoots.includes(configuredRoot)) {\n featureRoots.push(configuredRoot);\n }\n }\n return { featureRoots, workspaceRoots: [...WORKSPACE_ROOTS] };\n}\n\n/** Build feature ownership policy without consulting feature-file existence. */\nexport function createPhaseAnchorScope(\n cwd: string,\n ticketPath: string,\n configuredFeatures?: string,\n): PhaseAnchorScope {\n return {\n ticketPath: toRepoPath(ticketPath),\n ...createPhaseAnchorEnvironment(cwd, configuredFeatures),\n };\n}\n\nexport function collectExecutableFeatureFiles(cwd: string, fileName?: string): string[] {\n return collectExecutableFeatureDirectories(cwd).flatMap(directory =>\n findFeatureFiles(directory, fileName),\n );\n}\n\n/** Whether Safeword's built-in feature locations already contain a real suite. */\nexport function hasDefaultExecutableFeatureFiles(cwd: string): boolean {\n return collectDefaultFeatureDirectories(cwd).some(directory => containsFeatureFile(directory));\n}\n\n/**\n * Return every directory that contributes executable feature files.\n *\n * Keep consumers that need feature-lane coverage (for example, audit checks)\n * on this shared enumeration rather than reconstructing workspace discovery.\n */\nexport function collectExecutableFeatureDirectories(cwd: string): string[] {\n const directories = collectDefaultFeatureDirectories(cwd);\n\n // paths.features AUGMENTS the defaults (ticket 56JCFZ) — relocated/host\n // lanes become readable without abandoning root features/.\n const configured = resolveConfiguredLaneDirectory(cwd, 'features');\n if (configured !== undefined && !directories.includes(configured)) {\n directories.push(configured);\n }\n return directories;\n}\n\nfunction collectDefaultFeatureDirectories(cwd: string): string[] {\n return [\n nodePath.join(cwd, 'features'),\n ...WORKSPACE_ROOTS.flatMap(root =>\n collectWorkspaceFeatureDirectories(nodePath.join(cwd, root)),\n ),\n ];\n}\n\nfunction collectWorkspaceFeatureDirectories(workspaceDirectory: string): string[] {\n if (!existsSync(workspaceDirectory)) return [];\n return readdirSync(workspaceDirectory, { withFileTypes: true })\n .filter(entry => entry.isDirectory())\n .map(entry => nodePath.join(workspaceDirectory, entry.name, 'features'));\n}\n\nfunction findFeatureFiles(directory: string, fileName?: string): string[] {\n let entries;\n try {\n entries = readdirSync(directory, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const matches: string[] = [];\n for (const entry of entries) {\n const absolute = nodePath.join(directory, entry.name);\n if (entry.isDirectory()) {\n matches.push(...findFeatureFiles(absolute, fileName));\n } else if (entry.isFile() && isMatchingFeatureFile(entry.name, fileName)) {\n matches.push(absolute);\n }\n }\n return matches.toSorted((a, b) => a.localeCompare(b));\n}\n\nfunction containsFeatureFile(directory: string): boolean {\n let entries;\n try {\n entries = readdirSync(directory, { withFileTypes: true });\n } catch {\n return false;\n }\n return entries.some(entry => {\n const absolute = nodePath.join(directory, entry.name);\n return entry.isDirectory()\n ? containsFeatureFile(absolute)\n : entry.isFile() && entry.name.endsWith('.feature');\n });\n}\n\nfunction isMatchingFeatureFile(entryName: string, fileName: string | undefined): boolean {\n if (!entryName.endsWith('.feature')) return false;\n return fileName === undefined || entryName === fileName;\n}\n","/** Read one scalar field from a `---`-fenced YAML frontmatter block. */\nexport function readFrontmatterScalar(\n content: string | undefined,\n field: string,\n): string | undefined {\n const lines = content?.split(/\\r?\\n/) ?? [];\n if (lines[0] !== '---') return undefined;\n const prefix = `${field}:`;\n for (const line of lines.slice(1)) {\n if (line === '---') return undefined;\n if (!line.startsWith(prefix)) continue;\n const value = line.slice(prefix.length).trim();\n return value === '' ? undefined : value;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,YAAY,mBAAmB;AACxC,OAAO,cAAc;;;ACAd,SAAS,sBACd,SACA,OACoB;AACpB,QAAM,QAAQ,SAAS,MAAM,OAAO,KAAK,CAAC;AAC1C,MAAI,MAAM,CAAC,MAAM,MAAO,QAAO;AAC/B,QAAM,SAAS,GAAG,KAAK;AACvB,aAAW,QAAQ,MAAM,MAAM,CAAC,GAAG;AACjC,QAAI,SAAS,MAAO,QAAO;AAC3B,QAAI,CAAC,KAAK,WAAW,MAAM,EAAG;AAC9B,UAAM,QAAQ,KAAK,MAAM,OAAO,MAAM,EAAE,KAAK;AAC7C,WAAO,UAAU,KAAK,SAAY;AAAA,EACpC;AACA,SAAO;AACT;;;ADJA,SAAS,cAAc,KAAa,cAA8B;AAChE,QAAM,UAAU;AAAA,IACd,SAAS,KAAK,wBAAwB,GAAG,GAAG,cAAc,WAAW;AAAA,EACvE;AACA,QAAM,OAAO,sBAAsB,SAAS,MAAM;AAClD,MAAI,SAAS,UAAa,SAAS,GAAI,QAAO;AAE9C,QAAM,YAAY,aAAa,QAAQ,GAAG;AAC1C,SAAO,cAAc,KAAK,eAAe,aAAa,MAAM,YAAY,CAAC;AAC3E;AAOO,SAAS,sBAAsB,KAAa,cAA8B;AAC/E,SAAO,GAAG,cAAc,KAAK,YAAY,CAAC;AAC5C;AAEO,SAAS,sBACd,KACA,cACA,eAAkC,8BAA8B,GAAG,GAC/C;AACpB,QAAM,WAAW,sBAAsB,KAAK,YAAY;AACxD,SAAO,aAAa,KAAK,UAAQ,SAAS,SAAS,IAAI,MAAM,QAAQ;AACvE;AAGO,SAAS,6BACd,KACA,oBACsC;AACtC,QAAM,eAAe,CAAC,UAAU;AAChC,MAAI,uBAAuB,QAAW;AACpC,UAAM,iBAAiB,gBAAgB,KAAK,kBAAkB;AAC9D,QAAI,mBAAmB,UAAa,CAAC,aAAa,SAAS,cAAc,GAAG;AAC1E,mBAAa,KAAK,cAAc;AAAA,IAClC;AAAA,EACF;AACA,SAAO,EAAE,cAAc,gBAAgB,CAAC,GAAG,eAAe,EAAE;AAC9D;AAGO,SAAS,uBACd,KACA,YACA,oBACkB;AAClB,SAAO;AAAA,IACL,YAAY,WAAW,UAAU;AAAA,IACjC,GAAG,6BAA6B,KAAK,kBAAkB;AAAA,EACzD;AACF;AAEO,SAAS,8BAA8B,KAAa,UAA6B;AACtF,SAAO,oCAAoC,GAAG,EAAE;AAAA,IAAQ,eACtD,iBAAiB,WAAW,QAAQ;AAAA,EACtC;AACF;AAGO,SAAS,iCAAiC,KAAsB;AACrE,SAAO,iCAAiC,GAAG,EAAE,KAAK,eAAa,oBAAoB,SAAS,CAAC;AAC/F;AAQO,SAAS,oCAAoC,KAAuB;AACzE,QAAM,cAAc,iCAAiC,GAAG;AAIxD,QAAM,aAAa,+BAA+B,KAAK,UAAU;AACjE,MAAI,eAAe,UAAa,CAAC,YAAY,SAAS,UAAU,GAAG;AACjE,gBAAY,KAAK,UAAU;AAAA,EAC7B;AACA,SAAO;AACT;AAEA,SAAS,iCAAiC,KAAuB;AAC/D,SAAO;AAAA,IACL,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,GAAG,gBAAgB;AAAA,MAAQ,UACzB,mCAAmC,SAAS,KAAK,KAAK,IAAI,CAAC;AAAA,IAC7D;AAAA,EACF;AACF;AAEA,SAAS,mCAAmC,oBAAsC;AAChF,MAAI,CAAC,WAAW,kBAAkB,EAAG,QAAO,CAAC;AAC7C,SAAO,YAAY,oBAAoB,EAAE,eAAe,KAAK,CAAC,EAC3D,OAAO,WAAS,MAAM,YAAY,CAAC,EACnC,IAAI,WAAS,SAAS,KAAK,oBAAoB,MAAM,MAAM,UAAU,CAAC;AAC3E;AAEA,SAAS,iBAAiB,WAAmB,UAA6B;AACxE,MAAI;AACJ,MAAI;AACF,cAAU,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAoB,CAAC;AAC3B,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,SAAS,KAAK,WAAW,MAAM,IAAI;AACpD,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAG,iBAAiB,UAAU,QAAQ,CAAC;AAAA,IACtD,WAAW,MAAM,OAAO,KAAK,sBAAsB,MAAM,MAAM,QAAQ,GAAG;AACxE,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,SAAO,QAAQ,SAAS,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACtD;AAEA,SAAS,oBAAoB,WAA4B;AACvD,MAAI;AACJ,MAAI;AACF,cAAU,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,KAAK,WAAS;AAC3B,UAAM,WAAW,SAAS,KAAK,WAAW,MAAM,IAAI;AACpD,WAAO,MAAM,YAAY,IACrB,oBAAoB,QAAQ,IAC5B,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,UAAU;AAAA,EACtD,CAAC;AACH;AAEA,SAAS,sBAAsB,WAAmB,UAAuC;AACvF,MAAI,CAAC,UAAU,SAAS,UAAU,EAAG,QAAO;AAC5C,SAAO,aAAa,UAAa,cAAc;AACjD;","names":[]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
assertSafeClaudeCleanupTarget,
|
|
3
3
|
containedClaudeCleanupPath,
|
|
4
4
|
observeClaudeLegacy
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7ULZND56.js";
|
|
6
6
|
import {
|
|
7
7
|
canonicalClaudeProjectRoot
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7XELS325.js";
|
|
9
9
|
import {
|
|
10
10
|
CLAUDE_MIGRATION_SCHEMA,
|
|
11
11
|
advisoryStateDigest,
|
|
@@ -14,19 +14,19 @@ import {
|
|
|
14
14
|
readClaudePluginMode,
|
|
15
15
|
writeClaudeMigrationAttention,
|
|
16
16
|
writeClaudePluginMode
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WVA4Q6BX.js";
|
|
18
18
|
import {
|
|
19
19
|
cataloguedClaudeLegacyPaths,
|
|
20
20
|
isAcceptedHistoricalFile,
|
|
21
21
|
isAcceptedHistoricalHook
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-HNCUIXDY.js";
|
|
23
23
|
import {
|
|
24
24
|
writeDurableFile,
|
|
25
25
|
writeDurableFileExclusive
|
|
26
26
|
} from "./chunk-QOLHGAD5.js";
|
|
27
27
|
import {
|
|
28
28
|
createResult
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FOYGZ4MK.js";
|
|
30
30
|
|
|
31
31
|
// src/claude-plugin/cleanup.ts
|
|
32
32
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -833,4 +833,4 @@ export {
|
|
|
833
833
|
migrateClaudeLegacyAutomatically,
|
|
834
834
|
recoverClaudeCleanup
|
|
835
835
|
};
|
|
836
|
-
//# sourceMappingURL=chunk-
|
|
836
|
+
//# sourceMappingURL=chunk-CFME47IY.js.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
SAFEWORD_SCHEMA
|
|
2
|
+
SAFEWORD_SCHEMA
|
|
3
|
+
} from "./chunk-IPTM5VE7.js";
|
|
4
|
+
import {
|
|
3
5
|
parse
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JSR3HXFT.js";
|
|
5
7
|
import {
|
|
6
8
|
CODEX_MIGRATION_SCHEMA
|
|
7
9
|
} from "./chunk-AVAUUIDB.js";
|
|
@@ -120,10 +122,13 @@ import {
|
|
|
120
122
|
constants as fsConstants,
|
|
121
123
|
existsSync,
|
|
122
124
|
fstatSync,
|
|
125
|
+
lstatSync as lstatSync2,
|
|
123
126
|
openSync,
|
|
127
|
+
readdirSync,
|
|
124
128
|
readFileSync as readFileSync2,
|
|
125
129
|
readSync,
|
|
126
130
|
realpathSync,
|
|
131
|
+
rmdirSync,
|
|
127
132
|
rmSync
|
|
128
133
|
} from "fs";
|
|
129
134
|
import { homedir } from "os";
|
|
@@ -210,15 +215,17 @@ function observeCodexHostProcesses() {
|
|
|
210
215
|
if (result.status !== 0) return { available: false, running: [], current: null };
|
|
211
216
|
return codexHostsFromProcessTable(result.stdout, process.ppid);
|
|
212
217
|
}
|
|
213
|
-
function observeRunningCodexHosts() {
|
|
214
|
-
const observation = observeCodexHostProcesses();
|
|
215
|
-
return observation.available ? observation.running : null;
|
|
216
|
-
}
|
|
217
218
|
function sameCodexHost(left, right) {
|
|
218
219
|
return left.pid === right.pid && left.started_at === right.started_at;
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
// src/codex-plugin/profile-proof.ts
|
|
223
|
+
var SHA256_PATTERN = /^[a-f\d]{64}$/u;
|
|
224
|
+
var NUMERIC_VERSION_PART_PATTERN = /^\d+$/u;
|
|
225
|
+
var VERSION_IDENTIFIER_PATTERN = /^[\dA-Za-z-]+$/u;
|
|
226
|
+
var MAX_PLUGIN_VERSION_LENGTH = 128;
|
|
227
|
+
var MAX_RETAINED_SESSION_PROOFS_PER_PROJECT = 256;
|
|
228
|
+
var MAX_RETAINED_SESSION_PROOF_PROJECTS = 64;
|
|
222
229
|
var CODEX_PLUGIN_HOOK_EVENTS = [
|
|
223
230
|
"session-start",
|
|
224
231
|
"pre-tool-use",
|
|
@@ -227,7 +234,8 @@ var CODEX_PLUGIN_HOOK_EVENTS = [
|
|
|
227
234
|
"stop"
|
|
228
235
|
];
|
|
229
236
|
function codexProfileDirectory(environment = process.env) {
|
|
230
|
-
|
|
237
|
+
const configuredHome = environment.CODEX_HOME?.trim();
|
|
238
|
+
return configuredHome || nodePath2.join(homedir(), ".codex");
|
|
231
239
|
}
|
|
232
240
|
function codexProofPath(environment = process.env, event = "session-start") {
|
|
233
241
|
return nodePath2.join(
|
|
@@ -267,7 +275,13 @@ function legacyCodexRestartMarkerPath(environment = process.env) {
|
|
|
267
275
|
return nodePath2.join(codexProfileDirectory(environment), "safeword/restart-pending-v1.json");
|
|
268
276
|
}
|
|
269
277
|
function codexActivationIsPending(environment = process.env) {
|
|
270
|
-
|
|
278
|
+
try {
|
|
279
|
+
return activationIsPendingForIdentity(environment, currentCodexPluginIdentity());
|
|
280
|
+
} catch {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function activationIsPendingForIdentity(environment, identity) {
|
|
271
285
|
return (
|
|
272
286
|
// The canonical marker is written atomically. If it exists but cannot be
|
|
273
287
|
// parsed or matched, fail closed instead of accepting unbound hook proof.
|
|
@@ -276,6 +290,23 @@ function codexActivationIsPending(environment = process.env) {
|
|
|
276
290
|
)
|
|
277
291
|
);
|
|
278
292
|
}
|
|
293
|
+
function codexActivationMarkerIssue(environment = process.env) {
|
|
294
|
+
const markerPath = codexActivationMarkerPath(environment);
|
|
295
|
+
if (!existsSync(markerPath)) return void 0;
|
|
296
|
+
let identity;
|
|
297
|
+
try {
|
|
298
|
+
identity = currentCodexPluginIdentity();
|
|
299
|
+
} catch {
|
|
300
|
+
return "package-unavailable";
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const marker = readTrustedHookProofJson(markerPath);
|
|
304
|
+
if (!isActivationMarkerV2(marker)) return "malformed";
|
|
305
|
+
return matchesCodexPluginIdentity(marker, identity) ? void 0 : "identity-mismatch";
|
|
306
|
+
} catch {
|
|
307
|
+
return "malformed";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
279
310
|
function packagedHookManifestPath() {
|
|
280
311
|
const candidates = [
|
|
281
312
|
nodePath2.resolve(import.meta.dirname, "../codex-plugin/hooks.json"),
|
|
@@ -287,19 +318,25 @@ function packagedHookManifestPath() {
|
|
|
287
318
|
}
|
|
288
319
|
return manifest;
|
|
289
320
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
|
|
294
|
-
|
|
321
|
+
var currentCodexPluginIdentity = /* @__PURE__ */ (() => {
|
|
322
|
+
let cachedIdentity;
|
|
323
|
+
return () => {
|
|
324
|
+
cachedIdentity ??= (() => {
|
|
325
|
+
const manifest = readFileSync2(packagedHookManifestPath());
|
|
326
|
+
return {
|
|
327
|
+
plugin_version: SAFEWORD_SCHEMA.version,
|
|
328
|
+
manifest_sha256: createHash("sha256").update(manifest).digest("hex")
|
|
329
|
+
};
|
|
330
|
+
})();
|
|
331
|
+
return cachedIdentity;
|
|
295
332
|
};
|
|
296
|
-
}
|
|
333
|
+
})();
|
|
297
334
|
function matchesCodexPluginIdentity(value, identity) {
|
|
298
335
|
return value.plugin_version === identity.plugin_version && value.manifest_sha256 === identity.manifest_sha256;
|
|
299
336
|
}
|
|
300
337
|
function writeCodexActivationMarker(environment = process.env, now = /* @__PURE__ */ new Date(), options = {}) {
|
|
301
338
|
const path = codexActivationMarkerPath(environment);
|
|
302
|
-
const activeHosts = options
|
|
339
|
+
const activeHosts = activeHostsForMarker(options);
|
|
303
340
|
const marker = {
|
|
304
341
|
schema_version: 2,
|
|
305
342
|
...currentCodexPluginIdentity(),
|
|
@@ -315,15 +352,24 @@ function writeCodexActivationMarker(environment = process.env, now = /* @__PURE_
|
|
|
315
352
|
recursive: true,
|
|
316
353
|
force: true
|
|
317
354
|
});
|
|
318
|
-
rmSync(nodePath2.join(codexProfileDirectory(environment), "safeword/session-proof-v1"), {
|
|
319
|
-
recursive: true,
|
|
320
|
-
force: true
|
|
321
|
-
});
|
|
322
355
|
rmSync(codexActivationReceiptPath(environment), { force: true });
|
|
323
356
|
rmSync(legacyCodexActivationMarkerPath(environment), { force: true });
|
|
324
357
|
rmSync(legacyCodexRestartMarkerPath(environment), { force: true });
|
|
325
358
|
return marker;
|
|
326
359
|
}
|
|
360
|
+
function activeHostsForMarker(options) {
|
|
361
|
+
if (options.activeHosts !== void 0) {
|
|
362
|
+
return options.activeHosts === null || options.activeHosts.length === 0 ? null : options.activeHosts;
|
|
363
|
+
}
|
|
364
|
+
const observation = options.hostObservation ?? observeCodexHostProcesses();
|
|
365
|
+
if (!observation.available) return null;
|
|
366
|
+
const activeHosts = [...observation.running];
|
|
367
|
+
const currentHost = observation.current;
|
|
368
|
+
if (currentHost !== null && activeHosts.every((host) => !sameCodexHost(host, currentHost))) {
|
|
369
|
+
activeHosts.push(currentHost);
|
|
370
|
+
}
|
|
371
|
+
return activeHosts.length === 0 ? null : activeHosts;
|
|
372
|
+
}
|
|
327
373
|
function hostObservationForOverride(current = null) {
|
|
328
374
|
return {
|
|
329
375
|
available: true,
|
|
@@ -333,7 +379,7 @@ function hostObservationForOverride(current = null) {
|
|
|
333
379
|
}
|
|
334
380
|
function activationReceiptForRestart(marker, hostObservation, identity, now) {
|
|
335
381
|
const currentHost = hostObservation.current;
|
|
336
|
-
if (!hostObservation.available || currentHost === null || marker.host_observation !== "observed" || now.getTime() < Date.parse(marker.installed_at) || marker.active_hosts.some(
|
|
382
|
+
if (!hostObservation.available || currentHost === null || marker.host_observation !== "observed" || marker.active_hosts.length === 0 || now.getTime() < Date.parse(marker.installed_at) || marker.active_hosts.some((installedHost) => sameCodexHost(installedHost, currentHost)) || marker.active_hosts.some(
|
|
337
383
|
(installedHost) => hostObservation.running.some((runningHost) => sameCodexHost(installedHost, runningHost))
|
|
338
384
|
)) {
|
|
339
385
|
return null;
|
|
@@ -361,36 +407,174 @@ function recordCodexHookProof(event, environment = process.env, now = /* @__PURE
|
|
|
361
407
|
}
|
|
362
408
|
const projectDirectory = writeOptions.projectDirectory === void 0 ? void 0 : canonicalProjectDirectory(writeOptions.projectDirectory);
|
|
363
409
|
const sessionId = writeOptions.sessionId?.trim();
|
|
364
|
-
const
|
|
365
|
-
|
|
410
|
+
const activationId = activeCodexActivationId(marker, receipt);
|
|
411
|
+
const recordedAt = now.toISOString();
|
|
412
|
+
const proof = projectDirectory !== void 0 && sessionId ? {
|
|
413
|
+
schema_version: 3,
|
|
414
|
+
event,
|
|
415
|
+
...identity,
|
|
416
|
+
activation_id: activationId,
|
|
417
|
+
project_directory: projectDirectory,
|
|
418
|
+
session_id: sessionId,
|
|
419
|
+
recorded_at: recordedAt
|
|
420
|
+
} : {
|
|
421
|
+
schema_version: 2,
|
|
366
422
|
event,
|
|
367
423
|
...identity,
|
|
368
|
-
activation_id:
|
|
369
|
-
|
|
370
|
-
recorded_at: now.toISOString()
|
|
424
|
+
activation_id: activationId,
|
|
425
|
+
recorded_at: recordedAt
|
|
371
426
|
};
|
|
372
427
|
writeAtomicJson(codexProofPath(environment, event), proof, writeOptions);
|
|
373
428
|
if (event === "session-start" && projectDirectory !== void 0 && sessionId) {
|
|
374
429
|
const sessionProof = {
|
|
375
|
-
schema_version:
|
|
430
|
+
schema_version: 2,
|
|
376
431
|
...identity,
|
|
432
|
+
activation_id: activationId,
|
|
377
433
|
project_directory: projectDirectory,
|
|
378
434
|
session_id: sessionId,
|
|
379
|
-
recorded_at:
|
|
435
|
+
recorded_at: recordedAt
|
|
380
436
|
};
|
|
381
|
-
|
|
437
|
+
const path = codexSessionProofPath(projectDirectory, sessionId, environment);
|
|
438
|
+
writeAtomicJson(path, sessionProof);
|
|
439
|
+
pruneCodexSessionProofs(path);
|
|
382
440
|
}
|
|
383
441
|
return proof;
|
|
384
442
|
}
|
|
385
|
-
function
|
|
443
|
+
function validSessionProof(value, canonicalProject, sessionId) {
|
|
444
|
+
if (typeof value !== "object" || value === null) return false;
|
|
445
|
+
const proof = value;
|
|
446
|
+
return sessionProofBindingMatches(proof, canonicalProject, sessionId) && sessionProofIdentityIsWellFormed(proof) && sessionProofTimeIsWellFormed(proof);
|
|
447
|
+
}
|
|
448
|
+
function sessionProofBindingMatches(proof, canonicalProject, sessionId) {
|
|
449
|
+
return (proof.schema_version === 1 || proof.schema_version === 2) && proof.project_directory === canonicalProject && proof.session_id === sessionId && (proof.schema_version === 1 || typeof proof.activation_id === "string" || proof.activation_id === null);
|
|
450
|
+
}
|
|
451
|
+
function sessionProofIdentityIsWellFormed(proof) {
|
|
452
|
+
return pluginVersionIsWellFormed(proof.plugin_version) && typeof proof.manifest_sha256 === "string" && SHA256_PATTERN.test(proof.manifest_sha256);
|
|
453
|
+
}
|
|
454
|
+
function numericVersionPartIsWellFormed(part) {
|
|
455
|
+
return part.length > 0 && NUMERIC_VERSION_PART_PATTERN.test(part) && (part === "0" || !part.startsWith("0"));
|
|
456
|
+
}
|
|
457
|
+
function versionIdentifiersAreWellFormed(value) {
|
|
458
|
+
return value === void 0 || value.split(".").every(
|
|
459
|
+
(identifier) => identifier.length > 0 ? VERSION_IDENTIFIER_PATTERN.test(identifier) : false
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
function pluginVersionIsWellFormed(value) {
|
|
463
|
+
if (typeof value !== "string" || value.length > MAX_PLUGIN_VERSION_LENGTH) return false;
|
|
464
|
+
const buildSplit = value.split("+");
|
|
465
|
+
if (buildSplit.length > 2) return false;
|
|
466
|
+
const coreAndPrerelease = buildSplit[0] ?? "";
|
|
467
|
+
const prereleaseSeparator = coreAndPrerelease.indexOf("-");
|
|
468
|
+
const coreText = prereleaseSeparator === -1 ? coreAndPrerelease : coreAndPrerelease.slice(0, prereleaseSeparator);
|
|
469
|
+
const prerelease = prereleaseSeparator === -1 ? void 0 : coreAndPrerelease.slice(prereleaseSeparator + 1);
|
|
470
|
+
const core = coreText.split(".");
|
|
471
|
+
return core.length === 3 && core.every((part) => numericVersionPartIsWellFormed(part)) && versionIdentifiersAreWellFormed(prerelease) && versionIdentifiersAreWellFormed(buildSplit[1]);
|
|
472
|
+
}
|
|
473
|
+
function sessionProofTimeIsWellFormed(proof) {
|
|
474
|
+
return typeof proof.recorded_at === "string" && !Number.isNaN(Date.parse(proof.recorded_at));
|
|
475
|
+
}
|
|
476
|
+
function classifySessionProof(proof, identity, marker, receipt, activationPending) {
|
|
477
|
+
if (proof.schema_version === 1) return "prior-observed";
|
|
478
|
+
if (!matchesCodexPluginIdentity(proof, identity)) return "prior-observed";
|
|
479
|
+
if (marker !== null && Date.parse(proof.recorded_at) < Date.parse(marker.installed_at)) {
|
|
480
|
+
return "prior-observed";
|
|
481
|
+
}
|
|
482
|
+
if (activationPending) return "activation-pending";
|
|
483
|
+
if (receipt === null) return proof.activation_id === null ? "current" : "prior-observed";
|
|
484
|
+
return proof.activation_id === receipt.activation_id && Date.parse(proof.recorded_at) >= Date.parse(receipt.activated_at) ? "current" : "prior-observed";
|
|
485
|
+
}
|
|
486
|
+
function observeCodexSessionProof(projectDirectory, sessionId, environment = process.env) {
|
|
386
487
|
const canonicalProject = canonicalProjectDirectory(projectDirectory);
|
|
488
|
+
const path = codexSessionProofPath(canonicalProject, sessionId, environment);
|
|
489
|
+
if (!existsSync(path)) {
|
|
490
|
+
return {
|
|
491
|
+
status: "missing",
|
|
492
|
+
plugin_version: null,
|
|
493
|
+
manifest_sha256: null,
|
|
494
|
+
recorded_at: null
|
|
495
|
+
};
|
|
496
|
+
}
|
|
387
497
|
try {
|
|
388
|
-
const value =
|
|
389
|
-
|
|
498
|
+
const value = readTrustedHookProofJson(path);
|
|
499
|
+
if (!validSessionProof(value, canonicalProject, sessionId)) throw new Error("Invalid proof.");
|
|
500
|
+
const identity = currentCodexPluginIdentity();
|
|
501
|
+
const marker = readActivationMarkerV2(environment, identity);
|
|
502
|
+
const receipt = readActivationReceipt(environment, identity);
|
|
503
|
+
const status = classifySessionProof(
|
|
504
|
+
value,
|
|
505
|
+
identity,
|
|
506
|
+
marker,
|
|
507
|
+
receipt,
|
|
508
|
+
activationIsPendingForIdentity(environment, identity)
|
|
390
509
|
);
|
|
391
|
-
return
|
|
510
|
+
return {
|
|
511
|
+
status,
|
|
512
|
+
plugin_version: value.plugin_version,
|
|
513
|
+
manifest_sha256: value.manifest_sha256,
|
|
514
|
+
recorded_at: value.recorded_at
|
|
515
|
+
};
|
|
392
516
|
} catch {
|
|
393
|
-
return
|
|
517
|
+
return {
|
|
518
|
+
status: "untrusted",
|
|
519
|
+
plugin_version: null,
|
|
520
|
+
manifest_sha256: null,
|
|
521
|
+
recorded_at: null
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function sessionProofProjectDirectories(root) {
|
|
526
|
+
try {
|
|
527
|
+
return readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).flatMap((projectEntry) => {
|
|
528
|
+
const path = nodePath2.join(root, projectEntry.name);
|
|
529
|
+
try {
|
|
530
|
+
if (sessionProofProjectFiles(path).length === 0) return [];
|
|
531
|
+
return [{ modifiedAt: lstatSync2(path).mtimeMs, path }];
|
|
532
|
+
} catch {
|
|
533
|
+
return [];
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
} catch {
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function sessionProofProjectFiles(projectPath) {
|
|
541
|
+
try {
|
|
542
|
+
return readdirSync(projectPath, { withFileTypes: true }).flatMap((entry) => {
|
|
543
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.name.startsWith(".") || !entry.name.endsWith(".json"))
|
|
544
|
+
return [];
|
|
545
|
+
const path = nodePath2.join(projectPath, entry.name);
|
|
546
|
+
try {
|
|
547
|
+
return [{ modifiedAt: lstatSync2(path).mtimeMs, path }];
|
|
548
|
+
} catch {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
} catch {
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
function pruneCodexSessionProofs(retainedPath) {
|
|
557
|
+
const retainedProjectPath = nodePath2.dirname(retainedPath);
|
|
558
|
+
const files = sessionProofProjectFiles(retainedProjectPath).toSorted((left, right) => {
|
|
559
|
+
if (left.path === retainedPath) return -1;
|
|
560
|
+
if (right.path === retainedPath) return 1;
|
|
561
|
+
return right.modifiedAt - left.modifiedAt || left.path.localeCompare(right.path);
|
|
562
|
+
});
|
|
563
|
+
for (const stale of files.slice(MAX_RETAINED_SESSION_PROOFS_PER_PROJECT)) {
|
|
564
|
+
rmSync(stale.path, { force: true });
|
|
565
|
+
}
|
|
566
|
+
const root = nodePath2.dirname(retainedProjectPath);
|
|
567
|
+
const projects = sessionProofProjectDirectories(root).toSorted((left, right) => {
|
|
568
|
+
if (left.path === retainedProjectPath) return -1;
|
|
569
|
+
if (right.path === retainedProjectPath) return 1;
|
|
570
|
+
return right.modifiedAt - left.modifiedAt || left.path.localeCompare(right.path);
|
|
571
|
+
});
|
|
572
|
+
for (const stale of projects.slice(MAX_RETAINED_SESSION_PROOF_PROJECTS)) {
|
|
573
|
+
for (const proof of sessionProofProjectFiles(stale.path)) rmSync(proof.path, { force: true });
|
|
574
|
+
try {
|
|
575
|
+
rmdirSync(stale.path);
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
394
578
|
}
|
|
395
579
|
}
|
|
396
580
|
function writeAtomicJson(path, value, options = {}) {
|
|
@@ -423,10 +607,13 @@ function readActivationReceipt(environment, identity) {
|
|
|
423
607
|
isActivationReceiptV1
|
|
424
608
|
);
|
|
425
609
|
}
|
|
610
|
+
function activeCodexActivationId(marker, receipt) {
|
|
611
|
+
return receipt?.activation_id ?? marker?.activation_id ?? null;
|
|
612
|
+
}
|
|
426
613
|
function readIdentityBoundJson(path, identity, validator) {
|
|
427
614
|
if (!existsSync(path)) return null;
|
|
428
615
|
try {
|
|
429
|
-
const value =
|
|
616
|
+
const value = readTrustedHookProofJson(path);
|
|
430
617
|
return validator(value) && matchesCodexPluginIdentity(value, identity) ? value : null;
|
|
431
618
|
} catch {
|
|
432
619
|
return null;
|
|
@@ -442,8 +629,8 @@ function readEventProof(path, expectedEvent) {
|
|
|
442
629
|
}
|
|
443
630
|
var MAX_CODEX_HOOK_PROOF_BYTES = 64 * 1024;
|
|
444
631
|
function assertTrustedHookProofMetadata(metadata) {
|
|
445
|
-
const
|
|
446
|
-
if (!metadata.isFile() || metadata.nlink !== 1 || !
|
|
632
|
+
const posixPermissionsAreTrusted = process.getuid === void 0 || metadata.uid === process.getuid() && (metadata.mode & 63) === 0;
|
|
633
|
+
if (!metadata.isFile() || metadata.nlink !== 1 || !posixPermissionsAreTrusted || metadata.size > MAX_CODEX_HOOK_PROOF_BYTES) {
|
|
447
634
|
throw new Error("Untrusted Codex hook proof file.");
|
|
448
635
|
}
|
|
449
636
|
}
|
|
@@ -496,15 +683,25 @@ function observeCodexHookProof(environment = process.env, binding) {
|
|
|
496
683
|
const parsed = existing.map((item) => readEventProof(item.path, item.event));
|
|
497
684
|
if (parsed.includes(null)) return malformedObservation();
|
|
498
685
|
const validProofs = parsed.filter((proof) => proof !== null);
|
|
499
|
-
|
|
500
|
-
|
|
686
|
+
let identity;
|
|
687
|
+
try {
|
|
688
|
+
identity = currentCodexPluginIdentity();
|
|
689
|
+
} catch {
|
|
690
|
+
return malformedObservation();
|
|
691
|
+
}
|
|
692
|
+
const marker = readActivationMarkerV2(environment, identity);
|
|
693
|
+
const receipt = readActivationReceipt(environment, identity);
|
|
694
|
+
const activationId = activeCodexActivationId(marker, receipt);
|
|
695
|
+
if (activationIsPendingForIdentity(environment, identity)) {
|
|
696
|
+
return staleHookObservation(validProofs, activationId);
|
|
697
|
+
}
|
|
501
698
|
const canonicalProject = binding === void 0 ? void 0 : canonicalProjectDirectory(binding.projectDirectory);
|
|
502
699
|
const current = validProofs.filter(
|
|
503
|
-
(proof) => matchesCodexPluginIdentity(proof, identity) && (activationId === null
|
|
700
|
+
(proof) => matchesCodexPluginIdentity(proof, identity) && (activationId === null ? proof.activation_id === null : proof.activation_id === activationId) && (receipt === null || Date.parse(proof.recorded_at) >= Date.parse(receipt.activated_at)) && (binding === void 0 || proof.schema_version === 3 && proof.project_directory === canonicalProject && proof.session_id === binding.sessionId)
|
|
504
701
|
);
|
|
505
702
|
const events = current.map((proof) => proof.event);
|
|
506
703
|
const missingEvents = CODEX_PLUGIN_HOOK_EVENTS.filter((event) => !events.includes(event));
|
|
507
|
-
const latest = current.toSorted((left, right) => right.recorded_at.
|
|
704
|
+
const latest = current.toSorted((left, right) => Date.parse(right.recorded_at) - Date.parse(left.recorded_at)).at(0);
|
|
508
705
|
let status = "stale";
|
|
509
706
|
if (current.length === CODEX_PLUGIN_HOOK_EVENTS.length) status = "current";
|
|
510
707
|
else if (current.length > 0) status = "partial";
|
|
@@ -518,6 +715,17 @@ function observeCodexHookProof(environment = process.env, binding) {
|
|
|
518
715
|
missing_events: missingEvents
|
|
519
716
|
};
|
|
520
717
|
}
|
|
718
|
+
function staleHookObservation(proofs, activationId) {
|
|
719
|
+
return {
|
|
720
|
+
status: "stale",
|
|
721
|
+
plugin_version: proofs[0]?.plugin_version ?? null,
|
|
722
|
+
manifest_sha256: proofs[0]?.manifest_sha256 ?? null,
|
|
723
|
+
recorded_at: null,
|
|
724
|
+
activation_id: activationId,
|
|
725
|
+
events: [],
|
|
726
|
+
missing_events: CODEX_PLUGIN_HOOK_EVENTS
|
|
727
|
+
};
|
|
728
|
+
}
|
|
521
729
|
function malformedObservation() {
|
|
522
730
|
return {
|
|
523
731
|
status: "malformed",
|
|
@@ -532,17 +740,17 @@ function malformedObservation() {
|
|
|
532
740
|
function isCodexHookProof(value) {
|
|
533
741
|
if (typeof value !== "object" || value === null) return false;
|
|
534
742
|
const proof = value;
|
|
535
|
-
return
|
|
743
|
+
return (proof.schema_version === 2 || proof.schema_version === 3) && isCodexPluginHookEvent(proof.event) && pluginVersionIsWellFormed(proof.plugin_version) && typeof proof.manifest_sha256 === "string" && SHA256_PATTERN.test(proof.manifest_sha256) && "activation_id" in proof && (typeof proof.activation_id === "string" || proof.activation_id === null) && (proof.schema_version !== 3 || typeof proof.project_directory === "string" && typeof proof.session_id === "string" && proof.session_id.trim() !== "") && typeof proof.recorded_at === "string" && !Number.isNaN(Date.parse(proof.recorded_at));
|
|
536
744
|
}
|
|
537
745
|
function isActivationMarkerV2(value) {
|
|
538
746
|
if (typeof value !== "object" || value === null) return false;
|
|
539
747
|
const marker = value;
|
|
540
|
-
return marker.schema_version === 2 &&
|
|
748
|
+
return marker.schema_version === 2 && pluginVersionIsWellFormed(marker.plugin_version) && typeof marker.manifest_sha256 === "string" && SHA256_PATTERN.test(marker.manifest_sha256) && typeof marker.activation_id === "string" && typeof marker.installed_at === "string" && !Number.isNaN(Date.parse(marker.installed_at)) && (marker.host_observation === "observed" || marker.host_observation === "unavailable") && Array.isArray(marker.active_hosts) && marker.active_hosts.every(isCodexHostProcessIdentity) && (marker.host_observation === "observed" ? marker.active_hosts.length > 0 : marker.active_hosts.length === 0);
|
|
541
749
|
}
|
|
542
750
|
function isActivationReceiptV1(value) {
|
|
543
751
|
if (typeof value !== "object" || value === null) return false;
|
|
544
752
|
const receipt = value;
|
|
545
|
-
return receipt.schema_version === 1 &&
|
|
753
|
+
return receipt.schema_version === 1 && pluginVersionIsWellFormed(receipt.plugin_version) && typeof receipt.manifest_sha256 === "string" && SHA256_PATTERN.test(receipt.manifest_sha256) && typeof receipt.activation_id === "string" && typeof receipt.activated_at === "string" && !Number.isNaN(Date.parse(receipt.activated_at)) && isCodexHostProcessIdentity(receipt.host);
|
|
546
754
|
}
|
|
547
755
|
function isCodexHostProcessIdentity(value) {
|
|
548
756
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -556,10 +764,12 @@ function isCodexPluginHookEvent(value) {
|
|
|
556
764
|
export {
|
|
557
765
|
legacyCommandIdentity,
|
|
558
766
|
legacyCodexEventIsViable,
|
|
767
|
+
CODEX_PLUGIN_HOOK_EVENTS,
|
|
559
768
|
codexActivationIsPending,
|
|
769
|
+
codexActivationMarkerIssue,
|
|
560
770
|
writeCodexActivationMarker,
|
|
561
771
|
recordCodexHookProof,
|
|
562
|
-
|
|
772
|
+
observeCodexSessionProof,
|
|
563
773
|
observeCodexHookProof
|
|
564
774
|
};
|
|
565
|
-
//# sourceMappingURL=chunk-
|
|
775
|
+
//# sourceMappingURL=chunk-CNBZ3S5U.js.map
|