manus-api 0.1.0-beta.4 → 0.1.0-beta.5

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.cjs CHANGED
@@ -2,6 +2,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
3
  var rl = require('@henrygd/queue/rl');
4
4
  var extractErrorMessage = require('foxts/extract-error-message');
5
+ var isNetworkError = require('foxts/is-network-error');
5
6
  var wait = require('foxts/wait');
6
7
 
7
8
  const AGENT_PROFILES = [
@@ -73,6 +74,11 @@ const MANUS_API_BASE_URL = 'https://api.manus.ai';
73
74
  return rl.newQueue(concurrency, ratePerMinute, 60000);
74
75
  }
75
76
  const MAX_RATE_LIMIT_RETRIES = 5;
77
+ /**
78
+ * Network failures and 5xx are retried for GET only: a POST that failed mid-flight may already have
79
+ * created the task or sent the message. A request timeout is not retried either: the caller has
80
+ * already waited `timeoutMs`, and three more hangs would cost minutes.
81
+ */ const MAX_TRANSIENT_RETRIES = 3;
76
82
  const MAX_BACKOFF_MS = 60000;
77
83
  const TRAILING_SLASHES = /\/+$/;
78
84
  function backoffMs(attempt, retryAfterHeader) {
@@ -222,6 +228,7 @@ class ManusClient {
222
228
  }
223
229
  let retriedAuth = false;
224
230
  let rateLimitRetries = 0;
231
+ let transientRetries = 0;
225
232
  for(;;){
226
233
  // eslint-disable-next-line no-await-in-loop -- sequential retry loop
227
234
  const apiKey = await this.options.getApiKey();
@@ -248,6 +255,12 @@ class ManusClient {
248
255
  // eslint-disable-next-line no-await-in-loop -- sequential retry loop
249
256
  ({ res, text } = await (init.poll && this.options.pollQueue ? this.options.pollQueue.add(send) : send()));
250
257
  } catch (error) {
258
+ if (method === 'GET' && transientRetries < MAX_TRANSIENT_RETRIES && isNetworkError.isNetworkError(error)) {
259
+ transientRetries++;
260
+ // eslint-disable-next-line no-await-in-loop -- sequential retry loop
261
+ await this.sleep(backoffMs(transientRetries, null));
262
+ continue;
263
+ }
251
264
  throw new ManusApiError(extractErrorMessage.extractErrorMessage(error) ?? 'request failed', {
252
265
  status: 0,
253
266
  code: 'network',
@@ -280,6 +293,12 @@ class ManusClient {
280
293
  await this.sleep(backoffMs(rateLimitRetries, res.headers.get('retry-after')));
281
294
  continue;
282
295
  }
296
+ if (method === 'GET' && transientRetries < MAX_TRANSIENT_RETRIES && res.status >= 500) {
297
+ transientRetries++;
298
+ // eslint-disable-next-line no-await-in-loop -- sequential retry loop
299
+ await this.sleep(backoffMs(transientRetries, res.headers.get('retry-after')));
300
+ continue;
301
+ }
283
302
  throw new ManusApiError(json?.error?.message ?? `Manus API ${method} ${endpoint} failed with HTTP ${res.status}`, {
284
303
  status: res.status,
285
304
  code: json?.error?.code ?? (res.status === 429 ? 'rate_limited' : `http_${res.status}`),
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { newQueue } from '@henrygd/queue/rl';
2
2
  import { extractErrorMessage } from 'foxts/extract-error-message';
3
+ import { isNetworkError } from 'foxts/is-network-error';
3
4
  import { wait } from 'foxts/wait';
4
5
 
5
6
  const AGENT_PROFILES = [
@@ -71,6 +72,11 @@ const MANUS_API_BASE_URL = 'https://api.manus.ai';
71
72
  return newQueue(concurrency, ratePerMinute, 60000);
72
73
  }
73
74
  const MAX_RATE_LIMIT_RETRIES = 5;
75
+ /**
76
+ * Network failures and 5xx are retried for GET only: a POST that failed mid-flight may already have
77
+ * created the task or sent the message. A request timeout is not retried either: the caller has
78
+ * already waited `timeoutMs`, and three more hangs would cost minutes.
79
+ */ const MAX_TRANSIENT_RETRIES = 3;
74
80
  const MAX_BACKOFF_MS = 60000;
75
81
  const TRAILING_SLASHES = /\/+$/;
76
82
  function backoffMs(attempt, retryAfterHeader) {
@@ -220,6 +226,7 @@ class ManusClient {
220
226
  }
221
227
  let retriedAuth = false;
222
228
  let rateLimitRetries = 0;
229
+ let transientRetries = 0;
223
230
  for(;;){
224
231
  // eslint-disable-next-line no-await-in-loop -- sequential retry loop
225
232
  const apiKey = await this.options.getApiKey();
@@ -246,6 +253,12 @@ class ManusClient {
246
253
  // eslint-disable-next-line no-await-in-loop -- sequential retry loop
247
254
  ({ res, text } = await (init.poll && this.options.pollQueue ? this.options.pollQueue.add(send) : send()));
248
255
  } catch (error) {
256
+ if (method === 'GET' && transientRetries < MAX_TRANSIENT_RETRIES && isNetworkError(error)) {
257
+ transientRetries++;
258
+ // eslint-disable-next-line no-await-in-loop -- sequential retry loop
259
+ await this.sleep(backoffMs(transientRetries, null));
260
+ continue;
261
+ }
249
262
  throw new ManusApiError(extractErrorMessage(error) ?? 'request failed', {
250
263
  status: 0,
251
264
  code: 'network',
@@ -278,6 +291,12 @@ class ManusClient {
278
291
  await this.sleep(backoffMs(rateLimitRetries, res.headers.get('retry-after')));
279
292
  continue;
280
293
  }
294
+ if (method === 'GET' && transientRetries < MAX_TRANSIENT_RETRIES && res.status >= 500) {
295
+ transientRetries++;
296
+ // eslint-disable-next-line no-await-in-loop -- sequential retry loop
297
+ await this.sleep(backoffMs(transientRetries, res.headers.get('retry-after')));
298
+ continue;
299
+ }
281
300
  throw new ManusApiError(json?.error?.message ?? `Manus API ${method} ${endpoint} failed with HTTP ${res.status}`, {
282
301
  status: res.status,
283
302
  code: json?.error?.code ?? (res.status === 429 ? 'rate_limited' : `http_${res.status}`),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manus-api",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "Typed client for the Manus API v2",
5
5
  "repository": {
6
6
  "type": "git",