@aopslabs/aops 0.2.1 → 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.
- package/README.md +26 -22
- package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +9 -7
- package/assets/agent-assets/core/references/chatv3/SKILL.md +2 -1
- package/assets/agent-assets/core/user-guides/aops-cli.md +15 -16
- package/assets/skills/aops-install/SKILL.md +24 -23
- package/dist/commands/agent.js +1 -5
- package/dist/commands/api.js +1 -5
- package/dist/commands/assets.js +2 -134
- package/dist/commands/community-auth.js +1 -1
- package/dist/commands/community-setup.js +17 -76
- package/dist/commands/init.js +5 -17
- package/dist/commands/start.js +5 -76
- package/dist/lib/community-setup-server-env.js +2 -28
- package/dist/lib/setup-init-orchestrator.js +14 -267
- package/dist/lib/setup-install-guide.js +1 -1
- package/dist/main.js +0 -2
- package/package.json +2 -4
- package/dist/commands/community-console.js +0 -109
- package/dist/utils/prompts.js +0 -70
|
@@ -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('
|
|
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
|
|
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
|
-
|
|
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
|
|
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('
|
|
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
|
|
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.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
|
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.
|
|
305
|
-
|
|
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
|
-
|
|
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.
|
package/dist/commands/init.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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', '
|
|
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;
|
package/dist/commands/start.js
CHANGED
|
@@ -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
|
|
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
|
|
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', '
|
|
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
|
|
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 {
|
|
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:
|
|
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);
|