@ouro.bot/cli 0.1.0-alpha.21 → 0.1.0-alpha.210

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 (227) 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 +1312 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +179 -0
  7. package/dist/arc/episodes.js +119 -0
  8. package/dist/arc/intentions.js +173 -0
  9. package/dist/arc/obligations.js +321 -0
  10. package/dist/arc/presence.js +185 -0
  11. package/dist/heart/active-work.js +832 -0
  12. package/dist/heart/agent-entry.js +37 -2
  13. package/dist/heart/auth/auth-flow.js +444 -0
  14. package/dist/heart/bridges/manager.js +358 -0
  15. package/dist/heart/bridges/state-machine.js +135 -0
  16. package/dist/heart/bridges/store.js +123 -0
  17. package/dist/heart/commitments.js +111 -0
  18. package/dist/heart/config-registry.js +305 -0
  19. package/dist/heart/config.js +59 -30
  20. package/dist/heart/core.js +606 -131
  21. package/dist/heart/cross-chat-delivery.js +146 -0
  22. package/dist/heart/daemon/agent-config-check.js +149 -0
  23. package/dist/heart/daemon/agent-discovery.js +81 -0
  24. package/dist/heart/daemon/agent-service.js +360 -0
  25. package/dist/heart/daemon/cadence.js +70 -0
  26. package/dist/heart/daemon/cli-defaults.js +557 -0
  27. package/dist/heart/daemon/cli-exec.js +1847 -0
  28. package/dist/heart/daemon/cli-parse.js +759 -0
  29. package/dist/heart/daemon/cli-render.js +316 -0
  30. package/dist/heart/daemon/cli-types.js +8 -0
  31. package/dist/heart/daemon/daemon-cli.js +29 -1163
  32. package/dist/heart/daemon/daemon-entry.js +182 -4
  33. package/dist/heart/daemon/daemon-health.js +136 -0
  34. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  35. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  36. package/dist/heart/daemon/daemon.js +491 -18
  37. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  38. package/dist/heart/daemon/hooks/bundle-meta.js +206 -0
  39. package/dist/heart/daemon/inner-status.js +89 -0
  40. package/dist/heart/daemon/launchd.js +165 -0
  41. package/dist/heart/daemon/log-tailer.js +4 -3
  42. package/dist/heart/daemon/message-router.js +17 -8
  43. package/dist/heart/daemon/os-cron-deps.js +134 -0
  44. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  45. package/dist/heart/daemon/process-manager.js +123 -0
  46. package/dist/heart/daemon/run-hooks.js +39 -0
  47. package/dist/heart/daemon/runtime-logging.js +58 -15
  48. package/dist/heart/daemon/runtime-metadata.js +101 -0
  49. package/dist/heart/daemon/runtime-mode.js +67 -0
  50. package/dist/heart/daemon/safe-mode.js +161 -0
  51. package/dist/heart/daemon/sense-manager.js +71 -2
  52. package/dist/heart/daemon/session-id-resolver.js +131 -0
  53. package/dist/heart/daemon/skill-management-installer.js +94 -0
  54. package/dist/heart/daemon/socket-client.js +202 -0
  55. package/dist/heart/daemon/task-scheduler.js +3 -25
  56. package/dist/heart/daemon/thoughts.js +507 -0
  57. package/dist/heart/delegation.js +62 -0
  58. package/dist/heart/habits/habit-migration.js +181 -0
  59. package/dist/heart/habits/habit-parser.js +119 -0
  60. package/dist/heart/habits/habit-scheduler.js +369 -0
  61. package/dist/heart/{daemon → hatch}/hatch-flow.js +29 -119
  62. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  63. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  64. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  65. package/dist/heart/identity.js +144 -55
  66. package/dist/heart/kicks.js +2 -20
  67. package/dist/heart/mcp/mcp-server.js +608 -0
  68. package/dist/heart/migrate-config.js +127 -0
  69. package/dist/heart/model-capabilities.js +48 -0
  70. package/dist/heart/outlook/outlook-http.js +439 -0
  71. package/dist/heart/outlook/outlook-read.js +1618 -0
  72. package/dist/heart/outlook/outlook-render.js +1032 -0
  73. package/dist/heart/outlook/outlook-types.js +7 -0
  74. package/dist/heart/outlook/outlook-view.js +194 -0
  75. package/dist/heart/progress-story.js +42 -0
  76. package/dist/heart/provider-failover.js +88 -0
  77. package/dist/heart/provider-ping.js +162 -0
  78. package/dist/heart/providers/anthropic-token.js +163 -0
  79. package/dist/heart/providers/anthropic.js +187 -41
  80. package/dist/heart/providers/azure.js +116 -10
  81. package/dist/heart/providers/github-copilot.js +157 -0
  82. package/dist/heart/providers/minimax.js +33 -3
  83. package/dist/heart/providers/openai-codex.js +51 -16
  84. package/dist/heart/session-activity.js +172 -0
  85. package/dist/heart/session-recall.js +216 -0
  86. package/dist/heart/start-of-turn-packet.js +330 -0
  87. package/dist/heart/streaming.js +36 -27
  88. package/dist/heart/sync.js +217 -0
  89. package/dist/heart/target-resolution.js +127 -0
  90. package/dist/heart/tempo.js +93 -0
  91. package/dist/heart/temporal-view.js +41 -0
  92. package/dist/heart/tool-activity-callbacks.js +36 -0
  93. package/dist/heart/tool-description.js +135 -0
  94. package/dist/heart/tool-loop.js +200 -0
  95. package/dist/heart/turn-context.js +358 -0
  96. package/dist/heart/turn-coordinator.js +28 -0
  97. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  98. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  99. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  100. package/dist/heart/versioning/ouro-version-manager.js +171 -0
  101. package/dist/heart/versioning/staged-restart.js +138 -0
  102. package/dist/heart/versioning/update-checker.js +111 -0
  103. package/dist/heart/versioning/update-hooks.js +138 -0
  104. package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
  105. package/dist/mind/associative-recall.js +122 -32
  106. package/dist/mind/bundle-manifest.js +19 -1
  107. package/dist/mind/context.js +125 -14
  108. package/dist/mind/{memory.js → diary.js} +69 -56
  109. package/dist/mind/file-state.js +179 -0
  110. package/dist/mind/first-impressions.js +16 -2
  111. package/dist/mind/friends/channel.js +56 -0
  112. package/dist/mind/friends/group-context.js +144 -0
  113. package/dist/mind/friends/store-file.js +36 -0
  114. package/dist/mind/friends/trust-explanation.js +74 -0
  115. package/dist/mind/friends/types.js +8 -0
  116. package/dist/mind/journal-index.js +161 -0
  117. package/dist/mind/obligation-steering.js +221 -0
  118. package/dist/mind/pending.js +76 -9
  119. package/dist/mind/phrases.js +1 -0
  120. package/dist/mind/prompt.js +795 -108
  121. package/dist/mind/scrutiny.js +173 -0
  122. package/dist/mind/token-estimate.js +8 -12
  123. package/dist/nerves/coverage/audit.js +1 -1
  124. package/dist/nerves/coverage/file-completeness.js +42 -5
  125. package/dist/nerves/coverage/run-artifacts.js +1 -1
  126. package/dist/nerves/event-buffer.js +111 -0
  127. package/dist/nerves/index.js +63 -3
  128. package/dist/nerves/observation.js +20 -0
  129. package/dist/nerves/runtime.js +5 -1
  130. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  131. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  132. package/dist/outlook-ui/index.html +15 -0
  133. package/dist/repertoire/ado-semantic.js +11 -5
  134. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  135. package/dist/repertoire/coding/context-pack.js +330 -0
  136. package/dist/repertoire/coding/feedback.js +197 -30
  137. package/dist/repertoire/coding/manager.js +149 -2
  138. package/dist/repertoire/coding/spawner.js +55 -9
  139. package/dist/repertoire/coding/tools.js +170 -7
  140. package/dist/repertoire/guardrails.js +291 -0
  141. package/dist/repertoire/mcp-client.js +254 -0
  142. package/dist/repertoire/mcp-manager.js +251 -0
  143. package/dist/repertoire/shell-sessions.js +133 -0
  144. package/dist/repertoire/skills.js +14 -23
  145. package/dist/repertoire/tasks/board.js +43 -5
  146. package/dist/repertoire/tasks/fix.js +182 -0
  147. package/dist/repertoire/tasks/index.js +28 -10
  148. package/dist/repertoire/tasks/lifecycle.js +2 -2
  149. package/dist/repertoire/tasks/parser.js +3 -2
  150. package/dist/repertoire/tasks/scanner.js +194 -37
  151. package/dist/repertoire/tasks/transitions.js +11 -28
  152. package/dist/repertoire/tools-base.js +31 -771
  153. package/dist/repertoire/tools-bluebubbles.js +94 -0
  154. package/dist/repertoire/tools-bridge.js +141 -0
  155. package/dist/repertoire/tools-config.js +266 -0
  156. package/dist/repertoire/tools-continuity.js +248 -0
  157. package/dist/repertoire/tools-files.js +342 -0
  158. package/dist/repertoire/tools-flow.js +62 -0
  159. package/dist/repertoire/tools-github.js +1 -6
  160. package/dist/repertoire/tools-memory.js +347 -0
  161. package/dist/repertoire/tools-session.js +682 -0
  162. package/dist/repertoire/tools-shell.js +120 -0
  163. package/dist/repertoire/tools-surface.js +228 -0
  164. package/dist/repertoire/tools-teams.js +12 -59
  165. package/dist/repertoire/tools.js +132 -124
  166. package/dist/scripts/claude-code-hook.js +41 -0
  167. package/dist/scripts/claude-code-stop-hook.js +47 -0
  168. package/dist/senses/attention-queue.js +97 -0
  169. package/dist/senses/bluebubbles-client.js +102 -3
  170. package/dist/senses/bluebubbles-entry.js +2 -0
  171. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  172. package/dist/senses/bluebubbles-media.js +118 -23
  173. package/dist/senses/bluebubbles-model.js +12 -4
  174. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  175. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  176. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  177. package/dist/senses/bluebubbles.js +1008 -102
  178. package/dist/senses/cli/adapter.js +274 -0
  179. package/dist/senses/cli/ink-app.js +156 -0
  180. package/dist/senses/cli/inline-diff.js +64 -0
  181. package/dist/senses/cli/message-list.js +51 -0
  182. package/dist/senses/cli/ouro-tui.js +358 -0
  183. package/dist/senses/cli/spinner-imperative.js +135 -0
  184. package/dist/senses/cli/spinner.js +101 -0
  185. package/dist/senses/cli/status-line.js +60 -0
  186. package/dist/senses/cli/streaming-markdown.js +526 -0
  187. package/dist/senses/cli/tool-display.js +83 -0
  188. package/dist/senses/cli/tool-render.js +85 -0
  189. package/dist/senses/cli/tui-store.js +198 -0
  190. package/dist/senses/cli/virtual-list.js +35 -0
  191. package/dist/senses/cli-entry.js +1 -1
  192. package/dist/senses/cli-layout.js +187 -0
  193. package/dist/senses/cli.js +508 -243
  194. package/dist/senses/commands.js +65 -1
  195. package/dist/senses/continuity.js +94 -0
  196. package/dist/senses/habit-turn-message.js +108 -0
  197. package/dist/senses/inner-dialog-worker.js +112 -19
  198. package/dist/senses/inner-dialog.js +591 -84
  199. package/dist/senses/pipeline.js +559 -0
  200. package/dist/senses/shared-turn.js +196 -0
  201. package/dist/senses/surface-tool.js +68 -0
  202. package/dist/senses/teams.js +667 -120
  203. package/dist/senses/trust-gate.js +112 -2
  204. package/package.json +16 -6
  205. package/skills/configure-dev-tools.md +81 -0
  206. package/subagents/README.md +4 -70
  207. package/dist/heart/daemon/subagent-installer.js +0 -134
  208. package/dist/senses/debug-activity.js +0 -107
  209. package/subagents/work-doer.md +0 -235
  210. package/subagents/work-merger.md +0 -618
  211. package/subagents/work-planner.md +0 -382
  212. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  213. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  214. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  215. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  216. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  217. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  218. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  219. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  220. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  221. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  222. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  223. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  224. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  225. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  226. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  227. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
@@ -38,10 +38,30 @@ const fs = __importStar(require("fs"));
38
38
  const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  const runtime_1 = require("../../nerves/runtime");
41
- const CLI_PACKAGE_SPECIFIER = "@ouro.bot/cli@alpha";
42
41
  const WRAPPER_SCRIPT = `#!/bin/sh
43
- exec npx --yes ${CLI_PACKAGE_SPECIFIER} "$@"
42
+ # Check for dev mode — if dev-config.json exists, dispatch to the dev repo
43
+ # Skip dev dispatch for "up" command (explicitly returns to production)
44
+ DEV_CONFIG="$HOME/.ouro-cli/dev-config.json"
45
+ if [ -f "$DEV_CONFIG" ] && [ "$1" != "up" ]; then
46
+ DEV_REPO=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync('$DEV_CONFIG','utf-8')).repoPath)}catch{}" 2>/dev/null)
47
+ DEV_ENTRY="$DEV_REPO/dist/heart/daemon/ouro-entry.js"
48
+ if [ -n "$DEV_REPO" ] && [ -e "$DEV_ENTRY" ]; then
49
+ exec node "$DEV_ENTRY" "$@"
50
+ fi
51
+ fi
52
+ # Fall back to installed version
53
+ ENTRY="$HOME/.ouro-cli/CurrentVersion/node_modules/@ouro.bot/cli/dist/heart/daemon/ouro-entry.js"
54
+ if [ ! -e "$ENTRY" ]; then
55
+ echo "ouro not installed. Run: npx ouro.bot" >&2
56
+ exit 1
57
+ fi
58
+ exec node "$ENTRY" "$@"
44
59
  `;
60
+ function writeWrapperScript(scriptPath, mkdirSync, writeFileSync, chmodSync) {
61
+ mkdirSync(path.dirname(scriptPath), { recursive: true });
62
+ writeFileSync(scriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
63
+ chmodSync(scriptPath, 0o755);
64
+ }
45
65
  function detectShellProfile(homeDir, shell) {
46
66
  if (!shell)
47
67
  return null;
@@ -67,6 +87,16 @@ function buildPathExportLine(binDir, shell) {
67
87
  }
68
88
  return `\n# Added by ouro\nexport PATH="${binDir}:$PATH"\n`;
69
89
  }
90
+ function isWrapperCurrent(scriptPath, existsSync, readFileSync) {
91
+ if (!existsSync(scriptPath))
92
+ return false;
93
+ try {
94
+ return readFileSync(scriptPath, "utf-8") === WRAPPER_SCRIPT;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
70
100
  function installOuroCommand(deps = {}) {
71
101
  /* v8 ignore start -- dep defaults: only used in real runtime, tests always inject @preserve */
72
102
  const platform = deps.platform ?? process.platform;
@@ -87,46 +117,59 @@ function installOuroCommand(deps = {}) {
87
117
  message: "skipped ouro PATH install on Windows",
88
118
  meta: { platform },
89
119
  });
90
- return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: "windows" };
120
+ return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: "windows", repairedOldLauncher: false };
121
+ }
122
+ // Ensure ~/.ouro-cli/ directory layout exists
123
+ if (deps.ensureCliLayout) {
124
+ deps.ensureCliLayout();
91
125
  }
92
- const binDir = path.join(homeDir, ".local", "bin");
126
+ const binDir = path.join(homeDir, ".ouro-cli", "bin");
93
127
  const scriptPath = path.join(binDir, "ouro");
94
- (0, runtime_1.emitNervesEvent)({
95
- component: "daemon",
96
- event: "daemon.ouro_path_install_start",
97
- message: "installing ouro command to PATH",
98
- meta: { scriptPath, binDir },
99
- });
100
- // If ouro already exists, check content and repair if stale
101
- if (existsSync(scriptPath)) {
102
- let existingContent = "";
128
+ const oldScriptPath = path.join(homeDir, ".local", "bin", "ouro");
129
+ const modernCurrent = isWrapperCurrent(scriptPath, existsSync, readFileSync);
130
+ const oldExists = existsSync(oldScriptPath);
131
+ const oldCurrent = oldExists && isWrapperCurrent(oldScriptPath, existsSync, readFileSync);
132
+ // ── Repair old ~/.local/bin/ouro launcher ──
133
+ // If the old launcher exists with stale content it can shadow the modern
134
+ // path and cause the wrong CLI version to run. Overwrite it with the
135
+ // current wrapper so both paths resolve to ~/.ouro-cli/CurrentVersion.
136
+ let repairedOldLauncher = false;
137
+ if (oldExists && !oldCurrent) {
138
+ (0, runtime_1.emitNervesEvent)({
139
+ component: "daemon",
140
+ event: "daemon.ouro_path_repair_old",
141
+ message: "repairing stale old launcher at ~/.local/bin/ouro",
142
+ meta: { oldScriptPath },
143
+ });
103
144
  try {
104
- existingContent = readFileSync(scriptPath, "utf-8");
145
+ writeFileSync(oldScriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
146
+ chmodSync(oldScriptPath, 0o755);
147
+ repairedOldLauncher = true;
105
148
  }
106
149
  catch {
107
- // Can't read — treat as stale, will overwrite below
108
- }
109
- if (existingContent === WRAPPER_SCRIPT) {
110
- (0, runtime_1.emitNervesEvent)({
111
- component: "daemon",
112
- event: "daemon.ouro_path_install_skip",
113
- message: "ouro command already installed",
114
- meta: { scriptPath },
115
- });
116
- return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed" };
150
+ // Best effort — old launcher repair failure must not block modern install
117
151
  }
118
- // Content is stale — repair by overwriting
152
+ }
153
+ // ── Fast-path: modern wrapper already current ──
154
+ if (modernCurrent) {
119
155
  (0, runtime_1.emitNervesEvent)({
120
156
  component: "daemon",
121
- event: "daemon.ouro_path_install_repair",
122
- message: "repairing stale ouro wrapper script",
157
+ event: "daemon.ouro_path_install_skip",
158
+ message: "ouro command already installed",
123
159
  meta: { scriptPath },
124
160
  });
161
+ return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed", repairedOldLauncher };
125
162
  }
163
+ (0, runtime_1.emitNervesEvent)({
164
+ component: "daemon",
165
+ event: "daemon.ouro_path_install_start",
166
+ message: existsSync(scriptPath) ? "repairing stale ouro wrapper script" : "installing ouro command to PATH",
167
+ meta: { scriptPath, binDir },
168
+ });
126
169
  try {
127
- mkdirSync(binDir, { recursive: true });
128
- writeFileSync(scriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
129
- chmodSync(scriptPath, 0o755);
170
+ if (!modernCurrent) {
171
+ writeWrapperScript(scriptPath, mkdirSync, writeFileSync, chmodSync);
172
+ }
130
173
  }
131
174
  catch (error) {
132
175
  (0, runtime_1.emitNervesEvent)({
@@ -136,11 +179,11 @@ function installOuroCommand(deps = {}) {
136
179
  message: "failed to install ouro command",
137
180
  meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
138
181
  });
139
- return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: error instanceof Error ? error.message : /* v8 ignore next -- defensive @preserve */ String(error) };
182
+ return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: error instanceof Error ? error.message : /* v8 ignore next -- defensive @preserve */ String(error), repairedOldLauncher };
140
183
  }
141
- // Check if ~/.local/bin is already in PATH
142
- let shellProfileUpdated = null;
184
+ // Check if ~/.ouro-cli/bin is already in PATH
143
185
  const pathReady = isBinDirInPath(binDir, envPath);
186
+ let shellProfileUpdated = null;
144
187
  if (!pathReady) {
145
188
  const profilePath = detectShellProfile(homeDir, shell);
146
189
  if (profilePath) {
@@ -172,7 +215,7 @@ function installOuroCommand(deps = {}) {
172
215
  component: "daemon",
173
216
  event: "daemon.ouro_path_install_end",
174
217
  message: "ouro command installed",
175
- meta: { scriptPath, pathReady, shellProfileUpdated },
218
+ meta: { scriptPath, pathReady, shellProfileUpdated, oldScriptPath: oldExists ? oldScriptPath : null },
176
219
  });
177
- return { installed: true, scriptPath, pathReady, shellProfileUpdated };
220
+ return { installed: true, scriptPath, pathReady, shellProfileUpdated, repairedOldLauncher };
178
221
  }
@@ -0,0 +1,171 @@
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.getOuroCliHome = getOuroCliHome;
37
+ exports.getCurrentVersion = getCurrentVersion;
38
+ exports.getPreviousVersion = getPreviousVersion;
39
+ exports.buildChangelogCommand = buildChangelogCommand;
40
+ exports.listInstalledVersions = listInstalledVersions;
41
+ exports.installVersion = installVersion;
42
+ exports.activateVersion = activateVersion;
43
+ exports.ensureLayout = ensureLayout;
44
+ const fs = __importStar(require("fs"));
45
+ const os = __importStar(require("os"));
46
+ const path = __importStar(require("path"));
47
+ const runtime_1 = require("../../nerves/runtime");
48
+ function getOuroCliHome(homeDir) {
49
+ /* v8 ignore next -- dep default: tests always inject @preserve */
50
+ const home = homeDir ?? os.homedir();
51
+ return path.join(home, ".ouro-cli");
52
+ }
53
+ function getCurrentVersion(deps) {
54
+ const cliHome = getOuroCliHome(deps.homeDir);
55
+ /* v8 ignore next -- dep default: tests always inject @preserve */
56
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
57
+ try {
58
+ const target = readlinkSync(path.join(cliHome, "CurrentVersion"));
59
+ return path.basename(target);
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ function getPreviousVersion(deps) {
66
+ const cliHome = getOuroCliHome(deps.homeDir);
67
+ /* v8 ignore next -- dep default: tests always inject @preserve */
68
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
69
+ try {
70
+ const target = readlinkSync(path.join(cliHome, "previous"));
71
+ return path.basename(target);
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ function buildChangelogCommand(previousVersion, currentVersion) {
78
+ if (!previousVersion || !currentVersion || previousVersion === currentVersion) {
79
+ return null;
80
+ }
81
+ return `ouro changelog --from ${previousVersion}`;
82
+ }
83
+ function listInstalledVersions(deps) {
84
+ const cliHome = getOuroCliHome(deps.homeDir);
85
+ /* v8 ignore next -- dep default: tests always inject @preserve */
86
+ const readdirSync = deps.readdirSync ?? ((p, opts) => fs.readdirSync(p, opts));
87
+ try {
88
+ const entries = readdirSync(path.join(cliHome, "versions"), { withFileTypes: true });
89
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
90
+ }
91
+ catch {
92
+ return [];
93
+ }
94
+ }
95
+ function installVersion(version, deps) {
96
+ const cliHome = getOuroCliHome(deps.homeDir);
97
+ /* v8 ignore start -- dep defaults: tests always inject @preserve */
98
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
99
+ const execSync = deps.execSync ?? ((cmd, opts) => require("child_process").execSync(cmd, opts));
100
+ /* v8 ignore stop */
101
+ const versionDir = path.join(cliHome, "versions", version);
102
+ (0, runtime_1.emitNervesEvent)({
103
+ component: "daemon",
104
+ event: "daemon.cli_version_install_start",
105
+ message: "installing CLI version",
106
+ meta: { version, versionDir },
107
+ });
108
+ mkdirSync(versionDir, { recursive: true });
109
+ execSync(`npm install --prefix ${versionDir} @ouro.bot/cli@${version}`, { stdio: "pipe" });
110
+ (0, runtime_1.emitNervesEvent)({
111
+ component: "daemon",
112
+ event: "daemon.cli_version_install_end",
113
+ message: "CLI version installed",
114
+ meta: { version, versionDir },
115
+ });
116
+ }
117
+ function activateVersion(version, deps) {
118
+ const cliHome = getOuroCliHome(deps.homeDir);
119
+ /* v8 ignore start -- dep defaults: tests always inject @preserve */
120
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
121
+ const unlinkSync = deps.unlinkSync ?? fs.unlinkSync;
122
+ const symlinkSync = deps.symlinkSync ?? fs.symlinkSync;
123
+ const existsSync = deps.existsSync ?? fs.existsSync;
124
+ /* v8 ignore stop */
125
+ const currentVersionPath = path.join(cliHome, "CurrentVersion");
126
+ const previousPath = path.join(cliHome, "previous");
127
+ const newTarget = path.join(cliHome, "versions", version);
128
+ (0, runtime_1.emitNervesEvent)({
129
+ component: "daemon",
130
+ event: "daemon.cli_version_activate",
131
+ message: "activating CLI version",
132
+ meta: { version },
133
+ });
134
+ // Read old CurrentVersion target (may not exist)
135
+ let oldTarget = null;
136
+ try {
137
+ oldTarget = readlinkSync(currentVersionPath);
138
+ }
139
+ catch {
140
+ // No current version — first install
141
+ }
142
+ // Update previous symlink to point to old current
143
+ if (oldTarget) {
144
+ try {
145
+ unlinkSync(previousPath);
146
+ }
147
+ catch {
148
+ // previous symlink may not exist yet
149
+ }
150
+ symlinkSync(oldTarget, previousPath);
151
+ }
152
+ // Update CurrentVersion symlink
153
+ if (existsSync(currentVersionPath)) {
154
+ unlinkSync(currentVersionPath);
155
+ }
156
+ symlinkSync(newTarget, currentVersionPath);
157
+ }
158
+ function ensureLayout(deps) {
159
+ const cliHome = getOuroCliHome(deps.homeDir);
160
+ /* v8 ignore next -- dep default: tests always inject @preserve */
161
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
162
+ mkdirSync(cliHome, { recursive: true });
163
+ mkdirSync(path.join(cliHome, "bin"), { recursive: true });
164
+ mkdirSync(path.join(cliHome, "versions"), { recursive: true });
165
+ (0, runtime_1.emitNervesEvent)({
166
+ component: "daemon",
167
+ event: "daemon.cli_layout_ensured",
168
+ message: "CLI directory layout ensured",
169
+ meta: { cliHome },
170
+ });
171
+ }
@@ -0,0 +1,138 @@
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.performStagedRestart = performStagedRestart;
37
+ const path = __importStar(require("path"));
38
+ const runtime_1 = require("../../nerves/runtime");
39
+ async function performStagedRestart(version, deps) {
40
+ (0, runtime_1.emitNervesEvent)({
41
+ component: "daemon",
42
+ event: "daemon.staged_restart_start",
43
+ message: "starting staged restart",
44
+ meta: { version },
45
+ });
46
+ // Step 1: Install new version
47
+ try {
48
+ deps.execSync(`npm install -g @ouro.bot/cli@${version}`);
49
+ }
50
+ catch (err) {
51
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
52
+ (0, runtime_1.emitNervesEvent)({
53
+ component: "daemon",
54
+ event: "daemon.staged_restart_install_failed",
55
+ message: "npm install failed",
56
+ meta: { version, error: errorMessage },
57
+ });
58
+ return { ok: false, error: errorMessage };
59
+ }
60
+ // Step 2: Resolve new code path
61
+ const newCodePath = deps.resolveNewCodePath(version);
62
+ if (!newCodePath) {
63
+ (0, runtime_1.emitNervesEvent)({
64
+ component: "daemon",
65
+ event: "daemon.staged_restart_path_failed",
66
+ message: "could not resolve new code path",
67
+ meta: { version },
68
+ });
69
+ return { ok: false, error: "could not resolve new code path after install" };
70
+ }
71
+ // Step 3: Spawn hook runner on NEW code
72
+ const hookRunnerPath = path.join(newCodePath, "dist", "heart", "daemon", "run-hooks.js");
73
+ const spawnResult = deps.spawnSync(deps.nodePath, [hookRunnerPath, "--bundles-root", deps.bundlesRoot], { stdio: "inherit" });
74
+ if (spawnResult.error) {
75
+ const errorMessage = spawnResult.error.message;
76
+ (0, runtime_1.emitNervesEvent)({
77
+ component: "daemon",
78
+ event: "daemon.staged_restart_spawn_failed",
79
+ message: "hook runner spawn failed",
80
+ meta: { version, error: errorMessage },
81
+ });
82
+ return { ok: false, error: errorMessage };
83
+ }
84
+ if (spawnResult.status !== 0) {
85
+ (0, runtime_1.emitNervesEvent)({
86
+ component: "daemon",
87
+ event: "daemon.staged_restart_hooks_failed",
88
+ message: "hook runner exited with non-zero status",
89
+ meta: { version, exitCode: spawnResult.status },
90
+ });
91
+ return { ok: false, error: `hook runner exited with code ${spawnResult.status}` };
92
+ }
93
+ // Step 4: Graceful shutdown then self-spawn new daemon
94
+ // We can't rely on launchd KeepAlive — the plist may not be loaded.
95
+ // Instead: shut down (releases socket), spawn new daemon from updated code.
96
+ (0, runtime_1.emitNervesEvent)({
97
+ component: "daemon",
98
+ event: "daemon.staged_restart_hooks_passed",
99
+ message: "hooks passed, shutting down and spawning new daemon",
100
+ meta: { version },
101
+ });
102
+ let shutdownError;
103
+ try {
104
+ await deps.gracefulShutdown();
105
+ }
106
+ catch (err) {
107
+ shutdownError = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
108
+ (0, runtime_1.emitNervesEvent)({
109
+ component: "daemon",
110
+ event: "daemon.staged_restart_shutdown_error",
111
+ message: "graceful shutdown encountered error (continuing with spawn)",
112
+ meta: { version, error: shutdownError },
113
+ });
114
+ }
115
+ // Spawn new daemon from updated code path
116
+ const newEntry = path.join(newCodePath, "dist", "heart", "daemon", "daemon-entry.js");
117
+ const socketArg = deps.socketPath ?? "/tmp/ouroboros-daemon.sock";
118
+ try {
119
+ const { pid } = deps.spawnNewDaemon(newEntry, socketArg);
120
+ (0, runtime_1.emitNervesEvent)({
121
+ component: "daemon",
122
+ event: "daemon.staged_restart_spawned",
123
+ message: "new daemon spawned successfully",
124
+ meta: { version, pid, entry: newEntry },
125
+ });
126
+ }
127
+ catch (err) {
128
+ const spawnError = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
129
+ (0, runtime_1.emitNervesEvent)({
130
+ component: "daemon",
131
+ event: "daemon.staged_restart_respawn_failed",
132
+ message: "failed to spawn new daemon after shutdown",
133
+ meta: { version, error: spawnError, entry: newEntry },
134
+ });
135
+ return { ok: false, error: `shutdown succeeded but failed to spawn new daemon: ${spawnError}`, shutdownError };
136
+ }
137
+ return { ok: true, shutdownError };
138
+ }
@@ -0,0 +1,111 @@
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.checkForUpdate = checkForUpdate;
37
+ exports.startUpdateChecker = startUpdateChecker;
38
+ exports.stopUpdateChecker = stopUpdateChecker;
39
+ const semver = __importStar(require("semver"));
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ const DEFAULT_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
42
+ async function checkForUpdate(currentVersion, deps) {
43
+ (0, runtime_1.emitNervesEvent)({
44
+ component: "daemon",
45
+ event: "daemon.update_check",
46
+ message: "checking for update",
47
+ meta: { currentVersion, distTag: deps.distTag },
48
+ });
49
+ let registryData;
50
+ try {
51
+ registryData = (await deps.fetchRegistryJson());
52
+ }
53
+ catch (err) {
54
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
55
+ return { available: false, error: errorMessage };
56
+ }
57
+ const distTags = registryData?.["dist-tags"];
58
+ if (!distTags) {
59
+ return { available: false, error: "registry response missing dist-tags" };
60
+ }
61
+ const latestVersion = distTags[deps.distTag];
62
+ if (!latestVersion) {
63
+ return { available: false, error: `dist-tag "${deps.distTag}" not found in registry` };
64
+ }
65
+ const available = semver.gt(latestVersion, currentVersion);
66
+ (0, runtime_1.emitNervesEvent)({
67
+ component: "daemon",
68
+ event: "daemon.update_check_result",
69
+ message: available ? "update available" : "no update available",
70
+ meta: { currentVersion, latestVersion, available },
71
+ });
72
+ return { available, latestVersion };
73
+ }
74
+ let _intervalId = null;
75
+ function startUpdateChecker(options) {
76
+ const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
77
+ (0, runtime_1.emitNervesEvent)({
78
+ component: "daemon",
79
+ event: "daemon.update_checker_start",
80
+ message: "starting update checker",
81
+ meta: { intervalMs, currentVersion: options.currentVersion },
82
+ });
83
+ _intervalId = setInterval(() => {
84
+ void (async () => {
85
+ const result = await checkForUpdate(options.currentVersion, options.deps);
86
+ if (result.available && options.onUpdate) {
87
+ await options.onUpdate(result);
88
+ }
89
+ })().catch((err) => {
90
+ (0, runtime_1.emitNervesEvent)({
91
+ component: "daemon",
92
+ event: "daemon.update_checker_error",
93
+ level: "warn",
94
+ message: "update checker tick failed",
95
+ meta: { reason: err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err) },
96
+ });
97
+ });
98
+ }, intervalMs);
99
+ }
100
+ function stopUpdateChecker() {
101
+ if (_intervalId) {
102
+ clearInterval(_intervalId);
103
+ _intervalId = null;
104
+ }
105
+ (0, runtime_1.emitNervesEvent)({
106
+ component: "daemon",
107
+ event: "daemon.update_checker_stop",
108
+ message: "stopping update checker",
109
+ meta: {},
110
+ });
111
+ }