create-op-node 0.9.0 → 0.10.0
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 +255 -13
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { execa } from 'execa';
|
|
|
6
6
|
import { Octokit } from '@octokit/rest';
|
|
7
7
|
import { randomBytes, createHmac } from 'crypto';
|
|
8
8
|
import { join, resolve, dirname } from 'path';
|
|
9
|
-
import { mkdir, writeFile, access,
|
|
9
|
+
import { mkdir, writeFile, access, chmod, rename, rm } from 'fs/promises';
|
|
10
10
|
import { homedir } from 'os';
|
|
11
11
|
import { readFileSync } from 'fs';
|
|
12
12
|
import * as tls from 'tls';
|
|
@@ -237,7 +237,10 @@ var FRIENDLY = {
|
|
|
237
237
|
"jwt-secret": "JWT signing secret",
|
|
238
238
|
"supabase-anon-key": "Supabase anon key",
|
|
239
239
|
"supabase-service-role-key": "Supabase service role key",
|
|
240
|
-
"dashboard-password": "Supabase Studio dashboard password"
|
|
240
|
+
"dashboard-password": "Supabase Studio dashboard password",
|
|
241
|
+
"prompts-db-password": "prompt-service Postgres password",
|
|
242
|
+
"prompt-service-api-key": "prompt-service HMAC API key",
|
|
243
|
+
"prompt-service-admin-api-key": "prompt-service admin API key"
|
|
241
244
|
};
|
|
242
245
|
function labelFor(coords) {
|
|
243
246
|
return `Opus Populi (${coords.region}) \u2014 ${FRIENDLY[coords.account]}`;
|
|
@@ -448,6 +451,9 @@ function generatePostgresPassword() {
|
|
|
448
451
|
function generateDashboardPassword() {
|
|
449
452
|
return base64url(randomBytes(24));
|
|
450
453
|
}
|
|
454
|
+
function generateHmacApiKey() {
|
|
455
|
+
return base64url(randomBytes(32));
|
|
456
|
+
}
|
|
451
457
|
function generateJwtSecret() {
|
|
452
458
|
return randomBytes(48).toString("base64");
|
|
453
459
|
}
|
|
@@ -1403,6 +1409,139 @@ async function teardownLaunchAgent(paths, opts = {}) {
|
|
|
1403
1409
|
return { ok: steps.every((s) => s.ok), steps };
|
|
1404
1410
|
}
|
|
1405
1411
|
|
|
1412
|
+
// src/lib/op-compose-script.ts
|
|
1413
|
+
var REGION_RE = /^[a-z0-9-]{2,32}$/;
|
|
1414
|
+
var PROMPT_SERVICE_URL_RE = /^[A-Za-z0-9:/_.-]+$/;
|
|
1415
|
+
function renderOpComposeScript(input) {
|
|
1416
|
+
if (!REGION_RE.test(input.region)) {
|
|
1417
|
+
throw new Error(
|
|
1418
|
+
`region ${JSON.stringify(input.region)} contains characters not allowed in a launchd / Keychain service identifier`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
if (input.promptServiceUrl !== void 0 && !PROMPT_SERVICE_URL_RE.test(input.promptServiceUrl)) {
|
|
1422
|
+
throw new Error(
|
|
1423
|
+
`promptServiceUrl ${JSON.stringify(input.promptServiceUrl)} contains characters not allowed in a wrapper export`
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
const promptServiceUrlExport = input.promptServiceUrl !== void 0 ? `# Backend services call prompt-service via this URL. Service-level env
|
|
1427
|
+
# from docker-compose-prompt-service.yml overlay (when layered) wins
|
|
1428
|
+
# over this default \u2014 so colocated deployments still hit
|
|
1429
|
+
# http://opuspopuli-prompts:3210 even though we may have a public URL
|
|
1430
|
+
# baked in here.
|
|
1431
|
+
export PROMPT_SERVICE_URL="\${PROMPT_SERVICE_URL:-${input.promptServiceUrl}}"
|
|
1432
|
+
|
|
1433
|
+
` : "";
|
|
1434
|
+
return `#!/bin/bash
|
|
1435
|
+
# op-compose \u2014 wrapper that reads region secrets from macOS Keychain just
|
|
1436
|
+
# before invoking \`docker compose\`. Auto-generated by
|
|
1437
|
+
# \`create-op-node bootstrap --region ${input.region}\`. Do not edit by
|
|
1438
|
+
# hand; re-run bootstrap to regenerate.
|
|
1439
|
+
#
|
|
1440
|
+
# Why: launchctl setenv only propagates to launchd-spawned processes, so
|
|
1441
|
+
# SSH shells and pre-existing Terminal.app instances never see the env
|
|
1442
|
+
# vars bootstrap exports. This wrapper hydrates the docker compose
|
|
1443
|
+
# subprocess directly, so every invocation works regardless of how the
|
|
1444
|
+
# operator's shell was started.
|
|
1445
|
+
#
|
|
1446
|
+
# Usage:
|
|
1447
|
+
# ./bin/op-compose -f docker-compose-prod.yml up -d
|
|
1448
|
+
# ./bin/op-compose -f docker-compose-prod.yml pull
|
|
1449
|
+
# ./bin/op-compose -f docker-compose-prod.yml down
|
|
1450
|
+
|
|
1451
|
+
set -euo pipefail
|
|
1452
|
+
|
|
1453
|
+
SVC="org.opuspopuli.${input.region}"
|
|
1454
|
+
|
|
1455
|
+
require_secret() {
|
|
1456
|
+
local account="$1"
|
|
1457
|
+
local value
|
|
1458
|
+
if ! value=$(security find-generic-password -s "$SVC" -a "$account" -w 2>/dev/null); then
|
|
1459
|
+
echo "op-compose: missing Keychain entry '$account' under service '$SVC'." >&2
|
|
1460
|
+
echo "Run: create-op-node bootstrap --region ${input.region}" >&2
|
|
1461
|
+
exit 1
|
|
1462
|
+
fi
|
|
1463
|
+
printf '%s' "$value"
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
optional_secret() {
|
|
1467
|
+
local account="$1"
|
|
1468
|
+
security find-generic-password -s "$SVC" -a "$account" -w 2>/dev/null || true
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
# Bootstrap-critical: Postgres + Supabase admin credentials.
|
|
1472
|
+
export PGSODIUM_ROOT_KEY="$(require_secret pgsodium-root-key)"
|
|
1473
|
+
export POSTGRES_PASSWORD="$(require_secret postgres-password)"
|
|
1474
|
+
export JWT_SECRET="$(require_secret jwt-secret)"
|
|
1475
|
+
export SUPABASE_ANON_KEY="$(require_secret supabase-anon-key)"
|
|
1476
|
+
export SUPABASE_SERVICE_ROLE_KEY="$(require_secret supabase-service-role-key)"
|
|
1477
|
+
export DASHBOARD_PASSWORD="$(require_secret dashboard-password)"
|
|
1478
|
+
|
|
1479
|
+
# AUTH_JWT_SECRET historically equals JWT_SECRET in dev/UAT \u2014 supply that
|
|
1480
|
+
# default if the operator hasn't set one independently.
|
|
1481
|
+
export AUTH_JWT_SECRET="\${AUTH_JWT_SECRET:-$JWT_SECRET}"
|
|
1482
|
+
|
|
1483
|
+
# Optional: Cloudflare Tunnel token (only present in non-local-only mode).
|
|
1484
|
+
TUNNEL_TOKEN_VAL="$(optional_secret tunnel-token)"
|
|
1485
|
+
if [ -n "$TUNNEL_TOKEN_VAL" ]; then
|
|
1486
|
+
export TUNNEL_TOKEN="$TUNNEL_TOKEN_VAL"
|
|
1487
|
+
fi
|
|
1488
|
+
|
|
1489
|
+
# SUPABASE_URL is not a secret \u2014 read from env if the operator set one,
|
|
1490
|
+
# default to localhost for --local-only.
|
|
1491
|
+
export SUPABASE_URL="\${SUPABASE_URL:-http://localhost:8000}"
|
|
1492
|
+
|
|
1493
|
+
${promptServiceUrlExport}# prompt-service credentials. Always exported \u2014 harmless when the operator
|
|
1494
|
+
# is NOT colocating prompt-service (the main compose doesn't read these
|
|
1495
|
+
# vars; the overlay does). The PROMPT_SERVICE_API_KEYS list is built from
|
|
1496
|
+
# the operator's region + the single HMAC key in Keychain, matching the
|
|
1497
|
+
# prompt-service env format \`<region>:<key>,<region>:<key>,\u2026\`.
|
|
1498
|
+
PROMPTS_DB_PASSWORD_VAL="$(optional_secret prompts-db-password)"
|
|
1499
|
+
PROMPT_SERVICE_API_KEY_VAL="$(optional_secret prompt-service-api-key)"
|
|
1500
|
+
PROMPT_SERVICE_ADMIN_API_KEY_VAL="$(optional_secret prompt-service-admin-api-key)"
|
|
1501
|
+
if [ -n "$PROMPTS_DB_PASSWORD_VAL" ]; then
|
|
1502
|
+
export PROMPTS_DB_PASSWORD="$PROMPTS_DB_PASSWORD_VAL"
|
|
1503
|
+
fi
|
|
1504
|
+
if [ -n "$PROMPT_SERVICE_API_KEY_VAL" ]; then
|
|
1505
|
+
export PROMPT_SERVICE_API_KEY="$PROMPT_SERVICE_API_KEY_VAL"
|
|
1506
|
+
# API_KEYS env on prompt-service is \`<region>:<key>,\u2026\` \u2014 render the
|
|
1507
|
+
# single-region pair here so prompt-service accepts the same key the
|
|
1508
|
+
# backend sends. The prompts team adds additional regions on the
|
|
1509
|
+
# central deployment for Phase 2.
|
|
1510
|
+
export PROMPT_SERVICE_API_KEYS="${input.region}:$PROMPT_SERVICE_API_KEY_VAL"
|
|
1511
|
+
fi
|
|
1512
|
+
if [ -n "$PROMPT_SERVICE_ADMIN_API_KEY_VAL" ]; then
|
|
1513
|
+
export PROMPT_SERVICE_ADMIN_API_KEYS="$PROMPT_SERVICE_ADMIN_API_KEY_VAL"
|
|
1514
|
+
fi
|
|
1515
|
+
|
|
1516
|
+
exec docker compose "$@"
|
|
1517
|
+
`;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// src/lib/op-compose-install.ts
|
|
1521
|
+
async function installOpComposeWrapper(input) {
|
|
1522
|
+
let content;
|
|
1523
|
+
try {
|
|
1524
|
+
content = renderOpComposeScript({
|
|
1525
|
+
region: input.region,
|
|
1526
|
+
...input.promptServiceUrl !== void 0 ? { promptServiceUrl: input.promptServiceUrl } : {}
|
|
1527
|
+
});
|
|
1528
|
+
} catch (err) {
|
|
1529
|
+
return { ok: false, reason: err.message };
|
|
1530
|
+
}
|
|
1531
|
+
const binDir = join(input.repoDir, "bin");
|
|
1532
|
+
const target = join(binDir, "op-compose");
|
|
1533
|
+
const tmp = `${target}.tmp.${process.pid}`;
|
|
1534
|
+
try {
|
|
1535
|
+
await mkdir(binDir, { recursive: true });
|
|
1536
|
+
await writeFile(tmp, content, { mode: 493 });
|
|
1537
|
+
await chmod(tmp, 493);
|
|
1538
|
+
await rename(tmp, target);
|
|
1539
|
+
return { ok: true, path: target };
|
|
1540
|
+
} catch (err) {
|
|
1541
|
+
return { ok: false, reason: `writing ${target} failed: ${err.message}` };
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1406
1545
|
// src/lib/docker.ts
|
|
1407
1546
|
var GHCR_REGISTRY = "ghcr.io";
|
|
1408
1547
|
async function loginToGhcr() {
|
|
@@ -1449,16 +1588,19 @@ function composeArgs(opts, sub) {
|
|
|
1449
1588
|
const profiles = (opts.profiles ?? []).flatMap((pr) => ["--profile", pr]);
|
|
1450
1589
|
return ["compose", ...files, ...env, ...profiles, ...sub];
|
|
1451
1590
|
}
|
|
1591
|
+
function execOpts(opts) {
|
|
1592
|
+
return opts.env ? { cwd: opts.cwd, env: opts.env } : { cwd: opts.cwd };
|
|
1593
|
+
}
|
|
1452
1594
|
async function composePull(opts) {
|
|
1453
|
-
const res = await safeExeca("docker", composeArgs(opts, ["pull"]));
|
|
1595
|
+
const res = await safeExeca("docker", composeArgs(opts, ["pull"]), execOpts(opts));
|
|
1454
1596
|
return result(res, "compose pull");
|
|
1455
1597
|
}
|
|
1456
1598
|
async function composeUp(opts) {
|
|
1457
|
-
const res = await safeExeca("docker", composeArgs(opts, ["up", "-d", "--remove-orphans"]));
|
|
1599
|
+
const res = await safeExeca("docker", composeArgs(opts, ["up", "-d", "--remove-orphans"]), execOpts(opts));
|
|
1458
1600
|
return result(res, "compose up");
|
|
1459
1601
|
}
|
|
1460
1602
|
async function composeRemoveService(opts, service) {
|
|
1461
|
-
const res = await safeExeca("docker", composeArgs(opts, ["rm", "-sfv", service]));
|
|
1603
|
+
const res = await safeExeca("docker", composeArgs(opts, ["rm", "-sfv", service]), execOpts(opts));
|
|
1462
1604
|
return result(res, `compose rm ${service}`);
|
|
1463
1605
|
}
|
|
1464
1606
|
async function composeDown(opts) {
|
|
@@ -1471,7 +1613,7 @@ async function composeDown(opts) {
|
|
|
1471
1613
|
opts.wipeVolumes ? "-v" : "",
|
|
1472
1614
|
opts.removeImages ? `--rmi ${opts.removeImages}` : ""
|
|
1473
1615
|
].filter(Boolean).join(" ");
|
|
1474
|
-
const res = await safeExeca("docker", composeArgs(opts, flags));
|
|
1616
|
+
const res = await safeExeca("docker", composeArgs(opts, flags), execOpts(opts));
|
|
1475
1617
|
return result(res, label);
|
|
1476
1618
|
}
|
|
1477
1619
|
async function dockerLogout(registry = GHCR_REGISTRY) {
|
|
@@ -1517,7 +1659,7 @@ function normalize(raw) {
|
|
|
1517
1659
|
return { name, state, health, exitCode };
|
|
1518
1660
|
}
|
|
1519
1661
|
async function composePs(opts) {
|
|
1520
|
-
const res = await safeExeca("docker", composeArgs(opts, ["ps", "--format", "json"]));
|
|
1662
|
+
const res = await safeExeca("docker", composeArgs(opts, ["ps", "--format", "json"]), execOpts(opts));
|
|
1521
1663
|
if (res === null || res.exitCode !== 0) return null;
|
|
1522
1664
|
return parseComposePs(res.stdout);
|
|
1523
1665
|
}
|
|
@@ -1776,6 +1918,16 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
1776
1918
|
"--supabase-url <url>",
|
|
1777
1919
|
"Public-facing Supabase URL (what browsers + microservices use). Default: `http://localhost:8000` in --local-only mode, otherwise `https://supabase.<domain>` (which the operator can override)."
|
|
1778
1920
|
)
|
|
1921
|
+
).addOption(
|
|
1922
|
+
new Option(
|
|
1923
|
+
"--node-type <type>",
|
|
1924
|
+
"Deployment topology. `region` (default) = region node connecting to remote prompt-service. `region-with-prompts` = region node + colocated prompt-service overlay (team/dev use). `prompts-only` = future stub for the central prompts deployment."
|
|
1925
|
+
).choices(["region", "region-with-prompts", "prompts-only"])
|
|
1926
|
+
).addOption(
|
|
1927
|
+
new Option(
|
|
1928
|
+
"--prompt-service-url <url>",
|
|
1929
|
+
"Remote prompt-service URL. Only meaningful for --node-type=region (default: https://prompts.opuspopuli.org). Ignored for --node-type=region-with-prompts (the overlay pins it to http://opuspopuli-prompts:3210)."
|
|
1930
|
+
)
|
|
1779
1931
|
).addOption(new Option("--skip-stack", "Stop before `docker compose pull && up`").default(false)).addOption(
|
|
1780
1932
|
new Option(
|
|
1781
1933
|
"--local-only",
|
|
@@ -1783,6 +1935,32 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
1783
1935
|
).default(false)
|
|
1784
1936
|
).addOption(new Option("-y, --yes", "Skip confirmation prompts").default(false)).action(async (opts) => {
|
|
1785
1937
|
p3.intro(pc2.bgCyan(pc2.black(" create-op-node bootstrap ")));
|
|
1938
|
+
const nodeType = opts.nodeType ? opts.nodeType : unwrap(
|
|
1939
|
+
await p3.select({
|
|
1940
|
+
message: "What kind of node are you provisioning?",
|
|
1941
|
+
options: [
|
|
1942
|
+
{
|
|
1943
|
+
value: "region",
|
|
1944
|
+
label: "Region node (civic data for one region, remote prompt-service)"
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
value: "region-with-prompts",
|
|
1948
|
+
label: "Region + colocated prompt-service (team / dev)"
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
value: "prompts-only",
|
|
1952
|
+
label: "Prompt-service node (central prompts deployment \u2014 future)"
|
|
1953
|
+
}
|
|
1954
|
+
],
|
|
1955
|
+
initialValue: "region"
|
|
1956
|
+
})
|
|
1957
|
+
);
|
|
1958
|
+
if (nodeType === "prompts-only") {
|
|
1959
|
+
p3.cancel(
|
|
1960
|
+
"prompts-only node deployment is not yet implemented. This flag exists so the CLI surface is stable for when the prompts team deploys the central prompts.opuspopuli.org instance \u2014 but the deployment stack (compose file, healthchecks, observability) is filled in later, when there is an actual central prompt-service to deploy."
|
|
1961
|
+
);
|
|
1962
|
+
process.exit(1);
|
|
1963
|
+
}
|
|
1786
1964
|
const region = opts.region ? opts.region : unwrap(
|
|
1787
1965
|
await p3.text({
|
|
1788
1966
|
message: "Region label (the slug used during init \u2014 e.g. us-ca)?",
|
|
@@ -1968,6 +2146,46 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
1968
2146
|
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
1969
2147
|
generate: generateDashboardPassword
|
|
1970
2148
|
});
|
|
2149
|
+
if (nodeType === "region-with-prompts") {
|
|
2150
|
+
await loadSecret({
|
|
2151
|
+
region,
|
|
2152
|
+
account: "prompts-db-password",
|
|
2153
|
+
label: "prompt-service Postgres password",
|
|
2154
|
+
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2155
|
+
generate: generatePostgresPassword
|
|
2156
|
+
});
|
|
2157
|
+
await loadSecret({
|
|
2158
|
+
region,
|
|
2159
|
+
account: "prompt-service-api-key",
|
|
2160
|
+
label: "prompt-service HMAC API key",
|
|
2161
|
+
// Same URL-safe alphabet as other HMAC keys; embedded in the
|
|
2162
|
+
// `<region>:<key>` API_KEYS list and in Authorization headers.
|
|
2163
|
+
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2164
|
+
generate: generateHmacApiKey
|
|
2165
|
+
});
|
|
2166
|
+
await loadSecret({
|
|
2167
|
+
region,
|
|
2168
|
+
account: "prompt-service-admin-api-key",
|
|
2169
|
+
label: "prompt-service admin API key",
|
|
2170
|
+
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2171
|
+
generate: generateHmacApiKey
|
|
2172
|
+
});
|
|
2173
|
+
} else {
|
|
2174
|
+
await loadSecret({
|
|
2175
|
+
region,
|
|
2176
|
+
account: "prompt-service-api-key",
|
|
2177
|
+
label: "prompt-service HMAC API key (issued by the prompts team)",
|
|
2178
|
+
placeholder: "paste the region-specific HMAC key",
|
|
2179
|
+
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)"
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
const promptServiceUrl = nodeType === "region-with-prompts" ? "http://opuspopuli-prompts:3210" : opts.promptServiceUrl ?? "https://prompts.opuspopuli.org";
|
|
2183
|
+
if (!SAFE_URL_RE.test(promptServiceUrl)) {
|
|
2184
|
+
p3.cancel(
|
|
2185
|
+
`--prompt-service-url ${JSON.stringify(promptServiceUrl)} contains characters outside the allowed URL set`
|
|
2186
|
+
);
|
|
2187
|
+
process.exit(1);
|
|
2188
|
+
}
|
|
1971
2189
|
let supabaseUrl;
|
|
1972
2190
|
if (opts.supabaseUrl) {
|
|
1973
2191
|
supabaseUrl = opts.supabaseUrl;
|
|
@@ -2014,6 +2232,15 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2014
2232
|
)
|
|
2015
2233
|
);
|
|
2016
2234
|
}
|
|
2235
|
+
const wrapperSpin = p3.spinner();
|
|
2236
|
+
wrapperSpin.start("Installing bin/op-compose wrapper\u2026");
|
|
2237
|
+
const wrapper = await installOpComposeWrapper({ repoDir: repoPath, region, promptServiceUrl });
|
|
2238
|
+
if (!wrapper.ok) {
|
|
2239
|
+
wrapperSpin.stop(pc2.red("\u2717 op-compose wrapper install failed."));
|
|
2240
|
+
p3.cancel(wrapper.reason ?? "op-compose wrapper install failed.");
|
|
2241
|
+
process.exit(1);
|
|
2242
|
+
}
|
|
2243
|
+
wrapperSpin.stop(pc2.green(`\u2713 Installed ${wrapper.path} (mode 0755).`));
|
|
2017
2244
|
const ghcrSpin = p3.spinner();
|
|
2018
2245
|
ghcrSpin.start("Authenticating Docker to ghcr.io\u2026");
|
|
2019
2246
|
const ghcr = await loginToGhcr();
|
|
@@ -2066,17 +2293,31 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2066
2293
|
const profileFlag = opts.localOnly ? "" : "--profile public ";
|
|
2067
2294
|
p3.outro(
|
|
2068
2295
|
pc2.cyan(
|
|
2069
|
-
`Stack-up skipped. Run
|
|
2296
|
+
`Stack-up skipped. Run \`./bin/op-compose -f ${composeFile.join(" -f ")} ${profileFlag}pull && ./bin/op-compose -f ${composeFile.join(" -f ")} ${profileFlag}up -d\` from ${repoPath} when ready. (op-compose hydrates Keychain secrets per-invocation; use it instead of raw \`docker compose\`.)`
|
|
2070
2297
|
)
|
|
2071
2298
|
);
|
|
2072
2299
|
return;
|
|
2073
2300
|
}
|
|
2074
2301
|
const composeFiles = resolveComposeFiles(repoPath, composeFile);
|
|
2302
|
+
const composeEnv = {
|
|
2303
|
+
PGSODIUM_ROOT_KEY: pgsodiumKey,
|
|
2304
|
+
POSTGRES_PASSWORD: postgresPassword,
|
|
2305
|
+
JWT_SECRET: jwtSecret,
|
|
2306
|
+
SUPABASE_ANON_KEY: supabaseAnonKey,
|
|
2307
|
+
SUPABASE_SERVICE_ROLE_KEY: supabaseServiceRoleKey,
|
|
2308
|
+
DASHBOARD_PASSWORD: dashboardPassword,
|
|
2309
|
+
SUPABASE_URL: supabaseUrl,
|
|
2310
|
+
AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? jwtSecret,
|
|
2311
|
+
...tunnelToken !== void 0 ? { TUNNEL_TOKEN: tunnelToken } : {},
|
|
2312
|
+
...llmModel ? { LLM_MODEL: llmModel } : {},
|
|
2313
|
+
...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
|
|
2314
|
+
};
|
|
2075
2315
|
const composeOpts = {
|
|
2076
2316
|
files: composeFiles,
|
|
2077
2317
|
cwd: repoPath,
|
|
2078
2318
|
...opts.envFile ? { envFile: opts.envFile } : {},
|
|
2079
|
-
profiles: opts.localOnly ? LOCAL_PROFILES : PUBLIC_PROFILES
|
|
2319
|
+
profiles: opts.localOnly ? LOCAL_PROFILES : PUBLIC_PROFILES,
|
|
2320
|
+
env: composeEnv
|
|
2080
2321
|
};
|
|
2081
2322
|
if (opts.localOnly) {
|
|
2082
2323
|
const evict = p3.spinner();
|
|
@@ -2372,7 +2613,7 @@ async function loadSecret(input) {
|
|
|
2372
2613
|
}
|
|
2373
2614
|
return pasted;
|
|
2374
2615
|
}
|
|
2375
|
-
var
|
|
2616
|
+
var REGION_RE2 = /^[a-z0-9-]{2,32}$/;
|
|
2376
2617
|
var RESET_PHASES = {
|
|
2377
2618
|
STOP_STACK: "Stop stack",
|
|
2378
2619
|
LAUNCH_AGENT: "LaunchAgent",
|
|
@@ -2529,10 +2770,10 @@ var resetCommand = new Command("reset").description(
|
|
|
2529
2770
|
await p3.text({
|
|
2530
2771
|
message: "Region label (the slug used during init \u2014 e.g. us-ca)?",
|
|
2531
2772
|
placeholder: "us-ca",
|
|
2532
|
-
validate: (v) =>
|
|
2773
|
+
validate: (v) => REGION_RE2.test(v ?? "") ? void 0 : "lowercase letters, digits, hyphens; 2\u201332 chars"
|
|
2533
2774
|
})
|
|
2534
2775
|
);
|
|
2535
|
-
if (!
|
|
2776
|
+
if (!REGION_RE2.test(region)) {
|
|
2536
2777
|
p3.cancel(`--region ${JSON.stringify(region)} is not a valid region slug.`);
|
|
2537
2778
|
process.exit(2);
|
|
2538
2779
|
}
|
|
@@ -2577,6 +2818,7 @@ var resetCommand = new Command("reset").description(
|
|
|
2577
2818
|
if (repoPath) {
|
|
2578
2819
|
runningContainers = await composePs({
|
|
2579
2820
|
files: resolveComposeFiles(repoPath, opts.composeFile),
|
|
2821
|
+
cwd: repoPath,
|
|
2580
2822
|
...opts.envFile ? { envFile: opts.envFile } : {}
|
|
2581
2823
|
});
|
|
2582
2824
|
}
|
|
@@ -4092,7 +4334,7 @@ async function looksLikeRegionsRepo(dir) {
|
|
|
4092
4334
|
}
|
|
4093
4335
|
|
|
4094
4336
|
// src/cli.ts
|
|
4095
|
-
var VERSION = "0.
|
|
4337
|
+
var VERSION = "0.10.0";
|
|
4096
4338
|
var program = new Command();
|
|
4097
4339
|
program.name("create-op-node").description(
|
|
4098
4340
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|