@ouro.bot/cli 0.1.0-alpha.440 → 0.1.0-alpha.441

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.441",
6
+ "changes": [
7
+ "`ouro up` now lets the shared startup TUI own the masthead render path, so the screen no longer double-prints into scrollback and the shipped banner is a clean, correctly spelled classic `OUROBOROS` wordmark.",
8
+ "Vault unlock lookup now normalizes the canonical vault host while still reusing previously saved local unlock material from legacy vault coordinates, so a machine that already unlocked an agent vault does not get treated like it forgot after harmless vault-host drift.",
9
+ "macOS keychain read failures now surface as truthful local-store errors instead of being mislabeled as a locked vault, and new masthead/vault coverage plus packaged-install verification lock the fix into the shipped CLI."
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "0.1.0-alpha.440",
6
14
  "changes": [
@@ -91,7 +91,6 @@ const agentic_repair_1 = require("./agentic-repair");
91
91
  const readiness_repair_1 = require("./readiness-repair");
92
92
  const human_readiness_1 = require("./human-readiness");
93
93
  const human_command_screens_1 = require("./human-command-screens");
94
- const terminal_ui_1 = require("./terminal-ui");
95
94
  const startup_tui_1 = require("./startup-tui");
96
95
  const stale_bundle_prune_1 = require("./stale-bundle-prune");
97
96
  const up_progress_1 = require("./up-progress");
@@ -3933,13 +3932,6 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
3933
3932
  }
3934
3933
  const linkedVersionBeforeUp = deps.getCurrentCliVersion?.() ?? null;
3935
3934
  const outputIsTTY = deps.isTTY ?? process.stdout.isTTY === true;
3936
- if (outputIsTTY && deps.writeRaw) {
3937
- deps.writeRaw(`${(0, terminal_ui_1.renderOuroMasthead)({
3938
- isTTY: true,
3939
- columns: deps.stdoutColumns ?? process.stdout.columns,
3940
- subtitle: "Preparing the house.",
3941
- }).trimEnd()}\n\n`);
3942
- }
3943
3935
  const progress = new up_progress_1.UpProgress({
3944
3936
  write: deps.writeRaw ?? deps.writeStdout,
3945
3937
  isTTY: outputIsTTY,
@@ -17,6 +17,14 @@ const GLOW = "\x1b[38;2;74;227;108m";
17
17
  const BONE = "\x1b[38;2;237;242;238m";
18
18
  const MIST = "\x1b[38;2;154;174;159m";
19
19
  const ANSI_RE = /\x1b\[[0-9;]*m/g;
20
+ const MASTHEAD_WORD = "OUROBOROS";
21
+ const CLASSIC_WORDMARK_GLYPHS = {
22
+ O: [" ___ ", " / _ \\ ", "| | | |", "| |_| |", " \\___/ "],
23
+ U: [" _ _ ", "| | | |", "| | | |", "| |_| |", " \\___/ "],
24
+ R: [" ____ ", "| _ \\ ", "| |_) |", "| _ < ", "|_| \\_\\"],
25
+ B: [" ____ ", "| __ ) ", "| _ \\ ", "| |_) |", "|____/ "],
26
+ S: [" ____ ", "/ ___| ", "\\___ \\ ", " ___) |", "|____/ "],
27
+ };
20
28
  function color(text, tone, bold = false) {
21
29
  if (!text)
22
30
  return text;
@@ -87,16 +95,14 @@ function renderPanelPlain(title, lines) {
87
95
  }
88
96
  function mastheadArt(columns) {
89
97
  if ((columns ?? 88) >= 74) {
90
- return [
91
- " .----------------------------.",
92
- " .--' O U R O B O R O S '--.",
93
- " .' .--------------------. '.",
94
- " / / .--------------. \\ \\",
95
- " \\ \\ '--------------' / /",
96
- " '. '--------------------' .'",
97
- " '--._ _.--'",
98
- " '------------------------'",
99
- ];
98
+ const rows = Array.from({ length: 5 }, () => []);
99
+ for (const letter of MASTHEAD_WORD.split("")) {
100
+ const glyph = CLASSIC_WORDMARK_GLYPHS[letter];
101
+ for (const [index, line] of glyph.entries()) {
102
+ rows[index].push(line);
103
+ }
104
+ }
105
+ return rows.map((row) => row.join(" "));
100
106
  }
101
107
  return [
102
108
  " O U R O B O R O S",
@@ -108,7 +114,7 @@ function renderOuroMasthead(options) {
108
114
  const subtitle = options.subtitle ?? "the house wakes when called";
109
115
  const branded = [
110
116
  ...lines,
111
- "OUROBOROS",
117
+ MASTHEAD_WORD,
112
118
  subtitle,
113
119
  ];
114
120
  if (!options.isTTY) {
@@ -116,7 +122,7 @@ function renderOuroMasthead(options) {
116
122
  }
117
123
  const ttyLines = [
118
124
  ...lines.map((line, index) => color(line, index < 2 ? GLOW : SCALE, true)),
119
- color("OUROBOROS", BONE, true),
125
+ color(MASTHEAD_WORD, BONE, true),
120
126
  color(subtitle, MIST),
121
127
  ];
122
128
  return `${ttyLines.join("\n")}\n`;
@@ -33,7 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.DEFAULT_VAULT_SERVER_URL = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = exports.PROVIDER_CREDENTIALS = void 0;
36
+ exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.LEGACY_VAULT_SERVER_URL_ALIASES = exports.DEFAULT_VAULT_SERVER_URL = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = exports.PROVIDER_CREDENTIALS = void 0;
37
+ exports.normalizeVaultServerUrl = normalizeVaultServerUrl;
38
+ exports.getVaultServerUrlCandidates = getVaultServerUrlCandidates;
37
39
  exports.defaultStableVaultEmail = defaultStableVaultEmail;
38
40
  exports.resolveVaultConfig = resolveVaultConfig;
39
41
  exports.normalizeSenses = normalizeSenses;
@@ -77,6 +79,40 @@ exports.DEFAULT_AGENT_PHRASES = {
77
79
  followup: ["processing"],
78
80
  };
79
81
  exports.DEFAULT_VAULT_SERVER_URL = "https://vault.ouroboros.bot";
82
+ exports.LEGACY_VAULT_SERVER_URL_ALIASES = [
83
+ "https://vault.ouro.bot",
84
+ "https://ouro-vault.gentleflower-74452a1e.eastus2.azurecontainerapps.io",
85
+ ];
86
+ function normalizeVaultServerUrl(serverUrl) {
87
+ const trimmed = serverUrl.trim();
88
+ const withoutTrailingSlash = trimmed.replace(/\/+$/, "");
89
+ if (!withoutTrailingSlash) {
90
+ return exports.DEFAULT_VAULT_SERVER_URL;
91
+ }
92
+ if (exports.LEGACY_VAULT_SERVER_URL_ALIASES.includes(withoutTrailingSlash)) {
93
+ return exports.DEFAULT_VAULT_SERVER_URL;
94
+ }
95
+ return withoutTrailingSlash;
96
+ }
97
+ function getVaultServerUrlCandidates(serverUrl) {
98
+ const raw = serverUrl.trim();
99
+ const withoutTrailingSlash = raw.replace(/\/+$/, "");
100
+ const normalized = normalizeVaultServerUrl(serverUrl);
101
+ const candidates = [normalized];
102
+ for (const candidate of [withoutTrailingSlash, raw]) {
103
+ if (candidate && !candidates.includes(candidate)) {
104
+ candidates.push(candidate);
105
+ }
106
+ }
107
+ if (normalized === exports.DEFAULT_VAULT_SERVER_URL) {
108
+ for (const alias of exports.LEGACY_VAULT_SERVER_URL_ALIASES) {
109
+ if (!candidates.includes(alias)) {
110
+ candidates.push(alias);
111
+ }
112
+ }
113
+ }
114
+ return candidates;
115
+ }
80
116
  function defaultStableVaultEmail(agentName) {
81
117
  const local = agentName
82
118
  .toLowerCase()
@@ -91,7 +127,7 @@ function defaultStableVaultEmail(agentName) {
91
127
  function resolveVaultConfig(agentName, config) {
92
128
  return {
93
129
  email: config?.email ?? defaultStableVaultEmail(agentName),
94
- serverUrl: config?.serverUrl ?? exports.DEFAULT_VAULT_SERVER_URL,
130
+ serverUrl: normalizeVaultServerUrl(config?.serverUrl ?? exports.DEFAULT_VAULT_SERVER_URL),
95
131
  };
96
132
  }
97
133
  exports.DEFAULT_AGENT_SENSES = {
@@ -48,6 +48,7 @@ const fs = __importStar(require("node:fs"));
48
48
  const os = __importStar(require("node:os"));
49
49
  const path = __importStar(require("node:path"));
50
50
  const runtime_1 = require("../nerves/runtime");
51
+ const identity_1 = require("../heart/identity");
51
52
  const VAULT_UNLOCK_SERVICE = "ouro.vault";
52
53
  const CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX = "credential vault is not configured in ";
53
54
  const PLAINTEXT_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock");
@@ -68,6 +69,19 @@ function vaultKey(config) {
68
69
  function vaultLabel(config) {
69
70
  return `${config.email} at ${config.serverUrl}`;
70
71
  }
72
+ function canonicalizeVaultUnlockConfig(config) {
73
+ return {
74
+ ...config,
75
+ serverUrl: (0, identity_1.normalizeVaultServerUrl)(config.serverUrl),
76
+ };
77
+ }
78
+ function vaultConfigCandidates(config) {
79
+ const canonical = canonicalizeVaultUnlockConfig(config);
80
+ return (0, identity_1.getVaultServerUrlCandidates)(config.serverUrl).map((serverUrl) => ({
81
+ ...canonical,
82
+ serverUrl,
83
+ }));
84
+ }
71
85
  function plaintextUnlockPath(config, deps) {
72
86
  const digest = crypto.createHash("sha256").update(vaultKey(config)).digest("hex").slice(0, 24);
73
87
  return path.join(homeDir(deps), PLAINTEXT_UNLOCK_DIR, `${digest}.secret`);
@@ -179,6 +193,7 @@ function validateStoreKind(store) {
179
193
  return requested;
180
194
  }
181
195
  function resolveVaultUnlockStore(config, deps = {}) {
196
+ const canonicalConfig = canonicalizeVaultUnlockConfig(config);
182
197
  const requested = validateStoreKind(deps.store);
183
198
  const currentPlatform = platform(deps);
184
199
  if (requested === "macos-keychain") {
@@ -200,33 +215,89 @@ function resolveVaultUnlockStore(config, deps = {}) {
200
215
  if (currentPlatform !== "win32") {
201
216
  throw new Error(`windows-dpapi unlock store is only available on Windows; this machine is ${currentPlatform}.`);
202
217
  }
203
- return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
218
+ return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(canonicalConfig, deps) };
204
219
  }
205
220
  if (requested === "plaintext-file") {
206
- return { kind: "plaintext-file", secure: false, location: plaintextUnlockPath(config, deps) };
221
+ return { kind: "plaintext-file", secure: false, location: plaintextUnlockPath(canonicalConfig, deps) };
207
222
  }
208
223
  if (currentPlatform === "darwin") {
209
224
  return { kind: "macos-keychain", secure: true, location: "macOS Keychain" };
210
225
  }
211
226
  if (currentPlatform === "win32") {
212
- return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(config, deps) };
227
+ return { kind: "windows-dpapi", secure: true, location: windowsDpapiUnlockPath(canonicalConfig, deps) };
213
228
  }
214
229
  if (currentPlatform === "linux" && commandExists("secret-tool", deps)) {
215
230
  return { kind: "linux-secret-service", secure: true, location: "Secret Service via secret-tool" };
216
231
  }
217
- throw new Error(missingSecureStoreMessage(config));
232
+ throw new Error(missingSecureStoreMessage(canonicalConfig));
218
233
  }
219
- function readFromMacosKeychain(config, deps) {
234
+ function readFromMacosKeychainExact(accountKey, deps) {
220
235
  const result = spawnSync(deps)("security", [
221
236
  "find-generic-password",
222
237
  "-s",
223
238
  VAULT_UNLOCK_SERVICE,
224
239
  "-a",
225
- vaultKey(config),
240
+ accountKey,
226
241
  "-w",
227
242
  ], { encoding: "utf8" });
228
243
  const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
229
- return result.status === 0 && secret ? secret : null;
244
+ if (result.status === 0) {
245
+ return secret || null;
246
+ }
247
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
248
+ const error = result.error instanceof Error ? result.error.message : "";
249
+ const detail = stderr || error;
250
+ if (!detail || /could not be found in the keychain/i.test(detail)) {
251
+ return null;
252
+ }
253
+ throw new Error(`failed to read vault unlock secret from macOS Keychain: ${detail}`);
254
+ }
255
+ function noteVaultUnlockSelfHeal(config, storeKind, sourceServerUrl) {
256
+ (0, runtime_1.emitNervesEvent)({
257
+ component: "repertoire",
258
+ event: "repertoire.vault_unlock_self_healed",
259
+ message: "rewrote local unlock material using canonical vault coordinates",
260
+ meta: {
261
+ store: storeKind,
262
+ email: config.email,
263
+ sourceServerUrl,
264
+ targetServerUrl: config.serverUrl,
265
+ },
266
+ });
267
+ }
268
+ function warnVaultUnlockSelfHealFailure(config, storeKind, sourceServerUrl, error) {
269
+ (0, runtime_1.emitNervesEvent)({
270
+ level: "warn",
271
+ component: "repertoire",
272
+ event: "repertoire.vault_unlock_self_heal_failed",
273
+ message: "failed to rewrite local unlock material using canonical vault coordinates",
274
+ meta: {
275
+ store: storeKind,
276
+ email: config.email,
277
+ sourceServerUrl,
278
+ targetServerUrl: config.serverUrl,
279
+ error: error instanceof Error ? error.message : String(error),
280
+ },
281
+ });
282
+ }
283
+ function readFromMacosKeychain(config, deps) {
284
+ const candidates = vaultConfigCandidates(config);
285
+ for (const [index, candidate] of candidates.entries()) {
286
+ const secret = readFromMacosKeychainExact(vaultKey(candidate), deps);
287
+ if (!secret)
288
+ continue;
289
+ if (index > 0) {
290
+ try {
291
+ writeToMacosKeychain(config, secret, deps);
292
+ noteVaultUnlockSelfHeal(config, "macos-keychain", candidate.serverUrl);
293
+ }
294
+ catch (error) {
295
+ warnVaultUnlockSelfHealFailure(config, "macos-keychain", candidate.serverUrl, error);
296
+ }
297
+ }
298
+ return secret;
299
+ }
300
+ return null;
230
301
  }
231
302
  function writeToMacosKeychain(config, secret, deps) {
232
303
  const result = spawnSync(deps)("security", [
@@ -244,16 +315,44 @@ function writeToMacosKeychain(config, secret, deps) {
244
315
  throw new Error(`failed to store vault unlock secret in macOS Keychain${stderr ? `: ${stderr}` : ""}`);
245
316
  }
246
317
  }
247
- function readFromLinuxSecretService(config, deps) {
318
+ function readFromLinuxSecretServiceExact(accountKey, deps) {
248
319
  const result = spawnSync(deps)("secret-tool", [
249
320
  "lookup",
250
321
  "service",
251
322
  VAULT_UNLOCK_SERVICE,
252
323
  "account",
253
- vaultKey(config),
324
+ accountKey,
254
325
  ], { encoding: "utf8" });
255
326
  const secret = typeof result.stdout === "string" ? result.stdout.trim() : "";
256
- return result.status === 0 && secret ? secret : null;
327
+ if (result.status === 0) {
328
+ return secret || null;
329
+ }
330
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
331
+ const error = result.error instanceof Error ? result.error.message : "";
332
+ const detail = stderr || error;
333
+ if (!detail || /not found/i.test(detail)) {
334
+ return null;
335
+ }
336
+ throw new Error(`failed to read vault unlock secret from Linux Secret Service: ${detail}`);
337
+ }
338
+ function readFromLinuxSecretService(config, deps) {
339
+ const candidates = vaultConfigCandidates(config);
340
+ for (const [index, candidate] of candidates.entries()) {
341
+ const secret = readFromLinuxSecretServiceExact(vaultKey(candidate), deps);
342
+ if (!secret)
343
+ continue;
344
+ if (index > 0) {
345
+ try {
346
+ writeToLinuxSecretService(config, secret, deps);
347
+ noteVaultUnlockSelfHeal(config, "linux-secret-service", candidate.serverUrl);
348
+ }
349
+ catch (error) {
350
+ warnVaultUnlockSelfHealFailure(config, "linux-secret-service", candidate.serverUrl, error);
351
+ }
352
+ }
353
+ return secret;
354
+ }
355
+ return null;
257
356
  }
258
357
  function writeToLinuxSecretService(config, secret, deps) {
259
358
  const result = spawnSync(deps)("secret-tool", [
@@ -306,7 +405,7 @@ if ($payload.mode -eq "protect") {
306
405
  }
307
406
  return typeof result.stdout === "string" ? result.stdout : "";
308
407
  }
309
- function readFromWindowsDpapi(config, deps) {
408
+ function readFromWindowsDpapiExact(config, deps) {
310
409
  const filePath = windowsDpapiUnlockPath(config, deps);
311
410
  if (!fs.existsSync(filePath))
312
411
  return null;
@@ -316,13 +415,32 @@ function readFromWindowsDpapi(config, deps) {
316
415
  const secret = runWindowsDpapi("unprotect", { ciphertext }, deps).trim();
317
416
  return secret || null;
318
417
  }
418
+ function readFromWindowsDpapi(config, deps) {
419
+ const candidates = vaultConfigCandidates(config);
420
+ for (const [index, candidate] of candidates.entries()) {
421
+ const secret = readFromWindowsDpapiExact(candidate, deps);
422
+ if (!secret)
423
+ continue;
424
+ if (index > 0) {
425
+ try {
426
+ writeToWindowsDpapi(config, secret, deps);
427
+ noteVaultUnlockSelfHeal(config, "windows-dpapi", candidate.serverUrl);
428
+ }
429
+ catch (error) {
430
+ warnVaultUnlockSelfHealFailure(config, "windows-dpapi", candidate.serverUrl, error);
431
+ }
432
+ }
433
+ return secret;
434
+ }
435
+ return null;
436
+ }
319
437
  function writeToWindowsDpapi(config, secret, deps) {
320
438
  const filePath = windowsDpapiUnlockPath(config, deps);
321
439
  const ciphertext = runWindowsDpapi("protect", { secret }, deps).trim();
322
440
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
323
441
  fs.writeFileSync(filePath, `${ciphertext}\n`, "utf8");
324
442
  }
325
- function readFromPlaintextFile(config, deps) {
443
+ function readFromPlaintextFileExact(config, deps) {
326
444
  const filePath = plaintextUnlockPath(config, deps);
327
445
  if (!fs.existsSync(filePath))
328
446
  return null;
@@ -335,6 +453,25 @@ function readFromPlaintextFile(config, deps) {
335
453
  const secret = fs.readFileSync(filePath, "utf8").trim();
336
454
  return secret || null;
337
455
  }
456
+ function readFromPlaintextFile(config, deps) {
457
+ const candidates = vaultConfigCandidates(config);
458
+ for (const [index, candidate] of candidates.entries()) {
459
+ const secret = readFromPlaintextFileExact(candidate, deps);
460
+ if (!secret)
461
+ continue;
462
+ if (index > 0) {
463
+ try {
464
+ writeToPlaintextFile(config, secret, deps);
465
+ noteVaultUnlockSelfHeal(config, "plaintext-file", candidate.serverUrl);
466
+ }
467
+ catch (error) {
468
+ warnVaultUnlockSelfHealFailure(config, "plaintext-file", candidate.serverUrl, error);
469
+ }
470
+ }
471
+ return secret;
472
+ }
473
+ return null;
474
+ }
338
475
  function writeToPlaintextFile(config, secret, deps) {
339
476
  const filePath = plaintextUnlockPath(config, deps);
340
477
  fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
@@ -369,10 +506,11 @@ function writeToStore(config, store, secret, deps) {
369
506
  writeToPlaintextFile(config, secret, deps);
370
507
  }
371
508
  function readVaultUnlockSecret(config, deps = {}) {
372
- const store = resolveVaultUnlockStore(config, deps);
373
- const secret = readFromStore(config, store, deps);
509
+ const canonicalConfig = canonicalizeVaultUnlockConfig(config);
510
+ const store = resolveVaultUnlockStore(canonicalConfig, deps);
511
+ const secret = readFromStore(canonicalConfig, store, deps);
374
512
  if (!secret) {
375
- throw new Error(lockedMessage(config, store));
513
+ throw new Error(lockedMessage(canonicalConfig, store));
376
514
  }
377
515
  (0, runtime_1.emitNervesEvent)({
378
516
  component: "repertoire",
@@ -383,12 +521,13 @@ function readVaultUnlockSecret(config, deps = {}) {
383
521
  return { secret, store };
384
522
  }
385
523
  function storeVaultUnlockSecret(config, secret, deps = {}) {
524
+ const canonicalConfig = canonicalizeVaultUnlockConfig(config);
386
525
  const trimmed = secret.trim();
387
526
  if (!trimmed) {
388
527
  throw new Error("vault unlock secret is required");
389
528
  }
390
- const store = resolveVaultUnlockStore(config, deps);
391
- writeToStore(config, store, trimmed, deps);
529
+ const store = resolveVaultUnlockStore(canonicalConfig, deps);
530
+ writeToStore(canonicalConfig, store, trimmed, deps);
392
531
  (0, runtime_1.emitNervesEvent)({
393
532
  component: "repertoire",
394
533
  event: "repertoire.vault_unlock_stored",
@@ -399,15 +538,16 @@ function storeVaultUnlockSecret(config, secret, deps = {}) {
399
538
  }
400
539
  function getVaultUnlockStatus(config, deps = {}) {
401
540
  try {
402
- const store = resolveVaultUnlockStore(config, deps);
403
- const stored = !!readFromStore(config, store, deps);
541
+ const canonicalConfig = canonicalizeVaultUnlockConfig(config);
542
+ const store = resolveVaultUnlockStore(canonicalConfig, deps);
543
+ const stored = !!readFromStore(canonicalConfig, store, deps);
404
544
  return {
405
545
  configured: true,
406
546
  stored,
407
547
  store,
408
548
  fix: stored
409
549
  ? "Vault unlock secret is available on this machine."
410
- : lockedMessage(config, store),
550
+ : lockedMessage(canonicalConfig, store),
411
551
  };
412
552
  }
413
553
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.440",
3
+ "version": "0.1.0-alpha.441",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",