@ouro.bot/cli 0.1.0-alpha.438 → 0.1.0-alpha.439

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.
package/changelog.json CHANGED
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.439",
6
+ "changes": [
7
+ "When an older Ouro-owned launcher is shadowing the managed `~/.ouro-cli/bin/ouro` path, system setup now repairs it in place instead of dumping a PATH lecture onto the human. Old global Homebrew/npm launchers that clearly belong to Ouro get rewritten to the current managed wrapper automatically.",
8
+ "That repair is intentionally narrow and truthful: Ouro only rewrites a shadowed launcher when its realpath or readable content proves it is one of our old launchers. Unknown launchers still keep the explicit PATH warning instead of being touched.",
9
+ "New path-installer and startup output coverage lock in the self-heal path, including readable non-Ouro launchers, disappearing files, write failures, and the happy-path repair of a stale `/opt/homebrew/bin/ouro`, with full coverage, integration, and packaged-install validation on top."
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "0.1.0-alpha.438",
6
14
  "changes": [
@@ -3262,6 +3262,9 @@ async function performSystemSetup(deps) {
3262
3262
  if (installResult.repairedOldLauncher) {
3263
3263
  deps.writeStdout("repaired stale ouro launcher at ~/.local/bin/ouro");
3264
3264
  }
3265
+ if (installResult.repairedShadowedLauncherPath) {
3266
+ deps.writeStdout(`updated stale ouro launcher at ${installResult.repairedShadowedLauncherPath}`);
3267
+ }
3265
3268
  if (installResult.pathResolution?.status === "shadowed") {
3266
3269
  deps.writeStdout(`fix ouro PATH: ${installResult.pathResolution.detail}; ` +
3267
3270
  `fix: ${installResult.pathResolution.remediation}`);
@@ -106,6 +106,72 @@ function isWrapperCurrent(scriptPath, existsSync, readFileSync) {
106
106
  return false;
107
107
  }
108
108
  }
109
+ function isOwnedOuroLauncherPath(resolvedPath) {
110
+ const normalized = path.normalize(resolvedPath);
111
+ return (normalized.includes(`${path.sep}node_modules${path.sep}@ouro.bot${path.sep}cli${path.sep}`) ||
112
+ normalized.includes(`${path.sep}node_modules${path.sep}ouro.bot${path.sep}`));
113
+ }
114
+ function isOwnedOuroLauncherContent(content) {
115
+ return (content.includes("@ouro.bot/cli") ||
116
+ content.includes("ouro.bot@latest") ||
117
+ content.includes('exec npx --yes ouro.bot "$@"') ||
118
+ content.includes("CurrentVersion/node_modules/@ouro.bot/cli"));
119
+ }
120
+ function canRepairShadowedLauncher(shadowPath, existsSync, readFileSync, realpathSync) {
121
+ if (!existsSync(shadowPath))
122
+ return false;
123
+ try {
124
+ if (isOwnedOuroLauncherPath(realpathSync(shadowPath)))
125
+ return true;
126
+ }
127
+ catch {
128
+ // Fall through to content detection below.
129
+ }
130
+ try {
131
+ return isOwnedOuroLauncherContent(readFileSync(shadowPath, "utf-8"));
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ function repairShadowedLauncher(input) {
138
+ if (!canRepairShadowedLauncher(input.shadowPath, input.existsSync, input.readFileSync, input.realpathSync)) {
139
+ return null;
140
+ }
141
+ (0, runtime_1.emitNervesEvent)({
142
+ component: "daemon",
143
+ event: "daemon.ouro_path_shadow_repair_start",
144
+ message: "repairing stale shadowed ouro launcher",
145
+ meta: { shadowPath: input.shadowPath },
146
+ });
147
+ try {
148
+ if (input.lstatSync(input.shadowPath).isSymbolicLink()) {
149
+ input.unlinkSync(input.shadowPath);
150
+ }
151
+ input.writeFileSync(input.shadowPath, WRAPPER_SCRIPT, { mode: 0o755 });
152
+ input.chmodSync(input.shadowPath, 0o755);
153
+ (0, runtime_1.emitNervesEvent)({
154
+ component: "daemon",
155
+ event: "daemon.ouro_path_shadow_repair_end",
156
+ message: "repaired stale shadowed ouro launcher",
157
+ meta: { shadowPath: input.shadowPath },
158
+ });
159
+ return input.shadowPath;
160
+ }
161
+ catch (error) {
162
+ (0, runtime_1.emitNervesEvent)({
163
+ level: "warn",
164
+ component: "daemon",
165
+ event: "daemon.ouro_path_shadow_repair_error",
166
+ message: "failed to repair stale shadowed ouro launcher",
167
+ meta: {
168
+ shadowPath: input.shadowPath,
169
+ error: error instanceof Error ? error.message : String(error),
170
+ },
171
+ });
172
+ return null;
173
+ }
174
+ }
109
175
  function firstOuroOnPath(envPath, existsSync) {
110
176
  for (const dir of envPath.split(path.delimiter)) {
111
177
  if (!dir)
@@ -166,6 +232,9 @@ function installOuroCommand(deps = {}) {
166
232
  const readFileSync = deps.readFileSync ?? ((p, enc) => fs.readFileSync(p, enc));
167
233
  const appendFileSync = deps.appendFileSync ?? fs.appendFileSync;
168
234
  const chmodSync = deps.chmodSync ?? fs.chmodSync;
235
+ const realpathSync = deps.realpathSync ?? fs.realpathSync;
236
+ const lstatSync = deps.lstatSync ?? fs.lstatSync;
237
+ const unlinkSync = deps.unlinkSync ?? fs.unlinkSync;
169
238
  const envPath = deps.envPath ?? process.env.PATH ?? "";
170
239
  const shell = deps.shell ?? process.env.SHELL;
171
240
  /* v8 ignore stop */
@@ -212,7 +281,22 @@ function installOuroCommand(deps = {}) {
212
281
  }
213
282
  // ── Fast-path: modern wrapper already current ──
214
283
  if (modernCurrent) {
215
- const pathResolution = resolvePath();
284
+ let pathResolution = resolvePath();
285
+ const repairedShadowedLauncherPath = pathResolution.status === "shadowed" && pathResolution.resolvedPath
286
+ ? repairShadowedLauncher({
287
+ shadowPath: pathResolution.resolvedPath,
288
+ existsSync,
289
+ readFileSync,
290
+ writeFileSync,
291
+ chmodSync,
292
+ realpathSync,
293
+ lstatSync,
294
+ unlinkSync,
295
+ })
296
+ : null;
297
+ if (repairedShadowedLauncherPath) {
298
+ pathResolution = resolvePath();
299
+ }
216
300
  if (pathResolution.status === "shadowed") {
217
301
  (0, runtime_1.emitNervesEvent)({
218
302
  level: "warn",
@@ -228,7 +312,16 @@ function installOuroCommand(deps = {}) {
228
312
  message: "ouro command already installed",
229
313
  meta: { scriptPath, pathStatus: pathResolution.status, resolvedPath: pathResolution.resolvedPath },
230
314
  });
231
- return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed", repairedOldLauncher, pathResolution };
315
+ return {
316
+ installed: false,
317
+ scriptPath,
318
+ pathReady: isBinDirInPath(binDir, envPath),
319
+ shellProfileUpdated: null,
320
+ skippedReason: "already-installed",
321
+ repairedOldLauncher,
322
+ repairedShadowedLauncherPath,
323
+ pathResolution,
324
+ };
232
325
  }
233
326
  (0, runtime_1.emitNervesEvent)({
234
327
  component: "daemon",
@@ -281,7 +374,22 @@ function installOuroCommand(deps = {}) {
281
374
  }
282
375
  }
283
376
  }
284
- const pathResolution = resolvePath();
377
+ let pathResolution = resolvePath();
378
+ const repairedShadowedLauncherPath = pathResolution.status === "shadowed" && pathResolution.resolvedPath
379
+ ? repairShadowedLauncher({
380
+ shadowPath: pathResolution.resolvedPath,
381
+ existsSync,
382
+ readFileSync,
383
+ writeFileSync,
384
+ chmodSync,
385
+ realpathSync,
386
+ lstatSync,
387
+ unlinkSync,
388
+ })
389
+ : null;
390
+ if (repairedShadowedLauncherPath) {
391
+ pathResolution = resolvePath();
392
+ }
285
393
  if (pathResolution.status === "shadowed") {
286
394
  (0, runtime_1.emitNervesEvent)({
287
395
  level: "warn",
@@ -295,7 +403,23 @@ function installOuroCommand(deps = {}) {
295
403
  component: "daemon",
296
404
  event: "daemon.ouro_path_install_end",
297
405
  message: "ouro command installed",
298
- meta: { scriptPath, pathReady, shellProfileUpdated, oldScriptPath: oldExists ? oldScriptPath : null, pathStatus: pathResolution.status, resolvedPath: pathResolution.resolvedPath },
406
+ meta: {
407
+ scriptPath,
408
+ pathReady,
409
+ shellProfileUpdated,
410
+ oldScriptPath: oldExists ? oldScriptPath : null,
411
+ repairedShadowedLauncherPath,
412
+ pathStatus: pathResolution.status,
413
+ resolvedPath: pathResolution.resolvedPath,
414
+ },
299
415
  });
300
- return { installed: true, scriptPath, pathReady, shellProfileUpdated, repairedOldLauncher, pathResolution };
416
+ return {
417
+ installed: true,
418
+ scriptPath,
419
+ pathReady,
420
+ shellProfileUpdated,
421
+ repairedOldLauncher,
422
+ repairedShadowedLauncherPath,
423
+ pathResolution,
424
+ };
301
425
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.438",
3
+ "version": "0.1.0-alpha.439",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",