@nitsan-ai/ragsuite-test 0.1.1 → 0.1.3

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/src/index.js CHANGED
@@ -31,15 +31,16 @@ function globalHelp() {
31
31
  'Global options:',
32
32
  ' --help, -h Show help',
33
33
  ' --repo-root <path> Monorepo / install root',
34
- ' --from-git [url] init: clone public monorepo',
35
- ' --install-dir <path> init: target for --from-git (default ~/ragsuite-test)',
34
+ ' --from-git [url] init: clone source (default public repo)',
35
+ ' --from-images init: Docker images only (no app source on disk)',
36
+ ' --install-dir <path> init: install target (default ~/ragsuite-test)',
36
37
  ' --llm-api-key <key> init: optional override for CUSTOM_LLM_INTERNAL_API_KEY',
37
38
  ' --smtp-host <host> init: optional SMTP override',
38
39
  ' --smtp-user <user> init: optional SMTP override',
39
40
  ' --smtp-password <pass> init: optional SMTP override',
40
41
  ' --email-from <email> init: optional SMTP override',
41
42
  ' --force init: overwrite non-empty install dir / .env',
42
- ' --yes, -y Non-interactive',
43
+ ' --yes, -y Non-interactive (same defaults as plain init)',
43
44
  ' --mode <docker|native> Override config (Docker optional)',
44
45
  ' --dry-run Print actions without running (start/stop/doctor/logs)',
45
46
  '',
package/src/utils/args.js CHANGED
@@ -11,6 +11,7 @@ function parseArgv(argv) {
11
11
  dryRun: false,
12
12
  mode: null,
13
13
  fromGit: null,
14
+ fromImages: false,
14
15
  fromRelease: null,
15
16
  installDir: null,
16
17
  force: false,
@@ -79,6 +80,11 @@ function parseArgv(argv) {
79
80
  continue;
80
81
  }
81
82
 
83
+ if (t === '--from-images') {
84
+ globals.fromImages = true;
85
+ continue;
86
+ }
87
+
82
88
  if (t === '--from-git' || t.startsWith('--from-git=')) {
83
89
  if (t === '--from-git') {
84
90
  const next = tokens[i + 1];
@@ -21,6 +21,12 @@ function defaultConfig(repoRoot, mode = 'docker') {
21
21
  };
22
22
  }
23
23
 
24
+ function normalizeSource(raw) {
25
+ const s = String(raw || '').trim();
26
+ if (s === 'images' || s === 'git' || s === 'zip' || s === 'checkout') return s;
27
+ return 'checkout';
28
+ }
29
+
24
30
  function readConfig(repoRoot) {
25
31
  const file = configPath(repoRoot);
26
32
  if (!fs.existsSync(file)) {
@@ -31,11 +37,10 @@ function readConfig(repoRoot) {
31
37
  if (!raw || typeof raw !== 'object') {
32
38
  return null;
33
39
  }
34
- const source = raw.source === 'zip' ? 'zip' : 'checkout';
35
40
  return {
36
41
  version: Number(raw.version) || CONFIG_VERSION,
37
42
  mode: raw.mode === 'native' ? 'native' : 'docker',
38
- source,
43
+ source: normalizeSource(raw.source),
39
44
  repoRoot: path.resolve(String(raw.repoRoot || repoRoot)),
40
45
  installDir: path.resolve(String(raw.installDir || raw.repoRoot || repoRoot)),
41
46
  };
@@ -48,10 +53,11 @@ function writeConfig(repoRoot, partial = {}) {
48
53
  const dir = path.join(repoRoot, CONFIG_DIR);
49
54
  fs.mkdirSync(dir, { recursive: true });
50
55
  const existing = readConfig(repoRoot) || defaultConfig(repoRoot);
51
- const source =
52
- partial.source === 'zip' || partial.source === 'checkout'
56
+ const source = normalizeSource(
57
+ partial.source !== undefined && partial.source !== null
53
58
  ? partial.source
54
- : existing.source || 'checkout';
59
+ : existing.source || 'checkout',
60
+ );
55
61
  const next = {
56
62
  ...existing,
57
63
  ...partial,
@@ -3,22 +3,21 @@
3
3
  /**
4
4
  * Distribution modes for the testing CLI.
5
5
  *
6
- * - git-clone: init --from-git (public deploy)
7
- * - local-checkout: --repo-root / env / walk-up
8
- * Registry/GHCR compose remains stubbed.
6
+ * - git-clone: `ragsuite-test init` (source on disk)
7
+ * - images: `ragsuite-test init --from-images` (pull GHCR, no app source)
8
+ * - local-checkout: --repo-root
9
9
  */
10
10
 
11
11
  const MODE_LOCAL = 'local-checkout';
12
12
  const MODE_GIT = 'git-clone';
13
+ const MODE_IMAGES = 'images';
13
14
  const MODE_REGISTRY = 'registry-compose';
14
15
 
15
- function assertSupportedInstall() {
16
- // git + checkout supported
17
- }
16
+ function assertSupportedInstall() {}
18
17
 
19
18
  function resolveComposeBundle() {
20
19
  const err = new Error(
21
- 'Registry/compose (GHCR) install is not available yet. Use: ragsuite-test init --from-git OR --repo-root <checkout>.',
20
+ 'Use: ragsuite-test init --from-images (pull-only) OR ragsuite-test init (git source)',
22
21
  );
23
22
  err.code = 'DIST_NOT_READY';
24
23
  throw err;
@@ -26,16 +25,17 @@ function resolveComposeBundle() {
26
25
 
27
26
  function installHint() {
28
27
  return [
29
- 'Install paths:',
30
- ' Public: ragsuite-test init --from-git [--install-dir ~/ragsuite-test]',
31
- ' Checkout: ragsuite-test init --repo-root <path>',
32
- 'Docker is optional use --mode native (Postgres :5436 + Redis :6382) if you have no Docker.',
28
+ 'Choose an install style:',
29
+ ' Source on disk: ragsuite-test init',
30
+ ' No source: ragsuite-test init --from-images',
31
+ 'Then: ragsuite-test start --repo-root ~/ragsuite-test --detach',
33
32
  ].join('\n');
34
33
  }
35
34
 
36
35
  module.exports = {
37
36
  MODE_LOCAL,
38
37
  MODE_GIT,
38
+ MODE_IMAGES,
39
39
  MODE_REGISTRY,
40
40
  mode: MODE_GIT,
41
41
  assertSupportedInstall,
@@ -0,0 +1,100 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { defaultInstallDir } = require('./git-install');
6
+
7
+ function templatesRoot() {
8
+ return path.join(__dirname, '..', '..', 'templates', 'images');
9
+ }
10
+
11
+ function isDirEmptyOrMissing(dir) {
12
+ if (!fs.existsSync(dir)) return true;
13
+ const items = fs.readdirSync(dir).filter((n) => n !== '.DS_Store');
14
+ return items.length === 0;
15
+ }
16
+
17
+ function copyFile(src, dest) {
18
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
19
+ fs.copyFileSync(src, dest);
20
+ }
21
+
22
+ /**
23
+ * Materialize a pull-only install dir (compose + scripts + .env.example).
24
+ * No monorepo source.
25
+ */
26
+ function materializeImagesInstall(options = {}) {
27
+ const { installDir = defaultInstallDir(), force = false } = options;
28
+ const srcRoot = templatesRoot();
29
+ if (!fs.existsSync(path.join(srcRoot, 'docker-compose.yml'))) {
30
+ const err = new Error(
31
+ `Images templates missing in CLI package (${srcRoot}). Reinstall @nitsan-ai/ragsuite-test.`,
32
+ );
33
+ err.code = 'IMAGES_TEMPLATE_MISSING';
34
+ throw err;
35
+ }
36
+
37
+ const target = path.resolve(installDir);
38
+ if (!isDirEmptyOrMissing(target)) {
39
+ if (!force) {
40
+ const err = new Error(
41
+ `Install directory is not empty: ${target}. Pass --force to replace, or choose another --install-dir.`,
42
+ );
43
+ err.code = 'INSTALL_DIR_NONEMPTY';
44
+ throw err;
45
+ }
46
+ fs.rmSync(target, { recursive: true, force: true });
47
+ }
48
+
49
+ fs.mkdirSync(target, { recursive: true });
50
+ copyFile(
51
+ path.join(srcRoot, 'docker-compose.yml'),
52
+ path.join(target, 'docker-compose.yml'),
53
+ );
54
+ copyFile(
55
+ path.join(srcRoot, '.env.example'),
56
+ path.join(target, '.env.example'),
57
+ );
58
+ copyFile(
59
+ path.join(srcRoot, 'scripts', 'docker-start-images.sh'),
60
+ path.join(target, 'scripts', 'docker-start-images.sh'),
61
+ );
62
+ copyFile(
63
+ path.join(srcRoot, 'scripts', 'docker-stop-images.sh'),
64
+ path.join(target, 'scripts', 'docker-stop-images.sh'),
65
+ );
66
+ copyFile(
67
+ path.join(srcRoot, 'scripts', 'docker-doctor-images.sh'),
68
+ path.join(target, 'scripts', 'docker-doctor-images.sh'),
69
+ );
70
+ try {
71
+ fs.chmodSync(path.join(target, 'scripts', 'docker-start-images.sh'), 0o755);
72
+ fs.chmodSync(path.join(target, 'scripts', 'docker-stop-images.sh'), 0o755);
73
+ fs.chmodSync(path.join(target, 'scripts', 'docker-doctor-images.sh'), 0o755);
74
+ } catch {
75
+ /* windows */
76
+ }
77
+
78
+ // Minimal package.json so tooling can identify the install dir
79
+ fs.writeFileSync(
80
+ path.join(target, 'package.json'),
81
+ `${JSON.stringify(
82
+ {
83
+ name: 'ragsuite-images-install',
84
+ private: true,
85
+ description: 'RAGSuite pull-only images install (no source tree)',
86
+ },
87
+ null,
88
+ 2,
89
+ )}\n`,
90
+ 'utf8',
91
+ );
92
+
93
+ return { repoRoot: target };
94
+ }
95
+
96
+ module.exports = {
97
+ templatesRoot,
98
+ materializeImagesInstall,
99
+ defaultInstallDir,
100
+ };
@@ -4,14 +4,35 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { readConfig, CONFIG_DIR } = require('./config');
6
6
 
7
- function looksLikeRepoRoot(dir) {
7
+ function looksLikeGitCheckout(dir) {
8
8
  const start = path.join(dir, 'scripts', 'docker-start.sh');
9
9
  const pkg = path.join(dir, 'package.json');
10
10
  return fs.existsSync(start) && fs.existsSync(pkg);
11
11
  }
12
12
 
13
+ function looksLikeImagesInstall(dir) {
14
+ const compose = path.join(dir, 'docker-compose.yml');
15
+ const start = path.join(dir, 'scripts', 'docker-start-images.sh');
16
+ if (!fs.existsSync(compose) || !fs.existsSync(start)) return false;
17
+ const cfgPath = path.join(dir, CONFIG_DIR, 'config.json');
18
+ if (fs.existsSync(cfgPath)) {
19
+ try {
20
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
21
+ if (cfg.source === 'images') return true;
22
+ } catch {
23
+ /* fall through */
24
+ }
25
+ }
26
+ // Before config is written, or force-detect by scripts present without docker-start.sh
27
+ return !fs.existsSync(path.join(dir, 'scripts', 'docker-start.sh'));
28
+ }
29
+
30
+ function looksLikeRepoRoot(dir) {
31
+ return looksLikeGitCheckout(dir) || looksLikeImagesInstall(dir);
32
+ }
33
+
13
34
  function looksLikeFullBundleRoot(dir) {
14
- if (!looksLikeRepoRoot(dir)) return false;
35
+ if (!looksLikeGitCheckout(dir)) return false;
15
36
  const compose = path.join(dir, 'docker-compose.yml');
16
37
  return fs.existsSync(compose);
17
38
  }
@@ -42,10 +63,6 @@ function envRepoRoot(env = process.env) {
42
63
  return null;
43
64
  }
44
65
 
45
- /**
46
- * Resolve monorepo root:
47
- * --repo-root → RAGSUITE_REPO_ROOT / RAGSUITE_INSTALL_DIR → walk-up / config.
48
- */
49
66
  function resolveRepoRoot({
50
67
  cwd = process.cwd(),
51
68
  repoRootFlag,
@@ -56,7 +73,7 @@ function resolveRepoRoot({
56
73
  const root = path.resolve(repoRootFlag);
57
74
  if (!looksLikeRepoRoot(root)) {
58
75
  const err = new Error(
59
- `Not a RAGSuite repo root (missing scripts/docker-start.sh): ${root}`,
76
+ `Not a RAGSuite install root: ${root} (need git checkout or images install)`,
60
77
  );
61
78
  err.code = 'NOT_REPO_ROOT';
62
79
  throw err;
@@ -91,7 +108,7 @@ function resolveRepoRoot({
91
108
  }
92
109
 
93
110
  const err = new Error(
94
- 'Could not find RAGSuite repo root. Pass --repo-root <path>, set RAGSUITE_REPO_ROOT, or run: ragsuite-test init --from-git',
111
+ 'Could not find RAGSuite install. Run: ragsuite-test init OR ragsuite-test init --from-images',
95
112
  );
96
113
  err.code = 'NOT_REPO_ROOT';
97
114
  throw err;
@@ -107,13 +124,20 @@ function ensureRagsuiteDir(repoRoot) {
107
124
  return dir;
108
125
  }
109
126
 
110
- /** Native PID/logs stay under .ragsuite/native (Phase 4 scripts). */
111
127
  function nativeLogDir(repoRoot) {
112
128
  return path.join(repoRoot, '.ragsuite', 'native');
113
129
  }
114
130
 
131
+ function isImagesInstall(repoRoot) {
132
+ const cfg = readConfig(repoRoot);
133
+ if (cfg && cfg.source === 'images') return true;
134
+ return looksLikeImagesInstall(repoRoot) && !looksLikeGitCheckout(repoRoot);
135
+ }
136
+
115
137
  module.exports = {
116
138
  looksLikeRepoRoot,
139
+ looksLikeGitCheckout,
140
+ looksLikeImagesInstall,
117
141
  looksLikeFullBundleRoot,
118
142
  walkUpForRepoRoot,
119
143
  envRepoRoot,
@@ -121,4 +145,5 @@ module.exports = {
121
145
  scriptPath,
122
146
  ensureRagsuiteDir,
123
147
  nativeLogDir,
148
+ isImagesInstall,
124
149
  };
@@ -0,0 +1,76 @@
1
+ # RAGSuite Server — copy to .env: cp .env.example .env
2
+ # Never commit .env.
3
+ #
4
+ # Used by docker compose (backend + worker env_file) and native-start (host process).
5
+ # Compose injects DATABASE_URL, REDIS_HOST/PORT, CHROMA_* for containers — those override
6
+ # anything duplicated here when using Docker mode.
7
+ # For native mode (npm run start:native), set the Native block below (or rely on script defaults).
8
+
9
+ # --- Required secrets ---
10
+ # JWT is regenerated by `ragsuite-test init` (do not put real secrets here — use .env).
11
+ JWT_SECRET_KEY=change-me-use-a-long-random-secret-in-production
12
+ # First-run default (not a cloud vendor key). Override in .env anytime.
13
+ CUSTOM_LLM_INTERNAL_API_KEY=ragsuite-default-llm-internal-key
14
+
15
+ # --- Transactional email (invites, 2FA, password reset) ---
16
+ # Template only — real SMTP_USER / SMTP_PASSWORD / EMAIL_FROM belong in .env (gitignored).
17
+ # `ragsuite-test init` copies this file to .env and does NOT ask for SMTP.
18
+ SMTP_HOST=smtp.gmail.com
19
+ SMTP_PORT=587
20
+ SMTP_USER=your-smtp-user@example.com
21
+ SMTP_PASSWORD=change-me-app-password-or-smtp-secret
22
+ SMTP_USE_TLS=true
23
+ EMAIL_FROM=your-smtp-user@example.com
24
+
25
+ # --- Frontend Docker build (baked into env.json at image build) ---
26
+ FRONTEND_API_URL=http://localhost:9090
27
+ # Optional: public origin that serves /widget assets (defaults to FRONTEND_API_URL in Dockerfile)
28
+ # WIDGET_ASSET_BASE=
29
+
30
+ # --- App policy ---
31
+ HF_HUB_OFFLINE=1
32
+ SSO_ENABLED=false
33
+ SSO_REQUIRE_REDIS=true
34
+
35
+ # --- RAG / ingest (optional) ---
36
+ ENABLE_ASYNC_DOCUMENT_INGEST=true
37
+ EMBEDDING_PREFERRED_SOURCE=chat
38
+ COMPARE_MODEL_CONFIG_SOURCE=chat
39
+
40
+ # --- Optional: Docker host-port overrides (defaults match npm start) ---
41
+ # API_PORT=9090
42
+ # WEB_PORT=9091
43
+ # POSTGRES_HOST_PORT=5436
44
+ # REDIS_HOST_PORT=6382
45
+
46
+ # --- Optional: Compose Postgres credentials (defaults: ragsuite / ragsuite / ragsuite_v3) ---
47
+ # POSTGRES_USER=ragsuite
48
+ # POSTGRES_PASSWORD=ragsuite
49
+ # POSTGRES_DB=ragsuite_v3
50
+
51
+ # --- Optional: App URL / CORS (compose sets local defaults for Docker) ---
52
+ # CORS_ORIGINS=http://localhost:9091
53
+ # FRONTEND_BASE_URL=http://localhost:9091
54
+ # PUBLIC_API_BASE_URL=http://localhost:9090/api/v1
55
+ # SSO_CALLBACK_BASE_URL=http://localhost:9090/api/v1/auth/sso/callback
56
+
57
+ # --- Native mode (npm run start:native) — host Postgres/Redis/Chroma ---
58
+ # Do not use Docker service DNS names (postgres/redis/chromadb) on the host.
59
+ # DATABASE_URL=postgresql://ragsuite:ragsuite@localhost:5436/ragsuite_v3
60
+ # REDIS_HOST=localhost
61
+ # REDIS_PORT=6382
62
+ # CHROMA_MODE=http
63
+ # CHROMA_HOST=127.0.0.1
64
+ # CHROMA_PORT=8004
65
+ # CHROMA_PERSIST_PATH=data/chroma_db
66
+ # CORS_ORIGINS=http://localhost:9191,http://127.0.0.1:9191,http://localhost:9091
67
+ # FRONTEND_BASE_URL=http://localhost:9191
68
+ # PUBLIC_API_BASE_URL=http://localhost:9090/api/v1
69
+ # EXPO_DEV_SERVER_PORT=9191
70
+ # RAGSUITE_MODE=native
71
+
72
+ # Advanced tuning: backend/docs/backend/configuration.md
73
+
74
+ # --- Images install (init --from-images) ---
75
+ # IMAGE_REGISTRY=ghcr.io/nitsan-ai
76
+ # IMAGE_TAG=latest
@@ -0,0 +1,165 @@
1
+ # Pull-only stack (no monorepo source / no local build).
2
+ # Used by: ragsuite-test init --from-images
3
+ # Images: ghcr.io/nitsan-ai/ragsuite-backend and ragsuite-frontend
4
+ #
5
+ # Override tag: IMAGE_TAG=1.0.0 (default: latest)
6
+ # Registry: IMAGE_REGISTRY=ghcr.io/nitsan-ai (default)
7
+
8
+ name: ragsuite-server
9
+
10
+ networks:
11
+ ragsuite-server-net:
12
+ name: ragsuite-server-net
13
+
14
+ x-backend-image: &backend_image
15
+ image: ${IMAGE_REGISTRY:-ghcr.io/nitsan-ai}/ragsuite-backend:${IMAGE_TAG:-latest}
16
+ pull_policy: ${PULL_POLICY:-always}
17
+
18
+ services:
19
+ postgres:
20
+ image: postgres:15-alpine
21
+ environment:
22
+ POSTGRES_USER: ${POSTGRES_USER:-ragsuite}
23
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ragsuite}
24
+ POSTGRES_DB: ${POSTGRES_DB:-ragsuite_v3}
25
+ ports:
26
+ - "${POSTGRES_HOST_PORT:-5436}:5432"
27
+ volumes:
28
+ - postgres_data:/var/lib/postgresql/data
29
+ healthcheck:
30
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
31
+ interval: 5s
32
+ timeout: 5s
33
+ retries: 10
34
+ networks:
35
+ - ragsuite-server-net
36
+ restart: unless-stopped
37
+
38
+ redis:
39
+ image: redis:7-alpine
40
+ ports:
41
+ - "${REDIS_HOST_PORT:-6382}:6379"
42
+ healthcheck:
43
+ test: ["CMD", "redis-cli", "ping"]
44
+ interval: 5s
45
+ timeout: 3s
46
+ retries: 10
47
+ networks:
48
+ - ragsuite-server-net
49
+ restart: unless-stopped
50
+
51
+ chromadb:
52
+ <<: *backend_image
53
+ entrypoint: []
54
+ command:
55
+ - chroma
56
+ - run
57
+ - --host
58
+ - "0.0.0.0"
59
+ - --port
60
+ - "8000"
61
+ - --path
62
+ - /data/chroma
63
+ volumes:
64
+ - chroma_data:/data/chroma
65
+ healthcheck:
66
+ test: ["CMD", "curl", "-sf", "http://localhost:8000/api/v2/heartbeat"]
67
+ interval: 10s
68
+ timeout: 5s
69
+ retries: 10
70
+ start_period: 30s
71
+ networks:
72
+ - ragsuite-server-net
73
+ restart: unless-stopped
74
+
75
+ backend:
76
+ <<: *backend_image
77
+ env_file:
78
+ - .env
79
+ environment:
80
+ DEBUG: "True"
81
+ CORS_ORIGINS: "http://localhost:${WEB_PORT:-9091}"
82
+ ENABLE_SCHEDULER: "true"
83
+ ENABLE_DURABLE_JOBS: "true"
84
+ DATABASE_URL: postgresql://${POSTGRES_USER:-ragsuite}:${POSTGRES_PASSWORD:-ragsuite}@postgres:5432/${POSTGRES_DB:-ragsuite_v3}
85
+ REDIS_HOST: redis
86
+ REDIS_PORT: "6379"
87
+ CHROMA_MODE: http
88
+ CHROMA_HOST: chromadb
89
+ CHROMA_PORT: "8000"
90
+ CHROMA_PERSIST_PATH: /data/chroma
91
+ DOCUMENT_STAGING_DIR: /data/staging
92
+ RUN_INLINE_WORKER: "false"
93
+ WEB_CONCURRENCY: "2"
94
+ FRONTEND_BASE_URL: http://localhost:${WEB_PORT:-9091}
95
+ PUBLIC_API_BASE_URL: http://localhost:${API_PORT:-9090}/api/v1
96
+ SSO_CALLBACK_BASE_URL: http://localhost:${API_PORT:-9090}/api/v1/auth/sso/callback
97
+ ports:
98
+ - "${API_PORT:-9090}:8000"
99
+ depends_on:
100
+ postgres:
101
+ condition: service_healthy
102
+ redis:
103
+ condition: service_healthy
104
+ chromadb:
105
+ condition: service_healthy
106
+ healthcheck:
107
+ test: ["CMD", "curl", "-sf", "http://localhost:8000/api/v1/health/ready"]
108
+ interval: 30s
109
+ timeout: 10s
110
+ retries: 5
111
+ start_period: 120s
112
+ volumes:
113
+ - staging_data:/data/staging
114
+ networks:
115
+ - ragsuite-server-net
116
+ restart: unless-stopped
117
+
118
+ worker:
119
+ <<: *backend_image
120
+ entrypoint: []
121
+ command: ["python", "-m", "app.worker"]
122
+ env_file:
123
+ - .env
124
+ environment:
125
+ DEBUG: "False"
126
+ CORS_ORIGINS: "http://localhost:${WEB_PORT:-9091}"
127
+ ENABLE_SCHEDULER: "true"
128
+ ENABLE_DURABLE_JOBS: "true"
129
+ DATABASE_URL: postgresql://${POSTGRES_USER:-ragsuite}:${POSTGRES_PASSWORD:-ragsuite}@postgres:5432/${POSTGRES_DB:-ragsuite_v3}
130
+ REDIS_HOST: redis
131
+ REDIS_PORT: "6379"
132
+ CHROMA_MODE: http
133
+ CHROMA_HOST: chromadb
134
+ CHROMA_PORT: "8000"
135
+ CHROMA_PERSIST_PATH: /data/chroma
136
+ DOCUMENT_STAGING_DIR: /data/staging
137
+ RUN_INLINE_WORKER: "false"
138
+ depends_on:
139
+ backend:
140
+ condition: service_healthy
141
+ volumes:
142
+ - staging_data:/data/staging
143
+ networks:
144
+ - ragsuite-server-net
145
+ restart: unless-stopped
146
+
147
+ frontend:
148
+ image: ${IMAGE_REGISTRY:-ghcr.io/nitsan-ai}/ragsuite-frontend:${IMAGE_TAG:-latest}
149
+ pull_policy: ${PULL_POLICY:-always}
150
+ ports:
151
+ - "${WEB_PORT:-9091}:80"
152
+ healthcheck:
153
+ test: ["CMD", "wget", "-q", "--spider", "http://localhost/healthz"]
154
+ interval: 15s
155
+ timeout: 5s
156
+ retries: 5
157
+ start_period: 10s
158
+ networks:
159
+ - ragsuite-server-net
160
+ restart: unless-stopped
161
+
162
+ volumes:
163
+ postgres_data:
164
+ chroma_data:
165
+ staging_data:
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # Lightweight doctor for images-only installs.
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
7
+ cd "$ROOT"
8
+
9
+ ok=0
10
+ fail=0
11
+
12
+ check() {
13
+ local label="$1"
14
+ shift
15
+ if "$@"; then
16
+ echo " ✓ $label"
17
+ ok=$((ok + 1))
18
+ else
19
+ echo " ✗ $label"
20
+ fail=$((fail + 1))
21
+ fi
22
+ }
23
+
24
+ echo "Doctor (images install):"
25
+ check "docker CLI" command -v docker >/dev/null
26
+ check "docker daemon" docker info >/dev/null 2>&1
27
+ check "docker compose" docker compose version >/dev/null 2>&1
28
+ check ".env present" test -f .env
29
+ check "compose file" test -f docker-compose.yml
30
+
31
+ API_PORT="${API_PORT:-9090}"
32
+ if curl -sf "http://127.0.0.1:${API_PORT}/api/v1/health/ping" >/dev/null 2>&1 \
33
+ || curl -sf "http://127.0.0.1:${API_PORT}/api/v1/crawl/auth/public-config" >/dev/null 2>&1; then
34
+ echo " ✓ API reachable :${API_PORT}"
35
+ ok=$((ok + 1))
36
+ else
37
+ echo " ○ API not up yet on :${API_PORT} (start the stack if needed)"
38
+ fi
39
+
40
+ echo "Summary: ${ok} ok, ${fail} failed"
41
+ if [[ "$fail" -gt 0 ]]; then
42
+ echo "How to fix:"
43
+ echo " → Install Docker Desktop: https://www.docker.com/products/docker-desktop/"
44
+ echo " → Publish images: GitHub Actions → Publish images (GHCR)"
45
+ echo " → Start: ragsuite-test start --repo-root $ROOT --detach"
46
+ exit 1
47
+ fi
48
+ exit 0