@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.
- package/README.md +29 -23
- 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 +91 -68
- 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-docker-server.js +666 -0
- package/dist/lib/setup-init-orchestrator.js +14 -267
- package/dist/lib/setup-install-guide.js +1 -1
- package/dist/lib/tui-launcher.js +0 -4
- package/dist/main.js +0 -2
- package/native/tui/win32-x64/aops-tui.exe +0 -0
- package/package.json +4 -4
- package/dist/commands/community-console.js +0 -109
- package/dist/utils/prompts.js +0 -70
|
@@ -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 {
|
|
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('
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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 (
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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('
|
|
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
|
|
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.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|
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.
|
|
223
|
-
|
|
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
|
-
|
|
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.
|
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);
|