@biznetgio/neo-app 0.4.27 → 0.4.28

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/package.json +1 -1
  2. package/src/mcp/tools.js +45 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biznetgio/neo-app",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "NEO App CLI — one-command agent onboarding, MCP server, and project ops for coding agents (Codex, Claude Code).",
5
5
  "type": "module",
6
6
  "bin": {
package/src/mcp/tools.js CHANGED
@@ -94,7 +94,7 @@ export const TOOLS = [
94
94
  { type: 'template', create: true, deploy: true, scale: false, logs: true, variables: true, delete: true },
95
95
  { type: 'static_site', create: 'via_folder', deploy: true, scale: false, logs: true, variables: true, delete: true, nativeStatic: true, note: 'neo_deploy_folder serviceType=static_site with buildCommand/publishDirectory → native nginx static hosting (0.4.27); auto-detected for static-only folders' },
96
96
  { type: 'worker', create: 'via_folder', deploy: true, scale: true, logs: true, variables: true, delete: true, note: 'neo_deploy_folder serviceType=worker — no public ingress; repo auto-detection still TODO (handoff 6)' },
97
- { type: 'database', create: true, get: true, list: true, update: true, backup: true, delete: true, note: 'neo_create/list/get/update(resize)/delete_database + backup (list/create/run); restore still TODO (handoff 4.4)' },
97
+ { type: 'database', create: true, get: true, list: true, update: true, backup: true, verify: true, delete: true, note: 'neo_create/list/get/update(resize)/delete_database + backup + verify (SQL round-trip probe); restore still TODO (handoff 4.4)' },
98
98
  { type: 'queue', create: true, get: true, list: true, update: true, delete: true, note: 'neo_create/list/get/update/delete_queue (0.4.15-0.4.25) (handoff 4.5)' },
99
99
  { type: 'cron', create: true, get: true, list: true, run: true, runs: true, delete: true, note: 'neo_create/list/get/run/list_runs/delete_cron (0.4.15-0.4.21) (handoff 4.6)' },
100
100
  { type: 'object_storage', create: true, get: true, list: true, objects: true, upload: true, delete: true, note: 'neo_create/list/get/delete_bucket + list/upload/presign(get)/delete objects (0.4.15-0.4.25) (handoff 4.7)' },
@@ -105,7 +105,7 @@ export const TOOLS = [
105
105
  controlPlane,
106
106
  resourceTypes,
107
107
  // Verbs this MCP build exposes today (no create-DB/queue/cron/bucket, no costed plan/apply yet).
108
- lifecycleVerbs: ['plan', 'apply', 'get_operation', 'wait_operation', 'delete_stack', 'reconcile_usage', 'create', 'list', 'get', 'update', 'deploy', 'scale', 'logs', 'get_variables', 'set_variable', 'estimate', 'scan_repo', 'bind', 'unbind', 'run_cron', 'presign_object', 'backup', 'delete', 'list_approvals', 'get_approval', 'get_credit', 'list_templates'],
108
+ lifecycleVerbs: ['plan', 'apply', 'get_operation', 'wait_operation', 'delete_stack', 'reconcile_usage', 'service_usage', 'create', 'list', 'get', 'update', 'verify', 'deploy', 'scale', 'logs', 'get_variables', 'set_variable', 'estimate', 'scan_repo', 'bind', 'unbind', 'run_cron', 'presign_object', 'backup', 'delete', 'list_approvals', 'get_approval', 'get_credit', 'list_templates'],
109
109
  bindings: { supported: true, targets: ['neo_db', 'neo_queue'], writeOnly: true, note: 'neo_bind_resource injects DATABASE_URL/NATS_URL server-side at deploy; the secret is never returned (handoff 5)' },
110
110
  sourceTypes: ['git', 'image', 'dockerfile', 'local', 'template'],
111
111
  environments: ['staging', 'production'],
@@ -293,6 +293,25 @@ export const TOOLS = [
293
293
  return (await api.get(`/api/v1/orgs/${orgId}/credit`)).data;
294
294
  },
295
295
  },
296
+ {
297
+ name: 'neo_service_usage',
298
+ description: 'Get the metered usage + cost attributed to ONE service/resource in a project — per-service usage attribution (handoff §4). Filters the project usage breakdown to this resourceId.',
299
+ mutating: false,
300
+ schema: { type: 'object', properties: { projectId: str('Project id'), resourceId: str('Service/database/queue/bucket id (from neo_list_services/databases/…)') }, required: ['projectId', 'resourceId'] },
301
+ async call(api, args) {
302
+ const u = (await api.get(`/api/v1/projects/${args.projectId}/usage`)).data || {};
303
+ const lines = (u.lines || []).filter((l) => l.resourceId === args.resourceId);
304
+ const totalCost = Math.round(lines.reduce((sum, l) => sum + (l.cost || 0), 0));
305
+ return {
306
+ resourceId: args.resourceId,
307
+ period: u.period,
308
+ currency: u.currency,
309
+ lines,
310
+ totalCost,
311
+ note: lines.length ? undefined : 'no metered usage attributed to this resource yet (usage accrues hourly)',
312
+ };
313
+ },
314
+ },
296
315
  {
297
316
  name: 'neo_estimate',
298
317
  description:
@@ -526,6 +545,16 @@ export const TOOLS = [
526
545
  return redactSecrets(result(status, data));
527
546
  },
528
547
  },
548
+ {
549
+ name: 'neo_verify_database',
550
+ description: 'Functionally verify a managed database (handoff §8.1): runs an in-cluster probe that opens the DB and does a SQL round-trip (SELECT 1), returning {verified, log} — WITHOUT handing you any credentials. Postgres only for now. Synchronous (waits up to ~60s for the probe).',
551
+ mutating: true,
552
+ schema: { type: 'object', properties: { databaseId: str('Database id (postgres)') }, required: ['databaseId'] },
553
+ async call(api, args) {
554
+ const { status, data } = await api.post(`/api/v1/databases/${args.databaseId}/verify`);
555
+ return result(status, data);
556
+ },
557
+ },
529
558
  {
530
559
  name: 'neo_list_backups',
531
560
  description: 'List configured database backups in a project (handoff 4.4): schedule, destination, last run status.',
@@ -974,6 +1003,16 @@ export const TOOLS = [
974
1003
  const plan = (await api.get(`/api/v1/plans/${args.planId}`)).data;
975
1004
  let usage = null;
976
1005
  try { usage = (await api.get(`/api/v1/projects/${plan.projectId}/usage`)).data; } catch { /* usage may be unavailable */ }
1006
+ // Per-resource actuals: attribute the project usage lines to this plan's resources.
1007
+ const planIds = new Set(Object.values(plan.result || {}).map((v) => v && v.id).filter(Boolean));
1008
+ const perResource = {};
1009
+ for (const l of (usage?.lines || [])) {
1010
+ if (!planIds.has(l.resourceId)) continue;
1011
+ const r = perResource[l.resourceId] || { resourceName: l.resourceName, resourceType: l.resourceType, cost: 0 };
1012
+ r.cost += l.cost || 0;
1013
+ perResource[l.resourceId] = r;
1014
+ }
1015
+ const actualForPlan = Math.round(Object.values(perResource).reduce((s, r) => s + r.cost, 0));
977
1016
  return {
978
1017
  planId: args.planId,
979
1018
  projectId: plan.projectId,
@@ -981,8 +1020,10 @@ export const TOOLS = [
981
1020
  currency: plan.cost?.currency,
982
1021
  priceBook: plan.cost?.priceBook,
983
1022
  variableSkus: plan.cost?.variable || [],
984
- actualUsage: redactSecrets(usage),
985
- note: 'estimatedMonthly is the FIXED compute/storage estimate; actualUsage is metered (incl. variable usage). Compare over a full billing period — a just-applied stack has not accrued usage yet.',
1023
+ actualForPlanResources: actualForPlan,
1024
+ perResource,
1025
+ projectUsageTotal: usage?.totalCost,
1026
+ note: 'estimatedMonthly is the FIXED monthly compute/storage estimate; actualForPlanResources sums the metered cost of THIS plan\'s resources so far (accrues hourly — ~0 just after apply). perResource breaks it down.',
986
1027
  };
987
1028
  },
988
1029
  },