@agimon-ai/doompi-config 0.0.1-alpha.2 → 0.0.1-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/README.md +139 -14
  2. package/dist/adapters/atomicJson.cjs +2 -0
  3. package/dist/adapters/atomicJson.cjs.map +1 -0
  4. package/dist/adapters/atomicJson.d.cts +6 -0
  5. package/dist/adapters/atomicJson.d.cts.map +1 -0
  6. package/dist/adapters/atomicJson.d.mts +6 -0
  7. package/dist/adapters/atomicJson.d.mts.map +1 -0
  8. package/dist/adapters/atomicJson.mjs +2 -0
  9. package/dist/adapters/atomicJson.mjs.map +1 -0
  10. package/dist/adapters/config.cjs +2 -0
  11. package/dist/adapters/config.cjs.map +1 -0
  12. package/dist/adapters/config.d.cts +11 -0
  13. package/dist/adapters/config.d.cts.map +1 -0
  14. package/dist/adapters/config.d.mts +11 -0
  15. package/dist/adapters/config.d.mts.map +1 -0
  16. package/dist/adapters/config.mjs +2 -0
  17. package/dist/adapters/config.mjs.map +1 -0
  18. package/dist/adapters/configWriter.cjs +2 -0
  19. package/dist/adapters/configWriter.cjs.map +1 -0
  20. package/dist/adapters/configWriter.d.cts +52 -0
  21. package/dist/adapters/configWriter.d.cts.map +1 -0
  22. package/dist/adapters/configWriter.d.mts +52 -0
  23. package/dist/adapters/configWriter.d.mts.map +1 -0
  24. package/dist/adapters/configWriter.mjs +2 -0
  25. package/dist/adapters/configWriter.mjs.map +1 -0
  26. package/dist/adapters/domains.cjs +2 -0
  27. package/dist/adapters/domains.cjs.map +1 -0
  28. package/dist/adapters/domains.d.cts +25 -0
  29. package/dist/adapters/domains.d.cts.map +1 -0
  30. package/dist/adapters/domains.d.mts +25 -0
  31. package/dist/adapters/domains.d.mts.map +1 -0
  32. package/dist/adapters/domains.mjs +2 -0
  33. package/dist/adapters/domains.mjs.map +1 -0
  34. package/dist/adapters/harnessState.cjs +2 -0
  35. package/dist/adapters/harnessState.cjs.map +1 -0
  36. package/dist/adapters/harnessState.d.cts +50 -0
  37. package/dist/adapters/harnessState.d.cts.map +1 -0
  38. package/dist/adapters/harnessState.d.mts +50 -0
  39. package/dist/adapters/harnessState.d.mts.map +1 -0
  40. package/dist/adapters/harnessState.mjs +2 -0
  41. package/dist/adapters/harnessState.mjs.map +1 -0
  42. package/dist/adapters/harnessStore.cjs +2 -0
  43. package/dist/adapters/harnessStore.cjs.map +1 -0
  44. package/dist/adapters/harnessStore.d.cts +102 -0
  45. package/dist/adapters/harnessStore.d.cts.map +1 -0
  46. package/dist/adapters/harnessStore.d.mts +102 -0
  47. package/dist/adapters/harnessStore.d.mts.map +1 -0
  48. package/dist/adapters/harnessStore.mjs +2 -0
  49. package/dist/adapters/harnessStore.mjs.map +1 -0
  50. package/dist/adapters/init.cjs +178 -0
  51. package/dist/adapters/init.cjs.map +1 -0
  52. package/dist/adapters/init.d.cts +39 -0
  53. package/dist/adapters/init.d.cts.map +1 -0
  54. package/dist/adapters/init.d.mts +39 -0
  55. package/dist/adapters/init.d.mts.map +1 -0
  56. package/dist/adapters/init.mjs +178 -0
  57. package/dist/adapters/init.mjs.map +1 -0
  58. package/dist/adapters/layeredConfig.cjs +2 -0
  59. package/dist/adapters/layeredConfig.cjs.map +1 -0
  60. package/dist/adapters/layeredConfig.d.cts +49 -0
  61. package/dist/adapters/layeredConfig.d.cts.map +1 -0
  62. package/dist/adapters/layeredConfig.d.mts +49 -0
  63. package/dist/adapters/layeredConfig.d.mts.map +1 -0
  64. package/dist/adapters/layeredConfig.mjs +2 -0
  65. package/dist/adapters/layeredConfig.mjs.map +1 -0
  66. package/dist/adapters/majorModes.cjs +2 -0
  67. package/dist/adapters/majorModes.cjs.map +1 -0
  68. package/dist/adapters/majorModes.d.cts +88 -0
  69. package/dist/adapters/majorModes.d.cts.map +1 -0
  70. package/dist/adapters/majorModes.d.mts +88 -0
  71. package/dist/adapters/majorModes.d.mts.map +1 -0
  72. package/dist/adapters/majorModes.mjs +2 -0
  73. package/dist/adapters/majorModes.mjs.map +1 -0
  74. package/dist/adapters/pi/configExtension.cjs +2 -0
  75. package/dist/adapters/pi/configExtension.cjs.map +1 -0
  76. package/dist/adapters/pi/configExtension.d.cts +8 -0
  77. package/dist/adapters/pi/configExtension.d.cts.map +1 -0
  78. package/dist/adapters/pi/configExtension.d.mts +8 -0
  79. package/dist/adapters/pi/configExtension.d.mts.map +1 -0
  80. package/dist/adapters/pi/configExtension.mjs +2 -0
  81. package/dist/adapters/pi/configExtension.mjs.map +1 -0
  82. package/dist/adapters/pi/doomConfigExtension.cjs +2 -0
  83. package/dist/adapters/pi/doomConfigExtension.cjs.map +1 -0
  84. package/dist/adapters/pi/doomConfigExtension.d.cts +8 -0
  85. package/dist/adapters/pi/doomConfigExtension.d.cts.map +1 -0
  86. package/dist/adapters/pi/doomConfigExtension.d.mts +8 -0
  87. package/dist/adapters/pi/doomConfigExtension.d.mts.map +1 -0
  88. package/dist/adapters/pi/doomConfigExtension.mjs +2 -0
  89. package/dist/adapters/pi/doomConfigExtension.mjs.map +1 -0
  90. package/dist/adapters/pi/helpContribution.cjs +2 -0
  91. package/dist/adapters/pi/helpContribution.cjs.map +1 -0
  92. package/dist/adapters/pi/helpContribution.mjs +2 -0
  93. package/dist/adapters/pi/helpContribution.mjs.map +1 -0
  94. package/dist/adapters/pi/piContext.cjs +2 -0
  95. package/dist/adapters/pi/piContext.cjs.map +1 -0
  96. package/dist/adapters/pi/piContext.d.cts +20 -0
  97. package/dist/adapters/pi/piContext.d.cts.map +1 -0
  98. package/dist/adapters/pi/piContext.d.mts +20 -0
  99. package/dist/adapters/pi/piContext.d.mts.map +1 -0
  100. package/dist/adapters/pi/piContext.mjs +2 -0
  101. package/dist/adapters/pi/piContext.mjs.map +1 -0
  102. package/dist/adapters/piConfig.cjs +2 -0
  103. package/dist/adapters/piConfig.cjs.map +1 -0
  104. package/dist/adapters/piConfig.d.cts +8 -0
  105. package/dist/adapters/piConfig.d.cts.map +1 -0
  106. package/dist/adapters/piConfig.d.mts +8 -0
  107. package/dist/adapters/piConfig.d.mts.map +1 -0
  108. package/dist/adapters/piConfig.mjs +2 -0
  109. package/dist/adapters/piConfig.mjs.map +1 -0
  110. package/dist/adapters/pluginCatalog.cjs +2 -0
  111. package/dist/adapters/pluginCatalog.cjs.map +1 -0
  112. package/dist/adapters/pluginCatalog.d.cts +12 -0
  113. package/dist/adapters/pluginCatalog.d.cts.map +1 -0
  114. package/dist/adapters/pluginCatalog.d.mts +12 -0
  115. package/dist/adapters/pluginCatalog.d.mts.map +1 -0
  116. package/dist/adapters/pluginCatalog.mjs +2 -0
  117. package/dist/adapters/pluginCatalog.mjs.map +1 -0
  118. package/dist/adapters/profiles.cjs +4 -0
  119. package/dist/adapters/profiles.cjs.map +1 -0
  120. package/dist/adapters/profiles.d.cts +36 -0
  121. package/dist/adapters/profiles.d.cts.map +1 -0
  122. package/dist/adapters/profiles.d.mts +36 -0
  123. package/dist/adapters/profiles.d.mts.map +1 -0
  124. package/dist/adapters/profiles.mjs +4 -0
  125. package/dist/adapters/profiles.mjs.map +1 -0
  126. package/dist/atomicJson.cjs +1 -2
  127. package/dist/atomicJson.d.cts +2 -6
  128. package/dist/atomicJson.d.mts +2 -6
  129. package/dist/atomicJson.mjs +1 -2
  130. package/dist/config/piConfig.cjs +1 -2
  131. package/dist/config/piConfig.d.cts +2 -7
  132. package/dist/config/piConfig.d.mts +2 -7
  133. package/dist/config/piConfig.mjs +1 -2
  134. package/dist/config/schema.d.cts +2 -33
  135. package/dist/config/schema.d.mts +2 -33
  136. package/dist/config.cjs +1 -2
  137. package/dist/config.d.cts +3 -24
  138. package/dist/config.d.mts +3 -24
  139. package/dist/config.mjs +1 -2
  140. package/dist/configWriter.cjs +1 -2
  141. package/dist/configWriter.d.cts +2 -52
  142. package/dist/configWriter.d.mts +2 -52
  143. package/dist/configWriter.mjs +1 -2
  144. package/dist/container/doomConfigContainer.cjs +2 -0
  145. package/dist/container/doomConfigContainer.cjs.map +1 -0
  146. package/dist/container/doomConfigContainer.d.cts +12 -0
  147. package/dist/container/doomConfigContainer.d.cts.map +1 -0
  148. package/dist/container/doomConfigContainer.d.mts +12 -0
  149. package/dist/container/doomConfigContainer.d.mts.map +1 -0
  150. package/dist/container/doomConfigContainer.mjs +2 -0
  151. package/dist/container/doomConfigContainer.mjs.map +1 -0
  152. package/dist/container.cjs +1 -2
  153. package/dist/container.d.cts +2 -12
  154. package/dist/container.d.mts +2 -12
  155. package/dist/container.mjs +1 -2
  156. package/dist/domains.cjs +1 -2
  157. package/dist/domains.d.cts +6 -77
  158. package/dist/domains.d.mts +6 -77
  159. package/dist/domains.mjs +1 -2
  160. package/dist/extensions/doom.cjs +1 -2
  161. package/dist/extensions/doom.d.cts +2 -8
  162. package/dist/extensions/doom.d.mts +2 -8
  163. package/dist/extensions/doom.mjs +1 -2
  164. package/dist/extensions/pi.cjs +1 -2
  165. package/dist/extensions/pi.d.cts +2 -8
  166. package/dist/extensions/pi.d.mts +2 -8
  167. package/dist/extensions/pi.mjs +1 -2
  168. package/dist/harnessState.cjs +1 -2
  169. package/dist/harnessState.d.cts +2 -50
  170. package/dist/harnessState.d.mts +2 -50
  171. package/dist/harnessState.mjs +1 -2
  172. package/dist/harnessStore.cjs +1 -2
  173. package/dist/harnessStore.d.cts +2 -102
  174. package/dist/harnessStore.d.mts +2 -102
  175. package/dist/harnessStore.mjs +1 -2
  176. package/dist/index.cjs +1 -1
  177. package/dist/index.d.cts +20 -16
  178. package/dist/index.d.mts +20 -16
  179. package/dist/index.mjs +1 -1
  180. package/dist/init.cjs +1 -76
  181. package/dist/init.d.cts +2 -33
  182. package/dist/init.d.mts +2 -33
  183. package/dist/init.mjs +1 -76
  184. package/dist/layeredConfig.cjs +1 -2
  185. package/dist/layeredConfig.d.cts +2 -49
  186. package/dist/layeredConfig.d.mts +2 -49
  187. package/dist/layeredConfig.mjs +1 -2
  188. package/dist/majorModes.cjs +1 -2
  189. package/dist/majorModes.d.cts +2 -75
  190. package/dist/majorModes.d.mts +2 -75
  191. package/dist/majorModes.mjs +1 -2
  192. package/dist/pi.cjs +1 -1
  193. package/dist/pi.d.cts +1 -1
  194. package/dist/pi.d.mts +1 -1
  195. package/dist/pi.mjs +1 -1
  196. package/dist/piContext.cjs +1 -2
  197. package/dist/piContext.d.cts +2 -19
  198. package/dist/piContext.d.mts +2 -19
  199. package/dist/piContext.mjs +1 -2
  200. package/dist/profiles.cjs +1 -4
  201. package/dist/profiles.d.cts +2 -35
  202. package/dist/profiles.d.mts +2 -35
  203. package/dist/profiles.mjs +1 -4
  204. package/dist/schemas/config/schema.d.cts +33 -0
  205. package/dist/schemas/config/schema.d.cts.map +1 -0
  206. package/dist/schemas/config/schema.d.mts +33 -0
  207. package/dist/schemas/config/schema.d.mts.map +1 -0
  208. package/dist/services/configPolicy.cjs +2 -0
  209. package/dist/services/configPolicy.cjs.map +1 -0
  210. package/dist/services/configPolicy.d.cts +19 -0
  211. package/dist/services/configPolicy.d.cts.map +1 -0
  212. package/dist/services/configPolicy.d.mts +19 -0
  213. package/dist/services/configPolicy.d.mts.map +1 -0
  214. package/dist/services/configPolicy.mjs +2 -0
  215. package/dist/services/configPolicy.mjs.map +1 -0
  216. package/dist/services/domains.cjs +2 -0
  217. package/dist/services/domains.cjs.map +1 -0
  218. package/dist/services/domains.d.cts +12 -0
  219. package/dist/services/domains.d.cts.map +1 -0
  220. package/dist/services/domains.d.mts +12 -0
  221. package/dist/services/domains.d.mts.map +1 -0
  222. package/dist/services/domains.mjs +2 -0
  223. package/dist/services/domains.mjs.map +1 -0
  224. package/dist/types/config.cjs +2 -0
  225. package/dist/types/config.cjs.map +1 -0
  226. package/dist/types/config.d.cts +136 -0
  227. package/dist/types/config.d.cts.map +1 -0
  228. package/dist/types/config.d.mts +136 -0
  229. package/dist/types/config.d.mts.map +1 -0
  230. package/dist/types/config.mjs +2 -0
  231. package/dist/types/config.mjs.map +1 -0
  232. package/dist/types/domains.d.cts +89 -0
  233. package/dist/types/domains.d.cts.map +1 -0
  234. package/dist/types/domains.d.mts +89 -0
  235. package/dist/types/domains.d.mts.map +1 -0
  236. package/dist/types.cjs +1 -2
  237. package/dist/types.d.cts +2 -132
  238. package/dist/types.d.mts +2 -132
  239. package/dist/types.mjs +1 -2
  240. package/llms.txt +9 -0
  241. package/package.json +18 -6
  242. package/skills/doom-pi-config/SKILL.md +6 -3
  243. package/skills/doom-pi-config/references/config-contract.md +123 -8
  244. package/dist/atomicJson.cjs.map +0 -1
  245. package/dist/atomicJson.d.cts.map +0 -1
  246. package/dist/atomicJson.d.mts.map +0 -1
  247. package/dist/atomicJson.mjs.map +0 -1
  248. package/dist/config/piConfig.cjs.map +0 -1
  249. package/dist/config/piConfig.d.cts.map +0 -1
  250. package/dist/config/piConfig.d.mts.map +0 -1
  251. package/dist/config/piConfig.mjs.map +0 -1
  252. package/dist/config/schema.d.cts.map +0 -1
  253. package/dist/config/schema.d.mts.map +0 -1
  254. package/dist/config.cjs.map +0 -1
  255. package/dist/config.d.cts.map +0 -1
  256. package/dist/config.d.mts.map +0 -1
  257. package/dist/config.mjs.map +0 -1
  258. package/dist/configWriter.cjs.map +0 -1
  259. package/dist/configWriter.d.cts.map +0 -1
  260. package/dist/configWriter.d.mts.map +0 -1
  261. package/dist/configWriter.mjs.map +0 -1
  262. package/dist/container.cjs.map +0 -1
  263. package/dist/container.d.cts.map +0 -1
  264. package/dist/container.d.mts.map +0 -1
  265. package/dist/container.mjs.map +0 -1
  266. package/dist/domains.cjs.map +0 -1
  267. package/dist/domains.d.cts.map +0 -1
  268. package/dist/domains.d.mts.map +0 -1
  269. package/dist/domains.mjs.map +0 -1
  270. package/dist/extensions/doom.cjs.map +0 -1
  271. package/dist/extensions/doom.d.cts.map +0 -1
  272. package/dist/extensions/doom.d.mts.map +0 -1
  273. package/dist/extensions/doom.mjs.map +0 -1
  274. package/dist/extensions/pi.cjs.map +0 -1
  275. package/dist/extensions/pi.d.cts.map +0 -1
  276. package/dist/extensions/pi.d.mts.map +0 -1
  277. package/dist/extensions/pi.mjs.map +0 -1
  278. package/dist/harnessState.cjs.map +0 -1
  279. package/dist/harnessState.d.cts.map +0 -1
  280. package/dist/harnessState.d.mts.map +0 -1
  281. package/dist/harnessState.mjs.map +0 -1
  282. package/dist/harnessStore.cjs.map +0 -1
  283. package/dist/harnessStore.d.cts.map +0 -1
  284. package/dist/harnessStore.d.mts.map +0 -1
  285. package/dist/harnessStore.mjs.map +0 -1
  286. package/dist/init.cjs.map +0 -1
  287. package/dist/init.d.cts.map +0 -1
  288. package/dist/init.d.mts.map +0 -1
  289. package/dist/init.mjs.map +0 -1
  290. package/dist/layeredConfig.cjs.map +0 -1
  291. package/dist/layeredConfig.d.cts.map +0 -1
  292. package/dist/layeredConfig.d.mts.map +0 -1
  293. package/dist/layeredConfig.mjs.map +0 -1
  294. package/dist/majorModes.cjs.map +0 -1
  295. package/dist/majorModes.d.cts.map +0 -1
  296. package/dist/majorModes.d.mts.map +0 -1
  297. package/dist/majorModes.mjs.map +0 -1
  298. package/dist/piContext.cjs.map +0 -1
  299. package/dist/piContext.d.cts.map +0 -1
  300. package/dist/piContext.d.mts.map +0 -1
  301. package/dist/piContext.mjs.map +0 -1
  302. package/dist/profiles.cjs.map +0 -1
  303. package/dist/profiles.d.cts.map +0 -1
  304. package/dist/profiles.d.mts.map +0 -1
  305. package/dist/profiles.mjs.map +0 -1
  306. package/dist/types.cjs.map +0 -1
  307. package/dist/types.d.cts.map +0 -1
  308. package/dist/types.d.mts.map +0 -1
  309. package/dist/types.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.mjs","names":[],"sources":["../../src/adapters/domains.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { expandDomainNames } from '../services/domains.ts';\nimport type {\n DomainDefinition,\n DomainManifest,\n DomainMcpAllowlist,\n DomainPlugin,\n PluginEntry,\n ResolvedDomain,\n} from '../types/domains.ts';\nimport { globalDoomConfigDirectory } from './config.ts';\nimport { readDoomConfigSources } from './layeredConfig.ts';\nimport { loadPluginCatalog, pluginDirectoryForSource } from './pluginCatalog.ts';\n\nexport { DOOM_DIR } from './layeredConfig.ts';\n\nconst DOMAINS_FILE = 'domains.yaml';\nconst UNKNOWN_DOMAIN_ERROR = 'Unknown domain';\nconst UNKNOWN_PLUGIN_ERROR = 'Unknown plugin';\nconst MISSING_PLUGIN_DIRECTORY_ERROR = 'Plugin directory does not exist';\nconst DEFAULT_DOMAINS_PATH = '.doom/domains.yaml';\nconst SUPPORTED_AGENT_PLUGIN_SCHEMA = 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction requireRecord(value: unknown, label: string): Record<string, unknown> {\n if (!isRecord(value)) throw new Error(`${label} must be an object`);\n return value;\n}\n\nfunction assertOnlyKeys(record: Record<string, unknown>, allowed: readonly string[], label: string): void {\n const unexpected = Object.keys(record).filter((key) => !allowed.includes(key));\n if (unexpected.length > 0) throw new Error(`${label} contains unsupported field \"${unexpected[0]}\"`);\n}\n\nfunction requireString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} must be a non-empty string`);\n return value.trim();\n}\n\nfunction optionalString(value: unknown, label: string): string | undefined {\n if (value === undefined) return undefined;\n return requireString(value, label);\n}\n\nfunction parseStringArray(value: unknown, label: string): string[] {\n if (!Array.isArray(value)) throw new Error(`${label} must be an array of non-empty strings`);\n return [...new Set(value.map((entry, index) => requireString(entry, `${label}[${index}]`)))];\n}\n\nfunction parseOptionalBoolean(value: unknown, label: string): boolean | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'boolean') throw new Error(`${label} must be a boolean`);\n return value;\n}\n\nfunction parseDefaultDomains(value: unknown): string[] {\n return parseStringArray(value, `defaultDomains in ${DEFAULT_DOMAINS_PATH}`);\n}\n\nfunction parseDomainPlugin(value: unknown, label: string): DomainPlugin {\n if (typeof value === 'string') return requireString(value, label);\n const plugin = requireRecord(value, label);\n assertOnlyKeys(plugin, ['name', 'skills', 'agents', 'hooks', 'mcp'], label);\n const skills = plugin.skills === undefined ? undefined : parseStringArray(plugin.skills, `${label}.skills`);\n const agents = plugin.agents === undefined ? undefined : parseStringArray(plugin.agents, `${label}.agents`);\n const hooks = parseOptionalBoolean(plugin.hooks, `${label}.hooks`);\n const mcp = parseOptionalBoolean(plugin.mcp, `${label}.mcp`);\n return {\n name: requireString(plugin.name, `${label}.name`),\n ...(skills ? { skills } : {}),\n ...(agents ? { agents } : {}),\n ...(hooks === undefined ? {} : { hooks }),\n ...(mcp === undefined ? {} : { mcp }),\n };\n}\n\nfunction parseMcpAllowlist(value: unknown, label: string): DomainMcpAllowlist {\n const mcp = requireRecord(value, label);\n assertOnlyKeys(mcp, ['servers', 'proxy'], label);\n return {\n ...(mcp.servers === undefined ? {} : { servers: parseStringArray(mcp.servers, `${label}.servers`) }),\n ...(mcp.proxy === undefined ? {} : { proxy: parseStringArray(mcp.proxy, `${label}.proxy`) }),\n };\n}\n\nfunction parseDomain(value: unknown, label: string): DomainDefinition {\n const domain = requireRecord(value, label);\n assertOnlyKeys(domain, ['description', 'plugins', 'mcp', 'sharedSkills'], label);\n const description = optionalString(domain.description, `${label}.description`);\n if (domain.plugins !== undefined && !Array.isArray(domain.plugins)) {\n throw new Error(`${label}.plugins must be an array`);\n }\n const sharedSkills = parseOptionalBoolean(domain.sharedSkills, `${label}.sharedSkills`);\n return {\n ...(description ? { description } : {}),\n ...(Array.isArray(domain.plugins)\n ? { plugins: domain.plugins.map((plugin, index) => parseDomainPlugin(plugin, `${label}.plugins[${index}]`)) }\n : {}),\n ...(domain.mcp === undefined ? {} : { mcp: parseMcpAllowlist(domain.mcp, `${label}.mcp`) }),\n ...(sharedSkills === undefined ? {} : { sharedSkills }),\n };\n}\n\nfunction parseAliases(value: unknown, label: string): Record<string, string[]> {\n if (value === undefined) return {};\n const aliases = requireRecord(value, label);\n return Object.fromEntries(\n Object.entries(aliases).map(([name, domains]) => [name, parseStringArray(domains, `${label}.${name}`)]),\n );\n}\n\n/**\n * Reads domains and plugin catalogs from global and repository configs.\n *\n * Named domains and explicit plugin entries are replaced by later repository\n * definitions. Configured roots accumulate, with exact marketplace manifests\n * deduplicated in discovery order. Explicit entries override discovered IDs.\n */\nexport function loadDomains(repoRoot: string, homeDirectory: string = os.homedir()): DomainManifest {\n const sources = readDoomConfigSources<unknown>(DOMAINS_FILE, repoRoot, homeDirectory);\n const aliases: Record<string, string[]> = {};\n const domains: Record<string, ResolvedDomain> = {};\n let defaultDomains: string[] | undefined;\n\n for (const source of sources) {\n const document = requireRecord(source.document, source.filePath);\n assertOnlyKeys(document, ['plugins', 'domains', 'aliases', 'defaultDomains'], source.filePath);\n Object.assign(aliases, parseAliases(document.aliases, `aliases in ${source.filePath}`));\n if (document.domains !== undefined) {\n const sourceDomains = requireRecord(document.domains, `domains in ${source.filePath}`);\n for (const [name, definition] of Object.entries(sourceDomains)) {\n domains[name] = {\n ...parseDomain(definition, `domains.${name} in ${source.filePath}`),\n baseDirectory: source.baseDirectory,\n };\n }\n }\n if (Object.hasOwn(document, 'defaultDomains')) defaultDomains = parseDefaultDomains(document.defaultDomains);\n }\n\n const plugins = loadPluginCatalog(\n sources.map((source) => ({\n filePath: source.filePath,\n baseDirectory: source.baseDirectory,\n value: requireRecord(source.document, source.filePath).plugins,\n })),\n repoRoot,\n homeDirectory,\n );\n\n if (defaultDomains) {\n for (const name of expandDomainNames({ plugins, domains, aliases }, defaultDomains)) {\n if (!Object.hasOwn(domains, name)) {\n throw new Error(`Unknown default domain \"${name}\" in ${DEFAULT_DOMAINS_PATH}`);\n }\n }\n }\n return { plugins, domains, aliases, ...(defaultDomains ? { defaultDomains } : {}) };\n}\n\nexport function listDomainNames(repoRoot: string, homeDirectory?: string): string[] {\n const manifest = loadDomains(repoRoot, homeDirectory);\n return [...new Set([...Object.keys(manifest.domains), ...Object.keys(manifest.aliases)])].sort();\n}\n\n/**\n * Whether to include .claude/skills as shared fallbacks.\n *\n * Shared skills are included unless every selected domain opts out, so mixing a\n * trimmed domain with an untrimmed one cannot strip the untrimmed one's skills.\n */\nexport function resolveSharedSkills(repoRoot: string, domainNames: string[], homeDirectory?: string): boolean {\n const manifest = loadDomains(repoRoot, homeDirectory);\n const expanded = expandDomainNames(manifest, domainNames);\n if (expanded.length === 0) return true;\n return expanded.some((name) => manifest.domains[name]?.sharedSkills !== false);\n}\n\n/** Resolves named domain plugins while preserving each subset filter. */\nexport function resolvePluginEntries(\n repoRoot: string,\n domainNames: string[],\n explicitDirectories: string[],\n homeDirectory: string = os.homedir(),\n): PluginEntry[] {\n const manifest = loadDomains(repoRoot, homeDirectory);\n const entries: PluginEntry[] = [];\n const seen = new Set<string>();\n const homeDoomDirectory = globalDoomConfigDirectory(homeDirectory);\n\n for (const domainName of expandDomainNames(manifest, domainNames)) {\n const domain = manifest.domains[domainName];\n if (!domain) throw new Error(`${UNKNOWN_DOMAIN_ERROR}: ${domainName}`);\n for (const selection of domain.plugins ?? []) {\n const isSubset = typeof selection !== 'string';\n const pluginName = isSubset ? selection.name : selection;\n const plugin = manifest.plugins.entries[pluginName];\n if (!plugin) throw new Error(`${UNKNOWN_PLUGIN_ERROR} \"${pluginName}\" referenced by domain \"${domainName}\"`);\n const directory = pluginDirectoryForSource(plugin.source, homeDoomDirectory);\n if (plugin.source.type === 'local' && (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory())) {\n throw new Error(`${MISSING_PLUGIN_DIRECTORY_ERROR}: ${directory}`);\n }\n if (seen.has(directory)) continue;\n seen.add(directory);\n entries.push({\n name: pluginName,\n directory,\n source: plugin.source,\n ...(plugin.manifest?.agentPluginSchema === SUPPORTED_AGENT_PLUGIN_SCHEMA\n ? { skillDiscovery: 'direct-children' as const }\n : {}),\n ...(isSubset && selection.skills ? { skills: selection.skills } : {}),\n ...(isSubset && selection.agents ? { agents: selection.agents } : {}),\n ...(isSubset && selection.hooks !== undefined ? { hooks: selection.hooks } : {}),\n ...(isSubset && selection.mcp !== undefined ? { mcp: selection.mcp } : {}),\n });\n }\n }\n\n for (const configuredDirectory of explicitDirectories) {\n const directory = path.resolve(configuredDirectory);\n if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {\n throw new Error(`${MISSING_PLUGIN_DIRECTORY_ERROR}: ${directory}`);\n }\n if (seen.has(directory)) continue;\n seen.add(directory);\n entries.push({\n name: path.basename(directory),\n directory,\n source: { type: 'local', path: directory },\n });\n }\n return entries;\n}\n\nexport function resolvePluginDirectories(\n repoRoot: string,\n domainNames: string[],\n explicitDirectories: string[],\n homeDirectory?: string,\n): string[] {\n return resolvePluginEntries(repoRoot, domainNames, explicitDirectories, homeDirectory).map(\n (entry) => entry.directory,\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);const n=new Set([`profileEnvironment`,`pluginHooks`]),r={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,workflowExtension:`DOOMPI_WORKFLOW_EXTENSION`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function i(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e?.split(t.default.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function o(e){return e!==`0`}function s(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function u(e=process.env,t){let n=r,u=e[n.hookGroups];return{root:e[n.root]||void 0,majorMode:e[n.majorMode]||`copilot`,temporaryDirectory:e[n.temporaryDirectory]||void 0,domains:i(e[n.domains]),layers:i(e[n.layers]),profile:e[n.profile]||void 0,profileEnvironment:s(e[n.profileEnvironment],n.profileEnvironment,t),hookGroups:u===void 0?void 0:i(u),skillDirectories:a(e[n.skillDirectories]),agentDirectories:a(e[n.agentDirectories]),additionalDirectories:a(e[n.additionalDirectories]),childExtensions:c(e[n.childExtensions],n.childExtensions,t),pluginDirectories:a(e[n.pluginDirectories]),pluginHooks:l(e[n.pluginHooks],n.pluginHooks,t),mcpConfigPath:e[n.mcpConfigPath]||void 0,personaFile:e[n.personaFile]||void 0,workflowExtension:e[n.workflowExtension]||void 0,hooks:o(e[n.hooks]),agents:o(e[n.agents]),mcp:o(e[n.mcp]),allowProtectedWrites:o(e[n.allowProtectedWrites])}}function d(e,n=process.env){let i=(e,t)=>{t===void 0?delete n[e]:n[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=r;return`root`in e&&i(o.root,e.root),`majorMode`in e&&i(o.majorMode,e.majorMode),`temporaryDirectory`in e&&i(o.temporaryDirectory,e.temporaryDirectory),`domains`in e&&i(o.domains,e.domains?.join(`,`)),`layers`in e&&i(o.layers,e.layers?.join(`,`)),`profile`in e&&i(o.profile,e.profile),`hookGroups`in e&&i(o.hookGroups,e.hookGroups?.join(`,`)),`skillDirectories`in e&&i(o.skillDirectories,e.skillDirectories?.join(t.default.delimiter)),`agentDirectories`in e&&i(o.agentDirectories,e.agentDirectories?.join(t.default.delimiter)),`additionalDirectories`in e&&i(o.additionalDirectories,e.additionalDirectories?.join(t.default.delimiter)),`childExtensions`in e&&i(o.childExtensions,JSON.stringify(e.childExtensions??[])),`pluginDirectories`in e&&i(o.pluginDirectories,e.pluginDirectories?.join(t.default.delimiter)),`mcpConfigPath`in e&&i(o.mcpConfigPath,e.mcpConfigPath),`personaFile`in e&&i(o.personaFile,e.personaFile),`workflowExtension`in e&&i(o.workflowExtension,e.workflowExtension),e.hooks!==void 0&&a(o.hooks,e.hooks),e.agents!==void 0&&a(o.agents,e.agents),e.mcp!==void 0&&a(o.mcp,e.mcp),e.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,e.allowProtectedWrites),n}exports.FILE_ONLY_STATE_FIELDS=n,exports.HARNESS_STATE_KEYS=r,exports.projectHarnessEnvironment=d,exports.readHarnessState=u;
2
+ //# sourceMappingURL=harnessState.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.cjs","names":["path"],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * Both are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment, and neither has a reader outside this\n * package: the plugin hook list is consumed by the Pi hooks extension and the\n * profile environment by the profile switcher, both of which read the store.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set(['profileEnvironment', 'pluginHooks']);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n workflowExtension: 'DOOMPI_WORKFLOW_EXTENSION',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n workflowExtension: environment[keys.workflowExtension] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if ('workflowExtension' in patch) assign(keys.workflowExtension, patch.workflowExtension);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { HarnessState } from "../types/config.cjs";
2
+ //#region src/adapters/harnessState.d.ts
3
+ /**
4
+ * State that lives in the file only.
5
+ *
6
+ * Both are JSON documents that every spawned process and every hook would
7
+ * otherwise copy in its environment, and neither has a reader outside this
8
+ * package: the plugin hook list is consumed by the Pi hooks extension and the
9
+ * profile environment by the profile switcher, both of which read the store.
10
+ */
11
+ declare const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState>;
12
+ type HarnessStateParseReporter = (key: string, error: unknown) => void;
13
+ declare const HARNESS_STATE_KEYS: {
14
+ readonly root: "DOOMPI_ROOT";
15
+ readonly majorMode: "DOOMPI_MAJOR_MODE";
16
+ readonly temporaryDirectory: "DOOMPI_TEMP_DIR";
17
+ readonly domains: "DOOMPI_DOMAINS";
18
+ readonly layers: "DOOMPI_LAYERS";
19
+ readonly profile: "DOOMPI_PROFILE";
20
+ readonly profileEnvironment: "DOOMPI_PROFILE_ENV";
21
+ readonly hookGroups: "DOOMPI_HOOK_GROUPS";
22
+ readonly skillDirectories: "DOOMPI_SKILL_DIRS";
23
+ readonly agentDirectories: "DOOMPI_AGENT_DIRS";
24
+ readonly additionalDirectories: "DOOMPI_ADDITIONAL_DIRS";
25
+ readonly childExtensions: "DOOMPI_CHILD_EXTENSIONS";
26
+ readonly pluginDirectories: "DOOMPI_PLUGIN_DIRS";
27
+ readonly pluginHooks: "DOOMPI_PLUGIN_HOOKS";
28
+ readonly mcpConfigPath: "DOOMPI_MCP_CONFIG";
29
+ readonly personaFile: "DOOMPI_PERSONA_FILE";
30
+ readonly workflowExtension: "DOOMPI_WORKFLOW_EXTENSION";
31
+ readonly hooks: "DOOMPI_HOOKS_ENABLED";
32
+ readonly agents: "DOOMPI_AGENTS_ENABLED";
33
+ readonly mcp: "DOOMPI_MCP_ENABLED";
34
+ readonly allowProtectedWrites: "DOOMPI_ALLOW_PROTECTED_WRITES";
35
+ };
36
+ declare function readHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): HarnessState;
37
+ /**
38
+ * Publishes a state patch into an environment.
39
+ *
40
+ * Takes the environment rather than assuming `process.env`, because the
41
+ * launcher builds a child environment and Doom Team builds a spawn snapshot;
42
+ * neither should mutate its own process to describe another one.
43
+ *
44
+ * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader
45
+ * that needs them reads the store.
46
+ */
47
+ declare function projectHarnessEnvironment(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
48
+ //#endregion
49
+ export { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState };
50
+ //# sourceMappingURL=harnessState.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.d.cts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;cAwBa,wBAAwB,kBAAkB;KAE3C,6BAA6B,aAAa;cAEzC;;;;;;;;;;;;;;;;;;;;;;;iBAiGG,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;iBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -0,0 +1,50 @@
1
+ import { HarnessState } from "../types/config.mjs";
2
+ //#region src/adapters/harnessState.d.ts
3
+ /**
4
+ * State that lives in the file only.
5
+ *
6
+ * Both are JSON documents that every spawned process and every hook would
7
+ * otherwise copy in its environment, and neither has a reader outside this
8
+ * package: the plugin hook list is consumed by the Pi hooks extension and the
9
+ * profile environment by the profile switcher, both of which read the store.
10
+ */
11
+ declare const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState>;
12
+ type HarnessStateParseReporter = (key: string, error: unknown) => void;
13
+ declare const HARNESS_STATE_KEYS: {
14
+ readonly root: "DOOMPI_ROOT";
15
+ readonly majorMode: "DOOMPI_MAJOR_MODE";
16
+ readonly temporaryDirectory: "DOOMPI_TEMP_DIR";
17
+ readonly domains: "DOOMPI_DOMAINS";
18
+ readonly layers: "DOOMPI_LAYERS";
19
+ readonly profile: "DOOMPI_PROFILE";
20
+ readonly profileEnvironment: "DOOMPI_PROFILE_ENV";
21
+ readonly hookGroups: "DOOMPI_HOOK_GROUPS";
22
+ readonly skillDirectories: "DOOMPI_SKILL_DIRS";
23
+ readonly agentDirectories: "DOOMPI_AGENT_DIRS";
24
+ readonly additionalDirectories: "DOOMPI_ADDITIONAL_DIRS";
25
+ readonly childExtensions: "DOOMPI_CHILD_EXTENSIONS";
26
+ readonly pluginDirectories: "DOOMPI_PLUGIN_DIRS";
27
+ readonly pluginHooks: "DOOMPI_PLUGIN_HOOKS";
28
+ readonly mcpConfigPath: "DOOMPI_MCP_CONFIG";
29
+ readonly personaFile: "DOOMPI_PERSONA_FILE";
30
+ readonly workflowExtension: "DOOMPI_WORKFLOW_EXTENSION";
31
+ readonly hooks: "DOOMPI_HOOKS_ENABLED";
32
+ readonly agents: "DOOMPI_AGENTS_ENABLED";
33
+ readonly mcp: "DOOMPI_MCP_ENABLED";
34
+ readonly allowProtectedWrites: "DOOMPI_ALLOW_PROTECTED_WRITES";
35
+ };
36
+ declare function readHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): HarnessState;
37
+ /**
38
+ * Publishes a state patch into an environment.
39
+ *
40
+ * Takes the environment rather than assuming `process.env`, because the
41
+ * launcher builds a child environment and Doom Team builds a spawn snapshot;
42
+ * neither should mutate its own process to describe another one.
43
+ *
44
+ * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader
45
+ * that needs them reads the store.
46
+ */
47
+ declare function projectHarnessEnvironment(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
48
+ //#endregion
49
+ export { FILE_ONLY_STATE_FIELDS, HARNESS_STATE_KEYS, HarnessStateParseReporter, projectHarnessEnvironment, readHarnessState };
50
+ //# sourceMappingURL=harnessState.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.d.mts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;cAwBa,wBAAwB,kBAAkB;KAE3C,6BAA6B,aAAa;cAEzC;;;;;;;;;;;;;;;;;;;;;;;iBAiGG,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;iBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";const t=new Set([`profileEnvironment`,`pluginHooks`]),n={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,workflowExtension:`DOOMPI_WORKFLOW_EXTENSION`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function r(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function i(t){return t?.split(e.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e!==`0`}function o(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function s(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e=process.env,t){let l=n,u=e[l.hookGroups];return{root:e[l.root]||void 0,majorMode:e[l.majorMode]||`copilot`,temporaryDirectory:e[l.temporaryDirectory]||void 0,domains:r(e[l.domains]),layers:r(e[l.layers]),profile:e[l.profile]||void 0,profileEnvironment:o(e[l.profileEnvironment],l.profileEnvironment,t),hookGroups:u===void 0?void 0:r(u),skillDirectories:i(e[l.skillDirectories]),agentDirectories:i(e[l.agentDirectories]),additionalDirectories:i(e[l.additionalDirectories]),childExtensions:s(e[l.childExtensions],l.childExtensions,t),pluginDirectories:i(e[l.pluginDirectories]),pluginHooks:c(e[l.pluginHooks],l.pluginHooks,t),mcpConfigPath:e[l.mcpConfigPath]||void 0,personaFile:e[l.personaFile]||void 0,workflowExtension:e[l.workflowExtension]||void 0,hooks:a(e[l.hooks]),agents:a(e[l.agents]),mcp:a(e[l.mcp]),allowProtectedWrites:a(e[l.allowProtectedWrites])}}function u(t,r=process.env){let i=(e,t)=>{t===void 0?delete r[e]:r[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=n;return`root`in t&&i(o.root,t.root),`majorMode`in t&&i(o.majorMode,t.majorMode),`temporaryDirectory`in t&&i(o.temporaryDirectory,t.temporaryDirectory),`domains`in t&&i(o.domains,t.domains?.join(`,`)),`layers`in t&&i(o.layers,t.layers?.join(`,`)),`profile`in t&&i(o.profile,t.profile),`hookGroups`in t&&i(o.hookGroups,t.hookGroups?.join(`,`)),`skillDirectories`in t&&i(o.skillDirectories,t.skillDirectories?.join(e.delimiter)),`agentDirectories`in t&&i(o.agentDirectories,t.agentDirectories?.join(e.delimiter)),`additionalDirectories`in t&&i(o.additionalDirectories,t.additionalDirectories?.join(e.delimiter)),`childExtensions`in t&&i(o.childExtensions,JSON.stringify(t.childExtensions??[])),`pluginDirectories`in t&&i(o.pluginDirectories,t.pluginDirectories?.join(e.delimiter)),`mcpConfigPath`in t&&i(o.mcpConfigPath,t.mcpConfigPath),`personaFile`in t&&i(o.personaFile,t.personaFile),`workflowExtension`in t&&i(o.workflowExtension,t.workflowExtension),t.hooks!==void 0&&a(o.hooks,t.hooks),t.agents!==void 0&&a(o.agents,t.agents),t.mcp!==void 0&&a(o.mcp,t.mcp),t.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,t.allowProtectedWrites),r}export{t as FILE_ONLY_STATE_FIELDS,n as HARNESS_STATE_KEYS,u as projectHarnessEnvironment,l as readHarnessState};
2
+ //# sourceMappingURL=harnessState.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessState.mjs","names":[],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * Both are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment, and neither has a reader outside this\n * package: the plugin hook list is consumed by the Pi hooks extension and the\n * profile environment by the profile switcher, both of which read the store.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set(['profileEnvironment', 'pluginHooks']);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n workflowExtension: 'DOOMPI_WORKFLOW_EXTENSION',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n workflowExtension: environment[keys.workflowExtension] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if ('workflowExtension' in patch) assign(keys.workflowExtension, patch.workflowExtension);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./atomicJson.cjs"),n=require("./harnessState.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);const o=`DOOMPI_STATE`;let s;function c(){s=void 0}function l(e,t){let n=JSON.parse(e);if(n.version!==1)throw Error(`Harness state at ${t} has version ${String(n.version)}, expected 1`);if(typeof n.state!=`object`||n.state===null)throw Error(`Harness state at ${t} has no state object`);return{version:1,owner:{pid:n.owner?.pid??-1,startedAt:n.owner?.startedAt??``},state:n.state}}function u(e=process.env,t){let i=e===process.env;if(i&&s)return s;let a=e[o],c;if(a&&r.default.existsSync(a))try{let e=l(r.default.readFileSync(a,`utf8`),a);c={filePath:a,owned:e.owner.pid===process.pid||e.owner.pid===0,state:e.state}}catch(e){t?.(o,e)}return c??={owned:!1,state:n.readHarnessState(e,t)},i&&(s=c),c}function d(e){return u(process.env,e).state}function f(e){return i.default.join(e,`harness-state.json`)}function p(){return i.default.join(a.default.tmpdir(),`doom-harness`,`${String(process.pid)}.json`)}function m(e,n,r=process.pid){t.writePrivateAtomicJson(e,{version:1,owner:{pid:r,startedAt:new Date().toISOString()},state:n})}function h(e,t){let r=f(t.directory);return m(r,e,t.unclaimed?0:process.pid),t.environment[o]=r,n.projectHarnessEnvironment(e,t.environment),!t.unclaimed&&t.environment===process.env&&(s={filePath:r,owned:!0,state:e}),r}function g(e,t=process.env){let r=u(t),i={...r.state,...e},a=r.owned&&r.filePath?r.filePath:p();return m(a,i),t[o]=a,t===process.env&&(s={filePath:a,owned:!0,state:i}),n.projectHarnessEnvironment(e,t),i}function _(e=process.env){let t=s;c(),!(!t?.owned||!t.filePath)&&(r.default.rmSync(t.filePath,{force:!0}),delete e[o])}function v(e=d()){return e.root??process.cwd()}function y(e=d()){if(!e.root)throw Error(`${n.HARNESS_STATE_KEYS.root} is not set`);return e.root}function b(e=d()){let t=y(e);if(!e.temporaryDirectory)throw Error(`${n.HARNESS_STATE_KEYS.temporaryDirectory} is not set`);return{root:t,temporaryDirectory:e.temporaryDirectory}}exports.HARNESS_STATE_POINTER=o,exports.createHarnessSession=h,exports.disposeHarnessState=_,exports.getHarnessState=d,exports.harnessRoot=v,exports.loadHarnessState=u,exports.requireHarnessPaths=b,exports.requireHarnessRoot=y,exports.resetHarnessStore=c,exports.updateHarnessState=g;
2
+ //# sourceMappingURL=harnessStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.cjs","names":["fs","readHarnessState","path","os","HARNESS_STATE_KEYS"],"sources":["../../src/adapters/harnessStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { HarnessState } from '../types/config.ts';\nimport { writePrivateAtomicJson } from './atomicJson.ts';\nimport {\n HARNESS_STATE_KEYS,\n type HarnessStateParseReporter,\n projectHarnessEnvironment,\n readHarnessState,\n} from './harnessState.ts';\n\n/**\n * Who owns the harness state, and where it lives.\n *\n * The state used to be the environment: twenty variables that every reader\n * re-parsed and every writer mutated globally. The file is the authority now,\n * and the environment is a projection published for the readers that can only\n * see an environment, which are bash hooks, the shell launchers, packages that\n * do not depend on this one, and any process spawned by any of them.\n *\n * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:\n * Doom Team subagents run detached, in their own process group, from an\n * environment snapshot taken at spawn. A child can outlive its parent. If it\n * shared the parent's file it would corrupt a session it does not own, and if\n * that file sat in the parent's run directory the parent's shutdown sweep would\n * delete it from under a live run. So every process writes a file it owns:\n * `owner.pid` records who, and a process that finds someone else's file copies\n * it before its first write.\n *\n * AVOID:\n * - Treating the file as shared mutable state between processes. Atomic writes\n * prevent torn reads, not lost updates\n * - Reading the environment directly for anything this exposes\n */\n\n/** Points at the state file. The one variable that is not derived. */\nexport const HARNESS_STATE_POINTER = 'DOOMPI_STATE';\n\nconst STATE_FILE_VERSION = 1;\nconst STATE_FILE_NAME = 'harness-state.json';\nconst ADOPTED_DIRECTORY = 'doom-harness';\n/**\n * Written for a process that does not exist yet, so the first one to read it\n * owns it. A spawner cannot know its child's pid, and a child that inherited\n * its parent's file would lose it the moment the parent cleaned up.\n */\nconst UNCLAIMED_OWNER = 0;\n\nexport interface HarnessStateFile {\n version: number;\n owner: { pid: number; startedAt: string };\n state: HarnessState;\n}\n\nexport interface LoadedHarnessState {\n /** Undefined when this process is running off the environment fallback. */\n filePath?: string;\n owned: boolean;\n state: HarnessState;\n}\n\nlet loaded: LoadedHarnessState | undefined;\n\n/** Drops the cached state. For tests, and for a process changing identity. */\nexport function resetHarnessStore(): void {\n loaded = undefined;\n}\n\nfunction parseStateFile(source: string, filePath: string): HarnessStateFile {\n const parsed = JSON.parse(source) as Partial<HarnessStateFile>;\n if (parsed.version !== STATE_FILE_VERSION) {\n throw new Error(\n `Harness state at ${filePath} has version ${String(parsed.version)}, expected ${STATE_FILE_VERSION}`,\n );\n }\n if (typeof parsed.state !== 'object' || parsed.state === null) {\n throw new Error(`Harness state at ${filePath} has no state object`);\n }\n return {\n version: STATE_FILE_VERSION,\n owner: { pid: parsed.owner?.pid ?? -1, startedAt: parsed.owner?.startedAt ?? '' },\n state: parsed.state as HarnessState,\n };\n}\n\n/**\n * Reads the state for this process, once.\n *\n * A missing pointer means nobody wrote a file for this process, so the\n * environment is all there is: a nested run, a third-party spawn, or a caller\n * older than the store. A pointer that cannot be read is reported and then\n * treated the same way, because a session that starts degraded beats a session\n * that does not start at all.\n */\nexport function loadHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): LoadedHarnessState {\n // The cache describes this process. Asking about another environment is a\n // question about another process, so it reads through rather than answering\n // from what this one happens to hold.\n const cacheable = environment === process.env;\n if (cacheable && loaded) return loaded;\n\n const filePath = environment[HARNESS_STATE_POINTER];\n let result: LoadedHarnessState | undefined;\n if (filePath && fs.existsSync(filePath)) {\n try {\n const file = parseStateFile(fs.readFileSync(filePath, 'utf8'), filePath);\n const owned = file.owner.pid === process.pid || file.owner.pid === UNCLAIMED_OWNER;\n result = { filePath, owned, state: file.state };\n } catch (error) {\n report?.(HARNESS_STATE_POINTER, error);\n }\n }\n result ??= { owned: false, state: readHarnessState(environment, report) };\n if (cacheable) loaded = result;\n return result;\n}\n\nexport function getHarnessState(report?: HarnessStateParseReporter): HarnessState {\n return loadHarnessState(process.env, report).state;\n}\n\nfunction stateFilePath(directory: string): string {\n return path.join(directory, STATE_FILE_NAME);\n}\n\n/** Where a process writes state when it was not given a file of its own. */\nfunction adoptedStateFilePath(): string {\n return path.join(os.tmpdir(), ADOPTED_DIRECTORY, `${String(process.pid)}.json`);\n}\n\nfunction persist(filePath: string, state: HarnessState, ownerPid = process.pid): void {\n writePrivateAtomicJson(filePath, {\n version: STATE_FILE_VERSION,\n owner: { pid: ownerPid, startedAt: new Date().toISOString() },\n state,\n } satisfies HarnessStateFile);\n}\n\n/**\n * Starts a session: writes the file and publishes the derived environment.\n *\n * The environment is a parameter because the caller is usually describing a\n * process other than itself. The launcher builds the environment for the Pi it\n * is about to spawn, and Doom Team builds the snapshot for a detached child, so\n * neither should touch its own `process.env` to do it.\n */\nexport function createHarnessSession(\n state: HarnessState,\n options: {\n directory: string;\n environment: NodeJS.ProcessEnv;\n /**\n * Leave the file for a process that does not exist yet to claim.\n *\n * What a spawner wants: the child owns its state from its first read, in a\n * directory the spawner manages, so the parent cleaning up its own session\n * cannot pull the file out from under a detached run.\n */\n unclaimed?: boolean;\n },\n): string {\n const filePath = stateFilePath(options.directory);\n persist(filePath, state, options.unclaimed ? UNCLAIMED_OWNER : process.pid);\n options.environment[HARNESS_STATE_POINTER] = filePath;\n projectHarnessEnvironment(state, options.environment);\n // Only prime the cache when this process is the one being described.\n if (!options.unclaimed && options.environment === process.env) loaded = { filePath, owned: true, state };\n return filePath;\n}\n\n/**\n * Applies a patch to this process's state.\n *\n * Copy on write: a process holding a file it does not own writes its own copy\n * and repoints itself at it, so a child can never rewrite its parent's session.\n */\nexport function updateHarnessState(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): HarnessState {\n const current = loadHarnessState(environment);\n const state = { ...current.state, ...patch };\n const filePath = current.owned && current.filePath ? current.filePath : adoptedStateFilePath();\n persist(filePath, state);\n environment[HARNESS_STATE_POINTER] = filePath;\n if (environment === process.env) loaded = { filePath, owned: true, state };\n projectHarnessEnvironment(patch, environment);\n return state;\n}\n\n/** Removes this process's own state file. Safe when there is none. */\nexport function disposeHarnessState(environment: NodeJS.ProcessEnv = process.env): void {\n const current = loaded;\n resetHarnessStore();\n if (!current?.owned || !current.filePath) return;\n fs.rmSync(current.filePath, { force: true });\n delete environment[HARNESS_STATE_POINTER];\n}\n\nexport function harnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n return state.root ?? process.cwd();\n}\n\n/**\n * The repository root, or a hard error.\n *\n * Separate from `requireHarnessPaths` because most callers only need the root,\n * and separate from `harnessRoot` because falling back to `process.cwd()` would\n * quietly operate on whatever directory the session happens to sit in. A caller\n * that asks for the root wants the session's repository or nothing.\n */\nexport function requireHarnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n if (!state.root) throw new Error(`${HARNESS_STATE_KEYS.root} is not set`);\n return state.root;\n}\n\n/** The root and the run's scratch directory, for callers that write to disk. */\nexport function requireHarnessPaths(state: Pick<HarnessState, 'root' | 'temporaryDirectory'> = getHarnessState()): {\n root: string;\n temporaryDirectory: string;\n} {\n const root = requireHarnessRoot(state);\n if (!state.temporaryDirectory) throw new Error(`${HARNESS_STATE_KEYS.temporaryDirectory} is not set`);\n return { root, temporaryDirectory: state.temporaryDirectory };\n}\n"],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { HarnessState } from "../types/config.cjs";
2
+ import { HarnessStateParseReporter } from "./harnessState.cjs";
3
+ //#region src/adapters/harnessStore.d.ts
4
+ /**
5
+ * Who owns the harness state, and where it lives.
6
+ *
7
+ * The state used to be the environment: twenty variables that every reader
8
+ * re-parsed and every writer mutated globally. The file is the authority now,
9
+ * and the environment is a projection published for the readers that can only
10
+ * see an environment, which are bash hooks, the shell launchers, packages that
11
+ * do not depend on this one, and any process spawned by any of them.
12
+ *
13
+ * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:
14
+ * Doom Team subagents run detached, in their own process group, from an
15
+ * environment snapshot taken at spawn. A child can outlive its parent. If it
16
+ * shared the parent's file it would corrupt a session it does not own, and if
17
+ * that file sat in the parent's run directory the parent's shutdown sweep would
18
+ * delete it from under a live run. So every process writes a file it owns:
19
+ * `owner.pid` records who, and a process that finds someone else's file copies
20
+ * it before its first write.
21
+ *
22
+ * AVOID:
23
+ * - Treating the file as shared mutable state between processes. Atomic writes
24
+ * prevent torn reads, not lost updates
25
+ * - Reading the environment directly for anything this exposes
26
+ */
27
+ /** Points at the state file. The one variable that is not derived. */
28
+ declare const HARNESS_STATE_POINTER = "DOOMPI_STATE";
29
+ interface HarnessStateFile {
30
+ version: number;
31
+ owner: {
32
+ pid: number;
33
+ startedAt: string;
34
+ };
35
+ state: HarnessState;
36
+ }
37
+ interface LoadedHarnessState {
38
+ /** Undefined when this process is running off the environment fallback. */
39
+ filePath?: string;
40
+ owned: boolean;
41
+ state: HarnessState;
42
+ }
43
+ /** Drops the cached state. For tests, and for a process changing identity. */
44
+ declare function resetHarnessStore(): void;
45
+ /**
46
+ * Reads the state for this process, once.
47
+ *
48
+ * A missing pointer means nobody wrote a file for this process, so the
49
+ * environment is all there is: a nested run, a third-party spawn, or a caller
50
+ * older than the store. A pointer that cannot be read is reported and then
51
+ * treated the same way, because a session that starts degraded beats a session
52
+ * that does not start at all.
53
+ */
54
+ declare function loadHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): LoadedHarnessState;
55
+ declare function getHarnessState(report?: HarnessStateParseReporter): HarnessState;
56
+ /**
57
+ * Starts a session: writes the file and publishes the derived environment.
58
+ *
59
+ * The environment is a parameter because the caller is usually describing a
60
+ * process other than itself. The launcher builds the environment for the Pi it
61
+ * is about to spawn, and Doom Team builds the snapshot for a detached child, so
62
+ * neither should touch its own `process.env` to do it.
63
+ */
64
+ declare function createHarnessSession(state: HarnessState, options: {
65
+ directory: string;
66
+ environment: NodeJS.ProcessEnv;
67
+ /**
68
+ * Leave the file for a process that does not exist yet to claim.
69
+ *
70
+ * What a spawner wants: the child owns its state from its first read, in a
71
+ * directory the spawner manages, so the parent cleaning up its own session
72
+ * cannot pull the file out from under a detached run.
73
+ */
74
+ unclaimed?: boolean;
75
+ }): string;
76
+ /**
77
+ * Applies a patch to this process's state.
78
+ *
79
+ * Copy on write: a process holding a file it does not own writes its own copy
80
+ * and repoints itself at it, so a child can never rewrite its parent's session.
81
+ */
82
+ declare function updateHarnessState(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): HarnessState;
83
+ /** Removes this process's own state file. Safe when there is none. */
84
+ declare function disposeHarnessState(environment?: NodeJS.ProcessEnv): void;
85
+ declare function harnessRoot(state?: Pick<HarnessState, 'root'>): string;
86
+ /**
87
+ * The repository root, or a hard error.
88
+ *
89
+ * Separate from `requireHarnessPaths` because most callers only need the root,
90
+ * and separate from `harnessRoot` because falling back to `process.cwd()` would
91
+ * quietly operate on whatever directory the session happens to sit in. A caller
92
+ * that asks for the root wants the session's repository or nothing.
93
+ */
94
+ declare function requireHarnessRoot(state?: Pick<HarnessState, 'root'>): string;
95
+ /** The root and the run's scratch directory, for callers that write to disk. */
96
+ declare function requireHarnessPaths(state?: Pick<HarnessState, 'root' | 'temporaryDirectory'>): {
97
+ root: string;
98
+ temporaryDirectory: string;
99
+ };
100
+ //#endregion
101
+ export { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState };
102
+ //# sourceMappingURL=harnessStore.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.d.cts","names":[],"sources":["../../src/adapters/harnessStore.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cAqCa;UAYI;EACf;EACA;IAAS;IAAa;;EACtB,OAAO;;UAGQ;;EAEf;EACA;EACA,OAAO;;;iBAMO;;;;;;;;;;iBA8BA,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;iBAuBa,gBAAgB,SAAS,4BAA4B;;;;;;;;;iBA6BrD,qBACd,OAAO,cACP;EACE;EACA,aAAa,OAAO;;;;;;;;EAQpB;;;;;;;;iBAkBY,mBACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB;;iBAYa,oBAAoB,cAAa,OAAO;iBAQxC,YAAY,QAAO,KAAK;;;;;;;;;iBAYxB,mBAAmB,QAAO,KAAK;;iBAM/B,oBAAoB,QAAO,KAAK;EAC9C;EACA"}
@@ -0,0 +1,102 @@
1
+ import { HarnessState } from "../types/config.mjs";
2
+ import { HarnessStateParseReporter } from "./harnessState.mjs";
3
+ //#region src/adapters/harnessStore.d.ts
4
+ /**
5
+ * Who owns the harness state, and where it lives.
6
+ *
7
+ * The state used to be the environment: twenty variables that every reader
8
+ * re-parsed and every writer mutated globally. The file is the authority now,
9
+ * and the environment is a projection published for the readers that can only
10
+ * see an environment, which are bash hooks, the shell launchers, packages that
11
+ * do not depend on this one, and any process spawned by any of them.
12
+ *
13
+ * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:
14
+ * Doom Team subagents run detached, in their own process group, from an
15
+ * environment snapshot taken at spawn. A child can outlive its parent. If it
16
+ * shared the parent's file it would corrupt a session it does not own, and if
17
+ * that file sat in the parent's run directory the parent's shutdown sweep would
18
+ * delete it from under a live run. So every process writes a file it owns:
19
+ * `owner.pid` records who, and a process that finds someone else's file copies
20
+ * it before its first write.
21
+ *
22
+ * AVOID:
23
+ * - Treating the file as shared mutable state between processes. Atomic writes
24
+ * prevent torn reads, not lost updates
25
+ * - Reading the environment directly for anything this exposes
26
+ */
27
+ /** Points at the state file. The one variable that is not derived. */
28
+ declare const HARNESS_STATE_POINTER = "DOOMPI_STATE";
29
+ interface HarnessStateFile {
30
+ version: number;
31
+ owner: {
32
+ pid: number;
33
+ startedAt: string;
34
+ };
35
+ state: HarnessState;
36
+ }
37
+ interface LoadedHarnessState {
38
+ /** Undefined when this process is running off the environment fallback. */
39
+ filePath?: string;
40
+ owned: boolean;
41
+ state: HarnessState;
42
+ }
43
+ /** Drops the cached state. For tests, and for a process changing identity. */
44
+ declare function resetHarnessStore(): void;
45
+ /**
46
+ * Reads the state for this process, once.
47
+ *
48
+ * A missing pointer means nobody wrote a file for this process, so the
49
+ * environment is all there is: a nested run, a third-party spawn, or a caller
50
+ * older than the store. A pointer that cannot be read is reported and then
51
+ * treated the same way, because a session that starts degraded beats a session
52
+ * that does not start at all.
53
+ */
54
+ declare function loadHarnessState(environment?: NodeJS.ProcessEnv, report?: HarnessStateParseReporter): LoadedHarnessState;
55
+ declare function getHarnessState(report?: HarnessStateParseReporter): HarnessState;
56
+ /**
57
+ * Starts a session: writes the file and publishes the derived environment.
58
+ *
59
+ * The environment is a parameter because the caller is usually describing a
60
+ * process other than itself. The launcher builds the environment for the Pi it
61
+ * is about to spawn, and Doom Team builds the snapshot for a detached child, so
62
+ * neither should touch its own `process.env` to do it.
63
+ */
64
+ declare function createHarnessSession(state: HarnessState, options: {
65
+ directory: string;
66
+ environment: NodeJS.ProcessEnv;
67
+ /**
68
+ * Leave the file for a process that does not exist yet to claim.
69
+ *
70
+ * What a spawner wants: the child owns its state from its first read, in a
71
+ * directory the spawner manages, so the parent cleaning up its own session
72
+ * cannot pull the file out from under a detached run.
73
+ */
74
+ unclaimed?: boolean;
75
+ }): string;
76
+ /**
77
+ * Applies a patch to this process's state.
78
+ *
79
+ * Copy on write: a process holding a file it does not own writes its own copy
80
+ * and repoints itself at it, so a child can never rewrite its parent's session.
81
+ */
82
+ declare function updateHarnessState(patch: Partial<HarnessState>, environment?: NodeJS.ProcessEnv): HarnessState;
83
+ /** Removes this process's own state file. Safe when there is none. */
84
+ declare function disposeHarnessState(environment?: NodeJS.ProcessEnv): void;
85
+ declare function harnessRoot(state?: Pick<HarnessState, 'root'>): string;
86
+ /**
87
+ * The repository root, or a hard error.
88
+ *
89
+ * Separate from `requireHarnessPaths` because most callers only need the root,
90
+ * and separate from `harnessRoot` because falling back to `process.cwd()` would
91
+ * quietly operate on whatever directory the session happens to sit in. A caller
92
+ * that asks for the root wants the session's repository or nothing.
93
+ */
94
+ declare function requireHarnessRoot(state?: Pick<HarnessState, 'root'>): string;
95
+ /** The root and the run's scratch directory, for callers that write to disk. */
96
+ declare function requireHarnessPaths(state?: Pick<HarnessState, 'root' | 'temporaryDirectory'>): {
97
+ root: string;
98
+ temporaryDirectory: string;
99
+ };
100
+ //#endregion
101
+ export { HARNESS_STATE_POINTER, HarnessStateFile, LoadedHarnessState, createHarnessSession, disposeHarnessState, getHarnessState, harnessRoot, loadHarnessState, requireHarnessPaths, requireHarnessRoot, resetHarnessStore, updateHarnessState };
102
+ //# sourceMappingURL=harnessStore.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.d.mts","names":[],"sources":["../../src/adapters/harnessStore.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cAqCa;UAYI;EACf;EACA;IAAS;IAAa;;EACtB,OAAO;;UAGQ;;EAEf;EACA;EACA,OAAO;;;iBAMO;;;;;;;;;;iBA8BA,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;iBAuBa,gBAAgB,SAAS,4BAA4B;;;;;;;;;iBA6BrD,qBACd,OAAO,cACP;EACE;EACA,aAAa,OAAO;;;;;;;;EAQpB;;;;;;;;iBAkBY,mBACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB;;iBAYa,oBAAoB,cAAa,OAAO;iBAQxC,YAAY,QAAO,KAAK;;;;;;;;;iBAYxB,mBAAmB,QAAO,KAAK;;iBAM/B,oBAAoB,QAAO,KAAK;EAC9C;EACA"}
@@ -0,0 +1,2 @@
1
+ import{writePrivateAtomicJson as e}from"./atomicJson.mjs";import{HARNESS_STATE_KEYS as t,projectHarnessEnvironment as n,readHarnessState as r}from"./harnessState.mjs";import i from"node:fs";import a from"node:path";import o from"node:os";const s=`DOOMPI_STATE`;let c;function l(){c=void 0}function u(e,t){let n=JSON.parse(e);if(n.version!==1)throw Error(`Harness state at ${t} has version ${String(n.version)}, expected 1`);if(typeof n.state!=`object`||n.state===null)throw Error(`Harness state at ${t} has no state object`);return{version:1,owner:{pid:n.owner?.pid??-1,startedAt:n.owner?.startedAt??``},state:n.state}}function d(e=process.env,t){let n=e===process.env;if(n&&c)return c;let a=e[s],o;if(a&&i.existsSync(a))try{let e=u(i.readFileSync(a,`utf8`),a);o={filePath:a,owned:e.owner.pid===process.pid||e.owner.pid===0,state:e.state}}catch(e){t?.(s,e)}return o??={owned:!1,state:r(e,t)},n&&(c=o),o}function f(e){return d(process.env,e).state}function p(e){return a.join(e,`harness-state.json`)}function m(){return a.join(o.tmpdir(),`doom-harness`,`${String(process.pid)}.json`)}function h(t,n,r=process.pid){e(t,{version:1,owner:{pid:r,startedAt:new Date().toISOString()},state:n})}function g(e,t){let r=p(t.directory);return h(r,e,t.unclaimed?0:process.pid),t.environment[s]=r,n(e,t.environment),!t.unclaimed&&t.environment===process.env&&(c={filePath:r,owned:!0,state:e}),r}function _(e,t=process.env){let r=d(t),i={...r.state,...e},a=r.owned&&r.filePath?r.filePath:m();return h(a,i),t[s]=a,t===process.env&&(c={filePath:a,owned:!0,state:i}),n(e,t),i}function v(e=process.env){let t=c;l(),!(!t?.owned||!t.filePath)&&(i.rmSync(t.filePath,{force:!0}),delete e[s])}function y(e=f()){return e.root??process.cwd()}function b(e=f()){if(!e.root)throw Error(`${t.root} is not set`);return e.root}function x(e=f()){let n=b(e);if(!e.temporaryDirectory)throw Error(`${t.temporaryDirectory} is not set`);return{root:n,temporaryDirectory:e.temporaryDirectory}}export{s as HARNESS_STATE_POINTER,g as createHarnessSession,v as disposeHarnessState,f as getHarnessState,y as harnessRoot,d as loadHarnessState,x as requireHarnessPaths,b as requireHarnessRoot,l as resetHarnessStore,_ as updateHarnessState};
2
+ //# sourceMappingURL=harnessStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessStore.mjs","names":[],"sources":["../../src/adapters/harnessStore.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { HarnessState } from '../types/config.ts';\nimport { writePrivateAtomicJson } from './atomicJson.ts';\nimport {\n HARNESS_STATE_KEYS,\n type HarnessStateParseReporter,\n projectHarnessEnvironment,\n readHarnessState,\n} from './harnessState.ts';\n\n/**\n * Who owns the harness state, and where it lives.\n *\n * The state used to be the environment: twenty variables that every reader\n * re-parsed and every writer mutated globally. The file is the authority now,\n * and the environment is a projection published for the readers that can only\n * see an environment, which are bash hooks, the shell launchers, packages that\n * do not depend on this one, and any process spawned by any of them.\n *\n * OWNERSHIP IS PER PROCESS, AND THAT IS THE WHOLE DESIGN:\n * Doom Team subagents run detached, in their own process group, from an\n * environment snapshot taken at spawn. A child can outlive its parent. If it\n * shared the parent's file it would corrupt a session it does not own, and if\n * that file sat in the parent's run directory the parent's shutdown sweep would\n * delete it from under a live run. So every process writes a file it owns:\n * `owner.pid` records who, and a process that finds someone else's file copies\n * it before its first write.\n *\n * AVOID:\n * - Treating the file as shared mutable state between processes. Atomic writes\n * prevent torn reads, not lost updates\n * - Reading the environment directly for anything this exposes\n */\n\n/** Points at the state file. The one variable that is not derived. */\nexport const HARNESS_STATE_POINTER = 'DOOMPI_STATE';\n\nconst STATE_FILE_VERSION = 1;\nconst STATE_FILE_NAME = 'harness-state.json';\nconst ADOPTED_DIRECTORY = 'doom-harness';\n/**\n * Written for a process that does not exist yet, so the first one to read it\n * owns it. A spawner cannot know its child's pid, and a child that inherited\n * its parent's file would lose it the moment the parent cleaned up.\n */\nconst UNCLAIMED_OWNER = 0;\n\nexport interface HarnessStateFile {\n version: number;\n owner: { pid: number; startedAt: string };\n state: HarnessState;\n}\n\nexport interface LoadedHarnessState {\n /** Undefined when this process is running off the environment fallback. */\n filePath?: string;\n owned: boolean;\n state: HarnessState;\n}\n\nlet loaded: LoadedHarnessState | undefined;\n\n/** Drops the cached state. For tests, and for a process changing identity. */\nexport function resetHarnessStore(): void {\n loaded = undefined;\n}\n\nfunction parseStateFile(source: string, filePath: string): HarnessStateFile {\n const parsed = JSON.parse(source) as Partial<HarnessStateFile>;\n if (parsed.version !== STATE_FILE_VERSION) {\n throw new Error(\n `Harness state at ${filePath} has version ${String(parsed.version)}, expected ${STATE_FILE_VERSION}`,\n );\n }\n if (typeof parsed.state !== 'object' || parsed.state === null) {\n throw new Error(`Harness state at ${filePath} has no state object`);\n }\n return {\n version: STATE_FILE_VERSION,\n owner: { pid: parsed.owner?.pid ?? -1, startedAt: parsed.owner?.startedAt ?? '' },\n state: parsed.state as HarnessState,\n };\n}\n\n/**\n * Reads the state for this process, once.\n *\n * A missing pointer means nobody wrote a file for this process, so the\n * environment is all there is: a nested run, a third-party spawn, or a caller\n * older than the store. A pointer that cannot be read is reported and then\n * treated the same way, because a session that starts degraded beats a session\n * that does not start at all.\n */\nexport function loadHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): LoadedHarnessState {\n // The cache describes this process. Asking about another environment is a\n // question about another process, so it reads through rather than answering\n // from what this one happens to hold.\n const cacheable = environment === process.env;\n if (cacheable && loaded) return loaded;\n\n const filePath = environment[HARNESS_STATE_POINTER];\n let result: LoadedHarnessState | undefined;\n if (filePath && fs.existsSync(filePath)) {\n try {\n const file = parseStateFile(fs.readFileSync(filePath, 'utf8'), filePath);\n const owned = file.owner.pid === process.pid || file.owner.pid === UNCLAIMED_OWNER;\n result = { filePath, owned, state: file.state };\n } catch (error) {\n report?.(HARNESS_STATE_POINTER, error);\n }\n }\n result ??= { owned: false, state: readHarnessState(environment, report) };\n if (cacheable) loaded = result;\n return result;\n}\n\nexport function getHarnessState(report?: HarnessStateParseReporter): HarnessState {\n return loadHarnessState(process.env, report).state;\n}\n\nfunction stateFilePath(directory: string): string {\n return path.join(directory, STATE_FILE_NAME);\n}\n\n/** Where a process writes state when it was not given a file of its own. */\nfunction adoptedStateFilePath(): string {\n return path.join(os.tmpdir(), ADOPTED_DIRECTORY, `${String(process.pid)}.json`);\n}\n\nfunction persist(filePath: string, state: HarnessState, ownerPid = process.pid): void {\n writePrivateAtomicJson(filePath, {\n version: STATE_FILE_VERSION,\n owner: { pid: ownerPid, startedAt: new Date().toISOString() },\n state,\n } satisfies HarnessStateFile);\n}\n\n/**\n * Starts a session: writes the file and publishes the derived environment.\n *\n * The environment is a parameter because the caller is usually describing a\n * process other than itself. The launcher builds the environment for the Pi it\n * is about to spawn, and Doom Team builds the snapshot for a detached child, so\n * neither should touch its own `process.env` to do it.\n */\nexport function createHarnessSession(\n state: HarnessState,\n options: {\n directory: string;\n environment: NodeJS.ProcessEnv;\n /**\n * Leave the file for a process that does not exist yet to claim.\n *\n * What a spawner wants: the child owns its state from its first read, in a\n * directory the spawner manages, so the parent cleaning up its own session\n * cannot pull the file out from under a detached run.\n */\n unclaimed?: boolean;\n },\n): string {\n const filePath = stateFilePath(options.directory);\n persist(filePath, state, options.unclaimed ? UNCLAIMED_OWNER : process.pid);\n options.environment[HARNESS_STATE_POINTER] = filePath;\n projectHarnessEnvironment(state, options.environment);\n // Only prime the cache when this process is the one being described.\n if (!options.unclaimed && options.environment === process.env) loaded = { filePath, owned: true, state };\n return filePath;\n}\n\n/**\n * Applies a patch to this process's state.\n *\n * Copy on write: a process holding a file it does not own writes its own copy\n * and repoints itself at it, so a child can never rewrite its parent's session.\n */\nexport function updateHarnessState(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): HarnessState {\n const current = loadHarnessState(environment);\n const state = { ...current.state, ...patch };\n const filePath = current.owned && current.filePath ? current.filePath : adoptedStateFilePath();\n persist(filePath, state);\n environment[HARNESS_STATE_POINTER] = filePath;\n if (environment === process.env) loaded = { filePath, owned: true, state };\n projectHarnessEnvironment(patch, environment);\n return state;\n}\n\n/** Removes this process's own state file. Safe when there is none. */\nexport function disposeHarnessState(environment: NodeJS.ProcessEnv = process.env): void {\n const current = loaded;\n resetHarnessStore();\n if (!current?.owned || !current.filePath) return;\n fs.rmSync(current.filePath, { force: true });\n delete environment[HARNESS_STATE_POINTER];\n}\n\nexport function harnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n return state.root ?? process.cwd();\n}\n\n/**\n * The repository root, or a hard error.\n *\n * Separate from `requireHarnessPaths` because most callers only need the root,\n * and separate from `harnessRoot` because falling back to `process.cwd()` would\n * quietly operate on whatever directory the session happens to sit in. A caller\n * that asks for the root wants the session's repository or nothing.\n */\nexport function requireHarnessRoot(state: Pick<HarnessState, 'root'> = getHarnessState()): string {\n if (!state.root) throw new Error(`${HARNESS_STATE_KEYS.root} is not set`);\n return state.root;\n}\n\n/** The root and the run's scratch directory, for callers that write to disk. */\nexport function requireHarnessPaths(state: Pick<HarnessState, 'root' | 'temporaryDirectory'> = getHarnessState()): {\n root: string;\n temporaryDirectory: string;\n} {\n const root = requireHarnessRoot(state);\n if (!state.temporaryDirectory) throw new Error(`${HARNESS_STATE_KEYS.temporaryDirectory} is not set`);\n return { root, temporaryDirectory: state.temporaryDirectory };\n}\n"],"mappings":"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"}