@ouro.bot/cli 0.1.0-alpha.252 → 0.1.0-alpha.253

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,13 @@
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.253",
6
+ "changes": [
7
+ "fix(daemon): test-isolation guard \u2014 stop tests from leaking real `inner.wake` commands into the running daemon. A pattern in 36+ test files mocks `getAgentName` to the literal `\"testagent\"` but does NOT mock `socket-client`, so any code path through pondering / rest / coding feedback fires a real socket connection at /tmp/ouroboros-daemon.sock with `inner.wake testagent`. The daemon errored on every command (`Unknown managed agent 'testagent'`) and at flood volumes that contributed to a real outage on the developer's machine. The fix is in `socket-client.ts` itself: detect vitest via `process.argv` (no env vars) and convert all socket operations into safe no-ops. Tests that legitimately exercise the real transport (socket-client.test.ts, daemon-cli-defaults.test.ts) opt out of the guard via a new `__bypassVitestGuardForTests()` setter that lives on globalThis to survive `vi.resetModules()`. New nerves events: `daemon.socket_command_test_blocked`, `daemon.inner_wake_test_blocked`.",
8
+ "test(contract): new test-isolation contract test ratchets two anti-patterns: (1) test files using `name: \"testagent\"` without mocking socket-client, and (2) test files constructing write paths under the real `~/AgentBundles` via `os.homedir()`. Existing offenders are grandfathered in two allowlists; new offenders fail the build. Follow-up PRs shrink the allowlists toward zero."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.252",
6
13
  "changes": [
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DEFAULT_DAEMON_SOCKET_PATH = void 0;
37
+ exports.__bypassVitestGuardForTests = __bypassVitestGuardForTests;
37
38
  exports.sendDaemonCommand = sendDaemonCommand;
38
39
  exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
39
40
  exports.requestInnerWake = requestInnerWake;
@@ -41,7 +42,50 @@ const fs = __importStar(require("fs"));
41
42
  const net = __importStar(require("net"));
42
43
  const runtime_1 = require("../../nerves/runtime");
43
44
  exports.DEFAULT_DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
45
+ /**
46
+ * Defense-in-depth: detect if we're running under vitest. Tests that forget
47
+ * to `vi.mock("../../heart/daemon/socket-client")` would otherwise leak real
48
+ * `inner.wake` commands (with whatever literal agent name the test uses,
49
+ * commonly "testagent") into whichever real daemon happens to be running on
50
+ * the developer's machine. That has caused real outages when test loops
51
+ * flooded the production-on-localhost daemon.
52
+ *
53
+ * We detect vitest via `process.argv` (not env vars — the project bans those)
54
+ * and convert all socket operations into safe no-ops. Individual tests are
55
+ * still expected to vi.mock this module so they can assert on call counts,
56
+ * but this guard guarantees that "I forgot to mock" can never again take
57
+ * down the daemon.
58
+ *
59
+ * The socket-client's OWN tests (which exercise the real functions against a
60
+ * test socket) call `__bypassVitestGuardForTests(true)` to opt out of the
61
+ * guard. We persist that flag on globalThis so it survives `vi.resetModules`
62
+ * (which clears the module cache between tests).
63
+ */
64
+ const BYPASS_KEY = "__ouro_socket_client_bypass_vitest_guard__";
65
+ /** ONLY for socket-client.test.ts. Do not call from any other test or production code. */
66
+ function __bypassVitestGuardForTests(bypass) {
67
+ ;
68
+ globalThis[BYPASS_KEY] = bypass;
69
+ }
70
+ function isUnderVitest() {
71
+ if (globalThis[BYPASS_KEY] === true)
72
+ return false;
73
+ /* v8 ignore next -- defensive: process and process.argv always exist in node @preserve */
74
+ if (typeof process === "undefined" || !Array.isArray(process.argv))
75
+ return false;
76
+ return process.argv.some((arg) => typeof arg === "string" && arg.includes("vitest"));
77
+ }
44
78
  function sendDaemonCommand(socketPath, command) {
79
+ if (isUnderVitest()) {
80
+ (0, runtime_1.emitNervesEvent)({
81
+ level: "warn",
82
+ component: "daemon",
83
+ event: "daemon.socket_command_test_blocked",
84
+ message: "blocked socket command from leaking into real daemon under vitest",
85
+ meta: { socketPath, kind: command.kind },
86
+ });
87
+ return Promise.resolve({ ok: true, message: "test mode: socket call suppressed" });
88
+ }
45
89
  (0, runtime_1.emitNervesEvent)({
46
90
  component: "daemon",
47
91
  event: "daemon.socket_command_start",
@@ -132,6 +176,9 @@ function sendDaemonCommand(socketPath, command) {
132
176
  });
133
177
  }
134
178
  function checkDaemonSocketAlive(socketPath) {
179
+ if (isUnderVitest()) {
180
+ return Promise.resolve(false);
181
+ }
135
182
  (0, runtime_1.emitNervesEvent)({
136
183
  component: "daemon",
137
184
  event: "daemon.socket_alive_check_start",
@@ -184,6 +231,16 @@ function checkDaemonSocketAlive(socketPath) {
184
231
  });
185
232
  }
186
233
  async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH) {
234
+ if (isUnderVitest()) {
235
+ (0, runtime_1.emitNervesEvent)({
236
+ level: "warn",
237
+ component: "daemon",
238
+ event: "daemon.inner_wake_test_blocked",
239
+ message: "blocked inner wake from leaking into real daemon under vitest",
240
+ meta: { agent, socketPath },
241
+ });
242
+ return null;
243
+ }
187
244
  const socketAvailable = fs.existsSync(socketPath);
188
245
  (0, runtime_1.emitNervesEvent)({
189
246
  component: "daemon",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.252",
3
+ "version": "0.1.0-alpha.253",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",