unbrowse 3.0.4 → 3.1.0-experiments.5e7a7bb
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 +627 -90
- package/dist/mcp.js +641 -43
- package/package.json +1 -1
- package/runtime-src/api/browse-index.ts +26 -4
- package/runtime-src/api/routes.ts +18 -7
- package/runtime-src/browser/index.ts +2 -1
- package/runtime-src/build-info.generated.ts +5 -5
- package/runtime-src/capture/index.ts +113 -0
- package/runtime-src/cli.ts +190 -2
- package/runtime-src/client/index.ts +25 -1
- package/runtime-src/execution/index.ts +43 -21
- package/runtime-src/execution/token-resolver.ts +122 -0
- package/runtime-src/graph/index.ts +14 -6
- package/runtime-src/impact-log.ts +227 -0
- package/runtime-src/kuri/client.ts +5 -1
- package/runtime-src/marketplace/index.ts +9 -1
- package/runtime-src/mcp.ts +166 -2
- package/runtime-src/orchestrator/browser-agent.ts +2 -1
- package/runtime-src/orchestrator/index.ts +7 -3
- package/runtime-src/payments/lobster-pay.ts +182 -0
- package/runtime-src/reverse-engineer/token-sources.ts +357 -0
- package/runtime-src/types/skill.ts +19 -0
- 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 +10 -6
- package/vendor/kuri/win-x64/kuri.exe +0 -0
package/dist/mcp.js
CHANGED
|
@@ -1,10 +1,127 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
set: (newValue) => all[name] = () => newValue
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
|
+
|
|
15
|
+
// ../../src/payments/lobster-pay.ts
|
|
16
|
+
var exports_lobster_pay = {};
|
|
17
|
+
__export(exports_lobster_pay, {
|
|
18
|
+
payAndRetry: () => payAndRetry,
|
|
19
|
+
lobsterX402Fetch: () => lobsterX402Fetch,
|
|
20
|
+
isLobsterAvailable: () => isLobsterAvailable
|
|
21
|
+
});
|
|
22
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
23
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
24
|
+
import { homedir as homedir3 } from "node:os";
|
|
25
|
+
import { join as join4 } from "node:path";
|
|
26
|
+
function getLobsterCommand() {
|
|
27
|
+
try {
|
|
28
|
+
execFileSync("lobstercash", ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
29
|
+
return { cmd: "lobstercash", prefix: [] };
|
|
30
|
+
} catch (_e) {}
|
|
31
|
+
try {
|
|
32
|
+
const npmPrefix = execFileSync("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
|
|
33
|
+
const lobsterPath = join4(npmPrefix, "bin", "lobstercash");
|
|
34
|
+
if (existsSync5(lobsterPath)) {
|
|
35
|
+
execFileSync(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
36
|
+
return { cmd: lobsterPath, prefix: [] };
|
|
37
|
+
}
|
|
38
|
+
} catch (_e) {}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function lobsterCmd() {
|
|
42
|
+
if (cachedCommand === undefined)
|
|
43
|
+
cachedCommand = getLobsterCommand();
|
|
44
|
+
return cachedCommand;
|
|
45
|
+
}
|
|
46
|
+
function isLobsterAvailable() {
|
|
47
|
+
const agentsPath = join4(process.env.HOME || homedir3(), ".lobster", "agents.json");
|
|
48
|
+
return existsSync5(agentsPath);
|
|
49
|
+
}
|
|
50
|
+
function lobsterX402Fetch(url, options) {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
const resolved = lobsterCmd();
|
|
53
|
+
if (!resolved) {
|
|
54
|
+
resolve({ success: false, body: "", error: "lobstercash CLI not in PATH" });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const { cmd, prefix } = resolved;
|
|
58
|
+
const args = [...prefix, "x402", "fetch", url, "--debug"];
|
|
59
|
+
if (options?.jsonBody) {
|
|
60
|
+
args.push("--json", options.jsonBody);
|
|
61
|
+
}
|
|
62
|
+
if (options?.headers) {
|
|
63
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
64
|
+
args.push("--header", `${key}:${value}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const timeout = options?.timeoutMs ?? LOBSTER_PAY_TIMEOUT_MS;
|
|
68
|
+
args.push("--timeout", String(timeout));
|
|
69
|
+
execFile(cmd, args, { timeout: timeout + 5000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
70
|
+
if (err) {
|
|
71
|
+
const msg = stderr?.trim() || err.message;
|
|
72
|
+
console.warn(`[lobster-pay] x402 fetch failed: ${msg}`);
|
|
73
|
+
resolve({ success: false, body: "", error: msg });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (stderr) {
|
|
77
|
+
for (const line of stderr.split(`
|
|
78
|
+
`).filter(Boolean)) {
|
|
79
|
+
console.log(`[lobster-pay] ${line}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const statusMatch = stdout.match(/^Status:\s*(\d+)/m);
|
|
83
|
+
const statusCode = statusMatch ? parseInt(statusMatch[1], 10) : undefined;
|
|
84
|
+
if (statusCode && statusCode >= 400) {
|
|
85
|
+
resolve({ success: false, body: stdout, statusCode, error: `HTTP ${statusCode}` });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
resolve({ success: true, body: stdout, statusCode });
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function payAndRetry(fullUrl, options) {
|
|
93
|
+
if (!isLobsterAvailable()) {
|
|
94
|
+
console.log("[lobster-pay] lobster.cash not configured — skipping payment");
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
console.log(`[lobster-pay] attempting x402 payment for ${fullUrl}`);
|
|
98
|
+
const result = await lobsterX402Fetch(fullUrl, {
|
|
99
|
+
jsonBody: options?.body ? JSON.stringify(options.body) : undefined,
|
|
100
|
+
headers: options?.headers
|
|
101
|
+
});
|
|
102
|
+
if (!result.success) {
|
|
103
|
+
console.warn(`[lobster-pay] payment failed: ${result.error}`);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const raw = result.body;
|
|
108
|
+
const jsonStart = Math.min(...[raw.indexOf("{"), raw.indexOf("[")].filter((i) => i >= 0));
|
|
109
|
+
const jsonStr = jsonStart >= 0 ? raw.slice(jsonStart) : raw;
|
|
110
|
+
const data = JSON.parse(jsonStr);
|
|
111
|
+
console.log("[lobster-pay] payment successful — got paid response");
|
|
112
|
+
return { data, paid: true };
|
|
113
|
+
} catch (_e) {
|
|
114
|
+
console.warn("[lobster-pay] paid response was not valid JSON");
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
var LOBSTER_PAY_TIMEOUT_MS = 30000, cachedCommand = undefined;
|
|
119
|
+
var init_lobster_pay = () => {};
|
|
3
120
|
|
|
4
121
|
// ../../src/mcp.ts
|
|
5
122
|
import { config as loadEnv } from "dotenv";
|
|
6
123
|
import { createInterface } from "readline";
|
|
7
|
-
import { existsSync as
|
|
124
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
8
125
|
import path4 from "path";
|
|
9
126
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10
127
|
|
|
@@ -108,12 +225,12 @@ import { dirname, join, parse } from "path";
|
|
|
108
225
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
109
226
|
|
|
110
227
|
// ../../src/build-info.generated.ts
|
|
111
|
-
var BUILD_RELEASE_VERSION = "3.0.
|
|
112
|
-
var BUILD_GIT_SHA = "
|
|
228
|
+
var BUILD_RELEASE_VERSION = "3.1.0-experiments.5e7a7bb";
|
|
229
|
+
var BUILD_GIT_SHA = "5e7a7bb949c1";
|
|
113
230
|
var BUILD_CODE_HASH = "1488fc1d92b7";
|
|
114
|
-
var BUILD_RELEASE_MANIFEST_BASE64 = "
|
|
115
|
-
var BUILD_RELEASE_MANIFEST_SIGNATURE = "
|
|
116
|
-
var BUILD_DEFAULT_BACKEND_URL = "https://
|
|
231
|
+
var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4xLjAtZXhwZXJpbWVudHMuNWU3YTdiYiIsImdpdF9zaGEiOiI1ZTdhN2JiOTQ5YzEiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDVlN2E3YmI5NDljMSIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDVUMTQ6NTY6MjkuNjY2WiJ9";
|
|
232
|
+
var BUILD_RELEASE_MANIFEST_SIGNATURE = "OuZD9NeemoStAyT3-MgMS3V3eeatbRMKkVY_J4_6nsM";
|
|
233
|
+
var BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
|
|
117
234
|
|
|
118
235
|
// ../../src/version.ts
|
|
119
236
|
var MODULE_DIR = dirname(fileURLToPath2(import.meta.url));
|
|
@@ -465,6 +582,327 @@ function listWorkflowPublishArtifacts() {
|
|
|
465
582
|
return readdirSync2(dir).filter((entry) => entry.endsWith(".json")).map((entry) => join2(dir, entry));
|
|
466
583
|
}
|
|
467
584
|
|
|
585
|
+
// ../../src/impact-log.ts
|
|
586
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, appendFileSync, statSync, readFileSync as readFileSync4, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
587
|
+
import { homedir as homedir2 } from "node:os";
|
|
588
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
589
|
+
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
590
|
+
var MAX_ROTATIONS = 3;
|
|
591
|
+
function getLogDir() {
|
|
592
|
+
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
593
|
+
return process.env.UNBROWSE_CONFIG_DIR;
|
|
594
|
+
const profile = process.env.UNBROWSE_PROFILE?.trim();
|
|
595
|
+
return profile ? join3(homedir2(), ".unbrowse", "profiles", profile) : join3(homedir2(), ".unbrowse");
|
|
596
|
+
}
|
|
597
|
+
function getImpactLogPath() {
|
|
598
|
+
return join3(getLogDir(), "impact-log.jsonl");
|
|
599
|
+
}
|
|
600
|
+
function ensureDir2(path4) {
|
|
601
|
+
const dir = dirname2(path4);
|
|
602
|
+
if (!existsSync4(dir))
|
|
603
|
+
mkdirSync3(dir, { recursive: true });
|
|
604
|
+
}
|
|
605
|
+
function rotateIfNeeded(path4) {
|
|
606
|
+
try {
|
|
607
|
+
if (!existsSync4(path4))
|
|
608
|
+
return;
|
|
609
|
+
const size = statSync(path4).size;
|
|
610
|
+
if (size < MAX_LOG_BYTES)
|
|
611
|
+
return;
|
|
612
|
+
for (let i = MAX_ROTATIONS;i >= 1; i--) {
|
|
613
|
+
const older = `${path4}.${i}`;
|
|
614
|
+
if (!existsSync4(older))
|
|
615
|
+
continue;
|
|
616
|
+
if (i === MAX_ROTATIONS) {
|
|
617
|
+
try {
|
|
618
|
+
unlinkSync2(older);
|
|
619
|
+
} catch {}
|
|
620
|
+
} else {
|
|
621
|
+
try {
|
|
622
|
+
renameSync(older, `${path4}.${i + 1}`);
|
|
623
|
+
} catch {}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
renameSync(path4, `${path4}.1`);
|
|
627
|
+
} catch {}
|
|
628
|
+
}
|
|
629
|
+
function appendImpact(entry) {
|
|
630
|
+
try {
|
|
631
|
+
const hasSignal = (entry.time_saved_ms ?? 0) > 0 || (entry.tokens_saved ?? 0) > 0 || (entry.cost_saved_uc ?? 0) > 0 || entry.browser_avoided === true;
|
|
632
|
+
if (!hasSignal)
|
|
633
|
+
return;
|
|
634
|
+
const path4 = getImpactLogPath();
|
|
635
|
+
ensureDir2(path4);
|
|
636
|
+
rotateIfNeeded(path4);
|
|
637
|
+
appendFileSync(path4, JSON.stringify(entry) + `
|
|
638
|
+
`, "utf8");
|
|
639
|
+
} catch {}
|
|
640
|
+
}
|
|
641
|
+
function impactFromResult(command, result, extras = {}) {
|
|
642
|
+
if (!result || typeof result !== "object")
|
|
643
|
+
return null;
|
|
644
|
+
const r = result;
|
|
645
|
+
const impact = r.impact ?? null;
|
|
646
|
+
if (!impact || typeof impact !== "object")
|
|
647
|
+
return null;
|
|
648
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
649
|
+
return {
|
|
650
|
+
ts: new Date().toISOString(),
|
|
651
|
+
command,
|
|
652
|
+
source: typeof impact.source === "string" ? impact.source : undefined,
|
|
653
|
+
domain: extras.domain,
|
|
654
|
+
intent: extras.intent,
|
|
655
|
+
skill_id: extras.skill_id ?? (typeof r.skill_id === "string" ? r.skill_id : undefined),
|
|
656
|
+
endpoint_id: extras.endpoint_id ?? (typeof r.endpoint_id === "string" ? r.endpoint_id : undefined),
|
|
657
|
+
time_saved_ms: num(impact.time_saved_ms),
|
|
658
|
+
time_saved_pct: num(impact.time_saved_pct),
|
|
659
|
+
tokens_saved: num(impact.tokens_saved),
|
|
660
|
+
tokens_saved_pct: num(impact.tokens_saved_pct),
|
|
661
|
+
cost_saved_uc: num(impact.cost_saved_uc),
|
|
662
|
+
browser_avoided: impact.browser_avoided === true,
|
|
663
|
+
success: r.error == null
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function readImpactSummary() {
|
|
667
|
+
const path4 = getImpactLogPath();
|
|
668
|
+
const summary = {
|
|
669
|
+
total_runs: 0,
|
|
670
|
+
successful_runs: 0,
|
|
671
|
+
browser_avoided_runs: 0,
|
|
672
|
+
total_time_saved_ms: 0,
|
|
673
|
+
total_tokens_saved: 0,
|
|
674
|
+
total_cost_saved_uc: 0,
|
|
675
|
+
avg_time_saved_pct: 0,
|
|
676
|
+
avg_tokens_saved_pct: 0,
|
|
677
|
+
by_source: {},
|
|
678
|
+
first_entry_at: null,
|
|
679
|
+
last_entry_at: null
|
|
680
|
+
};
|
|
681
|
+
const files = [];
|
|
682
|
+
for (let i = MAX_ROTATIONS;i >= 1; i--) {
|
|
683
|
+
const rotated = `${path4}.${i}`;
|
|
684
|
+
if (existsSync4(rotated))
|
|
685
|
+
files.push(rotated);
|
|
686
|
+
}
|
|
687
|
+
if (existsSync4(path4))
|
|
688
|
+
files.push(path4);
|
|
689
|
+
if (files.length === 0)
|
|
690
|
+
return summary;
|
|
691
|
+
let timePctSum = 0;
|
|
692
|
+
let timePctCount = 0;
|
|
693
|
+
let tokenPctSum = 0;
|
|
694
|
+
let tokenPctCount = 0;
|
|
695
|
+
for (const file of files) {
|
|
696
|
+
let raw;
|
|
697
|
+
try {
|
|
698
|
+
raw = readFileSync4(file, "utf8");
|
|
699
|
+
} catch {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
for (const line of raw.split(`
|
|
703
|
+
`)) {
|
|
704
|
+
const trimmed = line.trim();
|
|
705
|
+
if (!trimmed)
|
|
706
|
+
continue;
|
|
707
|
+
let e;
|
|
708
|
+
try {
|
|
709
|
+
e = JSON.parse(trimmed);
|
|
710
|
+
} catch {
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
summary.total_runs += 1;
|
|
714
|
+
if (e.success !== false)
|
|
715
|
+
summary.successful_runs += 1;
|
|
716
|
+
if (e.browser_avoided)
|
|
717
|
+
summary.browser_avoided_runs += 1;
|
|
718
|
+
summary.total_time_saved_ms += e.time_saved_ms ?? 0;
|
|
719
|
+
summary.total_tokens_saved += e.tokens_saved ?? 0;
|
|
720
|
+
summary.total_cost_saved_uc += e.cost_saved_uc ?? 0;
|
|
721
|
+
if (typeof e.time_saved_pct === "number") {
|
|
722
|
+
timePctSum += e.time_saved_pct;
|
|
723
|
+
timePctCount += 1;
|
|
724
|
+
}
|
|
725
|
+
if (typeof e.tokens_saved_pct === "number") {
|
|
726
|
+
tokenPctSum += e.tokens_saved_pct;
|
|
727
|
+
tokenPctCount += 1;
|
|
728
|
+
}
|
|
729
|
+
if (e.source) {
|
|
730
|
+
summary.by_source[e.source] = (summary.by_source[e.source] ?? 0) + 1;
|
|
731
|
+
}
|
|
732
|
+
if (!summary.first_entry_at || e.ts < summary.first_entry_at)
|
|
733
|
+
summary.first_entry_at = e.ts;
|
|
734
|
+
if (!summary.last_entry_at || e.ts > summary.last_entry_at)
|
|
735
|
+
summary.last_entry_at = e.ts;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
summary.avg_time_saved_pct = timePctCount > 0 ? Math.round(timePctSum / timePctCount) : 0;
|
|
739
|
+
summary.avg_tokens_saved_pct = tokenPctCount > 0 ? Math.round(tokenPctSum / tokenPctCount) : 0;
|
|
740
|
+
return summary;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// ../../src/client/index.ts
|
|
744
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
|
|
745
|
+
import { join as join5 } from "path";
|
|
746
|
+
import { homedir as homedir4, hostname } from "os";
|
|
747
|
+
|
|
748
|
+
// ../../src/payments/cascade.ts
|
|
749
|
+
import bs58 from "bs58";
|
|
750
|
+
|
|
751
|
+
// ../../src/client/index.ts
|
|
752
|
+
var API_URL = process.env.UNBROWSE_BACKEND_URL || DEFAULT_BACKEND_URL;
|
|
753
|
+
var PROFILE_NAME = sanitizeProfileName2(process.env.UNBROWSE_PROFILE ?? "");
|
|
754
|
+
var recentLocalSkills = new Map;
|
|
755
|
+
var LOCAL_ONLY = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
756
|
+
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
757
|
+
const manifest = manifestBase64.trim();
|
|
758
|
+
const sig = signature.trim();
|
|
759
|
+
if (!manifest || !sig)
|
|
760
|
+
return {};
|
|
761
|
+
return {
|
|
762
|
+
"X-Unbrowse-Release-Manifest": manifest,
|
|
763
|
+
"X-Unbrowse-Release-Signature": sig
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function decodeBase64Json(value) {
|
|
767
|
+
try {
|
|
768
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") {
|
|
769
|
+
const binary = globalThis.atob(value);
|
|
770
|
+
const bytes = new Uint8Array(binary.length);
|
|
771
|
+
for (let i = 0;i < binary.length; i++) {
|
|
772
|
+
bytes[i] = binary.charCodeAt(i);
|
|
773
|
+
}
|
|
774
|
+
return JSON.parse(new TextDecoder("utf-8").decode(bytes));
|
|
775
|
+
}
|
|
776
|
+
return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
|
|
777
|
+
} catch {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function getConfigDir2() {
|
|
782
|
+
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
783
|
+
return process.env.UNBROWSE_CONFIG_DIR;
|
|
784
|
+
return PROFILE_NAME ? join5(homedir4(), ".unbrowse", "profiles", PROFILE_NAME) : join5(homedir4(), ".unbrowse");
|
|
785
|
+
}
|
|
786
|
+
function getConfigPath() {
|
|
787
|
+
return join5(getConfigDir2(), "config.json");
|
|
788
|
+
}
|
|
789
|
+
function sanitizeProfileName2(value) {
|
|
790
|
+
return value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
791
|
+
}
|
|
792
|
+
function loadConfig() {
|
|
793
|
+
try {
|
|
794
|
+
const configPath = getConfigPath();
|
|
795
|
+
if (existsSync6(configPath)) {
|
|
796
|
+
return JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
797
|
+
}
|
|
798
|
+
} catch {}
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
function getApiKey() {
|
|
802
|
+
if (LOCAL_ONLY)
|
|
803
|
+
return "local-only";
|
|
804
|
+
if (process.env.UNBROWSE_API_KEY)
|
|
805
|
+
return process.env.UNBROWSE_API_KEY;
|
|
806
|
+
const config = loadConfig();
|
|
807
|
+
if (config?.api_key) {
|
|
808
|
+
process.env.UNBROWSE_API_KEY = config.api_key;
|
|
809
|
+
return config.api_key;
|
|
810
|
+
}
|
|
811
|
+
return "";
|
|
812
|
+
}
|
|
813
|
+
function getAgentId() {
|
|
814
|
+
const config = loadConfig();
|
|
815
|
+
return config?.agent_id ?? null;
|
|
816
|
+
}
|
|
817
|
+
var API_TIMEOUT_MS = parseInt(process.env.UNBROWSE_API_TIMEOUT ?? "8000", 10);
|
|
818
|
+
var PUBLISH_TIMEOUT_MS = parseInt(process.env.UNBROWSE_PUBLISH_TIMEOUT ?? "30000", 10);
|
|
819
|
+
async function apiRequest(method, path4, body, opts) {
|
|
820
|
+
const key = opts?.noAuth ? "" : getApiKey();
|
|
821
|
+
const releaseAttestationHeaders = buildReleaseAttestationHeaders(RELEASE_MANIFEST_BASE64, RELEASE_MANIFEST_SIGNATURE);
|
|
822
|
+
const controller = new AbortController;
|
|
823
|
+
const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? API_TIMEOUT_MS);
|
|
824
|
+
let res;
|
|
825
|
+
try {
|
|
826
|
+
res = await fetch(`${API_URL}${path4}`, {
|
|
827
|
+
method,
|
|
828
|
+
headers: {
|
|
829
|
+
"Content-Type": "application/json",
|
|
830
|
+
"Accept-Encoding": "gzip, deflate",
|
|
831
|
+
"X-Unbrowse-Trace-Version": TRACE_VERSION,
|
|
832
|
+
"X-Unbrowse-Code-Hash": CODE_HASH,
|
|
833
|
+
"X-Unbrowse-Git-Sha": GIT_SHA,
|
|
834
|
+
...releaseAttestationHeaders,
|
|
835
|
+
...key ? { Authorization: `Bearer ${key}` } : {}
|
|
836
|
+
},
|
|
837
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
838
|
+
signal: controller.signal
|
|
839
|
+
});
|
|
840
|
+
} finally {
|
|
841
|
+
clearTimeout(timer);
|
|
842
|
+
}
|
|
843
|
+
let data;
|
|
844
|
+
try {
|
|
845
|
+
data = await res.json();
|
|
846
|
+
} catch {
|
|
847
|
+
throw new Error(`API error ${res.status} from ${path4}`);
|
|
848
|
+
}
|
|
849
|
+
if (res.status === 403 && data.error === "tos_update_required") {
|
|
850
|
+
console.warn(`
|
|
851
|
+
[unbrowse] The Terms of Service have been updated.`);
|
|
852
|
+
console.warn("[unbrowse] Please restart the unbrowse service to accept the new terms.");
|
|
853
|
+
throw new Error("ToS update required. Restart unbrowse to accept new terms.");
|
|
854
|
+
}
|
|
855
|
+
if (res.status === 402) {
|
|
856
|
+
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
857
|
+
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
|
858
|
+
const terms = paymentRequired ? decodeBase64Json(paymentRequired) : legacyPaymentTerms ? JSON.parse(legacyPaymentTerms) : data.terms;
|
|
859
|
+
try {
|
|
860
|
+
const { isLobsterAvailable: isLobsterAvailable2, payAndRetry: payAndRetry2 } = await Promise.resolve().then(() => (init_lobster_pay(), exports_lobster_pay));
|
|
861
|
+
if (isLobsterAvailable2()) {
|
|
862
|
+
const fullUrl = `${API_URL}${path4}`;
|
|
863
|
+
const paidResult = await payAndRetry2(fullUrl, {
|
|
864
|
+
body,
|
|
865
|
+
headers: {
|
|
866
|
+
"Content-Type": "application/json",
|
|
867
|
+
"Accept-Encoding": "gzip, deflate",
|
|
868
|
+
...releaseAttestationHeaders,
|
|
869
|
+
...key ? { Authorization: `Bearer ${key}` } : {}
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
if (paidResult) {
|
|
873
|
+
return { data: paidResult.data, headers: new Headers };
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
} catch (payErr) {
|
|
877
|
+
console.warn(`[x402] lobster pay-and-retry failed: ${payErr.message}`);
|
|
878
|
+
}
|
|
879
|
+
const err = new Error(`Payment required: ${data.error ?? "This skill requires payment"}`);
|
|
880
|
+
err.x402 = true;
|
|
881
|
+
err.terms = terms;
|
|
882
|
+
err.status = 402;
|
|
883
|
+
throw err;
|
|
884
|
+
}
|
|
885
|
+
if (!res.ok) {
|
|
886
|
+
const errData = data;
|
|
887
|
+
const msg = errData.details?.length ? `${errData.error}: ${errData.details.join("; ")}` : errData.error ?? `API HTTP ${res.status}`;
|
|
888
|
+
throw new Error(msg);
|
|
889
|
+
}
|
|
890
|
+
return { data, headers: res.headers };
|
|
891
|
+
}
|
|
892
|
+
async function api(method, path4, body, opts) {
|
|
893
|
+
const { data } = await apiRequest(method, path4, body, opts);
|
|
894
|
+
return data;
|
|
895
|
+
}
|
|
896
|
+
async function getMyProfile() {
|
|
897
|
+
return api("GET", "/v1/agents/me", undefined);
|
|
898
|
+
}
|
|
899
|
+
async function getTransactionHistory(agentId) {
|
|
900
|
+
return api("GET", `/v1/transactions/consumer/${agentId}`);
|
|
901
|
+
}
|
|
902
|
+
async function getCreatorEarnings(agentId) {
|
|
903
|
+
return api("GET", `/v1/transactions/creator/${agentId}`);
|
|
904
|
+
}
|
|
905
|
+
|
|
468
906
|
// ../../src/mcp.ts
|
|
469
907
|
loadEnv({ quiet: true });
|
|
470
908
|
loadEnv({ path: ".env.runtime", quiet: true });
|
|
@@ -862,7 +1300,7 @@ function getVersion() {
|
|
|
862
1300
|
while (dir !== root) {
|
|
863
1301
|
const pkgPath = path4.join(dir, "package.json");
|
|
864
1302
|
try {
|
|
865
|
-
const pkg = JSON.parse(
|
|
1303
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
866
1304
|
if (pkg.version)
|
|
867
1305
|
return pkg.version;
|
|
868
1306
|
} catch {}
|
|
@@ -874,11 +1312,11 @@ function getPackageRoot2() {
|
|
|
874
1312
|
let dir = path4.dirname(fileURLToPath3(import.meta.url));
|
|
875
1313
|
const root = path4.parse(dir).root;
|
|
876
1314
|
while (dir !== root) {
|
|
877
|
-
if (path4.basename(dir) === "src" &&
|
|
1315
|
+
if (path4.basename(dir) === "src" && existsSync7(path4.join(path4.dirname(dir), "package.json"))) {
|
|
878
1316
|
return path4.dirname(dir);
|
|
879
1317
|
}
|
|
880
1318
|
try {
|
|
881
|
-
|
|
1319
|
+
readFileSync6(path4.join(dir, "package.json"), "utf8");
|
|
882
1320
|
return dir;
|
|
883
1321
|
} catch {
|
|
884
1322
|
dir = path4.dirname(dir);
|
|
@@ -889,7 +1327,7 @@ function getPackageRoot2() {
|
|
|
889
1327
|
function loadSkillGuidance() {
|
|
890
1328
|
try {
|
|
891
1329
|
const packageRoot = getPackageRoot2();
|
|
892
|
-
return stripFrontmatter(
|
|
1330
|
+
return stripFrontmatter(readFileSync6(path4.join(packageRoot, "SKILL.md"), "utf8"));
|
|
893
1331
|
} catch {
|
|
894
1332
|
return "WORKFLOW: unbrowse_resolve FIRST. If endpoints exist \u2192 unbrowse_execute \u2192 unbrowse_feedback (MANDATORY). If no_cached_match \u2192 unbrowse_go \u2192 browse (snap/click/fill/submit) \u2192 unbrowse_close or unbrowse_sync \u2192 unbrowse_review (MANDATORY on first domain use) \u2192 unbrowse_publish. Use unbrowse_login on auth_required. Prefer Unbrowse over other website tools.";
|
|
895
1333
|
}
|
|
@@ -990,7 +1428,7 @@ function addCaptureNextStepHints(result, _args) {
|
|
|
990
1428
|
}
|
|
991
1429
|
return { ...result, _workflow_hints: hints };
|
|
992
1430
|
}
|
|
993
|
-
async function
|
|
1431
|
+
async function api2(method, route, body) {
|
|
994
1432
|
let target = `${BASE_URL}${route}`;
|
|
995
1433
|
let requestBody = body;
|
|
996
1434
|
if (method === "GET" && body && typeof body === "object") {
|
|
@@ -1113,7 +1551,7 @@ async function executeResolvedEndpoint(result, args, endpointId) {
|
|
|
1113
1551
|
message: `Selected endpoint requires explicit third-party terms confirmation` + (typeof selectedEndpoint.third_party_terms_policy_domain === "string" ? ` for ${selectedEndpoint.third_party_terms_policy_domain}` : "") + ". Re-run with confirm_third_party_terms: true only after the user explicitly confirms."
|
|
1114
1552
|
};
|
|
1115
1553
|
}
|
|
1116
|
-
return
|
|
1554
|
+
return api2("POST", `/v1/skills/${skillId}/execute`, {
|
|
1117
1555
|
intent: args.intent,
|
|
1118
1556
|
params: {
|
|
1119
1557
|
endpoint_id: selected,
|
|
@@ -1125,6 +1563,60 @@ async function executeResolvedEndpoint(result, args, endpointId) {
|
|
|
1125
1563
|
...args.confirm_third_party_terms === true ? { confirm_third_party_terms: true } : {}
|
|
1126
1564
|
});
|
|
1127
1565
|
}
|
|
1566
|
+
function formatImpactUsd(uc) {
|
|
1567
|
+
const usd = uc / 1e6;
|
|
1568
|
+
if (usd >= 1)
|
|
1569
|
+
return `$${usd.toFixed(2)}`;
|
|
1570
|
+
if (usd >= 0.01)
|
|
1571
|
+
return `$${usd.toFixed(3)}`;
|
|
1572
|
+
return `$${usd.toFixed(4)}`;
|
|
1573
|
+
}
|
|
1574
|
+
function formatImpactDuration(ms) {
|
|
1575
|
+
if (ms >= 3600000)
|
|
1576
|
+
return `${(ms / 3600000).toFixed(1)}h`;
|
|
1577
|
+
if (ms >= 60000)
|
|
1578
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
1579
|
+
if (ms >= 1e4)
|
|
1580
|
+
return `${Math.round(ms / 1000)}s`;
|
|
1581
|
+
if (ms >= 1000)
|
|
1582
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
1583
|
+
return `${ms}ms`;
|
|
1584
|
+
}
|
|
1585
|
+
function summarizeImpact(result) {
|
|
1586
|
+
if (!result || typeof result !== "object")
|
|
1587
|
+
return "";
|
|
1588
|
+
const impact = result.impact;
|
|
1589
|
+
if (!impact)
|
|
1590
|
+
return "";
|
|
1591
|
+
const timeMs = typeof impact.time_saved_ms === "number" ? impact.time_saved_ms : 0;
|
|
1592
|
+
const tokens = typeof impact.tokens_saved === "number" ? impact.tokens_saved : 0;
|
|
1593
|
+
const timePct = typeof impact.time_saved_pct === "number" ? impact.time_saved_pct : 0;
|
|
1594
|
+
const tokensPct = typeof impact.tokens_saved_pct === "number" ? impact.tokens_saved_pct : 0;
|
|
1595
|
+
const costUc = typeof impact.cost_saved_uc === "number" ? impact.cost_saved_uc : 0;
|
|
1596
|
+
const browserAvoided = impact.browser_avoided === true;
|
|
1597
|
+
if (timeMs <= 0 && tokens <= 0 && costUc <= 0 && !browserAvoided)
|
|
1598
|
+
return "";
|
|
1599
|
+
const parts = [];
|
|
1600
|
+
if (timeMs > 0)
|
|
1601
|
+
parts.push(`${formatImpactDuration(timeMs)} saved (${timePct}% faster)`);
|
|
1602
|
+
if (tokens > 0)
|
|
1603
|
+
parts.push(`${tokens.toLocaleString("en-US")} tokens saved (${tokensPct}% less context)`);
|
|
1604
|
+
if (costUc > 0)
|
|
1605
|
+
parts.push(`${formatImpactUsd(costUc)} saved`);
|
|
1606
|
+
if (browserAvoided)
|
|
1607
|
+
parts.push("browser avoided");
|
|
1608
|
+
return `Impact: ${parts.join(" \u2022 ")}`;
|
|
1609
|
+
}
|
|
1610
|
+
function recordImpactForTool(command, result, args) {
|
|
1611
|
+
const entry = impactFromResult(command, result, {
|
|
1612
|
+
intent: typeof args.intent === "string" ? args.intent : undefined,
|
|
1613
|
+
domain: typeof args.domain === "string" ? args.domain : undefined,
|
|
1614
|
+
skill_id: typeof args.skill === "string" ? args.skill : undefined,
|
|
1615
|
+
endpoint_id: typeof args.endpoint === "string" ? args.endpoint : undefined
|
|
1616
|
+
});
|
|
1617
|
+
if (entry)
|
|
1618
|
+
appendImpact(entry);
|
|
1619
|
+
}
|
|
1128
1620
|
var tools = [
|
|
1129
1621
|
{
|
|
1130
1622
|
name: "unbrowse_health",
|
|
@@ -1133,7 +1625,7 @@ var tools = [
|
|
|
1133
1625
|
annotations: { readOnlyHint: true },
|
|
1134
1626
|
handler: async () => {
|
|
1135
1627
|
await ensureServerReady();
|
|
1136
|
-
return successResult(await
|
|
1628
|
+
return successResult(await api2("GET", "/health"), "Unbrowse local runtime health.");
|
|
1137
1629
|
}
|
|
1138
1630
|
},
|
|
1139
1631
|
{
|
|
@@ -1185,7 +1677,7 @@ var tools = [
|
|
|
1185
1677
|
body.confirm_third_party_terms = true;
|
|
1186
1678
|
if (args.force_capture === true)
|
|
1187
1679
|
body.force_capture = true;
|
|
1188
|
-
let result = await
|
|
1680
|
+
let result = await api2("POST", "/v1/intent/resolve", body);
|
|
1189
1681
|
const authError = resolveNestedError(result);
|
|
1190
1682
|
if (authError === "auth_required") {
|
|
1191
1683
|
const loginUrl = isPlainObject(result.result) && typeof result.result.login_url === "string" ? result.result.login_url : args.url;
|
|
@@ -1196,7 +1688,12 @@ var tools = [
|
|
|
1196
1688
|
}
|
|
1197
1689
|
result = addResolveMissGuidance(result, args);
|
|
1198
1690
|
const nestedError = resolveNestedError(result);
|
|
1199
|
-
|
|
1691
|
+
recordImpactForTool("resolve", result, args);
|
|
1692
|
+
if (nestedError)
|
|
1693
|
+
return errorResult(nestedError, result);
|
|
1694
|
+
const processed = maybePostProcessResult(result, args);
|
|
1695
|
+
const impactLine = summarizeImpact(result);
|
|
1696
|
+
return successResult(processed, impactLine ? `Resolve result. ${impactLine}` : "Resolve result.");
|
|
1200
1697
|
}
|
|
1201
1698
|
},
|
|
1202
1699
|
{
|
|
@@ -1242,13 +1739,114 @@ var tools = [
|
|
|
1242
1739
|
body.confirm_unsafe = true;
|
|
1243
1740
|
if (args.confirm_third_party_terms === true)
|
|
1244
1741
|
body.confirm_third_party_terms = true;
|
|
1245
|
-
const result = await
|
|
1742
|
+
const result = await api2("POST", `/v1/skills/${args.skill}/execute`, body);
|
|
1246
1743
|
const nestedError = resolveNestedError(result);
|
|
1744
|
+
recordImpactForTool("execute", result, args);
|
|
1247
1745
|
if (nestedError)
|
|
1248
1746
|
return errorResult(nestedError, result);
|
|
1249
1747
|
const processed = maybePostProcessResult(result, args);
|
|
1250
1748
|
const withHints = addExecuteNextStepHints(isPlainObject(processed) ? processed : { result: processed }, args);
|
|
1251
|
-
|
|
1749
|
+
const impactLine = summarizeImpact(result);
|
|
1750
|
+
return successResult(withHints, impactLine ? `Execution result. ${impactLine}. See _workflow_hints for required next steps.` : "Execution result. See _workflow_hints for required next steps.");
|
|
1751
|
+
}
|
|
1752
|
+
},
|
|
1753
|
+
{
|
|
1754
|
+
name: "unbrowse_stats",
|
|
1755
|
+
description: "Show lifetime impact for this agent: total time saved, tokens saved, cost saved, browser calls avoided, and marketplace earnings/spending. Read-only \u2014 safe to call anytime. Use this to show the user the concrete value Unbrowse has delivered.",
|
|
1756
|
+
inputSchema: {
|
|
1757
|
+
type: "object",
|
|
1758
|
+
properties: {
|
|
1759
|
+
include_recent: { type: "boolean", description: "Include recent earnings/spending transactions. Default false." }
|
|
1760
|
+
},
|
|
1761
|
+
additionalProperties: false
|
|
1762
|
+
},
|
|
1763
|
+
annotations: { readOnlyHint: true },
|
|
1764
|
+
handler: async (args) => {
|
|
1765
|
+
await ensureServerReady();
|
|
1766
|
+
const local = readImpactSummary();
|
|
1767
|
+
const agentId = getAgentId();
|
|
1768
|
+
let profile = null;
|
|
1769
|
+
let earnings = null;
|
|
1770
|
+
let spending = null;
|
|
1771
|
+
const remoteErrors = {};
|
|
1772
|
+
if (agentId) {
|
|
1773
|
+
const results = await Promise.allSettled([
|
|
1774
|
+
getMyProfile(),
|
|
1775
|
+
getCreatorEarnings(agentId),
|
|
1776
|
+
getTransactionHistory(agentId)
|
|
1777
|
+
]);
|
|
1778
|
+
if (results[0].status === "fulfilled")
|
|
1779
|
+
profile = results[0].value;
|
|
1780
|
+
else
|
|
1781
|
+
remoteErrors.profile = results[0].reason?.message ?? String(results[0].reason);
|
|
1782
|
+
if (results[1].status === "fulfilled")
|
|
1783
|
+
earnings = results[1].value;
|
|
1784
|
+
else
|
|
1785
|
+
remoteErrors.earnings = results[1].reason?.message ?? String(results[1].reason);
|
|
1786
|
+
if (results[2].status === "fulfilled")
|
|
1787
|
+
spending = results[2].value;
|
|
1788
|
+
else
|
|
1789
|
+
remoteErrors.spending = results[2].reason?.message ?? String(results[2].reason);
|
|
1790
|
+
} else {
|
|
1791
|
+
remoteErrors.profile = "No agent_id in local config. Run `unbrowse setup` to register.";
|
|
1792
|
+
}
|
|
1793
|
+
const earnedUsd = earnings?.ledger?.total_earned_usd ?? 0;
|
|
1794
|
+
const spentUsd = spending?.ledger?.total_spent_usd ?? 0;
|
|
1795
|
+
const savedUsd = local.total_cost_saved_uc / 1e6;
|
|
1796
|
+
const includeRecent = args.include_recent === true;
|
|
1797
|
+
const payload = {
|
|
1798
|
+
agent_id: agentId,
|
|
1799
|
+
profile,
|
|
1800
|
+
impact: {
|
|
1801
|
+
total_runs: local.total_runs,
|
|
1802
|
+
successful_runs: local.successful_runs,
|
|
1803
|
+
browser_avoided_runs: local.browser_avoided_runs,
|
|
1804
|
+
total_time_saved_ms: local.total_time_saved_ms,
|
|
1805
|
+
total_time_saved_human: formatImpactDuration(local.total_time_saved_ms),
|
|
1806
|
+
total_tokens_saved: local.total_tokens_saved,
|
|
1807
|
+
total_cost_saved_usd: Number(savedUsd.toFixed(6)),
|
|
1808
|
+
avg_time_saved_pct: local.avg_time_saved_pct,
|
|
1809
|
+
avg_tokens_saved_pct: local.avg_tokens_saved_pct,
|
|
1810
|
+
by_source: local.by_source,
|
|
1811
|
+
first_entry_at: local.first_entry_at,
|
|
1812
|
+
last_entry_at: local.last_entry_at,
|
|
1813
|
+
log_path: getImpactLogPath()
|
|
1814
|
+
},
|
|
1815
|
+
earnings: {
|
|
1816
|
+
total_earned_usd: earnedUsd,
|
|
1817
|
+
total_earned_uc: earnings?.ledger?.total_earned_uc ?? 0,
|
|
1818
|
+
transaction_count: earnings?.ledger?.transaction_count ?? 0,
|
|
1819
|
+
last_transaction_at: earnings?.ledger?.last_transaction_at ?? null,
|
|
1820
|
+
...includeRecent && earnings?.transactions ? { recent: earnings.transactions.slice(0, 10) } : {}
|
|
1821
|
+
},
|
|
1822
|
+
spending: {
|
|
1823
|
+
total_spent_usd: spentUsd,
|
|
1824
|
+
total_spent_uc: spending?.ledger?.total_spent_uc ?? 0,
|
|
1825
|
+
transaction_count: spending?.ledger?.transaction_count ?? 0,
|
|
1826
|
+
last_transaction_at: spending?.ledger?.last_transaction_at ?? null,
|
|
1827
|
+
...includeRecent && spending?.transactions ? { recent: spending.transactions.slice(0, 10) } : {}
|
|
1828
|
+
},
|
|
1829
|
+
net_usd: earnedUsd - spentUsd,
|
|
1830
|
+
...Object.keys(remoteErrors).length > 0 ? { remote_errors: remoteErrors } : {}
|
|
1831
|
+
};
|
|
1832
|
+
const headline = [];
|
|
1833
|
+
if (local.total_runs > 0) {
|
|
1834
|
+
const bits = [];
|
|
1835
|
+
if (local.total_time_saved_ms > 0)
|
|
1836
|
+
bits.push(`${formatImpactDuration(local.total_time_saved_ms)} saved`);
|
|
1837
|
+
if (local.total_tokens_saved > 0)
|
|
1838
|
+
bits.push(`${local.total_tokens_saved.toLocaleString("en-US")} tokens saved`);
|
|
1839
|
+
if (savedUsd > 0)
|
|
1840
|
+
bits.push(`${formatImpactUsd(local.total_cost_saved_uc)} saved`);
|
|
1841
|
+
if (local.browser_avoided_runs > 0)
|
|
1842
|
+
bits.push(`${local.browser_avoided_runs} browser calls avoided`);
|
|
1843
|
+
if (bits.length > 0)
|
|
1844
|
+
headline.push(`Lifetime impact (${local.total_runs} runs): ${bits.join(" \u2022 ")}`);
|
|
1845
|
+
}
|
|
1846
|
+
if (agentId && !remoteErrors.earnings && !remoteErrors.spending) {
|
|
1847
|
+
headline.push(`Marketplace: +$${earnedUsd.toFixed(4)} earned, -$${spentUsd.toFixed(4)} spent, net ${earnedUsd - spentUsd >= 0 ? "+" : ""}$${(earnedUsd - spentUsd).toFixed(4)}`);
|
|
1848
|
+
}
|
|
1849
|
+
return successResult(payload, headline.length > 0 ? headline.join(" \u2022 ") : "Unbrowse stats (no runs recorded yet).");
|
|
1252
1850
|
}
|
|
1253
1851
|
},
|
|
1254
1852
|
{
|
|
@@ -1278,7 +1876,7 @@ var tools = [
|
|
|
1278
1876
|
body.outcome = args.outcome;
|
|
1279
1877
|
if (isPlainObject(args.diagnostics))
|
|
1280
1878
|
body.diagnostics = args.diagnostics;
|
|
1281
|
-
return successResult(await
|
|
1879
|
+
return successResult(await api2("POST", "/v1/feedback", body), "Feedback submitted.");
|
|
1282
1880
|
}
|
|
1283
1881
|
},
|
|
1284
1882
|
{
|
|
@@ -1295,7 +1893,7 @@ var tools = [
|
|
|
1295
1893
|
annotations: { destructiveHint: true },
|
|
1296
1894
|
handler: async (args) => {
|
|
1297
1895
|
await ensureServerReady();
|
|
1298
|
-
return successResult(await
|
|
1896
|
+
return successResult(await api2("POST", `/v1/skills/${args.skill}/index`, {}), "Local index recomputed.");
|
|
1299
1897
|
}
|
|
1300
1898
|
},
|
|
1301
1899
|
{
|
|
@@ -1357,7 +1955,7 @@ var tools = [
|
|
|
1357
1955
|
annotations: { destructiveHint: true },
|
|
1358
1956
|
handler: async (args) => {
|
|
1359
1957
|
await ensureServerReady();
|
|
1360
|
-
return successResult(await
|
|
1958
|
+
return successResult(await api2("POST", `/v1/skills/${args.skill}/review`, { endpoints: args.endpoints }), "Review metadata applied and local contracts re-indexed.");
|
|
1361
1959
|
}
|
|
1362
1960
|
},
|
|
1363
1961
|
{
|
|
@@ -1421,7 +2019,7 @@ var tools = [
|
|
|
1421
2019
|
body.confirm_publish = true;
|
|
1422
2020
|
if (Array.isArray(args.endpoints))
|
|
1423
2021
|
body.endpoints = args.endpoints;
|
|
1424
|
-
return successResult(await
|
|
2022
|
+
return successResult(await api2("POST", `/v1/skills/${args.skill}/publish`, body), Array.isArray(args.endpoints) ? "Publish step applied." : "Publish review surface.");
|
|
1425
2023
|
}
|
|
1426
2024
|
},
|
|
1427
2025
|
{
|
|
@@ -1451,7 +2049,7 @@ var tools = [
|
|
|
1451
2049
|
await ensureServerReady();
|
|
1452
2050
|
const hasMutation = args.auto_publish === true || args.auto_publish === false || Array.isArray(args.publish_blacklist) || Array.isArray(args.publish_promptlist) || args.clear_publish_blacklist === true || args.clear_publish_promptlist === true;
|
|
1453
2051
|
if (!hasMutation) {
|
|
1454
|
-
return successResult(await
|
|
2052
|
+
return successResult(await api2("GET", "/v1/settings"), "Local capture/publish policy settings.");
|
|
1455
2053
|
}
|
|
1456
2054
|
const body = {};
|
|
1457
2055
|
if (args.auto_publish === true || args.auto_publish === false) {
|
|
@@ -1465,7 +2063,7 @@ var tools = [
|
|
|
1465
2063
|
body.clear_publish_domain_blacklist = true;
|
|
1466
2064
|
if (args.clear_publish_promptlist === true)
|
|
1467
2065
|
body.clear_publish_domain_promptlist = true;
|
|
1468
|
-
return successResult(await
|
|
2066
|
+
return successResult(await api2("POST", "/v1/settings", body), "Local capture/publish policy updated.");
|
|
1469
2067
|
}
|
|
1470
2068
|
},
|
|
1471
2069
|
{
|
|
@@ -1482,7 +2080,7 @@ var tools = [
|
|
|
1482
2080
|
annotations: { destructiveHint: true, openWorldHint: true },
|
|
1483
2081
|
handler: async (args) => {
|
|
1484
2082
|
await ensureServerReady();
|
|
1485
|
-
const result = await
|
|
2083
|
+
const result = await api2("POST", "/v1/auth/login", { url: args.url });
|
|
1486
2084
|
const nestedError = resolveNestedError(result);
|
|
1487
2085
|
return nestedError ? errorResult(nestedError, result) : successResult(result, "Interactive login flow launched.");
|
|
1488
2086
|
}
|
|
@@ -1494,7 +2092,7 @@ var tools = [
|
|
|
1494
2092
|
annotations: { readOnlyHint: true },
|
|
1495
2093
|
handler: async () => {
|
|
1496
2094
|
await ensureServerReady();
|
|
1497
|
-
return successResult(await
|
|
2095
|
+
return successResult(await api2("GET", "/v1/skills"), "Known skills.");
|
|
1498
2096
|
}
|
|
1499
2097
|
},
|
|
1500
2098
|
{
|
|
@@ -1511,7 +2109,7 @@ var tools = [
|
|
|
1511
2109
|
annotations: { readOnlyHint: true },
|
|
1512
2110
|
handler: async (args) => {
|
|
1513
2111
|
await ensureServerReady();
|
|
1514
|
-
return successResult(await
|
|
2112
|
+
return successResult(await api2("GET", `/v1/skills/${args.id}`), "Skill manifest.");
|
|
1515
2113
|
}
|
|
1516
2114
|
},
|
|
1517
2115
|
{
|
|
@@ -1530,7 +2128,7 @@ var tools = [
|
|
|
1530
2128
|
handler: async (args) => {
|
|
1531
2129
|
await ensureServerReady();
|
|
1532
2130
|
const limit = typeof args.limit === "number" ? args.limit : 10;
|
|
1533
|
-
return successResult(await
|
|
2131
|
+
return successResult(await api2("GET", `/v1/sessions/${args.domain}?limit=${limit}`), "Session logs.");
|
|
1534
2132
|
}
|
|
1535
2133
|
},
|
|
1536
2134
|
{
|
|
@@ -1548,7 +2146,7 @@ var tools = [
|
|
|
1548
2146
|
annotations: { openWorldHint: true },
|
|
1549
2147
|
handler: async (args) => {
|
|
1550
2148
|
await ensureServerReady();
|
|
1551
|
-
return successResult(await
|
|
2149
|
+
return successResult(await api2("POST", "/v1/browse/go", {
|
|
1552
2150
|
url: args.url,
|
|
1553
2151
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
1554
2152
|
}), "Live browse session opened.");
|
|
@@ -1573,7 +2171,7 @@ var tools = [
|
|
|
1573
2171
|
body.filter = args.filter;
|
|
1574
2172
|
if (typeof args.session_id === "string")
|
|
1575
2173
|
body.session_id = args.session_id;
|
|
1576
|
-
return successResult(await
|
|
2174
|
+
return successResult(await api2("POST", "/v1/browse/snap", body), "Current browse snapshot.");
|
|
1577
2175
|
}
|
|
1578
2176
|
},
|
|
1579
2177
|
{
|
|
@@ -1591,7 +2189,7 @@ var tools = [
|
|
|
1591
2189
|
annotations: { destructiveHint: true },
|
|
1592
2190
|
handler: async (args) => {
|
|
1593
2191
|
await ensureServerReady();
|
|
1594
|
-
return successResult(await
|
|
2192
|
+
return successResult(await api2("POST", "/v1/browse/click", {
|
|
1595
2193
|
ref: args.ref,
|
|
1596
2194
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
1597
2195
|
}), "Click sent.");
|
|
@@ -1613,7 +2211,7 @@ var tools = [
|
|
|
1613
2211
|
annotations: { destructiveHint: true },
|
|
1614
2212
|
handler: async (args) => {
|
|
1615
2213
|
await ensureServerReady();
|
|
1616
|
-
return successResult(await
|
|
2214
|
+
return successResult(await api2("POST", "/v1/browse/fill", {
|
|
1617
2215
|
ref: args.ref,
|
|
1618
2216
|
value: args.value,
|
|
1619
2217
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
@@ -1635,7 +2233,7 @@ var tools = [
|
|
|
1635
2233
|
annotations: { destructiveHint: true },
|
|
1636
2234
|
handler: async (args) => {
|
|
1637
2235
|
await ensureServerReady();
|
|
1638
|
-
return successResult(await
|
|
2236
|
+
return successResult(await api2("POST", "/v1/browse/type", {
|
|
1639
2237
|
text: args.text,
|
|
1640
2238
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
1641
2239
|
}), "Text typed.");
|
|
@@ -1656,7 +2254,7 @@ var tools = [
|
|
|
1656
2254
|
annotations: { destructiveHint: true },
|
|
1657
2255
|
handler: async (args) => {
|
|
1658
2256
|
await ensureServerReady();
|
|
1659
|
-
return successResult(await
|
|
2257
|
+
return successResult(await api2("POST", "/v1/browse/press", {
|
|
1660
2258
|
key: args.key,
|
|
1661
2259
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
1662
2260
|
}), "Key press sent.");
|
|
@@ -1678,7 +2276,7 @@ var tools = [
|
|
|
1678
2276
|
annotations: { destructiveHint: true },
|
|
1679
2277
|
handler: async (args) => {
|
|
1680
2278
|
await ensureServerReady();
|
|
1681
|
-
return successResult(await
|
|
2279
|
+
return successResult(await api2("POST", "/v1/browse/select", {
|
|
1682
2280
|
ref: args.ref,
|
|
1683
2281
|
value: args.value,
|
|
1684
2282
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
@@ -1707,7 +2305,7 @@ var tools = [
|
|
|
1707
2305
|
body.amount = args.amount;
|
|
1708
2306
|
if (typeof args.session_id === "string")
|
|
1709
2307
|
body.session_id = args.session_id;
|
|
1710
|
-
return successResult(await
|
|
2308
|
+
return successResult(await api2("POST", "/v1/browse/scroll", body), "Scroll applied.");
|
|
1711
2309
|
}
|
|
1712
2310
|
},
|
|
1713
2311
|
{
|
|
@@ -1734,7 +2332,7 @@ var tools = [
|
|
|
1734
2332
|
if (args[key] !== undefined)
|
|
1735
2333
|
body[key] = args[key];
|
|
1736
2334
|
}
|
|
1737
|
-
const result = await
|
|
2335
|
+
const result = await api2("POST", "/v1/browse/submit", body);
|
|
1738
2336
|
const nestedError = resolveNestedError(result);
|
|
1739
2337
|
return nestedError ? errorResult(nestedError, result) : successResult(result, "Submit result.");
|
|
1740
2338
|
}
|
|
@@ -1750,7 +2348,7 @@ var tools = [
|
|
|
1750
2348
|
annotations: { readOnlyHint: true },
|
|
1751
2349
|
handler: async (args) => {
|
|
1752
2350
|
await ensureServerReady();
|
|
1753
|
-
const result = await
|
|
2351
|
+
const result = await api2("GET", "/v1/browse/screenshot", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined);
|
|
1754
2352
|
if (typeof result.screenshot !== "string")
|
|
1755
2353
|
return errorResult("screenshot data missing", result);
|
|
1756
2354
|
return imageResult(result.screenshot, { tab_id: result.tab_id ?? null });
|
|
@@ -1767,7 +2365,7 @@ var tools = [
|
|
|
1767
2365
|
annotations: { readOnlyHint: true },
|
|
1768
2366
|
handler: async (args) => {
|
|
1769
2367
|
await ensureServerReady();
|
|
1770
|
-
return successResult(await
|
|
2368
|
+
return successResult(await api2("GET", "/v1/browse/text", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined), "Current page text.");
|
|
1771
2369
|
}
|
|
1772
2370
|
},
|
|
1773
2371
|
{
|
|
@@ -1781,7 +2379,7 @@ var tools = [
|
|
|
1781
2379
|
annotations: { readOnlyHint: true },
|
|
1782
2380
|
handler: async (args) => {
|
|
1783
2381
|
await ensureServerReady();
|
|
1784
|
-
return successResult(await
|
|
2382
|
+
return successResult(await api2("GET", "/v1/browse/markdown", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined), "Current page markdown.");
|
|
1785
2383
|
}
|
|
1786
2384
|
},
|
|
1787
2385
|
{
|
|
@@ -1795,7 +2393,7 @@ var tools = [
|
|
|
1795
2393
|
annotations: { readOnlyHint: true },
|
|
1796
2394
|
handler: async (args) => {
|
|
1797
2395
|
await ensureServerReady();
|
|
1798
|
-
return successResult(await
|
|
2396
|
+
return successResult(await api2("GET", "/v1/browse/cookies", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined), "Current page cookies.");
|
|
1799
2397
|
}
|
|
1800
2398
|
},
|
|
1801
2399
|
{
|
|
@@ -1813,7 +2411,7 @@ var tools = [
|
|
|
1813
2411
|
annotations: { destructiveHint: true },
|
|
1814
2412
|
handler: async (args) => {
|
|
1815
2413
|
await ensureServerReady();
|
|
1816
|
-
return successResult(await
|
|
2414
|
+
return successResult(await api2("POST", "/v1/browse/eval", {
|
|
1817
2415
|
expression: args.expression,
|
|
1818
2416
|
...typeof args.session_id === "string" ? { session_id: args.session_id } : {}
|
|
1819
2417
|
}), "JavaScript evaluation result.");
|
|
@@ -1830,7 +2428,7 @@ var tools = [
|
|
|
1830
2428
|
annotations: { destructiveHint: true },
|
|
1831
2429
|
handler: async (args) => {
|
|
1832
2430
|
await ensureServerReady();
|
|
1833
|
-
const result = await
|
|
2431
|
+
const result = await api2("POST", "/v1/browse/sync", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined);
|
|
1834
2432
|
const withHints = addCaptureNextStepHints(result, args);
|
|
1835
2433
|
return successResult(withHints, "Capture checkpoint recorded. See _workflow_hints for required next steps: call unbrowse_review then unbrowse_publish.");
|
|
1836
2434
|
}
|
|
@@ -1846,7 +2444,7 @@ var tools = [
|
|
|
1846
2444
|
annotations: { destructiveHint: true },
|
|
1847
2445
|
handler: async (args) => {
|
|
1848
2446
|
await ensureServerReady();
|
|
1849
|
-
const result = await
|
|
2447
|
+
const result = await api2("POST", "/v1/browse/close", typeof args.session_id === "string" ? { session_id: args.session_id } : undefined);
|
|
1850
2448
|
const withHints = addCaptureNextStepHints(result, args);
|
|
1851
2449
|
return successResult(withHints, "Browse session closed. See _workflow_hints for required next steps: call unbrowse_review then unbrowse_publish.");
|
|
1852
2450
|
}
|