@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 +1 -1
- package/prompt.md +25 -1
- package/src/api.js +23 -5
- package/src/mcp/tools.js +7 -0
package/package.json
CHANGED
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.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
},
|