Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

194 lines
7.1 KiB
TypeScript

import { spawnSync } from 'node:child_process'
import { showBanner } from './banner.ts'
import { loadCheckContext, runChecks } from './checks.ts'
import { type Detection, runDetection } from './detect.ts'
import { archiveEnvFile, ROOT } from './env-files.ts'
import { runComposeMode } from './modes/compose.ts'
import { runDevMode } from './modes/dev.ts'
import { runK8sMode } from './modes/k8s.ts'
import { ensurePortsFree } from './ports.ts'
import * as p from './prompter.ts'
import { glyph, theme } from './theme.ts'
import { APP_SIGNUP_URL } from './urls.ts'
export type WizardMode = 'compose' | 'dev' | 'k8s'
export interface WizardFlags {
quick: boolean
mode?: WizardMode
}
async function handleExistingConfig(detection: Detection): Promise<'continue' | 'doctor'> {
// Root counts: a compose install writes only `.env`, so excluding it made a
// configured machine look unconfigured and silently re-run from scratch.
const present = Object.entries(detection.envFiles)
.filter(([, exists]) => exists)
.map(([target]) => (target === 'root' ? '.env' : `${target}/.env`))
if (present.length === 0) return 'continue'
const choice = await p.select({
message: `Found existing config (${present.join(', ')}) — what should we do?`,
options: [
{ value: 'keep', label: 'Keep it', hint: 'run doctor against the current setup and exit' },
{
value: 'review',
label: 'Review and update',
hint: 'walk the wizard with current values prefilled',
},
{ value: 'reset', label: 'Reset', hint: 'archive current .env files and start fresh' },
],
initialValue: 'keep',
})
if (choice === 'keep') return 'doctor'
if (choice === 'reset') {
for (const target of ['sim', 'realtime', 'db', 'root'] as const) {
const backup = archiveEnvFile(target)
if (backup) p.log.step(`Archived ${backup}`)
}
}
return 'continue'
}
const LOW_DOCKER_MEM_GB = 6
const LOW_DISK_GB = 15
async function selectMode(detection: Detection, flags: WizardFlags): Promise<WizardMode> {
if (flags.mode) return flags.mode
const { dockerMemGb } = detection.specs
const vm = dockerMemGb !== null ? ` · VM ${dockerMemGb}GB` : ''
const composeState = detection.dockerRunning ? `Docker ready${vm}` : 'Docker is NOT running'
const k8sState = detection.kubeContext
? `context: ${detection.kubeContext}${vm}`
: detection.binaries.kind
? `kind available${vm}`
: 'needs kind or Docker Desktop k8s'
return p.select({
message: 'How do you want to run Sim?',
options: [
{
value: 'compose',
label: 'Docker Compose',
hint: `Run bundled Sim, fastest way to start · ${composeState}`,
},
{
value: 'dev',
label: 'Local dev (bun run dev:full)',
hint: 'Work on Sim itself · app :3000 + realtime :3002',
},
{
value: 'k8s',
label: 'Kubernetes (helm)',
hint: `Test a production-style k8s deploy · ${k8sState}`,
},
],
// Always Compose: it is the fastest path and the one most people want. A
// stopped Docker is not a reason to steer them to a source checkout — the
// compose path offers to start Docker Desktop itself.
initialValue: 'compose',
})
}
/** Spec warnings before committing to a mode — informed choice, no silent degradation. */
function warnLowSpecs(detection: Detection, mode: WizardMode): void {
const { dockerMemGb, freeDiskGb } = detection.specs
if (
(mode === 'compose' || mode === 'k8s') &&
dockerMemGb !== null &&
dockerMemGb < LOW_DOCKER_MEM_GB
) {
p.log.warn(
`Docker's VM has only ${dockerMemGb}GB of memory${mode === 'k8s' ? ' — pods will likely sit Pending or OOM' : ' — containers may OOM'}. Raise it in Docker Desktop → Settings → Resources (8GB+ recommended).`
)
}
if ((mode === 'compose' || mode === 'k8s') && freeDiskGb !== null && freeDiskGb < LOW_DISK_GB) {
p.log.warn(`Only ${freeDiskGb}GB of free disk — image pulls need 5-10GB.`)
}
}
async function finalVerify(): Promise<void> {
const findings = await runChecks(loadCheckContext(true), ['live'])
for (const finding of findings) {
console.log(` ${glyph[finding.status]} ${finding.message}`)
}
}
export async function runWizard(flags: WizardFlags): Promise<void> {
// Detection is ~600ms of subprocess work and the banner is ~600ms of animation
// with nothing to do — overlap them so the spinner below usually resolves at once.
const detecting = runDetection()
await showBanner()
const spin = p.spinner()
spin.start('Looking at what you already have…')
const detection = await detecting
spin.stop(
`Detected: docker ${detection.dockerRunning ? '✓' : '✗'} · postgres ${detection.postgresPortOpen ? '✓' : '✗'} · ` +
`${detection.shellLlmKeys.length} shell LLM key${detection.shellLlmKeys.length === 1 ? '' : 's'}` +
(detection.kubeContext ? ` · kube: ${detection.kubeContext}` : '')
)
if ((await handleExistingConfig(detection)) === 'doctor') {
const { runDoctor } = await import('./doctor.ts')
process.exitCode = await runDoctor({ fix: false, json: false })
return
}
const quick =
flags.quick ||
(await p.select({
message: 'Setup style?',
options: [
{ value: 'quick', label: 'Quick', hint: 'sensible defaults, minimal questions' },
{
value: 'custom',
label: 'Custom',
hint: 'every option: redis, trigger.dev, image variants',
},
],
initialValue: 'quick',
})) === 'quick'
const mode = await selectMode(detection, flags)
warnLowSpecs(detection, mode)
let startDevNow = false
let devScript = 'dev:full'
if (mode === 'compose') await runComposeMode(detection, quick)
else if (mode === 'dev') {
const dev = await runDevMode(detection, quick)
startDevNow = dev.startNow
devScript = dev.script
} else await runK8sMode(detection)
if (mode !== 'k8s' && !startDevNow) await finalVerify()
p.note(
[
mode === 'k8s' ? `port-forward, then open ${APP_SIGNUP_URL}` : `open ${APP_SIGNUP_URL}`,
'manage it: bun run sim start · stop · status · logs',
'check your setup: bun run sim doctor',
mode === 'dev' && !startDevNow ? `start Sim: bun run ${devScript}` : null,
`prefer a bare "sim"? ${theme.command('bun link')} once (needs ~/.bun/bin on PATH)`,
]
.filter(Boolean)
.join('\n'),
'Next steps'
)
p.outro(theme.accent('Sim is ready.'))
if (mode === 'compose' && process.platform === 'darwin') {
spawnSync('open', [APP_SIGNUP_URL], { stdio: 'ignore' })
}
if (startDevNow) {
// dev:full binds 3000 (app) and 3002 (realtime) — resolve any conflict
// (e.g. another worktree's dev server) before starting, instead of spawning
// a server that fails to bind. If the user leaves the ports, skip the start.
if (await ensurePortsFree([3000, 3002])) {
const child = spawnSync('bun', ['run', devScript], { cwd: ROOT, stdio: 'inherit' })
process.exitCode = child.status ?? 0
} else {
p.log.warn(
`Ports still in use — Sim wasn't started. Free them, then run ${theme.command(`bun run ${devScript}`)}.`
)
}
}
}