@elisym/cli 0.27.0 → 0.28.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/index.js CHANGED
@@ -9,7 +9,7 @@ import { generateSecretKey, getPublicKey, nip19, verifyEvent } from 'nostr-tools
9
9
  import YAML2 from 'yaml';
10
10
  import { Command } from 'commander';
11
11
  import { mkdir, writeFile, readFile, mkdtemp, rm, rename, stat } from 'node:fs/promises';
12
- import { parseSkillMd, validateSkillFrontmatter, DEFAULT_X402_MAX_INPUT_BYTES, resolveInsidePathReal, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill as DynamicScriptSkill$1, StaticScriptSkill as StaticScriptSkill$1, StaticFileSkill as StaticFileSkill$1, ScriptSkill as ScriptSkill$1 } from '@elisym/sdk/skills';
12
+ import { parseSkillMd, validateSkillFrontmatter, DEFAULT_X402_MAX_INPUT_BYTES, resolveInsidePathReal, DEFAULT_SCRIPT_TIMEOUT_MS, OnchainCallSkill, DynamicScriptSkill as DynamicScriptSkill$1, StaticScriptSkill as StaticScriptSkill$1, StaticFileSkill as StaticFileSkill$1, ScriptSkill as ScriptSkill$1 } from '@elisym/sdk/skills';
13
13
  import chalk from 'chalk';
14
14
  import Decimal from 'decimal.js-light';
15
15
  import { decodePaymentRequiredHeader } from '@x402/core/http';
@@ -6704,6 +6704,51 @@ var DynamicScriptSkill = class {
6704
6704
  return this.inner.execute(input, ctx);
6705
6705
  }
6706
6706
  };
6707
+ var OnchainSkill = class {
6708
+ name;
6709
+ description;
6710
+ capabilities;
6711
+ priceSubunits;
6712
+ asset;
6713
+ mode = "onchain";
6714
+ image;
6715
+ imageFile;
6716
+ dir;
6717
+ llmOverride;
6718
+ onchain;
6719
+ inner;
6720
+ constructor(params) {
6721
+ this.name = params.name;
6722
+ this.description = params.description;
6723
+ this.capabilities = params.capabilities;
6724
+ this.priceSubunits = params.priceSubunits;
6725
+ this.asset = params.asset;
6726
+ this.image = params.image;
6727
+ this.imageFile = params.imageFile;
6728
+ this.dir = params.dir;
6729
+ this.llmOverride = params.llmOverride;
6730
+ this.onchain = params.onchain;
6731
+ this.inner = new OnchainCallSkill({
6732
+ name: params.name,
6733
+ description: params.description,
6734
+ capabilities: params.capabilities,
6735
+ priceSubunits: BigInt(Math.round(params.priceSubunits)),
6736
+ asset: params.asset,
6737
+ scriptPath: params.scriptPath,
6738
+ scriptArgs: params.scriptArgs,
6739
+ scriptTimeoutMs: params.scriptTimeoutMs,
6740
+ scriptEnv: params.scriptEnv,
6741
+ image: params.image,
6742
+ imageFile: params.imageFile,
6743
+ llmOverride: params.llmOverride,
6744
+ onchain: params.onchain,
6745
+ network: params.network
6746
+ });
6747
+ }
6748
+ async execute(input, ctx) {
6749
+ return this.inner.execute(input, ctx);
6750
+ }
6751
+ };
6707
6752
  var ScriptSkill = class {
6708
6753
  name;
6709
6754
  description;
@@ -6836,7 +6881,7 @@ function scopeLlmKeys(scriptEnv, declaredProvider) {
6836
6881
  }
6837
6882
  return env;
6838
6883
  }
6839
- function buildCliSkill(parsed, entryPath, scriptEnv) {
6884
+ function buildCliSkill(parsed, entryPath, scriptEnv, network) {
6840
6885
  let safeImageFile = parsed.imageFile;
6841
6886
  if (safeImageFile !== void 0 && resolveInsidePathReal(entryPath, safeImageFile) === null) {
6842
6887
  console.warn(
@@ -6924,6 +6969,35 @@ function buildCliSkill(parsed, entryPath, scriptEnv) {
6924
6969
  }) : new StaticScriptSkill(scriptParams);
6925
6970
  break;
6926
6971
  }
6972
+ case "onchain": {
6973
+ if (parsed.script === void 0 || parsed.onchain === void 0) {
6974
+ throw new Error(
6975
+ `SKILL.md "${parsed.name}": internal error - script or onchain block missing for mode 'onchain'`
6976
+ );
6977
+ }
6978
+ const scriptPath = resolveInsidePathReal(entryPath, parsed.script);
6979
+ if (!scriptPath) {
6980
+ throw new Error(`SKILL.md "${parsed.name}": "script" must stay inside the skill directory`);
6981
+ }
6982
+ skill = new OnchainSkill({
6983
+ name: parsed.name,
6984
+ description: parsed.description,
6985
+ capabilities: parsed.capabilities,
6986
+ priceSubunits: Number(parsed.priceSubunits),
6987
+ asset: parsed.asset,
6988
+ scriptPath,
6989
+ scriptArgs: parsed.scriptArgs,
6990
+ scriptTimeoutMs: parsed.scriptTimeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS,
6991
+ scriptEnv: scopeLlmKeys(scriptEnv, parsed.llmOverride?.provider),
6992
+ image: parsed.image,
6993
+ imageFile: safeImageFile,
6994
+ dir: entryPath,
6995
+ llmOverride: parsed.llmOverride,
6996
+ onchain: parsed.onchain,
6997
+ network
6998
+ });
6999
+ break;
7000
+ }
6927
7001
  case "x402": {
6928
7002
  if (parsed.x402 === void 0) {
6929
7003
  throw new Error(
@@ -6989,7 +7063,7 @@ function loadSkillsFromDir(skillsDir, options) {
6989
7063
  warn: (_obj, msg) => console.warn(` ! ${msg ?? ""}`)
6990
7064
  }
6991
7065
  });
6992
- skills.push(buildCliSkill(parsed, entryPath, options.scriptEnv));
7066
+ skills.push(buildCliSkill(parsed, entryPath, options.scriptEnv, options.network));
6993
7067
  } catch (e) {
6994
7068
  const message = e instanceof Error ? e.message : String(e);
6995
7069
  console.warn(` ! Skipping skill "${entry}": ${message}`);
@@ -8368,6 +8442,12 @@ async function cmdStart(nameArg, options = {}) {
8368
8442
  agentName,
8369
8443
  agentDescription: loaded.yaml.description ?? ""
8370
8444
  };
8445
+ const onchainSkillCount = allSkills.filter((skill) => skill.onchain !== void 0).length;
8446
+ if (solanaAddress === void 0 && onchainSkillCount > 0) {
8447
+ console.warn(
8448
+ ` ! ${onchainSkillCount} onchain capability(ies) declare a block but this agent has no Solana payment address, so the network their cards would promise is a guess. Add a payments entry to elisym.yaml.`
8449
+ );
8450
+ }
8371
8451
  const x402Skills = allSkills.filter((skill) => skill.mode === "x402");
8372
8452
  let x402InvariantBroken;
8373
8453
  if (x402Skills.length > 0) {
@@ -8377,9 +8457,13 @@ async function cmdStart(nameArg, options = {}) {
8377
8457
  } else if (!solanaAddress) {
8378
8458
  x402InvariantBroken = "no payments[] entry in elisym.yaml";
8379
8459
  } else {
8380
- const bridgeSigner = await signerFromSecretKeyBase58(solanaSecretKey);
8381
- if (bridgeSigner.address !== solanaAddress) {
8382
- x402InvariantBroken = `payments[].address (${solanaAddress}) differs from the solana_secret_key address (${bridgeSigner.address})`;
8460
+ try {
8461
+ const bridgeSigner = await signerFromSecretKeyBase58(solanaSecretKey);
8462
+ if (bridgeSigner.address !== solanaAddress) {
8463
+ x402InvariantBroken = `payments[].address (${solanaAddress}) differs from the solana_secret_key address (${bridgeSigner.address})`;
8464
+ }
8465
+ } catch (error) {
8466
+ x402InvariantBroken = `solana_secret_key is present but unreadable - ${error instanceof Error ? error.message : String(error)}`;
8383
8467
  }
8384
8468
  }
8385
8469
  for (const skill of x402Skills) {
@@ -8655,6 +8739,16 @@ async function cmdStart(nameArg, options = {}) {
8655
8739
  );
8656
8740
  continue;
8657
8741
  }
8742
+ if (withholdsOnchainCard(skill, solanaAddress)) {
8743
+ console.warn(
8744
+ ` ! Not republishing "${skill.name}" - no Solana payments entry. An earlier card for it, if any, stays on the relays until it expires.`
8745
+ );
8746
+ logger.warn(
8747
+ { event: "publish_skipped_onchain_no_wallet", kind: 31990, skill: skill.name },
8748
+ "capability not advertised - onchain block with no Solana payment address"
8749
+ );
8750
+ continue;
8751
+ }
8658
8752
  try {
8659
8753
  await client.discovery.publishCapability(identity2, buildCard(skill), kinds);
8660
8754
  cardsPublished += 1;
@@ -8832,6 +8926,13 @@ function buildCapabilityCard(skill, inputs) {
8832
8926
  // exists (delegatePubkey resolved). The pubkey is injected here, never
8833
8927
  // taken from the SKILL.md - the operator declares only the cap/mechanism.
8834
8928
  ...skill.delegation && delegatePubkey ? { delegation: { ...skill.delegation, delegate_pubkey: delegatePubkey } } : {},
8929
+ // On-chain promise (`mode: onchain`): the network is stamped from the
8930
+ // agent's wallet here, never from SKILL.md, so a capability copied between
8931
+ // a devnet and a mainnet agent cannot advertise the wrong chain.
8932
+ // Only stamped when the agent actually has a Solana payment entry: without
8933
+ // one `walletNetwork` is a default, and a card must never promise a chain
8934
+ // the operator never chose. `cmdStart` warns for such a skill at step 7a.
8935
+ ...skill.onchain && solanaAddress ? { onchain: { ...skill.onchain, network: walletNetwork } } : {},
8835
8936
  payment: solanaAddress ? {
8836
8937
  chain: "solana",
8837
8938
  network: walletNetwork,
@@ -8844,6 +8945,9 @@ function buildCapabilityCard(skill, inputs) {
8844
8945
  } : void 0
8845
8946
  };
8846
8947
  }
8948
+ function withholdsOnchainCard(skill, solanaAddress) {
8949
+ return skill.onchain !== void 0 && solanaAddress === void 0;
8950
+ }
8847
8951
  function buildScriptEnv(secrets) {
8848
8952
  const scriptEnv = { ...process.env };
8849
8953
  delete scriptEnv.ELISYM_PASSPHRASE;