@ouro.bot/cli 0.1.0-alpha.271 → 0.1.0-alpha.273

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,21 @@
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.273",
6
+ "changes": [
7
+ "feat(version-manager): auto-prune old CLI versions during activate. The user observed `~/.ouro-cli/versions/` accumulating every CLI version they'd ever installed (alpha.85 from 2026-03-20 onward, ~100MB+ of dead node_modules trees) because nothing ever GCed. New `pruneOldVersions(retain=5, deps?)` walks `~/.ouro-cli/versions/`, sorts by alpha-suffix numerically, and deletes everything outside the retention window — except always preserves (a) the currently-active version (CurrentVersion symlink target), and (b) the previous version (previous symlink target), so `ouro rollback` stays one command away. Wired into `cli-defaults.ts` via `ensureCurrentVersionInstalled` and `activateCliVersion` — every successful version activation self-prunes. New helpers `compareCliVersions(a, b)` and `selectVersionsToPrune(installed, protected, retain)` are pure and exported for direct unit coverage. 9 new tests covering version comparison, retention selection, current/previous protection, partial-failure handling, missing-symlink fallback, and non-directory entry filtering.",
8
+ "test(contract): new OURO_DAEMON_INSTANTIATION_ALLOWLIST in test-isolation.contract.test.ts flags any new test file that constructs `new OuroDaemon(...)` outside the 11 grandfathered files. Constructing a real daemon and calling start() runs killOrphanProcesses() and writePidfile() against the production pidfile at ~/.ouro-cli/daemon.pids. The runtime guards added in #346 short-circuit those functions under vitest, but if a future change to start() adds a NEW production-state side-effect, the existing 11 tests would silently exercise it. The contract test forces conscious review of each new file taking this shape — same defense-in-depth pattern as BYPASS_USE_ALLOWLIST in alpha.265."
9
+ ]
10
+ },
11
+ {
12
+ "version": "0.1.0-alpha.272",
13
+ "changes": [
14
+ "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`.",
15
+ "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.",
16
+ "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."
17
+ ]
18
+ },
4
19
  {
5
20
  "version": "0.1.0-alpha.271",
6
21
  "changes": [
@@ -494,6 +494,14 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
494
494
  (0, ouro_version_manager_1.installVersion)(version, {});
495
495
  }
496
496
  (0, ouro_version_manager_1.activateVersion)(version, {});
497
+ // Self-prune: every successful version activation cleans up old
498
+ // versions outside the retention window. Without this, every CLI
499
+ // version ever installed accumulates indefinitely under
500
+ // ~/.ouro-cli/versions/ — the user observed installs going back to
501
+ // alpha.85 from March 20 (~100MB of dead node_modules trees).
502
+ // pruneOldVersions keeps the 5 most recent + the active + the
503
+ // previous version, so rollback stays one command away.
504
+ (0, ouro_version_manager_1.pruneOldVersions)(undefined, {});
497
505
  },
498
506
  /* v8 ignore stop */
499
507
  /* v8 ignore start -- CLI version management defaults: integration code @preserve */
@@ -508,7 +516,14 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
508
516
  });
509
517
  },
510
518
  installCliVersion: async (version) => { (0, ouro_version_manager_1.installVersion)(version, {}); },
511
- activateCliVersion: (version) => { (0, ouro_version_manager_1.activateVersion)(version, {}); },
519
+ activateCliVersion: (version) => {
520
+ (0, ouro_version_manager_1.activateVersion)(version, {});
521
+ // Same self-prune as ensureCurrentVersionInstalled — fires from the
522
+ // checkForCliUpdate path 1 (in-process update detected → activate
523
+ // → re-exec). Without this, the path 1 code path never triggers
524
+ // a prune.
525
+ (0, ouro_version_manager_1.pruneOldVersions)(undefined, {});
526
+ },
512
527
  getCurrentCliVersion: () => (0, ouro_version_manager_1.getCurrentVersion)({}),
513
528
  getPreviousCliVersion: () => (0, ouro_version_manager_1.getPreviousVersion)({}),
514
529
  listCliVersions: () => (0, ouro_version_manager_1.listInstalledVersions)({}),
@@ -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 (previousCliVersion && previousCliVersion !== currentVersion && linkedVersionBeforeUp !== currentVersion) {
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
- try {
1127
- await deps.sendCommand(deps.socketPath, { kind: "message.send", from: `claude-code:${sessionId}`, to: command.agent, content }).catch(() => { });
1128
- await deps.sendCommand(deps.socketPath, { kind: "inner.wake", agent: command.agent }).catch(() => { });
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
- resolveNewCodePath: (_version) => {
434
- try {
435
- const resolved = (0, child_process_1.execSync)(`node -e "console.log(require.resolve('@ouro.bot/cli/package.json'))"`, { encoding: "utf-8" }).trim();
436
- return resolved ? path.dirname(resolved) : null;
437
- }
438
- catch {
439
- return null;
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) => {
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_RETAIN_VERSIONS = void 0;
36
37
  exports.getOuroCliHome = getOuroCliHome;
37
38
  exports.getCurrentVersion = getCurrentVersion;
38
39
  exports.getPreviousVersion = getPreviousVersion;
@@ -40,11 +41,16 @@ exports.buildChangelogCommand = buildChangelogCommand;
40
41
  exports.listInstalledVersions = listInstalledVersions;
41
42
  exports.installVersion = installVersion;
42
43
  exports.activateVersion = activateVersion;
44
+ exports.compareCliVersions = compareCliVersions;
45
+ exports.selectVersionsToPrune = selectVersionsToPrune;
46
+ exports.pruneOldVersions = pruneOldVersions;
43
47
  exports.ensureLayout = ensureLayout;
44
48
  const fs = __importStar(require("fs"));
45
49
  const os = __importStar(require("os"));
46
50
  const path = __importStar(require("path"));
47
51
  const runtime_1 = require("../../nerves/runtime");
52
+ /** Maximum number of installed CLI versions to retain after pruning. */
53
+ exports.DEFAULT_RETAIN_VERSIONS = 5;
48
54
  function getOuroCliHome(homeDir) {
49
55
  /* v8 ignore next -- dep default: tests always inject @preserve */
50
56
  const home = homeDir ?? os.homedir();
@@ -155,6 +161,124 @@ function activateVersion(version, deps) {
155
161
  }
156
162
  symlinkSync(newTarget, currentVersionPath);
157
163
  }
164
+ /**
165
+ * Compare two version strings of the form `0.1.0-alpha.{n}`. Returns
166
+ * positive when `a` is newer, negative when `b` is newer, 0 when equal.
167
+ *
168
+ * The harness only ships `0.1.0-alpha.{n}` versions today; this comparator
169
+ * extracts the numeric tail and falls back to a lexicographic compare for
170
+ * any version that doesn't match the pattern (so unexpected version
171
+ * formats sort consistently rather than throwing).
172
+ */
173
+ function compareCliVersions(a, b) {
174
+ const aMatch = /alpha\.(\d+)/.exec(a);
175
+ const bMatch = /alpha\.(\d+)/.exec(b);
176
+ if (aMatch && bMatch) {
177
+ const aN = parseInt(aMatch[1], 10);
178
+ const bN = parseInt(bMatch[1], 10);
179
+ return aN - bN;
180
+ }
181
+ // Fallback: lexicographic. Both-mismatched and one-mismatched cases land
182
+ // here. Predictable, even if not strictly semver-correct.
183
+ if (a < b)
184
+ return -1;
185
+ if (a > b)
186
+ return 1;
187
+ return 0;
188
+ }
189
+ /**
190
+ * Identify which installed CLI versions can be safely deleted, given a
191
+ * retention policy. Pure: takes the full version list and the protected
192
+ * (current/previous) versions, returns the versions to delete.
193
+ *
194
+ * Retention rules:
195
+ * - Always keep the N most recent versions (default 5).
196
+ * - Always keep the currently-active version (CurrentVersion symlink target).
197
+ * - Always keep the previous version (previous symlink target) so rollback
198
+ * stays one command away.
199
+ * - Delete everything else.
200
+ *
201
+ * Exported so tests can pin the policy without shelling out to a real fs.
202
+ */
203
+ function selectVersionsToPrune(installedVersions, protectedVersions, retain = exports.DEFAULT_RETAIN_VERSIONS) {
204
+ if (installedVersions.length <= retain)
205
+ return [];
206
+ const sorted = [...installedVersions].sort(compareCliVersions).reverse();
207
+ const keepers = new Set(sorted.slice(0, retain));
208
+ if (protectedVersions.current)
209
+ keepers.add(protectedVersions.current);
210
+ if (protectedVersions.previous)
211
+ keepers.add(protectedVersions.previous);
212
+ return installedVersions.filter((v) => !keepers.has(v));
213
+ }
214
+ /**
215
+ * Prune installed CLI versions according to the retention policy. Removes
216
+ * `~/.ouro-cli/versions/{version}/` directories for versions outside the
217
+ * retention window. Best-effort: per-version delete failures are logged
218
+ * via nerves but don't propagate.
219
+ *
220
+ * Called from the activate path (cli-defaults.ts) so that every successful
221
+ * `ouro up` self-prunes. The user observed `~/.ouro-cli/versions/` going
222
+ * back to alpha.85 from March 20 — every CLI version they'd ever installed
223
+ * was still on disk because nothing ever GCed.
224
+ */
225
+ function pruneOldVersions(retain = exports.DEFAULT_RETAIN_VERSIONS, deps = {}) {
226
+ const cliHome = getOuroCliHome(deps.homeDir);
227
+ /* v8 ignore start -- dep defaults: tests always inject @preserve */
228
+ const readdirSync = deps.readdirSync ?? ((p, opts) => fs.readdirSync(p, opts));
229
+ const readlinkSync = deps.readlinkSync ?? fs.readlinkSync;
230
+ const rmSync = deps.rmSync ?? fs.rmSync;
231
+ /* v8 ignore stop */
232
+ const versionsDir = path.join(cliHome, "versions");
233
+ let installed;
234
+ try {
235
+ installed = readdirSync(versionsDir, { withFileTypes: true })
236
+ .filter((e) => e.isDirectory())
237
+ .map((e) => e.name);
238
+ }
239
+ catch {
240
+ return { kept: [], deleted: [], failed: [] };
241
+ }
242
+ const current = (() => {
243
+ try {
244
+ return path.basename(readlinkSync(path.join(cliHome, "CurrentVersion")));
245
+ }
246
+ catch {
247
+ return null;
248
+ }
249
+ })();
250
+ const previous = (() => {
251
+ try {
252
+ return path.basename(readlinkSync(path.join(cliHome, "previous")));
253
+ }
254
+ catch {
255
+ return null;
256
+ }
257
+ })();
258
+ const toDelete = selectVersionsToPrune(installed, { current, previous }, retain);
259
+ const deleted = [];
260
+ const failed = [];
261
+ for (const version of toDelete) {
262
+ const versionDir = path.join(versionsDir, version);
263
+ try {
264
+ rmSync(versionDir, { recursive: true, force: true });
265
+ deleted.push(version);
266
+ }
267
+ catch (error) {
268
+ failed.push({
269
+ version,
270
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
271
+ });
272
+ }
273
+ }
274
+ (0, runtime_1.emitNervesEvent)({
275
+ component: "daemon",
276
+ event: "daemon.cli_versions_pruned",
277
+ message: `pruned ${deleted.length} old CLI versions`,
278
+ meta: { retain, deleted, failed: failed.length, kept: installed.filter((v) => !deleted.includes(v)) },
279
+ });
280
+ return { kept: installed.filter((v) => !deleted.includes(v)), deleted, failed };
281
+ }
158
282
  function ensureLayout(deps) {
159
283
  const cliHome = getOuroCliHome(deps.homeDir);
160
284
  /* v8 ignore next -- dep default: tests always inject @preserve */
@@ -45,14 +45,22 @@ async function performStagedRestart(version, deps) {
45
45
  });
46
46
  // Step 1: Install new version
47
47
  try {
48
- deps.execSync(`npm install -g @ouro.bot/cli@${version}`);
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: "npm install failed",
63
+ message: "install failed",
56
64
  meta: { version, error: errorMessage },
57
65
  });
58
66
  return { ok: false, error: errorMessage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.271",
3
+ "version": "0.1.0-alpha.273",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",