@rulebricks/cli 2.1.7 → 2.3.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 +51 -16
- package/cluster-setup/aws/README.md +96 -47
- package/cluster-setup/aws/check-aws-access.sh +216 -52
- package/cluster-setup/aws/parameters.json +13 -0
- package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
- package/cluster-setup/azure/README.md +103 -55
- package/cluster-setup/azure/check-aks-prereqs.sh +236 -56
- package/cluster-setup/azure/parameters.json +30 -0
- package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
- package/cluster-setup/gcp/README.md +51 -34
- package/cluster-setup/gcp/check-gke-prereqs.sh +222 -60
- package/dist/commands/backup.d.ts +5 -0
- package/dist/commands/backup.js +104 -0
- package/dist/commands/deploy.d.ts +3 -1
- package/dist/commands/deploy.js +226 -326
- package/dist/commands/destroy.d.ts +1 -1
- package/dist/commands/destroy.js +73 -123
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.js +78 -54
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +74 -0
- package/dist/commands/open.d.ts +1 -1
- package/dist/commands/open.js +4 -12
- package/dist/commands/redeploy.d.ts +6 -0
- package/dist/commands/redeploy.js +310 -0
- package/dist/commands/restore.d.ts +5 -0
- package/dist/commands/restore.js +338 -0
- package/dist/commands/status.js +62 -49
- package/dist/commands/upgrade.js +74 -51
- package/dist/components/DNSWaitScreen.d.ts +5 -1
- package/dist/components/DNSWaitScreen.js +47 -41
- package/dist/components/Wizard/WizardContext.d.ts +157 -36
- package/dist/components/Wizard/WizardContext.js +872 -160
- package/dist/components/Wizard/steps/CloudProviderStep.js +192 -107
- package/dist/components/Wizard/steps/DomainStep.js +5 -24
- package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
- package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
- package/dist/components/Wizard/steps/FeatureConfigStep.js +739 -425
- package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
- package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
- package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
- package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
- package/dist/components/Wizard/steps/ReviewStep.js +56 -12
- package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
- package/dist/components/Wizard/steps/StorageStep.js +592 -0
- package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
- package/dist/components/Wizard/steps/VersionStep.js +45 -23
- package/dist/components/Wizard/steps/index.d.ts +3 -3
- package/dist/components/Wizard/steps/index.js +3 -3
- package/dist/components/common/CommandApproval.d.ts +12 -0
- package/dist/components/common/CommandApproval.js +91 -0
- package/dist/components/common/DeploymentPicker.d.ts +14 -0
- package/dist/components/common/DeploymentPicker.js +16 -0
- package/dist/components/common/index.d.ts +2 -0
- package/dist/components/common/index.js +2 -0
- package/dist/index.js +94 -62
- package/dist/lib/cloudCli.d.ts +134 -63
- package/dist/lib/cloudCli.js +512 -220
- package/dist/lib/clusterSetupDefaults.d.ts +30 -0
- package/dist/lib/clusterSetupDefaults.js +64 -0
- package/dist/lib/commandApproval.d.ts +26 -0
- package/dist/lib/commandApproval.js +114 -0
- package/dist/lib/config.d.ts +12 -10
- package/dist/lib/config.js +91 -33
- package/dist/lib/configFixtures.d.ts +5 -0
- package/dist/lib/configFixtures.js +513 -0
- package/dist/lib/deploymentHealth.d.ts +32 -0
- package/dist/lib/deploymentHealth.js +157 -0
- package/dist/lib/dns.d.ts +1 -1
- package/dist/lib/dns.js +19 -1
- package/dist/lib/dns.test.d.ts +1 -0
- package/dist/lib/dns.test.js +27 -0
- package/dist/lib/dockerHub.d.ts +12 -1
- package/dist/lib/dockerHub.js +18 -8
- package/dist/lib/helm.d.ts +4 -0
- package/dist/lib/helm.js +16 -0
- package/dist/lib/helmValues.d.ts +25 -0
- package/dist/lib/helmValues.js +1762 -289
- package/dist/lib/helmValues.test.d.ts +1 -0
- package/dist/lib/helmValues.test.js +966 -0
- package/dist/lib/htpasswd.d.ts +1 -0
- package/dist/lib/htpasswd.js +15 -0
- package/dist/lib/kubernetes.d.ts +124 -17
- package/dist/lib/kubernetes.js +576 -145
- package/dist/lib/secrets.d.ts +23 -0
- package/dist/lib/secrets.js +158 -0
- package/dist/lib/validateValues.d.ts +31 -0
- package/dist/lib/validateValues.js +253 -0
- package/dist/lib/versions.d.ts +82 -11
- package/dist/lib/versions.js +131 -31
- package/dist/lib/versions.test.d.ts +1 -0
- package/dist/lib/versions.test.js +81 -0
- package/dist/lib/wizardSteps.d.ts +14 -0
- package/dist/lib/wizardSteps.js +23 -0
- package/dist/lib/workloadIdentity.d.ts +26 -0
- package/dist/lib/workloadIdentity.js +323 -0
- package/dist/lib/workloadIdentity.test.d.ts +1 -0
- package/dist/lib/workloadIdentity.test.js +57 -0
- package/dist/types/index.d.ts +1860 -164
- package/dist/types/index.js +518 -295
- package/package.json +9 -4
- package/schema/values.schema.json +1934 -0
- package/cluster-setup/aws/cluster.yaml +0 -33
- package/cluster-setup/azure/main.bicep +0 -282
- package/cluster-setup/azure/main.parameters.json +0 -21
- package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
- package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
- package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
- package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
- package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
- package/dist/components/Wizard/steps/TierStep.js +0 -29
- package/dist/lib/terraform.d.ts +0 -66
- package/dist/lib/terraform.js +0 -754
- package/terraform/aws/main.tf +0 -355
- package/terraform/azure/main.tf +0 -371
- package/terraform/gcp/main.tf +0 -407
|
@@ -5,17 +5,19 @@ import TextInput from "ink-text-input";
|
|
|
5
5
|
import { useWizard } from "../WizardContext.js";
|
|
6
6
|
import { BorderBox } from "../../common/index.js";
|
|
7
7
|
import { generateSecureSecret } from "../../../lib/validation.js";
|
|
8
|
-
// Fixed JWT secret - users can edit this in the raw config if needed
|
|
9
|
-
const JWT_SECRET = "your-super-secret-jwt-token-with-at-least-32-characters-long";
|
|
10
8
|
export function SupabaseCredentialsStep({ onComplete, onBack, }) {
|
|
11
9
|
const { state, dispatch } = useWizard();
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// Secure fallbacks used only when the user leaves a field empty. Generated
|
|
11
|
+
// once so they stay stable across renders. The inputs themselves start empty
|
|
12
|
+
// (or prefilled with an existing value when editing) so there's no confusing
|
|
13
|
+
// pre-filled secret to clear.
|
|
14
|
+
const [defaultDbPass] = useState(() => generateSecureSecret(24));
|
|
15
|
+
const [defaultDashboardPass] = useState(() => generateSecureSecret(16));
|
|
16
|
+
const [defaultJwtSecret] = useState(() => state.supabaseJwtSecret || generateSecureSecret(64));
|
|
15
17
|
const [subStep, setSubStep] = useState("db-password");
|
|
16
|
-
const [dbPassword, setDbPassword] = useState(
|
|
18
|
+
const [dbPassword, setDbPassword] = useState(state.supabaseDbPassword || "");
|
|
17
19
|
const [dashboardUser, setDashboardUser] = useState(state.supabaseDashboardUser || "supabase");
|
|
18
|
-
const [dashboardPass, setDashboardPass] = useState(
|
|
20
|
+
const [dashboardPass, setDashboardPass] = useState(state.supabaseDashboardPass || "");
|
|
19
21
|
const [error, setError] = useState(null);
|
|
20
22
|
useInput((input, key) => {
|
|
21
23
|
if (key.escape) {
|
|
@@ -32,10 +34,13 @@ export function SupabaseCredentialsStep({ onComplete, onBack, }) {
|
|
|
32
34
|
}
|
|
33
35
|
});
|
|
34
36
|
const handleDbPasswordSubmit = () => {
|
|
35
|
-
|
|
37
|
+
// Empty means "use a generated secure value".
|
|
38
|
+
const effective = dbPassword.trim() || defaultDbPass;
|
|
39
|
+
if (effective.length < 8) {
|
|
36
40
|
setError("Database password must be at least 8 characters");
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
43
|
+
setDbPassword(effective);
|
|
39
44
|
setError(null);
|
|
40
45
|
setSubStep("dashboard-user");
|
|
41
46
|
};
|
|
@@ -48,7 +53,9 @@ export function SupabaseCredentialsStep({ onComplete, onBack, }) {
|
|
|
48
53
|
setSubStep("dashboard-pass");
|
|
49
54
|
};
|
|
50
55
|
const handleDashboardPassSubmit = () => {
|
|
51
|
-
|
|
56
|
+
// Empty means "use a generated secure value".
|
|
57
|
+
const effectivePass = dashboardPass.trim() || defaultDashboardPass;
|
|
58
|
+
if (effectivePass.length < 8) {
|
|
52
59
|
setError("Dashboard password must be at least 8 characters");
|
|
53
60
|
return;
|
|
54
61
|
}
|
|
@@ -56,21 +63,13 @@ export function SupabaseCredentialsStep({ onComplete, onBack, }) {
|
|
|
56
63
|
dispatch({
|
|
57
64
|
type: "SET_SUPABASE_SELF_HOSTED",
|
|
58
65
|
config: {
|
|
59
|
-
supabaseJwtSecret:
|
|
60
|
-
supabaseDbPassword: dbPassword,
|
|
66
|
+
supabaseJwtSecret: defaultJwtSecret,
|
|
67
|
+
supabaseDbPassword: dbPassword.trim() || defaultDbPass,
|
|
61
68
|
supabaseDashboardUser: dashboardUser,
|
|
62
|
-
supabaseDashboardPass:
|
|
69
|
+
supabaseDashboardPass: effectivePass,
|
|
63
70
|
},
|
|
64
71
|
});
|
|
65
72
|
onComplete();
|
|
66
73
|
};
|
|
67
|
-
|
|
68
|
-
if (field === "db") {
|
|
69
|
-
setDbPassword(generateSecureSecret(24));
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
setDashboardPass(generateSecureSecret(16));
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
return (_jsxs(BorderBox, { title: "Supabase Credentials", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: "gray", dimColor: true, children: "Configure credentials for your self-hosted Supabase instance" }), _jsx(Text, { color: "yellow", dimColor: true, children: "\u26A0 Save these credentials securely - you'll need them to access Supabase" })] }), subStep === "db-password" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Database Password:" }), _jsx(Text, { color: "gray", dimColor: true, children: "PostgreSQL database password" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default (press Enter to use): ", defaultDbPass] }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dbPassword, onChange: setDbPassword, onSubmit: handleDbPasswordSubmit, placeholder: "Database password (min 8 chars)", mask: "*" }) })] })), subStep === "dashboard-user" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Supabase Studio Username:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Username for accessing the Supabase dashboard" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dashboardUser, onChange: setDashboardUser, onSubmit: handleDashboardUserSubmit, placeholder: "supabase" }) }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { color: "gray", children: " Database password configured" })] }) })] })), subStep === "dashboard-pass" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Supabase Studio Password:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Password for accessing the Supabase dashboard" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default (press Enter to use): ", defaultDashboardPass] }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dashboardPass, onChange: setDashboardPass, onSubmit: handleDashboardPassSubmit, placeholder: "Dashboard password (min 8 chars)", mask: "*" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { color: "gray", children: " Database password configured" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Dashboard user: ", dashboardUser] })] })] })] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back \u2022 Enter to continue" }) })] }));
|
|
74
|
+
return (_jsxs(BorderBox, { title: "Supabase Credentials", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: "gray", dimColor: true, children: "Configure credentials for your self-hosted Supabase instance" }), _jsx(Text, { color: "yellow", dimColor: true, children: "\u26A0 Save these credentials securely - you'll need them to access Supabase" })] }), subStep === "db-password" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Database Password:" }), _jsx(Text, { color: "gray", dimColor: true, children: "PostgreSQL database password. Leave empty to generate a secure value." }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dbPassword, onChange: setDbPassword, onSubmit: handleDbPasswordSubmit, placeholder: "Leave empty to generate a secure value", mask: "*" }) })] })), subStep === "dashboard-user" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Supabase Studio Username:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Username for accessing the Supabase dashboard" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dashboardUser, onChange: setDashboardUser, onSubmit: handleDashboardUserSubmit, placeholder: "supabase" }) }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { color: "gray", children: " Database password configured" })] }) })] })), subStep === "dashboard-pass" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Supabase Studio Password:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Password for accessing the Supabase dashboard. Leave empty to generate a secure value." }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: dashboardPass, onChange: setDashboardPass, onSubmit: handleDashboardPassSubmit, placeholder: "Leave empty to generate a secure value", mask: "*" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { color: "gray", children: " Database password configured" })] }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Dashboard user: ", dashboardUser] })] })] })] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back \u2022 Enter to continue" }) })] }));
|
|
76
75
|
}
|
|
@@ -7,8 +7,9 @@ import { useWizard } from "../WizardContext.js";
|
|
|
7
7
|
import { BorderBox, useTheme } from "../../common/index.js";
|
|
8
8
|
import { Spinner } from "../../common/Spinner.js";
|
|
9
9
|
import { fetchAppVersions, formatDate } from "../../../lib/versions.js";
|
|
10
|
-
import { CHANGELOG_URL } from "../../../types/index.js";
|
|
10
|
+
import { CHANGELOG_URL, } from "../../../types/index.js";
|
|
11
11
|
import { formatVersionDisplay } from "../../../lib/dockerHub.js";
|
|
12
|
+
import { inferClusterCapabilities } from "../../../lib/kubernetes.js";
|
|
12
13
|
export function VersionStep({ onComplete, onBack }) {
|
|
13
14
|
const { state, dispatch } = useWizard();
|
|
14
15
|
const { colors } = useTheme();
|
|
@@ -32,10 +33,44 @@ export function VersionStep({ onComplete, onBack }) {
|
|
|
32
33
|
setSubStep("loading-versions");
|
|
33
34
|
setLoadError(null);
|
|
34
35
|
try {
|
|
35
|
-
|
|
36
|
+
// Scan the cluster for its capabilities (node architecture, storage
|
|
37
|
+
// class, ARM tolerations) and store them in wizard state, unless an
|
|
38
|
+
// earlier scan already populated them. The architecture is needed to
|
|
39
|
+
// fetch the matching image versions below.
|
|
40
|
+
let architecture;
|
|
41
|
+
if (state.nodeArchitecture === "amd64" ||
|
|
42
|
+
state.nodeArchitecture === "arm64") {
|
|
43
|
+
architecture = state.nodeArchitecture;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const capabilities = await inferClusterCapabilities();
|
|
47
|
+
if (capabilities) {
|
|
48
|
+
dispatch({
|
|
49
|
+
type: "SET_CLUSTER_CAPABILITIES",
|
|
50
|
+
nodeArchitecture: capabilities.nodeArchitecture,
|
|
51
|
+
arm64TolerationRequired: capabilities.arm64TolerationRequired,
|
|
52
|
+
storageClass: capabilities.storageClass,
|
|
53
|
+
storageProvisioner: capabilities.storageProvisioner,
|
|
54
|
+
schedulableNodeCount: capabilities.schedulableNodeCount,
|
|
55
|
+
totalCpuCores: capabilities.totalCpuCores,
|
|
56
|
+
totalMemoryGi: capabilities.totalMemoryGi,
|
|
57
|
+
eligibleCpuCores: capabilities.eligibleCpuCores,
|
|
58
|
+
eligibleMemoryGi: capabilities.eligibleMemoryGi,
|
|
59
|
+
totalPersistentStorageGi: capabilities.totalPersistentStorageGi ?? 0,
|
|
60
|
+
});
|
|
61
|
+
if (capabilities.nodeArchitecture === "amd64" ||
|
|
62
|
+
capabilities.nodeArchitecture === "arm64") {
|
|
63
|
+
architecture = capabilities.nodeArchitecture;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const appVersions = await fetchAppVersions(licenseKey, architecture);
|
|
36
68
|
setVersions(appVersions);
|
|
37
|
-
if (appVersions.length === 0) {
|
|
38
|
-
setLoadError(
|
|
69
|
+
if (appVersions.length === 0 && architecture) {
|
|
70
|
+
setLoadError(`No compatible Rulebricks version found for ${architecture} nodes.`);
|
|
71
|
+
}
|
|
72
|
+
else if (appVersions.length === 0) {
|
|
73
|
+
setLoadError("No Rulebricks versions found.");
|
|
39
74
|
}
|
|
40
75
|
setSubStep("version");
|
|
41
76
|
}
|
|
@@ -67,9 +102,8 @@ export function VersionStep({ onComplete, onBack }) {
|
|
|
67
102
|
};
|
|
68
103
|
const handleVersionSelect = (item) => {
|
|
69
104
|
dispatch({
|
|
70
|
-
type: "
|
|
71
|
-
|
|
72
|
-
hpsVersion: item.hpsVersion || item.value,
|
|
105
|
+
type: "SET_VERSION",
|
|
106
|
+
version: item.value,
|
|
73
107
|
});
|
|
74
108
|
onComplete();
|
|
75
109
|
};
|
|
@@ -82,32 +116,20 @@ export function VersionStep({ onComplete, onBack }) {
|
|
|
82
116
|
{
|
|
83
117
|
label: `✨ Latest (${formatVersionDisplay(latestVersion.version)})`,
|
|
84
118
|
value: latestVersion.version,
|
|
85
|
-
hpsVersion: latestVersion.hpsVersion || latestVersion.version,
|
|
86
119
|
releaseDate: latestVersion.releaseDate,
|
|
87
120
|
},
|
|
88
121
|
// Skip the first version since it's shown as "Latest"
|
|
89
122
|
...versions.slice(1, 15).map((v) => ({
|
|
90
123
|
label: formatVersionDisplay(v.version),
|
|
91
124
|
value: v.version,
|
|
92
|
-
hpsVersion: v.hpsVersion || v.version,
|
|
93
125
|
releaseDate: v.releaseDate,
|
|
94
126
|
})),
|
|
95
127
|
]
|
|
96
|
-
: [
|
|
97
|
-
|
|
98
|
-
label: "✨ Latest (recommended)",
|
|
99
|
-
value: "latest",
|
|
100
|
-
hpsVersion: "latest",
|
|
101
|
-
releaseDate: null,
|
|
102
|
-
},
|
|
103
|
-
];
|
|
104
|
-
return (_jsxs(BorderBox, { title: "License & Version", children: [subStep === "license" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Enter your Rulebricks license key:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Get a license at https://rulebricks.com/pricing" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: licenseKey, onChange: setLicenseKey, onSubmit: handleLicenseSubmit, placeholder: "vd67aveCHr1G..." }) })] })), subStep === "loading-versions" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Spinner, { label: "Authenticating and fetching versions..." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Connecting to Docker Hub..." }) })] })), subStep === "version" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select app version to deploy:" }), loadError && (_jsxs(Text, { color: colors.warning, dimColor: true, children: ["\u26A0 ", loadError] })), _jsx(Box, { marginTop: 1, height: 12, flexDirection: "column", overflowY: "hidden", children: _jsx(SelectInput, { items: versionItems, onSelect: handleVersionSelect, limit: 10, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => {
|
|
128
|
+
: [];
|
|
129
|
+
return (_jsxs(BorderBox, { title: "License & Version", children: [subStep === "license" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Enter your Rulebricks license key:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Get a license at https://rulebricks.com/pricing" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: licenseKey, onChange: setLicenseKey, onSubmit: handleLicenseSubmit, placeholder: "vd67aveCHr1G..." }) })] })), subStep === "loading-versions" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Spinner, { label: "Authenticating and fetching versions..." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Connecting to Docker Hub..." }) })] })), subStep === "version" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select Rulebricks version to deploy:" }), loadError && (_jsxs(Text, { color: colors.warning, dimColor: true, children: ["\u26A0 ", loadError] })), _jsx(Box, { marginTop: 1, height: 12, flexDirection: "column", overflowY: "hidden", children: versionItems.length > 0 ? (_jsx(SelectInput, { items: versionItems, onSelect: handleVersionSelect, limit: 10, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => {
|
|
105
130
|
// Find the version item from the list using the label
|
|
106
131
|
const versionItem = versionItems.find((v) => v.label === label);
|
|
107
|
-
const hasHps = versionItem?.value &&
|
|
108
|
-
versionItem?.hpsVersion &&
|
|
109
|
-
versionItem.value !== "latest";
|
|
110
132
|
const hasDate = versionItem?.releaseDate;
|
|
111
|
-
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] }), hasDate && (_jsxs(Text, { color: colors.muted, children: [" ", "(", formatDate(versionItem.releaseDate), ")"] }))
|
|
112
|
-
} }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" ", "License key: ", licenseKey.substring(0, 8), "..."] })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["View changelog: ", CHANGELOG_URL] }) })] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.error, children: ["\u2717 ", error] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back \u2022 Enter to continue" }) })] }));
|
|
133
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] }), hasDate && (_jsxs(Text, { color: colors.muted, children: [" ", "(", formatDate(versionItem.releaseDate), ")"] }))] }));
|
|
134
|
+
} })) : (_jsx(Text, { color: colors.warning, children: "No compatible image versions are available for this cluster." })) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" ", "License key: ", licenseKey.substring(0, 8), "..."] })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["View changelog: ", CHANGELOG_URL] }) })] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.error, children: ["\u2717 ", error] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back \u2022 Enter to continue" }) })] }));
|
|
113
135
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { DeploymentModeStep } from './DeploymentModeStep.js';
|
|
2
1
|
export { CloudProviderStep } from './CloudProviderStep.js';
|
|
3
2
|
export { DomainStep } from './DomainStep.js';
|
|
4
3
|
export { SMTPStep } from './SMTPStep.js';
|
|
5
4
|
export { DatabaseStep } from './DatabaseStep.js';
|
|
6
5
|
export { SupabaseCredentialsStep } from './SupabaseCredentialsStep.js';
|
|
7
|
-
export { TierStep } from './TierStep.js';
|
|
8
6
|
export { FeaturesStep } from './FeaturesStep.js';
|
|
7
|
+
export { StorageStep } from './StorageStep.js';
|
|
8
|
+
export { ObservabilityStep } from './ObservabilityStep.js';
|
|
9
|
+
export { ExternalServicesStep } from './ExternalServicesStep.js';
|
|
9
10
|
export { FeatureConfigStep } from './FeatureConfigStep.js';
|
|
10
11
|
export { VersionStep } from './VersionStep.js';
|
|
11
|
-
export { CredentialsStep } from './CredentialsStep.js';
|
|
12
12
|
export { ReviewStep } from './ReviewStep.js';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { DeploymentModeStep } from './DeploymentModeStep.js';
|
|
2
1
|
export { CloudProviderStep } from './CloudProviderStep.js';
|
|
3
2
|
export { DomainStep } from './DomainStep.js';
|
|
4
3
|
export { SMTPStep } from './SMTPStep.js';
|
|
5
4
|
export { DatabaseStep } from './DatabaseStep.js';
|
|
6
5
|
export { SupabaseCredentialsStep } from './SupabaseCredentialsStep.js';
|
|
7
|
-
export { TierStep } from './TierStep.js';
|
|
8
6
|
export { FeaturesStep } from './FeaturesStep.js';
|
|
7
|
+
export { StorageStep } from './StorageStep.js';
|
|
8
|
+
export { ObservabilityStep } from './ObservabilityStep.js';
|
|
9
|
+
export { ExternalServicesStep } from './ExternalServicesStep.js';
|
|
9
10
|
export { FeatureConfigStep } from './FeatureConfigStep.js';
|
|
10
11
|
export { VersionStep } from './VersionStep.js';
|
|
11
|
-
export { CredentialsStep } from './CredentialsStep.js';
|
|
12
12
|
export { ReviewStep } from './ReviewStep.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { useInput } from "ink";
|
|
3
|
+
interface CommandApprovalContextValue {
|
|
4
|
+
pending: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface CommandApprovalProviderProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare function CommandApprovalProvider({ children, }: CommandApprovalProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function useCommandApproval(): CommandApprovalContextValue;
|
|
11
|
+
export declare function useGatedInput(inputHandler: Parameters<typeof useInput>[0], options?: Parameters<typeof useInput>[1]): void;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
3
|
+
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import SelectInput from "ink-select-input";
|
|
5
|
+
import { useTheme } from "../../lib/theme.js";
|
|
6
|
+
import { getCurrentCommandApproval, respondToCommandApproval, setCommandApprovalInteractive, subscribeCommandApprovals, } from "../../lib/commandApproval.js";
|
|
7
|
+
const CommandApprovalContext = createContext({
|
|
8
|
+
pending: false,
|
|
9
|
+
});
|
|
10
|
+
export function CommandApprovalProvider({ children, }) {
|
|
11
|
+
const [pending, setPending] = useState(getCurrentCommandApproval());
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setCommandApprovalInteractive(true);
|
|
14
|
+
const unsubscribe = subscribeCommandApprovals(() => {
|
|
15
|
+
setPending(getCurrentCommandApproval());
|
|
16
|
+
});
|
|
17
|
+
setPending(getCurrentCommandApproval());
|
|
18
|
+
return () => {
|
|
19
|
+
unsubscribe();
|
|
20
|
+
setCommandApprovalInteractive(false);
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
return (_jsx(CommandApprovalContext.Provider, { value: { pending: pending !== null }, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { display: pending ? "none" : "flex", flexDirection: "column", children: children }), pending && _jsx(CommandApprovalScreen, { request: pending })] }) }));
|
|
24
|
+
}
|
|
25
|
+
export function useCommandApproval() {
|
|
26
|
+
return useContext(CommandApprovalContext);
|
|
27
|
+
}
|
|
28
|
+
export function useGatedInput(inputHandler, options = {}) {
|
|
29
|
+
const { pending } = useCommandApproval();
|
|
30
|
+
useInput(inputHandler, {
|
|
31
|
+
...options,
|
|
32
|
+
isActive: (options?.isActive ?? true) && !pending,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function CommandApprovalScreen({ request, }) {
|
|
36
|
+
const { colors } = useTheme();
|
|
37
|
+
const tagColor = request.mutating ? colors.warning : colors.success;
|
|
38
|
+
const tag = request.mutating ? "modifies cloud resources" : "read-only";
|
|
39
|
+
const provider = request.provider ? request.provider.toUpperCase() : "CLOUD CLI";
|
|
40
|
+
const approve = (scope) => {
|
|
41
|
+
respondToCommandApproval(request.id, "approve", scope);
|
|
42
|
+
};
|
|
43
|
+
const deny = () => {
|
|
44
|
+
respondToCommandApproval(request.id, "deny", "once");
|
|
45
|
+
};
|
|
46
|
+
useInput((input) => {
|
|
47
|
+
if (input === "y" || input === "Y") {
|
|
48
|
+
approve("once");
|
|
49
|
+
}
|
|
50
|
+
else if (input === "a" || input === "A") {
|
|
51
|
+
approve("all-like");
|
|
52
|
+
}
|
|
53
|
+
else if (input === "n" || input === "N") {
|
|
54
|
+
deny();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const items = [
|
|
58
|
+
{ label: "Approve", value: "once" },
|
|
59
|
+
{ label: `Approve all "${request.intent}" commands`, value: "all-like" },
|
|
60
|
+
{ label: "Deny", value: "deny" },
|
|
61
|
+
];
|
|
62
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [_jsxs(Text, { color: colors.accent, children: ["\u250C\u2500 Cloud CLI Approval ", "─".repeat(51), "\u2510"] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, width: 76, children: [_jsx(Box, { children: _jsx(Text, { color: colors.accent, bold: true, children: request.intent }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.muted, children: provider }), _jsx(Text, { color: colors.muted, children: " \u2022 " }), _jsx(Text, { color: tagColor, children: tag })] }), request.description && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, children: request.description }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "The Rulebricks CLI wants to run:" }), _jsx(Box, { flexDirection: "column", marginTop: 1, paddingX: 1, children: wrapCommand(request.command).map((line, index) => (_jsxs(Text, { color: colors.accentBright, children: [index === 0 ? "$ " : " ", line] }, index))) })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(SelectInput, { items: items, onSelect: (item) => {
|
|
63
|
+
if (item.value === "deny") {
|
|
64
|
+
deny();
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
approve(item.value);
|
|
68
|
+
}
|
|
69
|
+
}, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "> " : " ", label] })) }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, dimColor: true, children: "y approve \u2022 a approve all like this \u2022 n deny" }), _jsx(Text, { color: colors.muted, dimColor: true, children: "You can run this yourself in another terminal, then choose Deny." })] })] }), _jsxs(Text, { color: colors.accent, children: ["\u2514", "─".repeat(74), "\u2518"] })] }));
|
|
70
|
+
}
|
|
71
|
+
function wrapCommand(command, width = 68) {
|
|
72
|
+
const words = command.split(" ");
|
|
73
|
+
const lines = [];
|
|
74
|
+
let current = "";
|
|
75
|
+
for (const word of words) {
|
|
76
|
+
if (!current) {
|
|
77
|
+
current = word;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (`${current} ${word}`.length > width) {
|
|
81
|
+
lines.push(current);
|
|
82
|
+
current = word;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
current = `${current} ${word}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (current)
|
|
89
|
+
lines.push(current);
|
|
90
|
+
return lines.length > 0 ? lines : [command];
|
|
91
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface DeploymentPickerProps {
|
|
2
|
+
deployments: string[];
|
|
3
|
+
/** Verb phrase shown in the prompt, e.g. "deploy" or "view logs for". */
|
|
4
|
+
action?: string;
|
|
5
|
+
onSelect: (name: string) => void;
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Interactive deployment selector shown when a command is run without a
|
|
10
|
+
* deployment name and more than one deployment exists. Kept standalone (no
|
|
11
|
+
* ThemeProvider) so it can render before a command's own Ink tree mounts.
|
|
12
|
+
*/
|
|
13
|
+
export declare function DeploymentPicker({ deployments, action, onSelect, onCancel, }: DeploymentPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
4
|
+
/**
|
|
5
|
+
* Interactive deployment selector shown when a command is run without a
|
|
6
|
+
* deployment name and more than one deployment exists. Kept standalone (no
|
|
7
|
+
* ThemeProvider) so it can render before a command's own Ink tree mounts.
|
|
8
|
+
*/
|
|
9
|
+
export function DeploymentPicker({ deployments, action, onSelect, onCancel, }) {
|
|
10
|
+
useInput((input, key) => {
|
|
11
|
+
if (key.escape) {
|
|
12
|
+
onCancel();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { bold: true, children: ["Select a deployment", action ? ` to ${action}` : "", ":"] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(SelectInput, { items: deployments.map((d) => ({ label: d, value: d })), onSelect: (item) => onSelect(item.value), limit: 10, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? "cyan" : undefined, children: [isSelected ? "❯ " : " ", label] })) }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "\u2191/\u2193 to navigate \u2022 Enter to select \u2022 Esc to cancel" }) })] }));
|
|
16
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { BorderBox, Section, ProgressBar } from "./Box.js";
|
|
2
2
|
export { Spinner, StatusLine } from "./Spinner.js";
|
|
3
3
|
export { AppShell, ScreenContainer, ProgressHeader } from "./AppShell.js";
|
|
4
|
+
export { DeploymentPicker } from "./DeploymentPicker.js";
|
|
4
5
|
export { Logo, LOGO_LINES } from "./Logo.js";
|
|
6
|
+
export { CommandApprovalProvider, useCommandApproval, useGatedInput, } from "./CommandApproval.js";
|
|
5
7
|
export { ThemeProvider, useTheme, THEMES } from "../../lib/theme.js";
|
|
6
8
|
export type { CommandTheme, ThemeColors } from "../../lib/theme.js";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { BorderBox, Section, ProgressBar } from "./Box.js";
|
|
2
2
|
export { Spinner, StatusLine } from "./Spinner.js";
|
|
3
3
|
export { AppShell, ScreenContainer, ProgressHeader } from "./AppShell.js";
|
|
4
|
+
export { DeploymentPicker } from "./DeploymentPicker.js";
|
|
4
5
|
export { Logo, LOGO_LINES } from "./Logo.js";
|
|
6
|
+
export { CommandApprovalProvider, useCommandApproval, useGatedInput, } from "./CommandApproval.js";
|
|
5
7
|
export { ThemeProvider, useTheme, THEMES } from "../../lib/theme.js";
|