agentbnb 9.2.0 → 9.2.1

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.
Files changed (49) hide show
  1. package/README.md +22 -0
  2. package/dist/{card-VVXNKHDX.js → card-L3ZPPBVI.js} +2 -2
  3. package/dist/{chunk-Z7XWQ63B.js → chunk-2KSRFDKF.js} +1 -1
  4. package/dist/{chunk-WEZ7PSOE.js → chunk-563ZZUOA.js} +56 -22
  5. package/dist/chunk-5FXLZ5FX.js +16 -0
  6. package/dist/{chunk-ELFGYC22.js → chunk-7YJOBVWN.js} +3 -0
  7. package/dist/{chunk-V5TJXK3F.js → chunk-COOBXNXU.js} +5 -5
  8. package/dist/{chunk-EC6DIVE5.js → chunk-CPSV5WR2.js} +1 -1
  9. package/dist/{process-guard-6324CZDC.js → chunk-DG4FQ4MD.js} +1 -1
  10. package/dist/{chunk-MPS4RE7T.js → chunk-DT2IEL5U.js} +11 -0
  11. package/dist/chunk-EMVVFP2L.js +54 -0
  12. package/dist/{chunk-FK54LVDR.js → chunk-FVLHEI3Y.js} +5 -5
  13. package/dist/chunk-GIK2AZQH.js +23 -0
  14. package/dist/{chunk-B6AKTLXB.js → chunk-NISX3N7K.js} +16 -7
  15. package/dist/{chunk-CMGJ52SX.js → chunk-NLGLGR2K.js} +1 -1
  16. package/dist/{chunk-RNALIVRR.js → chunk-OFIRWD6B.js} +1 -1
  17. package/dist/{chunk-GGRH5PCD.js → chunk-OI46BKQF.js} +9 -9
  18. package/dist/{chunk-MQIT2F5V.js → chunk-OTAZIF65.js} +1 -1
  19. package/dist/{chunk-KKFP5Y2Z.js → chunk-OXU4QJSZ.js} +2 -2
  20. package/dist/{chunk-53Q2HHHH.js → chunk-PFAEZI32.js} +9 -9
  21. package/dist/{chunk-D7NH6YLM.js → chunk-UJXDBOKV.js} +12 -1
  22. package/dist/{chunk-UPWAXWY2.js → chunk-VDYHCI5F.js} +22 -1
  23. package/dist/{chunk-QG2LLVXP.js → chunk-WA23XRTN.js} +1 -1
  24. package/dist/cli/index.js +787 -97
  25. package/dist/{conduct-QLWXU2ZU.js → conduct-65BGO2EU.js} +13 -11
  26. package/dist/{conduct-JRLLA4PB.js → conduct-AALDEKTH.js} +13 -11
  27. package/dist/{conductor-mode-PFO2VLH6.js → conductor-mode-6S6ADNLW.js} +15 -15
  28. package/dist/{conductor-mode-66IITI4I.js → conductor-mode-HJHU4XLT.js} +2 -2
  29. package/dist/{credits-action-XERUEDF3.js → credits-action-CLLPNRDT.js} +15 -6
  30. package/dist/{execute-NOQVN7ZG.js → execute-R5STYWLD.js} +10 -10
  31. package/dist/{execute-YBNCDAOX.js → execute-YMPHTJPN.js} +2 -2
  32. package/dist/{openclaw-setup-4RIZRMXA.js → openclaw-setup-HUOBTGN4.js} +14 -12
  33. package/dist/{openclaw-skills-TQ2JVBRM.js → openclaw-skills-74372B6I.js} +2 -2
  34. package/dist/process-guard-IUMZ2GSD.js +8 -0
  35. package/dist/{publish-capability-2FMD3K6Z.js → publish-capability-OYXXXYAU.js} +3 -3
  36. package/dist/remote-registry-5TM7DMCO.js +16 -0
  37. package/dist/{request-EYN4CVXC.js → request-NEA66RCW.js} +63 -21
  38. package/dist/{serve-skill-NWERGVH5.js → serve-skill-VKNRBVWE.js} +16 -15
  39. package/dist/{server-UPOPLZ24.js → server-LNT4YQZ7.js} +21 -17
  40. package/dist/{service-coordinator-ZOZTW2U6.js → service-coordinator-RE2KPWO4.js} +91 -31
  41. package/dist/{session-action-OSBZB4TX.js → session-action-UBWJTQVQ.js} +17 -2
  42. package/dist/skills/agentbnb/bootstrap.js +213 -24
  43. package/dist/{store-74EWU77V.js → store-GJJFFEQZ.js} +2 -2
  44. package/dist/{vc-action-A6VBKERF.js → vc-action-72TQVMY2.js} +13 -3
  45. package/package.json +12 -18
  46. package/skills/agentbnb/install.sh +0 -0
  47. package/dist/{chunk-C56X7EFJ.js → chunk-QE42IJC4.js} +3 -3
  48. package/dist/{daemon-ETXXE4IS.js → daemon-OM2K3U7J.js} +1 -1
  49. package/dist/{did-action-ODWTBVXL.js → did-action-ERXWCVEJ.js} +1 -1
package/dist/cli/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-TA73FIZU.js";
5
5
  import {
6
6
  syncCreditsFromRegistry
7
- } from "../chunk-KKFP5Y2Z.js";
7
+ } from "../chunk-OXU4QJSZ.js";
8
8
  import {
9
9
  resolveSelfCli
10
10
  } from "../chunk-7S4ZLFVI.js";
@@ -14,44 +14,54 @@ import {
14
14
  injectHeartbeatSection,
15
15
  performInit,
16
16
  publishFromSoulV2
17
- } from "../chunk-WEZ7PSOE.js";
17
+ } from "../chunk-563ZZUOA.js";
18
18
  import {
19
- getPricingStats
20
- } from "../chunk-UPWAXWY2.js";
19
+ getPricingStats,
20
+ probeRegistry
21
+ } from "../chunk-VDYHCI5F.js";
22
+ import {
23
+ TimeoutError,
24
+ withTimeout
25
+ } from "../chunk-GIK2AZQH.js";
26
+ import {
27
+ shouldSkipNetwork
28
+ } from "../chunk-5FXLZ5FX.js";
21
29
  import {
22
30
  createLedger
23
- } from "../chunk-Z7XWQ63B.js";
31
+ } from "../chunk-2KSRFDKF.js";
24
32
  import {
25
33
  ensureIdentity
26
34
  } from "../chunk-AA25Z6FW.js";
27
- import "../chunk-RNALIVRR.js";
35
+ import "../chunk-OFIRWD6B.js";
28
36
  import "../chunk-RJNKX347.js";
37
+ import {
38
+ ProcessGuard
39
+ } from "../chunk-DG4FQ4MD.js";
40
+ import {
41
+ parseSkillsFile
42
+ } from "../chunk-WX3GZVFG.js";
29
43
  import {
30
44
  AutoRequestor,
31
45
  requestViaTemporaryRelay
32
- } from "../chunk-GGRH5PCD.js";
46
+ } from "../chunk-OI46BKQF.js";
33
47
  import "../chunk-5PV5YCSN.js";
34
- import "../chunk-FK54LVDR.js";
35
- import {
36
- fetchRemoteCards,
37
- mergeResults
38
- } from "../chunk-ELFGYC22.js";
48
+ import "../chunk-FVLHEI3Y.js";
39
49
  import {
40
50
  BudgetManager,
41
51
  DEFAULT_BUDGET_CONFIG
42
- } from "../chunk-QG2LLVXP.js";
52
+ } from "../chunk-WA23XRTN.js";
43
53
  import {
44
54
  DEFAULT_AUTONOMY_CONFIG
45
55
  } from "../chunk-G5WKW3ED.js";
46
56
  import {
47
57
  filterCards,
48
58
  searchCards
49
- } from "../chunk-EC6DIVE5.js";
59
+ } from "../chunk-CPSV5WR2.js";
50
60
  import {
51
61
  getBalance,
52
62
  getTransactions,
53
63
  openCreditDb
54
- } from "../chunk-D7NH6YLM.js";
64
+ } from "../chunk-UJXDBOKV.js";
55
65
  import {
56
66
  attachCanonicalAgentId,
57
67
  deleteCard,
@@ -59,7 +69,8 @@ import {
59
69
  insertCard,
60
70
  listCards,
61
71
  openDatabase
62
- } from "../chunk-C56X7EFJ.js";
72
+ } from "../chunk-QE42IJC4.js";
73
+ import "../chunk-4XTYT4JW.js";
63
74
  import "../chunk-GZUTU6IZ.js";
64
75
  import "../chunk-UPNREF4L.js";
65
76
  import "../chunk-Q5OFZ2JR.js";
@@ -74,9 +85,6 @@ import {
74
85
  } from "../chunk-65GNX2KC.js";
75
86
  import "../chunk-YDGXKH2T.js";
76
87
  import "../chunk-J4RFJVXI.js";
77
- import {
78
- AnyCardSchema
79
- } from "../chunk-UVCNMRPS.js";
80
88
  import {
81
89
  findPeer,
82
90
  loadPeers,
@@ -88,14 +96,20 @@ import {
88
96
  loadConfig,
89
97
  saveConfig
90
98
  } from "../chunk-3XPBFF6H.js";
91
- import "../chunk-4XTYT4JW.js";
99
+ import {
100
+ fetchRemoteCards,
101
+ mergeResults
102
+ } from "../chunk-7YJOBVWN.js";
103
+ import {
104
+ AnyCardSchema
105
+ } from "../chunk-UVCNMRPS.js";
92
106
  import "../chunk-3RG5ZIWI.js";
93
107
 
94
108
  // src/cli/index.ts
95
109
  import { Command } from "commander";
96
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
110
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
97
111
  import { randomUUID } from "crypto";
98
- import { join as join2 } from "path";
112
+ import { join as join3 } from "path";
99
113
  import { networkInterfaces } from "os";
100
114
  import { createInterface } from "readline";
101
115
 
@@ -240,9 +254,9 @@ Skills: ${skills2.skillCount} skill(s) in ${skills2.path}`);
240
254
  let daemonStatus = { running: false, reason: "skipped" };
241
255
  if (!skipServe) {
242
256
  try {
243
- const { ProcessGuard } = await import("../process-guard-6324CZDC.js");
244
- const { ServiceCoordinator } = await import("../service-coordinator-ZOZTW2U6.js");
245
- const guard = new ProcessGuard(join(initResult.configDir, ".pid"));
257
+ const { ProcessGuard: ProcessGuard2 } = await import("../process-guard-IUMZ2GSD.js");
258
+ const { ServiceCoordinator } = await import("../service-coordinator-RE2KPWO4.js");
259
+ const guard = new ProcessGuard2(join(initResult.configDir, ".pid"));
246
260
  const coordinator = new ServiceCoordinator(initResult.config, guard);
247
261
  const result = await coordinator.ensureRunning({
248
262
  port: initResult.config.gateway_port,
@@ -299,11 +313,625 @@ Skills: ${skills2.skillCount} skill(s) in ${skills2.path}`);
299
313
  console.log(" Consumer: In Claude Code, use agentbnb_discover and agentbnb_request");
300
314
  console.log(" Provider: Your claude -p skills are live and accepting requests");
301
315
  console.log(` Dashboard: http://localhost:7701/hub/#/myagent`);
316
+ console.log("");
317
+ console.log(" Verify: Run `agentbnb doctor` to validate your provider setup");
318
+ }
319
+ }
320
+
321
+ // src/cli/doctor.ts
322
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
323
+ import { join as join2 } from "path";
324
+ function checkIdentity(configDir, config) {
325
+ try {
326
+ loadKeyPair(configDir);
327
+ } catch {
328
+ return {
329
+ name: "Identity",
330
+ status: "fail",
331
+ message: "keypair not found",
332
+ context: "Without an identity, your agent cannot sign requests or publish cards.",
333
+ fix: "Run `agentbnb init`"
334
+ };
335
+ }
336
+ let did2 = "";
337
+ try {
338
+ const identityPath = join2(configDir, "identity.json");
339
+ if (existsSync2(identityPath)) {
340
+ const raw = JSON.parse(readFileSync2(identityPath, "utf-8"));
341
+ did2 = raw.did ?? `did:agentbnb:${raw.agent_id ?? "unknown"}`;
342
+ }
343
+ } catch {
344
+ }
345
+ const label = did2 ? `${config.owner} (${did2})` : config.owner;
346
+ return { name: "Identity", status: "pass", message: label };
347
+ }
348
+ function checkSkills(configDir) {
349
+ const skillsPath = join2(configDir, "skills.yaml");
350
+ if (!existsSync2(skillsPath)) {
351
+ return {
352
+ name: "Skills",
353
+ status: "fail",
354
+ message: "skills.yaml not found",
355
+ context: "Skills define what your agent can do for hire.",
356
+ fix: "Run `agentbnb quickstart` to generate default skills"
357
+ };
358
+ }
359
+ let skills2;
360
+ try {
361
+ const content = readFileSync2(skillsPath, "utf-8");
362
+ skills2 = parseSkillsFile(content);
363
+ } catch (err) {
364
+ const msg = err instanceof Error ? err.message : String(err);
365
+ if (msg.includes("environment variable") || msg.includes("env var")) {
366
+ return {
367
+ name: "Skills",
368
+ status: "warn",
369
+ message: `skills.yaml has unresolved environment variables`,
370
+ context: "Skills exist but may not execute until env vars are set.",
371
+ fix: `Check missing env vars: ${msg}`
372
+ };
373
+ }
374
+ return {
375
+ name: "Skills",
376
+ status: "fail",
377
+ message: `skills.yaml parse error: ${msg}`,
378
+ context: "Malformed skills.yaml prevents your agent from executing any skill.",
379
+ fix: "Fix syntax errors in ~/.agentbnb/skills.yaml"
380
+ };
381
+ }
382
+ if (skills2.length === 0) {
383
+ return {
384
+ name: "Skills",
385
+ status: "fail",
386
+ message: "skills.yaml is empty (0 skills)",
387
+ context: "Without skills, there is nothing for other agents to hire.",
388
+ fix: "Add at least one skill to ~/.agentbnb/skills.yaml"
389
+ };
390
+ }
391
+ return { name: "Skills", status: "pass", message: `${skills2.length} skills in ~/.agentbnb/skills.yaml` };
392
+ }
393
+ async function checkGateway(configDir) {
394
+ const guard = new ProcessGuard(join2(configDir, ".pid"));
395
+ const meta = guard.getRunningMeta();
396
+ if (!meta) {
397
+ return {
398
+ result: {
399
+ name: "Gateway",
400
+ status: "fail",
401
+ message: "not running",
402
+ context: "Your skills cannot receive requests without the daemon.",
403
+ fix: "Run `agentbnb serve`"
404
+ },
405
+ meta: null
406
+ };
407
+ }
408
+ const healthy = await probeGatewayHealth(meta.port);
409
+ if (!healthy) {
410
+ return {
411
+ result: {
412
+ name: "Gateway",
413
+ status: "warn",
414
+ message: `pid ${meta.pid} exists but /health is not responding on port ${meta.port}`,
415
+ context: "The daemon process is running but may not be accepting requests.",
416
+ fix: "Restart with `agentbnb serve --restart` or check logs"
417
+ },
418
+ meta
419
+ };
420
+ }
421
+ return {
422
+ result: {
423
+ name: "Gateway",
424
+ status: "pass",
425
+ message: `running on port ${meta.port} (pid ${meta.pid})`
426
+ },
427
+ meta
428
+ };
429
+ }
430
+ function checkCards(config) {
431
+ let localCount = 0;
432
+ try {
433
+ const db = openDatabase(config.db_path);
434
+ try {
435
+ const cards = listCards(db);
436
+ localCount = cards.filter((c) => c.owner === config.owner).length;
437
+ } finally {
438
+ db.close();
439
+ }
440
+ } catch {
441
+ return {
442
+ result: {
443
+ name: "Cards",
444
+ status: "fail",
445
+ message: "cannot read registry database",
446
+ context: "Capability cards make your skills discoverable to other agents.",
447
+ fix: "Run `agentbnb init` to recreate the database"
448
+ },
449
+ localCount: 0
450
+ };
451
+ }
452
+ if (localCount === 0) {
453
+ return {
454
+ result: {
455
+ name: "Cards",
456
+ status: "fail",
457
+ message: "no capability cards published",
458
+ context: "Without published cards, no agent can discover or hire your skills.",
459
+ fix: "Run `agentbnb publish-skills` or `agentbnb serve` to auto-publish"
460
+ },
461
+ localCount: 0
462
+ };
463
+ }
464
+ return {
465
+ result: {
466
+ name: "Cards",
467
+ status: "pass",
468
+ // upgraded or downgraded by remote check below
469
+ message: `${localCount} cards published locally`
470
+ },
471
+ localCount
472
+ };
473
+ }
474
+ async function checkCardsRemote(config, localCount) {
475
+ if (!config.registry) {
476
+ return {
477
+ name: "Cards",
478
+ status: localCount > 0 ? "warn" : "fail",
479
+ message: localCount > 0 ? `${localCount} cards published locally \u2014 remote discoverability not verified (no registry configured)` : "no cards published",
480
+ context: localCount > 0 ? "Without a registry connection, other agents outside your network cannot discover you." : "Without published cards, no agent can discover or hire your skills.",
481
+ fix: localCount > 0 ? void 0 : "Run `agentbnb publish-skills`"
482
+ };
483
+ }
484
+ try {
485
+ const { fetchRemoteCards: fetchRemoteCards2 } = await import("../remote-registry-5TM7DMCO.js");
486
+ const remoteCards = await fetchRemoteCards2(config.registry, { q: config.owner }, 3e3);
487
+ const ownCards = remoteCards.filter((c) => c.owner === config.owner);
488
+ if (ownCards.length > 0) {
489
+ const host = extractHost(config.registry);
490
+ return {
491
+ name: "Cards",
492
+ status: "pass",
493
+ message: `${localCount} cards published, ${ownCards.length} verified discoverable on ${host}`
494
+ };
495
+ }
496
+ return {
497
+ name: "Cards",
498
+ status: "warn",
499
+ message: `${localCount} cards published locally \u2014 not yet discoverable on remote registry`,
500
+ context: "Cards are local but the relay has not propagated them yet. This may resolve after daemon restart.",
501
+ fix: "Restart daemon with `agentbnb serve` to push cards to registry"
502
+ };
503
+ } catch {
504
+ return {
505
+ name: "Cards",
506
+ status: "warn",
507
+ message: `${localCount} cards published locally \u2014 remote discoverability not verified (registry probe failed)`,
508
+ context: "Could not reach registry to verify discoverability."
509
+ };
510
+ }
511
+ }
512
+ function checkCredits(config) {
513
+ let balance = 0;
514
+ try {
515
+ const creditDb = openCreditDb(config.credit_db_path);
516
+ try {
517
+ balance = getBalance(creditDb, config.owner);
518
+ } finally {
519
+ creditDb.close();
520
+ }
521
+ } catch {
522
+ return {
523
+ name: "Credits",
524
+ status: "warn",
525
+ message: "cannot read credit database",
526
+ context: "Credit state is unknown but serving may still work.",
527
+ fix: "Run `agentbnb init` to recreate the database"
528
+ };
529
+ }
530
+ if (balance <= 0) {
531
+ return {
532
+ name: "Credits",
533
+ status: "warn",
534
+ message: "0 credits \u2014 serving still works, but `--test-hire` requires credits",
535
+ context: "You can receive hire requests at any balance. Credits are needed to hire others or run self-tests.",
536
+ fix: "Credits are earned by completing jobs. Run `agentbnb credits` for details"
537
+ };
538
+ }
539
+ return { name: "Credits", status: "pass", message: `${balance} credits available` };
540
+ }
541
+ async function checkRegistry(config) {
542
+ if (!config.registry) {
543
+ return {
544
+ name: "Registry",
545
+ status: "warn",
546
+ message: "no remote registry configured (local-only mode)",
547
+ context: "Without a registry, only local-network agents can discover you via mDNS."
548
+ };
549
+ }
550
+ const reachable = await probeRegistry(config.registry);
551
+ if (!reachable) {
552
+ const host2 = extractHost(config.registry);
553
+ return {
554
+ name: "Registry",
555
+ status: "fail",
556
+ message: `configured but unreachable (${host2})`,
557
+ context: "Remote agents cannot discover you if the registry is down.",
558
+ fix: "Check network connection, or verify registry URL with `agentbnb config get registry`"
559
+ };
560
+ }
561
+ const host = extractHost(config.registry);
562
+ return { name: "Registry", status: "pass", message: `connected to ${host}` };
563
+ }
564
+ function checkRelay(config, daemonRunning) {
565
+ if (!daemonRunning) {
566
+ return {
567
+ name: "Relay",
568
+ status: "fail",
569
+ message: "cannot connect without running daemon",
570
+ context: "Relay enables cross-network hiring \u2014 agents outside your LAN reach you through it.",
571
+ fix: "Start daemon first: `agentbnb serve`"
572
+ };
573
+ }
574
+ if (!config.registry) {
575
+ return {
576
+ name: "Relay",
577
+ status: "warn",
578
+ message: "no relay target configured (no registry URL)",
579
+ context: "Without a relay, only local-network agents can hire you."
580
+ };
581
+ }
582
+ return {
583
+ name: "Relay",
584
+ status: "warn",
585
+ message: "likely connected (inferred from daemon state \u2014 not directly verified)",
586
+ context: "Relay connectivity is inferred. A future update will add direct verification."
587
+ };
588
+ }
589
+ async function runTestHire(config, configDir, explicitSkillId) {
590
+ if (!config.registry) {
591
+ return {
592
+ success: false,
593
+ skillId: "",
594
+ selectionReason: "",
595
+ creditsSpent: 0,
596
+ latency_ms: 0,
597
+ error: "Test-hire requires a registry (relay) connection. Configure a registry URL with `agentbnb config set registry <url>`"
598
+ };
599
+ }
600
+ const reachable = await probeRegistry(config.registry);
601
+ if (!reachable) {
602
+ return {
603
+ success: false,
604
+ skillId: "",
605
+ selectionReason: "",
606
+ creditsSpent: 0,
607
+ latency_ms: 0,
608
+ error: `Registry unreachable (${extractHost(config.registry)}). Test-hire needs relay connectivity`
609
+ };
610
+ }
611
+ let cards;
612
+ try {
613
+ const db = openDatabase(config.db_path);
614
+ try {
615
+ cards = listCards(db).filter((c) => c.owner === config.owner);
616
+ } finally {
617
+ db.close();
618
+ }
619
+ } catch {
620
+ return { success: false, skillId: "", selectionReason: "", creditsSpent: 0, latency_ms: 0, error: "Cannot read registry database" };
621
+ }
622
+ if (cards.length === 0) {
623
+ return { success: false, skillId: "", selectionReason: "", creditsSpent: 0, latency_ms: 0, error: "No published capability cards" };
624
+ }
625
+ let skillConfigs = [];
626
+ try {
627
+ const skillsPath = join2(configDir, "skills.yaml");
628
+ if (existsSync2(skillsPath)) {
629
+ skillConfigs = parseSkillsFile(readFileSync2(skillsPath, "utf-8"));
630
+ }
631
+ } catch {
632
+ }
633
+ let selectedCard = cards[0];
634
+ let selectedSkillId = "";
635
+ let selectionReason = "";
636
+ let creditsCost = 0;
637
+ if (explicitSkillId) {
638
+ const found = findSkillInCards(cards, explicitSkillId);
639
+ if (!found) {
640
+ return { success: false, skillId: explicitSkillId, selectionReason: "explicit --skill flag", creditsSpent: 0, latency_ms: 0, error: `Skill "${explicitSkillId}" not found in published cards` };
641
+ }
642
+ selectedCard = found.card;
643
+ selectedSkillId = found.skillId;
644
+ creditsCost = found.credits;
645
+ selectionReason = "explicit --skill flag";
646
+ } else {
647
+ const safeCandidate = findSafeSkill(cards, skillConfigs);
648
+ if (safeCandidate) {
649
+ selectedCard = safeCandidate.card;
650
+ selectedSkillId = safeCandidate.skillId;
651
+ creditsCost = safeCandidate.credits;
652
+ selectionReason = safeCandidate.reason;
653
+ } else {
654
+ const cheapest = findCheapestSkill(cards);
655
+ selectedCard = cheapest.card;
656
+ selectedSkillId = cheapest.skillId;
657
+ creditsCost = cheapest.credits;
658
+ selectionReason = "cheapest available (no safe candidate found)";
659
+ }
660
+ }
661
+ try {
662
+ const creditDb = openCreditDb(config.credit_db_path);
663
+ try {
664
+ const balance = getBalance(creditDb, config.owner);
665
+ if (balance < creditsCost) {
666
+ return {
667
+ success: false,
668
+ skillId: selectedSkillId || selectedCard.id,
669
+ selectionReason,
670
+ creditsSpent: 0,
671
+ latency_ms: 0,
672
+ error: `Insufficient credits: need ${creditsCost}, have ${balance}. Credits are earned by completing jobs`
673
+ };
674
+ }
675
+ } finally {
676
+ creditDb.close();
677
+ }
678
+ } catch {
679
+ }
680
+ let agentId;
681
+ try {
682
+ const identity = ensureIdentity(configDir, config.owner);
683
+ agentId = identity.agent_id;
684
+ } catch {
685
+ }
686
+ const start = Date.now();
687
+ try {
688
+ await requestViaTemporaryRelay({
689
+ registryUrl: config.registry,
690
+ agent_id: agentId,
691
+ owner: config.owner,
692
+ token: config.token,
693
+ targetOwner: config.owner,
694
+ targetAgentId: agentId,
695
+ cardId: selectedCard.id,
696
+ skillId: selectedSkillId || void 0,
697
+ params: {
698
+ prompt: "Reply with exactly: DOCTOR_SMOKE_TEST_OK",
699
+ ...selectedSkillId ? { skill_id: selectedSkillId } : {},
700
+ requester: config.owner
701
+ },
702
+ timeoutMs: 6e4
703
+ });
704
+ return {
705
+ success: true,
706
+ skillId: selectedSkillId || selectedCard.name || selectedCard.id,
707
+ selectionReason,
708
+ creditsSpent: creditsCost,
709
+ latency_ms: Date.now() - start
710
+ };
711
+ } catch (err) {
712
+ return {
713
+ success: false,
714
+ skillId: selectedSkillId || selectedCard.name || selectedCard.id,
715
+ selectionReason,
716
+ creditsSpent: 0,
717
+ latency_ms: Date.now() - start,
718
+ error: err instanceof Error ? err.message : String(err)
719
+ };
720
+ }
721
+ }
722
+ async function probeGatewayHealth(port, timeoutMs = 2e3) {
723
+ const controller = new AbortController();
724
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
725
+ try {
726
+ const res = await fetch(`http://localhost:${port}/health`, { signal: controller.signal });
727
+ if (!res.ok) return false;
728
+ const body = await res.json();
729
+ return body.status === "ok";
730
+ } catch {
731
+ return false;
732
+ } finally {
733
+ clearTimeout(timer);
734
+ }
735
+ }
736
+ function extractHost(url) {
737
+ try {
738
+ return new URL(url).host;
739
+ } catch {
740
+ return url;
741
+ }
742
+ }
743
+ function findSkillInCards(cards, skillId) {
744
+ for (const card of cards) {
745
+ if (card.skills) {
746
+ const skill2 = card.skills.find((s) => s.id === skillId);
747
+ if (skill2) {
748
+ return { card, skillId: skill2.id, credits: skill2.pricing?.credits_per_call ?? 0, reason: "" };
749
+ }
750
+ }
751
+ if (card.id === skillId) {
752
+ return { card, skillId: "", credits: card.pricing?.credits_per_call ?? 0, reason: "" };
753
+ }
754
+ }
755
+ return null;
756
+ }
757
+ function findSafeSkill(cards, skillConfigs) {
758
+ const safeSkillIds = /* @__PURE__ */ new Set();
759
+ for (const sc of skillConfigs) {
760
+ if (sc.type === "command") {
761
+ const cmd = sc.command ?? "";
762
+ if (/\bclaude\b/.test(cmd) || /\becho\b/.test(cmd)) {
763
+ safeSkillIds.add(sc.id);
764
+ }
765
+ }
766
+ }
767
+ let best = null;
768
+ for (const card of cards) {
769
+ if (card.skills) {
770
+ for (const skill2 of card.skills) {
771
+ if (safeSkillIds.has(skill2.id)) {
772
+ const credits2 = skill2.pricing?.credits_per_call ?? 0;
773
+ if (!best || credits2 < best.credits) {
774
+ best = { card, skillId: skill2.id, credits: credits2, reason: "safe command-type skill" };
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+ if (!best) {
781
+ for (const sc of skillConfigs) {
782
+ if (safeSkillIds.has(sc.id)) {
783
+ for (const card of cards) {
784
+ const pricing = card.pricing;
785
+ const credits2 = pricing?.credits_per_call ?? 0;
786
+ if (!best || credits2 < best.credits) {
787
+ best = { card, skillId: sc.id, credits: credits2, reason: "safe command-type skill" };
788
+ }
789
+ }
790
+ }
791
+ }
792
+ }
793
+ return best;
794
+ }
795
+ function findCheapestSkill(cards) {
796
+ let best = { card: cards[0], skillId: "", credits: Infinity, reason: "" };
797
+ for (const card of cards) {
798
+ if (card.skills) {
799
+ for (const skill2 of card.skills) {
800
+ const credits2 = skill2.pricing?.credits_per_call ?? 0;
801
+ if (credits2 < best.credits) {
802
+ best = { card, skillId: skill2.id, credits: credits2, reason: "cheapest available" };
803
+ }
804
+ }
805
+ }
806
+ const cardCredits = card.pricing?.credits_per_call ?? 0;
807
+ if (cardCredits < best.credits) {
808
+ best = { card, skillId: "", credits: cardCredits, reason: "cheapest available" };
809
+ }
810
+ }
811
+ if (best.credits === Infinity) best.credits = 0;
812
+ return best;
813
+ }
814
+ async function runDoctor(opts) {
815
+ const configDir = getConfigDir();
816
+ const config = loadConfig();
817
+ if (!config) {
818
+ const report2 = {
819
+ checks: [{
820
+ name: "Identity",
821
+ status: "fail",
822
+ message: "not initialized",
823
+ context: "AgentBnB has not been set up on this machine.",
824
+ fix: "Run `agentbnb quickstart` to get started"
825
+ }],
826
+ passed: 0,
827
+ warnings: 0,
828
+ failed: 1,
829
+ total: 1
830
+ };
831
+ renderReport(report2, opts.json ?? false);
832
+ process.exit(1);
833
+ }
834
+ const checks = [];
835
+ checks.push(checkIdentity(configDir, config));
836
+ checks.push(checkSkills(configDir));
837
+ const gateway = await checkGateway(configDir);
838
+ checks.push(gateway.result);
839
+ const daemonRunning = gateway.result.status === "pass";
840
+ const cardsLocal = checkCards(config);
841
+ checks.push(checkCredits(config));
842
+ const registryResult = await checkRegistry(config);
843
+ checks.push(registryResult);
844
+ if (cardsLocal.localCount > 0 && registryResult.status === "pass") {
845
+ checks.splice(3, 0, await checkCardsRemote(config, cardsLocal.localCount));
846
+ } else if (cardsLocal.localCount > 0) {
847
+ checks.splice(3, 0, {
848
+ name: "Cards",
849
+ status: "warn",
850
+ message: `${cardsLocal.localCount} cards published locally \u2014 remote discoverability not verified`,
851
+ context: "Without remote registry verification, other agents may not find your skills."
852
+ });
853
+ } else {
854
+ checks.splice(3, 0, cardsLocal.result);
855
+ }
856
+ checks.push(checkRelay(config, daemonRunning));
857
+ let testHireResult;
858
+ if (opts.testHire) {
859
+ testHireResult = await runTestHire(config, configDir, opts.skill);
860
+ }
861
+ const passed = checks.filter((c) => c.status === "pass").length;
862
+ const warnings = checks.filter((c) => c.status === "warn").length;
863
+ const failed = checks.filter((c) => c.status === "fail").length;
864
+ const report = {
865
+ checks,
866
+ testHire: testHireResult,
867
+ passed,
868
+ warnings,
869
+ failed,
870
+ total: checks.length
871
+ };
872
+ renderReport(report, opts.json ?? false);
873
+ if (failed > 0) process.exit(1);
874
+ }
875
+ function renderReport(report, jsonMode) {
876
+ if (jsonMode) {
877
+ console.log(JSON.stringify(report, null, 2));
878
+ return;
879
+ }
880
+ console.log("\nagentbnb doctor \u2014 Provider Readiness Check\n");
881
+ for (const check of report.checks) {
882
+ const icon = check.status === "pass" ? "\u2713" : check.status === "warn" ? "\u26A0" : "\u2717";
883
+ const pad = check.name.padEnd(12);
884
+ console.log(` ${icon} ${pad} ${check.message}`);
885
+ }
886
+ const parts = [`${report.passed}/${report.total} checks passed`];
887
+ if (report.warnings > 0) parts.push(`${report.warnings} warning${report.warnings > 1 ? "s" : ""}`);
888
+ if (report.failed > 0) parts.push(`${report.failed} failure${report.failed > 1 ? "s" : ""}`);
889
+ console.log(`
890
+ Result: ${parts.join(", ")}
891
+ `);
892
+ const failures = report.checks.filter((c) => c.status === "fail");
893
+ if (failures.length > 0) {
894
+ console.log(" To fix:");
895
+ for (const f of failures) {
896
+ console.log(` \u2717 ${f.name} \u2014 ${f.context ?? f.message}`);
897
+ if (f.fix) console.log(` \u2192 ${f.fix}`);
898
+ }
899
+ console.log("");
900
+ }
901
+ const warns = report.checks.filter((c) => c.status === "warn" && c.context);
902
+ if (warns.length > 0) {
903
+ console.log(" Warnings:");
904
+ for (const w of warns) {
905
+ console.log(` \u26A0 ${w.name} \u2014 ${w.context}`);
906
+ if (w.fix) console.log(` \u2192 ${w.fix}`);
907
+ }
908
+ console.log("");
909
+ }
910
+ if (report.testHire) {
911
+ const th = report.testHire;
912
+ console.log(" Test hire:");
913
+ console.log(` Skill: ${th.skillId} (${th.creditsSpent} credits, selected: ${th.selectionReason})`);
914
+ if (th.success) {
915
+ console.log(` Result: \u2713 completed in ${th.latency_ms}ms`);
916
+ console.log(` Credits: ${th.creditsSpent} credits spent (self-to-self)`);
917
+ console.log(" Proof: Your provider can execute real hire requests end-to-end.");
918
+ } else {
919
+ console.log(` Result: \u2717 failed after ${th.latency_ms}ms`);
920
+ console.log(` Error: ${th.error}`);
921
+ }
922
+ console.log("");
923
+ }
924
+ if (!report.testHire && report.failed === 0) {
925
+ console.log(" Next steps:");
926
+ console.log(" \u2192 Run `agentbnb doctor --test-hire` to prove end-to-end hiring works");
927
+ console.log(' \u2192 Run `agentbnb discover "code review"` to see your skills as a hirer would');
928
+ console.log("");
302
929
  }
303
930
  }
304
931
 
305
932
  // src/cli/index.ts
306
- var VERSION = true ? "9.2.0" : "0.0.0-dev";
933
+ var REGISTRY_HTTP_BUDGET_MS = 5e3;
934
+ var VERSION = true ? "9.2.1" : "0.0.0-dev";
307
935
  function loadIdentityAuth(owner) {
308
936
  const configDir = getConfigDir();
309
937
  let keys;
@@ -402,7 +1030,7 @@ program.command("publish <card.json>").description("Publish a Capability Card to
402
1030
  }
403
1031
  let raw;
404
1032
  try {
405
- raw = readFileSync2(cardPath, "utf-8");
1033
+ raw = readFileSync3(cardPath, "utf-8");
406
1034
  } catch {
407
1035
  console.error(`Error: cannot read file: ${cardPath}`);
408
1036
  process.exit(1);
@@ -478,14 +1106,17 @@ program.command("publish <card.json>").description("Publish a Capability Card to
478
1106
  }
479
1107
  const registryUrl = opts.registry ?? config.registry;
480
1108
  let remoteSuccess = false;
481
- if (registryUrl) {
1109
+ if (registryUrl && !shouldSkipNetwork()) {
482
1110
  const url = `${registryUrl.replace(/\/$/, "")}/cards`;
483
1111
  const remoteCard = config.agent_id && localCard.owner === config.owner && localCard.agent_id !== config.agent_id ? { ...localCard, agent_id: config.agent_id, gateway_url: config.gateway_url } : { ...localCard, gateway_url: config.gateway_url };
1112
+ const controller = new AbortController();
1113
+ const timer = setTimeout(() => controller.abort(), REGISTRY_HTTP_BUDGET_MS);
484
1114
  try {
485
1115
  const response = await fetch(url, {
486
1116
  method: "POST",
487
1117
  headers: { "Content-Type": "application/json" },
488
- body: JSON.stringify(remoteCard)
1118
+ body: JSON.stringify(remoteCard),
1119
+ signal: controller.signal
489
1120
  });
490
1121
  if (!response.ok) {
491
1122
  const body = await response.text();
@@ -497,7 +1128,11 @@ program.command("publish <card.json>").description("Publish a Capability Card to
497
1128
  }
498
1129
  }
499
1130
  } catch (err) {
500
- console.error(`Warning: cannot reach registry at ${url}: ${err.message}`);
1131
+ const isAbort = err.name === "AbortError";
1132
+ const reason = isAbort ? `timeout after ${REGISTRY_HTTP_BUDGET_MS}ms` : err.message;
1133
+ console.error(`Warning: cannot reach registry at ${url}: ${reason}`);
1134
+ } finally {
1135
+ clearTimeout(timer);
501
1136
  }
502
1137
  }
503
1138
  if (opts.json) {
@@ -516,19 +1151,19 @@ program.command("publish-skills").description("Publish capabilities from skills.
516
1151
  console.error("Error: not initialized. Run `agentbnb init` first.");
517
1152
  process.exit(1);
518
1153
  }
519
- const { parseSkillsFile } = await import("../skill-config-VYNF7BCY.js");
520
- const { skillConfigToSkill } = await import("../publish-capability-2FMD3K6Z.js");
1154
+ const { parseSkillsFile: parseSkillsFile2 } = await import("../skill-config-VYNF7BCY.js");
1155
+ const { skillConfigToSkill } = await import("../publish-capability-OYXXXYAU.js");
521
1156
  const skillsPath = typeof opts.fromSkills === "string" ? opts.fromSkills : "./skills.yaml";
522
1157
  let yamlContent;
523
1158
  try {
524
- yamlContent = readFileSync2(skillsPath, "utf-8");
1159
+ yamlContent = readFileSync3(skillsPath, "utf-8");
525
1160
  } catch {
526
1161
  console.error(`Error: cannot read skills.yaml at ${skillsPath}`);
527
1162
  process.exit(1);
528
1163
  }
529
1164
  let allSkillConfigs;
530
1165
  try {
531
- allSkillConfigs = parseSkillsFile(yamlContent);
1166
+ allSkillConfigs = parseSkillsFile2(yamlContent);
532
1167
  } catch (err) {
533
1168
  const msg = err instanceof Error ? err.message : String(err);
534
1169
  console.error(`Error: failed to parse skills.yaml \u2014 ${msg}`);
@@ -599,15 +1234,30 @@ program.command("sync").description("Push all local capability cards to the conf
599
1234
  let synced = 0;
600
1235
  let failed = 0;
601
1236
  const results = [];
1237
+ if (!shouldSkipNetwork()) {
1238
+ const reachable = await probeRegistry(registryUrl);
1239
+ if (!reachable) {
1240
+ if (!opts.json) {
1241
+ console.error(`Error: cannot reach registry at ${registryUrl}. Try again when online.`);
1242
+ }
1243
+ if (opts.json) {
1244
+ console.log(JSON.stringify({ synced: 0, failed: localCards.length, registry: registryUrl, error: "unreachable" }, null, 2));
1245
+ }
1246
+ process.exit(1);
1247
+ }
1248
+ }
602
1249
  for (const card of localCards) {
603
1250
  const { _internal: _, ...publicCard } = card;
604
1251
  const remoteCard = { ...publicCard, gateway_url: config.gateway_url };
605
1252
  const displayName = card.name ?? card.agent_name ?? card.id;
1253
+ const controller = new AbortController();
1254
+ const timer = setTimeout(() => controller.abort(), REGISTRY_HTTP_BUDGET_MS);
606
1255
  try {
607
1256
  const response = await fetch(url, {
608
1257
  method: "POST",
609
1258
  headers: { "Content-Type": "application/json" },
610
- body: JSON.stringify(remoteCard)
1259
+ body: JSON.stringify(remoteCard),
1260
+ signal: controller.signal
611
1261
  });
612
1262
  if (response.ok) {
613
1263
  synced++;
@@ -625,11 +1275,14 @@ program.command("sync").description("Push all local capability cards to the conf
625
1275
  }
626
1276
  } catch (err) {
627
1277
  failed++;
628
- const msg = err instanceof Error ? err.message : String(err);
1278
+ const isAbort = err.name === "AbortError";
1279
+ const msg = isAbort ? `timeout after ${REGISTRY_HTTP_BUDGET_MS}ms` : err instanceof Error ? err.message : String(err);
629
1280
  results.push({ id: card.id, name: displayName, ok: false, error: msg });
630
1281
  if (!opts.json) {
631
1282
  console.error(` Failed: ${displayName} \u2014 ${msg}`);
632
1283
  }
1284
+ } finally {
1285
+ clearTimeout(timer);
633
1286
  }
634
1287
  }
635
1288
  if (opts.json) {
@@ -760,16 +1413,19 @@ program.command("request [card-id]").description("Request a capability from anot
760
1413
  console.error("Error: not initialized. Run `agentbnb init` first.");
761
1414
  process.exit(1);
762
1415
  }
763
- if (config.registry) {
764
- const creditDb = openCreditDb(config.credit_db_path);
765
- try {
766
- const syncResult = await syncCreditsFromRegistry(config, creditDb);
767
- if (syncResult.synced && syncResult.remoteBalance !== void 0) {
768
- console.log(`Credits synced: ${syncResult.remoteBalance} [registry]`);
1416
+ if (config.registry && !shouldSkipNetwork()) {
1417
+ const reachable = await probeRegistry(config.registry);
1418
+ if (reachable) {
1419
+ const creditDb = openCreditDb(config.credit_db_path);
1420
+ try {
1421
+ const syncResult = await withTimeout(syncCreditsFromRegistry(config, creditDb), 3e3);
1422
+ if (syncResult.synced && syncResult.remoteBalance !== void 0) {
1423
+ console.log(`Credits synced: ${syncResult.remoteBalance} [registry]`);
1424
+ }
1425
+ } catch {
1426
+ } finally {
1427
+ creditDb.close();
769
1428
  }
770
- } catch {
771
- } finally {
772
- creditDb.close();
773
1429
  }
774
1430
  }
775
1431
  if (opts.batch) {
@@ -780,13 +1436,16 @@ program.command("request [card-id]").description("Request a capability from anot
780
1436
  }
781
1437
  let batchPayload;
782
1438
  try {
783
- const raw = readFileSync2(opts.batch, "utf-8");
1439
+ const raw = readFileSync3(opts.batch, "utf-8");
784
1440
  batchPayload = JSON.parse(raw);
785
1441
  } catch (err) {
786
1442
  console.error(`Error: could not read batch file: ${err.message}`);
787
1443
  process.exit(1);
788
1444
  }
789
1445
  const batchUrl = `${registryUrl.replace(/\/$/, "")}/api/request/batch`;
1446
+ const BATCH_BUDGET_MS = 6e4;
1447
+ const controller = new AbortController();
1448
+ const timer = setTimeout(() => controller.abort(), BATCH_BUDGET_MS);
790
1449
  let batchResp;
791
1450
  let batchResult;
792
1451
  try {
@@ -796,12 +1455,17 @@ program.command("request [card-id]").description("Request a capability from anot
796
1455
  "Content-Type": "application/json",
797
1456
  Authorization: `Bearer ${config.owner}`
798
1457
  },
799
- body: JSON.stringify(batchPayload)
1458
+ body: JSON.stringify(batchPayload),
1459
+ signal: controller.signal
800
1460
  });
801
1461
  batchResult = await batchResp.json();
802
1462
  } catch (err) {
803
- console.error(`Error: batch request failed: ${err.message}`);
1463
+ const isAbort = err.name === "AbortError";
1464
+ const reason = isAbort ? `timeout after ${BATCH_BUDGET_MS}ms` : err.message;
1465
+ console.error(`Error: batch request failed: ${reason}`);
804
1466
  process.exit(1);
1467
+ } finally {
1468
+ clearTimeout(timer);
805
1469
  }
806
1470
  if (opts.json) {
807
1471
  console.log(JSON.stringify(batchResult, null, 2));
@@ -841,8 +1505,8 @@ Batch Results (${res.results.length} items):`);
841
1505
  process.exit(1);
842
1506
  }
843
1507
  }
844
- const registryDb = openDatabase(join2(getConfigDir(), "registry.db"));
845
- const creditDb = openCreditDb(join2(getConfigDir(), "credit.db"));
1508
+ const registryDb = openDatabase(join3(getConfigDir(), "registry.db"));
1509
+ const creditDb = openCreditDb(join3(getConfigDir(), "credit.db"));
846
1510
  registryDb.pragma("busy_timeout = 5000");
847
1511
  creditDb.pragma("busy_timeout = 5000");
848
1512
  try {
@@ -924,16 +1588,22 @@ Batch Results (${res.results.length} items):`);
924
1588
  }
925
1589
  const cardUrl = `${registryUrl.replace(/\/$/, "")}/cards/${cardId}`;
926
1590
  let remoteCard;
1591
+ const cardController = new AbortController();
1592
+ const cardTimer = setTimeout(() => cardController.abort(), REGISTRY_HTTP_BUDGET_MS);
927
1593
  try {
928
- const resp = await fetch(cardUrl);
1594
+ const resp = await fetch(cardUrl, { signal: cardController.signal });
929
1595
  if (!resp.ok) {
930
1596
  console.error(`Error: card ${cardId} not found on remote registry (${resp.status}).`);
931
1597
  process.exit(1);
932
1598
  }
933
1599
  remoteCard = await resp.json();
934
1600
  } catch (err) {
935
- console.error(`Error: cannot reach registry: ${err.message}`);
1601
+ const isAbort = err.name === "AbortError";
1602
+ const reason = isAbort ? `timeout after ${REGISTRY_HTTP_BUDGET_MS}ms` : err.message;
1603
+ console.error(`Error: cannot reach registry: ${reason}`);
936
1604
  process.exit(1);
1605
+ } finally {
1606
+ clearTimeout(cardTimer);
937
1607
  }
938
1608
  targetOwner = remoteCard.owner ?? remoteCard.agent_name;
939
1609
  targetAgentId = typeof remoteCard.agent_id === "string" ? remoteCard.agent_id : void 0;
@@ -1047,23 +1717,37 @@ program.command("status").description("Show credit balance and recent transactio
1047
1717
  creditDb.close();
1048
1718
  }
1049
1719
  const hasRegistry = config.registry != null;
1050
- if (hasRegistry) {
1051
- const statusIdentityAuth = loadIdentityAuth(config.owner);
1052
- const statusLedger = createLedger({
1053
- registryUrl: config.registry,
1054
- ownerPublicKey: statusIdentityAuth.publicKey,
1055
- privateKey: statusIdentityAuth.privateKey
1056
- });
1057
- try {
1058
- registryBalance = await statusLedger.getBalance(config.owner);
1059
- transactions = await statusLedger.getHistory(config.owner, 5);
1060
- } catch (err) {
1720
+ if (hasRegistry && !shouldSkipNetwork()) {
1721
+ const reachable = await probeRegistry(config.registry);
1722
+ if (!reachable) {
1061
1723
  registryUnavailable = true;
1062
- const msg = err instanceof Error ? err.message : String(err);
1063
- if (!opts.json) {
1064
- console.warn(`Note: could not fetch balance from registry (${msg}). Run \`agentbnb init\` if this is a new agent.`);
1724
+ } else {
1725
+ const statusIdentityAuth = loadIdentityAuth(config.owner);
1726
+ const statusLedger = createLedger({
1727
+ registryUrl: config.registry,
1728
+ ownerPublicKey: statusIdentityAuth.publicKey,
1729
+ privateKey: statusIdentityAuth.privateKey
1730
+ });
1731
+ try {
1732
+ const [balance, history] = await withTimeout(
1733
+ Promise.all([
1734
+ statusLedger.getBalance(config.owner),
1735
+ statusLedger.getHistory(config.owner, 5)
1736
+ ]),
1737
+ REGISTRY_HTTP_BUDGET_MS
1738
+ );
1739
+ registryBalance = balance;
1740
+ transactions = history;
1741
+ } catch (err) {
1742
+ registryUnavailable = true;
1743
+ const msg = err instanceof TimeoutError ? `timeout after ${REGISTRY_HTTP_BUDGET_MS}ms` : err instanceof Error ? err.message : String(err);
1744
+ if (!opts.json) {
1745
+ console.warn(`Note: could not fetch balance from registry (${msg}). Run \`agentbnb init\` if this is a new agent.`);
1746
+ }
1065
1747
  }
1066
1748
  }
1749
+ } else if (hasRegistry) {
1750
+ registryUnavailable = true;
1067
1751
  }
1068
1752
  const syncNeeded = hasRegistry && !registryUnavailable && registryBalance !== null && Math.abs(registryBalance - localBalance) > 1;
1069
1753
  const displayBalance = registryBalance ?? localBalance;
@@ -1122,7 +1806,7 @@ Active Escrows (${heldEscrows.length}):`);
1122
1806
  });
1123
1807
  program.command("serve").description("Start the AgentBnB gateway server").option("--port <port>", "Port to listen on (overrides config)").option("--handler-url <url>", "Local capability handler URL", "http://localhost:8080").option("--skills-yaml <path>", "Path to skills.yaml (default: ~/.agentbnb/skills.yaml)").option("--registry-port <port>", "Public registry API port (0 to disable)", "7701").option("--registry <url>", "Connect to remote registry via WebSocket relay (e.g., hub.agentbnb.dev)").option("--conductor", "Enable Conductor orchestration mode").option("--announce", "Announce this gateway on the local network via mDNS").option("--no-relay", "Do not auto-connect to remote registry relay").option("--daemon", "Run in background as daemon").option("--status", "Show daemon status").option("--stop", "Stop the daemon").option("--restart", "Restart the daemon").option("--startup", "Register for auto-start on boot").action(async (opts) => {
1124
1808
  if (opts.status || opts.stop || opts.restart || opts.daemon || opts.startup) {
1125
- const { startDaemon, stopDaemon, restartDaemon, daemonStatus, registerStartup } = await import("../daemon-ETXXE4IS.js");
1809
+ const { startDaemon, stopDaemon, restartDaemon, daemonStatus, registerStartup } = await import("../daemon-OM2K3U7J.js");
1126
1810
  if (opts.status) {
1127
1811
  daemonStatus();
1128
1812
  return;
@@ -1158,15 +1842,15 @@ program.command("serve").description("Start the AgentBnB gateway server").option
1158
1842
  console.error("Error: not initialized. Run `agentbnb init` first.");
1159
1843
  process.exit(1);
1160
1844
  }
1161
- const { ProcessGuard } = await import("../process-guard-6324CZDC.js");
1162
- const { ServiceCoordinator } = await import("../service-coordinator-ZOZTW2U6.js");
1845
+ const { ProcessGuard: ProcessGuard2 } = await import("../process-guard-IUMZ2GSD.js");
1846
+ const { ServiceCoordinator } = await import("../service-coordinator-RE2KPWO4.js");
1163
1847
  const port = opts.port ? parseInt(opts.port, 10) : config.gateway_port;
1164
1848
  const registryPort = parseInt(opts.registryPort, 10);
1165
1849
  if (!Number.isFinite(port) || !Number.isFinite(registryPort)) {
1166
1850
  console.error("Error: --port and --registry-port must be valid numbers.");
1167
1851
  process.exit(1);
1168
1852
  }
1169
- const guard = new ProcessGuard(join2(getConfigDir(), ".pid"));
1853
+ const guard = new ProcessGuard2(join3(getConfigDir(), ".pid"));
1170
1854
  const coordinator = new ServiceCoordinator(config, guard);
1171
1855
  try {
1172
1856
  await coordinator.ensureRunning({
@@ -1528,7 +2212,7 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
1528
2212
  const found = findSoulMd(searchName) ?? (searchName !== config.owner ? findSoulMd(config.owner) : null);
1529
2213
  if (found) {
1530
2214
  resolvedSoulPath = found;
1531
- } else if (existsSync2("./SOUL.md")) {
2215
+ } else if (existsSync3("./SOUL.md")) {
1532
2216
  resolvedSoulPath = "./SOUL.md";
1533
2217
  } else {
1534
2218
  const { homedir: homedir2 } = await import("os");
@@ -1536,9 +2220,9 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
1536
2220
  const workspaceDir = getOpenClawWorkspaceDir();
1537
2221
  console.error(`No SOUL.md found for agent "${searchName}".`);
1538
2222
  console.error("Searched:");
1539
- console.error(` - ${join2(workspaceDir, "brains", searchName, "SOUL.md")}`);
1540
- console.error(` - ${join2(home, ".openclaw", "agents", searchName, "SOUL.md")}`);
1541
- console.error(` - ${join2(workspaceDir, "SOUL.md")}`);
2223
+ console.error(` - ${join3(workspaceDir, "brains", searchName, "SOUL.md")}`);
2224
+ console.error(` - ${join3(home, ".openclaw", "agents", searchName, "SOUL.md")}`);
2225
+ console.error(` - ${join3(workspaceDir, "SOUL.md")}`);
1542
2226
  console.error(` - ./SOUL.md`);
1543
2227
  console.error("");
1544
2228
  console.error("Create a SOUL.md file in one of these locations, or use:");
@@ -1549,7 +2233,7 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
1549
2233
  }
1550
2234
  let content;
1551
2235
  try {
1552
- content = readFileSync2(resolvedSoulPath, "utf-8");
2236
+ content = readFileSync3(resolvedSoulPath, "utf-8");
1553
2237
  } catch {
1554
2238
  console.error(`Error: cannot read SOUL.md at ${resolvedSoulPath}`);
1555
2239
  process.exit(1);
@@ -1619,34 +2303,34 @@ openclaw.command("rules").description("Print HEARTBEAT.md rules block (or inject
1619
2303
  }
1620
2304
  });
1621
2305
  openclaw.command("setup").description("Interactive onboarding: connect an OpenClaw agent to AgentBnB").option("--agent <name>", "Agent name to set up (skip interactive selection)").option("--soul-path <path>", "Override SOUL.md path").option("-y, --yes", "Skip confirmation prompts").action(async (opts) => {
1622
- const { runOpenClawSetup } = await import("../openclaw-setup-4RIZRMXA.js");
2306
+ const { runOpenClawSetup } = await import("../openclaw-setup-HUOBTGN4.js");
1623
2307
  await runOpenClawSetup(opts);
1624
2308
  });
1625
2309
  var skills = openclaw.command("skills").description("Manage shared skills on AgentBnB");
1626
2310
  skills.command("list").description("List all shared skills with stats").action(async () => {
1627
- const { skillsList } = await import("../openclaw-skills-TQ2JVBRM.js");
2311
+ const { skillsList } = await import("../openclaw-skills-74372B6I.js");
1628
2312
  await skillsList({});
1629
2313
  });
1630
2314
  skills.command("add").description("Add a skill to share (interactive or --manual)").option("--manual", "Non-interactive: use flags instead of prompts").option("--name <id>", "Skill ID").option("--type <type>", "Skill type (command|openclaw)").option("--price <n>", "Credits per call", parseFloat).option("--description <text>", "Skill description").action(
1631
2315
  async (opts) => {
1632
- const { skillsAdd } = await import("../openclaw-skills-TQ2JVBRM.js");
2316
+ const { skillsAdd } = await import("../openclaw-skills-74372B6I.js");
1633
2317
  await skillsAdd(opts);
1634
2318
  }
1635
2319
  );
1636
2320
  skills.command("remove <skillId>").description("Remove a skill from AgentBnB").action(async (skillId) => {
1637
- const { skillsRemove } = await import("../openclaw-skills-TQ2JVBRM.js");
2321
+ const { skillsRemove } = await import("../openclaw-skills-74372B6I.js");
1638
2322
  await skillsRemove(skillId);
1639
2323
  });
1640
2324
  skills.command("price <skillId> <price>").description("Update skill price").action(async (skillId, price) => {
1641
- const { skillsPrice } = await import("../openclaw-skills-TQ2JVBRM.js");
2325
+ const { skillsPrice } = await import("../openclaw-skills-74372B6I.js");
1642
2326
  await skillsPrice(skillId, parseFloat(price));
1643
2327
  });
1644
2328
  skills.command("stats").description("Revenue and performance report").option("--days <n>", "Days to look back", parseInt, 7).action(async (opts) => {
1645
- const { skillsStats } = await import("../openclaw-skills-TQ2JVBRM.js");
2329
+ const { skillsStats } = await import("../openclaw-skills-74372B6I.js");
1646
2330
  await skillsStats(opts);
1647
2331
  });
1648
2332
  program.command("conduct <task>").description("Orchestrate a complex task across the AgentBnB network").option("--plan-only", "Show execution plan without executing").option("--max-budget <credits>", "Maximum credits to spend", "100").option("--json", "Output as JSON").action(async (task, opts) => {
1649
- const { conductAction } = await import("../conduct-JRLLA4PB.js");
2333
+ const { conductAction } = await import("../conduct-AALDEKTH.js");
1650
2334
  const result = await conductAction(task, opts);
1651
2335
  if (opts.json) {
1652
2336
  console.log(JSON.stringify(result, null, 2));
@@ -1705,10 +2389,12 @@ feedback.command("submit").description("Submit structured feedback for a complet
1705
2389
  res = await fetch(`${registryUrl}/api/feedback`, {
1706
2390
  method: "POST",
1707
2391
  headers: { "Content-Type": "application/json" },
1708
- body: JSON.stringify(result.data)
2392
+ body: JSON.stringify(result.data),
2393
+ signal: AbortSignal.timeout(1e4)
1709
2394
  });
1710
2395
  } catch (err) {
1711
- console.error("Error: failed to connect to registry \u2014", err.message);
2396
+ const msg = err instanceof Error ? err.message : String(err);
2397
+ console.error(`Error: failed to connect to registry \u2014 ${msg}`);
1712
2398
  process.exit(1);
1713
2399
  }
1714
2400
  const body = await res.json();
@@ -1728,9 +2414,10 @@ feedback.command("list").description("List feedback entries for a skill").requir
1728
2414
  const url = `${registryUrl}/api/feedback/${encodeURIComponent(opts.skill)}?limit=20`;
1729
2415
  let res;
1730
2416
  try {
1731
- res = await fetch(url);
2417
+ res = await fetch(url, { signal: AbortSignal.timeout(1e4) });
1732
2418
  } catch (err) {
1733
- console.error("Error: failed to connect to registry \u2014", err.message);
2419
+ const msg = err instanceof Error ? err.message : String(err);
2420
+ console.error(`Error: failed to connect to registry \u2014 ${msg}`);
1734
2421
  process.exit(1);
1735
2422
  }
1736
2423
  const body = await res.json();
@@ -1764,7 +2451,7 @@ skill.command("wrap").description("Wrap a CLI command as a rentable AgentBnB ski
1764
2451
  });
1765
2452
  var did = program.command("did").description("Decentralized Identity commands");
1766
2453
  did.command("show").description("Display local agent DID identifiers").option("--json", "Output as JSON").action(async (opts) => {
1767
- const { didShow, didShowJson } = await import("../did-action-ODWTBVXL.js");
2454
+ const { didShow, didShowJson } = await import("../did-action-ERXWCVEJ.js");
1768
2455
  if (opts.json) {
1769
2456
  await didShowJson();
1770
2457
  } else {
@@ -1773,45 +2460,48 @@ did.command("show").description("Display local agent DID identifiers").option("-
1773
2460
  });
1774
2461
  var vc = program.command("vc").description("Verifiable Credentials commands");
1775
2462
  vc.command("show").description("Display Verifiable Credentials for this agent").option("--json", "Output as JSON").action(async (opts) => {
1776
- const { vcShow } = await import("../vc-action-A6VBKERF.js");
2463
+ const { vcShow } = await import("../vc-action-72TQVMY2.js");
1777
2464
  await vcShow(opts);
1778
2465
  });
1779
2466
  var credits = program.command("credits").description("Credit balance and transaction management");
1780
2467
  credits.command("sync").description("Sync local credit balance from remote registry").action(async () => {
1781
- const { creditsSync } = await import("../credits-action-XERUEDF3.js");
2468
+ const { creditsSync } = await import("../credits-action-CLLPNRDT.js");
1782
2469
  await creditsSync();
1783
2470
  });
1784
2471
  credits.command("history").description("Show recent credit transactions").option("--limit <n>", "Number of transactions to show", "20").option("--json", "Output as JSON").action(async (opts) => {
1785
- const { creditsHistory } = await import("../credits-action-XERUEDF3.js");
2472
+ const { creditsHistory } = await import("../credits-action-CLLPNRDT.js");
1786
2473
  await creditsHistory(opts);
1787
2474
  });
1788
2475
  credits.command("grant <agent_id> <amount>").description("Admin: grant credits to an agent (requires ADMIN_TOKEN)").action(async (agentId, amount) => {
1789
- const { creditsGrant } = await import("../credits-action-XERUEDF3.js");
2476
+ const { creditsGrant } = await import("../credits-action-CLLPNRDT.js");
1790
2477
  await creditsGrant(agentId, amount);
1791
2478
  });
1792
2479
  var sessionCmd = program.command("session").description("Manage interactive agent-to-agent sessions");
1793
2480
  sessionCmd.command("open <card_id>").description("Open an interactive session with a provider agent").requiredOption("--skill <skill_id>", "Skill ID to use").requiredOption("--budget <credits>", "Maximum credits for this session", parseInt).requiredOption("--message <msg>", "Initial message to send").option("--pricing <model>", "Pricing model: per_message, per_minute, per_session", "per_message").action(async (cardId, opts) => {
1794
- const { sessionOpen } = await import("../session-action-OSBZB4TX.js");
2481
+ const { sessionOpen } = await import("../session-action-UBWJTQVQ.js");
1795
2482
  await sessionOpen(cardId, opts);
1796
2483
  });
1797
2484
  sessionCmd.command("send <session_id> <message>").description("Send a message within an active session").action(async (sessionId, message) => {
1798
- const { sessionSend } = await import("../session-action-OSBZB4TX.js");
2485
+ const { sessionSend } = await import("../session-action-UBWJTQVQ.js");
1799
2486
  await sessionSend(sessionId, message);
1800
2487
  });
1801
2488
  sessionCmd.command("end <session_id>").description("End an active session").option("--reason <reason>", "End reason: completed, cancelled", "completed").action(async (sessionId, opts) => {
1802
- const { sessionEnd } = await import("../session-action-OSBZB4TX.js");
2489
+ const { sessionEnd } = await import("../session-action-UBWJTQVQ.js");
1803
2490
  await sessionEnd(sessionId, opts.reason);
1804
2491
  });
1805
2492
  sessionCmd.command("list").description("List active sessions").action(async () => {
1806
- const { sessionList } = await import("../session-action-OSBZB4TX.js");
2493
+ const { sessionList } = await import("../session-action-UBWJTQVQ.js");
1807
2494
  await sessionList();
1808
2495
  });
1809
2496
  sessionCmd.command("status <session_id>").description("Show session details").action(async (sessionId) => {
1810
- const { sessionStatus } = await import("../session-action-OSBZB4TX.js");
2497
+ const { sessionStatus } = await import("../session-action-UBWJTQVQ.js");
1811
2498
  await sessionStatus(sessionId);
1812
2499
  });
2500
+ program.command("doctor").description("Validate provider readiness \u2014 setup, skills, connectivity, discoverability").option("--json", "Output as JSON").option("--test-hire", "Run a self-hire smoke test to prove end-to-end execution").option("--skill <id>", "Specific skill to test with --test-hire").action(async (opts) => {
2501
+ await runDoctor(opts);
2502
+ });
1813
2503
  program.command("mcp-server").description("Start an MCP (Model Context Protocol) server for IDE integration").action(async () => {
1814
- const { startMcpServer } = await import("../server-UPOPLZ24.js");
2504
+ const { startMcpServer } = await import("../server-LNT4YQZ7.js");
1815
2505
  await startMcpServer();
1816
2506
  });
1817
2507
  await program.parseAsync(process.argv);