unbrowse 3.0.2 → 3.1.0-experiments.01f0036
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 +677 -116
- package/dist/index.js +2 -6
- package/dist/mcp.js +765 -77
- package/dist/server.js +25994 -0
- package/package.json +1 -2
- package/vendor/kuri/darwin-arm64/kuri +0 -0
- package/vendor/kuri/darwin-x64/kuri +0 -0
- package/vendor/kuri/linux-arm64/kuri +0 -0
- package/vendor/kuri/linux-x64/kuri +0 -0
- package/vendor/kuri/manifest.json +7 -6
- 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 -1800
- 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 -1546
- package/runtime-src/client/graph-client.ts +0 -100
- package/runtime-src/client/index.ts +0 -1409
- package/runtime-src/debug-trace.ts +0 -18
- package/runtime-src/domain.ts +0 -38
- package/runtime-src/execution/index.ts +0 -3385
- 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 -1835
- package/runtime-src/logger.ts +0 -30
- package/runtime-src/marketplace/index.ts +0 -103
- package/runtime-src/mcp.ts +0 -1698
- 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 -4476
- 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/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
|
@@ -17,12 +17,21 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
17
17
|
});
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
20
29
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
21
30
|
var __promiseAll = (args) => Promise.all(args);
|
|
22
31
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
23
32
|
|
|
24
33
|
// ../../src/build-info.generated.ts
|
|
25
|
-
var BUILD_RELEASE_VERSION = "3.0.
|
|
34
|
+
var BUILD_RELEASE_VERSION = "3.1.0-experiments.01f0036", BUILD_GIT_SHA = "01f00363ffe9", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuMDFmMDAzNiIsImdpdF9zaGEiOiIwMWYwMDM2M2ZmZTkiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDAxZjAwMzYzZmZlOSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMDY6MDA6MDIuNjk0WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "8Lba-sSVxIWGBqxHRyCgPwKJD9Rn4vURXK08W92ZEGM", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
|
|
26
35
|
|
|
27
36
|
// ../../src/version.ts
|
|
28
37
|
import { createHash } from "crypto";
|
|
@@ -284,8 +293,114 @@ var init_telemetry_attribution = __esm(() => {
|
|
|
284
293
|
];
|
|
285
294
|
});
|
|
286
295
|
|
|
296
|
+
// ../../src/payments/lobster-pay.ts
|
|
297
|
+
var exports_lobster_pay = {};
|
|
298
|
+
__export(exports_lobster_pay, {
|
|
299
|
+
payAndRetry: () => payAndRetry,
|
|
300
|
+
lobsterX402Fetch: () => lobsterX402Fetch,
|
|
301
|
+
isLobsterAvailable: () => isLobsterAvailable
|
|
302
|
+
});
|
|
303
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
304
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
305
|
+
import { homedir as homedir2 } from "node:os";
|
|
306
|
+
import { join as join3 } from "node:path";
|
|
307
|
+
function getLobsterCommand() {
|
|
308
|
+
try {
|
|
309
|
+
execFileSync("lobstercash", ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
310
|
+
return { cmd: "lobstercash", prefix: [] };
|
|
311
|
+
} catch (_e) {}
|
|
312
|
+
try {
|
|
313
|
+
const npmPrefix = execFileSync("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
|
|
314
|
+
const lobsterPath = join3(npmPrefix, "bin", "lobstercash");
|
|
315
|
+
if (existsSync3(lobsterPath)) {
|
|
316
|
+
execFileSync(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
317
|
+
return { cmd: lobsterPath, prefix: [] };
|
|
318
|
+
}
|
|
319
|
+
} catch (_e) {}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
function lobsterCmd() {
|
|
323
|
+
if (cachedCommand === undefined)
|
|
324
|
+
cachedCommand = getLobsterCommand();
|
|
325
|
+
return cachedCommand;
|
|
326
|
+
}
|
|
327
|
+
function isLobsterAvailable() {
|
|
328
|
+
const agentsPath = join3(process.env.HOME || homedir2(), ".lobster", "agents.json");
|
|
329
|
+
return existsSync3(agentsPath);
|
|
330
|
+
}
|
|
331
|
+
function lobsterX402Fetch(url, options) {
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
const resolved = lobsterCmd();
|
|
334
|
+
if (!resolved) {
|
|
335
|
+
resolve({ success: false, body: "", error: "lobstercash CLI not in PATH" });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const { cmd, prefix } = resolved;
|
|
339
|
+
const args = [...prefix, "x402", "fetch", url, "--debug"];
|
|
340
|
+
if (options?.jsonBody) {
|
|
341
|
+
args.push("--json", options.jsonBody);
|
|
342
|
+
}
|
|
343
|
+
if (options?.headers) {
|
|
344
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
345
|
+
args.push("--header", `${key}:${value}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const timeout = options?.timeoutMs ?? LOBSTER_PAY_TIMEOUT_MS;
|
|
349
|
+
args.push("--timeout", String(timeout));
|
|
350
|
+
execFile(cmd, args, { timeout: timeout + 5000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
351
|
+
if (err) {
|
|
352
|
+
const msg = stderr?.trim() || err.message;
|
|
353
|
+
console.warn(`[lobster-pay] x402 fetch failed: ${msg}`);
|
|
354
|
+
resolve({ success: false, body: "", error: msg });
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (stderr) {
|
|
358
|
+
for (const line of stderr.split(`
|
|
359
|
+
`).filter(Boolean)) {
|
|
360
|
+
console.log(`[lobster-pay] ${line}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const statusMatch = stdout.match(/^Status:\s*(\d+)/m);
|
|
364
|
+
const statusCode = statusMatch ? parseInt(statusMatch[1], 10) : undefined;
|
|
365
|
+
if (statusCode && statusCode >= 400) {
|
|
366
|
+
resolve({ success: false, body: stdout, statusCode, error: `HTTP ${statusCode}` });
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
resolve({ success: true, body: stdout, statusCode });
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async function payAndRetry(fullUrl, options) {
|
|
374
|
+
if (!isLobsterAvailable()) {
|
|
375
|
+
console.log("[lobster-pay] lobster.cash not configured — skipping payment");
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
console.log(`[lobster-pay] attempting x402 payment for ${fullUrl}`);
|
|
379
|
+
const result = await lobsterX402Fetch(fullUrl, {
|
|
380
|
+
jsonBody: options?.body ? JSON.stringify(options.body) : undefined,
|
|
381
|
+
headers: options?.headers
|
|
382
|
+
});
|
|
383
|
+
if (!result.success) {
|
|
384
|
+
console.warn(`[lobster-pay] payment failed: ${result.error}`);
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
const raw = result.body;
|
|
389
|
+
const jsonStart = Math.min(...[raw.indexOf("{"), raw.indexOf("[")].filter((i) => i >= 0));
|
|
390
|
+
const jsonStr = jsonStart >= 0 ? raw.slice(jsonStart) : raw;
|
|
391
|
+
const data = JSON.parse(jsonStr);
|
|
392
|
+
console.log("[lobster-pay] payment successful — got paid response");
|
|
393
|
+
return { data, paid: true };
|
|
394
|
+
} catch (_e) {
|
|
395
|
+
console.warn("[lobster-pay] paid response was not valid JSON");
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
var LOBSTER_PAY_TIMEOUT_MS = 30000, cachedCommand = undefined;
|
|
400
|
+
var init_lobster_pay = () => {};
|
|
401
|
+
|
|
287
402
|
// ../../src/runtime/paths.ts
|
|
288
|
-
import { existsSync as
|
|
403
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, realpathSync } from "node:fs";
|
|
289
404
|
import os from "node:os";
|
|
290
405
|
import path from "node:path";
|
|
291
406
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -299,7 +414,7 @@ function getPackageRoot(metaUrl) {
|
|
|
299
414
|
let dir = getModuleDir(metaUrl);
|
|
300
415
|
const root = path.parse(dir).root;
|
|
301
416
|
while (dir !== root) {
|
|
302
|
-
if (
|
|
417
|
+
if (existsSync6(path.join(dir, "package.json")))
|
|
303
418
|
return dir;
|
|
304
419
|
dir = path.dirname(dir);
|
|
305
420
|
}
|
|
@@ -318,7 +433,7 @@ function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
|
|
|
318
433
|
const req = createRequire2(metaUrl);
|
|
319
434
|
const tsxPkg = req.resolve("tsx/package.json");
|
|
320
435
|
const tsxLoader = path.join(path.dirname(tsxPkg), "dist", "loader.mjs");
|
|
321
|
-
if (
|
|
436
|
+
if (existsSync6(tsxLoader))
|
|
322
437
|
return ["--import", pathToFileURL(tsxLoader).href, entrypoint];
|
|
323
438
|
} catch {}
|
|
324
439
|
return ["--import", "tsx", entrypoint];
|
|
@@ -326,16 +441,16 @@ function runtimeArgsForEntrypoint(metaUrl, entrypoint) {
|
|
|
326
441
|
function getUnbrowseHome() {
|
|
327
442
|
return path.join(os.homedir(), ".unbrowse");
|
|
328
443
|
}
|
|
329
|
-
function
|
|
330
|
-
if (!
|
|
331
|
-
|
|
444
|
+
function ensureDir2(dir) {
|
|
445
|
+
if (!existsSync6(dir))
|
|
446
|
+
mkdirSync3(dir, { recursive: true });
|
|
332
447
|
return dir;
|
|
333
448
|
}
|
|
334
449
|
function getLogsDir() {
|
|
335
|
-
return
|
|
450
|
+
return ensureDir2(path.join(getUnbrowseHome(), "logs"));
|
|
336
451
|
}
|
|
337
452
|
function getRunDir() {
|
|
338
|
-
return
|
|
453
|
+
return ensureDir2(process.env.UNBROWSE_RUN_DIR || path.join(getUnbrowseHome(), "run"));
|
|
339
454
|
}
|
|
340
455
|
function sanitizeSegment(value) {
|
|
341
456
|
return value.replace(/[^a-zA-Z0-9.-]+/g, "_");
|
|
@@ -470,8 +585,8 @@ var init_logger = __esm(() => {
|
|
|
470
585
|
});
|
|
471
586
|
|
|
472
587
|
// ../../src/kuri/client.ts
|
|
473
|
-
import { execFileSync, spawn as spawn2 } from "node:child_process";
|
|
474
|
-
import { existsSync as
|
|
588
|
+
import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
|
|
589
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
475
590
|
import path5 from "node:path";
|
|
476
591
|
function createBrokerState(port = KURI_DEFAULT_PORT) {
|
|
477
592
|
return {
|
|
@@ -496,12 +611,14 @@ function currentBundledKuriTarget() {
|
|
|
496
611
|
return "linux-arm64";
|
|
497
612
|
if (process.platform === "linux" && process.arch === "x64")
|
|
498
613
|
return "linux-x64";
|
|
614
|
+
if (process.platform === "win32" && process.arch === "x64")
|
|
615
|
+
return "win-x64";
|
|
499
616
|
return null;
|
|
500
617
|
}
|
|
501
618
|
function resolveBinaryOnPath(name) {
|
|
502
619
|
const checker = process.platform === "win32" ? "where" : "which";
|
|
503
620
|
try {
|
|
504
|
-
const output =
|
|
621
|
+
const output = execFileSync2(checker, [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
505
622
|
const match = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
506
623
|
return match || null;
|
|
507
624
|
} catch {
|
|
@@ -544,7 +661,7 @@ function findKuriBinary() {
|
|
|
544
661
|
if (process.env.KURI_BIN)
|
|
545
662
|
return process.env.KURI_BIN;
|
|
546
663
|
const candidates = getKuriBinaryCandidates();
|
|
547
|
-
return candidates.find((candidate) =>
|
|
664
|
+
return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0] ?? kuriBinaryName();
|
|
548
665
|
}
|
|
549
666
|
var KURI_DEFAULT_PORT = 7700, defaultBrokerState, brokerClients;
|
|
550
667
|
var init_client2 = __esm(() => {
|
|
@@ -567,7 +684,7 @@ var init_browser_access = () => {};
|
|
|
567
684
|
|
|
568
685
|
// ../../src/capture/index.ts
|
|
569
686
|
import { nanoid as nanoid2 } from "nanoid";
|
|
570
|
-
var activeTabRegistry, interceptorInjectedTabs;
|
|
687
|
+
var activeTabRegistry, interceptorInjectedTabs, cdpDocStartTabs, cdpCapturedHeaders;
|
|
571
688
|
var init_capture = __esm(() => {
|
|
572
689
|
init_client2();
|
|
573
690
|
init_domain();
|
|
@@ -575,6 +692,8 @@ var init_capture = __esm(() => {
|
|
|
575
692
|
init_browser_access();
|
|
576
693
|
activeTabRegistry = new Set;
|
|
577
694
|
interceptorInjectedTabs = new Set;
|
|
695
|
+
cdpDocStartTabs = new Set;
|
|
696
|
+
cdpCapturedHeaders = new Map;
|
|
578
697
|
});
|
|
579
698
|
|
|
580
699
|
// ../../src/transform/index.ts
|
|
@@ -584,11 +703,11 @@ var init_transform = __esm(() => {
|
|
|
584
703
|
});
|
|
585
704
|
|
|
586
705
|
// ../../src/debug-trace.ts
|
|
587
|
-
import { join as
|
|
706
|
+
import { join as join6 } from "node:path";
|
|
588
707
|
import { nanoid as nanoid3 } from "nanoid";
|
|
589
708
|
var TRACE_DIR;
|
|
590
709
|
var init_debug_trace = __esm(() => {
|
|
591
|
-
TRACE_DIR = process.env.TRACES_DIR ??
|
|
710
|
+
TRACE_DIR = process.env.TRACES_DIR ?? join6(process.cwd(), "traces");
|
|
592
711
|
});
|
|
593
712
|
|
|
594
713
|
// ../../src/publish/sanitize.ts
|
|
@@ -677,9 +796,17 @@ var init_bundle_scanner = __esm(() => {
|
|
|
677
796
|
init_logger();
|
|
678
797
|
});
|
|
679
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
|
+
|
|
680
807
|
// ../../src/vault/index.ts
|
|
681
|
-
import { join as
|
|
682
|
-
import { homedir as
|
|
808
|
+
import { join as join7 } from "path";
|
|
809
|
+
import { homedir as homedir5 } from "os";
|
|
683
810
|
function normalizeKeytarModule(mod) {
|
|
684
811
|
let candidate = mod;
|
|
685
812
|
for (let depth = 0;depth < 3; depth++) {
|
|
@@ -701,9 +828,9 @@ var init_vault = __esm(async () => {
|
|
|
701
828
|
try {
|
|
702
829
|
keytar = normalizeKeytarModule(await import("keytar"));
|
|
703
830
|
} catch {}
|
|
704
|
-
VAULT_DIR =
|
|
705
|
-
VAULT_FILE =
|
|
706
|
-
KEY_FILE =
|
|
831
|
+
VAULT_DIR = join7(homedir5(), ".unbrowse", "vault");
|
|
832
|
+
VAULT_FILE = join7(VAULT_DIR, "credentials.enc");
|
|
833
|
+
KEY_FILE = join7(VAULT_DIR, ".key");
|
|
707
834
|
vaultLock = Promise.resolve();
|
|
708
835
|
});
|
|
709
836
|
|
|
@@ -780,18 +907,6 @@ var init_extraction = __esm(() => {
|
|
|
780
907
|
CHROME_TAGS = new Set(["nav", "footer", "header"]);
|
|
781
908
|
});
|
|
782
909
|
|
|
783
|
-
// ../../src/graph/agent-augment.ts
|
|
784
|
-
var DEFAULT_MODEL, ENABLED, AUGMENT_TIMEOUT_MS, MAX_AUGMENT_ENDPOINTS, MAX_AUGMENT_PAYLOAD_CHARS, GENERIC_SEMANTIC_TYPES;
|
|
785
|
-
var init_agent_augment = __esm(() => {
|
|
786
|
-
init_graph();
|
|
787
|
-
DEFAULT_MODEL = process.env.UNBROWSE_AGENT_SEMANTIC_MODEL ?? process.env.UNBROWSE_AGENT_JUDGE_MODEL ?? "gpt-4.1-mini";
|
|
788
|
-
ENABLED = process.env.UNBROWSE_AGENT_SEMANTIC_AUGMENT !== "0";
|
|
789
|
-
AUGMENT_TIMEOUT_MS = Number(process.env.UNBROWSE_AGENT_SEMANTIC_TIMEOUT_MS ?? 8000);
|
|
790
|
-
MAX_AUGMENT_ENDPOINTS = Math.max(1, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_ENDPOINTS ?? 6));
|
|
791
|
-
MAX_AUGMENT_PAYLOAD_CHARS = Math.max(4000, Number(process.env.UNBROWSE_AGENT_SEMANTIC_MAX_PAYLOAD_CHARS ?? 24000));
|
|
792
|
-
GENERIC_SEMANTIC_TYPES = new Set(["identifier", "input", "resource", "entity", "item"]);
|
|
793
|
-
});
|
|
794
|
-
|
|
795
910
|
// ../../src/execution/search-forms.ts
|
|
796
911
|
var SEARCH_FIELD_NAMES, LOGIN_FIELD_NAMES, SUPPORTED_INPUT_TYPES;
|
|
797
912
|
var init_search_forms = __esm(() => {
|
|
@@ -854,7 +969,7 @@ var init_schema_review = __esm(() => {
|
|
|
854
969
|
});
|
|
855
970
|
|
|
856
971
|
// ../../src/indexer/index.ts
|
|
857
|
-
import { join as
|
|
972
|
+
import { join as join8 } from "node:path";
|
|
858
973
|
var SKILL_SNAPSHOT_DIR, indexInFlight, pendingIndexJobs;
|
|
859
974
|
var init_indexer = __esm(async () => {
|
|
860
975
|
init_graph();
|
|
@@ -869,7 +984,7 @@ var init_indexer = __esm(async () => {
|
|
|
869
984
|
init_graph();
|
|
870
985
|
init_schema_review();
|
|
871
986
|
await init_orchestrator();
|
|
872
|
-
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");
|
|
873
988
|
indexInFlight = new Map;
|
|
874
989
|
pendingIndexJobs = new Map;
|
|
875
990
|
});
|
|
@@ -907,6 +1022,7 @@ var init_execution = __esm(async () => {
|
|
|
907
1022
|
init_capture();
|
|
908
1023
|
init_reverse_engineer();
|
|
909
1024
|
init_bundle_scanner();
|
|
1025
|
+
init_token_resolver();
|
|
910
1026
|
init_marketplace();
|
|
911
1027
|
init_runtime();
|
|
912
1028
|
init_transform();
|
|
@@ -917,7 +1033,6 @@ var init_execution = __esm(async () => {
|
|
|
917
1033
|
init_domain();
|
|
918
1034
|
init_extraction();
|
|
919
1035
|
init_graph();
|
|
920
|
-
init_agent_augment();
|
|
921
1036
|
init_logger();
|
|
922
1037
|
init_version();
|
|
923
1038
|
init_search_forms();
|
|
@@ -1046,8 +1161,8 @@ var init_routing_telemetry = __esm(() => {
|
|
|
1046
1161
|
});
|
|
1047
1162
|
// ../../src/orchestrator/index.ts
|
|
1048
1163
|
import { nanoid as nanoid9 } from "nanoid";
|
|
1049
|
-
import { existsSync as
|
|
1050
|
-
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";
|
|
1051
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;
|
|
1052
1167
|
var init_orchestrator = __esm(async () => {
|
|
1053
1168
|
init_client();
|
|
@@ -1077,13 +1192,13 @@ var init_orchestrator = __esm(async () => {
|
|
|
1077
1192
|
captureInFlight = new Map;
|
|
1078
1193
|
captureDomainLocks = new Map;
|
|
1079
1194
|
skillRouteCache = new Map;
|
|
1080
|
-
ROUTE_CACHE_FILE =
|
|
1081
|
-
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");
|
|
1082
1197
|
domainSkillCache = new Map;
|
|
1083
|
-
DOMAIN_CACHE_FILE =
|
|
1198
|
+
DOMAIN_CACHE_FILE = join9(process.env.HOME ?? "/tmp", ".unbrowse", "domain-skill-cache.json");
|
|
1084
1199
|
try {
|
|
1085
|
-
if (
|
|
1086
|
-
const data = JSON.parse(
|
|
1200
|
+
if (existsSync10(DOMAIN_CACHE_FILE)) {
|
|
1201
|
+
const data = JSON.parse(readFileSync6(DOMAIN_CACHE_FILE, "utf-8"));
|
|
1087
1202
|
for (const [k, v] of Object.entries(data)) {
|
|
1088
1203
|
const entry = v;
|
|
1089
1204
|
if (Date.now() - entry.ts < 7 * 24 * 60 * 60000) {
|
|
@@ -1098,17 +1213,17 @@ var init_orchestrator = __esm(async () => {
|
|
|
1098
1213
|
return;
|
|
1099
1214
|
_routeCacheDirty = false;
|
|
1100
1215
|
try {
|
|
1101
|
-
const dir =
|
|
1102
|
-
if (!
|
|
1103
|
-
|
|
1216
|
+
const dir = dirname3(ROUTE_CACHE_FILE);
|
|
1217
|
+
if (!existsSync10(dir))
|
|
1218
|
+
mkdirSync5(dir, { recursive: true });
|
|
1104
1219
|
const entries = Object.fromEntries(skillRouteCache);
|
|
1105
1220
|
writeFileSync3(ROUTE_CACHE_FILE, JSON.stringify(entries), "utf-8");
|
|
1106
1221
|
} catch {}
|
|
1107
1222
|
}, 5000);
|
|
1108
1223
|
routeCacheFlushTimer.unref?.();
|
|
1109
1224
|
try {
|
|
1110
|
-
if (
|
|
1111
|
-
const data = JSON.parse(
|
|
1225
|
+
if (existsSync10(ROUTE_CACHE_FILE)) {
|
|
1226
|
+
const data = JSON.parse(readFileSync6(ROUTE_CACHE_FILE, "utf-8"));
|
|
1112
1227
|
for (const [k, v] of Object.entries(data)) {
|
|
1113
1228
|
const entry = v;
|
|
1114
1229
|
if (Date.now() - entry.ts < 24 * 60 * 60000) {
|
|
@@ -1198,6 +1313,60 @@ var init_orchestrator = __esm(async () => {
|
|
|
1198
1313
|
]);
|
|
1199
1314
|
});
|
|
1200
1315
|
|
|
1316
|
+
// ../../src/payments/wallet.ts
|
|
1317
|
+
var exports_wallet = {};
|
|
1318
|
+
__export(exports_wallet, {
|
|
1319
|
+
getWalletContext: () => getWalletContext2,
|
|
1320
|
+
checkWalletConfigured: () => checkWalletConfigured2
|
|
1321
|
+
});
|
|
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";
|
|
1325
|
+
function asNonEmptyString2(value) {
|
|
1326
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
1327
|
+
}
|
|
1328
|
+
function getLobsterWalletFromLocalConfig2() {
|
|
1329
|
+
const agentsPath = join11(process.env.HOME || homedir6(), ".lobster", "agents.json");
|
|
1330
|
+
if (!existsSync14(agentsPath))
|
|
1331
|
+
return;
|
|
1332
|
+
try {
|
|
1333
|
+
const raw = JSON.parse(readFileSync9(agentsPath, "utf8"));
|
|
1334
|
+
const activeAgentId = asNonEmptyString2(raw.activeAgentId);
|
|
1335
|
+
const activeAgent = Array.isArray(raw.agents) ? raw.agents.find((agent) => asNonEmptyString2(agent.id) === activeAgentId) : activeAgentId ? raw.agents?.[activeAgentId] : undefined;
|
|
1336
|
+
return asNonEmptyString2(activeAgent?.authorizedWallets?.solana) ?? asNonEmptyString2(activeAgent?.walletAddress) ?? asNonEmptyString2(activeAgent?.wallet_address);
|
|
1337
|
+
} catch {
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function getWalletContext2() {
|
|
1342
|
+
const lobsterWallet = asNonEmptyString2(process.env.LOBSTER_WALLET_ADDRESS);
|
|
1343
|
+
if (lobsterWallet) {
|
|
1344
|
+
return { wallet_address: lobsterWallet, wallet_provider: "lobster.cash" };
|
|
1345
|
+
}
|
|
1346
|
+
const genericWallet = asNonEmptyString2(process.env.AGENT_WALLET_ADDRESS);
|
|
1347
|
+
if (genericWallet) {
|
|
1348
|
+
return {
|
|
1349
|
+
wallet_address: genericWallet,
|
|
1350
|
+
wallet_provider: asNonEmptyString2(process.env.AGENT_WALLET_PROVIDER)
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
const localLobsterWallet = getLobsterWalletFromLocalConfig2();
|
|
1354
|
+
if (localLobsterWallet) {
|
|
1355
|
+
return { wallet_address: localLobsterWallet, wallet_provider: "lobster.cash" };
|
|
1356
|
+
}
|
|
1357
|
+
return {};
|
|
1358
|
+
}
|
|
1359
|
+
function checkWalletConfigured2() {
|
|
1360
|
+
const wallet = getWalletContext2();
|
|
1361
|
+
if (!wallet.wallet_address)
|
|
1362
|
+
return { configured: false };
|
|
1363
|
+
return {
|
|
1364
|
+
configured: true,
|
|
1365
|
+
provider: wallet.wallet_provider ?? "unknown"
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
var init_wallet2 = () => {};
|
|
1369
|
+
|
|
1201
1370
|
// ../../src/cli.ts
|
|
1202
1371
|
import { config as loadEnv } from "dotenv";
|
|
1203
1372
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -1207,9 +1376,9 @@ init_version();
|
|
|
1207
1376
|
init_cascade();
|
|
1208
1377
|
init_wallet();
|
|
1209
1378
|
init_telemetry_attribution();
|
|
1210
|
-
import { readFileSync as readFileSync3, writeFileSync, existsSync as
|
|
1211
|
-
import { join as
|
|
1212
|
-
import { homedir as
|
|
1379
|
+
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2 } from "fs";
|
|
1380
|
+
import { join as join4 } from "path";
|
|
1381
|
+
import { homedir as homedir3, hostname, release as osRelease } from "os";
|
|
1213
1382
|
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
1214
1383
|
import { createInterface } from "readline";
|
|
1215
1384
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
@@ -1244,13 +1413,13 @@ function decodeBase64Json(value) {
|
|
|
1244
1413
|
function getConfigDir() {
|
|
1245
1414
|
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
1246
1415
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
1247
|
-
return PROFILE_NAME ?
|
|
1416
|
+
return PROFILE_NAME ? join4(homedir3(), ".unbrowse", "profiles", PROFILE_NAME) : join4(homedir3(), ".unbrowse");
|
|
1248
1417
|
}
|
|
1249
1418
|
function getConfigPath() {
|
|
1250
|
-
return
|
|
1419
|
+
return join4(getConfigDir(), "config.json");
|
|
1251
1420
|
}
|
|
1252
1421
|
function getInstallTelemetryPath() {
|
|
1253
|
-
return
|
|
1422
|
+
return join4(getConfigDir(), "install-state.json");
|
|
1254
1423
|
}
|
|
1255
1424
|
function getLandingToken() {
|
|
1256
1425
|
const token = process.env.UNBROWSE_LANDING_TOKEN?.trim();
|
|
@@ -1265,7 +1434,7 @@ function getActiveProfile() {
|
|
|
1265
1434
|
function loadConfig() {
|
|
1266
1435
|
try {
|
|
1267
1436
|
const configPath = getConfigPath();
|
|
1268
|
-
if (
|
|
1437
|
+
if (existsSync4(configPath)) {
|
|
1269
1438
|
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1270
1439
|
}
|
|
1271
1440
|
} catch {}
|
|
@@ -1274,14 +1443,14 @@ function loadConfig() {
|
|
|
1274
1443
|
function saveConfig(config) {
|
|
1275
1444
|
const configDir = getConfigDir();
|
|
1276
1445
|
const configPath = getConfigPath();
|
|
1277
|
-
if (!
|
|
1446
|
+
if (!existsSync4(configDir))
|
|
1278
1447
|
mkdirSync(configDir, { recursive: true });
|
|
1279
1448
|
writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
1280
1449
|
}
|
|
1281
1450
|
function loadInstallTelemetryState() {
|
|
1282
1451
|
try {
|
|
1283
1452
|
const statePath = getInstallTelemetryPath();
|
|
1284
|
-
if (
|
|
1453
|
+
if (existsSync4(statePath)) {
|
|
1285
1454
|
return JSON.parse(readFileSync3(statePath, "utf-8"));
|
|
1286
1455
|
}
|
|
1287
1456
|
} catch {}
|
|
@@ -1290,7 +1459,7 @@ function loadInstallTelemetryState() {
|
|
|
1290
1459
|
function saveInstallTelemetryState(state) {
|
|
1291
1460
|
const configDir = getConfigDir();
|
|
1292
1461
|
const statePath = getInstallTelemetryPath();
|
|
1293
|
-
if (!
|
|
1462
|
+
if (!existsSync4(configDir))
|
|
1294
1463
|
mkdirSync(configDir, { recursive: true });
|
|
1295
1464
|
writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
1296
1465
|
}
|
|
@@ -1402,9 +1571,19 @@ async function recordInstallTelemetryEvent(source, options) {
|
|
|
1402
1571
|
skill_version: options?.skillVersion,
|
|
1403
1572
|
status: options?.status ?? "installed",
|
|
1404
1573
|
created_at: createdAt,
|
|
1405
|
-
properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
|
|
1574
|
+
properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
|
|
1406
1575
|
});
|
|
1407
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
|
+
}
|
|
1408
1587
|
async function recordFunnelTelemetryEvent(name, options) {
|
|
1409
1588
|
const createdAt = options?.createdAt ?? new Date().toISOString();
|
|
1410
1589
|
const landingToken = getLandingToken();
|
|
@@ -1416,7 +1595,7 @@ async function recordFunnelTelemetryEvent(name, options) {
|
|
|
1416
1595
|
source: options?.source ?? "cli",
|
|
1417
1596
|
host_type: options?.hostType ?? detectTelemetryHostType(),
|
|
1418
1597
|
created_at: createdAt,
|
|
1419
|
-
properties: mergeTelemetryProperties(options?.properties, getTelemetryAttribution())
|
|
1598
|
+
properties: mergeTelemetryProperties({ ...getRuntimeContext(), ...options?.properties }, getTelemetryAttribution())
|
|
1420
1599
|
});
|
|
1421
1600
|
}
|
|
1422
1601
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i;
|
|
@@ -1448,6 +1627,10 @@ function getApiKey() {
|
|
|
1448
1627
|
}
|
|
1449
1628
|
return "";
|
|
1450
1629
|
}
|
|
1630
|
+
function getAgentId() {
|
|
1631
|
+
const config = loadConfig();
|
|
1632
|
+
return config?.agent_id ?? null;
|
|
1633
|
+
}
|
|
1451
1634
|
var API_TIMEOUT_MS = parseInt(process.env.UNBROWSE_API_TIMEOUT ?? "8000", 10);
|
|
1452
1635
|
var PUBLISH_TIMEOUT_MS = parseInt(process.env.UNBROWSE_PUBLISH_TIMEOUT ?? "30000", 10);
|
|
1453
1636
|
async function validateApiKey(key) {
|
|
@@ -1547,6 +1730,26 @@ async function apiRequest(method, path, body, opts) {
|
|
|
1547
1730
|
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
1548
1731
|
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
|
1549
1732
|
const terms = paymentRequired ? decodeBase64Json(paymentRequired) : legacyPaymentTerms ? JSON.parse(legacyPaymentTerms) : data.terms;
|
|
1733
|
+
try {
|
|
1734
|
+
const { isLobsterAvailable: isLobsterAvailable2, payAndRetry: payAndRetry2 } = await Promise.resolve().then(() => (init_lobster_pay(), exports_lobster_pay));
|
|
1735
|
+
if (isLobsterAvailable2()) {
|
|
1736
|
+
const fullUrl = `${API_URL}${path}`;
|
|
1737
|
+
const paidResult = await payAndRetry2(fullUrl, {
|
|
1738
|
+
body,
|
|
1739
|
+
headers: {
|
|
1740
|
+
"Content-Type": "application/json",
|
|
1741
|
+
"Accept-Encoding": "gzip, deflate",
|
|
1742
|
+
...releaseAttestationHeaders,
|
|
1743
|
+
...key ? { Authorization: `Bearer ${key}` } : {}
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
if (paidResult) {
|
|
1747
|
+
return { data: paidResult.data, headers: new Headers };
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
} catch (payErr) {
|
|
1751
|
+
console.warn(`[x402] lobster pay-and-retry failed: ${payErr.message}`);
|
|
1752
|
+
}
|
|
1550
1753
|
const err = new Error(`Payment required: ${data.error ?? "This skill requires payment"}`);
|
|
1551
1754
|
err.x402 = true;
|
|
1552
1755
|
err.terms = terms;
|
|
@@ -1565,19 +1768,10 @@ async function api(method, path, body, opts) {
|
|
|
1565
1768
|
return data;
|
|
1566
1769
|
}
|
|
1567
1770
|
function parseInstallAttribution() {
|
|
1568
|
-
const result = {};
|
|
1569
|
-
const b64 = process.env.UNBROWSE_ATTRIBUTION_B64;
|
|
1570
|
-
if (b64) {
|
|
1571
|
-
try {
|
|
1572
|
-
const decoded = JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
|
|
1573
|
-
if (decoded && typeof decoded === "object")
|
|
1574
|
-
result.install_attribution = decoded;
|
|
1575
|
-
} catch {}
|
|
1576
|
-
}
|
|
1577
1771
|
const token = process.env.UNBROWSE_LANDING_TOKEN;
|
|
1578
1772
|
if (token && token.length < 2048)
|
|
1579
|
-
|
|
1580
|
-
return
|
|
1773
|
+
return { landing_token: token };
|
|
1774
|
+
return {};
|
|
1581
1775
|
}
|
|
1582
1776
|
async function promptTosAcceptance(summary, tosUrl) {
|
|
1583
1777
|
if (process.env.UNBROWSE_NON_INTERACTIVE === "1") {
|
|
@@ -1748,6 +1942,170 @@ async function syncAgentWallet(wallet = getLocalWalletContext()) {
|
|
|
1748
1942
|
return;
|
|
1749
1943
|
saveConfig({ ...config, ...wallet });
|
|
1750
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
|
+
}
|
|
1751
2109
|
|
|
1752
2110
|
// ../../src/cli/shortcuts.ts
|
|
1753
2111
|
var linkedin = {
|
|
@@ -1948,7 +2306,7 @@ function buildDepsMetadata(pack, taskName) {
|
|
|
1948
2306
|
init_paths();
|
|
1949
2307
|
init_supervisor();
|
|
1950
2308
|
init_version();
|
|
1951
|
-
import { openSync, readFileSync as
|
|
2309
|
+
import { existsSync as existsSync7, openSync, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1952
2310
|
import path2 from "node:path";
|
|
1953
2311
|
import { spawn } from "node:child_process";
|
|
1954
2312
|
function isServerVersionMismatch(runningVersion, installedVersion, runningCodeHash, installedCodeHash) {
|
|
@@ -1986,14 +2344,14 @@ function isPidAlive(pid) {
|
|
|
1986
2344
|
}
|
|
1987
2345
|
function readPidState(pidFile) {
|
|
1988
2346
|
try {
|
|
1989
|
-
return JSON.parse(
|
|
2347
|
+
return JSON.parse(readFileSync5(pidFile, "utf-8"));
|
|
1990
2348
|
} catch {
|
|
1991
2349
|
return null;
|
|
1992
2350
|
}
|
|
1993
2351
|
}
|
|
1994
2352
|
function clearStalePidFile(pidFile) {
|
|
1995
2353
|
try {
|
|
1996
|
-
|
|
2354
|
+
unlinkSync2(pidFile);
|
|
1997
2355
|
} catch {}
|
|
1998
2356
|
}
|
|
1999
2357
|
function deriveListenEnv(baseUrl) {
|
|
@@ -2014,6 +2372,10 @@ function getServerSpawnSpec(metaUrl, entrypoint = resolveSiblingEntrypoint(metaU
|
|
|
2014
2372
|
recordedEntrypoint: `${process.execPath} serve`
|
|
2015
2373
|
};
|
|
2016
2374
|
}
|
|
2375
|
+
const serverJs = path2.join(path2.dirname(entrypoint), "server.js");
|
|
2376
|
+
if (existsSync7(serverJs) && path2.basename(entrypoint) !== "server.js") {
|
|
2377
|
+
entrypoint = serverJs;
|
|
2378
|
+
}
|
|
2017
2379
|
return {
|
|
2018
2380
|
command: process.execPath,
|
|
2019
2381
|
args: runtimeArgsForEntrypoint(metaUrl, entrypoint),
|
|
@@ -2027,7 +2389,7 @@ function spawnServer(baseUrl, metaUrl, pidFile, restartCount = 0) {
|
|
|
2027
2389
|
const entrypoint = resolveSiblingEntrypoint(metaUrl, "index");
|
|
2028
2390
|
const spawnSpec = getServerSpawnSpec(metaUrl, entrypoint);
|
|
2029
2391
|
const logFile = getServerAutostartLogFile();
|
|
2030
|
-
|
|
2392
|
+
ensureDir2(path2.dirname(logFile));
|
|
2031
2393
|
const logFd = openSync(logFile, "a");
|
|
2032
2394
|
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
2033
2395
|
cwd: spawnSpec.cwd,
|
|
@@ -2159,7 +2521,7 @@ async function restartServer(baseUrl, metaUrl) {
|
|
|
2159
2521
|
}
|
|
2160
2522
|
|
|
2161
2523
|
// ../../src/runtime/paths.ts
|
|
2162
|
-
import { existsSync as
|
|
2524
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, realpathSync as realpathSync2 } from "node:fs";
|
|
2163
2525
|
import path3 from "node:path";
|
|
2164
2526
|
import { createRequire as createRequire3 } from "node:module";
|
|
2165
2527
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -2179,7 +2541,7 @@ function runtimeArgsForEntrypoint2(metaUrl, entrypoint) {
|
|
|
2179
2541
|
const req = createRequire3(metaUrl);
|
|
2180
2542
|
const tsxPkg = req.resolve("tsx/package.json");
|
|
2181
2543
|
const tsxLoader = path3.join(path3.dirname(tsxPkg), "dist", "loader.mjs");
|
|
2182
|
-
if (
|
|
2544
|
+
if (existsSync8(tsxLoader))
|
|
2183
2545
|
return ["--import", pathToFileURL2(tsxLoader).href, entrypoint];
|
|
2184
2546
|
} catch {}
|
|
2185
2547
|
return ["--import", "tsx", entrypoint];
|
|
@@ -2209,8 +2571,8 @@ init_publish();
|
|
|
2209
2571
|
init_settings();
|
|
2210
2572
|
init_graph();
|
|
2211
2573
|
init_schema_review();
|
|
2212
|
-
import { join as
|
|
2213
|
-
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");
|
|
2214
2576
|
var indexInFlight2 = new Map;
|
|
2215
2577
|
var pendingIndexJobs2 = new Map;
|
|
2216
2578
|
async function drainPendingIndexJobs() {
|
|
@@ -2247,14 +2609,14 @@ init_paths();
|
|
|
2247
2609
|
init_client2();
|
|
2248
2610
|
init_logger();
|
|
2249
2611
|
init_wallet();
|
|
2250
|
-
import { execFileSync as
|
|
2251
|
-
import { existsSync as
|
|
2612
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
2613
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2252
2614
|
import os4 from "node:os";
|
|
2253
2615
|
import path7 from "node:path";
|
|
2254
2616
|
|
|
2255
2617
|
// ../../src/runtime/update-hints.ts
|
|
2256
2618
|
init_paths();
|
|
2257
|
-
import { existsSync as
|
|
2619
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2258
2620
|
import os3 from "node:os";
|
|
2259
2621
|
import path6 from "node:path";
|
|
2260
2622
|
var DEFAULT_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
@@ -2267,20 +2629,20 @@ function getConfigDir2() {
|
|
|
2267
2629
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
2268
2630
|
return path6.join(getHomeDir(), ".unbrowse");
|
|
2269
2631
|
}
|
|
2270
|
-
function
|
|
2271
|
-
if (!
|
|
2272
|
-
|
|
2632
|
+
function ensureDir3(dir) {
|
|
2633
|
+
if (!existsSync11(dir))
|
|
2634
|
+
mkdirSync6(dir, { recursive: true });
|
|
2273
2635
|
return dir;
|
|
2274
2636
|
}
|
|
2275
2637
|
function readJsonFile(file) {
|
|
2276
2638
|
try {
|
|
2277
|
-
return JSON.parse(
|
|
2639
|
+
return JSON.parse(readFileSync7(file, "utf8"));
|
|
2278
2640
|
} catch {
|
|
2279
2641
|
return null;
|
|
2280
2642
|
}
|
|
2281
2643
|
}
|
|
2282
2644
|
function writeJsonFile(file, value) {
|
|
2283
|
-
|
|
2645
|
+
ensureDir3(path6.dirname(file));
|
|
2284
2646
|
writeFileSync4(file, `${JSON.stringify(value, null, 2)}
|
|
2285
2647
|
`);
|
|
2286
2648
|
}
|
|
@@ -2291,7 +2653,7 @@ function detectRepoRoot(start2) {
|
|
|
2291
2653
|
let dir = path6.resolve(start2);
|
|
2292
2654
|
const root = path6.parse(dir).root;
|
|
2293
2655
|
while (dir !== root) {
|
|
2294
|
-
if (
|
|
2656
|
+
if (existsSync11(path6.join(dir, ".git")))
|
|
2295
2657
|
return dir;
|
|
2296
2658
|
dir = path6.dirname(dir);
|
|
2297
2659
|
}
|
|
@@ -2370,13 +2732,13 @@ codex_hooks = true
|
|
|
2370
2732
|
}
|
|
2371
2733
|
function writeCodexHook(metaUrl) {
|
|
2372
2734
|
const configPath = getCodexConfigPath();
|
|
2373
|
-
if (!
|
|
2735
|
+
if (!existsSync11(path6.dirname(configPath))) {
|
|
2374
2736
|
return { host: "codex", action: "not-detected", config_file: configPath };
|
|
2375
2737
|
}
|
|
2376
2738
|
try {
|
|
2377
2739
|
const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
|
|
2378
|
-
const fileExistsBefore =
|
|
2379
|
-
let content = fileExistsBefore ?
|
|
2740
|
+
const fileExistsBefore = existsSync11(configPath);
|
|
2741
|
+
let content = fileExistsBefore ? readFileSync7(configPath, "utf8") : "";
|
|
2380
2742
|
const previous = content;
|
|
2381
2743
|
content = ensureCodexHooksFeature(content);
|
|
2382
2744
|
if (!content.includes("unbrowse-update-hint.mjs")) {
|
|
@@ -2410,13 +2772,13 @@ command = ${JSON.stringify(command)}
|
|
|
2410
2772
|
}
|
|
2411
2773
|
function writeClaudeHook(metaUrl) {
|
|
2412
2774
|
const settingsPath = getClaudeSettingsPath();
|
|
2413
|
-
if (!
|
|
2775
|
+
if (!existsSync11(path6.dirname(settingsPath))) {
|
|
2414
2776
|
return { host: "claude", action: "not-detected", config_file: settingsPath };
|
|
2415
2777
|
}
|
|
2416
2778
|
try {
|
|
2417
2779
|
const hookScript = getHookScriptPath(metaUrl).replace(/\\/g, "/");
|
|
2418
2780
|
const command = `node "${hookScript}"`;
|
|
2419
|
-
const fileExistsBefore =
|
|
2781
|
+
const fileExistsBefore = existsSync11(settingsPath);
|
|
2420
2782
|
const settings = readJsonFile(settingsPath) ?? {};
|
|
2421
2783
|
settings.hooks ??= {};
|
|
2422
2784
|
settings.hooks.SessionStart ??= [];
|
|
@@ -2454,7 +2816,7 @@ function configureUpdateHintHooks(metaUrl, install) {
|
|
|
2454
2816
|
function hasBinary(name) {
|
|
2455
2817
|
const checker = process.platform === "win32" ? "where" : "which";
|
|
2456
2818
|
try {
|
|
2457
|
-
|
|
2819
|
+
execFileSync3(checker, [name], { stdio: "ignore" });
|
|
2458
2820
|
return true;
|
|
2459
2821
|
} catch {
|
|
2460
2822
|
return false;
|
|
@@ -2481,7 +2843,7 @@ function getOpenCodeProjectCommandsDir(cwd) {
|
|
|
2481
2843
|
return path7.join(cwd, ".opencode", "commands");
|
|
2482
2844
|
}
|
|
2483
2845
|
function detectOpenCode(cwd) {
|
|
2484
|
-
return hasBinary("opencode") ||
|
|
2846
|
+
return hasBinary("opencode") || existsSync12(path7.join(resolveConfigHome(), "opencode")) || existsSync12(path7.join(cwd, ".opencode"));
|
|
2485
2847
|
}
|
|
2486
2848
|
function renderOpenCodeCommand() {
|
|
2487
2849
|
return `---
|
|
@@ -2509,12 +2871,12 @@ function writeOpenCodeCommand(scope, cwd) {
|
|
|
2509
2871
|
if (scope === "auto" && !detected) {
|
|
2510
2872
|
return { detected: false, action: "not-detected", scope: "off" };
|
|
2511
2873
|
}
|
|
2512
|
-
const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" :
|
|
2874
|
+
const resolvedScope = scope === "project" ? "project" : scope === "global" ? "global" : existsSync12(path7.join(cwd, ".opencode")) ? "project" : "global";
|
|
2513
2875
|
const commandsDir = resolvedScope === "project" ? getOpenCodeProjectCommandsDir(cwd) : getOpenCodeGlobalCommandsDir();
|
|
2514
|
-
const commandFile = path7.join(
|
|
2876
|
+
const commandFile = path7.join(ensureDir2(commandsDir), "unbrowse.md");
|
|
2515
2877
|
const content = renderOpenCodeCommand();
|
|
2516
|
-
const action2 =
|
|
2517
|
-
|
|
2878
|
+
const action2 = existsSync12(commandFile) ? "updated" : "installed";
|
|
2879
|
+
mkdirSync7(path7.dirname(commandFile), { recursive: true });
|
|
2518
2880
|
writeFileSync5(commandFile, content);
|
|
2519
2881
|
return {
|
|
2520
2882
|
detected: detected || scope !== "auto",
|
|
@@ -2525,10 +2887,10 @@ function writeOpenCodeCommand(scope, cwd) {
|
|
|
2525
2887
|
}
|
|
2526
2888
|
async function ensureBrowserEngineInstalled() {
|
|
2527
2889
|
const binary = findKuriBinary();
|
|
2528
|
-
if (
|
|
2890
|
+
if (existsSync12(binary)) {
|
|
2529
2891
|
return { installed: true, action: "already-installed" };
|
|
2530
2892
|
}
|
|
2531
|
-
const sourceDir = getKuriSourceCandidates().find((candidate) =>
|
|
2893
|
+
const sourceDir = getKuriSourceCandidates().find((candidate) => existsSync12(path7.join(candidate, "build.zig")));
|
|
2532
2894
|
if (!sourceDir) {
|
|
2533
2895
|
return {
|
|
2534
2896
|
installed: false,
|
|
@@ -2544,13 +2906,13 @@ async function ensureBrowserEngineInstalled() {
|
|
|
2544
2906
|
};
|
|
2545
2907
|
}
|
|
2546
2908
|
try {
|
|
2547
|
-
|
|
2909
|
+
execFileSync3("zig", ["build", "-Doptimize=ReleaseFast"], {
|
|
2548
2910
|
cwd: sourceDir,
|
|
2549
2911
|
stdio: "inherit",
|
|
2550
2912
|
timeout: 300000
|
|
2551
2913
|
});
|
|
2552
2914
|
const builtBinary = findKuriBinary();
|
|
2553
|
-
if (
|
|
2915
|
+
if (existsSync12(builtBinary)) {
|
|
2554
2916
|
return {
|
|
2555
2917
|
installed: true,
|
|
2556
2918
|
action: "installed",
|
|
@@ -2575,11 +2937,11 @@ async function runSetup(options) {
|
|
|
2575
2937
|
const browser = options?.installBrowser === false ? { installed: false, action: "skipped" } : await ensureBrowserEngineInstalled();
|
|
2576
2938
|
const walletCheck = checkWalletConfigured();
|
|
2577
2939
|
const skipWalletSetup = process.env.UNBROWSE_SKIP_WALLET_SETUP === "1";
|
|
2578
|
-
const lobsterInstalled = hasBinary("lobstercash") ||
|
|
2940
|
+
const lobsterInstalled = hasBinary("lobstercash") || existsSync12(path7.join(os4.homedir(), ".agents", "skills", "lobstercash", "SKILL.md"));
|
|
2579
2941
|
if (!skipWalletSetup && !walletCheck.configured && lobsterInstalled) {
|
|
2580
2942
|
console.log("[unbrowse] Crossmint lobster.cash detected but wallet not configured — running wallet setup...");
|
|
2581
2943
|
try {
|
|
2582
|
-
|
|
2944
|
+
execFileSync3("npx", ["@crossmint/lobster-cli", "setup"], {
|
|
2583
2945
|
stdio: "inherit",
|
|
2584
2946
|
timeout: 60000
|
|
2585
2947
|
});
|
|
@@ -2615,7 +2977,7 @@ async function runSetup(options) {
|
|
|
2615
2977
|
|
|
2616
2978
|
// ../../src/runtime/update-hints.ts
|
|
2617
2979
|
init_paths();
|
|
2618
|
-
import { existsSync as
|
|
2980
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2619
2981
|
import os5 from "node:os";
|
|
2620
2982
|
import path8 from "node:path";
|
|
2621
2983
|
var INSTALL_SCRIPT_URL = "https://unbrowse.ai/install.sh";
|
|
@@ -2628,20 +2990,20 @@ function getConfigDir3() {
|
|
|
2628
2990
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
2629
2991
|
return path8.join(getHomeDir2(), ".unbrowse");
|
|
2630
2992
|
}
|
|
2631
|
-
function
|
|
2632
|
-
if (!
|
|
2633
|
-
|
|
2993
|
+
function ensureDir4(dir) {
|
|
2994
|
+
if (!existsSync13(dir))
|
|
2995
|
+
mkdirSync8(dir, { recursive: true });
|
|
2634
2996
|
return dir;
|
|
2635
2997
|
}
|
|
2636
2998
|
function readJsonFile2(file) {
|
|
2637
2999
|
try {
|
|
2638
|
-
return JSON.parse(
|
|
3000
|
+
return JSON.parse(readFileSync8(file, "utf8"));
|
|
2639
3001
|
} catch {
|
|
2640
3002
|
return null;
|
|
2641
3003
|
}
|
|
2642
3004
|
}
|
|
2643
3005
|
function writeJsonFile2(file, value) {
|
|
2644
|
-
|
|
3006
|
+
ensureDir4(path8.dirname(file));
|
|
2645
3007
|
writeFileSync6(file, `${JSON.stringify(value, null, 2)}
|
|
2646
3008
|
`);
|
|
2647
3009
|
}
|
|
@@ -2655,7 +3017,7 @@ function detectRepoRoot2(start2) {
|
|
|
2655
3017
|
let dir = path8.resolve(start2);
|
|
2656
3018
|
const root = path8.parse(dir).root;
|
|
2657
3019
|
while (dir !== root) {
|
|
2658
|
-
if (
|
|
3020
|
+
if (existsSync13(path8.join(dir, ".git")))
|
|
2659
3021
|
return dir;
|
|
2660
3022
|
dir = path8.dirname(dir);
|
|
2661
3023
|
}
|
|
@@ -2688,7 +3050,7 @@ function detectInstallHost2(repoRoot) {
|
|
|
2688
3050
|
function getInstalledVersion(metaUrl) {
|
|
2689
3051
|
const packageRoot = getPackageRoot(metaUrl);
|
|
2690
3052
|
try {
|
|
2691
|
-
const pkg = JSON.parse(
|
|
3053
|
+
const pkg = JSON.parse(readFileSync8(path8.join(packageRoot, "package.json"), "utf8"));
|
|
2692
3054
|
return pkg.version ?? "unknown";
|
|
2693
3055
|
} catch {
|
|
2694
3056
|
return "unknown";
|
|
@@ -2789,6 +3151,7 @@ loadEnv({ quiet: true });
|
|
|
2789
3151
|
loadEnv({ path: ".env.runtime", quiet: true });
|
|
2790
3152
|
var BASE_URL = process.env.UNBROWSE_URL || "http://localhost:6969";
|
|
2791
3153
|
var CLI_CLIENT_ID = process.env.UNBROWSE_CLIENT_ID || `cli-${process.ppid || process.pid}`;
|
|
3154
|
+
var walletNudgeShown = false;
|
|
2792
3155
|
function parseArgs(argv) {
|
|
2793
3156
|
const raw = argv.slice(2);
|
|
2794
3157
|
const command = raw[0] && !raw[0].startsWith("--") ? raw[0] : "help";
|
|
@@ -2935,6 +3298,14 @@ function formatSavedDuration(ms) {
|
|
|
2935
3298
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
2936
3299
|
return `${ms}ms`;
|
|
2937
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
|
+
}
|
|
2938
3309
|
function emitImpactSummary(result) {
|
|
2939
3310
|
const impact = result.impact;
|
|
2940
3311
|
if (!impact)
|
|
@@ -2943,14 +3314,17 @@ function emitImpactSummary(result) {
|
|
|
2943
3314
|
const tokensSaved = typeof impact.tokens_saved === "number" ? impact.tokens_saved : 0;
|
|
2944
3315
|
const timeSavedPct = typeof impact.time_saved_pct === "number" ? impact.time_saved_pct : 0;
|
|
2945
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;
|
|
2946
3318
|
const browserAvoided = impact.browser_avoided === true;
|
|
2947
|
-
if (timeSavedMs <= 0 && tokensSaved <= 0 && !browserAvoided)
|
|
3319
|
+
if (timeSavedMs <= 0 && tokensSaved <= 0 && costSavedUc <= 0 && !browserAvoided)
|
|
2948
3320
|
return;
|
|
2949
3321
|
const parts = [];
|
|
2950
3322
|
if (timeSavedMs > 0)
|
|
2951
3323
|
parts.push(`${formatSavedDuration(timeSavedMs)} saved (${timeSavedPct}% faster)`);
|
|
2952
3324
|
if (tokensSaved > 0)
|
|
2953
3325
|
parts.push(`${tokensSaved.toLocaleString("en-US")} tokens saved (${tokensSavedPct}% less context)`);
|
|
3326
|
+
if (costSavedUc > 0)
|
|
3327
|
+
parts.push(`${formatCostUsd(costSavedUc)} saved`);
|
|
2954
3328
|
if (browserAvoided)
|
|
2955
3329
|
parts.push("browser avoided");
|
|
2956
3330
|
info(parts.join(" \u2022 "));
|
|
@@ -3095,9 +3469,24 @@ async function cmdResolve(flags) {
|
|
|
3095
3469
|
}
|
|
3096
3470
|
result = slimTrace(result);
|
|
3097
3471
|
emitImpactSummary(result);
|
|
3472
|
+
{
|
|
3473
|
+
const entry = impactFromResult("resolve", result, { intent, domain });
|
|
3474
|
+
if (entry)
|
|
3475
|
+
appendImpact(entry);
|
|
3476
|
+
}
|
|
3098
3477
|
emitNextActionSummary(result);
|
|
3099
3478
|
const skill = result.skill;
|
|
3100
3479
|
const trace = result.trace;
|
|
3480
|
+
if (trace?.success && !walletNudgeShown) {
|
|
3481
|
+
try {
|
|
3482
|
+
const { checkWalletConfigured: checkWalletConfigured3 } = await Promise.resolve().then(() => (init_wallet2(), exports_wallet));
|
|
3483
|
+
const wallet = checkWalletConfigured3();
|
|
3484
|
+
if (!wallet.configured) {
|
|
3485
|
+
info("You're indexing routes but have no payout wallet. Run: npx @crossmint/lobster-cli setup");
|
|
3486
|
+
walletNudgeShown = true;
|
|
3487
|
+
}
|
|
3488
|
+
} catch (_e) {}
|
|
3489
|
+
}
|
|
3101
3490
|
if (skill?.skill_id && trace) {
|
|
3102
3491
|
result._feedback = `unbrowse feedback --skill ${skill.skill_id} --endpoint ${trace.endpoint_id || "?"} --rating <1-5>`;
|
|
3103
3492
|
}
|
|
@@ -3255,6 +3644,14 @@ async function cmdExecute(flags) {
|
|
|
3255
3644
|
}
|
|
3256
3645
|
result = slimTrace(result);
|
|
3257
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
|
+
}
|
|
3258
3655
|
emitNextActionSummary(result);
|
|
3259
3656
|
const pathFlag = flags.path;
|
|
3260
3657
|
const extractFlag = flags.extract;
|
|
@@ -3343,6 +3740,25 @@ async function cmdFeedback(flags) {
|
|
|
3343
3740
|
body.diagnostics = JSON.parse(flags.diagnostics);
|
|
3344
3741
|
output(await api2("POST", "/v1/feedback", body), !!flags.pretty);
|
|
3345
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
|
+
}
|
|
3346
3762
|
async function cmdReview(flags) {
|
|
3347
3763
|
const skillId = flags.skill;
|
|
3348
3764
|
if (!skillId)
|
|
@@ -3481,6 +3897,16 @@ async function cmdSetup(flags) {
|
|
|
3481
3897
|
info(`${hook.host} update hint hook ${hook.action} at ${hook.config_file}`);
|
|
3482
3898
|
}
|
|
3483
3899
|
}
|
|
3900
|
+
if (report.wallet.configured) {
|
|
3901
|
+
info(`Wallet configured (${report.wallet.provider}): ${report.wallet.wallet_address ?? "linked"}`);
|
|
3902
|
+
} else if (report.wallet.lobster_installed) {
|
|
3903
|
+
info("Wallet not paired \u2014 your indexed routes won't earn payouts.");
|
|
3904
|
+
info("Run: npx @crossmint/lobster-cli setup");
|
|
3905
|
+
} else {
|
|
3906
|
+
info("No wallet configured \u2014 you're indexing routes for free.");
|
|
3907
|
+
info("Set up a wallet so you earn when agents use your routes:");
|
|
3908
|
+
info(" npx @crossmint/lobster-cli setup");
|
|
3909
|
+
}
|
|
3484
3910
|
await recordInstallTelemetryEvent("setup", {
|
|
3485
3911
|
hostType,
|
|
3486
3912
|
status: report.browser_engine.action === "failed" ? "failed" : "installed",
|
|
@@ -3547,6 +3973,7 @@ var CLI_REFERENCE = {
|
|
|
3547
3973
|
{ name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Search cached indexed/published routes and optionally execute the top trusted endpoint" },
|
|
3548
3974
|
{ name: "execute", usage: "--skill ID --endpoint ID [opts]", desc: "Execute a specific endpoint" },
|
|
3549
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" },
|
|
3550
3977
|
{ name: "review", usage: "--skill ID --endpoints '[...]'", desc: "Push reviewed descriptions/schema metadata back to a captured skill before publish" },
|
|
3551
3978
|
{ name: "index", usage: "--skill ID", desc: "Recompute local graph/contracts/export from cached skill state only" },
|
|
3552
3979
|
{ name: "publish", usage: "--skill ID [--confirm-publish] [--endpoints '[...]']", desc: "Re-index locally, inspect publish-review metadata, then publish/share from cached skill state" },
|
|
@@ -3574,7 +4001,8 @@ var CLI_REFERENCE = {
|
|
|
3574
4001
|
{ name: "back", usage: "[--session id]", desc: "Navigate back" },
|
|
3575
4002
|
{ name: "forward", usage: "[--session id]", desc: "Navigate forward" },
|
|
3576
4003
|
{ name: "sync", usage: "[--session id]", desc: "Checkpoint current capture, keep tab open, queue background index + publish, then inspect via skill/publish review" },
|
|
3577
|
-
{ 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" }
|
|
3578
4006
|
],
|
|
3579
4007
|
globalFlags: [
|
|
3580
4008
|
{ flag: "--pretty", desc: "Indented JSON output" },
|
|
@@ -3615,6 +4043,131 @@ var CLI_REFERENCE = {
|
|
|
3615
4043
|
`unbrowse publish --skill abc --endpoints '[{"endpoint_id":"def","description":"Search court judgments by keywords","action_kind":"search","resource_kind":"judgment"}]'`
|
|
3616
4044
|
]
|
|
3617
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
|
+
}
|
|
3618
4171
|
function printHelp() {
|
|
3619
4172
|
const r = CLI_REFERENCE;
|
|
3620
4173
|
const lines = ["unbrowse \u2014 shell-safe CLI for the local API", ""];
|
|
@@ -4036,6 +4589,8 @@ async function main() {
|
|
|
4036
4589
|
return cmdUpgrade(flags);
|
|
4037
4590
|
if (command === "connect-chrome")
|
|
4038
4591
|
return cmdConnectChrome();
|
|
4592
|
+
if (command === "stats")
|
|
4593
|
+
return cmdStats(flags);
|
|
4039
4594
|
const KNOWN_COMMANDS = new Set([
|
|
4040
4595
|
"health",
|
|
4041
4596
|
"mcp",
|
|
@@ -4045,6 +4600,7 @@ async function main() {
|
|
|
4045
4600
|
"exec",
|
|
4046
4601
|
"feedback",
|
|
4047
4602
|
"fb",
|
|
4603
|
+
"annotate",
|
|
4048
4604
|
"review",
|
|
4049
4605
|
"index",
|
|
4050
4606
|
"publish",
|
|
@@ -4079,7 +4635,8 @@ async function main() {
|
|
|
4079
4635
|
"forward",
|
|
4080
4636
|
"sync",
|
|
4081
4637
|
"close",
|
|
4082
|
-
"connect-chrome"
|
|
4638
|
+
"connect-chrome",
|
|
4639
|
+
"stats"
|
|
4083
4640
|
]);
|
|
4084
4641
|
if (!KNOWN_COMMANDS.has(command)) {
|
|
4085
4642
|
const pack = findSitePack(command);
|
|
@@ -4115,6 +4672,8 @@ async function main() {
|
|
|
4115
4672
|
case "feedback":
|
|
4116
4673
|
case "fb":
|
|
4117
4674
|
return cmdFeedback(flags);
|
|
4675
|
+
case "annotate":
|
|
4676
|
+
return cmdAnnotate(flags);
|
|
4118
4677
|
case "review":
|
|
4119
4678
|
return cmdReview(flags);
|
|
4120
4679
|
case "index":
|
|
@@ -4175,6 +4734,8 @@ async function main() {
|
|
|
4175
4734
|
return cmdClose(flags);
|
|
4176
4735
|
case "connect-chrome":
|
|
4177
4736
|
return cmdConnectChrome();
|
|
4737
|
+
case "stats":
|
|
4738
|
+
return cmdStats(flags);
|
|
4178
4739
|
default:
|
|
4179
4740
|
info(`Unknown command: ${command}`);
|
|
4180
4741
|
printHelp();
|