@ouro.bot/cli 0.1.0-alpha.23 → 0.1.0-alpha.231

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 (241) hide show
  1. package/README.md +187 -191
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/changelog.json +1408 -1
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +119 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +120 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/presence.js +185 -0
  12. package/dist/heart/active-work.js +832 -0
  13. package/dist/heart/agent-entry.js +37 -2
  14. package/dist/heart/auth/auth-flow.js +447 -0
  15. package/dist/heart/bridges/manager.js +358 -0
  16. package/dist/heart/bridges/state-machine.js +135 -0
  17. package/dist/heart/bridges/store.js +123 -0
  18. package/dist/heart/commitments.js +111 -0
  19. package/dist/heart/config-registry.js +305 -0
  20. package/dist/heart/config.js +63 -30
  21. package/dist/heart/core.js +601 -145
  22. package/dist/heart/cross-chat-delivery.js +131 -0
  23. package/dist/heart/daemon/agent-config-check.js +149 -0
  24. package/dist/heart/daemon/agent-discovery.js +81 -0
  25. package/dist/heart/daemon/agent-service.js +360 -0
  26. package/dist/heart/daemon/cadence.js +70 -0
  27. package/dist/heart/daemon/cli-defaults.js +557 -0
  28. package/dist/heart/daemon/cli-exec.js +1847 -0
  29. package/dist/heart/daemon/cli-parse.js +759 -0
  30. package/dist/heart/daemon/cli-render.js +316 -0
  31. package/dist/heart/daemon/cli-types.js +8 -0
  32. package/dist/heart/daemon/daemon-cli.js +29 -1163
  33. package/dist/heart/daemon/daemon-entry.js +202 -4
  34. package/dist/heart/daemon/daemon-health.js +136 -0
  35. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  36. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  37. package/dist/heart/daemon/daemon.js +489 -20
  38. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  39. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  40. package/dist/heart/daemon/inner-status.js +89 -0
  41. package/dist/heart/daemon/launchd.js +39 -8
  42. package/dist/heart/daemon/log-tailer.js +4 -3
  43. package/dist/heart/daemon/message-router.js +17 -8
  44. package/dist/heart/daemon/os-cron-deps.js +134 -0
  45. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  46. package/dist/heart/daemon/process-manager.js +123 -0
  47. package/dist/heart/daemon/run-hooks.js +2 -0
  48. package/dist/heart/daemon/runtime-logging.js +58 -15
  49. package/dist/heart/daemon/runtime-metadata.js +101 -0
  50. package/dist/heart/daemon/runtime-mode.js +67 -0
  51. package/dist/heart/daemon/safe-mode.js +161 -0
  52. package/dist/heart/daemon/sense-manager.js +72 -3
  53. package/dist/heart/daemon/session-id-resolver.js +131 -0
  54. package/dist/heart/daemon/skill-management-installer.js +94 -0
  55. package/dist/heart/daemon/socket-client.js +201 -0
  56. package/dist/heart/daemon/task-scheduler.js +3 -25
  57. package/dist/heart/daemon/thoughts.js +507 -0
  58. package/dist/heart/delegation.js +62 -0
  59. package/dist/heart/habits/habit-migration.js +181 -0
  60. package/dist/heart/habits/habit-parser.js +119 -0
  61. package/dist/heart/habits/habit-scheduler.js +369 -0
  62. package/dist/heart/{daemon → hatch}/hatch-flow.js +29 -119
  63. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  64. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  65. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  66. package/dist/heart/identity.js +144 -55
  67. package/dist/heart/kicks.js +2 -20
  68. package/dist/heart/mcp/mcp-server.js +608 -0
  69. package/dist/heart/migrate-config.js +127 -0
  70. package/dist/heart/model-capabilities.js +48 -0
  71. package/dist/heart/outlook/outlook-http.js +439 -0
  72. package/dist/heart/outlook/outlook-read.js +1618 -0
  73. package/dist/heart/outlook/outlook-render.js +1032 -0
  74. package/dist/heart/outlook/outlook-types.js +7 -0
  75. package/dist/heart/outlook/outlook-view.js +194 -0
  76. package/dist/heart/progress-story.js +42 -0
  77. package/dist/heart/provider-failover.js +88 -0
  78. package/dist/heart/provider-ping.js +162 -0
  79. package/dist/heart/providers/anthropic-token.js +163 -0
  80. package/dist/heart/providers/anthropic.js +187 -41
  81. package/dist/heart/providers/azure.js +116 -10
  82. package/dist/heart/providers/github-copilot.js +157 -0
  83. package/dist/heart/providers/minimax.js +33 -3
  84. package/dist/heart/providers/openai-codex.js +51 -16
  85. package/dist/heart/session-activity.js +172 -0
  86. package/dist/heart/session-recall.js +216 -0
  87. package/dist/heart/start-of-turn-packet.js +330 -0
  88. package/dist/heart/streaming.js +36 -27
  89. package/dist/heart/sync.js +217 -0
  90. package/dist/heart/target-resolution.js +127 -0
  91. package/dist/heart/tempo.js +93 -0
  92. package/dist/heart/temporal-view.js +41 -0
  93. package/dist/heart/tool-activity-callbacks.js +36 -0
  94. package/dist/heart/tool-description.js +135 -0
  95. package/dist/heart/tool-loop.js +200 -0
  96. package/dist/heart/turn-context.js +358 -0
  97. package/dist/heart/turn-coordinator.js +28 -0
  98. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  99. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  100. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  101. package/dist/heart/versioning/ouro-version-manager.js +171 -0
  102. package/dist/heart/{daemon → versioning}/staged-restart.js +30 -6
  103. package/dist/heart/{daemon → versioning}/update-checker.js +9 -1
  104. package/dist/heart/{daemon → versioning}/update-hooks.js +20 -2
  105. package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
  106. package/dist/mind/associative-recall.js +126 -66
  107. package/dist/mind/bundle-manifest.js +8 -1
  108. package/dist/mind/context.js +125 -14
  109. package/dist/mind/{memory.js → diary.js} +73 -96
  110. package/dist/mind/embedding-provider.js +60 -0
  111. package/dist/mind/file-state.js +179 -0
  112. package/dist/mind/first-impressions.js +16 -2
  113. package/dist/mind/friends/channel.js +56 -0
  114. package/dist/mind/friends/group-context.js +144 -0
  115. package/dist/mind/friends/store-file.js +58 -3
  116. package/dist/mind/friends/trust-explanation.js +74 -0
  117. package/dist/mind/friends/types.js +8 -0
  118. package/dist/mind/journal-index.js +161 -0
  119. package/dist/mind/obligation-steering.js +221 -0
  120. package/dist/mind/pending.js +76 -9
  121. package/dist/mind/phrases.js +1 -0
  122. package/dist/mind/prompt.js +779 -109
  123. package/dist/mind/scrutiny.js +173 -0
  124. package/dist/mind/token-estimate.js +8 -12
  125. package/dist/nerves/coverage/audit.js +1 -1
  126. package/dist/nerves/coverage/file-completeness.js +47 -5
  127. package/dist/nerves/coverage/run-artifacts.js +1 -1
  128. package/dist/nerves/event-buffer.js +111 -0
  129. package/dist/nerves/index.js +64 -4
  130. package/dist/nerves/observation.js +20 -0
  131. package/dist/nerves/runtime.js +5 -1
  132. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  133. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  134. package/dist/outlook-ui/index.html +15 -0
  135. package/dist/repertoire/ado-client.js +15 -56
  136. package/dist/repertoire/ado-semantic.js +11 -5
  137. package/dist/repertoire/api-client.js +97 -0
  138. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  139. package/dist/repertoire/coding/context-pack.js +330 -0
  140. package/dist/repertoire/coding/feedback.js +197 -30
  141. package/dist/repertoire/coding/manager.js +149 -2
  142. package/dist/repertoire/coding/spawner.js +55 -9
  143. package/dist/repertoire/coding/tools.js +170 -7
  144. package/dist/repertoire/credential-access.js +449 -0
  145. package/dist/repertoire/github-client.js +14 -55
  146. package/dist/repertoire/graph-client.js +11 -52
  147. package/dist/repertoire/guardrails.js +342 -0
  148. package/dist/repertoire/mcp-client.js +254 -0
  149. package/dist/repertoire/mcp-manager.js +251 -0
  150. package/dist/repertoire/shell-sessions.js +133 -0
  151. package/dist/repertoire/skills.js +14 -23
  152. package/dist/repertoire/tasks/board.js +43 -5
  153. package/dist/repertoire/tasks/fix.js +182 -0
  154. package/dist/repertoire/tasks/index.js +28 -10
  155. package/dist/repertoire/tasks/lifecycle.js +2 -2
  156. package/dist/repertoire/tasks/parser.js +3 -2
  157. package/dist/repertoire/tasks/scanner.js +194 -37
  158. package/dist/repertoire/tasks/transitions.js +11 -28
  159. package/dist/repertoire/tools-base.js +35 -771
  160. package/dist/repertoire/tools-bluebubbles.js +94 -0
  161. package/dist/repertoire/tools-bridge.js +141 -0
  162. package/dist/repertoire/tools-config.js +266 -0
  163. package/dist/repertoire/tools-continuity.js +248 -0
  164. package/dist/repertoire/tools-credential.js +184 -0
  165. package/dist/repertoire/tools-files.js +342 -0
  166. package/dist/repertoire/tools-flow.js +62 -0
  167. package/dist/repertoire/tools-github.js +1 -6
  168. package/dist/repertoire/tools-memory.js +347 -0
  169. package/dist/repertoire/tools-session.js +739 -0
  170. package/dist/repertoire/tools-shell.js +120 -0
  171. package/dist/repertoire/tools-surface.js +243 -0
  172. package/dist/repertoire/tools-teams.js +12 -59
  173. package/dist/repertoire/tools-travel.js +125 -0
  174. package/dist/repertoire/tools.js +132 -124
  175. package/dist/repertoire/travel-api-client.js +214 -0
  176. package/dist/scripts/claude-code-hook.js +41 -0
  177. package/dist/scripts/claude-code-stop-hook.js +47 -0
  178. package/dist/senses/attention-queue.js +97 -0
  179. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +109 -10
  180. package/dist/senses/bluebubbles/entry.js +13 -0
  181. package/dist/senses/bluebubbles/inbound-log.js +109 -0
  182. package/dist/senses/bluebubbles/index.js +1374 -0
  183. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +119 -24
  184. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +13 -5
  185. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  186. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  187. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  188. package/dist/senses/cli/adapter.js +274 -0
  189. package/dist/senses/cli/ink-app.js +156 -0
  190. package/dist/senses/cli/inline-diff.js +64 -0
  191. package/dist/senses/cli/message-list.js +51 -0
  192. package/dist/senses/cli/ouro-tui.js +358 -0
  193. package/dist/senses/cli/spinner-imperative.js +135 -0
  194. package/dist/senses/cli/spinner.js +101 -0
  195. package/dist/senses/cli/status-line.js +60 -0
  196. package/dist/senses/cli/streaming-markdown.js +526 -0
  197. package/dist/senses/cli/tool-display.js +83 -0
  198. package/dist/senses/cli/tool-render.js +85 -0
  199. package/dist/senses/cli/tui-store.js +198 -0
  200. package/dist/senses/cli/virtual-list.js +35 -0
  201. package/dist/senses/cli-entry.js +1 -1
  202. package/dist/senses/cli-layout.js +187 -0
  203. package/dist/senses/cli.js +503 -244
  204. package/dist/senses/commands.js +65 -1
  205. package/dist/senses/continuity.js +94 -0
  206. package/dist/senses/habit-turn-message.js +108 -0
  207. package/dist/senses/inner-dialog-worker.js +112 -19
  208. package/dist/senses/inner-dialog.js +594 -85
  209. package/dist/senses/pipeline.js +559 -0
  210. package/dist/senses/shared-turn.js +196 -0
  211. package/dist/senses/surface-tool.js +68 -0
  212. package/dist/senses/teams.js +667 -120
  213. package/dist/senses/trust-gate.js +112 -2
  214. package/package.json +12 -6
  215. package/skills/browser-navigation.md +110 -0
  216. package/skills/configure-dev-tools.md +81 -0
  217. package/skills/travel-planning.md +138 -0
  218. package/dist/heart/daemon/subagent-installer.js +0 -134
  219. package/dist/senses/bluebubbles-entry.js +0 -11
  220. package/dist/senses/bluebubbles.js +0 -421
  221. package/dist/senses/debug-activity.js +0 -107
  222. package/subagents/README.md +0 -73
  223. package/subagents/work-doer.md +0 -235
  224. package/subagents/work-merger.md +0 -618
  225. package/subagents/work-planner.md +0 -382
  226. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  227. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  228. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  229. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  230. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  231. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  232. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  233. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  234. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  235. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  236. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  237. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  238. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  239. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  240. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  241. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
@@ -0,0 +1,449 @@
1
+ "use strict";
2
+ /**
3
+ * Credential access layer — store-agnostic credential management.
4
+ *
5
+ * Two backends, one interface:
6
+ * - BuiltInCredentialStore: AES-256-GCM encrypted files, zero npm deps
7
+ * - AacCredentialStore: delegates to the `aac` CLI (Bitwarden Agent Access)
8
+ *
9
+ * Raw secrets are NEVER returned to callers except via getRawSecret(),
10
+ * which is reserved for internal use by the credential gateway (apiRequest).
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.AacCredentialStore = exports.BuiltInCredentialStore = void 0;
47
+ exports.domainToSlug = domainToSlug;
48
+ exports.getCredentialStore = getCredentialStore;
49
+ exports.resetCredentialStore = resetCredentialStore;
50
+ const crypto = __importStar(require("node:crypto"));
51
+ const fs = __importStar(require("node:fs"));
52
+ const path = __importStar(require("node:path"));
53
+ const os = __importStar(require("node:os"));
54
+ const node_child_process_1 = require("node:child_process");
55
+ const runtime_1 = require("../nerves/runtime");
56
+ // ---------------------------------------------------------------------------
57
+ // Helpers
58
+ // ---------------------------------------------------------------------------
59
+ /** Convert a domain string to a filesystem-safe slug. */
60
+ function domainToSlug(domain) {
61
+ return domain
62
+ .toLowerCase()
63
+ .replace(/[^a-z0-9]+/g, "-")
64
+ .replace(/^-+|-+$/g, "");
65
+ }
66
+ class BuiltInCredentialStore {
67
+ vaultDir;
68
+ keyPath;
69
+ masterKey = null;
70
+ constructor(agentName, vaultDir, keyDir) {
71
+ const homeBase = process.env.WEBSITE_SITE_NAME ? "/home" : os.homedir();
72
+ this.vaultDir = vaultDir ?? path.join(homeBase, "AgentBundles", `${agentName}.ouro`, "vault");
73
+ const effectiveKeyDir = keyDir ?? path.join(homeBase, ".agentsecrets", agentName);
74
+ this.keyPath = path.join(effectiveKeyDir, "vault.key");
75
+ }
76
+ isReady() {
77
+ return true;
78
+ }
79
+ async get(domain) {
80
+ (0, runtime_1.emitNervesEvent)({
81
+ event: "repertoire.credential_get_start",
82
+ component: "repertoire",
83
+ message: `getting credential for ${domain}`,
84
+ meta: { domain },
85
+ });
86
+ const stored = this.readEncrypted(domain);
87
+ if (!stored) {
88
+ (0, runtime_1.emitNervesEvent)({
89
+ event: "repertoire.credential_get_end",
90
+ component: "repertoire",
91
+ message: `no credential found for ${domain}`,
92
+ meta: { domain, found: false },
93
+ });
94
+ return null;
95
+ }
96
+ (0, runtime_1.emitNervesEvent)({
97
+ event: "repertoire.credential_get_end",
98
+ component: "repertoire",
99
+ message: `credential found for ${domain}`,
100
+ meta: { domain, found: true },
101
+ });
102
+ return {
103
+ domain: stored.domain,
104
+ username: stored.username,
105
+ notes: stored.notes,
106
+ createdAt: stored.createdAt,
107
+ };
108
+ }
109
+ async getRawSecret(domain, field) {
110
+ const stored = this.readEncrypted(domain);
111
+ if (!stored) {
112
+ (0, runtime_1.emitNervesEvent)({
113
+ level: "error",
114
+ event: "repertoire.credential_get_error",
115
+ component: "repertoire",
116
+ message: `no credential found for domain "${domain}"`,
117
+ meta: { domain, field, reason: `no credential found for domain "${domain}"` },
118
+ });
119
+ throw new Error(`no credential found for domain "${domain}"`);
120
+ }
121
+ const value = stored[field];
122
+ if (value === undefined || value === null) {
123
+ (0, runtime_1.emitNervesEvent)({
124
+ level: "error",
125
+ event: "repertoire.credential_get_error",
126
+ component: "repertoire",
127
+ message: `field "${field}" not found for domain "${domain}"`,
128
+ meta: { domain, field, reason: `field "${field}" not found for domain "${domain}"` },
129
+ });
130
+ throw new Error(`field "${field}" not found for domain "${domain}"`);
131
+ }
132
+ return String(value);
133
+ }
134
+ async store(domain, data) {
135
+ (0, runtime_1.emitNervesEvent)({
136
+ event: "repertoire.credential_store_start",
137
+ component: "repertoire",
138
+ message: `storing credential for ${domain}`,
139
+ meta: { domain },
140
+ });
141
+ this.ensureDirs();
142
+ const key = this.getOrCreateKey();
143
+ const credential = {
144
+ domain,
145
+ username: data.username,
146
+ password: data.password,
147
+ notes: data.notes,
148
+ createdAt: new Date().toISOString(),
149
+ };
150
+ const plaintext = JSON.stringify(credential);
151
+ const iv = crypto.randomBytes(12);
152
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
153
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
154
+ const tag = cipher.getAuthTag();
155
+ const payload = {
156
+ iv: iv.toString("hex"),
157
+ tag: tag.toString("hex"),
158
+ data: encrypted.toString("hex"),
159
+ };
160
+ const slug = domainToSlug(domain);
161
+ const encPath = path.join(this.vaultDir, `${slug}.enc`);
162
+ fs.writeFileSync(encPath, JSON.stringify(payload), "utf-8");
163
+ (0, runtime_1.emitNervesEvent)({
164
+ event: "repertoire.credential_store_end",
165
+ component: "repertoire",
166
+ message: `credential stored for ${domain}`,
167
+ meta: { domain },
168
+ });
169
+ }
170
+ async list() {
171
+ (0, runtime_1.emitNervesEvent)({
172
+ event: "repertoire.credential_list_start",
173
+ component: "repertoire",
174
+ message: "listing credentials",
175
+ meta: {},
176
+ });
177
+ if (!fs.existsSync(this.vaultDir)) {
178
+ (0, runtime_1.emitNervesEvent)({
179
+ event: "repertoire.credential_list_end",
180
+ component: "repertoire",
181
+ message: "credential list complete (no vault dir)",
182
+ meta: { count: 0 },
183
+ });
184
+ return [];
185
+ }
186
+ const files = fs.readdirSync(this.vaultDir).filter((f) => f.endsWith(".enc"));
187
+ const results = [];
188
+ for (const file of files) {
189
+ const slug = file.replace(/\.enc$/, "");
190
+ let stored = null;
191
+ try {
192
+ stored = this.readEncryptedBySlug(slug);
193
+ }
194
+ catch {
195
+ // Skip corrupt/unreadable files
196
+ }
197
+ if (stored) {
198
+ results.push({
199
+ domain: stored.domain,
200
+ username: stored.username,
201
+ notes: stored.notes,
202
+ createdAt: stored.createdAt,
203
+ });
204
+ }
205
+ }
206
+ (0, runtime_1.emitNervesEvent)({
207
+ event: "repertoire.credential_list_end",
208
+ component: "repertoire",
209
+ message: "credential list complete",
210
+ meta: { count: results.length },
211
+ });
212
+ return results;
213
+ }
214
+ async delete(domain) {
215
+ (0, runtime_1.emitNervesEvent)({
216
+ event: "repertoire.credential_delete_start",
217
+ component: "repertoire",
218
+ message: `deleting credential for ${domain}`,
219
+ meta: { domain },
220
+ });
221
+ const slug = domainToSlug(domain);
222
+ const encPath = path.join(this.vaultDir, `${slug}.enc`);
223
+ if (!fs.existsSync(encPath)) {
224
+ (0, runtime_1.emitNervesEvent)({
225
+ event: "repertoire.credential_delete_end",
226
+ component: "repertoire",
227
+ message: `no credential to delete for ${domain}`,
228
+ meta: { domain, deleted: false },
229
+ });
230
+ return false;
231
+ }
232
+ fs.unlinkSync(encPath);
233
+ (0, runtime_1.emitNervesEvent)({
234
+ event: "repertoire.credential_delete_end",
235
+ component: "repertoire",
236
+ message: `credential deleted for ${domain}`,
237
+ meta: { domain, deleted: true },
238
+ });
239
+ return true;
240
+ }
241
+ // --- Private ---
242
+ ensureDirs() {
243
+ if (!fs.existsSync(this.vaultDir)) {
244
+ fs.mkdirSync(this.vaultDir, { recursive: true });
245
+ }
246
+ const keyDir = path.dirname(this.keyPath);
247
+ if (!fs.existsSync(keyDir)) {
248
+ fs.mkdirSync(keyDir, { recursive: true, mode: 0o700 });
249
+ }
250
+ }
251
+ getOrCreateKey() {
252
+ if (this.masterKey)
253
+ return this.masterKey;
254
+ if (fs.existsSync(this.keyPath)) {
255
+ this.masterKey = fs.readFileSync(this.keyPath);
256
+ return this.masterKey;
257
+ }
258
+ // Generate new 256-bit key
259
+ const key = crypto.randomBytes(32);
260
+ fs.writeFileSync(this.keyPath, key, { mode: 0o600 });
261
+ this.masterKey = key;
262
+ return key;
263
+ }
264
+ readEncrypted(domain) {
265
+ return this.readEncryptedBySlug(domainToSlug(domain));
266
+ }
267
+ readEncryptedBySlug(slug) {
268
+ const encPath = path.join(this.vaultDir, `${slug}.enc`);
269
+ if (!fs.existsSync(encPath))
270
+ return null;
271
+ const key = this.getOrCreateKey();
272
+ const raw = fs.readFileSync(encPath, "utf-8");
273
+ const payload = JSON.parse(raw);
274
+ const iv = Buffer.from(payload.iv, "hex");
275
+ const tag = Buffer.from(payload.tag, "hex");
276
+ const encrypted = Buffer.from(payload.data, "hex");
277
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
278
+ decipher.setAuthTag(tag);
279
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
280
+ return JSON.parse(decrypted.toString("utf-8"));
281
+ }
282
+ }
283
+ exports.BuiltInCredentialStore = BuiltInCredentialStore;
284
+ // ---------------------------------------------------------------------------
285
+ // AacCredentialStore — delegates to `aac` CLI
286
+ // ---------------------------------------------------------------------------
287
+ function execAac(args) {
288
+ return new Promise((resolve, reject) => {
289
+ (0, node_child_process_1.execFile)("aac", args, { timeout: 10_000 }, (err, stdout) => {
290
+ if (err) {
291
+ reject(new Error(`aac CLI error: ${err.message}`));
292
+ return;
293
+ }
294
+ resolve(stdout);
295
+ });
296
+ });
297
+ }
298
+ class AacCredentialStore {
299
+ aacAvailable = null;
300
+ isReady() {
301
+ // Synchronous check — returns last-known state or false if unchecked
302
+ return this.aacAvailable === true;
303
+ }
304
+ async checkReady() {
305
+ try {
306
+ const stdout = await execAac(["connections", "list"]);
307
+ const parsed = JSON.parse(stdout);
308
+ this.aacAvailable = Array.isArray(parsed) && parsed.length > 0;
309
+ }
310
+ catch {
311
+ this.aacAvailable = false;
312
+ }
313
+ return this.aacAvailable;
314
+ }
315
+ async get(domain) {
316
+ (0, runtime_1.emitNervesEvent)({
317
+ event: "repertoire.credential_get_start",
318
+ component: "repertoire",
319
+ message: `getting credential via aac for ${domain}`,
320
+ meta: { domain, backend: "aac" },
321
+ });
322
+ try {
323
+ const stdout = await execAac(["--domain", domain, "--output", "json"]);
324
+ const parsed = JSON.parse(stdout);
325
+ if (!parsed.success) {
326
+ (0, runtime_1.emitNervesEvent)({
327
+ event: "repertoire.credential_get_end",
328
+ component: "repertoire",
329
+ message: `no aac credential for ${domain}`,
330
+ meta: { domain, found: false, backend: "aac" },
331
+ });
332
+ return null;
333
+ }
334
+ const cred = parsed.credential;
335
+ (0, runtime_1.emitNervesEvent)({
336
+ event: "repertoire.credential_get_end",
337
+ component: "repertoire",
338
+ message: `aac credential found for ${domain}`,
339
+ meta: { domain, found: true, backend: "aac" },
340
+ });
341
+ return {
342
+ domain,
343
+ username: cred.username,
344
+ notes: cred.notes,
345
+ createdAt: cred.createdAt ?? new Date().toISOString(),
346
+ };
347
+ }
348
+ catch {
349
+ (0, runtime_1.emitNervesEvent)({
350
+ event: "repertoire.credential_get_end",
351
+ component: "repertoire",
352
+ message: `aac credential lookup failed for ${domain}`,
353
+ meta: { domain, found: false, backend: "aac" },
354
+ });
355
+ return null;
356
+ }
357
+ }
358
+ async getRawSecret(domain, field) {
359
+ const stdout = await execAac(["--domain", domain, "--output", "json"]);
360
+ const parsed = JSON.parse(stdout);
361
+ if (!parsed.success) {
362
+ throw new Error(`aac lookup failed for domain "${domain}": ${parsed.error ?? "unknown error"}`);
363
+ }
364
+ const value = parsed.credential[field];
365
+ if (value === undefined || value === null) {
366
+ throw new Error(`field "${field}" not found for domain "${domain}"`);
367
+ }
368
+ return String(value);
369
+ }
370
+ async store(_domain, _data) {
371
+ throw new Error("store() is not supported in aac mode — aac is read-from-vault only. " +
372
+ "Use BuiltInCredentialStore for agent-owned credentials, or manage vault items " +
373
+ "directly in Bitwarden.");
374
+ }
375
+ async list() {
376
+ (0, runtime_1.emitNervesEvent)({
377
+ event: "repertoire.credential_list_start",
378
+ component: "repertoire",
379
+ message: "listing aac connections",
380
+ meta: { backend: "aac" },
381
+ });
382
+ try {
383
+ const stdout = await execAac(["connections", "list"]);
384
+ const parsed = JSON.parse(stdout);
385
+ const results = (Array.isArray(parsed) ? parsed : []).map((item) => ({
386
+ domain: String(item.domain ?? ""),
387
+ username: item.username ? String(item.username) : undefined,
388
+ notes: item.notes ? String(item.notes) : undefined,
389
+ createdAt: item.createdAt ? String(item.createdAt) : new Date().toISOString(),
390
+ }));
391
+ (0, runtime_1.emitNervesEvent)({
392
+ event: "repertoire.credential_list_end",
393
+ component: "repertoire",
394
+ message: "aac connections listed",
395
+ meta: { backend: "aac", count: results.length },
396
+ });
397
+ return results;
398
+ }
399
+ catch {
400
+ (0, runtime_1.emitNervesEvent)({
401
+ event: "repertoire.credential_list_end",
402
+ component: "repertoire",
403
+ message: "aac connections list failed",
404
+ meta: { backend: "aac", count: 0 },
405
+ });
406
+ return [];
407
+ }
408
+ }
409
+ async delete(_domain) {
410
+ throw new Error("delete() is not supported in aac mode — manage vault items directly in Bitwarden.");
411
+ }
412
+ }
413
+ exports.AacCredentialStore = AacCredentialStore;
414
+ // ---------------------------------------------------------------------------
415
+ // Singleton + auto-detection
416
+ // ---------------------------------------------------------------------------
417
+ let _store = null;
418
+ /**
419
+ * Get the credential store singleton.
420
+ * Tries aac first (if binary exists and has sessions); falls back to BuiltInCredentialStore.
421
+ */
422
+ function getCredentialStore() {
423
+ if (_store)
424
+ return _store;
425
+ // Default to BuiltInCredentialStore — aac detection is async and
426
+ // would need to be done at startup. For now, always use built-in.
427
+ // Callers can explicitly create AacCredentialStore if needed.
428
+ let agentName;
429
+ try {
430
+ // Dynamic import to avoid circular dependency at module level
431
+ const identity = require("../heart/identity");
432
+ agentName = identity.getAgentName();
433
+ }
434
+ catch {
435
+ agentName = "default";
436
+ }
437
+ _store = new BuiltInCredentialStore(agentName);
438
+ (0, runtime_1.emitNervesEvent)({
439
+ event: "repertoire.credential_store_init",
440
+ component: "repertoire",
441
+ message: "credential store initialized",
442
+ meta: { backend: "built-in", agentName },
443
+ });
444
+ return _store;
445
+ }
446
+ /** Reset the singleton — for tests only. */
447
+ function resetCredentialStore() {
448
+ _store = null;
449
+ }
@@ -3,62 +3,21 @@
3
3
  // Provides a generic githubRequest() for arbitrary GitHub REST API endpoints.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.githubRequest = githubRequest;
6
- const api_error_1 = require("../heart/api-error");
7
- const runtime_1 = require("../nerves/runtime");
6
+ const api_client_1 = require("./api-client");
8
7
  const GITHUB_BASE = "https://api.github.com";
9
8
  // Generic GitHub API request. Returns response body as pretty-printed JSON string.
10
9
  async function githubRequest(token, method, path, body) {
11
- try {
12
- (0, runtime_1.emitNervesEvent)({
13
- event: "client.request_start",
14
- component: "clients",
15
- message: "starting GitHub request",
16
- meta: { client: "github", method, path },
17
- });
18
- const url = `${GITHUB_BASE}${path}`;
19
- const opts = {
20
- method,
21
- headers: {
22
- Authorization: `Bearer ${token}`,
23
- Accept: "application/vnd.github+json",
24
- "Content-Type": "application/json",
25
- },
26
- };
27
- if (body)
28
- opts.body = body;
29
- const res = await fetch(url, opts);
30
- if (!res.ok) {
31
- (0, runtime_1.emitNervesEvent)({
32
- level: "error",
33
- event: "client.error",
34
- component: "clients",
35
- message: "GitHub request failed",
36
- meta: { client: "github", method, path, status: res.status },
37
- });
38
- return (0, api_error_1.handleApiError)(res, "GitHub", "github");
39
- }
40
- const data = await res.json();
41
- (0, runtime_1.emitNervesEvent)({
42
- event: "client.request_end",
43
- component: "clients",
44
- message: "GitHub request completed",
45
- meta: { client: "github", method, path, success: true },
46
- });
47
- return JSON.stringify(data, null, 2);
48
- }
49
- catch (err) {
50
- (0, runtime_1.emitNervesEvent)({
51
- level: "error",
52
- event: "client.error",
53
- component: "clients",
54
- message: "GitHub request threw exception",
55
- meta: {
56
- client: "github",
57
- method,
58
- path,
59
- reason: err instanceof Error ? err.message : String(err),
60
- },
61
- });
62
- return (0, api_error_1.handleApiError)(err, "GitHub", "github");
63
- }
10
+ return (0, api_client_1.apiRequest)({
11
+ baseUrl: GITHUB_BASE,
12
+ method,
13
+ path,
14
+ token,
15
+ clientName: "github",
16
+ serviceLabel: "GitHub",
17
+ connectionName: "github",
18
+ body,
19
+ extraHeaders: {
20
+ Accept: "application/vnd.github+json",
21
+ },
22
+ });
64
23
  }
@@ -7,61 +7,20 @@ exports.graphRequest = graphRequest;
7
7
  exports.getProfile = getProfile;
8
8
  const api_error_1 = require("../heart/api-error");
9
9
  const runtime_1 = require("../nerves/runtime");
10
+ const api_client_1 = require("./api-client");
10
11
  const GRAPH_BASE = "https://graph.microsoft.com/v1.0";
11
12
  // Generic Graph API request. Returns response body as pretty-printed JSON string.
12
13
  async function graphRequest(token, method, path, body) {
13
- try {
14
- (0, runtime_1.emitNervesEvent)({
15
- event: "client.request_start",
16
- component: "clients",
17
- message: "starting Graph request",
18
- meta: { client: "graph", method, path },
19
- });
20
- const url = `${GRAPH_BASE}${path}`;
21
- const opts = {
22
- method,
23
- headers: {
24
- Authorization: `Bearer ${token}`,
25
- "Content-Type": "application/json",
26
- },
27
- };
28
- if (body)
29
- opts.body = body;
30
- const res = await fetch(url, opts);
31
- if (!res.ok) {
32
- (0, runtime_1.emitNervesEvent)({
33
- level: "error",
34
- event: "client.error",
35
- component: "clients",
36
- message: "Graph request failed",
37
- meta: { client: "graph", method, path, status: res.status },
38
- });
39
- return (0, api_error_1.handleApiError)(res, "Graph", "graph");
40
- }
41
- const data = await res.json();
42
- (0, runtime_1.emitNervesEvent)({
43
- event: "client.request_end",
44
- component: "clients",
45
- message: "Graph request completed",
46
- meta: { client: "graph", method, path, success: true },
47
- });
48
- return JSON.stringify(data, null, 2);
49
- }
50
- catch (err) {
51
- (0, runtime_1.emitNervesEvent)({
52
- level: "error",
53
- event: "client.error",
54
- component: "clients",
55
- message: "Graph request threw exception",
56
- meta: {
57
- client: "graph",
58
- method,
59
- path,
60
- reason: err instanceof Error ? err.message : String(err),
61
- },
62
- });
63
- return (0, api_error_1.handleApiError)(err, "Graph", "graph");
64
- }
14
+ return (0, api_client_1.apiRequest)({
15
+ baseUrl: GRAPH_BASE,
16
+ method,
17
+ path,
18
+ token,
19
+ clientName: "graph",
20
+ serviceLabel: "Graph",
21
+ connectionName: "graph",
22
+ body,
23
+ });
65
24
  }
66
25
  // Backward-compatible thin wrapper: fetches /me and formats as human-readable text.
67
26
  async function getProfile(token) {