@ouro.bot/cli 0.1.0-alpha.28 → 0.1.0-alpha.280

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 (267) 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 +1696 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +117 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +117 -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 +456 -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 +304 -0
  20. package/dist/heart/config.js +63 -30
  21. package/dist/heart/core.js +644 -196
  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 +79 -3
  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 +574 -0
  28. package/dist/heart/daemon/cli-exec.js +1932 -0
  29. package/dist/heart/daemon/cli-parse.js +762 -0
  30. package/dist/heart/daemon/cli-render.js +503 -0
  31. package/dist/heart/daemon/cli-types.js +8 -0
  32. package/dist/heart/daemon/daemon-cli.js +29 -1155
  33. package/dist/heart/daemon/daemon-entry.js +234 -3
  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 +236 -0
  37. package/dist/heart/daemon/daemon.js +702 -57
  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 +46 -9
  42. package/dist/heart/daemon/log-tailer.js +82 -12
  43. package/dist/heart/daemon/logs-prune.js +105 -0
  44. package/dist/heart/daemon/message-router.js +17 -8
  45. package/dist/heart/daemon/os-cron-deps.js +134 -0
  46. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  47. package/dist/heart/daemon/process-manager.js +180 -0
  48. package/dist/heart/daemon/pulse.js +463 -0
  49. package/dist/heart/daemon/run-hooks.js +2 -0
  50. package/dist/heart/daemon/runtime-logging.js +67 -16
  51. package/dist/heart/daemon/runtime-metadata.js +101 -0
  52. package/dist/heart/daemon/runtime-mode.js +67 -0
  53. package/dist/heart/daemon/safe-mode.js +161 -0
  54. package/dist/heart/daemon/sense-manager.js +72 -3
  55. package/dist/heart/daemon/session-id-resolver.js +131 -0
  56. package/dist/heart/daemon/skill-management-installer.js +94 -0
  57. package/dist/heart/daemon/socket-client.js +307 -0
  58. package/dist/heart/daemon/task-scheduler.js +3 -25
  59. package/dist/heart/daemon/thoughts.js +510 -0
  60. package/dist/heart/delegation.js +62 -0
  61. package/dist/heart/habits/habit-migration.js +181 -0
  62. package/dist/heart/habits/habit-parser.js +119 -0
  63. package/dist/heart/habits/habit-scheduler.js +371 -0
  64. package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
  65. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  66. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  67. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  68. package/dist/heart/identity.js +163 -60
  69. package/dist/heart/kicks.js +2 -20
  70. package/dist/heart/mcp/mcp-server.js +653 -0
  71. package/dist/heart/migrate-config.js +127 -0
  72. package/dist/heart/model-capabilities.js +59 -0
  73. package/dist/heart/outlook/outlook-http.js +439 -0
  74. package/dist/heart/outlook/outlook-read.js +1618 -0
  75. package/dist/heart/outlook/outlook-render.js +1032 -0
  76. package/dist/heart/outlook/outlook-types.js +7 -0
  77. package/dist/heart/outlook/outlook-view.js +194 -0
  78. package/dist/heart/progress-story.js +42 -0
  79. package/dist/heart/provider-failover.js +88 -0
  80. package/dist/heart/provider-ping.js +162 -0
  81. package/dist/heart/providers/anthropic-token.js +163 -0
  82. package/dist/heart/providers/anthropic.js +169 -46
  83. package/dist/heart/providers/azure.js +98 -11
  84. package/dist/heart/providers/error-classification.js +63 -0
  85. package/dist/heart/providers/github-copilot.js +136 -0
  86. package/dist/heart/providers/minimax-vlm.js +189 -0
  87. package/dist/heart/providers/minimax.js +23 -5
  88. package/dist/heart/providers/openai-codex.js +33 -22
  89. package/dist/heart/session-activity.js +172 -0
  90. package/dist/heart/session-recall.js +216 -0
  91. package/dist/heart/start-of-turn-packet.js +330 -0
  92. package/dist/heart/streaming.js +36 -27
  93. package/dist/heart/sync.js +277 -0
  94. package/dist/heart/target-resolution.js +127 -0
  95. package/dist/heart/tempo.js +93 -0
  96. package/dist/heart/temporal-view.js +41 -0
  97. package/dist/heart/tool-activity-callbacks.js +36 -0
  98. package/dist/heart/tool-description.js +135 -0
  99. package/dist/heart/tool-loop.js +200 -0
  100. package/dist/heart/turn-context.js +358 -0
  101. package/dist/heart/turn-coordinator.js +28 -0
  102. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  103. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  104. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  105. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  106. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  107. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  108. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  109. package/dist/mind/associative-recall.js +126 -66
  110. package/dist/mind/bundle-manifest.js +8 -1
  111. package/dist/mind/context.js +125 -14
  112. package/dist/mind/{memory.js → diary.js} +68 -96
  113. package/dist/mind/embedding-provider.js +60 -0
  114. package/dist/mind/file-state.js +179 -0
  115. package/dist/mind/first-impressions.js +14 -1
  116. package/dist/mind/friends/channel.js +56 -0
  117. package/dist/mind/friends/group-context.js +144 -0
  118. package/dist/mind/friends/resolver.js +37 -0
  119. package/dist/mind/friends/store-file.js +58 -3
  120. package/dist/mind/friends/trust-explanation.js +74 -0
  121. package/dist/mind/friends/types.js +8 -0
  122. package/dist/mind/journal-index.js +161 -0
  123. package/dist/mind/obligation-steering.js +221 -0
  124. package/dist/mind/pending.js +76 -9
  125. package/dist/mind/prompt.js +874 -109
  126. package/dist/mind/scrutiny.js +173 -0
  127. package/dist/mind/token-estimate.js +8 -12
  128. package/dist/nerves/cli-logging.js +7 -1
  129. package/dist/nerves/coverage/audit.js +1 -1
  130. package/dist/nerves/coverage/file-completeness.js +50 -5
  131. package/dist/nerves/coverage/run-artifacts.js +1 -1
  132. package/dist/nerves/event-buffer.js +111 -0
  133. package/dist/nerves/index.js +220 -4
  134. package/dist/nerves/observation.js +20 -0
  135. package/dist/nerves/runtime.js +5 -1
  136. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  137. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  138. package/dist/outlook-ui/index.html +15 -0
  139. package/dist/repertoire/ado-client.js +15 -56
  140. package/dist/repertoire/ado-semantic.js +11 -10
  141. package/dist/repertoire/api-client.js +97 -0
  142. package/dist/repertoire/bitwarden-store.js +319 -0
  143. package/dist/repertoire/bw-installer.js +79 -0
  144. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  145. package/dist/repertoire/coding/context-pack.js +330 -0
  146. package/dist/repertoire/coding/feedback.js +197 -30
  147. package/dist/repertoire/coding/manager.js +149 -2
  148. package/dist/repertoire/coding/spawner.js +55 -9
  149. package/dist/repertoire/coding/tools.js +170 -7
  150. package/dist/repertoire/commerce-errors.js +109 -0
  151. package/dist/repertoire/commerce-self-test.js +156 -0
  152. package/dist/repertoire/credential-access.js +515 -0
  153. package/dist/repertoire/duffel-client.js +185 -0
  154. package/dist/repertoire/github-client.js +14 -55
  155. package/dist/repertoire/graph-client.js +11 -52
  156. package/dist/repertoire/guardrails.js +369 -0
  157. package/dist/repertoire/mcp-client.js +255 -0
  158. package/dist/repertoire/mcp-manager.js +305 -0
  159. package/dist/repertoire/mcp-tools.js +63 -0
  160. package/dist/repertoire/shell-sessions.js +133 -0
  161. package/dist/repertoire/skills.js +14 -23
  162. package/dist/repertoire/stripe-client.js +131 -0
  163. package/dist/repertoire/tasks/board.js +43 -5
  164. package/dist/repertoire/tasks/fix.js +182 -0
  165. package/dist/repertoire/tasks/index.js +28 -10
  166. package/dist/repertoire/tasks/lifecycle.js +2 -2
  167. package/dist/repertoire/tasks/parser.js +3 -2
  168. package/dist/repertoire/tasks/scanner.js +194 -37
  169. package/dist/repertoire/tasks/transitions.js +11 -28
  170. package/dist/repertoire/tools-base.js +43 -771
  171. package/dist/repertoire/tools-bluebubbles.js +201 -0
  172. package/dist/repertoire/tools-bridge.js +141 -0
  173. package/dist/repertoire/tools-config.js +185 -0
  174. package/dist/repertoire/tools-continuity.js +248 -0
  175. package/dist/repertoire/tools-credential.js +182 -0
  176. package/dist/repertoire/tools-files.js +342 -0
  177. package/dist/repertoire/tools-flight.js +224 -0
  178. package/dist/repertoire/tools-flow.js +62 -0
  179. package/dist/repertoire/tools-github.js +1 -7
  180. package/dist/repertoire/tools-memory.js +347 -0
  181. package/dist/repertoire/tools-session.js +739 -0
  182. package/dist/repertoire/tools-shell.js +120 -0
  183. package/dist/repertoire/tools-stripe.js +180 -0
  184. package/dist/repertoire/tools-surface.js +243 -0
  185. package/dist/repertoire/tools-teams.js +12 -62
  186. package/dist/repertoire/tools-travel.js +125 -0
  187. package/dist/repertoire/tools-user-profile.js +144 -0
  188. package/dist/repertoire/tools-vault.js +110 -0
  189. package/dist/repertoire/tools.js +155 -130
  190. package/dist/repertoire/travel-api-client.js +360 -0
  191. package/dist/repertoire/user-profile.js +118 -0
  192. package/dist/repertoire/vault-setup.js +241 -0
  193. package/dist/scripts/claude-code-hook.js +41 -0
  194. package/dist/scripts/claude-code-stop-hook.js +47 -0
  195. package/dist/senses/attention-queue.js +97 -0
  196. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  197. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +143 -9
  198. package/dist/senses/bluebubbles/entry.js +13 -0
  199. package/dist/senses/bluebubbles/inbound-log.js +110 -0
  200. package/dist/senses/bluebubbles/index.js +1410 -0
  201. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +140 -23
  202. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +20 -5
  203. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  204. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  205. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  206. package/dist/senses/cli/bracketed-paste.js +82 -0
  207. package/dist/senses/cli/image-paste.js +211 -0
  208. package/dist/senses/cli/image-ref-navigation.js +75 -0
  209. package/dist/senses/cli/ink-app.js +156 -0
  210. package/dist/senses/cli/inline-diff.js +64 -0
  211. package/dist/senses/cli/input-keys.js +174 -0
  212. package/dist/senses/cli/kill-ring.js +86 -0
  213. package/dist/senses/cli/message-list.js +51 -0
  214. package/dist/senses/cli/ouro-tui.js +595 -0
  215. package/dist/senses/cli/spinner-imperative.js +135 -0
  216. package/dist/senses/cli/spinner.js +101 -0
  217. package/dist/senses/cli/status-line.js +60 -0
  218. package/dist/senses/cli/streaming-markdown.js +526 -0
  219. package/dist/senses/cli/tool-display.js +83 -0
  220. package/dist/senses/cli/tool-render.js +85 -0
  221. package/dist/senses/cli/tui-store.js +240 -0
  222. package/dist/senses/cli/virtual-list.js +35 -0
  223. package/dist/senses/cli-entry.js +1 -1
  224. package/dist/senses/cli-layout.js +187 -0
  225. package/dist/senses/cli.js +569 -246
  226. package/dist/senses/commands.js +65 -1
  227. package/dist/senses/continuity.js +94 -0
  228. package/dist/senses/habit-turn-message.js +108 -0
  229. package/dist/senses/inner-dialog-worker.js +112 -19
  230. package/dist/senses/inner-dialog.js +593 -85
  231. package/dist/senses/pipeline.js +552 -0
  232. package/dist/senses/shared-turn.js +199 -0
  233. package/dist/senses/surface-tool.js +68 -0
  234. package/dist/senses/teams.js +664 -165
  235. package/dist/senses/trust-gate.js +112 -2
  236. package/package.json +14 -7
  237. package/skills/agent-commerce.md +106 -0
  238. package/skills/browser-navigation.md +110 -0
  239. package/skills/commerce-setup-guide.md +116 -0
  240. package/skills/commerce-setup.md +84 -0
  241. package/skills/configure-dev-tools.md +81 -0
  242. package/skills/travel-planning.md +138 -0
  243. package/dist/heart/daemon/subagent-installer.js +0 -134
  244. package/dist/senses/bluebubbles-entry.js +0 -11
  245. package/dist/senses/bluebubbles.js +0 -421
  246. package/dist/senses/debug-activity.js +0 -108
  247. package/subagents/README.md +0 -73
  248. package/subagents/work-doer.md +0 -235
  249. package/subagents/work-merger.md +0 -618
  250. package/subagents/work-planner.md +0 -382
  251. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  252. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  253. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  254. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  255. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  256. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  257. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  258. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  259. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  260. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  261. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  262. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  263. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  264. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  265. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  266. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  267. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -1,17 +1,59 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_MAX_GENERATIONS = exports.DEFAULT_MAX_LOG_SIZE_BYTES = void 0;
3
37
  exports.createTraceId = createTraceId;
4
38
  exports.ensureTraceId = ensureTraceId;
5
39
  exports.createFanoutSink = createFanoutSink;
6
40
  exports.formatTerminalEntry = formatTerminalEntry;
41
+ exports.registerSpinnerHooks = registerSpinnerHooks;
7
42
  exports.createTerminalSink = createTerminalSink;
8
43
  exports.createStderrSink = createStderrSink;
44
+ exports.rotateIfNeeded = rotateIfNeeded;
9
45
  exports.createNdjsonFileSink = createNdjsonFileSink;
10
46
  exports.registerGlobalLogSink = registerGlobalLogSink;
11
47
  exports.createLogger = createLogger;
12
48
  const fs_1 = require("fs");
13
49
  const path_1 = require("path");
14
50
  const crypto_1 = require("crypto");
51
+ const zlib = __importStar(require("zlib"));
52
+ // NOTE: runtime.ts imports `createLogger` and `ensureTraceId` from this file.
53
+ // The cycle is safe in CommonJS because runtime.ts only uses those values
54
+ // lazily inside `getRuntimeLogger()`, and we only call `emitNervesEvent` from
55
+ // inside `rotateIfNeeded` — never at module top level.
56
+ const runtime_1 = require("./runtime");
15
57
  const LEVEL_PRIORITY = {
16
58
  debug: 10,
17
59
  info: 20,
@@ -73,36 +115,210 @@ function formatTerminalEntry(entry) {
73
115
  const level = entry.level.toUpperCase();
74
116
  return `${formatTerminalTime(entry.ts)} ${level} [${entry.component}] ${entry.message}${formatTerminalMeta(entry.meta)}`;
75
117
  }
76
- function createTerminalSink(write = (chunk) => process.stderr.write(chunk), colorize = true) {
118
+ // Spinner coordination: the CLI sense registers these so log output
119
+ // doesn't interleave with the active spinner animation.
120
+ let _pauseSpinner = null;
121
+ let _resumeSpinner = null;
122
+ function registerSpinnerHooks(pause, resume) {
123
+ _pauseSpinner = pause;
124
+ _resumeSpinner = resume;
125
+ }
126
+ function createTerminalSink(write = (chunk) => { try {
127
+ process.stderr.write(chunk);
128
+ }
129
+ catch { /* EPIPE: daemon detached, no terminal */ } }, colorize = true) {
77
130
  return (entry) => {
131
+ _pauseSpinner?.();
78
132
  const line = formatTerminalEntry(entry);
79
133
  if (!colorize) {
80
134
  write(`${line}\n`);
135
+ _resumeSpinner?.();
81
136
  return;
82
137
  }
83
138
  const prefix = LEVEL_COLORS[entry.level];
84
139
  write(`${prefix}${line}\x1b[0m\n`);
140
+ _resumeSpinner?.();
85
141
  };
86
142
  }
87
- function createStderrSink(write = (chunk) => process.stderr.write(chunk)) {
143
+ function createStderrSink(write) {
88
144
  return createTerminalSink(write);
89
145
  }
90
- function createNdjsonFileSink(filePath) {
146
+ exports.DEFAULT_MAX_LOG_SIZE_BYTES = 25 * 1024 * 1024; // 25 MB per active stream
147
+ exports.DEFAULT_MAX_GENERATIONS = 5; // keep 5 gzipped historical generations
148
+ const DEFAULT_ROTATION_CHECK_INTERVAL_BYTES = 1024 * 1024; // ~1MB between stat checks
149
+ /** Internal: compute the gzipped generation path for a given ndjson file. */
150
+ function generationGzPath(base, ext, n) {
151
+ return ext === ".ndjson" ? `${base}.${n}.ndjson.gz` : `${base}.${n}.gz`;
152
+ }
153
+ /** Internal: compute the legacy uncompressed generation path for a given file. */
154
+ function generationPlainPath(base, ext, n) {
155
+ return ext === ".ndjson" ? `${base}.${n}.ndjson` : `${base}.${n}`;
156
+ }
157
+ /**
158
+ * Rotate a log file in place.
159
+ *
160
+ * Scheme (25 MB × 5 gzipped generations by default):
161
+ * active : foo.ndjson
162
+ * newest gen : foo.1.ndjson.gz
163
+ * oldest gen : foo.5.ndjson.gz (dropped on next rotation)
164
+ *
165
+ * Legacy tolerance: uncompressed `foo.1.ndjson` / `foo.2.ndjson` files from the
166
+ * old scheme are treated as the corresponding generation and gzipped on first
167
+ * rotation.
168
+ *
169
+ * Concurrent-writer safety: the active file is renamed (inode stays alive for
170
+ * any open writer fd) then gzipped at the renamed path. An active writer
171
+ * continues writing to its original fd; on the next write cycle it sees the
172
+ * old path is missing and creates a fresh file.
173
+ *
174
+ * Backwards-compatible signature: passing a `number` as the second argument
175
+ * (the old API) is still accepted and interpreted as `maxSizeBytes`.
176
+ */
177
+ function rotateIfNeeded(filePath, optionsOrMaxSize) {
178
+ const options = typeof optionsOrMaxSize === "number"
179
+ ? { maxSizeBytes: optionsOrMaxSize }
180
+ : optionsOrMaxSize ?? {};
181
+ const maxSizeBytes = options.maxSizeBytes ?? exports.DEFAULT_MAX_LOG_SIZE_BYTES;
182
+ const maxGenerations = options.maxGenerations ?? exports.DEFAULT_MAX_GENERATIONS;
183
+ const compress = options.compress ?? true;
184
+ let size;
185
+ try {
186
+ size = (0, fs_1.statSync)(filePath).size;
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ if (size < maxSizeBytes)
192
+ return false;
193
+ const ext = filePath.endsWith(".ndjson") ? ".ndjson" : "";
194
+ const base = ext ? filePath.slice(0, -ext.length) : filePath;
195
+ const traceId = (0, crypto_1.randomUUID)();
196
+ (0, runtime_1.emitNervesEvent)({
197
+ component: "nerves",
198
+ event: "nerves.rotation_start",
199
+ trace_id: traceId,
200
+ message: "rotating log file",
201
+ meta: { path: filePath, currentSize: size, threshold: maxSizeBytes, generation: 1 },
202
+ });
203
+ let completed = false;
204
+ try {
205
+ // Step 1: drop / shift existing generations starting from the oldest.
206
+ // For each slot N (maxGenerations..2), find whatever occupies slot (N-1)
207
+ // (as .gz or legacy uncompressed) and move it to slot N. Slot N occupants
208
+ // get overwritten (oldest is dropped). Non-existent slots are skipped.
209
+ for (let n = maxGenerations; n >= 2; n--) {
210
+ const destGz = generationGzPath(base, ext, n);
211
+ const srcGz = generationGzPath(base, ext, n - 1);
212
+ const srcPlain = generationPlainPath(base, ext, n - 1);
213
+ // Drop whatever currently occupies the destination slot (oldest generation).
214
+ if ((0, fs_1.existsSync)(destGz)) {
215
+ (0, fs_1.unlinkSync)(destGz);
216
+ }
217
+ const destPlain = generationPlainPath(base, ext, n);
218
+ if ((0, fs_1.existsSync)(destPlain)) {
219
+ (0, fs_1.unlinkSync)(destPlain);
220
+ }
221
+ // Prefer moving .gz → .gz (cheap rename).
222
+ if ((0, fs_1.existsSync)(srcGz)) {
223
+ (0, fs_1.renameSync)(srcGz, destGz);
224
+ continue;
225
+ }
226
+ // Legacy migration: if a plain .ndjson generation file exists from the
227
+ // old scheme, read it, gzip it into the destination slot, and delete
228
+ // the plain source.
229
+ if ((0, fs_1.existsSync)(srcPlain)) {
230
+ if (compress) {
231
+ const buf = (0, fs_1.readFileSync)(srcPlain);
232
+ const compressed = zlib.gzipSync(buf);
233
+ (0, fs_1.writeFileSync)(destGz, compressed);
234
+ (0, fs_1.unlinkSync)(srcPlain);
235
+ }
236
+ else {
237
+ // compress=false: just rename
238
+ (0, fs_1.renameSync)(srcPlain, destPlain);
239
+ }
240
+ }
241
+ }
242
+ // Step 2: rename the active file to the generation-1 plain path. The
243
+ // active writer keeps its open fd; the file path now points elsewhere.
244
+ const plain1 = generationPlainPath(base, ext, 1);
245
+ // If a stale .1 plain path exists (e.g. a previous compress=false run),
246
+ // remove it so the rename can claim the slot cleanly.
247
+ if ((0, fs_1.existsSync)(plain1)) {
248
+ (0, fs_1.unlinkSync)(plain1);
249
+ }
250
+ (0, fs_1.renameSync)(filePath, plain1);
251
+ // Step 3: gzip (or keep plain) the renamed file into the .1 generation.
252
+ if (compress) {
253
+ const gz1 = generationGzPath(base, ext, 1);
254
+ // Remove any lingering .1.ndjson.gz to avoid "file exists" on write.
255
+ if ((0, fs_1.existsSync)(gz1)) {
256
+ (0, fs_1.unlinkSync)(gz1);
257
+ }
258
+ const buf = (0, fs_1.readFileSync)(plain1);
259
+ const compressed = zlib.gzipSync(buf);
260
+ (0, fs_1.writeFileSync)(gz1, compressed);
261
+ (0, fs_1.unlinkSync)(plain1);
262
+ }
263
+ completed = true;
264
+ (0, runtime_1.emitNervesEvent)({
265
+ component: "nerves",
266
+ event: "nerves.rotation_end",
267
+ trace_id: traceId,
268
+ message: "log rotation complete",
269
+ meta: { path: filePath, compressedPath: compress ? generationGzPath(base, ext, 1) : plain1, bytesFreed: size },
270
+ });
271
+ return true;
272
+ }
273
+ catch (err) {
274
+ /* v8 ignore next -- defensive: completed=true only reached after the try block's return @preserve */
275
+ if (!completed) {
276
+ /* v8 ignore next -- defensive: fs ops throw real Errors @preserve */
277
+ const reason = err instanceof Error ? err.message : String(err);
278
+ (0, runtime_1.emitNervesEvent)({
279
+ component: "nerves",
280
+ event: "nerves.rotation_error",
281
+ trace_id: traceId,
282
+ level: "error",
283
+ message: "log rotation failed",
284
+ meta: { path: filePath, error: reason },
285
+ });
286
+ }
287
+ throw err;
288
+ }
289
+ }
290
+ function createNdjsonFileSink(filePath, optionsOrMaxSize) {
91
291
  (0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
292
+ const options = typeof optionsOrMaxSize === "number"
293
+ ? { maxSizeBytes: optionsOrMaxSize }
294
+ : optionsOrMaxSize ?? {};
295
+ const rotationCheckInterval = options.rotationCheckIntervalBytes ?? DEFAULT_ROTATION_CHECK_INTERVAL_BYTES;
92
296
  const queue = [];
93
297
  let flushing = false;
298
+ let bytesSinceCheck = 0;
94
299
  function flush() {
95
300
  if (flushing || queue.length === 0)
96
301
  return;
97
302
  flushing = true;
98
303
  const line = queue.shift();
304
+ if (bytesSinceCheck >= rotationCheckInterval) {
305
+ bytesSinceCheck = 0;
306
+ try {
307
+ rotateIfNeeded(filePath, options);
308
+ }
309
+ catch {
310
+ // Rotation errors are surfaced via nerves events; never block writes.
311
+ }
312
+ }
99
313
  (0, fs_1.appendFile)(filePath, line, "utf8", () => {
100
314
  flushing = false;
101
315
  flush();
102
316
  });
103
317
  }
104
318
  return (entry) => {
105
- queue.push(`${JSON.stringify(entry)}\n`);
319
+ const line = `${JSON.stringify(entry)}\n`;
320
+ bytesSinceCheck += line.length;
321
+ queue.push(line);
106
322
  flush();
107
323
  };
108
324
  }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Nerves observation layer — shared typed readers for runtime state.
4
+ *
5
+ * This module re-exports domain types that multiple consumers need
6
+ * (Outlook, CLI, agent tools) so they don't maintain parallel type universes.
7
+ *
8
+ * The Outlook UI consumes these through the HTTP API, but the same
9
+ * observation functions back CLI commands and future native clients.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ /* v8 ignore start — module-level observability event */
13
+ const runtime_1 = require("./runtime");
14
+ (0, runtime_1.emitNervesEvent)({
15
+ component: "nerves",
16
+ event: "nerves.observation_loaded",
17
+ message: "observation layer loaded",
18
+ meta: {},
19
+ });
20
+ /* v8 ignore stop */
@@ -6,7 +6,11 @@ const index_1 = require("./index");
6
6
  let runtimeLogger = null;
7
7
  function getRuntimeLogger() {
8
8
  if (!runtimeLogger) {
9
- runtimeLogger = (0, index_1.createLogger)({ level: "info" });
9
+ // Default logger has no sinks — events emitted before the real logger is
10
+ // configured (e.g. identity resolution during startup) are silently dropped.
11
+ // This prevents INFO lines from leaking to stderr and interleaving with
12
+ // the CLI spinner when the ouro subprocess hasn't configured its logger yet.
13
+ runtimeLogger = (0, index_1.createLogger)({ level: "info", sinks: [] });
10
14
  }
11
15
  return runtimeLogger;
12
16
  }