@biznetgio/neo-app 0.4.40 → 0.4.41
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/package.json +1 -1
- package/src/api.js +23 -5
- package/src/mcp/tools.js +7 -0
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// All routes are under /api/v1. A 202 + {status:"pending_approval"} is a normal
|
|
4
4
|
// result (org approval policy), NOT an error, so only >=400 throws.
|
|
5
5
|
|
|
6
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7
|
+
|
|
6
8
|
export class ApiError extends Error {
|
|
7
9
|
constructor(status, message, body) {
|
|
8
10
|
super(message);
|
|
@@ -28,12 +30,28 @@ export class NeoApi {
|
|
|
28
30
|
const headers = { Accept: accept || 'application/json' };
|
|
29
31
|
if (this.token) headers.Authorization = `Bearer ${this.token}`;
|
|
30
32
|
if (body !== undefined) headers['Content-Type'] = 'application/json';
|
|
33
|
+
const init = { method, headers, body: body !== undefined ? JSON.stringify(body) : undefined };
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Transient-failure retry: a long stack apply polls for minutes, and a single
|
|
36
|
+
// dropped connection ("fetch failed") or a brief 502/503/504 shouldn't fail the
|
|
37
|
+
// whole operation. Only retry IDEMPOTENT methods automatically — a POST/PUT that
|
|
38
|
+
// may have been processed must not be silently re-sent.
|
|
39
|
+
const idempotent = method === 'GET' || method === 'DELETE';
|
|
40
|
+
const maxTries = idempotent ? 4 : 1;
|
|
41
|
+
let res, lastErr;
|
|
42
|
+
for (let attempt = 1; attempt <= maxTries; attempt++) {
|
|
43
|
+
try {
|
|
44
|
+
res = await fetch(url, init);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
lastErr = e; // connection never completed — transient
|
|
47
|
+
if (idempotent && attempt < maxTries) { await sleep(250 * 2 ** (attempt - 1)); continue; }
|
|
48
|
+
throw new ApiError(0, `network error contacting ${this.baseUrl}: ${e?.message || e}`, null);
|
|
49
|
+
}
|
|
50
|
+
if (idempotent && [502, 503, 504].includes(res.status) && attempt < maxTries) {
|
|
51
|
+
await sleep(250 * 2 ** (attempt - 1)); continue;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
37
55
|
|
|
38
56
|
const ctype = res.headers.get('content-type') || '';
|
|
39
57
|
const isJson = ctype.includes('application/json');
|
package/src/mcp/tools.js
CHANGED
|
@@ -1242,6 +1242,13 @@ export const TOOLS = [
|
|
|
1242
1242
|
targetService: str('cron http_endpoint: name of a web app resource IN THIS STACK to hit — its deployed URL is resolved at apply (use instead of a literal url for a same-stack service)'),
|
|
1243
1243
|
targetPath: str('cron http_endpoint: path appended to the targetService URL (e.g. "/cron")'),
|
|
1244
1244
|
method: str('cron http_endpoint: HTTP method'),
|
|
1245
|
+
headers: { type: 'object', description: 'cron http_endpoint: request headers (secret values redacted on read-back)' },
|
|
1246
|
+
body: str('cron http_endpoint: request body'),
|
|
1247
|
+
contentType: str('cron http_endpoint: Content-Type for the body (merged into headers)'),
|
|
1248
|
+
expectedStatus: int('cron http_endpoint: exact HTTP status required for a run to succeed (default any 2xx)'),
|
|
1249
|
+
timeoutSeconds: int('cron: per-run timeout (seconds)'),
|
|
1250
|
+
retryCount: int('cron: retries on failure'),
|
|
1251
|
+
concurrencyPolicy: { type: 'string', enum: ['Allow', 'Forbid', 'Replace'], description: 'cron: overlap policy (default Allow)' },
|
|
1245
1252
|
},
|
|
1246
1253
|
required: ['kind', 'name'],
|
|
1247
1254
|
},
|