offwatch 0.5.12 → 0.5.13

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 (95) hide show
  1. package/README.md +132 -178
  2. package/bin/offwatch.js +6 -7
  3. package/lib/downloader.js +112 -0
  4. package/package.json +18 -11
  5. package/postinstall.js +21 -0
  6. package/src/__tests__/agent-jwt-env.test.ts +0 -79
  7. package/src/__tests__/allowed-hostname.test.ts +0 -80
  8. package/src/__tests__/auth-command-registration.test.ts +0 -16
  9. package/src/__tests__/board-auth.test.ts +0 -53
  10. package/src/__tests__/common.test.ts +0 -98
  11. package/src/__tests__/company-delete.test.ts +0 -95
  12. package/src/__tests__/company-import-export-e2e.test.ts +0 -502
  13. package/src/__tests__/company-import-url.test.ts +0 -74
  14. package/src/__tests__/company-import-zip.test.ts +0 -44
  15. package/src/__tests__/company.test.ts +0 -599
  16. package/src/__tests__/context.test.ts +0 -70
  17. package/src/__tests__/data-dir.test.ts +0 -79
  18. package/src/__tests__/doctor.test.ts +0 -102
  19. package/src/__tests__/feedback.test.ts +0 -177
  20. package/src/__tests__/helpers/embedded-postgres.ts +0 -6
  21. package/src/__tests__/helpers/zip.ts +0 -87
  22. package/src/__tests__/home-paths.test.ts +0 -44
  23. package/src/__tests__/http.test.ts +0 -106
  24. package/src/__tests__/network-bind.test.ts +0 -62
  25. package/src/__tests__/onboard.test.ts +0 -166
  26. package/src/__tests__/routines.test.ts +0 -249
  27. package/src/__tests__/telemetry.test.ts +0 -117
  28. package/src/__tests__/worktree-merge-history.test.ts +0 -492
  29. package/src/__tests__/worktree.test.ts +0 -982
  30. package/src/adapters/http/format-event.ts +0 -4
  31. package/src/adapters/http/index.ts +0 -7
  32. package/src/adapters/index.ts +0 -2
  33. package/src/adapters/process/format-event.ts +0 -4
  34. package/src/adapters/process/index.ts +0 -7
  35. package/src/adapters/registry.ts +0 -63
  36. package/src/checks/agent-jwt-secret-check.ts +0 -40
  37. package/src/checks/config-check.ts +0 -33
  38. package/src/checks/database-check.ts +0 -59
  39. package/src/checks/deployment-auth-check.ts +0 -88
  40. package/src/checks/index.ts +0 -18
  41. package/src/checks/llm-check.ts +0 -82
  42. package/src/checks/log-check.ts +0 -30
  43. package/src/checks/path-resolver.ts +0 -1
  44. package/src/checks/port-check.ts +0 -24
  45. package/src/checks/secrets-check.ts +0 -146
  46. package/src/checks/storage-check.ts +0 -51
  47. package/src/client/board-auth.ts +0 -282
  48. package/src/client/command-label.ts +0 -4
  49. package/src/client/context.ts +0 -175
  50. package/src/client/http.ts +0 -255
  51. package/src/commands/allowed-hostname.ts +0 -40
  52. package/src/commands/auth-bootstrap-ceo.ts +0 -138
  53. package/src/commands/client/activity.ts +0 -71
  54. package/src/commands/client/agent.ts +0 -315
  55. package/src/commands/client/approval.ts +0 -259
  56. package/src/commands/client/auth.ts +0 -113
  57. package/src/commands/client/common.ts +0 -221
  58. package/src/commands/client/company.ts +0 -1578
  59. package/src/commands/client/context.ts +0 -125
  60. package/src/commands/client/dashboard.ts +0 -34
  61. package/src/commands/client/feedback.ts +0 -645
  62. package/src/commands/client/issue.ts +0 -411
  63. package/src/commands/client/plugin.ts +0 -374
  64. package/src/commands/client/zip.ts +0 -129
  65. package/src/commands/configure.ts +0 -201
  66. package/src/commands/db-backup.ts +0 -102
  67. package/src/commands/doctor.ts +0 -203
  68. package/src/commands/env.ts +0 -411
  69. package/src/commands/heartbeat-run.ts +0 -344
  70. package/src/commands/onboard.ts +0 -692
  71. package/src/commands/routines.ts +0 -352
  72. package/src/commands/run.ts +0 -216
  73. package/src/commands/worktree-lib.ts +0 -279
  74. package/src/commands/worktree-merge-history-lib.ts +0 -764
  75. package/src/commands/worktree.ts +0 -2876
  76. package/src/config/data-dir.ts +0 -48
  77. package/src/config/env.ts +0 -125
  78. package/src/config/home.ts +0 -80
  79. package/src/config/hostnames.ts +0 -26
  80. package/src/config/schema.ts +0 -30
  81. package/src/config/secrets-key.ts +0 -48
  82. package/src/config/server-bind.ts +0 -183
  83. package/src/config/store.ts +0 -120
  84. package/src/index.ts +0 -182
  85. package/src/prompts/database.ts +0 -157
  86. package/src/prompts/llm.ts +0 -43
  87. package/src/prompts/logging.ts +0 -37
  88. package/src/prompts/secrets.ts +0 -99
  89. package/src/prompts/server.ts +0 -221
  90. package/src/prompts/storage.ts +0 -146
  91. package/src/telemetry.ts +0 -49
  92. package/src/utils/banner.ts +0 -24
  93. package/src/utils/net.ts +0 -18
  94. package/src/utils/path-resolver.ts +0 -25
  95. package/src/version.ts +0 -10
package/src/index.ts DELETED
@@ -1,182 +0,0 @@
1
- import { Command } from "commander";
2
- import { onboard } from "./commands/onboard.js";
3
- import { doctor } from "./commands/doctor.js";
4
- import { envCommand } from "./commands/env.js";
5
- import { configure } from "./commands/configure.js";
6
- import { addAllowedHostname } from "./commands/allowed-hostname.js";
7
- import { heartbeatRun } from "./commands/heartbeat-run.js";
8
- import { runCommand } from "./commands/run.js";
9
- import { bootstrapCeoInvite } from "./commands/auth-bootstrap-ceo.js";
10
- import { dbBackupCommand } from "./commands/db-backup.js";
11
- import { registerContextCommands } from "./commands/client/context.js";
12
- import { registerCompanyCommands } from "./commands/client/company.js";
13
- import { registerIssueCommands } from "./commands/client/issue.js";
14
- import { registerAgentCommands } from "./commands/client/agent.js";
15
- import { registerApprovalCommands } from "./commands/client/approval.js";
16
- import { registerActivityCommands } from "./commands/client/activity.js";
17
- import { registerDashboardCommands } from "./commands/client/dashboard.js";
18
- import { registerRoutineCommands } from "./commands/routines.js";
19
- import { registerFeedbackCommands } from "./commands/client/feedback.js";
20
- import { applyDataDirOverride, type DataDirOptionLike } from "./config/data-dir.js";
21
- import { loadPaperclipEnvFile } from "./config/env.js";
22
- import { initTelemetryFromConfigFile, flushTelemetry } from "./telemetry.js";
23
- import { registerWorktreeCommands } from "./commands/worktree.js";
24
- import { registerPluginCommands } from "./commands/client/plugin.js";
25
- import { registerClientAuthCommands } from "./commands/client/auth.js";
26
- import { cliVersion } from "./version.js";
27
-
28
- const program = new Command();
29
- const DATA_DIR_OPTION_HELP =
30
- "Paperclip data directory root (isolates state from ~/.paperclip)";
31
-
32
- program
33
- .name("paperclipai")
34
- .description("Paperclip CLI — setup, diagnose, and configure your instance")
35
- .version(cliVersion);
36
-
37
- program.hook("preAction", (_thisCommand, actionCommand) => {
38
- const options = actionCommand.optsWithGlobals() as DataDirOptionLike;
39
- const optionNames = new Set(actionCommand.options.map((option) => option.attributeName()));
40
- applyDataDirOverride(options, {
41
- hasConfigOption: optionNames.has("config"),
42
- hasContextOption: optionNames.has("context"),
43
- });
44
- loadPaperclipEnvFile(options.config);
45
- initTelemetryFromConfigFile(options.config);
46
- });
47
-
48
- program
49
- .command("onboard")
50
- .description("Interactive first-run setup wizard")
51
- .option("-c, --config <path>", "Path to config file")
52
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
53
- .option("--bind <mode>", "Quickstart reachability preset (loopback, lan, tailnet)")
54
- .option("-y, --yes", "Accept quickstart defaults (trusted local loopback unless --bind is set) and start immediately", false)
55
- .option("--run", "Start Paperclip immediately after saving config", false)
56
- .action(onboard);
57
-
58
- program
59
- .command("doctor")
60
- .description("Run diagnostic checks on your Paperclip setup")
61
- .option("-c, --config <path>", "Path to config file")
62
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
63
- .option("--repair", "Attempt to repair issues automatically")
64
- .alias("--fix")
65
- .option("-y, --yes", "Skip repair confirmation prompts")
66
- .action(async (opts) => {
67
- await doctor(opts);
68
- });
69
-
70
- program
71
- .command("env")
72
- .description("Print environment variables for deployment")
73
- .option("-c, --config <path>", "Path to config file")
74
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
75
- .action(envCommand);
76
-
77
- program
78
- .command("configure")
79
- .description("Update configuration sections")
80
- .option("-c, --config <path>", "Path to config file")
81
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
82
- .option("-s, --section <section>", "Section to configure (llm, database, logging, server, storage, secrets)")
83
- .action(configure);
84
-
85
- program
86
- .command("db:backup")
87
- .description("Create a one-off database backup using current config")
88
- .option("-c, --config <path>", "Path to config file")
89
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
90
- .option("--dir <path>", "Backup output directory (overrides config)")
91
- .option("--retention-days <days>", "Retention window used for pruning", (value) => Number(value))
92
- .option("--filename-prefix <prefix>", "Backup filename prefix", "paperclip")
93
- .option("--json", "Print backup metadata as JSON")
94
- .action(async (opts) => {
95
- await dbBackupCommand(opts);
96
- });
97
-
98
- program
99
- .command("allowed-hostname")
100
- .description("Allow a hostname for authenticated/private mode access")
101
- .argument("<host>", "Hostname to allow (for example dotta-macbook-pro)")
102
- .option("-c, --config <path>", "Path to config file")
103
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
104
- .action(addAllowedHostname);
105
-
106
- program
107
- .command("run")
108
- .description("Bootstrap local setup (onboard + doctor) and run Paperclip")
109
- .option("-c, --config <path>", "Path to config file")
110
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
111
- .option("-i, --instance <id>", "Local instance id (default: default)")
112
- .option("--bind <mode>", "On first run, use onboarding reachability preset (loopback, lan, tailnet)")
113
- .option("--repair", "Attempt automatic repairs during doctor", true)
114
- .option("--no-repair", "Disable automatic repairs during doctor")
115
- .action(runCommand);
116
-
117
- const heartbeat = program.command("heartbeat").description("Heartbeat utilities");
118
-
119
- heartbeat
120
- .command("run")
121
- .description("Run one agent heartbeat and stream live logs")
122
- .requiredOption("-a, --agent-id <agentId>", "Agent ID to invoke")
123
- .option("-c, --config <path>", "Path to config file")
124
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
125
- .option("--context <path>", "Path to CLI context file")
126
- .option("--profile <name>", "CLI context profile name")
127
- .option("--api-base <url>", "Base URL for the Paperclip server API")
128
- .option("--api-key <token>", "Bearer token for agent-authenticated calls")
129
- .option(
130
- "--source <source>",
131
- "Invocation source (timer | assignment | on_demand | automation)",
132
- "on_demand",
133
- )
134
- .option("--trigger <trigger>", "Trigger detail (manual | ping | callback | system)", "manual")
135
- .option("--timeout-ms <ms>", "Max time to wait before giving up", "0")
136
- .option("--json", "Output raw JSON where applicable")
137
- .option("--debug", "Show raw adapter stdout/stderr JSON chunks")
138
- .action(heartbeatRun);
139
-
140
- registerContextCommands(program);
141
- registerCompanyCommands(program);
142
- registerIssueCommands(program);
143
- registerAgentCommands(program);
144
- registerApprovalCommands(program);
145
- registerActivityCommands(program);
146
- registerDashboardCommands(program);
147
- registerRoutineCommands(program);
148
- registerFeedbackCommands(program);
149
- registerWorktreeCommands(program);
150
- registerPluginCommands(program);
151
-
152
- const auth = program.command("auth").description("Authentication and bootstrap utilities");
153
-
154
- auth
155
- .command("bootstrap-ceo")
156
- .description("Create a one-time bootstrap invite URL for first instance admin")
157
- .option("-c, --config <path>", "Path to config file")
158
- .option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP)
159
- .option("--force", "Create new invite even if admin already exists", false)
160
- .option("--expires-hours <hours>", "Invite expiration window in hours", (value) => Number(value))
161
- .option("--base-url <url>", "Public base URL used to print invite link")
162
- .action(bootstrapCeoInvite);
163
-
164
- registerClientAuthCommands(auth);
165
-
166
- async function main(): Promise<void> {
167
- let failed = false;
168
- try {
169
- await program.parseAsync();
170
- } catch (err) {
171
- failed = true;
172
- console.error(err instanceof Error ? err.message : String(err));
173
- } finally {
174
- await flushTelemetry();
175
- }
176
-
177
- if (failed) {
178
- process.exit(1);
179
- }
180
- }
181
-
182
- void main();
@@ -1,157 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import type { DatabaseConfig } from "../config/schema.js";
3
- import {
4
- resolveDefaultBackupDir,
5
- resolveDefaultEmbeddedPostgresDir,
6
- resolvePaperclipInstanceId,
7
- } from "../config/home.js";
8
-
9
- export async function promptDatabase(current?: DatabaseConfig): Promise<DatabaseConfig> {
10
- const instanceId = resolvePaperclipInstanceId();
11
- const defaultEmbeddedDir = resolveDefaultEmbeddedPostgresDir(instanceId);
12
- const defaultBackupDir = resolveDefaultBackupDir(instanceId);
13
- const base: DatabaseConfig = current ?? {
14
- mode: "embedded-postgres",
15
- embeddedPostgresDataDir: defaultEmbeddedDir,
16
- embeddedPostgresPort: 54329,
17
- backup: {
18
- enabled: true,
19
- intervalMinutes: 60,
20
- retentionDays: 30,
21
- dir: defaultBackupDir,
22
- },
23
- };
24
-
25
- const mode = await p.select({
26
- message: "Database mode",
27
- options: [
28
- { value: "embedded-postgres" as const, label: "Embedded PostgreSQL (managed locally)", hint: "recommended" },
29
- { value: "postgres" as const, label: "PostgreSQL (external server)" },
30
- ],
31
- initialValue: base.mode,
32
- });
33
-
34
- if (p.isCancel(mode)) {
35
- p.cancel("Setup cancelled.");
36
- process.exit(0);
37
- }
38
-
39
- let connectionString: string | undefined = base.connectionString;
40
- let embeddedPostgresDataDir = base.embeddedPostgresDataDir || defaultEmbeddedDir;
41
- let embeddedPostgresPort = base.embeddedPostgresPort || 54329;
42
-
43
- if (mode === "postgres") {
44
- const value = await p.text({
45
- message: "PostgreSQL connection string",
46
- defaultValue: base.connectionString ?? "",
47
- placeholder: "postgres://user:pass@localhost:5432/paperclip",
48
- validate: (val) => {
49
- if (!val) return "Connection string is required for PostgreSQL mode";
50
- if (!val.startsWith("postgres")) return "Must be a postgres:// or postgresql:// URL";
51
- },
52
- });
53
-
54
- if (p.isCancel(value)) {
55
- p.cancel("Setup cancelled.");
56
- process.exit(0);
57
- }
58
-
59
- connectionString = value;
60
- } else {
61
- const dataDir = await p.text({
62
- message: "Embedded PostgreSQL data directory",
63
- defaultValue: base.embeddedPostgresDataDir || defaultEmbeddedDir,
64
- placeholder: defaultEmbeddedDir,
65
- });
66
-
67
- if (p.isCancel(dataDir)) {
68
- p.cancel("Setup cancelled.");
69
- process.exit(0);
70
- }
71
-
72
- embeddedPostgresDataDir = dataDir || defaultEmbeddedDir;
73
-
74
- const portValue = await p.text({
75
- message: "Embedded PostgreSQL port",
76
- defaultValue: String(base.embeddedPostgresPort || 54329),
77
- placeholder: "54329",
78
- validate: (val) => {
79
- const n = Number(val);
80
- if (!Number.isInteger(n) || n < 1 || n > 65535) return "Port must be an integer between 1 and 65535";
81
- },
82
- });
83
-
84
- if (p.isCancel(portValue)) {
85
- p.cancel("Setup cancelled.");
86
- process.exit(0);
87
- }
88
-
89
- embeddedPostgresPort = Number(portValue || "54329");
90
- connectionString = undefined;
91
- }
92
-
93
- const backupEnabled = await p.confirm({
94
- message: "Enable automatic database backups?",
95
- initialValue: base.backup.enabled,
96
- });
97
- if (p.isCancel(backupEnabled)) {
98
- p.cancel("Setup cancelled.");
99
- process.exit(0);
100
- }
101
-
102
- const backupDirInput = await p.text({
103
- message: "Backup directory",
104
- defaultValue: base.backup.dir || defaultBackupDir,
105
- placeholder: defaultBackupDir,
106
- validate: (val) => (!val || val.trim().length === 0 ? "Backup directory is required" : undefined),
107
- });
108
- if (p.isCancel(backupDirInput)) {
109
- p.cancel("Setup cancelled.");
110
- process.exit(0);
111
- }
112
-
113
- const backupIntervalInput = await p.text({
114
- message: "Backup interval (minutes)",
115
- defaultValue: String(base.backup.intervalMinutes || 60),
116
- placeholder: "60",
117
- validate: (val) => {
118
- const n = Number(val);
119
- if (!Number.isInteger(n) || n < 1) return "Interval must be a positive integer";
120
- if (n > 10080) return "Interval must be 10080 minutes (7 days) or less";
121
- return undefined;
122
- },
123
- });
124
- if (p.isCancel(backupIntervalInput)) {
125
- p.cancel("Setup cancelled.");
126
- process.exit(0);
127
- }
128
-
129
- const backupRetentionInput = await p.text({
130
- message: "Backup retention (days)",
131
- defaultValue: String(base.backup.retentionDays || 30),
132
- placeholder: "30",
133
- validate: (val) => {
134
- const n = Number(val);
135
- if (!Number.isInteger(n) || n < 1) return "Retention must be a positive integer";
136
- if (n > 3650) return "Retention must be 3650 days or less";
137
- return undefined;
138
- },
139
- });
140
- if (p.isCancel(backupRetentionInput)) {
141
- p.cancel("Setup cancelled.");
142
- process.exit(0);
143
- }
144
-
145
- return {
146
- mode,
147
- connectionString,
148
- embeddedPostgresDataDir,
149
- embeddedPostgresPort,
150
- backup: {
151
- enabled: backupEnabled,
152
- intervalMinutes: Number(backupIntervalInput || "60"),
153
- retentionDays: Number(backupRetentionInput || "30"),
154
- dir: backupDirInput || defaultBackupDir,
155
- },
156
- };
157
- }
@@ -1,43 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import type { LlmConfig } from "../config/schema.js";
3
-
4
- export async function promptLlm(): Promise<LlmConfig | undefined> {
5
- const configureLlm = await p.confirm({
6
- message: "Configure an LLM provider now?",
7
- initialValue: false,
8
- });
9
-
10
- if (p.isCancel(configureLlm)) {
11
- p.cancel("Setup cancelled.");
12
- process.exit(0);
13
- }
14
-
15
- if (!configureLlm) return undefined;
16
-
17
- const provider = await p.select({
18
- message: "LLM provider",
19
- options: [
20
- { value: "claude" as const, label: "Claude (Anthropic)" },
21
- { value: "openai" as const, label: "OpenAI" },
22
- ],
23
- });
24
-
25
- if (p.isCancel(provider)) {
26
- p.cancel("Setup cancelled.");
27
- process.exit(0);
28
- }
29
-
30
- const apiKey = await p.password({
31
- message: `${provider === "claude" ? "Anthropic" : "OpenAI"} API key`,
32
- validate: (val) => {
33
- if (!val) return "API key is required";
34
- },
35
- });
36
-
37
- if (p.isCancel(apiKey)) {
38
- p.cancel("Setup cancelled.");
39
- process.exit(0);
40
- }
41
-
42
- return { provider, apiKey };
43
- }
@@ -1,37 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import type { LoggingConfig } from "../config/schema.js";
3
- import { resolveDefaultLogsDir, resolvePaperclipInstanceId } from "../config/home.js";
4
-
5
- export async function promptLogging(): Promise<LoggingConfig> {
6
- const defaultLogDir = resolveDefaultLogsDir(resolvePaperclipInstanceId());
7
- const mode = await p.select({
8
- message: "Logging mode",
9
- options: [
10
- { value: "file" as const, label: "File-based logging", hint: "recommended" },
11
- { value: "cloud" as const, label: "Cloud logging", hint: "coming soon" },
12
- ],
13
- });
14
-
15
- if (p.isCancel(mode)) {
16
- p.cancel("Setup cancelled.");
17
- process.exit(0);
18
- }
19
-
20
- if (mode === "file") {
21
- const logDir = await p.text({
22
- message: "Log directory",
23
- defaultValue: defaultLogDir,
24
- placeholder: defaultLogDir,
25
- });
26
-
27
- if (p.isCancel(logDir)) {
28
- p.cancel("Setup cancelled.");
29
- process.exit(0);
30
- }
31
-
32
- return { mode: "file", logDir: logDir || defaultLogDir };
33
- }
34
-
35
- p.note("Cloud logging is coming soon. Using file-based logging for now.");
36
- return { mode: "file", logDir: defaultLogDir };
37
- }
@@ -1,99 +0,0 @@
1
- import * as p from "@clack/prompts";
2
- import type { SecretProvider } from "@paperclipai/shared";
3
- import type { SecretsConfig } from "../config/schema.js";
4
- import { resolveDefaultSecretsKeyFilePath, resolvePaperclipInstanceId } from "../config/home.js";
5
-
6
- function defaultKeyFilePath(): string {
7
- return resolveDefaultSecretsKeyFilePath(resolvePaperclipInstanceId());
8
- }
9
-
10
- export function defaultSecretsConfig(): SecretsConfig {
11
- const keyFilePath = defaultKeyFilePath();
12
- return {
13
- provider: "local_encrypted",
14
- strictMode: false,
15
- localEncrypted: {
16
- keyFilePath,
17
- },
18
- };
19
- }
20
-
21
- export async function promptSecrets(current?: SecretsConfig): Promise<SecretsConfig> {
22
- const base = current ?? defaultSecretsConfig();
23
-
24
- const provider = await p.select({
25
- message: "Secrets provider",
26
- options: [
27
- {
28
- value: "local_encrypted" as const,
29
- label: "Local encrypted (recommended)",
30
- hint: "best for single-developer installs",
31
- },
32
- {
33
- value: "aws_secrets_manager" as const,
34
- label: "AWS Secrets Manager",
35
- hint: "requires external adapter integration",
36
- },
37
- {
38
- value: "gcp_secret_manager" as const,
39
- label: "GCP Secret Manager",
40
- hint: "requires external adapter integration",
41
- },
42
- {
43
- value: "vault" as const,
44
- label: "HashiCorp Vault",
45
- hint: "requires external adapter integration",
46
- },
47
- ],
48
- initialValue: base.provider,
49
- });
50
-
51
- if (p.isCancel(provider)) {
52
- p.cancel("Setup cancelled.");
53
- process.exit(0);
54
- }
55
-
56
- const strictMode = await p.confirm({
57
- message: "Require secret refs for sensitive env vars?",
58
- initialValue: base.strictMode,
59
- });
60
-
61
- if (p.isCancel(strictMode)) {
62
- p.cancel("Setup cancelled.");
63
- process.exit(0);
64
- }
65
-
66
- const fallbackDefault = defaultKeyFilePath();
67
- let keyFilePath = base.localEncrypted.keyFilePath || fallbackDefault;
68
- if (provider === "local_encrypted") {
69
- const keyPath = await p.text({
70
- message: "Local encrypted key file path",
71
- defaultValue: keyFilePath,
72
- placeholder: fallbackDefault,
73
- validate: (value) => {
74
- if (!value || value.trim().length === 0) return "Key file path is required";
75
- },
76
- });
77
-
78
- if (p.isCancel(keyPath)) {
79
- p.cancel("Setup cancelled.");
80
- process.exit(0);
81
- }
82
- keyFilePath = keyPath.trim();
83
- }
84
-
85
- if (provider !== "local_encrypted") {
86
- p.note(
87
- `${provider} is not fully wired in this build yet. Keep local_encrypted unless you are actively implementing that adapter.`,
88
- "Heads up",
89
- );
90
- }
91
-
92
- return {
93
- provider: provider as SecretProvider,
94
- strictMode,
95
- localEncrypted: {
96
- keyFilePath,
97
- },
98
- };
99
- }