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.
- package/dist/cli.js +454 -96
- package/dist/index.js +2 -6
- package/dist/mcp.js +695 -46
- package/dist/server.js +25994 -0
- package/package.json +1 -2
- package/vendor/kuri/manifest.json +1 -1
- package/runtime-src/agent-outcome.ts +0 -166
- package/runtime-src/analytics-session.ts +0 -55
- package/runtime-src/api/browse-index.ts +0 -317
- package/runtime-src/api/browse-session.ts +0 -572
- package/runtime-src/api/browse-submit-prereqs.ts +0 -48
- package/runtime-src/api/browse-submit.ts +0 -1184
- package/runtime-src/api/routes.ts +0 -1823
- package/runtime-src/auth/browser-cookies.ts +0 -423
- package/runtime-src/auth/index.ts +0 -535
- package/runtime-src/auth/runtime.ts +0 -116
- package/runtime-src/browser/index.ts +0 -659
- package/runtime-src/browser/types.ts +0 -41
- package/runtime-src/build-info.generated.ts +0 -6
- package/runtime-src/capture/index.ts +0 -1794
- package/runtime-src/capture/prefetch.ts +0 -95
- package/runtime-src/capture/rsc.ts +0 -45
- package/runtime-src/cli/shortcuts.ts +0 -273
- package/runtime-src/cli.ts +0 -1572
- package/runtime-src/client/graph-client.ts +0 -100
- package/runtime-src/client/index.ts +0 -1425
- package/runtime-src/debug-trace.ts +0 -18
- package/runtime-src/domain.ts +0 -38
- package/runtime-src/execution/index.ts +0 -3397
- package/runtime-src/execution/retry.ts +0 -46
- package/runtime-src/execution/robots.ts +0 -167
- package/runtime-src/execution/search-forms.ts +0 -188
- package/runtime-src/extraction/index.ts +0 -1507
- package/runtime-src/foundry/publish-bundle.ts +0 -392
- package/runtime-src/graph/agent-augment.ts +0 -315
- package/runtime-src/graph/index.ts +0 -1524
- package/runtime-src/graph/local-fixtures.ts +0 -393
- package/runtime-src/graph/local-harness.ts +0 -646
- package/runtime-src/graph/planner.ts +0 -411
- package/runtime-src/graph/session.ts +0 -294
- package/runtime-src/graph/trace-store.ts +0 -136
- package/runtime-src/index.ts +0 -24
- package/runtime-src/indexer/index.ts +0 -465
- package/runtime-src/intent-match.ts +0 -1515
- package/runtime-src/kuri/client.ts +0 -1839
- package/runtime-src/logger.ts +0 -30
- package/runtime-src/marketplace/index.ts +0 -103
- package/runtime-src/mcp.ts +0 -1747
- package/runtime-src/orchestrator/browser-agent.ts +0 -374
- package/runtime-src/orchestrator/dag-advisor.ts +0 -59
- package/runtime-src/orchestrator/dag-feedback.ts +0 -257
- package/runtime-src/orchestrator/first-pass-action.ts +0 -403
- package/runtime-src/orchestrator/index.ts +0 -4480
- package/runtime-src/orchestrator/passive-publish.ts +0 -187
- package/runtime-src/orchestrator/timing-economics.ts +0 -80
- package/runtime-src/payments/cascade.ts +0 -137
- package/runtime-src/payments/index.ts +0 -270
- package/runtime-src/payments/lobster-pay.ts +0 -182
- package/runtime-src/payments/wallet.ts +0 -98
- package/runtime-src/publish/review-context.ts +0 -93
- package/runtime-src/publish/sanitize.ts +0 -197
- package/runtime-src/publish/schema-review.ts +0 -192
- package/runtime-src/publish-admission.ts +0 -388
- package/runtime-src/ratelimit/index.ts +0 -23
- package/runtime-src/reverse-engineer/bundle-scanner.ts +0 -127
- package/runtime-src/reverse-engineer/description-prompt.ts +0 -213
- package/runtime-src/reverse-engineer/index.ts +0 -1551
- package/runtime-src/router.ts +0 -17
- package/runtime-src/routing-telemetry.ts +0 -395
- package/runtime-src/runtime/browser-access.ts +0 -11
- package/runtime-src/runtime/browser-auth.ts +0 -12
- package/runtime-src/runtime/browser-host.ts +0 -48
- package/runtime-src/runtime/lifecycle.ts +0 -17
- package/runtime-src/runtime/local-server.ts +0 -311
- package/runtime-src/runtime/paths.ts +0 -99
- package/runtime-src/runtime/setup.ts +0 -251
- package/runtime-src/runtime/supervisor.ts +0 -69
- package/runtime-src/runtime/update-hints.ts +0 -351
- package/runtime-src/server.ts +0 -100
- package/runtime-src/session-logs.ts +0 -142
- package/runtime-src/settings.ts +0 -221
- package/runtime-src/single-binary.ts +0 -143
- package/runtime-src/site-policy.ts +0 -54
- package/runtime-src/stale-cleanup-runner.ts +0 -144
- package/runtime-src/stale-cleanup.ts +0 -133
- package/runtime-src/telemetry-attribution.ts +0 -120
- package/runtime-src/telemetry.ts +0 -253
- package/runtime-src/template-params.ts +0 -141
- package/runtime-src/transform/drift.ts +0 -60
- package/runtime-src/transform/index.ts +0 -277
- package/runtime-src/types/index.ts +0 -1
- package/runtime-src/types/skill.ts +0 -912
- package/runtime-src/vault/index.ts +0 -196
- package/runtime-src/verification/auth-gate.ts +0 -8
- package/runtime-src/verification/candidates.ts +0 -27
- package/runtime-src/verification/index.ts +0 -120
- package/runtime-src/verification/matrix.ts +0 -30
- package/runtime-src/version.ts +0 -148
- package/runtime-src/workflow/artifact.ts +0 -161
- package/runtime-src/workflow/compile.ts +0 -808
- package/runtime-src/workflow/publish.ts +0 -225
- 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
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
445
|
-
if (!
|
|
446
|
-
|
|
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
|
|
450
|
+
return ensureDir2(path.join(getUnbrowseHome(), "logs"));
|
|
451
451
|
}
|
|
452
452
|
function getRunDir() {
|
|
453
|
-
return
|
|
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
|
|
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) =>
|
|
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
|
|
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 ??
|
|
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
|
|
799
|
-
import { homedir as
|
|
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 =
|
|
822
|
-
VAULT_FILE =
|
|
823
|
-
KEY_FILE =
|
|
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
|
|
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 ??
|
|
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
|
|
1167
|
-
import { dirname as
|
|
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 =
|
|
1198
|
-
SKILL_SNAPSHOT_DIR2 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ??
|
|
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 =
|
|
1198
|
+
DOMAIN_CACHE_FILE = join9(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
|
|
1201
1199
|
try {
|
|
1202
|
-
if (
|
|
1203
|
-
const data = JSON.parse(
|
|
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 =
|
|
1219
|
-
if (!
|
|
1220
|
-
|
|
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 (
|
|
1228
|
-
const data = JSON.parse(
|
|
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
|
|
1325
|
-
import { homedir as
|
|
1326
|
-
import { join as
|
|
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 =
|
|
1332
|
-
if (!
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
2395
|
-
var SKILL_SNAPSHOT_DIR3 = process.env.UNBROWSE_SKILL_SNAPSHOT_DIR ??
|
|
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
|
|
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
|
|
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
|
|
2453
|
-
if (!
|
|
2454
|
-
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
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 (!
|
|
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 =
|
|
2561
|
-
let content = fileExistsBefore ?
|
|
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 (!
|
|
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 =
|
|
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") ||
|
|
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" :
|
|
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(
|
|
2876
|
+
const commandFile = path7.join(ensureDir2(commandsDir), "unbrowse.md");
|
|
2697
2877
|
const content = renderOpenCodeCommand();
|
|
2698
|
-
const action2 =
|
|
2699
|
-
|
|
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 (
|
|
2890
|
+
if (existsSync12(binary)) {
|
|
2711
2891
|
return { installed: true, action: "already-installed" };
|
|
2712
2892
|
}
|
|
2713
|
-
const sourceDir = getKuriSourceCandidates().find((candidate) =>
|
|
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 (
|
|
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") ||
|
|
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
|
|
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
|
|
2814
|
-
if (!
|
|
2815
|
-
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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();
|