@biznetgio/neo-app 0.4.65 → 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.
Files changed (2) hide show
  1. package/package.json +2 -1
  2. package/src/mcp/tools.js +13 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biznetgio/neo-app",
3
- "version": "0.4.65",
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