unbrowse 2.12.2 → 2.12.7
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/README.md +8 -44
- package/dist/cli.js +514 -20723
- package/package.json +4 -10
- package/runtime-src/api/routes.ts +15 -801
- package/runtime-src/auth/index.ts +32 -142
- package/runtime-src/capture/index.ts +101 -436
- package/runtime-src/cli.ts +371 -956
- package/runtime-src/client/index.ts +29 -622
- package/runtime-src/execution/index.ts +85 -345
- package/runtime-src/graph/index.ts +10 -128
- package/runtime-src/intent-match.ts +27 -27
- package/runtime-src/kuri/client.ts +82 -543
- package/runtime-src/orchestrator/index.ts +462 -2246
- package/runtime-src/reverse-engineer/index.ts +22 -220
- package/runtime-src/runtime/local-server.ts +16 -149
- package/runtime-src/runtime/paths.ts +5 -9
- package/runtime-src/runtime/setup.ts +1 -52
- package/runtime-src/server.ts +11 -6
- package/runtime-src/transform/schema-hints.ts +358 -0
- package/runtime-src/types/skill.ts +2 -49
- package/runtime-src/verification/index.ts +0 -15
- package/runtime-src/version.ts +13 -13
- 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/bin/unbrowse-wrapper.mjs +0 -39
- package/bin/unbrowse.js +0 -38
- package/runtime-src/analytics-session.ts +0 -33
- package/runtime-src/api/browse-index.ts +0 -254
- package/runtime-src/api/browse-session.ts +0 -179
- package/runtime-src/api/browse-submit.ts +0 -455
- package/runtime-src/auth/runtime.ts +0 -116
- package/runtime-src/browser/index.ts +0 -635
- package/runtime-src/browser/types.ts +0 -41
- package/runtime-src/capture/prefetch.ts +0 -122
- package/runtime-src/capture/rsc.ts +0 -45
- package/runtime-src/cli/shortcuts.ts +0 -273
- package/runtime-src/client/graph-client.ts +0 -99
- package/runtime-src/execution/robots.ts +0 -167
- package/runtime-src/execution/search-forms.ts +0 -188
- 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/indexer/index.ts +0 -480
- 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 -256
- package/runtime-src/orchestrator/first-pass-action.ts +0 -362
- package/runtime-src/orchestrator/passive-publish.ts +0 -152
- package/runtime-src/orchestrator/timing-economics.ts +0 -80
- package/runtime-src/payments/cascade.ts +0 -137
- package/runtime-src/payments/index.ts +0 -268
- package/runtime-src/payments/wallet.ts +0 -33
- package/runtime-src/reverse-engineer/description-prompt.ts +0 -132
- package/runtime-src/router.ts +0 -17
- package/runtime-src/runtime/browser-access.ts +0 -11
- package/runtime-src/runtime/browser-host.ts +0 -48
- package/runtime-src/runtime/lifecycle.ts +0 -17
- package/runtime-src/runtime/supervisor.ts +0 -69
- package/runtime-src/single-binary.ts +0 -141
- package/runtime-src/telemetry.ts +0 -253
- package/runtime-src/verification/matrix.ts +0 -30
- package/scripts/postinstall.mjs +0 -81
|
@@ -1,39 +1,15 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import { homedir, hostname } from "os";
|
|
4
|
-
import { randomBytes
|
|
4
|
+
import { randomBytes } from "crypto";
|
|
5
5
|
import { createInterface } from "readline";
|
|
6
6
|
import type { AgentSkillChunkView, EndpointStats, ExecutionTrace, OrchestrationTiming, SkillManifest, ValidationResult } from "../types/index.js";
|
|
7
|
-
import { ensureCascadeSplitForSkill } from "../payments/cascade.js";
|
|
8
|
-
import { attributeLifecycle } from "../runtime/lifecycle.js";
|
|
9
|
-
import type { LifecycleEvent } from "../runtime/lifecycle.js";
|
|
10
|
-
import { detectHostEnvironment } from "../runtime/browser-host.js";
|
|
11
7
|
|
|
12
8
|
const API_URL = process.env.UNBROWSE_BACKEND_URL || "https://beta-api.unbrowse.ai";
|
|
13
9
|
const PROFILE_NAME = sanitizeProfileName(process.env.UNBROWSE_PROFILE ?? "");
|
|
14
10
|
const recentLocalSkills = new Map<string, SkillManifest>();
|
|
15
11
|
const LOCAL_ONLY = process.env.UNBROWSE_LOCAL_ONLY === "1";
|
|
16
12
|
|
|
17
|
-
function decodeBase64Json(value: string): unknown {
|
|
18
|
-
try {
|
|
19
|
-
if (typeof globalThis !== "undefined" && typeof globalThis.atob === "function") {
|
|
20
|
-
const binary = globalThis.atob(value);
|
|
21
|
-
const bytes = new Uint8Array(binary.length);
|
|
22
|
-
for (let i = 0; i < binary.length; i++) {
|
|
23
|
-
bytes[i] = binary.charCodeAt(i);
|
|
24
|
-
}
|
|
25
|
-
return JSON.parse(new TextDecoder("utf-8").decode(bytes));
|
|
26
|
-
}
|
|
27
|
-
return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
|
|
28
|
-
} catch {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function isX402Error(err: unknown): err is Error & { x402: true; terms?: unknown; status?: number } {
|
|
34
|
-
return !!err && typeof err === "object" && (err as { x402?: unknown }).x402 === true;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
13
|
function scopedSkillKey(skillId: string, scopeId?: string): string {
|
|
38
14
|
return scopeId ? `${scopeId}:${skillId}` : skillId;
|
|
39
15
|
}
|
|
@@ -53,10 +29,6 @@ function getConfigPath(): string {
|
|
|
53
29
|
return join(getConfigDir(), "config.json");
|
|
54
30
|
}
|
|
55
31
|
|
|
56
|
-
function getInstallTelemetryPath(): string {
|
|
57
|
-
return join(getConfigDir(), "install-state.json");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
32
|
function sanitizeProfileName(value: string): string {
|
|
61
33
|
return value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
62
34
|
}
|
|
@@ -76,20 +48,8 @@ interface UnbrowseConfig {
|
|
|
76
48
|
registered_at: string;
|
|
77
49
|
tos_accepted_version: string | null;
|
|
78
50
|
tos_accepted_at: string | null;
|
|
79
|
-
wallet_address?: string;
|
|
80
|
-
wallet_provider?: string;
|
|
81
51
|
}
|
|
82
52
|
|
|
83
|
-
interface InstallTelemetryState {
|
|
84
|
-
install_id: string;
|
|
85
|
-
first_seen_at: string;
|
|
86
|
-
cli_first_seen_reported_at?: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
type TelemetryHostType = "cli" | "codex" | "openclaw" | "mcp" | "native" | "unknown";
|
|
90
|
-
type InstallTelemetrySource = "host" | "setup" | "cli-first-seen";
|
|
91
|
-
type FunnelTelemetrySource = "host" | "setup" | "cli-first-seen" | "cli" | "agent" | "server";
|
|
92
|
-
|
|
93
53
|
type ApiKeySource = "env" | "config";
|
|
94
54
|
type ApiKeyValidationStatus = "ok" | "missing_profile" | "invalid" | "offline";
|
|
95
55
|
|
|
@@ -115,147 +75,6 @@ function saveConfig(config: UnbrowseConfig): void {
|
|
|
115
75
|
writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
116
76
|
}
|
|
117
77
|
|
|
118
|
-
function loadInstallTelemetryState(): InstallTelemetryState | null {
|
|
119
|
-
try {
|
|
120
|
-
const statePath = getInstallTelemetryPath();
|
|
121
|
-
if (existsSync(statePath)) {
|
|
122
|
-
return JSON.parse(readFileSync(statePath, "utf-8")) as InstallTelemetryState;
|
|
123
|
-
}
|
|
124
|
-
} catch {}
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function saveInstallTelemetryState(state: InstallTelemetryState): void {
|
|
129
|
-
const configDir = getConfigDir();
|
|
130
|
-
const statePath = getInstallTelemetryPath();
|
|
131
|
-
if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
|
|
132
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 0o600 });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function createInstallTelemetryState(): InstallTelemetryState {
|
|
136
|
-
return {
|
|
137
|
-
install_id: `install_${randomBytes(8).toString("hex")}`,
|
|
138
|
-
first_seen_at: new Date().toISOString(),
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function getOrCreateInstallTelemetryState(): InstallTelemetryState {
|
|
143
|
-
const existing = loadInstallTelemetryState();
|
|
144
|
-
if (existing?.install_id) return existing;
|
|
145
|
-
const created = createInstallTelemetryState();
|
|
146
|
-
saveInstallTelemetryState(created);
|
|
147
|
-
return created;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function getInstallId(): string {
|
|
151
|
-
return getOrCreateInstallTelemetryState().install_id;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function detectTelemetryHostType(): TelemetryHostType {
|
|
155
|
-
switch (detectHostEnvironment()) {
|
|
156
|
-
case "openai":
|
|
157
|
-
return "codex";
|
|
158
|
-
case "openclaw":
|
|
159
|
-
return "openclaw";
|
|
160
|
-
case "mcp":
|
|
161
|
-
return "mcp";
|
|
162
|
-
case "native":
|
|
163
|
-
return "native";
|
|
164
|
-
case "unknown":
|
|
165
|
-
default:
|
|
166
|
-
return "cli";
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async function postTelemetry(path: string, body: Record<string, unknown>): Promise<boolean> {
|
|
171
|
-
if (LOCAL_ONLY) return false;
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
const key = getApiKey();
|
|
175
|
-
const res = await fetch(`${API_URL}${path}`, {
|
|
176
|
-
method: "POST",
|
|
177
|
-
headers: {
|
|
178
|
-
"Content-Type": "application/json",
|
|
179
|
-
"Accept-Encoding": "gzip, deflate",
|
|
180
|
-
...(key ? { Authorization: `Bearer ${key}` } : {}),
|
|
181
|
-
},
|
|
182
|
-
body: JSON.stringify(body),
|
|
183
|
-
});
|
|
184
|
-
return res.ok;
|
|
185
|
-
} catch {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export async function ensureCliInstallTracked(hostType = detectTelemetryHostType()): Promise<void> {
|
|
191
|
-
const state = getOrCreateInstallTelemetryState();
|
|
192
|
-
if (state.cli_first_seen_reported_at) return;
|
|
193
|
-
|
|
194
|
-
const createdAt = new Date().toISOString();
|
|
195
|
-
const ok = await postTelemetry("/v1/telemetry/install", {
|
|
196
|
-
install_id: state.install_id,
|
|
197
|
-
source: "cli-first-seen",
|
|
198
|
-
host_type: hostType,
|
|
199
|
-
skill: "unbrowse",
|
|
200
|
-
status: "installed",
|
|
201
|
-
created_at: createdAt,
|
|
202
|
-
properties: {
|
|
203
|
-
profile: getActiveProfile(),
|
|
204
|
-
first_seen_at: state.first_seen_at,
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
if (!ok) return;
|
|
209
|
-
state.cli_first_seen_reported_at = createdAt;
|
|
210
|
-
saveInstallTelemetryState(state);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export async function recordInstallTelemetryEvent(
|
|
214
|
-
source: InstallTelemetrySource,
|
|
215
|
-
options?: {
|
|
216
|
-
hostType?: TelemetryHostType;
|
|
217
|
-
status?: string;
|
|
218
|
-
createdAt?: string;
|
|
219
|
-
properties?: Record<string, unknown>;
|
|
220
|
-
skill?: string;
|
|
221
|
-
skillVersion?: string;
|
|
222
|
-
},
|
|
223
|
-
): Promise<void> {
|
|
224
|
-
const createdAt = options?.createdAt ?? new Date().toISOString();
|
|
225
|
-
await postTelemetry("/v1/telemetry/install", {
|
|
226
|
-
install_id: getInstallId(),
|
|
227
|
-
source,
|
|
228
|
-
host_type: options?.hostType ?? detectTelemetryHostType(),
|
|
229
|
-
skill: options?.skill ?? "unbrowse",
|
|
230
|
-
skill_version: options?.skillVersion,
|
|
231
|
-
status: options?.status ?? "installed",
|
|
232
|
-
created_at: createdAt,
|
|
233
|
-
properties: options?.properties,
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export async function recordFunnelTelemetryEvent(
|
|
238
|
-
name: string,
|
|
239
|
-
options?: {
|
|
240
|
-
source?: FunnelTelemetrySource;
|
|
241
|
-
hostType?: TelemetryHostType;
|
|
242
|
-
createdAt?: string;
|
|
243
|
-
sessionId?: string;
|
|
244
|
-
properties?: Record<string, unknown>;
|
|
245
|
-
},
|
|
246
|
-
): Promise<void> {
|
|
247
|
-
const createdAt = options?.createdAt ?? new Date().toISOString();
|
|
248
|
-
await postTelemetry("/v1/telemetry/events", {
|
|
249
|
-
install_id: getInstallId(),
|
|
250
|
-
session_id: options?.sessionId,
|
|
251
|
-
name,
|
|
252
|
-
source: options?.source ?? "cli",
|
|
253
|
-
host_type: options?.hostType ?? detectTelemetryHostType(),
|
|
254
|
-
created_at: createdAt,
|
|
255
|
-
properties: options?.properties,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
78
|
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i;
|
|
260
79
|
|
|
261
80
|
export function normalizeAgentEmail(value: string): string {
|
|
@@ -275,23 +94,6 @@ export function resolveAgentName(preferredEmail: string | undefined, fallbackNam
|
|
|
275
94
|
return isValidAgentEmail(normalized) ? normalized : fallbackName;
|
|
276
95
|
}
|
|
277
96
|
|
|
278
|
-
export function getLocalWalletContext(): { wallet_address?: string; wallet_provider?: string } {
|
|
279
|
-
const lobsterWallet = process.env.LOBSTER_WALLET_ADDRESS?.trim();
|
|
280
|
-
if (lobsterWallet) {
|
|
281
|
-
return { wallet_address: lobsterWallet, wallet_provider: "lobster.cash" };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const genericWallet = process.env.AGENT_WALLET_ADDRESS?.trim();
|
|
285
|
-
if (genericWallet) {
|
|
286
|
-
return {
|
|
287
|
-
wallet_address: genericWallet,
|
|
288
|
-
wallet_provider: process.env.AGENT_WALLET_PROVIDER?.trim() || undefined,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return {};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
97
|
export function getApiKey(): string {
|
|
296
98
|
if (LOCAL_ONLY) return "local-only";
|
|
297
99
|
// Env var takes priority, then cached config
|
|
@@ -304,27 +106,7 @@ export function getApiKey(): string {
|
|
|
304
106
|
return "";
|
|
305
107
|
}
|
|
306
108
|
|
|
307
|
-
/**
|
|
308
|
-
* Derive a stable, privacy-safe indexer identifier from the raw API key.
|
|
309
|
-
* Returns a hex SHA-256 hash, or "" for empty / local-only keys.
|
|
310
|
-
*/
|
|
311
|
-
export function hashApiKey(key: string): string {
|
|
312
|
-
if (!key || key === "local-only") return "";
|
|
313
|
-
return createHash("sha256").update(key).digest("hex");
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Return the locally registered agent_id, or null if not registered.
|
|
318
|
-
* Used as the default indexer_id for Tier 1 attribution when the skill
|
|
319
|
-
* manifest doesn't already carry one.
|
|
320
|
-
*/
|
|
321
|
-
export function getAgentId(): string | null {
|
|
322
|
-
const config = loadConfig();
|
|
323
|
-
return config?.agent_id ?? null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
109
|
const API_TIMEOUT_MS = parseInt(process.env.UNBROWSE_API_TIMEOUT ?? "8000", 10);
|
|
327
|
-
const PUBLISH_TIMEOUT_MS = parseInt(process.env.UNBROWSE_PUBLISH_TIMEOUT ?? "30000", 10);
|
|
328
110
|
|
|
329
111
|
async function validateApiKey(key: string): Promise<ApiKeyValidationResult> {
|
|
330
112
|
const controller = new AbortController();
|
|
@@ -387,15 +169,10 @@ async function findUsableApiKey(): Promise<{ key: string; source: ApiKeySource }
|
|
|
387
169
|
return null;
|
|
388
170
|
}
|
|
389
171
|
|
|
390
|
-
async function
|
|
391
|
-
method: string,
|
|
392
|
-
path: string,
|
|
393
|
-
body?: unknown,
|
|
394
|
-
opts?: { noAuth?: boolean; timeoutMs?: number },
|
|
395
|
-
): Promise<{ data: T; headers: Headers }> {
|
|
172
|
+
async function api<T = unknown>(method: string, path: string, body?: unknown, opts?: { noAuth?: boolean }): Promise<T> {
|
|
396
173
|
const key = opts?.noAuth ? "" : getApiKey();
|
|
397
174
|
const controller = new AbortController();
|
|
398
|
-
const timer = setTimeout(() => controller.abort(),
|
|
175
|
+
const timer = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
399
176
|
let res: Response;
|
|
400
177
|
try {
|
|
401
178
|
res = await fetch(`${API_URL}${path}`, {
|
|
@@ -428,32 +205,11 @@ async function apiRequest<T = unknown>(
|
|
|
428
205
|
throw new Error("ToS update required. Restart unbrowse to accept new terms.");
|
|
429
206
|
}
|
|
430
207
|
|
|
431
|
-
// Handle x402 payment required — surface payment terms to the caller
|
|
432
|
-
if (res.status === 402) {
|
|
433
|
-
const paymentRequired = res.headers.get("PAYMENT-REQUIRED");
|
|
434
|
-
const legacyPaymentTerms = res.headers.get("X-Payment-Required");
|
|
435
|
-
const terms = paymentRequired
|
|
436
|
-
? decodeBase64Json(paymentRequired)
|
|
437
|
-
: legacyPaymentTerms
|
|
438
|
-
? JSON.parse(legacyPaymentTerms)
|
|
439
|
-
: (data as Record<string, unknown>).terms;
|
|
440
|
-
const err = new Error(`Payment required: ${(data as Record<string, unknown>).error ?? "This skill requires payment"}`);
|
|
441
|
-
(err as Error & { x402: boolean; terms: unknown; status: number }).x402 = true;
|
|
442
|
-
(err as Error & { terms: unknown }).terms = terms;
|
|
443
|
-
(err as Error & { status: number }).status = 402;
|
|
444
|
-
throw err;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
208
|
if (!res.ok) {
|
|
448
209
|
const errData = data as { error?: string; details?: string[] };
|
|
449
210
|
const msg = errData.details?.length ? `${errData.error}: ${errData.details.join("; ")}` : errData.error ?? `API HTTP ${res.status}`;
|
|
450
211
|
throw new Error(msg);
|
|
451
212
|
}
|
|
452
|
-
return { data: data as T, headers: res.headers };
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
async function api<T = unknown>(method: string, path: string, body?: unknown, opts?: { noAuth?: boolean; timeoutMs?: number }): Promise<T> {
|
|
456
|
-
const { data } = await apiRequest<T>(method, path, body, opts);
|
|
457
213
|
return data;
|
|
458
214
|
}
|
|
459
215
|
|
|
@@ -518,8 +274,7 @@ async function promptAgentEmail(defaultName: string): Promise<string> {
|
|
|
518
274
|
}
|
|
519
275
|
}
|
|
520
276
|
|
|
521
|
-
async function checkTosStatus(
|
|
522
|
-
const exitOnFailure = options?.exitOnFailure ?? true;
|
|
277
|
+
async function checkTosStatus(): Promise<void> {
|
|
523
278
|
const config = loadConfig();
|
|
524
279
|
|
|
525
280
|
let tosInfo: { version: string; summary: string; url: string };
|
|
@@ -528,11 +283,11 @@ async function checkTosStatus(options?: { exitOnFailure?: boolean }): Promise<bo
|
|
|
528
283
|
} catch {
|
|
529
284
|
// Offline — allow usage with whatever ToS was previously accepted.
|
|
530
285
|
// Backend will enforce on next actual API call anyway.
|
|
531
|
-
return
|
|
286
|
+
return;
|
|
532
287
|
}
|
|
533
288
|
|
|
534
289
|
if (config?.tos_accepted_version === tosInfo.version) {
|
|
535
|
-
return
|
|
290
|
+
return; // Already accepted current version
|
|
536
291
|
}
|
|
537
292
|
|
|
538
293
|
// Need re-acceptance
|
|
@@ -540,8 +295,7 @@ async function checkTosStatus(options?: { exitOnFailure?: boolean }): Promise<bo
|
|
|
540
295
|
const accepted = await promptTosAcceptance(tosInfo.summary, tosInfo.url);
|
|
541
296
|
if (!accepted) {
|
|
542
297
|
console.log("You must accept the updated Terms of Service to continue using Unbrowse.");
|
|
543
|
-
|
|
544
|
-
return false;
|
|
298
|
+
process.exit(1);
|
|
545
299
|
}
|
|
546
300
|
|
|
547
301
|
// Call accept-tos endpoint
|
|
@@ -559,27 +313,17 @@ async function checkTosStatus(options?: { exitOnFailure?: boolean }): Promise<bo
|
|
|
559
313
|
console.warn(`Failed to record ToS acceptance: ${(err as Error).message}`);
|
|
560
314
|
// Don't block — backend will enforce on next call
|
|
561
315
|
}
|
|
562
|
-
return true;
|
|
563
316
|
}
|
|
564
317
|
|
|
565
318
|
/** Auto-register with the backend if no API key is configured. Persists to ~/.unbrowse/config.json. */
|
|
566
|
-
export async function ensureRegistered(options?: { promptForEmail?: boolean
|
|
319
|
+
export async function ensureRegistered(options?: { promptForEmail?: boolean }): Promise<void> {
|
|
567
320
|
if (LOCAL_ONLY) return;
|
|
568
|
-
const exitOnFailure = options?.exitOnFailure ?? true;
|
|
569
321
|
const usableKey = await findUsableApiKey();
|
|
570
322
|
if (usableKey) {
|
|
571
323
|
if (usableKey.source === "config") {
|
|
572
324
|
console.log("[unbrowse] Restored saved registration.");
|
|
573
325
|
}
|
|
574
|
-
|
|
575
|
-
if (!accepted) return;
|
|
576
|
-
try {
|
|
577
|
-
const profile = await getMyProfile();
|
|
578
|
-
const wallet = getLocalWalletContext();
|
|
579
|
-
if (wallet.wallet_address && profile.wallet_address !== wallet.wallet_address) {
|
|
580
|
-
await syncAgentWallet(wallet);
|
|
581
|
-
}
|
|
582
|
-
} catch { /* non-fatal */ }
|
|
326
|
+
await checkTosStatus();
|
|
583
327
|
return;
|
|
584
328
|
}
|
|
585
329
|
|
|
@@ -597,8 +341,7 @@ export async function ensureRegistered(options?: { promptForEmail?: boolean; exi
|
|
|
597
341
|
const accepted = await promptTosAcceptance(tosInfo.summary, tosInfo.url);
|
|
598
342
|
if (!accepted) {
|
|
599
343
|
console.log("You must accept the Terms of Service to use Unbrowse.");
|
|
600
|
-
|
|
601
|
-
return;
|
|
344
|
+
process.exit(1);
|
|
602
345
|
}
|
|
603
346
|
|
|
604
347
|
// Step 3: Register with ToS version
|
|
@@ -607,9 +350,8 @@ export async function ensureRegistered(options?: { promptForEmail?: boolean; exi
|
|
|
607
350
|
console.log(`Registering as "${name}"...`);
|
|
608
351
|
|
|
609
352
|
try {
|
|
610
|
-
const wallet = getLocalWalletContext();
|
|
611
353
|
const { agent_id, api_key } = await api<{ agent_id: string; api_key: string }>(
|
|
612
|
-
"POST", "/v1/agents/register", { name, tos_version: tosInfo.version
|
|
354
|
+
"POST", "/v1/agents/register", { name, tos_version: tosInfo.version }
|
|
613
355
|
);
|
|
614
356
|
|
|
615
357
|
process.env.UNBROWSE_API_KEY = api_key;
|
|
@@ -620,52 +362,14 @@ export async function ensureRegistered(options?: { promptForEmail?: boolean; exi
|
|
|
620
362
|
registered_at: new Date().toISOString(),
|
|
621
363
|
tos_accepted_version: tosInfo.version,
|
|
622
364
|
tos_accepted_at: new Date().toISOString(),
|
|
623
|
-
...wallet,
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
await recordFunnelTelemetryEvent("registration_succeeded", {
|
|
627
|
-
source: "cli",
|
|
628
|
-
properties: {
|
|
629
|
-
prompt_for_email: options?.promptForEmail === true,
|
|
630
|
-
},
|
|
631
365
|
});
|
|
632
366
|
|
|
633
367
|
console.log(`Registered as ${name}. API key saved to ~/.unbrowse/config.json`);
|
|
634
368
|
} catch (err) {
|
|
635
369
|
console.warn(`Registration failed: ${(err as Error).message}`);
|
|
636
370
|
console.warn("Set UNBROWSE_API_KEY manually or try again.");
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
let backgroundRegistrationPromise: Promise<void> | null = null;
|
|
642
|
-
|
|
643
|
-
export function startBackgroundRegistration(options?: { promptForEmail?: boolean }): Promise<void> {
|
|
644
|
-
if (LOCAL_ONLY) return Promise.resolve();
|
|
645
|
-
if (backgroundRegistrationPromise) return backgroundRegistrationPromise;
|
|
646
|
-
backgroundRegistrationPromise = ensureRegistered({
|
|
647
|
-
promptForEmail: options?.promptForEmail,
|
|
648
|
-
exitOnFailure: false,
|
|
649
|
-
})
|
|
650
|
-
.catch((err) => {
|
|
651
|
-
console.warn(`[unbrowse] Background registration failed: ${(err as Error).message}`);
|
|
652
|
-
})
|
|
653
|
-
.finally(() => {
|
|
654
|
-
backgroundRegistrationPromise = null;
|
|
655
|
-
});
|
|
656
|
-
return backgroundRegistrationPromise;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
export async function waitForBackgroundRegistration(timeoutMs = 0): Promise<void> {
|
|
660
|
-
if (!backgroundRegistrationPromise) return;
|
|
661
|
-
if (timeoutMs <= 0) {
|
|
662
|
-
await backgroundRegistrationPromise;
|
|
663
|
-
return;
|
|
371
|
+
process.exit(1);
|
|
664
372
|
}
|
|
665
|
-
await Promise.race([
|
|
666
|
-
backgroundRegistrationPromise,
|
|
667
|
-
new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),
|
|
668
|
-
]);
|
|
669
373
|
}
|
|
670
374
|
|
|
671
375
|
// --- Skill CRUD ---
|
|
@@ -834,29 +538,7 @@ export async function publishSkill(
|
|
|
834
538
|
} as SkillManifest & { warnings: string[] };
|
|
835
539
|
}
|
|
836
540
|
if (LOCAL_ONLY) throw new Error("local-only mode");
|
|
837
|
-
|
|
838
|
-
const published = await api<SkillManifest & { warnings: string[] }>("POST", "/v1/skills", {
|
|
839
|
-
...draft,
|
|
840
|
-
...(wallet.wallet_address ? wallet : {}),
|
|
841
|
-
}, { timeoutMs: PUBLISH_TIMEOUT_MS });
|
|
842
|
-
|
|
843
|
-
const cascade = await ensureCascadeSplitForSkill(published).catch((err) => ({
|
|
844
|
-
warning: `cascade_split_failed:${(err as Error).message}`,
|
|
845
|
-
}));
|
|
846
|
-
const warnings = [...(published.warnings ?? [])];
|
|
847
|
-
if (cascade.warning) warnings.push(cascade.warning);
|
|
848
|
-
|
|
849
|
-
if (cascade.split_config && cascade.split_config !== published.split_config) {
|
|
850
|
-
const updated = await api<SkillManifest>("PATCH", `/v1/skills/${published.skill_id}`, {
|
|
851
|
-
split_config: cascade.split_config,
|
|
852
|
-
});
|
|
853
|
-
return {
|
|
854
|
-
...updated,
|
|
855
|
-
warnings,
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
return { ...published, warnings };
|
|
541
|
+
return api("POST", "/v1/skills", draft);
|
|
860
542
|
}
|
|
861
543
|
|
|
862
544
|
export async function deprecateSkill(skillId: string): Promise<void> {
|
|
@@ -929,11 +611,10 @@ export async function searchIntentResolve(
|
|
|
929
611
|
domain_results: Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
930
612
|
global_results: Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
931
613
|
skipped_global: boolean;
|
|
932
|
-
actual_cost_uc?: number;
|
|
933
614
|
}> {
|
|
934
615
|
if (LOCAL_ONLY) return { domain_results: [], global_results: [], skipped_global: false };
|
|
935
616
|
try {
|
|
936
|
-
|
|
617
|
+
return await api<{
|
|
937
618
|
domain_results: Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
938
619
|
global_results: Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
939
620
|
skipped_global: boolean;
|
|
@@ -943,24 +624,12 @@ export async function searchIntentResolve(
|
|
|
943
624
|
domain_k: domainK,
|
|
944
625
|
global_k: globalK,
|
|
945
626
|
});
|
|
946
|
-
|
|
947
|
-
const actualCostUc = actualCostHeader && /^\d+$/.test(actualCostHeader)
|
|
948
|
-
? Number(actualCostHeader)
|
|
949
|
-
: undefined;
|
|
950
|
-
return actualCostUc != null ? { ...data, actual_cost_uc: actualCostUc } : data;
|
|
951
|
-
} catch (err) {
|
|
952
|
-
if (isX402Error(err)) throw err;
|
|
627
|
+
} catch {
|
|
953
628
|
const [domain_results, global_results] = await Promise.all([
|
|
954
629
|
domain
|
|
955
|
-
? searchIntentInDomain(intent, domain, domainK).catch((
|
|
956
|
-
if (isX402Error(fallbackErr)) throw fallbackErr;
|
|
957
|
-
return [] as Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
958
|
-
})
|
|
630
|
+
? searchIntentInDomain(intent, domain, domainK).catch(() => [] as Array<{ id: number; score: number; metadata: Record<string, unknown> }>)
|
|
959
631
|
: Promise.resolve([] as Array<{ id: number; score: number; metadata: Record<string, unknown> }>),
|
|
960
|
-
searchIntent(intent, globalK).catch((
|
|
961
|
-
if (isX402Error(fallbackErr)) throw fallbackErr;
|
|
962
|
-
return [] as Array<{ id: number; score: number; metadata: Record<string, unknown> }>;
|
|
963
|
-
}),
|
|
632
|
+
searchIntent(intent, globalK).catch(() => [] as Array<{ id: number; score: number; metadata: Record<string, unknown> }>),
|
|
964
633
|
]);
|
|
965
634
|
return { domain_results, global_results, skipped_global: false };
|
|
966
635
|
}
|
|
@@ -968,81 +637,21 @@ export async function searchIntentResolve(
|
|
|
968
637
|
|
|
969
638
|
// --- Stats ---
|
|
970
639
|
|
|
971
|
-
|
|
972
|
-
export interface ExecutionPayload {
|
|
973
|
-
skill_id: string;
|
|
974
|
-
endpoint_id: string;
|
|
975
|
-
trace: Omit<ExecutionTrace, "result">;
|
|
976
|
-
indexer_id?: string;
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
export interface AnalyticsSessionPayload {
|
|
980
|
-
session_id: string;
|
|
981
|
-
started_at: string;
|
|
982
|
-
completed_at?: string;
|
|
983
|
-
trace_version?: string;
|
|
984
|
-
api_calls: number;
|
|
985
|
-
discovery_queries?: number;
|
|
986
|
-
cached_skill_calls?: number;
|
|
987
|
-
fresh_index_calls?: number;
|
|
988
|
-
browser_mode?: "default" | "replaced" | "manual" | "unknown";
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* Build the POST body for /v1/stats/execution.
|
|
993
|
-
* Pure function — no I/O, fully testable.
|
|
994
|
-
*
|
|
995
|
-
* Derives indexer_id from:
|
|
996
|
-
* 1. Explicit override (opts.indexer_id)
|
|
997
|
-
* 2. skill.indexer_id (set by the backend at publish time)
|
|
998
|
-
* 3. undefined (backend will fall back to its own lookup)
|
|
999
|
-
*/
|
|
1000
|
-
export function buildExecutionPayload(
|
|
640
|
+
export async function recordExecution(
|
|
1001
641
|
skillId: string,
|
|
1002
642
|
endpointId: string,
|
|
1003
|
-
trace: ExecutionTrace
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
643
|
+
trace: ExecutionTrace
|
|
644
|
+
): Promise<void> {
|
|
645
|
+
if (LOCAL_ONLY) return;
|
|
646
|
+
// Strip actual API response data — only send metadata for scoring
|
|
1007
647
|
const { result: _result, ...metadata } = trace;
|
|
1008
|
-
|
|
1009
|
-
const payload: ExecutionPayload = {
|
|
648
|
+
await api("POST", "/v1/stats/execution", {
|
|
1010
649
|
skill_id: skillId,
|
|
1011
650
|
endpoint_id: endpointId,
|
|
1012
651
|
trace: metadata,
|
|
1013
|
-
};
|
|
1014
|
-
if (indexer_id) payload.indexer_id = indexer_id;
|
|
1015
|
-
return payload;
|
|
1016
|
-
}
|
|
1017
|
-
export async function recordExecution(
|
|
1018
|
-
skillId: string,
|
|
1019
|
-
endpointId: string,
|
|
1020
|
-
trace: ExecutionTrace,
|
|
1021
|
-
skill?: Pick<SkillManifest, "indexer_id"> | null,
|
|
1022
|
-
): Promise<void> {
|
|
1023
|
-
if (LOCAL_ONLY) return;
|
|
1024
|
-
const payload = buildExecutionPayload(skillId, endpointId, trace, skill);
|
|
1025
|
-
await api("POST", "/v1/stats/execution", payload);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
export async function recordAnalyticsSession(payload: AnalyticsSessionPayload): Promise<void> {
|
|
1029
|
-
if (LOCAL_ONLY) return;
|
|
1030
|
-
await api("POST", "/v1/analytics/sessions", payload);
|
|
652
|
+
});
|
|
1031
653
|
}
|
|
1032
654
|
|
|
1033
|
-
/** Record a payment transaction for a paid skill execution. Fire-and-forget. */
|
|
1034
|
-
export async function recordTransaction(params: {
|
|
1035
|
-
transaction_id: string;
|
|
1036
|
-
consumer_id: string;
|
|
1037
|
-
creator_id?: string;
|
|
1038
|
-
skill_id: string;
|
|
1039
|
-
endpoint_id?: string;
|
|
1040
|
-
price_usd: number;
|
|
1041
|
-
payment_proof?: string;
|
|
1042
|
-
}): Promise<void> {
|
|
1043
|
-
if (LOCAL_ONLY) return;
|
|
1044
|
-
await api("POST", "/v1/transactions", params);
|
|
1045
|
-
}
|
|
1046
655
|
export async function recordFeedback(
|
|
1047
656
|
skillId: string,
|
|
1048
657
|
endpointId: string,
|
|
@@ -1076,23 +685,7 @@ export async function recordDiagnostics(
|
|
|
1076
685
|
|
|
1077
686
|
export async function recordOrchestrationPerf(timing: OrchestrationTiming): Promise<void> {
|
|
1078
687
|
if (LOCAL_ONLY) return;
|
|
1079
|
-
|
|
1080
|
-
timing.source === "marketplace" ? "marketplace"
|
|
1081
|
-
: timing.source === "live-capture" ? "live-capture"
|
|
1082
|
-
: "cache";
|
|
1083
|
-
const now = new Date().toISOString();
|
|
1084
|
-
const events: LifecycleEvent[] = [];
|
|
1085
|
-
if (timing.search_ms > 0) {
|
|
1086
|
-
events.push({ phase: "discover", skill_id: timing.skill_id ?? "", timestamp: now, duration_ms: timing.search_ms, source: lifecycleSource });
|
|
1087
|
-
}
|
|
1088
|
-
if (timing.get_skill_ms > 0) {
|
|
1089
|
-
events.push({ phase: "resolve", skill_id: timing.skill_id ?? "", timestamp: now, duration_ms: timing.get_skill_ms, source: lifecycleSource });
|
|
1090
|
-
}
|
|
1091
|
-
if (timing.execute_ms > 0) {
|
|
1092
|
-
events.push({ phase: "execute", skill_id: timing.skill_id ?? "", timestamp: now, duration_ms: timing.execute_ms, source: lifecycleSource });
|
|
1093
|
-
}
|
|
1094
|
-
const phaseTotals = Object.fromEntries(attributeLifecycle(events));
|
|
1095
|
-
await api("POST", "/v1/stats/perf", { ...timing, phase_totals_ms: phaseTotals });
|
|
688
|
+
await api("POST", "/v1/stats/perf", timing);
|
|
1096
689
|
}
|
|
1097
690
|
|
|
1098
691
|
// --- Validation ---
|
|
@@ -1102,121 +695,13 @@ export async function validateManifest(manifest: unknown): Promise<ValidationRes
|
|
|
1102
695
|
return api<ValidationResult>("POST", "/v1/validate", manifest);
|
|
1103
696
|
}
|
|
1104
697
|
|
|
1105
|
-
// --- Graph Edge Publishing ---
|
|
1106
|
-
|
|
1107
|
-
/**
|
|
1108
|
-
* Publish operation graph edges to the dedicated graph endpoint.
|
|
1109
|
-
* Fire-and-forget: logs errors but does not throw.
|
|
1110
|
-
*/
|
|
1111
|
-
export async function publishGraphEdges(
|
|
1112
|
-
domain: string,
|
|
1113
|
-
node: { endpoint_id: string; method: string; url_template: string },
|
|
1114
|
-
edges: Array<{ target_endpoint_id: string; kind: string; confidence: number }>
|
|
1115
|
-
): Promise<void> {
|
|
1116
|
-
if (LOCAL_ONLY) return;
|
|
1117
|
-
try {
|
|
1118
|
-
await api("POST", "/v1/graph/edges", { domain, node, edges });
|
|
1119
|
-
} catch (err) {
|
|
1120
|
-
console.error(`[graph] failed to publish edges for ${domain}: ${(err as Error).message}`);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// ---------------------------------------------------------------------------
|
|
1125
|
-
// Auto-file GitHub issues from accumulated agent errors
|
|
1126
|
-
// ---------------------------------------------------------------------------
|
|
1127
|
-
|
|
1128
|
-
export interface AutoFilePayload {
|
|
1129
|
-
skill_id: string;
|
|
1130
|
-
endpoint_id: string;
|
|
1131
|
-
domain: string;
|
|
1132
|
-
intent: string;
|
|
1133
|
-
url?: string;
|
|
1134
|
-
error: string;
|
|
1135
|
-
failure_count: number;
|
|
1136
|
-
first_seen: string;
|
|
1137
|
-
last_seen: string;
|
|
1138
|
-
kuri_version: string;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* Auto-file a GitHub issue via the backend. Fire-and-forget — failures
|
|
1143
|
-
* are logged but never thrown.
|
|
1144
|
-
*/
|
|
1145
|
-
export async function autoFileIssue(payload: AutoFilePayload): Promise<void> {
|
|
1146
|
-
if (isLocalOnlyMode()) {
|
|
1147
|
-
console.log(`[auto-file] skipped (local-only mode): ${payload.skill_id}:${payload.endpoint_id}`);
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
try {
|
|
1151
|
-
await api("POST", "/v1/issues/auto-file", payload);
|
|
1152
|
-
console.log(`[auto-file] issue filed for ${payload.skill_id}:${payload.endpoint_id} (${payload.failure_count} failures)`);
|
|
1153
|
-
} catch (err) {
|
|
1154
|
-
console.warn(`[auto-file] failed: ${(err as Error).message}`);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
// --- Cross-Agent Discovery Diagnostics ---
|
|
1159
|
-
|
|
1160
|
-
/**
|
|
1161
|
-
* Diagnostic function: polls marketplace search to verify a skill is discoverable.
|
|
1162
|
-
* Not called in production flow -- used for verifying cross-agent discovery within 60s.
|
|
1163
|
-
*/
|
|
1164
|
-
export async function verifyMarketplaceDiscovery(
|
|
1165
|
-
skillId: string,
|
|
1166
|
-
intent: string,
|
|
1167
|
-
maxWaitMs = 60000
|
|
1168
|
-
): Promise<{ found: boolean; latency_ms: number }> {
|
|
1169
|
-
const start = Date.now();
|
|
1170
|
-
const pollInterval = 2000;
|
|
1171
|
-
|
|
1172
|
-
while (Date.now() - start < maxWaitMs) {
|
|
1173
|
-
try {
|
|
1174
|
-
const results = await searchIntent(intent, 10);
|
|
1175
|
-
for (const result of results) {
|
|
1176
|
-
const meta = result.metadata ?? {};
|
|
1177
|
-
let foundId: string | undefined;
|
|
1178
|
-
// Check metadata.skill_id directly
|
|
1179
|
-
if (typeof meta.skill_id === "string") {
|
|
1180
|
-
foundId = meta.skill_id;
|
|
1181
|
-
}
|
|
1182
|
-
// Try parsing metadata.content as JSON for skill_id
|
|
1183
|
-
if (!foundId && typeof meta.content === "string") {
|
|
1184
|
-
try {
|
|
1185
|
-
const parsed = JSON.parse(meta.content);
|
|
1186
|
-
if (typeof parsed.skill_id === "string") foundId = parsed.skill_id;
|
|
1187
|
-
} catch { /* not JSON */ }
|
|
1188
|
-
}
|
|
1189
|
-
if (foundId === skillId) {
|
|
1190
|
-
return { found: true, latency_ms: Date.now() - start };
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
} catch { /* search failed, retry */ }
|
|
1194
|
-
|
|
1195
|
-
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
return { found: false, latency_ms: Date.now() - start };
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
698
|
// --- Agent Registration ---
|
|
1202
699
|
|
|
1203
|
-
export async function registerAgent(
|
|
1204
|
-
|
|
1205
|
-
wallet: { wallet_address?: string; wallet_provider?: string } = getLocalWalletContext(),
|
|
1206
|
-
): Promise<{ agent_id: string; api_key: string }> {
|
|
1207
|
-
return api<{ agent_id: string; api_key: string }>("POST", "/v1/agents/register", { name, ...wallet });
|
|
700
|
+
export async function registerAgent(name: string): Promise<{ agent_id: string; api_key: string }> {
|
|
701
|
+
return api<{ agent_id: string; api_key: string }>("POST", "/v1/agents/register", { name });
|
|
1208
702
|
}
|
|
1209
703
|
|
|
1210
|
-
export async function getAgent(agentId: string): Promise<{
|
|
1211
|
-
agent_id: string;
|
|
1212
|
-
name: string;
|
|
1213
|
-
created_at: string;
|
|
1214
|
-
wallet_address?: string | null;
|
|
1215
|
-
wallet_provider?: string | null;
|
|
1216
|
-
skills_discovered: string[];
|
|
1217
|
-
total_executions: number;
|
|
1218
|
-
total_feedback_given: number;
|
|
1219
|
-
} | null> {
|
|
704
|
+
export async function getAgent(agentId: string): Promise<{ agent_id: string; name: string; created_at: string; skills_discovered: string[]; total_executions: number; total_feedback_given: number } | null> {
|
|
1220
705
|
try {
|
|
1221
706
|
return await api("GET", `/v1/agents/${agentId}`);
|
|
1222
707
|
} catch {
|
|
@@ -1224,84 +709,6 @@ export async function getAgent(agentId: string): Promise<{
|
|
|
1224
709
|
}
|
|
1225
710
|
}
|
|
1226
711
|
|
|
1227
|
-
export async function getMyProfile(): Promise<{
|
|
1228
|
-
agent_id: string;
|
|
1229
|
-
name: string;
|
|
1230
|
-
created_at: string;
|
|
1231
|
-
wallet_address?: string | null;
|
|
1232
|
-
wallet_provider?: string | null;
|
|
1233
|
-
skills_discovered: string[];
|
|
1234
|
-
total_executions: number;
|
|
1235
|
-
total_feedback_given: number;
|
|
1236
|
-
}> {
|
|
712
|
+
export async function getMyProfile(): Promise<{ agent_id: string; name: string; created_at: string; skills_discovered: string[]; total_executions: number; total_feedback_given: number }> {
|
|
1237
713
|
return api("GET", "/v1/agents/me", undefined);
|
|
1238
714
|
}
|
|
1239
|
-
|
|
1240
|
-
export async function syncAgentWallet(wallet = getLocalWalletContext()): Promise<void> {
|
|
1241
|
-
if (!wallet.wallet_address) return;
|
|
1242
|
-
await api("POST", "/v1/agents/wallet", wallet);
|
|
1243
|
-
const config = loadConfig();
|
|
1244
|
-
if (!config) return;
|
|
1245
|
-
saveConfig({ ...config, ...wallet });
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
// --- Transaction Visibility ---
|
|
1250
|
-
|
|
1251
|
-
/** Get consumer payment history for an agent. */
|
|
1252
|
-
export async function getTransactionHistory(agentId: string): Promise<{
|
|
1253
|
-
ledger: {
|
|
1254
|
-
agent_id: string;
|
|
1255
|
-
total_spent_uc: number;
|
|
1256
|
-
total_spent_usd: number;
|
|
1257
|
-
transaction_count: number;
|
|
1258
|
-
first_transaction_at: string;
|
|
1259
|
-
last_transaction_at: string;
|
|
1260
|
-
} | null;
|
|
1261
|
-
transactions: Array<{
|
|
1262
|
-
transaction_id: string;
|
|
1263
|
-
consumer_id: string;
|
|
1264
|
-
creator_id: string;
|
|
1265
|
-
skill_id: string;
|
|
1266
|
-
price_usd: number;
|
|
1267
|
-
price_uc: number;
|
|
1268
|
-
status: string;
|
|
1269
|
-
created_at: string;
|
|
1270
|
-
}>;
|
|
1271
|
-
}> {
|
|
1272
|
-
return api("GET", `/v1/transactions/consumer/${agentId}`);
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
/** Get creator earnings history for an agent/indexer. */
|
|
1276
|
-
export async function getCreatorEarnings(agentId: string): Promise<{
|
|
1277
|
-
ledger: {
|
|
1278
|
-
agent_id: string;
|
|
1279
|
-
total_earned_uc: number;
|
|
1280
|
-
total_earned_usd: number;
|
|
1281
|
-
total_fees_uc: number;
|
|
1282
|
-
transaction_count: number;
|
|
1283
|
-
first_transaction_at: string;
|
|
1284
|
-
last_transaction_at: string;
|
|
1285
|
-
} | null;
|
|
1286
|
-
transactions: Array<{
|
|
1287
|
-
transaction_id: string;
|
|
1288
|
-
consumer_id: string;
|
|
1289
|
-
creator_id: string;
|
|
1290
|
-
skill_id: string;
|
|
1291
|
-
price_usd: number;
|
|
1292
|
-
creator_payout_uc: number;
|
|
1293
|
-
status: string;
|
|
1294
|
-
created_at: string;
|
|
1295
|
-
}>;
|
|
1296
|
-
}> {
|
|
1297
|
-
return api("GET", `/v1/transactions/creator/${agentId}`);
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
/** Set the base price for a skill (requires auth as skill owner). */
|
|
1301
|
-
export async function setSkillPrice(skillId: string, priceUsd: number): Promise<unknown> {
|
|
1302
|
-
return api("PATCH", `/v1/skills/${skillId}`, { base_price_usd: priceUsd });
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
export async function setSkillSplitConfig(skillId: string, splitConfig: string | null): Promise<unknown> {
|
|
1306
|
-
return api("PATCH", `/v1/skills/${skillId}`, { split_config: splitConfig });
|
|
1307
|
-
}
|