sim-setup 1.0.24-preview.146.1 → 1.0.24-preview.149.1

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.
@@ -15,7 +15,7 @@ services:
15
15
  memory: 8G
16
16
  environment:
17
17
  - NODE_ENV=production
18
- - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
18
+ - 'DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?is required. For a new install set it in .env to the output of: openssl rand -hex 24. An install created before it was required uses the password postgres - set POSTGRES_PASSWORD=postgres to keep its data.}@db:5432/${POSTGRES_DB:-simstudio}'
19
19
  - BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
20
20
  - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
21
21
  # TRUSTED_ORIGINS: comma-separated public origins to trust for auth in
@@ -85,7 +85,7 @@ services:
85
85
  memory: 1G
86
86
  environment:
87
87
  - NODE_ENV=production
88
- - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
88
+ - 'DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?is required. For a new install set it in .env to the output of: openssl rand -hex 24. An install created before it was required uses the password postgres - set POSTGRES_PASSWORD=postgres to keep its data.}@db:5432/${POSTGRES_DB:-simstudio}'
89
89
  - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
90
90
  # Derived from NEXT_PUBLIC_APP_URL, matching the simstudio service — a
91
91
  # single public-origin variable keeps the two from disagreeing.
@@ -109,7 +109,7 @@ services:
109
109
  image: ghcr.io/simstudioai/migrations:${SIM_VERSION:-latest}
110
110
  working_dir: /app/packages/db
111
111
  environment:
112
- - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
112
+ - 'DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?is required. For a new install set it in .env to the output of: openssl rand -hex 24. An install created before it was required uses the password postgres - set POSTGRES_PASSWORD=postgres to keep its data.}@db:5432/${POSTGRES_DB:-simstudio}'
113
113
  depends_on:
114
114
  db:
115
115
  condition: service_healthy
@@ -151,14 +151,21 @@ services:
151
151
  simstudio:
152
152
  condition: service_healthy
153
153
 
154
+ # Not published to the host: app, realtime and migrations reach it over the
155
+ # Compose network as db:5432. A host `ports:` mapping binds every interface and
156
+ # Docker's own iptables rules bypass host firewalls, so it would put the
157
+ # database on the network. For host access (psql, a desktop client), use
158
+ # `docker compose exec db psql -U postgres simstudio`, or pass a second -f
159
+ # file that publishes '127.0.0.1:5432:5432' (-f disables override files).
154
160
  db:
155
161
  image: pgvector/pgvector:pg17
156
162
  restart: unless-stopped
157
- ports:
158
- - '${POSTGRES_PORT:-5432}:5432'
159
163
  environment:
160
164
  - POSTGRES_USER=${POSTGRES_USER:-postgres}
161
- - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
165
+ # Postgres applies this only when it creates the data volume; later
166
+ # changes are ignored. An install created before this was required got
167
+ # the password `postgres` — set exactly that to keep it working.
168
+ - 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?is required. For a new install set it in .env to the output of: openssl rand -hex 24. An install created before it was required uses the password postgres - set POSTGRES_PASSWORD=postgres to keep its data.}'
162
169
  - POSTGRES_DB=${POSTGRES_DB:-simstudio}
163
170
  volumes:
164
171
  - postgres_data:/var/lib/postgresql/data
package/dist/index.js CHANGED
@@ -11415,6 +11415,76 @@ var init_compose_asset = __esm(() => {
11415
11415
  init_context();
11416
11416
  });
11417
11417
 
11418
+ // src/compose-database.ts
11419
+ import { spawnSync as spawnSync5 } from "node:child_process";
11420
+ import { readFileSync as readFileSync7 } from "node:fs";
11421
+ function composeFileRequiresPostgresPassword(composeFile) {
11422
+ return readFileSync7(composeFile, "utf8").includes("${POSTGRES_PASSWORD:?");
11423
+ }
11424
+ function choosePostgresPassword(envFileValue, project, { shell = process.env, hasDatabaseVolume = composeDatabaseVolumeExists } = {}) {
11425
+ const shellValue = shell.POSTGRES_PASSWORD;
11426
+ if (shellValue === "") {
11427
+ throw new SetupError("POSTGRES_PASSWORD is exported but empty, and Compose uses it over .env.", ["unset it (unset POSTGRES_PASSWORD) so the value in .env applies"]);
11428
+ }
11429
+ if (shellValue !== undefined || envFileValue)
11430
+ return null;
11431
+ return hasDatabaseVolume(project) ? { value: LEGACY_POSTGRES_PASSWORD, source: "legacy" } : { value: generateSecret(), source: "generated" };
11432
+ }
11433
+ function composeDatabaseVolumeExists(project) {
11434
+ const result = spawnSync5("docker", [
11435
+ "volume",
11436
+ "ls",
11437
+ "-q",
11438
+ "--filter",
11439
+ `label=com.docker.compose.project=${project}`,
11440
+ "--filter",
11441
+ `label=com.docker.compose.volume=${POSTGRES_VOLUME}`
11442
+ ], { encoding: "utf8" });
11443
+ if (result.status !== 0) {
11444
+ throw new SetupError(`could not check for an existing database volume: ${result.stderr.trim() || result.stdout.trim()}`);
11445
+ }
11446
+ return result.stdout.trim().length > 0;
11447
+ }
11448
+ function composeProjectName(composeFile, cwd) {
11449
+ const result = spawnSync5("docker", ["compose", "-f", composeFile, "config", "--no-interpolate", "--format", "json"], { cwd, encoding: "utf8" });
11450
+ const name = result.status === 0 ? parseProjectName(result.stdout) : null;
11451
+ if (name)
11452
+ return name;
11453
+ throw new SetupError(`could not resolve the Compose project name for ${composeFile}: ${result.stderr.trim() || "no name in docker compose config"}`);
11454
+ }
11455
+ function parseProjectName(stdout) {
11456
+ try {
11457
+ const { name } = JSON.parse(stdout);
11458
+ return typeof name === "string" && name ? name : null;
11459
+ } catch {
11460
+ return null;
11461
+ }
11462
+ }
11463
+ function reportPostgresPasswordChoice(choice, { compose, user, envPath }) {
11464
+ if (choice.source === "generated") {
11465
+ log2.step(`Generated POSTGRES_PASSWORD in ${envPath}`);
11466
+ return;
11467
+ }
11468
+ note2([
11469
+ `This database was created with the password "${LEGACY_POSTGRES_PASSWORD}", so ${envPath}`,
11470
+ `now sets POSTGRES_PASSWORD=${LEGACY_POSTGRES_PASSWORD} to keep it working. The database is not`,
11471
+ "published to the host, so only the containers in this stack can reach it. To rotate it:",
11472
+ ` ${compose} exec db psql -U ${user} -c "ALTER ROLE CURRENT_USER PASSWORD '<new password>'"`,
11473
+ " then set POSTGRES_PASSWORD=<new password> in .env and run: npx sim-setup start"
11474
+ ].join(`
11475
+ `), "Database password");
11476
+ }
11477
+ function postgresUser(envFileValue) {
11478
+ const shellUser = process.env.POSTGRES_USER;
11479
+ return shellUser === undefined ? envFileValue || "postgres" : shellUser || "postgres";
11480
+ }
11481
+ var LEGACY_POSTGRES_PASSWORD = "postgres", POSTGRES_VOLUME = "postgres_data";
11482
+ var init_compose_database = __esm(() => {
11483
+ init_env_files();
11484
+ init_errors2();
11485
+ init_prompter();
11486
+ });
11487
+
11418
11488
  // src/compose-project.ts
11419
11489
  import { createHash as createHash3 } from "node:crypto";
11420
11490
  import path7 from "node:path";
@@ -11432,12 +11502,12 @@ function legacyComposeProjectName(root) {
11432
11502
  var init_compose_project = () => {};
11433
11503
 
11434
11504
  // src/docker.ts
11435
- import { spawnSync as spawnSync5 } from "node:child_process";
11505
+ import { spawnSync as spawnSync6 } from "node:child_process";
11436
11506
  import { existsSync as existsSync7 } from "node:fs";
11437
11507
  import { homedir } from "node:os";
11438
11508
  import { join } from "node:path";
11439
11509
  function daemonUp() {
11440
- return spawnSync5("docker", ["info"], { stdio: "ignore" }).status === 0;
11510
+ return spawnSync6("docker", ["info"], { stdio: "ignore" }).status === 0;
11441
11511
  }
11442
11512
  function installed() {
11443
11513
  return executableExists("docker");
@@ -11446,7 +11516,7 @@ function orbstackSelected() {
11446
11516
  const host = process.env.DOCKER_HOST;
11447
11517
  if (host)
11448
11518
  return host.includes(".orbstack/");
11449
- const result = spawnSync5("docker", ["context", "show"], { encoding: "utf8" });
11519
+ const result = spawnSync6("docker", ["context", "show"], { encoding: "utf8" });
11450
11520
  return result.status === 0 && result.stdout.trim() === "orbstack";
11451
11521
  }
11452
11522
  function appInstalled(app) {
@@ -11459,7 +11529,7 @@ function macDockerApp() {
11459
11529
  return { app: orbstackOnly ? ORBSTACK_APP : DOCKER_DESKTOP_APP, explicit: false };
11460
11530
  }
11461
11531
  function openApp(app) {
11462
- return spawnSync5("open", ["-a", app.name], { stdio: "ignore" }).status === 0;
11532
+ return spawnSync6("open", ["-a", app.name], { stdio: "ignore" }).status === 0;
11463
11533
  }
11464
11534
  function startDockerApp({ app, explicit }) {
11465
11535
  if (openApp(app))
@@ -11544,9 +11614,9 @@ var init_docker = __esm(() => {
11544
11614
  });
11545
11615
 
11546
11616
  // src/modes/k8s.ts
11547
- import { spawn, spawnSync as spawnSync6 } from "node:child_process";
11617
+ import { spawn, spawnSync as spawnSync7 } from "node:child_process";
11548
11618
  function run(command, args, failMessage, input) {
11549
- const result = spawnSync6(command, args, { encoding: "utf8", input, cwd: ROOT });
11619
+ const result = spawnSync7(command, args, { encoding: "utf8", input, cwd: ROOT });
11550
11620
  if (result.status !== 0) {
11551
11621
  throw new Error(`${failMessage}: ${result.stderr.trim() || result.stdout.trim()}`);
11552
11622
  }
@@ -11560,12 +11630,12 @@ function isLocalKubeContext(context) {
11560
11630
  return server !== null && LOCAL_SERVER_HOSTS.has(server);
11561
11631
  }
11562
11632
  function clusterReachable(context) {
11563
- return spawnSync6("kubectl", ["cluster-info", "--context", context, "--request-timeout=5s"], {
11633
+ return spawnSync7("kubectl", ["cluster-info", "--context", context, "--request-timeout=5s"], {
11564
11634
  stdio: "ignore"
11565
11635
  }).status === 0;
11566
11636
  }
11567
11637
  function contextServerHost(context) {
11568
- const result = spawnSync6("kubectl", [
11638
+ const result = spawnSync7("kubectl", [
11569
11639
  "config",
11570
11640
  "view",
11571
11641
  "--minify",
@@ -11638,7 +11708,7 @@ async function ensureLocalContext(detection) {
11638
11708
  const nodes = run("kind", ["get", "nodes", "--name", "sim"], "kind get nodes failed").trim().split(`
11639
11709
  `).filter(Boolean);
11640
11710
  for (const node of nodes)
11641
- spawnSync6("docker", ["start", node], { stdio: "ignore" });
11711
+ spawnSync7("docker", ["start", node], { stdio: "ignore" });
11642
11712
  const up = await waitFor(() => Promise.resolve(clusterReachable("kind-sim")), 60000, 2000);
11643
11713
  if (!up) {
11644
11714
  spin.stop(`${glyph.fail} kind cluster "sim" would not start`);
@@ -11658,7 +11728,7 @@ async function ensureLocalContext(detection) {
11658
11728
  return "kind-sim";
11659
11729
  }
11660
11730
  function podProgress(context) {
11661
- const result = spawnSync6("kubectl", [
11731
+ const result = spawnSync7("kubectl", [
11662
11732
  "get",
11663
11733
  "pods",
11664
11734
  "--context",
@@ -11728,7 +11798,7 @@ async function helmInstall(args, input, context, spin) {
11728
11798
  }
11729
11799
  function existingReleaseValues(context) {
11730
11800
  const scope = ["--kube-context", context, "-n", NAMESPACE];
11731
- const status = spawnSync6("helm", ["status", RELEASE, ...scope], { stdio: "ignore" });
11801
+ const status = spawnSync7("helm", ["status", RELEASE, ...scope], { stdio: "ignore" });
11732
11802
  if (status.status !== 0)
11733
11803
  return null;
11734
11804
  return JSON.parse(run("helm", ["get", "values", RELEASE, ...scope, "-o", "json"], "helm get values failed"));
@@ -11825,7 +11895,7 @@ async function runK8sMode(detection) {
11825
11895
  spin.stop("Release deployed, all pods ready");
11826
11896
  const testSpin = spinner2();
11827
11897
  testSpin.start("Running helm test…");
11828
- const test = spawnSync6("helm", ["test", RELEASE, "--kube-context", context, "-n", NAMESPACE], {
11898
+ const test = spawnSync7("helm", ["test", RELEASE, "--kube-context", context, "-n", NAMESPACE], {
11829
11899
  encoding: "utf8",
11830
11900
  cwd: ROOT
11831
11901
  });
@@ -11876,7 +11946,7 @@ ${forwardCommands(context).map((command) => ` ${command}`).join(`
11876
11946
  log2.warn(`The realtime forward (:3002) stopped — the editor's socket will not connect. ${realtimeError.trim() || `Check that :3002 is free and svc/${RELEASE}-realtime exists.`}`);
11877
11947
  });
11878
11948
  log2.step(`Forwarding ${APP_URL} (app) and :3002 (realtime) — Ctrl-C to stop`);
11879
- spawnSync6("kubectl", [...scope, "port-forward", `svc/${RELEASE}-app`, "3000:3000"], {
11949
+ spawnSync7("kubectl", [...scope, "port-forward", `svc/${RELEASE}-app`, "3000:3000"], {
11880
11950
  stdio: "inherit"
11881
11951
  });
11882
11952
  stopping = true;
@@ -11907,8 +11977,8 @@ var init_k8s = __esm(() => {
11907
11977
  });
11908
11978
 
11909
11979
  // src/lifecycle.ts
11910
- import { spawnSync as spawnSync7 } from "node:child_process";
11911
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
11980
+ import { spawnSync as spawnSync8 } from "node:child_process";
11981
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
11912
11982
  import path8 from "node:path";
11913
11983
  function shq2(value) {
11914
11984
  if (/^[A-Za-z0-9._/-]+$/.test(value))
@@ -11916,17 +11986,17 @@ function shq2(value) {
11916
11986
  return `'${value.replace(/'/g, `'\\''`)}'`;
11917
11987
  }
11918
11988
  function dockerReachable() {
11919
- return spawnSync7("docker", ["info"], { stdio: "ignore" }).status === 0;
11989
+ return spawnSync8("docker", ["info"], { stdio: "ignore" }).status === 0;
11920
11990
  }
11921
11991
  function dockerText(args, cwd = ROOT) {
11922
- const result = spawnSync7("docker", args, { cwd, encoding: "utf8" });
11992
+ const result = spawnSync8("docker", args, { cwd, encoding: "utf8" });
11923
11993
  return result.status === 0 ? result.stdout.trim() : null;
11924
11994
  }
11925
11995
  function dockerInherit(args, cwd = ROOT) {
11926
- return spawnSync7("docker", args, { cwd, stdio: "inherit" }).status ?? 1;
11996
+ return spawnSync8("docker", args, { cwd, stdio: "inherit" }).status ?? 1;
11927
11997
  }
11928
11998
  function dockerRun(args, failMessage, cwd = ROOT) {
11929
- const result = spawnSync7("docker", args, { cwd, encoding: "utf8" });
11999
+ const result = spawnSync8("docker", args, { cwd, encoding: "utf8" });
11930
12000
  if (result.status !== 0) {
11931
12001
  throw new SetupError(`${failMessage}: ${result.stderr.trim() || result.stdout.trim()}`);
11932
12002
  }
@@ -11935,7 +12005,7 @@ function isSimComposeFile(file) {
11935
12005
  if (!COMPOSE_FILES2.includes(path8.basename(file)))
11936
12006
  return false;
11937
12007
  try {
11938
- const contents = readFileSync7(file, "utf8");
12008
+ const contents = readFileSync8(file, "utf8");
11939
12009
  return SIM_COMPOSE_MARKERS2.some((marker) => contents.includes(marker));
11940
12010
  } catch {
11941
12011
  return false;
@@ -11972,7 +12042,7 @@ function composeInstallFromDirectory(root, activeInstalls) {
11972
12042
  if (activeInstalls.some((install) => path8.resolve(install.file) === path8.resolve(file)))
11973
12043
  return null;
11974
12044
  const envFile = path8.join(root, ".env");
11975
- const configuredProject = existsSync8(envFile) ? parseEnv(readFileSync7(envFile, "utf8")).get("COMPOSE_PROJECT_NAME") : undefined;
12045
+ const configuredProject = existsSync8(envFile) ? parseEnv(readFileSync8(envFile, "utf8")).get("COMPOSE_PROJECT_NAME") : undefined;
11976
12046
  return {
11977
12047
  kind: "compose",
11978
12048
  file,
@@ -11983,6 +12053,22 @@ function composeInstallFromDirectory(root, activeInstalls) {
11983
12053
  function composeArgs(install, ...verb) {
11984
12054
  return ["compose", "-p", install.project, "-f", install.file, ...verb];
11985
12055
  }
12056
+ function ensureComposePostgresPassword(install) {
12057
+ if (!composeFileRequiresPostgresPassword(install.file))
12058
+ return;
12059
+ const envPath = path8.join(install.dir, ".env");
12060
+ const content = existsSync8(envPath) ? readFileSync8(envPath, "utf8") : "";
12061
+ const vars = parseEnv(content);
12062
+ const choice = choosePostgresPassword(vars.get("POSTGRES_PASSWORD"), install.project);
12063
+ if (!choice)
12064
+ return;
12065
+ writeEnvFile(envPath, upsertEnv(content, "POSTGRES_PASSWORD", choice.value));
12066
+ reportPostgresPasswordChoice(choice, {
12067
+ compose: `docker compose -p ${install.project} -f ${install.file}`,
12068
+ user: postgresUser(vars.get("POSTGRES_USER")),
12069
+ envPath
12070
+ });
12071
+ }
11986
12072
  function devInstall(detection) {
11987
12073
  const postgres = detection.dbContainer?.managed ?? false;
11988
12074
  const redis = detection.redisContainer?.managed ?? false;
@@ -11995,7 +12081,7 @@ function k8sInstall(detection) {
11995
12081
  const context = detection.kubeContext;
11996
12082
  if (!context)
11997
12083
  return null;
11998
- const status = spawnSync7("helm", ["status", K8S_RELEASE, "--kube-context", context, "-n", K8S_NAMESPACE], { stdio: "ignore" });
12084
+ const status = spawnSync8("helm", ["status", K8S_RELEASE, "--kube-context", context, "-n", K8S_NAMESPACE], { stdio: "ignore" });
11999
12085
  if (status.status !== 0)
12000
12086
  return null;
12001
12087
  return { kind: "k8s", context, local: isLocalKubeContext(context) };
@@ -12060,6 +12146,7 @@ function k8sReachHints(context) {
12060
12146
  }
12061
12147
  function start(install) {
12062
12148
  if (install.kind === "compose") {
12149
+ ensureComposePostgresPassword(install);
12063
12150
  const spin = spinner2();
12064
12151
  spin.start("Starting containers…");
12065
12152
  dockerRun(composeArgs(install, "up", "-d"), "docker compose up failed", install.dir);
@@ -12113,6 +12200,7 @@ function stop(install) {
12113
12200
  }
12114
12201
  function restart(install) {
12115
12202
  if (install.kind === "compose") {
12203
+ ensureComposePostgresPassword(install);
12116
12204
  const spin = spinner2();
12117
12205
  spin.start("Restarting containers…");
12118
12206
  dockerRun(composeArgs(install, "restart"), "docker compose restart failed", install.dir);
@@ -12156,9 +12244,11 @@ function update(install) {
12156
12244
  ]);
12157
12245
  }
12158
12246
  const mode = getComposeUpdateMode(install.file);
12247
+ if (mode === "pull")
12248
+ install.file = refreshComposeFileForUpdate(install.file, install.dir);
12249
+ ensureComposePostgresPassword(install);
12159
12250
  const spin = spinner2();
12160
12251
  if (mode === "pull") {
12161
- install.file = refreshComposeFileForUpdate(install.file, install.dir);
12162
12252
  spin.start("Pulling configured Sim images…");
12163
12253
  dockerRun(composeArgs(install, "pull"), "docker compose pull failed", install.dir);
12164
12254
  } else {
@@ -12189,7 +12279,7 @@ function showLogs(install) {
12189
12279
  `), "Logs");
12190
12280
  return;
12191
12281
  }
12192
- spawnSync7("kubectl", ["--context", install.context, "-n", K8S_NAMESPACE, "logs", "-f", `deploy/${K8S_RELEASE}-app`], { stdio: "inherit" });
12282
+ spawnSync8("kubectl", ["--context", install.context, "-n", K8S_NAMESPACE, "logs", "-f", `deploy/${K8S_RELEASE}-app`], { stdio: "inherit" });
12193
12283
  }
12194
12284
  async function down(install) {
12195
12285
  const ok = await confirm2({
@@ -12209,13 +12299,13 @@ async function down(install) {
12209
12299
  const names = managedNames(install);
12210
12300
  if (names.length) {
12211
12301
  dockerRun(["rm", "-f", ...names], "docker rm failed");
12212
- log2.step(`Removed ${names.join(", ")} (Postgres volume ${POSTGRES_VOLUME} kept)`);
12302
+ log2.step(`Removed ${names.join(", ")} (Postgres volume ${POSTGRES_VOLUME2} kept)`);
12213
12303
  } else {
12214
12304
  log2.info("No managed containers to remove.");
12215
12305
  }
12216
12306
  return;
12217
12307
  }
12218
- const result = spawnSync7("helm", ["uninstall", K8S_RELEASE, "--kube-context", install.context, "-n", K8S_NAMESPACE], { stdio: "inherit" });
12308
+ const result = spawnSync8("helm", ["uninstall", K8S_RELEASE, "--kube-context", install.context, "-n", K8S_NAMESPACE], { stdio: "inherit" });
12219
12309
  if (result.status !== 0)
12220
12310
  throw new SetupError("helm uninstall failed");
12221
12311
  log2.step(`Uninstalled ${K8S_RELEASE}`);
@@ -12247,11 +12337,11 @@ async function reset2(install) {
12247
12337
  } else if (install?.kind === "dev") {
12248
12338
  const names = managedNames(install);
12249
12339
  if (names.length)
12250
- spawnSync7("docker", ["rm", "-f", ...names], { cwd: ROOT, stdio: "ignore" });
12251
- spawnSync7("docker", ["volume", "rm", POSTGRES_VOLUME], { cwd: ROOT, stdio: "ignore" });
12340
+ spawnSync8("docker", ["rm", "-f", ...names], { cwd: ROOT, stdio: "ignore" });
12341
+ spawnSync8("docker", ["volume", "rm", POSTGRES_VOLUME2], { cwd: ROOT, stdio: "ignore" });
12252
12342
  log2.step("Managed containers and Postgres volume removed");
12253
12343
  } else if (install?.kind === "k8s") {
12254
- const uninstall = spawnSync7("helm", ["uninstall", K8S_RELEASE, "--kube-context", install.context, "-n", K8S_NAMESPACE], { stdio: "inherit" });
12344
+ const uninstall = spawnSync8("helm", ["uninstall", K8S_RELEASE, "--kube-context", install.context, "-n", K8S_NAMESPACE], { stdio: "inherit" });
12255
12345
  if (uninstall.status !== 0) {
12256
12346
  throw new SetupError(`env files were archived, but helm uninstall failed — the ${K8S_RELEASE} release is still running.`, [
12257
12347
  `retry: ${theme.command(`helm uninstall ${K8S_RELEASE} --kube-context ${shq2(install.context)} -n ${K8S_NAMESPACE}`)}`,
@@ -12318,9 +12408,10 @@ async function runLifecycle(command) {
12318
12408
  return down(install);
12319
12409
  }
12320
12410
  }
12321
- var REALTIME_HEALTH = "http://localhost:3002/health", POSTGRES_VOLUME = "sim-postgres-data", COMPOSE_FILES2, K8S_RELEASE = "sim-dev", K8S_NAMESPACE = "sim-dev", SIM_COMPOSE_MARKERS2;
12411
+ var REALTIME_HEALTH = "http://localhost:3002/health", POSTGRES_VOLUME2 = "sim-postgres-data", COMPOSE_FILES2, K8S_RELEASE = "sim-dev", K8S_NAMESPACE = "sim-dev", SIM_COMPOSE_MARKERS2;
12322
12412
  var init_lifecycle = __esm(() => {
12323
12413
  init_compose_asset();
12414
+ init_compose_database();
12324
12415
  init_compose_project();
12325
12416
  init_context();
12326
12417
  init_detect();
@@ -12472,10 +12563,10 @@ var init_ports = __esm(() => {
12472
12563
  });
12473
12564
 
12474
12565
  // src/modes/compose.ts
12475
- import { spawnSync as spawnSync8 } from "node:child_process";
12566
+ import { spawnSync as spawnSync9 } from "node:child_process";
12476
12567
  import path9 from "node:path";
12477
12568
  function activeComposeProject(composeFile) {
12478
- const result = spawnSync8("docker", ["compose", "ls", "-a", "--format", "json"], {
12569
+ const result = spawnSync9("docker", ["compose", "ls", "-a", "--format", "json"], {
12479
12570
  encoding: "utf8"
12480
12571
  });
12481
12572
  if (result.status !== 0)
@@ -12496,7 +12587,7 @@ function composeCommand(composeFile, project) {
12496
12587
  return `docker compose${project ? ` -p ${project}` : ""} -f ${composeFile}`;
12497
12588
  }
12498
12589
  function composePublishedPorts(composeFile, project) {
12499
- const ids = spawnSync8("docker", composeArgs2(composeFile, project, "ps", "-q"), {
12590
+ const ids = spawnSync9("docker", composeArgs2(composeFile, project, "ps", "-q"), {
12500
12591
  cwd: ROOT,
12501
12592
  encoding: "utf8"
12502
12593
  });
@@ -12504,7 +12595,7 @@ function composePublishedPorts(composeFile, project) {
12504
12595
  `).filter(Boolean) : [];
12505
12596
  if (containers.length === 0)
12506
12597
  return new Set;
12507
- const inspect = spawnSync8("docker", [
12598
+ const inspect = spawnSync9("docker", [
12508
12599
  "inspect",
12509
12600
  ...containers,
12510
12601
  "--format",
@@ -12569,6 +12660,9 @@ async function runComposeMode(detection, quick) {
12569
12660
  if (composeProject && !configuredComposeProject) {
12570
12661
  values.COMPOSE_PROJECT_NAME = composeProject;
12571
12662
  }
12663
+ const postgresPassword = composeFileRequiresPostgresPassword(composeFile) ? choosePostgresPassword(root.vars.get("POSTGRES_PASSWORD"), composeProject ?? composeProjectName(composeFile, ROOT)) : null;
12664
+ if (postgresPassword)
12665
+ values.POSTGRES_PASSWORD = postgresPassword.value;
12572
12666
  Object.assign(values, mothershipOverride());
12573
12667
  const copilotKey = await promptCopilotKey(root.vars.get("COPILOT_API_KEY"));
12574
12668
  if (copilotKey)
@@ -12607,7 +12701,14 @@ async function runComposeMode(detection, quick) {
12607
12701
  remove.delete(key);
12608
12702
  reconcileEnvValues("root", [...remove], values);
12609
12703
  log2.step("Wrote .env (compose reads it for variable substitution)");
12610
- const validation = spawnSync8("docker", composeArgs2(composeFile, composeProject, "config"), {
12704
+ if (postgresPassword) {
12705
+ reportPostgresPasswordChoice(postgresPassword, {
12706
+ compose: composeCommand(composeFile, composeProject),
12707
+ user: postgresUser(root.vars.get("POSTGRES_USER")),
12708
+ envPath: root.path
12709
+ });
12710
+ }
12711
+ const validation = spawnSync9("docker", composeArgs2(composeFile, composeProject, "config"), {
12611
12712
  cwd: ROOT,
12612
12713
  encoding: "utf8"
12613
12714
  });
@@ -12617,7 +12718,7 @@ async function runComposeMode(detection, quick) {
12617
12718
  await ensureComposePortsFree(composeFile, composeProject);
12618
12719
  const command = composeCommand(composeFile, composeProject);
12619
12720
  log2.step(`Running ${command} up -d`);
12620
- const result = spawnSync8("docker", composeArgs2(composeFile, composeProject, "up", "-d"), {
12721
+ const result = spawnSync9("docker", composeArgs2(composeFile, composeProject, "up", "-d"), {
12621
12722
  cwd: ROOT,
12622
12723
  stdio: "inherit"
12623
12724
  });
@@ -12646,6 +12747,7 @@ var init_compose = __esm(() => {
12646
12747
  init_capability_config();
12647
12748
  init_capability_setup();
12648
12749
  init_compose_asset();
12750
+ init_compose_database();
12649
12751
  init_compose_project();
12650
12752
  init_context();
12651
12753
  init_docker();
@@ -12661,18 +12763,18 @@ var init_compose = __esm(() => {
12661
12763
  });
12662
12764
 
12663
12765
  // src/db.ts
12664
- import { spawnSync as spawnSync9 } from "node:child_process";
12766
+ import { spawnSync as spawnSync10 } from "node:child_process";
12665
12767
  function buildDsn(password, hostPort) {
12666
12768
  return `postgresql://postgres:${encodeURIComponent(password)}@localhost:${hostPort}/simstudio`;
12667
12769
  }
12668
12770
  function docker(args) {
12669
- const result = spawnSync9("docker", args, { encoding: "utf8" });
12771
+ const result = spawnSync10("docker", args, { encoding: "utf8" });
12670
12772
  if (result.status !== 0) {
12671
12773
  throw new Error(`docker ${args[0]} failed: ${result.stderr.trim() || result.stdout.trim()}`);
12672
12774
  }
12673
12775
  }
12674
12776
  function dockerOutput(args) {
12675
- const result = spawnSync9("docker", args, { encoding: "utf8" });
12777
+ const result = spawnSync10("docker", args, { encoding: "utf8" });
12676
12778
  return result.status === 0 ? result.stdout.trim() : null;
12677
12779
  }
12678
12780
  function inspectManagedContainer() {
@@ -12753,10 +12855,10 @@ async function promptExternalDsn() {
12753
12855
  }
12754
12856
  }
12755
12857
  function volumeInitialized() {
12756
- if (spawnSync9("docker", ["volume", "inspect", DB_VOLUME], { stdio: "ignore" }).status !== 0) {
12858
+ if (spawnSync10("docker", ["volume", "inspect", DB_VOLUME], { stdio: "ignore" }).status !== 0) {
12757
12859
  return false;
12758
12860
  }
12759
- return spawnSync9("docker", [
12861
+ return spawnSync10("docker", [
12760
12862
  "run",
12761
12863
  "--rm",
12762
12864
  "-v",
@@ -12862,7 +12964,7 @@ async function startManagedContainer(detection) {
12862
12964
  `or discard the old data: ${theme.command(`docker rm -f ${DB_CONTAINER} && docker volume rm ${DB_VOLUME}`)}`
12863
12965
  ]);
12864
12966
  }
12865
- const logs = spawnSync9("docker", ["logs", "--tail", "20", DB_CONTAINER], { encoding: "utf8" });
12967
+ const logs = spawnSync10("docker", ["logs", "--tail", "20", DB_CONTAINER], { encoding: "utf8" });
12866
12968
  throw new SetupError(`the Postgres container failed to start. Last logs:
12867
12969
  ${logs.stdout}${logs.stderr}`, [
12868
12970
  `inspect: ${theme.command(`docker logs ${DB_CONTAINER}`)}`,
@@ -12923,9 +13025,9 @@ var init_db = __esm(() => {
12923
13025
  });
12924
13026
 
12925
13027
  // src/redis.ts
12926
- import { spawnSync as spawnSync10 } from "node:child_process";
13028
+ import { spawnSync as spawnSync11 } from "node:child_process";
12927
13029
  function managedRedisUrl() {
12928
- const result = spawnSync10("docker", ["port", REDIS_CONTAINER, "6379/tcp"], { encoding: "utf8" });
13030
+ const result = spawnSync11("docker", ["port", REDIS_CONTAINER, "6379/tcp"], { encoding: "utf8" });
12929
13031
  if (result.status !== 0)
12930
13032
  return null;
12931
13033
  const port = result.stdout.trim().split(`
@@ -12942,7 +13044,7 @@ async function pingWithSpinner(url, label) {
12942
13044
  async function startManagedRedis(detection) {
12943
13045
  const hostPort = detection.redisPortOpen ? 6380 : 6379;
12944
13046
  const url = `redis://localhost:${hostPort}`;
12945
- spawnSync10("docker", ["rm", "-f", REDIS_CONTAINER], { stdio: "ignore" });
13047
+ spawnSync11("docker", ["rm", "-f", REDIS_CONTAINER], { stdio: "ignore" });
12946
13048
  docker([
12947
13049
  "run",
12948
13050
  "-d",
@@ -13053,12 +13155,12 @@ var init_redis = __esm(() => {
13053
13155
  });
13054
13156
 
13055
13157
  // src/modes/dev.ts
13056
- import { spawnSync as spawnSync11 } from "node:child_process";
13158
+ import { spawnSync as spawnSync12 } from "node:child_process";
13057
13159
  import path10 from "node:path";
13058
13160
  async function runMigrations(dsn) {
13059
13161
  const spin = spinner2();
13060
13162
  spin.start("Running database migrations…");
13061
- const result = spawnSync11("bun", ["run", "db:migrate"], {
13163
+ const result = spawnSync12("bun", ["run", "db:migrate"], {
13062
13164
  cwd: path10.join(ROOT, "packages/db"),
13063
13165
  encoding: "utf8"
13064
13166
  });
@@ -13201,7 +13303,7 @@ var init_dev = __esm(() => {
13201
13303
  });
13202
13304
 
13203
13305
  // src/wizard.ts
13204
- import { spawnSync as spawnSync12 } from "node:child_process";
13306
+ import { spawnSync as spawnSync13 } from "node:child_process";
13205
13307
  async function handleExistingConfig(detection) {
13206
13308
  const present = Object.entries(detection.envFiles).filter(([, exists]) => exists).map(([target]) => target === "root" ? ".env" : `${target}/.env`);
13207
13309
  if (present.length === 0)
@@ -13327,11 +13429,11 @@ async function runWizard(flags) {
13327
13429
  `), "Next steps");
13328
13430
  outro2(theme.accent("Sim is ready."));
13329
13431
  if (mode === "compose" && process.platform === "darwin") {
13330
- spawnSync12("open", [APP_SIGNUP_URL], { stdio: "ignore" });
13432
+ spawnSync13("open", [APP_SIGNUP_URL], { stdio: "ignore" });
13331
13433
  }
13332
13434
  if (startDevNow) {
13333
13435
  if (await ensurePortsFree([3000, 3002])) {
13334
- const child = spawnSync12("bun", ["run", devScript], { cwd: ROOT, stdio: "inherit" });
13436
+ const child = spawnSync13("bun", ["run", devScript], { cwd: ROOT, stdio: "inherit" });
13335
13437
  process.exitCode = child.status ?? 0;
13336
13438
  } else {
13337
13439
  log2.warn(`Ports still in use — Sim wasn't started. Free them, then run ${theme.command(`bun run ${devScript}`)}.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim-setup",
3
- "version": "1.0.24-preview.146.1",
3
+ "version": "1.0.24-preview.149.1",
4
4
  "description": "Set up and manage a self-hosted Sim installation",
5
5
  "type": "module",
6
6
  "bin": {