@marshell/cli 0.7.7 → 0.7.9

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.
Files changed (2) hide show
  1. package/dist/network.js +96 -29
  2. package/package.json +1 -1
package/dist/network.js CHANGED
@@ -15,7 +15,7 @@ exports.fetchHistory = fetchHistory;
15
15
  exports.askAgent = askAgent;
16
16
  exports.toWsUrl = toWsUrl;
17
17
  const config_1 = require("./config");
18
- exports.MARSHELL_CLI_VERSION = "0.7.7";
18
+ exports.MARSHELL_CLI_VERSION = "0.7.9";
19
19
  function formatWalletLine(wallet) {
20
20
  const parts = [];
21
21
  if (wallet.free_remaining > 0) {
@@ -94,26 +94,80 @@ async function pingNetwork(baseUrl) {
94
94
  message: "Health endpoint not found.",
95
95
  };
96
96
  }
97
- async function postJson(url, body, headers) {
98
- const response = await fetch(url, {
99
- method: "POST",
100
- headers,
101
- body: JSON.stringify(body),
102
- });
103
- const text = await response.text();
104
- let data = {};
105
- if (text.trim().length > 0) {
97
+ function sleep(ms) {
98
+ return new Promise((resolve) => setTimeout(resolve, ms));
99
+ }
100
+ function isRetryableStatus(status) {
101
+ return status === 429 || status === 502 || status === 503 || status === 504;
102
+ }
103
+ async function withRetry(fn, opts) {
104
+ const attempts = opts?.attempts ?? 3;
105
+ let lastError;
106
+ for (let i = 0; i < attempts; i++) {
106
107
  try {
107
- data = JSON.parse(text);
108
+ const result = await fn();
109
+ if (!opts?.shouldRetry?.(result) || i === attempts - 1) {
110
+ return result;
111
+ }
108
112
  }
109
- catch {
110
- data = { raw: text };
113
+ catch (error) {
114
+ lastError = error;
115
+ if (i === attempts - 1) {
116
+ throw error;
117
+ }
111
118
  }
119
+ await sleep(250 * (i + 1));
112
120
  }
113
- return {
114
- status: response.status,
115
- data: data,
116
- };
121
+ throw lastError instanceof Error ? lastError : new Error("request failed");
122
+ }
123
+ async function postJson(url, body, headers) {
124
+ return withRetry(async () => {
125
+ const response = await fetch(url, {
126
+ method: "POST",
127
+ headers,
128
+ body: JSON.stringify(body),
129
+ });
130
+ const text = await response.text();
131
+ let data = {};
132
+ if (text.trim().length > 0) {
133
+ try {
134
+ data = JSON.parse(text);
135
+ }
136
+ catch {
137
+ data = { raw: text };
138
+ }
139
+ }
140
+ return {
141
+ status: response.status,
142
+ data: data,
143
+ };
144
+ }, {
145
+ shouldRetry: (result) => isRetryableStatus(result.status),
146
+ });
147
+ }
148
+ async function getJson(url, headers) {
149
+ return withRetry(async () => {
150
+ const response = await fetch(url, {
151
+ method: "GET",
152
+ headers,
153
+ });
154
+ const text = await response.text();
155
+ let data = {};
156
+ if (text.trim().length > 0) {
157
+ try {
158
+ data = JSON.parse(text);
159
+ }
160
+ catch {
161
+ data = { raw: text };
162
+ }
163
+ }
164
+ return {
165
+ status: response.status,
166
+ data: data,
167
+ };
168
+ }, {
169
+ shouldRetry: (result) => isRetryableStatus(result.status),
170
+ });
117
171
  }
118
172
  async function joinAgent(baseUrl, name, options = {}) {
119
173
  const config = await (0, config_1.readConfig)();
@@ -216,7 +270,23 @@ async function fetchWallet(baseUrl) {
216
270
  async function sendMessage(baseUrl, to, text) {
217
271
  try {
218
272
  const headers = await authHeaders("agent");
219
- const result = await postJson(withPath(baseUrl, "/v1/messages/send"), { to, text }, headers);
273
+ // Sends are not idempotent do not retry POST on 502/503/504.
274
+ const response = await fetch(withPath(baseUrl, "/v1/messages/send"), {
275
+ method: "POST",
276
+ headers,
277
+ body: JSON.stringify({ to, text }),
278
+ });
279
+ const raw = await response.text();
280
+ let data = {};
281
+ if (raw.trim().length > 0) {
282
+ try {
283
+ data = JSON.parse(raw);
284
+ }
285
+ catch {
286
+ data = { error: raw };
287
+ }
288
+ }
289
+ const result = { status: response.status, data };
220
290
  const wallet = parseWallet(result.data.wallet);
221
291
  if (result.status === 402) {
222
292
  return {
@@ -259,32 +329,29 @@ async function sendMessage(baseUrl, to, text) {
259
329
  }
260
330
  }
261
331
  async function fetchInbox(baseUrl, options) {
332
+ const peek = options?.peek !== false;
262
333
  try {
263
334
  const headers = await authHeaders("agent");
264
335
  const params = new URLSearchParams();
265
- if (options?.peek) {
336
+ if (peek) {
266
337
  params.set("peek", "1");
267
338
  }
268
339
  if (options?.waitSeconds && options.waitSeconds > 0) {
269
340
  params.set("wait", String(Math.min(120, options.waitSeconds)));
270
341
  }
271
342
  const qs = params.size > 0 ? `?${params.toString()}` : "";
272
- const response = await fetch(withPath(baseUrl, `/v1/messages/inbox${qs}`), {
273
- method: "GET",
274
- headers,
275
- });
276
- const data = (await response.json());
277
- if (!response.ok) {
343
+ const result = await getJson(withPath(baseUrl, `/v1/messages/inbox${qs}`), headers);
344
+ if (result.status < 200 || result.status >= 300) {
278
345
  return {
279
346
  kind: "error",
280
- status: response.status,
281
- message: data.error ?? `Inbox failed with HTTP ${response.status}.`,
347
+ status: result.status,
348
+ message: result.data.error ?? `Inbox failed with HTTP ${result.status}.`,
282
349
  };
283
350
  }
284
351
  return {
285
352
  kind: "ok",
286
- messages: data.messages ?? [],
287
- agent: data.agent ?? "",
353
+ messages: result.data.messages ?? [],
354
+ agent: result.data.agent ?? "",
288
355
  };
289
356
  }
290
357
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marshell/cli",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
4
4
  "description": "Marshell CLI — join a subnet, discover agents, send messages",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",