@elisym/sdk 0.25.4 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/skills.cjs CHANGED
@@ -454,6 +454,53 @@ var DynamicScriptSkill = class {
454
454
  }
455
455
  }
456
456
  };
457
+
458
+ // src/skills/x402ProxySkill.ts
459
+ var X402ProxySkill = class {
460
+ name;
461
+ description;
462
+ capabilities;
463
+ priceSubunits;
464
+ asset;
465
+ mode = "x402";
466
+ image;
467
+ imageFile;
468
+ llmOverride;
469
+ x402;
470
+ constructor(params) {
471
+ this.name = params.name;
472
+ this.description = params.description;
473
+ this.capabilities = params.capabilities;
474
+ this.priceSubunits = params.priceSubunits;
475
+ this.asset = params.asset;
476
+ this.x402 = params.x402;
477
+ this.image = params.image;
478
+ this.imageFile = params.imageFile;
479
+ }
480
+ /** GET upstream without a query param consumes no buyer input (card should be static). */
481
+ get noInput() {
482
+ return this.x402.method === "GET" && this.x402.queryParam === void 0;
483
+ }
484
+ requireInvoker(ctx) {
485
+ if (ctx.x402 === void 0) {
486
+ throw new Error(
487
+ `Skill "${this.name}": x402 skills require the elisym CLI runtime (no x402 driver in SkillContext)`
488
+ );
489
+ }
490
+ return ctx.x402;
491
+ }
492
+ async preflight(input, ctx) {
493
+ await this.requireInvoker(ctx).preflight(this.x402, input);
494
+ }
495
+ async execute(input, ctx) {
496
+ const result = await this.requireInvoker(ctx).execute(this.x402, input, ctx.signal);
497
+ return {
498
+ data: result.data,
499
+ outputMime: result.outputMime,
500
+ filePath: result.filePath
501
+ };
502
+ }
503
+ };
457
504
  function resolveInsidePath(rootDir, value) {
458
505
  const root = path.resolve(rootDir);
459
506
  const candidate = path.resolve(root, value);
@@ -464,6 +511,10 @@ function resolveInsidePath(rootDir, value) {
464
511
  return candidate;
465
512
  }
466
513
  var LIMITS = {
514
+ // Ceiling above which a text/* attachment is NOT materialized back into a string
515
+ // (re-inlined into SkillInput.data) - the consumer gets a filePath / explicit
516
+ // fetch instead. Also bounds the in-memory git-diff buffer (memory-DoS guard).
517
+ MAX_REINLINE_TEXT_BYTES: 4194304,
467
518
  // Upper bound for execution budgets (`max_execution_secs` / `execution_timeout_secs`).
468
519
  // Distinct from MAX_TIMEOUT_SECS (the result-wait cap): execution budgets may be
469
520
  // hours, so this exists only to keep `secs * 1000` within Node's setTimeout limit
@@ -543,8 +594,10 @@ var VALID_MODES = [
543
594
  "llm",
544
595
  "static-file",
545
596
  "static-script",
546
- "dynamic-script"
597
+ "dynamic-script",
598
+ "x402"
547
599
  ];
600
+ var DEFAULT_X402_MAX_INPUT_BYTES = 1e5;
548
601
  function solToLamports(sol) {
549
602
  const asString = (typeof sol === "string" ? sol : String(sol)).trim();
550
603
  if (/^0+(?:\.0+)?$/.test(asString)) {
@@ -700,6 +753,11 @@ function validateLlmOverride(skillName, frontmatter, mode) {
700
753
  if (!hasProvider && !hasModel && !hasMaxTokens) {
701
754
  return void 0;
702
755
  }
756
+ if (mode === "x402") {
757
+ throw new Error(
758
+ `SKILL.md "${skillName}": "provider"/"model"/"max_tokens" are not valid in mode 'x402'`
759
+ );
760
+ }
703
761
  if (hasMaxTokens && mode !== "llm") {
704
762
  throw new Error(
705
763
  `SKILL.md "${skillName}": "max_tokens" is only valid in mode 'llm' (got '${mode}'). For script modes, control token limits inside the script.`
@@ -823,6 +881,114 @@ function validateMaxExecutionSecs(skillName, raw) {
823
881
  }
824
882
  return raw;
825
883
  }
884
+ var X402_METHODS = ["GET", "POST"];
885
+ function validateX402Config(skillName, frontmatter, mode, options) {
886
+ const fieldNames = [
887
+ "x402_url",
888
+ "x402_method",
889
+ "x402_query_param",
890
+ "x402_max_upstream",
891
+ "x402_max_input_bytes"
892
+ ];
893
+ const presentFields = fieldNames.filter(
894
+ (field) => frontmatter[field] !== void 0 && frontmatter[field] !== null
895
+ );
896
+ if (mode !== "x402") {
897
+ if (presentFields.length > 0) {
898
+ throw new Error(
899
+ `SKILL.md "${skillName}": "${presentFields[0]}" is only valid in mode 'x402'`
900
+ );
901
+ }
902
+ return void 0;
903
+ }
904
+ if (!options.allowX402Skills) {
905
+ throw new Error(
906
+ `SKILL.md "${skillName}": x402 skills require the elisym CLI runtime; this host cannot execute mode 'x402'`
907
+ );
908
+ }
909
+ if (typeof frontmatter.x402_url !== "string" || frontmatter.x402_url.length === 0) {
910
+ throw new Error(`SKILL.md "${skillName}": mode 'x402' requires "x402_url" (string)`);
911
+ }
912
+ let url;
913
+ try {
914
+ url = new URL(frontmatter.x402_url);
915
+ } catch {
916
+ throw new Error(`SKILL.md "${skillName}": "x402_url" is not a valid URL`);
917
+ }
918
+ const isLoopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
919
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) {
920
+ throw new Error(
921
+ `SKILL.md "${skillName}": "x402_url" must be an https:// URL (plain http is allowed for localhost only)`
922
+ );
923
+ }
924
+ let method = "POST";
925
+ if (frontmatter.x402_method !== void 0 && frontmatter.x402_method !== null) {
926
+ if (typeof frontmatter.x402_method !== "string" || !X402_METHODS.includes(frontmatter.x402_method)) {
927
+ throw new Error(
928
+ `SKILL.md "${skillName}": "x402_method" must be one of ${X402_METHODS.join(", ")}`
929
+ );
930
+ }
931
+ method = frontmatter.x402_method;
932
+ }
933
+ let queryParam;
934
+ if (frontmatter.x402_query_param !== void 0 && frontmatter.x402_query_param !== null) {
935
+ if (method !== "GET") {
936
+ throw new Error(
937
+ `SKILL.md "${skillName}": "x402_query_param" is only valid with x402_method 'GET' (POST maps the input to the request body)`
938
+ );
939
+ }
940
+ if (typeof frontmatter.x402_query_param !== "string" || frontmatter.x402_query_param.length === 0) {
941
+ throw new Error(`SKILL.md "${skillName}": "x402_query_param" must be a non-empty string`);
942
+ }
943
+ if (url.searchParams.has(frontmatter.x402_query_param)) {
944
+ throw new Error(
945
+ `SKILL.md "${skillName}": "x402_query_param" ("${frontmatter.x402_query_param}") collides with a parameter already present in "x402_url"`
946
+ );
947
+ }
948
+ queryParam = frontmatter.x402_query_param;
949
+ }
950
+ const rawMaxUpstream = frontmatter.x402_max_upstream;
951
+ if (rawMaxUpstream === void 0 || rawMaxUpstream === null) {
952
+ throw new Error(
953
+ `SKILL.md "${skillName}": mode 'x402' requires "x402_max_upstream" (integer subunits - the ceiling on the upstream quote)`
954
+ );
955
+ }
956
+ let maxUpstreamSubunits;
957
+ if (typeof rawMaxUpstream === "number" && Number.isSafeInteger(rawMaxUpstream)) {
958
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
959
+ } else if (typeof rawMaxUpstream === "string" && /^[0-9]+$/.test(rawMaxUpstream)) {
960
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
961
+ } else {
962
+ throw new Error(
963
+ `SKILL.md "${skillName}": "x402_max_upstream" must be a non-negative integer (subunits)`
964
+ );
965
+ }
966
+ if (maxUpstreamSubunits <= 0n) {
967
+ throw new Error(`SKILL.md "${skillName}": "x402_max_upstream" must be > 0`);
968
+ }
969
+ let maxInputBytes = DEFAULT_X402_MAX_INPUT_BYTES;
970
+ if (frontmatter.x402_max_input_bytes !== void 0 && frontmatter.x402_max_input_bytes !== null) {
971
+ const raw = frontmatter.x402_max_input_bytes;
972
+ if (typeof raw !== "number" || !Number.isInteger(raw) || raw <= 0) {
973
+ throw new Error(
974
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be a positive integer (bytes)`
975
+ );
976
+ }
977
+ if (raw > LIMITS.MAX_REINLINE_TEXT_BYTES) {
978
+ throw new Error(
979
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be <= ${LIMITS.MAX_REINLINE_TEXT_BYTES} (inputs above it never reach the skill as text)`
980
+ );
981
+ }
982
+ maxInputBytes = raw;
983
+ }
984
+ return {
985
+ url: frontmatter.x402_url,
986
+ method,
987
+ queryParam,
988
+ maxUpstreamSubunits,
989
+ maxInputBytes
990
+ };
991
+ }
826
992
  function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
827
993
  if (typeof frontmatter.name !== "string" || frontmatter.name.length === 0) {
828
994
  throw new Error('SKILL.md: missing or invalid "name" field');
@@ -1015,6 +1181,7 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1015
1181
  frontmatter.name,
1016
1182
  frontmatter.max_execution_secs
1017
1183
  );
1184
+ const x402 = validateX402Config(frontmatter.name, frontmatter, mode, options);
1018
1185
  return {
1019
1186
  name: frontmatter.name,
1020
1187
  description: frontmatter.description,
@@ -1036,7 +1203,9 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1036
1203
  inputMime,
1037
1204
  inputText,
1038
1205
  rateLimit,
1039
- executionTimeoutSecs
1206
+ executionTimeoutSecs,
1207
+ x402,
1208
+ noInput: x402 === void 0 ? void 0 : x402.method === "GET" && x402.queryParam === void 0
1040
1209
  };
1041
1210
  }
1042
1211
  function buildSkillFromParsed(parsed, skillDir, logger) {
@@ -1115,6 +1284,23 @@ function buildSkillFromParsed(parsed, skillDir, logger) {
1115
1284
  };
1116
1285
  return parsed.mode === "dynamic-script" ? new DynamicScriptSkill({ ...scriptParams, outputMime: parsed.outputMime }) : new StaticScriptSkill(scriptParams);
1117
1286
  }
1287
+ case "x402": {
1288
+ if (parsed.x402 === void 0) {
1289
+ throw new Error(
1290
+ `SKILL.md "${parsed.name}": internal error - x402 config missing for mode 'x402'`
1291
+ );
1292
+ }
1293
+ return new X402ProxySkill({
1294
+ name: parsed.name,
1295
+ description: parsed.description,
1296
+ capabilities: parsed.capabilities,
1297
+ priceSubunits: parsed.priceSubunits,
1298
+ asset: parsed.asset,
1299
+ x402: parsed.x402,
1300
+ image: parsed.image,
1301
+ imageFile
1302
+ });
1303
+ }
1118
1304
  }
1119
1305
  }
1120
1306
  function loadSkillsFromDir(skillsDir, options = {}) {
@@ -1152,12 +1338,14 @@ function loadSkillsFromDir(skillsDir, options = {}) {
1152
1338
 
1153
1339
  exports.DEFAULT_MAX_TOOL_ROUNDS = DEFAULT_MAX_TOOL_ROUNDS;
1154
1340
  exports.DEFAULT_SCRIPT_TIMEOUT_MS = DEFAULT_SCRIPT_TIMEOUT_MS;
1341
+ exports.DEFAULT_X402_MAX_INPUT_BYTES = DEFAULT_X402_MAX_INPUT_BYTES;
1155
1342
  exports.DynamicScriptSkill = DynamicScriptSkill;
1156
1343
  exports.MAX_SCRIPT_OUTPUT = MAX_SCRIPT_OUTPUT;
1157
1344
  exports.MAX_STATIC_FILE_SIZE = MAX_STATIC_FILE_SIZE;
1158
1345
  exports.ScriptSkill = ScriptSkill;
1159
1346
  exports.StaticFileSkill = StaticFileSkill;
1160
1347
  exports.StaticScriptSkill = StaticScriptSkill;
1348
+ exports.X402ProxySkill = X402ProxySkill;
1161
1349
  exports.loadSkillsFromDir = loadSkillsFromDir;
1162
1350
  exports.parseSkillMd = parseSkillMd;
1163
1351
  exports.resolveInsidePath = resolveInsidePath;