@nitsan-ai/ragsuite-test 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,85 +1,83 @@
1
1
  # RAGSuite test CLI (`ragsuite-test`)
2
2
 
3
- > **TESTING PACKAGE** — `@nitsan-ai/ragsuite-test` / `ragsuite-test` is temporary.
4
- > **Not** the final production `@nitsan-ai/ragsuite` / `ragsuite` CLI (later release).
5
- > Do **not** treat this as the long-term npm identity.
3
+ > **TESTING PACKAGE** — temporary. Final public CLI will be `ragsuite` later.
6
4
 
7
- Wraps monorepo Docker/native scripts. Does **not** reimplement compose.
5
+ **One-command public install** (GitHub repo must be public):
8
6
 
9
- Requires **Node.js 18+**.
10
-
11
- **Run modes (both fully supported):**
12
-
13
- | Situation | Mode | Command |
14
- |-----------|------|---------|
15
- | You have Docker | `docker` (default) | `ragsuite-test init … --mode docker` then `start` |
16
- | No Docker | `native` | Need Postgres **:5436** + Redis **:6382** on the host, then `ragsuite-test init … --mode native` then `start` |
17
-
18
- Docker is **not** mandatory — choose native in the wizard or pass `--mode native`.
7
+ ```bash
8
+ npm install -g @nitsan-ai/ragsuite-test
9
+ ragsuite-test init
10
+ ragsuite-test start --repo-root ~/ragsuite-test --detach
11
+ ```
19
12
 
20
- **Full tester steps:** [TEST_DISTRIBUTION.md](./TEST_DISTRIBUTION.md)
13
+ That clones `https://github.com/nitsan-ai/RAGSUITE.git` into `~/ragsuite-test`, writes `.env` (no SMTP/LLM questions), and picks **Docker** if the daemon is up, otherwise **native**.
21
14
 
22
- ## Quick start (wizard)
15
+ - API: http://localhost:9090
16
+ - Web (Docker): http://localhost:9091
17
+ - Web (native): http://localhost:9191
23
18
 
24
19
  ```bash
25
- bash scripts/build-release-zip.sh
26
- cd cli && npm pack && npm install -g ./nitsan-ai-ragsuite-test-*.tgz
20
+ ragsuite-test stop --repo-root ~/ragsuite-test
21
+ ```
27
22
 
28
- # Interactive:
29
- ragsuite-test init
23
+ ## Prerequisites
30
24
 
31
- # Or CI / noninteractive:
32
- ragsuite-test init --yes \
33
- --from-zip ../dist/ragsuite-server-test-1.0.0.zip \
34
- --install-dir ~/ragsuite-test \
35
- --mode docker \
36
- --llm-api-key 'your-key'
25
+ | Need | Required? | How |
26
+ |------|-----------|-----|
27
+ | Node.js 18+ | Yes | https://nodejs.org · `brew install node` |
28
+ | git | Yes | `xcode-select --install` · `brew install git` |
29
+ | Docker | No | Optional; without it init uses native (needs Postgres `:5436` + Redis `:6382`) |
37
30
 
38
- ragsuite-test start --repo-root ~/ragsuite-test --detach
39
- # API http://localhost:9090 · Web http://localhost:9091
40
- ```
31
+ Real SMTP belongs only in local `.env` (gitignored). Init never asks for it.
41
32
 
42
- `init` auto-generates `JWT_SECRET_KEY`, writes `.ragsuite-test/config.json`, checks ports **9090 / 9091 / 5436 / 6382**, and warns if SMTP is skipped (invites/email may fail).
33
+ ## Advanced (optional)
43
34
 
44
- ## Install sources
35
+ ```bash
36
+ # Force native / docker
37
+ ragsuite-test init --mode native
38
+ ragsuite-test init --mode docker
39
+
40
+ # Custom install dir
41
+ ragsuite-test init --install-dir ~/my-ragsuite
45
42
 
46
- | Method | Flag |
47
- |--------|------|
48
- | Local ZIP | `--from-zip` (recommended for private testing) |
49
- | Existing checkout | `--repo-root` |
50
- | GitHub Release | `--from-release` — not implemented yet |
43
+ # Existing checkout (CI / developers)
44
+ ragsuite-test init --repo-root /path/to/RAGSUITE
45
+ ```
51
46
 
52
- Making GitHub public is **not** required for ZIP installs.
47
+ ## Ports
53
48
 
54
- ## Config
49
+ | Service | Port |
50
+ |---------|------|
51
+ | API | 9090 |
52
+ | Web (Docker) | 9091 |
53
+ | Web (native Expo) | 9191 |
54
+ | Postgres | 5436 |
55
+ | Redis | 6382 |
55
56
 
56
- - CLI: `<install>/.ragsuite-test/config.json`
57
- - Native PID/logs: `<install>/.ragsuite/native/` (Phase 4 scripts)
57
+ Busy ports: `ragsuite-test stop --repo-root ~/ragsuite-test` · `lsof -nP -iTCP:9090 -sTCP:LISTEN`
58
58
 
59
59
  ## Commands
60
60
 
61
61
  | Command | Behavior |
62
62
  |---------|----------|
63
- | `init` | Installer wizard (ZIP/checkout.env + config) |
64
- | `start` / `stop` | `scripts/docker-*.sh` or `native-*.sh` |
65
- | `doctor` | `scripts/doctor.sh` |
63
+ | `init` | Clone public repo`.env` + config (default) |
64
+ | `start` / `stop` | Docker or native scripts |
65
+ | `doctor` | Health / prereq hints |
66
66
  | `logs` / `update` / `version` | As before |
67
67
 
68
- ## Publish (testing package only — Phase 12)
68
+ ## Publish
69
69
 
70
- See [PUBLISH.md](./PUBLISH.md) (full runbook) and [TEST_DISTRIBUTION.md](./TEST_DISTRIBUTION.md) (tester steps + go/no-go).
70
+ See [PUBLISH.md](./PUBLISH.md).
71
71
 
72
72
  ```bash
73
73
  cd cli && npm run prepublish:check
74
- # Only when someone says "publish now":
74
+ # Only when approved:
75
75
  RAGSUITE_TEST_ALLOW_PUBLISH=1 npm publish --access public
76
- # Or: GitHub Actions → CLI publish (workflow_dispatch, NPM_TOKEN)
77
- # Optional app ZIP: Actions → Release app ZIP
78
76
  ```
79
77
 
80
78
  ## Windows
81
79
 
82
- Use WSL or Git Bash (`bash` scripts).
80
+ Use WSL or Git Bash.
83
81
 
84
82
  ## Tests
85
83
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@nitsan-ai/ragsuite-test",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
- "description": "RAGSuite testing CLI (ragsuite-test) — install from ZIP or --repo-root checkout. Temporary npm identity before public ragsuite release.",
5
+ "description": "RAGSuite testing CLI (ragsuite-test) — public install: npm i -g && ragsuite-test init (clones GitHub). Temporary name before public ragsuite release.",
6
6
  "main": "src/index.js",
7
7
  "bin": {
8
8
  "ragsuite-test": "./bin/ragsuite-test.js"
@@ -1,51 +1,29 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('fs');
4
3
  const path = require('path');
5
4
  const { writeConfig, CONFIG_DIR } = require('../utils/config');
6
- const { ensureRagsuiteDir, resolveRepoRoot, looksLikeRepoRoot } = require('../utils/paths');
5
+ const { ensureRagsuiteDir, resolveRepoRoot } = require('../utils/paths');
7
6
  const { assertSupportedInstall, installHint } = require('../utils/distribution');
8
7
  const {
8
+ DEFAULT_GIT_URL,
9
9
  defaultInstallDir,
10
- extractZipToInstallDir,
11
- } = require('../utils/zip-install');
10
+ cloneGitToInstallDir,
11
+ } = require('../utils/git-install');
12
12
  const { detectOs } = require('../utils/os-detect');
13
- const { printPrereqs, checkPrereqs } = require('../utils/prereqs');
14
- const { ask, confirm, choose } = require('../utils/prompt');
13
+ const {
14
+ printPrereqs,
15
+ resolvePreferredMode,
16
+ } = require('../utils/prereqs');
15
17
  const {
16
18
  generateJwtSecret,
17
- generateCiLlmKey,
18
19
  isPlaceholderSecret,
19
20
  writeInstallEnv,
20
21
  } = require('../utils/env-file');
21
22
  const { assertPortsFree } = require('../utils/port');
22
- const { info, warn, error } = require('../utils/log');
23
+ const { info, error } = require('../utils/log');
23
24
 
24
25
  const name = 'init';
25
- const summary = 'Installer wizard: ZIP or checkout → .env + config';
26
-
27
- function help() {
28
- return `Usage: ragsuite-test init [options]
29
-
30
- Interactive installer (or --yes for CI) that:
31
- - detects OS / prerequisites
32
- - installs from ZIP or configures a checkout
33
- - writes .ragsuite-test/config.json and .env (JWT auto-generated)
34
- - checks ports 9090, 9091, 5436, 6382
35
-
36
- Install sources:
37
- --from-zip <path> Extract app ZIP into --install-dir
38
- --repo-root <path> Use an existing monorepo checkout
39
- --from-release <tag> Not implemented (download ZIP manually)
40
-
41
- Options:
42
- --install-dir <path> ZIP target (default: ~/ragsuite-test)
43
- --mode <docker|native> docker = Docker stack; native = no Docker (needs host Postgres/Redis)
44
- --llm-api-key <key> CUSTOM_LLM_INTERNAL_API_KEY (--yes)
45
- --force Overwrite non-empty install dir / existing .env
46
- --yes, -y Non-interactive (requires --from-zip or --repo-root)
47
- `;
48
- }
26
+ const summary = 'One-command install: clone public GitHub repo → .env + config';
49
27
 
50
28
  function printSuccess(repoRoot, mode) {
51
29
  info('');
@@ -61,9 +39,6 @@ function printSuccess(repoRoot, mode) {
61
39
  info(' Web http://localhost:9091 (Docker nginx)');
62
40
  }
63
41
  info('');
64
- info(' Mode reminder: docker needs Docker; native needs host Postgres/Redis (no Docker required).');
65
- info(' Each install directory is one deployment / one organization.');
66
- info('');
67
42
  info('Next steps:');
68
43
  info(` 1. ragsuite-test start --repo-root ${repoRoot}${mode === 'docker' ? ' --detach' : ''}`);
69
44
  if (mode === 'native') {
@@ -76,139 +51,119 @@ function printSuccess(repoRoot, mode) {
76
51
  info(installHint());
77
52
  }
78
53
 
79
- async function resolveLlmKey(g, env, interactive) {
80
- if (g.llmApiKey && !isPlaceholderSecret(g.llmApiKey)) {
81
- return g.llmApiKey;
82
- }
54
+ function help() {
55
+ return `Usage: ragsuite-test init [options]
56
+
57
+ Simple public install (default — no questions):
58
+ clones ${DEFAULT_GIT_URL}
59
+ into ~/ragsuite-test
60
+ picks docker mode if Docker is running, otherwise native
61
+ writes .env (JWT auto; SMTP/LLM from .env.example — no prompts)
62
+
63
+ ragsuite-test init
64
+ ragsuite-test start --repo-root ~/ragsuite-test --detach
65
+
66
+ Advanced (optional):
67
+ --from-git [url] Clone this URL instead of the default
68
+ --repo-root <path> Use an existing checkout (no clone)
69
+ --install-dir <path> Clone target (default: ~/ragsuite-test)
70
+ --mode <docker|native> Force run mode
71
+ --force Overwrite non-empty install dir / existing .env
72
+ --yes, -y Same defaults; for scripts/CI
73
+ --llm-api-key / --smtp-* Optional overrides (usually not needed)
74
+ `;
75
+ }
76
+
77
+ function optionalSmtpOverrides(g, env) {
78
+ const host = g.smtpHost || env.RAGSUITE_TEST_SMTP_HOST || null;
79
+ const user = g.smtpUser || env.RAGSUITE_TEST_SMTP_USER || null;
80
+ const password = g.smtpPassword || env.RAGSUITE_TEST_SMTP_PASSWORD || null;
81
+ const emailFrom = g.emailFrom || env.RAGSUITE_TEST_EMAIL_FROM || null;
82
+ if (!host && !user && !password && !emailFrom) return null;
83
+ return { host, user, password, emailFrom };
84
+ }
85
+
86
+ function resolveLlmOverride(g, env) {
87
+ if (g.llmApiKey && !isPlaceholderSecret(g.llmApiKey)) return g.llmApiKey;
83
88
  if (env.RAGSUITE_TEST_LLM_API_KEY && !isPlaceholderSecret(env.RAGSUITE_TEST_LLM_API_KEY)) {
84
89
  return env.RAGSUITE_TEST_LLM_API_KEY;
85
90
  }
86
- if (g.yes || !interactive) {
87
- const key = generateCiLlmKey();
88
- warn(`Using generated smoke LLM key (${key.slice(0, 18)}…). Set a real CUSTOM_LLM_INTERNAL_API_KEY for production-like tests.`);
89
- return key;
90
- }
91
- for (;;) {
92
- const key = await ask('CUSTOM_LLM_INTERNAL_API_KEY', { yesMode: false });
93
- if (!isPlaceholderSecret(key)) return key;
94
- error('Key required and must not start with change-me');
95
- }
91
+ return null;
92
+ }
93
+
94
+ function countSources(fromGit, repoRootFlag) {
95
+ let n = 0;
96
+ if (fromGit !== null && fromGit !== undefined) n += 1;
97
+ if (repoRootFlag) n += 1;
98
+ return n;
96
99
  }
97
100
 
98
101
  async function run(ctx) {
99
102
  assertSupportedInstall();
100
103
  const g = ctx.globals;
101
- const interactive = Boolean(process.stdin.isTTY) && !g.yes;
102
104
 
103
105
  if (g.fromRelease) {
104
- error(
105
- `--from-release is not implemented. Download a ZIP (private Release asset or local file), then: ragsuite-test init --from-zip <file.zip>`,
106
- );
106
+ error('--from-release is not supported. Use default init, --from-git, or --repo-root.');
107
107
  return 1;
108
108
  }
109
109
 
110
- if (g.fromZip && g.repoRoot) {
111
- error('Use either --from-zip or --repo-root, not both');
110
+ if (countSources(g.fromGit, g.repoRoot) > 1) {
111
+ error('Use only one of --from-git or --repo-root');
112
112
  return 1;
113
113
  }
114
114
 
115
115
  const osInfo = detectOs();
116
116
  info(`OS: ${osInfo.label} (${osInfo.platform})`);
117
117
 
118
- let mode = g.mode || 'docker';
119
- let fromZip = g.fromZip;
118
+ let fromGit = g.fromGit;
120
119
  let repoRootFlag = g.repoRoot;
121
- let installDir = g.installDir || defaultInstallDir();
120
+ const installDir = g.installDir || defaultInstallDir();
122
121
 
123
- if (g.yes) {
124
- if (!fromZip && !repoRootFlag) {
125
- error('--yes requires --from-zip <path> or --repo-root <path>');
126
- return 1;
127
- }
128
- } else if (interactive && !fromZip && !repoRootFlag) {
129
- const method = await choose(
130
- 'Install method:',
131
- [
132
- { id: 'zip', label: 'Local ZIP file (--from-zip)' },
133
- { id: 'checkout', label: 'Existing checkout (--repo-root)' },
134
- ],
135
- { defaultIndex: 0 },
136
- );
137
- if (method.id === 'zip') {
138
- fromZip = await ask('Path to app ZIP', { yesMode: false });
139
- if (!fromZip) {
140
- error('ZIP path required');
141
- return 1;
142
- }
143
- installDir = await ask('Install directory', {
144
- defaultValue: defaultInstallDir(),
145
- yesMode: false,
146
- });
147
- } else {
148
- repoRootFlag = await ask('Path to existing RAGSUITE checkout', { yesMode: false });
149
- if (!repoRootFlag || !looksLikeRepoRoot(path.resolve(repoRootFlag))) {
150
- error('Not a valid checkout (need scripts/docker-start.sh)');
151
- return 1;
152
- }
153
- }
154
-
155
- const modeChoice = await choose(
156
- 'Run mode (Docker is optional):',
157
- [
158
- { id: 'docker', label: 'Docker — if you have Docker Desktop / daemon (default)' },
159
- { id: 'native', label: 'Native — no Docker; needs Postgres :5436 + Redis :6382 on this machine' },
160
- ],
161
- { defaultIndex: 0 },
162
- );
163
- mode = modeChoice.id;
164
- } else if (!fromZip && !repoRootFlag) {
165
- error('Pass --from-zip / --repo-root, or run in a TTY for the interactive wizard');
166
- return 1;
122
+ // Default public path: no menus, no path prompts — clone GitHub → ~/ragsuite-test
123
+ if (fromGit === null && !repoRootFlag) {
124
+ fromGit = '';
125
+ info(`Install: clone ${DEFAULT_GIT_URL}`);
126
+ info(`Target: ${path.resolve(installDir)}`);
167
127
  }
168
128
 
169
- if (!printPrereqs(mode)) {
129
+ const mode = g.mode || resolvePreferredMode(null);
130
+ info(`Run mode: ${mode} (Docker optional — pass --mode docker|native to force)`);
131
+
132
+ const needGit = fromGit !== null && fromGit !== undefined;
133
+ if (!printPrereqs(mode, { needGit })) {
170
134
  error('Fix prerequisites above and re-run init');
171
135
  return 1;
172
136
  }
173
137
 
174
138
  const jwtSecret = generateJwtSecret();
175
139
  info(`JWT_SECRET_KEY will be auto-generated (…${jwtSecret.slice(-8)})`);
140
+ info('SMTP + LLM: from .env.example into .env (no prompts).');
176
141
 
177
- const llmKey = await resolveLlmKey(g, ctx.env, interactive);
142
+ const llmKey = resolveLlmOverride(g, ctx.env);
143
+ const smtp = optionalSmtpOverrides(g, ctx.env);
178
144
 
179
- let smtpSkipped = true;
180
- if (interactive && !g.yes) {
181
- const configureSmtp = await confirm('Configure SMTP now? (needed for invites/email)', {
182
- defaultYes: false,
183
- });
184
- smtpSkipped = !configureSmtp;
185
- if (!smtpSkipped) {
186
- info('After init, edit SMTP_* in .env (SMTP_HOST, SMTP_USER, SMTP_PASSWORD, EMAIL_FROM).');
145
+ if (ctx.env.RAGSUITE_TEST_SKIP_PORT_CHECK !== '1') {
146
+ try {
147
+ await assertPortsFree();
148
+ } catch (err) {
149
+ error(err.message);
150
+ return 1;
187
151
  }
188
- } else {
189
- warn('SMTP skipped (--yes / noninteractive). Invites/email may fail until SMTP_* is set.');
190
- }
191
-
192
- // Port checks before mutating install (clearer UX)
193
- try {
194
- await assertPortsFree();
195
- } catch (err) {
196
- error(err.message);
197
- return 1;
198
152
  }
199
153
 
200
154
  let repoRoot;
201
155
  let source = 'checkout';
202
156
 
203
157
  try {
204
- if (fromZip) {
205
- info(`Extracting ZIP ${path.resolve(installDir)}`);
206
- const result = extractZipToInstallDir(fromZip, {
158
+ if (fromGit !== null && fromGit !== undefined) {
159
+ const url = fromGit === '' ? DEFAULT_GIT_URL : fromGit;
160
+ info(`Cloning ${url} ${path.resolve(installDir)}`);
161
+ const result = cloneGitToInstallDir(url, {
207
162
  installDir,
208
163
  force: g.force,
209
164
  });
210
165
  repoRoot = result.repoRoot;
211
- source = 'zip';
166
+ source = 'git';
212
167
  info(`Installed app at ${repoRoot}`);
213
168
  } else {
214
169
  repoRoot = resolveRepoRoot({
@@ -235,10 +190,10 @@ async function run(ctx) {
235
190
  force: g.force,
236
191
  jwtSecret,
237
192
  llmApiKey: llmKey,
238
- smtpSkipped,
193
+ smtp,
239
194
  });
240
195
  } catch (err) {
241
- if (err.code === 'ENV_EXISTS') {
196
+ if (err.code === 'ENV_EXISTS' || err.code === 'ENV_INCOMPLETE') {
242
197
  error(err.message);
243
198
  return 1;
244
199
  }
@@ -14,8 +14,7 @@ function help() {
14
14
  return `Usage: ragsuite-test update [options]
15
15
 
16
16
  Verifies the install/repo path and prints safe update steps.
17
- ZIP installs: download a newer ZIP and extract to a new dir (or backup + --force).
18
- Checkout installs: optional git fetch + status (no reset).
17
+ Git installs: git fetch + pull guidance (no destructive reset).
19
18
 
20
19
  Never runs docker compose down -v.
21
20
 
@@ -32,38 +31,26 @@ function run(ctx) {
32
31
  });
33
32
 
34
33
  const cfg = readConfig(repoRoot);
35
- const source = cfg && cfg.source === 'zip' ? 'zip' : 'checkout';
34
+ const source = (cfg && cfg.source) || 'checkout';
36
35
 
37
36
  info(`Install path OK: ${repoRoot}`);
38
37
  info(` source=${source}`);
39
38
  info('');
40
39
 
41
- if (source === 'zip') {
42
- info('This install came from a ZIP. Safe update steps:');
43
- info(' 1. ragsuite-test stop --repo-root ' + repoRoot);
44
- info(' 2. Download a newer release ZIP (do not wipe Docker volumes)');
45
- info(' 3. Extract to a NEW directory, e.g. --install-dir ~/ragsuite-test-v2');
46
- info(' or: backup this dir, then init --from-zip … --force');
47
- info(' 4. Copy your .env into the new install (do not commit secrets)');
48
- info(' 5. ragsuite-test doctor --repo-root <new-dir>');
49
- info(' 6. ragsuite-test start --repo-root <new-dir>');
50
- info('');
51
- info('Never: docker compose down -v (destroys Postgres/Chroma data).');
52
- return 0;
53
- }
54
-
55
- info('Safe update steps (checkout install):');
40
+ info('Safe update steps:');
56
41
  info(' 1. Stop the stack (ragsuite-test stop --repo-root …)');
57
- info(' 2. Update sources (git pull when you have remote access)');
42
+ info(' 2. Update sources (git pull)');
58
43
  info(' 3. Docker: npm run rebuild or docker compose build');
59
44
  info(' 4. Native: re-run backend/scripts/setup.sh if Python deps changed');
60
45
  info(' 5. Start again (ragsuite-test start --repo-root …)');
61
46
  info(' 6. Run doctor after upgrades');
62
47
  info('');
48
+ info('Never: docker compose down -v (destroys Postgres/Chroma data).');
49
+ info('');
63
50
 
64
51
  const gitDir = path.join(repoRoot, '.git');
65
52
  if (!fs.existsSync(gitDir)) {
66
- warn('No .git directory — skip fetch/status (ZIP or packed tree).');
53
+ warn('No .git directory — skip fetch/status. Re-init with --from-git if needed.');
67
54
  return 0;
68
55
  }
69
56
 
package/src/index.js CHANGED
@@ -31,16 +31,20 @@ function globalHelp() {
31
31
  'Global options:',
32
32
  ' --help, -h Show help',
33
33
  ' --repo-root <path> Monorepo / install root',
34
- ' --from-zip <path> init: extract app ZIP',
35
- ' --install-dir <path> init: ZIP target (default ~/ragsuite-test)',
36
- ' --from-release <tag> init: stub (not implemented)',
37
- ' --llm-api-key <key> init: CUSTOM_LLM_INTERNAL_API_KEY (--yes)',
34
+ ' --from-git [url] init: clone this URL (default: public nitsan-ai/RAGSUITE)',
35
+ ' --install-dir <path> init: clone target (default ~/ragsuite-test)',
36
+ ' --llm-api-key <key> init: optional override for CUSTOM_LLM_INTERNAL_API_KEY',
37
+ ' --smtp-host <host> init: optional SMTP override',
38
+ ' --smtp-user <user> init: optional SMTP override',
39
+ ' --smtp-password <pass> init: optional SMTP override',
40
+ ' --email-from <email> init: optional SMTP override',
38
41
  ' --force init: overwrite non-empty install dir / .env',
39
- ' --yes, -y Non-interactive',
40
- ' --mode <docker|native> Override .ragsuite-test/config.json / RAGSUITE_MODE',
42
+ ' --yes, -y Non-interactive (same defaults as plain init)',
43
+ ' --mode <docker|native> Override config (Docker optional)',
41
44
  ' --dry-run Print actions without running (start/stop/doctor/logs)',
42
45
  '',
43
46
  'Env: RAGSUITE_REPO_ROOT or RAGSUITE_INSTALL_DIR',
47
+ ' RAGSUITE_TEST_LLM_API_KEY, RAGSUITE_TEST_SMTP_* , RAGSUITE_TEST_EMAIL_FROM',
44
48
  '',
45
49
  'Run ragsuite-test <command> --help for command details.',
46
50
  );
@@ -88,7 +92,7 @@ async function main(argv = process.argv) {
88
92
  const code = typeof result?.then === 'function' ? await result : result;
89
93
  return typeof code === 'number' ? code : 0;
90
94
  } catch (err) {
91
- if (err && (err.code === 'NOT_REPO_ROOT' || err.code === 'USAGE' || err.code === 'ZIP_SLIP' || err.code === 'INSTALL_DIR_NONEMPTY' || err.code === 'ZIP_INVALID' || err.code === 'ZIP_MISSING' || err.code === 'ENV_EXISTS' || err.code === 'PORT_IN_USE' || err.code === 'LLM_KEY_INVALID')) {
95
+ if (err && (err.code === 'NOT_REPO_ROOT' || err.code === 'USAGE' || err.code === 'INSTALL_DIR_NONEMPTY' || err.code === 'ENV_EXISTS' || err.code === 'ENV_INCOMPLETE' || err.code === 'PORT_IN_USE' || err.code === 'LLM_KEY_INVALID' || err.code === 'SMTP_REQUIRED' || err.code === 'GIT_CLONE_FAILED' || err.code === 'MISSING_GIT')) {
92
96
  error(err.message);
93
97
  return 1;
94
98
  }