create-op-node 0.12.1 → 0.12.3
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 +80 -16
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Octokit } from '@octokit/rest';
|
|
|
7
7
|
import _sodium from 'libsodium-wrappers';
|
|
8
8
|
import { randomBytes, createHmac } from 'crypto';
|
|
9
9
|
import { join, resolve, dirname } from 'path';
|
|
10
|
-
import { mkdir, writeFile, access, chmod, rename, rm } from 'fs/promises';
|
|
10
|
+
import { readFile, mkdir, writeFile, access, chmod, rename, rm } from 'fs/promises';
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
import { readFileSync } from 'fs';
|
|
13
13
|
import * as tls from 'tls';
|
|
@@ -1335,8 +1335,6 @@ async function waitForApplyAndFetchTunnelToken(input) {
|
|
|
1335
1335
|
return null;
|
|
1336
1336
|
}
|
|
1337
1337
|
}
|
|
1338
|
-
|
|
1339
|
-
// src/lib/homebrew.ts
|
|
1340
1338
|
var STUDIO_PACKAGES = [
|
|
1341
1339
|
{ name: "git", kind: "formula" },
|
|
1342
1340
|
{ name: "gh", kind: "formula" },
|
|
@@ -1347,8 +1345,11 @@ var STUDIO_PACKAGES = [
|
|
|
1347
1345
|
{ name: "ollama", kind: "formula" },
|
|
1348
1346
|
// Required for the fail-closed image-signature gate in `bootstrap` (#34).
|
|
1349
1347
|
{ name: "cosign", kind: "formula" },
|
|
1350
|
-
|
|
1351
|
-
|
|
1348
|
+
// Homebrew renamed the `docker` cask to `docker-desktop`. The appPath skip
|
|
1349
|
+
// means a directly-installed Docker Desktop (the common case) is treated as
|
|
1350
|
+
// present rather than tripping `brew install` on the existing app. (#89)
|
|
1351
|
+
{ name: "docker-desktop", kind: "cask", appPath: "/Applications/Docker.app" },
|
|
1352
|
+
{ name: "tailscale", kind: "cask", appPath: "/Applications/Tailscale.app" }
|
|
1352
1353
|
];
|
|
1353
1354
|
async function detectBrew() {
|
|
1354
1355
|
const res = await safeExeca("brew", ["--version"]);
|
|
@@ -1359,7 +1360,18 @@ async function detectBrew() {
|
|
|
1359
1360
|
return { installed: true, ...version ? { version } : {} };
|
|
1360
1361
|
}
|
|
1361
1362
|
var HOMEBREW_INSTALL_COMMAND = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
|
|
1363
|
+
async function pathExists(path) {
|
|
1364
|
+
try {
|
|
1365
|
+
await access(path);
|
|
1366
|
+
return true;
|
|
1367
|
+
} catch {
|
|
1368
|
+
return false;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1362
1371
|
async function isPackageInstalled(pkg) {
|
|
1372
|
+
if (pkg.appPath && await pathExists(pkg.appPath)) {
|
|
1373
|
+
return true;
|
|
1374
|
+
}
|
|
1363
1375
|
const flag = pkg.kind === "cask" ? "--cask" : "--formula";
|
|
1364
1376
|
const res = await safeExeca("brew", ["list", flag, pkg.name]);
|
|
1365
1377
|
return res !== null && res.exitCode === 0;
|
|
@@ -2615,15 +2627,18 @@ async function collectCoreSecrets(args) {
|
|
|
2615
2627
|
}
|
|
2616
2628
|
async function collectPromptServiceSecrets(args) {
|
|
2617
2629
|
const { region, nodeType, opts } = args;
|
|
2630
|
+
let promptsDbPassword;
|
|
2631
|
+
let promptServiceApiKey;
|
|
2632
|
+
let promptServiceAdminApiKeys;
|
|
2618
2633
|
if (nodeType === "region-with-prompts") {
|
|
2619
|
-
await loadSecret({
|
|
2634
|
+
promptsDbPassword = await loadSecret({
|
|
2620
2635
|
region,
|
|
2621
2636
|
account: "prompts-db-password",
|
|
2622
2637
|
label: "prompt-service Postgres password",
|
|
2623
2638
|
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2624
2639
|
generate: generatePostgresPassword
|
|
2625
2640
|
});
|
|
2626
|
-
await loadSecret({
|
|
2641
|
+
promptServiceApiKey = await loadSecret({
|
|
2627
2642
|
region,
|
|
2628
2643
|
account: "prompt-service-api-key",
|
|
2629
2644
|
label: "prompt-service HMAC API key",
|
|
@@ -2632,7 +2647,7 @@ async function collectPromptServiceSecrets(args) {
|
|
|
2632
2647
|
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
|
|
2633
2648
|
generate: generateHmacApiKey
|
|
2634
2649
|
});
|
|
2635
|
-
await loadSecret({
|
|
2650
|
+
promptServiceAdminApiKeys = await loadSecret({
|
|
2636
2651
|
region,
|
|
2637
2652
|
account: "prompt-service-admin-api-key",
|
|
2638
2653
|
label: "prompt-service admin API key",
|
|
@@ -2640,7 +2655,7 @@ async function collectPromptServiceSecrets(args) {
|
|
|
2640
2655
|
generate: generateHmacApiKey
|
|
2641
2656
|
});
|
|
2642
2657
|
} else {
|
|
2643
|
-
await loadSecret({
|
|
2658
|
+
promptServiceApiKey = await loadSecret({
|
|
2644
2659
|
region,
|
|
2645
2660
|
account: "prompt-service-api-key",
|
|
2646
2661
|
label: "prompt-service HMAC API key (issued by the prompts team)",
|
|
@@ -2658,7 +2673,15 @@ async function collectPromptServiceSecrets(args) {
|
|
|
2658
2673
|
);
|
|
2659
2674
|
process.exit(1);
|
|
2660
2675
|
}
|
|
2661
|
-
return
|
|
2676
|
+
return {
|
|
2677
|
+
url: promptServiceUrl,
|
|
2678
|
+
...promptsDbPassword !== void 0 ? { promptsDbPassword } : {},
|
|
2679
|
+
// PROMPT_SERVICE_API_KEY is the raw key; PROMPT_SERVICE_API_KEYS is the
|
|
2680
|
+
// `<region>:<key>` list prompt-service validates against — same derivation
|
|
2681
|
+
// the op-compose wrapper does.
|
|
2682
|
+
...promptServiceApiKey !== void 0 ? { promptServiceApiKey, promptServiceApiKeys: `${region}:${promptServiceApiKey}` } : {},
|
|
2683
|
+
...promptServiceAdminApiKeys !== void 0 ? { promptServiceAdminApiKeys } : {}
|
|
2684
|
+
};
|
|
2662
2685
|
}
|
|
2663
2686
|
async function resolveSupabaseUrl(opts) {
|
|
2664
2687
|
let supabaseUrl;
|
|
@@ -2688,9 +2711,9 @@ async function resolveSupabaseUrl(opts) {
|
|
|
2688
2711
|
}
|
|
2689
2712
|
async function collectSecretsPhase(args) {
|
|
2690
2713
|
const core = await collectCoreSecrets({ region: args.region, opts: args.opts });
|
|
2691
|
-
const promptServiceUrl = await collectPromptServiceSecrets(args);
|
|
2714
|
+
const { url: promptServiceUrl, ...promptSecrets } = await collectPromptServiceSecrets(args);
|
|
2692
2715
|
const supabaseUrl = await resolveSupabaseUrl(args.opts);
|
|
2693
|
-
return { ...core, promptServiceUrl, supabaseUrl };
|
|
2716
|
+
return { ...core, ...promptSecrets, promptServiceUrl, supabaseUrl };
|
|
2694
2717
|
}
|
|
2695
2718
|
async function runLaunchAgentPhase(args) {
|
|
2696
2719
|
const { opts, secrets, llmModel, embeddingModel } = args;
|
|
@@ -2806,6 +2829,16 @@ function buildComposeEnv(args) {
|
|
|
2806
2829
|
SUPABASE_URL: secrets.supabaseUrl,
|
|
2807
2830
|
AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? secrets.jwtSecret,
|
|
2808
2831
|
...secrets.tunnelToken !== void 0 ? { TUNNEL_TOKEN: secrets.tunnelToken } : {},
|
|
2832
|
+
// prompt-service wiring — mirrors the op-compose wrapper so bootstrap's OWN
|
|
2833
|
+
// compose calls can process the region-with-prompts overlay instead of
|
|
2834
|
+
// aborting on `${PROMPTS_DB_PASSWORD:?}`. PROMPT_SERVICE_URL is always set
|
|
2835
|
+
// (in-network for colocated, remote otherwise); the rest are present only
|
|
2836
|
+
// when the node runs or calls a prompt-service. (#90)
|
|
2837
|
+
PROMPT_SERVICE_URL: secrets.promptServiceUrl,
|
|
2838
|
+
...secrets.promptsDbPassword !== void 0 ? { PROMPTS_DB_PASSWORD: secrets.promptsDbPassword } : {},
|
|
2839
|
+
...secrets.promptServiceApiKey !== void 0 ? { PROMPT_SERVICE_API_KEY: secrets.promptServiceApiKey } : {},
|
|
2840
|
+
...secrets.promptServiceApiKeys !== void 0 ? { PROMPT_SERVICE_API_KEYS: secrets.promptServiceApiKeys } : {},
|
|
2841
|
+
...secrets.promptServiceAdminApiKeys !== void 0 ? { PROMPT_SERVICE_ADMIN_API_KEYS: secrets.promptServiceAdminApiKeys } : {},
|
|
2809
2842
|
...llmModel ? { LLM_MODEL: llmModel } : {},
|
|
2810
2843
|
...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
|
|
2811
2844
|
};
|
|
@@ -3259,6 +3292,9 @@ async function resetStopStackPhase(input, deps) {
|
|
|
3259
3292
|
files: input.stack.composeFiles,
|
|
3260
3293
|
cwd: input.stack.repoPath,
|
|
3261
3294
|
...input.stack.envFile ? { envFile: input.stack.envFile } : {},
|
|
3295
|
+
// Hydrate placeholder values for the template's `${VAR:?…}` required vars
|
|
3296
|
+
// so interpolation succeeds; `down` doesn't use the values. (#85)
|
|
3297
|
+
...input.stack.env ? { env: input.stack.env } : {},
|
|
3262
3298
|
wipeVolumes: input.stack.wipeVolumes,
|
|
3263
3299
|
removeOrphans: input.stack.removeOrphans,
|
|
3264
3300
|
...input.stack.wipeImages ? { removeImages: "all" } : {}
|
|
@@ -3398,6 +3434,7 @@ var resetCommand = new Command("reset").description(
|
|
|
3398
3434
|
wipeImages
|
|
3399
3435
|
});
|
|
3400
3436
|
await confirmReset({ opts, region, wipeData });
|
|
3437
|
+
const teardownEnv = repoPath ? await computeTeardownEnv(resolveComposeFiles(repoPath, opts.composeFile)) : void 0;
|
|
3401
3438
|
const input = buildResetInput({
|
|
3402
3439
|
opts,
|
|
3403
3440
|
repoPath,
|
|
@@ -3405,7 +3442,8 @@ var resetCommand = new Command("reset").description(
|
|
|
3405
3442
|
launchAgentPaths,
|
|
3406
3443
|
wipeData,
|
|
3407
3444
|
wipeImages,
|
|
3408
|
-
removeOrphans
|
|
3445
|
+
removeOrphans,
|
|
3446
|
+
...teardownEnv ? { teardownEnv } : {}
|
|
3409
3447
|
});
|
|
3410
3448
|
await runResetWithSpinners({ opts, region, wipeData, input });
|
|
3411
3449
|
});
|
|
@@ -3514,15 +3552,41 @@ async function confirmReset(args) {
|
|
|
3514
3552
|
}
|
|
3515
3553
|
}
|
|
3516
3554
|
}
|
|
3555
|
+
var TEARDOWN_PLACEHOLDER = "reset-teardown-placeholder";
|
|
3556
|
+
var REQUIRED_VAR_RE = /\$\{([A-Za-z_]\w*):?\?/g;
|
|
3557
|
+
function buildTeardownEnv(composeFileContents, baseEnv = process.env) {
|
|
3558
|
+
const overlay = {};
|
|
3559
|
+
for (const text6 of composeFileContents) {
|
|
3560
|
+
for (const match of text6.matchAll(REQUIRED_VAR_RE)) {
|
|
3561
|
+
const name = match[1];
|
|
3562
|
+
const existing = baseEnv[name];
|
|
3563
|
+
if (existing === void 0 || existing === "") {
|
|
3564
|
+
overlay[name] = TEARDOWN_PLACEHOLDER;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
return overlay;
|
|
3569
|
+
}
|
|
3570
|
+
async function computeTeardownEnv(composeFiles) {
|
|
3571
|
+
const contents = [];
|
|
3572
|
+
for (const file of composeFiles) {
|
|
3573
|
+
try {
|
|
3574
|
+
contents.push(await readFile(file, "utf8"));
|
|
3575
|
+
} catch {
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
return buildTeardownEnv(contents);
|
|
3579
|
+
}
|
|
3517
3580
|
function buildResetInput(args) {
|
|
3518
|
-
const { opts, repoPath, plistExists, launchAgentPaths, wipeData, wipeImages, removeOrphans } = args;
|
|
3581
|
+
const { opts, repoPath, plistExists, launchAgentPaths, wipeData, wipeImages, removeOrphans, teardownEnv } = args;
|
|
3519
3582
|
const stack = repoPath ? {
|
|
3520
3583
|
repoPath,
|
|
3521
3584
|
composeFiles: resolveComposeFiles(repoPath, opts.composeFile),
|
|
3522
3585
|
wipeVolumes: wipeData,
|
|
3523
3586
|
wipeImages,
|
|
3524
3587
|
removeOrphans,
|
|
3525
|
-
...opts.envFile ? { envFile: opts.envFile } : {}
|
|
3588
|
+
...opts.envFile ? { envFile: opts.envFile } : {},
|
|
3589
|
+
...teardownEnv ? { env: teardownEnv } : {}
|
|
3526
3590
|
} : void 0;
|
|
3527
3591
|
const launchAgent = plistExists && !opts.skipLaunchAgent ? {
|
|
3528
3592
|
paths: launchAgentPaths,
|
|
@@ -5090,7 +5154,7 @@ function withDefaultSubcommand(argv, known) {
|
|
|
5090
5154
|
}
|
|
5091
5155
|
|
|
5092
5156
|
// src/cli.ts
|
|
5093
|
-
var VERSION = "0.12.
|
|
5157
|
+
var VERSION = "0.12.3";
|
|
5094
5158
|
var program = new Command();
|
|
5095
5159
|
program.name("create-op-node").description(
|
|
5096
5160
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|