@aopslabs/aops 0.2.0 → 0.3.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.
@@ -6,15 +6,7 @@ import { runCommunitySetupServerEnv, runCommunitySetupServerEnvConnectionTest, }
6
6
  import { createSetupOfficialCatalogProviderV1 } from '../lib/setup-official-catalog-bridge.js';
7
7
  import { createSetupAgentAssetsProvider } from '../lib/setup-agent-assets-bridge.js';
8
8
  import { buildAopsInstallAgentPrompt, loadAopsInstallSkill, } from '../lib/setup-install-guide.js';
9
- import { promptSelect } from '../utils/prompts.js';
10
- const COMMUNITY_SETUP_HOME = `AOPS Setup — choose how to begin
11
- Install interactively aops setup init
12
- Set up with an AI agent aops setup ai
13
- Inspect readiness aops setup init --yes --json
14
- Agent install skill aops setup guide
15
- Configure PostgreSQL aops setup server-env
16
- Help: aops setup --help
17
- `;
9
+ import { applyDockerServerSetup, inspectDockerServerSetupPreview, inspectDockerServerSetupStatus, runDockerServerLifecycle, } from '../lib/setup-docker-server.js';
18
10
  export function runCommunitySetupGuide(options = {}) {
19
11
  if (options.json && options.path)
20
12
  throw new Error('setup_guide_selector_conflict:choose_--json_or_--path');
@@ -61,7 +53,7 @@ export function runCommunitySetupAi(options = {}) {
61
53
  }
62
54
  banner('AOPS Setup with AI');
63
55
  logInfo('Copy the prompt below to Codex, Claude, or another terminal AI agent.');
64
- logInfo('Enter database secrets only in AOPS masked prompts; never paste them into chat.');
56
+ logInfo('Pass database secrets only through documented environment variables or private files; never paste them into chat.');
65
57
  process.stdout.write(`\n--- copy from here ---\n${prompt}\n--- end prompt ---\n`);
66
58
  }
67
59
  export async function runCommunitySetupInit(options = {}) {
@@ -71,49 +63,66 @@ export async function runCommunitySetupInit(options = {}) {
71
63
  officialCatalog: createSetupOfficialCatalogProviderV1(),
72
64
  });
73
65
  }
74
- export async function runCommunitySetupMenu() {
75
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
76
- process.stdout.write(COMMUNITY_SETUP_HOME);
77
- return;
78
- }
79
- banner('AOPS Setup');
80
- logInfo('Install here, or hand a safe setup prompt to any terminal AI agent.');
81
- logInfo('Enter database secrets only in masked AOPS prompts; never paste them into chat.');
82
- while (true) {
83
- const action = await promptSelect({
84
- message: 'How do you want to continue?',
85
- type: process.env.AOPS_CLI_MENU_STYLE?.toLowerCase() === 'rawlist' ? 'rawlist' : 'select',
86
- pageSize: 6,
87
- choices: [
88
- { name: 'Install AOPS interactively', value: 'install' },
89
- { name: 'Set up with an AI agent', value: 'ai' },
90
- { name: 'Inspect setup readiness', value: 'inspect' },
91
- { name: 'Configure PostgreSQL connection', value: 'server-env' },
92
- { name: 'Show packaged agent installation skill', value: 'guide' },
93
- { name: 'Exit', value: 'exit' },
94
- ],
95
- });
96
- if (action === 'exit')
66
+ export async function runCommunityDockerSetupPreview(options = {}) {
67
+ const selectedActions = [options.apply, options.status, Boolean(options.lifecycle)].filter(Boolean).length;
68
+ if (selectedActions > 1)
69
+ throw new Error('setup_docker_action_conflict:choose_one_action');
70
+ if (options.status) {
71
+ const result = await inspectDockerServerSetupStatus(options);
72
+ if (options.json) {
73
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
97
74
  return;
98
- if (action === 'ai') {
99
- runCommunitySetupAi();
100
- continue;
101
75
  }
102
- if (action === 'inspect') {
103
- await runCommunitySetupInit({ yes: true });
104
- continue;
76
+ banner('AOPS Docker Settings');
77
+ logInfo(`AOPS: ${result.components.server.state} · Database: ${result.components.database.state}`);
78
+ logInfo(`Address: ${result.origin}`);
79
+ logInfo(`Settings: ${result.environmentPath}`);
80
+ if (result.nextAction)
81
+ logInfo(result.nextAction);
82
+ return;
83
+ }
84
+ if (options.lifecycle) {
85
+ const actions = ['start', 'stop', 'restart', 'open-cockpit', 'repair'];
86
+ if (!actions.includes(options.lifecycle)) {
87
+ throw new Error('setup_docker_lifecycle_invalid:use_start_stop_restart_open-cockpit_or_repair');
105
88
  }
106
- if (action === 'server-env') {
107
- await runCommunitySetupServerEnv({});
108
- continue;
89
+ if (!options.yes)
90
+ throw new Error('setup_docker_lifecycle_confirmation_required:use_--yes');
91
+ const result = await runDockerServerLifecycle(options.lifecycle, options);
92
+ if (options.json) {
93
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
94
+ return;
109
95
  }
110
- if (action === 'guide') {
111
- runCommunitySetupGuide();
112
- continue;
96
+ banner('AOPS Docker Settings');
97
+ logInfo(`${result.action} completed · ${result.origin}`);
98
+ return;
99
+ }
100
+ if (options.apply) {
101
+ if (!options.yes)
102
+ throw new Error('setup_docker_apply_confirmation_required:use_--yes');
103
+ const result = await applyDockerServerSetup(options);
104
+ if (options.json) {
105
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
106
+ return;
113
107
  }
114
- await runCommunitySetupInit({ skipBanner: true });
108
+ banner('AOPS Docker Setup');
109
+ logInfo(`Installed · ${result.origin}`);
110
+ logInfo('PostgreSQL migrations and AOPS Server health completed.');
115
111
  return;
116
112
  }
113
+ const preview = await inspectDockerServerSetupPreview(options);
114
+ if (options.json) {
115
+ process.stdout.write(`${JSON.stringify(preview, null, 2)}\n`);
116
+ return;
117
+ }
118
+ banner('AOPS Docker Setup');
119
+ logInfo(`${preview.method.title}: ${preview.method.description}`);
120
+ logInfo(`Image: ${preview.generated.image}`);
121
+ logInfo(`Address: ${preview.generated.origin}`);
122
+ for (const check of preview.checks) {
123
+ logInfo(`${check.status === 'pass' ? 'OK' : 'ACTION'} · ${check.summary}`);
124
+ }
125
+ logInfo('This preview made no changes. Guided Apply will be enabled in the next setup slice.');
117
126
  }
118
127
  function addSetupInitOptions(command) {
119
128
  return command
@@ -142,7 +151,7 @@ function addSetupInitOptions(command) {
142
151
  .option('--catalog-release <path>', 'Explicit verified-release override (normally resolved from the Community install)')
143
152
  .option('--catalog-idempotency-key <key>', 'Explicit official catalog reconcile replay key')
144
153
  .option('--plan-id <sha256>', 'Apply only if the current mutation-free installer plan still has this exact identity')
145
- .option('--apply', 'Apply the selected path in non-interactive or scripted use (interactive setup applies directly)')
154
+ .option('--apply', 'Apply the selected explicit path')
146
155
  .option('--resume', 'Resume the same idempotent setup orchestration')
147
156
  .option('--timeout-ms <ms>', 'Read-only host probe timeout', (value) => Number.parseInt(String(value), 10))
148
157
  .option('--yes', 'Non-interactive; report missing selections as actions')
@@ -156,8 +165,8 @@ Agent bootstrap:
156
165
  aops setup ai Print a copy-ready prompt for any terminal AI agent
157
166
  aops setup guide Print the packaged agent-readable installation skill
158
167
  aops setup guide --json Return the same guide in a structured envelope
159
- `)
160
- .action(async () => runCommunitySetupMenu());
168
+ `);
169
+ command.action(() => command.outputHelp());
161
170
  command.addCommand(makeOfficialCatalogSetupCommand());
162
171
  command.command('ai')
163
172
  .description('Print a safe, copy-ready AOPS installation prompt for a terminal AI agent')
@@ -165,7 +174,8 @@ Agent bootstrap:
165
174
  .addHelpText('after', `
166
175
  This read-only handoff works with Codex, Claude, or another terminal agent. The
167
176
  prompt directs the agent to the packaged install skill and keeps database
168
- credentials in AOPS masked terminal prompts rather than chat or command argv.
177
+ credentials in documented environment variables or private files rather than
178
+ chat or command argv.
169
179
  `)
170
180
  .action((options) => runCommunitySetupAi(options));
171
181
  command.command('guide')
@@ -178,8 +188,28 @@ It explains PostgreSQL ownership, setup paths, explicit Gateway activation,
178
188
  server health verification, and Cockpit handoff. It performs no setup itself.
179
189
  `)
180
190
  .action((options) => runCommunitySetupGuide(options));
191
+ command.command('docker')
192
+ .description('Preview or apply the recommended Docker-hosted AOPS Server setup')
193
+ .option('--instance <name>', 'Local AOPS instance name (default: default)')
194
+ .option('--image <reference>', 'Exact local AOPS Server image reference')
195
+ .option('--pull-image <reference>', 'Image source used only when the exact local image is missing')
196
+ .option('--port <port>', 'Loopback AOPS Server port (default: 5900)', (value) => Number.parseInt(String(value), 10))
197
+ .option('--status', 'Read the installed Docker AOPS and PostgreSQL state without changing it')
198
+ .option('--lifecycle <action>', 'Settings action: start | stop | restart | open-cockpit | repair')
199
+ .option('--apply', 'Create the reviewed Docker resources and wait for migrations and health')
200
+ .option('--yes', 'Required with --apply in non-interactive use')
201
+ .option('--json', 'Output one redacted preview or Apply result')
202
+ .addHelpText('after', `
203
+ This first guided-setup slice only inspects Docker, the exact server-only image,
204
+ loopback port, resource names, and private environment-file location. It does
205
+ not create files, containers, networks, or volumes, and it does not accept an
206
+ apply flag unless both --apply and --yes are provided. Apply creates only the
207
+ displayed namespaced resources and rolls back resources created by a failed
208
+ first attempt.
209
+ `)
210
+ .action((options) => runCommunityDockerSetupPreview(options));
181
211
  addSetupInitOptions(command.command('init')
182
- .description('Interactively install AOPS, or inspect/apply an explicit path for automation'))
212
+ .description('Inspect or apply an explicit AOPS installation path'))
183
213
  .addHelpText('after', `
184
214
  Examples:
185
215
  aops setup ai
@@ -199,28 +229,21 @@ Path 1 uses an existing PostgreSQL connection from \`~/.aops/aops.server.env\` b
199
229
  the selected env file. \`--postgres-config\` remains an explicit override.
200
230
  Path 2 uses the same npm server and standard port, while AOPS creates a
201
231
  loopback-only PostgreSQL 17 container on a collision-free Docker-assigned port.
202
- Its password is generated securely by default; the interactive wizard also
203
- allows a masked, confirmed custom password without placing it in shell history.
232
+ Its password is generated securely by default.
204
233
  All PostgreSQL paths plan, apply when needed, and verify database migrations
205
- before the server is reported ready. Interactive terminals show animated
206
- progress and apply directly after required private inputs are collected; there
207
- is no redundant continue confirmation. \`--json\` remains free
208
- of spinner output. Path 1 asks for the masked PostgreSQL URL first, then tests
209
- the connection with TLS \`require\` by default. A TLS-policy prompt is shown only
210
- when that encrypted connection cannot be established. Choose \`verify-full\`
211
- when a trusted CA file is available, or explicitly choose \`disable\` when
212
- accepting an unencrypted PostgreSQL connection. Long-running setup reports its
213
- runtime, connection, migration-plan, migration-apply, server-start, and health
214
- stages without exposing database credentials.
234
+ before the server is reported ready. \`--json\` remains machine-clean. Path 1
235
+ reads the private PostgreSQL URL from the saved env or \`AOPS_PG_URL\`, then
236
+ tests TLS \`require\` by default. Pass \`--postgres-tls verify-full\` when a
237
+ trusted CA file is available, or explicitly pass \`disable\` when accepting an
238
+ unencrypted PostgreSQL connection.
215
239
  Local Community setup is always \`trusted-local\` with \`loopback\`. Paid or
216
240
  multi-user identity uses a separate HTTPS \`remote-session\` target through path
217
241
  4; it is not installed into the Community server. The legacy
218
242
  \`authv2-jwt-session\` target name is accepted only as a one-release alias.
219
- Path 3 detects PostgreSQL on this computer, securely asks for an existing
220
- administrator role/password, and creates a new dedicated AOPS role and database
243
+ Path 3 detects PostgreSQL on this computer and creates a new dedicated AOPS role and database
221
244
  before running the same migration verification. Administrator credentials are
222
- never stored. For non-interactive path 3, provide the password through the
223
- private \`AOPS_LOCAL_POSTGRES_ADMIN_PASSWORD\` environment variable; use
245
+ never stored. Provide the password through the private
246
+ \`AOPS_LOCAL_POSTGRES_ADMIN_PASSWORD\` environment variable; use
224
247
  \`--local-postgres-admin-no-password\` only when local PostgreSQL trust auth is
225
248
  already configured. When PostgreSQL is missing or stopped, readiness returns
226
249
  platform-appropriate Windows, macOS, or Linux installation/start guidance.
@@ -234,7 +257,7 @@ Source and npm setup import only the inert signed official catalog bundled with
234
257
  the verified Community release by default.
235
258
  The optional application image reuses the exact npm CLI/server lifecycle
236
259
  inside a container; it remains a distribution surface rather than another
237
- interactive setup path.
260
+ setup path.
238
261
  The CLI first resolves the canonical signed release bundled with the official
239
262
  npm package, selected source, or installed Community runtime;
240
263
  \`--catalog-release\` is only an explicit override.
@@ -2,9 +2,8 @@ import fs from 'node:fs/promises';
2
2
  import { existsSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { Command } from 'commander';
5
- import { banner, logError, logInfo, logSuccess, logWarn } from '@aopslabs/xf-cli-ui';
5
+ import { logError, logInfo, logSuccess, logWarn } from '@aopslabs/xf-cli-ui';
6
6
  import { findWorkspaceRoot } from '../utils/workspace-root.js';
7
- import { promptConfirm, promptInput } from '../utils/prompts.js';
8
7
  import { buildAopsRepoConfig } from '../utils/repo-config.js';
9
8
  function nonEmpty(value) {
10
9
  if (typeof value !== 'string')
@@ -50,26 +49,15 @@ export async function runInit(options = {}) {
50
49
  const startDir = process.cwd();
51
50
  const rootDir = options.root ? path.resolve(startDir, options.root) : findWorkspaceRoot(startDir);
52
51
  const configPath = path.join(rootDir, '.aops', 'aops.config.json');
53
- const interactive = options.yes !== true && options.json !== true;
54
52
  const existingConfig = existsSync(configPath) ? await readJsonFile(configPath) : null;
55
- if (interactive)
56
- banner('AOPS Community Init');
57
53
  if (existsSync(configPath) && options.force !== true) {
58
- const overwrite = interactive
59
- ? await promptConfirm({ message: `${configPath} exists. Overwrite?`, default: false })
60
- : false;
61
- if (!overwrite) {
62
- logError('aops.config.json already exists. Use --force to overwrite.');
63
- process.exitCode = 1;
64
- return;
65
- }
54
+ logError('aops.config.json already exists. Use --force to overwrite.');
55
+ process.exitCode = 1;
56
+ return;
66
57
  }
67
58
  const existingRepoName = nonEmpty(existingConfig?.repo?.name);
68
59
  let repoName = nonEmpty(options.repoName);
69
60
  const fallbackRepoName = path.basename(rootDir);
70
- if (!repoName && interactive) {
71
- repoName = nonEmpty(await promptInput({ message: 'Repository name:', default: existingRepoName ?? fallbackRepoName }));
72
- }
73
61
  repoName ??= existingRepoName ?? fallbackRepoName;
74
62
  if (!repoName) {
75
63
  logError('Repository name is required.');
@@ -111,7 +99,7 @@ export function makeInitCommand() {
111
99
  .option('--project-id <id>', 'Project id')
112
100
  .option('--force', 'Overwrite existing aops.config.json')
113
101
  .option('--no-agents-md', 'Skip AGENTS.md #INIT update')
114
- .option('--yes', 'Use defaults and skip prompts')
102
+ .option('--yes', 'Compatibility flag; the CLI never prompts')
115
103
  .option('--json', 'Output JSON only')
116
104
  .action(async (options) => runInit(options));
117
105
  return command;
@@ -3,7 +3,6 @@ import path from 'node:path';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { Command } from 'commander';
5
5
  import { logInfo, logSuccess, logWarn } from '@aopslabs/xf-cli-ui';
6
- import { promptInput, promptSelect } from '../utils/prompts.js';
7
6
  import { loadAopsRepoConfigReadOnly } from '../utils/repo-config.js';
8
7
  import { resolveRepoFirstProjectmanPaths } from '../utils/repo-first-projectman.js';
9
8
  import { readSessionStateNudges } from '../utils/session-state.js';
@@ -949,70 +948,6 @@ async function loadStartMissionResumePack(options, answers, activeProject) {
949
948
  function missingQuestions(answers, questions) {
950
949
  return questions.filter((question) => question.required && normalize(answers[question.key]) === undefined);
951
950
  }
952
- async function promptForAnswersInteractively(answers, boards) {
953
- if (normalize(answers.mode) === undefined) {
954
- answers.mode = await promptSelect({
955
- message: 'Session mode?',
956
- choices: START_MODES.map((mode) => ({ name: mode, value: mode })),
957
- default: 'solo',
958
- });
959
- }
960
- if (normalize(answers.board) === undefined) {
961
- if (boards.length > 0) {
962
- const OTHER = '__other__';
963
- const picked = await promptSelect({
964
- message: 'PM board?',
965
- choices: [
966
- ...boards.map((board) => ({ name: board.name ? `${board.slug} (${board.name})` : board.slug, value: board.slug })),
967
- { name: 'other (type a slug or new:<Title>)', value: OTHER },
968
- ],
969
- });
970
- answers.board =
971
- picked === OTHER
972
- ? await promptInput({
973
- message: 'Board slug or new:<Title>:',
974
- validate: (v) => (v.trim().length > 0 ? true : 'Board is required'),
975
- })
976
- : picked;
977
- }
978
- else {
979
- answers.board = await promptInput({
980
- message: 'Board slug or new:<Title>:',
981
- validate: (v) => (v.trim().length > 0 ? true : 'Board is required'),
982
- });
983
- }
984
- }
985
- if (normalize(answers.task) === undefined) {
986
- answers.task = await promptInput({
987
- message: 'Initial task (optional, empty = set up and wait):',
988
- default: '',
989
- });
990
- }
991
- if (isMultiAgentMode(normalize(answers.mode))) {
992
- const board = normalize(answers.board);
993
- const roomSlugDefault = board && !board.startsWith('new:') ? `${board}-room` : undefined;
994
- if (normalize(answers.roomSlug) === undefined) {
995
- answers.roomSlug = await promptInput({
996
- message: 'Chat room slug:',
997
- default: roomSlugDefault,
998
- validate: (v) => (v.trim().length > 0 ? true : 'Room slug is required in multi-agent modes'),
999
- });
1000
- }
1001
- if (normalize(answers.roomTitle) === undefined) {
1002
- answers.roomTitle = await promptInput({
1003
- message: 'Chat room title:',
1004
- default: normalize(answers.roomSlug),
1005
- validate: (v) => (v.trim().length > 0 ? true : 'Room title is required in multi-agent modes'),
1006
- });
1007
- }
1008
- if (normalize(answers.roles) === undefined) {
1009
- answers.roles = await promptInput({
1010
- message: 'Participants and roles (e.g. "claude=implementer, codex=reviewer, mzs=operator"):',
1011
- validate: (v) => (v.trim().length > 0 ? true : 'Roles are operator-assigned and required in multi-agent modes'),
1012
- });
1013
- }
1014
- }
1015
- }
1016
951
  export async function composeStart(options = {}) {
1017
952
  assertSupportedMode(normalize(options.mode));
1018
953
  assertCompatibleDisciplineAliases({
@@ -1028,10 +963,6 @@ export async function composeStart(options = {}) {
1028
963
  const boards = await listLocalBoards(rootDir, activeProject?.localRoot);
1029
964
  const sessionStateNudges = await readSessionStateNudges({ repoRoot: rootDir, localRoot: activeProject?.localRoot });
1030
965
  const answers = collectAnswers(options);
1031
- const interactiveAllowed = options.interactive !== false && process.stdin.isTTY === true && process.stdout.isTTY === true;
1032
- if (interactiveAllowed && missingQuestions(answers, buildStartQuestions(answers, boards)).length > 0) {
1033
- await promptForAnswersInteractively(answers, boards);
1034
- }
1035
966
  const questions = buildStartQuestions(answers, boards);
1036
967
  const missing = missingQuestions(answers, questions);
1037
968
  const base = {
@@ -1227,7 +1158,6 @@ function serializableStartReminderResult(result) {
1227
1158
  };
1228
1159
  }
1229
1160
  export async function runStart(options = {}) {
1230
- const interactive = options.json === true ? false : options.interactive;
1231
1161
  if (options.reminder === true) {
1232
1162
  const result = await composeStartReminder({ ...options, interactive: false });
1233
1163
  if (options.json) {
@@ -1238,7 +1168,7 @@ export async function runStart(options = {}) {
1238
1168
  console.log(JSON.stringify(result.sessionGuidance, null, 2));
1239
1169
  return;
1240
1170
  }
1241
- const result = await composeStart({ ...options, interactive });
1171
+ const result = await composeStart({ ...options, interactive: false });
1242
1172
  if (options.json) {
1243
1173
  const compact = options.fullOutput === true ? false : true;
1244
1174
  console.log(JSON.stringify({ command: 'start', result: serializableStartResult(result, { compact }) }, null, 2));
@@ -1246,7 +1176,7 @@ export async function runStart(options = {}) {
1246
1176
  }
1247
1177
  result.warnings.forEach((warning) => logWarn(warning));
1248
1178
  if (result.status === 'needs-input') {
1249
- logWarn('Missing answers — re-run with the flags below (or answer interactively in a TTY):');
1179
+ logWarn('Missing answers — re-run with the flags below:');
1250
1180
  for (const question of result.missing) {
1251
1181
  const hints = [];
1252
1182
  if (question.default)
@@ -1266,7 +1196,7 @@ export async function runStart(options = {}) {
1266
1196
  }
1267
1197
  export function makeStartCommand() {
1268
1198
  const cmd = new Command('start')
1269
- .description('Compose the AOPS Collaborative Startup kickoff prompt (interactive or flag-driven)')
1199
+ .description('Compose the AOPS Collaborative Startup kickoff prompt from explicit flags')
1270
1200
  .option('--mode <mode>', `Session mode: ${START_MODES.join(' | ')}`)
1271
1201
  .option('--task <text>', 'Initial task definition (optional; empty = set up and wait)')
1272
1202
  .option('--board <slug>', 'PM board slug or new:<title>')
@@ -1297,7 +1227,7 @@ export function makeStartCommand() {
1297
1227
  .option('--root <path>', 'Workspace root override (default: nearest repo config ancestor)')
1298
1228
  .option('--out <file>', 'Write the composed prompt to a file')
1299
1229
  .option('--no-memory-brief', 'Do not build/embed the read-only local memory cache brief')
1300
- .option('--no-interactive', 'Never prompt on a TTY; report missing answers instead')
1230
+ .option('--no-interactive', 'Compatibility flag; the CLI never prompts')
1301
1231
  .option('--json', 'Output JSON only (implies --no-interactive)')
1302
1232
  .action(async (options) => {
1303
1233
  await runStart(options);
@@ -1310,8 +1240,7 @@ read-only local memory cache brief pack for startup context unless
1310
1240
  --no-memory-brief is set.
1311
1241
 
1312
1242
  Examples:
1313
- aops-cli start # interactive (operator at a TTY)
1314
- aops-cli start --json # agent interview: questions + missing answers
1243
+ aops-cli start --json # questions + missing explicit flags
1315
1244
  aops-cli start --mode solo --board ops --json # compact JSON; read result.promptRef
1316
1245
  aops-cli start --mode solo --board ops --out tmp/start.md --json
1317
1246
  aops-cli start --mode solo --board ops --full-output --json
@@ -1,7 +1,6 @@
1
1
  import { statSync } from 'node:fs';
2
- import { banner, logInfo, logSuccess } from '@aopslabs/xf-cli-ui';
2
+ import { logInfo, logSuccess } from '@aopslabs/xf-cli-ui';
3
3
  import { readAopsServerEnvFileContent, writeAopsServerEnvFileContent, } from '@aopslabs/aops-runtime-config';
4
- import { promptPassword } from '../utils/prompts.js';
5
4
  import { buildServerBootstrapEnvMap, buildServerBootstrapPreset, parseDotEnvAssignments, upsertServerBootstrapBlock, validateServerBootstrapEnv, } from './server-bootstrap.js';
6
5
  import { ExternalPostgresProbeError, probeExternalPostgresConnection, } from './setup-external-postgres.js';
7
6
  const MAX_POSTGRES_URL_BYTES = 4_096;
@@ -24,9 +23,6 @@ function resolveServerEnvAuthProvider(requested, existing) {
24
23
  }
25
24
  return 'trusted-local';
26
25
  }
27
- export function resolvePromptedPostgresUrl(promptedValue, savedValue) {
28
- return normalizeNonEmpty(promptedValue) ?? savedValue;
29
- }
30
26
  function validatePostgresUrl(value) {
31
27
  if (/\0|\r|\n/.test(value) || Buffer.byteLength(value, 'utf8') > MAX_POSTGRES_URL_BYTES) {
32
28
  throw new Error('setup_server_env_postgres_url_invalid');
@@ -131,30 +127,8 @@ export async function runCommunitySetupServerEnv(options = {}) {
131
127
  const environmentUrl = normalizeNonEmpty(process.env.AOPS_PG_URL)
132
128
  ?? normalizeNonEmpty(process.env.AOPS_REPO_URL);
133
129
  let repoUrl = normalizeNonEmpty(options.repoUrl) ?? environmentUrl ?? existingUrl;
134
- if (!options.repoUrl && !options.yes && !options.json) {
135
- if (!options.skipBanner)
136
- banner('AOPS Community Server Environment');
137
- const savedUrl = repoUrl;
138
- const promptedUrl = await promptPassword({
139
- message: existingUrl || environmentUrl
140
- ? 'External PostgreSQL URL [saved: ********] (press Enter to keep):'
141
- : 'External PostgreSQL URL:',
142
- validate: (value) => {
143
- if (!value.trim() && savedUrl)
144
- return true;
145
- try {
146
- validatePostgresUrl(value.trim());
147
- return true;
148
- }
149
- catch {
150
- return 'A PostgreSQL URL is required.';
151
- }
152
- },
153
- });
154
- repoUrl = resolvePromptedPostgresUrl(promptedUrl, savedUrl);
155
- }
156
130
  if (!repoUrl) {
157
- throw new Error('setup_server_env_postgres_url_required:use_interactive_prompt_or_AOPS_PG_URL');
131
+ throw new Error('setup_server_env_postgres_url_required:set_AOPS_PG_URL');
158
132
  }
159
133
  repoUrl = validatePostgresUrl(repoUrl);
160
134
  const authProvider = resolveServerEnvAuthProvider(options.auth, existing);