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