@biznetgio/neo-app 0.4.64 → 0.4.66
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 +2 -1
- package/src/mcp/tools.js +18 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@biznetgio/neo-app",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.66",
|
|
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": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "node --test",
|
|
19
19
|
"e2e": "node e2e/mcp-regression.mjs",
|
|
20
|
+
"release:status": "node scripts/release-status.mjs",
|
|
20
21
|
"prepublishOnly": "node --test"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
package/src/mcp/tools.js
CHANGED
|
@@ -178,7 +178,7 @@ async function confirmCost(api, args, spec) {
|
|
|
178
178
|
// { requiresConfirmation } — a preview; CREATES NOTHING
|
|
179
179
|
// A confirmationId re-plays an already-approved confirmation; confirm:true is a one-call
|
|
180
180
|
// shortcut that mints + auto-approves one; neither returns the cost + a pending confirmationId.
|
|
181
|
-
async function billableGate(api, { projectId, kind, name, confirmationId, confirm }, spec) {
|
|
181
|
+
async function billableGate(api, { projectId, kind, name, confirmationId, confirm, environment }, spec) {
|
|
182
182
|
if (confirmationId) return { query: `?confirmationId=${encodeURIComponent(confirmationId)}` };
|
|
183
183
|
|
|
184
184
|
// Estimate the cost (best-effort) for display and the confirmation summary.
|
|
@@ -197,7 +197,7 @@ async function billableGate(api, { projectId, kind, name, confirmationId, confir
|
|
|
197
197
|
// from failing an otherwise-fine create (same spirit as the api-layer idempotent retry).
|
|
198
198
|
let mint;
|
|
199
199
|
for (let attempt = 1; ; attempt++) {
|
|
200
|
-
try { mint = (await api.post('/api/v1/confirmations', { action: 'billable_create', projectId, kind, name, estimatedCost: costNote })).data; break; }
|
|
200
|
+
try { mint = (await api.post('/api/v1/confirmations', { action: 'billable_create', projectId, kind, name, environment, estimatedCost: costNote })).data; break; }
|
|
201
201
|
catch (e) {
|
|
202
202
|
if (attempt < 3 && (e?.status >= 500 || e?.status === 0)) { await new Promise((r) => setTimeout(r, 300 * attempt)); continue; }
|
|
203
203
|
throw e;
|
|
@@ -509,6 +509,15 @@ export const TOOLS = [
|
|
|
509
509
|
return result(status, data);
|
|
510
510
|
},
|
|
511
511
|
},
|
|
512
|
+
{
|
|
513
|
+
name: 'neo_get_domain_status',
|
|
514
|
+
description: 'Check a custom domain\'s readiness to a TERMINAL state (JNG #112 §8.6): whether it is attached, whether DNS resolves to the expected CNAME target (with an expected-vs-observed diagnostic), whether managed TLS has been issued, and whether HTTPS actually serves a valid certificate for the host. Poll this after neo_add_service_domain + redeploy until ready:true — do NOT assume "attach accepted" means live. Returns {attached, expectedTarget, dns:{pointed,observed,diagnostic}, tls:{issued}, https:{ok,statusCode,certHostnameValid}, ready}.',
|
|
515
|
+
mutating: false,
|
|
516
|
+
schema: { type: 'object', properties: { appId: str('Service/app id'), domain: str('Custom domain host to check, e.g. "app.example.com"') }, required: ['appId', 'domain'] },
|
|
517
|
+
async call(api, args) {
|
|
518
|
+
return (await api.get(`/api/v1/apps/${args.appId}/domains/status`, { query: { domain: args.domain } })).data;
|
|
519
|
+
},
|
|
520
|
+
},
|
|
512
521
|
{
|
|
513
522
|
name: 'neo_remove_service_domain',
|
|
514
523
|
description: 'Detach a custom domain from a service (JNG #112). SAFE BY DEFAULT: without confirm:true it returns a preview (what will be detached) and removes nothing; re-call with confirm:true to detach — dropping its ingress + managed TLS on the next deploy. The default platform host and other domains are untouched.',
|
|
@@ -1088,7 +1097,7 @@ export const TOOLS = [
|
|
|
1088
1097
|
async call(api, args) {
|
|
1089
1098
|
const environment = requireEnv(args.environment, args.allowCrossEnvironment); // explicit, else the session pin (JNG #136)
|
|
1090
1099
|
validateDbArgs(args); // reject sub-10Gi / EOL config before pricing (JNG #132)
|
|
1091
|
-
const gate = await billableGate(api, { projectId: args.projectId, kind: 'database', name: args.name, confirmationId: args.confirmationId, confirm: args.confirm }, { storageGB: gbFromSize(args.volumeSize), replicas: args.replicas });
|
|
1100
|
+
const gate = await billableGate(api, { projectId: args.projectId, kind: 'database', name: args.name, confirmationId: args.confirmationId, confirm: args.confirm, environment }, { storageGB: gbFromSize(args.volumeSize), replicas: args.replicas });
|
|
1092
1101
|
if (gate.requiresConfirmation) return { environment, ...gate };
|
|
1093
1102
|
const { projectId, confirm, confirmationId, orgId, ...body } = args;
|
|
1094
1103
|
body.environment = environment; // send the resolved env so the pin actually targets it
|
|
@@ -1261,7 +1270,7 @@ export const TOOLS = [
|
|
|
1261
1270
|
},
|
|
1262
1271
|
async call(api, args) {
|
|
1263
1272
|
const environment = requireEnv(args.environment, args.allowCrossEnvironment); // explicit, else the session pin (JNG #136)
|
|
1264
|
-
const gate = await billableGate(api, { projectId: args.projectId, kind: 'queue', name: args.name, confirmationId: args.confirmationId, confirm: args.confirm }, { storageGB: gbFromSize(args.volumeSize) });
|
|
1273
|
+
const gate = await billableGate(api, { projectId: args.projectId, kind: 'queue', name: args.name, confirmationId: args.confirmationId, confirm: args.confirm, environment }, { storageGB: gbFromSize(args.volumeSize) });
|
|
1265
1274
|
if (gate.requiresConfirmation) return { environment, ...gate };
|
|
1266
1275
|
const { projectId, confirm, confirmationId, orgId, ...body } = args;
|
|
1267
1276
|
body.environment = environment; // send the resolved env so the pin actually targets it
|
|
@@ -1443,7 +1452,7 @@ export const TOOLS = [
|
|
|
1443
1452
|
},
|
|
1444
1453
|
{
|
|
1445
1454
|
name: 'neo_list_cron_runs',
|
|
1446
|
-
description: 'List the recent runs of a scheduled job (cron)
|
|
1455
|
+
description: 'List the recent runs of a scheduled job (cron): status (running|succeeded|failed), startedAt/finishedAt, and for a FAILED run the terminal diagnostics — reason, message, and the container exitCode — so you can see WHY it failed, not just that it did (JNG #112, handoff 4.6).',
|
|
1447
1456
|
mutating: false,
|
|
1448
1457
|
schema: { type: 'object', properties: { cronId: str('Cron/scheduled-job id') }, required: ['cronId'] },
|
|
1449
1458
|
async call(api, args) {
|
|
@@ -1680,6 +1689,7 @@ export const TOOLS = [
|
|
|
1680
1689
|
image: str('app/cron: prebuilt image reference'),
|
|
1681
1690
|
repoUrl: str('app: git repository URL'),
|
|
1682
1691
|
branch: str('app: git branch'),
|
|
1692
|
+
deps: { type: 'array', items: { type: 'string', enum: ['database', 'queue', 'object_storage'] }, description: 'app: managed-resource dependencies this service needs (from neo_scan_repo `deps`). The plan flags any dep not satisfied by a binding as incompleteTopology and apply refuses it unless acknowledged — so a service needing NATS_URL/DATABASE_URL isn\'t deployed into a 503 (JNG #112 §7).' },
|
|
1683
1693
|
port: int('app: container port'),
|
|
1684
1694
|
computeTier: str('app/db/queue: micro | small | medium | large | xlarge'),
|
|
1685
1695
|
engine: str('database: postgres | mysql'),
|
|
@@ -1738,18 +1748,19 @@ export const TOOLS = [
|
|
|
1738
1748
|
{
|
|
1739
1749
|
name: 'neo_apply_stack',
|
|
1740
1750
|
description:
|
|
1741
|
-
'Apply a planned stack. REQUIRES the planId and the exact planHash from neo_plan_stack — the SERVER rejects a missing, changed, expired, or already-applied plan, so a billable stack can never be created without first creating + reviewing its costed plan (handoff 3.4/3.5, server-enforced approval). Creates resources in dependency order (foundations → services → bindings → deploy) and returns an operationId to poll with neo_get_operation.',
|
|
1751
|
+
'Apply a planned stack. REQUIRES the planId and the exact planHash from neo_plan_stack — the SERVER rejects a missing, changed, expired, or already-applied plan, so a billable stack can never be created without first creating + reviewing its costed plan (handoff 3.4/3.5, server-enforced approval). Creates resources in dependency order (foundations → services → bindings → deploy) and returns an operationId to poll with neo_get_operation. If the plan has an incompleteTopology (a service dependency not bound), apply returns error "incomplete_topology" — wire the missing resource + re-plan, or pass acknowledgeIncompleteTopology:true to deploy anyway.',
|
|
1742
1752
|
mutating: true,
|
|
1743
1753
|
schema: {
|
|
1744
1754
|
type: 'object',
|
|
1745
1755
|
properties: {
|
|
1746
1756
|
planId: str('Plan id from neo_plan_stack'),
|
|
1747
1757
|
planHash: str('Plan hash from neo_plan_stack — must match exactly (proves you reviewed this plan)'),
|
|
1758
|
+
acknowledgeIncompleteTopology: { type: 'boolean', description: 'Deploy past a detected unmet dependency (incompleteTopology) — e.g. a service that needs NATS_URL with no queue bound. Default false refuses it (JNG #112 §7).' },
|
|
1748
1759
|
},
|
|
1749
1760
|
required: ['planId', 'planHash'],
|
|
1750
1761
|
},
|
|
1751
1762
|
async call(api, args) {
|
|
1752
|
-
const { status, data } = await api.post(`/api/v1/plans/${args.planId}/apply`, { planHash: args.planHash });
|
|
1763
|
+
const { status, data } = await api.post(`/api/v1/plans/${args.planId}/apply`, { planHash: args.planHash, acknowledgeIncompleteTopology: !!args.acknowledgeIncompleteTopology });
|
|
1753
1764
|
return result(status, data);
|
|
1754
1765
|
},
|
|
1755
1766
|
},
|