insta 0.0.35 → 0.0.37

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.
@@ -0,0 +1,69 @@
1
+ // nixpacks glue for `insta build`: framework detection (`nixpacks plan`) and Dockerfile
2
+ // generation (`nixpacks build --out`) — both static, neither touches a Docker daemon.
3
+ // Same injectable-runner pattern as flyctl-build.ts.
4
+ import { spawn } from 'node:child_process';
5
+ import { mkdtempSync, readFileSync, rmSync, existsSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ // Capture-only runner: plan output is parsed (not shown), and a wedged binary must not hang the
9
+ // command — kill after 30s and let the caller degrade.
10
+ export const quietRunner = (cmd, args, opts) => new Promise((resolve) => {
11
+ const child = spawn(cmd, args, { cwd: opts.cwd, env: opts.env, stdio: ['ignore', 'pipe', 'pipe'] });
12
+ let output = '';
13
+ const timer = setTimeout(() => child.kill('SIGKILL'), 30_000);
14
+ child.stdout?.on('data', (b) => { output += b.toString(); });
15
+ child.stderr?.on('data', (b) => { output += b.toString(); });
16
+ child.on('error', (err) => { clearTimeout(timer); resolve({ code: -1, output: `${output}\n${err.message}` }); });
17
+ child.on('close', (code) => { clearTimeout(timer); resolve({ code: code ?? -1, output }); });
18
+ });
19
+ export function parseNixpacksPlan(text) {
20
+ try {
21
+ const j = JSON.parse(text);
22
+ // Real plans (nixpacks ≥1.x) leave `providers` empty and name the matched provider(s) in the
23
+ // NIXPACKS_METADATA build variable instead.
24
+ const listed = Array.isArray(j.providers) ? j.providers : [];
25
+ const meta = typeof j.variables?.NIXPACKS_METADATA === 'string'
26
+ ? j.variables.NIXPACKS_METADATA.split(',').map((s) => s.trim()).filter(Boolean)
27
+ : [];
28
+ return {
29
+ providers: listed.length ? listed : meta,
30
+ installCommand: j.phases?.install?.cmds?.join(' && ') || undefined,
31
+ buildCommand: j.phases?.build?.cmds?.join(' && ') || undefined,
32
+ startCommand: j.start?.cmd || undefined,
33
+ };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ export async function nixpacksPlan(dir, run = quietRunner) {
40
+ const { code, output } = await run('nixpacks', ['plan', dir], { cwd: dir, env: process.env });
41
+ if (code !== 0)
42
+ return null;
43
+ return parseNixpacksPlan(output);
44
+ }
45
+ // `nixpacks build --out <dir>` generates .nixpacks/Dockerfile and skips Docker entirely. The out
46
+ // dir is a temp dir so the user's source tree stays clean (the platform writes into the source
47
+ // dir because it builds from a scratch clone — a local verify must not).
48
+ export async function nixpacksGeneratedDockerfile(dir, run = quietRunner) {
49
+ const out = mkdtempSync(join(tmpdir(), 'insta-nixpacks-'));
50
+ try {
51
+ const { code } = await run('nixpacks', ['build', dir, '--out', out], { cwd: dir, env: process.env });
52
+ if (code !== 0)
53
+ return null;
54
+ const generated = join(out, '.nixpacks', 'Dockerfile');
55
+ return existsSync(generated) ? readFileSync(generated, 'utf8') : null;
56
+ }
57
+ finally {
58
+ rmSync(out, { recursive: true, force: true });
59
+ }
60
+ }
61
+ // Quiet probe — no install, no output. `insta build` advertises itself as local and offline, so
62
+ // unlike deploy's ensureFlyctl it must never download anything or write to stdout (which would
63
+ // corrupt --json); when nixpacks is missing the command degrades to Dockerfile-only checks and
64
+ // the report's nextAction says how to install it.
65
+ export async function nixpacksAvailable(run = quietRunner) {
66
+ const { code } = await run('nixpacks', ['--version'], { cwd: '.', env: process.env });
67
+ return code === 0;
68
+ }
69
+ //# sourceMappingURL=nixpacks.js.map
@@ -1,5 +1,5 @@
1
1
  // `insta services add` with no type (or no name): the kinds are otherwise only discoverable by
2
- // guessing wrong and reading `type must be postgres|storage|compute|redis`, so missing arguments answer
2
+ // guessing wrong and reading `type must be postgres|storage|compute|redis|mysql|mongodb`, so missing arguments answer
3
3
  // "what can I add?" instead. The list mirrors the dashboard's Add Service menu (frontend
4
4
  // `add-service-button.tsx`) — Docker Image sits BESIDE Empty Service, not under it, because
5
5
  // picking an image is a different intent rather than a compute flag. An agent gets the same list
@@ -12,6 +12,8 @@ export const SERVICE_KINDS = [
12
12
  { id: 'image', label: 'Docker Image', type: 'compute', hint: 'run an existing container image', needsImage: true },
13
13
  { id: 'postgres', label: 'Postgres', type: 'postgres', hint: 'relational DB, usable as soon as it is added', defaultName: 'main-db' },
14
14
  { id: 'redis', label: 'Redis', type: 'redis', hint: 'private Redis-compatible cache', defaultName: 'cache' },
15
+ { id: 'mysql', label: 'MySQL', type: 'mysql', hint: 'private MySQL database', defaultName: 'mysql-db' },
16
+ { id: 'mongodb', label: 'MongoDB', type: 'mongodb', hint: 'private MongoDB database', defaultName: 'mongo-db' },
15
17
  { id: 'storage', label: 'Storage', type: 'storage', hint: 'S3-compatible bucket, private by default', defaultName: 'assets' },
16
18
  { id: 'compute', label: 'Empty Service', type: 'compute', hint: 'an app to deploy code to (empty until `insta deploy`)', defaultName: 'compute' },
17
19
  ];
package/dist/util.js CHANGED
@@ -26,10 +26,17 @@ export function info(msg) {
26
26
  process.stdout.write(msg + '\n');
27
27
  }
28
28
  // If the platform gated the action (HTTP 202), tell the user how to get it approved. Returns
29
- // true when an approval is pending (caller should stop).
30
- export function handleApproval(res) {
29
+ // true when an approval is pending (caller should stop). The hint goes to STDERR and the exit
30
+ // code is set to 2: a pending gate is not success (redirected stdout must never swallow it as
31
+ // output), and not a plain error either (die() owns 1) — it's approvable and re-runnable, and
32
+ // scripts/agents branch on the distinct code. With json, stdout carries the platform's raw 202
33
+ // envelope so a scripted caller can lift approvalId/action.
34
+ export function handleApproval(res, json) {
31
35
  if (res.status === 202 && res.body?.status === 'approval_required') {
32
- info(`approval required for ${res.body.action} — run: insta approvals approve ${res.body.approvalId}`);
36
+ if (json)
37
+ printJson(res.body);
38
+ process.stderr.write(`approval required for ${res.body.action} — run: insta approvals approve ${res.body.approvalId}\n`);
39
+ process.exitCode = 2;
33
40
  return true;
34
41
  }
35
42
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [