opentool 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.d.ts +3 -4
- package/dist/cli/index.js +93 -78
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +436 -417
- package/dist/index.js.map +1 -1
- package/dist/payment-orkZA9se.d.ts +96 -0
- package/dist/{validate-BgNU5laL.d.ts → validate-DbhJ_r0Z.d.ts} +1 -1
- package/dist/x402/index.d.ts +2 -96
- package/dist/x402/index.js +157 -157
- package/dist/x402/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as path6 from 'path';
|
|
2
|
-
import { fileURLToPath, pathToFileURL } from 'url';
|
|
3
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
3
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
-
import * as
|
|
4
|
+
import * as fs4 from 'fs';
|
|
5
|
+
import * as path5 from 'path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
7
7
|
import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { zeroAddress, createWalletClient, http, createPublicClient, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
|
|
@@ -25,8 +25,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
25
25
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
26
26
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
27
27
|
});
|
|
28
|
-
var getFilename = () => fileURLToPath(import.meta.url);
|
|
29
|
-
var __filename$1 = /* @__PURE__ */ getFilename();
|
|
30
28
|
var X402_VERSION = 1;
|
|
31
29
|
var HEADER_X402 = "X-PAYMENT";
|
|
32
30
|
var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
|
|
@@ -331,295 +329,8 @@ function ensureTrailingSlash(url) {
|
|
|
331
329
|
return url.endsWith("/") ? url : `${url}/`;
|
|
332
330
|
}
|
|
333
331
|
var PAYMENT_HEADERS = [HEADER_X402, HEADER_PAYMENT_RESPONSE];
|
|
334
|
-
var X402Client = class {
|
|
335
|
-
constructor(config) {
|
|
336
|
-
this.account = privateKeyToAccount(config.privateKey);
|
|
337
|
-
const chain = baseSepolia;
|
|
338
|
-
this.walletClient = createWalletClient({
|
|
339
|
-
account: this.account,
|
|
340
|
-
chain,
|
|
341
|
-
transport: http(config.rpcUrl)
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
async pay(request) {
|
|
345
|
-
try {
|
|
346
|
-
const initialResponse = await fetch(request.url, {
|
|
347
|
-
method: request.method ?? "POST",
|
|
348
|
-
headers: {
|
|
349
|
-
"Content-Type": "application/json",
|
|
350
|
-
...request.headers
|
|
351
|
-
},
|
|
352
|
-
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
353
|
-
});
|
|
354
|
-
if (initialResponse.status !== 402) {
|
|
355
|
-
return {
|
|
356
|
-
success: initialResponse.ok,
|
|
357
|
-
response: initialResponse
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
const paymentRequirements = await initialResponse.json();
|
|
361
|
-
const x402Requirements = paymentRequirements.x402?.accepts?.[0];
|
|
362
|
-
if (!x402Requirements) {
|
|
363
|
-
return {
|
|
364
|
-
success: false,
|
|
365
|
-
error: "No x402 payment requirements found in 402 response"
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
const authorization = await this.signTransferAuthorization({
|
|
369
|
-
from: this.account.address,
|
|
370
|
-
to: x402Requirements.payTo,
|
|
371
|
-
value: BigInt(x402Requirements.maxAmountRequired),
|
|
372
|
-
validAfter: BigInt(Math.floor(Date.now() / 1e3)),
|
|
373
|
-
validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
|
|
374
|
-
// 15 min
|
|
375
|
-
nonce: `0x${Array.from(
|
|
376
|
-
{ length: 32 },
|
|
377
|
-
() => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
|
|
378
|
-
).join("")}`,
|
|
379
|
-
tokenAddress: x402Requirements.asset
|
|
380
|
-
});
|
|
381
|
-
const paymentProof = {
|
|
382
|
-
x402Version: 1,
|
|
383
|
-
scheme: x402Requirements.scheme,
|
|
384
|
-
network: x402Requirements.network,
|
|
385
|
-
correlationId: "",
|
|
386
|
-
payload: {
|
|
387
|
-
signature: authorization.signature,
|
|
388
|
-
authorization: {
|
|
389
|
-
from: authorization.from,
|
|
390
|
-
to: authorization.to,
|
|
391
|
-
value: authorization.value.toString(),
|
|
392
|
-
validAfter: authorization.validAfter.toString(),
|
|
393
|
-
validBefore: authorization.validBefore.toString(),
|
|
394
|
-
nonce: authorization.nonce
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString("base64");
|
|
399
|
-
const paidResponse = await fetch(request.url, {
|
|
400
|
-
method: request.method ?? "POST",
|
|
401
|
-
headers: {
|
|
402
|
-
"Content-Type": "application/json",
|
|
403
|
-
"X-PAYMENT": paymentHeader,
|
|
404
|
-
...request.headers
|
|
405
|
-
},
|
|
406
|
-
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
407
|
-
});
|
|
408
|
-
return {
|
|
409
|
-
success: paidResponse.ok,
|
|
410
|
-
response: paidResponse,
|
|
411
|
-
paymentDetails: {
|
|
412
|
-
amount: x402Requirements.maxAmountRequired,
|
|
413
|
-
currency: x402Requirements.extra?.currencyCode ?? "USDC",
|
|
414
|
-
network: x402Requirements.network,
|
|
415
|
-
signature: authorization.signature
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
} catch (error) {
|
|
419
|
-
return {
|
|
420
|
-
success: false,
|
|
421
|
-
error: error instanceof Error ? error.message : String(error)
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
async signTransferAuthorization(params) {
|
|
426
|
-
if (!this.walletClient.chain) {
|
|
427
|
-
throw new Error("Wallet client chain not configured");
|
|
428
|
-
}
|
|
429
|
-
const domain = {
|
|
430
|
-
name: "USD Coin",
|
|
431
|
-
version: "2",
|
|
432
|
-
chainId: this.walletClient.chain.id,
|
|
433
|
-
verifyingContract: params.tokenAddress
|
|
434
|
-
};
|
|
435
|
-
const types = {
|
|
436
|
-
TransferWithAuthorization: [
|
|
437
|
-
{ name: "from", type: "address" },
|
|
438
|
-
{ name: "to", type: "address" },
|
|
439
|
-
{ name: "value", type: "uint256" },
|
|
440
|
-
{ name: "validAfter", type: "uint256" },
|
|
441
|
-
{ name: "validBefore", type: "uint256" },
|
|
442
|
-
{ name: "nonce", type: "bytes32" }
|
|
443
|
-
]
|
|
444
|
-
};
|
|
445
|
-
const message = {
|
|
446
|
-
from: params.from,
|
|
447
|
-
to: params.to,
|
|
448
|
-
value: params.value,
|
|
449
|
-
validAfter: params.validAfter,
|
|
450
|
-
validBefore: params.validBefore,
|
|
451
|
-
nonce: params.nonce
|
|
452
|
-
};
|
|
453
|
-
const signature = await this.walletClient.signTypedData({
|
|
454
|
-
account: this.account,
|
|
455
|
-
domain,
|
|
456
|
-
types,
|
|
457
|
-
primaryType: "TransferWithAuthorization",
|
|
458
|
-
message
|
|
459
|
-
});
|
|
460
|
-
return {
|
|
461
|
-
signature,
|
|
462
|
-
from: params.from,
|
|
463
|
-
to: params.to,
|
|
464
|
-
value: params.value,
|
|
465
|
-
validAfter: params.validAfter,
|
|
466
|
-
validBefore: params.validBefore,
|
|
467
|
-
nonce: params.nonce
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
getAddress() {
|
|
471
|
-
return this.account.address;
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
async function payX402(config) {
|
|
475
|
-
const client = new X402Client({
|
|
476
|
-
privateKey: config.privateKey,
|
|
477
|
-
...config.rpcUrl ? { rpcUrl: config.rpcUrl } : {}
|
|
478
|
-
});
|
|
479
|
-
return client.pay({
|
|
480
|
-
url: config.url,
|
|
481
|
-
body: config.body
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
var X402BrowserClient = class {
|
|
485
|
-
constructor(config) {
|
|
486
|
-
this.walletClient = config.walletClient;
|
|
487
|
-
this.chainId = config.chainId;
|
|
488
|
-
}
|
|
489
|
-
async pay(request) {
|
|
490
|
-
try {
|
|
491
|
-
const initialResponse = await fetch(request.url, {
|
|
492
|
-
method: request.method ?? "POST",
|
|
493
|
-
headers: {
|
|
494
|
-
"Content-Type": "application/json",
|
|
495
|
-
...request.headers
|
|
496
|
-
},
|
|
497
|
-
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
498
|
-
});
|
|
499
|
-
if (initialResponse.status !== 402) {
|
|
500
|
-
return {
|
|
501
|
-
success: initialResponse.ok,
|
|
502
|
-
response: initialResponse
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
const paymentRequirements = await initialResponse.json();
|
|
506
|
-
const x402Requirements = paymentRequirements.x402?.accepts?.[0];
|
|
507
|
-
if (!x402Requirements) {
|
|
508
|
-
return {
|
|
509
|
-
success: false,
|
|
510
|
-
error: "No x402 payment requirements found in 402 response"
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
const account = this.walletClient.account;
|
|
514
|
-
if (!account) {
|
|
515
|
-
return {
|
|
516
|
-
success: false,
|
|
517
|
-
error: "No account connected to wallet"
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
const authorization = {
|
|
521
|
-
from: account.address,
|
|
522
|
-
to: x402Requirements.payTo,
|
|
523
|
-
value: BigInt(x402Requirements.maxAmountRequired),
|
|
524
|
-
validAfter: BigInt(Math.floor(Date.now() / 1e3)),
|
|
525
|
-
validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
|
|
526
|
-
nonce: `0x${Array.from(
|
|
527
|
-
{ length: 32 },
|
|
528
|
-
() => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
|
|
529
|
-
).join("")}`
|
|
530
|
-
};
|
|
531
|
-
const signature = await this.signTransferAuthorization(
|
|
532
|
-
authorization,
|
|
533
|
-
x402Requirements.asset
|
|
534
|
-
);
|
|
535
|
-
const paymentProof = {
|
|
536
|
-
x402Version: 1,
|
|
537
|
-
scheme: x402Requirements.scheme,
|
|
538
|
-
network: x402Requirements.network,
|
|
539
|
-
correlationId: "",
|
|
540
|
-
payload: {
|
|
541
|
-
signature,
|
|
542
|
-
authorization: {
|
|
543
|
-
from: authorization.from,
|
|
544
|
-
to: authorization.to,
|
|
545
|
-
value: authorization.value.toString(),
|
|
546
|
-
validAfter: authorization.validAfter.toString(),
|
|
547
|
-
validBefore: authorization.validBefore.toString(),
|
|
548
|
-
nonce: authorization.nonce
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
const paymentHeader = btoa(JSON.stringify(paymentProof));
|
|
553
|
-
const paidResponse = await fetch(request.url, {
|
|
554
|
-
method: request.method ?? "POST",
|
|
555
|
-
headers: {
|
|
556
|
-
"Content-Type": "application/json",
|
|
557
|
-
"X-PAYMENT": paymentHeader,
|
|
558
|
-
...request.headers
|
|
559
|
-
},
|
|
560
|
-
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
561
|
-
});
|
|
562
|
-
return {
|
|
563
|
-
success: paidResponse.ok,
|
|
564
|
-
response: paidResponse,
|
|
565
|
-
paymentDetails: {
|
|
566
|
-
amount: x402Requirements.maxAmountRequired,
|
|
567
|
-
currency: x402Requirements.extra?.currencyCode ?? "USDC",
|
|
568
|
-
network: x402Requirements.network,
|
|
569
|
-
signature
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
} catch (error) {
|
|
573
|
-
return {
|
|
574
|
-
success: false,
|
|
575
|
-
error: error instanceof Error ? error.message : String(error)
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
async signTransferAuthorization(authorization, tokenAddress) {
|
|
580
|
-
const account = this.walletClient.account;
|
|
581
|
-
if (!account) {
|
|
582
|
-
throw new Error("No account connected to wallet");
|
|
583
|
-
}
|
|
584
|
-
const domain = {
|
|
585
|
-
name: "USD Coin",
|
|
586
|
-
version: "2",
|
|
587
|
-
chainId: this.chainId,
|
|
588
|
-
verifyingContract: tokenAddress
|
|
589
|
-
};
|
|
590
|
-
const types = {
|
|
591
|
-
TransferWithAuthorization: [
|
|
592
|
-
{ name: "from", type: "address" },
|
|
593
|
-
{ name: "to", type: "address" },
|
|
594
|
-
{ name: "value", type: "uint256" },
|
|
595
|
-
{ name: "validAfter", type: "uint256" },
|
|
596
|
-
{ name: "validBefore", type: "uint256" },
|
|
597
|
-
{ name: "nonce", type: "bytes32" }
|
|
598
|
-
]
|
|
599
|
-
};
|
|
600
|
-
const message = {
|
|
601
|
-
from: authorization.from,
|
|
602
|
-
to: authorization.to,
|
|
603
|
-
value: authorization.value,
|
|
604
|
-
validAfter: authorization.validAfter,
|
|
605
|
-
validBefore: authorization.validBefore,
|
|
606
|
-
nonce: authorization.nonce
|
|
607
|
-
};
|
|
608
|
-
return await this.walletClient.signTypedData({
|
|
609
|
-
account,
|
|
610
|
-
domain,
|
|
611
|
-
types,
|
|
612
|
-
primaryType: "TransferWithAuthorization",
|
|
613
|
-
message
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
async function payX402WithWallet(walletClient, chainId, request) {
|
|
618
|
-
const client = new X402BrowserClient({ walletClient, chainId });
|
|
619
|
-
return client.pay(request);
|
|
620
|
-
}
|
|
621
332
|
|
|
622
|
-
// src/x402/
|
|
333
|
+
// src/x402/payment.ts
|
|
623
334
|
var PAYMENT_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("opentool.x402.context");
|
|
624
335
|
var X402PaymentRequiredError = class extends Error {
|
|
625
336
|
constructor(response, verification) {
|
|
@@ -988,16 +699,16 @@ function buildAdapters(tools) {
|
|
|
988
699
|
}
|
|
989
700
|
async function loadToolsFromDirectory(metadataMap) {
|
|
990
701
|
const tools = [];
|
|
991
|
-
const toolsDir =
|
|
992
|
-
if (!
|
|
702
|
+
const toolsDir = path5.join(process.cwd(), "tools");
|
|
703
|
+
if (!fs4.existsSync(toolsDir)) {
|
|
993
704
|
return tools;
|
|
994
705
|
}
|
|
995
|
-
const files =
|
|
706
|
+
const files = fs4.readdirSync(toolsDir);
|
|
996
707
|
for (const file of files) {
|
|
997
708
|
if (!isSupportedToolFile(file)) {
|
|
998
709
|
continue;
|
|
999
710
|
}
|
|
1000
|
-
const toolPath =
|
|
711
|
+
const toolPath = path5.join(toolsDir, file);
|
|
1001
712
|
try {
|
|
1002
713
|
const exportsObject = __require(toolPath);
|
|
1003
714
|
const candidate = resolveModuleCandidate(exportsObject);
|
|
@@ -1061,12 +772,12 @@ async function loadToolsFromDirectory(metadataMap) {
|
|
|
1061
772
|
return tools;
|
|
1062
773
|
}
|
|
1063
774
|
function loadMetadata() {
|
|
1064
|
-
const metadataPath =
|
|
1065
|
-
if (!
|
|
775
|
+
const metadataPath = path5.join(process.cwd(), "metadata.json");
|
|
776
|
+
if (!fs4.existsSync(metadataPath)) {
|
|
1066
777
|
return null;
|
|
1067
778
|
}
|
|
1068
779
|
try {
|
|
1069
|
-
const contents =
|
|
780
|
+
const contents = fs4.readFileSync(metadataPath, "utf8");
|
|
1070
781
|
return JSON.parse(contents);
|
|
1071
782
|
} catch (error) {
|
|
1072
783
|
console.warn(`Failed to parse metadata.json: ${error}`);
|
|
@@ -1120,74 +831,361 @@ function collectHttpHandlers(module) {
|
|
|
1120
831
|
handler: async (request) => handler.call(module, request)
|
|
1121
832
|
});
|
|
1122
833
|
}
|
|
1123
|
-
});
|
|
1124
|
-
return handlers;
|
|
1125
|
-
}
|
|
1126
|
-
function toHttpHandlerMap(handlers) {
|
|
1127
|
-
return handlers.reduce((acc, handler) => {
|
|
1128
|
-
acc[handler.method.toUpperCase()] = handler.handler;
|
|
1129
|
-
return acc;
|
|
1130
|
-
}, {});
|
|
1131
|
-
}
|
|
1132
|
-
function normalizeInputSchema(schema) {
|
|
1133
|
-
if (!schema || typeof schema !== "object") {
|
|
1134
|
-
return schema;
|
|
834
|
+
});
|
|
835
|
+
return handlers;
|
|
836
|
+
}
|
|
837
|
+
function toHttpHandlerMap(handlers) {
|
|
838
|
+
return handlers.reduce((acc, handler) => {
|
|
839
|
+
acc[handler.method.toUpperCase()] = handler.handler;
|
|
840
|
+
return acc;
|
|
841
|
+
}, {});
|
|
842
|
+
}
|
|
843
|
+
function normalizeInputSchema(schema) {
|
|
844
|
+
if (!schema || typeof schema !== "object") {
|
|
845
|
+
return schema;
|
|
846
|
+
}
|
|
847
|
+
const clone = JSON.parse(JSON.stringify(schema));
|
|
848
|
+
if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
|
|
849
|
+
const refKey = clone.$ref.replace("#/definitions/", "");
|
|
850
|
+
if (clone.definitions && typeof clone.definitions[refKey] === "object") {
|
|
851
|
+
return normalizeInputSchema(clone.definitions[refKey]);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
delete clone.$ref;
|
|
855
|
+
delete clone.definitions;
|
|
856
|
+
if (!clone.type) {
|
|
857
|
+
clone.type = "object";
|
|
858
|
+
}
|
|
859
|
+
return clone;
|
|
860
|
+
}
|
|
861
|
+
function normalizeRuntimeMcpConfig(rawConfig) {
|
|
862
|
+
if (isPlainObject(rawConfig) && rawConfig.enabled === true) {
|
|
863
|
+
let normalizedMode;
|
|
864
|
+
if (typeof rawConfig.mode === "string") {
|
|
865
|
+
const candidate = rawConfig.mode.toLowerCase();
|
|
866
|
+
if (candidate === "stdio" || candidate === "lambda" || candidate === "dual") {
|
|
867
|
+
normalizedMode = candidate;
|
|
868
|
+
} else {
|
|
869
|
+
throw new Error('mcp.mode must be one of "stdio", "lambda", or "dual"');
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const metadataOverrides = isPlainObject(rawConfig.metadataOverrides) ? rawConfig.metadataOverrides : void 0;
|
|
873
|
+
const config = { enabled: true };
|
|
874
|
+
if (normalizedMode) {
|
|
875
|
+
config.mode = normalizedMode;
|
|
876
|
+
}
|
|
877
|
+
if (typeof rawConfig.defaultMethod === "string") {
|
|
878
|
+
config.defaultMethod = rawConfig.defaultMethod.toUpperCase();
|
|
879
|
+
}
|
|
880
|
+
if (metadataOverrides) {
|
|
881
|
+
config.metadataOverrides = metadataOverrides;
|
|
882
|
+
}
|
|
883
|
+
return config;
|
|
884
|
+
}
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
function isPlainObject(value) {
|
|
888
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
889
|
+
}
|
|
890
|
+
function isMcpEnabled(tool) {
|
|
891
|
+
return Boolean(tool.mcpConfig?.enabled);
|
|
892
|
+
}
|
|
893
|
+
function resolveRuntimePath(value) {
|
|
894
|
+
if (value.startsWith("file://")) {
|
|
895
|
+
return fileURLToPath(value);
|
|
896
|
+
}
|
|
897
|
+
return path5.resolve(value);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/types/index.ts
|
|
901
|
+
var HTTP_METHODS2 = ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
|
|
902
|
+
var X402Client = class {
|
|
903
|
+
constructor(config) {
|
|
904
|
+
this.account = privateKeyToAccount(config.privateKey);
|
|
905
|
+
const chain = baseSepolia;
|
|
906
|
+
this.walletClient = createWalletClient({
|
|
907
|
+
account: this.account,
|
|
908
|
+
chain,
|
|
909
|
+
transport: http(config.rpcUrl)
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
async pay(request) {
|
|
913
|
+
try {
|
|
914
|
+
const initialResponse = await fetch(request.url, {
|
|
915
|
+
method: request.method ?? "POST",
|
|
916
|
+
headers: {
|
|
917
|
+
"Content-Type": "application/json",
|
|
918
|
+
...request.headers
|
|
919
|
+
},
|
|
920
|
+
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
921
|
+
});
|
|
922
|
+
if (initialResponse.status !== 402) {
|
|
923
|
+
return {
|
|
924
|
+
success: initialResponse.ok,
|
|
925
|
+
response: initialResponse
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
const paymentRequirements = await initialResponse.json();
|
|
929
|
+
const x402Requirements = paymentRequirements.x402?.accepts?.[0];
|
|
930
|
+
if (!x402Requirements) {
|
|
931
|
+
return {
|
|
932
|
+
success: false,
|
|
933
|
+
error: "No x402 payment requirements found in 402 response"
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
const authorization = await this.signTransferAuthorization({
|
|
937
|
+
from: this.account.address,
|
|
938
|
+
to: x402Requirements.payTo,
|
|
939
|
+
value: BigInt(x402Requirements.maxAmountRequired),
|
|
940
|
+
validAfter: BigInt(Math.floor(Date.now() / 1e3)),
|
|
941
|
+
validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
|
|
942
|
+
// 15 min
|
|
943
|
+
nonce: `0x${Array.from(
|
|
944
|
+
{ length: 32 },
|
|
945
|
+
() => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
|
|
946
|
+
).join("")}`,
|
|
947
|
+
tokenAddress: x402Requirements.asset
|
|
948
|
+
});
|
|
949
|
+
const paymentProof = {
|
|
950
|
+
x402Version: 1,
|
|
951
|
+
scheme: x402Requirements.scheme,
|
|
952
|
+
network: x402Requirements.network,
|
|
953
|
+
correlationId: "",
|
|
954
|
+
payload: {
|
|
955
|
+
signature: authorization.signature,
|
|
956
|
+
authorization: {
|
|
957
|
+
from: authorization.from,
|
|
958
|
+
to: authorization.to,
|
|
959
|
+
value: authorization.value.toString(),
|
|
960
|
+
validAfter: authorization.validAfter.toString(),
|
|
961
|
+
validBefore: authorization.validBefore.toString(),
|
|
962
|
+
nonce: authorization.nonce
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString("base64");
|
|
967
|
+
const paidResponse = await fetch(request.url, {
|
|
968
|
+
method: request.method ?? "POST",
|
|
969
|
+
headers: {
|
|
970
|
+
"Content-Type": "application/json",
|
|
971
|
+
"X-PAYMENT": paymentHeader,
|
|
972
|
+
...request.headers
|
|
973
|
+
},
|
|
974
|
+
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
975
|
+
});
|
|
976
|
+
return {
|
|
977
|
+
success: paidResponse.ok,
|
|
978
|
+
response: paidResponse,
|
|
979
|
+
paymentDetails: {
|
|
980
|
+
amount: x402Requirements.maxAmountRequired,
|
|
981
|
+
currency: x402Requirements.extra?.currencyCode ?? "USDC",
|
|
982
|
+
network: x402Requirements.network,
|
|
983
|
+
signature: authorization.signature
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
} catch (error) {
|
|
987
|
+
return {
|
|
988
|
+
success: false,
|
|
989
|
+
error: error instanceof Error ? error.message : String(error)
|
|
990
|
+
};
|
|
991
|
+
}
|
|
1135
992
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
if (clone.definitions && typeof clone.definitions[refKey] === "object") {
|
|
1140
|
-
return normalizeInputSchema(clone.definitions[refKey]);
|
|
993
|
+
async signTransferAuthorization(params) {
|
|
994
|
+
if (!this.walletClient.chain) {
|
|
995
|
+
throw new Error("Wallet client chain not configured");
|
|
1141
996
|
}
|
|
997
|
+
const domain = {
|
|
998
|
+
name: "USD Coin",
|
|
999
|
+
version: "2",
|
|
1000
|
+
chainId: this.walletClient.chain.id,
|
|
1001
|
+
verifyingContract: params.tokenAddress
|
|
1002
|
+
};
|
|
1003
|
+
const types = {
|
|
1004
|
+
TransferWithAuthorization: [
|
|
1005
|
+
{ name: "from", type: "address" },
|
|
1006
|
+
{ name: "to", type: "address" },
|
|
1007
|
+
{ name: "value", type: "uint256" },
|
|
1008
|
+
{ name: "validAfter", type: "uint256" },
|
|
1009
|
+
{ name: "validBefore", type: "uint256" },
|
|
1010
|
+
{ name: "nonce", type: "bytes32" }
|
|
1011
|
+
]
|
|
1012
|
+
};
|
|
1013
|
+
const message = {
|
|
1014
|
+
from: params.from,
|
|
1015
|
+
to: params.to,
|
|
1016
|
+
value: params.value,
|
|
1017
|
+
validAfter: params.validAfter,
|
|
1018
|
+
validBefore: params.validBefore,
|
|
1019
|
+
nonce: params.nonce
|
|
1020
|
+
};
|
|
1021
|
+
const signature = await this.walletClient.signTypedData({
|
|
1022
|
+
account: this.account,
|
|
1023
|
+
domain,
|
|
1024
|
+
types,
|
|
1025
|
+
primaryType: "TransferWithAuthorization",
|
|
1026
|
+
message
|
|
1027
|
+
});
|
|
1028
|
+
return {
|
|
1029
|
+
signature,
|
|
1030
|
+
from: params.from,
|
|
1031
|
+
to: params.to,
|
|
1032
|
+
value: params.value,
|
|
1033
|
+
validAfter: params.validAfter,
|
|
1034
|
+
validBefore: params.validBefore,
|
|
1035
|
+
nonce: params.nonce
|
|
1036
|
+
};
|
|
1142
1037
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (!clone.type) {
|
|
1146
|
-
clone.type = "object";
|
|
1038
|
+
getAddress() {
|
|
1039
|
+
return this.account.address;
|
|
1147
1040
|
}
|
|
1148
|
-
|
|
1041
|
+
};
|
|
1042
|
+
async function payX402(config) {
|
|
1043
|
+
const client = new X402Client({
|
|
1044
|
+
privateKey: config.privateKey,
|
|
1045
|
+
...config.rpcUrl ? { rpcUrl: config.rpcUrl } : {}
|
|
1046
|
+
});
|
|
1047
|
+
return client.pay({
|
|
1048
|
+
url: config.url,
|
|
1049
|
+
body: config.body
|
|
1050
|
+
});
|
|
1149
1051
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1052
|
+
var X402BrowserClient = class {
|
|
1053
|
+
constructor(config) {
|
|
1054
|
+
this.walletClient = config.walletClient;
|
|
1055
|
+
this.chainId = config.chainId;
|
|
1056
|
+
}
|
|
1057
|
+
async pay(request) {
|
|
1058
|
+
try {
|
|
1059
|
+
const initialResponse = await fetch(request.url, {
|
|
1060
|
+
method: request.method ?? "POST",
|
|
1061
|
+
headers: {
|
|
1062
|
+
"Content-Type": "application/json",
|
|
1063
|
+
...request.headers
|
|
1064
|
+
},
|
|
1065
|
+
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
1066
|
+
});
|
|
1067
|
+
if (initialResponse.status !== 402) {
|
|
1068
|
+
return {
|
|
1069
|
+
success: initialResponse.ok,
|
|
1070
|
+
response: initialResponse
|
|
1071
|
+
};
|
|
1159
1072
|
}
|
|
1073
|
+
const paymentRequirements = await initialResponse.json();
|
|
1074
|
+
const x402Requirements = paymentRequirements.x402?.accepts?.[0];
|
|
1075
|
+
if (!x402Requirements) {
|
|
1076
|
+
return {
|
|
1077
|
+
success: false,
|
|
1078
|
+
error: "No x402 payment requirements found in 402 response"
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
const account = this.walletClient.account;
|
|
1082
|
+
if (!account) {
|
|
1083
|
+
return {
|
|
1084
|
+
success: false,
|
|
1085
|
+
error: "No account connected to wallet"
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
const authorization = {
|
|
1089
|
+
from: account.address,
|
|
1090
|
+
to: x402Requirements.payTo,
|
|
1091
|
+
value: BigInt(x402Requirements.maxAmountRequired),
|
|
1092
|
+
validAfter: BigInt(Math.floor(Date.now() / 1e3)),
|
|
1093
|
+
validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
|
|
1094
|
+
nonce: `0x${Array.from(
|
|
1095
|
+
{ length: 32 },
|
|
1096
|
+
() => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
|
|
1097
|
+
).join("")}`
|
|
1098
|
+
};
|
|
1099
|
+
const signature = await this.signTransferAuthorization(
|
|
1100
|
+
authorization,
|
|
1101
|
+
x402Requirements.asset
|
|
1102
|
+
);
|
|
1103
|
+
const paymentProof = {
|
|
1104
|
+
x402Version: 1,
|
|
1105
|
+
scheme: x402Requirements.scheme,
|
|
1106
|
+
network: x402Requirements.network,
|
|
1107
|
+
correlationId: "",
|
|
1108
|
+
payload: {
|
|
1109
|
+
signature,
|
|
1110
|
+
authorization: {
|
|
1111
|
+
from: authorization.from,
|
|
1112
|
+
to: authorization.to,
|
|
1113
|
+
value: authorization.value.toString(),
|
|
1114
|
+
validAfter: authorization.validAfter.toString(),
|
|
1115
|
+
validBefore: authorization.validBefore.toString(),
|
|
1116
|
+
nonce: authorization.nonce
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
const paymentHeader = btoa(JSON.stringify(paymentProof));
|
|
1121
|
+
const paidResponse = await fetch(request.url, {
|
|
1122
|
+
method: request.method ?? "POST",
|
|
1123
|
+
headers: {
|
|
1124
|
+
"Content-Type": "application/json",
|
|
1125
|
+
"X-PAYMENT": paymentHeader,
|
|
1126
|
+
...request.headers
|
|
1127
|
+
},
|
|
1128
|
+
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
1129
|
+
});
|
|
1130
|
+
return {
|
|
1131
|
+
success: paidResponse.ok,
|
|
1132
|
+
response: paidResponse,
|
|
1133
|
+
paymentDetails: {
|
|
1134
|
+
amount: x402Requirements.maxAmountRequired,
|
|
1135
|
+
currency: x402Requirements.extra?.currencyCode ?? "USDC",
|
|
1136
|
+
network: x402Requirements.network,
|
|
1137
|
+
signature
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
return {
|
|
1142
|
+
success: false,
|
|
1143
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1144
|
+
};
|
|
1160
1145
|
}
|
|
1161
|
-
const metadataOverrides = isPlainObject(rawConfig.metadataOverrides) ? rawConfig.metadataOverrides : void 0;
|
|
1162
|
-
const config = { enabled: true };
|
|
1163
|
-
if (normalizedMode) {
|
|
1164
|
-
config.mode = normalizedMode;
|
|
1165
|
-
}
|
|
1166
|
-
if (typeof rawConfig.defaultMethod === "string") {
|
|
1167
|
-
config.defaultMethod = rawConfig.defaultMethod.toUpperCase();
|
|
1168
|
-
}
|
|
1169
|
-
if (metadataOverrides) {
|
|
1170
|
-
config.metadataOverrides = metadataOverrides;
|
|
1171
|
-
}
|
|
1172
|
-
return config;
|
|
1173
1146
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1147
|
+
async signTransferAuthorization(authorization, tokenAddress) {
|
|
1148
|
+
const account = this.walletClient.account;
|
|
1149
|
+
if (!account) {
|
|
1150
|
+
throw new Error("No account connected to wallet");
|
|
1151
|
+
}
|
|
1152
|
+
const domain = {
|
|
1153
|
+
name: "USD Coin",
|
|
1154
|
+
version: "2",
|
|
1155
|
+
chainId: this.chainId,
|
|
1156
|
+
verifyingContract: tokenAddress
|
|
1157
|
+
};
|
|
1158
|
+
const types = {
|
|
1159
|
+
TransferWithAuthorization: [
|
|
1160
|
+
{ name: "from", type: "address" },
|
|
1161
|
+
{ name: "to", type: "address" },
|
|
1162
|
+
{ name: "value", type: "uint256" },
|
|
1163
|
+
{ name: "validAfter", type: "uint256" },
|
|
1164
|
+
{ name: "validBefore", type: "uint256" },
|
|
1165
|
+
{ name: "nonce", type: "bytes32" }
|
|
1166
|
+
]
|
|
1167
|
+
};
|
|
1168
|
+
const message = {
|
|
1169
|
+
from: authorization.from,
|
|
1170
|
+
to: authorization.to,
|
|
1171
|
+
value: authorization.value,
|
|
1172
|
+
validAfter: authorization.validAfter,
|
|
1173
|
+
validBefore: authorization.validBefore,
|
|
1174
|
+
nonce: authorization.nonce
|
|
1175
|
+
};
|
|
1176
|
+
return await this.walletClient.signTypedData({
|
|
1177
|
+
account,
|
|
1178
|
+
domain,
|
|
1179
|
+
types,
|
|
1180
|
+
primaryType: "TransferWithAuthorization",
|
|
1181
|
+
message
|
|
1182
|
+
});
|
|
1185
1183
|
}
|
|
1186
|
-
|
|
1184
|
+
};
|
|
1185
|
+
async function payX402WithWallet(walletClient, chainId, request) {
|
|
1186
|
+
const client = new X402BrowserClient({ walletClient, chainId });
|
|
1187
|
+
return client.pay(request);
|
|
1187
1188
|
}
|
|
1188
|
-
|
|
1189
|
-
// src/types/index.ts
|
|
1190
|
-
var HTTP_METHODS2 = ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
|
|
1191
1189
|
var BASE_ALCHEMY_HOST = "https://base-mainnet.g.alchemy.com/v2/";
|
|
1192
1190
|
var ETHEREUM_ALCHEMY_HOST = "https://eth-mainnet.g.alchemy.com/v2/";
|
|
1193
1191
|
var BASE_SEPOLIA_ALCHEMY_HOST = "https://base-sepolia.g.alchemy.com/v2/";
|
|
@@ -1734,8 +1732,8 @@ async function store(input, options) {
|
|
|
1734
1732
|
);
|
|
1735
1733
|
}
|
|
1736
1734
|
const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
|
|
1737
|
-
const
|
|
1738
|
-
const url = `${baseUrl}${
|
|
1735
|
+
const path7 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
|
|
1736
|
+
const url = `${baseUrl}${path7}`;
|
|
1739
1737
|
let response;
|
|
1740
1738
|
try {
|
|
1741
1739
|
response = await fetchFn(url, {
|
|
@@ -1775,8 +1773,8 @@ async function store(input, options) {
|
|
|
1775
1773
|
async function retrieve(params, options) {
|
|
1776
1774
|
const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
|
|
1777
1775
|
const mode = params?.mode ?? "live";
|
|
1778
|
-
const
|
|
1779
|
-
const url = new URL(`${baseUrl}${
|
|
1776
|
+
const path7 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
|
|
1777
|
+
const url = new URL(`${baseUrl}${path7}`);
|
|
1780
1778
|
if (params?.source) url.searchParams.set("source", params.source);
|
|
1781
1779
|
if (params?.walletAddress) url.searchParams.set("walletAddress", params.walletAddress);
|
|
1782
1780
|
if (params?.symbol) url.searchParams.set("symbol", params.symbol);
|
|
@@ -4803,9 +4801,9 @@ function parseOptionalDate(value) {
|
|
|
4803
4801
|
function buildHmacSignature(args) {
|
|
4804
4802
|
const timestamp2 = args.timestamp.toString();
|
|
4805
4803
|
const method = args.method.toUpperCase();
|
|
4806
|
-
const
|
|
4804
|
+
const path7 = args.path;
|
|
4807
4805
|
const body = args.body == null ? "" : typeof args.body === "string" ? args.body : JSON.stringify(args.body);
|
|
4808
|
-
const payload = `${timestamp2}${method}${
|
|
4806
|
+
const payload = `${timestamp2}${method}${path7}${body}`;
|
|
4809
4807
|
const key = Buffer.from(args.secret, "base64");
|
|
4810
4808
|
return createHmac("sha256", key).update(payload).digest("hex");
|
|
4811
4809
|
}
|
|
@@ -6158,9 +6156,9 @@ function assignIfDefined(target, key, value) {
|
|
|
6158
6156
|
target[key] = value;
|
|
6159
6157
|
}
|
|
6160
6158
|
}
|
|
6161
|
-
function buildUrl(baseUrl,
|
|
6159
|
+
function buildUrl(baseUrl, path7) {
|
|
6162
6160
|
const sanitizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
6163
|
-
return `${sanitizedBase}${
|
|
6161
|
+
return `${sanitizedBase}${path7}`;
|
|
6164
6162
|
}
|
|
6165
6163
|
function createAbortBundle(upstreamSignal, timeoutMs) {
|
|
6166
6164
|
const controller = new AbortController();
|
|
@@ -6430,8 +6428,8 @@ var BuildMetadataSchema = z.object({
|
|
|
6430
6428
|
chains: z.array(z.union([z.string(), z.number()])).optional()
|
|
6431
6429
|
}).strict();
|
|
6432
6430
|
function resolveTsconfig(projectRoot) {
|
|
6433
|
-
const candidate =
|
|
6434
|
-
if (
|
|
6431
|
+
const candidate = path5.join(projectRoot, "tsconfig.json");
|
|
6432
|
+
if (fs4.existsSync(candidate)) {
|
|
6435
6433
|
return candidate;
|
|
6436
6434
|
}
|
|
6437
6435
|
return void 0;
|
|
@@ -6441,9 +6439,9 @@ async function transpileWithEsbuild(options) {
|
|
|
6441
6439
|
throw new Error("No entry points provided for esbuild transpilation");
|
|
6442
6440
|
}
|
|
6443
6441
|
const projectRoot = options.projectRoot;
|
|
6444
|
-
const tempBase = options.outDir ??
|
|
6445
|
-
if (!
|
|
6446
|
-
|
|
6442
|
+
const tempBase = options.outDir ?? fs4.mkdtempSync(path5.join(tmpdir(), "opentool-"));
|
|
6443
|
+
if (!fs4.existsSync(tempBase)) {
|
|
6444
|
+
fs4.mkdirSync(tempBase, { recursive: true });
|
|
6447
6445
|
}
|
|
6448
6446
|
const tsconfig = resolveTsconfig(projectRoot);
|
|
6449
6447
|
const buildOptions = {
|
|
@@ -6474,6 +6472,9 @@ async function transpileWithEsbuild(options) {
|
|
|
6474
6472
|
if (options.external && options.external.length > 0) {
|
|
6475
6473
|
buildOptions.external = options.external;
|
|
6476
6474
|
}
|
|
6475
|
+
if (options.nodePaths && options.nodePaths.length > 0) {
|
|
6476
|
+
buildOptions.nodePaths = options.nodePaths;
|
|
6477
|
+
}
|
|
6477
6478
|
if (options.outBase) {
|
|
6478
6479
|
buildOptions.outbase = options.outBase;
|
|
6479
6480
|
}
|
|
@@ -6485,25 +6486,25 @@ async function transpileWithEsbuild(options) {
|
|
|
6485
6486
|
}
|
|
6486
6487
|
await build(buildOptions);
|
|
6487
6488
|
if (options.format === "esm") {
|
|
6488
|
-
const packageJsonPath =
|
|
6489
|
-
if (!
|
|
6490
|
-
|
|
6489
|
+
const packageJsonPath = path5.join(tempBase, "package.json");
|
|
6490
|
+
if (!fs4.existsSync(packageJsonPath)) {
|
|
6491
|
+
fs4.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
|
|
6491
6492
|
}
|
|
6492
6493
|
}
|
|
6493
6494
|
const cleanup = () => {
|
|
6494
6495
|
if (options.outDir) {
|
|
6495
6496
|
return;
|
|
6496
6497
|
}
|
|
6497
|
-
|
|
6498
|
+
fs4.rmSync(tempBase, { recursive: true, force: true });
|
|
6498
6499
|
};
|
|
6499
6500
|
return { outDir: tempBase, cleanup };
|
|
6500
6501
|
}
|
|
6501
6502
|
createRequire(
|
|
6502
|
-
typeof __filename
|
|
6503
|
+
typeof __filename !== "undefined" ? __filename : import.meta.url
|
|
6503
6504
|
);
|
|
6504
6505
|
function resolveCompiledPath(outDir, originalFile, extension = ".js") {
|
|
6505
|
-
const baseName =
|
|
6506
|
-
return
|
|
6506
|
+
const baseName = path5.basename(originalFile).replace(/\.[^.]+$/, "");
|
|
6507
|
+
return path5.join(outDir, `${baseName}${extension}`);
|
|
6507
6508
|
}
|
|
6508
6509
|
async function importFresh(modulePath) {
|
|
6509
6510
|
const fileUrl = pathToFileURL(modulePath).href;
|
|
@@ -6515,16 +6516,16 @@ async function importFresh(modulePath) {
|
|
|
6515
6516
|
// src/cli/shared/metadata.ts
|
|
6516
6517
|
var METADATA_ENTRY = "metadata.ts";
|
|
6517
6518
|
async function loadMetadata2(projectRoot) {
|
|
6518
|
-
const absPath =
|
|
6519
|
-
if (!
|
|
6519
|
+
const absPath = path5.join(projectRoot, METADATA_ENTRY);
|
|
6520
|
+
if (!fs4.existsSync(absPath)) {
|
|
6520
6521
|
return {
|
|
6521
6522
|
metadata: MetadataSchema.parse({}),
|
|
6522
6523
|
sourcePath: "smart defaults (metadata.ts missing)"
|
|
6523
6524
|
};
|
|
6524
6525
|
}
|
|
6525
|
-
const tempDir =
|
|
6526
|
-
if (
|
|
6527
|
-
|
|
6526
|
+
const tempDir = path5.join(projectRoot, ".opentool-temp");
|
|
6527
|
+
if (fs4.existsSync(tempDir)) {
|
|
6528
|
+
fs4.rmSync(tempDir, { recursive: true, force: true });
|
|
6528
6529
|
}
|
|
6529
6530
|
const { outDir, cleanup } = await transpileWithEsbuild({
|
|
6530
6531
|
entryPoints: [absPath],
|
|
@@ -6540,8 +6541,8 @@ async function loadMetadata2(projectRoot) {
|
|
|
6540
6541
|
return { metadata: parsed, sourcePath: absPath };
|
|
6541
6542
|
} finally {
|
|
6542
6543
|
cleanup();
|
|
6543
|
-
if (
|
|
6544
|
-
|
|
6544
|
+
if (fs4.existsSync(tempDir)) {
|
|
6545
|
+
fs4.rmSync(tempDir, { recursive: true, force: true });
|
|
6545
6546
|
}
|
|
6546
6547
|
}
|
|
6547
6548
|
}
|
|
@@ -6563,12 +6564,12 @@ function extractMetadataExport(moduleExports) {
|
|
|
6563
6564
|
return moduleExports;
|
|
6564
6565
|
}
|
|
6565
6566
|
function readPackageJson(projectRoot) {
|
|
6566
|
-
const packagePath =
|
|
6567
|
-
if (!
|
|
6567
|
+
const packagePath = path5.join(projectRoot, "package.json");
|
|
6568
|
+
if (!fs4.existsSync(packagePath)) {
|
|
6568
6569
|
return {};
|
|
6569
6570
|
}
|
|
6570
6571
|
try {
|
|
6571
|
-
const content =
|
|
6572
|
+
const content = fs4.readFileSync(packagePath, "utf8");
|
|
6572
6573
|
return JSON.parse(content);
|
|
6573
6574
|
} catch (error) {
|
|
6574
6575
|
throw new Error(`Failed to read package.json: ${error}`);
|
|
@@ -6579,7 +6580,7 @@ async function buildMetadataArtifact(options) {
|
|
|
6579
6580
|
const packageInfo = readPackageJson(projectRoot);
|
|
6580
6581
|
const { metadata: authored, sourcePath } = await loadMetadata2(projectRoot);
|
|
6581
6582
|
const defaultsApplied = [];
|
|
6582
|
-
const folderName =
|
|
6583
|
+
const folderName = path5.basename(projectRoot);
|
|
6583
6584
|
const name = resolveField(
|
|
6584
6585
|
"name",
|
|
6585
6586
|
authored.name,
|
|
@@ -6791,6 +6792,8 @@ function validateCronTokens(fields, context) {
|
|
|
6791
6792
|
|
|
6792
6793
|
// src/cli/validate.ts
|
|
6793
6794
|
var SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
6795
|
+
var OPENTOOL_ROOT = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
6796
|
+
var OPENTOOL_NODE_MODULES = path5.join(OPENTOOL_ROOT, "node_modules");
|
|
6794
6797
|
var MIN_TEMPLATE_CONFIG_VERSION = 2;
|
|
6795
6798
|
var TEMPLATE_PREVIEW_TITLE_MAX = 80;
|
|
6796
6799
|
var TEMPLATE_PREVIEW_SUBTITLE_MAX = 120;
|
|
@@ -6877,37 +6880,37 @@ function normalizeTemplatePreview(value, file, toolName, requirePreview) {
|
|
|
6877
6880
|
};
|
|
6878
6881
|
}
|
|
6879
6882
|
async function validateCommand(options) {
|
|
6880
|
-
console.log("\u{1F50D} Validating OpenTool
|
|
6883
|
+
console.log("\u{1F50D} Validating OpenTool project...");
|
|
6881
6884
|
try {
|
|
6882
|
-
const toolsDir =
|
|
6883
|
-
if (!
|
|
6885
|
+
const toolsDir = path5.resolve(options.input);
|
|
6886
|
+
if (!fs4.existsSync(toolsDir)) {
|
|
6884
6887
|
throw new Error(`Tools directory not found: ${toolsDir}`);
|
|
6885
6888
|
}
|
|
6886
|
-
const projectRoot =
|
|
6889
|
+
const projectRoot = path5.dirname(toolsDir);
|
|
6887
6890
|
const tools = await loadAndValidateTools(toolsDir, { projectRoot });
|
|
6888
6891
|
if (tools.length === 0) {
|
|
6889
|
-
throw new Error("No valid tools found -
|
|
6892
|
+
throw new Error("No valid tools found - validation aborted");
|
|
6890
6893
|
}
|
|
6891
6894
|
const { metadata, defaultsApplied, sourceMetadataPath } = await buildMetadataArtifact({
|
|
6892
6895
|
projectRoot,
|
|
6893
6896
|
tools
|
|
6894
6897
|
});
|
|
6895
6898
|
logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath);
|
|
6896
|
-
console.log("\n\u2705
|
|
6899
|
+
console.log("\n\u2705 OpenTool validation passed!\n");
|
|
6897
6900
|
} catch (error) {
|
|
6898
|
-
console.error("\u274C
|
|
6901
|
+
console.error("\u274C OpenTool validation failed:", error);
|
|
6899
6902
|
process.exit(1);
|
|
6900
6903
|
}
|
|
6901
6904
|
}
|
|
6902
6905
|
async function loadAndValidateTools(toolsDir, options = {}) {
|
|
6903
|
-
const files =
|
|
6906
|
+
const files = fs4.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path5.extname(file)));
|
|
6904
6907
|
if (files.length === 0) {
|
|
6905
6908
|
return [];
|
|
6906
6909
|
}
|
|
6907
|
-
const projectRoot = options.projectRoot ??
|
|
6908
|
-
const tempDir =
|
|
6909
|
-
if (
|
|
6910
|
-
|
|
6910
|
+
const projectRoot = options.projectRoot ?? path5.dirname(toolsDir);
|
|
6911
|
+
const tempDir = path5.join(toolsDir, ".opentool-temp");
|
|
6912
|
+
if (fs4.existsSync(tempDir)) {
|
|
6913
|
+
fs4.rmSync(tempDir, { recursive: true, force: true });
|
|
6911
6914
|
}
|
|
6912
6915
|
const kebabCase = /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z]+$/;
|
|
6913
6916
|
for (const f of files) {
|
|
@@ -6915,20 +6918,23 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
6915
6918
|
throw new Error(`Tool filename must be kebab-case: ${f}`);
|
|
6916
6919
|
}
|
|
6917
6920
|
}
|
|
6918
|
-
const entryPoints = files.map((file) =>
|
|
6921
|
+
const entryPoints = files.map((file) => path5.join(toolsDir, file));
|
|
6922
|
+
const fallbackNodePaths = [OPENTOOL_NODE_MODULES].filter((dir) => fs4.existsSync(dir));
|
|
6919
6923
|
const { outDir, cleanup } = await transpileWithEsbuild({
|
|
6920
6924
|
entryPoints,
|
|
6921
6925
|
projectRoot,
|
|
6922
6926
|
format: "esm",
|
|
6923
6927
|
outDir: tempDir,
|
|
6924
6928
|
bundle: true,
|
|
6925
|
-
external: ["opentool", "opentool/*"]
|
|
6929
|
+
external: ["opentool", "opentool/*"],
|
|
6930
|
+
...fallbackNodePaths.length > 0 ? { nodePaths: fallbackNodePaths } : {}
|
|
6926
6931
|
});
|
|
6927
6932
|
const tools = [];
|
|
6928
6933
|
try {
|
|
6934
|
+
ensureLocalRuntimeLinks(tempDir);
|
|
6929
6935
|
for (const file of files) {
|
|
6930
6936
|
const compiledPath = resolveCompiledPath(outDir, file);
|
|
6931
|
-
if (!
|
|
6937
|
+
if (!fs4.existsSync(compiledPath)) {
|
|
6932
6938
|
throw new Error(`Failed to compile ${file}`);
|
|
6933
6939
|
}
|
|
6934
6940
|
const moduleExports = await importFresh(compiledPath);
|
|
@@ -7079,11 +7085,6 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
7079
7085
|
if (!schema) {
|
|
7080
7086
|
throw new Error(`${file}: POST tools must export a Zod schema as 'schema'`);
|
|
7081
7087
|
}
|
|
7082
|
-
if (schedule && typeof schedule.cron === "string") {
|
|
7083
|
-
throw new Error(
|
|
7084
|
-
`${file}: POST tools must not define profile.schedule; use GET + cron for scheduled tasks.`
|
|
7085
|
-
);
|
|
7086
|
-
}
|
|
7087
7088
|
}
|
|
7088
7089
|
const httpHandlers = [...httpHandlersRaw];
|
|
7089
7090
|
if (httpHandlers.length === 0) {
|
|
@@ -7131,7 +7132,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
7131
7132
|
httpHandlers,
|
|
7132
7133
|
mcpConfig: normalizeMcpConfig(toolModule.mcp, file),
|
|
7133
7134
|
filename: toBaseName(file),
|
|
7134
|
-
sourcePath:
|
|
7135
|
+
sourcePath: path5.join(toolsDir, file),
|
|
7135
7136
|
handler: async (params) => adapter(params),
|
|
7136
7137
|
payment: paymentExport ?? null,
|
|
7137
7138
|
schedule: normalizedSchedule,
|
|
@@ -7144,12 +7145,30 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
7144
7145
|
}
|
|
7145
7146
|
} finally {
|
|
7146
7147
|
cleanup();
|
|
7147
|
-
if (
|
|
7148
|
-
|
|
7148
|
+
if (fs4.existsSync(tempDir)) {
|
|
7149
|
+
fs4.rmSync(tempDir, { recursive: true, force: true });
|
|
7149
7150
|
}
|
|
7150
7151
|
}
|
|
7151
7152
|
return tools;
|
|
7152
7153
|
}
|
|
7154
|
+
function ensureLocalRuntimeLinks(tempDir) {
|
|
7155
|
+
const nodeModulesDir = path5.join(tempDir, "node_modules");
|
|
7156
|
+
fs4.mkdirSync(nodeModulesDir, { recursive: true });
|
|
7157
|
+
const packageLinks = [
|
|
7158
|
+
{ name: "opentool", target: OPENTOOL_ROOT },
|
|
7159
|
+
{ name: "zod", target: path5.join(OPENTOOL_NODE_MODULES, "zod") }
|
|
7160
|
+
];
|
|
7161
|
+
for (const { name, target } of packageLinks) {
|
|
7162
|
+
if (!fs4.existsSync(target)) {
|
|
7163
|
+
continue;
|
|
7164
|
+
}
|
|
7165
|
+
const linkPath = path5.join(nodeModulesDir, name);
|
|
7166
|
+
if (fs4.existsSync(linkPath)) {
|
|
7167
|
+
continue;
|
|
7168
|
+
}
|
|
7169
|
+
fs4.symlinkSync(target, linkPath, "junction");
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7153
7172
|
function extractToolModule(exportsObject, filename) {
|
|
7154
7173
|
const candidates = [exportsObject, exportsObject?.default];
|
|
7155
7174
|
for (const candidate of candidates) {
|
|
@@ -7347,18 +7366,18 @@ async function generateMetadataCommand(options) {
|
|
|
7347
7366
|
}
|
|
7348
7367
|
}
|
|
7349
7368
|
async function generateMetadata(options) {
|
|
7350
|
-
const toolsDir =
|
|
7351
|
-
if (!
|
|
7369
|
+
const toolsDir = path5.resolve(options.input);
|
|
7370
|
+
if (!fs4.existsSync(toolsDir)) {
|
|
7352
7371
|
throw new Error(`Tools directory not found: ${toolsDir}`);
|
|
7353
7372
|
}
|
|
7354
|
-
const projectRoot =
|
|
7373
|
+
const projectRoot = path5.dirname(toolsDir);
|
|
7355
7374
|
const tools = await loadAndValidateTools(toolsDir, { projectRoot });
|
|
7356
7375
|
const { metadata, defaultsApplied } = await buildMetadataArtifact({
|
|
7357
7376
|
projectRoot,
|
|
7358
7377
|
tools
|
|
7359
7378
|
});
|
|
7360
|
-
const outputPath = options.output ?
|
|
7361
|
-
|
|
7379
|
+
const outputPath = options.output ? path5.resolve(options.output) : path5.join(projectRoot, "metadata.json");
|
|
7380
|
+
fs4.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
|
|
7362
7381
|
return {
|
|
7363
7382
|
metadata,
|
|
7364
7383
|
defaultsApplied,
|