@biznetgio/neo-app 0.4.34 → 0.4.35
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/mcp/tools.js +46 -12
package/package.json
CHANGED
package/src/mcp/tools.js
CHANGED
|
@@ -40,6 +40,26 @@ function requireEnv(env) {
|
|
|
40
40
|
return e;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// deleteAndConverge deletes a resource, then polls its endpoint until the inventory
|
|
44
|
+
// actually converges (GET → 404), so a delete isn't reported done while the record
|
|
45
|
+
// still exists. Returns a durable operation reference (`del-<id>`) + convergence
|
|
46
|
+
// state (handoff 3.6; 0.4.34 retest §9 "every delete returns a durable operation ID").
|
|
47
|
+
async function deleteAndConverge(api, path, resourceId, kind) {
|
|
48
|
+
const { status, data } = await api.del(path);
|
|
49
|
+
let converged = false, checks = 0;
|
|
50
|
+
for (let i = 0; i < 12; i++) {
|
|
51
|
+
checks++;
|
|
52
|
+
try {
|
|
53
|
+
await api.get(path);
|
|
54
|
+
await new Promise((r) => setTimeout(r, 500)); // still present — keep polling
|
|
55
|
+
} catch (e) {
|
|
56
|
+
if (e && e.status === 404) { converged = true; break; }
|
|
57
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { operationId: 'del-' + resourceId, kind, status: (data && data.status) || 'deleted', converged, convergenceChecks: checks };
|
|
61
|
+
}
|
|
62
|
+
|
|
43
63
|
// gbFromSize parses "10Gi"/"10G"/"10" → 10 (integer GB), else undefined.
|
|
44
64
|
function gbFromSize(v) {
|
|
45
65
|
if (v == null) return undefined;
|
|
@@ -820,8 +840,16 @@ export const TOOLS = [
|
|
|
820
840
|
],
|
|
821
841
|
branches: {
|
|
822
842
|
app_command: 'Provide serviceId (an existing service) OR image, plus command (argv array).',
|
|
823
|
-
http_endpoint: 'Provide url and method (GET/POST/…).',
|
|
843
|
+
http_endpoint: 'Provide url and method (GET/POST/…). Optional: headers (object), body + contentType, expectedStatus (exact code required for success), timeoutSeconds, retryCount, concurrencyPolicy (Allow|Forbid|Replace).',
|
|
824
844
|
},
|
|
845
|
+
httpOptions: [
|
|
846
|
+
{ field: 'headers', ask: 'Any request headers? (e.g. Authorization) — object', optional: true },
|
|
847
|
+
{ field: 'body', ask: 'A request body? (+ contentType, e.g. application/json)', optional: true },
|
|
848
|
+
{ field: 'expectedStatus', ask: 'A specific expected HTTP status for success? (default: any 2xx)', optional: true },
|
|
849
|
+
{ field: 'timeoutSeconds', ask: 'A per-run timeout (seconds)?', optional: true },
|
|
850
|
+
{ field: 'retryCount', ask: 'How many retries on failure?', optional: true },
|
|
851
|
+
{ field: 'concurrencyPolicy', ask: 'If a run overruns the next schedule: Allow | Forbid | Replace?', optional: true },
|
|
852
|
+
],
|
|
825
853
|
examples: [
|
|
826
854
|
{ intent: 'Run `node cleanup.js` in my web service every night at 2am Jakarta', args: { name: 'nightly-cleanup', mode: 'app_command', scheduleType: 'cron', schedule: '0 2 * * *', timezone: 'Asia/Jakarta', serviceId: '<web service id>', command: ['node', 'cleanup.js'], environment: 'staging' } },
|
|
827
855
|
{ intent: 'Ping a health URL every 15 minutes', args: { name: 'healthcheck', mode: 'http_endpoint', scheduleType: 'interval', intervalMinutes: 15, url: 'https://example.app/health', method: 'GET', environment: 'production' } },
|
|
@@ -849,6 +877,13 @@ export const TOOLS = [
|
|
|
849
877
|
command: { type: 'array', items: { type: 'string' }, description: 'Command argv (mode=app_command)' },
|
|
850
878
|
url: str('Target URL (mode=http_endpoint)'),
|
|
851
879
|
method: str('HTTP method (mode=http_endpoint), e.g. GET/POST'),
|
|
880
|
+
headers: { type: 'object', description: 'HTTP request headers (mode=http_endpoint), e.g. {"Authorization":"Bearer …"}. Secret header values are redacted when read back.' },
|
|
881
|
+
body: str('HTTP request body (mode=http_endpoint)'),
|
|
882
|
+
contentType: str('Content-Type for the body (mode=http_endpoint), e.g. "application/json" — merged into headers'),
|
|
883
|
+
expectedStatus: int('Exact HTTP status a run must receive to count as success (mode=http_endpoint; default: any 2xx)'),
|
|
884
|
+
timeoutSeconds: int('Per-run timeout in seconds (optional) → activeDeadlineSeconds'),
|
|
885
|
+
retryCount: int('Retries on failure (optional) → backoffLimit'),
|
|
886
|
+
concurrencyPolicy: { type: 'string', enum: ['Allow', 'Forbid', 'Replace'], description: 'Overlap policy if a run is still going when the next is due (optional, default Allow)' },
|
|
852
887
|
environment: { type: 'string', enum: ['staging', 'production'], description: 'REQUIRED — staging or production (never defaulted, handoff §2)' },
|
|
853
888
|
confirm: { type: 'boolean', description: 'Approve & Create: without confirm:true the tool returns the cost (crons are billed per run — variable) and creates nothing. Re-call with confirm:true to create.' },
|
|
854
889
|
},
|
|
@@ -858,7 +893,11 @@ export const TOOLS = [
|
|
|
858
893
|
requireEnv(args.environment);
|
|
859
894
|
const gate = await confirmCost(api, args, {}); // cron cost is variable (per-run) — gate surfaces the rate
|
|
860
895
|
if (gate) return gate;
|
|
861
|
-
|
|
896
|
+
// Merge contentType into headers for convenience (unless already set).
|
|
897
|
+
if (args.contentType) {
|
|
898
|
+
args = { ...args, headers: { 'Content-Type': args.contentType, ...(args.headers || {}) } };
|
|
899
|
+
}
|
|
900
|
+
const { projectId, confirm, orgId, contentType, ...body } = args;
|
|
862
901
|
const { status, data } = await api.post(`/api/v1/projects/${projectId}/crons`, body);
|
|
863
902
|
return redactSecrets(result(status, data));
|
|
864
903
|
},
|
|
@@ -1228,8 +1267,7 @@ export const TOOLS = [
|
|
|
1228
1267
|
destructive: true,
|
|
1229
1268
|
schema: { type: 'object', properties: { appId: str('Service/app id (from neo_list_services)') }, required: ['appId'] },
|
|
1230
1269
|
async call(api, args) {
|
|
1231
|
-
|
|
1232
|
-
return result(status, data);
|
|
1270
|
+
return deleteAndConverge(api, `/api/v1/apps/${args.appId}`, args.appId, 'service');
|
|
1233
1271
|
},
|
|
1234
1272
|
},
|
|
1235
1273
|
{
|
|
@@ -1239,8 +1277,7 @@ export const TOOLS = [
|
|
|
1239
1277
|
destructive: true,
|
|
1240
1278
|
schema: { type: 'object', properties: { databaseId: str('Database id') }, required: ['databaseId'] },
|
|
1241
1279
|
async call(api, args) {
|
|
1242
|
-
|
|
1243
|
-
return result(status, data);
|
|
1280
|
+
return deleteAndConverge(api, `/api/v1/databases/${args.databaseId}`, args.databaseId, 'database');
|
|
1244
1281
|
},
|
|
1245
1282
|
},
|
|
1246
1283
|
{
|
|
@@ -1250,8 +1287,7 @@ export const TOOLS = [
|
|
|
1250
1287
|
destructive: true,
|
|
1251
1288
|
schema: { type: 'object', properties: { queueId: str('Queue id') }, required: ['queueId'] },
|
|
1252
1289
|
async call(api, args) {
|
|
1253
|
-
|
|
1254
|
-
return result(status, data);
|
|
1290
|
+
return deleteAndConverge(api, `/api/v1/queues/${args.queueId}`, args.queueId, 'queue');
|
|
1255
1291
|
},
|
|
1256
1292
|
},
|
|
1257
1293
|
{
|
|
@@ -1261,8 +1297,7 @@ export const TOOLS = [
|
|
|
1261
1297
|
destructive: true,
|
|
1262
1298
|
schema: { type: 'object', properties: { cronId: str('Cron/scheduled-job id') }, required: ['cronId'] },
|
|
1263
1299
|
async call(api, args) {
|
|
1264
|
-
|
|
1265
|
-
return result(status, data);
|
|
1300
|
+
return deleteAndConverge(api, `/api/v1/crons/${args.cronId}`, args.cronId, 'cron');
|
|
1266
1301
|
},
|
|
1267
1302
|
},
|
|
1268
1303
|
{
|
|
@@ -1272,8 +1307,7 @@ export const TOOLS = [
|
|
|
1272
1307
|
destructive: true,
|
|
1273
1308
|
schema: { type: 'object', properties: { bucketId: str('Bucket id') }, required: ['bucketId'] },
|
|
1274
1309
|
async call(api, args) {
|
|
1275
|
-
|
|
1276
|
-
return result(status, data);
|
|
1310
|
+
return deleteAndConverge(api, `/api/v1/buckets/${args.bucketId}`, args.bucketId, 'bucket');
|
|
1277
1311
|
},
|
|
1278
1312
|
},
|
|
1279
1313
|
{
|