@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.js CHANGED
@@ -447,6 +447,53 @@ var DynamicScriptSkill = class {
447
447
  }
448
448
  }
449
449
  };
450
+
451
+ // src/skills/x402ProxySkill.ts
452
+ var X402ProxySkill = class {
453
+ name;
454
+ description;
455
+ capabilities;
456
+ priceSubunits;
457
+ asset;
458
+ mode = "x402";
459
+ image;
460
+ imageFile;
461
+ llmOverride;
462
+ x402;
463
+ constructor(params) {
464
+ this.name = params.name;
465
+ this.description = params.description;
466
+ this.capabilities = params.capabilities;
467
+ this.priceSubunits = params.priceSubunits;
468
+ this.asset = params.asset;
469
+ this.x402 = params.x402;
470
+ this.image = params.image;
471
+ this.imageFile = params.imageFile;
472
+ }
473
+ /** GET upstream without a query param consumes no buyer input (card should be static). */
474
+ get noInput() {
475
+ return this.x402.method === "GET" && this.x402.queryParam === void 0;
476
+ }
477
+ requireInvoker(ctx) {
478
+ if (ctx.x402 === void 0) {
479
+ throw new Error(
480
+ `Skill "${this.name}": x402 skills require the elisym CLI runtime (no x402 driver in SkillContext)`
481
+ );
482
+ }
483
+ return ctx.x402;
484
+ }
485
+ async preflight(input, ctx) {
486
+ await this.requireInvoker(ctx).preflight(this.x402, input);
487
+ }
488
+ async execute(input, ctx) {
489
+ const result = await this.requireInvoker(ctx).execute(this.x402, input, ctx.signal);
490
+ return {
491
+ data: result.data,
492
+ outputMime: result.outputMime,
493
+ filePath: result.filePath
494
+ };
495
+ }
496
+ };
450
497
  function resolveInsidePath(rootDir, value) {
451
498
  const root = resolve(rootDir);
452
499
  const candidate = resolve(root, value);
@@ -457,6 +504,10 @@ function resolveInsidePath(rootDir, value) {
457
504
  return candidate;
458
505
  }
459
506
  var LIMITS = {
507
+ // Ceiling above which a text/* attachment is NOT materialized back into a string
508
+ // (re-inlined into SkillInput.data) - the consumer gets a filePath / explicit
509
+ // fetch instead. Also bounds the in-memory git-diff buffer (memory-DoS guard).
510
+ MAX_REINLINE_TEXT_BYTES: 4194304,
460
511
  // Upper bound for execution budgets (`max_execution_secs` / `execution_timeout_secs`).
461
512
  // Distinct from MAX_TIMEOUT_SECS (the result-wait cap): execution budgets may be
462
513
  // hours, so this exists only to keep `secs * 1000` within Node's setTimeout limit
@@ -536,8 +587,10 @@ var VALID_MODES = [
536
587
  "llm",
537
588
  "static-file",
538
589
  "static-script",
539
- "dynamic-script"
590
+ "dynamic-script",
591
+ "x402"
540
592
  ];
593
+ var DEFAULT_X402_MAX_INPUT_BYTES = 1e5;
541
594
  function solToLamports(sol) {
542
595
  const asString = (typeof sol === "string" ? sol : String(sol)).trim();
543
596
  if (/^0+(?:\.0+)?$/.test(asString)) {
@@ -693,6 +746,11 @@ function validateLlmOverride(skillName, frontmatter, mode) {
693
746
  if (!hasProvider && !hasModel && !hasMaxTokens) {
694
747
  return void 0;
695
748
  }
749
+ if (mode === "x402") {
750
+ throw new Error(
751
+ `SKILL.md "${skillName}": "provider"/"model"/"max_tokens" are not valid in mode 'x402'`
752
+ );
753
+ }
696
754
  if (hasMaxTokens && mode !== "llm") {
697
755
  throw new Error(
698
756
  `SKILL.md "${skillName}": "max_tokens" is only valid in mode 'llm' (got '${mode}'). For script modes, control token limits inside the script.`
@@ -816,6 +874,114 @@ function validateMaxExecutionSecs(skillName, raw) {
816
874
  }
817
875
  return raw;
818
876
  }
877
+ var X402_METHODS = ["GET", "POST"];
878
+ function validateX402Config(skillName, frontmatter, mode, options) {
879
+ const fieldNames = [
880
+ "x402_url",
881
+ "x402_method",
882
+ "x402_query_param",
883
+ "x402_max_upstream",
884
+ "x402_max_input_bytes"
885
+ ];
886
+ const presentFields = fieldNames.filter(
887
+ (field) => frontmatter[field] !== void 0 && frontmatter[field] !== null
888
+ );
889
+ if (mode !== "x402") {
890
+ if (presentFields.length > 0) {
891
+ throw new Error(
892
+ `SKILL.md "${skillName}": "${presentFields[0]}" is only valid in mode 'x402'`
893
+ );
894
+ }
895
+ return void 0;
896
+ }
897
+ if (!options.allowX402Skills) {
898
+ throw new Error(
899
+ `SKILL.md "${skillName}": x402 skills require the elisym CLI runtime; this host cannot execute mode 'x402'`
900
+ );
901
+ }
902
+ if (typeof frontmatter.x402_url !== "string" || frontmatter.x402_url.length === 0) {
903
+ throw new Error(`SKILL.md "${skillName}": mode 'x402' requires "x402_url" (string)`);
904
+ }
905
+ let url;
906
+ try {
907
+ url = new URL(frontmatter.x402_url);
908
+ } catch {
909
+ throw new Error(`SKILL.md "${skillName}": "x402_url" is not a valid URL`);
910
+ }
911
+ const isLoopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
912
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) {
913
+ throw new Error(
914
+ `SKILL.md "${skillName}": "x402_url" must be an https:// URL (plain http is allowed for localhost only)`
915
+ );
916
+ }
917
+ let method = "POST";
918
+ if (frontmatter.x402_method !== void 0 && frontmatter.x402_method !== null) {
919
+ if (typeof frontmatter.x402_method !== "string" || !X402_METHODS.includes(frontmatter.x402_method)) {
920
+ throw new Error(
921
+ `SKILL.md "${skillName}": "x402_method" must be one of ${X402_METHODS.join(", ")}`
922
+ );
923
+ }
924
+ method = frontmatter.x402_method;
925
+ }
926
+ let queryParam;
927
+ if (frontmatter.x402_query_param !== void 0 && frontmatter.x402_query_param !== null) {
928
+ if (method !== "GET") {
929
+ throw new Error(
930
+ `SKILL.md "${skillName}": "x402_query_param" is only valid with x402_method 'GET' (POST maps the input to the request body)`
931
+ );
932
+ }
933
+ if (typeof frontmatter.x402_query_param !== "string" || frontmatter.x402_query_param.length === 0) {
934
+ throw new Error(`SKILL.md "${skillName}": "x402_query_param" must be a non-empty string`);
935
+ }
936
+ if (url.searchParams.has(frontmatter.x402_query_param)) {
937
+ throw new Error(
938
+ `SKILL.md "${skillName}": "x402_query_param" ("${frontmatter.x402_query_param}") collides with a parameter already present in "x402_url"`
939
+ );
940
+ }
941
+ queryParam = frontmatter.x402_query_param;
942
+ }
943
+ const rawMaxUpstream = frontmatter.x402_max_upstream;
944
+ if (rawMaxUpstream === void 0 || rawMaxUpstream === null) {
945
+ throw new Error(
946
+ `SKILL.md "${skillName}": mode 'x402' requires "x402_max_upstream" (integer subunits - the ceiling on the upstream quote)`
947
+ );
948
+ }
949
+ let maxUpstreamSubunits;
950
+ if (typeof rawMaxUpstream === "number" && Number.isSafeInteger(rawMaxUpstream)) {
951
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
952
+ } else if (typeof rawMaxUpstream === "string" && /^[0-9]+$/.test(rawMaxUpstream)) {
953
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
954
+ } else {
955
+ throw new Error(
956
+ `SKILL.md "${skillName}": "x402_max_upstream" must be a non-negative integer (subunits)`
957
+ );
958
+ }
959
+ if (maxUpstreamSubunits <= 0n) {
960
+ throw new Error(`SKILL.md "${skillName}": "x402_max_upstream" must be > 0`);
961
+ }
962
+ let maxInputBytes = DEFAULT_X402_MAX_INPUT_BYTES;
963
+ if (frontmatter.x402_max_input_bytes !== void 0 && frontmatter.x402_max_input_bytes !== null) {
964
+ const raw = frontmatter.x402_max_input_bytes;
965
+ if (typeof raw !== "number" || !Number.isInteger(raw) || raw <= 0) {
966
+ throw new Error(
967
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be a positive integer (bytes)`
968
+ );
969
+ }
970
+ if (raw > LIMITS.MAX_REINLINE_TEXT_BYTES) {
971
+ throw new Error(
972
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be <= ${LIMITS.MAX_REINLINE_TEXT_BYTES} (inputs above it never reach the skill as text)`
973
+ );
974
+ }
975
+ maxInputBytes = raw;
976
+ }
977
+ return {
978
+ url: frontmatter.x402_url,
979
+ method,
980
+ queryParam,
981
+ maxUpstreamSubunits,
982
+ maxInputBytes
983
+ };
984
+ }
819
985
  function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
820
986
  if (typeof frontmatter.name !== "string" || frontmatter.name.length === 0) {
821
987
  throw new Error('SKILL.md: missing or invalid "name" field');
@@ -1008,6 +1174,7 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1008
1174
  frontmatter.name,
1009
1175
  frontmatter.max_execution_secs
1010
1176
  );
1177
+ const x402 = validateX402Config(frontmatter.name, frontmatter, mode, options);
1011
1178
  return {
1012
1179
  name: frontmatter.name,
1013
1180
  description: frontmatter.description,
@@ -1029,7 +1196,9 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1029
1196
  inputMime,
1030
1197
  inputText,
1031
1198
  rateLimit,
1032
- executionTimeoutSecs
1199
+ executionTimeoutSecs,
1200
+ x402,
1201
+ noInput: x402 === void 0 ? void 0 : x402.method === "GET" && x402.queryParam === void 0
1033
1202
  };
1034
1203
  }
1035
1204
  function buildSkillFromParsed(parsed, skillDir, logger) {
@@ -1108,6 +1277,23 @@ function buildSkillFromParsed(parsed, skillDir, logger) {
1108
1277
  };
1109
1278
  return parsed.mode === "dynamic-script" ? new DynamicScriptSkill({ ...scriptParams, outputMime: parsed.outputMime }) : new StaticScriptSkill(scriptParams);
1110
1279
  }
1280
+ case "x402": {
1281
+ if (parsed.x402 === void 0) {
1282
+ throw new Error(
1283
+ `SKILL.md "${parsed.name}": internal error - x402 config missing for mode 'x402'`
1284
+ );
1285
+ }
1286
+ return new X402ProxySkill({
1287
+ name: parsed.name,
1288
+ description: parsed.description,
1289
+ capabilities: parsed.capabilities,
1290
+ priceSubunits: parsed.priceSubunits,
1291
+ asset: parsed.asset,
1292
+ x402: parsed.x402,
1293
+ image: parsed.image,
1294
+ imageFile
1295
+ });
1296
+ }
1111
1297
  }
1112
1298
  }
1113
1299
  function loadSkillsFromDir(skillsDir, options = {}) {
@@ -1143,6 +1329,6 @@ function loadSkillsFromDir(skillsDir, options = {}) {
1143
1329
  return skills;
1144
1330
  }
1145
1331
 
1146
- export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
1332
+ export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_X402_MAX_INPUT_BYTES, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, X402ProxySkill, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
1147
1333
  //# sourceMappingURL=skills.js.map
1148
1334
  //# sourceMappingURL=skills.js.map