unbrowse 2.9.1 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +275 -34
- package/package.json +1 -1
- package/runtime-src/analytics-session.ts +33 -0
- package/runtime-src/api/routes.ts +59 -8
- package/runtime-src/auth/index.ts +44 -6
- package/runtime-src/client/index.ts +143 -9
- package/runtime-src/execution/index.ts +2 -1
- package/runtime-src/orchestrator/index.ts +15 -1
- package/runtime-src/payments/cascade.ts +137 -0
- package/runtime-src/types/skill.ts +18 -0
package/dist/cli.js
CHANGED
|
@@ -30,6 +30,83 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
30
30
|
var __promiseAll = (args) => Promise.all(args);
|
|
31
31
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
32
32
|
|
|
33
|
+
// ../../src/payments/cascade.ts
|
|
34
|
+
import { createHash } from "crypto";
|
|
35
|
+
import bs58 from "bs58";
|
|
36
|
+
function payableContributors(skill) {
|
|
37
|
+
return (skill.contributors ?? []).filter((c) => !!c.wallet_address?.trim());
|
|
38
|
+
}
|
|
39
|
+
function cascadeLabel(skillId) {
|
|
40
|
+
const digest = createHash("sha256").update(skillId).digest("hex");
|
|
41
|
+
return `ubr-${digest.slice(0, 23)}`;
|
|
42
|
+
}
|
|
43
|
+
function decodeSecretKey(raw) {
|
|
44
|
+
const trimmed = raw.trim();
|
|
45
|
+
if (!trimmed)
|
|
46
|
+
throw new Error("empty signer secret");
|
|
47
|
+
if (trimmed.startsWith("[")) {
|
|
48
|
+
return Uint8Array.from(JSON.parse(trimmed));
|
|
49
|
+
}
|
|
50
|
+
if (/^[1-9A-HJ-NP-Za-km-z]+$/.test(trimmed)) {
|
|
51
|
+
return Uint8Array.from(bs58.decode(trimmed));
|
|
52
|
+
}
|
|
53
|
+
return Uint8Array.from(Buffer.from(trimmed, "base64"));
|
|
54
|
+
}
|
|
55
|
+
function recipientsForSkill(skill, platformWallet) {
|
|
56
|
+
return [
|
|
57
|
+
{ address: platformWallet, share: 10 },
|
|
58
|
+
...payableContributors(skill).map((c) => ({
|
|
59
|
+
address: c.wallet_address.trim(),
|
|
60
|
+
share: c.share
|
|
61
|
+
}))
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
async function ensureCascadeSplitForSkill(skill, deps = {}) {
|
|
65
|
+
const env = deps.env ?? process.env;
|
|
66
|
+
if (skill.split_config?.trim()) {
|
|
67
|
+
return { split_config: skill.split_config.trim(), source: "existing" };
|
|
68
|
+
}
|
|
69
|
+
const contributors = payableContributors(skill);
|
|
70
|
+
if (contributors.length <= 1)
|
|
71
|
+
return {};
|
|
72
|
+
const explicitSplitConfig = env.UNBROWSE_CASCADE_SPLIT_ADDRESS?.trim() || env.UNBROWSE_CASCADE_SPLIT_CONFIG?.trim();
|
|
73
|
+
if (explicitSplitConfig) {
|
|
74
|
+
return { split_config: explicitSplitConfig, source: "env" };
|
|
75
|
+
}
|
|
76
|
+
const platformWallet = env.UNBROWSE_CASCADE_PLATFORM_WALLET?.trim() || env.PAYMENT_RECIPIENT?.trim();
|
|
77
|
+
const secretKey = env.UNBROWSE_CASCADE_SIGNER_SECRET_KEY?.trim();
|
|
78
|
+
const rpcUrl = env.UNBROWSE_CASCADE_RPC_URL?.trim();
|
|
79
|
+
const rpcWsUrl = env.UNBROWSE_CASCADE_RPC_WS_URL?.trim();
|
|
80
|
+
if (!platformWallet || !secretKey || !rpcUrl || !rpcWsUrl) {
|
|
81
|
+
return {
|
|
82
|
+
warning: "cascade_split_not_configured"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const loadSdk = deps.loadSdk ?? (async () => await import("@cascade-fyi/splits-sdk"));
|
|
86
|
+
const loadKit = deps.loadKit ?? (async () => await import("@solana/kit"));
|
|
87
|
+
const [sdk, kit] = await Promise.all([loadSdk(), loadKit()]);
|
|
88
|
+
const signer = await kit.createKeyPairSignerFromBytes(decodeSecretKey(secretKey));
|
|
89
|
+
const splits = sdk.createSplitsClient({
|
|
90
|
+
rpc: kit.createSolanaRpc(rpcUrl),
|
|
91
|
+
rpcSubscriptions: kit.createSolanaRpcSubscriptions(rpcWsUrl),
|
|
92
|
+
signer
|
|
93
|
+
});
|
|
94
|
+
const result = await splits.ensureSplit({
|
|
95
|
+
recipients: recipientsForSkill(skill, platformWallet),
|
|
96
|
+
uniqueId: sdk.labelToSeed(cascadeLabel(skill.skill_id))
|
|
97
|
+
});
|
|
98
|
+
if (result.status === "created" || result.status === "updated" || result.status === "no_change") {
|
|
99
|
+
return {
|
|
100
|
+
split_config: result.splitConfig,
|
|
101
|
+
source: "sdk"
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
warning: result.message || `cascade_split_${result.status}`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
var init_cascade = () => {};
|
|
109
|
+
|
|
33
110
|
// ../../src/runtime/lifecycle.ts
|
|
34
111
|
function attributeLifecycle(events) {
|
|
35
112
|
const totals = new Map;
|
|
@@ -4645,6 +4722,8 @@ __export(exports_client2, {
|
|
|
4645
4722
|
validateManifest: () => validateManifest,
|
|
4646
4723
|
updateEndpointScore: () => updateEndpointScore,
|
|
4647
4724
|
updateEndpointSchema: () => updateEndpointSchema,
|
|
4725
|
+
syncAgentWallet: () => syncAgentWallet2,
|
|
4726
|
+
setSkillSplitConfig: () => setSkillSplitConfig,
|
|
4648
4727
|
setSkillPrice: () => setSkillPrice,
|
|
4649
4728
|
searchIntentResolve: () => searchIntentResolve,
|
|
4650
4729
|
searchIntentInDomain: () => searchIntentInDomain,
|
|
@@ -4656,6 +4735,7 @@ __export(exports_client2, {
|
|
|
4656
4735
|
recordFeedback: () => recordFeedback,
|
|
4657
4736
|
recordExecution: () => recordExecution,
|
|
4658
4737
|
recordDiagnostics: () => recordDiagnostics,
|
|
4738
|
+
recordAnalyticsSession: () => recordAnalyticsSession,
|
|
4659
4739
|
publishSkill: () => publishSkill,
|
|
4660
4740
|
publishGraphEdges: () => publishGraphEdges,
|
|
4661
4741
|
normalizeAgentEmail: () => normalizeAgentEmail2,
|
|
@@ -4668,7 +4748,7 @@ __export(exports_client2, {
|
|
|
4668
4748
|
getSkillChunk: () => getSkillChunk2,
|
|
4669
4749
|
getSkill: () => getSkill,
|
|
4670
4750
|
getRecentLocalSkill: () => getRecentLocalSkill,
|
|
4671
|
-
getMyProfile: () =>
|
|
4751
|
+
getMyProfile: () => getMyProfile2,
|
|
4672
4752
|
getEndpointSchema: () => getEndpointSchema,
|
|
4673
4753
|
getCreatorEarnings: () => getCreatorEarnings,
|
|
4674
4754
|
getApiKey: () => getApiKey2,
|
|
@@ -4686,7 +4766,7 @@ __export(exports_client2, {
|
|
|
4686
4766
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
|
|
4687
4767
|
import { join as join3 } from "path";
|
|
4688
4768
|
import { homedir as homedir2, hostname as hostname2 } from "os";
|
|
4689
|
-
import { randomBytes as randomBytes2, createHash as
|
|
4769
|
+
import { randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
|
|
4690
4770
|
import { createInterface as createInterface2 } from "readline";
|
|
4691
4771
|
function decodeBase64Json2(value) {
|
|
4692
4772
|
try {
|
|
@@ -4758,6 +4838,20 @@ function resolveAgentName2(preferredEmail, fallbackName) {
|
|
|
4758
4838
|
const normalized = normalizeAgentEmail2(preferredEmail ?? "");
|
|
4759
4839
|
return isValidAgentEmail2(normalized) ? normalized : fallbackName;
|
|
4760
4840
|
}
|
|
4841
|
+
function getLocalWalletContext2() {
|
|
4842
|
+
const lobsterWallet = process.env.LOBSTER_WALLET_ADDRESS?.trim();
|
|
4843
|
+
if (lobsterWallet) {
|
|
4844
|
+
return { wallet_address: lobsterWallet, wallet_provider: "lobster.cash" };
|
|
4845
|
+
}
|
|
4846
|
+
const genericWallet = process.env.AGENT_WALLET_ADDRESS?.trim();
|
|
4847
|
+
if (genericWallet) {
|
|
4848
|
+
return {
|
|
4849
|
+
wallet_address: genericWallet,
|
|
4850
|
+
wallet_provider: process.env.AGENT_WALLET_PROVIDER?.trim() || undefined
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4853
|
+
return {};
|
|
4854
|
+
}
|
|
4761
4855
|
function getApiKey2() {
|
|
4762
4856
|
if (LOCAL_ONLY2)
|
|
4763
4857
|
return "local-only";
|
|
@@ -4773,7 +4867,7 @@ function getApiKey2() {
|
|
|
4773
4867
|
function hashApiKey(key) {
|
|
4774
4868
|
if (!key || key === "local-only")
|
|
4775
4869
|
return "";
|
|
4776
|
-
return
|
|
4870
|
+
return createHash3("sha256").update(key).digest("hex");
|
|
4777
4871
|
}
|
|
4778
4872
|
function getAgentId() {
|
|
4779
4873
|
const config = loadConfig2();
|
|
@@ -4836,7 +4930,7 @@ async function findUsableApiKey2() {
|
|
|
4836
4930
|
}
|
|
4837
4931
|
return null;
|
|
4838
4932
|
}
|
|
4839
|
-
async function
|
|
4933
|
+
async function apiRequest2(method, path4, body, opts) {
|
|
4840
4934
|
const key = opts?.noAuth ? "" : getApiKey2();
|
|
4841
4935
|
const controller = new AbortController;
|
|
4842
4936
|
const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? API_TIMEOUT_MS2);
|
|
@@ -4882,6 +4976,10 @@ async function api2(method, path4, body, opts) {
|
|
|
4882
4976
|
const msg = errData.details?.length ? `${errData.error}: ${errData.details.join("; ")}` : errData.error ?? `API HTTP ${res.status}`;
|
|
4883
4977
|
throw new Error(msg);
|
|
4884
4978
|
}
|
|
4979
|
+
return { data, headers: res.headers };
|
|
4980
|
+
}
|
|
4981
|
+
async function api2(method, path4, body, opts) {
|
|
4982
|
+
const { data } = await apiRequest2(method, path4, body, opts);
|
|
4885
4983
|
return data;
|
|
4886
4984
|
}
|
|
4887
4985
|
async function promptTosAcceptance2(summary, tosUrl) {
|
|
@@ -4979,6 +5077,13 @@ async function ensureRegistered2(options) {
|
|
|
4979
5077
|
console.log("[unbrowse] Restored saved registration.");
|
|
4980
5078
|
}
|
|
4981
5079
|
await checkTosStatus2();
|
|
5080
|
+
try {
|
|
5081
|
+
const profile = await getMyProfile2();
|
|
5082
|
+
const wallet = getLocalWalletContext2();
|
|
5083
|
+
if (wallet.wallet_address && profile.wallet_address !== wallet.wallet_address) {
|
|
5084
|
+
await syncAgentWallet2(wallet);
|
|
5085
|
+
}
|
|
5086
|
+
} catch {}
|
|
4982
5087
|
return;
|
|
4983
5088
|
}
|
|
4984
5089
|
let tosInfo;
|
|
@@ -4998,7 +5103,8 @@ async function ensureRegistered2(options) {
|
|
|
4998
5103
|
const name = options?.promptForEmail ? await promptAgentEmail2(fallbackName) : resolveAgentName2(process.env.UNBROWSE_AGENT_EMAIL, fallbackName);
|
|
4999
5104
|
console.log(`Registering as "${name}"...`);
|
|
5000
5105
|
try {
|
|
5001
|
-
const
|
|
5106
|
+
const wallet = getLocalWalletContext2();
|
|
5107
|
+
const { agent_id, api_key } = await api2("POST", "/v1/agents/register", { name, tos_version: tosInfo.version, ...wallet });
|
|
5002
5108
|
process.env.UNBROWSE_API_KEY = api_key;
|
|
5003
5109
|
saveConfig2({
|
|
5004
5110
|
api_key,
|
|
@@ -5006,7 +5112,8 @@ async function ensureRegistered2(options) {
|
|
|
5006
5112
|
agent_name: name,
|
|
5007
5113
|
registered_at: new Date().toISOString(),
|
|
5008
5114
|
tos_accepted_version: tosInfo.version,
|
|
5009
|
-
tos_accepted_at: new Date().toISOString()
|
|
5115
|
+
tos_accepted_at: new Date().toISOString(),
|
|
5116
|
+
...wallet
|
|
5010
5117
|
});
|
|
5011
5118
|
console.log(`Registered as ${name}. API key saved to ~/.unbrowse/config.json`);
|
|
5012
5119
|
} catch (err) {
|
|
@@ -5162,7 +5269,27 @@ async function publishSkill(draft) {
|
|
|
5162
5269
|
}
|
|
5163
5270
|
if (LOCAL_ONLY2)
|
|
5164
5271
|
throw new Error("local-only mode");
|
|
5165
|
-
|
|
5272
|
+
const wallet = getLocalWalletContext2();
|
|
5273
|
+
const published = await api2("POST", "/v1/skills", {
|
|
5274
|
+
...draft,
|
|
5275
|
+
...wallet.wallet_address ? wallet : {}
|
|
5276
|
+
}, { timeoutMs: PUBLISH_TIMEOUT_MS2 });
|
|
5277
|
+
const cascade = await ensureCascadeSplitForSkill(published).catch((err) => ({
|
|
5278
|
+
warning: `cascade_split_failed:${err.message}`
|
|
5279
|
+
}));
|
|
5280
|
+
const warnings = [...published.warnings ?? []];
|
|
5281
|
+
if (cascade.warning)
|
|
5282
|
+
warnings.push(cascade.warning);
|
|
5283
|
+
if (cascade.split_config && cascade.split_config !== published.split_config) {
|
|
5284
|
+
const updated = await api2("PATCH", `/v1/skills/${published.skill_id}`, {
|
|
5285
|
+
split_config: cascade.split_config
|
|
5286
|
+
});
|
|
5287
|
+
return {
|
|
5288
|
+
...updated,
|
|
5289
|
+
warnings
|
|
5290
|
+
};
|
|
5291
|
+
}
|
|
5292
|
+
return { ...published, warnings };
|
|
5166
5293
|
}
|
|
5167
5294
|
async function deprecateSkill(skillId) {
|
|
5168
5295
|
if (LOCAL_ONLY2)
|
|
@@ -5204,12 +5331,15 @@ async function searchIntentResolve(intent, domain, domainK = 5, globalK = 10) {
|
|
|
5204
5331
|
if (LOCAL_ONLY2)
|
|
5205
5332
|
return { domain_results: [], global_results: [], skipped_global: false };
|
|
5206
5333
|
try {
|
|
5207
|
-
|
|
5334
|
+
const { data, headers } = await apiRequest2("POST", "/v1/search/resolve", {
|
|
5208
5335
|
intent,
|
|
5209
5336
|
domain,
|
|
5210
5337
|
domain_k: domainK,
|
|
5211
5338
|
global_k: globalK
|
|
5212
5339
|
});
|
|
5340
|
+
const actualCostHeader = headers.get("X-Unbrowse-Cost-Uc");
|
|
5341
|
+
const actualCostUc = actualCostHeader && /^\d+$/.test(actualCostHeader) ? Number(actualCostHeader) : undefined;
|
|
5342
|
+
return actualCostUc != null ? { ...data, actual_cost_uc: actualCostUc } : data;
|
|
5213
5343
|
} catch (err) {
|
|
5214
5344
|
if (isX402Error(err))
|
|
5215
5345
|
throw err;
|
|
@@ -5288,6 +5418,11 @@ async function recordOrchestrationPerf(timing) {
|
|
|
5288
5418
|
const phaseTotals = Object.fromEntries(attributeLifecycle(events));
|
|
5289
5419
|
await api2("POST", "/v1/stats/perf", { ...timing, phase_totals_ms: phaseTotals });
|
|
5290
5420
|
}
|
|
5421
|
+
async function recordAnalyticsSession(session) {
|
|
5422
|
+
if (LOCAL_ONLY2)
|
|
5423
|
+
return;
|
|
5424
|
+
await api2("POST", "/v1/analytics/sessions", session);
|
|
5425
|
+
}
|
|
5291
5426
|
async function validateManifest(manifest) {
|
|
5292
5427
|
if (LOCAL_ONLY2)
|
|
5293
5428
|
return { valid: true, hardErrors: [], softWarnings: [] };
|
|
@@ -5342,8 +5477,8 @@ async function verifyMarketplaceDiscovery(skillId, intent, maxWaitMs = 60000) {
|
|
|
5342
5477
|
}
|
|
5343
5478
|
return { found: false, latency_ms: Date.now() - start2 };
|
|
5344
5479
|
}
|
|
5345
|
-
async function registerAgent(name) {
|
|
5346
|
-
return api2("POST", "/v1/agents/register", { name });
|
|
5480
|
+
async function registerAgent(name, wallet = getLocalWalletContext2()) {
|
|
5481
|
+
return api2("POST", "/v1/agents/register", { name, ...wallet });
|
|
5347
5482
|
}
|
|
5348
5483
|
async function getAgent(agentId) {
|
|
5349
5484
|
try {
|
|
@@ -5352,9 +5487,18 @@ async function getAgent(agentId) {
|
|
|
5352
5487
|
return null;
|
|
5353
5488
|
}
|
|
5354
5489
|
}
|
|
5355
|
-
async function
|
|
5490
|
+
async function getMyProfile2() {
|
|
5356
5491
|
return api2("GET", "/v1/agents/me", undefined);
|
|
5357
5492
|
}
|
|
5493
|
+
async function syncAgentWallet2(wallet = getLocalWalletContext2()) {
|
|
5494
|
+
if (!wallet.wallet_address)
|
|
5495
|
+
return;
|
|
5496
|
+
await api2("POST", "/v1/agents/wallet", wallet);
|
|
5497
|
+
const config = loadConfig2();
|
|
5498
|
+
if (!config)
|
|
5499
|
+
return;
|
|
5500
|
+
saveConfig2({ ...config, ...wallet });
|
|
5501
|
+
}
|
|
5358
5502
|
async function getTransactionHistory(agentId) {
|
|
5359
5503
|
return api2("GET", `/v1/transactions/consumer/${agentId}`);
|
|
5360
5504
|
}
|
|
@@ -5364,8 +5508,12 @@ async function getCreatorEarnings(agentId) {
|
|
|
5364
5508
|
async function setSkillPrice(skillId, priceUsd) {
|
|
5365
5509
|
return api2("PATCH", `/v1/skills/${skillId}`, { base_price_usd: priceUsd });
|
|
5366
5510
|
}
|
|
5511
|
+
async function setSkillSplitConfig(skillId, splitConfig) {
|
|
5512
|
+
return api2("PATCH", `/v1/skills/${skillId}`, { split_config: splitConfig });
|
|
5513
|
+
}
|
|
5367
5514
|
var API_URL2, PROFILE_NAME2, recentLocalSkills2, LOCAL_ONLY2, EMAIL_RE2, API_TIMEOUT_MS2, PUBLISH_TIMEOUT_MS2;
|
|
5368
5515
|
var init_client2 = __esm(() => {
|
|
5516
|
+
init_cascade();
|
|
5369
5517
|
API_URL2 = process.env.UNBROWSE_BACKEND_URL || "https://beta-api.unbrowse.ai";
|
|
5370
5518
|
PROFILE_NAME2 = sanitizeProfileName2(process.env.UNBROWSE_PROFILE ?? "");
|
|
5371
5519
|
recentLocalSkills2 = new Map;
|
|
@@ -5449,7 +5597,7 @@ var init_marketplace = __esm(() => {
|
|
|
5449
5597
|
});
|
|
5450
5598
|
|
|
5451
5599
|
// ../../src/version.ts
|
|
5452
|
-
import { createHash as
|
|
5600
|
+
import { createHash as createHash4 } from "crypto";
|
|
5453
5601
|
import { readFileSync as readFileSync3, readdirSync as readdirSync3 } from "fs";
|
|
5454
5602
|
import { dirname, join as join4 } from "path";
|
|
5455
5603
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -5469,7 +5617,7 @@ function computeCodeHash() {
|
|
|
5469
5617
|
try {
|
|
5470
5618
|
const srcDir = join4(MODULE_DIR, ".");
|
|
5471
5619
|
const files = collectTsFiles(srcDir).sort();
|
|
5472
|
-
const hash =
|
|
5620
|
+
const hash = createHash4("sha256");
|
|
5473
5621
|
for (const file of files) {
|
|
5474
5622
|
hash.update(file.slice(srcDir.length));
|
|
5475
5623
|
hash.update(readFileSync3(file, "utf-8"));
|
|
@@ -5491,7 +5639,7 @@ var init_version = __esm(() => {
|
|
|
5491
5639
|
});
|
|
5492
5640
|
|
|
5493
5641
|
// ../../src/telemetry.ts
|
|
5494
|
-
import { createHash as
|
|
5642
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5495
5643
|
import { existsSync as existsSync5, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5496
5644
|
import { join as join5 } from "node:path";
|
|
5497
5645
|
function getTraceDir() {
|
|
@@ -5508,7 +5656,7 @@ function safeBindingNames(bindings) {
|
|
|
5508
5656
|
}
|
|
5509
5657
|
function hashResponseBody(result) {
|
|
5510
5658
|
const str = typeof result === "string" ? result : JSON.stringify(result ?? "");
|
|
5511
|
-
return
|
|
5659
|
+
return createHash5("sha256").update(str).digest("hex").slice(0, 32);
|
|
5512
5660
|
}
|
|
5513
5661
|
function classifyFailure(error) {
|
|
5514
5662
|
if (!error)
|
|
@@ -6321,6 +6469,10 @@ function filterExpired(cookies) {
|
|
|
6321
6469
|
});
|
|
6322
6470
|
}
|
|
6323
6471
|
async function getStoredAuth(domain) {
|
|
6472
|
+
const bundle = await getStoredAuthBundle(domain);
|
|
6473
|
+
return bundle?.cookies?.length ? bundle.cookies : null;
|
|
6474
|
+
}
|
|
6475
|
+
async function getStoredAuthBundle(domain) {
|
|
6324
6476
|
const regDomain = getRegistrableDomain(domain);
|
|
6325
6477
|
const keysToTry = [`auth:${regDomain}`];
|
|
6326
6478
|
if (domain !== regDomain)
|
|
@@ -6331,11 +6483,9 @@ async function getStoredAuth(domain) {
|
|
|
6331
6483
|
continue;
|
|
6332
6484
|
try {
|
|
6333
6485
|
const parsed = JSON.parse(stored);
|
|
6334
|
-
const cookies = parsed.cookies;
|
|
6335
|
-
if (!cookies || cookies.length === 0)
|
|
6336
|
-
continue;
|
|
6486
|
+
const cookies = parsed.cookies ?? [];
|
|
6337
6487
|
const valid = filterExpired(cookies);
|
|
6338
|
-
if (valid.length === 0) {
|
|
6488
|
+
if (cookies.length > 0 && valid.length === 0 && Object.keys(parsed.headers ?? {}).length === 0) {
|
|
6339
6489
|
log("auth", `all ${cookies.length} cookies for ${domain} (key: ${key}) are expired — deleting`);
|
|
6340
6490
|
await deleteCredential(key);
|
|
6341
6491
|
continue;
|
|
@@ -6343,7 +6493,12 @@ async function getStoredAuth(domain) {
|
|
|
6343
6493
|
if (valid.length < cookies.length) {
|
|
6344
6494
|
log("auth", `filtered ${cookies.length - valid.length} expired cookies for ${domain}`);
|
|
6345
6495
|
}
|
|
6346
|
-
return
|
|
6496
|
+
return {
|
|
6497
|
+
cookies: valid,
|
|
6498
|
+
headers: parsed.headers ?? {},
|
|
6499
|
+
source_keys: parsed.source_keys ?? [],
|
|
6500
|
+
source_meta: parsed.source_meta ?? null
|
|
6501
|
+
};
|
|
6347
6502
|
} catch {
|
|
6348
6503
|
continue;
|
|
6349
6504
|
}
|
|
@@ -11424,7 +11579,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
11424
11579
|
} catch {}
|
|
11425
11580
|
}
|
|
11426
11581
|
recordExecution(skill.skill_id, endpoint.endpoint_id, trace, skill).catch(() => {});
|
|
11427
|
-
if (trace.success &&
|
|
11582
|
+
if (trace.success && options?.payment_verified === true && skill.base_price_usd && skill.base_price_usd > 0) {
|
|
11428
11583
|
const consumerConfig = (() => {
|
|
11429
11584
|
try {
|
|
11430
11585
|
return JSON.parse(__require("fs").readFileSync(__require("os").homedir() + "/.unbrowse/config.json", "utf-8"));
|
|
@@ -12734,7 +12889,7 @@ var init_prefetch = __esm(async () => {
|
|
|
12734
12889
|
});
|
|
12735
12890
|
|
|
12736
12891
|
// ../../src/orchestrator/first-pass-action.ts
|
|
12737
|
-
import { createHash as
|
|
12892
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
12738
12893
|
function classifyIntent(intent) {
|
|
12739
12894
|
if (/\b(search|find|discover|look\s+for|browse)\b/i.test(intent))
|
|
12740
12895
|
return "search";
|
|
@@ -12781,7 +12936,7 @@ function filterJsonApiEntries(entries) {
|
|
|
12781
12936
|
function synthesizeSkillFromIntercepted(interceptedEntries, domain, intent) {
|
|
12782
12937
|
if (interceptedEntries.length === 0)
|
|
12783
12938
|
return;
|
|
12784
|
-
const hash =
|
|
12939
|
+
const hash = createHash6("sha1").update(interceptedEntries.map((e) => e.request.url).join("|")).digest("hex").slice(0, 8);
|
|
12785
12940
|
const endpoints = interceptedEntries.map((entry, i) => {
|
|
12786
12941
|
const parsed = new URL(entry.request.url);
|
|
12787
12942
|
return {
|
|
@@ -13005,7 +13160,7 @@ function checkWalletConfigured() {
|
|
|
13005
13160
|
import { nanoid as nanoid7 } from "nanoid";
|
|
13006
13161
|
import { existsSync as existsSync9, writeFileSync as writeFileSync7, readFileSync as readFileSync6, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
13007
13162
|
import { dirname as dirname2, join as join9 } from "node:path";
|
|
13008
|
-
import { createHash as
|
|
13163
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
13009
13164
|
function summarizeSchema(schema, maxDepth = 3) {
|
|
13010
13165
|
function walk(s, depth) {
|
|
13011
13166
|
if (depth <= 0)
|
|
@@ -13120,7 +13275,7 @@ function scopedResolveCacheKeys(scope, key) {
|
|
|
13120
13275
|
return scope === "global" ? [scopedCacheKey("global", key)] : [scopedCacheKey(scope, key), scopedCacheKey("global", key)];
|
|
13121
13276
|
}
|
|
13122
13277
|
function snapshotPathForCacheKey(cacheKey) {
|
|
13123
|
-
const digest =
|
|
13278
|
+
const digest = createHash7("sha1").update(cacheKey).digest("hex");
|
|
13124
13279
|
return join9(SKILL_SNAPSHOT_DIR, `${digest}.json`);
|
|
13125
13280
|
}
|
|
13126
13281
|
function writeSkillSnapshot(cacheKey, skill) {
|
|
@@ -14224,6 +14379,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14224
14379
|
response_bytes: 0,
|
|
14225
14380
|
time_saved_pct: 0,
|
|
14226
14381
|
tokens_saved_pct: 0,
|
|
14382
|
+
actual_total_ms: 0,
|
|
14227
14383
|
trace_version: TRACE_VERSION
|
|
14228
14384
|
};
|
|
14229
14385
|
const decisionTrace = {
|
|
@@ -14259,6 +14415,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14259
14415
|
}
|
|
14260
14416
|
function finalize(source, result2, skillId, skill, trace2) {
|
|
14261
14417
|
timing.total_ms = Date.now() - t0;
|
|
14418
|
+
timing.actual_total_ms = timing.total_ms;
|
|
14262
14419
|
timing.source = source;
|
|
14263
14420
|
timing.skill_id = skillId;
|
|
14264
14421
|
const resultStr = typeof result2 === "string" ? result2 : JSON.stringify(result2 ?? "");
|
|
@@ -14267,11 +14424,20 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14267
14424
|
const cost = skill?.discovery_cost;
|
|
14268
14425
|
const baselineTokens = cost?.capture_tokens ?? DEFAULT_CAPTURE_TOKENS;
|
|
14269
14426
|
const baselineMs = cost?.capture_ms ?? DEFAULT_CAPTURE_MS;
|
|
14427
|
+
const paidSearchUc = timing.paid_search_uc ?? 0;
|
|
14428
|
+
const paidExecutionUc = timing.paid_execution_uc ?? 0;
|
|
14429
|
+
const totalActualCostUc = paidSearchUc + paidExecutionUc;
|
|
14430
|
+
if (totalActualCostUc > 0)
|
|
14431
|
+
timing.actual_cost_uc = totalActualCostUc;
|
|
14270
14432
|
if (source === "marketplace" || source === "route-cache" || source === "first-pass") {
|
|
14271
14433
|
timing.tokens_saved = Math.max(0, baselineTokens - responseTokens);
|
|
14272
14434
|
timing.tokens_saved_pct = baselineTokens > 0 ? Math.round(timing.tokens_saved / baselineTokens * 100) : 0;
|
|
14273
14435
|
timing.time_saved_pct = baselineMs > 0 ? Math.round(Math.max(0, baselineMs - timing.total_ms) / baselineMs * 100) : 0;
|
|
14274
14436
|
}
|
|
14437
|
+
if (cost?.capture_ms != null) {
|
|
14438
|
+
timing.baseline_total_ms = cost.capture_ms;
|
|
14439
|
+
timing.time_saved_ms = Math.max(0, cost.capture_ms - timing.total_ms);
|
|
14440
|
+
}
|
|
14275
14441
|
if (trace2) {
|
|
14276
14442
|
trace2.tokens_used = responseTokens;
|
|
14277
14443
|
trace2.tokens_saved = timing.tokens_saved;
|
|
@@ -15175,6 +15341,9 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15175
15341
|
skipped_global: false
|
|
15176
15342
|
};
|
|
15177
15343
|
}
|
|
15344
|
+
if (typeof searchResponse.actual_cost_uc === "number" && searchResponse.actual_cost_uc > 0) {
|
|
15345
|
+
timing.paid_search_uc = searchResponse.actual_cost_uc;
|
|
15346
|
+
}
|
|
15178
15347
|
const { domain_results: domainResults, global_results: globalResults } = searchResponse;
|
|
15179
15348
|
timing.search_ms = Date.now() - ts0;
|
|
15180
15349
|
console.log(`[marketplace] search: ${domainResults.length} domain + ${globalResults.length} global results (${timing.search_ms}ms)`);
|
|
@@ -16499,11 +16668,29 @@ var init_verification = __esm(() => {
|
|
|
16499
16668
|
var exports_routes = {};
|
|
16500
16669
|
__export(exports_routes, {
|
|
16501
16670
|
registerRoutes: () => registerRoutes,
|
|
16502
|
-
registerBrowseSession: () => registerBrowseSession
|
|
16671
|
+
registerBrowseSession: () => registerBrowseSession,
|
|
16672
|
+
buildAnalyticsSessionPayload: () => buildAnalyticsSessionPayload
|
|
16503
16673
|
});
|
|
16504
16674
|
import { nanoid as nanoid8 } from "nanoid";
|
|
16505
16675
|
import { writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
|
|
16506
16676
|
import { join as join12 } from "path";
|
|
16677
|
+
function buildAnalyticsSessionPayload(result, opts) {
|
|
16678
|
+
const source = result.timing?.source ?? result.source;
|
|
16679
|
+
const apiCalls = result.trace.endpoint_id ? 1 : 0;
|
|
16680
|
+
const cachedSkillCalls = opts.cached_skill_calls ?? (apiCalls > 0 && source !== "live-capture" && source !== "first-pass" ? 1 : 0);
|
|
16681
|
+
const freshIndexCalls = opts.fresh_index_calls ?? (apiCalls > 0 && (source === "live-capture" || source === "first-pass") ? 1 : 0);
|
|
16682
|
+
return {
|
|
16683
|
+
session_id: result.trace.trace_id,
|
|
16684
|
+
started_at: result.trace.started_at,
|
|
16685
|
+
completed_at: result.trace.completed_at,
|
|
16686
|
+
trace_version: result.trace.trace_version ?? TRACE_VERSION,
|
|
16687
|
+
api_calls: apiCalls,
|
|
16688
|
+
discovery_queries: opts.discovery_queries,
|
|
16689
|
+
cached_skill_calls: cachedSkillCalls,
|
|
16690
|
+
fresh_index_calls: freshIndexCalls,
|
|
16691
|
+
browser_mode: opts.browser_mode ?? "unknown"
|
|
16692
|
+
};
|
|
16693
|
+
}
|
|
16507
16694
|
function harEntriesToRawRequests(entries) {
|
|
16508
16695
|
return entries.filter((e) => e.request && e.response).map((e) => ({
|
|
16509
16696
|
url: e.request.url,
|
|
@@ -16605,11 +16792,11 @@ async function fetchStats() {
|
|
|
16605
16792
|
const npmRange = (pkg) => fetch(`https://api.npmjs.org/downloads/range/last-month/${pkg}`).then((r) => r.json());
|
|
16606
16793
|
const externalCalls = [
|
|
16607
16794
|
npmPoint("unbrowse", "last-month"),
|
|
16608
|
-
npmPoint("
|
|
16795
|
+
npmPoint("unbrowse-openclaw", "last-month"),
|
|
16609
16796
|
npmPoint("unbrowse", "1970-01-01:2099-12-31"),
|
|
16610
|
-
npmPoint("
|
|
16797
|
+
npmPoint("unbrowse-openclaw", "1970-01-01:2099-12-31"),
|
|
16611
16798
|
npmRange("unbrowse"),
|
|
16612
|
-
npmRange("
|
|
16799
|
+
npmRange("unbrowse-openclaw"),
|
|
16613
16800
|
fetch("https://api.github.com/repos/anthropic-ai/unbrowse", {
|
|
16614
16801
|
headers: { "User-Agent": "unbrowse-stats" }
|
|
16615
16802
|
}).then((r) => r.json())
|
|
@@ -16718,6 +16905,10 @@ async function registerRoutes(app) {
|
|
|
16718
16905
|
if (innerResult?.available_endpoints && !res.available_endpoints) {
|
|
16719
16906
|
res.available_endpoints = innerResult.available_endpoints;
|
|
16720
16907
|
}
|
|
16908
|
+
await recordAnalyticsSession(buildAnalyticsSessionPayload(result, {
|
|
16909
|
+
browser_mode: "replaced",
|
|
16910
|
+
discovery_queries: 1
|
|
16911
|
+
})).catch(() => {});
|
|
16721
16912
|
return reply.send(result);
|
|
16722
16913
|
} catch (err) {
|
|
16723
16914
|
return reply.code(500).send({ error: err.message });
|
|
@@ -16927,6 +17118,10 @@ async function registerRoutes(app) {
|
|
|
16927
17118
|
if (freshResult.trace?.skill_id && freshResult.trace?.endpoint_id) {
|
|
16928
17119
|
recordExecution(freshResult.trace.skill_id, freshResult.trace.endpoint_id, freshResult.trace, skill).catch(() => {});
|
|
16929
17120
|
}
|
|
17121
|
+
await recordAnalyticsSession(buildAnalyticsSessionPayload(freshResult, {
|
|
17122
|
+
browser_mode: "manual",
|
|
17123
|
+
discovery_queries: 1
|
|
17124
|
+
})).catch(() => {});
|
|
16930
17125
|
return reply.send({
|
|
16931
17126
|
...freshResult,
|
|
16932
17127
|
_recovery: {
|
|
@@ -16937,6 +17132,12 @@ async function registerRoutes(app) {
|
|
|
16937
17132
|
});
|
|
16938
17133
|
} catch {}
|
|
16939
17134
|
}
|
|
17135
|
+
await recordAnalyticsSession(buildAnalyticsSessionPayload(execResult, {
|
|
17136
|
+
browser_mode: "manual",
|
|
17137
|
+
discovery_queries: 0,
|
|
17138
|
+
cached_skill_calls: execResult.trace.endpoint_id ? 1 : 0,
|
|
17139
|
+
fresh_index_calls: 0
|
|
17140
|
+
})).catch(() => {});
|
|
16940
17141
|
return reply.send(execResult);
|
|
16941
17142
|
} catch (err) {
|
|
16942
17143
|
return reply.code(500).send({ error: err.message });
|
|
@@ -17443,10 +17644,10 @@ var init_routes = __esm(async () => {
|
|
|
17443
17644
|
init_session_logs();
|
|
17444
17645
|
await __promiseAll([
|
|
17445
17646
|
init_indexer(),
|
|
17647
|
+
init_vault(),
|
|
17446
17648
|
init_orchestrator(),
|
|
17447
17649
|
init_orchestrator(),
|
|
17448
17650
|
init_execution(),
|
|
17449
|
-
init_vault(),
|
|
17450
17651
|
init_auth(),
|
|
17451
17652
|
init_indexer()
|
|
17452
17653
|
]);
|
|
@@ -17531,10 +17732,11 @@ var init_server = __esm(async () => {
|
|
|
17531
17732
|
import { config as loadEnv } from "dotenv";
|
|
17532
17733
|
|
|
17533
17734
|
// ../../src/client/index.ts
|
|
17735
|
+
init_cascade();
|
|
17534
17736
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
17535
17737
|
import { join } from "path";
|
|
17536
17738
|
import { homedir, hostname } from "os";
|
|
17537
|
-
import { randomBytes, createHash } from "crypto";
|
|
17739
|
+
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
17538
17740
|
import { createInterface } from "readline";
|
|
17539
17741
|
var API_URL = process.env.UNBROWSE_BACKEND_URL || "https://beta-api.unbrowse.ai";
|
|
17540
17742
|
var PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
@@ -17596,6 +17798,20 @@ function resolveAgentName(preferredEmail, fallbackName) {
|
|
|
17596
17798
|
const normalized = normalizeAgentEmail(preferredEmail ?? "");
|
|
17597
17799
|
return isValidAgentEmail(normalized) ? normalized : fallbackName;
|
|
17598
17800
|
}
|
|
17801
|
+
function getLocalWalletContext() {
|
|
17802
|
+
const lobsterWallet = process.env.LOBSTER_WALLET_ADDRESS?.trim();
|
|
17803
|
+
if (lobsterWallet) {
|
|
17804
|
+
return { wallet_address: lobsterWallet, wallet_provider: "lobster.cash" };
|
|
17805
|
+
}
|
|
17806
|
+
const genericWallet = process.env.AGENT_WALLET_ADDRESS?.trim();
|
|
17807
|
+
if (genericWallet) {
|
|
17808
|
+
return {
|
|
17809
|
+
wallet_address: genericWallet,
|
|
17810
|
+
wallet_provider: process.env.AGENT_WALLET_PROVIDER?.trim() || undefined
|
|
17811
|
+
};
|
|
17812
|
+
}
|
|
17813
|
+
return {};
|
|
17814
|
+
}
|
|
17599
17815
|
function getApiKey() {
|
|
17600
17816
|
if (LOCAL_ONLY)
|
|
17601
17817
|
return "local-only";
|
|
@@ -17667,7 +17883,7 @@ async function findUsableApiKey() {
|
|
|
17667
17883
|
}
|
|
17668
17884
|
return null;
|
|
17669
17885
|
}
|
|
17670
|
-
async function
|
|
17886
|
+
async function apiRequest(method, path, body, opts) {
|
|
17671
17887
|
const key = opts?.noAuth ? "" : getApiKey();
|
|
17672
17888
|
const controller = new AbortController;
|
|
17673
17889
|
const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? API_TIMEOUT_MS);
|
|
@@ -17713,6 +17929,10 @@ async function api(method, path, body, opts) {
|
|
|
17713
17929
|
const msg = errData.details?.length ? `${errData.error}: ${errData.details.join("; ")}` : errData.error ?? `API HTTP ${res.status}`;
|
|
17714
17930
|
throw new Error(msg);
|
|
17715
17931
|
}
|
|
17932
|
+
return { data, headers: res.headers };
|
|
17933
|
+
}
|
|
17934
|
+
async function api(method, path, body, opts) {
|
|
17935
|
+
const { data } = await apiRequest(method, path, body, opts);
|
|
17716
17936
|
return data;
|
|
17717
17937
|
}
|
|
17718
17938
|
async function promptTosAcceptance(summary, tosUrl) {
|
|
@@ -17810,6 +18030,13 @@ async function ensureRegistered(options) {
|
|
|
17810
18030
|
console.log("[unbrowse] Restored saved registration.");
|
|
17811
18031
|
}
|
|
17812
18032
|
await checkTosStatus();
|
|
18033
|
+
try {
|
|
18034
|
+
const profile = await getMyProfile();
|
|
18035
|
+
const wallet = getLocalWalletContext();
|
|
18036
|
+
if (wallet.wallet_address && profile.wallet_address !== wallet.wallet_address) {
|
|
18037
|
+
await syncAgentWallet(wallet);
|
|
18038
|
+
}
|
|
18039
|
+
} catch {}
|
|
17813
18040
|
return;
|
|
17814
18041
|
}
|
|
17815
18042
|
let tosInfo;
|
|
@@ -17829,7 +18056,8 @@ async function ensureRegistered(options) {
|
|
|
17829
18056
|
const name = options?.promptForEmail ? await promptAgentEmail(fallbackName) : resolveAgentName(process.env.UNBROWSE_AGENT_EMAIL, fallbackName);
|
|
17830
18057
|
console.log(`Registering as "${name}"...`);
|
|
17831
18058
|
try {
|
|
17832
|
-
const
|
|
18059
|
+
const wallet = getLocalWalletContext();
|
|
18060
|
+
const { agent_id, api_key } = await api("POST", "/v1/agents/register", { name, tos_version: tosInfo.version, ...wallet });
|
|
17833
18061
|
process.env.UNBROWSE_API_KEY = api_key;
|
|
17834
18062
|
saveConfig({
|
|
17835
18063
|
api_key,
|
|
@@ -17837,7 +18065,8 @@ async function ensureRegistered(options) {
|
|
|
17837
18065
|
agent_name: name,
|
|
17838
18066
|
registered_at: new Date().toISOString(),
|
|
17839
18067
|
tos_accepted_version: tosInfo.version,
|
|
17840
|
-
tos_accepted_at: new Date().toISOString()
|
|
18068
|
+
tos_accepted_at: new Date().toISOString(),
|
|
18069
|
+
...wallet
|
|
17841
18070
|
});
|
|
17842
18071
|
console.log(`Registered as ${name}. API key saved to ~/.unbrowse/config.json`);
|
|
17843
18072
|
} catch (err) {
|
|
@@ -17846,6 +18075,18 @@ async function ensureRegistered(options) {
|
|
|
17846
18075
|
process.exit(1);
|
|
17847
18076
|
}
|
|
17848
18077
|
}
|
|
18078
|
+
async function getMyProfile() {
|
|
18079
|
+
return api("GET", "/v1/agents/me", undefined);
|
|
18080
|
+
}
|
|
18081
|
+
async function syncAgentWallet(wallet = getLocalWalletContext()) {
|
|
18082
|
+
if (!wallet.wallet_address)
|
|
18083
|
+
return;
|
|
18084
|
+
await api("POST", "/v1/agents/wallet", wallet);
|
|
18085
|
+
const config = loadConfig();
|
|
18086
|
+
if (!config)
|
|
18087
|
+
return;
|
|
18088
|
+
saveConfig({ ...config, ...wallet });
|
|
18089
|
+
}
|
|
17849
18090
|
|
|
17850
18091
|
// ../../src/cli/shortcuts.ts
|
|
17851
18092
|
var linkedin = {
|