@ouro.bot/cli 0.1.0-alpha.271 → 0.1.0-alpha.272
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.272",
|
|
6
|
+
"changes": [
|
|
7
|
+
"fix(daemon): the daemon's periodic update checker can now actually auto-update itself. The `onUpdate` callback in `daemon.ts` invoked `performStagedRestart` which (a) ran `npm install -g @ouro.bot/cli@{version}` to install to the global node prefix, and (b) tried to find the new code via `node -e \"console.log(require.resolve('@ouro.bot/cli/package.json'))\"` which depends on the daemon process's NODE_PATH. Neither path was actually reachable from the daemon process running out of `~/.ouro-cli/versions/{version}/...`, so every auto-update attempt bailed at `daemon.staged_restart_path_failed` (\"could not resolve new code path\") and the daemon never updated itself. The user had to manually run `ouro up` to pick up new versions. Fix: switch the staged restart to use the version-managed installer (same one the CLI's `up` flow uses) — `installVersion(version)` puts files at `~/.ouro-cli/versions/{version}/node_modules/@ouro.bot/cli` (deterministic, computable), then `activateVersion(version)` flips the CurrentVersion symlink so the next user-driven `ouro up` sees the same version the daemon is running. `performStagedRestart` gained an optional `installNewVersion` dep that production callers inject; the legacy `npm install -g` fallback path is preserved for tests. Two new regression tests in `staged-restart.test.ts`.",
|
|
8
|
+
"fix(cli): `ouro up` no longer prints the 'ouro updated to ...' message twice during npx invocations. Three independent paths can detect a CLI version change: (1) `checkForCliUpdate` finds a newer version on npm and re-execs (cross-process print), (2) `ensureCurrentVersionInstalled` flips the CurrentVersion symlink during `performSystemSetup` because the running package version is newer than what the symlink pointed at (path 2, in-process), (3) `bundle-meta.json`'s stored runtime version differs from the running version (path 3, in-process fallback). Path 3's existing guard `linkedVersionBeforeUp !== currentVersion` correctly skipped path 3 when path 1 had already printed in a different process, but did NOT catch the case where path 2 fired in the same process. Verified live on 2026-04-08: `npx --yes @ouro.bot/cli@alpha up` printed the message twice. Fix: track an in-process `printedUpdateMessage` flag set by path 2; path 3 checks it before printing. Path 3 still acts as a fallback when path 2 didn't fire. New regression test in `daemon-cli-update-flow.test.ts` simulates the npx scenario and asserts exactly one print.",
|
|
9
|
+
"fix(hooks): Claude Code lifecycle hooks (`ouro hook session-start|stop|post-tool-use`) now short-circuit when the daemon socket file doesn't exist, instead of attempting `sendDaemonCommand` and logging two ENOENT errors per hook fire (one for `message.send`, one for `inner.wake`). Every Claude Code event during a daemon-down window was producing noisy `connect ENOENT /tmp/ouroboros-daemon.sock` entries in `ouro.ndjson`, which made it hard to read logs around outages. The hook is best-effort by design — dropping notifications when the daemon is down is correct behavior; we just don't want to log spam about it. New nerves event `daemon.hook_skipped_no_socket` (info level) when the short-circuit fires."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
4
12
|
{
|
|
5
13
|
"version": "0.1.0-alpha.271",
|
|
6
14
|
"changes": [
|
|
@@ -791,6 +791,27 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
791
791
|
}
|
|
792
792
|
}
|
|
793
793
|
await performSystemSetup(deps);
|
|
794
|
+
// Track whether we've already printed the "ouro updated to" message
|
|
795
|
+
// this turn so the bundle-meta-fallback path below doesn't double-print.
|
|
796
|
+
// There are three independent paths that can detect "the binary just
|
|
797
|
+
// got newer":
|
|
798
|
+
// 1. checkForCliUpdate found a newer version on npm (above) — this
|
|
799
|
+
// path always re-execs, so the duplicate-detect runs in a
|
|
800
|
+
// different process and the in-process tracker doesn't apply.
|
|
801
|
+
// 2. ensureCurrentVersionInstalled (called from performSystemSetup)
|
|
802
|
+
// flipped the CurrentVersion symlink because the running package
|
|
803
|
+
// version is newer than what the symlink pointed at. This is the
|
|
804
|
+
// common npx path.
|
|
805
|
+
// 3. bundle-meta.json's stored runtime version differs from the
|
|
806
|
+
// running version (fallback for when path 2 couldn't activate
|
|
807
|
+
// but the binary is still newer).
|
|
808
|
+
//
|
|
809
|
+
// Verified live on 2026-04-08: npx download triggered both path 2 and
|
|
810
|
+
// path 3 in the same process and the user saw the message printed twice.
|
|
811
|
+
// Path 3's existing `linkedVersionBeforeUp !== currentVersion` guard
|
|
812
|
+
// catches the cross-process re-exec case (path 1) but not the
|
|
813
|
+
// in-process double-fire case (path 2 + path 3 in the same process).
|
|
814
|
+
let printedUpdateMessage = false;
|
|
794
815
|
const linkedVersionAfterSetup = deps.getCurrentCliVersion?.() ?? null;
|
|
795
816
|
const runtimeVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
796
817
|
if (linkedVersionBeforeUp && linkedVersionBeforeUp !== runtimeVersion && linkedVersionAfterSetup === runtimeVersion) {
|
|
@@ -799,6 +820,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
799
820
|
if (changelogCommand) {
|
|
800
821
|
deps.writeStdout(`review changes with: ${changelogCommand}`);
|
|
801
822
|
}
|
|
823
|
+
printedUpdateMessage = true;
|
|
802
824
|
}
|
|
803
825
|
// Run update hooks before starting daemon so user sees the output
|
|
804
826
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
@@ -812,9 +834,14 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
812
834
|
// Notify about CLI binary update (npx downloaded a new version).
|
|
813
835
|
// Skip when the symlink already points to the running version — that
|
|
814
836
|
// means path 1 (checkForCliUpdate + reExecFromNewVersion) already
|
|
815
|
-
// printed the update message before re-exec.
|
|
837
|
+
// printed the update message before re-exec. Also skip when path 2
|
|
838
|
+
// (the symlink-flip detector above) already printed in this same
|
|
839
|
+
// process — otherwise npx invocations print the message twice.
|
|
816
840
|
/* v8 ignore start -- CLI update detection: tested via daemon-cli-version-detect.test.ts @preserve */
|
|
817
|
-
if (
|
|
841
|
+
if (!printedUpdateMessage
|
|
842
|
+
&& previousCliVersion
|
|
843
|
+
&& previousCliVersion !== currentVersion
|
|
844
|
+
&& linkedVersionBeforeUp !== currentVersion) {
|
|
818
845
|
deps.writeStdout(`ouro updated to ${currentVersion} (was ${previousCliVersion})`);
|
|
819
846
|
const changelogCommand = (0, ouro_version_manager_1.buildChangelogCommand)(previousCliVersion, currentVersion);
|
|
820
847
|
/* v8 ignore next -- buildChangelogCommand is non-null when previous/current runtime versions differ @preserve */
|
|
@@ -1122,12 +1149,29 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1122
1149
|
else {
|
|
1123
1150
|
content = `[Claude Code hook: ${eventType} in session ${sessionId}]`;
|
|
1124
1151
|
}
|
|
1125
|
-
// Send to the specific agent configured for this hook
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1152
|
+
// Send to the specific agent configured for this hook. Short-circuit
|
|
1153
|
+
// when the daemon socket file doesn't exist — otherwise every
|
|
1154
|
+
// Claude Code lifecycle event during a daemon-down window logs two
|
|
1155
|
+
// ENOENT errors in ouro.ndjson (one for message.send, one for
|
|
1156
|
+
// inner.wake) which makes it hard to read the log around outages.
|
|
1157
|
+
// The hook is best-effort: dropping notifications when the daemon
|
|
1158
|
+
// is down is the correct behavior; we just don't want to log spam
|
|
1159
|
+
// about it.
|
|
1160
|
+
if (require("fs").existsSync(deps.socketPath)) {
|
|
1161
|
+
try {
|
|
1162
|
+
await deps.sendCommand(deps.socketPath, { kind: "message.send", from: `claude-code:${sessionId}`, to: command.agent, content }).catch(() => { });
|
|
1163
|
+
await deps.sendCommand(deps.socketPath, { kind: "inner.wake", agent: command.agent }).catch(() => { });
|
|
1164
|
+
}
|
|
1165
|
+
catch { /* daemon not running — silent */ }
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1169
|
+
component: "daemon",
|
|
1170
|
+
event: "daemon.hook_skipped_no_socket",
|
|
1171
|
+
message: "claude code hook skipped — daemon socket missing",
|
|
1172
|
+
meta: { socketPath: deps.socketPath, eventType, agent: command.agent },
|
|
1173
|
+
});
|
|
1129
1174
|
}
|
|
1130
|
-
catch { /* daemon not running — silent */ }
|
|
1131
1175
|
// Output for Claude Code hook system
|
|
1132
1176
|
deps.writeStdout(JSON.stringify({ continue: true }));
|
|
1133
1177
|
return JSON.stringify({ continue: true });
|
|
@@ -54,6 +54,7 @@ const agent_config_v2_1 = require("./hooks/agent-config-v2");
|
|
|
54
54
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
55
55
|
const update_checker_1 = require("../versioning/update-checker");
|
|
56
56
|
const staged_restart_1 = require("../versioning/staged-restart");
|
|
57
|
+
const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
|
|
57
58
|
const child_process_1 = require("child_process");
|
|
58
59
|
const pending_1 = require("../../mind/pending");
|
|
59
60
|
const agent_service_1 = require("./agent-service");
|
|
@@ -427,17 +428,34 @@ class OuroDaemon {
|
|
|
427
428
|
onUpdate: /* v8 ignore start -- integration: real npm install + process spawn @preserve */ async (result) => {
|
|
428
429
|
if (!result.latestVersion)
|
|
429
430
|
return;
|
|
431
|
+
// Install via the version manager (NOT `npm install -g`). The
|
|
432
|
+
// global install path doesn't end up on the daemon process's
|
|
433
|
+
// NODE_PATH, so the previous `require.resolve('@ouro.bot/cli')`
|
|
434
|
+
// -based path lookup always returned null and the staged restart
|
|
435
|
+
// never actually completed. Verified live on 2026-04-08:
|
|
436
|
+
// alpha.268 daemon detected alpha.270 was available, ran the
|
|
437
|
+
// staged restart, and bailed at `staged_restart_path_failed` —
|
|
438
|
+
// meaning the daemon could never auto-update itself and required
|
|
439
|
+
// manual `ouro up` to pick up new versions.
|
|
440
|
+
//
|
|
441
|
+
// Switch to the version-managed layout the CLI itself uses:
|
|
442
|
+
// installVersion(version) puts files at
|
|
443
|
+
// ~/.ouro-cli/versions/{version}/node_modules/@ouro.bot/cli
|
|
444
|
+
// which is a known path we can compute deterministically.
|
|
445
|
+
// Then activateVersion(version) flips the CurrentVersion symlink
|
|
446
|
+
// so the next `ouro up` from the user sees the same version
|
|
447
|
+
// the daemon is running.
|
|
448
|
+
const cliHome = (0, ouro_version_manager_1.getOuroCliHome)();
|
|
430
449
|
await (0, staged_restart_1.performStagedRestart)(result.latestVersion, {
|
|
431
450
|
execSync: (cmd) => (0, child_process_1.execSync)(cmd, { stdio: "inherit" }),
|
|
432
451
|
spawnSync: child_process_1.spawnSync,
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
452
|
+
installNewVersion: (version) => {
|
|
453
|
+
(0, ouro_version_manager_1.installVersion)(version, {});
|
|
454
|
+
(0, ouro_version_manager_1.activateVersion)(version, {});
|
|
455
|
+
},
|
|
456
|
+
resolveNewCodePath: (version) => {
|
|
457
|
+
const versionPath = path.join(cliHome, "versions", version, "node_modules", "@ouro.bot", "cli");
|
|
458
|
+
return fs.existsSync(versionPath) ? versionPath : null;
|
|
441
459
|
},
|
|
442
460
|
gracefulShutdown: () => daemon.stop(),
|
|
443
461
|
spawnNewDaemon: (entryPath, sock) => {
|
|
@@ -45,14 +45,22 @@ async function performStagedRestart(version, deps) {
|
|
|
45
45
|
});
|
|
46
46
|
// Step 1: Install new version
|
|
47
47
|
try {
|
|
48
|
-
deps.
|
|
48
|
+
if (deps.installNewVersion) {
|
|
49
|
+
deps.installNewVersion(version);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Backward-compat fallback for callers that haven't migrated to the
|
|
53
|
+
// version-managed installer. Tests use this path with a mocked
|
|
54
|
+
// execSync; production callers inject installNewVersion.
|
|
55
|
+
deps.execSync(`npm install -g @ouro.bot/cli@${version}`);
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
58
|
catch (err) {
|
|
51
59
|
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
52
60
|
(0, runtime_1.emitNervesEvent)({
|
|
53
61
|
component: "daemon",
|
|
54
62
|
event: "daemon.staged_restart_install_failed",
|
|
55
|
-
message: "
|
|
63
|
+
message: "install failed",
|
|
56
64
|
meta: { version, error: errorMessage },
|
|
57
65
|
});
|
|
58
66
|
return { ok: false, error: errorMessage };
|