@biznetgio/neo-app 0.4.19 → 0.4.21

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 +91 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biznetgio/neo-app",
3
- "version": "0.4.19",
3
+ "version": "0.4.21",
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,10 +94,10 @@ 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, note: 'neo_deploy_folder serviceType=static_site (auto-detected for static-only folders); auto-detection from a repo/monorepo is still TODO (handoff 6)' },
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, delete: true, note: 'neo_create/list/get/delete_database (0.4.15); update/plan still TODO (handoff 4.4)' },
97
+ { type: 'database', create: true, get: true, list: true, update: true, delete: true, note: 'neo_create/list/get/update(resize)/delete_database (0.4.15-0.4.21); backup/restore still TODO (handoff 4.4)' },
98
98
  { type: 'queue', create: true, get: true, list: true, delete: true, note: 'neo_create/list/get/delete_queue (0.4.15); update/plan still TODO (handoff 4.5)' },
99
- { type: 'cron', create: true, get: true, list: true, run: true, delete: true, note: 'neo_create/list/get/run/delete_cron (0.4.15-0.4.17); run-history still TODO (handoff 4.6)' },
100
- { type: 'object_storage', create: true, get: true, list: true, delete: true, note: 'neo_create/list/get/delete_bucket (0.4.15); object put/get still TODO (handoff 4.7)' },
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
+ { type: 'object_storage', create: true, get: true, list: true, objects: true, delete: true, note: 'neo_create/list/get/delete_bucket + list/delete objects (0.4.15-0.4.21); object upload still TODO (handoff 4.7)' },
101
101
  ];
102
102
 
103
103
  return {
@@ -304,6 +304,44 @@ export const TOOLS = [
304
304
  return { ...est, priceBook: orgId ? `org:${orgId}` : 'global-defaults' };
305
305
  },
306
306
  },
307
+ {
308
+ name: 'neo_scan_repo',
309
+ description:
310
+ 'Scan a Git repository and return its deployable components (monorepo-aware): each root directory with a build marker, its detected stack, and a suggested service type + build type. Use it to show the user the component inventory and ask which to deploy — never collapse a monorepo into one service without explicit selection (handoff 6). Read-only; deploys nothing.',
311
+ mutating: false,
312
+ schema: {
313
+ type: 'object',
314
+ properties: {
315
+ repoUrl: str('Git repository URL (https://host/owner/repo)'),
316
+ branch: str('Branch (optional; default the repo default branch)'),
317
+ gitProviderId: str('Git profile id for a private repo (optional)'),
318
+ },
319
+ required: ['repoUrl'],
320
+ },
321
+ async call(api, args) {
322
+ const { data } = await api.post('/api/v1/repos/scan', { repoUrl: args.repoUrl, branch: args.branch, gitProviderId: args.gitProviderId });
323
+ const subdirs = data?.subdirs || [];
324
+ const components = subdirs.map((s) => ({
325
+ path: s.path || '.',
326
+ name: s.name,
327
+ stack: s.type, // docker | node | go | python | java | php | ruby | rust | static
328
+ suggestedServiceType: s.type === 'static' ? 'static_site' : 'web',
329
+ suggestedBuildType: s.type === 'docker' ? 'dockerfile' : 'railpack',
330
+ contextDir: s.path || undefined,
331
+ }));
332
+ return {
333
+ repoUrl: args.repoUrl,
334
+ count: components.length,
335
+ components,
336
+ note:
337
+ components.length > 1
338
+ ? 'Monorepo: multiple deployable components. Ask the user which to deploy and create each selected one with its `contextDir` — do NOT collapse them into one service (handoff 6). Worker vs web cannot be inferred from the tree alone; confirm the type with the user.'
339
+ : components.length === 1
340
+ ? 'One deployable component found.'
341
+ : 'No deployable component found (no Dockerfile/package.json/go.mod/requirements.txt/index.html/… at any root).',
342
+ };
343
+ },
344
+ },
307
345
  {
308
346
  name: 'neo_list_templates',
309
347
  description: 'List deployable templates (e.g. wordpress, n8n, redis).',
@@ -435,6 +473,27 @@ export const TOOLS = [
435
473
  return redactSecrets((await api.get(`/api/v1/databases/${args.databaseId}`)).data);
436
474
  },
437
475
  },
476
+ {
477
+ name: 'neo_update_database',
478
+ description: 'Resize a managed database (handoff 4.4): grow storage (volumeSize) and/or set replicas for HA. Storage is GROW-ONLY — the server rejects a smaller volumeSize. Redeploy the database for a change to take effect.',
479
+ mutating: true,
480
+ schema: {
481
+ type: 'object',
482
+ properties: {
483
+ databaseId: str('Database id'),
484
+ volumeSize: str('New storage size, e.g. "20Gi" — must be >= current (grow-only)'),
485
+ replicas: int('Read/failover replicas (CNPG/postgres); total instances = 1 + replicas'),
486
+ },
487
+ required: ['databaseId'],
488
+ },
489
+ async call(api, args) {
490
+ const body = {};
491
+ if (args.volumeSize) body.volumeSize = args.volumeSize;
492
+ if (args.replicas !== undefined) body.replicas = args.replicas;
493
+ const { status, data } = await api.put(`/api/v1/databases/${args.databaseId}`, body);
494
+ return redactSecrets(result(status, data));
495
+ },
496
+ },
438
497
  {
439
498
  name: 'neo_create_queue',
440
499
  description: 'Create a managed queue in a project (handoff 4.5). Credentials redacted. May return pending_approval.',
@@ -534,6 +593,15 @@ export const TOOLS = [
534
593
  return redactSecrets(result(status, data));
535
594
  },
536
595
  },
596
+ {
597
+ name: 'neo_list_cron_runs',
598
+ description: 'List the recent runs of a scheduled job (cron) with their status/timing — run history + output verification (handoff 4.6).',
599
+ mutating: false,
600
+ schema: { type: 'object', properties: { cronId: str('Cron/scheduled-job id') }, required: ['cronId'] },
601
+ async call(api, args) {
602
+ return redactSecrets((await api.get(`/api/v1/crons/${args.cronId}/runs`)).data);
603
+ },
604
+ },
537
605
  {
538
606
  name: 'neo_create_bucket',
539
607
  description: 'Create an S3-compatible object-storage bucket in a project (handoff 4.7). Access keys are provisioned server-side and are NOT returned (write-only). May return pending_approval.',
@@ -573,6 +641,26 @@ export const TOOLS = [
573
641
  return redactSecrets((await api.get(`/api/v1/buckets/${args.bucketId}`)).data);
574
642
  },
575
643
  },
644
+ {
645
+ name: 'neo_list_bucket_objects',
646
+ description: 'List the objects in an object-storage bucket (handoff 4.7).',
647
+ mutating: false,
648
+ schema: { type: 'object', properties: { bucketId: str('Bucket id') }, required: ['bucketId'] },
649
+ async call(api, args) {
650
+ return (await api.get(`/api/v1/buckets/${args.bucketId}/objects`)).data;
651
+ },
652
+ },
653
+ {
654
+ name: 'neo_delete_bucket_object',
655
+ description: 'Delete one object from an object-storage bucket by its key. Destructive and irreversible (handoff 4.7).',
656
+ mutating: true,
657
+ destructive: true,
658
+ schema: { type: 'object', properties: { bucketId: str('Bucket id'), object: str('Object key/path to delete') }, required: ['bucketId', 'object'] },
659
+ async call(api, args) {
660
+ const { status, data } = await api.del(`/api/v1/buckets/${args.bucketId}/objects`, { query: { object: args.object } });
661
+ return result(status, data);
662
+ },
663
+ },
576
664
  // --- Typed bindings (handoff 5): attach a DB/queue to a consumer service. The
577
665
  // connection string is injected as an env var SERVER-SIDE at the consumer's next
578
666
  // deploy (marked a secret) — it is never returned to the agent (write-only). ---