create-op-node 0.10.5 → 0.10.6
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/cli.js +222 -137
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1989,6 +1989,10 @@ async function locateOrCloneRepo(input) {
|
|
|
1989
1989
|
return { kind: "cloned", path: target };
|
|
1990
1990
|
}
|
|
1991
1991
|
|
|
1992
|
+
// src/lib/assert.ts
|
|
1993
|
+
function assertNever(_value) {
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1992
1996
|
// src/commands/bootstrap.ts
|
|
1993
1997
|
var PUBLIC_PROFILES = ["public"];
|
|
1994
1998
|
var LOCAL_PROFILES = [];
|
|
@@ -2036,6 +2040,33 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2036
2040
|
).default(false)
|
|
2037
2041
|
).addOption(new Option("-y, --yes", "Skip confirmation prompts").default(false)).action(async (opts) => {
|
|
2038
2042
|
p3.intro(pc2.bgCyan(pc2.black(" create-op-node bootstrap ")));
|
|
2043
|
+
const nodeType = await selectNodeType(opts);
|
|
2044
|
+
const region = await resolveRegion(opts);
|
|
2045
|
+
const owner = opts.owner ?? "OpusPopuli";
|
|
2046
|
+
const repoName = `opuspopuli-node-${region}`;
|
|
2047
|
+
const composeFile = opts.composeFile ?? defaultComposeFiles(opts);
|
|
2048
|
+
const llmModelChoice = opts.llmModel ?? await selectLlmModel(opts);
|
|
2049
|
+
const [embeddingModel, llmModel] = resolveModels({
|
|
2050
|
+
llmModel: llmModelChoice,
|
|
2051
|
+
...opts.embeddingModel !== void 0 ? { embeddingModel: opts.embeddingModel } : {}
|
|
2052
|
+
});
|
|
2053
|
+
await runSystemChecksPhase();
|
|
2054
|
+
await runBrewPhase(opts);
|
|
2055
|
+
await ensureGhAuth();
|
|
2056
|
+
await promptTailscaleSignin();
|
|
2057
|
+
const repoPath = await locateRegionRepoPhase(opts, owner, repoName);
|
|
2058
|
+
await unlockKeychainPhase();
|
|
2059
|
+
const secrets = await collectSecretsPhase({ region, nodeType, opts });
|
|
2060
|
+
await runLaunchAgentPhase({ opts, secrets, llmModel, embeddingModel });
|
|
2061
|
+
await installWrapperPhase({ repoPath, region, promptServiceUrl: secrets.promptServiceUrl });
|
|
2062
|
+
await loginGhcrPhase();
|
|
2063
|
+
await runOllamaPhase({ opts, embeddingModel, llmModel });
|
|
2064
|
+
await runStackPhase({ opts, repoPath, region, composeFile, secrets, llmModel, embeddingModel });
|
|
2065
|
+
});
|
|
2066
|
+
function defaultComposeFiles(opts) {
|
|
2067
|
+
return opts.localOnly ? ["docker-compose-prod.yml"] : ["docker-compose-prod.yml", "docker-compose-backup.yml"];
|
|
2068
|
+
}
|
|
2069
|
+
async function selectNodeType(opts) {
|
|
2039
2070
|
const nodeType = opts.nodeType ? opts.nodeType : unwrap(
|
|
2040
2071
|
await p3.select({
|
|
2041
2072
|
message: "What kind of node are you provisioning?",
|
|
@@ -2062,22 +2093,18 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2062
2093
|
);
|
|
2063
2094
|
process.exit(1);
|
|
2064
2095
|
}
|
|
2065
|
-
|
|
2096
|
+
return nodeType;
|
|
2097
|
+
}
|
|
2098
|
+
async function resolveRegion(opts) {
|
|
2099
|
+
return opts.region ? opts.region : unwrap(
|
|
2066
2100
|
await p3.text({
|
|
2067
2101
|
message: "Region label (the slug used during init \u2014 e.g. us-ca)?",
|
|
2068
2102
|
placeholder: "us-ca",
|
|
2069
2103
|
validate: (v) => /^[a-z0-9-]{2,32}$/.test(v ?? "") ? void 0 : "lowercase letters, digits, hyphens; 2\u201332 chars"
|
|
2070
2104
|
})
|
|
2071
2105
|
);
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
const composeFileDefault = opts.localOnly ? ["docker-compose-prod.yml"] : ["docker-compose-prod.yml", "docker-compose-backup.yml"];
|
|
2075
|
-
const composeFile = opts.composeFile ?? composeFileDefault;
|
|
2076
|
-
const llmModelChoice = opts.llmModel ?? await selectLlmModel(opts);
|
|
2077
|
-
const [embeddingModel, llmModel] = resolveModels({
|
|
2078
|
-
llmModel: llmModelChoice,
|
|
2079
|
-
...opts.embeddingModel !== void 0 ? { embeddingModel: opts.embeddingModel } : {}
|
|
2080
|
-
});
|
|
2106
|
+
}
|
|
2107
|
+
async function runSystemChecksPhase() {
|
|
2081
2108
|
const sysSpin = p3.spinner();
|
|
2082
2109
|
sysSpin.start("Inspecting macOS\u2026");
|
|
2083
2110
|
const snap = await inspectSystem();
|
|
@@ -2122,54 +2149,55 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2122
2149
|
if (!r.ok) p3.note(`${pc2.red("\u2717")} ${r.reason}`, "pmset failed");
|
|
2123
2150
|
}
|
|
2124
2151
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
"
|
|
2137
|
-
)
|
|
2138
|
-
|
|
2139
|
-
}
|
|
2140
|
-
const brewSpin = p3.spinner();
|
|
2141
|
-
brewSpin.start("Installing Studio packages\u2026 (~5\u201315 min first run)");
|
|
2142
|
-
const report = await installPackages(STUDIO_PACKAGES, (pkg, status) => {
|
|
2143
|
-
brewSpin.message(`${status}: ${pkg.name}`);
|
|
2144
|
-
});
|
|
2145
|
-
brewSpin.stop(
|
|
2146
|
-
report.failed.length === 0 ? pc2.green(
|
|
2147
|
-
`\u2713 ${report.installed.length} installed, ${report.alreadyPresent.length} already present`
|
|
2148
|
-
) : pc2.yellow(
|
|
2149
|
-
`\u26A0 ${report.installed.length} installed, ${report.alreadyPresent.length} present, ${report.failed.length} failed`
|
|
2150
|
-
)
|
|
2152
|
+
}
|
|
2153
|
+
async function runBrewPhase(opts) {
|
|
2154
|
+
if (opts.skipBrew) return;
|
|
2155
|
+
const brewInfo = await detectBrew();
|
|
2156
|
+
if (!brewInfo.installed) {
|
|
2157
|
+
p3.note(
|
|
2158
|
+
[
|
|
2159
|
+
`Homebrew is not installed. Open another shell and run:`,
|
|
2160
|
+
"",
|
|
2161
|
+
pc2.cyan(HOMEBREW_INSTALL_COMMAND),
|
|
2162
|
+
"",
|
|
2163
|
+
pc2.dim("Then come back and press Enter.")
|
|
2164
|
+
].join("\n"),
|
|
2165
|
+
"Manual step"
|
|
2151
2166
|
);
|
|
2152
|
-
|
|
2153
|
-
p3.note(
|
|
2154
|
-
[
|
|
2155
|
-
report.failed.map((f) => `${pc2.red("\u2022")} ${f.pkg.name}: ${f.reason}`).join("\n"),
|
|
2156
|
-
"",
|
|
2157
|
-
pc2.dim("A partial install usually fails downstream (Docker / Ollama / compose)."),
|
|
2158
|
-
pc2.dim("Install the failed packages manually and re-run with --skip-brew.")
|
|
2159
|
-
].join("\n"),
|
|
2160
|
-
"Brew failures"
|
|
2161
|
-
);
|
|
2162
|
-
const cont = unwrap(
|
|
2163
|
-
await p3.confirm({
|
|
2164
|
-
message: "Continue anyway? (Default no \u2014 recommended to fix and re-run.)",
|
|
2165
|
-
initialValue: false
|
|
2166
|
-
})
|
|
2167
|
-
);
|
|
2168
|
-
if (!cont) process.exit(1);
|
|
2169
|
-
}
|
|
2167
|
+
unwrap(await p3.confirm({ message: "Homebrew installed?", initialValue: true }));
|
|
2170
2168
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2169
|
+
const brewSpin = p3.spinner();
|
|
2170
|
+
brewSpin.start("Installing Studio packages\u2026 (~5\u201315 min first run)");
|
|
2171
|
+
const report = await installPackages(STUDIO_PACKAGES, (pkg, status) => {
|
|
2172
|
+
brewSpin.message(`${status}: ${pkg.name}`);
|
|
2173
|
+
});
|
|
2174
|
+
brewSpin.stop(
|
|
2175
|
+
report.failed.length === 0 ? pc2.green(
|
|
2176
|
+
`\u2713 ${report.installed.length} installed, ${report.alreadyPresent.length} already present`
|
|
2177
|
+
) : pc2.yellow(
|
|
2178
|
+
`\u26A0 ${report.installed.length} installed, ${report.alreadyPresent.length} present, ${report.failed.length} failed`
|
|
2179
|
+
)
|
|
2180
|
+
);
|
|
2181
|
+
if (report.failed.length > 0) {
|
|
2182
|
+
p3.note(
|
|
2183
|
+
[
|
|
2184
|
+
report.failed.map((f) => `${pc2.red("\u2022")} ${f.pkg.name}: ${f.reason}`).join("\n"),
|
|
2185
|
+
"",
|
|
2186
|
+
pc2.dim("A partial install usually fails downstream (Docker / Ollama / compose)."),
|
|
2187
|
+
pc2.dim("Install the failed packages manually and re-run with --skip-brew.")
|
|
2188
|
+
].join("\n"),
|
|
2189
|
+
"Brew failures"
|
|
2190
|
+
);
|
|
2191
|
+
const cont = unwrap(
|
|
2192
|
+
await p3.confirm({
|
|
2193
|
+
message: "Continue anyway? (Default no \u2014 recommended to fix and re-run.)",
|
|
2194
|
+
initialValue: false
|
|
2195
|
+
})
|
|
2196
|
+
);
|
|
2197
|
+
if (!cont) process.exit(1);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
async function locateRegionRepoPhase(opts, owner, repoName) {
|
|
2173
2201
|
const repoSpin = p3.spinner();
|
|
2174
2202
|
repoSpin.start("Locating your region node repo\u2026");
|
|
2175
2203
|
const located = await locateOrCloneRepo({
|
|
@@ -2187,6 +2215,9 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2187
2215
|
repoSpin.stop(
|
|
2188
2216
|
located.kind === "cloned" ? pc2.green(`\u2713 Cloned ${owner}/${repoName} to ${repoPath}`) : pc2.green(`\u2713 Found region repo at ${repoPath}`)
|
|
2189
2217
|
);
|
|
2218
|
+
return repoPath;
|
|
2219
|
+
}
|
|
2220
|
+
async function unlockKeychainPhase() {
|
|
2190
2221
|
const keychain = await detectKeychain();
|
|
2191
2222
|
if (!keychain.available) {
|
|
2192
2223
|
p3.cancel(
|
|
@@ -2219,6 +2250,9 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2219
2250
|
}
|
|
2220
2251
|
p3.note(`${pc2.green("\u2713")} Keychain unlocked for this session.`, "Keychain");
|
|
2221
2252
|
}
|
|
2253
|
+
}
|
|
2254
|
+
async function collectCoreSecrets(args) {
|
|
2255
|
+
const { region, opts } = args;
|
|
2222
2256
|
const pgsodiumKey = await loadSecret({
|
|
2223
2257
|
region,
|
|
2224
2258
|
account: "pgsodium-root-key",
|
|
@@ -2272,6 +2306,18 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2272
2306
|
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2273
2307
|
generate: generateDashboardPassword
|
|
2274
2308
|
});
|
|
2309
|
+
return {
|
|
2310
|
+
pgsodiumKey,
|
|
2311
|
+
tunnelToken,
|
|
2312
|
+
postgresPassword,
|
|
2313
|
+
jwtSecret,
|
|
2314
|
+
supabaseAnonKey,
|
|
2315
|
+
supabaseServiceRoleKey,
|
|
2316
|
+
dashboardPassword
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
async function collectPromptServiceSecrets(args) {
|
|
2320
|
+
const { region, nodeType, opts } = args;
|
|
2275
2321
|
if (nodeType === "region-with-prompts") {
|
|
2276
2322
|
await loadSecret({
|
|
2277
2323
|
region,
|
|
@@ -2315,6 +2361,9 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2315
2361
|
);
|
|
2316
2362
|
process.exit(1);
|
|
2317
2363
|
}
|
|
2364
|
+
return promptServiceUrl;
|
|
2365
|
+
}
|
|
2366
|
+
async function resolveSupabaseUrl(opts) {
|
|
2318
2367
|
let supabaseUrl;
|
|
2319
2368
|
if (opts.supabaseUrl) {
|
|
2320
2369
|
supabaseUrl = opts.supabaseUrl;
|
|
@@ -2338,32 +2387,44 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2338
2387
|
);
|
|
2339
2388
|
process.exit(1);
|
|
2340
2389
|
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2390
|
+
return supabaseUrl;
|
|
2391
|
+
}
|
|
2392
|
+
async function collectSecretsPhase(args) {
|
|
2393
|
+
const core = await collectCoreSecrets({ region: args.region, opts: args.opts });
|
|
2394
|
+
const promptServiceUrl = await collectPromptServiceSecrets(args);
|
|
2395
|
+
const supabaseUrl = await resolveSupabaseUrl(args.opts);
|
|
2396
|
+
return { ...core, promptServiceUrl, supabaseUrl };
|
|
2397
|
+
}
|
|
2398
|
+
async function runLaunchAgentPhase(args) {
|
|
2399
|
+
const { opts, secrets, llmModel, embeddingModel } = args;
|
|
2400
|
+
if (opts.skipLaunchAgent) return;
|
|
2401
|
+
const laSpin = p3.spinner();
|
|
2402
|
+
laSpin.start("Writing pgsodium key file + LaunchAgent plist\u2026");
|
|
2403
|
+
const la = await setupLaunchAgent({
|
|
2404
|
+
pgsodiumKey: secrets.pgsodiumKey,
|
|
2405
|
+
...secrets.tunnelToken !== void 0 ? { tunnelToken: secrets.tunnelToken } : {},
|
|
2406
|
+
llmModel,
|
|
2407
|
+
embeddingModel,
|
|
2408
|
+
postgresPassword: secrets.postgresPassword,
|
|
2409
|
+
jwtSecret: secrets.jwtSecret,
|
|
2410
|
+
supabaseAnonKey: secrets.supabaseAnonKey,
|
|
2411
|
+
supabaseServiceRoleKey: secrets.supabaseServiceRoleKey,
|
|
2412
|
+
dashboardPassword: secrets.dashboardPassword,
|
|
2413
|
+
supabaseUrl: secrets.supabaseUrl
|
|
2414
|
+
});
|
|
2415
|
+
if (!la.ok) {
|
|
2416
|
+
laSpin.stop(pc2.red(`\u2717 LaunchAgent step ${la.step} failed.`));
|
|
2417
|
+
p3.cancel(la.reason ?? "LaunchAgent setup failed.");
|
|
2418
|
+
process.exit(1);
|
|
2366
2419
|
}
|
|
2420
|
+
laSpin.stop(
|
|
2421
|
+
pc2.green(
|
|
2422
|
+
`\u2713 LaunchAgent loaded (${la.paths.plistFile})${opts.localOnly ? " \u2014 local-only mode, no TUNNEL_TOKEN set" : ""}.`
|
|
2423
|
+
)
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2426
|
+
async function installWrapperPhase(args) {
|
|
2427
|
+
const { repoPath, region, promptServiceUrl } = args;
|
|
2367
2428
|
const wrapperSpin = p3.spinner();
|
|
2368
2429
|
wrapperSpin.start("Installing bin/op-compose wrapper\u2026");
|
|
2369
2430
|
const wrapper = await installOpComposeWrapper({ repoDir: repoPath, region, promptServiceUrl });
|
|
@@ -2373,6 +2434,8 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2373
2434
|
process.exit(1);
|
|
2374
2435
|
}
|
|
2375
2436
|
wrapperSpin.stop(pc2.green(`\u2713 Installed ${wrapper.path} (mode 0755).`));
|
|
2437
|
+
}
|
|
2438
|
+
async function loginGhcrPhase() {
|
|
2376
2439
|
const ghcrSpin = p3.spinner();
|
|
2377
2440
|
ghcrSpin.start("Authenticating Docker to ghcr.io\u2026");
|
|
2378
2441
|
const ghcr = await loginToGhcr();
|
|
@@ -2382,45 +2445,66 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2382
2445
|
process.exit(1);
|
|
2383
2446
|
}
|
|
2384
2447
|
ghcrSpin.stop(pc2.green("\u2713 Logged in to ghcr.io."));
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
if (!olHealth.reachable) {
|
|
2402
|
-
olSpin.stop(
|
|
2403
|
-
pc2.yellow("\u26A0 Ollama service started but daemon not yet answering on :11434.")
|
|
2404
|
-
);
|
|
2405
|
-
p3.cancel(
|
|
2406
|
-
"Give it another few seconds, then re-run with --skip-brew --skip-launch-agent."
|
|
2407
|
-
);
|
|
2408
|
-
process.exit(1);
|
|
2409
|
-
}
|
|
2448
|
+
}
|
|
2449
|
+
async function runOllamaPhase(args) {
|
|
2450
|
+
const { opts, embeddingModel, llmModel } = args;
|
|
2451
|
+
if (opts.skipOllama) return;
|
|
2452
|
+
const olSpin = p3.spinner();
|
|
2453
|
+
olSpin.start(`Pulling + warming Ollama models\u2026 (${estimatedPullTime(llmModel)})`);
|
|
2454
|
+
let olHealth = await checkOllamaHealth();
|
|
2455
|
+
if (!olHealth.reachable) {
|
|
2456
|
+
olSpin.message("Ollama not reachable \u2014 running `brew services start ollama`\u2026");
|
|
2457
|
+
const start = await startOllamaService();
|
|
2458
|
+
if (!start.ok) {
|
|
2459
|
+
olSpin.stop(pc2.red("\u2717 Couldn't start Ollama service."));
|
|
2460
|
+
p3.cancel(
|
|
2461
|
+
`${start.reason ?? "unknown"} \u2014 start it manually with \`brew services start ollama\` and re-run with --skip-brew --skip-launch-agent.`
|
|
2462
|
+
);
|
|
2463
|
+
process.exit(1);
|
|
2410
2464
|
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
p3.note(`${pc2.yellow("\u26A0")} ${probe.reason}`, "Docker host networking");
|
|
2465
|
+
await new Promise((res) => setTimeout(res, 3e3));
|
|
2466
|
+
olHealth = await checkOllamaHealth();
|
|
2467
|
+
if (!olHealth.reachable) {
|
|
2468
|
+
olSpin.stop(
|
|
2469
|
+
pc2.yellow("\u26A0 Ollama service started but daemon not yet answering on :11434.")
|
|
2470
|
+
);
|
|
2471
|
+
p3.cancel(
|
|
2472
|
+
"Give it another few seconds, then re-run with --skip-brew --skip-launch-agent."
|
|
2473
|
+
);
|
|
2474
|
+
process.exit(1);
|
|
2422
2475
|
}
|
|
2423
2476
|
}
|
|
2477
|
+
const modelReport = await setupModels([embeddingModel, llmModel], (model, status) => {
|
|
2478
|
+
olSpin.message(`${status}: ${model}`);
|
|
2479
|
+
});
|
|
2480
|
+
olSpin.stop(
|
|
2481
|
+
modelReport.failed.length === 0 ? pc2.green(
|
|
2482
|
+
`\u2713 ${modelReport.pulled.length} pulled, ${modelReport.alreadyPresent.length} present, ${modelReport.warmed.length} warmed`
|
|
2483
|
+
) : pc2.yellow(`\u26A0 ${modelReport.failed.length} model pull(s) failed`)
|
|
2484
|
+
);
|
|
2485
|
+
const probe = await probeHostDockerInternal();
|
|
2486
|
+
if (!probe.ok) {
|
|
2487
|
+
p3.note(`${pc2.yellow("\u26A0")} ${probe.reason}`, "Docker host networking");
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
function buildComposeEnv(args) {
|
|
2491
|
+
const { secrets, llmModel, embeddingModel } = args;
|
|
2492
|
+
return {
|
|
2493
|
+
PGSODIUM_ROOT_KEY: secrets.pgsodiumKey,
|
|
2494
|
+
POSTGRES_PASSWORD: secrets.postgresPassword,
|
|
2495
|
+
JWT_SECRET: secrets.jwtSecret,
|
|
2496
|
+
SUPABASE_ANON_KEY: secrets.supabaseAnonKey,
|
|
2497
|
+
SUPABASE_SERVICE_ROLE_KEY: secrets.supabaseServiceRoleKey,
|
|
2498
|
+
DASHBOARD_PASSWORD: secrets.dashboardPassword,
|
|
2499
|
+
SUPABASE_URL: secrets.supabaseUrl,
|
|
2500
|
+
AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? secrets.jwtSecret,
|
|
2501
|
+
...secrets.tunnelToken !== void 0 ? { TUNNEL_TOKEN: secrets.tunnelToken } : {},
|
|
2502
|
+
...llmModel ? { LLM_MODEL: llmModel } : {},
|
|
2503
|
+
...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
async function runStackPhase(args) {
|
|
2507
|
+
const { opts, repoPath, region, composeFile, secrets, llmModel, embeddingModel } = args;
|
|
2424
2508
|
if (opts.skipStack) {
|
|
2425
2509
|
const profileFlag = opts.localOnly ? "" : "--profile public ";
|
|
2426
2510
|
p3.outro(
|
|
@@ -2431,19 +2515,7 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2431
2515
|
return;
|
|
2432
2516
|
}
|
|
2433
2517
|
const composeFiles = resolveComposeFiles(repoPath, composeFile);
|
|
2434
|
-
const composeEnv = {
|
|
2435
|
-
PGSODIUM_ROOT_KEY: pgsodiumKey,
|
|
2436
|
-
POSTGRES_PASSWORD: postgresPassword,
|
|
2437
|
-
JWT_SECRET: jwtSecret,
|
|
2438
|
-
SUPABASE_ANON_KEY: supabaseAnonKey,
|
|
2439
|
-
SUPABASE_SERVICE_ROLE_KEY: supabaseServiceRoleKey,
|
|
2440
|
-
DASHBOARD_PASSWORD: dashboardPassword,
|
|
2441
|
-
SUPABASE_URL: supabaseUrl,
|
|
2442
|
-
AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? jwtSecret,
|
|
2443
|
-
...tunnelToken !== void 0 ? { TUNNEL_TOKEN: tunnelToken } : {},
|
|
2444
|
-
...llmModel ? { LLM_MODEL: llmModel } : {},
|
|
2445
|
-
...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
|
|
2446
|
-
};
|
|
2518
|
+
const composeEnv = buildComposeEnv({ secrets, llmModel, embeddingModel });
|
|
2447
2519
|
const composeOpts = {
|
|
2448
2520
|
files: composeFiles,
|
|
2449
2521
|
cwd: repoPath,
|
|
@@ -2489,9 +2561,25 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2489
2561
|
healthSpin.message(`${healthy}/${running} healthy`);
|
|
2490
2562
|
}
|
|
2491
2563
|
});
|
|
2564
|
+
healthSpin.stop(healthOutcomeHeadline(outcome));
|
|
2565
|
+
finishHealthOutcome(outcome, { region, opts, composeFiles });
|
|
2566
|
+
}
|
|
2567
|
+
function healthOutcomeHeadline(outcome) {
|
|
2568
|
+
switch (outcome.kind) {
|
|
2569
|
+
case "healthy":
|
|
2570
|
+
return pc2.green(`\u2713 All ${outcome.snapshots.length} containers healthy.`);
|
|
2571
|
+
case "unhealthy":
|
|
2572
|
+
return pc2.red(`\u2717 ${outcome.problem}`);
|
|
2573
|
+
case "timeout":
|
|
2574
|
+
return pc2.yellow("\u26A0 Timed out waiting for all containers to report healthy.");
|
|
2575
|
+
default:
|
|
2576
|
+
return assertNever();
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
function finishHealthOutcome(outcome, args) {
|
|
2580
|
+
const { region, opts, composeFiles } = args;
|
|
2492
2581
|
switch (outcome.kind) {
|
|
2493
2582
|
case "healthy":
|
|
2494
|
-
healthSpin.stop(pc2.green(`\u2713 All ${outcome.snapshots.length} containers healthy.`));
|
|
2495
2583
|
if (opts.localOnly) {
|
|
2496
2584
|
p3.outro(
|
|
2497
2585
|
pc2.cyan(
|
|
@@ -2511,7 +2599,6 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2511
2599
|
}
|
|
2512
2600
|
return;
|
|
2513
2601
|
case "unhealthy":
|
|
2514
|
-
healthSpin.stop(pc2.red(`\u2717 ${outcome.problem}`));
|
|
2515
2602
|
p3.note(
|
|
2516
2603
|
outcome.snapshots.map((s) => ` ${kvHealth(s.health)} ${s.name} (${s.state})`).join("\n"),
|
|
2517
2604
|
"Container state"
|
|
@@ -2522,7 +2609,6 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2522
2609
|
process.exit(1);
|
|
2523
2610
|
// eslint-disable-next-line no-fallthrough
|
|
2524
2611
|
case "timeout":
|
|
2525
|
-
healthSpin.stop(pc2.yellow("\u26A0 Timed out waiting for all containers to report healthy."));
|
|
2526
2612
|
p3.note(
|
|
2527
2613
|
outcome.snapshots.map((s) => ` ${kvHealth(s.health)} ${s.name} (${s.state})`).join("\n"),
|
|
2528
2614
|
"Container state at timeout"
|
|
@@ -2532,11 +2618,10 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2532
2618
|
);
|
|
2533
2619
|
process.exit(1);
|
|
2534
2620
|
// eslint-disable-next-line no-fallthrough
|
|
2535
|
-
default:
|
|
2621
|
+
default:
|
|
2536
2622
|
return;
|
|
2537
|
-
}
|
|
2538
2623
|
}
|
|
2539
|
-
}
|
|
2624
|
+
}
|
|
2540
2625
|
function resolveComposeFiles(repoPath, composeFile) {
|
|
2541
2626
|
const inputs = composeFile ?? ["docker-compose-prod.yml"];
|
|
2542
2627
|
return inputs.map((f) => f.startsWith("/") ? f : join(repoPath, f));
|
|
@@ -4492,7 +4577,7 @@ async function looksLikeRegionsRepo(dir) {
|
|
|
4492
4577
|
}
|
|
4493
4578
|
|
|
4494
4579
|
// src/cli.ts
|
|
4495
|
-
var VERSION = "0.10.
|
|
4580
|
+
var VERSION = "0.10.6";
|
|
4496
4581
|
var program = new Command();
|
|
4497
4582
|
program.name("create-op-node").description(
|
|
4498
4583
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|