@xingular/harness 0.1.7 → 1.0.0

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 (683) hide show
  1. package/CHANGELOG.md +97 -1
  2. package/LICENSE +202 -0
  3. package/NOTICE.md +22 -0
  4. package/README.md +187 -85
  5. package/THIRD_PARTY_LICENSES.md +942 -0
  6. package/dist/cli.js +13701 -13498
  7. package/dist/sdk/accounts/accounts.d.ts +165 -0
  8. package/dist/sdk/accounts/accounts.js +730 -0
  9. package/dist/sdk/accounts/choices.d.ts +23 -0
  10. package/dist/sdk/accounts/choices.js +62 -0
  11. package/dist/sdk/accounts/env.d.ts +78 -0
  12. package/dist/sdk/accounts/env.js +294 -0
  13. package/dist/sdk/accounts/secrets.d.ts +16 -0
  14. package/dist/sdk/accounts/secrets.js +40 -0
  15. package/dist/sdk/accounts/store.d.ts +114 -0
  16. package/dist/sdk/accounts/store.js +255 -0
  17. package/dist/sdk/browser/config.d.ts +16 -0
  18. package/dist/sdk/browser/config.js +22 -0
  19. package/dist/sdk/browser/diagnostics.d.ts +4 -0
  20. package/dist/sdk/browser/diagnostics.js +38 -0
  21. package/dist/sdk/browser/index.d.ts +17 -0
  22. package/dist/sdk/browser/index.js +65 -0
  23. package/dist/sdk/browser/network.d.ts +10 -0
  24. package/dist/sdk/browser/network.js +59 -0
  25. package/dist/sdk/browser/session.d.ts +84 -0
  26. package/dist/sdk/browser/session.js +192 -0
  27. package/dist/sdk/browser/tools.d.ts +8 -0
  28. package/dist/sdk/browser/tools.js +329 -0
  29. package/dist/sdk/capabilities/api.d.ts +22 -0
  30. package/dist/sdk/capabilities/api.js +54 -0
  31. package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
  32. package/dist/sdk/capabilities/runSnapshot.js +47 -0
  33. package/dist/sdk/capabilities/types.d.ts +29 -0
  34. package/dist/sdk/capabilities/types.js +1 -0
  35. package/dist/sdk/cli.d.ts +2 -0
  36. package/dist/sdk/cli.js +142 -0
  37. package/dist/sdk/core/bus.d.ts +23 -0
  38. package/dist/sdk/core/bus.js +76 -0
  39. package/dist/sdk/core/caller.d.ts +22 -0
  40. package/dist/sdk/core/caller.js +16 -0
  41. package/dist/sdk/core/changes.d.ts +15 -0
  42. package/dist/sdk/core/changes.js +15 -0
  43. package/dist/sdk/core/effort.d.ts +2 -0
  44. package/dist/sdk/core/effort.js +1 -0
  45. package/dist/sdk/core/env.d.ts +18 -0
  46. package/dist/sdk/core/env.js +8 -0
  47. package/dist/sdk/core/errors.d.ts +15 -0
  48. package/dist/sdk/core/errors.js +24 -0
  49. package/dist/sdk/core/frontmatter.d.ts +12 -0
  50. package/dist/sdk/core/frontmatter.js +50 -0
  51. package/dist/sdk/core/fs.d.ts +18 -0
  52. package/dist/sdk/core/fs.js +106 -0
  53. package/dist/sdk/core/index.d.ts +24 -0
  54. package/dist/sdk/core/index.js +187 -0
  55. package/dist/sdk/core/path.d.ts +7 -0
  56. package/dist/sdk/core/path.js +79 -0
  57. package/dist/sdk/core/projects.d.ts +18 -0
  58. package/dist/sdk/core/projects.js +67 -0
  59. package/dist/sdk/core/secrets.d.ts +2 -0
  60. package/dist/sdk/core/secrets.js +5 -0
  61. package/dist/sdk/core/text.d.ts +7 -0
  62. package/dist/sdk/core/text.js +40 -0
  63. package/dist/sdk/doctor/checks.d.ts +5 -0
  64. package/dist/sdk/doctor/checks.js +208 -0
  65. package/dist/sdk/doctor/probe.d.ts +4 -0
  66. package/dist/sdk/doctor/probe.js +92 -0
  67. package/dist/sdk/doctor/service.d.ts +23 -0
  68. package/dist/sdk/doctor/service.js +144 -0
  69. package/dist/sdk/doctor/types.d.ts +141 -0
  70. package/dist/sdk/doctor/types.js +29 -0
  71. package/dist/sdk/harness.d.ts +133 -0
  72. package/dist/sdk/harness.js +299 -0
  73. package/dist/sdk/history/lessonPrompt.d.ts +2 -0
  74. package/dist/sdk/history/lessonPrompt.js +15 -0
  75. package/dist/sdk/history/lessons.d.ts +52 -0
  76. package/dist/sdk/history/lessons.js +173 -0
  77. package/dist/sdk/history/recall.d.ts +68 -0
  78. package/dist/sdk/history/recall.js +199 -0
  79. package/dist/sdk/history/trajectory.d.ts +7 -0
  80. package/dist/sdk/history/trajectory.js +216 -0
  81. package/dist/sdk/hub/catalog.json +123 -0
  82. package/dist/sdk/hub/cli.d.ts +7 -0
  83. package/dist/sdk/hub/cli.js +81 -0
  84. package/dist/sdk/hub/hub.d.ts +74 -0
  85. package/dist/sdk/hub/hub.js +474 -0
  86. package/dist/sdk/hub/schema.d.ts +280 -0
  87. package/dist/sdk/hub/schema.js +78 -0
  88. package/dist/sdk/index.d.ts +63 -0
  89. package/dist/sdk/index.js +48 -0
  90. package/dist/sdk/mcp/connections.d.ts +83 -0
  91. package/dist/sdk/mcp/connections.js +669 -0
  92. package/dist/sdk/mcp/manager.d.ts +26 -0
  93. package/dist/sdk/mcp/manager.js +124 -0
  94. package/dist/sdk/mcp/oauth.d.ts +35 -0
  95. package/dist/sdk/mcp/oauth.js +171 -0
  96. package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
  97. package/dist/sdk/mcp/scopeMigration.js +198 -0
  98. package/dist/sdk/mcp/service.d.ts +160 -0
  99. package/dist/sdk/mcp/service.js +510 -0
  100. package/dist/sdk/mcp/types.d.ts +131 -0
  101. package/dist/sdk/mcp/types.js +49 -0
  102. package/dist/sdk/pi/runtime.d.ts +13 -0
  103. package/dist/sdk/pi/runtime.js +31 -0
  104. package/dist/sdk/plugins/api.d.ts +66 -0
  105. package/dist/sdk/plugins/api.js +155 -0
  106. package/dist/sdk/plugins/attachments.d.ts +46 -0
  107. package/dist/sdk/plugins/attachments.js +111 -0
  108. package/dist/sdk/plugins/builtin.d.ts +6 -0
  109. package/dist/sdk/plugins/builtin.js +68 -0
  110. package/dist/sdk/plugins/catalogue.d.ts +29 -0
  111. package/dist/sdk/plugins/catalogue.js +196 -0
  112. package/dist/sdk/plugins/cli.d.ts +13 -0
  113. package/dist/sdk/plugins/cli.js +187 -0
  114. package/dist/sdk/plugins/discover.d.ts +42 -0
  115. package/dist/sdk/plugins/discover.js +115 -0
  116. package/dist/sdk/plugins/firstParty.d.ts +50 -0
  117. package/dist/sdk/plugins/firstParty.js +113 -0
  118. package/dist/sdk/plugins/hooks.d.ts +94 -0
  119. package/dist/sdk/plugins/hooks.js +162 -0
  120. package/dist/sdk/plugins/identity.d.ts +19 -0
  121. package/dist/sdk/plugins/identity.js +48 -0
  122. package/dist/sdk/plugins/install.d.ts +20 -0
  123. package/dist/sdk/plugins/install.js +110 -0
  124. package/dist/sdk/plugins/inventory.d.ts +22 -0
  125. package/dist/sdk/plugins/inventory.js +110 -0
  126. package/dist/sdk/plugins/manifest.d.ts +13 -0
  127. package/dist/sdk/plugins/manifest.js +56 -0
  128. package/dist/sdk/plugins/plugins.d.ts +104 -0
  129. package/dist/sdk/plugins/plugins.js +164 -0
  130. package/dist/sdk/plugins/runtime.d.ts +9 -0
  131. package/dist/sdk/plugins/runtime.js +95 -0
  132. package/dist/sdk/plugins/trust.d.ts +44 -0
  133. package/dist/sdk/plugins/trust.js +120 -0
  134. package/dist/sdk/plugins/types.d.ts +159 -0
  135. package/dist/sdk/plugins/types.js +7 -0
  136. package/dist/sdk/prompt/compile.d.ts +174 -0
  137. package/dist/sdk/prompt/compile.js +396 -0
  138. package/dist/sdk/refine/cli.d.ts +4 -0
  139. package/dist/sdk/refine/cli.js +186 -0
  140. package/dist/sdk/refine/demo.d.ts +21 -0
  141. package/dist/sdk/refine/demo.js +114 -0
  142. package/dist/sdk/refine/evidence.d.ts +55 -0
  143. package/dist/sdk/refine/evidence.js +330 -0
  144. package/dist/sdk/refine/host.d.ts +51 -0
  145. package/dist/sdk/refine/host.js +76 -0
  146. package/dist/sdk/refine/ledger.d.ts +48 -0
  147. package/dist/sdk/refine/ledger.js +76 -0
  148. package/dist/sdk/refine/library.d.ts +55 -0
  149. package/dist/sdk/refine/library.js +177 -0
  150. package/dist/sdk/refine/pi.d.ts +72 -0
  151. package/dist/sdk/refine/pi.js +248 -0
  152. package/dist/sdk/refine/prompts.d.ts +45 -0
  153. package/dist/sdk/refine/prompts.js +390 -0
  154. package/dist/sdk/refine/recursion.d.ts +113 -0
  155. package/dist/sdk/refine/recursion.js +258 -0
  156. package/dist/sdk/refine/runner.d.ts +81 -0
  157. package/dist/sdk/refine/runner.js +150 -0
  158. package/dist/sdk/refine/selection.d.ts +57 -0
  159. package/dist/sdk/refine/selection.js +251 -0
  160. package/dist/sdk/refine/service.d.ts +84 -0
  161. package/dist/sdk/refine/service.js +165 -0
  162. package/dist/sdk/refine/suite.d.ts +67 -0
  163. package/dist/sdk/refine/suite.js +174 -0
  164. package/dist/sdk/refine/trajectory.d.ts +9 -0
  165. package/dist/sdk/refine/trajectory.js +68 -0
  166. package/dist/sdk/refine/types.d.ts +77 -0
  167. package/dist/sdk/refine/types.js +10 -0
  168. package/dist/sdk/server/methods.d.ts +3 -0
  169. package/dist/sdk/server/methods.js +370 -0
  170. package/dist/sdk/server/remote.d.ts +19 -0
  171. package/dist/sdk/server/remote.js +84 -0
  172. package/dist/sdk/server/rpc.d.ts +20 -0
  173. package/dist/sdk/server/rpc.js +102 -0
  174. package/dist/sdk/server/serve.d.ts +10 -0
  175. package/dist/sdk/server/serve.js +18 -0
  176. package/dist/sdk/sessions/approvals.d.ts +47 -0
  177. package/dist/sdk/sessions/approvals.js +119 -0
  178. package/dist/sdk/sessions/continuation.d.ts +48 -0
  179. package/dist/sdk/sessions/continuation.js +85 -0
  180. package/dist/sdk/sessions/contracts.d.ts +534 -0
  181. package/dist/sdk/sessions/contracts.js +299 -0
  182. package/dist/sdk/sessions/extensionUi.d.ts +19 -0
  183. package/dist/sdk/sessions/extensionUi.js +64 -0
  184. package/dist/sdk/sessions/history.d.ts +16 -0
  185. package/dist/sdk/sessions/history.js +68 -0
  186. package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
  187. package/dist/sdk/sessions/lineageJoin.js +61 -0
  188. package/dist/sdk/sessions/planMode.d.ts +36 -0
  189. package/dist/sdk/sessions/planMode.js +84 -0
  190. package/dist/sdk/sessions/policy.d.ts +43 -0
  191. package/dist/sdk/sessions/policy.js +1 -0
  192. package/dist/sdk/sessions/pricing.d.ts +34 -0
  193. package/dist/sdk/sessions/pricing.js +40 -0
  194. package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
  195. package/dist/sdk/sessions/reasoningSummaries.js +61 -0
  196. package/dist/sdk/sessions/registry.d.ts +102 -0
  197. package/dist/sdk/sessions/registry.js +383 -0
  198. package/dist/sdk/sessions/runIndex.d.ts +21 -0
  199. package/dist/sdk/sessions/runIndex.js +65 -0
  200. package/dist/sdk/sessions/runStore.d.ts +66 -0
  201. package/dist/sdk/sessions/runStore.js +335 -0
  202. package/dist/sdk/sessions/runner.d.ts +213 -0
  203. package/dist/sdk/sessions/runner.js +589 -0
  204. package/dist/sdk/sessions/sessions.d.ts +140 -0
  205. package/dist/sdk/sessions/sessions.js +394 -0
  206. package/dist/sdk/sessions/subagentService.d.ts +41 -0
  207. package/dist/sdk/sessions/subagentService.js +247 -0
  208. package/dist/sdk/sessions/subagents.d.ts +50 -0
  209. package/dist/sdk/sessions/subagents.js +68 -0
  210. package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
  211. package/dist/sdk/terminal/accountsMenu.js +392 -0
  212. package/dist/sdk/terminal/cli.d.ts +20 -0
  213. package/dist/sdk/terminal/cli.js +121 -0
  214. package/dist/sdk/terminal/client.d.ts +15 -0
  215. package/dist/sdk/terminal/client.js +126 -0
  216. package/dist/sdk/terminal/control.d.ts +4 -0
  217. package/dist/sdk/terminal/control.js +25 -0
  218. package/dist/sdk/terminal/controller.d.ts +72 -0
  219. package/dist/sdk/terminal/controller.js +351 -0
  220. package/dist/sdk/terminal/hubMenu.d.ts +10 -0
  221. package/dist/sdk/terminal/hubMenu.js +34 -0
  222. package/dist/sdk/terminal/launch.d.ts +5 -0
  223. package/dist/sdk/terminal/launch.js +27 -0
  224. package/dist/sdk/terminal/logs.d.ts +14 -0
  225. package/dist/sdk/terminal/logs.js +59 -0
  226. package/dist/sdk/terminal/piExtension.d.ts +6 -0
  227. package/dist/sdk/terminal/piExtension.js +392 -0
  228. package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
  229. package/dist/sdk/terminal/pluginMenu.js +101 -0
  230. package/dist/sdk/terminal/socket.d.ts +70 -0
  231. package/dist/sdk/terminal/socket.js +256 -0
  232. package/dist/sdk/terminal/standalone.d.ts +18 -0
  233. package/dist/sdk/terminal/standalone.js +146 -0
  234. package/dist/sdk/terminal/terminals.d.ts +176 -0
  235. package/dist/sdk/terminal/terminals.js +767 -0
  236. package/dist/sdk/terminal/trajectory.d.ts +10 -0
  237. package/dist/sdk/terminal/trajectory.js +30 -0
  238. package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
  239. package/dist/sdk/terminal/tui/chrome.js +169 -0
  240. package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
  241. package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
  242. package/dist/sdk/terminal/tui/commandPages.d.ts +7 -0
  243. package/dist/sdk/terminal/tui/commandPages.js +63 -0
  244. package/dist/sdk/terminal/tui/commands.d.ts +42 -0
  245. package/dist/sdk/terminal/tui/commands.js +276 -0
  246. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
  247. package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
  248. package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
  249. package/dist/sdk/terminal/tui/dialogs.js +363 -0
  250. package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
  251. package/dist/sdk/terminal/tui/keybindings.js +49 -0
  252. package/dist/sdk/terminal/tui/list.d.ts +73 -0
  253. package/dist/sdk/terminal/tui/list.js +196 -0
  254. package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
  255. package/dist/sdk/terminal/tui/modeChoices.js +70 -0
  256. package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
  257. package/dist/sdk/terminal/tui/modelPicker.js +155 -0
  258. package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
  259. package/dist/sdk/terminal/tui/mouse.js +3 -0
  260. package/dist/sdk/terminal/tui/page.d.ts +14 -0
  261. package/dist/sdk/terminal/tui/page.js +1 -0
  262. package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
  263. package/dist/sdk/terminal/tui/pageOverlay.js +145 -0
  264. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
  265. package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -0
  266. package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
  267. package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
  268. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +37 -0
  269. package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -0
  270. package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
  271. package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
  272. package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
  273. package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
  274. package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
  275. package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
  276. package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
  277. package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
  278. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +55 -0
  279. package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
  280. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
  281. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -0
  282. package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
  283. package/dist/sdk/terminal/tui/piAdapter.js +48 -0
  284. package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
  285. package/dist/sdk/terminal/tui/planMode.js +2 -0
  286. package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
  287. package/dist/sdk/terminal/tui/questionPicker.js +150 -0
  288. package/dist/sdk/terminal/tui/questions.d.ts +19 -0
  289. package/dist/sdk/terminal/tui/questions.js +42 -0
  290. package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
  291. package/dist/sdk/terminal/tui/runtime.js +441 -0
  292. package/dist/sdk/terminal/tui/search.d.ts +22 -0
  293. package/dist/sdk/terminal/tui/search.js +60 -0
  294. package/dist/sdk/terminal/tui/selection.d.ts +16 -0
  295. package/dist/sdk/terminal/tui/selection.js +16 -0
  296. package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
  297. package/dist/sdk/terminal/tui/sessionUi.js +30 -0
  298. package/dist/sdk/terminal/tui/settingsModel.d.ts +12 -0
  299. package/dist/sdk/terminal/tui/settingsModel.js +84 -0
  300. package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
  301. package/dist/sdk/terminal/tui/sideQuestion.js +197 -0
  302. package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
  303. package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
  304. package/dist/sdk/terminal/tui/status.d.ts +18 -0
  305. package/dist/sdk/terminal/tui/status.js +67 -0
  306. package/dist/sdk/terminal/tui/theme.d.ts +45 -0
  307. package/dist/sdk/terminal/tui/theme.js +176 -0
  308. package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
  309. package/dist/sdk/terminal/tui/treeModel.js +483 -0
  310. package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
  311. package/dist/sdk/terminal/tui/treePicker.js +251 -0
  312. package/dist/sdk/terminal/tui/ui.d.ts +47 -0
  313. package/dist/sdk/terminal/tui/ui.js +148 -0
  314. package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
  315. package/dist/sdk/terminal/tui/workingStatus.js +229 -0
  316. package/dist/sdk/tools/askUser.d.ts +6 -0
  317. package/dist/sdk/tools/askUser.js +74 -0
  318. package/dist/sdk/tools/checklist.d.ts +9 -0
  319. package/dist/sdk/tools/checklist.js +54 -0
  320. package/dist/sdk/tools/definitions.d.ts +30 -0
  321. package/dist/sdk/tools/definitions.js +117 -0
  322. package/dist/sdk/tools/dispatch.d.ts +16 -0
  323. package/dist/sdk/tools/dispatch.js +67 -0
  324. package/dist/sdk/tools/harnessTools.d.ts +6 -0
  325. package/dist/sdk/tools/harnessTools.js +20 -0
  326. package/dist/sdk/tools/sessionName.d.ts +7 -0
  327. package/dist/sdk/tools/sessionName.js +31 -0
  328. package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
  329. package/dist/sdk/tools/spawnSubagent.js +49 -0
  330. package/dist/sdk/tools/types.d.ts +48 -0
  331. package/dist/sdk/tools/types.js +1 -0
  332. package/dist/sdk/variant/components.d.ts +34 -0
  333. package/dist/sdk/variant/components.js +230 -0
  334. package/dist/sdk/variant/materialise.d.ts +66 -0
  335. package/dist/sdk/variant/materialise.js +81 -0
  336. package/dist/sdk/variant/migrate.d.ts +4 -0
  337. package/dist/sdk/variant/migrate.js +41 -0
  338. package/dist/sdk/variant/seed.d.ts +20 -0
  339. package/dist/sdk/variant/seed.js +116 -0
  340. package/dist/sdk/variant/store.d.ts +82 -0
  341. package/dist/sdk/variant/store.js +151 -0
  342. package/dist/sdk/variant/surface.d.ts +56 -0
  343. package/dist/sdk/variant/surface.js +81 -0
  344. package/dist/sdk/variant/trust.d.ts +27 -0
  345. package/dist/sdk/variant/trust.js +64 -0
  346. package/dist/sdk/variant/variant.d.ts +71 -0
  347. package/dist/sdk/variant/variant.js +331 -0
  348. package/dist/sdk/web/markdown.d.ts +10 -0
  349. package/dist/sdk/web/markdown.js +66 -0
  350. package/dist/sdk/web/safeRequest.d.ts +34 -0
  351. package/dist/sdk/web/safeRequest.js +175 -0
  352. package/dist/sdk/web/search.d.ts +53 -0
  353. package/dist/sdk/web/search.js +168 -0
  354. package/dist/sdk/web/text.d.ts +2 -0
  355. package/dist/sdk/web/text.js +7 -0
  356. package/dist/sdk/web/tools.d.ts +22 -0
  357. package/dist/sdk/web/tools.js +195 -0
  358. package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
  359. package/dist/sdk/wiki/notes.d.ts +68 -0
  360. package/dist/sdk/wiki/notes.js +297 -0
  361. package/dist/sdk/world/actions.d.ts +23 -0
  362. package/dist/sdk/world/actions.js +31 -0
  363. package/dist/sdk/world/cli.d.ts +12 -0
  364. package/dist/sdk/world/cli.js +166 -0
  365. package/dist/sdk/world/images.d.ts +20 -0
  366. package/dist/sdk/world/images.js +48 -0
  367. package/dist/sdk/world/untrusted.d.ts +12 -0
  368. package/dist/sdk/world/untrusted.js +20 -0
  369. package/dist/sdk/world/world.d.ts +65 -0
  370. package/dist/sdk/world/world.js +80 -0
  371. package/dist/terminal-opentui.js +13612 -0
  372. package/dist/terminal-pi.js +808 -151
  373. package/docs/README.md +22 -0
  374. package/docs/architecture/00-overview.md +159 -0
  375. package/docs/architecture/01-extension-points.md +78 -0
  376. package/docs/architecture/02-sessions-and-runs.md +146 -0
  377. package/docs/architecture/03-variants.md +95 -0
  378. package/docs/architecture/04-prompt.md +102 -0
  379. package/docs/architecture/05-refinement.md +107 -0
  380. package/docs/architecture/06-plugins-and-capabilities.md +93 -0
  381. package/docs/architecture/07-tools-and-gate.md +88 -0
  382. package/docs/architecture/08-memory-and-history.md +94 -0
  383. package/docs/architecture/09-accounts-and-connectors.md +90 -0
  384. package/docs/architecture/10-server-protocol.md +84 -0
  385. package/docs/architecture/11-terminal.md +81 -0
  386. package/docs/architecture/12-storage.md +61 -0
  387. package/docs/architecture/13-world-tools.md +23 -0
  388. package/docs/architecture/README.md +114 -0
  389. package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
  390. package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
  391. package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
  392. package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
  393. package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
  394. package/docs/architecture/proposals/PROP-0006-run-checklist.md +68 -0
  395. package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
  396. package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
  397. package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
  398. package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
  399. package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
  400. package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
  401. package/docs/architecture/proposals/README.md +76 -0
  402. package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
  403. package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
  404. package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
  405. package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
  406. package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
  407. package/docs/decisions/ADR-0007-reporting-section.md +33 -0
  408. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
  409. package/docs/decisions/ADR-0009-xharness-command.md +38 -0
  410. package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
  411. package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
  412. package/docs/decisions/ADR-0012-native-session-names.md +34 -0
  413. package/docs/decisions/ADR-0013-doctor.md +27 -0
  414. package/docs/decisions/ADR-0014-public-release-baseline.md +21 -0
  415. package/docs/doctor.md +74 -0
  416. package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
  417. package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
  418. package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
  419. package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
  420. package/docs/features/README.md +20 -0
  421. package/docs/features/automations/README.md +41 -0
  422. package/docs/features/cores/README.md +39 -0
  423. package/docs/features/google-harness2/README.md +52 -0
  424. package/docs/features/integrations/README.md +40 -0
  425. package/docs/features/interfaces/README.md +29 -0
  426. package/docs/features/memory/README.md +28 -0
  427. package/docs/releases/1.0.0.md +40 -0
  428. package/docs/releases/README.md +36 -0
  429. package/docs/study/00-index.md +78 -0
  430. package/docs/study/arms/00-matrix.md +20 -0
  431. package/docs/study/arms/01-system-prompt.md +211 -0
  432. package/docs/study/arms/02-tools.md +79 -0
  433. package/docs/study/arms/03-subagents.md +90 -0
  434. package/docs/study/arms/04-skills.md +75 -0
  435. package/docs/study/arms/05-context-management.md +90 -0
  436. package/docs/study/arms/06-memory.md +90 -0
  437. package/docs/study/arms/07-governance.md +71 -0
  438. package/docs/study/arms/08-execution.md +69 -0
  439. package/docs/study/arms/09-observability.md +60 -0
  440. package/docs/study/arms/10-refinement.md +110 -0
  441. package/docs/study/btw-comparison.md +37 -0
  442. package/docs/study/feature-gap-review.md +42 -0
  443. package/docs/study/insights.md +123 -0
  444. package/docs/study/roadmap.md +72 -0
  445. package/docs/study/sources/claude-code.md +44 -0
  446. package/docs/study/sources/deepseek-harness.md +102 -0
  447. package/docs/study/sources/harness2.md +50 -0
  448. package/docs/study/sources/kimi-cli.md +42 -0
  449. package/docs/study/sources/omp.md +93 -0
  450. package/docs/study/sources/opencode.md +43 -0
  451. package/docs/study/sources/openhands.md +46 -0
  452. package/docs/study/sources/smolagents.md +40 -0
  453. package/docs/study/sources/swe-agent.md +34 -0
  454. package/docs/system-prompt-design.md +196 -0
  455. package/docs/terminal-tui.md +473 -56
  456. package/package.json +43 -13
  457. package/docs/evidence/terminal-2026-10-17/120x40-completed-transcript.ansi.gz +0 -0
  458. package/docs/evidence/terminal-2026-10-17/40x20-settings-interaction.ansi.gz +0 -0
  459. package/docs/evidence/terminal-2026-10-17/40x20-settings-keyboard.ansi.gz +0 -0
  460. package/docs/evidence/terminal-2026-10-17/40x20-welcome.ansi.gz +0 -0
  461. package/docs/evidence/terminal-2026-10-17/80x24-approval-0.ansi.gz +0 -0
  462. package/docs/evidence/terminal-2026-10-17/80x24-question.ansi.gz +0 -0
  463. package/docs/evidence/terminal-2026-10-17/verification.json +0 -130
  464. package/docs/evidence/terminal-2026-10-17-followup/120x40-completed-transcript.ansi.gz +0 -0
  465. package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-interaction.ansi.gz +0 -0
  466. package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-keyboard.ansi.gz +0 -0
  467. package/docs/evidence/terminal-2026-10-17-followup/40x20-welcome.ansi.gz +0 -0
  468. package/docs/evidence/terminal-2026-10-17-followup/80x24-approval-0.ansi.gz +0 -0
  469. package/docs/evidence/terminal-2026-10-17-followup/80x24-plugin-light.ansi.gz +0 -0
  470. package/docs/evidence/terminal-2026-10-17-followup/80x24-question-batch.ansi.gz +0 -0
  471. package/docs/evidence/terminal-2026-10-17-followup/80x24-question.ansi.gz +0 -0
  472. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-approval-0.ansi.gz +0 -0
  473. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-plugin-light.ansi.gz +0 -0
  474. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question-batch.ansi.gz +0 -0
  475. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question.ansi.gz +0 -0
  476. package/docs/evidence/terminal-2026-10-17-followup/app-bundle/verification.json +0 -95
  477. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-approval-0.ansi.gz +0 -0
  478. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-plugin-light.ansi.gz +0 -0
  479. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question-batch.ansi.gz +0 -0
  480. package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question.ansi.gz +0 -0
  481. package/docs/evidence/terminal-2026-10-17-followup/global-install/README.md +0 -27
  482. package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command-80x24.ansi.gz +0 -0
  483. package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command.json +0 -18
  484. package/docs/evidence/terminal-2026-10-17-followup/global-install/verification.json +0 -95
  485. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-80x24.json.gz +0 -0
  486. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-final-80x24.json.gz +0 -0
  487. package/docs/evidence/terminal-2026-10-17-followup/harness-reference-scenario.json +0 -60
  488. package/docs/evidence/terminal-2026-10-17-followup/live-codex/README.md +0 -24
  489. package/docs/evidence/terminal-2026-10-17-followup/live-codex/complete-80x24.ansi.gz +0 -0
  490. package/docs/evidence/terminal-2026-10-17-followup/live-codex/verification.json +0 -57
  491. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24-early.json.gz +0 -0
  492. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24.json.gz +0 -0
  493. package/docs/evidence/terminal-2026-10-17-followup/omp-reference-scenario.json +0 -58
  494. package/docs/evidence/terminal-2026-10-17-followup/omp-supplemental-static.json.gz +0 -0
  495. package/docs/evidence/terminal-2026-10-17-followup/rea-artifacts.json +0 -320
  496. package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.json +0 -30
  497. package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.md +0 -133
  498. package/docs/evidence/terminal-2026-10-17-followup/reference-source-observations.json +0 -20
  499. package/docs/evidence/terminal-2026-10-17-followup/verification.json +0 -207
  500. package/src/accounts/accounts.test.ts +0 -333
  501. package/src/accounts/accounts.ts +0 -658
  502. package/src/accounts/env.test.ts +0 -95
  503. package/src/accounts/env.ts +0 -338
  504. package/src/accounts/secrets.ts +0 -50
  505. package/src/accounts/store.ts +0 -338
  506. package/src/capabilities/api.ts +0 -75
  507. package/src/capabilities/runSnapshot.ts +0 -59
  508. package/src/capabilities/types.ts +0 -34
  509. package/src/cli.ts +0 -118
  510. package/src/core/bus.ts +0 -34
  511. package/src/core/changes.ts +0 -33
  512. package/src/core/env.ts +0 -27
  513. package/src/core/errors.ts +0 -32
  514. package/src/core/frontmatter.ts +0 -47
  515. package/src/core/fs.test.ts +0 -38
  516. package/src/core/fs.ts +0 -92
  517. package/src/core/index.ts +0 -193
  518. package/src/core/path.test.ts +0 -14
  519. package/src/core/path.ts +0 -79
  520. package/src/core/projects.ts +0 -90
  521. package/src/core/text.ts +0 -39
  522. package/src/harness.ts +0 -312
  523. package/src/hub/catalog.json +0 -123
  524. package/src/hub/cli.ts +0 -59
  525. package/src/hub/hub.test.ts +0 -205
  526. package/src/hub/hub.ts +0 -256
  527. package/src/hub/schema.ts +0 -81
  528. package/src/index.ts +0 -75
  529. package/src/mcp/connections.ts +0 -614
  530. package/src/mcp/manager.ts +0 -130
  531. package/src/mcp/oauth.ts +0 -195
  532. package/src/mcp/scopeMigration.test.ts +0 -79
  533. package/src/mcp/scopeMigration.ts +0 -204
  534. package/src/mcp/service.test.ts +0 -376
  535. package/src/mcp/service.ts +0 -549
  536. package/src/mcp/types.ts +0 -108
  537. package/src/memory/api.test.ts +0 -82
  538. package/src/memory/api.ts +0 -303
  539. package/src/memory/blocks.test.ts +0 -78
  540. package/src/memory/blocks.ts +0 -408
  541. package/src/memory/caller.ts +0 -69
  542. package/src/memory/curation.test.ts +0 -163
  543. package/src/memory/curation.ts +0 -64
  544. package/src/memory/dispatch.ts +0 -59
  545. package/src/memory/facts.test.ts +0 -41
  546. package/src/memory/facts.ts +0 -248
  547. package/src/memory/graph.ts +0 -103
  548. package/src/memory/health.test.ts +0 -130
  549. package/src/memory/health.ts +0 -74
  550. package/src/memory/maintainer.ts +0 -272
  551. package/src/memory/maintenance.test.ts +0 -176
  552. package/src/memory/maintenance.ts +0 -518
  553. package/src/memory/migrate.ts +0 -79
  554. package/src/memory/operations.ts +0 -150
  555. package/src/memory/proposals.test.ts +0 -168
  556. package/src/memory/proposals.ts +0 -584
  557. package/src/memory/recall.test.ts +0 -115
  558. package/src/memory/recall.ts +0 -278
  559. package/src/memory/references.ts +0 -77
  560. package/src/memory/runContext.ts +0 -93
  561. package/src/memory/secrets.ts +0 -6
  562. package/src/memory/store.test.ts +0 -66
  563. package/src/memory/store.ts +0 -385
  564. package/src/memory/wiki.test.ts +0 -47
  565. package/src/memory/wiki.ts +0 -422
  566. package/src/memory/wikiIndex.test.ts +0 -88
  567. package/src/memory/wikiIndex.ts +0 -116
  568. package/src/plugins/api.ts +0 -181
  569. package/src/plugins/attachments.test.ts +0 -84
  570. package/src/plugins/attachments.ts +0 -141
  571. package/src/plugins/builtin.ts +0 -82
  572. package/src/plugins/catalogue.test.ts +0 -254
  573. package/src/plugins/catalogue.ts +0 -251
  574. package/src/plugins/cli.ts +0 -184
  575. package/src/plugins/discover.ts +0 -151
  576. package/src/plugins/firstParty.test.ts +0 -143
  577. package/src/plugins/firstParty.ts +0 -129
  578. package/src/plugins/hooks.test.ts +0 -250
  579. package/src/plugins/hooks.ts +0 -239
  580. package/src/plugins/identity.ts +0 -37
  581. package/src/plugins/install.test.ts +0 -107
  582. package/src/plugins/install.ts +0 -106
  583. package/src/plugins/inventory.ts +0 -123
  584. package/src/plugins/manifest.ts +0 -76
  585. package/src/plugins/plugins.test.ts +0 -205
  586. package/src/plugins/plugins.ts +0 -189
  587. package/src/plugins/proposal.ts +0 -121
  588. package/src/plugins/runtime.test.ts +0 -40
  589. package/src/plugins/runtime.ts +0 -82
  590. package/src/plugins/trust.ts +0 -142
  591. package/src/plugins/types.ts +0 -149
  592. package/src/prompt/compile.test.ts +0 -261
  593. package/src/prompt/compile.ts +0 -732
  594. package/src/server/methods.ts +0 -349
  595. package/src/server/remote.ts +0 -77
  596. package/src/server/rpc.ts +0 -116
  597. package/src/server/serve.ts +0 -28
  598. package/src/server/server.test.ts +0 -174
  599. package/src/sessions/approvals.ts +0 -118
  600. package/src/sessions/contracts.ts +0 -336
  601. package/src/sessions/conversation.test.ts +0 -177
  602. package/src/sessions/extensionSession.test.ts +0 -74
  603. package/src/sessions/extensionUi.test.ts +0 -65
  604. package/src/sessions/extensionUi.ts +0 -102
  605. package/src/sessions/history.ts +0 -51
  606. package/src/sessions/lineageJoin.ts +0 -75
  607. package/src/sessions/policy.ts +0 -36
  608. package/src/sessions/pricing.ts +0 -60
  609. package/src/sessions/questionBatches.test.ts +0 -110
  610. package/src/sessions/reasoningSummaries.ts +0 -59
  611. package/src/sessions/registry.ts +0 -381
  612. package/src/sessions/runIndex.ts +0 -99
  613. package/src/sessions/runStore.test.ts +0 -152
  614. package/src/sessions/runStore.ts +0 -332
  615. package/src/sessions/runner.ts +0 -604
  616. package/src/sessions/runtimeResources.test.ts +0 -61
  617. package/src/sessions/sessions.test.ts +0 -549
  618. package/src/sessions/sessions.ts +0 -339
  619. package/src/skills/registry.test.ts +0 -104
  620. package/src/skills/registry.ts +0 -413
  621. package/src/terminal/accountsMenu.test.ts +0 -225
  622. package/src/terminal/accountsMenu.ts +0 -382
  623. package/src/terminal/cli.ts +0 -149
  624. package/src/terminal/client.ts +0 -74
  625. package/src/terminal/control.test.ts +0 -23
  626. package/src/terminal/control.ts +0 -23
  627. package/src/terminal/controller.ts +0 -291
  628. package/src/terminal/hubMenu.ts +0 -24
  629. package/src/terminal/launch.test.ts +0 -25
  630. package/src/terminal/launch.ts +0 -48
  631. package/src/terminal/piExtension.lifecycle.test.ts +0 -55
  632. package/src/terminal/piExtension.ts +0 -259
  633. package/src/terminal/pluginControl.test.ts +0 -146
  634. package/src/terminal/pluginMenu.ts +0 -112
  635. package/src/terminal/socket.test.ts +0 -71
  636. package/src/terminal/socket.ts +0 -232
  637. package/src/terminal/standalone.test.ts +0 -167
  638. package/src/terminal/standalone.ts +0 -153
  639. package/src/terminal/terminal.test.ts +0 -399
  640. package/src/terminal/terminals.ts +0 -553
  641. package/src/terminal/tui/application.test.ts +0 -114
  642. package/src/terminal/tui/application.ts +0 -491
  643. package/src/terminal/tui/commands.test.ts +0 -218
  644. package/src/terminal/tui/commands.ts +0 -361
  645. package/src/terminal/tui/dialogs.ts +0 -301
  646. package/src/terminal/tui/editor.test.ts +0 -84
  647. package/src/terminal/tui/editor.ts +0 -79
  648. package/src/terminal/tui/keybindings.test.ts +0 -22
  649. package/src/terminal/tui/keybindings.ts +0 -50
  650. package/src/terminal/tui/piAdapter.ts +0 -62
  651. package/src/terminal/tui/presentation.test.ts +0 -79
  652. package/src/terminal/tui/questions.test.ts +0 -41
  653. package/src/terminal/tui/questions.ts +0 -47
  654. package/src/terminal/tui/runtime.socket.test.ts +0 -104
  655. package/src/terminal/tui/runtime.test.ts +0 -215
  656. package/src/terminal/tui/runtime.ts +0 -388
  657. package/src/terminal/tui/sessionUi.ts +0 -24
  658. package/src/terminal/tui/settings.test.ts +0 -150
  659. package/src/terminal/tui/settings.ts +0 -188
  660. package/src/terminal/tui/status.test.ts +0 -70
  661. package/src/terminal/tui/status.ts +0 -109
  662. package/src/terminal/tui/theme.ts +0 -174
  663. package/src/terminal/tui/tools.test.ts +0 -145
  664. package/src/terminal/tui/tools.ts +0 -142
  665. package/src/terminal/tui/transcript.test.ts +0 -25
  666. package/src/terminal/tui/transcript.ts +0 -124
  667. package/src/terminal/tui/ui.test.ts +0 -313
  668. package/src/terminal/tui/ui.ts +0 -166
  669. package/src/terminal/tui/welcome.ts +0 -102
  670. package/src/tools/askUser.test.ts +0 -69
  671. package/src/tools/askUser.ts +0 -59
  672. package/src/tools/definitions.ts +0 -187
  673. package/src/tools/maintenance.ts +0 -24
  674. package/src/tools/memoryTools.ts +0 -22
  675. package/src/tools/pluginPropose.ts +0 -41
  676. package/src/tools/registryWrite.test.ts +0 -101
  677. package/src/tools/registryWrite.ts +0 -182
  678. package/src/tools/spawnSubagent.ts +0 -92
  679. package/src/tools/types.ts +0 -39
  680. package/src/web/safeRequest.ts +0 -171
  681. package/src/web/text.ts +0 -53
  682. package/src/web/tools.ts +0 -134
  683. package/src/web/web.test.ts +0 -141
@@ -1,189 +0,0 @@
1
- // The harness decides what a Pi session loads. Pi's own discovery is switched off for sessions
2
- // and terminals; they receive the paths chosen here, so nothing reaches a model unlisted.
3
- import { chmodSync, readdirSync } from 'node:fs'
4
- import { join } from 'node:path'
5
-
6
- import type { Bus } from '../core/bus'
7
- import { now, readJsonOr, writeJson } from '../core/fs'
8
- import { removeLinkedSkills } from '../skills/registry'
9
- import { attached, type Attachments, type AttachScope } from './attachments'
10
- import { piResolver, type Found, type ResolveInput, type Resolver } from './discover'
11
- import { foundIds, pluginIdOf } from './identity'
12
- import { packageCode, projectCode, Trust, type ProjectCode } from './trust'
13
-
14
- /** What one session or terminal loads besides the registry's own items. */
15
- export interface Loading {
16
- /** Extension files and folders, in the order Pi would have loaded them. */
17
- extensionPaths: string[]
18
- skillPaths: string[]
19
- promptPaths: string[]
20
- themePaths: string[]
21
- /** The `pi` command's own extensions left on, as `builtin:<name>`. Only a terminal has them. */
22
- builtins: string[]
23
- /** Hooks files the harness runs itself. Pi is never given them. */
24
- hooks: { path: string; packageRoot: string; hash: string }[]
25
- /** Everything found, loaded or not. */
26
- found: Found[]
27
- /** Paths of found items a person switched off for this caller. */
28
- detached: string[]
29
- /** Applies the same attachment policy to resources supplied by a trusted extension. */
30
- resourceAllowed?: (item: Found) => boolean
31
- /** Code the working folder brings, and whether a person trusted it. */
32
- projectCode: (ProjectCode & { trusted: boolean }) | null
33
- /** Each installed package that brings code, by its folder, and whether a person trusted it. */
34
- packageCode: Record<string, ProjectCode & { trusted: boolean }>
35
- }
36
-
37
- /** What one loaded extension added to a session. */
38
- export interface Registered {
39
- path: string
40
- hooks: string[]
41
- tools: string[]
42
- commands: string[]
43
- flags: string[]
44
- shortcuts: string[]
45
- /** Fingerprint at observation time; stale registrations are not presented as current. */
46
- hash?: string | null
47
- seenAt?: string
48
- resources?: RuntimeResource[]
49
- }
50
-
51
- export interface RuntimeResource {
52
- kind: 'skill' | 'command' | 'theme'
53
- name: string
54
- path: string
55
- chars: number | null
56
- }
57
-
58
- export interface PluginsOptions {
59
- stateDir: string
60
- bus: Bus
61
- resolver?: Resolver
62
- /** The choices a person made about what is switched on, or `null` while there is no data root. */
63
- attachments?: () => Attachments | null
64
- /** The folder of packages kept with a person's data, or `null` while there is no data root. */
65
- packagesDir?: () => string | null
66
- }
67
-
68
- /** The package folders inside a folder: each direct child that is a folder and not hidden. */
69
- export function packageFolders(directory: string | null): string[] {
70
- if (!directory) return []
71
- try {
72
- return readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith('.')).map((entry) => join(directory, entry.name)).sort()
73
- } catch {
74
- return []
75
- }
76
- }
77
-
78
- export interface LoadingInput extends ResolveInput {
79
- /** Who the session is for. Something switched off for this agent or project does not load. */
80
- caller?: AttachScope
81
- /** The contributions frozen for a run. When present, this snapshot governs loading. */
82
- contributions?: readonly string[]
83
- }
84
-
85
- /** Which code a person trusted: the working folder's as a whole, and each installed package's. */
86
- export interface Trusted {
87
- project: boolean
88
- /** Folders of installed packages whose code is not trusted. */
89
- untrustedPackages: ReadonlySet<string>
90
- }
91
-
92
- export const trustedBy = (loading: Pick<Loading, 'projectCode' | 'packageCode'> | null): Trusted => ({
93
- project: !!loading?.projectCode?.trusted,
94
- untrustedPackages: new Set(Object.entries(loading?.packageCode ?? {}).filter(([, code]) => !code.trusted).map(([root]) => root))
95
- })
96
-
97
- /**
98
- * Whether a found item loads: it is on in Pi's settings, and if it is code, a person trusted it.
99
- * A loose file in the shared folder was put there by hand and needs no trust. Text always loads.
100
- */
101
- export function loads(item: Found, trusted: Trusted): boolean {
102
- if (!item.enabled) return false
103
- if (item.kind !== 'extension' && item.kind !== 'hook') return true
104
- if (item.project) return trusted.project
105
- return !(item.packageRoot && trusted.untrustedPackages.has(item.packageRoot))
106
- }
107
-
108
- /**
109
- * Whether a person left a found item switched on for a caller. Pi reads its instruction files
110
- * itself and its command owns its built-in extensions, so neither can be switched off here.
111
- */
112
- export function switchable(item: Found): boolean {
113
- return item.kind !== 'context' && item.origin !== 'builtin'
114
- }
115
-
116
- export class Plugins {
117
- readonly trust: Trust
118
- private readonly resolver: Resolver
119
- private readonly registeredPath: string
120
-
121
- constructor(private readonly options: PluginsOptions) {
122
- this.trust = new Trust(options.stateDir, options.bus)
123
- this.resolver = options.resolver ?? piResolver
124
- this.registeredPath = join(options.stateDir, 'plugin-registered.json')
125
- }
126
-
127
- /** Finds what Pi would load in this folder, and chooses what does. */
128
- async loading(input: LoadingInput): Promise<Loading> {
129
- // Links an earlier version put in the shared skills folder would be found as skills again.
130
- removeLinkedSkills(join(input.agentDir, 'skills'))
131
- const found = await this.resolver({ ...input, packages: packageFolders(this.options.packagesDir?.() ?? null) })
132
- const code = projectCode(found, input.cwd)
133
- const packages = packageCode(found)
134
- // Packages that loaded before trust was asked per package keep loading: they are taken over once.
135
- if (!this.trust.adopted) this.trust.adopt([...packages].map(([folder, packaged]) => ({ code: packaged, folder })))
136
- const trustedHashes = new Set(this.trust.list().map((record) => record.hash))
137
- const projectCodeState = code ? { ...code, trusted: trustedHashes.has(code.hash) } : null
138
- const packageCodeState = Object.fromEntries([...packages].map(([folder, packaged]) => [folder, { ...packaged, trusted: trustedHashes.has(packaged.hash) }]))
139
- const trusted = trustedBy({ projectCode: projectCodeState, packageCode: packageCodeState })
140
- const choices = this.options.attachments?.() ?? null
141
- const off = (item: Found): boolean => {
142
- if (!choices || !switchable(item)) return false
143
- // A hook runs for everyone or not at all, so only its plugin's switch for everyone counts.
144
- if (item.kind === 'hook') return !attached(choices, [pluginIdOf(item)], {}, true)
145
- return !attached(choices, foundIds(item), input.caller ?? {}, true)
146
- }
147
- const allowed = (item: Found): boolean => input.contributions === undefined
148
- ? !off(item)
149
- : input.contributions.includes(foundIds(item)[0])
150
- const detached = found.filter((item) => !allowed(item)).map((item) => item.path)
151
- const chosen = found.filter((item) => loads(item, trusted) && allowed(item))
152
- const paths = (kind: Found['kind'], builtin: boolean): string[] => chosen.filter((item) => item.kind === kind && (item.origin === 'builtin') === builtin).map((item) => item.path)
153
- return {
154
- extensionPaths: paths('extension', false),
155
- skillPaths: paths('skill', false),
156
- promptPaths: paths('prompt', false),
157
- themePaths: paths('theme', false),
158
- builtins: paths('extension', true),
159
- hooks: chosen.filter((item) => item.kind === 'hook' && item.packageRoot).map((item) => ({ path: item.path, packageRoot: item.packageRoot!, hash: packageCodeState[item.packageRoot!]?.hash ?? '' })),
160
- found,
161
- detached,
162
- resourceAllowed: allowed,
163
- projectCode: projectCodeState,
164
- packageCode: packageCodeState
165
- }
166
- }
167
-
168
- /** What each extension registered the last time a session loaded it. */
169
- registered(): Record<string, Registered & { seenAt: string }> {
170
- return readJsonOr<Record<string, Registered & { seenAt: string }>>(this.registeredPath, {})
171
- }
172
-
173
- /** Records what a session's extensions registered, so the list can show it without loading code. */
174
- loaded(extensions: readonly Registered[]): void {
175
- if (!extensions.length) return
176
- const stored = this.registered()
177
- let changed = false
178
- for (const extension of extensions) {
179
- const { seenAt: _seenAt, ...before } = stored[extension.path] ?? ({} as Registered & { seenAt: string })
180
- if (JSON.stringify(before) === JSON.stringify(extension)) continue
181
- stored[extension.path] = { ...extension, seenAt: now() }
182
- changed = true
183
- }
184
- if (!changed) return
185
- writeJson(this.registeredPath, stored)
186
- chmodSync(this.registeredPath, 0o600)
187
- this.options.bus.emit({ type: 'plugins.changed' })
188
- }
189
- }
@@ -1,121 +0,0 @@
1
- // A plugin an agent wrote for a person to consider. Nothing here installs anything on its own:
2
- // the proposal waits in the Inbox; accepting it writes a package beside the person's data,
3
- // switched off and (when it brings code) untrusted. The person then reads it, switches it on,
4
- // and trusts it. No agent has a tool for either of those.
5
- import { existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from 'node:fs'
6
- import { dirname, join } from 'node:path'
7
- import { z } from 'zod'
8
-
9
- import { invalid } from '../core/errors'
10
- import { now, safeRelative, writeJson } from '../core/fs'
11
- import { slugify } from '../core/projects'
12
- import { inbox } from '../memory/operations'
13
- import { looksSecret } from '../memory/secrets'
14
- import * as store from '../memory/store'
15
- import * as attachments from './attachments'
16
-
17
- /** The parts a plugin can say it extends. */
18
- export const EXTENDABLE = ['memory', 'wiki', 'history'] as const
19
-
20
- const MAX_FILES = 40
21
- const MAX_BYTES = 256 * 1024
22
- const HOOKS_FILES = ['hooks.mjs', 'hooks.js']
23
-
24
- export const pluginProposalSchema = z.object({
25
- name: z.string(),
26
- description: z.string().default(''),
27
- extends: z.array(z.string()).default([]),
28
- reason: z.string().default(''),
29
- files: z.array(z.object({ path: z.string(), content: z.string() }))
30
- })
31
- export type PluginProposal = z.infer<typeof pluginProposalSchema>
32
-
33
- interface Checked {
34
- slug: string
35
- extends: string[]
36
- hooks: string | null
37
- runsCode: boolean
38
- files: { path: string; content: string }[]
39
- }
40
-
41
- /** The proposal with its paths made safe, or the reason it cannot be a plugin. */
42
- export function checkPluginProposal(proposal: PluginProposal): Checked {
43
- const name = proposal.name.trim()
44
- const slug = slugify(name)
45
- if (!name || slug.startsWith('page-') || name.length > 64) throw invalid('Give the plugin a short name of letters and numbers.')
46
- if (!proposal.description.trim()) throw invalid('Say in one line what the plugin does; that line is what the user sees first.')
47
- const unknown = proposal.extends.find((part) => !(EXTENDABLE as readonly string[]).includes(part))
48
- if (unknown !== undefined) throw invalid(`A plugin can extend ${EXTENDABLE.join(', ')}; "${unknown}" is none of them.`)
49
- if (!proposal.files.length || proposal.files.length > MAX_FILES) throw invalid(`A plugin needs between 1 and ${MAX_FILES} files.`)
50
- const files: Checked['files'] = []
51
- let bytes = 0
52
- for (const file of proposal.files) {
53
- let path: string
54
- try {
55
- path = safeRelative(file.path.replace(/^\.\//, ''))
56
- } catch {
57
- throw invalid(`"${file.path}" is not a path inside the plugin.`)
58
- }
59
- const parts = path.split('/')
60
- if (parts.some((part) => part.startsWith('.') || part.includes(':') || part === 'node_modules')) throw invalid(`"${file.path}" cannot be part of a plugin.`)
61
- if (path === 'package.json') throw invalid('Leave package.json out; it is written from the name and description you gave.')
62
- if (files.some((existing) => existing.path.toLowerCase() === path.toLowerCase())) throw invalid(`"${file.path}" is listed twice.`)
63
- bytes += Buffer.byteLength(file.content, 'utf8')
64
- if (bytes > MAX_BYTES) throw invalid('The plugin is larger than 256 KiB.')
65
- if (looksSecret(file.content)) throw invalid(`"${file.path}" contains text that looks like a credential. A plugin reaches outside through a connected app, never with a key of its own.`)
66
- files.push({ path, content: file.content })
67
- }
68
- const hooks = files.find((file) => HOOKS_FILES.includes(file.path))?.path ?? null
69
- return { slug, extends: [...new Set(proposal.extends)], hooks, runsCode: hooks !== null || files.some((file) => file.path.startsWith('extensions/')), files }
70
- }
71
-
72
- /** Puts a proposed plugin in the Inbox, for a person to read before anything is written. */
73
- export function parkPluginProposal(wiki: string, from: string, proposal: PluginProposal): string {
74
- const checked = checkPluginProposal(proposal)
75
- const listed = checked.files.map((file) => `- ${file.path} (${file.content.length} characters)`).join('\n')
76
- const code = checked.runsCode ? '\n\nIt brings code. Accepting only saves the files: the code runs after you also switch the plugin on and trust it.' : ''
77
- inbox(
78
- wiki,
79
- 'proposed',
80
- `Proposed plugin "${proposal.name.trim()}"`,
81
- `${proposal.description.trim()}\n\n${proposal.reason.trim() || 'Review it before it is saved.'}${checked.extends.length ? `\n\nExtends: ${checked.extends.join(', ')}.` : ''}\n\nFiles:\n${listed}${code}\n\nFrom ${from}.`,
82
- [],
83
- null,
84
- { kind: 'plugin', proposal: { ...proposal, files: checked.files }, trigger: from }
85
- )
86
- store.log(wiki, `- ${now()} · ${from} · proposed plugin \`${checked.slug}\` · waiting in Inbox`)
87
- return checked.slug
88
- }
89
-
90
- /** Writes an accepted proposal as a package beside the person's data, switched off everywhere. */
91
- export function acceptPluginProposal(root: string, proposal: PluginProposal): string {
92
- const checked = checkPluginProposal(proposal)
93
- const directory = join(root, 'plugins')
94
- const target = join(directory, checked.slug)
95
- if (existsSync(target)) throw invalid(`There is already a plugin folder called "${checked.slug}". Remove it or rename the proposal.`)
96
- mkdirSync(directory, { recursive: true })
97
- // Written beside its place and moved in whole, so a half-written plugin is never found.
98
- const staging = mkdtempSync(join(directory, '.accepting-'))
99
- try {
100
- writeJson(join(staging, 'package.json'), {
101
- name: checked.slug,
102
- version: '0.1.0',
103
- description: proposal.description.trim(),
104
- keywords: ['pi-package'],
105
- ...(checked.extends.length || checked.hooks ? { harness: { ...(checked.extends.length ? { extends: checked.extends } : {}), ...(checked.hooks ? { hooks: `./${checked.hooks}` } : {}) } } : {})
106
- })
107
- for (const file of checked.files) {
108
- const path = join(staging, ...file.path.split('/'))
109
- mkdirSync(dirname(path), { recursive: true })
110
- writeFileSync(path, file.content, { flag: 'wx' })
111
- }
112
- renameSync(staging, target)
113
- } catch (error) {
114
- rmSync(staging, { recursive: true, force: true })
115
- throw error
116
- }
117
- const choices = attachments.load(root)
118
- attachments.set(choices, [`package:${checked.slug}`], {}, false, true)
119
- attachments.save(root, choices)
120
- return `Saved the plugin "${checked.slug}", switched off.${checked.runsCode ? ' Its code does not run until you switch it on and trust it.' : ' Switch it on to use it.'}`
121
- }
@@ -1,40 +0,0 @@
1
- import { mkdtempSync, symlinkSync } from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { expect, it } from 'vitest'
5
- import type * as Pi from '@earendil-works/pi-coding-agent'
6
-
7
- import { writeFile } from '../../tests/helpers'
8
- import type { Found } from './discover'
9
- import type { Loading, Registered } from './plugins'
10
- import { observeResources } from './runtime'
11
-
12
- it('expands theme directories, filters individual files and replaces each handler observation', async () => {
13
- const pi = await import('@earendil-works/pi-coding-agent')
14
- const base = mkdtempSync(join(tmpdir(), 'resource-observation-'))
15
- const path = join(base, 'extension.ts')
16
- writeFile(path, 'export default function() {}')
17
- const first = join(base, 'themes/first.json')
18
- const second = join(base, 'themes/second.json')
19
- writeFile(first, '{}')
20
- writeFile(second, '{}')
21
- symlinkSync(join(base, 'absent.json'), join(base, 'themes/broken.json'))
22
- writeFile(join(base, 'review.md'), '---\nname: runtime-review\ndescription: Review.\n---\nReview text.')
23
- let active = true
24
- const handlers = new Map([['resources_discover', [async () => active ? { themePaths: ['./themes'] } : undefined, async () => ({ promptPaths: ['./review.md'] })]]])
25
- const extension = { path, resolvedPath: path, handlers }
26
- const loader = { getExtensions: () => ({ extensions: [extension] }) } as unknown as Pi.DefaultResourceLoader
27
- const owner: Found = { kind: 'extension', path, origin: 'shared', project: false, packageRoot: null, source: null, enabled: true }
28
- const loading: Loading = { extensionPaths: [path], skillPaths: [], promptPaths: [], themePaths: [], builtins: [], hooks: [], found: [owner], detached: [], projectCode: null, packageCode: {}, resourceAllowed: (item) => item.path !== first }
29
- const records: Registered[] = [{ path, tools: [], commands: [], hooks: ['resources_discover'], flags: [], shortcuts: [] }]
30
- let published = 0
31
- observeResources(pi, loader, loading, records, () => { published++ })
32
- const wrapped = extension.handlers.get('resources_discover')! as unknown as Array<(event: unknown, context: unknown) => Promise<Pi.ResourcesDiscoverResult | undefined>>
33
- expect(await wrapped[0]({}, {})).toMatchObject({ themePaths: [second] })
34
- await wrapped[1]({}, {})
35
- expect(records[0].resources?.map((resource) => resource.name)).toEqual(['first.json', 'second.json', 'runtime-review'])
36
- active = false
37
- await wrapped[0]({}, {})
38
- expect(records[0].resources?.map((resource) => resource.name)).toEqual(['runtime-review'])
39
- expect(published).toBe(3)
40
- })
@@ -1,82 +0,0 @@
1
- // Observe Pi's structured resource discovery without running code while the catalogue is read.
2
- import { createHash } from 'node:crypto'
3
- import { readFileSync, readdirSync, statSync } from 'node:fs'
4
- import { dirname, resolve } from 'node:path'
5
- import type * as Pi from '@earendil-works/pi-coding-agent'
6
-
7
- import { charCount } from '../core/text'
8
- import { FOUND_KINDS, describeFound } from './identity'
9
- import type { Found } from './discover'
10
- import type { Loading, Registered, RuntimeResource } from './plugins'
11
-
12
- export function runtimeOwner(loading: Loading, path: string): Found | undefined {
13
- return loading.found.find((item) => item.kind === 'extension' && (item.path === path || path.startsWith(`${item.path}/`)))
14
- }
15
-
16
- export function registrationHash(loading: Loading, path: string): string | null {
17
- const owner = runtimeOwner(loading, path)
18
- if (owner?.project) return loading.projectCode?.hash ?? null
19
- if (owner?.packageRoot) return loading.packageCode[owner.packageRoot]?.hash ?? null
20
- try { return createHash('sha256').update(readFileSync(path)).digest('hex') } catch { return null }
21
- }
22
-
23
- /** A discovered resource has the same identity for its switch, catalogue and load decision. */
24
- export function resourceOf(owner: Found, kind: 'skill' | 'prompt' | 'theme', path: string): RuntimeResource {
25
- const found = { ...owner, kind, path }
26
- const described = describeFound(found)
27
- let chars: number | null = null
28
- try { chars = charCount(readFileSync(path, 'utf8')) } catch { /* Missing files remain observable. */ }
29
- return { kind: FOUND_KINDS[kind] as RuntimeResource['kind'], name: described.name, path, chars }
30
- }
31
-
32
- /** Wrap each handler separately so identical extension basenames never lose their owner. */
33
- export function observeResources(pi: typeof Pi, loader: Pi.DefaultResourceLoader, loading: Loading, records: Registered[], publish: () => void): void {
34
- for (const extension of loader.getExtensions().extensions) {
35
- if (extension.hidden) continue
36
- const path = extension.resolvedPath || extension.path
37
- const owner = runtimeOwner(loading, path)
38
- const record = records.find((entry) => entry.path === path)
39
- if (!owner || !record) continue
40
- const handlers = extension.handlers.get('resources_discover')
41
- if (!handlers) continue
42
- const observations = new Map<number, RuntimeResource[]>()
43
- extension.handlers.set('resources_discover', handlers.map((handler, index) => async (event, context) => {
44
- const result = await handler(event, context) as Pi.ResourcesDiscoverResult | undefined
45
- if (!result) {
46
- observations.delete(index)
47
- record.resources = [...observations.values()].flat()
48
- publish()
49
- return result
50
- }
51
- const collected: RuntimeResource[] = []
52
- const allowed = (kind: 'skill' | 'prompt' | 'theme', resourcePath: string): boolean => {
53
- const resource = resourceOf(owner, kind, resourcePath)
54
- collected.push(resource)
55
- return loading.resourceAllowed?.({ ...owner, kind, path: resourcePath }) !== false
56
- }
57
- const absolute = (entry: string): string => resolve(dirname(path), entry)
58
- const skills = pi.loadSkills({ cwd: dirname(path), agentDir: dirname(path), includeDefaults: false, skillPaths: (result.skillPaths ?? []).map(absolute) }).skills
59
- const scanner = new pi.DefaultResourceLoader({ cwd: dirname(path), agentDir: dirname(path), settingsManager: pi.SettingsManager.inMemory({}), noExtensions: true, noSkills: true, noThemes: true, noContextFiles: true, noPromptTemplates: true, additionalPromptTemplatePaths: (result.promptPaths ?? []).map(absolute) })
60
- await scanner.reload()
61
- const prompts = scanner.getPrompts().prompts
62
- const themes = (result.themePaths ?? []).map(absolute).flatMap((theme) => {
63
- try {
64
- if (statSync(theme).isDirectory()) return readdirSync(theme).filter((name) => {
65
- if (!name.endsWith('.json')) return false
66
- try { return statSync(resolve(theme, name)).isFile() } catch { return false }
67
- }).map((name) => resolve(theme, name))
68
- } catch { /* Preserve missing paths for Pi's diagnostics. */ }
69
- return [theme]
70
- })
71
- const filtered = {
72
- skillPaths: skills.filter((skill) => allowed('skill', skill.filePath)).map((skill) => skill.filePath),
73
- promptPaths: prompts.filter((prompt) => allowed('prompt', prompt.filePath)).map((prompt) => prompt.filePath),
74
- themePaths: themes.filter((theme) => allowed('theme', theme))
75
- }
76
- observations.set(index, collected)
77
- record.resources = [...new Map([...observations.values()].flat().map((entry) => [`${entry.kind}:${entry.path}`, entry])).values()]
78
- publish()
79
- return filtered
80
- }))
81
- }
82
- }
@@ -1,142 +0,0 @@
1
- // Code a person did not write into the shared folder by hand loads only after they trusted it:
2
- // a working folder's own (its `.pi/extensions`, or a package its settings name), and every
3
- // installed package. Trust belongs to the code, not the folder: it is a hash of the files, so a
4
- // copy of the same code elsewhere is trusted too and a changed file asks again.
5
- import { createHash } from 'node:crypto'
6
- import { chmodSync, lstatSync, readdirSync, readFileSync } from 'node:fs'
7
- import { basename, dirname, join, relative, sep } from 'node:path'
8
-
9
- import type { Bus } from '../core/bus'
10
- import { now, readJsonOr, writeJson } from '../core/fs'
11
- import { compareText } from '../core/text'
12
- import type { Found } from './discover'
13
-
14
- export interface ProjectCode {
15
- /** SHA-256 over every file's name and contents. */
16
- hash: string
17
- /** The files, named from the working folder where they are inside it. */
18
- files: string[]
19
- }
20
-
21
- // Dependencies and version control are left out: the hash says whether the code a person
22
- // read has changed, and is not a seal on everything that code can import.
23
- const SKIPPED = new Set(['node_modules', '.git'])
24
- const MAX_FILES = 5000
25
-
26
- function walk(path: string, into: string[]): void {
27
- if (into.length >= MAX_FILES) return
28
- let meta
29
- try {
30
- meta = lstatSync(path)
31
- } catch {
32
- return
33
- }
34
- if (meta.isSymbolicLink()) return
35
- if (meta.isFile()) {
36
- into.push(path)
37
- return
38
- }
39
- if (!meta.isDirectory()) return
40
- for (const name of readdirSync(path).sort(compareText)) if (!SKIPPED.has(name)) walk(join(path, name), into)
41
- }
42
-
43
- /** The folder or file that holds one extension: its package, its own folder, or the file. */
44
- const unit = (item: Found): string => item.packageRoot ?? (basename(item.path).startsWith('index.') ? dirname(item.path) : item.path)
45
-
46
- /** A hash over the files of some folders and files, each named from `base` where it is inside it. */
47
- function hashOf(units: readonly string[], base: string): ProjectCode {
48
- const paths: string[] = []
49
- for (const path of [...units].sort(compareText)) walk(path, paths)
50
- const name = (path: string): string => {
51
- const within = relative(base, path)
52
- return within && !within.startsWith('..') ? within.split(sep).join('/') : path
53
- }
54
- const hash = createHash('sha256')
55
- const files: string[] = []
56
- for (const path of [...new Set(paths)].sort(compareText)) {
57
- let contents: Buffer
58
- try {
59
- contents = readFileSync(path)
60
- } catch {
61
- continue
62
- }
63
- files.push(name(path))
64
- hash.update(`${name(path)}\0${contents.length}\0`).update(contents)
65
- }
66
- return { hash: hash.digest('hex'), files }
67
- }
68
-
69
- /** The code the working folder brings, or `null` when it brings none. */
70
- export function projectCode(found: readonly Found[], cwd: string): ProjectCode | null {
71
- const units = [...new Set(found.filter((item) => item.project && item.kind === 'extension' && item.enabled).map(unit))]
72
- return units.length ? hashOf(units, cwd) : null
73
- }
74
-
75
- /**
76
- * Each package a person installed that brings code, by its folder: the whole package is hashed,
77
- * so a changed skill or manifest asks again as well. A working folder's packages are part of
78
- * `projectCode`.
79
- */
80
- export function packageCode(found: readonly Found[]): Map<string, ProjectCode> {
81
- const roots = [...new Set(found.filter((item) => !item.project && (item.kind === 'extension' || item.kind === 'hook') && item.packageRoot).map((item) => item.packageRoot!))]
82
- return new Map(roots.map((root) => [root, hashOf([root], root)]))
83
- }
84
-
85
- export interface TrustRecord {
86
- hash: string
87
- /** The working folder it was trusted in. */
88
- folder: string
89
- files: string[]
90
- trustedAt: string
91
- }
92
-
93
- export class Trust {
94
- private readonly path: string
95
-
96
- constructor(stateDir: string, private readonly bus: Bus) {
97
- this.path = join(stateDir, 'plugin-trust.json')
98
- }
99
-
100
- private read(): { trusted?: unknown; adopted?: unknown } {
101
- return readJsonOr<{ trusted?: unknown; adopted?: unknown }>(this.path, {})
102
- }
103
-
104
- list(): TrustRecord[] {
105
- const stored = this.read()
106
- return Array.isArray(stored.trusted) ? (stored.trusted as TrustRecord[]).filter((record) => typeof record?.hash === 'string') : []
107
- }
108
-
109
- /** Whether the packages that loaded before trust was asked per package were taken over as trusted. */
110
- get adopted(): boolean {
111
- return this.read().adopted === true
112
- }
113
-
114
- /**
115
- * Trusts the packages present the first time this runs, so nothing that loaded before stops.
116
- * It happens once; a package installed later, or one of these after a change, waits for a person.
117
- */
118
- adopt(codes: readonly { code: ProjectCode; folder: string }[]): void {
119
- const records = this.list()
120
- for (const { code, folder } of codes) if (!records.some((record) => record.hash === code.hash)) records.push({ hash: code.hash, folder, files: code.files, trustedAt: now() })
121
- writeJson(this.path, { trusted: records, adopted: true })
122
- chmodSync(this.path, 0o600)
123
- }
124
-
125
- has(hash: string): boolean {
126
- return this.list().some((record) => record.hash === hash)
127
- }
128
-
129
- private save(records: TrustRecord[]): void {
130
- writeJson(this.path, { trusted: records, ...(this.adopted ? { adopted: true } : {}) })
131
- chmodSync(this.path, 0o600)
132
- this.bus.emit({ type: 'plugins.changed' })
133
- }
134
-
135
- grant(code: ProjectCode, folder: string): void {
136
- this.save([...this.list().filter((record) => record.hash !== code.hash), { hash: code.hash, folder, files: code.files, trustedAt: now() }])
137
- }
138
-
139
- revoke(hash: string): void {
140
- this.save(this.list().filter((record) => record.hash !== hash))
141
- }
142
- }