@ouro.bot/cli 0.1.0-alpha.8 → 0.1.0-alpha.80

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 (127) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +462 -0
  7. package/dist/heart/active-work.js +218 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/commitments.js +89 -0
  12. package/dist/heart/config.js +68 -23
  13. package/dist/heart/core.js +452 -93
  14. package/dist/heart/cross-chat-delivery.js +146 -0
  15. package/dist/heart/daemon/agent-discovery.js +81 -0
  16. package/dist/heart/daemon/auth-flow.js +430 -0
  17. package/dist/heart/daemon/daemon-cli.js +1746 -247
  18. package/dist/heart/daemon/daemon-entry.js +55 -6
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon.js +216 -10
  21. package/dist/heart/daemon/hatch-animation.js +10 -3
  22. package/dist/heart/daemon/hatch-flow.js +7 -82
  23. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  24. package/dist/heart/daemon/launchd.js +159 -0
  25. package/dist/heart/daemon/log-tailer.js +4 -3
  26. package/dist/heart/daemon/message-router.js +17 -8
  27. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  28. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  29. package/dist/heart/daemon/ouro-entry.js +0 -0
  30. package/dist/heart/daemon/ouro-path-installer.js +260 -0
  31. package/dist/heart/daemon/ouro-uti.js +11 -2
  32. package/dist/heart/daemon/ouro-version-manager.js +164 -0
  33. package/dist/heart/daemon/process-manager.js +14 -1
  34. package/dist/heart/daemon/run-hooks.js +37 -0
  35. package/dist/heart/daemon/runtime-logging.js +58 -15
  36. package/dist/heart/daemon/runtime-metadata.js +219 -0
  37. package/dist/heart/daemon/runtime-mode.js +67 -0
  38. package/dist/heart/daemon/sense-manager.js +307 -0
  39. package/dist/heart/daemon/skill-management-installer.js +94 -0
  40. package/dist/heart/daemon/socket-client.js +202 -0
  41. package/dist/heart/daemon/specialist-orchestrator.js +53 -84
  42. package/dist/heart/daemon/specialist-prompt.js +63 -11
  43. package/dist/heart/daemon/specialist-tools.js +211 -60
  44. package/dist/heart/daemon/staged-restart.js +114 -0
  45. package/dist/heart/daemon/thoughts.js +507 -0
  46. package/dist/heart/daemon/update-checker.js +111 -0
  47. package/dist/heart/daemon/update-hooks.js +138 -0
  48. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  49. package/dist/heart/delegation.js +62 -0
  50. package/dist/heart/identity.js +126 -21
  51. package/dist/heart/kicks.js +1 -19
  52. package/dist/heart/model-capabilities.js +48 -0
  53. package/dist/heart/obligations.js +141 -0
  54. package/dist/heart/progress-story.js +42 -0
  55. package/dist/heart/providers/anthropic.js +74 -9
  56. package/dist/heart/providers/azure.js +86 -7
  57. package/dist/heart/providers/github-copilot.js +149 -0
  58. package/dist/heart/providers/minimax.js +4 -0
  59. package/dist/heart/providers/openai-codex.js +12 -3
  60. package/dist/heart/safe-workspace.js +228 -0
  61. package/dist/heart/sense-truth.js +61 -0
  62. package/dist/heart/session-activity.js +169 -0
  63. package/dist/heart/session-recall.js +116 -0
  64. package/dist/heart/streaming.js +100 -22
  65. package/dist/heart/target-resolution.js +123 -0
  66. package/dist/heart/turn-coordinator.js +28 -0
  67. package/dist/mind/associative-recall.js +14 -2
  68. package/dist/mind/bundle-manifest.js +70 -0
  69. package/dist/mind/context.js +27 -11
  70. package/dist/mind/first-impressions.js +16 -2
  71. package/dist/mind/friends/channel.js +35 -0
  72. package/dist/mind/friends/group-context.js +144 -0
  73. package/dist/mind/friends/store-file.js +19 -0
  74. package/dist/mind/friends/trust-explanation.js +74 -0
  75. package/dist/mind/friends/types.js +8 -0
  76. package/dist/mind/memory.js +27 -26
  77. package/dist/mind/pending.js +76 -9
  78. package/dist/mind/phrases.js +1 -0
  79. package/dist/mind/prompt.js +445 -77
  80. package/dist/mind/token-estimate.js +8 -12
  81. package/dist/nerves/cli-logging.js +15 -2
  82. package/dist/nerves/coverage/run-artifacts.js +1 -1
  83. package/dist/nerves/index.js +12 -0
  84. package/dist/repertoire/ado-client.js +4 -2
  85. package/dist/repertoire/coding/feedback.js +134 -0
  86. package/dist/repertoire/coding/index.js +4 -1
  87. package/dist/repertoire/coding/manager.js +62 -4
  88. package/dist/repertoire/coding/spawner.js +3 -3
  89. package/dist/repertoire/coding/tools.js +41 -2
  90. package/dist/repertoire/data/ado-endpoints.json +188 -0
  91. package/dist/repertoire/guardrails.js +290 -0
  92. package/dist/repertoire/mcp-client.js +254 -0
  93. package/dist/repertoire/mcp-manager.js +195 -0
  94. package/dist/repertoire/skills.js +3 -26
  95. package/dist/repertoire/tasks/board.js +12 -0
  96. package/dist/repertoire/tasks/index.js +23 -9
  97. package/dist/repertoire/tasks/transitions.js +1 -2
  98. package/dist/repertoire/tools-base.js +686 -251
  99. package/dist/repertoire/tools-bluebubbles.js +93 -0
  100. package/dist/repertoire/tools-teams.js +58 -25
  101. package/dist/repertoire/tools.js +95 -53
  102. package/dist/senses/bluebubbles-client.js +210 -5
  103. package/dist/senses/bluebubbles-entry.js +2 -0
  104. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  105. package/dist/senses/bluebubbles-media.js +339 -0
  106. package/dist/senses/bluebubbles-model.js +12 -4
  107. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  108. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  109. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  110. package/dist/senses/bluebubbles.js +894 -45
  111. package/dist/senses/cli-layout.js +187 -0
  112. package/dist/senses/cli.js +405 -156
  113. package/dist/senses/continuity.js +94 -0
  114. package/dist/senses/debug-activity.js +154 -0
  115. package/dist/senses/inner-dialog-worker.js +47 -18
  116. package/dist/senses/inner-dialog.js +377 -83
  117. package/dist/senses/pipeline.js +307 -0
  118. package/dist/senses/teams.js +573 -129
  119. package/dist/senses/trust-gate.js +112 -2
  120. package/package.json +14 -3
  121. package/subagents/README.md +4 -70
  122. package/dist/heart/daemon/specialist-session.js +0 -142
  123. package/dist/heart/daemon/subagent-installer.js +0 -125
  124. package/dist/inner-worker-entry.js +0 -4
  125. package/subagents/work-doer.md +0 -233
  126. package/subagents/work-merger.md +0 -624
  127. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,260 @@
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.installOuroCommand = installOuroCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ const WRAPPER_SCRIPT = `#!/bin/sh
42
+ ENTRY="$HOME/.ouro-cli/CurrentVersion/node_modules/@ouro.bot/cli/dist/heart/daemon/ouro-entry.js"
43
+ if [ ! -e "$ENTRY" ]; then
44
+ echo "ouro not installed. Run: npx ouro.bot" >&2
45
+ exit 1
46
+ fi
47
+ exec node "$ENTRY" "$@"
48
+ `;
49
+ function detectShellProfile(homeDir, shell) {
50
+ if (!shell)
51
+ return null;
52
+ const base = path.basename(shell);
53
+ if (base === "zsh")
54
+ return path.join(homeDir, ".zshrc");
55
+ if (base === "bash") {
56
+ // macOS uses .bash_profile, Linux uses .bashrc
57
+ const profilePath = path.join(homeDir, ".bash_profile");
58
+ return profilePath;
59
+ }
60
+ if (base === "fish")
61
+ return path.join(homeDir, ".config", "fish", "config.fish");
62
+ return null;
63
+ }
64
+ function isBinDirInPath(binDir, envPath) {
65
+ return envPath.split(path.delimiter).some((p) => p === binDir);
66
+ }
67
+ function buildPathExportLine(binDir, shell) {
68
+ const base = shell ? path.basename(shell) : /* v8 ignore next -- unreachable: only called when detectShellProfile returns non-null, which requires shell @preserve */ "";
69
+ if (base === "fish") {
70
+ return `\n# Added by ouro\nset -gx PATH ${binDir} $PATH\n`;
71
+ }
72
+ return `\n# Added by ouro\nexport PATH="${binDir}:$PATH"\n`;
73
+ }
74
+ /**
75
+ * Remove lines matching the old ouro PATH block from shell profile content.
76
+ * Returns the cleaned content.
77
+ */
78
+ function removeOldPathBlock(content, oldBinDir) {
79
+ const lines = content.split("\n");
80
+ const result = [];
81
+ let i = 0;
82
+ while (i < lines.length) {
83
+ // Detect "# Added by ouro" followed by a PATH export containing the old binDir
84
+ if (lines[i].trim() === "# Added by ouro" && i + 1 < lines.length && lines[i + 1].includes(oldBinDir)) {
85
+ // Skip both lines (comment + export)
86
+ i += 2;
87
+ // Also skip trailing blank line if present
88
+ /* v8 ignore next -- edge: trailing blank line presence varies @preserve */
89
+ if (i < lines.length && lines[i].trim() === "")
90
+ i++;
91
+ continue;
92
+ }
93
+ result.push(lines[i]);
94
+ i++;
95
+ }
96
+ return result.join("\n");
97
+ }
98
+ function installOuroCommand(deps = {}) {
99
+ /* v8 ignore start -- dep defaults: only used in real runtime, tests always inject @preserve */
100
+ const platform = deps.platform ?? process.platform;
101
+ const homeDir = deps.homeDir ?? os.homedir();
102
+ const existsSync = deps.existsSync ?? fs.existsSync;
103
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
104
+ const writeFileSync = deps.writeFileSync ?? fs.writeFileSync;
105
+ const readFileSync = deps.readFileSync ?? ((p, enc) => fs.readFileSync(p, enc));
106
+ const appendFileSync = deps.appendFileSync ?? fs.appendFileSync;
107
+ const chmodSync = deps.chmodSync ?? fs.chmodSync;
108
+ const unlinkSync = deps.unlinkSync ?? fs.unlinkSync;
109
+ const rmdirSync = deps.rmdirSync ?? fs.rmdirSync;
110
+ const readdirSync = deps.readdirSync ?? ((p) => fs.readdirSync(p).map(String));
111
+ const envPath = deps.envPath ?? process.env.PATH ?? "";
112
+ const shell = deps.shell ?? process.env.SHELL;
113
+ /* v8 ignore stop */
114
+ if (platform === "win32") {
115
+ (0, runtime_1.emitNervesEvent)({
116
+ component: "daemon",
117
+ event: "daemon.ouro_path_install_skip",
118
+ message: "skipped ouro PATH install on Windows",
119
+ meta: { platform },
120
+ });
121
+ return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: "windows", migratedFromOldPath: false };
122
+ }
123
+ // Ensure ~/.ouro-cli/ directory layout exists
124
+ if (deps.ensureCliLayout) {
125
+ deps.ensureCliLayout();
126
+ }
127
+ const binDir = path.join(homeDir, ".ouro-cli", "bin");
128
+ const scriptPath = path.join(binDir, "ouro");
129
+ // ── Migration from old ~/.local/bin/ouro ──
130
+ const oldBinDir = path.join(homeDir, ".local", "bin");
131
+ const oldScriptPath = path.join(oldBinDir, "ouro");
132
+ let migratedFromOldPath = false;
133
+ if (existsSync(oldScriptPath)) {
134
+ (0, runtime_1.emitNervesEvent)({
135
+ component: "daemon",
136
+ event: "daemon.ouro_path_migrate_start",
137
+ message: "migrating ouro from old PATH location",
138
+ meta: { oldScriptPath },
139
+ });
140
+ try {
141
+ unlinkSync(oldScriptPath);
142
+ migratedFromOldPath = true;
143
+ // Remove empty ~/.local/bin/ directory
144
+ if (existsSync(oldBinDir)) {
145
+ try {
146
+ const remaining = readdirSync(oldBinDir);
147
+ if (remaining.length === 0) {
148
+ rmdirSync(oldBinDir);
149
+ }
150
+ }
151
+ catch {
152
+ // Best effort cleanup
153
+ }
154
+ }
155
+ }
156
+ catch {
157
+ // Best effort migration — continue with new install
158
+ }
159
+ // Remove old PATH entry from shell profile
160
+ const profilePath = detectShellProfile(homeDir, shell);
161
+ /* v8 ignore start -- profile cleanup: only fires during migration from old layout @preserve */
162
+ if (profilePath) {
163
+ try {
164
+ const profileContent = readFileSync(profilePath, "utf-8");
165
+ if (profileContent.includes(oldBinDir)) {
166
+ const cleaned = removeOldPathBlock(profileContent, oldBinDir);
167
+ writeFileSync(profilePath, cleaned);
168
+ }
169
+ }
170
+ catch {
171
+ // Best effort profile cleanup
172
+ }
173
+ }
174
+ /* v8 ignore stop */
175
+ }
176
+ (0, runtime_1.emitNervesEvent)({
177
+ component: "daemon",
178
+ event: "daemon.ouro_path_install_start",
179
+ message: "installing ouro command to PATH",
180
+ meta: { scriptPath, binDir },
181
+ });
182
+ // If ouro already exists, check content and repair if stale
183
+ if (existsSync(scriptPath)) {
184
+ let existingContent = "";
185
+ try {
186
+ existingContent = readFileSync(scriptPath, "utf-8");
187
+ }
188
+ catch {
189
+ // Can't read — treat as stale, will overwrite below
190
+ }
191
+ if (existingContent === WRAPPER_SCRIPT) {
192
+ (0, runtime_1.emitNervesEvent)({
193
+ component: "daemon",
194
+ event: "daemon.ouro_path_install_skip",
195
+ message: "ouro command already installed",
196
+ meta: { scriptPath },
197
+ });
198
+ return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed", migratedFromOldPath };
199
+ }
200
+ // Content is stale — repair by overwriting
201
+ (0, runtime_1.emitNervesEvent)({
202
+ component: "daemon",
203
+ event: "daemon.ouro_path_install_repair",
204
+ message: "repairing stale ouro wrapper script",
205
+ meta: { scriptPath },
206
+ });
207
+ }
208
+ try {
209
+ mkdirSync(binDir, { recursive: true });
210
+ writeFileSync(scriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
211
+ chmodSync(scriptPath, 0o755);
212
+ }
213
+ catch (error) {
214
+ (0, runtime_1.emitNervesEvent)({
215
+ level: "warn",
216
+ component: "daemon",
217
+ event: "daemon.ouro_path_install_error",
218
+ message: "failed to install ouro command",
219
+ meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
220
+ });
221
+ return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: error instanceof Error ? error.message : /* v8 ignore next -- defensive @preserve */ String(error), migratedFromOldPath };
222
+ }
223
+ // Check if ~/.ouro-cli/bin is already in PATH
224
+ let shellProfileUpdated = null;
225
+ const pathReady = isBinDirInPath(binDir, envPath);
226
+ if (!pathReady) {
227
+ const profilePath = detectShellProfile(homeDir, shell);
228
+ if (profilePath) {
229
+ try {
230
+ let existing = "";
231
+ try {
232
+ existing = readFileSync(profilePath, "utf-8");
233
+ }
234
+ catch {
235
+ // Profile doesn't exist yet — that's fine, we'll create it
236
+ }
237
+ if (!existing.includes(binDir)) {
238
+ appendFileSync(profilePath, buildPathExportLine(binDir, shell));
239
+ shellProfileUpdated = profilePath;
240
+ }
241
+ }
242
+ catch (error) {
243
+ (0, runtime_1.emitNervesEvent)({
244
+ level: "warn",
245
+ component: "daemon",
246
+ event: "daemon.ouro_path_profile_error",
247
+ message: "failed to update shell profile for PATH",
248
+ meta: { profilePath, error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
249
+ });
250
+ }
251
+ }
252
+ }
253
+ (0, runtime_1.emitNervesEvent)({
254
+ component: "daemon",
255
+ event: "daemon.ouro_path_install_end",
256
+ message: "ouro command installed",
257
+ meta: { scriptPath, pathReady, shellProfileUpdated },
258
+ });
259
+ return { installed: true, scriptPath, pathReady, shellProfileUpdated, migratedFromOldPath };
260
+ }
@@ -42,7 +42,13 @@ const identity_1 = require("../identity");
42
42
  const runtime_1 = require("../../nerves/runtime");
43
43
  const LSREGISTER_PATH = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
44
44
  const ICON_SIZES = [16, 32, 128, 256, 512];
45
- function resolveIconSourcePath(repoRoot) {
45
+ function resolveIconSourcePath(repoRoot, existsSync) {
46
+ // Prefer bundled asset (shipped with npm package)
47
+ const bundledPath = path.resolve(repoRoot, "assets", "ouroboros.png");
48
+ if (existsSync(bundledPath)) {
49
+ return bundledPath;
50
+ }
51
+ // Fall back to adjacent repo path (dev environment)
46
52
  return path.resolve(repoRoot, "..", "ouroboros-website", "public", "images", "ouroboros.png");
47
53
  }
48
54
  function buildIconAsset(iconSourcePath, icnsPath, iconsetDir, deps) {
@@ -91,6 +97,7 @@ function buildInfoPlist(iconInstalled) {
91
97
  " <key>UTTypeConformsTo</key>",
92
98
  " <array>",
93
99
  " <string>public.folder</string>",
100
+ " <string>com.apple.package</string>",
94
101
  " </array>",
95
102
  " <key>UTTypeTagSpecification</key>",
96
103
  " <dict>",
@@ -112,6 +119,8 @@ function buildInfoPlist(iconInstalled) {
112
119
  " </array>",
113
120
  " <key>CFBundleTypeRole</key>",
114
121
  " <string>Editor</string>",
122
+ " <key>LSTypeIsPackage</key>",
123
+ " <true/>",
115
124
  ` ${iconTag.trim()}`,
116
125
  " </dict>",
117
126
  " </array>",
@@ -152,7 +161,7 @@ function registerOuroBundleUti(deps = {}) {
152
161
  const plistPath = path.join(contentsDir, "Info.plist");
153
162
  const icnsPath = path.join(resourcesDir, "ouro.icns");
154
163
  const iconsetDir = path.join(supportRoot, "ouro.iconset");
155
- const iconSourcePath = resolveIconSourcePath(repoRoot);
164
+ const iconSourcePath = resolveIconSourcePath(repoRoot, existsSync);
156
165
  (0, runtime_1.emitNervesEvent)({
157
166
  component: "daemon",
158
167
  event: "daemon.ouro_uti_register_start",
@@ -0,0 +1,164 @@
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.listInstalledVersions = listInstalledVersions;
40
+ exports.installVersion = installVersion;
41
+ exports.activateVersion = activateVersion;
42
+ exports.ensureLayout = ensureLayout;
43
+ const fs = __importStar(require("fs"));
44
+ const os = __importStar(require("os"));
45
+ const path = __importStar(require("path"));
46
+ const runtime_1 = require("../../nerves/runtime");
47
+ function getOuroCliHome(homeDir) {
48
+ /* v8 ignore next -- dep default: tests always inject @preserve */
49
+ const home = homeDir ?? os.homedir();
50
+ return path.join(home, ".ouro-cli");
51
+ }
52
+ function getCurrentVersion(deps) {
53
+ const cliHome = getOuroCliHome(deps.homeDir);
54
+ /* v8 ignore next -- dep default: tests always inject @preserve */
55
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
56
+ try {
57
+ const target = readlinkSync(path.join(cliHome, "CurrentVersion"));
58
+ return path.basename(target);
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ function getPreviousVersion(deps) {
65
+ const cliHome = getOuroCliHome(deps.homeDir);
66
+ /* v8 ignore next -- dep default: tests always inject @preserve */
67
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
68
+ try {
69
+ const target = readlinkSync(path.join(cliHome, "previous"));
70
+ return path.basename(target);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ function listInstalledVersions(deps) {
77
+ const cliHome = getOuroCliHome(deps.homeDir);
78
+ /* v8 ignore next -- dep default: tests always inject @preserve */
79
+ const readdirSync = deps.readdirSync ?? ((p, opts) => fs.readdirSync(p, opts));
80
+ try {
81
+ const entries = readdirSync(path.join(cliHome, "versions"), { withFileTypes: true });
82
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ function installVersion(version, deps) {
89
+ const cliHome = getOuroCliHome(deps.homeDir);
90
+ /* v8 ignore start -- dep defaults: tests always inject @preserve */
91
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
92
+ const execSync = deps.execSync ?? ((cmd, opts) => require("child_process").execSync(cmd, opts));
93
+ /* v8 ignore stop */
94
+ const versionDir = path.join(cliHome, "versions", version);
95
+ (0, runtime_1.emitNervesEvent)({
96
+ component: "daemon",
97
+ event: "daemon.cli_version_install_start",
98
+ message: "installing CLI version",
99
+ meta: { version, versionDir },
100
+ });
101
+ mkdirSync(versionDir, { recursive: true });
102
+ execSync(`npm install --prefix ${versionDir} @ouro.bot/cli@${version}`, { stdio: "pipe" });
103
+ (0, runtime_1.emitNervesEvent)({
104
+ component: "daemon",
105
+ event: "daemon.cli_version_install_end",
106
+ message: "CLI version installed",
107
+ meta: { version, versionDir },
108
+ });
109
+ }
110
+ function activateVersion(version, deps) {
111
+ const cliHome = getOuroCliHome(deps.homeDir);
112
+ /* v8 ignore start -- dep defaults: tests always inject @preserve */
113
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
114
+ const unlinkSync = deps.unlinkSync ?? fs.unlinkSync;
115
+ const symlinkSync = deps.symlinkSync ?? fs.symlinkSync;
116
+ const existsSync = deps.existsSync ?? fs.existsSync;
117
+ /* v8 ignore stop */
118
+ const currentVersionPath = path.join(cliHome, "CurrentVersion");
119
+ const previousPath = path.join(cliHome, "previous");
120
+ const newTarget = path.join(cliHome, "versions", version);
121
+ (0, runtime_1.emitNervesEvent)({
122
+ component: "daemon",
123
+ event: "daemon.cli_version_activate",
124
+ message: "activating CLI version",
125
+ meta: { version },
126
+ });
127
+ // Read old CurrentVersion target (may not exist)
128
+ let oldTarget = null;
129
+ try {
130
+ oldTarget = readlinkSync(currentVersionPath);
131
+ }
132
+ catch {
133
+ // No current version — first install
134
+ }
135
+ // Update previous symlink to point to old current
136
+ if (oldTarget) {
137
+ try {
138
+ unlinkSync(previousPath);
139
+ }
140
+ catch {
141
+ // previous symlink may not exist yet
142
+ }
143
+ symlinkSync(oldTarget, previousPath);
144
+ }
145
+ // Update CurrentVersion symlink
146
+ if (existsSync(currentVersionPath)) {
147
+ unlinkSync(currentVersionPath);
148
+ }
149
+ symlinkSync(newTarget, currentVersionPath);
150
+ }
151
+ function ensureLayout(deps) {
152
+ const cliHome = getOuroCliHome(deps.homeDir);
153
+ /* v8 ignore next -- dep default: tests always inject @preserve */
154
+ const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
155
+ mkdirSync(cliHome, { recursive: true });
156
+ mkdirSync(path.join(cliHome, "bin"), { recursive: true });
157
+ mkdirSync(path.join(cliHome, "versions"), { recursive: true });
158
+ (0, runtime_1.emitNervesEvent)({
159
+ component: "daemon",
160
+ event: "daemon.cli_layout_ensured",
161
+ message: "CLI directory layout ensured",
162
+ meta: { cliHome },
163
+ });
164
+ }
@@ -51,6 +51,7 @@ class DaemonProcessManager {
51
51
  now;
52
52
  setTimeoutFn;
53
53
  clearTimeoutFn;
54
+ existsSyncFn;
54
55
  constructor(options) {
55
56
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
56
57
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
@@ -60,6 +61,7 @@ class DaemonProcessManager {
60
61
  this.now = options.now ?? (() => Date.now());
61
62
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
62
63
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
64
+ this.existsSyncFn = options.existsSync ?? null;
63
65
  for (const agent of options.agents) {
64
66
  this.agents.set(agent.name, {
65
67
  config: agent,
@@ -96,7 +98,18 @@ class DaemonProcessManager {
96
98
  state.snapshot.status = "starting";
97
99
  const runCwd = (0, identity_1.getRepoRoot)();
98
100
  const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
99
- const args = [entryScript, "--agent", agent, ...(state.config.args ?? [])];
101
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
102
+ state.snapshot.status = "crashed";
103
+ (0, runtime_1.emitNervesEvent)({
104
+ level: "error",
105
+ component: "daemon",
106
+ event: "daemon.agent_entry_missing",
107
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
108
+ meta: { agent, entryScript },
109
+ });
110
+ return;
111
+ }
112
+ const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
100
113
  const child = this.spawnFn("node", args, {
101
114
  cwd: runCwd,
102
115
  env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runHooks = runHooks;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ const bundle_meta_1 = require("./hooks/bundle-meta");
6
+ async function runHooks(deps) {
7
+ (0, runtime_1.emitNervesEvent)({
8
+ component: "daemon",
9
+ event: "daemon.run_hooks_start",
10
+ message: "running update hooks",
11
+ meta: { bundlesRoot: deps.bundlesRoot },
12
+ });
13
+ try {
14
+ deps.registerUpdateHook(bundle_meta_1.bundleMetaHook);
15
+ const currentVersion = deps.getPackageVersion();
16
+ await deps.applyPendingUpdates(deps.bundlesRoot, currentVersion);
17
+ (0, runtime_1.emitNervesEvent)({
18
+ component: "daemon",
19
+ event: "daemon.run_hooks_success",
20
+ message: "update hooks completed successfully",
21
+ meta: { bundlesRoot: deps.bundlesRoot },
22
+ });
23
+ return 0;
24
+ }
25
+ catch (err) {
26
+ (0, runtime_1.emitNervesEvent)({
27
+ component: "daemon",
28
+ event: "daemon.run_hooks_error",
29
+ message: "update hooks failed",
30
+ meta: {
31
+ bundlesRoot: deps.bundlesRoot,
32
+ error: err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err),
33
+ },
34
+ });
35
+ return 1;
36
+ }
37
+ }
@@ -35,52 +35,95 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.configureDaemonRuntimeLogger = configureDaemonRuntimeLogger;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const nerves_1 = require("../../nerves");
41
40
  const runtime_1 = require("../../nerves/runtime");
42
- const DEFAULT_RUNTIME_LOGGING = {
41
+ const identity_1 = require("../identity");
42
+ const LEGACY_SHARED_RUNTIME_LOGGING = {
43
43
  level: "info",
44
44
  sinks: ["terminal", "ndjson"],
45
45
  };
46
- function defaultLevelForProcess(processName) {
47
- return processName === "daemon" ? "info" : "warn";
46
+ function defaultLoggingForProcess(processName) {
47
+ if (processName === "ouro" || processName === "ouro-bot") {
48
+ return {
49
+ level: "info",
50
+ sinks: ["ndjson"],
51
+ };
52
+ }
53
+ if (processName === "bluebubbles") {
54
+ return {
55
+ level: "warn",
56
+ sinks: ["terminal", "ndjson"],
57
+ };
58
+ }
59
+ return { ...LEGACY_SHARED_RUNTIME_LOGGING };
48
60
  }
49
61
  function isLogLevel(value) {
50
62
  return value === "debug" || value === "info" || value === "warn" || value === "error";
51
63
  }
64
+ function normalizeSinks(value, fallback) {
65
+ if (!Array.isArray(value)) {
66
+ return [...fallback];
67
+ }
68
+ const sinks = value.filter((entry) => entry === "terminal" || entry === "ndjson");
69
+ return sinks.length > 0 ? [...new Set(sinks)] : [...fallback];
70
+ }
71
+ function isLegacySharedDefaultConfig(candidate, normalizedLevel, normalizedSinks) {
72
+ return normalizedLevel === LEGACY_SHARED_RUNTIME_LOGGING.level
73
+ && normalizedSinks.length === LEGACY_SHARED_RUNTIME_LOGGING.sinks.length
74
+ && LEGACY_SHARED_RUNTIME_LOGGING.sinks.every((sink) => normalizedSinks.includes(sink))
75
+ && Object.keys(candidate).every((key) => key === "level" || key === "sinks");
76
+ }
52
77
  function resolveRuntimeLoggingConfig(configPath, processName) {
53
- const defaultLevel = defaultLevelForProcess(processName);
78
+ const processDefault = defaultLoggingForProcess(processName);
54
79
  let parsed = null;
55
80
  try {
56
81
  const raw = fs.readFileSync(configPath, "utf-8");
57
82
  parsed = JSON.parse(raw);
58
83
  }
59
84
  catch {
60
- return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
85
+ return { ...processDefault };
61
86
  }
62
87
  if (!parsed || typeof parsed !== "object") {
63
- return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
88
+ return { ...processDefault };
64
89
  }
65
90
  const candidate = parsed;
66
- const level = isLogLevel(candidate.level) ? candidate.level : defaultLevel;
67
- const sinks = Array.isArray(candidate.sinks)
68
- ? candidate.sinks.filter((entry) => entry === "terminal" || entry === "ndjson")
69
- : DEFAULT_RUNTIME_LOGGING.sinks;
91
+ const level = isLogLevel(candidate.level) ? candidate.level : processDefault.level;
92
+ const sinks = normalizeSinks(candidate.sinks, processDefault.sinks);
93
+ if ((processName === "ouro" || processName === "ouro-bot")
94
+ && isLegacySharedDefaultConfig(candidate, level, sinks)) {
95
+ return { ...processDefault };
96
+ }
70
97
  return {
71
98
  level,
72
- sinks: sinks.length > 0 ? [...new Set(sinks)] : [...DEFAULT_RUNTIME_LOGGING.sinks],
99
+ sinks,
73
100
  };
74
101
  }
102
+ function resolveAgentNameForPaths(explicit) {
103
+ if (explicit && explicit.trim().length > 0)
104
+ return explicit.trim();
105
+ try {
106
+ return (0, identity_1.getAgentName)();
107
+ }
108
+ catch {
109
+ return "slugger";
110
+ }
111
+ }
75
112
  function configureDaemonRuntimeLogger(processName, options = {}) {
76
- const homeDir = options.homeDir ?? os.homedir();
77
- const configPath = options.configPath ?? path.join(homeDir, ".agentstate", "daemon", "logging.json");
113
+ const agentName = resolveAgentNameForPaths(options.agentName);
114
+ const configPath = options.configPath
115
+ ?? (options.homeDir
116
+ ? path.join(options.homeDir, "AgentBundles", `${agentName}.ouro`, "state", "daemon", "logging.json")
117
+ : (0, identity_1.getAgentDaemonLoggingConfigPath)(agentName));
78
118
  const config = resolveRuntimeLoggingConfig(configPath, processName);
119
+ const logsDir = options.homeDir
120
+ ? path.join(options.homeDir, "AgentBundles", `${agentName}.ouro`, "state", "daemon", "logs")
121
+ : (0, identity_1.getAgentDaemonLogsDir)(agentName);
79
122
  const sinks = config.sinks.map((sinkName) => {
80
123
  if (sinkName === "terminal") {
81
124
  return (0, nerves_1.createTerminalSink)();
82
125
  }
83
- const ndjsonPath = path.join(homeDir, ".agentstate", "daemon", "logs", `${processName}.ndjson`);
126
+ const ndjsonPath = path.join(logsDir, `${processName}.ndjson`);
84
127
  return (0, nerves_1.createNdjsonFileSink)(ndjsonPath);
85
128
  });
86
129
  const logger = (0, nerves_1.createLogger)({