@x12i/sdk 0.4.0 → 1.1.0
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 +62 -0
- package/dist/admin-import.d.ts +6 -0
- package/dist/admin-import.js +127 -0
- package/dist/admin-import.js.map +1 -0
- package/dist/admin-mcp.d.ts +3 -0
- package/dist/admin-mcp.js +95 -0
- package/dist/admin-mcp.js.map +1 -0
- package/dist/admin.d.ts +156 -0
- package/dist/admin.js +369 -0
- package/dist/admin.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +68 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +74 -195
- package/dist/client.js +187 -695
- package/dist/client.js.map +1 -1
- package/dist/identity.d.ts +8 -0
- package/dist/identity.js +34 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +6 -22
- package/dist/index.js +6 -12
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +18 -0
- package/dist/init.js +147 -0
- package/dist/init.js.map +1 -0
- package/dist/maturity.d.ts +65 -0
- package/dist/maturity.js +138 -0
- package/dist/maturity.js.map +1 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +168 -0
- package/dist/mcp.js.map +1 -0
- package/dist/protocol.d.ts +38 -0
- package/dist/protocol.js +168 -0
- package/dist/protocol.js.map +1 -0
- package/dist/rank.d.ts +19 -0
- package/dist/rank.js +82 -0
- package/dist/rank.js.map +1 -0
- package/dist/runner.d.ts +22 -0
- package/dist/runner.js +99 -0
- package/dist/runner.js.map +1 -0
- package/package.json +66 -61
- package/LICENSE +0 -21
- package/dist/client.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/registry.d.ts +0 -8
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -21
- package/dist/registry.js.map +0 -1
package/dist/client.js
CHANGED
|
@@ -1,728 +1,220 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function siteFetchDefaults(url) {
|
|
19
|
-
try {
|
|
20
|
-
const parsed = new URL(url);
|
|
21
|
-
const host = parsed.hostname;
|
|
22
|
-
const path = parsed.pathname;
|
|
23
|
-
if ((host === "openrouter.ai" || host.endsWith(".openrouter.ai")) &&
|
|
24
|
-
(path === "/rankings" || path.startsWith("/rankings/"))) {
|
|
25
|
-
return {
|
|
26
|
-
waitForSelector: "div.grid.grid-cols-12.items-center",
|
|
27
|
-
settleMs: 500,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if ((host === "linkedin.com" || host === "www.linkedin.com" || host.endsWith(".linkedin.com")) &&
|
|
31
|
-
path.startsWith("/company/")) {
|
|
32
|
-
return { waitForSelector: "h1", settleMs: 1000 };
|
|
33
|
-
}
|
|
34
|
-
if (host === "medium.com" ||
|
|
35
|
-
host.endsWith(".medium.com") ||
|
|
36
|
-
host === "towardsdatascience.com") {
|
|
37
|
-
return { waitForSelector: "article h1, h1", settleMs: 800 };
|
|
38
|
-
}
|
|
39
|
-
if (host === "g2.com" || host === "www.g2.com" || host.endsWith(".g2.com")) {
|
|
40
|
-
return { settleMs: 1500 };
|
|
41
|
-
}
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { getReadme, searchPackages } from "@x12i/npm-core";
|
|
3
|
+
import { loadOrCreateIdentity, saveIdentity, defaultIdentityPath } from "./identity.js";
|
|
4
|
+
import { rankPackages } from "./rank.js";
|
|
5
|
+
import { canonicalRequest, randomNonce, reviewRating, sign, solveWork, usageFields, } from "./protocol.js";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
export const SDK_VERSION = require("../package.json").version;
|
|
8
|
+
/** Catalog service. Override with X12I_SDK_URL. */
|
|
9
|
+
export const DEFAULT_SERVICE_URL = "https://x12i-sdk.x12i.com";
|
|
10
|
+
export class SdkError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
code;
|
|
13
|
+
constructor(message, status = 0, code = "REQUEST_FAILED") {
|
|
14
|
+
super(message);
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.name = "SdkError";
|
|
42
18
|
}
|
|
43
|
-
catch {
|
|
44
|
-
// ignore unparseable URLs
|
|
45
|
-
}
|
|
46
|
-
return {};
|
|
47
19
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export class WebAgentClient {
|
|
53
|
-
registry;
|
|
54
|
-
defaultFetchOptions;
|
|
55
|
-
reuseBrowser;
|
|
56
|
-
searchProvider;
|
|
57
|
-
browserPromise;
|
|
58
|
-
fetchCache;
|
|
59
|
-
snapshotStore;
|
|
60
|
-
runStore;
|
|
61
|
-
extractionAdapter;
|
|
62
|
-
ownsCache;
|
|
63
|
-
ownsSnapshots;
|
|
64
|
-
ownsRuns;
|
|
65
|
-
funcxExecutor;
|
|
66
|
-
ownsFuncxExecutor;
|
|
67
|
-
webFunctionBindings;
|
|
68
|
-
hostPolicy;
|
|
69
|
-
aiProfilesSource;
|
|
70
|
-
functionCheckpoints = new InMemoryFunctionCheckpointStore();
|
|
71
|
-
activeRunEventSink;
|
|
20
|
+
export class X12iClient {
|
|
21
|
+
baseUrl;
|
|
22
|
+
identityPath;
|
|
23
|
+
fetchImpl;
|
|
72
24
|
constructor(options = {}) {
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
this.searchProvider = resolveSearchProvider(options.search);
|
|
77
|
-
this.ownsCache = !options.fetchCache;
|
|
78
|
-
this.ownsSnapshots = !options.snapshotStore;
|
|
79
|
-
this.ownsRuns = !options.runStore;
|
|
80
|
-
this.fetchCache = options.fetchCache ?? new InMemoryFetchCache();
|
|
81
|
-
this.snapshotStore = options.snapshotStore ?? new InMemorySnapshotStore();
|
|
82
|
-
this.runStore = options.runStore ?? new InMemoryRunStore();
|
|
83
|
-
this.extractionAdapter = options.extractionAdapter;
|
|
84
|
-
this.hostPolicy = loadHostPolicyFromEnv();
|
|
85
|
-
if (options.aiProfiles?.source)
|
|
86
|
-
this.hostPolicy.aiProfilesSource = options.aiProfiles.source;
|
|
87
|
-
this.aiProfilesSource = options.aiProfiles?.source ?? this.hostPolicy.aiProfilesSource ?? "auto";
|
|
88
|
-
this.ownsFuncxExecutor = options.ownsFuncxExecutor ?? !options.funcxExecutor;
|
|
89
|
-
this.funcxExecutor = options.funcxExecutor ?? createExecutorFromPolicy(this.hostPolicy);
|
|
90
|
-
this.webFunctionBindings = options.webFunctionBindings ?? [];
|
|
91
|
-
void this.loadBindingsFromEnv();
|
|
25
|
+
this.baseUrl = (options.baseUrl ?? process.env.X12I_SDK_URL ?? DEFAULT_SERVICE_URL).replace(/\/$/, "");
|
|
26
|
+
this.identityPath = options.identityPath ?? defaultIdentityPath();
|
|
27
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
92
28
|
}
|
|
93
|
-
async
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
29
|
+
async search(task) {
|
|
30
|
+
try {
|
|
31
|
+
const body = await this.send("POST", "/v1/recommend", JSON.stringify({ task }), { "content-type": "application/json" });
|
|
32
|
+
const parsed = body;
|
|
33
|
+
return { source: "service", ranked: parsed.ranked === true, task, results: parsed.results ?? [] };
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof SdkError && error.status >= 400 && error.status < 500)
|
|
37
|
+
throw error;
|
|
38
|
+
const found = await searchPackages(task, { scope: "@x12i/", size: 20, fetch: this.fetchImpl });
|
|
39
|
+
const results = rankPackages(task, found.map((pkg) => ({ name: pkg.name, description: pkg.description, keywords: pkg.keywords })));
|
|
40
|
+
return { source: "registry", ranked: results.some((item) => item.confidence === "high"), task, results };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catalog(cursor, limit = 50) {
|
|
44
|
+
const query = new URLSearchParams();
|
|
45
|
+
if (cursor)
|
|
46
|
+
query.set("cursor", cursor);
|
|
47
|
+
query.set("limit", String(limit));
|
|
48
|
+
return this.send("GET", `/v1/catalog?${query}`);
|
|
49
|
+
}
|
|
50
|
+
package(name) {
|
|
51
|
+
return this.readThrough(packagePath(name), name);
|
|
52
|
+
}
|
|
53
|
+
readme(name) {
|
|
54
|
+
return this.readThrough(`${packagePath(name)}/readme`, name);
|
|
55
|
+
}
|
|
56
|
+
reviews(name) {
|
|
57
|
+
return this.send("GET", `${packagePath(name)}/reviews`);
|
|
58
|
+
}
|
|
59
|
+
async report(input) {
|
|
60
|
+
const fields = usageFields(input);
|
|
61
|
+
const identity = await this.ensureEnrolled();
|
|
62
|
+
return this.signedPost("/v1/reports", { ...fields, sdkVersion: SDK_VERSION }, identity);
|
|
63
|
+
}
|
|
64
|
+
async review(input) {
|
|
65
|
+
const fields = usageFields(input);
|
|
66
|
+
const rating = reviewRating(input.rating);
|
|
67
|
+
const identity = await this.ensureEnrolled();
|
|
68
|
+
return this.signedPost("/v1/reviews", { ...fields, rating, sdkVersion: SDK_VERSION }, identity);
|
|
69
|
+
}
|
|
70
|
+
async status() {
|
|
71
|
+
let identity;
|
|
97
72
|
try {
|
|
98
|
-
|
|
73
|
+
identity = await loadOrCreateIdentity(this.identityPath);
|
|
99
74
|
}
|
|
100
75
|
catch {
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
webFunctionsDeps() {
|
|
105
|
-
return {
|
|
106
|
-
bindings: this.webFunctionBindings,
|
|
107
|
-
executor: this.funcxExecutor,
|
|
108
|
-
hostPolicy: this.hostPolicy,
|
|
109
|
-
checkpoints: this.functionCheckpoints,
|
|
110
|
-
onEvent: (type, data) => {
|
|
111
|
-
this.activeRunEventSink?.(type, data);
|
|
112
|
-
},
|
|
113
|
-
resolvePacket: async (runId) => {
|
|
114
|
-
for (const kind of ["research_packet", "packet", "grounded_answer"]) {
|
|
115
|
-
const items = await this.runStore.listItems(runId, { kind, limit: 1 });
|
|
116
|
-
const data = items.items[0]?.data;
|
|
117
|
-
if (kind === "grounded_answer")
|
|
118
|
-
continue;
|
|
119
|
-
if (data)
|
|
120
|
-
return data;
|
|
121
|
-
}
|
|
122
|
-
// Prefer packet item explicitly
|
|
123
|
-
const packetItems = await this.runStore.listItems(runId, { limit: 20 });
|
|
124
|
-
const packet = packetItems.items.find((i) => i.kind === "packet" || i.kind === "research_packet");
|
|
125
|
-
return packet?.data;
|
|
126
|
-
},
|
|
127
|
-
resolveRunSource: async (runId, sourceId) => {
|
|
128
|
-
const packet = await this.webFunctionsDeps().resolvePacket?.(runId);
|
|
129
|
-
return packet?.sources.find((s) => s.sourceId === sourceId);
|
|
130
|
-
},
|
|
131
|
-
retrieveUrl: async (url, opts) => {
|
|
132
|
-
const fetch = await this.retrieve(url, { strategy: "auto", signal: opts?.signal });
|
|
133
|
-
return {
|
|
134
|
-
sourceId: `src_${createHash("sha256").update(url).digest("hex").slice(0, 10)}`,
|
|
135
|
-
title: fetch.title,
|
|
136
|
-
fetch,
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
research: (req, opts) => this.research(req, opts),
|
|
140
|
-
checkChanges: async ({ url, againstSnapshotId, signal }) => {
|
|
141
|
-
void signal;
|
|
142
|
-
const previousSnapshot = await this.snapshotStore.get(againstSnapshotId);
|
|
143
|
-
return this.checkChanges({ url, previousSnapshot });
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
buildResearchFunctionHooks(request) {
|
|
148
|
-
if (!request.functions || this.funcxExecutor.mode === "disabled")
|
|
149
|
-
return undefined;
|
|
150
|
-
const fn = request.functions;
|
|
151
|
-
const deps = this.webFunctionsDeps();
|
|
152
|
-
return {
|
|
153
|
-
planQueries: fn.queryPlanBindingId
|
|
154
|
-
? async (input) => planQueries(deps, {
|
|
155
|
-
bindingId: fn.queryPlanBindingId,
|
|
156
|
-
question: input.question,
|
|
157
|
-
constraints: input.constraints,
|
|
158
|
-
profileAlternativeId: input.profileAlternativeId ?? fn.profileAlternativeId,
|
|
159
|
-
signal: input.signal,
|
|
160
|
-
})
|
|
161
|
-
: undefined,
|
|
162
|
-
evaluateCandidate: fn.candidateEvaluateBindingId
|
|
163
|
-
? async (input) => evaluateCandidate(deps, {
|
|
164
|
-
bindingId: fn.candidateEvaluateBindingId,
|
|
165
|
-
candidate: input.candidate,
|
|
166
|
-
profileAlternativeId: input.profileAlternativeId ?? fn.profileAlternativeId,
|
|
167
|
-
signal: input.signal,
|
|
168
|
-
})
|
|
169
|
-
: undefined,
|
|
170
|
-
analyzeSource: fn.sourceAnalyzeBindingId
|
|
171
|
-
? async (input) => {
|
|
172
|
-
const result = await analyzeWebSource(deps, {
|
|
173
|
-
bindingId: fn.sourceAnalyzeBindingId,
|
|
174
|
-
profileAlternativeId: input.profileAlternativeId ?? fn.profileAlternativeId,
|
|
175
|
-
source: { researchSource: input.source },
|
|
176
|
-
includeOriginal: false,
|
|
177
|
-
}, { signal: input.signal });
|
|
178
|
-
return {
|
|
179
|
-
analysis: result.analysis,
|
|
180
|
-
warnings: result.warnings,
|
|
181
|
-
errors: result.errors,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
: undefined,
|
|
185
|
-
enrichRecord: fn.recordEnrichBindingId
|
|
186
|
-
? async (input) => {
|
|
187
|
-
const result = await analyzeWebSource(deps, {
|
|
188
|
-
bindingId: fn.recordEnrichBindingId,
|
|
189
|
-
profileAlternativeId: input.profileAlternativeId ?? fn.profileAlternativeId,
|
|
190
|
-
source: { researchSource: input.source },
|
|
191
|
-
enrichBindingId: fn.recordEnrichBindingId,
|
|
192
|
-
includeOriginal: true,
|
|
193
|
-
}, { signal: input.signal });
|
|
194
|
-
return {
|
|
195
|
-
original: result.original,
|
|
196
|
-
enriched: result.enriched,
|
|
197
|
-
conflicts: result.conflicts,
|
|
198
|
-
warnings: result.warnings,
|
|
199
|
-
errors: result.errors,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
: undefined,
|
|
203
|
-
rankEvidence: fn.evidenceRankBindingId
|
|
204
|
-
? async (input) => rankEvidenceBlocks(deps, {
|
|
205
|
-
bindingId: fn.evidenceRankBindingId,
|
|
206
|
-
blocks: input.blocks,
|
|
207
|
-
question: input.question,
|
|
208
|
-
profileAlternativeId: input.profileAlternativeId ?? fn.profileAlternativeId,
|
|
209
|
-
signal: input.signal,
|
|
210
|
-
})
|
|
211
|
-
: undefined,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
use(parser) {
|
|
215
|
-
this.registry.register(parser);
|
|
216
|
-
return this;
|
|
217
|
-
}
|
|
218
|
-
/** Download a URL via Playwright (compatibility default: browser strategy). */
|
|
219
|
-
async fetch(url, options = {}) {
|
|
220
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
221
|
-
return fetchUrl(url, {
|
|
222
|
-
...this.defaultFetchOptions,
|
|
223
|
-
...siteFetchDefaults(url),
|
|
224
|
-
...options,
|
|
225
|
-
browser: browser ?? options.browser,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
/** Strategy-aware multi-format retrieval (default strategy: auto). */
|
|
229
|
-
async retrieve(url, options = {}) {
|
|
230
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
231
|
-
return retrieve(url, {
|
|
232
|
-
...this.defaultFetchOptions,
|
|
233
|
-
...siteFetchDefaults(url),
|
|
234
|
-
strategy: "auto",
|
|
235
|
-
...options,
|
|
236
|
-
browser: browser ?? options.browser,
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
async fetchBatch(request) {
|
|
240
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
241
|
-
return retrieveBatch({
|
|
242
|
-
...request,
|
|
243
|
-
retrieveOptions: {
|
|
244
|
-
...this.defaultFetchOptions,
|
|
245
|
-
strategy: "auto",
|
|
246
|
-
...request.retrieveOptions,
|
|
247
|
-
browser: browser ?? request.retrieveOptions?.browser,
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
async retrieveCached(url, options = {}) {
|
|
252
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
253
|
-
return retrieveWithCache(url, {
|
|
254
|
-
...this.defaultFetchOptions,
|
|
255
|
-
...siteFetchDefaults(url),
|
|
256
|
-
strategy: "auto",
|
|
257
|
-
cache: this.fetchCache,
|
|
258
|
-
...options,
|
|
259
|
-
browser: browser ?? options.browser,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
async classify(url, options = {}) {
|
|
263
|
-
const fetched = await this.fetch(url, options);
|
|
264
|
-
return this.registry.classify(fetched);
|
|
265
|
-
}
|
|
266
|
-
async parse(url, options = {}) {
|
|
267
|
-
const fetched = await this.fetch(url, options);
|
|
268
|
-
return this.registry.parse(fetched);
|
|
269
|
-
}
|
|
270
|
-
async parseAs(type, url, options = {}) {
|
|
271
|
-
const fetched = await this.fetch(url, options);
|
|
272
|
-
return this.registry.parseAs(type, fetched);
|
|
273
|
-
}
|
|
274
|
-
listParsers() {
|
|
275
|
-
return this.registry.list();
|
|
276
|
-
}
|
|
277
|
-
ingest(rootUrl, options = {}) {
|
|
278
|
-
return ingestRepository(rootUrl, options);
|
|
279
|
-
}
|
|
280
|
-
search(request, options = {}) {
|
|
281
|
-
return runSearch(request, {
|
|
282
|
-
provider: this.searchProvider ?? null,
|
|
283
|
-
signal: options.signal,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
searchConfigured() {
|
|
287
|
-
return this.searchProvider !== undefined;
|
|
288
|
-
}
|
|
289
|
-
async fetchSearchResults(searchResult, options = {}) {
|
|
290
|
-
const targets = resolveFetchTargets(searchResult, options);
|
|
291
|
-
const concurrency = Math.max(1, options.concurrency ?? 2);
|
|
292
|
-
const continueOnError = options.continueOnError !== false;
|
|
293
|
-
const pages = [];
|
|
294
|
-
for (let i = 0; i < targets.length; i += concurrency) {
|
|
295
|
-
if (options.signal?.aborted)
|
|
296
|
-
break;
|
|
297
|
-
const batch = targets.slice(i, i + concurrency);
|
|
298
|
-
const settled = await Promise.all(batch.map(async (t) => {
|
|
299
|
-
try {
|
|
300
|
-
const fetched = await this.fetch(t.url, {
|
|
301
|
-
...options.fetchOptions,
|
|
302
|
-
});
|
|
303
|
-
return {
|
|
304
|
-
position: t.position,
|
|
305
|
-
url: t.url,
|
|
306
|
-
title: fetched.title ?? t.title,
|
|
307
|
-
fetch: fetched,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
catch (err) {
|
|
311
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
312
|
-
if (!continueOnError)
|
|
313
|
-
throw err;
|
|
314
|
-
return { position: t.position, url: t.url, title: t.title, error };
|
|
315
|
-
}
|
|
316
|
-
}));
|
|
317
|
-
pages.push(...settled);
|
|
76
|
+
identity = undefined;
|
|
318
77
|
}
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
async searchAndFetch(request) {
|
|
322
|
-
const { top, positions, urls, concurrency, continueOnError, fetchOptions, signal, ...searchRequest } = request;
|
|
323
|
-
const searchResult = await this.search(searchRequest, { signal });
|
|
324
|
-
const pages = await this.fetchSearchResults(searchResult, {
|
|
325
|
-
positions: positions ?? (urls ? undefined : range(1, top ?? 3)),
|
|
326
|
-
urls,
|
|
327
|
-
concurrency,
|
|
328
|
-
continueOnError,
|
|
329
|
-
fetchOptions,
|
|
330
|
-
signal,
|
|
331
|
-
});
|
|
332
|
-
return { search: searchResult, pages };
|
|
333
|
-
}
|
|
334
|
-
async research(request, options = {}) {
|
|
335
|
-
await this.loadBindingsFromEnv();
|
|
336
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
337
|
-
const deps = {
|
|
338
|
-
searchFn: (req, opts) => this.search(req, opts),
|
|
339
|
-
retrieveFn: (url, ropts) => retrieve(url, {
|
|
340
|
-
...this.defaultFetchOptions,
|
|
341
|
-
strategy: "auto",
|
|
342
|
-
...ropts,
|
|
343
|
-
browser,
|
|
344
|
-
}),
|
|
345
|
-
registry: this.registry,
|
|
346
|
-
signal: options.signal,
|
|
347
|
-
functionHooks: this.buildResearchFunctionHooks(request),
|
|
348
|
-
};
|
|
349
|
-
return research(request, deps);
|
|
350
|
-
}
|
|
351
|
-
async startResearch(request, opts) {
|
|
352
|
-
const run = await this.runStore.create({
|
|
353
|
-
type: "research",
|
|
354
|
-
request,
|
|
355
|
-
requestHash: requestHash(request),
|
|
356
|
-
idempotencyKey: opts?.idempotencyKey,
|
|
357
|
-
});
|
|
358
|
-
void this.runResearchJob(run.id, request);
|
|
359
|
-
return run;
|
|
360
|
-
}
|
|
361
|
-
async runResearchJob(runId, request) {
|
|
362
|
-
await this.runStore.update(runId, { status: "planning" });
|
|
363
|
-
await this.runStore.update(runId, { status: "running", phase: "research" });
|
|
78
|
+
let service;
|
|
364
79
|
try {
|
|
365
|
-
|
|
366
|
-
const status = packet.status === "completed"
|
|
367
|
-
? "completed"
|
|
368
|
-
: packet.status === "failed"
|
|
369
|
-
? "failed"
|
|
370
|
-
: packet.status === "cancelled"
|
|
371
|
-
? "cancelled"
|
|
372
|
-
: packet.status === "partial"
|
|
373
|
-
? "partial"
|
|
374
|
-
: "completed_with_errors";
|
|
375
|
-
await this.runStore.update(runId, { status });
|
|
376
|
-
await this.runStore.appendItem?.(runId, {
|
|
377
|
-
kind: "packet",
|
|
378
|
-
data: packet,
|
|
379
|
-
});
|
|
80
|
+
service = await this.send("GET", "/v1/status");
|
|
380
81
|
}
|
|
381
|
-
catch (
|
|
382
|
-
|
|
383
|
-
status: "failed",
|
|
384
|
-
errors: [
|
|
385
|
-
{
|
|
386
|
-
code: "PARSER_FAILED",
|
|
387
|
-
message: err instanceof Error ? err.message : String(err),
|
|
388
|
-
phase: "export",
|
|
389
|
-
retryable: false,
|
|
390
|
-
},
|
|
391
|
-
],
|
|
392
|
-
});
|
|
82
|
+
catch (error) {
|
|
83
|
+
service = { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
393
84
|
}
|
|
394
|
-
}
|
|
395
|
-
async crawl(request, options = {}) {
|
|
396
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
397
|
-
return crawl(request, {
|
|
398
|
-
retrieve: (url, ropts) => retrieve(url, {
|
|
399
|
-
...this.defaultFetchOptions,
|
|
400
|
-
strategy: "auto",
|
|
401
|
-
includeLinks: true,
|
|
402
|
-
...ropts,
|
|
403
|
-
browser,
|
|
404
|
-
}),
|
|
405
|
-
signal: options.signal,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
async startCrawl(request, opts) {
|
|
409
|
-
const run = await this.runStore.create({
|
|
410
|
-
type: "crawl",
|
|
411
|
-
request,
|
|
412
|
-
requestHash: requestHash(request),
|
|
413
|
-
idempotencyKey: opts?.idempotencyKey,
|
|
414
|
-
});
|
|
415
|
-
void this.runCrawlJob(run.id, request);
|
|
416
|
-
return run;
|
|
417
|
-
}
|
|
418
|
-
async runCrawlJob(runId, request) {
|
|
419
|
-
await this.runStore.update(runId, { status: "running", phase: "crawl" });
|
|
420
|
-
try {
|
|
421
|
-
const result = await this.crawl(request);
|
|
422
|
-
await this.runStore.update(runId, {
|
|
423
|
-
status: result.terminationReason === "cancelled" ? "cancelled" : "completed",
|
|
424
|
-
usage: {
|
|
425
|
-
pagesFetched: result.usage.pagesFetched,
|
|
426
|
-
bytesFetched: result.usage.bytesFetched,
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
await this.runStore.appendItem?.(runId, {
|
|
430
|
-
kind: "crawl_result",
|
|
431
|
-
data: result,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
catch (err) {
|
|
435
|
-
await this.runStore.update(runId, {
|
|
436
|
-
status: "failed",
|
|
437
|
-
errors: [
|
|
438
|
-
{
|
|
439
|
-
code: "PARSER_FAILED",
|
|
440
|
-
message: err instanceof Error ? err.message : String(err),
|
|
441
|
-
phase: "crawl",
|
|
442
|
-
retryable: false,
|
|
443
|
-
},
|
|
444
|
-
],
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
async extract(request) {
|
|
449
|
-
const browser = this.reuseBrowser ? await this.getBrowser() : undefined;
|
|
450
|
-
return extract(request, {
|
|
451
|
-
registry: this.registry,
|
|
452
|
-
adapter: this.extractionAdapter,
|
|
453
|
-
retrieveFn: (url, opts) => retrieve(url, {
|
|
454
|
-
...this.defaultFetchOptions,
|
|
455
|
-
strategy: "auto",
|
|
456
|
-
...opts,
|
|
457
|
-
browser,
|
|
458
|
-
}),
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
async checkChanges(request) {
|
|
462
|
-
let current = request.current;
|
|
463
|
-
if (!current && request.url) {
|
|
464
|
-
current = await this.retrieve(request.url, { strategy: "auto" });
|
|
465
|
-
if (current.canonicalUrl) {
|
|
466
|
-
await this.snapshotStore.put({
|
|
467
|
-
canonicalUrl: current.canonicalUrl,
|
|
468
|
-
markdown: current.markdown,
|
|
469
|
-
rawContentHash: current.rawContentHash,
|
|
470
|
-
normalizedContentHash: current.normalizedContentHash,
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
let previousSnapshot = request.previousSnapshot;
|
|
475
|
-
if (!previousSnapshot && !request.previous && current?.canonicalUrl) {
|
|
476
|
-
previousSnapshot = await this.snapshotStore.latestForUrl(current.canonicalUrl);
|
|
477
|
-
}
|
|
478
|
-
return checkChanges({ ...request, current, previousSnapshot });
|
|
479
|
-
}
|
|
480
|
-
async analyze(request, opts) {
|
|
481
|
-
await this.loadBindingsFromEnv();
|
|
482
|
-
return analyzeWebSource(this.webFunctionsDeps(), request, opts);
|
|
483
|
-
}
|
|
484
|
-
async answer(request, opts) {
|
|
485
|
-
await this.loadBindingsFromEnv();
|
|
486
|
-
return answerFromWeb(this.webFunctionsDeps(), request, opts);
|
|
487
|
-
}
|
|
488
|
-
async compare(request, opts) {
|
|
489
|
-
await this.loadBindingsFromEnv();
|
|
490
|
-
return compareWebSubjects(this.webFunctionsDeps(), request, opts);
|
|
491
|
-
}
|
|
492
|
-
async analyzeChanges(request, opts) {
|
|
493
|
-
await this.loadBindingsFromEnv();
|
|
494
|
-
return analyzeWebChanges(this.webFunctionsDeps(), request, opts);
|
|
495
|
-
}
|
|
496
|
-
async auditResearch(request, opts) {
|
|
497
|
-
await this.loadBindingsFromEnv();
|
|
498
|
-
return auditWebResearch(this.webFunctionsDeps(), request, opts);
|
|
499
|
-
}
|
|
500
|
-
async listWebFunctions(options) {
|
|
501
|
-
await this.loadBindingsFromEnv();
|
|
502
|
-
const listed = listBindings(this.webFunctionBindings, { role: options?.role });
|
|
503
|
-
const statuses = await Promise.all(listed.map(async (binding) => {
|
|
504
|
-
const compat = await validateBindingCompatibility(binding, this.funcxExecutor, this.hostPolicy);
|
|
505
|
-
return { binding, ...compat };
|
|
506
|
-
}));
|
|
507
85
|
return {
|
|
508
|
-
|
|
509
|
-
|
|
86
|
+
sdkVersion: SDK_VERSION,
|
|
87
|
+
serviceUrl: this.baseUrl,
|
|
88
|
+
identity: identity ? { fingerprint: identity.fingerprint, enrolled: identity.enrolledService === this.baseUrl } : null,
|
|
89
|
+
service,
|
|
510
90
|
};
|
|
511
91
|
}
|
|
512
|
-
async
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
};
|
|
542
|
-
await this.runStore.appendEvent(runId, { type: "function_started", data: { bindingId: request.synthesisBindingId } });
|
|
92
|
+
async ensureEnrolled() {
|
|
93
|
+
const identity = await loadOrCreateIdentity(this.identityPath);
|
|
94
|
+
if (identity.enrolledService === this.baseUrl)
|
|
95
|
+
return identity;
|
|
96
|
+
const challenge = await this.send("POST", "/v1/enrollment/challenges", JSON.stringify({ publicKey: identity.publicKey }), {
|
|
97
|
+
"content-type": "application/json",
|
|
98
|
+
});
|
|
99
|
+
const work = await solveWork(challenge.challenge, challenge.workBits ?? 0);
|
|
100
|
+
await this.signedPost("/v1/installs/enroll", { challenge: challenge.challenge, work }, identity);
|
|
101
|
+
identity.enrolledService = this.baseUrl;
|
|
102
|
+
await saveIdentity(this.identityPath, identity);
|
|
103
|
+
return identity;
|
|
104
|
+
}
|
|
105
|
+
async signedPost(path, payload, identity) {
|
|
106
|
+
const body = JSON.stringify(payload);
|
|
107
|
+
const timestamp = String(Date.now());
|
|
108
|
+
const nonce = randomNonce();
|
|
109
|
+
const message = await canonicalRequest("POST", path, timestamp, nonce, body);
|
|
110
|
+
const signature = await sign(identity.privateKey, message);
|
|
111
|
+
return this.send("POST", path, body, {
|
|
112
|
+
"content-type": "application/json",
|
|
113
|
+
"x-x12i-key": identity.publicKey,
|
|
114
|
+
"x-x12i-timestamp": timestamp,
|
|
115
|
+
"x-x12i-nonce": nonce,
|
|
116
|
+
"x-x12i-signature": signature,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async readThrough(path, name) {
|
|
120
|
+
let cachedBody;
|
|
543
121
|
try {
|
|
544
|
-
|
|
545
|
-
const existing = await this.runStore.listItems(runId, { kind: "grounded_answer", limit: 1 });
|
|
546
|
-
if (existing.items[0]?.data) {
|
|
547
|
-
const result = existing.items[0].data;
|
|
548
|
-
await this.runStore.update(runId, {
|
|
549
|
-
status: result.status === "failed"
|
|
550
|
-
? "failed"
|
|
551
|
-
: result.status === "completed_with_errors"
|
|
552
|
-
? "completed_with_errors"
|
|
553
|
-
: "completed",
|
|
554
|
-
});
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
const result = await this.answer(request);
|
|
558
|
-
await this.runStore.appendItem?.(runId, { kind: "grounded_answer", data: result });
|
|
559
|
-
await this.runStore.appendEvent(runId, {
|
|
560
|
-
type: result.audit ? "audit_completed" : "function_completed",
|
|
561
|
-
data: { status: result.status },
|
|
562
|
-
});
|
|
563
|
-
await this.runStore.update(runId, {
|
|
564
|
-
status: result.status === "failed" ? "failed" : result.status === "completed_with_errors" ? "completed_with_errors" : "completed",
|
|
565
|
-
});
|
|
122
|
+
cachedBody = await this.send("GET", path);
|
|
566
123
|
}
|
|
567
|
-
catch (
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
{
|
|
572
|
-
code: "FUNCTION_INVOCATION_FAILED",
|
|
573
|
-
message: err instanceof Error ? err.message : String(err),
|
|
574
|
-
phase: "function",
|
|
575
|
-
retryable: false,
|
|
576
|
-
},
|
|
577
|
-
],
|
|
578
|
-
});
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error instanceof SdkError && error.status >= 400 && error.status < 500)
|
|
126
|
+
throw error;
|
|
127
|
+
return this.readmeFromRegistry(name);
|
|
579
128
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
async startCompare(request, opts) {
|
|
585
|
-
const run = await this.runStore.create({
|
|
586
|
-
type: "compare",
|
|
587
|
-
request,
|
|
588
|
-
requestHash: requestHash(request),
|
|
589
|
-
idempotencyKey: opts?.idempotencyKey,
|
|
590
|
-
});
|
|
591
|
-
void this.runCompareJob(run.id, request);
|
|
592
|
-
return run;
|
|
593
|
-
}
|
|
594
|
-
async runCompareJob(runId, request) {
|
|
595
|
-
await this.runStore.update(runId, { status: "running", phase: "function" });
|
|
596
|
-
this.activeRunEventSink = (type, data) => {
|
|
597
|
-
void this.runStore.appendEvent(runId, { type: type, data });
|
|
598
|
-
};
|
|
599
|
-
await this.runStore.appendEvent(runId, {
|
|
600
|
-
type: "function_started",
|
|
601
|
-
data: { bindingId: request.comparisonBindingId },
|
|
602
|
-
});
|
|
129
|
+
const cached = snapshotPackage(cachedBody);
|
|
130
|
+
if (cached.readmeStatus === "fresh")
|
|
131
|
+
return packageView(cachedBody, cached, false, "service");
|
|
603
132
|
try {
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
status: result.status === "failed"
|
|
609
|
-
? "failed"
|
|
610
|
-
: result.status === "completed_with_errors"
|
|
611
|
-
? "completed_with_errors"
|
|
612
|
-
: "completed",
|
|
613
|
-
});
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
const result = await this.compare(request);
|
|
617
|
-
await this.runStore.appendItem?.(runId, { kind: "web_comparison", data: result });
|
|
618
|
-
await this.runStore.appendEvent(runId, {
|
|
619
|
-
type: result.audit ? "audit_completed" : "function_completed",
|
|
620
|
-
data: { status: result.status },
|
|
621
|
-
});
|
|
622
|
-
await this.runStore.update(runId, {
|
|
623
|
-
status: result.status === "failed"
|
|
624
|
-
? "failed"
|
|
625
|
-
: result.status === "completed_with_errors"
|
|
626
|
-
? "completed_with_errors"
|
|
627
|
-
: "completed",
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
catch (err) {
|
|
631
|
-
await this.runStore.update(runId, {
|
|
632
|
-
status: "failed",
|
|
633
|
-
errors: [
|
|
634
|
-
{
|
|
635
|
-
code: "FUNCTION_INVOCATION_FAILED",
|
|
636
|
-
message: err instanceof Error ? err.message : String(err),
|
|
637
|
-
phase: "function",
|
|
638
|
-
retryable: false,
|
|
639
|
-
},
|
|
640
|
-
],
|
|
641
|
-
});
|
|
133
|
+
const freshBody = await this.send("GET", `${path}?update=1`);
|
|
134
|
+
const fresh = snapshotPackage(freshBody);
|
|
135
|
+
const updated = fresh.readme !== cached.readme || fresh.version !== cached.version || fresh.description !== cached.description;
|
|
136
|
+
return packageView(freshBody, cached, updated, "service");
|
|
642
137
|
}
|
|
643
|
-
|
|
644
|
-
|
|
138
|
+
catch (error) {
|
|
139
|
+
return { ...packageView(cachedBody, cached, false, "service"), refreshError: error instanceof Error ? error.message : String(error) };
|
|
645
140
|
}
|
|
646
141
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
142
|
+
async readmeFromRegistry(name) {
|
|
143
|
+
const info = await getReadme(name, { fetch: this.fetchImpl });
|
|
144
|
+
const cached = { readme: "", readmeStatus: "missing", version: null, description: "", modifiedAt: null, refreshedAt: null };
|
|
145
|
+
return {
|
|
146
|
+
name: info.name,
|
|
147
|
+
readme: info.readme,
|
|
148
|
+
readmeStatus: "fresh",
|
|
149
|
+
version: info.version,
|
|
150
|
+
description: info.description,
|
|
151
|
+
modifiedAt: info.modifiedAt,
|
|
152
|
+
refreshedAt: null,
|
|
153
|
+
updated: Boolean(info.readme),
|
|
154
|
+
source: "registry",
|
|
155
|
+
cached,
|
|
156
|
+
detail: { ...info },
|
|
157
|
+
};
|
|
655
158
|
}
|
|
656
|
-
async
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (!["partial", "cancelled", "failed"].includes(run.status)) {
|
|
661
|
-
throw new Error("RUN_NOT_RESUMABLE");
|
|
662
|
-
}
|
|
663
|
-
await this.runStore.update(runId, { status: "queued", cancellationRequested: false });
|
|
664
|
-
if (run.type === "research") {
|
|
665
|
-
void this.runResearchJob(runId, run.request);
|
|
666
|
-
}
|
|
667
|
-
else if (run.type === "crawl") {
|
|
668
|
-
void this.runCrawlJob(runId, run.request);
|
|
669
|
-
}
|
|
670
|
-
else if (run.type === "answer") {
|
|
671
|
-
void this.runAnswerJob(runId, run.request);
|
|
159
|
+
async send(method, path, body, headers = {}) {
|
|
160
|
+
let response;
|
|
161
|
+
try {
|
|
162
|
+
response = await this.fetchImpl(`${this.baseUrl}${path}`, { method, headers, body });
|
|
672
163
|
}
|
|
673
|
-
|
|
674
|
-
|
|
164
|
+
catch (error) {
|
|
165
|
+
throw new SdkError(error instanceof Error ? error.message : "Service unreachable");
|
|
675
166
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
async close() {
|
|
683
|
-
if (this.browserPromise) {
|
|
684
|
-
const browser = await this.browserPromise;
|
|
685
|
-
await browser.close();
|
|
686
|
-
this.browserPromise = undefined;
|
|
167
|
+
const text = await response.text();
|
|
168
|
+
const parsed = text ? safeJson(text) : {};
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const record = parsed;
|
|
171
|
+
throw new SdkError(record.error?.message ?? `Service returned ${response.status}`, response.status, record.error?.code ?? "HTTP");
|
|
687
172
|
}
|
|
688
|
-
|
|
689
|
-
void this.ownsSnapshots;
|
|
690
|
-
void this.ownsRuns;
|
|
173
|
+
return parsed;
|
|
691
174
|
}
|
|
692
175
|
}
|
|
693
|
-
function
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
176
|
+
function snapshotPackage(body) {
|
|
177
|
+
const status = body.readmeStatus;
|
|
178
|
+
return {
|
|
179
|
+
readme: typeof body.readme === "string" ? body.readme : "",
|
|
180
|
+
readmeStatus: status === "fresh" || status === "stale" || status === "missing" ? status : "missing",
|
|
181
|
+
version: typeof body.version === "string" ? body.version : null,
|
|
182
|
+
description: typeof body.description === "string" ? body.description : "",
|
|
183
|
+
modifiedAt: typeof body.modifiedAt === "string" ? body.modifiedAt : null,
|
|
184
|
+
refreshedAt: typeof body.refreshedAt === "string" ? body.refreshedAt : null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function packageView(body, cached, updated, source) {
|
|
188
|
+
const current = updated || cached.readmeStatus !== "fresh" ? snapshotPackage(body) : cached;
|
|
189
|
+
const name = typeof body.name === "string" ? body.name : "";
|
|
190
|
+
return {
|
|
191
|
+
name,
|
|
192
|
+
readme: current.readme,
|
|
193
|
+
readmeStatus: current.readmeStatus,
|
|
194
|
+
version: current.version,
|
|
195
|
+
description: current.description,
|
|
196
|
+
modifiedAt: current.modifiedAt,
|
|
197
|
+
refreshedAt: current.refreshedAt,
|
|
198
|
+
updated,
|
|
199
|
+
source,
|
|
200
|
+
cached,
|
|
201
|
+
detail: body,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function packagePath(name) {
|
|
205
|
+
if (name.startsWith("@")) {
|
|
206
|
+
const slash = name.indexOf("/");
|
|
207
|
+
if (slash > 1)
|
|
208
|
+
return `/v1/packages/${encodeURIComponent(name.slice(0, slash))}/${encodeURIComponent(name.slice(slash + 1))}`;
|
|
701
209
|
}
|
|
702
|
-
return
|
|
210
|
+
return `/v1/packages/${encodeURIComponent(name)}`;
|
|
703
211
|
}
|
|
704
|
-
function
|
|
705
|
-
|
|
706
|
-
return
|
|
707
|
-
const match = searchResult.organic.find((o) => o.url === url);
|
|
708
|
-
return { url, position: match?.position, title: match?.title };
|
|
709
|
-
});
|
|
212
|
+
function safeJson(text) {
|
|
213
|
+
try {
|
|
214
|
+
return JSON.parse(text);
|
|
710
215
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
: searchResult.organic.map((o) => o.position);
|
|
714
|
-
const out = [];
|
|
715
|
-
for (const pos of positions) {
|
|
716
|
-
const hit = searchResult.organic.find((o) => o.position === pos);
|
|
717
|
-
if (hit)
|
|
718
|
-
out.push({ url: hit.url, position: hit.position, title: hit.title });
|
|
216
|
+
catch {
|
|
217
|
+
return { text };
|
|
719
218
|
}
|
|
720
|
-
return out;
|
|
721
|
-
}
|
|
722
|
-
function range(start, endInclusive) {
|
|
723
|
-
const out = [];
|
|
724
|
-
for (let i = start; i <= endInclusive; i++)
|
|
725
|
-
out.push(i);
|
|
726
|
-
return out;
|
|
727
219
|
}
|
|
728
220
|
//# sourceMappingURL=client.js.map
|