@biznetgio/neo-app 0.4.39 → 0.4.41

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biznetgio/neo-app",
3
- "version": "0.4.39",
3
+ "version": "0.4.41",
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/prompt.md CHANGED
@@ -6,7 +6,7 @@ Keep this in sync with cli/prompt.md (the npm package's bundled copy).
6
6
  -->
7
7
  # Set this repo up on NEO App
8
8
 
9
- **Version:** 0.4.4 · **Last updated:** 2026-10-15
9
+ **Version:** 0.4.5 · **Last updated:** 2026-10-15
10
10
 
11
11
  These are the official setup instructions from NEO App (Biznet Gio's managed-app
12
12
  platform). If you are an AI coding agent, complete the steps below yourself by running
@@ -138,6 +138,30 @@ use `neo_enable_db_public_access` / `neo_enable_queue_public_access` (deploy it
138
138
  Never deploy to **production** without an explicit request, and never default a missing
139
139
  environment — every create/deploy names `staging` or `production`.
140
140
 
141
+ ## Talking to non-technical users (vibe coders)
142
+
143
+ Most people deploying here are not platform engineers. Keep the conversation about
144
+ *their app*, not Kubernetes.
145
+
146
+ - **Ask one short question at a time**, and lead with the recommended answer, e.g.
147
+ "I'll deploy this as a web service (recommended) — or is it a background worker?".
148
+ - **Follow the user's language.** If they write in Bahasa Indonesia, reply and ask in
149
+ Bahasa Indonesia.
150
+ - **Don't ask about platform internals** — ingress, build contexts, runtime classes,
151
+ Dockerfile paths, namespaces. Infer them (`neo_scan_repo` gives contextDir, build
152
+ type, and a suggested port) or use safe defaults, and only surface a choice that
153
+ genuinely changes the outcome (web vs worker, which components, the cost).
154
+ - **Only ask which components to deploy when there's real ambiguity** — a monorepo
155
+ with several runnable services. For a single component, just proceed.
156
+ - **A scheduled job needs a target.** If the user asks for a cron but doesn't say what
157
+ it should hit, ask which URL or service command to run (use `neo_cron_questionnaire`).
158
+ - **Show the cost and get an explicit yes before anything billable** (the Approve &
159
+ Create / confirmation gates). Never infer consent from silence or an earlier,
160
+ unrelated approval.
161
+
162
+ The confirmed component/topology choice is captured in the immutable plan
163
+ (`neo_plan_stack`) — you don't modify the user's repository to record it.
164
+
141
165
  ## Environment
142
166
 
143
167
  The CLI targets production by default. Set `NEO_APP_URL` to point it at a different
package/src/api.js CHANGED
@@ -3,6 +3,8 @@
3
3
  // All routes are under /api/v1. A 202 + {status:"pending_approval"} is a normal
4
4
  // result (org approval policy), NOT an error, so only >=400 throws.
5
5
 
6
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
7
+
6
8
  export class ApiError extends Error {
7
9
  constructor(status, message, body) {
8
10
  super(message);
@@ -28,12 +30,28 @@ export class NeoApi {
28
30
  const headers = { Accept: accept || 'application/json' };
29
31
  if (this.token) headers.Authorization = `Bearer ${this.token}`;
30
32
  if (body !== undefined) headers['Content-Type'] = 'application/json';
33
+ const init = { method, headers, body: body !== undefined ? JSON.stringify(body) : undefined };
31
34
 
32
- const res = await fetch(url, {
33
- method,
34
- headers,
35
- body: body !== undefined ? JSON.stringify(body) : undefined,
36
- });
35
+ // Transient-failure retry: a long stack apply polls for minutes, and a single
36
+ // dropped connection ("fetch failed") or a brief 502/503/504 shouldn't fail the
37
+ // whole operation. Only retry IDEMPOTENT methods automatically — a POST/PUT that
38
+ // may have been processed must not be silently re-sent.
39
+ const idempotent = method === 'GET' || method === 'DELETE';
40
+ const maxTries = idempotent ? 4 : 1;
41
+ let res, lastErr;
42
+ for (let attempt = 1; attempt <= maxTries; attempt++) {
43
+ try {
44
+ res = await fetch(url, init);
45
+ } catch (e) {
46
+ lastErr = e; // connection never completed — transient
47
+ if (idempotent && attempt < maxTries) { await sleep(250 * 2 ** (attempt - 1)); continue; }
48
+ throw new ApiError(0, `network error contacting ${this.baseUrl}: ${e?.message || e}`, null);
49
+ }
50
+ if (idempotent && [502, 503, 504].includes(res.status) && attempt < maxTries) {
51
+ await sleep(250 * 2 ** (attempt - 1)); continue;
52
+ }
53
+ break;
54
+ }
37
55
 
38
56
  const ctype = res.headers.get('content-type') || '';
39
57
  const isJson = ctype.includes('application/json');
package/src/mcp/tools.js CHANGED
@@ -1242,6 +1242,13 @@ export const TOOLS = [
1242
1242
  targetService: str('cron http_endpoint: name of a web app resource IN THIS STACK to hit — its deployed URL is resolved at apply (use instead of a literal url for a same-stack service)'),
1243
1243
  targetPath: str('cron http_endpoint: path appended to the targetService URL (e.g. "/cron")'),
1244
1244
  method: str('cron http_endpoint: HTTP method'),
1245
+ headers: { type: 'object', description: 'cron http_endpoint: request headers (secret values redacted on read-back)' },
1246
+ body: str('cron http_endpoint: request body'),
1247
+ contentType: str('cron http_endpoint: Content-Type for the body (merged into headers)'),
1248
+ expectedStatus: int('cron http_endpoint: exact HTTP status required for a run to succeed (default any 2xx)'),
1249
+ timeoutSeconds: int('cron: per-run timeout (seconds)'),
1250
+ retryCount: int('cron: retries on failure'),
1251
+ concurrencyPolicy: { type: 'string', enum: ['Allow', 'Forbid', 'Replace'], description: 'cron: overlap policy (default Allow)' },
1245
1252
  },
1246
1253
  required: ['kind', 'name'],
1247
1254
  },