unbrowse 3.1.0 → 3.2.0

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 (107) hide show
  1. package/dist/cli.js +455 -96
  2. package/dist/index.js +2 -6
  3. package/dist/mcp.js +695 -46
  4. package/dist/server.js +25811 -0
  5. package/package.json +1 -2
  6. package/vendor/kuri/darwin-arm64/kuri +0 -0
  7. package/vendor/kuri/darwin-x64/kuri +0 -0
  8. package/vendor/kuri/linux-arm64/kuri +0 -0
  9. package/vendor/kuri/linux-x64/kuri +0 -0
  10. package/vendor/kuri/manifest.json +7 -10
  11. package/runtime-src/agent-outcome.ts +0 -166
  12. package/runtime-src/analytics-session.ts +0 -55
  13. package/runtime-src/api/browse-index.ts +0 -317
  14. package/runtime-src/api/browse-session.ts +0 -572
  15. package/runtime-src/api/browse-submit-prereqs.ts +0 -48
  16. package/runtime-src/api/browse-submit.ts +0 -1184
  17. package/runtime-src/api/routes.ts +0 -1823
  18. package/runtime-src/auth/browser-cookies.ts +0 -423
  19. package/runtime-src/auth/index.ts +0 -535
  20. package/runtime-src/auth/runtime.ts +0 -116
  21. package/runtime-src/browser/index.ts +0 -659
  22. package/runtime-src/browser/types.ts +0 -41
  23. package/runtime-src/build-info.generated.ts +0 -6
  24. package/runtime-src/capture/index.ts +0 -1794
  25. package/runtime-src/capture/prefetch.ts +0 -95
  26. package/runtime-src/capture/rsc.ts +0 -45
  27. package/runtime-src/cli/shortcuts.ts +0 -273
  28. package/runtime-src/cli.ts +0 -1572
  29. package/runtime-src/client/graph-client.ts +0 -100
  30. package/runtime-src/client/index.ts +0 -1425
  31. package/runtime-src/debug-trace.ts +0 -18
  32. package/runtime-src/domain.ts +0 -38
  33. package/runtime-src/execution/index.ts +0 -3397
  34. package/runtime-src/execution/retry.ts +0 -46
  35. package/runtime-src/execution/robots.ts +0 -167
  36. package/runtime-src/execution/search-forms.ts +0 -188
  37. package/runtime-src/extraction/index.ts +0 -1507
  38. package/runtime-src/foundry/publish-bundle.ts +0 -392
  39. package/runtime-src/graph/agent-augment.ts +0 -315
  40. package/runtime-src/graph/index.ts +0 -1524
  41. package/runtime-src/graph/local-fixtures.ts +0 -393
  42. package/runtime-src/graph/local-harness.ts +0 -646
  43. package/runtime-src/graph/planner.ts +0 -411
  44. package/runtime-src/graph/session.ts +0 -294
  45. package/runtime-src/graph/trace-store.ts +0 -136
  46. package/runtime-src/index.ts +0 -24
  47. package/runtime-src/indexer/index.ts +0 -465
  48. package/runtime-src/intent-match.ts +0 -1515
  49. package/runtime-src/kuri/client.ts +0 -1839
  50. package/runtime-src/logger.ts +0 -30
  51. package/runtime-src/marketplace/index.ts +0 -103
  52. package/runtime-src/mcp.ts +0 -1747
  53. package/runtime-src/orchestrator/browser-agent.ts +0 -374
  54. package/runtime-src/orchestrator/dag-advisor.ts +0 -59
  55. package/runtime-src/orchestrator/dag-feedback.ts +0 -257
  56. package/runtime-src/orchestrator/first-pass-action.ts +0 -403
  57. package/runtime-src/orchestrator/index.ts +0 -4480
  58. package/runtime-src/orchestrator/passive-publish.ts +0 -187
  59. package/runtime-src/orchestrator/timing-economics.ts +0 -80
  60. package/runtime-src/payments/cascade.ts +0 -137
  61. package/runtime-src/payments/index.ts +0 -270
  62. package/runtime-src/payments/lobster-pay.ts +0 -182
  63. package/runtime-src/payments/wallet.ts +0 -98
  64. package/runtime-src/publish/review-context.ts +0 -93
  65. package/runtime-src/publish/sanitize.ts +0 -197
  66. package/runtime-src/publish/schema-review.ts +0 -192
  67. package/runtime-src/publish-admission.ts +0 -388
  68. package/runtime-src/ratelimit/index.ts +0 -23
  69. package/runtime-src/reverse-engineer/bundle-scanner.ts +0 -127
  70. package/runtime-src/reverse-engineer/description-prompt.ts +0 -213
  71. package/runtime-src/reverse-engineer/index.ts +0 -1551
  72. package/runtime-src/router.ts +0 -17
  73. package/runtime-src/routing-telemetry.ts +0 -395
  74. package/runtime-src/runtime/browser-access.ts +0 -11
  75. package/runtime-src/runtime/browser-auth.ts +0 -12
  76. package/runtime-src/runtime/browser-host.ts +0 -48
  77. package/runtime-src/runtime/lifecycle.ts +0 -17
  78. package/runtime-src/runtime/local-server.ts +0 -311
  79. package/runtime-src/runtime/paths.ts +0 -99
  80. package/runtime-src/runtime/setup.ts +0 -251
  81. package/runtime-src/runtime/supervisor.ts +0 -69
  82. package/runtime-src/runtime/update-hints.ts +0 -351
  83. package/runtime-src/server.ts +0 -100
  84. package/runtime-src/session-logs.ts +0 -142
  85. package/runtime-src/settings.ts +0 -221
  86. package/runtime-src/single-binary.ts +0 -143
  87. package/runtime-src/site-policy.ts +0 -54
  88. package/runtime-src/stale-cleanup-runner.ts +0 -144
  89. package/runtime-src/stale-cleanup.ts +0 -133
  90. package/runtime-src/telemetry-attribution.ts +0 -120
  91. package/runtime-src/telemetry.ts +0 -253
  92. package/runtime-src/template-params.ts +0 -141
  93. package/runtime-src/transform/drift.ts +0 -60
  94. package/runtime-src/transform/index.ts +0 -277
  95. package/runtime-src/types/index.ts +0 -1
  96. package/runtime-src/types/skill.ts +0 -912
  97. package/runtime-src/vault/index.ts +0 -196
  98. package/runtime-src/verification/auth-gate.ts +0 -8
  99. package/runtime-src/verification/candidates.ts +0 -27
  100. package/runtime-src/verification/index.ts +0 -120
  101. package/runtime-src/verification/matrix.ts +0 -30
  102. package/runtime-src/version.ts +0 -148
  103. package/runtime-src/workflow/artifact.ts +0 -161
  104. package/runtime-src/workflow/compile.ts +0 -808
  105. package/runtime-src/workflow/publish.ts +0 -225
  106. package/runtime-src/workflow/runtime.ts +0 -213
  107. package/vendor/kuri/win-x64/kuri.exe +0 -0
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "3.1.0", BUILD_GIT_SHA = "c9f9ce0e5cb1", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAiLCJnaXRfc2hhIjoiYzlmOWNlMGU1Y2IxIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0BjOWY5Y2UwZTVjYjEiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA1VDA2OjUwOjA4LjQzNVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "_2pdxyoIEYkKUHO0nXaXTubP5SrZCa-8CqStcPBsXxw", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "3.2.0", BUILD_GIT_SHA = "c3fc3f822751", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjAiLCJnaXRfc2hhIjoiYzNmYzNmODIyNzUxIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0BjM2ZjM2Y4MjI3NTEiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA1OjA2OjAxLjIwMFoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "xCBEHEB2UsniVYLfzuTpoXlcomZHL2pXht-7Ii1e7mM", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -400,7 +400,7 @@ var LOBSTER_PAY_TIMEOUT_MS = 30000, cachedCommand = undefined;
400
400
  var init_lobster_pay = () => {};
401
401
 
402
402
  // ../../src/runtime/paths.ts
403
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, realpathSync } from "node:fs";
403
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, realpathSync } from "node:fs";
404
404
  import os from "node:os";
405
405
  import path from "node:path";
406
406
  import { createRequire as createRequire2 } from "node:module";
@@ -414,7 +414,7 @@ function getPackageRoot(metaUrl) {
414
414
  let dir = getModuleDir(metaUrl);
415
415
  const root = path.parse(dir).root;
416
416
  while (dir !== root) {
417
- if (existsSync5(path.join(dir, "package.json")))
417
+ if (existsSync6(path.join(dir, "package.json")))
418
418
  return dir;
419
419
  dir = path.dirname(dir);
420
420
  }
@@ -433,7 +433,7 @@ function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
433
433
  const req = createRequire2(metaUrl);
434
434
  const tsxPkg = req.resolve("tsx/package.json");
435
435
  const tsxLoader = path.join(path.dirname(tsxPkg), "dist", "loader.mjs");
436
- if (existsSync5(tsxLoader))
436
+ if (existsSync6(tsxLoader))
437
437
  return ["--import", pathToFileURL(tsxLoader).href, entrypoint];
438
438
  } catch {}
439
439
  return ["--import", "tsx", entrypoint];
@@ -441,16 +441,16 @@ function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
441
441
  function getUnbrowseHome() {
442
442
  return path.join(os.homedir(), ".unbrowse");
443
443
  }
444
- function ensureDir(dir) {
445
- if (!existsSync5(dir))
446
- mkdirSync2(dir, { recursive: true });
444
+ function ensureDir2(dir) {
445
+ if (!existsSync6(dir))
446
+ mkdirSync3(dir, { recursive: true });
447
447
  return dir;
448
448
  }
449
449
  function getLogsDir() {
450
- return ensureDir(path.join(getUnbrowseHome(), "logs"));
450
+ return ensureDir2(path.join(getUnbrowseHome(), "logs"));
451
451
  }
452
452
  function getRunDir() {
453
- return ensureDir(process.env.UNBROWSE_RUN_DIR || path.join(getUnbrowseHome(), "run"));
453
+ return ensureDir2(process.env.UNBROWSE_RUN_DIR || path.join(getUnbrowseHome(), "run"));
454
454
  }
455
455
  function sanitizeSegment(value) {
456
456
  return value.replace(/[^a-zA-Z0-9.-]+/g, "_");
@@ -586,7 +586,7 @@ var init_logger = __esm(() => {
586
586
 
587
587
  // ../../src/kuri/client.ts
588
588
  import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
589
- import { existsSync as existsSync7 } from "node:fs";
589
+ import { existsSync as existsSync9 } from "node:fs";
590
590
  import path5 from "node:path";
591
591
  function createBrokerState(port = KURI_DEFAULT_PORT) {
592
592
  return {
@@ -661,7 +661,7 @@ function findKuriBinary() {
661
661
  if (process.env.KURI_BIN)
662
662
  return process.env.KURI_BIN;
663
663
  const candidates = getKuriBinaryCandidates();
664
- return candidates.find((candidate) => existsSync7(candidate)) ?? candidates[0] ?? kuriBinaryName();
664
+ return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0] ?? kuriBinaryName();
665
665
  }
666
666
  var KURI_DEFAULT_PORT = 7700, defaultBrokerState, brokerClients;
667
667
  var init_client2 = __esm(() => {
@@ -684,7 +684,7 @@ var init_browser_access = () => {};
684
684
 
685
685
  // ../../src/capture/index.ts
686
686
  import { nanoid as nanoid2 } from "nanoid";
687
- var activeTabRegistry, interceptorInjectedTabs;
687
+ var activeTabRegistry, interceptorInjectedTabs, cdpDocStartTabs, cdpCapturedHeaders;
688
688
  var init_capture = __esm(() => {
689
689
  init_client2();
690
690
  init_domain();
@@ -692,6 +692,8 @@ var init_capture = __esm(() => {
692
692
  init_browser_access();
693
693
  activeTabRegistry = new Set;
694
694
  interceptorInjectedTabs = new Set;
695
+ cdpDocStartTabs = new Set;
696
+ cdpCapturedHeaders = new Map;
695
697
  });
696
698
 
697
699
  // ../../src/transform/index.ts
@@ -701,11 +703,11 @@ var init_transform = __esm(() => {
701
703
  });
702
704
 
703
705
  // ../../src/debug-trace.ts
704
- import { join as join5 } from "node:path";
706
+ import { join as join6 } from "node:path";
705
707
  import { nanoid as nanoid3 } from "nanoid";
706
708
  var TRACE_DIR;
707
709
  var init_debug_trace = __esm(() => {
708
- TRACE_DIR = process.env.TRACES_DIR ?? join5(process.cwd(), "traces");
710
+ TRACE_DIR = process.env.TRACES_DIR ?? join6(process.cwd(), "traces");
709
711
  });
710
712
 
711
713
  // ../../src/publish/sanitize.ts
@@ -794,9 +796,18 @@ var init_bundle_scanner = __esm(() => {
794
796
  init_logger();
795
797
  });
796
798
 
799
+ // ../../src/reverse-engineer/token-sources.ts
800
+ var init_token_sources = () => {};
801
+
802
+ // ../../src/execution/token-resolver.ts
803
+ var init_token_resolver = __esm(() => {
804
+ init_token_sources();
805
+ init_client2();
806
+ });
807
+
797
808
  // ../../src/vault/index.ts
798
- import { join as join6 } from "path";
799
- import { homedir as homedir4 } from "os";
809
+ import { join as join7 } from "path";
810
+ import { homedir as homedir5 } from "os";
800
811
  function normalizeKeytarModule(mod) {
801
812
  let candidate = mod;
802
813
  for (let depth = 0;depth < 3; depth++) {
@@ -818,9 +829,9 @@ var init_vault = __esm(async () => {
818
829
  try {
819
830
  keytar = normalizeKeytarModule(await import("keytar"));
820
831
  } catch {}
821
- VAULT_DIR = join6(homedir4(), ".unbrowse", "vault");
822
- VAULT_FILE = join6(VAULT_DIR, "credentials.enc");
823
- KEY_FILE = join6(VAULT_DIR, ".key");
832
+ VAULT_DIR = join7(homedir5(), ".unbrowse", "vault");
833
+ VAULT_FILE = join7(VAULT_DIR, "credentials.enc");
834
+ KEY_FILE = join7(VAULT_DIR, ".key");
824
835
  vaultLock = Promise.resolve();
825
836
  });
826
837
 
@@ -897,18 +908,6 @@ var init_extraction = __esm(() => {
897
908
  CHROME_TAGS = new Set(["nav", "footer", "header"]);
898
909
  });
899
910
 
900
- // ../../src/graph/agent-augment.ts
901
- var DEFAULT_MODEL, ENABLED, AUGMENT_TIMEOUT_MS, MAX_AUGMENT_ENDPOINTS, MAX_AUGMENT_PAYLOAD_CHARS, GENERIC_SEMANTIC_TYPES;
902
- var init_agent_augment = __esm(() => {
903
- init_graph();
904
- DEFAULT_MODEL = process.env.UNBROWSE_AGENT_SEMANTIC_MODEL ?? process.env.UNBROWSE_AGENT_JUDGE_MODEL ?? "gpt-4.1-mini";
905
- ENABLED = process.env.UNBROWSE_AGENT_SEMANTIC_AUGMENT !== "0";
906
- AUGMENT_TIMEOUT_MS = Number(process.env.UNBROWSE_AGENT_SEMANTIC_TIMEOUT_MS ?? 8000);
907
- MAX_AUGMENT_ENDPOINTS = Math.max(1, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_ENDPOINTS ?? 6));
908
- MAX_AUGMENT_PAYLOAD_CHARS = Math.max(4000, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_PAYLOAD_CHARS ?? 24000));
909
- GENERIC_SEMANTIC_TYPES = new Set(["identifier", "input", "resource", "entity", "item"]);
910
- });
911
-
912
911
  // ../../src/execution/search-forms.ts
913
912
  var SEARCH_FIELD_NAMES, LOGIN_FIELD_NAMES, SUPPORTED_INPUT_TYPES;
914
913
  var init_search_forms = __esm(() => {
@@ -971,7 +970,7 @@ var init_schema_review = __esm(() => {
971
970
  });
972
971
 
973
972
  // ../../src/indexer/index.ts
974
- import { join as join7 } from "node:path";
973
+ import { join as join8 } from "node:path";
975
974
  var SKILL_SNAPSHOT_DIR, indexInFlight, pendingIndexJobs;
976
975
  var init_indexer = __esm(async () => {
977
976
  init_graph();
@@ -986,7 +985,7 @@ var init_indexer = __esm(async () => {
986
985
  init_graph();
987
986
  init_schema_review();
988
987
  await init_orchestrator();
989
- SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join7(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
988
+ SKILL_SNAPSHOT_DIR = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join8(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
990
989
  indexInFlight = new Map;
991
990
  pendingIndexJobs = new Map;
992
991
  });
@@ -1024,6 +1023,7 @@ var init_execution = __esm(async () => {
1024
1023
  init_capture();
1025
1024
  init_reverse_engineer();
1026
1025
  init_bundle_scanner();
1026
+ init_token_resolver();
1027
1027
  init_marketplace();
1028
1028
  init_runtime();
1029
1029
  init_transform();
@@ -1034,7 +1034,6 @@ var init_execution = __esm(async () => {
1034
1034
  init_domain();
1035
1035
  init_extraction();
1036
1036
  init_graph();
1037
- init_agent_augment();
1038
1037
  init_logger();
1039
1038
  init_version();
1040
1039
  init_search_forms();
@@ -1163,8 +1162,8 @@ var init_routing_telemetry = __esm(() => {
1163
1162
  });
1164
1163
  // ../../src/orchestrator/index.ts
1165
1164
  import { nanoid as nanoid9 } from "nanoid";
1166
- import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "node:fs";
1167
- import { dirname as dirname2, join as join8 } from "node:path";
1165
+ import { existsSync as existsSync10, writeFileSync as writeFileSync3, readFileSync as readFileSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "node:fs";
1166
+ import { dirname as dirname3, join as join9 } from "node:path";
1168
1167
  var LIVE_CAPTURE_TIMEOUT_MS, capturedDomainCache, captureInFlight, captureDomainLocks, skillRouteCache, ROUTE_CACHE_FILE, SKILL_SNAPSHOT_DIR2, domainSkillCache, DOMAIN_CACHE_FILE, _routeCacheDirty = false, routeCacheFlushTimer, routeResultCache, ROUTE_CACHE_TTL, MARKETPLACE_HYDRATE_LIMIT, MARKETPLACE_GET_SKILL_TIMEOUT_MS, MARKETPLACE_DOMAIN_SEARCH_K, MARKETPLACE_GLOBAL_SEARCH_K, SEARCH_INTENT_STOPWORDS;
1169
1168
  var init_orchestrator = __esm(async () => {
1170
1169
  init_client();
@@ -1194,13 +1193,13 @@ var init_orchestrator = __esm(async () => {
1194
1193
  captureInFlight = new Map;
1195
1194
  captureDomainLocks = new Map;
1196
1195
  skillRouteCache = new Map;
1197
- ROUTE_CACHE_FILE = join8(process.env.HOME ?? "/tmp", ".unbrowse", "route-cache.json");
1198
- SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join8(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
1196
+ ROUTE_CACHE_FILE = join9(process.env.HOME ?? "/tmp", ".unbrowse", "route-cache.json");
1197
+ SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join9(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
1199
1198
  domainSkillCache = new Map;
1200
- DOMAIN_CACHE_FILE = join8(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
1199
+ DOMAIN_CACHE_FILE = join9(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
1201
1200
  try {
1202
- if (existsSync8(DOMAIN_CACHE_FILE)) {
1203
- const data = JSON.parse(readFileSync5(DOMAIN_CACHE_FILE, "utf-8"));
1201
+ if (existsSync10(DOMAIN_CACHE_FILE)) {
1202
+ const data = JSON.parse(readFileSync6(DOMAIN_CACHE_FILE, "utf-8"));
1204
1203
  for (const [k, v] of Object.entries(data)) {
1205
1204
  const entry = v;
1206
1205
  if (Date.now() - entry.ts < 7 * 24 * 60 * 60000) {
@@ -1215,17 +1214,17 @@ var init_orchestrator = __esm(async () => {
1215
1214
  return;
1216
1215
  _routeCacheDirty = false;
1217
1216
  try {
1218
- const dir = dirname2(ROUTE_CACHE_FILE);
1219
- if (!existsSync8(dir))
1220
- mkdirSync4(dir, { recursive: true });
1217
+ const dir = dirname3(ROUTE_CACHE_FILE);
1218
+ if (!existsSync10(dir))
1219
+ mkdirSync5(dir, { recursive: true });
1221
1220
  const entries = Object.fromEntries(skillRouteCache);
1222
1221
  writeFileSync3(ROUTE_CACHE_FILE, JSON.stringify(entries), "utf-8");
1223
1222
  } catch {}
1224
1223
  }, 5000);
1225
1224
  routeCacheFlushTimer.unref?.();
1226
1225
  try {
1227
- if (existsSync8(ROUTE_CACHE_FILE)) {
1228
- const data = JSON.parse(readFileSync5(ROUTE_CACHE_FILE, "utf-8"));
1226
+ if (existsSync10(ROUTE_CACHE_FILE)) {
1227
+ const data = JSON.parse(readFileSync6(ROUTE_CACHE_FILE, "utf-8"));
1229
1228
  for (const [k, v] of Object.entries(data)) {
1230
1229
  const entry = v;
1231
1230
  if (Date.now() - entry.ts < 24 * 60 * 60000) {
@@ -1321,18 +1320,18 @@ __export(exports_wallet, {
1321
1320
  getWalletContext: () => getWalletContext2,
1322
1321
  checkWalletConfigured: () => checkWalletConfigured2
1323
1322
  });
1324
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
1325
- import { homedir as homedir5 } from "node:os";
1326
- import { join as join10 } from "node:path";
1323
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
1324
+ import { homedir as homedir6 } from "node:os";
1325
+ import { join as join11 } from "node:path";
1327
1326
  function asNonEmptyString2(value) {
1328
1327
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
1329
1328
  }
1330
1329
  function getLobsterWalletFromLocalConfig2() {
1331
- const agentsPath = join10(process.env.HOME || homedir5(), ".lobster", "agents.json");
1332
- if (!existsSync12(agentsPath))
1330
+ const agentsPath = join11(process.env.HOME || homedir6(), ".lobster", "agents.json");
1331
+ if (!existsSync14(agentsPath))
1333
1332
  return;
1334
1333
  try {
1335
- const raw = JSON.parse(readFileSync8(agentsPath, "utf8"));
1334
+ const raw = JSON.parse(readFileSync9(agentsPath, "utf8"));
1336
1335
  const activeAgentId = asNonEmptyString2(raw.activeAgentId);
1337
1336
  const activeAgent = Array.isArray(raw.agents) ? raw.agents.find((agent) => asNonEmptyString2(agent.id) === activeAgentId) : activeAgentId ? raw.agents?.[activeAgentId] : undefined;
1338
1337
  return asNonEmptyString2(activeAgent?.authorizedWallets?.solana) ?? asNonEmptyString2(activeAgent?.walletAddress) ?? asNonEmptyString2(activeAgent?.wallet_address);
@@ -1380,7 +1379,7 @@ init_wallet();
1380
1379
  init_telemetry_attribution();
1381
1380
  import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2 } from "fs";
1382
1381
  import { join as join4 } from "path";
1383
- import { homedir as homedir3, hostname } from "os";
1382
+ import { homedir as homedir3, hostname, release as osRelease } from "os";
1384
1383
  import { randomBytes, createHash as createHash2 } from "crypto";
1385
1384
  import { createInterface } from "readline";
1386
1385
  var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
@@ -1573,9 +1572,19 @@ async function recordInstallTelemetryEvent(source, options) {
1573
1572
  skill_version: options?.skillVersion,
1574
1573
  status: options?.status ?? "installed",
1575
1574
  created_at: createdAt,
1576
- properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
1575
+ properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
1577
1576
  });
1578
1577
  }
1578
+ function getRuntimeContext() {
1579
+ return {
1580
+ cli_version: PACKAGE_VERSION,
1581
+ code_hash: CODE_HASH,
1582
+ node_version: process.version,
1583
+ platform: process.platform,
1584
+ arch: process.arch,
1585
+ os_release: osRelease()
1586
+ };
1587
+ }
1579
1588
  async function recordFunnelTelemetryEvent(name, options) {
1580
1589
  const createdAt = options?.createdAt ?? new Date().toISOString();
1581
1590
  const landingToken = getLandingToken();
@@ -1587,7 +1596,7 @@ async function recordFunnelTelemetryEvent(name, options) {
1587
1596
  source: options?.source ?? "cli",
1588
1597
  host_type: options?.hostType ?? detectTelemetryHostType(),
1589
1598
  created_at: createdAt,
1590
- properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
1599
+ properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
1591
1600
  });
1592
1601
  }
1593
1602
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i;
@@ -1619,6 +1628,10 @@ function getApiKey() {
1619
1628
  }
1620
1629
  return "";
1621
1630
  }
1631
+ function getAgentId() {
1632
+ const config = loadConfig();
1633
+ return config?.agent_id ?? null;
1634
+ }
1622
1635
  var API_TIMEOUT_MS = parseInt(process.env.UNBROWSE_API_TIMEOUT ?? "8000", 10);
1623
1636
  var PUBLISH_TIMEOUT_MS = parseInt(process.env.UNBROWSE_PUBLISH_TIMEOUT ?? "30000", 10);
1624
1637
  async function validateApiKey(key) {
@@ -1930,6 +1943,170 @@ async function syncAgentWallet(wallet = getLocalWalletContext()) {
1930
1943
  return;
1931
1944
  saveConfig({ ...config, ...wallet });
1932
1945
  }
1946
+ async function getTransactionHistory(agentId) {
1947
+ return api("GET", `/v1/transactions/consumer/${agentId}`);
1948
+ }
1949
+ async function getCreatorEarnings(agentId) {
1950
+ return api("GET", `/v1/transactions/creator/${agentId}`);
1951
+ }
1952
+
1953
+ // ../../src/impact-log.ts
1954
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, appendFileSync, statSync, readFileSync as readFileSync4, renameSync, unlinkSync } from "node:fs";
1955
+ import { homedir as homedir4 } from "node:os";
1956
+ import { dirname as dirname2, join as join5 } from "node:path";
1957
+ var MAX_LOG_BYTES = 5 * 1024 * 1024;
1958
+ var MAX_ROTATIONS = 3;
1959
+ function getLogDir() {
1960
+ if (process.env.UNBROWSE_CONFIG_DIR)
1961
+ return process.env.UNBROWSE_CONFIG_DIR;
1962
+ const profile = process.env.UNBROWSE_PROFILE?.trim();
1963
+ return profile ? join5(homedir4(), ".unbrowse", "profiles", profile) : join5(homedir4(), ".unbrowse");
1964
+ }
1965
+ function getImpactLogPath() {
1966
+ return join5(getLogDir(), "impact-log.jsonl");
1967
+ }
1968
+ function ensureDir(path) {
1969
+ const dir = dirname2(path);
1970
+ if (!existsSync5(dir))
1971
+ mkdirSync2(dir, { recursive: true });
1972
+ }
1973
+ function rotateIfNeeded(path) {
1974
+ try {
1975
+ if (!existsSync5(path))
1976
+ return;
1977
+ const size = statSync(path).size;
1978
+ if (size < MAX_LOG_BYTES)
1979
+ return;
1980
+ for (let i = MAX_ROTATIONS;i >= 1; i--) {
1981
+ const older = `${path}.${i}`;
1982
+ if (!existsSync5(older))
1983
+ continue;
1984
+ if (i === MAX_ROTATIONS) {
1985
+ try {
1986
+ unlinkSync(older);
1987
+ } catch {}
1988
+ } else {
1989
+ try {
1990
+ renameSync(older, `${path}.${i + 1}`);
1991
+ } catch {}
1992
+ }
1993
+ }
1994
+ renameSync(path, `${path}.1`);
1995
+ } catch {}
1996
+ }
1997
+ function appendImpact(entry) {
1998
+ try {
1999
+ const hasSignal = (entry.time_saved_ms ?? 0) > 0 || (entry.tokens_saved ?? 0) > 0 || (entry.cost_saved_uc ?? 0) > 0 || entry.browser_avoided === true;
2000
+ if (!hasSignal)
2001
+ return;
2002
+ const path = getImpactLogPath();
2003
+ ensureDir(path);
2004
+ rotateIfNeeded(path);
2005
+ appendFileSync(path, JSON.stringify(entry) + `
2006
+ `, "utf8");
2007
+ } catch {}
2008
+ }
2009
+ function impactFromResult(command, result, extras = {}) {
2010
+ if (!result || typeof result !== "object")
2011
+ return null;
2012
+ const r = result;
2013
+ const impact = r.impact ?? null;
2014
+ if (!impact || typeof impact !== "object")
2015
+ return null;
2016
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
2017
+ return {
2018
+ ts: new Date().toISOString(),
2019
+ command,
2020
+ source: typeof impact.source === "string" ? impact.source : undefined,
2021
+ domain: extras.domain,
2022
+ intent: extras.intent,
2023
+ skill_id: extras.skill_id ?? (typeof r.skill_id === "string" ? r.skill_id : undefined),
2024
+ endpoint_id: extras.endpoint_id ?? (typeof r.endpoint_id === "string" ? r.endpoint_id : undefined),
2025
+ time_saved_ms: num(impact.time_saved_ms),
2026
+ time_saved_pct: num(impact.time_saved_pct),
2027
+ tokens_saved: num(impact.tokens_saved),
2028
+ tokens_saved_pct: num(impact.tokens_saved_pct),
2029
+ cost_saved_uc: num(impact.cost_saved_uc),
2030
+ browser_avoided: impact.browser_avoided === true,
2031
+ success: r.error == null
2032
+ };
2033
+ }
2034
+ function readImpactSummary() {
2035
+ const path = getImpactLogPath();
2036
+ const summary = {
2037
+ total_runs: 0,
2038
+ successful_runs: 0,
2039
+ browser_avoided_runs: 0,
2040
+ total_time_saved_ms: 0,
2041
+ total_tokens_saved: 0,
2042
+ total_cost_saved_uc: 0,
2043
+ avg_time_saved_pct: 0,
2044
+ avg_tokens_saved_pct: 0,
2045
+ by_source: {},
2046
+ first_entry_at: null,
2047
+ last_entry_at: null
2048
+ };
2049
+ const files = [];
2050
+ for (let i = MAX_ROTATIONS;i >= 1; i--) {
2051
+ const rotated = `${path}.${i}`;
2052
+ if (existsSync5(rotated))
2053
+ files.push(rotated);
2054
+ }
2055
+ if (existsSync5(path))
2056
+ files.push(path);
2057
+ if (files.length === 0)
2058
+ return summary;
2059
+ let timePctSum = 0;
2060
+ let timePctCount = 0;
2061
+ let tokenPctSum = 0;
2062
+ let tokenPctCount = 0;
2063
+ for (const file of files) {
2064
+ let raw;
2065
+ try {
2066
+ raw = readFileSync4(file, "utf8");
2067
+ } catch {
2068
+ continue;
2069
+ }
2070
+ for (const line of raw.split(`
2071
+ `)) {
2072
+ const trimmed = line.trim();
2073
+ if (!trimmed)
2074
+ continue;
2075
+ let e;
2076
+ try {
2077
+ e = JSON.parse(trimmed);
2078
+ } catch {
2079
+ continue;
2080
+ }
2081
+ summary.total_runs += 1;
2082
+ if (e.success !== false)
2083
+ summary.successful_runs += 1;
2084
+ if (e.browser_avoided)
2085
+ summary.browser_avoided_runs += 1;
2086
+ summary.total_time_saved_ms += e.time_saved_ms ?? 0;
2087
+ summary.total_tokens_saved += e.tokens_saved ?? 0;
2088
+ summary.total_cost_saved_uc += e.cost_saved_uc ?? 0;
2089
+ if (typeof e.time_saved_pct === "number") {
2090
+ timePctSum += e.time_saved_pct;
2091
+ timePctCount += 1;
2092
+ }
2093
+ if (typeof e.tokens_saved_pct === "number") {
2094
+ tokenPctSum += e.tokens_saved_pct;
2095
+ tokenPctCount += 1;
2096
+ }
2097
+ if (e.source) {
2098
+ summary.by_source[e.source] = (summary.by_source[e.source] ?? 0) + 1;
2099
+ }
2100
+ if (!summary.first_entry_at || e.ts < summary.first_entry_at)
2101
+ summary.first_entry_at = e.ts;
2102
+ if (!summary.last_entry_at || e.ts > summary.last_entry_at)
2103
+ summary.last_entry_at = e.ts;
2104
+ }
2105
+ }
2106
+ summary.avg_time_saved_pct = timePctCount > 0 ? Math.round(timePctSum / timePctCount) : 0;
2107
+ summary.avg_tokens_saved_pct = tokenPctCount > 0 ? Math.round(tokenPctSum / tokenPctCount) : 0;
2108
+ return summary;
2109
+ }
1933
2110
 
1934
2111
  // ../../src/cli/shortcuts.ts
1935
2112
  var linkedin = {
@@ -2130,7 +2307,7 @@ function buildDepsMetadata(pack, taskName) {
2130
2307
  init_paths();
2131
2308
  init_supervisor();
2132
2309
  init_version();
2133
- import { openSync, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
2310
+ import { existsSync as existsSync7, openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
2134
2311
  import path2 from "node:path";
2135
2312
  import { spawn } from "node:child_process";
2136
2313
  function isServerVersionMismatch(runningVersion, installedVersion, runningCodeHash, installedCodeHash) {
@@ -2168,14 +2345,14 @@ function isPidAlive(pid) {
2168
2345
  }
2169
2346
  function readPidState(pidFile) {
2170
2347
  try {
2171
- return JSON.parse(readFileSync4(pidFile, "utf-8"));
2348
+ return JSON.parse(readFileSync5(pidFile, "utf-8"));
2172
2349
  } catch {
2173
2350
  return null;
2174
2351
  }
2175
2352
  }
2176
2353
  function clearStalePidFile(pidFile) {
2177
2354
  try {
2178
- unlinkSync(pidFile);
2355
+ unlinkSync2(pidFile);
2179
2356
  } catch {}
2180
2357
  }
2181
2358
  function deriveListenEnv(baseUrl) {
@@ -2196,6 +2373,10 @@ function getServerSpawnSpec(metaUrl, entrypoint = resolveSiblingEntrypoint(metaU
2196
2373
  recordedEntrypoint: `${process.execPath} serve`
2197
2374
  };
2198
2375
  }
2376
+ const serverJs = path2.join(path2.dirname(entrypoint), "server.js");
2377
+ if (existsSync7(serverJs) && path2.basename(entrypoint) !== "server.js") {
2378
+ entrypoint = serverJs;
2379
+ }
2199
2380
  return {
2200
2381
  command: process.execPath,
2201
2382
  args: runtimeArgsForEntrypoint(metaUrl, entrypoint),
@@ -2209,7 +2390,7 @@ function spawnServer(baseUrl, metaUrl, pidFile, restartCount = 0) {
2209
2390
  const entrypoint = resolveSiblingEntrypoint(metaUrl, "index");
2210
2391
  const spawnSpec = getServerSpawnSpec(metaUrl, entrypoint);
2211
2392
  const logFile = getServerAutostartLogFile();
2212
- ensureDir(path2.dirname(logFile));
2393
+ ensureDir2(path2.dirname(logFile));
2213
2394
  const logFd = openSync(logFile, "a");
2214
2395
  const child = spawn(spawnSpec.command, spawnSpec.args, {
2215
2396
  cwd: spawnSpec.cwd,
@@ -2341,7 +2522,7 @@ async function restartServer(baseUrl, metaUrl) {
2341
2522
  }
2342
2523
 
2343
2524
  // ../../src/runtime/paths.ts
2344
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, realpathSync as realpathSync2 } from "node:fs";
2525
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, realpathSync as realpathSync2 } from "node:fs";
2345
2526
  import path3 from "node:path";
2346
2527
  import { createRequire as createRequire3 } from "node:module";
2347
2528
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
@@ -2361,7 +2542,7 @@ function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
2361
2542
  const req = createRequire3(metaUrl);
2362
2543
  const tsxPkg = req.resolve("tsx/package.json");
2363
2544
  const tsxLoader = path3.join(path3.dirname(tsxPkg), "dist", "loader.mjs");
2364
- if (existsSync6(tsxLoader))
2545
+ if (existsSync8(tsxLoader))
2365
2546
  return ["--import", pathToFileURL2(tsxLoader).href, entrypoint];
2366
2547
  } catch {}
2367
2548
  return ["--import", "tsx", entrypoint];
@@ -2391,8 +2572,8 @@ init_publish();
2391
2572
  init_settings();
2392
2573
  init_graph();
2393
2574
  init_schema_review();
2394
- import { join as join9 } from "node:path";
2395
- var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join9(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
2575
+ import { join as join10 } from "node:path";
2576
+ var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ?? join10(process.env.HOME ?? "/tmp", ".unbrowse", "skill-snapshots");
2396
2577
  var indexInFlight2 = new Map;
2397
2578
  var pendingIndexJobs2 = new Map;
2398
2579
  async function drainPendingIndexJobs() {
@@ -2430,13 +2611,13 @@ init_client2();
2430
2611
  init_logger();
2431
2612
  init_wallet();
2432
2613
  import { execFileSync as execFileSync3 } from "node:child_process";
2433
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2614
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
2434
2615
  import os4 from "node:os";
2435
2616
  import path7 from "node:path";
2436
2617
 
2437
2618
  // ../../src/runtime/update-hints.ts
2438
2619
  init_paths();
2439
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
2620
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2440
2621
  import os3 from "node:os";
2441
2622
  import path6 from "node:path";
2442
2623
  var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
@@ -2449,20 +2630,20 @@ function getConfigDir2() {
2449
2630
  return process.env.UNBROWSE_CONFIG_DIR;
2450
2631
  return path6.join(getHomeDir(), ".unbrowse");
2451
2632
  }
2452
- function ensureDir2(dir) {
2453
- if (!existsSync9(dir))
2454
- mkdirSync5(dir, { recursive: true });
2633
+ function ensureDir3(dir) {
2634
+ if (!existsSync11(dir))
2635
+ mkdirSync6(dir, { recursive: true });
2455
2636
  return dir;
2456
2637
  }
2457
2638
  function readJsonFile(file) {
2458
2639
  try {
2459
- return JSON.parse(readFileSync6(file, "utf8"));
2640
+ return JSON.parse(readFileSync7(file, "utf8"));
2460
2641
  } catch {
2461
2642
  return null;
2462
2643
  }
2463
2644
  }
2464
2645
  function writeJsonFile(file, value) {
2465
- ensureDir2(path6.dirname(file));
2646
+ ensureDir3(path6.dirname(file));
2466
2647
  writeFileSync4(file, `${JSON.stringify(value, null, 2)}
2467
2648
  `);
2468
2649
  }
@@ -2473,7 +2654,7 @@ function detectRepoRoot(start2) {
2473
2654
  let dir = path6.resolve(start2);
2474
2655
  const root = path6.parse(dir).root;
2475
2656
  while (dir !== root) {
2476
- if (existsSync9(path6.join(dir, ".git")))
2657
+ if (existsSync11(path6.join(dir, ".git")))
2477
2658
  return dir;
2478
2659
  dir = path6.dirname(dir);
2479
2660
  }
@@ -2552,13 +2733,13 @@ codex_hooks = true
2552
2733
  }
2553
2734
  function writeCodexHook(metaUrl) {
2554
2735
  const configPath = getCodexConfigPath();
2555
- if (!existsSync9(path6.dirname(configPath))) {
2736
+ if (!existsSync11(path6.dirname(configPath))) {
2556
2737
  return { host: "codex", action: "not-detected", config_file: configPath };
2557
2738
  }
2558
2739
  try {
2559
2740
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2560
- const fileExistsBefore = existsSync9(configPath);
2561
- let content = fileExistsBefore ? readFileSync6(configPath, "utf8") : "";
2741
+ const fileExistsBefore = existsSync11(configPath);
2742
+ let content = fileExistsBefore ? readFileSync7(configPath, "utf8") : "";
2562
2743
  const previous = content;
2563
2744
  content = ensureCodexHooksFeature(content);
2564
2745
  if (!content.includes("unbrowse-update-hint.mjs")) {
@@ -2592,13 +2773,13 @@ command = ${JSON.stringify(command)}
2592
2773
  }
2593
2774
  function writeClaudeHook(metaUrl) {
2594
2775
  const settingsPath = getClaudeSettingsPath();
2595
- if (!existsSync9(path6.dirname(settingsPath))) {
2776
+ if (!existsSync11(path6.dirname(settingsPath))) {
2596
2777
  return { host: "claude", action: "not-detected", config_file: settingsPath };
2597
2778
  }
2598
2779
  try {
2599
2780
  const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
2600
2781
  const command = `node "${hookScript}"`;
2601
- const fileExistsBefore = existsSync9(settingsPath);
2782
+ const fileExistsBefore = existsSync11(settingsPath);
2602
2783
  const settings = readJsonFile(settingsPath) ?? {};
2603
2784
  settings.hooks ??= {};
2604
2785
  settings.hooks.SessionStart ??= [];
@@ -2663,7 +2844,7 @@ function getOpenCodeProjectCommandsDir(cwd) {
2663
2844
  return path7.join(cwd, ".opencode", "commands");
2664
2845
  }
2665
2846
  function detectOpenCode(cwd) {
2666
- return hasBinary("opencode") || existsSync10(path7.join(resolveConfigHome(), "opencode")) || existsSync10(path7.join(cwd, ".opencode"));
2847
+ return hasBinary("opencode") || existsSync12(path7.join(resolveConfigHome(), "opencode")) || existsSync12(path7.join(cwd, ".opencode"));
2667
2848
  }
2668
2849
  function renderOpenCodeCommand() {
2669
2850
  return `---
@@ -2691,12 +2872,12 @@ function writeOpenCodeCommand(scope, cwd) {
2691
2872
  if (scope === "auto" && !detected) {
2692
2873
  return { detected: false, action: "not-detected", scope: "off" };
2693
2874
  }
2694
- const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync10(path7.join(cwd, ".opencode")) ? "project" : "global";
2875
+ const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync12(path7.join(cwd, ".opencode")) ? "project" : "global";
2695
2876
  const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
2696
- const commandFile = path7.join(ensureDir(commandsDir), "unbrowse.md");
2877
+ const commandFile = path7.join(ensureDir2(commandsDir), "unbrowse.md");
2697
2878
  const content = renderOpenCodeCommand();
2698
- const action2 = existsSync10(commandFile) ? "updated" : "installed";
2699
- mkdirSync6(path7.dirname(commandFile), { recursive: true });
2879
+ const action2 = existsSync12(commandFile) ? "updated" : "installed";
2880
+ mkdirSync7(path7.dirname(commandFile), { recursive: true });
2700
2881
  writeFileSync5(commandFile, content);
2701
2882
  return {
2702
2883
  detected: detected || scope !== "auto",
@@ -2707,10 +2888,10 @@ function writeOpenCodeCommand(scope, cwd) {
2707
2888
  }
2708
2889
  async function ensureBrowserEngineInstalled() {
2709
2890
  const binary = findKuriBinary();
2710
- if (existsSync10(binary)) {
2891
+ if (existsSync12(binary)) {
2711
2892
  return { installed: true, action: "already-installed" };
2712
2893
  }
2713
- const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync10(path7.join(candidate, "build.zig")));
2894
+ const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync12(path7.join(candidate, "build.zig")));
2714
2895
  if (!sourceDir) {
2715
2896
  return {
2716
2897
  installed: false,
@@ -2732,7 +2913,7 @@ async function ensureBrowserEngineInstalled() {
2732
2913
  timeout: 300000
2733
2914
  });
2734
2915
  const builtBinary = findKuriBinary();
2735
- if (existsSync10(builtBinary)) {
2916
+ if (existsSync12(builtBinary)) {
2736
2917
  return {
2737
2918
  installed: true,
2738
2919
  action: "installed",
@@ -2757,7 +2938,7 @@ async function runSetup(options) {
2757
2938
  const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
2758
2939
  const walletCheck = checkWalletConfigured();
2759
2940
  const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
2760
- const lobsterInstalled = hasBinary("lobstercash") || existsSync10(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2941
+ const lobsterInstalled = hasBinary("lobstercash") || existsSync12(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
2761
2942
  if (!skipWalletSetup && !walletCheck.configured && lobsterInstalled) {
2762
2943
  console.log("[unbrowse] Crossmint lobster.cash detected but wallet not configured — running wallet setup...");
2763
2944
  try {
@@ -2797,7 +2978,7 @@ async function runSetup(options) {
2797
2978
 
2798
2979
  // ../../src/runtime/update-hints.ts
2799
2980
  init_paths();
2800
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
2981
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
2801
2982
  import os5 from "node:os";
2802
2983
  import path8 from "node:path";
2803
2984
  var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
@@ -2810,20 +2991,20 @@ function getConfigDir3() {
2810
2991
  return process.env.UNBROWSE_CONFIG_DIR;
2811
2992
  return path8.join(getHomeDir2(), ".unbrowse");
2812
2993
  }
2813
- function ensureDir3(dir) {
2814
- if (!existsSync11(dir))
2815
- mkdirSync7(dir, { recursive: true });
2994
+ function ensureDir4(dir) {
2995
+ if (!existsSync13(dir))
2996
+ mkdirSync8(dir, { recursive: true });
2816
2997
  return dir;
2817
2998
  }
2818
2999
  function readJsonFile2(file) {
2819
3000
  try {
2820
- return JSON.parse(readFileSync7(file, "utf8"));
3001
+ return JSON.parse(readFileSync8(file, "utf8"));
2821
3002
  } catch {
2822
3003
  return null;
2823
3004
  }
2824
3005
  }
2825
3006
  function writeJsonFile2(file, value) {
2826
- ensureDir3(path8.dirname(file));
3007
+ ensureDir4(path8.dirname(file));
2827
3008
  writeFileSync6(file, `${JSON.stringify(value, null, 2)}
2828
3009
  `);
2829
3010
  }
@@ -2837,7 +3018,7 @@ function detectRepoRoot2(start2) {
2837
3018
  let dir = path8.resolve(start2);
2838
3019
  const root = path8.parse(dir).root;
2839
3020
  while (dir !== root) {
2840
- if (existsSync11(path8.join(dir, ".git")))
3021
+ if (existsSync13(path8.join(dir, ".git")))
2841
3022
  return dir;
2842
3023
  dir = path8.dirname(dir);
2843
3024
  }
@@ -2870,7 +3051,7 @@ function detectInstallHost2(repoRoot) {
2870
3051
  function getInstalledVersion(metaUrl) {
2871
3052
  const packageRoot = getPackageRoot(metaUrl);
2872
3053
  try {
2873
- const pkg = JSON.parse(readFileSync7(path8.join(packageRoot, "package.json"), "utf8"));
3054
+ const pkg = JSON.parse(readFileSync8(path8.join(packageRoot, "package.json"), "utf8"));
2874
3055
  return pkg.version ?? "unknown";
2875
3056
  } catch {
2876
3057
  return "unknown";
@@ -3118,6 +3299,14 @@ function formatSavedDuration(ms) {
3118
3299
  return `${(ms / 1000).toFixed(1)}s`;
3119
3300
  return `${ms}ms`;
3120
3301
  }
3302
+ function formatCostUsd(uc) {
3303
+ const usd = uc / 1e6;
3304
+ if (usd >= 1)
3305
+ return `$${usd.toFixed(2)}`;
3306
+ if (usd >= 0.01)
3307
+ return `$${usd.toFixed(3)}`;
3308
+ return `$${usd.toFixed(4)}`;
3309
+ }
3121
3310
  function emitImpactSummary(result) {
3122
3311
  const impact = result.impact;
3123
3312
  if (!impact)
@@ -3126,14 +3315,17 @@ function emitImpactSummary(result) {
3126
3315
  const tokensSaved = typeof impact.tokens_saved === "number" ? impact.tokens_saved : 0;
3127
3316
  const timeSavedPct = typeof impact.time_saved_pct === "number" ? impact.time_saved_pct : 0;
3128
3317
  const tokensSavedPct = typeof impact.tokens_saved_pct === "number" ? impact.tokens_saved_pct : 0;
3318
+ const costSavedUc = typeof impact.cost_saved_uc === "number" ? impact.cost_saved_uc : 0;
3129
3319
  const browserAvoided = impact.browser_avoided === true;
3130
- if (timeSavedMs <= 0 && tokensSaved <= 0 && !browserAvoided)
3320
+ if (timeSavedMs <= 0 && tokensSaved <= 0 && costSavedUc <= 0 && !browserAvoided)
3131
3321
  return;
3132
3322
  const parts = [];
3133
3323
  if (timeSavedMs > 0)
3134
3324
  parts.push(`${formatSavedDuration(timeSavedMs)} saved (${timeSavedPct}% faster)`);
3135
3325
  if (tokensSaved > 0)
3136
3326
  parts.push(`${tokensSaved.toLocaleString("en-US")} tokens saved (${tokensSavedPct}% less context)`);
3327
+ if (costSavedUc > 0)
3328
+ parts.push(`${formatCostUsd(costSavedUc)} saved`);
3137
3329
  if (browserAvoided)
3138
3330
  parts.push("browser avoided");
3139
3331
  info(parts.join(" \u2022 "));
@@ -3278,6 +3470,11 @@ async function cmdResolve(flags) {
3278
3470
  }
3279
3471
  result = slimTrace(result);
3280
3472
  emitImpactSummary(result);
3473
+ {
3474
+ const entry = impactFromResult("resolve", result, { intent, domain });
3475
+ if (entry)
3476
+ appendImpact(entry);
3477
+ }
3281
3478
  emitNextActionSummary(result);
3282
3479
  const skill = result.skill;
3283
3480
  const trace = result.trace;
@@ -3448,6 +3645,14 @@ async function cmdExecute(flags) {
3448
3645
  }
3449
3646
  result = slimTrace(result);
3450
3647
  emitImpactSummary(result);
3648
+ {
3649
+ const entry = impactFromResult("execute", result, {
3650
+ skill_id: skillId,
3651
+ endpoint_id: typeof flags.endpoint === "string" ? flags.endpoint : undefined
3652
+ });
3653
+ if (entry)
3654
+ appendImpact(entry);
3655
+ }
3451
3656
  emitNextActionSummary(result);
3452
3657
  const pathFlag = flags.path;
3453
3658
  const extractFlag = flags.extract;
@@ -3536,6 +3741,25 @@ async function cmdFeedback(flags) {
3536
3741
  body.diagnostics = JSON.parse(flags.diagnostics);
3537
3742
  output(await api2("POST", "/v1/feedback", body), !!flags.pretty);
3538
3743
  }
3744
+ async function cmdAnnotate(flags) {
3745
+ const skillId = flags.skill;
3746
+ const endpointId = flags.endpoint;
3747
+ if (!skillId || !endpointId)
3748
+ die("--skill and --endpoint are required");
3749
+ const body = {};
3750
+ if (flags.text) {
3751
+ body.annotations = [{ text: flags.text }];
3752
+ }
3753
+ if (flags.constraint) {
3754
+ const parts = flags.constraint.split(":");
3755
+ if (parts.length >= 3) {
3756
+ body.constraints = [{ param: parts[0], rule: parts[1], message: parts.slice(2).join(":") }];
3757
+ }
3758
+ }
3759
+ if (!body.annotations && !body.constraints)
3760
+ die("--text or --constraint required");
3761
+ output(await api2("POST", `/v1/skills/${skillId}/endpoints/${endpointId}/annotate`, body), !!flags.pretty);
3762
+ }
3539
3763
  async function cmdReview(flags) {
3540
3764
  const skillId = flags.skill;
3541
3765
  if (!skillId)
@@ -3750,6 +3974,7 @@ var CLI_REFERENCE = {
3750
3974
  { name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Search cached indexed/published routes and optionally execute the top trusted endpoint" },
3751
3975
  { name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
3752
3976
  { name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
3977
+ { name: "annotate", usage: "--skill ID --endpoint ID --text 'tip' [--constraint 'param:rule:message']", desc: "Contribute best practices or constraints for an endpoint" },
3753
3978
  { name: "review", usage: "--skill ID --endpoints '[...]'", desc: "Push reviewed descriptions/schema metadata back to a captured skill before publish" },
3754
3979
  { name: "index", usage: "--skill ID", desc: "Recompute local graph/contracts/export from cached skill state only" },
3755
3980
  { name: "publish", usage: "--skill ID [--confirm-publish] [--endpoints '[...]']", desc: "Re-index locally, inspect publish-review metadata, then publish/share from cached skill state" },
@@ -3777,7 +4002,8 @@ var CLI_REFERENCE = {
3777
4002
  { name: "back", usage: "[--session id]", desc: "Navigate back" },
3778
4003
  { name: "forward", usage: "[--session id]", desc: "Navigate forward" },
3779
4004
  { name: "sync", usage: "[--session id]", desc: "Checkpoint current capture, keep tab open, queue background index + publish, then inspect via skill/publish review" },
3780
- { name: "close", usage: "[--session id]", desc: "Checkpoint capture, queue background index + publish, close browse session, then inspect via skill/publish review" }
4005
+ { name: "close", usage: "[--session id]", desc: "Checkpoint capture, queue background index + publish, close browse session, then inspect via skill/publish review" },
4006
+ { name: "stats", usage: "[--json] [--pretty]", desc: "Show lifetime time/tokens/cost saved and marketplace earnings/spending" }
3781
4007
  ],
3782
4008
  globalFlags: [
3783
4009
  { flag: "--pretty", desc: "Indented JSON output" },
@@ -3818,6 +4044,131 @@ var CLI_REFERENCE = {
3818
4044
  `unbrowse publish --skill abc --endpoints '[{"endpoint_id":"def","description":"Search court judgments by keywords","action_kind":"search","resource_kind":"judgment"}]'`
3819
4045
  ]
3820
4046
  };
4047
+ function formatTotalDuration(ms) {
4048
+ if (ms >= 3600000)
4049
+ return `${(ms / 3600000).toFixed(1)}h`;
4050
+ if (ms >= 60000)
4051
+ return `${(ms / 60000).toFixed(1)}m`;
4052
+ if (ms >= 1000)
4053
+ return `${(ms / 1000).toFixed(1)}s`;
4054
+ return `${ms}ms`;
4055
+ }
4056
+ async function cmdStats(flags) {
4057
+ const pretty = !!flags.pretty;
4058
+ const jsonOnly = !!flags.json;
4059
+ const local = readImpactSummary();
4060
+ const agentId = getAgentId();
4061
+ let profile = null;
4062
+ let earnings = null;
4063
+ let spending = null;
4064
+ const remoteErrors = {};
4065
+ if (agentId) {
4066
+ const results = await Promise.allSettled([
4067
+ getMyProfile(),
4068
+ getCreatorEarnings(agentId),
4069
+ getTransactionHistory(agentId)
4070
+ ]);
4071
+ if (results[0].status === "fulfilled")
4072
+ profile = results[0].value;
4073
+ else
4074
+ remoteErrors.profile = results[0].reason?.message ?? String(results[0].reason);
4075
+ if (results[1].status === "fulfilled")
4076
+ earnings = results[1].value;
4077
+ else
4078
+ remoteErrors.earnings = results[1].reason?.message ?? String(results[1].reason);
4079
+ if (results[2].status === "fulfilled")
4080
+ spending = results[2].value;
4081
+ else
4082
+ remoteErrors.spending = results[2].reason?.message ?? String(results[2].reason);
4083
+ } else {
4084
+ remoteErrors.profile = "No agent_id in local config. Run `unbrowse setup` to register.";
4085
+ }
4086
+ const earnedUsd = earnings?.ledger?.total_earned_usd ?? 0;
4087
+ const spentUsd = spending?.ledger?.total_spent_usd ?? 0;
4088
+ const netUsd = earnedUsd - spentUsd;
4089
+ const savedUsd = local.total_cost_saved_uc / 1e6;
4090
+ const payload = {
4091
+ agent_id: agentId,
4092
+ profile,
4093
+ impact: {
4094
+ total_runs: local.total_runs,
4095
+ successful_runs: local.successful_runs,
4096
+ browser_avoided_runs: local.browser_avoided_runs,
4097
+ total_time_saved_ms: local.total_time_saved_ms,
4098
+ total_time_saved_human: formatTotalDuration(local.total_time_saved_ms),
4099
+ total_tokens_saved: local.total_tokens_saved,
4100
+ total_cost_saved_usd: Number(savedUsd.toFixed(6)),
4101
+ avg_time_saved_pct: local.avg_time_saved_pct,
4102
+ avg_tokens_saved_pct: local.avg_tokens_saved_pct,
4103
+ by_source: local.by_source,
4104
+ first_entry_at: local.first_entry_at,
4105
+ last_entry_at: local.last_entry_at,
4106
+ log_path: getImpactLogPath()
4107
+ },
4108
+ earnings: {
4109
+ total_earned_usd: earnedUsd,
4110
+ total_earned_uc: earnings?.ledger?.total_earned_uc ?? 0,
4111
+ transaction_count: earnings?.ledger?.transaction_count ?? 0,
4112
+ last_transaction_at: earnings?.ledger?.last_transaction_at ?? null
4113
+ },
4114
+ spending: {
4115
+ total_spent_usd: spentUsd,
4116
+ total_spent_uc: spending?.ledger?.total_spent_uc ?? 0,
4117
+ transaction_count: spending?.ledger?.transaction_count ?? 0,
4118
+ last_transaction_at: spending?.ledger?.last_transaction_at ?? null
4119
+ },
4120
+ net_usd: netUsd,
4121
+ ...Object.keys(remoteErrors).length > 0 ? { remote_errors: remoteErrors } : {}
4122
+ };
4123
+ if (jsonOnly) {
4124
+ output(payload, pretty);
4125
+ return;
4126
+ }
4127
+ const lines = [];
4128
+ lines.push("Unbrowse stats");
4129
+ lines.push(` agent_id: ${agentId ?? "(not registered \u2014 run `unbrowse setup`)"}`);
4130
+ if (profile?.name)
4131
+ lines.push(` name: ${profile.name}`);
4132
+ lines.push("");
4133
+ lines.push("Impact (local, this machine):");
4134
+ if (local.total_runs === 0) {
4135
+ lines.push(" No resolve/execute runs recorded yet.");
4136
+ lines.push(` Log file: ${getImpactLogPath()}`);
4137
+ } else {
4138
+ lines.push(` Runs: ${local.total_runs} (${local.successful_runs} successful, ${local.browser_avoided_runs} browser-avoided)`);
4139
+ if (local.total_time_saved_ms > 0) {
4140
+ lines.push(` Time saved: ${formatTotalDuration(local.total_time_saved_ms)} (avg ${local.avg_time_saved_pct}% faster)`);
4141
+ }
4142
+ if (local.total_tokens_saved > 0) {
4143
+ lines.push(` Tokens saved: ${local.total_tokens_saved.toLocaleString("en-US")} (avg ${local.avg_tokens_saved_pct}% less context)`);
4144
+ }
4145
+ if (savedUsd > 0) {
4146
+ lines.push(` Cost saved: ${formatCostUsd(local.total_cost_saved_uc)}`);
4147
+ }
4148
+ if (Object.keys(local.by_source).length > 0) {
4149
+ const topSources = Object.entries(local.by_source).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([k, v]) => `${k}=${v}`).join(", ");
4150
+ lines.push(` By source: ${topSources}`);
4151
+ }
4152
+ }
4153
+ lines.push("");
4154
+ lines.push("Money (backend ledger):");
4155
+ if (!agentId) {
4156
+ lines.push(" (not registered \u2014 earnings/spending unavailable)");
4157
+ } else if (remoteErrors.earnings || remoteErrors.spending) {
4158
+ if (remoteErrors.earnings)
4159
+ lines.push(` earnings: error \u2014 ${remoteErrors.earnings}`);
4160
+ if (remoteErrors.spending)
4161
+ lines.push(` spending: error \u2014 ${remoteErrors.spending}`);
4162
+ } else {
4163
+ lines.push(` Earned: $${earnedUsd.toFixed(4)} (${earnings?.ledger?.transaction_count ?? 0} payouts)`);
4164
+ lines.push(` Spent: $${spentUsd.toFixed(4)} (${spending?.ledger?.transaction_count ?? 0} payments)`);
4165
+ lines.push(` Net: ${netUsd >= 0 ? "+" : ""}$${netUsd.toFixed(4)}`);
4166
+ }
4167
+ lines.push("");
4168
+ info(lines.join(`
4169
+ `));
4170
+ output(payload, pretty);
4171
+ }
3821
4172
  function printHelp() {
3822
4173
  const r = CLI_REFERENCE;
3823
4174
  const lines = ["unbrowse \u2014 shell-safe CLI for the local API", ""];
@@ -4239,6 +4590,8 @@ async function main() {
4239
4590
  return cmdUpgrade(flags);
4240
4591
  if (command === "connect-chrome")
4241
4592
  return cmdConnectChrome();
4593
+ if (command === "stats")
4594
+ return cmdStats(flags);
4242
4595
  const KNOWN_COMMANDS = new Set([
4243
4596
  "health",
4244
4597
  "mcp",
@@ -4248,6 +4601,7 @@ async function main() {
4248
4601
  "exec",
4249
4602
  "feedback",
4250
4603
  "fb",
4604
+ "annotate",
4251
4605
  "review",
4252
4606
  "index",
4253
4607
  "publish",
@@ -4282,7 +4636,8 @@ async function main() {
4282
4636
  "forward",
4283
4637
  "sync",
4284
4638
  "close",
4285
- "connect-chrome"
4639
+ "connect-chrome",
4640
+ "stats"
4286
4641
  ]);
4287
4642
  if (!KNOWN_COMMANDS.has(command)) {
4288
4643
  const pack = findSitePack(command);
@@ -4318,6 +4673,8 @@ async function main() {
4318
4673
  case "feedback":
4319
4674
  case "fb":
4320
4675
  return cmdFeedback(flags);
4676
+ case "annotate":
4677
+ return cmdAnnotate(flags);
4321
4678
  case "review":
4322
4679
  return cmdReview(flags);
4323
4680
  case "index":
@@ -4378,6 +4735,8 @@ async function main() {
4378
4735
  return cmdClose(flags);
4379
4736
  case "connect-chrome":
4380
4737
  return cmdConnectChrome();
4738
+ case "stats":
4739
+ return cmdStats(flags);
4381
4740
  default:
4382
4741
  info(`Unknown command: ${command}`);
4383
4742
  printHelp();