@ouro.bot/cli 0.1.0-alpha.43 → 0.1.0-alpha.430

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 (337) hide show
  1. package/README.md +123 -15
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  4. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  5. package/changelog.json +2715 -9
  6. package/dist/arc/attention-types.js +8 -0
  7. package/dist/arc/cares.js +140 -0
  8. package/dist/arc/episodes.js +117 -0
  9. package/dist/arc/intentions.js +133 -0
  10. package/dist/arc/json-store.js +117 -0
  11. package/dist/arc/obligations.js +237 -0
  12. package/dist/arc/packets.js +193 -0
  13. package/dist/arc/presence.js +185 -0
  14. package/dist/arc/task-lifecycle.js +65 -0
  15. package/dist/heart/active-work.js +832 -0
  16. package/dist/heart/agent-entry.js +58 -3
  17. package/dist/heart/attachments/image-normalize.js +194 -0
  18. package/dist/heart/attachments/materialize.js +97 -0
  19. package/dist/heart/attachments/originals.js +88 -0
  20. package/dist/heart/attachments/render.js +29 -0
  21. package/dist/heart/attachments/sources/adapter.js +2 -0
  22. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  23. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  24. package/dist/heart/attachments/sources/index.js +16 -0
  25. package/dist/heart/attachments/store.js +103 -0
  26. package/dist/heart/attachments/types.js +93 -0
  27. package/dist/heart/auth/auth-flow.js +426 -0
  28. package/dist/heart/bridges/manager.js +358 -0
  29. package/dist/heart/bridges/state-machine.js +135 -0
  30. package/dist/heart/bridges/store.js +123 -0
  31. package/dist/heart/bundle-state.js +168 -0
  32. package/dist/heart/commitments.js +111 -0
  33. package/dist/heart/config-registry.js +304 -0
  34. package/dist/heart/config.js +110 -128
  35. package/dist/heart/core.js +745 -227
  36. package/dist/heart/cross-chat-delivery.js +131 -0
  37. package/dist/heart/daemon/agent-config-check.js +424 -0
  38. package/dist/heart/daemon/agent-discovery.js +79 -3
  39. package/dist/heart/daemon/agent-service.js +360 -0
  40. package/dist/heart/daemon/agentic-repair.js +214 -0
  41. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  42. package/dist/heart/daemon/cadence.js +70 -0
  43. package/dist/heart/daemon/cli-defaults.js +604 -0
  44. package/dist/heart/daemon/cli-exec.js +5094 -0
  45. package/dist/heart/daemon/cli-help.js +428 -0
  46. package/dist/heart/daemon/cli-parse.js +1156 -0
  47. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  48. package/dist/heart/daemon/cli-render.js +561 -0
  49. package/dist/heart/daemon/cli-types.js +8 -0
  50. package/dist/heart/daemon/connect-bay.js +442 -0
  51. package/dist/heart/daemon/daemon-cli.js +28 -1582
  52. package/dist/heart/daemon/daemon-entry.js +356 -3
  53. package/dist/heart/daemon/daemon-health.js +141 -0
  54. package/dist/heart/daemon/daemon-runtime-sync.js +175 -12
  55. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  56. package/dist/heart/daemon/daemon.js +684 -58
  57. package/dist/heart/daemon/doctor-types.js +8 -0
  58. package/dist/heart/daemon/doctor.js +427 -0
  59. package/dist/heart/daemon/health-monitor.js +79 -1
  60. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  61. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  62. package/dist/heart/daemon/http-health-probe.js +80 -0
  63. package/dist/heart/daemon/human-command-screens.js +140 -0
  64. package/dist/heart/daemon/human-readiness.js +114 -0
  65. package/dist/heart/daemon/inner-status.js +89 -0
  66. package/dist/heart/daemon/interactive-repair.js +307 -0
  67. package/dist/heart/daemon/launchd.js +46 -9
  68. package/dist/heart/daemon/log-tailer.js +82 -12
  69. package/dist/heart/daemon/logs-prune.js +105 -0
  70. package/dist/heart/daemon/message-router.js +2 -2
  71. package/dist/heart/daemon/os-cron-deps.js +134 -0
  72. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  73. package/dist/heart/daemon/ouro-entry.js +3 -1
  74. package/dist/heart/daemon/process-manager.js +214 -0
  75. package/dist/heart/daemon/provider-discovery.js +137 -0
  76. package/dist/heart/daemon/pulse.js +475 -0
  77. package/dist/heart/daemon/readiness-repair.js +275 -0
  78. package/dist/heart/daemon/run-hooks.js +2 -0
  79. package/dist/heart/daemon/runtime-logging.js +67 -16
  80. package/dist/heart/daemon/runtime-metadata.js +73 -0
  81. package/dist/heart/daemon/runtime-mode.js +67 -0
  82. package/dist/heart/daemon/safe-mode.js +161 -0
  83. package/dist/heart/daemon/sense-manager.js +145 -32
  84. package/dist/heart/daemon/session-id-resolver.js +131 -0
  85. package/dist/heart/daemon/skill-management-installer.js +94 -0
  86. package/dist/heart/daemon/socket-client.js +307 -0
  87. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  88. package/dist/heart/daemon/startup-tui.js +259 -0
  89. package/dist/heart/daemon/task-scheduler.js +3 -25
  90. package/dist/heart/daemon/terminal-ui.js +169 -0
  91. package/dist/heart/daemon/thoughts.js +510 -0
  92. package/dist/heart/daemon/up-progress.js +259 -0
  93. package/dist/heart/delegation.js +62 -0
  94. package/dist/heart/habits/habit-migration.js +189 -0
  95. package/dist/heart/habits/habit-parser.js +140 -0
  96. package/dist/heart/habits/habit-runtime-state.js +100 -0
  97. package/dist/heart/habits/habit-scheduler.js +372 -0
  98. package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -117
  99. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  100. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  101. package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
  102. package/dist/heart/identity.js +161 -65
  103. package/dist/heart/kept-notes.js +357 -0
  104. package/dist/heart/kicks.js +1 -1
  105. package/dist/heart/machine-identity.js +161 -0
  106. package/dist/heart/mcp/mcp-server.js +653 -0
  107. package/dist/heart/migrate-config.js +100 -0
  108. package/dist/heart/model-capabilities.js +59 -0
  109. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  110. package/dist/heart/outlook/outlook-http-response.js +7 -0
  111. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  112. package/dist/heart/outlook/outlook-http-static.js +99 -0
  113. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  114. package/dist/heart/outlook/outlook-http.js +99 -0
  115. package/dist/heart/outlook/outlook-read.js +28 -0
  116. package/dist/heart/outlook/outlook-types.js +27 -0
  117. package/dist/heart/outlook/outlook-view.js +195 -0
  118. package/dist/heart/outlook/readers/agent-machine.js +359 -0
  119. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  120. package/dist/heart/outlook/readers/runtime-readers.js +644 -0
  121. package/dist/heart/outlook/readers/sessions.js +232 -0
  122. package/dist/heart/outlook/readers/shared.js +111 -0
  123. package/dist/heart/platform.js +81 -0
  124. package/dist/heart/progress-story.js +42 -0
  125. package/dist/heart/provider-attempt.js +133 -0
  126. package/dist/heart/provider-binding-resolver.js +239 -0
  127. package/dist/heart/provider-credentials.js +391 -0
  128. package/dist/heart/provider-failover.js +266 -0
  129. package/dist/heart/provider-models.js +81 -0
  130. package/dist/heart/provider-ping.js +237 -0
  131. package/dist/heart/provider-state.js +216 -0
  132. package/dist/heart/provider-visibility.js +186 -0
  133. package/dist/heart/providers/anthropic-token.js +131 -0
  134. package/dist/heart/providers/anthropic.js +193 -55
  135. package/dist/heart/providers/azure.js +103 -12
  136. package/dist/heart/providers/error-classification.js +63 -0
  137. package/dist/heart/providers/github-copilot.js +145 -0
  138. package/dist/heart/providers/minimax-vlm.js +189 -0
  139. package/dist/heart/providers/minimax.js +29 -7
  140. package/dist/heart/providers/openai-codex.js +62 -38
  141. package/dist/heart/runtime-credentials.js +260 -0
  142. package/dist/heart/sense-truth.js +3 -0
  143. package/dist/heart/session-activity.js +190 -0
  144. package/dist/heart/session-events.js +855 -0
  145. package/dist/heart/session-transcript.js +167 -0
  146. package/dist/heart/start-of-turn-packet.js +345 -0
  147. package/dist/heart/streaming.js +36 -27
  148. package/dist/heart/sync.js +332 -0
  149. package/dist/heart/target-resolution.js +127 -0
  150. package/dist/heart/tempo.js +93 -0
  151. package/dist/heart/temporal-view.js +41 -0
  152. package/dist/heart/tool-activity-callbacks.js +36 -0
  153. package/dist/heart/tool-description.js +135 -0
  154. package/dist/heart/tool-friction.js +55 -0
  155. package/dist/heart/tool-loop.js +200 -0
  156. package/dist/heart/turn-context.js +351 -0
  157. package/dist/heart/turn-coordinator.js +28 -0
  158. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  159. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  160. package/dist/heart/versioning/ouro-path-installer.js +301 -0
  161. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  162. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  163. package/dist/heart/{daemon → versioning}/update-checker.js +3 -1
  164. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  165. package/dist/mind/bundle-manifest.js +7 -1
  166. package/dist/mind/context.js +132 -93
  167. package/dist/mind/diary-integrity.js +60 -0
  168. package/dist/mind/{memory.js → diary.js} +74 -93
  169. package/dist/mind/embedding-provider.js +60 -0
  170. package/dist/mind/file-state.js +179 -0
  171. package/dist/mind/friends/channel.js +21 -0
  172. package/dist/mind/friends/group-context.js +144 -0
  173. package/dist/mind/friends/resolver.js +38 -1
  174. package/dist/mind/friends/store-file.js +39 -3
  175. package/dist/mind/friends/trust-explanation.js +74 -0
  176. package/dist/mind/friends/types.js +1 -1
  177. package/dist/mind/journal-index.js +161 -0
  178. package/dist/mind/note-search.js +268 -0
  179. package/dist/mind/obligation-steering.js +221 -0
  180. package/dist/mind/pending.js +66 -7
  181. package/dist/mind/prompt-refresh.js +3 -2
  182. package/dist/mind/prompt.js +947 -165
  183. package/dist/mind/provenance-trust.js +26 -0
  184. package/dist/mind/scrutiny.js +173 -0
  185. package/dist/nerves/cli-logging.js +7 -1
  186. package/dist/nerves/coverage/audit-rules.js +15 -6
  187. package/dist/nerves/coverage/audit.js +28 -2
  188. package/dist/nerves/coverage/cli.js +1 -1
  189. package/dist/nerves/coverage/contract.js +5 -5
  190. package/dist/nerves/coverage/file-completeness.js +83 -5
  191. package/dist/nerves/coverage/run-artifacts.js +1 -1
  192. package/dist/nerves/event-buffer.js +111 -0
  193. package/dist/nerves/index.js +224 -4
  194. package/dist/nerves/observation.js +20 -0
  195. package/dist/nerves/redact.js +79 -0
  196. package/dist/nerves/runtime.js +5 -1
  197. package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
  198. package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
  199. package/dist/outlook-ui/index.html +15 -0
  200. package/dist/repertoire/ado-client.js +15 -56
  201. package/dist/repertoire/ado-semantic.js +11 -10
  202. package/dist/repertoire/api-client.js +97 -0
  203. package/dist/repertoire/bitwarden-store.js +702 -0
  204. package/dist/repertoire/bundle-templates.js +72 -0
  205. package/dist/repertoire/bw-installer.js +79 -0
  206. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  207. package/dist/repertoire/coding/context-pack.js +330 -0
  208. package/dist/repertoire/coding/feedback.js +197 -30
  209. package/dist/repertoire/coding/manager.js +158 -9
  210. package/dist/repertoire/coding/spawner.js +55 -9
  211. package/dist/repertoire/coding/tools.js +170 -7
  212. package/dist/repertoire/commerce-errors.js +109 -0
  213. package/dist/repertoire/commerce-self-test.js +156 -0
  214. package/dist/repertoire/credential-access.js +111 -0
  215. package/dist/repertoire/duffel-client.js +185 -0
  216. package/dist/repertoire/github-client.js +14 -55
  217. package/dist/repertoire/graph-client.js +11 -52
  218. package/dist/repertoire/guardrails.js +371 -0
  219. package/dist/repertoire/mcp-client.js +255 -0
  220. package/dist/repertoire/mcp-manager.js +305 -0
  221. package/dist/repertoire/mcp-tools.js +63 -0
  222. package/dist/repertoire/shell-sessions.js +133 -0
  223. package/dist/repertoire/skills.js +15 -24
  224. package/dist/repertoire/stripe-client.js +131 -0
  225. package/dist/repertoire/tasks/board.js +43 -5
  226. package/dist/repertoire/tasks/fix.js +182 -0
  227. package/dist/repertoire/tasks/index.js +37 -4
  228. package/dist/repertoire/tasks/lifecycle.js +2 -2
  229. package/dist/repertoire/tasks/parser.js +3 -2
  230. package/dist/repertoire/tasks/scanner.js +194 -37
  231. package/dist/repertoire/tasks/transitions.js +16 -78
  232. package/dist/repertoire/tool-results.js +29 -0
  233. package/dist/repertoire/tools-attachments.js +317 -0
  234. package/dist/repertoire/tools-base.js +42 -690
  235. package/dist/repertoire/tools-bluebubbles.js +1 -0
  236. package/dist/repertoire/tools-bridge.js +141 -0
  237. package/dist/repertoire/tools-bundle.js +984 -0
  238. package/dist/repertoire/tools-config.js +185 -0
  239. package/dist/repertoire/tools-continuity.js +248 -0
  240. package/dist/repertoire/tools-credential.js +361 -0
  241. package/dist/repertoire/tools-files.js +342 -0
  242. package/dist/repertoire/tools-flight.js +224 -0
  243. package/dist/repertoire/tools-flow.js +105 -0
  244. package/dist/repertoire/tools-github.js +1 -7
  245. package/dist/repertoire/tools-notes.js +376 -0
  246. package/dist/repertoire/tools-session.js +739 -0
  247. package/dist/repertoire/tools-shell.js +120 -0
  248. package/dist/repertoire/tools-stripe.js +180 -0
  249. package/dist/repertoire/tools-surface.js +243 -0
  250. package/dist/repertoire/tools-teams.js +9 -39
  251. package/dist/repertoire/tools-travel.js +125 -0
  252. package/dist/repertoire/tools-user-profile.js +144 -0
  253. package/dist/repertoire/tools-vault.js +40 -0
  254. package/dist/repertoire/tools.js +144 -113
  255. package/dist/repertoire/travel-api-client.js +360 -0
  256. package/dist/repertoire/user-profile.js +131 -0
  257. package/dist/repertoire/vault-setup.js +246 -0
  258. package/dist/repertoire/vault-unlock.js +421 -0
  259. package/dist/scripts/claude-code-hook.js +41 -0
  260. package/dist/scripts/claude-code-stop-hook.js +47 -0
  261. package/dist/senses/attention-queue.js +116 -0
  262. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  263. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  264. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
  265. package/dist/senses/bluebubbles/entry.js +73 -0
  266. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  267. package/dist/senses/bluebubbles/index.js +1620 -0
  268. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  269. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  270. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +45 -3
  271. package/dist/senses/bluebubbles/replay.js +129 -0
  272. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  273. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  274. package/dist/senses/cli/bracketed-paste.js +82 -0
  275. package/dist/senses/cli/image-paste.js +287 -0
  276. package/dist/senses/cli/image-ref-navigation.js +75 -0
  277. package/dist/senses/cli/ink-app.js +156 -0
  278. package/dist/senses/cli/inline-diff.js +64 -0
  279. package/dist/senses/cli/input-keys.js +174 -0
  280. package/dist/senses/cli/kill-ring.js +86 -0
  281. package/dist/senses/cli/message-list.js +51 -0
  282. package/dist/senses/cli/ouro-tui.js +605 -0
  283. package/dist/senses/cli/spinner-imperative.js +135 -0
  284. package/dist/senses/cli/spinner.js +101 -0
  285. package/dist/senses/cli/status-line.js +60 -0
  286. package/dist/senses/cli/streaming-markdown.js +526 -0
  287. package/dist/senses/cli/tool-display.js +83 -0
  288. package/dist/senses/cli/tool-render.js +85 -0
  289. package/dist/senses/cli/tui-store.js +240 -0
  290. package/dist/senses/cli/virtual-list.js +35 -0
  291. package/dist/senses/cli-entry.js +60 -8
  292. package/dist/senses/cli-layout.js +187 -0
  293. package/dist/senses/cli.js +516 -211
  294. package/dist/senses/commands.js +66 -3
  295. package/dist/senses/habit-turn-message.js +108 -0
  296. package/dist/senses/inner-dialog-worker.js +102 -19
  297. package/dist/senses/inner-dialog.js +597 -95
  298. package/dist/senses/pipeline.js +533 -72
  299. package/dist/senses/proactive-content-guard.js +51 -0
  300. package/dist/senses/shared-turn.js +205 -0
  301. package/dist/senses/surface-tool.js +68 -0
  302. package/dist/senses/teams-entry.js +60 -8
  303. package/dist/senses/teams.js +413 -163
  304. package/dist/senses/trust-gate.js +5 -5
  305. package/package.json +29 -7
  306. package/skills/agent-commerce.md +106 -0
  307. package/skills/browser-navigation.md +117 -0
  308. package/skills/commerce-setup-guide.md +116 -0
  309. package/skills/commerce-setup.md +84 -0
  310. package/skills/configure-dev-tools.md +101 -0
  311. package/skills/travel-planning.md +138 -0
  312. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  313. package/dist/heart/daemon/subagent-installer.js +0 -134
  314. package/dist/mind/associative-recall.js +0 -209
  315. package/dist/senses/bluebubbles-entry.js +0 -11
  316. package/dist/senses/bluebubbles.js +0 -854
  317. package/dist/senses/debug-activity.js +0 -127
  318. package/subagents/README.md +0 -60
  319. package/subagents/work-doer.md +0 -235
  320. package/subagents/work-merger.md +0 -618
  321. package/subagents/work-planner.md +0 -382
  322. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  323. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  324. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  325. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  326. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  327. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  328. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  329. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  330. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  331. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  332. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  333. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  334. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  335. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  336. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  337. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /**
3
+ * Vault setup module — Bitwarden/Vaultwarden account creation.
4
+ *
5
+ * Implements the Bitwarden registration protocol using Node.js crypto:
6
+ * - PBKDF2-SHA256 for master key derivation
7
+ * - HKDF-SHA256 for key stretching
8
+ * - AES-256-CBC for symmetric key protection
9
+ * - RSA-2048 keypair for asymmetric encryption
10
+ *
11
+ * All crypto follows the Bitwarden security whitepaper:
12
+ * https://bitwarden.com/help/bitwarden-security-white-paper/
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.deriveMasterKey = deriveMasterKey;
49
+ exports.deriveMasterPasswordHash = deriveMasterPasswordHash;
50
+ exports.deriveStretchedMasterKey = deriveStretchedMasterKey;
51
+ exports.makeProtectedSymmetricKey = makeProtectedSymmetricKey;
52
+ exports.createVaultAccount = createVaultAccount;
53
+ const crypto = __importStar(require("node:crypto"));
54
+ const runtime_1 = require("../nerves/runtime");
55
+ // ---------------------------------------------------------------------------
56
+ // Crypto primitives
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * Derive the master key from password and email using PBKDF2-SHA256.
60
+ * Email is lowercased and used as the salt per Bitwarden spec.
61
+ */
62
+ function deriveMasterKey(password, email, iterations) {
63
+ return new Promise((resolve, reject) => {
64
+ crypto.pbkdf2(password, email.toLowerCase(), iterations, 32, "sha256", (err, key) => {
65
+ /* v8 ignore next -- defensive: pbkdf2 rejects on invalid input @preserve */
66
+ if (err)
67
+ reject(err);
68
+ else
69
+ resolve(key);
70
+ });
71
+ });
72
+ }
73
+ /**
74
+ * Derive the master password hash: PBKDF2-SHA256(masterKey, password, 1 iteration).
75
+ * This hash is sent to the server for authentication — it never sees the master key.
76
+ */
77
+ function deriveMasterPasswordHash(masterKey, password) {
78
+ return new Promise((resolve, reject) => {
79
+ crypto.pbkdf2(masterKey, password, 1, 32, "sha256", (err, hash) => {
80
+ /* v8 ignore next -- defensive: pbkdf2 rejects on invalid input @preserve */
81
+ if (err)
82
+ reject(err);
83
+ else
84
+ resolve(hash.toString("base64"));
85
+ });
86
+ });
87
+ }
88
+ /**
89
+ * Stretch the master key using HKDF-Expand-only (RFC 5869 §2.3) to produce a 64-byte key.
90
+ * First 32 bytes = encryption key, last 32 bytes = MAC key.
91
+ *
92
+ * CRITICAL: Bitwarden uses HKDF-Expand ONLY (no Extract step).
93
+ * Node.js crypto.hkdfSync() does Extract+Expand which produces DIFFERENT output.
94
+ * Reference: https://github.com/bitwarden/sdk-internal/blob/main/crates/bitwarden-crypto/src/util.rs
95
+ * Bitwarden calls Hkdf::<Sha256>::from_prk(masterKey).expand(info, output) — Expand only.
96
+ */
97
+ function deriveStretchedMasterKey(masterKey) {
98
+ const encKey = hkdfExpandOnly(masterKey, "enc", 32);
99
+ const macKey = hkdfExpandOnly(masterKey, "mac", 32);
100
+ return Buffer.concat([encKey, macKey]);
101
+ }
102
+ /**
103
+ * HKDF-Expand only (RFC 5869 §2.3) — no Extract step.
104
+ * Matches Bitwarden's Hkdf::from_prk(prk).expand(info).
105
+ */
106
+ function hkdfExpandOnly(prk, info, length) {
107
+ const hashLen = 32; // SHA-256
108
+ const n = Math.ceil(length / hashLen);
109
+ let okm = Buffer.alloc(0);
110
+ let t = Buffer.alloc(0);
111
+ for (let i = 1; i <= n; i++) {
112
+ t = crypto.createHmac("sha256", prk)
113
+ .update(Buffer.concat([t, Buffer.from(info, "utf8"), Buffer.from([i])]))
114
+ .digest();
115
+ okm = Buffer.concat([okm, t]);
116
+ }
117
+ return okm.subarray(0, length);
118
+ }
119
+ /**
120
+ * Encrypt data with AES-256-CBC and HMAC-SHA256 MAC.
121
+ * Returns a Bitwarden "type 2" cipherstring: "2.<iv>|<ct>|<mac>"
122
+ */
123
+ function encryptWithStretchedKey(data, stretchedKey) {
124
+ const encKey = stretchedKey.subarray(0, 32);
125
+ const macKey = stretchedKey.subarray(32, 64);
126
+ const iv = crypto.randomBytes(16);
127
+ const cipher = crypto.createCipheriv("aes-256-cbc", encKey, iv);
128
+ const ct = Buffer.concat([cipher.update(data), cipher.final()]);
129
+ // MAC covers iv + ct
130
+ const mac = crypto.createHmac("sha256", macKey)
131
+ .update(iv)
132
+ .update(ct)
133
+ .digest();
134
+ return `2.${iv.toString("base64")}|${ct.toString("base64")}|${mac.toString("base64")}`;
135
+ }
136
+ /**
137
+ * Generate a 64-byte symmetric key, encrypt it with the stretched master key.
138
+ * Returns the "protected symmetric key" cipherstring.
139
+ */
140
+ function makeProtectedSymmetricKey(stretchedMasterKey) {
141
+ const symKey = crypto.randomBytes(64);
142
+ return encryptWithStretchedKey(symKey, stretchedMasterKey);
143
+ }
144
+ /**
145
+ * Generate an RSA-2048 keypair.
146
+ * Returns { publicKey: base64-DER, privateKeyDer: Buffer }.
147
+ */
148
+ function generateRsaKeypair() {
149
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
150
+ modulusLength: 2048,
151
+ publicKeyEncoding: { type: "spki", format: "der" },
152
+ privateKeyEncoding: { type: "pkcs8", format: "der" },
153
+ });
154
+ return {
155
+ publicKeyB64: publicKey.toString("base64"),
156
+ privateKeyDer: privateKey,
157
+ };
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Registration
161
+ // ---------------------------------------------------------------------------
162
+ const KDF_PBKDF2 = 0;
163
+ const KDF_ITERATIONS = 600000;
164
+ const REGISTER_ACCOUNT_PATH = "/identity/accounts/register";
165
+ /**
166
+ * Create a Bitwarden account on the configured Vaultwarden server.
167
+ * Uses the Bitwarden registration API with standard KDF implementation.
168
+ */
169
+ async function createVaultAccount(agentName, serverUrl, email, masterPassword) {
170
+ (0, runtime_1.emitNervesEvent)({
171
+ event: "repertoire.vault_setup_start",
172
+ component: "repertoire",
173
+ message: `creating vault account for ${agentName}`,
174
+ meta: { agentName, serverUrl, email },
175
+ });
176
+ try {
177
+ // Step 1: Derive keys
178
+ const masterKey = await deriveMasterKey(masterPassword, email, KDF_ITERATIONS);
179
+ const masterPasswordHash = await deriveMasterPasswordHash(masterKey, masterPassword);
180
+ const stretchedKey = deriveStretchedMasterKey(masterKey);
181
+ // Step 2: Generate symmetric key (64 bytes = 32 enc + 32 mac), encrypt with stretched key
182
+ const symKey = crypto.randomBytes(64);
183
+ const protectedSymKey = encryptWithStretchedKey(symKey, stretchedKey);
184
+ // Step 3: Generate RSA keypair, encrypt private key with the symmetric key
185
+ const { publicKeyB64, privateKeyDer } = generateRsaKeypair();
186
+ const encryptedPrivateKey = encryptWithStretchedKey(privateKeyDer, symKey);
187
+ // Step 4: POST registration
188
+ const registrationUrl = `${serverUrl}${REGISTER_ACCOUNT_PATH}`;
189
+ const res = await fetch(registrationUrl, {
190
+ method: "POST",
191
+ headers: { "Content-Type": "application/json" },
192
+ body: JSON.stringify({
193
+ name: agentName,
194
+ email,
195
+ masterPasswordHash,
196
+ masterPasswordHint: null,
197
+ key: protectedSymKey,
198
+ kdf: KDF_PBKDF2,
199
+ kdfIterations: KDF_ITERATIONS,
200
+ keys: {
201
+ publicKey: publicKeyB64,
202
+ encryptedPrivateKey,
203
+ },
204
+ }),
205
+ });
206
+ if (!res.ok) {
207
+ let errorDetail;
208
+ try {
209
+ const body = await res.json();
210
+ errorDetail = body.message ?? `HTTP ${res.status} ${res.statusText}`;
211
+ }
212
+ catch {
213
+ errorDetail = `HTTP ${res.status} ${res.statusText}`;
214
+ }
215
+ const endpointAwareError = `${errorDetail} from ${registrationUrl}. Check --server; Ouro expects a Bitwarden/Vaultwarden identity API.`;
216
+ (0, runtime_1.emitNervesEvent)({
217
+ level: "error",
218
+ event: "repertoire.vault_setup_error",
219
+ component: "repertoire",
220
+ message: `vault registration failed: ${endpointAwareError}`,
221
+ meta: { agentName, serverUrl, email, registrationUrl, reason: endpointAwareError },
222
+ });
223
+ return { success: false, email, serverUrl, error: endpointAwareError };
224
+ }
225
+ (0, runtime_1.emitNervesEvent)({
226
+ event: "repertoire.vault_setup_end",
227
+ component: "repertoire",
228
+ message: `vault account created for ${agentName}`,
229
+ meta: { agentName, serverUrl, email },
230
+ });
231
+ return { success: true, email, serverUrl };
232
+ }
233
+ catch (err) {
234
+ const reason = err instanceof Error ? err.message : String(err);
235
+ const registrationUrl = `${serverUrl}${REGISTER_ACCOUNT_PATH}`;
236
+ const endpointAwareError = `cannot reach vault registration endpoint ${registrationUrl}: ${reason}. Check network, DNS/TLS, and --server.`;
237
+ (0, runtime_1.emitNervesEvent)({
238
+ level: "error",
239
+ event: "repertoire.vault_setup_error",
240
+ component: "repertoire",
241
+ message: `vault setup failed: ${endpointAwareError}`,
242
+ meta: { agentName, serverUrl, email, registrationUrl, reason: endpointAwareError },
243
+ });
244
+ return { success: false, email, serverUrl, error: endpointAwareError };
245
+ }
246
+ }
@@ -0,0 +1,421 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.vaultUnlockReplaceRecoverFix = vaultUnlockReplaceRecoverFix;
37
+ exports.credentialVaultNotConfiguredError = credentialVaultNotConfiguredError;
38
+ exports.isCredentialVaultNotConfiguredError = isCredentialVaultNotConfiguredError;
39
+ exports.vaultCreateRecoverFix = vaultCreateRecoverFix;
40
+ exports.promptConfirmedVaultUnlockSecret = promptConfirmedVaultUnlockSecret;
41
+ exports.resolveVaultUnlockStore = resolveVaultUnlockStore;
42
+ exports.readVaultUnlockSecret = readVaultUnlockSecret;
43
+ exports.storeVaultUnlockSecret = storeVaultUnlockSecret;
44
+ exports.getVaultUnlockStatus = getVaultUnlockStatus;
45
+ const node_child_process_1 = require("node:child_process");
46
+ const crypto = __importStar(require("node:crypto"));
47
+ const fs = __importStar(require("node:fs"));
48
+ const os = __importStar(require("node:os"));
49
+ const path = __importStar(require("node:path"));
50
+ const runtime_1 = require("../nerves/runtime");
51
+ const VAULT_UNLOCK_SERVICE = "ouro.vault";
52
+ const CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX = "credential vault is not configured in ";
53
+ const PLAINTEXT_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock");
54
+ const WINDOWS_DPAPI_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock-dpapi");
55
+ const SUPPORTED_STORES = ["auto", "macos-keychain", "windows-dpapi", "linux-secret-service", "plaintext-file"];
56
+ function platform(deps) {
57
+ return deps.platform ?? process.platform;
58
+ }
59
+ function spawnSync(deps) {
60
+ return deps.spawnSync ?? node_child_process_1.spawnSync;
61
+ }
62
+ function homeDir(deps) {
63
+ return deps.homeDir ?? os.homedir();
64
+ }
65
+ function vaultKey(config) {
66
+ return `${config.serverUrl}:${config.email}`;
67
+ }
68
+ function vaultLabel(config) {
69
+ return `${config.email} at ${config.serverUrl}`;
70
+ }
71
+ function plaintextUnlockPath(config, deps) {
72
+ const digest = crypto.createHash("sha256").update(vaultKey(config)).digest("hex").slice(0, 24);
73
+ return path.join(homeDir(deps), PLAINTEXT_UNLOCK_DIR, `${digest}.secret`);
74
+ }
75
+ function windowsDpapiUnlockPath(config, deps) {
76
+ const digest = crypto.createHash("sha256").update(vaultKey(config)).digest("hex").slice(0, 24);
77
+ const localAppData = process.env.LOCALAPPDATA;
78
+ const baseDir = localAppData && platform(deps) === "win32"
79
+ ? path.join(localAppData, "Ouro")
80
+ : path.join(homeDir(deps), WINDOWS_DPAPI_UNLOCK_DIR);
81
+ return path.join(baseDir, "vault-unlock", `${digest}.dpapi`);
82
+ }
83
+ function commandExists(command, deps) {
84
+ const result = spawnSync(deps)(command, ["--version"], { encoding: "utf8" });
85
+ const code = result.error?.code;
86
+ return code !== "ENOENT";
87
+ }
88
+ function missingSecureStoreMessage(config) {
89
+ const agentPart = config.agentName ? ` for ${config.agentName}` : "";
90
+ return [
91
+ `No supported secure local secret store was found on this machine${agentPart}.`,
92
+ "",
93
+ `Ouro knows the credential vault is ${vaultLabel(config)}, but it cannot cache the vault unlock secret here yet.`,
94
+ "",
95
+ "On macOS, Ouro uses Keychain automatically.",
96
+ "On Windows, Ouro uses a CurrentUser DPAPI-encrypted local file automatically.",
97
+ "On Linux/WSL, install and configure Secret Service/libsecret, or choose the explicit plaintext fallback on a trusted machine.",
98
+ "",
99
+ config.agentName
100
+ ? `Run \`ouro vault unlock --agent ${config.agentName} --store plaintext-file\` to store the vault unlock secret in a chmod 0600 local file.`
101
+ : "Run `ouro vault unlock --store plaintext-file` to store the vault unlock secret in a chmod 0600 local file.",
102
+ ].join("\n");
103
+ }
104
+ function vaultUnlockReplaceRecoverFix(agentName, _nextStep) {
105
+ return `Run 'ouro vault unlock --agent ${agentName}' or 'ouro vault replace --agent ${agentName}' if the secret is lost.`;
106
+ }
107
+ function credentialVaultNotConfiguredError(agentName, configPath) {
108
+ return (`${CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX}${configPath}. ` +
109
+ `Run 'ouro vault create --agent ${agentName}' to create this agent's vault before loading or storing credentials.`);
110
+ }
111
+ function isCredentialVaultNotConfiguredError(message) {
112
+ return message.includes(CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX);
113
+ }
114
+ function vaultCreateRecoverFix(agentName, _nextStep) {
115
+ return `Run 'ouro vault create --agent ${agentName}' to set up this agent's vault.`;
116
+ }
117
+ function vaultUnlockSecretStrengthIssues(secret) {
118
+ const issues = [];
119
+ if (secret.length < 8)
120
+ issues.push("at least 8 characters");
121
+ if (!/[a-z]/.test(secret))
122
+ issues.push("a lowercase letter");
123
+ if (!/[A-Z]/.test(secret))
124
+ issues.push("an uppercase letter");
125
+ if (!/[0-9]/.test(secret))
126
+ issues.push("a number");
127
+ if (!/[^A-Za-z0-9]/.test(secret))
128
+ issues.push("a special character");
129
+ return issues;
130
+ }
131
+ async function promptConfirmedVaultUnlockSecret(input) {
132
+ const secret = (await input.promptSecret(input.question)).trim();
133
+ if (!secret) {
134
+ throw new Error(input.emptyError);
135
+ }
136
+ const issues = vaultUnlockSecretStrengthIssues(secret);
137
+ if (issues.length > 0) {
138
+ throw new Error(`vault unlock secret is too weak: add ${issues.join(", ")}. Use at least 8 characters with uppercase and lowercase letters, one number, and one special character.`);
139
+ }
140
+ const confirmation = (await input.promptSecret(input.confirmQuestion)).trim();
141
+ if (secret !== confirmation) {
142
+ throw new Error("vault unlock secrets did not match. Re-run the command and enter the same secret twice.");
143
+ }
144
+ return secret;
145
+ }
146
+ function lostUnlockSecretGuidance(config) {
147
+ if (!config.agentName) {
148
+ return "If nobody saved that unlock secret, run `ouro vault replace --agent <agent>` to create a new empty vault and re-enter credentials. If you do have a local JSON credential export, run `ouro vault recover --agent <agent> --from <json>` to import it.";
149
+ }
150
+ return [
151
+ `If nobody saved that unlock secret, run \`ouro vault replace --agent ${config.agentName}\` to create a new empty vault and re-enter credentials.`,
152
+ `If you do have a local JSON credential export, run \`ouro vault recover --agent ${config.agentName} --from <json>\` to import it.`,
153
+ ].join(" ");
154
+ }
155
+ function lockedMessage(config, store) {
156
+ const agentPart = config.agentName ? ` for ${config.agentName}` : "";
157
+ const command = config.agentName
158
+ ? `ouro vault unlock --agent ${config.agentName}${store.kind === "plaintext-file" ? " --store plaintext-file" : ""}`
159
+ : `ouro vault unlock${store.kind === "plaintext-file" ? " --store plaintext-file" : ""}`;
160
+ return [
161
+ `Ouro credential vault is locked on this machine${agentPart}.`,
162
+ "",
163
+ `Vault: ${vaultLabel(config)}`,
164
+ `Local unlock store: ${store.kind} (${store.location})`,
165
+ "",
166
+ "Provider credentials are still stored in the agent vault.",
167
+ "This computer does not currently have usable local unlock material for that vault.",
168
+ "This can happen on a new computer, after a local profile or hostname migration, or if the local unlock entry was removed.",
169
+ "",
170
+ `Run \`${command}\` and enter the saved agent vault unlock secret from the human/operator who controls that vault.`,
171
+ lostUnlockSecretGuidance(config),
172
+ ].join("\n");
173
+ }
174
+ function validateStoreKind(store) {
175
+ const requested = store ?? "auto";
176
+ if (!SUPPORTED_STORES.includes(requested)) {
177
+ throw new Error(`unknown vault unlock store '${requested}'. Use auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file.`);
178
+ }
179
+ return requested;
180
+ }
181
+ function resolveVaultUnlockStore(config, deps = {}) {
182
+ const requested = validateStoreKind(deps.store);
183
+ const currentPlatform = platform(deps);
184
+ if (requested === "macos-keychain") {
185
+ if (currentPlatform !== "darwin") {
186
+ throw new Error(`macos-keychain unlock store is only available on macOS; this machine is ${currentPlatform}.`);
187
+ }
188
+ return { kind: "macos-keychain", secure: true, location: "macOS Keychain" };
189
+ }
190
+ if (requested === "linux-secret-service") {
191
+ if (currentPlatform !== "linux") {
192
+ throw new Error(`linux-secret-service unlock store is only available on Linux/WSL; this machine is ${currentPlatform}.`);
193
+ }
194
+ if (!commandExists("secret-tool", deps)) {
195
+ throw new Error("linux-secret-service unlock store requires the `secret-tool` command from libsecret.");
196
+ }
197
+ return { kind: "linux-secret-service", secure: true, location: "Secret Service via secret-tool" };
198
+ }
199
+ if (requested === "windows-dpapi") {
200
+ if (currentPlatform !== "win32") {
201
+ throw new Error(`windows-dpapi unlock store is only available on Windows; this machine is ${currentPlatform}.`);
202
+ }
203
+ return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
204
+ }
205
+ if (requested === "plaintext-file") {
206
+ return { kind: "plaintext-file", secure: false, location: plaintextUnlockPath(config, deps) };
207
+ }
208
+ if (currentPlatform === "darwin") {
209
+ return { kind: "macos-keychain", secure: true, location: "macOS Keychain" };
210
+ }
211
+ if (currentPlatform === "win32") {
212
+ return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
213
+ }
214
+ if (currentPlatform === "linux" && commandExists("secret-tool", deps)) {
215
+ return { kind: "linux-secret-service", secure: true, location: "Secret Service via secret-tool" };
216
+ }
217
+ throw new Error(missingSecureStoreMessage(config));
218
+ }
219
+ function readFromMacosKeychain(config, deps) {
220
+ const result = spawnSync(deps)("security", [
221
+ "find-generic-password",
222
+ "-s",
223
+ VAULT_UNLOCK_SERVICE,
224
+ "-a",
225
+ vaultKey(config),
226
+ "-w",
227
+ ], { encoding: "utf8" });
228
+ const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
229
+ return result.status === 0 && secret ? secret : null;
230
+ }
231
+ function writeToMacosKeychain(config, secret, deps) {
232
+ const result = spawnSync(deps)("security", [
233
+ "add-generic-password",
234
+ "-U",
235
+ "-s",
236
+ VAULT_UNLOCK_SERVICE,
237
+ "-a",
238
+ vaultKey(config),
239
+ "-w",
240
+ secret,
241
+ ], { encoding: "utf8" });
242
+ if (result.status !== 0) {
243
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
244
+ throw new Error(`failed to store vault unlock secret in macOS Keychain${stderr ? `: ${stderr}` : ""}`);
245
+ }
246
+ }
247
+ function readFromLinuxSecretService(config, deps) {
248
+ const result = spawnSync(deps)("secret-tool", [
249
+ "lookup",
250
+ "service",
251
+ VAULT_UNLOCK_SERVICE,
252
+ "account",
253
+ vaultKey(config),
254
+ ], { encoding: "utf8" });
255
+ const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
256
+ return result.status === 0 && secret ? secret : null;
257
+ }
258
+ function writeToLinuxSecretService(config, secret, deps) {
259
+ const result = spawnSync(deps)("secret-tool", [
260
+ "store",
261
+ "--label",
262
+ `Ouro credential vault ${vaultLabel(config)}`,
263
+ "service",
264
+ VAULT_UNLOCK_SERVICE,
265
+ "account",
266
+ vaultKey(config),
267
+ ], { encoding: "utf8", input: secret });
268
+ if (result.status !== 0) {
269
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
270
+ throw new Error(`failed to store vault unlock secret in Linux Secret Service${stderr ? `: ${stderr}` : ""}`);
271
+ }
272
+ }
273
+ function runWindowsDpapi(mode, payload, deps) {
274
+ const script = `
275
+ $ErrorActionPreference = "Stop"
276
+ $inputJson = [Console]::In.ReadToEnd()
277
+ $payload = $inputJson | ConvertFrom-Json
278
+ Add-Type -AssemblyName System.Security
279
+ if ($payload.mode -eq "protect") {
280
+ $bytes = [Text.Encoding]::UTF8.GetBytes([string]$payload.secret)
281
+ $protected = [Security.Cryptography.ProtectedData]::Protect($bytes, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
282
+ [Console]::Out.Write([Convert]::ToBase64String($protected))
283
+ } elseif ($payload.mode -eq "unprotect") {
284
+ $protected = [Convert]::FromBase64String([string]$payload.ciphertext)
285
+ $bytes = [Security.Cryptography.ProtectedData]::Unprotect($protected, $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
286
+ [Console]::Out.Write([Text.Encoding]::UTF8.GetString($bytes))
287
+ } else {
288
+ throw "unknown DPAPI mode"
289
+ }
290
+ `;
291
+ const result = spawnSync(deps)("powershell.exe", [
292
+ "-NoProfile",
293
+ "-NonInteractive",
294
+ "-ExecutionPolicy",
295
+ "Bypass",
296
+ "-Command",
297
+ script,
298
+ ], {
299
+ encoding: "utf8",
300
+ input: JSON.stringify({ mode, ...payload }),
301
+ });
302
+ if (result.status !== 0) {
303
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
304
+ const error = result.error instanceof Error ? result.error.message : stderr;
305
+ throw new Error(`Windows DPAPI ${mode} failed${error ? `: ${error}` : ""}`);
306
+ }
307
+ return typeof result.stdout === "string" ? result.stdout : "";
308
+ }
309
+ function readFromWindowsDpapi(config, deps) {
310
+ const filePath = windowsDpapiUnlockPath(config, deps);
311
+ if (!fs.existsSync(filePath))
312
+ return null;
313
+ const ciphertext = fs.readFileSync(filePath, "utf8").trim();
314
+ if (!ciphertext)
315
+ return null;
316
+ const secret = runWindowsDpapi("unprotect", { ciphertext }, deps).trim();
317
+ return secret || null;
318
+ }
319
+ function writeToWindowsDpapi(config, secret, deps) {
320
+ const filePath = windowsDpapiUnlockPath(config, deps);
321
+ const ciphertext = runWindowsDpapi("protect", { secret }, deps).trim();
322
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
323
+ fs.writeFileSync(filePath, `${ciphertext}\n`, "utf8");
324
+ }
325
+ function readFromPlaintextFile(config, deps) {
326
+ const filePath = plaintextUnlockPath(config, deps);
327
+ if (!fs.existsSync(filePath))
328
+ return null;
329
+ if (platform(deps) !== "win32") {
330
+ const mode = fs.statSync(filePath).mode & 0o777;
331
+ if ((mode & 0o077) !== 0) {
332
+ throw new Error(`refusing to read plaintext vault unlock file at ${filePath} because permissions are too broad; run chmod 600 ${filePath}`);
333
+ }
334
+ }
335
+ const secret = fs.readFileSync(filePath, "utf8").trim();
336
+ return secret || null;
337
+ }
338
+ function writeToPlaintextFile(config, secret, deps) {
339
+ const filePath = plaintextUnlockPath(config, deps);
340
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
341
+ fs.writeFileSync(filePath, secret, { encoding: "utf8", mode: 0o600 });
342
+ if (platform(deps) !== "win32") {
343
+ fs.chmodSync(path.dirname(filePath), 0o700);
344
+ fs.chmodSync(filePath, 0o600);
345
+ }
346
+ }
347
+ function readFromStore(config, store, deps) {
348
+ if (store.kind === "macos-keychain")
349
+ return readFromMacosKeychain(config, deps);
350
+ if (store.kind === "windows-dpapi")
351
+ return readFromWindowsDpapi(config, deps);
352
+ if (store.kind === "linux-secret-service")
353
+ return readFromLinuxSecretService(config, deps);
354
+ return readFromPlaintextFile(config, deps);
355
+ }
356
+ function writeToStore(config, store, secret, deps) {
357
+ if (store.kind === "macos-keychain") {
358
+ writeToMacosKeychain(config, secret, deps);
359
+ return;
360
+ }
361
+ if (store.kind === "linux-secret-service") {
362
+ writeToLinuxSecretService(config, secret, deps);
363
+ return;
364
+ }
365
+ if (store.kind === "windows-dpapi") {
366
+ writeToWindowsDpapi(config, secret, deps);
367
+ return;
368
+ }
369
+ writeToPlaintextFile(config, secret, deps);
370
+ }
371
+ function readVaultUnlockSecret(config, deps = {}) {
372
+ const store = resolveVaultUnlockStore(config, deps);
373
+ const secret = readFromStore(config, store, deps);
374
+ if (!secret) {
375
+ throw new Error(lockedMessage(config, store));
376
+ }
377
+ (0, runtime_1.emitNervesEvent)({
378
+ component: "repertoire",
379
+ event: "repertoire.vault_unlock_loaded",
380
+ message: "loaded vault unlock material from local store",
381
+ meta: { store: store.kind, secure: store.secure, hasAgentName: !!config.agentName },
382
+ });
383
+ return { secret, store };
384
+ }
385
+ function storeVaultUnlockSecret(config, secret, deps = {}) {
386
+ const trimmed = secret.trim();
387
+ if (!trimmed) {
388
+ throw new Error("vault unlock secret is required");
389
+ }
390
+ const store = resolveVaultUnlockStore(config, deps);
391
+ writeToStore(config, store, trimmed, deps);
392
+ (0, runtime_1.emitNervesEvent)({
393
+ component: "repertoire",
394
+ event: "repertoire.vault_unlock_stored",
395
+ message: "stored vault unlock material in local store",
396
+ meta: { store: store.kind, secure: store.secure, hasAgentName: !!config.agentName },
397
+ });
398
+ return store;
399
+ }
400
+ function getVaultUnlockStatus(config, deps = {}) {
401
+ try {
402
+ const store = resolveVaultUnlockStore(config, deps);
403
+ const stored = !!readFromStore(config, store, deps);
404
+ return {
405
+ configured: true,
406
+ stored,
407
+ store,
408
+ fix: stored
409
+ ? "Vault unlock secret is available on this machine."
410
+ : lockedMessage(config, store),
411
+ };
412
+ }
413
+ catch (error) {
414
+ return {
415
+ configured: false,
416
+ stored: false,
417
+ error: error instanceof Error ? error.message : String(error),
418
+ fix: error instanceof Error ? error.message : String(error),
419
+ };
420
+ }
421
+ }