sim-setup 1.0.1 → 1.0.2-preview.36.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/README.md +6 -0
- package/dist/index.js +437 -417
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,3 +9,9 @@ npx sim-setup
|
|
|
9
9
|
Outside a Sim source checkout, the command creates a Docker Compose installation using published
|
|
10
10
|
images. Inside a Sim source checkout, use `bun run sim-setup` to expose the complete development
|
|
11
11
|
and deployment wizard.
|
|
12
|
+
|
|
13
|
+
To connect or replace the Chat API key without rerunning the full wizard:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx sim-setup add chat
|
|
17
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -1521,9 +1521,12 @@ var init_env_capabilities = __esm(() => {
|
|
|
1521
1521
|
...new Set([
|
|
1522
1522
|
...CORE_CONFIGURATION_KEYS,
|
|
1523
1523
|
...ENV_CAPABILITIES.flatMap(capabilityKeys),
|
|
1524
|
+
"COPILOT_API_KEY",
|
|
1524
1525
|
"EMAIL_VERIFICATION_ENABLED",
|
|
1526
|
+
"NEXT_PUBLIC_CHAT_DISABLED",
|
|
1525
1527
|
"NEXT_PUBLIC_E2B_ENABLED",
|
|
1526
1528
|
"NEXT_PUBLIC_SANDBOXES_ENABLED",
|
|
1529
|
+
"SIM_AGENT_API_URL",
|
|
1527
1530
|
...Object.values(LLM_KEY_POOLS).flatMap((pool) => [
|
|
1528
1531
|
...pool.keys,
|
|
1529
1532
|
..."fallbackKey" in pool ? [pool.fallbackKey] : []
|
|
@@ -27897,6 +27900,7 @@ var init_capability_config = __esm(() => {
|
|
|
27897
27900
|
id: setup.definition.id,
|
|
27898
27901
|
label: setup.label
|
|
27899
27902
|
})),
|
|
27903
|
+
{ id: "chat", label: "Chat" },
|
|
27900
27904
|
{ id: "llm", label: "LLM API keys" },
|
|
27901
27905
|
{ id: "integration", label: "OAuth integration" }
|
|
27902
27906
|
];
|
|
@@ -32992,7 +32996,7 @@ var init_setup_status = __esm(() => {
|
|
|
32992
32996
|
init_theme();
|
|
32993
32997
|
SECRET_KEYS2 = new Set(["BETTER_AUTH_SECRET", "ENCRYPTION_KEY", "INTERNAL_API_SECRET"]);
|
|
32994
32998
|
URL_KEYS2 = new Set(["DATABASE_URL", "BETTER_AUTH_URL", "NEXT_PUBLIC_APP_URL"]);
|
|
32995
|
-
FEATURE_ORDER = SETUP_FEATURES.flatMap((feature) => feature.id === "integration" ? [] : [feature.id]);
|
|
32999
|
+
FEATURE_ORDER = SETUP_FEATURES.flatMap((feature) => feature.id === "chat" || feature.id === "integration" ? [] : [feature.id]);
|
|
32996
33000
|
});
|
|
32997
33001
|
|
|
32998
33002
|
// ../../node_modules/fast-string-truncated-width/dist/utils.js
|
|
@@ -35251,418 +35255,10 @@ var init_capability_setup = __esm(() => {
|
|
|
35251
35255
|
init_prompter();
|
|
35252
35256
|
});
|
|
35253
35257
|
|
|
35254
|
-
// src/feature-setup.ts
|
|
35255
|
-
var exports_feature_setup = {};
|
|
35256
|
-
__export(exports_feature_setup, {
|
|
35257
|
-
setupFeatureUsage: () => setupFeatureUsage,
|
|
35258
|
-
runFeatureSetup: () => runFeatureSetup,
|
|
35259
|
-
resolveFeatureSetupDestination: () => resolveFeatureSetupDestination,
|
|
35260
|
-
reconcileLlmSetup: () => reconcileLlmSetup
|
|
35261
|
-
});
|
|
35262
|
-
function isSetupFeatureId(value) {
|
|
35263
|
-
return SETUP_FEATURES.some((feature) => feature.id === value);
|
|
35264
|
-
}
|
|
35265
|
-
async function setupIntegration(requestedId, vars) {
|
|
35266
|
-
if (!requestedId) {
|
|
35267
|
-
throw new Error("Missing integration id. Example: npx sim-setup add integration slack");
|
|
35268
|
-
}
|
|
35269
|
-
const providerId = resolveOAuthClientCapabilityId(requestedId);
|
|
35270
|
-
if (!providerId) {
|
|
35271
|
-
throw new Error(`Unknown OAuth integration "${requestedId}". Expected one of: ${Object.keys(OAUTH_CLIENT_CAPABILITIES).join(", ")}`);
|
|
35272
|
-
}
|
|
35273
|
-
const fields = getOAuthClientSetupFields(providerId);
|
|
35274
|
-
const values2 = {};
|
|
35275
|
-
for (const field of fields) {
|
|
35276
|
-
const existing = vars.get(field.key);
|
|
35277
|
-
if (field.input === "secret") {
|
|
35278
|
-
const value = await password2({
|
|
35279
|
-
message: existing ? `${field.key} (Currently used); leave empty to keep it` : field.key,
|
|
35280
|
-
validate: (candidate) => candidate || existing ? undefined : "required"
|
|
35281
|
-
});
|
|
35282
|
-
const resolved = value || existing;
|
|
35283
|
-
if (!resolved)
|
|
35284
|
-
throw new Error(`${field.key} was not provided`);
|
|
35285
|
-
values2[field.key] = resolved;
|
|
35286
|
-
} else {
|
|
35287
|
-
values2[field.key] = await text2({
|
|
35288
|
-
message: `${field.key}${existing ? " (Currently used)" : ""}`,
|
|
35289
|
-
initialValue: existing,
|
|
35290
|
-
validate: (candidate) => candidate ? undefined : "required"
|
|
35291
|
-
});
|
|
35292
|
-
}
|
|
35293
|
-
}
|
|
35294
|
-
log2.info(`Configured the ${providerId} OAuth client.`);
|
|
35295
|
-
return values2;
|
|
35296
|
-
}
|
|
35297
|
-
function reconcileLlmSetup(providerId, values2) {
|
|
35298
|
-
const pool = LLM_KEY_POOLS[providerId];
|
|
35299
|
-
const fields = [...pool.keys, ..."fallbackKey" in pool ? [pool.fallbackKey] : []];
|
|
35300
|
-
return {
|
|
35301
|
-
values: values2,
|
|
35302
|
-
remove: fields.filter((key) => !Object.hasOwn(values2, key))
|
|
35303
|
-
};
|
|
35304
|
-
}
|
|
35305
|
-
async function setupLlm(vars) {
|
|
35306
|
-
const currentProvider = Object.entries(LLM_KEY_POOLS).find(([, pool2]) => [...pool2.keys, ..."fallbackKey" in pool2 ? [pool2.fallbackKey] : []].some((key) => vars.has(key)))?.[0];
|
|
35307
|
-
const provider = await select3({
|
|
35308
|
-
message: "LLM key pool?",
|
|
35309
|
-
options: Object.keys(LLM_KEY_POOLS).map((id) => ({
|
|
35310
|
-
value: id,
|
|
35311
|
-
label: id,
|
|
35312
|
-
hint: id === currentProvider ? "Currently used" : undefined
|
|
35313
|
-
})),
|
|
35314
|
-
initialValue: currentProvider
|
|
35315
|
-
});
|
|
35316
|
-
const pool = LLM_KEY_POOLS[provider];
|
|
35317
|
-
const keys = pool.keys;
|
|
35318
|
-
const values2 = {};
|
|
35319
|
-
for (const [index, key] of keys.entries()) {
|
|
35320
|
-
const legacyKey = index === 0 && "fallbackKey" in pool ? pool.fallbackKey : undefined;
|
|
35321
|
-
const existingKey = vars.has(key) ? key : legacyKey;
|
|
35322
|
-
const existing = existingKey ? vars.get(existingKey) : undefined;
|
|
35323
|
-
const value = await password2({
|
|
35324
|
-
message: existing ? `${key} (${existingKey} is currently used); leave empty to keep it` : `${key}${index === 0 ? "" : " (empty to finish)"}`,
|
|
35325
|
-
validate: index === 0 ? (candidate) => candidate || existing ? undefined : "required" : undefined
|
|
35326
|
-
});
|
|
35327
|
-
const resolved = value || existing;
|
|
35328
|
-
if (!resolved)
|
|
35329
|
-
break;
|
|
35330
|
-
values2[key] = resolved;
|
|
35331
|
-
}
|
|
35332
|
-
return reconcileLlmSetup(provider, values2);
|
|
35333
|
-
}
|
|
35334
|
-
function setupFeatureUsage() {
|
|
35335
|
-
return SETUP_FEATURES.map((feature) => feature.id === "integration" ? "integration <slug>" : feature.id).join(" | ");
|
|
35336
|
-
}
|
|
35337
|
-
function resolveFeatureSetupDestination(sources) {
|
|
35338
|
-
if (sources.length === 0) {
|
|
35339
|
-
throw new Error("No Sim configuration was detected. Run npx sim-setup first.");
|
|
35340
|
-
}
|
|
35341
|
-
const managed = sources.filter((source2) => source2.managedByCurrentCheckout);
|
|
35342
|
-
if (managed.length === 0) {
|
|
35343
|
-
throw new Error("No effective configuration is safely writable by this checkout. Process overrides, higher-precedence development env files, external Compose projects, and Helm releases must be updated at their source. Run npx sim-setup config for the detected sources.");
|
|
35344
|
-
}
|
|
35345
|
-
if (managed.length > 1) {
|
|
35346
|
-
throw new Error(`More than one effective configuration is writable by this checkout (${managed.map((source2) => source2.label).join(", ")}). Run npx sim-setup config and remove the ambiguity before configuring a feature.`);
|
|
35347
|
-
}
|
|
35348
|
-
const source = managed[0];
|
|
35349
|
-
if (!source.values) {
|
|
35350
|
-
throw new Error(`${source.label} is managed by this checkout, but its effective environment could not be resolved. Run npx sim-setup config and fix the reported source error first.`);
|
|
35351
|
-
}
|
|
35352
|
-
if (source.kind === "helm") {
|
|
35353
|
-
throw new Error("Helm configuration cannot be updated by npx sim-setup add. Update the release Secret or values and upgrade the release.");
|
|
35354
|
-
}
|
|
35355
|
-
return {
|
|
35356
|
-
source,
|
|
35357
|
-
target: source.kind === "compose" ? "root" : "sim",
|
|
35358
|
-
vars: source.values,
|
|
35359
|
-
containerized: source.kind === "compose"
|
|
35360
|
-
};
|
|
35361
|
-
}
|
|
35362
|
-
async function runFeatureSetup(feature, args) {
|
|
35363
|
-
if (!isSetupFeatureId(feature)) {
|
|
35364
|
-
throw new Error(`Unknown setup feature "${feature}". Expected: ${setupFeatureUsage()}`);
|
|
35365
|
-
}
|
|
35366
|
-
const destination = resolveFeatureSetupDestination(discoverConfigurationSources());
|
|
35367
|
-
const { target, vars } = destination;
|
|
35368
|
-
let values2;
|
|
35369
|
-
let remove;
|
|
35370
|
-
const capabilitySetup = getCapabilitySetup(feature);
|
|
35371
|
-
if (capabilitySetup) {
|
|
35372
|
-
const result = await promptCapabilitySetup(capabilitySetup, vars, {
|
|
35373
|
-
containerized: destination.containerized
|
|
35374
|
-
});
|
|
35375
|
-
values2 = result.values;
|
|
35376
|
-
remove = result.remove;
|
|
35377
|
-
} else if (feature === "integration") {
|
|
35378
|
-
values2 = await setupIntegration(args[0], vars);
|
|
35379
|
-
remove = [];
|
|
35380
|
-
} else if (feature === "llm") {
|
|
35381
|
-
const result = await setupLlm(vars);
|
|
35382
|
-
values2 = result.values;
|
|
35383
|
-
remove = result.remove;
|
|
35384
|
-
} else {
|
|
35385
|
-
throw new Error(`Setup feature ${feature} has no handler`);
|
|
35386
|
-
}
|
|
35387
|
-
reconcileEnvValues(target, remove, values2);
|
|
35388
|
-
const label = SETUP_FEATURES.find((item) => item.id === feature)?.label;
|
|
35389
|
-
outro2(theme.accent(destination.containerized ? `${label} written to .env. Recreate the app container for it to take effect.` : `${label} configured.`));
|
|
35390
|
-
}
|
|
35391
|
-
var init_feature_setup = __esm(() => {
|
|
35392
|
-
init_env_capabilities();
|
|
35393
|
-
init_capability_config();
|
|
35394
|
-
init_capability_setup();
|
|
35395
|
-
init_configuration_sources();
|
|
35396
|
-
init_env_files();
|
|
35397
|
-
init_prompter();
|
|
35398
|
-
init_theme();
|
|
35399
|
-
});
|
|
35400
|
-
|
|
35401
|
-
// src/doctor.ts
|
|
35402
|
-
var exports_doctor = {};
|
|
35403
|
-
__export(exports_doctor, {
|
|
35404
|
-
runDoctor: () => runDoctor
|
|
35405
|
-
});
|
|
35406
|
-
function render(findings, fixedCount) {
|
|
35407
|
-
console.log(`
|
|
35408
|
-
${theme.heading("◆ Sim doctor")}
|
|
35409
|
-
`);
|
|
35410
|
-
for (const group of GROUP_ORDER) {
|
|
35411
|
-
const groupFindings = findings.filter((f2) => f2.group === group);
|
|
35412
|
-
if (groupFindings.length === 0)
|
|
35413
|
-
continue;
|
|
35414
|
-
console.log(theme.heading(GROUP_TITLES[group]));
|
|
35415
|
-
for (const finding of groupFindings) {
|
|
35416
|
-
console.log(` ${glyph[finding.status]} ${finding.message}`);
|
|
35417
|
-
if (finding.fix && finding.status !== "pass") {
|
|
35418
|
-
console.log(` ${theme.muted(`fix: ${finding.fix}`)}`);
|
|
35419
|
-
}
|
|
35420
|
-
}
|
|
35421
|
-
console.log();
|
|
35422
|
-
}
|
|
35423
|
-
const counts = {
|
|
35424
|
-
pass: findings.filter((f2) => f2.status === "pass").length,
|
|
35425
|
-
warn: findings.filter((f2) => f2.status === "warn").length,
|
|
35426
|
-
fail: findings.filter((f2) => f2.status === "fail").length
|
|
35427
|
-
};
|
|
35428
|
-
const summary = [`${counts.pass} passed`];
|
|
35429
|
-
if (counts.warn)
|
|
35430
|
-
summary.push(theme.warn(`${counts.warn} warning${counts.warn > 1 ? "s" : ""}`));
|
|
35431
|
-
if (counts.fail)
|
|
35432
|
-
summary.push(theme.error(`${counts.fail} failed`));
|
|
35433
|
-
if (fixedCount)
|
|
35434
|
-
summary.push(theme.success(`${fixedCount} fixed`));
|
|
35435
|
-
console.log(summary.join(theme.muted(" · ")));
|
|
35436
|
-
}
|
|
35437
|
-
async function runDoctor(options) {
|
|
35438
|
-
let findings = await runChecks(loadCheckContext(true));
|
|
35439
|
-
let fixedCount = 0;
|
|
35440
|
-
if (options.fix) {
|
|
35441
|
-
const fixable = findings.filter((f2) => (f2.status === "fail" || f2.status === "warn") && f2.autofix);
|
|
35442
|
-
for (const finding of fixable) {
|
|
35443
|
-
finding.autofix?.();
|
|
35444
|
-
fixedCount++;
|
|
35445
|
-
}
|
|
35446
|
-
if (fixedCount > 0)
|
|
35447
|
-
findings = await runChecks(loadCheckContext(true));
|
|
35448
|
-
}
|
|
35449
|
-
if (options.json) {
|
|
35450
|
-
console.log(JSON.stringify(findings.map(({ autofix: _autofix, ...rest }) => rest), null, 2));
|
|
35451
|
-
} else {
|
|
35452
|
-
render(findings, fixedCount);
|
|
35453
|
-
}
|
|
35454
|
-
return findings.some((f2) => f2.status === "fail") ? 1 : 0;
|
|
35455
|
-
}
|
|
35456
|
-
var GROUP_TITLES, GROUP_ORDER;
|
|
35457
|
-
var init_doctor = __esm(() => {
|
|
35458
|
-
init_checks();
|
|
35459
|
-
init_theme();
|
|
35460
|
-
GROUP_TITLES = {
|
|
35461
|
-
files: "Env files",
|
|
35462
|
-
schema: "Schema",
|
|
35463
|
-
consistency: "Consistency",
|
|
35464
|
-
coherence: "Coherence",
|
|
35465
|
-
live: "Live"
|
|
35466
|
-
};
|
|
35467
|
-
GROUP_ORDER = ["files", "schema", "consistency", "coherence", "live"];
|
|
35468
|
-
});
|
|
35469
|
-
|
|
35470
|
-
// src/compose-asset.ts
|
|
35471
|
-
import { createHash } from "node:crypto";
|
|
35472
|
-
import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
|
|
35473
|
-
import path6 from "node:path";
|
|
35474
|
-
import { fileURLToPath } from "node:url";
|
|
35475
|
-
function sha2562(contents) {
|
|
35476
|
-
return createHash("sha256").update(contents).digest("hex");
|
|
35477
|
-
}
|
|
35478
|
-
function readManagedState(root) {
|
|
35479
|
-
const file = path6.join(root, MANAGED_STATE_FILE);
|
|
35480
|
-
if (!existsSync6(file))
|
|
35481
|
-
return null;
|
|
35482
|
-
const parsed = JSON.parse(readFileSync6(file, "utf8"));
|
|
35483
|
-
if (typeof parsed !== "object" || parsed === null || !("schemaVersion" in parsed) || parsed.schemaVersion !== 1 || !("composeSha256" in parsed) || typeof parsed.composeSha256 !== "string") {
|
|
35484
|
-
throw new Error(`${file} is not a valid sim-setup managed-state file`);
|
|
35485
|
-
}
|
|
35486
|
-
return { schemaVersion: 1, composeSha256: parsed.composeSha256 };
|
|
35487
|
-
}
|
|
35488
|
-
function writeManagedState(root, contents) {
|
|
35489
|
-
const state = { schemaVersion: 1, composeSha256: sha2562(contents) };
|
|
35490
|
-
writeFileSync2(path6.join(root, MANAGED_STATE_FILE), `${JSON.stringify(state, null, 2)}
|
|
35491
|
-
`);
|
|
35492
|
-
}
|
|
35493
|
-
function packagedComposeFile() {
|
|
35494
|
-
const moduleDirectory = path6.dirname(fileURLToPath(import.meta.url));
|
|
35495
|
-
return path6.basename(moduleDirectory) === "src" ? path6.resolve(moduleDirectory, "../../..", COMPOSE_FILE2) : path6.join(moduleDirectory, COMPOSE_FILE2);
|
|
35496
|
-
}
|
|
35497
|
-
function ensureProductionComposeFile(context = SETUP_CONTEXT) {
|
|
35498
|
-
const destination = path6.join(context.root, COMPOSE_FILE2);
|
|
35499
|
-
if (context.kind === "source") {
|
|
35500
|
-
if (!existsSync6(destination)) {
|
|
35501
|
-
throw new Error(`Sim source checkout is missing ${COMPOSE_FILE2}`);
|
|
35502
|
-
}
|
|
35503
|
-
return destination;
|
|
35504
|
-
}
|
|
35505
|
-
const bundled = packagedComposeFile();
|
|
35506
|
-
if (!existsSync6(bundled)) {
|
|
35507
|
-
throw new Error(`The sim-setup package is missing its bundled ${COMPOSE_FILE2}`);
|
|
35508
|
-
}
|
|
35509
|
-
mkdirSync2(context.root, { recursive: true });
|
|
35510
|
-
const packaged = readFileSync6(bundled, "utf8");
|
|
35511
|
-
if (existsSync6(destination)) {
|
|
35512
|
-
const current = readFileSync6(destination, "utf8");
|
|
35513
|
-
if (current === packaged) {
|
|
35514
|
-
writeManagedState(context.root, packaged);
|
|
35515
|
-
return destination;
|
|
35516
|
-
}
|
|
35517
|
-
if (!current.includes(SIM_COMPOSE_MARKER2)) {
|
|
35518
|
-
throw new Error(`${destination} exists but is not a recognized Sim Compose file`);
|
|
35519
|
-
}
|
|
35520
|
-
const managed = readManagedState(context.root);
|
|
35521
|
-
if (!managed || managed.composeSha256 !== sha2562(current)) {
|
|
35522
|
-
throw new Error(`${destination} has local changes; preserve or remove your customizations before updating it.`);
|
|
35523
|
-
}
|
|
35524
|
-
}
|
|
35525
|
-
copyFileSync(bundled, destination);
|
|
35526
|
-
writeManagedState(context.root, packaged);
|
|
35527
|
-
return destination;
|
|
35528
|
-
}
|
|
35529
|
-
var COMPOSE_FILE2 = "docker-compose.prod.yml", SIM_COMPOSE_MARKER2 = "ghcr.io/simstudioai/simstudio", MANAGED_STATE_FILE = ".sim-setup.json";
|
|
35530
|
-
var init_compose_asset = __esm(() => {
|
|
35531
|
-
init_context();
|
|
35532
|
-
});
|
|
35533
|
-
|
|
35534
|
-
// src/compose-project.ts
|
|
35535
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
35536
|
-
import path7 from "node:path";
|
|
35537
|
-
function standaloneComposeProjectName(root) {
|
|
35538
|
-
const digest = createHash2("sha256").update(path7.resolve(root)).digest("hex").slice(0, 12);
|
|
35539
|
-
return `sim-${digest}`;
|
|
35540
|
-
}
|
|
35541
|
-
function legacyComposeProjectName(root) {
|
|
35542
|
-
const name = path7.basename(path7.resolve(root)).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
35543
|
-
if (!/^[a-z0-9]/.test(name)) {
|
|
35544
|
-
throw new Error(`Cannot derive the existing Compose project name from ${root}; set COMPOSE_PROJECT_NAME in its .env file.`);
|
|
35545
|
-
}
|
|
35546
|
-
return name;
|
|
35547
|
-
}
|
|
35548
|
-
var init_compose_project = () => {};
|
|
35549
|
-
|
|
35550
|
-
// src/docker.ts
|
|
35551
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
35552
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
35553
|
-
import { homedir } from "node:os";
|
|
35554
|
-
import { join } from "node:path";
|
|
35555
|
-
function daemonUp() {
|
|
35556
|
-
return spawnSync4("docker", ["info"], { stdio: "ignore" }).status === 0;
|
|
35557
|
-
}
|
|
35558
|
-
function installed() {
|
|
35559
|
-
return executableExists("docker");
|
|
35560
|
-
}
|
|
35561
|
-
function orbstackSelected() {
|
|
35562
|
-
const host = process.env.DOCKER_HOST;
|
|
35563
|
-
if (host)
|
|
35564
|
-
return host.includes(".orbstack/");
|
|
35565
|
-
const result = spawnSync4("docker", ["context", "show"], { encoding: "utf8" });
|
|
35566
|
-
return result.status === 0 && result.stdout.trim() === "orbstack";
|
|
35567
|
-
}
|
|
35568
|
-
function appInstalled(app) {
|
|
35569
|
-
return APP_DIRS.some((dir) => existsSync7(join(dir, app.bundle)));
|
|
35570
|
-
}
|
|
35571
|
-
function macDockerApp() {
|
|
35572
|
-
if (orbstackSelected())
|
|
35573
|
-
return { app: ORBSTACK_APP, explicit: true };
|
|
35574
|
-
const orbstackOnly = appInstalled(ORBSTACK_APP) && !appInstalled(DOCKER_DESKTOP_APP);
|
|
35575
|
-
return { app: orbstackOnly ? ORBSTACK_APP : DOCKER_DESKTOP_APP, explicit: false };
|
|
35576
|
-
}
|
|
35577
|
-
function openApp(app) {
|
|
35578
|
-
return spawnSync4("open", ["-a", app.name], { stdio: "ignore" }).status === 0;
|
|
35579
|
-
}
|
|
35580
|
-
function startDockerApp({ app, explicit }) {
|
|
35581
|
-
if (openApp(app))
|
|
35582
|
-
return app;
|
|
35583
|
-
if (explicit)
|
|
35584
|
-
return null;
|
|
35585
|
-
const other = app === ORBSTACK_APP ? DOCKER_DESKTOP_APP : ORBSTACK_APP;
|
|
35586
|
-
return openApp(other) ? other : null;
|
|
35587
|
-
}
|
|
35588
|
-
function launchFailed(required, message, hints) {
|
|
35589
|
-
if (required)
|
|
35590
|
-
throw new SetupError(message, hints);
|
|
35591
|
-
log2.warn([message, ...hints].join(`
|
|
35592
|
-
`));
|
|
35593
|
-
return false;
|
|
35594
|
-
}
|
|
35595
|
-
async function ensureDocker(required) {
|
|
35596
|
-
if (daemonUp())
|
|
35597
|
-
return true;
|
|
35598
|
-
if (!installed()) {
|
|
35599
|
-
if (required)
|
|
35600
|
-
throw new SetupError("Docker is not installed.", INSTALL_HINTS);
|
|
35601
|
-
return false;
|
|
35602
|
-
}
|
|
35603
|
-
if (process.platform !== "darwin") {
|
|
35604
|
-
if (required) {
|
|
35605
|
-
throw new SetupError("Docker is installed but the daemon is not running.", [
|
|
35606
|
-
`start it: ${theme.command("sudo systemctl start docker")} (Linux)`
|
|
35607
|
-
]);
|
|
35608
|
-
}
|
|
35609
|
-
return false;
|
|
35610
|
-
}
|
|
35611
|
-
const choice = macDockerApp();
|
|
35612
|
-
const launch = await confirm2({
|
|
35613
|
-
message: `Docker is installed but not running — start ${choice.app.name} now?`,
|
|
35614
|
-
initialValue: true
|
|
35615
|
-
});
|
|
35616
|
-
if (!launch) {
|
|
35617
|
-
if (required) {
|
|
35618
|
-
throw new SetupError("Docker is required for this mode.", [
|
|
35619
|
-
`start ${choice.app.name}, then re-run the wizard`
|
|
35620
|
-
]);
|
|
35621
|
-
}
|
|
35622
|
-
return false;
|
|
35623
|
-
}
|
|
35624
|
-
const app = startDockerApp(choice);
|
|
35625
|
-
if (!app) {
|
|
35626
|
-
return choice.explicit ? launchFailed(required, "The docker CLI is pointed at OrbStack, which is not installed.", [
|
|
35627
|
-
`reinstall it: ${theme.command("brew install orbstack")}`,
|
|
35628
|
-
`or point the CLI elsewhere: unset DOCKER_HOST and DOCKER_CONTEXT, then ${theme.command("docker context use <name>")}`
|
|
35629
|
-
]) : launchFailed(required, "Found the docker CLI, but no app to start.", NO_APP_HINTS);
|
|
35630
|
-
}
|
|
35631
|
-
const spin = spinner2();
|
|
35632
|
-
spin.start(`Waiting for the Docker daemon (${app.name})…`);
|
|
35633
|
-
const up = await waitFor(async () => daemonUp(), 90000, 2000);
|
|
35634
|
-
spin.stop(up ? "Docker is running" : `${glyph.fail} daemon did not come up`);
|
|
35635
|
-
if (!up) {
|
|
35636
|
-
return launchFailed(required, `${app.name} did not start within 90s.`, [
|
|
35637
|
-
app === ORBSTACK_APP ? "open OrbStack manually once to finish its first-run setup, then re-run" : "first-ever launch needs a GUI license acceptance — open Docker Desktop manually once, then re-run"
|
|
35638
|
-
]);
|
|
35639
|
-
}
|
|
35640
|
-
return true;
|
|
35641
|
-
}
|
|
35642
|
-
var INSTALL_HINTS, NO_APP_HINTS, ORBSTACK_APP, DOCKER_DESKTOP_APP, APP_DIRS;
|
|
35643
|
-
var init_docker = __esm(() => {
|
|
35644
|
-
init_errors();
|
|
35645
|
-
init_executables();
|
|
35646
|
-
init_probes();
|
|
35647
|
-
init_prompter();
|
|
35648
|
-
init_theme();
|
|
35649
|
-
INSTALL_HINTS = [
|
|
35650
|
-
"install Docker Desktop: https://docker.com/products/docker-desktop",
|
|
35651
|
-
`or OrbStack (lighter on macOS): ${theme.command("brew install orbstack")}`
|
|
35652
|
-
];
|
|
35653
|
-
NO_APP_HINTS = [
|
|
35654
|
-
...INSTALL_HINTS,
|
|
35655
|
-
`or start your existing runtime its own way, e.g. ${theme.command("colima start")}`
|
|
35656
|
-
];
|
|
35657
|
-
ORBSTACK_APP = { name: "OrbStack", bundle: "OrbStack.app" };
|
|
35658
|
-
DOCKER_DESKTOP_APP = { name: "Docker", bundle: "Docker.app" };
|
|
35659
|
-
APP_DIRS = ["/Applications", join(homedir(), "Applications")];
|
|
35660
|
-
});
|
|
35661
|
-
|
|
35662
35258
|
// ../security/src/hash.ts
|
|
35663
|
-
import { createHash
|
|
35259
|
+
import { createHash } from "node:crypto";
|
|
35664
35260
|
function sha256Base64Url(input) {
|
|
35665
|
-
const hash =
|
|
35261
|
+
const hash = createHash("sha256");
|
|
35666
35262
|
if (typeof input === "string") {
|
|
35667
35263
|
hash.update(input, "utf8");
|
|
35668
35264
|
} else {
|
|
@@ -35716,19 +35312,19 @@ var init_string = __esm(() => {
|
|
|
35716
35312
|
});
|
|
35717
35313
|
|
|
35718
35314
|
// src/cli-auth.ts
|
|
35719
|
-
import { spawnSync as
|
|
35315
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
35720
35316
|
function openBrowser(url) {
|
|
35721
35317
|
if (process.env.SIM_SETUP_NO_BROWSER)
|
|
35722
35318
|
return;
|
|
35723
35319
|
if (process.platform === "win32") {
|
|
35724
|
-
|
|
35320
|
+
spawnSync4("cmd", ["/c", "start", '""', `"${url}"`], {
|
|
35725
35321
|
stdio: "ignore",
|
|
35726
35322
|
windowsVerbatimArguments: true
|
|
35727
35323
|
});
|
|
35728
35324
|
return;
|
|
35729
35325
|
}
|
|
35730
35326
|
const command = process.platform === "darwin" ? "open" : "xdg-open";
|
|
35731
|
-
|
|
35327
|
+
spawnSync4(command, [url], { stdio: "ignore" });
|
|
35732
35328
|
}
|
|
35733
35329
|
function createPairingCode() {
|
|
35734
35330
|
const chars = generateShortId(8, PAIRING_ALPHABET);
|
|
@@ -35751,8 +35347,8 @@ function normalizeAuthOrigin(origin) {
|
|
|
35751
35347
|
const prefix = parsed.pathname.replace(/\/+$/, "");
|
|
35752
35348
|
return `${parsed.origin}${prefix}`;
|
|
35753
35349
|
}
|
|
35754
|
-
function authUrl(origin,
|
|
35755
|
-
return `${normalizeAuthOrigin(origin)}${
|
|
35350
|
+
function authUrl(origin, path6) {
|
|
35351
|
+
return `${normalizeAuthOrigin(origin)}${path6}`;
|
|
35756
35352
|
}
|
|
35757
35353
|
function buildApprovalUrl(origin, request, challenge, pairing) {
|
|
35758
35354
|
const query = new URLSearchParams({ request, challenge, pairing });
|
|
@@ -36111,6 +35707,430 @@ var init_steps = __esm(() => {
|
|
|
36111
35707
|
};
|
|
36112
35708
|
});
|
|
36113
35709
|
|
|
35710
|
+
// src/feature-setup.ts
|
|
35711
|
+
var exports_feature_setup = {};
|
|
35712
|
+
__export(exports_feature_setup, {
|
|
35713
|
+
setupFeatureUsage: () => setupFeatureUsage,
|
|
35714
|
+
runFeatureSetup: () => runFeatureSetup,
|
|
35715
|
+
resolveFeatureSetupDestination: () => resolveFeatureSetupDestination,
|
|
35716
|
+
reconcileLlmSetup: () => reconcileLlmSetup
|
|
35717
|
+
});
|
|
35718
|
+
function isSetupFeatureId(value) {
|
|
35719
|
+
return SETUP_FEATURES.some((feature) => feature.id === value);
|
|
35720
|
+
}
|
|
35721
|
+
async function setupIntegration(requestedId, vars) {
|
|
35722
|
+
if (!requestedId) {
|
|
35723
|
+
throw new Error("Missing integration id. Example: npx sim-setup add integration slack");
|
|
35724
|
+
}
|
|
35725
|
+
const providerId = resolveOAuthClientCapabilityId(requestedId);
|
|
35726
|
+
if (!providerId) {
|
|
35727
|
+
throw new Error(`Unknown OAuth integration "${requestedId}". Expected one of: ${Object.keys(OAUTH_CLIENT_CAPABILITIES).join(", ")}`);
|
|
35728
|
+
}
|
|
35729
|
+
const fields = getOAuthClientSetupFields(providerId);
|
|
35730
|
+
const values2 = {};
|
|
35731
|
+
for (const field of fields) {
|
|
35732
|
+
const existing = vars.get(field.key);
|
|
35733
|
+
if (field.input === "secret") {
|
|
35734
|
+
const value = await password2({
|
|
35735
|
+
message: existing ? `${field.key} (Currently used); leave empty to keep it` : field.key,
|
|
35736
|
+
validate: (candidate) => candidate || existing ? undefined : "required"
|
|
35737
|
+
});
|
|
35738
|
+
const resolved = value || existing;
|
|
35739
|
+
if (!resolved)
|
|
35740
|
+
throw new Error(`${field.key} was not provided`);
|
|
35741
|
+
values2[field.key] = resolved;
|
|
35742
|
+
} else {
|
|
35743
|
+
values2[field.key] = await text2({
|
|
35744
|
+
message: `${field.key}${existing ? " (Currently used)" : ""}`,
|
|
35745
|
+
initialValue: existing,
|
|
35746
|
+
validate: (candidate) => candidate ? undefined : "required"
|
|
35747
|
+
});
|
|
35748
|
+
}
|
|
35749
|
+
}
|
|
35750
|
+
log2.info(`Configured the ${providerId} OAuth client.`);
|
|
35751
|
+
return values2;
|
|
35752
|
+
}
|
|
35753
|
+
function reconcileLlmSetup(providerId, values2) {
|
|
35754
|
+
const pool = LLM_KEY_POOLS[providerId];
|
|
35755
|
+
const fields = [...pool.keys, ..."fallbackKey" in pool ? [pool.fallbackKey] : []];
|
|
35756
|
+
return {
|
|
35757
|
+
values: values2,
|
|
35758
|
+
remove: fields.filter((key) => !Object.hasOwn(values2, key))
|
|
35759
|
+
};
|
|
35760
|
+
}
|
|
35761
|
+
async function setupLlm(vars) {
|
|
35762
|
+
const currentProvider = Object.entries(LLM_KEY_POOLS).find(([, pool2]) => [...pool2.keys, ..."fallbackKey" in pool2 ? [pool2.fallbackKey] : []].some((key) => vars.has(key)))?.[0];
|
|
35763
|
+
const provider = await select3({
|
|
35764
|
+
message: "LLM key pool?",
|
|
35765
|
+
options: Object.keys(LLM_KEY_POOLS).map((id) => ({
|
|
35766
|
+
value: id,
|
|
35767
|
+
label: id,
|
|
35768
|
+
hint: id === currentProvider ? "Currently used" : undefined
|
|
35769
|
+
})),
|
|
35770
|
+
initialValue: currentProvider
|
|
35771
|
+
});
|
|
35772
|
+
const pool = LLM_KEY_POOLS[provider];
|
|
35773
|
+
const keys = pool.keys;
|
|
35774
|
+
const values2 = {};
|
|
35775
|
+
for (const [index, key] of keys.entries()) {
|
|
35776
|
+
const legacyKey = index === 0 && "fallbackKey" in pool ? pool.fallbackKey : undefined;
|
|
35777
|
+
const existingKey = vars.has(key) ? key : legacyKey;
|
|
35778
|
+
const existing = existingKey ? vars.get(existingKey) : undefined;
|
|
35779
|
+
const value = await password2({
|
|
35780
|
+
message: existing ? `${key} (${existingKey} is currently used); leave empty to keep it` : `${key}${index === 0 ? "" : " (empty to finish)"}`,
|
|
35781
|
+
validate: index === 0 ? (candidate) => candidate || existing ? undefined : "required" : undefined
|
|
35782
|
+
});
|
|
35783
|
+
const resolved = value || existing;
|
|
35784
|
+
if (!resolved)
|
|
35785
|
+
break;
|
|
35786
|
+
values2[key] = resolved;
|
|
35787
|
+
}
|
|
35788
|
+
return reconcileLlmSetup(provider, values2);
|
|
35789
|
+
}
|
|
35790
|
+
async function setupChat(vars) {
|
|
35791
|
+
const overrides = mothershipOverride();
|
|
35792
|
+
const copilotKey = await promptCopilotKey(vars.get("COPILOT_API_KEY"));
|
|
35793
|
+
if (!copilotKey) {
|
|
35794
|
+
throw new Error("Chat setup did not receive an API key. No configuration was changed.");
|
|
35795
|
+
}
|
|
35796
|
+
return {
|
|
35797
|
+
...overrides,
|
|
35798
|
+
COPILOT_API_KEY: copilotKey,
|
|
35799
|
+
...chatFlagValues(copilotKey)
|
|
35800
|
+
};
|
|
35801
|
+
}
|
|
35802
|
+
function setupFeatureUsage() {
|
|
35803
|
+
return SETUP_FEATURES.map((feature) => feature.id === "integration" ? "integration <slug>" : feature.id).join(" | ");
|
|
35804
|
+
}
|
|
35805
|
+
function resolveFeatureSetupDestination(sources) {
|
|
35806
|
+
if (sources.length === 0) {
|
|
35807
|
+
throw new Error("No Sim configuration was detected. Run npx sim-setup first.");
|
|
35808
|
+
}
|
|
35809
|
+
const managed = sources.filter((source2) => source2.managedByCurrentCheckout);
|
|
35810
|
+
if (managed.length === 0) {
|
|
35811
|
+
throw new Error("No effective configuration is safely writable by this checkout. Process overrides, higher-precedence development env files, external Compose projects, and Helm releases must be updated at their source. Run npx sim-setup config for the detected sources.");
|
|
35812
|
+
}
|
|
35813
|
+
if (managed.length > 1) {
|
|
35814
|
+
throw new Error(`More than one effective configuration is writable by this checkout (${managed.map((source2) => source2.label).join(", ")}). Run npx sim-setup config and remove the ambiguity before configuring a feature.`);
|
|
35815
|
+
}
|
|
35816
|
+
const source = managed[0];
|
|
35817
|
+
if (!source.values) {
|
|
35818
|
+
throw new Error(`${source.label} is managed by this checkout, but its effective environment could not be resolved. Run npx sim-setup config and fix the reported source error first.`);
|
|
35819
|
+
}
|
|
35820
|
+
if (source.kind === "helm") {
|
|
35821
|
+
throw new Error("Helm configuration cannot be updated by npx sim-setup add. Update the release Secret or values and upgrade the release.");
|
|
35822
|
+
}
|
|
35823
|
+
return {
|
|
35824
|
+
source,
|
|
35825
|
+
target: source.kind === "compose" ? "root" : "sim",
|
|
35826
|
+
vars: source.values,
|
|
35827
|
+
containerized: source.kind === "compose"
|
|
35828
|
+
};
|
|
35829
|
+
}
|
|
35830
|
+
async function runFeatureSetup(feature, args) {
|
|
35831
|
+
if (!isSetupFeatureId(feature)) {
|
|
35832
|
+
throw new Error(`Unknown setup feature "${feature}". Expected: ${setupFeatureUsage()}`);
|
|
35833
|
+
}
|
|
35834
|
+
const destination = resolveFeatureSetupDestination(discoverConfigurationSources());
|
|
35835
|
+
const { target, vars } = destination;
|
|
35836
|
+
let values2;
|
|
35837
|
+
let remove;
|
|
35838
|
+
const capabilitySetup = getCapabilitySetup(feature);
|
|
35839
|
+
if (capabilitySetup) {
|
|
35840
|
+
const result = await promptCapabilitySetup(capabilitySetup, vars, {
|
|
35841
|
+
containerized: destination.containerized
|
|
35842
|
+
});
|
|
35843
|
+
values2 = result.values;
|
|
35844
|
+
remove = result.remove;
|
|
35845
|
+
} else if (feature === "chat") {
|
|
35846
|
+
values2 = await setupChat(vars);
|
|
35847
|
+
remove = [];
|
|
35848
|
+
} else if (feature === "integration") {
|
|
35849
|
+
values2 = await setupIntegration(args[0], vars);
|
|
35850
|
+
remove = [];
|
|
35851
|
+
} else if (feature === "llm") {
|
|
35852
|
+
const result = await setupLlm(vars);
|
|
35853
|
+
values2 = result.values;
|
|
35854
|
+
remove = result.remove;
|
|
35855
|
+
} else {
|
|
35856
|
+
throw new Error(`Setup feature ${feature} has no handler`);
|
|
35857
|
+
}
|
|
35858
|
+
reconcileEnvValues(target, remove, values2);
|
|
35859
|
+
const label = SETUP_FEATURES.find((item) => item.id === feature)?.label;
|
|
35860
|
+
outro2(theme.accent(destination.containerized ? `${label} written to .env. Recreate the app container for it to take effect.` : `${label} configured.`));
|
|
35861
|
+
}
|
|
35862
|
+
var init_feature_setup = __esm(() => {
|
|
35863
|
+
init_env_capabilities();
|
|
35864
|
+
init_capability_config();
|
|
35865
|
+
init_capability_setup();
|
|
35866
|
+
init_configuration_sources();
|
|
35867
|
+
init_env_files();
|
|
35868
|
+
init_prompter();
|
|
35869
|
+
init_steps();
|
|
35870
|
+
init_theme();
|
|
35871
|
+
});
|
|
35872
|
+
|
|
35873
|
+
// src/doctor.ts
|
|
35874
|
+
var exports_doctor = {};
|
|
35875
|
+
__export(exports_doctor, {
|
|
35876
|
+
runDoctor: () => runDoctor
|
|
35877
|
+
});
|
|
35878
|
+
function render(findings, fixedCount) {
|
|
35879
|
+
console.log(`
|
|
35880
|
+
${theme.heading("◆ Sim doctor")}
|
|
35881
|
+
`);
|
|
35882
|
+
for (const group of GROUP_ORDER) {
|
|
35883
|
+
const groupFindings = findings.filter((f2) => f2.group === group);
|
|
35884
|
+
if (groupFindings.length === 0)
|
|
35885
|
+
continue;
|
|
35886
|
+
console.log(theme.heading(GROUP_TITLES[group]));
|
|
35887
|
+
for (const finding of groupFindings) {
|
|
35888
|
+
console.log(` ${glyph[finding.status]} ${finding.message}`);
|
|
35889
|
+
if (finding.fix && finding.status !== "pass") {
|
|
35890
|
+
console.log(` ${theme.muted(`fix: ${finding.fix}`)}`);
|
|
35891
|
+
}
|
|
35892
|
+
}
|
|
35893
|
+
console.log();
|
|
35894
|
+
}
|
|
35895
|
+
const counts = {
|
|
35896
|
+
pass: findings.filter((f2) => f2.status === "pass").length,
|
|
35897
|
+
warn: findings.filter((f2) => f2.status === "warn").length,
|
|
35898
|
+
fail: findings.filter((f2) => f2.status === "fail").length
|
|
35899
|
+
};
|
|
35900
|
+
const summary = [`${counts.pass} passed`];
|
|
35901
|
+
if (counts.warn)
|
|
35902
|
+
summary.push(theme.warn(`${counts.warn} warning${counts.warn > 1 ? "s" : ""}`));
|
|
35903
|
+
if (counts.fail)
|
|
35904
|
+
summary.push(theme.error(`${counts.fail} failed`));
|
|
35905
|
+
if (fixedCount)
|
|
35906
|
+
summary.push(theme.success(`${fixedCount} fixed`));
|
|
35907
|
+
console.log(summary.join(theme.muted(" · ")));
|
|
35908
|
+
}
|
|
35909
|
+
async function runDoctor(options) {
|
|
35910
|
+
let findings = await runChecks(loadCheckContext(true));
|
|
35911
|
+
let fixedCount = 0;
|
|
35912
|
+
if (options.fix) {
|
|
35913
|
+
const fixable = findings.filter((f2) => (f2.status === "fail" || f2.status === "warn") && f2.autofix);
|
|
35914
|
+
for (const finding of fixable) {
|
|
35915
|
+
finding.autofix?.();
|
|
35916
|
+
fixedCount++;
|
|
35917
|
+
}
|
|
35918
|
+
if (fixedCount > 0)
|
|
35919
|
+
findings = await runChecks(loadCheckContext(true));
|
|
35920
|
+
}
|
|
35921
|
+
if (options.json) {
|
|
35922
|
+
console.log(JSON.stringify(findings.map(({ autofix: _autofix, ...rest }) => rest), null, 2));
|
|
35923
|
+
} else {
|
|
35924
|
+
render(findings, fixedCount);
|
|
35925
|
+
}
|
|
35926
|
+
return findings.some((f2) => f2.status === "fail") ? 1 : 0;
|
|
35927
|
+
}
|
|
35928
|
+
var GROUP_TITLES, GROUP_ORDER;
|
|
35929
|
+
var init_doctor = __esm(() => {
|
|
35930
|
+
init_checks();
|
|
35931
|
+
init_theme();
|
|
35932
|
+
GROUP_TITLES = {
|
|
35933
|
+
files: "Env files",
|
|
35934
|
+
schema: "Schema",
|
|
35935
|
+
consistency: "Consistency",
|
|
35936
|
+
coherence: "Coherence",
|
|
35937
|
+
live: "Live"
|
|
35938
|
+
};
|
|
35939
|
+
GROUP_ORDER = ["files", "schema", "consistency", "coherence", "live"];
|
|
35940
|
+
});
|
|
35941
|
+
|
|
35942
|
+
// src/compose-asset.ts
|
|
35943
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
35944
|
+
import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
|
|
35945
|
+
import path6 from "node:path";
|
|
35946
|
+
import { fileURLToPath } from "node:url";
|
|
35947
|
+
function sha2562(contents) {
|
|
35948
|
+
return createHash2("sha256").update(contents).digest("hex");
|
|
35949
|
+
}
|
|
35950
|
+
function readManagedState(root) {
|
|
35951
|
+
const file = path6.join(root, MANAGED_STATE_FILE);
|
|
35952
|
+
if (!existsSync6(file))
|
|
35953
|
+
return null;
|
|
35954
|
+
const parsed = JSON.parse(readFileSync6(file, "utf8"));
|
|
35955
|
+
if (typeof parsed !== "object" || parsed === null || !("schemaVersion" in parsed) || parsed.schemaVersion !== 1 || !("composeSha256" in parsed) || typeof parsed.composeSha256 !== "string") {
|
|
35956
|
+
throw new Error(`${file} is not a valid sim-setup managed-state file`);
|
|
35957
|
+
}
|
|
35958
|
+
return { schemaVersion: 1, composeSha256: parsed.composeSha256 };
|
|
35959
|
+
}
|
|
35960
|
+
function writeManagedState(root, contents) {
|
|
35961
|
+
const state = { schemaVersion: 1, composeSha256: sha2562(contents) };
|
|
35962
|
+
writeFileSync2(path6.join(root, MANAGED_STATE_FILE), `${JSON.stringify(state, null, 2)}
|
|
35963
|
+
`);
|
|
35964
|
+
}
|
|
35965
|
+
function packagedComposeFile() {
|
|
35966
|
+
const moduleDirectory = path6.dirname(fileURLToPath(import.meta.url));
|
|
35967
|
+
return path6.basename(moduleDirectory) === "src" ? path6.resolve(moduleDirectory, "../../..", COMPOSE_FILE2) : path6.join(moduleDirectory, COMPOSE_FILE2);
|
|
35968
|
+
}
|
|
35969
|
+
function ensureProductionComposeFile(context = SETUP_CONTEXT) {
|
|
35970
|
+
const destination = path6.join(context.root, COMPOSE_FILE2);
|
|
35971
|
+
if (context.kind === "source") {
|
|
35972
|
+
if (!existsSync6(destination)) {
|
|
35973
|
+
throw new Error(`Sim source checkout is missing ${COMPOSE_FILE2}`);
|
|
35974
|
+
}
|
|
35975
|
+
return destination;
|
|
35976
|
+
}
|
|
35977
|
+
const bundled = packagedComposeFile();
|
|
35978
|
+
if (!existsSync6(bundled)) {
|
|
35979
|
+
throw new Error(`The sim-setup package is missing its bundled ${COMPOSE_FILE2}`);
|
|
35980
|
+
}
|
|
35981
|
+
mkdirSync2(context.root, { recursive: true });
|
|
35982
|
+
const packaged = readFileSync6(bundled, "utf8");
|
|
35983
|
+
if (existsSync6(destination)) {
|
|
35984
|
+
const current = readFileSync6(destination, "utf8");
|
|
35985
|
+
if (current === packaged) {
|
|
35986
|
+
writeManagedState(context.root, packaged);
|
|
35987
|
+
return destination;
|
|
35988
|
+
}
|
|
35989
|
+
if (!current.includes(SIM_COMPOSE_MARKER2)) {
|
|
35990
|
+
throw new Error(`${destination} exists but is not a recognized Sim Compose file`);
|
|
35991
|
+
}
|
|
35992
|
+
const managed = readManagedState(context.root);
|
|
35993
|
+
if (!managed || managed.composeSha256 !== sha2562(current)) {
|
|
35994
|
+
throw new Error(`${destination} has local changes; preserve or remove your customizations before updating it.`);
|
|
35995
|
+
}
|
|
35996
|
+
}
|
|
35997
|
+
copyFileSync(bundled, destination);
|
|
35998
|
+
writeManagedState(context.root, packaged);
|
|
35999
|
+
return destination;
|
|
36000
|
+
}
|
|
36001
|
+
var COMPOSE_FILE2 = "docker-compose.prod.yml", SIM_COMPOSE_MARKER2 = "ghcr.io/simstudioai/simstudio", MANAGED_STATE_FILE = ".sim-setup.json";
|
|
36002
|
+
var init_compose_asset = __esm(() => {
|
|
36003
|
+
init_context();
|
|
36004
|
+
});
|
|
36005
|
+
|
|
36006
|
+
// src/compose-project.ts
|
|
36007
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
36008
|
+
import path7 from "node:path";
|
|
36009
|
+
function standaloneComposeProjectName(root) {
|
|
36010
|
+
const digest = createHash3("sha256").update(path7.resolve(root)).digest("hex").slice(0, 12);
|
|
36011
|
+
return `sim-${digest}`;
|
|
36012
|
+
}
|
|
36013
|
+
function legacyComposeProjectName(root) {
|
|
36014
|
+
const name = path7.basename(path7.resolve(root)).toLowerCase().replace(/[^a-z0-9_-]/g, "");
|
|
36015
|
+
if (!/^[a-z0-9]/.test(name)) {
|
|
36016
|
+
throw new Error(`Cannot derive the existing Compose project name from ${root}; set COMPOSE_PROJECT_NAME in its .env file.`);
|
|
36017
|
+
}
|
|
36018
|
+
return name;
|
|
36019
|
+
}
|
|
36020
|
+
var init_compose_project = () => {};
|
|
36021
|
+
|
|
36022
|
+
// src/docker.ts
|
|
36023
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
36024
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
36025
|
+
import { homedir } from "node:os";
|
|
36026
|
+
import { join } from "node:path";
|
|
36027
|
+
function daemonUp() {
|
|
36028
|
+
return spawnSync5("docker", ["info"], { stdio: "ignore" }).status === 0;
|
|
36029
|
+
}
|
|
36030
|
+
function installed() {
|
|
36031
|
+
return executableExists("docker");
|
|
36032
|
+
}
|
|
36033
|
+
function orbstackSelected() {
|
|
36034
|
+
const host = process.env.DOCKER_HOST;
|
|
36035
|
+
if (host)
|
|
36036
|
+
return host.includes(".orbstack/");
|
|
36037
|
+
const result = spawnSync5("docker", ["context", "show"], { encoding: "utf8" });
|
|
36038
|
+
return result.status === 0 && result.stdout.trim() === "orbstack";
|
|
36039
|
+
}
|
|
36040
|
+
function appInstalled(app) {
|
|
36041
|
+
return APP_DIRS.some((dir) => existsSync7(join(dir, app.bundle)));
|
|
36042
|
+
}
|
|
36043
|
+
function macDockerApp() {
|
|
36044
|
+
if (orbstackSelected())
|
|
36045
|
+
return { app: ORBSTACK_APP, explicit: true };
|
|
36046
|
+
const orbstackOnly = appInstalled(ORBSTACK_APP) && !appInstalled(DOCKER_DESKTOP_APP);
|
|
36047
|
+
return { app: orbstackOnly ? ORBSTACK_APP : DOCKER_DESKTOP_APP, explicit: false };
|
|
36048
|
+
}
|
|
36049
|
+
function openApp(app) {
|
|
36050
|
+
return spawnSync5("open", ["-a", app.name], { stdio: "ignore" }).status === 0;
|
|
36051
|
+
}
|
|
36052
|
+
function startDockerApp({ app, explicit }) {
|
|
36053
|
+
if (openApp(app))
|
|
36054
|
+
return app;
|
|
36055
|
+
if (explicit)
|
|
36056
|
+
return null;
|
|
36057
|
+
const other = app === ORBSTACK_APP ? DOCKER_DESKTOP_APP : ORBSTACK_APP;
|
|
36058
|
+
return openApp(other) ? other : null;
|
|
36059
|
+
}
|
|
36060
|
+
function launchFailed(required, message, hints) {
|
|
36061
|
+
if (required)
|
|
36062
|
+
throw new SetupError(message, hints);
|
|
36063
|
+
log2.warn([message, ...hints].join(`
|
|
36064
|
+
`));
|
|
36065
|
+
return false;
|
|
36066
|
+
}
|
|
36067
|
+
async function ensureDocker(required) {
|
|
36068
|
+
if (daemonUp())
|
|
36069
|
+
return true;
|
|
36070
|
+
if (!installed()) {
|
|
36071
|
+
if (required)
|
|
36072
|
+
throw new SetupError("Docker is not installed.", INSTALL_HINTS);
|
|
36073
|
+
return false;
|
|
36074
|
+
}
|
|
36075
|
+
if (process.platform !== "darwin") {
|
|
36076
|
+
if (required) {
|
|
36077
|
+
throw new SetupError("Docker is installed but the daemon is not running.", [
|
|
36078
|
+
`start it: ${theme.command("sudo systemctl start docker")} (Linux)`
|
|
36079
|
+
]);
|
|
36080
|
+
}
|
|
36081
|
+
return false;
|
|
36082
|
+
}
|
|
36083
|
+
const choice = macDockerApp();
|
|
36084
|
+
const launch = await confirm2({
|
|
36085
|
+
message: `Docker is installed but not running — start ${choice.app.name} now?`,
|
|
36086
|
+
initialValue: true
|
|
36087
|
+
});
|
|
36088
|
+
if (!launch) {
|
|
36089
|
+
if (required) {
|
|
36090
|
+
throw new SetupError("Docker is required for this mode.", [
|
|
36091
|
+
`start ${choice.app.name}, then re-run the wizard`
|
|
36092
|
+
]);
|
|
36093
|
+
}
|
|
36094
|
+
return false;
|
|
36095
|
+
}
|
|
36096
|
+
const app = startDockerApp(choice);
|
|
36097
|
+
if (!app) {
|
|
36098
|
+
return choice.explicit ? launchFailed(required, "The docker CLI is pointed at OrbStack, which is not installed.", [
|
|
36099
|
+
`reinstall it: ${theme.command("brew install orbstack")}`,
|
|
36100
|
+
`or point the CLI elsewhere: unset DOCKER_HOST and DOCKER_CONTEXT, then ${theme.command("docker context use <name>")}`
|
|
36101
|
+
]) : launchFailed(required, "Found the docker CLI, but no app to start.", NO_APP_HINTS);
|
|
36102
|
+
}
|
|
36103
|
+
const spin = spinner2();
|
|
36104
|
+
spin.start(`Waiting for the Docker daemon (${app.name})…`);
|
|
36105
|
+
const up = await waitFor(async () => daemonUp(), 90000, 2000);
|
|
36106
|
+
spin.stop(up ? "Docker is running" : `${glyph.fail} daemon did not come up`);
|
|
36107
|
+
if (!up) {
|
|
36108
|
+
return launchFailed(required, `${app.name} did not start within 90s.`, [
|
|
36109
|
+
app === ORBSTACK_APP ? "open OrbStack manually once to finish its first-run setup, then re-run" : "first-ever launch needs a GUI license acceptance — open Docker Desktop manually once, then re-run"
|
|
36110
|
+
]);
|
|
36111
|
+
}
|
|
36112
|
+
return true;
|
|
36113
|
+
}
|
|
36114
|
+
var INSTALL_HINTS, NO_APP_HINTS, ORBSTACK_APP, DOCKER_DESKTOP_APP, APP_DIRS;
|
|
36115
|
+
var init_docker = __esm(() => {
|
|
36116
|
+
init_errors();
|
|
36117
|
+
init_executables();
|
|
36118
|
+
init_probes();
|
|
36119
|
+
init_prompter();
|
|
36120
|
+
init_theme();
|
|
36121
|
+
INSTALL_HINTS = [
|
|
36122
|
+
"install Docker Desktop: https://docker.com/products/docker-desktop",
|
|
36123
|
+
`or OrbStack (lighter on macOS): ${theme.command("brew install orbstack")}`
|
|
36124
|
+
];
|
|
36125
|
+
NO_APP_HINTS = [
|
|
36126
|
+
...INSTALL_HINTS,
|
|
36127
|
+
`or start your existing runtime its own way, e.g. ${theme.command("colima start")}`
|
|
36128
|
+
];
|
|
36129
|
+
ORBSTACK_APP = { name: "OrbStack", bundle: "OrbStack.app" };
|
|
36130
|
+
DOCKER_DESKTOP_APP = { name: "Docker", bundle: "Docker.app" };
|
|
36131
|
+
APP_DIRS = ["/Applications", join(homedir(), "Applications")];
|
|
36132
|
+
});
|
|
36133
|
+
|
|
36114
36134
|
// src/urls.ts
|
|
36115
36135
|
var APP_URL = "http://localhost:3000", APP_SIGNUP_URL;
|
|
36116
36136
|
var init_urls = __esm(() => {
|
|
@@ -38103,7 +38123,7 @@ function parseSetupArguments(rawArgs) {
|
|
|
38103
38123
|
init_errors();
|
|
38104
38124
|
init_theme();
|
|
38105
38125
|
init_version();
|
|
38106
|
-
var SETUP_FEATURES2 = "email | storage | sandbox | jobs | cache | knowledge | knowledge-embeddings | llm | integration <slug>";
|
|
38126
|
+
var SETUP_FEATURES2 = "email | storage | sandbox | jobs | cache | knowledge | knowledge-embeddings | chat | llm | integration <slug>";
|
|
38107
38127
|
var USAGE = `Usage:
|
|
38108
38128
|
sim-setup [--quick] [--dir <path>] [--mode compose|dev|k8s]
|
|
38109
38129
|
sim-setup config show configured capabilities and integrations
|