@pulseai/sdk 0.1.2 → 0.1.4
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/index.d.ts +154 -33
- package/dist/index.js +635 -84
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -13,6 +13,9 @@ var megaethTestnet = defineChain({
|
|
|
13
13
|
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
14
14
|
rpcUrls: {
|
|
15
15
|
default: { http: ["https://carrot.megaeth.com/rpc"] }
|
|
16
|
+
},
|
|
17
|
+
blockExplorers: {
|
|
18
|
+
default: { name: "Blockscout", url: "https://megaeth-testnet-v2.blockscout.com" }
|
|
16
19
|
}
|
|
17
20
|
});
|
|
18
21
|
var megaethMainnet = defineChain({
|
|
@@ -21,6 +24,9 @@ var megaethMainnet = defineChain({
|
|
|
21
24
|
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
22
25
|
rpcUrls: {
|
|
23
26
|
default: { http: ["https://mainnet.megaeth.com/rpc"] }
|
|
27
|
+
},
|
|
28
|
+
blockExplorers: {
|
|
29
|
+
default: { name: "Blockscout", url: "https://megaeth.blockscout.com" }
|
|
24
30
|
}
|
|
25
31
|
});
|
|
26
32
|
|
|
@@ -36,15 +42,15 @@ var TESTNET_ADDRESSES = {
|
|
|
36
42
|
};
|
|
37
43
|
var MAINNET_ADDRESSES = {
|
|
38
44
|
pulseExtension: "0xf1616D2008c4Ff5Ed7BDBd448DAE68615b7A71f0",
|
|
39
|
-
serviceMarketplace: "
|
|
45
|
+
serviceMarketplace: "0x0573C91396184323979F3f7b322C694932F91D44",
|
|
40
46
|
jobEngine: "0xb5E56262b55aE453E8B16470228F0a5Ef617FF67",
|
|
41
47
|
feeDistributor: "0x51EdD8E4C4B423b952821fc9e2a7dad15a858B56",
|
|
42
48
|
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
43
49
|
reputationRegistry: "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
|
|
44
50
|
usdm: "0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7",
|
|
45
|
-
buyerRelay: "
|
|
51
|
+
buyerRelay: "0x843B5B4AD44C174BAB8bBe11eb001aD7fE70647A"
|
|
46
52
|
};
|
|
47
|
-
var PLATFORM_BUYER_AGENT_ID =
|
|
53
|
+
var PLATFORM_BUYER_AGENT_ID = 8155n;
|
|
48
54
|
var DEFAULT_INDEXER_URLS = {
|
|
49
55
|
testnet: "https://pulse-indexer.up.railway.app",
|
|
50
56
|
mainnet: "https://pulse-indexer.up.railway.app"
|
|
@@ -1034,6 +1040,11 @@ var serviceMarketplaceAbi = [
|
|
|
1034
1040
|
"type": "uint32",
|
|
1035
1041
|
"internalType": "uint32"
|
|
1036
1042
|
},
|
|
1043
|
+
{
|
|
1044
|
+
"name": "name",
|
|
1045
|
+
"type": "string",
|
|
1046
|
+
"internalType": "string"
|
|
1047
|
+
},
|
|
1037
1048
|
{
|
|
1038
1049
|
"name": "description",
|
|
1039
1050
|
"type": "string",
|
|
@@ -1104,6 +1115,11 @@ var serviceMarketplaceAbi = [
|
|
|
1104
1115
|
"type": "uint32",
|
|
1105
1116
|
"internalType": "uint32"
|
|
1106
1117
|
},
|
|
1118
|
+
{
|
|
1119
|
+
"name": "name",
|
|
1120
|
+
"type": "string",
|
|
1121
|
+
"internalType": "string"
|
|
1122
|
+
},
|
|
1107
1123
|
{
|
|
1108
1124
|
"name": "description",
|
|
1109
1125
|
"type": "string",
|
|
@@ -1156,6 +1172,11 @@ var serviceMarketplaceAbi = [
|
|
|
1156
1172
|
"type": "uint32",
|
|
1157
1173
|
"internalType": "uint32"
|
|
1158
1174
|
},
|
|
1175
|
+
{
|
|
1176
|
+
"name": "name",
|
|
1177
|
+
"type": "string",
|
|
1178
|
+
"internalType": "string"
|
|
1179
|
+
},
|
|
1159
1180
|
{
|
|
1160
1181
|
"name": "description",
|
|
1161
1182
|
"type": "string",
|
|
@@ -1165,6 +1186,24 @@ var serviceMarketplaceAbi = [
|
|
|
1165
1186
|
"outputs": [],
|
|
1166
1187
|
"stateMutability": "nonpayable"
|
|
1167
1188
|
},
|
|
1189
|
+
{
|
|
1190
|
+
"type": "function",
|
|
1191
|
+
"name": "updateOfferingSchema",
|
|
1192
|
+
"inputs": [
|
|
1193
|
+
{
|
|
1194
|
+
"name": "offeringId",
|
|
1195
|
+
"type": "uint256",
|
|
1196
|
+
"internalType": "uint256"
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
"name": "requirementsSchemaURI",
|
|
1200
|
+
"type": "string",
|
|
1201
|
+
"internalType": "string"
|
|
1202
|
+
}
|
|
1203
|
+
],
|
|
1204
|
+
"outputs": [],
|
|
1205
|
+
"stateMutability": "nonpayable"
|
|
1206
|
+
},
|
|
1168
1207
|
{
|
|
1169
1208
|
"type": "event",
|
|
1170
1209
|
"name": "OfferingActivated",
|
|
@@ -1222,6 +1261,25 @@ var serviceMarketplaceAbi = [
|
|
|
1222
1261
|
],
|
|
1223
1262
|
"anonymous": false
|
|
1224
1263
|
},
|
|
1264
|
+
{
|
|
1265
|
+
"type": "event",
|
|
1266
|
+
"name": "OfferingSchemaUpdated",
|
|
1267
|
+
"inputs": [
|
|
1268
|
+
{
|
|
1269
|
+
"name": "offeringId",
|
|
1270
|
+
"type": "uint256",
|
|
1271
|
+
"indexed": true,
|
|
1272
|
+
"internalType": "uint256"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
"name": "requirementsSchemaURI",
|
|
1276
|
+
"type": "string",
|
|
1277
|
+
"indexed": false,
|
|
1278
|
+
"internalType": "string"
|
|
1279
|
+
}
|
|
1280
|
+
],
|
|
1281
|
+
"anonymous": false
|
|
1282
|
+
},
|
|
1225
1283
|
{
|
|
1226
1284
|
"type": "event",
|
|
1227
1285
|
"name": "OfferingUpdated",
|
|
@@ -1285,6 +1343,7 @@ async function listOffering(client, params) {
|
|
|
1285
1343
|
params.serviceType,
|
|
1286
1344
|
params.priceUSDm,
|
|
1287
1345
|
params.slaMinutes,
|
|
1346
|
+
params.name,
|
|
1288
1347
|
params.description,
|
|
1289
1348
|
params.requirementsSchemaURI ?? ""
|
|
1290
1349
|
]
|
|
@@ -1296,12 +1355,20 @@ async function listOffering(client, params) {
|
|
|
1296
1355
|
const offeringId = offeringLog?.topics[1] ? BigInt(offeringLog.topics[1]) : 0n;
|
|
1297
1356
|
return { offeringId, txHash };
|
|
1298
1357
|
}
|
|
1299
|
-
async function updateOffering(client, offeringId, priceUSDm, slaMinutes, description) {
|
|
1358
|
+
async function updateOffering(client, offeringId, priceUSDm, slaMinutes, name, description) {
|
|
1300
1359
|
return write(client, {
|
|
1301
1360
|
address: client.addresses.serviceMarketplace,
|
|
1302
1361
|
abi: serviceMarketplaceAbi,
|
|
1303
1362
|
functionName: "updateOffering",
|
|
1304
|
-
args: [offeringId, priceUSDm, slaMinutes, description]
|
|
1363
|
+
args: [offeringId, priceUSDm, slaMinutes, name, description]
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
async function updateOfferingSchema(client, offeringId, requirementsSchemaURI) {
|
|
1367
|
+
return write(client, {
|
|
1368
|
+
address: client.addresses.serviceMarketplace,
|
|
1369
|
+
abi: serviceMarketplaceAbi,
|
|
1370
|
+
functionName: "updateOfferingSchema",
|
|
1371
|
+
args: [offeringId, requirementsSchemaURI]
|
|
1305
1372
|
});
|
|
1306
1373
|
}
|
|
1307
1374
|
async function deactivateOffering(client, offeringId) {
|
|
@@ -1337,6 +1404,9 @@ async function getOfferingCount(client) {
|
|
|
1337
1404
|
});
|
|
1338
1405
|
}
|
|
1339
1406
|
|
|
1407
|
+
// src/jobs/index.ts
|
|
1408
|
+
import { maxUint256 } from "viem";
|
|
1409
|
+
|
|
1340
1410
|
// src/abis/JobEngine.ts
|
|
1341
1411
|
var jobEngineAbi = [
|
|
1342
1412
|
{
|
|
@@ -1812,6 +1882,19 @@ var jobEngineAbi = [
|
|
|
1812
1882
|
],
|
|
1813
1883
|
"stateMutability": "view"
|
|
1814
1884
|
},
|
|
1885
|
+
{
|
|
1886
|
+
"type": "function",
|
|
1887
|
+
"name": "setServiceMarketplace",
|
|
1888
|
+
"inputs": [
|
|
1889
|
+
{
|
|
1890
|
+
"name": "newMarketplace",
|
|
1891
|
+
"type": "address",
|
|
1892
|
+
"internalType": "address"
|
|
1893
|
+
}
|
|
1894
|
+
],
|
|
1895
|
+
"outputs": [],
|
|
1896
|
+
"stateMutability": "nonpayable"
|
|
1897
|
+
},
|
|
1815
1898
|
{
|
|
1816
1899
|
"type": "function",
|
|
1817
1900
|
"name": "settle",
|
|
@@ -2088,6 +2171,25 @@ var jobEngineAbi = [
|
|
|
2088
2171
|
],
|
|
2089
2172
|
"anonymous": false
|
|
2090
2173
|
},
|
|
2174
|
+
{
|
|
2175
|
+
"type": "event",
|
|
2176
|
+
"name": "ServiceMarketplaceUpdated",
|
|
2177
|
+
"inputs": [
|
|
2178
|
+
{
|
|
2179
|
+
"name": "oldMarketplace",
|
|
2180
|
+
"type": "address",
|
|
2181
|
+
"indexed": true,
|
|
2182
|
+
"internalType": "address"
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
"name": "newMarketplace",
|
|
2186
|
+
"type": "address",
|
|
2187
|
+
"indexed": true,
|
|
2188
|
+
"internalType": "address"
|
|
2189
|
+
}
|
|
2190
|
+
],
|
|
2191
|
+
"anonymous": false
|
|
2192
|
+
},
|
|
2091
2193
|
{
|
|
2092
2194
|
"type": "event",
|
|
2093
2195
|
"name": "Upgraded",
|
|
@@ -2339,6 +2441,7 @@ async function signMemo(walletClient, params) {
|
|
|
2339
2441
|
|
|
2340
2442
|
// src/jobs/index.ts
|
|
2341
2443
|
async function createJob(client, params) {
|
|
2444
|
+
const { account } = requireWallet(client);
|
|
2342
2445
|
const offering = await client.publicClient.readContract({
|
|
2343
2446
|
address: client.addresses.serviceMarketplace,
|
|
2344
2447
|
abi: serviceMarketplaceAbi,
|
|
@@ -2346,13 +2449,21 @@ async function createJob(client, params) {
|
|
|
2346
2449
|
args: [params.offeringId]
|
|
2347
2450
|
});
|
|
2348
2451
|
const price = offering.priceUSDm;
|
|
2349
|
-
const
|
|
2452
|
+
const allowance = await client.publicClient.readContract({
|
|
2350
2453
|
address: client.addresses.usdm,
|
|
2351
2454
|
abi: erc20Abi,
|
|
2352
|
-
functionName: "
|
|
2353
|
-
args: [client.addresses.jobEngine
|
|
2455
|
+
functionName: "allowance",
|
|
2456
|
+
args: [account.address, client.addresses.jobEngine]
|
|
2354
2457
|
});
|
|
2355
|
-
|
|
2458
|
+
if (allowance < price) {
|
|
2459
|
+
const approveTx = await write(client, {
|
|
2460
|
+
address: client.addresses.usdm,
|
|
2461
|
+
abi: erc20Abi,
|
|
2462
|
+
functionName: "approve",
|
|
2463
|
+
args: [client.addresses.jobEngine, maxUint256]
|
|
2464
|
+
});
|
|
2465
|
+
await client.publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
2466
|
+
}
|
|
2356
2467
|
const txHash = await write(client, {
|
|
2357
2468
|
address: client.addresses.jobEngine,
|
|
2358
2469
|
abi: jobEngineAbi,
|
|
@@ -2666,39 +2777,14 @@ async function deployJobTerms(client, agentId, params) {
|
|
|
2666
2777
|
const { masterAddress } = await deployWarrenMaster(client, agentId, pageAddress, contentBytes.length, 0);
|
|
2667
2778
|
return { masterAddress, pageAddress, hash, txHash };
|
|
2668
2779
|
}
|
|
2669
|
-
function
|
|
2670
|
-
let result = content.trim();
|
|
2671
|
-
result = result.replace(/^```\w*\n?/, "");
|
|
2672
|
-
result = result.replace(/\n?```\s*$/, "");
|
|
2673
|
-
return result;
|
|
2674
|
-
}
|
|
2675
|
-
async function deployDeliverable(client, agentId, jobId, params, indexerUrl) {
|
|
2780
|
+
async function deployDeliverable(client, jobId, params, indexerUrl) {
|
|
2676
2781
|
const { json, hash } = createDeliverable(params);
|
|
2677
|
-
const
|
|
2678
|
-
const contentBytes = new TextEncoder().encode(warrenContent);
|
|
2679
|
-
const { pageAddress, txHash } = await deployWarrenPage(client, warrenContent);
|
|
2680
|
-
const { masterAddress } = await deployWarrenMaster(client, agentId, pageAddress, contentBytes.length, 1);
|
|
2681
|
-
await write(client, {
|
|
2782
|
+
const txHash = await write(client, {
|
|
2682
2783
|
address: client.addresses.jobEngine,
|
|
2683
2784
|
abi: jobEngineAbi,
|
|
2684
2785
|
functionName: "submitDeliverable",
|
|
2685
2786
|
args: [jobId, hash]
|
|
2686
2787
|
});
|
|
2687
|
-
if (indexerUrl) {
|
|
2688
|
-
try {
|
|
2689
|
-
await fetch(`${indexerUrl}/warren-links`, {
|
|
2690
|
-
method: "POST",
|
|
2691
|
-
headers: { "Content-Type": "application/json" },
|
|
2692
|
-
body: JSON.stringify({
|
|
2693
|
-
jobId: Number(jobId),
|
|
2694
|
-
linkType: "deliverable",
|
|
2695
|
-
masterAddress,
|
|
2696
|
-
contentHash: hash
|
|
2697
|
-
})
|
|
2698
|
-
});
|
|
2699
|
-
} catch {
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
2788
|
if (indexerUrl) {
|
|
2703
2789
|
try {
|
|
2704
2790
|
await fetch(`${indexerUrl.replace(/\/$/, "")}/deliverables`, {
|
|
@@ -2708,24 +2794,23 @@ async function deployDeliverable(client, agentId, jobId, params, indexerUrl) {
|
|
|
2708
2794
|
jobId: Number(jobId),
|
|
2709
2795
|
content: json,
|
|
2710
2796
|
contentHash: hash,
|
|
2711
|
-
storageType: "
|
|
2797
|
+
storageType: "deliverable"
|
|
2712
2798
|
})
|
|
2713
2799
|
});
|
|
2714
2800
|
} catch {
|
|
2715
2801
|
}
|
|
2716
2802
|
}
|
|
2717
|
-
return {
|
|
2803
|
+
return { hash, txHash };
|
|
2718
2804
|
}
|
|
2719
|
-
async function readDeliverable(
|
|
2720
|
-
const res = await fetch(`${indexerUrl}/
|
|
2721
|
-
if (!res.ok) throw new Error(`Failed to fetch
|
|
2805
|
+
async function readDeliverable(jobId, indexerUrl) {
|
|
2806
|
+
const res = await fetch(`${indexerUrl.replace(/\/$/, "")}/deliverables/${Number(jobId)}`);
|
|
2807
|
+
if (!res.ok) throw new Error(`Failed to fetch deliverable for job ${jobId}`);
|
|
2722
2808
|
const { data } = await res.json();
|
|
2723
|
-
if (!data
|
|
2724
|
-
|
|
2725
|
-
if (!verifyContentHash(content, data.deliverable.content_hash)) {
|
|
2809
|
+
if (!data?.content) throw new Error(`No deliverable content for job ${jobId}`);
|
|
2810
|
+
if (!verifyContentHash(data.content, data.content_hash)) {
|
|
2726
2811
|
throw new Error("Deliverable content hash mismatch");
|
|
2727
2812
|
}
|
|
2728
|
-
return JSON.parse(content);
|
|
2813
|
+
return JSON.parse(data.content);
|
|
2729
2814
|
}
|
|
2730
2815
|
async function deployRequirements(client, agentId, jobId, params, indexerUrl) {
|
|
2731
2816
|
const { json, hash } = createRequirements(params);
|
|
@@ -2881,7 +2966,7 @@ async function callOpenAI(params) {
|
|
|
2881
2966
|
role: message.role,
|
|
2882
2967
|
content: message.content
|
|
2883
2968
|
})),
|
|
2884
|
-
|
|
2969
|
+
max_completion_tokens: params.maxTokens
|
|
2885
2970
|
}),
|
|
2886
2971
|
signal: params.signal
|
|
2887
2972
|
});
|
|
@@ -2896,7 +2981,8 @@ async function callOpenAI(params) {
|
|
|
2896
2981
|
throw new Error("OpenAI returned an empty response");
|
|
2897
2982
|
}
|
|
2898
2983
|
const truncated = payload?.choices?.[0]?.finish_reason === "length";
|
|
2899
|
-
|
|
2984
|
+
const usage = payload?.usage ? { inputTokens: payload.usage.prompt_tokens ?? 0, outputTokens: payload.usage.completion_tokens ?? 0 } : void 0;
|
|
2985
|
+
return { content, truncated, raw: payload, usage };
|
|
2900
2986
|
}
|
|
2901
2987
|
async function callAnthropic(params) {
|
|
2902
2988
|
const systemPrompt = params.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
|
|
@@ -2930,7 +3016,8 @@ async function callAnthropic(params) {
|
|
|
2930
3016
|
throw new Error("Anthropic returned an empty response");
|
|
2931
3017
|
}
|
|
2932
3018
|
const truncated = payload?.stop_reason === "max_tokens";
|
|
2933
|
-
|
|
3019
|
+
const usage = payload?.usage ? { inputTokens: payload.usage.input_tokens ?? 0, outputTokens: payload.usage.output_tokens ?? 0 } : void 0;
|
|
3020
|
+
return { content, truncated, raw: payload, usage };
|
|
2934
3021
|
}
|
|
2935
3022
|
async function callGoogle(params) {
|
|
2936
3023
|
const systemPrompt = params.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
|
|
@@ -2965,7 +3052,8 @@ async function callGoogle(params) {
|
|
|
2965
3052
|
throw new Error("Google returned an empty response");
|
|
2966
3053
|
}
|
|
2967
3054
|
const truncated = payload?.candidates?.[0]?.finishReason === "MAX_TOKENS";
|
|
2968
|
-
|
|
3055
|
+
const usage = payload?.usageMetadata ? { inputTokens: payload.usageMetadata.promptTokenCount ?? 0, outputTokens: payload.usageMetadata.candidatesTokenCount ?? 0 } : void 0;
|
|
3056
|
+
return { content, truncated, raw: payload, usage };
|
|
2969
3057
|
}
|
|
2970
3058
|
async function callAI(params) {
|
|
2971
3059
|
if (params.provider === "openai") {
|
|
@@ -2989,8 +3077,8 @@ var RETRY_DELAY_MS = 2e3;
|
|
|
2989
3077
|
var GOOGLE_FALLBACK_MODEL = "gemini-2.5-pro";
|
|
2990
3078
|
var SUPPORTED_PROVIDERS = ["anthropic", "google", "openai"];
|
|
2991
3079
|
var DEFAULT_MODELS = {
|
|
2992
|
-
openai: "gpt-
|
|
2993
|
-
anthropic: "claude-
|
|
3080
|
+
openai: "gpt-5.2",
|
|
3081
|
+
anthropic: "claude-sonnet-4-6",
|
|
2994
3082
|
google: "gemini-3.1-pro-preview"
|
|
2995
3083
|
};
|
|
2996
3084
|
var DEFAULT_SYSTEM_PROMPT = [
|
|
@@ -3034,6 +3122,16 @@ function parseAndValidateUrl(urlString) {
|
|
|
3034
3122
|
}
|
|
3035
3123
|
return url;
|
|
3036
3124
|
}
|
|
3125
|
+
function normalizeWarrenUrl(url) {
|
|
3126
|
+
const host = url.hostname.toLowerCase();
|
|
3127
|
+
if (host !== "thewarren.app" && !host.endsWith(".thewarren.app")) return url;
|
|
3128
|
+
const match = url.pathname.match(/^\/v\/site=(\d+)$/);
|
|
3129
|
+
if (!match) return url;
|
|
3130
|
+
const normalized = new URL(url.origin);
|
|
3131
|
+
normalized.pathname = "/api/source";
|
|
3132
|
+
normalized.searchParams.set("site", match[1]);
|
|
3133
|
+
return normalized;
|
|
3134
|
+
}
|
|
3037
3135
|
function ensureAllowedUrl(url, allowedDomains) {
|
|
3038
3136
|
if (!allowedDomains || allowedDomains.length === 0) return;
|
|
3039
3137
|
if (!isAllowedDomain(url.hostname, allowedDomains)) {
|
|
@@ -3110,6 +3208,52 @@ function getByteLength(content) {
|
|
|
3110
3208
|
var SiteModifierHandler = class {
|
|
3111
3209
|
offeringId;
|
|
3112
3210
|
autoAccept;
|
|
3211
|
+
schema = {
|
|
3212
|
+
version: 1,
|
|
3213
|
+
serviceRequirements: {
|
|
3214
|
+
type: "object",
|
|
3215
|
+
properties: {
|
|
3216
|
+
siteUrl: {
|
|
3217
|
+
type: "string",
|
|
3218
|
+
description: "URL of the site to modify (http/https)"
|
|
3219
|
+
},
|
|
3220
|
+
modificationRequest: {
|
|
3221
|
+
type: "string",
|
|
3222
|
+
description: "What changes to make"
|
|
3223
|
+
},
|
|
3224
|
+
provider: {
|
|
3225
|
+
type: "string",
|
|
3226
|
+
description: "AI provider",
|
|
3227
|
+
enum: ["google", "anthropic", "openai"]
|
|
3228
|
+
},
|
|
3229
|
+
model: {
|
|
3230
|
+
type: "string",
|
|
3231
|
+
description: "Specific model override"
|
|
3232
|
+
}
|
|
3233
|
+
},
|
|
3234
|
+
required: ["siteUrl", "modificationRequest"]
|
|
3235
|
+
},
|
|
3236
|
+
deliverableRequirements: {
|
|
3237
|
+
type: "object",
|
|
3238
|
+
properties: {
|
|
3239
|
+
type: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
description: 'Delivery type, always "inline"',
|
|
3242
|
+
enum: ["inline"]
|
|
3243
|
+
},
|
|
3244
|
+
content: {
|
|
3245
|
+
type: "string",
|
|
3246
|
+
description: "Modified HTML content"
|
|
3247
|
+
},
|
|
3248
|
+
mimeType: {
|
|
3249
|
+
type: "string",
|
|
3250
|
+
description: 'Content type, always "text/html"',
|
|
3251
|
+
enum: ["text/html"]
|
|
3252
|
+
}
|
|
3253
|
+
},
|
|
3254
|
+
required: ["type", "content", "mimeType"]
|
|
3255
|
+
}
|
|
3256
|
+
};
|
|
3113
3257
|
config;
|
|
3114
3258
|
constructor(offeringId, config, options) {
|
|
3115
3259
|
this.offeringId = offeringId;
|
|
@@ -3132,7 +3276,8 @@ var SiteModifierHandler = class {
|
|
|
3132
3276
|
const targetUrl = parseAndValidateUrl(requirements.siteUrl);
|
|
3133
3277
|
ensureAllowedUrl(targetUrl, this.config.allowedDomains);
|
|
3134
3278
|
const provider = requirements.provider ?? this.config.defaultProvider ?? "openai";
|
|
3135
|
-
const
|
|
3279
|
+
const useConfigModel = provider === (this.config.defaultProvider ?? "openai");
|
|
3280
|
+
const model = requirements.model?.trim() || (useConfigModel ? this.config.defaultModel?.trim() : void 0) || DEFAULT_MODELS[provider];
|
|
3136
3281
|
const maxTokens = this.config.maxTokens && this.config.maxTokens > 0 ? this.config.maxTokens : DEFAULT_MAX_TOKENS;
|
|
3137
3282
|
if (!model) {
|
|
3138
3283
|
throw new Error(
|
|
@@ -3235,7 +3380,8 @@ var SiteModifierHandler = class {
|
|
|
3235
3380
|
return {
|
|
3236
3381
|
type: "inline",
|
|
3237
3382
|
content: modifiedHtml,
|
|
3238
|
-
mimeType: "text/html"
|
|
3383
|
+
mimeType: "text/html",
|
|
3384
|
+
usage: aiResponse.usage ? { provider, inputTokens: aiResponse.usage.inputTokens, outputTokens: aiResponse.usage.outputTokens } : void 0
|
|
3239
3385
|
};
|
|
3240
3386
|
}
|
|
3241
3387
|
parseRequirements(requirements) {
|
|
@@ -3246,7 +3392,7 @@ var SiteModifierHandler = class {
|
|
|
3246
3392
|
if (!isNonEmptyString(siteUrl)) {
|
|
3247
3393
|
throw new Error("requirements.siteUrl must be a non-empty string");
|
|
3248
3394
|
}
|
|
3249
|
-
const parsedUrl = parseAndValidateUrl(siteUrl);
|
|
3395
|
+
const parsedUrl = normalizeWarrenUrl(parseAndValidateUrl(siteUrl));
|
|
3250
3396
|
ensureAllowedUrl(parsedUrl, this.config.allowedDomains);
|
|
3251
3397
|
const modificationRequest = requirements.modificationRequest;
|
|
3252
3398
|
if (!isNonEmptyString(modificationRequest)) {
|
|
@@ -3265,7 +3411,7 @@ var SiteModifierHandler = class {
|
|
|
3265
3411
|
throw new Error("requirements.model must be a non-empty string");
|
|
3266
3412
|
}
|
|
3267
3413
|
return {
|
|
3268
|
-
siteUrl:
|
|
3414
|
+
siteUrl: parsedUrl.toString(),
|
|
3269
3415
|
modificationRequest: modificationRequest.trim(),
|
|
3270
3416
|
provider,
|
|
3271
3417
|
model: typeof model === "string" ? model.trim() : void 0
|
|
@@ -3351,6 +3497,334 @@ var SiteModifierHandler = class {
|
|
|
3351
3497
|
}
|
|
3352
3498
|
};
|
|
3353
3499
|
|
|
3500
|
+
// src/handler/prompt-handler.ts
|
|
3501
|
+
var DEFAULT_MAX_TOKENS2 = 8192;
|
|
3502
|
+
var AI_MAX_RETRIES2 = 3;
|
|
3503
|
+
var RETRY_DELAY_MS2 = 2e3;
|
|
3504
|
+
var DEFAULT_MODELS2 = {
|
|
3505
|
+
openai: "gpt-5.2",
|
|
3506
|
+
anthropic: "claude-sonnet-4-6",
|
|
3507
|
+
google: "gemini-3.1-pro-preview"
|
|
3508
|
+
};
|
|
3509
|
+
function isRecord2(value) {
|
|
3510
|
+
return typeof value === "object" && value !== null;
|
|
3511
|
+
}
|
|
3512
|
+
function isNonEmptyString2(value) {
|
|
3513
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
3514
|
+
}
|
|
3515
|
+
var PromptHandler = class {
|
|
3516
|
+
offeringId;
|
|
3517
|
+
autoAccept = true;
|
|
3518
|
+
schema = {
|
|
3519
|
+
version: 1,
|
|
3520
|
+
serviceRequirements: {
|
|
3521
|
+
type: "object",
|
|
3522
|
+
properties: {
|
|
3523
|
+
prompt: {
|
|
3524
|
+
type: "string",
|
|
3525
|
+
description: "Prompt to send to AI"
|
|
3526
|
+
},
|
|
3527
|
+
context: {
|
|
3528
|
+
type: "string",
|
|
3529
|
+
description: "Optional additional context"
|
|
3530
|
+
}
|
|
3531
|
+
},
|
|
3532
|
+
required: ["prompt"]
|
|
3533
|
+
},
|
|
3534
|
+
deliverableRequirements: {
|
|
3535
|
+
type: "object",
|
|
3536
|
+
properties: {
|
|
3537
|
+
type: {
|
|
3538
|
+
type: "string",
|
|
3539
|
+
description: 'Delivery type, always "inline"',
|
|
3540
|
+
enum: ["inline"]
|
|
3541
|
+
},
|
|
3542
|
+
content: {
|
|
3543
|
+
type: "string",
|
|
3544
|
+
description: "Generated markdown content"
|
|
3545
|
+
},
|
|
3546
|
+
mimeType: {
|
|
3547
|
+
type: "string",
|
|
3548
|
+
description: 'Content type, always "text/markdown"',
|
|
3549
|
+
enum: ["text/markdown"]
|
|
3550
|
+
}
|
|
3551
|
+
},
|
|
3552
|
+
required: ["type", "content", "mimeType"]
|
|
3553
|
+
}
|
|
3554
|
+
};
|
|
3555
|
+
config;
|
|
3556
|
+
constructor(offeringId, config) {
|
|
3557
|
+
this.offeringId = offeringId;
|
|
3558
|
+
this.config = config;
|
|
3559
|
+
}
|
|
3560
|
+
async validateRequirements(context) {
|
|
3561
|
+
try {
|
|
3562
|
+
this.parseRequirements(context.requirements);
|
|
3563
|
+
return { valid: true };
|
|
3564
|
+
} catch (error) {
|
|
3565
|
+
return {
|
|
3566
|
+
valid: false,
|
|
3567
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
async executeJob(context) {
|
|
3572
|
+
const requirements = this.parseRequirements(context.requirements);
|
|
3573
|
+
const provider = this.config.aiProvider;
|
|
3574
|
+
const model = this.config.aiModel?.trim() || DEFAULT_MODELS2[provider];
|
|
3575
|
+
const maxTokens = this.config.maxTokens && this.config.maxTokens > 0 ? this.config.maxTokens : DEFAULT_MAX_TOKENS2;
|
|
3576
|
+
const apiKey = await this.config.getApiKey(provider);
|
|
3577
|
+
if (!isNonEmptyString2(apiKey)) {
|
|
3578
|
+
throw new Error(`Missing API key for provider "${provider}"`);
|
|
3579
|
+
}
|
|
3580
|
+
const userPrompt = requirements.context ? [requirements.prompt, requirements.context].join("\n\n") : requirements.prompt;
|
|
3581
|
+
const aiMessages = [
|
|
3582
|
+
{ role: "system", content: this.config.systemPrompt },
|
|
3583
|
+
{ role: "user", content: userPrompt }
|
|
3584
|
+
];
|
|
3585
|
+
let aiResponse = null;
|
|
3586
|
+
let lastError = null;
|
|
3587
|
+
for (let attempt = 1; attempt <= AI_MAX_RETRIES2; attempt++) {
|
|
3588
|
+
try {
|
|
3589
|
+
aiResponse = await callAI({
|
|
3590
|
+
provider,
|
|
3591
|
+
model,
|
|
3592
|
+
maxTokens,
|
|
3593
|
+
apiKey,
|
|
3594
|
+
signal: context.abortSignal,
|
|
3595
|
+
messages: aiMessages
|
|
3596
|
+
});
|
|
3597
|
+
if (aiResponse.truncated) {
|
|
3598
|
+
throw new Error("AI output was truncated (hit token limit).");
|
|
3599
|
+
}
|
|
3600
|
+
break;
|
|
3601
|
+
} catch (err) {
|
|
3602
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
3603
|
+
console.error(
|
|
3604
|
+
`[prompt-handler] ${model} failed (attempt ${attempt}/${AI_MAX_RETRIES2}): ${lastError.message}`
|
|
3605
|
+
);
|
|
3606
|
+
}
|
|
3607
|
+
if (attempt < AI_MAX_RETRIES2) {
|
|
3608
|
+
const delay = RETRY_DELAY_MS2 * attempt;
|
|
3609
|
+
console.log(`[prompt-handler] retrying in ${delay}ms...`);
|
|
3610
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
if (!aiResponse) {
|
|
3614
|
+
throw new Error(
|
|
3615
|
+
`AI generation failed after ${AI_MAX_RETRIES2} attempts. Please try again later. Last error: ${lastError?.message ?? "unknown"}`
|
|
3616
|
+
);
|
|
3617
|
+
}
|
|
3618
|
+
return {
|
|
3619
|
+
type: "inline",
|
|
3620
|
+
content: aiResponse.content,
|
|
3621
|
+
mimeType: "text/markdown",
|
|
3622
|
+
usage: aiResponse.usage ? { provider, inputTokens: aiResponse.usage.inputTokens, outputTokens: aiResponse.usage.outputTokens } : void 0
|
|
3623
|
+
};
|
|
3624
|
+
}
|
|
3625
|
+
parseRequirements(requirements) {
|
|
3626
|
+
if (!isRecord2(requirements)) {
|
|
3627
|
+
throw new Error("Missing requirements object");
|
|
3628
|
+
}
|
|
3629
|
+
const prompt = requirements.prompt;
|
|
3630
|
+
if (!isNonEmptyString2(prompt)) {
|
|
3631
|
+
throw new Error("requirements.prompt must be a non-empty string");
|
|
3632
|
+
}
|
|
3633
|
+
const context = requirements.context;
|
|
3634
|
+
if (context !== void 0 && typeof context !== "string") {
|
|
3635
|
+
throw new Error("requirements.context must be a string when provided");
|
|
3636
|
+
}
|
|
3637
|
+
const normalizedContext = typeof context === "string" ? context.trim() : void 0;
|
|
3638
|
+
return {
|
|
3639
|
+
prompt: prompt.trim(),
|
|
3640
|
+
context: normalizedContext && normalizedContext.length > 0 ? normalizedContext : void 0
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
};
|
|
3644
|
+
|
|
3645
|
+
// src/types.ts
|
|
3646
|
+
var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
|
|
3647
|
+
JobStatus2[JobStatus2["Created"] = 0] = "Created";
|
|
3648
|
+
JobStatus2[JobStatus2["Accepted"] = 1] = "Accepted";
|
|
3649
|
+
JobStatus2[JobStatus2["InProgress"] = 2] = "InProgress";
|
|
3650
|
+
JobStatus2[JobStatus2["Delivered"] = 3] = "Delivered";
|
|
3651
|
+
JobStatus2[JobStatus2["Evaluated"] = 4] = "Evaluated";
|
|
3652
|
+
JobStatus2[JobStatus2["Completed"] = 5] = "Completed";
|
|
3653
|
+
JobStatus2[JobStatus2["Disputed"] = 6] = "Disputed";
|
|
3654
|
+
JobStatus2[JobStatus2["Cancelled"] = 7] = "Cancelled";
|
|
3655
|
+
return JobStatus2;
|
|
3656
|
+
})(JobStatus || {});
|
|
3657
|
+
var ServiceType = /* @__PURE__ */ ((ServiceType2) => {
|
|
3658
|
+
ServiceType2[ServiceType2["TextGeneration"] = 0] = "TextGeneration";
|
|
3659
|
+
ServiceType2[ServiceType2["ImageGeneration"] = 1] = "ImageGeneration";
|
|
3660
|
+
ServiceType2[ServiceType2["DataAnalysis"] = 2] = "DataAnalysis";
|
|
3661
|
+
ServiceType2[ServiceType2["CodeGeneration"] = 3] = "CodeGeneration";
|
|
3662
|
+
ServiceType2[ServiceType2["Translation"] = 4] = "Translation";
|
|
3663
|
+
ServiceType2[ServiceType2["Custom"] = 5] = "Custom";
|
|
3664
|
+
return ServiceType2;
|
|
3665
|
+
})(ServiceType || {});
|
|
3666
|
+
|
|
3667
|
+
// src/handler/schema.ts
|
|
3668
|
+
var DEFAULT_SCHEMAS = {
|
|
3669
|
+
[0 /* TextGeneration */]: {
|
|
3670
|
+
version: 1,
|
|
3671
|
+
serviceRequirements: {
|
|
3672
|
+
type: "object",
|
|
3673
|
+
properties: {
|
|
3674
|
+
prompt: { type: "string", description: "Text prompt to generate from" },
|
|
3675
|
+
maxTokens: { type: "number", description: "Maximum output token count" }
|
|
3676
|
+
},
|
|
3677
|
+
required: ["prompt"]
|
|
3678
|
+
},
|
|
3679
|
+
deliverableRequirements: {
|
|
3680
|
+
type: "object",
|
|
3681
|
+
properties: {
|
|
3682
|
+
text: { type: "string", description: "Generated text output" },
|
|
3683
|
+
tokenCount: { type: "number", description: "Tokens used to produce output" }
|
|
3684
|
+
},
|
|
3685
|
+
required: ["text"]
|
|
3686
|
+
}
|
|
3687
|
+
},
|
|
3688
|
+
[1 /* ImageGeneration */]: {
|
|
3689
|
+
version: 1,
|
|
3690
|
+
serviceRequirements: {
|
|
3691
|
+
type: "object",
|
|
3692
|
+
properties: {
|
|
3693
|
+
prompt: { type: "string", description: "Image prompt to generate from" },
|
|
3694
|
+
size: { type: "string", description: "Output size (for example: 1024x1024)" },
|
|
3695
|
+
style: { type: "string", description: "Desired visual style" }
|
|
3696
|
+
},
|
|
3697
|
+
required: ["prompt"]
|
|
3698
|
+
},
|
|
3699
|
+
deliverableRequirements: {
|
|
3700
|
+
type: "object",
|
|
3701
|
+
properties: {
|
|
3702
|
+
imageUrl: { type: "string", description: "URL of the generated image" },
|
|
3703
|
+
mimeType: { type: "string", description: "Image MIME type" }
|
|
3704
|
+
},
|
|
3705
|
+
required: ["imageUrl"]
|
|
3706
|
+
}
|
|
3707
|
+
},
|
|
3708
|
+
[2 /* DataAnalysis */]: {
|
|
3709
|
+
version: 1,
|
|
3710
|
+
serviceRequirements: {
|
|
3711
|
+
type: "object",
|
|
3712
|
+
properties: {
|
|
3713
|
+
data: { type: "string", description: "Input data for analysis" },
|
|
3714
|
+
analysisRequest: { type: "string", description: "Question or analysis task" }
|
|
3715
|
+
},
|
|
3716
|
+
required: ["data", "analysisRequest"]
|
|
3717
|
+
},
|
|
3718
|
+
deliverableRequirements: {
|
|
3719
|
+
type: "object",
|
|
3720
|
+
properties: {
|
|
3721
|
+
summary: { type: "string", description: "Analysis summary" },
|
|
3722
|
+
findings: {
|
|
3723
|
+
type: "array",
|
|
3724
|
+
description: "Structured key findings",
|
|
3725
|
+
items: { type: "string" }
|
|
3726
|
+
}
|
|
3727
|
+
},
|
|
3728
|
+
required: ["summary"]
|
|
3729
|
+
}
|
|
3730
|
+
},
|
|
3731
|
+
[3 /* CodeGeneration */]: {
|
|
3732
|
+
version: 1,
|
|
3733
|
+
serviceRequirements: {
|
|
3734
|
+
type: "object",
|
|
3735
|
+
properties: {
|
|
3736
|
+
prompt: { type: "string", description: "Code generation request" },
|
|
3737
|
+
language: { type: "string", description: "Target programming language" }
|
|
3738
|
+
},
|
|
3739
|
+
required: ["prompt"]
|
|
3740
|
+
},
|
|
3741
|
+
deliverableRequirements: {
|
|
3742
|
+
type: "object",
|
|
3743
|
+
properties: {
|
|
3744
|
+
code: { type: "string", description: "Generated source code" },
|
|
3745
|
+
language: { type: "string", description: "Detected or requested language" }
|
|
3746
|
+
},
|
|
3747
|
+
required: ["code"]
|
|
3748
|
+
}
|
|
3749
|
+
},
|
|
3750
|
+
[4 /* Translation */]: {
|
|
3751
|
+
version: 1,
|
|
3752
|
+
serviceRequirements: {
|
|
3753
|
+
type: "object",
|
|
3754
|
+
properties: {
|
|
3755
|
+
text: { type: "string", description: "Text to translate" },
|
|
3756
|
+
targetLanguage: { type: "string", description: "Target language code or name" },
|
|
3757
|
+
sourceLanguage: { type: "string", description: "Optional source language code or name" }
|
|
3758
|
+
},
|
|
3759
|
+
required: ["text", "targetLanguage"]
|
|
3760
|
+
},
|
|
3761
|
+
deliverableRequirements: {
|
|
3762
|
+
type: "object",
|
|
3763
|
+
properties: {
|
|
3764
|
+
translatedText: { type: "string", description: "Translated text output" },
|
|
3765
|
+
sourceLanguage: { type: "string", description: "Detected or provided source language" }
|
|
3766
|
+
},
|
|
3767
|
+
required: ["translatedText"]
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
};
|
|
3771
|
+
function isObjectRecord(value) {
|
|
3772
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3773
|
+
}
|
|
3774
|
+
function isTypeMatch(value, type) {
|
|
3775
|
+
switch (type) {
|
|
3776
|
+
case "string":
|
|
3777
|
+
return typeof value === "string";
|
|
3778
|
+
case "number":
|
|
3779
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
3780
|
+
case "boolean":
|
|
3781
|
+
return typeof value === "boolean";
|
|
3782
|
+
case "object":
|
|
3783
|
+
return isObjectRecord(value);
|
|
3784
|
+
case "array":
|
|
3785
|
+
return Array.isArray(value);
|
|
3786
|
+
default:
|
|
3787
|
+
return false;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
function validateAgainstSchema(data, schema) {
|
|
3791
|
+
if (!data || !isObjectRecord(data)) {
|
|
3792
|
+
return { valid: false, reason: "Requirements must be a JSON object" };
|
|
3793
|
+
}
|
|
3794
|
+
for (const requiredField of schema.required ?? []) {
|
|
3795
|
+
if (!(requiredField in data)) {
|
|
3796
|
+
return { valid: false, reason: `Missing required field: ${requiredField}` };
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
for (const [field, fieldSchema] of Object.entries(schema.properties)) {
|
|
3800
|
+
const value = data[field];
|
|
3801
|
+
if (value === void 0) continue;
|
|
3802
|
+
if (!isTypeMatch(value, fieldSchema.type)) {
|
|
3803
|
+
return {
|
|
3804
|
+
valid: false,
|
|
3805
|
+
reason: `Field "${field}" must be of type ${fieldSchema.type}`
|
|
3806
|
+
};
|
|
3807
|
+
}
|
|
3808
|
+
if (fieldSchema.enum && !fieldSchema.enum.some((enumValue) => Object.is(enumValue, value))) {
|
|
3809
|
+
return {
|
|
3810
|
+
valid: false,
|
|
3811
|
+
reason: `Field "${field}" must be one of: ${fieldSchema.enum.map((v) => JSON.stringify(v)).join(", ")}`
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
if (fieldSchema.type === "array" && fieldSchema.items && Array.isArray(value)) {
|
|
3815
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3816
|
+
if (!isTypeMatch(value[i], fieldSchema.items.type)) {
|
|
3817
|
+
return {
|
|
3818
|
+
valid: false,
|
|
3819
|
+
reason: `Field "${field}[${i}]" must be of type ${fieldSchema.items.type}`
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
return { valid: true };
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3354
3828
|
// src/utils.ts
|
|
3355
3829
|
var USDM_DECIMALS = 18;
|
|
3356
3830
|
var USDM_SCALE = 10n ** BigInt(USDM_DECIMALS);
|
|
@@ -3387,28 +3861,6 @@ function formatUsdm(amount) {
|
|
|
3387
3861
|
return `${whole}.${trimmedFraction.padEnd(2, "0")}`;
|
|
3388
3862
|
}
|
|
3389
3863
|
|
|
3390
|
-
// src/types.ts
|
|
3391
|
-
var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
|
|
3392
|
-
JobStatus2[JobStatus2["Created"] = 0] = "Created";
|
|
3393
|
-
JobStatus2[JobStatus2["Accepted"] = 1] = "Accepted";
|
|
3394
|
-
JobStatus2[JobStatus2["InProgress"] = 2] = "InProgress";
|
|
3395
|
-
JobStatus2[JobStatus2["Delivered"] = 3] = "Delivered";
|
|
3396
|
-
JobStatus2[JobStatus2["Evaluated"] = 4] = "Evaluated";
|
|
3397
|
-
JobStatus2[JobStatus2["Completed"] = 5] = "Completed";
|
|
3398
|
-
JobStatus2[JobStatus2["Disputed"] = 6] = "Disputed";
|
|
3399
|
-
JobStatus2[JobStatus2["Cancelled"] = 7] = "Cancelled";
|
|
3400
|
-
return JobStatus2;
|
|
3401
|
-
})(JobStatus || {});
|
|
3402
|
-
var ServiceType = /* @__PURE__ */ ((ServiceType2) => {
|
|
3403
|
-
ServiceType2[ServiceType2["TextGeneration"] = 0] = "TextGeneration";
|
|
3404
|
-
ServiceType2[ServiceType2["ImageGeneration"] = 1] = "ImageGeneration";
|
|
3405
|
-
ServiceType2[ServiceType2["DataAnalysis"] = 2] = "DataAnalysis";
|
|
3406
|
-
ServiceType2[ServiceType2["CodeGeneration"] = 3] = "CodeGeneration";
|
|
3407
|
-
ServiceType2[ServiceType2["Translation"] = 4] = "Translation";
|
|
3408
|
-
ServiceType2[ServiceType2["Custom"] = 5] = "Custom";
|
|
3409
|
-
return ServiceType2;
|
|
3410
|
-
})(ServiceType || {});
|
|
3411
|
-
|
|
3412
3864
|
// src/indexer/client.ts
|
|
3413
3865
|
var IndexerClientError = class extends Error {
|
|
3414
3866
|
status;
|
|
@@ -3422,7 +3874,7 @@ var IndexerClientError = class extends Error {
|
|
|
3422
3874
|
this.body = options.body;
|
|
3423
3875
|
}
|
|
3424
3876
|
};
|
|
3425
|
-
function
|
|
3877
|
+
function isRecord3(value) {
|
|
3426
3878
|
return typeof value === "object" && value !== null;
|
|
3427
3879
|
}
|
|
3428
3880
|
function toIndexerAgent(raw) {
|
|
@@ -3446,6 +3898,7 @@ function toIndexerOffering(raw) {
|
|
|
3446
3898
|
priceUsdm: raw.price_usdm,
|
|
3447
3899
|
slaMinutes: raw.sla_minutes,
|
|
3448
3900
|
description: raw.description,
|
|
3901
|
+
requirementsSchemaUri: raw.requirements_schema_uri ?? void 0,
|
|
3449
3902
|
active: raw.active === 1,
|
|
3450
3903
|
blockNumber: raw.block_number,
|
|
3451
3904
|
txHash: raw.tx_hash
|
|
@@ -3610,7 +4063,7 @@ var IndexerClient = class {
|
|
|
3610
4063
|
});
|
|
3611
4064
|
}
|
|
3612
4065
|
if (!response.ok) {
|
|
3613
|
-
const apiError =
|
|
4066
|
+
const apiError = isRecord3(payload) ? payload : void 0;
|
|
3614
4067
|
const message = response.status === 404 && notFoundMessage ? notFoundMessage : apiError?.error ?? apiError?.message ?? `Indexer request failed (${response.status})`;
|
|
3615
4068
|
throw new IndexerClientError(message, {
|
|
3616
4069
|
url,
|
|
@@ -3701,7 +4154,6 @@ var ProviderRuntime = class {
|
|
|
3701
4154
|
this.processedJobs.add(deliverKey);
|
|
3702
4155
|
await deployDeliverable(
|
|
3703
4156
|
this.client,
|
|
3704
|
-
this.agentId,
|
|
3705
4157
|
BigInt(job.jobId),
|
|
3706
4158
|
{ ...params, jobId: BigInt(job.jobId) },
|
|
3707
4159
|
this.indexer["baseUrl"]
|
|
@@ -3836,7 +4288,6 @@ var BuyerRuntime = class {
|
|
|
3836
4288
|
this.processedJobs.add(deliveredKey);
|
|
3837
4289
|
try {
|
|
3838
4290
|
const deliverable = await readDeliverable(
|
|
3839
|
-
this.client,
|
|
3840
4291
|
BigInt(job.jobId),
|
|
3841
4292
|
this.indexer["baseUrl"]
|
|
3842
4293
|
);
|
|
@@ -3878,6 +4329,7 @@ var HandlerProviderRuntime = class {
|
|
|
3878
4329
|
running = false;
|
|
3879
4330
|
processedJobs = /* @__PURE__ */ new Set();
|
|
3880
4331
|
failedJobs = /* @__PURE__ */ new Map();
|
|
4332
|
+
schemaCache = /* @__PURE__ */ new Map();
|
|
3881
4333
|
maxRetries = 3;
|
|
3882
4334
|
onError;
|
|
3883
4335
|
constructor(client, agentId, options) {
|
|
@@ -3922,8 +4374,8 @@ var HandlerProviderRuntime = class {
|
|
|
3922
4374
|
for (const job of jobs) {
|
|
3923
4375
|
const newKey = `new:${job.jobId}`;
|
|
3924
4376
|
if (!this.canProcess(newKey)) continue;
|
|
3925
|
-
const
|
|
3926
|
-
if (!
|
|
4377
|
+
const offering = offerings.find((o) => o.offeringId === job.offeringId);
|
|
4378
|
+
if (!offering) continue;
|
|
3927
4379
|
const handler = this.handlers.get(job.offeringId);
|
|
3928
4380
|
if (!handler) continue;
|
|
3929
4381
|
try {
|
|
@@ -3946,6 +4398,10 @@ var HandlerProviderRuntime = class {
|
|
|
3946
4398
|
if (reqData) {
|
|
3947
4399
|
context.requirements = reqData.requirements;
|
|
3948
4400
|
}
|
|
4401
|
+
const schemaValidation = await this.validateSchemaRequirements(context, handler, offering);
|
|
4402
|
+
if (!schemaValidation.valid) {
|
|
4403
|
+
throw new Error(`Schema validation failed for job ${job.jobId}: ${schemaValidation.reason}`);
|
|
4404
|
+
}
|
|
3949
4405
|
if (handler.validateRequirements) {
|
|
3950
4406
|
const validation = await handler.validateRequirements(context);
|
|
3951
4407
|
if (!validation.valid) {
|
|
@@ -3966,6 +4422,10 @@ var HandlerProviderRuntime = class {
|
|
|
3966
4422
|
}
|
|
3967
4423
|
}
|
|
3968
4424
|
async checkInProgressJobs() {
|
|
4425
|
+
const offerings = await this.indexer.getOfferings({ agentId: Number(this.agentId) });
|
|
4426
|
+
const offeringsById = new Map(
|
|
4427
|
+
offerings.map((offering) => [offering.offeringId, offering])
|
|
4428
|
+
);
|
|
3969
4429
|
const inProgressJobs = await this.indexer.getJobs({
|
|
3970
4430
|
status: 2,
|
|
3971
4431
|
agentId: Number(this.agentId)
|
|
@@ -3975,6 +4435,7 @@ var HandlerProviderRuntime = class {
|
|
|
3975
4435
|
if (!this.canProcess(deliverKey)) continue;
|
|
3976
4436
|
const handler = this.handlers.get(job.offeringId);
|
|
3977
4437
|
if (!handler) continue;
|
|
4438
|
+
const offering = offeringsById.get(job.offeringId);
|
|
3978
4439
|
try {
|
|
3979
4440
|
if (job.slaMinutes === null) {
|
|
3980
4441
|
throw new Error(`Job ${job.jobId} is missing slaMinutes`);
|
|
@@ -3995,6 +4456,14 @@ var HandlerProviderRuntime = class {
|
|
|
3995
4456
|
if (reqData) {
|
|
3996
4457
|
context.requirements = reqData.requirements;
|
|
3997
4458
|
}
|
|
4459
|
+
const schemaValidation = await this.validateSchemaRequirements(context, handler, offering);
|
|
4460
|
+
if (!schemaValidation.valid) {
|
|
4461
|
+
this.markFailed(
|
|
4462
|
+
deliverKey,
|
|
4463
|
+
new Error(`Schema validation failed for job ${job.jobId}: ${schemaValidation.reason}`)
|
|
4464
|
+
);
|
|
4465
|
+
continue;
|
|
4466
|
+
}
|
|
3998
4467
|
if (handler.validateRequirements) {
|
|
3999
4468
|
const validation = await handler.validateRequirements(context);
|
|
4000
4469
|
if (!validation.valid) {
|
|
@@ -4040,7 +4509,6 @@ var HandlerProviderRuntime = class {
|
|
|
4040
4509
|
};
|
|
4041
4510
|
await deployDeliverable(
|
|
4042
4511
|
this.client,
|
|
4043
|
-
this.agentId,
|
|
4044
4512
|
BigInt(job.jobId),
|
|
4045
4513
|
deliverableParams,
|
|
4046
4514
|
this.indexerUrl
|
|
@@ -4088,6 +4556,85 @@ var HandlerProviderRuntime = class {
|
|
|
4088
4556
|
this.onError?.(new Error(`Giving up on ${key} after ${retries} failed attempts`));
|
|
4089
4557
|
}
|
|
4090
4558
|
}
|
|
4559
|
+
async validateSchemaRequirements(context, handler, offering) {
|
|
4560
|
+
if (offering?.requirementsSchemaUri) {
|
|
4561
|
+
try {
|
|
4562
|
+
const offeringSchema = await this.resolveOfferingSchemaFromUri(offering.requirementsSchemaUri);
|
|
4563
|
+
if (!offeringSchema) {
|
|
4564
|
+
return {
|
|
4565
|
+
valid: false,
|
|
4566
|
+
reason: `Unsupported requirementsSchemaUri format: ${offering.requirementsSchemaUri}`
|
|
4567
|
+
};
|
|
4568
|
+
}
|
|
4569
|
+
return validateAgainstSchema(context.requirements, offeringSchema.serviceRequirements);
|
|
4570
|
+
} catch (error) {
|
|
4571
|
+
return {
|
|
4572
|
+
valid: false,
|
|
4573
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
const defaultSchema = offering ? DEFAULT_SCHEMAS[offering.serviceType] : void 0;
|
|
4578
|
+
const schema = handler.schema ?? defaultSchema;
|
|
4579
|
+
if (!schema) return { valid: true };
|
|
4580
|
+
return validateAgainstSchema(context.requirements, schema.serviceRequirements);
|
|
4581
|
+
}
|
|
4582
|
+
async resolveOfferingSchemaFromUri(schemaUri) {
|
|
4583
|
+
const cached = this.schemaCache.get(schemaUri);
|
|
4584
|
+
if (cached !== void 0) {
|
|
4585
|
+
return cached;
|
|
4586
|
+
}
|
|
4587
|
+
let schemaPayload;
|
|
4588
|
+
if (schemaUri.startsWith("data:")) {
|
|
4589
|
+
schemaPayload = this.parseDataUriJson(schemaUri);
|
|
4590
|
+
} else if (schemaUri.startsWith("http://") || schemaUri.startsWith("https://")) {
|
|
4591
|
+
const controller = new AbortController();
|
|
4592
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
4593
|
+
try {
|
|
4594
|
+
const response = await fetch(schemaUri, { signal: controller.signal });
|
|
4595
|
+
if (!response.ok) {
|
|
4596
|
+
throw new Error(`Failed to fetch requirements schema URI (${response.status} ${response.statusText})`);
|
|
4597
|
+
}
|
|
4598
|
+
const buf = await response.arrayBuffer();
|
|
4599
|
+
if (buf.byteLength > 1048576) {
|
|
4600
|
+
throw new Error(`Schema too large (${buf.byteLength} bytes). Limit is 1MB`);
|
|
4601
|
+
}
|
|
4602
|
+
schemaPayload = JSON.parse(new TextDecoder().decode(buf));
|
|
4603
|
+
} finally {
|
|
4604
|
+
clearTimeout(timeoutId);
|
|
4605
|
+
}
|
|
4606
|
+
} else if (schemaUri.trim().startsWith("{")) {
|
|
4607
|
+
schemaPayload = JSON.parse(schemaUri);
|
|
4608
|
+
} else {
|
|
4609
|
+
this.schemaCache.set(schemaUri, null);
|
|
4610
|
+
return null;
|
|
4611
|
+
}
|
|
4612
|
+
if (!this.isOfferingSchema(schemaPayload)) {
|
|
4613
|
+
throw new Error("requirementsSchemaUri did not resolve to a valid OfferingSchema document");
|
|
4614
|
+
}
|
|
4615
|
+
this.schemaCache.set(schemaUri, schemaPayload);
|
|
4616
|
+
return schemaPayload;
|
|
4617
|
+
}
|
|
4618
|
+
parseDataUriJson(dataUri) {
|
|
4619
|
+
const commaIndex = dataUri.indexOf(",");
|
|
4620
|
+
if (commaIndex < 0) {
|
|
4621
|
+
throw new Error("Invalid data URI schema format");
|
|
4622
|
+
}
|
|
4623
|
+
const metadata = dataUri.slice(5, commaIndex);
|
|
4624
|
+
const payload = dataUri.slice(commaIndex + 1);
|
|
4625
|
+
const decoded = metadata.includes(";base64") ? new TextDecoder().decode(Uint8Array.from(atob(payload), (char) => char.charCodeAt(0))) : decodeURIComponent(payload);
|
|
4626
|
+
return JSON.parse(decoded);
|
|
4627
|
+
}
|
|
4628
|
+
isOfferingSchema(value) {
|
|
4629
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4630
|
+
const candidate = value;
|
|
4631
|
+
return typeof candidate.version === "number" && this.isJsonSchema(candidate.serviceRequirements) && this.isJsonSchema(candidate.deliverableRequirements);
|
|
4632
|
+
}
|
|
4633
|
+
isJsonSchema(value) {
|
|
4634
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4635
|
+
const candidate = value;
|
|
4636
|
+
return candidate.type === "object" && typeof candidate.properties === "object" && candidate.properties !== null;
|
|
4637
|
+
}
|
|
4091
4638
|
getRemainingSlaMs(createdAt, acceptedAt, slaMinutes) {
|
|
4092
4639
|
const start = acceptedAt ?? createdAt;
|
|
4093
4640
|
if (start === null) return null;
|
|
@@ -5470,6 +6017,7 @@ export {
|
|
|
5470
6017
|
BPS_DENOMINATOR,
|
|
5471
6018
|
BuyerRuntime,
|
|
5472
6019
|
DEFAULT_INDEXER_URLS,
|
|
6020
|
+
DEFAULT_SCHEMAS,
|
|
5473
6021
|
EVALUATION_TIMEOUT,
|
|
5474
6022
|
FEE_BPS,
|
|
5475
6023
|
HandlerProviderRuntime,
|
|
@@ -5480,6 +6028,7 @@ export {
|
|
|
5480
6028
|
MEMO_TYPES,
|
|
5481
6029
|
PLATFORM_BUYER_AGENT_ID,
|
|
5482
6030
|
PULSE_DOMAIN,
|
|
6031
|
+
PromptHandler,
|
|
5483
6032
|
ProviderRuntime,
|
|
5484
6033
|
RISK_POOL_BPS,
|
|
5485
6034
|
ServiceType,
|
|
@@ -5543,6 +6092,8 @@ export {
|
|
|
5543
6092
|
submitDeliverable,
|
|
5544
6093
|
testnetERC8004,
|
|
5545
6094
|
updateOffering,
|
|
6095
|
+
updateOfferingSchema,
|
|
6096
|
+
validateAgainstSchema,
|
|
5546
6097
|
verifyContentHash
|
|
5547
6098
|
};
|
|
5548
6099
|
//# sourceMappingURL=index.js.map
|