@biznetgio/neo-app 0.4.35 → 0.4.36
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 +26 -2
package/package.json
CHANGED
package/src/mcp/tools.js
CHANGED
|
@@ -333,7 +333,7 @@ export const TOOLS = [
|
|
|
333
333
|
},
|
|
334
334
|
{
|
|
335
335
|
name: 'neo_scale_service',
|
|
336
|
-
description: 'Scale a service to N replicas. May return pending_approval under org policy.',
|
|
336
|
+
description: 'Scale a service to N replicas. Scaling changes cost, so without confirm:true it returns the new estimated cost and scales nothing (re-call with confirm:true). May return pending_approval under org policy.',
|
|
337
337
|
mutating: true,
|
|
338
338
|
schema: {
|
|
339
339
|
type: 'object',
|
|
@@ -341,10 +341,22 @@ export const TOOLS = [
|
|
|
341
341
|
projectId: str('Project id'),
|
|
342
342
|
service: str('Service/app name'),
|
|
343
343
|
replicas: int('Desired replica count (>=0)'),
|
|
344
|
+
confirm: { type: 'boolean', description: 'Approve the cost change: without confirm:true the tool returns the new cost and scales nothing.' },
|
|
344
345
|
},
|
|
345
346
|
required: ['projectId', 'service', 'replicas'],
|
|
346
347
|
},
|
|
347
348
|
async call(api, args) {
|
|
349
|
+
if (args.confirm !== true) {
|
|
350
|
+
let computeTier;
|
|
351
|
+
try {
|
|
352
|
+
const apps = (await api.get(`/api/v1/projects/${args.projectId}/apps`)).data;
|
|
353
|
+
const list = Array.isArray(apps) ? apps : (apps.applications || []);
|
|
354
|
+
const svc = list.find((a) => a.name === args.service || a.id === args.service);
|
|
355
|
+
computeTier = svc && (svc.cpuLimit || svc.computeTier);
|
|
356
|
+
} catch { /* best effort — gate regardless */ }
|
|
357
|
+
const gate = await confirmCost(api, args, { computeTier, replicas: args.replicas });
|
|
358
|
+
if (gate) return { ...gate, change: `scale "${args.service}" to ${args.replicas} replica(s)`, message: 'Scaling changes cost — show the user and re-call with confirm:true to apply.' };
|
|
359
|
+
}
|
|
348
360
|
const { status, data } = await api.post(
|
|
349
361
|
`/api/v1/projects/${args.projectId}/services/${args.service}/scale`,
|
|
350
362
|
{ replicas: args.replicas }
|
|
@@ -641,10 +653,16 @@ export const TOOLS = [
|
|
|
641
653
|
databaseId: str('Database id'),
|
|
642
654
|
volumeSize: str('New storage size, e.g. "20Gi" — must be >= current (grow-only)'),
|
|
643
655
|
replicas: int('Read/failover replicas (CNPG/postgres); total instances = 1 + replicas'),
|
|
656
|
+
confirm: { type: 'boolean', description: 'Approve the cost change: without confirm:true a storage/replica resize returns the new cost and applies nothing.' },
|
|
644
657
|
},
|
|
645
658
|
required: ['databaseId'],
|
|
646
659
|
},
|
|
647
660
|
async call(api, args) {
|
|
661
|
+
const costChanging = args.volumeSize || args.replicas !== undefined;
|
|
662
|
+
if (costChanging && args.confirm !== true) {
|
|
663
|
+
const gate = await confirmCost(api, args, { storageGB: gbFromSize(args.volumeSize), replicas: args.replicas });
|
|
664
|
+
if (gate) return { ...gate, change: 'resize database (storage/replicas)', message: 'Resizing changes cost — re-call with confirm:true to apply.' };
|
|
665
|
+
}
|
|
648
666
|
const body = {};
|
|
649
667
|
if (args.volumeSize) body.volumeSize = args.volumeSize;
|
|
650
668
|
if (args.replicas !== undefined) body.replicas = args.replicas;
|
|
@@ -807,11 +825,17 @@ export const TOOLS = [
|
|
|
807
825
|
dlqEnabled: { type: 'boolean', description: 'Enable/disable the dead-letter queue' },
|
|
808
826
|
volumeSize: str('New storage size, e.g. "20Gi" — must be >= current (grow-only)'),
|
|
809
827
|
retention: { type: 'object', description: 'Retention policy {maxAgeSeconds, maxBytes, maxMsgs}' },
|
|
828
|
+
confirm: { type: 'boolean', description: 'Approve the cost change: without confirm:true a storage/plan change returns the new cost and applies nothing. DLQ/retention-only edits apply directly.' },
|
|
810
829
|
},
|
|
811
830
|
required: ['queueId'],
|
|
812
831
|
},
|
|
813
832
|
async call(api, args) {
|
|
814
|
-
const
|
|
833
|
+
const costChanging = args.volumeSize || args.planClass;
|
|
834
|
+
if (costChanging && args.confirm !== true) {
|
|
835
|
+
const gate = await confirmCost(api, args, { storageGB: gbFromSize(args.volumeSize) });
|
|
836
|
+
if (gate) return { ...gate, change: 'update queue (storage/plan)', message: 'This changes cost — re-call with confirm:true to apply.' };
|
|
837
|
+
}
|
|
838
|
+
const { queueId, confirm, orgId, ...body } = args;
|
|
815
839
|
const { status, data } = await api.put(`/api/v1/queues/${queueId}`, body);
|
|
816
840
|
return redactSecrets(result(status, data));
|
|
817
841
|
},
|