gitops-ai 1.0.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.
Files changed (47) hide show
  1. package/README.md +155 -0
  2. package/dist/commands/bootstrap.d.ts +2 -0
  3. package/dist/commands/bootstrap.js +721 -0
  4. package/dist/commands/bootstrap.js.map +1 -0
  5. package/dist/commands/sops.d.ts +1 -0
  6. package/dist/commands/sops.js +300 -0
  7. package/dist/commands/sops.js.map +1 -0
  8. package/dist/core/bootstrap-runner.d.ts +13 -0
  9. package/dist/core/bootstrap-runner.js +194 -0
  10. package/dist/core/bootstrap-runner.js.map +1 -0
  11. package/dist/core/dependencies.d.ts +3 -0
  12. package/dist/core/dependencies.js +134 -0
  13. package/dist/core/dependencies.js.map +1 -0
  14. package/dist/core/encryption.d.ts +25 -0
  15. package/dist/core/encryption.js +209 -0
  16. package/dist/core/encryption.js.map +1 -0
  17. package/dist/core/flux.d.ts +6 -0
  18. package/dist/core/flux.js +60 -0
  19. package/dist/core/flux.js.map +1 -0
  20. package/dist/core/gitlab.d.ts +10 -0
  21. package/dist/core/gitlab.js +65 -0
  22. package/dist/core/gitlab.js.map +1 -0
  23. package/dist/core/kubernetes.d.ts +10 -0
  24. package/dist/core/kubernetes.js +81 -0
  25. package/dist/core/kubernetes.js.map +1 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +49 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/schemas.d.ts +50 -0
  30. package/dist/schemas.js +56 -0
  31. package/dist/schemas.js.map +1 -0
  32. package/dist/utils/config.d.ts +3 -0
  33. package/dist/utils/config.js +26 -0
  34. package/dist/utils/config.js.map +1 -0
  35. package/dist/utils/log.d.ts +31 -0
  36. package/dist/utils/log.js +96 -0
  37. package/dist/utils/log.js.map +1 -0
  38. package/dist/utils/platform.d.ts +7 -0
  39. package/dist/utils/platform.js +21 -0
  40. package/dist/utils/platform.js.map +1 -0
  41. package/dist/utils/shell.d.ts +41 -0
  42. package/dist/utils/shell.js +86 -0
  43. package/dist/utils/shell.js.map +1 -0
  44. package/dist/utils/wizard.d.ts +16 -0
  45. package/dist/utils/wizard.js +117 -0
  46. package/dist/utils/wizard.js.map +1 -0
  47. package/package.json +32 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kubernetes.js","sourceRoot":"","sources":["../../src/core/kubernetes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAa,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,kCAAkC,CAAC,CAAC;IAClE,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,8BAA8B,CAAC,CAAC;IAC5D,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,OAAO,CAAC,gBAAgB,WAAW,kBAAkB,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG;QACV,oBAAoB;QACpB,WAAW;QACX,yBAAyB,kBAAkB,QAAQ;QACnD,wCAAwC;QACxC,6BAA6B;QAC7B,+BAA+B;QAC/B,QAAQ;KACT,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChB,MAAM,WAAW,CAAC,yBAAyB,WAAW,GAAG,EAAE,GAAG,EAAE,CAC9D,SAAS,CAAC,GAAG,CAAC,CACf,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAChD,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,GAAG,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,WAAW,CAAC,gBAAgB,EAAE,GAAG,EAAE,CACvC,SAAS,CACP,wDAAwD,kBAAkB,qFAAqF,CAChK,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,SAAS,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5D,IAAI,OAAO,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;QACxB,MAAM,cAAc,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,cAAc,WAAW,EAAE,CAAC;QACtE,MAAM,UAAU,GAAG,IAAI,CAAC,sBAAsB,WAAW,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,cAAc,CAAC;QACxC,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,sDAAsD,CAAC,CAAC;IAC7D,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAC1D,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACrC,MAAM,cAAc,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,cAAc,CAAC;IACxC,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,WAAW,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,MAAM,SAAS,CACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,MAAM,SAAS,CACb,4BAA4B,IAAI,gDAAgD,CACjF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAY,EACZ,SAAiB,EACjB,IAA4B;IAE5B,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC;SAC7C,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,GAAG,iCAAiC,IAAI,gBAAgB,SAAS,IAAI,QAAQ,EAAE,CAAC;IACzF,GAAG,CAAC,MAAM,CAAC,iCAAiC,IAAI,gBAAgB,SAAS,EAAE,CAAC,CAAC;IAC7E,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAiB;IAC1D,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAC3B,sBAAsB,IAAI,OAAO,SAAS,cAAc,CACzD,CAAC;IACF,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAY,EACZ,SAAiB;IAEjB,MAAM,SAAS,CAAC,yBAAyB,IAAI,OAAO,SAAS,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAY,EACZ,SAAiB,EACjB,GAAW,EACX,QAAgB;IAEhB,MAAM,GAAG,GAAG,iCAAiC,IAAI,gBAAgB,SAAS,gBAAgB,GAAG,KAAK,QAAQ,GAAG,CAAC;IAC9G,GAAG,CAAC,MAAM,CAAC,iCAAiC,IAAI,gBAAgB,SAAS,gBAAgB,GAAG,EAAE,CAAC,CAAC;IAChG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { bootstrap, openclawPair } from "./commands/bootstrap.js";
4
+ import { sops } from "./commands/sops.js";
5
+ import pc from "picocolors";
6
+ const program = new Command();
7
+ program
8
+ .name("flux-cli")
9
+ .description("Flux GitOps cluster bootstrap CLI with interactive TUI")
10
+ .version("1.0.0");
11
+ program
12
+ .command("bootstrap")
13
+ .alias("install")
14
+ .description("Bootstrap a Kubernetes cluster with Flux GitOps (create new repo or use existing)")
15
+ .action(async () => {
16
+ try {
17
+ await bootstrap();
18
+ }
19
+ catch (err) {
20
+ console.error(pc.red(`\n Error: ${err.message}`));
21
+ process.exit(1);
22
+ }
23
+ });
24
+ program
25
+ .command("sops [subcommand] [file]")
26
+ .description("SOPS secret encryption management (init, encrypt, decrypt, edit, status, import, rotate)")
27
+ .action(async (subcommand, file) => {
28
+ try {
29
+ await sops(subcommand, file);
30
+ }
31
+ catch (err) {
32
+ console.error(pc.red(`\n Error: ${err.message}`));
33
+ process.exit(1);
34
+ }
35
+ });
36
+ program
37
+ .command("openclaw-pair")
38
+ .description("Pair an OpenClaw device with the cluster")
39
+ .action(async () => {
40
+ try {
41
+ await openclawPair();
42
+ }
43
+ catch (err) {
44
+ console.error(pc.red(`\n Error: ${err.message}`));
45
+ process.exit(1);
46
+ }
47
+ });
48
+ program.parse();
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,KAAK,CAAC,SAAS,CAAC;KAChB,WAAW,CAAC,mFAAmF,CAAC;KAChG,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,cAAe,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,0BAA0B,CAAC;KACnC,WAAW,CAAC,0FAA0F,CAAC;KACvG,MAAM,CAAC,KAAK,EAAE,UAAmB,EAAE,IAAa,EAAE,EAAE;IACnD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,cAAe,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,cAAe,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ export declare const ClusterConfigSchema: z.ZodObject<{
3
+ clusterName: z.ZodString;
4
+ clusterDomain: z.ZodString;
5
+ clusterPublicIp: z.ZodString;
6
+ letsencryptEmail: z.ZodOptional<z.ZodString>;
7
+ ingressAllowedIps: z.ZodString;
8
+ }, z.core.$strip>;
9
+ export declare const GitLabConfigSchema: z.ZodObject<{
10
+ gitlabPat: z.ZodString;
11
+ repoName: z.ZodString;
12
+ repoOwner: z.ZodString;
13
+ repoBranch: z.ZodString;
14
+ }, z.core.$strip>;
15
+ export declare const SecretsConfigSchema: z.ZodObject<{
16
+ cloudflareApiToken: z.ZodOptional<z.ZodString>;
17
+ openaiApiKey: z.ZodOptional<z.ZodString>;
18
+ openclawGatewayToken: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$strip>;
20
+ export type ClusterConfig = z.infer<typeof ClusterConfigSchema>;
21
+ export type GitLabConfig = z.infer<typeof GitLabConfigSchema>;
22
+ export type SecretsConfig = z.infer<typeof SecretsConfigSchema>;
23
+ export type BootstrapConfig = ClusterConfig & GitLabConfig & SecretsConfig & {
24
+ selectedComponents: string[];
25
+ };
26
+ export interface SopsConfig {
27
+ keyDir: string;
28
+ keyFile: string;
29
+ namespace: string;
30
+ secretName: string;
31
+ configFile: string;
32
+ backupDir: string;
33
+ }
34
+ export interface ComponentDef {
35
+ id: string;
36
+ label: string;
37
+ hint: string;
38
+ required: boolean;
39
+ secrets?: string[];
40
+ subdomain?: string;
41
+ }
42
+ export declare const COMPONENTS: ComponentDef[];
43
+ export declare const REQUIRED_COMPONENT_IDS: string[];
44
+ export declare const DNS_TLS_COMPONENT_IDS: string[];
45
+ export declare const OPTIONAL_COMPONENTS: ComponentDef[];
46
+ export declare const KUBERNETES_VERSION = "1.35.1";
47
+ export declare const SOURCE_GITLAB_HOST = "gitlab.com";
48
+ export declare const SOURCE_PROJECT_PATH = "everythings-gonna-be-alright/fluxcd_ai_template";
49
+ export declare const INSTALL_PLAN_PATH = "/tmp/installplan.json";
50
+ export declare function defaultSopsConfig(repoRoot: string): SopsConfig;
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ // ---------------------------------------------------------------------------
3
+ // Validation schemas
4
+ // ---------------------------------------------------------------------------
5
+ export const ClusterConfigSchema = z.object({
6
+ clusterName: z.string().min(1),
7
+ clusterDomain: z
8
+ .string()
9
+ .min(1)
10
+ .refine((v) => v.includes("."), "Must be a valid domain"),
11
+ clusterPublicIp: z.string().min(1),
12
+ letsencryptEmail: z.string().optional(),
13
+ ingressAllowedIps: z.string().min(1),
14
+ });
15
+ export const GitLabConfigSchema = z.object({
16
+ gitlabPat: z.string().min(1, "GitLab PAT is required"),
17
+ repoName: z.string().min(1),
18
+ repoOwner: z.string().min(1),
19
+ repoBranch: z.string().min(1),
20
+ });
21
+ export const SecretsConfigSchema = z.object({
22
+ cloudflareApiToken: z.string().optional(),
23
+ openaiApiKey: z.string().optional(),
24
+ openclawGatewayToken: z.string().optional(),
25
+ });
26
+ export const COMPONENTS = [
27
+ { id: "helm-repos", label: "Helm Repositories", hint: "Shared Helm chart repos", required: true },
28
+ { id: "ingress-nginx-external", label: "Ingress Nginx (external)", hint: "External HTTP/HTTPS ingress controller", required: true },
29
+ { id: "cert-manager", label: "Cert Manager", hint: "Automatic TLS certificates via Let's Encrypt", required: false, secrets: ["secret-cloudflare.yaml"] },
30
+ { id: "external-dns", label: "External DNS", hint: "Automatic DNS records in Cloudflare", required: false, secrets: ["secret-cloudflare.yaml"] },
31
+ { id: "prometheus-operator-crds", label: "Prometheus CRDs", hint: "Monitoring custom resource definitions", required: true },
32
+ { id: "flux-web", label: "Flux Web UI", hint: "Web dashboard for Flux status", required: false, subdomain: "flux" },
33
+ { id: "openclaw", label: "OpenClaw", hint: "AI assistant gateway (requires OpenAI key)", required: false, secrets: ["secret-openclaw-envs.yaml"], subdomain: "openclaw" },
34
+ ];
35
+ export const REQUIRED_COMPONENT_IDS = COMPONENTS.filter((c) => c.required).map((c) => c.id);
36
+ export const DNS_TLS_COMPONENT_IDS = ["cert-manager", "external-dns"];
37
+ export const OPTIONAL_COMPONENTS = COMPONENTS.filter((c) => !c.required && !DNS_TLS_COMPONENT_IDS.includes(c.id));
38
+ // ---------------------------------------------------------------------------
39
+ // Constants
40
+ // ---------------------------------------------------------------------------
41
+ export const KUBERNETES_VERSION = "1.35.1";
42
+ export const SOURCE_GITLAB_HOST = "gitlab.com";
43
+ export const SOURCE_PROJECT_PATH = "everythings-gonna-be-alright/fluxcd_ai_template";
44
+ export const INSTALL_PLAN_PATH = "/tmp/installplan.json";
45
+ export function defaultSopsConfig(repoRoot) {
46
+ const keyDir = process.env.SOPS_AGE_KEY_DIR ?? `${process.env.HOME}/.sops`;
47
+ return {
48
+ keyDir,
49
+ keyFile: `${keyDir}/age.agekey`,
50
+ namespace: process.env.SOPS_NAMESPACE ?? "flux-system",
51
+ secretName: process.env.SOPS_SECRET_NAME ?? "sops-age",
52
+ configFile: `${repoRoot}/.sops.yaml`,
53
+ backupDir: `${keyDir}/backups`,
54
+ };
55
+ }
56
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,wBAAwB,CAAC;IAC3D,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;IACtD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5C,CAAC,CAAC;AAqCH,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,yBAAyB,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjG,EAAE,EAAE,EAAE,wBAAwB,EAAE,KAAK,EAAE,0BAA0B,EAAE,IAAI,EAAE,wCAAwC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACnI,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,8CAA8C,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,wBAAwB,CAAC,EAAE;IACzJ,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,qCAAqC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,wBAAwB,CAAC,EAAE;IAChJ,EAAE,EAAE,EAAE,0BAA0B,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,wCAAwC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC5H,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,+BAA+B,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE;IACnH,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,4CAA4C,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,2BAA2B,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE;CAC1K,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC5F,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC,MAAM,CAClD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAC5D,CAAC;AAEF,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAC3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAC9B,iDAAiD,CAAC;AACpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAEzD,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC;IAC3E,OAAO;QACL,MAAM;QACN,OAAO,EAAE,GAAG,MAAM,aAAa;QAC/B,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,aAAa;QACtD,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,UAAU;QACtD,UAAU,EAAE,GAAG,QAAQ,aAAa;QACpC,SAAS,EAAE,GAAG,MAAM,UAAU;KAC/B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function saveInstallPlan(config: Record<string, string>): void;
2
+ export declare function loadInstallPlan(): Record<string, string> | null;
3
+ export declare function clearInstallPlan(): void;
@@ -0,0 +1,26 @@
1
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, } from "node:fs";
2
+ import { INSTALL_PLAN_PATH } from "../schemas.js";
3
+ export function saveInstallPlan(config) {
4
+ writeFileSync(INSTALL_PLAN_PATH, JSON.stringify(config, null, 2), {
5
+ mode: 0o600,
6
+ });
7
+ }
8
+ export function loadInstallPlan() {
9
+ if (!existsSync(INSTALL_PLAN_PATH))
10
+ return null;
11
+ try {
12
+ return JSON.parse(readFileSync(INSTALL_PLAN_PATH, "utf-8"));
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ export function clearInstallPlan() {
19
+ try {
20
+ unlinkSync(INSTALL_PLAN_PATH);
21
+ }
22
+ catch {
23
+ /* already gone */
24
+ }
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,UAAU,eAAe,CAAC,MAA8B;IAC5D,aAAa,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAChE,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ export declare const log: {
4
+ step: (msg: string) => void;
5
+ success: (msg: string) => void;
6
+ warn: (msg: string) => void;
7
+ error: (msg: string) => void;
8
+ info: (msg: string) => void;
9
+ message: (msg: string) => void;
10
+ detail: (msg: string) => void;
11
+ };
12
+ export declare function header(title: string, subtitle?: string): void;
13
+ export declare function summary(title: string, entries: Record<string, string>): void;
14
+ export declare function nextSteps(steps: string[]): void;
15
+ export declare function finish(msg: string): void;
16
+ export declare function handleCancel(): never;
17
+ /**
18
+ * Format an error for display — extracts stderr, stdout, and message.
19
+ */
20
+ export declare function formatError(err: unknown): string;
21
+ /**
22
+ * Wrap an async task with a @clack/prompts spinner.
23
+ * Shows command output on success (if string) and error details on failure.
24
+ */
25
+ export declare function withSpinner<T>(message: string, fn: () => Promise<T>, doneMessage?: string): Promise<T>;
26
+ /**
27
+ * Run a step with error handling — logs the error and exits.
28
+ * Use for steps that should abort the bootstrap on failure.
29
+ */
30
+ export declare function runStep<T>(title: string, fn: () => Promise<T>): Promise<T>;
31
+ export { pc, p };
@@ -0,0 +1,96 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ export const log = {
4
+ step: (msg) => p.log.step(pc.cyan(msg)),
5
+ success: (msg) => p.log.success(pc.green(msg)),
6
+ warn: (msg) => p.log.warning(pc.yellow(msg)),
7
+ error: (msg) => p.log.error(pc.red(msg)),
8
+ info: (msg) => p.log.info(msg),
9
+ message: (msg) => p.log.message(msg),
10
+ detail: (msg) => p.log.message(pc.dim(msg)),
11
+ };
12
+ export function header(title, subtitle) {
13
+ console.log();
14
+ p.intro(pc.bgCyan(pc.black(` ${title} `)));
15
+ if (subtitle) {
16
+ p.log.info(pc.dim(subtitle));
17
+ }
18
+ }
19
+ export function summary(title, entries) {
20
+ const lines = Object.entries(entries)
21
+ .map(([key, value]) => `${pc.bold(key + ":")} ${pc.cyan(value)}`)
22
+ .join("\n");
23
+ p.note(lines, title);
24
+ }
25
+ export function nextSteps(steps) {
26
+ const lines = steps
27
+ .map((s, i) => `${pc.bold(`${i + 1}.`)} ${s}`)
28
+ .join("\n");
29
+ p.note(lines, "Next Steps");
30
+ }
31
+ export function finish(msg) {
32
+ p.outro(pc.green(msg));
33
+ }
34
+ export function handleCancel() {
35
+ p.cancel("Operation cancelled.");
36
+ return process.exit(0);
37
+ }
38
+ /**
39
+ * Format an error for display — extracts stderr, stdout, and message.
40
+ */
41
+ export function formatError(err) {
42
+ const e = err;
43
+ const parts = [];
44
+ if (e.stderr)
45
+ parts.push(e.stderr.trim());
46
+ else if (e.message)
47
+ parts.push(e.message);
48
+ if (e.stdout)
49
+ parts.push(pc.dim(e.stdout.trim()));
50
+ return parts.join("\n");
51
+ }
52
+ /**
53
+ * Wrap an async task with a @clack/prompts spinner.
54
+ * Shows command output on success (if string) and error details on failure.
55
+ */
56
+ export async function withSpinner(message, fn, doneMessage) {
57
+ const s = p.spinner();
58
+ s.start(message);
59
+ try {
60
+ const result = await fn();
61
+ s.stop(doneMessage ?? pc.green(message));
62
+ if (typeof result === "string" && result.trim()) {
63
+ log.detail(result.trim());
64
+ }
65
+ return result;
66
+ }
67
+ catch (err) {
68
+ s.stop(pc.red(`Failed: ${message}`));
69
+ const details = formatError(err);
70
+ if (details)
71
+ log.error(details);
72
+ throw err;
73
+ }
74
+ }
75
+ /**
76
+ * Run a step with error handling — logs the error and exits.
77
+ * Use for steps that should abort the bootstrap on failure.
78
+ */
79
+ export async function runStep(title, fn) {
80
+ log.step(title);
81
+ try {
82
+ const result = await fn();
83
+ if (typeof result === "string" && result.trim()) {
84
+ log.detail(result.trim());
85
+ }
86
+ return result;
87
+ }
88
+ catch (err) {
89
+ const details = formatError(err);
90
+ if (details)
91
+ log.error(details);
92
+ return process.exit(1);
93
+ }
94
+ }
95
+ export { pc, p };
96
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type OS = "darwin" | "linux";
2
+ export type Arch = "amd64" | "arm64";
3
+ export declare function getOS(): OS;
4
+ export declare function getArch(): Arch;
5
+ export declare const isMacOS: () => boolean;
6
+ export declare const isLinux: () => boolean;
7
+ export declare const isCI: () => boolean;
@@ -0,0 +1,21 @@
1
+ import os from "node:os";
2
+ export function getOS() {
3
+ const p = os.platform();
4
+ if (p === "darwin")
5
+ return "darwin";
6
+ if (p === "linux")
7
+ return "linux";
8
+ throw new Error(`Unsupported platform: ${p}`);
9
+ }
10
+ export function getArch() {
11
+ const a = os.arch();
12
+ if (a === "x64")
13
+ return "amd64";
14
+ if (a === "arm64")
15
+ return "arm64";
16
+ throw new Error(`Unsupported architecture: ${a}`);
17
+ }
18
+ export const isMacOS = () => getOS() === "darwin";
19
+ export const isLinux = () => getOS() === "linux";
20
+ export const isCI = () => process.env.CI === "true";
21
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAKzB,MAAM,UAAU,KAAK;IACnB,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACpC,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACpB,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IAChC,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE,CAAC,KAAK,EAAE,KAAK,QAAQ,CAAC;AAC3D,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE,CAAC,KAAK,EAAE,KAAK,OAAO,CAAC;AAC1D,MAAM,CAAC,MAAM,IAAI,GAAG,GAAY,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type StdioOptions } from "node:child_process";
2
+ export interface ExecResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ exitCode: number;
6
+ }
7
+ /**
8
+ * Run a command synchronously, returning trimmed stdout.
9
+ * Throws on non-zero exit.
10
+ */
11
+ export declare function exec(command: string, opts?: {
12
+ cwd?: string;
13
+ env?: Record<string, string>;
14
+ }): string;
15
+ /**
16
+ * Run a command synchronously, never throws.
17
+ */
18
+ export declare function execSafe(command: string, opts?: {
19
+ cwd?: string;
20
+ }): ExecResult;
21
+ /**
22
+ * Run a command asynchronously (for use with spinners).
23
+ */
24
+ export declare function execAsync(command: string, opts?: {
25
+ cwd?: string;
26
+ }): Promise<string>;
27
+ /**
28
+ * Run a command with output streamed to the terminal (inherit stdio).
29
+ */
30
+ export declare function streamExec(command: string, opts?: {
31
+ cwd?: string;
32
+ stdio?: StdioOptions;
33
+ }): void;
34
+ /**
35
+ * Run a command with spawnSync for finer arg control.
36
+ */
37
+ export declare function run(cmd: string, args: string[], opts?: {
38
+ cwd?: string;
39
+ stdio?: StdioOptions;
40
+ }): ExecResult;
41
+ export declare function commandExists(name: string): boolean;
@@ -0,0 +1,86 @@
1
+ import { execSync, exec as execCb, spawnSync, } from "node:child_process";
2
+ /**
3
+ * Run a command synchronously, returning trimmed stdout.
4
+ * Throws on non-zero exit.
5
+ */
6
+ export function exec(command, opts) {
7
+ return execSync(command, {
8
+ encoding: "utf-8",
9
+ cwd: opts?.cwd,
10
+ env: { ...process.env, ...opts?.env },
11
+ stdio: ["pipe", "pipe", "pipe"],
12
+ maxBuffer: 10 * 1024 * 1024,
13
+ }).trim();
14
+ }
15
+ /**
16
+ * Run a command synchronously, never throws.
17
+ */
18
+ export function execSafe(command, opts) {
19
+ try {
20
+ const stdout = execSync(command, {
21
+ encoding: "utf-8",
22
+ cwd: opts?.cwd,
23
+ stdio: "pipe",
24
+ }).trim();
25
+ return { stdout, stderr: "", exitCode: 0 };
26
+ }
27
+ catch (err) {
28
+ const e = err;
29
+ return {
30
+ stdout: e.stdout?.toString().trim() ?? "",
31
+ stderr: e.stderr?.toString().trim() ?? "",
32
+ exitCode: e.status ?? 1,
33
+ };
34
+ }
35
+ }
36
+ /**
37
+ * Run a command asynchronously (for use with spinners).
38
+ */
39
+ export function execAsync(command, opts) {
40
+ return new Promise((resolve, reject) => {
41
+ execCb(command, {
42
+ encoding: "utf-8",
43
+ cwd: opts?.cwd,
44
+ maxBuffer: 10 * 1024 * 1024,
45
+ }, (error, stdout, stderr) => {
46
+ if (error) {
47
+ const wrapped = new Error(stderr || error.message);
48
+ wrapped.exitCode = error.code;
49
+ wrapped.stdout = stdout;
50
+ reject(wrapped);
51
+ }
52
+ else {
53
+ resolve(stdout.trim());
54
+ }
55
+ });
56
+ });
57
+ }
58
+ /**
59
+ * Run a command with output streamed to the terminal (inherit stdio).
60
+ */
61
+ export function streamExec(command, opts) {
62
+ execSync(command, {
63
+ cwd: opts?.cwd,
64
+ stdio: opts?.stdio ?? "inherit",
65
+ });
66
+ }
67
+ /**
68
+ * Run a command with spawnSync for finer arg control.
69
+ */
70
+ export function run(cmd, args, opts) {
71
+ const result = spawnSync(cmd, args, {
72
+ encoding: "utf-8",
73
+ stdio: opts?.stdio ?? "pipe",
74
+ cwd: opts?.cwd,
75
+ });
76
+ return {
77
+ stdout: result.stdout?.trim() ?? "",
78
+ stderr: result.stderr?.trim() ?? "",
79
+ exitCode: result.status ?? 1,
80
+ };
81
+ }
82
+ export function commandExists(name) {
83
+ const { exitCode } = execSafe(`command -v ${name}`);
84
+ return exitCode === 0;
85
+ }
86
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ declare const BACK: unique symbol;
2
+ export type StepResult<T> = T | typeof BACK;
3
+ export declare function back<T>(): StepResult<T>;
4
+ export interface WizardField<T> {
5
+ id: string;
6
+ section: string;
7
+ run: (state: T) => Promise<StepResult<T>>;
8
+ review: (state: T) => [label: string, value: string];
9
+ /** Skip prompting during forward pass (field still shows in review) */
10
+ skip?: (state: T) => boolean;
11
+ /** Hide from review table entirely (field is not applicable) */
12
+ hidden?: (state: T) => boolean;
13
+ }
14
+ declare function maskSecret(value: string): string;
15
+ export { maskSecret };
16
+ export declare function stepWizard<T>(fields: WizardField<T>[], initialState: T): Promise<T>;
@@ -0,0 +1,117 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import { handleCancel } from "./log.js";
4
+ // ---------------------------------------------------------------------------
5
+ // Types
6
+ // ---------------------------------------------------------------------------
7
+ const BACK = Symbol("wizard-back");
8
+ export function back() {
9
+ return BACK;
10
+ }
11
+ function isBack(result) {
12
+ return result === BACK;
13
+ }
14
+ // ---------------------------------------------------------------------------
15
+ // Helpers
16
+ // ---------------------------------------------------------------------------
17
+ function maskSecret(value) {
18
+ if (!value || value.length < 8)
19
+ return value ? "••••" : pc.dim("(empty)");
20
+ return value.slice(0, 4) + "••••" + value.slice(-4);
21
+ }
22
+ export { maskSecret };
23
+ function uniqueSections(fields) {
24
+ const seen = new Set();
25
+ const result = [];
26
+ for (const f of fields) {
27
+ if (!seen.has(f.section)) {
28
+ seen.add(f.section);
29
+ result.push(f.section);
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Step-by-step navigation within a subset of fields.
36
+ // Ctrl+C = go back one non-skipped field.
37
+ // Returns false if the user backed out past the first field in the subset.
38
+ // ---------------------------------------------------------------------------
39
+ async function runFieldSlice(fields, state, opts) {
40
+ let step = 0;
41
+ const shouldSkip = (f, s) => f.hidden?.(s) || (!opts?.ignoreSkip && f.skip?.(s));
42
+ while (step < fields.length) {
43
+ const field = fields[step];
44
+ if (shouldSkip(field, state.current)) {
45
+ step++;
46
+ continue;
47
+ }
48
+ const result = await field.run(state.current);
49
+ if (isBack(result)) {
50
+ let prev = step - 1;
51
+ while (prev >= 0 && shouldSkip(fields[prev], state.current)) {
52
+ prev--;
53
+ }
54
+ if (prev < 0)
55
+ return false;
56
+ step = prev;
57
+ continue;
58
+ }
59
+ state.current = result;
60
+ step++;
61
+ }
62
+ return true;
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Public API
66
+ // ---------------------------------------------------------------------------
67
+ export async function stepWizard(fields, initialState) {
68
+ const state = { current: initialState };
69
+ // ── Initial forward pass with per-field back ─────────────────────────
70
+ const completed = await runFieldSlice(fields, state);
71
+ if (!completed)
72
+ handleCancel();
73
+ // ── Review / edit loop ───────────────────────────────────────────────
74
+ const sections = uniqueSections(fields);
75
+ while (true) {
76
+ const reviewLines = [];
77
+ for (const section of sections) {
78
+ const sectionFields = fields.filter((f) => f.section === section);
79
+ const visibleFields = sectionFields.filter((f) => !f.hidden?.(state.current));
80
+ if (visibleFields.length === 0)
81
+ continue;
82
+ reviewLines.push(pc.bold(pc.cyan(section)));
83
+ for (const field of visibleFields) {
84
+ const [label, value] = field.review(state.current);
85
+ reviewLines.push(` ${pc.dim(label + ":")} ${value}`);
86
+ }
87
+ reviewLines.push("");
88
+ }
89
+ p.note(reviewLines.join("\n"), "Configuration Review");
90
+ const visibleSections = sections.filter((s) => fields.some((f) => f.section === s && !f.hidden?.(state.current)));
91
+ const action = await p.select({
92
+ message: "How would you like to proceed?",
93
+ options: [
94
+ {
95
+ value: "_confirm",
96
+ label: "Confirm and proceed",
97
+ hint: "start bootstrap",
98
+ },
99
+ ...visibleSections.map((s) => ({
100
+ value: s,
101
+ label: `Edit: ${s}`,
102
+ })),
103
+ { value: "_cancel", label: "Cancel" },
104
+ ],
105
+ });
106
+ if (p.isCancel(action))
107
+ handleCancel();
108
+ if (action === "_confirm")
109
+ break;
110
+ if (action === "_cancel")
111
+ handleCancel();
112
+ const sectionFields = fields.filter((f) => f.section === action);
113
+ await runFieldSlice(sectionFields, state, { ignoreSkip: true });
114
+ }
115
+ return state.current;
116
+ }
117
+ //# sourceMappingURL=wizard.js.map