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.
- package/dist/docker-compose.prod.yml +13 -6
- package/dist/index.js +152 -50
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
11617
|
+
import { spawn, spawnSync as spawnSync7 } from "node:child_process";
|
|
11548
11618
|
function run(command, args, failMessage, input) {
|
|
11549
|
-
const result =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
11911
|
-
import { existsSync as existsSync8, readFileSync as
|
|
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
|
|
11989
|
+
return spawnSync8("docker", ["info"], { stdio: "ignore" }).status === 0;
|
|
11920
11990
|
}
|
|
11921
11991
|
function dockerText(args, cwd = ROOT) {
|
|
11922
|
-
const result =
|
|
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
|
|
11996
|
+
return spawnSync8("docker", args, { cwd, stdio: "inherit" }).status ?? 1;
|
|
11927
11997
|
}
|
|
11928
11998
|
function dockerRun(args, failMessage, cwd = ROOT) {
|
|
11929
|
-
const result =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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 ${
|
|
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 =
|
|
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
|
-
|
|
12251
|
-
|
|
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 =
|
|
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",
|
|
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
|
|
12566
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12476
12567
|
import path9 from "node:path";
|
|
12477
12568
|
function activeComposeProject(composeFile) {
|
|
12478
|
-
const result =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (
|
|
12858
|
+
if (spawnSync10("docker", ["volume", "inspect", DB_VOLUME], { stdio: "ignore" }).status !== 0) {
|
|
12757
12859
|
return false;
|
|
12758
12860
|
}
|
|
12759
|
-
return
|
|
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 =
|
|
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
|
|
13028
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
12927
13029
|
function managedRedisUrl() {
|
|
12928
|
-
const result =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
13432
|
+
spawnSync13("open", [APP_SIGNUP_URL], { stdio: "ignore" });
|
|
13331
13433
|
}
|
|
13332
13434
|
if (startDevNow) {
|
|
13333
13435
|
if (await ensurePortsFree([3000, 3002])) {
|
|
13334
|
-
const child =
|
|
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}`)}.`);
|