create-principles-disciple 1.121.7 → 1.121.9

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 (461) hide show
  1. package/README.md +13 -1
  2. package/console/dist/server/config/pd-config-store.d.ts +1 -3
  3. package/console/dist/server/config/pd-config-store.js +4 -4
  4. package/console/dist/server/feedback/channels.d.ts +2 -8
  5. package/console/dist/server/feedback/channels.js +0 -11
  6. package/console/dist/server/index.js +34 -5
  7. package/console/dist/server/models/ActivationsConsoleModel.d.ts +28 -1
  8. package/console/dist/server/models/ActivationsConsoleModel.js +72 -30
  9. package/console/dist/server/models/GovernanceExperienceCollector.d.ts +28 -0
  10. package/console/dist/server/models/GovernanceExperienceCollector.js +305 -0
  11. package/console/dist/server/models/GovernanceProjectionCollector.d.ts +60 -2
  12. package/console/dist/server/models/GovernanceProjectionCollector.js +323 -285
  13. package/console/dist/server/models/PrincipleClassifier.d.ts +1 -1
  14. package/console/dist/server/models/PrincipleClassifier.js +7 -4
  15. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +2 -1
  16. package/console/dist/server/models/ReceiptsConsoleModel.d.ts +5 -0
  17. package/console/dist/server/models/ReceiptsConsoleModel.js +140 -13
  18. package/console/dist/server/routes/activations.js +1 -1
  19. package/console/dist/server/routes/failed-tasks.js +28 -11
  20. package/console/dist/server/routes/governance.d.ts +26 -0
  21. package/console/dist/server/routes/governance.js +51 -0
  22. package/console/dist/server/routes/owner-identity.d.ts +31 -0
  23. package/console/dist/server/routes/owner-identity.js +76 -0
  24. package/console/dist/server/routes/update-history.d.ts +5 -0
  25. package/console/dist/server/routes/update-history.js +50 -4
  26. package/console/dist/server/routes/update.js +186 -32
  27. package/console/dist/shared/feedback-channel-ids.d.ts +13 -0
  28. package/console/dist/shared/feedback-channel-ids.js +12 -0
  29. package/console/dist/shared/feedback-contract.d.ts +46 -0
  30. package/console/dist/shared/feedback-contract.js +45 -0
  31. package/console/dist/ui/App.js +1 -1
  32. package/console/dist/ui/api.d.ts +22 -3
  33. package/console/dist/ui/api.js +63 -3
  34. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.d.ts +6 -0
  35. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.js +66 -0
  36. package/console/dist/ui/i18n/en.json +198 -2
  37. package/console/dist/ui/i18n/zh-CN.json +196 -0
  38. package/console/dist/ui/pages/activation/ActivationPage.js +14 -2
  39. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +25 -5
  40. package/console/dist/ui/pages/focus/FocusPage.d.ts +28 -1
  41. package/console/dist/ui/pages/focus/FocusPage.js +128 -15
  42. package/console/dist/ui/pages/principles/PrincipleDetailPage.d.ts +30 -0
  43. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +49 -6
  44. package/console/dist/ui/pages/settings/SettingsPage.js +67 -2
  45. package/console/dist/ui/pages/settings/UpdatePage.js +7 -7
  46. package/console/dist/ui/utils/enum-labels.js +0 -1
  47. package/console/dist/ui/utils/validators.d.ts +53 -21
  48. package/console/dist/ui/utils/validators.js +248 -21
  49. package/console/dist/web/assets/app.css +6 -0
  50. package/console/dist/web/assets/app.js +2721 -1664
  51. package/console/package.json +4 -2
  52. package/core/dist/host/host-adapter.d.ts +1 -1
  53. package/core/dist/host/host-adapter.d.ts.map +1 -1
  54. package/core/dist/host/host-adapter.js +1 -0
  55. package/core/dist/host/host-adapter.js.map +1 -1
  56. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +12 -2
  57. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  58. package/core/dist/runtime-v2/__tests__/attack-e2e-pipeline-smoke.test.js +1 -1
  59. package/core/dist/runtime-v2/__tests__/attack-e2e-pipeline-smoke.test.js.map +1 -1
  60. package/core/dist/runtime-v2/__tests__/governance-experience.test.d.ts +2 -0
  61. package/core/dist/runtime-v2/__tests__/governance-experience.test.d.ts.map +1 -0
  62. package/core/dist/runtime-v2/__tests__/governance-experience.test.js +300 -0
  63. package/core/dist/runtime-v2/__tests__/governance-experience.test.js.map +1 -0
  64. package/core/dist/runtime-v2/__tests__/owner-identity.test.d.ts +2 -0
  65. package/core/dist/runtime-v2/__tests__/owner-identity.test.d.ts.map +1 -0
  66. package/core/dist/runtime-v2/__tests__/owner-identity.test.js +147 -0
  67. package/core/dist/runtime-v2/__tests__/owner-identity.test.js.map +1 -0
  68. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.d.ts +18 -0
  69. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.d.ts.map +1 -0
  70. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.js +303 -0
  71. package/core/dist/runtime-v2/__tests__/product-telemetry-contract.test.js.map +1 -0
  72. package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js +40 -0
  73. package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js.map +1 -1
  74. package/core/dist/runtime-v2/__tests__/status-contract.test.d.ts +2 -0
  75. package/core/dist/runtime-v2/__tests__/status-contract.test.d.ts.map +1 -0
  76. package/core/dist/runtime-v2/__tests__/status-contract.test.js +107 -0
  77. package/core/dist/runtime-v2/__tests__/status-contract.test.js.map +1 -0
  78. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.d.ts +2 -0
  79. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.d.ts.map +1 -0
  80. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.js +104 -0
  81. package/core/dist/runtime-v2/activation/__tests__/promotion-evidence-snapshot.test.js.map +1 -0
  82. package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js +19 -0
  83. package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js.map +1 -1
  84. package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js +20 -0
  85. package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js.map +1 -1
  86. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js +40 -0
  87. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js.map +1 -1
  88. package/core/dist/runtime-v2/activation/index.d.ts +2 -0
  89. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  90. package/core/dist/runtime-v2/activation/index.js +1 -0
  91. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  92. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.d.ts +30 -0
  93. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.d.ts.map +1 -0
  94. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.js +87 -0
  95. package/core/dist/runtime-v2/activation/promotion-evidence-snapshot.js.map +1 -0
  96. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts.map +1 -1
  97. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js +3 -0
  98. package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js.map +1 -1
  99. package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts +1 -1
  100. package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts.map +1 -1
  101. package/core/dist/runtime-v2/activation/promotion-readiness-reader.js +2 -2
  102. package/core/dist/runtime-v2/activation/promotion-readiness-reader.js.map +1 -1
  103. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts.map +1 -1
  104. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js +18 -9
  105. package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js.map +1 -1
  106. package/core/dist/runtime-v2/adapter/__tests__/chaos-json-repair.test.js +1 -1
  107. package/core/dist/runtime-v2/adapter/__tests__/chaos-json-repair.test.js.map +1 -1
  108. package/core/dist/runtime-v2/adapter/__tests__/json-extractor.test.js +91 -1
  109. package/core/dist/runtime-v2/adapter/__tests__/json-extractor.test.js.map +1 -1
  110. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +59 -12
  111. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
  112. package/core/dist/runtime-v2/adapter/__tests__/runtime-config-contract.test.js +10 -9
  113. package/core/dist/runtime-v2/adapter/__tests__/runtime-config-contract.test.js.map +1 -1
  114. package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js +42 -1
  115. package/core/dist/runtime-v2/adapter/__tests__/structured-output-repair.test.js.map +1 -1
  116. package/core/dist/runtime-v2/adapter/json-extractor.d.ts +25 -0
  117. package/core/dist/runtime-v2/adapter/json-extractor.d.ts.map +1 -1
  118. package/core/dist/runtime-v2/adapter/json-extractor.js +126 -0
  119. package/core/dist/runtime-v2/adapter/json-extractor.js.map +1 -1
  120. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +9 -4
  121. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
  122. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +9 -4
  123. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
  124. package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts +5 -1
  125. package/core/dist/runtime-v2/adapter/output-repair-contract.d.ts.map +1 -1
  126. package/core/dist/runtime-v2/adapter/output-repair-contract.js +4 -1
  127. package/core/dist/runtime-v2/adapter/output-repair-contract.js.map +1 -1
  128. package/core/dist/runtime-v2/adapter/pi-ai-catalog.d.ts +7 -0
  129. package/core/dist/runtime-v2/adapter/pi-ai-catalog.d.ts.map +1 -0
  130. package/core/dist/runtime-v2/adapter/pi-ai-catalog.js +19 -0
  131. package/core/dist/runtime-v2/adapter/pi-ai-catalog.js.map +1 -0
  132. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +13 -15
  133. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  134. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +104 -37
  135. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  136. package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts +16 -1
  137. package/core/dist/runtime-v2/adapter/structured-output-repair.d.ts.map +1 -1
  138. package/core/dist/runtime-v2/adapter/structured-output-repair.js +38 -6
  139. package/core/dist/runtime-v2/adapter/structured-output-repair.js.map +1 -1
  140. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts +1 -1
  141. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts.map +1 -1
  142. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js +8 -9
  143. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js.map +1 -1
  144. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +27 -3
  145. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
  146. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.d.ts +11 -0
  147. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.d.ts.map +1 -0
  148. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.js +93 -0
  149. package/core/dist/runtime-v2/config/__tests__/pd-config-principles.test.js.map +1 -0
  150. package/core/dist/runtime-v2/config/__tests__/pd-config-workspace.test.js +41 -0
  151. package/core/dist/runtime-v2/config/__tests__/pd-config-workspace.test.js.map +1 -1
  152. package/core/dist/runtime-v2/config/index.d.ts +2 -2
  153. package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
  154. package/core/dist/runtime-v2/config/index.js +2 -0
  155. package/core/dist/runtime-v2/config/index.js.map +1 -1
  156. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
  157. package/core/dist/runtime-v2/config/pd-config-agent-binding.js +4 -13
  158. package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
  159. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
  160. package/core/dist/runtime-v2/config/pd-config-effective.js +14 -7
  161. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
  162. package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts.map +1 -1
  163. package/core/dist/runtime-v2/config/pd-config-feature-flags.js +5 -12
  164. package/core/dist/runtime-v2/config/pd-config-feature-flags.js.map +1 -1
  165. package/core/dist/runtime-v2/config/pd-config-types.d.ts +8 -0
  166. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  167. package/core/dist/runtime-v2/config/pd-config-types.js +2 -0
  168. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  169. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  170. package/core/dist/runtime-v2/config/pd-config-validate.js +48 -3
  171. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  172. package/core/dist/runtime-v2/core-principles/__tests__/core-axiom-block.test.js +36 -8
  173. package/core/dist/runtime-v2/core-principles/__tests__/core-axiom-block.test.js.map +1 -1
  174. package/core/dist/runtime-v2/core-principles/__tests__/core-principle-registry.test.js +36 -12
  175. package/core/dist/runtime-v2/core-principles/__tests__/core-principle-registry.test.js.map +1 -1
  176. package/core/dist/runtime-v2/core-principles/core-axiom-block.d.ts +22 -8
  177. package/core/dist/runtime-v2/core-principles/core-axiom-block.d.ts.map +1 -1
  178. package/core/dist/runtime-v2/core-principles/core-axiom-block.js +28 -13
  179. package/core/dist/runtime-v2/core-principles/core-axiom-block.js.map +1 -1
  180. package/core/dist/runtime-v2/core-principles/core-principle-registry.d.ts +29 -4
  181. package/core/dist/runtime-v2/core-principles/core-principle-registry.d.ts.map +1 -1
  182. package/core/dist/runtime-v2/core-principles/core-principle-registry.js +68 -33
  183. package/core/dist/runtime-v2/core-principles/core-principle-registry.js.map +1 -1
  184. package/core/dist/runtime-v2/core-principles/index.d.ts +3 -3
  185. package/core/dist/runtime-v2/core-principles/index.d.ts.map +1 -1
  186. package/core/dist/runtime-v2/core-principles/index.js +1 -1
  187. package/core/dist/runtime-v2/core-principles/index.js.map +1 -1
  188. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +2 -1
  189. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  190. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  191. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.d.ts +2 -0
  192. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.d.ts.map +1 -0
  193. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.js +48 -0
  194. package/core/dist/runtime-v2/feature-flags/__tests__/anonymous-product-telemetry-flag.test.js.map +1 -0
  195. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.d.ts +2 -0
  196. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.d.ts.map +1 -0
  197. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.js +46 -0
  198. package/core/dist/runtime-v2/feature-flags/__tests__/codex-conversation-ingestion-flag.test.js.map +1 -0
  199. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +86 -31
  200. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  201. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.d.ts +2 -0
  202. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.d.ts.map +1 -0
  203. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.js +83 -0
  204. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-lifecycle.test.js.map +1 -0
  205. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts +19 -1
  206. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  207. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +128 -10
  208. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  209. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts +37 -0
  210. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -0
  211. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +304 -0
  212. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -0
  213. package/core/dist/runtime-v2/feature-flags/index.d.ts +4 -2
  214. package/core/dist/runtime-v2/feature-flags/index.d.ts.map +1 -1
  215. package/core/dist/runtime-v2/feature-flags/index.js +3 -1
  216. package/core/dist/runtime-v2/feature-flags/index.js.map +1 -1
  217. package/core/dist/runtime-v2/governance-experience-contract.d.ts +468 -0
  218. package/core/dist/runtime-v2/governance-experience-contract.d.ts.map +1 -0
  219. package/core/dist/runtime-v2/governance-experience-contract.js +208 -0
  220. package/core/dist/runtime-v2/governance-experience-contract.js.map +1 -0
  221. package/core/dist/runtime-v2/governance-experience.d.ts +11 -0
  222. package/core/dist/runtime-v2/governance-experience.d.ts.map +1 -0
  223. package/core/dist/runtime-v2/governance-experience.js +276 -0
  224. package/core/dist/runtime-v2/governance-experience.js.map +1 -0
  225. package/core/dist/runtime-v2/index.d.ts +20 -8
  226. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  227. package/core/dist/runtime-v2/index.js +20 -6
  228. package/core/dist/runtime-v2/index.js.map +1 -1
  229. package/core/dist/runtime-v2/internalization/__tests__/artificer-output-retry-flag.test.js +29 -10
  230. package/core/dist/runtime-v2/internalization/__tests__/artificer-output-retry-flag.test.js.map +1 -1
  231. package/core/dist/runtime-v2/owner-identity.d.ts +69 -0
  232. package/core/dist/runtime-v2/owner-identity.d.ts.map +1 -0
  233. package/core/dist/runtime-v2/owner-identity.js +179 -0
  234. package/core/dist/runtime-v2/owner-identity.js.map +1 -0
  235. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  236. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +2 -3
  237. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  238. package/core/dist/runtime-v2/product-telemetry/daily-identity.d.ts +70 -0
  239. package/core/dist/runtime-v2/product-telemetry/daily-identity.d.ts.map +1 -0
  240. package/core/dist/runtime-v2/product-telemetry/daily-identity.js +89 -0
  241. package/core/dist/runtime-v2/product-telemetry/daily-identity.js.map +1 -0
  242. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +110 -0
  243. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -0
  244. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +250 -0
  245. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -0
  246. package/core/dist/runtime-v2/pruning-read-model.d.ts +2 -1
  247. package/core/dist/runtime-v2/pruning-read-model.d.ts.map +1 -1
  248. package/core/dist/runtime-v2/pruning-read-model.js.map +1 -1
  249. package/core/dist/runtime-v2/receipt-coverage.d.ts +63 -0
  250. package/core/dist/runtime-v2/receipt-coverage.d.ts.map +1 -0
  251. package/core/dist/runtime-v2/receipt-coverage.js +19 -0
  252. package/core/dist/runtime-v2/receipt-coverage.js.map +1 -0
  253. package/core/dist/runtime-v2/recovery-actions-log.d.ts +7 -0
  254. package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -1
  255. package/core/dist/runtime-v2/recovery-actions-log.js +6 -0
  256. package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -1
  257. package/core/dist/runtime-v2/runtime-protocol.js +1 -1
  258. package/core/dist/runtime-v2/runtime-protocol.js.map +1 -1
  259. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store.failed-tasks.test.js +17 -1
  260. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store.failed-tasks.test.js.map +1 -1
  261. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  262. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +10 -0
  263. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  264. package/core/dist/runtime-v2/store/task/task-types.d.ts +2 -0
  265. package/core/dist/runtime-v2/store/task/task-types.d.ts.map +1 -1
  266. package/core/dist/runtime-v2/task-status.d.ts +9 -5
  267. package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
  268. package/core/dist/runtime-v2/task-status.js +15 -7
  269. package/core/dist/runtime-v2/task-status.js.map +1 -1
  270. package/core/dist/runtime-v2/types/event-types.d.ts +4 -0
  271. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  272. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  273. package/core/dist/runtime-v2/types/index.d.ts +1 -1
  274. package/core/dist/runtime-v2/types/index.d.ts.map +1 -1
  275. package/core/dist/runtime-v2/types/index.js +1 -1
  276. package/core/dist/runtime-v2/types/index.js.map +1 -1
  277. package/core/dist/runtime-v2/types/principle-enums.d.ts +3 -2
  278. package/core/dist/runtime-v2/types/principle-enums.d.ts.map +1 -1
  279. package/core/dist/runtime-v2/types/principle-enums.js +11 -5
  280. package/core/dist/runtime-v2/types/principle-enums.js.map +1 -1
  281. package/core/dist/runtime-v2/types/principle-schema.d.ts +1 -1
  282. package/core/dist/runtime-v2/types/principle-tree-store.d.ts +1 -1
  283. package/core/dist/runtime-v2/utils/cli-process-runner.d.ts.map +1 -1
  284. package/core/dist/runtime-v2/utils/cli-process-runner.js +3 -2
  285. package/core/dist/runtime-v2/utils/cli-process-runner.js.map +1 -1
  286. package/core/package.json +4 -5
  287. package/dist/i18n.d.ts.map +1 -1
  288. package/dist/i18n.js +4 -2
  289. package/dist/i18n.js.map +1 -1
  290. package/dist/index.d.ts +2 -0
  291. package/dist/index.d.ts.map +1 -1
  292. package/dist/index.js +47 -11
  293. package/dist/index.js.map +1 -1
  294. package/dist/installer.d.ts +31 -5
  295. package/dist/installer.d.ts.map +1 -1
  296. package/dist/installer.js +472 -161
  297. package/dist/installer.js.map +1 -1
  298. package/dist/installers/codex-host-installer.d.ts.map +1 -1
  299. package/dist/installers/codex-host-installer.js +4 -0
  300. package/dist/installers/codex-host-installer.js.map +1 -1
  301. package/dist/mvp-config.d.ts +10 -0
  302. package/dist/mvp-config.d.ts.map +1 -1
  303. package/dist/mvp-config.js +30 -3
  304. package/dist/mvp-config.js.map +1 -1
  305. package/dist/uninstaller.d.ts +9 -1
  306. package/dist/uninstaller.d.ts.map +1 -1
  307. package/dist/uninstaller.js +103 -23
  308. package/dist/uninstaller.js.map +1 -1
  309. package/dist/update/atomic-file.d.ts +35 -0
  310. package/dist/update/atomic-file.d.ts.map +1 -0
  311. package/dist/update/atomic-file.js +120 -0
  312. package/dist/update/atomic-file.js.map +1 -0
  313. package/dist/update/bootstrap-protocol.d.ts +55 -0
  314. package/dist/update/bootstrap-protocol.d.ts.map +1 -0
  315. package/dist/update/bootstrap-protocol.js +132 -0
  316. package/dist/update/bootstrap-protocol.js.map +1 -0
  317. package/dist/update/channel-metadata.d.ts +28 -0
  318. package/dist/update/channel-metadata.d.ts.map +1 -0
  319. package/dist/update/channel-metadata.js +90 -0
  320. package/dist/update/channel-metadata.js.map +1 -0
  321. package/dist/update/channel-promotion.d.ts +39 -0
  322. package/dist/update/channel-promotion.d.ts.map +1 -0
  323. package/dist/update/channel-promotion.js +55 -0
  324. package/dist/update/channel-promotion.js.map +1 -0
  325. package/dist/update/data-compatibility.d.ts +35 -0
  326. package/dist/update/data-compatibility.d.ts.map +1 -0
  327. package/dist/update/data-compatibility.js +45 -0
  328. package/dist/update/data-compatibility.js.map +1 -0
  329. package/dist/update/install-layout.d.ts +59 -0
  330. package/dist/update/install-layout.d.ts.map +1 -0
  331. package/dist/update/install-layout.js +127 -0
  332. package/dist/update/install-layout.js.map +1 -0
  333. package/dist/update/legacy-migration.d.ts +56 -0
  334. package/dist/update/legacy-migration.d.ts.map +1 -0
  335. package/dist/update/legacy-migration.js +254 -0
  336. package/dist/update/legacy-migration.js.map +1 -0
  337. package/dist/update/product-identity.d.ts +50 -0
  338. package/dist/update/product-identity.d.ts.map +1 -0
  339. package/dist/update/product-identity.js +63 -0
  340. package/dist/update/product-identity.js.map +1 -0
  341. package/dist/update/release-asset-manifest.d.ts +31 -0
  342. package/dist/update/release-asset-manifest.d.ts.map +1 -0
  343. package/dist/update/release-asset-manifest.js +161 -0
  344. package/dist/update/release-asset-manifest.js.map +1 -0
  345. package/dist/update/release-identity.d.ts +33 -0
  346. package/dist/update/release-identity.d.ts.map +1 -0
  347. package/dist/update/release-identity.js +108 -0
  348. package/dist/update/release-identity.js.map +1 -0
  349. package/dist/update/release-manager.d.ts +96 -0
  350. package/dist/update/release-manager.d.ts.map +1 -0
  351. package/dist/update/release-manager.js +253 -0
  352. package/dist/update/release-manager.js.map +1 -0
  353. package/dist/update/release-metadata.d.ts +57 -0
  354. package/dist/update/release-metadata.d.ts.map +1 -0
  355. package/dist/update/release-metadata.js +200 -0
  356. package/dist/update/release-metadata.js.map +1 -0
  357. package/dist/update/release-policy.d.ts +51 -0
  358. package/dist/update/release-policy.d.ts.map +1 -0
  359. package/dist/update/release-policy.js +103 -0
  360. package/dist/update/release-policy.js.map +1 -0
  361. package/dist/update/rollback-policy.d.ts +56 -0
  362. package/dist/update/rollback-policy.d.ts.map +1 -0
  363. package/dist/update/rollback-policy.js +61 -0
  364. package/dist/update/rollback-policy.js.map +1 -0
  365. package/dist/update/transaction-journal.d.ts +121 -0
  366. package/dist/update/transaction-journal.d.ts.map +1 -0
  367. package/dist/update/transaction-journal.js +333 -0
  368. package/dist/update/transaction-journal.js.map +1 -0
  369. package/dist/update/trust-metadata.d.ts +55 -0
  370. package/dist/update/trust-metadata.d.ts.map +1 -0
  371. package/dist/update/trust-metadata.js +170 -0
  372. package/dist/update/trust-metadata.js.map +1 -0
  373. package/dist/update/update-history.d.ts +46 -0
  374. package/dist/update/update-history.d.ts.map +1 -0
  375. package/dist/update/update-history.js +123 -0
  376. package/dist/update/update-history.js.map +1 -0
  377. package/dist/utils/env.d.ts +7 -0
  378. package/dist/utils/env.d.ts.map +1 -1
  379. package/dist/utils/env.js +134 -44
  380. package/dist/utils/env.js.map +1 -1
  381. package/host-runtime/dist/governance-observation-store.d.ts +172 -0
  382. package/host-runtime/dist/governance-observation-store.js +649 -0
  383. package/host-runtime/dist/index.d.ts +6 -0
  384. package/host-runtime/dist/index.js +8 -0
  385. package/host-runtime/dist/product-telemetry/consent-store.d.ts +107 -0
  386. package/host-runtime/dist/product-telemetry/consent-store.js +329 -0
  387. package/host-runtime/dist/product-telemetry/eligibility.d.ts +53 -0
  388. package/host-runtime/dist/product-telemetry/eligibility.js +69 -0
  389. package/host-runtime/dist/product-telemetry/exporter.d.ts +46 -0
  390. package/host-runtime/dist/product-telemetry/exporter.js +71 -0
  391. package/host-runtime/dist/product-telemetry/milestone-readers.d.ts +61 -0
  392. package/host-runtime/dist/product-telemetry/milestone-readers.js +234 -0
  393. package/host-runtime/dist/product-telemetry/service.d.ts +142 -0
  394. package/host-runtime/dist/product-telemetry/service.js +532 -0
  395. package/host-runtime/dist/product-telemetry/workspace-scope.d.ts +38 -0
  396. package/host-runtime/dist/product-telemetry/workspace-scope.js +64 -0
  397. package/host-runtime/package.json +7 -2
  398. package/install-layout/dist/index.d.ts +48 -0
  399. package/install-layout/dist/index.js +81 -0
  400. package/install-layout/dist/index.test.d.ts +1 -0
  401. package/install-layout/dist/index.test.js +34 -0
  402. package/install-layout/package.json +33 -0
  403. package/package.json +16 -6
  404. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +9 -0
  405. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +1 -0
  406. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.js +76 -0
  407. package/pd-cli/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +1 -0
  408. package/pd-cli/dist/commands/__tests__/version.test.d.ts +2 -0
  409. package/pd-cli/dist/commands/__tests__/version.test.d.ts.map +1 -0
  410. package/pd-cli/dist/commands/__tests__/version.test.js +134 -0
  411. package/pd-cli/dist/commands/__tests__/version.test.js.map +1 -0
  412. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  413. package/pd-cli/dist/commands/console.js +29 -10
  414. package/pd-cli/dist/commands/console.js.map +1 -1
  415. package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
  416. package/pd-cli/dist/commands/pain-record.js +6 -15
  417. package/pd-cli/dist/commands/pain-record.js.map +1 -1
  418. package/pd-cli/dist/commands/runtime-activation.d.ts +29 -0
  419. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  420. package/pd-cli/dist/commands/runtime-activation.js +70 -31
  421. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  422. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
  423. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js +2 -2
  424. package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
  425. package/pd-cli/dist/commands/telemetry.d.ts +82 -0
  426. package/pd-cli/dist/commands/telemetry.d.ts.map +1 -0
  427. package/pd-cli/dist/commands/telemetry.js +268 -0
  428. package/pd-cli/dist/commands/telemetry.js.map +1 -0
  429. package/pd-cli/dist/commands/version.d.ts +11 -0
  430. package/pd-cli/dist/commands/version.d.ts.map +1 -0
  431. package/pd-cli/dist/commands/version.js +48 -0
  432. package/pd-cli/dist/commands/version.js.map +1 -0
  433. package/pd-cli/dist/index.js +35 -2
  434. package/pd-cli/dist/index.js.map +1 -1
  435. package/pd-cli/dist/services/version-report.d.ts +44 -0
  436. package/pd-cli/dist/services/version-report.d.ts.map +1 -0
  437. package/pd-cli/dist/services/version-report.js +152 -0
  438. package/pd-cli/dist/services/version-report.js.map +1 -0
  439. package/pd-cli/package.json +3 -2
  440. package/plugin/dist/bundle.js +870 -932
  441. package/plugin/dist/core/init.d.ts +2 -1
  442. package/plugin/dist/core/principle-application-ledger.d.ts +10 -0
  443. package/plugin/dist/core/product-telemetry-trigger.d.ts +14 -0
  444. package/plugin/dist/core/thinking-os-parser.d.ts +5 -2
  445. package/plugin/dist/governance-audit.js +386 -0
  446. package/plugin/dist/hooks/gate-block-helper.d.ts +25 -1
  447. package/plugin/dist/hooks/gate.d.ts +22 -0
  448. package/plugin/dist/openclaw.plugin.json +1 -1
  449. package/plugin/dist/rulehost-evidence.js +832 -0
  450. package/plugin/dist/templates/langs/en/principles/THINKING_OS.md +53 -45
  451. package/plugin/dist/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  452. package/plugin/dist/templates/langs/zh/principles/THINKING_OS.md +62 -46
  453. package/plugin/dist/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  454. package/plugin/dist/templates/workspace/.principles/THINKING_OS.md +66 -38
  455. package/plugin/openclaw.plugin.json +1 -1
  456. package/plugin/package.json +1 -1
  457. package/plugin/templates/langs/en/principles/THINKING_OS.md +53 -45
  458. package/plugin/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  459. package/plugin/templates/langs/zh/principles/THINKING_OS.md +62 -46
  460. package/plugin/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  461. package/plugin/templates/workspace/.principles/THINKING_OS.md +66 -38
@@ -0,0 +1,532 @@
1
+ /**
2
+ * Product telemetry service — Anonymous Product Telemetry v1
3
+ * (PRI-597/598/599 orchestration; review remediation: workspace unit).
4
+ *
5
+ * Single seam for the CLI control plane (`pd telemetry status/enable/disable/
6
+ * preview/reset`) and the two fire-and-forget export triggers (OpenClaw plugin
7
+ * workspace init, pd-console startup). Export requires ALL gates:
8
+ *
9
+ * canExport = feature flag anonymous_product_telemetry (workspace config)
10
+ * AND consent granted (~/.pd/product-telemetry.json)
11
+ * AND environment not suppressed (eligibility.ts)
12
+ *
13
+ * Scope model (review remediation P1-1): the MEASUREMENT UNIT is the
14
+ * workspace. Consent, consentVersion, and the telemetry secret are
15
+ * machine-scope; the daily identity, dedup, retry bookkeeping, and export
16
+ * lock are workspace-scope. One workspace's success/backoff/lock never
17
+ * suppresses another workspace on the same installation.
18
+ */
19
+ import fs from 'node:fs';
20
+ import os from 'node:os';
21
+ import path from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ import { bucketDateFromTime, buildProductTelemetrySnapshot, deriveDailyTelemetryId, isValidTelemetrySecretHex, } from '@principles/core/runtime-v2';
24
+ import { getInstallLayoutPaths, parseInstallManifest, resolveInstallLayout } from '@principles/install-layout';
25
+ import { loadFeatureFlagFromConfig, loadPdConfigForPlugin } from '../pd-config.js';
26
+ import { deniedControlState, defaultProductTelemetryControlState, getProductTelemetryStatePath, grantedControlState, pruneWorkspaceExports, readProductTelemetryControlState, resetControlState, writeProductTelemetryControlState, } from './consent-store.js';
27
+ import { computeTelemetryEnvironment } from './eligibility.js';
28
+ import { DEFAULT_PRODUCT_TELEMETRY_ENDPOINT, exportSnapshot, nextRetryDelayMs } from './exporter.js';
29
+ import { readMilestoneFacts } from './milestone-readers.js';
30
+ import { workspaceExportLockPath, workspaceScopeIdFor } from './workspace-scope.js';
31
+ export const ANONYMOUS_PRODUCT_TELEMETRY_FLAG = 'anonymous_product_telemetry';
32
+ export const PD_TELEMETRY_DISABLED_ENV = 'PD_TELEMETRY_DISABLED';
33
+ export const PREVIEW_BANNER = 'Preview only. Nothing was sent.';
34
+ /** Hard cap on export attempts per UTC day per workspace (1 initial + 4 retries; review round 2). */
35
+ export const MAX_DAILY_EXPORT_ATTEMPTS = 5;
36
+ /** A lock older than this is treated as orphaned (holder crashed) and taken over. */
37
+ const EXPORT_LOCK_STALE_MS = 5 * 60 * 1000;
38
+ /**
39
+ * The state-update critical section (fresh read → prune → merge → atomic
40
+ * write) holds for a few ms, so the poll budget and stale threshold are
41
+ * tiny compared to the export lock's — a crashed holder blocks bookkeeping
42
+ * for seconds, not minutes, and live holders can never be evicted (the
43
+ * hold is far shorter than the stale threshold).
44
+ */
45
+ const STATE_UPDATE_LOCK_STALE_MS = 5 * 1000;
46
+ const STATE_UPDATE_LOCK_TIMEOUT_MS = 3 * 1000;
47
+ const STATE_UPDATE_LOCK_POLL_MS = 15;
48
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
49
+ /**
50
+ * Per-workspace cross-process export lock: exclusive create ('wx') is atomic,
51
+ * so exactly one of N simultaneous holders wins — and the lock name embeds
52
+ * only the opaque scope key, so workspace A's in-flight export never blocks
53
+ * workspace B's (review remediation P1-1). Stale takeover uses mtime — a
54
+ * crashed holder can block that workspace for at most EXPORT_LOCK_STALE_MS.
55
+ * Never throws: any filesystem failure degrades to "not acquired"
56
+ * (telemetry skips, PD unaffected).
57
+ */
58
+ function tryAcquireExportLock(stateFilePath, workspaceScopeId) {
59
+ const lockPath = workspaceExportLockPath(stateFilePath, workspaceScopeId);
60
+ const create = () => {
61
+ try {
62
+ const fd = fs.openSync(lockPath, 'wx');
63
+ try {
64
+ fs.writeSync(fd, `${process.pid} ${new Date().toISOString()}`);
65
+ }
66
+ finally {
67
+ fs.closeSync(fd);
68
+ }
69
+ return true;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ };
75
+ if (create())
76
+ return { acquired: true, lockPath };
77
+ try {
78
+ const stat = fs.statSync(lockPath);
79
+ if (Date.now() - stat.mtimeMs > EXPORT_LOCK_STALE_MS) {
80
+ fs.rmSync(lockPath, { force: true });
81
+ if (create())
82
+ return { acquired: true, lockPath };
83
+ }
84
+ }
85
+ catch {
86
+ // stat/rm failure → treat as busy; never throw for telemetry
87
+ }
88
+ return { acquired: false };
89
+ }
90
+ function releaseExportLock(lockPath) {
91
+ try {
92
+ fs.rmSync(lockPath, { force: true });
93
+ }
94
+ catch {
95
+ // best effort; a leaked lock is reclaimed after EXPORT_LOCK_STALE_MS
96
+ }
97
+ }
98
+ /**
99
+ * Machine-scope state-update lock: serializes ONLY the control-state file
100
+ * read→merge→write across workspaces (a few ms each) — the network export
101
+ * itself stays fully concurrent. Review round 4: without it, two
102
+ * concurrently-completing exports built their writes from the same stale
103
+ * base and the later write silently dropped the earlier workspace's entry
104
+ * (lost update), defeating once/day dedup, backoff, and the attempt cap.
105
+ *
106
+ * Lock acquisition polls asynchronously (never blocks the event loop) and
107
+ * takes over a stale (crashed-holder) lock after STATE_UPDATE_LOCK_STALE_MS.
108
+ */
109
+ async function acquireStateUpdateLock(stateFilePath) {
110
+ const lockPath = `${stateFilePath}.state-lock`;
111
+ const deadline = Date.now() + STATE_UPDATE_LOCK_TIMEOUT_MS;
112
+ const create = () => {
113
+ try {
114
+ const fd = fs.openSync(lockPath, 'wx');
115
+ try {
116
+ fs.writeSync(fd, `${process.pid} ${new Date().toISOString()}`);
117
+ }
118
+ finally {
119
+ fs.closeSync(fd);
120
+ }
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ };
127
+ for (;;) {
128
+ if (create())
129
+ return { acquired: true, lockPath };
130
+ try {
131
+ const stat = fs.statSync(lockPath);
132
+ if (Date.now() - stat.mtimeMs > STATE_UPDATE_LOCK_STALE_MS) {
133
+ fs.rmSync(lockPath, { force: true });
134
+ if (create())
135
+ return { acquired: true, lockPath };
136
+ }
137
+ }
138
+ catch {
139
+ // disappeared between poll attempts → next loop's create decides
140
+ }
141
+ if (Date.now() >= deadline)
142
+ return { acquired: false };
143
+ await sleep(STATE_UPDATE_LOCK_POLL_MS);
144
+ }
145
+ }
146
+ /**
147
+ * Merge ONE workspace's export entry into the control state inside the
148
+ * state-update critical section. The entry itself is built by the caller
149
+ * (its inputs are stable for this workspace under the per-workspace export
150
+ * lock); the OTHER workspaces' entries come from a FRESH read under the
151
+ * lock, so concurrent completions compose instead of overwrite. Identity
152
+ * transitions that raced the export (reset/disable/secret rotation mid-
153
+ * flight) are detected and win — an in-flight result is never merged into
154
+ * a rotated/denied identity. Best-effort by contract: every non-written
155
+ * outcome is observable at the call site via structured logs (rc-9).
156
+ */
157
+ async function mergeWorkspaceExportEntry(args) {
158
+ const stateFilePath = getProductTelemetryStatePath(args.homeDir);
159
+ const lock = await acquireStateUpdateLock(stateFilePath);
160
+ if (!lock.acquired)
161
+ return 'skipped_contention';
162
+ try {
163
+ const read = readProductTelemetryControlState(args.homeDir);
164
+ if (!read.ok)
165
+ return 'skipped_unreadable';
166
+ if (read.state.consent !== 'granted' || read.state.telemetrySecret !== args.secret)
167
+ return 'skipped_identity_changed';
168
+ const pruned = pruneWorkspaceExports(read.state, args.timestamp);
169
+ const nextState = {
170
+ ...pruned,
171
+ workspaceExports: { ...(pruned.workspaceExports ?? {}), [args.scopeKey]: args.nextEntry },
172
+ };
173
+ return writeProductTelemetryControlState(args.homeDir, nextState).ok ? 'written' : 'skipped_unreadable';
174
+ }
175
+ finally {
176
+ releaseExportLock(lock.lockPath);
177
+ }
178
+ }
179
+ const DEFAULT_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
180
+ function readJsonFile(filePath) {
181
+ try {
182
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
183
+ }
184
+ catch {
185
+ return undefined;
186
+ }
187
+ }
188
+ function resolveInstallContext(homeDir) {
189
+ const paths = getInstallLayoutPaths(homeDir);
190
+ const manifestParsed = parseInstallManifest(readJsonFile(paths.manifest));
191
+ const manifest = manifestParsed.manifest;
192
+ const canonicalRuntimeExists = fs.existsSync(paths.hostRuntimeDir);
193
+ const legacyExtensionExists = fs.existsSync(paths.openClawExtensionDir);
194
+ const layout = resolveInstallLayout({ homeDir, manifest, canonicalRuntimeExists, legacyExtensionExists });
195
+ let hostKind = 'other';
196
+ if (manifest && manifest.hosts.length === 1 && (manifest.hosts[0] === 'codex' || manifest.hosts[0] === 'openclaw')) {
197
+ [hostKind] = manifest.hosts;
198
+ }
199
+ else if (legacyExtensionExists) {
200
+ hostKind = 'openclaw';
201
+ }
202
+ const pluginVersion = readJsonFile(path.join(paths.pluginDir, 'package.json')) ?? readJsonFile(path.join(paths.openClawExtensionDir, 'package.json'));
203
+ const versionValue = pluginVersion !== null && typeof pluginVersion === 'object' && !Array.isArray(pluginVersion)
204
+ ? pluginVersion.version
205
+ : undefined;
206
+ const pdVersion = typeof versionValue === 'string' ? versionValue.slice(0, 32) : 'unknown';
207
+ return { installMode: layout.mode, hostKind, pdVersion };
208
+ }
209
+ function workspaceEnvironmentOf(workspaceDir) {
210
+ if (!workspaceDir)
211
+ return 'unknown';
212
+ const loaded = loadPdConfigForPlugin(workspaceDir);
213
+ const environment = loaded.effective.config.workspace?.environment;
214
+ return environment ?? 'unknown';
215
+ }
216
+ export function createProductTelemetryService(deps = {}) {
217
+ const homeDir = () => path.resolve(deps.homeDir ?? os.homedir());
218
+ const env = () => deps.env ?? process.env;
219
+ const moduleDir = () => path.resolve(deps.moduleDir ?? DEFAULT_MODULE_DIR);
220
+ const now = () => (deps.now ?? Date.now)();
221
+ const logger = deps.logger ?? {};
222
+ function environmentResult(workspaceDir) {
223
+ return computeTelemetryEnvironment({
224
+ env: {
225
+ killSwitch: env()[PD_TELEMETRY_DISABLED_ENV],
226
+ ci: env().CI,
227
+ vitest: env().VITEST,
228
+ e2eMode: env().PD_E2E_MODE,
229
+ },
230
+ workspaceEnvironment: workspaceEnvironmentOf(workspaceDir),
231
+ installMode: resolveInstallContext(homeDir()).installMode,
232
+ moduleDir: moduleDir(),
233
+ });
234
+ }
235
+ function gatesFor(state, workspaceDir) {
236
+ let flagEnabled = null;
237
+ let flagSource = null;
238
+ if (workspaceDir) {
239
+ const flag = loadFeatureFlagFromConfig(workspaceDir, ANONYMOUS_PRODUCT_TELEMETRY_FLAG);
240
+ flagEnabled = flag.enabled;
241
+ flagSource = flag.source;
242
+ }
243
+ const environment = environmentResult(workspaceDir);
244
+ const blockers = [];
245
+ if (flagEnabled === null)
246
+ blockers.push('feature_flag_unresolved');
247
+ else if (flagEnabled !== true)
248
+ blockers.push('feature_flag_disabled');
249
+ if (state.consent !== 'granted')
250
+ blockers.push(`consent_${state.consent}`);
251
+ blockers.push(...environment.reasons);
252
+ return {
253
+ flagEnabled,
254
+ flagSource,
255
+ consent: state.consent,
256
+ environmentSuppressed: environment.suppressed,
257
+ suppressionReasons: environment.reasons,
258
+ canExport: blockers.length === 0,
259
+ blockers,
260
+ };
261
+ }
262
+ function nextActionFor(gates) {
263
+ if (gates.canExport)
264
+ return undefined;
265
+ if (gates.suppressionReasons.includes('env_kill_switch'))
266
+ return `Unset ${PD_TELEMETRY_DISABLED_ENV} to allow telemetry export.`;
267
+ if (gates.flagEnabled === false)
268
+ return `Set features.${ANONYMOUS_PRODUCT_TELEMETRY_FLAG}.enabled: true in the workspace .pd/config.yaml.`;
269
+ if (gates.consent !== 'granted')
270
+ return 'Run: pd telemetry enable --confirm';
271
+ if (gates.environmentSuppressed)
272
+ return `Telemetry is suppressed in this environment (${gates.suppressionReasons.join(', ')}). This is expected for CI/test/dev environments.`;
273
+ return 'Resolve the blockers listed above.';
274
+ }
275
+ return {
276
+ /** Status for `pd telemetry status`. Never exposes the secret. */
277
+ getStatus(workspaceDir) {
278
+ const read = readProductTelemetryControlState(homeDir());
279
+ if (!read.ok)
280
+ return read;
281
+ const gates = gatesFor(read.state, workspaceDir);
282
+ // Export bookkeeping is workspace-scoped: show this workspace's entry
283
+ // (when a workspace was given and its scope can be derived).
284
+ let workspaceEntry;
285
+ const storedSecret = read.state.telemetrySecret;
286
+ if (workspaceDir !== undefined && isValidTelemetrySecretHex(storedSecret)) {
287
+ const scopeKey = workspaceScopeIdFor(storedSecret, workspaceDir);
288
+ workspaceEntry = read.state.workspaceExports?.[scopeKey];
289
+ }
290
+ const view = {
291
+ ...gates,
292
+ consentVersion: read.state.consentVersion,
293
+ hasSecret: isValidTelemetrySecretHex(read.state.telemetrySecret),
294
+ ...(workspaceEntry?.lastAttemptedAt !== undefined ? { lastAttemptedAt: workspaceEntry.lastAttemptedAt } : {}),
295
+ ...(workspaceEntry?.lastSucceededAt !== undefined ? { lastSucceededAt: workspaceEntry.lastSucceededAt } : {}),
296
+ ...(workspaceEntry?.lastFailureCode !== undefined ? { lastFailureCode: workspaceEntry.lastFailureCode } : {}),
297
+ ...(workspaceEntry?.nextRetryAt !== undefined ? { nextRetryAt: workspaceEntry.nextRetryAt } : {}),
298
+ endpoint: deps.endpoint ?? DEFAULT_PRODUCT_TELEMETRY_ENDPOINT,
299
+ ...(nextActionFor(gates) !== undefined ? { nextAction: nextActionFor(gates) } : {}),
300
+ };
301
+ return { ok: true, view };
302
+ },
303
+ /** Exact outbound payload for `pd telemetry preview`. Never sends. */
304
+ preview(workspaceDir) {
305
+ const read = readProductTelemetryControlState(homeDir());
306
+ const state = read.ok ? read.state : defaultProductTelemetryControlState();
307
+ const gates = gatesFor(state, workspaceDir);
308
+ const timestamp = now();
309
+ const bucketDate = bucketDateFromTime(timestamp);
310
+ const secretEphemeral = !isValidTelemetrySecretHex(state.telemetrySecret);
311
+ // Without a stored secret there is no stable daily ID; an all-zero
312
+ // hex placeholder keeps the preview shape exact while the note says
313
+ // the ID would differ after enable.
314
+ const secret = state.telemetrySecret ?? '0'.repeat(64);
315
+ const scopeKey = workspaceScopeIdFor(secret, workspaceDir);
316
+ const install = resolveInstallContext(homeDir());
317
+ const { facts, notes } = readMilestoneFacts(workspaceDir);
318
+ const snapshot = buildProductTelemetrySnapshot({
319
+ dailyTelemetryId: deriveDailyTelemetryId(secret, scopeKey, bucketDate),
320
+ bucketDate,
321
+ pdVersion: install.pdVersion,
322
+ hostKind: install.hostKind,
323
+ milestones: {
324
+ initialized: facts.initialized,
325
+ painObserved: facts.painObserved,
326
+ principleObserved: facts.principleObserved,
327
+ activationObserved: facts.activationObserved,
328
+ presenceReceiptObserved: facts.presenceReceiptObserved,
329
+ effectReceiptObserved: facts.effectReceiptObserved,
330
+ },
331
+ reliability: { initializationFailed: facts.initializationFailed },
332
+ });
333
+ const previewNotes = [...notes];
334
+ if (secretEphemeral)
335
+ previewNotes.push('payload shape is exact; dailyTelemetryId is provisional and will change after pd telemetry enable');
336
+ if (!read.ok)
337
+ previewNotes.push(`control state unreadable (${read.reason}) — showing defaults`);
338
+ return { banner: PREVIEW_BANNER, snapshot, gates, notes: previewNotes, secretEphemeral };
339
+ },
340
+ /** `pd telemetry enable --confirm`: explicit granted consent (+ secret). */
341
+ enable() {
342
+ const read = readProductTelemetryControlState(homeDir());
343
+ if (!read.ok)
344
+ return read;
345
+ const next = grantedControlState(read.state);
346
+ const written = writeProductTelemetryControlState(homeDir(), next);
347
+ if (!written.ok)
348
+ return written;
349
+ logger.info?.(`[PD:Telemetry] Consent granted (control state: ${getProductTelemetryStatePath(homeDir())}).`);
350
+ return { ok: true, consent: 'granted' };
351
+ },
352
+ /** `pd telemetry disable --confirm`: deny consent and delete export identity. */
353
+ disable() {
354
+ const read = readProductTelemetryControlState(homeDir());
355
+ if (!read.ok)
356
+ return read;
357
+ const written = writeProductTelemetryControlState(homeDir(), deniedControlState());
358
+ if (!written.ok)
359
+ return written;
360
+ logger.info?.('[PD:Telemetry] Consent denied; local telemetry secret and export status deleted. Future export requests: 0.');
361
+ return { ok: true, consent: 'denied' };
362
+ },
363
+ /** `pd telemetry reset --confirm`: delete identity; fresh secret only if still enabled. */
364
+ reset() {
365
+ const read = readProductTelemetryControlState(homeDir());
366
+ if (!read.ok)
367
+ return read;
368
+ const next = resetControlState(read.state);
369
+ const written = writeProductTelemetryControlState(homeDir(), next);
370
+ if (!written.ok)
371
+ return written;
372
+ logger.info?.('[PD:Telemetry] Telemetry identity reset; previous daily IDs are unlinkable going forward.');
373
+ return { ok: true, consent: next.consent };
374
+ },
375
+ /**
376
+ * One gate-checked export attempt for a normal-activity trigger.
377
+ * Never throws; every path returns a structured outcome.
378
+ *
379
+ * Concurrency: a per-workspace lock file
380
+ * (`product-telemetry.json.export-lock.<scopeId>`) closes the read→send→
381
+ * write race between simultaneous triggers (OpenClaw gateway init +
382
+ * pd-console startup in different processes) WITHOUT coupling unrelated
383
+ * workspaces. Stale locks (crashed holder) are taken over after
384
+ * EXPORT_LOCK_STALE_MS.
385
+ */
386
+ async maybeExportDaily(workspaceDir) {
387
+ const read = readProductTelemetryControlState(homeDir());
388
+ if (!read.ok) {
389
+ logger.warn?.(`[PD:Telemetry] Export skipped: ${read.reason}`);
390
+ return { attempted: false, skipReason: 'control_state_unreadable' };
391
+ }
392
+ const { state } = read;
393
+ const gates = gatesFor(state, workspaceDir);
394
+ if (!gates.flagEnabled)
395
+ return { attempted: false, skipReason: 'feature_flag_disabled' };
396
+ if (state.consent !== 'granted')
397
+ return { attempted: false, skipReason: `consent_${state.consent}` };
398
+ if (gates.environmentSuppressed) {
399
+ logger.info?.(`[PD:Telemetry] Export suppressed (${gates.suppressionReasons.join(', ')}).`);
400
+ return { attempted: false, skipReason: `environment_suppressed:${gates.suppressionReasons.join('+')}` };
401
+ }
402
+ if (!isValidTelemetrySecretHex(state.telemetrySecret)) {
403
+ logger.warn?.('[PD:Telemetry] Export skipped: telemetry secret missing/invalid. Run: pd telemetry enable --confirm');
404
+ return { attempted: false, skipReason: 'secret_missing' };
405
+ }
406
+ const secret = state.telemetrySecret;
407
+ const timestamp = now();
408
+ const bucketDate = bucketDateFromTime(timestamp);
409
+ const scopeKey = workspaceScopeIdFor(secret, workspaceDir);
410
+ const workspaceEntry = state.workspaceExports?.[scopeKey];
411
+ if (workspaceEntry?.lastSucceededAt !== undefined && bucketDateFromTime(Date.parse(workspaceEntry.lastSucceededAt)) === bucketDate) {
412
+ return { attempted: false, skipReason: 'already_succeeded_today' };
413
+ }
414
+ if (workspaceEntry?.nextRetryAt !== undefined && timestamp < Date.parse(workspaceEntry.nextRetryAt)) {
415
+ return { attempted: false, skipReason: 'retry_backoff' };
416
+ }
417
+ // Hard daily attempt cap (review round 2): the 1h/6h backoff alone
418
+ // permits up to 5 attempts on a bad day (0h/1h/7h/13h/19h); the counter
419
+ // makes the bound explicit and independent of clock skew. Per-workspace.
420
+ const priorAttempts = workspaceEntry?.attemptBucketDate === bucketDate ? (workspaceEntry?.dailyAttemptCount ?? 0) : 0;
421
+ if (priorAttempts >= MAX_DAILY_EXPORT_ATTEMPTS) {
422
+ return { attempted: false, skipReason: 'daily_attempt_cap' };
423
+ }
424
+ const lock = tryAcquireExportLock(getProductTelemetryStatePath(homeDir()), scopeKey);
425
+ if (!lock.acquired || lock.lockPath === undefined) {
426
+ // Another process is mid-export for THIS workspace; it will record
427
+ // shared-day success and our next trigger will see
428
+ // already_succeeded_today.
429
+ logger.info?.('[PD:Telemetry] Export skipped: another export is in flight for this workspace (lock busy).');
430
+ return { attempted: false, skipReason: 'export_lock_busy' };
431
+ }
432
+ try {
433
+ // Re-read inside the lock: the lock holder before us may have just
434
+ // succeeded for today (fresh state beats the stale pre-lock copy).
435
+ const fresh = readProductTelemetryControlState(homeDir());
436
+ const freshSecret = fresh.ok ? fresh.state.telemetrySecret : undefined;
437
+ const effectiveState = fresh.ok && freshSecret === secret
438
+ ? fresh.state
439
+ : state;
440
+ if (isValidTelemetrySecretHex(effectiveState.telemetrySecret)) {
441
+ const freshEntry = effectiveState.workspaceExports?.[scopeKey];
442
+ if (freshEntry?.lastSucceededAt !== undefined && bucketDateFromTime(Date.parse(freshEntry.lastSucceededAt)) === bucketDate) {
443
+ return { attempted: false, skipReason: 'already_succeeded_today' };
444
+ }
445
+ }
446
+ const install = resolveInstallContext(homeDir());
447
+ const { facts } = readMilestoneFacts(workspaceDir);
448
+ const snapshot = buildProductTelemetrySnapshot({
449
+ dailyTelemetryId: deriveDailyTelemetryId(secret, scopeKey, bucketDate),
450
+ bucketDate,
451
+ pdVersion: install.pdVersion,
452
+ hostKind: install.hostKind,
453
+ milestones: {
454
+ initialized: facts.initialized,
455
+ painObserved: facts.painObserved,
456
+ principleObserved: facts.principleObserved,
457
+ activationObserved: facts.activationObserved,
458
+ presenceReceiptObserved: facts.presenceReceiptObserved,
459
+ effectReceiptObserved: facts.effectReceiptObserved,
460
+ },
461
+ reliability: { initializationFailed: facts.initializationFailed },
462
+ });
463
+ const result = await exportSnapshot({
464
+ snapshot,
465
+ ...(deps.endpoint !== undefined ? { endpoint: deps.endpoint } : {}),
466
+ ...(deps.fetchFn !== undefined ? { fetchFn: deps.fetchFn } : {}),
467
+ });
468
+ const priorEntry = effectiveState.workspaceExports?.[scopeKey];
469
+ const failedRecently = priorEntry?.lastAttemptedAt !== undefined && timestamp - Date.parse(priorEntry.lastAttemptedAt) < 24 * 60 * 60 * 1000;
470
+ const nextEntry = result.ok
471
+ ? {
472
+ lastAttemptedAt: new Date(timestamp).toISOString(),
473
+ lastSucceededAt: new Date(timestamp).toISOString(),
474
+ }
475
+ : {
476
+ lastAttemptedAt: new Date(timestamp).toISOString(),
477
+ lastFailureCode: result.code,
478
+ nextRetryAt: new Date(timestamp + nextRetryDelayMs(failedRecently)).toISOString(),
479
+ dailyAttemptCount: priorAttempts + 1,
480
+ attemptBucketDate: bucketDate,
481
+ };
482
+ // Review round 4: merge under the machine-scope state-update lock —
483
+ // the OTHER workspaces' entries come from a fresh read inside the
484
+ // critical section, so concurrently-completing exports compose
485
+ // instead of dropping each other's bookkeeping (lost update). The
486
+ // network export above stayed concurrent; only this ms-scale file
487
+ // RMW is serialized. Every non-written outcome is logged (rc-9).
488
+ const merged = await mergeWorkspaceExportEntry({
489
+ homeDir: homeDir(),
490
+ secret,
491
+ scopeKey,
492
+ timestamp,
493
+ nextEntry,
494
+ });
495
+ if (merged === 'skipped_contention') {
496
+ logger.warn?.(`[PD:Telemetry] Export ${result.ok ? 'succeeded' : `failed (${result.code})`} but control state update was skipped after ${STATE_UPDATE_LOCK_TIMEOUT_MS / 1000}s of state-lock contention — this workspace may re-attempt today.`);
497
+ }
498
+ else if (merged === 'skipped_unreadable') {
499
+ logger.warn?.(`[PD:Telemetry] Export ${result.ok ? 'succeeded' : `failed (${result.code})`} but control state could not be read/updated — ${getProductTelemetryStatePath(homeDir())}`);
500
+ }
501
+ else if (merged === 'skipped_identity_changed') {
502
+ logger.info?.('[PD:Telemetry] Export finished after a consent/secret change; stale export bookkeeping was NOT written back.');
503
+ }
504
+ if (result.ok) {
505
+ logger.info?.('[PD:Telemetry] Daily anonymous product telemetry snapshot exported.');
506
+ return { attempted: true, ok: true, httpStatus: result.status };
507
+ }
508
+ logger.warn?.(`[PD:Telemetry] Export failed (${result.code}); next retry after ${nextEntry.nextRetryAt}. PD behavior is unaffected.`);
509
+ return { attempted: true, ok: false, failureCode: result.code };
510
+ }
511
+ finally {
512
+ releaseExportLock(lock.lockPath);
513
+ }
514
+ },
515
+ };
516
+ }
517
+ /**
518
+ * Fire-and-forget scheduling for normal-activity triggers. The timer is
519
+ * unref'd so it never keeps a host process alive, and every failure path is
520
+ * contained — telemetry can never block or crash PD startup or hooks.
521
+ */
522
+ export function scheduleProductTelemetryExport(service, workspaceDir, scheduleLogger) {
523
+ const timer = setTimeout(() => {
524
+ void service
525
+ .maybeExportDaily(workspaceDir)
526
+ .catch((error) => {
527
+ const message = error instanceof Error ? error.message : String(error);
528
+ scheduleLogger?.warn?.(`[PD:Telemetry] Unexpected export error (PD unaffected): ${message}`);
529
+ });
530
+ }, 0);
531
+ timer.unref?.();
532
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Workspace scope resolution — Anonymous Product Telemetry v1
3
+ * (review remediation: measurement unit = workspace).
4
+ *
5
+ * Telemetry bookkeeping (dedup, retry, export lock) is keyed per workspace
6
+ * while consent and the telemetry secret stay machine-scope. The key is a
7
+ * keyed fingerprint of the canonical workspace path:
8
+ *
9
+ * workspaceScopeId = HMAC(telemetrySecret, "workspace:" + canonicalPath)
10
+ *
11
+ * — opaque, locally stable, and carrying no path information without the
12
+ * secret (a plain path hash would be enumerable; this is not). It exists
13
+ * only in `~/.pd/product-telemetry.json` and lock filenames. The server
14
+ * never sees it.
15
+ *
16
+ * A workspace that is moved or renamed resolves to a new scope and is
17
+ * deliberately treated as a new telemetry workspace — no cross-path
18
+ * identity is created.
19
+ */
20
+ /**
21
+ * Canonicalize a workspace directory for scope derivation.
22
+ *
23
+ * Invariants (tested): `D:\Code\PD`, `d:\code\pd\`, and any casing variant
24
+ * on Windows resolve to ONE canonical form, so equivalent spellings never
25
+ * produce two telemetry units. On POSIX (case-sensitive filesystems) case
26
+ * is preserved. realpath collapses symlinks and Windows 8.3 short names;
27
+ * when realpath is unavailable (permissions, vanished dir) the resolved
28
+ * path is used as-is — a degraded key, never a thrown error.
29
+ */
30
+ export declare function canonicalizeWorkspacePath(workspaceDir: string): string;
31
+ /** Compute the local workspace scope key (hex16) for one secret + workspace. */
32
+ export declare function workspaceScopeIdFor(telemetrySecretHex: string, workspaceDir: string): string;
33
+ /**
34
+ * Per-workspace export lock path. The filename embeds ONLY the opaque hex
35
+ * scope key — never the workspace path — so one workspace's in-flight
36
+ * export never blocks another's (review remediation P1-1).
37
+ */
38
+ export declare function workspaceExportLockPath(stateFilePath: string, workspaceScopeId: string): string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Workspace scope resolution — Anonymous Product Telemetry v1
3
+ * (review remediation: measurement unit = workspace).
4
+ *
5
+ * Telemetry bookkeeping (dedup, retry, export lock) is keyed per workspace
6
+ * while consent and the telemetry secret stay machine-scope. The key is a
7
+ * keyed fingerprint of the canonical workspace path:
8
+ *
9
+ * workspaceScopeId = HMAC(telemetrySecret, "workspace:" + canonicalPath)
10
+ *
11
+ * — opaque, locally stable, and carrying no path information without the
12
+ * secret (a plain path hash would be enumerable; this is not). It exists
13
+ * only in `~/.pd/product-telemetry.json` and lock filenames. The server
14
+ * never sees it.
15
+ *
16
+ * A workspace that is moved or renamed resolves to a new scope and is
17
+ * deliberately treated as a new telemetry workspace — no cross-path
18
+ * identity is created.
19
+ */
20
+ import fs from 'node:fs';
21
+ import path from 'node:path';
22
+ import { deriveWorkspaceScopeId } from '@principles/core/runtime-v2';
23
+ /**
24
+ * Canonicalize a workspace directory for scope derivation.
25
+ *
26
+ * Invariants (tested): `D:\Code\PD`, `d:\code\pd\`, and any casing variant
27
+ * on Windows resolve to ONE canonical form, so equivalent spellings never
28
+ * produce two telemetry units. On POSIX (case-sensitive filesystems) case
29
+ * is preserved. realpath collapses symlinks and Windows 8.3 short names;
30
+ * when realpath is unavailable (permissions, vanished dir) the resolved
31
+ * path is used as-is — a degraded key, never a thrown error.
32
+ */
33
+ export function canonicalizeWorkspacePath(workspaceDir) {
34
+ let resolved = path.resolve(workspaceDir);
35
+ try {
36
+ resolved = fs.realpathSync(resolved);
37
+ }
38
+ catch {
39
+ // Non-throwing by contract (telemetry must never break PD); the resolved
40
+ // path is still a usable local key.
41
+ }
42
+ let canonical = resolved.split(path.sep).join('/');
43
+ if (process.platform === 'win32') {
44
+ // Windows filesystems are case-insensitive: normalize away casing drift
45
+ // (drive letter included) so D:\Code\PD == d:\code\pd.
46
+ canonical = canonical.toLowerCase();
47
+ }
48
+ if (canonical.length > 1 && canonical.endsWith('/')) {
49
+ canonical = canonical.slice(0, -1);
50
+ }
51
+ return canonical;
52
+ }
53
+ /** Compute the local workspace scope key (hex16) for one secret + workspace. */
54
+ export function workspaceScopeIdFor(telemetrySecretHex, workspaceDir) {
55
+ return deriveWorkspaceScopeId(telemetrySecretHex, canonicalizeWorkspacePath(workspaceDir));
56
+ }
57
+ /**
58
+ * Per-workspace export lock path. The filename embeds ONLY the opaque hex
59
+ * scope key — never the workspace path — so one workspace's in-flight
60
+ * export never blocks another's (review remediation P1-1).
61
+ */
62
+ export function workspaceExportLockPath(stateFilePath, workspaceScopeId) {
63
+ return `${stateFilePath}.export-lock.${workspaceScopeId}`;
64
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/host-runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Shared host-neutral orchestration for Principles Disciple MVP-Core hook paths.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,16 +22,21 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@principles/core": "file:../core",
25
+ "@principles/install-layout": "file:../install-layout",
25
26
  "better-sqlite3": "^13.0.3",
26
27
  "js-yaml": "^5.2.0"
27
28
  },
28
29
  "devDependencies": {
29
- "@types/better-sqlite3": "^7.6.13",
30
+ "@types/better-sqlite3": "^9.6.0",
30
31
  "@types/node": "^26.1.2",
31
32
  "@types/js-yaml": "^4.0.9",
32
33
  "typescript": "^7.0.2",
33
34
  "vite": "^8.0.16",
34
35
  "vitest": "^4.1.10"
35
36
  },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/csuzngjh/principles.git"
40
+ },
36
41
  "license": "MIT"
37
42
  }