@aopslabs/aops 0.2.1 → 0.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.
@@ -6,16 +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
9
  import { applyDockerServerSetup, inspectDockerServerSetupPreview, inspectDockerServerSetupStatus, runDockerServerLifecycle, } from '../lib/setup-docker-server.js';
11
- const COMMUNITY_SETUP_HOME = `AOPS Setup — choose how to begin
12
- Install interactively aops setup init
13
- Set up with an AI agent aops setup ai
14
- Inspect readiness aops setup init --yes --json
15
- Agent install skill aops setup guide
16
- Configure PostgreSQL aops setup server-env
17
- Help: aops setup --help
18
- `;
19
10
  export function runCommunitySetupGuide(options = {}) {
20
11
  if (options.json && options.path)
21
12
  throw new Error('setup_guide_selector_conflict:choose_--json_or_--path');
@@ -62,7 +53,7 @@ export function runCommunitySetupAi(options = {}) {
62
53
  }
63
54
  banner('AOPS Setup with AI');
64
55
  logInfo('Copy the prompt below to Codex, Claude, or another terminal AI agent.');
65
- 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.');
66
57
  process.stdout.write(`\n--- copy from here ---\n${prompt}\n--- end prompt ---\n`);
67
58
  }
68
59
  export async function runCommunitySetupInit(options = {}) {
@@ -133,50 +124,6 @@ export async function runCommunityDockerSetupPreview(options = {}) {
133
124
  }
134
125
  logInfo('This preview made no changes. Guided Apply will be enabled in the next setup slice.');
135
126
  }
136
- export async function runCommunitySetupMenu() {
137
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
138
- process.stdout.write(COMMUNITY_SETUP_HOME);
139
- return;
140
- }
141
- banner('AOPS Setup');
142
- logInfo('Install here, or hand a safe setup prompt to any terminal AI agent.');
143
- logInfo('Enter database secrets only in masked AOPS prompts; never paste them into chat.');
144
- while (true) {
145
- const action = await promptSelect({
146
- message: 'How do you want to continue?',
147
- type: process.env.AOPS_CLI_MENU_STYLE?.toLowerCase() === 'rawlist' ? 'rawlist' : 'select',
148
- pageSize: 6,
149
- choices: [
150
- { name: 'Install AOPS interactively', value: 'install' },
151
- { name: 'Set up with an AI agent', value: 'ai' },
152
- { name: 'Inspect setup readiness', value: 'inspect' },
153
- { name: 'Configure PostgreSQL connection', value: 'server-env' },
154
- { name: 'Show packaged agent installation skill', value: 'guide' },
155
- { name: 'Exit', value: 'exit' },
156
- ],
157
- });
158
- if (action === 'exit')
159
- return;
160
- if (action === 'ai') {
161
- runCommunitySetupAi();
162
- continue;
163
- }
164
- if (action === 'inspect') {
165
- await runCommunitySetupInit({ yes: true });
166
- continue;
167
- }
168
- if (action === 'server-env') {
169
- await runCommunitySetupServerEnv({});
170
- continue;
171
- }
172
- if (action === 'guide') {
173
- runCommunitySetupGuide();
174
- continue;
175
- }
176
- await runCommunitySetupInit({ skipBanner: true });
177
- return;
178
- }
179
- }
180
127
  function addSetupInitOptions(command) {
181
128
  return command
182
129
  .option('--path <path>', 'Setup path: 1 | 2 | 3 | 4 (semantic ids are also accepted)')
@@ -204,7 +151,7 @@ function addSetupInitOptions(command) {
204
151
  .option('--catalog-release <path>', 'Explicit verified-release override (normally resolved from the Community install)')
205
152
  .option('--catalog-idempotency-key <key>', 'Explicit official catalog reconcile replay key')
206
153
  .option('--plan-id <sha256>', 'Apply only if the current mutation-free installer plan still has this exact identity')
207
- .option('--apply', 'Apply the selected path in non-interactive or scripted use (interactive setup applies directly)')
154
+ .option('--apply', 'Apply the selected explicit path')
208
155
  .option('--resume', 'Resume the same idempotent setup orchestration')
209
156
  .option('--timeout-ms <ms>', 'Read-only host probe timeout', (value) => Number.parseInt(String(value), 10))
210
157
  .option('--yes', 'Non-interactive; report missing selections as actions')
@@ -218,8 +165,8 @@ Agent bootstrap:
218
165
  aops setup ai Print a copy-ready prompt for any terminal AI agent
219
166
  aops setup guide Print the packaged agent-readable installation skill
220
167
  aops setup guide --json Return the same guide in a structured envelope
221
- `)
222
- .action(async () => runCommunitySetupMenu());
168
+ `);
169
+ command.action(() => command.outputHelp());
223
170
  command.addCommand(makeOfficialCatalogSetupCommand());
224
171
  command.command('ai')
225
172
  .description('Print a safe, copy-ready AOPS installation prompt for a terminal AI agent')
@@ -227,7 +174,8 @@ Agent bootstrap:
227
174
  .addHelpText('after', `
228
175
  This read-only handoff works with Codex, Claude, or another terminal agent. The
229
176
  prompt directs the agent to the packaged install skill and keeps database
230
- 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.
231
179
  `)
232
180
  .action((options) => runCommunitySetupAi(options));
233
181
  command.command('guide')
@@ -261,7 +209,7 @@ first attempt.
261
209
  `)
262
210
  .action((options) => runCommunityDockerSetupPreview(options));
263
211
  addSetupInitOptions(command.command('init')
264
- .description('Interactively install AOPS, or inspect/apply an explicit path for automation'))
212
+ .description('Inspect or apply an explicit AOPS installation path'))
265
213
  .addHelpText('after', `
266
214
  Examples:
267
215
  aops setup ai
@@ -281,28 +229,21 @@ Path 1 uses an existing PostgreSQL connection from \`~/.aops/aops.server.env\` b
281
229
  the selected env file. \`--postgres-config\` remains an explicit override.
282
230
  Path 2 uses the same npm server and standard port, while AOPS creates a
283
231
  loopback-only PostgreSQL 17 container on a collision-free Docker-assigned port.
284
- Its password is generated securely by default; the interactive wizard also
285
- allows a masked, confirmed custom password without placing it in shell history.
232
+ Its password is generated securely by default.
286
233
  All PostgreSQL paths plan, apply when needed, and verify database migrations
287
- before the server is reported ready. Interactive terminals show animated
288
- progress and apply directly after required private inputs are collected; there
289
- is no redundant continue confirmation. \`--json\` remains free
290
- of spinner output. Path 1 asks for the masked PostgreSQL URL first, then tests
291
- the connection with TLS \`require\` by default. A TLS-policy prompt is shown only
292
- when that encrypted connection cannot be established. Choose \`verify-full\`
293
- when a trusted CA file is available, or explicitly choose \`disable\` when
294
- accepting an unencrypted PostgreSQL connection. Long-running setup reports its
295
- runtime, connection, migration-plan, migration-apply, server-start, and health
296
- 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.
297
239
  Local Community setup is always \`trusted-local\` with \`loopback\`. Paid or
298
240
  multi-user identity uses a separate HTTPS \`remote-session\` target through path
299
241
  4; it is not installed into the Community server. The legacy
300
242
  \`authv2-jwt-session\` target name is accepted only as a one-release alias.
301
- Path 3 detects PostgreSQL on this computer, securely asks for an existing
302
- 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
303
244
  before running the same migration verification. Administrator credentials are
304
- never stored. For non-interactive path 3, provide the password through the
305
- 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
306
247
  \`--local-postgres-admin-no-password\` only when local PostgreSQL trust auth is
307
248
  already configured. When PostgreSQL is missing or stopped, readiness returns
308
249
  platform-appropriate Windows, macOS, or Linux installation/start guidance.
@@ -316,7 +257,7 @@ Source and npm setup import only the inert signed official catalog bundled with
316
257
  the verified Community release by default.
317
258
  The optional application image reuses the exact npm CLI/server lifecycle
318
259
  inside a container; it remains a distribution surface rather than another
319
- interactive setup path.
260
+ setup path.
320
261
  The CLI first resolves the canonical signed release bundled with the official
321
262
  npm package, selected source, or installed Community runtime;
322
263
  \`--catalog-release\` is only an explicit override.
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import { spawn } from 'node:child_process';
5
5
  import { Command } from 'commander';
6
6
  import { banner, logError, logInfo, logSuccess } from '@aopslabs/xf-cli-ui';
7
- import { getHostRegistrationsDir, listInstalledHostRegistrations, materializeHostRegistrationManifest, mergeHostRegistrationsIntoConfig, normalizeHostRegistrationManifest, unregisterHostRegistration, writeHostRegistration, } from '@aopslabs/aops-host-registration';
7
+ import { getHostRegistrationsDir, listInstalledHostRegistrations, mergeHostRegistrationsIntoConfig, unregisterHostRegistration, } from '@aopslabs/aops-host-registration';
8
8
  import { getAopsServerEnvPath, inferAopsRepoDialect, readAopsServerEnvConfig, redactAopsRepoUrl, writeAopsServerEnvConfig, } from '@aopslabs/aops-runtime-config';
9
9
  import { createCliApiClientFromOptions, fetchCliHealth } from '../utils/api.js';
10
10
  import { applyCommonOptions, compactPayload } from '../utils/command.js';
@@ -325,71 +325,20 @@ function printHostRegistrationResult(payload, options) {
325
325
  }
326
326
  console.log(JSON.stringify(payload, null, 2));
327
327
  }
328
- function requireCommunityRegistrationJsonPath(options) {
329
- const from = options.from?.trim();
330
- if (!from)
331
- throw new Error('Missing registration source. Use --from /path/to/host-registration.json.');
332
- const filePath = path.resolve(process.cwd(), from);
333
- if (path.extname(filePath).toLowerCase() !== '.json') {
334
- throw new Error('Community host registration accepts JSON files only.');
335
- }
336
- const stats = fs.lstatSync(filePath);
337
- if (!stats.isFile() || stats.isSymbolicLink()) {
338
- throw new Error(`Community host registration must be a regular JSON file: ${filePath}`);
339
- }
340
- return fs.realpathSync(filePath);
341
- }
342
- function loadCommunityHostRegistrationJson(filePath) {
343
- let parsed;
344
- try {
345
- parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
346
- }
347
- catch (error) {
348
- throw new Error(`Host registration JSON parse failed: ${error instanceof Error ? error.message : String(error)}`);
349
- }
350
- const materialized = materializeHostRegistrationManifest(normalizeHostRegistrationManifest(parsed), path.dirname(filePath));
351
- return normalizeHostRegistrationManifest({
352
- ...materialized,
353
- provenance: {
354
- ...materialized.provenance,
355
- source: filePath,
356
- sourceType: 'file',
357
- registeredAt: new Date().toISOString(),
358
- },
359
- });
360
- }
361
328
  export async function runHostRegister(options = {}) {
362
- const interactive = !options.yes && !options.json;
363
- const source = requireCommunityRegistrationJsonPath(options);
364
- const registrationsDir = resolveHostRegistrationsDir(options);
365
- if (interactive) {
366
- banner('AOPS Host Register');
367
- logInfo(`Registry: ${registrationsDir}`);
368
- logInfo(`Source: ${source}`);
369
- }
370
- try {
371
- const manifest = loadCommunityHostRegistrationJson(source);
372
- const filePath = writeHostRegistration(manifest, {
373
- registrationsDir,
374
- processEnv: process.env,
375
- });
376
- if (!options.json)
377
- logSuccess(`Registered ${manifest.domain}.`);
378
- printHostRegistrationResult({
379
- ok: true,
380
- domain: manifest.domain,
381
- displayName: manifest.displayName ?? null,
382
- packageName: manifest.packageName ?? null,
383
- filePath,
384
- registrationsDir,
385
- manifest,
386
- }, options);
387
- }
388
- catch (error) {
389
- const message = error instanceof Error ? error.message : String(error);
390
- logError(`Registration failed: ${message}`);
391
- process.exitCode = 1;
392
- }
329
+ const unsupported = {
330
+ ok: false,
331
+ schemaVersion: 1,
332
+ error: 'commercial_profile_required',
333
+ profile: 'trusted-local',
334
+ action: 'host.register',
335
+ message: 'External domain registration is not available in the public trusted-local profile.',
336
+ };
337
+ if (options.json)
338
+ console.log(JSON.stringify(unsupported, null, 2));
339
+ else
340
+ logError(`${unsupported.error}: ${unsupported.message}`);
341
+ process.exitCode = 1;
393
342
  }
394
343
  export async function runHostRegistrations(options = {}) {
395
344
  const interactive = !options.yes && !options.json;
@@ -995,8 +944,8 @@ export function makeHostCommand() {
995
944
  }), { withAuth: false, withProject: false });
996
945
  applyHostRegistrationOptions(applyCommonOptions(cmd
997
946
  .command('register')
998
- .description('Install a JSON host registration manifest into the operator registry')
999
- .requiredOption('--from <path>', 'Path to a host registration JSON document')
947
+ .description('Return commercial_profile_required; external domains are disabled in trusted-local')
948
+ .option('--from <path>', 'Ignored registration source; retained only for a stable refusal contract')
1000
949
  .action(async (options) => {
1001
950
  await runHostRegister(options);
1002
951
  }), { withAuth: false, withProject: false }));
@@ -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);