@elisym/cli 0.19.0 → 0.20.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
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env -S node --no-deprecation
2
- import { readFileSync, existsSync, readdirSync, statSync, renameSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { ReadableStream } from 'node:stream/web';
3
+ import { readFileSync, existsSync, readdirSync, statSync, renameSync, chmodSync, mkdirSync, writeFileSync } from 'node:fs';
3
4
  import { dirname, join, resolve, basename, relative, sep } from 'node:path';
4
5
  import { SolanaPaymentStrategy, validateAgentName, RELAYS, ElisymIdentity, formatSol, formatAssetAmount, USDC_SOLANA_DEVNET, ElisymClient, MediaService, POLICY_D_TAG_PREFIX, KIND_LONG_FORM_ARTICLE, POLICY_T_TAG, jobRequestKind, DEFAULT_KIND_OFFSET, toDTag, DEFAULTS, makeCensor, DEFAULT_REDACT_PATHS, createSlidingWindowLimiter, getProtocolProgramId, getProtocolConfig, LIMITS, calculateProtocolFee, BoundedSet, KIND_JOB_FEEDBACK, NATIVE_SOL } from '@elisym/sdk';
5
6
  import { ElisymYamlSchema, resolveInHome, resolveInProject, createAgentDir, writeYamlInitial, writeExampleSkillTemplate, writeSecrets, listAgents, loadAgent, writeYaml, agentPaths, readMediaCache, loadPoliciesFromDir, lookupCachedUrl, newCacheEntry, writeMediaCache } from '@elisym/sdk/agent-store';
@@ -8,7 +9,7 @@ import { generateSecretKey, getPublicKey, nip19, verifyEvent } from 'nostr-tools
8
9
  import YAML from 'yaml';
9
10
  import { Command } from 'commander';
10
11
  import { createHash } from 'node:crypto';
11
- import { LlmHealthMonitor, startLlmRecovery, createFreeLlmLimiterSet, ScriptBillingExhaustedError, FREE_LLM_GLOBAL_KEY, freeLlmCustomerKey, LlmHealthError } from '@elisym/sdk/llm-health';
12
+ import { LlmHealthMonitor, startLlmRecovery, createFreeLlmLimiterSet, ScriptBillingExhaustedError, ScriptExecutionError, FREE_LLM_GLOBAL_KEY, freeLlmCustomerKey, LlmHealthError } from '@elisym/sdk/llm-health';
12
13
  import { lookup } from 'node:dns/promises';
13
14
  import { Socket } from 'node:net';
14
15
  import pino from 'pino';
@@ -25,8 +26,6 @@ var __export = (target, all) => {
25
26
  for (var name in all)
26
27
  __defProp(target, name, { get: all[name], enumerable: true });
27
28
  };
28
-
29
- // src/llm/providers/http.ts
30
29
  function resolveLlmTimeoutMs() {
31
30
  const raw = process.env.ELISYM_LLM_TIMEOUT_MS;
32
31
  if (raw === void 0) {
@@ -71,12 +70,53 @@ async function fetchWithTimeout(url, init, signal) {
71
70
  const timer = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
72
71
  const onAbort = () => controller.abort();
73
72
  signal?.addEventListener("abort", onAbort, { once: true });
74
- try {
75
- return await fetch(url, { ...init, signal: controller.signal });
76
- } finally {
73
+ let toreDown = false;
74
+ const teardown = () => {
75
+ if (toreDown) {
76
+ return;
77
+ }
78
+ toreDown = true;
77
79
  clearTimeout(timer);
78
80
  signal?.removeEventListener("abort", onAbort);
81
+ };
82
+ let response;
83
+ try {
84
+ response = await fetch(url, { ...init, signal: controller.signal });
85
+ } catch (error) {
86
+ teardown();
87
+ throw error;
79
88
  }
89
+ const body = response.body;
90
+ if (!body || typeof body.getReader !== "function") {
91
+ teardown();
92
+ return response;
93
+ }
94
+ const reader = body.getReader();
95
+ const tappedStream = new ReadableStream({
96
+ async pull(streamController) {
97
+ try {
98
+ const { done, value } = await reader.read();
99
+ if (done) {
100
+ teardown();
101
+ streamController.close();
102
+ return;
103
+ }
104
+ streamController.enqueue(value);
105
+ } catch (error) {
106
+ teardown();
107
+ streamController.error(error);
108
+ }
109
+ },
110
+ cancel(reason) {
111
+ teardown();
112
+ return reader.cancel(reason);
113
+ }
114
+ });
115
+ return new Response(tappedStream, {
116
+ status: response.status,
117
+ statusText: response.statusText,
118
+ headers: response.headers
119
+ });
80
120
  }
81
121
  async function fetchWithRetry(url, init, signal) {
82
122
  for (let attempt = 0; ; attempt++) {
@@ -1721,6 +1761,8 @@ async function fetchUsdcBalance(rpc, owner) {
1721
1761
  return 0n;
1722
1762
  }
1723
1763
  }
1764
+ var LEDGER_DIR_MODE = 448;
1765
+ var LEDGER_FILE_MODE = 384;
1724
1766
  var VALID_TRANSITIONS = {
1725
1767
  paid: ["executed", "failed"],
1726
1768
  executed: ["delivered", "failed"],
@@ -1750,7 +1792,9 @@ var JobLedger = class {
1750
1792
  if (e?.code !== "ENOENT") {
1751
1793
  console.warn(` ! Ledger load warning: ${e?.message ?? "unknown error"}`);
1752
1794
  try {
1753
- renameSync(this.path, this.path + ".corrupt." + Date.now());
1795
+ const backupPath = this.path + ".corrupt." + Date.now();
1796
+ renameSync(this.path, backupPath);
1797
+ chmodSync(backupPath, LEDGER_FILE_MODE);
1754
1798
  } catch {
1755
1799
  }
1756
1800
  }
@@ -1758,11 +1802,12 @@ var JobLedger = class {
1758
1802
  }
1759
1803
  flush() {
1760
1804
  const dir = dirname(this.path);
1761
- mkdirSync(dir, { recursive: true });
1805
+ mkdirSync(dir, { recursive: true, mode: LEDGER_DIR_MODE });
1762
1806
  const obj = Object.fromEntries(this.entries);
1763
1807
  const tmp = this.path + ".tmp";
1764
- writeFileSync(tmp, JSON.stringify(obj, null, 2));
1808
+ writeFileSync(tmp, JSON.stringify(obj, null, 2), { mode: LEDGER_FILE_MODE });
1765
1809
  renameSync(tmp, this.path);
1810
+ chmodSync(this.path, LEDGER_FILE_MODE);
1766
1811
  }
1767
1812
  recordPaid(entry) {
1768
1813
  if (this.entries.has(entry.job_id)) {
@@ -2026,6 +2071,22 @@ var ExecutionBudgetExceededError = class extends Error {
2026
2071
  this.name = "ExecutionBudgetExceededError";
2027
2072
  }
2028
2073
  };
2074
+ var CUSTOMER_SAFE_MESSAGE_PREFIXES = ["Input too long", "No skill matched", "Payment timeout"];
2075
+ function customerSafeMessage(error) {
2076
+ if (error instanceof AgentUnavailableError || error instanceof ExecutionBudgetExceededError) {
2077
+ return error.message;
2078
+ }
2079
+ if (error instanceof ScriptExecutionError) {
2080
+ return error.message;
2081
+ }
2082
+ if (error instanceof ScriptBillingExhaustedError) {
2083
+ return AGENT_UNAVAILABLE_MESSAGE;
2084
+ }
2085
+ if (error instanceof Error && CUSTOMER_SAFE_MESSAGE_PREFIXES.some((prefix) => error.message.startsWith(prefix))) {
2086
+ return error.message;
2087
+ }
2088
+ return "Internal processing error";
2089
+ }
2029
2090
  function bodyLooksLikeBilling3(body) {
2030
2091
  const lower = body.toLowerCase();
2031
2092
  return BILLING_BODY_MARKERS3.some((marker) => lower.includes(marker));
@@ -2208,7 +2269,14 @@ var AgentRuntime = class {
2208
2269
  return true;
2209
2270
  }
2210
2271
  if (skill.mode !== "llm") {
2211
- const message = err instanceof Error ? err.message : String(err);
2272
+ let message;
2273
+ if (err instanceof ScriptExecutionError) {
2274
+ message = err.detail;
2275
+ } else if (err instanceof Error) {
2276
+ message = err.message;
2277
+ } else {
2278
+ message = String(err);
2279
+ }
2212
2280
  const provider = skill.llmOverride?.provider;
2213
2281
  const model = skill.llmOverride?.model;
2214
2282
  if (!provider || !model) {
@@ -2413,15 +2481,9 @@ var AgentRuntime = class {
2413
2481
  `[${job.jobId.slice(0, 8)}] Keeping status=paid; recovery will re-execute when LLM pair recovers (24h cutoff).`
2414
2482
  );
2415
2483
  }
2416
- this.callbacks.onJobError?.(job.jobId, e.message);
2417
- let safeMessage;
2418
- if (e instanceof AgentUnavailableError) {
2419
- safeMessage = e.message;
2420
- } else if (e.message?.includes("API")) {
2421
- safeMessage = "Internal processing error";
2422
- } else {
2423
- safeMessage = e.message ?? "Unknown error";
2424
- }
2484
+ const operatorMessage = e instanceof ScriptExecutionError ? `${e.message}: ${e.detail}` : e.message ?? "Unknown error";
2485
+ this.callbacks.onJobError?.(job.jobId, operatorMessage);
2486
+ const safeMessage = customerSafeMessage(e);
2425
2487
  await this.transport.sendFeedback(job, { type: "error", message: safeMessage }).catch(() => {
2426
2488
  });
2427
2489
  } finally {
@@ -3608,7 +3670,7 @@ async function cmdStart(nameArg, options = {}) {
3608
3670
  console.log(` Network ${walletNetwork}`);
3609
3671
  console.log(` Address ${solanaAddress}`);
3610
3672
  if (process.env.SOLANA_RPC_URL) {
3611
- console.log(` RPC ${process.env.SOLANA_RPC_URL} (custom)`);
3673
+ console.log(` RPC ${stripRpcSecrets(process.env.SOLANA_RPC_URL)} (custom)`);
3612
3674
  }
3613
3675
  console.log(` SOL ${formatSol(balance)} (${balance} lamports)`);
3614
3676
  console.log(` USDC ${formatAssetAmount(USDC_SOLANA_DEVNET, usdcBalance)}`);
@@ -4094,11 +4156,19 @@ async function cmdStart(nameArg, options = {}) {
4094
4156
  console.log(" * Running. Press Ctrl+C to stop.\n");
4095
4157
  await runtime.run();
4096
4158
  }
4159
+ var PUBLIC_SOLANA_RPC_HOSTS = /* @__PURE__ */ new Set([
4160
+ "api.devnet.solana.com",
4161
+ "api.mainnet-beta.solana.com",
4162
+ "api.testnet.solana.com"
4163
+ ]);
4097
4164
  function stripRpcSecrets(raw) {
4098
4165
  try {
4099
4166
  const parsed = new URL(raw);
4100
4167
  parsed.username = "";
4101
4168
  parsed.password = "";
4169
+ if (!PUBLIC_SOLANA_RPC_HOSTS.has(parsed.hostname)) {
4170
+ return `${parsed.protocol}//${parsed.host}/***`;
4171
+ }
4102
4172
  const marker = parsed.search.length > 0 ? "?***" : "";
4103
4173
  parsed.search = "";
4104
4174
  return `${parsed.toString()}${marker}`;