@monster-orange/den 0.2.8 → 0.4.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/CHANGELOG.md +1731 -0
- package/README.md +4 -2
- package/bin/den +2 -1
- package/docs/distributions.md +730 -6
- package/make/den.mk +19 -4
- package/make/journeys.mk +2 -1
- package/make/local_dev.mk +6 -13
- package/make/secrets-1password.mk +18 -0
- package/make/test.mk +1 -1
- package/package.json +12 -2
- package/schemas/den.plugin.v1.json +156 -0
- package/schemas/den.theme.v1.json +269 -0
- package/scripts/auth-configure.ts +16 -8
- package/scripts/heal-config.ts +0 -5
- package/scripts/setup-1password.ts +41 -19
- package/scripts/setup-secrets.ts +67 -101
- package/src/pillars/api/global-setup.ts +1 -1
- package/src/pillars/journeys/sandbox.ts +23 -18
- package/src/pillars/journeys/tools/browser-session.ts +2 -2
- package/src/scripts/setup-default-workspace.ts +2 -2
- package/src/shared/agents/default-pack/be-dev.md +20 -0
- package/src/shared/agents/default-pack/devops.md +14 -0
- package/src/shared/agents/default-pack/fe-dev.md +20 -0
- package/src/shared/agents/default-pack/orchestrator.md +16 -0
- package/src/shared/agents/default-pack/planner.md +17 -0
- package/src/shared/agents/default-pack/reviewer.md +14 -0
- package/src/shared/agents/default-pack/ux-designer.md +15 -0
- package/src/shared/agents/den-claude.ts +173 -0
- package/src/shared/agents/generic.ts +52 -0
- package/src/shared/agents/personas.ts +218 -0
- package/src/shared/agents/records.ts +118 -0
- package/src/shared/agents/runtime.ts +192 -0
- package/src/shared/agents/runtimes.ts +47 -0
- package/src/shared/auth/headless-login.ts +1 -1
- package/src/shared/auth/sudo-vault.ts +93 -31
- package/src/shared/auth/token-providers/index.ts +7 -3
- package/src/shared/brand.ts +7 -0
- package/src/shared/config-error.ts +33 -3
- package/src/shared/constants.ts +3 -24
- package/src/shared/den-config.ts +257 -5
- package/src/shared/edge/config.ts +147 -21
- package/src/shared/edge/exec.ts +31 -0
- package/src/shared/edge/included.ts +169 -0
- package/src/shared/edge/render.ts +69 -14
- package/src/shared/edge/runtime.ts +128 -121
- package/src/shared/edge/status.ts +68 -8
- package/src/shared/edge/tls.ts +328 -0
- package/src/shared/edge/types.ts +25 -9
- package/src/shared/engine.ts +23 -0
- package/src/shared/environment-manager/default-workspace.ts +16 -1
- package/src/shared/environment-manager/health.ts +73 -38
- package/src/shared/environment-manager/scope.ts +168 -0
- package/src/shared/environment-manager/service-runner.ts +789 -43
- package/src/shared/environment-manager/up-targets.ts +132 -0
- package/src/shared/environment-manager/workspaces.ts +19 -0
- package/src/shared/health/rollup.ts +48 -0
- package/src/shared/open-url.ts +21 -2
- package/src/shared/org-sources.ts +78 -59
- package/src/shared/org.ts +53 -5
- package/src/shared/plugins/manifest.ts +406 -0
- package/src/shared/plugins/store.ts +358 -0
- package/src/shared/process/legacy-adopt.ts +285 -0
- package/src/shared/process/process-group.ts +202 -0
- package/src/shared/process/spawn-streaming.ts +7 -0
- package/src/shared/secret-env.ts +70 -0
- package/src/shared/secrets/doppler.ts +1 -1
- package/src/shared/secrets/index.ts +279 -50
- package/src/shared/secrets/none.ts +56 -0
- package/src/shared/secrets/strategies.ts +202 -0
- package/src/shared/secrets/types.ts +1 -1
- package/src/shared/sessions/client.ts +277 -0
- package/src/shared/sessions/den-pty.ts +97 -0
- package/src/shared/sessions/env.ts +79 -0
- package/src/shared/sessions/host.ts +667 -0
- package/src/shared/sessions/names.ts +85 -0
- package/src/shared/sessions/paths.ts +27 -0
- package/src/shared/sessions/protocol.ts +72 -0
- package/src/shared/sessions/pty.ts +141 -0
- package/src/shared/sessions/ring-buffer.ts +160 -0
- package/src/shared/sessions/types.ts +81 -0
- package/src/shared/smoke/git-remote.ts +40 -0
- package/src/shared/stacks/manifest-commands.ts +1 -1
- package/src/shared/stacks/namespace.ts +178 -0
- package/src/shared/stacks/owners.ts +303 -0
- package/src/shared/stacks/render.ts +8 -3
- package/src/shared/stacks/runtime.ts +100 -16
- package/src/shared/stacks/status.ts +63 -12
- package/src/shared/strategies/registry.ts +89 -0
- package/src/shared/theme/contract.ts +538 -0
- package/src/shared/types/index.ts +12 -3
- package/src/ui/cli/agents-command.ts +812 -0
- package/src/ui/cli/create.ts +24 -7
- package/src/ui/cli/doctor-command.ts +260 -0
- package/src/ui/cli/edge-command.ts +56 -10
- package/src/ui/cli/env-probe.ts +266 -0
- package/src/ui/cli/flag-parser.ts +18 -5
- package/src/ui/cli/health-display.ts +25 -0
- package/src/ui/cli/index.ts +580 -60
- package/src/ui/cli/notice.ts +20 -0
- package/src/ui/cli/plugin-command.ts +849 -0
- package/src/ui/cli/sessions-command.ts +718 -0
- package/src/ui/cli/stack-command.ts +63 -2
- package/src/ui/cli/test-runner.ts +11 -9
- package/src/ui/cli/theme-command.ts +136 -0
- package/src/ui/cli/web-command.ts +47 -23
- package/src/ui/mcp/api.ts +28 -2
- package/src/ui/mcp/scope.ts +145 -20
- package/src/ui/mcp/server.ts +27 -2
- package/src/ui/mcp/tools.ts +94 -12
- package/src/ui/server/appearance-prefs.ts +95 -0
- package/src/ui/server/audit.ts +35 -0
- package/src/ui/server/daemon-reaper.ts +29 -1
- package/src/ui/server/engine-stamp.ts +83 -0
- package/src/ui/server/github-service.ts +23 -44
- package/src/ui/server/graph-page.ts +20 -0
- package/src/ui/server/graph-prefs.ts +44 -1
- package/src/ui/server/http-errors.ts +293 -0
- package/src/ui/server/http-helpers.ts +22 -3
- package/src/ui/server/index.ts +539 -51
- package/src/ui/server/public/appearance.js +531 -0
- package/src/ui/server/public/edge-page.js +248 -0
- package/src/ui/server/public/edge.html +251 -0
- package/src/ui/server/public/environments.html +65 -83
- package/src/ui/server/public/error-page.js +247 -0
- package/src/ui/server/public/error.html +260 -0
- package/src/ui/server/public/graph-model.js +332 -14
- package/src/ui/server/public/graph.css +141 -47
- package/src/ui/server/public/graph.js +223 -44
- package/src/ui/server/public/header.js +396 -600
- package/src/ui/server/public/health-fit.js +248 -0
- package/src/ui/server/public/health-rollup.js +128 -0
- package/src/ui/server/public/i18n/es.js +287 -0
- package/src/ui/server/public/i18n/ja.js +276 -0
- package/src/ui/server/public/index.html +139 -438
- package/src/ui/server/public/local-dev.html +3468 -4044
- package/src/ui/server/public/logs.html +71 -208
- package/src/ui/server/public/panel.js +14 -2
- package/src/ui/server/public/release.html +175 -389
- package/src/ui/server/public/services.html +596 -770
- package/src/ui/server/public/settings-page.js +759 -0
- package/src/ui/server/public/settings-registry.js +521 -0
- package/src/ui/server/public/settings.html +590 -0
- package/src/ui/server/public/shell.js +3869 -0
- package/src/ui/server/public/topbar.js +1413 -0
- package/src/ui/server/public/version-label.js +12 -0
- package/src/ui/server/public/workspace-picker.js +74 -34
- package/src/ui/server/readonly-routes.ts +37 -3
- package/src/ui/server/services-list.ts +94 -17
- package/src/ui/server/settings-distribution.ts +74 -0
- package/src/ui/server/shell-prefs.ts +192 -0
- package/src/ui/server/theme-routes.ts +292 -0
- package/src/ui/server/theme-runtime.ts +407 -0
- package/src/ui/server/ui-assets.ts +16 -1
- package/src/ui/server/ui-language.ts +50 -0
- package/src/ui/server/workspace-routes.ts +48 -18
- package/themes/den/den-theme.json +72 -0
- package/themes/den/favicon.ico +0 -0
- package/themes/den/mark.svg +6 -0
package/scripts/heal-config.ts
CHANGED
|
@@ -16,12 +16,7 @@ import * as fs from "fs";
|
|
|
16
16
|
import * as path from "path";
|
|
17
17
|
import { execFileSync } from "child_process";
|
|
18
18
|
|
|
19
|
-
import { OP_ACCOUNT_DEFAULT } from "../src/shared/constants.js";
|
|
20
|
-
|
|
21
19
|
import { product } from "../src/shared/product.js";
|
|
22
|
-
// Ensure op (if used later) hits the right account when this script runs
|
|
23
|
-
// standalone via npx tsx, matching the pattern in setup-1password.ts et al.
|
|
24
|
-
process.env.OP_ACCOUNT ||= OP_ACCOUNT_DEFAULT;
|
|
25
20
|
|
|
26
21
|
const ROOT = process.cwd();
|
|
27
22
|
const CONFIG_PATH = path.join(ROOT, product().configFile);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 1Password Developer API Keys setup.
|
|
3
3
|
*
|
|
4
|
-
* Creates and populates the developer-keys item
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Creates and populates the developer-keys item inside a vault, which
|
|
5
|
+
* services can use to inject secrets at `make up` time via
|
|
6
|
+
* `op run --env-file=.env.op`. Where it lives is the distribution's choice:
|
|
7
|
+
* `secrets.developer_keys.{vault,item}` in the org config, else the vault
|
|
8
|
+
* and item of `secrets.refs.sudo`. Den has no default; with neither set the
|
|
9
|
+
* script says "not configured" and exits 0 (1 with --check).
|
|
7
10
|
*
|
|
8
11
|
* Which keys to collect is org data: `secrets.developer_keys.fields` in the
|
|
9
12
|
* org config (default: only the keys the engine itself uses), and
|
|
@@ -11,7 +14,8 @@
|
|
|
11
14
|
* offered as defaults.
|
|
12
15
|
*
|
|
13
16
|
* Usage:
|
|
14
|
-
* make
|
|
17
|
+
* make <cli>-setup-1password — full interactive setup (only when
|
|
18
|
+
* secrets.provider is 1password)
|
|
15
19
|
* npx tsx scripts/setup-1password.ts --check — non-interactive vault check
|
|
16
20
|
* exits 0 if ready, 1 if not
|
|
17
21
|
*/
|
|
@@ -20,22 +24,23 @@ import { execFile, spawnSync } from "child_process";
|
|
|
20
24
|
import { promisify } from "util";
|
|
21
25
|
import * as fs from "fs";
|
|
22
26
|
import * as path from "path";
|
|
23
|
-
import { fileURLToPath } from "url";
|
|
24
27
|
import chalk from "chalk";
|
|
25
28
|
import {
|
|
26
|
-
|
|
27
|
-
OP_API_KEYS_VAULT,
|
|
28
|
-
OP_DEV_KEYS_ITEM,
|
|
29
|
-
} from "../src/shared/constants.js";
|
|
30
|
-
import {
|
|
29
|
+
notConfiguredMessage,
|
|
31
30
|
readSecretsConfig,
|
|
31
|
+
secretsStrategy,
|
|
32
32
|
type DeveloperKeyDef,
|
|
33
33
|
} from "../src/shared/secrets/index.js";
|
|
34
34
|
|
|
35
35
|
import { product } from "../src/shared/product.js";
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
import { getDistRoot } from "../src/shared/paths.js";
|
|
37
|
+
const SECRETS_CFG = readSecretsConfig();
|
|
38
|
+
// op uses the distribution's account (secrets.onepassword.account) or the
|
|
39
|
+
// engineer's own OP_ACCOUNT; Den has none of its own.
|
|
40
|
+
Object.assign(
|
|
41
|
+
process.env,
|
|
42
|
+
secretsStrategy("1password")?.capabilities.env?.(SECRETS_CFG, process.env),
|
|
43
|
+
);
|
|
39
44
|
|
|
40
45
|
const execFileAsync = promisify(execFile);
|
|
41
46
|
|
|
@@ -46,11 +51,19 @@ async function opJson<T>(args: string[]): Promise<T> {
|
|
|
46
51
|
|
|
47
52
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
const DEN_ROOT =
|
|
54
|
+
// dotenv paths are relative to the distribution root, never the engine.
|
|
55
|
+
const DEN_ROOT = getDistRoot();
|
|
51
56
|
|
|
52
|
-
const
|
|
53
|
-
|
|
57
|
+
const pick = (...v: Array<string | undefined>): string =>
|
|
58
|
+
v.map((x) => (typeof x === "string" ? x.trim() : "")).find(Boolean) ?? "";
|
|
59
|
+
const VAULT_NAME = pick(
|
|
60
|
+
SECRETS_CFG.developer_keys?.vault,
|
|
61
|
+
SECRETS_CFG.refs?.sudo?.vault,
|
|
62
|
+
);
|
|
63
|
+
const ITEM_NAME = pick(
|
|
64
|
+
SECRETS_CFG.developer_keys?.item,
|
|
65
|
+
SECRETS_CFG.refs?.sudo?.item,
|
|
66
|
+
);
|
|
54
67
|
|
|
55
68
|
// ─── .env reader ─────────────────────────────────────────────────────────────
|
|
56
69
|
|
|
@@ -89,7 +102,7 @@ function parseEnvFile(filePath: string): Record<string, string> {
|
|
|
89
102
|
return result;
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
const DEVELOPER_KEYS =
|
|
105
|
+
const DEVELOPER_KEYS = SECRETS_CFG.developer_keys ?? {};
|
|
93
106
|
|
|
94
107
|
/**
|
|
95
108
|
* The dotenv file `secrets.developer_keys.env_file` names (relative to the
|
|
@@ -560,7 +573,16 @@ async function setupMode(): Promise<void> {
|
|
|
560
573
|
|
|
561
574
|
const isCheck = process.argv.includes("--check");
|
|
562
575
|
|
|
563
|
-
if (
|
|
576
|
+
if (!VAULT_NAME || !ITEM_NAME) {
|
|
577
|
+
// No place for the developer keys: nothing to set up, and no op call.
|
|
578
|
+
console.log(
|
|
579
|
+
`Developer keys item ${notConfiguredMessage("sudo").replace(
|
|
580
|
+
/secrets\.refs\.sudo/,
|
|
581
|
+
"secrets.developer_keys.vault + .item (or secrets.refs.sudo)",
|
|
582
|
+
)}`,
|
|
583
|
+
);
|
|
584
|
+
process.exit(isCheck ? 1 : 0);
|
|
585
|
+
} else if (isCheck) {
|
|
564
586
|
checkMode().catch(() => process.exit(1));
|
|
565
587
|
} else {
|
|
566
588
|
setupMode().catch((err: Error) => {
|
package/scripts/setup-secrets.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Backend-agnostic developer-secrets setup.
|
|
3
3
|
*
|
|
4
|
-
* Dispatches to the strategy selected by
|
|
5
|
-
*
|
|
6
|
-
* config/local/user.yml). See src/shared/secrets.
|
|
4
|
+
* Dispatches to the `setup` capability of the strategy selected by
|
|
5
|
+
* `secrets.provider` in the org config (override: DEN_SECRETS_PROVIDER or
|
|
6
|
+
* config/local/user.yml). See src/shared/secrets/strategies.ts.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* make den-setup-secrets — interactive setup
|
|
10
10
|
* npx tsx scripts/setup-secrets.ts --check — non-interactive; exit 0 when ready
|
|
11
11
|
* npx tsx scripts/setup-secrets.ts --cli — print "<provider> <binary> <formula>"
|
|
12
|
-
* npx tsx scripts/setup-secrets.ts --
|
|
13
|
-
*
|
|
14
|
-
* secrets.required
|
|
12
|
+
* npx tsx scripts/setup-secrets.ts --provider — print the selected strategy id
|
|
13
|
+
* npx tsx scripts/setup-secrets.ts --gate — the up / web gate: passes
|
|
14
|
+
* silently unless secrets.required
|
|
15
|
+
* is true, then like --check
|
|
16
|
+
*
|
|
17
|
+
* With `secrets.provider: none` there is nothing to set up: it says so and
|
|
18
|
+
* exits 0.
|
|
15
19
|
*/
|
|
16
20
|
import { spawnSync } from "child_process";
|
|
17
21
|
import * as path from "path";
|
|
@@ -20,6 +24,8 @@ import chalk from "chalk";
|
|
|
20
24
|
import {
|
|
21
25
|
getSecretsProvider,
|
|
22
26
|
readSecretsConfig,
|
|
27
|
+
resolveProvider,
|
|
28
|
+
strategyFor,
|
|
23
29
|
} from "../src/shared/secrets/index.js";
|
|
24
30
|
|
|
25
31
|
import { envKey, product } from "../src/shared/product.js";
|
|
@@ -31,126 +37,86 @@ function run(cmd: string, args: string[]): number {
|
|
|
31
37
|
return spawnSync(cmd, args, { stdio: "inherit", cwd: ROOT }).status ?? 1;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const INSTALL: Record<string, string> = {
|
|
39
|
-
doppler: "brew install dopplerhq/cli/doppler",
|
|
40
|
-
op: "brew install 1password-cli",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
function otherBackends(current: string): string {
|
|
44
|
-
return (
|
|
45
|
-
"Or pick another backend for just you (config/local/user.yml):\n" +
|
|
46
|
-
" secrets:\n" +
|
|
47
|
-
` provider: env # or 1password / doppler (team default: ${current})\n` +
|
|
48
|
-
` or for one shell: ${envKey("SECRETS_PROVIDER")}=env make web`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
40
|
+
const NONE_MESSAGE =
|
|
41
|
+
"No secrets backend is configured (secrets.provider: none), so there is nothing to set up.\n" +
|
|
42
|
+
`To use one, set secrets.provider (doppler | 1password | env) and secrets.refs.<name> in ${product().configFile}.`;
|
|
51
43
|
|
|
52
44
|
async function main(): Promise<number> {
|
|
53
|
-
const provider = getSecretsProvider();
|
|
54
45
|
const cfg = readSecretsConfig();
|
|
55
|
-
const
|
|
46
|
+
const resolved = resolveProvider(cfg);
|
|
47
|
+
const strategy = strategyFor(resolved.name);
|
|
48
|
+
const label = `secrets backend: ${resolved.name}`;
|
|
49
|
+
|
|
50
|
+
// `--provider`: print the selected strategy id (make reads it to offer
|
|
51
|
+
// backend-specific targets). Never probes the backend.
|
|
52
|
+
if (process.argv.includes("--provider")) {
|
|
53
|
+
console.log(resolved.name);
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
56
|
|
|
57
57
|
// `--cli`: print "<provider> <binary> <brew formula>" for make setup's
|
|
58
|
-
// install step ("
|
|
58
|
+
// install step ("<provider> - -" when the backend needs no CLI). An unset
|
|
59
|
+
// provider needs nothing installed until something asks for a secret.
|
|
59
60
|
if (process.argv.includes("--cli")) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
? "doppler"
|
|
63
|
-
: provider.name === "1password"
|
|
64
|
-
? "op"
|
|
65
|
-
: "";
|
|
66
|
-
const formula = bin ? INSTALL[bin].replace(/^brew install /, "") : "";
|
|
67
|
-
console.log(`${provider.name} ${bin || "-"} ${formula || "-"}`);
|
|
61
|
+
const cli = resolved.implicit ? undefined : strategy.capabilities.cli;
|
|
62
|
+
console.log(`${resolved.name} ${cli?.bin ?? "-"} ${cli?.formula ?? "-"}`);
|
|
68
63
|
return 0;
|
|
69
64
|
}
|
|
70
65
|
|
|
66
|
+
// The up / web gate: only a distribution that requires its backend
|
|
67
|
+
// (secrets.required) is checked. Nothing required: pass, silently, whatever
|
|
68
|
+
// the provider; a feature that needs a secret reports it when it asks.
|
|
69
|
+
if (gate && !cfg.required) return 0;
|
|
70
|
+
|
|
71
71
|
if (check || gate) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Signed in but no default scope: secret_env refs name their own
|
|
76
|
-
// (<project>/<config>/<key>), so only the engine's own secrets lack one.
|
|
77
|
-
if (await provider.isReady()) {
|
|
78
|
-
console.error(
|
|
79
|
-
chalk.gray(
|
|
80
|
-
` ${label}: signed in, no default scope set (secret_env refs still resolve;` +
|
|
81
|
-
` test logins and the saved sudo password need one: \`make ${product().cli}-setup-secrets\`).`,
|
|
82
|
-
),
|
|
83
|
-
);
|
|
84
|
-
return 0;
|
|
85
|
-
}
|
|
86
|
-
console.error(
|
|
87
|
-
chalk.yellow(
|
|
88
|
-
`⚠️ ${label} is not ready — continuing.\n` +
|
|
89
|
-
" Test logins and the saved sudo password need it; run " +
|
|
90
|
-
`\`make ${product().cli}-setup-secrets\` when you want them.`,
|
|
91
|
-
),
|
|
92
|
-
);
|
|
72
|
+
if (resolved.name === "none" || (resolved.implicit && !cfg.required)) {
|
|
73
|
+
if (resolved.name === "none")
|
|
74
|
+
console.log(` ✓ ${NONE_MESSAGE.split("\n")[0]}`);
|
|
93
75
|
return 0;
|
|
94
76
|
}
|
|
77
|
+
const r = await getSecretsProvider().preflight();
|
|
78
|
+
if (r.ok) return 0;
|
|
95
79
|
console.error(chalk.red(`✗ ${label}\n${r.message ?? ""}`));
|
|
96
80
|
return 1;
|
|
97
81
|
}
|
|
98
82
|
|
|
83
|
+
const setup = strategy.capabilities.setup;
|
|
84
|
+
if (resolved.name === "none" || !setup) {
|
|
85
|
+
console.log(NONE_MESSAGE);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
if (resolved.implicit && !cfg.required) {
|
|
89
|
+
// Nothing chose a backend and nothing requires one: no store to set up.
|
|
90
|
+
console.log(
|
|
91
|
+
"secrets.provider is not set, so there is nothing to set up.\n" +
|
|
92
|
+
`To use a backend, set secrets.provider (doppler | 1password | env | none) in ${product().configFile}.`,
|
|
93
|
+
);
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
99
96
|
console.log(
|
|
100
97
|
chalk.bold(`\n${product().title} developer secrets (${label})\n`),
|
|
101
98
|
);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log("→ doppler login");
|
|
114
|
-
if (run("doppler", ["login"]) !== 0) return 1;
|
|
115
|
-
}
|
|
116
|
-
const scope: string[] = [];
|
|
117
|
-
if (cfg.doppler?.project) scope.push("--project", cfg.doppler.project);
|
|
118
|
-
if (cfg.doppler?.config) scope.push("--config", cfg.doppler.config);
|
|
119
|
-
console.log(`→ doppler setup ${scope.join(" ")}`.trim());
|
|
120
|
-
if (
|
|
121
|
-
run("doppler", ["setup", "--no-interactive", ...scope]) !== 0 &&
|
|
122
|
-
scope.length === 0
|
|
123
|
-
) {
|
|
124
|
-
run("doppler", ["setup"]);
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
case "1password":
|
|
129
|
-
if (!installed("op")) {
|
|
130
|
-
console.error(
|
|
131
|
-
chalk.red("✗ The 1Password CLI (op) is not installed.\n") +
|
|
132
|
-
` Install it: ${INSTALL.op}\n\n ` +
|
|
133
|
-
otherBackends(provider.name),
|
|
134
|
-
);
|
|
135
|
-
return 1;
|
|
136
|
-
}
|
|
137
|
-
return run(path.join(ROOT, "node_modules", ".bin", "tsx"), [
|
|
138
|
-
path.join(ROOT, "scripts", "setup-1password.ts"),
|
|
139
|
-
]);
|
|
140
|
-
case "env":
|
|
141
|
-
console.log(
|
|
142
|
-
"The env backend reads secrets from the process environment.\n" +
|
|
143
|
-
`Export them yourself (direnv, CI secrets, \`doppler run -- ${product().cli} up\`).`,
|
|
144
|
-
);
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
99
|
+
const provider = getSecretsProvider();
|
|
100
|
+
const code = await setup({
|
|
101
|
+
cfg,
|
|
102
|
+
provider,
|
|
103
|
+
engineRoot: ROOT,
|
|
104
|
+
cli: product().cli,
|
|
105
|
+
run,
|
|
106
|
+
log: (l) => console.log(l),
|
|
107
|
+
error: (l) => console.error(chalk.red(l)),
|
|
108
|
+
});
|
|
109
|
+
if (code !== 0) return code;
|
|
147
110
|
const r = await provider.preflight();
|
|
148
111
|
console.log(
|
|
149
112
|
r.ok ? chalk.green(`✓ ${label} ready`) : chalk.red(`✗ ${r.message}`),
|
|
150
113
|
);
|
|
114
|
+
if (!r.ok)
|
|
115
|
+
console.log(
|
|
116
|
+
` Or for one shell: ${envKey("SECRETS_PROVIDER")}=none make web`,
|
|
117
|
+
);
|
|
151
118
|
return r.ok ? 0 : 1;
|
|
152
119
|
}
|
|
153
|
-
|
|
154
120
|
main().then(
|
|
155
121
|
(code) => process.exit(code),
|
|
156
122
|
(err: unknown) => {
|
|
@@ -86,7 +86,7 @@ async function globalSetup(_config: FullConfig) {
|
|
|
86
86
|
` ⚠ No credentials for ${role} — authenticated ${role} tests will fail`,
|
|
87
87
|
);
|
|
88
88
|
console.warn(
|
|
89
|
-
`
|
|
89
|
+
` set E2E_${role.toUpperCase()}_EMAIL / _PASSWORD, or set up the secrets backend (secrets.provider)`,
|
|
90
90
|
);
|
|
91
91
|
} else {
|
|
92
92
|
// Token mint failure, IdP misconfiguration, etc. — fail fast
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
|
|
37
37
|
import * as fs from "fs";
|
|
38
38
|
import * as path from "path";
|
|
39
|
-
import { OP_API_KEYS_VAULT, OP_DEV_KEYS_ITEM } from "../../shared/constants.js";
|
|
40
39
|
import {
|
|
41
40
|
getSecretsProvider,
|
|
42
41
|
namedSecretRef,
|
|
42
|
+
notConfiguredMessage,
|
|
43
43
|
type SecretRef,
|
|
44
44
|
} from "../../shared/secrets/index.js";
|
|
45
45
|
import type { Environment } from "../../shared/types/index.js";
|
|
@@ -51,22 +51,12 @@ import { getDistRoot } from "../../shared/paths.js";
|
|
|
51
51
|
import { containsValue } from "../../shared/redact.js";
|
|
52
52
|
// ── Layer 1: captureSecrets ──────────────────────────────────────────────────
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/** The Cursor API key's env var name (and the usual field label). */
|
|
55
55
|
export const CURSOR_API_KEY_FIELD = "CURSOR_API_KEY";
|
|
56
56
|
|
|
57
57
|
/** Name of the agent-key ref under `secrets.refs` in the org config. */
|
|
58
58
|
export const JOURNEYS_AGENT_KEY_REF = "journeys_agent_key";
|
|
59
59
|
|
|
60
|
-
/**
|
|
61
|
-
* Where the agent key lives when `secrets.refs.journeys_agent_key` is not
|
|
62
|
-
* configured: the engineer's own developer-keys item (neutral default).
|
|
63
|
-
*/
|
|
64
|
-
export const JOURNEYS_AGENT_KEY_DEFAULT: SecretRef = {
|
|
65
|
-
vault: OP_API_KEYS_VAULT,
|
|
66
|
-
item: OP_DEV_KEYS_ITEM,
|
|
67
|
-
field: CURSOR_API_KEY_FIELD,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
60
|
/**
|
|
71
61
|
* Secrets captured into memory before environment sanitization.
|
|
72
62
|
*
|
|
@@ -84,6 +74,7 @@ export interface OpFieldRequest {
|
|
|
84
74
|
item: string;
|
|
85
75
|
vault: string;
|
|
86
76
|
field: string;
|
|
77
|
+
key?: string;
|
|
87
78
|
}
|
|
88
79
|
|
|
89
80
|
export interface CaptureSecretsDeps {
|
|
@@ -91,8 +82,11 @@ export interface CaptureSecretsDeps {
|
|
|
91
82
|
opReadField?: (request: OpFieldRequest) => Promise<string>;
|
|
92
83
|
/** Env-var source; defaults to process.env. */
|
|
93
84
|
env?: NodeJS.ProcessEnv;
|
|
94
|
-
/**
|
|
95
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Where the key lives; defaults to the configured `journeys_agent_key`
|
|
87
|
+
* ref. null: not configured (there is no built-in location).
|
|
88
|
+
*/
|
|
89
|
+
agentKeyRef?: SecretRef | null;
|
|
96
90
|
}
|
|
97
91
|
|
|
98
92
|
/** Default reader: the configured secrets strategy. */
|
|
@@ -118,14 +112,25 @@ export async function captureSecrets(
|
|
|
118
112
|
if (cursorApiKey.length === 0) {
|
|
119
113
|
let opError = "";
|
|
120
114
|
const ref =
|
|
121
|
-
deps.agentKeyRef
|
|
122
|
-
|
|
115
|
+
deps.agentKeyRef !== undefined
|
|
116
|
+
? deps.agentKeyRef
|
|
117
|
+
: namedSecretRef(JOURNEYS_AGENT_KEY_REF);
|
|
118
|
+
if (!ref) {
|
|
119
|
+
// No configured place for the key: say so, without asking a backend.
|
|
120
|
+
throw new Error(
|
|
121
|
+
`No Cursor API key available for journey agents.\n` +
|
|
122
|
+
` Options:\n` +
|
|
123
|
+
` • set the ${CURSOR_API_KEY_FIELD} env var\n` +
|
|
124
|
+
` • secrets backend: ${notConfiguredMessage(JOURNEYS_AGENT_KEY_REF)}\n`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
123
127
|
try {
|
|
124
128
|
cursorApiKey = (
|
|
125
129
|
await opReadField({
|
|
126
130
|
item: ref.item,
|
|
127
131
|
vault: ref.vault,
|
|
128
132
|
field: ref.field,
|
|
133
|
+
...(ref.key ? { key: ref.key } : {}),
|
|
129
134
|
})
|
|
130
135
|
).trim();
|
|
131
136
|
} catch (error) {
|
|
@@ -137,8 +142,8 @@ export async function captureSecrets(
|
|
|
137
142
|
` Options:\n` +
|
|
138
143
|
` • CI: set the ${CURSOR_API_KEY_FIELD} env var\n` +
|
|
139
144
|
` • Local: sign in to the secrets backend (make ${product().cli}-setup-secrets) and\n` +
|
|
140
|
-
` make sure
|
|
141
|
-
` ${ref.field}
|
|
145
|
+
` make sure secrets.refs.${JOURNEYS_AGENT_KEY_REF} (item "${ref.item}", vault "${ref.vault}",\n` +
|
|
146
|
+
` field ${ref.field}) holds the key\n` +
|
|
142
147
|
(opError ? ` Secrets backend error: ${opError}\n` : ""),
|
|
143
148
|
);
|
|
144
149
|
}
|
|
@@ -32,7 +32,7 @@ import { product } from "../../../shared/product.js";
|
|
|
32
32
|
* Mint a fresh authenticated browser session for `role` against `env`'s
|
|
33
33
|
* web surface and return the storageState auth-file path (and nothing
|
|
34
34
|
* else). Throws a loud, actionable error when credentials cannot be
|
|
35
|
-
* sourced — naming both the
|
|
35
|
+
* sourced — naming both the secrets-backend path (local) and the E2E_* env-var
|
|
36
36
|
* path (CI), mirroring browser/global-setup.ts.
|
|
37
37
|
*/
|
|
38
38
|
export async function provisionBrowserSession(
|
|
@@ -47,7 +47,7 @@ export async function provisionBrowserSession(
|
|
|
47
47
|
if (!credentials) {
|
|
48
48
|
const localHint =
|
|
49
49
|
role === "admin"
|
|
50
|
-
? ` Local: make auth-configure env=${env}
|
|
50
|
+
? ` Local: with secrets.provider: 1password, make auth-configure env=${env}\n`
|
|
51
51
|
: ` Local: sign in to the secrets backend (make ${product().cli}-setup-secrets); the ${role} account comes from test_accounts in the org config\n`;
|
|
52
52
|
throw new Error(
|
|
53
53
|
`No credentials for ${role}.\n` +
|
|
@@ -145,7 +145,7 @@ async function main() {
|
|
|
145
145
|
' This builds the "default" workspace — every standard system repo,\n' +
|
|
146
146
|
` all managed: ${product().cli} clones and keeps each one up to date for\n` +
|
|
147
147
|
" you, no checkouts or decisions required. For anything you actively\n" +
|
|
148
|
-
" develop, flip that repo to dev-managed in the web UX (
|
|
148
|
+
" develop, flip that repo to dev-managed in the web UX (Workspaces)\n" +
|
|
149
149
|
" and point it at your own checkout.\n",
|
|
150
150
|
);
|
|
151
151
|
|
|
@@ -183,7 +183,7 @@ async function main() {
|
|
|
183
183
|
`\n ✓ Default workspace configured with ${seed.length} service(s), all managed.`,
|
|
184
184
|
);
|
|
185
185
|
console.log(
|
|
186
|
-
" Change any repo to dev-managed in the web UX: make web →
|
|
186
|
+
" Change any repo to dev-managed in the web UX: make web → Workspaces.",
|
|
187
187
|
);
|
|
188
188
|
console.log(` Run 'make ${product().cli}-up' to start your services.\n`);
|
|
189
189
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: be-dev
|
|
3
|
+
description: Implements back-end stories, test first, keeping contracts stable.
|
|
4
|
+
context:
|
|
5
|
+
[
|
|
6
|
+
the story and its acceptance criteria,
|
|
7
|
+
the service's API contracts,
|
|
8
|
+
the repo's conventions,
|
|
9
|
+
]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are a back-end developer. Implement one story end to end.
|
|
13
|
+
|
|
14
|
+
- Write a failing test for each acceptance criterion first, then make it pass.
|
|
15
|
+
- Keep public contracts backward compatible unless the story says otherwise;
|
|
16
|
+
call out any migration.
|
|
17
|
+
- Validate input at the boundary, handle errors explicitly, and never log
|
|
18
|
+
secrets.
|
|
19
|
+
- Run the repo's checks before opening a pull request, and describe what you
|
|
20
|
+
verified in it.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devops
|
|
3
|
+
description: Builds and maintains pipelines, environments and service configuration.
|
|
4
|
+
context: [the story, the workspace's service manifests, CI configuration]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a devops engineer. Make services easy to build, run and ship.
|
|
8
|
+
|
|
9
|
+
- Prefer declarative configuration and small, reversible changes.
|
|
10
|
+
- Keep secrets in the configured secret store; never write a value into a
|
|
11
|
+
file, a log or a pull request.
|
|
12
|
+
- Prove each change locally (bring the service up, check its health) before
|
|
13
|
+
proposing it.
|
|
14
|
+
- Explain the operational impact and the rollback in the pull request.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fe-dev
|
|
3
|
+
description: Implements front-end stories, test first, to the design system.
|
|
4
|
+
context:
|
|
5
|
+
[
|
|
6
|
+
the story and its acceptance criteria,
|
|
7
|
+
the design system,
|
|
8
|
+
the front-end conventions of the repo,
|
|
9
|
+
]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are a front-end developer. Implement one story end to end.
|
|
13
|
+
|
|
14
|
+
- Write a failing test for each acceptance criterion first, then make it pass.
|
|
15
|
+
- Use the design system's components and tokens; do not invent new ones
|
|
16
|
+
without the ux-designer.
|
|
17
|
+
- Keep the change small and focused on the story. Check accessibility and
|
|
18
|
+
narrow screens.
|
|
19
|
+
- Run the repo's checks before opening a pull request, and describe what you
|
|
20
|
+
verified in it.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orchestrator
|
|
3
|
+
description: Takes the next ready story, picks the persona for it and starts that agent.
|
|
4
|
+
standing: true
|
|
5
|
+
context: [ready work items, running agents in the workspace]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are the orchestrator. Keep work flowing without overloading the machine
|
|
9
|
+
or the people reviewing it.
|
|
10
|
+
|
|
11
|
+
- Take the next ready story, in priority order. Pick the persona that fits it:
|
|
12
|
+
fe-dev, be-dev or devops for implementation, ux-designer for design work.
|
|
13
|
+
- Start one agent per story, in its own worktree, and link it to the story.
|
|
14
|
+
- Respect the workspace's concurrency cap; wait rather than queueing more.
|
|
15
|
+
- When a pull request is opened, ask the reviewer for a review. Never approve
|
|
16
|
+
or merge anything yourself.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: planner
|
|
3
|
+
description: Writes stories and their acceptance criteria for the workspace.
|
|
4
|
+
standing: true
|
|
5
|
+
context: [the workspace's specs and docs, open work items, the design system]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are the planner. Turn goals and requests into small, independently
|
|
9
|
+
shippable stories, each with a clear user outcome and testable acceptance
|
|
10
|
+
criteria.
|
|
11
|
+
|
|
12
|
+
- Read the existing specs and work items before writing a new story; extend
|
|
13
|
+
them rather than duplicating them.
|
|
14
|
+
- Keep each story to one vertical slice that can be reviewed on its own.
|
|
15
|
+
- Suggest an architecture or design record (ARD, ADR or LLD) only when a story
|
|
16
|
+
changes a contract, a data model or a boundary between components.
|
|
17
|
+
- Hand each ready story to the orchestrator. You do not write code.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reviewer
|
|
3
|
+
description: Reviews a pull request adversarially. Never approves or merges.
|
|
4
|
+
context: [the pull request and its story, the repo's conventions and tests]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the reviewer. Try to prove the change wrong before anyone relies on it.
|
|
8
|
+
|
|
9
|
+
- Check it against the story's acceptance criteria, then look for what the
|
|
10
|
+
author did not test: edge cases, failure paths, security, performance.
|
|
11
|
+
- Verify each finding by reading the code; drop anything you cannot show.
|
|
12
|
+
- Report findings ranked by impact, each with a concrete scenario and a
|
|
13
|
+
suggested fix.
|
|
14
|
+
- You never approve, merge or push. A person decides.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ux-designer
|
|
3
|
+
description: Designs flows and screens with the design system, on request.
|
|
4
|
+
context: [the story, the design system, existing screens of the product]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the UX designer. Make the next step obvious for the person using the
|
|
8
|
+
product.
|
|
9
|
+
|
|
10
|
+
- Start from the user's goal and the story's acceptance criteria.
|
|
11
|
+
- Compose from the design system's components; propose a new component only
|
|
12
|
+
when none fits, and say why.
|
|
13
|
+
- Describe states (empty, loading, error, success) and narrow screens, not only
|
|
14
|
+
the happy path.
|
|
15
|
+
- Hand off a short spec the developers can implement and test.
|