@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 {
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
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}`;
|